@bitkyc08/opencodex 2.57.0 → 2.59.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.
Files changed (241) hide show
  1. package/README.md +28 -10
  2. package/gui/dist/assets/index-C5IebErG.js +136 -0
  3. package/gui/dist/assets/{index-C5-RdDmD.css → index-OESInAjC.css} +1 -1
  4. package/gui/dist/index.html +2 -2
  5. package/gui/dist/provider-icons/crusoe.svg +1 -0
  6. package/gui/dist/provider-icons/opper.svg +3 -0
  7. package/package.json +2 -2
  8. package/src/adapters/base.ts +11 -1
  9. package/src/adapters/codebuddy/scaffold-guard.ts +5 -4
  10. package/src/adapters/command-code.ts +13 -4
  11. package/src/adapters/cursor/catalog.ts +11 -0
  12. package/src/adapters/cursor/cursor-errors.ts +15 -0
  13. package/src/adapters/cursor/discovery.ts +65 -1
  14. package/src/adapters/cursor/effort-map.ts +16 -2
  15. package/src/adapters/cursor/envelope-echo.ts +55 -2
  16. package/src/adapters/cursor/live-transport.ts +5 -1
  17. package/src/adapters/cursor/message-mapper.ts +3 -2
  18. package/src/adapters/cursor/protobuf-events.ts +110 -11
  19. package/src/adapters/cursor/protobuf-request.ts +27 -6
  20. package/src/adapters/cursor/request-builder.ts +14 -3
  21. package/src/adapters/cursor/text-toolcall.ts +230 -0
  22. package/src/adapters/cursor/thread-continuity.ts +141 -0
  23. package/src/adapters/cursor/tool-guidance.ts +5 -4
  24. package/src/adapters/cursor/types.ts +5 -0
  25. package/src/adapters/cursor.ts +97 -6
  26. package/src/adapters/devin/cloud-direct/chat.ts +11 -2
  27. package/src/adapters/devin/cloud-direct/index.ts +7 -0
  28. package/src/adapters/devin/cloud-direct/stated-reset-retry.ts +103 -0
  29. package/src/adapters/devin.ts +75 -13
  30. package/src/adapters/google-antigravity-wire.ts +29 -2
  31. package/src/adapters/google-http.ts +45 -13
  32. package/src/adapters/google.ts +23 -4
  33. package/src/adapters/mimo-free.ts +32 -17
  34. package/src/adapters/ollama-native.ts +42 -8
  35. package/src/adapters/openai-chat/response-events.ts +61 -0
  36. package/src/adapters/openai-chat.ts +5 -10
  37. package/src/adapters/openai-responses/passthrough.ts +40 -5
  38. package/src/adapters/openai-responses/request-strips.ts +43 -0
  39. package/src/adapters/openai-responses/tool-output-recovery.ts +75 -0
  40. package/src/adapters/openai-responses/tool-schema.ts +19 -7
  41. package/src/adapters/physical-send.ts +50 -0
  42. package/src/adapters/responses-tool-schema.ts +76 -46
  43. package/src/adapters/run-turn-queue.ts +17 -4
  44. package/src/bridge/response-json.ts +2 -2
  45. package/src/bridge/sse.ts +166 -25
  46. package/src/claude/context-windows.ts +22 -0
  47. package/src/claude/outbound.ts +46 -5
  48. package/src/cli/account-api.ts +4 -3
  49. package/src/cli/account-extended.ts +22 -2
  50. package/src/cli/account-orca-import.ts +63 -0
  51. package/src/cli/account.ts +32 -4
  52. package/src/cli/capabilities.ts +40 -0
  53. package/src/cli/claude.ts +29 -1
  54. package/src/cli/codex-cli-update.ts +97 -2
  55. package/src/cli/config-command.ts +35 -18
  56. package/src/cli/dispatch.ts +71 -4
  57. package/src/cli/doctor.ts +197 -2
  58. package/src/cli/help.ts +4 -1
  59. package/src/cli/index.ts +132 -22
  60. package/src/cli/models-runtime.ts +33 -4
  61. package/src/cli/registry.ts +11 -1
  62. package/src/cli/runtime-api.ts +44 -0
  63. package/src/cli/start-args.ts +94 -0
  64. package/src/cli/system-command.ts +72 -1
  65. package/src/cli/uninstall-client-state.ts +12 -0
  66. package/src/client/machine-api.ts +4 -3
  67. package/src/client/machine-listener.ts +14 -1
  68. package/src/clients/config-export/constants.ts +2 -3
  69. package/src/clients/config-export.ts +5 -5
  70. package/src/codex/account-store.ts +81 -5
  71. package/src/codex/auth-api/pool-quota-probe.ts +14 -3
  72. package/src/codex/auth-api/routes.ts +17 -2
  73. package/src/codex/auth-context.ts +58 -20
  74. package/src/codex/catalog/build-entries.ts +25 -4
  75. package/src/codex/catalog/derive-entry.ts +8 -1
  76. package/src/codex/catalog/effort.ts +10 -6
  77. package/src/codex/catalog/gather-capture.ts +1 -0
  78. package/src/codex/catalog/model-hints.ts +37 -5
  79. package/src/codex/catalog/parsing.ts +83 -5
  80. package/src/codex/catalog/reserve-warn.ts +96 -0
  81. package/src/codex/catalog/retained-sync.ts +19 -0
  82. package/src/codex/catalog/routed-gather.ts +42 -3
  83. package/src/codex/cli-installation-identity.ts +210 -0
  84. package/src/codex/cli-installation-targets.ts +158 -0
  85. package/src/codex/convergence.ts +5 -0
  86. package/src/codex/desktop-switches.ts +145 -0
  87. package/src/codex/history-job.ts +5 -1
  88. package/src/codex/history-provider.ts +37 -5
  89. package/src/codex/history-state-open.ts +105 -0
  90. package/src/codex/history-worker.ts +14 -1
  91. package/src/codex/inject/config-toml.ts +44 -2
  92. package/src/codex/inject/remove.ts +145 -7
  93. package/src/codex/inject/restore.ts +204 -32
  94. package/src/codex/inject.ts +6 -9
  95. package/src/codex/lineage.ts +83 -32
  96. package/src/codex/loopback-target.ts +40 -0
  97. package/src/codex/main-account-hard-lock.ts +2 -1
  98. package/src/codex/main-account.ts +10 -3
  99. package/src/codex/main-device-reauth.ts +17 -9
  100. package/src/codex/model-entitlements.ts +60 -1
  101. package/src/codex/native-profile-startup.ts +64 -20
  102. package/src/codex/observed-model-denials.ts +137 -0
  103. package/src/codex/orca-auth-source.ts +94 -0
  104. package/src/codex/orca-import.ts +219 -0
  105. package/src/codex/prompt-text-probe.ts +282 -12
  106. package/src/codex/quota-401-recovery.ts +12 -0
  107. package/src/codex/quota-types.ts +65 -0
  108. package/src/codex/quota.ts +24 -19
  109. package/src/codex/routing/cooldown-math.ts +8 -47
  110. package/src/codex/routing/pin-drain.ts +57 -0
  111. package/src/codex/routing.ts +13 -15
  112. package/src/codex/subagent-model-fallback.ts +94 -0
  113. package/src/codex/windows-installation-files.ts +224 -0
  114. package/src/combos/failover.ts +122 -5
  115. package/src/config/atomic-write.ts +83 -8
  116. package/src/config/diagnostics.ts +21 -0
  117. package/src/config/load-degrade.ts +15 -0
  118. package/src/config/pending-teardown.ts +8 -0
  119. package/src/config/process-state.ts +36 -3
  120. package/src/config/provider-relative-send-path.ts +16 -0
  121. package/src/config/proxy-env.ts +23 -5
  122. package/src/config/schema/config-schema.ts +23 -0
  123. package/src/config/schema/leaf-validators.ts +65 -17
  124. package/src/generated/compatibility-version.json +337 -201
  125. package/src/generated/model-metadata.ts +1 -1
  126. package/src/lib/bounded-body.ts +4 -2
  127. package/src/lib/bounded-subprocess.ts +62 -10
  128. package/src/lib/destination-policy.ts +48 -6
  129. package/src/lib/errors.ts +3 -15
  130. package/src/lib/local-destinations.ts +32 -5
  131. package/src/lib/provider-outbound.ts +3 -3
  132. package/src/lib/proxy-env.ts +70 -3
  133. package/src/lib/request-execution-budget.ts +11 -3
  134. package/src/lib/response-body-inactivity.ts +193 -0
  135. package/src/lib/retry-delay.ts +69 -0
  136. package/src/lib/socks5-fetch.ts +631 -0
  137. package/src/lib/spend-reservation-ledger.ts +115 -9
  138. package/src/lib/windows-secret-acl.ts +151 -15
  139. package/src/lib/windows-user-principal.ts +5 -1
  140. package/src/lib/workflow-budget.ts +145 -8
  141. package/src/oauth/account-quota-rank.ts +72 -15
  142. package/src/oauth/generic-account-failover.ts +40 -27
  143. package/src/oauth/orcarouter.ts +15 -2
  144. package/src/oauth/store.ts +8 -0
  145. package/src/providers/codex-capacity.ts +9 -0
  146. package/src/providers/derive.ts +6 -0
  147. package/src/providers/devin-provider-merge-migration.ts +33 -12
  148. package/src/providers/free-directory.ts +20 -2
  149. package/src/providers/key-failover.ts +261 -7
  150. package/src/providers/model-discovery.ts +19 -7
  151. package/src/providers/model-rename-migration.ts +1 -0
  152. package/src/providers/openai-sidecar.ts +4 -0
  153. package/src/providers/opencode-go-transport.ts +14 -5
  154. package/src/providers/quota/report-cache.ts +3 -0
  155. package/src/providers/registry/entries-core.ts +11 -0
  156. package/src/providers/registry/entries-extended.ts +146 -28
  157. package/src/providers/registry/model-seeds.ts +136 -29
  158. package/src/providers/registry/types.ts +9 -0
  159. package/src/responses/apply-patch-envelope.ts +44 -11
  160. package/src/responses/bridge-search-replay-cache.ts +152 -0
  161. package/src/responses/code-mode-helper-compat.ts +26 -16
  162. package/src/responses/custom-tool-compat.ts +1 -1
  163. package/src/responses/hosted-tool-policy.ts +85 -2
  164. package/src/responses/schema.ts +9 -2
  165. package/src/responses/spill-store.ts +17 -0
  166. package/src/responses/state/body-policy.ts +25 -0
  167. package/src/responses/state/spill-queue.ts +8 -6
  168. package/src/responses/state.ts +3 -22
  169. package/src/router.ts +4 -0
  170. package/src/server/auth-cors.ts +27 -0
  171. package/src/server/chat-completions.ts +9 -4
  172. package/src/server/chat-native-sse.ts +26 -9
  173. package/src/server/chat-native.ts +10 -4
  174. package/src/server/claude-messages.ts +24 -2
  175. package/src/server/gui-static.ts +36 -2
  176. package/src/server/inbound-body-admission.ts +187 -0
  177. package/src/server/index/websocket-handler.ts +48 -1
  178. package/src/server/index.ts +15 -19
  179. package/src/server/management/api-access.ts +3 -4
  180. package/src/server/management/config-routes.ts +57 -10
  181. package/src/server/management/provider-capability-config.ts +35 -7
  182. package/src/server/management/provider-routes.ts +70 -18
  183. package/src/server/models-capabilities.ts +24 -3
  184. package/src/server/proxy-liveness.ts +97 -2
  185. package/src/server/relay.ts +17 -24
  186. package/src/server/request-log.ts +25 -1
  187. package/src/server/responses/adapter-continuation.ts +71 -27
  188. package/src/server/responses/adapter-delivery.ts +39 -8
  189. package/src/server/responses/adapter-dispatch.ts +52 -24
  190. package/src/server/responses/codex-ws-exchange.ts +65 -4
  191. package/src/server/responses/combo-stream-preflight.ts +68 -5
  192. package/src/server/responses/compact.ts +60 -11
  193. package/src/server/responses/core-codex-account.ts +83 -22
  194. package/src/server/responses/core-combo.ts +26 -0
  195. package/src/server/responses/core-normalize.ts +12 -5
  196. package/src/server/responses/core-options.ts +3 -0
  197. package/src/server/responses/fetch-helpers.ts +72 -3
  198. package/src/server/responses/native-injection-protocol.ts +42 -0
  199. package/src/server/responses/native-injection-replay.ts +105 -0
  200. package/src/server/responses/native-injection.ts +242 -0
  201. package/src/server/responses/native-response-control.ts +56 -0
  202. package/src/server/responses/native-response-json.ts +14 -0
  203. package/src/server/responses/native-response-output.ts +37 -0
  204. package/src/server/responses/native-steering-log.ts +44 -0
  205. package/src/server/responses/native-steering-policy.ts +49 -0
  206. package/src/server/responses/native-steering-replay.ts +126 -0
  207. package/src/server/responses/native-steering-settings.ts +76 -0
  208. package/src/server/responses/native-steering.ts +400 -0
  209. package/src/server/responses/native-tool-results.ts +130 -0
  210. package/src/server/responses/passthrough-delivery.ts +21 -1
  211. package/src/server/responses/passthrough-dispatch.ts +146 -49
  212. package/src/server/responses/passthrough-execution.ts +11 -1
  213. package/src/server/responses/request-prepare.ts +70 -0
  214. package/src/server/responses/request-send-budget.ts +84 -7
  215. package/src/server/responses/request-sidecar-auth.ts +16 -8
  216. package/src/server/responses/request-spend.ts +38 -9
  217. package/src/server/responses/request-transport.ts +13 -10
  218. package/src/server/responses/run-turn-execution.ts +20 -5
  219. package/src/server/responses/sidecar-execution.ts +2 -0
  220. package/src/server/responses/ws-upstream.ts +23 -2
  221. package/src/server/responses-custom-tool-repair.ts +2 -2
  222. package/src/server/sse-frame-buffer.ts +12 -10
  223. package/src/server/sse-payload-rewrite.ts +36 -9
  224. package/src/server/stop-teardown.ts +8 -1
  225. package/src/server/system-env-shell.ts +5 -1
  226. package/src/server/system-env.ts +7 -1
  227. package/src/server/workflow-refusal.ts +56 -2
  228. package/src/server/ws-bridge.ts +16 -1
  229. package/src/service/cli.ts +29 -7
  230. package/src/service/guards.ts +10 -0
  231. package/src/service/health.ts +43 -0
  232. package/src/service/state.ts +7 -2
  233. package/src/types/accounts.ts +4 -0
  234. package/src/types/config.ts +104 -3
  235. package/src/types/provider.ts +32 -0
  236. package/src/types/request.ts +7 -1
  237. package/src/types/wire.ts +9 -1
  238. package/src/usage/expected-prices.ts +28 -0
  239. package/src/usage/log.ts +87 -4
  240. package/src/web-search/passthrough-bridge.ts +39 -5
  241. package/gui/dist/assets/index-Cz7CLdif.js +0 -128
