@yansigit/opencodex 2.35.1-dev.20260828.3 → 2.35.1-dev.20260828.5

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.
@@ -16,7 +16,7 @@
16
16
  } catch (e) {}
17
17
  })();
18
18
  </script>
19
- <script type="module" crossorigin src="/assets/index-Doud7zg9.js"></script>
19
+ <script type="module" crossorigin src="/assets/index-Bnyz_191.js"></script>
20
20
  <link rel="stylesheet" crossorigin href="/assets/index-DLkXOXLC.css">
21
21
  </head>
22
22
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yansigit/opencodex",
3
- "version": "2.35.1-dev.20260828.3",
3
+ "version": "2.35.1-dev.20260828.5",
4
4
  "description": "Universal provider proxy for OpenAI Codex & Claude Code — use any LLM with Codex CLI/App/SDK and Claude Code",
5
5
  "type": "module",
6
6
  "main": "./bin/package-main.mjs",
@@ -39,7 +39,9 @@
39
39
  "dev:gui": "cd gui && bun run dev",
40
40
  "start": "bun run src/cli/index.ts start",
41
41
  "test": "bun scripts/test.ts",
42
+ "test:v2-bridge": "bun scripts/test.ts tests/config.test.ts tests/multi-agent-keep-native-v1.test.ts tests/namespace-tool-compat.test.ts tests/v2-routed-delegation-bridge.test.ts tests/responses-v2-routed-delegation-bridge.test.ts tests/v2-agent-message-failfast.test.ts tests/responses-compaction-routing.test.ts tests/responses-v2-native-parent-override.test.ts tests/passthrough-abort.test.ts tests/ws-upstream.test.ts tests/core-lab-boundary.test.ts",
42
43
  "typecheck": "bun x tsc --noEmit",
44
+ "verify:v2-bridge": "bun run typecheck && bun run test:v2-bridge && cd gui && bun test tests/subagents-ultra-mode.test.tsx && bun run lint:i18n",
43
45
  "audit:high": "bun audit --audit-level=high && cd gui && bun audit --audit-level=high",
44
46
  "privacy:scan": "bun scripts/privacy-scan.ts",
45
47
  "generate:model-metadata": "bun scripts/generate-model-metadata.ts",
