@bitkyc08/opencodex 2.83.0 → 2.86.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 +0 -4
- package/bin/ocx.mjs +61 -3
- package/gui/dist/assets/App-BjlttVLy.js +58 -0
- package/gui/dist/assets/Tray-DyR9yBee.js +1 -0
- package/gui/dist/assets/{index-BAbcj51J.css → index-BB6uK6tU.css} +1 -1
- package/gui/dist/assets/index-BMssP4k3.js +94 -0
- package/gui/dist/assets/quota-summary-D8M1bc_R.js +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +2 -3
- package/src/adapters/anthropic/beta-allowlist.ts +87 -8
- package/src/adapters/anthropic/client-identity.ts +1 -1
- package/src/adapters/anthropic/passthrough.ts +33 -4
- package/src/adapters/azure.ts +19 -1
- package/src/adapters/client-fingerprint.ts +24 -0
- package/src/adapters/coding-agent/protocol.ts +15 -8
- package/src/adapters/coding-agent/turn.ts +22 -9
- package/src/adapters/cursor/arg-codec.ts +17 -5
- package/src/adapters/cursor/catalog.ts +8 -0
- package/src/adapters/cursor/effort-map.ts +3 -0
- package/src/adapters/cursor/freeform-args.ts +75 -0
- package/src/adapters/cursor/protobuf-events.ts +36 -23
- package/src/adapters/google-antigravity-wire.ts +22 -1
- package/src/adapters/google.ts +2 -2
- package/src/adapters/ollama-native.ts +29 -1
- package/src/adapters/openai-chat/messages.ts +8 -4
- package/src/adapters/openai-chat/response-events.ts +59 -1
- package/src/adapters/openai-chat.ts +8 -5
- package/src/adapters/openai-responses/empty-assistant.ts +32 -0
- package/src/adapters/openai-responses/passthrough.ts +3 -0
- package/src/adapters/openai-responses/request-strips.ts +10 -2
- package/src/adapters/qoder/adapter.ts +56 -15
- package/src/advisor/consult.ts +220 -0
- package/src/advisor/context.ts +226 -0
- package/src/advisor/disclosure.ts +20 -0
- package/src/advisor/runtime.ts +305 -0
- package/src/advisor/settings.ts +135 -0
- package/src/advisor/state.ts +369 -0
- package/src/advisor/synthetic-tool.ts +33 -0
- package/src/bridge/errors.ts +24 -0
- package/src/bridge.ts +1 -1
- package/src/chat/inbound.ts +30 -6
- package/src/chat/outbound.ts +5 -0
- package/src/claude/context-windows.ts +54 -1
- package/src/claude/desktop-3p-library.ts +43 -3
- package/src/claude/desktop-3p.ts +22 -8
- package/src/claude/desktop-picker-profile.ts +11 -4
- package/src/claude/desktop-profile.ts +10 -1
- package/src/claude/desktop-remote-store-io.ts +5 -3
- package/src/claude/inbound.ts +121 -50
- package/src/claude/intercept/connect-proxy.ts +10 -3
- package/src/claude/intercept/local-ca.ts +8 -1
- package/src/claude/intercept/picker-ca-persistence.ts +3 -1
- package/src/claude/intercept/picker-listener.ts +49 -1
- package/src/claude/intercept/settings-no-proxy.ts +146 -0
- package/src/claude/intercept/settings.ts +101 -9
- package/src/claude/model-default-settings.ts +154 -0
- package/src/claude/model-info.ts +7 -7
- package/src/claude/outbound.ts +13 -6
- package/src/cli/account-api.ts +65 -0
- package/src/cli/account-auth.ts +26 -2
- package/src/cli/account.ts +41 -5
- package/src/cli/advisor.ts +151 -0
- package/src/cli/auxiliary-listener-preflight.ts +37 -0
- package/src/cli/capabilities-access-remote.ts +8 -0
- package/src/cli/capabilities-agents-routing.ts +18 -0
- package/src/cli/capabilities-base.ts +14 -2
- package/src/cli/catalog-command-result.ts +2 -1
- package/src/cli/chatgpt-command.ts +35 -14
- package/src/cli/claude.ts +60 -11
- package/src/cli/config-command.ts +8 -2
- package/src/cli/debug.ts +8 -0
- package/src/cli/dispatch.ts +21 -4
- package/src/cli/doctor.ts +9 -0
- package/src/cli/ensure-desktop-cold-start.ts +329 -0
- package/src/cli/export-command.ts +15 -2
- package/src/cli/help.ts +2 -1
- package/src/cli/hub-disable.ts +99 -0
- package/src/cli/hub.ts +4 -2
- package/src/cli/index.ts +26 -8
- package/src/cli/integration-plan-dto.ts +2 -0
- package/src/cli/link-child.ts +104 -0
- package/src/cli/link-enrollment.ts +96 -0
- package/src/cli/link.ts +39 -24
- package/src/cli/observe-stream.ts +6 -1
- package/src/cli/observe.ts +10 -2
- package/src/cli/provider.ts +12 -0
- package/src/cli/registry.ts +20 -7
- package/src/cli/restart-scope.ts +3 -1
- package/src/cli/runtime-api.ts +27 -2
- package/src/cli/status.ts +104 -20
- package/src/client/connect.ts +39 -4
- package/src/client/hub-client.ts +14 -7
- package/src/client/hub-state.ts +25 -14
- package/src/client/link-home.ts +248 -0
- package/src/client/link-management-relay.ts +61 -0
- package/src/client/link-relay.ts +6 -1
- package/src/client/link-status.ts +14 -0
- package/src/client/machine-api.ts +14 -7
- package/src/client/machine-listener.ts +29 -4
- package/src/clients/config-export/contracts.ts +1 -0
- package/src/clients/config-export/model-metadata.ts +3 -2
- package/src/clients/config-export/qoder.ts +93 -0
- package/src/clients/config-export.ts +59 -16
- package/src/clients/omo-role-models.ts +6 -3
- package/src/codex/agent-role-models.ts +26 -8
- package/src/codex/auth-api/account-list.ts +17 -2
- package/src/codex/auth-api/account-selection.ts +17 -7
- package/src/codex/auth-api/pool-mode-gate.ts +5 -2
- package/src/codex/auth-api/routes.ts +5 -1
- package/src/codex/auth-context.ts +79 -5
- package/src/codex/autostart-health.ts +7 -1
- package/src/codex/catalog/build-entries.ts +11 -2
- package/src/codex/catalog/bundled.ts +5 -1
- package/src/codex/catalog/configured-tiers.ts +50 -0
- package/src/codex/catalog/effort.ts +6 -3
- package/src/codex/catalog/metadata.ts +4 -2
- package/src/codex/catalog/model-hints.ts +21 -3
- package/src/codex/catalog/native-models.ts +16 -2
- package/src/codex/catalog/native-ultrafast.ts +106 -0
- package/src/codex/catalog/parsing.ts +5 -0
- package/src/codex/catalog/provider-models.ts +4 -0
- package/src/codex/catalog/retained-sync.ts +2 -0
- package/src/codex/catalog/routed-gather.ts +22 -4
- package/src/codex/catalog/vendor-context.ts +82 -0
- package/src/codex/config-write-lock.ts +40 -2
- package/src/codex/convergence.ts +2 -0
- package/src/codex/desktop-app/types.ts +2 -0
- package/src/codex/desktop-app/windows.ts +29 -16
- package/src/codex/desktop-app-restart.ts +4 -2
- package/src/codex/desktop-compatibility/certificate-service.ts +99 -0
- package/src/codex/desktop-compatibility/certificate-store.ts +193 -0
- package/src/codex/desktop-compatibility/connection-store.ts +87 -0
- package/src/codex/desktop-compatibility/installed-build.ts +55 -0
- package/src/codex/desktop-compatibility/json-body.ts +29 -0
- package/src/codex/desktop-compatibility/native-identity.ts +62 -0
- package/src/codex/desktop-compatibility/relay-listener.ts +93 -0
- package/src/codex/desktop-compatibility/routing-binding.ts +10 -0
- package/src/codex/desktop-compatibility/routing-preflight.ts +69 -0
- package/src/codex/desktop-compatibility/runtime-ownership.ts +28 -0
- package/src/codex/desktop-compatibility/runtime.ts +173 -0
- package/src/codex/desktop-compatibility/service.ts +30 -0
- package/src/codex/desktop-compatibility/startup-settings.ts +50 -0
- package/src/codex/desktop-compatibility/usage-activation.ts +86 -0
- package/src/codex/desktop-compatibility/usage-controlled-fetch.ts +59 -0
- package/src/codex/desktop-compatibility/usage-controller.ts +107 -0
- package/src/codex/desktop-compatibility/usage-policy.ts +59 -0
- package/src/codex/desktop-compatibility/usage-refresh.ts +33 -0
- package/src/codex/desktop-compatibility/usage-sse-controller.ts +86 -0
- package/src/codex/desktop-compatibility/windows-activation-source.ts +44 -0
- package/src/codex/desktop-compatibility/windows-certificate-trust.ts +81 -0
- package/src/codex/desktop-compatibility/windows-key-protection.ts +59 -0
- package/src/codex/desktop-compatibility/windows-package-command.ts +86 -0
- package/src/codex/desktop-compatibility/windows-package-launch.ts +28 -0
- package/src/codex/history-provider.ts +1 -1
- package/src/codex/inject/config-write-section.ts +4 -1
- package/src/codex/inject/remove.ts +18 -6
- package/src/codex/journal.ts +16 -1
- package/src/codex/main-account.ts +99 -15
- package/src/codex/native-main-refresh-mode.ts +217 -0
- package/src/codex/native-profile-startup.ts +4 -1
- package/src/codex/prompt-journal.ts +20 -2
- package/src/codex/prompt-layers/encoding.ts +49 -0
- package/src/codex/prompt-layers/import-source.ts +30 -0
- package/src/codex/prompt-layers/toml-edit.ts +269 -37
- package/src/codex/prompt-layers/toml-read.ts +52 -4
- package/src/codex/prompt-layers.ts +383 -20
- package/src/codex/prompt-lock-claim.ts +13 -3
- package/src/codex/prompt-lock-evidence.ts +3 -3
- package/src/codex/prompt-lock-owner.ts +14 -6
- package/src/codex/prompt-text-probe.ts +34 -5
- package/src/codex/quota.ts +13 -6
- package/src/codex/role-auto-assign.ts +2 -5
- package/src/codex/routing/active-account.ts +21 -0
- package/src/codex/routing/health-store.ts +1 -1
- package/src/codex/routing/pool-recovery.ts +79 -0
- package/src/codex/routing.ts +1 -1
- package/src/codex/runtime.ts +196 -8
- package/src/codex/shim-diagnostics.ts +6 -0
- package/src/codex/shim-overlay.ts +15 -6
- package/src/codex/shim.ts +1 -1
- package/src/combos/failover.ts +3 -0
- package/src/config/api-key-reconcile.ts +34 -0
- package/src/config/diagnostics.ts +18 -1
- package/src/config/feature-flags.ts +5 -0
- package/src/config/live-reconcile.ts +56 -29
- package/src/config/live-topology.ts +29 -0
- package/src/config/load-degrade.ts +8 -2
- package/src/config/paths.ts +13 -3
- package/src/config/salvage.ts +3 -1
- package/src/config/schema/config-schema.ts +19 -0
- package/src/config/schema/desktop-compatibility.ts +9 -0
- package/src/config/schema/leaf-validators.ts +1 -0
- package/src/generated/compatibility-version.json +713 -285
- package/src/generated/model-metadata.ts +1 -1
- package/src/grok/grpc-web.ts +23 -1
- package/src/grok/reset-coupon-ledger.ts +75 -5
- package/src/grok/reset-coupons.ts +4 -2
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/config-io.ts +20 -2
- package/src/integrations/journal.ts +15 -1
- package/src/integrations/mutation-plan.ts +14 -7
- package/src/integrations/registry.ts +52 -11
- package/src/integrations/state.ts +19 -7
- package/src/integrations/store.ts +3 -0
- package/src/integrations/target.ts +42 -5
- package/src/integrations/writer.ts +24 -7
- package/src/lib/advisor-activation.ts +16 -0
- package/src/lib/app-owned-memory-stores.ts +4 -0
- package/src/lib/app-owned-memory.ts +11 -0
- package/src/lib/background-work.ts +65 -0
- package/src/lib/bounded-file-read.ts +58 -0
- package/src/lib/bun-path-runtime.d.mts +10 -1
- package/src/lib/bun-path-runtime.mjs +62 -3
- package/src/lib/desktop-proxy-route.ts +30 -0
- package/src/lib/desktop-upstream-tunnel.ts +107 -0
- package/src/lib/errors.ts +7 -0
- package/src/lib/json-byte-size.ts +31 -0
- package/src/lib/local-internal-call-capability.ts +55 -0
- package/src/lib/optional-shutdown-hooks.ts +35 -0
- package/src/lib/outbound-credential-registry.ts +210 -0
- package/src/lib/package-tree-integrity.ts +2 -2
- package/src/lib/passive-read.ts +35 -0
- package/src/lib/shadow-call.ts +44 -5
- package/src/lib/socks5-fetch.ts +48 -71
- package/src/lib/socks5-handshake.ts +112 -0
- package/src/lib/translator-budget.ts +7 -1
- package/src/link/codex-settings.ts +155 -0
- package/src/link/management-routes.ts +89 -0
- package/src/link/remote-probe.ts +38 -0
- package/src/link/ssh-argv.ts +26 -5
- package/src/link/ssh-runner.ts +58 -27
- package/src/link/status-projection.ts +22 -4
- package/src/link/supervisor.ts +144 -49
- package/src/link/tunnel-state.ts +11 -4
- package/src/oauth/anthropic-continuity.ts +4 -3
- package/src/oauth/anthropic-identity.ts +13 -2
- package/src/oauth/anthropic-model-quota.ts +13 -1
- package/src/oauth/anthropic-routing.ts +230 -23
- package/src/oauth/anthropic.ts +18 -4
- package/src/oauth/generic-account-failover.ts +29 -5
- package/src/oauth/google-antigravity.ts +24 -7
- package/src/oauth/index.ts +28 -17
- package/src/oauth/kiro-account-identity.ts +106 -0
- package/src/oauth/kiro-device-login.ts +2 -1
- package/src/oauth/kiro.ts +55 -6
- package/src/oauth/plan.ts +29 -0
- package/src/oauth/store.ts +40 -3
- package/src/oauth/types.ts +2 -0
- package/src/plugins/loader.ts +11 -0
- package/src/protocols/codecs/chat-ir.ts +467 -0
- package/src/protocols/codecs/chat.ts +10 -1
- package/src/protocols/codecs/messages-ir.ts +500 -0
- package/src/protocols/features.ts +10 -2
- package/src/protocols/path.ts +11 -1
- package/src/protocols/plan-snapshot.ts +39 -16
- package/src/protocols/plan.ts +9 -1
- package/src/protocols/settings.ts +8 -0
- package/src/providers/anthropic-billing-ownership.ts +74 -0
- package/src/providers/anthropic-billing-recovery.ts +31 -0
- package/src/providers/anthropic-billing-refusal.ts +55 -0
- package/src/providers/anthropic-reset-admission.ts +62 -0
- package/src/providers/anthropic-reset-grant-ledger.ts +28 -6
- package/src/providers/antigravity-account-roster.ts +102 -0
- package/src/providers/antigravity-models.ts +85 -7
- package/src/providers/api-key-resolve.ts +17 -2
- package/src/providers/key-failover.ts +52 -5
- package/src/providers/model-rename-fields.ts +2 -0
- package/src/providers/model-rename-migration.ts +5 -3
- package/src/providers/model-rename-startup.ts +5 -3
- package/src/providers/quota/account-cache.ts +11 -0
- package/src/providers/quota/anthropic-account-quota.ts +4 -2
- package/src/providers/quota/report-cache.ts +14 -0
- package/src/providers/quota/vendor-probes-oauth.ts +3 -3
- package/src/providers/quota.ts +4 -1
- package/src/providers/registry/entries-core.ts +21 -20
- package/src/providers/registry/entries-extended.ts +19 -1
- package/src/providers/registry/model-seeds.ts +17 -0
- package/src/providers/request-pacing.ts +9 -0
- package/src/providers/stale-no-vision-seed-migration.ts +72 -0
- package/src/responses/compaction.ts +14 -0
- package/src/responses/parser.ts +37 -20
- package/src/responses/state.ts +32 -29
- package/src/routing/history/build-file.ts +92 -0
- package/src/routing/history/indexer.ts +248 -216
- package/src/routing/history/ingest.ts +238 -0
- package/src/routing/history/rebuild-job.ts +111 -0
- package/src/routing/history/rebuild-protocol.ts +15 -0
- package/src/routing/history/rebuild-worker.ts +34 -0
- package/src/server/admission-model-scope.ts +4 -1
- package/src/server/antigravity-direct-eligibility.ts +208 -0
- package/src/server/auth-cors.ts +46 -0
- package/src/server/chat-completions.ts +75 -9
- package/src/server/claude-messages.ts +110 -20
- package/src/server/configured-credentials.ts +194 -0
- package/src/server/index/desktop-compatibility-startup.ts +56 -0
- package/src/server/index/link-management.ts +58 -0
- package/src/server/index/optional-listeners.ts +5 -1
- package/src/server/index/serve-options.ts +15 -2
- package/src/server/index/startup-warnings.ts +9 -0
- package/src/server/index/websocket-handler.ts +9 -0
- package/src/server/index.ts +13 -10
- package/src/server/inference/client-encoder-delivery.ts +6 -5
- package/src/server/lifecycle.ts +2 -1
- package/src/server/login-shell-path.ts +146 -0
- package/src/server/management/advisor-routes.ts +191 -0
- package/src/server/management/agent-settings-routes.ts +50 -14
- package/src/server/management/anthropic-reset-grant-routes.ts +17 -1
- package/src/server/management/codex-agent-role-routes.ts +19 -6
- package/src/server/management/codex-prompt-routes.ts +136 -5
- package/src/server/management/codex-role-auto-assign.ts +1 -1
- package/src/server/management/companion-routes.ts +4 -0
- package/src/server/management/config-routes.ts +2 -2
- package/src/server/management/context.ts +4 -0
- package/src/server/management/desktop-compatibility-routes.ts +46 -0
- package/src/server/management/desktop-compatibility-runtime-routes.ts +46 -0
- package/src/server/management/desktop-compatibility-settings-routes.ts +27 -0
- package/src/server/management/grok-coupon-routes.ts +78 -27
- package/src/server/management/link-enrollment.ts +192 -0
- package/src/server/management/link-route-state.ts +185 -0
- package/src/server/management/link-routes.ts +82 -402
- package/src/server/management/model-routes.ts +2 -2
- package/src/server/management/model-rows.ts +3 -1
- package/src/server/management/protocol-settings-patch.ts +1 -0
- package/src/server/management/provider-overwrite-carry.ts +10 -0
- package/src/server/management/provider-routes.ts +1 -0
- package/src/server/management/route-registry.ts +16 -3
- package/src/server/management/shared.ts +3 -0
- package/src/server/management/sibling-guard.ts +1 -0
- package/src/server/management-api.ts +9 -1
- package/src/server/messages-antigravity-direct.ts +195 -0
- package/src/server/messages-native.ts +73 -43
- package/src/server/relay-eager.ts +2 -0
- package/src/server/request-log-filter.ts +6 -1
- package/src/server/request-log-terminal-status.ts +63 -6
- package/src/server/request-log.ts +7 -0
- package/src/server/responses/adapter-continuation.ts +35 -18
- package/src/server/responses/adapter-delivery.ts +24 -2
- package/src/server/responses/adapter-dispatch.ts +261 -81
- package/src/server/responses/advisor-plan-slot.ts +27 -0
- package/src/server/responses/advisor-slot.ts +336 -0
- package/src/server/responses/code-mode-collab-surface.ts +313 -0
- package/src/server/responses/codex-ws-exchange.ts +147 -6
- package/src/server/responses/codex-ws-wire.ts +203 -0
- package/src/server/responses/collaboration.ts +36 -7
- package/src/server/responses/compact.ts +126 -48
- package/src/server/responses/context-overflow.ts +13 -1
- package/src/server/responses/core-codex-account.ts +105 -1
- package/src/server/responses/core-combo-failure.ts +24 -1
- package/src/server/responses/core-combo.ts +28 -2
- package/src/server/responses/core-errors.ts +29 -1
- package/src/server/responses/core-lifetime.ts +2 -0
- package/src/server/responses/core-normalize.ts +11 -0
- package/src/server/responses/core-options.ts +48 -0
- package/src/server/responses/core-replay.ts +7 -4
- package/src/server/responses/core.ts +1 -1
- package/src/server/responses/deferred-collab-surface.ts +158 -0
- package/src/server/responses/deferred-send-reporter.ts +25 -0
- package/src/server/responses/fetch-helpers.ts +12 -1
- package/src/server/responses/passthrough-delivery.ts +20 -11
- package/src/server/responses/passthrough-dispatch.ts +29 -7
- package/src/server/responses/request-prepare.ts +74 -9
- package/src/server/responses/request-transport.ts +22 -8
- package/src/server/responses/run-turn-execution.ts +70 -8
- package/src/server/responses/sidecar-execution.ts +89 -5
- package/src/server/responses/terminal-guard.ts +2 -1
- package/src/server/responses/ws-upstream.ts +9 -1
- package/src/server/responses-undeclared-tool-guard.ts +30 -9
- package/src/server/startup-action-control.ts +38 -4
- package/src/server/startup-health-cache.ts +5 -0
- package/src/server/system-env-shell.ts +7 -2
- package/src/server/system-env.ts +11 -1
- package/src/service/cli.ts +5 -1
- package/src/service/desktop-startup.ts +161 -34
- package/src/service/desktop-supervision.d.mts +27 -1
- package/src/service/desktop-supervision.mjs +111 -5
- package/src/service/health-sentinel-launch.ts +176 -0
- package/src/service/health-sentinel-main.ts +466 -0
- package/src/service/health-sentinel-policy.ts +241 -0
- package/src/service/health-sentinel-probe.ts +71 -0
- package/src/service/health-sentinel-windows.ts +87 -0
- package/src/service/linux-linger.ts +20 -0
- package/src/service/logging.ts +72 -0
- package/src/tray/windows-tray.ps1 +43 -24
- package/src/types/config.ts +68 -6
- package/src/types/provider.ts +7 -0
- package/src/types/request.ts +15 -0
- package/src/types/tools.ts +2 -0
- package/src/update/async-check.ts +1 -1
- package/src/update/badge.ts +2 -2
- package/src/update/index.ts +88 -15
- package/src/update/job-supervised-restart.ts +107 -0
- package/src/update/job.ts +32 -32
- package/src/update/notify.ts +1 -1
- package/src/update/refresh-scheduler.ts +2 -2
- package/src/update/supervised-recovery.d.mts +27 -0
- package/src/update/supervised-recovery.mjs +157 -0
- package/src/usage/cache-diagnostic.ts +2 -0
- package/src/usage/expected-prices.ts +16 -0
- package/src/usage/log.ts +19 -1
- package/src/web-search/gemini-executor.ts +2 -2
- package/assets/sponsors/packycode.png +0 -0
- package/gui/dist/assets/App-Ba8uG696.js +0 -54
- package/gui/dist/assets/Tray-IImbNPRy.js +0 -1
- package/gui/dist/assets/index-BHRZdJuv.js +0 -94
- package/gui/dist/assets/quota-summary-DiJO6sYY.js +0 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { freeformFallbackKeys } from "../../responses/apply-patch-envelope";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pre-emission gate for Cursor calls to request-declared freeform tools (#7049).
|
|
5
|
+
*
|
|
6
|
+
* Cursor carries a freeform body as a JSON object, and the canonical shape is `{input:string}`.
|
|
7
|
+
* Claude models routed through Cursor also name the body after its content — `{"code": "..."}`
|
|
8
|
+
* for the code-mode `exec` cell — and the shared Responses repair downstream already unwraps
|
|
9
|
+
* exactly that (`unwrapFreeformToolInput`: no `input` key, ONE string-valued fallback key).
|
|
10
|
+
* This gate used to accept only `input`, so it turned a payload the bridge knows how to read
|
|
11
|
+
* into a fatal adapter error before any host execution. It now applies the same single faithful
|
|
12
|
+
* reading and re-emits it canonically; anything ambiguous or non-string still fails closed.
|
|
13
|
+
*
|
|
14
|
+
* A canonical wrapper is returned byte-identical. Only the repaired shape is re-serialized.
|
|
15
|
+
*/
|
|
16
|
+
export type CursorFreeformArgs =
|
|
17
|
+
| { ok: true; args: string }
|
|
18
|
+
| { ok: false; reason: string };
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The only key names a diagnostic may print. They are constants written here, so the error can
|
|
22
|
+
* never carry request-derived text: a model can put body text or an identifier in key position,
|
|
23
|
+
* and the message is persisted to request logs. Every other key is counted, not named.
|
|
24
|
+
*/
|
|
25
|
+
const NAMEABLE_KEYS: ReadonlySet<string> = new Set([
|
|
26
|
+
"input",
|
|
27
|
+
"code", "script", "js", "javascript", "command", "cmd", "content", "patch",
|
|
28
|
+
"source", "program", "body", "text", "args", "arguments",
|
|
29
|
+
]);
|
|
30
|
+
const MAX_LISTED_KEYS = 8;
|
|
31
|
+
|
|
32
|
+
function valueKind(value: unknown): string {
|
|
33
|
+
if (value === null) return "null";
|
|
34
|
+
if (Array.isArray(value)) return "array";
|
|
35
|
+
return typeof value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Shape-only description of a rejected wrapper: allowlisted key names with their value kinds,
|
|
40
|
+
* then a count of every other key. Never values, never an unlisted key name.
|
|
41
|
+
*/
|
|
42
|
+
function describeKeys(record: Record<string, unknown>): string {
|
|
43
|
+
const keys = Object.keys(record);
|
|
44
|
+
if (keys.length === 0) return "received an empty object";
|
|
45
|
+
const listed = keys.filter(key => NAMEABLE_KEYS.has(key))
|
|
46
|
+
.slice(0, MAX_LISTED_KEYS)
|
|
47
|
+
.map(key => `${key}:${valueKind(record[key])}`);
|
|
48
|
+
const hidden = keys.length - listed.length;
|
|
49
|
+
if (hidden > 0) listed.push(`+${hidden} other`);
|
|
50
|
+
return `received keys [${listed.join(", ")}]`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function canonicalCursorFreeformArgs(args: string, toolName: string): CursorFreeformArgs {
|
|
54
|
+
if (args.length === 0) return { ok: false, reason: "received no arguments" };
|
|
55
|
+
let parsed: unknown;
|
|
56
|
+
try {
|
|
57
|
+
parsed = JSON.parse(args);
|
|
58
|
+
} catch {
|
|
59
|
+
return { ok: false, reason: "received arguments that are not JSON" };
|
|
60
|
+
}
|
|
61
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
62
|
+
return { ok: false, reason: `received a JSON ${valueKind(parsed)}` };
|
|
63
|
+
}
|
|
64
|
+
const record = parsed as Record<string, unknown>;
|
|
65
|
+
if (Object.prototype.hasOwnProperty.call(record, "input")) {
|
|
66
|
+
return typeof record.input === "string"
|
|
67
|
+
? { ok: true, args }
|
|
68
|
+
: { ok: false, reason: `input was ${valueKind(record.input)}` };
|
|
69
|
+
}
|
|
70
|
+
const candidates = freeformFallbackKeys(toolName).filter(key => typeof record[key] === "string");
|
|
71
|
+
if (candidates.length === 1) {
|
|
72
|
+
return { ok: true, args: JSON.stringify({ input: record[candidates[0]] as string }) };
|
|
73
|
+
}
|
|
74
|
+
return { ok: false, reason: describeKeys(record) };
|
|
75
|
+
}
|
|
@@ -2,6 +2,7 @@ import type { OcxUsage } from "../../types";
|
|
|
2
2
|
import type { AgentServerMessage, McpArgs, ToolCall } from "./gen/agent_pb";
|
|
3
3
|
import { decodeCursorArgsMap } from "./arg-codec";
|
|
4
4
|
import { normalizeArgKeys } from "./arg-normalize";
|
|
5
|
+
import { canonicalCursorFreeformArgs } from "./freeform-args";
|
|
5
6
|
import {
|
|
6
7
|
CODEX_APPLY_PATCH_TOOL,
|
|
7
8
|
CURSOR_MULTI_EDIT_TOOL,
|
|
@@ -334,9 +335,20 @@ function toolSchemaForWireName(state: CursorProtobufEventState, toolName: string
|
|
|
334
335
|
return resolveShellBridgeAliasKey(toolName, alias => state.toolSchemas!.get(alias));
|
|
335
336
|
}
|
|
336
337
|
|
|
337
|
-
function
|
|
338
|
-
|
|
338
|
+
function isFreeformCall(state: CursorProtobufEventState, responsesName: string | undefined): boolean {
|
|
339
|
+
return responsesName !== undefined && state.freeformToolNames?.has(responsesName) === true;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function decodeMcpArgsNormalized(
|
|
343
|
+
args: McpArgs | undefined,
|
|
344
|
+
state: CursorProtobufEventState,
|
|
345
|
+
openResponsesName?: string,
|
|
346
|
+
): string {
|
|
339
347
|
const toolName = mcpWireNameFromArgs(args);
|
|
348
|
+
const responsesName = toolName
|
|
349
|
+
? responsesToolNameFromCursorWire(toolName, state.cursorToolNameMap)
|
|
350
|
+
: openResponsesName;
|
|
351
|
+
const decoded = decodeCursorArgsMap(args?.args, { preserveStrings: isFreeformCall(state, responsesName) });
|
|
340
352
|
const schema = toolSchemaForWireName(state, toolName);
|
|
341
353
|
if (schema) return JSON.stringify(normalizeArgKeys(decoded, schema));
|
|
342
354
|
return JSON.stringify(decoded);
|
|
@@ -381,8 +393,13 @@ function normalizeJsonText(text: string, toolName: string | undefined, state: Cu
|
|
|
381
393
|
* preserved verbatim so the bridge can reject malformed or truncated JSON instead of silently
|
|
382
394
|
* converting it to `{}`. A genuinely empty buffer remains the no-argument case.
|
|
383
395
|
*/
|
|
384
|
-
function resolveCompletedArgs(
|
|
385
|
-
|
|
396
|
+
function resolveCompletedArgs(
|
|
397
|
+
buffered: string,
|
|
398
|
+
args: McpArgs | undefined,
|
|
399
|
+
state: CursorProtobufEventState,
|
|
400
|
+
openResponsesName?: string,
|
|
401
|
+
): string {
|
|
402
|
+
if (hasMcpArgBytes(args)) return decodeMcpArgsNormalized(args, state, openResponsesName);
|
|
386
403
|
const name = mcpWireNameFromArgs(args);
|
|
387
404
|
if (isCompleteJson(buffered)) return normalizeJsonText(buffered, name, state);
|
|
388
405
|
return buffered;
|
|
@@ -1092,7 +1109,7 @@ export function mapSyntheticMcpExecToToolEvents(
|
|
|
1092
1109
|
const out: CursorServerMessage[] = [...recordToolCall(options.state, callId, cursorWireName)];
|
|
1093
1110
|
if (out.some(event => event.type === "error")) return out;
|
|
1094
1111
|
const open = options.state.openToolCalls.get(callId);
|
|
1095
|
-
const finalArgs = resolveCompletedArgs(open?.args ?? "", args, options.state);
|
|
1112
|
+
const finalArgs = resolveCompletedArgs(open?.args ?? "", args, options.state, open?.name);
|
|
1096
1113
|
out.push(...commitToolCall(options.state, callId, finalArgs));
|
|
1097
1114
|
return out;
|
|
1098
1115
|
}
|
|
@@ -1167,23 +1184,16 @@ function discardBufferedTextToolCalls(state: CursorProtobufEventState): void {
|
|
|
1167
1184
|
* recorded in `openToolCalls`. Because each completion emits a whole non-interleaved unit, the bridge
|
|
1168
1185
|
* (which tracks a single current tool call) serializes parallel Cursor calls correctly.
|
|
1169
1186
|
*/
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
return !!parsed
|
|
1174
|
-
&& typeof parsed === "object"
|
|
1175
|
-
&& !Array.isArray(parsed)
|
|
1176
|
-
&& typeof (parsed as Record<string, unknown>).input === "string";
|
|
1177
|
-
} catch {
|
|
1178
|
-
return false;
|
|
1179
|
-
}
|
|
1187
|
+
/** True when the args already carry a body this call can commit, canonical or repairable (#7049). */
|
|
1188
|
+
function cursorFreeformArgsCommittable(args: string, toolName: string): boolean {
|
|
1189
|
+
return canonicalCursorFreeformArgs(args, toolName).ok;
|
|
1180
1190
|
}
|
|
1181
1191
|
|
|
1182
|
-
function dropInvalidFreeformCall(state: CursorProtobufEventState, callId: string, toolName: string): CursorServerMessage[] {
|
|
1192
|
+
function dropInvalidFreeformCall(state: CursorProtobufEventState, callId: string, toolName: string, reason: string): CursorServerMessage[] {
|
|
1183
1193
|
state.openToolCalls.delete(callId);
|
|
1184
1194
|
state.translatorBudget?.closeCall(callId);
|
|
1185
1195
|
state.completedToolCalls.add(callId);
|
|
1186
|
-
return [{ type: "error", message: `${toolName} call had invalid freeform arguments; expected {input:string}` }];
|
|
1196
|
+
return [{ type: "error", message: `${toolName} call had invalid freeform arguments; expected {input:string} (${reason})` }];
|
|
1187
1197
|
}
|
|
1188
1198
|
|
|
1189
1199
|
function dropShellBridgeCall(state: CursorProtobufEventState, callId: string, toolName: string): CursorServerMessage[] {
|
|
@@ -1202,11 +1212,14 @@ function dropStructuredEditCall(state: CursorProtobufEventState, callId: string,
|
|
|
1202
1212
|
return [{ type: "text", text: `\n${toolName} call was not converted to apply_patch: ${reason}` }];
|
|
1203
1213
|
}
|
|
1204
1214
|
|
|
1205
|
-
function commitToolCall(state: CursorProtobufEventState, callId: string,
|
|
1215
|
+
function commitToolCall(state: CursorProtobufEventState, callId: string, committedArgs: string): CursorServerMessage[] {
|
|
1206
1216
|
const open = state.openToolCalls.get(callId);
|
|
1207
1217
|
if (!open) return [];
|
|
1208
|
-
|
|
1209
|
-
|
|
1218
|
+
let finalArgs = committedArgs;
|
|
1219
|
+
if (isFreeformCall(state, open.name)) {
|
|
1220
|
+
const freeform = canonicalCursorFreeformArgs(finalArgs, open.name);
|
|
1221
|
+
if (!freeform.ok) return dropInvalidFreeformCall(state, callId, open.name, freeform.reason);
|
|
1222
|
+
finalArgs = freeform.args;
|
|
1210
1223
|
}
|
|
1211
1224
|
const schema = toolSchemaForWireName(state, open.name);
|
|
1212
1225
|
if (!cursorShellBridgeArgsValid(finalArgs, open.name, schema)) {
|
|
@@ -1386,7 +1399,7 @@ export function mapCursorProtobufServerMessage(
|
|
|
1386
1399
|
&& (
|
|
1387
1400
|
(name !== undefined && openBeforeStart.args.length === 0)
|
|
1388
1401
|
|| (state.freeformToolNames?.has(openBeforeStart.name) === true
|
|
1389
|
-
&& !
|
|
1402
|
+
&& !cursorFreeformArgsCommittable(openBeforeStart.args, openBeforeStart.name)
|
|
1390
1403
|
)
|
|
1391
1404
|
)
|
|
1392
1405
|
) {
|
|
@@ -1410,13 +1423,13 @@ export function mapCursorProtobufServerMessage(
|
|
|
1410
1423
|
if (
|
|
1411
1424
|
!openBeforeStart && open && !hasMcpArgBytes(args)
|
|
1412
1425
|
&& state.freeformToolNames?.has(open.name) === true
|
|
1413
|
-
&& !
|
|
1426
|
+
&& !cursorFreeformArgsCommittable(open.args, open.name)
|
|
1414
1427
|
) {
|
|
1415
1428
|
open.awaitingNativeArgs = true;
|
|
1416
1429
|
return [];
|
|
1417
1430
|
}
|
|
1418
1431
|
if (open) {
|
|
1419
|
-
const finalArgs = resolveCompletedArgs(open.args, args, state);
|
|
1432
|
+
const finalArgs = resolveCompletedArgs(open.args, args, state, open.name);
|
|
1420
1433
|
out.push(...commitToolCall(state, update.value.callId, finalArgs));
|
|
1421
1434
|
}
|
|
1422
1435
|
return out;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import type { OcxContentPart, OcxParsedRequest } from "../types";
|
|
3
|
-
import { antigravityUserAgent } from "./client-fingerprint";
|
|
3
|
+
import { antigravityCliUserAgent, antigravityUserAgent } from "./client-fingerprint";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Antigravity request User-Agent. Mirrors the real Antigravity IDE UA
|
|
@@ -11,6 +11,27 @@ import { antigravityUserAgent } from "./client-fingerprint";
|
|
|
11
11
|
*/
|
|
12
12
|
export const ANTIGRAVITY_REQUEST_UA = antigravityUserAgent();
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Antigravity CLI User-Agent. CCA serves Gemini 4 Argon only to the CLI client family, and it
|
|
16
|
+
* 404s other agent models (`gemini-3.7-flash-tiered`, `gemini-3.1-pro-low`) under it, so the
|
|
17
|
+
* choice is per wire model: see `antigravityUserAgentForWireModel`.
|
|
18
|
+
*/
|
|
19
|
+
export const ANTIGRAVITY_CLI_REQUEST_UA = antigravityCliUserAgent();
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Model discovery (`:fetchAvailableModels`) uses the CLI UA so Argon's wire ids are listed;
|
|
23
|
+
* under the IDE UA they are absent.
|
|
24
|
+
*/
|
|
25
|
+
export const ANTIGRAVITY_DISCOVERY_UA = ANTIGRAVITY_CLI_REQUEST_UA;
|
|
26
|
+
|
|
27
|
+
/** Wire ids CCA serves only to the CLI client family (verified 2026-10-14). */
|
|
28
|
+
const ANTIGRAVITY_CLI_ONLY_WIRE_MODEL = /^gemini-4-argon(?:-(?:low|medium|high))?$/;
|
|
29
|
+
|
|
30
|
+
/** The User-Agent an Antigravity inference request for `wireModelId` must carry. */
|
|
31
|
+
export function antigravityUserAgentForWireModel(wireModelId: string): string {
|
|
32
|
+
return ANTIGRAVITY_CLI_ONLY_WIRE_MODEL.test(wireModelId) ? ANTIGRAVITY_CLI_REQUEST_UA : ANTIGRAVITY_REQUEST_UA;
|
|
33
|
+
}
|
|
34
|
+
|
|
14
35
|
/**
|
|
15
36
|
* Whether a stored `OcxToolCall.thoughtSignature` is a REAL upstream Gemini signature versus a
|
|
16
37
|
* foreign id that must not be forwarded to Gemini/Antigravity.
|
package/src/adapters/google.ts
CHANGED
|
@@ -21,7 +21,7 @@ import { getVertexAccessToken } from "../lib/gcp-adc";
|
|
|
21
21
|
import { fetchAntigravityWithRetry, fetchVertexWithRetry } from "./google-http";
|
|
22
22
|
import { safeAntigravityHttpErrorMessage, safeVertexHttpErrorMessage } from "./google-errors";
|
|
23
23
|
import { isVertexTruncatedTurn, vertexTruncationErrorEvent } from "./google-truncation";
|
|
24
|
-
import {
|
|
24
|
+
import { antigravitySessionAnchor, antigravityUserAgentForWireModel, antigravitySessionId, isLikelyRealThoughtSignature, sanitizeAntigravityClaudeSignatures } from "./google-antigravity-wire";
|
|
25
25
|
import { summarizeGoogleWireShape } from "./google-wire-shape";
|
|
26
26
|
import { compileGoogleWireBody } from "./google-wire-compiler";
|
|
27
27
|
import type { GoogleToolSchemaLossReport, GoogleToolSchemaProfile } from "./google-tool-schema";
|
|
@@ -1155,7 +1155,7 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
1155
1155
|
requestId: `agent-${crypto.randomUUID()}`,
|
|
1156
1156
|
request,
|
|
1157
1157
|
};
|
|
1158
|
-
headers["User-Agent"] =
|
|
1158
|
+
headers["User-Agent"] = antigravityUserAgentForWireModel(wireModelId);
|
|
1159
1159
|
headers["Authorization"] = `Bearer ${token}`;
|
|
1160
1160
|
return { url, method: "POST", headers, body: JSON.stringify(envelope) };
|
|
1161
1161
|
}
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
} from "../lib/translator-budget";
|
|
32
32
|
import { redactSecretString, SENSITIVE_KEY_PATTERN } from "../lib/redact";
|
|
33
33
|
import { parseDataUrl } from "./image";
|
|
34
|
+
import { jsonStringPartsUtf8Bytes, jsonUtf8Bytes } from "../lib/json-byte-size";
|
|
34
35
|
import {
|
|
35
36
|
ollamaNativeChatUrl,
|
|
36
37
|
ollamaNativeEndpointKind,
|
|
@@ -110,6 +111,21 @@ const NATIVE_THINK_VALUES = new Set(["low", "medium", "high", "max"]);
|
|
|
110
111
|
const NATIVE_TOOL_ID_MAX_LENGTH = 256;
|
|
111
112
|
const NATIVE_TOOL_NAME_MAX_BYTES = 1024;
|
|
112
113
|
const NATIVE_MAX_PENDING_TOOL_CALLS = 128;
|
|
114
|
+
const NATIVE_MAX_LATE_ATTRIBUTION_BYTES = 256 * 1024;
|
|
115
|
+
/** Proxy-owned refusal text for an exhausted late-attribution budget. A compaction on the same
|
|
116
|
+
* Ollama route resends the same history, so the only reliable recovery is a new thread. */
|
|
117
|
+
export const OLLAMA_LATE_ATTRIBUTION_LIMIT_MESSAGE =
|
|
118
|
+
"Late tool-output attribution for this conversation exceeded the 256 KiB per-request limit. Start a new thread to continue.";
|
|
119
|
+
|
|
120
|
+
/** The late-attribution budget refusal, reporting the full limit rather than the bytes left. */
|
|
121
|
+
export class OllamaLateAttributionBudgetError extends TranslatorBudgetExceededError {
|
|
122
|
+
readonly publicMessage = OLLAMA_LATE_ATTRIBUTION_LIMIT_MESSAGE;
|
|
123
|
+
|
|
124
|
+
constructor() {
|
|
125
|
+
super("request_copies", NATIVE_MAX_LATE_ATTRIBUTION_BYTES);
|
|
126
|
+
this.name = "OllamaLateAttributionBudgetError";
|
|
127
|
+
}
|
|
128
|
+
}
|
|
113
129
|
// Account for the retained Map key and call bookkeeping in addition to the provider's name.
|
|
114
130
|
const NATIVE_TOOL_CALL_BOOKKEEPING_BYTES = 128;
|
|
115
131
|
const NATIVE_TOOL_ID_CONTROL = /[\u0000-\u001f\u007f]/u;
|
|
@@ -325,6 +341,7 @@ function buildNativeMessages(
|
|
|
325
341
|
reservedToolCallIds.clear();
|
|
326
342
|
let pending: PendingToolBatch | undefined;
|
|
327
343
|
const issuedCalls = new Map<string, PendingToolCall>();
|
|
344
|
+
let lateAttributionBytes = 0;
|
|
328
345
|
// Codex records mid-turn injections (a PostToolUse hook verdict, a context notice) between an
|
|
329
346
|
// assistant tool call and that call's own tool result. Native Ollama needs the call and its
|
|
330
347
|
// results adjacent, so those conversational messages wait here instead of closing the batch
|
|
@@ -392,9 +409,20 @@ function buildNativeMessages(
|
|
|
392
409
|
// Reopening a settled batch would split a later call/result pair. Keep known late
|
|
393
410
|
// output explicitly attributed as conversation text instead of dropping it or
|
|
394
411
|
// fabricating another executable call. Unknown/mismatched identities still fail above.
|
|
412
|
+
// Generated attribution is not present in the inbound size estimate. Reserve its
|
|
413
|
+
// JSON wire bytes before joining strings, across all late outputs and all calls.
|
|
414
|
+
// Count the joined JSON string exactly, without constructing a measurement copy.
|
|
415
|
+
const attribution = ['[ocx] additional output for previously issued tool "',
|
|
416
|
+
call.wireName, '" (', call.id, '):\n', message.isError ? "ERROR: " : ""];
|
|
417
|
+
try {
|
|
418
|
+
lateAttributionBytes += jsonStringPartsUtf8Bytes(attribution, NATIVE_MAX_LATE_ATTRIBUTION_BYTES - lateAttributionBytes);
|
|
419
|
+
} catch (error) {
|
|
420
|
+
if (error instanceof TranslatorBudgetExceededError) throw new OllamaLateAttributionBudgetError();
|
|
421
|
+
throw error;
|
|
422
|
+
}
|
|
395
423
|
const translated = contentToNative(message.content, "late tool result");
|
|
396
424
|
const late: OllamaNativeMessage = { role: "user",
|
|
397
|
-
content:
|
|
425
|
+
content: attribution.join("") + translated.content,
|
|
398
426
|
...(translated.images ? { images: translated.images } : {}),
|
|
399
427
|
};
|
|
400
428
|
if (pending) deferred.push(late);
|
|
@@ -142,12 +142,12 @@ export function messagesToChatFormat(parsed: OcxParsedRequest, provider: OcxProv
|
|
|
142
142
|
};
|
|
143
143
|
|
|
144
144
|
const nativeOpenAI = isNativeOpenAIChatTarget(provider);
|
|
145
|
-
//
|
|
146
|
-
// later user renders in place: Qwen3.8-27B and the OrcaSAQ-2-Cyber-27B GGUF family.
|
|
145
|
+
// Leading-system chat templates reject non-leading system and every developer role while a
|
|
146
|
+
// later user renders in place: Qwen3.8-27B, Flash-Next, and the OrcaSAQ-2-Cyber-27B GGUF family.
|
|
147
147
|
// Gateways serve them under namespaced ids (LiteLLM's openai/Qwen3.8-27B, #6674), so any
|
|
148
148
|
// number of leading path segments is accepted; the final segment names the checkpoint.
|
|
149
149
|
// Orca's GGUF marker must precede any colon quant tag; other Orca variants are unchanged.
|
|
150
|
-
const leadingSystemTemplate = !nativeOpenAI && /^(?:[^/]+\/)*(?:Qwen3\.8-27B|qwen3-8-27b(?:-(?:fp8|lora))?|OrcaSAQ-2-Cyber-27B(?=[^:/\s]*GGUF)[^:/\s]*(?::[\w-]+)?)$/i.test(parsed.modelId);
|
|
150
|
+
const leadingSystemTemplate = !nativeOpenAI && /^(?:[^/]+\/)*(?:flash-next|Qwen3\.8-Flash-Next|Qwen3\.8-27B|qwen3-8-27b(?:-(?:fp8|lora))?|OrcaSAQ-2-Cyber-27B(?=[^:/\s]*GGUF)[^:/\s]*(?::[\w-]+)?)$/i.test(parsed.modelId);
|
|
151
151
|
// Which role a developer message carries, and why the unrecorded state folds, is stated once
|
|
152
152
|
// in ./developer-role.ts and read from there by the native passthrough as well. Either way the
|
|
153
153
|
// message keeps the slot it arrived in — only the role changes, never the position.
|
|
@@ -292,7 +292,11 @@ export function messagesToChatFormat(parsed: OcxParsedRequest, provider: OcxProv
|
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
const hasReplayedReasoning = chatMsg.reasoning_content !== undefined || chatMsg.reasoning_details !== undefined;
|
|
295
|
-
|
|
295
|
+
// A turn with no answer text, tools or reasoning carries nothing. Between a call and its
|
|
296
|
+
// result it would also split the pair, so drop it there; a standalone empty answer keeps
|
|
297
|
+
// its slot so templates that require strict user/assistant alternation still accept it.
|
|
298
|
+
const carriesNothing = !chatMsg.content && toolCalls.length === 0 && !hasReplayedReasoning;
|
|
299
|
+
if (carriesNothing && (chatMsg.content === undefined || pendingToolCalls.length > 0)) break;
|
|
296
300
|
flushPendingToolCalls();
|
|
297
301
|
const wireToolCalls = toolCalls.map(tc => {
|
|
298
302
|
let id = tc.id;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { diagnoseInvalidToolCalls, isRecord, type InvalidToolCallDiagnostic } from "./tool-call-validation";
|
|
2
2
|
import { TranslatorBudgetExceededError, type TranslatorBudget } from "../../lib/translator-budget";
|
|
3
|
-
import type { AdapterEvent, OcxUsage } from "../../types";
|
|
3
|
+
import type { AdapterEvent, OcxProviderConfig, OcxUsage } from "../../types";
|
|
4
|
+
import { createInlineThinkContentSplitter } from "../inline-think-tags";
|
|
4
5
|
|
|
5
6
|
export function stopReasonFor(finishReason: unknown): "max_tokens" | "content_filter" | undefined {
|
|
6
7
|
return finishReason === "length"
|
|
@@ -18,6 +19,63 @@ export function reasoningTextFrom(record: Record<string, unknown>): string | und
|
|
|
18
19
|
: undefined;
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
type ChatContentEvent = Extract<AdapterEvent, { type: "text_delta" | "reasoning_raw_delta" | "error" }>;
|
|
23
|
+
|
|
24
|
+
/** Mistral can mix typed blocks and strings in one response. Other destinations keep their parser. */
|
|
25
|
+
export function chatContentEvents(content: unknown, baseUrl: string, usage?: OcxUsage, buffered = false): ChatContentEvent[] {
|
|
26
|
+
if (typeof content === "string") return content.length > 0 || buffered ? [{ type: "text_delta", text: content }] : [];
|
|
27
|
+
if (!Array.isArray(content)) return [];
|
|
28
|
+
let host: string;
|
|
29
|
+
try { host = new URL(baseUrl).hostname; } catch { return []; }
|
|
30
|
+
if (host !== "api.mistral.ai") return [];
|
|
31
|
+
const invalid = (): ChatContentEvent[] => [{
|
|
32
|
+
type: "error", status: 502, errorType: "upstream_error",
|
|
33
|
+
message: "unsupported Mistral content block", ...(usage ? { usage } : {}),
|
|
34
|
+
}];
|
|
35
|
+
const events: ChatContentEvent[] = [];
|
|
36
|
+
for (const block of content) {
|
|
37
|
+
if (!isRecord(block)) return invalid();
|
|
38
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
39
|
+
if (block.text.length > 0 || buffered) events.push({ type: "text_delta", text: block.text });
|
|
40
|
+
} else if (block.type === "thinking" && Array.isArray(block.thinking)) {
|
|
41
|
+
for (const inner of block.thinking) {
|
|
42
|
+
if (!isRecord(inner) || inner.type !== "text" || typeof inner.text !== "string") return invalid();
|
|
43
|
+
if (inner.text.length > 0) events.push({ type: "reasoning_raw_delta", text: inner.text });
|
|
44
|
+
}
|
|
45
|
+
} else return invalid();
|
|
46
|
+
}
|
|
47
|
+
return events;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Keep adjacent text together; typed reasoning flushes and resets inline-tag parsing. */
|
|
51
|
+
export function createChatContentSplitter(provider: OcxProviderConfig, modelId: string | undefined, budget: TranslatorBudget) {
|
|
52
|
+
let splitter = createInlineThinkContentSplitter(provider.inlineThinkTagModels, modelId, budget);
|
|
53
|
+
return {
|
|
54
|
+
feed(event: ChatContentEvent): AdapterEvent[] {
|
|
55
|
+
if (event.type === "error") return [event];
|
|
56
|
+
if (event.type === "text_delta") return splitter.feed(event.text);
|
|
57
|
+
const pending = splitter.flush();
|
|
58
|
+
splitter.dispose();
|
|
59
|
+
splitter = createInlineThinkContentSplitter(provider.inlineThinkTagModels, modelId, budget);
|
|
60
|
+
return [...pending, event];
|
|
61
|
+
},
|
|
62
|
+
flush: () => splitter.flush(),
|
|
63
|
+
dispose: () => splitter.dispose(),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Split a buffered answer with the same typed-reasoning boundaries as a stream. */
|
|
68
|
+
export function splitChatContentEvents(content: unknown, provider: OcxProviderConfig, modelId: string | undefined, budget: TranslatorBudget, usage?: OcxUsage): AdapterEvent[] {
|
|
69
|
+
const events = chatContentEvents(content, provider.baseUrl, usage, true);
|
|
70
|
+
if (events.some(event => event.type === "error")) return events;
|
|
71
|
+
const splitter = createChatContentSplitter(provider, modelId, budget);
|
|
72
|
+
try {
|
|
73
|
+
return [...events.flatMap(event => splitter.feed(event)), ...splitter.flush()];
|
|
74
|
+
} finally {
|
|
75
|
+
splitter.dispose();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
21
79
|
export interface ReasoningDetailSegment {
|
|
22
80
|
key: string;
|
|
23
81
|
text: string;
|
|
@@ -5,7 +5,6 @@ import { applyExplicitChatReasoningWirePolicy } from "./openai-chat/reasoning-wi
|
|
|
5
5
|
import type { AdapterRequest, IncomingMeta, ProviderAdapter } from "./base";
|
|
6
6
|
import type { AdapterEvent, OcxParsedRequest, OcxProviderConfig, OcxUsage } from "../types";
|
|
7
7
|
import { modelInList } from "../types";
|
|
8
|
-
import { createInlineThinkContentSplitter, splitInlineThinkContent } from "./inline-think-tags";
|
|
9
8
|
import { mapReasoningEffort } from "../reasoning-effort";
|
|
10
9
|
import { debugProviderDiagnostic } from "../lib/debug";
|
|
11
10
|
import { sseFieldValue } from "../lib/sse-decoder";
|
|
@@ -28,6 +27,7 @@ import {
|
|
|
28
27
|
type InvalidToolCallDiagnostic,
|
|
29
28
|
} from "./openai-chat/tool-call-validation";
|
|
30
29
|
import {
|
|
30
|
+
chatContentEvents, createChatContentSplitter, splitChatContentEvents,
|
|
31
31
|
createReasoningDetailSnapshotTracker,
|
|
32
32
|
invalidChoicesEvent,
|
|
33
33
|
invalidToolCallsEvent,
|
|
@@ -368,7 +368,7 @@ export function createOpenAIChatAdapter(provider: OcxProviderConfig): ProviderAd
|
|
|
368
368
|
const reasoningDetailsOptIn = modelInList(provider.reasoningDetailsModels, lastRequestedModelId ?? "");
|
|
369
369
|
// A gateway with no server-side reasoning parser leaves thinking inline in `content` as
|
|
370
370
|
// <think> blocks, which would otherwise render as the answer. Passthrough unless opted in.
|
|
371
|
-
const inlineThink =
|
|
371
|
+
const inlineThink = createChatContentSplitter(provider, lastRequestedModelId, budget);
|
|
372
372
|
const emitContent = function* (events: AdapterEvent[]): Generator<AdapterEvent> {
|
|
373
373
|
for (const event of events) {
|
|
374
374
|
// Any other event keeps its place behind held text instead of overtaking it.
|
|
@@ -444,8 +444,9 @@ export function createOpenAIChatAdapter(provider: OcxProviderConfig): ProviderAd
|
|
|
444
444
|
const reasoningText = reasoningTextFrom(delta);
|
|
445
445
|
if (reasoningText !== undefined) yield* toolCallContent.hold({ type: "reasoning_raw_delta", text: reasoningText });
|
|
446
446
|
}
|
|
447
|
-
|
|
448
|
-
yield*
|
|
447
|
+
for (const event of chatContentEvents(delta.content, provider.baseUrl, pendingUsage)) {
|
|
448
|
+
if (event.type === "error") return yield* terminateWithError(event);
|
|
449
|
+
yield* emitContent(inlineThink.feed(event));
|
|
449
450
|
}
|
|
450
451
|
|
|
451
452
|
const rawToolCalls = delta.tool_calls;
|
|
@@ -767,7 +768,9 @@ export function createOpenAIChatAdapter(provider: OcxProviderConfig): ProviderAd
|
|
|
767
768
|
}
|
|
768
769
|
if (reasoningText !== undefined) events.push({ type: "reasoning_raw_delta", text: reasoningText });
|
|
769
770
|
const contentStart = events.length;
|
|
770
|
-
|
|
771
|
+
const contentEvents = splitChatContentEvents(msg.content, provider, lastRequestedModelId, budget, usage);
|
|
772
|
+
if (contentEvents.some(event => event.type === "error")) return contentEvents;
|
|
773
|
+
events.push(...contentEvents);
|
|
771
774
|
const contentEnd = events.length;
|
|
772
775
|
const answerText = events.slice(contentStart).map(event => (event.type === "text_delta" ? event.text : "")).join("");
|
|
773
776
|
// Each call holds the delta event it emitted, so the batch repair sets its arguments later.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { isPlainObject } from "./internal";
|
|
2
|
+
|
|
3
|
+
const MESSAGE_FIELDS = new Set(["type", "role", "content", "id", "status", "phase"]);
|
|
4
|
+
|
|
5
|
+
function isEmptyTextPart(part: unknown): boolean {
|
|
6
|
+
if (!isPlainObject(part) || !["text", "input_text", "output_text"].includes(String(part.type)) || part.text !== "") return false;
|
|
7
|
+
// Nonempty annotations and unknown fields may carry information despite the blank text.
|
|
8
|
+
return Object.keys(part).every(key => key === "type" || key === "text"
|
|
9
|
+
|| ((key === "annotations" || key === "logprobs") && Array.isArray(part[key]) && part[key].length === 0));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function isEmptyAssistantMessage(item: unknown): boolean {
|
|
13
|
+
if (!isPlainObject(item) || item.role !== "assistant" || (item.type !== undefined && item.type !== "message")) return false;
|
|
14
|
+
// Never discard tools, reasoning, opaque state or unknown extension fields on a message.
|
|
15
|
+
if (!Object.keys(item).every(key => MESSAGE_FIELDS.has(key))) return false;
|
|
16
|
+
return item.content === "" || (Array.isArray(item.content) && item.content.every(isEmptyTextPart));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Kimi Code rejects content-empty assistant messages even on its native Responses endpoint. */
|
|
20
|
+
export function stripKimiEmptyAssistantMessages(body: unknown, responseUrl: string): unknown {
|
|
21
|
+
let url: URL;
|
|
22
|
+
try { url = new URL(responseUrl); } catch { return body; }
|
|
23
|
+
if (url.hostname !== "api.kimi.com" || !/^\/coding\/v1\/responses\/?$/.test(url.pathname)) return body;
|
|
24
|
+
if (!isPlainObject(body) || !Array.isArray(body.input)) return body;
|
|
25
|
+
const referencedIds = new Set<string>();
|
|
26
|
+
for (const item of body.input) {
|
|
27
|
+
if (isPlainObject(item) && item.type === "item_reference" && typeof item.id === "string") referencedIds.add(item.id);
|
|
28
|
+
}
|
|
29
|
+
const input = body.input.filter(item => !isEmptyAssistantMessage(item)
|
|
30
|
+
|| (isPlainObject(item) && typeof item.id === "string" && referencedIds.has(item.id)));
|
|
31
|
+
return input.length === body.input.length ? body : { ...body, input };
|
|
32
|
+
}
|
|
@@ -50,6 +50,7 @@ import { observeOutbound } from "../../usage/cache-diagnostic";
|
|
|
50
50
|
import { normalizeForwardedClientHeaderName } from "../../lib/provider-client-headers";
|
|
51
51
|
import { normalizeMuseToolChoice } from "./muse-tool-choice";
|
|
52
52
|
import { renderCompactionSearchHistoryItems } from "./compaction-search-history";
|
|
53
|
+
import { stripKimiEmptyAssistantMessages } from "./empty-assistant";
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
56
|
* Identifies DeepSeek's strict Responses replay contract: tool-bearing continuations need
|
|
@@ -477,6 +478,7 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
|
|
|
477
478
|
// Run after routed compaction so nested input_image parts are replaced before a malformed
|
|
478
479
|
// tool output is flattened to text and can no longer be inspected structurally.
|
|
479
480
|
outBody = repairUnidentifiedToolOutputItems(outBody);
|
|
481
|
+
outBody = stripKimiEmptyAssistantMessages(outBody, url);
|
|
480
482
|
if (parsed._plaintextV2AgentMessages === true && isCanonicalOpenAiForwardProvider(provider)) {
|
|
481
483
|
const prepared = preparePlaintextV2AgentMessages(outBody);
|
|
482
484
|
outBody = prepared.body;
|
|
@@ -499,6 +501,7 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
|
|
|
499
501
|
outBody,
|
|
500
502
|
destinationDecodesNativeCompactionBlob(provider),
|
|
501
503
|
threadServingIdentityChanged,
|
|
504
|
+
parsed._compactionRequest !== true,
|
|
502
505
|
),
|
|
503
506
|
{
|
|
504
507
|
preserveRawReasoningContent: provider.preserveResponsesReasoningContent === true,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { COMPACT_PROMPT, compactionItemToText, decodeCompactionSummary, isCompactionItemType } from "../../responses/compaction";
|
|
2
|
+
import { COMPACT_PROMPT, appendCheckpointContinuationNote, compactionItemToText, decodeCompactionSummary, isCompactionItemType } from "../../responses/compaction";
|
|
3
3
|
import { debugProviderDiagnostic } from "../../lib/debug";
|
|
4
4
|
import { modelInList } from "../../types";
|
|
5
5
|
import { isPlainObject } from "./internal";
|
|
@@ -260,10 +260,12 @@ export function scrubOcxCompactionItems(
|
|
|
260
260
|
body: unknown,
|
|
261
261
|
destinationDecodesNativeBlob: boolean,
|
|
262
262
|
threadServingIdentityChanged: boolean,
|
|
263
|
+
addContinuationNote = false,
|
|
263
264
|
): unknown {
|
|
264
265
|
if (!isPlainObject(body) || !Array.isArray(body.input)) return body;
|
|
265
266
|
|
|
266
267
|
let changed = false;
|
|
268
|
+
let decodedCheckpoint = false;
|
|
267
269
|
const input = body.input.map(item => {
|
|
268
270
|
if (!isPlainObject(item) || !isCompactionItemType(item.type)) return item;
|
|
269
271
|
const encrypted = typeof item.encrypted_content === "string" ? item.encrypted_content : undefined;
|
|
@@ -274,6 +276,7 @@ export function scrubOcxCompactionItems(
|
|
|
274
276
|
&& !threadServingIdentityChanged
|
|
275
277
|
) return item;
|
|
276
278
|
changed = true;
|
|
279
|
+
if (decodeCompactionSummary(encrypted) !== null) decodedCheckpoint = true;
|
|
277
280
|
return {
|
|
278
281
|
type: "message",
|
|
279
282
|
role: "user",
|
|
@@ -281,5 +284,10 @@ export function scrubOcxCompactionItems(
|
|
|
281
284
|
};
|
|
282
285
|
});
|
|
283
286
|
|
|
284
|
-
|
|
287
|
+
if (!changed) return body;
|
|
288
|
+
// #6764: a decoded checkpoint is plain user text from here on, so name its role in the
|
|
289
|
+
// instructions. Compaction requests skip this; the summarizer must not be told to continue.
|
|
290
|
+
return addContinuationNote && decodedCheckpoint
|
|
291
|
+
? { ...body, input, instructions: appendCheckpointContinuationNote(body.instructions) }
|
|
292
|
+
: { ...body, input };
|
|
285
293
|
}
|