@bitkyc08/opencodex 2.65.0 → 2.66.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 (213) hide show
  1. package/README.md +14 -0
  2. package/gui/dist/assets/App-DyHlFeNB.js +51 -0
  3. package/gui/dist/assets/App-gkoJIEOV.css +1 -0
  4. package/gui/dist/assets/{Tray-D4bA1vff.js → Tray-CI8cVKWu.js} +1 -1
  5. package/gui/dist/assets/index-BiwnVAUm.js +86 -0
  6. package/gui/dist/assets/index-ComVsdSk.css +1 -0
  7. package/gui/dist/assets/{tray-data-BZXhB3qz.js → tray-data-_WyYzbMt.js} +1 -1
  8. package/gui/dist/index.html +2 -2
  9. package/package.json +1 -2
  10. package/src/adapters/anthropic/beta-allowlist.ts +80 -0
  11. package/src/adapters/anthropic/passthrough.ts +221 -0
  12. package/src/adapters/anthropic-image-codec.ts +56 -19
  13. package/src/adapters/anthropic-image-normalize.ts +20 -10
  14. package/src/adapters/anthropic.ts +55 -24
  15. package/src/adapters/coding-agent/protocol.ts +29 -19
  16. package/src/adapters/cursor/live-transport.ts +18 -3
  17. package/src/adapters/cursor/native-exec-shell.ts +18 -63
  18. package/src/adapters/cursor/native-exec.ts +5 -2
  19. package/src/adapters/cursor/native-foreground-shell.ts +44 -0
  20. package/src/adapters/devin/cloud-direct/chat.ts +7 -1
  21. package/src/adapters/exec-tool-result-normalize.ts +4 -1
  22. package/src/adapters/google.ts +12 -0
  23. package/src/adapters/ollama-native.ts +32 -1
  24. package/src/adapters/openai-chat/serialized-tool-call-content.ts +131 -34
  25. package/src/adapters/openai-chat.ts +6 -4
  26. package/src/adapters/openai-responses/passthrough.ts +1 -0
  27. package/src/adapters/openai-responses/reasoning.ts +45 -1
  28. package/src/bridge/response-json.ts +10 -2
  29. package/src/chat/outbound.ts +104 -52
  30. package/src/claude/claude-code-block.ts +16 -0
  31. package/src/claude/context-windows.ts +52 -4
  32. package/src/claude/desktop-first-party.ts +25 -8
  33. package/src/claude/inbound.ts +22 -0
  34. package/src/claude/intercept/connect-proxy.ts +37 -4
  35. package/src/claude/intercept/proxy-auth.ts +166 -0
  36. package/src/claude/intercept/runtime.ts +21 -0
  37. package/src/claude/intercept/settings.ts +33 -12
  38. package/src/claude/outbound.ts +45 -27
  39. package/src/cli/account-extended.ts +47 -15
  40. package/src/cli/account-target.ts +115 -0
  41. package/src/cli/account.ts +20 -28
  42. package/src/cli/agent.ts +10 -1
  43. package/src/cli/api-protocols.ts +206 -0
  44. package/src/cli/capabilities.ts +86 -0
  45. package/src/cli/combo.ts +2 -2
  46. package/src/cli/connect.ts +127 -3
  47. package/src/cli/dispatch.ts +8 -0
  48. package/src/cli/doctor.ts +33 -0
  49. package/src/cli/help.ts +2 -0
  50. package/src/cli/link.ts +288 -0
  51. package/src/cli/registry.ts +23 -0
  52. package/src/cli/runtime-api.ts +79 -0
  53. package/src/cli/system-command.ts +21 -2
  54. package/src/client/connect.ts +125 -23
  55. package/src/client/hub-relay.ts +12 -8
  56. package/src/client/link-join.ts +323 -0
  57. package/src/client/link-relay.ts +241 -0
  58. package/src/client/link-state.ts +110 -0
  59. package/src/client/link-teardown.ts +63 -0
  60. package/src/client/link-tunnel.ts +465 -0
  61. package/src/client/machine-listener.ts +14 -5
  62. package/src/client/runtime.ts +91 -42
  63. package/src/client/state.ts +4 -0
  64. package/src/codex/catalog/build-entries.ts +4 -0
  65. package/src/codex/catalog/derive-entry.ts +5 -0
  66. package/src/codex/catalog/parsing.ts +4 -0
  67. package/src/codex/catalog/retained-sync.ts +4 -1
  68. package/src/codex/desktop-switches.ts +88 -8
  69. package/src/codex/inject/plan.ts +16 -3
  70. package/src/codex/inject.ts +3 -0
  71. package/src/codex/internal/catalog-writer.ts +13 -2
  72. package/src/combos/failover.ts +3 -2
  73. package/src/combos/index.ts +12 -0
  74. package/src/combos/jev.ts +646 -0
  75. package/src/combos/request.ts +5 -0
  76. package/src/combos/resolve.ts +17 -15
  77. package/src/combos/types.ts +47 -3
  78. package/src/config/atomic-write.ts +3 -0
  79. package/src/config/live-reconcile.ts +20 -4
  80. package/src/config/persist-unlocked.ts +20 -7
  81. package/src/config/schema/config-schema.ts +16 -0
  82. package/src/config/schema/leaf-validators.ts +38 -1
  83. package/src/generated/compatibility-version.json +416 -128
  84. package/src/integrations/omp-yaml-source.ts +109 -4
  85. package/src/lab/automation/orchestrator.ts +60 -4
  86. package/src/lab/events/limits.ts +1 -1
  87. package/src/lib/bounded-body.ts +55 -38
  88. package/src/lib/config-ownership.ts +11 -22
  89. package/src/lib/local-desktop-snapshot-capability.ts +56 -0
  90. package/src/lib/local-management-capability.ts +25 -3
  91. package/src/lib/optional-shutdown-hooks.ts +17 -0
  92. package/src/lib/state-store-registrations.ts +2 -2
  93. package/src/lib/windows-elevation.ts +62 -25
  94. package/src/lib/windows-secret-acl.ts +114 -23
  95. package/src/link/admission-wait.ts +73 -0
  96. package/src/link/compensation.ts +100 -0
  97. package/src/link/fingerprint.ts +21 -0
  98. package/src/link/paths.ts +19 -0
  99. package/src/link/ports.ts +12 -0
  100. package/src/link/routes.ts +32 -0
  101. package/src/link/ssh-argv.ts +170 -0
  102. package/src/link/ssh-config.ts +152 -0
  103. package/src/link/ssh-runner.ts +161 -0
  104. package/src/link/status-projection.ts +96 -0
  105. package/src/link/store.ts +139 -0
  106. package/src/link/supervisor.ts +404 -0
  107. package/src/link/tunnel-state.ts +91 -0
  108. package/src/protocols/baseline.ts +88 -0
  109. package/src/protocols/codecs/chat.ts +17 -0
  110. package/src/protocols/codecs/messages.ts +18 -0
  111. package/src/protocols/codecs/responses.ts +17 -0
  112. package/src/protocols/contract.ts +185 -0
  113. package/src/protocols/dto.ts +267 -0
  114. package/src/protocols/encoders/adapter-events.ts +876 -0
  115. package/src/protocols/encoders/chat.ts +243 -0
  116. package/src/protocols/encoders/messages.ts +462 -0
  117. package/src/protocols/envelope.ts +43 -0
  118. package/src/protocols/features.ts +295 -0
  119. package/src/protocols/guard.ts +33 -0
  120. package/src/protocols/opaque-state.ts +132 -0
  121. package/src/protocols/path.ts +39 -0
  122. package/src/protocols/plan-snapshot.ts +226 -0
  123. package/src/protocols/plan.ts +192 -0
  124. package/src/protocols/provider-summary.ts +101 -0
  125. package/src/protocols/settings.ts +118 -0
  126. package/src/protocols/shadow-plan.ts +36 -0
  127. package/src/protocols/shadow.ts +62 -0
  128. package/src/protocols/trace.ts +335 -0
  129. package/src/providers/model-rename-migration.ts +3 -3
  130. package/src/providers/registry/entries-core.ts +6 -1
  131. package/src/providers/registry/entries-extended.ts +15 -0
  132. package/src/providers/registry/model-ids.ts +1 -0
  133. package/src/providers/registry/types.ts +6 -0
  134. package/src/providers/registry.ts +1 -1
  135. package/src/remote-control/workspace-hub.ts +5 -4
  136. package/src/responses/apply-patch-envelope.ts +6 -1
  137. package/src/responses/code-mode-shell-input.ts +4 -2
  138. package/src/responses/freeform-wrapper-scan.ts +51 -24
  139. package/src/responses/progressive-freeform-input.ts +14 -20
  140. package/src/responses/spill-store.ts +187 -33
  141. package/src/responses/state/snapshot-select.ts +49 -0
  142. package/src/responses/state/spill-inspect.ts +46 -0
  143. package/src/responses/state/spill-queue.ts +16 -0
  144. package/src/responses/state.ts +51 -27
  145. package/src/server/audio-client.ts +8 -3
  146. package/src/server/audio-upstream.ts +9 -4
  147. package/src/server/auth-cors.ts +25 -8
  148. package/src/server/chat-completions.ts +71 -11
  149. package/src/server/chat-native-eligibility.ts +74 -0
  150. package/src/server/chat-native.ts +139 -74
  151. package/src/server/claude-messages.ts +140 -22
  152. package/src/server/hub-usage.ts +4 -3
  153. package/src/server/index/link-listener.ts +195 -0
  154. package/src/server/index/optional-listeners.ts +115 -0
  155. package/src/server/index/serve-options.ts +44 -10
  156. package/src/server/index.ts +10 -9
  157. package/src/server/inference/attempt.ts +51 -0
  158. package/src/server/inference/client-encoder-delivery.ts +235 -0
  159. package/src/server/inference/client-wire-log.ts +49 -0
  160. package/src/server/inference/client-wire.ts +69 -0
  161. package/src/server/inference/context.ts +15 -0
  162. package/src/server/inference/final-log.ts +37 -0
  163. package/src/server/local-desktop-snapshot-auth.ts +57 -0
  164. package/src/server/management/agent-settings-routes.ts +15 -13
  165. package/src/server/management/api-access.ts +11 -1
  166. package/src/server/management/config-routes.ts +3 -6
  167. package/src/server/management/context.ts +25 -0
  168. package/src/server/management/link-routes.ts +535 -0
  169. package/src/server/management/logs-usage-routes.ts +31 -1
  170. package/src/server/management/native-integration-routes.ts +6 -11
  171. package/src/server/management/oauth-account-routes.ts +42 -16
  172. package/src/server/management/protocol-routes.ts +159 -0
  173. package/src/server/management/protocol-settings-patch.ts +145 -0
  174. package/src/server/management/provider-patch-transaction.ts +61 -0
  175. package/src/server/management/provider-routes.ts +39 -18
  176. package/src/server/management/route-registry.ts +12 -0
  177. package/src/server/management/sidebar-routes.ts +8 -3
  178. package/src/server/management/system-routes.ts +18 -1
  179. package/src/server/management/usage-aggregate-cache.ts +206 -6
  180. package/src/server/management-api.ts +26 -1
  181. package/src/server/management-auth.ts +19 -5
  182. package/src/server/messages-native-eligibility.ts +161 -0
  183. package/src/server/messages-native-oauth.ts +149 -0
  184. package/src/server/messages-native.ts +790 -0
  185. package/src/server/relay.ts +9 -0
  186. package/src/server/request-log-filter.ts +92 -0
  187. package/src/server/request-log.ts +31 -70
  188. package/src/server/responses/adapter-delivery.ts +43 -16
  189. package/src/server/responses/agent-task-recovery.ts +90 -22
  190. package/src/server/responses/codex-ws-exchange.ts +4 -0
  191. package/src/server/responses/codex-ws-wire.ts +10 -1
  192. package/src/server/responses/core-combo-native.ts +323 -0
  193. package/src/server/responses/core-combo.ts +224 -13
  194. package/src/server/responses/core-options.ts +22 -0
  195. package/src/server/responses/core.ts +14 -26
  196. package/src/server/responses/request-sidecar-auth.ts +8 -3
  197. package/src/server/responses/run-turn-execution.ts +257 -63
  198. package/src/server/responses/sidecar-execution.ts +7 -4
  199. package/src/server/system-env.ts +7 -0
  200. package/src/service/diagnostics.ts +7 -1
  201. package/src/service/launchd.ts +26 -37
  202. package/src/service/windows-ops.ts +24 -20
  203. package/src/types/config.ts +58 -1
  204. package/src/types.ts +2 -0
  205. package/src/update/transactional-install.d.mts +0 -1
  206. package/src/update/transactional-install.mjs +17 -17
  207. package/src/usage/jev-stats.ts +495 -0
  208. package/src/usage/log.ts +24 -3
  209. package/src/web-search/run-turn-loop.ts +568 -0
  210. package/gui/dist/assets/App-8NMiZxT0.css +0 -1
  211. package/gui/dist/assets/App-DGLte6IR.js +0 -50
  212. package/gui/dist/assets/index--EWgGQvZ.css +0 -1
  213. package/gui/dist/assets/index-Bi1K37Wl.js +0 -86
