@juspay/neurolink 12.36.0 → 12.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -2
- package/dist/agent/directTools.js +56 -22
- package/dist/browser/neurolink.min.js +374 -374
- package/dist/proxy/argumentParsing.d.ts +12 -0
- package/dist/proxy/argumentParsing.js +23 -0
- package/dist/proxy/codexOutboundFallback.d.ts +10 -6
- package/dist/proxy/codexOutboundFallback.js +163 -43
- package/dist/proxy/codexResponsesFormat.d.ts +125 -0
- package/dist/proxy/codexResponsesFormat.js +591 -0
- package/dist/proxy/codexToAnthropicFallback.d.ts +54 -0
- package/dist/proxy/codexToAnthropicFallback.js +408 -0
- package/dist/proxy/proxyTracer.d.ts +21 -0
- package/dist/proxy/proxyTracer.js +45 -0
- package/dist/proxy/streamLimits.d.ts +16 -0
- package/dist/proxy/streamLimits.js +16 -0
- package/dist/types/codex.d.ts +113 -1
- package/dist/types/file.d.ts +12 -0
- package/dist/types/proxy.d.ts +12 -1
- package/dist/utils/csvProcessor.d.ts +3 -3
- package/dist/utils/csvProcessor.js +53 -5
- package/docs-site/static/search-index.json +12 -12
- package/package.json +1 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a Codex tool call's raw argument text into the record a Claude
|
|
3
|
+
* `tool_use.input` needs, kind-aware: a `function` tool's `arguments` is
|
|
4
|
+
* JSON-Schema-shaped JSON text; a `custom` tool has no JSON at all — only the
|
|
5
|
+
* raw grammar-constrained string that `mapCodexCustomToolToClaude`'s
|
|
6
|
+
* single-field `input_schema` (`codexOutboundFallback.ts` §3.4) expects
|
|
7
|
+
* wrapped as `{ input: raw }`. Centralized so both call sites (a
|
|
8
|
+
* `function_call` item's `arguments` and a `custom_tool_call` item's `input`)
|
|
9
|
+
* go through the same rule rather than two hand-rolled wrappings.
|
|
10
|
+
*/
|
|
11
|
+
import type { CodexNativeToolKind } from "../types/index.js";
|
|
12
|
+
export declare function parseToolArguments(raw: string, kind: CodexNativeToolKind): Record<string, unknown>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
function isPlainRecord(value) {
|
|
2
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
export function parseToolArguments(raw, kind) {
|
|
5
|
+
if (kind === "custom") {
|
|
6
|
+
return { input: raw };
|
|
7
|
+
}
|
|
8
|
+
// Empty arguments is the documented zero-argument-call shape, not
|
|
9
|
+
// malformed input — it must not be wrapped as `{ input: "" }`.
|
|
10
|
+
if (raw === "") {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const parsed = JSON.parse(raw);
|
|
15
|
+
if (isPlainRecord(parsed)) {
|
|
16
|
+
return parsed;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// fall through — not JSON at all (e.g. a stray shell-command string)
|
|
21
|
+
}
|
|
22
|
+
return { input: raw };
|
|
23
|
+
}
|
|
@@ -8,14 +8,17 @@
|
|
|
8
8
|
* own target/model selection, response/stream translation back to Codex wire
|
|
9
9
|
* format, tool execution, or dispatch/HTTP.
|
|
10
10
|
*
|
|
11
|
-
* `parallel_tool_calls`
|
|
12
|
-
* `mapCodexToolChoiceToClaude
|
|
13
|
-
*
|
|
14
|
-
* `
|
|
15
|
-
*
|
|
11
|
+
* `parallel_tool_calls` feeds `tool_choice.disable_parallel_tool_use` in
|
|
12
|
+
* `mapCodexToolChoiceToClaude`, and tool declarations are flattened through
|
|
13
|
+
* `inlineJsonSchema` (dropping `strict` and degrading a circular `$ref` to a
|
|
14
|
+
* bare `{type:"object"}`) — the tool-call-fidelity PR's changes to this file.
|
|
15
|
+
* A `toolKindByName` map is also returned alongside the translated request,
|
|
16
|
+
* so response serialization (which sees only Claude's `name`+`input` on a
|
|
17
|
+
* `tool_use` block, no kind discriminator) knows which declared tools were
|
|
18
|
+
* `custom` rather than `function`.
|
|
16
19
|
*/
|
|
17
20
|
import type { ClaudeRequest } from "../types/index.js";
|
|
18
|
-
import type { CodexNativeRequest, CodexTranslationError } from "../types/index.js";
|
|
21
|
+
import type { CodexNativeRequest, CodexNativeToolKind, CodexTranslationError } from "../types/index.js";
|
|
19
22
|
export declare function parseCodexNativeRequest(raw: unknown): {
|
|
20
23
|
ok: true;
|
|
21
24
|
value: CodexNativeRequest;
|
|
@@ -29,6 +32,7 @@ export declare function translateCodexRequestToClaude(request: CodexNativeReques
|
|
|
29
32
|
}): {
|
|
30
33
|
ok: true;
|
|
31
34
|
value: ClaudeRequest;
|
|
35
|
+
toolKindByName: ReadonlyMap<string, CodexNativeToolKind>;
|
|
32
36
|
} | {
|
|
33
37
|
ok: false;
|
|
34
38
|
error: CodexTranslationError;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { resolveClaudeMaxTokens } from "../utils/tokenLimits.js";
|
|
2
|
+
import { inlineJsonSchema } from "../utils/schemaConversion.js";
|
|
3
|
+
import { parseToolArguments } from "./argumentParsing.js";
|
|
4
|
+
import { recordCodexOutboundSchemaDegraded, recordCodexOutboundUnsupportedField, } from "./proxyTracer.js";
|
|
5
|
+
import { CODEX_STREAM_MAX_TOOL_CALLS, CODEX_STREAM_SIZE_CEILING_BYTES, } from "./streamLimits.js";
|
|
2
6
|
// ---------------------------------------------------------------------------
|
|
3
7
|
// §3.1 parseCodexNativeRequest — the untrusted-JSON -> typed boundary.
|
|
4
8
|
// ---------------------------------------------------------------------------
|
|
@@ -122,29 +126,82 @@ export function parseCodexNativeRequest(raw) {
|
|
|
122
126
|
// ---------------------------------------------------------------------------
|
|
123
127
|
// §3.3 tool_choice — exact inversion of codexFallback.ts's convertClaudeRequestToCodex.
|
|
124
128
|
// ---------------------------------------------------------------------------
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
// Absent tool_choice and "auto" both mean the same thing to Codex, and both
|
|
130
|
+
// now map to an *explicit* `{type:"auto"}` (previously: `undefined` for the
|
|
131
|
+
// absent case) so `disable_parallel_tool_use` always has a field on the
|
|
132
|
+
// output to attach to, regardless of whether the source request bothered to
|
|
133
|
+
// say "auto" out loud. `parallel_tool_calls: false` is inert on `"none"` —
|
|
134
|
+
// there is nothing running in parallel to disable.
|
|
135
|
+
function mapCodexToolChoiceToClaude(choice, parallelToolCalls) {
|
|
136
|
+
const disable = parallelToolCalls === false;
|
|
137
|
+
if (choice === undefined || choice === "auto") {
|
|
138
|
+
return disable
|
|
139
|
+
? { type: "auto", disable_parallel_tool_use: true }
|
|
140
|
+
: { type: "auto" };
|
|
128
141
|
}
|
|
129
142
|
if (choice === "required") {
|
|
130
|
-
return
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return { type: "auto" };
|
|
143
|
+
return disable
|
|
144
|
+
? { type: "any", disable_parallel_tool_use: true }
|
|
145
|
+
: { type: "any" };
|
|
134
146
|
}
|
|
135
147
|
if (choice === "none") {
|
|
136
148
|
return { type: "none" };
|
|
137
149
|
}
|
|
138
|
-
return
|
|
150
|
+
return disable
|
|
151
|
+
? { type: "tool", name: choice.name, disable_parallel_tool_use: true }
|
|
152
|
+
: { type: "tool", name: choice.name };
|
|
139
153
|
}
|
|
140
154
|
// ---------------------------------------------------------------------------
|
|
141
155
|
// §3.4 Tools: flattened across namespaces, split by each declaration's own type.
|
|
142
156
|
// ---------------------------------------------------------------------------
|
|
157
|
+
/**
|
|
158
|
+
* Detection-only walk of a schema's `$ref` chain, independent of
|
|
159
|
+
* `inlineJsonSchema`'s own flattening — the actual flattening stays fully
|
|
160
|
+
* delegated to that function unmodified (never reimplemented here), so this
|
|
161
|
+
* only answers "would a ref chain revisit itself" for the schemaDegraded
|
|
162
|
+
* counter's benefit.
|
|
163
|
+
*/
|
|
164
|
+
function hasCircularRef(schema, defs, seen = new Set()) {
|
|
165
|
+
if (typeof schema.$ref === "string" && schema.$ref.startsWith("#/")) {
|
|
166
|
+
if (seen.has(schema.$ref)) {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
const key = schema.$ref.split("/").pop();
|
|
170
|
+
const target = defs && key ? defs[key] : undefined;
|
|
171
|
+
return target
|
|
172
|
+
? hasCircularRef(target, defs, new Set(seen).add(schema.$ref))
|
|
173
|
+
: false;
|
|
174
|
+
}
|
|
175
|
+
const properties = isRecord(schema.properties)
|
|
176
|
+
? Object.values(schema.properties)
|
|
177
|
+
: [];
|
|
178
|
+
const items = Array.isArray(schema.items)
|
|
179
|
+
? schema.items
|
|
180
|
+
: isRecord(schema.items)
|
|
181
|
+
? [schema.items]
|
|
182
|
+
: [];
|
|
183
|
+
return [...properties, ...items].some((value) => isRecord(value) && hasCircularRef(value, defs, seen));
|
|
184
|
+
}
|
|
143
185
|
function mapCodexFunctionToolToClaude(tool) {
|
|
186
|
+
const reasons = [];
|
|
187
|
+
const defs = (tool.parameters.definitions ?? tool.parameters.$defs);
|
|
188
|
+
if (defs && hasCircularRef(tool.parameters, defs)) {
|
|
189
|
+
reasons.push("circular_ref_flattened");
|
|
190
|
+
}
|
|
191
|
+
const input_schema = inlineJsonSchema(tool.parameters);
|
|
192
|
+
if (tool.strict) {
|
|
193
|
+
// Anthropic tool declarations have no `strict`-mode equivalent, so the
|
|
194
|
+
// flag is dropped rather than silently passed through as an unknown key.
|
|
195
|
+
reasons.push("strict_mode_flag_dropped");
|
|
196
|
+
}
|
|
144
197
|
return {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
198
|
+
tool: {
|
|
199
|
+
name: tool.name,
|
|
200
|
+
...(tool.description ? { description: tool.description } : {}),
|
|
201
|
+
input_schema,
|
|
202
|
+
},
|
|
203
|
+
kind: "function",
|
|
204
|
+
reasons,
|
|
148
205
|
};
|
|
149
206
|
}
|
|
150
207
|
function mapCodexCustomToolToClaude(tool) {
|
|
@@ -152,23 +209,27 @@ function mapCodexCustomToolToClaude(tool) {
|
|
|
152
209
|
// The model is no longer grammar-constrained after this mapping — needs
|
|
153
210
|
// product sign-off before shipping (§10 of the design).
|
|
154
211
|
return {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
212
|
+
tool: {
|
|
213
|
+
name: tool.name,
|
|
214
|
+
description: [
|
|
215
|
+
tool.description,
|
|
216
|
+
`Grammar (${tool.format.syntax}):\n${tool.format.definition}`,
|
|
217
|
+
]
|
|
218
|
+
.filter((s) => Boolean(s))
|
|
219
|
+
.join("\n\n"),
|
|
220
|
+
input_schema: {
|
|
221
|
+
type: "object",
|
|
222
|
+
properties: {
|
|
223
|
+
input: {
|
|
224
|
+
type: "string",
|
|
225
|
+
description: "Raw command text, constrained by the grammar in this tool's description.",
|
|
226
|
+
},
|
|
168
227
|
},
|
|
228
|
+
required: ["input"],
|
|
169
229
|
},
|
|
170
|
-
required: ["input"],
|
|
171
230
|
},
|
|
231
|
+
kind: "custom",
|
|
232
|
+
reasons: ["custom_tool_wrapped"],
|
|
172
233
|
};
|
|
173
234
|
}
|
|
174
235
|
function mapCodexToolDeclarationToClaude(tool) {
|
|
@@ -182,10 +243,14 @@ function mapCodexToolDeclarationToClaude(tool) {
|
|
|
182
243
|
}
|
|
183
244
|
function buildClaudeTools(additionalTools) {
|
|
184
245
|
if (!additionalTools) {
|
|
185
|
-
return undefined;
|
|
246
|
+
return { tools: undefined, toolKindByName: new Map(), reasons: [] };
|
|
186
247
|
}
|
|
187
|
-
const
|
|
188
|
-
return
|
|
248
|
+
const mapped = additionalTools.tools.flatMap((namespace) => namespace.tools.map(mapCodexToolDeclarationToClaude));
|
|
249
|
+
return {
|
|
250
|
+
tools: mapped.length > 0 ? mapped.map((m) => m.tool) : undefined,
|
|
251
|
+
toolKindByName: new Map(mapped.map((m) => [m.tool.name, m.kind])),
|
|
252
|
+
reasons: mapped.flatMap((m) => m.reasons.map((reason) => ({ toolName: m.tool.name, reason }))),
|
|
253
|
+
};
|
|
189
254
|
}
|
|
190
255
|
// ---------------------------------------------------------------------------
|
|
191
256
|
// §3.5 Reasoning: reasoning.effort -> thinking.
|
|
@@ -210,19 +275,23 @@ function mapCodexReasoningToThinking(reasoning, resolvedMaxTokens) {
|
|
|
210
275
|
return { type: "enabled", budget_tokens };
|
|
211
276
|
}
|
|
212
277
|
// ---------------------------------------------------------------------------
|
|
213
|
-
// §3.6 Non-JSON function_call.arguments — never throws.
|
|
278
|
+
// §3.6 Non-JSON function_call.arguments — never throws. Parsing itself is
|
|
279
|
+
// `parseToolArguments` (argumentParsing.ts); this is a cheap, separate,
|
|
280
|
+
// redundant check purely so a malformed (non-empty, non-record) arguments
|
|
281
|
+
// string can be reported via the unsupportedFieldTotal counter without
|
|
282
|
+
// polluting that pure function's return shape with a degrade flag.
|
|
214
283
|
// ---------------------------------------------------------------------------
|
|
215
|
-
function
|
|
284
|
+
function isMalformedFunctionArguments(raw) {
|
|
285
|
+
if (raw === "") {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
216
288
|
try {
|
|
217
289
|
const parsed = JSON.parse(raw);
|
|
218
|
-
|
|
219
|
-
return parsed;
|
|
220
|
-
}
|
|
290
|
+
return !isRecord(parsed);
|
|
221
291
|
}
|
|
222
292
|
catch {
|
|
223
|
-
|
|
293
|
+
return true;
|
|
224
294
|
}
|
|
225
|
-
return { input: raw };
|
|
226
295
|
}
|
|
227
296
|
// ---------------------------------------------------------------------------
|
|
228
297
|
// §3.7 Content parts, with the image data-URI fix.
|
|
@@ -299,8 +368,8 @@ function coalesceCodexInputToClaudeMessages(items) {
|
|
|
299
368
|
// A custom tool is declared with a single `input` string (§3.4), so
|
|
300
369
|
// its raw call text maps onto that field unchanged.
|
|
301
370
|
input: item.type === "function_call"
|
|
302
|
-
?
|
|
303
|
-
:
|
|
371
|
+
? parseToolArguments(item.arguments, "function")
|
|
372
|
+
: parseToolArguments(item.input, "custom"),
|
|
304
373
|
});
|
|
305
374
|
continue;
|
|
306
375
|
}
|
|
@@ -342,6 +411,32 @@ function hasFreshSessionPreamble(items) {
|
|
|
342
411
|
return items[0]?.type === "additional_tools";
|
|
343
412
|
}
|
|
344
413
|
// ---------------------------------------------------------------------------
|
|
414
|
+
// Tool-call history ceilings — the same streamLimits.ts numbers the response
|
|
415
|
+
// leg enforces, so a history is never accepted that the reply could not carry.
|
|
416
|
+
// ---------------------------------------------------------------------------
|
|
417
|
+
function toolHistoryText(item) {
|
|
418
|
+
switch (item.type) {
|
|
419
|
+
case "function_call":
|
|
420
|
+
return item.arguments;
|
|
421
|
+
case "custom_tool_call":
|
|
422
|
+
return item.input;
|
|
423
|
+
case "function_call_output":
|
|
424
|
+
case "custom_tool_call_output":
|
|
425
|
+
return item.output;
|
|
426
|
+
default:
|
|
427
|
+
return undefined;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
function describeOversizedToolHistory(items) {
|
|
431
|
+
const calls = items.filter((item) => item.type === "function_call" || item.type === "custom_tool_call").length;
|
|
432
|
+
if (calls > CODEX_STREAM_MAX_TOOL_CALLS) {
|
|
433
|
+
return `the history carries ${calls} tool calls, over the limit of ${CODEX_STREAM_MAX_TOOL_CALLS}`;
|
|
434
|
+
}
|
|
435
|
+
return items.some((item) => (toolHistoryText(item)?.length ?? 0) > CODEX_STREAM_SIZE_CEILING_BYTES)
|
|
436
|
+
? `a tool call's arguments or output exceed the limit of ${CODEX_STREAM_SIZE_CEILING_BYTES} characters`
|
|
437
|
+
: undefined;
|
|
438
|
+
}
|
|
439
|
+
// ---------------------------------------------------------------------------
|
|
345
440
|
// Inputs Codex accepts but the Messages API rejects — fail loud, never guess.
|
|
346
441
|
// ---------------------------------------------------------------------------
|
|
347
442
|
function describeUntranslatableItem(item, provider) {
|
|
@@ -423,6 +518,13 @@ function lastAssistantMessageUsesTool(messages) {
|
|
|
423
518
|
// §3.2 translateCodexRequestToClaude — the full field-by-field mapping.
|
|
424
519
|
// ---------------------------------------------------------------------------
|
|
425
520
|
export function translateCodexRequestToClaude(request, target) {
|
|
521
|
+
const oversized = describeOversizedToolHistory(request.input);
|
|
522
|
+
if (oversized) {
|
|
523
|
+
return {
|
|
524
|
+
ok: false,
|
|
525
|
+
error: { code: "REQUEST_TOO_LARGE", message: oversized },
|
|
526
|
+
};
|
|
527
|
+
}
|
|
426
528
|
// Read from client_metadata, not top-level session_id/thread_id: the real
|
|
427
529
|
// capture carries neither at top level (only nested in client_metadata),
|
|
428
530
|
// which is why CodexNativeRequest has no top-level fields for them.
|
|
@@ -451,9 +553,9 @@ export function translateCodexRequestToClaude(request, target) {
|
|
|
451
553
|
const additionalTools = request.input
|
|
452
554
|
.filter((item) => item.type === "additional_tools")
|
|
453
555
|
.at(-1);
|
|
454
|
-
const tools = buildClaudeTools(additionalTools);
|
|
455
|
-
const toolChoice = mapCodexToolChoiceToClaude(request.tool_choice);
|
|
456
|
-
const forcesTool = toolChoice
|
|
556
|
+
const { tools, toolKindByName, reasons: schemaReasons, } = buildClaudeTools(additionalTools);
|
|
557
|
+
const toolChoice = mapCodexToolChoiceToClaude(request.tool_choice, request.parallel_tool_calls);
|
|
558
|
+
const forcesTool = toolChoice.type === "any" || toolChoice.type === "tool";
|
|
457
559
|
if (forcesTool && !tools) {
|
|
458
560
|
return {
|
|
459
561
|
ok: false,
|
|
@@ -463,7 +565,7 @@ export function translateCodexRequestToClaude(request, target) {
|
|
|
463
565
|
},
|
|
464
566
|
};
|
|
465
567
|
}
|
|
466
|
-
if (toolChoice
|
|
568
|
+
if (toolChoice.type === "tool" &&
|
|
467
569
|
!tools?.some((tool) => tool.name === toolChoice.name)) {
|
|
468
570
|
return {
|
|
469
571
|
ok: false,
|
|
@@ -507,9 +609,27 @@ export function translateCodexRequestToClaude(request, target) {
|
|
|
507
609
|
...(systemBlocks.length > 0 ? { system: systemBlocks } : {}),
|
|
508
610
|
...(tools ? { tools } : {}),
|
|
509
611
|
// The Messages API accepts tool_choice only alongside tools.
|
|
510
|
-
...(
|
|
612
|
+
...(tools ? { tool_choice: toolChoice } : {}),
|
|
511
613
|
...(thinking ? { thinking } : {}),
|
|
512
614
|
stream: request.stream ?? true,
|
|
513
615
|
};
|
|
514
|
-
|
|
616
|
+
// Recorded only once translation has fully succeeded, so a request that is
|
|
617
|
+
// rejected earlier (malformed tool_choice, untranslatable content, a
|
|
618
|
+
// pairing gap) never fires a degrade counter for work that was discarded.
|
|
619
|
+
for (const reason of schemaReasons) {
|
|
620
|
+
recordCodexOutboundSchemaDegraded({
|
|
621
|
+
toolName: reason.toolName,
|
|
622
|
+
reason: reason.reason,
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
for (const item of request.input) {
|
|
626
|
+
if (item.type === "function_call" &&
|
|
627
|
+
isMalformedFunctionArguments(item.arguments)) {
|
|
628
|
+
recordCodexOutboundUnsupportedField({
|
|
629
|
+
field: "function_call.arguments",
|
|
630
|
+
reason: "malformed_arguments_wrapped",
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return { ok: true, value: claudeRequest, toolKindByName };
|
|
515
635
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire-format translation: Anthropic `ClaudeResponse` shape -> the Codex
|
|
3
|
+
* Responses shape a native Codex client expects.
|
|
4
|
+
*
|
|
5
|
+
* Pure wire-format only — no account selection, retry policy, or transport.
|
|
6
|
+
* Mirrors `claudeFormat.ts`'s role for the opposite direction: that module
|
|
7
|
+
* serializes NeuroLink's internal result into Claude SSE, this one serializes
|
|
8
|
+
* an already-Claude-shaped result into Codex SSE (or one JSON envelope).
|
|
9
|
+
*/
|
|
10
|
+
import type { ClaudeContentBlock, ClaudeResponse, ClaudeUsage, CodexNativeToolKind, CodexResponseEnvelope, CodexResponseItem, CodexResponseUsage } from "../types/index.js";
|
|
11
|
+
/** Same idiom as `generateToolUseId` (`claudeFormat.ts`), Codex's own prefix. */
|
|
12
|
+
export declare function generateCodexResponseId(): string;
|
|
13
|
+
/** Item ids are prefixed by kind so a client can tell message/tool items apart at a glance. */
|
|
14
|
+
export declare function generateCodexItemId(kind: "message" | "function_call" | "custom_tool_call"): string;
|
|
15
|
+
/**
|
|
16
|
+
* Reuse the Anthropic `tool_use.id` (`toolu_...`) verbatim as the Codex `call_id`.
|
|
17
|
+
* No id-mapping table exists anywhere in this codebase's translation modules, and
|
|
18
|
+
* nothing on the way in validates `call_id` format — see the design doc's §5 for
|
|
19
|
+
* the full reasoning and its one unresolved edge (a real, non-fallback Codex
|
|
20
|
+
* backend rejecting a non-`call_`-prefixed id on a later turn).
|
|
21
|
+
*/
|
|
22
|
+
export declare function codexCallIdFromToolUseId(toolUseId: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Absent cache fields are omitted rather than zeroed (never conflate "not
|
|
25
|
+
* observed" with "observed as zero" — the field's own doc comment on
|
|
26
|
+
* `CodexResponseUsage` states this contract).
|
|
27
|
+
*/
|
|
28
|
+
export declare function synthesizeCodexUsage(usage: ClaudeUsage): CodexResponseUsage;
|
|
29
|
+
/**
|
|
30
|
+
* Shared by the streaming serializer's non-failure item close and the
|
|
31
|
+
* non-streaming path (`serializeCodexResponse`), so item shape is defined
|
|
32
|
+
* once. Always produces a "completed" item — the streaming failure path
|
|
33
|
+
* (an item closed "incomplete" mid-turn) builds its item directly, since it
|
|
34
|
+
* must preserve the raw accumulated text/argument bytes rather than a value
|
|
35
|
+
* reconstructed through this function.
|
|
36
|
+
*/
|
|
37
|
+
export declare function buildCodexResponseItem(block: ClaudeContentBlock, itemId: string, toolKindByName?: ReadonlyMap<string, CodexNativeToolKind>): CodexResponseItem | null;
|
|
38
|
+
/** Non-streaming path: `§7` of the design — the document root IS a `CodexResponseEnvelope`. */
|
|
39
|
+
export declare function serializeCodexResponse(result: ClaudeResponse, requestModel: string, toolKindByName?: ReadonlyMap<string, CodexNativeToolKind>): CodexResponseEnvelope;
|
|
40
|
+
/**
|
|
41
|
+
* Push-based streaming serializer. A genuinely incremental tool-call-argument
|
|
42
|
+
* path (`openToolCall`/`pushToolCallArgsDelta`/`closeToolCall`) forwards each
|
|
43
|
+
* raw `input_json_delta` chunk as it arrives rather than buffering the whole
|
|
44
|
+
* call before emitting anything — `partial_json` is not individually valid
|
|
45
|
+
* JSON, so a serializer that validated per-chunk could not do this.
|
|
46
|
+
*/
|
|
47
|
+
export declare class CodexResponsesStreamSerializer {
|
|
48
|
+
private readonly model;
|
|
49
|
+
private readonly toolKindByName;
|
|
50
|
+
private state;
|
|
51
|
+
private readonly responseId;
|
|
52
|
+
private outputIndex;
|
|
53
|
+
private openKind;
|
|
54
|
+
private openItemId;
|
|
55
|
+
private textAccum;
|
|
56
|
+
private argsAccum;
|
|
57
|
+
private toolCallId;
|
|
58
|
+
private toolName;
|
|
59
|
+
private readonly closedItems;
|
|
60
|
+
private terminalEnvelope;
|
|
61
|
+
constructor(model: string, toolKindByName?: ReadonlyMap<string, CodexNativeToolKind>);
|
|
62
|
+
start(): Generator<string>;
|
|
63
|
+
pushDelta(text: string): Generator<string>;
|
|
64
|
+
/** Opens the tool-call item header at content_block_start, before any argument text
|
|
65
|
+
* is known — mirrors what content_block_start already gives us (id, name). A tool
|
|
66
|
+
* the request declared `custom` opens as a `custom_tool_call` item. */
|
|
67
|
+
openToolCall(toolUseId: string, name: string): Generator<string>;
|
|
68
|
+
/** Forwards one raw partial_json fragment as-is — no JSON.parse here, because a lone
|
|
69
|
+
* fragment is not required to be valid JSON on its own. A custom call's fragments
|
|
70
|
+
* are the `{"input":"..."}` wrapper, not client text, so they are only buffered;
|
|
71
|
+
* `closeToolCall` emits the unwrapped input once the whole wrapper is known. */
|
|
72
|
+
pushToolCallArgsDelta(rawChunk: string): Generator<string>;
|
|
73
|
+
/**
|
|
74
|
+
* Public close for a tool-use block at `content_block_stop`. A no-op when no
|
|
75
|
+
* tool-call item is open, so a driver can call it unconditionally.
|
|
76
|
+
* Validates the accumulated arguments once, here — the one place validity
|
|
77
|
+
* is required. Throws on malformed JSON; the caller must catch and route to
|
|
78
|
+
* `emitFailure` (never swallowed, never retried after this point).
|
|
79
|
+
*/
|
|
80
|
+
closeToolCall(): Generator<string>;
|
|
81
|
+
/**
|
|
82
|
+
* Public close for a text block at `content_block_stop`, mirroring
|
|
83
|
+
* `closeToolCall`'s role for the message item. A no-op when no message item
|
|
84
|
+
* is open, so a driver can call it unconditionally alongside `closeToolCall`
|
|
85
|
+
* without first inspecting which kind is currently open.
|
|
86
|
+
*/
|
|
87
|
+
closeTextBlock(): Generator<string>;
|
|
88
|
+
finish(stopReason: string | null, usage: ClaudeUsage): Generator<string, CodexResponseEnvelope>;
|
|
89
|
+
/**
|
|
90
|
+
* Post-commit terminal path. Closes any open item as "incomplete" — now
|
|
91
|
+
* representable via `CodexResponseItemStatus` — then emits a single
|
|
92
|
+
* `response.failed`. Idempotent: a second call after this one already ran
|
|
93
|
+
* returns the same envelope without emitting further frames, so a driver
|
|
94
|
+
* that reaches it defensively from more than one place never double-emits.
|
|
95
|
+
*/
|
|
96
|
+
emitFailure(_status: number, message: string, code?: string): Generator<string, CodexResponseEnvelope>;
|
|
97
|
+
private assertNotTerminal;
|
|
98
|
+
/**
|
|
99
|
+
* The custom-kind close: nothing was streamed live, so one input delta
|
|
100
|
+
* carries the whole unwrapped input, then its done event. Lenient where the
|
|
101
|
+
* function path throws: the wrapper is Claude's own output for the
|
|
102
|
+
* single-field schema `mapCodexCustomToolToClaude` declared, so a parse
|
|
103
|
+
* failure is an internal bug, and the raw accumulated text is emitted rather
|
|
104
|
+
* than failing the turn over it.
|
|
105
|
+
*/
|
|
106
|
+
private closeCustomToolCall;
|
|
107
|
+
private openMessage;
|
|
108
|
+
/** Closes whichever item is currently open, as "incomplete" when `failed`. */
|
|
109
|
+
private closeOpenItem;
|
|
110
|
+
/**
|
|
111
|
+
* The failure-close counterpart to `closeToolCall`. Arguments are carried
|
|
112
|
+
* verbatim from `argsAccum` and never re-validated — `JSON.parse` is not
|
|
113
|
+
* called on the failure path, and no `function_call_arguments.done` is
|
|
114
|
+
* emitted, since the arguments never reached a validated, complete state.
|
|
115
|
+
*/
|
|
116
|
+
private closeFailedFunctionCall;
|
|
117
|
+
/**
|
|
118
|
+
* The failure close for a custom call. Its accumulated bytes are Anthropic's
|
|
119
|
+
* JSON wrapper, not the client's grammar text, so `input` is the unwrapped
|
|
120
|
+
* string when the wrapper already parses and "" otherwise — an incomplete
|
|
121
|
+
* item is never executed. No input done event, as on the function path.
|
|
122
|
+
*/
|
|
123
|
+
private closeFailedCustomToolCall;
|
|
124
|
+
private closeMessageItem;
|
|
125
|
+
}
|