@cjhyy/code-shell-core 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -23
- package/dist/agent/agent-definition-registry.d.ts +2 -2
- package/dist/agent/agent-definition-registry.js +47 -10
- package/dist/agent/agent-definition.d.ts +5 -0
- package/dist/agent/agent-definition.js +14 -0
- package/dist/automation/index.d.ts +7 -4
- package/dist/automation/index.js +4 -2
- package/dist/automation/runner.js +3 -1
- package/dist/automation/scheduler.d.ts +57 -1
- package/dist/automation/scheduler.js +123 -23
- package/dist/automation/store.d.ts +6 -2
- package/dist/automation/store.js +7 -3
- package/dist/capabilities/index.d.ts +109 -0
- package/dist/capabilities/index.js +80 -0
- package/dist/capability-control/disabled-lists.d.ts +2 -1
- package/dist/capability-control/disabled-lists.js +3 -5
- package/dist/capability-control/overlay.d.ts +14 -1
- package/dist/capability-control/overlay.js +40 -0
- package/dist/capability-control/service.js +6 -4
- package/dist/cli/agent-server-stdio.d.ts +1 -1
- package/dist/cli/agent-server-stdio.js +66 -6
- package/dist/cli/agent-server-tcp.js +20 -5
- package/dist/context/compaction.d.ts +39 -3
- package/dist/context/compaction.js +151 -39
- package/dist/context/manager.d.ts +20 -4
- package/dist/context/manager.js +42 -7
- package/dist/context/token-counter.js +5 -3
- package/dist/context/tool-result-storage.d.ts +6 -0
- package/dist/context/tool-result-storage.js +25 -4
- package/dist/credentials/access.d.ts +13 -2
- package/dist/credentials/access.js +99 -4
- package/dist/credentials/index.d.ts +3 -2
- package/dist/credentials/index.js +2 -1
- package/dist/credentials/oauth.d.ts +9 -1
- package/dist/credentials/oauth.js +80 -1
- package/dist/credentials/store.d.ts +16 -2
- package/dist/credentials/store.js +33 -11
- package/dist/credentials/types.d.ts +62 -1
- package/dist/credentials/types.js +16 -1
- package/dist/credentials/use-credential-tool.js +15 -1
- package/dist/engine/auxiliary-pipeline.d.ts +40 -0
- package/dist/engine/auxiliary-pipeline.js +181 -0
- package/dist/{settings → engine}/disk-defaults.d.ts +2 -2
- package/dist/{settings → engine}/disk-defaults.js +5 -2
- package/dist/engine/dynamic-tool-defs.d.ts +4 -0
- package/dist/engine/dynamic-tool-defs.js +4 -0
- package/dist/engine/engine.d.ts +272 -184
- package/dist/engine/engine.js +1869 -1864
- package/dist/engine/file-history-hook.d.ts +11 -0
- package/dist/engine/file-history-hook.js +31 -0
- package/dist/engine/goal-judge-context.d.ts +9 -0
- package/dist/engine/goal-judge-context.js +234 -0
- package/dist/engine/injected-context-cache.d.ts +6 -0
- package/dist/engine/injected-context-cache.js +13 -0
- package/dist/engine/input-attachments.d.ts +1 -1
- package/dist/engine/model-facade.d.ts +3 -3
- package/dist/engine/model-facade.js +15 -14
- package/dist/engine/permission-controller.d.ts +55 -0
- package/dist/engine/permission-controller.js +148 -0
- package/dist/engine/prompt-cache-diagnostics.d.ts +58 -0
- package/dist/engine/prompt-cache-diagnostics.js +178 -0
- package/dist/engine/run-accounting.d.ts +41 -0
- package/dist/engine/run-accounting.js +126 -0
- package/dist/engine/run-context.d.ts +44 -0
- package/dist/engine/run-context.js +88 -0
- package/dist/engine/run-environment.d.ts +30 -0
- package/dist/engine/run-environment.js +77 -0
- package/dist/engine/run-finalize.d.ts +58 -0
- package/dist/engine/run-finalize.js +245 -0
- package/dist/engine/run-goal.d.ts +58 -0
- package/dist/engine/run-goal.js +188 -0
- package/dist/engine/run-image-input.d.ts +1 -1
- package/dist/engine/run-image-input.js +5 -3
- package/dist/engine/run-session-open.d.ts +41 -0
- package/dist/engine/run-session-open.js +186 -0
- package/dist/engine/run-setup.d.ts +42 -0
- package/dist/engine/run-setup.js +58 -0
- package/dist/engine/run-tooling.d.ts +125 -0
- package/dist/engine/run-tooling.js +283 -0
- package/dist/engine/run-types.d.ts +139 -0
- package/dist/engine/run-types.js +12 -0
- package/dist/engine/run-workspace.d.ts +50 -0
- package/dist/engine/run-workspace.js +114 -0
- package/dist/engine/session-title.d.ts +2 -1
- package/dist/engine/session-title.js +4 -1
- package/dist/engine/steer-queue.d.ts +1 -1
- package/dist/engine/subagent-spawner.d.ts +32 -0
- package/dist/engine/subagent-spawner.js +287 -0
- package/dist/engine/tool-summary.d.ts +2 -2
- package/dist/engine/tool-summary.js +2 -2
- package/dist/engine/turn-loop.d.ts +67 -10
- package/dist/engine/turn-loop.js +538 -97
- package/dist/engine/types.d.ts +35 -4
- package/dist/engine/types.js +1 -1
- package/dist/{engine/goal.d.ts → goal/lifecycle.d.ts} +80 -6
- package/dist/goal/lifecycle.js +482 -0
- package/dist/hooks/events.d.ts +26 -9
- package/dist/hooks/events.js +0 -3
- package/dist/hooks/goal-stop-hook.d.ts +28 -29
- package/dist/hooks/goal-stop-hook.js +344 -144
- package/dist/hooks/registry.js +4 -1
- package/dist/hooks/shell-runner.d.ts +12 -1
- package/dist/hooks/shell-runner.js +160 -9
- package/dist/index.d.ts +51 -110
- package/dist/index.extension.d.ts +51 -0
- package/dist/index.extension.js +39 -0
- package/dist/index.internal.d.ts +86 -0
- package/dist/index.internal.js +92 -0
- package/dist/index.js +37 -125
- package/dist/links/cli.d.ts +48 -0
- package/dist/links/cli.js +617 -0
- package/dist/links/http.d.ts +40 -0
- package/dist/links/http.js +166 -0
- package/dist/links/index.d.ts +4 -0
- package/dist/links/index.js +3 -0
- package/dist/links/link-action-tool.d.ts +5 -0
- package/dist/links/link-action-tool.js +241 -0
- package/dist/links/providers.d.ts +8 -0
- package/dist/links/providers.js +665 -0
- package/dist/links/types.d.ts +44 -0
- package/dist/llm/client-base.d.ts +7 -0
- package/dist/llm/client-base.js +72 -10
- package/dist/llm/model-pool.d.ts +1 -1
- package/dist/llm/model-pool.js +1 -1
- package/dist/llm/providers/anthropic.d.ts +1 -0
- package/dist/llm/providers/anthropic.js +9 -0
- package/dist/llm/providers/openai.d.ts +1 -0
- package/dist/llm/providers/openai.js +17 -0
- package/dist/llm/types.d.ts +12 -5
- package/dist/model-catalog/index.js +15 -2
- package/dist/model-catalog/resolve.d.ts +13 -1
- package/dist/model-catalog/resolve.js +37 -2
- package/dist/model-catalog/types.d.ts +74 -48
- package/dist/model-catalog/types.js +16 -5
- package/dist/model-catalog/upsert.d.ts +3 -1
- package/dist/model-catalog/upsert.js +9 -0
- package/dist/onboarding.d.ts +0 -1
- package/dist/onboarding.js +1 -22
- package/dist/panel-apps/bindings.d.ts +24 -0
- package/dist/panel-apps/bindings.js +69 -0
- package/dist/panel-apps/github-archive.d.ts +21 -0
- package/dist/panel-apps/github-archive.js +98 -0
- package/dist/panel-apps/index.d.ts +4 -0
- package/dist/panel-apps/index.js +4 -0
- package/dist/panel-apps/installer.d.ts +68 -0
- package/dist/panel-apps/installer.js +622 -0
- package/dist/panel-apps/manifest.d.ts +364 -0
- package/dist/panel-apps/manifest.js +189 -0
- package/dist/panel-apps/paths.d.ts +13 -0
- package/dist/panel-apps/paths.js +38 -0
- package/dist/panel-apps/registry.d.ts +51 -0
- package/dist/panel-apps/registry.js +99 -0
- package/dist/panel-apps/runtime.d.ts +80 -0
- package/dist/panel-apps/runtime.js +154 -0
- package/dist/plugins/gitOps.js +8 -1
- package/dist/plugins/installedPlugins.d.ts +2 -1
- package/dist/plugins/installedPlugins.js +146 -8
- package/dist/plugins/installer/codex/convertAgents.d.ts +4 -1
- package/dist/plugins/installer/codex/convertAgents.js +62 -8
- package/dist/plugins/installer/codex/convertCommands.d.ts +5 -5
- package/dist/plugins/installer/codex/convertCommands.js +35 -12
- package/dist/plugins/installer/codex/convertHooks.d.ts +10 -0
- package/dist/plugins/installer/codex/convertHooks.js +82 -0
- package/dist/plugins/installer/codex/convertSkills.js +41 -6
- package/dist/plugins/installer/install.d.ts +7 -1
- package/dist/plugins/installer/install.js +21 -87
- package/dist/plugins/installer/installFromArchive.d.ts +26 -4
- package/dist/plugins/installer/installFromArchive.js +44 -27
- package/dist/plugins/installer/installFromNpm.d.ts +31 -0
- package/dist/plugins/installer/installFromNpm.js +273 -0
- package/dist/plugins/installer/loadPluginAgents.js +17 -4
- package/dist/plugins/installer/loadPluginMcp.d.ts +14 -0
- package/dist/plugins/installer/loadPluginMcp.js +245 -35
- package/dist/plugins/installer/normalizeManifest.d.ts +13 -0
- package/dist/plugins/installer/normalizeManifest.js +306 -0
- package/dist/plugins/installer/npmTar.d.ts +13 -0
- package/dist/plugins/installer/npmTar.js +321 -0
- package/dist/plugins/installer/parseSource.d.ts +11 -0
- package/dist/plugins/installer/parseSource.js +48 -0
- package/dist/plugins/installer/preview.d.ts +101 -0
- package/dist/plugins/installer/preview.js +336 -0
- package/dist/plugins/installer/projectPluginSource.d.ts +14 -0
- package/dist/plugins/installer/projectPluginSource.js +140 -0
- package/dist/plugins/installer/types.d.ts +775 -0
- package/dist/plugins/installer/types.js +100 -0
- package/dist/plugins/installer/unzip.d.ts +11 -0
- package/dist/plugins/installer/unzip.js +123 -9
- package/dist/plugins/installer/update.d.ts +2 -2
- package/dist/plugins/installer/update.js +43 -4
- package/dist/plugins/loadPluginHooks.d.ts +19 -2
- package/dist/plugins/loadPluginHooks.js +97 -86
- package/dist/plugins/pluginAutomationTemplates.d.ts +18 -0
- package/dist/plugins/pluginAutomationTemplates.js +50 -0
- package/dist/plugins/pluginCatalog.d.ts +45 -0
- package/dist/plugins/pluginCatalog.js +87 -0
- package/dist/plugins/pluginCommandHook.d.ts +10 -4
- package/dist/plugins/pluginCommandHook.js +133 -18
- package/dist/plugins/pluginCommandsLoader.d.ts +19 -0
- package/dist/plugins/pluginCommandsLoader.js +181 -7
- package/dist/plugins/pluginContent.d.ts +10 -1
- package/dist/plugins/pluginContent.js +87 -30
- package/dist/plugins/pluginHookApproval.d.ts +30 -0
- package/dist/plugins/pluginHookApproval.js +160 -0
- package/dist/plugins/pluginHookIntegrity.d.ts +69 -0
- package/dist/plugins/pluginHookIntegrity.js +361 -0
- package/dist/plugins/pluginInstaller.js +71 -12
- package/dist/plugins/pluginMcpApproval.d.ts +19 -0
- package/dist/plugins/pluginMcpApproval.js +110 -0
- package/dist/plugins/pluginMcpIntegrity.d.ts +21 -0
- package/dist/plugins/pluginMcpIntegrity.js +74 -0
- package/dist/plugins/types.d.ts +30 -0
- package/dist/plugins/varRewrite.d.ts +9 -5
- package/dist/plugins/varRewrite.js +26 -17
- package/dist/preset/index.d.ts +11 -8
- package/dist/preset/index.js +62 -203
- package/dist/product/define.js +9 -3
- package/dist/product/types.d.ts +0 -2
- package/dist/profile/activation.d.ts +36 -0
- package/dist/profile/activation.js +57 -0
- package/dist/profile/catalog-store.d.ts +77 -0
- package/dist/profile/catalog-store.js +346 -0
- package/dist/profile/catalog.d.ts +54 -0
- package/dist/profile/catalog.js +152 -0
- package/dist/profile/index.d.ts +7 -0
- package/dist/profile/index.js +7 -0
- package/dist/profile/requirements.d.ts +76 -0
- package/dist/profile/requirements.js +117 -0
- package/dist/profile/resolve.d.ts +11 -0
- package/dist/profile/resolve.js +41 -0
- package/dist/profile/store.d.ts +17 -0
- package/dist/profile/store.js +167 -0
- package/dist/profile/types.d.ts +321 -0
- package/dist/profile/types.js +104 -0
- package/dist/prompt/composer.d.ts +46 -7
- package/dist/prompt/composer.js +78 -48
- package/dist/prompt/instruction-scanner.d.ts +6 -5
- package/dist/prompt/instruction-scanner.js +8 -23
- package/dist/prompt/section-loader.d.ts +2 -2
- package/dist/prompt/section-loader.js +7 -4
- package/dist/prompt/sections/browser.md +10 -9
- package/dist/prompt/sections/harness-base.md +9 -0
- package/dist/protocol/chat-session-manager.d.ts +77 -3
- package/dist/protocol/chat-session-manager.js +194 -18
- package/dist/protocol/chat-session.d.ts +72 -9
- package/dist/protocol/chat-session.js +151 -16
- package/dist/protocol/client.d.ts +37 -4
- package/dist/protocol/client.js +118 -6
- package/dist/protocol/index.d.ts +2 -2
- package/dist/protocol/index.js +2 -2
- package/dist/protocol/mobile-remote-types.d.ts +406 -0
- package/dist/protocol/mobile-remote-types.js +1 -0
- package/dist/protocol/server.d.ts +152 -14
- package/dist/protocol/server.js +1574 -229
- package/dist/protocol/types.d.ts +182 -32
- package/dist/protocol/types.js +18 -0
- package/dist/run/ArtifactTracker.d.ts +6 -1
- package/dist/run/ArtifactTracker.js +29 -21
- package/dist/run/EngineRunner.d.ts +2 -0
- package/dist/run/EngineRunner.js +1 -0
- package/dist/run/RunManager.d.ts +26 -0
- package/dist/run/RunManager.js +82 -11
- package/dist/run/factory.d.ts +3 -0
- package/dist/run/factory.js +2 -0
- package/dist/runtime/safe-spawn.d.ts +6 -0
- package/dist/runtime/safe-spawn.js +8 -13
- package/dist/services/auto-dream.d.ts +13 -1
- package/dist/services/auto-dream.js +56 -8
- package/dist/services/dream-consolidation.d.ts +3 -0
- package/dist/services/dream-consolidation.js +14 -2
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/memory-orchestrator.js +6 -2
- package/dist/services/oauth.d.ts +34 -10
- package/dist/services/oauth.js +236 -98
- package/dist/services/session-memory.d.ts +4 -4
- package/dist/services/session-memory.js +13 -10
- package/dist/session/memory.d.ts +26 -7
- package/dist/session/memory.js +79 -21
- package/dist/session/session-manager.d.ts +208 -25
- package/dist/session/session-manager.js +1232 -116
- package/dist/session/session-message.d.ts +22 -0
- package/dist/session/session-message.js +1 -0
- package/dist/session/transcript.d.ts +76 -6
- package/dist/session/transcript.js +278 -7
- package/dist/settings/feature-flags.d.ts +40 -0
- package/dist/settings/feature-flags.js +40 -0
- package/dist/settings/manager.d.ts +39 -1
- package/dist/settings/manager.js +102 -41
- package/dist/settings/migrate-config.js +4 -2
- package/dist/settings/schema.d.ts +511 -177
- package/dist/settings/schema.js +98 -25
- package/dist/skills/scanner.d.ts +6 -2
- package/dist/skills/scanner.js +160 -9
- package/dist/sources/adapter.d.ts +14 -0
- package/dist/sources/adapter.js +7 -0
- package/dist/sources/adapters/local-files.d.ts +10 -0
- package/dist/sources/adapters/local-files.js +127 -0
- package/dist/sources/adapters/mcp-resource.d.ts +17 -0
- package/dist/sources/adapters/mcp-resource.js +41 -0
- package/dist/sources/adapters/mock.d.ts +3 -0
- package/dist/sources/adapters/mock.js +29 -0
- package/dist/sources/binding.d.ts +6 -0
- package/dist/sources/binding.js +24 -0
- package/dist/sources/catalog.d.ts +6 -0
- package/dist/sources/catalog.js +64 -0
- package/dist/sources/context-summary.d.ts +8 -0
- package/dist/sources/context-summary.js +13 -0
- package/dist/sources/credential-status.d.ts +2 -0
- package/dist/sources/credential-status.js +13 -0
- package/dist/sources/index.d.ts +10 -0
- package/dist/sources/index.js +10 -0
- package/dist/sources/resolve.d.ts +28 -0
- package/dist/sources/resolve.js +47 -0
- package/dist/sources/truncate-utf8.d.ts +7 -0
- package/dist/sources/truncate-utf8.js +18 -0
- package/dist/sources/types.d.ts +71 -0
- package/dist/sources/types.js +26 -0
- package/dist/testing/fetch-stub.d.ts +32 -0
- package/dist/testing/fetch-stub.js +24 -0
- package/dist/themes/image.d.ts +18 -0
- package/dist/themes/image.js +117 -0
- package/dist/themes/index.d.ts +4 -0
- package/dist/themes/index.js +4 -0
- package/dist/themes/installer.d.ts +50 -0
- package/dist/themes/installer.js +290 -0
- package/dist/themes/manifest.d.ts +102 -0
- package/dist/themes/manifest.js +86 -0
- package/dist/themes/paths.d.ts +14 -0
- package/dist/themes/paths.js +33 -0
- package/dist/tool-system/browser-bridge.d.ts +53 -11
- package/dist/tool-system/browser-bridge.js +8 -9
- package/dist/tool-system/builtin/agent-heartbeat.d.ts +2 -2
- package/dist/tool-system/builtin/agent-heartbeat.js +20 -6
- package/dist/tool-system/builtin/agent-notifications.d.ts +127 -72
- package/dist/tool-system/builtin/agent-notifications.js +291 -101
- package/dist/tool-system/builtin/agent-output-file.js +1 -3
- package/dist/tool-system/builtin/agent-progress.d.ts +6 -0
- package/dist/tool-system/builtin/agent-progress.js +83 -0
- package/dist/tool-system/builtin/agent-registry.d.ts +38 -2
- package/dist/tool-system/builtin/agent-registry.js +178 -9
- package/dist/tool-system/builtin/agent-transcript-translator.js +1 -1
- package/dist/tool-system/builtin/agent.d.ts +2 -0
- package/dist/tool-system/builtin/agent.js +279 -34
- package/dist/tool-system/builtin/background-jobs.d.ts +63 -7
- package/dist/tool-system/builtin/background-jobs.js +111 -15
- package/dist/tool-system/builtin/background-work.d.ts +12 -1
- package/dist/tool-system/builtin/background-work.js +12 -1
- package/dist/tool-system/builtin/bash.d.ts +3 -5
- package/dist/tool-system/builtin/bash.js +14 -6
- package/dist/tool-system/builtin/browser-tools.d.ts +8 -5
- package/dist/tool-system/builtin/browser-tools.js +89 -23
- package/dist/tool-system/builtin/edit-model-catalog.d.ts +6 -0
- package/dist/tool-system/builtin/edit-model-catalog.js +172 -12
- package/dist/tool-system/builtin/edit.d.ts +2 -1
- package/dist/tool-system/builtin/edit.js +14 -10
- package/dist/tool-system/builtin/file-cache.d.ts +2 -0
- package/dist/tool-system/builtin/file-cache.js +4 -0
- package/dist/tool-system/builtin/generate-image.js +17 -10
- package/dist/tool-system/builtin/generate-video.d.ts +4 -0
- package/dist/tool-system/builtin/generate-video.js +164 -28
- package/dist/tool-system/builtin/glob.d.ts +2 -1
- package/dist/tool-system/builtin/glob.js +28 -3
- package/dist/tool-system/builtin/grep.d.ts +1 -0
- package/dist/tool-system/builtin/grep.js +82 -17
- package/dist/tool-system/builtin/image-uploader.js +7 -1
- package/dist/tool-system/builtin/index.d.ts +57 -18
- package/dist/tool-system/builtin/index.js +280 -167
- package/dist/tool-system/builtin/memory.d.ts +6 -0
- package/dist/tool-system/builtin/memory.js +36 -15
- package/dist/tool-system/builtin/panel.d.ts +9 -0
- package/dist/tool-system/builtin/panel.js +314 -0
- package/dist/tool-system/builtin/read.js +5 -1
- package/dist/tool-system/builtin/send-message-to-session.d.ts +6 -0
- package/dist/tool-system/builtin/send-message-to-session.js +67 -0
- package/dist/tool-system/builtin/sources.d.ts +8 -0
- package/dist/tool-system/builtin/sources.js +137 -0
- package/dist/tool-system/builtin/tool-search.js +7 -2
- package/dist/tool-system/builtin/update-automation-memory.js +2 -0
- package/dist/tool-system/builtin/video-providers.d.ts +12 -15
- package/dist/tool-system/builtin/video-providers.js +1 -0
- package/dist/tool-system/builtin/view-image.d.ts +2 -2
- package/dist/tool-system/builtin/web-fetch.js +60 -15
- package/dist/tool-system/builtin/web-search.js +1 -3
- package/dist/tool-system/builtin/write.d.ts +2 -1
- package/dist/tool-system/builtin/write.js +14 -4
- package/dist/tool-system/capability-module.d.ts +93 -0
- package/dist/tool-system/capability-module.js +53 -0
- package/dist/tool-system/context.d.ts +94 -24
- package/dist/tool-system/executor.js +99 -58
- package/dist/tool-system/external-tool-exposure.d.ts +60 -0
- package/dist/tool-system/external-tool-exposure.js +304 -0
- package/dist/tool-system/mcp-manager.d.ts +17 -1
- package/dist/tool-system/mcp-manager.js +72 -7
- package/dist/tool-system/mcp-tool-policy.d.ts +9 -0
- package/dist/tool-system/mcp-tool-policy.js +34 -0
- package/dist/tool-system/panel-bridge.d.ts +53 -0
- package/dist/tool-system/panel-bridge.js +1 -0
- package/dist/tool-system/path-policy.d.ts +19 -0
- package/dist/tool-system/path-policy.js +62 -1
- package/dist/tool-system/permission.d.ts +69 -3
- package/dist/tool-system/permission.js +429 -30
- package/dist/tool-system/registry.d.ts +15 -3
- package/dist/tool-system/registry.js +82 -40
- package/dist/tool-system/sandbox/index.js +3 -8
- package/dist/tool-system/sandbox/seatbelt.js +2 -6
- package/dist/tool-system/session-tool-host.d.ts +139 -0
- package/dist/tool-system/session-tool-host.js +167 -0
- package/dist/tool-system/testing/tool-registry-harness.d.ts +31 -0
- package/dist/tool-system/testing/tool-registry-harness.js +51 -0
- package/dist/tool-system/validation.d.ts +14 -7
- package/dist/tool-system/validation.js +701 -7
- package/dist/types.d.ts +201 -20
- package/dist/{cc-orchestrator → utils}/cwd-normalize.d.ts +1 -0
- package/dist/{cc-orchestrator → utils}/cwd-normalize.js +1 -0
- package/dist/utils/file-mutex.d.ts +65 -0
- package/dist/utils/file-mutex.js +145 -0
- package/dist/utils/json.d.ts +2 -4
- package/dist/utils/json.js +2 -4
- package/dist/utils/lockfile.d.ts +30 -1
- package/dist/utils/lockfile.js +2 -2
- package/dist/utils/secret-scrubber.d.ts +8 -0
- package/dist/utils/secret-scrubber.js +227 -0
- package/dist/utils/toolDisplay.js +1 -1
- package/package.json +20 -5
- package/THIRD_PARTY_NOTICES.md +0 -206
- package/dist/arena/arena.d.ts +0 -43
- package/dist/arena/arena.js +0 -333
- package/dist/arena/context/context-tools.d.ts +0 -16
- package/dist/arena/context/context-tools.js +0 -272
- package/dist/arena/context/within-root.d.ts +0 -7
- package/dist/arena/context/within-root.js +0 -15
- package/dist/arena/detect-mode.d.ts +0 -20
- package/dist/arena/detect-mode.js +0 -78
- package/dist/arena/digest-builder.d.ts +0 -25
- package/dist/arena/digest-builder.js +0 -120
- package/dist/arena/index.d.ts +0 -29
- package/dist/arena/index.js +0 -29
- package/dist/arena/iterate/convergence.d.ts +0 -25
- package/dist/arena/iterate/convergence.js +0 -103
- package/dist/arena/iterate/formats/index.d.ts +0 -22
- package/dist/arena/iterate/formats/index.js +0 -283
- package/dist/arena/iterate/index.d.ts +0 -11
- package/dist/arena/iterate/index.js +0 -9
- package/dist/arena/iterate/iterative-arena.d.ts +0 -23
- package/dist/arena/iterate/iterative-arena.js +0 -237
- package/dist/arena/iterate/parse.d.ts +0 -42
- package/dist/arena/iterate/parse.js +0 -123
- package/dist/arena/iterate/phases/argue.d.ts +0 -22
- package/dist/arena/iterate/phases/argue.js +0 -165
- package/dist/arena/iterate/phases/revise.d.ts +0 -16
- package/dist/arena/iterate/phases/revise.js +0 -62
- package/dist/arena/iterate/phases/tournament.d.ts +0 -34
- package/dist/arena/iterate/phases/tournament.js +0 -113
- package/dist/arena/iterate/tools/web-tools.d.ts +0 -13
- package/dist/arena/iterate/tools/web-tools.js +0 -54
- package/dist/arena/iterate/types.d.ts +0 -152
- package/dist/arena/iterate/types.js +0 -8
- package/dist/arena/ledger.d.ts +0 -47
- package/dist/arena/ledger.js +0 -159
- package/dist/arena/lenses/architecture.d.ts +0 -5
- package/dist/arena/lenses/architecture.js +0 -22
- package/dist/arena/lenses/engineering.d.ts +0 -5
- package/dist/arena/lenses/engineering.js +0 -22
- package/dist/arena/lenses/general.d.ts +0 -5
- package/dist/arena/lenses/general.js +0 -20
- package/dist/arena/lenses/index.d.ts +0 -16
- package/dist/arena/lenses/index.js +0 -47
- package/dist/arena/lenses/product.d.ts +0 -5
- package/dist/arena/lenses/product.js +0 -22
- package/dist/arena/model-presets.d.ts +0 -23
- package/dist/arena/model-presets.js +0 -44
- package/dist/arena/phases/adjudication.d.ts +0 -24
- package/dist/arena/phases/adjudication.js +0 -141
- package/dist/arena/phases/build-consensus.d.ts +0 -29
- package/dist/arena/phases/build-consensus.js +0 -83
- package/dist/arena/phases/claim-registry.d.ts +0 -26
- package/dist/arena/phases/claim-registry.js +0 -60
- package/dist/arena/phases/cross-review.d.ts +0 -45
- package/dist/arena/phases/cross-review.js +0 -218
- package/dist/arena/phases/debate-rounds.d.ts +0 -27
- package/dist/arena/phases/debate-rounds.js +0 -159
- package/dist/arena/phases/participant-research.d.ts +0 -38
- package/dist/arena/phases/participant-research.js +0 -314
- package/dist/arena/phases/planning-detail-expansion.d.ts +0 -38
- package/dist/arena/phases/planning-detail-expansion.js +0 -119
- package/dist/arena/planner.d.ts +0 -27
- package/dist/arena/planner.js +0 -311
- package/dist/arena/providers/docs.d.ts +0 -7
- package/dist/arena/providers/docs.js +0 -111
- package/dist/arena/providers/git.d.ts +0 -8
- package/dist/arena/providers/git.js +0 -174
- package/dist/arena/providers/index.d.ts +0 -32
- package/dist/arena/providers/index.js +0 -132
- package/dist/arena/providers/none.d.ts +0 -7
- package/dist/arena/providers/none.js +0 -11
- package/dist/arena/providers/repo.d.ts +0 -7
- package/dist/arena/providers/repo.js +0 -258
- package/dist/arena/render/session.d.ts +0 -17
- package/dist/arena/render/session.js +0 -190
- package/dist/arena/render/terminal.d.ts +0 -34
- package/dist/arena/render/terminal.js +0 -286
- package/dist/arena/strategies/discussion.d.ts +0 -25
- package/dist/arena/strategies/discussion.js +0 -143
- package/dist/arena/strategies/index.d.ts +0 -15
- package/dist/arena/strategies/index.js +0 -28
- package/dist/arena/strategies/language-wrapper.d.ts +0 -17
- package/dist/arena/strategies/language-wrapper.js +0 -102
- package/dist/arena/strategies/lens-wrapper.d.ts +0 -16
- package/dist/arena/strategies/lens-wrapper.js +0 -236
- package/dist/arena/strategies/planning.d.ts +0 -30
- package/dist/arena/strategies/planning.js +0 -225
- package/dist/arena/strategies/review.d.ts +0 -26
- package/dist/arena/strategies/review.js +0 -168
- package/dist/arena/strategies/utils.d.ts +0 -36
- package/dist/arena/strategies/utils.js +0 -603
- package/dist/arena/tools/selector.d.ts +0 -17
- package/dist/arena/tools/selector.js +0 -61
- package/dist/arena/transitions.d.ts +0 -53
- package/dist/arena/transitions.js +0 -97
- package/dist/arena/types.d.ts +0 -514
- package/dist/arena/types.js +0 -27
- package/dist/cc-orchestrator/agent-adapter.d.ts +0 -54
- package/dist/cc-orchestrator/agent-adapter.js +0 -143
- package/dist/cc-orchestrator/cc-capability.d.ts +0 -19
- package/dist/cc-orchestrator/cc-capability.js +0 -53
- package/dist/cc-orchestrator/codex-session-discovery.d.ts +0 -24
- package/dist/cc-orchestrator/codex-session-discovery.js +0 -191
- package/dist/cc-orchestrator/codex-session-history.d.ts +0 -38
- package/dist/cc-orchestrator/codex-session-history.js +0 -247
- package/dist/cc-orchestrator/external-agent-bindings.d.ts +0 -27
- package/dist/cc-orchestrator/external-agent-bindings.js +0 -150
- package/dist/cc-orchestrator/external-agent-changes.d.ts +0 -19
- package/dist/cc-orchestrator/external-agent-changes.js +0 -231
- package/dist/cc-orchestrator/external-agent-driver.d.ts +0 -19
- package/dist/cc-orchestrator/external-agent-driver.js +0 -284
- package/dist/cc-orchestrator/external-agent-session-store.d.ts +0 -23
- package/dist/cc-orchestrator/external-agent-session-store.js +0 -146
- package/dist/cc-orchestrator/index.d.ts +0 -8
- package/dist/cc-orchestrator/index.js +0 -8
- package/dist/cc-orchestrator/relevance-judge.d.ts +0 -15
- package/dist/cc-orchestrator/relevance-judge.js +0 -29
- package/dist/cc-orchestrator/session-discovery.d.ts +0 -46
- package/dist/cc-orchestrator/session-discovery.js +0 -125
- package/dist/cc-orchestrator/session-history.d.ts +0 -54
- package/dist/cc-orchestrator/session-history.js +0 -150
- package/dist/cron/cron-runtime.d.ts +0 -2
- package/dist/cron/cron-runtime.js +0 -2
- package/dist/cron/cron-store.d.ts +0 -2
- package/dist/cron/cron-store.js +0 -2
- package/dist/cron/scheduler.d.ts +0 -7
- package/dist/cron/scheduler.js +0 -7
- package/dist/engine/goal.js +0 -206
- package/dist/external-agents/config.d.ts +0 -2
- package/dist/external-agents/config.js +0 -15
- package/dist/external-agents/types.d.ts +0 -31
- package/dist/git/parse-log.d.ts +0 -13
- package/dist/git/parse-log.js +0 -21
- package/dist/git/utils.d.ts +0 -49
- package/dist/git/utils.js +0 -161
- package/dist/git/worktree/crud.d.ts +0 -69
- package/dist/git/worktree/crud.js +0 -206
- package/dist/git/worktree/diff.d.ts +0 -14
- package/dist/git/worktree/diff.js +0 -82
- package/dist/git/worktree/git-exec.d.ts +0 -7
- package/dist/git/worktree/git-exec.js +0 -51
- package/dist/git/worktree/index.d.ts +0 -5
- package/dist/git/worktree/index.js +0 -5
- package/dist/git/worktree/query.d.ts +0 -42
- package/dist/git/worktree/query.js +0 -121
- package/dist/git/worktree/slug.d.ts +0 -11
- package/dist/git/worktree/slug.js +0 -58
- package/dist/git/worktree.d.ts +0 -1
- package/dist/git/worktree.js +0 -5
- package/dist/lsp/client.d.ts +0 -41
- package/dist/lsp/client.js +0 -186
- package/dist/lsp/manager.d.ts +0 -40
- package/dist/lsp/manager.js +0 -155
- package/dist/lsp/root-path.d.ts +0 -9
- package/dist/lsp/root-path.js +0 -12
- package/dist/lsp/servers.d.ts +0 -16
- package/dist/lsp/servers.js +0 -60
- package/dist/prompt/sections/coding.md +0 -35
- package/dist/quota/credentials.d.ts +0 -3
- package/dist/quota/credentials.js +0 -80
- package/dist/quota/index.d.ts +0 -36
- package/dist/quota/index.js +0 -155
- package/dist/quota/types.d.ts +0 -48
- package/dist/quota/types.js +0 -13
- package/dist/review/review-prompt.d.ts +0 -28
- package/dist/review/review-prompt.js +0 -81
- package/dist/state.d.ts +0 -160
- package/dist/state.js +0 -267
- package/dist/tool-system/builtin/apply-patch/applier.d.ts +0 -26
- package/dist/tool-system/builtin/apply-patch/applier.js +0 -308
- package/dist/tool-system/builtin/apply-patch/backup-targets.d.ts +0 -10
- package/dist/tool-system/builtin/apply-patch/backup-targets.js +0 -30
- package/dist/tool-system/builtin/apply-patch/index.d.ts +0 -20
- package/dist/tool-system/builtin/apply-patch/index.js +0 -105
- package/dist/tool-system/builtin/apply-patch/parser.d.ts +0 -17
- package/dist/tool-system/builtin/apply-patch/parser.js +0 -212
- package/dist/tool-system/builtin/apply-patch/seek-sequence.d.ts +0 -18
- package/dist/tool-system/builtin/apply-patch/seek-sequence.js +0 -123
- package/dist/tool-system/builtin/apply-patch/types.d.ts +0 -49
- package/dist/tool-system/builtin/apply-patch/types.js +0 -13
- package/dist/tool-system/builtin/arena.d.ts +0 -31
- package/dist/tool-system/builtin/arena.js +0 -415
- package/dist/tool-system/builtin/brief.d.ts +0 -6
- package/dist/tool-system/builtin/brief.js +0 -41
- package/dist/tool-system/builtin/check-quota.d.ts +0 -15
- package/dist/tool-system/builtin/check-quota.js +0 -34
- package/dist/tool-system/builtin/drive-claude-code.d.ts +0 -51
- package/dist/tool-system/builtin/drive-claude-code.js +0 -698
- package/dist/tool-system/builtin/lsp.d.ts +0 -7
- package/dist/tool-system/builtin/lsp.js +0 -144
- package/dist/tool-system/builtin/notebook-edit.d.ts +0 -7
- package/dist/tool-system/builtin/notebook-edit.js +0 -127
- package/dist/tool-system/builtin/worktree.d.ts +0 -11
- package/dist/tool-system/builtin/worktree.js +0 -345
- /package/dist/{external-agents → links}/types.js +0 -0
- /package/dist/{engine/session-usage.d.ts → session/usage.d.ts} +0 -0
- /package/dist/{engine/session-usage.js → session/usage.js} +0 -0
package/dist/engine/turn-loop.js
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
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 {
|
|
7
|
+
import { randomUUID } from "node:crypto";
|
|
8
|
+
import { buildAgentDirectionMessage } from "../tool-system/builtin/agent-notifications.js";
|
|
9
|
+
import { newTurnId } from "./turn-state.js";
|
|
8
10
|
import { formatFriendlyError } from "./friendly-error.js";
|
|
9
|
-
import {
|
|
11
|
+
import { buildGoalJudgeRuntimeContext } from "./goal-judge-context.js";
|
|
10
12
|
import { wrapHookMessages } from "../hooks/inject.js";
|
|
11
13
|
import { ContextLimitError } from "../exceptions.js";
|
|
12
14
|
import { logger } from "../logging/logger.js";
|
|
@@ -18,9 +20,9 @@ import { isAbortError } from "../llm/client-base.js";
|
|
|
18
20
|
import { crossedReactiveThreshold } from "./reactive-threshold.js";
|
|
19
21
|
import { COMPLETE_GOAL_TOOL_NAME } from "../tool-system/builtin/complete-goal.js";
|
|
20
22
|
import { CANCEL_GOAL_TOOL_NAME } from "../tool-system/builtin/cancel-goal.js";
|
|
21
|
-
import { redactSensitiveToolResultsInMessages, toolResultForDisplay, toolResultTranscriptText, toolResultsForDisplay, } from "../tool-system/tool-result-redaction.js";
|
|
22
|
-
import { addTokenUsage, cacheHitRateFromUsage, cumulativeCacheHitRate, } from "
|
|
23
|
-
import { createGoalBudgetTracker, recordGoalUsage, goalBudgetTerminationReason, applyGoalExtension, limitProximity, GOAL_DEFAULT_MAX_STOP_BLOCKS, } from "
|
|
23
|
+
import { redactSensitiveToolResultsInMessages, SENSITIVE_TOOL_RESULT_PLACEHOLDER, toolResultForDisplay, toolResultTranscriptText, toolResultsForDisplay, } from "../tool-system/tool-result-redaction.js";
|
|
24
|
+
import { addTokenUsage, cacheHitRateFromUsage, cumulativeCacheHitRate, } from "../session/usage.js";
|
|
25
|
+
import { createGoalBudgetTracker, recordGoalUsage, goalBudgetTerminationReason, applyGoalExtension, limitProximity, isSameGoalVersion, normalizeGoal, GOAL_DEFAULT_MAX_STOP_BLOCKS, } from "../goal/lifecycle.js";
|
|
24
26
|
/**
|
|
25
27
|
* 把一个 ToolResult 映射成发给 LLM 的 tool_result ContentBlock。
|
|
26
28
|
* 有 contentBlocks(view_image 的图片块)就原样用作 content;否则
|
|
@@ -64,6 +66,10 @@ export class TurnLoop {
|
|
|
64
66
|
sensitiveToolResultRedactions = new Map();
|
|
65
67
|
pendingImageMessages = new Set();
|
|
66
68
|
volatileContextMessages = new Set();
|
|
69
|
+
/** Last outer-loop turn for which the model returned a complete response. */
|
|
70
|
+
lastCompletedModelTurn = 0;
|
|
71
|
+
/** Last model turn whose transcript/persistence boundary was finalized. */
|
|
72
|
+
lastFinalizedTurn = 0;
|
|
67
73
|
/**
|
|
68
74
|
* Consecutive on_stop blocks (Goal mode kept the agent going). Reset to 0
|
|
69
75
|
* on any unblocked completion. When it reaches config.maxStopBlocks the
|
|
@@ -83,6 +89,57 @@ export class TurnLoop {
|
|
|
83
89
|
* being re-emitted every turn while still within the approach threshold.
|
|
84
90
|
*/
|
|
85
91
|
approachAnnounced = false;
|
|
92
|
+
/** Latest coalesced Goal edit/resume reminder, consumed at a safe point. */
|
|
93
|
+
pendingGoalControlMessage = null;
|
|
94
|
+
/** Pause/delete requested while a Goal run is in flight. The current
|
|
95
|
+
* model/tool step is allowed to settle, then the loop stops before starting
|
|
96
|
+
* another model request. */
|
|
97
|
+
goalControlStopRequested = false;
|
|
98
|
+
/** Auxiliary tool-summary calls started by this loop and revoked by Goal pause/delete. */
|
|
99
|
+
activeToolSummaryControllers = new Set();
|
|
100
|
+
/**
|
|
101
|
+
* Apply a user goal edit/pause/resume to the live loop. Passing undefined
|
|
102
|
+
* pauses goal-driving at the next step boundary; a goal value resumes it.
|
|
103
|
+
* Usage accumulated before an edit is retained, while a pause/resume starts
|
|
104
|
+
* a fresh run-scoped budget window (matching a later persisted resume).
|
|
105
|
+
*/
|
|
106
|
+
updateGoal(goal, controlMessage, limits) {
|
|
107
|
+
const normalized = normalizeGoal(goal);
|
|
108
|
+
if (!normalized) {
|
|
109
|
+
this.config = { ...this.config, goal: undefined };
|
|
110
|
+
this.goalTracker = null;
|
|
111
|
+
this.pendingGoalControlMessage = null;
|
|
112
|
+
this.goalControlStopRequested = true;
|
|
113
|
+
for (const controller of this.activeToolSummaryControllers)
|
|
114
|
+
controller.abort();
|
|
115
|
+
this.activeToolSummaryControllers.clear();
|
|
116
|
+
this.stopBlockCount = 0;
|
|
117
|
+
this.approachAnnounced = false;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const next = { ...normalized, goalId: normalized.goalId ?? randomUUID() };
|
|
121
|
+
const wasPaused = !this.config.goal;
|
|
122
|
+
this.config = {
|
|
123
|
+
...this.config,
|
|
124
|
+
goal: next,
|
|
125
|
+
...(typeof limits?.maxTurns === "number" && limits.maxTurns > 0
|
|
126
|
+
? { maxTurns: Math.max(this.config.maxTurns, Math.floor(limits.maxTurns)) }
|
|
127
|
+
: {}),
|
|
128
|
+
...(typeof limits?.maxStopBlocks === "number" && limits.maxStopBlocks > 0
|
|
129
|
+
? { maxStopBlocks: Math.floor(limits.maxStopBlocks) }
|
|
130
|
+
: {}),
|
|
131
|
+
};
|
|
132
|
+
this.goalControlStopRequested = false;
|
|
133
|
+
this.pendingGoalControlMessage = controlMessage?.trim()
|
|
134
|
+
? { goalId: next.goalId, revision: next.revision, text: controlMessage.trim() }
|
|
135
|
+
: null;
|
|
136
|
+
if (this.goalTracker && !wasPaused)
|
|
137
|
+
this.goalTracker.goal = next;
|
|
138
|
+
else
|
|
139
|
+
this.goalTracker = createGoalBudgetTracker(next, Date.now());
|
|
140
|
+
this.stopBlockCount = 0;
|
|
141
|
+
this.approachAnnounced = false;
|
|
142
|
+
}
|
|
86
143
|
/**
|
|
87
144
|
* Extend the in-flight run's limits (TODO 3.1 — 运行中续轮/加预算). Mutates
|
|
88
145
|
* the maxTurns ceiling, the maxStopBlocks cap, and/or the live goal budgets;
|
|
@@ -128,6 +185,20 @@ export class TurnLoop {
|
|
|
128
185
|
}
|
|
129
186
|
return { ...next, maxStopBlocks: nextCap };
|
|
130
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Private Goal-judge accounting seam. Judge calls bypass ModelFacade, so the
|
|
190
|
+
* Engine forwards their provider usage here while the run-scoped tracker is
|
|
191
|
+
* live. The returned reason lets the hook suppress verdict side effects when
|
|
192
|
+
* this very request crossed a hard token/time budget.
|
|
193
|
+
*/
|
|
194
|
+
recordGoalJudgeUsage(usage) {
|
|
195
|
+
if (!this.goalTracker)
|
|
196
|
+
return undefined;
|
|
197
|
+
if (usage) {
|
|
198
|
+
recordGoalUsage(this.goalTracker, (usage.promptTokens ?? 0) + (usage.completionTokens ?? 0));
|
|
199
|
+
}
|
|
200
|
+
return goalBudgetTerminationReason(this.goalTracker, Date.now());
|
|
201
|
+
}
|
|
131
202
|
/**
|
|
132
203
|
* Goal mode only: if the run is nearing EITHER stop ceiling (maxTurns or
|
|
133
204
|
* maxStopBlocks) and we haven't announced it yet, emit one approaching_limit
|
|
@@ -145,6 +216,8 @@ export class TurnLoop {
|
|
|
145
216
|
this.approachAnnounced = true;
|
|
146
217
|
this.config.onStream?.({
|
|
147
218
|
type: "goal_progress",
|
|
219
|
+
goalId: this.config.goal.goalId,
|
|
220
|
+
revision: this.config.goal.revision,
|
|
148
221
|
status: "approaching_limit",
|
|
149
222
|
round: this.stopBlockCount,
|
|
150
223
|
turnsRemaining: prox.turnsRemaining,
|
|
@@ -159,6 +232,16 @@ export class TurnLoop {
|
|
|
159
232
|
config) {
|
|
160
233
|
this.deps = deps;
|
|
161
234
|
this.config = config;
|
|
235
|
+
const normalizedGoal = normalizeGoal(this.config.goal);
|
|
236
|
+
if (normalizedGoal) {
|
|
237
|
+
this.config = {
|
|
238
|
+
...this.config,
|
|
239
|
+
goal: { ...normalizedGoal, goalId: normalizedGoal.goalId ?? randomUUID() },
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
else if (this.config.goal !== undefined) {
|
|
243
|
+
this.config = { ...this.config, goal: undefined };
|
|
244
|
+
}
|
|
162
245
|
for (const msg of this.config.freshImageMessages ?? []) {
|
|
163
246
|
this.pendingImageMessages.add(msg);
|
|
164
247
|
}
|
|
@@ -217,6 +300,21 @@ export class TurnLoop {
|
|
|
217
300
|
return;
|
|
218
301
|
this.deps.transcript.appendTurnStopped();
|
|
219
302
|
}
|
|
303
|
+
/**
|
|
304
|
+
* Close one completed model turn exactly once. The transcript boundary must
|
|
305
|
+
* be durable before the Engine flushes usage, heartbeat, and turnCount via
|
|
306
|
+
* onTurnBoundary, so crash recovery never folds two assistant responses into
|
|
307
|
+
* the same transcript turn or observes state ahead of the transcript.
|
|
308
|
+
*/
|
|
309
|
+
finalizeModelTurn() {
|
|
310
|
+
if (this.lastCompletedModelTurn !== this.turnCount ||
|
|
311
|
+
this.lastFinalizedTurn === this.turnCount) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
this.lastFinalizedTurn = this.turnCount;
|
|
315
|
+
this.deps.transcript.appendTurnBoundary();
|
|
316
|
+
this.config.onTurnBoundary?.(this.turnCount);
|
|
317
|
+
}
|
|
220
318
|
prepareMessagesForModel(messages) {
|
|
221
319
|
const preserveMessages = this.pendingImageMessages.size > 0 ? this.pendingImageMessages : undefined;
|
|
222
320
|
const result = downgradeImagePayloadsInHistory(messages, { preserveMessages });
|
|
@@ -326,10 +424,17 @@ export class TurnLoop {
|
|
|
326
424
|
promptTokensConfidence: overhead > 0 ? "medium" : "low",
|
|
327
425
|
});
|
|
328
426
|
}
|
|
329
|
-
recordResponseUsage(usage) {
|
|
427
|
+
recordResponseUsage(usage, requestKind = "primary", recordCacheDiagnostics = true) {
|
|
330
428
|
this.currentTurnUsage = addTokenUsage(this.currentTurnUsage, usage);
|
|
429
|
+
this.deps.onAgentUsage?.(usage);
|
|
331
430
|
this.currentCumulativeUsage = this.deps.recordCumulativeUsage?.(usage);
|
|
332
|
-
this.deps.recordCacheReadDiagnostics
|
|
431
|
+
if (recordCacheDiagnostics && this.deps.recordCacheReadDiagnostics) {
|
|
432
|
+
this.deps.recordCacheReadDiagnostics({
|
|
433
|
+
usage,
|
|
434
|
+
requestKind,
|
|
435
|
+
fingerprint: this.deps.model.getPromptPrefixFingerprint(this.deps.systemPrompt, this.deps.tools),
|
|
436
|
+
});
|
|
437
|
+
}
|
|
333
438
|
}
|
|
334
439
|
emitCtxFromUsage(usage, messages) {
|
|
335
440
|
if (!this.config.onStream)
|
|
@@ -396,24 +501,28 @@ export class TurnLoop {
|
|
|
396
501
|
*/
|
|
397
502
|
async run(initialMessages) {
|
|
398
503
|
const result = await this.runUnredacted(initialMessages);
|
|
399
|
-
return {
|
|
504
|
+
return {
|
|
505
|
+
...result,
|
|
506
|
+
...(result.reason === "completed" && this.goalControlStopRequested
|
|
507
|
+
? { completionKind: "goal_control_stop" }
|
|
508
|
+
: {}),
|
|
509
|
+
messages: this.redactConsumedSensitiveToolResults(result.messages),
|
|
510
|
+
};
|
|
400
511
|
}
|
|
401
512
|
async runUnredacted(initialMessages) {
|
|
402
513
|
let messages = [...initialMessages];
|
|
403
514
|
let finalText = "";
|
|
404
515
|
const budgetTracker = createBudgetTracker();
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
const goalToolResults = [];
|
|
516
|
+
const sensitiveGoalResultTools = this.config.goal
|
|
517
|
+
? new Set(this.deps.tools.filter((tool) => tool.sensitiveResult).map((tool) => tool.name))
|
|
518
|
+
: undefined;
|
|
409
519
|
// Goal-mode run-scoped budget tracker (P0). Null when no goal. Stamps a
|
|
410
520
|
// wall-clock start now and accumulates prompt+completion tokens across
|
|
411
521
|
// every turn; the guardrail below force-stops the run once any configured
|
|
412
522
|
// budget is blown — the unattended-safety backstop.
|
|
413
|
-
this.goalTracker
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
const goalTracker = this.goalTracker;
|
|
523
|
+
if (!this.goalTracker && this.config.goal) {
|
|
524
|
+
this.goalTracker = createGoalBudgetTracker(this.config.goal, Date.now());
|
|
525
|
+
}
|
|
417
526
|
// Fresh run: re-arm the approaching-limit announcement.
|
|
418
527
|
this.approachAnnounced = false;
|
|
419
528
|
// run() must never reject: the engine's post-run bookkeeping (saveState
|
|
@@ -425,6 +534,30 @@ export class TurnLoop {
|
|
|
425
534
|
// hook emits, guards) and surfaces them as a model_error result.
|
|
426
535
|
try {
|
|
427
536
|
while (this.turnCount < this.config.maxTurns) {
|
|
537
|
+
if (this.goalControlStopRequested) {
|
|
538
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
539
|
+
return { text: finalText, reason: "completed", messages };
|
|
540
|
+
}
|
|
541
|
+
const preTurnGoalTermination = this.goalTracker
|
|
542
|
+
? goalBudgetTerminationReason(this.goalTracker, Date.now())
|
|
543
|
+
: undefined;
|
|
544
|
+
if (preTurnGoalTermination) {
|
|
545
|
+
this.config.onStream?.({
|
|
546
|
+
type: "assistant_message",
|
|
547
|
+
message: {
|
|
548
|
+
role: "assistant",
|
|
549
|
+
content: "(Goal 预算已耗尽,强制停止。)",
|
|
550
|
+
},
|
|
551
|
+
});
|
|
552
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
553
|
+
return {
|
|
554
|
+
text: finalText,
|
|
555
|
+
reason: "goal_budget_exhausted",
|
|
556
|
+
messages,
|
|
557
|
+
goalTermination: preTurnGoalTermination,
|
|
558
|
+
goalTerminationRound: this.stopBlockCount,
|
|
559
|
+
};
|
|
560
|
+
}
|
|
428
561
|
this.turnCount++;
|
|
429
562
|
this.currentTurnUsage = {
|
|
430
563
|
promptTokens: 0,
|
|
@@ -452,8 +585,18 @@ export class TurnLoop {
|
|
|
452
585
|
// they join THIS step's request — no abort, no lost in-flight work. Same
|
|
453
586
|
// loop-top user-push pattern as turnStartInjection / turn-limit warnings
|
|
454
587
|
// below. Push to transcript too so they persist + survive resume.
|
|
588
|
+
// Bind self-reported Goal terminal tools to the revision that was
|
|
589
|
+
// actually visible when this model step began. An edit/pause/delete can
|
|
590
|
+
// arrive while context management, the model request, or its tool batch
|
|
591
|
+
// is in flight; a late complete_goal/cancel_goal from that old prompt
|
|
592
|
+
// must never terminate the newer revision.
|
|
593
|
+
const requestedGoalVersion = this.config.goal ? { ...this.config.goal } : undefined;
|
|
455
594
|
await this.consumeQueuedSteer(messages, "normal_step");
|
|
456
|
-
|
|
595
|
+
if (this.goalControlStopRequested) {
|
|
596
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
597
|
+
return { text: finalText, reason: "completed", messages };
|
|
598
|
+
}
|
|
599
|
+
this.deps.onAgentControlState?.("model");
|
|
457
600
|
// Per-turn correlation ID. Every log written through `tlog` (or any
|
|
458
601
|
// child derived from it) is stamped with `turn` + `turnId`, so
|
|
459
602
|
// `jq 'select(.turnId == "...")'` reconstructs one turn's timeline.
|
|
@@ -468,11 +611,6 @@ export class TurnLoop {
|
|
|
468
611
|
tlog.info("turn.start", { cat: "turn", messageCount: messages.length });
|
|
469
612
|
// Tag downstream tool-exec / permission lines with this turn's IDs.
|
|
470
613
|
this.deps.toolExecutor.setLogger(tlog);
|
|
471
|
-
this.config.onStream?.({
|
|
472
|
-
type: "stream_request_start",
|
|
473
|
-
turnNumber: this.turnCount,
|
|
474
|
-
messageId: assistantMessageId,
|
|
475
|
-
});
|
|
476
614
|
const turnStartHook = await this.emitHook("on_turn_start", {
|
|
477
615
|
turnNumber: this.turnCount,
|
|
478
616
|
});
|
|
@@ -480,6 +618,10 @@ export class TurnLoop {
|
|
|
480
618
|
if (turnStartInjection) {
|
|
481
619
|
messages.push(turnStartInjection);
|
|
482
620
|
}
|
|
621
|
+
if (this.goalControlStopRequested) {
|
|
622
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
623
|
+
return { text: finalText, reason: "completed", messages };
|
|
624
|
+
}
|
|
483
625
|
// Approaching max turns: inject a warning so the model can wrap up.
|
|
484
626
|
// (Model-facing only — the user-facing "再续" marker is handled by
|
|
485
627
|
// maybeAnnounceApproachingLimit below, which also watches the stop-block
|
|
@@ -523,7 +665,7 @@ export class TurnLoop {
|
|
|
523
665
|
}
|
|
524
666
|
else {
|
|
525
667
|
// Context management (async — may trigger LLM summarization)
|
|
526
|
-
messages = await this.deps.contextManager.manageAsync(messages);
|
|
668
|
+
messages = await this.deps.contextManager.manageAsync(messages, this.config.signal);
|
|
527
669
|
// manageAsync can itself issue an LLM summarization call lasting several
|
|
528
670
|
// seconds; if the signal aborted during it, stop here rather than
|
|
529
671
|
// proceeding into the (expensive) main model call. Belt to the loop-top
|
|
@@ -532,6 +674,10 @@ export class TurnLoop {
|
|
|
532
674
|
this.markStopped();
|
|
533
675
|
return { text: finalText, reason: "aborted_streaming", messages };
|
|
534
676
|
}
|
|
677
|
+
if (this.goalControlStopRequested) {
|
|
678
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
679
|
+
return { text: finalText, reason: "completed", messages };
|
|
680
|
+
}
|
|
535
681
|
}
|
|
536
682
|
// No pre-llm ctx emit here: the messages-only estimate would be ~16k
|
|
537
683
|
// smaller than the real prompt (system + tools not included), making
|
|
@@ -559,8 +705,17 @@ export class TurnLoop {
|
|
|
559
705
|
messages.push(compactInjection);
|
|
560
706
|
}
|
|
561
707
|
}
|
|
708
|
+
if (this.goalControlStopRequested) {
|
|
709
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
710
|
+
return { text: finalText, reason: "completed", messages };
|
|
711
|
+
}
|
|
562
712
|
messages = this.appendVolatileContextMessages(messages);
|
|
563
713
|
// Model call (with streaming fallback and max_output_tokens continuation)
|
|
714
|
+
this.config.onStream?.({
|
|
715
|
+
type: "stream_request_start",
|
|
716
|
+
turnNumber: this.turnCount,
|
|
717
|
+
messageId: assistantMessageId,
|
|
718
|
+
});
|
|
564
719
|
// Track tool IDs streamed during this turn to avoid duplicate UI events
|
|
565
720
|
this.streamedToolIds.clear();
|
|
566
721
|
// Tool queue is created before the call, but enqueue happens only after
|
|
@@ -572,10 +727,18 @@ export class TurnLoop {
|
|
|
572
727
|
}
|
|
573
728
|
catch (err) {
|
|
574
729
|
if (err instanceof ContextLimitError) {
|
|
730
|
+
if (this.goalControlStopRequested) {
|
|
731
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
732
|
+
return { text: finalText, reason: "completed", messages };
|
|
733
|
+
}
|
|
575
734
|
// Progressive recovery: drop oldest API rounds, up to 3 retries
|
|
576
735
|
const { dropOldestRounds } = await import("../context/compaction.js");
|
|
577
736
|
let recovered = false;
|
|
578
737
|
for (let retry = 1; retry <= 3; retry++) {
|
|
738
|
+
if (this.goalControlStopRequested) {
|
|
739
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
740
|
+
return { text: finalText, reason: "completed", messages };
|
|
741
|
+
}
|
|
579
742
|
tlog.warn("turn.ptl_recovery", { cat: "turn", retry, roundsToDrop: retry });
|
|
580
743
|
messages = dropOldestRounds(messages, retry);
|
|
581
744
|
try {
|
|
@@ -621,12 +784,22 @@ export class TurnLoop {
|
|
|
621
784
|
return { text: finalText, reason: "model_error", messages };
|
|
622
785
|
}
|
|
623
786
|
}
|
|
787
|
+
this.lastCompletedModelTurn = this.turnCount;
|
|
624
788
|
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
625
789
|
// Record the response once into current-turn and whole-session counters.
|
|
626
790
|
if (response.usage?.promptTokens !== undefined) {
|
|
627
791
|
this.recordResponseUsage(response.usage);
|
|
628
792
|
this.emitCtxFromUsage(response.usage, messages);
|
|
629
793
|
}
|
|
794
|
+
// Charge every provider response to the run-scoped Goal budget before
|
|
795
|
+
// any truncation path can retry or continue the outer turn loop.
|
|
796
|
+
if (this.goalTracker && response.usage) {
|
|
797
|
+
const used = (response.usage.promptTokens ?? 0) + (response.usage.completionTokens ?? 0);
|
|
798
|
+
recordGoalUsage(this.goalTracker, used);
|
|
799
|
+
}
|
|
800
|
+
let budgetTermination = this.goalTracker
|
|
801
|
+
? goalBudgetTerminationReason(this.goalTracker, Date.now())
|
|
802
|
+
: undefined;
|
|
630
803
|
// Feed actual token usage back to the context manager so subsequent
|
|
631
804
|
// compaction decisions use hybrid (actual + delta) estimation rather than
|
|
632
805
|
// pure heuristics. Without this the manager falls back to char/4 estimates.
|
|
@@ -637,13 +810,24 @@ export class TurnLoop {
|
|
|
637
810
|
this.deps.recordContextUsageAnchor?.(anchor);
|
|
638
811
|
}
|
|
639
812
|
messages = this.markPendingImagesConsumed(messages);
|
|
813
|
+
// stopBlockCount guards only the no-tool final-answer loop evaluated by
|
|
814
|
+
// on_stop (text -> block -> text -> block). A response that structurally
|
|
815
|
+
// contains tool_use has left that streak, regardless of whether the call
|
|
816
|
+
// will execute successfully. Reset here, before truncated-tool and later
|
|
817
|
+
// queued-steer paths can continue the outer loop. Repeated ineffective
|
|
818
|
+
// tool_use turns still consume turns and are bounded by maxTurns.
|
|
819
|
+
if (response.toolCalls.length > 0) {
|
|
820
|
+
this.stopBlockCount = 0;
|
|
821
|
+
}
|
|
640
822
|
// Truncation that cut off a TOOL CALL: the model overflowed
|
|
641
823
|
// max_output_tokens mid tool-call, so the arg JSON is incomplete (e.g. a
|
|
642
824
|
// Write whose `content` was clipped, leaving file_path unset). Executing
|
|
643
825
|
// it raised a misleading "Missing required parameter: file_path". Instead,
|
|
644
826
|
// tell the model its output was truncated and let the next turn retry —
|
|
645
827
|
// bounded by the outer maxTurns loop.
|
|
646
|
-
if (
|
|
828
|
+
if (!budgetTermination &&
|
|
829
|
+
isTruncatedStop(response.stopReason) &&
|
|
830
|
+
response.toolCalls.length > 0) {
|
|
647
831
|
tlog.info("turn.truncated_tool_call", {
|
|
648
832
|
cat: "turn",
|
|
649
833
|
toolCount: response.toolCalls.length,
|
|
@@ -655,13 +839,15 @@ export class TurnLoop {
|
|
|
655
839
|
role: "user",
|
|
656
840
|
content: "<system-reminder>Your previous response was truncated by the max output token limit before the tool call finished, so its arguments are incomplete. Do not assume it ran. Either retry with a smaller/more focused tool call (e.g. write the file in sections via Edit), or raise this model's maxOutputTokens.</system-reminder>",
|
|
657
841
|
});
|
|
842
|
+
this.finalizeModelTurn();
|
|
658
843
|
continue;
|
|
659
844
|
}
|
|
660
845
|
// Handle max_output_tokens: if response was truncated, do continuation
|
|
661
846
|
// (up to 3 times). Truncation is reported as finish_reason "length"
|
|
662
847
|
// (OpenAI) or stop_reason "max_tokens" (Anthropic) — isTruncatedStop
|
|
663
848
|
// accepts both, so the OpenAI streaming path triggers continuation too.
|
|
664
|
-
if (
|
|
849
|
+
if (!budgetTermination &&
|
|
850
|
+
isTruncatedStop(response.stopReason) &&
|
|
665
851
|
response.toolCalls.length === 0 &&
|
|
666
852
|
response.text) {
|
|
667
853
|
let combinedText = response.text;
|
|
@@ -670,7 +856,12 @@ export class TurnLoop {
|
|
|
670
856
|
// Don't fire another continuation call if the user cancelled — without
|
|
671
857
|
// this an abort during a truncated response could still issue up to 3
|
|
672
858
|
// more model calls, emitting text after Stop.
|
|
673
|
-
if (this.config.signal?.aborted)
|
|
859
|
+
if (this.config.signal?.aborted || this.goalControlStopRequested)
|
|
860
|
+
break;
|
|
861
|
+
budgetTermination = this.goalTracker
|
|
862
|
+
? goalBudgetTerminationReason(this.goalTracker, Date.now())
|
|
863
|
+
: undefined;
|
|
864
|
+
if (budgetTermination)
|
|
674
865
|
break;
|
|
675
866
|
tlog.info("turn.max_tokens_continuation", { cat: "turn", retry: retry + 1 });
|
|
676
867
|
const contMessages = [
|
|
@@ -683,18 +874,40 @@ export class TurnLoop {
|
|
|
683
874
|
];
|
|
684
875
|
try {
|
|
685
876
|
const contResponse = await this.deps.model.call(this.deps.systemPrompt, this.prepareMessagesForModel(contMessages), this.deps.tools, this.config.onStream, this.config.signal);
|
|
877
|
+
// Continuations are separate provider responses, so preserve the
|
|
878
|
+
// same structural tool_use invariant before processing this one.
|
|
879
|
+
if (contResponse.toolCalls.length > 0) {
|
|
880
|
+
this.stopBlockCount = 0;
|
|
881
|
+
}
|
|
686
882
|
if (contResponse.usage?.promptTokens !== undefined) {
|
|
687
|
-
this.recordResponseUsage(contResponse.usage);
|
|
883
|
+
this.recordResponseUsage(contResponse.usage, "continuation");
|
|
688
884
|
continuedResponse = true;
|
|
689
885
|
}
|
|
886
|
+
if (this.goalTracker && contResponse.usage) {
|
|
887
|
+
const used = (contResponse.usage.promptTokens ?? 0) +
|
|
888
|
+
(contResponse.usage.completionTokens ?? 0);
|
|
889
|
+
recordGoalUsage(this.goalTracker, used);
|
|
890
|
+
}
|
|
891
|
+
budgetTermination = this.goalTracker
|
|
892
|
+
? goalBudgetTerminationReason(this.goalTracker, Date.now())
|
|
893
|
+
: undefined;
|
|
690
894
|
combinedText += contResponse.text;
|
|
691
|
-
if (
|
|
895
|
+
if (budgetTermination ||
|
|
896
|
+
!isTruncatedStop(contResponse.stopReason) ||
|
|
897
|
+
contResponse.toolCalls.length > 0) {
|
|
692
898
|
response = { ...contResponse, text: combinedText };
|
|
693
899
|
break;
|
|
694
900
|
}
|
|
695
901
|
}
|
|
696
|
-
catch {
|
|
697
|
-
|
|
902
|
+
catch (err) {
|
|
903
|
+
if (isAbortError(err) || this.config.signal?.aborted) {
|
|
904
|
+
this.markStopped();
|
|
905
|
+
this.finalizeModelTurn();
|
|
906
|
+
return { text: finalText, reason: "aborted_streaming", messages };
|
|
907
|
+
}
|
|
908
|
+
this.config.onStream?.({ type: "error", error: formatFriendlyError(err) });
|
|
909
|
+
this.finalizeModelTurn();
|
|
910
|
+
return { text: finalText, reason: "model_error", messages };
|
|
698
911
|
}
|
|
699
912
|
}
|
|
700
913
|
response = { ...response, text: combinedText };
|
|
@@ -702,16 +915,10 @@ export class TurnLoop {
|
|
|
702
915
|
this.emitCtxFromUsage(response.usage, messages);
|
|
703
916
|
}
|
|
704
917
|
}
|
|
705
|
-
// Goal-mode run-scoped accounting: add this turn's total token usage
|
|
706
|
-
// (prompt + completion) to the running total. Done after continuation so
|
|
707
|
-
// continued output is counted against the budget.
|
|
708
|
-
if (goalTracker && response.usage) {
|
|
709
|
-
const used = (response.usage.promptTokens ?? 0) + (response.usage.completionTokens ?? 0);
|
|
710
|
-
recordGoalUsage(goalTracker, used);
|
|
711
|
-
}
|
|
712
918
|
// Aborted?
|
|
713
919
|
if (this.config.signal?.aborted) {
|
|
714
920
|
this.markStopped();
|
|
921
|
+
this.finalizeModelTurn();
|
|
715
922
|
return { text: finalText, reason: "aborted_streaming", messages };
|
|
716
923
|
}
|
|
717
924
|
// Accumulate text
|
|
@@ -722,13 +929,13 @@ export class TurnLoop {
|
|
|
722
929
|
// is force-stopped regardless of what the model wants to do next (stop OR
|
|
723
930
|
// continue with tool calls). This is the unattended-safety backstop, so
|
|
724
931
|
// it sits BEFORE the "tool calls?" branch — both paths pass the gate.
|
|
725
|
-
|
|
726
|
-
? goalBudgetTerminationReason(goalTracker, Date.now())
|
|
932
|
+
budgetTermination = this.goalTracker
|
|
933
|
+
? (budgetTermination ?? goalBudgetTerminationReason(this.goalTracker, Date.now()))
|
|
727
934
|
: undefined;
|
|
728
|
-
if (goalTracker && budgetTermination) {
|
|
935
|
+
if (this.goalTracker && budgetTermination) {
|
|
729
936
|
tlog.info("turn.goal_budget_exhausted", {
|
|
730
937
|
cat: "goal",
|
|
731
|
-
tokensUsed: goalTracker.tokensUsed,
|
|
938
|
+
tokensUsed: this.goalTracker.tokensUsed,
|
|
732
939
|
tokenBudget: this.config.goal?.tokenBudget,
|
|
733
940
|
timeBudgetMs: this.config.goal?.timeBudgetMs,
|
|
734
941
|
});
|
|
@@ -740,11 +947,13 @@ export class TurnLoop {
|
|
|
740
947
|
content: "(Goal 预算已耗尽,强制停止。)",
|
|
741
948
|
},
|
|
742
949
|
});
|
|
950
|
+
this.finalizeModelTurn();
|
|
743
951
|
return {
|
|
744
952
|
text: finalText,
|
|
745
953
|
reason: "goal_budget_exhausted",
|
|
746
954
|
messages,
|
|
747
955
|
goalTermination: budgetTermination,
|
|
956
|
+
goalTerminationRound: this.stopBlockCount,
|
|
748
957
|
};
|
|
749
958
|
}
|
|
750
959
|
// Post-check: tool calls?
|
|
@@ -760,37 +969,93 @@ export class TurnLoop {
|
|
|
760
969
|
hasToolUse: false,
|
|
761
970
|
});
|
|
762
971
|
messages.push({ role: "assistant", content: finalText });
|
|
972
|
+
this.finalizeModelTurn();
|
|
763
973
|
if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
764
974
|
continue;
|
|
765
975
|
}
|
|
976
|
+
// A steer accepted while an async tool was launching must be
|
|
977
|
+
// answered before parking the run. Keep the tool's yield request
|
|
978
|
+
// pending across that extra model round, then park once the model
|
|
979
|
+
// has replied and there is still no background result to consume.
|
|
980
|
+
if (this.deps.consumeToolRunYield?.() === "background_notification") {
|
|
981
|
+
tlog.info("turn.background_notification_wait_after_steer", { cat: "turn" });
|
|
982
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
983
|
+
return {
|
|
984
|
+
text: finalText,
|
|
985
|
+
reason: "completed",
|
|
986
|
+
messages,
|
|
987
|
+
completionKind: "background_wait",
|
|
988
|
+
};
|
|
989
|
+
}
|
|
766
990
|
// on_stop seam: the model wants to stop. Give handlers (Goal mode)
|
|
767
991
|
// a chance to BLOCK termination and keep the agent working. A
|
|
768
992
|
// handler returning continueSession=true injects its messages and
|
|
769
993
|
// we run another turn instead of returning. Bounded by
|
|
770
994
|
// maxStopBlocks (consecutive) and the outer maxTurns ceiling.
|
|
771
995
|
const maxStopBlocks = this.config.maxStopBlocks ?? GOAL_DEFAULT_MAX_STOP_BLOCKS;
|
|
772
|
-
if (goalTracker) {
|
|
773
|
-
this.
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
progress: {
|
|
778
|
-
turnCount: this.turnCount,
|
|
779
|
-
stopRound: this.stopBlockCount + 1,
|
|
780
|
-
elapsedMs: Math.max(0, Date.now() - goalTracker.startedAtMs),
|
|
781
|
-
tokensUsed: goalTracker.tokensUsed,
|
|
782
|
-
tokenBudget: goalTracker.goal.tokenBudget,
|
|
783
|
-
timeBudgetMs: goalTracker.goal.timeBudgetMs,
|
|
784
|
-
maxTurns: this.config.maxTurns,
|
|
785
|
-
maxStopBlocks,
|
|
786
|
-
},
|
|
787
|
-
});
|
|
996
|
+
if (this.goalTracker) {
|
|
997
|
+
const judgeMessages = this.stripVolatileContextMessages(messages);
|
|
998
|
+
this.deps.publishGoalJudgeContext?.(buildGoalJudgeRuntimeContext(judgeMessages, {
|
|
999
|
+
sensitiveToolResultRedactions: this.sensitiveToolResultRedactions,
|
|
1000
|
+
}));
|
|
788
1001
|
}
|
|
789
1002
|
const stopHook = await this.emitHook("on_stop", {
|
|
790
1003
|
goal: this.config.goal,
|
|
791
1004
|
finalText,
|
|
792
1005
|
turnCount: this.turnCount,
|
|
793
1006
|
});
|
|
1007
|
+
// The primary-model judge is a real billed request. Its hook forwards
|
|
1008
|
+
// usage through recordGoalJudgeUsage above; re-check immediately so an
|
|
1009
|
+
// over-budget verdict cannot authorize another main-model turn.
|
|
1010
|
+
const hookGoalTermination = stopHook.goalTermination;
|
|
1011
|
+
const hookBudgetTermination = hookGoalTermination === "token_budget_exhausted" ||
|
|
1012
|
+
hookGoalTermination === "time_budget_exhausted"
|
|
1013
|
+
? hookGoalTermination
|
|
1014
|
+
: undefined;
|
|
1015
|
+
const postJudgeBudgetTermination = this.goalTracker
|
|
1016
|
+
? (hookBudgetTermination ?? goalBudgetTerminationReason(this.goalTracker, Date.now()))
|
|
1017
|
+
: undefined;
|
|
1018
|
+
if (this.goalTracker && postJudgeBudgetTermination) {
|
|
1019
|
+
tlog.info("turn.goal_budget_exhausted_after_judge", {
|
|
1020
|
+
cat: "goal",
|
|
1021
|
+
reason: postJudgeBudgetTermination,
|
|
1022
|
+
tokensUsed: this.goalTracker.tokensUsed,
|
|
1023
|
+
tokenBudget: this.goalTracker.goal.tokenBudget,
|
|
1024
|
+
timeBudgetMs: this.goalTracker.goal.timeBudgetMs,
|
|
1025
|
+
});
|
|
1026
|
+
this.config.onStream?.({
|
|
1027
|
+
type: "assistant_message",
|
|
1028
|
+
messageId: assistantMessageId,
|
|
1029
|
+
message: {
|
|
1030
|
+
role: "assistant",
|
|
1031
|
+
content: "(Goal 预算已耗尽,强制停止。)",
|
|
1032
|
+
},
|
|
1033
|
+
});
|
|
1034
|
+
return {
|
|
1035
|
+
text: finalText,
|
|
1036
|
+
reason: "goal_budget_exhausted",
|
|
1037
|
+
messages,
|
|
1038
|
+
goalTermination: postJudgeBudgetTermination,
|
|
1039
|
+
goalTerminationRound: this.stopBlockCount,
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
if (this.goalTracker && hookGoalTermination === "judge_prompt_too_large") {
|
|
1043
|
+
tlog.warn("turn.goal_judge_prompt_too_large", { cat: "goal" });
|
|
1044
|
+
this.config.onStream?.({
|
|
1045
|
+
type: "assistant_message",
|
|
1046
|
+
messageId: assistantMessageId,
|
|
1047
|
+
message: {
|
|
1048
|
+
role: "assistant",
|
|
1049
|
+
content: "(Goal 裁判输入超过安全上限,无法判定;本次已停止,目标已保留。)",
|
|
1050
|
+
},
|
|
1051
|
+
});
|
|
1052
|
+
return {
|
|
1053
|
+
text: finalText,
|
|
1054
|
+
reason: "completed",
|
|
1055
|
+
messages,
|
|
1056
|
+
goalTermination: hookGoalTermination,
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
794
1059
|
// The judge's structured verdict (set by GoalStopHook in result.data)
|
|
795
1060
|
// rides back here so we can show goal progress WITHOUT a second LLM
|
|
796
1061
|
// call — `gaps` is whatever the judge already computed.
|
|
@@ -801,6 +1066,8 @@ export class TurnLoop {
|
|
|
801
1066
|
// up with stopBlockCount so the UI can show "第 N 轮".
|
|
802
1067
|
this.config.onStream?.({
|
|
803
1068
|
type: "goal_progress",
|
|
1069
|
+
goalId: this.config.goal?.goalId,
|
|
1070
|
+
revision: this.config.goal?.revision,
|
|
804
1071
|
status: "not_met",
|
|
805
1072
|
round: this.stopBlockCount,
|
|
806
1073
|
gaps: goalVerdict?.gaps || undefined,
|
|
@@ -839,11 +1106,6 @@ export class TurnLoop {
|
|
|
839
1106
|
stopBlockCount: this.stopBlockCount,
|
|
840
1107
|
maxStopBlocks,
|
|
841
1108
|
});
|
|
842
|
-
this.config.onStream?.({
|
|
843
|
-
type: "goal_progress",
|
|
844
|
-
status: "exhausted",
|
|
845
|
-
round: this.stopBlockCount,
|
|
846
|
-
});
|
|
847
1109
|
goalTermination = "stop_blocks_exhausted";
|
|
848
1110
|
this.config.onStream?.({
|
|
849
1111
|
type: "assistant_message",
|
|
@@ -859,18 +1121,28 @@ export class TurnLoop {
|
|
|
859
1121
|
// rounds = prior blocks + this accepted final round.
|
|
860
1122
|
this.config.onStream?.({
|
|
861
1123
|
type: "goal_progress",
|
|
1124
|
+
goalId: this.config.goal.goalId,
|
|
1125
|
+
revision: this.config.goal.revision,
|
|
862
1126
|
status: "met",
|
|
863
1127
|
round: this.stopBlockCount + 1,
|
|
864
1128
|
});
|
|
865
1129
|
}
|
|
1130
|
+
const goalTerminationRound = goalTermination ? this.stopBlockCount : undefined;
|
|
866
1131
|
this.stopBlockCount = 0;
|
|
867
1132
|
if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
868
1133
|
continue;
|
|
869
1134
|
}
|
|
870
1135
|
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
871
|
-
return {
|
|
1136
|
+
return {
|
|
1137
|
+
text: finalText,
|
|
1138
|
+
reason: "completed",
|
|
1139
|
+
messages,
|
|
1140
|
+
goalTermination,
|
|
1141
|
+
goalTerminationRound,
|
|
1142
|
+
};
|
|
872
1143
|
}
|
|
873
1144
|
// Tool execution phase
|
|
1145
|
+
this.deps.onAgentControlState?.("tool-batch");
|
|
874
1146
|
tlog.info("turn.tool_use", {
|
|
875
1147
|
cat: "turn",
|
|
876
1148
|
tools: response.toolCalls.map((t) => t.toolName),
|
|
@@ -911,17 +1183,13 @@ export class TurnLoop {
|
|
|
911
1183
|
// Record results in transcript and stream
|
|
912
1184
|
const resultBlocks = [];
|
|
913
1185
|
for (const result of results) {
|
|
914
|
-
if (goalTracker) {
|
|
915
|
-
goalToolResults.push(projectGoalJudgeToolResult(result, this.turnCount));
|
|
916
|
-
// The prompt renderer keeps at most 12, but retaining a few extra
|
|
917
|
-
// bounded projections lets its total-char budget choose the newest useful subset.
|
|
918
|
-
if (goalToolResults.length > 20)
|
|
919
|
-
goalToolResults.splice(0, goalToolResults.length - 20);
|
|
920
|
-
}
|
|
921
1186
|
resultBlocks.push(toolResultToBlock(result));
|
|
922
1187
|
const streamResult = toolResultForDisplay(result);
|
|
923
|
-
const
|
|
924
|
-
|
|
1188
|
+
const sensitiveByMetadata = sensitiveGoalResultTools?.has(result.toolName) === true;
|
|
1189
|
+
const transcriptResult = sensitiveByMetadata
|
|
1190
|
+
? SENSITIVE_TOOL_RESULT_PLACEHOLDER
|
|
1191
|
+
: toolResultTranscriptText(result);
|
|
1192
|
+
if ((result.sensitive || sensitiveByMetadata) && transcriptResult !== undefined) {
|
|
925
1193
|
this.sensitiveToolResultRedactions.set(result.id, transcriptResult);
|
|
926
1194
|
}
|
|
927
1195
|
this.deps.transcript.appendToolResult(result.id, result.toolName, transcriptResult, result.error, result.sensitive ? undefined : result.contentBlocks);
|
|
@@ -931,13 +1199,15 @@ export class TurnLoop {
|
|
|
931
1199
|
// best-effort observability — a thrown onStream handler, a failed dynamic
|
|
932
1200
|
// import, or a rejecting summarize must never surface as an unhandled
|
|
933
1201
|
// rejection (Node can treat those as fatal). Swallow at the tail. (B-2)
|
|
934
|
-
if (this.config.onStream) {
|
|
1202
|
+
if (this.config.onStream && this.deps.model.summarize && !this.goalControlStopRequested) {
|
|
1203
|
+
const summaryController = new AbortController();
|
|
1204
|
+
this.activeToolSummaryControllers.add(summaryController);
|
|
935
1205
|
void import("./tool-summary.js")
|
|
936
1206
|
.then(({ generateToolUseSummary }) => {
|
|
937
|
-
if (
|
|
1207
|
+
if (this.goalControlStopRequested || summaryController.signal.aborted)
|
|
938
1208
|
return;
|
|
939
|
-
return generateToolUseSummary(toolCalls, toolResultsForDisplay(results), this.deps.model.summarize).then((summary) => {
|
|
940
|
-
if (summary) {
|
|
1209
|
+
return generateToolUseSummary(toolCalls, toolResultsForDisplay(results), this.deps.model.summarize, summaryController.signal).then((summary) => {
|
|
1210
|
+
if (summary && !summaryController.signal.aborted) {
|
|
941
1211
|
this.config.onStream?.({
|
|
942
1212
|
type: "tool_summary",
|
|
943
1213
|
summary,
|
|
@@ -951,6 +1221,9 @@ export class TurnLoop {
|
|
|
951
1221
|
cat: "turn",
|
|
952
1222
|
error: err.message,
|
|
953
1223
|
});
|
|
1224
|
+
})
|
|
1225
|
+
.finally(() => {
|
|
1226
|
+
this.activeToolSummaryControllers.delete(summaryController);
|
|
954
1227
|
});
|
|
955
1228
|
}
|
|
956
1229
|
const toolResultMessage = { role: "user", content: resultBlocks };
|
|
@@ -981,6 +1254,31 @@ export class TurnLoop {
|
|
|
981
1254
|
// Tool results just pushed; recompute ctx so the bar updates *before*
|
|
982
1255
|
// the next model round-trip — large tool outputs can move it sharply.
|
|
983
1256
|
this.emitCtxFromMessages(messages);
|
|
1257
|
+
// A trusted tool launched asynchronous work whose completion is routed
|
|
1258
|
+
// back into this Session. End this run at the tool boundary instead of
|
|
1259
|
+
// asking the model for another step with no new evidence; the queued
|
|
1260
|
+
// completion notification will wake the Session and continue normally.
|
|
1261
|
+
// This precedes complete_goal so a single batch cannot launch unfinished
|
|
1262
|
+
// background work and simultaneously claim the enclosing Goal is done.
|
|
1263
|
+
if (this.deps.peekToolRunYield?.() === "background_notification") {
|
|
1264
|
+
// Close the current model turn before a queued steer re-drives it;
|
|
1265
|
+
// interrupt-and-redrive swaps the turn signal at this boundary.
|
|
1266
|
+
this.finalizeModelTurn();
|
|
1267
|
+
if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
1268
|
+
continue;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
if (this.deps.peekToolRunYield?.() === "background_notification" &&
|
|
1272
|
+
this.deps.consumeToolRunYield?.() === "background_notification") {
|
|
1273
|
+
tlog.info("turn.background_notification_wait", { cat: "turn" });
|
|
1274
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
1275
|
+
return {
|
|
1276
|
+
text: finalText,
|
|
1277
|
+
reason: "completed",
|
|
1278
|
+
messages,
|
|
1279
|
+
completionKind: "background_wait",
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
984
1282
|
// Goal mode P0: explicit completion. If the model called complete_goal,
|
|
985
1283
|
// it has DECLARED the goal done — short-circuit to "completed" WITHOUT
|
|
986
1284
|
// running the judge hook. The tool's result is already in `messages`
|
|
@@ -990,15 +1288,33 @@ export class TurnLoop {
|
|
|
990
1288
|
// the model just declared finished (the judge's onMet does this when IT
|
|
991
1289
|
// decides; a self-report must do the same, else the goal outlives its
|
|
992
1290
|
// completion and re-arms every follow-up turn).
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1291
|
+
const completeGoalRequested = !!this.goalTracker &&
|
|
1292
|
+
isSameGoalVersion(requestedGoalVersion, this.config.goal) &&
|
|
1293
|
+
toolCalls.some((tc) => tc.toolName === COMPLETE_GOAL_TOOL_NAME);
|
|
1294
|
+
if (completeGoalRequested) {
|
|
1295
|
+
const persisted = this.deps.clearPersistedGoal?.("completed") !== false;
|
|
1296
|
+
if (persisted) {
|
|
1297
|
+
tlog.info("turn.goal_self_reported_complete", { cat: "goal" });
|
|
1298
|
+
this.config.onStream?.({
|
|
1299
|
+
type: "goal_progress",
|
|
1300
|
+
goalId: this.config.goal?.goalId,
|
|
1301
|
+
revision: this.config.goal?.revision,
|
|
1302
|
+
status: "met",
|
|
1303
|
+
round: this.stopBlockCount + 1,
|
|
1304
|
+
});
|
|
1305
|
+
this.stopBlockCount = 0;
|
|
1306
|
+
this.finalizeModelTurn();
|
|
1307
|
+
if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
1308
|
+
continue;
|
|
1309
|
+
}
|
|
1310
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
1311
|
+
return { text: finalText, reason: "completed", messages };
|
|
999
1312
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1313
|
+
tlog.warn("turn.goal_self_reported_complete_persist_failed", { cat: "goal" });
|
|
1314
|
+
messages.push({
|
|
1315
|
+
role: "user",
|
|
1316
|
+
content: "<system-reminder>complete_goal 未能持久化完成状态;目标仍处于活动状态。请不要宣称已经结束,并在会话状态恢复可写后重试。</system-reminder>",
|
|
1317
|
+
});
|
|
1002
1318
|
}
|
|
1003
1319
|
// Goal mode: user-initiated cancellation. cancel_goal is the "strong
|
|
1004
1320
|
// intent" escape hatch — honor it ONLY when confirm===true (the guard the
|
|
@@ -1006,14 +1322,35 @@ export class TurnLoop {
|
|
|
1006
1322
|
// persisted goal so it never re-arms; an unconfirmed call is a no-op here
|
|
1007
1323
|
// (the tool's own result string already told the model it was ignored).
|
|
1008
1324
|
const confirmedCancel = toolCalls.some((tc) => tc.toolName === CANCEL_GOAL_TOOL_NAME && tc.args?.confirm === true);
|
|
1009
|
-
if (goalTracker &&
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1325
|
+
if (this.goalTracker &&
|
|
1326
|
+
isSameGoalVersion(requestedGoalVersion, this.config.goal) &&
|
|
1327
|
+
confirmedCancel &&
|
|
1328
|
+
!completeGoalRequested) {
|
|
1329
|
+
const persisted = this.deps.clearPersistedGoal?.("cancelled") !== false;
|
|
1330
|
+
if (persisted) {
|
|
1331
|
+
tlog.info("turn.goal_user_cancelled", { cat: "goal" });
|
|
1332
|
+
this.stopBlockCount = 0;
|
|
1333
|
+
this.config.onStream?.({
|
|
1334
|
+
type: "goal_cleared",
|
|
1335
|
+
goalId: this.config.goal?.goalId,
|
|
1336
|
+
revision: this.config.goal?.revision,
|
|
1337
|
+
});
|
|
1338
|
+
this.finalizeModelTurn();
|
|
1339
|
+
if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
return {
|
|
1343
|
+
text: finalText,
|
|
1344
|
+
reason: "completed",
|
|
1345
|
+
messages,
|
|
1346
|
+
completionKind: "goal_control_stop",
|
|
1347
|
+
};
|
|
1015
1348
|
}
|
|
1016
|
-
|
|
1349
|
+
tlog.warn("turn.goal_user_cancelled_persist_failed", { cat: "goal" });
|
|
1350
|
+
messages.push({
|
|
1351
|
+
role: "user",
|
|
1352
|
+
content: "<system-reminder>cancel_goal 未能持久化删除状态;目标仍处于活动状态。请在会话状态恢复可写后重试。</system-reminder>",
|
|
1353
|
+
});
|
|
1017
1354
|
}
|
|
1018
1355
|
// Token budget check
|
|
1019
1356
|
const totalOutputTokens = this.deps.model.getOutputTokens?.() ?? 0;
|
|
@@ -1030,11 +1367,17 @@ export class TurnLoop {
|
|
|
1030
1367
|
message: { role: "assistant", content: finalText },
|
|
1031
1368
|
});
|
|
1032
1369
|
messages.push({ role: "assistant", content: finalText });
|
|
1370
|
+
this.finalizeModelTurn();
|
|
1033
1371
|
if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
1034
1372
|
continue;
|
|
1035
1373
|
}
|
|
1036
1374
|
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
1037
|
-
return {
|
|
1375
|
+
return {
|
|
1376
|
+
text: finalText,
|
|
1377
|
+
reason: "completed",
|
|
1378
|
+
messages,
|
|
1379
|
+
completionKind: "limit_stop",
|
|
1380
|
+
};
|
|
1038
1381
|
}
|
|
1039
1382
|
if (budgetDecision === "nudge") {
|
|
1040
1383
|
messages.push({
|
|
@@ -1072,9 +1415,7 @@ export class TurnLoop {
|
|
|
1072
1415
|
hasToolUse: true,
|
|
1073
1416
|
toolCallCount: toolCalls.length,
|
|
1074
1417
|
});
|
|
1075
|
-
|
|
1076
|
-
this.deps.transcript.appendTurnBoundary();
|
|
1077
|
-
this.config.onTurnBoundary?.(this.turnCount);
|
|
1418
|
+
this.finalizeModelTurn();
|
|
1078
1419
|
tlog.info("turn.end", {
|
|
1079
1420
|
cat: "turn",
|
|
1080
1421
|
duration_ms: Date.now() - turnStartedAt,
|
|
@@ -1089,6 +1430,9 @@ export class TurnLoop {
|
|
|
1089
1430
|
// engine's post-run saveState records model_error instead of leaving
|
|
1090
1431
|
// the session stuck at "active".
|
|
1091
1432
|
this.patchOrphanedToolUses(messages);
|
|
1433
|
+
// If the model already returned, this is still a completed model turn.
|
|
1434
|
+
// The idempotent helper is a no-op when its normal branch finalized first.
|
|
1435
|
+
this.finalizeModelTurn();
|
|
1092
1436
|
// A user-initiated Stop also bubbles here (an AbortError thrown from the
|
|
1093
1437
|
// per-turn scaffolding). Treat it as a clean abort, not a model_error —
|
|
1094
1438
|
// no error event, so the UI shows only the "你停止了本轮" line.
|
|
@@ -1106,6 +1450,12 @@ export class TurnLoop {
|
|
|
1106
1450
|
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
1107
1451
|
return { text: finalText, reason: "model_error", messages };
|
|
1108
1452
|
}
|
|
1453
|
+
// Pause/delete may have landed while the final allowed tool batch was
|
|
1454
|
+
// draining. Do not cross that safe boundary into an extra summary request.
|
|
1455
|
+
if (this.goalControlStopRequested) {
|
|
1456
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
1457
|
+
return { text: finalText, reason: "completed", messages };
|
|
1458
|
+
}
|
|
1109
1459
|
// Max turns reached — do one final summarization call (no tools)
|
|
1110
1460
|
logger.warn("turn.max_turns_reached", {
|
|
1111
1461
|
cat: "turn",
|
|
@@ -1113,6 +1463,11 @@ export class TurnLoop {
|
|
|
1113
1463
|
turnCount: this.turnCount,
|
|
1114
1464
|
});
|
|
1115
1465
|
await this.consumeQueuedSteer(messages, "finalize_backfill");
|
|
1466
|
+
if (this.goalControlStopRequested) {
|
|
1467
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
1468
|
+
return { text: finalText, reason: "completed", messages };
|
|
1469
|
+
}
|
|
1470
|
+
this.deps.onAgentControlState?.("model");
|
|
1116
1471
|
const hasPendingSensitiveToolResults = this.sensitiveToolResultRedactions.size > 0;
|
|
1117
1472
|
messages = this.prepareMessagesForModel(messages);
|
|
1118
1473
|
if (hasPendingSensitiveToolResults) {
|
|
@@ -1125,20 +1480,44 @@ export class TurnLoop {
|
|
|
1125
1480
|
else {
|
|
1126
1481
|
messages = this.deps.contextManager.manage(messages);
|
|
1127
1482
|
}
|
|
1483
|
+
if (this.goalControlStopRequested) {
|
|
1484
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
1485
|
+
return { text: finalText, reason: "completed", messages };
|
|
1486
|
+
}
|
|
1128
1487
|
messages.push({
|
|
1129
1488
|
role: "user",
|
|
1130
1489
|
content: "<system-reminder>Turn limit reached. Provide a final summary of what you accomplished and what remains to be done. Do NOT call any tools.</system-reminder>",
|
|
1131
1490
|
});
|
|
1132
1491
|
this.emitCtxFromMessages(messages);
|
|
1133
1492
|
try {
|
|
1493
|
+
if (this.goalControlStopRequested) {
|
|
1494
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
1495
|
+
return { text: finalText, reason: "completed", messages };
|
|
1496
|
+
}
|
|
1134
1497
|
const summaryResponse = await this.deps.model.call(this.deps.systemPrompt, this.prepareMessagesForModel(messages), [], // No tools available for summary turn
|
|
1135
1498
|
this.config.onStream, this.config.signal, this.modelCallRecordingOptions());
|
|
1499
|
+
if (summaryResponse.usage?.promptTokens !== undefined) {
|
|
1500
|
+
this.recordResponseUsage(summaryResponse.usage, "primary", false);
|
|
1501
|
+
}
|
|
1502
|
+
if (this.goalTracker && summaryResponse.usage) {
|
|
1503
|
+
recordGoalUsage(this.goalTracker, (summaryResponse.usage.promptTokens ?? 0) + (summaryResponse.usage.completionTokens ?? 0));
|
|
1504
|
+
}
|
|
1505
|
+
if (this.config.signal?.aborted) {
|
|
1506
|
+
this.markStopped();
|
|
1507
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
1508
|
+
return { text: finalText, reason: "aborted_streaming", messages };
|
|
1509
|
+
}
|
|
1136
1510
|
messages = this.markPendingImagesConsumed(messages);
|
|
1137
1511
|
if (summaryResponse.text) {
|
|
1138
1512
|
finalText = summaryResponse.text;
|
|
1139
1513
|
}
|
|
1140
1514
|
}
|
|
1141
|
-
catch {
|
|
1515
|
+
catch (err) {
|
|
1516
|
+
if (isAbortError(err) || this.config.signal?.aborted) {
|
|
1517
|
+
this.markStopped();
|
|
1518
|
+
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
1519
|
+
return { text: finalText, reason: "aborted_streaming", messages };
|
|
1520
|
+
}
|
|
1142
1521
|
// If even summary fails, just return what we have
|
|
1143
1522
|
this.currentTurnLog.warn("turn.summary_failed", { cat: "turn" });
|
|
1144
1523
|
}
|
|
@@ -1155,6 +1534,7 @@ export class TurnLoop {
|
|
|
1155
1534
|
reason: "max_turns",
|
|
1156
1535
|
messages,
|
|
1157
1536
|
...(this.config.goal ? { goalTermination: "max_turns_exhausted" } : {}),
|
|
1537
|
+
...(this.config.goal ? { goalTerminationRound: this.stopBlockCount } : {}),
|
|
1158
1538
|
};
|
|
1159
1539
|
}
|
|
1160
1540
|
/**
|
|
@@ -1213,6 +1593,14 @@ export class TurnLoop {
|
|
|
1213
1593
|
// really just a cancel. The signal is the authoritative cancel source.
|
|
1214
1594
|
if (isAbortError(err) || this.config.signal?.aborted)
|
|
1215
1595
|
throw err;
|
|
1596
|
+
// Pause/delete arrived while the streaming attempt was in flight. A
|
|
1597
|
+
// non-streaming fallback would be a brand-new provider request after the
|
|
1598
|
+
// promised safe boundary, so revoke any partial stream and synthesize an
|
|
1599
|
+
// empty settled step instead.
|
|
1600
|
+
if (this.goalControlStopRequested) {
|
|
1601
|
+
this.config.onStream?.({ type: "tombstone", messageId: assistantMessageId });
|
|
1602
|
+
return { text: "", toolCalls: [], stopReason: "stop" };
|
|
1603
|
+
}
|
|
1216
1604
|
// Sensitive tool results are model-facing exactly once. A streaming
|
|
1217
1605
|
// fallback would re-send the same pending plaintext in a second request,
|
|
1218
1606
|
// so fail the turn and let the unified exit redact returned history.
|
|
@@ -1238,8 +1626,25 @@ export class TurnLoop {
|
|
|
1238
1626
|
return this.turnCount;
|
|
1239
1627
|
}
|
|
1240
1628
|
async consumeQueuedSteer(messages, source) {
|
|
1241
|
-
const steered = this.deps.consumeSteer?.(source) ?? [];
|
|
1242
1629
|
let consumed = false;
|
|
1630
|
+
const goalControl = this.pendingGoalControlMessage;
|
|
1631
|
+
this.pendingGoalControlMessage = null;
|
|
1632
|
+
if (goalControl &&
|
|
1633
|
+
this.config.goal?.paused !== true &&
|
|
1634
|
+
this.config.goal?.goalId === goalControl.goalId &&
|
|
1635
|
+
(this.config.goal?.revision ?? 1) === (goalControl.revision ?? 1)) {
|
|
1636
|
+
const content = `<system-reminder>\n${goalControl.text}\n</system-reminder>`;
|
|
1637
|
+
const message = { role: "user", content };
|
|
1638
|
+
messages.push(message);
|
|
1639
|
+
this.deps.transcript.appendMessage("user", content, {
|
|
1640
|
+
injected: true,
|
|
1641
|
+
source: "goal-control",
|
|
1642
|
+
});
|
|
1643
|
+
consumed = true;
|
|
1644
|
+
}
|
|
1645
|
+
const directionConsumed = await this.consumeQueuedAgentDirections(messages, source);
|
|
1646
|
+
const steered = this.deps.consumeSteer?.(source) ?? [];
|
|
1647
|
+
consumed ||= directionConsumed;
|
|
1243
1648
|
for (let index = 0; index < steered.length; index++) {
|
|
1244
1649
|
const item = steered[index];
|
|
1245
1650
|
const { id, text, clientMessageId } = item;
|
|
@@ -1285,6 +1690,42 @@ export class TurnLoop {
|
|
|
1285
1690
|
}
|
|
1286
1691
|
return consumed;
|
|
1287
1692
|
}
|
|
1693
|
+
async consumeQueuedAgentDirections(messages, source) {
|
|
1694
|
+
if (!this.deps.consumeAgentDirections)
|
|
1695
|
+
return false;
|
|
1696
|
+
this.deps.onAgentControlState?.("safe-point");
|
|
1697
|
+
const directions = this.deps.consumeAgentDirections();
|
|
1698
|
+
if (directions.length === 0)
|
|
1699
|
+
return false;
|
|
1700
|
+
directions.sort((left, right) => left.sequence - right.sequence);
|
|
1701
|
+
const content = buildAgentDirectionMessage(directions);
|
|
1702
|
+
const envelopeIds = directions.map((item) => item.id);
|
|
1703
|
+
const correlationIds = directions
|
|
1704
|
+
.map((item) => item.correlationId)
|
|
1705
|
+
.filter((value) => value !== undefined);
|
|
1706
|
+
const hook = await this.emitHook("agent_direction_submit", {
|
|
1707
|
+
source: "agent-direction",
|
|
1708
|
+
authority: "agent",
|
|
1709
|
+
prompt: directions.map((item) => item.payload.prompt).join("\n"),
|
|
1710
|
+
envelopeIds,
|
|
1711
|
+
correlationIds,
|
|
1712
|
+
safePoint: source,
|
|
1713
|
+
});
|
|
1714
|
+
const message = { role: "user", content };
|
|
1715
|
+
messages.push(message);
|
|
1716
|
+
this.deps.transcript.appendMessage("user", content, {
|
|
1717
|
+
injected: true,
|
|
1718
|
+
authority: "agent",
|
|
1719
|
+
source: "agent-direction",
|
|
1720
|
+
envelopeIds,
|
|
1721
|
+
correlationIds,
|
|
1722
|
+
});
|
|
1723
|
+
this.deps.onAgentDirectionsDelivered?.(envelopeIds);
|
|
1724
|
+
const hookInjection = wrapHookMessages(hook.messages);
|
|
1725
|
+
if (hookInjection)
|
|
1726
|
+
messages.push(hookInjection);
|
|
1727
|
+
return true;
|
|
1728
|
+
}
|
|
1288
1729
|
/**
|
|
1289
1730
|
* Generate synthetic error tool_results for any dangling tool_use blocks
|
|
1290
1731
|
* that never received results (e.g. because the API call failed).
|