@cjhyy/code-shell-core 0.5.0-rc.1 → 0.6.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -10
- package/dist/agent/agent-definition-registry.d.ts +21 -3
- package/dist/agent/agent-definition-registry.js +49 -18
- package/dist/agent/agent-definition.d.ts +32 -0
- package/dist/agent/agent-definition.js +49 -4
- package/dist/arena/arena.js +11 -12
- package/dist/arena/context/context-tools.js +47 -6
- package/dist/arena/context/within-root.d.ts +7 -0
- package/dist/arena/context/within-root.js +15 -0
- package/dist/arena/iterate/phases/argue.js +8 -2
- package/dist/arena/iterate/phases/revise.js +1 -1
- package/dist/arena/iterate/phases/tournament.js +3 -3
- package/dist/arena/ledger.js +9 -1
- package/dist/arena/phases/adjudication.js +1 -4
- package/dist/arena/phases/build-consensus.js +1 -4
- package/dist/arena/phases/cross-review.js +2 -8
- package/dist/arena/phases/debate-rounds.js +1 -4
- package/dist/arena/phases/participant-research.js +1 -4
- package/dist/arena/phases/planning-detail-expansion.js +8 -10
- package/dist/arena/planner.js +0 -1
- package/dist/arena/providers/docs.d.ts +1 -0
- package/dist/arena/providers/docs.js +5 -2
- package/dist/arena/providers/repo.d.ts +1 -0
- package/dist/arena/providers/repo.js +5 -2
- package/dist/arena/strategies/utils.d.ts +1 -4
- package/dist/arena/strategies/utils.js +7 -51
- package/dist/arena/transitions.d.ts +7 -2
- package/dist/arena/transitions.js +9 -4
- package/dist/arena/types.d.ts +8 -2
- package/dist/automation/cron-expr.d.ts +41 -0
- package/dist/automation/cron-expr.js +169 -0
- package/dist/automation/index.d.ts +41 -0
- package/dist/automation/index.js +39 -0
- package/dist/automation/runner.d.ts +84 -0
- package/dist/automation/runner.js +92 -0
- package/dist/automation/scheduler.d.ts +194 -0
- package/dist/automation/scheduler.js +623 -0
- package/dist/automation/store.d.ts +39 -0
- package/dist/automation/store.js +122 -0
- package/dist/automation/write-policy.d.ts +34 -0
- package/dist/automation/write-policy.js +113 -0
- package/dist/automation/write-run.d.ts +51 -0
- package/dist/automation/write-run.js +38 -0
- package/dist/capability-control/disabled-lists.d.ts +25 -0
- package/dist/capability-control/disabled-lists.js +57 -0
- package/dist/capability-control/index.d.ts +5 -0
- package/dist/capability-control/index.js +3 -0
- package/dist/capability-control/overlay.d.ts +64 -0
- package/dist/capability-control/overlay.js +116 -0
- package/dist/capability-control/project.d.ts +65 -0
- package/dist/capability-control/project.js +147 -0
- package/dist/capability-control/service.d.ts +70 -0
- package/dist/capability-control/service.js +147 -0
- package/dist/capability-control/types.d.ts +62 -0
- package/dist/capability-control/types.js +17 -0
- package/dist/cc-orchestrator/agent-adapter.d.ts +50 -0
- package/dist/cc-orchestrator/agent-adapter.js +133 -0
- package/dist/cc-orchestrator/cc-capability.d.ts +19 -0
- package/dist/cc-orchestrator/cc-capability.js +53 -0
- package/dist/cc-orchestrator/codex-session-discovery.d.ts +24 -0
- package/dist/cc-orchestrator/codex-session-discovery.js +191 -0
- package/dist/cc-orchestrator/codex-session-history.d.ts +25 -0
- package/dist/cc-orchestrator/codex-session-history.js +187 -0
- package/dist/cc-orchestrator/external-agent-changes.d.ts +19 -0
- package/dist/cc-orchestrator/external-agent-changes.js +214 -0
- package/dist/cc-orchestrator/external-agent-driver.d.ts +18 -0
- package/dist/cc-orchestrator/external-agent-driver.js +69 -0
- package/dist/cc-orchestrator/index.d.ts +8 -0
- package/dist/cc-orchestrator/index.js +8 -0
- package/dist/cc-orchestrator/relevance-judge.d.ts +15 -0
- package/dist/cc-orchestrator/relevance-judge.js +29 -0
- package/dist/cc-orchestrator/session-discovery.d.ts +46 -0
- package/dist/cc-orchestrator/session-discovery.js +125 -0
- package/dist/cc-orchestrator/session-history.d.ts +19 -0
- package/dist/cc-orchestrator/session-history.js +67 -0
- package/dist/cli/agent-server-stdio.d.ts +26 -1
- package/dist/cli/agent-server-stdio.js +221 -28
- package/dist/cli/agent-server-tcp.d.ts +20 -0
- package/dist/cli/agent-server-tcp.js +134 -0
- package/dist/cli/graceful-shutdown.d.ts +27 -0
- package/dist/cli/graceful-shutdown.js +30 -0
- package/dist/context/compaction.d.ts +56 -0
- package/dist/context/compaction.js +209 -15
- package/dist/context/manager.d.ts +0 -20
- package/dist/context/manager.js +25 -41
- package/dist/cost-tracker.js +5 -61
- package/dist/credentials/cipher.d.ts +49 -0
- package/dist/credentials/cipher.js +45 -0
- package/dist/credentials/cookie-jar.d.ts +24 -0
- package/dist/credentials/cookie-jar.js +40 -0
- package/dist/credentials/index.d.ts +7 -0
- package/dist/credentials/index.js +5 -0
- package/dist/credentials/inject-credential-tool.d.ts +20 -0
- package/dist/credentials/inject-credential-tool.js +130 -0
- package/dist/credentials/store.d.ts +72 -0
- package/dist/credentials/store.js +184 -0
- package/dist/credentials/types.d.ts +56 -0
- package/dist/credentials/use-credential-tool.d.ts +29 -0
- package/dist/credentials/use-credential-tool.js +205 -0
- package/dist/credentials/use-gate.d.ts +56 -0
- package/dist/credentials/use-gate.js +52 -0
- package/dist/cron/cron-runtime.d.ts +2 -0
- package/dist/cron/cron-runtime.js +2 -0
- package/dist/cron/cron-store.d.ts +2 -0
- package/dist/cron/cron-store.js +2 -0
- package/dist/cron/scheduler.d.ts +5 -32
- package/dist/cron/scheduler.js +5 -113
- package/dist/data/model-metadata.d.ts +77 -0
- package/dist/data/model-metadata.js +56 -0
- package/dist/data/model-metadata.json +216 -0
- package/dist/data/openrouter-models.d.ts +18 -7
- package/dist/data/openrouter-models.js +35 -8
- package/dist/data/openrouter-sync.js +1 -1
- package/dist/engine/aux-key.d.ts +10 -0
- package/dist/engine/aux-key.js +11 -0
- package/dist/engine/dynamic-tool-defs.d.ts +19 -0
- package/dist/engine/dynamic-tool-defs.js +36 -0
- package/dist/engine/engine.d.ts +431 -108
- package/dist/engine/engine.js +1630 -279
- package/dist/engine/friendly-error.d.ts +18 -0
- package/dist/engine/friendly-error.js +63 -0
- package/dist/engine/goal.d.ts +187 -0
- package/dist/engine/goal.js +196 -0
- package/dist/engine/image-compression.d.ts +81 -0
- package/dist/engine/image-compression.js +186 -0
- package/dist/engine/image-policy.d.ts +165 -0
- package/dist/engine/image-policy.js +189 -0
- package/dist/engine/model-connections-pool.d.ts +17 -0
- package/dist/engine/model-connections-pool.js +66 -0
- package/dist/engine/model-facade.d.ts +10 -0
- package/dist/engine/model-facade.js +25 -0
- package/dist/engine/patch-orphaned-tools.js +3 -0
- package/dist/engine/reactive-threshold.d.ts +13 -0
- package/dist/engine/reactive-threshold.js +17 -0
- package/dist/engine/resolve-llm-config.d.ts +16 -0
- package/dist/engine/resolve-llm-config.js +44 -0
- package/dist/engine/runtime.js +9 -1
- package/dist/engine/sandbox-cache-key.d.ts +10 -0
- package/dist/engine/sandbox-cache-key.js +9 -0
- package/dist/engine/sandbox-config.d.ts +31 -0
- package/dist/engine/sandbox-config.js +38 -0
- package/dist/engine/session-title.d.ts +13 -0
- package/dist/engine/session-title.js +34 -0
- package/dist/engine/session-usage.d.ts +19 -0
- package/dist/engine/session-usage.js +25 -0
- package/dist/engine/steer-queue.d.ts +32 -0
- package/dist/engine/steer-queue.js +26 -0
- package/dist/engine/streaming-tool-queue.d.ts +12 -0
- package/dist/engine/streaming-tool-queue.js +50 -10
- package/dist/engine/turn-loop.d.ts +94 -9
- package/dist/engine/turn-loop.js +775 -342
- package/dist/engine/types.d.ts +175 -0
- package/dist/engine/types.js +13 -0
- package/dist/external-agents/config.d.ts +2 -0
- package/dist/external-agents/config.js +15 -0
- package/dist/external-agents/types.d.ts +31 -0
- package/dist/external-agents/types.js +1 -0
- package/dist/git/parse-log.d.ts +13 -0
- package/dist/git/parse-log.js +21 -0
- package/dist/git/utils.d.ts +8 -6
- package/dist/git/utils.js +62 -26
- package/dist/git/worktree.d.ts +48 -0
- package/dist/git/worktree.js +111 -20
- package/dist/hooks/events.d.ts +23 -1
- package/dist/hooks/goal-stop-hook.d.ts +58 -0
- package/dist/hooks/goal-stop-hook.js +279 -0
- package/dist/hooks/hook-output.d.ts +33 -0
- package/dist/hooks/hook-output.js +79 -0
- package/dist/hooks/registry.d.ts +15 -0
- package/dist/hooks/registry.js +57 -1
- package/dist/hooks/shell-runner.d.ts +2 -0
- package/dist/hooks/shell-runner.js +48 -18
- package/dist/index.d.ts +79 -11
- package/dist/index.js +89 -6
- package/dist/llm/capabilities/param-specs.d.ts +14 -0
- package/dist/llm/capabilities/param-specs.js +62 -0
- package/dist/llm/capabilities/reasoning-control.d.ts +24 -0
- package/dist/llm/capabilities/reasoning-control.js +38 -0
- package/dist/llm/capabilities/rules.js +37 -3
- package/dist/llm/capabilities/types.d.ts +32 -2
- package/dist/llm/clamp-max-tokens.d.ts +11 -0
- package/dist/llm/clamp-max-tokens.js +17 -0
- package/dist/llm/client-base.d.ts +69 -5
- package/dist/llm/client-base.js +185 -25
- package/dist/llm/client-factory.d.ts +3 -3
- package/dist/llm/client-factory.js +2 -2
- package/dist/llm/model-cache.js +4 -2
- package/dist/llm/model-pool.d.ts +29 -11
- package/dist/llm/model-pool.js +36 -20
- package/dist/llm/provider-auth.d.ts +41 -0
- package/dist/llm/provider-auth.js +76 -0
- package/dist/llm/provider-catalog.d.ts +6 -2
- package/dist/llm/providers/anthropic.d.ts +32 -2
- package/dist/llm/providers/anthropic.js +207 -18
- package/dist/llm/providers/openai.d.ts +47 -2
- package/dist/llm/providers/openai.js +478 -71
- package/dist/llm/reasoning-setting.d.ts +51 -0
- package/dist/llm/reasoning-setting.js +40 -0
- package/dist/llm/stop-reason.d.ts +13 -0
- package/dist/llm/stop-reason.js +21 -0
- package/dist/llm/stream-watchdog.js +5 -1
- package/dist/llm/strip-vision.d.ts +32 -0
- package/dist/llm/strip-vision.js +94 -0
- package/dist/llm/token-counter.js +9 -2
- package/dist/llm/types.d.ts +9 -6
- package/dist/logging/logger.js +18 -4
- package/dist/logging/sanitize-messages.d.ts +10 -0
- package/dist/logging/sanitize-messages.js +112 -0
- package/dist/lsp/client.js +28 -8
- package/dist/lsp/manager.d.ts +1 -1
- package/dist/lsp/manager.js +42 -11
- package/dist/lsp/root-path.d.ts +9 -0
- package/dist/lsp/root-path.js +12 -0
- package/dist/model-catalog/builtin.d.ts +12 -0
- package/dist/model-catalog/builtin.js +407 -0
- package/dist/model-catalog/gen-connections.d.ts +20 -0
- package/dist/model-catalog/gen-connections.js +28 -0
- package/dist/model-catalog/index.d.ts +41 -0
- package/dist/model-catalog/index.js +90 -0
- package/dist/model-catalog/params.d.ts +20 -0
- package/dist/model-catalog/params.js +45 -0
- package/dist/model-catalog/resolve.d.ts +48 -0
- package/dist/model-catalog/resolve.js +33 -0
- package/dist/model-catalog/save-entry.d.ts +32 -0
- package/dist/model-catalog/save-entry.js +104 -0
- package/dist/model-catalog/types.d.ts +561 -0
- package/dist/model-catalog/types.js +93 -0
- package/dist/model-catalog/upsert.d.ts +9 -0
- package/dist/model-catalog/upsert.js +8 -0
- package/dist/onboarding.d.ts +12 -82
- package/dist/onboarding.js +75 -333
- package/dist/plugins/gitOps.d.ts +53 -0
- package/dist/plugins/gitOps.js +130 -5
- package/dist/plugins/installer/checkUpdate.d.ts +16 -0
- package/dist/plugins/installer/checkUpdate.js +32 -0
- package/dist/plugins/installer/codex/convertAgents.d.ts +7 -0
- package/dist/plugins/installer/codex/convertAgents.js +55 -0
- package/dist/plugins/installer/codex/convertCommands.d.ts +19 -0
- package/dist/plugins/installer/codex/convertCommands.js +46 -0
- package/dist/plugins/installer/codex/convertMcp.d.ts +11 -0
- package/dist/plugins/installer/codex/convertMcp.js +85 -0
- package/dist/plugins/installer/codex/convertSkills.d.ts +12 -0
- package/dist/plugins/installer/codex/convertSkills.js +33 -0
- package/dist/plugins/installer/detectFormat.d.ts +2 -0
- package/dist/plugins/installer/detectFormat.js +6 -0
- package/dist/plugins/installer/install.d.ts +7 -0
- package/dist/plugins/installer/install.js +123 -0
- package/dist/plugins/installer/installFromArchive.d.ts +43 -0
- package/dist/plugins/installer/installFromArchive.js +134 -0
- package/dist/plugins/installer/installFromSource.d.ts +13 -0
- package/dist/plugins/installer/installFromSource.js +51 -0
- package/dist/plugins/installer/list.d.ts +9 -0
- package/dist/plugins/installer/list.js +30 -0
- package/dist/plugins/installer/loadPluginAgents.d.ts +3 -0
- package/dist/plugins/installer/loadPluginAgents.js +27 -0
- package/dist/plugins/installer/loadPluginMcp.d.ts +14 -0
- package/dist/plugins/installer/loadPluginMcp.js +107 -0
- package/dist/plugins/installer/parseSource.d.ts +20 -0
- package/dist/plugins/installer/parseSource.js +74 -0
- package/dist/plugins/installer/paths.d.ts +5 -0
- package/dist/plugins/installer/paths.js +25 -0
- package/dist/plugins/installer/pruneDisabled.d.ts +24 -0
- package/dist/plugins/installer/pruneDisabled.js +73 -0
- package/dist/plugins/installer/types.d.ts +52 -0
- package/dist/plugins/installer/types.js +28 -0
- package/dist/plugins/installer/uninstall.d.ts +2 -0
- package/dist/plugins/installer/uninstall.js +18 -0
- package/dist/plugins/installer/unzip.d.ts +14 -0
- package/dist/plugins/installer/unzip.js +82 -0
- package/dist/plugins/installer/update.d.ts +28 -0
- package/dist/plugins/installer/update.js +97 -0
- package/dist/plugins/loadPluginHooks.d.ts +54 -1
- package/dist/plugins/loadPluginHooks.js +83 -1
- package/dist/plugins/marketplaceManager.d.ts +15 -1
- package/dist/plugins/marketplaceManager.js +58 -4
- package/dist/plugins/pluginCommandHook.js +34 -14
- package/dist/plugins/pluginContent.d.ts +30 -0
- package/dist/plugins/pluginContent.js +83 -0
- package/dist/plugins/pluginInstaller.d.ts +34 -0
- package/dist/plugins/pluginInstaller.js +168 -35
- package/dist/plugins/schemas.js +41 -6
- package/dist/plugins/types.d.ts +10 -0
- package/dist/preset/index.d.ts +7 -1
- package/dist/preset/index.js +112 -6
- package/dist/product/types.d.ts +1 -1
- package/dist/prompt/composer.d.ts +31 -0
- package/dist/prompt/composer.js +95 -25
- package/dist/prompt/instruction-scanner.js +5 -3
- package/dist/prompt/section-loader.js +1 -0
- package/dist/prompt/sections/base.md +1 -0
- package/dist/prompt/sections/browser.md +10 -0
- package/dist/prompt/sections/coding.md +4 -0
- package/dist/prompt/sections/orchestration.md +8 -0
- package/dist/protocol/chat-session-manager.d.ts +15 -2
- package/dist/protocol/chat-session-manager.js +46 -0
- package/dist/protocol/chat-session.d.ts +82 -0
- package/dist/protocol/chat-session.js +121 -1
- package/dist/protocol/client.d.ts +27 -0
- package/dist/protocol/client.js +38 -0
- package/dist/protocol/redact.d.ts +50 -0
- package/dist/protocol/redact.js +71 -0
- package/dist/protocol/server.d.ts +153 -2
- package/dist/protocol/server.js +807 -39
- package/dist/protocol/tcp-transport.d.ts +45 -0
- package/dist/protocol/tcp-transport.js +74 -0
- package/dist/protocol/transport.js +3 -2
- package/dist/protocol/types.d.ts +69 -0
- package/dist/protocol/types.js +22 -0
- package/dist/quota/credentials.d.ts +3 -0
- package/dist/quota/credentials.js +80 -0
- package/dist/quota/index.d.ts +36 -0
- package/dist/quota/index.js +155 -0
- package/dist/quota/types.d.ts +48 -0
- package/dist/quota/types.js +13 -0
- package/dist/remote/bridge.d.ts +11 -2
- package/dist/remote/bridge.js +61 -33
- package/dist/review/review-prompt.d.ts +28 -0
- package/dist/review/review-prompt.js +81 -0
- package/dist/run/ArtifactTracker.js +5 -4
- package/dist/run/EngineRunner.d.ts +27 -1
- package/dist/run/EngineRunner.js +46 -8
- package/dist/run/FileRunStore.js +40 -11
- package/dist/run/RunApprovalBackend.js +25 -5
- package/dist/run/RunLock.d.ts +28 -2
- package/dist/run/RunLock.js +49 -7
- package/dist/run/RunManager.d.ts +12 -0
- package/dist/run/RunManager.js +86 -3
- package/dist/run/RunQueue.d.ts +1 -0
- package/dist/run/RunQueue.js +8 -2
- package/dist/run/factory.d.ts +8 -1
- package/dist/run/factory.js +1 -0
- package/dist/run/index.d.ts +2 -2
- package/dist/run/index.js +1 -1
- package/dist/run/redirect-target.d.ts +7 -0
- package/dist/run/redirect-target.js +13 -0
- package/dist/run/types.js +1 -1
- package/dist/runtime/background-shell.d.ts +139 -0
- package/dist/runtime/background-shell.js +495 -0
- package/dist/runtime/output-clean.d.ts +24 -0
- package/dist/runtime/output-clean.js +41 -0
- package/dist/runtime/ring-file.d.ts +64 -0
- package/dist/runtime/ring-file.js +174 -0
- package/dist/runtime/safe-spawn.js +15 -34
- package/dist/runtime/spawn-common.d.ts +148 -0
- package/dist/runtime/spawn-common.js +303 -0
- package/dist/runtime/truncate-output.d.ts +22 -0
- package/dist/runtime/truncate-output.js +49 -0
- package/dist/runtime/utf8-cut.d.ts +11 -0
- package/dist/runtime/utf8-cut.js +23 -0
- package/dist/services/auto-dream.d.ts +4 -0
- package/dist/services/auto-dream.js +26 -26
- package/dist/services/browser-open.d.ts +13 -0
- package/dist/services/browser-open.js +16 -0
- package/dist/services/diagnostics.d.ts +1 -2
- package/dist/services/diagnostics.js +12 -7
- package/dist/services/dream-consolidation.d.ts +57 -0
- package/dist/services/dream-consolidation.js +151 -0
- package/dist/services/extract-memories.d.ts +14 -1
- package/dist/services/extract-memories.js +45 -6
- package/dist/services/memory-orchestrator.d.ts +21 -0
- package/dist/services/memory-orchestrator.js +125 -32
- package/dist/services/notifier.d.ts +8 -0
- package/dist/services/notifier.js +39 -25
- package/dist/services/oauth.js +5 -7
- package/dist/services/session-memory-sort.d.ts +8 -0
- package/dist/services/session-memory-sort.js +9 -0
- package/dist/services/session-memory.js +30 -19
- package/dist/session/file-history.d.ts +124 -1
- package/dist/session/file-history.js +232 -9
- package/dist/session/memory.d.ts +116 -2
- package/dist/session/memory.js +250 -28
- package/dist/session/session-manager.d.ts +63 -1
- package/dist/session/session-manager.js +186 -5
- package/dist/session/simple-diff.d.ts +23 -0
- package/dist/session/simple-diff.js +84 -0
- package/dist/session/transcript.d.ts +25 -1
- package/dist/session/transcript.js +35 -2
- package/dist/session/undo-target.d.ts +67 -0
- package/dist/session/undo-target.js +144 -0
- package/dist/settings/disk-defaults.d.ts +42 -0
- package/dist/settings/disk-defaults.js +33 -0
- package/dist/settings/feature-flags.d.ts +64 -0
- package/dist/settings/feature-flags.js +61 -0
- package/dist/settings/manager.d.ts +101 -1
- package/dist/settings/manager.js +345 -15
- package/dist/settings/migrate-config.d.ts +45 -0
- package/dist/settings/migrate-config.js +125 -0
- package/dist/settings/personalization.d.ts +19 -0
- package/dist/settings/personalization.js +7 -0
- package/dist/settings/schema-export.d.ts +25 -0
- package/dist/settings/schema-export.js +38 -0
- package/dist/settings/schema.d.ts +1827 -396
- package/dist/settings/schema.js +361 -81
- package/dist/skills/scanner.d.ts +9 -0
- package/dist/skills/scanner.js +30 -2
- package/dist/stt/resolve-transcribe.d.ts +31 -0
- package/dist/stt/resolve-transcribe.js +108 -0
- package/dist/stt/transcribe.d.ts +51 -0
- package/dist/stt/transcribe.js +65 -0
- package/dist/tool-system/browser-bridge.d.ts +226 -0
- package/dist/tool-system/browser-bridge.js +163 -0
- package/dist/tool-system/builtin/add-marketplace.d.ts +12 -0
- package/dist/tool-system/builtin/add-marketplace.js +76 -0
- package/dist/tool-system/builtin/agent-heartbeat.d.ts +49 -0
- package/dist/tool-system/builtin/agent-heartbeat.js +89 -0
- package/dist/tool-system/builtin/agent-notifications.d.ts +12 -3
- package/dist/tool-system/builtin/agent-notifications.js +9 -3
- package/dist/tool-system/builtin/agent-output-file.d.ts +38 -0
- package/dist/tool-system/builtin/agent-output-file.js +72 -0
- package/dist/tool-system/builtin/agent-registry.d.ts +23 -0
- package/dist/tool-system/builtin/agent-registry.js +16 -4
- package/dist/tool-system/builtin/agent.d.ts +50 -11
- package/dist/tool-system/builtin/agent.js +576 -73
- package/dist/tool-system/builtin/apply-patch/applier.js +66 -8
- package/dist/tool-system/builtin/apply-patch/backup-targets.d.ts +10 -0
- package/dist/tool-system/builtin/apply-patch/backup-targets.js +30 -0
- package/dist/tool-system/builtin/apply-patch/index.js +7 -3
- package/dist/tool-system/builtin/arena.js +6 -7
- package/dist/tool-system/builtin/background-jobs.d.ts +76 -0
- package/dist/tool-system/builtin/background-jobs.js +124 -0
- package/dist/tool-system/builtin/background-shell-tools.d.ts +20 -0
- package/dist/tool-system/builtin/background-shell-tools.js +108 -0
- package/dist/tool-system/builtin/background-work.d.ts +67 -0
- package/dist/tool-system/builtin/background-work.js +86 -0
- package/dist/tool-system/builtin/bash-output-style.d.ts +32 -0
- package/dist/tool-system/builtin/bash-output-style.js +40 -0
- package/dist/tool-system/builtin/bash.d.ts +5 -2
- package/dist/tool-system/builtin/bash.js +98 -62
- package/dist/tool-system/builtin/browser-tools.d.ts +33 -0
- package/dist/tool-system/builtin/browser-tools.js +312 -0
- package/dist/tool-system/builtin/cancel-goal.d.ts +31 -0
- package/dist/tool-system/builtin/cancel-goal.js +64 -0
- package/dist/tool-system/builtin/check-quota.d.ts +15 -0
- package/dist/tool-system/builtin/check-quota.js +34 -0
- package/dist/tool-system/builtin/complete-goal.d.ts +25 -0
- package/dist/tool-system/builtin/complete-goal.js +45 -0
- package/dist/tool-system/builtin/config.js +7 -0
- package/dist/tool-system/builtin/cron.d.ts +7 -0
- package/dist/tool-system/builtin/cron.js +105 -9
- package/dist/tool-system/builtin/drive-claude-code.d.ts +30 -0
- package/dist/tool-system/builtin/drive-claude-code.js +157 -0
- package/dist/tool-system/builtin/edit-model-catalog.d.ts +3 -0
- package/dist/tool-system/builtin/edit-model-catalog.js +94 -0
- package/dist/tool-system/builtin/edit.d.ts +2 -1
- package/dist/tool-system/builtin/edit.js +21 -12
- package/dist/tool-system/builtin/eol.d.ts +29 -0
- package/dist/tool-system/builtin/eol.js +37 -0
- package/dist/tool-system/builtin/file-cache.d.ts +6 -0
- package/dist/tool-system/builtin/file-cache.js +8 -0
- package/dist/tool-system/builtin/generate-image.d.ts +60 -0
- package/dist/tool-system/builtin/generate-image.js +374 -0
- package/dist/tool-system/builtin/generate-video.d.ts +55 -0
- package/dist/tool-system/builtin/generate-video.js +361 -0
- package/dist/tool-system/builtin/grep.js +6 -2
- package/dist/tool-system/builtin/image-providers.d.ts +86 -0
- package/dist/tool-system/builtin/image-providers.js +190 -0
- package/dist/tool-system/builtin/image-uploader.d.ts +33 -0
- package/dist/tool-system/builtin/image-uploader.js +74 -0
- package/dist/tool-system/builtin/index.d.ts +24 -1
- package/dist/tool-system/builtin/index.js +310 -25
- package/dist/tool-system/builtin/mcp-tools.js +23 -3
- package/dist/tool-system/builtin/memory.js +45 -7
- package/dist/tool-system/builtin/notebook-edit.d.ts +2 -1
- package/dist/tool-system/builtin/notebook-edit.js +1 -1
- package/dist/tool-system/builtin/powershell.js +4 -1
- package/dist/tool-system/builtin/read.d.ts +2 -1
- package/dist/tool-system/builtin/read.js +11 -3
- package/dist/tool-system/builtin/repl.js +4 -1
- package/dist/tool-system/builtin/skill.js +9 -0
- package/dist/tool-system/builtin/sleep.js +12 -5
- package/dist/tool-system/builtin/task.js +4 -1
- package/dist/tool-system/builtin/tool-search.js +25 -7
- package/dist/tool-system/builtin/update-automation-memory.d.ts +24 -0
- package/dist/tool-system/builtin/update-automation-memory.js +60 -0
- package/dist/tool-system/builtin/video-providers.d.ts +154 -0
- package/dist/tool-system/builtin/video-providers.js +235 -0
- package/dist/tool-system/builtin/view-image.d.ts +18 -0
- package/dist/tool-system/builtin/view-image.js +96 -0
- package/dist/tool-system/builtin/web-fetch.js +12 -2
- package/dist/tool-system/builtin/web-search.d.ts +4 -1
- package/dist/tool-system/builtin/web-search.js +57 -10
- package/dist/tool-system/builtin/worktree.d.ts +2 -1
- package/dist/tool-system/builtin/worktree.js +31 -4
- package/dist/tool-system/builtin/write.d.ts +2 -1
- package/dist/tool-system/builtin/write.js +1 -1
- package/dist/tool-system/context.d.ts +166 -6
- package/dist/tool-system/executor.d.ts +3 -2
- package/dist/tool-system/executor.js +197 -119
- package/dist/tool-system/investigation-guard.d.ts +4 -1
- package/dist/tool-system/investigation-guard.js +20 -4
- package/dist/tool-system/mcp-manager.d.ts +78 -16
- package/dist/tool-system/mcp-manager.js +357 -28
- package/dist/tool-system/path-policy.d.ts +99 -0
- package/dist/tool-system/path-policy.js +578 -0
- package/dist/tool-system/permission.d.ts +36 -1
- package/dist/tool-system/permission.js +322 -117
- package/dist/tool-system/plan-mode-allowlist.d.ts +32 -0
- package/dist/tool-system/plan-mode-allowlist.js +65 -0
- package/dist/tool-system/registry.d.ts +1 -0
- package/dist/tool-system/registry.js +26 -1
- package/dist/tool-system/sandbox/index.d.ts +8 -0
- package/dist/tool-system/sandbox/index.js +7 -2
- package/dist/tool-system/validate-tool-metadata.d.ts +36 -0
- package/dist/tool-system/validate-tool-metadata.js +63 -0
- package/dist/tool-system/validation.d.ts +10 -0
- package/dist/tool-system/validation.js +16 -3
- package/dist/types.d.ts +301 -15
- package/dist/updater.js +20 -9
- package/dist/utils/envUtils.d.ts +0 -9
- package/dist/utils/envUtils.js +3 -28
- package/dist/utils/exec.d.ts +40 -0
- package/dist/utils/exec.js +144 -0
- package/dist/utils/format.js +4 -2
- package/dist/utils/json.d.ts +12 -0
- package/dist/utils/json.js +92 -0
- package/dist/utils/lockfile.js +9 -2
- package/dist/utils/theme.d.ts +7 -1
- package/dist/utils/theme.js +5 -3
- package/dist/utils/toolDisplay.js +0 -1
- package/package.json +14 -7
- package/dist/agent/coordinator.d.ts +0 -49
- package/dist/agent/coordinator.js +0 -77
- package/dist/settings/manager.test.js +0 -73
- package/dist/tool-system/builtin/remote-trigger.d.ts +0 -6
- package/dist/tool-system/builtin/remote-trigger.js +0 -54
- package/dist/tool-system/builtin/send-message.d.ts +0 -6
- package/dist/tool-system/builtin/send-message.js +0 -47
- /package/dist/{settings/manager.test.d.ts → credentials/types.js} +0 -0
|
@@ -7,10 +7,57 @@ import { homedir } from "node:os";
|
|
|
7
7
|
import { nanoid } from "nanoid";
|
|
8
8
|
import { Transcript } from "./transcript.js";
|
|
9
9
|
import { SessionError } from "../exceptions.js";
|
|
10
|
+
/**
|
|
11
|
+
* Validate a session ID before it is joined into a filesystem path.
|
|
12
|
+
*
|
|
13
|
+
* Internally generated IDs use `nanoid(16)` and are trusted by construction.
|
|
14
|
+
* But every public entry point (`create`'s explicitSessionId, `resume`,
|
|
15
|
+
* `exists`, `saveState`'s state.sessionId, `fork`'s sourceSessionId) accepts
|
|
16
|
+
* an ID from an outside caller — protocol clients, ChatSessionManager-driven
|
|
17
|
+
* cold starts, persisted state files — and join()'s it into `sessionsDir`.
|
|
18
|
+
* Without this check a value like "../etc/passwd" or "/tmp/x" would let the
|
|
19
|
+
* caller escape the sessions directory.
|
|
20
|
+
*
|
|
21
|
+
* Exported for direct unit testing.
|
|
22
|
+
*/
|
|
23
|
+
export function assertSafeSessionId(sessionId) {
|
|
24
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
25
|
+
throw new SessionError(`invalid session id: must be a non-empty string`);
|
|
26
|
+
}
|
|
27
|
+
// basename check: reject any path-shaped value. Covers absolute paths,
|
|
28
|
+
// POSIX and Windows separators, parent-dir tokens, and the lone "..".
|
|
29
|
+
if (sessionId.includes("/") || sessionId.includes("\\")) {
|
|
30
|
+
throw new SessionError(`invalid session id: contains path separator: ${sessionId}`);
|
|
31
|
+
}
|
|
32
|
+
if (sessionId === "." || sessionId === ".." || sessionId.includes("..")) {
|
|
33
|
+
throw new SessionError(`invalid session id: contains parent-dir token: ${sessionId}`);
|
|
34
|
+
}
|
|
35
|
+
// Conservative character allow-list: letters, digits, and `-_.` only.
|
|
36
|
+
// This matches what nanoid emits plus the dotted variants in-house code
|
|
37
|
+
// already uses (e.g. "tui-main", "agent.foo"). Anything else (NUL,
|
|
38
|
+
// newline, control chars, shell metacharacters, glob chars) is rejected.
|
|
39
|
+
if (!/^[A-Za-z0-9_.-]+$/.test(sessionId)) {
|
|
40
|
+
throw new SessionError(`invalid session id: unexpected characters: ${sessionId}`);
|
|
41
|
+
}
|
|
42
|
+
// Cap the length to keep filesystem APIs happy and avoid disk-name DoS.
|
|
43
|
+
if (sessionId.length > 128) {
|
|
44
|
+
throw new SessionError(`invalid session id: too long (max 128 chars)`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the `.code-shell` home dir. `CODE_SHELL_HOME` overrides the default
|
|
49
|
+
* `~/.code-shell` — mirrors Codex's `CODEX_HOME`. Tests set it to a temp dir
|
|
50
|
+
* (see bunfig.toml preload) so a `new Engine()` / `new SessionManager()` with
|
|
51
|
+
* no explicit storageDir doesn't pollute the user's real ~/.code-shell/sessions
|
|
52
|
+
* with throwaway test sessions (the rm-usage/test-model sidebar junk).
|
|
53
|
+
*/
|
|
54
|
+
export function codeShellHome() {
|
|
55
|
+
return process.env.CODE_SHELL_HOME || join(homedir(), ".code-shell");
|
|
56
|
+
}
|
|
10
57
|
export class SessionManager {
|
|
11
58
|
sessionsDir;
|
|
12
59
|
constructor(storageDir) {
|
|
13
|
-
this.sessionsDir = storageDir ?? join(
|
|
60
|
+
this.sessionsDir = storageDir ?? join(codeShellHome(), "sessions");
|
|
14
61
|
mkdirSync(this.sessionsDir, { recursive: true });
|
|
15
62
|
}
|
|
16
63
|
/**
|
|
@@ -20,7 +67,12 @@ export class SessionManager {
|
|
|
20
67
|
* nanoid. Either way the on-disk directory is materialized and the
|
|
21
68
|
* state.json + transcript.jsonl files are written before return.
|
|
22
69
|
*/
|
|
23
|
-
create(cwd, model, provider, explicitSessionId) {
|
|
70
|
+
create(cwd, model, provider, explicitSessionId, parentSessionId, origin) {
|
|
71
|
+
// External callers may pass any string; nanoid output is trusted. Either
|
|
72
|
+
// way the ID gets joined into a filesystem path, so the public entry
|
|
73
|
+
// point validates before that join.
|
|
74
|
+
if (explicitSessionId !== undefined)
|
|
75
|
+
assertSafeSessionId(explicitSessionId);
|
|
24
76
|
const sessionId = explicitSessionId ?? nanoid(16);
|
|
25
77
|
const sessionDir = join(this.sessionsDir, sessionId);
|
|
26
78
|
mkdirSync(sessionDir, { recursive: true });
|
|
@@ -34,8 +86,19 @@ export class SessionManager {
|
|
|
34
86
|
turnCount: 0,
|
|
35
87
|
invokedSkills: [],
|
|
36
88
|
status: "active",
|
|
89
|
+
// Always write the key: a sub-agent gets its parent sid; a top-level
|
|
90
|
+
// session gets explicit null. This lets the desktop disk-rebuild tell a
|
|
91
|
+
// new top-level session (key present, null) apart from a legacy session
|
|
92
|
+
// (key absent) and from a sub-agent (key present, non-empty string).
|
|
93
|
+
parentSessionId: parentSessionId ?? null,
|
|
94
|
+
...(origin ? { origin } : {}),
|
|
37
95
|
};
|
|
38
|
-
|
|
96
|
+
// Atomic write (tmp+rename) like saveState, so a crash during this one-time
|
|
97
|
+
// create can't leave a torn state.json that resume() then fails to parse.
|
|
98
|
+
const stateTarget = join(sessionDir, "state.json");
|
|
99
|
+
const stateTmp = `${stateTarget}.${process.pid}.${Date.now()}.create.tmp`;
|
|
100
|
+
writeFileSync(stateTmp, JSON.stringify(state, null, 2), "utf-8");
|
|
101
|
+
renameSync(stateTmp, stateTarget);
|
|
39
102
|
const transcript = new Transcript(join(sessionDir, "transcript.jsonl"));
|
|
40
103
|
transcript.append("session_meta", {
|
|
41
104
|
sessionId,
|
|
@@ -52,9 +115,115 @@ export class SessionManager {
|
|
|
52
115
|
* without catching SessionError.
|
|
53
116
|
*/
|
|
54
117
|
exists(sessionId) {
|
|
118
|
+
// exists() is a probe — callers use it to decide between resume and
|
|
119
|
+
// create-with-explicit-sid. Treat an invalid id as "not present"
|
|
120
|
+
// rather than letting the traversal-shaped string reach existsSync.
|
|
121
|
+
try {
|
|
122
|
+
assertSafeSessionId(sessionId);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
55
127
|
return existsSync(join(this.sessionsDir, sessionId));
|
|
56
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Cheap "what cwd is this session bound to?" probe — reads only state.json,
|
|
131
|
+
* NOT the transcript (unlike resume()). engine.run uses this to recover a
|
|
132
|
+
* resumed session's project directory when the caller omits options.cwd
|
|
133
|
+
* (e.g. a desktop host whose sidebar repo selection has drifted to null),
|
|
134
|
+
* so a project-bound session keeps loading its own agents/settings/memory
|
|
135
|
+
* instead of falling back to process.cwd(). Returns undefined for an
|
|
136
|
+
* unknown, malformed, or traversal-shaped id rather than throwing — the
|
|
137
|
+
* caller treats "no recoverable cwd" the same as "not given".
|
|
138
|
+
*/
|
|
139
|
+
readCwd(sessionId) {
|
|
140
|
+
try {
|
|
141
|
+
assertSafeSessionId(sessionId);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
const stateFile = join(this.sessionsDir, sessionId, "state.json");
|
|
147
|
+
if (!existsSync(stateFile))
|
|
148
|
+
return undefined;
|
|
149
|
+
try {
|
|
150
|
+
const state = JSON.parse(readFileSync(stateFile, "utf-8"));
|
|
151
|
+
return typeof state.cwd === "string" && state.cwd.length > 0 ? state.cwd : undefined;
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Cheap "does this session have a persisted goal?" probe — reads only
|
|
159
|
+
* state.json, NOT the transcript (like readCwd). A persistent goal lives ONLY
|
|
160
|
+
* in state.activeGoal; it is never appended to the transcript as an event, so
|
|
161
|
+
* a session rebuilt from disk (e.g. localStorage wiped) can't recover the
|
|
162
|
+
* goal from its messages. The desktop host calls this on session load to
|
|
163
|
+
* re-surface the active-goal block + its Cancel button, which would otherwise
|
|
164
|
+
* be invisible (and thus uncancellable) after a reload of an aborted goal
|
|
165
|
+
* run. Returns undefined for an unknown / malformed / traversal-shaped id, or
|
|
166
|
+
* a session with no active goal — never throws.
|
|
167
|
+
*/
|
|
168
|
+
readActiveGoal(sessionId) {
|
|
169
|
+
try {
|
|
170
|
+
assertSafeSessionId(sessionId);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
const stateFile = join(this.sessionsDir, sessionId, "state.json");
|
|
176
|
+
if (!existsSync(stateFile))
|
|
177
|
+
return undefined;
|
|
178
|
+
try {
|
|
179
|
+
const state = JSON.parse(readFileSync(stateFile, "utf-8"));
|
|
180
|
+
return state.activeGoal;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Disk-only "wipe this session's persistent goal" — the counterpart to
|
|
188
|
+
* readActiveGoal. Reads state.json, removes state.activeGoal, and rewrites it
|
|
189
|
+
* atomically (via saveState). Returns true only if a goal was actually
|
|
190
|
+
* present (idempotent — clearing a session with no goal is a no-op returning
|
|
191
|
+
* false). Never throws on an unknown / malformed / traversal-shaped id
|
|
192
|
+
* (returns false), matching readActiveGoal's tolerance.
|
|
193
|
+
*
|
|
194
|
+
* Why it lives here and not only on Engine: a persistent goal can strand a
|
|
195
|
+
* session whose worker is NOT live (aborted/reloaded) — Engine.clearGoal
|
|
196
|
+
* needs a resumed in-RAM session and a matching in-flight hook, neither of
|
|
197
|
+
* which exists once the worker has exited. Hosts (the desktop bridge) call
|
|
198
|
+
* THIS to clear such a goal off disk without spinning up a full Engine.
|
|
199
|
+
* Engine.clearGoal also delegates its disk write here so the wipe logic lives
|
|
200
|
+
* in exactly one place.
|
|
201
|
+
*/
|
|
202
|
+
clearActiveGoal(sessionId) {
|
|
203
|
+
try {
|
|
204
|
+
assertSafeSessionId(sessionId);
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
const stateFile = join(this.sessionsDir, sessionId, "state.json");
|
|
210
|
+
if (!existsSync(stateFile))
|
|
211
|
+
return false;
|
|
212
|
+
let state;
|
|
213
|
+
try {
|
|
214
|
+
state = JSON.parse(readFileSync(stateFile, "utf-8"));
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
if (state.activeGoal === undefined)
|
|
220
|
+
return false;
|
|
221
|
+
state.activeGoal = undefined;
|
|
222
|
+
this.saveState(state);
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
57
225
|
resume(sessionId) {
|
|
226
|
+
assertSafeSessionId(sessionId);
|
|
58
227
|
const sessionDir = join(this.sessionsDir, sessionId);
|
|
59
228
|
if (!existsSync(sessionDir)) {
|
|
60
229
|
throw new SessionError(`Session not found: ${sessionId}`);
|
|
@@ -63,13 +232,25 @@ export class SessionManager {
|
|
|
63
232
|
if (!existsSync(stateFile)) {
|
|
64
233
|
throw new SessionError(`Session state file not found: ${sessionId}`);
|
|
65
234
|
}
|
|
66
|
-
|
|
235
|
+
let state;
|
|
236
|
+
try {
|
|
237
|
+
state = JSON.parse(readFileSync(stateFile, "utf-8"));
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
// A corrupt state.json (external tampering, disk corruption, or a crash
|
|
241
|
+
// during the one-time create() write) must surface as a clean SessionError
|
|
242
|
+
// — not a raw SyntaxError that escapes callers expecting SessionError.
|
|
243
|
+
throw new SessionError(`Session state is corrupt for ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
244
|
+
}
|
|
67
245
|
const transcriptFile = join(sessionDir, "transcript.jsonl");
|
|
68
246
|
const transcript = Transcript.loadFromFile(transcriptFile);
|
|
69
247
|
state.status = "active";
|
|
70
248
|
return { state, transcript };
|
|
71
249
|
}
|
|
72
250
|
saveState(state) {
|
|
251
|
+
// state.sessionId could come from a deserialized state.json that was
|
|
252
|
+
// tampered with on disk. Validate before joining.
|
|
253
|
+
assertSafeSessionId(state.sessionId);
|
|
73
254
|
const sessionDir = join(this.sessionsDir, state.sessionId);
|
|
74
255
|
mkdirSync(sessionDir, { recursive: true });
|
|
75
256
|
// Atomic write: stage to .tmp, then rename. Protects against two processes
|
|
@@ -94,7 +275,7 @@ export class SessionManager {
|
|
|
94
275
|
newBundle.state.parentSessionId = sourceSessionId;
|
|
95
276
|
// Copy events up to the fork point
|
|
96
277
|
for (const event of events) {
|
|
97
|
-
if (event.type === "turn_boundary" && event.data.turnNumber > forkTurn) {
|
|
278
|
+
if (event.type === "turn_boundary" && (event.data.turnNumber ?? -1) > forkTurn) {
|
|
98
279
|
break;
|
|
99
280
|
}
|
|
100
281
|
newBundle.transcript.append(event.type, event.data);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal, dependency-free unified-diff for human-readable previews (e.g. the
|
|
3
|
+
* /undo confirmation). NOT a patch generator — it's for showing a user "here's
|
|
4
|
+
* what restoring will change" before they confirm. Uses a standard LCS so the
|
|
5
|
+
* output reads like a normal diff (unchanged context, -removed, +added).
|
|
6
|
+
*
|
|
7
|
+
* `from` is the content currently on disk; `to` is the snapshot we'd restore.
|
|
8
|
+
* So the preview answers "if I undo, these lines change."
|
|
9
|
+
*/
|
|
10
|
+
/** A single diff line with its marker. */
|
|
11
|
+
export interface DiffLine {
|
|
12
|
+
marker: " " | "-" | "+";
|
|
13
|
+
text: string;
|
|
14
|
+
}
|
|
15
|
+
/** Longest-common-subsequence table → minimal -/+ line diff (Myers-equivalent
|
|
16
|
+
* result via classic LCS; fine for preview-sized files). */
|
|
17
|
+
export declare function diffLines(from: string, to: string): DiffLine[];
|
|
18
|
+
/**
|
|
19
|
+
* Render a compact unified-diff string for a preview. Collapses long runs of
|
|
20
|
+
* unchanged context to at most `context` lines around each change so a small
|
|
21
|
+
* edit in a big file doesn't print the whole file. Returns "" when identical.
|
|
22
|
+
*/
|
|
23
|
+
export declare function renderDiffPreview(from: string, to: string, context?: number): string;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal, dependency-free unified-diff for human-readable previews (e.g. the
|
|
3
|
+
* /undo confirmation). NOT a patch generator — it's for showing a user "here's
|
|
4
|
+
* what restoring will change" before they confirm. Uses a standard LCS so the
|
|
5
|
+
* output reads like a normal diff (unchanged context, -removed, +added).
|
|
6
|
+
*
|
|
7
|
+
* `from` is the content currently on disk; `to` is the snapshot we'd restore.
|
|
8
|
+
* So the preview answers "if I undo, these lines change."
|
|
9
|
+
*/
|
|
10
|
+
/** Longest-common-subsequence table → minimal -/+ line diff (Myers-equivalent
|
|
11
|
+
* result via classic LCS; fine for preview-sized files). */
|
|
12
|
+
export function diffLines(from, to) {
|
|
13
|
+
// Normalize CRLF → LF before splitting so a Windows (CRLF) snapshot vs. an
|
|
14
|
+
// LF edit doesn't render as every-line-changed in the /undo preview.
|
|
15
|
+
const a = from.replace(/\r\n/g, "\n").split("\n");
|
|
16
|
+
const b = to.replace(/\r\n/g, "\n").split("\n");
|
|
17
|
+
const n = a.length;
|
|
18
|
+
const m = b.length;
|
|
19
|
+
// LCS length table.
|
|
20
|
+
const lcs = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
|
21
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
22
|
+
for (let j = m - 1; j >= 0; j--) {
|
|
23
|
+
lcs[i][j] = a[i] === b[j] ? lcs[i + 1][j + 1] + 1 : Math.max(lcs[i + 1][j], lcs[i][j + 1]);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const out = [];
|
|
27
|
+
let i = 0;
|
|
28
|
+
let j = 0;
|
|
29
|
+
while (i < n && j < m) {
|
|
30
|
+
if (a[i] === b[j]) {
|
|
31
|
+
out.push({ marker: " ", text: a[i] });
|
|
32
|
+
i++;
|
|
33
|
+
j++;
|
|
34
|
+
}
|
|
35
|
+
else if (lcs[i + 1][j] >= lcs[i][j + 1]) {
|
|
36
|
+
out.push({ marker: "-", text: a[i] });
|
|
37
|
+
i++;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
out.push({ marker: "+", text: b[j] });
|
|
41
|
+
j++;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
while (i < n)
|
|
45
|
+
out.push({ marker: "-", text: a[i++] });
|
|
46
|
+
while (j < m)
|
|
47
|
+
out.push({ marker: "+", text: b[j++] });
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Render a compact unified-diff string for a preview. Collapses long runs of
|
|
52
|
+
* unchanged context to at most `context` lines around each change so a small
|
|
53
|
+
* edit in a big file doesn't print the whole file. Returns "" when identical.
|
|
54
|
+
*/
|
|
55
|
+
export function renderDiffPreview(from, to, context = 3) {
|
|
56
|
+
const lines = diffLines(from, to);
|
|
57
|
+
if (lines.every((l) => l.marker === " "))
|
|
58
|
+
return "";
|
|
59
|
+
// Mark which lines are within `context` of a change; drop the rest, inserting
|
|
60
|
+
// a "⋯" gap marker where we elide.
|
|
61
|
+
const keep = new Array(lines.length).fill(false);
|
|
62
|
+
for (let k = 0; k < lines.length; k++) {
|
|
63
|
+
if (lines[k].marker !== " ") {
|
|
64
|
+
for (let d = -context; d <= context; d++) {
|
|
65
|
+
const idx = k + d;
|
|
66
|
+
if (idx >= 0 && idx < lines.length)
|
|
67
|
+
keep[idx] = true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const rows = [];
|
|
72
|
+
let elided = false;
|
|
73
|
+
for (let k = 0; k < lines.length; k++) {
|
|
74
|
+
if (keep[k]) {
|
|
75
|
+
rows.push(`${lines[k].marker}${lines[k].text}`);
|
|
76
|
+
elided = false;
|
|
77
|
+
}
|
|
78
|
+
else if (!elided) {
|
|
79
|
+
rows.push("⋯");
|
|
80
|
+
elided = true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return rows.join("\n");
|
|
84
|
+
}
|
|
@@ -10,10 +10,34 @@ export declare class Transcript {
|
|
|
10
10
|
getFilePath(): string;
|
|
11
11
|
constructor(filePath: string);
|
|
12
12
|
append(type: TranscriptEventType, data: Record<string, unknown>): TranscriptEvent;
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Append a chat message to the transcript.
|
|
15
|
+
*
|
|
16
|
+
* `injected` marks a synthetic system-reminder turn (e.g. a background-job
|
|
17
|
+
* completion notification) that is submitted to the model as `role:"user"`
|
|
18
|
+
* but is NOT the user's own input. The disk reader uses this flag to skip
|
|
19
|
+
* rendering it as a user bubble on replay (matching the live UI, which never
|
|
20
|
+
* shows it as a bubble — only the assistant's reply). Real user input and
|
|
21
|
+
* step-gap steering messages are left unmarked so they render normally.
|
|
22
|
+
*/
|
|
23
|
+
appendMessage(role: string, content: string | ContentBlock[], opts?: {
|
|
24
|
+
injected?: boolean;
|
|
25
|
+
}): TranscriptEvent;
|
|
14
26
|
appendToolUse(toolName: string, toolCallId: string, args: Record<string, unknown>): TranscriptEvent;
|
|
15
27
|
appendToolResult(toolCallId: string, toolName: string, result?: string, error?: string): TranscriptEvent;
|
|
28
|
+
/** Anchor for a spawned sub-agent (see TranscriptEventType "subagent").
|
|
29
|
+
* Written at spawn time so replay can rebuild the sub-agent's card from
|
|
30
|
+
* sessions/<agentId>/ — agentId === the sub-agent's session id. */
|
|
31
|
+
appendSubagent(agentId: string, name: string | undefined, description: string): TranscriptEvent;
|
|
16
32
|
appendTurnBoundary(): TranscriptEvent;
|
|
33
|
+
/**
|
|
34
|
+
* Mark the in-flight turn as user-interrupted (Stop). Persisted so a resume
|
|
35
|
+
* rebuilds the renderer's "stopped" marker (foldTranscript) — otherwise the
|
|
36
|
+
* interrupted turn folds behind the process-card header on reload.
|
|
37
|
+
* Idempotent: a no-op if the last event is already a turn_stopped (the loop
|
|
38
|
+
* can hit more than one abort return for a single Stop).
|
|
39
|
+
*/
|
|
40
|
+
appendTurnStopped(): TranscriptEvent | undefined;
|
|
17
41
|
appendSummary(summary: string, compactedRange: {
|
|
18
42
|
fromTurn: number;
|
|
19
43
|
toTurn: number;
|
|
@@ -29,8 +29,22 @@ export class Transcript {
|
|
|
29
29
|
this.flush(event);
|
|
30
30
|
return event;
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Append a chat message to the transcript.
|
|
34
|
+
*
|
|
35
|
+
* `injected` marks a synthetic system-reminder turn (e.g. a background-job
|
|
36
|
+
* completion notification) that is submitted to the model as `role:"user"`
|
|
37
|
+
* but is NOT the user's own input. The disk reader uses this flag to skip
|
|
38
|
+
* rendering it as a user bubble on replay (matching the live UI, which never
|
|
39
|
+
* shows it as a bubble — only the assistant's reply). Real user input and
|
|
40
|
+
* step-gap steering messages are left unmarked so they render normally.
|
|
41
|
+
*/
|
|
42
|
+
appendMessage(role, content, opts) {
|
|
43
|
+
return this.append("message", {
|
|
44
|
+
role,
|
|
45
|
+
content,
|
|
46
|
+
...(opts?.injected ? { injected: true } : {}),
|
|
47
|
+
});
|
|
34
48
|
}
|
|
35
49
|
appendToolUse(toolName, toolCallId, args) {
|
|
36
50
|
return this.append("tool_use", { toolName, toolCallId, args });
|
|
@@ -38,10 +52,29 @@ export class Transcript {
|
|
|
38
52
|
appendToolResult(toolCallId, toolName, result, error) {
|
|
39
53
|
return this.append("tool_result", { toolCallId, toolName, result, error });
|
|
40
54
|
}
|
|
55
|
+
/** Anchor for a spawned sub-agent (see TranscriptEventType "subagent").
|
|
56
|
+
* Written at spawn time so replay can rebuild the sub-agent's card from
|
|
57
|
+
* sessions/<agentId>/ — agentId === the sub-agent's session id. */
|
|
58
|
+
appendSubagent(agentId, name, description) {
|
|
59
|
+
return this.append("subagent", { agentId, name, description });
|
|
60
|
+
}
|
|
41
61
|
appendTurnBoundary() {
|
|
42
62
|
this.currentTurn++;
|
|
43
63
|
return this.append("turn_boundary", { turnNumber: this.currentTurn });
|
|
44
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Mark the in-flight turn as user-interrupted (Stop). Persisted so a resume
|
|
67
|
+
* rebuilds the renderer's "stopped" marker (foldTranscript) — otherwise the
|
|
68
|
+
* interrupted turn folds behind the process-card header on reload.
|
|
69
|
+
* Idempotent: a no-op if the last event is already a turn_stopped (the loop
|
|
70
|
+
* can hit more than one abort return for a single Stop).
|
|
71
|
+
*/
|
|
72
|
+
appendTurnStopped() {
|
|
73
|
+
const last = this.events[this.events.length - 1];
|
|
74
|
+
if (last && last.type === "turn_stopped")
|
|
75
|
+
return undefined;
|
|
76
|
+
return this.append("turn_stopped", {});
|
|
77
|
+
}
|
|
45
78
|
appendSummary(summary, compactedRange) {
|
|
46
79
|
return this.append("summary", {
|
|
47
80
|
summary,
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure selection of the "most recent file modification" to undo, given a
|
|
3
|
+
* FileHistory snapshot list. Kept fs-free so the "what does /undo target"
|
|
4
|
+
* decision is unit-testable and shared between the TUI command and any future
|
|
5
|
+
* desktop entry point.
|
|
6
|
+
*
|
|
7
|
+
* A snapshot is the *pre-modification* backup of a file (see file-history.ts:
|
|
8
|
+
* saveSnapshot runs before Write/Edit/ApplyPatch). So the newest snapshot's
|
|
9
|
+
* backup is the content to restore for a single-step undo: it is the state the
|
|
10
|
+
* file had immediately before the last edit.
|
|
11
|
+
*/
|
|
12
|
+
import type { FileSnapshot, RedoRecord } from "./file-history.js";
|
|
13
|
+
/**
|
|
14
|
+
* The snapshot for the most recent modification. Returns the entry with the
|
|
15
|
+
* greatest `timestamp`; on a tie (same-millisecond snapshots of different
|
|
16
|
+
* files) the LAST such entry in array order wins — saveSnapshot pushes in
|
|
17
|
+
* chronological order, so the last pushed is the most recently recorded, which
|
|
18
|
+
* is the stable, predictable choice. Returns null for an empty history.
|
|
19
|
+
*/
|
|
20
|
+
export declare function latestUndoTarget(snapshots: FileSnapshot[]): FileSnapshot | null;
|
|
21
|
+
/**
|
|
22
|
+
* For "/undo all": each tracked file's EARLIEST snapshot — its content before
|
|
23
|
+
* the *first* AI edit this session. Restoring all of these reverts the whole
|
|
24
|
+
* session's file changes (vs. latestUndoTarget, which is the single last step).
|
|
25
|
+
*
|
|
26
|
+
* Returns one snapshot per file path, ordered by that file's first-edit time
|
|
27
|
+
* (oldest first) so a preview reads chronologically. On a same-millisecond tie
|
|
28
|
+
* for a given file, the FIRST array entry wins (earliest recorded).
|
|
29
|
+
*/
|
|
30
|
+
export declare function earliestSnapshotsPerFile(snapshots: FileSnapshot[]): FileSnapshot[];
|
|
31
|
+
/**
|
|
32
|
+
* For turn-level "/undo": the snapshots needed to revert just the MOST RECENT
|
|
33
|
+
* conversation turn (one user message = one turn, see SessionState.turnSeq).
|
|
34
|
+
*
|
|
35
|
+
* The latest turn = the greatest `turnSeq` present. A turn boundary is defined
|
|
36
|
+
* by turnSeq, NOT wall-clock — a clock skew must not move the boundary. Within
|
|
37
|
+
* that turn, each touched file is restored to its EARLIEST snapshot of the turn
|
|
38
|
+
* (its content before the *first* edit that turn), so re-editing a file twice
|
|
39
|
+
* in one turn still reverts to the pre-turn baseline — and edits from *earlier*
|
|
40
|
+
* turns are left intact (that's the whole point vs. earliestSnapshotsPerFile,
|
|
41
|
+
* which spans the whole session).
|
|
42
|
+
*
|
|
43
|
+
* Backward compatibility: snapshots written before this feature have no
|
|
44
|
+
* `turnSeq`. They share the `undefined` bucket. `undefined` sorts as the
|
|
45
|
+
* smallest "turn", so any real (tagged) turn supersedes the legacy bucket; a
|
|
46
|
+
* history that is ALL-legacy degrades to whole-session behaviour (each file's
|
|
47
|
+
* earliest) rather than throwing.
|
|
48
|
+
*
|
|
49
|
+
* Returns one snapshot per file, ordered by that file's first-edit time within
|
|
50
|
+
* the turn (oldest first) so a preview reads chronologically. Empty → [].
|
|
51
|
+
*/
|
|
52
|
+
export declare function latestTurnUndoTargets(snapshots: FileSnapshot[]): FileSnapshot[];
|
|
53
|
+
/**
|
|
54
|
+
* For "/redo": the RedoRecord[] of the MOST RECENT undone turn — the turn that
|
|
55
|
+
* `redoLatestTurn` should re-apply — but ONLY when that turn is still the latest
|
|
56
|
+
* undone state (i.e. nothing newer happened since the undo). Otherwise [].
|
|
57
|
+
*
|
|
58
|
+
* Rule: let R = the greatest turnSeq present in `redoRecords` (a RedoRecord
|
|
59
|
+
* exists only for a turn that was undone and not yet redone). R is redoable iff
|
|
60
|
+
* NO snapshot belongs to a strictly newer LIVE (non-undone) turn — a fresh edit
|
|
61
|
+
* after the undo supersedes the redo and invalidates it (spec: "新轮使 redo
|
|
62
|
+
* 失效"). Created-only turns have no pre-turn snapshot, so the RedoRecord itself
|
|
63
|
+
* is the sole evidence and is honoured as long as nothing newer superseded it.
|
|
64
|
+
*
|
|
65
|
+
* Empty `redoRecords` → []. fs-free so /redo's target decision is unit-testable.
|
|
66
|
+
*/
|
|
67
|
+
export declare function latestRedoTargets(redoRecords: RedoRecord[], snapshots: FileSnapshot[]): RedoRecord[];
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure selection of the "most recent file modification" to undo, given a
|
|
3
|
+
* FileHistory snapshot list. Kept fs-free so the "what does /undo target"
|
|
4
|
+
* decision is unit-testable and shared between the TUI command and any future
|
|
5
|
+
* desktop entry point.
|
|
6
|
+
*
|
|
7
|
+
* A snapshot is the *pre-modification* backup of a file (see file-history.ts:
|
|
8
|
+
* saveSnapshot runs before Write/Edit/ApplyPatch). So the newest snapshot's
|
|
9
|
+
* backup is the content to restore for a single-step undo: it is the state the
|
|
10
|
+
* file had immediately before the last edit.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The snapshot for the most recent modification. Returns the entry with the
|
|
14
|
+
* greatest `timestamp`; on a tie (same-millisecond snapshots of different
|
|
15
|
+
* files) the LAST such entry in array order wins — saveSnapshot pushes in
|
|
16
|
+
* chronological order, so the last pushed is the most recently recorded, which
|
|
17
|
+
* is the stable, predictable choice. Returns null for an empty history.
|
|
18
|
+
*/
|
|
19
|
+
export function latestUndoTarget(snapshots) {
|
|
20
|
+
let best = null;
|
|
21
|
+
for (const s of snapshots) {
|
|
22
|
+
// `>=` so a later array entry with an equal timestamp supersedes an
|
|
23
|
+
// earlier one (last-pushed wins on a tie).
|
|
24
|
+
if (best === null || s.timestamp >= best.timestamp)
|
|
25
|
+
best = s;
|
|
26
|
+
}
|
|
27
|
+
return best;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* For "/undo all": each tracked file's EARLIEST snapshot — its content before
|
|
31
|
+
* the *first* AI edit this session. Restoring all of these reverts the whole
|
|
32
|
+
* session's file changes (vs. latestUndoTarget, which is the single last step).
|
|
33
|
+
*
|
|
34
|
+
* Returns one snapshot per file path, ordered by that file's first-edit time
|
|
35
|
+
* (oldest first) so a preview reads chronologically. On a same-millisecond tie
|
|
36
|
+
* for a given file, the FIRST array entry wins (earliest recorded).
|
|
37
|
+
*/
|
|
38
|
+
export function earliestSnapshotsPerFile(snapshots) {
|
|
39
|
+
const earliest = new Map();
|
|
40
|
+
for (const s of snapshots) {
|
|
41
|
+
// Undone snapshots belong to a reverted turn: their content is no longer on
|
|
42
|
+
// disk and they are not the session baseline, so skip them — otherwise an
|
|
43
|
+
// undone (earlier) snapshot could win the baseline and `/undo all` would
|
|
44
|
+
// restore stale content.
|
|
45
|
+
if (s.undone)
|
|
46
|
+
continue;
|
|
47
|
+
const cur = earliest.get(s.filePath);
|
|
48
|
+
// strict `<` so the first-seen entry wins on a tie (earliest recorded).
|
|
49
|
+
if (!cur || s.timestamp < cur.timestamp)
|
|
50
|
+
earliest.set(s.filePath, s);
|
|
51
|
+
}
|
|
52
|
+
return [...earliest.values()].sort((a, b) => a.timestamp - b.timestamp);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* For turn-level "/undo": the snapshots needed to revert just the MOST RECENT
|
|
56
|
+
* conversation turn (one user message = one turn, see SessionState.turnSeq).
|
|
57
|
+
*
|
|
58
|
+
* The latest turn = the greatest `turnSeq` present. A turn boundary is defined
|
|
59
|
+
* by turnSeq, NOT wall-clock — a clock skew must not move the boundary. Within
|
|
60
|
+
* that turn, each touched file is restored to its EARLIEST snapshot of the turn
|
|
61
|
+
* (its content before the *first* edit that turn), so re-editing a file twice
|
|
62
|
+
* in one turn still reverts to the pre-turn baseline — and edits from *earlier*
|
|
63
|
+
* turns are left intact (that's the whole point vs. earliestSnapshotsPerFile,
|
|
64
|
+
* which spans the whole session).
|
|
65
|
+
*
|
|
66
|
+
* Backward compatibility: snapshots written before this feature have no
|
|
67
|
+
* `turnSeq`. They share the `undefined` bucket. `undefined` sorts as the
|
|
68
|
+
* smallest "turn", so any real (tagged) turn supersedes the legacy bucket; a
|
|
69
|
+
* history that is ALL-legacy degrades to whole-session behaviour (each file's
|
|
70
|
+
* earliest) rather than throwing.
|
|
71
|
+
*
|
|
72
|
+
* Returns one snapshot per file, ordered by that file's first-edit time within
|
|
73
|
+
* the turn (oldest first) so a preview reads chronologically. Empty → [].
|
|
74
|
+
*/
|
|
75
|
+
export function latestTurnUndoTargets(snapshots) {
|
|
76
|
+
if (snapshots.length === 0)
|
|
77
|
+
return [];
|
|
78
|
+
// Find the latest turn, SKIPPING undone turns: once a turn is undone (marked,
|
|
79
|
+
// not deleted) its snapshots stay on disk for redo but must not be re-selected
|
|
80
|
+
// by undo — so the next /undo peels the prior live turn ("onion" behaviour).
|
|
81
|
+
// undefined (legacy) is treated as the smallest turn so it loses to any tagged
|
|
82
|
+
// turn; a key of `-Infinity` models that ordering.
|
|
83
|
+
const turnKey = (s) => s.turnSeq ?? -Infinity;
|
|
84
|
+
let maxTurn = -Infinity;
|
|
85
|
+
let found = false;
|
|
86
|
+
for (const s of snapshots) {
|
|
87
|
+
if (s.undone)
|
|
88
|
+
continue;
|
|
89
|
+
const k = turnKey(s);
|
|
90
|
+
if (k > maxTurn)
|
|
91
|
+
maxTurn = k;
|
|
92
|
+
found = true;
|
|
93
|
+
}
|
|
94
|
+
if (!found)
|
|
95
|
+
return [];
|
|
96
|
+
// Within the latest live turn, each file's earliest snapshot = its pre-turn
|
|
97
|
+
// state. Undone snapshots are skipped here too (defensive: they share neither
|
|
98
|
+
// the selected turn nor the baseline).
|
|
99
|
+
const earliest = new Map();
|
|
100
|
+
for (const s of snapshots) {
|
|
101
|
+
if (s.undone)
|
|
102
|
+
continue;
|
|
103
|
+
if (turnKey(s) !== maxTurn)
|
|
104
|
+
continue;
|
|
105
|
+
const cur = earliest.get(s.filePath);
|
|
106
|
+
// strict `<` so the first-seen entry wins on a same-ms tie (earliest).
|
|
107
|
+
if (!cur || s.timestamp < cur.timestamp)
|
|
108
|
+
earliest.set(s.filePath, s);
|
|
109
|
+
}
|
|
110
|
+
return [...earliest.values()].sort((a, b) => a.timestamp - b.timestamp);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* For "/redo": the RedoRecord[] of the MOST RECENT undone turn — the turn that
|
|
114
|
+
* `redoLatestTurn` should re-apply — but ONLY when that turn is still the latest
|
|
115
|
+
* undone state (i.e. nothing newer happened since the undo). Otherwise [].
|
|
116
|
+
*
|
|
117
|
+
* Rule: let R = the greatest turnSeq present in `redoRecords` (a RedoRecord
|
|
118
|
+
* exists only for a turn that was undone and not yet redone). R is redoable iff
|
|
119
|
+
* NO snapshot belongs to a strictly newer LIVE (non-undone) turn — a fresh edit
|
|
120
|
+
* after the undo supersedes the redo and invalidates it (spec: "新轮使 redo
|
|
121
|
+
* 失效"). Created-only turns have no pre-turn snapshot, so the RedoRecord itself
|
|
122
|
+
* is the sole evidence and is honoured as long as nothing newer superseded it.
|
|
123
|
+
*
|
|
124
|
+
* Empty `redoRecords` → []. fs-free so /redo's target decision is unit-testable.
|
|
125
|
+
*/
|
|
126
|
+
export function latestRedoTargets(redoRecords, snapshots) {
|
|
127
|
+
if (redoRecords.length === 0)
|
|
128
|
+
return [];
|
|
129
|
+
let maxRedoTurn = -Infinity;
|
|
130
|
+
for (const r of redoRecords) {
|
|
131
|
+
if (r.turnSeq > maxRedoTurn)
|
|
132
|
+
maxRedoTurn = r.turnSeq;
|
|
133
|
+
}
|
|
134
|
+
// If any LIVE snapshot belongs to a strictly newer turn, the redo turn is no
|
|
135
|
+
// longer the latest undone state → not redoable.
|
|
136
|
+
for (const s of snapshots) {
|
|
137
|
+
if (s.undone)
|
|
138
|
+
continue;
|
|
139
|
+
const k = s.turnSeq ?? -Infinity;
|
|
140
|
+
if (k > maxRedoTurn)
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
return redoRecords.filter((r) => r.turnSeq === maxRedoTurn);
|
|
144
|
+
}
|