@cjhyy/code-shell-core 0.5.0-rc.2 → 0.6.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -10
- package/dist/agent/agent-definition-registry.d.ts +3 -0
- package/dist/agent/agent-definition-registry.js +3 -1
- package/dist/agent/agent-definition.d.ts +15 -0
- package/dist/agent/agent-definition.js +27 -3
- package/dist/arena/context/context-tools.js +47 -4
- package/dist/arena/ledger.js +9 -1
- package/dist/arena/strategies/utils.d.ts +1 -4
- package/dist/arena/strategies/utils.js +7 -84
- package/dist/automation/cron-expr.d.ts +10 -0
- package/dist/automation/cron-expr.js +23 -5
- package/dist/automation/runner.d.ts +21 -4
- package/dist/automation/runner.js +48 -12
- package/dist/automation/scheduler.d.ts +38 -0
- package/dist/automation/scheduler.js +89 -12
- package/dist/automation/store.js +5 -2
- package/dist/capability-control/disabled-lists.d.ts +25 -0
- package/dist/capability-control/disabled-lists.js +57 -0
- package/dist/capability-control/overlay.d.ts +15 -0
- package/dist/capability-control/overlay.js +27 -0
- package/dist/cc-orchestrator/agent-adapter.d.ts +50 -0
- package/dist/cc-orchestrator/agent-adapter.js +133 -0
- package/dist/cc-orchestrator/cc-capability.d.ts +19 -0
- package/dist/cc-orchestrator/cc-capability.js +53 -0
- package/dist/cc-orchestrator/codex-session-discovery.d.ts +24 -0
- package/dist/cc-orchestrator/codex-session-discovery.js +191 -0
- package/dist/cc-orchestrator/codex-session-history.d.ts +25 -0
- package/dist/cc-orchestrator/codex-session-history.js +187 -0
- package/dist/cc-orchestrator/external-agent-changes.d.ts +19 -0
- package/dist/cc-orchestrator/external-agent-changes.js +214 -0
- package/dist/cc-orchestrator/external-agent-driver.d.ts +18 -0
- package/dist/cc-orchestrator/external-agent-driver.js +69 -0
- package/dist/cc-orchestrator/index.d.ts +8 -0
- package/dist/cc-orchestrator/index.js +8 -0
- package/dist/cc-orchestrator/relevance-judge.d.ts +15 -0
- package/dist/cc-orchestrator/relevance-judge.js +29 -0
- package/dist/cc-orchestrator/session-discovery.d.ts +46 -0
- package/dist/cc-orchestrator/session-discovery.js +125 -0
- package/dist/cc-orchestrator/session-history.d.ts +19 -0
- package/dist/cc-orchestrator/session-history.js +67 -0
- package/dist/cli/agent-server-stdio.d.ts +15 -1
- package/dist/cli/agent-server-stdio.js +115 -17
- package/dist/cli/agent-server-tcp.js +40 -26
- package/dist/context/compaction.d.ts +86 -0
- package/dist/context/compaction.js +279 -0
- package/dist/context/manager.d.ts +18 -0
- package/dist/context/manager.js +181 -48
- package/dist/context/token-counter.js +13 -0
- package/dist/cost-tracker.js +5 -61
- package/dist/credentials/cipher.d.ts +49 -0
- package/dist/credentials/cipher.js +45 -0
- package/dist/credentials/cookie-jar.d.ts +24 -0
- package/dist/credentials/cookie-jar.js +40 -0
- package/dist/credentials/index.d.ts +7 -0
- package/dist/credentials/index.js +5 -0
- package/dist/credentials/inject-credential-tool.d.ts +20 -0
- package/dist/credentials/inject-credential-tool.js +130 -0
- package/dist/credentials/store.d.ts +72 -0
- package/dist/credentials/store.js +184 -0
- package/dist/credentials/types.d.ts +56 -0
- package/dist/credentials/use-credential-tool.d.ts +29 -0
- package/dist/credentials/use-credential-tool.js +205 -0
- package/dist/credentials/use-gate.d.ts +56 -0
- package/dist/credentials/use-gate.js +52 -0
- package/dist/data/model-metadata.d.ts +77 -0
- package/dist/data/model-metadata.js +56 -0
- package/dist/data/model-metadata.json +216 -0
- package/dist/data/openrouter-models.d.ts +18 -7
- package/dist/data/openrouter-models.js +35 -8
- package/dist/engine/aux-key.d.ts +10 -0
- package/dist/engine/aux-key.js +11 -0
- package/dist/engine/dynamic-tool-defs.d.ts +19 -0
- package/dist/engine/dynamic-tool-defs.js +36 -0
- package/dist/engine/engine.d.ts +280 -141
- package/dist/engine/engine.js +1193 -245
- package/dist/engine/friendly-error.d.ts +18 -0
- package/dist/engine/friendly-error.js +63 -0
- package/dist/engine/goal.d.ts +145 -0
- package/dist/engine/goal.js +144 -0
- package/dist/engine/image-policy.d.ts +13 -0
- package/dist/engine/image-policy.js +24 -0
- package/dist/engine/model-connections-pool.d.ts +17 -0
- package/dist/engine/model-connections-pool.js +67 -0
- package/dist/engine/model-facade.d.ts +10 -0
- package/dist/engine/model-facade.js +15 -0
- package/dist/engine/patch-orphaned-tools.js +3 -0
- package/dist/engine/query.js +2 -0
- package/dist/engine/resolve-llm-config.d.ts +16 -0
- package/dist/engine/resolve-llm-config.js +44 -0
- package/dist/engine/runtime.d.ts +2 -0
- package/dist/engine/runtime.js +27 -1
- package/dist/engine/sandbox-cache-key.d.ts +10 -0
- package/dist/engine/sandbox-cache-key.js +9 -0
- package/dist/engine/sandbox-config.d.ts +31 -0
- package/dist/engine/sandbox-config.js +38 -0
- package/dist/engine/session-usage.d.ts +31 -0
- package/dist/engine/session-usage.js +81 -0
- package/dist/engine/steer-queue.d.ts +33 -0
- package/dist/engine/steer-queue.js +26 -0
- package/dist/engine/streaming-tool-queue.d.ts +12 -0
- package/dist/engine/streaming-tool-queue.js +50 -10
- package/dist/engine/turn-loop.d.ts +94 -4
- package/dist/engine/turn-loop.js +432 -54
- package/dist/engine/types.d.ts +175 -0
- package/dist/engine/types.js +13 -0
- package/dist/external-agents/config.d.ts +2 -0
- package/dist/external-agents/config.js +15 -0
- package/dist/external-agents/types.d.ts +31 -0
- package/dist/external-agents/types.js +1 -0
- package/dist/git/utils.d.ts +12 -0
- package/dist/git/utils.js +38 -6
- package/dist/git/worktree.d.ts +48 -0
- package/dist/git/worktree.js +86 -10
- package/dist/hooks/goal-stop-hook.d.ts +28 -0
- package/dist/hooks/goal-stop-hook.js +186 -9
- package/dist/hooks/registry.d.ts +8 -0
- package/dist/hooks/registry.js +19 -0
- package/dist/hooks/shell-runner.js +10 -21
- package/dist/index.d.ts +51 -7
- package/dist/index.js +62 -5
- package/dist/llm/capabilities/param-specs.d.ts +14 -0
- package/dist/llm/capabilities/param-specs.js +62 -0
- package/dist/llm/capabilities/rules.js +5 -1
- package/dist/llm/capabilities/types.d.ts +10 -0
- package/dist/llm/client-base.d.ts +28 -1
- package/dist/llm/client-base.js +119 -13
- package/dist/llm/model-cache.js +4 -2
- package/dist/llm/model-pool.d.ts +21 -0
- package/dist/llm/model-pool.js +21 -1
- package/dist/llm/provider-auth.d.ts +41 -0
- package/dist/llm/provider-auth.js +76 -0
- package/dist/llm/provider-catalog.d.ts +4 -0
- package/dist/llm/providers/anthropic.js +64 -14
- package/dist/llm/providers/openai.d.ts +36 -0
- package/dist/llm/providers/openai.js +257 -51
- package/dist/llm/reasoning-setting.d.ts +3 -3
- package/dist/llm/reasoning-setting.js +9 -1
- package/dist/llm/stream-watchdog.js +5 -1
- package/dist/llm/token-counter.js +9 -2
- package/dist/llm/types.d.ts +4 -0
- package/dist/logging/sanitize-messages.js +22 -0
- package/dist/lsp/manager.d.ts +1 -1
- package/dist/lsp/manager.js +40 -10
- package/dist/model-catalog/builtin.d.ts +12 -0
- package/dist/model-catalog/builtin.js +412 -0
- package/dist/model-catalog/gen-connections.d.ts +20 -0
- package/dist/model-catalog/gen-connections.js +28 -0
- package/dist/model-catalog/index.d.ts +41 -0
- package/dist/model-catalog/index.js +90 -0
- package/dist/model-catalog/params.d.ts +20 -0
- package/dist/model-catalog/params.js +45 -0
- package/dist/model-catalog/resolve.d.ts +48 -0
- package/dist/model-catalog/resolve.js +33 -0
- package/dist/model-catalog/save-entry.d.ts +32 -0
- package/dist/model-catalog/save-entry.js +104 -0
- package/dist/model-catalog/types.d.ts +561 -0
- package/dist/model-catalog/types.js +93 -0
- package/dist/model-catalog/upsert.d.ts +9 -0
- package/dist/model-catalog/upsert.js +8 -0
- package/dist/onboarding.d.ts +12 -82
- package/dist/onboarding.js +61 -326
- package/dist/plugins/gitOps.d.ts +19 -0
- package/dist/plugins/gitOps.js +73 -4
- package/dist/plugins/installer/checkUpdate.d.ts +16 -0
- package/dist/plugins/installer/checkUpdate.js +32 -0
- package/dist/plugins/installer/codex/convertCommands.d.ts +19 -0
- package/dist/plugins/installer/codex/convertCommands.js +46 -0
- package/dist/plugins/installer/codex/convertMcp.d.ts +5 -2
- package/dist/plugins/installer/codex/convertMcp.js +45 -5
- package/dist/plugins/installer/install.js +25 -1
- package/dist/plugins/installer/installFromArchive.d.ts +43 -0
- package/dist/plugins/installer/installFromArchive.js +134 -0
- package/dist/plugins/installer/installFromSource.js +8 -2
- package/dist/plugins/installer/loadPluginAgents.js +6 -2
- package/dist/plugins/installer/loadPluginMcp.d.ts +9 -2
- package/dist/plugins/installer/loadPluginMcp.js +35 -2
- package/dist/plugins/installer/pruneDisabled.d.ts +24 -0
- package/dist/plugins/installer/pruneDisabled.js +73 -0
- package/dist/plugins/installer/types.d.ts +5 -2
- package/dist/plugins/installer/types.js +1 -0
- package/dist/plugins/installer/uninstall.js +4 -0
- package/dist/plugins/installer/unzip.d.ts +14 -0
- package/dist/plugins/installer/unzip.js +82 -0
- package/dist/plugins/installer/update.d.ts +14 -0
- package/dist/plugins/installer/update.js +53 -21
- package/dist/plugins/loadPluginHooks.d.ts +47 -1
- package/dist/plugins/loadPluginHooks.js +73 -1
- package/dist/plugins/marketplaceManager.d.ts +7 -0
- package/dist/plugins/marketplaceManager.js +20 -0
- package/dist/plugins/pluginCommandHook.js +4 -18
- package/dist/plugins/pluginContent.d.ts +30 -0
- package/dist/plugins/pluginContent.js +83 -0
- package/dist/plugins/pluginInstaller.d.ts +13 -0
- package/dist/plugins/pluginInstaller.js +42 -8
- package/dist/plugins/schemas.js +1 -0
- package/dist/plugins/types.d.ts +6 -0
- package/dist/preset/index.d.ts +11 -1
- package/dist/preset/index.js +115 -6
- package/dist/product/types.d.ts +1 -1
- package/dist/prompt/composer.d.ts +32 -0
- package/dist/prompt/composer.js +75 -21
- package/dist/prompt/instruction-scanner.js +5 -3
- package/dist/prompt/section-loader.js +1 -0
- package/dist/prompt/sections/base.md +2 -0
- package/dist/prompt/sections/browser.md +10 -0
- package/dist/prompt/sections/coding.md +4 -0
- package/dist/protocol/chat-session-manager.d.ts +9 -2
- package/dist/protocol/chat-session-manager.js +38 -0
- package/dist/protocol/chat-session.d.ts +69 -1
- package/dist/protocol/chat-session.js +102 -4
- package/dist/protocol/client.d.ts +31 -0
- package/dist/protocol/client.js +44 -0
- package/dist/protocol/server.d.ts +120 -7
- package/dist/protocol/server.js +753 -51
- package/dist/protocol/transport.js +3 -2
- package/dist/protocol/types.d.ts +60 -0
- package/dist/protocol/types.js +16 -0
- package/dist/quota/credentials.d.ts +3 -0
- package/dist/quota/credentials.js +80 -0
- package/dist/quota/index.d.ts +36 -0
- package/dist/quota/index.js +155 -0
- package/dist/quota/types.d.ts +48 -0
- package/dist/quota/types.js +13 -0
- package/dist/review/review-prompt.d.ts +28 -0
- package/dist/review/review-prompt.js +81 -0
- package/dist/run/FileRunStore.js +8 -3
- package/dist/run/RunApprovalBackend.js +25 -5
- package/dist/run/RunManager.d.ts +12 -0
- package/dist/run/RunManager.js +35 -0
- package/dist/run/factory.d.ts +1 -1
- package/dist/runtime/background-shell.d.ts +139 -0
- package/dist/runtime/background-shell.js +509 -0
- package/dist/runtime/output-clean.d.ts +24 -0
- package/dist/runtime/output-clean.js +41 -0
- package/dist/runtime/ring-file.d.ts +64 -0
- package/dist/runtime/ring-file.js +174 -0
- package/dist/runtime/safe-spawn.js +98 -39
- package/dist/runtime/spawn-common.d.ts +159 -0
- package/dist/runtime/spawn-common.js +404 -0
- package/dist/runtime/truncate-output.d.ts +22 -0
- package/dist/runtime/truncate-output.js +49 -0
- package/dist/runtime/utf8-cut.d.ts +11 -0
- package/dist/runtime/utf8-cut.js +23 -0
- package/dist/services/auto-dream.d.ts +4 -0
- package/dist/services/auto-dream.js +26 -26
- package/dist/services/diagnostics.d.ts +1 -2
- package/dist/services/diagnostics.js +12 -7
- package/dist/services/extract-memories.d.ts +14 -1
- package/dist/services/extract-memories.js +45 -6
- package/dist/services/memory-orchestrator.d.ts +21 -0
- package/dist/services/memory-orchestrator.js +125 -50
- package/dist/services/session-memory.js +24 -12
- package/dist/session/file-history.d.ts +124 -1
- package/dist/session/file-history.js +222 -6
- package/dist/session/memory.d.ts +116 -2
- package/dist/session/memory.js +250 -28
- package/dist/session/session-manager.d.ts +40 -0
- package/dist/session/session-manager.js +119 -3
- package/dist/session/simple-diff.d.ts +23 -0
- package/dist/session/simple-diff.js +84 -0
- package/dist/session/transcript.d.ts +29 -1
- package/dist/session/transcript.js +56 -2
- package/dist/session/undo-target.d.ts +67 -0
- package/dist/session/undo-target.js +144 -0
- package/dist/settings/disk-defaults.d.ts +9 -2
- package/dist/settings/disk-defaults.js +11 -2
- package/dist/settings/feature-flags.d.ts +64 -0
- package/dist/settings/feature-flags.js +61 -0
- package/dist/settings/manager.d.ts +68 -1
- package/dist/settings/manager.js +266 -29
- package/dist/settings/migrate-config.d.ts +45 -0
- package/dist/settings/migrate-config.js +125 -0
- package/dist/settings/schema-export.d.ts +25 -0
- package/dist/settings/schema-export.js +38 -0
- package/dist/settings/schema.d.ts +1186 -771
- package/dist/settings/schema.js +268 -97
- package/dist/skills/scanner.d.ts +9 -0
- package/dist/skills/scanner.js +30 -2
- package/dist/stt/resolve-transcribe.d.ts +31 -0
- package/dist/stt/resolve-transcribe.js +108 -0
- package/dist/stt/transcribe.d.ts +51 -0
- package/dist/stt/transcribe.js +65 -0
- package/dist/tool-system/browser-bridge.d.ts +226 -0
- package/dist/tool-system/browser-bridge.js +163 -0
- package/dist/tool-system/builtin/agent-heartbeat.d.ts +49 -0
- package/dist/tool-system/builtin/agent-heartbeat.js +89 -0
- package/dist/tool-system/builtin/agent-notifications.d.ts +12 -3
- package/dist/tool-system/builtin/agent-notifications.js +9 -3
- package/dist/tool-system/builtin/agent-output-file.d.ts +38 -0
- package/dist/tool-system/builtin/agent-output-file.js +72 -0
- package/dist/tool-system/builtin/agent-registry.d.ts +12 -0
- package/dist/tool-system/builtin/agent-registry.js +8 -0
- package/dist/tool-system/builtin/agent.d.ts +21 -1
- package/dist/tool-system/builtin/agent.js +489 -42
- package/dist/tool-system/builtin/apply-patch/applier.js +66 -8
- package/dist/tool-system/builtin/apply-patch/backup-targets.d.ts +10 -0
- package/dist/tool-system/builtin/apply-patch/backup-targets.js +30 -0
- package/dist/tool-system/builtin/apply-patch/index.js +0 -15
- package/dist/tool-system/builtin/background-jobs.d.ts +76 -0
- package/dist/tool-system/builtin/background-jobs.js +124 -0
- package/dist/tool-system/builtin/background-shell-tools.d.ts +20 -0
- package/dist/tool-system/builtin/background-shell-tools.js +108 -0
- package/dist/tool-system/builtin/background-work.d.ts +67 -0
- package/dist/tool-system/builtin/background-work.js +86 -0
- package/dist/tool-system/builtin/bash-output-style.d.ts +32 -0
- package/dist/tool-system/builtin/bash-output-style.js +40 -0
- package/dist/tool-system/builtin/bash.d.ts +5 -2
- package/dist/tool-system/builtin/bash.js +101 -64
- package/dist/tool-system/builtin/browser-tools.d.ts +33 -0
- package/dist/tool-system/builtin/browser-tools.js +312 -0
- package/dist/tool-system/builtin/cancel-goal.d.ts +31 -0
- package/dist/tool-system/builtin/cancel-goal.js +64 -0
- package/dist/tool-system/builtin/check-quota.d.ts +15 -0
- package/dist/tool-system/builtin/check-quota.js +34 -0
- package/dist/tool-system/builtin/config.js +7 -0
- package/dist/tool-system/builtin/cron.d.ts +7 -0
- package/dist/tool-system/builtin/cron.js +64 -4
- package/dist/tool-system/builtin/drive-claude-code.d.ts +30 -0
- package/dist/tool-system/builtin/drive-claude-code.js +157 -0
- package/dist/tool-system/builtin/edit-model-catalog.d.ts +3 -0
- package/dist/tool-system/builtin/edit-model-catalog.js +104 -0
- package/dist/tool-system/builtin/edit.js +20 -16
- package/dist/tool-system/builtin/eol.d.ts +29 -0
- package/dist/tool-system/builtin/eol.js +37 -0
- package/dist/tool-system/builtin/file-cache.d.ts +6 -0
- package/dist/tool-system/builtin/file-cache.js +8 -0
- package/dist/tool-system/builtin/generate-image.d.ts +35 -0
- package/dist/tool-system/builtin/generate-image.js +278 -50
- package/dist/tool-system/builtin/generate-video.d.ts +55 -0
- package/dist/tool-system/builtin/generate-video.js +364 -0
- package/dist/tool-system/builtin/glob.js +0 -7
- package/dist/tool-system/builtin/grep.d.ts +9 -0
- package/dist/tool-system/builtin/grep.js +106 -11
- package/dist/tool-system/builtin/image-providers.d.ts +86 -0
- package/dist/tool-system/builtin/image-providers.js +190 -0
- package/dist/tool-system/builtin/image-uploader.d.ts +33 -0
- package/dist/tool-system/builtin/image-uploader.js +74 -0
- package/dist/tool-system/builtin/index.d.ts +10 -2
- package/dist/tool-system/builtin/index.js +251 -27
- package/dist/tool-system/builtin/mcp-tools.js +23 -3
- package/dist/tool-system/builtin/memory.js +45 -7
- package/dist/tool-system/builtin/notebook-edit.js +0 -7
- package/dist/tool-system/builtin/powershell.js +8 -2
- package/dist/tool-system/builtin/read.js +10 -10
- package/dist/tool-system/builtin/repl.js +4 -1
- package/dist/tool-system/builtin/skill.js +9 -0
- package/dist/tool-system/builtin/sleep.js +8 -2
- package/dist/tool-system/builtin/tool-search.js +25 -7
- package/dist/tool-system/builtin/video-providers.d.ts +154 -0
- package/dist/tool-system/builtin/video-providers.js +235 -0
- package/dist/tool-system/builtin/web-fetch.js +12 -2
- package/dist/tool-system/builtin/web-search.js +21 -7
- package/dist/tool-system/builtin/worktree.d.ts +2 -1
- package/dist/tool-system/builtin/worktree.js +31 -4
- package/dist/tool-system/builtin/write.js +0 -6
- package/dist/tool-system/context.d.ts +164 -6
- package/dist/tool-system/executor.d.ts +3 -1
- package/dist/tool-system/executor.js +197 -90
- package/dist/tool-system/investigation-guard.js +1 -1
- package/dist/tool-system/mcp-manager.d.ts +42 -4
- package/dist/tool-system/mcp-manager.js +203 -20
- package/dist/tool-system/mcp-stdio-diagnostics.d.ts +9 -0
- package/dist/tool-system/mcp-stdio-diagnostics.js +93 -0
- package/dist/tool-system/path-policy.d.ts +5 -0
- package/dist/tool-system/path-policy.js +307 -8
- package/dist/tool-system/permission.d.ts +39 -2
- package/dist/tool-system/permission.js +283 -70
- package/dist/tool-system/plan-mode-allowlist.d.ts +13 -2
- package/dist/tool-system/plan-mode-allowlist.js +24 -2
- package/dist/tool-system/registry.d.ts +1 -0
- package/dist/tool-system/registry.js +16 -2
- package/dist/tool-system/sandbox/index.d.ts +8 -0
- package/dist/tool-system/sandbox/index.js +7 -2
- package/dist/tool-system/sandbox/off.js +7 -1
- package/dist/tool-system/validate-tool-metadata.d.ts +36 -0
- package/dist/tool-system/validate-tool-metadata.js +63 -0
- package/dist/types.d.ts +252 -7
- package/dist/updater.js +20 -9
- package/dist/utils/envUtils.d.ts +0 -9
- package/dist/utils/envUtils.js +3 -28
- package/dist/utils/exec.d.ts +48 -0
- package/dist/utils/exec.js +154 -0
- package/dist/utils/json.d.ts +12 -0
- package/dist/utils/json.js +92 -0
- package/dist/utils/theme.d.ts +1 -1
- package/dist/utils/theme.js +1 -1
- package/dist/utils/toolDisplay.js +0 -1
- package/package.json +13 -7
- package/dist/agent/coordinator.d.ts +0 -49
- package/dist/agent/coordinator.js +0 -77
- package/dist/settings/manager.test.js +0 -73
- package/dist/tool-system/builtin/remote-trigger.d.ts +0 -6
- package/dist/tool-system/builtin/remote-trigger.js +0 -54
- package/dist/tool-system/builtin/send-message.d.ts +0 -6
- package/dist/tool-system/builtin/send-message.js +0 -47
- /package/dist/{settings/manager.test.d.ts → credentials/types.js} +0 -0
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
// Algorithm uses code adapted from codex-rs/apply-patch/src/lib.rs (Apache-2.0)
|
|
11
11
|
// for the chunk-replacement phase (compute_replacements, apply_replacements).
|
|
12
12
|
import { mkdir, readFile, stat, unlink, writeFile } from "node:fs/promises";
|
|
13
|
-
import { existsSync } from "node:fs";
|
|
14
|
-
import { dirname, isAbsolute, resolve } from "node:path";
|
|
13
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
14
|
+
import { dirname, isAbsolute, resolve, sep } from "node:path";
|
|
15
15
|
import { seekSequence } from "./seek-sequence.js";
|
|
16
|
+
import { detectEol, toLf, applyEol } from "../eol.js";
|
|
16
17
|
/**
|
|
17
18
|
* Plan and commit the patch atomically.
|
|
18
19
|
*
|
|
@@ -76,8 +77,15 @@ async function planHunks(hunks, cwd) {
|
|
|
76
77
|
if (!stats) {
|
|
77
78
|
throw new Error(`Failed to update file: ${sourcePath} does not exist`);
|
|
78
79
|
}
|
|
79
|
-
const
|
|
80
|
-
|
|
80
|
+
const originalRaw = await readFile(sourcePath, "utf-8");
|
|
81
|
+
// CRLF handling: patch context lines are LF (the model emits LF). Match in
|
|
82
|
+
// LF space, then write back in the file's original EOL so a CRLF file isn't
|
|
83
|
+
// silently converted to LF (which would diff the whole file). originalText
|
|
84
|
+
// (LF) is what we compare; originalRaw is preserved for rollback so a
|
|
85
|
+
// restore writes back the exact bytes. See eol.ts.
|
|
86
|
+
const eol = detectEol(originalRaw);
|
|
87
|
+
const originalText = toLf(originalRaw);
|
|
88
|
+
const newText = applyEol(applyChunksToText(originalText, hunk.chunks, sourcePath), eol);
|
|
81
89
|
if (hunk.movePath !== undefined) {
|
|
82
90
|
const destPath = resolveAgainst(hunk.movePath, cwd);
|
|
83
91
|
if (destPath === sourcePath) {
|
|
@@ -85,7 +93,7 @@ async function planHunks(hunks, cwd) {
|
|
|
85
93
|
schedule(sourcePath, {
|
|
86
94
|
path: sourcePath,
|
|
87
95
|
newContent: newText,
|
|
88
|
-
originalContent:
|
|
96
|
+
originalContent: originalRaw,
|
|
89
97
|
});
|
|
90
98
|
}
|
|
91
99
|
else {
|
|
@@ -93,7 +101,7 @@ async function planHunks(hunks, cwd) {
|
|
|
93
101
|
schedule(sourcePath, {
|
|
94
102
|
path: sourcePath,
|
|
95
103
|
newContent: null,
|
|
96
|
-
originalContent:
|
|
104
|
+
originalContent: originalRaw,
|
|
97
105
|
});
|
|
98
106
|
schedule(destPath, {
|
|
99
107
|
path: destPath,
|
|
@@ -107,7 +115,7 @@ async function planHunks(hunks, cwd) {
|
|
|
107
115
|
schedule(sourcePath, {
|
|
108
116
|
path: sourcePath,
|
|
109
117
|
newContent: newText,
|
|
110
|
-
originalContent:
|
|
118
|
+
originalContent: originalRaw,
|
|
111
119
|
});
|
|
112
120
|
set.modified.push(original);
|
|
113
121
|
}
|
|
@@ -229,8 +237,58 @@ async function writeChange(change) {
|
|
|
229
237
|
await writeFile(change.path, change.newContent, "utf-8");
|
|
230
238
|
}
|
|
231
239
|
// ─── helpers ──────────────────────────────────────────────────────
|
|
240
|
+
/**
|
|
241
|
+
* Resolve a patch-relative (or absolute) path against cwd, then HARD-ENFORCE
|
|
242
|
+
* that the result stays inside cwd.
|
|
243
|
+
*
|
|
244
|
+
* This is the applier's own containment guard, independent of the executor-level
|
|
245
|
+
* pathPolicy. apply-patch is reachable with the path gate bypassed (e.g.
|
|
246
|
+
* bypassPermissions, or a direct call), so the last line of defense lives here.
|
|
247
|
+
*
|
|
248
|
+
* Containment is checked against the REAL paths: we realpath cwd and walk the
|
|
249
|
+
* target up to its nearest existing ancestor and realpath that. This defeats a
|
|
250
|
+
* symlink planted inside cwd that points outside — a plain string `resolve()`
|
|
251
|
+
* would look contained while the write follows the link out.
|
|
252
|
+
*/
|
|
232
253
|
function resolveAgainst(p, cwd) {
|
|
233
|
-
|
|
254
|
+
const resolved = isAbsolute(p) ? p : resolve(cwd, p);
|
|
255
|
+
const realCwd = nearestRealPath(cwd);
|
|
256
|
+
const realTarget = nearestRealPath(resolved);
|
|
257
|
+
if (!isInside(realTarget, realCwd)) {
|
|
258
|
+
throw new Error(`Refusing to apply patch outside the working directory: "${p}" resolves to "${realTarget}", which escapes "${realCwd}".`);
|
|
259
|
+
}
|
|
260
|
+
return resolved;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Realpath the nearest existing ancestor of `abs`, then re-attach the
|
|
264
|
+
* not-yet-existing trailing segments. Mirrors path-policy.safeRealpath so
|
|
265
|
+
* symlinked ancestors are resolved even when the leaf doesn't exist yet.
|
|
266
|
+
*/
|
|
267
|
+
function nearestRealPath(abs) {
|
|
268
|
+
let candidate = abs;
|
|
269
|
+
const segments = [];
|
|
270
|
+
for (let i = 0; i < 64; i++) {
|
|
271
|
+
try {
|
|
272
|
+
const real = realpathSync(candidate);
|
|
273
|
+
if (segments.length === 0)
|
|
274
|
+
return real;
|
|
275
|
+
return resolve(real, ...segments.reverse());
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
const parent = dirname(candidate);
|
|
279
|
+
if (parent === candidate)
|
|
280
|
+
return abs;
|
|
281
|
+
segments.push(candidate.slice(parent.length + (parent.endsWith(sep) ? 0 : 1)));
|
|
282
|
+
candidate = parent;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return abs;
|
|
286
|
+
}
|
|
287
|
+
function isInside(child, parent) {
|
|
288
|
+
if (child === parent)
|
|
289
|
+
return true;
|
|
290
|
+
const p = parent.endsWith(sep) ? parent : parent + sep;
|
|
291
|
+
return child.startsWith(p);
|
|
234
292
|
}
|
|
235
293
|
async function safeStat(path) {
|
|
236
294
|
try {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which files an ApplyPatch would modify-in-place or delete — i.e. the files
|
|
3
|
+
* that already exist and therefore have pre-patch content worth snapshotting
|
|
4
|
+
* for /undo. Pure (parse-only, no fs): given the patch text and the cwd to
|
|
5
|
+
* resolve relative paths against, return absolute target paths.
|
|
6
|
+
*
|
|
7
|
+
* "add" hunks are excluded: a newly-created file has no prior content to back
|
|
8
|
+
* up (undo of an add = delete, handled separately if/when /undo grows that).
|
|
9
|
+
*/
|
|
10
|
+
export declare function patchBackupTargets(patchText: string, cwd: string): string[];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which files an ApplyPatch would modify-in-place or delete — i.e. the files
|
|
3
|
+
* that already exist and therefore have pre-patch content worth snapshotting
|
|
4
|
+
* for /undo. Pure (parse-only, no fs): given the patch text and the cwd to
|
|
5
|
+
* resolve relative paths against, return absolute target paths.
|
|
6
|
+
*
|
|
7
|
+
* "add" hunks are excluded: a newly-created file has no prior content to back
|
|
8
|
+
* up (undo of an add = delete, handled separately if/when /undo grows that).
|
|
9
|
+
*/
|
|
10
|
+
import { resolve as resolvePath } from "node:path";
|
|
11
|
+
import { parsePatch } from "./parser.js";
|
|
12
|
+
export function patchBackupTargets(patchText, cwd) {
|
|
13
|
+
let parsed;
|
|
14
|
+
try {
|
|
15
|
+
parsed = parsePatch(patchText, "lenient");
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// Unparseable patch → nothing to back up; the tool itself will reject it.
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
const out = [];
|
|
22
|
+
for (const hunk of parsed.hunks) {
|
|
23
|
+
if (hunk.kind === "update" || hunk.kind === "delete") {
|
|
24
|
+
out.push(resolvePath(cwd, hunk.path));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// De-dupe while preserving order (a patch shouldn't target one file twice,
|
|
28
|
+
// but be defensive — saveSnapshot is idempotent on identical content anyway).
|
|
29
|
+
return [...new Set(out)];
|
|
30
|
+
}
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
import { fileCache } from "../file-cache.js";
|
|
18
18
|
import { applyPatch } from "./applier.js";
|
|
19
19
|
import { parsePatch } from "./parser.js";
|
|
20
|
-
import { enforcePathPolicy } from "../../path-policy.js";
|
|
21
20
|
import { resolve as resolvePath } from "node:path";
|
|
22
21
|
export const applyPatchToolDef = {
|
|
23
22
|
name: "ApplyPatch",
|
|
@@ -70,20 +69,6 @@ export async function applyPatchTool(args, ctx) {
|
|
|
70
69
|
// A4: resolve relative patch paths against the Engine's cwd, not the
|
|
71
70
|
// host process cwd. See standard §S5.
|
|
72
71
|
const cwd = ctx?.cwd ?? process.cwd();
|
|
73
|
-
// Path-policy gate every target before any IO. A multi-file patch is
|
|
74
|
-
// atomic — if *any* target is blocked the whole patch must reject so we
|
|
75
|
-
// don't end up writing some files and refusing others.
|
|
76
|
-
for (const hunk of parsed.hunks) {
|
|
77
|
-
const targets = [resolvePath(cwd, hunk.path)];
|
|
78
|
-
if (hunk.kind === "update" && hunk.movePath) {
|
|
79
|
-
targets.push(resolvePath(cwd, hunk.movePath));
|
|
80
|
-
}
|
|
81
|
-
for (const t of targets) {
|
|
82
|
-
const blocked = enforcePathPolicy(t, "write", cwd);
|
|
83
|
-
if (blocked)
|
|
84
|
-
return blocked;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
72
|
let result;
|
|
88
73
|
try {
|
|
89
74
|
result = await applyPatch(parsed.hunks, { cwd });
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry of NON-agent background jobs (GenerateVideo poll loops, DriveAgent
|
|
3
|
+
* external CLI runs). Mirrors the role `asyncAgentRegistry` plays for background
|
|
4
|
+
* sub-agents, but a job is not an agent — it has no transcript, no abort(), and
|
|
5
|
+
* must NOT show up in AgentStatus.
|
|
6
|
+
*
|
|
7
|
+
* The engine's wait-for-background loop parks the turn until a session has no
|
|
8
|
+
* more RUNNING jobs, so the goal-stop-hook doesn't force the model to busy-loop
|
|
9
|
+
* with `sleep` while a video renders (the s-mqe0ox7n-a8d11c26 bug).
|
|
10
|
+
*
|
|
11
|
+
* Finished jobs are RETAINED (status flips to completed/failed, result stored)
|
|
12
|
+
* so the background panel can show them + let the user open the result — they
|
|
13
|
+
* don't vanish the instant they complete (#2/#5). Retention is bounded:
|
|
14
|
+
* - event-driven: a session's jobs are dropped when the session is deleted
|
|
15
|
+
* (dropForSession, wired to session close/delete);
|
|
16
|
+
* - a generous per-session cap on TERMINAL jobs is a pure memory backstop for
|
|
17
|
+
* a session that spawns hundreds of jobs and is never deleted.
|
|
18
|
+
*
|
|
19
|
+
* Process-local singleton, same pattern as asyncAgentRegistry. The video/Drive
|
|
20
|
+
* loops run in the main engine process, so start/finish and the engine's wait
|
|
21
|
+
* loop observe the same instance.
|
|
22
|
+
*/
|
|
23
|
+
type Listener = () => void;
|
|
24
|
+
export type BackgroundJobStatus = "running" | "completed" | "failed";
|
|
25
|
+
/** A background job, running or finished. */
|
|
26
|
+
export interface BackgroundJobEntry {
|
|
27
|
+
jobId: string;
|
|
28
|
+
sessionId: string;
|
|
29
|
+
/** Human description (e.g. "Generating video: <prompt>"). Shown to the goal
|
|
30
|
+
* judge (running) and in the background panel (all). */
|
|
31
|
+
description: string;
|
|
32
|
+
status: BackgroundJobStatus;
|
|
33
|
+
startedAt: number;
|
|
34
|
+
/** Set when status flips to a terminal state. */
|
|
35
|
+
finishedAt?: number;
|
|
36
|
+
/** Result summary (video URL, DriveAgent final text) or error message. */
|
|
37
|
+
finalText?: string;
|
|
38
|
+
/** External CLI session id, when the job is a DriveAgent run — lets the host
|
|
39
|
+
* read that CLI's transcript to attribute file changes (#6). */
|
|
40
|
+
ccSessionId?: string;
|
|
41
|
+
/** Files the external agent changed (parsed from its transcript, #6). */
|
|
42
|
+
changedFiles?: string[];
|
|
43
|
+
}
|
|
44
|
+
/** Outcome passed to finish() to record how a job ended. */
|
|
45
|
+
export interface BackgroundJobOutcome {
|
|
46
|
+
status?: "completed" | "failed";
|
|
47
|
+
finalText?: string;
|
|
48
|
+
ccSessionId?: string;
|
|
49
|
+
changedFiles?: string[];
|
|
50
|
+
}
|
|
51
|
+
declare class BackgroundJobRegistry {
|
|
52
|
+
private jobs;
|
|
53
|
+
private listeners;
|
|
54
|
+
/** Register a running job. Invalid sessionId is ignored (cannot be waited on). */
|
|
55
|
+
start(jobId: string, sessionId: string, description?: string): void;
|
|
56
|
+
/** Mark a job terminal (retained, not deleted). Unknown id is a no-op (no
|
|
57
|
+
* notify) so a double-finish or a finish after reset stays quiet. */
|
|
58
|
+
finish(jobId: string, outcome?: BackgroundJobOutcome): void;
|
|
59
|
+
/** True while any RUNNING job spawned by `sessionId` remains. */
|
|
60
|
+
hasRunningForSession(sessionId: string): boolean;
|
|
61
|
+
/** Running jobs spawned by `sessionId`. Feeds the goal judge's task list. */
|
|
62
|
+
listRunningForSession(sessionId: string): BackgroundJobEntry[];
|
|
63
|
+
/** All jobs (running + retained terminal) for `sessionId`. Feeds the panel. */
|
|
64
|
+
listForSession(sessionId: string): BackgroundJobEntry[];
|
|
65
|
+
/** Drop every job of a session — called when the session is deleted/closed. */
|
|
66
|
+
dropForSession(sessionId: string): void;
|
|
67
|
+
subscribe: (cb: Listener) => (() => void);
|
|
68
|
+
/** Test helper: drop all tracked jobs. */
|
|
69
|
+
reset(): void;
|
|
70
|
+
/** Backstop: keep at most MAX_TERMINAL_JOBS_PER_SESSION terminal jobs per
|
|
71
|
+
* session, evicting the oldest (insertion order). Running jobs never evicted. */
|
|
72
|
+
private evictTerminalOverCap;
|
|
73
|
+
private notify;
|
|
74
|
+
}
|
|
75
|
+
export declare const backgroundJobRegistry: BackgroundJobRegistry;
|
|
76
|
+
export {};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry of NON-agent background jobs (GenerateVideo poll loops, DriveAgent
|
|
3
|
+
* external CLI runs). Mirrors the role `asyncAgentRegistry` plays for background
|
|
4
|
+
* sub-agents, but a job is not an agent — it has no transcript, no abort(), and
|
|
5
|
+
* must NOT show up in AgentStatus.
|
|
6
|
+
*
|
|
7
|
+
* The engine's wait-for-background loop parks the turn until a session has no
|
|
8
|
+
* more RUNNING jobs, so the goal-stop-hook doesn't force the model to busy-loop
|
|
9
|
+
* with `sleep` while a video renders (the s-mqe0ox7n-a8d11c26 bug).
|
|
10
|
+
*
|
|
11
|
+
* Finished jobs are RETAINED (status flips to completed/failed, result stored)
|
|
12
|
+
* so the background panel can show them + let the user open the result — they
|
|
13
|
+
* don't vanish the instant they complete (#2/#5). Retention is bounded:
|
|
14
|
+
* - event-driven: a session's jobs are dropped when the session is deleted
|
|
15
|
+
* (dropForSession, wired to session close/delete);
|
|
16
|
+
* - a generous per-session cap on TERMINAL jobs is a pure memory backstop for
|
|
17
|
+
* a session that spawns hundreds of jobs and is never deleted.
|
|
18
|
+
*
|
|
19
|
+
* Process-local singleton, same pattern as asyncAgentRegistry. The video/Drive
|
|
20
|
+
* loops run in the main engine process, so start/finish and the engine's wait
|
|
21
|
+
* loop observe the same instance.
|
|
22
|
+
*/
|
|
23
|
+
function isValidSessionId(sid) {
|
|
24
|
+
return typeof sid === "string" && sid.length > 0;
|
|
25
|
+
}
|
|
26
|
+
/** Generous per-session cap on retained terminal jobs — a leak backstop, not a
|
|
27
|
+
* UX limit; a human never spawns this many background jobs in one session. */
|
|
28
|
+
const MAX_TERMINAL_JOBS_PER_SESSION = 50;
|
|
29
|
+
class BackgroundJobRegistry {
|
|
30
|
+
jobs = new Map(); // jobId -> entry (insertion-ordered)
|
|
31
|
+
listeners = new Set();
|
|
32
|
+
/** Register a running job. Invalid sessionId is ignored (cannot be waited on). */
|
|
33
|
+
start(jobId, sessionId, description = "") {
|
|
34
|
+
if (!isValidSessionId(sessionId))
|
|
35
|
+
return;
|
|
36
|
+
this.jobs.set(jobId, {
|
|
37
|
+
jobId,
|
|
38
|
+
sessionId,
|
|
39
|
+
description,
|
|
40
|
+
status: "running",
|
|
41
|
+
startedAt: Date.now(),
|
|
42
|
+
});
|
|
43
|
+
this.notify();
|
|
44
|
+
}
|
|
45
|
+
/** Mark a job terminal (retained, not deleted). Unknown id is a no-op (no
|
|
46
|
+
* notify) so a double-finish or a finish after reset stays quiet. */
|
|
47
|
+
finish(jobId, outcome) {
|
|
48
|
+
const entry = this.jobs.get(jobId);
|
|
49
|
+
if (!entry)
|
|
50
|
+
return;
|
|
51
|
+
entry.status = outcome?.status ?? "completed";
|
|
52
|
+
entry.finishedAt = Date.now();
|
|
53
|
+
if (outcome?.finalText !== undefined)
|
|
54
|
+
entry.finalText = outcome.finalText;
|
|
55
|
+
if (outcome?.ccSessionId !== undefined)
|
|
56
|
+
entry.ccSessionId = outcome.ccSessionId;
|
|
57
|
+
if (outcome?.changedFiles !== undefined)
|
|
58
|
+
entry.changedFiles = outcome.changedFiles;
|
|
59
|
+
this.evictTerminalOverCap(entry.sessionId);
|
|
60
|
+
this.notify();
|
|
61
|
+
}
|
|
62
|
+
/** True while any RUNNING job spawned by `sessionId` remains. */
|
|
63
|
+
hasRunningForSession(sessionId) {
|
|
64
|
+
for (const e of this.jobs.values()) {
|
|
65
|
+
if (e.sessionId === sessionId && e.status === "running")
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
/** Running jobs spawned by `sessionId`. Feeds the goal judge's task list. */
|
|
71
|
+
listRunningForSession(sessionId) {
|
|
72
|
+
return [...this.jobs.values()].filter((e) => e.sessionId === sessionId && e.status === "running");
|
|
73
|
+
}
|
|
74
|
+
/** All jobs (running + retained terminal) for `sessionId`. Feeds the panel. */
|
|
75
|
+
listForSession(sessionId) {
|
|
76
|
+
return [...this.jobs.values()].filter((e) => e.sessionId === sessionId);
|
|
77
|
+
}
|
|
78
|
+
/** Drop every job of a session — called when the session is deleted/closed. */
|
|
79
|
+
dropForSession(sessionId) {
|
|
80
|
+
let removed = false;
|
|
81
|
+
for (const [id, e] of this.jobs) {
|
|
82
|
+
if (e.sessionId === sessionId) {
|
|
83
|
+
this.jobs.delete(id);
|
|
84
|
+
removed = true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (removed)
|
|
88
|
+
this.notify();
|
|
89
|
+
}
|
|
90
|
+
subscribe = (cb) => {
|
|
91
|
+
this.listeners.add(cb);
|
|
92
|
+
return () => {
|
|
93
|
+
this.listeners.delete(cb);
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
/** Test helper: drop all tracked jobs. */
|
|
97
|
+
reset() {
|
|
98
|
+
this.jobs.clear();
|
|
99
|
+
}
|
|
100
|
+
/** Backstop: keep at most MAX_TERMINAL_JOBS_PER_SESSION terminal jobs per
|
|
101
|
+
* session, evicting the oldest (insertion order). Running jobs never evicted. */
|
|
102
|
+
evictTerminalOverCap(sessionId) {
|
|
103
|
+
const terminal = [...this.jobs.values()].filter((e) => e.sessionId === sessionId && e.status !== "running");
|
|
104
|
+
const over = terminal.length - MAX_TERMINAL_JOBS_PER_SESSION;
|
|
105
|
+
if (over <= 0)
|
|
106
|
+
return;
|
|
107
|
+
// Map iteration is insertion order → the first `over` terminal entries are
|
|
108
|
+
// the oldest.
|
|
109
|
+
for (let i = 0; i < over; i++) {
|
|
110
|
+
this.jobs.delete(terminal[i].jobId);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
notify() {
|
|
114
|
+
for (const cb of this.listeners) {
|
|
115
|
+
try {
|
|
116
|
+
cb();
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// isolate per-listener errors
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export const backgroundJobRegistry = new BackgroundJobRegistry();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background-shell tools — BashOutput / KillShell / ListShells.
|
|
3
|
+
*
|
|
4
|
+
* Companions to `Bash(run_in_background=true)` (design §5.2–5.4). They
|
|
5
|
+
* operate on the {@link BackgroundShellManager} (threaded via
|
|
6
|
+
* `ctx.backgroundShells`, falling back to the process-local singleton).
|
|
7
|
+
*
|
|
8
|
+
* All three are session-scoped: a shell started in session A is invisible
|
|
9
|
+
* and unreachable from session B (the manager enforces this; we pass
|
|
10
|
+
* `ctx.sessionId` so cross-session ids report a plain "unknown shell_id"
|
|
11
|
+
* rather than leaking another session's processes).
|
|
12
|
+
*/
|
|
13
|
+
import type { ToolContext } from "../context.js";
|
|
14
|
+
import type { ToolDefinition } from "../../types.js";
|
|
15
|
+
export declare const bashOutputToolDef: ToolDefinition;
|
|
16
|
+
export declare function bashOutputTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string>;
|
|
17
|
+
export declare const killShellToolDef: ToolDefinition;
|
|
18
|
+
export declare function killShellTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string>;
|
|
19
|
+
export declare const listShellsToolDef: ToolDefinition;
|
|
20
|
+
export declare function listShellsTool(_args: Record<string, unknown>, ctx?: ToolContext): Promise<string>;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background-shell tools — BashOutput / KillShell / ListShells.
|
|
3
|
+
*
|
|
4
|
+
* Companions to `Bash(run_in_background=true)` (design §5.2–5.4). They
|
|
5
|
+
* operate on the {@link BackgroundShellManager} (threaded via
|
|
6
|
+
* `ctx.backgroundShells`, falling back to the process-local singleton).
|
|
7
|
+
*
|
|
8
|
+
* All three are session-scoped: a shell started in session A is invisible
|
|
9
|
+
* and unreachable from session B (the manager enforces this; we pass
|
|
10
|
+
* `ctx.sessionId` so cross-session ids report a plain "unknown shell_id"
|
|
11
|
+
* rather than leaking another session's processes).
|
|
12
|
+
*/
|
|
13
|
+
import { backgroundShellManager } from "../../runtime/background-shell.js";
|
|
14
|
+
function managerFor(ctx) {
|
|
15
|
+
return ctx?.backgroundShells ?? backgroundShellManager;
|
|
16
|
+
}
|
|
17
|
+
// ─── BashOutput ──────────────────────────────────────────────────────
|
|
18
|
+
export const bashOutputToolDef = {
|
|
19
|
+
name: "BashOutput",
|
|
20
|
+
description: "Read output from a background shell started via Bash(run_in_background=true). " +
|
|
21
|
+
"Returns new output since your last read (incremental by default). ANSI colors and progress bars are stripped. " +
|
|
22
|
+
"Use this only for an on-demand peek at a STILL-RUNNING process — you do NOT need to poll it to wait for " +
|
|
23
|
+
"completion: when the background command finishes you are automatically woken with the result, so end your " +
|
|
24
|
+
"turn instead of looping Sleep + BashOutput.",
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: {
|
|
28
|
+
shell_id: {
|
|
29
|
+
type: "string",
|
|
30
|
+
description: "The shell_id returned by Bash(run_in_background=true).",
|
|
31
|
+
},
|
|
32
|
+
mode: {
|
|
33
|
+
type: "string",
|
|
34
|
+
enum: ["incremental", "all"],
|
|
35
|
+
description: "incremental (default): output since your last read. all: the full retained buffer.",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
required: ["shell_id"],
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
export async function bashOutputTool(args, ctx) {
|
|
42
|
+
const shellId = args.shell_id;
|
|
43
|
+
if (!shellId)
|
|
44
|
+
return "Error: shell_id is required";
|
|
45
|
+
const mode = args.mode === "all" ? "all" : "incremental";
|
|
46
|
+
const res = managerFor(ctx).readOutput(shellId, mode, ctx?.sessionId);
|
|
47
|
+
if (!res.ok)
|
|
48
|
+
return `Error: ${res.error}`;
|
|
49
|
+
const body = res.text.length > 0 ? res.text : "(no new output)";
|
|
50
|
+
return `${res.header}\n${body}`;
|
|
51
|
+
}
|
|
52
|
+
// ─── KillShell ───────────────────────────────────────────────────────
|
|
53
|
+
export const killShellToolDef = {
|
|
54
|
+
name: "KillShell",
|
|
55
|
+
description: "Terminate a background shell (and its whole process group) started via Bash(run_in_background=true).",
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: "object",
|
|
58
|
+
properties: {
|
|
59
|
+
shell_id: { type: "string", description: "The shell_id to terminate." },
|
|
60
|
+
},
|
|
61
|
+
required: ["shell_id"],
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
export async function killShellTool(args, ctx) {
|
|
65
|
+
const shellId = args.shell_id;
|
|
66
|
+
if (!shellId)
|
|
67
|
+
return "Error: shell_id is required";
|
|
68
|
+
const res = await managerFor(ctx).kill(shellId, ctx?.sessionId);
|
|
69
|
+
if (!res.ok)
|
|
70
|
+
return `Error: ${res.error}`;
|
|
71
|
+
if (res.alreadyExited) {
|
|
72
|
+
return `Background shell ${shellId} had already exited (status=${res.status}).`;
|
|
73
|
+
}
|
|
74
|
+
return `Background shell ${shellId} terminated (status=${res.status}).`;
|
|
75
|
+
}
|
|
76
|
+
// ─── ListShells ──────────────────────────────────────────────────────
|
|
77
|
+
export const listShellsToolDef = {
|
|
78
|
+
name: "ListShells",
|
|
79
|
+
description: "List background shells for the current session, with status and detected port.",
|
|
80
|
+
inputSchema: { type: "object", properties: {} },
|
|
81
|
+
};
|
|
82
|
+
function ago(ms) {
|
|
83
|
+
const s = Math.max(0, Math.round((Date.now() - ms) / 1000));
|
|
84
|
+
if (s < 60)
|
|
85
|
+
return `${s}s ago`;
|
|
86
|
+
const m = Math.round(s / 60);
|
|
87
|
+
if (m < 60)
|
|
88
|
+
return `${m}m ago`;
|
|
89
|
+
return `${Math.round(m / 60)}h ago`;
|
|
90
|
+
}
|
|
91
|
+
export async function listShellsTool(_args, ctx) {
|
|
92
|
+
const sessionId = ctx?.sessionId;
|
|
93
|
+
if (!sessionId)
|
|
94
|
+
return "No background shells (no session context).";
|
|
95
|
+
const shells = managerFor(ctx).listForSession(sessionId);
|
|
96
|
+
if (shells.length === 0)
|
|
97
|
+
return "No background shells for this session.";
|
|
98
|
+
const rows = shells.map((s) => {
|
|
99
|
+
const portPart = s.detectedPort !== undefined ? ` port=${s.detectedPort}` : "";
|
|
100
|
+
const exitPart = s.status === "exited"
|
|
101
|
+
? s.signal
|
|
102
|
+
? ` signal=${s.signal}`
|
|
103
|
+
: ` exit=${s.exitCode ?? "?"}`
|
|
104
|
+
: "";
|
|
105
|
+
return `${s.shellId} ${s.status}${portPart}${exitPart} ${s.command} (started ${ago(s.startedAt)})`;
|
|
106
|
+
});
|
|
107
|
+
return rows.join("\n");
|
|
108
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified view over the THREE kinds of async background work — background
|
|
3
|
+
* sub-agents (asyncAgentRegistry), background jobs / video polls
|
|
4
|
+
* (backgroundJobRegistry), and background shells (backgroundShellManager).
|
|
5
|
+
*
|
|
6
|
+
* Used by the goal-stop-hook to show the judge LLM what's still running, so it
|
|
7
|
+
* can tell "the goal is done except for a finite task that will wake me" (a
|
|
8
|
+
* download / video render → allow stop, wait for the wakeup) from "a long-lived
|
|
9
|
+
* service that the goal doesn't depend on" (a dev server → judge the goal
|
|
10
|
+
* normally). Replacing the old mechanical `hasRunningForSession` short-circuit:
|
|
11
|
+
* a boolean can't distinguish a finite download from a never-ending dev server,
|
|
12
|
+
* but the judge — given each task's kind + command — can.
|
|
13
|
+
*/
|
|
14
|
+
import { type AsyncAgentStatus } from "./agent-registry.js";
|
|
15
|
+
import { type BackgroundJobStatus } from "./background-jobs.js";
|
|
16
|
+
import { type BgShell } from "../../runtime/background-shell.js";
|
|
17
|
+
export type BackgroundWorkKind = "subagent" | "job" | "shell";
|
|
18
|
+
export interface BackgroundWorkItem {
|
|
19
|
+
kind: BackgroundWorkKind;
|
|
20
|
+
/** Human description for the judge (sub-agent task / video prompt / shell command). */
|
|
21
|
+
description: string;
|
|
22
|
+
/**
|
|
23
|
+
* For kind === "shell": a port the shell is listening on, if detected. A
|
|
24
|
+
* listening port is a strong signal the shell is a long-lived service (dev
|
|
25
|
+
* server) rather than a finite task — fed to the judge so it doesn't mistake
|
|
26
|
+
* `make serve` / an opaquely-named server script for something to "wait on".
|
|
27
|
+
*/
|
|
28
|
+
detectedPort?: number;
|
|
29
|
+
}
|
|
30
|
+
/** List every still-running background work item spawned by `sessionId`. */
|
|
31
|
+
export declare function listRunningBackgroundWork(sessionId: string): BackgroundWorkItem[];
|
|
32
|
+
/** One background-work row for the desktop panel, discriminated by `kind`. */
|
|
33
|
+
export type BackgroundWorkEntry = {
|
|
34
|
+
kind: "shell";
|
|
35
|
+
/** Full shell snapshot — the panel already renders this shape (output/kill
|
|
36
|
+
* still go through the dedicated agent/backgroundShells RPC by shellId). */
|
|
37
|
+
shell: BgShell;
|
|
38
|
+
} | {
|
|
39
|
+
kind: "subagent";
|
|
40
|
+
agentId: string;
|
|
41
|
+
name?: string;
|
|
42
|
+
agentType?: string;
|
|
43
|
+
description: string;
|
|
44
|
+
status: AsyncAgentStatus;
|
|
45
|
+
startedAt: number;
|
|
46
|
+
finishedAt?: number;
|
|
47
|
+
} | {
|
|
48
|
+
kind: "job";
|
|
49
|
+
jobId: string;
|
|
50
|
+
description: string;
|
|
51
|
+
status: BackgroundJobStatus;
|
|
52
|
+
startedAt: number;
|
|
53
|
+
finishedAt?: number;
|
|
54
|
+
/** Result summary / error, once finished. */
|
|
55
|
+
finalText?: string;
|
|
56
|
+
/** Files an external agent (DriveAgent) changed, parsed from its transcript. */
|
|
57
|
+
changedFiles?: string[];
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Every background-work item spawned by `sessionId`, with per-kind detail, for
|
|
61
|
+
* the desktop panel. Includes finished sub-agents that are still within their
|
|
62
|
+
* fade window (so a just-completed agent doesn't vanish before the user sees
|
|
63
|
+
* it); shells carry their own terminal status and the panel decides how long to
|
|
64
|
+
* keep them. Jobs are only ever present while running (the registry drops them
|
|
65
|
+
* on finish).
|
|
66
|
+
*/
|
|
67
|
+
export declare function listBackgroundWorkForUI(sessionId: string): BackgroundWorkEntry[];
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified view over the THREE kinds of async background work — background
|
|
3
|
+
* sub-agents (asyncAgentRegistry), background jobs / video polls
|
|
4
|
+
* (backgroundJobRegistry), and background shells (backgroundShellManager).
|
|
5
|
+
*
|
|
6
|
+
* Used by the goal-stop-hook to show the judge LLM what's still running, so it
|
|
7
|
+
* can tell "the goal is done except for a finite task that will wake me" (a
|
|
8
|
+
* download / video render → allow stop, wait for the wakeup) from "a long-lived
|
|
9
|
+
* service that the goal doesn't depend on" (a dev server → judge the goal
|
|
10
|
+
* normally). Replacing the old mechanical `hasRunningForSession` short-circuit:
|
|
11
|
+
* a boolean can't distinguish a finite download from a never-ending dev server,
|
|
12
|
+
* but the judge — given each task's kind + command — can.
|
|
13
|
+
*/
|
|
14
|
+
import { asyncAgentRegistry } from "./agent-registry.js";
|
|
15
|
+
import { backgroundJobRegistry } from "./background-jobs.js";
|
|
16
|
+
import { backgroundShellManager } from "../../runtime/background-shell.js";
|
|
17
|
+
/** List every still-running background work item spawned by `sessionId`. */
|
|
18
|
+
export function listRunningBackgroundWork(sessionId) {
|
|
19
|
+
const items = [];
|
|
20
|
+
for (const a of asyncAgentRegistry.listForSession(sessionId)) {
|
|
21
|
+
if (a.status === "running") {
|
|
22
|
+
items.push({
|
|
23
|
+
kind: "subagent",
|
|
24
|
+
description: a.description || a.name || a.agentType || "(background sub-agent)",
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
for (const j of backgroundJobRegistry.listRunningForSession(sessionId)) {
|
|
29
|
+
items.push({ kind: "job", description: j.description || "(background job)" });
|
|
30
|
+
}
|
|
31
|
+
for (const s of backgroundShellManager.listForSession(sessionId)) {
|
|
32
|
+
if (s.status === "running" || s.status === "starting") {
|
|
33
|
+
items.push({
|
|
34
|
+
kind: "shell",
|
|
35
|
+
description: s.command,
|
|
36
|
+
...(s.detectedPort != null ? { detectedPort: s.detectedPort } : {}),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return items;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Every background-work item spawned by `sessionId`, with per-kind detail, for
|
|
44
|
+
* the desktop panel. Includes finished sub-agents that are still within their
|
|
45
|
+
* fade window (so a just-completed agent doesn't vanish before the user sees
|
|
46
|
+
* it); shells carry their own terminal status and the panel decides how long to
|
|
47
|
+
* keep them. Jobs are only ever present while running (the registry drops them
|
|
48
|
+
* on finish).
|
|
49
|
+
*/
|
|
50
|
+
export function listBackgroundWorkForUI(sessionId) {
|
|
51
|
+
const entries = [];
|
|
52
|
+
for (const s of backgroundShellManager.listForSession(sessionId)) {
|
|
53
|
+
entries.push({ kind: "shell", shell: s });
|
|
54
|
+
}
|
|
55
|
+
const now = Date.now();
|
|
56
|
+
for (const a of asyncAgentRegistry.listForSession(sessionId)) {
|
|
57
|
+
// Keep running agents, plus finished ones still inside their fade window so
|
|
58
|
+
// a completion is briefly visible. (finishedFadeAt = finishedAt + 30s.)
|
|
59
|
+
const fresh = a.status === "running" || (a.finishedFadeAt != null && a.finishedFadeAt > now);
|
|
60
|
+
if (!fresh)
|
|
61
|
+
continue;
|
|
62
|
+
entries.push({
|
|
63
|
+
kind: "subagent",
|
|
64
|
+
agentId: a.agentId,
|
|
65
|
+
name: a.name,
|
|
66
|
+
agentType: a.agentType,
|
|
67
|
+
description: a.description,
|
|
68
|
+
status: a.status,
|
|
69
|
+
startedAt: a.startedAt,
|
|
70
|
+
finishedAt: a.finishedAt,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
for (const j of backgroundJobRegistry.listForSession(sessionId)) {
|
|
74
|
+
entries.push({
|
|
75
|
+
kind: "job",
|
|
76
|
+
jobId: j.jobId,
|
|
77
|
+
description: j.description,
|
|
78
|
+
status: j.status,
|
|
79
|
+
startedAt: j.startedAt,
|
|
80
|
+
...(j.finishedAt != null ? { finishedAt: j.finishedAt } : {}),
|
|
81
|
+
...(j.finalText != null ? { finalText: j.finalText } : {}),
|
|
82
|
+
...(j.changedFiles && j.changedFiles.length ? { changedFiles: j.changedFiles } : {}),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return entries;
|
|
86
|
+
}
|