@bitkyc08/opencodex 2.55.0 → 2.57.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.
- package/bin/ocx.mjs +10 -0
- package/gui/dist/assets/{index-BBOZWGB6.css → index-C5-RdDmD.css} +1 -1
- package/gui/dist/assets/{index-VuoiWj9J.js → index-Cz7CLdif.js} +21 -21
- package/gui/dist/index.html +2 -2
- package/package.json +4 -3
- package/src/adapters/base.ts +21 -0
- package/src/adapters/codebuddy/adapter.ts +2 -1
- package/src/adapters/codebuddy/scaffold-guard.ts +248 -0
- package/src/adapters/command-code.ts +1 -1
- package/src/adapters/cursor/envelope-echo.ts +8 -2
- package/src/adapters/cursor/transport-retry.ts +46 -1
- package/src/adapters/cursor.ts +4 -0
- package/src/adapters/google.ts +7 -7
- package/src/adapters/kiro/adapter.ts +42 -1
- package/src/adapters/kiro/payload.ts +17 -3
- package/src/adapters/kiro/reasoning.ts +70 -7
- package/src/adapters/kiro/stream.ts +8 -2
- package/src/adapters/kiro/wire.ts +2 -1
- package/src/adapters/kiro-events.ts +21 -13
- package/src/adapters/kiro-retry.ts +23 -4
- package/src/adapters/openai-chat/errors.ts +116 -0
- package/src/adapters/openai-chat/messages.ts +346 -0
- package/src/adapters/openai-chat/passthrough.ts +146 -0
- package/src/adapters/openai-chat/response-events.ts +117 -0
- package/src/adapters/openai-chat/tool-call-validation.ts +200 -0
- package/src/adapters/openai-chat/tool-name-registry.ts +166 -0
- package/src/adapters/openai-chat/tool-schema.ts +495 -0
- package/src/adapters/openai-chat/wire.ts +50 -0
- package/src/adapters/openai-chat.ts +40 -1452
- package/src/adapters/openai-responses/canonical-forward.ts +202 -0
- package/src/adapters/openai-responses/image-gen.ts +406 -0
- package/src/adapters/openai-responses/internal.ts +3 -0
- package/src/adapters/openai-responses/passthrough.ts +642 -0
- package/src/adapters/openai-responses/prompt-cache.ts +83 -0
- package/src/adapters/openai-responses/reasoning.ts +220 -0
- package/src/adapters/openai-responses/request-strips.ts +185 -0
- package/src/adapters/openai-responses/tool-output-recovery.ts +509 -0
- package/src/adapters/openai-responses/tool-schema.ts +293 -0
- package/src/adapters/openai-responses/web-search.ts +156 -0
- package/src/adapters/openai-responses.ts +4 -2625
- package/src/bridge/errors.ts +58 -0
- package/src/bridge/internal.ts +174 -0
- package/src/bridge/response-json.ts +630 -0
- package/src/bridge/sse.ts +1462 -0
- package/src/bridge.ts +5 -2204
- package/src/chat/inbound.ts +12 -1
- package/src/claude/desktop-profile.ts +66 -9
- package/src/claude/outbound.ts +18 -0
- package/src/cli/account-main.ts +1 -1
- package/src/cli/capabilities.ts +2 -2
- package/src/cli/combo.ts +10 -1
- package/src/cli/index.ts +48 -5
- package/src/cli/registry.ts +2 -1
- package/src/cli/system-command.ts +4 -4
- package/src/clients/config-export.ts +7 -3
- package/src/codex/account-label.ts +14 -3
- package/src/codex/account-lifecycle.ts +3 -0
- package/src/codex/account-store.ts +184 -35
- package/src/codex/account-usability.ts +21 -0
- package/src/codex/auth-api/account-list.ts +507 -0
- package/src/codex/auth-api/http.ts +32 -0
- package/src/codex/auth-api/login-flow.ts +566 -0
- package/src/codex/auth-api/login-state.ts +64 -0
- package/src/codex/auth-api/main-account-probe.ts +331 -0
- package/src/codex/auth-api/pool-mode-gate.ts +274 -0
- package/src/codex/auth-api/pool-quota-probe.ts +512 -0
- package/src/codex/auth-api/reset-credit-service.ts +431 -0
- package/src/codex/auth-api/routes.ts +425 -0
- package/src/codex/auth-api/runtime-config.ts +48 -0
- package/src/codex/auth-api.ts +27 -3118
- package/src/codex/auth-context.ts +252 -35
- package/src/codex/catalog/aggregation.ts +80 -1
- package/src/codex/catalog/auto-review.ts +507 -0
- package/src/codex/catalog/build-entries.ts +981 -0
- package/src/codex/catalog/combo-member.ts +375 -0
- package/src/codex/catalog/derive-entry.ts +229 -0
- package/src/codex/catalog/effort.ts +0 -1
- package/src/codex/catalog/gated-native-warn.ts +63 -0
- package/src/codex/catalog/gather-capture.ts +533 -0
- package/src/codex/catalog/model-hints.ts +691 -0
- package/src/codex/catalog/model-visibility.ts +305 -0
- package/src/codex/catalog/provider-fetch.ts +52 -2942
- package/src/codex/catalog/provider-models.ts +685 -0
- package/src/codex/catalog/remote.ts +30 -0
- package/src/codex/catalog/restore.ts +132 -0
- package/src/codex/catalog/retained-sync.ts +714 -0
- package/src/codex/catalog/routed-gather.ts +895 -0
- package/src/codex/catalog/subagent-roster.ts +176 -0
- package/src/codex/catalog/sync.ts +52 -2698
- package/src/codex/cli-install-provenance.ts +7 -1
- package/src/codex/convergence.ts +7 -2
- package/src/codex/desktop-app/types.ts +11 -2
- package/src/codex/desktop-app/windows.ts +5 -5
- package/src/codex/inject/config-toml.ts +563 -0
- package/src/codex/inject/remove.ts +192 -0
- package/src/codex/inject/restore.ts +567 -0
- package/src/codex/inject/routing-classify.ts +109 -0
- package/src/codex/inject/routing-target.ts +125 -0
- package/src/codex/inject.ts +89 -1444
- package/src/codex/lineage.ts +458 -0
- package/src/codex/model-entitlements.ts +152 -15
- package/src/codex/pool-refresh-backoff.ts +161 -0
- package/src/codex/quota-rejection.ts +104 -15
- package/src/codex/routing/active-account.ts +194 -0
- package/src/codex/routing/cache-affinity.ts +70 -0
- package/src/codex/routing/cooldown-math.ts +285 -0
- package/src/codex/routing/health-store.ts +402 -0
- package/src/codex/routing/probe-lease.ts +358 -0
- package/src/codex/routing/selection.ts +780 -0
- package/src/codex/routing/thread-affinity.ts +586 -0
- package/src/codex/routing/transient-hold-dispatch.ts +141 -0
- package/src/codex/routing.ts +370 -2271
- package/src/codex/shim-fingerprint.ts +223 -0
- package/src/codex/shim-inspect.ts +175 -0
- package/src/codex/shim-probe.ts +367 -0
- package/src/codex/shim-restore-lock.ts +169 -0
- package/src/codex/shim-state-file.ts +151 -0
- package/src/codex/shim-templates.ts +265 -0
- package/src/codex/shim.ts +48 -1268
- package/src/codex/warmup.ts +1 -1
- package/src/combos/failover.ts +85 -0
- package/src/combos/request.ts +17 -10
- package/src/combos/types.ts +23 -2
- package/src/config/diagnostics.ts +705 -0
- package/src/config/feature-flags.ts +55 -0
- package/src/config/live-reconcile.ts +403 -0
- package/src/config/load-degrade.ts +880 -0
- package/src/config/mutation-lock.ts +244 -0
- package/src/config/openai-tier-backup.ts +268 -0
- package/src/config/pending-teardown.ts +31 -0
- package/src/config/persist-unlocked.ts +92 -0
- package/src/config/proxy-env.ts +188 -0
- package/src/config/salvage.ts +244 -0
- package/src/config/schema/config-schema.ts +640 -0
- package/src/config/schema/leaf-validators.ts +855 -0
- package/src/config/warn-memo.ts +28 -0
- package/src/config.ts +234 -4481
- package/src/generated/compatibility-version.json +649 -121
- package/src/images/loop.ts +1 -1
- package/src/lib/errors.ts +17 -0
- package/src/lib/request-execution-budget.ts +198 -23
- package/src/lib/spend-reservation-ledger.ts +958 -0
- package/src/lib/state-store-registrations.ts +6 -2
- package/src/lib/test-home-guard.ts +85 -1
- package/src/lib/upstream-retry.ts +132 -21
- package/src/lib/windows-elevation.ts +76 -14
- package/src/lib/workflow-budget.ts +553 -30
- package/src/oauth/index.ts +2 -2
- package/src/oauth/key-providers.ts +2 -2
- package/src/providers/kiro-models.ts +4 -3
- package/src/providers/label.ts +19 -1
- package/src/providers/model-discovery.ts +16 -0
- package/src/providers/quota/account-cache.ts +441 -0
- package/src/providers/quota/antigravity.ts +295 -0
- package/src/providers/quota/report-cache.ts +320 -0
- package/src/providers/quota/vendor-probes-key.ts +1243 -0
- package/src/providers/quota/vendor-probes-oauth.ts +590 -0
- package/src/providers/quota.ts +324 -3079
- package/src/providers/registry/entries-core.ts +1228 -0
- package/src/providers/registry/entries-extended.ts +1213 -0
- package/src/providers/registry/model-seeds.ts +912 -0
- package/src/providers/registry/types.ts +352 -0
- package/src/providers/registry.ts +24 -3536
- package/src/responses/continuation-ownership.ts +29 -0
- package/src/responses/reasoning-envelope.ts +6 -3
- package/src/responses/state/replay-fingerprint.ts +80 -0
- package/src/responses/state/snapshot-codec.ts +104 -0
- package/src/responses/state/spill-failure.ts +118 -0
- package/src/responses/state/spill-queue.ts +665 -0
- package/src/responses/state/temp-recovery.ts +257 -0
- package/src/responses/state.ts +82 -1143
- package/src/routing/identity-domains.ts +456 -0
- package/src/routing/probe-lease.ts +613 -0
- package/src/server/chat-completions.ts +3 -1
- package/src/server/chat-native.ts +37 -9
- package/src/server/index/bounded-request.ts +88 -0
- package/src/server/index/live-sideband.ts +601 -0
- package/src/server/index/serve-options.ts +1766 -0
- package/src/server/index/startup-warnings.ts +213 -0
- package/src/server/index/websocket-handler.ts +339 -0
- package/src/server/index.ts +45 -2552
- package/src/server/inspection-tee.ts +107 -0
- package/src/server/live.ts +46 -1
- package/src/server/management/combo-routes.ts +10 -1
- package/src/server/management/route-registry.ts +26 -23
- package/src/server/management/shared.ts +8 -5
- package/src/server/management/workflow-budget-routes.ts +133 -0
- package/src/server/management-api.ts +12 -0
- package/src/server/relay-eager.ts +2 -0
- package/src/server/relay.ts +14 -19
- package/src/server/request-log-conversation.ts +9 -7
- package/src/server/request-log.ts +372 -4
- package/src/server/response-log-body.ts +153 -0
- package/src/server/responses/account-change-state.ts +307 -0
- package/src/server/responses/adapter-continuation.ts +540 -0
- package/src/server/responses/adapter-delivery.ts +208 -0
- package/src/server/responses/adapter-dispatch.ts +1042 -0
- package/src/server/responses/codex-ws-wire.ts +5 -0
- package/src/server/responses/collaboration.ts +74 -4
- package/src/server/responses/combo-session-recall.ts +68 -8
- package/src/server/responses/compact.ts +113 -17
- package/src/server/responses/completion-policy.ts +33 -0
- package/src/server/responses/core-auth.ts +529 -0
- package/src/server/responses/core-codex-account.ts +907 -0
- package/src/server/responses/core-combo-failure.ts +210 -0
- package/src/server/responses/core-combo.ts +787 -0
- package/src/server/responses/core-errors.ts +170 -0
- package/src/server/responses/core-lifetime.ts +95 -0
- package/src/server/responses/core-normalize.ts +350 -0
- package/src/server/responses/core-opaque-recovery.ts +380 -0
- package/src/server/responses/core-options.ts +159 -0
- package/src/server/responses/core-replay.ts +298 -0
- package/src/server/responses/core.ts +192 -8893
- package/src/server/responses/encrypted-payload.ts +0 -1
- package/src/server/responses/input-admission.ts +126 -6
- package/src/server/responses/passthrough-delivery.ts +869 -0
- package/src/server/responses/passthrough-dispatch.ts +1494 -0
- package/src/server/responses/passthrough-error.ts +38 -2
- package/src/server/responses/passthrough-execution.ts +54 -0
- package/src/server/responses/request-prepare.ts +1080 -0
- package/src/server/responses/request-send-budget.ts +259 -0
- package/src/server/responses/request-sidecar-auth.ts +149 -0
- package/src/server/responses/request-spend.ts +147 -0
- package/src/server/responses/request-transport.ts +803 -0
- package/src/server/responses/response-effects.ts +157 -0
- package/src/server/responses/run-turn-execution.ts +476 -0
- package/src/server/responses/sidecar-execution.ts +463 -0
- package/src/server/responses/terminal-guard.ts +65 -4
- package/src/server/responses-image-gen-repair.ts +1 -1
- package/src/server/responses-undeclared-tool-guard.ts +9 -5
- package/src/server/workflow-refusal.ts +84 -0
- package/src/service/windows-ops.ts +210 -16
- package/src/service/windows-scheduler.ts +28 -21
- package/src/service.ts +1 -1
- package/src/types/config.ts +34 -1
- package/src/types/request.ts +8 -5
- package/src/types/tools.ts +24 -0
- package/src/types.ts +2 -0
- package/src/update/index.ts +10 -0
- package/src/update/stop-contract.d.mts +1 -0
- package/src/update/stop-contract.mjs +19 -0
- package/src/update/stop-decision.d.mts +1 -1
- package/src/update/stop-decision.mjs +12 -3
- package/src/usage/log.ts +147 -1
- package/src/usage/summary.ts +171 -21
- package/src/vision/anthropic-describe.ts +1 -1
- package/src/vision/describe.ts +5 -5
- package/src/web-search/anthropic-executor.ts +1 -1
- package/src/web-search/exa-executor.ts +1 -1
- package/src/web-search/executor.ts +1 -1
- package/src/web-search/gemini-executor.ts +1 -1
- package/src/web-search/loop.ts +1 -1
- package/src/web-search/ollama-executor.ts +1 -1
- package/src/web-search/parse.ts +67 -14
- package/src/web-search/passthrough-bridge.ts +64 -31
- package/src/web-search/xai-executor.ts +1 -1
|
@@ -4,21 +4,46 @@ import type { OcxParsedRequest } from "../../types";
|
|
|
4
4
|
export type KiroReasoningMode = "native" | "emulated";
|
|
5
5
|
|
|
6
6
|
// Kiro takes a verified native effort field for these models, and each model family names it
|
|
7
|
-
// differently: the
|
|
8
|
-
// Models absent from this table fall back to emulated thinking
|
|
7
|
+
// differently: the GPT-5.6 family's `reasoning.effort` versus the Claude-specific
|
|
8
|
+
// `output_config.effort`. Models absent from this table fall back to emulated thinking
|
|
9
|
+
// instructions.
|
|
10
|
+
//
|
|
11
|
+
// The GPT-5.6 entries are measured against the live runtime rather than inferred from the vendor
|
|
12
|
+
// schema: the field is accepted (HTTP 200) and the encrypted reasoning blob that comes back grows
|
|
13
|
+
// with the effort. On one fixed hard prompt — a primality search plus a 20-bit recurrence count —
|
|
14
|
+
// luna's blob measured 5,130 chars at `low`, 16,686 at `medium`, 30,670 at `high` and 48,594 at
|
|
15
|
+
// `max`, against 13,118 with no effort signal at all; terra's measured 34,590 and 38,106 at native
|
|
16
|
+
// `max` against 11,758 and 17,598 bare, two repetitions each. The channel this replaces — the
|
|
17
|
+
// emulated `<thinking_mode>` tag block, which was all those models used to receive — measured
|
|
18
|
+
// 21,202 (`low`) and 28,302 (`max`) for luna, i.e. between that model's native `medium` and
|
|
19
|
+
// `high`, never reaching native `max`. `gpt-5.6-sol`'s native `max` cross-checked at 30,498 on the
|
|
20
|
+
// same prompt. Terra's absence from this table was therefore an omission rather than a capability
|
|
21
|
+
// difference: what the earlier Sol-only scope recorded was not reproducible here.
|
|
9
22
|
export const KIRO_NATIVE_EFFORT_FIELDS: Record<string, "reasoning" | "output_config"> = {
|
|
10
23
|
"gpt-5.6-sol": "reasoning",
|
|
24
|
+
"gpt-5.6-terra": "reasoning",
|
|
25
|
+
"gpt-5.6-luna": "reasoning",
|
|
11
26
|
"claude-opus-5": "output_config",
|
|
12
27
|
};
|
|
13
28
|
|
|
14
29
|
export const KIRO_NATIVE_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
15
30
|
|
|
16
|
-
|
|
17
|
-
|
|
31
|
+
// The newly enabled models have evidence for these rungs only. Keep the previous
|
|
32
|
+
// emulation for xhigh, and never widen their native wire when the shared ladder grows.
|
|
33
|
+
const KIRO_LUNA_TERRA_NATIVE_EFFORTS = new Set(["low", "medium", "high", "max"]);
|
|
34
|
+
|
|
35
|
+
export function kiroNativeEffortField(
|
|
36
|
+
modelId: string,
|
|
37
|
+
effort?: string,
|
|
38
|
+
): "reasoning" | "output_config" | undefined {
|
|
39
|
+
const model = normalizeKiroModelId(modelId);
|
|
40
|
+
if ((model === "gpt-5.6-luna" || model === "gpt-5.6-terra")
|
|
41
|
+
&& effort !== undefined && !KIRO_LUNA_TERRA_NATIVE_EFFORTS.has(effort)) return undefined;
|
|
42
|
+
return KIRO_NATIVE_EFFORT_FIELDS[model];
|
|
18
43
|
}
|
|
19
44
|
|
|
20
|
-
export function kiroReasoningMode(modelId: string): KiroReasoningMode {
|
|
21
|
-
return kiroNativeEffortField(modelId) ? "native" : "emulated";
|
|
45
|
+
export function kiroReasoningMode(modelId: string, effort?: string): KiroReasoningMode {
|
|
46
|
+
return kiroNativeEffortField(modelId, effort) ? "native" : "emulated";
|
|
22
47
|
}
|
|
23
48
|
|
|
24
49
|
export function kiroThinkingBudget(parsed: OcxParsedRequest): number | undefined {
|
|
@@ -38,7 +63,7 @@ export function kiroThinkingBudget(parsed: OcxParsedRequest): number | undefined
|
|
|
38
63
|
}
|
|
39
64
|
|
|
40
65
|
export function injectKiroThinkingTags(content: string, parsed: OcxParsedRequest): string {
|
|
41
|
-
if (kiroReasoningMode(parsed.modelId) !== "emulated") return content;
|
|
66
|
+
if (kiroReasoningMode(parsed.modelId, parsed.options.reasoning) !== "emulated") return content;
|
|
42
67
|
const budget = kiroThinkingBudget(parsed);
|
|
43
68
|
if (!budget) return content;
|
|
44
69
|
const instruction = [
|
|
@@ -54,3 +79,41 @@ export function injectKiroThinkingTags(content: string, parsed: OcxParsedRequest
|
|
|
54
79
|
content,
|
|
55
80
|
].join("\n");
|
|
56
81
|
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The blob from a Kiro `reasoningContentEvent` has two possible homes on a replayed assistant
|
|
85
|
+
* turn, and the wire validates the SHAPE of each rather than its content: `signature` takes the
|
|
86
|
+
* emitted string verbatim, while `redactedContent` is a base64 member. The `.KTR~~…` value every
|
|
87
|
+
* GPT-5.6 capture returns is NOT valid base64, which is exactly why replaying it as
|
|
88
|
+
* `redactedContent` — what this proxy did before the field was measured — came back as
|
|
89
|
+
* REQUEST_BODY_INVALID ("Improperly formed request").
|
|
90
|
+
*
|
|
91
|
+
* The blob travels as ONE opaque string: adapter event, `ocxr1:` reasoning envelope, then
|
|
92
|
+
* `OcxAssistantMessage.kiroRedactedReasoning`. The field it arrived on therefore rides that same
|
|
93
|
+
* string, instead of a second parallel value that could drift from it. Provider data cannot forge
|
|
94
|
+
* the tag: the other channel is base64, whose alphabet has no colon.
|
|
95
|
+
*/
|
|
96
|
+
export const KIRO_REASONING_SIGNATURE_TAG = "signature:";
|
|
97
|
+
|
|
98
|
+
export function tagKiroReasoningBlob(field: "signature" | "redactedContent", data: string): string {
|
|
99
|
+
return field === "signature" ? KIRO_REASONING_SIGNATURE_TAG + data : data;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The wire field a stored blob arrived on, and its untagged value. */
|
|
103
|
+
export function splitKiroReasoningBlob(value: string): { field: "signature" | "redactedContent"; data: string } {
|
|
104
|
+
return value.startsWith(KIRO_REASONING_SIGNATURE_TAG)
|
|
105
|
+
? { field: "signature", data: value.slice(KIRO_REASONING_SIGNATURE_TAG.length) }
|
|
106
|
+
: { field: "redactedContent", data: value };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The `reasoningContent` object on an `assistantResponseMessage`. Exactly one member is set: the
|
|
111
|
+
* wire validates the shape, so the two cannot be substituted for each other.
|
|
112
|
+
*/
|
|
113
|
+
export type KiroReasoningContent = { signature: string } | { redactedContent: string };
|
|
114
|
+
|
|
115
|
+
/** `reasoningContent` for a replayed `assistantResponseMessage`, carrying the blob verbatim. */
|
|
116
|
+
export function kiroReasoningContent(value: string): KiroReasoningContent {
|
|
117
|
+
const { field, data } = splitKiroReasoningBlob(value);
|
|
118
|
+
return field === "signature" ? { signature: data } : { redactedContent: data };
|
|
119
|
+
}
|
|
@@ -19,6 +19,7 @@ import { noteKiroTransientThrottle } from "../kiro-retry";
|
|
|
19
19
|
import { KiroThinkingParser } from "../kiro-thinking";
|
|
20
20
|
import { isCompleteKiroToolInput, kiroTruncationErrorMessage } from "../kiro-truncation";
|
|
21
21
|
import { isValidKiroConversationId } from "../kiro-wire";
|
|
22
|
+
import { tagKiroReasoningBlob } from "./reasoning";
|
|
22
23
|
import { estimateKiroTokens, kiroUpstreamContextWindow } from "./usage";
|
|
23
24
|
|
|
24
25
|
// Stream parsing (shared by parseStream + parseResponse)
|
|
@@ -633,8 +634,13 @@ async function* parseKiroAttemptEvents(
|
|
|
633
634
|
if (ev.data) {
|
|
634
635
|
yield* emitRetained(stage({ type: "reasoning_raw_delta", text: ev.data }));
|
|
635
636
|
}
|
|
636
|
-
|
|
637
|
-
|
|
637
|
+
// The blob is replayed on the field it arrived on, so remember that field here — this is
|
|
638
|
+
// the only place that still knows it. See kiro/reasoning.ts for why the distinction is
|
|
639
|
+
// load-bearing rather than cosmetic.
|
|
640
|
+
if (ev.signature) {
|
|
641
|
+
yield* emitRetained(stage({ type: "kiro_redacted_reasoning", data: tagKiroReasoningBlob("signature", ev.signature) }));
|
|
642
|
+
} else if (ev.redactedContent) {
|
|
643
|
+
yield* emitRetained(stage({ type: "kiro_redacted_reasoning", data: tagKiroReasoningBlob("redactedContent", ev.redactedContent) }));
|
|
638
644
|
}
|
|
639
645
|
break;
|
|
640
646
|
case "context_usage":
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { OcxProviderConfig } from "../../types";
|
|
2
2
|
import type { KiroImage } from "../kiro-images";
|
|
3
|
+
import type { KiroReasoningContent } from "./reasoning";
|
|
3
4
|
|
|
4
5
|
export const AMZ_TARGET = "AmazonCodeWhispererStreamingService.GenerateAssistantResponse";
|
|
5
6
|
export const SDK_VERSION = "1.0.27";
|
|
@@ -51,7 +52,7 @@ export interface KiroHistoryEntry {
|
|
|
51
52
|
assistantResponseMessage?: {
|
|
52
53
|
content: string;
|
|
53
54
|
toolUses?: KiroToolUse[];
|
|
54
|
-
reasoningContent?:
|
|
55
|
+
reasoningContent?: KiroReasoningContent;
|
|
55
56
|
};
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -3,7 +3,7 @@ import { kiroTruncationReason } from "./kiro-truncation";
|
|
|
3
3
|
|
|
4
4
|
export type ParsedKiroEvent =
|
|
5
5
|
| { type: "content"; data?: string; modelId?: string }
|
|
6
|
-
| { type: "reasoning"; data?: string; redactedContent?: string }
|
|
6
|
+
| { type: "reasoning"; data?: string; signature?: string; redactedContent?: string }
|
|
7
7
|
| { type: "context_usage"; contextUsagePercentage: number }
|
|
8
8
|
| { type: "tool"; name?: string; toolUseId?: string; input?: string; stop?: boolean }
|
|
9
9
|
| { type: "truncation"; data: string }
|
|
@@ -138,18 +138,26 @@ export function parseKiroEvent(eventType: string, payload: Uint8Array): ParsedKi
|
|
|
138
138
|
: {}),
|
|
139
139
|
};
|
|
140
140
|
case "reasoningContentEvent":
|
|
141
|
-
// `text` is plaintext reasoning;
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
141
|
+
// `text` is plaintext reasoning; the GPT-5.6 family (sol/terra/luna) instead returns an
|
|
142
|
+
// encrypted blob, and the field it arrives on has to be replayed unchanged (see
|
|
143
|
+
// kiro/reasoning.ts): `signature` carries the `.KTR~~…` value verbatim and is what every
|
|
144
|
+
// capture of those models sent, while `redactedContent` — the base64 shape a capture has
|
|
145
|
+
// never shown — stays accepted for any model that sends it. Keyed off the wire field, not the
|
|
146
|
+
// model id. Any of the three may be absent on a bare event.
|
|
147
|
+
{
|
|
148
|
+
const text = optionalString(eventType, parsed, "text");
|
|
149
|
+
const signature = optionalString(eventType, parsed, "signature");
|
|
150
|
+
const redacted = optionalString(eventType, parsed, "redactedContent");
|
|
151
|
+
return {
|
|
152
|
+
type: "reasoning",
|
|
153
|
+
...(text !== undefined ? { data: text } : {}),
|
|
154
|
+
...(signature !== undefined
|
|
155
|
+
? { signature }
|
|
156
|
+
: redacted !== undefined
|
|
157
|
+
? { redactedContent: redacted }
|
|
158
|
+
: {}),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
153
161
|
case "toolUseEvent":
|
|
154
162
|
return {
|
|
155
163
|
type: "tool",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AdapterFetchContext, AdapterRequest } from "./base";
|
|
2
|
+
import type { AttemptRecoveryKind } from "../usage/log";
|
|
2
3
|
import { classifyKiroHttpError, safeKiroHttpErrorMessage } from "./kiro-errors";
|
|
3
4
|
import { normalizeUpstreamHttpErrorResponse } from "./upstream-http-error";
|
|
4
5
|
import { readBoundedResponseBody } from "../lib/bounded-body";
|
|
@@ -159,6 +160,7 @@ async function fetchWithResetRecovery(
|
|
|
159
160
|
url: string,
|
|
160
161
|
ctx: AdapterFetchContext,
|
|
161
162
|
timeoutMs: number,
|
|
163
|
+
notePhysicalSend: (reset: boolean) => void,
|
|
162
164
|
): Promise<Response> {
|
|
163
165
|
let lastError: unknown;
|
|
164
166
|
for (let attempt = 0; attempt < RESET_ATTEMPTS; attempt++) {
|
|
@@ -170,6 +172,9 @@ async function fetchWithResetRecovery(
|
|
|
170
172
|
if (decision && (!decision.allowed || !decision.permit.use())) {
|
|
171
173
|
throw new SendBudgetExhaustedError(url);
|
|
172
174
|
}
|
|
175
|
+
// Reported after admission and before dispatch, so a refused send is never counted and an
|
|
176
|
+
// admitted one is counted exactly once whichever way the fetch below settles.
|
|
177
|
+
notePhysicalSend(attempt > 0);
|
|
173
178
|
try {
|
|
174
179
|
const headers = new Headers(request.headers);
|
|
175
180
|
const recovered = attempt > 0;
|
|
@@ -252,14 +257,15 @@ async function fetchKiroAttempt(
|
|
|
252
257
|
request: AdapterRequest,
|
|
253
258
|
ctx: AdapterFetchContext,
|
|
254
259
|
timeoutMs: number,
|
|
260
|
+
notePhysicalSend: (reset: boolean) => void,
|
|
255
261
|
): Promise<Response> {
|
|
256
262
|
const legacy = legacyUrl(request.url);
|
|
257
263
|
let response: Response;
|
|
258
264
|
try {
|
|
259
|
-
response = await fetchWithResetRecovery(request, request.url, ctx, timeoutMs);
|
|
265
|
+
response = await fetchWithResetRecovery(request, request.url, ctx, timeoutMs, notePhysicalSend);
|
|
260
266
|
} catch (error) {
|
|
261
267
|
if (!legacy || !endpointConnectFailure(error)) throw error;
|
|
262
|
-
return fetchWithResetRecovery(request, legacy, ctx, timeoutMs);
|
|
268
|
+
return fetchWithResetRecovery(request, legacy, ctx, timeoutMs, notePhysicalSend);
|
|
263
269
|
}
|
|
264
270
|
|
|
265
271
|
if (legacy && !response.ok) {
|
|
@@ -267,7 +273,7 @@ async function fetchKiroAttempt(
|
|
|
267
273
|
response = inspected.response;
|
|
268
274
|
if (inspected.fallback) {
|
|
269
275
|
cancelResponseBodyBestEffort(response);
|
|
270
|
-
response = await fetchWithResetRecovery(request, legacy, ctx, timeoutMs);
|
|
276
|
+
response = await fetchWithResetRecovery(request, legacy, ctx, timeoutMs, notePhysicalSend);
|
|
271
277
|
}
|
|
272
278
|
}
|
|
273
279
|
return response;
|
|
@@ -281,12 +287,25 @@ async function fetchKiroAttempt(
|
|
|
281
287
|
export async function fetchKiroWithRetry(request: AdapterRequest, ctx: AdapterFetchContext = {}): Promise<Response> {
|
|
282
288
|
const timeoutMs = ctx.timeoutMs ?? 200_000;
|
|
283
289
|
let probeToken: symbol | undefined;
|
|
290
|
+
// One ordinal sequence for the whole call, across the throttle loop, the endpoint fallback
|
|
291
|
+
// and the reset ladder nested inside it. The caller records ordinal 1 itself, so this is what
|
|
292
|
+
// turns "one adapter call" back into the physical count the request actually made.
|
|
293
|
+
let physicalSends = 0;
|
|
294
|
+
let throttleRound = 0;
|
|
295
|
+
const notePhysicalSend = (reset: boolean): void => {
|
|
296
|
+
physicalSends += 1;
|
|
297
|
+
const recovery: AttemptRecoveryKind | undefined = reset
|
|
298
|
+
? "connection-reset"
|
|
299
|
+
: throttleRound > 0 ? "rate-limit-429" : undefined;
|
|
300
|
+
ctx.onPhysicalSend?.({ ordinal: physicalSends, ...(recovery ? { recovery } : {}) });
|
|
301
|
+
};
|
|
284
302
|
try {
|
|
285
303
|
for (let attempt = 0; attempt < THROTTLE_ATTEMPTS; attempt++) {
|
|
304
|
+
throttleRound = attempt;
|
|
286
305
|
if (!probeToken) probeToken = await enterKiroThrottleGate(ctx.abortSignal);
|
|
287
306
|
else await waitForKiroCooldown(ctx.abortSignal);
|
|
288
307
|
|
|
289
|
-
const response = await fetchKiroAttempt(request, ctx, timeoutMs);
|
|
308
|
+
const response = await fetchKiroAttempt(request, ctx, timeoutMs, notePhysicalSend);
|
|
290
309
|
const throttle = await inspectKiroThrottle(response, ctx.abortSignal);
|
|
291
310
|
if (!throttle || !throttle.transient) {
|
|
292
311
|
releaseKiroThrottleProbe(probeToken);
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { isCyberPolicyCode } from "../../lib/errors";
|
|
2
|
+
import { redactSecretString } from "../../lib/redact";
|
|
3
|
+
import type { AdapterEvent, OcxUsage } from "../../types";
|
|
4
|
+
|
|
5
|
+
// 260715 (issue #126): surface upstream error detail through the web-search sidecar loop.
|
|
6
|
+
// loop.ts only appends a suffix to "Provider error N" when the adapter exposes
|
|
7
|
+
// formatErrorBody; without it, strict OpenAI-compatible backends (NVIDIA NIM pydantic
|
|
8
|
+
// validation, "This model only supports single tool-calls at once!", etc.) were reduced
|
|
9
|
+
// to a bare status code. JSON-only extraction: recognized string fields are returned,
|
|
10
|
+
// HTML/non-JSON bodies yield "" so raw markup is never echoed to the client.
|
|
11
|
+
export function formatOpenAIChatErrorBody(status: number, _headers: Headers, payloadText: string): string {
|
|
12
|
+
let parsed: unknown;
|
|
13
|
+
try {
|
|
14
|
+
parsed = JSON.parse(payloadText);
|
|
15
|
+
} catch {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
const detail = extractErrorDetail(parsed);
|
|
19
|
+
if (!detail) return "";
|
|
20
|
+
return redactSecretString(detail).slice(0, 400);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function extractErrorDetail(parsed: unknown): string | undefined {
|
|
24
|
+
if (typeof parsed === "string") return parsed.trim() || undefined;
|
|
25
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return undefined;
|
|
26
|
+
const obj = parsed as Record<string, unknown>;
|
|
27
|
+
const err = obj.error;
|
|
28
|
+
if (typeof err === "string" && err.trim()) return err.trim();
|
|
29
|
+
if (err !== null && typeof err === "object" && !Array.isArray(err)) {
|
|
30
|
+
const msg = (err as Record<string, unknown>).message;
|
|
31
|
+
if (typeof msg === "string" && msg.trim()) return msg.trim();
|
|
32
|
+
}
|
|
33
|
+
const det = obj.detail;
|
|
34
|
+
if (typeof det === "string" && det.trim()) return det.trim();
|
|
35
|
+
if (Array.isArray(det)) {
|
|
36
|
+
const msgs = det
|
|
37
|
+
.map(item => (item !== null && typeof item === "object" && typeof (item as Record<string, unknown>).msg === "string"
|
|
38
|
+
? ((item as Record<string, unknown>).msg as string).trim()
|
|
39
|
+
: ""))
|
|
40
|
+
.filter(m => m.length > 0);
|
|
41
|
+
if (msgs.length > 0) return msgs.join("; ");
|
|
42
|
+
}
|
|
43
|
+
if (typeof obj.message === "string" && obj.message.trim()) return obj.message.trim();
|
|
44
|
+
if (typeof obj.title === "string" && obj.title.trim()) return obj.title.trim();
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function unwrapChatCompletionPayload(json: Record<string, unknown>): Record<string, unknown> {
|
|
49
|
+
if ((json.error !== undefined && json.error !== null) || Array.isArray(json.choices)) return json;
|
|
50
|
+
const data = json.data;
|
|
51
|
+
return data !== null && typeof data === "object" && !Array.isArray(data)
|
|
52
|
+
? data as Record<string, unknown>
|
|
53
|
+
: json;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface OpenAIChatError {
|
|
57
|
+
message?: unknown;
|
|
58
|
+
code?: unknown;
|
|
59
|
+
type?: unknown;
|
|
60
|
+
status?: unknown;
|
|
61
|
+
metadata?: unknown;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function safeUpstreamRequestId(metadata: unknown): string | undefined {
|
|
65
|
+
if (metadata === null || typeof metadata !== "object" || Array.isArray(metadata)) return undefined;
|
|
66
|
+
const record = metadata as Record<string, unknown>;
|
|
67
|
+
const value = record.request_id ?? record.requestId;
|
|
68
|
+
if (typeof value !== "string") return undefined;
|
|
69
|
+
const requestId = value.trim();
|
|
70
|
+
return /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(requestId)
|
|
71
|
+
&& redactSecretString(requestId) === requestId
|
|
72
|
+
? requestId
|
|
73
|
+
: undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function upstreamErrorEvent(
|
|
77
|
+
error: unknown,
|
|
78
|
+
usage?: OcxUsage,
|
|
79
|
+
): Extract<AdapterEvent, { type: "error" }> {
|
|
80
|
+
const details = error !== null && typeof error === "object" && !Array.isArray(error)
|
|
81
|
+
? error as OpenAIChatError
|
|
82
|
+
: undefined;
|
|
83
|
+
const rawMessage = typeof error === "string"
|
|
84
|
+
? error.trim() || "upstream error"
|
|
85
|
+
: typeof details?.message === "string" ? details.message : "upstream error";
|
|
86
|
+
const safeMessage = redactSecretString(rawMessage);
|
|
87
|
+
const requestId = safeUpstreamRequestId(details?.metadata);
|
|
88
|
+
const message = requestId !== undefined && !safeMessage.includes(requestId)
|
|
89
|
+
? `${safeMessage} (request ID: ${requestId})`
|
|
90
|
+
: safeMessage;
|
|
91
|
+
const code = typeof details?.code === "string"
|
|
92
|
+
? details.code
|
|
93
|
+
: typeof details?.code === "number" && Number.isFinite(details.code) && Number.isInteger(details.code)
|
|
94
|
+
? String(details.code)
|
|
95
|
+
: undefined;
|
|
96
|
+
const errorType = typeof details?.type === "string" ? details.type : undefined;
|
|
97
|
+
const codeStatus = typeof details?.code === "number"
|
|
98
|
+
&& Number.isInteger(details.code)
|
|
99
|
+
&& details.code >= 100
|
|
100
|
+
&& details.code <= 599
|
|
101
|
+
? details.code
|
|
102
|
+
: undefined;
|
|
103
|
+
const status = isCyberPolicyCode(code)
|
|
104
|
+
? 400
|
|
105
|
+
: typeof details?.status === "number" && Number.isInteger(details.status)
|
|
106
|
+
? details.status
|
|
107
|
+
: codeStatus;
|
|
108
|
+
return {
|
|
109
|
+
type: "error",
|
|
110
|
+
message,
|
|
111
|
+
...(usage !== undefined ? { usage } : {}),
|
|
112
|
+
...(code !== undefined ? { code } : {}),
|
|
113
|
+
...(errorType !== undefined ? { errorType } : {}),
|
|
114
|
+
...(status !== undefined ? { status } : {}),
|
|
115
|
+
};
|
|
116
|
+
}
|