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
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
// Worker thread of the session catalog DB (index.mjs is its only client).
|
|
2
|
+
//
|
|
3
|
+
// It owns `<dataDir>/session-catalog.sqlite` and runs the ordinary catalog
|
|
4
|
+
// engine (store-summary-catalog.mjs): the summary index stamps and delta-log
|
|
5
|
+
// apply, the sessions-directory name diff, per-file stamps, `.hb` liveness and
|
|
6
|
+
// the background verification. An ordinary listing is one `list` request: the
|
|
7
|
+
// engine's cheap stamp check runs first (so changes by any writer appear as
|
|
8
|
+
// soon as they do on the JSON path), its changes are written to the DB, and
|
|
9
|
+
// the answer comes from the DB. When one of the daemon's own writes changed a
|
|
10
|
+
// session file, a `hint` request (ids only, tagged with the data dir) arrives
|
|
11
|
+
// before any later listing, and that listing re-checks those ids from storage
|
|
12
|
+
// (read-your-writes for landed saves and completed deletes).
|
|
13
|
+
//
|
|
14
|
+
// The DB is fenced: the connection runs in EXCLUSIVE locking mode and holds
|
|
15
|
+
// the lock for its whole life, so a second process cannot open it and simply
|
|
16
|
+
// stays on JSON. Only proven corruption (quick_check, SQLITE_CORRUPT,
|
|
17
|
+
// SQLITE_NOTADB) moves the file aside; busy, locked or access errors never do.
|
|
18
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
19
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, statSync } from 'node:fs';
|
|
20
|
+
import { dirname, join } from 'node:path';
|
|
21
|
+
import { performance } from 'node:perf_hooks';
|
|
22
|
+
import { postWorkerEvent, serveWorkerRequests } from '../../../../shared/worker-requests.mjs';
|
|
23
|
+
import { agentHeartbeatsByOwner, createOrdinaryCatalog, isLeadVisibleRow } from '../store-summary-catalog.mjs';
|
|
24
|
+
import { positiveNumber } from '../store-summary-fields.mjs';
|
|
25
|
+
import { SQLITE_REQUIREMENT, sqliteVersionSupported } from './sqlite-gate.mjs';
|
|
26
|
+
import {
|
|
27
|
+
APPLICATION_ID,
|
|
28
|
+
SCHEMA_SQL,
|
|
29
|
+
SCHEMA_VERSION,
|
|
30
|
+
UPSERT_SQL,
|
|
31
|
+
compareListings,
|
|
32
|
+
rowFromDb,
|
|
33
|
+
rowSignature,
|
|
34
|
+
upsertParams,
|
|
35
|
+
} from './schema.mjs';
|
|
36
|
+
|
|
37
|
+
const DB_FILE = 'session-catalog.sqlite';
|
|
38
|
+
const DEFAULTS = Object.freeze({
|
|
39
|
+
backfillBatch: 500,
|
|
40
|
+
shadowIntervalMs: 60_000,
|
|
41
|
+
retryMs: 60_000,
|
|
42
|
+
// How long a second process waits for the fence before staying on JSON.
|
|
43
|
+
fenceTimeoutMs: 250,
|
|
44
|
+
maxRecoveries: 3,
|
|
45
|
+
});
|
|
46
|
+
// Settings only a test run may change.
|
|
47
|
+
const TEST_ONLY = ['sqliteVersion', 'crashAfterBatches'];
|
|
48
|
+
// SQLite primary result codes.
|
|
49
|
+
const CORRUPT_CODES = new Set([11, 26]); // SQLITE_CORRUPT, SQLITE_NOTADB
|
|
50
|
+
const LOCKED_CODES = new Set([5, 6]); // SQLITE_BUSY, SQLITE_LOCKED
|
|
51
|
+
|
|
52
|
+
let state = null;
|
|
53
|
+
|
|
54
|
+
const log = (line) => postWorkerEvent({ type: 'log', line });
|
|
55
|
+
const message = (error) => String(error?.message || error);
|
|
56
|
+
|
|
57
|
+
function settingsOf(raw) {
|
|
58
|
+
const settings = { ...DEFAULTS, ...(raw || {}) };
|
|
59
|
+
if (!process.env.NODE_TEST_CONTEXT) for (const key of TEST_ONLY) delete settings[key];
|
|
60
|
+
return settings;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function runtimeLabel() {
|
|
64
|
+
return `node=${process.version}${process.versions.electron ? ` electron=${process.versions.electron}` : ''}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function codedError(text, code) {
|
|
68
|
+
return Object.assign(new Error(text), { code });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Proven corruption and lock contention get stable codes; nothing else is
|
|
72
|
+
* ever treated as either. */
|
|
73
|
+
function classify(error) {
|
|
74
|
+
if (String(error?.code || '').startsWith('ESESSIONCATALOG')) return error;
|
|
75
|
+
const text = message(error);
|
|
76
|
+
const primary = Number(error?.errcode) & 0xff;
|
|
77
|
+
if (CORRUPT_CODES.has(primary) || /file is not a database|disk image is malformed/i.test(text)) {
|
|
78
|
+
return codedError(text, 'ESESSIONCATALOGCORRUPT');
|
|
79
|
+
}
|
|
80
|
+
if (LOCKED_CODES.has(primary) || /database is locked|database table is locked/i.test(text)) {
|
|
81
|
+
return codedError(text, 'ESESSIONCATALOGLOCKED');
|
|
82
|
+
}
|
|
83
|
+
return error;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function runtimeSqliteVersion() {
|
|
87
|
+
const db = new DatabaseSync(':memory:');
|
|
88
|
+
try {
|
|
89
|
+
return db.prepare('SELECT sqlite_version() AS version').get().version;
|
|
90
|
+
} finally {
|
|
91
|
+
db.close();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function stampKey(stamp) {
|
|
96
|
+
return stamp ? `${stamp.dev}:${stamp.ino}:${stamp.size}:${stamp.mtimeNs}:${stamp.ctimeNs}` : null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ── Connection ──────────────────────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
function meta(db, key) {
|
|
102
|
+
const row = db.prepare('SELECT value FROM meta WHERE key=?').get(key);
|
|
103
|
+
return row ? row.value : null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function setMeta(db, key, value) {
|
|
107
|
+
db.prepare('INSERT INTO meta(key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value=excluded.value').run(
|
|
108
|
+
key,
|
|
109
|
+
String(value)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function transaction(db, body) {
|
|
114
|
+
db.exec('BEGIN IMMEDIATE');
|
|
115
|
+
try {
|
|
116
|
+
const result = body();
|
|
117
|
+
db.exec('COMMIT');
|
|
118
|
+
return result;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
try {
|
|
121
|
+
db.exec('ROLLBACK');
|
|
122
|
+
} catch {
|
|
123
|
+
/* the failure already ended it */
|
|
124
|
+
}
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Opens `path` as the fenced owner and checks it; throws a classified error. */
|
|
130
|
+
function openChecked(path, settings) {
|
|
131
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
132
|
+
let db;
|
|
133
|
+
try {
|
|
134
|
+
db = new DatabaseSync(path);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
throw classify(error);
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
db.exec(`PRAGMA busy_timeout=${Math.max(0, Math.floor(settings.fenceTimeoutMs))}`);
|
|
140
|
+
// The fence: an exclusive lock taken now and kept until close.
|
|
141
|
+
db.exec('PRAGMA locking_mode=EXCLUSIVE');
|
|
142
|
+
db.exec('BEGIN EXCLUSIVE');
|
|
143
|
+
db.exec('COMMIT');
|
|
144
|
+
const applicationId = db.prepare('PRAGMA application_id').get().application_id;
|
|
145
|
+
const objects = db.prepare('SELECT count(*) AS n FROM sqlite_master').get().n;
|
|
146
|
+
if (applicationId !== APPLICATION_ID && !(applicationId === 0 && objects === 0)) {
|
|
147
|
+
throw codedError(`not a session catalog (application_id=${applicationId})`, 'ESESSIONCATALOGFOREIGN');
|
|
148
|
+
}
|
|
149
|
+
const check = Object.values(db.prepare('PRAGMA quick_check').get())[0];
|
|
150
|
+
if (check !== 'ok') throw codedError(`quick_check: ${check}`, 'ESESSIONCATALOGCORRUPT');
|
|
151
|
+
db.exec(
|
|
152
|
+
`PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL; PRAGMA busy_timeout=5000; PRAGMA application_id=${APPLICATION_ID};`
|
|
153
|
+
);
|
|
154
|
+
db.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');
|
|
155
|
+
let recreated = false;
|
|
156
|
+
if (Number(meta(db, 'schema_version')) !== SCHEMA_VERSION) {
|
|
157
|
+
// A derived cache: another layout is dropped and backfilled again.
|
|
158
|
+
transaction(db, () => {
|
|
159
|
+
db.exec('DROP TABLE IF EXISTS sessions; DELETE FROM meta;');
|
|
160
|
+
db.exec(SCHEMA_SQL);
|
|
161
|
+
setMeta(db, 'schema_version', SCHEMA_VERSION);
|
|
162
|
+
});
|
|
163
|
+
recreated = true;
|
|
164
|
+
}
|
|
165
|
+
return { db, recreated };
|
|
166
|
+
} catch (error) {
|
|
167
|
+
try {
|
|
168
|
+
db.close();
|
|
169
|
+
} catch {
|
|
170
|
+
/* already unusable */
|
|
171
|
+
}
|
|
172
|
+
throw classify(error);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Renames the DB and its WAL/SHM sidecars aside; returns the new path. */
|
|
177
|
+
function moveAside(path) {
|
|
178
|
+
const aside = `${path}.corrupt-${new Date().toISOString().replace(/[:.]/g, '-')}`;
|
|
179
|
+
for (const suffix of ['', '-wal', '-shm']) {
|
|
180
|
+
if (existsSync(path + suffix)) renameSync(path + suffix, aside + suffix);
|
|
181
|
+
}
|
|
182
|
+
return aside;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function prepare(db) {
|
|
186
|
+
const lead = 'SELECT * FROM sessions WHERE lead_visible=1';
|
|
187
|
+
const order = 'ORDER BY activity DESC, id ASC';
|
|
188
|
+
return {
|
|
189
|
+
upsert: db.prepare(UPSERT_SQL),
|
|
190
|
+
signature: db.prepare('SELECT row_sig FROM sessions WHERE id=?'),
|
|
191
|
+
remove: db.prepare('DELETE FROM sessions WHERE id=?'),
|
|
192
|
+
ids: db.prepare('SELECT id FROM sessions'),
|
|
193
|
+
count: db.prepare('SELECT count(*) AS n FROM sessions'),
|
|
194
|
+
listAll: db.prepare(`${lead} ${order}`),
|
|
195
|
+
listFirst: db.prepare(`${lead} ${order} LIMIT ?`),
|
|
196
|
+
listAfter: db.prepare(`${lead} AND (activity < ? OR (activity = ? AND id > ?)) ${order} LIMIT ?`),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ── State ───────────────────────────────────────────────────────────────────
|
|
201
|
+
// phase: closed → (off | backfilling) → ready; rebuilding/backfilling again
|
|
202
|
+
// after a fresh scan or a shadow mismatch. Only `ready` answers listings.
|
|
203
|
+
|
|
204
|
+
function newState(dataDir) {
|
|
205
|
+
const engine = createOrdinaryCatalog();
|
|
206
|
+
engine.subscribe(() => postWorkerEvent({ type: 'changed' }));
|
|
207
|
+
return {
|
|
208
|
+
dataDir,
|
|
209
|
+
path: join(dataDir, DB_FILE),
|
|
210
|
+
settings: settingsOf(null),
|
|
211
|
+
db: null,
|
|
212
|
+
statements: null,
|
|
213
|
+
phase: 'closed',
|
|
214
|
+
reason: null,
|
|
215
|
+
final: false,
|
|
216
|
+
retryAt: 0,
|
|
217
|
+
recoveries: 0,
|
|
218
|
+
recoverOnOpen: false,
|
|
219
|
+
engine,
|
|
220
|
+
absent: false,
|
|
221
|
+
backfill: null,
|
|
222
|
+
generation: 0,
|
|
223
|
+
shadowTimer: null,
|
|
224
|
+
shadowDueAt: 0,
|
|
225
|
+
shadowAt: 0,
|
|
226
|
+
switchOff: null,
|
|
227
|
+
sqliteVersion: null,
|
|
228
|
+
lastBackfill: null,
|
|
229
|
+
lastShadow: null,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function closeConnection(s) {
|
|
234
|
+
try {
|
|
235
|
+
s.db?.close();
|
|
236
|
+
} catch {
|
|
237
|
+
/* closing a broken connection */
|
|
238
|
+
}
|
|
239
|
+
s.db = null;
|
|
240
|
+
s.statements = null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function closeState() {
|
|
244
|
+
if (!state) return;
|
|
245
|
+
const s = state;
|
|
246
|
+
state = null;
|
|
247
|
+
s.generation++;
|
|
248
|
+
if (s.shadowTimer) clearTimeout(s.shadowTimer);
|
|
249
|
+
s.engine.clear();
|
|
250
|
+
closeConnection(s);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function ensure(dataDir, raw) {
|
|
254
|
+
if (state && state.dataDir !== dataDir) closeState();
|
|
255
|
+
state ||= newState(dataDir);
|
|
256
|
+
state.settings = settingsOf(raw);
|
|
257
|
+
// The engine resolves every path from MIXDOG_DATA_DIR (this thread's copy).
|
|
258
|
+
process.env.MIXDOG_DATA_DIR = dataDir;
|
|
259
|
+
if (state.phase === 'closed' || (state.phase === 'off' && !state.final && Date.now() >= state.retryAt)) open(state);
|
|
260
|
+
return state;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function off(s, reason, final, line) {
|
|
264
|
+
closeConnection(s);
|
|
265
|
+
s.phase = 'off';
|
|
266
|
+
s.reason = reason;
|
|
267
|
+
s.final = final;
|
|
268
|
+
s.retryAt = Date.now() + s.settings.retryMs;
|
|
269
|
+
log(line);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function open(s) {
|
|
273
|
+
const version = s.settings.sqliteVersion || runtimeSqliteVersion();
|
|
274
|
+
s.sqliteVersion = version;
|
|
275
|
+
if (!sqliteVersionSupported(version)) {
|
|
276
|
+
off(
|
|
277
|
+
s,
|
|
278
|
+
'sqlite-version',
|
|
279
|
+
true,
|
|
280
|
+
`disabled: SQLite ${version} (${runtimeLabel()}) lacks the WAL-reset fix (needs ${SQLITE_REQUIREMENT}); catalog stays JSON`
|
|
281
|
+
);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
let opened;
|
|
285
|
+
try {
|
|
286
|
+
opened = openChecked(s.path, s.settings);
|
|
287
|
+
} catch (error) {
|
|
288
|
+
if (error.code !== 'ESESSIONCATALOGCORRUPT') {
|
|
289
|
+
const reason = error.code === 'ESESSIONCATALOGLOCKED' ? 'locked' : 'unopenable';
|
|
290
|
+
off(s, reason, false, `${reason}: ${message(error)}; catalog stays JSON (retried later) path=${s.path}`);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
try {
|
|
294
|
+
const aside = moveAside(s.path);
|
|
295
|
+
log(`corrupt DB (${message(error)}) moved aside to ${aside}; rebuilding from JSON`);
|
|
296
|
+
opened = openChecked(s.path, s.settings);
|
|
297
|
+
} catch (retry) {
|
|
298
|
+
off(s, 'unopenable', false, `unopenable after moving a corrupt DB aside: ${message(retry)}; catalog stays JSON`);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
s.db = opened.db;
|
|
303
|
+
s.statements = prepare(opened.db);
|
|
304
|
+
const complete = meta(s.db, 'backfill_complete') === '1';
|
|
305
|
+
log(
|
|
306
|
+
`open sqlite=${version} ${runtimeLabel()} schema=${SCHEMA_VERSION}${opened.recreated ? ' (new)' : ''} rows=${
|
|
307
|
+
s.statements.count.get().n
|
|
308
|
+
} backfill=${complete ? 'complete' : 'pending'} path=${s.path}`
|
|
309
|
+
);
|
|
310
|
+
let kind = complete ? 'resync' : 'resume';
|
|
311
|
+
if (opened.recreated) kind = 'initial';
|
|
312
|
+
else if (s.recoverOnOpen) kind = 'recover';
|
|
313
|
+
s.recoverOnOpen = false;
|
|
314
|
+
startBackfill(s, kind);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/** A DB failure: corruption is moved aside and rebuilt (bounded), anything
|
|
318
|
+
* else closes the DB and is retried later. Listings use JSON meanwhile. */
|
|
319
|
+
function fail(s, error, phase) {
|
|
320
|
+
if (s !== state) return;
|
|
321
|
+
const failure = classify(error);
|
|
322
|
+
s.generation++;
|
|
323
|
+
closeConnection(s);
|
|
324
|
+
if (failure.code === 'ESESSIONCATALOGCORRUPT' && s.recoveries < s.settings.maxRecoveries) {
|
|
325
|
+
s.recoveries++;
|
|
326
|
+
log(`corrupt DB during ${phase}: ${message(failure)}`);
|
|
327
|
+
try {
|
|
328
|
+
log(`moved aside to ${moveAside(s.path)}; rebuilding from JSON`);
|
|
329
|
+
} catch (moveError) {
|
|
330
|
+
off(s, 'unopenable', false, `cannot move the corrupt DB aside: ${message(moveError)}; catalog stays JSON`);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
s.phase = 'closed';
|
|
334
|
+
open(s);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
// The reopen rewrites every row (see runBackfill).
|
|
338
|
+
s.recoverOnOpen = true;
|
|
339
|
+
off(s, 'error', false, `${phase} failed: ${message(failure)}; catalog stays JSON (retried later)`);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ── Rows ────────────────────────────────────────────────────────────────────
|
|
343
|
+
|
|
344
|
+
/** What `id` contributes to the engine's current catalog, or null. */
|
|
345
|
+
function resolve(s, id) {
|
|
346
|
+
const catalog = s.engine.current();
|
|
347
|
+
const row = catalog ? s.engine.entryRow(catalog, id) : null;
|
|
348
|
+
if (!row) return null;
|
|
349
|
+
return { row, fingerprint: stampKey(catalog.entries.get(id)?.stamp), leadVisible: isLeadVisibleRow(row) };
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** Writes the resolved rows of `ids` whose signature changed; deletes the
|
|
353
|
+
* ids that resolve to nothing. */
|
|
354
|
+
function writeIds(s, ids, force = false) {
|
|
355
|
+
const counts = { written: 0, skipped: 0, removed: 0 };
|
|
356
|
+
for (const id of ids) {
|
|
357
|
+
const entry = resolve(s, id);
|
|
358
|
+
if (!entry) {
|
|
359
|
+
counts.removed += Number(s.statements.remove.run(id).changes);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
const signature = rowSignature(entry);
|
|
363
|
+
if (!force && s.statements.signature.get(id)?.row_sig === signature) {
|
|
364
|
+
counts.skipped++;
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
s.statements.upsert.run(...upsertParams(entry, signature));
|
|
368
|
+
counts.written++;
|
|
369
|
+
}
|
|
370
|
+
return counts;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ── Backfill ────────────────────────────────────────────────────────────────
|
|
374
|
+
// A full scan of storage imported in batches (yielding between them, so
|
|
375
|
+
// listings get an immediate "use JSON" meanwhile). Membership is the scan's
|
|
376
|
+
// own id set: at the end every DB row the scan does not contain is deleted, so an interrupted and resumed import never keeps a
|
|
377
|
+
// session deleted in between. Unchanged rows are skipped by signature.
|
|
378
|
+
|
|
379
|
+
function startBackfill(s, kind) {
|
|
380
|
+
s.phase = kind === 'rebuild' ? 'rebuilding' : 'backfilling';
|
|
381
|
+
const generation = ++s.generation;
|
|
382
|
+
const run = runBackfill(s, kind, generation).catch((error) => fail(s, error, 'backfill'));
|
|
383
|
+
s.backfill = run;
|
|
384
|
+
run.finally(() => {
|
|
385
|
+
if (s.backfill === run) s.backfill = null;
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async function runBackfill(s, kind, generation) {
|
|
390
|
+
const startedAt = performance.now();
|
|
391
|
+
const current = () => s === state && generation === s.generation;
|
|
392
|
+
// A full rescan: the DB is rebuilt from what storage says now.
|
|
393
|
+
s.engine.clear();
|
|
394
|
+
const result = s.engine.refresh(s.engine.loadIndex(), { assemble: false });
|
|
395
|
+
if (!result.catalog && !result.absent) throw new Error('the sessions directory cannot be enumerated');
|
|
396
|
+
s.absent = result.absent;
|
|
397
|
+
const ids = result.catalog ? [...result.catalog.entries.keys()] : [];
|
|
398
|
+
setMeta(s.db, 'backfill_complete', 0);
|
|
399
|
+
const totals = { written: 0, skipped: 0 };
|
|
400
|
+
const { backfillBatch, crashAfterBatches } = s.settings;
|
|
401
|
+
// A rebuild after a mismatch or an error rewrites every row: a damaged
|
|
402
|
+
// stored row still carries the signature of the row it was written from.
|
|
403
|
+
const force = kind === 'rebuild' || kind === 'recover';
|
|
404
|
+
let batches = 0;
|
|
405
|
+
for (let start = 0; start < ids.length; start += backfillBatch) {
|
|
406
|
+
if (!current()) return;
|
|
407
|
+
const counts = transaction(s.db, () => writeIds(s, ids.slice(start, start + backfillBatch), force));
|
|
408
|
+
totals.written += counts.written;
|
|
409
|
+
totals.skipped += counts.skipped;
|
|
410
|
+
batches++;
|
|
411
|
+
// Test seam: the thread dies mid-backfill.
|
|
412
|
+
if (crashAfterBatches === batches) process.exit(1);
|
|
413
|
+
await new Promise((resolveTurn) => setImmediate(resolveTurn));
|
|
414
|
+
}
|
|
415
|
+
if (!current()) return;
|
|
416
|
+
const finished = transaction(s.db, () => {
|
|
417
|
+
const members = new Set();
|
|
418
|
+
for (const id of ids) if (resolve(s, id)) members.add(id);
|
|
419
|
+
let removed = 0;
|
|
420
|
+
for (const { id } of s.statements.ids.all()) {
|
|
421
|
+
if (!members.has(id)) removed += Number(s.statements.remove.run(id).changes);
|
|
422
|
+
}
|
|
423
|
+
setMeta(s.db, 'backfill_complete', 1);
|
|
424
|
+
return { removed, rows: s.statements.count.get().n };
|
|
425
|
+
});
|
|
426
|
+
s.phase = 'ready';
|
|
427
|
+
s.recoveries = 0;
|
|
428
|
+
s.lastBackfill = { kind, ...totals, ...finished, ms: performance.now() - startedAt };
|
|
429
|
+
log(
|
|
430
|
+
`backfill complete (${kind}) rows=${finished.rows} written=${totals.written} skipped=${totals.skipped} removed=${
|
|
431
|
+
finished.removed
|
|
432
|
+
} ms=${Math.round(s.lastBackfill.ms)}`
|
|
433
|
+
);
|
|
434
|
+
// Every completed backfill is verified at once.
|
|
435
|
+
if (s.shadowTimer) clearTimeout(s.shadowTimer);
|
|
436
|
+
s.shadowTimer = null;
|
|
437
|
+
scheduleShadow(s, 0);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// ── Shadow verification ─────────────────────────────────────────────────────
|
|
441
|
+
// Rate-limited: the engine's JSON listing against the DB listing, every row
|
|
442
|
+
// in exact order. A mismatch stops serving from the DB and rebuilds it from
|
|
443
|
+
// JSON.
|
|
444
|
+
|
|
445
|
+
function scheduleShadow(s, delay = null) {
|
|
446
|
+
if (s.shadowTimer || s.phase !== 'ready') return;
|
|
447
|
+
const wait = delay ?? Math.max(0, s.shadowAt + s.settings.shadowIntervalMs - Date.now());
|
|
448
|
+
s.shadowDueAt = Date.now() + wait;
|
|
449
|
+
s.shadowTimer = setTimeout(() => {
|
|
450
|
+
s.shadowTimer = null;
|
|
451
|
+
if (s === state) shadow(s);
|
|
452
|
+
}, wait);
|
|
453
|
+
s.shadowTimer.unref?.();
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function formatReport(report) {
|
|
457
|
+
const mismatch = report.missing + report.extra + report.order + report.fields > 0;
|
|
458
|
+
const head = `shadow-verify ${mismatch ? 'mismatch' : 'ok'} json=${report.json} db=${report.db}`;
|
|
459
|
+
if (!mismatch) return head;
|
|
460
|
+
const first = Object.entries(report.ids)
|
|
461
|
+
.filter(([, ids]) => ids.length > 0)
|
|
462
|
+
.map(([kind, ids]) => `${kind}:${ids.join(',')}`)
|
|
463
|
+
.join(' ');
|
|
464
|
+
return `${head} missing=${report.missing} extra=${report.extra} order=${report.order} fields=${report.fields} first=${first}`;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function shadow(s) {
|
|
468
|
+
if (s.phase !== 'ready') return null;
|
|
469
|
+
try {
|
|
470
|
+
s.shadowAt = Date.now();
|
|
471
|
+
const catalog = s.engine.current();
|
|
472
|
+
const json = catalog ? s.engine.rowsOf(catalog) : [];
|
|
473
|
+
const report = compareListings(json, s.statements.listAll.all().map(rowFromDb));
|
|
474
|
+
s.lastShadow = report;
|
|
475
|
+
log(formatReport(report));
|
|
476
|
+
if (report.missing + report.extra + report.order + report.fields > 0) {
|
|
477
|
+
log('listings use JSON until the DB is rebuilt');
|
|
478
|
+
startBackfill(s, 'rebuild');
|
|
479
|
+
}
|
|
480
|
+
return report;
|
|
481
|
+
} catch (error) {
|
|
482
|
+
fail(s, error, 'shadow-verify');
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// ── Listing ─────────────────────────────────────────────────────────────────
|
|
488
|
+
|
|
489
|
+
/** The off-switch: `{ "sessionStore": { "sqliteCatalog": false } }` in the
|
|
490
|
+
* data dir's config (re-read when the file changes). */
|
|
491
|
+
function switchedOff(s) {
|
|
492
|
+
if (s.settings.enabled === false) return true;
|
|
493
|
+
if (s.settings.enabled === true) return false;
|
|
494
|
+
const path = join(s.dataDir, 'mixdog-config.json');
|
|
495
|
+
let info;
|
|
496
|
+
try {
|
|
497
|
+
info = statSync(path);
|
|
498
|
+
} catch {
|
|
499
|
+
s.switchOff = null;
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
if (!s.switchOff || s.switchOff.mtimeMs !== info.mtimeMs || s.switchOff.size !== info.size) {
|
|
503
|
+
let value = s.switchOff?.value ?? false;
|
|
504
|
+
try {
|
|
505
|
+
value = JSON.parse(readFileSync(path, 'utf8'))?.sessionStore?.sqliteCatalog === false;
|
|
506
|
+
} catch {
|
|
507
|
+
/* a torn or malformed config keeps the last answer */
|
|
508
|
+
}
|
|
509
|
+
if (value !== (s.switchOff?.value ?? false)) {
|
|
510
|
+
log(value ? 'off-switch set: ordinary listings use JSON' : 'off-switch cleared');
|
|
511
|
+
}
|
|
512
|
+
s.switchOff = { mtimeMs: info.mtimeMs, size: info.size, value };
|
|
513
|
+
}
|
|
514
|
+
return s.switchOff.value;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/** Rows from the DB with the liveness of the current scan overlaid. */
|
|
518
|
+
function answer(s, page) {
|
|
519
|
+
const heartbeatMtimes = s.engine.current()?.heartbeatMtimes ?? new Map();
|
|
520
|
+
const live = [];
|
|
521
|
+
for (const [id, heartbeatAt] of heartbeatMtimes) {
|
|
522
|
+
const entry = resolve(s, id);
|
|
523
|
+
if (entry) live.push({ ...entry.row, heartbeatAt });
|
|
524
|
+
}
|
|
525
|
+
const agentHeartbeatByOwner = agentHeartbeatsByOwner(live);
|
|
526
|
+
const overlay = (row) => {
|
|
527
|
+
const agentHeartbeatAt = agentHeartbeatByOwner.get(row.id) || 0;
|
|
528
|
+
return {
|
|
529
|
+
...row,
|
|
530
|
+
heartbeatAt: positiveNumber(heartbeatMtimes.get(row.id), 0),
|
|
531
|
+
...(agentHeartbeatAt > 0 ? { agentHeartbeatAt } : {}),
|
|
532
|
+
};
|
|
533
|
+
};
|
|
534
|
+
const { statements } = s;
|
|
535
|
+
if (!page) return { rows: statements.listAll.all().map((record) => overlay(rowFromDb(record))), more: false };
|
|
536
|
+
const { limit, after } = page;
|
|
537
|
+
const records = after
|
|
538
|
+
? statements.listAfter.all(after.activity, after.activity, after.id, limit + 1)
|
|
539
|
+
: statements.listFirst.all(limit + 1);
|
|
540
|
+
return { rows: records.slice(0, limit).map((record) => overlay(rowFromDb(record))), more: records.length > limit };
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const unavailable = (reason, final = false) => ({ status: 'unavailable', reason, final });
|
|
544
|
+
|
|
545
|
+
function list({ dataDir, page, settings }) {
|
|
546
|
+
const s = ensure(dataDir, settings);
|
|
547
|
+
if (s.phase === 'off') return unavailable(s.reason, s.final);
|
|
548
|
+
if (switchedOff(s)) return unavailable('switch-off');
|
|
549
|
+
if (s.phase !== 'ready') return unavailable(s.phase);
|
|
550
|
+
try {
|
|
551
|
+
const result = s.engine.refresh(s.engine.loadIndex(), { assemble: false });
|
|
552
|
+
if (!result.catalog && !result.absent) return unavailable('storage');
|
|
553
|
+
if (result.fresh || result.absent !== s.absent) {
|
|
554
|
+
startBackfill(s, 'rescan');
|
|
555
|
+
return unavailable(s.phase);
|
|
556
|
+
}
|
|
557
|
+
const ids = result.changedIds || new Set();
|
|
558
|
+
if (ids.size > 0) {
|
|
559
|
+
transaction(s.db, () => writeIds(s, ids));
|
|
560
|
+
scheduleShadow(s);
|
|
561
|
+
}
|
|
562
|
+
return { status: 'ok', ...answer(s, page) };
|
|
563
|
+
} catch (error) {
|
|
564
|
+
fail(s, error, 'listing');
|
|
565
|
+
return unavailable('error');
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/** Storage hints: the daemon's own writes changed these ids' files. They
|
|
570
|
+
* carry no row data; the next listing re-checks the ids from storage. A hint
|
|
571
|
+
* for another data dir than the open one is ignored. */
|
|
572
|
+
function hint({ dataDir, ids }) {
|
|
573
|
+
if (state?.dataDir !== dataDir) return { hinted: 0 };
|
|
574
|
+
state.engine.hint(ids);
|
|
575
|
+
return { hinted: ids.length };
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function status() {
|
|
579
|
+
if (!state) return { phase: 'closed' };
|
|
580
|
+
return {
|
|
581
|
+
phase: state.phase,
|
|
582
|
+
reason: state.reason,
|
|
583
|
+
final: state.final,
|
|
584
|
+
sqliteVersion: state.sqliteVersion,
|
|
585
|
+
rows: state.statements ? state.statements.count.get().n : null,
|
|
586
|
+
lastBackfill: state.lastBackfill,
|
|
587
|
+
lastShadow: state.lastShadow,
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const operations = {
|
|
592
|
+
list,
|
|
593
|
+
hint,
|
|
594
|
+
status,
|
|
595
|
+
/** Run the engine's background verification now. */
|
|
596
|
+
verifyFiles: async ({ dataDir }) => {
|
|
597
|
+
if (state?.dataDir === dataDir) await state.engine.verify();
|
|
598
|
+
return true;
|
|
599
|
+
},
|
|
600
|
+
/** Run a shadow verification now (tests, benchmarks). */
|
|
601
|
+
shadow: ({ dataDir, settings }) => {
|
|
602
|
+
if (state?.dataDir !== dataDir) return null;
|
|
603
|
+
state.settings = settingsOf(settings);
|
|
604
|
+
const report = shadow(state);
|
|
605
|
+
return report && { ...report, phase: state.phase };
|
|
606
|
+
},
|
|
607
|
+
/** Resolves once no backfill or due shadow check is outstanding. */
|
|
608
|
+
settle: async ({ dataDir, settings }) => {
|
|
609
|
+
ensure(dataDir, settings);
|
|
610
|
+
for (;;) {
|
|
611
|
+
if (state?.backfill) await state.backfill;
|
|
612
|
+
else if (state?.shadowTimer && state.shadowDueAt <= Date.now()) {
|
|
613
|
+
await new Promise((resolveTurn) => setTimeout(resolveTurn, 1));
|
|
614
|
+
} else return status();
|
|
615
|
+
}
|
|
616
|
+
},
|
|
617
|
+
close: () => {
|
|
618
|
+
closeState();
|
|
619
|
+
return true;
|
|
620
|
+
},
|
|
621
|
+
/** Test seam: run SQL on the owner connection (the fence keeps others out). */
|
|
622
|
+
testExec: ({ sql, params = [] }) => {
|
|
623
|
+
if (!process.env.NODE_TEST_CONTEXT) throw new Error('testExec is a test seam');
|
|
624
|
+
return Number(state.db.prepare(sql).run(...params).changes);
|
|
625
|
+
},
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
serveWorkerRequests(({ op, ...payload }) => operations[op](payload));
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { SUMMARY_PREFIX, SUMMARY_PREFIX_ANCHOR } from './constants.mjs';
|
|
4
4
|
import { sha16, roleCounts } from './text-utils.mjs';
|
|
5
5
|
import { carryMessageId, withoutMessageIdsReplacer } from '../message-ids.mjs';
|
|
6
|
+
import { isImportedMessage } from '../imported-message.mjs';
|
|
6
7
|
import {
|
|
7
8
|
isInternalRuntimeNotificationText,
|
|
8
9
|
isModelVisibleToolCompletionWrapper,
|
|
@@ -30,6 +31,7 @@ export function makeSummaryMessage(content) {
|
|
|
30
31
|
export function isSummaryMessage(m) {
|
|
31
32
|
return (
|
|
32
33
|
m?.role === 'user' &&
|
|
34
|
+
!isImportedMessage(m) &&
|
|
33
35
|
(String(m?.meta?.source || '') === 'compact-summary' ||
|
|
34
36
|
(typeof m.content === 'string' &&
|
|
35
37
|
m.content.startsWith(SUMMARY_PREFIX_ANCHOR) &&
|
|
@@ -38,7 +40,7 @@ export function isSummaryMessage(m) {
|
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
export function isProtectedContextUserMessage(m) {
|
|
41
|
-
if (m?.role !== 'user' || typeof m.content !== 'string') return false;
|
|
43
|
+
if (m?.role !== 'user' || typeof m.content !== 'string' || isImportedMessage(m)) return false;
|
|
42
44
|
let content = m.content.trim();
|
|
43
45
|
if (!content.toLowerCase().startsWith('<system-reminder>')) return false;
|
|
44
46
|
const closingTag = '</system-reminder>';
|
|
@@ -62,7 +64,7 @@ export function isProtectedContextUserMessage(m) {
|
|
|
62
64
|
// "latest human request" selection (deriveCurrentRequest). The `meta:'skill'`
|
|
63
65
|
// marker is also honoured.
|
|
64
66
|
export function isInjectedSkillBodyMessage(m) {
|
|
65
|
-
if (m?.role !== 'user') return false;
|
|
67
|
+
if (m?.role !== 'user' || isImportedMessage(m)) return false;
|
|
66
68
|
if (m.meta === 'skill') return true;
|
|
67
69
|
return typeof m.content === 'string' && m.content.trimStart().startsWith('<skill>');
|
|
68
70
|
}
|
|
@@ -96,6 +98,8 @@ export function isActualUserInstructionMessage(message) {
|
|
|
96
98
|
return false;
|
|
97
99
|
}
|
|
98
100
|
const text = userMessageText(message);
|
|
101
|
+
// Imported prose is the user's own words whatever it looks like.
|
|
102
|
+
if (isImportedMessage(message)) return text.trim().length > 0;
|
|
99
103
|
if (/^Reference files:\s*/i.test(text.trimStart())) return false;
|
|
100
104
|
if (
|
|
101
105
|
SYNTHETIC_USER_CONTROL_RE.test(text.trimStart()) ||
|