@cjhyy/code-shell-core 0.5.0-rc.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 +162 -0
- package/dist/agent/coordinator.d.ts +49 -0
- package/dist/agent/coordinator.js +77 -0
- package/dist/arena/arena.d.ts +43 -0
- package/dist/arena/arena.js +334 -0
- package/dist/arena/context/context-tools.d.ts +16 -0
- package/dist/arena/context/context-tools.js +231 -0
- package/dist/arena/detect-mode.d.ts +20 -0
- package/dist/arena/detect-mode.js +78 -0
- package/dist/arena/digest-builder.d.ts +25 -0
- package/dist/arena/digest-builder.js +120 -0
- package/dist/arena/index.d.ts +29 -0
- package/dist/arena/index.js +29 -0
- package/dist/arena/iterate/convergence.d.ts +25 -0
- package/dist/arena/iterate/convergence.js +103 -0
- package/dist/arena/iterate/formats/index.d.ts +22 -0
- package/dist/arena/iterate/formats/index.js +283 -0
- package/dist/arena/iterate/index.d.ts +11 -0
- package/dist/arena/iterate/index.js +9 -0
- package/dist/arena/iterate/iterative-arena.d.ts +23 -0
- package/dist/arena/iterate/iterative-arena.js +237 -0
- package/dist/arena/iterate/parse.d.ts +42 -0
- package/dist/arena/iterate/parse.js +123 -0
- package/dist/arena/iterate/phases/argue.d.ts +22 -0
- package/dist/arena/iterate/phases/argue.js +159 -0
- package/dist/arena/iterate/phases/revise.d.ts +16 -0
- package/dist/arena/iterate/phases/revise.js +62 -0
- package/dist/arena/iterate/phases/tournament.d.ts +34 -0
- package/dist/arena/iterate/phases/tournament.js +113 -0
- package/dist/arena/iterate/tools/web-tools.d.ts +13 -0
- package/dist/arena/iterate/tools/web-tools.js +54 -0
- package/dist/arena/iterate/types.d.ts +152 -0
- package/dist/arena/iterate/types.js +8 -0
- package/dist/arena/ledger.d.ts +47 -0
- package/dist/arena/ledger.js +151 -0
- package/dist/arena/lenses/architecture.d.ts +5 -0
- package/dist/arena/lenses/architecture.js +22 -0
- package/dist/arena/lenses/engineering.d.ts +5 -0
- package/dist/arena/lenses/engineering.js +22 -0
- package/dist/arena/lenses/general.d.ts +5 -0
- package/dist/arena/lenses/general.js +20 -0
- package/dist/arena/lenses/index.d.ts +16 -0
- package/dist/arena/lenses/index.js +47 -0
- package/dist/arena/lenses/product.d.ts +5 -0
- package/dist/arena/lenses/product.js +22 -0
- package/dist/arena/model-presets.d.ts +23 -0
- package/dist/arena/model-presets.js +44 -0
- package/dist/arena/phases/adjudication.d.ts +24 -0
- package/dist/arena/phases/adjudication.js +144 -0
- package/dist/arena/phases/build-consensus.d.ts +29 -0
- package/dist/arena/phases/build-consensus.js +86 -0
- package/dist/arena/phases/claim-registry.d.ts +26 -0
- package/dist/arena/phases/claim-registry.js +60 -0
- package/dist/arena/phases/cross-review.d.ts +45 -0
- package/dist/arena/phases/cross-review.js +224 -0
- package/dist/arena/phases/debate-rounds.d.ts +27 -0
- package/dist/arena/phases/debate-rounds.js +162 -0
- package/dist/arena/phases/participant-research.d.ts +38 -0
- package/dist/arena/phases/participant-research.js +317 -0
- package/dist/arena/phases/planning-detail-expansion.d.ts +38 -0
- package/dist/arena/phases/planning-detail-expansion.js +121 -0
- package/dist/arena/planner.d.ts +27 -0
- package/dist/arena/planner.js +312 -0
- package/dist/arena/providers/docs.d.ts +6 -0
- package/dist/arena/providers/docs.js +108 -0
- package/dist/arena/providers/git.d.ts +8 -0
- package/dist/arena/providers/git.js +174 -0
- package/dist/arena/providers/index.d.ts +32 -0
- package/dist/arena/providers/index.js +132 -0
- package/dist/arena/providers/none.d.ts +7 -0
- package/dist/arena/providers/none.js +11 -0
- package/dist/arena/providers/repo.d.ts +6 -0
- package/dist/arena/providers/repo.js +255 -0
- package/dist/arena/render/session.d.ts +17 -0
- package/dist/arena/render/session.js +190 -0
- package/dist/arena/render/terminal.d.ts +34 -0
- package/dist/arena/render/terminal.js +286 -0
- package/dist/arena/strategies/discussion.d.ts +25 -0
- package/dist/arena/strategies/discussion.js +143 -0
- package/dist/arena/strategies/index.d.ts +15 -0
- package/dist/arena/strategies/index.js +28 -0
- package/dist/arena/strategies/language-wrapper.d.ts +17 -0
- package/dist/arena/strategies/language-wrapper.js +102 -0
- package/dist/arena/strategies/lens-wrapper.d.ts +16 -0
- package/dist/arena/strategies/lens-wrapper.js +236 -0
- package/dist/arena/strategies/planning.d.ts +30 -0
- package/dist/arena/strategies/planning.js +225 -0
- package/dist/arena/strategies/review.d.ts +26 -0
- package/dist/arena/strategies/review.js +168 -0
- package/dist/arena/strategies/utils.d.ts +39 -0
- package/dist/arena/strategies/utils.js +647 -0
- package/dist/arena/tools/selector.d.ts +17 -0
- package/dist/arena/tools/selector.js +61 -0
- package/dist/arena/transitions.d.ts +48 -0
- package/dist/arena/transitions.js +92 -0
- package/dist/arena/types.d.ts +508 -0
- package/dist/arena/types.js +27 -0
- package/dist/cli/agent-server-stdio.d.ts +32 -0
- package/dist/cli/agent-server-stdio.js +116 -0
- package/dist/colorizer.d.ts +23 -0
- package/dist/colorizer.js +12 -0
- package/dist/context/compaction.d.ts +116 -0
- package/dist/context/compaction.js +534 -0
- package/dist/context/manager.d.ts +145 -0
- package/dist/context/manager.js +423 -0
- package/dist/context/token-counter.d.ts +27 -0
- package/dist/context/token-counter.js +91 -0
- package/dist/context/tool-result-storage.d.ts +96 -0
- package/dist/context/tool-result-storage.js +296 -0
- package/dist/cost-tracker.d.ts +64 -0
- package/dist/cost-tracker.js +314 -0
- package/dist/cron/scheduler.d.ts +34 -0
- package/dist/cron/scheduler.js +115 -0
- package/dist/data/deepseek-models.json +46 -0
- package/dist/data/gemini-models.json +67 -0
- package/dist/data/openai-models.json +175 -0
- package/dist/data/openrouter-models.d.ts +35 -0
- package/dist/data/openrouter-models.js +36 -0
- package/dist/data/openrouter-models.json +4626 -0
- package/dist/data/openrouter-sync.d.ts +16 -0
- package/dist/data/openrouter-sync.js +53 -0
- package/dist/data/static-catalogs.d.ts +25 -0
- package/dist/data/static-catalogs.js +26 -0
- package/dist/data/zai-models.json +56 -0
- package/dist/engine/cost-store.d.ts +22 -0
- package/dist/engine/cost-store.js +15 -0
- package/dist/engine/engine.d.ts +295 -0
- package/dist/engine/engine.js +1467 -0
- package/dist/engine/model-facade.d.ts +24 -0
- package/dist/engine/model-facade.js +194 -0
- package/dist/engine/parse-task.d.ts +59 -0
- package/dist/engine/parse-task.js +135 -0
- package/dist/engine/patch-orphaned-tools.d.ts +52 -0
- package/dist/engine/patch-orphaned-tools.js +95 -0
- package/dist/engine/query.d.ts +50 -0
- package/dist/engine/query.js +107 -0
- package/dist/engine/runtime.d.ts +50 -0
- package/dist/engine/runtime.js +59 -0
- package/dist/engine/streaming-tool-queue.d.ts +34 -0
- package/dist/engine/streaming-tool-queue.js +61 -0
- package/dist/engine/token-budget.d.ts +22 -0
- package/dist/engine/token-budget.js +40 -0
- package/dist/engine/tool-summary.d.ts +13 -0
- package/dist/engine/tool-summary.js +35 -0
- package/dist/engine/turn-loop.d.ts +122 -0
- package/dist/engine/turn-loop.js +602 -0
- package/dist/engine/turn-state.d.ts +20 -0
- package/dist/engine/turn-state.js +17 -0
- package/dist/exceptions.d.ts +61 -0
- package/dist/exceptions.js +116 -0
- package/dist/git/utils.d.ts +35 -0
- package/dist/git/utils.js +98 -0
- package/dist/git/worktree.d.ts +36 -0
- package/dist/git/worktree.js +139 -0
- package/dist/hooks/events.d.ts +114 -0
- package/dist/hooks/events.js +4 -0
- package/dist/hooks/inject.d.ts +19 -0
- package/dist/hooks/inject.js +29 -0
- package/dist/hooks/registry.d.ts +15 -0
- package/dist/hooks/registry.js +84 -0
- package/dist/hooks/shell-runner.d.ts +45 -0
- package/dist/hooks/shell-runner.js +173 -0
- package/dist/index.d.ts +117 -0
- package/dist/index.js +145 -0
- package/dist/llm/api-key-sanitize.d.ts +22 -0
- package/dist/llm/api-key-sanitize.js +81 -0
- package/dist/llm/capabilities/index.d.ts +20 -0
- package/dist/llm/capabilities/index.js +39 -0
- package/dist/llm/capabilities/rules.d.ts +26 -0
- package/dist/llm/capabilities/rules.js +215 -0
- package/dist/llm/capabilities/types.d.ts +114 -0
- package/dist/llm/capabilities/types.js +24 -0
- package/dist/llm/client-base.d.ts +29 -0
- package/dist/llm/client-base.js +137 -0
- package/dist/llm/client-factory.d.ts +10 -0
- package/dist/llm/client-factory.js +29 -0
- package/dist/llm/model-cache.d.ts +21 -0
- package/dist/llm/model-cache.js +46 -0
- package/dist/llm/model-fetcher.d.ts +29 -0
- package/dist/llm/model-fetcher.js +282 -0
- package/dist/llm/model-pool.d.ts +95 -0
- package/dist/llm/model-pool.js +246 -0
- package/dist/llm/provider-catalog.d.ts +32 -0
- package/dist/llm/provider-catalog.js +50 -0
- package/dist/llm/provider-kinds.d.ts +21 -0
- package/dist/llm/provider-kinds.js +132 -0
- package/dist/llm/providers/anthropic.d.ts +19 -0
- package/dist/llm/providers/anthropic.js +267 -0
- package/dist/llm/providers/openai.d.ts +64 -0
- package/dist/llm/providers/openai.js +606 -0
- package/dist/llm/retry.d.ts +12 -0
- package/dist/llm/retry.js +17 -0
- package/dist/llm/stream-watchdog.d.ts +42 -0
- package/dist/llm/stream-watchdog.js +70 -0
- package/dist/llm/token-counter.d.ts +17 -0
- package/dist/llm/token-counter.js +36 -0
- package/dist/llm/types.d.ts +37 -0
- package/dist/llm/types.js +4 -0
- package/dist/logging/logger.d.ts +109 -0
- package/dist/logging/logger.js +390 -0
- package/dist/logging/sanitize-messages.d.ts +42 -0
- package/dist/logging/sanitize-messages.js +150 -0
- package/dist/logging/session-recorder.d.ts +90 -0
- package/dist/logging/session-recorder.js +304 -0
- package/dist/lsp/client.d.ts +41 -0
- package/dist/lsp/client.js +166 -0
- package/dist/lsp/manager.d.ts +40 -0
- package/dist/lsp/manager.js +124 -0
- package/dist/lsp/servers.d.ts +16 -0
- package/dist/lsp/servers.js +60 -0
- package/dist/migrate-models.d.ts +45 -0
- package/dist/migrate-models.js +179 -0
- package/dist/onboarding.d.ts +189 -0
- package/dist/onboarding.js +625 -0
- package/dist/plugins/gitOps.d.ts +32 -0
- package/dist/plugins/gitOps.js +62 -0
- package/dist/plugins/installedPlugins.d.ts +15 -0
- package/dist/plugins/installedPlugins.js +56 -0
- package/dist/plugins/knownMarketplaces.d.ts +10 -0
- package/dist/plugins/knownMarketplaces.js +46 -0
- package/dist/plugins/loadPluginHooks.d.ts +52 -0
- package/dist/plugins/loadPluginHooks.js +162 -0
- package/dist/plugins/marketplaceManager.d.ts +49 -0
- package/dist/plugins/marketplaceManager.js +142 -0
- package/dist/plugins/parseMarketplaceInput.d.ts +16 -0
- package/dist/plugins/parseMarketplaceInput.js +73 -0
- package/dist/plugins/pluginCommandHook.d.ts +54 -0
- package/dist/plugins/pluginCommandHook.js +180 -0
- package/dist/plugins/pluginCommandsLoader.d.ts +25 -0
- package/dist/plugins/pluginCommandsLoader.js +114 -0
- package/dist/plugins/pluginInstaller.d.ts +36 -0
- package/dist/plugins/pluginInstaller.js +228 -0
- package/dist/plugins/schemas.d.ts +9 -0
- package/dist/plugins/schemas.js +136 -0
- package/dist/plugins/types.d.ts +75 -0
- package/dist/plugins/types.js +7 -0
- package/dist/plugins/varRewrite.d.ts +42 -0
- package/dist/plugins/varRewrite.js +129 -0
- package/dist/preset/index.d.ts +60 -0
- package/dist/preset/index.js +159 -0
- package/dist/product/define.d.ts +60 -0
- package/dist/product/define.js +123 -0
- package/dist/product/index.d.ts +5 -0
- package/dist/product/index.js +4 -0
- package/dist/product/types.d.ts +111 -0
- package/dist/product/types.js +21 -0
- package/dist/prompt/composer.d.ts +49 -0
- package/dist/prompt/composer.js +168 -0
- package/dist/prompt/instruction-scanner.d.ts +54 -0
- package/dist/prompt/instruction-scanner.js +187 -0
- package/dist/prompt/section-cache.d.ts +15 -0
- package/dist/prompt/section-cache.js +31 -0
- package/dist/prompt/section-loader.d.ts +26 -0
- package/dist/prompt/section-loader.js +53 -0
- package/dist/prompt/sections/base.md +35 -0
- package/dist/prompt/sections/coding.md +31 -0
- package/dist/prompt/sections/orchestration.md +16 -0
- package/dist/prompt/sections/tone.md +9 -0
- package/dist/protocol/chat-session-manager.d.ts +33 -0
- package/dist/protocol/chat-session-manager.js +65 -0
- package/dist/protocol/chat-session.d.ts +49 -0
- package/dist/protocol/chat-session.js +86 -0
- package/dist/protocol/client.d.ts +104 -0
- package/dist/protocol/client.js +260 -0
- package/dist/protocol/factories.d.ts +93 -0
- package/dist/protocol/factories.js +64 -0
- package/dist/protocol/helpers.d.ts +42 -0
- package/dist/protocol/helpers.js +55 -0
- package/dist/protocol/index.d.ts +4 -0
- package/dist/protocol/index.js +4 -0
- package/dist/protocol/server.d.ts +80 -0
- package/dist/protocol/server.js +855 -0
- package/dist/protocol/transport.d.ts +37 -0
- package/dist/protocol/transport.js +85 -0
- package/dist/protocol/types.d.ts +221 -0
- package/dist/protocol/types.js +61 -0
- package/dist/remote/bridge.d.ts +37 -0
- package/dist/remote/bridge.js +103 -0
- package/dist/run/ArtifactTracker.d.ts +38 -0
- package/dist/run/ArtifactTracker.js +132 -0
- package/dist/run/CheckpointWriter.d.ts +53 -0
- package/dist/run/CheckpointWriter.js +135 -0
- package/dist/run/EngineRunner.d.ts +72 -0
- package/dist/run/EngineRunner.js +119 -0
- package/dist/run/Evaluator.d.ts +56 -0
- package/dist/run/Evaluator.js +62 -0
- package/dist/run/FileRunStore.d.ts +39 -0
- package/dist/run/FileRunStore.js +176 -0
- package/dist/run/Heartbeat.d.ts +54 -0
- package/dist/run/Heartbeat.js +123 -0
- package/dist/run/RunApprovalBackend.d.ts +53 -0
- package/dist/run/RunApprovalBackend.js +85 -0
- package/dist/run/RunLock.d.ts +40 -0
- package/dist/run/RunLock.js +98 -0
- package/dist/run/RunManager.d.ts +82 -0
- package/dist/run/RunManager.js +587 -0
- package/dist/run/RunQueue.d.ts +27 -0
- package/dist/run/RunQueue.js +73 -0
- package/dist/run/RunStore.d.ts +25 -0
- package/dist/run/RunStore.js +9 -0
- package/dist/run/factory.d.ts +63 -0
- package/dist/run/factory.js +51 -0
- package/dist/run/index.d.ts +17 -0
- package/dist/run/index.js +23 -0
- package/dist/run/types.d.ts +136 -0
- package/dist/run/types.js +28 -0
- package/dist/runtime/safe-spawn.d.ts +118 -0
- package/dist/runtime/safe-spawn.js +268 -0
- package/dist/services/analytics.d.ts +29 -0
- package/dist/services/analytics.js +84 -0
- package/dist/services/auto-dream.d.ts +49 -0
- package/dist/services/auto-dream.js +124 -0
- package/dist/services/diagnostics.d.ts +41 -0
- package/dist/services/diagnostics.js +99 -0
- package/dist/services/extract-memories.d.ts +31 -0
- package/dist/services/extract-memories.js +71 -0
- package/dist/services/index.d.ts +11 -0
- package/dist/services/index.js +17 -0
- package/dist/services/memory-orchestrator.d.ts +60 -0
- package/dist/services/memory-orchestrator.js +133 -0
- package/dist/services/notifier.d.ts +25 -0
- package/dist/services/notifier.js +69 -0
- package/dist/services/oauth.d.ts +27 -0
- package/dist/services/oauth.js +153 -0
- package/dist/services/session-memory.d.ts +37 -0
- package/dist/services/session-memory.js +81 -0
- package/dist/session/file-history.d.ts +41 -0
- package/dist/session/file-history.js +115 -0
- package/dist/session/memory.d.ts +104 -0
- package/dist/session/memory.js +310 -0
- package/dist/session/session-manager.d.ts +41 -0
- package/dist/session/session-manager.js +261 -0
- package/dist/session/transcript.d.ts +39 -0
- package/dist/session/transcript.js +186 -0
- package/dist/settings/manager.d.ts +36 -0
- package/dist/settings/manager.js +187 -0
- package/dist/settings/schema.d.ts +1219 -0
- package/dist/settings/schema.js +246 -0
- package/dist/skills/frontmatter.d.ts +13 -0
- package/dist/skills/frontmatter.js +70 -0
- package/dist/skills/index.d.ts +7 -0
- package/dist/skills/index.js +6 -0
- package/dist/skills/scanner.d.ts +31 -0
- package/dist/skills/scanner.js +171 -0
- package/dist/state.d.ts +160 -0
- package/dist/state.js +267 -0
- package/dist/tool-system/builtin/agent-notifications.d.ts +84 -0
- package/dist/tool-system/builtin/agent-notifications.js +197 -0
- package/dist/tool-system/builtin/agent-registry.d.ts +67 -0
- package/dist/tool-system/builtin/agent-registry.js +126 -0
- package/dist/tool-system/builtin/agent-transcript-translator.d.ts +22 -0
- package/dist/tool-system/builtin/agent-transcript-translator.js +160 -0
- package/dist/tool-system/builtin/agent.d.ts +16 -0
- package/dist/tool-system/builtin/agent.js +364 -0
- package/dist/tool-system/builtin/apply-patch/applier.d.ts +26 -0
- package/dist/tool-system/builtin/apply-patch/applier.js +250 -0
- package/dist/tool-system/builtin/apply-patch/index.d.ts +20 -0
- package/dist/tool-system/builtin/apply-patch/index.js +101 -0
- package/dist/tool-system/builtin/apply-patch/parser.d.ts +17 -0
- package/dist/tool-system/builtin/apply-patch/parser.js +212 -0
- package/dist/tool-system/builtin/apply-patch/seek-sequence.d.ts +18 -0
- package/dist/tool-system/builtin/apply-patch/seek-sequence.js +123 -0
- package/dist/tool-system/builtin/apply-patch/types.d.ts +49 -0
- package/dist/tool-system/builtin/apply-patch/types.js +13 -0
- package/dist/tool-system/builtin/arena.d.ts +31 -0
- package/dist/tool-system/builtin/arena.js +416 -0
- package/dist/tool-system/builtin/ask-user.d.ts +20 -0
- package/dist/tool-system/builtin/ask-user.js +99 -0
- package/dist/tool-system/builtin/bash.d.ts +21 -0
- package/dist/tool-system/builtin/bash.js +149 -0
- package/dist/tool-system/builtin/brief.d.ts +6 -0
- package/dist/tool-system/builtin/brief.js +41 -0
- package/dist/tool-system/builtin/config.d.ts +6 -0
- package/dist/tool-system/builtin/config.js +67 -0
- package/dist/tool-system/builtin/cron.d.ts +10 -0
- package/dist/tool-system/builtin/cron.js +61 -0
- package/dist/tool-system/builtin/edit.d.ts +6 -0
- package/dist/tool-system/builtin/edit.js +100 -0
- package/dist/tool-system/builtin/file-cache.d.ts +11 -0
- package/dist/tool-system/builtin/file-cache.js +39 -0
- package/dist/tool-system/builtin/glob.d.ts +7 -0
- package/dist/tool-system/builtin/glob.js +76 -0
- package/dist/tool-system/builtin/grep.d.ts +7 -0
- package/dist/tool-system/builtin/grep.js +150 -0
- package/dist/tool-system/builtin/index.d.ts +20 -0
- package/dist/tool-system/builtin/index.js +449 -0
- package/dist/tool-system/builtin/lsp.d.ts +6 -0
- package/dist/tool-system/builtin/lsp.js +141 -0
- package/dist/tool-system/builtin/mcp-tools.d.ts +10 -0
- package/dist/tool-system/builtin/mcp-tools.js +102 -0
- package/dist/tool-system/builtin/memory.d.ts +26 -0
- package/dist/tool-system/builtin/memory.js +226 -0
- package/dist/tool-system/builtin/notebook-edit.d.ts +6 -0
- package/dist/tool-system/builtin/notebook-edit.js +124 -0
- package/dist/tool-system/builtin/plan.d.ts +15 -0
- package/dist/tool-system/builtin/plan.js +55 -0
- package/dist/tool-system/builtin/powershell.d.ts +11 -0
- package/dist/tool-system/builtin/powershell.js +64 -0
- package/dist/tool-system/builtin/read.d.ts +6 -0
- package/dist/tool-system/builtin/read.js +70 -0
- package/dist/tool-system/builtin/remote-trigger.d.ts +6 -0
- package/dist/tool-system/builtin/remote-trigger.js +54 -0
- package/dist/tool-system/builtin/repl.d.ts +11 -0
- package/dist/tool-system/builtin/repl.js +82 -0
- package/dist/tool-system/builtin/send-message.d.ts +6 -0
- package/dist/tool-system/builtin/send-message.js +47 -0
- package/dist/tool-system/builtin/skill-prompt.d.ts +20 -0
- package/dist/tool-system/builtin/skill-prompt.js +52 -0
- package/dist/tool-system/builtin/skill.d.ts +9 -0
- package/dist/tool-system/builtin/skill.js +55 -0
- package/dist/tool-system/builtin/sleep.d.ts +6 -0
- package/dist/tool-system/builtin/sleep.js +33 -0
- package/dist/tool-system/builtin/task.d.ts +48 -0
- package/dist/tool-system/builtin/task.js +139 -0
- package/dist/tool-system/builtin/tool-search.d.ts +11 -0
- package/dist/tool-system/builtin/tool-search.js +90 -0
- package/dist/tool-system/builtin/web-fetch.d.ts +10 -0
- package/dist/tool-system/builtin/web-fetch.js +328 -0
- package/dist/tool-system/builtin/web-search.d.ts +25 -0
- package/dist/tool-system/builtin/web-search.js +174 -0
- package/dist/tool-system/builtin/worktree.d.ts +10 -0
- package/dist/tool-system/builtin/worktree.js +95 -0
- package/dist/tool-system/builtin/write.d.ts +6 -0
- package/dist/tool-system/builtin/write.js +36 -0
- package/dist/tool-system/context.d.ts +165 -0
- package/dist/tool-system/context.js +18 -0
- package/dist/tool-system/executor.d.ts +49 -0
- package/dist/tool-system/executor.js +495 -0
- package/dist/tool-system/investigation-guard.d.ts +46 -0
- package/dist/tool-system/investigation-guard.js +155 -0
- package/dist/tool-system/mcp-manager.d.ts +67 -0
- package/dist/tool-system/mcp-manager.js +249 -0
- package/dist/tool-system/permission.d.ts +111 -0
- package/dist/tool-system/permission.js +774 -0
- package/dist/tool-system/registry.d.ts +32 -0
- package/dist/tool-system/registry.js +131 -0
- package/dist/tool-system/sandbox/bwrap.d.ts +13 -0
- package/dist/tool-system/sandbox/bwrap.js +63 -0
- package/dist/tool-system/sandbox/index.d.ts +78 -0
- package/dist/tool-system/sandbox/index.js +222 -0
- package/dist/tool-system/sandbox/off.d.ts +2 -0
- package/dist/tool-system/sandbox/off.js +8 -0
- package/dist/tool-system/sandbox/seatbelt.d.ts +18 -0
- package/dist/tool-system/sandbox/seatbelt.js +109 -0
- package/dist/tool-system/task-guard.d.ts +29 -0
- package/dist/tool-system/task-guard.js +70 -0
- package/dist/tool-system/validation.d.ts +5 -0
- package/dist/tool-system/validation.js +39 -0
- package/dist/types.d.ts +376 -0
- package/dist/types.js +4 -0
- package/dist/updater.d.ts +65 -0
- package/dist/updater.js +405 -0
- package/dist/utils/debug.d.ts +41 -0
- package/dist/utils/debug.js +110 -0
- package/dist/utils/earlyInput.d.ts +43 -0
- package/dist/utils/earlyInput.js +166 -0
- package/dist/utils/env.d.ts +24 -0
- package/dist/utils/env.js +104 -0
- package/dist/utils/envUtils.d.ts +51 -0
- package/dist/utils/envUtils.js +135 -0
- package/dist/utils/execFileNoThrow.d.ts +39 -0
- package/dist/utils/execFileNoThrow.js +74 -0
- package/dist/utils/format.d.ts +44 -0
- package/dist/utils/format.js +236 -0
- package/dist/utils/intl.d.ts +22 -0
- package/dist/utils/intl.js +83 -0
- package/dist/utils/lockfile.d.ts +15 -0
- package/dist/utils/lockfile.js +30 -0
- package/dist/utils/memoize.d.ts +19 -0
- package/dist/utils/memoize.js +14 -0
- package/dist/utils/semver.d.ts +6 -0
- package/dist/utils/semver.js +13 -0
- package/dist/utils/sliceAnsi.d.ts +12 -0
- package/dist/utils/sliceAnsi.js +62 -0
- package/dist/utils/systemTheme.d.ts +40 -0
- package/dist/utils/systemTheme.js +108 -0
- package/dist/utils/task-sanitizer.d.ts +6 -0
- package/dist/utils/task-sanitizer.js +24 -0
- package/dist/utils/theme.d.ts +93 -0
- package/dist/utils/theme.js +544 -0
- package/dist/utils/toolDisplay.d.ts +31 -0
- package/dist/utils/toolDisplay.js +188 -0
- package/package.json +72 -0
|
@@ -0,0 +1,606 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI GPT provider using openai SDK.
|
|
3
|
+
*
|
|
4
|
+
* Serves every OpenAI-compatible endpoint we support (OpenAI, DeepSeek,
|
|
5
|
+
* OpenRouter, Z.AI, xAI, Mistral, Groq, Gemini-OpenAI-compat, Ollama,
|
|
6
|
+
* custom). Per-(provider, model) request-shape divergence — token-limit
|
|
7
|
+
* field name, rejected sampling params, thinking knob, reasoning echo —
|
|
8
|
+
* is resolved through `capabilitiesFor()`, not hardcoded.
|
|
9
|
+
*/
|
|
10
|
+
import OpenAI from "openai";
|
|
11
|
+
import { LLMClientBase } from "../client-base.js";
|
|
12
|
+
import { ContextLimitError, LLMError, LLMRateLimitError } from "../../exceptions.js";
|
|
13
|
+
import { logger } from "../../logging/logger.js";
|
|
14
|
+
import { countTokens } from "../token-counter.js";
|
|
15
|
+
import { capabilitiesFor } from "../capabilities/index.js";
|
|
16
|
+
import { STREAM_WATCHDOG_CONFIG, StreamIdleTimeoutError, } from "../stream-watchdog.js";
|
|
17
|
+
/**
|
|
18
|
+
* Consume an async iterable of stream chunks with an idle watchdog.
|
|
19
|
+
* Returns the accumulated text — either from onChunk return values, or
|
|
20
|
+
* by extracting choices[0].delta.content from raw chunks (useful in tests
|
|
21
|
+
* that call runStreamWithWatchdog directly without an onChunk handler).
|
|
22
|
+
*
|
|
23
|
+
* The watchdog is active whenever opts.idleTimeoutMs is explicitly provided,
|
|
24
|
+
* or when STREAM_WATCHDOG_CONFIG.enabled is true. When active, each call to
|
|
25
|
+
* iterator.next() races against an idle deadline; if the deadline fires first
|
|
26
|
+
* the function rejects with StreamIdleTimeoutError.
|
|
27
|
+
*/
|
|
28
|
+
export async function runStreamWithWatchdog(stream, opts = {}) {
|
|
29
|
+
const watchdogActive = STREAM_WATCHDOG_CONFIG.enabled || opts.idleTimeoutMs !== undefined;
|
|
30
|
+
const idleTimeoutMs = opts.idleTimeoutMs ?? STREAM_WATCHDOG_CONFIG.idleTimeoutMs;
|
|
31
|
+
let text = "";
|
|
32
|
+
// Fast path: watchdog disabled AND caller did not override → no overhead.
|
|
33
|
+
if (!watchdogActive) {
|
|
34
|
+
for await (const chunk of stream) {
|
|
35
|
+
if (opts.onChunk) {
|
|
36
|
+
text += opts.onChunk(chunk) ?? "";
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
text += chunk?.choices?.[0]?.delta?.content ?? "";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return text;
|
|
43
|
+
}
|
|
44
|
+
// Watchdog path: race each iterator.next() against a per-chunk idle timer.
|
|
45
|
+
// This ensures the for-await cannot block forever between chunks.
|
|
46
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
47
|
+
try {
|
|
48
|
+
while (true) {
|
|
49
|
+
const nextPromise = iterator.next();
|
|
50
|
+
// Build a timeout promise that rejects if no chunk arrives in time.
|
|
51
|
+
let timeoutHandle = null;
|
|
52
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
53
|
+
timeoutHandle = setTimeout(() => {
|
|
54
|
+
reject(new StreamIdleTimeoutError(idleTimeoutMs, opts.requestId));
|
|
55
|
+
}, idleTimeoutMs);
|
|
56
|
+
});
|
|
57
|
+
let result;
|
|
58
|
+
try {
|
|
59
|
+
result = await Promise.race([nextPromise, timeoutPromise]);
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
if (timeoutHandle !== null)
|
|
63
|
+
clearTimeout(timeoutHandle);
|
|
64
|
+
}
|
|
65
|
+
if (result.done)
|
|
66
|
+
break;
|
|
67
|
+
const chunk = result.value;
|
|
68
|
+
if (opts.onChunk) {
|
|
69
|
+
text += opts.onChunk(chunk) ?? "";
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
text += chunk?.choices?.[0]?.delta?.content ?? "";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
// Best-effort cleanup: signal iterator to stop. Do NOT await — if the
|
|
78
|
+
// generator is stuck (e.g., wedged HTTP stream), awaiting return() would
|
|
79
|
+
// itself hang. Fire-and-forget is intentional here.
|
|
80
|
+
iterator.return?.();
|
|
81
|
+
}
|
|
82
|
+
return text;
|
|
83
|
+
}
|
|
84
|
+
export class OpenAIClient extends LLMClientBase {
|
|
85
|
+
_client = null;
|
|
86
|
+
// Sticky override: once the endpoint tells us `max_tokens` is rejected for
|
|
87
|
+
// this model, switch to `max_completion_tokens` for the lifetime of the
|
|
88
|
+
// client. Cheaper and more reliable than re-deriving from the model id when
|
|
89
|
+
// a new variant ships before our regex knows about it.
|
|
90
|
+
_forceMaxCompletionTokens = false;
|
|
91
|
+
constructor(config) {
|
|
92
|
+
super(config);
|
|
93
|
+
}
|
|
94
|
+
initClient() {
|
|
95
|
+
// Lazy init — client created on first use
|
|
96
|
+
}
|
|
97
|
+
get client() {
|
|
98
|
+
if (!this._client) {
|
|
99
|
+
this._client = new OpenAI({
|
|
100
|
+
apiKey: this.config.apiKey ?? process.env.OPENAI_API_KEY,
|
|
101
|
+
...(this.config.baseUrl ? { baseURL: this.config.baseUrl } : {}),
|
|
102
|
+
timeout: this.timeout,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return this._client;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Resolve the capability descriptor for the current (provider kind, model)
|
|
109
|
+
* pair. Cached for the lifetime of the client — model never changes
|
|
110
|
+
* mid-client. `providerKind` defaults to "openai" because legacy LLMConfig
|
|
111
|
+
* paths (arena, env-derived) don't always populate it; for those, the
|
|
112
|
+
* built-in rules table treats them as plain OpenAI.
|
|
113
|
+
*/
|
|
114
|
+
_capability = null;
|
|
115
|
+
get capability() {
|
|
116
|
+
if (!this._capability) {
|
|
117
|
+
const kind = (this.config.providerKind ?? "openai");
|
|
118
|
+
this._capability = capabilitiesFor(kind, this.model);
|
|
119
|
+
}
|
|
120
|
+
return this._capability;
|
|
121
|
+
}
|
|
122
|
+
async createMessage(options) {
|
|
123
|
+
return this.withRetry(async () => {
|
|
124
|
+
// Per-call thinking wins; otherwise fall back to provider default
|
|
125
|
+
// (settings.providers[].thinking, threaded through LLMConfig).
|
|
126
|
+
const thinking = options.thinking ?? this.config.thinking;
|
|
127
|
+
const messages = this.buildMessages(options.systemPrompt, options.messages, thinking);
|
|
128
|
+
const tools = options.tools?.length ? this.convertTools(options.tools) : undefined;
|
|
129
|
+
const span = logger.span("llm.request", {
|
|
130
|
+
cat: "llm",
|
|
131
|
+
provider: this.provider,
|
|
132
|
+
model: this.model,
|
|
133
|
+
stream: !!(options.stream && options.onChunk),
|
|
134
|
+
messageCount: messages.length,
|
|
135
|
+
toolCount: tools?.length ?? 0,
|
|
136
|
+
});
|
|
137
|
+
try {
|
|
138
|
+
const response = options.stream && options.onChunk
|
|
139
|
+
? await this.streamMessage(options, messages, tools, thinking)
|
|
140
|
+
: await this.nonStreamMessage(options, messages, tools, thinking);
|
|
141
|
+
span.end({
|
|
142
|
+
stopReason: response.stopReason,
|
|
143
|
+
promptTokens: response.usage?.promptTokens,
|
|
144
|
+
completionTokens: response.usage?.completionTokens,
|
|
145
|
+
cacheReadTokens: response.usage?.cacheReadTokens,
|
|
146
|
+
cacheCreationTokens: response.usage?.cacheCreationTokens,
|
|
147
|
+
});
|
|
148
|
+
return response;
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
span.fail(err);
|
|
152
|
+
throw err;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Build the request body honoring the model's capability descriptor.
|
|
158
|
+
* Centralized so both streaming and non-streaming paths agree on the
|
|
159
|
+
* exact shape.
|
|
160
|
+
*/
|
|
161
|
+
buildRequestBody(options, messages, tools, thinking, stream) {
|
|
162
|
+
const cap = this.capability;
|
|
163
|
+
const maxTokens = options.maxTokens ?? this.maxTokens;
|
|
164
|
+
// Token-limit field — capability picks `max_tokens` vs `max_completion_tokens`.
|
|
165
|
+
// Sticky fallback (set by handleApiError on a 400) overrides the rule for
|
|
166
|
+
// ids the regex hasn't learned about yet.
|
|
167
|
+
const useCompletion = this._forceMaxCompletionTokens || cap.tokenLimitField === "max_completion_tokens";
|
|
168
|
+
const tokenLimit = useCompletion
|
|
169
|
+
? { max_completion_tokens: maxTokens }
|
|
170
|
+
: { max_tokens: maxTokens };
|
|
171
|
+
// Sampling params — only include if the model accepts them.
|
|
172
|
+
const sampling = {};
|
|
173
|
+
if (!cap.rejectedParams.has("temperature")) {
|
|
174
|
+
sampling.temperature =
|
|
175
|
+
options.temperature !== undefined ? options.temperature : this.temperature;
|
|
176
|
+
}
|
|
177
|
+
// Reasoning shape — different vendors, different fields, never combine.
|
|
178
|
+
// We treat `options.thinking` as the user's intent ("enabled"/"disabled")
|
|
179
|
+
// and translate to whichever wire shape the model expects.
|
|
180
|
+
const reasoning = {};
|
|
181
|
+
if (thinking) {
|
|
182
|
+
switch (cap.reasoning.kind) {
|
|
183
|
+
case "deepseek-thinking":
|
|
184
|
+
// DeepSeek V4, Z.AI GLM-4.5+ — top-level {thinking: {type}}.
|
|
185
|
+
reasoning.thinking = { type: thinking };
|
|
186
|
+
break;
|
|
187
|
+
case "openai-effort":
|
|
188
|
+
// OpenAI o-series, gpt-5+, Gemini OpenAI-compat, xAI grok-4.3,
|
|
189
|
+
// Mistral magistral, Groq reasoning models — `reasoning_effort`.
|
|
190
|
+
// "enabled" → "medium" (safe middle). "disabled" → the capability's
|
|
191
|
+
// `disabledEffort` value; defaults to "minimal" (OpenAI), but xAI
|
|
192
|
+
// uses "low" (no minimal) and Mistral uses "none" (only high|none).
|
|
193
|
+
reasoning.reasoning_effort =
|
|
194
|
+
thinking === "disabled"
|
|
195
|
+
? (cap.reasoning.disabledEffort ?? "minimal")
|
|
196
|
+
: "medium";
|
|
197
|
+
break;
|
|
198
|
+
case "openrouter-reasoning":
|
|
199
|
+
// OpenRouter normalized shape — {reasoning: {effort, exclude}}.
|
|
200
|
+
reasoning.reasoning =
|
|
201
|
+
thinking === "disabled"
|
|
202
|
+
? { effort: "minimal", exclude: true }
|
|
203
|
+
: { effort: "medium" };
|
|
204
|
+
break;
|
|
205
|
+
case "anthropic-budget":
|
|
206
|
+
case "anthropic-adaptive":
|
|
207
|
+
case "none":
|
|
208
|
+
// OpenAI client doesn't serve Anthropic-direct; nothing to do.
|
|
209
|
+
// `none` means the model doesn't expose a knob — skip silently.
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
model: this.model,
|
|
215
|
+
messages,
|
|
216
|
+
...tokenLimit,
|
|
217
|
+
...sampling,
|
|
218
|
+
...reasoning,
|
|
219
|
+
...(tools ? { tools } : {}),
|
|
220
|
+
...(stream ? { stream: true, stream_options: { include_usage: true } } : {}),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
async nonStreamMessage(options, messages, tools, thinking) {
|
|
224
|
+
try {
|
|
225
|
+
const response = await this.client.chat.completions.create(this.buildRequestBody(options, messages, tools, thinking, false), { signal: options.signal });
|
|
226
|
+
const choice = response.choices[0];
|
|
227
|
+
if (!choice)
|
|
228
|
+
throw new LLMError("No response from OpenAI", "openai");
|
|
229
|
+
const usage = {
|
|
230
|
+
promptTokens: response.usage?.prompt_tokens ?? 0,
|
|
231
|
+
completionTokens: response.usage?.completion_tokens ?? 0,
|
|
232
|
+
totalTokens: response.usage?.total_tokens ?? 0,
|
|
233
|
+
};
|
|
234
|
+
this.recordUsage(usage, options);
|
|
235
|
+
return this.processChoice(choice, usage);
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
this.handleApiError(err);
|
|
239
|
+
throw err;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async streamMessage(options, messages, tools, thinking) {
|
|
243
|
+
try {
|
|
244
|
+
const stream = await this.client.chat.completions.create(this.buildRequestBody(options, messages, tools, thinking, true), { signal: options.signal });
|
|
245
|
+
let text = "";
|
|
246
|
+
let reasoningContent = "";
|
|
247
|
+
const toolCallsMap = new Map();
|
|
248
|
+
let streamUsage;
|
|
249
|
+
// TTFT — first chunk that actually carried text. Tool-call-only chunks
|
|
250
|
+
// earlier in the stream don't count: the user-visible "text starts now"
|
|
251
|
+
// moment is what we want to compare across providers.
|
|
252
|
+
const streamStartedAt = Date.now();
|
|
253
|
+
let firstByteLogged = false;
|
|
254
|
+
const requestId = stream.request_id;
|
|
255
|
+
const handleChunk = (chunk) => {
|
|
256
|
+
// Capture usage from the final chunk
|
|
257
|
+
if (chunk.usage) {
|
|
258
|
+
streamUsage = chunk.usage;
|
|
259
|
+
}
|
|
260
|
+
const delta = chunk.choices[0]?.delta;
|
|
261
|
+
if (!delta)
|
|
262
|
+
return "";
|
|
263
|
+
// DeepSeek thinking-mode streams reasoning as a separate delta
|
|
264
|
+
// field. Accumulate it so we can echo it back next turn.
|
|
265
|
+
const reasoningDelta = delta.reasoning_content;
|
|
266
|
+
if (typeof reasoningDelta === "string" && reasoningDelta.length > 0) {
|
|
267
|
+
reasoningContent += reasoningDelta;
|
|
268
|
+
}
|
|
269
|
+
if (delta.content) {
|
|
270
|
+
if (!firstByteLogged) {
|
|
271
|
+
firstByteLogged = true;
|
|
272
|
+
logger.debug("llm.first_byte", {
|
|
273
|
+
cat: "llm",
|
|
274
|
+
provider: this.provider,
|
|
275
|
+
model: this.model,
|
|
276
|
+
ttft_ms: Date.now() - streamStartedAt,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
text += delta.content;
|
|
280
|
+
options.onChunk?.({
|
|
281
|
+
type: "text",
|
|
282
|
+
text: delta.content,
|
|
283
|
+
tokens: countTokens(delta.content),
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
if (delta.tool_calls) {
|
|
287
|
+
for (const tc of delta.tool_calls) {
|
|
288
|
+
const idx = tc.index;
|
|
289
|
+
if (!toolCallsMap.has(idx)) {
|
|
290
|
+
toolCallsMap.set(idx, { id: tc.id ?? "", name: tc.function?.name ?? "", args: "" });
|
|
291
|
+
options.onChunk?.({
|
|
292
|
+
type: "tool_use_start",
|
|
293
|
+
toolCall: { id: tc.id, toolName: tc.function?.name },
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
const existing = toolCallsMap.get(idx);
|
|
297
|
+
if (tc.id)
|
|
298
|
+
existing.id = tc.id;
|
|
299
|
+
if (tc.function?.name)
|
|
300
|
+
existing.name = tc.function.name;
|
|
301
|
+
if (tc.function?.arguments) {
|
|
302
|
+
existing.args += tc.function.arguments;
|
|
303
|
+
// Best-effort partial-JSON parse: succeeds once a complete
|
|
304
|
+
// top-level object has streamed in (typical for short args)
|
|
305
|
+
// and silently waits otherwise.
|
|
306
|
+
if (existing.id) {
|
|
307
|
+
try {
|
|
308
|
+
const parsed = JSON.parse(existing.args || "{}");
|
|
309
|
+
options.onChunk?.({
|
|
310
|
+
type: "tool_use_delta",
|
|
311
|
+
toolCall: { id: existing.id, toolName: existing.name, args: parsed },
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
// partial JSON — wait for more deltas
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (chunk.choices[0]?.finish_reason) {
|
|
322
|
+
options.onChunk?.({ type: "stop", stopReason: chunk.choices[0].finish_reason });
|
|
323
|
+
}
|
|
324
|
+
return delta.content ?? "";
|
|
325
|
+
};
|
|
326
|
+
await runStreamWithWatchdog(stream, {
|
|
327
|
+
idleTimeoutMs: STREAM_WATCHDOG_CONFIG.enabled
|
|
328
|
+
? STREAM_WATCHDOG_CONFIG.idleTimeoutMs
|
|
329
|
+
: undefined,
|
|
330
|
+
requestId,
|
|
331
|
+
onChunk: handleChunk,
|
|
332
|
+
});
|
|
333
|
+
const toolCalls = [];
|
|
334
|
+
for (const [, tc] of toolCallsMap) {
|
|
335
|
+
let args = {};
|
|
336
|
+
try {
|
|
337
|
+
args = JSON.parse(tc.args || "{}");
|
|
338
|
+
}
|
|
339
|
+
catch { }
|
|
340
|
+
toolCalls.push({ id: tc.id, toolName: tc.name, args });
|
|
341
|
+
}
|
|
342
|
+
const usage = {
|
|
343
|
+
promptTokens: streamUsage?.prompt_tokens ?? 0,
|
|
344
|
+
completionTokens: streamUsage?.completion_tokens ?? 0,
|
|
345
|
+
totalTokens: streamUsage?.total_tokens ?? 0,
|
|
346
|
+
};
|
|
347
|
+
this.recordUsage(usage, options);
|
|
348
|
+
return {
|
|
349
|
+
text,
|
|
350
|
+
toolCalls,
|
|
351
|
+
usage,
|
|
352
|
+
stopReason: "stop",
|
|
353
|
+
...(reasoningContent ? { reasoningContent } : {}),
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
catch (err) {
|
|
357
|
+
this.handleApiError(err);
|
|
358
|
+
throw err;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
processChoice(choice, usage) {
|
|
362
|
+
const text = choice.message.content ?? "";
|
|
363
|
+
const toolCalls = [];
|
|
364
|
+
if (choice.message.tool_calls) {
|
|
365
|
+
for (const tc of choice.message.tool_calls) {
|
|
366
|
+
let args = {};
|
|
367
|
+
try {
|
|
368
|
+
args = JSON.parse(tc.function.arguments || "{}");
|
|
369
|
+
}
|
|
370
|
+
catch { }
|
|
371
|
+
toolCalls.push({
|
|
372
|
+
id: tc.id,
|
|
373
|
+
toolName: tc.function.name,
|
|
374
|
+
args,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const reasoningContent = extractReasoningContent(choice.message);
|
|
379
|
+
return {
|
|
380
|
+
text,
|
|
381
|
+
toolCalls,
|
|
382
|
+
usage,
|
|
383
|
+
stopReason: choice.finish_reason ?? undefined,
|
|
384
|
+
...(reasoningContent ? { reasoningContent } : {}),
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
buildMessages(systemPrompt, messages, thinking) {
|
|
388
|
+
const result = [{ role: "system", content: systemPrompt }];
|
|
389
|
+
// Reasoning-content echo-back contract — driven by capability:
|
|
390
|
+
// "when-tools" : backfill an empty placeholder if the prior assistant
|
|
391
|
+
// turn doesn't carry one (DeepSeek V4 + tools 400s
|
|
392
|
+
// otherwise). Skip when thinking is explicitly disabled,
|
|
393
|
+
// because empty-string in that mode can degenerate into
|
|
394
|
+
// an empty reply on the *next* turn.
|
|
395
|
+
// "never" : drop any reasoning_content (deepseek-reasoner 400s
|
|
396
|
+
// if input messages contain it).
|
|
397
|
+
// "optional" : pass through when we have it, never synthesize.
|
|
398
|
+
const cap = this.capability;
|
|
399
|
+
const hasTools = messages.some((m) => Array.isArray(m.content) &&
|
|
400
|
+
m.content.some((b) => b.type === "tool_use" || b.type === "tool_result"));
|
|
401
|
+
const needsReasoningBackfill = thinking !== "disabled" &&
|
|
402
|
+
cap.echoReasoning === "when-tools" &&
|
|
403
|
+
hasTools;
|
|
404
|
+
const stripReasoning = cap.echoReasoning === "never";
|
|
405
|
+
for (const msg of messages) {
|
|
406
|
+
if (msg.role === "system")
|
|
407
|
+
continue;
|
|
408
|
+
if (msg.role === "assistant") {
|
|
409
|
+
const param = { role: "assistant" };
|
|
410
|
+
let reasoningContent;
|
|
411
|
+
if (typeof msg.content === "string") {
|
|
412
|
+
param.content = msg.content;
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
const textParts = [];
|
|
416
|
+
const toolCalls = [];
|
|
417
|
+
for (const block of msg.content) {
|
|
418
|
+
if (block.type === "text" && block.text) {
|
|
419
|
+
textParts.push(block.text);
|
|
420
|
+
}
|
|
421
|
+
else if (block.type === "tool_use" && block.id && block.name) {
|
|
422
|
+
toolCalls.push({
|
|
423
|
+
id: block.id,
|
|
424
|
+
type: "function",
|
|
425
|
+
function: {
|
|
426
|
+
name: block.name,
|
|
427
|
+
arguments: JSON.stringify(block.input ?? {}),
|
|
428
|
+
},
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
else if (block.type === "reasoning" && block.reasoningContent) {
|
|
432
|
+
reasoningContent = block.reasoningContent;
|
|
433
|
+
}
|
|
434
|
+
// Reasoning may also ride alongside another block (e.g. on a
|
|
435
|
+
// text block) — pick it up wherever it appears so we never
|
|
436
|
+
// silently drop it.
|
|
437
|
+
if (!reasoningContent && block.reasoningContent) {
|
|
438
|
+
reasoningContent = block.reasoningContent;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (textParts.length)
|
|
442
|
+
param.content = textParts.join("\n");
|
|
443
|
+
if (toolCalls.length)
|
|
444
|
+
param.tool_calls = toolCalls;
|
|
445
|
+
}
|
|
446
|
+
if (stripReasoning) {
|
|
447
|
+
// Some endpoints (deepseek-reasoner) 400 if reasoning_content
|
|
448
|
+
// appears in input history. Don't attach the field at all.
|
|
449
|
+
reasoningContent = undefined;
|
|
450
|
+
}
|
|
451
|
+
else if (!reasoningContent && needsReasoningBackfill) {
|
|
452
|
+
reasoningContent = "";
|
|
453
|
+
}
|
|
454
|
+
if (reasoningContent !== undefined) {
|
|
455
|
+
param.reasoning_content = reasoningContent;
|
|
456
|
+
}
|
|
457
|
+
// OpenAI rejects { role: "assistant" } with no content AND no
|
|
458
|
+
// tool_calls ("Invalid assistant message: content or tool_calls
|
|
459
|
+
// must be set"). This shape can arise from a transcript where
|
|
460
|
+
// the assistant produced only reasoning blocks (no text, no tool
|
|
461
|
+
// calls) before the stream was cut off, or from a synthetic
|
|
462
|
+
// message left behind by an interrupted retry. Drop these so
|
|
463
|
+
// the API call doesn't 400 on a structurally-empty turn.
|
|
464
|
+
// reasoning_content alone does NOT satisfy OpenAI's requirement —
|
|
465
|
+
// it's an extension field and not counted as "content".
|
|
466
|
+
if (param.content === undefined && param.tool_calls === undefined) {
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
result.push(param);
|
|
470
|
+
}
|
|
471
|
+
else if (msg.role === "tool") {
|
|
472
|
+
result.push({
|
|
473
|
+
role: "tool",
|
|
474
|
+
tool_call_id: msg.tool_call_id ?? "",
|
|
475
|
+
content: typeof msg.content === "string" ? msg.content : "",
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
// user messages — may contain tool_result blocks mixed with text
|
|
480
|
+
// and image blocks (P2-6: image input).
|
|
481
|
+
if (typeof msg.content === "string") {
|
|
482
|
+
result.push({ role: "user", content: msg.content });
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
// Separate tool_result blocks from text/image blocks. tool_result
|
|
486
|
+
// must become its own `role: "tool"` message (OpenAI's wire format
|
|
487
|
+
// doesn't allow tool_result inside a user content array); text and
|
|
488
|
+
// image stay together so the model sees "this text refers to this
|
|
489
|
+
// image" without an intervening tool turn.
|
|
490
|
+
const textParts = [];
|
|
491
|
+
const imageParts = [];
|
|
492
|
+
const toolResults = [];
|
|
493
|
+
for (const block of msg.content) {
|
|
494
|
+
if (block.type === "tool_result" && block.tool_use_id) {
|
|
495
|
+
toolResults.push({
|
|
496
|
+
tool_use_id: block.tool_use_id,
|
|
497
|
+
content: typeof block.content === "string" ? block.content : "",
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
else if (block.type === "text" && block.text) {
|
|
501
|
+
textParts.push(block.text);
|
|
502
|
+
}
|
|
503
|
+
else if (block.type === "image" && block.source) {
|
|
504
|
+
// OpenAI-compat image_url: every supported provider (OpenAI,
|
|
505
|
+
// OpenRouter, OpenAI-compatible proxies for Gemini/xAI/etc)
|
|
506
|
+
// accepts a base64 data URL as the URL. Per-(provider, model)
|
|
507
|
+
// vision-capability gating happens earlier in Engine.run —
|
|
508
|
+
// by the time we reach here, the model supports vision.
|
|
509
|
+
imageParts.push({
|
|
510
|
+
type: "image_url",
|
|
511
|
+
image_url: {
|
|
512
|
+
url: `data:${block.source.media_type};base64,${block.source.data}`,
|
|
513
|
+
},
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
// Emit tool_result blocks as separate tool messages
|
|
518
|
+
for (const tr of toolResults) {
|
|
519
|
+
result.push({
|
|
520
|
+
role: "tool",
|
|
521
|
+
tool_call_id: tr.tool_use_id,
|
|
522
|
+
content: tr.content,
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
// Emit the user-visible turn. With images present, content MUST be
|
|
526
|
+
// an array — putting a stringified version into `content: "..."`
|
|
527
|
+
// strips the images. Without images we keep the legacy string form
|
|
528
|
+
// so requests for vanilla text turns are byte-identical to before.
|
|
529
|
+
if (imageParts.length > 0) {
|
|
530
|
+
const parts = [];
|
|
531
|
+
if (textParts.length > 0) {
|
|
532
|
+
parts.push({ type: "text", text: textParts.join("\n") });
|
|
533
|
+
}
|
|
534
|
+
parts.push(...imageParts);
|
|
535
|
+
result.push({ role: "user", content: parts });
|
|
536
|
+
}
|
|
537
|
+
else if (textParts.length > 0) {
|
|
538
|
+
result.push({ role: "user", content: textParts.join("\n") });
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return result;
|
|
544
|
+
}
|
|
545
|
+
convertTools(tools) {
|
|
546
|
+
return tools.map((t) => ({
|
|
547
|
+
type: "function",
|
|
548
|
+
function: {
|
|
549
|
+
name: t.name,
|
|
550
|
+
description: t.description,
|
|
551
|
+
parameters: t.inputSchema,
|
|
552
|
+
},
|
|
553
|
+
}));
|
|
554
|
+
}
|
|
555
|
+
handleApiError(err) {
|
|
556
|
+
if (err instanceof OpenAI.APIError) {
|
|
557
|
+
if (err.status === 429) {
|
|
558
|
+
throw new LLMRateLimitError("openai");
|
|
559
|
+
}
|
|
560
|
+
const msg = (err.message ?? "").toLowerCase();
|
|
561
|
+
// o-series / gpt-5+ reject `max_tokens` and demand
|
|
562
|
+
// `max_completion_tokens`. The id-based regex catches the common
|
|
563
|
+
// cases; this is the belt-and-suspenders path for ids that ship
|
|
564
|
+
// before the regex knows about them (e.g. new `gpt-5.x` variants
|
|
565
|
+
// routed via OpenAI-compatible proxies). Flip a sticky flag so the
|
|
566
|
+
// next request — including `withRetry`'s next attempt — sends the
|
|
567
|
+
// right field, instead of looping on the same 400.
|
|
568
|
+
if (err.status === 400 &&
|
|
569
|
+
msg.includes("max_tokens") &&
|
|
570
|
+
msg.includes("max_completion_tokens")) {
|
|
571
|
+
this._forceMaxCompletionTokens = true;
|
|
572
|
+
}
|
|
573
|
+
if (msg.includes("context_length_exceeded") ||
|
|
574
|
+
msg.includes("maximum context length") ||
|
|
575
|
+
msg.includes("too many tokens") ||
|
|
576
|
+
msg.includes("prompt is too long")) {
|
|
577
|
+
throw new ContextLimitError("openai");
|
|
578
|
+
}
|
|
579
|
+
// OpenRouter and other compatible endpoints often return 401 "Provider
|
|
580
|
+
// returned error" when the routed backend disables function calling for
|
|
581
|
+
// the selected model. A real auth failure is also possible — surface
|
|
582
|
+
// both possibilities so users don't blindly swap models.
|
|
583
|
+
if (err.status === 401 && msg.includes("provider returned error")) {
|
|
584
|
+
throw new LLMError(`OpenAI-compatible endpoint returned 401 "Provider returned error". ` +
|
|
585
|
+
`Two likely causes: (1) the API key is invalid/revoked, or ` +
|
|
586
|
+
`(2) the upstream provider rejects tool calls for this model ` +
|
|
587
|
+
`(common on OpenRouter's gpt-4o-mini route). Verify the key, ` +
|
|
588
|
+
`then try a tool-capable model such as openai/gpt-4.1-mini or ` +
|
|
589
|
+
`anthropic/claude-3.5-haiku.`, "openai", { status: err.status });
|
|
590
|
+
}
|
|
591
|
+
throw new LLMError(`OpenAI API error: ${err.message}`, "openai", { status: err.status });
|
|
592
|
+
}
|
|
593
|
+
throw err;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Pull a non-empty reasoning payload out of a provider message.
|
|
598
|
+
*
|
|
599
|
+
* DeepSeek thinking mode emits this on the assistant message; the
|
|
600
|
+
* spec requires that the same value be echoed back on the next
|
|
601
|
+
* request. Some routers also surface it under `reasoning`.
|
|
602
|
+
*/
|
|
603
|
+
function extractReasoningContent(msg) {
|
|
604
|
+
const candidate = msg.reasoning_content ?? msg.reasoning;
|
|
605
|
+
return typeof candidate === "string" && candidate.length > 0 ? candidate : undefined;
|
|
606
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry classification — which errors trigger an automatic retry of the
|
|
3
|
+
* streaming LLM call?
|
|
4
|
+
*
|
|
5
|
+
* Stream-idle timeouts are retried — the upstream may have just recovered.
|
|
6
|
+
* User-initiated aborts (AbortError) are never retried — the user explicitly
|
|
7
|
+
* asked for the work to stop.
|
|
8
|
+
*
|
|
9
|
+
* Other transient errors (5xx, 429, ECONNRESET) are out of scope for this
|
|
10
|
+
* iteration; add them here as they become observed.
|
|
11
|
+
*/
|
|
12
|
+
export declare function isRetryable(err: unknown): boolean;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry classification — which errors trigger an automatic retry of the
|
|
3
|
+
* streaming LLM call?
|
|
4
|
+
*
|
|
5
|
+
* Stream-idle timeouts are retried — the upstream may have just recovered.
|
|
6
|
+
* User-initiated aborts (AbortError) are never retried — the user explicitly
|
|
7
|
+
* asked for the work to stop.
|
|
8
|
+
*
|
|
9
|
+
* Other transient errors (5xx, 429, ECONNRESET) are out of scope for this
|
|
10
|
+
* iteration; add them here as they become observed.
|
|
11
|
+
*/
|
|
12
|
+
import { StreamIdleTimeoutError } from "./stream-watchdog.js";
|
|
13
|
+
export function isRetryable(err) {
|
|
14
|
+
if (err instanceof StreamIdleTimeoutError)
|
|
15
|
+
return true;
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stream Idle Watchdog — guarantees an upper bound on the time a streaming
|
|
3
|
+
* LLM call can spend with no bytes arriving. Without this, a wedged HTTP/2
|
|
4
|
+
* connection can hang the session indefinitely; the SDK's request timeout
|
|
5
|
+
* only covers the initial fetch, not the streaming body.
|
|
6
|
+
*
|
|
7
|
+
* Disabled by default. Enabled via env CODESHELL_ENABLE_STREAM_WATCHDOG=1.
|
|
8
|
+
*/
|
|
9
|
+
export interface StreamWatchdogOptions {
|
|
10
|
+
/** Abort the stream when no chunks arrive within this window. */
|
|
11
|
+
idleTimeoutMs: number;
|
|
12
|
+
/** Called when the watchdog decides to abort. */
|
|
13
|
+
onTimeout: () => void;
|
|
14
|
+
/**
|
|
15
|
+
* Called at idleTimeoutMs/2 (or warningMs if provided). Logging only —
|
|
16
|
+
* the watchdog does not abort on warning. The argument is the configured
|
|
17
|
+
* warning threshold (NOT real-time elapsed) so callers can format the log
|
|
18
|
+
* without re-deriving it.
|
|
19
|
+
*/
|
|
20
|
+
onWarning?: (warnThresholdMs: number) => void;
|
|
21
|
+
/** Override the warning trigger; defaults to idleTimeoutMs / 2. */
|
|
22
|
+
warningMs?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface StreamWatchdog {
|
|
25
|
+
/** Re-arm both timers — call after every chunk. */
|
|
26
|
+
reset(): void;
|
|
27
|
+
/** Clear all timers permanently — call in finally. */
|
|
28
|
+
dispose(): void;
|
|
29
|
+
}
|
|
30
|
+
export declare class StreamIdleTimeoutError extends Error {
|
|
31
|
+
idleMs: number;
|
|
32
|
+
requestId?: string | undefined;
|
|
33
|
+
readonly kind = "stream-idle-timeout";
|
|
34
|
+
constructor(idleMs: number, requestId?: string | undefined);
|
|
35
|
+
}
|
|
36
|
+
export declare function createStreamWatchdog(opts: StreamWatchdogOptions): StreamWatchdog;
|
|
37
|
+
/** Environment-driven defaults. Read once at import time is fine — these never change inside a process. */
|
|
38
|
+
export declare const STREAM_WATCHDOG_CONFIG: {
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
idleTimeoutMs: number;
|
|
41
|
+
retries: number;
|
|
42
|
+
};
|