@yansigit/opencodex 2.36.1-dev.20260829.35 → 2.36.1-dev.20260829.39

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.
Files changed (42) hide show
  1. package/gui/dist/assets/{ApiKeys-Dmc3vk4f.js → ApiKeys-BDLkAqHs.js} +1 -1
  2. package/gui/dist/assets/{Claude-B8nBn_TE.js → Claude-CbTHl2B0.js} +1 -1
  3. package/gui/dist/assets/{CodexSet-RKnAl3Rr.js → CodexSet-BWMY8up4.js} +1 -1
  4. package/gui/dist/assets/{FileIntegrationPage-LBYKB5gf.js → FileIntegrationPage-BFywFnu_.js} +1 -1
  5. package/gui/dist/assets/{Grok-B6nDl-pq.js → Grok-D7_rD_UK.js} +1 -1
  6. package/gui/dist/assets/{Integrations-DPcI3mwy.js → Integrations-BM5cNRt8.js} +2 -2
  7. package/gui/dist/assets/{IntegrationsOverview-BLgOYwUO.js → IntegrationsOverview-DUL8qhww.js} +1 -1
  8. package/gui/dist/assets/{Logs-BkM5ic5m.js → Logs-BG9kIs3A.js} +1 -1
  9. package/gui/dist/assets/Models-BJWzO0Kh.js +1 -0
  10. package/gui/dist/assets/{NumberStepper-Dsiahf_V.js → NumberStepper-K9tocDBx.js} +1 -1
  11. package/gui/dist/assets/{Providers-DFBGM2BG.js → Providers-BLweq_Hw.js} +1 -1
  12. package/gui/dist/assets/{RestoreDialog-0YZ3cmhw.js → RestoreDialog-D9aPE4O0.js} +1 -1
  13. package/gui/dist/assets/{Startup-BRXwp250.js → Startup-CkcpO3Jv.js} +1 -1
  14. package/gui/dist/assets/{Storage-D0Ve2fj0.js → Storage-YIqUviJJ.js} +1 -1
  15. package/gui/dist/assets/Subagents-C1AfLEl9.js +1 -0
  16. package/gui/dist/assets/{Usage-C0T5SUrf.js → Usage-BerHlEN0.js} +1 -1
  17. package/gui/dist/assets/codex-stale-banner-D4gvSWnO.js +1 -0
  18. package/gui/dist/assets/{data-surface-D8RXShV2.js → data-surface-BtO3lwam.js} +1 -1
  19. package/gui/dist/assets/{data-surface-Bfu-W0pf.js → data-surface-DCct07RT.js} +1 -1
  20. package/gui/dist/assets/{index-WNYAjf83.js → index-Bs27ITj_.js} +3 -3
  21. package/gui/dist/assets/{model-display-hWp9VTdv.js → model-display-KCeq505s.js} +1 -1
  22. package/gui/dist/assets/{provider-payload-F7zzYMeI.js → provider-payload-DHs_ctdK.js} +1 -1
  23. package/gui/dist/assets/{section-tabs-DEASco68.js → section-tabs-BP7gEPK6.js} +1 -1
  24. package/gui/dist/assets/shared-BdYGV-yJ.js +69 -0
  25. package/gui/dist/index.html +2 -2
  26. package/package.json +1 -1
  27. package/src/codex/app-server-processes.ts +160 -54
  28. package/src/codex/internal/catalog-writer.ts +3 -0
  29. package/src/codex/subagent-defaults.ts +114 -0
  30. package/src/codex/sync.ts +5 -0
  31. package/src/generated/compatibility-version.json +13 -13
  32. package/src/server/effort-policy.ts +43 -12
  33. package/src/server/index.ts +6 -0
  34. package/src/server/management/agent-settings-routes.ts +2 -0
  35. package/src/server/request-log.ts +7 -0
  36. package/src/server/responses/collaboration.ts +20 -4
  37. package/src/server/responses/core.ts +6 -4
  38. package/src/server/ws-bridge.ts +5 -0
  39. package/src/usage/log.ts +9 -0
  40. package/gui/dist/assets/Models-uh6UTNK5.js +0 -1
  41. package/gui/dist/assets/Subagents-TaTl5eEL.js +0 -1
  42. package/gui/dist/assets/shared-p0R307IX.js +0 -69
@@ -10,7 +10,7 @@
10
10
  },
11
11
  {
12
12
  "path": "package.json",
13
- "sha256": "237debdd8a2a62e40e44d7e5f1ccd6cd641abe3cb694d2edb60fd004c698e4e7"
13
+ "sha256": "0056c7ad8f06271be7aceee8ea055ad817e2aeeb3ede46fcf21b48e812c1c420"
14
14
  },
