@coseung2/opencodex 2.8.0-cs.16 → 2.8.0-cs.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/gui/dist/assets/{index-BZGMtkmp.js → index-Ch-99jy3.js} +1 -1
- package/gui/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/adapters/base.ts +12 -0
- package/src/adapters/kiro-calibration.ts +83 -0
- package/src/adapters/kiro-constants.ts +11 -2
- package/src/adapters/kiro-errors.ts +11 -0
- package/src/adapters/kiro-events.ts +19 -1
- package/src/adapters/kiro-thinking.ts +18 -2
- package/src/adapters/kiro-tools.ts +12 -3
- package/src/adapters/kiro.ts +300 -78
- package/src/adapters/openai-chat.ts +1 -42
- package/src/adapters/openai-responses.ts +93 -14
- package/src/adapters/xai-schema-analysis.ts +78 -0
- package/src/adapters/xai-tool-schema.ts +274 -0
- package/src/adapters/xai-web-search.ts +138 -0
- package/src/bridge.ts +61 -6
- package/src/codex/catalog/effort.ts +4 -2
- package/src/codex/catalog/metadata.ts +38 -9
- package/src/codex/catalog/parsing.ts +17 -2
- package/src/codex/catalog/provider-fetch.ts +9 -3
- package/src/codex/catalog/sync.ts +8 -5
- package/src/codex/data/upstream-models.json +169 -0
- package/src/grok/inject.ts +1 -1
- package/src/lib/token-estimate.ts +42 -38
- package/src/lib/translator-budget.ts +34 -0
- package/src/oauth/index.ts +10 -4
- package/src/oauth/kiro.ts +71 -6
- package/src/oauth/store.ts +3 -1
- package/src/oauth/types.ts +4 -0
- package/src/providers/derive.ts +7 -5
- package/src/providers/opencode-go-transport.ts +18 -0
- package/src/providers/registry.ts +34 -10
- package/src/providers/xai-transport.ts +10 -0
- package/src/responses/compaction.ts +8 -1
- package/src/responses/namespace-aliases.ts +56 -0
- package/src/responses/parser.ts +12 -0
- package/src/responses/reasoning-envelope.ts +9 -1
- package/src/responses/snapshot-policy.ts +108 -0
- package/src/responses/state.ts +23 -10
- package/src/responses/turn-termination.ts +108 -0
- package/src/responses/xai-custom-tool-compat.ts +237 -0
- package/src/server/grok-responses-snapshot-repair.ts +338 -0
- package/src/server/index.ts +2 -1
- package/src/server/relay-eager.ts +1 -0
- package/src/server/responses/core.ts +173 -13
- package/src/server/responses-image-gen-repair.ts +2 -2
- package/src/server/sse-payload-rewrite.ts +20 -3
- package/src/types.ts +10 -1
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +7 -0
- package/vendor/ocx-notch/win32-x64/ocx-notch.exe +0 -0
package/src/server/index.ts
CHANGED
|
@@ -527,7 +527,7 @@ export function startServer(port?: number) {
|
|
|
527
527
|
// Disabled natives stay in the catalog shape with visibility "hide" (mirrors the
|
|
528
528
|
// on-disk sync; codex-rs keeps them out of the picker itself).
|
|
529
529
|
const maMode = config.multiAgentMode === "v1" || config.multiAgentMode === "v2" ? config.multiAgentMode : "default";
|
|
530
|
-
const entries = buildCatalogEntries(loadCatalogTemplate(), nativeSlugs, goOrdered, config.subagentModels, websocketsEnabled(config), maMode as "v1" | "default" | "v2", exactComboCatalogSlugs(config));
|
|
530
|
+
const entries = buildCatalogEntries(loadCatalogTemplate(), nativeSlugs, goOrdered, config.subagentModels, websocketsEnabled(config), maMode as "v1" | "default" | "v2", exactComboCatalogSlugs(config), config);
|
|
531
531
|
return jsonResponse({ models: applyNativeVisibility(entries, disabledNativeSlugs(config)) }, 200, req, config);
|
|
532
532
|
}
|
|
533
533
|
// OpenAI list shape: native gpt bare + routed models namespaced "<provider>/<id>"
|
|
@@ -709,6 +709,7 @@ export function startServer(port?: number) {
|
|
|
709
709
|
...admissionFields(admission),
|
|
710
710
|
inboundProtocol: "responses",
|
|
711
711
|
};
|
|
712
|
+
if (req.headers.get("x-opencodex-grok") === "1") logCtx.surface = "grok";
|
|
712
713
|
let logged = false;
|
|
713
714
|
const finalizeNativePassthroughLog = (
|
|
714
715
|
status: number,
|
|
@@ -283,6 +283,7 @@ export function relaySseEagerBounded(
|
|
|
283
283
|
if (!cancelled) {
|
|
284
284
|
try { controllerRef?.close(); } catch { /* already closed/errored */ }
|
|
285
285
|
}
|
|
286
|
+
try { rewrite?.dispose?.(); } catch { /* rewrite teardown must not block lifecycle cleanup */ }
|
|
286
287
|
try { hooks.disposeInspection?.(); } catch { /* inspection teardown must not block lifecycle cleanup */ }
|
|
287
288
|
fireDone();
|
|
288
289
|
}
|
|
@@ -9,12 +9,14 @@ import {
|
|
|
9
9
|
import { parseRequest } from "../../responses/parser";
|
|
10
10
|
import { buildCompactV1Output, COMPACT_PROMPT, decodeCompactionSummary, extractCompactUserMessages } from "../../responses/compaction";
|
|
11
11
|
import { FORWARD_HEADERS, sanitizeReasoningInputContent } from "../../adapters/openai-responses";
|
|
12
|
+
import { XaiToolSchemaCompatibilityError } from "../../adapters/xai-tool-schema";
|
|
12
13
|
import {
|
|
13
14
|
expandPreviousResponseInput,
|
|
14
15
|
previousResponseProviderState,
|
|
15
16
|
previousResponseReplayFailure,
|
|
16
17
|
rememberResponseState,
|
|
17
18
|
} from "../../responses/state";
|
|
19
|
+
import { bindTurnTerminationScope, rememberDeliveredFinalAnswer } from "../../responses/turn-termination";
|
|
18
20
|
import { routeModel, type RouteResult } from "../../router";
|
|
19
21
|
import {
|
|
20
22
|
advanceComboAfterFailure,
|
|
@@ -100,8 +102,9 @@ import { resolveAdapter, resolveWireProtocolOverride } from "../adapter-resolve"
|
|
|
100
102
|
import type { InboundWire } from "../../providers/registry";
|
|
101
103
|
import { hasKeyPoolFailover, rotateProviderTransportOn429 } from "../../providers/key-failover";
|
|
102
104
|
import { shouldAttemptImageTierRetry } from "../image-retry";
|
|
103
|
-
import { resolveProviderTransport } from "../../providers/xai-transport";
|
|
104
|
-
import { resolveOpenCodeGoTransport } from "../../providers/opencode-go-transport";
|
|
105
|
+
import { isXaiResponsesDestination, resolveProviderTransport } from "../../providers/xai-transport";
|
|
106
|
+
import { isOpenCodeMuseResponses, resolveOpenCodeGoTransport } from "../../providers/opencode-go-transport";
|
|
107
|
+
import { declaredNamespaceAliases } from "../../responses/namespace-aliases";
|
|
105
108
|
import type { WsData } from "../ws-bridge";
|
|
106
109
|
import { trackActiveTurnLease, trackStreamLifetime } from "../lifecycle";
|
|
107
110
|
import { redactSecretString } from "../../lib/redact";
|
|
@@ -158,12 +161,18 @@ import {
|
|
|
158
161
|
createResponsesItemIdPayloadRewrite,
|
|
159
162
|
hasResponsesItemIdRepair,
|
|
160
163
|
} from "../responses-item-id-repair";
|
|
164
|
+
import { createGrokResponsesSparseTerminalPayloadRewrite } from "../grok-responses-snapshot-repair";
|
|
161
165
|
import {
|
|
162
166
|
createImageGenCallRestoreRewrite,
|
|
163
167
|
imageGenToolCallAliases,
|
|
164
168
|
restoreImageGenCallsInJson,
|
|
165
169
|
} from "../responses-image-gen-repair";
|
|
166
170
|
import { composeSsePayloadRewrites, relaySseWithPayloadRewrite } from "../sse-payload-rewrite";
|
|
171
|
+
import {
|
|
172
|
+
createXaiCustomToolPayloadRewrite,
|
|
173
|
+
restoreXaiCustomCallsInJson,
|
|
174
|
+
xaiResponsesCustomToolNames,
|
|
175
|
+
} from "../../responses/xai-custom-tool-compat";
|
|
167
176
|
import type { EffectiveSubagentRoster, SpawnAgentSurface } from "../../codex/catalog";
|
|
168
177
|
|
|
169
178
|
import { buildToolBridgeMaps, collabSurface, injectDeveloperMessage, multiAgentGuidanceText } from "./collaboration";
|
|
@@ -889,8 +898,11 @@ async function applyFinalRouteRequestNormalization(args: {
|
|
|
889
898
|
// Virtual model rewriting: Pro aliases → base model + reasoning.mode="pro".
|
|
890
899
|
applyOpenAiVirtualModel(parsed, route, logCtx);
|
|
891
900
|
|
|
892
|
-
// Fast mode override for OpenAI-
|
|
893
|
-
|
|
901
|
+
// Fast mode override for OpenAI-shaped routes. xAI OAuth's subscription Responses lane has no
|
|
902
|
+
// caller-owned Priority/Fast contract, so switching Grok to its native wire must not make a
|
|
903
|
+
// global OpenAI fastMode leak service_tier upstream.
|
|
904
|
+
const callerFastTierAllowed = !(route.providerName === "xai" && route.provider.authMode === "oauth");
|
|
905
|
+
if (config.fastMode !== undefined && route.provider.adapter === "openai-responses" && callerFastTierAllowed) {
|
|
894
906
|
const tier = config.fastMode ? "priority" : undefined;
|
|
895
907
|
if (parsed._rawBody && typeof parsed._rawBody === "object") {
|
|
896
908
|
if (tier) (parsed._rawBody as Record<string, unknown>).service_tier = tier;
|
|
@@ -1330,6 +1342,7 @@ async function handleResponsesInner(
|
|
|
1330
1342
|
cursorConversationId: parsed._cursorConversationId,
|
|
1331
1343
|
});
|
|
1332
1344
|
}
|
|
1345
|
+
bindTurnTerminationScope(parsed, logCtx.conversationId);
|
|
1333
1346
|
logCtx.requestedModel = parsed.modelId;
|
|
1334
1347
|
logCtx.requestedEffort = parsed.options.reasoning;
|
|
1335
1348
|
logCtx.requestedServiceTier = parsed.options.serviceTier;
|
|
@@ -1636,9 +1649,6 @@ async function handleResponsesInner(
|
|
|
1636
1649
|
}
|
|
1637
1650
|
|
|
1638
1651
|
if ("passthrough" in adapter && adapter.passthrough && !routedCompaction) {
|
|
1639
|
-
const imageGenCallAliases = route.provider.authMode === "forward"
|
|
1640
|
-
? new Map<string, { namespace: string; name: string }>()
|
|
1641
|
-
: imageGenToolCallAliases(toolBridgeMaps.toolNsMap, parsed._rawBody, translatorBudget);
|
|
1642
1652
|
// Local continuation cache for the ChatGPT passthrough. Codex WS turns chain with
|
|
1643
1653
|
// previous_response_id, ocx converts them to internal HTTP requests, and the ChatGPT Codex
|
|
1644
1654
|
// REST backend rejects the parameter — the adapter strips it in forward mode, so the ONLY
|
|
@@ -1660,7 +1670,27 @@ async function handleResponsesInner(
|
|
|
1660
1670
|
+ `(model ${parsed.modelId}); forwarding without it — earlier turns may be missing from this request`,
|
|
1661
1671
|
);
|
|
1662
1672
|
}
|
|
1663
|
-
let request
|
|
1673
|
+
let request: Awaited<ReturnType<typeof adapter.buildRequest>>;
|
|
1674
|
+
try {
|
|
1675
|
+
request = await adapter.buildRequest(parsed, { headers: selectedForwardHeaders, translatorBudget });
|
|
1676
|
+
} catch (error) {
|
|
1677
|
+
releaseCodexAuthContextProbeLease(authCtx);
|
|
1678
|
+
if (error instanceof XaiToolSchemaCompatibilityError) {
|
|
1679
|
+
return formatErrorResponse(400, "invalid_request_error", redactSecretString(error.message));
|
|
1680
|
+
}
|
|
1681
|
+
throw error;
|
|
1682
|
+
}
|
|
1683
|
+
const xaiCustomToolNames = isXaiResponsesDestination(route.provider)
|
|
1684
|
+
? xaiResponsesCustomToolNames(parsed._rawBody)
|
|
1685
|
+
: new Set<string>();
|
|
1686
|
+
// Reuse the existing client-only namespace rewrite and its bounded SSE relay. The
|
|
1687
|
+
// inspection/cache branch retains raw names for continuation replay. Resolve all
|
|
1688
|
+
// declaration ownership before admitting a Muse dotted alias.
|
|
1689
|
+
const imageGenCallAliases = route.provider.authMode === "forward"
|
|
1690
|
+
? new Map<string, { namespace: string; name: string }>()
|
|
1691
|
+
: isOpenCodeMuseResponses(parsed.modelId, request.url)
|
|
1692
|
+
? declaredNamespaceAliases(parsed.context.tools ?? [], translatorBudget)
|
|
1693
|
+
: imageGenToolCallAliases(toolBridgeMaps.toolNsMap, parsed._rawBody, translatorBudget);
|
|
1664
1694
|
recordAdapterReasoning(logCtx, request);
|
|
1665
1695
|
const passthroughEstimate = typeof request.usageLog?.inputTokens === "number"
|
|
1666
1696
|
? request.usageLog.inputTokens
|
|
@@ -1715,6 +1745,66 @@ async function handleResponsesInner(
|
|
|
1715
1745
|
request.releaseBodyObservation?.();
|
|
1716
1746
|
}
|
|
1717
1747
|
|
|
1748
|
+
// Native Responses providers return from this passthrough branch before the generic recovery
|
|
1749
|
+
// loop below. Preserve the same OAuth contract here: one pre-stream 401 forces one credential
|
|
1750
|
+
// refresh and one rebuilt replay. This is required for xAI now that Grok 4.6/4.5 subscription
|
|
1751
|
+
// traffic uses the provider's native Responses wire.
|
|
1752
|
+
if (upstreamResponse.status === 401 && isOAuth401ReplayProvider && sentOAuthSnapshot) {
|
|
1753
|
+
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed */ }
|
|
1754
|
+
let refreshed: OAuthAccessSnapshot;
|
|
1755
|
+
try {
|
|
1756
|
+
refreshed = await forceRefreshOAuthAccessSnapshot(sentOAuthSnapshot);
|
|
1757
|
+
} catch (err) {
|
|
1758
|
+
upstream.abort();
|
|
1759
|
+
releaseCodexAuthContextProbeLease(authCtx);
|
|
1760
|
+
return formatErrorResponse(401, "authentication_error", publicOAuthAuthenticationErrorMessage(err));
|
|
1761
|
+
}
|
|
1762
|
+
sentOAuthSnapshot = refreshed;
|
|
1763
|
+
if (route.providerName === "kiro") parsed._kiroAuthContext = { ...(refreshed.kiro ?? {}) };
|
|
1764
|
+
const refreshedProvider = resolveProviderTransport(
|
|
1765
|
+
route.providerName,
|
|
1766
|
+
{ ...route.provider, apiKey: refreshed.accessToken },
|
|
1767
|
+
parsed.options.promptCacheKey,
|
|
1768
|
+
route.providerName === "github-copilot" ? getOAuthCredentialApiBaseUrl(route.providerName) : undefined,
|
|
1769
|
+
);
|
|
1770
|
+
route.provider = refreshedProvider;
|
|
1771
|
+
const refreshedAdapter = resolveAdapter(
|
|
1772
|
+
resolveWireProtocolOverride(route.providerName, route.modelId, refreshedProvider, inboundWire),
|
|
1773
|
+
config.cacheRetention,
|
|
1774
|
+
);
|
|
1775
|
+
if (!("passthrough" in refreshedAdapter) || !refreshedAdapter.passthrough) {
|
|
1776
|
+
upstream.abort();
|
|
1777
|
+
return formatErrorResponse(502, "upstream_error", "OAuth refresh changed the provider wire unexpectedly");
|
|
1778
|
+
}
|
|
1779
|
+
logCtx.providerAdapter = refreshedAdapter.name;
|
|
1780
|
+
sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, refreshedAdapter.name, logCtx.accountLogLabel);
|
|
1781
|
+
try {
|
|
1782
|
+
request = await refreshedAdapter.buildRequest(parsed, { headers: selectedForwardHeaders, translatorBudget });
|
|
1783
|
+
recordAdapterReasoning(logCtx, request);
|
|
1784
|
+
} catch (err) {
|
|
1785
|
+
upstream.abort();
|
|
1786
|
+
if (options.abortSignal?.aborted) return clientCancelledResponse();
|
|
1787
|
+
return formatErrorResponse(400, "invalid_request_error", redactSecretString(err instanceof Error ? err.message : String(err)));
|
|
1788
|
+
}
|
|
1789
|
+
try {
|
|
1790
|
+
upstreamResponse = await fetchWithTransientRetry(
|
|
1791
|
+
recovery => {
|
|
1792
|
+
noteAttemptSend(logCtx.activeAttempt, passthroughEstimate, recovery ?? "oauth-401");
|
|
1793
|
+
return fetchWithHeaderTimeout(request.url, applyUpstreamRecoveryInit({
|
|
1794
|
+
method: request.method,
|
|
1795
|
+
headers: request.headers,
|
|
1796
|
+
body: request.body,
|
|
1797
|
+
}, recovery), upstream.signal, connectMs, parsed.stream, providerFetch(route.provider, options.codexWsRuntimeIdentity));
|
|
1798
|
+
},
|
|
1799
|
+
{ abortSignal: upstream.signal, label: safeHostLabel(request.url) },
|
|
1800
|
+
);
|
|
1801
|
+
} catch (err) {
|
|
1802
|
+
return transportFailureResponse(err);
|
|
1803
|
+
} finally {
|
|
1804
|
+
request.releaseBodyObservation?.();
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1718
1808
|
if (usesCodexForwardPoolAuth(authCtx, route.provider) && !authCtx.fixedAccount) {
|
|
1719
1809
|
let poolRetryOutcome: number | undefined;
|
|
1720
1810
|
if (await shouldRetryCodexPoolAccountModel400(
|
|
@@ -1865,10 +1955,20 @@ async function handleResponsesInner(
|
|
|
1865
1955
|
// The bundled known-bad runtime remains on tee by default on both platforms.
|
|
1866
1956
|
if (isEventStream && upstreamResponse.body) {
|
|
1867
1957
|
const repairConfig = route.provider.responsesItemIdRepair;
|
|
1868
|
-
const
|
|
1869
|
-
|
|
1958
|
+
const xaiCustomToolRewrite = createXaiCustomToolPayloadRewrite(xaiCustomToolNames);
|
|
1959
|
+
const grokSparseTerminalRewrite = logCtx.surface === "grok"
|
|
1960
|
+
? createGrokResponsesSparseTerminalPayloadRewrite(translatorBudget)
|
|
1961
|
+
: undefined;
|
|
1962
|
+
const needsClientRewrite = imageGenCallAliases.size > 0
|
|
1963
|
+
|| hasResponsesItemIdRepair(repairConfig)
|
|
1964
|
+
|| xaiCustomToolRewrite !== undefined
|
|
1965
|
+
|| grokSparseTerminalRewrite !== undefined;
|
|
1966
|
+
// Compose opt-in payload rewrites into one parse/stringify pass. Provider-shape restoration
|
|
1967
|
+
// runs before generic item-id repair so the client sees the correct custom-tool identity.
|
|
1870
1968
|
const payloadRewrites = [
|
|
1871
1969
|
createImageGenCallRestoreRewrite(imageGenCallAliases),
|
|
1970
|
+
xaiCustomToolRewrite,
|
|
1971
|
+
grokSparseTerminalRewrite,
|
|
1872
1972
|
hasResponsesItemIdRepair(repairConfig)
|
|
1873
1973
|
? createResponsesItemIdPayloadRewrite(repairConfig!, translatorBudget)
|
|
1874
1974
|
: undefined,
|
|
@@ -2031,7 +2131,8 @@ async function handleResponsesInner(
|
|
|
2031
2131
|
rememberPassthroughResponse(JSON.parse(text) as { id?: unknown; output?: unknown; status?: unknown });
|
|
2032
2132
|
} catch { /* non-JSON despite content-type; recording is best-effort */ }
|
|
2033
2133
|
}
|
|
2034
|
-
|
|
2134
|
+
const restoredCustomTools = restoreXaiCustomCallsInJson(text, xaiCustomToolNames);
|
|
2135
|
+
return new Response(restoreImageGenCallsInJson(restoredCustomTools, imageGenCallAliases), {
|
|
2035
2136
|
status: upstreamResponse.status,
|
|
2036
2137
|
statusText: upstreamResponse.statusText,
|
|
2037
2138
|
headers,
|
|
@@ -2390,6 +2491,62 @@ async function handleResponsesInner(
|
|
|
2390
2491
|
return new Response(JSON.stringify(json), { headers: { "Content-Type": "application/json" } });
|
|
2391
2492
|
}
|
|
2392
2493
|
|
|
2494
|
+
// Some adapter inputs already contain the completed answer and require no inference at all.
|
|
2495
|
+
// Kiro uses this for replayed history ending in a delivered final answer. Keep this OUTSIDE the
|
|
2496
|
+
// ordinary empty-completion path: an outputless done is intentional here and must never trigger
|
|
2497
|
+
// another request for the closed task.
|
|
2498
|
+
const localTerminal = adapter.localTerminal?.(parsed);
|
|
2499
|
+
if (localTerminal) {
|
|
2500
|
+
const terminalEvents: AdapterEvent[] = [{
|
|
2501
|
+
type: "done",
|
|
2502
|
+
endTurn: true,
|
|
2503
|
+
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
2504
|
+
}];
|
|
2505
|
+
const { toolNsMap, freeformToolNames, toolSearchToolNames } = toolBridgeMaps;
|
|
2506
|
+
if (parsed.stream) {
|
|
2507
|
+
const localSse = bridgeToResponsesSSE(
|
|
2508
|
+
(async function* () { yield* terminalEvents; })(),
|
|
2509
|
+
parsed.modelId,
|
|
2510
|
+
toolNsMap,
|
|
2511
|
+
freeformToolNames,
|
|
2512
|
+
toolSearchToolNames,
|
|
2513
|
+
undefined,
|
|
2514
|
+
2_000,
|
|
2515
|
+
{
|
|
2516
|
+
translatorBudget,
|
|
2517
|
+
...(options.forceEmptyResponseId ? { responseId: "" } : {}),
|
|
2518
|
+
...(options.onFirstOutput ? { onFirstOutput: options.onFirstOutput } : {}),
|
|
2519
|
+
onUsage: usage => {
|
|
2520
|
+
logCtx.usageFromBridge = true;
|
|
2521
|
+
if (usage) {
|
|
2522
|
+
logCtx.usage = usage;
|
|
2523
|
+
if (logCtx.activeAttempt) logCtx.activeAttempt.usage = usage;
|
|
2524
|
+
}
|
|
2525
|
+
},
|
|
2526
|
+
},
|
|
2527
|
+
);
|
|
2528
|
+
const localTurnAc = new AbortController();
|
|
2529
|
+
const tracked = trackStreamLifetime(localSse, localTurnAc, undefined, options.turnAdmissionLease);
|
|
2530
|
+
return new Response(tracked, {
|
|
2531
|
+
headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no" },
|
|
2532
|
+
});
|
|
2533
|
+
}
|
|
2534
|
+
const json = buildResponseJSON(terminalEvents, parsed.modelId, {
|
|
2535
|
+
translatorBudget,
|
|
2536
|
+
toolNsMap,
|
|
2537
|
+
freeformToolNames,
|
|
2538
|
+
toolSearchToolNames,
|
|
2539
|
+
onUsage: usage => {
|
|
2540
|
+
logCtx.usageFromBridge = true;
|
|
2541
|
+
if (usage) {
|
|
2542
|
+
logCtx.usage = usage;
|
|
2543
|
+
if (logCtx.activeAttempt) logCtx.activeAttempt.usage = usage;
|
|
2544
|
+
}
|
|
2545
|
+
},
|
|
2546
|
+
});
|
|
2547
|
+
return new Response(JSON.stringify(json), { headers: { "Content-Type": "application/json" } });
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2393
2550
|
const upstream = new AbortController();
|
|
2394
2551
|
const cleanupUpstreamAbort = linkAbortSignal(upstream, options.abortSignal);
|
|
2395
2552
|
const connectMs = config.connectTimeoutMs ?? 200_000;
|
|
@@ -2878,13 +3035,15 @@ async function handleResponsesInner(
|
|
|
2878
3035
|
// PRE-compaction history, and a later previous_response_id expansion would rehydrate the
|
|
2879
3036
|
// giant stale chain Codex just replaced.
|
|
2880
3037
|
...(routedCompaction ? {} : {
|
|
2881
|
-
onCompletedResponse: (response: Record<string, unknown>, providerState?: OcxProviderContinuationState) =>
|
|
3038
|
+
onCompletedResponse: (response: Record<string, unknown>, providerState?: OcxProviderContinuationState) => {
|
|
3039
|
+
if (activeAdapter.name === "kiro") rememberDeliveredFinalAnswer(parsed, response);
|
|
2882
3040
|
rememberResponseState(
|
|
2883
3041
|
parsed._rawBody,
|
|
2884
3042
|
response,
|
|
2885
3043
|
continuationStateForResponse(providerState),
|
|
2886
3044
|
activeAdapter.name === "kiro" ? { force: true } : undefined,
|
|
2887
|
-
)
|
|
3045
|
+
);
|
|
3046
|
+
},
|
|
2888
3047
|
}),
|
|
2889
3048
|
},
|
|
2890
3049
|
);
|
|
@@ -2944,6 +3103,7 @@ async function handleResponsesInner(
|
|
|
2944
3103
|
});
|
|
2945
3104
|
// See the streaming branch: compaction turns skip the continuation cache.
|
|
2946
3105
|
if (!routedCompaction) {
|
|
3106
|
+
if (activeAdapter.name === "kiro") rememberDeliveredFinalAnswer(parsed, json);
|
|
2947
3107
|
rememberResponseState(
|
|
2948
3108
|
parsed._rawBody,
|
|
2949
3109
|
json,
|
|
@@ -60,7 +60,7 @@ export function imageGenToolCallAliases(
|
|
|
60
60
|
return aliases;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
/**
|
|
63
|
+
/** Restore exact client tool identities. Callers own destination-scoped alias admission. */
|
|
64
64
|
function restoreImageGenCalls(
|
|
65
65
|
value: unknown,
|
|
66
66
|
aliases: ReadonlyMap<string, NamespacedTool>,
|
|
@@ -86,7 +86,7 @@ function restoreImageGenCalls(
|
|
|
86
86
|
|
|
87
87
|
const name = typeof value.name === "string" ? value.name : undefined;
|
|
88
88
|
const target = value.type === "function_call" && name ? aliases.get(name) : undefined;
|
|
89
|
-
if (target) {
|
|
89
|
+
if (target && (value.namespace === undefined || value.namespace === target.namespace)) {
|
|
90
90
|
restored.name = target.name;
|
|
91
91
|
restored.namespace = target.namespace;
|
|
92
92
|
changed = true;
|
|
@@ -7,7 +7,7 @@ import type { TranslatorBudget } from "../lib/translator-budget";
|
|
|
7
7
|
* parse/stringify pass so a tee'd stream is not re-framed twice per event.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
export type SsePayloadRewrite = (payload: string) => string;
|
|
10
|
+
export type SsePayloadRewrite = ((payload: string) => string) & { dispose?: () => void };
|
|
11
11
|
|
|
12
12
|
/** Split one complete SSE event block while retaining its original blank-line delimiter. */
|
|
13
13
|
export function nextSseBlock(buffer: string): { block: string; delimiter: string; rest: string } | null {
|
|
@@ -54,11 +54,19 @@ export function replaceSseDataPayload(block: string, payload: string): string {
|
|
|
54
54
|
export function composeSsePayloadRewrites(...rewrites: SsePayloadRewrite[]): SsePayloadRewrite {
|
|
55
55
|
if (rewrites.length === 0) return (payload) => payload;
|
|
56
56
|
if (rewrites.length === 1) return rewrites[0]!;
|
|
57
|
-
|
|
57
|
+
const composed = ((payload: string) => {
|
|
58
58
|
let next = payload;
|
|
59
59
|
for (const rewrite of rewrites) next = rewrite(next);
|
|
60
60
|
return next;
|
|
61
|
-
};
|
|
61
|
+
}) as SsePayloadRewrite;
|
|
62
|
+
if (rewrites.some(rewrite => rewrite.dispose !== undefined)) {
|
|
63
|
+
composed.dispose = () => {
|
|
64
|
+
for (const rewrite of rewrites) {
|
|
65
|
+
try { rewrite.dispose?.(); } catch { /* rewrite teardown is best-effort */ }
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return composed;
|
|
62
70
|
}
|
|
63
71
|
|
|
64
72
|
/**
|
|
@@ -122,6 +130,12 @@ export function relaySseWithPayloadRewrite(
|
|
|
122
130
|
buffer = "";
|
|
123
131
|
bufferBytes = 0;
|
|
124
132
|
};
|
|
133
|
+
let rewriteDisposed = false;
|
|
134
|
+
const disposeRewrite = (): void => {
|
|
135
|
+
if (rewriteDisposed) return;
|
|
136
|
+
rewriteDisposed = true;
|
|
137
|
+
try { rewrite.dispose?.(); } catch { /* stream teardown must not throw */ }
|
|
138
|
+
};
|
|
125
139
|
|
|
126
140
|
const emitProcessedBlocks = (
|
|
127
141
|
controller: ReadableStreamDefaultController<Uint8Array>,
|
|
@@ -156,6 +170,7 @@ export function relaySseWithPayloadRewrite(
|
|
|
156
170
|
appendBuffer(decoder.decode());
|
|
157
171
|
emitProcessedBlocks(controller, true);
|
|
158
172
|
releaseBuffer();
|
|
173
|
+
disposeRewrite();
|
|
159
174
|
controller.close();
|
|
160
175
|
return;
|
|
161
176
|
}
|
|
@@ -163,12 +178,14 @@ export function relaySseWithPayloadRewrite(
|
|
|
163
178
|
emitProcessedBlocks(controller);
|
|
164
179
|
} catch (error) {
|
|
165
180
|
releaseBuffer();
|
|
181
|
+
disposeRewrite();
|
|
166
182
|
try { await reader.cancel(error); } catch { /* already closed */ }
|
|
167
183
|
controller.error(error);
|
|
168
184
|
}
|
|
169
185
|
},
|
|
170
186
|
cancel(reason) {
|
|
171
187
|
releaseBuffer();
|
|
188
|
+
disposeRewrite();
|
|
172
189
|
reader.cancel(reason).catch(() => {});
|
|
173
190
|
},
|
|
174
191
|
});
|
package/src/types.ts
CHANGED
|
@@ -28,7 +28,7 @@ export interface OcxParsedRequest {
|
|
|
28
28
|
*/
|
|
29
29
|
_cursorIsolateConversation?: boolean;
|
|
30
30
|
/** Account-scoped, non-secret Kiro request metadata selected with the OAuth access token. */
|
|
31
|
-
_kiroAuthContext?: Pick<KiroOAuthMetadata, "profileArn" | "apiRegion" | "ssoRegion">;
|
|
31
|
+
_kiroAuthContext?: Pick<KiroOAuthMetadata, "profileArn" | "apiRegion" | "ssoRegion" | "authType">;
|
|
32
32
|
/** Provider-private continuation metadata resolved from the Responses previous_response_id chain. */
|
|
33
33
|
_providerContinuation?: OcxProviderContinuationState;
|
|
34
34
|
/**
|
|
@@ -85,6 +85,12 @@ export interface OcxAssistantMessage {
|
|
|
85
85
|
phase?: OcxMessagePhase;
|
|
86
86
|
model?: string;
|
|
87
87
|
timestamp: number;
|
|
88
|
+
/**
|
|
89
|
+
* Kiro `reasoningContent.redactedContent` for THIS assistant turn — an opaque encrypted blob
|
|
90
|
+
* Kiro replays to preserve model reasoning across turns. Provider-specific and unrenderable, so
|
|
91
|
+
* it rides the message rather than a content part: any other adapter simply ignores it.
|
|
92
|
+
*/
|
|
93
|
+
kiroRedactedReasoning?: string;
|
|
88
94
|
}
|
|
89
95
|
|
|
90
96
|
export interface OcxDeveloperMessage {
|
|
@@ -254,6 +260,9 @@ export type AdapterEvent =
|
|
|
254
260
|
// opaque redacted_thinking blocks. Both must be replayed verbatim or tool-use turns 400.
|
|
255
261
|
| { type: "thinking_signature"; signature: string }
|
|
256
262
|
| { type: "redacted_thinking"; data: string }
|
|
263
|
+
// Kiro reasoning round-trip: the encrypted `redactedContent` blob for the CURRENT assistant turn.
|
|
264
|
+
// Never rendered — it only rides the reasoning item's envelope so the next request can replay it.
|
|
265
|
+
| { type: "kiro_redacted_reasoning"; data: string }
|
|
257
266
|
| { type: "reasoning_raw_delta"; text: string }
|
|
258
267
|
| { type: "tool_call_start"; id: string; name: string }
|
|
259
268
|
| { type: "tool_call_delta"; arguments: string }
|
package/src/usage/cost.ts
CHANGED
|
Binary file
|
|
@@ -29,6 +29,9 @@ export interface ExpectedPriceOverlay {
|
|
|
29
29
|
status: ExpectedPriceStatus;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
const GPT6_ASTRA: Cost4 = { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 };
|
|
33
|
+
const ASTRA_API_PRICING = "https://developers.openai.com/api/docs/models/gpt-6-astra";
|
|
34
|
+
|
|
32
35
|
const GEMINI_31_PRO: Cost4 = { input: 2, output: 12, cacheRead: 0.2, cacheWrite: 0 };
|
|
33
36
|
const GEMINI_36_FLASH: Cost4 = { input: 1.5, output: 7.5, cacheRead: 0.15, cacheWrite: 0 };
|
|
34
37
|
const MINIMAX_M21_HIGHSPEED: Cost4 = { input: 0.6, output: 2.4, cacheRead: 0.03, cacheWrite: 0.375 };
|
|
@@ -59,6 +62,9 @@ const KIMI_PRICING = "https://platform.kimi.ai/docs/pricing (official table; cac
|
|
|
59
62
|
const QWEN38_ROUTEWAY_PRICING = "https://routeway.ai/models/qwen3.8-max-preview (temporary reseller proxy; NOT Alibaba Token Plan billing; cacheWrite unpublished -> 0)";
|
|
60
63
|
|
|
61
64
|
export const EXPECTED_PRICE_OVERLAYS: readonly ExpectedPriceOverlay[] = [
|
|
65
|
+
{ provider: "openai-apikey", modelId: "gpt-6-astra", cost4: GPT6_ASTRA, source: ASTRA_API_PRICING, verifiedAt: "2026-09-07", status: "verified" },
|
|
66
|
+
// Dollar display is an API-reference comparison, not a conversion of subscription credits.
|
|
67
|
+
{ provider: "openai", modelId: "gpt-6-astra", cost4: GPT6_ASTRA, source: `API-reference comparison estimate: ${ASTRA_API_PRICING}`, verifiedAt: "2026-09-07", status: "verified-derived" },
|
|
62
68
|
// claude-opus-5 is exposed by three providers but absent from the jawcode bundle, so
|
|
63
69
|
// cost resolution returned null and the Logs `~$` column rendered an em dash. The
|
|
64
70
|
// model-level vendor fallback only searches jawcode metadata, never overlays, so one
|
|
@@ -150,6 +156,7 @@ export function findExpectedPriceOverlay(
|
|
|
150
156
|
* Models not listed here fall back to 1× (no multiplier).
|
|
151
157
|
*/
|
|
152
158
|
export const PRIORITY_MULTIPLIERS: Readonly<Record<string, number>> = {
|
|
159
|
+
"gpt-6-astra": 2,
|
|
153
160
|
"gpt-5.6-sol": 2,
|
|
154
161
|
"gpt-5.6-terra": 1.6,
|
|
155
162
|
"gpt-5.6-luna": 0.4,
|
|
Binary file
|