@cjhyy/code-shell-core 0.5.0-rc.2 → 0.6.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -10
- package/dist/agent/agent-definition-registry.d.ts +3 -0
- package/dist/agent/agent-definition-registry.js +3 -1
- package/dist/agent/agent-definition.d.ts +15 -0
- package/dist/agent/agent-definition.js +27 -3
- package/dist/arena/context/context-tools.js +47 -4
- package/dist/arena/ledger.js +9 -1
- package/dist/arena/strategies/utils.d.ts +1 -4
- package/dist/arena/strategies/utils.js +7 -84
- package/dist/automation/cron-expr.d.ts +10 -0
- package/dist/automation/cron-expr.js +23 -5
- package/dist/automation/runner.d.ts +21 -4
- package/dist/automation/runner.js +48 -12
- package/dist/automation/scheduler.d.ts +38 -0
- package/dist/automation/scheduler.js +89 -12
- package/dist/automation/store.js +5 -2
- package/dist/capability-control/disabled-lists.d.ts +25 -0
- package/dist/capability-control/disabled-lists.js +57 -0
- package/dist/capability-control/overlay.d.ts +15 -0
- package/dist/capability-control/overlay.js +27 -0
- package/dist/cc-orchestrator/agent-adapter.d.ts +50 -0
- package/dist/cc-orchestrator/agent-adapter.js +133 -0
- package/dist/cc-orchestrator/cc-capability.d.ts +19 -0
- package/dist/cc-orchestrator/cc-capability.js +53 -0
- package/dist/cc-orchestrator/codex-session-discovery.d.ts +24 -0
- package/dist/cc-orchestrator/codex-session-discovery.js +191 -0
- package/dist/cc-orchestrator/codex-session-history.d.ts +25 -0
- package/dist/cc-orchestrator/codex-session-history.js +187 -0
- package/dist/cc-orchestrator/external-agent-changes.d.ts +19 -0
- package/dist/cc-orchestrator/external-agent-changes.js +214 -0
- package/dist/cc-orchestrator/external-agent-driver.d.ts +18 -0
- package/dist/cc-orchestrator/external-agent-driver.js +69 -0
- package/dist/cc-orchestrator/index.d.ts +8 -0
- package/dist/cc-orchestrator/index.js +8 -0
- package/dist/cc-orchestrator/relevance-judge.d.ts +15 -0
- package/dist/cc-orchestrator/relevance-judge.js +29 -0
- package/dist/cc-orchestrator/session-discovery.d.ts +46 -0
- package/dist/cc-orchestrator/session-discovery.js +125 -0
- package/dist/cc-orchestrator/session-history.d.ts +19 -0
- package/dist/cc-orchestrator/session-history.js +67 -0
- package/dist/cli/agent-server-stdio.d.ts +15 -1
- package/dist/cli/agent-server-stdio.js +115 -17
- package/dist/cli/agent-server-tcp.js +40 -26
- package/dist/context/compaction.d.ts +86 -0
- package/dist/context/compaction.js +279 -0
- package/dist/context/manager.d.ts +18 -0
- package/dist/context/manager.js +181 -48
- package/dist/context/token-counter.js +13 -0
- package/dist/cost-tracker.js +5 -61
- package/dist/credentials/cipher.d.ts +49 -0
- package/dist/credentials/cipher.js +45 -0
- package/dist/credentials/cookie-jar.d.ts +24 -0
- package/dist/credentials/cookie-jar.js +40 -0
- package/dist/credentials/index.d.ts +7 -0
- package/dist/credentials/index.js +5 -0
- package/dist/credentials/inject-credential-tool.d.ts +20 -0
- package/dist/credentials/inject-credential-tool.js +130 -0
- package/dist/credentials/store.d.ts +72 -0
- package/dist/credentials/store.js +184 -0
- package/dist/credentials/types.d.ts +56 -0
- package/dist/credentials/use-credential-tool.d.ts +29 -0
- package/dist/credentials/use-credential-tool.js +205 -0
- package/dist/credentials/use-gate.d.ts +56 -0
- package/dist/credentials/use-gate.js +52 -0
- package/dist/data/model-metadata.d.ts +77 -0
- package/dist/data/model-metadata.js +56 -0
- package/dist/data/model-metadata.json +216 -0
- package/dist/data/openrouter-models.d.ts +18 -7
- package/dist/data/openrouter-models.js +35 -8
- package/dist/engine/aux-key.d.ts +10 -0
- package/dist/engine/aux-key.js +11 -0
- package/dist/engine/dynamic-tool-defs.d.ts +19 -0
- package/dist/engine/dynamic-tool-defs.js +36 -0
- package/dist/engine/engine.d.ts +280 -141
- package/dist/engine/engine.js +1193 -245
- package/dist/engine/friendly-error.d.ts +18 -0
- package/dist/engine/friendly-error.js +63 -0
- package/dist/engine/goal.d.ts +145 -0
- package/dist/engine/goal.js +144 -0
- package/dist/engine/image-policy.d.ts +13 -0
- package/dist/engine/image-policy.js +24 -0
- package/dist/engine/model-connections-pool.d.ts +17 -0
- package/dist/engine/model-connections-pool.js +67 -0
- package/dist/engine/model-facade.d.ts +10 -0
- package/dist/engine/model-facade.js +15 -0
- package/dist/engine/patch-orphaned-tools.js +3 -0
- package/dist/engine/query.js +2 -0
- package/dist/engine/resolve-llm-config.d.ts +16 -0
- package/dist/engine/resolve-llm-config.js +44 -0
- package/dist/engine/runtime.d.ts +2 -0
- package/dist/engine/runtime.js +27 -1
- package/dist/engine/sandbox-cache-key.d.ts +10 -0
- package/dist/engine/sandbox-cache-key.js +9 -0
- package/dist/engine/sandbox-config.d.ts +31 -0
- package/dist/engine/sandbox-config.js +38 -0
- package/dist/engine/session-usage.d.ts +31 -0
- package/dist/engine/session-usage.js +81 -0
- package/dist/engine/steer-queue.d.ts +33 -0
- package/dist/engine/steer-queue.js +26 -0
- package/dist/engine/streaming-tool-queue.d.ts +12 -0
- package/dist/engine/streaming-tool-queue.js +50 -10
- package/dist/engine/turn-loop.d.ts +94 -4
- package/dist/engine/turn-loop.js +432 -54
- package/dist/engine/types.d.ts +175 -0
- package/dist/engine/types.js +13 -0
- package/dist/external-agents/config.d.ts +2 -0
- package/dist/external-agents/config.js +15 -0
- package/dist/external-agents/types.d.ts +31 -0
- package/dist/external-agents/types.js +1 -0
- package/dist/git/utils.d.ts +12 -0
- package/dist/git/utils.js +38 -6
- package/dist/git/worktree.d.ts +48 -0
- package/dist/git/worktree.js +86 -10
- package/dist/hooks/goal-stop-hook.d.ts +28 -0
- package/dist/hooks/goal-stop-hook.js +186 -9
- package/dist/hooks/registry.d.ts +8 -0
- package/dist/hooks/registry.js +19 -0
- package/dist/hooks/shell-runner.js +10 -21
- package/dist/index.d.ts +51 -7
- package/dist/index.js +62 -5
- package/dist/llm/capabilities/param-specs.d.ts +14 -0
- package/dist/llm/capabilities/param-specs.js +62 -0
- package/dist/llm/capabilities/rules.js +5 -1
- package/dist/llm/capabilities/types.d.ts +10 -0
- package/dist/llm/client-base.d.ts +28 -1
- package/dist/llm/client-base.js +119 -13
- package/dist/llm/model-cache.js +4 -2
- package/dist/llm/model-pool.d.ts +21 -0
- package/dist/llm/model-pool.js +21 -1
- package/dist/llm/provider-auth.d.ts +41 -0
- package/dist/llm/provider-auth.js +76 -0
- package/dist/llm/provider-catalog.d.ts +4 -0
- package/dist/llm/providers/anthropic.js +64 -14
- package/dist/llm/providers/openai.d.ts +36 -0
- package/dist/llm/providers/openai.js +257 -51
- package/dist/llm/reasoning-setting.d.ts +3 -3
- package/dist/llm/reasoning-setting.js +9 -1
- package/dist/llm/stream-watchdog.js +5 -1
- package/dist/llm/token-counter.js +9 -2
- package/dist/llm/types.d.ts +4 -0
- package/dist/logging/sanitize-messages.js +22 -0
- package/dist/lsp/manager.d.ts +1 -1
- package/dist/lsp/manager.js +40 -10
- package/dist/model-catalog/builtin.d.ts +12 -0
- package/dist/model-catalog/builtin.js +412 -0
- package/dist/model-catalog/gen-connections.d.ts +20 -0
- package/dist/model-catalog/gen-connections.js +28 -0
- package/dist/model-catalog/index.d.ts +41 -0
- package/dist/model-catalog/index.js +90 -0
- package/dist/model-catalog/params.d.ts +20 -0
- package/dist/model-catalog/params.js +45 -0
- package/dist/model-catalog/resolve.d.ts +48 -0
- package/dist/model-catalog/resolve.js +33 -0
- package/dist/model-catalog/save-entry.d.ts +32 -0
- package/dist/model-catalog/save-entry.js +104 -0
- package/dist/model-catalog/types.d.ts +561 -0
- package/dist/model-catalog/types.js +93 -0
- package/dist/model-catalog/upsert.d.ts +9 -0
- package/dist/model-catalog/upsert.js +8 -0
- package/dist/onboarding.d.ts +12 -82
- package/dist/onboarding.js +61 -326
- package/dist/plugins/gitOps.d.ts +19 -0
- package/dist/plugins/gitOps.js +73 -4
- package/dist/plugins/installer/checkUpdate.d.ts +16 -0
- package/dist/plugins/installer/checkUpdate.js +32 -0
- package/dist/plugins/installer/codex/convertCommands.d.ts +19 -0
- package/dist/plugins/installer/codex/convertCommands.js +46 -0
- package/dist/plugins/installer/codex/convertMcp.d.ts +5 -2
- package/dist/plugins/installer/codex/convertMcp.js +45 -5
- package/dist/plugins/installer/install.js +25 -1
- package/dist/plugins/installer/installFromArchive.d.ts +43 -0
- package/dist/plugins/installer/installFromArchive.js +134 -0
- package/dist/plugins/installer/installFromSource.js +8 -2
- package/dist/plugins/installer/loadPluginAgents.js +6 -2
- package/dist/plugins/installer/loadPluginMcp.d.ts +9 -2
- package/dist/plugins/installer/loadPluginMcp.js +35 -2
- package/dist/plugins/installer/pruneDisabled.d.ts +24 -0
- package/dist/plugins/installer/pruneDisabled.js +73 -0
- package/dist/plugins/installer/types.d.ts +5 -2
- package/dist/plugins/installer/types.js +1 -0
- package/dist/plugins/installer/uninstall.js +4 -0
- package/dist/plugins/installer/unzip.d.ts +14 -0
- package/dist/plugins/installer/unzip.js +82 -0
- package/dist/plugins/installer/update.d.ts +14 -0
- package/dist/plugins/installer/update.js +53 -21
- package/dist/plugins/loadPluginHooks.d.ts +47 -1
- package/dist/plugins/loadPluginHooks.js +73 -1
- package/dist/plugins/marketplaceManager.d.ts +7 -0
- package/dist/plugins/marketplaceManager.js +20 -0
- package/dist/plugins/pluginCommandHook.js +4 -18
- package/dist/plugins/pluginContent.d.ts +30 -0
- package/dist/plugins/pluginContent.js +83 -0
- package/dist/plugins/pluginInstaller.d.ts +13 -0
- package/dist/plugins/pluginInstaller.js +42 -8
- package/dist/plugins/schemas.js +1 -0
- package/dist/plugins/types.d.ts +6 -0
- package/dist/preset/index.d.ts +11 -1
- package/dist/preset/index.js +115 -6
- package/dist/product/types.d.ts +1 -1
- package/dist/prompt/composer.d.ts +32 -0
- package/dist/prompt/composer.js +75 -21
- package/dist/prompt/instruction-scanner.js +5 -3
- package/dist/prompt/section-loader.js +1 -0
- package/dist/prompt/sections/base.md +2 -0
- package/dist/prompt/sections/browser.md +10 -0
- package/dist/prompt/sections/coding.md +4 -0
- package/dist/protocol/chat-session-manager.d.ts +9 -2
- package/dist/protocol/chat-session-manager.js +38 -0
- package/dist/protocol/chat-session.d.ts +69 -1
- package/dist/protocol/chat-session.js +102 -4
- package/dist/protocol/client.d.ts +31 -0
- package/dist/protocol/client.js +44 -0
- package/dist/protocol/server.d.ts +120 -7
- package/dist/protocol/server.js +753 -51
- package/dist/protocol/transport.js +3 -2
- package/dist/protocol/types.d.ts +60 -0
- package/dist/protocol/types.js +16 -0
- package/dist/quota/credentials.d.ts +3 -0
- package/dist/quota/credentials.js +80 -0
- package/dist/quota/index.d.ts +36 -0
- package/dist/quota/index.js +155 -0
- package/dist/quota/types.d.ts +48 -0
- package/dist/quota/types.js +13 -0
- package/dist/review/review-prompt.d.ts +28 -0
- package/dist/review/review-prompt.js +81 -0
- package/dist/run/FileRunStore.js +8 -3
- package/dist/run/RunApprovalBackend.js +25 -5
- package/dist/run/RunManager.d.ts +12 -0
- package/dist/run/RunManager.js +35 -0
- package/dist/run/factory.d.ts +1 -1
- package/dist/runtime/background-shell.d.ts +139 -0
- package/dist/runtime/background-shell.js +509 -0
- package/dist/runtime/output-clean.d.ts +24 -0
- package/dist/runtime/output-clean.js +41 -0
- package/dist/runtime/ring-file.d.ts +64 -0
- package/dist/runtime/ring-file.js +174 -0
- package/dist/runtime/safe-spawn.js +98 -39
- package/dist/runtime/spawn-common.d.ts +159 -0
- package/dist/runtime/spawn-common.js +404 -0
- package/dist/runtime/truncate-output.d.ts +22 -0
- package/dist/runtime/truncate-output.js +49 -0
- package/dist/runtime/utf8-cut.d.ts +11 -0
- package/dist/runtime/utf8-cut.js +23 -0
- package/dist/services/auto-dream.d.ts +4 -0
- package/dist/services/auto-dream.js +26 -26
- package/dist/services/diagnostics.d.ts +1 -2
- package/dist/services/diagnostics.js +12 -7
- package/dist/services/extract-memories.d.ts +14 -1
- package/dist/services/extract-memories.js +45 -6
- package/dist/services/memory-orchestrator.d.ts +21 -0
- package/dist/services/memory-orchestrator.js +125 -50
- package/dist/services/session-memory.js +24 -12
- package/dist/session/file-history.d.ts +124 -1
- package/dist/session/file-history.js +222 -6
- package/dist/session/memory.d.ts +116 -2
- package/dist/session/memory.js +250 -28
- package/dist/session/session-manager.d.ts +40 -0
- package/dist/session/session-manager.js +119 -3
- package/dist/session/simple-diff.d.ts +23 -0
- package/dist/session/simple-diff.js +84 -0
- package/dist/session/transcript.d.ts +29 -1
- package/dist/session/transcript.js +56 -2
- package/dist/session/undo-target.d.ts +67 -0
- package/dist/session/undo-target.js +144 -0
- package/dist/settings/disk-defaults.d.ts +9 -2
- package/dist/settings/disk-defaults.js +11 -2
- package/dist/settings/feature-flags.d.ts +64 -0
- package/dist/settings/feature-flags.js +61 -0
- package/dist/settings/manager.d.ts +68 -1
- package/dist/settings/manager.js +266 -29
- package/dist/settings/migrate-config.d.ts +45 -0
- package/dist/settings/migrate-config.js +125 -0
- package/dist/settings/schema-export.d.ts +25 -0
- package/dist/settings/schema-export.js +38 -0
- package/dist/settings/schema.d.ts +1186 -771
- package/dist/settings/schema.js +268 -97
- package/dist/skills/scanner.d.ts +9 -0
- package/dist/skills/scanner.js +30 -2
- package/dist/stt/resolve-transcribe.d.ts +31 -0
- package/dist/stt/resolve-transcribe.js +108 -0
- package/dist/stt/transcribe.d.ts +51 -0
- package/dist/stt/transcribe.js +65 -0
- package/dist/tool-system/browser-bridge.d.ts +226 -0
- package/dist/tool-system/browser-bridge.js +163 -0
- package/dist/tool-system/builtin/agent-heartbeat.d.ts +49 -0
- package/dist/tool-system/builtin/agent-heartbeat.js +89 -0
- package/dist/tool-system/builtin/agent-notifications.d.ts +12 -3
- package/dist/tool-system/builtin/agent-notifications.js +9 -3
- package/dist/tool-system/builtin/agent-output-file.d.ts +38 -0
- package/dist/tool-system/builtin/agent-output-file.js +72 -0
- package/dist/tool-system/builtin/agent-registry.d.ts +12 -0
- package/dist/tool-system/builtin/agent-registry.js +8 -0
- package/dist/tool-system/builtin/agent.d.ts +21 -1
- package/dist/tool-system/builtin/agent.js +489 -42
- package/dist/tool-system/builtin/apply-patch/applier.js +66 -8
- package/dist/tool-system/builtin/apply-patch/backup-targets.d.ts +10 -0
- package/dist/tool-system/builtin/apply-patch/backup-targets.js +30 -0
- package/dist/tool-system/builtin/apply-patch/index.js +0 -15
- package/dist/tool-system/builtin/background-jobs.d.ts +76 -0
- package/dist/tool-system/builtin/background-jobs.js +124 -0
- package/dist/tool-system/builtin/background-shell-tools.d.ts +20 -0
- package/dist/tool-system/builtin/background-shell-tools.js +108 -0
- package/dist/tool-system/builtin/background-work.d.ts +67 -0
- package/dist/tool-system/builtin/background-work.js +86 -0
- package/dist/tool-system/builtin/bash-output-style.d.ts +32 -0
- package/dist/tool-system/builtin/bash-output-style.js +40 -0
- package/dist/tool-system/builtin/bash.d.ts +5 -2
- package/dist/tool-system/builtin/bash.js +101 -64
- package/dist/tool-system/builtin/browser-tools.d.ts +33 -0
- package/dist/tool-system/builtin/browser-tools.js +312 -0
- package/dist/tool-system/builtin/cancel-goal.d.ts +31 -0
- package/dist/tool-system/builtin/cancel-goal.js +64 -0
- package/dist/tool-system/builtin/check-quota.d.ts +15 -0
- package/dist/tool-system/builtin/check-quota.js +34 -0
- package/dist/tool-system/builtin/config.js +7 -0
- package/dist/tool-system/builtin/cron.d.ts +7 -0
- package/dist/tool-system/builtin/cron.js +64 -4
- package/dist/tool-system/builtin/drive-claude-code.d.ts +30 -0
- package/dist/tool-system/builtin/drive-claude-code.js +157 -0
- package/dist/tool-system/builtin/edit-model-catalog.d.ts +3 -0
- package/dist/tool-system/builtin/edit-model-catalog.js +104 -0
- package/dist/tool-system/builtin/edit.js +20 -16
- package/dist/tool-system/builtin/eol.d.ts +29 -0
- package/dist/tool-system/builtin/eol.js +37 -0
- package/dist/tool-system/builtin/file-cache.d.ts +6 -0
- package/dist/tool-system/builtin/file-cache.js +8 -0
- package/dist/tool-system/builtin/generate-image.d.ts +35 -0
- package/dist/tool-system/builtin/generate-image.js +278 -50
- package/dist/tool-system/builtin/generate-video.d.ts +55 -0
- package/dist/tool-system/builtin/generate-video.js +364 -0
- package/dist/tool-system/builtin/glob.js +0 -7
- package/dist/tool-system/builtin/grep.d.ts +9 -0
- package/dist/tool-system/builtin/grep.js +106 -11
- package/dist/tool-system/builtin/image-providers.d.ts +86 -0
- package/dist/tool-system/builtin/image-providers.js +190 -0
- package/dist/tool-system/builtin/image-uploader.d.ts +33 -0
- package/dist/tool-system/builtin/image-uploader.js +74 -0
- package/dist/tool-system/builtin/index.d.ts +10 -2
- package/dist/tool-system/builtin/index.js +251 -27
- package/dist/tool-system/builtin/mcp-tools.js +23 -3
- package/dist/tool-system/builtin/memory.js +45 -7
- package/dist/tool-system/builtin/notebook-edit.js +0 -7
- package/dist/tool-system/builtin/powershell.js +8 -2
- package/dist/tool-system/builtin/read.js +10 -10
- package/dist/tool-system/builtin/repl.js +4 -1
- package/dist/tool-system/builtin/skill.js +9 -0
- package/dist/tool-system/builtin/sleep.js +8 -2
- package/dist/tool-system/builtin/tool-search.js +25 -7
- package/dist/tool-system/builtin/video-providers.d.ts +154 -0
- package/dist/tool-system/builtin/video-providers.js +235 -0
- package/dist/tool-system/builtin/web-fetch.js +12 -2
- package/dist/tool-system/builtin/web-search.js +21 -7
- package/dist/tool-system/builtin/worktree.d.ts +2 -1
- package/dist/tool-system/builtin/worktree.js +31 -4
- package/dist/tool-system/builtin/write.js +0 -6
- package/dist/tool-system/context.d.ts +164 -6
- package/dist/tool-system/executor.d.ts +3 -1
- package/dist/tool-system/executor.js +197 -90
- package/dist/tool-system/investigation-guard.js +1 -1
- package/dist/tool-system/mcp-manager.d.ts +42 -4
- package/dist/tool-system/mcp-manager.js +203 -20
- package/dist/tool-system/mcp-stdio-diagnostics.d.ts +9 -0
- package/dist/tool-system/mcp-stdio-diagnostics.js +93 -0
- package/dist/tool-system/path-policy.d.ts +5 -0
- package/dist/tool-system/path-policy.js +307 -8
- package/dist/tool-system/permission.d.ts +39 -2
- package/dist/tool-system/permission.js +283 -70
- package/dist/tool-system/plan-mode-allowlist.d.ts +13 -2
- package/dist/tool-system/plan-mode-allowlist.js +24 -2
- package/dist/tool-system/registry.d.ts +1 -0
- package/dist/tool-system/registry.js +16 -2
- package/dist/tool-system/sandbox/index.d.ts +8 -0
- package/dist/tool-system/sandbox/index.js +7 -2
- package/dist/tool-system/sandbox/off.js +7 -1
- package/dist/tool-system/validate-tool-metadata.d.ts +36 -0
- package/dist/tool-system/validate-tool-metadata.js +63 -0
- package/dist/types.d.ts +252 -7
- package/dist/updater.js +20 -9
- package/dist/utils/envUtils.d.ts +0 -9
- package/dist/utils/envUtils.js +3 -28
- package/dist/utils/exec.d.ts +48 -0
- package/dist/utils/exec.js +154 -0
- package/dist/utils/json.d.ts +12 -0
- package/dist/utils/json.js +92 -0
- package/dist/utils/theme.d.ts +1 -1
- package/dist/utils/theme.js +1 -1
- package/dist/utils/toolDisplay.js +0 -1
- package/package.json +13 -7
- package/dist/agent/coordinator.d.ts +0 -49
- package/dist/agent/coordinator.js +0 -77
- package/dist/settings/manager.test.js +0 -73
- package/dist/tool-system/builtin/remote-trigger.d.ts +0 -6
- package/dist/tool-system/builtin/remote-trigger.js +0 -54
- package/dist/tool-system/builtin/send-message.d.ts +0 -6
- package/dist/tool-system/builtin/send-message.js +0 -47
- /package/dist/{settings/manager.test.d.ts → credentials/types.js} +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getMergedCatalog } from "../model-catalog/index.js";
|
|
2
|
+
import { modelEntriesFromConnections } from "./model-connections-pool.js";
|
|
3
|
+
import { ModelPool } from "../llm/model-pool.js";
|
|
4
|
+
export function resolveLLMConfigForTag(settings, tag, preferredInstanceId) {
|
|
5
|
+
const connections = settings.modelConnections;
|
|
6
|
+
if (!Array.isArray(connections) || connections.length === 0)
|
|
7
|
+
return null;
|
|
8
|
+
const credentials = settings.credentials;
|
|
9
|
+
const catalog = getMergedCatalog();
|
|
10
|
+
const entries = modelEntriesFromConnections(connections, (Array.isArray(credentials) ? credentials : []), catalog);
|
|
11
|
+
if (entries.length === 0)
|
|
12
|
+
return null;
|
|
13
|
+
// entry.key === connection.id(见 modelEntriesFromConnections)。
|
|
14
|
+
const defaultId = settings.defaults?.text;
|
|
15
|
+
const pick = (preferredInstanceId && entries.find((e) => e.key === preferredInstanceId)) ||
|
|
16
|
+
(defaultId && entries.find((e) => e.key === defaultId)) ||
|
|
17
|
+
entries[0];
|
|
18
|
+
if (!pick)
|
|
19
|
+
return null;
|
|
20
|
+
// 用户显式选了 default/preferred 但它解析不到(连接的 catalogId 被删 →
|
|
21
|
+
// modelEntriesFromConnections 静默过滤掉)→ 这里 find 落空、悄悄回退到 entries[0]
|
|
22
|
+
// 是「换了个模型还不告诉你」。不改回退行为(有可用连接就用),但出一条 warn 让
|
|
23
|
+
// 静默替换可见(否则用户以为还在用选的那个)。
|
|
24
|
+
const wanted = preferredInstanceId ?? defaultId;
|
|
25
|
+
if (wanted && pick.key !== wanted) {
|
|
26
|
+
console.warn(`[resolveLLMConfigForTag] 偏好/默认连接 "${wanted}" 解析不到(catalogId 可能已删)— 回退到 "${pick.key}"。`);
|
|
27
|
+
}
|
|
28
|
+
// The picked connection's catalog template needs a key but none resolved
|
|
29
|
+
// (credentialId missing/points at a deleted credential). Returning null here
|
|
30
|
+
// routes to the caller's existing clean "没有可用的文本模型连接 … 请在「连接」
|
|
31
|
+
// 页添加并填写凭证" message + exit, instead of silently building a config with
|
|
32
|
+
// apiKey:undefined that only fails later as a cryptic provider 401. In
|
|
33
|
+
// CONTRACT: this fn's null already means "no USABLE connection for this tag".
|
|
34
|
+
// resolveInstance's no-crash (apiKey:undefined) behavior is untouched — this
|
|
35
|
+
// gate is one layer up where usability is decided.
|
|
36
|
+
if (pick.needsKey !== false && !pick.apiKey) {
|
|
37
|
+
console.warn(`[resolveLLMConfigForTag] 连接 "${pick.key}" 需要密钥但未解析到凭证(credentialId 缺失或指向已删凭证)— 视为该 tag 无可用连接。`);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
// 临时 pool 复用 toLLMConfig 的全部映射逻辑(reasoning/headers/providerKind 等)。
|
|
41
|
+
const pool = new ModelPool([]);
|
|
42
|
+
pool.register(pick);
|
|
43
|
+
return pool.toLLMConfig(pick);
|
|
44
|
+
}
|
package/dist/engine/runtime.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export declare class EngineRuntime {
|
|
|
23
23
|
readonly costTracker: CostTracker;
|
|
24
24
|
private sandboxCache;
|
|
25
25
|
constructor(opts: EngineRuntimeOptions);
|
|
26
|
+
clearModels(): void;
|
|
27
|
+
reloadModelsFromSettings(): void;
|
|
26
28
|
/**
|
|
27
29
|
* Lazily resolve and cache a sandbox backend for `(mode, cwd)`.
|
|
28
30
|
*
|
package/dist/engine/runtime.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { getMergedCatalog } from "../model-catalog/index.js";
|
|
2
|
+
import { modelEntriesFromConnections } from "./model-connections-pool.js";
|
|
3
|
+
import { defaultCacheDir } from "../llm/model-cache.js";
|
|
1
4
|
import { resolveSandboxBackend, } from "../tool-system/sandbox/index.js";
|
|
5
|
+
import { sandboxCacheKey } from "./sandbox-cache-key.js";
|
|
2
6
|
/**
|
|
3
7
|
* Shared read-only resources used by all Engine instances in a worker.
|
|
4
8
|
* Mutable per-session state stays on Engine itself.
|
|
@@ -21,6 +25,28 @@ export class EngineRuntime {
|
|
|
21
25
|
this.mcpPool = opts.mcpPool;
|
|
22
26
|
this.costTracker = opts.costTracker;
|
|
23
27
|
}
|
|
28
|
+
clearModels() {
|
|
29
|
+
this.modelPool.clear();
|
|
30
|
+
}
|
|
31
|
+
reloadModelsFromSettings() {
|
|
32
|
+
const settings = this.settings.load();
|
|
33
|
+
const connections = settings.modelConnections ?? [];
|
|
34
|
+
const credentials = settings.credentials ?? [];
|
|
35
|
+
const catalog = getMergedCatalog();
|
|
36
|
+
const entries = modelEntriesFromConnections(connections, credentials, catalog);
|
|
37
|
+
this.modelPool.clear();
|
|
38
|
+
if (entries.length === 0)
|
|
39
|
+
return;
|
|
40
|
+
for (const entry of entries)
|
|
41
|
+
this.modelPool.register(entry);
|
|
42
|
+
if (entries.length > 0) {
|
|
43
|
+
const defaultText = settings.defaults?.text;
|
|
44
|
+
const key = defaultText && entries.some((e) => e.key === defaultText) ? defaultText : entries[0].key;
|
|
45
|
+
this.modelPool.switch(key);
|
|
46
|
+
this.modelPool.setCacheDir(defaultCacheDir());
|
|
47
|
+
this.modelPool.reloadCachedContextWindows();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
24
50
|
/**
|
|
25
51
|
* Lazily resolve and cache a sandbox backend for `(mode, cwd)`.
|
|
26
52
|
*
|
|
@@ -31,7 +57,7 @@ export class EngineRuntime {
|
|
|
31
57
|
* violate standard §S4 fail-closed rule.
|
|
32
58
|
*/
|
|
33
59
|
resolveSandbox(config, cwd) {
|
|
34
|
-
const key =
|
|
60
|
+
const key = sandboxCacheKey(config, cwd);
|
|
35
61
|
let cached = this.sandboxCache.get(key);
|
|
36
62
|
if (!cached) {
|
|
37
63
|
cached = resolveSandboxBackend(config, cwd);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sandboxCacheKey — cache key for a resolved sandbox backend. Includes every
|
|
3
|
+
* field that affects the backend (mode, network, writableRoots, deniedReads)
|
|
4
|
+
* plus cwd. The key was previously `${mode}:${cwd}`, which ignored network /
|
|
5
|
+
* roots / reads — so editing those in 设置页 hit the stale cached backend and
|
|
6
|
+
* didn't take effect until app restart. Stable JSON shape so equal configs map
|
|
7
|
+
* to equal keys.
|
|
8
|
+
*/
|
|
9
|
+
import type { SandboxConfig } from "../tool-system/sandbox/index.js";
|
|
10
|
+
export declare function sandboxCacheKey(config: SandboxConfig, cwd: string): string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* resolveSandboxConfig — pick the sandbox config for a run, reading the
|
|
3
|
+
* 设置页's settings.sandbox so a project-scoped sandbox setting actually takes
|
|
4
|
+
* effect. Priority: explicit config.sandbox → settings.sandbox.mode → per-run
|
|
5
|
+
* default (headless=auto, REPL/desktop=off).
|
|
6
|
+
*
|
|
7
|
+
* Background: the engine previously only honored config.sandbox, which the
|
|
8
|
+
* desktop host never passes — so a user enabling sandbox in 设置页 had no
|
|
9
|
+
* effect. This bridges settings → run config.
|
|
10
|
+
* See memory project_10_review_bug_list #9.
|
|
11
|
+
*/
|
|
12
|
+
import { type SandboxConfig, type SandboxMode } from "../tool-system/sandbox/index.js";
|
|
13
|
+
/** The shape of settings.sandbox (all fields optional). */
|
|
14
|
+
export interface SettingsSandbox {
|
|
15
|
+
mode?: SandboxMode;
|
|
16
|
+
network?: "allow" | "deny";
|
|
17
|
+
writableRoots?: string[];
|
|
18
|
+
deniedReads?: string[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Three-layer resolve. Priority:
|
|
22
|
+
* 1. config.sandbox — explicit host-passed.
|
|
23
|
+
* 2. project settings.sandbox (has mode) — overrides global.
|
|
24
|
+
* 3. global settings.sandbox (has mode) — what a project "follows".
|
|
25
|
+
* 4. per-run default: headless → auto, desktop/REPL → off.
|
|
26
|
+
*
|
|
27
|
+
* A layer without `mode` is "unset / 跟随上一层" — pass unmerged per-scope
|
|
28
|
+
* values so a project that wrote nothing genuinely follows global (a merged
|
|
29
|
+
* value would make every project look like it set global's mode).
|
|
30
|
+
*/
|
|
31
|
+
export declare function resolveSandboxConfig(configSandbox: SandboxConfig | undefined, projectSandbox: SettingsSandbox | undefined, globalSandbox: SettingsSandbox | undefined, headless: boolean): SandboxConfig;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* resolveSandboxConfig — pick the sandbox config for a run, reading the
|
|
3
|
+
* 设置页's settings.sandbox so a project-scoped sandbox setting actually takes
|
|
4
|
+
* effect. Priority: explicit config.sandbox → settings.sandbox.mode → per-run
|
|
5
|
+
* default (headless=auto, REPL/desktop=off).
|
|
6
|
+
*
|
|
7
|
+
* Background: the engine previously only honored config.sandbox, which the
|
|
8
|
+
* desktop host never passes — so a user enabling sandbox in 设置页 had no
|
|
9
|
+
* effect. This bridges settings → run config.
|
|
10
|
+
* See memory project_10_review_bug_list #9.
|
|
11
|
+
*/
|
|
12
|
+
import { defaultSandboxConfig } from "../tool-system/sandbox/index.js";
|
|
13
|
+
/**
|
|
14
|
+
* Three-layer resolve. Priority:
|
|
15
|
+
* 1. config.sandbox — explicit host-passed.
|
|
16
|
+
* 2. project settings.sandbox (has mode) — overrides global.
|
|
17
|
+
* 3. global settings.sandbox (has mode) — what a project "follows".
|
|
18
|
+
* 4. per-run default: headless → auto, desktop/REPL → off.
|
|
19
|
+
*
|
|
20
|
+
* A layer without `mode` is "unset / 跟随上一层" — pass unmerged per-scope
|
|
21
|
+
* values so a project that wrote nothing genuinely follows global (a merged
|
|
22
|
+
* value would make every project look like it set global's mode).
|
|
23
|
+
*/
|
|
24
|
+
export function resolveSandboxConfig(configSandbox, projectSandbox, globalSandbox, headless) {
|
|
25
|
+
if (configSandbox)
|
|
26
|
+
return configSandbox;
|
|
27
|
+
const layer = projectSandbox?.mode ? projectSandbox : globalSandbox?.mode ? globalSandbox : undefined;
|
|
28
|
+
if (layer?.mode) {
|
|
29
|
+
const base = defaultSandboxConfig(layer.mode);
|
|
30
|
+
return {
|
|
31
|
+
mode: layer.mode,
|
|
32
|
+
network: layer.network ?? base.network,
|
|
33
|
+
writableRoots: layer.writableRoots ?? base.writableRoots,
|
|
34
|
+
deniedReads: layer.deniedReads ?? base.deniedReads,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return defaultSandboxConfig(headless ? "auto" : "off");
|
|
38
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { TokenUsage } from "../types.js";
|
|
2
|
+
import type { LLMUsageTracker } from "../llm/types.js";
|
|
3
|
+
export interface CumulativeUsageCounters {
|
|
4
|
+
cumulativePromptTokens: number;
|
|
5
|
+
cumulativeCacheReadTokens: number;
|
|
6
|
+
cumulativeCacheCreationTokens: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function emptyCumulativeUsageCounters(): CumulativeUsageCounters;
|
|
9
|
+
export declare function normalizeCumulativeUsageCounters(counters: Partial<CumulativeUsageCounters> | undefined, legacyUsage?: TokenUsage): CumulativeUsageCounters;
|
|
10
|
+
export declare function addCumulativeUsage(counters: Partial<CumulativeUsageCounters> | undefined, usage: TokenUsage): CumulativeUsageCounters;
|
|
11
|
+
export declare function addTokenUsage(left: TokenUsage, right: TokenUsage): TokenUsage;
|
|
12
|
+
export declare function cacheHitRateFromTokens(promptTokens: number, cacheReadTokens: number | undefined, cacheCreationTokens: number | undefined): number | undefined;
|
|
13
|
+
export declare function cacheHitRateFromUsage(usage: TokenUsage | undefined): number | undefined;
|
|
14
|
+
export declare function cumulativeCacheHitRate(counters: CumulativeUsageCounters): number | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Fold one run's cumulative usage onto a session baseline, producing the new
|
|
17
|
+
* session-cumulative TokenUsage.
|
|
18
|
+
*
|
|
19
|
+
* The LLM client is recreated per `engine.run()`, so its `getUsage()` totals
|
|
20
|
+
* count only the current run and start from zero each run. To get a true
|
|
21
|
+
* session cumulative we add the run's running total onto the baseline captured
|
|
22
|
+
* at run start (the value persisted from prior runs).
|
|
23
|
+
*
|
|
24
|
+
* Crucially this is `baseline + current`, NOT `baseline += current`: the
|
|
25
|
+
* turn-boundary heartbeat fires many times within one run, each reading the
|
|
26
|
+
* run's growing total. Recomputing `baseline + current` every time is
|
|
27
|
+
* idempotent — it never double-counts — whereas a `+=` would add the running
|
|
28
|
+
* total repeatedly. Feed the folded result back as the baseline only across
|
|
29
|
+
* run boundaries (new client, fresh run total).
|
|
30
|
+
*/
|
|
31
|
+
export declare function foldRunUsage(baseline: TokenUsage, runUsage: LLMUsageTracker): TokenUsage;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export function emptyCumulativeUsageCounters() {
|
|
2
|
+
return {
|
|
3
|
+
cumulativePromptTokens: 0,
|
|
4
|
+
cumulativeCacheReadTokens: 0,
|
|
5
|
+
cumulativeCacheCreationTokens: 0,
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export function normalizeCumulativeUsageCounters(counters, legacyUsage) {
|
|
9
|
+
return {
|
|
10
|
+
cumulativePromptTokens: typeof counters?.cumulativePromptTokens === "number"
|
|
11
|
+
? counters.cumulativePromptTokens
|
|
12
|
+
: (legacyUsage?.promptTokens ?? 0),
|
|
13
|
+
cumulativeCacheReadTokens: typeof counters?.cumulativeCacheReadTokens === "number"
|
|
14
|
+
? counters.cumulativeCacheReadTokens
|
|
15
|
+
: (legacyUsage?.cacheReadTokens ?? 0),
|
|
16
|
+
cumulativeCacheCreationTokens: typeof counters?.cumulativeCacheCreationTokens === "number"
|
|
17
|
+
? counters.cumulativeCacheCreationTokens
|
|
18
|
+
: (legacyUsage?.cacheCreationTokens ?? 0),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function addCumulativeUsage(counters, usage) {
|
|
22
|
+
const current = normalizeCumulativeUsageCounters(counters);
|
|
23
|
+
return {
|
|
24
|
+
cumulativePromptTokens: current.cumulativePromptTokens + (usage.promptTokens ?? 0),
|
|
25
|
+
cumulativeCacheReadTokens: current.cumulativeCacheReadTokens + (usage.cacheReadTokens ?? 0),
|
|
26
|
+
cumulativeCacheCreationTokens: current.cumulativeCacheCreationTokens + (usage.cacheCreationTokens ?? 0),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function addTokenUsage(left, right) {
|
|
30
|
+
return {
|
|
31
|
+
promptTokens: left.promptTokens + (right.promptTokens ?? 0),
|
|
32
|
+
completionTokens: left.completionTokens + (right.completionTokens ?? 0),
|
|
33
|
+
totalTokens: left.totalTokens + (right.totalTokens ?? 0),
|
|
34
|
+
cacheReadTokens: (left.cacheReadTokens ?? 0) + (right.cacheReadTokens ?? 0),
|
|
35
|
+
cacheCreationTokens: (left.cacheCreationTokens ?? 0) + (right.cacheCreationTokens ?? 0),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function cacheHitRateFromTokens(promptTokens, cacheReadTokens, cacheCreationTokens) {
|
|
39
|
+
const read = cacheReadTokens ?? 0;
|
|
40
|
+
const creation = cacheCreationTokens ?? 0;
|
|
41
|
+
if (read === 0 && creation === 0)
|
|
42
|
+
return undefined;
|
|
43
|
+
const uncached = Math.max(0, promptTokens - read - creation);
|
|
44
|
+
const denom = read + creation + uncached;
|
|
45
|
+
if (denom === 0)
|
|
46
|
+
return undefined;
|
|
47
|
+
return read / denom;
|
|
48
|
+
}
|
|
49
|
+
export function cacheHitRateFromUsage(usage) {
|
|
50
|
+
if (!usage)
|
|
51
|
+
return undefined;
|
|
52
|
+
return cacheHitRateFromTokens(usage.promptTokens ?? 0, usage.cacheReadTokens, usage.cacheCreationTokens);
|
|
53
|
+
}
|
|
54
|
+
export function cumulativeCacheHitRate(counters) {
|
|
55
|
+
return cacheHitRateFromTokens(counters.cumulativePromptTokens, counters.cumulativeCacheReadTokens, counters.cumulativeCacheCreationTokens);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Fold one run's cumulative usage onto a session baseline, producing the new
|
|
59
|
+
* session-cumulative TokenUsage.
|
|
60
|
+
*
|
|
61
|
+
* The LLM client is recreated per `engine.run()`, so its `getUsage()` totals
|
|
62
|
+
* count only the current run and start from zero each run. To get a true
|
|
63
|
+
* session cumulative we add the run's running total onto the baseline captured
|
|
64
|
+
* at run start (the value persisted from prior runs).
|
|
65
|
+
*
|
|
66
|
+
* Crucially this is `baseline + current`, NOT `baseline += current`: the
|
|
67
|
+
* turn-boundary heartbeat fires many times within one run, each reading the
|
|
68
|
+
* run's growing total. Recomputing `baseline + current` every time is
|
|
69
|
+
* idempotent — it never double-counts — whereas a `+=` would add the running
|
|
70
|
+
* total repeatedly. Feed the folded result back as the baseline only across
|
|
71
|
+
* run boundaries (new client, fresh run total).
|
|
72
|
+
*/
|
|
73
|
+
export function foldRunUsage(baseline, runUsage) {
|
|
74
|
+
return {
|
|
75
|
+
promptTokens: baseline.promptTokens + runUsage.totalPromptTokens,
|
|
76
|
+
completionTokens: baseline.completionTokens + runUsage.totalCompletionTokens,
|
|
77
|
+
totalTokens: baseline.totalTokens + runUsage.totalTokens,
|
|
78
|
+
cacheReadTokens: (baseline.cacheReadTokens ?? 0) + runUsage.totalCacheReadTokens,
|
|
79
|
+
cacheCreationTokens: (baseline.cacheCreationTokens ?? 0) + runUsage.totalCacheCreationTokens,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Step-gap steering queue — pure helpers over a per-session list of pending
|
|
3
|
+
* user messages. Each entry carries a stable `id` so the host can (a) match the
|
|
4
|
+
* later `steer_injected` event back to the queued draft it showed in the UI and
|
|
5
|
+
* (b) revoke a still-pending entry via `unsteer` before the turn loop consumes
|
|
6
|
+
* it. Pure + side-effect-free so it unit-tests without the Engine/TurnLoop
|
|
7
|
+
* harness (same rationale as the renderer's queuedInput.ts).
|
|
8
|
+
*/
|
|
9
|
+
export interface SteerItem {
|
|
10
|
+
id: string;
|
|
11
|
+
text: string;
|
|
12
|
+
clientMessageId?: string;
|
|
13
|
+
}
|
|
14
|
+
/** Append a steer entry. Blank text is dropped (returns the list unchanged). */
|
|
15
|
+
export declare function enqueueSteerItem(list: SteerItem[], id: string, text: string, clientMessageId?: string): SteerItem[];
|
|
16
|
+
/**
|
|
17
|
+
* Take everything currently queued and clear the list. Returns the drained
|
|
18
|
+
* entries (in order) and the now-empty remainder. The turn loop calls this at
|
|
19
|
+
* each step boundary.
|
|
20
|
+
*/
|
|
21
|
+
export declare function consumeSteerItems(list: SteerItem[]): {
|
|
22
|
+
drained: SteerItem[];
|
|
23
|
+
rest: SteerItem[];
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Remove a still-pending entry by id (the 撤回 path). Returns the new list and
|
|
27
|
+
* whether anything was removed — `false` means it was already consumed (the
|
|
28
|
+
* host can't take it back; it has already been spliced into the run).
|
|
29
|
+
*/
|
|
30
|
+
export declare function removeSteerItem(list: SteerItem[], id: string): {
|
|
31
|
+
list: SteerItem[];
|
|
32
|
+
removed: boolean;
|
|
33
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Append a steer entry. Blank text is dropped (returns the list unchanged). */
|
|
2
|
+
export function enqueueSteerItem(list, id, text, clientMessageId) {
|
|
3
|
+
const t = text?.trim();
|
|
4
|
+
if (!id || !t)
|
|
5
|
+
return list;
|
|
6
|
+
return [...list, { id, text: t, ...(clientMessageId ? { clientMessageId } : {}) }];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Take everything currently queued and clear the list. Returns the drained
|
|
10
|
+
* entries (in order) and the now-empty remainder. The turn loop calls this at
|
|
11
|
+
* each step boundary.
|
|
12
|
+
*/
|
|
13
|
+
export function consumeSteerItems(list) {
|
|
14
|
+
if (list.length === 0)
|
|
15
|
+
return { drained: [], rest: [] };
|
|
16
|
+
return { drained: list, rest: [] };
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Remove a still-pending entry by id (the 撤回 path). Returns the new list and
|
|
20
|
+
* whether anything was removed — `false` means it was already consumed (the
|
|
21
|
+
* host can't take it back; it has already been spliced into the run).
|
|
22
|
+
*/
|
|
23
|
+
export function removeSteerItem(list, id) {
|
|
24
|
+
const next = list.filter((item) => item.id !== id);
|
|
25
|
+
return { list: next, removed: next.length !== list.length };
|
|
26
|
+
}
|
|
@@ -17,6 +17,7 @@ export declare class StreamingToolQueue {
|
|
|
17
17
|
private readonly pending;
|
|
18
18
|
private readonly unsafeQueue;
|
|
19
19
|
private readonly callOrder;
|
|
20
|
+
private readonly toolNameById;
|
|
20
21
|
private draining;
|
|
21
22
|
constructor(executor: ToolExecutor);
|
|
22
23
|
/**
|
|
@@ -27,8 +28,19 @@ export declare class StreamingToolQueue {
|
|
|
27
28
|
/**
|
|
28
29
|
* Execute all remaining unsafe tools sequentially, then await all results.
|
|
29
30
|
* Returns results in original enqueue order.
|
|
31
|
+
*
|
|
32
|
+
* A tool's promise can REJECT — not just resolve to an error ToolResult —
|
|
33
|
+
* because permission.handleAsk (and pre_tool_use "ask") throw outside
|
|
34
|
+
* executeSingle's try/catch. We must never let one rejection (a) abort the
|
|
35
|
+
* drain and lose the other tools' results, or (b) leave a hole that surfaces
|
|
36
|
+
* as `undefined` downstream (toolResultToBlock would crash on it). So every
|
|
37
|
+
* await is funneled through toResult(), which converts a thrown value into a
|
|
38
|
+
* synthetic error ToolResult, and any id still missing at the end is filled
|
|
39
|
+
* with one too.
|
|
30
40
|
*/
|
|
31
41
|
drain(): Promise<ToolResult[]>;
|
|
42
|
+
/** Await a tool promise, converting a rejection into an error ToolResult. */
|
|
43
|
+
private toResult;
|
|
32
44
|
/** Number of tools currently enqueued or executing. */
|
|
33
45
|
get size(): number;
|
|
34
46
|
}
|
|
@@ -15,6 +15,7 @@ export class StreamingToolQueue {
|
|
|
15
15
|
pending = new Map();
|
|
16
16
|
unsafeQueue = [];
|
|
17
17
|
callOrder = [];
|
|
18
|
+
toolNameById = new Map();
|
|
18
19
|
draining = false;
|
|
19
20
|
constructor(executor) {
|
|
20
21
|
this.executor = executor;
|
|
@@ -25,9 +26,14 @@ export class StreamingToolQueue {
|
|
|
25
26
|
*/
|
|
26
27
|
enqueue(call) {
|
|
27
28
|
this.callOrder.push(call.id);
|
|
29
|
+
this.toolNameById.set(call.id, call.toolName);
|
|
28
30
|
if (this.executor.isConcurrencySafe(call.toolName)) {
|
|
29
|
-
// Start immediately
|
|
30
|
-
|
|
31
|
+
// Start immediately. Attach a no-op catch so a synchronous/early rejection
|
|
32
|
+
// doesn't surface as an unhandledRejection before drain() awaits it; the
|
|
33
|
+
// real handling (→ error ToolResult) happens in drain()/toResult().
|
|
34
|
+
const p = this.executor.executeSingle(call);
|
|
35
|
+
p.catch(() => { });
|
|
36
|
+
this.pending.set(call.id, p);
|
|
31
37
|
}
|
|
32
38
|
else {
|
|
33
39
|
this.unsafeQueue.push(call);
|
|
@@ -36,23 +42,57 @@ export class StreamingToolQueue {
|
|
|
36
42
|
/**
|
|
37
43
|
* Execute all remaining unsafe tools sequentially, then await all results.
|
|
38
44
|
* Returns results in original enqueue order.
|
|
45
|
+
*
|
|
46
|
+
* A tool's promise can REJECT — not just resolve to an error ToolResult —
|
|
47
|
+
* because permission.handleAsk (and pre_tool_use "ask") throw outside
|
|
48
|
+
* executeSingle's try/catch. We must never let one rejection (a) abort the
|
|
49
|
+
* drain and lose the other tools' results, or (b) leave a hole that surfaces
|
|
50
|
+
* as `undefined` downstream (toolResultToBlock would crash on it). So every
|
|
51
|
+
* await is funneled through toResult(), which converts a thrown value into a
|
|
52
|
+
* synthetic error ToolResult, and any id still missing at the end is filled
|
|
53
|
+
* with one too.
|
|
39
54
|
*/
|
|
40
55
|
async drain() {
|
|
41
56
|
if (this.draining)
|
|
42
57
|
throw new Error("StreamingToolQueue already draining");
|
|
43
58
|
this.draining = true;
|
|
44
|
-
|
|
59
|
+
const resultMap = new Map();
|
|
60
|
+
// Execute unsafe tools sequentially. A rejection here must not stop the
|
|
61
|
+
// remaining unsafe tools from running.
|
|
45
62
|
for (const call of this.unsafeQueue) {
|
|
46
|
-
|
|
47
|
-
|
|
63
|
+
const p = this.executor.executeSingle(call);
|
|
64
|
+
this.pending.set(call.id, p);
|
|
65
|
+
resultMap.set(call.id, await this.toResult(call.id, call.toolName, p));
|
|
48
66
|
}
|
|
49
|
-
// Await all results
|
|
50
|
-
const resultMap = new Map();
|
|
67
|
+
// Await all results (concurrency-safe tools started at enqueue time).
|
|
51
68
|
for (const [id, promise] of this.pending) {
|
|
52
|
-
resultMap.
|
|
69
|
+
if (resultMap.has(id))
|
|
70
|
+
continue; // already drained above
|
|
71
|
+
const toolName = this.toolNameById.get(id) ?? "unknown";
|
|
72
|
+
resultMap.set(id, await this.toResult(id, toolName, promise));
|
|
73
|
+
}
|
|
74
|
+
// Return in original order, with a synthetic error for any id that somehow
|
|
75
|
+
// never produced a result (defensive — should not happen now).
|
|
76
|
+
return this.callOrder.map((id) => resultMap.get(id) ?? {
|
|
77
|
+
id,
|
|
78
|
+
toolName: this.toolNameById.get(id) ?? "unknown",
|
|
79
|
+
error: "Tool execution produced no result.",
|
|
80
|
+
isError: true,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/** Await a tool promise, converting a rejection into an error ToolResult. */
|
|
84
|
+
async toResult(id, toolName, p) {
|
|
85
|
+
try {
|
|
86
|
+
return await p;
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
return {
|
|
90
|
+
id,
|
|
91
|
+
toolName,
|
|
92
|
+
error: err instanceof Error ? err.message : String(err),
|
|
93
|
+
isError: true,
|
|
94
|
+
};
|
|
53
95
|
}
|
|
54
|
-
// Return in original order
|
|
55
|
-
return this.callOrder.map((id) => resultMap.get(id));
|
|
56
96
|
}
|
|
57
97
|
/** Number of tools currently enqueued or executing. */
|
|
58
98
|
get size() {
|
|
@@ -4,19 +4,27 @@
|
|
|
4
4
|
* Following Claude Code's po_() pattern:
|
|
5
5
|
* pre_check → model_call → post_check → tool_exec → context_mgmt → hook_notify → next turn
|
|
6
6
|
*/
|
|
7
|
-
import type { Message, StreamCallback, TerminalReason, ContentBlock, ToolResult } from "../types.js";
|
|
7
|
+
import type { Message, StreamCallback, TerminalReason, ContentBlock, ToolResult, TokenUsage } from "../types.js";
|
|
8
|
+
import type { SteerItem } from "./steer-queue.js";
|
|
8
9
|
import { ModelFacade } from "./model-facade.js";
|
|
9
10
|
import { ToolExecutor } from "../tool-system/executor.js";
|
|
10
11
|
import { ContextManager } from "../context/manager.js";
|
|
11
12
|
import { HookRegistry } from "../hooks/registry.js";
|
|
12
13
|
import { Transcript } from "../session/transcript.js";
|
|
13
|
-
import { type
|
|
14
|
+
import { type CumulativeUsageCounters } from "./session-usage.js";
|
|
15
|
+
import { type GoalConfig, type GoalExtension } from "./goal.js";
|
|
14
16
|
export interface TurnLoopConfig {
|
|
15
17
|
maxTurns: number;
|
|
16
18
|
maxToolCallsPerTurn: number;
|
|
17
19
|
tokenBudget?: number;
|
|
18
20
|
onStream?: StreamCallback;
|
|
19
21
|
signal?: AbortSignal;
|
|
22
|
+
/**
|
|
23
|
+
* Image-bearing messages added for the current request and not yet consumed
|
|
24
|
+
* by the model. TurnLoop preserves their base64 for one successful model
|
|
25
|
+
* response, then downgrades them to placeholders in the working history.
|
|
26
|
+
*/
|
|
27
|
+
freshImageMessages?: Iterable<Message>;
|
|
20
28
|
/**
|
|
21
29
|
* Fired after each turn boundary is recorded. Lets the engine flush an
|
|
22
30
|
* up-to-date snapshot to state.json mid-run, so a long run doesn't leave
|
|
@@ -35,7 +43,8 @@ export interface TurnLoopConfig {
|
|
|
35
43
|
* Max consecutive times an on_stop handler may block termination before
|
|
36
44
|
* the loop forces a stop, mirroring Claude Code's stop-hook block cap.
|
|
37
45
|
* Resets to 0 whenever a turn completes without being blocked. Defaults
|
|
38
|
-
* to
|
|
46
|
+
* to GOAL_DEFAULT_MAX_STOP_BLOCKS. Independent of maxTurns, which still
|
|
47
|
+
* bounds total turns.
|
|
39
48
|
*/
|
|
40
49
|
maxStopBlocks?: number;
|
|
41
50
|
}
|
|
@@ -74,6 +83,35 @@ export interface TurnLoopDeps {
|
|
|
74
83
|
before: number;
|
|
75
84
|
after: number;
|
|
76
85
|
} | null;
|
|
86
|
+
/**
|
|
87
|
+
* Records one LLM response into the session-monotonic cumulative counters.
|
|
88
|
+
* Returns the updated counters so usage_update can carry both metric scopes.
|
|
89
|
+
*/
|
|
90
|
+
recordCumulativeUsage?: (usage: TokenUsage) => CumulativeUsageCounters;
|
|
91
|
+
/**
|
|
92
|
+
* Reads/clears any user messages queued for THIS session via the steering
|
|
93
|
+
* channel (Engine.enqueueSteer) while a run is in flight. Consumed at the top
|
|
94
|
+
* of each turn so the messages join the next LLM request WITHOUT aborting the
|
|
95
|
+
* current run — the "不打断、步间隙注入" path (vs the relay abort+resend).
|
|
96
|
+
* Returns [] when nothing is queued. Wired by Engine; absent in standalone
|
|
97
|
+
* tests (turn loop tolerates undefined).
|
|
98
|
+
*/
|
|
99
|
+
consumeSteer?: (source?: "normal_step" | "finalize_backfill") => SteerItem[];
|
|
100
|
+
/**
|
|
101
|
+
* Execution-level idempotency guard for host-supplied user/steer intents.
|
|
102
|
+
* Returns false only when a present clientMessageId has already entered this
|
|
103
|
+
* run or the persisted transcript. Messages without an id bypass this guard.
|
|
104
|
+
*/
|
|
105
|
+
claimClientMessageId?: (clientMessageId: string, source: "steer") => boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Clear this session's PERSISTED goal (state.activeGoal) and drop the
|
|
108
|
+
* in-flight goal-stop hook, so a user-initiated cancel_goal both stops the
|
|
109
|
+
* current run AND prevents the goal from re-inheriting on the next bare send.
|
|
110
|
+
* Wired by Engine (delegates to Engine.clearGoal for the running session);
|
|
111
|
+
* absent in standalone tests (turn loop tolerates undefined — the local
|
|
112
|
+
* goalTracker short-circuit still stops the run).
|
|
113
|
+
*/
|
|
114
|
+
clearPersistedGoal?: () => void;
|
|
77
115
|
}
|
|
78
116
|
export interface TurnLoopResult {
|
|
79
117
|
text: string;
|
|
@@ -88,7 +126,7 @@ export interface TurnLoopResult {
|
|
|
88
126
|
export declare function toolResultToBlock(result: ToolResult): ContentBlock;
|
|
89
127
|
export declare class TurnLoop {
|
|
90
128
|
private readonly deps;
|
|
91
|
-
private
|
|
129
|
+
private config;
|
|
92
130
|
private turnCount;
|
|
93
131
|
/** Tool IDs already emitted as tool_use_start during streaming (to avoid duplicates). */
|
|
94
132
|
private streamedToolIds;
|
|
@@ -101,12 +139,49 @@ export declare class TurnLoop {
|
|
|
101
139
|
private currentTurnLog;
|
|
102
140
|
/** Last emitted ctx token estimate; used to skip no-op usage_update events. */
|
|
103
141
|
private lastCtxEmit;
|
|
142
|
+
private currentTurnUsage;
|
|
143
|
+
private currentCumulativeUsage;
|
|
144
|
+
private readonly pendingImageMessages;
|
|
104
145
|
/**
|
|
105
146
|
* Consecutive on_stop blocks (Goal mode kept the agent going). Reset to 0
|
|
106
147
|
* on any unblocked completion. When it reaches config.maxStopBlocks the
|
|
107
148
|
* loop forces a stop so a stuck goal can't loop forever.
|
|
108
149
|
*/
|
|
109
150
|
private stopBlockCount;
|
|
151
|
+
/**
|
|
152
|
+
* Run-scoped goal budget tracker (Goal mode). Hoisted to an instance field
|
|
153
|
+
* (not a run() local) so extend() can bump its budgets mid-run. Null when no
|
|
154
|
+
* goal or between runs.
|
|
155
|
+
*/
|
|
156
|
+
private goalTracker;
|
|
157
|
+
/**
|
|
158
|
+
* Whether an approaching_limit marker was already emitted for the current
|
|
159
|
+
* ceiling. Reset when the run advances past the moment (a fresh extension, or
|
|
160
|
+
* the goal completing) so the next approach re-announces. Prevents the marker
|
|
161
|
+
* being re-emitted every turn while still within the approach threshold.
|
|
162
|
+
*/
|
|
163
|
+
private approachAnnounced;
|
|
164
|
+
/**
|
|
165
|
+
* Extend the in-flight run's limits (TODO 3.1 — 运行中续轮/加预算). Mutates
|
|
166
|
+
* the maxTurns ceiling, the maxStopBlocks cap, and/or the live goal budgets;
|
|
167
|
+
* the loop re-reads all of them each turn so the change takes effect on the
|
|
168
|
+
* next iteration. No-op for fields not supplied. Returns the resulting
|
|
169
|
+
* effective limits.
|
|
170
|
+
*/
|
|
171
|
+
extend(opts: GoalExtension): {
|
|
172
|
+
maxTurns: number;
|
|
173
|
+
tokenBudget?: number;
|
|
174
|
+
timeBudgetMs?: number;
|
|
175
|
+
maxStopBlocks: number;
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* Goal mode only: if the run is nearing EITHER stop ceiling (maxTurns or
|
|
179
|
+
* maxStopBlocks) and we haven't announced it yet, emit one approaching_limit
|
|
180
|
+
* marker so the UI can offer a "再续" button while the run is still live.
|
|
181
|
+
* Watches both limits because a re-blocked goal hits the stop-block cap long
|
|
182
|
+
* before maxTurns. Idempotent within an approach window via approachAnnounced.
|
|
183
|
+
*/
|
|
184
|
+
private maybeAnnounceApproachingLimit;
|
|
110
185
|
constructor(deps: TurnLoopDeps, config: TurnLoopConfig);
|
|
111
186
|
/**
|
|
112
187
|
* Emit a lifecycle hook with isSubAgent + sessionId auto-merged into data.
|
|
@@ -114,6 +189,19 @@ export declare class TurnLoop {
|
|
|
114
189
|
* `decision` / `stop`. Use this instead of `deps.hooks.emit` directly so
|
|
115
190
|
* every emit carries the same context envelope.
|
|
116
191
|
*/
|
|
192
|
+
/**
|
|
193
|
+
* Persist a turn_stopped marker on a user Stop so a resume can rebuild the
|
|
194
|
+
* renderer's "你在 Ns 后停止了" line (the in-memory turn_end is lost on reload,
|
|
195
|
+
* which makes the interrupted turn fold behind the process-card header).
|
|
196
|
+
* Skipped for sub-agents — their signal.aborted is usually a parent abort,
|
|
197
|
+
* not a user Stop, and sub-agent turns aren't shown as interruptible. The
|
|
198
|
+
* underlying append is idempotent, so calling this at several abort returns
|
|
199
|
+
* for one Stop still writes a single marker.
|
|
200
|
+
*/
|
|
201
|
+
private markStopped;
|
|
202
|
+
private prepareMessagesForModel;
|
|
203
|
+
private markPendingImagesConsumed;
|
|
204
|
+
private trackFreshImageMessage;
|
|
117
205
|
private emitHook;
|
|
118
206
|
/**
|
|
119
207
|
* Emit a usage_update so the UI ctx bar reflects current message-array
|
|
@@ -123,6 +211,7 @@ export declare class TurnLoop {
|
|
|
123
211
|
* promptTokens to override our estimate).
|
|
124
212
|
*/
|
|
125
213
|
private emitCtxFromMessages;
|
|
214
|
+
private recordResponseUsage;
|
|
126
215
|
private emitCtxFromUsage;
|
|
127
216
|
/**
|
|
128
217
|
* Run the multi-turn agent loop until completion.
|
|
@@ -134,6 +223,7 @@ export declare class TurnLoop {
|
|
|
134
223
|
*/
|
|
135
224
|
private callModelWithFallback;
|
|
136
225
|
get currentTurn(): number;
|
|
226
|
+
private consumeQueuedSteer;
|
|
137
227
|
/**
|
|
138
228
|
* Generate synthetic error tool_results for any dangling tool_use blocks
|
|
139
229
|
* that never received results (e.g. because the API call failed).
|