@bitkyc08/opencodex 2.81.0 → 2.83.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 +166 -73
- package/gui/dist/assets/App-Ba8uG696.js +54 -0
- package/gui/dist/assets/{Tray-EwAOsP0S.js → Tray-IImbNPRy.js} +1 -1
- package/gui/dist/assets/{index-Brk6F3fn.css → index-BAbcj51J.css} +1 -1
- package/gui/dist/assets/{index-RsoaAxXY.js → index-BHRZdJuv.js} +13 -13
- package/gui/dist/assets/{quota-summary-D2lN8tDm.js → quota-summary-DiJO6sYY.js} +1 -1
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic.ts +1 -1
- package/src/adapters/cursor/catalog.ts +2 -2
- package/src/adapters/cursor/effort-map.ts +1 -1
- package/src/adapters/devin/live-models.ts +2 -2
- package/src/adapters/google-truncation.ts +22 -0
- package/src/adapters/google.ts +7 -3
- package/src/adapters/ollama-native.ts +18 -14
- package/src/adapters/openai-responses/compaction-search-history.ts +109 -0
- package/src/adapters/openai-responses/passthrough.ts +11 -4
- package/src/adapters/openai-responses/plaintext-reasoning-notice.ts +85 -0
- package/src/adapters/openai-responses/reasoning.ts +11 -1
- package/src/adapters/openai-responses/tool-output-recovery.ts +20 -0
- package/src/bridge/errors.ts +7 -1
- package/src/claude/agents-inject.ts +14 -3
- package/src/claude/context-windows.ts +22 -20
- package/src/claude/desktop-3p.ts +88 -12
- package/src/claude/desktop-picker.ts +1 -1
- package/src/claude/desktop-profile.ts +2 -1
- package/src/claude/gateway-cache.ts +18 -1
- package/src/claude/inbound.ts +77 -3
- package/src/claude/intercept/cli-catalog.ts +3 -0
- package/src/claude/intercept/picker-ca-persistence.ts +7 -6
- package/src/claude/intercept/picker-models.ts +14 -6
- package/src/claude/intercept/settings.ts +3 -0
- package/src/claude/long-context.ts +66 -0
- package/src/claude/model-info.ts +19 -16
- package/src/claude/outbound.ts +94 -9
- package/src/claude/subagent-model.ts +40 -11
- package/src/cli/account-auth.ts +20 -12
- package/src/cli/agent.ts +9 -5
- package/src/cli/aside-profile-recovery.ts +33 -0
- package/src/cli/capabilities-base.ts +5 -5
- package/src/cli/capabilities-integrations.ts +2 -2
- package/src/cli/cli-path-diagnostics.ts +132 -0
- package/src/cli/desktop-runtime-guidance.ts +65 -0
- package/src/cli/dispatch.ts +5 -0
- package/src/cli/doctor.ts +16 -3
- package/src/cli/index.ts +14 -9
- package/src/cli/integration-aside-sync.ts +2 -1
- package/src/cli/integrations.ts +16 -3
- package/src/cli/resolve.ts +26 -3
- package/src/cli/root.ts +4 -2
- package/src/cli/status.ts +64 -12
- package/src/cli/update-restart-child.ts +4 -2
- package/src/cli/update-restart-home.ts +28 -10
- package/src/cli/update-restart-service-record.ts +116 -0
- package/src/cli/update-restart-supervision.ts +96 -0
- package/src/cli/update-restart.ts +59 -13
- package/src/cli/v2.ts +27 -20
- package/src/cli/version-skew-notice.ts +77 -0
- package/src/clients/config-export/model-metadata.ts +16 -0
- package/src/clients/config-export/zcode-store.ts +46 -11
- package/src/clients/config-export/zcode.ts +3 -5
- package/src/clients/config-export.ts +1 -1
- package/src/clients/lazycodex.ts +1 -1
- package/src/clients/omo-role-models.ts +32 -7
- package/src/codex/auth-api/account-selection.ts +107 -0
- package/src/codex/auth-api/pool-mode-gate.ts +4 -5
- package/src/codex/auth-api/routes.ts +10 -49
- package/src/codex/auth-context.ts +28 -4
- package/src/codex/autostart-health.ts +12 -3
- package/src/codex/catalog/build-entries.ts +18 -0
- package/src/codex/catalog/bundled.ts +155 -26
- package/src/codex/catalog/combo-member.ts +3 -2
- package/src/codex/catalog/effort.ts +26 -5
- package/src/codex/catalog/gather-capture.ts +5 -0
- package/src/codex/catalog/model-hints.ts +15 -8
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/native-models.ts +1 -1
- package/src/codex/catalog/operator-tiers.ts +38 -0
- package/src/codex/catalog/provider-models.ts +68 -35
- package/src/codex/catalog/retained-sync.ts +7 -5
- package/src/codex/catalog/routed-gather.ts +15 -1
- package/src/codex/catalog-auto-refresh-sources.ts +3 -4
- package/src/codex/catalog-auto-refresh.ts +19 -0
- package/src/codex/catalog.ts +1 -1
- package/src/codex/codex-home-owner.ts +3 -0
- package/src/codex/config-write-lock.ts +333 -0
- package/src/codex/convergence.ts +2 -1
- package/src/codex/desktop-app/darwin.ts +1 -1
- package/src/codex/desktop-app/handoff.ts +1 -1
- package/src/codex/desktop-app/linux.ts +1 -1
- package/src/codex/desktop-app/lock.ts +1 -1
- package/src/codex/desktop-app/types.ts +1 -1
- package/src/codex/desktop-app/windows.ts +1 -1
- package/src/codex/desktop-app-restart.ts +1 -1
- package/src/codex/features.ts +257 -200
- package/src/codex/inject/config-write-section.ts +34 -0
- package/src/codex/inject/multi-agent-v2.ts +14 -6
- package/src/codex/inject/remove.ts +36 -11
- package/src/codex/inject/restore.ts +85 -20
- package/src/codex/inject-coordination.ts +15 -6
- package/src/codex/inject.ts +62 -20
- package/src/codex/journal.ts +62 -17
- package/src/codex/main-account-cache.ts +43 -0
- package/src/codex/main-account.ts +6 -7
- package/src/codex/paths.ts +2 -0
- package/src/codex/prepared-home.ts +18 -0
- package/src/codex/prompt-journal.ts +25 -12
- package/src/codex/prompt-layers.ts +49 -26
- package/src/codex/prompt-lock-claim.ts +134 -0
- package/src/codex/prompt-lock-evidence.ts +26 -0
- package/src/codex/prompt-lock-io.ts +16 -0
- package/src/codex/prompt-lock-owner.ts +130 -0
- package/src/codex/prompt-lock.ts +84 -53
- package/src/codex/routing/failure-window.ts +146 -0
- package/src/codex/routing/selection.ts +4 -1
- package/src/codex/routing.ts +13 -17
- package/src/codex/runtime.ts +106 -18
- package/src/codex/shim.ts +45 -19
- package/src/combos/jev-service-exchange.ts +281 -0
- package/src/combos/jev.ts +34 -220
- package/src/config/atomic-write.ts +1 -1
- package/src/config/codex-ws-reuse-setting.ts +10 -0
- package/src/config/live-reconcile.ts +17 -0
- package/src/config/load-degrade.ts +8 -0
- package/src/config/rebase-provenance.ts +11 -0
- package/src/config/schema/config-schema.ts +4 -0
- package/src/generated/compatibility-version.json +375 -227
- package/src/generated/model-metadata.ts +2 -2
- package/src/grok/inject.ts +276 -206
- package/src/grok/reset-coupon-ledger.ts +223 -57
- package/src/grok/reset-coupons.ts +13 -6
- package/src/grok/sync.ts +2 -1
- package/src/images/loop.ts +29 -11
- package/src/integrations/aside-profiles.ts +10 -3
- package/src/integrations/catalog-refresh.ts +18 -2
- package/src/integrations/cursor-detect.ts +1 -1
- package/src/integrations/writer.ts +9 -3
- package/src/lib/bun-path-runtime.d.mts +32 -0
- package/src/lib/bun-path-runtime.mjs +95 -0
- package/src/lib/bun-runtime-preflight.ts +140 -0
- package/src/lib/desktop-cli-handoff.d.mts +20 -0
- package/src/lib/desktop-cli-handoff.mjs +92 -0
- package/src/lib/desktop-cli-record.d.mts +29 -0
- package/src/lib/desktop-cli-record.mjs +102 -0
- package/src/lib/errors.ts +25 -0
- package/src/lib/redact.ts +2 -2
- package/src/lib/request-execution-budget.ts +8 -0
- package/src/lib/synced-state-location.ts +46 -6
- package/src/lib/test-home-guard.ts +87 -1
- package/src/lib/translator-budget.ts +17 -4
- package/src/lib/windows-atomic-replace.ts +38 -16
- package/src/lib/windows-elevation.ts +6 -0
- package/src/lib/windows-secret-acl.ts +92 -25
- package/src/lib/winsw.ts +2 -3
- package/src/oauth/anthropic-account-refusal.ts +39 -1
- package/src/oauth/anthropic-routing.ts +1 -1
- package/src/oauth/anthropic-send-ownership.ts +3 -3
- package/src/oauth/chatgpt.ts +113 -41
- package/src/oauth/index.ts +7 -3
- package/src/oauth/meta-muse-device.ts +1 -1
- package/src/oauth/store.ts +1 -1
- package/src/oauth/types.ts +1 -1
- package/src/providers/anthropic-fast.ts +1 -1
- package/src/providers/anthropic-reset-grant-ledger.ts +1 -1
- package/src/providers/azure-model-metadata.ts +115 -0
- package/src/providers/derive.ts +21 -0
- package/src/providers/devin-cli-authmode-migration.ts +1 -1
- package/src/providers/devin-provider-merge-migration.ts +1 -1
- package/src/providers/github-copilot-auto.ts +169 -0
- package/src/providers/model-presets.ts +1 -1
- package/src/providers/muse-subscription-usage.ts +1 -1
- package/src/providers/quota/vendor-probes-key.ts +127 -17
- package/src/providers/quota.ts +1 -1
- package/src/providers/registry/entries-core.ts +6 -6
- package/src/providers/registry/entries-extended.ts +4 -0
- package/src/providers/registry/model-seeds.ts +3 -3
- package/src/providers/xai-fast-model.ts +1 -1
- package/src/responses/reasoning-envelope.ts +29 -1
- package/src/responses/reasoning-replay-cache.ts +17 -1
- package/src/responses/spill-store.ts +15 -2
- package/src/responses/state/metrics.ts +8 -0
- package/src/responses/state/spill-failure.ts +12 -0
- package/src/responses/state/spill-queue.ts +12 -5
- package/src/responses/state.ts +18 -10
- package/src/responses/task-input.ts +17 -0
- package/src/server/background-lifecycle.ts +27 -0
- package/src/server/chat-completions.ts +1 -0
- package/src/server/chat-native.ts +28 -12
- package/src/server/claude-messages.ts +43 -6
- package/src/server/inbound-body-admission.ts +25 -7
- package/src/server/index/claude-intercept-lifecycle.ts +3 -1
- package/src/server/index/link-listener.ts +30 -17
- package/src/server/index/serve-options.ts +18 -4
- package/src/server/index/spend-ledger-lifecycle.ts +2 -1
- package/src/server/index/startup-warnings.ts +19 -0
- package/src/server/index.ts +2 -1
- package/src/server/lifecycle.ts +31 -4
- package/src/server/management/agent-settings-routes.ts +93 -32
- package/src/server/management/codex-agent-role-routes.ts +4 -2
- package/src/server/management/codex-prompt-routes.ts +1 -0
- package/src/server/management/codex-role-auto-assign.ts +13 -2
- package/src/server/management/config-routes.ts +59 -8
- package/src/server/management/context.ts +2 -2
- package/src/server/management/grok-coupon-routes.ts +277 -52
- package/src/server/management/link-routes.ts +42 -20
- package/src/server/management/route-registry.ts +2 -2
- package/src/server/management/shared.ts +3 -3
- package/src/server/messages-native-oauth.ts +9 -8
- package/src/server/messages-native.ts +49 -13
- package/src/server/messages-response-headers.ts +14 -0
- package/src/server/relay-eager.ts +2 -0
- package/src/server/relay.ts +18 -7
- package/src/server/request-log-conversation.ts +48 -5
- package/src/server/request-log-failure-attribution.ts +3 -0
- package/src/server/request-log-terminal-status.ts +186 -0
- package/src/server/request-log.ts +15 -89
- package/src/server/responses/adapter-continuation.ts +11 -3
- package/src/server/responses/adapter-dispatch.ts +99 -19
- package/src/server/responses/codex-ws-exchange.ts +17 -2
- package/src/server/responses/codex-ws-pool.ts +100 -13
- package/src/server/responses/codex-ws-wire.ts +9 -0
- package/src/server/responses/combo-requested-effort.ts +3 -1
- package/src/server/responses/compact.ts +3 -2
- package/src/server/responses/core-codex-account.ts +22 -1
- package/src/server/responses/core-combo-native.ts +20 -3
- package/src/server/responses/core-combo.ts +22 -14
- package/src/server/responses/core-lifetime.ts +3 -36
- package/src/server/responses/core-opaque-recovery.ts +5 -5
- package/src/server/responses/core-options.ts +5 -0
- package/src/server/responses/core-replay.ts +73 -0
- package/src/server/responses/core.ts +9 -9
- package/src/server/responses/empty-completion-guard.ts +18 -2
- package/src/server/responses/fetch-helpers.ts +26 -3
- package/src/server/responses/jev-model-invoke.ts +1 -1
- package/src/server/responses/passthrough-delivery.ts +24 -2
- package/src/server/responses/passthrough-dispatch.ts +134 -62
- package/src/server/responses/passthrough-execution.ts +3 -3
- package/src/server/responses/request-prepare.ts +4 -0
- package/src/server/responses/request-send-budget.ts +112 -49
- package/src/server/responses/request-transport.ts +125 -5
- package/src/server/responses/response-effects.ts +1 -0
- package/src/server/responses/run-turn-execution.ts +15 -5
- package/src/server/responses/sidecar-execution.ts +30 -16
- package/src/server/responses/sidecar-send-budget.ts +58 -0
- package/src/server/responses/spend-storage-error.ts +33 -0
- package/src/server/responses/ws-upstream.ts +19 -6
- package/src/server/startup-health-cache.ts +2 -1
- package/src/server/workflow-refusal.ts +3 -0
- package/src/service/cli.ts +20 -4
- package/src/service/desktop-command-guard.ts +32 -0
- package/src/service/desktop-startup.ts +66 -40
- package/src/service/desktop-supervision.d.mts +26 -0
- package/src/service/desktop-supervision.mjs +140 -0
- package/src/service/managing-cli.ts +1 -0
- package/src/service/orchestration.ts +46 -20
- package/src/service/repair.ts +25 -9
- package/src/service/state.ts +4 -3
- package/src/service/systemd.ts +17 -8
- package/src/service/windows-ops.ts +61 -22
- package/src/storage/policy-job.ts +5 -1
- package/src/storage/policy-worker.ts +3 -1
- package/src/storage/policy.ts +9 -0
- package/src/types/config.ts +21 -0
- package/src/types/request.ts +10 -0
- package/src/types/wire.ts +1 -0
- package/src/update/index.ts +57 -9
- package/src/update/restart-ownership.ts +10 -3
- package/src/update/runtime-ownership.d.mts +6 -0
- package/src/update/runtime-ownership.mjs +23 -7
- package/src/usage/expected-prices.ts +1 -1
- package/src/web-search/loop.ts +5 -2
- package/gui/dist/assets/App-7yhoys8a.js +0 -54
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CYBER_POLICY_ERROR_CODE,
|
|
3
|
+
httpStatusFromTerminalError,
|
|
4
|
+
isCyberPolicyCode,
|
|
5
|
+
isCyberPolicyMessage,
|
|
6
|
+
isRateLimitOrQuotaFailureMessage,
|
|
7
|
+
recognizedUpstreamError,
|
|
8
|
+
} from "../lib/errors";
|
|
9
|
+
import { redactSecretString } from "../lib/redact";
|
|
10
|
+
|
|
11
|
+
/** First bounded upstream diagnostics shared by request contexts and final rows. */
|
|
12
|
+
export interface UpstreamErrorDiagnostics {
|
|
13
|
+
upstreamErrorCode?: string;
|
|
14
|
+
upstreamErrorType?: string;
|
|
15
|
+
upstreamRequestId?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface TerminalStatusContext extends UpstreamErrorDiagnostics {
|
|
19
|
+
terminalHttpStatus?: number;
|
|
20
|
+
terminalErrorCode?: typeof CYBER_POLICY_ERROR_CODE;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface UpstreamErrorPayload {
|
|
24
|
+
type?: unknown;
|
|
25
|
+
code?: unknown;
|
|
26
|
+
message?: unknown;
|
|
27
|
+
error?: { type?: unknown; code?: unknown; message?: unknown };
|
|
28
|
+
last_error?: { type?: unknown; code?: unknown; message?: unknown };
|
|
29
|
+
response?: {
|
|
30
|
+
error?: { type?: unknown; code?: unknown; message?: unknown };
|
|
31
|
+
incomplete_details?: { code?: unknown; message?: unknown; reason?: unknown };
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Bare error evidence is provisional: only a genuine terminal owns account-health status.
|
|
36
|
+
const provisionalStatuses = new WeakMap<TerminalStatusContext, number>();
|
|
37
|
+
const genuineTerminals = new WeakSet<TerminalStatusContext>();
|
|
38
|
+
|
|
39
|
+
const UPSTREAM_DIAGNOSTIC_TOKEN = /^[A-Za-z0-9_.:-]{1,128}$/;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Upstream diagnostics are provider-controlled. The token alphabet alone also admits
|
|
43
|
+
* credential formats (an echoed `sk-...` key fits it), so a value the shared redactor
|
|
44
|
+
* would change is dropped rather than recorded, warned, or projected into log rows.
|
|
45
|
+
*/
|
|
46
|
+
function isSafeDiagnosticToken(value: unknown): value is string {
|
|
47
|
+
return typeof value === "string" && UPSTREAM_DIAGNOSTIC_TOKEN.test(value) && redactSecretString(value) === value;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Error classes published by the supported provider APIs (OpenAI/Responses, Anthropic, Gemini).
|
|
52
|
+
* The type is diagnostic only, so the vocabulary is closed: a gateway can echo anything in
|
|
53
|
+
* `error.type`, including a configured credential whose format no redactor recognizes, and a
|
|
54
|
+
* value outside this set is simply not recorded, which is what 2.82.0 did for every type.
|
|
55
|
+
*/
|
|
56
|
+
const KNOWN_UPSTREAM_ERROR_TYPES: ReadonlySet<string> = new Set([
|
|
57
|
+
"api_error", "authentication_error", "billing_error", "insufficient_quota", "invalid_request_error",
|
|
58
|
+
"not_found_error", "overloaded_error", "permission_error", "rate_limit_error", "request_too_large",
|
|
59
|
+
"requests", "server_error", "timeout_error", "tokens",
|
|
60
|
+
"INVALID_ARGUMENT", "FAILED_PRECONDITION", "PERMISSION_DENIED", "UNAUTHENTICATED", "NOT_FOUND",
|
|
61
|
+
"RESOURCE_EXHAUSTED", "INTERNAL", "UNAVAILABLE", "DEADLINE_EXCEEDED",
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
/** Bare top-level error codes worth keeping: the classes recognizedUpstreamError maps, plus server_error. */
|
|
65
|
+
const KNOWN_BARE_ERROR_CODES: ReadonlySet<string> = new Set([
|
|
66
|
+
"rate_limit_exceeded", "rate_limit_error", "server_is_overloaded", "overloaded_error", "server_error",
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
function noteBoundedDiagnostic(
|
|
70
|
+
logCtx: TerminalStatusContext,
|
|
71
|
+
field: "upstreamErrorCode" | "upstreamErrorType" | "upstreamRequestId",
|
|
72
|
+
value: unknown,
|
|
73
|
+
): void {
|
|
74
|
+
if (logCtx[field] !== undefined) return;
|
|
75
|
+
if (!isSafeDiagnosticToken(value)) return;
|
|
76
|
+
if (field === "upstreamErrorType" && !KNOWN_UPSTREAM_ERROR_TYPES.has(value)) return;
|
|
77
|
+
logCtx[field] = value;
|
|
78
|
+
const status = logCtx.terminalHttpStatus;
|
|
79
|
+
if (status === undefined || status >= 500) console.warn(`[opencodex] upstream failure${status ? ` status=${status}` : ""}${logCtx.upstreamErrorCode ? ` code=${logCtx.upstreamErrorCode}` : ""}${logCtx.upstreamRequestId ? ` request_id=${logCtx.upstreamRequestId}` : ""}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function noteUpstreamRequestId(logCtx: TerminalStatusContext, headers: Headers): void {
|
|
83
|
+
noteBoundedDiagnostic(logCtx, "upstreamRequestId", headers.get("openai-request-id") ?? headers.get("x-request-id"));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function captureUpstreamTerminalDiagnostics(logCtx: TerminalStatusContext, json: UpstreamErrorPayload): void {
|
|
87
|
+
captureTerminalHttpStatus(logCtx, json);
|
|
88
|
+
noteBoundedDiagnostic(logCtx, "upstreamErrorCode",
|
|
89
|
+
json.error?.code ?? json.last_error?.code ?? json.response?.error?.code
|
|
90
|
+
// A bare top-level code (new in 2.83.0) has no envelope contract, so only a known class is kept.
|
|
91
|
+
?? (typeof json.code === "string" && KNOWN_BARE_ERROR_CODES.has(json.code) ? json.code : undefined));
|
|
92
|
+
noteBoundedDiagnostic(logCtx, "upstreamErrorType",
|
|
93
|
+
json.error?.type ?? json.last_error?.type ?? json.response?.error?.type);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function upstreamDiagnosticLogFields(logCtx: UpstreamErrorDiagnostics): UpstreamErrorDiagnostics {
|
|
97
|
+
return {
|
|
98
|
+
...(logCtx.upstreamErrorCode ? { upstreamErrorCode: logCtx.upstreamErrorCode } : {}),
|
|
99
|
+
...(logCtx.upstreamErrorType ? { upstreamErrorType: logCtx.upstreamErrorType } : {}),
|
|
100
|
+
...(logCtx.upstreamRequestId ? { upstreamRequestId: logCtx.upstreamRequestId } : {}),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function captureTerminalHttpStatus(
|
|
105
|
+
logCtx: TerminalStatusContext,
|
|
106
|
+
json: UpstreamErrorPayload,
|
|
107
|
+
): void {
|
|
108
|
+
const type = json.type;
|
|
109
|
+
const terminal = type === "response.failed" || type === "response.completed" || type === "response.incomplete";
|
|
110
|
+
if (!terminal && type !== "error") return;
|
|
111
|
+
if (terminal && provisionalStatuses.has(logCtx)) {
|
|
112
|
+
const provisional = provisionalStatuses.get(logCtx);
|
|
113
|
+
provisionalStatuses.delete(logCtx);
|
|
114
|
+
if (logCtx.terminalHttpStatus === provisional) {
|
|
115
|
+
delete logCtx.terminalHttpStatus;
|
|
116
|
+
delete logCtx.terminalErrorCode;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (logCtx.terminalHttpStatus !== undefined || (type === "error" && genuineTerminals.has(logCtx))) return;
|
|
120
|
+
if (terminal) genuineTerminals.add(logCtx);
|
|
121
|
+
const noteStatus = (status: number): void => {
|
|
122
|
+
logCtx.terminalHttpStatus = status;
|
|
123
|
+
if (type === "error") provisionalStatuses.set(logCtx, status);
|
|
124
|
+
};
|
|
125
|
+
// A completed terminal only retires provisional bare-error evidence; it records no status.
|
|
126
|
+
if (type === "response.completed") return;
|
|
127
|
+
const responseError = json.response?.error;
|
|
128
|
+
const responseDetails = json.response?.incomplete_details;
|
|
129
|
+
const candidates: Array<{ type?: unknown; code?: unknown; message?: unknown } | undefined> = [
|
|
130
|
+
json.error, json.last_error, responseError, responseDetails, json,
|
|
131
|
+
];
|
|
132
|
+
const policy = candidates.some(candidate => (
|
|
133
|
+
candidate?.code === null || typeof candidate?.code === "string"
|
|
134
|
+
) && isCyberPolicyCode(candidate.code as string | null | undefined))
|
|
135
|
+
|| candidates.some(candidate => (
|
|
136
|
+
typeof candidate?.message === "string"
|
|
137
|
+
&& candidate.message.trim().length > 0
|
|
138
|
+
&& isCyberPolicyMessage(candidate.message)
|
|
139
|
+
));
|
|
140
|
+
if (policy) {
|
|
141
|
+
logCtx.terminalErrorCode = CYBER_POLICY_ERROR_CODE;
|
|
142
|
+
noteStatus(400);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// A quota terminal can carry only a structured reason, without an error message.
|
|
146
|
+
// Keep this separate from normal output limits and from the policy precedence above.
|
|
147
|
+
const quotaTag = (value: unknown): boolean => value === "usage_limit_reached"
|
|
148
|
+
|| value === "rate_limit_exceeded" || value === "insufficient_quota";
|
|
149
|
+
const structuredRefusal = candidates.some(candidate => [400, 401, 403, 499].includes(
|
|
150
|
+
httpStatusFromTerminalError({
|
|
151
|
+
type: typeof candidate?.type === "string" ? candidate.type : undefined,
|
|
152
|
+
code: typeof candidate?.code === "string" ? candidate.code : undefined,
|
|
153
|
+
}),
|
|
154
|
+
));
|
|
155
|
+
const ordinaryIncompleteReason = typeof responseDetails?.reason === "string"
|
|
156
|
+
&& ["max_output_tokens", "content_filter", "steered", "upstream_stall_timeout", "adapter_eof"].includes(responseDetails.reason);
|
|
157
|
+
if (type === "response.incomplete" && !structuredRefusal && (quotaTag(responseDetails?.reason) || candidates.some(candidate =>
|
|
158
|
+
quotaTag(candidate?.code)
|
|
159
|
+
|| quotaTag(candidate?.type) || candidate?.type === "rate_limit_error"
|
|
160
|
+
|| (!ordinaryIncompleteReason && typeof candidate?.message === "string" && isRateLimitOrQuotaFailureMessage(candidate.message))
|
|
161
|
+
))) {
|
|
162
|
+
// The shared quota classifier also accepts a numeric HTTP status as its message.
|
|
163
|
+
// Preserve explicit payment-required evidence rather than relabeling it as 429.
|
|
164
|
+
noteStatus(candidates.some(candidate => typeof candidate?.message === "string"
|
|
165
|
+
&& Number(candidate.message.trim()) === 402) ? 402 : 429);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (type === "error") {
|
|
169
|
+
const structured = recognizedUpstreamError({
|
|
170
|
+
code: candidates.find(candidate => typeof candidate?.code === "string")?.code,
|
|
171
|
+
type: candidates.slice(0, -1).find(candidate => typeof candidate?.type === "string")?.type,
|
|
172
|
+
});
|
|
173
|
+
// Unrecognized bare errors leave the status to explicit event fields (combo preflight).
|
|
174
|
+
if (structured) noteStatus(structured.httpStatus);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (type !== "response.failed" || !responseError || typeof responseError !== "object") return;
|
|
178
|
+
const responseCode = responseError.code === null || typeof responseError.code === "string"
|
|
179
|
+
? responseError.code
|
|
180
|
+
: undefined;
|
|
181
|
+
noteStatus(httpStatusFromTerminalError({
|
|
182
|
+
type: typeof responseError.type === "string" ? responseError.type : undefined,
|
|
183
|
+
code: responseCode,
|
|
184
|
+
message: typeof responseError.message === "string" ? responseError.message : undefined,
|
|
185
|
+
}));
|
|
186
|
+
}
|
|
@@ -4,6 +4,8 @@ import { stampApiKeyAccountLabel, usesApiKeyAccount } from "../providers/label";
|
|
|
4
4
|
import { KEY_ACCOUNT_LOG_LABEL_RE } from "../codex/account-label";
|
|
5
5
|
import { attemptAccountChanged, sealRequestAttemptIdentity } from "./request-log-account-rotation";
|
|
6
6
|
export { sealRequestAttemptIdentity };
|
|
7
|
+
import { captureUpstreamTerminalDiagnostics, upstreamDiagnosticLogFields, type UpstreamErrorDiagnostics, type UpstreamErrorPayload } from "./request-log-terminal-status";
|
|
8
|
+
export { noteUpstreamRequestId } from "./request-log-terminal-status";
|
|
7
9
|
import { readBoundedResponseBody } from "../lib/bounded-body";
|
|
8
10
|
import type { ResponsesTerminalStatus } from "../bridge";
|
|
9
11
|
import {
|
|
@@ -12,8 +14,6 @@ import {
|
|
|
12
14
|
httpStatusFromTerminalError as httpStatusFromClassifiedTerminalError,
|
|
13
15
|
isClientClosedMessage,
|
|
14
16
|
isCyberPolicyCode,
|
|
15
|
-
isCyberPolicyMessage,
|
|
16
|
-
isRateLimitOrQuotaFailureMessage,
|
|
17
17
|
isUpstreamResetReplayRefusedMessage,
|
|
18
18
|
upstreamErrorMessageFromPayload,
|
|
19
19
|
} from "../lib/errors";
|
|
@@ -104,7 +104,7 @@ function cacheDiagnosticHooks(): CacheDiagnosticHooks | undefined {
|
|
|
104
104
|
return (globalThis as Record<symbol, CacheDiagnosticHooks | undefined>)[CACHE_DIAGNOSTIC_HOOK];
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
export interface RequestLogContext {
|
|
107
|
+
export interface RequestLogContext extends UpstreamErrorDiagnostics {
|
|
108
108
|
model: string;
|
|
109
109
|
provider: string;
|
|
110
110
|
/** Optional process-lifetime aggregate sink, injected by the server composition owner. */
|
|
@@ -254,6 +254,8 @@ export interface RequestLogContext {
|
|
|
254
254
|
conversationStateScrub?: "account-change";
|
|
255
255
|
transportPhase?: "pre_headers" | "mid_stream" | "terminal_sse";
|
|
256
256
|
terminalSource?: "upstream" | "synthetic";
|
|
257
|
+
/** Closed failure evidence supplied by the transport, never inferred from provider text. */
|
|
258
|
+
causeHint?: RequestFailureCause;
|
|
257
259
|
/** Bounded route-decision trace (RI-01); never contains secrets. */
|
|
258
260
|
routeDecision?: RouteDecisionTraceV1;
|
|
259
261
|
/** Full eligible provider/model membership from a policy route; never logged. */
|
|
@@ -282,7 +284,7 @@ export function rebindCacheDiagnosticBody(
|
|
|
282
284
|
cacheDiagnosticHooks()?.rebind(body, draft);
|
|
283
285
|
}
|
|
284
286
|
|
|
285
|
-
export interface RequestLogEntry {
|
|
287
|
+
export interface RequestLogEntry extends UpstreamErrorDiagnostics {
|
|
286
288
|
requestId: string;
|
|
287
289
|
/** The logical request this row belongs to (#4546); absent on rows written without a budget. */
|
|
288
290
|
logicalRequestId?: string;
|
|
@@ -840,6 +842,7 @@ export function requestLogErrorCode(
|
|
|
840
842
|
status: number,
|
|
841
843
|
upstreamError?: string,
|
|
842
844
|
terminalErrorCode?: string,
|
|
845
|
+
replayRefusal = false,
|
|
843
846
|
): string | undefined {
|
|
844
847
|
if (status >= 200 && status < 400) return undefined;
|
|
845
848
|
// A structured terminal code is authoritative even when the provider message is localized,
|
|
@@ -868,10 +871,8 @@ export function requestLogErrorCode(
|
|
|
868
871
|
return "permission_denied";
|
|
869
872
|
}
|
|
870
873
|
if (status === 429) {
|
|
871
|
-
//
|
|
872
|
-
|
|
873
|
-
// distinguishes a proxy refusal from provider throttling.
|
|
874
|
-
if (upstreamError?.trim() && isUpstreamResetReplayRefusedMessage(upstreamError)) {
|
|
874
|
+
// Prefer transport provenance; retain the legacy message fallback for older callers.
|
|
875
|
+
if (replayRefusal || (upstreamError?.trim() && isUpstreamResetReplayRefusedMessage(upstreamError))) {
|
|
875
876
|
return UPSTREAM_RESET_REPLAY_REFUSED_CODE;
|
|
876
877
|
}
|
|
877
878
|
return "rate_limit_exceeded";
|
|
@@ -1112,16 +1113,8 @@ function captureUpstreamErrorParsed(
|
|
|
1112
1113
|
parsed: unknown | undefined,
|
|
1113
1114
|
): void {
|
|
1114
1115
|
if (parsed !== undefined && parsed !== null) {
|
|
1115
|
-
const json = parsed as
|
|
1116
|
-
|
|
1117
|
-
error?: { message?: unknown };
|
|
1118
|
-
last_error?: { message?: unknown };
|
|
1119
|
-
response?: {
|
|
1120
|
-
error?: { type?: unknown; code?: unknown; message?: unknown };
|
|
1121
|
-
incomplete_details?: { reason?: unknown; message?: unknown };
|
|
1122
|
-
};
|
|
1123
|
-
};
|
|
1124
|
-
captureTerminalHttpStatus(logCtx, json);
|
|
1116
|
+
const json = parsed as UpstreamErrorPayload;
|
|
1117
|
+
captureUpstreamTerminalDiagnostics(logCtx, json);
|
|
1125
1118
|
const reason = json?.response?.incomplete_details?.reason;
|
|
1126
1119
|
if (json.type === "response.incomplete"
|
|
1127
1120
|
&& logCtx.terminalIncompleteReason === undefined
|
|
@@ -1165,75 +1158,6 @@ function incompleteReasonLabel(reason: string): string {
|
|
|
1165
1158
|
}
|
|
1166
1159
|
}
|
|
1167
1160
|
|
|
1168
|
-
function captureTerminalHttpStatus(
|
|
1169
|
-
logCtx: RequestLogContext,
|
|
1170
|
-
json: {
|
|
1171
|
-
type?: unknown;
|
|
1172
|
-
code?: unknown;
|
|
1173
|
-
message?: unknown;
|
|
1174
|
-
error?: { type?: unknown; code?: unknown; message?: unknown };
|
|
1175
|
-
last_error?: { type?: unknown; code?: unknown; message?: unknown };
|
|
1176
|
-
response?: {
|
|
1177
|
-
error?: { type?: unknown; code?: unknown; message?: unknown };
|
|
1178
|
-
incomplete_details?: { code?: unknown; message?: unknown; reason?: unknown };
|
|
1179
|
-
};
|
|
1180
|
-
},
|
|
1181
|
-
): void {
|
|
1182
|
-
if (logCtx.terminalHttpStatus !== undefined) return;
|
|
1183
|
-
const type = json.type;
|
|
1184
|
-
if (type !== "response.failed" && type !== "response.incomplete" && type !== "error") return;
|
|
1185
|
-
const responseError = json.response?.error;
|
|
1186
|
-
const responseDetails = json.response?.incomplete_details;
|
|
1187
|
-
const candidates: Array<{ type?: unknown; code?: unknown; message?: unknown } | undefined> = [
|
|
1188
|
-
json.error, json.last_error, responseError, responseDetails, json,
|
|
1189
|
-
];
|
|
1190
|
-
const policy = candidates.some(candidate => (
|
|
1191
|
-
candidate?.code === null || typeof candidate?.code === "string"
|
|
1192
|
-
) && isCyberPolicyCode(candidate.code as string | null | undefined))
|
|
1193
|
-
|| candidates.some(candidate => (
|
|
1194
|
-
typeof candidate?.message === "string"
|
|
1195
|
-
&& candidate.message.trim().length > 0
|
|
1196
|
-
&& isCyberPolicyMessage(candidate.message)
|
|
1197
|
-
));
|
|
1198
|
-
if (policy) {
|
|
1199
|
-
logCtx.terminalErrorCode = CYBER_POLICY_ERROR_CODE;
|
|
1200
|
-
logCtx.terminalHttpStatus = 400;
|
|
1201
|
-
return;
|
|
1202
|
-
}
|
|
1203
|
-
// A quota terminal can carry only a structured reason, without an error message.
|
|
1204
|
-
// Keep this separate from normal output limits and from the policy precedence above.
|
|
1205
|
-
const quotaTag = (value: unknown): boolean => value === "usage_limit_reached"
|
|
1206
|
-
|| value === "rate_limit_exceeded" || value === "insufficient_quota";
|
|
1207
|
-
const structuredRefusal = candidates.some(candidate => [400, 401, 403, 499].includes(
|
|
1208
|
-
httpStatusFromTerminalError({
|
|
1209
|
-
type: typeof candidate?.type === "string" ? candidate.type : undefined,
|
|
1210
|
-
code: typeof candidate?.code === "string" ? candidate.code : undefined,
|
|
1211
|
-
}),
|
|
1212
|
-
));
|
|
1213
|
-
const ordinaryIncompleteReason = typeof responseDetails?.reason === "string"
|
|
1214
|
-
&& ["max_output_tokens", "content_filter", "steered", "upstream_stall_timeout", "adapter_eof"].includes(responseDetails.reason);
|
|
1215
|
-
if (type === "response.incomplete" && !structuredRefusal && (quotaTag(responseDetails?.reason) || candidates.some(candidate =>
|
|
1216
|
-
quotaTag(candidate?.code)
|
|
1217
|
-
|| quotaTag(candidate?.type) || candidate?.type === "rate_limit_error"
|
|
1218
|
-
|| (!ordinaryIncompleteReason && typeof candidate?.message === "string" && isRateLimitOrQuotaFailureMessage(candidate.message))
|
|
1219
|
-
))) {
|
|
1220
|
-
// The shared quota classifier also accepts a numeric HTTP status as its message.
|
|
1221
|
-
// Preserve explicit payment-required evidence rather than relabeling it as 429.
|
|
1222
|
-
logCtx.terminalHttpStatus = candidates.some(candidate => typeof candidate?.message === "string"
|
|
1223
|
-
&& Number(candidate.message.trim()) === 402) ? 402 : 429;
|
|
1224
|
-
return;
|
|
1225
|
-
}
|
|
1226
|
-
if (type !== "response.failed" || !responseError || typeof responseError !== "object") return;
|
|
1227
|
-
const responseCode = responseError.code === null || typeof responseError.code === "string"
|
|
1228
|
-
? responseError.code
|
|
1229
|
-
: undefined;
|
|
1230
|
-
logCtx.terminalHttpStatus = httpStatusFromTerminalError({
|
|
1231
|
-
type: typeof responseError.type === "string" ? responseError.type : undefined,
|
|
1232
|
-
code: responseCode,
|
|
1233
|
-
message: typeof responseError.message === "string" ? responseError.message : undefined,
|
|
1234
|
-
});
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
1161
|
/** Map a terminal Responses error object to the HTTP status we record in /api/logs. */
|
|
1238
1162
|
export function httpStatusFromTerminalError(error: {
|
|
1239
1163
|
type?: string;
|
|
@@ -1440,12 +1364,12 @@ export function addFinalRequestLog(
|
|
|
1440
1364
|
const effectiveStatus = status >= 500 && logCtx.upstreamError && isClientClosedMessage(logCtx.upstreamError)
|
|
1441
1365
|
? 499
|
|
1442
1366
|
: status;
|
|
1443
|
-
//
|
|
1444
|
-
// status-plus-upstream-message classification can reconstruct.
|
|
1367
|
+
// Locally assigned codes name proxy refusals that status and upstream text cannot reconstruct.
|
|
1445
1368
|
const errorCode = logCtx.errorCode ?? requestLogErrorCode(
|
|
1446
1369
|
effectiveStatus,
|
|
1447
1370
|
logCtx.upstreamError,
|
|
1448
1371
|
logCtx.terminalErrorCode,
|
|
1372
|
+
logCtx.causeHint === "transport-ambiguous" && logCtx.terminalSource === "synthetic",
|
|
1449
1373
|
);
|
|
1450
1374
|
// A response.failed whose classified status is 499 is still a client cancel, not an upstream
|
|
1451
1375
|
// terminal failure — keep /api/logs closeReason aligned with that.
|
|
@@ -1475,6 +1399,7 @@ export function addFinalRequestLog(
|
|
|
1475
1399
|
...(logCtx.transportPhase ? { transportPhase: logCtx.transportPhase } : {}),
|
|
1476
1400
|
...(logCtx.terminalSource ? { terminalSource: logCtx.terminalSource } : {}),
|
|
1477
1401
|
outputObserved: logCtx.firstOutputMs !== undefined,
|
|
1402
|
+
...(logCtx.causeHint ? { causeHint: logCtx.causeHint } : {}),
|
|
1478
1403
|
locallyAnswered: logCtx.localTerminalReason !== undefined,
|
|
1479
1404
|
...(logCtx.activeAttempt ? { attempt: logCtx.activeAttempt } : {}),
|
|
1480
1405
|
});
|
|
@@ -1593,6 +1518,7 @@ export function addFinalRequestLog(
|
|
|
1593
1518
|
...(meta?.terminalStatus ? { terminalStatus: meta.terminalStatus } : {}),
|
|
1594
1519
|
...(closeReason ? { closeReason } : {}),
|
|
1595
1520
|
...(logCtx.upstreamError ? { upstreamError: logCtx.upstreamError } : {}),
|
|
1521
|
+
...upstreamDiagnosticLogFields(logCtx),
|
|
1596
1522
|
usageStatus,
|
|
1597
1523
|
...(loggedUsage ? { usage: loggedUsage } : {}),
|
|
1598
1524
|
...(totalTokens !== undefined ? { totalTokens } : {}),
|
|
@@ -83,6 +83,7 @@ export function createAdapterContinuations(
|
|
|
83
83
|
| "oauthDispatch"
|
|
84
84
|
| "invalidateSameTargetRequest"
|
|
85
85
|
| "resolveSelectionAdapter"
|
|
86
|
+
| "resolveCopilotSelection"
|
|
86
87
|
| "anthropicInstance"
|
|
87
88
|
| "currentAnthropicRouteDecision"
|
|
88
89
|
| "anthropicRouteDecision"
|
|
@@ -156,7 +157,8 @@ export function createAdapterContinuations(
|
|
|
156
157
|
/**
|
|
157
158
|
* One bounded internal re-ask for Anthropic end_turn-without-tool-call turns. Replays the
|
|
158
159
|
* continuation on a 429 with the same-key retry budget (hoisted per request), then falls
|
|
159
|
-
* back to key/account failover
|
|
160
|
+
* back to key/account failover, rebuilding Copilot model, wire, and credential negotiation.
|
|
161
|
+
* A failure becomes an in-stream adapter error so the client
|
|
160
162
|
* never sees a second hidden HTTP response or an unbounded retry loop.
|
|
161
163
|
*/
|
|
162
164
|
const fetchTerminalGuardContinuation = async function* (
|
|
@@ -377,6 +379,12 @@ export function createAdapterContinuations(
|
|
|
377
379
|
if (rotated) {
|
|
378
380
|
try { void response.body?.cancel().catch(() => {}); } catch { /* already closed */ }
|
|
379
381
|
route.provider = rotated;
|
|
382
|
+
try { await transportState.resolveCopilotSelection(nextParsed); }
|
|
383
|
+
catch (error) {
|
|
384
|
+
yield { type: "error", message: `Provider continuation failed: ${redactSecretString(error instanceof Error ? error.message : String(error))}`,
|
|
385
|
+
...(options.abortSignal?.aborted || upstream.signal.aborted ? { status: 499 } : {}) };
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
380
388
|
invalidateSameTargetRequest();
|
|
381
389
|
transportState.activeAdapter = resolveSelectionAdapter(
|
|
382
390
|
resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire, route.staticPolicy),
|
|
@@ -401,7 +409,7 @@ export function createAdapterContinuations(
|
|
|
401
409
|
}
|
|
402
410
|
}
|
|
403
411
|
if (
|
|
404
|
-
(response.status === 429 || response.status === 403)
|
|
412
|
+
(response.status === 429 || response.status === 403 || response.status === 401)
|
|
405
413
|
&& anthropicInstance
|
|
406
414
|
&& transportState.anthropicPoolAccountId
|
|
407
415
|
&& !isNonReplayableResponse(response)
|
|
@@ -428,7 +436,7 @@ export function createAdapterContinuations(
|
|
|
428
436
|
);
|
|
429
437
|
sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, transportState.activeAdapter.name, logCtx.accountLogLabel);
|
|
430
438
|
recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, route.provider, transportState.activeAdapter.name);
|
|
431
|
-
nextContinuationRecoveryKind = "anthropic-oauth-429";
|
|
439
|
+
nextContinuationRecoveryKind = response.status === 401 ? "oauth-401" : "anthropic-oauth-429";
|
|
432
440
|
continue;
|
|
433
441
|
} catch {
|
|
434
442
|
// fall through to emit continuation error below
|