package/src/config.ts CHANGED
@@ -928,6 +928,9 @@ const configSchema = z.object({
928
928
  providerContextCaps: z.record(z.string(), z.number().int().positive()).optional(),
929
929
  contextCapValue: z.number().int().positive().optional(),
930
930
  multiAgentGuidanceEnabled: z.boolean().optional(),
931
+ // Invalid hand edits disable only this experimental opt-in.
932
+ v2RoutedDelegationBridge: z.boolean().optional().catch(undefined),
933
+ // Invalid hand edits disable only this experimental opt-in subtree.
931
934
  v2NativeParentOverride: v2NativeParentOverrideSchema.optional().catch(undefined),
932
935
  // Invalid optional recovery config must not discard unrelated provider/account state.
933
936
  agentTaskRecovery: agentTaskRecoverySchema.optional().catch(undefined),
@@ -2240,6 +2243,15 @@ function agentTaskRecoveryError(value: unknown): string | null {
2240
2243
  return `schema_invalid: agentTaskRecovery${field ? `.${field}` : ""}: ${issue?.message ?? "invalid configuration"}`;
2241
2244
  }
2242
2245
 
2246
+ function v2RoutedDelegationBridgeError(value: unknown): string | null {
2247
+ const raw = rawConfigRecord(value);
2248
+ if (!raw || !Object.hasOwn(raw, "v2RoutedDelegationBridge")) return null;
2249
+ const enabled = raw.v2RoutedDelegationBridge;
2250
+ return enabled === undefined || typeof enabled === "boolean"
2251
+ ? null
2252
+ : "schema_invalid: v2RoutedDelegationBridge: must be a boolean or omitted";
2253
+ }
2254
+
2243
2255
  function v2NativeParentOverrideError(value: unknown): string | null {
2244
2256
  const raw = rawConfigRecord(value);
2245
2257
  if (!raw || !Object.hasOwn(raw, "v2NativeParentOverride") || raw.v2NativeParentOverride === undefined) return null;
@@ -2363,6 +2375,7 @@ export function validateConfigCandidate(value: unknown): { ok: true; config: Ocx
2363
2375
  ?? appOwnedMemoryBudgetError(value)
2364
2376
  ?? upstreamHostCircuitThresholdError(value)
2365
2377
  ?? agentTaskRecoveryError(value)
2378
+ ?? v2RoutedDelegationBridgeError(value)
2366
2379
  ?? v2NativeParentOverrideError(value)
2367
2380
  ?? googleAntigravityStaticCatalogVersionError(value)
2368
2381
  ?? codexAccountPrioritiesError(value)
@@ -10,7 +10,7 @@
10
10
  },
11
11
  {
12
12
  "path": "package.json",
13
- "sha256": "04c678cdcea4e81189fc9b670c41fd01418a6811bad1589d44cbc5af2c82fd4a"
13
+ "sha256": "a5ca11ad28ee71275c97eac3a5776b910f8836fe6641548e54ab0e4e32a07e40"
14
14
  },
15
15
  {
16
16
  "path": "scripts/model-metadata.source.json",
@@ -1166,7 +1166,7 @@
1166
1166
  },
1167
1167
  {
1168
1168
  "path": "src/config.ts",
1169
- "sha256": "17b011a9980e50b95e37ea8b15a310bfc6d11c671029530426ea34f0ceb6df5c"
1169
+ "sha256": "4534807abf3722078ae2c6d377885a12d4e6921b376601ea6d76ccc3f677cfb9"
1170
1170
  },
1171
1171
  {
1172
1172
  "path": "src/config/atomic-write.ts",
@@ -2710,7 +2710,7 @@
2710
2710
  },
2711
2711
  {
2712
2712
  "path": "src/server/management/agent-settings-routes.ts",
2713
- "sha256": "b3ab4de3aa4b3137980f592ed85f202573e6c57ad54371ed164bbfecbd97e437"
2713
+ "sha256": "1fb5e34401044aedbc3164c083d0c1d37857083f3144e296d4e5ad4993bc3cee"
2714
2714
  },
2715
2715
  {
2716
2716
  "path": "src/server/management/api-access.ts",
@@ -2946,7 +2946,7 @@
2946
2946
  },
2947
2947
  {
2948
2948
  "path": "src/server/responses/core.ts",
2949
- "sha256": "f6acbd947c2abc6a5811406ec993876ae0e10fe56a2ce67507eb778bb46de956"
2949
+ "sha256": "90ce7232996139a594f4055155db1cba61692f680c85e9d6d5601a0657c12c49"
2950
2950
  },
2951
2951
  {
2952
2952
  "path": "src/server/responses/empty-completion-guard.ts",
@@ -2992,6 +2992,10 @@
2992
2992
  "path": "src/server/responses/v2-native-parent-override.ts",
2993
2993
  "sha256": "ec20872a1cf656d0c3d29bebdb8863e80f16140913f725612b617f1e271abada"
2994
2994
  },
2995
+ {
2996
+ "path": "src/server/responses/v2-routed-delegation-bridge.ts",
2997
+ "sha256": "a0ea013323d54c81426a0abc42eef6682d160a9ce71e3fc209c3124034ee514b"
2998
+ },
2995
2999
  {
2996
3000
  "path": "src/server/responses/ws-upstream.ts",
2997
3001
  "sha256": "a892b860cdc50e9f15f23fdb2f286a8e3416eff4ce163650f5226aab4159fd38"
@@ -3162,7 +3166,7 @@
3162
3166
  },
3163
3167
  {
3164
3168
  "path": "src/types/config.ts",
3165
- "sha256": "e9bd0d48a52e54528593999ad87350bd2e134d662da104d22eaefc7c6c609e1a"
3169
+ "sha256": "2947399e84229ff915fbe1677b4b29ddefafbde0cf42a10bdb130460fa1715fd"
3166
3170
  },
3167
3171
  {
3168
3172
  "path": "src/types/provider.ts",
@@ -92,6 +92,17 @@ let grokApplyTestHooks: { now?: () => number; run?: () => Promise<unknown> } | n
92
92
  type V2NativeParentOverrideInput = { enabled: boolean; model: string | null };
93
93
  type AgentTaskRecoveryInput = { enabled: boolean; model: string | null };
94
94
 
95
+ function persistV2RoutedDelegationBridge(config: OcxConfig, enabled: boolean): { ok: true } | { ok: false; reason: string } {
96
+ const outcome = mutatePersistedConfig(persisted => {
97
+ const changed = persisted.v2RoutedDelegationBridge !== enabled;
98
+ if (changed) persisted.v2RoutedDelegationBridge = enabled;
99
+ return { changed, value: true };
100
+ });
101
+ if (outcome.status === "unavailable") return { ok: false, reason: outcome.reason };
102
+ config.v2RoutedDelegationBridge = enabled;
103
+ return { ok: true };
104
+ }
105
+
95
106
  function agentTaskRecoveryDto(
96
107
  config: OcxConfig,
97
108
  ): { enabled: boolean; model: string | null } {
@@ -342,6 +353,7 @@ export async function handleAgentSettingsRoutes(ctx: ManagementContext): Promise
342
353
  // server-side so no client can present it as an effective V2 limit.
343
354
  agentsMaxDepthAppliesWhenV2Disabled: !enabled,
344
355
  v2NativeParentOverride,
356
+ v2RoutedDelegationBridge: config.v2RoutedDelegationBridge === true,
345
357
  agentTaskRecovery: agentTaskRecoveryDto(config),
346
358
  });
347
359
  }
@@ -356,6 +368,7 @@ export async function handleAgentSettingsRoutes(ctx: ManagementContext): Promise
356
368
  subagentDeveloperInstructions?: unknown;
357
369
  multiAgentModeHintText?: unknown;
358
370
  v2NativeParentOverride?: unknown;
371
+ v2RoutedDelegationBridge?: unknown;
359
372
  agentTaskRecovery?: unknown;
360
373
  };
361
374
  try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
@@ -368,9 +381,10 @@ export async function handleAgentSettingsRoutes(ctx: ManagementContext): Promise
368
381
  const wantsSubagentInstructions = body.subagentDeveloperInstructions !== undefined;
369
382
  const wantsModeHintText = body.multiAgentModeHintText !== undefined;
370
383
  const wantsV2NativeParentOverride = body.v2NativeParentOverride !== undefined;
384
+ const wantsV2RoutedDelegationBridge = body.v2RoutedDelegationBridge !== undefined;
371
385
  const wantsAgentTaskRecovery = body.agentTaskRecovery !== undefined;
372
- if (!wantsFlag && !wantsThreads && !wantsMode && !wantsKeepNative && !wantsAgentsEnabled && !wantsMaxDepth && !wantsSubagentInstructions && !wantsModeHintText && !wantsV2NativeParentOverride && !wantsAgentTaskRecovery) {
373
- return jsonResponse({ error: "body must set enabled, multiAgentMode, keepNativeChatGptOnV1, maxConcurrentThreadsPerSession, agentsEnabled, agentsMaxDepth, subagentDeveloperInstructions, multiAgentModeHintText, v2NativeParentOverride, and/or agentTaskRecovery" }, 400);
386
+ if (!wantsFlag && !wantsThreads && !wantsMode && !wantsKeepNative && !wantsAgentsEnabled && !wantsMaxDepth && !wantsSubagentInstructions && !wantsModeHintText && !wantsV2NativeParentOverride && !wantsV2RoutedDelegationBridge && !wantsAgentTaskRecovery) {
387
+ return jsonResponse({ error: "body must set enabled, multiAgentMode, keepNativeChatGptOnV1, maxConcurrentThreadsPerSession, agentsEnabled, agentsMaxDepth, subagentDeveloperInstructions, multiAgentModeHintText, v2NativeParentOverride, v2RoutedDelegationBridge, and/or agentTaskRecovery" }, 400);
374
388
  }
375
389
  if (wantsFlag && typeof body.enabled !== "boolean") return jsonResponse({ error: "body.enabled must be a boolean" }, 400);
376
390
  if (wantsMode && body.multiAgentMode !== "v1" && body.multiAgentMode !== "default" && body.multiAgentMode !== "v2") {
@@ -379,6 +393,9 @@ export async function handleAgentSettingsRoutes(ctx: ManagementContext): Promise
379
393
  if (wantsKeepNative && typeof body.keepNativeChatGptOnV1 !== "boolean") {
380
394
  return jsonResponse({ error: "body.keepNativeChatGptOnV1 must be a boolean" }, 400);
381
395
  }
396
+ if (wantsV2RoutedDelegationBridge && typeof body.v2RoutedDelegationBridge !== "boolean") {
397
+ return jsonResponse({ error: "body.v2RoutedDelegationBridge must be a boolean" }, 400);
398
+ }
382
399
  if (wantsThreads && (typeof body.maxConcurrentThreadsPerSession !== "number" || !Number.isInteger(body.maxConcurrentThreadsPerSession) || body.maxConcurrentThreadsPerSession < 1)) {
383
400
  return jsonResponse({ error: "body.maxConcurrentThreadsPerSession must be an integer >= 1" }, 400);
384
401
  }
@@ -493,6 +510,12 @@ export async function handleAgentSettingsRoutes(ctx: ManagementContext): Promise
493
510
  model: candidate.model === null || candidate.model === undefined ? null : (candidate.model as string).trim(),
494
511
  };
495
512
  }
513
+ if (wantsV2RoutedDelegationBridge && !wantsFlag && !wantsThreads && !wantsMode && !wantsKeepNative
514
+ && !wantsAgentsEnabled && !wantsMaxDepth && !wantsSubagentInstructions && !wantsModeHintText && !wantsV2NativeParentOverride && !wantsAgentTaskRecovery) {
515
+ const persisted = persistV2RoutedDelegationBridge(config, body.v2RoutedDelegationBridge as boolean);
516
+ if (!persisted.ok) return jsonResponse({ error: `persisting v2RoutedDelegationBridge failed: ${persisted.reason}` }, 502);
517
+ return jsonResponse({ ok: true, v2RoutedDelegationBridge: config.v2RoutedDelegationBridge === true });
518
+ }
496
519
  if (agentTaskRecovery && !wantsFlag && !wantsThreads && !wantsMode && !wantsKeepNative
497
520
  && !wantsAgentsEnabled && !wantsMaxDepth && !wantsSubagentInstructions && !wantsModeHintText && !wantsV2NativeParentOverride) {
498
521
  const persisted = persistAgentTaskRecovery(config, agentTaskRecovery);
@@ -593,6 +616,10 @@ export async function handleAgentSettingsRoutes(ctx: ManagementContext): Promise
593
616
  const persisted = persistV2NativeParentOverride(config, v2NativeParentOverride);
594
617
  if (!persisted.ok) return jsonResponse({ error: `persisting v2NativeParentOverride failed: ${persisted.reason}` }, 502);
595
618
  }
619
+ if (wantsV2RoutedDelegationBridge) {
620
+ const persisted = persistV2RoutedDelegationBridge(config, body.v2RoutedDelegationBridge as boolean);
621
+ if (!persisted.ok) return jsonResponse({ error: `persisting v2RoutedDelegationBridge failed: ${persisted.reason}` }, 502);
622
+ }
596
623
  if (agentTaskRecovery) {
597
624
  const persisted = persistAgentTaskRecovery(config, agentTaskRecovery);
598
625
  if (!persisted.ok) return jsonResponse({ error: `persisting agentTaskRecovery failed: ${persisted.reason}` }, 502);
@@ -613,6 +640,7 @@ export async function handleAgentSettingsRoutes(ctx: ManagementContext): Promise
613
640
  multiAgentModeHintText: getMultiAgentModeHintText(),
614
641
  agentsMaxDepthAppliesWhenV2Disabled: !enabled,
615
642
  v2NativeParentOverride: v2NativeParentOverrideDto(config, enabled),
643
+ v2RoutedDelegationBridge: config.v2RoutedDelegationBridge === true,
616
644
  agentTaskRecovery: agentTaskRecoveryDto(config),
617
645
  warnings,
618
646
  catalogRefresh,
@@ -244,6 +244,7 @@ import { readBoundedResponseBody } from "../../lib/bounded-body";
244
244
  import type { AdmissionLease } from "../../lib/admission";
245
245
  import { supportedLadderFor } from "../effort-policy";
246
246
  import { isThreadSpawnRequest } from "../effort-policy";
247
+ import { isMultiAgentV2Enabled } from "../../codex/features";
247
248
  import {
248
249
  applySubagentModelFallback,
249
250
  maybePrimeSubagentQuota,
@@ -321,6 +322,12 @@ import type { EffectiveSubagentRoster, SpawnAgentSurface } from "../../codex/cat
321
322
 
322
323
  import { buildToolBridgeMaps, collabSurface, injectDeveloperMessage, multiAgentGuidanceText } from "./collaboration";
323
324
  import { decideV2NativeParentOverride } from "./v2-native-parent-override";
325
+ import {
326
+ createV2RoutedDelegationSseRewrite,
327
+ injectV2RoutedDelegationBridge,
328
+ rewriteV2RoutedDelegationCallsInJson,
329
+ type V2RoutedDelegationBridgeContext,
330
+ } from "./v2-routed-delegation-bridge";
324
331
  import { mapCodexAuthContextErrorToResponse } from "./codex-auth-error";
325
332
  import { hasUnreadableEncryptedAgentTask, looksLikeBackendCiphertext, sanitizeEncryptedContentInPlace } from "./encrypted-payload";
326
333
  import { fetchWithHeaderTimeout, providerFetch, safeHostLabel, safeOriginLabel } from "./fetch-helpers";
@@ -2529,11 +2536,13 @@ async function handleResponsesInner(
2529
2536
 
2530
2537
  // Shadow call intercept: rewrite Codex 0.145.0+ helper calls (gpt-5.6-luna).
2531
2538
  // Ancient clients using gpt-5.4-mini remain configurable via sourceModels.
2539
+ let shadowIntercepted = false;
2532
2540
  const _sci = config.shadowCallIntercept;
2533
2541
  if (_sci?.enabled && _sci.model && shouldInterceptShadowCall(
2534
2542
  parsed.modelId,
2535
2543
  _sci.sourceModels,
2536
2544
  )) {
2545
+ shadowIntercepted = true;
2537
2546
  const _sciOriginal = parsed.modelId;
2538
2547
  parsed.modelId = _sci.model;
2539
2548
  if (parsed._rawBody && typeof parsed._rawBody === "object") {
@@ -2596,6 +2605,28 @@ async function handleResponsesInner(
2596
2605
  }
2597
2606
  }
2598
2607
 
2608
+ let v2RoutedDelegationBridge: V2RoutedDelegationBridgeContext | undefined;
2609
+ if (
2610
+ inboundWire === "responses"
2611
+ && config.v2RoutedDelegationBridge === true
2612
+ && config.multiAgentMode === "v2"
2613
+ && isMultiAgentV2Enabled()
2614
+ && isCanonicalOpenAiForwardProvider(route.provider)
2615
+ && collabSurface(parsed) === "v2"
2616
+ && !isThreadSpawnRequest(req.headers)
2617
+ && !req.headers.has("x-openai-subagent")
2618
+ && !options.comboAttempt
2619
+ && parsed._compactionRequest !== true
2620
+ && !shadowIntercepted
2621
+ ) {
2622
+ try {
2623
+ v2RoutedDelegationBridge = injectV2RoutedDelegationBridge(parsed);
2624
+ if (v2RoutedDelegationBridge) toolBridgeMaps = buildToolBridgeMaps(parsed, translatorBudget);
2625
+ } catch (error) {
2626
+ return formatErrorResponse(400, "invalid_request_error", error instanceof Error ? error.message : String(error));
2627
+ }
2628
+ }
2629
+
2599
2630
  const hasUnexpandedPreviousResponse = !!parsed.previousResponseId
2600
2631
  && parsed._previousResponseInputExpanded !== true;
2601
2632
  // Exact account selectors are isolated from Pool-wide quota work. A canonical replay miss must
@@ -3422,7 +3453,12 @@ async function handleResponsesInner(
3422
3453
  && (!parsed.previousResponseId || parsed._previousResponseInputExpanded === true);
3423
3454
  const rememberPassthroughResponse = passthroughRecordEligible
3424
3455
  ? (response: { id?: unknown; output?: unknown; status?: unknown }) =>
3425
- rememberResponseState(parsed._rawBody, response, undefined, responseStateOptions(true))
3456
+ rememberResponseState(
3457
+ v2RoutedDelegationBridge?.requestStateBody ?? parsed._rawBody,
3458
+ response,
3459
+ undefined,
3460
+ responseStateOptions(true),
3461
+ )
3426
3462
  : undefined;
3427
3463
  if (parsed.previousResponseId && !parsed._previousResponseInputExpanded) {
3428
3464
  console.warn(
@@ -4208,6 +4244,17 @@ async function handleResponsesInner(
4208
4244
  options.responsesTerminalRepairScheduler,
4209
4245
  )
4210
4246
  : upstreamResponse.body;
4247
+ // The bridge owns request-scoped item-id admission. Apply it before the
4248
+ // stream is split so the client, inspector, and continuation cache see
4249
+ // the same authorized event history.
4250
+ const bridgeSseRewrite = createV2RoutedDelegationSseRewrite(v2RoutedDelegationBridge);
4251
+ const normalizedPassthroughSseBody = bridgeSseRewrite
4252
+ ? relaySseWithBlockRewrite(
4253
+ passthroughSseBody,
4254
+ payloadRewriteAsBlockRewrite(bridgeSseRewrite),
4255
+ translatorBudget,
4256
+ )
4257
+ : passthroughSseBody;
4211
4258
  const repairConfig = route.provider.responsesItemIdRepair;
4212
4259
  const snapshotRepairEnabled = hasResponsesSnapshotRepair(route.provider.responsesSnapshotRepair);
4213
4260
  const githubCopilotRepairEnabled = route.providerName === "github-copilot";
@@ -4269,7 +4316,7 @@ async function handleResponsesInner(
4269
4316
  const clientBlockRewrite = blockRewrites.length > 0
4270
4317
  ? composeSseBlockRewrites(...blockRewrites)
4271
4318
  : undefined;
4272
- const needsClientRewrite = clientBlockRewrite !== undefined;
4319
+ const needsClientRewrite = bridgeSseRewrite !== undefined || clientBlockRewrite !== undefined;
4273
4320
  // #864: win32 rewrite traffic must never enter the tee()+JS-pull chain
4274
4321
  // (Bun#32111 JS-sink segfault — text frames pass, the terminal block is
4275
4322
  // lost). The eager single reader applies the same rewrites inline.
@@ -4318,7 +4365,7 @@ async function handleResponsesInner(
4318
4365
  onFirstOutput: options.onFirstOutput,
4319
4366
  pinCompletedResponseIdToFirstSeen: githubCopilotRepairEnabled,
4320
4367
  });
4321
- const eagerBody = relaySseEagerBounded(passthroughSseBody, turnAc, {
4368
+ const eagerBody = relaySseEagerBounded(normalizedPassthroughSseBody, turnAc, {
4322
4369
  inspectChunk: chunk => inspector.feed(chunk),
4323
4370
  finishInspection: () => inspector.finish(),
4324
4371
  disposeInspection: () => inspector.dispose(),
@@ -4355,7 +4402,7 @@ async function handleResponsesInner(
4355
4402
  })),
4356
4403
  );
4357
4404
  }
4358
- const [nativeBody, inspectBody] = passthroughSseBody.tee();
4405
+ const [nativeBody, inspectBody] = normalizedPassthroughSseBody.tee();
4359
4406
  const turnAc = new AbortController();
4360
4407
  const clientGone = new AbortController();
4361
4408
  linkAbortSignal(upstream, turnAc.signal);
@@ -4446,8 +4493,12 @@ async function handleResponsesInner(
4446
4493
  restoreImageGenCallsInJson(text, imageGenCallAliases),
4447
4494
  routedNamespaceToolAliases,
4448
4495
  );
4449
- const restored = restoreRoutedCustomCallsInJson(
4496
+ const bridgeNormalized = rewriteV2RoutedDelegationCallsInJson(
4450
4497
  restoredNamespace,
4498
+ v2RoutedDelegationBridge,
4499
+ );
4500
+ const restored = restoreRoutedCustomCallsInJson(
4501
+ bridgeNormalized,
4451
4502
  routedCustomToolNames,
4452
4503
  routedCustomToolRepairNames,
4453
4504
  declaredWireToolNames,
@@ -4495,7 +4546,7 @@ async function handleResponsesInner(
4495
4546
  if (rememberPassthroughResponseChecked) {
4496
4547
  try {
4497
4548
  rememberPassthroughResponseChecked(
4498
- JSON.parse(text) as { id?: unknown; output?: unknown; status?: unknown },
4549
+ JSON.parse(clientJson) as { id?: unknown; output?: unknown; status?: unknown },
4499
4550
  );
4500
4551
  } catch { /* non-JSON despite content-type; recording is best-effort */ }
4501
4552
  }
@@ -0,0 +1,233 @@
1
+ import { isDeepStrictEqual } from "node:util";
2
+ import type { OcxParsedRequest, OcxTool } from "../../types";
3
+ import type { SsePayloadRewrite } from "../sse-payload-rewrite";
4
+
5
+ const MIRROR_NAMESPACE = "ocx_agents";
6
+ const NATIVE_NAMESPACE = "collaboration";
7
+ const MIRRORED_NAMES = new Set(["spawn_agent", "send_message", "followup_task"]);
8
+ const GUIDANCE = "Use this routed-child mirror for collaboration operations.";
9
+ const MAX_SSE_BINDINGS = 128;
10
+
11
+ type RecordValue = Record<string, unknown>;
12
+
13
+ export interface V2RoutedDelegationBridgeContext {
14
+ readonly names: ReadonlySet<string>;
15
+ /** Request snapshot taken before mirror injection, for continuation-cache persistence. */
16
+ readonly requestStateBody: unknown;
17
+ }
18
+
19
+ function isRecord(value: unknown): value is RecordValue {
20
+ return !!value && typeof value === "object" && !Array.isArray(value);
21
+ }
22
+
23
+ function rawToolLists(body: unknown, replayPrefixLength: number): unknown[][] {
24
+ if (!isRecord(body)) return [];
25
+ const lists: unknown[][] = [];
26
+ if (Array.isArray(body.tools)) lists.push(body.tools);
27
+ if (Array.isArray(body.input)) {
28
+ for (const item of body.input.slice(Math.max(0, Math.min(replayPrefixLength, body.input.length)))) {
29
+ if (isRecord(item) && item.type === "additional_tools" && Array.isArray(item.tools)) lists.push(item.tools);
30
+ }
31
+ }
32
+ return lists;
33
+ }
34
+
35
+ function requestStateBody(body: unknown): unknown {
36
+ if (!isRecord(body) || !Array.isArray(body.input)) return body;
37
+ // Only additional_tools containers are mutated below. Clone that narrow path so the
38
+ // continuation cache retains the caller's catalog without copying unrelated context.
39
+ return {
40
+ ...body,
41
+ input: body.input.map(item => (
42
+ isRecord(item) && item.type === "additional_tools" && Array.isArray(item.tools)
43
+ ? { ...item, tools: [...item.tools] }
44
+ : item
45
+ )),
46
+ };
47
+ }
48
+
49
+ function mirrorTool(tool: RecordValue): RecordValue {
50
+ return { ...tool, description: `${GUIDANCE} ${tool.name}.` };
51
+ }
52
+
53
+ function mirrorChildren(group: RecordValue): RecordValue[] {
54
+ if (!Array.isArray(group.tools)) return [];
55
+ return group.tools.filter((tool): tool is RecordValue => (
56
+ isRecord(tool) && tool.type === "function" && typeof tool.name === "string" && MIRRORED_NAMES.has(tool.name)
57
+ )).map(mirrorTool);
58
+ }
59
+
60
+ function mirrorGroup(group: RecordValue): RecordValue {
61
+ return { type: "namespace", name: MIRROR_NAMESPACE, description: GUIDANCE, tools: mirrorChildren(group) };
62
+ }
63
+
64
+ /**
65
+ * Add request-local plaintext collaboration mirrors to raw and parsed Responses catalogs.
66
+ * The caller has already proved this request is eligible; this helper owns no routing policy.
67
+ */
68
+ export function injectV2RoutedDelegationBridge(
69
+ parsed: OcxParsedRequest,
70
+ ): V2RoutedDelegationBridgeContext | undefined {
71
+ const stateBody = requestStateBody(parsed._rawBody);
72
+ const lists = rawToolLists(parsed._rawBody, parsed._replayPrefixLen ?? 0);
73
+ const nativeGroups: Array<{ list: unknown[]; index: number; group: RecordValue }> = [];
74
+ const existing: Array<{ list: unknown[]; index: number; group: RecordValue }> = [];
75
+ for (const list of lists) {
76
+ list.forEach((tool, index) => {
77
+ if (!isRecord(tool) || tool.type !== "namespace") return;
78
+ if (tool.name === NATIVE_NAMESPACE) nativeGroups.push({ list, index, group: tool });
79
+ if (tool.name === MIRROR_NAMESPACE) existing.push({ list, index, group: tool });
80
+ });
81
+ }
82
+ if (nativeGroups.length === 0) return undefined;
83
+
84
+ const names = new Set<string>();
85
+ for (const { group } of nativeGroups) {
86
+ for (const child of mirrorChildren(group)) names.add(child.name as string);
87
+ }
88
+ if (names.size === 0) return undefined;
89
+
90
+ const expected = nativeGroups.map(({ list, index, group }) => ({ list, index: index + 1, group: mirrorGroup(group) }));
91
+ const idempotent = existing.length === expected.length && expected.every(candidate => {
92
+ const actual = candidate.list[candidate.index];
93
+ return isRecord(actual) && isDeepStrictEqual(actual, candidate.group);
94
+ });
95
+ if (existing.length > 0 && !idempotent) {
96
+ throw new Error("v2 routed delegation bridge namespace collision");
97
+ }
98
+ if (!idempotent) {
99
+ for (const { list, index, group } of [...expected].reverse()) list.splice(index, 0, group);
100
+ }
101
+
102
+ const mirrorTools: OcxTool[] = [];
103
+ for (const name of names) {
104
+ const source = parsed.context.tools?.find(tool => tool.namespace === NATIVE_NAMESPACE && tool.name === name);
105
+ if (source) {
106
+ mirrorTools.push({ ...source, namespace: MIRROR_NAMESPACE, description: `${GUIDANCE} ${name}.` });
107
+ continue;
108
+ }
109
+ const raw = nativeGroups.flatMap(entry => mirrorChildren(entry.group)).find(tool => tool.name === name);
110
+ mirrorTools.push({
111
+ name,
112
+ namespace: MIRROR_NAMESPACE,
113
+ description: `${GUIDANCE} ${name}.`,
114
+ parameters: isRecord(raw?.parameters) ? raw.parameters : {},
115
+ });
116
+ }
117
+ if (mirrorTools.length > 0) {
118
+ const present = new Set((parsed.context.tools ?? [])
119
+ .filter(tool => tool.namespace === MIRROR_NAMESPACE)
120
+ .map(tool => tool.name));
121
+ if (mirrorTools.some(tool => !present.has(tool.name))) {
122
+ parsed.context.tools = [...(parsed.context.tools ?? []), ...mirrorTools.filter(tool => !present.has(tool.name))];
123
+ }
124
+ }
125
+ return names.size > 0 ? { names, requestStateBody: stateBody } : undefined;
126
+ }
127
+
128
+ function rewriteValue(
129
+ value: unknown,
130
+ active: V2RoutedDelegationBridgeContext,
131
+ authorizedIds?: ReadonlySet<string>,
132
+ ): { value: unknown; changed: boolean } {
133
+ if (Array.isArray(value)) {
134
+ let changed = false;
135
+ const next = value.map(entry => {
136
+ const rewritten = rewriteValue(entry, active, authorizedIds);
137
+ changed ||= rewritten.changed;
138
+ return rewritten.value;
139
+ });
140
+ return changed ? { value: next, changed: true } : { value, changed: false };
141
+ }
142
+ if (!isRecord(value)) return { value, changed: false };
143
+ let changed = false;
144
+ const entries = Object.entries(value).map(([key, entry]) => {
145
+ const rewritten = rewriteValue(entry, active, authorizedIds);
146
+ changed ||= rewritten.changed;
147
+ return [key, rewritten.value];
148
+ });
149
+ const next: RecordValue = Object.fromEntries(entries);
150
+ const armed =
151
+ value.type === "function_call"
152
+ && value.namespace === MIRROR_NAMESPACE
153
+ && typeof value.name === "string"
154
+ && active.names.has(value.name);
155
+ if (armed && authorizedIds !== undefined) {
156
+ if (typeof value.id !== "string" || !authorizedIds.has(value.id)) {
157
+ const capped = authorizedIds.size >= MAX_SSE_BINDINGS;
158
+ throw Object.assign(new Error(capped
159
+ ? `v2 routed delegation bridge exceeded ${MAX_SSE_BINDINGS} SSE call bindings`
160
+ : "v2 routed delegation bridge received an unbound SSE call"), {
161
+ ...(capped ? { code: "translation_buffer_limit" } : {}),
162
+ });
163
+ }
164
+ }
165
+ if (armed) {
166
+ next.namespace = NATIVE_NAMESPACE;
167
+ next.encrypted_function_args = [];
168
+ changed = true;
169
+ }
170
+ return changed ? { value: next, changed: true } : { value, changed: false };
171
+ }
172
+
173
+ /** Normalize only mirror calls armed by this request in a complete JSON response. */
174
+ export function rewriteV2RoutedDelegationCallsInJson(
175
+ json: string,
176
+ active: V2RoutedDelegationBridgeContext | undefined,
177
+ ): string {
178
+ if (!active || active.names.size === 0) return json;
179
+ let parsed: unknown;
180
+ try { parsed = JSON.parse(json); } catch { return json; }
181
+ const rewritten = rewriteValue(parsed, active);
182
+ return rewritten.changed ? JSON.stringify(rewritten.value) : json;
183
+ }
184
+
185
+ /** Stateful payload rewrite for SSE; item ids bind later argument events to their mirror call. */
186
+ export function createV2RoutedDelegationSseRewrite(
187
+ active: V2RoutedDelegationBridgeContext | undefined,
188
+ ): SsePayloadRewrite | undefined {
189
+ if (!active || active.names.size === 0) return undefined;
190
+ const admittedIds = new Set<string>();
191
+ const openArgumentIds = new Set<string>();
192
+ const bind = (itemId: unknown): void => {
193
+ if (typeof itemId !== "string" || itemId.trim().length === 0) return;
194
+ if (admittedIds.has(itemId)) return;
195
+ if (admittedIds.size >= MAX_SSE_BINDINGS) {
196
+ throw Object.assign(
197
+ new Error(`v2 routed delegation bridge exceeded ${MAX_SSE_BINDINGS} SSE call bindings`),
198
+ { code: "translation_buffer_limit" },
199
+ );
200
+ }
201
+ admittedIds.add(itemId);
202
+ openArgumentIds.add(itemId);
203
+ };
204
+ return payload => {
205
+ let event: unknown;
206
+ try { event = JSON.parse(payload); } catch { return payload; }
207
+ if (!isRecord(event)) return payload;
208
+ const type = event.type;
209
+ const item = isRecord(event.item) ? event.item : undefined;
210
+ const armed = !!item && item.type === "function_call" && item.namespace === MIRROR_NAMESPACE
211
+ && typeof item.name === "string" && active.names.has(item.name);
212
+ const added = type === "response.output_item.added";
213
+ const itemDone = type === "response.output_item.done";
214
+ if (added && armed) bind(item?.id);
215
+ const admittedSnapshot = itemDone && armed && typeof item?.id === "string" && admittedIds.has(item.id);
216
+ const argumentEvent = type === "response.function_call_arguments.delta" || type === "response.function_call_arguments.done";
217
+ const matchedArgument = argumentEvent && typeof event.item_id === "string" && openArgumentIds.has(event.item_id);
218
+ const failedTerminal = type === "response.failed" || type === "response.incomplete";
219
+ const rewritten = rewriteValue(event, active, admittedIds);
220
+ if (type === "response.function_call_arguments.done" && matchedArgument) openArgumentIds.delete(event.item_id as string);
221
+ if (itemDone && admittedSnapshot) openArgumentIds.delete(item!.id as string);
222
+ if (type === "response.completed" || failedTerminal) {
223
+ admittedIds.clear();
224
+ openArgumentIds.clear();
225
+ }
226
+ if (matchedArgument) {
227
+ const next = rewritten.changed && isRecord(rewritten.value) ? rewritten.value : { ...event };
228
+ next.encrypted_function_args = [];
229
+ return JSON.stringify(next);
230
+ }
231
+ return rewritten.changed ? JSON.stringify(rewritten.value) : payload;
232
+ };
233
+ }
@@ -492,6 +492,8 @@ export interface OcxConfig {
492
492
  * Routed parents get v2 tools; Sol/Terra can still spawn Grok/Claude (issue #92).
493
493
  */
494
494
  keepNativeChatGptOnV1?: boolean;
495
+ /** Experimental plaintext delegation bridge for eligible native V2 roots. */
496
+ v2RoutedDelegationBridge?: boolean;
495
497
  /** Optional v2-native parent override for spawn_agent routing. */
496
498
  v2NativeParentOverride?: { enabled?: boolean; model?: string };
497
499
  /** Experimental, default-off ChatGPT recovery for encrypted V2 routed tasks. */