@cjhyy/code-shell-core 0.5.0-rc.2 → 0.6.0-rc.2
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 +56 -0
- package/dist/context/compaction.js +186 -0
- package/dist/context/manager.js +25 -4
- 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 +267 -138
- package/dist/engine/engine.js +957 -191
- 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 +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/resolve-llm-config.d.ts +16 -0
- package/dist/engine/resolve-llm-config.js +44 -0
- package/dist/engine/runtime.js +2 -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 +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 +67 -3
- package/dist/engine/turn-loop.js +292 -41
- 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.js +11 -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 +49 -6
- package/dist/index.js +60 -4
- 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 +4 -0
- 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 +14 -0
- package/dist/llm/model-pool.js +13 -0
- 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 +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 +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 +7 -1
- package/dist/preset/index.js +96 -6
- package/dist/product/types.d.ts +1 -1
- package/dist/prompt/composer.d.ts +27 -0
- package/dist/prompt/composer.js +67 -21
- 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/protocol/chat-session-manager.d.ts +8 -2
- package/dist/protocol/chat-session-manager.js +36 -0
- package/dist/protocol/chat-session.d.ts +65 -0
- package/dist/protocol/chat-session.js +94 -2
- package/dist/protocol/client.d.ts +27 -0
- package/dist/protocol/client.js +38 -0
- package/dist/protocol/server.d.ts +112 -0
- package/dist/protocol/server.js +586 -16
- package/dist/protocol/transport.js +3 -2
- package/dist/protocol/types.d.ts +46 -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 +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/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 +113 -3
- 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 +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 +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/config.js +7 -0
- package/dist/tool-system/builtin/cron.d.ts +7 -0
- package/dist/tool-system/builtin/cron.js +55 -3
- 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.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 +361 -0
- package/dist/tool-system/builtin/glob.js +0 -7
- package/dist/tool-system/builtin/grep.js +6 -8
- 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 +7 -1
- package/dist/tool-system/builtin/index.js +249 -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.js +0 -7
- package/dist/tool-system/builtin/powershell.js +4 -1
- 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 +3 -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 +154 -6
- package/dist/tool-system/executor.d.ts +3 -1
- package/dist/tool-system/executor.js +172 -88
- 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 +186 -20
- 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 +36 -1
- package/dist/tool-system/permission.js +281 -69
- 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/validate-tool-metadata.d.ts +36 -0
- package/dist/tool-system/validate-tool-metadata.js +63 -0
- package/dist/types.d.ts +217 -6
- 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/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
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
UI-agnostic agent orchestration framework. The headless core of [`code-shell`](https://github.com/cjhyy/codeshell): turn an LLM + a tool registry + a transcript into a multi-turn agent loop, with permissions, hooks, MCP support, and pluggable approval backends.
|
|
4
4
|
|
|
5
|
-
Compatible with Anthropic and OpenAI-protocol providers (Claude, DeepSeek, GPT, Gemini, Qwen, …). No terminal UI dependencies — embed it in a CLI, a web service, a Slack bot, a desktop app.
|
|
5
|
+
Compatible with Anthropic and OpenAI-protocol providers (Claude, DeepSeek, GPT, Gemini, Qwen, …). No terminal UI dependencies — embed it in a CLI, a web service, a Slack bot, a desktop app. This is a Node.js runtime package, not a browser SDK.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ npm install @cjhyy/code-shell-core
|
|
|
12
12
|
bun add @cjhyy/code-shell-core
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
Requires Node ≥ 20.10.
|
|
15
|
+
Requires Node ≥ 20.10 and an ESM-capable runtime.
|
|
16
16
|
|
|
17
17
|
## Quickstart
|
|
18
18
|
|
|
@@ -29,8 +29,10 @@ const engine = new Engine({
|
|
|
29
29
|
apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
30
30
|
},
|
|
31
31
|
cwd: process.cwd(),
|
|
32
|
-
// Headless approval — "approve-all" trusts the model fully
|
|
33
|
-
// "approve-read-only"
|
|
32
|
+
// Headless approval — "approve-all" trusts the model fully and is
|
|
33
|
+
// only appropriate for local/trusted prompts. Use "approve-read-only",
|
|
34
|
+
// explicit permissionMode/tool allowlists, or a custom ApprovalBackend
|
|
35
|
+
// in production.
|
|
34
36
|
approvalBackend: new HeadlessApprovalBackend("approve-all"),
|
|
35
37
|
headless: true,
|
|
36
38
|
});
|
|
@@ -78,7 +80,7 @@ new Engine({
|
|
|
78
80
|
|
|
79
81
|
## Designing for production
|
|
80
82
|
|
|
81
|
-
- **Approval backend.** `HeadlessApprovalBackend("approve-all")` is fine for trusted prompts. For untrusted input, implement `ApprovalBackend` to gate destructive tools (Write/Edit/Bash) through your own auth.
|
|
83
|
+
- **Approval backend.** `HeadlessApprovalBackend("approve-all")` is fine only for local/trusted prompts. For untrusted input, explicitly set `permissionMode`, restrict tools with `enabledBuiltinTools` / `disabledBuiltinTools`, or implement `ApprovalBackend` to gate destructive tools (Write/Edit/Bash) through your own auth.
|
|
82
84
|
- **Permission rules.** Pass `permissionRules` to the engine for fine-grained allow/deny per tool + args pattern. Bash commands additionally get a built-in safety classifier (dangerous commands like `rm -rf` always require explicit approval).
|
|
83
85
|
- **Hooks** are the right place for audit logging, prompt rewriting, and policy enforcement.
|
|
84
86
|
- **Memory.** The engine ships with an extract-memories + auto-dream pipeline that persists to `~/.code-shell/memory/`. Disable by passing a no-op `MemoryOrchestrator`, or scope to a project by setting `CODE_SHELL_HOME`.
|
|
@@ -144,11 +146,12 @@ These exports are covered by the stability promise:
|
|
|
144
146
|
|
|
145
147
|
### Treat as internal
|
|
146
148
|
|
|
147
|
-
Anything not in the table above — including
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
Anything not in the table above — including root-entry exports for
|
|
150
|
+
product/runtime helpers and direct imports from `./engine/...`,
|
|
151
|
+
`./tool-system/...`, `./session/...`, `./hooks/...` subpaths — is
|
|
152
|
+
experimental/internal unless explicitly documented otherwise, and may
|
|
153
|
+
change between minor versions without notice. Plan to migrate via the
|
|
154
|
+
protocol surface instead of deep-importing.
|
|
152
155
|
|
|
153
156
|
## Stability
|
|
154
157
|
|
|
@@ -2,6 +2,9 @@ import { type AgentDefinition } from "./agent-definition.js";
|
|
|
2
2
|
export interface AgentSourceDir {
|
|
3
3
|
dir: string;
|
|
4
4
|
source: "project" | "user" | "plugin";
|
|
5
|
+
/** For source === "plugin": the owning plugin name, carried onto each def
|
|
6
|
+
* so the spawn layer can namespace bare skill allowlists. */
|
|
7
|
+
pluginName?: string;
|
|
5
8
|
}
|
|
6
9
|
/**
|
|
7
10
|
* Loads reusable sub-agent role definitions from one or more directories
|
|
@@ -24,7 +24,7 @@ export class AgentDefinitionRegistry {
|
|
|
24
24
|
}
|
|
25
25
|
static loadFromDirs(dirs, disabled) {
|
|
26
26
|
const reg = new AgentDefinitionRegistry();
|
|
27
|
-
for (const { dir, source } of dirs) {
|
|
27
|
+
for (const { dir, source, pluginName } of dirs) {
|
|
28
28
|
if (!existsSync(dir) || !statSync(dir).isDirectory())
|
|
29
29
|
continue;
|
|
30
30
|
for (const entry of readdirSync(dir).sort()) {
|
|
@@ -36,6 +36,8 @@ export class AgentDefinitionRegistry {
|
|
|
36
36
|
continue;
|
|
37
37
|
const def = parseAgentDefinition(readFileSync(full, "utf8"), entry);
|
|
38
38
|
def.source = source;
|
|
39
|
+
if (pluginName)
|
|
40
|
+
def.pluginName = pluginName;
|
|
39
41
|
def.filePath = full;
|
|
40
42
|
// A later dir overriding an earlier one. Carry forward the shadowed
|
|
41
43
|
// sources (the prev def's own source + anything it already shadowed)
|
|
@@ -10,10 +10,25 @@ export interface AgentDefinition {
|
|
|
10
10
|
maxTurns?: number;
|
|
11
11
|
/** Optional tool allowlist. Undefined → inherit parent's full tool set. */
|
|
12
12
|
tools?: string[];
|
|
13
|
+
/**
|
|
14
|
+
* Optional skill allowlist. Undefined → inherit the parent's full skill
|
|
15
|
+
* pool (every non-disabled skill in the project). When set, the sub-agent
|
|
16
|
+
* physically only sees these skills: they're the only ones listed in its
|
|
17
|
+
* system prompt and the only ones it may invoke. Empty array → no skills.
|
|
18
|
+
*/
|
|
19
|
+
skills?: string[];
|
|
13
20
|
/** Markdown body — becomes the child Engine's appendSystemPrompt. */
|
|
14
21
|
systemPrompt: string;
|
|
15
22
|
/** Where this def was loaded from. Runtime-only; never serialized. */
|
|
16
23
|
source?: "project" | "user" | "plugin";
|
|
24
|
+
/**
|
|
25
|
+
* Owning plugin name when source === "plugin" (e.g. "mimi-video"). Runtime-
|
|
26
|
+
* only; never serialized. Used to namespace this agent's bare skill
|
|
27
|
+
* allowlist into `<pluginName>:<skill>` at spawn time, since a plugin's
|
|
28
|
+
* frontmatter references its own skills by bare name (CC convention) but
|
|
29
|
+
* the scanner registers them namespaced. Undefined for project/user agents.
|
|
30
|
+
*/
|
|
31
|
+
pluginName?: string;
|
|
17
32
|
/** Absolute path of the file it came from. Runtime-only. */
|
|
18
33
|
filePath?: string;
|
|
19
34
|
/** True when this def shadows a same-named def from a lower-priority dir. */
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
2
|
+
/**
|
|
3
|
+
* Normalize a frontmatter `tools:` / `skills:` value into a string[].
|
|
4
|
+
* Accepts both a YAML list (`[a, b]`) and a comma/whitespace-separated
|
|
5
|
+
* string (`a, b`) — CC-lineage agent files use either form. Returns
|
|
6
|
+
* undefined when the field is absent or not a usable shape, so the caller
|
|
7
|
+
* keeps the "inherit parent" default.
|
|
8
|
+
*/
|
|
9
|
+
function normalizeNameList(value) {
|
|
10
|
+
if (Array.isArray(value)) {
|
|
11
|
+
return value.filter((t) => typeof t === "string");
|
|
12
|
+
}
|
|
13
|
+
if (typeof value === "string") {
|
|
14
|
+
return value
|
|
15
|
+
.split(/[,\s]+/)
|
|
16
|
+
.map((s) => s.trim())
|
|
17
|
+
.filter((s) => s.length > 0);
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
2
21
|
/**
|
|
3
22
|
* Parse a Markdown agent-definition file (YAML frontmatter + body).
|
|
4
23
|
* Pure: no filesystem access. `sourceName` is only used in error messages.
|
|
@@ -31,9 +50,12 @@ export function parseAgentDefinition(raw, sourceName) {
|
|
|
31
50
|
def.model = fm.model.trim();
|
|
32
51
|
if (typeof fm.maxTurns === "number")
|
|
33
52
|
def.maxTurns = fm.maxTurns;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
53
|
+
const tools = normalizeNameList(fm.tools);
|
|
54
|
+
if (tools !== undefined)
|
|
55
|
+
def.tools = tools;
|
|
56
|
+
const skills = normalizeNameList(fm.skills);
|
|
57
|
+
if (skills !== undefined)
|
|
58
|
+
def.skills = skills;
|
|
37
59
|
return def;
|
|
38
60
|
}
|
|
39
61
|
/**
|
|
@@ -54,6 +76,8 @@ export function serializeAgentDefinition(def) {
|
|
|
54
76
|
fm.maxTurns = def.maxTurns;
|
|
55
77
|
if (def.tools !== undefined)
|
|
56
78
|
fm.tools = def.tools;
|
|
79
|
+
if (def.skills !== undefined)
|
|
80
|
+
fm.skills = def.skills;
|
|
57
81
|
const yaml = stringifyYaml(fm).trimEnd();
|
|
58
82
|
return `---\n${yaml}\n---\n${def.systemPrompt}\n`;
|
|
59
83
|
}
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* to prevent command injection. File paths are validated against the
|
|
7
7
|
* repository boundary to prevent path traversal.
|
|
8
8
|
*/
|
|
9
|
-
import { readFileSync, existsSync, readdirSync, statSync } from "node:fs";
|
|
9
|
+
import { readFileSync, existsSync, readdirSync, statSync, realpathSync } from "node:fs";
|
|
10
10
|
import { execFileSync } from "node:child_process";
|
|
11
|
-
import { resolve, join } from "node:path";
|
|
11
|
+
import { resolve, join, dirname, basename } from "node:path";
|
|
12
12
|
import { isWithinRoot } from "./within-root.js";
|
|
13
13
|
export const MAX_TOOL_RESULT = 15_000; // chars per tool call
|
|
14
14
|
// 5 was too generous for thinking-mode models — they'd spend the
|
|
@@ -113,7 +113,41 @@ export function executeContextTool(tc) {
|
|
|
113
113
|
*/
|
|
114
114
|
function validatePath(filePath) {
|
|
115
115
|
const resolved = resolve(filePath);
|
|
116
|
-
|
|
116
|
+
// Realpath both sides so a symlink planted inside the repo that points OUTSIDE
|
|
117
|
+
// can't pass a lexical containment check (isWithinRoot assumes resolved input
|
|
118
|
+
// and won't catch a link escape on its own). Matches fs-service's discipline.
|
|
119
|
+
// Reads are read-only, but a contained-looking symlink would still leak an
|
|
120
|
+
// out-of-repo file into arena context. Fall back to the lexical check when the
|
|
121
|
+
// target doesn't exist yet (a read of a missing file fails downstream anyway).
|
|
122
|
+
let realRoot;
|
|
123
|
+
try {
|
|
124
|
+
realRoot = realpathSync(REPO_ROOT);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
realRoot = REPO_ROOT;
|
|
128
|
+
}
|
|
129
|
+
let realTarget;
|
|
130
|
+
try {
|
|
131
|
+
realTarget = realpathSync(resolved);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Target doesn't exist yet. Compare REAL-vs-REAL by resolving the existing
|
|
135
|
+
// parent dir's realpath and rejoining the missing leaf — comparing realRoot
|
|
136
|
+
// (a real path) against a lexical `resolved` would false-reject valid files
|
|
137
|
+
// whenever REPO_ROOT (or any ancestor) is reached through a symlink.
|
|
138
|
+
let realParent;
|
|
139
|
+
try {
|
|
140
|
+
realParent = realpathSync(dirname(resolved));
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Parent missing too — no realpath available; fall back to lexical-vs-lexical
|
|
144
|
+
// so both operands share the same (unresolved) mode.
|
|
145
|
+
return isWithinRoot(resolve(REPO_ROOT), resolved) ? resolved : null;
|
|
146
|
+
}
|
|
147
|
+
const candidate = join(realParent, basename(resolved));
|
|
148
|
+
return isWithinRoot(realRoot, candidate) ? candidate : null;
|
|
149
|
+
}
|
|
150
|
+
return isWithinRoot(realRoot, realTarget) ? realTarget : null;
|
|
117
151
|
}
|
|
118
152
|
function executeReadFile(args) {
|
|
119
153
|
const filePath = args.path;
|
|
@@ -124,7 +158,16 @@ function executeReadFile(args) {
|
|
|
124
158
|
const raw = readFileSync(filePath, "utf-8");
|
|
125
159
|
const lines = raw.split("\n");
|
|
126
160
|
const offset = Math.max(0, (args.offset ?? 1) - 1);
|
|
127
|
-
|
|
161
|
+
// Guard `limit` before slicing: a negative limit makes lines.slice count
|
|
162
|
+
// "from the end" and silently drop the file's tail (reader thinks the file is
|
|
163
|
+
// shorter). A missing/NaN limit falls back to the default; a non-positive
|
|
164
|
+
// limit reads nothing.
|
|
165
|
+
const rawLimit = args.limit;
|
|
166
|
+
const limit = rawLimit === undefined || !Number.isFinite(rawLimit)
|
|
167
|
+
? 200
|
|
168
|
+
: rawLimit > 0
|
|
169
|
+
? Math.floor(rawLimit)
|
|
170
|
+
: 0;
|
|
128
171
|
const slice = lines.slice(offset, offset + limit);
|
|
129
172
|
const numbered = slice.map((l, i) => `${offset + i + 1}\t${l}`).join("\n");
|
|
130
173
|
return truncateResult(numbered);
|
package/dist/arena/ledger.js
CHANGED
|
@@ -68,11 +68,16 @@ export class ArenaLedger {
|
|
|
68
68
|
}
|
|
69
69
|
appendChallenge(challenge) {
|
|
70
70
|
this.ledger.challenges.push(challenge);
|
|
71
|
-
// Also add to the claim's challenges array
|
|
71
|
+
// Also add to the claim's challenges array. If the claimId doesn't resolve
|
|
72
|
+
// the challenge still lives in ledger.challenges but is invisible to any
|
|
73
|
+
// code reading claim.challenges directly — surface that inconsistency.
|
|
72
74
|
const claim = this.index.claimsById.get(challenge.claimId);
|
|
73
75
|
if (claim) {
|
|
74
76
|
claim.challenges.push(challenge);
|
|
75
77
|
}
|
|
78
|
+
else {
|
|
79
|
+
logger.warn("arena.challenge_for_unknown_claim", { claimId: challenge.claimId });
|
|
80
|
+
}
|
|
76
81
|
this.checkGrowth();
|
|
77
82
|
// Register any requested checks
|
|
78
83
|
if (challenge.requestedChecks) {
|
|
@@ -94,6 +99,9 @@ export class ArenaLedger {
|
|
|
94
99
|
if (claim) {
|
|
95
100
|
claim.adjudication = adjudication;
|
|
96
101
|
}
|
|
102
|
+
else {
|
|
103
|
+
logger.warn("arena.adjudication_for_unknown_claim", { claimId: adjudication.claimId });
|
|
104
|
+
}
|
|
97
105
|
}
|
|
98
106
|
// ─── Query operations ───────────────────────────────────────
|
|
99
107
|
/**
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
/** Shared utilities for arena strategy implementations. */
|
|
2
2
|
import type { ArenaBaseContext, FindingReview, ParticipantReport, ArenaConsensus, ArenaRoadmapPhaseDetail, ClaimRecord, ClaimChallenge, ClaimAdjudication, ClaimStatusSummary, DebateTurn, RoundResearchDigest } from "../types.js";
|
|
3
|
-
|
|
4
|
-
export declare function extractJSON(text: string): string;
|
|
5
|
-
/** Extract a JSON array from text */
|
|
6
|
-
export declare function extractJSONArray(text: string): string;
|
|
3
|
+
export { extractJSON, extractJSONArray } from "../../utils/json.js";
|
|
7
4
|
/** Format base context into a readable text block for LLM prompts */
|
|
8
5
|
export declare function formatBaseContext(ctx: ArenaBaseContext): string;
|
|
9
6
|
/** Format participant reports for cross-review or consensus prompts */
|
|
@@ -22,90 +22,13 @@ function sanitizeForPrompt(text) {
|
|
|
22
22
|
.replace(/^(?:IGNORE|DISREGARD|FORGET|OVERRIDE|SYSTEM|INSTRUCTION)[:\s].*/gim, "")
|
|
23
23
|
.slice(0, 3000);
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const start = text.indexOf("{");
|
|
33
|
-
if (start !== -1) {
|
|
34
|
-
let depth = 0;
|
|
35
|
-
let inString = false;
|
|
36
|
-
let escape = false;
|
|
37
|
-
for (let i = start; i < text.length; i++) {
|
|
38
|
-
const ch = text[i];
|
|
39
|
-
if (escape) {
|
|
40
|
-
escape = false;
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
if (ch === "\\") {
|
|
44
|
-
escape = true;
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
if (ch === '"') {
|
|
48
|
-
inString = !inString;
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (inString)
|
|
52
|
-
continue;
|
|
53
|
-
if (ch === "{")
|
|
54
|
-
depth++;
|
|
55
|
-
else if (ch === "}") {
|
|
56
|
-
depth--;
|
|
57
|
-
if (depth === 0)
|
|
58
|
-
return text.slice(start, i + 1);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
// Unbalanced — return from start to end as best effort
|
|
62
|
-
return text.slice(start);
|
|
63
|
-
}
|
|
64
|
-
return text;
|
|
65
|
-
}
|
|
66
|
-
/** Extract a JSON array from text */
|
|
67
|
-
export function extractJSONArray(text) {
|
|
68
|
-
const fenced = text.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
69
|
-
if (fenced)
|
|
70
|
-
return fenced[1].trim();
|
|
71
|
-
// Return the FIRST balanced top-level array. A greedy /\[[\s\S]*\]/ spanned
|
|
72
|
-
// to the last ']', merging two arrays (or trailing prose) into one invalid
|
|
73
|
-
// blob. Scan for bracket balance, ignoring brackets inside strings.
|
|
74
|
-
const balanced = firstBalancedArray(text);
|
|
75
|
-
if (balanced)
|
|
76
|
-
return balanced;
|
|
77
|
-
return text;
|
|
78
|
-
}
|
|
79
|
-
function firstBalancedArray(text) {
|
|
80
|
-
const start = text.indexOf("[");
|
|
81
|
-
if (start === -1)
|
|
82
|
-
return undefined;
|
|
83
|
-
let depth = 0;
|
|
84
|
-
let inString = false;
|
|
85
|
-
let escaped = false;
|
|
86
|
-
for (let i = start; i < text.length; i++) {
|
|
87
|
-
const ch = text[i];
|
|
88
|
-
if (inString) {
|
|
89
|
-
if (escaped)
|
|
90
|
-
escaped = false;
|
|
91
|
-
else if (ch === "\\")
|
|
92
|
-
escaped = true;
|
|
93
|
-
else if (ch === '"')
|
|
94
|
-
inString = false;
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
if (ch === '"')
|
|
98
|
-
inString = true;
|
|
99
|
-
else if (ch === "[")
|
|
100
|
-
depth++;
|
|
101
|
-
else if (ch === "]") {
|
|
102
|
-
depth--;
|
|
103
|
-
if (depth === 0)
|
|
104
|
-
return text.slice(start, i + 1);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return undefined; // unbalanced
|
|
108
|
-
}
|
|
25
|
+
// extractJSON / extractJSONArray are generic JSON extractors with no Arena
|
|
26
|
+
// dependency. They moved to utils/json.ts so non-Arena consumers (e.g.
|
|
27
|
+
// services/memory-orchestrator) can use them without importing this Arena
|
|
28
|
+
// module — that import made Arena a dependency of a core service. Re-exported
|
|
29
|
+
// here for back-compat with Arena's own callers and the existing test.
|
|
30
|
+
export { extractJSON, extractJSONArray } from "../../utils/json.js";
|
|
31
|
+
import { extractJSON, extractJSONArray } from "../../utils/json.js";
|
|
109
32
|
/** Format base context into a readable text block for LLM prompts */
|
|
110
33
|
export function formatBaseContext(ctx) {
|
|
111
34
|
const sections = [];
|
|
@@ -18,6 +18,16 @@ export interface ParsedCron {
|
|
|
18
18
|
daysOfMonth: Set<number>;
|
|
19
19
|
months: Set<number>;
|
|
20
20
|
daysOfWeek: Set<number>;
|
|
21
|
+
/**
|
|
22
|
+
* Whether the dayOfMonth / dayOfWeek FIELD was restricted (anything other
|
|
23
|
+
* than a bare "*"). Vixie-cron semantics: when BOTH day fields are
|
|
24
|
+
* restricted, a tick matches if EITHER matches (OR); when only one is
|
|
25
|
+
* restricted, the "*" one is ignored and the restricted one governs. We
|
|
26
|
+
* can't recover this from the value Set alone ("*" and an explicit full
|
|
27
|
+
* range both expand to the full set), so we capture it at parse time.
|
|
28
|
+
*/
|
|
29
|
+
domRestricted: boolean;
|
|
30
|
+
dowRestricted: boolean;
|
|
21
31
|
}
|
|
22
32
|
/** Cheap check: is this string a 5-field cron expression (vs an interval like "5m")? */
|
|
23
33
|
export declare function isCronExpression(s: string): boolean;
|
|
@@ -76,6 +76,10 @@ export function parseCronExpression(expr) {
|
|
|
76
76
|
daysOfMonth: parseField(parts[2], SPECS[2]),
|
|
77
77
|
months: parseField(parts[3], SPECS[3]),
|
|
78
78
|
daysOfWeek: parseField(parts[4], SPECS[4]),
|
|
79
|
+
// A bare "*" is unrestricted; anything else (a number, range, list, or even
|
|
80
|
+
// "*/n") restricts the field for the OR rule below.
|
|
81
|
+
domRestricted: parts[2].trim() !== "*",
|
|
82
|
+
dowRestricted: parts[4].trim() !== "*",
|
|
79
83
|
};
|
|
80
84
|
}
|
|
81
85
|
// Cache one formatter per timezone — constructing Intl.DateTimeFormat is costly.
|
|
@@ -123,11 +127,25 @@ function wallClockInZone(ms, timeZone) {
|
|
|
123
127
|
};
|
|
124
128
|
}
|
|
125
129
|
function matches(cron, wc) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
if (!cron.minutes.has(wc.minute))
|
|
131
|
+
return false;
|
|
132
|
+
if (!cron.hours.has(wc.hour))
|
|
133
|
+
return false;
|
|
134
|
+
if (!cron.months.has(wc.month))
|
|
135
|
+
return false;
|
|
136
|
+
// Day-of-month vs day-of-week per Vixie/POSIX cron:
|
|
137
|
+
// - both restricted → match if EITHER matches (OR)
|
|
138
|
+
// - only one restricted → that one governs (the "*" one is ignored)
|
|
139
|
+
// - neither restricted → matches every day
|
|
140
|
+
const domHit = cron.daysOfMonth.has(wc.dayOfMonth);
|
|
141
|
+
const dowHit = cron.daysOfWeek.has(wc.dayOfWeek);
|
|
142
|
+
if (cron.domRestricted && cron.dowRestricted)
|
|
143
|
+
return domHit || dowHit;
|
|
144
|
+
if (cron.domRestricted)
|
|
145
|
+
return domHit;
|
|
146
|
+
if (cron.dowRestricted)
|
|
147
|
+
return dowHit;
|
|
148
|
+
return true;
|
|
131
149
|
}
|
|
132
150
|
const MINUTE_MS = 60_000;
|
|
133
151
|
// ~2 years of minutes — bounds the search for an unsatisfiable expression.
|
|
@@ -18,6 +18,7 @@ import type { CronJob } from "./scheduler.js";
|
|
|
18
18
|
import type { CronScheduler } from "./scheduler.js";
|
|
19
19
|
import type { PermissionMode } from "../types.js";
|
|
20
20
|
import type { ApprovalBackend } from "../tool-system/permission.js";
|
|
21
|
+
import type { SandboxMode } from "../tool-system/sandbox/index.js";
|
|
21
22
|
/** What the executor hands to the run backend for one fired job. */
|
|
22
23
|
export interface CronRunRequest {
|
|
23
24
|
job: CronJob;
|
|
@@ -27,6 +28,11 @@ export interface CronRunRequest {
|
|
|
27
28
|
permissionMode: PermissionMode;
|
|
28
29
|
/** Approval backend the run must install (resolved from the job's tier). */
|
|
29
30
|
approvalBackend: ApprovalBackend;
|
|
31
|
+
/** Sandbox mode the run should confine writes/shell to (resolved from the
|
|
32
|
+
* job's tier). The host runner must forward this to `Engine({ sandbox })`
|
|
33
|
+
* so even a `full` tier can't escape the workspace — defense in depth on
|
|
34
|
+
* top of the approval backend. */
|
|
35
|
+
sandboxMode: SandboxMode;
|
|
30
36
|
/** Abort signal for the run — tripped by `CronScheduler.abort(jobId)` when
|
|
31
37
|
* the run must be cancelled mid-flight (e.g. the user deletes its session
|
|
32
38
|
* while it's still executing). Forward to `Engine.run({ signal })`. */
|
|
@@ -35,16 +41,27 @@ export interface CronRunRequest {
|
|
|
35
41
|
export interface CronRunResult {
|
|
36
42
|
text: string;
|
|
37
43
|
reason: string;
|
|
44
|
+
/**
|
|
45
|
+
* When present, this fire hit a PERMANENT failure that will recur every tick
|
|
46
|
+
* (e.g. a `resumeSessionId` whose session was deleted). The scheduler should
|
|
47
|
+
* auto-disable the job with `stop.reason` rather than silently retrying
|
|
48
|
+
* forever. Absent = transient/normal outcome; keep scheduling.
|
|
49
|
+
*/
|
|
50
|
+
stop?: {
|
|
51
|
+
reason: string;
|
|
52
|
+
};
|
|
38
53
|
}
|
|
39
54
|
/** The pluggable run backend. Production wires this to a headless Engine run;
|
|
40
55
|
* tests pass a stub. Phase 5 will swap this for a RunManager.submit() path. */
|
|
41
56
|
export type CronRunner = (req: CronRunRequest) => Promise<CronRunResult>;
|
|
42
57
|
/**
|
|
43
58
|
* Install the cron executor. After this call, a fired job invokes `runner`
|
|
44
|
-
* with a run request whose permission tier comes from the job.
|
|
45
|
-
* `
|
|
46
|
-
*
|
|
47
|
-
*
|
|
59
|
+
* with a run request whose permission tier comes from the job. A thrown error
|
|
60
|
+
* is logged (`logCronFailure`) and re-thrown so the scheduler's catch records
|
|
61
|
+
* stats and keeps ticking — one transient bad run never stops future ticks. A
|
|
62
|
+
* PERMANENT failure the runner flags via `result.stop` (e.g. a resume target
|
|
63
|
+
* whose session was deleted) auto-disables the job with that reason, so it
|
|
64
|
+
* stops silently retrying forever.
|
|
48
65
|
*/
|
|
49
66
|
export declare function bindCronToEngine(scheduler: CronScheduler, runner: CronRunner): void;
|
|
50
67
|
/** Minimal structural view of RunManager.submit the executor needs. */
|
|
@@ -16,12 +16,30 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { AUTOMATION_RUN_SOURCE } from "../run/EngineRunner.js";
|
|
18
18
|
import { resolveWritePolicy } from "./write-policy.js";
|
|
19
|
+
import { logger } from "../logging/logger.js";
|
|
20
|
+
/**
|
|
21
|
+
* Log a cron executor failure, then re-throw so the scheduler's own try/catch
|
|
22
|
+
* still records run stats and keeps ticking. Without this the failure was
|
|
23
|
+
* silently swallowed (the scheduler catch is empty) — a job could fail every
|
|
24
|
+
* fire with zero trace, which is the exact opposite of what the executor
|
|
25
|
+
* docstrings claimed ("we still log here so failures are visible").
|
|
26
|
+
*/
|
|
27
|
+
function logCronFailure(job, err) {
|
|
28
|
+
logger.warn("cron.executor_failed", {
|
|
29
|
+
cat: "automation",
|
|
30
|
+
jobId: job.id,
|
|
31
|
+
jobName: job.name,
|
|
32
|
+
error: err instanceof Error ? err.message : String(err),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
19
35
|
/**
|
|
20
36
|
* Install the cron executor. After this call, a fired job invokes `runner`
|
|
21
|
-
* with a run request whose permission tier comes from the job.
|
|
22
|
-
* `
|
|
23
|
-
*
|
|
24
|
-
*
|
|
37
|
+
* with a run request whose permission tier comes from the job. A thrown error
|
|
38
|
+
* is logged (`logCronFailure`) and re-thrown so the scheduler's catch records
|
|
39
|
+
* stats and keeps ticking — one transient bad run never stops future ticks. A
|
|
40
|
+
* PERMANENT failure the runner flags via `result.stop` (e.g. a resume target
|
|
41
|
+
* whose session was deleted) auto-disables the job with that reason, so it
|
|
42
|
+
* stops silently retrying forever.
|
|
25
43
|
*/
|
|
26
44
|
export function bindCronToEngine(scheduler, runner) {
|
|
27
45
|
scheduler.setExecutor(async (job, signal) => {
|
|
@@ -31,9 +49,21 @@ export function bindCronToEngine(scheduler, runner) {
|
|
|
31
49
|
prompt: job.prompt,
|
|
32
50
|
permissionMode: policy.permissionMode,
|
|
33
51
|
approvalBackend: policy.approvalBackend,
|
|
52
|
+
sandboxMode: policy.sandboxMode,
|
|
34
53
|
signal,
|
|
35
54
|
};
|
|
36
|
-
|
|
55
|
+
try {
|
|
56
|
+
const result = await runner(req);
|
|
57
|
+
// A PERMANENT failure the runner flags (e.g. a resume target whose session
|
|
58
|
+
// was deleted) auto-disables the job so it stops silently retrying.
|
|
59
|
+
if (result?.stop) {
|
|
60
|
+
scheduler.disableWithReason(job.id, result.stop.reason);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
logCronFailure(job, err);
|
|
65
|
+
throw err; // let the scheduler's catch record stats + keep ticking
|
|
66
|
+
}
|
|
37
67
|
});
|
|
38
68
|
}
|
|
39
69
|
/**
|
|
@@ -45,12 +75,18 @@ export function bindCronToEngine(scheduler, runner) {
|
|
|
45
75
|
*/
|
|
46
76
|
export function bindCronToRunManager(scheduler, runManager) {
|
|
47
77
|
scheduler.setExecutor(async (job, _signal) => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
78
|
+
try {
|
|
79
|
+
const snapshot = await runManager.submit({
|
|
80
|
+
objective: job.prompt,
|
|
81
|
+
cwd: job.cwd,
|
|
82
|
+
metadata: { source: AUTOMATION_RUN_SOURCE, cronJobId: job.id, cronJobName: job.name },
|
|
83
|
+
});
|
|
84
|
+
// Record the run id on the job so the UI can link to run history.
|
|
85
|
+
job.lastRunId = snapshot.runId;
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
logCronFailure(job, err);
|
|
89
|
+
throw err; // let the scheduler's catch record stats + keep ticking
|
|
90
|
+
}
|
|
55
91
|
});
|
|
56
92
|
}
|
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
* with a predefined prompt on schedule.
|
|
6
6
|
*/
|
|
7
7
|
import type { CronStore } from "./store.js";
|
|
8
|
+
/**
|
|
9
|
+
* True when a cron timer fired too far past its scheduled instant to be a
|
|
10
|
+
* legitimate on-time run — i.e. the host slept through the scheduled time and
|
|
11
|
+
* the timer fired on wake. Exported for unit testing the sleep/wake guard.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isCronMisfire(scheduledFor: number, now: number): boolean;
|
|
8
14
|
/** Permission tier a scheduled job runs under (Phase 5 enforces write tiers). */
|
|
9
15
|
export type CronPermissionLevel = "read-only" | "workspace-write" | "full";
|
|
10
16
|
export interface CronJob {
|
|
@@ -25,12 +31,27 @@ export interface CronJob {
|
|
|
25
31
|
permissionLevel?: CronPermissionLevel;
|
|
26
32
|
/** RunStore run id of the most recent execution (Phase 2 RunManager path). */
|
|
27
33
|
lastRunId?: string;
|
|
34
|
+
/** True = one-shot: delete the job after its first real execution so it never
|
|
35
|
+
* fires again (e.g. "in 10 minutes, do X once"). */
|
|
36
|
+
once?: boolean;
|
|
37
|
+
/** When set, the fired job RESUMES this codeshell chat session instead of
|
|
38
|
+
* starting a fresh one: the prompt is appended as a new user turn to that
|
|
39
|
+
* session (restored from disk if not in memory), carrying its transcript /
|
|
40
|
+
* goal / context. Unset = the default standalone behaviour (new session per
|
|
41
|
+
* fire). The host's executor honours this. */
|
|
42
|
+
resumeSessionId?: string;
|
|
43
|
+
/** Why the job was auto-disabled (set by `disableWithReason`), e.g. a resume
|
|
44
|
+
* target whose session no longer exists. Surfaced in the UI so a silently
|
|
45
|
+
* stopped job is explainable; cleared when the job is re-enabled. */
|
|
46
|
+
disabledReason?: string;
|
|
28
47
|
}
|
|
29
48
|
/** Optional metadata accepted by create(). */
|
|
30
49
|
export interface CreateJobOptions {
|
|
31
50
|
cwd?: string;
|
|
32
51
|
timezone?: string;
|
|
33
52
|
permissionLevel?: CronPermissionLevel;
|
|
53
|
+
once?: boolean;
|
|
54
|
+
resumeSessionId?: string;
|
|
34
55
|
}
|
|
35
56
|
/** Fields editable via update(). Any omitted field is left unchanged. */
|
|
36
57
|
export interface UpdateJobPatch {
|
|
@@ -119,6 +140,14 @@ export declare class CronScheduler {
|
|
|
119
140
|
list(): CronJob[];
|
|
120
141
|
get(id: string): CronJob | undefined;
|
|
121
142
|
pause(id: string): boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Auto-disable a job and record WHY (e.g. its resume target session was
|
|
145
|
+
* deleted). Unlike `delete`, the job is retained (enabled=false) so the user
|
|
146
|
+
* can see it stopped and its reason in the UI, then delete or re-point it.
|
|
147
|
+
* Mirrors `pause` but also stamps `disabledReason`. Idempotent-safe: a
|
|
148
|
+
* subsequent `resume()` clears the reason.
|
|
149
|
+
*/
|
|
150
|
+
disableWithReason(id: string, reason: string): boolean;
|
|
122
151
|
resume(id: string): boolean;
|
|
123
152
|
/**
|
|
124
153
|
* Edit an existing job's fields (name/prompt/schedule/timezone/cwd/
|
|
@@ -153,4 +182,13 @@ export declare class CronScheduler {
|
|
|
153
182
|
*/
|
|
154
183
|
private fire;
|
|
155
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Parse a schedule string into milliseconds.
|
|
187
|
+
* Supports: "30s", "5m", "1h", "1d", or raw (all-digit) milliseconds.
|
|
188
|
+
*
|
|
189
|
+
* Throws on anything else rather than silently falling back to a default —
|
|
190
|
+
* a typo like "5mn" should surface as an error, not quietly schedule every
|
|
191
|
+
* 10 minutes (review-2026-05-30).
|
|
192
|
+
*/
|
|
193
|
+
export declare function parseSchedule(schedule: string): number;
|
|
156
194
|
export declare const cronScheduler: CronScheduler;
|