15
15
  {
16
16
  "path": "scripts/model-metadata.source.json",
@@ -730,7 +730,7 @@
730
730
  },
731
731
  {
732
732
  "path": "src/codex/app-server-processes.ts",
733
- "sha256": "605e11ba25a31e7d6a2bda30325794bfee9829b6c617c2865e90e6e4ed92a00a"
733
+ "sha256": "00e00faf3a032c13db509e4b38c3c7dc9e1a7b532a1b48dc34d7d5de10d80615"
734
734
  },
735
735
  {
736
736
  "path": "src/codex/app-server-restart-service.ts",
@@ -910,7 +910,7 @@
910
910
  },
911
911
  {
912
912
  "path": "src/codex/internal/catalog-writer.ts",
913
- "sha256": "a38a3b3b0735997cac608eb0f0a2147e15d5d3a2bf8bbd73f3bb4b538fc47517"
913
+ "sha256": "d59f218683c102f5f629ee092a0076deeefb01cc8a20ecde13d197d3ec4a034b"
914
914
  },
915
915
  {
916
916
  "path": "src/codex/internal/history-writer.ts",
@@ -1098,7 +1098,7 @@
1098
1098
  },
1099
1099
  {
1100
1100
  "path": "src/codex/subagent-defaults.ts",
1101
- "sha256": "66ac781355642d64e6926676f05d8e44f68bc27cc26c2edd66c9e0ffd19d5613"
1101
+ "sha256": "08e3efa192430891d9d221d41e5124177c2c03656e3eb832087fdfb778a7703e"
1102
1102
  },
1103
1103
  {
1104
1104
  "path": "src/codex/subagent-model-fallback.ts",
@@ -1106,7 +1106,7 @@
1106
1106
  },
1107
1107
  {
1108
1108
  "path": "src/codex/sync.ts",
1109
- "sha256": "faceb0d8b621e6471b34f9e1d9519d1feb36a33d39ad984fc3acb5748bf52a7b"
1109
+ "sha256": "b2b413f985c058024f5cf0714255ab8b3f74858d159631d22201c237c9aea134"
1110
1110
  },
1111
1111
  {
1112
1112
  "path": "src/codex/transition-state.ts",
@@ -2662,7 +2662,7 @@
2662
2662
  },
2663
2663
  {
2664
2664
  "path": "src/server/effort-policy.ts",
2665
- "sha256": "fc944973196eb482e4fe0310a64e46d69455d9f9bffb984a13d94581ff5bcff9"
2665
+ "sha256": "d00bd3a42975adec4c62560d57e564da28e4fc7476cc57fea7103f3d61080fc3"
2666
2666
  },
2667
2667
  {
2668
2668
  "path": "src/server/github-copilot-responses-repair.ts",
@@ -2682,7 +2682,7 @@
2682
2682
  },
2683
2683
  {
2684
2684
  "path": "src/server/index.ts",
2685
- "sha256": "571ee5edbbff9311f3c5bf044c5330b03b8defc47dd6c05ea118ece2b91c1b3d"
2685
+ "sha256": "e39a7d1b9812bdf91677f27be4b4ef0a486015170f41e69fb3d8c330970f0aba"
2686
2686
  },
2687
2687
  {
2688
2688
  "path": "src/server/lifecycle.ts",
@@ -2710,7 +2710,7 @@
2710
2710
  },
2711
2711
  {
2712
2712
  "path": "src/server/management/agent-settings-routes.ts",
2713
- "sha256": "223da28c5ed1951dbaf014ebac9e26c8035d14884b50b7985294c0d39e1c0ab8"
2713
+ "sha256": "2ba31a9adb7ba798a7a9fdeada3a2430ad2497205ec3671f4c3a5bfbab68f8c5"
2714
2714
  },
2715
2715
  {
2716
2716
  "path": "src/server/management/api-access.ts",
@@ -2874,7 +2874,7 @@
2874
2874
  },
2875
2875
  {
2876
2876
  "path": "src/server/request-log.ts",
2877
- "sha256": "a8980d727760364b174ec8a2ac020d482f5fcf58371a4557f806e2b487355542"
2877
+ "sha256": "b05d8d7b84f9cc6bf7cdd003aae7b2ef3791eed9353ee9441f10b87dd34b1621"
2878
2878
  },
2879
2879
  {
2880
2880
  "path": "src/server/responses-custom-tool-repair.ts",
@@ -2934,7 +2934,7 @@
2934
2934
  },
