@prestyj/cli 5.9.0 → 5.11.0
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 +4 -5
- package/assets/skills/evidence-led-ui/SKILL.md +1 -1
- package/dist/app-sidecar-auth-broadcast.test.d.ts +2 -0
- package/dist/app-sidecar-auth-broadcast.test.d.ts.map +1 -0
- package/dist/app-sidecar-auth-broadcast.test.js +282 -0
- package/dist/app-sidecar-auth-broadcast.test.js.map +1 -0
- package/dist/app-sidecar-sessions.d.ts +5 -2
- package/dist/app-sidecar-sessions.d.ts.map +1 -1
- package/dist/app-sidecar-sessions.js +34 -8
- package/dist/app-sidecar-sessions.js.map +1 -1
- package/dist/app-sidecar-sessions.test.js +95 -0
- package/dist/app-sidecar-sessions.test.js.map +1 -1
- package/dist/app-sidecar.js +871 -142
- package/dist/app-sidecar.js.map +1 -1
- package/dist/chat-agents/general.test.js +5 -3
- package/dist/chat-agents/general.test.js.map +1 -1
- package/dist/chat-agents/memory.d.ts.map +1 -1
- package/dist/chat-agents/memory.js +14 -5
- package/dist/chat-agents/memory.js.map +1 -1
- package/dist/chat-agents/memory.test.js +11 -1
- package/dist/chat-agents/memory.test.js.map +1 -1
- package/dist/chat-agents/shared.js +1 -1
- package/dist/chat-agents/shared.js.map +1 -1
- package/dist/chat-agents/specialists.test.js +3 -2
- package/dist/chat-agents/specialists.test.js.map +1 -1
- package/dist/cli/command-routing.d.ts +1 -1
- package/dist/cli/command-routing.d.ts.map +1 -1
- package/dist/cli/command-routing.js +1 -0
- package/dist/cli/command-routing.js.map +1 -1
- package/dist/cli/command-routing.test.js +1 -0
- package/dist/cli/command-routing.test.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +183 -51
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +51 -2
- package/dist/config.js.map +1 -1
- package/dist/config.test.js +71 -2
- package/dist/config.test.js.map +1 -1
- package/dist/core/agent-mcp-passthrough.test.d.ts +2 -0
- package/dist/core/agent-mcp-passthrough.test.d.ts.map +1 -0
- package/dist/core/agent-mcp-passthrough.test.js +61 -0
- package/dist/core/agent-mcp-passthrough.test.js.map +1 -0
- package/dist/core/agent-notifications.d.ts +62 -0
- package/dist/core/agent-notifications.d.ts.map +1 -0
- package/dist/core/agent-notifications.js +92 -0
- package/dist/core/agent-notifications.js.map +1 -0
- package/dist/core/agent-notifications.test.d.ts +2 -0
- package/dist/core/agent-notifications.test.d.ts.map +1 -0
- package/dist/core/agent-notifications.test.js +97 -0
- package/dist/core/agent-notifications.test.js.map +1 -0
- package/dist/core/agent-session-add-dir.test.d.ts +2 -0
- package/dist/core/agent-session-add-dir.test.d.ts.map +1 -0
- package/dist/core/agent-session-add-dir.test.js +110 -0
- package/dist/core/agent-session-add-dir.test.js.map +1 -0
- package/dist/core/agent-session-checkpoint-flush.test.d.ts +2 -0
- package/dist/core/agent-session-checkpoint-flush.test.d.ts.map +1 -0
- package/dist/core/agent-session-checkpoint-flush.test.js +201 -0
- package/dist/core/agent-session-checkpoint-flush.test.js.map +1 -0
- package/dist/core/agent-session-compaction.test.js +327 -13
- package/dist/core/agent-session-compaction.test.js.map +1 -1
- package/dist/core/agent-session-crash-durability.test.d.ts +2 -0
- package/dist/core/agent-session-crash-durability.test.d.ts.map +1 -0
- package/dist/core/agent-session-crash-durability.test.js +102 -0
- package/dist/core/agent-session-crash-durability.test.js.map +1 -0
- package/dist/core/agent-session-marker-anchors.test.d.ts +2 -0
- package/dist/core/agent-session-marker-anchors.test.d.ts.map +1 -0
- package/dist/core/agent-session-marker-anchors.test.js +326 -0
- package/dist/core/agent-session-marker-anchors.test.js.map +1 -0
- package/dist/core/agent-session-memory-tail.test.js +4 -7
- package/dist/core/agent-session-memory-tail.test.js.map +1 -1
- package/dist/core/agent-session-model-switch.test.d.ts +2 -0
- package/dist/core/agent-session-model-switch.test.d.ts.map +1 -0
- package/dist/core/agent-session-model-switch.test.js +161 -0
- package/dist/core/agent-session-model-switch.test.js.map +1 -0
- package/dist/core/agent-session-notifications.test.d.ts +2 -0
- package/dist/core/agent-session-notifications.test.d.ts.map +1 -0
- package/dist/core/agent-session-notifications.test.js +114 -0
- package/dist/core/agent-session-notifications.test.js.map +1 -0
- package/dist/core/agent-session-process-gate.test.d.ts +2 -0
- package/dist/core/agent-session-process-gate.test.d.ts.map +1 -0
- package/dist/core/agent-session-process-gate.test.js +92 -0
- package/dist/core/agent-session-process-gate.test.js.map +1 -0
- package/dist/core/agent-session-queue.test.js +69 -7
- package/dist/core/agent-session-queue.test.js.map +1 -1
- package/dist/core/agent-session-review-coverage.test.js +80 -23
- package/dist/core/agent-session-review-coverage.test.js.map +1 -1
- package/dist/core/agent-session-slash-restore.test.d.ts +2 -0
- package/dist/core/agent-session-slash-restore.test.d.ts.map +1 -0
- package/dist/core/agent-session-slash-restore.test.js +121 -0
- package/dist/core/agent-session-slash-restore.test.js.map +1 -0
- package/dist/core/agent-session-subagent-policy.test.js +7 -3
- package/dist/core/agent-session-subagent-policy.test.js.map +1 -1
- package/dist/core/agent-session.d.ts +256 -10
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1071 -219
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/agent-tools-allowlist.test.js +5 -3
- package/dist/core/agent-tools-allowlist.test.js.map +1 -1
- package/dist/core/agents.d.ts +11 -0
- package/dist/core/agents.d.ts.map +1 -1
- package/dist/core/agents.js +21 -0
- package/dist/core/agents.js.map +1 -1
- package/dist/core/agents.test.d.ts +2 -0
- package/dist/core/agents.test.d.ts.map +1 -0
- package/dist/core/agents.test.js +67 -0
- package/dist/core/agents.test.js.map +1 -0
- package/dist/core/auth-providers.js +1 -1
- package/dist/core/auth-providers.js.map +1 -1
- package/dist/core/compaction/active-context.test.js +21 -0
- package/dist/core/compaction/active-context.test.js.map +1 -1
- package/dist/core/compaction/compactor.d.ts +108 -8
- package/dist/core/compaction/compactor.d.ts.map +1 -1
- package/dist/core/compaction/compactor.js +423 -119
- package/dist/core/compaction/compactor.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +361 -9
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/compaction/policy.d.ts +17 -0
- package/dist/core/compaction/policy.d.ts.map +1 -0
- package/dist/core/compaction/policy.js +22 -0
- package/dist/core/compaction/policy.js.map +1 -0
- package/dist/core/compaction/policy.test.d.ts +2 -0
- package/dist/core/compaction/policy.test.d.ts.map +1 -0
- package/dist/core/compaction/policy.test.js +35 -0
- package/dist/core/compaction/policy.test.js.map +1 -0
- package/dist/core/compaction/query-aware-selector.d.ts +33 -0
- package/dist/core/compaction/query-aware-selector.d.ts.map +1 -0
- package/dist/core/compaction/query-aware-selector.js +202 -0
- package/dist/core/compaction/query-aware-selector.js.map +1 -0
- package/dist/core/compaction/query-aware-selector.test.d.ts +2 -0
- package/dist/core/compaction/query-aware-selector.test.d.ts.map +1 -0
- package/dist/core/compaction/query-aware-selector.test.js +96 -0
- package/dist/core/compaction/query-aware-selector.test.js.map +1 -0
- package/dist/core/compaction/token-estimator.test.js +1 -1
- package/dist/core/compaction/token-estimator.test.js.map +1 -1
- package/dist/core/compaction/tool-result-pruner.d.ts +24 -11
- package/dist/core/compaction/tool-result-pruner.d.ts.map +1 -1
- package/dist/core/compaction/tool-result-pruner.js +98 -26
- package/dist/core/compaction/tool-result-pruner.js.map +1 -1
- package/dist/core/compaction/tool-result-pruner.test.js +178 -6
- package/dist/core/compaction/tool-result-pruner.test.js.map +1 -1
- package/dist/core/encode-cwd.d.ts +17 -0
- package/dist/core/encode-cwd.d.ts.map +1 -1
- package/dist/core/encode-cwd.js +20 -7
- package/dist/core/encode-cwd.js.map +1 -1
- package/dist/core/encode-cwd.test.js +20 -1
- package/dist/core/encode-cwd.test.js.map +1 -1
- package/dist/core/event-bus.d.ts +23 -0
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +10 -0
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +28 -8
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/plugin-bundles.d.ts +23 -0
- package/dist/core/extensions/plugin-bundles.d.ts.map +1 -0
- package/dist/core/extensions/plugin-bundles.js +228 -0
- package/dist/core/extensions/plugin-bundles.js.map +1 -0
- package/dist/core/extensions/plugin-bundles.test.d.ts +2 -0
- package/dist/core/extensions/plugin-bundles.test.d.ts.map +1 -0
- package/dist/core/extensions/plugin-bundles.test.js +110 -0
- package/dist/core/extensions/plugin-bundles.test.js.map +1 -0
- package/dist/core/foreign-session-import.d.ts +120 -0
- package/dist/core/foreign-session-import.d.ts.map +1 -0
- package/dist/core/foreign-session-import.js +598 -0
- package/dist/core/foreign-session-import.js.map +1 -0
- package/dist/core/foreign-session-import.test.d.ts +2 -0
- package/dist/core/foreign-session-import.test.d.ts.map +1 -0
- package/dist/core/foreign-session-import.test.js +220 -0
- package/dist/core/foreign-session-import.test.js.map +1 -0
- package/dist/core/foreign-transcript-blocks.d.ts +51 -0
- package/dist/core/foreign-transcript-blocks.d.ts.map +1 -0
- package/dist/core/foreign-transcript-blocks.js +151 -0
- package/dist/core/foreign-transcript-blocks.js.map +1 -0
- package/dist/core/ideal-review.d.ts +20 -1
- package/dist/core/ideal-review.d.ts.map +1 -1
- package/dist/core/ideal-review.js +43 -3
- package/dist/core/ideal-review.js.map +1 -1
- package/dist/core/ideal-review.test.js +106 -5
- package/dist/core/ideal-review.test.js.map +1 -1
- package/dist/core/local-endpoint-store.d.ts +50 -0
- package/dist/core/local-endpoint-store.d.ts.map +1 -0
- package/dist/core/local-endpoint-store.js +201 -0
- package/dist/core/local-endpoint-store.js.map +1 -0
- package/dist/core/local-endpoint-store.test.d.ts +2 -0
- package/dist/core/local-endpoint-store.test.d.ts.map +1 -0
- package/dist/core/local-endpoint-store.test.js +131 -0
- package/dist/core/local-endpoint-store.test.js.map +1 -0
- package/dist/core/loop-breaker.d.ts.map +1 -1
- package/dist/core/loop-breaker.js +1 -0
- package/dist/core/loop-breaker.js.map +1 -1
- package/dist/core/lsp/client.d.ts +65 -0
- package/dist/core/lsp/client.d.ts.map +1 -1
- package/dist/core/lsp/client.js +197 -14
- package/dist/core/lsp/client.js.map +1 -1
- package/dist/core/lsp/jsonrpc.d.ts +38 -1
- package/dist/core/lsp/jsonrpc.d.ts.map +1 -1
- package/dist/core/lsp/jsonrpc.js +13 -1
- package/dist/core/lsp/jsonrpc.js.map +1 -1
- package/dist/core/lsp/manager.d.ts +47 -9
- package/dist/core/lsp/manager.d.ts.map +1 -1
- package/dist/core/lsp/manager.js +107 -59
- package/dist/core/lsp/manager.js.map +1 -1
- package/dist/core/lsp/manager.test.js +50 -1
- package/dist/core/lsp/manager.test.js.map +1 -1
- package/dist/core/lsp/pool.d.ts +113 -0
- package/dist/core/lsp/pool.d.ts.map +1 -0
- package/dist/core/lsp/pool.js +255 -0
- package/dist/core/lsp/pool.js.map +1 -0
- package/dist/core/lsp/pool.test.d.ts +2 -0
- package/dist/core/lsp/pool.test.d.ts.map +1 -0
- package/dist/core/lsp/pool.test.js +286 -0
- package/dist/core/lsp/pool.test.js.map +1 -0
- package/dist/core/lsp/servers.d.ts.map +1 -1
- package/dist/core/lsp/servers.js +43 -3
- package/dist/core/lsp/servers.js.map +1 -1
- package/dist/core/lsp/servers.test.d.ts +2 -0
- package/dist/core/lsp/servers.test.d.ts.map +1 -0
- package/dist/core/lsp/servers.test.js +67 -0
- package/dist/core/lsp/servers.test.js.map +1 -0
- package/dist/core/lsp/test-support.d.ts +18 -0
- package/dist/core/lsp/test-support.d.ts.map +1 -0
- package/dist/core/lsp/test-support.js +27 -0
- package/dist/core/lsp/test-support.js.map +1 -0
- package/dist/core/lsp/uri.test.d.ts +2 -0
- package/dist/core/lsp/uri.test.d.ts.map +1 -0
- package/dist/core/lsp/uri.test.js +56 -0
- package/dist/core/lsp/uri.test.js.map +1 -0
- package/dist/core/lsp/windows.test.d.ts +2 -0
- package/dist/core/lsp/windows.test.d.ts.map +1 -0
- package/dist/core/lsp/windows.test.js +239 -0
- package/dist/core/lsp/windows.test.js.map +1 -0
- package/dist/core/mcp/catalog-cache.d.ts +63 -0
- package/dist/core/mcp/catalog-cache.d.ts.map +1 -0
- package/dist/core/mcp/catalog-cache.js +163 -0
- package/dist/core/mcp/catalog-cache.js.map +1 -0
- package/dist/core/mcp/catalog-cache.test.d.ts +2 -0
- package/dist/core/mcp/catalog-cache.test.d.ts.map +1 -0
- package/dist/core/mcp/catalog-cache.test.js +99 -0
- package/dist/core/mcp/catalog-cache.test.js.map +1 -0
- package/dist/core/mcp/client-session-recovery.test.d.ts +2 -0
- package/dist/core/mcp/client-session-recovery.test.d.ts.map +1 -0
- package/dist/core/mcp/client-session-recovery.test.js +231 -0
- package/dist/core/mcp/client-session-recovery.test.js.map +1 -0
- package/dist/core/mcp/client.d.ts +158 -0
- package/dist/core/mcp/client.d.ts.map +1 -1
- package/dist/core/mcp/client.js +476 -35
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/mcp/elicitation-bridge.d.ts +41 -0
- package/dist/core/mcp/elicitation-bridge.d.ts.map +1 -0
- package/dist/core/mcp/elicitation-bridge.js +52 -0
- package/dist/core/mcp/elicitation-bridge.js.map +1 -0
- package/dist/core/mcp/elicitation.test.d.ts +2 -0
- package/dist/core/mcp/elicitation.test.d.ts.map +1 -0
- package/dist/core/mcp/elicitation.test.js +158 -0
- package/dist/core/mcp/elicitation.test.js.map +1 -0
- package/dist/core/mcp/index.d.ts +5 -1
- package/dist/core/mcp/index.d.ts.map +1 -1
- package/dist/core/mcp/index.js +2 -0
- package/dist/core/mcp/index.js.map +1 -1
- package/dist/core/mcp/legacy-negotiation.test.d.ts +2 -0
- package/dist/core/mcp/legacy-negotiation.test.d.ts.map +1 -0
- package/dist/core/mcp/legacy-negotiation.test.js +80 -0
- package/dist/core/mcp/legacy-negotiation.test.js.map +1 -0
- package/dist/core/mcp/oauth-provider.d.ts +1 -2
- package/dist/core/mcp/oauth-provider.d.ts.map +1 -1
- package/dist/core/mcp/oauth-provider.js +6 -2
- package/dist/core/mcp/oauth-provider.js.map +1 -1
- package/dist/core/mcp/oauth-store.d.ts +1 -1
- package/dist/core/mcp/oauth-store.d.ts.map +1 -1
- package/dist/core/mcp/oauth-store.js.map +1 -1
- package/dist/core/mcp/resolve-stdio.d.ts +35 -0
- package/dist/core/mcp/resolve-stdio.d.ts.map +1 -1
- package/dist/core/mcp/resolve-stdio.js +80 -1
- package/dist/core/mcp/resolve-stdio.js.map +1 -1
- package/dist/core/mcp/resolve-stdio.test.js +99 -4
- package/dist/core/mcp/resolve-stdio.test.js.map +1 -1
- package/dist/core/mcp/session-elicitation-wiring.test.d.ts +2 -0
- package/dist/core/mcp/session-elicitation-wiring.test.d.ts.map +1 -0
- package/dist/core/mcp/session-elicitation-wiring.test.js +138 -0
- package/dist/core/mcp/session-elicitation-wiring.test.js.map +1 -0
- package/dist/core/mcp/shared-pool.d.ts +158 -0
- package/dist/core/mcp/shared-pool.d.ts.map +1 -0
- package/dist/core/mcp/shared-pool.js +235 -0
- package/dist/core/mcp/shared-pool.js.map +1 -0
- package/dist/core/mcp/shared-pool.test.d.ts +2 -0
- package/dist/core/mcp/shared-pool.test.d.ts.map +1 -0
- package/dist/core/mcp/shared-pool.test.js +425 -0
- package/dist/core/mcp/shared-pool.test.js.map +1 -0
- package/dist/core/mcp/types.d.ts +12 -0
- package/dist/core/mcp/types.d.ts.map +1 -1
- package/dist/core/mcp/when-connected.test.d.ts +2 -0
- package/dist/core/mcp/when-connected.test.d.ts.map +1 -0
- package/dist/core/mcp/when-connected.test.js +46 -0
- package/dist/core/mcp/when-connected.test.js.map +1 -0
- package/dist/core/network-guard.d.ts +47 -0
- package/dist/core/network-guard.d.ts.map +1 -0
- package/dist/core/network-guard.js +208 -0
- package/dist/core/network-guard.js.map +1 -0
- package/dist/core/network-guard.test.d.ts +2 -0
- package/dist/core/network-guard.test.d.ts.map +1 -0
- package/dist/core/network-guard.test.js +72 -0
- package/dist/core/network-guard.test.js.map +1 -0
- package/dist/core/nolan-context.d.ts.map +1 -1
- package/dist/core/nolan-context.js +8 -0
- package/dist/core/nolan-context.js.map +1 -1
- package/dist/core/nolan-context.test.js +40 -0
- package/dist/core/nolan-context.test.js.map +1 -1
- package/dist/core/nolan-prompt.js +3 -0
- package/dist/core/nolan-prompt.js.map +1 -1
- package/dist/core/nolan-prompt.test.js +5 -0
- package/dist/core/nolan-prompt.test.js.map +1 -1
- package/dist/core/persistent-shell.d.ts +4 -1
- package/dist/core/persistent-shell.d.ts.map +1 -1
- package/dist/core/persistent-shell.js +21 -4
- package/dist/core/persistent-shell.js.map +1 -1
- package/dist/core/process-gate.d.ts +45 -0
- package/dist/core/process-gate.d.ts.map +1 -0
- package/dist/core/process-gate.js +79 -0
- package/dist/core/process-gate.js.map +1 -0
- package/dist/core/process-gate.test.d.ts +2 -0
- package/dist/core/process-gate.test.d.ts.map +1 -0
- package/dist/core/process-gate.test.js +87 -0
- package/dist/core/process-gate.test.js.map +1 -0
- package/dist/core/process-manager-dev-server-repro.test.js +34 -12
- package/dist/core/process-manager-dev-server-repro.test.js.map +1 -1
- package/dist/core/process-manager-notifications.test.d.ts +2 -0
- package/dist/core/process-manager-notifications.test.d.ts.map +1 -0
- package/dist/core/process-manager-notifications.test.js +217 -0
- package/dist/core/process-manager-notifications.test.js.map +1 -0
- package/dist/core/process-manager.d.ts +51 -2
- package/dist/core/process-manager.d.ts.map +1 -1
- package/dist/core/process-manager.js +212 -15
- package/dist/core/process-manager.js.map +1 -1
- package/dist/core/project-discovery.d.ts +38 -6
- package/dist/core/project-discovery.d.ts.map +1 -1
- package/dist/core/project-discovery.js +327 -40
- package/dist/core/project-discovery.js.map +1 -1
- package/dist/core/project-discovery.test.js +226 -1
- package/dist/core/project-discovery.test.js.map +1 -1
- package/dist/core/prompt-commands.d.ts.map +1 -1
- package/dist/core/prompt-commands.js +89 -156
- package/dist/core/prompt-commands.js.map +1 -1
- package/dist/core/prompt-commands.test.js +4 -10
- package/dist/core/prompt-commands.test.js.map +1 -1
- package/dist/core/radio.test.js +4 -1
- package/dist/core/radio.test.js.map +1 -1
- package/dist/core/regrounding.d.ts.map +1 -1
- package/dist/core/regrounding.js +1 -0
- package/dist/core/regrounding.js.map +1 -1
- package/dist/core/run-claim.d.ts +37 -0
- package/dist/core/run-claim.d.ts.map +1 -0
- package/dist/core/run-claim.js +46 -0
- package/dist/core/run-claim.js.map +1 -0
- package/dist/core/run-claim.test.d.ts +2 -0
- package/dist/core/run-claim.test.d.ts.map +1 -0
- package/dist/core/run-claim.test.js +107 -0
- package/dist/core/run-claim.test.js.map +1 -0
- package/dist/core/run-journal.test.d.ts +2 -0
- package/dist/core/run-journal.test.d.ts.map +1 -0
- package/dist/core/run-journal.test.js +195 -0
- package/dist/core/run-journal.test.js.map +1 -0
- package/dist/core/run-lifecycle.d.ts +22 -3
- package/dist/core/run-lifecycle.d.ts.map +1 -1
- package/dist/core/run-lifecycle.js +12 -3
- package/dist/core/run-lifecycle.js.map +1 -1
- package/dist/core/runtime-mode.d.ts +2 -0
- package/dist/core/runtime-mode.d.ts.map +1 -1
- package/dist/core/runtime-mode.js +4 -0
- package/dist/core/runtime-mode.js.map +1 -1
- package/dist/core/sandbox-domains.d.ts +14 -0
- package/dist/core/sandbox-domains.d.ts.map +1 -0
- package/dist/core/sandbox-domains.js +76 -0
- package/dist/core/sandbox-domains.js.map +1 -0
- package/dist/core/sandbox-feedback.d.ts +22 -0
- package/dist/core/sandbox-feedback.d.ts.map +1 -0
- package/dist/core/sandbox-feedback.js +69 -0
- package/dist/core/sandbox-feedback.js.map +1 -0
- package/dist/core/sandbox-feedback.test.d.ts +2 -0
- package/dist/core/sandbox-feedback.test.d.ts.map +1 -0
- package/dist/core/sandbox-feedback.test.js +32 -0
- package/dist/core/sandbox-feedback.test.js.map +1 -0
- package/dist/core/sandbox.d.ts +91 -0
- package/dist/core/sandbox.d.ts.map +1 -0
- package/dist/core/sandbox.js +254 -0
- package/dist/core/sandbox.js.map +1 -0
- package/dist/core/sandbox.test.d.ts +2 -0
- package/dist/core/sandbox.test.d.ts.map +1 -0
- package/dist/core/sandbox.test.js +157 -0
- package/dist/core/sandbox.test.js.map +1 -0
- package/dist/core/session-compaction.d.ts +6 -0
- package/dist/core/session-compaction.d.ts.map +1 -1
- package/dist/core/session-compaction.js +11 -1
- package/dist/core/session-compaction.js.map +1 -1
- package/dist/core/session-compaction.test.js +18 -1
- package/dist/core/session-compaction.test.js.map +1 -1
- package/dist/core/session-export.d.ts +98 -0
- package/dist/core/session-export.d.ts.map +1 -0
- package/dist/core/session-export.js +364 -0
- package/dist/core/session-export.js.map +1 -0
- package/dist/core/session-export.test.d.ts +2 -0
- package/dist/core/session-export.test.d.ts.map +1 -0
- package/dist/core/session-export.test.js +353 -0
- package/dist/core/session-export.test.js.map +1 -0
- package/dist/core/session-history.d.ts +95 -6
- package/dist/core/session-history.d.ts.map +1 -1
- package/dist/core/session-history.js +261 -17
- package/dist/core/session-history.js.map +1 -1
- package/dist/core/session-history.test.js +173 -2
- package/dist/core/session-history.test.js.map +1 -1
- package/dist/core/session-manager.d.ts +223 -6
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +515 -26
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-manager.test.js +147 -3
- package/dist/core/session-manager.test.js.map +1 -1
- package/dist/core/session-notification-visibility.test.d.ts +2 -0
- package/dist/core/session-notification-visibility.test.d.ts.map +1 -0
- package/dist/core/session-notification-visibility.test.js +113 -0
- package/dist/core/session-notification-visibility.test.js.map +1 -0
- package/dist/core/session-preview.d.ts +5 -4
- package/dist/core/session-preview.d.ts.map +1 -1
- package/dist/core/session-preview.js +12 -5
- package/dist/core/session-preview.js.map +1 -1
- package/dist/core/session-restore-display.test.js +25 -0
- package/dist/core/session-restore-display.test.js.map +1 -1
- package/dist/core/session-storage.d.ts.map +1 -1
- package/dist/core/session-storage.js +64 -4
- package/dist/core/session-storage.js.map +1 -1
- package/dist/core/session-storage.test.js +10 -2
- package/dist/core/session-storage.test.js.map +1 -1
- package/dist/core/settings-manager.d.ts +13 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +36 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/setup-history.d.ts +0 -6
- package/dist/core/setup-history.d.ts.map +1 -1
- package/dist/core/setup-history.js +2 -18
- package/dist/core/setup-history.js.map +1 -1
- package/dist/core/setup-history.test.js +11 -26
- package/dist/core/setup-history.test.js.map +1 -1
- package/dist/core/skills-routing.test.js +3 -1
- package/dist/core/skills-routing.test.js.map +1 -1
- package/dist/core/slash-commands.d.ts +18 -0
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +42 -10
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/slash-commands.test.d.ts +2 -0
- package/dist/core/slash-commands.test.d.ts.map +1 -0
- package/dist/core/slash-commands.test.js +59 -0
- package/dist/core/slash-commands.test.js.map +1 -0
- package/dist/core/speed-benchmark.test.js +9 -3
- package/dist/core/speed-benchmark.test.js.map +1 -1
- package/dist/core/steering.d.ts +12 -0
- package/dist/core/steering.d.ts.map +1 -1
- package/dist/core/steering.js +16 -0
- package/dist/core/steering.js.map +1 -1
- package/dist/core/subagent-manager.d.ts +14 -1
- package/dist/core/subagent-manager.d.ts.map +1 -1
- package/dist/core/subagent-manager.js +48 -1
- package/dist/core/subagent-manager.js.map +1 -1
- package/dist/core/subagent-manager.test.js +43 -7
- package/dist/core/subagent-manager.test.js.map +1 -1
- package/dist/core/subagent-store.test.js +14 -2
- package/dist/core/subagent-store.test.js.map +1 -1
- package/dist/core/verification-evidence.d.ts +17 -0
- package/dist/core/verification-evidence.d.ts.map +1 -0
- package/dist/core/verification-evidence.js +283 -0
- package/dist/core/verification-evidence.js.map +1 -0
- package/dist/core/verification-evidence.test.d.ts +2 -0
- package/dist/core/verification-evidence.test.d.ts.map +1 -0
- package/dist/core/verification-evidence.test.js +103 -0
- package/dist/core/verification-evidence.test.js.map +1 -0
- package/dist/core/workspace-guard.d.ts +2 -0
- package/dist/core/workspace-guard.d.ts.map +1 -1
- package/dist/core/workspace-guard.js +4 -1
- package/dist/core/workspace-guard.js.map +1 -1
- package/dist/core/workspace-guard.test.js +21 -0
- package/dist/core/workspace-guard.test.js.map +1 -1
- package/dist/interactive.d.ts.map +1 -1
- package/dist/interactive.js +17 -3
- package/dist/interactive.js.map +1 -1
- package/dist/modes/acp-mode.d.ts +122 -0
- package/dist/modes/acp-mode.d.ts.map +1 -0
- package/dist/modes/acp-mode.js +1366 -0
- package/dist/modes/acp-mode.js.map +1 -0
- package/dist/modes/acp-mode.test.d.ts +2 -0
- package/dist/modes/acp-mode.test.d.ts.map +1 -0
- package/dist/modes/acp-mode.test.js +1065 -0
- package/dist/modes/acp-mode.test.js.map +1 -0
- package/dist/modes/index.d.ts +1 -0
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js +1 -0
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/json-mode.d.ts +35 -0
- package/dist/modes/json-mode.d.ts.map +1 -1
- package/dist/modes/json-mode.js +38 -0
- package/dist/modes/json-mode.js.map +1 -1
- package/dist/modes/json-mode.test.d.ts +2 -0
- package/dist/modes/json-mode.test.d.ts.map +1 -0
- package/dist/modes/json-mode.test.js +84 -0
- package/dist/modes/json-mode.test.js.map +1 -0
- package/dist/modes/subagent-worker-mode.d.ts +5 -0
- package/dist/modes/subagent-worker-mode.d.ts.map +1 -1
- package/dist/modes/subagent-worker-mode.js +91 -11
- package/dist/modes/subagent-worker-mode.js.map +1 -1
- package/dist/modes/subagent-worker-mode.test.d.ts +2 -0
- package/dist/modes/subagent-worker-mode.test.d.ts.map +1 -0
- package/dist/modes/subagent-worker-mode.test.js +52 -0
- package/dist/modes/subagent-worker-mode.test.js.map +1 -0
- package/dist/session.d.ts +29 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +41 -0
- package/dist/session.js.map +1 -1
- package/dist/system-prompt.d.ts +11 -1
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +39 -18
- package/dist/system-prompt.js.map +1 -1
- package/dist/system-prompt.test.js +33 -17
- package/dist/system-prompt.test.js.map +1 -1
- package/dist/test-support/fake-home.d.ts +20 -0
- package/dist/test-support/fake-home.d.ts.map +1 -0
- package/dist/test-support/fake-home.js +37 -0
- package/dist/test-support/fake-home.js.map +1 -0
- package/dist/tools/bash.d.ts +3 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +70 -14
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/bash.test.js +122 -7
- package/dist/tools/bash.test.js.map +1 -1
- package/dist/tools/edit-robustness.test.js +3 -1
- package/dist/tools/edit-robustness.test.js.map +1 -1
- package/dist/tools/find.d.ts.map +1 -1
- package/dist/tools/find.js +4 -1
- package/dist/tools/find.js.map +1 -1
- package/dist/tools/goals.d.ts +2 -2
- package/dist/tools/grep.d.ts +3 -1
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +137 -12
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/grep.test.js +53 -0
- package/dist/tools/grep.test.js.map +1 -1
- package/dist/tools/index.d.ts +19 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +22 -19
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/index.test.d.ts +2 -0
- package/dist/tools/index.test.d.ts.map +1 -0
- package/dist/tools/index.test.js +55 -0
- package/dist/tools/index.test.js.map +1 -0
- package/dist/tools/overflow.test.js +4 -7
- package/dist/tools/overflow.test.js.map +1 -1
- package/dist/tools/path-utils.d.ts +15 -0
- package/dist/tools/path-utils.d.ts.map +1 -1
- package/dist/tools/path-utils.js +17 -0
- package/dist/tools/path-utils.js.map +1 -1
- package/dist/tools/path-utils.test.js +7 -2
- package/dist/tools/path-utils.test.js.map +1 -1
- package/dist/tools/prompt-hints.js +1 -1
- package/dist/tools/prompt-hints.js.map +1 -1
- package/dist/tools/read-only-bash.d.ts +3 -0
- package/dist/tools/read-only-bash.d.ts.map +1 -1
- package/dist/tools/read-only-bash.js +7 -9
- package/dist/tools/read-only-bash.js.map +1 -1
- package/dist/tools/search-code.d.ts.map +1 -1
- package/dist/tools/search-code.js +6 -4
- package/dist/tools/search-code.js.map +1 -1
- package/dist/tools/subagent-control.d.ts.map +1 -1
- package/dist/tools/subagent-control.js +6 -2
- package/dist/tools/subagent-control.js.map +1 -1
- package/dist/tools/subagent-shared.d.ts +6 -0
- package/dist/tools/subagent-shared.d.ts.map +1 -1
- package/dist/tools/subagent-shared.js +6 -0
- package/dist/tools/subagent-shared.js.map +1 -1
- package/dist/tools/subagent.d.ts +1 -1
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +51 -8
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/subagent.test.js +65 -3
- package/dist/tools/subagent.test.js.map +1 -1
- package/dist/tools/task-output.d.ts.map +1 -1
- package/dist/tools/task-output.js +3 -1
- package/dist/tools/task-output.js.map +1 -1
- package/dist/tools/tool-search.d.ts +12 -1
- package/dist/tools/tool-search.d.ts.map +1 -1
- package/dist/tools/tool-search.js +22 -3
- package/dist/tools/tool-search.js.map +1 -1
- package/dist/tools/tool-search.test.d.ts +2 -0
- package/dist/tools/tool-search.test.d.ts.map +1 -0
- package/dist/tools/tool-search.test.js +69 -0
- package/dist/tools/tool-search.test.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +3 -2
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +22 -8
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/web-fetch.test.js +34 -0
- package/dist/tools/web-fetch.test.js.map +1 -1
- package/dist/tools/web-search.d.ts +2 -1
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +21 -5
- package/dist/tools/web-search.js.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +32 -81
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/app-items.d.ts +1 -12
- package/dist/ui/app-items.d.ts.map +1 -1
- package/dist/ui/app-items.js.map +1 -1
- package/dist/ui/components/Footer.d.ts +1 -1
- package/dist/ui/components/Footer.js +2 -2
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
- package/dist/ui/hooks/useAgentLoop.js +18 -4
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/hooks/usePixelFixFlow.d.ts +1 -2
- package/dist/ui/hooks/usePixelFixFlow.d.ts.map +1 -1
- package/dist/ui/hooks/usePixelFixFlow.js +2 -5
- package/dist/ui/hooks/usePixelFixFlow.js.map +1 -1
- package/dist/ui/login.js +1 -1
- package/dist/ui/login.js.map +1 -1
- package/dist/ui/terminal-history-status-renderers.d.ts +1 -2
- package/dist/ui/terminal-history-status-renderers.d.ts.map +1 -1
- package/dist/ui/terminal-history-status-renderers.js +2 -15
- package/dist/ui/terminal-history-status-renderers.js.map +1 -1
- package/dist/ui/terminal-history.d.ts.map +1 -1
- package/dist/ui/terminal-history.js +2 -4
- package/dist/ui/terminal-history.js.map +1 -1
- package/dist/ui/thinking-level-cycle.test.js +8 -8
- package/dist/ui/thinking-level-cycle.test.js.map +1 -1
- package/dist/ui/transcript/MiscRows.d.ts +1 -4
- package/dist/ui/transcript/MiscRows.d.ts.map +1 -1
- package/dist/ui/transcript/MiscRows.js +2 -7
- package/dist/ui/transcript/MiscRows.js.map +1 -1
- package/dist/ui/transcript/TranscriptRenderer.d.ts.map +1 -1
- package/dist/ui/transcript/TranscriptRenderer.js +1 -3
- package/dist/ui/transcript/TranscriptRenderer.js.map +1 -1
- package/dist/ui/transcript/presentation.d.ts +1 -9
- package/dist/ui/transcript/presentation.d.ts.map +1 -1
- package/dist/ui/transcript/presentation.js +0 -9
- package/dist/ui/transcript/presentation.js.map +1 -1
- package/dist/ui/transcript/spacing.d.ts +2 -2
- package/dist/ui/transcript/spacing.d.ts.map +1 -1
- package/dist/ui/transcript/spacing.js +0 -1
- package/dist/ui/transcript/spacing.js.map +1 -1
- package/dist/ui/transcript/spacing.test.js +1 -3
- package/dist/ui/transcript/spacing.test.js.map +1 -1
- package/dist/ui/tui-history-parity.test.js +2 -9
- package/dist/ui/tui-history-parity.test.js.map +1 -1
- package/dist/utils/github.d.ts +20 -0
- package/dist/utils/github.d.ts.map +1 -0
- package/dist/utils/github.js +65 -0
- package/dist/utils/github.js.map +1 -0
- package/dist/utils/github.test.d.ts +2 -0
- package/dist/utils/github.test.d.ts.map +1 -0
- package/dist/utils/github.test.js +23 -0
- package/dist/utils/github.test.js.map +1 -0
- package/dist/utils/image.d.ts +26 -3
- package/dist/utils/image.d.ts.map +1 -1
- package/dist/utils/image.js +83 -14
- package/dist/utils/image.js.map +1 -1
- package/dist/utils/image.test.d.ts +2 -0
- package/dist/utils/image.test.d.ts.map +1 -0
- package/dist/utils/image.test.js +139 -0
- package/dist/utils/image.test.js.map +1 -0
- package/dist/utils/process.d.ts +26 -3
- package/dist/utils/process.d.ts.map +1 -1
- package/dist/utils/process.js +68 -10
- package/dist/utils/process.js.map +1 -1
- package/dist/utils/process.test.d.ts +2 -0
- package/dist/utils/process.test.d.ts.map +1 -0
- package/dist/utils/process.test.js +42 -0
- package/dist/utils/process.test.js.map +1 -0
- package/package.json +6 -5
|
@@ -5,6 +5,7 @@ import crypto from "node:crypto";
|
|
|
5
5
|
import { environmentSecrets, redactValue, } from "@prestyj/ai";
|
|
6
6
|
import { log } from "./logger.js";
|
|
7
7
|
import { encodeCwd } from "./encode-cwd.js";
|
|
8
|
+
import { getUserSessionPrompt } from "./session-preview.js";
|
|
8
9
|
import { archiveColdSession, archiveSessionPath, cleanupOldSessionTemps, COLD_SESSION_AGE_DAYS, emptyStorageNormalizationMetrics, hydrateSessionEntry, isSessionPath, normalizeSessionEntryForStorage, openSessionReadStream, plainSessionPath, resolveSessionPath, sessionAssetDir, thawSessionArchive, } from "./session-storage.js";
|
|
9
10
|
export const DISPLAY_ITEM_CUSTOM_KIND = "display_item";
|
|
10
11
|
export const TURN_METRIC_CUSTOM_KIND = "turn_metric";
|
|
@@ -71,12 +72,29 @@ export const AUTOPILOT_MARKER_CUSTOM_KIND = "autopilot_marker";
|
|
|
71
72
|
* so the LLM never sees it, anchored by `afterMessageCount` so the host can
|
|
72
73
|
* interleave it back into the transcript on resume. */
|
|
73
74
|
export const APP_MARKER_CUSTOM_KIND = "app_transcript_marker";
|
|
75
|
+
/**
|
|
76
|
+
* Run journal — a matched pair of custom entries bracketing every provider run.
|
|
77
|
+
*
|
|
78
|
+
* Same not-on-the-DAG treatment as Nolan turns (`parentId: null`): the model never
|
|
79
|
+
* sees them, and they can't race the message branch's leaf pointer. A
|
|
80
|
+
* `run_started` with no matching `run_finished` is the on-disk signature of a
|
|
81
|
+
* run that died mid-flight — the host surfaces it on load instead of silently
|
|
82
|
+
* resuming work whose tools already half-mutated the repo.
|
|
83
|
+
*
|
|
84
|
+
* `generation` is the `RunLifecycle` generation, so the pairing inherits that
|
|
85
|
+
* class's generation-safety: a stale run cannot close a newer one's journal.
|
|
86
|
+
*/
|
|
87
|
+
export const RUN_STARTED_CUSTOM_KIND = "run_started";
|
|
88
|
+
export const RUN_FINISHED_CUSTOM_KIND = "run_finished";
|
|
74
89
|
function isCompletedItemLike(value) {
|
|
75
90
|
return (typeof value === "object" &&
|
|
76
91
|
value !== null &&
|
|
77
92
|
typeof value.kind === "string" &&
|
|
78
93
|
typeof value.id === "string");
|
|
79
94
|
}
|
|
95
|
+
const COMPACTION_COORDINATION_DIR = ".compaction-coordination";
|
|
96
|
+
const CORRUPT_LEASE_STALE_MS = 60_000;
|
|
97
|
+
const LEASE_POLL_MS = 50;
|
|
80
98
|
// ── Session Manager ────────────────────────────────────────
|
|
81
99
|
export class SessionManager {
|
|
82
100
|
static activePathsByRoot = new Map();
|
|
@@ -88,6 +106,115 @@ export class SessionManager {
|
|
|
88
106
|
constructor(sessionsDir) {
|
|
89
107
|
this.sessionsDir = path.resolve(sessionsDir);
|
|
90
108
|
}
|
|
109
|
+
coordinationKey(conversationId) {
|
|
110
|
+
return crypto.createHash("sha256").update(conversationId).digest("hex");
|
|
111
|
+
}
|
|
112
|
+
coordinationRoot() {
|
|
113
|
+
return path.join(this.sessionsDir, COMPACTION_COORDINATION_DIR);
|
|
114
|
+
}
|
|
115
|
+
async leaseOwner(lockPath) {
|
|
116
|
+
try {
|
|
117
|
+
const parsed = JSON.parse(await fs.readFile(path.join(lockPath, "owner.json"), "utf-8"));
|
|
118
|
+
return typeof parsed.token === "string" &&
|
|
119
|
+
typeof parsed.pid === "number" &&
|
|
120
|
+
typeof parsed.createdAt === "string"
|
|
121
|
+
? parsed
|
|
122
|
+
: null;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
processIsAlive(pid) {
|
|
129
|
+
try {
|
|
130
|
+
process.kill(pid, 0);
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
return error.code !== "ESRCH";
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async waitForLease(signal) {
|
|
138
|
+
if (signal?.aborted)
|
|
139
|
+
throw new DOMException("Aborted", "AbortError");
|
|
140
|
+
await new Promise((resolve, reject) => {
|
|
141
|
+
const finish = () => {
|
|
142
|
+
signal?.removeEventListener("abort", onAbort);
|
|
143
|
+
resolve();
|
|
144
|
+
};
|
|
145
|
+
const timer = setTimeout(finish, LEASE_POLL_MS);
|
|
146
|
+
const onAbort = () => {
|
|
147
|
+
clearTimeout(timer);
|
|
148
|
+
signal?.removeEventListener("abort", onAbort);
|
|
149
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
150
|
+
};
|
|
151
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
152
|
+
timer.unref?.();
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/** Serialize compaction work across processes for one logical conversation. */
|
|
156
|
+
async withCompactionLease(conversationId, signal, work) {
|
|
157
|
+
const root = this.coordinationRoot();
|
|
158
|
+
await fs.mkdir(root, { recursive: true });
|
|
159
|
+
const lockPath = path.join(root, `${this.coordinationKey(conversationId)}.lock`);
|
|
160
|
+
const token = crypto.randomUUID();
|
|
161
|
+
while (true) {
|
|
162
|
+
try {
|
|
163
|
+
await fs.mkdir(lockPath);
|
|
164
|
+
const owner = {
|
|
165
|
+
token,
|
|
166
|
+
pid: process.pid,
|
|
167
|
+
createdAt: new Date().toISOString(),
|
|
168
|
+
};
|
|
169
|
+
await fs.writeFile(path.join(lockPath, "owner.json"), JSON.stringify(owner), "utf-8");
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
if (error.code !== "EEXIST")
|
|
174
|
+
throw error;
|
|
175
|
+
const owner = await this.leaseOwner(lockPath);
|
|
176
|
+
const stat = await fs.stat(lockPath).catch(() => null);
|
|
177
|
+
const corruptAndOld = !owner && stat !== null && Date.now() - stat.mtimeMs > CORRUPT_LEASE_STALE_MS;
|
|
178
|
+
const deadOwner = owner !== null && !this.processIsAlive(owner.pid);
|
|
179
|
+
if (deadOwner || corruptAndOld) {
|
|
180
|
+
await fs.rm(lockPath, { recursive: true, force: true });
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
await this.waitForLease(signal);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
return await work();
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
const owner = await this.leaseOwner(lockPath);
|
|
191
|
+
if (owner?.token === token)
|
|
192
|
+
await fs.rm(lockPath, { recursive: true, force: true });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async readCompactionAttemptState(conversationId) {
|
|
196
|
+
const statePath = path.join(this.coordinationRoot(), `${this.coordinationKey(conversationId)}.state.json`);
|
|
197
|
+
try {
|
|
198
|
+
const state = JSON.parse(await fs.readFile(statePath, "utf-8"));
|
|
199
|
+
return typeof state.fingerprint === "string" &&
|
|
200
|
+
typeof state.policyKey === "string" &&
|
|
201
|
+
["success", "failed", "noop"].includes(state.outcome) &&
|
|
202
|
+
typeof state.updatedAt === "string"
|
|
203
|
+
? state
|
|
204
|
+
: null;
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async writeCompactionAttemptState(conversationId, state) {
|
|
211
|
+
const root = this.coordinationRoot();
|
|
212
|
+
await fs.mkdir(root, { recursive: true });
|
|
213
|
+
const statePath = path.join(root, `${this.coordinationKey(conversationId)}.state.json`);
|
|
214
|
+
const temporaryPath = `${statePath}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
215
|
+
await fs.writeFile(temporaryPath, JSON.stringify(state), "utf-8");
|
|
216
|
+
await fs.rename(temporaryPath, statePath);
|
|
217
|
+
}
|
|
91
218
|
/**
|
|
92
219
|
* Session persistence must never crash a live session. Disk-full (ENOSPC),
|
|
93
220
|
* permission, or quota errors during transcript writes are reported once
|
|
@@ -124,6 +251,12 @@ export class SessionManager {
|
|
|
124
251
|
version: 2,
|
|
125
252
|
id,
|
|
126
253
|
conversationId: options?.conversationId ?? id,
|
|
254
|
+
generation: options?.generation ?? 0,
|
|
255
|
+
...(options?.parentSessionId ? { parentSessionId: options.parentSessionId } : {}),
|
|
256
|
+
...(options?.sourceFingerprint ? { sourceFingerprint: options.sourceFingerprint } : {}),
|
|
257
|
+
...(typeof options?.retainedMessageCount === "number"
|
|
258
|
+
? { retainedMessageCount: options.retainedMessageCount }
|
|
259
|
+
: {}),
|
|
127
260
|
...(safePreview ? { preview: safePreview } : {}),
|
|
128
261
|
timestamp,
|
|
129
262
|
cwd,
|
|
@@ -163,7 +296,68 @@ export class SessionManager {
|
|
|
163
296
|
async load(sessionPath) {
|
|
164
297
|
// Resuming is a write operation, so transparently thaw a gzip archive and
|
|
165
298
|
// return the effective plain path every future append must use.
|
|
166
|
-
const
|
|
299
|
+
const canonicalPath = (await this.resolveCanonicalSession(sessionPath)) ?? sessionPath;
|
|
300
|
+
const effectivePath = await thawSessionArchive(canonicalPath);
|
|
301
|
+
return this.loadPhysicalCheckpoint(effectivePath);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Load the contiguous checkpoint ancestry ending at the canonical newest file.
|
|
305
|
+
*
|
|
306
|
+
* The returned order is oldest → newest. A missing, corrupt, cyclic, or
|
|
307
|
+
* cross-conversation parent stops traversal at the oldest readable checkpoint,
|
|
308
|
+
* allowing display callers to retain that checkpoint's compaction summary as a
|
|
309
|
+
* fallback. Parent archives are read in place rather than thawed because this
|
|
310
|
+
* API is for history reconstruction, not resuming writes.
|
|
311
|
+
*/
|
|
312
|
+
async loadCheckpointChain(sessionPath) {
|
|
313
|
+
const requestedSummary = await this.readSessionSummary(sessionPath);
|
|
314
|
+
// A resumed session can compact under a different cwd than its parent (for
|
|
315
|
+
// example, a remote ACP client reconnecting from another workspace). The
|
|
316
|
+
// physical ancestry is therefore machine-wide, not confined to the newest
|
|
317
|
+
// checkpoint's encoded-cwd directory.
|
|
318
|
+
const directories = await this.storageDirectories();
|
|
319
|
+
const candidates = (await Promise.all(directories.map((directory) => this.sessionCandidates(directory)))).flat();
|
|
320
|
+
const summaries = (await Promise.all(candidates.map((candidate) => this.readSessionSummary(candidate)))).filter((summary) => summary !== null);
|
|
321
|
+
const requestedIdentity = requestedSummary?.conversationId ??
|
|
322
|
+
summaries.find((summary) => summary.id === sessionPath)?.conversationId;
|
|
323
|
+
const newestSummary = requestedIdentity
|
|
324
|
+
? summaries
|
|
325
|
+
.filter((summary) => summary.conversationId === requestedIdentity)
|
|
326
|
+
.sort((left, right) => SessionManager.compareCheckpoints(right, left))[0]
|
|
327
|
+
: undefined;
|
|
328
|
+
const newestPath = newestSummary?.path ?? sessionPath;
|
|
329
|
+
// Resuming the newest checkpoint is a write operation, so thaw only that
|
|
330
|
+
// generation. Historical parents stay archived and read-only.
|
|
331
|
+
const newest = await this.loadPhysicalCheckpoint(await thawSessionArchive(newestPath));
|
|
332
|
+
const newestConversationId = newest.header.conversationId ?? newest.header.id;
|
|
333
|
+
const byPhysicalId = new Map(summaries.map((summary) => [summary.id, summary.path]));
|
|
334
|
+
const newestResolvedPath = path.resolve(newest.path);
|
|
335
|
+
const visited = new Set([newest.header.id]);
|
|
336
|
+
const chain = [newest];
|
|
337
|
+
let current = newest;
|
|
338
|
+
while (current.header.parentSessionId) {
|
|
339
|
+
const parentId = current.header.parentSessionId;
|
|
340
|
+
if (visited.has(parentId))
|
|
341
|
+
break;
|
|
342
|
+
const parentPath = byPhysicalId.get(parentId);
|
|
343
|
+
if (!parentPath || path.resolve(parentPath) === newestResolvedPath)
|
|
344
|
+
break;
|
|
345
|
+
try {
|
|
346
|
+
const parent = await this.loadPhysicalCheckpoint(parentPath, true);
|
|
347
|
+
if ((parent.header.conversationId ?? parent.header.id) !== newestConversationId)
|
|
348
|
+
break;
|
|
349
|
+
visited.add(parentId);
|
|
350
|
+
chain.unshift(parent);
|
|
351
|
+
current = parent;
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return chain;
|
|
358
|
+
}
|
|
359
|
+
async loadPhysicalCheckpoint(sessionPath, rejectMalformedLines = false) {
|
|
360
|
+
const effectivePath = await resolveSessionPath(sessionPath);
|
|
167
361
|
const { stream } = await openSessionReadStream(effectivePath);
|
|
168
362
|
let header = null;
|
|
169
363
|
const entries = [];
|
|
@@ -184,6 +378,7 @@ export class SessionManager {
|
|
|
184
378
|
version: 2,
|
|
185
379
|
id: v1.id,
|
|
186
380
|
conversationId: v1.id,
|
|
381
|
+
generation: 0,
|
|
187
382
|
timestamp: v1.timestamp,
|
|
188
383
|
cwd: v1.cwd,
|
|
189
384
|
provider: v1.provider,
|
|
@@ -200,7 +395,9 @@ export class SessionManager {
|
|
|
200
395
|
}
|
|
201
396
|
entries.push(await hydrateSessionEntry(entry, effectivePath));
|
|
202
397
|
}
|
|
203
|
-
catch {
|
|
398
|
+
catch (error) {
|
|
399
|
+
if (rejectMalformedLines)
|
|
400
|
+
throw error;
|
|
204
401
|
// Skip malformed JSON lines — cold migration preserves their raw bytes
|
|
205
402
|
// so a future recovery tool still has a chance to repair them.
|
|
206
403
|
}
|
|
@@ -218,6 +415,7 @@ export class SessionManager {
|
|
|
218
415
|
let first = null;
|
|
219
416
|
let messageCount = 0;
|
|
220
417
|
let lastActivity = null;
|
|
418
|
+
let preview;
|
|
221
419
|
for await (const line of rl) {
|
|
222
420
|
if (!line)
|
|
223
421
|
continue;
|
|
@@ -227,11 +425,24 @@ export class SessionManager {
|
|
|
227
425
|
if (parsed.type !== "session")
|
|
228
426
|
break;
|
|
229
427
|
first = parsed;
|
|
428
|
+
// v2 headers usually carry the preview already; when they do,
|
|
429
|
+
// nothing below has to look for one.
|
|
430
|
+
preview = parsed.preview?.trim() || undefined;
|
|
230
431
|
}
|
|
231
432
|
else if (parsed.type === "message") {
|
|
232
433
|
messageCount += 1;
|
|
233
434
|
if (parsed.timestamp)
|
|
234
435
|
lastActivity = parsed.timestamp;
|
|
436
|
+
// Recover a title for the sessions whose header predates `preview`.
|
|
437
|
+
// Free here: this pass already reads every line. `getUserSessionPrompt`
|
|
438
|
+
// rejects compaction summaries and autopilot/status injections, so a
|
|
439
|
+
// session is titled by what its user actually asked.
|
|
440
|
+
if (!preview && parsed.message?.role === "user") {
|
|
441
|
+
preview =
|
|
442
|
+
getUserSessionPrompt(parsed.message.content, parsed.message.provenance)
|
|
443
|
+
?.replace(/\s+/g, " ")
|
|
444
|
+
.trim() || undefined;
|
|
445
|
+
}
|
|
235
446
|
}
|
|
236
447
|
}
|
|
237
448
|
catch {
|
|
@@ -245,11 +456,13 @@ export class SessionManager {
|
|
|
245
456
|
conversationId: first.version === 2
|
|
246
457
|
? (first.conversationId ?? first.id)
|
|
247
458
|
: first.id,
|
|
459
|
+
generation: first.version === 2 ? (first.generation ?? 0) : 0,
|
|
248
460
|
path: resolvedPath,
|
|
249
461
|
timestamp: first.timestamp,
|
|
250
462
|
lastActivity: lastActivity ?? first.timestamp,
|
|
251
463
|
cwd: first.cwd,
|
|
252
464
|
messageCount,
|
|
465
|
+
...(preview ? { preview: preview.slice(0, 100) } : {}),
|
|
253
466
|
};
|
|
254
467
|
}
|
|
255
468
|
catch {
|
|
@@ -266,9 +479,92 @@ export class SessionManager {
|
|
|
266
479
|
}
|
|
267
480
|
return files.filter(isSessionPath).map((file) => path.join(directory, file));
|
|
268
481
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
482
|
+
/**
|
|
483
|
+
* Read just enough of a session file to summarize it.
|
|
484
|
+
*
|
|
485
|
+
* Stops at the first user prompt (or the first message, when the header
|
|
486
|
+
* already carries a preview) instead of parsing the whole transcript — this
|
|
487
|
+
* is what makes listing every session on the machine cheap. Files whose
|
|
488
|
+
* first user message is far down (long tool runs before the user speaks)
|
|
489
|
+
* are capped rather than allowed to stall the list.
|
|
490
|
+
*/
|
|
491
|
+
async readSessionSummary(candidatePath) {
|
|
492
|
+
try {
|
|
493
|
+
const resolvedPath = await resolveSessionPath(candidatePath);
|
|
494
|
+
const { stream } = await openSessionReadStream(resolvedPath);
|
|
495
|
+
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
496
|
+
let first = null;
|
|
497
|
+
let preview;
|
|
498
|
+
let hasMessages = false;
|
|
499
|
+
let lines = 0;
|
|
500
|
+
const MAX_SCAN_LINES = 500;
|
|
501
|
+
try {
|
|
502
|
+
for await (const line of rl) {
|
|
503
|
+
if (!line)
|
|
504
|
+
continue;
|
|
505
|
+
if (++lines > MAX_SCAN_LINES)
|
|
506
|
+
break;
|
|
507
|
+
try {
|
|
508
|
+
const parsed = JSON.parse(line);
|
|
509
|
+
if (!first) {
|
|
510
|
+
if (parsed.type !== "session")
|
|
511
|
+
break;
|
|
512
|
+
first = parsed;
|
|
513
|
+
preview = parsed.preview?.trim() || undefined;
|
|
514
|
+
}
|
|
515
|
+
else if (parsed.type === "message") {
|
|
516
|
+
hasMessages = true;
|
|
517
|
+
// Header preview + any message: done. Otherwise keep scanning for
|
|
518
|
+
// the first user-authored prompt to title by.
|
|
519
|
+
if (preview)
|
|
520
|
+
break;
|
|
521
|
+
if (parsed.message?.role === "user") {
|
|
522
|
+
preview =
|
|
523
|
+
getUserSessionPrompt(parsed.message.content, parsed.message.provenance)
|
|
524
|
+
?.replace(/\s+/g, " ")
|
|
525
|
+
.trim() || undefined;
|
|
526
|
+
if (preview)
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
catch {
|
|
532
|
+
// Skip malformed lines while retaining readable entries around them.
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
finally {
|
|
537
|
+
rl.close();
|
|
538
|
+
stream.destroy();
|
|
539
|
+
}
|
|
540
|
+
if (!first || first.type !== "session")
|
|
541
|
+
return null;
|
|
542
|
+
const stat = await fs.stat(resolvedPath);
|
|
543
|
+
return {
|
|
544
|
+
id: first.id,
|
|
545
|
+
conversationId: first.version === 2
|
|
546
|
+
? (first.conversationId ?? first.id)
|
|
547
|
+
: first.id,
|
|
548
|
+
generation: first.version === 2 ? (first.generation ?? 0) : 0,
|
|
549
|
+
path: resolvedPath,
|
|
550
|
+
timestamp: first.timestamp,
|
|
551
|
+
lastActivity: stat.mtime.toISOString(),
|
|
552
|
+
cwd: first.cwd,
|
|
553
|
+
hasMessages,
|
|
554
|
+
...(preview ? { preview: preview.slice(0, 100) } : {}),
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
catch {
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Keep the newest file per conversation and sort newest first.
|
|
563
|
+
*
|
|
564
|
+
* A conversation can span several files (compaction forks a fresh one), so
|
|
565
|
+
* without this collapse a resumed thread appears once per checkpoint.
|
|
566
|
+
*/
|
|
567
|
+
static dedupeByConversation(summaries) {
|
|
272
568
|
const byConversation = new Map();
|
|
273
569
|
const seenResolvedPaths = new Set();
|
|
274
570
|
for (const summary of summaries) {
|
|
@@ -276,25 +572,109 @@ export class SessionManager {
|
|
|
276
572
|
continue;
|
|
277
573
|
seenResolvedPaths.add(summary.path);
|
|
278
574
|
const current = byConversation.get(summary.conversationId);
|
|
279
|
-
if (!current || summary
|
|
280
|
-
|
|
281
|
-
byConversation.set(summary.conversationId, info);
|
|
575
|
+
if (!current || SessionManager.compareCheckpoints(summary, current) > 0) {
|
|
576
|
+
byConversation.set(summary.conversationId, summary);
|
|
282
577
|
}
|
|
283
578
|
}
|
|
284
|
-
return [...byConversation.values()]
|
|
579
|
+
return [...byConversation.values()]
|
|
580
|
+
.sort((a, b) => b.lastActivity.localeCompare(a.lastActivity) ||
|
|
581
|
+
b.timestamp.localeCompare(a.timestamp) ||
|
|
582
|
+
b.path.localeCompare(a.path))
|
|
583
|
+
.map(({ conversationId: _conversationId, generation: _generation, ...info }) => info);
|
|
584
|
+
}
|
|
585
|
+
static compareCheckpoints(left, right) {
|
|
586
|
+
return ((left.generation ?? 0) - (right.generation ?? 0) ||
|
|
587
|
+
left.lastActivity.localeCompare(right.lastActivity) ||
|
|
588
|
+
left.timestamp.localeCompare(right.timestamp) ||
|
|
589
|
+
left.path.localeCompare(right.path));
|
|
590
|
+
}
|
|
591
|
+
async list(cwd) {
|
|
592
|
+
return this.summarize(await this.sessionCandidates(this.dirForCwd(cwd)));
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* One project's sessions, newest first, using the early-exit summary read.
|
|
596
|
+
*
|
|
597
|
+
* For callers that render a list rather than exact message counts — on a
|
|
598
|
+
* project with hundreds of sessions this is the difference between instant
|
|
599
|
+
* and a noticeable stall.
|
|
600
|
+
*/
|
|
601
|
+
async listSummaries(cwd) {
|
|
602
|
+
const candidates = await this.sessionCandidates(this.dirForCwd(cwd));
|
|
603
|
+
const summaries = await Promise.all(candidates.map((file) => this.readSessionSummary(file)));
|
|
604
|
+
return SessionManager.dedupeByConversation(summaries);
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Every session on this machine, across every project, newest first.
|
|
608
|
+
*
|
|
609
|
+
* A remote client is not browsing one checkout the way the TUI is — it is
|
|
610
|
+
* asking "what have I been working on?", and the answer spans projects. Each
|
|
611
|
+
* entry carries its own `cwd`, so the caller can group by project. Uses the
|
|
612
|
+
* early-exit summary read, because "every session on the machine" is exactly
|
|
613
|
+
* where a full parse of each file becomes a multi-second stall.
|
|
614
|
+
*/
|
|
615
|
+
async listAllSummaries() {
|
|
616
|
+
const directories = await this.storageDirectories();
|
|
617
|
+
const candidates = await Promise.all(directories.map((directory) => this.sessionCandidates(directory)));
|
|
618
|
+
const summaries = await Promise.all(candidates.flat().map((file) => this.readSessionSummary(file)));
|
|
619
|
+
return SessionManager.dedupeByConversation(summaries);
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Summarize session files, keeping the newest file per conversation.
|
|
623
|
+
*
|
|
624
|
+
* A conversation can span several files (compaction forks a fresh one), so
|
|
625
|
+
* without this collapse a resumed thread appears once per checkpoint.
|
|
626
|
+
*/
|
|
627
|
+
async summarize(candidates) {
|
|
628
|
+
const summaries = await Promise.all(candidates.map((file) => this.readSessionInfo(file)));
|
|
629
|
+
return SessionManager.dedupeByConversation(summaries);
|
|
285
630
|
}
|
|
286
631
|
async getMostRecent(cwd) {
|
|
287
632
|
const sessions = await this.list(cwd);
|
|
288
633
|
return sessions.find((session) => session.messageCount > 0)?.path ?? null;
|
|
289
634
|
}
|
|
635
|
+
/** Resolve an id, conversation id, or stale physical path to the newest checkpoint. */
|
|
636
|
+
async resolveCanonicalSession(requested, cwd) {
|
|
637
|
+
const looksLikePath = path.isAbsolute(requested) || requested.includes(path.sep) || isSessionPath(requested);
|
|
638
|
+
let candidates;
|
|
639
|
+
let requestedSummary = null;
|
|
640
|
+
if (looksLikePath) {
|
|
641
|
+
requestedSummary = await this.readSessionSummary(requested);
|
|
642
|
+
if (!requestedSummary)
|
|
643
|
+
return null;
|
|
644
|
+
candidates = await this.sessionCandidates(path.dirname(requestedSummary.path));
|
|
645
|
+
}
|
|
646
|
+
else if (cwd) {
|
|
647
|
+
candidates = await this.sessionCandidates(this.dirForCwd(cwd));
|
|
648
|
+
}
|
|
649
|
+
else {
|
|
650
|
+
const directories = await this.storageDirectories();
|
|
651
|
+
candidates = (await Promise.all(directories.map((dir) => this.sessionCandidates(dir)))).flat();
|
|
652
|
+
}
|
|
653
|
+
const summaries = (await Promise.all(candidates.map((candidate) => this.readSessionSummary(candidate)))).filter((summary) => summary !== null);
|
|
654
|
+
if (requestedSummary && !summaries.some((summary) => summary.path === requestedSummary?.path)) {
|
|
655
|
+
summaries.push(requestedSummary);
|
|
656
|
+
}
|
|
657
|
+
const identityMatches = requestedSummary
|
|
658
|
+
? [requestedSummary]
|
|
659
|
+
: summaries.filter((summary) => summary.id === requested || summary.conversationId === requested);
|
|
660
|
+
if (identityMatches.length === 0)
|
|
661
|
+
return null;
|
|
662
|
+
const requestedIdentity = identityMatches.sort((a, b) => SessionManager.compareCheckpoints(b, a))[0].conversationId;
|
|
663
|
+
const checkpoints = summaries.filter((summary) => summary.conversationId === requestedIdentity);
|
|
664
|
+
return (checkpoints.sort((a, b) => SessionManager.compareCheckpoints(b, a))[0]?.path ??
|
|
665
|
+
identityMatches[0].path);
|
|
666
|
+
}
|
|
290
667
|
async findById(cwd, sessionId) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
668
|
+
return this.resolveCanonicalSession(sessionId, cwd);
|
|
669
|
+
}
|
|
670
|
+
/** Locate and canonicalize a session identity across every project directory. */
|
|
671
|
+
async findAnyById(sessionId, cwd) {
|
|
672
|
+
if (cwd) {
|
|
673
|
+
const local = await this.resolveCanonicalSession(sessionId, cwd);
|
|
674
|
+
if (local)
|
|
675
|
+
return local;
|
|
296
676
|
}
|
|
297
|
-
return
|
|
677
|
+
return this.resolveCanonicalSession(sessionId);
|
|
298
678
|
}
|
|
299
679
|
async storageDirectories() {
|
|
300
680
|
let entries;
|
|
@@ -305,7 +685,7 @@ export class SessionManager {
|
|
|
305
685
|
return [];
|
|
306
686
|
}
|
|
307
687
|
return entries
|
|
308
|
-
.filter((entry) => entry.isDirectory())
|
|
688
|
+
.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."))
|
|
309
689
|
.map((entry) => path.join(this.sessionsDir, entry.name));
|
|
310
690
|
}
|
|
311
691
|
async logicalSessionBases(directory) {
|
|
@@ -471,6 +851,84 @@ export class SessionManager {
|
|
|
471
851
|
};
|
|
472
852
|
await this.appendEntry(sessionPath, entry);
|
|
473
853
|
}
|
|
854
|
+
/** Open the run journal for one `RunLifecycle` generation. */
|
|
855
|
+
async appendRunStarted(sessionPath, payload) {
|
|
856
|
+
await this.appendEntry(sessionPath, {
|
|
857
|
+
type: "custom",
|
|
858
|
+
kind: RUN_STARTED_CUSTOM_KIND,
|
|
859
|
+
id: crypto.randomUUID(),
|
|
860
|
+
parentId: null,
|
|
861
|
+
timestamp: new Date().toISOString(),
|
|
862
|
+
data: payload,
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
/** Close the run journal for one generation. Its absence marks a crashed run. */
|
|
866
|
+
async appendRunFinished(sessionPath, payload) {
|
|
867
|
+
await this.appendEntry(sessionPath, {
|
|
868
|
+
type: "custom",
|
|
869
|
+
kind: RUN_FINISHED_CUSTOM_KIND,
|
|
870
|
+
id: crypto.randomUUID(),
|
|
871
|
+
parentId: null,
|
|
872
|
+
timestamp: new Date().toISOString(),
|
|
873
|
+
data: payload,
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Reconstruct the run journal in file order, pairing each `run_started` with
|
|
878
|
+
* the `run_finished` carrying the same generation.
|
|
879
|
+
*
|
|
880
|
+
* Generations are NOT unique across a session file. `RunLifecycle` counts
|
|
881
|
+
* from zero per instance, and a resumed session builds a fresh one — so the
|
|
882
|
+
* first run after every app restart is generation 1 again. Pairing therefore
|
|
883
|
+
* tracks only the runs still OPEN: a `run_finished` closes its generation and
|
|
884
|
+
* releases the number, and a later `run_started` reusing it opens a new run.
|
|
885
|
+
*
|
|
886
|
+
* Without that release, a crash in a resumed session was invisible: the
|
|
887
|
+
* reused `run_started` looked like a duplicate and was dropped, which is
|
|
888
|
+
* exactly the case this journal exists to catch.
|
|
889
|
+
*
|
|
890
|
+
* A repeat `run_started` for a generation that is still open IS ignored, so a
|
|
891
|
+
* truncated or replayed log reports one unfinished run — never a phantom pile.
|
|
892
|
+
*/
|
|
893
|
+
getRunJournal(entries) {
|
|
894
|
+
const runs = [];
|
|
895
|
+
const openByGeneration = new Map();
|
|
896
|
+
for (const entry of entries) {
|
|
897
|
+
if (entry.type !== "custom")
|
|
898
|
+
continue;
|
|
899
|
+
if (entry.kind === RUN_STARTED_CUSTOM_KIND) {
|
|
900
|
+
const p = entry.data;
|
|
901
|
+
if (p?.version !== 1 || typeof p.generation !== "number")
|
|
902
|
+
continue;
|
|
903
|
+
if (openByGeneration.has(p.generation))
|
|
904
|
+
continue;
|
|
905
|
+
const run = {
|
|
906
|
+
generation: p.generation,
|
|
907
|
+
startedAt: typeof p.startedAt === "string" ? p.startedAt : entry.timestamp,
|
|
908
|
+
afterMessageCount: typeof p.afterMessageCount === "number" ? p.afterMessageCount : 0,
|
|
909
|
+
};
|
|
910
|
+
openByGeneration.set(p.generation, run);
|
|
911
|
+
runs.push(run);
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
if (entry.kind === RUN_FINISHED_CUSTOM_KIND) {
|
|
915
|
+
const p = entry.data;
|
|
916
|
+
if (p?.version !== 1 || typeof p.generation !== "number")
|
|
917
|
+
continue;
|
|
918
|
+
const run = openByGeneration.get(p.generation);
|
|
919
|
+
if (!run || !p.outcome)
|
|
920
|
+
continue;
|
|
921
|
+
run.outcome = p.outcome;
|
|
922
|
+
// Closed — the number is free for the next app run to reuse.
|
|
923
|
+
openByGeneration.delete(p.generation);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return runs;
|
|
927
|
+
}
|
|
928
|
+
/** Runs that opened the journal but never closed it — i.e. crashed mid-flight. */
|
|
929
|
+
getUnfinishedRuns(entries) {
|
|
930
|
+
return this.getRunJournal(entries).filter((run) => run.outcome === undefined);
|
|
931
|
+
}
|
|
474
932
|
async updateLeaf(sessionPath, leafId) {
|
|
475
933
|
try {
|
|
476
934
|
const writablePath = await thawSessionArchive(sessionPath);
|
|
@@ -540,11 +998,36 @@ export class SessionManager {
|
|
|
540
998
|
return isCompletedItemLike(item) ? [item] : [];
|
|
541
999
|
});
|
|
542
1000
|
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Walk entries in file order, tracking how many branch (non-system) messages
|
|
1003
|
+
* have been written so far, and hand each custom entry that count. This is
|
|
1004
|
+
* the independent position estimate behind {@link RecordedPosition}.
|
|
1005
|
+
*/
|
|
1006
|
+
mapCustomEntriesInFileOrder(entries, leafId, project) {
|
|
1007
|
+
// Only branch messages count — an off-branch fork's entries are not part of
|
|
1008
|
+
// the restored transcript the anchors are measured against.
|
|
1009
|
+
const onBranch = leafId
|
|
1010
|
+
? new Set(this.getBranch(entries, leafId).map((e) => e.id))
|
|
1011
|
+
: new Set(entries.map((e) => e.id));
|
|
1012
|
+
const out = [];
|
|
1013
|
+
let messagesSoFar = 0;
|
|
1014
|
+
for (const entry of entries) {
|
|
1015
|
+
if (entry.type === "message") {
|
|
1016
|
+
if (onBranch.has(entry.id) && entry.message.role !== "system")
|
|
1017
|
+
messagesSoFar++;
|
|
1018
|
+
continue;
|
|
1019
|
+
}
|
|
1020
|
+
if (entry.type !== "custom")
|
|
1021
|
+
continue;
|
|
1022
|
+
out.push(...project(entry, messagesSoFar));
|
|
1023
|
+
}
|
|
1024
|
+
return out;
|
|
1025
|
+
}
|
|
543
1026
|
/** Read all persisted Nolan turns in file order. Returns them regardless of
|
|
544
1027
|
* branch (Nolan turns are not chained into the DAG), validated + normalized. */
|
|
545
|
-
getNolanTurns(entries) {
|
|
546
|
-
return
|
|
547
|
-
if (entry.
|
|
1028
|
+
getNolanTurns(entries, leafId) {
|
|
1029
|
+
return this.mapCustomEntriesInFileOrder(entries, leafId, (entry, recordedAfterMessageCount) => {
|
|
1030
|
+
if (entry.kind !== NOLAN_TURN_CUSTOM_KIND)
|
|
548
1031
|
return [];
|
|
549
1032
|
const p = entry.data;
|
|
550
1033
|
if (p?.version === 1 && typeof p.question === "string" && typeof p.reply === "string") {
|
|
@@ -554,6 +1037,7 @@ export class SessionManager {
|
|
|
554
1037
|
question: p.question,
|
|
555
1038
|
reply: p.reply,
|
|
556
1039
|
afterMessageCount: typeof p.afterMessageCount === "number" ? p.afterMessageCount : 0,
|
|
1040
|
+
recordedAfterMessageCount,
|
|
557
1041
|
},
|
|
558
1042
|
];
|
|
559
1043
|
}
|
|
@@ -562,9 +1046,9 @@ export class SessionManager {
|
|
|
562
1046
|
}
|
|
563
1047
|
/** Read all persisted app transcript markers in file order, validated +
|
|
564
1048
|
* normalized (same not-on-the-DAG treatment as Nolan turns). */
|
|
565
|
-
getAppMarkers(entries) {
|
|
566
|
-
return
|
|
567
|
-
if (entry.
|
|
1049
|
+
getAppMarkers(entries, leafId) {
|
|
1050
|
+
return this.mapCustomEntriesInFileOrder(entries, leafId, (entry, recordedAfterMessageCount) => {
|
|
1051
|
+
if (entry.kind !== APP_MARKER_CUSTOM_KIND)
|
|
568
1052
|
return [];
|
|
569
1053
|
const p = entry.data;
|
|
570
1054
|
const kind = p?.kind;
|
|
@@ -574,13 +1058,17 @@ export class SessionManager {
|
|
|
574
1058
|
kind === "error" ||
|
|
575
1059
|
kind === "user_hint" ||
|
|
576
1060
|
kind === "compaction" ||
|
|
577
|
-
kind === "agent_handoff"
|
|
1061
|
+
kind === "agent_handoff" ||
|
|
1062
|
+
kind === "model_switch" ||
|
|
1063
|
+
kind === "import" ||
|
|
1064
|
+
kind === "interrupted_run")) {
|
|
578
1065
|
return [
|
|
579
1066
|
{
|
|
580
1067
|
version: 1,
|
|
581
1068
|
kind,
|
|
582
1069
|
afterMessageCount: typeof p.afterMessageCount === "number" ? p.afterMessageCount : 0,
|
|
583
1070
|
data: typeof p.data === "object" && p.data !== null ? p.data : {},
|
|
1071
|
+
recordedAfterMessageCount,
|
|
584
1072
|
},
|
|
585
1073
|
];
|
|
586
1074
|
}
|
|
@@ -598,9 +1086,9 @@ export class SessionManager {
|
|
|
598
1086
|
}
|
|
599
1087
|
/** Read all persisted autopilot markers in file order, validated + normalized
|
|
600
1088
|
* (same not-on-the-DAG treatment as Nolan turns). */
|
|
601
|
-
getAutopilotMarkers(entries) {
|
|
602
|
-
return
|
|
603
|
-
if (entry.
|
|
1089
|
+
getAutopilotMarkers(entries, leafId) {
|
|
1090
|
+
return this.mapCustomEntriesInFileOrder(entries, leafId, (entry, recordedAfterMessageCount) => {
|
|
1091
|
+
if (entry.kind !== AUTOPILOT_MARKER_CUSTOM_KIND)
|
|
604
1092
|
return [];
|
|
605
1093
|
const p = entry.data;
|
|
606
1094
|
const phase = p?.phase;
|
|
@@ -617,6 +1105,7 @@ export class SessionManager {
|
|
|
617
1105
|
...(typeof p.reason === "string" ? { reason: p.reason } : {}),
|
|
618
1106
|
...(typeof p.body === "string" ? { body: p.body } : {}),
|
|
619
1107
|
afterMessageCount: typeof p.afterMessageCount === "number" ? p.afterMessageCount : 0,
|
|
1108
|
+
recordedAfterMessageCount,
|
|
620
1109
|
},
|
|
621
1110
|
];
|
|
622
1111
|
}
|