@cjhyy/code-shell-core 0.5.0-rc.1 → 0.6.0-rc.1
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 +21 -3
- package/dist/agent/agent-definition-registry.js +49 -18
- package/dist/agent/agent-definition.d.ts +32 -0
- package/dist/agent/agent-definition.js +49 -4
- package/dist/arena/arena.js +11 -12
- package/dist/arena/context/context-tools.js +47 -6
- package/dist/arena/context/within-root.d.ts +7 -0
- package/dist/arena/context/within-root.js +15 -0
- package/dist/arena/iterate/phases/argue.js +8 -2
- package/dist/arena/iterate/phases/revise.js +1 -1
- package/dist/arena/iterate/phases/tournament.js +3 -3
- package/dist/arena/ledger.js +9 -1
- package/dist/arena/phases/adjudication.js +1 -4
- package/dist/arena/phases/build-consensus.js +1 -4
- package/dist/arena/phases/cross-review.js +2 -8
- package/dist/arena/phases/debate-rounds.js +1 -4
- package/dist/arena/phases/participant-research.js +1 -4
- package/dist/arena/phases/planning-detail-expansion.js +8 -10
- package/dist/arena/planner.js +0 -1
- package/dist/arena/providers/docs.d.ts +1 -0
- package/dist/arena/providers/docs.js +5 -2
- package/dist/arena/providers/repo.d.ts +1 -0
- package/dist/arena/providers/repo.js +5 -2
- package/dist/arena/strategies/utils.d.ts +1 -4
- package/dist/arena/strategies/utils.js +7 -51
- package/dist/arena/transitions.d.ts +7 -2
- package/dist/arena/transitions.js +9 -4
- package/dist/arena/types.d.ts +8 -2
- package/dist/automation/cron-expr.d.ts +41 -0
- package/dist/automation/cron-expr.js +169 -0
- package/dist/automation/index.d.ts +41 -0
- package/dist/automation/index.js +39 -0
- package/dist/automation/runner.d.ts +84 -0
- package/dist/automation/runner.js +92 -0
- package/dist/automation/scheduler.d.ts +194 -0
- package/dist/automation/scheduler.js +623 -0
- package/dist/automation/store.d.ts +39 -0
- package/dist/automation/store.js +122 -0
- package/dist/automation/write-policy.d.ts +34 -0
- package/dist/automation/write-policy.js +113 -0
- package/dist/automation/write-run.d.ts +51 -0
- package/dist/automation/write-run.js +38 -0
- package/dist/capability-control/disabled-lists.d.ts +25 -0
- package/dist/capability-control/disabled-lists.js +57 -0
- package/dist/capability-control/index.d.ts +5 -0
- package/dist/capability-control/index.js +3 -0
- package/dist/capability-control/overlay.d.ts +64 -0
- package/dist/capability-control/overlay.js +116 -0
- package/dist/capability-control/project.d.ts +65 -0
- package/dist/capability-control/project.js +147 -0
- package/dist/capability-control/service.d.ts +70 -0
- package/dist/capability-control/service.js +147 -0
- package/dist/capability-control/types.d.ts +62 -0
- package/dist/capability-control/types.js +17 -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 +26 -1
- package/dist/cli/agent-server-stdio.js +221 -28
- package/dist/cli/agent-server-tcp.d.ts +20 -0
- package/dist/cli/agent-server-tcp.js +134 -0
- package/dist/cli/graceful-shutdown.d.ts +27 -0
- package/dist/cli/graceful-shutdown.js +30 -0
- package/dist/context/compaction.d.ts +56 -0
- package/dist/context/compaction.js +209 -15
- package/dist/context/manager.d.ts +0 -20
- package/dist/context/manager.js +25 -41
- 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/cron/cron-runtime.d.ts +2 -0
- package/dist/cron/cron-runtime.js +2 -0
- package/dist/cron/cron-store.d.ts +2 -0
- package/dist/cron/cron-store.js +2 -0
- package/dist/cron/scheduler.d.ts +5 -32
- package/dist/cron/scheduler.js +5 -113
- 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/data/openrouter-sync.js +1 -1
- 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 +431 -108
- package/dist/engine/engine.js +1630 -279
- package/dist/engine/friendly-error.d.ts +18 -0
- package/dist/engine/friendly-error.js +63 -0
- package/dist/engine/goal.d.ts +187 -0
- package/dist/engine/goal.js +196 -0
- package/dist/engine/image-compression.d.ts +81 -0
- package/dist/engine/image-compression.js +186 -0
- package/dist/engine/image-policy.d.ts +165 -0
- package/dist/engine/image-policy.js +189 -0
- package/dist/engine/model-connections-pool.d.ts +17 -0
- package/dist/engine/model-connections-pool.js +66 -0
- package/dist/engine/model-facade.d.ts +10 -0
- package/dist/engine/model-facade.js +25 -0
- package/dist/engine/patch-orphaned-tools.js +3 -0
- package/dist/engine/reactive-threshold.d.ts +13 -0
- package/dist/engine/reactive-threshold.js +17 -0
- package/dist/engine/resolve-llm-config.d.ts +16 -0
- package/dist/engine/resolve-llm-config.js +44 -0
- package/dist/engine/runtime.js +9 -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-title.d.ts +13 -0
- package/dist/engine/session-title.js +34 -0
- package/dist/engine/session-usage.d.ts +19 -0
- package/dist/engine/session-usage.js +25 -0
- package/dist/engine/steer-queue.d.ts +32 -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 -9
- package/dist/engine/turn-loop.js +775 -342
- 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/parse-log.d.ts +13 -0
- package/dist/git/parse-log.js +21 -0
- package/dist/git/utils.d.ts +8 -6
- package/dist/git/utils.js +62 -26
- package/dist/git/worktree.d.ts +48 -0
- package/dist/git/worktree.js +111 -20
- package/dist/hooks/events.d.ts +23 -1
- package/dist/hooks/goal-stop-hook.d.ts +58 -0
- package/dist/hooks/goal-stop-hook.js +279 -0
- package/dist/hooks/hook-output.d.ts +33 -0
- package/dist/hooks/hook-output.js +79 -0
- package/dist/hooks/registry.d.ts +15 -0
- package/dist/hooks/registry.js +57 -1
- package/dist/hooks/shell-runner.d.ts +2 -0
- package/dist/hooks/shell-runner.js +48 -18
- package/dist/index.d.ts +79 -11
- package/dist/index.js +89 -6
- package/dist/llm/capabilities/param-specs.d.ts +14 -0
- package/dist/llm/capabilities/param-specs.js +62 -0
- package/dist/llm/capabilities/reasoning-control.d.ts +24 -0
- package/dist/llm/capabilities/reasoning-control.js +38 -0
- package/dist/llm/capabilities/rules.js +37 -3
- package/dist/llm/capabilities/types.d.ts +32 -2
- package/dist/llm/clamp-max-tokens.d.ts +11 -0
- package/dist/llm/clamp-max-tokens.js +17 -0
- package/dist/llm/client-base.d.ts +69 -5
- package/dist/llm/client-base.js +185 -25
- package/dist/llm/client-factory.d.ts +3 -3
- package/dist/llm/client-factory.js +2 -2
- package/dist/llm/model-cache.js +4 -2
- package/dist/llm/model-pool.d.ts +29 -11
- package/dist/llm/model-pool.js +36 -20
- package/dist/llm/provider-auth.d.ts +41 -0
- package/dist/llm/provider-auth.js +76 -0
- package/dist/llm/provider-catalog.d.ts +6 -2
- package/dist/llm/providers/anthropic.d.ts +32 -2
- package/dist/llm/providers/anthropic.js +207 -18
- package/dist/llm/providers/openai.d.ts +47 -2
- package/dist/llm/providers/openai.js +478 -71
- package/dist/llm/reasoning-setting.d.ts +51 -0
- package/dist/llm/reasoning-setting.js +40 -0
- package/dist/llm/stop-reason.d.ts +13 -0
- package/dist/llm/stop-reason.js +21 -0
- package/dist/llm/stream-watchdog.js +5 -1
- package/dist/llm/strip-vision.d.ts +32 -0
- package/dist/llm/strip-vision.js +94 -0
- package/dist/llm/token-counter.js +9 -2
- package/dist/llm/types.d.ts +9 -6
- package/dist/logging/logger.js +18 -4
- package/dist/logging/sanitize-messages.d.ts +10 -0
- package/dist/logging/sanitize-messages.js +112 -0
- package/dist/lsp/client.js +28 -8
- package/dist/lsp/manager.d.ts +1 -1
- package/dist/lsp/manager.js +42 -11
- package/dist/lsp/root-path.d.ts +9 -0
- package/dist/lsp/root-path.js +12 -0
- package/dist/model-catalog/builtin.d.ts +12 -0
- package/dist/model-catalog/builtin.js +407 -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 +75 -333
- package/dist/plugins/gitOps.d.ts +53 -0
- package/dist/plugins/gitOps.js +130 -5
- package/dist/plugins/installer/checkUpdate.d.ts +16 -0
- package/dist/plugins/installer/checkUpdate.js +32 -0
- package/dist/plugins/installer/codex/convertAgents.d.ts +7 -0
- package/dist/plugins/installer/codex/convertAgents.js +55 -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 +11 -0
- package/dist/plugins/installer/codex/convertMcp.js +85 -0
- package/dist/plugins/installer/codex/convertSkills.d.ts +12 -0
- package/dist/plugins/installer/codex/convertSkills.js +33 -0
- package/dist/plugins/installer/detectFormat.d.ts +2 -0
- package/dist/plugins/installer/detectFormat.js +6 -0
- package/dist/plugins/installer/install.d.ts +7 -0
- package/dist/plugins/installer/install.js +123 -0
- package/dist/plugins/installer/installFromArchive.d.ts +43 -0
- package/dist/plugins/installer/installFromArchive.js +134 -0
- package/dist/plugins/installer/installFromSource.d.ts +13 -0
- package/dist/plugins/installer/installFromSource.js +51 -0
- package/dist/plugins/installer/list.d.ts +9 -0
- package/dist/plugins/installer/list.js +30 -0
- package/dist/plugins/installer/loadPluginAgents.d.ts +3 -0
- package/dist/plugins/installer/loadPluginAgents.js +27 -0
- package/dist/plugins/installer/loadPluginMcp.d.ts +14 -0
- package/dist/plugins/installer/loadPluginMcp.js +107 -0
- package/dist/plugins/installer/parseSource.d.ts +20 -0
- package/dist/plugins/installer/parseSource.js +74 -0
- package/dist/plugins/installer/paths.d.ts +5 -0
- package/dist/plugins/installer/paths.js +25 -0
- package/dist/plugins/installer/pruneDisabled.d.ts +24 -0
- package/dist/plugins/installer/pruneDisabled.js +73 -0
- package/dist/plugins/installer/types.d.ts +52 -0
- package/dist/plugins/installer/types.js +28 -0
- package/dist/plugins/installer/uninstall.d.ts +2 -0
- package/dist/plugins/installer/uninstall.js +18 -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 +28 -0
- package/dist/plugins/installer/update.js +97 -0
- package/dist/plugins/loadPluginHooks.d.ts +54 -1
- package/dist/plugins/loadPluginHooks.js +83 -1
- package/dist/plugins/marketplaceManager.d.ts +15 -1
- package/dist/plugins/marketplaceManager.js +58 -4
- package/dist/plugins/pluginCommandHook.js +34 -14
- package/dist/plugins/pluginContent.d.ts +30 -0
- package/dist/plugins/pluginContent.js +83 -0
- package/dist/plugins/pluginInstaller.d.ts +34 -0
- package/dist/plugins/pluginInstaller.js +168 -35
- package/dist/plugins/schemas.js +41 -6
- package/dist/plugins/types.d.ts +10 -0
- package/dist/preset/index.d.ts +7 -1
- package/dist/preset/index.js +112 -6
- package/dist/product/types.d.ts +1 -1
- package/dist/prompt/composer.d.ts +31 -0
- package/dist/prompt/composer.js +95 -25
- package/dist/prompt/instruction-scanner.js +5 -3
- package/dist/prompt/section-loader.js +1 -0
- package/dist/prompt/sections/base.md +1 -0
- package/dist/prompt/sections/browser.md +10 -0
- package/dist/prompt/sections/coding.md +4 -0
- package/dist/prompt/sections/orchestration.md +8 -0
- package/dist/protocol/chat-session-manager.d.ts +15 -2
- package/dist/protocol/chat-session-manager.js +46 -0
- package/dist/protocol/chat-session.d.ts +82 -0
- package/dist/protocol/chat-session.js +121 -1
- package/dist/protocol/client.d.ts +27 -0
- package/dist/protocol/client.js +38 -0
- package/dist/protocol/redact.d.ts +50 -0
- package/dist/protocol/redact.js +71 -0
- package/dist/protocol/server.d.ts +153 -2
- package/dist/protocol/server.js +807 -39
- package/dist/protocol/tcp-transport.d.ts +45 -0
- package/dist/protocol/tcp-transport.js +74 -0
- package/dist/protocol/transport.js +3 -2
- package/dist/protocol/types.d.ts +69 -0
- package/dist/protocol/types.js +22 -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/remote/bridge.d.ts +11 -2
- package/dist/remote/bridge.js +61 -33
- package/dist/review/review-prompt.d.ts +28 -0
- package/dist/review/review-prompt.js +81 -0
- package/dist/run/ArtifactTracker.js +5 -4
- package/dist/run/EngineRunner.d.ts +27 -1
- package/dist/run/EngineRunner.js +46 -8
- package/dist/run/FileRunStore.js +40 -11
- package/dist/run/RunApprovalBackend.js +25 -5
- package/dist/run/RunLock.d.ts +28 -2
- package/dist/run/RunLock.js +49 -7
- package/dist/run/RunManager.d.ts +12 -0
- package/dist/run/RunManager.js +86 -3
- package/dist/run/RunQueue.d.ts +1 -0
- package/dist/run/RunQueue.js +8 -2
- package/dist/run/factory.d.ts +8 -1
- package/dist/run/factory.js +1 -0
- package/dist/run/index.d.ts +2 -2
- package/dist/run/index.js +1 -1
- package/dist/run/redirect-target.d.ts +7 -0
- package/dist/run/redirect-target.js +13 -0
- package/dist/run/types.js +1 -1
- package/dist/runtime/background-shell.d.ts +139 -0
- package/dist/runtime/background-shell.js +495 -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 +15 -34
- package/dist/runtime/spawn-common.d.ts +148 -0
- package/dist/runtime/spawn-common.js +303 -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/browser-open.d.ts +13 -0
- package/dist/services/browser-open.js +16 -0
- package/dist/services/diagnostics.d.ts +1 -2
- package/dist/services/diagnostics.js +12 -7
- package/dist/services/dream-consolidation.d.ts +57 -0
- package/dist/services/dream-consolidation.js +151 -0
- 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 -32
- package/dist/services/notifier.d.ts +8 -0
- package/dist/services/notifier.js +39 -25
- package/dist/services/oauth.js +5 -7
- package/dist/services/session-memory-sort.d.ts +8 -0
- package/dist/services/session-memory-sort.js +9 -0
- package/dist/services/session-memory.js +30 -19
- package/dist/session/file-history.d.ts +124 -1
- package/dist/session/file-history.js +232 -9
- package/dist/session/memory.d.ts +116 -2
- package/dist/session/memory.js +250 -28
- package/dist/session/session-manager.d.ts +63 -1
- package/dist/session/session-manager.js +186 -5
- package/dist/session/simple-diff.d.ts +23 -0
- package/dist/session/simple-diff.js +84 -0
- package/dist/session/transcript.d.ts +25 -1
- package/dist/session/transcript.js +35 -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 +42 -0
- package/dist/settings/disk-defaults.js +33 -0
- package/dist/settings/feature-flags.d.ts +64 -0
- package/dist/settings/feature-flags.js +61 -0
- package/dist/settings/manager.d.ts +101 -1
- package/dist/settings/manager.js +345 -15
- package/dist/settings/migrate-config.d.ts +45 -0
- package/dist/settings/migrate-config.js +125 -0
- package/dist/settings/personalization.d.ts +19 -0
- package/dist/settings/personalization.js +7 -0
- package/dist/settings/schema-export.d.ts +25 -0
- package/dist/settings/schema-export.js +38 -0
- package/dist/settings/schema.d.ts +1827 -396
- package/dist/settings/schema.js +361 -81
- 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/add-marketplace.d.ts +12 -0
- package/dist/tool-system/builtin/add-marketplace.js +76 -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 +23 -0
- package/dist/tool-system/builtin/agent-registry.js +16 -4
- package/dist/tool-system/builtin/agent.d.ts +50 -11
- package/dist/tool-system/builtin/agent.js +576 -73
- 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 +7 -3
- package/dist/tool-system/builtin/arena.js +6 -7
- 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 +98 -62
- 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/complete-goal.d.ts +25 -0
- package/dist/tool-system/builtin/complete-goal.js +45 -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 +105 -9
- 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 +94 -0
- package/dist/tool-system/builtin/edit.d.ts +2 -1
- package/dist/tool-system/builtin/edit.js +21 -12
- 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 +60 -0
- package/dist/tool-system/builtin/generate-image.js +374 -0
- package/dist/tool-system/builtin/generate-video.d.ts +55 -0
- package/dist/tool-system/builtin/generate-video.js +361 -0
- package/dist/tool-system/builtin/grep.js +6 -2
- 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 +24 -1
- package/dist/tool-system/builtin/index.js +310 -25
- 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.d.ts +2 -1
- package/dist/tool-system/builtin/notebook-edit.js +1 -1
- package/dist/tool-system/builtin/powershell.js +4 -1
- package/dist/tool-system/builtin/read.d.ts +2 -1
- package/dist/tool-system/builtin/read.js +11 -3
- 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 +12 -5
- package/dist/tool-system/builtin/task.js +4 -1
- package/dist/tool-system/builtin/tool-search.js +25 -7
- package/dist/tool-system/builtin/update-automation-memory.d.ts +24 -0
- package/dist/tool-system/builtin/update-automation-memory.js +60 -0
- 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/view-image.d.ts +18 -0
- package/dist/tool-system/builtin/view-image.js +96 -0
- package/dist/tool-system/builtin/web-fetch.js +12 -2
- package/dist/tool-system/builtin/web-search.d.ts +4 -1
- package/dist/tool-system/builtin/web-search.js +57 -10
- 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.d.ts +2 -1
- package/dist/tool-system/builtin/write.js +1 -1
- package/dist/tool-system/context.d.ts +166 -6
- package/dist/tool-system/executor.d.ts +3 -2
- package/dist/tool-system/executor.js +197 -119
- package/dist/tool-system/investigation-guard.d.ts +4 -1
- package/dist/tool-system/investigation-guard.js +20 -4
- package/dist/tool-system/mcp-manager.d.ts +78 -16
- package/dist/tool-system/mcp-manager.js +357 -28
- package/dist/tool-system/path-policy.d.ts +99 -0
- package/dist/tool-system/path-policy.js +578 -0
- package/dist/tool-system/permission.d.ts +36 -1
- package/dist/tool-system/permission.js +322 -117
- package/dist/tool-system/plan-mode-allowlist.d.ts +32 -0
- package/dist/tool-system/plan-mode-allowlist.js +65 -0
- package/dist/tool-system/registry.d.ts +1 -0
- package/dist/tool-system/registry.js +26 -1
- package/dist/tool-system/sandbox/index.d.ts +8 -0
- package/dist/tool-system/sandbox/index.js +7 -2
- package/dist/tool-system/validate-tool-metadata.d.ts +36 -0
- package/dist/tool-system/validate-tool-metadata.js +63 -0
- package/dist/tool-system/validation.d.ts +10 -0
- package/dist/tool-system/validation.js +16 -3
- package/dist/types.d.ts +301 -15
- 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 +40 -0
- package/dist/utils/exec.js +144 -0
- package/dist/utils/format.js +4 -2
- package/dist/utils/json.d.ts +12 -0
- package/dist/utils/json.js +92 -0
- package/dist/utils/lockfile.js +9 -2
- package/dist/utils/theme.d.ts +7 -1
- package/dist/utils/theme.js +5 -3
- package/dist/utils/toolDisplay.js +0 -1
- package/package.json +14 -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
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* Supports onStream for real-time output passthrough.
|
|
8
8
|
*/
|
|
9
9
|
import { asyncAgentRegistry, MAX_BACKGROUND_AGENTS } from "./agent-registry.js";
|
|
10
|
+
import { ensureAgentHeartbeat } from "./agent-heartbeat.js";
|
|
11
|
+
import { writeAgentOutputFile } from "./agent-output-file.js";
|
|
10
12
|
import { createTranscriptTranslator } from "./agent-transcript-translator.js";
|
|
11
13
|
import { notificationQueue } from "./agent-notifications.js";
|
|
12
14
|
import { nanoid } from "nanoid";
|
|
@@ -32,26 +34,126 @@ function safeEmit(sink, event) {
|
|
|
32
34
|
});
|
|
33
35
|
}
|
|
34
36
|
}
|
|
37
|
+
/** Preferred default role when the caller omits agent_type but roles exist. */
|
|
38
|
+
const DEFAULT_AGENT_TYPE = "general-purpose";
|
|
35
39
|
/**
|
|
36
40
|
* Resolve an `agent_type` against the role registry into spawn overrides.
|
|
37
|
-
*
|
|
38
|
-
*
|
|
41
|
+
*
|
|
42
|
+
* - Omitted type + non-empty registry → fall back to a configured role
|
|
43
|
+
* ("general-purpose" if present, else the first available) instead of
|
|
44
|
+
* running a nameless ephemeral agent. Relaxed from the earlier "throw when
|
|
45
|
+
* agent_type omitted": the model habitually omits it, and a hard error just
|
|
46
|
+
* turned every spawn into a failure. Falling back to a real role keeps the
|
|
47
|
+
* tool-allowlist / system-prompt benefits without breaking the call.
|
|
48
|
+
* - Omitted type + empty registry → empty overrides (true ephemeral; nothing
|
|
49
|
+
* to fall back to).
|
|
50
|
+
* - Unknown explicit type → throw, so the LLM gets a clear correction rather
|
|
51
|
+
* than silently running a generic agent.
|
|
39
52
|
*/
|
|
40
53
|
export function resolveAgentTypeOverrides(agentType, registry) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
54
|
+
const available = registry?.list().map((d) => d.name) ?? [];
|
|
55
|
+
let resolvedType = agentType;
|
|
56
|
+
if (!resolvedType) {
|
|
57
|
+
if (available.length === 0)
|
|
58
|
+
return {};
|
|
59
|
+
resolvedType = available.includes(DEFAULT_AGENT_TYPE) ? DEFAULT_AGENT_TYPE : available[0];
|
|
60
|
+
}
|
|
61
|
+
const def = registry?.get(resolvedType);
|
|
44
62
|
if (!def) {
|
|
45
|
-
const
|
|
46
|
-
throw new Error(`unknown agent_type '${
|
|
63
|
+
const list = available.join(", ") || "(none defined)";
|
|
64
|
+
throw new Error(`unknown agent_type '${resolvedType}'. Available: ${list}`);
|
|
47
65
|
}
|
|
48
66
|
return {
|
|
67
|
+
resolvedType,
|
|
49
68
|
model: def.model,
|
|
50
69
|
maxTurns: def.maxTurns,
|
|
51
70
|
toolAllowlist: def.tools,
|
|
71
|
+
skillAllowlist: namespacePluginSkills(def.skills, def.source, def.pluginName),
|
|
52
72
|
appendSystemPrompt: def.systemPrompt,
|
|
53
73
|
};
|
|
54
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* A plugin-bundled agent's frontmatter lists its own skills by BARE name
|
|
77
|
+
* (`skills: director-skill`), matching Claude Code's convention where a
|
|
78
|
+
* plugin agent references same-plugin skills unqualified. But the skill
|
|
79
|
+
* scanner registers plugin skills under their NAMESPACED name
|
|
80
|
+
* (`mimi-video:director-skill`), and the allowlist filter is an exact match —
|
|
81
|
+
* so a bare entry never matches and the sub-agent reports the skill "not
|
|
82
|
+
* found", forcing it to fall back to reading SKILL.md by hand.
|
|
83
|
+
*
|
|
84
|
+
* Bridge the two conventions: for a plugin-source agent, rewrite each bare
|
|
85
|
+
* skill name to `<pluginName>:<skill>`. Names that already carry a namespace
|
|
86
|
+
* (contain ":") are left untouched, so an agent may still reference another
|
|
87
|
+
* plugin's skill explicitly. undefined (inherit full pool) and [] (no skills)
|
|
88
|
+
* pass through unchanged. Non-plugin agents are never rewritten.
|
|
89
|
+
*/
|
|
90
|
+
export function namespacePluginSkills(skills, source, pluginName) {
|
|
91
|
+
if (skills === undefined)
|
|
92
|
+
return undefined;
|
|
93
|
+
if (source !== "plugin" || !pluginName)
|
|
94
|
+
return skills;
|
|
95
|
+
return skills.map((s) => (s.includes(":") ? s : `${pluginName}:${s}`));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Render the "Available agent types" block injected into the Agent tool's
|
|
99
|
+
* description, listing the roles defined in .code-shell/agents/*.md so the
|
|
100
|
+
* model knows it can pass `agent_type` instead of hand-rolling an ad-hoc
|
|
101
|
+
* agent. Without this the model never sees the registry (it lives per-engine,
|
|
102
|
+
* not in the static tool def) and falls back to nameless ephemeral agents —
|
|
103
|
+
* see the Core A/B/C incident. Returns "" when no roles are defined, so the
|
|
104
|
+
* base description is left untouched.
|
|
105
|
+
*/
|
|
106
|
+
export function buildAgentTypesBlock(registry) {
|
|
107
|
+
const defs = registry?.list() ?? [];
|
|
108
|
+
if (defs.length === 0)
|
|
109
|
+
return "";
|
|
110
|
+
const lines = defs.map((d) => {
|
|
111
|
+
const tools = d.tools && d.tools.length > 0 ? d.tools.join(", ") : "all parent tools";
|
|
112
|
+
// Only surface skills when the role restricts them — most roles inherit
|
|
113
|
+
// the full pool and listing "all skills" everywhere is noise.
|
|
114
|
+
const skillsNote = d.skills !== undefined
|
|
115
|
+
? `; skills: ${d.skills.length > 0 ? d.skills.join(", ") : "none"}`
|
|
116
|
+
: "";
|
|
117
|
+
return `- ${d.name}: ${d.description} (tools: ${tools}${skillsNote})`;
|
|
118
|
+
});
|
|
119
|
+
return [
|
|
120
|
+
"",
|
|
121
|
+
"Available agent types (pass one as `agent_type` to reuse its role, tool allowlist, and turn cap):",
|
|
122
|
+
...lines,
|
|
123
|
+
"Pass the closest matching agent_type (e.g. read-only investigation → researcher/explorer, planning → planner, full multi-step work → general-purpose). If you omit agent_type it defaults to general-purpose (or the first available role) — passing an explicit one is preferred.",
|
|
124
|
+
].join("\n");
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Produce an Agent tool definition whose description ends with the live
|
|
128
|
+
* available-agent-types listing. Pure: takes the registry, returns a new def
|
|
129
|
+
* (the base `agentToolDef` const is never mutated). When no roles exist the
|
|
130
|
+
* base def is returned unchanged.
|
|
131
|
+
*/
|
|
132
|
+
export function agentToolDefWithTypes(registry) {
|
|
133
|
+
const block = buildAgentTypesBlock(registry);
|
|
134
|
+
const names = registry?.list().map((d) => d.name) ?? [];
|
|
135
|
+
if (!block && names.length === 0)
|
|
136
|
+
return agentToolDef;
|
|
137
|
+
// Constrain agent_type to the loaded kind names so the model can't invent a
|
|
138
|
+
// role that doesn't exist (resolveAgentTypeOverrides would throw, wasting a
|
|
139
|
+
// turn). A free string still slipped through before; an enum surfaces the
|
|
140
|
+
// valid set directly in the schema the model sees. We rebuild the property
|
|
141
|
+
// (not mutate the shared const) so each engine's registry stays isolated.
|
|
142
|
+
const baseProps = agentToolDef.inputSchema.properties ?? {};
|
|
143
|
+
const baseAgentType = baseProps.agent_type ?? {};
|
|
144
|
+
const inputSchema = {
|
|
145
|
+
...agentToolDef.inputSchema,
|
|
146
|
+
properties: {
|
|
147
|
+
...baseProps,
|
|
148
|
+
agent_type: names.length > 0 ? { ...baseAgentType, enum: names } : baseAgentType,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
return {
|
|
152
|
+
...agentToolDef,
|
|
153
|
+
description: block ? agentToolDef.description + "\n" + block : agentToolDef.description,
|
|
154
|
+
inputSchema,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
55
157
|
/**
|
|
56
158
|
* Emit a sub-agent lifecycle event via the existing `notification` hook,
|
|
57
159
|
* tagged with a `kind`. No-op when hooks are absent. Fire-and-forget: emit is
|
|
@@ -61,41 +163,28 @@ export function resolveAgentTypeOverrides(agentType, registry) {
|
|
|
61
163
|
export function emitSubAgentHook(hooks, kind, payload) {
|
|
62
164
|
void hooks?.emit("notification", { kind, ...payload });
|
|
63
165
|
}
|
|
64
|
-
/** Default per-sub-agent wall-clock timeout (5 minutes). */
|
|
65
|
-
export const DEFAULT_SUBAGENT_TIMEOUT_MS = 5 * 60_000;
|
|
66
|
-
/**
|
|
67
|
-
* Run `work()` with a timeout. On expiry, calls `onTimeout` (to abort the
|
|
68
|
-
* child) and rejects with a timeout error. The child's own abort handling
|
|
69
|
-
* unwinds its resources.
|
|
70
|
-
*/
|
|
71
|
-
export async function runWithTimeout(work, timeoutMs, onTimeout) {
|
|
72
|
-
let timer;
|
|
73
|
-
const timeout = new Promise((_, reject) => {
|
|
74
|
-
timer = setTimeout(() => {
|
|
75
|
-
onTimeout();
|
|
76
|
-
reject(new Error(`Sub-agent timed out after ${timeoutMs}ms`));
|
|
77
|
-
}, timeoutMs);
|
|
78
|
-
});
|
|
79
|
-
try {
|
|
80
|
-
return await Promise.race([work(), timeout]);
|
|
81
|
-
}
|
|
82
|
-
finally {
|
|
83
|
-
if (timer)
|
|
84
|
-
clearTimeout(timer);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
166
|
export const agentToolDef = {
|
|
88
167
|
name: "Agent",
|
|
89
|
-
description: "Launch a sub-agent to handle a
|
|
90
|
-
"The sub-agent
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
168
|
+
description: "Launch a sub-agent to handle a task in its own clean, isolated context. " +
|
|
169
|
+
"The sub-agent runs independently with access to the same tools, and its final " +
|
|
170
|
+
"report is the ONLY thing returned to you — it is stateless, so you cannot send " +
|
|
171
|
+
"follow-up messages. Write a complete, self-contained task description.\n\n" +
|
|
172
|
+
"PRIMARY USE — context isolation: when a task needs to read many files or run a " +
|
|
173
|
+
"broad investigation but you only need the conclusion, delegate it. The sub-agent " +
|
|
174
|
+
"absorbs the noisy intermediate output in its own context; you keep the answer, not " +
|
|
175
|
+
"the file dumps. This protects your main context from being flooded.\n\n" +
|
|
176
|
+
"DON'T use this for a quick lookup where you know the file/symbol and expect a few " +
|
|
177
|
+
"matches — use Read/Grep/Glob directly instead; spawning an agent wastes a turn.\n\n" +
|
|
178
|
+
"Parallel fan-out is the EXCEPTION: only launch several agents in one message when " +
|
|
179
|
+
"the work truly splits into independent pieces with no shared state. Prefer one " +
|
|
180
|
+
"well-scoped delegation over a swarm.\n\n" +
|
|
181
|
+
"You can optionally run an agent in the background using the run_in_background " +
|
|
182
|
+
"parameter. When it runs in the background, you will be automatically notified when " +
|
|
183
|
+
"it completes — do NOT sleep, poll, or proactively check on its progress. Continue " +
|
|
184
|
+
"with other work or respond to the user instead. " +
|
|
185
|
+
"Even a synchronous agent that runs longer than ~2 minutes is automatically moved to " +
|
|
186
|
+
"the background and notifies you on completion the same way — so a long delegation never " +
|
|
187
|
+
"stalls you; just continue or end your turn when told it has moved to the background.",
|
|
99
188
|
inputSchema: {
|
|
100
189
|
type: "object",
|
|
101
190
|
properties: {
|
|
@@ -109,7 +198,10 @@ export const agentToolDef = {
|
|
|
109
198
|
type: "string",
|
|
110
199
|
description: "Optional reusable role defined in .code-shell/agents/*.md (e.g. 'researcher'). " +
|
|
111
200
|
"Loads that role's model, tool allowlist, turn cap, and system prompt. " +
|
|
112
|
-
"
|
|
201
|
+
"Disabled roles are not available. If you pass an unknown role you'll get " +
|
|
202
|
+
"an error listing the currently available roles. " +
|
|
203
|
+
"If omitted, defaults to a configured role (general-purpose or the first available); " +
|
|
204
|
+
"with no roles configured it runs an ephemeral agent. Passing an explicit role is preferred.",
|
|
113
205
|
},
|
|
114
206
|
description: {
|
|
115
207
|
type: "string",
|
|
@@ -135,6 +227,20 @@ export const agentToolDef = {
|
|
|
135
227
|
required: ["description", "prompt"],
|
|
136
228
|
},
|
|
137
229
|
};
|
|
230
|
+
/**
|
|
231
|
+
* Auto-background threshold (ms): a synchronous agent still running after this
|
|
232
|
+
* long is detached into the background (not killed) so the main turn isn't
|
|
233
|
+
* blocked for up to the 30min tool cap (TODO 4.1). Default 120s, matching
|
|
234
|
+
* Claude Code; overridable via CODE_SHELL_AGENT_BG_MS for tests. The agent
|
|
235
|
+
* keeps running on the same signal — we just stop awaiting it inline and let it
|
|
236
|
+
* notify on finish. Shared by the first-spawn (agentTool) and continuation
|
|
237
|
+
* (agentSendInputTool) paths.
|
|
238
|
+
*/
|
|
239
|
+
function resolveAutoBgMs() {
|
|
240
|
+
const raw = process.env.CODE_SHELL_AGENT_BG_MS;
|
|
241
|
+
const n = raw !== undefined ? Number(raw) : NaN;
|
|
242
|
+
return Number.isFinite(n) && n >= 0 ? n : 120_000;
|
|
243
|
+
}
|
|
138
244
|
/**
|
|
139
245
|
* Build and run a sub-agent. Returns the produced text or throws.
|
|
140
246
|
* Used by both the synchronous and background paths.
|
|
@@ -155,17 +261,17 @@ uiStream,
|
|
|
155
261
|
* sub-agents.
|
|
156
262
|
*/
|
|
157
263
|
streamOverride) {
|
|
158
|
-
const { agentId, name, description } = opts;
|
|
264
|
+
const { agentId, name, description, agentType } = opts;
|
|
159
265
|
const startEndSink = uiStream ?? spawner.parentStream;
|
|
160
|
-
safeEmit(startEndSink, { type: "agent_start", agentId, name, description });
|
|
266
|
+
safeEmit(startEndSink, { type: "agent_start", agentId, name, description, agentType });
|
|
161
267
|
emitSubAgentHook(opts.hooks, "subagent_start", { agentId, description });
|
|
162
268
|
// `resetPlanMode` / `restorePlanMode` operated on a module-level singleton
|
|
163
269
|
// that no longer exists. The child Engine is a fresh instance; plan-mode
|
|
164
270
|
// isolation between parent and child is enforced via separate Engine
|
|
165
271
|
// instances (finalized in T6).
|
|
166
|
-
const text = await spawner.spawn({ ...opts, streamOverride });
|
|
272
|
+
const { text } = await spawner.spawn({ ...opts, streamOverride });
|
|
167
273
|
const finalText = text || `Agent completed but produced no text output.`;
|
|
168
|
-
safeEmit(startEndSink, { type: "agent_end", agentId, name, description, text: finalText });
|
|
274
|
+
safeEmit(startEndSink, { type: "agent_end", agentId, name, description, text: finalText, agentType });
|
|
169
275
|
emitSubAgentHook(opts.hooks, "subagent_finish", { agentId, description, text: finalText });
|
|
170
276
|
return finalText;
|
|
171
277
|
}
|
|
@@ -173,7 +279,9 @@ export async function agentTool(args, ctx) {
|
|
|
173
279
|
const prompt = args.prompt;
|
|
174
280
|
const description = args.description || "sub-agent";
|
|
175
281
|
const rawName = args.name?.trim();
|
|
176
|
-
|
|
282
|
+
// Resolved below: when the model omits `name`, fall back to the resolved
|
|
283
|
+
// kind name so the dock shows a meaningful label instead of bare "Agent".
|
|
284
|
+
let name = rawName && rawName.length > 0 ? rawName : undefined;
|
|
177
285
|
if (!prompt)
|
|
178
286
|
return "Error: prompt is required";
|
|
179
287
|
if (!ctx?.subAgentSpawner) {
|
|
@@ -200,10 +308,18 @@ export async function agentTool(args, ctx) {
|
|
|
200
308
|
catch (err) {
|
|
201
309
|
return `Error: ${err.message}`;
|
|
202
310
|
}
|
|
311
|
+
// Dock label fallback: an omitted `name` defaults to the resolved kind
|
|
312
|
+
// (e.g. "explorer") so background agents are identifiable in the dock even
|
|
313
|
+
// when the model didn't pass an explicit label. True ephemeral agents
|
|
314
|
+
// (empty registry, no resolvedType) keep name undefined → dock shows "Agent".
|
|
315
|
+
if (name === undefined && overrides.resolvedType) {
|
|
316
|
+
name = overrides.resolvedType;
|
|
317
|
+
}
|
|
203
318
|
const maxTurns = args.max_turns || overrides.maxTurns || 15;
|
|
204
319
|
const runInBackground = args.run_in_background === true;
|
|
205
320
|
const agentId = nanoid(8);
|
|
206
321
|
const parentStream = spawner.parentStream;
|
|
322
|
+
const autoBgMs = resolveAutoBgMs();
|
|
207
323
|
// ─── Background path ───────────────────────────────────────────
|
|
208
324
|
// Register the agent, kick off execution detached from the current turn,
|
|
209
325
|
// return immediately with the agent_id. Parent abort does NOT cascade
|
|
@@ -218,11 +334,17 @@ export async function agentTool(args, ctx) {
|
|
|
218
334
|
asyncAgentRegistry.register({
|
|
219
335
|
agentId,
|
|
220
336
|
name,
|
|
337
|
+
agentType: overrides.resolvedType,
|
|
221
338
|
description,
|
|
339
|
+
// Tag with the spawning session so the parent Engine.run waits only on
|
|
340
|
+
// its own background agents (hasRunningForSession).
|
|
341
|
+
sessionId: ctx?.sessionId,
|
|
222
342
|
status: "running",
|
|
223
343
|
startedAt: Date.now(),
|
|
224
344
|
abort: () => controller.abort(),
|
|
225
345
|
});
|
|
346
|
+
// B: liveness heartbeat for this explicit background agent too.
|
|
347
|
+
ensureAgentHeartbeat();
|
|
226
348
|
// Translate this background agent's per-event stream into ChatEntry-
|
|
227
349
|
// shaped rows inside its own transcript. The dock detail view reuses
|
|
228
350
|
// App.renderEntry which only understands ChatEntry types — storing
|
|
@@ -241,17 +363,29 @@ export async function agentTool(args, ctx) {
|
|
|
241
363
|
agentId,
|
|
242
364
|
name,
|
|
243
365
|
description,
|
|
366
|
+
agentType: overrides.resolvedType,
|
|
244
367
|
prompt,
|
|
245
368
|
maxTurns,
|
|
246
369
|
model: overrides.model,
|
|
247
370
|
toolAllowlist: overrides.toolAllowlist,
|
|
371
|
+
skillAllowlist: overrides.skillAllowlist,
|
|
248
372
|
appendSystemPrompt: overrides.appendSystemPrompt,
|
|
373
|
+
readOnlySession: overrides.resolvedType === "researcher" || overrides.resolvedType === "explorer",
|
|
249
374
|
hooks: ctx?.hooks,
|
|
250
375
|
signal: controller.signal,
|
|
251
376
|
}, parentStream, // uiStream: agent_start/end → main feed
|
|
252
377
|
transcriptSink)
|
|
253
378
|
.then((text) => {
|
|
254
379
|
asyncAgentRegistry.markCompleted(agentId);
|
|
380
|
+
// External-readable copy of the result (tail / cross-session history).
|
|
381
|
+
// Best-effort; never blocks the completion path.
|
|
382
|
+
void writeAgentOutputFile(agentId, {
|
|
383
|
+
status: "completed",
|
|
384
|
+
body: text,
|
|
385
|
+
description,
|
|
386
|
+
name,
|
|
387
|
+
onError: (m, meta) => logger.warn(m, meta),
|
|
388
|
+
});
|
|
255
389
|
// B2 / Gate 1: attribute completion to the session that spawned
|
|
256
390
|
// this agent so concurrent sessions don't drain each other's
|
|
257
391
|
// notifications. Engine.run() always populates ctx.sessionId; the
|
|
@@ -288,6 +422,19 @@ export async function agentTool(args, ctx) {
|
|
|
288
422
|
});
|
|
289
423
|
})
|
|
290
424
|
.catch((err) => {
|
|
425
|
+
// runSubAgent emits its terminal agent_end only on the RESOLVE path
|
|
426
|
+
// (line ~344). On fail/cancel it threw before that, so the main-feed
|
|
427
|
+
// card started by agent_start is still 'working' — seal it here or the
|
|
428
|
+
// TUI spins forever. Emit once for BOTH branches (mirrors the sync→bg
|
|
429
|
+
// detach path ~885), then branch on abort for the notification only.
|
|
430
|
+
safeEmit(parentStream, {
|
|
431
|
+
type: "agent_end",
|
|
432
|
+
agentId,
|
|
433
|
+
name,
|
|
434
|
+
description,
|
|
435
|
+
error: err.message,
|
|
436
|
+
agentType: overrides.resolvedType,
|
|
437
|
+
});
|
|
291
438
|
if (controller.signal.aborted) {
|
|
292
439
|
// User-initiated cancel: mark status but do NOT enqueue —
|
|
293
440
|
// the main agent doesn't need a follow-up turn. Dock still
|
|
@@ -302,6 +449,13 @@ export async function agentTool(args, ctx) {
|
|
|
302
449
|
return;
|
|
303
450
|
}
|
|
304
451
|
asyncAgentRegistry.markFailed(agentId);
|
|
452
|
+
void writeAgentOutputFile(agentId, {
|
|
453
|
+
status: "failed",
|
|
454
|
+
body: err.message,
|
|
455
|
+
description,
|
|
456
|
+
name,
|
|
457
|
+
onError: (m, meta) => logger.warn(m, meta),
|
|
458
|
+
});
|
|
305
459
|
if (ctx?.sessionId) {
|
|
306
460
|
notificationQueue.enqueue({
|
|
307
461
|
agentId,
|
|
@@ -337,63 +491,293 @@ export async function agentTool(args, ctx) {
|
|
|
337
491
|
`If you need to stop it: AgentCancel(agent_id="${agentId}").`,
|
|
338
492
|
].join("\n");
|
|
339
493
|
}
|
|
340
|
-
// ─── Synchronous path
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
494
|
+
// ─── Synchronous path (with auto-background handoff) ────────────
|
|
495
|
+
return runSyncSubAgent({
|
|
496
|
+
spawner,
|
|
497
|
+
agentId,
|
|
498
|
+
name,
|
|
499
|
+
description,
|
|
500
|
+
prompt,
|
|
501
|
+
maxTurns,
|
|
502
|
+
overrides,
|
|
503
|
+
autoBgMs,
|
|
504
|
+
parentSignal,
|
|
505
|
+
parentStream,
|
|
506
|
+
ctx,
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Record a finished SYNCHRONOUS sub-agent in the registry so AgentSendInput's
|
|
511
|
+
* fast path can later find it — its agentType (for role-scope re-resolution on
|
|
512
|
+
* resume) and dock label. Background agents already register on spawn; sync
|
|
513
|
+
* agents historically didn't (they returned text and were forgotten), which
|
|
514
|
+
* left continuation unable to recover the role's tool/skill constraints.
|
|
515
|
+
*
|
|
516
|
+
* This is a SILENT entry: status already `completed`, no notification enqueued,
|
|
517
|
+
* so it doesn't flash a dock card or wake the parent. It's pure lookup state.
|
|
518
|
+
* Keyed by agentId === childSid, so it agrees with the on-disk session that the
|
|
519
|
+
* cross-restart disk-probe finds. Skipped when the agent moved to the
|
|
520
|
+
* background (that path registers its own live entry) or was aborted.
|
|
521
|
+
*/
|
|
522
|
+
function recordCompletedSyncAgent(opts) {
|
|
523
|
+
// Don't clobber a live/handed-off entry of the same id.
|
|
524
|
+
if (asyncAgentRegistry.get(opts.agentId))
|
|
525
|
+
return;
|
|
526
|
+
const now = Date.now();
|
|
527
|
+
asyncAgentRegistry.register({
|
|
528
|
+
agentId: opts.agentId,
|
|
529
|
+
name: opts.name,
|
|
530
|
+
agentType: opts.agentType,
|
|
531
|
+
description: opts.description,
|
|
532
|
+
sessionId: opts.parentSessionId,
|
|
533
|
+
// agent_id === childSid: the resumable session lives at sessions/<agentId>/.
|
|
534
|
+
childSessionId: opts.agentId,
|
|
535
|
+
status: "completed",
|
|
536
|
+
startedAt: now,
|
|
537
|
+
finishedAt: now,
|
|
538
|
+
// No fade window / no notification: this entry exists only so
|
|
539
|
+
// AgentSendInput can resolve the role on continuation, not to render.
|
|
540
|
+
abort: () => { },
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* The synchronous sub-agent run path, shared by `agentTool` (first spawn) and
|
|
545
|
+
* `agentSendInputTool` (resume/continuation). Races the run against the
|
|
546
|
+
* auto-background timer and hands off to the background registry if it runs
|
|
547
|
+
* long. `resumeSessionId`, when set, resumes an existing child session
|
|
548
|
+
* (transcript replay) instead of cold-starting.
|
|
549
|
+
*
|
|
550
|
+
* No wall-clock timeout on the lifecycle — matches Claude Code / Codex, where a
|
|
551
|
+
* sub-agent is bounded by maxTurns + per-tool timeouts + parent/user abort, NOT
|
|
552
|
+
* a global countdown that kills legitimate heavy work mid-task. (The old
|
|
553
|
+
* runWithTimeout(5min) murdered slow agents AND raced the completion path into
|
|
554
|
+
* a double agent_end; dropping it removed the race — fix
|
|
555
|
+
* subagent-timeout-double-agent-end.)
|
|
556
|
+
*/
|
|
557
|
+
async function runSyncSubAgent(args) {
|
|
558
|
+
const { spawner, agentId, name, description, prompt, maxTurns, overrides, autoBgMs, parentSignal, parentStream, ctx, resumeSessionId, } = args;
|
|
346
559
|
const syncController = new AbortController();
|
|
347
560
|
const onParentAbort = () => syncController.abort();
|
|
348
561
|
parentSignal?.addEventListener("abort", onParentAbort, { once: true });
|
|
562
|
+
// Per-call flag (NOT module-level): true once we've detached the agent into
|
|
563
|
+
// the background, so the finally block leaves the parent-abort listener with
|
|
564
|
+
// the background handlers instead of removing it.
|
|
565
|
+
let handedOff = false;
|
|
566
|
+
// Run the agent, but don't necessarily await it to completion: race the run
|
|
567
|
+
// against the auto-background timer. The run promise is shared between the
|
|
568
|
+
// inline await and (if we hand off) the background completion handlers, so
|
|
569
|
+
// the agent is never started twice and never killed by the handoff.
|
|
570
|
+
const runPromise = runSubAgent(spawner, {
|
|
571
|
+
agentId,
|
|
572
|
+
name,
|
|
573
|
+
description,
|
|
574
|
+
agentType: overrides.resolvedType,
|
|
575
|
+
prompt,
|
|
576
|
+
maxTurns,
|
|
577
|
+
model: overrides.model,
|
|
578
|
+
toolAllowlist: overrides.toolAllowlist,
|
|
579
|
+
skillAllowlist: overrides.skillAllowlist,
|
|
580
|
+
appendSystemPrompt: overrides.appendSystemPrompt,
|
|
581
|
+
readOnlySession: overrides.resolvedType === "researcher" || overrides.resolvedType === "explorer",
|
|
582
|
+
resumeSessionId,
|
|
583
|
+
hooks: ctx?.hooks,
|
|
584
|
+
signal: syncController.signal,
|
|
585
|
+
});
|
|
586
|
+
// Sentinel that the timer resolves with, so we can tell "agent finished" from
|
|
587
|
+
// "threshold elapsed" without rejecting either side.
|
|
588
|
+
const BG_HANDOFF = Symbol("bg-handoff");
|
|
589
|
+
let bgTimer;
|
|
590
|
+
const timerPromise = autoBgMs > 0 && autoBgMs !== Infinity
|
|
591
|
+
? new Promise((resolve) => {
|
|
592
|
+
bgTimer = setTimeout(() => resolve(BG_HANDOFF), autoBgMs);
|
|
593
|
+
})
|
|
594
|
+
: null;
|
|
349
595
|
try {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
596
|
+
// If there's no timer (threshold 0/∞ disabled), just await as before.
|
|
597
|
+
const winner = timerPromise
|
|
598
|
+
? await Promise.race([runPromise.then((t) => ({ ok: true, text: t })).catch((e) => ({ ok: false, err: e })), timerPromise])
|
|
599
|
+
: { ok: true, text: await runPromise };
|
|
600
|
+
if (winner === BG_HANDOFF) {
|
|
601
|
+
// Threshold elapsed while the agent is STILL running. Detach it into the
|
|
602
|
+
// background registry (it keeps executing on syncController.signal — we
|
|
603
|
+
// do NOT abort it) and wire its eventual completion into the
|
|
604
|
+
// notification queue, exactly like an explicit run_in_background agent.
|
|
605
|
+
handedOff = true;
|
|
606
|
+
handoffToBackground(runPromise, syncController, {
|
|
607
|
+
agentId,
|
|
608
|
+
name,
|
|
609
|
+
description,
|
|
610
|
+
agentType: overrides.resolvedType,
|
|
611
|
+
sessionId: ctx?.sessionId,
|
|
612
|
+
hooks: ctx?.hooks,
|
|
613
|
+
parentSignal,
|
|
614
|
+
onParentAbort,
|
|
615
|
+
// The agent_start fired in runSubAgent put the UI card into 'working'.
|
|
616
|
+
// runSubAgent's own agent_end (line ~299) only fires if spawn() RESOLVES;
|
|
617
|
+
// on failure/cancel it throws before that, so the card would hang
|
|
618
|
+
// 'working' forever. Hand the UI sink to the background completion
|
|
619
|
+
// handlers so they emit the terminal agent_end the card needs.
|
|
620
|
+
uiStream: parentStream,
|
|
621
|
+
});
|
|
622
|
+
return [
|
|
623
|
+
`Task is taking a while (>${Math.round(autoBgMs / 1000)}s) — moved it to the background so I'm not blocked.`,
|
|
624
|
+
`agent_id: ${agentId} (internal — do not show to user)`,
|
|
625
|
+
`description: ${description}`,
|
|
626
|
+
``,
|
|
627
|
+
`It is still running and you will be notified automatically when it completes.`,
|
|
628
|
+
`Briefly tell the user it's running in the background and either continue with other work or end your response. Do not sleep or poll.`,
|
|
629
|
+
`If you need to stop it: AgentCancel(agent_id="${agentId}").`,
|
|
630
|
+
].join("\n");
|
|
631
|
+
}
|
|
632
|
+
// Agent finished within the threshold. Record a silent completed entry so
|
|
633
|
+
// AgentSendInput can continue this sub-agent (recover its role scope). Both
|
|
634
|
+
// a first spawn and a resume land here; re-registering a resumed id is a
|
|
635
|
+
// no-op (recordCompletedSyncAgent skips when an entry already exists).
|
|
636
|
+
if (winner.ok) {
|
|
637
|
+
recordCompletedSyncAgent({
|
|
638
|
+
agentId,
|
|
639
|
+
name,
|
|
640
|
+
agentType: overrides.resolvedType,
|
|
641
|
+
description,
|
|
642
|
+
parentSessionId: ctx?.sessionId,
|
|
643
|
+
});
|
|
644
|
+
return winner.text;
|
|
645
|
+
}
|
|
646
|
+
throw winner.err;
|
|
362
647
|
}
|
|
363
648
|
catch (err) {
|
|
364
649
|
emitSubAgentHook(ctx?.hooks, "subagent_error", { agentId, description, error: err.message });
|
|
365
|
-
safeEmit(parentStream, { type: "agent_end", agentId, name, description, error: err.message });
|
|
650
|
+
safeEmit(parentStream, { type: "agent_end", agentId, name, description, error: err.message, agentType: overrides.resolvedType });
|
|
366
651
|
if (parentSignal?.aborted)
|
|
367
652
|
return "Agent was aborted.";
|
|
368
653
|
return `Agent error: ${err.message}`;
|
|
369
654
|
}
|
|
370
655
|
finally {
|
|
371
|
-
|
|
656
|
+
if (bgTimer)
|
|
657
|
+
clearTimeout(bgTimer);
|
|
658
|
+
// Only detach the parent-abort listener when we awaited to completion.
|
|
659
|
+
// On handoff, handoffToBackground already detached it (the agent is still
|
|
660
|
+
// running and now follows the background contract, not parent-turn abort).
|
|
661
|
+
if (!handedOff)
|
|
662
|
+
parentSignal?.removeEventListener("abort", onParentAbort);
|
|
372
663
|
}
|
|
373
664
|
}
|
|
665
|
+
/**
|
|
666
|
+
* Detach a still-running synchronous sub-agent into the background registry
|
|
667
|
+
* (TODO 4.1). The agent KEEPS running on its existing signal — we attach
|
|
668
|
+
* completion handlers that mirror the explicit run_in_background path so its
|
|
669
|
+
* result/error arrives via the notification queue and the registry shows it as
|
|
670
|
+
* a running (then completed/failed) background agent. Never aborts the agent.
|
|
671
|
+
*/
|
|
672
|
+
function handoffToBackground(runPromise, controller, meta) {
|
|
673
|
+
const { agentId, name, description, agentType, sessionId, hooks, uiStream } = meta;
|
|
674
|
+
// The agent outlives the spawning turn now, so parent-turn abort must NOT
|
|
675
|
+
// cascade to it (same contract as an explicit background agent). Detach the
|
|
676
|
+
// parent listener; cancellation from here on goes through AgentCancel.
|
|
677
|
+
meta.parentSignal?.removeEventListener("abort", meta.onParentAbort);
|
|
678
|
+
asyncAgentRegistry.register({
|
|
679
|
+
agentId,
|
|
680
|
+
name,
|
|
681
|
+
agentType,
|
|
682
|
+
description,
|
|
683
|
+
sessionId,
|
|
684
|
+
status: "running",
|
|
685
|
+
startedAt: Date.now(),
|
|
686
|
+
abort: () => controller.abort(),
|
|
687
|
+
});
|
|
688
|
+
// Tell the UI this agent is now running in the BACKGROUND (still working,
|
|
689
|
+
// just detached from the parent turn). Without this the card's only signals
|
|
690
|
+
// are agent_start (looks foreground) and the eventual agent_end — so during
|
|
691
|
+
// the handoff→completion gap turn_complete's done-sweep marks it done and the
|
|
692
|
+
// card collapses / shows no "running". (A: background-agent-visibility)
|
|
693
|
+
safeEmit(uiStream, { type: "agent_backgrounded", agentId, name, description, agentType });
|
|
694
|
+
// B: keep a liveness heartbeat going while this (and any sibling) background
|
|
695
|
+
// agent runs, so the UI shows "alive" even through long LLM-request silence.
|
|
696
|
+
// Idempotent; self-stops when no agent remains.
|
|
697
|
+
ensureAgentHeartbeat();
|
|
698
|
+
runPromise
|
|
699
|
+
.then((text) => {
|
|
700
|
+
asyncAgentRegistry.markCompleted(agentId);
|
|
701
|
+
void writeAgentOutputFile(agentId, {
|
|
702
|
+
status: "completed",
|
|
703
|
+
body: text,
|
|
704
|
+
description,
|
|
705
|
+
name,
|
|
706
|
+
onError: (m, meta) => logger.warn(m, meta),
|
|
707
|
+
});
|
|
708
|
+
if (sessionId) {
|
|
709
|
+
notificationQueue.enqueue({ agentId, name, description, status: "completed", finalText: text, enqueuedAt: Date.now() }, sessionId);
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
logger.warn("agent_completion_without_session", { agentId, name, status: "completed" });
|
|
713
|
+
}
|
|
714
|
+
void hooks?.emit("notification", { kind: "agent_completed", agentId, name, description, finalText: text });
|
|
715
|
+
})
|
|
716
|
+
.catch((err) => {
|
|
717
|
+
// runSubAgent threw before its success agent_end, so the UI card is still
|
|
718
|
+
// 'working'. Close it out with a terminal agent_end{error} (mirrors the
|
|
719
|
+
// synchronous catch at the agentTool level).
|
|
720
|
+
safeEmit(uiStream, { type: "agent_end", agentId, name, description, error: err.message, agentType });
|
|
721
|
+
if (controller.signal.aborted) {
|
|
722
|
+
asyncAgentRegistry.markCancelled(agentId);
|
|
723
|
+
void hooks?.emit("notification", { kind: "agent_cancelled", agentId, name, description });
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
asyncAgentRegistry.markFailed(agentId);
|
|
727
|
+
void writeAgentOutputFile(agentId, {
|
|
728
|
+
status: "failed",
|
|
729
|
+
body: err.message,
|
|
730
|
+
description,
|
|
731
|
+
name,
|
|
732
|
+
onError: (m, meta) => logger.warn(m, meta),
|
|
733
|
+
});
|
|
734
|
+
if (sessionId) {
|
|
735
|
+
notificationQueue.enqueue({ agentId, name, description, status: "failed", error: err.message, enqueuedAt: Date.now() }, sessionId);
|
|
736
|
+
}
|
|
737
|
+
else {
|
|
738
|
+
logger.warn("agent_completion_without_session", { agentId, name, status: "failed" });
|
|
739
|
+
}
|
|
740
|
+
void hooks?.emit("notification", { kind: "agent_failed", agentId, name, description, error: err.message });
|
|
741
|
+
});
|
|
742
|
+
}
|
|
374
743
|
// ─── AgentStatus / AgentCancel — companions to run_in_background ─
|
|
375
744
|
export const agentStatusToolDef = {
|
|
376
745
|
name: "AgentStatus",
|
|
377
746
|
description: "Check the status of a background agent launched with Agent(run_in_background=true). " +
|
|
378
747
|
"Returns running / completed / failed / cancelled, plus the result text once finished. " +
|
|
379
|
-
"Omit agent_id to list
|
|
748
|
+
"Omit agent_id to list this session's background agents (pass all=true for every session in the process). " +
|
|
749
|
+
"Results arrive automatically when an agent finishes — use this for an on-demand check, not to poll.",
|
|
380
750
|
inputSchema: {
|
|
381
751
|
type: "object",
|
|
382
752
|
properties: {
|
|
383
753
|
agent_id: {
|
|
384
754
|
type: "string",
|
|
385
|
-
description: "The agent_id returned by Agent(run_in_background=true). Omit to list
|
|
755
|
+
description: "The agent_id returned by Agent(run_in_background=true). Omit to list.",
|
|
756
|
+
},
|
|
757
|
+
all: {
|
|
758
|
+
type: "boolean",
|
|
759
|
+
description: "When listing (no agent_id): true lists background agents from every session in this process; " +
|
|
760
|
+
"default (false) lists only the current session's.",
|
|
386
761
|
},
|
|
387
762
|
},
|
|
388
763
|
},
|
|
389
764
|
};
|
|
390
|
-
export async function agentStatusTool(args) {
|
|
765
|
+
export async function agentStatusTool(args, ctx) {
|
|
391
766
|
const agentId = args.agent_id;
|
|
392
767
|
if (!agentId) {
|
|
393
|
-
const all =
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
768
|
+
const all = args.all === true;
|
|
769
|
+
// Default to the current session's agents so concurrent sessions don't
|
|
770
|
+
// leak into each other's listings; fall back to process-wide only when
|
|
771
|
+
// there's no session context or the caller explicitly asks for `all`.
|
|
772
|
+
const list = all || !ctx?.sessionId
|
|
773
|
+
? asyncAgentRegistry.list()
|
|
774
|
+
: asyncAgentRegistry.listForSession(ctx.sessionId);
|
|
775
|
+
if (list.length === 0) {
|
|
776
|
+
return all || !ctx?.sessionId
|
|
777
|
+
? "No background agents in this process."
|
|
778
|
+
: "No background agents in this session.";
|
|
779
|
+
}
|
|
780
|
+
return list
|
|
397
781
|
.map((e) => {
|
|
398
782
|
const dur = ((e.finishedAt ?? Date.now()) - e.startedAt) / 1000;
|
|
399
783
|
return `${e.agentId} [${e.status}] ${e.description} (${dur.toFixed(1)}s)`;
|
|
@@ -454,3 +838,122 @@ export async function agentCancelTool(args) {
|
|
|
454
838
|
? `Agent ${agentId} cancelled.`
|
|
455
839
|
: `Failed to cancel agent ${agentId}.`;
|
|
456
840
|
}
|
|
841
|
+
// ─── AgentSendInput — continue a previously-spawned sub-agent ────
|
|
842
|
+
export const agentSendInputToolDef = {
|
|
843
|
+
name: "AgentSendInput",
|
|
844
|
+
description: "Continue a sub-agent you previously launched with Agent — send it a follow-up " +
|
|
845
|
+
"instruction that it answers WITH FULL MEMORY of its earlier work (its prior " +
|
|
846
|
+
"messages, tool calls, and results). Use this instead of spawning a fresh agent " +
|
|
847
|
+
"with a re-pasted context dump: it resumes the same conversation by replaying the " +
|
|
848
|
+
"agent's saved transcript, so nothing is lost and you don't re-send what it already " +
|
|
849
|
+
"knows.\n\n" +
|
|
850
|
+
"Typical use: you got a partial result or a review verdict from a sub-agent, and now " +
|
|
851
|
+
"you want it to revise / answer a follow-up / act on feedback. Pass only the " +
|
|
852
|
+
"incremental instruction.\n\n" +
|
|
853
|
+
"Pass the agent_id the Agent tool returned. Works for both synchronous and background " +
|
|
854
|
+
"agents, and even after a process restart (the transcript is persisted on disk). " +
|
|
855
|
+
"Returns the agent's new reply, synchronously (a long continuation auto-moves to the " +
|
|
856
|
+
"background and notifies you on completion, like Agent does).",
|
|
857
|
+
inputSchema: {
|
|
858
|
+
type: "object",
|
|
859
|
+
properties: {
|
|
860
|
+
agent_id: {
|
|
861
|
+
type: "string",
|
|
862
|
+
description: "The agent_id returned by a prior Agent(...) call.",
|
|
863
|
+
},
|
|
864
|
+
prompt: {
|
|
865
|
+
type: "string",
|
|
866
|
+
description: "The follow-up instruction. The agent already remembers its earlier work — " +
|
|
867
|
+
"send only what's new (feedback, the next step, a question).",
|
|
868
|
+
},
|
|
869
|
+
},
|
|
870
|
+
required: ["agent_id", "prompt"],
|
|
871
|
+
},
|
|
872
|
+
};
|
|
873
|
+
export async function agentSendInputTool(args, ctx) {
|
|
874
|
+
const agentId = args.agent_id?.trim();
|
|
875
|
+
const prompt = args.prompt;
|
|
876
|
+
if (!agentId)
|
|
877
|
+
return "Error: agent_id is required.";
|
|
878
|
+
if (!prompt)
|
|
879
|
+
return "Error: prompt is required.";
|
|
880
|
+
if (!ctx?.subAgentSpawner) {
|
|
881
|
+
return "Error: Agent tool is not configured (no subAgentSpawner in ctx).";
|
|
882
|
+
}
|
|
883
|
+
// Same flat-hierarchy rule as Agent: a sub-agent cannot continue (or spawn)
|
|
884
|
+
// another sub-agent. Layered with the NESTED_AGENT_TOOLS strip in engine.ts.
|
|
885
|
+
if (ctx.isSubAgent === true) {
|
|
886
|
+
return "Error: nested agents are not supported. Sub-agents cannot send input to other sub-agents. Complete the task directly using your available tools.";
|
|
887
|
+
}
|
|
888
|
+
const spawner = ctx.subAgentSpawner;
|
|
889
|
+
const parentSignal = args.__signal;
|
|
890
|
+
if (parentSignal?.aborted) {
|
|
891
|
+
return "Agent aborted before resuming.";
|
|
892
|
+
}
|
|
893
|
+
// Resolve the child session to resume. With the agent_id===childSid
|
|
894
|
+
// convention the child session is stored at sessions/<agent_id>/, so the
|
|
895
|
+
// agent_id IS the resume target. Confirm it's resumable:
|
|
896
|
+
// 1. in-memory registry (fast path, same process) — gives us the recorded
|
|
897
|
+
// childSessionId (== agentId) and the dock label/agentType;
|
|
898
|
+
// 2. on-disk probe (cross-restart) — the registry is gone but the session
|
|
899
|
+
// transcript persists.
|
|
900
|
+
// Both miss → the agent never existed or was cleaned up.
|
|
901
|
+
const entry = asyncAgentRegistry.get(agentId);
|
|
902
|
+
const resumeSessionId = entry?.childSessionId ?? agentId;
|
|
903
|
+
const onDisk = spawner.sessionExists?.(resumeSessionId) ?? false;
|
|
904
|
+
if (!entry && !onDisk) {
|
|
905
|
+
return (`Error: no resumable sub-agent with agent_id "${agentId}". ` +
|
|
906
|
+
`It may never have run or its session was cleaned up. ` +
|
|
907
|
+
`Re-spawn with Agent(...) and a complete task description.`);
|
|
908
|
+
}
|
|
909
|
+
// Concurrency guard: a sub-agent that's still running (e.g. it auto-moved to
|
|
910
|
+
// the background and hasn't finished) owns its child session. Resuming now
|
|
911
|
+
// would build a SECOND child Engine that resumes the SAME session and appends
|
|
912
|
+
// to the SAME transcript concurrently — sub-agents have no per-session
|
|
913
|
+
// `active` lock (the protocol layer's serialization only covers top-level
|
|
914
|
+
// sessions), so the two writers interleave and can corrupt the transcript.
|
|
915
|
+
// Refuse until it finishes (or is cancelled).
|
|
916
|
+
if (entry?.status === "running") {
|
|
917
|
+
return (`Error: sub-agent "${agentId}" is still running. ` +
|
|
918
|
+
`Wait for it to finish (poll with AgentStatus), or cancel it with ` +
|
|
919
|
+
`AgentCancel, before sending it more input.`);
|
|
920
|
+
}
|
|
921
|
+
const name = entry?.name;
|
|
922
|
+
const description = entry?.description ?? "sub-agent continuation";
|
|
923
|
+
// Reconstruct the role's overrides from the recorded agentType. The child
|
|
924
|
+
// Engine is built FRESH on every spawn (engine.ts spawn closure) — resuming a
|
|
925
|
+
// session replays its transcript but the new Engine's tool/skill SCOPE comes
|
|
926
|
+
// from req.toolAllowlist / req.skillAllowlist, NOT from the persisted session.
|
|
927
|
+
// So if we didn't re-resolve these, a restricted role (e.g. a read-only
|
|
928
|
+
// reviewer) would silently regain the parent's full tool set on continuation.
|
|
929
|
+
// Re-resolving from agentType keeps the resumed turn under the same
|
|
930
|
+
// tool/skill/model constraints as the original spawn. An ephemeral agent
|
|
931
|
+
// (no agentType) resolves to empty overrides → inherits parent scope, same as
|
|
932
|
+
// its first turn did.
|
|
933
|
+
let overrides;
|
|
934
|
+
try {
|
|
935
|
+
overrides = resolveAgentTypeOverrides(entry?.agentType, ctx?.agentDefinitions);
|
|
936
|
+
}
|
|
937
|
+
catch {
|
|
938
|
+
// Role no longer exists (renamed/removed since spawn). Fall back to a bare
|
|
939
|
+
// continuation rather than refusing — the transcript still carries the
|
|
940
|
+
// role's system prompt, and replaying it is more useful than an error.
|
|
941
|
+
overrides = { resolvedType: entry?.agentType };
|
|
942
|
+
}
|
|
943
|
+
const maxTurns = overrides.maxTurns ?? 15;
|
|
944
|
+
const autoBgMs = resolveAutoBgMs();
|
|
945
|
+
return runSyncSubAgent({
|
|
946
|
+
spawner,
|
|
947
|
+
agentId,
|
|
948
|
+
name,
|
|
949
|
+
description,
|
|
950
|
+
prompt,
|
|
951
|
+
maxTurns,
|
|
952
|
+
overrides,
|
|
953
|
+
autoBgMs,
|
|
954
|
+
parentSignal,
|
|
955
|
+
parentStream: spawner.parentStream,
|
|
956
|
+
ctx,
|
|
957
|
+
resumeSessionId,
|
|
958
|
+
});
|
|
959
|
+
}
|