2935
2935
  {
2936
2936
  "path": "src/server/responses/collaboration.ts",
2937
- "sha256": "ce48f612fd22f4bdff7198089411aeabbd43228130eeaa464e5705a73a814237"
2937
+ "sha256": "c06f21efd00814eb1021b73e4fe16e693a93205768f8f1a55c63e854e657075d"
2938
2938
  },
2939
2939
  {
2940
2940
  "path": "src/server/responses/combo-stream-preflight.ts",
@@ -2946,7 +2946,7 @@
2946
2946
  },
2947
2947
  {
2948
2948
  "path": "src/server/responses/core.ts",
2949
- "sha256": "90ce7232996139a594f4055155db1cba61692f680c85e9d6d5601a0657c12c49"
2949
+ "sha256": "ff08c82f4289d3227725566fd7637e8f7b382bb0a1f303846f9c2da9c7889acf"
2950
2950
  },
2951
2951
  {
2952
2952
  "path": "src/server/responses/empty-completion-guard.ts",
@@ -3034,7 +3034,7 @@
3034
3034
  },
3035
3035
  {
3036
3036
  "path": "src/server/ws-bridge.ts",
3037
- "sha256": "515a150f3b1d71f29efe566629dea2056b7562f669c0f98464cf9000577541b3"
3037
+ "sha256": "0f7ea03225ddd992fad14dab1dda99a3890a380061f99f0e94fb05efb33e421f"
3038
3038
  },
3039
3039
  {
3040
3040
  "path": "src/service-manager-probe.ts",
@@ -3250,7 +3250,7 @@
3250
3250
  },
3251
3251
  {
3252
3252
  "path": "src/usage/log.ts",
3253
- "sha256": "2178aa1e4466a1bfa96806a1d4348ae0e8fc717d418c9f2f76dd37466d3a2013"
3253
+ "sha256": "a559008076a3d43365d4aa87f7d1b24c939822d915c143f7221cd3b915dc1f96"
3254
3254
  },