@@ -0,0 +1,323 @@
1
+ /**
2
+ * Native Chat candidates inside the combo loop (PF-07).
3
+ *
4
+ * The Chat ingress hands the combo a `ComboProtocolSource` only for a combo route with
5
+ * `protocols.rollout.nativeChatCombos` on; without one nothing here runs. For the candidate the
6
+ * combo is about to dispatch, the concrete route is settled the way the Chat ingress settles a
7
+ * single route, and the native Chat lane's own eligibility rule decides whether the child goes
8
+ * native. A native child runs `runNativeChatAttempt` (through the source) on the attempt the combo
9
+ * already opened, with the combo's per-target budget, the client's abort signal and the turn
10
+ * lease, built from a fresh copy of the source body; every other candidate keeps the bridge.
11
+ * Under `unrepresentable: "reject"` a candidate whose path would drop a requested feature is never
12
+ * picked, and a combo whose every enabled candidate is skipped answers the ingress refusal.
13
+ *
14
+ * No `./` sibling import on purpose: `core-options.ts` names this module's types, so an edge back
15
+ * into the owner graph would close a cycle.
16
+ */
17
+ import type { OcxConfig } from "../../types";
18
+ import type { RouteResult } from "../../router";
19
+ import type { DataPlaneAdmission } from "../auth-cors";
20
+ import type { AdmissionLease } from "../../lib/admission";
21
+ import type { RequestExecutionBudget } from "../../lib/request-execution-budget";
22
+ import type { TransientSendBudget } from "../../lib/upstream-retry";
23
+ import type { ResponsesTerminalStatus } from "../../bridge";
24
+ import type { NativeChatFinishLog } from "../chat-native";
25
+ import type { InferenceAttempt } from "../inference/attempt";
26
+ import type { ProtocolEnvelope } from "../../protocols/envelope";
27
+ import type { ProtocolFeature } from "../../protocols/features";
28
+ import type { PersistedUsageAttempt } from "../../usage/log";
29
+ import {
30
+ finishRequestAttempt,
31
+ sealRequestAttemptIdentity,
32
+ type RequestLogContext,
33
+ } from "../request-log";
34
+ import { captureRouteStaticPolicy, routeConcreteModel } from "../../router";
35
+ import { comboDefaultEffort, concreteComboRequestBody, getCombo } from "../../combos";
36
+ import { supportedLadderFor } from "../effort-policy";
37
+ import { resolveWireProtocolOverride } from "../adapter-resolve";
38
+ import { resolveOpenCodeGoTransport } from "../../providers/opencode-go-transport";
39
+ import { getOrAllocateRequestSessionLane } from "../request-log-conversation";
40
+ import { assertRouteAllowedByScope, resolveAdmissionModelScope } from "../admission-model-scope";
41
+ import { isNativeChatRouteEligible } from "../chat-native-eligibility";
42
+ import { chatCompletionsErrorResponse } from "../../chat/outbound";
43
+ import { isRequestExecutionBudget } from "../../lib/request-execution-budget";
44
+ import { markResponseNonReplayable } from "../../lib/upstream-retry";
45
+ import { redactSecretString } from "../../lib/redact";
46
+ import { upstreamWireForAdapter } from "../../protocols/contract";
47
+ import { checkRepresentable, unrepresentableMessage } from "../../protocols/guard";
48
+ import { deliveryModeForLane, requestPathForLane } from "../../protocols/path";
49
+ import { resolveProtocolSettings } from "../../protocols/settings";
50
+ import { addProtocolEntryReason, markAttemptProtocolPath, markProtocolBlocked } from "../../protocols/trace";
51
+ import { markClientWire } from "../inference/client-wire";
52
+
53
+ type Rec = Record<string, unknown>;
54
+ type ComboTarget = { provider: string; model: string };
55
+
56
+ /** What the source needs to run one native child on the combo's open attempt. */
57
+ export interface NativeComboChildRun {
58
+ route: RouteResult;
59
+ /** A fresh copy of the source Chat body, never another candidate's rewritten one. */
60
+ body: Rec;
61
+ childLog: RequestLogContext;
62
+ attemptHandle: InferenceAttempt;
63
+ sendBudget: RequestExecutionBudget;
64
+ turnAdmissionLease?: AdmissionLease;
65
+ onFirstOutput: () => void;
66
+ /** Reports to the combo's child callbacks; it never writes the parent's final row itself. */
67
+ finishLog: NativeChatFinishLog;
68
+ }
69
+
70
+ /** Supplied by the Chat ingress for a combo route when `nativeChatCombos` is on. */
71
+ export interface ComboProtocolSource {
72
+ readonly inbound: "chat";
73
+ readonly envelope: ProtocolEnvelope;
74
+ /** Runs `runNativeChatAttempt`; the response it returns is in the Chat wire. */
75
+ dispatchNativeChild(input: NativeComboChildRun): Promise<Response>;
76
+ }
77
+
78
+ /** The native plan for one dispatch: the settled route and its own fresh body. */
79
+ export interface NativeComboChildPlan {
80
+ route: RouteResult;
81
+ body: Rec;
82
+ sendBudget: RequestExecutionBudget;
83
+ }
84
+
85
+ export interface ComboProtocolLanes {
86
+ /** Pick predicate. False only for a candidate the `reject` policy skips. */
87
+ pickable(target: ComboTarget): boolean;
88
+ /** The ingress refusal when every enabled candidate was skipped; otherwise undefined. */
89
+ refusal(): Response | undefined;
90
+ /** The native plan for the candidate about to dispatch, or undefined to keep the bridge. */
91
+ nativeChild(
92
+ target: ComboTarget,
93
+ targetRoute: RouteResult,
94
+ targetSendBudget: TransientSendBudget | undefined,
95
+ ): NativeComboChildPlan | undefined;
96
+ }
97
+
98
+ interface CandidateVerdict {
99
+ skip?: ProtocolFeature[];
100
+ }
101
+
102
+ export function createComboProtocolLanes(input: {
103
+ source: ComboProtocolSource | undefined;
104
+ req: Request;
105
+ config: OcxConfig;
106
+ logCtx: RequestLogContext;
107
+ admission: DataPlaneAdmission | undefined;
108
+ comboId: string;
109
+ targets: readonly ComboTarget[];
110
+ }): ComboProtocolLanes | undefined {
111
+ const { source, req, config, logCtx, admission, comboId, targets } = input;
112
+ if (!source) return undefined;
113
+ const envelope = source.envelope;
114
+ const reject = resolveProtocolSettings(config).unrepresentable === "reject";
115
+ const selector = (target: ComboTarget): string => `${target.provider}/${target.model}`;
116
+
117
+ // The Chat ingress's own settlement of a single route, applied to the concrete target: the
118
+ // key's model scope, the static policy captured for a Chat inbound, the wire override and the
119
+ // OpenCode Go transport. A refusal here keeps the bridge, which reports it in its own shape.
120
+ const settle = (target: ComboTarget, targetRoute: RouteResult): RouteResult | undefined => {
121
+ try {
122
+ const route: RouteResult = { ...targetRoute };
123
+ assertRouteAllowedByScope(resolveAdmissionModelScope(config, admission), selector(target), route);
124
+ const routedProvider = route.provider;
125
+ route.staticPolicy = captureRouteStaticPolicy(
126
+ route.providerName, route.modelId, routedProvider, route.staticPolicy.effectiveAlias, "chat",
127
+ );
128
+ const wireProvider = resolveWireProtocolOverride(route.providerName, route.modelId, routedProvider, "chat", route.staticPolicy);
129
+ route.provider = resolveOpenCodeGoTransport(wireProvider, getOrAllocateRequestSessionLane(req), routedProvider);
130
+ return route;
131
+ } catch {
132
+ return undefined;
133
+ }
134
+ };
135
+
136
+ // Eligibility reads a body, and a body is a charged copy, so the adapter is checked first:
137
+ // a candidate that can never go native costs no copy at all.
138
+ const nativeBody = (target: ComboTarget, route: RouteResult | undefined): Rec | undefined => {
139
+ if (!route || route.provider.adapter !== "openai-chat") return undefined;
140
+ const body = envelope.freshBody();
141
+ // The concrete selector, as the bridge child's body carries it; the wire model comes from
142
+ // the route either way, and the pinned-effort lookup reads this.
143
+ body.model = selector(target);
144
+ return isNativeChatRouteEligible(route, body, config) ? body : undefined;
145
+ };
146
+
147
+ const verdicts = new Map<string, CandidateVerdict>();
148
+ let skipRecorded = false;
149
+ const judge = (target: ComboTarget): CandidateVerdict => {
150
+ const key = selector(target);
151
+ const cached = verdicts.get(key);
152
+ if (cached) return cached;
153
+ let verdict: CandidateVerdict = {};
154
+ let targetRoute: RouteResult | undefined;
155
+ try {
156
+ targetRoute = routeConcreteModel(config, key);
157
+ } catch {
158
+ // Not evidence about the path; dispatch keeps the existing routing failure surface.
159
+ targetRoute = undefined;
160
+ }
161
+ if (targetRoute) {
162
+ const settled = settle(target, targetRoute);
163
+ const native = nativeBody(target, settled) !== undefined;
164
+ const upstream = native ? "chat" : upstreamWireForAdapter((settled ?? targetRoute).provider.adapter);
165
+ const checked = checkRepresentable({
166
+ inbound: "chat",
167
+ requestPath: requestPathForLane("chat", native ? "native" : "bridge", upstream),
168
+ features: envelope.features(),
169
+ policy: "reject",
170
+ });
171
+ if (!checked.ok) {
172
+ verdict = { skip: checked.features };
173
+ if (!skipRecorded) {
174
+ skipRecorded = true;
175
+ addProtocolEntryReason(logCtx, "feature-unrepresentable");
176
+ }
177
+ }
178
+ }
179
+ verdicts.set(key, verdict);
180
+ return verdict;
181
+ };
182
+
183
+ return {
184
+ pickable: target => !reject || judge(target).skip === undefined,
185
+ refusal: () => {
186
+ if (!reject) return undefined;
187
+ const enabled = targets.filter(target => {
188
+ const provider = config.providers[target.provider];
189
+ return provider !== undefined && provider.disabled !== true;
190
+ });
191
+ if (enabled.length === 0) return undefined;
192
+ const skipped = enabled.map(judge);
193
+ if (skipped.some(verdict => verdict.skip === undefined)) return undefined;
194
+ const features = [...new Set(skipped.flatMap(verdict => verdict.skip ?? []))];
195
+ // The same refusal the ingress gives a single route: 400 in the Chat shape, feature keys
196
+ // only, a blocked trace, and no upstream send.
197
+ markProtocolBlocked(logCtx, { inbound: "chat", reasonCodes: ["feature-unrepresentable"], features });
198
+ logCtx.errorCode = "unsupported_feature";
199
+ return markClientWire(chatCompletionsErrorResponse(
200
+ 400, unrepresentableMessage(features), "invalid_request_error", "unsupported_feature",
201
+ ), "chat");
202
+ },
203
+ nativeChild: (target, targetRoute, targetSendBudget) => {
204
+ // Without the request's execution budget a native child could not share its sends, so it
205
+ // keeps the bridge rather than opening a tracker of its own.
206
+ if (!isRequestExecutionBudget(targetSendBudget)) return undefined;
207
+ const route = settle(target, targetRoute);
208
+ const body = nativeBody(target, route);
209
+ if (!route || !body) return undefined;
210
+ applyComboEffort(body, config, comboId, target, targetRoute);
211
+ return { route, body, sendBudget: targetSendBudget };
212
+ },
213
+ };
214
+ }
215
+
216
+ /**
217
+ * The combo's reasoning-effort policy, applied to the Chat spelling of the same field.
218
+ *
219
+ * The bridge child gets it from `concreteComboRequestBody` on the Responses body; a native child
220
+ * must not lose a forced or default effort just because it skipped that body. The same function
221
+ * decides, through a one-field Responses view, so the two lanes cannot disagree.
222
+ */
223
+ function applyComboEffort(
224
+ body: Rec,
225
+ config: OcxConfig,
226
+ comboId: string,
227
+ target: ComboTarget,
228
+ targetRoute: RouteResult,
229
+ ): void {
230
+ const combo = getCombo(config, comboId);
231
+ if (!combo) return;
232
+ const hadEffort = Object.hasOwn(body, "reasoning_effort");
233
+ const shaped = concreteComboRequestBody(
234
+ hadEffort ? { reasoning: { effort: body.reasoning_effort } } : {},
235
+ target,
236
+ comboDefaultEffort(config, comboId),
237
+ supportedLadderFor({ provider: targetRoute.provider, modelId: targetRoute.modelId }),
238
+ combo.reasoningEffortMode,
239
+ combo.defaultEffortMode,
240
+ );
241
+ const reasoning = shaped.reasoning;
242
+ if (reasoning === undefined) {
243
+ if (hadEffort) delete body.reasoning_effort;
244
+ return;
245
+ }
246
+ const effort = (reasoning as { effort?: unknown }).effort;
247
+ if (typeof effort === "string") body.reasoning_effort = effort;
248
+ }
249
+
250
+ /** The combo's child callbacks, gated so a discarded attempt never reaches the parent. */
251
+ export interface ComboChildCallbacks {
252
+ onTerminal(status: ResponsesTerminalStatus): void;
253
+ onCancel(): void;
254
+ onResponseComplete(model: string): void;
255
+ }
256
+
257
+ /**
258
+ * Run one native child on the attempt the combo opened, and mark its answer as Chat wire.
259
+ *
260
+ * The child's outcomes map onto the combo's existing child callbacks: a terminal or cancellation
261
+ * after the response was handed over publishes through the gate (and so reaches the parent's
262
+ * final-row owner only once the combo commits), while a failure answered before that is left to
263
+ * the combo's ordinary failure path. A non-OK answer produced after output was observed is marked
264
+ * non-replayable, so the combo stops instead of re-running a turn that already ran upstream.
265
+ */
266
+ export async function dispatchNativeComboChild(input: {
267
+ source: ComboProtocolSource;
268
+ plan: NativeComboChildPlan;
269
+ logCtx: RequestLogContext;
270
+ childLog: RequestLogContext;
271
+ attempt: PersistedUsageAttempt;
272
+ startedAt: number;
273
+ turnAdmissionLease?: AdmissionLease;
274
+ onFirstOutput: () => void;
275
+ callbacks: ComboChildCallbacks;
276
+ }): Promise<Response> {
277
+ const { plan, logCtx, childLog, attempt, startedAt, callbacks } = input;
278
+ childLog.providerAdapter = plan.route.provider.adapter;
279
+ markAttemptProtocolPath(attempt, {
280
+ mode: deliveryModeForLane("chat", "native", "chat"),
281
+ requestPath: requestPathForLane("chat", "native", "chat"),
282
+ });
283
+ let outputSeen = false;
284
+ const finishLog: NativeChatFinishLog = (status, message, closeReason = "non_stream") => {
285
+ if (message) childLog.upstreamError = redactSecretString(message).slice(0, 500);
286
+ if (closeReason !== "non_stream") {
287
+ // A streamed body ends after the combo merged this child into the parent row, so what the
288
+ // stream learned last is carried across here before the parent's row is written.
289
+ if (childLog.usage !== undefined) logCtx.usage = childLog.usage;
290
+ if (closeReason === "client_cancel") return callbacks.onCancel();
291
+ }
292
+ if (status < 400) {
293
+ callbacks.onTerminal("completed");
294
+ callbacks.onResponseComplete(plan.route.modelId);
295
+ return;
296
+ }
297
+ if (closeReason === "terminal") {
298
+ childLog.terminalHttpStatus = status;
299
+ callbacks.onTerminal("failed");
300
+ }
301
+ };
302
+ const attemptHandle: InferenceAttempt = {
303
+ attempt,
304
+ startedAt,
305
+ seal: label => sealRequestAttemptIdentity(attempt, childLog.provider, childLog.providerAdapter ?? attempt.adapter, label),
306
+ finish: (status, usage) => finishRequestAttempt(attempt, status, Date.now() - startedAt, usage),
307
+ };
308
+ const response = await input.source.dispatchNativeChild({
309
+ route: plan.route,
310
+ body: plan.body,
311
+ childLog,
312
+ attemptHandle,
313
+ sendBudget: plan.sendBudget,
314
+ ...(input.turnAdmissionLease ? { turnAdmissionLease: input.turnAdmissionLease } : {}),
315
+ onFirstOutput: () => {
316
+ outputSeen = true;
317
+ input.onFirstOutput();
318
+ },
319
+ finishLog,
320
+ });
321
+ if (!response.ok && outputSeen) markResponseNonReplayable(response);
322
+ return markClientWire(response, "chat");
323
+ }
@@ -1,4 +1,8 @@
1
- import { isDeclaredReasoningEffort } from "../../reasoning-effort";
1
+ import {
2
+ isCodexReasoningEffort,
3
+ isDeclaredReasoningEffort,
4
+ resolveEffortAtOrBelow,
5
+ } from "../../reasoning-effort";
2
6
  import { recordAttemptRequestedEffort } from "../request-log";
