@juspay/neurolink
Version:
The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002
126 lines (125 loc) • 7.14 kB
TypeScript
/**
* Wire-format translation: Anthropic `ClaudeResponse` shape -> the Codex
* Responses shape a native Codex client expects.
*
* Pure wire-format only — no account selection, retry policy, or transport.
* Mirrors `claudeFormat.ts`'s role for the opposite direction: that module
* serializes NeuroLink's internal result into Claude SSE, this one serializes
* an already-Claude-shaped result into Codex SSE (or one JSON envelope).
*/
import type { ClaudeContentBlock, ClaudeResponse, ClaudeUsage, CodexNativeToolKind, CodexResponseEnvelope, CodexResponseItem, CodexResponseUsage } from "../types/index.js";
/** Same idiom as `generateToolUseId` (`claudeFormat.ts`), Codex's own prefix. */
export declare function generateCodexResponseId(): string;
/** Item ids are prefixed by kind so a client can tell message/tool items apart at a glance. */
export declare function generateCodexItemId(kind: "message" | "function_call" | "custom_tool_call"): string;
/**
* Reuse the Anthropic `tool_use.id` (`toolu_...`) verbatim as the Codex `call_id`.
* No id-mapping table exists anywhere in this codebase's translation modules, and
* nothing on the way in validates `call_id` format — see the design doc's §5 for
* the full reasoning and its one unresolved edge (a real, non-fallback Codex
* backend rejecting a non-`call_`-prefixed id on a later turn).
*/
export declare function codexCallIdFromToolUseId(toolUseId: string): string;
/**
* Absent cache fields are omitted rather than zeroed (never conflate "not
* observed" with "observed as zero" — the field's own doc comment on
* `CodexResponseUsage` states this contract).
*/
export declare function synthesizeCodexUsage(usage: ClaudeUsage): CodexResponseUsage;
/**
* Shared by the streaming serializer's non-failure item close and the
* non-streaming path (`serializeCodexResponse`), so item shape is defined
* once. Always produces a "completed" item — the streaming failure path
* (an item closed "incomplete" mid-turn) builds its item directly, since it
* must preserve the raw accumulated text/argument bytes rather than a value
* reconstructed through this function.
*/
export declare function buildCodexResponseItem(block: ClaudeContentBlock, itemId: string, toolKindByName?: ReadonlyMap<string, CodexNativeToolKind>): CodexResponseItem | null;
/** Non-streaming path: `§7` of the design — the document root IS a `CodexResponseEnvelope`. */
export declare function serializeCodexResponse(result: ClaudeResponse, requestModel: string, toolKindByName?: ReadonlyMap<string, CodexNativeToolKind>): CodexResponseEnvelope;
/**
* Push-based streaming serializer. A genuinely incremental tool-call-argument
* path (`openToolCall`/`pushToolCallArgsDelta`/`closeToolCall`) forwards each
* raw `input_json_delta` chunk as it arrives rather than buffering the whole
* call before emitting anything — `partial_json` is not individually valid
* JSON, so a serializer that validated per-chunk could not do this.
*/
export declare class CodexResponsesStreamSerializer {
private readonly model;
private readonly toolKindByName;
private state;
private readonly responseId;
private outputIndex;
private openKind;
private openItemId;
private textAccum;
private argsAccum;
private toolCallId;
private toolName;
private readonly closedItems;
private terminalEnvelope;
constructor(model: string, toolKindByName?: ReadonlyMap<string, CodexNativeToolKind>);
start(): Generator<string>;
pushDelta(text: string): Generator<string>;
/** Opens the tool-call item header at content_block_start, before any argument text
* is known — mirrors what content_block_start already gives us (id, name). A tool
* the request declared `custom` opens as a `custom_tool_call` item. */
openToolCall(toolUseId: string, name: string): Generator<string>;
/** Forwards one raw partial_json fragment as-is — no JSON.parse here, because a lone
* fragment is not required to be valid JSON on its own. A custom call's fragments
* are the `{"input":"..."}` wrapper, not client text, so they are only buffered;
* `closeToolCall` emits the unwrapped input once the whole wrapper is known. */
pushToolCallArgsDelta(rawChunk: string): Generator<string>;
/**
* Public close for a tool-use block at `content_block_stop`. A no-op when no
* tool-call item is open, so a driver can call it unconditionally.
* Validates the accumulated arguments once, here — the one place validity
* is required. Throws on malformed JSON; the caller must catch and route to
* `emitFailure` (never swallowed, never retried after this point).
*/
closeToolCall(): Generator<string>;
/**
* Public close for a text block at `content_block_stop`, mirroring
* `closeToolCall`'s role for the message item. A no-op when no message item
* is open, so a driver can call it unconditionally alongside `closeToolCall`
* without first inspecting which kind is currently open.
*/
closeTextBlock(): Generator<string>;
finish(stopReason: string | null, usage: ClaudeUsage): Generator<string, CodexResponseEnvelope>;
/**
* Post-commit terminal path. Closes any open item as "incomplete" — now
* representable via `CodexResponseItemStatus` — then emits a single
* `response.failed`. Idempotent: a second call after this one already ran
* returns the same envelope without emitting further frames, so a driver
* that reaches it defensively from more than one place never double-emits.
*/
emitFailure(_status: number, message: string, code?: string): Generator<string, CodexResponseEnvelope>;
private assertNotTerminal;
/**
* The custom-kind close: nothing was streamed live, so one input delta
* carries the whole unwrapped input, then its done event. Lenient where the
* function path throws: the wrapper is Claude's own output for the
* single-field schema `mapCodexCustomToolToClaude` declared, so a parse
* failure is an internal bug, and the raw accumulated text is emitted rather
* than failing the turn over it.
*/
private closeCustomToolCall;
private openMessage;
/** Closes whichever item is currently open, as "incomplete" when `failed`. */
private closeOpenItem;
/**
* The failure-close counterpart to `closeToolCall`. Arguments are carried
* verbatim from `argsAccum` and never re-validated — `JSON.parse` is not
* called on the failure path, and no `function_call_arguments.done` is
* emitted, since the arguments never reached a validated, complete state.
*/
private closeFailedFunctionCall;
/**
* The failure close for a custom call. Its accumulated bytes are Anthropic's
* JSON wrapper, not the client's grammar text, so `input` is the unwrapped
* string when the wrapper already parses and "" otherwise — an incomplete
* item is never executed. No input done event, as on the function path.
*/
private closeFailedCustomToolCall;
private closeMessageItem;
}