@iislee/opencodex 2.11.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/LICENSE +21 -0
- package/README.md +225 -0
- package/assets/architecture.png +0 -0
- package/assets/banner.png +0 -0
- package/assets/claude-code-models.gif +0 -0
- package/assets/codex-app-picker.png +0 -0
- package/bin/ocx.mjs +451 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-DTpMHS4F.js +67 -0
- package/gui/dist/assets/index-ZNVDE3C7.css +1 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/icons.svg +24 -0
- package/gui/dist/index.html +25 -0
- package/gui/dist/logo.png +0 -0
- package/gui/dist/provider-icons/alibaba-color.svg +1 -0
- package/gui/dist/provider-icons/antigravity-color.svg +1 -0
- package/gui/dist/provider-icons/antigravity.svg +1 -0
- package/gui/dist/provider-icons/claude-color.svg +1 -0
- package/gui/dist/provider-icons/claude.svg +1 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -0
- package/gui/dist/provider-icons/copilot.svg +1 -0
- package/gui/dist/provider-icons/cursor-color.svg +2 -0
- package/gui/dist/provider-icons/cursor.svg +2 -0
- package/gui/dist/provider-icons/deepseek-color.svg +1 -0
- package/gui/dist/provider-icons/discord.svg +1 -0
- package/gui/dist/provider-icons/firepass-color.svg +1 -0
- package/gui/dist/provider-icons/fireworks-color.svg +1 -0
- package/gui/dist/provider-icons/gemini-color.svg +1 -0
- package/gui/dist/provider-icons/gemini.svg +1 -0
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
- package/gui/dist/provider-icons/grok-color.svg +1 -0
- package/gui/dist/provider-icons/grok.svg +1 -0
- package/gui/dist/provider-icons/groq-color.svg +1 -0
- package/gui/dist/provider-icons/huggingface-color.svg +1 -0
- package/gui/dist/provider-icons/kimi-color.svg +1 -0
- package/gui/dist/provider-icons/kiro-color.svg +15 -0
- package/gui/dist/provider-icons/kiro.svg +14 -0
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
- package/gui/dist/provider-icons/mistral-color.svg +1 -0
- package/gui/dist/provider-icons/moonshot-color.svg +1 -0
- package/gui/dist/provider-icons/nvidia-color.svg +1 -0
- package/gui/dist/provider-icons/ollama-color.svg +1 -0
- package/gui/dist/provider-icons/openai.svg +1 -0
- package/gui/dist/provider-icons/opencode.svg +1 -0
- package/gui/dist/provider-icons/openrouter-color.svg +1 -0
- package/gui/dist/provider-icons/pi.svg +21 -0
- package/gui/dist/provider-icons/qianfan-color.svg +1 -0
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
- package/gui/dist/provider-icons/telegram.svg +1 -0
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/vllm-color.svg +1 -0
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
- package/package.json +102 -0
- package/src/AGENTS.md +28 -0
- package/src/adapters/anthropic-image-guard.ts +251 -0
- package/src/adapters/anthropic-image-normalize.ts +518 -0
- package/src/adapters/anthropic.ts +1003 -0
- package/src/adapters/azure.ts +36 -0
- package/src/adapters/base.ts +72 -0
- package/src/adapters/client-fingerprint.ts +59 -0
- package/src/adapters/cursor/arg-codec.ts +38 -0
- package/src/adapters/cursor/arg-normalize.ts +104 -0
- package/src/adapters/cursor/cursor-errors.ts +165 -0
- package/src/adapters/cursor/discovery.ts +276 -0
- package/src/adapters/cursor/effort-map.ts +127 -0
- package/src/adapters/cursor/exec-policy.ts +88 -0
- package/src/adapters/cursor/framing.ts +211 -0
- package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
- package/src/adapters/cursor/kv-store.ts +52 -0
- package/src/adapters/cursor/live-models.ts +153 -0
- package/src/adapters/cursor/live-smoke-gate.ts +41 -0
- package/src/adapters/cursor/live-transport.ts +1214 -0
- package/src/adapters/cursor/mcp-config.ts +42 -0
- package/src/adapters/cursor/mcp-manager.ts +333 -0
- package/src/adapters/cursor/message-mapper.ts +49 -0
- package/src/adapters/cursor/native-exec-common.ts +55 -0
- package/src/adapters/cursor/native-exec-desktop.ts +184 -0
- package/src/adapters/cursor/native-exec-fs.ts +329 -0
- package/src/adapters/cursor/native-exec-mcp.ts +153 -0
- package/src/adapters/cursor/native-exec-network.ts +43 -0
- package/src/adapters/cursor/native-exec-shell.ts +548 -0
- package/src/adapters/cursor/native-exec-tools.ts +118 -0
- package/src/adapters/cursor/native-exec.ts +576 -0
- package/src/adapters/cursor/protobuf-events.ts +563 -0
- package/src/adapters/cursor/protobuf-request.ts +714 -0
- package/src/adapters/cursor/request-builder.ts +255 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/tool-definitions.ts +505 -0
- package/src/adapters/cursor/transport-retry.ts +132 -0
- package/src/adapters/cursor/transport.ts +57 -0
- package/src/adapters/cursor/types.ts +52 -0
- package/src/adapters/cursor.ts +196 -0
- package/src/adapters/google-antigravity-replay.ts +303 -0
- package/src/adapters/google-antigravity-wire.ts +108 -0
- package/src/adapters/google-errors.ts +85 -0
- package/src/adapters/google-http.ts +100 -0
- package/src/adapters/google-tool-schema.ts +173 -0
- package/src/adapters/google-truncation.ts +13 -0
- package/src/adapters/google-wire-compiler.ts +232 -0
- package/src/adapters/google.ts +758 -0
- package/src/adapters/identity.ts +44 -0
- package/src/adapters/image.ts +23 -0
- package/src/adapters/kiro-constants.ts +16 -0
- package/src/adapters/kiro-errors.ts +197 -0
- package/src/adapters/kiro-events.ts +179 -0
- package/src/adapters/kiro-images.ts +129 -0
- package/src/adapters/kiro-retry.ts +312 -0
- package/src/adapters/kiro-thinking.ts +96 -0
- package/src/adapters/kiro-tool-fallback.ts +36 -0
- package/src/adapters/kiro-tools.ts +215 -0
- package/src/adapters/kiro-truncation.ts +33 -0
- package/src/adapters/kiro-wire.ts +129 -0
- package/src/adapters/kiro.ts +1898 -0
- package/src/adapters/mimo-free.ts +263 -0
- package/src/adapters/openai-chat.ts +1005 -0
- package/src/adapters/openai-responses.ts +1137 -0
- package/src/adapters/run-turn-queue.ts +114 -0
- package/src/adapters/tool-catalog-nudge.ts +71 -0
- package/src/adapters/upstream-http-error.ts +48 -0
- package/src/bridge.ts +1619 -0
- package/src/chat/inbound.ts +295 -0
- package/src/chat/outbound.ts +765 -0
- package/src/claude/agents-inject.ts +243 -0
- package/src/claude/alias.ts +149 -0
- package/src/claude/auth-detect.ts +229 -0
- package/src/claude/auth-mode-migration.ts +32 -0
- package/src/claude/auth-mode.ts +62 -0
- package/src/claude/context-windows.ts +189 -0
- package/src/claude/desktop-3p-guard.ts +35 -0
- package/src/claude/desktop-3p-paths.ts +84 -0
- package/src/claude/desktop-3p.ts +381 -0
- package/src/claude/desktop-health.ts +26 -0
- package/src/claude/desktop-profile.ts +263 -0
- package/src/claude/gateway-cache.ts +70 -0
- package/src/claude/inbound-debug.ts +163 -0
- package/src/claude/inbound.ts +509 -0
- package/src/claude/model-info.ts +151 -0
- package/src/claude/outbound.ts +872 -0
- package/src/cli/access.ts +108 -0
- package/src/cli/account-api.ts +268 -0
- package/src/cli/account-auth.ts +223 -0
- package/src/cli/account-extended.ts +350 -0
- package/src/cli/account.ts +275 -0
- package/src/cli/agent-driven.ts +70 -0
- package/src/cli/agent.ts +184 -0
- package/src/cli/catalog-prewarm.ts +27 -0
- package/src/cli/claude-desktop.ts +188 -0
- package/src/cli/claude.ts +286 -0
- package/src/cli/codex-shim-autorestore.ts +45 -0
- package/src/cli/combo.ts +119 -0
- package/src/cli/config-command.ts +145 -0
- package/src/cli/debug.ts +228 -0
- package/src/cli/doctor.ts +930 -0
- package/src/cli/export-command.ts +187 -0
- package/src/cli/help.ts +354 -0
- package/src/cli/index.ts +1113 -0
- package/src/cli/init.ts +224 -0
- package/src/cli/integrations.ts +142 -0
- package/src/cli/interactive-confirm.ts +133 -0
- package/src/cli/internal-dispatch.ts +20 -0
- package/src/cli/models-runtime.ts +212 -0
- package/src/cli/models.ts +336 -0
- package/src/cli/observe.ts +117 -0
- package/src/cli/opencode.ts +586 -0
- package/src/cli/pi.ts +188 -0
- package/src/cli/provider-runtime.ts +162 -0
- package/src/cli/provider.ts +463 -0
- package/src/cli/runtime-api.ts +325 -0
- package/src/cli/star-prompt.ts +155 -0
- package/src/cli/status-oauth.ts +78 -0
- package/src/cli/status.ts +321 -0
- package/src/cli/sync-cloud.ts +283 -0
- package/src/cli/system-command.ts +112 -0
- package/src/cli/tray-proxy.ts +52 -0
- package/src/cli/v2.ts +173 -0
- package/src/cli.ts +10 -0
- package/src/clients/config-export.ts +377 -0
- package/src/clients/effective-status.ts +385 -0
- package/src/clients/probes/agy.ts +55 -0
- package/src/clients/probes/cc-switch.ts +110 -0
- package/src/clients/probes/claude.ts +90 -0
- package/src/clients/probes/codex.ts +125 -0
- package/src/clients/probes/grok.ts +29 -0
- package/src/clients/probes/opencode.ts +109 -0
- package/src/clients/probes/paseo.ts +55 -0
- package/src/clients/probes/pi.ts +55 -0
- package/src/cloud/onedrive-auth.ts +666 -0
- package/src/cloud/onedrive-graph.ts +108 -0
- package/src/cloud/settings.ts +75 -0
- package/src/cloud/sync.ts +212 -0
- package/src/cloud/types.ts +56 -0
- package/src/cloud/vault.ts +89 -0
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-label.ts +34 -0
- package/src/codex/account-lifecycle.ts +55 -0
- package/src/codex/account-namespace-match.ts +63 -0
- package/src/codex/account-namespaces.ts +149 -0
- package/src/codex/account-pause.ts +20 -0
- package/src/codex/account-runtime-state.ts +31 -0
- package/src/codex/account-store.ts +517 -0
- package/src/codex/account-usability.ts +20 -0
- package/src/codex/app-server-processes.ts +756 -0
- package/src/codex/auth-api.ts +1540 -0
- package/src/codex/auth-collision.ts +107 -0
- package/src/codex/auth-context.ts +352 -0
- package/src/codex/autostart-health.ts +149 -0
- package/src/codex/catalog/aggregation.ts +378 -0
- package/src/codex/catalog/bundled.ts +251 -0
- package/src/codex/catalog/effort.ts +355 -0
- package/src/codex/catalog/metadata.ts +180 -0
- package/src/codex/catalog/parsing.ts +456 -0
- package/src/codex/catalog/provider-fetch.ts +902 -0
- package/src/codex/catalog/sync.ts +620 -0
- package/src/codex/catalog.ts +12 -0
- package/src/codex/data/upstream-models.json +830 -0
- package/src/codex/exec-invocation.ts +22 -0
- package/src/codex/features.ts +969 -0
- package/src/codex/history-migration-guardian.ts +102 -0
- package/src/codex/history-provider.ts +776 -0
- package/src/codex/home.ts +206 -0
- package/src/codex/inject.ts +799 -0
- package/src/codex/injected-marker.ts +72 -0
- package/src/codex/journal.ts +163 -0
- package/src/codex/main-account-cache.ts +32 -0
- package/src/codex/main-account.ts +40 -0
- package/src/codex/model-cache.ts +227 -0
- package/src/codex/paths.ts +65 -0
- package/src/codex/plugins-doctor.ts +242 -0
- package/src/codex/pool-rotation.ts +225 -0
- package/src/codex/project-config-warnings.ts +411 -0
- package/src/codex/quota.ts +411 -0
- package/src/codex/refresh.ts +53 -0
- package/src/codex/routing.ts +1477 -0
- package/src/codex/runtime.ts +538 -0
- package/src/codex/shim.ts +1189 -0
- package/src/codex/subagent-defaults.ts +550 -0
- package/src/codex/subagent-model-fallback.ts +469 -0
- package/src/codex/sync.ts +130 -0
- package/src/codex/warmup.ts +192 -0
- package/src/codex/websocket-registry.ts +100 -0
- package/src/combos/failover.ts +140 -0
- package/src/combos/index.ts +41 -0
- package/src/combos/request.ts +62 -0
- package/src/combos/resolve.ts +232 -0
- package/src/combos/types.ts +326 -0
- package/src/config.ts +2356 -0
- package/src/generated/jawcode-model-metadata.ts +104 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +545 -0
- package/src/grok/status.ts +121 -0
- package/src/grok/sync.ts +103 -0
- package/src/grok/usage-hook/report.mjs +348 -0
- package/src/grok/usage-hook.ts +278 -0
- package/src/images/artifacts.ts +516 -0
- package/src/images/fulfill-video.ts +163 -0
- package/src/images/fulfill.ts +149 -0
- package/src/images/index.ts +4 -0
- package/src/images/loop.ts +829 -0
- package/src/images/plan.ts +133 -0
- package/src/images/synthetic-tool.ts +133 -0
- package/src/images/types.ts +41 -0
- package/src/images/xai-client.ts +141 -0
- package/src/images/xai-video-client.ts +163 -0
- package/src/index.ts +22 -0
- package/src/lib/abort.ts +146 -0
- package/src/lib/admin-secrets.ts +25 -0
- package/src/lib/admission.ts +83 -0
- package/src/lib/app-owned-memory-stores.ts +173 -0
- package/src/lib/app-owned-memory.ts +265 -0
- package/src/lib/bounded-body.ts +202 -0
- package/src/lib/bun-binary-validator.d.mts +3 -0
- package/src/lib/bun-binary-validator.mjs +18 -0
- package/src/lib/bun-runtime.ts +71 -0
- package/src/lib/bun-stream-caps.ts +126 -0
- package/src/lib/config-ownership.ts +360 -0
- package/src/lib/crash-guard.ts +344 -0
- package/src/lib/debug-log-buffer.ts +83 -0
- package/src/lib/debug-settings.ts +108 -0
- package/src/lib/debug.ts +31 -0
- package/src/lib/destination-policy.ts +316 -0
- package/src/lib/errors.ts +364 -0
- package/src/lib/eventstream-decoder.ts +253 -0
- package/src/lib/gcp-adc.ts +341 -0
- package/src/lib/injection-debug-log.ts +58 -0
- package/src/lib/open-url.ts +25 -0
- package/src/lib/pinned-http.ts +151 -0
- package/src/lib/privacy.ts +20 -0
- package/src/lib/process-control.ts +165 -0
- package/src/lib/provider-outbound.ts +170 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/redact.ts +105 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/service-secrets.ts +25 -0
- package/src/lib/shadow-call.ts +30 -0
- package/src/lib/sidecar-tracker.ts +52 -0
- package/src/lib/sse-decoder.ts +323 -0
- package/src/lib/state-store-registrations.ts +109 -0
- package/src/lib/state-store-sweeper.ts +184 -0
- package/src/lib/test-home-guard.ts +90 -0
- package/src/lib/token-estimate.ts +69 -0
- package/src/lib/translator-budget.ts +356 -0
- package/src/lib/upstream-retry.ts +239 -0
- package/src/lib/win-exec.ts +115 -0
- package/src/lib/win-paths.ts +68 -0
- package/src/lib/windows-elevation.ts +705 -0
- package/src/lib/windows-secret-acl.ts +514 -0
- package/src/lib/winsw.ts +375 -0
- package/src/oauth/anthropic-routing.ts +594 -0
- package/src/oauth/anthropic.ts +177 -0
- package/src/oauth/callback-server.ts +294 -0
- package/src/oauth/chatgpt.ts +150 -0
- package/src/oauth/cursor.ts +211 -0
- package/src/oauth/github-copilot.ts +428 -0
- package/src/oauth/google-antigravity.ts +230 -0
- package/src/oauth/health.ts +399 -0
- package/src/oauth/index.ts +1174 -0
- package/src/oauth/key-providers.ts +108 -0
- package/src/oauth/kimi.ts +213 -0
- package/src/oauth/kiro-credentials.ts +726 -0
- package/src/oauth/kiro.ts +577 -0
- package/src/oauth/local-token-detect.ts +121 -0
- package/src/oauth/log.ts +48 -0
- package/src/oauth/login-cli.ts +163 -0
- package/src/oauth/pkce.ts +15 -0
- package/src/oauth/store.ts +630 -0
- package/src/oauth/token-guardian.ts +303 -0
- package/src/oauth/types.ts +62 -0
- package/src/oauth/xai.ts +241 -0
- package/src/pi/extensions.ts +72 -0
- package/src/pi/home.ts +42 -0
- package/src/pi/index.ts +40 -0
- package/src/pi/models.ts +278 -0
- package/src/pi/packages.ts +219 -0
- package/src/pi/settings.ts +365 -0
- package/src/pi/status.ts +68 -0
- package/src/pi/sync.ts +75 -0
- package/src/providers/alibaba-region-backup.ts +75 -0
- package/src/providers/alibaba-region-migration.ts +156 -0
- package/src/providers/alibaba-region-startup.ts +36 -0
- package/src/providers/antigravity-models.ts +205 -0
- package/src/providers/api-keys.ts +140 -0
- package/src/providers/base-url-choices.ts +64 -0
- package/src/providers/context-cap.ts +65 -0
- package/src/providers/derive.ts +339 -0
- package/src/providers/free-directory.ts +184 -0
- package/src/providers/github-copilot-transport.ts +56 -0
- package/src/providers/key-failover.ts +203 -0
- package/src/providers/kiro-models.ts +67 -0
- package/src/providers/label.ts +19 -0
- package/src/providers/model-discovery.ts +356 -0
- package/src/providers/openai-sidecar.ts +175 -0
- package/src/providers/openai-tier-startup.ts +27 -0
- package/src/providers/openai-tiers.ts +301 -0
- package/src/providers/openai-virtual-models.ts +82 -0
- package/src/providers/openrouter-routing.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +150 -0
- package/src/providers/quota.ts +1260 -0
- package/src/providers/registry.ts +1600 -0
- package/src/providers/slug-codec.ts +67 -0
- package/src/providers/xai-transport.ts +141 -0
- package/src/reasoning-effort.ts +135 -0
- package/src/responses/compaction.ts +117 -0
- package/src/responses/parser.ts +656 -0
- package/src/responses/reasoning-envelope.ts +52 -0
- package/src/responses/schema.ts +159 -0
- package/src/responses/spill-store.ts +394 -0
- package/src/responses/state.ts +895 -0
- package/src/responses/tool-groups.ts +19 -0
- package/src/router.ts +425 -0
- package/src/server/adapter-resolve.ts +80 -0
- package/src/server/auth-cors.ts +530 -0
- package/src/server/chat-completions.ts +368 -0
- package/src/server/claude-messages.ts +914 -0
- package/src/server/effort-policy.ts +172 -0
- package/src/server/gui-static.ts +123 -0
- package/src/server/image-retry.ts +42 -0
- package/src/server/images.ts +476 -0
- package/src/server/index.ts +1126 -0
- package/src/server/lifecycle.ts +227 -0
- package/src/server/live.ts +598 -0
- package/src/server/management/agent-settings-routes.ts +1169 -0
- package/src/server/management/api-access.ts +141 -0
- package/src/server/management/api-key-usage.ts +167 -0
- package/src/server/management/body.ts +35 -0
- package/src/server/management/clients-routes.ts +63 -0
- package/src/server/management/cloud-sync-routes.ts +266 -0
- package/src/server/management/combo-routes.ts +220 -0
- package/src/server/management/config-routes.ts +422 -0
- package/src/server/management/context.ts +31 -0
- package/src/server/management/logs-usage-routes.ts +707 -0
- package/src/server/management/model-routes.ts +525 -0
- package/src/server/management/oauth-account-routes.ts +563 -0
- package/src/server/management/provider-routes.ts +556 -0
- package/src/server/management/shared.ts +277 -0
- package/src/server/management/sidebar-routes.ts +90 -0
- package/src/server/management/system-restart.ts +179 -0
- package/src/server/management/system-routes.ts +117 -0
- package/src/server/management/usage-summary-cache.ts +86 -0
- package/src/server/management-api.ts +215 -0
- package/src/server/management-auth.ts +267 -0
- package/src/server/memory-watchdog.ts +156 -0
- package/src/server/port-reclaim.ts +307 -0
- package/src/server/ports.ts +116 -0
- package/src/server/proxy-liveness.ts +201 -0
- package/src/server/relay-eager.ts +313 -0
- package/src/server/relay.ts +1049 -0
- package/src/server/request-decompress.ts +132 -0
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +1046 -0
- package/src/server/responses/collaboration.ts +354 -0
- package/src/server/responses/compact.ts +384 -0
- package/src/server/responses/core.ts +2758 -0
- package/src/server/responses/encrypted-payload.ts +308 -0
- package/src/server/responses/fetch-helpers.ts +157 -0
- package/src/server/responses/passthrough-error.ts +78 -0
- package/src/server/responses/terminal-guard.ts +230 -0
- package/src/server/responses/upstream-error.ts +48 -0
- package/src/server/responses-image-gen-repair.ts +132 -0
- package/src/server/responses-item-id-repair.ts +224 -0
- package/src/server/responses.ts +9 -0
- package/src/server/search.ts +136 -0
- package/src/server/sse-payload-rewrite.ts +175 -0
- package/src/server/startup-action-control.ts +308 -0
- package/src/server/startup-health-cache.ts +113 -0
- package/src/server/system-env.ts +413 -0
- package/src/server/windows-tcp-drop.ts +184 -0
- package/src/server/windows-tray-control.ts +41 -0
- package/src/server/ws-bridge.ts +471 -0
- package/src/service.ts +2554 -0
- package/src/stall-timeout.ts +20 -0
- package/src/storage/cleanup-job.ts +57 -0
- package/src/storage/cleanup.ts +3085 -0
- package/src/storage/policy-job.ts +457 -0
- package/src/storage/policy-scheduler.ts +40 -0
- package/src/storage/policy-worker.ts +59 -0
- package/src/storage/policy.ts +527 -0
- package/src/storage/restore-job.ts +299 -0
- package/src/storage/restore-worker.ts +58 -0
- package/src/storage/scanner.ts +238 -0
- package/src/storage/storage-mutation-coordinator.ts +139 -0
- package/src/storage/worker-lifecycle.ts +215 -0
- package/src/tray/assets/opencodex-tray-offline.ico +0 -0
- package/src/tray/assets/opencodex-tray-online.ico +0 -0
- package/src/tray/assets/opencodex-tray-warning.ico +0 -0
- package/src/tray/assets/opencodex-tray.png +0 -0
- package/src/tray/windows-tray.ps1 +290 -0
- package/src/tray/windows.ts +730 -0
- package/src/types.ts +1237 -0
- package/src/update/badge.ts +72 -0
- package/src/update/index.ts +407 -0
- package/src/update/job.ts +1520 -0
- package/src/update/notify.ts +257 -0
- package/src/update/npm-invocation.d.mts +23 -0
- package/src/update/npm-invocation.mjs +94 -0
- package/src/update/tray-update-plan.d.mts +18 -0
- package/src/update/tray-update-plan.mjs +38 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/debug.ts +97 -0
- package/src/usage/expected-prices.ts +164 -0
- package/src/usage/log.ts +658 -0
- package/src/usage/summary.ts +585 -0
- package/src/usage/totals.ts +14 -0
- package/src/vision/anthropic-describe.ts +185 -0
- package/src/vision/describe.ts +125 -0
- package/src/vision/index.ts +467 -0
- package/src/web-search/anthropic-executor.ts +189 -0
- package/src/web-search/executor.ts +105 -0
- package/src/web-search/format-result.ts +89 -0
- package/src/web-search/index.ts +196 -0
- package/src/web-search/loop.ts +664 -0
- package/src/web-search/parse.ts +220 -0
- package/src/web-search/progress-stream.ts +342 -0
- package/src/web-search/synthetic-tool.ts +47 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import type { AdapterFetchContext, AdapterRequest } from "./base";
|
|
2
|
+
import { classifyKiroHttpError, safeKiroHttpErrorMessage } from "./kiro-errors";
|
|
3
|
+
import { normalizeUpstreamHttpErrorResponse } from "./upstream-http-error";
|
|
4
|
+
import { readBoundedResponseBody } from "../lib/bounded-body";
|
|
5
|
+
import { resolveClientRetryAfter } from "../lib/retry-after";
|
|
6
|
+
import { parseRetryAfterMs } from "../combos";
|
|
7
|
+
import {
|
|
8
|
+
abortError,
|
|
9
|
+
cancelResponseBodyBestEffort,
|
|
10
|
+
fetchWithAttemptDeadline,
|
|
11
|
+
isConnectionResetError,
|
|
12
|
+
retryBackoffDelayMs,
|
|
13
|
+
sleepWithAbort,
|
|
14
|
+
} from "../lib/upstream-retry";
|
|
15
|
+
|
|
16
|
+
const RESET_ATTEMPTS = 3;
|
|
17
|
+
const RESET_RETRY_BASE_MS = 150;
|
|
18
|
+
const RESET_RETRY_MAX_MS = 1_000;
|
|
19
|
+
const THROTTLE_ATTEMPTS = 3;
|
|
20
|
+
const CANONICAL_RUNTIME_HOST = /^runtime\.([a-z]{2}(?:-[a-z]+)+-\d)\.kiro\.dev$/i;
|
|
21
|
+
const ENDPOINT_ERROR_MARKERS = [
|
|
22
|
+
"unknownoperation",
|
|
23
|
+
"unknown operation",
|
|
24
|
+
"invalidsignature",
|
|
25
|
+
"invalid signature",
|
|
26
|
+
"endpoint not found",
|
|
27
|
+
"unsupported endpoint",
|
|
28
|
+
];
|
|
29
|
+
const CONNECT_ERROR_CODES = new Set(["ENOTFOUND", "EAI_AGAIN", "ECONNREFUSED", "ENETUNREACH", "EHOSTUNREACH"]);
|
|
30
|
+
|
|
31
|
+
interface KiroThrottleProbe {
|
|
32
|
+
token: symbol;
|
|
33
|
+
promise: Promise<void>;
|
|
34
|
+
release: () => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let kiroThrottleCooldownUntil = 0;
|
|
38
|
+
let kiroThrottleProbe: KiroThrottleProbe | undefined;
|
|
39
|
+
|
|
40
|
+
function newKiroThrottleProbe(): KiroThrottleProbe {
|
|
41
|
+
let release!: () => void;
|
|
42
|
+
const promise = new Promise<void>(resolve => { release = resolve; });
|
|
43
|
+
const probe = { token: Symbol("kiro-throttle-probe"), promise, release };
|
|
44
|
+
kiroThrottleProbe = probe;
|
|
45
|
+
return probe;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function releaseKiroThrottleProbe(token: symbol | undefined): void {
|
|
49
|
+
if (!token || kiroThrottleProbe?.token !== token) return;
|
|
50
|
+
const probe = kiroThrottleProbe;
|
|
51
|
+
kiroThrottleProbe = undefined;
|
|
52
|
+
probe.release();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function waitWithAbort(promise: Promise<void>, signal?: AbortSignal): Promise<void> {
|
|
56
|
+
if (!signal) return promise;
|
|
57
|
+
if (signal.aborted) throw abortError(signal);
|
|
58
|
+
let onAbort!: () => void;
|
|
59
|
+
const aborted = new Promise<never>((_resolve, reject) => {
|
|
60
|
+
onAbort = () => reject(abortError(signal));
|
|
61
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
62
|
+
});
|
|
63
|
+
try {
|
|
64
|
+
await Promise.race([promise, aborted]);
|
|
65
|
+
} finally {
|
|
66
|
+
signal.removeEventListener("abort", onAbort);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function waitForKiroCooldown(signal?: AbortSignal): Promise<void> {
|
|
71
|
+
// Re-check after every sleep because another throttled request can extend the shared deadline.
|
|
72
|
+
while (true) {
|
|
73
|
+
const remaining = kiroThrottleCooldownUntil - Date.now();
|
|
74
|
+
if (remaining <= 0) return;
|
|
75
|
+
await sleepWithAbort(remaining, signal);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function enterKiroThrottleGate(signal?: AbortSignal): Promise<symbol | undefined> {
|
|
80
|
+
while (true) {
|
|
81
|
+
const active = kiroThrottleProbe;
|
|
82
|
+
if (active) {
|
|
83
|
+
await waitWithAbort(active.promise, signal);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (kiroThrottleCooldownUntil <= Date.now()) return undefined;
|
|
87
|
+
const owned = newKiroThrottleProbe();
|
|
88
|
+
try {
|
|
89
|
+
await waitForKiroCooldown(signal);
|
|
90
|
+
return owned.token;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
releaseKiroThrottleProbe(owned.token);
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function claimKiroThrottleProbe(
|
|
99
|
+
delayMs: number,
|
|
100
|
+
currentToken?: symbol,
|
|
101
|
+
): { token?: symbol; wait?: Promise<void> } {
|
|
102
|
+
kiroThrottleCooldownUntil = Math.max(kiroThrottleCooldownUntil, Date.now() + Math.max(0, delayMs));
|
|
103
|
+
if (currentToken && kiroThrottleProbe?.token === currentToken) return { token: currentToken };
|
|
104
|
+
if (!kiroThrottleProbe) return { token: newKiroThrottleProbe().token };
|
|
105
|
+
return { wait: kiroThrottleProbe.promise };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Stream-level throttles arrive after HTTP 200, so record their cooldown for the next client retry. */
|
|
109
|
+
export function noteKiroTransientThrottle(delayMs = 2_000): void {
|
|
110
|
+
kiroThrottleCooldownUntil = Math.max(kiroThrottleCooldownUntil, Date.now() + Math.max(0, delayMs));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function resetKiroThrottleStateForTests(): void {
|
|
114
|
+
const active = kiroThrottleProbe;
|
|
115
|
+
kiroThrottleProbe = undefined;
|
|
116
|
+
kiroThrottleCooldownUntil = 0;
|
|
117
|
+
active?.release();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function errorChain(error: unknown): Error[] {
|
|
121
|
+
const chain: Error[] = [];
|
|
122
|
+
let current = error;
|
|
123
|
+
const seen = new Set<unknown>();
|
|
124
|
+
while (current instanceof Error && !seen.has(current)) {
|
|
125
|
+
seen.add(current);
|
|
126
|
+
chain.push(current);
|
|
127
|
+
current = (current as Error & { cause?: unknown }).cause;
|
|
128
|
+
}
|
|
129
|
+
return chain;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function endpointConnectFailure(error: unknown): boolean {
|
|
133
|
+
return errorChain(error).some(item => {
|
|
134
|
+
if (item.name === "AbortError" || item.name === "TimeoutError") return false;
|
|
135
|
+
const code = (item as Error & { code?: unknown }).code;
|
|
136
|
+
if (typeof code === "string" && CONNECT_ERROR_CODES.has(code)) return true;
|
|
137
|
+
const message = item.message.toLowerCase();
|
|
138
|
+
return message.includes("dns")
|
|
139
|
+
|| message.includes("name resolution")
|
|
140
|
+
|| message.includes("failed to lookup address")
|
|
141
|
+
|| message.includes("connection refused")
|
|
142
|
+
|| message.includes("failed to connect")
|
|
143
|
+
|| message.includes("connect error");
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function legacyUrl(requestUrl: string): string | undefined {
|
|
148
|
+
let url: URL;
|
|
149
|
+
try { url = new URL(requestUrl); } catch { return undefined; }
|
|
150
|
+
const match = CANONICAL_RUNTIME_HOST.exec(url.hostname);
|
|
151
|
+
if (!match || url.pathname !== "/" || url.search || url.hash) return undefined;
|
|
152
|
+
url.hostname = `q.${match[1]}.amazonaws.com`;
|
|
153
|
+
return url.toString();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function fetchWithResetRecovery(
|
|
157
|
+
request: AdapterRequest,
|
|
158
|
+
url: string,
|
|
159
|
+
ctx: AdapterFetchContext,
|
|
160
|
+
timeoutMs: number,
|
|
161
|
+
): Promise<Response> {
|
|
162
|
+
let lastError: unknown;
|
|
163
|
+
for (let attempt = 0; attempt < RESET_ATTEMPTS; attempt++) {
|
|
164
|
+
if (ctx.abortSignal?.aborted) throw abortError(ctx.abortSignal);
|
|
165
|
+
try {
|
|
166
|
+
const headers = new Headers(request.headers);
|
|
167
|
+
const recovered = attempt > 0;
|
|
168
|
+
if (recovered) headers.set("connection", "close");
|
|
169
|
+
return await fetchWithAttemptDeadline(url, {
|
|
170
|
+
method: request.method,
|
|
171
|
+
headers,
|
|
172
|
+
body: request.body,
|
|
173
|
+
...(recovered ? { keepalive: false } : {}),
|
|
174
|
+
}, timeoutMs, ctx.abortSignal, ctx.stream);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (ctx.abortSignal?.aborted || !isConnectionResetError(error) || attempt === RESET_ATTEMPTS - 1) throw error;
|
|
177
|
+
lastError = error;
|
|
178
|
+
await sleepWithAbort(retryBackoffDelayMs(attempt, {
|
|
179
|
+
baseDelayMs: RESET_RETRY_BASE_MS,
|
|
180
|
+
maxDelayMs: RESET_RETRY_MAX_MS,
|
|
181
|
+
}), ctx.abortSignal);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
throw lastError ?? new Error("Kiro fetch failed");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function inspectEndpointHttpFailure(
|
|
188
|
+
response: Response,
|
|
189
|
+
signal?: AbortSignal,
|
|
190
|
+
): Promise<{ response: Response; fallback: boolean }> {
|
|
191
|
+
if (response.status === 404 || response.status === 405) return { response, fallback: true };
|
|
192
|
+
if (response.status !== 400 && response.status !== 403) return { response, fallback: false };
|
|
193
|
+
|
|
194
|
+
const body = await readBoundedResponseBody(response, { signal });
|
|
195
|
+
const headers = new Headers(response.headers);
|
|
196
|
+
headers.delete("content-encoding");
|
|
197
|
+
headers.delete("content-length");
|
|
198
|
+
const rebuilt = new Response(body.displaySafe ? body.text : "", {
|
|
199
|
+
status: response.status,
|
|
200
|
+
statusText: response.statusText,
|
|
201
|
+
headers,
|
|
202
|
+
});
|
|
203
|
+
const text = body.displaySafe ? body.text.toLowerCase() : "";
|
|
204
|
+
return { response: rebuilt, fallback: ENDPOINT_ERROR_MARKERS.some(marker => text.includes(marker)) };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function normalizeFinalKiroHttpError(res: Response, signal?: AbortSignal): Promise<Response> {
|
|
208
|
+
return normalizeUpstreamHttpErrorResponse(res, {
|
|
209
|
+
signal,
|
|
210
|
+
formatMessage: payloadText => safeKiroHttpErrorMessage(res.status, res.headers, payloadText),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function inspectKiroThrottle(
|
|
215
|
+
response: Response,
|
|
216
|
+
signal?: AbortSignal,
|
|
217
|
+
): Promise<{ response: Response; transient: boolean; delayMs: number } | undefined> {
|
|
218
|
+
if (response.status !== 429) return undefined;
|
|
219
|
+
const body = await readBoundedResponseBody(response, { signal });
|
|
220
|
+
const headers = new Headers(response.headers);
|
|
221
|
+
headers.delete("content-encoding");
|
|
222
|
+
headers.delete("content-length");
|
|
223
|
+
const payloadText = body.displaySafe ? body.text : "";
|
|
224
|
+
const rebuilt = new Response(payloadText, {
|
|
225
|
+
status: response.status,
|
|
226
|
+
statusText: response.statusText,
|
|
227
|
+
headers,
|
|
228
|
+
});
|
|
229
|
+
const failure = classifyKiroHttpError(response.status, headers, payloadText);
|
|
230
|
+
const retryAfter = resolveClientRetryAfter({
|
|
231
|
+
status: response.status,
|
|
232
|
+
message: failure.message,
|
|
233
|
+
upstreamRetryAfter: headers.get("retry-after"),
|
|
234
|
+
});
|
|
235
|
+
const delayMs = retryAfter === "0" ? 0 : parseRetryAfterMs(retryAfter) ?? 2_000;
|
|
236
|
+
return {
|
|
237
|
+
response: rebuilt,
|
|
238
|
+
transient: failure.retryable && failure.code === "rate_limit_exceeded",
|
|
239
|
+
delayMs,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function fetchKiroAttempt(
|
|
244
|
+
request: AdapterRequest,
|
|
245
|
+
ctx: AdapterFetchContext,
|
|
246
|
+
timeoutMs: number,
|
|
247
|
+
): Promise<Response> {
|
|
248
|
+
const legacy = legacyUrl(request.url);
|
|
249
|
+
let response: Response;
|
|
250
|
+
try {
|
|
251
|
+
response = await fetchWithResetRecovery(request, request.url, ctx, timeoutMs);
|
|
252
|
+
} catch (error) {
|
|
253
|
+
if (!legacy || !endpointConnectFailure(error)) throw error;
|
|
254
|
+
return fetchWithResetRecovery(request, legacy, ctx, timeoutMs);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (legacy && !response.ok) {
|
|
258
|
+
const inspected = await inspectEndpointHttpFailure(response, ctx.abortSignal);
|
|
259
|
+
response = inspected.response;
|
|
260
|
+
if (inspected.fallback) {
|
|
261
|
+
cancelResponseBodyBestEffort(response);
|
|
262
|
+
response = await fetchWithResetRecovery(request, legacy, ctx, timeoutMs);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return response;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Kiro owns replay-safe reset recovery, one endpoint fallback, and bounded process-wide transient
|
|
270
|
+
* throttle recovery. The shared probe starts only after a 429, so healthy parallel traffic remains
|
|
271
|
+
* parallel while a throttled account cannot burn every caller's independent retry budget (#532).
|
|
272
|
+
*/
|
|
273
|
+
export async function fetchKiroWithRetry(request: AdapterRequest, ctx: AdapterFetchContext = {}): Promise<Response> {
|
|
274
|
+
const timeoutMs = ctx.timeoutMs ?? 200_000;
|
|
275
|
+
let probeToken: symbol | undefined;
|
|
276
|
+
try {
|
|
277
|
+
for (let attempt = 0; attempt < THROTTLE_ATTEMPTS; attempt++) {
|
|
278
|
+
if (!probeToken) probeToken = await enterKiroThrottleGate(ctx.abortSignal);
|
|
279
|
+
else await waitForKiroCooldown(ctx.abortSignal);
|
|
280
|
+
|
|
281
|
+
const response = await fetchKiroAttempt(request, ctx, timeoutMs);
|
|
282
|
+
const throttle = await inspectKiroThrottle(response, ctx.abortSignal);
|
|
283
|
+
if (!throttle || !throttle.transient) {
|
|
284
|
+
releaseKiroThrottleProbe(probeToken);
|
|
285
|
+
const finalResponse = throttle?.response ?? response;
|
|
286
|
+
return ctx.returnRawErrors
|
|
287
|
+
? finalResponse
|
|
288
|
+
: normalizeFinalKiroHttpError(finalResponse, ctx.abortSignal);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const claim = claimKiroThrottleProbe(throttle.delayMs, probeToken);
|
|
292
|
+
if (!claim.token) {
|
|
293
|
+
cancelResponseBodyBestEffort(throttle.response);
|
|
294
|
+
await waitWithAbort(claim.wait!, ctx.abortSignal);
|
|
295
|
+
probeToken = undefined;
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
probeToken = claim.token;
|
|
299
|
+
if (attempt === THROTTLE_ATTEMPTS - 1) {
|
|
300
|
+
releaseKiroThrottleProbe(probeToken);
|
|
301
|
+
return ctx.returnRawErrors
|
|
302
|
+
? throttle.response
|
|
303
|
+
: normalizeFinalKiroHttpError(throttle.response, ctx.abortSignal);
|
|
304
|
+
}
|
|
305
|
+
cancelResponseBodyBestEffort(throttle.response);
|
|
306
|
+
}
|
|
307
|
+
throw new Error("Kiro throttle retry loop exhausted without a response");
|
|
308
|
+
} catch (error) {
|
|
309
|
+
releaseKiroThrottleProbe(probeToken);
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { AdapterEvent } from "../types";
|
|
2
|
+
import type { TranslatorBudget } from "../lib/translator-budget";
|
|
3
|
+
|
|
4
|
+
type ThinkingTag = "<thinking>" | "<think>" | "<reasoning>";
|
|
5
|
+
type ParserState = "pre" | "thinking" | "streaming";
|
|
6
|
+
|
|
7
|
+
const OPEN_TAGS: ThinkingTag[] = ["<thinking>", "<think>", "<reasoning>"];
|
|
8
|
+
const MAX_OPEN_TAG = Math.max(...OPEN_TAGS.map(t => t.length));
|
|
9
|
+
const MAX_CLOSE_TAG = Math.max(...OPEN_TAGS.map(t => `</${t.slice(1)}`.length));
|
|
10
|
+
|
|
11
|
+
function closeTagFor(openTag: ThinkingTag): string {
|
|
12
|
+
return `</${openTag.slice(1)}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function isPossibleOpenTagPrefix(text: string): boolean {
|
|
16
|
+
return OPEN_TAGS.some(tag => tag.startsWith(text) && text.length < tag.length);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class KiroThinkingParser {
|
|
20
|
+
private state: ParserState = "pre";
|
|
21
|
+
private preBuffer = "";
|
|
22
|
+
private thinkingBuffer = "";
|
|
23
|
+
private closeTag = "";
|
|
24
|
+
|
|
25
|
+
constructor(private readonly budget?: TranslatorBudget) {}
|
|
26
|
+
|
|
27
|
+
private replaceCarry(field: "preBuffer" | "thinkingBuffer", next: string): void {
|
|
28
|
+
const previous = this[field];
|
|
29
|
+
if (previous === next) return;
|
|
30
|
+
const previousBytes = Buffer.byteLength(previous);
|
|
31
|
+
const nextBytes = Buffer.byteLength(next);
|
|
32
|
+
const reservation = this.budget?.reserveTransient(nextBytes, { kind: "reasoning" });
|
|
33
|
+
this[field] = next;
|
|
34
|
+
reservation?.commitRetained();
|
|
35
|
+
this.budget?.releaseRetained(previousBytes, { kind: "reasoning" });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
feed(text: string): AdapterEvent[] {
|
|
39
|
+
if (!text) return [];
|
|
40
|
+
if (this.state === "streaming") return [{ type: "text_delta", text }];
|
|
41
|
+
if (this.state === "thinking") {
|
|
42
|
+
this.replaceCarry("thinkingBuffer", this.thinkingBuffer + text);
|
|
43
|
+
return this.drainThinking();
|
|
44
|
+
}
|
|
45
|
+
this.replaceCarry("preBuffer", this.preBuffer + text);
|
|
46
|
+
const stripped = this.preBuffer.trimStart();
|
|
47
|
+
const openTag = OPEN_TAGS.find(tag => stripped.startsWith(tag));
|
|
48
|
+
if (openTag) {
|
|
49
|
+
this.state = "thinking";
|
|
50
|
+
this.closeTag = closeTagFor(openTag);
|
|
51
|
+
this.replaceCarry("thinkingBuffer", stripped.slice(openTag.length));
|
|
52
|
+
this.replaceCarry("preBuffer", "");
|
|
53
|
+
return this.drainThinking();
|
|
54
|
+
}
|
|
55
|
+
if (stripped.length <= MAX_OPEN_TAG && isPossibleOpenTagPrefix(stripped)) return [];
|
|
56
|
+
this.state = "streaming";
|
|
57
|
+
const out = this.preBuffer;
|
|
58
|
+
this.replaceCarry("preBuffer", "");
|
|
59
|
+
return out ? [{ type: "text_delta", text: out }] : [];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
flush(): AdapterEvent[] {
|
|
63
|
+
if (this.state === "thinking") {
|
|
64
|
+
const out = this.thinkingBuffer;
|
|
65
|
+
this.replaceCarry("thinkingBuffer", "");
|
|
66
|
+
this.state = "streaming";
|
|
67
|
+
return out ? [{ type: "reasoning_raw_delta", text: out }] : [];
|
|
68
|
+
}
|
|
69
|
+
if (this.preBuffer) {
|
|
70
|
+
const out = this.preBuffer;
|
|
71
|
+
this.replaceCarry("preBuffer", "");
|
|
72
|
+
this.state = "streaming";
|
|
73
|
+
return [{ type: "text_delta", text: out }];
|
|
74
|
+
}
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private drainThinking(): AdapterEvent[] {
|
|
79
|
+
const close = this.closeTag;
|
|
80
|
+
const idx = this.thinkingBuffer.indexOf(close);
|
|
81
|
+
if (idx >= 0) {
|
|
82
|
+
const thinking = this.thinkingBuffer.slice(0, idx);
|
|
83
|
+
const after = this.thinkingBuffer.slice(idx + close.length).trimStart();
|
|
84
|
+
this.replaceCarry("thinkingBuffer", "");
|
|
85
|
+
this.state = "streaming";
|
|
86
|
+
const events: AdapterEvent[] = [];
|
|
87
|
+
if (thinking) events.push({ type: "reasoning_raw_delta", text: thinking });
|
|
88
|
+
if (after) events.push({ type: "text_delta", text: after });
|
|
89
|
+
return events;
|
|
90
|
+
}
|
|
91
|
+
if (this.thinkingBuffer.length <= MAX_CLOSE_TAG) return [];
|
|
92
|
+
const send = this.thinkingBuffer.slice(0, -MAX_CLOSE_TAG);
|
|
93
|
+
this.replaceCarry("thinkingBuffer", this.thinkingBuffer.slice(-MAX_CLOSE_TAG));
|
|
94
|
+
return send ? [{ type: "reasoning_raw_delta", text: send }] : [];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { OcxContentPart, OcxToolCall, OcxToolResultMessage } from "../types";
|
|
2
|
+
import { normalizeToolId } from "./kiro-wire";
|
|
3
|
+
|
|
4
|
+
function stringifyValue(value: unknown): string {
|
|
5
|
+
try { return JSON.stringify(value); } catch { return String(value); }
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function contentText(content: string | OcxContentPart[]): string {
|
|
9
|
+
if (typeof content === "string") return content;
|
|
10
|
+
return content
|
|
11
|
+
.map(part => {
|
|
12
|
+
if (part.type === "text") return part.text;
|
|
13
|
+
if (part.type === "image") return `[image:${part.detail ?? "auto"}]`;
|
|
14
|
+
return "";
|
|
15
|
+
})
|
|
16
|
+
.filter(Boolean)
|
|
17
|
+
.join("\n");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function appendFallbackText(base: string, fallback: string): string {
|
|
21
|
+
return [base, fallback].filter(Boolean).join("\n\n");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function toolCallFallbackText(toolCall: OcxToolCall): string {
|
|
25
|
+
return [
|
|
26
|
+
`Tool call fallback (${toolCall.name}, id ${normalizeToolId(toolCall.id)}):`,
|
|
27
|
+
stringifyValue(toolCall.arguments ?? {}),
|
|
28
|
+
].join("\n");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function toolResultFallbackText(toolResult: OcxToolResultMessage): string {
|
|
32
|
+
return [
|
|
33
|
+
`Tool result fallback (${toolResult.toolName}, id ${normalizeToolId(toolResult.toolCallId)}, ${toolResult.isError ? "error" : "success"}):`,
|
|
34
|
+
contentText(toolResult.content) || "(empty)",
|
|
35
|
+
].join("\n");
|
|
36
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import type { OcxParsedRequest, OcxTool } from "../types";
|
|
2
|
+
import { namespacedToolName } from "../types";
|
|
3
|
+
import { normalizeKiroModelId } from "../providers/kiro-models";
|
|
4
|
+
import { createKiroToolNameRegistry, type KiroToolNameRegistry } from "./kiro-wire";
|
|
5
|
+
|
|
6
|
+
const MAX_KIRO_TOOL_DESCRIPTION_UNVERIFIED = 1024;
|
|
7
|
+
const MAX_KIRO_TOOL_DESCRIPTION_GPT_56_SOL = 9_216;
|
|
8
|
+
// Issue #719's successful Kiro probe used 49 outbound tools at about 108 KiB. Leave one of those
|
|
9
|
+
// slots for Kiro's private completion tool and headroom for the enclosing request/message fields.
|
|
10
|
+
export const MAX_KIRO_TOOL_COUNT = 48;
|
|
11
|
+
export const MAX_KIRO_TOOL_CATALOG_BYTES = 96_000;
|
|
12
|
+
const textEncoder = new TextEncoder();
|
|
13
|
+
|
|
14
|
+
// JSON Schema validation/annotation keywords that Kiro's runtimeservice tool-spec validator
|
|
15
|
+
// rejects ("ValidationException: Invalid tool use format."). Codex's built-in tools omit these,
|
|
16
|
+
// but the `memories__*` tools (add_ad_hoc_note/read/search/list) emit pattern/length/range
|
|
17
|
+
// constraints via schemars, which trip the validator. Strip them everywhere in the schema tree;
|
|
18
|
+
// the constraints are advisory for the model, so dropping them does not change tool behavior.
|
|
19
|
+
const KIRO_REJECTED_SCHEMA_KEYS = new Set([
|
|
20
|
+
"additionalProperties",
|
|
21
|
+
"pattern",
|
|
22
|
+
"format",
|
|
23
|
+
"minLength",
|
|
24
|
+
"maxLength",
|
|
25
|
+
"minimum",
|
|
26
|
+
"maximum",
|
|
27
|
+
"exclusiveMinimum",
|
|
28
|
+
"exclusiveMaximum",
|
|
29
|
+
"multipleOf",
|
|
30
|
+
"minItems",
|
|
31
|
+
"maxItems",
|
|
32
|
+
"uniqueItems",
|
|
33
|
+
"minProperties",
|
|
34
|
+
"maxProperties",
|
|
35
|
+
"contentEncoding",
|
|
36
|
+
"contentMediaType",
|
|
37
|
+
"$schema",
|
|
38
|
+
// Validation-only composition/applicator keywords that Bedrock/Kiro do not support. Unlike
|
|
39
|
+
// `properties`/`$defs`, these are not plain property->schema maps the model needs, so they are
|
|
40
|
+
// dropped outright rather than recursed into.
|
|
41
|
+
"patternProperties",
|
|
42
|
+
"propertyNames",
|
|
43
|
+
"dependentSchemas",
|
|
44
|
+
"dependentRequired",
|
|
45
|
+
"if",
|
|
46
|
+
"then",
|
|
47
|
+
"else",
|
|
48
|
+
"contains",
|
|
49
|
+
"unevaluatedProperties",
|
|
50
|
+
"unevaluatedItems",
|
|
51
|
+
// Codex's Responses-only `encrypted: true` marker (openai/codex 5f4d06ef) stamped on v2
|
|
52
|
+
// collaboration tool schemas. Kiro/Bedrock validators reject a narrower, undocumented schema
|
|
53
|
+
// subset (issue #85 class); the marker is a ChatGPT-backend annotation with no meaning here.
|
|
54
|
+
"encrypted",
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
// Keys whose values are maps of *property/definition name -> schema* (not schema keywords). Their
|
|
58
|
+
// child keys must never be treated as schema keywords, or a legitimate property named e.g.
|
|
59
|
+
// "format"/"pattern" would be deleted. We recurse into the value schemas but keep every name intact.
|
|
60
|
+
const SCHEMA_MAP_KEYS = new Set(["properties", "$defs", "definitions"]);
|
|
61
|
+
|
|
62
|
+
function sanitizeSchemaMap(value: unknown): unknown {
|
|
63
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return sanitizeKiroSchema(value);
|
|
64
|
+
const out: Record<string, unknown> = {};
|
|
65
|
+
for (const [name, child] of Object.entries(value as Record<string, unknown>)) {
|
|
66
|
+
out[name] = sanitizeKiroSchema(child);
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function sanitizeKiroSchema(value: unknown): unknown {
|
|
72
|
+
if (Array.isArray(value)) return value.map(sanitizeKiroSchema);
|
|
73
|
+
if (!value || typeof value !== "object") return value;
|
|
74
|
+
const out: Record<string, unknown> = {};
|
|
75
|
+
for (const [key, child] of Object.entries(value as Record<string, unknown>)) {
|
|
76
|
+
if (KIRO_REJECTED_SCHEMA_KEYS.has(key)) continue;
|
|
77
|
+
if (key === "required" && Array.isArray(child) && child.length === 0) continue;
|
|
78
|
+
out[key] = SCHEMA_MAP_KEYS.has(key) ? sanitizeSchemaMap(child) : sanitizeKiroSchema(child);
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function ensureRootObjectType(schema: unknown): Record<string, unknown> {
|
|
84
|
+
const obj = schema && typeof schema === "object" && !Array.isArray(schema)
|
|
85
|
+
? schema as Record<string, unknown>
|
|
86
|
+
: {};
|
|
87
|
+
// Bedrock rejects oneOf/allOf/anyOf at the root ("input_schema does not support oneOf, allOf, or
|
|
88
|
+
// anyOf at the top level") and requires the root type to be "object". Flatten every root
|
|
89
|
+
// composition into the object schema while preserving the root's own properties/required and any
|
|
90
|
+
// other sibling keys. allOf merges required (AND); anyOf/oneOf drop required so a single valid
|
|
91
|
+
// branch still passes. Nested (non-root) composition is left intact — only the root is illegal.
|
|
92
|
+
const COMPOSITION_KEYS = ["oneOf", "anyOf", "allOf"] as const;
|
|
93
|
+
const hasComposition = COMPOSITION_KEYS.some(k => Array.isArray(obj[k]));
|
|
94
|
+
const t = obj.type;
|
|
95
|
+
const rootObjectType = t === "object" || (Array.isArray(t) && t.includes("object"));
|
|
96
|
+
if (!hasComposition) {
|
|
97
|
+
if (rootObjectType && t === "object") return obj;
|
|
98
|
+
return { ...obj, type: "object" };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const props: Record<string, unknown> = {};
|
|
102
|
+
const required = new Set<string>();
|
|
103
|
+
// Seed with the root's own properties/required so a schema like
|
|
104
|
+
// { type:"object", properties:{path}, required:["path"], oneOf:[...] } keeps them.
|
|
105
|
+
if (obj.properties && typeof obj.properties === "object") {
|
|
106
|
+
Object.assign(props, sanitizeKiroSchema(obj.properties) as Record<string, unknown>);
|
|
107
|
+
}
|
|
108
|
+
if (Array.isArray(obj.required)) {
|
|
109
|
+
for (const r of obj.required) if (typeof r === "string") required.add(r);
|
|
110
|
+
}
|
|
111
|
+
for (const key of COMPOSITION_KEYS) {
|
|
112
|
+
const variants = obj[key];
|
|
113
|
+
if (!Array.isArray(variants)) continue;
|
|
114
|
+
// allOf is conjunction: its required fields always apply. oneOf/anyOf are disjunction, so
|
|
115
|
+
// promoting their required would over-constrain a valid single-branch call.
|
|
116
|
+
const mergeRequired = key === "allOf";
|
|
117
|
+
for (const variant of variants) {
|
|
118
|
+
if (!variant || typeof variant !== "object" || Array.isArray(variant)) continue;
|
|
119
|
+
const v = variant as Record<string, unknown>;
|
|
120
|
+
if (v.properties && typeof v.properties === "object") {
|
|
121
|
+
Object.assign(props, sanitizeKiroSchema(v.properties) as Record<string, unknown>);
|
|
122
|
+
}
|
|
123
|
+
if (mergeRequired && Array.isArray(v.required)) {
|
|
124
|
+
for (const r of v.required) if (typeof r === "string") required.add(r);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Keep all non-composition sibling keys (description, $defs, definitions, etc.); replace
|
|
130
|
+
// type/properties/required with the flattened object form.
|
|
131
|
+
const merged: Record<string, unknown> = {};
|
|
132
|
+
for (const [key, child] of Object.entries(obj)) {
|
|
133
|
+
if (key === "oneOf" || key === "anyOf" || key === "allOf") continue;
|
|
134
|
+
if (key === "type" || key === "properties" || key === "required") continue;
|
|
135
|
+
merged[key] = child;
|
|
136
|
+
}
|
|
137
|
+
merged.type = "object";
|
|
138
|
+
if (Object.keys(props).length > 0) merged.properties = props;
|
|
139
|
+
if (required.size > 0) merged.required = [...required];
|
|
140
|
+
return merged;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function toolDescriptionLimit(modelId: string): number {
|
|
144
|
+
return normalizeKiroModelId(modelId) === "gpt-5.6-sol"
|
|
145
|
+
? MAX_KIRO_TOOL_DESCRIPTION_GPT_56_SOL
|
|
146
|
+
: MAX_KIRO_TOOL_DESCRIPTION_UNVERIFIED;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function truncateDescription(description: string, limit: number): string {
|
|
150
|
+
if (description.length <= limit) return description;
|
|
151
|
+
if (limit <= 1) return description.slice(0, limit);
|
|
152
|
+
return `${description.slice(0, limit - 1)}…`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function serializedToolCatalogBytes(tools: readonly unknown[]): number {
|
|
156
|
+
return textEncoder.encode(JSON.stringify(tools)).byteLength;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function omittedToolCatalogNotice(kept: number, omitted: readonly OcxTool[], registry: KiroToolNameRegistry): string {
|
|
160
|
+
const names = omitted.slice(0, 12).map(tool => registry.alias(namespacedToolName(tool.namespace, tool.name)));
|
|
161
|
+
const remainder = omitted.length - names.length;
|
|
162
|
+
const summary = `${names.join(", ")}${remainder > 0 ? `, and ${remainder} more` : ""}`;
|
|
163
|
+
return `[opencodex] Kiro's outbound catalog budget allows ${kept} of ${kept + omitted.length} client tools this turn. Omitted and unavailable this turn: ${summary}.`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function convertKiroToolContext(
|
|
167
|
+
parsed: OcxParsedRequest,
|
|
168
|
+
registry: KiroToolNameRegistry = createKiroToolNameRegistry(),
|
|
169
|
+
): { tools: unknown[]; systemAdditions: string[]; nameMap: Map<string, string>; registry: KiroToolNameRegistry } {
|
|
170
|
+
const tools = parsed.context.tools ?? [];
|
|
171
|
+
const descriptionLimit = toolDescriptionLimit(parsed.modelId);
|
|
172
|
+
// Validate every listed name even when tool_choice:none emulates a tool-free turn.
|
|
173
|
+
for (const tool of tools) registry.alias(namespacedToolName(tool.namespace, tool.name));
|
|
174
|
+
const effectiveTools = parsed.options.toolChoice === "none" ? [] : tools;
|
|
175
|
+
const convertedTools: unknown[] = [];
|
|
176
|
+
let omittedAt = effectiveTools.length;
|
|
177
|
+
for (const [index, tool] of effectiveTools.entries()) {
|
|
178
|
+
const description = tool.description || `Tool: ${tool.name}`;
|
|
179
|
+
// Send the full namespaced wire name (e.g. mcp__chrome-devtools__navigate_page) so Kiro echoes
|
|
180
|
+
// it back; the bridge's toolNsMap is keyed by this name and restores the MCP namespace Codex
|
|
181
|
+
// routes by. Kiro's runtimeservice rejects names with spaces or >64 chars, so normalize to a
|
|
182
|
+
// safe form and remember the mapping; the response parser restores the original wire name.
|
|
183
|
+
const wireName = namespacedToolName(tool.namespace, tool.name);
|
|
184
|
+
const toolName = registry.alias(wireName);
|
|
185
|
+
const converted = {
|
|
186
|
+
toolSpecification: {
|
|
187
|
+
name: toolName,
|
|
188
|
+
description: truncateDescription(description, descriptionLimit),
|
|
189
|
+
inputSchema: { json: ensureRootObjectType(sanitizeKiroSchema(tool.parameters ?? {})) },
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
// Preserve declaration order and only omit a suffix. Ranking tools would make a catalog change
|
|
193
|
+
// silently alter which capability disappears; this deterministic policy is paired with a
|
|
194
|
+
// model-visible omission notice so unavailable tools are explicit rather than assumed absent.
|
|
195
|
+
if (
|
|
196
|
+
convertedTools.length >= MAX_KIRO_TOOL_COUNT
|
|
197
|
+
|| serializedToolCatalogBytes([...convertedTools, converted]) > MAX_KIRO_TOOL_CATALOG_BYTES
|
|
198
|
+
) {
|
|
199
|
+
omittedAt = index;
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
convertedTools.push(converted);
|
|
203
|
+
}
|
|
204
|
+
const omittedTools = effectiveTools.slice(omittedAt);
|
|
205
|
+
return {
|
|
206
|
+
tools: convertedTools,
|
|
207
|
+
systemAdditions: omittedTools.length > 0 ? [omittedToolCatalogNotice(convertedTools.length, omittedTools, registry)] : [],
|
|
208
|
+
nameMap: registry.nameMap,
|
|
209
|
+
registry,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function convertKiroTools(parsed: OcxParsedRequest): unknown[] {
|
|
214
|
+
return convertKiroToolContext(parsed).tools;
|
|
215
|
+
}
|