@yansigit/opencodex 2.33.1 → 2.35.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/README.md +3 -3
- package/gui/dist/assets/index-BjCaHxdz.js +112 -0
- package/gui/dist/assets/index-DLkXOXLC.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -3
- package/src/adapters/anthropic.ts +79 -2
- package/src/adapters/command-code.ts +40 -3
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/checkpoint-store.ts +15 -10
- package/src/adapters/cursor/discovery.ts +60 -2
- package/src/adapters/cursor/effort-map.ts +79 -1
- package/src/adapters/cursor/envelope-echo.ts +9 -9
- package/src/adapters/cursor/live-models.ts +7 -2
- package/src/adapters/cursor/live-transport.ts +14 -0
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-shell.ts +103 -6
- package/src/adapters/cursor/native-exec.ts +20 -0
- package/src/adapters/cursor/protobuf-request.ts +74 -39
- package/src/adapters/cursor/request-builder.ts +32 -10
- package/src/adapters/cursor/tool-definitions.ts +13 -10
- package/src/adapters/cursor/tool-result-normalize.ts +35 -3
- package/src/adapters/cursor/types.ts +16 -4
- package/src/adapters/cursor.ts +97 -70
- package/src/adapters/google-antigravity-replay.ts +105 -25
- package/src/adapters/google-antigravity-wire.ts +5 -0
- package/src/adapters/google-errors.ts +41 -12
- package/src/adapters/google-http.ts +12 -11
- package/src/adapters/google.ts +156 -65
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +15 -0
- package/src/adapters/kiro-tools.ts +43 -15
- package/src/adapters/kiro.ts +54 -9
- package/src/adapters/openai-chat.ts +286 -242
- package/src/adapters/openai-responses.ts +334 -24
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-catalog-nudge.ts +2 -2
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/bridge.ts +67 -26
- package/src/chat/inbound.ts +14 -1
- package/src/chat/outbound.ts +15 -7
- package/src/claude/agents-inject.ts +8 -1
- package/src/claude/outbound.ts +10 -8
- package/src/cli/account-api.ts +27 -7
- package/src/cli/account-extended.ts +10 -3
- package/src/cli/account.ts +29 -5
- package/src/cli/alias.ts +66 -0
- package/src/cli/claude.ts +26 -1
- package/src/cli/dispatch.ts +13 -1
- package/src/cli/help.ts +1 -0
- package/src/cli/index.ts +5 -0
- package/src/cli/init.ts +1 -0
- package/src/cli/models-runtime.ts +95 -0
- package/src/cli/models.ts +13 -7
- package/src/cli/provider-runtime.ts +16 -2
- package/src/cli/registry.ts +6 -1
- package/src/cli/telemetry-commands.ts +25 -0
- package/src/cli/v2.ts +34 -10
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +3 -2
- package/src/codex/app-server-processes.ts +80 -6
- package/src/codex/auth-api.ts +48 -8
- package/src/codex/auth-context.ts +21 -18
- package/src/codex/catalog/aggregation.ts +6 -0
- package/src/codex/catalog/model-metadata.ts +13 -1
- package/src/codex/catalog/native-models.ts +5 -2
- package/src/codex/catalog/parsing.ts +16 -0
- package/src/codex/catalog/provider-fetch.ts +20 -37
- package/src/codex/catalog/sync.ts +127 -2
- package/src/codex/catalog.ts +1 -1
- package/src/codex/codex-write-lock.ts +3 -1
- package/src/codex/convergence-types.ts +1 -1
- package/src/codex/convergence.ts +22 -2
- package/src/codex/desired-state.ts +2 -2
- package/src/codex/desktop-app-restart.ts +18 -5
- package/src/codex/inject-coordination.ts +83 -0
- package/src/codex/inject.ts +14 -1
- package/src/codex/log-guard/inspect.ts +22 -4
- package/src/codex/model-entitlements.ts +9 -2
- package/src/codex/prompt-layers.ts +371 -25
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota.ts +123 -18
- package/src/codex/routing.ts +9 -0
- package/src/codex/subagent-model-fallback.ts +198 -27
- package/src/codex/transition-state.ts +107 -8
- package/src/combos/types.ts +10 -0
- package/src/compatibility/openai-responses.ts +33 -1
- package/src/config/autonomous-remediation.ts +21 -0
- package/src/config/provider-validation.ts +14 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +191 -17
- package/src/generated/compatibility-version.json +229 -153
- package/src/images/loop.ts +5 -4
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +1 -1
- package/src/lab/fabric/producer-child.ts +1 -1
- package/src/lib/config-ownership.ts +20 -0
- package/src/lib/errors.ts +11 -2
- package/src/lib/package-tree-integrity.ts +101 -0
- package/src/oauth/aistudio-credentials.ts +65 -0
- package/src/oauth/aistudio-native-daemon.ts +54 -0
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/index.ts +57 -5
- package/src/oauth/key-providers.ts +10 -1
- package/src/oauth/kiro.ts +45 -0
- package/src/oauth/login-cli.ts +19 -20
- package/src/oauth/types.ts +15 -0
- package/src/providers/codex-capacity.ts +5 -2
- package/src/providers/command-code-efforts.ts +38 -6
- package/src/providers/context-cap.ts +4 -3
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +28 -0
- package/src/providers/fastwire.ts +7 -1
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/new-model-policy.ts +146 -0
- package/src/providers/provider-id-rewrite.ts +2 -1
- package/src/providers/quota.ts +73 -14
- package/src/providers/registry.ts +160 -68
- package/src/providers/slug-codec.ts +52 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/custom-tool-compat.ts +34 -10
- package/src/responses/parser.ts +4 -0
- package/src/responses/schema.ts +5 -1
- package/src/responses/thought-signature-replay.ts +17 -0
- package/src/router.ts +40 -2
- package/src/routing/analytics.ts +1 -0
- package/src/routing/quota.ts +10 -0
- package/src/server/auth-cors.ts +23 -0
- package/src/server/chat-completions.ts +24 -16
- package/src/server/chat-native-sse.ts +3 -3
- package/src/server/chat-native.ts +30 -11
- package/src/server/claude-messages.ts +1 -1
- package/src/server/effort-policy.ts +16 -0
- package/src/server/index.ts +152 -80
- package/src/server/lifecycle.ts +52 -1
- package/src/server/management/agent-settings-routes.ts +31 -15
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +2 -1
- package/src/server/management/config-routes.ts +27 -9
- package/src/server/management/context.ts +9 -0
- package/src/server/management/logs-usage-routes.ts +1 -1
- package/src/server/management/model-routes.ts +266 -0
- package/src/server/management/provider-routes.ts +137 -3
- package/src/server/management/routing-profile-routes.ts +2 -2
- package/src/server/management-api.ts +2 -0
- package/src/server/relay-eager.ts +147 -20
- package/src/server/relay.ts +251 -19
- package/src/server/request-log-conversation.ts +21 -0
- package/src/server/request-log.ts +46 -20
- package/src/server/responses/collaboration.ts +42 -5
- package/src/server/responses/combo-stream-preflight.ts +10 -3
- package/src/server/responses/core.ts +571 -137
- package/src/server/responses/empty-completion-guard.ts +35 -0
- package/src/server/responses/fetch-helpers.ts +14 -6
- package/src/server/responses/input-admission.ts +3 -1
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/responses-field-backfill.ts +105 -13
- package/src/server/responses/ws-upstream.ts +35 -5
- package/src/server/responses-custom-tool-repair.ts +52 -7
- package/src/server/responses-terminal-repair.ts +25 -4
- package/src/server/sse-frame-buffer.ts +31 -4
- package/src/server/ws-bridge.ts +15 -4
- package/src/telemetry/dispatcher.ts +44 -0
- package/src/telemetry/fingerprint.ts +24 -0
- package/src/telemetry/hook.ts +43 -0
- package/src/telemetry/ledger.ts +54 -0
- package/src/telemetry/types.ts +23 -0
- package/src/types/config.ts +66 -14
- package/src/types/provider.ts +77 -0
- package/src/types/request.ts +16 -10
- package/src/types/tools.ts +20 -25
- package/src/types.ts +1 -0
- package/src/usage/expected-prices.ts +43 -24
- package/src/usage/log.ts +17 -7
- package/src/web-search/exa-executor.ts +40 -9
- package/src/web-search/loop.ts +5 -4
- package/gui/dist/assets/index-CIDo4y4k.js +0 -102
- package/gui/dist/assets/index-DrSQdTRd.css +0 -1
- package/src/server/aistudio-ws-hub.ts +0 -295
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isCyberPolicyCode, isCyberPolicyMessage } from "../lib/errors";
|
|
2
|
+
|
|
1
3
|
export const MAX_CLIENT_SSE_FRAME_BYTES = 4 * 1024 * 1024;
|
|
2
4
|
|
|
3
5
|
const LF_LF = Uint8Array.of(10, 10);
|
|
@@ -106,10 +108,35 @@ function isResponsesTerminalFrame(block: Uint8Array): boolean {
|
|
|
106
108
|
const payload = data.join("\n");
|
|
107
109
|
if (payload === "[DONE]") return false;
|
|
108
110
|
try {
|
|
109
|
-
const parsed = JSON.parse(payload) as {
|
|
110
|
-
|
|
111
|
+
const parsed = JSON.parse(payload) as {
|
|
112
|
+
type?: unknown;
|
|
113
|
+
code?: unknown;
|
|
114
|
+
message?: unknown;
|
|
115
|
+
error?: unknown;
|
|
116
|
+
last_error?: unknown;
|
|
117
|
+
response?: { error?: unknown; incomplete_details?: unknown };
|
|
118
|
+
};
|
|
119
|
+
if (parsed.type === "response.completed"
|
|
111
120
|
|| parsed.type === "response.failed"
|
|
112
|
-
|| parsed.type === "response.incomplete";
|
|
121
|
+
|| parsed.type === "response.incomplete") return true;
|
|
122
|
+
if (parsed.type !== "error") return false;
|
|
123
|
+
// A top-level error is terminal for this boundary only when it carries the
|
|
124
|
+
// same high-confidence cyber-policy evidence used by relay.ts. Ordinary
|
|
125
|
+
// upstream errors remain transport failures and still become a bounded 502.
|
|
126
|
+
const candidates = [
|
|
127
|
+
parsed,
|
|
128
|
+
parsed.error,
|
|
129
|
+
parsed.last_error,
|
|
130
|
+
parsed.response?.error,
|
|
131
|
+
parsed.response?.incomplete_details,
|
|
132
|
+
];
|
|
133
|
+
for (const candidate of candidates) {
|
|
134
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) continue;
|
|
135
|
+
const record = candidate as { code?: unknown; message?: unknown };
|
|
136
|
+
if (isCyberPolicyCode(typeof record.code === "string" ? record.code : undefined)) return true;
|
|
137
|
+
if (typeof record.message === "string" && isCyberPolicyMessage(record.message)) return true;
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
113
140
|
} catch {
|
|
114
141
|
return false;
|
|
115
142
|
}
|
|
@@ -289,4 +316,4 @@ export function joinSseFrameBytes(parts: readonly Uint8Array[]): Uint8Array {
|
|
|
289
316
|
offset += part.byteLength;
|
|
290
317
|
}
|
|
291
318
|
return joined;
|
|
292
|
-
}
|
|
319
|
+
}
|
package/src/server/ws-bridge.ts
CHANGED
|
@@ -34,9 +34,10 @@ export interface WsData {
|
|
|
34
34
|
authContext?: CodexAuthContext; // last resolved account decision for observability/registry cleanup
|
|
35
35
|
cancel?: () => void; // cancels the in-flight stream reader/fetch
|
|
36
36
|
turnId?: number; // monotonically increasing per socket; prevents stale frames after replacement turns
|
|
37
|
+
/** Fixed-size logical session lane derived at the HTTP upgrade boundary. */
|
|
38
|
+
sessionLaneId?: string;
|
|
37
39
|
/** Discriminator: Responses reframing vs transparent live/realtime sideband relay. */
|
|
38
|
-
kind?: "responses" | "live-sideband"
|
|
39
|
-
aistudioSessionId?: string;
|
|
40
|
+
kind?: "responses" | "live-sideband";
|
|
40
41
|
liveUpstream?: WebSocket;
|
|
41
42
|
liveUpstreamUrl?: string;
|
|
42
43
|
liveUpstreamHeaders?: Record<string, string>;
|
|
@@ -61,10 +62,20 @@ export interface WsData {
|
|
|
61
62
|
* A test that only asserts "the socket opened" would still pass if the admission
|
|
62
63
|
* were dropped from the payload, so the payload itself is what gets asserted.
|
|
63
64
|
*/
|
|
64
|
-
export function buildResponsesWsData(
|
|
65
|
+
export function buildResponsesWsData(
|
|
66
|
+
headers: Headers,
|
|
67
|
+
admission: DataPlaneAdmission,
|
|
68
|
+
admissionLease?: AdmissionReservation<ServerWebSocket<WsData>>,
|
|
69
|
+
sessionLaneId?: string,
|
|
70
|
+
): WsData {
|
|
65
71
|
// Auth is handshake-time only on this path: the per-frame contexts have no
|
|
66
72
|
// request headers left to re-resolve from, so the decision rides along here.
|
|
67
|
-
return {
|
|
73
|
+
return {
|
|
74
|
+
headers,
|
|
75
|
+
admission,
|
|
76
|
+
...(admissionLease ? { admissionLease } : {}),
|
|
77
|
+
...(sessionLaneId ? { sessionLaneId } : {}),
|
|
78
|
+
};
|
|
68
79
|
}
|
|
69
80
|
|
|
70
81
|
export class WsSendDroppedError extends Error {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { LedgerRecord } from "./types";
|
|
2
|
+
|
|
3
|
+
export type GhCommandRunner = (args: string[]) => Promise<string>;
|
|
4
|
+
|
|
5
|
+
export interface AutonomousRemediationConfig {
|
|
6
|
+
autonomousRemediation?: { enabled?: boolean; instanceId?: string };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type DispatchResult =
|
|
10
|
+
| { status: "skipped"; reason: "unauthorized" | "invalid-record" }
|
|
11
|
+
| { status: "dispatched"; issueNumber?: number; issueUrl?: string; existing: boolean };
|
|
12
|
+
|
|
13
|
+
const defaultRunner: GhCommandRunner = async args => {
|
|
14
|
+
const proc = Bun.spawn(["gh", ...args], { stdout: "pipe", stderr: "pipe" });
|
|
15
|
+
return await new Response(proc.stdout).text();
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export async function dispatchAutonomousFix(
|
|
19
|
+
record: LedgerRecord,
|
|
20
|
+
config: AutonomousRemediationConfig,
|
|
21
|
+
run: GhCommandRunner = defaultRunner,
|
|
22
|
+
): Promise<DispatchResult> {
|
|
23
|
+
const authorization = config.autonomousRemediation;
|
|
24
|
+
const eventInstance = typeof record.details?.instanceId === "string" ? record.details.instanceId : undefined;
|
|
25
|
+
if (authorization?.enabled !== true || !authorization.instanceId || (eventInstance !== undefined && eventInstance !== authorization.instanceId)) {
|
|
26
|
+
return { status: "skipped", reason: "unauthorized" };
|
|
27
|
+
}
|
|
28
|
+
if (!record.fingerprint) return { status: "skipped", reason: "invalid-record" };
|
|
29
|
+
|
|
30
|
+
const search = await run(["issue", "list", "--repo", "yansigit/opencodex", "--state", "open", "--search", `fingerprint:${record.fingerprint}`]);
|
|
31
|
+
const existing = search.match(/(?:^|\D)(\d+)(?:\D|$)/);
|
|
32
|
+
if (existing) {
|
|
33
|
+
const issueNumber = Number(existing[1]);
|
|
34
|
+
return { status: "dispatched", issueNumber, existing: true };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const metadata = { fingerprint: record.fingerprint, instanceId: authorization.instanceId, firstSeen: record.firstSeen, lastSeen: record.lastSeen, count: record.count, ...(record.details ?? {}) };
|
|
38
|
+
const body = `<!-- opencodex-failure-telemetry ${JSON.stringify(metadata)} -->\n\nFailure fingerprint: ${record.fingerprint}\n\nThis issue was dispatched by the verified local telemetry instance.`;
|
|
39
|
+
const title = `Autonomous fix: ${String(record.details?.failureKind ?? "failure")} (${record.fingerprint})`;
|
|
40
|
+
const created = await run(["issue", "create", "--repo", "yansigit/opencodex", "--title", title, "--body", body, "--label", "agent:jules,autonomous-fix,instance:verified"]);
|
|
41
|
+
const url = created.match(/https?:\/\/\S+/)?.[0];
|
|
42
|
+
const issueNumber = url?.match(/\/issues\/(\d+)/)?.[1];
|
|
43
|
+
return { status: "dispatched", existing: false, ...(url ? { issueUrl: url } : {}), ...(issueNumber ? { issueNumber: Number(issueNumber) } : {}) };
|
|
44
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import type { FailureEvent, FailureFingerprint } from "./types";
|
|
3
|
+
|
|
4
|
+
const EPHEMERAL_KEYS = /^(timestamp|.*(?:request|session)[_-]?id|line|column|col)$/i;
|
|
5
|
+
const EPHEMERAL_TEXT = [
|
|
6
|
+
/\b(?:request|session)[_-]?id\s*[:=]\s*[^\s,;]+/gi,
|
|
7
|
+
/\b(?:timestamp|time)\s*[:=]\s*[^\s,;]+/gi,
|
|
8
|
+
/:\d+\s*:\s*\d+(?=\b|\D)/g,
|
|
9
|
+
/\b(?:line|col(?:umn)?)\s*[:=]?\s*\d+/gi,
|
|
10
|
+
/\b\d{10,13}\b/g,
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
function canonical(value: unknown): unknown {
|
|
14
|
+
if (typeof value === "string") return EPHEMERAL_TEXT.reduce((text, pattern) => text.replace(pattern, ""), value).replace(/\s+/g, " ").trim();
|
|
15
|
+
if (Array.isArray(value)) return value.map(canonical);
|
|
16
|
+
if (value && typeof value === "object") {
|
|
17
|
+
return Object.fromEntries(Object.entries(value).filter(([key]) => !EPHEMERAL_KEYS.test(key)).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, canonical(item)]));
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function computeFailureFingerprint(event: FailureEvent): FailureFingerprint {
|
|
23
|
+
return createHash("sha256").update(JSON.stringify(canonical(event))).digest("hex");
|
|
24
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { resolveAutonomousRemediationConfig, type AutonomousRemediationConfig } from "../config/autonomous-remediation";
|
|
2
|
+
import { TelemetryLedger } from "./ledger";
|
|
3
|
+
import type { LedgerRecord } from "./types";
|
|
4
|
+
|
|
5
|
+
export type RuntimeFailureCategory = "websocket_1006" | "tool_repetition_loop" | "upstream_wire_error";
|
|
6
|
+
|
|
7
|
+
export interface RuntimeFailureContext {
|
|
8
|
+
category?: RuntimeFailureCategory;
|
|
9
|
+
provider?: string;
|
|
10
|
+
model?: string;
|
|
11
|
+
requestId?: string;
|
|
12
|
+
sessionId?: string;
|
|
13
|
+
timestamp?: number;
|
|
14
|
+
config?: AutonomousRemediationConfig | unknown;
|
|
15
|
+
ledger?: TelemetryLedger;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let defaultLedger: TelemetryLedger | undefined;
|
|
19
|
+
|
|
20
|
+
function categoryFor(error: unknown, requested?: RuntimeFailureCategory): RuntimeFailureCategory {
|
|
21
|
+
if (requested) return requested;
|
|
22
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
23
|
+
if (/1006|abnormal closure|websocket/i.test(message)) return "websocket_1006";
|
|
24
|
+
if (/tool.{0,32}(repeat|loop)|(?:repeat|loop).{0,32}tool/i.test(message)) return "tool_repetition_loop";
|
|
25
|
+
return "upstream_wire_error";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function interceptRuntimeFailure(error: unknown, context: RuntimeFailureContext = {}): LedgerRecord | null {
|
|
29
|
+
const config = resolveAutonomousRemediationConfig(context.config);
|
|
30
|
+
if (!config.enabled) return null;
|
|
31
|
+
const ledger = context.ledger ?? (defaultLedger ??= new TelemetryLedger());
|
|
32
|
+
const category = categoryFor(error, context.category);
|
|
33
|
+
const signature = error instanceof Error ? error.message : String(error);
|
|
34
|
+
return ledger.recordFailure({
|
|
35
|
+
failureKind: category,
|
|
36
|
+
signature,
|
|
37
|
+
...(context.provider ? { provider: context.provider } : {}),
|
|
38
|
+
...(context.model ? { model: context.model } : {}),
|
|
39
|
+
...(context.requestId ? { requestId: context.requestId } : {}),
|
|
40
|
+
...(context.sessionId ? { sessionId: context.sessionId } : {}),
|
|
41
|
+
...(context.timestamp !== undefined ? { timestamp: context.timestamp } : {}),
|
|
42
|
+
}, config.rollingWindowMs);
|
|
43
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { mkdirSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { computeFailureFingerprint } from "./fingerprint";
|
|
6
|
+
import type { FailureEvent, FailureFingerprint, LedgerRecord, RemediationStatus } from "./types";
|
|
7
|
+
|
|
8
|
+
type Stored = LedgerRecord & { occurrences: number[] };
|
|
9
|
+
|
|
10
|
+
export class TelemetryLedger {
|
|
11
|
+
private readonly db: Database;
|
|
12
|
+
|
|
13
|
+
constructor(path = join(homedir(), ".opencodex", "telemetry-issues.sqlite")) {
|
|
14
|
+
if (path !== ":memory:") mkdirSync(dirname(path), { recursive: true });
|
|
15
|
+
this.db = new Database(path, { create: true });
|
|
16
|
+
this.db.run("CREATE TABLE IF NOT EXISTS failure_events (fingerprint TEXT PRIMARY KEY, first_seen INTEGER NOT NULL, last_seen INTEGER NOT NULL, count INTEGER NOT NULL, status TEXT NOT NULL, details TEXT, occurrences TEXT NOT NULL)");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
recordFailure(event: FailureEvent, windowMs: number): LedgerRecord {
|
|
20
|
+
const fingerprint = computeFailureFingerprint(event);
|
|
21
|
+
const timestamp = event.timestamp ?? Date.now();
|
|
22
|
+
const old = this.db.query("SELECT * FROM failure_events WHERE fingerprint = ?").get(fingerprint) as Stored | null;
|
|
23
|
+
const occurrences = [...(old ? JSON.parse((old as unknown as { occurrences: string }).occurrences) : []), timestamp].filter((seen) => seen >= timestamp - windowMs).sort((a, b) => a - b);
|
|
24
|
+
const record: LedgerRecord = { fingerprint, firstSeen: old?.firstSeen ?? timestamp, lastSeen: timestamp, count: occurrences.length, status: old?.status ?? "monitoring", ...(old?.details ? { details: JSON.parse(old.details as unknown as string) } : {}) };
|
|
25
|
+
this.db.query("INSERT INTO failure_events (fingerprint, first_seen, last_seen, count, status, details, occurrences) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(fingerprint) DO UPDATE SET first_seen=excluded.first_seen, last_seen=excluded.last_seen, count=excluded.count, status=excluded.status, details=excluded.details, occurrences=excluded.occurrences").run(fingerprint, record.firstSeen, timestamp, record.count, record.status, record.details ? JSON.stringify(record.details) : null, JSON.stringify(occurrences));
|
|
26
|
+
return record;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getRecord(fingerprint: FailureFingerprint): LedgerRecord | null {
|
|
30
|
+
const row = this.db.query("SELECT fingerprint, first_seen, last_seen, count, status, details FROM failure_events WHERE fingerprint = ?").get(fingerprint) as Record<string, unknown> | null;
|
|
31
|
+
if (!row) return null;
|
|
32
|
+
return { fingerprint: row.fingerprint as string, firstSeen: row.first_seen as number, lastSeen: row.last_seen as number, count: row.count as number, status: row.status as RemediationStatus, ...(row.details ? { details: JSON.parse(row.details as string) } : {}) };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
listRecords(): LedgerRecord[] {
|
|
36
|
+
return this.db.query("SELECT fingerprint, first_seen, last_seen, count, status, details FROM failure_events ORDER BY last_seen DESC").all().map(row => {
|
|
37
|
+
const value = row as Record<string, unknown>;
|
|
38
|
+
return { fingerprint: value.fingerprint as string, firstSeen: value.first_seen as number, lastSeen: value.last_seen as number, count: value.count as number, status: value.status as RemediationStatus, ...(value.details ? { details: JSON.parse(value.details as string) } : {}) };
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
updateStatus(fingerprint: FailureFingerprint, status: RemediationStatus, details?: Record<string, unknown>): void {
|
|
43
|
+
this.db.query("UPDATE failure_events SET status = ?, details = ? WHERE fingerprint = ?").run(status, details ? JSON.stringify(details) : null, fingerprint);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
shouldDispatch(fingerprint: FailureFingerprint, threshold: number, windowMs: number): boolean {
|
|
47
|
+
const row = this.db.query("SELECT status, last_seen, occurrences FROM failure_events WHERE fingerprint = ?").get(fingerprint) as { status: RemediationStatus; last_seen: number; occurrences: string } | null;
|
|
48
|
+
if (!row || row.status !== "monitoring") return false;
|
|
49
|
+
const count = (JSON.parse(row.occurrences) as number[]).filter((seen) => seen >= row.last_seen - windowMs).length;
|
|
50
|
+
return count >= threshold;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
close(): void { this.db.close(); }
|
|
54
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type RemediationStatus = "monitoring" | "dispatched" | "fixed" | "ignored";
|
|
2
|
+
|
|
3
|
+
export interface FailureEvent {
|
|
4
|
+
failureKind: string;
|
|
5
|
+
provider?: string;
|
|
6
|
+
model?: string;
|
|
7
|
+
signature: string;
|
|
8
|
+
timestamp?: number;
|
|
9
|
+
requestId?: string;
|
|
10
|
+
sessionId?: string;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type FailureFingerprint = string;
|
|
15
|
+
|
|
16
|
+
export interface LedgerRecord {
|
|
17
|
+
fingerprint: FailureFingerprint;
|
|
18
|
+
firstSeen: number;
|
|
19
|
+
lastSeen: number;
|
|
20
|
+
count: number;
|
|
21
|
+
status: RemediationStatus;
|
|
22
|
+
details?: Record<string, unknown>;
|
|
23
|
+
}
|
package/src/types/config.ts
CHANGED
|
@@ -255,8 +255,19 @@ export interface OcxSubagentRole {
|
|
|
255
255
|
enabled?: boolean;
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
+
export interface OcxConfigRebaseProvenance {
|
|
259
|
+
version: 1;
|
|
260
|
+
deletedTopLevelKeys: string[];
|
|
261
|
+
}
|
|
262
|
+
|
|
258
263
|
export interface OcxConfig {
|
|
259
264
|
port: number;
|
|
265
|
+
autonomousRemediation?: {
|
|
266
|
+
enabled?: boolean;
|
|
267
|
+
instanceId?: string;
|
|
268
|
+
threshold?: number;
|
|
269
|
+
rollingWindowMs?: number;
|
|
270
|
+
};
|
|
260
271
|
/** Opt in to one identical-turn retry when a Responses completion has no text or tool call. */
|
|
261
272
|
emptyCompletionRetry?: boolean;
|
|
262
273
|
/**
|
|
@@ -276,6 +287,22 @@ export interface OcxConfig {
|
|
|
276
287
|
managementUsageMaxReadBytes?: number;
|
|
277
288
|
providers: Record<string, OcxProviderConfig>;
|
|
278
289
|
defaultProvider: string;
|
|
290
|
+
/** Persisted state for newly discovered provider models (#2464). Absent keeps legacy "on" behavior. */
|
|
291
|
+
modelDiscovery?: {
|
|
292
|
+
newModelPolicy?: "on" | "off";
|
|
293
|
+
knownModels?: Record<string, {
|
|
294
|
+
ids: string[];
|
|
295
|
+
removed: string[];
|
|
296
|
+
updatedAt: string;
|
|
297
|
+
/** Consecutive successful discoveries in which an active id was absent. */
|
|
298
|
+
missing?: Record<string, number>;
|
|
299
|
+
}>;
|
|
300
|
+
recentArrivals?: Record<string, Array<{ id: string; at: string }>>;
|
|
301
|
+
};
|
|
302
|
+
/** Enable the shipped model alias patterns for providers without an override. */
|
|
303
|
+
defaultModelAliases?: boolean;
|
|
304
|
+
/** Explicit top-level deletion intent used by stale whole-config rebases. */
|
|
305
|
+
configRebaseProvenance?: OcxConfigRebaseProvenance | Record<string, unknown>;
|
|
279
306
|
/** OpenAI provider-contract migration marker (v2 = single `openai` provider with account mode). */
|
|
280
307
|
openaiProviderTierVersion?: 1 | 2;
|
|
281
308
|
/** One-time migration marker for Antigravity's static-catalog defaults. */
|
|
@@ -294,12 +321,7 @@ export interface OcxConfig {
|
|
|
294
321
|
* into a selector-qualified group; Codex still advertises only the first 5 visible rows.
|
|
295
322
|
*/
|
|
296
323
|
subagentModels?: string[];
|
|
297
|
-
/**
|
|
298
|
-
* Named specialist roles the parent may spawn: id, when-to-use description,
|
|
299
|
-
* model, optional effort, and child developer instructions. Max 8 roles and
|
|
300
|
-
* 5 unique enabled models (the spawn picker window). Do not store
|
|
301
|
-
* `model_fallback` here — use `subagentModelFallbackByModel`.
|
|
302
|
-
*/
|
|
324
|
+
/** Named specialist roles the parent may spawn by id. */
|
|
303
325
|
subagentRoles?: OcxSubagentRole[];
|
|
304
326
|
/**
|
|
305
327
|
* Project enabled roles into marker-owned `$CODEX_HOME/agents/ocx-<id>.toml`.
|
|
@@ -338,6 +360,10 @@ export interface OcxConfig {
|
|
|
338
360
|
* reject the whole role file as an unknown field (#1190).
|
|
339
361
|
*/
|
|
340
362
|
subagentModelFallbackByModel?: Record<string, string[]>;
|
|
363
|
+
/**
|
|
364
|
+
* Ordered candidates for spawned sub-agents, globally or keyed by role/model.
|
|
365
|
+
*/
|
|
366
|
+
subagentCandidates?: string[] | Record<string, string[]>;
|
|
341
367
|
/**
|
|
342
368
|
* TTL (ms) for cached sub-agent model availability probes. Default 60_000.
|
|
343
369
|
*/
|
|
@@ -402,9 +428,7 @@ export interface OcxConfig {
|
|
|
402
428
|
* remain unchanged),
|
|
403
429
|
* `{{effort}}` -> injectionEffort, `{{roster}}` -> the resolved sub-agent roster
|
|
404
430
|
* block ("" when nothing resolves), `{{fallback}}` -> the configured subagent
|
|
405
|
-
* model fallback guidance block ("" when unset)
|
|
406
|
-
* enabled role catalog ("" when none). A custom prompt replaces the built-in
|
|
407
|
-
* body; include `{{roles}}` to keep the catalog.
|
|
431
|
+
* model fallback guidance block ("" when unset).
|
|
408
432
|
*/
|
|
409
433
|
injectionPrompt?: string;
|
|
410
434
|
/**
|
|
@@ -468,12 +492,12 @@ export interface OcxConfig {
|
|
|
468
492
|
* Routed parents get v2 tools; Sol/Terra can still spawn Grok/Claude (issue #92).
|
|
469
493
|
*/
|
|
470
494
|
keepNativeChatGptOnV1?: boolean;
|
|
471
|
-
/**
|
|
495
|
+
/** Optional v2-native parent override for spawn_agent routing. */
|
|
472
496
|
v2NativeParentOverride?: { enabled?: boolean; model?: string };
|
|
473
497
|
/** Experimental, default-off ChatGPT recovery for encrypted V2 routed tasks. */
|
|
474
498
|
agentTaskRecovery?: {
|
|
475
499
|
enabled?: boolean;
|
|
476
|
-
/** ChatGPT model used by the recovery request. Default: gpt-5.6-
|
|
500
|
+
/** ChatGPT model used by the recovery request. Default: gpt-5.6-sol. */
|
|
477
501
|
model?: string;
|
|
478
502
|
/** Recovery request timeout in milliseconds. Default: 45000. */
|
|
479
503
|
timeoutMs?: number;
|
|
@@ -516,6 +540,13 @@ export interface OcxConfig {
|
|
|
516
540
|
* those are unset — Bun's fetch honors them for all outbound calls; localhost is excluded.
|
|
517
541
|
*/
|
|
518
542
|
proxy?: string;
|
|
543
|
+
/**
|
|
544
|
+
* Hosts that bypass `proxy` for OpenCodex's own outbound provider calls, merged into
|
|
545
|
+
* NO_PROXY at startup. Accepts a comma-separated string (NO_PROXY syntax) or an array.
|
|
546
|
+
* Loopback is always excluded regardless of this setting, and an inherited NO_PROXY is
|
|
547
|
+
* preserved — this ADDS entries, it never replaces the environment.
|
|
548
|
+
*/
|
|
549
|
+
noProxy?: string | string[];
|
|
519
550
|
/**
|
|
520
551
|
* Upstream stall timeout (seconds). After this many seconds of no upstream data, emits
|
|
521
552
|
* response.incomplete. Default 300. Min 1.
|
|
@@ -587,6 +618,15 @@ export interface OcxConfig {
|
|
|
587
618
|
* selector map remains visible for compatibility with hand-written configurations.
|
|
588
619
|
*/
|
|
589
620
|
codexAccountPickerEnabled?: boolean;
|
|
621
|
+
/**
|
|
622
|
+
* Show the GPT-5.3-Codex-Spark weekly window on Codex quota surfaces. Default false.
|
|
623
|
+
*
|
|
624
|
+
* Spark is a single-model window that reads 0% for most operators, and on a multi-account
|
|
625
|
+
* pool it doubles the bar count for information almost nobody acts on. Hidden by default and
|
|
626
|
+
* revealed by an explicit `true`; a malformed value reads as hidden rather than rejecting the
|
|
627
|
+
* whole config.
|
|
628
|
+
*/
|
|
629
|
+
showCodexSparkQuota?: boolean;
|
|
590
630
|
/** Active pool account id for next session. undefined = main (passthrough as-is). */
|
|
591
631
|
activeCodexAccountId?: string;
|
|
592
632
|
/** Auto-switch threshold (0-100). Default 80. 0 = disabled. */
|
|
@@ -617,10 +657,22 @@ export interface OcxConfig {
|
|
|
617
657
|
stickyLimit?: number;
|
|
618
658
|
};
|
|
619
659
|
/**
|
|
620
|
-
*
|
|
621
|
-
*
|
|
622
|
-
*
|
|
660
|
+
* Generic OAuth multi-account 429 failover (#2568). Presence-driven by default.
|
|
661
|
+
*
|
|
662
|
+
* Rotates to another logged-in account of the SAME provider when one is rate-limited, for
|
|
663
|
+
* OAuth providers that have no pool of their own — xAI, Cursor, Kimi, GitHub Copilot,
|
|
664
|
+
* Antigravity, Nous. The Codex pool and the Anthropic pool own their own rotation and are
|
|
665
|
+
* excluded; this setting changes neither.
|
|
666
|
+
*
|
|
667
|
+
* With the key absent, rotation activates when a provider has 2 or more eligible stored
|
|
668
|
+
* accounts — the same consent rule API-key pools already apply to a 2+ key pool (#2568d). A
|
|
669
|
+
* single account is a strict no-op. Set `false` to keep strict single-account behaviour;
|
|
670
|
+
* `providers.<name>.oauthAccountFailover` overrides this per provider.
|
|
623
671
|
*/
|
|
672
|
+
oauthAccountFailover?: {
|
|
673
|
+
enabled?: boolean;
|
|
674
|
+
};
|
|
675
|
+
/** Cursor OAuth account pool rotation for api2.cursor.sh. */
|
|
624
676
|
cursorAccountPool?: {
|
|
625
677
|
enabled?: boolean;
|
|
626
678
|
};
|
package/src/types/provider.ts
CHANGED
|
@@ -119,6 +119,15 @@ export interface TierObservationContext {
|
|
|
119
119
|
fastWire: FastWire | null;
|
|
120
120
|
demandDecision: "force-fast" | "force-default" | "inherit";
|
|
121
121
|
callerTier?: string;
|
|
122
|
+
/**
|
|
123
|
+
* Whether the destination's echoed `service_tier` is authoritative about Fast scheduling.
|
|
124
|
+
*
|
|
125
|
+
* The ChatGPT-internal Codex backend returns `service_tier: "default"` on turns that were in
|
|
126
|
+
* fact scheduled as priority, so treating its echo as a downgrade produced a false
|
|
127
|
+
* `response-declined` on every Fast request (#2558). Absent means "assume authoritative",
|
|
128
|
+
* preserving the behaviour for the public API where the echo does mean what it says.
|
|
129
|
+
*/
|
|
130
|
+
responseTierAuthoritative?: boolean;
|
|
122
131
|
}
|
|
123
132
|
|
|
124
133
|
export type TierDecision =
|
|
@@ -131,6 +140,12 @@ export type TierDecision =
|
|
|
131
140
|
* retries are allowed; OAuth/forward credentials and local runtimes are never replayed.
|
|
132
141
|
*/
|
|
133
142
|
export interface OcxProviderConfig {
|
|
143
|
+
/** Optional short provider namespace used only at request/catalog presentation time. */
|
|
144
|
+
alias?: string;
|
|
145
|
+
/** Native model id -> short, slash-free request alias. */
|
|
146
|
+
modelAliases?: Record<string, string>;
|
|
147
|
+
/** Override the global built-in model-alias switch for this provider. */
|
|
148
|
+
defaultAliases?: boolean;
|
|
134
149
|
adapter: string;
|
|
135
150
|
/**
|
|
136
151
|
* Codex tool calling mode for routed models.
|
|
@@ -227,6 +242,19 @@ export interface OcxProviderConfig {
|
|
|
227
242
|
* link-local, or unique-local upstreams. Metadata endpoints remain blocked.
|
|
228
243
|
*/
|
|
229
244
|
allowPrivateNetwork?: boolean;
|
|
245
|
+
/**
|
|
246
|
+
* ChatGPT Codex backend WebSocket upstream transport.
|
|
247
|
+
* Defaults to false (routes streaming turns over standard HTTP/SSE).
|
|
248
|
+
* Set `true` to opt into the faster responses_websockets transport.
|
|
249
|
+
* `OCX_CODEX_WS_UPSTREAM=true` or `1` also enables it when this is omitted;
|
|
250
|
+
* `false` and `0` disable it. Invalid or absent values default to HTTP/SSE.
|
|
251
|
+
*/
|
|
252
|
+
wsUpstream?: boolean;
|
|
253
|
+
/**
|
|
254
|
+
* Maximum WebSocket request frame size in bytes before routing over standard HTTP/SSE.
|
|
255
|
+
* Defaults to CODEX_WS_CREATE_FRAME_LIMIT_BYTES (~16 MiB minus margin).
|
|
256
|
+
*/
|
|
257
|
+
maxWsFrameBytes?: number;
|
|
230
258
|
/**
|
|
231
259
|
* Pin the HTTP version used for upstream provider requests. Bun's fetch negotiates
|
|
232
260
|
* HTTP/2 via TLS ALPN by default; some Cloudflare-fronted SSE endpoints hang on
|
|
@@ -284,6 +312,33 @@ export interface OcxProviderConfig {
|
|
|
284
312
|
* full set so the user can pick). See devlog issue_052_provider-model-allowlist.
|
|
285
313
|
*/
|
|
286
314
|
selectedModels?: string[];
|
|
315
|
+
/** Override for newly discovered models. Absent/"inherit" uses the install policy. */
|
|
316
|
+
newModelPolicy?: "on" | "off" | "inherit";
|
|
317
|
+
/**
|
|
318
|
+
* Model-preset marker for `selectedModels` (#2465). Absent means "all", exactly today's
|
|
319
|
+
* semantics — an existing provider is never narrowed by an upgrade.
|
|
320
|
+
*
|
|
321
|
+
* The preset is a SEED, not a lock: `selectedModels` holds concrete ids materialized from
|
|
322
|
+
* the shipped rules, so every existing consumer and older binaries keep working against a
|
|
323
|
+
* plain allowlist. Divergence is detected at the WRITE path rather than by diffing — any user
|
|
324
|
+
* edit while the mode is "preset" flips it to "custom", after which the proxy never
|
|
325
|
+
* re-materializes. That collapses upgrade reconciliation to a version compare.
|
|
326
|
+
*
|
|
327
|
+
* Deliberately distinct from `deriveProviderPresets`, which curates WHICH PROVIDERS to offer.
|
|
328
|
+
* This curates which MODELS a provider exposes; the code says "model preset" throughout.
|
|
329
|
+
*/
|
|
330
|
+
modelPreset?: {
|
|
331
|
+
mode: "preset" | "all" | "custom";
|
|
332
|
+
/** MODEL_PRESETS version materialized into `selectedModels`. */
|
|
333
|
+
appliedVersion?: number;
|
|
334
|
+
appliedAt?: string;
|
|
335
|
+
/**
|
|
336
|
+
* Set when materialization matched nothing and the provider fell back to "all". A preset
|
|
337
|
+
* must never write an empty allowlist, because empty means ALL and would silently
|
|
338
|
+
* un-curate; the fallback marker lets the next convergence retry.
|
|
339
|
+
*/
|
|
340
|
+
fallback?: "preset-empty";
|
|
341
|
+
};
|
|
287
342
|
/** Provider-wide fallback when context metadata is absent; otherwise caps the reported window. */
|
|
288
343
|
contextWindow?: number;
|
|
289
344
|
/** Per-model fallback when context metadata is absent; otherwise caps the reported window. */
|
|
@@ -327,6 +382,16 @@ export interface OcxProviderConfig {
|
|
|
327
382
|
* providers whose registry entry declares authKind "local" (management API enforces).
|
|
328
383
|
*/
|
|
329
384
|
authMode?: "key" | "forward" | "oauth" | "local";
|
|
385
|
+
/**
|
|
386
|
+
* Per-provider override for generic OAuth multi-account 429 failover (#2568).
|
|
387
|
+
*
|
|
388
|
+
* Rotation is presence-driven by default — 2+ logged-in accounts activate it — so this exists
|
|
389
|
+
* for the operator who accepts rotation on one provider and refuses it on another. An explicit
|
|
390
|
+
* boolean here beats the global `oauthAccountFailover` and beats presence.
|
|
391
|
+
*/
|
|
392
|
+
oauthAccountFailover?: {
|
|
393
|
+
enabled?: boolean;
|
|
394
|
+
};
|
|
330
395
|
/** Allow an explicitly key/oauth provider to run without a credential (for keyless local proxies). */
|
|
331
396
|
keyOptional?: boolean;
|
|
332
397
|
/**
|
|
@@ -359,6 +424,18 @@ export interface OcxProviderConfig {
|
|
|
359
424
|
* Responses backend rejects Codex summary-delivery fields for that model.
|
|
360
425
|
*/
|
|
361
426
|
modelSupportsReasoningSummaries?: Record<string, boolean>;
|
|
427
|
+
/**
|
|
428
|
+
* Model-specific Codex Responses verbosity capability. Set false when the upstream ignores
|
|
429
|
+
* `text.verbosity`; the catalog hides the no-op picker and the Responses adapter strips stale
|
|
430
|
+
* or caller-supplied values while preserving other `text` fields.
|
|
431
|
+
*/
|
|
432
|
+
modelSupportsVerbosity?: Record<string, boolean>;
|
|
433
|
+
/**
|
|
434
|
+
* Provider-wide Codex Responses verbosity capability, applied to models the per-model map
|
|
435
|
+
* does not enumerate (a live-discovered id, for example). Materialized from the registry at
|
|
436
|
+
* seed/enrich time so the catalog hint pass never has to read PROVIDER_REGISTRY.
|
|
437
|
+
*/
|
|
438
|
+
supportsVerbosity?: boolean;
|
|
362
439
|
/**
|
|
363
440
|
* Per-model wire value for Responses `stream_options.reasoning_summary_delivery`.
|
|
364
441
|
* Presence also advertises reasoning-summary support for that routed model.
|
package/src/types/request.ts
CHANGED
|
@@ -72,6 +72,8 @@ export interface OcxParsedRequest {
|
|
|
72
72
|
_promptCacheKeyIsSharedCohort?: boolean;
|
|
73
73
|
/** Provider-private Command Code session affinity id, stable across in-process request mutation. */
|
|
74
74
|
_commandCodeSessionId?: string;
|
|
75
|
+
/** Cursor-only thread owner; may be an opaque process-local Desktop session/thread identity. */
|
|
76
|
+
_cursorClientThreadId?: string;
|
|
75
77
|
/** Conversation/provider/account/model-bound namespace for reasoning replay state. */
|
|
76
78
|
_reasoningReplayScope?: OcxReasoningReplayScopeRef;
|
|
77
79
|
/**
|
|
@@ -90,7 +92,7 @@ export interface OcxParsedRequest {
|
|
|
90
92
|
*/
|
|
91
93
|
_cursorIsolateConversation?: boolean;
|
|
92
94
|
/** Account-scoped, non-secret Kiro request metadata selected with the OAuth access token. */
|
|
93
|
-
_kiroAuthContext?: Pick<KiroOAuthMetadata, "profileArn" | "apiRegion" | "ssoRegion">;
|
|
95
|
+
_kiroAuthContext?: Pick<KiroOAuthMetadata, "profileArn" | "apiRegion" | "ssoRegion" | "authType">;
|
|
94
96
|
/** Provider-private continuation metadata resolved from the Responses previous_response_id chain. */
|
|
95
97
|
_providerContinuation?: OcxProviderContinuationState;
|
|
96
98
|
/** Persisted continuation considered only after the final physical route is known. */
|
|
@@ -198,8 +200,14 @@ export interface OcxImageContent {
|
|
|
198
200
|
detail?: string;
|
|
199
201
|
}
|
|
200
202
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
+
export interface OcxVideoContent {
|
|
204
|
+
type: "video";
|
|
205
|
+
/** A base64 `data:` URL from an OpenAI-compatible `video_url` part. */
|
|
206
|
+
videoUrl: string;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** A user/developer message content part: text or native media. */
|
|
210
|
+
export type OcxContentPart = OcxTextContent | OcxImageContent | OcxVideoContent;
|
|
203
211
|
|
|
204
212
|
export interface OcxThinkingContent {
|
|
205
213
|
type: "thinking";
|
|
@@ -260,13 +268,9 @@ export interface OcxRequestOptions {
|
|
|
260
268
|
/**
|
|
261
269
|
* Responses `text.format` (json_schema / json_object), preserved for adapters whose
|
|
262
270
|
* upstream wire has an equivalent. The openai-chat adapter re-nests it as chat
|
|
263
|
-
* `response_format`, the exact inverse of responseFormatToText in src/chat/inbound.ts
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
* The `openai-chat` adapter can omit it for models in `noStructuredOutputModels`; Kiro
|
|
267
|
-
* rejects structured output via `_structuredOutput`; and Cursor has no structured-output
|
|
268
|
-
* wire field and rejects the request before transport.
|
|
269
|
-
* Native passthrough does not consume this option and forwards `_rawBody.text` verbatim.
|
|
271
|
+
* `response_format`, the exact inverse of responseFormatToText in src/chat/inbound.ts.
|
|
272
|
+
* The native passthrough ignores it (it forwards `_rawBody.text` verbatim) and Kiro
|
|
273
|
+
* keeps rejecting structured output via `_structuredOutput`.
|
|
270
274
|
*/
|
|
271
275
|
textFormat?: {
|
|
272
276
|
type: "json_schema" | "json_object";
|
|
@@ -292,10 +296,12 @@ export type GoogleSafetyThreshold =
|
|
|
292
296
|
| "BLOCK_ONLY_HIGH"
|
|
293
297
|
| "BLOCK_NONE"
|
|
294
298
|
| "OFF";
|
|
299
|
+
|
|
295
300
|
export interface GoogleSafetySetting {
|
|
296
301
|
category: GoogleSafetyCategory;
|
|
297
302
|
threshold: GoogleSafetyThreshold;
|
|
298
303
|
}
|
|
304
|
+
|
|
299
305
|
export interface GoogleProviderOptions {
|
|
300
306
|
thinkingBudget?: number;
|
|
301
307
|
includeThoughts?: boolean;
|