npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@planetarium/oai2a2a-server

v0.3.1

Published

Framework-agnostic Web-Fetch handlers (Request → Response) for OpenAI-compatible /v1/chat/completions and /v1/completions routes backed by A2A agents

Downloads

369

Readme

@planetarium/oai2a2a-server

Framework-agnostic Web Fetch handlers for exposing OpenAI-compatible endpoints backed by A2A agents.

createOpenAIRoutes serves:

  • GET /v1/models
  • POST /v1/chat/completions
  • POST /v1/completions
  • POST /v1/responses

Install

npm install @planetarium/oai2a2a-server @planetarium/oai2a2a-codec @a2x/sdk x402

@planetarium/oai2a2a-codec and @a2x/sdk are peer dependencies. The host application owns model-to-agent resolution, A2XClient construction, authentication, SSRF protection, and any billing or usage logging.

x402 is required by @a2x/sdk/client in bundled Next.js route handlers even when the app does not use paid agents directly.

Next.js App Router

import { createOpenAIRoutes } from "@planetarium/oai2a2a-server";
import { hooks } from "@/lib/openai-hooks";

export const runtime = "nodejs";

export const { GET, POST } = createOpenAIRoutes(hooks);

For authenticated hosts, pass a context resolver:

export const { GET, POST } = createOpenAIRoutes(hooks, {
  getContext: (request) => authenticate(request),
});

Declare hooks as ChatCompletionsHandlerHooks<MyContext> so resolveAgent, pollUntilTerminal, and onFinalCompletion receive the resolved context.

Hooks

import {
  pollUntilTerminal,
  type ChatCompletionsHandlerHooks,
} from "@planetarium/oai2a2a-server";

export const hooks: ChatCompletionsHandlerHooks = {
  async resolveAgent(req) {
    return { client, agentCard };
  },
  pollUntilTerminal,
  onFinalCompletion(req, completion) {
    // Optional: usage logging for finalized non-streaming responses and
    // single-chunk streaming fallbacks.
  },
};

onFinalCompletion does not fire for the successful streaming path because no single completion object is built. Streaming clients should read usage from the terminal SSE usage chunk when stream_options.include_usage is enabled.

Injecting downstream message metadata

resolveAgent may return a messageMetadata map alongside the client and agent card. Each entry is merged into the outbound A2A sendParams.message.metadata keyed by extension URI per A2A's Message.metadata convention. When the codec and host both provide object values for the same extension URI, the merge is one level deep and host-supplied fields win. This lets a model-first router forward its selector (e.g. the openai-compat model selector key) without dropping codec-injected fields such as chat_history, system, tools, or tool_choice. The merge does not recurse below the extension payload: { a: { x: 1 } } overlaid with { a: { y: 2 } } becomes { a: { y: 2 } }.

async resolveAgent(req) {
  const agentCard = await pickAgentFor(req.model);
  return {
    client,
    agentCard,
    messageMetadata: {
      "https://github.com/planetarium/oai2a2a/extensions/openai-compat/v1": {
        model: req.model,
      },
    },
  };
}

Responses API shim

createResponsesHandler and createOpenAIRoutes expose an MVP POST /v1/responses shim. It converts supported Responses inputs into the same Chat Completions request shape used by the rest of the package, then converts the result back into a Responses-style object or SSE stream.

Supported request fields:

  • input string or message item arrays
  • instructions
  • function tools
  • tool_choice
  • text.format
  • max_output_tokens
  • stream

Streaming text is emitted as response.output_text.delta events. When the underlying A2A stream has first been converted into OpenAI Chat Completions SSE chunks, any choices[].delta.tool_calls chunks are emitted as function_call output items with response.function_call_arguments.delta and response.function_call_arguments.done events.

The shim intentionally does not implement persisted response state. It rejects previous_response_id, file_id references, and input_file.file_url until a host supplies state/file resolution semantics.