@@ -0,0 +1,44 @@
1
+ import { inspectResponseLogSsePayload, usageFromResponsesPayload, type RequestLogContext } from "../request-log";
2
+ import type { OcxUsage } from "../../types";
3
+ import { MAX_NATIVE_STEERING_RESPONSES } from "./native-steering";
4
+
5
+ /** Count every terminal once. Control frames may echo user input: never sample them. */
6
+ export function createNativeSteeringLogObserver(logCtx: RequestLogContext, onFirstOutput?: () => void): (payload: string) => void {
7
+ let outputSeen = false;
8
+ const usages = new Map<string, OcxUsage>();
9
+ return payload => {
10
+ let event: { type?: string; delta?: unknown; response?: { id?: string; usage?: unknown; incomplete_details?: { reason?: string } } };
11
+ try { event = JSON.parse(payload); } catch { return; }
12
+ if (event.type?.startsWith("response.steer.") || event.type?.startsWith("response.inject.")) return;
13
+ if (!outputSeen && event.type?.endsWith(".delta") && typeof event.delta === "string" && event.delta.length) {
14
+ outputSeen = true; onFirstOutput?.();
15
+ }
16
+ // A steered parent is not a failed logical turn. Keep its usage, but never
17
+ // label an eventual successful successor as an upstream failure.
18
+ if (!(event.type === "response.incomplete" && event.response?.incomplete_details?.reason === "steered")) {
19
+ inspectResponseLogSsePayload(logCtx, payload);
20
+ }
21
+ const terminal = ["response.completed", "response.failed", "response.incomplete"].includes(event.type ?? "");
22
+ if (terminal && typeof event.response?.id === "string") {
23
+ const usage = usageFromResponsesPayload(event.response.usage);
24
+ if (usage && (usages.has(event.response.id) || usages.size < MAX_NATIVE_STEERING_RESPONSES)) {
25
+ // Retain numeric counters only, never arbitrary rawUsage metadata.
26
+ const counters: OcxUsage = { inputTokens: usage.inputTokens, outputTokens: usage.outputTokens };
27
+ for (const key of ["totalTokens", "cachedInputTokens", "cacheReadInputTokens", "cacheCreationInputTokens", "reasoningOutputTokens"] as const) {
28
+ if (typeof usage[key] === "number") counters[key] = usage[key];
29
+ }
30
+ usages.set(event.response.id, counters);
31
+ }
32
+ }
33
+ if (!usages.size) return;
34
+ const total: OcxUsage = { inputTokens: 0, outputTokens: 0 };
35
+ for (const usage of usages.values()) {
36
+ for (const key of ["inputTokens", "outputTokens", "totalTokens", "cachedInputTokens", "cacheReadInputTokens", "cacheCreationInputTokens", "reasoningOutputTokens"] as const) {
37
+ const value = usage[key];
38
+ if (typeof value === "number" && Number.isFinite(value)) total[key] = (total[key] ?? 0) + value;
39
+ }
40
+ }
41
+ logCtx.usage = total;
42
+ if (logCtx.activeAttempt) logCtx.activeAttempt.usage = total;
43
+ };
44
+ }
@@ -0,0 +1,49 @@
1
+ import type { OcxConfig, OcxParsedRequest, OcxProviderConfig } from "../../types";
2
+ import { nativeEffortClamp, shouldApplyNativeEffortClamp } from "../../codex/catalog";
3
+ import { applyEffortCap, applyPinnedEffort, effortCapAppliesTo, prepareEffortNormalization, stripEmptyLadderEffort, supportedLadderFor } from "../effort-policy";
4
+ import { collabSurface } from "./collaboration";
5
+ import { mapRoutedResponsesReasoningEffort, normalizeConfiguredReasoningSummaryDelivery,
6
+ stripDisabledReasoningSummaries, stripDisabledVerbosity, stripUnsupportedReasoningSummaryDelivery } from "../../adapters/openai-responses/reasoning";
7
+ import { NativeSteeringError } from "./native-steering";
8
+ import { nativeResponseRecord as record } from "./native-response-json";
9
+ import { STEERING_MUTABLE_SETTINGS } from "./native-steering-settings";
10
+
11
+ type Frame = Record<string, unknown>;
12
+
13
+ /** Reuse normal route-specific policy on a private override, never reroute or rebuild saved tool results. */
14
+ export function createSteeringSettingsNormalizer(
15
+ parsed: OcxParsedRequest,
16
+ route: { provider: OcxProviderConfig; providerName: string; modelId: string },
17
+ config: OcxConfig,
18
+ headers: Headers,
19
+ ): (frame: Frame) => Frame {
20
+ const selector = prepareEffortNormalization(parsed, route);
21
+ const surface = collabSurface(parsed);
22
+ return (frame: Frame): Frame => {
23
+ if (route.provider.authMode === "forward" && Object.hasOwn(frame, "max_output_tokens")) {
24
+ throw new NativeSteeringError("steering_settings_unsupported", "This subscription route does not accept max_output_tokens; omit that override.");
25
+ }
26
+ // The frame is cloned by the channel. Policy receives only generation keys,
27
+ // not saved results, tool declarations, credentials or caller response IDs.
28
+ let body: Frame = Object.fromEntries(STEERING_MUTABLE_SETTINGS.filter(key => Object.hasOwn(frame, key)).map(key => [key, frame[key]]));
29
+ if (Object.hasOwn(body, "reasoning")) {
30
+ const candidate = { ...parsed, options: { ...parsed.options,
31
+ reasoning: record(body.reasoning) && typeof body.reasoning.effort === "string" ? body.reasoning.effort : undefined }, _rawBody: body };
32
+ applyPinnedEffort(candidate, route, config, selector);
33
+ if (effortCapAppliesTo(surface, headers, config, parsed._compactionRequest === true)) {
34
+ applyEffortCap(candidate, headers, config, supportedLadderFor(route));
35
+ }
36
+ const clamp = shouldApplyNativeEffortClamp(route.providerName, route.provider, route.modelId)
37
+ ? nativeEffortClamp(route.modelId, candidate.options.reasoning) : null;
38
+ if (clamp && record(body.reasoning)) body.reasoning.effort = clamp;
39
+ body = mapRoutedResponsesReasoningEffort(body, route.provider, route.modelId) as Frame;
40
+ body.reasoning = stripEmptyLadderEffort(body.reasoning, supportedLadderFor(route));
41
+ }
42
+ body = stripDisabledVerbosity(stripDisabledReasoningSummaries(
43
+ normalizeConfiguredReasoningSummaryDelivery(stripUnsupportedReasoningSummaryDelivery(body, route.modelId), route.provider, route.modelId),
44
+ route.provider, route.modelId), route.provider, route.modelId) as Frame;
45
+ const next = { ...frame };
46
+ for (const key of STEERING_MUTABLE_SETTINGS) if (Object.hasOwn(frame, key)) next[key] = body[key];
47
+ return next;
48
+ };
49
+ }
@@ -0,0 +1,126 @@
1
+ import { nativeResponseOutput } from "./native-response-output";
2
+
3
+ /**
4
+ * Connection-local replay journal. Only input committed by response.created enters
5
+ * a successor's prefix. Uncommitted/rejected steering never enters the shared
6
+ * continuation cache. Bodies are bounded and discarded at connection teardown.
7
+ */
8
+ export const MAX_NATIVE_STEERING_REPLAY_BYTES = 32 * 1024 * 1024;
9
+ type Frame = Record<string, unknown>;
10
+ /** Accept JSON object envelopes without treating arrays as records. */
11
+ function record(value: unknown): value is Frame {
12
+ return value !== null && typeof value === "object" && !Array.isArray(value);
13
+ }
14
+ /** Normalize string input to one user message while preserving array order. */
15
+ function inputItems(input: unknown): unknown[] {
16
+ if (typeof input === "string") return [{ type: "message", role: "user", content: [{ type: "input_text", text: input }] }];
17
+ return Array.isArray(input) ? input : [];
18
+ }
19
+ export interface NativeSteeringReplayObserver {
20
+ submitted(frame: Frame): () => void;
21
+ observe(frame: Frame): void;
22
+ dispose(): void;
23
+ }
24
+
25
+ /** Keep bounded, connection-local input until a validated successor commits it. */
26
+ export class NativeSteeringReplay implements NativeSteeringReplayObserver {
27
+ private prefix: unknown[];
28
+ private bytes: number;
29
+ private current?: string;
30
+ private previousOutput: unknown[] = [];
31
+ private outputItems = new Map<number, Frame>();
32
+ private submissions: Array<{ parent: string; input: unknown[]; id?: string; bytes: number }> = [];
33
+ private explicitInput: unknown[] = [];
34
+ private explicitBytes = 0;
35
+
36
+ /** Capture the initial prefix and reject over-budget history before dispatch. */
37
+ constructor(input: unknown, private readonly remember: (input: unknown[], response: Frame) => void) {
38
+ this.prefix = [...inputItems(input)];
39
+ this.bytes = Buffer.byteLength(JSON.stringify(this.prefix));
40
+ this.check();
41
+ }
42
+ /** Reject overflow rather than silently truncating retained conversation input. */
43
+ private check(): void {
44
+ if (this.bytes > MAX_NATIVE_STEERING_REPLAY_BYTES) throw new Error("Native steering replay exceeded its bounded history budget; input was not silently truncated.");
45
+ }
46
+ /** Reserve replay bytes before send and return a rollback for synchronous failure. */
47
+ submitted(frame: Frame): () => void {
48
+ const input = inputItems(frame.input);
49
+ const bytes = Buffer.byteLength(JSON.stringify(input));
50
+ this.bytes += bytes;
51
+ try { this.check(); } catch (error) { this.bytes -= bytes; throw error; }
52
+ if (frame.type === "response.steer") {
53
+ const submission = { parent: String(frame.previous_response_id), input, bytes };
54
+ this.submissions.push(submission);
55
+ return () => {
56
+ const index = this.submissions.indexOf(submission);
57
+ if (index >= 0) { this.submissions.splice(index, 1); this.bytes -= bytes; }
58
+ };
59
+ }
60
+ this.explicitInput = input;
61
+ this.explicitBytes = bytes;
62
+ return () => { this.explicitInput = []; this.bytes -= this.explicitBytes; this.explicitBytes = 0; };
63
+ }
64
+ /** Apply ordered upstream events; only created successors commit queued input. */
65
+ observe(frame: Frame): void {
66
+ const response = record(frame.response) ? frame.response : undefined;
67
+ const steer = record(frame.steer) ? frame.steer : undefined;
68
+ if (frame.type === "response.steer.accepted") {
69
+ const first = this.submissions.find(item => item.parent === steer?.previous_response_id && item.id === undefined);
70
+ if (!first || typeof steer?.id !== "string") throw new Error("Native steering replay acceptance does not match submitted input");
71
+ first.id = steer.id;
72
+ } else if (frame.type === "response.steer.failed") {
73
+ const index = this.submissions.findIndex(item => steer?.id !== undefined
74
+ ? item.id === steer.id
75
+ : item.parent === steer?.previous_response_id && item.id === undefined);
76
+ if (index >= 0) {
77
+ const [failed] = this.submissions.splice(index, 1);
78
+ this.bytes -= failed.bytes;
79
+ }
80
+ } else if (frame.type === "response.created") {
81
+ if (this.current) {
82
+ const committed = this.submissions.filter(item => item.parent === this.current && item.id !== undefined);
83
+ // Byte-valid histories may exceed the runtime's positional-argument limit.
84
+ for (const item of this.previousOutput) this.prefix.push(item);
85
+ for (const submission of committed) {
86
+ for (const item of submission.input) this.prefix.push(item);
87
+ }
88
+ for (const item of this.explicitInput) this.prefix.push(item);
89
+ this.submissions = this.submissions.filter(item => !committed.includes(item));
90
+ }
91
+ this.explicitInput = [];
92
+ this.explicitBytes = 0;
93
+ this.previousOutput = [];
94
+ this.outputItems.clear();
95
+ this.current = String(response?.id);
96
+ } else if (frame.type === "response.output_item.done") {
97
+ const index = frame.output_index as number;
98
+ if (!Number.isSafeInteger(index) || index < 0 || index > 10_000 || !record(frame.item)) throw new Error("Native steering replay output identity is invalid");
99
+ const previous = this.outputItems.get(index);
100
+ if (previous !== undefined) this.bytes -= Buffer.byteLength(JSON.stringify(previous));
101
+ this.bytes += Buffer.byteLength(JSON.stringify(frame.item));
102
+ this.check();
103
+ this.outputItems.set(index, frame.item);
104
+ } else if (response && ["response.completed", "response.incomplete", "response.failed"].includes(String(frame.type))) {
105
+ const doneItems = [...this.outputItems.entries()].sort((a, b) => a[0] - b[0]).map(([, item]) => item);
106
+ const output = nativeResponseOutput(this.outputItems, response.output);
107
+ for (const item of doneItems) this.bytes -= Buffer.byteLength(JSON.stringify(item));
108
+ this.bytes += Buffer.byteLength(JSON.stringify(output));
109
+ this.check();
110
+ this.outputItems.clear();
111
+ this.previousOutput = output;
112
+ // Failed and steered parents are never presented to shared state as completed.
113
+ // Their output is used only when a validated successor commits that prefix.
114
+ if (frame.type === "response.completed") this.remember(this.prefix, { ...response, output });
115
+ }
116
+ }
117
+ /** Release retained input, output and queued submissions when the owner detaches. */
118
+ dispose(): void {
119
+ this.prefix = [];
120
+ this.previousOutput = [];
121
+ this.submissions = [];
122
+ this.explicitInput = [];
123
+ this.outputItems.clear();
124
+ this.bytes = 0;
125
+ }
126
+ }
@@ -0,0 +1,76 @@
1
+ import { REASONING_SUMMARY_DELIVERY_VALUES } from "../../types/wire";
2
+ import { nativeResponseRecord as record } from "./native-response-json";
3
+
4
+ type Frame = Record<string, unknown>;
5
+ /** Only generation settings may change without selecting a new route or tool surface. */
6
+ export const STEERING_MUTABLE_SETTINGS = ["reasoning", "text", "max_output_tokens", "stream_options"] as const;
7
+ export const isSteeringMutableSetting = (key: string): boolean =>
8
+ (STEERING_MUTABLE_SETTINGS as readonly string[]).includes(key);
9
+
10
+ /** Bound nested structured-output schemas before fingerprinting or copying them. */
11
+ function boundedJson(value: unknown): boolean {
12
+ const pending = [{ value, depth: 0 }];
13
+ let nodes = 0;
14
+ while (pending.length) {
15
+ const current = pending.pop()!;
16
+ if (++nodes > 20_000 || current.depth > 64) return false;
17
+ if (current.value && typeof current.value === "object") {
18
+ for (const value of Object.values(current.value)) pending.push({ value, depth: current.depth + 1 });
19
+ }
20
+ }
21
+ return true;
22
+ }
23
+ const only = (value: Frame, keys: readonly string[]) => Object.keys(value).every(key => keys.includes(key));
24
+ const optionalEnum = (value: unknown, choices: readonly string[]) => value === undefined || value === null
25
+ || (typeof value === "string" && choices.includes(value));
26
+
27
+ /** Reject malformed overrides rather than treating them as omitted settings. */
28
+ export function validSteeringSettings(frame: Frame): boolean {
29
+ for (const key of STEERING_MUTABLE_SETTINGS) {
30
+ if (!Object.hasOwn(frame, key)) continue;
31
+ const value = frame[key];
32
+ if (!boundedJson(value)) return false;
33
+ if (value === null) continue;
34
+ if (key === "max_output_tokens") {
35
+ if (!Number.isSafeInteger(value) || (value as number) < 1) return false;
36
+ continue;
37
+ }
38
+ if (!record(value)) return false;
39
+ if (key === "reasoning") {
40
+ if (!only(value, ["effort", "summary", "generate_summary"])
41
+ || !optionalEnum(value.effort, ["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"])
42
+ || !optionalEnum(value.summary, ["auto", "concise", "detailed", "none"])
43
+ || !optionalEnum(value.generate_summary, ["auto", "concise", "detailed", "none"])) return false;
44
+ } else if (key === "stream_options") {
45
+ if (!only(value, ["reasoning_summary_delivery", "include_usage", "include_obfuscation"])
46
+ || !optionalEnum(value.reasoning_summary_delivery, REASONING_SUMMARY_DELIVERY_VALUES)
47
+ || [value.include_usage, value.include_obfuscation].some(item => item !== undefined && typeof item !== "boolean")) return false;
48
+ } else {
49
+ if (!only(value, ["format", "verbosity"]) || !optionalEnum(value.verbosity, ["low", "medium", "high"])) return false;
50
+ if (value.format === undefined || value.format === null) continue;
51
+ const format = value.format;
52
+ if (!record(format)) return false;
53
+ if (format.type === "text" || format.type === "json_object") {
54
+ if (!only(format, ["type"])) return false;
55
+ } else if (format.type === "json_schema") {
56
+ if (!only(format, ["type", "name", "schema", "strict", "description"])
57
+ || typeof format.name !== "string" || !/^[a-zA-Z0-9_-]{1,64}$/.test(format.name)
58
+ || !record(format.schema)
59
+ || (format.strict != null && typeof format.strict !== "boolean")
60
+ || (format.description !== undefined && typeof format.description !== "string")) return false;
61
+ } else return false;
62
+ }
63
+ }
64
+ return true;
65
+ }
66
+
67
+ /** Overlay only validated generation keys on the current, already authorized wire settings. */
68
+ export function mergeSteeringContinuation(base: Frame, frame: Frame): Frame {
69
+ const outgoing: Frame = { ...base, input: frame.input, previous_response_id: frame.previous_response_id };
70
+ for (const key of STEERING_MUTABLE_SETTINGS) {
71
+ if (!Object.hasOwn(frame, key)) continue;
72
+ if (frame[key] === undefined) delete outgoing[key];
73
+ else outgoing[key] = frame[key];
74
+ }
75
+ return outgoing;
76
+ }