3255
3255
  {
3256
3256
  "path": "src/usage/summary.ts",
@@ -17,6 +17,43 @@ import { modelInList } from "../types";
17
17
  import { codexEffortRank, configuredReasoningEfforts, isCodexReasoningEffort, modelRecordValue } from "../reasoning-effort";
18
18
  import { catalogModelEfforts } from "../codex/catalog";
19
19
 
20
+ export type AgentKind = "main" | "subagent" | "internal";
21
+
22
+ type MarkerValue = { value: string; valid: boolean } | undefined;
23
+
24
+ function turnMetadataMarker(headers: Headers): MarkerValue {
25
+ const raw = headers.get("x-codex-turn-metadata");
26
+ if (raw === null) return undefined;
27
+ try {
28
+ const parsed = JSON.parse(raw) as unknown;
29
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return { value: "", valid: false };
30
+ const metadata = parsed as Record<string, unknown>;
31
+ if (!("subagent_kind" in metadata)) return undefined;
32
+ const value = metadata.subagent_kind;
33
+ if (typeof value !== "string" || value.trim().length === 0) return { value: "", valid: false };
34
+ return { value, valid: true };
35
+ } catch {
36
+ return { value: "", valid: false };
37
+ }
38
+ }
39
+
40
+ /** Classify Responses-origin headers without retaining any marker values. */
41
+ export function classifyAgentKind(headers: Headers, traffic?: "responses"): AgentKind | undefined {
42
+ const headerRaw = headers.get("x-openai-subagent");
43
+ const headerMarker: MarkerValue = headerRaw === null
44
+ ? undefined
45
+ : { value: headerRaw, valid: headerRaw.length > 0 };
46
+ const metadataMarker = turnMetadataMarker(headers);
47
+
48
+ // A genuine spawn marker is authoritative even when a sibling marker is stale or malformed.
49
+ if (headerMarker?.value === "collab_spawn" || metadataMarker?.value === "thread_spawn") return "subagent";
50
+ const present = [headerMarker, metadataMarker].filter((marker): marker is NonNullable<MarkerValue> => marker !== undefined);
51
+ if (present.some(marker => !marker.valid)) return undefined;
52
+ if (present.length > 1 && present[0].value !== present[1].value) return undefined;
53
+ if (present.length > 0) return "internal";
54
+ return traffic === "responses" ? "main" : undefined;
55
+ }
56
+
20
57
  /**
21
58
  * True when the request carries codex-rs's spawned-child markers, matched EXACTLY.
22
59
  * Source of truth (openai/codex @ 6138909d): every collab-spawned child turn sends
@@ -30,16 +67,8 @@ import { catalogModelEfforts } from "../codex/catalog";
30
67
  * sources — responses_metadata.rs subagent_source). Those are maintenance turns,
31
68
  * not spawned children, and must never trip subagentEffortCap.
32
69
  */
33
- export function isThreadSpawnRequest(headers: Headers): boolean {
34
- if (headers.get("x-openai-subagent") === "collab_spawn") return true;
35
- const turnMeta = headers.get("x-codex-turn-metadata");
36
- if (!turnMeta) return false;
37
- try {
38
- const parsed = JSON.parse(turnMeta) as { subagent_kind?: unknown };
39
- return parsed.subagent_kind === "thread_spawn";
40
- } catch {
41
- return false;
42
- }
70
+ export function isThreadSpawnRequest(headers: Headers, agentKind?: AgentKind): boolean {
71
+ return (agentKind ?? classifyAgentKind(headers)) === "subagent";
43
72
  }
44
73
 
45
74
  /** The effective ceiling for this turn, or undefined when no configured cap applies. */
@@ -73,10 +102,11 @@ export function effortCapAppliesTo(
73
102
  headers: Headers,
74
103
  config: OcxConfig,
75
104
  compaction = false,
105
+ agentKind?: AgentKind,
76
106
  ): boolean {
77
107
  if (compaction) return false;
78
108
  if (config.multiAgentMode === "v1") return false;
79
- return surface === "v2" || isThreadSpawnRequest(headers);
109
+ return surface === "v2" || isThreadSpawnRequest(headers, agentKind);
80
110
  }
81
111
 
82
112
  /**
@@ -185,8 +215,9 @@ export function applyEffortCap(
185
215
  headers: Headers,
186
216
  config: OcxConfig,
187
217
  supported?: readonly string[] | undefined,
218
+ agentKind?: AgentKind,
188
219
  ): { from: string; to: string; subagent: boolean } | null {
189
- const subagent = isThreadSpawnRequest(headers);
220
+ const subagent = isThreadSpawnRequest(headers, agentKind);
190
221
  const cap = effortCapFor(config, subagent);
191
222
  if (!cap) return null;
192
223
  const resolved = resolveCappedEffort(cap, supported);
@@ -113,6 +113,7 @@ import {
113
113
  type RequestLogEntry,
114
114
  } from "./request-log";
115
115
  import { sessionLaneIdFromRequest } from "./request-log-conversation";
116
+ import { classifyAgentKind } from "./effort-policy";
116
117
  export {
117
118
  addFinalRequestLog,
118
119
  filterRequestLogs,
@@ -972,12 +973,14 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
972
973
  // binding. They are the same object for the public listener, but the
973
974
  // unauthenticated loopback listener (#1102) is a second Bun.serve, and handing its
974
975
  // request to the public server's upgrade would fail or cross sockets.
976
+ const agentKind = classifyAgentKind(req.headers, "responses");
975
977
  if (requestServer.upgrade(req, {
976
978
  data: buildResponsesWsData(
977
979
  selectForwardHeaders(req.headers),
978
980
  admission,
979
981
  websocketLease,
980
982
  sessionLaneIdFromRequest(req.headers),
983
+ agentKind,
981
984
  ),
982
985
  })) return undefined as unknown as Response;
983
986
  websocketLease.release();
@@ -1395,6 +1398,7 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
1395
1398
  provider: "unknown",
1396
1399
  ...admissionFields(admission),
1397
1400
  inboundProtocol: "responses",
1401
+ agentKind: classifyAgentKind(req.headers, "responses"),
1398
1402
  };
1399
1403
  return runAdmittedHttpTurn(req, policy, async turnAdmissionLease => {
1400
1404
  let response: Response;
@@ -1508,6 +1512,7 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
1508
1512
  provider: "unknown",
1509
1513
  ...admissionFields(admission),
1510
1514
  inboundProtocol: "responses",
1515
+ agentKind: classifyAgentKind(req.headers, "responses"),
1511
1516
  };
1512
1517
  if (req.headers.get("x-opencodex-grok") === "1") logCtx.surface = "grok";
1513
1518
  let logged = false;
@@ -1857,6 +1862,7 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
1857
1862
  provider: "unknown",
1858
1863
  ...(wsAdmission ? admissionFields(wsAdmission) : {}),
1859
1864
  inboundProtocol: "responses",
1865
+ agentKind: ws.data.agentKind,
1860
1866
  };
1861
1867
  let logged = false;
1862
1868
  const finalizeLog = (
@@ -797,6 +797,7 @@ export async function handleAgentSettingsRoutes(ctx: ManagementContext): Promise
797
797
  const disabled = new Set(config.disabledModels ?? []);
798
798
  const { listCatalogNativeSlugs } = await import("../../codex/catalog");
799
799
  const { CODEX_REASONING_LEVELS } = await import("../../reasoning-effort");
800
+ const { resolveNativeDefaultState } = await import("../../codex/subagent-defaults");
800
801
  const nativeModels = listCatalogNativeSlugs()
801
802
  .filter(slug => !disabled.has(slug))
802
803
  .map(slug => ({ provider: "openai", model: slug, namespaced: slug }));
@@ -811,6 +812,7 @@ export async function handleAgentSettingsRoutes(ctx: ManagementContext): Promise
811
812
  model: config.injectionModel ?? null,
812
813
  effort: config.injectionEffort ?? null,
813
814
  prompt: config.injectionPrompt ?? null,
815
+ nativeDefaultState: await resolveNativeDefaultState(config),
814
816
  efforts: CODEX_REASONING_LEVELS.map(l => l.effort),
815
817
  available: [...nativeModels, ...routedModels],
816
818
  });
@@ -17,6 +17,7 @@ import type { AdapterTierMetadata } from "../providers/fastwire";
17
17
  import { redactSecretString, sanitizeLogMetadataString } from "../lib/redact";
18
18
  import {
19
19
  appendUsageEntry,
20
+ isKnownAgentKind,
20
21
  isKnownAdmissionKind,
21
22
  isKnownInboundProtocol,
22
23
  isKnownUsageSurface,
@@ -31,6 +32,7 @@ import {
31
32
  type PersistedUsageEntry,
32
33
  type UsageStatus,
33
34
  } from "../usage/log";
35
+ import type { AgentKind } from "./effort-policy";
34
36
  import {
35
37
  appendUsageDebug,
36
38
  isUsageDebugEnabled,
@@ -63,6 +65,7 @@ export interface RequestLogContext {
63
65
  * product: widening that enum would merge Responses and Chat Completions,
64
66
  * since both leave it undefined. */
65
67
  inboundProtocol?: "responses" | "chat" | "messages";
68
+ agentKind?: AgentKind;
66
69
  /** Stable non-PII Codex Pool account identity for durable usage attribution. */
67
70
  accountLogLabel?: string;
68
71
  requestedModel?: string;
@@ -143,6 +146,7 @@ export interface RequestLogEntry {
143
146
  * product: widening that enum would merge Responses and Chat Completions,
144
147
  * since both leave it undefined. */
145
148
  inboundProtocol?: "responses" | "chat" | "messages";
149
+ agentKind?: AgentKind;
146
150
  accountLogLabel?: string;
147
151
  /** Best-effort chat/session correlation for Logs grouping (#330). */
148
152
  conversationId?: string;
@@ -256,6 +260,7 @@ export function requestLogEntryFromPersistedUsage(entry: PersistedUsageEntry): R
256
260
  timestamp: entry.timestamp,
257
261
  model: entry.model,
258
262
  provider: entry.provider,
263
+ ...(isKnownAgentKind(entry.agentKind) ? { agentKind: entry.agentKind } : {}),
259
264
  ...(entry.firstOutputMs !== undefined ? { firstOutputMs: entry.firstOutputMs } : {}),
260
265
  ...(isKnownUsageSurface(entry.surface) ? { surface: entry.surface } : {}),
261
266
  ...(entry.conversationId ? { conversationId: entry.conversationId } : {}),
@@ -378,6 +383,7 @@ export function addRequestLog(entry: RequestLogEntry) {
378
383
  ...(entry.apiKeyId ? { apiKeyId: entry.apiKeyId } : {}),
379
384
  ...(isKnownAdmissionKind(entry.admissionKind) ? { admissionKind: entry.admissionKind } : {}),
380
385
  ...(isKnownInboundProtocol(entry.inboundProtocol) ? { inboundProtocol: entry.inboundProtocol } : {}),
386
+ ...(isKnownAgentKind(entry.agentKind) ? { agentKind: entry.agentKind } : {}),
381
387
  ...(isPersistableAccountLogLabel(entry.accountLogLabel)
382
388
  ? { accountLogLabel: entry.accountLogLabel }
383
389
  : {}),
@@ -970,6 +976,7 @@ export function addFinalRequestLog(
970
976
  ...(logCtx.apiKeyId ? { apiKeyId: logCtx.apiKeyId } : {}),
971
977
  ...(logCtx.admissionKind ? { admissionKind: logCtx.admissionKind } : {}),
972
978
  ...(logCtx.inboundProtocol ? { inboundProtocol: logCtx.inboundProtocol } : {}),
979
+ ...(logCtx.agentKind ? { agentKind: logCtx.agentKind } : {}),
973
980
  ...(isPersistableAccountLogLabel(logCtx.accountLogLabel)
974
981
  ? { accountLogLabel: logCtx.accountLogLabel }
975
982
  : {}),
@@ -77,6 +77,7 @@ import { registerTurn, trackStreamLifetime, unregisterTurn } from "../lifecycle"
77
77
  import { redactSecretString } from "../../lib/redact";
78
78
  import { readBoundedResponseBody } from "../../lib/bounded-body";
79
79
  import { supportedLadderFor } from "../effort-policy";
80
+ import { resolveNativeDefaultState, type NativeDefaultState } from "../../codex/subagent-defaults";
80
81
  import {
81
82
  beginRequestAttempt,
82
83
  catalogModelSupportsServiceTier,
@@ -216,11 +217,18 @@ export interface MultiAgentGuidanceOptions {
216
217
  subagentModelFallback?: string[];
217
218
  injectionPrompt?: string;
218
219
  subagentRoles?: OcxSubagentRole[];
220
+ nativeDefaultState?: NativeDefaultState;
221
+ syncCodexSubagentDefaults?: boolean;
219
222
  }
220
223
 
221
224
 
222
225
 
223
226
  export interface MultiAgentGuidanceDeps {
227
+ resolveNativeDefaultState?: (config: {
228
+ injectionModel?: string;
229
+ injectionEffort?: string;
230
+ syncCodexSubagentDefaults?: boolean;
231
+ }) => NativeDefaultState | Promise<NativeDefaultState>;
224
232
  resolveEffectiveSubagentRoster?: (
225
233
  configuredModels: readonly string[],
226
234
  surface: SpawnAgentSurface,
@@ -308,6 +316,8 @@ export async function multiAgentGuidanceText(
308
316
  subagentModelFallback,
309
317
  injectionPrompt,
310
318
  subagentRoles,
319
+ nativeDefaultState: configuredNativeDefaultState,
320
+ syncCodexSubagentDefaults,
311
321
  } = options;
312
322
  const activeAccountNamespace = codexAccountNamespace?.length
313
323
  ? codexAccountNamespace
@@ -340,6 +350,10 @@ export async function multiAgentGuidanceText(
340
350
  if (catalogState.state === "stale" || catalogState.state === "unknown") {
341
351
  return null;
342
352
  }
353
+ const nativeDefaultState = configuredNativeDefaultState
354
+ ?? await (deps.resolveNativeDefaultState ?? resolveNativeDefaultState)({
355
+ injectionModel, injectionEffort, syncCodexSubagentDefaults,
356
+ });
343
357
  // codex-rs supplies the Proactive text on v2; the proxy only adds model-designation
344
358
  // guidance, and only when there is something concrete to designate: a configured
345
359
  // injectionModel and/or a roster entry that resolves in the injected catalog.
@@ -417,7 +431,7 @@ export async function multiAgentGuidanceText(
417
431
  // fallback only for explicit routed/account-qualified ids.
418
432
  const promptModel = preferred?.model
419
433
  ?? (injectionModel?.includes("/") ? injectionModel : undefined);
420
- return `<multi_agent_mode>${applyInjectionPlaceholders(injectionPrompt, promptModel, injectionEffort, roster, fallbackGuidance, customRolesText)}</multi_agent_mode>`;
434
+ return `<multi_agent_mode>${applyInjectionPlaceholders(injectionPrompt, promptModel, injectionEffort, roster, fallbackGuidance, customRolesText, nativeDefaultState)}</multi_agent_mode>`;
421
435
  }
422
436
  if (!preferred && roster === "" && fallbackGuidance === "" && customRolesText === "") return null;
423
437
  const preamble = "When the active spawn_agent tool supports optional \"model\" or \"reasoning_effort\" overrides, "
@@ -425,13 +439,14 @@ export async function multiAgentGuidanceText(
425
439
  + "When setting either override, set fork_turns to \"none\" "
426
440
  + "(or a positive turn count such as \"3\"; full-history forks reject overrides) "
427
441
  + "and make the task message self-contained. "
428
- + "When specifying model overrides, omit agent_type or use \"worker\" to prevent enforcing locked default models. "
442
+ + "When specifying model overrides, preserve any caller-provided agent_type; do not replace it with \"worker\". "
429
443
  + "Subagent exec runs in a pure V8 isolate without require('fs'); "
430
444
  + "escape nested template literals in tools.apply_patch to prevent JavaScript syntax errors (e.g., write \\` and \\\${var}).";
431
445
  let preferredText = "";
432
446
  if (preferred) {
433
447
  preferredText = ` Preferred sub-agent: model "${preferred.model}"`
434
448
  + (injectionEffort ? `, reasoning_effort "${injectionEffort}"` : "")
449
+ + `; nativeDefaultState: ${nativeDefaultState}.`
435
450
  + " — use it unless the user names another.";
436
451
  }
437
452
  const specialist = (catalog: string): string =>
@@ -465,13 +480,14 @@ export async function multiAgentGuidanceText(
465
480
 
466
481
  export const V2_GUIDANCE_CHAR_BUDGET = 1200;
467
482
 
468
- export function applyInjectionPlaceholders(prompt: string, model?: string, effort?: string, roster?: string, fallback?: string, roles?: string): string {
483
+ export function applyInjectionPlaceholders(prompt: string, model?: string, effort?: string, roster?: string, fallback?: string, roles?: string, nativeDefaultState?: NativeDefaultState): string {
469
484
  return prompt
470
485
  .replaceAll("{{model}}", model ?? "")
471
486
  .replaceAll("{{effort}}", effort ?? "")
472
487
  .replaceAll("{{roster}}", roster ?? "")
473
488
  .replaceAll("{{fallback}}", fallback ?? "")
474
- .replaceAll("{{roles}}", roles ?? "");
489
+ .replaceAll("{{roles}}", roles ?? "")
490
+ .replaceAll("{{nativeDefaultState}}", nativeDefaultState ?? "");
475
491
  }
476
492
 
477
493
 
@@ -1879,6 +1879,7 @@ async function applyFinalRouteRequestNormalization(args: {
1879
1879
  subagentModelFallback: config.subagentModelFallback,
1880
1880
  injectionPrompt: config.injectionPrompt,
1881
1881
  subagentRoles: config.subagentRoles,
1882
+ syncCodexSubagentDefaults: config.syncCodexSubagentDefaults === true,
1882
1883
  });
1883
1884
  if (guidance) {
1884
1885
  injectDeveloperMessage(parsed, guidance);
@@ -1893,8 +1894,8 @@ async function applyFinalRouteRequestNormalization(args: {
1893
1894
  {
1894
1895
  const { applyEffortCap, effortCapAppliesTo, supportedLadderFor } = await import("../effort-policy");
1895
1896
  const surface = collabSurface(parsed);
1896
- if (effortCapAppliesTo(surface, req.headers, config, parsed._compactionRequest === true)) {
1897
- const capped = applyEffortCap(parsed, req.headers, config, supportedLadderFor(route));
1897
+ if (effortCapAppliesTo(surface, req.headers, config, parsed._compactionRequest === true, logCtx.agentKind)) {
1898
+ const capped = applyEffortCap(parsed, req.headers, config, supportedLadderFor(route), logCtx.agentKind);
1898
1899
  if (capped) {
1899
1900
  logCtx.requestedEffort = `${capped.from}->${capped.to}`;
1900
1901
  if (isInjectionDebugEnabled()) {
@@ -2057,6 +2058,7 @@ export async function handleComboResponses(
2057
2058
  const childLog: RequestLogContext = {
2058
2059
  model: pick.target.model,
2059
2060
  provider: pick.target.provider,
2061
+ ...(logCtx.agentKind ? { agentKind: logCtx.agentKind } : {}),
2060
2062
  ...(logCtx.conversationId ? { conversationId: logCtx.conversationId } : {}),
2061
2063
  ...(logCtx.surface ? { surface: logCtx.surface } : {}),
2062
2064
  };
@@ -2613,7 +2615,7 @@ async function handleResponsesInner(
2613
2615
  && isMultiAgentV2Enabled()
2614
2616
  && isCanonicalOpenAiForwardProvider(route.provider)
2615
2617
  && collabSurface(parsed) === "v2"
2616
- && !isThreadSpawnRequest(req.headers)
2618
+ && !isThreadSpawnRequest(req.headers, logCtx.agentKind)
2617
2619
  && !req.headers.has("x-openai-subagent")
2618
2620
  && !options.comboAttempt
2619
2621
  && parsed._compactionRequest !== true
@@ -2632,7 +2634,7 @@ async function handleResponsesInner(
2632
2634
  // Exact account selectors are isolated from Pool-wide quota work. A canonical replay miss must
2633
2635
  // also fail closed without polling quota upstream. Cached fallback state can still select a
2634
2636
  // provider with native continuation support below.
2635
- const threadSpawn = isThreadSpawnRequest(req.headers);
2637
+ const threadSpawn = isThreadSpawnRequest(req.headers, logCtx.agentKind);
2636
2638
  const initialSubagentFallbackChain = threadSpawn && !options.comboAttempt
2637
2639
  ? resolveSubagentFallbackChain(parsed, config)
2638
2640
  : null;
@@ -7,6 +7,7 @@ import type { ResponsesTerminalStatus } from "../bridge";
7
7
  import type { DataPlaneAdmission } from "./auth-cors";
8
8
  import type { AdmissionLease, AdmissionReservation } from "../lib/admission";
9
9
  import { BoundedSseFrameBuffer } from "./sse-frame-buffer";
10
+ import { classifyAgentKind, type AgentKind } from "./effort-policy";
10
11
 
11
12
  const OPEN = 1;
12
13
  type ResponsesTerminalReporter = (status: ResponsesTerminalStatus) => void;
@@ -23,6 +24,7 @@ const SAFE_RESPONSE_HEADER_EXACT = new Set([
23
24
 
24
25
  export interface WsData {
25
26
  headers?: Headers; // base inbound forward headers only; per-turn auth refresh injects current pool tokens
27
+ agentKind?: AgentKind;
26
28
  /**
27
29
  * Resolved once at the handshake. Auth is handshake-time only on this path, so
28
30
  * the per-frame log contexts have no request headers left to re-resolve from.
@@ -67,11 +69,14 @@ export function buildResponsesWsData(
67
69
  admission: DataPlaneAdmission,
68
70
  admissionLease?: AdmissionReservation<ServerWebSocket<WsData>>,
69
71
  sessionLaneId?: string,
72
+ agentKind?: AgentKind,
70
73
  ): WsData {
71
74
  // Auth is handshake-time only on this path: the per-frame contexts have no
72
75
  // request headers left to re-resolve from, so the decision rides along here.
76
+ const resolvedAgentKind = arguments.length >= 5 ? agentKind : classifyAgentKind(headers, "responses");
73
77
  return {
74
78
  headers,
79
+ agentKind: resolvedAgentKind,
75
80
  admission,
76
81
  ...(admissionLease ? { admissionLease } : {}),
77
82
  ...(sessionLaneId ? { sessionLaneId } : {}),
package/src/usage/log.ts CHANGED
@@ -9,6 +9,7 @@ import { usageDisplayTotalTokens } from "./totals";
9
9
  import type { AttemptTierOutcome, OcxUsage } from "../types";
10
10
  import { normalizeRouteDecisionTrace, type RouteDecisionTraceV1 } from "../routing/trace";
11
11
  import { CODEX_ACCOUNT_LOG_LABEL_RE } from "../codex/account-label";
12
+ import type { AgentKind } from "../server/effort-policy";
12
13
 
13
14
  export type UsageStatus = "reported" | "unreported" | "unsupported" | "estimated";
14
15
  export type CodexUsageAccountLogLabel = "main" | `p${string}`;
@@ -80,6 +81,8 @@ export interface PersistedUsageAttempt {
80
81
  }
81
82
 
82
83
  export interface PersistedUsageEntry {
84
+ /** Responses request origin; absent for unrelated traffic and old rows. */
85
+ agentKind?: AgentKind;
83
86
  requestedAlias?: string;
84
87
  requestId: string;
85
88
  timestamp: number;
@@ -163,6 +166,11 @@ const KNOWN_ADMISSION_KINDS = new Set<NonNullable<PersistedUsageEntry["admission
163
166
  const KNOWN_INBOUND_PROTOCOLS = new Set<NonNullable<PersistedUsageEntry["inboundProtocol"]>>([
164
167
  "responses", "chat", "messages",
165
168
  ]);
169
+ const KNOWN_AGENT_KINDS = new Set<AgentKind>(["main", "subagent", "internal"]);
170
+
171
+ export function isKnownAgentKind(value: unknown): value is AgentKind {
172
+ return typeof value === "string" && KNOWN_AGENT_KINDS.has(value as AgentKind);
173
+ }
166
174
 
167
175
  /** Same closed-set discipline as `isKnownUsageSurface`: an old or corrupted row
168
176
  * carrying an unexpected value drops the field instead of poisoning the enum. */
@@ -454,6 +462,7 @@ function normalizeUsageEntry(entry: PersistedUsageEntry): PersistedUsageEntry {
454
462
  timestamp: entry.timestamp,
455
463
  provider: entry.provider,
456
464
  model: entry.model,
465
+ ...(isKnownAgentKind(entry.agentKind) ? { agentKind: entry.agentKind } : {}),
457
466
  ...(isKnownUsageSurface(entry.surface) ? { surface: entry.surface } : {}),
458
467
  ...(typeof entry.apiKeyId === "string" && entry.apiKeyId.trim()
459
468
  // Deliberately NOT capped. `capMetadataString` protects free-form metadata