mixdog 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -5
- package/package.json +1 -1
- package/scripts/gate-local.mjs +19 -3
- package/scripts/model-catalog-audit.mjs +1 -15
- package/src/agents/reviewer/AGENT.md +3 -3
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +5 -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 +13 -3
- package/src/defaults/skills/visualize/SKILL.md +12 -9
- package/src/repl/slash-commands.mjs +16 -1
- 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-progress-watchdog.mjs +29 -4
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
- package/src/runtime/agent/orchestrator/config.mjs +10 -4
- 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 +122 -15
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +54 -11
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +104 -9
- package/src/runtime/agent/orchestrator/mcp/client.mjs +358 -140
- package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
- package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +294 -44
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
- package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +96 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +25 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +73 -35
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +8 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +53 -18
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +30 -14
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +82 -9
- 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 +447 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +38 -5
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +102 -23
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +29 -6
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +18 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +86 -19
- package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +29 -8
- package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +191 -0
- package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
- package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +21 -82
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -12
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +21 -20
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +97 -51
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +68 -19
- package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +15 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +35 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +12 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +22 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
- 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-response-normalization.mjs +23 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +94 -13
- package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +6 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +23 -14
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +26 -6
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +42 -25
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +2 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +34 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +5 -9
- package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
- package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +35 -24
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +581 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +35 -9
- package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +118 -219
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
- package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +3 -0
- 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/goal-reminder.mjs +0 -18
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +268 -181
- 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/session/catalog-db/index.mjs +284 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +270 -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/messages.mjs +6 -2
- 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.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -4
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +102 -18
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +42 -5
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +14 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +20 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +50 -23
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +22 -14
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
- package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +155 -29
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +819 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +22 -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 +183 -374
- 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 +41 -3
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +38 -17
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
- 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/builtin-tools.mjs +5 -5
- 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-range-index.mjs +173 -22
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +5 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +15 -2
- 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/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/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/shell-snapshot.mjs +4 -0
- package/src/runtime/attachments/store.d.mts +3 -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/pg/process.mjs +34 -24
- package/src/runtime/memory/lib/pg/supervisor.mjs +16 -13
- package/src/runtime/memory/lib/session-ingest.mjs +2 -2
- package/src/runtime/office/README.md +1 -1
- package/src/runtime/office/shared/values.mjs +0 -5
- package/src/runtime/shared/atomic-file.mjs +25 -4
- package/src/runtime/shared/background-tasks.mjs +92 -14
- package/src/runtime/shared/err-text.mjs +25 -0
- package/src/runtime/shared/error-presentation.mjs +159 -19
- package/src/runtime/shared/extension-scopes.mjs +5 -3
- package/src/runtime/shared/file-lock.mjs +37 -11
- package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
- package/src/runtime/shared/llm/anthropic-effort.mjs +134 -131
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
- package/src/runtime/shared/llm/model-catalog.mjs +53 -52
- package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +17 -0
- package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
- package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
- package/src/runtime/shared/model-display.mjs +13 -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 +124 -20
- package/src/runtime/shared/provider-api-key.mjs +12 -0
- package/src/runtime/shared/quota-resume-store.mjs +11 -0
- package/src/runtime/shared/session-baseline-store.mjs +960 -0
- package/src/runtime/shared/shell-display.mjs +9 -3
- 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-format.mjs +4 -4
- package/src/runtime/shared/task-notification-envelope.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +35 -2
- package/src/runtime/shared/turn-changes.mjs +447 -219
- package/src/runtime/shared/turn-snapshot-store.mjs +182 -1
- package/src/runtime/shared/turn-snapshot.mjs +170 -48
- package/src/runtime/shared/turn-worktree-snapshot.mjs +160 -51
- package/src/runtime/shared/worker-requests.mjs +15 -2
- package/src/runtime/shared/worktrees/contract.mjs +0 -24
- package/src/runtime/shared/worktrees/test-support.mjs +19 -4
- package/src/session-runtime/automation-agents.mjs +8 -2
- package/src/session-runtime/boot/apis.mjs +61 -0
- package/src/session-runtime/boot/config.mjs +5 -2
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/infrastructure.mjs +35 -18
- 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 +10 -5
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
- package/src/session-runtime/lifecycle/session-catalog.mjs +2 -0
- package/src/session-runtime/lifecycle/teardown.mjs +1 -1
- package/src/session-runtime/mcp-glue/connect.mjs +83 -16
- package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +135 -33
- package/src/session-runtime/mcp-glue.mjs +25 -2
- package/src/session-runtime/mcp-oauth-login.mjs +174 -0
- package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
- package/src/session-runtime/model-route/set-route.mjs +83 -15
- package/src/session-runtime/model-route/web-search-route.mjs +1 -1
- package/src/session-runtime/native-web-search/routes.mjs +22 -3
- package/src/session-runtime/new-session-config.mjs +3 -2
- package/src/session-runtime/notification-bus.mjs +9 -2
- package/src/session-runtime/provider-auth/accounts.mjs +33 -15
- package/src/session-runtime/provider-models/model-meta.mjs +12 -3
- 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/quick-model-rows/web-search-rows.mjs +13 -8
- package/src/session-runtime/quick-web-search-models.mjs +13 -38
- package/src/session-runtime/resource-extension-sync.mjs +24 -5
- package/src/session-runtime/resource-mcp-api.mjs +198 -78
- 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/runtime-review-api.mjs +13 -0
- package/src/session-runtime/schedule-session-run.mjs +1 -1
- package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
- package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
- package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
- package/src/session-runtime/services/channel-session-router.mjs +4 -1
- 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/settings-compaction-api.mjs +1 -0
- package/src/session-runtime/setup-tool/executor.mjs +12 -1
- package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +1 -0
- package/src/session-runtime/setup-tool/tool-defs.mjs +7 -1
- package/src/session-runtime/skills-api.mjs +7 -3
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/webhook-session-run.mjs +16 -4
- package/src/skill-script-entry.mjs +36 -0
- package/src/standalone/daemon-drain-policy.mjs +8 -0
- package/src/standalone/daemon-stored-session-views.mjs +11 -3
- package/src/standalone/daemon-telemetry.mjs +55 -0
- package/src/standalone/daemon.mjs +44 -11
- package/src/standalone/session-protocol.mjs +12 -0
- package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
- package/src/standalone/session-service/agent-tree.mjs +12 -0
- package/src/standalone/session-service/quota-resume.mjs +8 -0
- package/src/standalone/session-service/retention/busy.mjs +15 -1
- package/src/standalone/session-service/session-calls/session-action.mjs +6 -1
- package/src/standalone/session-service-api.mjs +5 -0
- package/src/standalone/session-service.mjs +1 -0
- 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 +7 -0
- package/src/tui/app/create-app-pickers.mjs +4 -0
- package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
- package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
- package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
- package/src/tui/app/extension-pickers.mjs +2 -1
- package/src/tui/app/model-options.mjs +13 -16
- package/src/tui/app/skill-mention-palette.mjs +88 -0
- package/src/tui/app/slash-commands.mjs +7 -2
- package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
- 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/StatusLine.jsx +2 -0
- package/src/tui/components/TranscriptItem.jsx +9 -1
- package/src/tui/components/prompt-input/key-handler.mjs +21 -8
- package/src/tui/components/status-line/local-line.mjs +5 -2
- package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
- package/src/tui/components/user-message-skills.mjs +38 -0
- package/src/tui/dist/index.mjs +1323 -214
- package/src/tui/session/queue-helpers.mjs +7 -2
- package/src/tui/session/quota-resume.mjs +46 -14
- package/src/tui/session/session-api/commands.mjs +33 -24
- package/src/tui/session/session-api/extensions.mjs +26 -2
- package/src/tui/session/session-api/integrations.mjs +7 -0
- package/src/tui/session/session-api/lifecycle.mjs +5 -1
- package/src/tui/session/session-api/routes.mjs +71 -7
- package/src/tui/session/session-api/settings.mjs +12 -14
- package/src/tui/session/session-flow/drain.mjs +21 -5
- package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
- package/src/tui/session/tui-steering-persist.mjs +25 -9
- package/src/tui/session/turn.mjs +55 -15
- package/src/tui/statusline-ansi-bridge.mjs +1 -1
- package/src/ui/statusline-agents.mjs +15 -4
- package/src/ui/statusline.mjs +7 -3
- package/src/ui/streaming-markdown-heal.mjs +34 -3
- package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
- package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
- package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
- package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
- package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
|
@@ -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,9 +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';
|
|
29
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';
|
|
30
38
|
import {
|
|
31
39
|
readCanonicalSessionRecord as readCanonicalLifecycle,
|
|
32
40
|
offerStrictSessionRecord,
|
|
@@ -65,331 +73,120 @@ export function storedTranscriptCacheStats() {
|
|
|
65
73
|
return storedTranscriptCache.stats();
|
|
66
74
|
}
|
|
67
75
|
|
|
68
|
-
// ──
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
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();
|
|
77
84
|
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** `{ state, value }`: `derive(text)` for the record at `path`, or its cached
|
|
87
|
-
* result while the file is provably unchanged. */
|
|
88
|
-
function stampedDerivation(kind, path, derive) {
|
|
89
|
-
const key = `${kind}|${path}`;
|
|
90
|
-
const before = observeStamp(path);
|
|
91
|
-
const cached = stampedDerivations.get(key);
|
|
92
|
-
if (before && cached && sameSessionStamp(cached.stamp, before)) {
|
|
93
|
-
stampedDerivations.delete(key);
|
|
94
|
-
stampedDerivations.set(key, cached);
|
|
95
|
-
return { state: PROBE_PRESENT, value: cached.value };
|
|
96
|
-
}
|
|
97
|
-
stampedDerivations.delete(key);
|
|
98
|
-
const read = readTextFile(path);
|
|
99
|
-
if (read.state !== PROBE_PRESENT) return { state: read.state, value: null };
|
|
100
|
-
const value = derive(read.text);
|
|
101
|
-
if (isSettledSessionStamp(before) && sameSessionStamp(before, observeStamp(path))) {
|
|
102
|
-
stampedDerivations.set(key, { stamp: before, value });
|
|
103
|
-
if (stampedDerivations.size > STAMPED_DERIVATION_LIMIT) {
|
|
104
|
-
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 */
|
|
105
91
|
}
|
|
106
92
|
}
|
|
107
|
-
return { state: PROBE_PRESENT, value };
|
|
108
93
|
}
|
|
109
94
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
stampedDerivations.clear();
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const SESSION_SUMMARY_INDEX_VERSION = 2;
|
|
116
|
-
|
|
117
|
-
// Mirror of lifecycle-api.mjs listLeadSessions visibility: the cold catalog
|
|
118
|
-
// must never surface worker/agent dispatches (memory ingest chunks, judges,
|
|
119
|
-
// spawned agents) — the authoritative engine excludes them, so a click on
|
|
120
|
-
// such a row dead-ends in "Session is not available." (user report).
|
|
121
|
-
const LEAD_OWNERS = new Set(['cli', 'user', 'mixdog']);
|
|
95
|
+
ordinaryCatalog.subscribe(notifyCatalogListeners);
|
|
96
|
+
onSessionCatalogDbChange(notifyCatalogListeners);
|
|
122
97
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// Mirror listLeadSessions: a previewless zero-message row is an unusable
|
|
130
|
-
// scratch (desktop boot leftovers, crashed first turns) — resuming it
|
|
131
|
-
// shows an empty conversation, so the catalog hides it.
|
|
132
|
-
if (!row.preview && row.messageCount === 0) return false;
|
|
133
|
-
const sourceType = String(row.sourceType || '')
|
|
134
|
-
.trim()
|
|
135
|
-
.toLowerCase();
|
|
136
|
-
// Mirror listLeadSessions: one-shot `mixdog exec` runs stay out of Recent.
|
|
137
|
-
if (sourceType === 'exec') return false;
|
|
138
|
-
const sourceName = String(row.sourceName || '')
|
|
139
|
-
.trim()
|
|
140
|
-
.toLowerCase();
|
|
141
|
-
const agent = String(row.agent || '')
|
|
142
|
-
.trim()
|
|
143
|
-
.toLowerCase();
|
|
144
|
-
return (
|
|
145
|
-
agent === 'lead' ||
|
|
146
|
-
sourceType === 'lead' ||
|
|
147
|
-
sourceType === 'cli' ||
|
|
148
|
-
sourceType === 'schedule' ||
|
|
149
|
-
sourceType === 'webhook' ||
|
|
150
|
-
(!sourceType && !sourceName && owner !== 'agent')
|
|
151
|
-
);
|
|
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);
|
|
152
104
|
}
|
|
153
105
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
.replace(/<mcp-instructions>[\s\S]*?<\/mcp-instructions>/gi, ' ')
|
|
159
|
-
// Session-context envelope (mirror of session-text.mjs
|
|
160
|
-
// stripSessionDisplayEnvelope): the "# Session / Cwd / Model /
|
|
161
|
-
// Workflow" header must never become a Recent title.
|
|
162
|
-
.replace(/^\s*# Session\r?\n(?:(?:Cwd|Model|Workflow):[^\r\n]*(?:\r?\n|$))+(?:\r?\n)?/i, ' ')
|
|
163
|
-
.replace(/^\s*#\s*Session\s+Cwd:\s+\S+(?:\s+Model:[^\r\n]*?)?(?:\s+Workflow:\s+\S+)?\s*/i, ' ')
|
|
164
|
-
.replace(/\s+/g, ' ')
|
|
165
|
-
.trim()
|
|
166
|
-
.slice(0, maximum)
|
|
167
|
-
);
|
|
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()]);
|
|
168
110
|
}
|
|
169
111
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return content
|
|
174
|
-
.map((part) => (typeof part === 'string' ? part : String(part?.text || part?.content || '')))
|
|
175
|
-
.filter(Boolean)
|
|
176
|
-
.join(' ');
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function normalizedRow(row, heartbeatAt = 0) {
|
|
180
|
-
if (!row || typeof row.id !== 'string' || !isStoredSessionId(row.id)) return null;
|
|
181
|
-
return {
|
|
182
|
-
id: row.id,
|
|
183
|
-
updatedAt: positiveNumber(row.updatedAt, 0),
|
|
184
|
-
// Conversation-activity timestamp (mirror of listLeadSessions):
|
|
185
|
-
// detach/resume bookkeeping bumps updatedAt in bulk on restarts, so
|
|
186
|
-
// Recent must order by lastUsedAt or every restart reshuffles rows.
|
|
187
|
-
lastUsedAt: positiveNumber(row.lastUsedAt, 0),
|
|
188
|
-
createdAt: positiveNumber(row.createdAt, 0),
|
|
189
|
-
lastHeartbeatAt: positiveNumber(row.lastHeartbeatAt, 0),
|
|
190
|
-
// Liveness comes from the .hb sidecar mtime alone: stored row fields
|
|
191
|
-
// (summary index / final session save) survive completion and must not
|
|
192
|
-
// keep the desktop working indicator on after the sidecar is deleted.
|
|
193
|
-
heartbeatAt: positiveNumber(heartbeatAt, 0),
|
|
194
|
-
closed: row.closed === true,
|
|
195
|
-
status: String(row.status || (row.closed === true ? 'closed' : 'idle')),
|
|
196
|
-
owner: row.owner || 'user',
|
|
197
|
-
agent: row.agent || null,
|
|
198
|
-
sourceType: row.sourceType || null,
|
|
199
|
-
sourceName: row.sourceName || null,
|
|
200
|
-
scopeKey: row.scopeKey || null,
|
|
201
|
-
ownerSessionId: row.ownerSessionId || row.parentSessionId || null,
|
|
202
|
-
visibility: sessionVisibility(row),
|
|
203
|
-
clientHostPid: positiveNumber(row.clientHostPid, 0) || null,
|
|
204
|
-
cwd: row.cwd || '',
|
|
205
|
-
desktopSession: desktopSession(row.desktopSession, row.cwd),
|
|
206
|
-
...(workspaceSummary(row.workspace) ? { workspace: workspaceSummary(row.workspace) } : {}),
|
|
207
|
-
provider: row.provider || null,
|
|
208
|
-
model: row.model || null,
|
|
209
|
-
agentTag: row.agentTag || null,
|
|
210
|
-
task_id: row.task_id || row.taskId || null,
|
|
211
|
-
permission: row.permission || null,
|
|
212
|
-
toolPermission: row.toolPermission || null,
|
|
213
|
-
messageCount: Math.max(0, Math.floor(Number(row.messageCount) || 0)),
|
|
214
|
-
title: cleanText(row.title, 100),
|
|
215
|
-
preview: cleanText(row.preview),
|
|
216
|
-
generation: typeof row.generation === 'number' ? row.generation : 0,
|
|
217
|
-
storageMtimeMs: positiveNumber(row.storageMtimeMs, 0),
|
|
218
|
-
storageSize: positiveNumber(row.storageSize, 0),
|
|
219
|
-
detachedReason: row.detachedReason || null,
|
|
220
|
-
};
|
|
112
|
+
/** Test seam: forget the ordinary catalog and the parsed summary index. */
|
|
113
|
+
export function clearStoredSessionCatalog() {
|
|
114
|
+
ordinaryCatalog.clear();
|
|
221
115
|
}
|
|
222
116
|
|
|
223
|
-
function
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if (!heartbeatAt || !ownerSessionId || (owner !== 'agent' && (!agent || agent === 'lead'))) continue;
|
|
235
|
-
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) ?? [];
|
|
236
128
|
}
|
|
237
|
-
return
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return agentHeartbeatAt > 0 ? { ...row, agentHeartbeatAt } : row;
|
|
242
|
-
});
|
|
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()) ?? [];
|
|
243
133
|
}
|
|
244
134
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
messageText(message.content)
|
|
256
|
-
)
|
|
257
|
-
)
|
|
258
|
-
.map((message) => cleanText(messageText(message.content)))
|
|
259
|
-
.find(Boolean) || '';
|
|
260
|
-
return normalizedRow(
|
|
261
|
-
{
|
|
262
|
-
...session,
|
|
263
|
-
messageCount: messages.filter((message) => message?.role === 'user' || message?.role === 'assistant').length,
|
|
264
|
-
preview,
|
|
265
|
-
},
|
|
266
|
-
heartbeatAt
|
|
267
|
-
);
|
|
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;
|
|
268
145
|
}
|
|
269
146
|
|
|
270
147
|
/**
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
* durable summary and a single UNREADABLE file retains its last known row
|
|
277
|
-
* 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.
|
|
278
153
|
*/
|
|
279
|
-
function
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
{ forceRead = false } = {}
|
|
283
|
-
) {
|
|
284
|
-
const directory = join(dataDir(), 'sessions');
|
|
285
|
-
const dirProbe = probePath(directory);
|
|
286
|
-
if (dirProbe.state === PROBE_ABSENT) return [];
|
|
287
|
-
if (dirProbe.state !== PROBE_PRESENT) return null;
|
|
288
|
-
let entries;
|
|
289
|
-
try {
|
|
290
|
-
entries = readdirSync(directory);
|
|
291
|
-
} catch {
|
|
292
|
-
return null;
|
|
154
|
+
export async function listStoredSessionSummariesAsync(options = {}) {
|
|
155
|
+
if (options.refreshFromStorage === true || options.rebuildIfMissing === false) {
|
|
156
|
+
return listStoredSessionSummaries(options);
|
|
293
157
|
}
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
if (!filename.endsWith('.json')) continue;
|
|
297
|
-
const storageId = filename.slice(0, -5);
|
|
298
|
-
const path = join(directory, filename);
|
|
299
|
-
const retained = indexRowsById.get(storageId);
|
|
300
|
-
const probe = probePath(path);
|
|
301
|
-
// Provably gone: the row legitimately disappears with the file.
|
|
302
|
-
if (probe.state === PROBE_ABSENT) continue;
|
|
303
|
-
if (probe.state !== PROBE_PRESENT) {
|
|
304
|
-
if (retained) rows.push(retained);
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
if (!forceRead && retained) {
|
|
308
|
-
const exactFingerprint =
|
|
309
|
-
retained.storageMtimeMs > 0 &&
|
|
310
|
-
retained.storageSize > 0 &&
|
|
311
|
-
retained.storageMtimeMs === probe.mtimeMs &&
|
|
312
|
-
retained.storageSize === probe.size;
|
|
313
|
-
if (exactFingerprint) {
|
|
314
|
-
rows.push(retained);
|
|
315
|
-
continue;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
// A file the index does not describe (never saved by the store, or
|
|
319
|
-
// saved after the index was written) is parsed once per full stamp.
|
|
320
|
-
const read = stampedDerivation('row', path, (text) => {
|
|
321
|
-
// Same strict authority as the store: a duplicate/ambiguous top-level
|
|
322
|
-
// record or a foreign identity is not this file's session.
|
|
323
|
-
const record = readTopLevelLifecycleRecord(text);
|
|
324
|
-
if (isLifecycleUnreadable(record) || record.id !== storageId) return null;
|
|
325
|
-
return rowFromSession(record.doc, 0);
|
|
326
|
-
});
|
|
327
|
-
// Provably gone: the row legitimately disappears with the file.
|
|
328
|
-
if (read.state === PROBE_ABSENT) continue;
|
|
329
|
-
if (read.state !== PROBE_PRESENT) {
|
|
330
|
-
// Exists and could not be read (EACCES/EIO): retain the last known
|
|
331
|
-
// row rather than let a transient IO error delete it from view.
|
|
332
|
-
if (retained) rows.push(retained);
|
|
333
|
-
continue;
|
|
334
|
-
}
|
|
335
|
-
if (read.value) {
|
|
336
|
-
rows.push({
|
|
337
|
-
...structuredClone(read.value),
|
|
338
|
-
heartbeatAt: positiveNumber(heartbeatMtimes.get(storageId), 0),
|
|
339
|
-
storageMtimeMs: probe.mtimeMs,
|
|
340
|
-
storageSize: probe.size,
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
return leadRowsWithAgentHeartbeat(rows).sort(byRecentActivity);
|
|
158
|
+
const answer = await catalogDbListing(null);
|
|
159
|
+
return answer ? answer.rows : listStoredSessionSummaries();
|
|
345
160
|
}
|
|
346
161
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
return (right.lastUsedAt || right.updatedAt || 0) - (left.lastUsedAt || left.updatedAt || 0);
|
|
350
|
-
}
|
|
162
|
+
const DEFAULT_PAGE_LIMIT = 50;
|
|
163
|
+
const MAX_PAGE_LIMIT = 1000;
|
|
351
164
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
const
|
|
363
|
-
const
|
|
364
|
-
if (
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
.map((row) => normalizedRow(row, heartbeatMtimes.get(row?.id) || 0))
|
|
370
|
-
.filter(Boolean);
|
|
371
|
-
for (const row of normalizedRows) indexRowsById.set(row.id, row);
|
|
372
|
-
indexRows = leadRowsWithAgentHeartbeat(normalizedRows).sort(byRecentActivity);
|
|
373
|
-
}
|
|
374
|
-
} catch {
|
|
375
|
-
/* malformed sidecar: the files below are the authority */
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
// An index that EXISTS but is unreadable (EACCES/EIO) is neither missing
|
|
379
|
-
// nor empty: the scan below still runs, and when IT cannot enumerate
|
|
380
|
-
// either, the catalog reports nothing rather than inventing an empty truth.
|
|
381
|
-
const scan = (forceRead = false) => scanSessionFiles(heartbeatMtimes, indexRowsById, { forceRead });
|
|
382
|
-
if (options.refreshFromStorage === true) return scan(true) ?? indexRows ?? [];
|
|
383
|
-
if (indexRows) {
|
|
384
|
-
if (options.rebuildIfMissing === false) return indexRows;
|
|
385
|
-
// Enumerate identities to detect deletions, stat known files, and parse
|
|
386
|
-
// only rows newer than the index. Cold-start cost therefore scales with
|
|
387
|
-
// changed transcripts rather than total transcript bytes.
|
|
388
|
-
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
|
+
};
|
|
389
182
|
}
|
|
390
|
-
//
|
|
391
|
-
|
|
392
|
-
|
|
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
|
+
};
|
|
393
190
|
}
|
|
394
191
|
|
|
395
192
|
/** Exact, fail-closed existence check for a durable session address.
|
|
@@ -464,8 +261,6 @@ function storedTranscriptInputPaths(sessionId) {
|
|
|
464
261
|
};
|
|
465
262
|
}
|
|
466
263
|
|
|
467
|
-
const STAMP_ABSENT_CODES = new Set(['ENOENT', 'ENOTDIR']);
|
|
468
|
-
|
|
469
264
|
/** Settled identity of the files readStoredSessionTranscript projects from:
|
|
470
265
|
* the canonical record and its turn-checkpoint sidecar. Null while either is
|
|
471
266
|
* too fresh to vouch for by stat alone (isSettledSessionStamp), unreadable,
|
|
@@ -490,6 +285,72 @@ export function storedSessionTranscriptStamp(id) {
|
|
|
490
285
|
return archive ? `${record}|${checkpoint}|${archive}` : null;
|
|
491
286
|
}
|
|
492
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
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
493
354
|
/** Read exactly one persisted session for a visible desktop pane. This never
|
|
494
355
|
* enumerates siblings. Normal reads stay independent of runtime ownership;
|
|
495
356
|
* interrupted turns conditionally use the same durable reconnect recovery as
|
|
@@ -526,10 +387,21 @@ export async function readStoredSessionTranscript(id, options = {}) {
|
|
|
526
387
|
let readStamp = null;
|
|
527
388
|
// The strict record parse (full JSON + duplicate-key scan) is itself a
|
|
528
389
|
// large share of a cold read, so it only runs when the content is new.
|
|
529
|
-
const
|
|
390
|
+
const includeMessages = options.includeMessages === true;
|
|
391
|
+
const mode = includeMessages ? 'messages' : 'items';
|
|
392
|
+
const checkpointAbsent = checkpoint.state === PROBE_ABSENT;
|
|
530
393
|
// Large, checkpoint-free item projections run off the daemon event loop.
|
|
531
|
-
const offloadable =
|
|
532
|
-
|
|
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
|
+
};
|
|
533
405
|
const { value, hit, read } = await storedTranscriptCache.read({
|
|
534
406
|
key: `${sessionId}|${itemLimit}|${mode}`,
|
|
535
407
|
// Growing history windows of one session replace each other.
|
|
@@ -566,70 +438,7 @@ export async function readStoredSessionTranscript(id, options = {}) {
|
|
|
566
438
|
readStamp = before && sameSessionStamp(before, observeStamp(recordPath)) ? before : null;
|
|
567
439
|
return body.text;
|
|
568
440
|
},
|
|
569
|
-
produce:
|
|
570
|
-
const stamp = readStamp;
|
|
571
|
-
// A large record with no checkpoint sidecar parses and projects on a
|
|
572
|
-
// worker thread: the same pipeline, off the daemon event loop. It hands
|
|
573
|
-
// no parse to the load cache (a later resume reads the file itself).
|
|
574
|
-
if (offloadable && text.length >= OFFLOAD_MIN_CHARS) {
|
|
575
|
-
try {
|
|
576
|
-
timing.worker = storedTranscriptWorkerRunning() ? 'warm' : 'cold';
|
|
577
|
-
const workerStartedAt = performance.now();
|
|
578
|
-
const answer = await projectStoredTranscriptOffThread({ sessionId, text, itemLimit });
|
|
579
|
-
timing.workerMs = performance.now() - workerStartedAt;
|
|
580
|
-
Object.assign(timing, answer.timing);
|
|
581
|
-
if (answer.unreadable) return null;
|
|
582
|
-
if (stamp) readCanonicalLifecycle.rememberStrictVerdict(recordPath, stamp, answer.lifecycle);
|
|
583
|
-
// Stamps are process-unique per module realm; re-issue in this one.
|
|
584
|
-
answer.value.projectionStamp = nextProjectionStamp();
|
|
585
|
-
return answer.value;
|
|
586
|
-
} catch {
|
|
587
|
-
// Worker unavailable: fall through to the in-process pipeline.
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
const parseStartedAt = performance.now();
|
|
591
|
-
const record = readTopLevelLifecycleRecord(text);
|
|
592
|
-
timing.parseMs = performance.now() - parseStartedAt;
|
|
593
|
-
if (isLifecycleUnreadable(record) || record.id !== sessionId) return null;
|
|
594
|
-
// The same strict verdict the lifecycle authority would reach for these
|
|
595
|
-
// bytes (storedSessionExists runs next when the pane's runtime loads).
|
|
596
|
-
if (stamp) readCanonicalLifecycle.rememberStrictVerdict(recordPath, stamp, record);
|
|
597
|
-
const checkpointAbsent = checkpoint.state === PROBE_ABSENT;
|
|
598
|
-
// Hand the parse to the session load cache BEFORE projecting: a resume
|
|
599
|
-
// that runs while this projection is still being built (the daemon
|
|
600
|
-
// resumes the panes it restores) claims it instead of reading the file
|
|
601
|
-
// again. The projection path loads the store anyway, so loading its
|
|
602
|
-
// cache first costs nothing and gives the hand-off somewhere to go on
|
|
603
|
-
// the process's first read.
|
|
604
|
-
let handedOver = false;
|
|
605
|
-
if (stamp) {
|
|
606
|
-
await import('./store/load-cache.mjs');
|
|
607
|
-
handedOver = offerStrictSessionRecord(recordPath, stamp, record, text.length);
|
|
608
|
-
}
|
|
609
|
-
// Once handed over, the record is a runtime's mutable session. The
|
|
610
|
-
// projection is then built from a private parse of the same bytes, so
|
|
611
|
-
// nothing it retains aliases that session and it needs no copying. With
|
|
612
|
-
// a turn checkpoint the projection recovers through loadSession, the very
|
|
613
|
-
// load the hand-off serves, and projects the load cache's session
|
|
614
|
-
// exactly as before; only that projection is detached.
|
|
615
|
-
const projectStartedAt = performance.now();
|
|
616
|
-
const value = await projectStoredTranscript(
|
|
617
|
-
sessionId,
|
|
618
|
-
handedOver && checkpointAbsent ? JSON.parse(text) : record.doc,
|
|
619
|
-
{
|
|
620
|
-
itemLimit,
|
|
621
|
-
includeMessages: options.includeMessages === true,
|
|
622
|
-
checkpointAbsent,
|
|
623
|
-
}
|
|
624
|
-
);
|
|
625
|
-
timing.projectMs = performance.now() - projectStartedAt;
|
|
626
|
-
if (!handedOver || checkpointAbsent || !value || mode !== 'items') return value;
|
|
627
|
-
try {
|
|
628
|
-
return structuredClone(value);
|
|
629
|
-
} catch {
|
|
630
|
-
return value;
|
|
631
|
-
}
|
|
632
|
-
},
|
|
441
|
+
produce: (text) => produceStoredTranscript(text, readStamp, produceContext),
|
|
633
442
|
});
|
|
634
443
|
// The record vanished between stat and read: same answer as an absent probe.
|
|
635
444
|
if (readState === PROBE_ABSENT) return readArchivedAgentResult(sessionId);
|