3
7
  import {
4
8
  CODEX_TEXT_GUARDED_BUDGET_POLICY,
@@ -9,13 +13,14 @@ import type {
9
13
  RequestExecutionBudgetPolicy,
10
14
  RequestExecutionBudget,
11
15
  } from "../../lib/request-execution-budget";
12
- import type { OcxConfig } from "../../types";
16
+ import type { OcxComboDefaultEffort, OcxConfig } from "../../types";
13
17
  import type { RequestLogContext } from "../request-log";
14
18
  import type { HandleResponsesOptions, ResponsesDispatchers, ConsumedComboFailure } from "./core-options";
15
19
  import type { TranslatorBudget } from "../../lib/translator-budget";
16
20
  import {
17
21
  getCombo,
18
22
  comboRequestHasImageInput,
23
+ pickComboTarget,
19
24
  pickComboTargetWithWait,
20
25
  targetKey,
21
26
  concreteComboRequestBody,
@@ -25,6 +30,11 @@ import {
25
30
  comboFailureDecision,
26
31
  advanceComboAfterFailure,
27
32
  comboFailureCooldownScope,
33
+ JEV_PROVIDER_ID,
34
+ resolveJevDecision,
35
+ type ComboPick,
36
+ type JevCandidate,
37
+ type JevDecision,
28
38
  } from "../../combos";
29
39
  import { formatErrorResponse } from "../../bridge";
30
40
  import { SEND_BUDGET_EXHAUSTED_CODE } from "../../lib/errors";
@@ -65,6 +75,7 @@ import type { ResponsesTerminalStatus } from "../../bridge";
65
75
  import { beginRequestAttempt, sealRequestAttemptIdentity, finishRequestAttempt } from "../request-log";
66
76
  import { rememberComboForLane } from "./combo-session-recall";
67
77
  import { runTurnAdapterSseResponses } from "./core-lifetime";
78
+ import { normalizePersistedJevDecision } from "../../usage/jev-stats";
68
79
  import {
69
80
  isNativePassthroughSseResponse,
70
81
  isEagerRelaySseResponse,
@@ -75,6 +86,8 @@ import { preflightComboStreamResponse } from "./combo-stream-preflight";
75
86
  import { streamingContextOverflowResponse, jsonContextOverflowResponse } from "./context-overflow";
76
87
  import { mandatoryResponsesReasoningReplayUnavailable } from "./core-replay";
77
88
  import { settleOperatorReplacement } from "../../lib/upstream-retry";
89
+ import { createComboProtocolLanes, dispatchNativeComboChild } from "./core-combo-native";
90
+ import { clientWireOf } from "../inference/client-wire";
78
91
 
79
92
  /**
80
93
  * Sends one combo target may run on its own before the ladder moves on. A target is a whole
@@ -166,6 +179,65 @@ export function comboTargetSendBudget(
166
179
  });
167
180
  }
168
181
 
182
+ interface JevComboChoice {
183
+ pick: ComboPick;
184
+ candidate: JevCandidate;
185
+ }
186
+
187
+ /** Enumerate the current ordinary Combo eligibility set without retaining attempted picks. */
188
+ function eligibleJevComboChoices(
189
+ config: OcxConfig,
190
+ comboId: string,
191
+ eligible: (target: NonNullable<ReturnType<typeof getCombo>>["targets"][number]) => boolean,
192
+ now: number,
193
+ ): JevComboChoice[] {
194
+ const combo = getCombo(config, comboId);
195
+ if (!combo) return [];
196
+ const excluded = new Set<string>();
197
+ const choices: JevComboChoice[] = [];
198
+ while (excluded.size < combo.targets.length) {
199
+ const pick = pickComboTarget(config, comboId, { exclude: excluded, eligible, now });
200
+ if (!pick) break;
201
+ const key = targetKey(pick.target);
202
+ excluded.add(key);
203
+ // The TypeSafe row owns a decision credential, not an inference transport.
204
+ if (pick.target.provider === JEV_PROVIDER_ID) continue;
205
+ let ladder: string[] | undefined;
206
+ try {
207
+ const route = routeConcreteModel(config, key);
208
+ ladder = supportedLadderFor({ provider: route.provider, modelId: route.modelId });
209
+ } catch {
210
+ // Preserve the existing routing-failure surface. Unknown capability becomes the explicit
211
+ // no-effort choice rather than broadening JEV's effort allowlist.
212
+ ladder = undefined;
213
+ }
214
+ const supportedEfforts = (ladder ?? []).filter(isCodexReasoningEffort) as OcxComboDefaultEffort[];
215
+ const configuredEfforts = pick.target.reasoningEfforts;
216
+ const reasoningEfforts = configuredEfforts === undefined
217
+ ? supportedEfforts
218
+ : configuredEfforts.filter(effort => supportedEfforts.includes(effort));
219
+ // An explicit allowlist is restrictive. If catalog capabilities drift until no configured
220
+ // effort remains supported, omit the target instead of silently broadening JEV's choices.
221
+ if (configuredEfforts !== undefined && reasoningEfforts.length === 0) continue;
222
+ choices.push({
223
+ pick: { ...pick, attempted: [key] },
224
+ candidate: {
225
+ key,
226
+ provider: pick.target.provider,
227
+ model: pick.target.model,
228
+ reasoningEfforts,
229
+ },
230
+ });
231
+ }
232
+ // #5691: the synchronous pick above does not defer emergency-only targets, so apply the
233
+ // same rule here — withhold them from JEV while any normal target is offered, never when
234
+ // they are all that remains.
235
+ if (combo.cooldownWaitPolicy === "before-last-resort" && choices.some(choice => !choice.pick.target.lastResort)) {
236
+ return choices.filter(choice => !choice.pick.target.lastResort);
237
+ }
238
+ return choices;
239
+ }
240
+
169
241
 
170
242
  /** Dispatch a Responses combo within its shared send budget and preserve terminal child failures. */
171
243
  export async function executeComboResponses(
@@ -190,6 +262,17 @@ export async function executeComboResponses(
190
262
  if (!combo) {
191
263
  return formatErrorResponse(404, "invalid_request_error", `Unknown combo: ${comboId}`);
192
264
  }
265
+ // PF-07: present only for a Chat combo with `nativeChatCombos` on; otherwise every child
266
+ // takes the bridge below exactly as before.
267
+ const protocolLanes = createComboProtocolLanes({
268
+ source: options.protocolSource,
269
+ req,
270
+ config,
271
+ logCtx,
272
+ admission: options.admission,
273
+ comboId,
274
+ targets: combo.targets,
275
+ });
193
276
  // The ladder's own scope, derived from what this combo DECLARES. It shares the request-wide
194
277
  // counter with the holder that arrived on options -- a combo child already inherited that
195
278
  // counter, but nothing read it as a limit across targets -- while its transition and
@@ -298,7 +381,10 @@ export async function executeComboResponses(
298
381
  const payloadEligible = (target: (typeof combo.targets)[number]): boolean =>
299
382
  comboPayloadReadable || !unreadableEncryptedAgentTask || canDecryptUnreadableAgentTask(target);
300
383
  const targetEligible = (target: (typeof combo.targets)[number]): boolean =>
301
- payloadEligible(target) && reasoningReplayEligible(target);
384
+ (combo.strategy !== "jev" || target.provider !== JEV_PROVIDER_ID)
385
+ && payloadEligible(target)
386
+ && reasoningReplayEligible(target)
387
+ && (protocolLanes?.pickable(target) ?? true);
302
388
  const onlyReplayIncompatibleTargetsRemain = (excluded: Iterable<string> = []): boolean => {
303
389
  const excludedKeys = new Set(excluded);
304
390
  const remaining = combo.targets.filter(target => {
@@ -398,11 +484,78 @@ export async function executeComboResponses(
398
484
  }
399
485
 
400
486
  if (!pick) {
487
+ // Every enabled candidate skipped as unrepresentable: the ingress refusal, with no send.
488
+ const protocolRefusal = protocolLanes?.refusal();
489
+ if (protocolRefusal) return protocolRefusal;
401
490
  if (onlyReplayIncompatibleTargetsRemain()) return targetIncompatibleResponse();
402
491
  return options.abortSignal?.aborted
403
492
  ? clientCancelledResponse()
404
493
  : comboUnavailable(comboId);
405
494
  }
495
+ let jevDecision: JevDecision | undefined;
496
+ if (combo.strategy === "jev") {
497
+ const choices = eligibleJevComboChoices(config, comboId, targetEligible, Date.now());
498
+ const first = choices[0];
499
+ if (!first) return comboUnavailable(comboId);
500
+ const resolvedFailOpenEffort = resolveEffortAtOrBelow(
501
+ "medium",
502
+ first.candidate.reasoningEfforts,
503
+ );
504
+ const fallback: Pick<JevDecision, "targetKey" | "effort"> = {
505
+ targetKey: first.candidate.key,
506
+ effort: resolvedFailOpenEffort && isCodexReasoningEffort(resolvedFailOpenEffort)
507
+ ? resolvedFailOpenEffort as OcxComboDefaultEffort
508
+ : null,
509
+ };
510
+ const decisionStartedAt = Date.now();
511
+ let decision: JevDecision;
512
+ try {
513
+ decision = await resolveJevDecision({
514
+ body,
515
+ candidates: choices.map(choice => choice.candidate),
516
+ fallback,
517
+ config,
518
+ signal: options.abortSignal,
519
+ });
520
+ } catch (error) {
521
+ if (options.abortSignal?.aborted) return clientCancelledResponse();
522
+ decision = {
523
+ ...fallback,
524
+ gate: "network",
525
+ latencyMs: Math.max(0, Date.now() - decisionStartedAt),
526
+ };
527
+ }
528
+ jevDecision = decision;
529
+ const selected = choices.find(choice => choice.candidate.key === decision.targetKey) ?? first;
530
+ pick = { ...selected.pick, attempted: [targetKey(selected.pick.target)] };
531
+ logCtx.jevDecision = normalizePersistedJevDecision({
532
+ version: 1,
533
+ comboId,
534
+ selected: {
535
+ provider: selected.pick.target.provider,
536
+ model: selected.pick.target.model,
537
+ effort: decision.effort,
538
+ },
539
+ gate: decision.gate,
540
+ latencyMs: decision.latencyMs,
541
+ ...(decision.confidence !== undefined ? { confidence: decision.confidence } : {}),
542
+ ...(decision.chosenProbability !== undefined
543
+ ? { chosenProbability: decision.chosenProbability }
544
+ : {}),
545
+ ...(decision.usage ? { usage: decision.usage } : {}),
546
+ });
547
+ console.debug("[combo] JEV decision", {
548
+ targetKey: decision.targetKey,
549
+ effort: decision.effort,
550
+ gate: decision.gate,
551
+ latencyMs: decision.latencyMs,
552
+ ...(decision.confidence !== undefined ? { confidence: decision.confidence } : {}),
553
+ ...(decision.chosenProbability !== undefined
554
+ ? { chosenProbability: decision.chosenProbability }
555
+ : {}),
556
+ ...(decision.usage ? { usage: decision.usage } : {}),
557
+ });
558
+ }
406
559
  // One immutable combo selection trace, before any child dispatch; child
407
560
  // adoption below must never replace it with a concrete child route trace.
408
561
  logCtx.routeDecision = comboRouteDecisionTrace(config, comboId, pick, requestedModel);
@@ -475,14 +628,32 @@ export async function executeComboResponses(
475
628
  ...(logCtx.surface ? { surface: logCtx.surface } : {}),
476
629
  };
477
630
  const targetRoute = routeConcreteModel(config, `${pick.target.provider}/${pick.target.model}`);
631
+ const targetReasoningEfforts = supportedLadderFor({
632
+ provider: targetRoute.provider,
633
+ modelId: targetRoute.modelId,
634
+ });
635
+ const initialJevDecision = firstComboTarget ? jevDecision : undefined;
478
636
  const childBody = concreteComboRequestBody(
479
637
  body,
480
638
  pick.target,
481
- comboDefaultEffort(config, comboId),
482
- supportedLadderFor({ provider: targetRoute.provider, modelId: targetRoute.modelId }),
639
+ initialJevDecision ? initialJevDecision.effort : comboDefaultEffort(config, comboId),
640
+ initialJevDecision?.effort === null ? [] : targetReasoningEfforts,
483
641
  combo.reasoningEffortMode,
484
- combo.defaultEffortMode,
642
+ initialJevDecision !== undefined && initialJevDecision.effort !== null ? "force" : combo.defaultEffortMode,
485
643
  );
644
+ if (initialJevDecision) {
645
+ delete childBody.service_tier;
646
+ if (initialJevDecision.effort !== null) {
647
+ const childReasoning = childBody.reasoning;
648
+ const preservedReasoning = childReasoning && typeof childReasoning === "object" && !Array.isArray(childReasoning)
649
+ ? childReasoning as Record<string, unknown>
650
+ : {};
651
+ childBody.reasoning = { ...preservedReasoning, effort: initialJevDecision.effort };
652
+ delete childBody.reasoning_effort;
653
+ delete childBody.thinking_budget;
654
+ delete childBody.thinking;
655
+ }
656
+ }
486
657
  const childHeaders = buildComboChildHeaders(req.headers);
487
658
  const childRequest = new Request(req.url, {
488
659
  method: req.method,
@@ -550,14 +721,40 @@ export async function executeComboResponses(
550
721
  let response: Response;
551
722
  try {
552
723
  const currentTargetProvider = pick.target.provider;
553
- const deferCodexResetDerivedCooldown = combo.strategy === "failover"
554
- && combo.targets.slice(pick.targetIndex + 1).some(target =>
724
+ const remainingTargets = combo.strategy === "jev"
725
+ ? combo.targets.filter(target => !pick!.attempted.includes(targetKey(target)))
726
+ : combo.targets.slice(pick.targetIndex + 1);
727
+ const deferCodexResetDerivedCooldown = (combo.strategy === "failover" || combo.strategy === "jev")
728
+ && remainingTargets.some(target =>
555
729
  target.provider === currentTargetProvider
556
730
  && targetEligible(target)
557
731
  && !isComboTargetInCooldown(comboId, target),
558
732
  );
559
- response = await requestDispatchers.handleResponses(childRequest, config, childLog, {
733
+ const nativeChild = protocolLanes?.nativeChild(pick.target, targetRoute, targetSendBudget);
734
+ response = nativeChild ? await dispatchNativeComboChild({
735
+ source: options.protocolSource!,
736
+ plan: nativeChild,
737
+ logCtx,
738
+ childLog,
739
+ attempt,
740
+ startedAt: started,
741
+ ...(options.turnAdmissionLease ? { turnAdmissionLease: options.turnAdmissionLease } : {}),
742
+ // Attempt-relative TTFT, recorded here for the same reason as the bridge child below.
743
+ onFirstOutput: () => {
744
+ if (attempt.firstOutputMs === undefined) {
745
+ attempt.firstOutputMs = Math.max(0, Date.now() - started);
746
+ }
747
+ options.onFirstOutput?.();
748
+ },
749
+ callbacks: {
750
+ onTerminal: callbackGate.onTerminal,
751
+ onCancel: callbackGate.onCancel,
752
+ onResponseComplete: callbackGate.onResponseComplete,
753
+ },
754
+ }) : await requestDispatchers.handleResponses(childRequest, config, childLog, {
560
755
  ...options,
756
+ // A bridge child is a concrete route; the native source belongs to this loop only.
757
+ ...(options.protocolSource ? { protocolSource: undefined } : {}),
561
758
  // After the spread: the child must run on THIS target's ladder, not on the holder the
562
759
  // parent arrived with.
563
760
  sendBudget: targetSendBudget,
@@ -597,7 +794,9 @@ export async function executeComboResponses(
597
794
  return clientCancelledResponse();
598
795
  }
599
796
 
600
- if (response.ok && !runTurnAdapterSseResponses.has(response)) {
797
+ // A native Chat child reports a pre-stream failure by status before any byte, so its body
798
+ // is never peeked; a non-OK one takes the ordinary failure path below.
799
+ if (response.ok && !runTurnAdapterSseResponses.has(response) && clientWireOf(response) !== "chat") {
601
800
  const nativePassthrough = isNativePassthroughSseResponse(response);
602
801
  const eagerRelay = isEagerRelaySseResponse(response);
603
802
  let preflight;
@@ -754,19 +953,30 @@ export async function executeComboResponses(
754
953
  );
755
954
  const failureNow = Date.now();
756
955
  const attemptedTargets = pick.attempted;
956
+ const failureCooldownScope = comboFailureCooldownScope(failure.response.status, failure.classificationText, {
957
+ code: failure.upstreamCode,
958
+ });
959
+ const failedTargetKey = targetKey(pick.target);
960
+ let failedTargetCooldownRecorded = false;
757
961
  const nextPick = advanceComboAfterFailure(config, pick, {
758
962
  retryAfter: failure.retryAfter,
759
963
  resetAt: failure.resetAt,
760
964
  cooldownMs: combo.cooldownMs,
761
965
  now: failureNow,
762
- cooldownScope: comboFailureCooldownScope(failure.response.status, failure.classificationText, {
763
- code: failure.upstreamCode,
764
- }),
966
+ cooldownScope: failureCooldownScope,
765
967
  eligible: targetEligible,
766
968
  status: failure.response.status,
767
969
  code: failure.upstreamCode,
768
970
  message: failure.classificationText,
971
+ onCooldownRecorded: target => {
972
+ failedTargetCooldownRecorded ||= targetKey(target) === failedTargetKey;
973
+ },
769
974
  });
975
+ // A sibling cooldown is not this failure's write: stale-generation removal can
976
+ // refuse recording even for a cooldown-producing classification. Require both.
977
+ const failedTargetCooled = failureCooldownScope !== "none"
978
+ && failedTargetCooldownRecorded
979
+ && isComboTargetInCooldown(comboId, pick.target, failureNow);
770
980
  // Same target selector as the exclusionary pick below, minus `exclude`: the only
771
981
  // difference is deliberate and is the whole point of the single-target retry.
772
982
  const retryAfterCooldown = () =>
@@ -797,6 +1007,7 @@ export async function executeComboResponses(
797
1007
  && combo.targets.length === 1
798
1008
  && combo.waitForCooldownMs > 0
799
1009
  && comboTargetsDispatched <= 1
1010
+ && failedTargetCooled
800
1011
  && !options.abortSignal?.aborted
801
1012
  ) {
802
1013
  pick = await retryAfterCooldown();