@bitkyc08/opencodex 2.41.0 → 2.43.0-preview.20260906
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/AGENTS_INSTALL.md +2 -2
- package/README.md +31 -0
- package/bin/ocx.mjs +10 -1
- package/gui/dist/assets/index-DS1NE4Jn.css +1 -0
- package/gui/dist/assets/index-VGeQEZ_v.js +112 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic-image-codec.ts +304 -0
- package/src/adapters/anthropic-image-normalize.ts +8 -298
- package/src/adapters/anthropic.ts +30 -7
- package/src/adapters/command-code.ts +7 -5
- package/src/adapters/cursor/desktop-executor-contract.ts +15 -0
- package/src/adapters/cursor/images.ts +36 -6
- package/src/adapters/cursor/live-transport.ts +7 -2
- package/src/adapters/cursor/native-exec-desktop.ts +2 -15
- package/src/adapters/cursor/protobuf-request.ts +54 -24
- package/src/adapters/cursor/tool-definitions.ts +5 -670
- package/src/adapters/cursor/tool-guidance.ts +236 -0
- package/src/adapters/cursor/tool-naming.ts +252 -0
- package/src/adapters/cursor/tool-schemas.ts +195 -0
- package/src/adapters/cursor/types.ts +6 -3
- package/src/adapters/exec-tool-result-normalize.ts +1 -1
- package/src/adapters/google-errors.ts +9 -1
- package/src/adapters/google.ts +1 -0
- package/src/adapters/identity.ts +8 -2
- package/src/adapters/kiro-calibration.ts +181 -0
- package/src/adapters/kiro.ts +135 -3
- package/src/adapters/openai-responses.ts +259 -29
- package/src/adapters/responses-code-mode.ts +59 -0
- package/src/adapters/tool-catalog-nudge.ts +1 -1
- package/src/adapters/xai-schema-analysis.ts +86 -0
- package/src/adapters/xai-tool-schema.ts +2 -87
- package/src/adapters/xai-web-search.ts +1 -1
- package/src/bridge.ts +47 -13
- package/src/chat/inbound.ts +11 -3
- package/src/claude/inbound-content-options.ts +60 -0
- package/src/claude/inbound-model-options.ts +142 -0
- package/src/claude/inbound-records.ts +7 -0
- package/src/claude/inbound.ts +10 -202
- package/src/claude/model-info.ts +45 -0
- package/src/cli/account-auth.ts +49 -9
- package/src/cli/account-extended.ts +7 -1
- package/src/cli/capabilities.ts +15 -4
- package/src/cli/claude.ts +232 -39
- package/src/cli/config-command.ts +9 -1
- package/src/cli/dispatch.ts +5 -1
- package/src/cli/doctor.ts +10 -0
- package/src/cli/effort.ts +372 -0
- package/src/cli/export-command.ts +3 -9
- package/src/cli/help.ts +1 -0
- package/src/cli/index.ts +13 -0
- package/src/cli/init.ts +4 -0
- package/src/cli/model-selection-guidance.ts +30 -0
- package/src/cli/models-runtime.ts +3 -2
- package/src/cli/models.ts +8 -3
- package/src/cli/observe.ts +3 -1
- package/src/cli/opencode.ts +4 -1
- package/src/cli/provider-runtime.ts +65 -0
- package/src/cli/provider.ts +8 -0
- package/src/cli/registry.ts +16 -2
- package/src/cli/runtime-api.ts +3 -1
- package/src/cli/star-prompt.ts +22 -6
- package/src/cli/status-probes.ts +168 -0
- package/src/cli/status.ts +5 -168
- package/src/clients/config-export/constants.ts +69 -0
- package/src/clients/config-export/contracts.ts +154 -0
- package/src/clients/config-export/dsh.ts +132 -0
- package/src/clients/config-export/fast-models.ts +29 -0
- package/src/clients/config-export/mcode.ts +83 -0
- package/src/clients/config-export/model-metadata.ts +108 -0
- package/src/clients/config-export/omp.ts +104 -0
- package/src/clients/config-export/zcode.ts +92 -0
- package/src/clients/config-export.ts +18 -710
- package/src/codex/account-lifecycle.ts +20 -3
- package/src/codex/account-usability.ts +2 -0
- package/src/codex/auth-api.ts +272 -32
- package/src/codex/auth-context.ts +328 -24
- package/src/codex/catalog/effort.ts +44 -5
- package/src/codex/catalog/metadata.ts +149 -14
- package/src/codex/catalog/native-models.ts +116 -4
- package/src/codex/catalog/parsing.ts +122 -8
- package/src/codex/catalog/provider-fetch.ts +154 -23
- package/src/codex/catalog/reserve.ts +52 -0
- package/src/codex/catalog/sync.ts +89 -16
- package/src/codex/catalog.ts +1 -1
- package/src/codex/convergence-types.ts +1 -0
- package/src/codex/convergence.ts +2 -0
- package/src/codex/data/upstream-models.json +169 -0
- package/src/codex/forward-transport-headers.ts +25 -0
- package/src/codex/inject.ts +99 -34
- package/src/codex/injected-marker.ts +30 -4
- package/src/codex/journal.ts +14 -0
- package/src/codex/legacy-config-keys.ts +68 -0
- package/src/codex/log-guard/inspect-schema.ts +137 -0
- package/src/codex/log-guard/inspect.ts +2 -134
- package/src/codex/loopback-target.ts +54 -0
- package/src/codex/main-account-cache.ts +63 -1
- package/src/codex/main-account-hard-lock.ts +52 -0
- package/src/codex/main-account.ts +3 -1
- package/src/codex/management-convergence.ts +3 -0
- package/src/codex/model-entitlements.ts +54 -4
- package/src/codex/project-config-warnings.ts +92 -2
- package/src/codex/prompt-layers/encoding.ts +80 -0
- package/src/codex/prompt-layers/paths.ts +54 -0
- package/src/codex/prompt-layers/revision.ts +55 -0
- package/src/codex/prompt-layers/toml-edit.ts +163 -0
- package/src/codex/prompt-layers/toml-read.ts +181 -0
- package/src/codex/prompt-layers.ts +14 -520
- package/src/codex/quota-auto-refresh-state.ts +16 -0
- package/src/codex/quota-auto-refresh.ts +219 -0
- package/src/codex/quota-types.ts +51 -0
- package/src/codex/quota.ts +252 -93
- package/src/codex/reserve-availability.ts +177 -0
- package/src/codex/routing.ts +28 -9
- package/src/codex/shim.ts +53 -11
- package/src/codex/subagent-model-fallback.ts +23 -3
- package/src/combos/failover.ts +125 -7
- package/src/combos/identifiers.ts +89 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/resolve.ts +80 -9
- package/src/combos/types.ts +20 -93
- package/src/config/subagent-models.ts +24 -0
- package/src/config.ts +156 -13
- package/src/generated/compatibility-version.json +474 -178
- package/src/generated/model-metadata.ts +1 -1
- package/src/integrations/journal.ts +65 -4
- package/src/integrations/store.ts +5 -0
- package/src/lab/events/limits.ts +4 -0
- package/src/lib/destination-policy.ts +31 -2
- package/src/lib/errors.ts +39 -0
- package/src/lib/provider-outbound.ts +69 -3
- package/src/lib/proxy-env.ts +22 -0
- package/src/lib/redact-folding.ts +176 -0
- package/src/lib/redact.ts +2 -175
- package/src/lib/state-store-sweeper.ts +20 -6
- package/src/lib/token-estimate.ts +94 -27
- package/src/lib/windows-user-principal.ts +53 -5
- package/src/oauth/account-quota-rank.ts +40 -1
- package/src/oauth/anthropic-routing.ts +99 -3
- package/src/oauth/chatgpt-device.ts +187 -0
- package/src/oauth/chatgpt.ts +31 -4
- package/src/oauth/generic-account-failover.ts +36 -13
- package/src/oauth/index.ts +140 -29
- package/src/oauth/log.ts +3 -0
- package/src/oauth/login-cli.ts +5 -0
- package/src/oauth/meta-muse.ts +117 -15
- package/src/oauth/pool-settings-capability.ts +15 -4
- package/src/providers/api-keys.ts +8 -10
- package/src/providers/default-aliases.ts +39 -0
- package/src/providers/derive.ts +10 -2
- package/src/providers/fastwire.ts +36 -7
- package/src/providers/initial-model-selection-runtime.ts +90 -0
- package/src/providers/initial-model-selection.ts +120 -0
- package/src/providers/key-failover.ts +134 -54
- package/src/providers/key-store.ts +11 -1
- package/src/providers/label.ts +1 -1
- package/src/providers/model-discovery.ts +76 -0
- package/src/providers/model-rename-startup.ts +72 -8
- package/src/providers/muse-subscription-usage.ts +95 -0
- package/src/providers/openai-sidecar.ts +17 -5
- package/src/providers/openai-tiers-destination.ts +102 -0
- package/src/providers/openai-tiers.ts +2 -99
- package/src/providers/opencode-go-transport.ts +41 -0
- package/src/providers/quota-key-accounts.ts +141 -0
- package/src/providers/quota-types.ts +9 -0
- package/src/providers/quota.ts +625 -98
- package/src/providers/registry.ts +60 -17
- package/src/providers/xai-responses-opt-in.ts +31 -5
- package/src/quota/reset-activation.ts +81 -0
- package/src/quota/reset-detector.ts +305 -0
- package/src/quota/reset-notify-config.ts +162 -0
- package/src/quota/reset-observer.ts +125 -0
- package/src/quota/reset-poller.ts +160 -0
- package/src/quota/reset-seen-store.ts +385 -0
- package/src/quota/reset-sinks.ts +199 -0
- package/src/quota/window-mapping.ts +106 -0
- package/src/responses/apply-patch-envelope.ts +46 -0
- package/src/responses/code-mode-helper-compat.ts +39 -1
- package/src/responses/custom-tool-compat.ts +10 -4
- package/src/responses/hosted-tool-policy.ts +12 -4
- package/src/responses/parser-content.ts +133 -0
- package/src/responses/parser-text-format.ts +24 -0
- package/src/responses/parser-tools.ts +188 -0
- package/src/responses/parser.ts +3 -326
- package/src/responses/state.ts +124 -28
- package/src/router.ts +48 -13
- package/src/routing/analytics.ts +1 -0
- package/src/routing/capability.ts +17 -4
- package/src/server/auth-cors.ts +7 -1
- package/src/server/background-lifecycle.ts +23 -1
- package/src/server/chat-completions.ts +25 -3
- package/src/server/claude-messages.ts +62 -5
- package/src/server/effort-row.ts +1 -1
- package/src/server/fast-row.ts +295 -0
- package/src/server/gui-static.ts +30 -4
- package/src/server/index.ts +122 -28
- package/src/server/live.ts +18 -4
- package/src/server/management/agent-settings-routes.ts +2 -2
- package/src/server/management/combo-routes.ts +37 -9
- package/src/server/management/config-routes.ts +93 -2
- package/src/server/management/integration-routes.ts +108 -0
- package/src/server/management/model-routes.ts +13 -3
- package/src/server/management/model-rows.ts +20 -1
- package/src/server/management/native-integration-routes.ts +4 -1
- package/src/server/management/oauth-account-routes.ts +45 -10
- package/src/server/management/provider-routes.ts +34 -3
- package/src/server/management/quota-reset-routes.ts +57 -0
- package/src/server/management/route-registry.ts +7 -4
- package/src/server/management/shared.ts +19 -5
- package/src/server/management/system-routes.ts +3 -2
- package/src/server/management-api.ts +14 -2
- package/src/server/ports.ts +12 -2
- package/src/server/relay-eager.ts +38 -23
- package/src/server/relay.ts +4 -0
- package/src/server/request-log.ts +6 -0
- package/src/server/responses/codex-ws-correlation.ts +65 -0
- package/src/server/responses/codex-ws-exchange.ts +261 -0
- package/src/server/responses/codex-ws-metadata.ts +134 -0
- package/src/server/responses/codex-ws-pool.ts +162 -0
- package/src/server/responses/codex-ws-request.ts +87 -0
- package/src/server/responses/codex-ws-session.ts +93 -0
- package/src/server/responses/codex-ws-wire.ts +144 -0
- package/src/server/responses/collaboration.ts +41 -1
- package/src/server/responses/compact.ts +105 -12
- package/src/server/responses/core.ts +510 -57
- package/src/server/responses/empty-completion-guard.ts +4 -0
- package/src/server/responses/fetch-helpers.ts +10 -3
- package/src/server/responses/input-admission.ts +16 -9
- package/src/server/responses/responses-field-backfill.ts +1 -1
- package/src/server/responses/ws-upstream.ts +34 -318
- package/src/server/responses-custom-tool-repair.ts +20 -4
- package/src/server/responses-undeclared-tool-guard.ts +100 -8
- package/src/server/safe-response-headers.ts +23 -0
- package/src/server/search.ts +9 -0
- package/src/server/subagent-models-startup.ts +27 -0
- package/src/server/system-env-shell.ts +238 -0
- package/src/server/system-env.ts +7 -234
- package/src/server/ws-bridge.ts +3 -25
- package/src/server/xai-responses-startup.ts +21 -0
- package/src/service-manager-probe.ts +1 -1
- package/src/service.ts +55 -16
- package/src/types/config.ts +108 -12
- package/src/types/provider.ts +36 -7
- package/src/types/request.ts +8 -0
- package/src/types/tools.ts +26 -1
- package/src/types.ts +2 -0
- package/src/update/notify.ts +8 -2
- package/src/usage/cost.ts +38 -28
- package/src/usage/expected-prices.ts +34 -15
- package/src/usage/log.ts +2 -0
- package/src/usage/model-identity.ts +26 -0
- package/src/usage/summary.ts +15 -1
- package/src/vision/describe.ts +6 -0
- package/src/vision/image-rewrite.ts +108 -0
- package/src/vision/index.ts +19 -306
- package/src/vision/plan.ts +205 -0
- package/src/web-search/executor.ts +6 -0
- package/src/web-search/index.ts +8 -1
- package/gui/dist/assets/index-B2YjLA-i.css +0 -1
- package/gui/dist/assets/index-aPup8CKb.js +0 -112
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import type { OcxTool } from "../../types";
|
|
2
|
+
import { CODEX_SHELL_COMMAND_TOOL, isBareCodexExecCommandTool, isBareCodexShellBridgeTool, isCodexShellBridgeToolName } from "./tool-naming";
|
|
3
|
+
|
|
4
|
+
export const CURSOR_EXEC_COMMAND_INPUT_SCHEMA = {
|
|
5
|
+
type: "object",
|
|
6
|
+
properties: {
|
|
7
|
+
cmd: { type: "string", description: "Shell command to execute." },
|
|
8
|
+
workdir: { type: "string", description: "Working directory for the command. Defaults to the turn cwd." },
|
|
9
|
+
shell: { type: "string", description: "Shell binary to launch. Defaults to the user's default shell." },
|
|
10
|
+
tty: { type: "boolean", description: "True allocates a PTY for the command; false or omitted uses plain pipes." },
|
|
11
|
+
yield_time_ms: { type: "number", description: "Wait before yielding output. Defaults to 10000 ms; effective range is 250-30000 ms." },
|
|
12
|
+
max_output_tokens: { type: "number", description: "Output token budget. Defaults to 10000 tokens; larger requests may be capped by policy." },
|
|
13
|
+
},
|
|
14
|
+
required: ["cmd"],
|
|
15
|
+
additionalProperties: false,
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Structured single-replacement schema advertised to Cursor models in addition to the freeform
|
|
20
|
+
* `apply_patch` tool. Cursor-trained models reliably emit exact-match replacements (the native
|
|
21
|
+
* Edit shape) but cannot produce Codex's freeform patch grammar, so every file edit attempt on the
|
|
22
|
+
* Cursor route produced malformed `apply_patch` payloads that the Codex client rejected locally
|
|
23
|
+
* (#1017). Calls to this tool are converted server-side into a valid apply_patch payload.
|
|
24
|
+
*/
|
|
25
|
+
export const CURSOR_EDIT_FILE_INPUT_SCHEMA = {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: {
|
|
28
|
+
file_path: { type: "string", description: "Path of the file to edit, relative to the workspace root." },
|
|
29
|
+
old_string: { type: "string", description: "Exact text to replace. Must match the current file content, including line breaks." },
|
|
30
|
+
new_string: { type: "string", description: "Replacement text. Empty removes the matched text." },
|
|
31
|
+
},
|
|
32
|
+
required: ["file_path", "old_string", "new_string"],
|
|
33
|
+
additionalProperties: false,
|
|
34
|
+
} as const;
|
|
35
|
+
|
|
36
|
+
/** Structured multi-replacement schema; mirrors Cursor's native MultiEdit shape. */
|
|
37
|
+
export const CURSOR_MULTI_EDIT_INPUT_SCHEMA = {
|
|
38
|
+
type: "object",
|
|
39
|
+
properties: {
|
|
40
|
+
file_path: { type: "string", description: "Path of the file to edit, relative to the workspace root." },
|
|
41
|
+
edits: {
|
|
42
|
+
type: "array",
|
|
43
|
+
items: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {
|
|
46
|
+
old_string: { type: "string", description: "Exact text to replace. Must match the current file content, including line breaks." },
|
|
47
|
+
new_string: { type: "string", description: "Replacement text. Empty removes the matched text." },
|
|
48
|
+
},
|
|
49
|
+
required: ["old_string", "new_string"],
|
|
50
|
+
additionalProperties: false,
|
|
51
|
+
},
|
|
52
|
+
description: "Ordered replacement edits for this file. Each old_string must match the current file content.",
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
required: ["file_path", "edits"],
|
|
56
|
+
additionalProperties: false,
|
|
57
|
+
} as const;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Responses/Codex-side schema used ONLY for arg-key normalization after Cursor returns a call.
|
|
61
|
+
* Cursor models are trained to emit `cmd`; Codex `shell_command` / `exec_command` validate
|
|
62
|
+
* `command`. Keeping `cmd` out of this schema lets `normalizeArgKeys` rewrite `cmd` → `command`.
|
|
63
|
+
*/
|
|
64
|
+
export const CODEX_SHELL_BRIDGE_ARG_NORMALIZE_SCHEMA = {
|
|
65
|
+
type: "object",
|
|
66
|
+
properties: {
|
|
67
|
+
command: { type: "string", description: "Shell command to execute." },
|
|
68
|
+
workdir: { type: "string", description: "Working directory for the command. Defaults to the turn cwd." },
|
|
69
|
+
shell: { type: "string", description: "Shell binary to launch. Defaults to the user's default shell." },
|
|
70
|
+
tty: { type: "boolean", description: "True allocates a PTY for the command; false or omitted uses plain pipes." },
|
|
71
|
+
yield_time_ms: { type: "number", description: "Wait before yielding output. Defaults to 10000 ms; effective range is 250-30000 ms." },
|
|
72
|
+
max_output_tokens: { type: "number", description: "Output token budget. Defaults to 10000 tokens; larger requests may be capped by policy." },
|
|
73
|
+
max_output_chars: { type: "number", description: "Output character budget when the Responses tool uses chars instead of tokens." },
|
|
74
|
+
},
|
|
75
|
+
required: ["command"],
|
|
76
|
+
} as const;
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
/** Schema advertised to Cursor for this tool (may use Cursor-preferred field names like `cmd`). */
|
|
80
|
+
export function cursorToolInputSchema(tool: OcxTool): unknown {
|
|
81
|
+
return isBareCodexExecCommandTool(tool) ? CURSOR_EXEC_COMMAND_INPUT_SCHEMA : (tool.parameters ?? {});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Schema used to normalize completed Cursor tool args back to Responses/Codex field names.
|
|
86
|
+
* Must NOT reuse `cursorToolInputSchema` for the shell bridge: advertising `cmd` while also
|
|
87
|
+
* treating `cmd` as canonical prevents the `cmd` → `command` rewrite Codex requires (#399).
|
|
88
|
+
*/
|
|
89
|
+
export function cursorToolArgNormalizeSchema(tool: OcxTool): unknown {
|
|
90
|
+
if (isBareCodexShellBridgeTool(tool)) {
|
|
91
|
+
return shellBridgeArgNormalizeSchema(tool);
|
|
92
|
+
}
|
|
93
|
+
return tool.parameters ?? {};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function shellBridgeArgNormalizeSchema(tool: OcxTool): unknown {
|
|
97
|
+
const parameters = tool.parameters;
|
|
98
|
+
if (!parameters || typeof parameters !== "object") return CODEX_SHELL_BRIDGE_ARG_NORMALIZE_SCHEMA;
|
|
99
|
+
const base = parameters as Record<string, unknown>;
|
|
100
|
+
const rawProps = base.properties && typeof base.properties === "object"
|
|
101
|
+
? { ...(base.properties as Record<string, unknown>) }
|
|
102
|
+
: {};
|
|
103
|
+
const required = Array.isArray(base.required) ? [...base.required as unknown[]] : [];
|
|
104
|
+
const requiresCommand = required.includes("command") || "command" in rawProps;
|
|
105
|
+
const requiresCmd = required.includes("cmd") || "cmd" in rawProps;
|
|
106
|
+
const shouldRewriteCmdToCommand = tool.name === CODEX_SHELL_COMMAND_TOOL || requiresCommand;
|
|
107
|
+
|
|
108
|
+
if (!shouldRewriteCmdToCommand && requiresCmd) {
|
|
109
|
+
return parameters;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Drop Cursor-preferred aliases so normalizeArgKeys can rewrite them to Responses keys.
|
|
113
|
+
delete rawProps.cmd;
|
|
114
|
+
const properties = {
|
|
115
|
+
...CODEX_SHELL_BRIDGE_ARG_NORMALIZE_SCHEMA.properties,
|
|
116
|
+
...rawProps,
|
|
117
|
+
command: rawProps.command ?? CODEX_SHELL_BRIDGE_ARG_NORMALIZE_SCHEMA.properties.command,
|
|
118
|
+
};
|
|
119
|
+
return {
|
|
120
|
+
...base,
|
|
121
|
+
type: "object",
|
|
122
|
+
properties,
|
|
123
|
+
required: requiresCommand ? required : ["command"],
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Required command payload keys for a shell bridge tool, derived from the advertised schema when present.
|
|
129
|
+
*/
|
|
130
|
+
export function shellBridgeRequiredCommandKeys(
|
|
131
|
+
toolName: string,
|
|
132
|
+
schema?: unknown,
|
|
133
|
+
): readonly ("cmd" | "command")[] {
|
|
134
|
+
if (schema && typeof schema === "object") {
|
|
135
|
+
const required = (schema as Record<string, unknown>).required;
|
|
136
|
+
if (Array.isArray(required)) {
|
|
137
|
+
const keys = required.filter((key): key is "cmd" | "command" => key === "cmd" || key === "command");
|
|
138
|
+
if (keys.length > 0) return keys;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return toolName === CODEX_SHELL_COMMAND_TOOL ? ["command"] : ["cmd"];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Normalize-schema defaults used when validating stateless synthetic shell-bridge calls. */
|
|
145
|
+
export function defaultShellBridgeArgNormalizeSchema(toolName: string): unknown {
|
|
146
|
+
return toolName === CODEX_SHELL_COMMAND_TOOL
|
|
147
|
+
? CODEX_SHELL_BRIDGE_ARG_NORMALIZE_SCHEMA
|
|
148
|
+
: {
|
|
149
|
+
type: "object",
|
|
150
|
+
properties: CURSOR_EXEC_COMMAND_INPUT_SCHEMA.properties,
|
|
151
|
+
required: ["cmd"],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function cursorShellBridgeDropError(toolName: string): string {
|
|
156
|
+
return `Cursor emitted ${toolName} without a non-empty command; the tool call was dropped.`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Extract a non-empty shell command from completed Cursor bridge args using the schema's required
|
|
161
|
+
* command key (`cmd` for bare exec_command, `command` for shell_command).
|
|
162
|
+
*/
|
|
163
|
+
export function nonEmptyShellBridgeCommandFromArgs(
|
|
164
|
+
finalArgs: string,
|
|
165
|
+
toolName: string,
|
|
166
|
+
schema?: unknown,
|
|
167
|
+
): string | undefined {
|
|
168
|
+
let parsed: unknown;
|
|
169
|
+
try {
|
|
170
|
+
parsed = finalArgs.length > 0 ? JSON.parse(finalArgs) : {};
|
|
171
|
+
} catch {
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return undefined;
|
|
175
|
+
const record = parsed as Record<string, unknown>;
|
|
176
|
+
const requiredKeys = shellBridgeRequiredCommandKeys(toolName, schema);
|
|
177
|
+
const candidateKeys = new Set<"cmd" | "command">([
|
|
178
|
+
...requiredKeys,
|
|
179
|
+
requiredKeys.includes("cmd") ? "command" : "cmd",
|
|
180
|
+
]);
|
|
181
|
+
for (const key of candidateKeys) {
|
|
182
|
+
const value = record[key];
|
|
183
|
+
if (typeof value === "string" && value.trim().length > 0) return value.trim();
|
|
184
|
+
}
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function cursorShellBridgeArgsValid(
|
|
189
|
+
finalArgs: string,
|
|
190
|
+
toolName: string,
|
|
191
|
+
schema?: unknown,
|
|
192
|
+
): boolean {
|
|
193
|
+
return !isCodexShellBridgeToolName(toolName)
|
|
194
|
+
|| nonEmptyShellBridgeCommandFromArgs(finalArgs, toolName, schema) !== undefined;
|
|
195
|
+
}
|
|
@@ -31,7 +31,7 @@ export interface CursorRunRequest {
|
|
|
31
31
|
/**
|
|
32
32
|
* Corrective active-turn text for the single envelope-echo retry (devlog 260826 gap-10).
|
|
33
33
|
* When set on an external tool-result continuation, buildPreparedCursorRunRequest uses it as
|
|
34
|
-
* the userMessageAction
|
|
34
|
+
* the userMessageAction prefix instead of the standard continuation text; rawMessages stay
|
|
35
35
|
* untouched so history replay is unchanged.
|
|
36
36
|
*/
|
|
37
37
|
echoRetryContinuationText?: string;
|
|
@@ -40,9 +40,12 @@ export interface CursorRunRequest {
|
|
|
40
40
|
messages: CursorRequestMessage[];
|
|
41
41
|
rawMessages?: readonly OcxMessage[];
|
|
42
42
|
/**
|
|
43
|
-
* Images for the active user/developer turn
|
|
43
|
+
* Images for the active user/developer turn or an external model's trailing tool-result run.
|
|
44
|
+
* Encoded as SelectedImage blobIdWithData refs under
|
|
44
45
|
* UserMessage.selected_context (bytes live in the request-scoped KV store for getBlobArgs
|
|
45
|
-
* hydration).
|
|
46
|
+
* hydration). Bounded sourceLabel metadata from tool images is appended to the active action,
|
|
47
|
+
* outside prunable history; it is not a new image wire field. Native Composer selection stays
|
|
48
|
+
* unchanged. History stays text-only. data: URLs only in this slice.
|
|
46
49
|
*/
|
|
47
50
|
selectedImages?: readonly ResolvedCursorImage[];
|
|
48
51
|
tools?: OcxTool[];
|
|
@@ -113,7 +113,7 @@ export const EMPTY_EXEC_OUTPUT_MESSAGE =
|
|
|
113
113
|
* that drifts apart is how a model gets told two different things about the same isolate.
|
|
114
114
|
*/
|
|
115
115
|
export const CODE_MODE_RESULT_ECHO_SENTENCE =
|
|
116
|
-
"Nothing in the isolate is echoed automatically: a bare trailing `await tools.<name>(...)` or final expression value is DISCARDED, and the cell reports empty output. Pass anything you need to read to `text(...)` (or `notify(...)`) in the same cell — for example `text(JSON.stringify(await tools.exec_command({cmd:
|
|
116
|
+
"Nothing in the isolate is echoed automatically: a bare trailing `await tools.<name>(...)` or final expression value is DISCARDED, and the cell reports empty output. Pass anything you need to read to `text(...)` (or `notify(...)`) in the same cell — for example `text(JSON.stringify(await tools.exec_command({cmd: 'ls'})))` — and treat an empty result as your own missing `text(...)` call rather than a failed command or lost context.";
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
119
|
* Codex exec / shell-bridge tool names (flat and MCP-prefixed display aliases). An empty result
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseUpstreamJsonPayload, safeUpstreamErrorString, sanitizeUpstreamErrorText } from "./upstream-http-error";
|
|
2
|
+
import { isLocationUnsupportedMessage } from "../lib/errors";
|
|
2
3
|
|
|
3
4
|
/** Pull the human detail out of the Google API error envelope `{error:{message,status,code}}`. */
|
|
4
5
|
function googleErrorDetail(payloadText: string): { message?: string; status?: string } {
|
|
@@ -65,9 +66,16 @@ function classifyGoogle(label: string, status: number | undefined, enumStatus: s
|
|
|
65
66
|
if (status === 401 || enumStatus === "UNAUTHENTICATED" || lower.includes("unauthenticated") || lower.includes("invalid authentication") || lower.includes("expired")) {
|
|
66
67
|
return `${label} authentication failed`;
|
|
67
68
|
}
|
|
68
|
-
if (status === 403 || enumStatus === "PERMISSION_DENIED" || lower.includes("permission denied") || lower.includes("access denied")) {
|
|
69
|
+
if (status === 403 || enumStatus === "PERMISSION_DENIED" || lower.includes("permission_denied") || lower.includes("permission denied") || lower.includes("access denied")) {
|
|
69
70
|
return `${label} access denied`;
|
|
70
71
|
}
|
|
72
|
+
// Google rejects unsupported geographic / datacenter locations with HTTP 400
|
|
73
|
+
// FAILED_PRECONDITION. The payload is not malformed, so it must not fall through to
|
|
74
|
+
// "invalid request" (#3467). Only the observed 400/precondition envelope permits
|
|
75
|
+
// this inference; other explicit enums and server statuses remain authoritative.
|
|
76
|
+
if (status === 400 && (!enumStatus || enumStatus === "FAILED_PRECONDITION") && isLocationUnsupportedMessage(lower)) {
|
|
77
|
+
return `${label} location not supported`;
|
|
78
|
+
}
|
|
71
79
|
if (status === 503 || enumStatus === "UNAVAILABLE" || lower.includes("overloaded") || lower.includes("unavailable")) {
|
|
72
80
|
return `${label} server overloaded`;
|
|
73
81
|
}
|
package/src/adapters/google.ts
CHANGED
|
@@ -52,6 +52,7 @@ const GOOGLE_BREVITY_INSTRUCTION = [
|
|
|
52
52
|
"- Do detailed reasoning internally, not as visible intermediate output.",
|
|
53
53
|
"- Prefer taking the next tool action over explaining; keep calling tools until the task is complete.",
|
|
54
54
|
"- This applies only to intermediate progress text. Your final answer after the work is done is exempt: write it in full and at whatever length the task requires.",
|
|
55
|
+
"- Formatting: The client environment renders standard Markdown and does not support LaTeX math delimiters ($...$, $$...$$, \\(...\\), \\[...\\]). Do not use LaTeX math delimiters or LaTeX markup (such as \\text{}, \\times, \\le, \\ge, etc.) for variables, formulas, dimensions, or units. Use clean plain text, Markdown, and Unicode symbols (e.g. 180°, 2560 × 1920 px, ≤, ≥, Δ, ±) instead.",
|
|
55
56
|
].join("\n");
|
|
56
57
|
|
|
57
58
|
const ANTIGRAVITY_REJECTED_CLAUDE_SDK_PARAGRAPH =
|
package/src/adapters/identity.ts
CHANGED
|
@@ -22,11 +22,17 @@ export const CODEX_GPT5_IDENTITY_LINE = "You are Codex, a coding agent based on
|
|
|
22
22
|
export const CODEX_GPT5_IDENTITY_LINE_AGENT = "You are Codex, an agent based on GPT-5.";
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* Known Codex
|
|
25
|
+
* Known Codex identity sentences. Narrow: only "coding agent" / "an agent" + GPT-<major>(.minor)*.
|
|
26
26
|
* Avoid a broad `You are Codex.*` rewrite that could touch unrelated content.
|
|
27
|
+
*
|
|
28
|
+
* The major version is a wildcard because Codex writes the CURRENT generation into this line and
|
|
29
|
+
* bumps it: `gpt-6-astra` (upstream #42607) ships "You are Codex, an agent based on GPT-6.".
|
|
30
|
+
* Pinning `GPT-5` meant a GPT-6-era prompt routed to a third-party provider kept telling that
|
|
31
|
+
* model it was Codex-on-GPT-6 — the exact misattribution this chokepoint exists to remove, silently
|
|
32
|
+
* reintroduced by a version bump.
|
|
27
33
|
*/
|
|
28
34
|
const CODEX_GPT5_IDENTITY_RE =
|
|
29
|
-
/You are Codex, (?:a coding agent|an agent) based on GPT-
|
|
35
|
+
/You are Codex, (?:a coding agent|an agent) based on GPT-[0-9]+(?:\.[0-9]+)*\./g;
|
|
30
36
|
|
|
31
37
|
/** Proxy-neutral replacement: no "opencodex proxy" mention, just the GPT-5/OpenAI disclaimer. */
|
|
32
38
|
export const NEUTRAL_IDENTITY_LINE = "You are a coding agent. Do not claim to be GPT-5 or to be made by OpenAI.";
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-conversation calibration of the Kiro input estimate.
|
|
3
|
+
*
|
|
4
|
+
* The estimator in `lib/token-estimate` is a fixed heuristic: constants derived from recorded
|
|
5
|
+
* traffic, applied to every account and every conversation identically. It is close on average
|
|
6
|
+
* and necessarily wrong in the particular, because how densely a prompt tokenizes depends on
|
|
7
|
+
* what is in it — a Korean design discussion and a repository of minified JSON do not share a
|
|
8
|
+
* ratio, and no constant can serve both.
|
|
9
|
+
*
|
|
10
|
+
* Kiro, however, tells us the answer. Mid-stream it reports `contextUsagePercentage`, which
|
|
11
|
+
* against a known window is an authoritative token count for the exact payload just sent. Today
|
|
12
|
+
* that number is used once, as a floor under the current turn's total, and then discarded. The
|
|
13
|
+
* conversation's own measured tokens-per-character is therefore recomputed from scratch, and
|
|
14
|
+
* mispredicted the same way, on every single turn.
|
|
15
|
+
*
|
|
16
|
+
* This module keeps it. After a TERMINAL attempt that produced both an estimate and a reported
|
|
17
|
+
* percentage, the realised ratio `charged / estimated` is folded into a per-conversation
|
|
18
|
+
* correction and applied to the next turn of that conversation, so a long conversation converges
|
|
19
|
+
* on the rate it actually exhibits rather than the rate the average conversation exhibits.
|
|
20
|
+
*
|
|
21
|
+
* "Terminal" matters: the adapter's bounded completion retry streams a second time for the same
|
|
22
|
+
* user turn against a rebuilt payload, so the caller commits an observation only once that turn
|
|
23
|
+
* is genuinely over.
|
|
24
|
+
*
|
|
25
|
+
* Four properties keep this from being able to make the gauge worse:
|
|
26
|
+
*
|
|
27
|
+
* - **Bounded.** The factor is clamped, so a single anomalous reading cannot distort the
|
|
28
|
+
* estimate by an unbounded amount, and a malformed or hostile percentage cannot either.
|
|
29
|
+
* - **Smoothed.** Each observation moves the factor part of the way rather than replacing it, so
|
|
30
|
+
* one cache-affected or truncated turn cannot swing the gauge.
|
|
31
|
+
* - **Bounded in memory.** Conversation-scoped with an eviction cap and no persistence. This is
|
|
32
|
+
* a hint that improves with use, not state anything depends on.
|
|
33
|
+
* - **Subordinate to the truth.** The existing upstream floor is untouched. Calibration only
|
|
34
|
+
* sharpens the estimate BEFORE upstream reports; it can never lower a value upstream has
|
|
35
|
+
* already justified.
|
|
36
|
+
*
|
|
37
|
+
* Deliberately not in `lib/token-estimate`: that module is pure, shared by every provider, and
|
|
38
|
+
* must stay so. This is Kiro-specific state and lives with the Kiro adapter.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Clamp for the correction factor.
|
|
43
|
+
*
|
|
44
|
+
* Wide enough to absorb the genuine spread between conversation kinds, narrow enough that a
|
|
45
|
+
* wrong reading cannot produce a nonsensical gauge. A factor below 1 shrinks the estimate, so
|
|
46
|
+
* the lower bound is the more dangerous side and is kept nearer 1.
|
|
47
|
+
*/
|
|
48
|
+
const MIN_FACTOR = 0.7;
|
|
49
|
+
const MAX_FACTOR = 3;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Weight given to a new observation. 0.35 reaches most of the way to a persistent new rate
|
|
53
|
+
* within a few turns while still requiring more than one reading to move far.
|
|
54
|
+
*/
|
|
55
|
+
const SMOOTHING = 0.35;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Maximum conversations tracked. Entries are small, but the map must not grow with uptime.
|
|
59
|
+
*
|
|
60
|
+
* Eviction is least-recently-USED, not oldest-inserted: `touch` re-inserts on every estimate and
|
|
61
|
+
* every observation, so an active long conversation survives an arbitrary number of short ones
|
|
62
|
+
* started after it. Insertion order would have evicted exactly the conversation most worth
|
|
63
|
+
* keeping.
|
|
64
|
+
*/
|
|
65
|
+
const MAX_TRACKED_CONVERSATIONS = 256;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* A ratio this far from 1 is not a mis-calibrated estimate, it is a different measurement:
|
|
69
|
+
* a compaction, a cache boundary, or a percentage reported against a window we did not expect.
|
|
70
|
+
* Learning from it would teach the wrong lesson, so it is ignored entirely.
|
|
71
|
+
*/
|
|
72
|
+
const MAX_PLAUSIBLE_OBSERVATION = 6;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* One conversation's calibration state.
|
|
76
|
+
*
|
|
77
|
+
* `factor` is the learned correction. `rawEstimate` is the uncorrected estimate most recently
|
|
78
|
+
* applied: the stream records what upstream charged, but by then the value it holds has already
|
|
79
|
+
* been corrected, and learning from that would make the factor measure its own residual error
|
|
80
|
+
* instead of the heuristic's — each round would close only the part it had not yet fixed and the
|
|
81
|
+
* factor would converge short of the true ratio.
|
|
82
|
+
*
|
|
83
|
+
* Both live in ONE entry so they cannot desynchronise. Held as two maps, a conversation could be
|
|
84
|
+
* refreshed in one and evicted from the other, and the pair could retain twice the documented
|
|
85
|
+
* bound in distinct ids.
|
|
86
|
+
*/
|
|
87
|
+
interface Calibration {
|
|
88
|
+
factor?: number;
|
|
89
|
+
rawEstimate?: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Insertion-ordered, so the first key is the least recently touched entry. */
|
|
93
|
+
const calibrations = new Map<string, Calibration>();
|
|
94
|
+
|
|
95
|
+
/** Re-insert to move an entry to the most-recently-used end, then evict from the old end. */
|
|
96
|
+
function touch(conversationId: string, update: (current: Calibration) => Calibration): void {
|
|
97
|
+
const current = calibrations.get(conversationId) ?? {};
|
|
98
|
+
calibrations.delete(conversationId);
|
|
99
|
+
calibrations.set(conversationId, update(current));
|
|
100
|
+
while (calibrations.size > MAX_TRACKED_CONVERSATIONS) {
|
|
101
|
+
const oldest = calibrations.keys().next();
|
|
102
|
+
if (oldest.done) break;
|
|
103
|
+
calibrations.delete(oldest.value);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function clamp(value: number): number {
|
|
108
|
+
return Math.min(MAX_FACTOR, Math.max(MIN_FACTOR, value));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Record what a conversation was actually charged against what we estimated.
|
|
113
|
+
*
|
|
114
|
+
* `estimated` must be the RAW estimate — what the fixed heuristic produced before any correction
|
|
115
|
+
* was applied — and `charged` the token count implied by the upstream percentage. Passing the
|
|
116
|
+
* already-corrected value instead would make the factor measure its own residual error rather
|
|
117
|
+
* than the heuristic's, so each round would learn only the part it had not yet fixed and the
|
|
118
|
+
* factor would settle short of the true ratio, permanently under-correcting.
|
|
119
|
+
*
|
|
120
|
+
* Non-finite, non-positive, or implausible inputs are ignored rather than clamped, because a bad
|
|
121
|
+
* reading carries no information worth smoothing in.
|
|
122
|
+
*/
|
|
123
|
+
export function recordKiroCalibration(
|
|
124
|
+
conversationId: string | undefined,
|
|
125
|
+
estimated: number,
|
|
126
|
+
charged: number,
|
|
127
|
+
): void {
|
|
128
|
+
if (!conversationId) return;
|
|
129
|
+
if (!Number.isFinite(estimated) || !Number.isFinite(charged)) return;
|
|
130
|
+
if (estimated <= 0 || charged <= 0) return;
|
|
131
|
+
const entry = calibrations.get(conversationId);
|
|
132
|
+
// Prefer the raw estimate recorded at build time; fall back to the caller's value when this
|
|
133
|
+
// conversation was never seen by `calibrateKiroEstimate` (a first turn, or a rebuilt retry).
|
|
134
|
+
const baseline = entry?.rawEstimate ?? estimated;
|
|
135
|
+
if (!Number.isFinite(baseline) || baseline <= 0) return;
|
|
136
|
+
const observed = charged / baseline;
|
|
137
|
+
if (!Number.isFinite(observed) || observed <= 0 || observed > MAX_PLAUSIBLE_OBSERVATION) return;
|
|
138
|
+
|
|
139
|
+
// Smooth from 1 (the "no correction" prior) on the FIRST observation too. Jumping straight to
|
|
140
|
+
// `clamp(observed)` would let a single cache-affected or truncated turn set the factor outright,
|
|
141
|
+
// which is exactly the swing the smoothing exists to prevent.
|
|
142
|
+
const previous = entry?.factor ?? 1;
|
|
143
|
+
const next = clamp(previous + (observed - previous) * SMOOTHING);
|
|
144
|
+
|
|
145
|
+
// The observation for this turn is consumed: dropping the raw estimate keeps a later record for
|
|
146
|
+
// the same conversation from being scored a second time against a stale baseline, which would
|
|
147
|
+
// re-learn the same correction from a payload that has since grown.
|
|
148
|
+
touch(conversationId, () => ({ factor: next }));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Apply a conversation's learned correction to an estimate. Unknown conversations are returned
|
|
153
|
+
* unchanged, so the first turn behaves exactly as it did before calibration existed.
|
|
154
|
+
*/
|
|
155
|
+
export function calibrateKiroEstimate(conversationId: string | undefined, estimate: number): number {
|
|
156
|
+
if (!conversationId || !Number.isFinite(estimate) || estimate <= 0) return estimate;
|
|
157
|
+
touch(conversationId, current => ({ ...current, rawEstimate: estimate }));
|
|
158
|
+
const factor = calibrations.get(conversationId)?.factor;
|
|
159
|
+
return factor === undefined ? estimate : Math.ceil(estimate * factor);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Carry a conversation's state to the id upstream actually returned.
|
|
164
|
+
*
|
|
165
|
+
* The estimate is stored under the id the request was BUILT with, but Kiro can answer with a
|
|
166
|
+
* different conversation id, and the stream then records under that one. Without this the record
|
|
167
|
+
* would miss its own raw estimate and silently fall back to the corrected value — the exact
|
|
168
|
+
* feedback bug the raw estimate exists to avoid.
|
|
169
|
+
*/
|
|
170
|
+
export function rekeyKiroCalibration(fromConversationId: string | undefined, toConversationId: string | undefined): void {
|
|
171
|
+
if (!fromConversationId || !toConversationId || fromConversationId === toConversationId) return;
|
|
172
|
+
const entry = calibrations.get(fromConversationId);
|
|
173
|
+
if (!entry) return;
|
|
174
|
+
calibrations.delete(fromConversationId);
|
|
175
|
+
touch(toConversationId, current => ({ ...current, ...entry }));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Test seam: drop all learned state. */
|
|
179
|
+
export function resetKiroCalibration(): void {
|
|
180
|
+
calibrations.clear();
|
|
181
|
+
}
|