@cjhyy/code-shell-core 0.5.0-rc.2 → 0.6.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -10
- package/dist/agent/agent-definition-registry.d.ts +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
|
@@ -5,6 +5,23 @@
|
|
|
5
5
|
* with a predefined prompt on schedule.
|
|
6
6
|
*/
|
|
7
7
|
import { isCronExpression, parseCronExpression, nextCronTime } from "./cron-expr.js";
|
|
8
|
+
/**
|
|
9
|
+
* How late a cron-expression timer may fire and still be considered "on time".
|
|
10
|
+
* A setTimeout that fires within this window of its scheduled instant runs
|
|
11
|
+
* normally; one that fires later (the host slept through the scheduled time
|
|
12
|
+
* and the timer fired on wake) is treated as a misfire — skipped and re-armed
|
|
13
|
+
* to the next occurrence. 90s comfortably clears cron's 60s granularity plus
|
|
14
|
+
* normal timer jitter, while still catching a multi-minute/hour sleep drift.
|
|
15
|
+
*/
|
|
16
|
+
const CRON_MISFIRE_GRACE_MS = 90_000;
|
|
17
|
+
/**
|
|
18
|
+
* True when a cron timer fired too far past its scheduled instant to be a
|
|
19
|
+
* legitimate on-time run — i.e. the host slept through the scheduled time and
|
|
20
|
+
* the timer fired on wake. Exported for unit testing the sleep/wake guard.
|
|
21
|
+
*/
|
|
22
|
+
export function isCronMisfire(scheduledFor, now) {
|
|
23
|
+
return now - scheduledFor > CRON_MISFIRE_GRACE_MS;
|
|
24
|
+
}
|
|
8
25
|
export class CronScheduler {
|
|
9
26
|
jobs = new Map();
|
|
10
27
|
timers = new Map();
|
|
@@ -168,10 +185,9 @@ export class CronScheduler {
|
|
|
168
185
|
}
|
|
169
186
|
}
|
|
170
187
|
persistRunStats(job) {
|
|
171
|
-
|
|
172
|
-
|
|
188
|
+
// No store → nothing to persist. (persist() would also no-op here.)
|
|
189
|
+
if (!this.store)
|
|
173
190
|
return;
|
|
174
|
-
}
|
|
175
191
|
try {
|
|
176
192
|
this.store.mutate((jobs) => {
|
|
177
193
|
const idx = jobs.findIndex((j) => j.id === job.id);
|
|
@@ -210,6 +226,8 @@ export class CronScheduler {
|
|
|
210
226
|
...(opts?.cwd !== undefined ? { cwd: opts.cwd } : {}),
|
|
211
227
|
...(opts?.timezone !== undefined ? { timezone: opts.timezone } : {}),
|
|
212
228
|
...(opts?.permissionLevel !== undefined ? { permissionLevel: opts.permissionLevel } : {}),
|
|
229
|
+
...(opts?.once === true ? { once: true } : {}),
|
|
230
|
+
...(opts?.resumeSessionId !== undefined ? { resumeSessionId: opts.resumeSessionId } : {}),
|
|
213
231
|
};
|
|
214
232
|
this.refreshNextRunForDisplay(job);
|
|
215
233
|
return { jobs: [...jobs, job], result: job };
|
|
@@ -229,6 +247,8 @@ export class CronScheduler {
|
|
|
229
247
|
...(opts?.cwd !== undefined ? { cwd: opts.cwd } : {}),
|
|
230
248
|
...(opts?.timezone !== undefined ? { timezone: opts.timezone } : {}),
|
|
231
249
|
...(opts?.permissionLevel !== undefined ? { permissionLevel: opts.permissionLevel } : {}),
|
|
250
|
+
...(opts?.once === true ? { once: true } : {}),
|
|
251
|
+
...(opts?.resumeSessionId !== undefined ? { resumeSessionId: opts.resumeSessionId } : {}),
|
|
232
252
|
};
|
|
233
253
|
this.jobs.set(id, job);
|
|
234
254
|
this.arm(job);
|
|
@@ -279,6 +299,37 @@ export class CronScheduler {
|
|
|
279
299
|
this.persist();
|
|
280
300
|
return true;
|
|
281
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* Auto-disable a job and record WHY (e.g. its resume target session was
|
|
304
|
+
* deleted). Unlike `delete`, the job is retained (enabled=false) so the user
|
|
305
|
+
* can see it stopped and its reason in the UI, then delete or re-point it.
|
|
306
|
+
* Mirrors `pause` but also stamps `disabledReason`. Idempotent-safe: a
|
|
307
|
+
* subsequent `resume()` clears the reason.
|
|
308
|
+
*/
|
|
309
|
+
disableWithReason(id, reason) {
|
|
310
|
+
if (this.store) {
|
|
311
|
+
const tx = this.store.mutate((jobs) => {
|
|
312
|
+
let changed = false;
|
|
313
|
+
const next = jobs.map((j) => {
|
|
314
|
+
if (j.id !== id)
|
|
315
|
+
return j;
|
|
316
|
+
changed = true;
|
|
317
|
+
return { ...j, enabled: false, disabledReason: reason };
|
|
318
|
+
});
|
|
319
|
+
return { jobs: next, result: changed };
|
|
320
|
+
});
|
|
321
|
+
this.reconcileJobs(tx.jobs);
|
|
322
|
+
return tx.result;
|
|
323
|
+
}
|
|
324
|
+
const job = this.jobs.get(id);
|
|
325
|
+
if (!job)
|
|
326
|
+
return false;
|
|
327
|
+
job.enabled = false;
|
|
328
|
+
job.disabledReason = reason;
|
|
329
|
+
this.clearTimer(id);
|
|
330
|
+
this.persist();
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
282
333
|
resume(id) {
|
|
283
334
|
if (this.store) {
|
|
284
335
|
const tx = this.store.mutate((jobs) => {
|
|
@@ -287,7 +338,7 @@ export class CronScheduler {
|
|
|
287
338
|
if (j.id !== id)
|
|
288
339
|
return j;
|
|
289
340
|
changed = true;
|
|
290
|
-
const job = { ...j, enabled: true };
|
|
341
|
+
const job = { ...j, enabled: true, disabledReason: undefined };
|
|
291
342
|
this.refreshNextRunForDisplay(job);
|
|
292
343
|
return job;
|
|
293
344
|
});
|
|
@@ -300,6 +351,7 @@ export class CronScheduler {
|
|
|
300
351
|
if (!job)
|
|
301
352
|
return false;
|
|
302
353
|
job.enabled = true;
|
|
354
|
+
job.disabledReason = undefined;
|
|
303
355
|
this.arm(job);
|
|
304
356
|
this.persist();
|
|
305
357
|
return true;
|
|
@@ -440,12 +492,27 @@ export class CronScheduler {
|
|
|
440
492
|
return;
|
|
441
493
|
}
|
|
442
494
|
job.nextRun = next;
|
|
495
|
+
const scheduledFor = next;
|
|
443
496
|
const delay = Math.max(0, next - Date.now());
|
|
444
497
|
const timer = setTimeout(() => {
|
|
445
|
-
|
|
446
|
-
|
|
498
|
+
// Misfire guard for sleep/wake drift. A setTimeout pauses while the host
|
|
499
|
+
// sleeps, then fires the instant the machine wakes — which can be hours
|
|
500
|
+
// off the scheduled wall-clock (observed: a `0 9 * * *` job running at
|
|
501
|
+
// 06:56 because the Mac did a maintenance wake and the timer fired on
|
|
502
|
+
// resume). If we wake too far PAST the scheduled instant, this is a
|
|
503
|
+
// misfire: skip running and re-arm to the next correct occurrence rather
|
|
504
|
+
// than running at the wrong time.
|
|
505
|
+
if (isCronMisfire(scheduledFor, Date.now())) {
|
|
447
506
|
if (job.enabled)
|
|
448
507
|
this.armCron(job);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
void this.fire(job, () => {
|
|
511
|
+
// Re-arm for the following occurrence — but only if the job still
|
|
512
|
+
// exists. A one-shot (once) job deletes itself in fire()'s finally,
|
|
513
|
+
// so this closure must not resurrect it via a stale reference.
|
|
514
|
+
if (job.enabled && this.jobs.has(job.id))
|
|
515
|
+
this.armCron(job);
|
|
449
516
|
});
|
|
450
517
|
}, delay);
|
|
451
518
|
this.timers.set(job.id, timer);
|
|
@@ -498,6 +565,11 @@ export class CronScheduler {
|
|
|
498
565
|
this.persistRunStats(job);
|
|
499
566
|
this.running.delete(job.id);
|
|
500
567
|
this.runningControllers.delete(job.id);
|
|
568
|
+
// One-shot: delete after its first execution so it never fires again.
|
|
569
|
+
// Done after persistRunStats so the final stats write can't race the
|
|
570
|
+
// removal; delete() clears the timer + removes from store/in-memory.
|
|
571
|
+
if (job.once)
|
|
572
|
+
this.delete(job.id);
|
|
501
573
|
// Unblock any abort() awaiting this run's teardown.
|
|
502
574
|
resolveDone();
|
|
503
575
|
}
|
|
@@ -523,15 +595,20 @@ function validateSchedule(schedule, timezone) {
|
|
|
523
595
|
* a typo like "5mn" should surface as an error, not quietly schedule every
|
|
524
596
|
* 10 minutes (review-2026-05-30).
|
|
525
597
|
*/
|
|
526
|
-
function parseSchedule(schedule) {
|
|
598
|
+
export function parseSchedule(schedule) {
|
|
527
599
|
const match = schedule.match(/^(\d+)(s|m|h|d)$/);
|
|
528
600
|
if (match) {
|
|
529
601
|
const value = parseInt(match[1], 10);
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
602
|
+
// A zero interval ("0s"/"0m"/…) → 0ms → setInterval(fn, 0) spins
|
|
603
|
+
// continuously. Reject it (fall through to throw), matching the raw-ms
|
|
604
|
+
// path's `> 0` guard and the "throw on bad input" contract.
|
|
605
|
+
if (value > 0) {
|
|
606
|
+
switch (match[2]) {
|
|
607
|
+
case "s": return value * 1000;
|
|
608
|
+
case "m": return value * 60 * 1000;
|
|
609
|
+
case "h": return value * 60 * 60 * 1000;
|
|
610
|
+
case "d": return value * 24 * 60 * 60 * 1000;
|
|
611
|
+
}
|
|
535
612
|
}
|
|
536
613
|
}
|
|
537
614
|
// Raw milliseconds — must be all digits and > 0 (parseInt would otherwise
|
package/dist/automation/store.js
CHANGED
|
@@ -113,7 +113,10 @@ export class CronStore {
|
|
|
113
113
|
throw lastError;
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
+
// Reused across calls — Atomics.wait only reads slot 0, which always stays 0,
|
|
117
|
+
// so a single shared buffer is safe and avoids per-call allocation in the
|
|
118
|
+
// lock-retry loop.
|
|
119
|
+
const SLEEP_SIGNAL = new Int32Array(new SharedArrayBuffer(4));
|
|
116
120
|
function sleepSync(ms) {
|
|
117
|
-
|
|
118
|
-
Atomics.wait(signal, 0, 0, ms);
|
|
121
|
+
Atomics.wait(SLEEP_SIGNAL, 0, 0, ms);
|
|
119
122
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effective disabled-skills/plugins computation — the ONE place that folds a
|
|
3
|
+
* project's capabilityOverrides (tri-state on/off/inherit) over the global
|
|
4
|
+
* baseline, plus the no-repo whitelist inversion.
|
|
5
|
+
*
|
|
6
|
+
* Extracted from Engine.readDisabledLists because the MCP merge consumers
|
|
7
|
+
* (engineFactory in the stdio/tcp servers, diskDefaultsFrom hot-reload) were
|
|
8
|
+
* passing the RAW global `settings.disabledPlugins` to mergePluginMcpServers:
|
|
9
|
+
* a plugin force-enabled at project level (能力总览 "on") stayed excluded from
|
|
10
|
+
* the MCP merge because the global list still named it. Every consumer must
|
|
11
|
+
* fold through here so "project on overrides global off" holds everywhere.
|
|
12
|
+
*/
|
|
13
|
+
import { SettingsManager } from "../settings/manager.js";
|
|
14
|
+
export interface EffectiveDisabledLists {
|
|
15
|
+
disabledSkills: string[];
|
|
16
|
+
disabledPlugins: string[];
|
|
17
|
+
/** Folded from capabilityOverrides.pluginHooks "off" keys (see
|
|
18
|
+
* pluginHookKey) — per-hook suppression for plugin-provided hooks. */
|
|
19
|
+
disabledPluginHooks: string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Compute the effective disabled lists for `cwd`. Never throws — any read
|
|
23
|
+
* error falls back to empty lists (same contract as Engine.readDisabledLists).
|
|
24
|
+
*/
|
|
25
|
+
export declare function computeEffectiveDisabledLists(sm: SettingsManager, cwd: string | undefined): EffectiveDisabledLists;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effective disabled-skills/plugins computation — the ONE place that folds a
|
|
3
|
+
* project's capabilityOverrides (tri-state on/off/inherit) over the global
|
|
4
|
+
* baseline, plus the no-repo whitelist inversion.
|
|
5
|
+
*
|
|
6
|
+
* Extracted from Engine.readDisabledLists because the MCP merge consumers
|
|
7
|
+
* (engineFactory in the stdio/tcp servers, diskDefaultsFrom hot-reload) were
|
|
8
|
+
* passing the RAW global `settings.disabledPlugins` to mergePluginMcpServers:
|
|
9
|
+
* a plugin force-enabled at project level (能力总览 "on") stayed excluded from
|
|
10
|
+
* the MCP merge because the global list still named it. Every consumer must
|
|
11
|
+
* fold through here so "project on overrides global off" holds everywhere.
|
|
12
|
+
*/
|
|
13
|
+
import { noRepoDir } from "../settings/manager.js";
|
|
14
|
+
import { scanSkills } from "../skills/scanner.js";
|
|
15
|
+
import { readInstalledPlugins } from "../plugins/installedPlugins.js";
|
|
16
|
+
import { effectiveDisabledList, whitelistDisabledList } from "./overlay.js";
|
|
17
|
+
/**
|
|
18
|
+
* Compute the effective disabled lists for `cwd`. Never throws — any read
|
|
19
|
+
* error falls back to empty lists (same contract as Engine.readDisabledLists).
|
|
20
|
+
*/
|
|
21
|
+
export function computeEffectiveDisabledLists(sm, cwd) {
|
|
22
|
+
try {
|
|
23
|
+
const settings = sm.get();
|
|
24
|
+
// Read the project overlay UNMERGED (getForScope), not the merged get(),
|
|
25
|
+
// so tri-state inheritance survives. No cwd / no overlay → the baseline
|
|
26
|
+
// is returned unchanged (zero regression).
|
|
27
|
+
const overrides = cwd
|
|
28
|
+
? sm.getForScope("project", cwd).capabilityOverrides
|
|
29
|
+
: undefined;
|
|
30
|
+
// no-repo "conversation" scope: INVERT skill/plugin filtering to a
|
|
31
|
+
// whitelist (default-all-off, only explicit "on" survives). Only this
|
|
32
|
+
// fixed cwd flips; every real project keeps the denylist below. agent/
|
|
33
|
+
// mcp/builtin are NOT inverted. See conversation-settings spec §3.
|
|
34
|
+
if (cwd && cwd === noRepoDir()) {
|
|
35
|
+
const allSkillNames = scanSkills(cwd).map((s) => s.name);
|
|
36
|
+
const allPluginNames = Object.keys(readInstalledPlugins().plugins).map((key) => {
|
|
37
|
+
const at = key.lastIndexOf("@");
|
|
38
|
+
return at > 0 ? key.slice(0, at) : key;
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
disabledSkills: whitelistDisabledList(allSkillNames, overrides?.skills),
|
|
42
|
+
disabledPlugins: whitelistDisabledList(allPluginNames, overrides?.plugins),
|
|
43
|
+
// pluginHooks is NOT inverted (like agents/mcp/builtin): there is no
|
|
44
|
+
// global per-hook baseline, only project-level "off" keys.
|
|
45
|
+
disabledPluginHooks: effectiveDisabledList([], overrides?.pluginHooks),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
disabledSkills: effectiveDisabledList(settings.disabledSkills ?? [], overrides?.skills),
|
|
50
|
+
disabledPlugins: effectiveDisabledList(settings.disabledPlugins ?? [], overrides?.plugins),
|
|
51
|
+
disabledPluginHooks: effectiveDisabledList([], overrides?.pluginHooks),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return { disabledSkills: [], disabledPlugins: [], disabledPluginHooks: [] };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -30,6 +30,21 @@ export declare function overrideFor(overrides: CapabilityOverrides | undefined,
|
|
|
30
30
|
* leaves the baseline.
|
|
31
31
|
*/
|
|
32
32
|
export declare function effectiveDisabledList(globalDisabled: string[], bucket: Record<string, CapabilityOverride> | undefined): string[];
|
|
33
|
+
/**
|
|
34
|
+
* Whitelist (opt-in) variant of {@link effectiveDisabledList}, used only for
|
|
35
|
+
* the no-repo "conversation" scope where skill/plugin default-state is INVERTED:
|
|
36
|
+
* everything is disabled UNLESS the project override bucket explicitly marks it
|
|
37
|
+
* `"on"`. This is the opposite of the normal denylist (default-enabled) model.
|
|
38
|
+
*
|
|
39
|
+
* Given the full set of installed names and an override bucket:
|
|
40
|
+
* - a name explicitly `"on"` → kept enabled (NOT in the returned list)
|
|
41
|
+
* - everything else (`"off"`, inherit/absent, garbage) → disabled (in the list)
|
|
42
|
+
*
|
|
43
|
+
* The returned list is the effective disabled list to feed run-time
|
|
44
|
+
* skill/plugin filtering. Order follows `allInstalledNames`; duplicates are
|
|
45
|
+
* collapsed.
|
|
46
|
+
*/
|
|
47
|
+
export declare function whitelistDisabledList(allInstalledNames: string[], bucket: Record<string, CapabilityOverride> | undefined): string[];
|
|
33
48
|
/**
|
|
34
49
|
* Fold a project builtin override bucket into the global enabled/disabled
|
|
35
50
|
* builtin-tool lists, producing the lists to feed resolveBuiltinToolNames
|
|
@@ -54,6 +54,33 @@ export function effectiveDisabledList(globalDisabled, bucket) {
|
|
|
54
54
|
}
|
|
55
55
|
return [...disabled];
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Whitelist (opt-in) variant of {@link effectiveDisabledList}, used only for
|
|
59
|
+
* the no-repo "conversation" scope where skill/plugin default-state is INVERTED:
|
|
60
|
+
* everything is disabled UNLESS the project override bucket explicitly marks it
|
|
61
|
+
* `"on"`. This is the opposite of the normal denylist (default-enabled) model.
|
|
62
|
+
*
|
|
63
|
+
* Given the full set of installed names and an override bucket:
|
|
64
|
+
* - a name explicitly `"on"` → kept enabled (NOT in the returned list)
|
|
65
|
+
* - everything else (`"off"`, inherit/absent, garbage) → disabled (in the list)
|
|
66
|
+
*
|
|
67
|
+
* The returned list is the effective disabled list to feed run-time
|
|
68
|
+
* skill/plugin filtering. Order follows `allInstalledNames`; duplicates are
|
|
69
|
+
* collapsed.
|
|
70
|
+
*/
|
|
71
|
+
export function whitelistDisabledList(allInstalledNames, bucket) {
|
|
72
|
+
const disabled = [];
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
for (const name of allInstalledNames) {
|
|
75
|
+
if (seen.has(name))
|
|
76
|
+
continue;
|
|
77
|
+
seen.add(name);
|
|
78
|
+
if (bucket?.[name] === "on")
|
|
79
|
+
continue; // explicit allow → stays enabled
|
|
80
|
+
disabled.push(name);
|
|
81
|
+
}
|
|
82
|
+
return disabled;
|
|
83
|
+
}
|
|
57
84
|
/**
|
|
58
85
|
* Fold a project builtin override bucket into the global enabled/disabled
|
|
59
86
|
* builtin-tool lists, producing the lists to feed resolveBuiltinToolNames
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type PermissionMode = "default" | "acceptEdits" | "bypassPermissions";
|
|
2
|
+
export interface BuildArgsOpts {
|
|
3
|
+
prompt: string;
|
|
4
|
+
resumeSessionId?: string;
|
|
5
|
+
permissionMode: PermissionMode;
|
|
6
|
+
cwd: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ParsedResult {
|
|
9
|
+
sessionId: string;
|
|
10
|
+
finalText: string;
|
|
11
|
+
isError: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface AgentAdapter {
|
|
14
|
+
/** Display name, e.g. "claude" / "codex". */
|
|
15
|
+
kind: string;
|
|
16
|
+
/** Build the CLI argv (command itself excluded). */
|
|
17
|
+
buildArgs(opts: BuildArgsOpts): string[];
|
|
18
|
+
/** Reduce collected stream-json output lines to {sessionId, finalText, isError}. */
|
|
19
|
+
parseResult(lines: string[]): ParsedResult;
|
|
20
|
+
/**
|
|
21
|
+
* When true, the prompt is fed over stdin instead of being passed in argv.
|
|
22
|
+
* claude takes the prompt as `-p <prompt>` (argv) so leaves this unset; codex
|
|
23
|
+
* `exec` reads the prompt from stdin (its argv ends with a bare `-`).
|
|
24
|
+
*/
|
|
25
|
+
promptViaStdin?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Soft cost-guard appended to a driven CC's system prompt. Workflow fans out a
|
|
29
|
+
* fleet of agents and is the main token sink; we ask CC to surface intent and
|
|
30
|
+
* get a go-ahead before running one, so a human can intervene (rooms) and the
|
|
31
|
+
* model stays cost-aware even where Workflow isn't hard-disabled. Shared so the
|
|
32
|
+
* room's resident process can reuse the exact same wording.
|
|
33
|
+
*/
|
|
34
|
+
export declare const CC_COST_GUARD_PROMPT: string;
|
|
35
|
+
export declare const claudeAdapter: AgentAdapter;
|
|
36
|
+
/**
|
|
37
|
+
* Drives the OpenAI Codex CLI via `codex exec`. Mirrors claudeAdapter but for
|
|
38
|
+
* codex's different surface:
|
|
39
|
+
* - prompt is fed over STDIN (argv ends with a bare `-`), not argv → promptViaStdin
|
|
40
|
+
* - permission is a spawn-time SANDBOX choice, not a per-tool approval loop:
|
|
41
|
+
* default → --sandbox read-only (codex can read, not write)
|
|
42
|
+
* acceptEdits → --sandbox workspace-write (codex can write the workspace)
|
|
43
|
+
* bypassPermissions → --dangerously-bypass-approvals-and-sandbox (full auto)
|
|
44
|
+
* (Matches the only model codex supports — same choice the `wand` tool makes;
|
|
45
|
+
* codex has no `--permission-prompt-tool stdio` equivalent.)
|
|
46
|
+
* - JSONL event shape differs: session id is `thread.started`.thread_id, the
|
|
47
|
+
* final answer is the last `item.completed` of item.type "agent_message", and
|
|
48
|
+
* failure is `turn.failed` / `error` events (no `is_error` flag).
|
|
49
|
+
*/
|
|
50
|
+
export declare const codexAdapter: AgentAdapter;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Soft cost-guard appended to a driven CC's system prompt. Workflow fans out a
|
|
3
|
+
* fleet of agents and is the main token sink; we ask CC to surface intent and
|
|
4
|
+
* get a go-ahead before running one, so a human can intervene (rooms) and the
|
|
5
|
+
* model stays cost-aware even where Workflow isn't hard-disabled. Shared so the
|
|
6
|
+
* room's resident process can reuse the exact same wording.
|
|
7
|
+
*/
|
|
8
|
+
export const CC_COST_GUARD_PROMPT = "Cost guard: before invoking the Workflow tool (which fans out many agents and " +
|
|
9
|
+
"burns a lot of tokens), first state in one short message what workflow you intend " +
|
|
10
|
+
"to run and why, and ask for a go-ahead — do NOT launch a Workflow unprompted. A " +
|
|
11
|
+
"single Task sub-agent is fine without asking.";
|
|
12
|
+
export const claudeAdapter = {
|
|
13
|
+
kind: "claude",
|
|
14
|
+
buildArgs(opts) {
|
|
15
|
+
// -p (print/headless) + stream-json REQUIRES --verbose (verified).
|
|
16
|
+
const args = ["-p", opts.prompt, "--output-format", "stream-json", "--verbose"];
|
|
17
|
+
if (opts.resumeSessionId)
|
|
18
|
+
args.push("--resume", opts.resumeSessionId);
|
|
19
|
+
// Hard-disallow Workflow: driving CC unattended (esp. bypassPermissions),
|
|
20
|
+
// CC's Workflow tool fans out a FLEET of agents — the real token-burn culprit
|
|
21
|
+
// (user: "自动了 2 个 workflow token 就烧没了"). A single Task (one sub-agent)
|
|
22
|
+
// is cheap, so it stays ALLOWED. (We earlier disallowed Task by mistake;
|
|
23
|
+
// Workflow is the one that fans out.) Disabling outright is cleaner than an
|
|
24
|
+
// approval round-trip this path doesn't have.
|
|
25
|
+
args.push("--disallowedTools", "Workflow");
|
|
26
|
+
// Soft constraint (belt-and-suspenders + carries to rooms where Workflow is
|
|
27
|
+
// allowed): ask CC to check in before expensive multi-agent work.
|
|
28
|
+
args.push("--append-system-prompt", CC_COST_GUARD_PROMPT);
|
|
29
|
+
args.push("--permission-mode", opts.permissionMode);
|
|
30
|
+
return args;
|
|
31
|
+
},
|
|
32
|
+
parseResult(lines) {
|
|
33
|
+
let sessionId = "";
|
|
34
|
+
let finalText = "";
|
|
35
|
+
let isError = false;
|
|
36
|
+
for (const line of lines) {
|
|
37
|
+
const t = line.trim();
|
|
38
|
+
if (!t)
|
|
39
|
+
continue;
|
|
40
|
+
let d;
|
|
41
|
+
try {
|
|
42
|
+
d = JSON.parse(t);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (typeof d.session_id === "string" && !sessionId)
|
|
48
|
+
sessionId = d.session_id;
|
|
49
|
+
if (d.type === "result") {
|
|
50
|
+
if (typeof d.session_id === "string")
|
|
51
|
+
sessionId = d.session_id;
|
|
52
|
+
if (typeof d.result === "string")
|
|
53
|
+
finalText = d.result;
|
|
54
|
+
isError = Boolean(d.is_error);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { sessionId, finalText, isError };
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Drives the OpenAI Codex CLI via `codex exec`. Mirrors claudeAdapter but for
|
|
62
|
+
* codex's different surface:
|
|
63
|
+
* - prompt is fed over STDIN (argv ends with a bare `-`), not argv → promptViaStdin
|
|
64
|
+
* - permission is a spawn-time SANDBOX choice, not a per-tool approval loop:
|
|
65
|
+
* default → --sandbox read-only (codex can read, not write)
|
|
66
|
+
* acceptEdits → --sandbox workspace-write (codex can write the workspace)
|
|
67
|
+
* bypassPermissions → --dangerously-bypass-approvals-and-sandbox (full auto)
|
|
68
|
+
* (Matches the only model codex supports — same choice the `wand` tool makes;
|
|
69
|
+
* codex has no `--permission-prompt-tool stdio` equivalent.)
|
|
70
|
+
* - JSONL event shape differs: session id is `thread.started`.thread_id, the
|
|
71
|
+
* final answer is the last `item.completed` of item.type "agent_message", and
|
|
72
|
+
* failure is `turn.failed` / `error` events (no `is_error` flag).
|
|
73
|
+
*/
|
|
74
|
+
export const codexAdapter = {
|
|
75
|
+
kind: "codex",
|
|
76
|
+
promptViaStdin: true,
|
|
77
|
+
buildArgs(opts) {
|
|
78
|
+
const args = ["exec", "--json", "--color", "never", "--skip-git-repo-check"];
|
|
79
|
+
if (opts.permissionMode === "bypassPermissions") {
|
|
80
|
+
args.push("--dangerously-bypass-approvals-and-sandbox");
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
args.push("--sandbox", opts.permissionMode === "acceptEdits" ? "workspace-write" : "read-only");
|
|
84
|
+
}
|
|
85
|
+
// `resume <thread_id>` continues a prior codex session; the trailing `-`
|
|
86
|
+
// tells codex exec to read the prompt from stdin (we feed it there).
|
|
87
|
+
if (opts.resumeSessionId)
|
|
88
|
+
args.push("resume", opts.resumeSessionId, "-");
|
|
89
|
+
else
|
|
90
|
+
args.push("-");
|
|
91
|
+
return args;
|
|
92
|
+
},
|
|
93
|
+
parseResult(lines) {
|
|
94
|
+
let sessionId = "";
|
|
95
|
+
let finalText = "";
|
|
96
|
+
let isError = false;
|
|
97
|
+
const errorBits = [];
|
|
98
|
+
for (const line of lines) {
|
|
99
|
+
const t = line.trim();
|
|
100
|
+
if (!t)
|
|
101
|
+
continue;
|
|
102
|
+
let d;
|
|
103
|
+
try {
|
|
104
|
+
d = JSON.parse(t);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (d.type === "thread.started" && typeof d.thread_id === "string")
|
|
110
|
+
sessionId = d.thread_id;
|
|
111
|
+
else if (d.type === "item.completed" && d.item?.type === "agent_message" && typeof d.item.text === "string") {
|
|
112
|
+
finalText = d.item.text; // keep the LAST agent_message
|
|
113
|
+
}
|
|
114
|
+
else if (d.type === "turn.failed") {
|
|
115
|
+
isError = true;
|
|
116
|
+
const msg = d.error?.message ?? d.error ?? "turn failed";
|
|
117
|
+
if (typeof msg === "string")
|
|
118
|
+
errorBits.push(msg);
|
|
119
|
+
}
|
|
120
|
+
else if (d.type === "error") {
|
|
121
|
+
isError = true;
|
|
122
|
+
const msg = d.message ?? d.error?.message ?? d.error ?? "error";
|
|
123
|
+
if (typeof msg === "string")
|
|
124
|
+
errorBits.push(msg);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// On failure, surface the error text as finalText (so the caller/notification
|
|
128
|
+
// shows *why*), preferring it over any partial agent_message.
|
|
129
|
+
if (isError && errorBits.length)
|
|
130
|
+
finalText = errorBits.join("\n");
|
|
131
|
+
return { sessionId, finalText, isError };
|
|
132
|
+
},
|
|
133
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface CCAvailability {
|
|
2
|
+
available: boolean;
|
|
3
|
+
command: string;
|
|
4
|
+
version?: string;
|
|
5
|
+
reason?: "not-found" | "not-executable";
|
|
6
|
+
}
|
|
7
|
+
/** macOS GUI-launched Electron has a minimal PATH (no Homebrew). Prepend common
|
|
8
|
+
* CLI dirs so `claude` resolves. Mirrors resident-agent.ts's fix. */
|
|
9
|
+
export declare function pathWithCommonBins(env?: NodeJS.ProcessEnv): string;
|
|
10
|
+
export type ProbeRunner = (command: string) => Promise<{
|
|
11
|
+
ok: boolean;
|
|
12
|
+
stdout: string;
|
|
13
|
+
}>;
|
|
14
|
+
/** Probe a CLI's availability. Injectable runner for tests. */
|
|
15
|
+
export declare function probeCli(command: string, runner?: ProbeRunner): Promise<CCAvailability>;
|
|
16
|
+
/** Cached probe; pass force=true to re-detect (user installed CLI mid-session). */
|
|
17
|
+
export declare function probeClaudeCli(force?: boolean): Promise<CCAvailability>;
|
|
18
|
+
/** Cached probe for the OpenAI Codex CLI; force=true re-detects. */
|
|
19
|
+
export declare function probeCodexCli(force?: boolean): Promise<CCAvailability>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { delimiter } from "node:path";
|
|
3
|
+
/** macOS GUI-launched Electron has a minimal PATH (no Homebrew). Prepend common
|
|
4
|
+
* CLI dirs so `claude` resolves. Mirrors resident-agent.ts's fix. */
|
|
5
|
+
export function pathWithCommonBins(env = process.env) {
|
|
6
|
+
const extra = ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"];
|
|
7
|
+
const current = (env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
8
|
+
const merged = [];
|
|
9
|
+
for (const dir of [...extra, ...current])
|
|
10
|
+
if (!merged.includes(dir))
|
|
11
|
+
merged.push(dir);
|
|
12
|
+
return merged.join(delimiter);
|
|
13
|
+
}
|
|
14
|
+
/** Default runner: `<command> --version` with PATH fix. */
|
|
15
|
+
const defaultRunner = (command) => new Promise((resolve, reject) => {
|
|
16
|
+
const child = spawn(command, ["--version"], {
|
|
17
|
+
env: { ...process.env, PATH: pathWithCommonBins() },
|
|
18
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
19
|
+
});
|
|
20
|
+
let stdout = "";
|
|
21
|
+
child.stdout?.on("data", (c) => (stdout += String(c)));
|
|
22
|
+
child.on("error", reject);
|
|
23
|
+
child.on("exit", (code) => resolve({ ok: code === 0, stdout }));
|
|
24
|
+
});
|
|
25
|
+
/** Probe a CLI's availability. Injectable runner for tests. */
|
|
26
|
+
export async function probeCli(command, runner = defaultRunner) {
|
|
27
|
+
try {
|
|
28
|
+
const { ok, stdout } = await runner(command);
|
|
29
|
+
if (!ok)
|
|
30
|
+
return { available: false, command, reason: "not-executable" };
|
|
31
|
+
return { available: true, command, version: stdout.trim() || undefined };
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
const code = err.code;
|
|
35
|
+
return { available: false, command, reason: code === "ENOENT" ? "not-found" : "not-executable" };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
let cached;
|
|
39
|
+
/** Cached probe; pass force=true to re-detect (user installed CLI mid-session). */
|
|
40
|
+
export async function probeClaudeCli(force = false) {
|
|
41
|
+
if (cached && !force)
|
|
42
|
+
return cached;
|
|
43
|
+
cached = await probeCli("claude");
|
|
44
|
+
return cached;
|
|
45
|
+
}
|
|
46
|
+
let cachedCodex;
|
|
47
|
+
/** Cached probe for the OpenAI Codex CLI; force=true re-detects. */
|
|
48
|
+
export async function probeCodexCli(force = false) {
|
|
49
|
+
if (cachedCodex && !force)
|
|
50
|
+
return cachedCodex;
|
|
51
|
+
cachedCodex = await probeCli("codex");
|
|
52
|
+
return cachedCodex;
|
|
53
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { DiscoveredSession, DiscoverOptions } from "./session-discovery.js";
|
|
2
|
+
/**
|
|
3
|
+
* Discover codex CLI sessions for a given `cwd`, mirroring the claude-side
|
|
4
|
+
* `discoverSessions` but for codex's storage layout, which is fundamentally
|
|
5
|
+
* different:
|
|
6
|
+
*
|
|
7
|
+
* ~/.codex/sessions/YYYY/MM/DD/rollout-<ts>-<uuid>.jsonl
|
|
8
|
+
*
|
|
9
|
+
* Unlike claude (one dir per project), codex stores rollouts by DATE, not by
|
|
10
|
+
* cwd. The cwd lives INSIDE the file: the first line is a `session_meta` event
|
|
11
|
+
* whose `payload` carries `{ id, cwd, timestamp }` (`id` is the thread id we
|
|
12
|
+
* resume by). So discovery means: walk every rollout file, read just its first
|
|
13
|
+
* line to learn its cwd, and keep the ones matching the requested project.
|
|
14
|
+
*
|
|
15
|
+
* Codex rollout files can be large, so we deliberately AVOID `readFileSync` on
|
|
16
|
+
* the whole file (the claude version reads the whole file because claude
|
|
17
|
+
* sessions are small). We read the first line for the meta, and — only for a
|
|
18
|
+
* matching file — read a bounded prefix to find the first real user message for
|
|
19
|
+
* the title. Returns the same `DiscoveredSession` shape so the UI is CLI-blind.
|
|
20
|
+
*/
|
|
21
|
+
export declare function discoverCodexSessions(cwd: string, codexHome?: string, opts?: DiscoverOptions): DiscoveredSession[];
|
|
22
|
+
/** Count codex sessions for `cwd` (reads first-line meta of every rollout; no
|
|
23
|
+
* window). Used so the UI knows whether a bounded list left older ones hidden. */
|
|
24
|
+
export declare function countCodexSessions(cwd: string, codexHome?: string): number;
|