mixdog 1.1.2 → 1.2.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 +8 -5
- package/package.json +2 -1
- package/scripts/lib/run-node-tests.mjs +9 -0
- package/scripts/prune-desktop-runtime.mjs +3 -0
- package/scripts/test-direct.mjs +4 -2
- package/src/defaults/shipped-history.json +10 -0
- package/src/defaults/skills/pptx/SKILL.md +6 -6
- package/src/defaults/skills/pptx/references/review.md +6 -4
- package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
- package/src/defaults/skills/setup/references/actions.md +18 -5
- package/src/defaults/skills/visualize/SKILL.md +160 -0
- package/src/defaults/skills/visualize/references/craft.md +88 -0
- package/src/headless-command.mjs +56 -3
- package/src/headless-exec.mjs +17 -1
- package/src/help.mjs +2 -0
- package/src/rules/shared/60-verification.md +3 -1
- package/src/rules/shared/70-delivery.md +4 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +1 -0
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -0
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
- package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-providers-merge.mjs +17 -11
- package/src/runtime/agent/orchestrator/config-storage.mjs +1 -0
- package/src/runtime/agent/orchestrator/config.mjs +19 -5
- package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +133 -15
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +55 -8
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +86 -8
- package/src/runtime/agent/orchestrator/mcp/client.mjs +474 -164
- package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
- package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
- package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +108 -17
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -12
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +444 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +21 -6
- package/src/runtime/agent/orchestrator/providers/effort-rejection.mjs +244 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +41 -28
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +8 -40
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +51 -12
- package/src/runtime/agent/orchestrator/providers/model-cache.mjs +37 -2
- package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +82 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +556 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +251 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +69 -102
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +15 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +15 -4
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/registry.mjs +47 -7
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +10 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +36 -3
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -11
- package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
- package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +16 -1
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +278 -84
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -3
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +9 -5
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +284 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +268 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +24 -13
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +362 -0
- package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +253 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +184 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
- package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/diagnostic.mjs +1 -7
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/refusal.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +21 -10
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +9 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +37 -16
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +17 -4
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +90 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +32 -3
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/message-ids.mjs +123 -0
- package/src/runtime/agent/orchestrator/session/message-ids.test-support.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +33 -22
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +817 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +23 -12
- package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +202 -374
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +20 -3
- package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +48 -9
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +52 -2
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +32 -22
- package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +10 -3
- package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-cache-policy.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +203 -33
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +32 -35
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +11 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +9 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +83 -68
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +8 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +20 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +16 -7
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +137 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +11 -10
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +21 -11
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +13 -10
- package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +56 -13
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +58 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +25 -1
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
- package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
- package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +78 -5
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
- package/src/runtime/attachments/store.d.mts +3 -0
- package/src/runtime/attachments/store.mjs +38 -0
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
- package/src/runtime/channels/lib/worker-main.mjs +17 -14
- package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
- package/src/runtime/effort-judge/judge-client.mjs +56 -18
- package/src/runtime/memory/lib/memory.mjs +1 -1
- package/src/runtime/memory/lib/session-ingest.mjs +2 -2
- package/src/runtime/shared/atomic-file.mjs +33 -6
- package/src/runtime/shared/best-effort-stderr.mjs +8 -0
- package/src/runtime/shared/code-graph-state.mjs +12 -0
- package/src/runtime/shared/defaults-separation.mjs +2 -0
- package/src/runtime/shared/extension-scopes.mjs +5 -3
- package/src/runtime/shared/file-lock.mjs +37 -11
- package/src/runtime/shared/git-runner-worker.mjs +3 -2
- package/src/runtime/shared/git-runner.mjs +4 -3
- package/src/runtime/shared/llm/anthropic-effort.mjs +14 -0
- package/src/runtime/shared/llm/default-context-window.mjs +6 -0
- package/src/runtime/shared/llm/model-catalog-projection.mjs +5 -4
- package/src/runtime/shared/llm/model-catalog.mjs +240 -130
- package/src/runtime/shared/llm/provider-catalog-cache.mjs +36 -28
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +162 -0
- package/src/runtime/shared/llm/provider-seed-rosters.mjs +130 -0
- package/src/runtime/shared/model-proxy-mode.d.mts +3 -0
- package/src/runtime/shared/model-proxy-mode.mjs +19 -0
- package/src/runtime/shared/plugin-manifest.mjs +18 -9
- package/src/runtime/shared/pristine-execution-contract.json +9 -2
- package/src/runtime/shared/pristine-execution.mjs +28 -4
- package/src/runtime/shared/provider-accounts.mjs +125 -21
- package/src/runtime/shared/provider-api-key.mjs +33 -1
- package/src/runtime/shared/schedule-model-ref.mjs +11 -2
- package/src/runtime/shared/session-usage-totals.mjs +19 -0
- package/src/runtime/shared/sharp-electron-linux.mjs +25 -0
- package/src/runtime/shared/shell-display.mjs +9 -3
- package/src/runtime/shared/singleton-owner.mjs +36 -6
- package/src/runtime/shared/skill-mentions.d.mts +10 -0
- package/src/runtime/shared/skill-mentions.mjs +133 -0
- package/src/runtime/shared/skill-script-runtime.mjs +264 -0
- package/src/runtime/shared/statusline/statusline-segments.mjs +68 -14
- package/src/runtime/shared/tool-primitives.mjs +35 -2
- package/src/runtime/shared/tool-surface.mjs +7 -0
- package/src/runtime/shared/tool-work-units.mjs +14 -0
- package/src/runtime/shared/transcript-metadata.mjs +17 -1
- package/src/runtime/shared/turn-changes.mjs +791 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +346 -12
- package/src/runtime/shared/turn-snapshot.mjs +690 -81
- package/src/runtime/shared/turn-worktree-snapshot.mjs +185 -15
- package/src/runtime/shared/worker-requests.mjs +15 -2
- package/src/runtime/shared/worktrees/cache-cleanups.mjs +52 -0
- package/src/runtime/shared/worktrees/contract.mjs +551 -0
- package/src/runtime/shared/worktrees/disk.mjs +57 -0
- package/src/runtime/shared/worktrees/git.mjs +247 -0
- package/src/runtime/shared/worktrees/include.mjs +480 -0
- package/src/runtime/shared/worktrees/lock.mjs +46 -0
- package/src/runtime/shared/worktrees/maintenance.mjs +96 -0
- package/src/runtime/shared/worktrees/manager.mjs +1215 -0
- package/src/runtime/shared/worktrees/merge.mjs +182 -0
- package/src/runtime/shared/worktrees/owner.mjs +46 -0
- package/src/runtime/shared/worktrees/paths.mjs +62 -0
- package/src/runtime/shared/worktrees/registry.mjs +110 -0
- package/src/runtime/shared/worktrees/settings.mjs +84 -0
- package/src/runtime/shared/worktrees/slug.mjs +62 -0
- package/src/runtime/shared/worktrees/test-support.mjs +96 -0
- package/src/runtime/visualize-bridge/client.mjs +181 -0
- package/src/runtime/visualize-bridge/tool-defs.mjs +97 -0
- package/src/session-runtime/automation-agents.mjs +8 -2
- package/src/session-runtime/boot/apis.mjs +90 -0
- package/src/session-runtime/boot/config.mjs +7 -2
- package/src/session-runtime/boot/facade.mjs +21 -0
- package/src/session-runtime/boot/infrastructure.mjs +30 -18
- package/src/session-runtime/boot/session.mjs +1 -0
- package/src/session-runtime/boot/tools.mjs +4 -0
- package/src/session-runtime/config-lifecycle.mjs +10 -1
- package/src/session-runtime/conversation-import/discovery.mjs +698 -0
- package/src/session-runtime/conversation-import/service.mjs +369 -0
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +21 -6
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
- package/src/session-runtime/global-extensions.mjs +2 -2
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +27 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +32 -1
- package/src/session-runtime/internal-tool-executor.mjs +9 -1
- package/src/session-runtime/lifecycle/session-catalog.mjs +8 -1
- package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +5 -0
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +21 -3
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +16 -0
- package/src/session-runtime/mcp-glue/connect.mjs +68 -16
- package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +122 -33
- package/src/session-runtime/mcp-glue/server-input.mjs +29 -0
- package/src/session-runtime/mcp-glue.mjs +22 -2
- package/src/session-runtime/model-route/route-persist.mjs +3 -0
- package/src/session-runtime/model-route/set-route.mjs +15 -2
- package/src/session-runtime/new-session-config.mjs +3 -2
- package/src/session-runtime/notification-bus.mjs +18 -3
- package/src/session-runtime/provider-auth/accounts.mjs +33 -15
- package/src/session-runtime/provider-models/row-hydration.mjs +2 -0
- package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
- package/src/session-runtime/provider-readiness.mjs +16 -10
- package/src/session-runtime/resource-extension-sync.mjs +45 -7
- package/src/session-runtime/resource-mcp-api.mjs +121 -84
- package/src/session-runtime/resource-plugins-api.mjs +29 -41
- package/src/session-runtime/resource-scope-decorate.mjs +6 -8
- package/src/session-runtime/resource-skills-api.mjs +23 -2
- package/src/session-runtime/route-resolution.mjs +10 -1
- package/src/session-runtime/route-state.mjs +16 -2
- package/src/session-runtime/runtime-core.mjs +5 -0
- package/src/session-runtime/runtime-facade.mjs +8 -0
- package/src/session-runtime/runtime-review-api.mjs +9 -0
- package/src/session-runtime/schedule-session-run.mjs +1 -1
- package/src/session-runtime/services/agent-tool/helpers.mjs +5 -0
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +3 -0
- package/src/session-runtime/services/agent-tool/job-views/job-lookup.mjs +1 -0
- package/src/session-runtime/services/agent-tool/job-views/spawn-meta.mjs +1 -0
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +15 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +1 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +10 -2
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -7
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +2 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -0
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +1 -0
- package/src/session-runtime/services/agent-tool.mjs +1 -0
- package/src/session-runtime/services/channel-session-router.mjs +4 -1
- package/src/session-runtime/services/daemon-candidate.mjs +3 -2
- package/src/session-runtime/services/daemon-crash-capture/spawn-capture.mjs +55 -0
- package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
- package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
- package/src/session-runtime/services/provider-admin.mjs +64 -18
- package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
- package/src/session-runtime/session-create/session-options.mjs +10 -1
- package/src/session-runtime/session-turn-api.mjs +1 -0
- package/src/session-runtime/settings-api.mjs +4 -2
- package/src/session-runtime/settings-builtin-tools-api.mjs +9 -2
- package/src/session-runtime/settings-worktree-api.mjs +17 -0
- package/src/session-runtime/setup-tool/executor.mjs +23 -5
- package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +4 -1
- package/src/session-runtime/setup-tool/tool-defs.mjs +38 -4
- package/src/session-runtime/skills-api.mjs +7 -3
- package/src/session-runtime/turn/agent-ops.mjs +12 -1
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +14 -3
- package/src/session-runtime/turn/turn-run/snapshot-tracker.mjs +20 -15
- package/src/session-runtime/turn/turn-run/turn-open.mjs +4 -5
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +29 -1
- package/src/session-runtime/turn/turn-run/turn-settle.mjs +6 -1
- package/src/session-runtime/turn/turn-run.mjs +6 -7
- package/src/session-runtime/webhook-session-run.mjs +16 -4
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -3
- package/src/session-runtime/workspace/admission-gate.mjs +197 -0
- package/src/session-runtime/workspace/project-branches.mjs +136 -0
- package/src/session-runtime/workspace/transition-steps.mjs +364 -0
- package/src/session-runtime/workspace/transitions.mjs +651 -0
- package/src/session-runtime/workspace/workspace-state.mjs +112 -0
- package/src/skill-script-entry.mjs +36 -0
- package/src/standalone/channel-transport/http-routes.mjs +17 -0
- package/src/standalone/channel-transport.mjs +13 -1
- package/src/standalone/daemon-desktop-runtime.mjs +1 -0
- package/src/standalone/daemon-owner.mjs +372 -0
- package/src/standalone/daemon-stored-session-views.mjs +15 -4
- package/src/standalone/daemon.mjs +179 -8
- package/src/standalone/desktop-service-registry.mjs +20 -0
- package/src/standalone/local-session-runtime.mjs +11 -0
- package/src/standalone/session-client/attachment-calls.mjs +3 -1
- package/src/standalone/session-client/daemon-unavailable.mjs +62 -0
- package/src/standalone/session-client/lease-heartbeat.mjs +47 -0
- package/src/standalone/session-client.mjs +104 -13
- package/src/standalone/session-protocol.mjs +21 -0
- package/src/standalone/session-proxy.mjs +3 -0
- package/src/standalone/session-service/agent-tree/agent-child.mjs +4 -0
- package/src/standalone/session-service/agent-tree/agent-rehydrate.mjs +1 -0
- package/src/standalone/session-service/entries/lifecycle.mjs +6 -0
- package/src/standalone/session-service/projection/revisions.mjs +2 -0
- package/src/standalone/session-service/projection/transcript-window.mjs +149 -19
- package/src/standalone/session-service/projection/workspace.mjs +14 -0
- package/src/standalone/session-service/session-calls/session-action.mjs +17 -4
- package/src/standalone/session-transport/call-route.mjs +13 -0
- package/src/standalone/session-transport/client-registry/client-grace.mjs +45 -3
- package/src/standalone/session-transport/client-registry/registration-replays.mjs +1 -0
- package/src/standalone/session-transport/client-registry.mjs +20 -3
- package/src/standalone/session-transport/routes.mjs +79 -2
- package/src/standalone/session-transport/stack.mjs +2 -0
- package/src/standalone/session-transport.mjs +24 -1
- package/src/tui/App.jsx +9 -1
- package/src/tui/app/app-view/floating-panel.jsx +9 -1
- package/src/tui/app/app-view.jsx +6 -0
- package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
- package/src/tui/app/message-selector.mjs +2 -1
- package/src/tui/app/model-options.mjs +37 -5
- package/src/tui/app/model-picker/provider-models-list.mjs +2 -2
- package/src/tui/app/model-picker.mjs +4 -2
- package/src/tui/app/skill-mention-palette.mjs +88 -0
- package/src/tui/app/slash-palette.mjs +23 -7
- package/src/tui/app/transcript-row-estimate.mjs +11 -3
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
- package/src/tui/app/use-prompt-state.mjs +5 -0
- package/src/tui/components/Message.jsx +22 -1
- package/src/tui/components/PromptInput.jsx +21 -3
- package/src/tui/components/SlashCommandPalette.jsx +12 -3
- package/src/tui/components/TranscriptItem.jsx +9 -1
- package/src/tui/components/prompt-input/key-handler.mjs +21 -8
- package/src/tui/components/user-message-skills.mjs +38 -0
- package/src/tui/dist/index.mjs +839 -207
- package/src/tui/index.jsx +10 -2
- package/src/tui/session/compaction-view.mjs +147 -0
- package/src/tui/session/context-state.mjs +6 -0
- package/src/tui/session/initial-state.mjs +3 -0
- package/src/tui/session/queue-helpers.mjs +7 -2
- package/src/tui/session/quota-resume.mjs +2 -1
- package/src/tui/session/session-api/commands.mjs +10 -2
- package/src/tui/session/session-api/controls.mjs +4 -0
- package/src/tui/session/session-api/extensions.mjs +4 -2
- package/src/tui/session/session-api/intake/rewind.mjs +2 -1
- package/src/tui/session/session-api/integrations.mjs +17 -0
- package/src/tui/session/session-api/lifecycle/dispose.mjs +6 -1
- package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -0
- package/src/tui/session/session-api/lifecycle/resume.mjs +3 -0
- package/src/tui/session/session-api/routes.mjs +10 -1
- package/src/tui/session/session-api/settings.mjs +9 -0
- package/src/tui/session/session-api/worktree.mjs +60 -0
- package/src/tui/session/session-api.mjs +2 -0
- package/src/tui/session/session-flow/auto-clear.mjs +31 -12
- package/src/tui/session/session-flow/drain.mjs +27 -3
- package/src/tui/session/session-flow/session-reset.mjs +4 -3
- package/src/tui/session/session-flow.mjs +2 -0
- package/src/tui/session/transcript-intake.mjs +14 -5
- package/src/tui/session/tui-steering-persist.mjs +25 -9
- package/src/tui/session/turn-stream.mjs +32 -4
- package/src/tui/session/turn-tool-cards.mjs +10 -2
- package/src/tui/session/turn.mjs +56 -10
- package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
- package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
* unless a durable turn checkpoint exists; only then does it lazily enter the
|
|
7
7
|
* reconnect recovery boundary so restored panes never paint a stale prompt.
|
|
8
8
|
*/
|
|
9
|
-
import { readdirSync } from 'node:fs';
|
|
10
9
|
import { readFile } from 'node:fs/promises';
|
|
11
10
|
import { join } from 'node:path';
|
|
12
11
|
// Leaf helpers only (no store.mjs, no workers, no config): the three-way
|
|
@@ -14,8 +13,15 @@ import { join } from 'node:path';
|
|
|
14
13
|
// authoritative store uses, so the cold catalog cannot disagree with it.
|
|
15
14
|
import { probePath, readTextFile, PROBE_PRESENT, PROBE_ABSENT } from './store/fs-probe.mjs';
|
|
16
15
|
import { readTopLevelLifecycleRecord, isLifecycleUnreadable } from './lifecycle-scan.mjs';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
16
|
+
import { sessionVisibility } from './store-summary-visibility.mjs';
|
|
17
|
+
import {
|
|
18
|
+
STAMP_ABSENT_CODES,
|
|
19
|
+
createOrdinaryCatalog,
|
|
20
|
+
indexListing,
|
|
21
|
+
observeStamp,
|
|
22
|
+
scanSessionFiles,
|
|
23
|
+
stampedDerivation,
|
|
24
|
+
} from './store-summary-catalog.mjs';
|
|
19
25
|
import { createStoredTranscriptCache, nextProjectionStamp } from './store-transcript-cache.mjs';
|
|
20
26
|
import {
|
|
21
27
|
OFFLOAD_MIN_CHARS,
|
|
@@ -24,8 +30,11 @@ import {
|
|
|
24
30
|
} from './store-transcript-worker.mjs';
|
|
25
31
|
import { projectStoredTranscript } from './store-transcript-projection.mjs';
|
|
26
32
|
import { dataDir, sessionHeartbeatMtimes } from './store-summary-locations.mjs';
|
|
27
|
-
import {
|
|
33
|
+
import { isStoredSessionId, workspaceSummary } from './store-summary-fields.mjs';
|
|
28
34
|
import { readArchivedAgentResult } from './store-summary-archived-agent.mjs';
|
|
35
|
+
import { compactionArchivePath, readCompactionArchivePage } from './compaction-archive.mjs';
|
|
36
|
+
import { currentSessionCatalogDb, onSessionCatalogDbChange, sessionCatalogDb } from './catalog-db/index.mjs';
|
|
37
|
+
import { decodeCatalogCursor, encodeCatalogCursor, isAfterCursor } from './catalog-db/schema.mjs';
|
|
29
38
|
import {
|
|
30
39
|
readCanonicalSessionRecord as readCanonicalLifecycle,
|
|
31
40
|
offerStrictSessionRecord,
|
|
@@ -64,328 +73,120 @@ export function storedTranscriptCacheStats() {
|
|
|
64
73
|
return storedTranscriptCache.stats();
|
|
65
74
|
}
|
|
66
75
|
|
|
67
|
-
// ──
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
const stampedDerivations = new Map(); // `${kind}|${path}` → { stamp, value }
|
|
76
|
+
// ── Ordinary catalog ────────────────────────────────────────────────────────
|
|
77
|
+
// The JSON path is the catalog engine of store-summary-catalog.mjs on this
|
|
78
|
+
// thread: every process but the daemon, and the daemon whenever its session
|
|
79
|
+
// catalog DB cannot answer (gate, off-switch, backfill, rebuild, any error).
|
|
80
|
+
// In the daemon, the catalog DB worker runs the same engine off this thread
|
|
81
|
+
// and an ordinary listing is one request to it (catalog-db/index.mjs).
|
|
82
|
+
const ordinaryCatalog = createOrdinaryCatalog();
|
|
83
|
+
const catalogListeners = new Set();
|
|
76
84
|
|
|
77
|
-
function
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** `{ state, value }`: `derive(text)` for the record at `path`, or its cached
|
|
86
|
-
* result while the file is provably unchanged. */
|
|
87
|
-
function stampedDerivation(kind, path, derive) {
|
|
88
|
-
const key = `${kind}|${path}`;
|
|
89
|
-
const before = observeStamp(path);
|
|
90
|
-
const cached = stampedDerivations.get(key);
|
|
91
|
-
if (before && cached && sameSessionStamp(cached.stamp, before)) {
|
|
92
|
-
stampedDerivations.delete(key);
|
|
93
|
-
stampedDerivations.set(key, cached);
|
|
94
|
-
return { state: PROBE_PRESENT, value: cached.value };
|
|
95
|
-
}
|
|
96
|
-
stampedDerivations.delete(key);
|
|
97
|
-
const read = readTextFile(path);
|
|
98
|
-
if (read.state !== PROBE_PRESENT) return { state: read.state, value: null };
|
|
99
|
-
const value = derive(read.text);
|
|
100
|
-
if (isSettledSessionStamp(before) && sameSessionStamp(before, observeStamp(path))) {
|
|
101
|
-
stampedDerivations.set(key, { stamp: before, value });
|
|
102
|
-
if (stampedDerivations.size > STAMPED_DERIVATION_LIMIT) {
|
|
103
|
-
stampedDerivations.delete(stampedDerivations.keys().next().value);
|
|
85
|
+
function notifyCatalogListeners() {
|
|
86
|
+
for (const listener of catalogListeners) {
|
|
87
|
+
try {
|
|
88
|
+
listener();
|
|
89
|
+
} catch {
|
|
90
|
+
/* a listener's failure is its own */
|
|
104
91
|
}
|
|
105
92
|
}
|
|
106
|
-
return { state: PROBE_PRESENT, value };
|
|
107
93
|
}
|
|
108
94
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
stampedDerivations.clear();
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const SESSION_SUMMARY_INDEX_VERSION = 2;
|
|
115
|
-
|
|
116
|
-
// Mirror of lifecycle-api.mjs listLeadSessions visibility: the cold catalog
|
|
117
|
-
// must never surface worker/agent dispatches (memory ingest chunks, judges,
|
|
118
|
-
// spawned agents) — the authoritative engine excludes them, so a click on
|
|
119
|
-
// such a row dead-ends in "Session is not available." (user report).
|
|
120
|
-
const LEAD_OWNERS = new Set(['cli', 'user', 'mixdog']);
|
|
95
|
+
ordinaryCatalog.subscribe(notifyCatalogListeners);
|
|
96
|
+
onSessionCatalogDbChange(notifyCatalogListeners);
|
|
121
97
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
// Mirror listLeadSessions: a previewless zero-message row is an unusable
|
|
129
|
-
// scratch (desktop boot leftovers, crashed first turns) — resuming it
|
|
130
|
-
// shows an empty conversation, so the catalog hides it.
|
|
131
|
-
if (!row.preview && row.messageCount === 0) return false;
|
|
132
|
-
const sourceType = String(row.sourceType || '')
|
|
133
|
-
.trim()
|
|
134
|
-
.toLowerCase();
|
|
135
|
-
const sourceName = String(row.sourceName || '')
|
|
136
|
-
.trim()
|
|
137
|
-
.toLowerCase();
|
|
138
|
-
const agent = String(row.agent || '')
|
|
139
|
-
.trim()
|
|
140
|
-
.toLowerCase();
|
|
141
|
-
return (
|
|
142
|
-
agent === 'lead' ||
|
|
143
|
-
sourceType === 'lead' ||
|
|
144
|
-
sourceType === 'cli' ||
|
|
145
|
-
sourceType === 'schedule' ||
|
|
146
|
-
sourceType === 'webhook' ||
|
|
147
|
-
(!sourceType && !sourceName && owner !== 'agent')
|
|
148
|
-
);
|
|
98
|
+
/** `listener()` runs when a background pass finds a session file that changed
|
|
99
|
+
* without a directory or index change, i.e. when an ordinary listing would
|
|
100
|
+
* now differ from the last one. Returns the unsubscribe function. */
|
|
101
|
+
export function subscribeStoredSessionCatalogChanges(listener) {
|
|
102
|
+
catalogListeners.add(listener);
|
|
103
|
+
return () => catalogListeners.delete(listener);
|
|
149
104
|
}
|
|
150
105
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
.replace(/<mcp-instructions>[\s\S]*?<\/mcp-instructions>/gi, ' ')
|
|
156
|
-
// Session-context envelope (mirror of session-text.mjs
|
|
157
|
-
// stripSessionDisplayEnvelope): the "# Session / Cwd / Model /
|
|
158
|
-
// Workflow" header must never become a Recent title.
|
|
159
|
-
.replace(/^\s*# Session\r?\n(?:(?:Cwd|Model|Workflow):[^\r\n]*(?:\r?\n|$))+(?:\r?\n)?/i, ' ')
|
|
160
|
-
.replace(/^\s*#\s*Session\s+Cwd:\s+\S+(?:\s+Model:[^\r\n]*?)?(?:\s+Workflow:\s+\S+)?\s*/i, ' ')
|
|
161
|
-
.replace(/\s+/g, ' ')
|
|
162
|
-
.trim()
|
|
163
|
-
.slice(0, maximum)
|
|
164
|
-
);
|
|
106
|
+
/** Run a background verification of the ordinary catalog now (or join the
|
|
107
|
+
* running one) — here and in the catalog DB worker; resolves when done. */
|
|
108
|
+
export async function verifyStoredSessionCatalog() {
|
|
109
|
+
await Promise.all([ordinaryCatalog.verify(), currentSessionCatalogDb(dataDir())?.verifyFiles()]);
|
|
165
110
|
}
|
|
166
111
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return content
|
|
171
|
-
.map((part) => (typeof part === 'string' ? part : String(part?.text || part?.content || '')))
|
|
172
|
-
.filter(Boolean)
|
|
173
|
-
.join(' ');
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function normalizedRow(row, heartbeatAt = 0) {
|
|
177
|
-
if (!row || typeof row.id !== 'string' || !isStoredSessionId(row.id)) return null;
|
|
178
|
-
return {
|
|
179
|
-
id: row.id,
|
|
180
|
-
updatedAt: positiveNumber(row.updatedAt, 0),
|
|
181
|
-
// Conversation-activity timestamp (mirror of listLeadSessions):
|
|
182
|
-
// detach/resume bookkeeping bumps updatedAt in bulk on restarts, so
|
|
183
|
-
// Recent must order by lastUsedAt or every restart reshuffles rows.
|
|
184
|
-
lastUsedAt: positiveNumber(row.lastUsedAt, 0),
|
|
185
|
-
createdAt: positiveNumber(row.createdAt, 0),
|
|
186
|
-
lastHeartbeatAt: positiveNumber(row.lastHeartbeatAt, 0),
|
|
187
|
-
// Liveness comes from the .hb sidecar mtime alone: stored row fields
|
|
188
|
-
// (summary index / final session save) survive completion and must not
|
|
189
|
-
// keep the desktop working indicator on after the sidecar is deleted.
|
|
190
|
-
heartbeatAt: positiveNumber(heartbeatAt, 0),
|
|
191
|
-
closed: row.closed === true,
|
|
192
|
-
status: String(row.status || (row.closed === true ? 'closed' : 'idle')),
|
|
193
|
-
owner: row.owner || 'user',
|
|
194
|
-
agent: row.agent || null,
|
|
195
|
-
sourceType: row.sourceType || null,
|
|
196
|
-
sourceName: row.sourceName || null,
|
|
197
|
-
scopeKey: row.scopeKey || null,
|
|
198
|
-
ownerSessionId: row.ownerSessionId || row.parentSessionId || null,
|
|
199
|
-
visibility: sessionVisibility(row),
|
|
200
|
-
clientHostPid: positiveNumber(row.clientHostPid, 0) || null,
|
|
201
|
-
cwd: row.cwd || '',
|
|
202
|
-
desktopSession: desktopSession(row.desktopSession, row.cwd),
|
|
203
|
-
provider: row.provider || null,
|
|
204
|
-
model: row.model || null,
|
|
205
|
-
agentTag: row.agentTag || null,
|
|
206
|
-
task_id: row.task_id || row.taskId || null,
|
|
207
|
-
permission: row.permission || null,
|
|
208
|
-
toolPermission: row.toolPermission || null,
|
|
209
|
-
messageCount: Math.max(0, Math.floor(Number(row.messageCount) || 0)),
|
|
210
|
-
title: cleanText(row.title, 100),
|
|
211
|
-
preview: cleanText(row.preview),
|
|
212
|
-
generation: typeof row.generation === 'number' ? row.generation : 0,
|
|
213
|
-
storageMtimeMs: positiveNumber(row.storageMtimeMs, 0),
|
|
214
|
-
storageSize: positiveNumber(row.storageSize, 0),
|
|
215
|
-
detachedReason: row.detachedReason || null,
|
|
216
|
-
};
|
|
112
|
+
/** Test seam: forget the ordinary catalog and the parsed summary index. */
|
|
113
|
+
export function clearStoredSessionCatalog() {
|
|
114
|
+
ordinaryCatalog.clear();
|
|
217
115
|
}
|
|
218
116
|
|
|
219
|
-
function
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (!heartbeatAt || !ownerSessionId || (owner !== 'agent' && (!agent || agent === 'lead'))) continue;
|
|
231
|
-
agentHeartbeatByOwner.set(ownerSessionId, Math.max(agentHeartbeatByOwner.get(ownerSessionId) || 0, heartbeatAt));
|
|
117
|
+
export function listStoredSessionSummaries(options = {}) {
|
|
118
|
+
// The index is consulted on EVERY path: its rows are the authority that
|
|
119
|
+
// must be retained whenever storage cannot be enumerated or an individual
|
|
120
|
+
// session file cannot be read.
|
|
121
|
+
const index = ordinaryCatalog.loadIndex();
|
|
122
|
+
// An index that EXISTS but is unreadable (EACCES/EIO) is neither missing
|
|
123
|
+
// nor empty: the scan still runs, and when IT cannot enumerate either, the
|
|
124
|
+
// catalog reports nothing rather than inventing an empty truth.
|
|
125
|
+
if (options.refreshFromStorage === true) {
|
|
126
|
+
const heartbeatMtimes = sessionHeartbeatMtimes();
|
|
127
|
+
return scanSessionFiles(heartbeatMtimes, index.rowsById, true) ?? indexListing(index, heartbeatMtimes) ?? [];
|
|
232
128
|
}
|
|
233
|
-
return
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
return agentHeartbeatAt > 0 ? { ...row, agentHeartbeatAt } : row;
|
|
238
|
-
});
|
|
129
|
+
if (options.rebuildIfMissing === false) return indexListing(index, sessionHeartbeatMtimes()) ?? [];
|
|
130
|
+
// A missing/malformed index leaves every file to be parsed (once per
|
|
131
|
+
// stamp); a present one lets unchanged files reuse their durable summary.
|
|
132
|
+
return ordinaryCatalog.refresh(index).rows ?? indexListing(index, sessionHeartbeatMtimes()) ?? [];
|
|
239
133
|
}
|
|
240
134
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
messageText(message.content)
|
|
252
|
-
)
|
|
253
|
-
)
|
|
254
|
-
.map((message) => cleanText(messageText(message.content)))
|
|
255
|
-
.find(Boolean) || '';
|
|
256
|
-
return normalizedRow(
|
|
257
|
-
{
|
|
258
|
-
...session,
|
|
259
|
-
messageCount: messages.filter((message) => message?.role === 'user' || message?.role === 'assistant').length,
|
|
260
|
-
preview,
|
|
261
|
-
},
|
|
262
|
-
heartbeatAt
|
|
263
|
-
);
|
|
135
|
+
/** The catalog DB's answer to an ordinary listing (`{ rows, more }`), or
|
|
136
|
+
* null when this process has none or it cannot answer. No filesystem work
|
|
137
|
+
* on this thread: the worker checks storage, applies changes, answers. */
|
|
138
|
+
async function catalogDbListing(page) {
|
|
139
|
+
const db = sessionCatalogDb(dataDir());
|
|
140
|
+
if (!db) return null;
|
|
141
|
+
const answer = await db.list(page);
|
|
142
|
+
// While the DB answers, the JSON path's reconcile state is dead weight.
|
|
143
|
+
if (answer) ordinaryCatalog.clear();
|
|
144
|
+
return answer;
|
|
264
145
|
}
|
|
265
146
|
|
|
266
147
|
/**
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
* durable summary and a single UNREADABLE file retains its last known row
|
|
273
|
-
* instead of vanishing.
|
|
148
|
+
* listStoredSessionSummaries for async callers (the daemon's session views).
|
|
149
|
+
* In the daemon the ordinary listing comes from the session catalog DB
|
|
150
|
+
* worker; anywhere else, and whenever the DB cannot answer, it is the JSON
|
|
151
|
+
* path. `refreshFromStorage` and `rebuildIfMissing: false` always take the
|
|
152
|
+
* JSON/file path.
|
|
274
153
|
*/
|
|
275
|
-
function
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
{ forceRead = false } = {}
|
|
279
|
-
) {
|
|
280
|
-
const directory = join(dataDir(), 'sessions');
|
|
281
|
-
const dirProbe = probePath(directory);
|
|
282
|
-
if (dirProbe.state === PROBE_ABSENT) return [];
|
|
283
|
-
if (dirProbe.state !== PROBE_PRESENT) return null;
|
|
284
|
-
let entries;
|
|
285
|
-
try {
|
|
286
|
-
entries = readdirSync(directory);
|
|
287
|
-
} catch {
|
|
288
|
-
return null;
|
|
289
|
-
}
|
|
290
|
-
const rows = [];
|
|
291
|
-
for (const filename of entries) {
|
|
292
|
-
if (!filename.endsWith('.json')) continue;
|
|
293
|
-
const storageId = filename.slice(0, -5);
|
|
294
|
-
const path = join(directory, filename);
|
|
295
|
-
const retained = indexRowsById.get(storageId);
|
|
296
|
-
const probe = probePath(path);
|
|
297
|
-
// Provably gone: the row legitimately disappears with the file.
|
|
298
|
-
if (probe.state === PROBE_ABSENT) continue;
|
|
299
|
-
if (probe.state !== PROBE_PRESENT) {
|
|
300
|
-
if (retained) rows.push(retained);
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
if (!forceRead && retained) {
|
|
304
|
-
const exactFingerprint =
|
|
305
|
-
retained.storageMtimeMs > 0 &&
|
|
306
|
-
retained.storageSize > 0 &&
|
|
307
|
-
retained.storageMtimeMs === probe.mtimeMs &&
|
|
308
|
-
retained.storageSize === probe.size;
|
|
309
|
-
if (exactFingerprint) {
|
|
310
|
-
rows.push(retained);
|
|
311
|
-
continue;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
// A file the index does not describe (never saved by the store, or
|
|
315
|
-
// saved after the index was written) is parsed once per full stamp.
|
|
316
|
-
const read = stampedDerivation('row', path, (text) => {
|
|
317
|
-
// Same strict authority as the store: a duplicate/ambiguous top-level
|
|
318
|
-
// record or a foreign identity is not this file's session.
|
|
319
|
-
const record = readTopLevelLifecycleRecord(text);
|
|
320
|
-
if (isLifecycleUnreadable(record) || record.id !== storageId) return null;
|
|
321
|
-
return rowFromSession(record.doc, 0);
|
|
322
|
-
});
|
|
323
|
-
// Provably gone: the row legitimately disappears with the file.
|
|
324
|
-
if (read.state === PROBE_ABSENT) continue;
|
|
325
|
-
if (read.state !== PROBE_PRESENT) {
|
|
326
|
-
// Exists and could not be read (EACCES/EIO): retain the last known
|
|
327
|
-
// row rather than let a transient IO error delete it from view.
|
|
328
|
-
if (retained) rows.push(retained);
|
|
329
|
-
continue;
|
|
330
|
-
}
|
|
331
|
-
if (read.value) {
|
|
332
|
-
rows.push({
|
|
333
|
-
...structuredClone(read.value),
|
|
334
|
-
heartbeatAt: positiveNumber(heartbeatMtimes.get(storageId), 0),
|
|
335
|
-
storageMtimeMs: probe.mtimeMs,
|
|
336
|
-
storageSize: probe.size,
|
|
337
|
-
});
|
|
338
|
-
}
|
|
154
|
+
export async function listStoredSessionSummariesAsync(options = {}) {
|
|
155
|
+
if (options.refreshFromStorage === true || options.rebuildIfMissing === false) {
|
|
156
|
+
return listStoredSessionSummaries(options);
|
|
339
157
|
}
|
|
340
|
-
|
|
158
|
+
const answer = await catalogDbListing(null);
|
|
159
|
+
return answer ? answer.rows : listStoredSessionSummaries();
|
|
341
160
|
}
|
|
342
161
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
return (right.lastUsedAt || right.updatedAt || 0) - (left.lastUsedAt || left.updatedAt || 0);
|
|
346
|
-
}
|
|
162
|
+
const DEFAULT_PAGE_LIMIT = 50;
|
|
163
|
+
const MAX_PAGE_LIMIT = 1000;
|
|
347
164
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const
|
|
359
|
-
const
|
|
360
|
-
if (
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
.map((row) => normalizedRow(row, heartbeatMtimes.get(row?.id) || 0))
|
|
366
|
-
.filter(Boolean);
|
|
367
|
-
for (const row of normalizedRows) indexRowsById.set(row.id, row);
|
|
368
|
-
indexRows = leadRowsWithAgentHeartbeat(normalizedRows).sort(byRecentActivity);
|
|
369
|
-
}
|
|
370
|
-
} catch {
|
|
371
|
-
/* malformed sidecar: the files below are the authority */
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
// An index that EXISTS but is unreadable (EACCES/EIO) is neither missing
|
|
375
|
-
// nor empty: the scan below still runs, and when IT cannot enumerate
|
|
376
|
-
// either, the catalog reports nothing rather than inventing an empty truth.
|
|
377
|
-
const scan = (forceRead = false) => scanSessionFiles(heartbeatMtimes, indexRowsById, { forceRead });
|
|
378
|
-
if (options.refreshFromStorage === true) return scan(true) ?? indexRows ?? [];
|
|
379
|
-
if (indexRows) {
|
|
380
|
-
if (options.rebuildIfMissing === false) return indexRows;
|
|
381
|
-
// Enumerate identities to detect deletions, stat known files, and parse
|
|
382
|
-
// only rows newer than the index. Cold-start cost therefore scales with
|
|
383
|
-
// changed transcripts rather than total transcript bytes.
|
|
384
|
-
return scan(false) ?? indexRows;
|
|
165
|
+
/**
|
|
166
|
+
* One page of the ordinary listing: `{ sessions, nextCursor }` in catalog
|
|
167
|
+
* order (newest activity first, ties by id), `limit` rows (default 50, at
|
|
168
|
+
* most 1000) after the keyset `cursor` (an earlier page's `nextCursor`;
|
|
169
|
+
* absent = first page). Answered like listStoredSessionSummariesAsync; from
|
|
170
|
+
* the DB only the requested rows are transferred.
|
|
171
|
+
*/
|
|
172
|
+
export async function listStoredSessionSummaryPage(options = {}) {
|
|
173
|
+
const requested = Math.floor(Number(options.limit));
|
|
174
|
+
const limit = requested > 0 ? Math.min(requested, MAX_PAGE_LIMIT) : DEFAULT_PAGE_LIMIT;
|
|
175
|
+
const after = decodeCatalogCursor(options.cursor);
|
|
176
|
+
const answer = await catalogDbListing({ limit, after });
|
|
177
|
+
if (answer) {
|
|
178
|
+
return {
|
|
179
|
+
sessions: answer.rows,
|
|
180
|
+
nextCursor: answer.more && answer.rows.length ? encodeCatalogCursor(answer.rows.at(-1)) : null,
|
|
181
|
+
};
|
|
385
182
|
}
|
|
386
|
-
//
|
|
387
|
-
|
|
388
|
-
|
|
183
|
+
// The JSON listing is already in catalog order.
|
|
184
|
+
const ordered = listStoredSessionSummaries().filter((row) => isAfterCursor(row, after));
|
|
185
|
+
const sessions = ordered.slice(0, limit);
|
|
186
|
+
return {
|
|
187
|
+
sessions,
|
|
188
|
+
nextCursor: ordered.length > limit ? encodeCatalogCursor(sessions.at(-1)) : null,
|
|
189
|
+
};
|
|
389
190
|
}
|
|
390
191
|
|
|
391
192
|
/** Exact, fail-closed existence check for a durable session address.
|
|
@@ -426,11 +227,13 @@ function storedSessionMetadata(sessionId, session) {
|
|
|
426
227
|
visibility: sessionVisibility(session),
|
|
427
228
|
agentTag: session.agentTag || null,
|
|
428
229
|
cwd: session.cwd || '',
|
|
230
|
+
...(workspaceSummary(session.workspace) ? { workspace: workspaceSummary(session.workspace) } : {}),
|
|
429
231
|
provider: session.provider || null,
|
|
430
232
|
model: session.model || null,
|
|
431
233
|
presetName: session.presetName || session.profileId || null,
|
|
432
234
|
effort: session.effort || null,
|
|
433
235
|
fast: session.fast === true,
|
|
236
|
+
...(session.autoEffort === false ? { autoEffort: false } : {}),
|
|
434
237
|
modelParameters: session.modelParameters || null,
|
|
435
238
|
taskType: session.taskType || null,
|
|
436
239
|
permission: session.permission || null,
|
|
@@ -452,11 +255,12 @@ function storedTranscriptInputPaths(sessionId) {
|
|
|
452
255
|
return {
|
|
453
256
|
recordPath: join(dataDir(), 'sessions', `${sessionId}.json`),
|
|
454
257
|
checkpointPath: join(dataDir(), 'turn-checkpoints', `${sessionId}.json`),
|
|
258
|
+
// Written after the compaction commit that names it, so its identity
|
|
259
|
+
// joins the projection's.
|
|
260
|
+
archivePath: compactionArchivePath(join(dataDir(), 'sessions'), sessionId),
|
|
455
261
|
};
|
|
456
262
|
}
|
|
457
263
|
|
|
458
|
-
const STAMP_ABSENT_CODES = new Set(['ENOENT', 'ENOTDIR']);
|
|
459
|
-
|
|
460
264
|
/** Settled identity of the files readStoredSessionTranscript projects from:
|
|
461
265
|
* the canonical record and its turn-checkpoint sidecar. Null while either is
|
|
462
266
|
* too fresh to vouch for by stat alone (isSettledSessionStamp), unreadable,
|
|
@@ -464,7 +268,7 @@ const STAMP_ABSENT_CODES = new Set(['ENOENT', 'ENOTDIR']);
|
|
|
464
268
|
export function storedSessionTranscriptStamp(id) {
|
|
465
269
|
const sessionId = String(id || '').trim();
|
|
466
270
|
if (!isStoredSessionId(sessionId)) return null;
|
|
467
|
-
const { recordPath, checkpointPath } = storedTranscriptInputPaths(sessionId);
|
|
271
|
+
const { recordPath, checkpointPath, archivePath } = storedTranscriptInputPaths(sessionId);
|
|
468
272
|
const settled = (path, absent) => {
|
|
469
273
|
let stamp;
|
|
470
274
|
try {
|
|
@@ -477,7 +281,74 @@ export function storedSessionTranscriptStamp(id) {
|
|
|
477
281
|
};
|
|
478
282
|
const record = settled(recordPath, null);
|
|
479
283
|
const checkpoint = record && settled(checkpointPath, 'absent');
|
|
480
|
-
|
|
284
|
+
const archive = checkpoint && settled(archivePath, 'absent');
|
|
285
|
+
return archive ? `${record}|${checkpoint}|${archive}` : null;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** Projects the strictly parsed record `text` (read at full stamp `stamp`, or
|
|
289
|
+
* null when not provably stable) into the cached transcript value. */
|
|
290
|
+
async function produceStoredTranscript(text, stamp, context) {
|
|
291
|
+
const { sessionId, recordPath, itemLimit, includeMessages, mode, offloadable, checkpointAbsent, timing } = context;
|
|
292
|
+
// A large record with no checkpoint sidecar parses and projects on a
|
|
293
|
+
// worker thread: the same pipeline, off the daemon event loop. It hands
|
|
294
|
+
// no parse to the load cache (a later resume reads the file itself).
|
|
295
|
+
if (offloadable && text.length >= OFFLOAD_MIN_CHARS) {
|
|
296
|
+
try {
|
|
297
|
+
timing.worker = storedTranscriptWorkerRunning() ? 'warm' : 'cold';
|
|
298
|
+
const workerStartedAt = performance.now();
|
|
299
|
+
const answer = await projectStoredTranscriptOffThread({ sessionId, text, itemLimit });
|
|
300
|
+
timing.workerMs = performance.now() - workerStartedAt;
|
|
301
|
+
Object.assign(timing, answer.timing);
|
|
302
|
+
if (answer.unreadable) return null;
|
|
303
|
+
if (stamp) readCanonicalLifecycle.rememberStrictVerdict(recordPath, stamp, answer.lifecycle);
|
|
304
|
+
// Stamps are process-unique per module realm; re-issue in this one.
|
|
305
|
+
answer.value.projectionStamp = nextProjectionStamp();
|
|
306
|
+
return answer.value;
|
|
307
|
+
} catch {
|
|
308
|
+
// Worker unavailable: fall through to the in-process pipeline.
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const parseStartedAt = performance.now();
|
|
312
|
+
const record = readTopLevelLifecycleRecord(text);
|
|
313
|
+
timing.parseMs = performance.now() - parseStartedAt;
|
|
314
|
+
if (isLifecycleUnreadable(record) || record.id !== sessionId) return null;
|
|
315
|
+
// The same strict verdict the lifecycle authority would reach for these
|
|
316
|
+
// bytes (storedSessionExists runs next when the pane's runtime loads).
|
|
317
|
+
if (stamp) readCanonicalLifecycle.rememberStrictVerdict(recordPath, stamp, record);
|
|
318
|
+
// Hand the parse to the session load cache BEFORE projecting: a resume
|
|
319
|
+
// that runs while this projection is still being built (the daemon
|
|
320
|
+
// resumes the panes it restores) claims it instead of reading the file
|
|
321
|
+
// again. The projection path loads the store anyway, so loading its
|
|
322
|
+
// cache first costs nothing and gives the hand-off somewhere to go on
|
|
323
|
+
// the process's first read.
|
|
324
|
+
let handedOver = false;
|
|
325
|
+
if (stamp) {
|
|
326
|
+
await import('./store/load-cache.mjs');
|
|
327
|
+
handedOver = offerStrictSessionRecord(recordPath, stamp, record, text.length);
|
|
328
|
+
}
|
|
329
|
+
// Once handed over, the record is a runtime's mutable session. The
|
|
330
|
+
// projection is then built from a private parse of the same bytes, so
|
|
331
|
+
// nothing it retains aliases that session and it needs no copying. With
|
|
332
|
+
// a turn checkpoint the projection recovers through loadSession, the very
|
|
333
|
+
// load the hand-off serves, and projects the load cache's session
|
|
334
|
+
// exactly as before; only that projection is detached.
|
|
335
|
+
const projectStartedAt = performance.now();
|
|
336
|
+
const value = await projectStoredTranscript(
|
|
337
|
+
sessionId,
|
|
338
|
+
handedOver && checkpointAbsent ? JSON.parse(text) : record.doc,
|
|
339
|
+
{
|
|
340
|
+
itemLimit,
|
|
341
|
+
includeMessages,
|
|
342
|
+
checkpointAbsent,
|
|
343
|
+
}
|
|
344
|
+
);
|
|
345
|
+
timing.projectMs = performance.now() - projectStartedAt;
|
|
346
|
+
if (!handedOver || checkpointAbsent || !value || mode !== 'items') return value;
|
|
347
|
+
try {
|
|
348
|
+
return structuredClone(value);
|
|
349
|
+
} catch {
|
|
350
|
+
return value;
|
|
351
|
+
}
|
|
481
352
|
}
|
|
482
353
|
|
|
483
354
|
/** Read exactly one persisted session for a visible desktop pane. This never
|
|
@@ -489,8 +360,13 @@ export async function readStoredSessionTranscript(id, options = {}) {
|
|
|
489
360
|
if (!isStoredSessionId(sessionId)) return null;
|
|
490
361
|
// Same strict authority as the store, and the same fail-closed rule: an
|
|
491
362
|
// absent, unreadable, ambiguous or foreign record yields no transcript.
|
|
492
|
-
const { recordPath, checkpointPath } = storedTranscriptInputPaths(sessionId);
|
|
363
|
+
const { recordPath, checkpointPath, archivePath } = storedTranscriptInputPaths(sessionId);
|
|
493
364
|
if (options.metadataOnly === true) return readStoredSessionMetadata(recordPath, sessionId);
|
|
365
|
+
// One page of the display-only compaction archive (the live head loader
|
|
366
|
+
// pages it once a runtime's in-memory rows are exhausted).
|
|
367
|
+
if (options.compactionArchive && typeof options.compactionArchive === 'object') {
|
|
368
|
+
return readCompactionArchivePage(join(dataDir(), 'sessions'), sessionId, options.compactionArchive);
|
|
369
|
+
}
|
|
494
370
|
// The checkpoint sidecar shapes the projection as much as the record does,
|
|
495
371
|
// so its identity joins the cache fingerprint. Only a PROVABLY absent
|
|
496
372
|
// checkpoint skips recovery: an unreadable probe must not silently
|
|
@@ -502,6 +378,7 @@ export async function readStoredSessionTranscript(id, options = {}) {
|
|
|
502
378
|
if (recordStat.state === PROBE_ABSENT) return readArchivedAgentResult(sessionId);
|
|
503
379
|
if (recordStat.state !== PROBE_PRESENT) return null;
|
|
504
380
|
const checkpoint = probePath(checkpointPath);
|
|
381
|
+
const archive = probePath(archivePath);
|
|
505
382
|
const requestedLimit = Number(options.transcriptItemLimit);
|
|
506
383
|
const itemLimit = Number.isFinite(requestedLimit) && requestedLimit > 0 ? requestedLimit : Number.POSITIVE_INFINITY;
|
|
507
384
|
let readState = PROBE_PRESENT;
|
|
@@ -510,10 +387,21 @@ export async function readStoredSessionTranscript(id, options = {}) {
|
|
|
510
387
|
let readStamp = null;
|
|
511
388
|
// The strict record parse (full JSON + duplicate-key scan) is itself a
|
|
512
389
|
// large share of a cold read, so it only runs when the content is new.
|
|
513
|
-
const
|
|
390
|
+
const includeMessages = options.includeMessages === true;
|
|
391
|
+
const mode = includeMessages ? 'messages' : 'items';
|
|
392
|
+
const checkpointAbsent = checkpoint.state === PROBE_ABSENT;
|
|
514
393
|
// Large, checkpoint-free item projections run off the daemon event loop.
|
|
515
|
-
const offloadable =
|
|
516
|
-
|
|
394
|
+
const offloadable = !includeMessages && checkpointAbsent && recordStat.size >= OFFLOAD_MIN_CHARS;
|
|
395
|
+
const produceContext = {
|
|
396
|
+
sessionId,
|
|
397
|
+
recordPath,
|
|
398
|
+
itemLimit,
|
|
399
|
+
includeMessages,
|
|
400
|
+
mode,
|
|
401
|
+
offloadable,
|
|
402
|
+
checkpointAbsent,
|
|
403
|
+
timing,
|
|
404
|
+
};
|
|
517
405
|
const { value, hit, read } = await storedTranscriptCache.read({
|
|
518
406
|
key: `${sessionId}|${itemLimit}|${mode}`,
|
|
519
407
|
// Growing history windows of one session replace each other.
|
|
@@ -525,6 +413,9 @@ export async function readStoredSessionTranscript(id, options = {}) {
|
|
|
525
413
|
checkpoint.size,
|
|
526
414
|
checkpoint.ino,
|
|
527
415
|
checkpoint.dev,
|
|
416
|
+
archive.state,
|
|
417
|
+
archive.mtimeMs,
|
|
418
|
+
archive.size,
|
|
528
419
|
].join(':'),
|
|
529
420
|
fileStat: recordStat,
|
|
530
421
|
loadText: async () => {
|
|
@@ -547,70 +438,7 @@ export async function readStoredSessionTranscript(id, options = {}) {
|
|
|
547
438
|
readStamp = before && sameSessionStamp(before, observeStamp(recordPath)) ? before : null;
|
|
548
439
|
return body.text;
|
|
549
440
|
},
|
|
550
|
-
produce:
|
|
551
|
-
const stamp = readStamp;
|
|
552
|
-
// A large record with no checkpoint sidecar parses and projects on a
|
|
553
|
-
// worker thread: the same pipeline, off the daemon event loop. It hands
|
|
554
|
-
// no parse to the load cache (a later resume reads the file itself).
|
|
555
|
-
if (offloadable && text.length >= OFFLOAD_MIN_CHARS) {
|
|
556
|
-
try {
|
|
557
|
-
timing.worker = storedTranscriptWorkerRunning() ? 'warm' : 'cold';
|
|
558
|
-
const workerStartedAt = performance.now();
|
|
559
|
-
const answer = await projectStoredTranscriptOffThread({ sessionId, text, itemLimit });
|
|
560
|
-
timing.workerMs = performance.now() - workerStartedAt;
|
|
561
|
-
Object.assign(timing, answer.timing);
|
|
562
|
-
if (answer.unreadable) return null;
|
|
563
|
-
if (stamp) readCanonicalLifecycle.rememberStrictVerdict(recordPath, stamp, answer.lifecycle);
|
|
564
|
-
// Stamps are process-unique per module realm; re-issue in this one.
|
|
565
|
-
answer.value.projectionStamp = nextProjectionStamp();
|
|
566
|
-
return answer.value;
|
|
567
|
-
} catch {
|
|
568
|
-
// Worker unavailable: fall through to the in-process pipeline.
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
const parseStartedAt = performance.now();
|
|
572
|
-
const record = readTopLevelLifecycleRecord(text);
|
|
573
|
-
timing.parseMs = performance.now() - parseStartedAt;
|
|
574
|
-
if (isLifecycleUnreadable(record) || record.id !== sessionId) return null;
|
|
575
|
-
// The same strict verdict the lifecycle authority would reach for these
|
|
576
|
-
// bytes (storedSessionExists runs next when the pane's runtime loads).
|
|
577
|
-
if (stamp) readCanonicalLifecycle.rememberStrictVerdict(recordPath, stamp, record);
|
|
578
|
-
const checkpointAbsent = checkpoint.state === PROBE_ABSENT;
|
|
579
|
-
// Hand the parse to the session load cache BEFORE projecting: a resume
|
|
580
|
-
// that runs while this projection is still being built (the daemon
|
|
581
|
-
// resumes the panes it restores) claims it instead of reading the file
|
|
582
|
-
// again. The projection path loads the store anyway, so loading its
|
|
583
|
-
// cache first costs nothing and gives the hand-off somewhere to go on
|
|
584
|
-
// the process's first read.
|
|
585
|
-
let handedOver = false;
|
|
586
|
-
if (stamp) {
|
|
587
|
-
await import('./store/load-cache.mjs');
|
|
588
|
-
handedOver = offerStrictSessionRecord(recordPath, stamp, record, text.length);
|
|
589
|
-
}
|
|
590
|
-
// Once handed over, the record is a runtime's mutable session. The
|
|
591
|
-
// projection is then built from a private parse of the same bytes, so
|
|
592
|
-
// nothing it retains aliases that session and it needs no copying. With
|
|
593
|
-
// a turn checkpoint the projection recovers through loadSession, the very
|
|
594
|
-
// load the hand-off serves, and projects the load cache's session
|
|
595
|
-
// exactly as before; only that projection is detached.
|
|
596
|
-
const projectStartedAt = performance.now();
|
|
597
|
-
const value = await projectStoredTranscript(
|
|
598
|
-
sessionId,
|
|
599
|
-
handedOver && checkpointAbsent ? JSON.parse(text) : record.doc,
|
|
600
|
-
{
|
|
601
|
-
itemLimit,
|
|
602
|
-
includeMessages: options.includeMessages === true,
|
|
603
|
-
checkpointAbsent,
|
|
604
|
-
}
|
|
605
|
-
);
|
|
606
|
-
timing.projectMs = performance.now() - projectStartedAt;
|
|
607
|
-
if (!handedOver || checkpointAbsent || !value || mode !== 'items') return value;
|
|
608
|
-
try {
|
|
609
|
-
return structuredClone(value);
|
|
610
|
-
} catch {
|
|
611
|
-
return value;
|
|
612
|
-
}
|
|
613
|
-
},
|
|
441
|
+
produce: (text) => produceStoredTranscript(text, readStamp, produceContext),
|
|
614
442
|
});
|
|
615
443
|
// The record vanished between stat and read: same answer as an absent probe.
|
|
616
444
|
if (readState === PROBE_ABSENT) return readArchivedAgentResult(sessionId);
|