mixdog 1.2.1 → 1.2.3
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 +18 -0
- package/package.json +2 -1
- package/scripts/gate-local.mjs +19 -3
- package/scripts/model-catalog-audit.mjs +1 -15
- package/scripts/provider-pingpong-live.mjs +509 -0
- package/src/agents/advisor/AGENT.md +1 -1
- package/src/agents/advisor/agent.json +1 -1
- package/src/agents/reviewer/AGENT.md +3 -3
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +6 -0
- package/src/defaults/skills/setup/SKILL.md +6 -1
- package/src/defaults/skills/setup/references/actions.md +44 -22
- package/src/defaults/skills/setup/references/surfaces.md +10 -8
- package/src/headless-exec.mjs +15 -8
- package/src/lib/keychain-cjs.cjs +7 -4
- package/src/output-styles/common.md +11 -10
- package/src/repl/slash-commands.mjs +16 -1
- package/src/rules/agent/AGENT.md +5 -5
- package/src/rules/lead/LEAD.md +5 -5
- 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/agent-trace-io.mjs +92 -61
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
- package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
- package/src/runtime/agent/orchestrator/config.mjs +5 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +36 -7
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +49 -10
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +40 -3
- package/src/runtime/agent/orchestrator/mcp/client.mjs +230 -96
- 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/providers/account-pool.mjs +188 -29
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
- package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +98 -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 +48 -37
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +10 -7
- 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 +102 -45
- 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-turn/turn-blocks.mjs +20 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +40 -7
- 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 +88 -21
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +30 -10
- package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +106 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +94 -15
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +117 -72
- package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +75 -10
- 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 +103 -23
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +32 -6
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +27 -3
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
- package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
- package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +80 -18
- package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +46 -8
- package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +273 -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-request-body.mjs +41 -5
- 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 +369 -112
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +140 -35
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +70 -24
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +80 -29
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +69 -19
- package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
- package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
- package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
- package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
- 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-channel.d.mts +1 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +123 -61
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +27 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
- 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 +47 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +64 -23
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +79 -33
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +144 -15
- 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 +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +109 -26
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +27 -6
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +34 -20
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +23 -4
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +37 -28
- 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/output-items.mjs +23 -10
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +43 -15
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +5 -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 +7 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
- 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-model-capabilities.mjs +80 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +37 -4
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +12 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +12 -1
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +76 -33
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
- package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
- package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
- 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-stream/event-switch.mjs +12 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
- package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +41 -22
- package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
- package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +720 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +123 -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/providers/transport-scope.d.mts +6 -0
- package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
- package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
- package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
- package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
- package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
- 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 +1 -97
- package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
- package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
- package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
- package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
- package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
- package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
- package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
- package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +67 -20
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +104 -19
- package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +30 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +57 -21
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +5 -10
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +16 -4
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
- package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
- package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +168 -29
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
- package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
- package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
- 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-archived-agent.mjs +34 -15
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
- package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +20 -1
- package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
- package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
- package/src/runtime/computer-bridge/action-schema.mjs +44 -21
- package/src/runtime/github/tool.mjs +92 -2
- package/src/runtime/memory/index.mjs +1 -0
- package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
- package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
- package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
- package/src/runtime/memory/lib/pg/process.mjs +65 -50
- package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +124 -16
- package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
- package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
- package/src/runtime/office/README.md +2 -2
- package/src/runtime/office/authoring/html-document-author.mjs +2 -0
- package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
- package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
- package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
- package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
- package/src/runtime/office/core/office-actions-read.mjs +3 -0
- package/src/runtime/office/core/office-render-preview.mjs +2 -0
- package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
- package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
- package/src/runtime/office/core/office-sessions.mjs +5 -0
- package/src/runtime/office/core/office-transactions.mjs +3 -0
- package/src/runtime/office/core/office-write-ledger.mjs +98 -0
- package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
- package/src/runtime/office/index.mjs +30 -0
- package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
- package/src/runtime/office/pdf/pdf-render.mjs +23 -7
- package/src/runtime/office/pdf/pdf-security.mjs +2 -0
- package/src/runtime/office/portable/portable-opc.mjs +4 -1
- package/src/runtime/office/quality/visual-diff.mjs +2 -0
- package/src/runtime/office/shared/values.mjs +0 -5
- package/src/runtime/shared/background-tasks.mjs +92 -14
- package/src/runtime/shared/bounded-file-read.mjs +13 -0
- package/src/runtime/shared/bounded-line-diff.mjs +182 -0
- package/src/runtime/shared/buffered-appender.mjs +18 -0
- package/src/runtime/shared/config.mjs +78 -18
- package/src/runtime/shared/err-text.mjs +31 -0
- package/src/runtime/shared/error-presentation.mjs +159 -19
- package/src/runtime/shared/file-permissions.d.mts +6 -1
- package/src/runtime/shared/file-permissions.mjs +54 -6
- package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
- package/src/runtime/shared/llm/anthropic-effort.mjs +129 -134
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
- package/src/runtime/shared/llm/model-catalog.mjs +53 -40
- package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +11 -0
- package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
- package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
- package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
- package/src/runtime/shared/loopback-listener.mjs +9 -0
- package/src/runtime/shared/model-display.mjs +13 -0
- package/src/runtime/shared/provider-api-key.mjs +2 -0
- package/src/runtime/shared/quota-resume-store.mjs +11 -0
- package/src/runtime/shared/review-diff.mjs +52 -0
- package/src/runtime/shared/session-baseline-store.mjs +960 -0
- package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
- package/src/runtime/shared/session-sidebar-host.mjs +38 -0
- package/src/runtime/shared/slow-span.mjs +114 -0
- package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
- package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
- package/src/runtime/shared/task-notification-envelope.mjs +2 -2
- package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
- package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
- package/src/runtime/shared/tool-card-model.mjs +5 -2
- package/src/runtime/shared/tool-output-limit.mjs +4 -0
- package/src/runtime/shared/tool-primitives.mjs +13 -0
- package/src/runtime/shared/tool-result-summary.mjs +8 -1
- package/src/runtime/shared/tool-surface.mjs +18 -3
- package/src/runtime/shared/tool-work-units.mjs +40 -4
- package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
- package/src/runtime/shared/transcript-writer.mjs +10 -2
- package/src/runtime/shared/turn-changes.mjs +670 -274
- package/src/runtime/shared/turn-review-reply.mjs +8 -39
- package/src/runtime/shared/turn-snapshot-store.mjs +94 -2
- package/src/runtime/shared/turn-snapshot.mjs +986 -690
- package/src/runtime/shared/turn-worktree-snapshot.mjs +279 -262
- package/src/runtime/shared/user-data-guard.mjs +262 -49
- package/src/runtime/shared/worktrees/carry.mjs +531 -0
- package/src/runtime/shared/worktrees/contract.mjs +34 -24
- package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
- package/src/runtime/shared/worktrees/git-head.mjs +205 -0
- package/src/runtime/shared/worktrees/git.mjs +8 -3
- package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
- package/src/runtime/shared/worktrees/manager.mjs +156 -8
- package/src/runtime/shared/worktrees/merge.mjs +31 -21
- package/src/runtime/shared/worktrees/paths.d.mts +6 -0
- package/src/runtime/shared/worktrees/test-support.mjs +19 -4
- package/src/runtime/terminal-bridge/client.mjs +44 -11
- package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
- package/src/runtime/tidy/apply.mjs +47 -2
- package/src/runtime/tidy/run-engines.mjs +11 -5
- package/src/session-runtime/agent-tool-routing.mjs +19 -13
- package/src/session-runtime/boot/apis.mjs +19 -5
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/infrastructure.mjs +12 -1
- package/src/session-runtime/boot/session.mjs +0 -1
- package/src/session-runtime/boot/tools.mjs +4 -1
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
- package/src/session-runtime/internal-tool-executor.mjs +6 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
- package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
- package/src/session-runtime/lifecycle/session-catalog.mjs +14 -3
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
- package/src/session-runtime/lifecycle/teardown.mjs +73 -8
- package/src/session-runtime/lifecycle-api.mjs +1 -0
- package/src/session-runtime/mcp-glue/connect.mjs +46 -14
- package/src/session-runtime/mcp-glue/server-catalog.mjs +20 -3
- package/src/session-runtime/mcp-glue.mjs +3 -0
- 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 +85 -17
- 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/native-web-search-usage.mjs +51 -0
- package/src/session-runtime/native-web-search.mjs +10 -1
- package/src/session-runtime/new-session-config.mjs +4 -2
- package/src/session-runtime/provider-auth/login.mjs +4 -0
- package/src/session-runtime/provider-models/model-meta.mjs +12 -3
- 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 +4 -0
- package/src/session-runtime/resource-mcp-api.mjs +98 -2
- package/src/session-runtime/resource-scope-decorate.mjs +10 -3
- package/src/session-runtime/runtime-review-api.mjs +21 -2
- package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
- 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/lead-worker-index/forgotten-leads.mjs +29 -0
- package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
- 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/tag-registry.mjs +15 -1
- 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-index/row-store.mjs +10 -1
- package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
- package/src/session-runtime/services/agent-tool.mjs +3 -0
- package/src/session-runtime/services/channel-admin.mjs +19 -3
- package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
- package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
- package/src/session-runtime/services/provider-admin.mjs +81 -15
- package/src/session-runtime/session-transcript-purge.mjs +32 -0
- package/src/session-runtime/session-turn-api.mjs +14 -5
- package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
- package/src/session-runtime/settings-compaction-api.mjs +1 -0
- package/src/session-runtime/setup-tool/executor.mjs +36 -6
- package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
- package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
- package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
- package/src/session-runtime/skills-api.mjs +3 -5
- package/src/session-runtime/tool-surface.mjs +5 -1
- package/src/session-runtime/turn/compaction-gate.mjs +25 -0
- package/src/session-runtime/turn/session-ops.mjs +7 -5
- package/src/session-runtime/turn/side-question-ops.mjs +22 -0
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
- package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
- package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
- package/src/session-runtime/turn/turn-run.mjs +25 -9
- package/src/session-runtime/usage-stats-api.mjs +43 -11
- package/src/session-runtime/workspace/project-branches.mjs +7 -5
- package/src/session-runtime/workspace/transition-steps.mjs +129 -31
- package/src/session-runtime/workspace/transitions.mjs +190 -15
- package/src/standalone/daemon-agent-control.mjs +9 -1
- package/src/standalone/daemon-drain-policy.mjs +8 -0
- package/src/standalone/daemon-local-session-bridge.mjs +14 -1
- package/src/standalone/daemon-owner.mjs +17 -4
- package/src/standalone/daemon-pg-stop.mjs +51 -0
- package/src/standalone/daemon-stop-budget.d.mts +2 -0
- package/src/standalone/daemon-stop-budget.mjs +18 -0
- package/src/standalone/daemon-stored-session-views.mjs +2 -0
- package/src/standalone/daemon-telemetry.mjs +74 -0
- package/src/standalone/daemon.mjs +64 -8
- package/src/standalone/desktop-service-registry.mjs +5 -0
- package/src/standalone/local-session-runtime.mjs +70 -19
- package/src/standalone/session-client.mjs +42 -0
- package/src/standalone/session-protocol.mjs +14 -0
- package/src/standalone/session-proxy/projection.mjs +41 -4
- package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
- package/src/standalone/session-runtime-inline-host.mjs +1 -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 +18 -3
- package/src/standalone/session-service-api.mjs +11 -1
- package/src/standalone/session-service.mjs +1 -0
- package/src/standalone/session-state-patch.d.mts +2 -1
- package/src/standalone/session-state-patch.mjs +41 -1
- package/src/tui/App.jsx +25 -0
- package/src/tui/app/app-view/floating-panel.jsx +26 -1
- package/src/tui/app/app-view.jsx +1 -0
- package/src/tui/app/btw-controller.mjs +118 -0
- package/src/tui/app/create-app-pickers.mjs +6 -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.mjs +2 -1
- package/src/tui/app/model-options.mjs +11 -16
- package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
- package/src/tui/app/shell-layout/surfaces.mjs +3 -1
- package/src/tui/app/slash-commands.mjs +20 -5
- package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
- package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
- package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
- package/src/tui/app/use-global-key-input.mjs +5 -12
- package/src/tui/app/use-prompt-handlers.mjs +11 -0
- package/src/tui/components/BtwPanel.jsx +53 -0
- package/src/tui/components/QuestionPanel.jsx +275 -0
- package/src/tui/components/StatusLine.jsx +2 -0
- package/src/tui/components/question-panel-model.mjs +156 -0
- 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/dist/index.mjs +3221 -1500
- package/src/tui/session/context-state.mjs +4 -2
- package/src/tui/session/quota-resume.mjs +46 -14
- package/src/tui/session/session-api/commands.mjs +43 -24
- package/src/tui/session/session-api/controls.mjs +2 -2
- package/src/tui/session/session-api/extensions.mjs +22 -0
- package/src/tui/session/session-api/integrations.mjs +16 -2
- package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
- package/src/tui/session/session-api/lifecycle.mjs +7 -2
- package/src/tui/session/session-api/routes.mjs +71 -7
- package/src/tui/session/session-api/settings.mjs +11 -14
- package/src/tui/session/session-api.mjs +1 -0
- package/src/tui/session/session-flow/drain.mjs +7 -1
- package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
- package/src/tui/session/tool-approval.mjs +10 -7
- package/src/tui/session/transcript-spill.mjs +14 -7
- package/src/tui/session/turn-stream.mjs +174 -31
- package/src/tui/session/turn.mjs +46 -16
- 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/workflows/default/WORKFLOW.md +3 -1
- package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
- package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
|
@@ -5,26 +5,29 @@ import { sleep as delay } from '../../../shared/sleep.mjs';
|
|
|
5
5
|
//
|
|
6
6
|
// Public API:
|
|
7
7
|
// startPg({ runtimeDir, pgdataDir, port?, logPath? }) → { pid, port }
|
|
8
|
-
// stopPg({ runtimeDir, pgdataDir })
|
|
8
|
+
// stopPg({ runtimeDir, pgdataDir, timeoutMs? }) → void
|
|
9
9
|
// healthcheckPg({ port, host? }) → boolean
|
|
10
|
+
//
|
|
11
|
+
// Every child on the start/stop path (initdb, pg_ctl start/reload/status/stop)
|
|
12
|
+
// runs async; only stopPgSync — a process 'exit' hook — may block.
|
|
10
13
|
|
|
11
14
|
import {
|
|
12
15
|
existsSync,
|
|
13
16
|
mkdirSync,
|
|
14
17
|
mkdtempSync,
|
|
15
|
-
readdirSync,
|
|
16
18
|
readFileSync,
|
|
17
19
|
rmdirSync,
|
|
18
20
|
rmSync,
|
|
19
|
-
statSync,
|
|
20
21
|
symlinkSync,
|
|
21
22
|
unlinkSync,
|
|
22
23
|
writeFileSync,
|
|
23
24
|
} from 'node:fs';
|
|
25
|
+
import { readdir, readFile, rm, stat } from 'node:fs/promises';
|
|
24
26
|
import { join } from 'node:path';
|
|
25
|
-
import { spawn, spawnSync } from 'node:child_process';
|
|
27
|
+
import { execFile, spawn, spawnSync } from 'node:child_process';
|
|
26
28
|
import { createConnection, createServer } from 'node:net';
|
|
27
29
|
import { isPidAlive } from '../../../shared/pid-liveness.mjs';
|
|
30
|
+
import { runChild } from './run-child.mjs';
|
|
28
31
|
|
|
29
32
|
// ---------------------------------------------------------------------------
|
|
30
33
|
// Helpers
|
|
@@ -202,14 +205,12 @@ function ensureConfV2(pgdataDir) {
|
|
|
202
205
|
// callers route through this rather than re-implementing the conf/reload pair.
|
|
203
206
|
// Reload-compatible settings apply immediately; postmaster settings such as
|
|
204
207
|
// max_connections apply on the next normal PG start (never restart live work).
|
|
205
|
-
export function reconcileConfV2(runtimeDir, pgdataDir) {
|
|
208
|
+
export async function reconcileConfV2(runtimeDir, pgdataDir) {
|
|
206
209
|
const applied = ensureConfV2(pgdataDir);
|
|
207
210
|
if (!applied) return { applied: false, reloaded: false };
|
|
208
|
-
const reload =
|
|
211
|
+
const reload = await runChild(pgBin(runtimeDir, 'pg_ctl'), ['reload', '-D', pgdataDir], {
|
|
209
212
|
env: libEnv(runtimeDir),
|
|
210
|
-
|
|
211
|
-
timeout: 5_000,
|
|
212
|
-
windowsHide: true,
|
|
213
|
+
timeoutMs: 5_000,
|
|
213
214
|
});
|
|
214
215
|
const reloaded = reload.status === 0;
|
|
215
216
|
if (reloaded) {
|
|
@@ -239,7 +240,7 @@ async function attachExistingPostmaster({ runtimeDir, pgdataDir, existingWaitMs,
|
|
|
239
240
|
__mixdogMemoryLog(`[pg-process] attaching to existing PG pid=${existing.pid} port=${existing.port}\n`);
|
|
240
241
|
// Route through the single reconcile entry point so v1 → v2 conf
|
|
241
242
|
// upgrades land on already-running instances without restart.
|
|
242
|
-
if (v2Applied) reconcileConfV2(runtimeDir, pgdataDir);
|
|
243
|
+
if (v2Applied) await reconcileConfV2(runtimeDir, pgdataDir);
|
|
243
244
|
return { pid: existing.pid, port: existing.port, attached: true };
|
|
244
245
|
}
|
|
245
246
|
if (existing.state === 'alive-not-ready') {
|
|
@@ -260,6 +261,10 @@ async function attachExistingPostmaster({ runtimeDir, pgdataDir, existingWaitMs,
|
|
|
260
261
|
// default_transaction_isolation: native PG default is read committed.
|
|
261
262
|
// Native PG does not default to serializable; set isolation level explicitly
|
|
262
263
|
// so behaviour is unambiguous across PG major versions.
|
|
264
|
+
// Carries every versioned block ensureConfV2 checks for, so a fresh cluster
|
|
265
|
+
// already holds what later boots expect: none of them rewrites the conf and
|
|
266
|
+
// pg_ctl-reloads it (a postmaster-only setting such as shared_buffers cannot
|
|
267
|
+
// change by reload, and every such reload logged an error).
|
|
263
268
|
function freshClusterConfAppend() {
|
|
264
269
|
return (
|
|
265
270
|
[
|
|
@@ -280,7 +285,8 @@ function freshClusterConfAppend() {
|
|
|
280
285
|
].join('\n') +
|
|
281
286
|
'\n' +
|
|
282
287
|
buildV2Block() +
|
|
283
|
-
buildV3Block()
|
|
288
|
+
buildV3Block() +
|
|
289
|
+
buildV4Block()
|
|
284
290
|
);
|
|
285
291
|
}
|
|
286
292
|
|
|
@@ -291,14 +297,14 @@ function freshClusterConfAppend() {
|
|
|
291
297
|
* user profile). Such a share dir is passed through an ASCII junction under
|
|
292
298
|
* %ProgramData% that is removed afterwards.
|
|
293
299
|
*/
|
|
294
|
-
export function withInitdbShareDir(runtimeDir, run) {
|
|
300
|
+
export async function withInitdbShareDir(runtimeDir, run) {
|
|
295
301
|
const shareDir = join(runtimeDir, 'share');
|
|
296
302
|
if (process.platform !== 'win32' || /^[\x20-\x7e]*$/.test(shareDir)) return run(null);
|
|
297
303
|
const linkRoot = mkdtempSync(join(process.env.ProgramData, 'mixdog-initdb-'));
|
|
298
304
|
const link = join(linkRoot, 'share');
|
|
299
305
|
try {
|
|
300
306
|
symlinkSync(shareDir, link, 'junction');
|
|
301
|
-
return run(link);
|
|
307
|
+
return await run(link);
|
|
302
308
|
} finally {
|
|
303
309
|
// Cleanup must not replace initdb's own result or error. Both removals are
|
|
304
310
|
// non-recursive, so the junction target (the runtime share) is never touched.
|
|
@@ -313,11 +319,11 @@ export function withInitdbShareDir(runtimeDir, run) {
|
|
|
313
319
|
}
|
|
314
320
|
|
|
315
321
|
/** initdb if pgdata is not yet initialised (no PG_VERSION file). */
|
|
316
|
-
function initClusterIfNeeded({ runtimeDir, pgdataDir, env }) {
|
|
322
|
+
async function initClusterIfNeeded({ runtimeDir, pgdataDir, env }) {
|
|
317
323
|
if (existsSync(join(pgdataDir, 'PG_VERSION'))) return;
|
|
318
324
|
__mixdogMemoryLog(`[pg-process] initdb → ${pgdataDir}\n`);
|
|
319
|
-
const r = withInitdbShareDir(runtimeDir, (shareDir) =>
|
|
320
|
-
|
|
325
|
+
const r = await withInitdbShareDir(runtimeDir, (shareDir) =>
|
|
326
|
+
runChild(
|
|
321
327
|
pgBin(runtimeDir, 'initdb'),
|
|
322
328
|
[
|
|
323
329
|
'-D',
|
|
@@ -330,7 +336,7 @@ function initClusterIfNeeded({ runtimeDir, pgdataDir, env }) {
|
|
|
330
336
|
'-U',
|
|
331
337
|
'postgres',
|
|
332
338
|
],
|
|
333
|
-
{ env
|
|
339
|
+
{ env }
|
|
334
340
|
)
|
|
335
341
|
);
|
|
336
342
|
if (r.status !== 0) {
|
|
@@ -436,12 +442,7 @@ async function startAndWaitReady({ pgctl, startArgs, env, pgdataDir, port }) {
|
|
|
436
442
|
*/
|
|
437
443
|
async function attachRaceWinner({ pgdataDir, pgctl, env, existingWaitMs }) {
|
|
438
444
|
__mixdogMemoryLog(`[pg-process] pg_ctl start: "another server might be running" — probing status\n`);
|
|
439
|
-
const statusR =
|
|
440
|
-
env,
|
|
441
|
-
stdio: 'pipe',
|
|
442
|
-
timeout: 3_000,
|
|
443
|
-
windowsHide: true,
|
|
444
|
-
});
|
|
445
|
+
const statusR = await runChild(pgctl, ['status', '-D', pgdataDir], { env, timeoutMs: 3_000 });
|
|
445
446
|
__mixdogMemoryLog(
|
|
446
447
|
`[pg-process] pg_ctl status: ${statusR.stdout?.toString() || statusR.stderr?.toString() || 'no output'}\n`
|
|
447
448
|
);
|
|
@@ -481,7 +482,7 @@ export async function startPg({
|
|
|
481
482
|
if (attached) return attached;
|
|
482
483
|
|
|
483
484
|
const pgctl = pgBin(runtimeDir, 'pg_ctl');
|
|
484
|
-
initClusterIfNeeded({ runtimeDir, pgdataDir, env });
|
|
485
|
+
await initClusterIfNeeded({ runtimeDir, pgdataDir, env });
|
|
485
486
|
// PostgreSQL requires a completely empty target on first init. Creating the
|
|
486
487
|
// Spotlight marker before initdb makes every fresh macOS cluster fail with
|
|
487
488
|
// "directory exists but is not empty"; add it only after initialization.
|
|
@@ -512,15 +513,18 @@ export async function startPg({
|
|
|
512
513
|
// stopPg
|
|
513
514
|
// ---------------------------------------------------------------------------
|
|
514
515
|
|
|
515
|
-
|
|
516
|
+
// Bounded graceful stop. `-m fast` rolls back open transactions and
|
|
517
|
+
// disconnects clients; pg_ctl's own wait (-t, whole seconds) and the child
|
|
518
|
+
// timeout share `timeoutMs`, so a wedged stop rejects instead of holding a
|
|
519
|
+
// shutdown sequence open.
|
|
520
|
+
export async function stopPg({ runtimeDir, pgdataDir, timeoutMs = 15_000 }) {
|
|
516
521
|
const pgctl = pgBin(runtimeDir, 'pg_ctl');
|
|
517
522
|
const env = libEnv(runtimeDir);
|
|
523
|
+
const waitSeconds = Math.max(1, Math.ceil(timeoutMs / 1000));
|
|
518
524
|
|
|
519
|
-
const r =
|
|
525
|
+
const r = await runChild(pgctl, ['stop', '-m', 'fast', '-w', '-t', String(waitSeconds), '-D', pgdataDir], {
|
|
520
526
|
env,
|
|
521
|
-
|
|
522
|
-
timeout: 15_000,
|
|
523
|
-
windowsHide: true,
|
|
527
|
+
timeoutMs: waitSeconds * 1000 + 1_000,
|
|
524
528
|
});
|
|
525
529
|
|
|
526
530
|
if (r.status !== 0) {
|
|
@@ -607,22 +611,36 @@ export function classifyOrphanTempPostmaster({
|
|
|
607
611
|
return Number(uptimeSec) >= Math.max(60, Number(minUptimeSec) || ORPHAN_TEMP_PG_MIN_UPTIME_SEC);
|
|
608
612
|
}
|
|
609
613
|
|
|
610
|
-
function tempPostmasterOwnerAlive(args) {
|
|
614
|
+
async function tempPostmasterOwnerAlive(args) {
|
|
611
615
|
const rawDataDir = pgDataDirFromArgs(args);
|
|
612
616
|
if (!rawDataDir) return false;
|
|
613
617
|
const dataRoot = join(rawDataDir, '..');
|
|
614
618
|
for (const name of ['memory-runtime-owner.json', 'daemon-owner.json']) {
|
|
615
619
|
try {
|
|
616
|
-
const owner = JSON.parse(
|
|
620
|
+
const owner = JSON.parse(await readFile(join(dataRoot, name), 'utf8'));
|
|
617
621
|
if (Number(owner?.pid) > 0 && isPidAlive(Number(owner.pid))) return true;
|
|
618
622
|
} catch {}
|
|
619
623
|
}
|
|
620
624
|
return false;
|
|
621
625
|
}
|
|
622
626
|
|
|
623
|
-
|
|
627
|
+
// Async child run: stdout on exit 0, null on any failure/timeout (the old
|
|
628
|
+
// spawnSync `status !== 0` contract). Never blocks the event loop.
|
|
629
|
+
function execFileText(file, args, options) {
|
|
630
|
+
return new Promise((resolve) => {
|
|
631
|
+
try {
|
|
632
|
+
execFile(file, args, { encoding: 'utf8', ...options }, (error, stdout) => {
|
|
633
|
+
resolve(error ? null : String(stdout || ''));
|
|
634
|
+
});
|
|
635
|
+
} catch {
|
|
636
|
+
resolve(null);
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
async function listPostmasterRows() {
|
|
624
642
|
if (process.platform === 'win32') {
|
|
625
|
-
const
|
|
643
|
+
const stdout = await execFileText(
|
|
626
644
|
'powershell',
|
|
627
645
|
[
|
|
628
646
|
'-NoProfile',
|
|
@@ -632,10 +650,10 @@ function listPostmasterRows() {
|
|
|
632
650
|
'started = [int](New-TimeSpan -Start $_.CreationDate -End (Get-Date)).TotalSeconds; ' +
|
|
633
651
|
'args = $_.CommandLine } }',
|
|
634
652
|
],
|
|
635
|
-
{
|
|
653
|
+
{ timeout: 15_000, windowsHide: true }
|
|
636
654
|
);
|
|
637
|
-
if (
|
|
638
|
-
return
|
|
655
|
+
if (stdout === null) return [];
|
|
656
|
+
return stdout
|
|
639
657
|
.split(/\r?\n/)
|
|
640
658
|
.filter(Boolean)
|
|
641
659
|
.flatMap((line) => {
|
|
@@ -647,12 +665,9 @@ function listPostmasterRows() {
|
|
|
647
665
|
}
|
|
648
666
|
});
|
|
649
667
|
}
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
});
|
|
654
|
-
if (result.status !== 0) return [];
|
|
655
|
-
return String(result.stdout || '')
|
|
668
|
+
const stdout = await execFileText('ps', ['-eo', 'pid=,etimes=,args='], { timeout: 15_000 });
|
|
669
|
+
if (stdout === null) return [];
|
|
670
|
+
return stdout
|
|
656
671
|
.split(/\r?\n/)
|
|
657
672
|
.flatMap((line) => {
|
|
658
673
|
const match = line.match(/^\s*(\d+)\s+(\d+)\s+(.*postgres.*)$/);
|
|
@@ -679,11 +694,11 @@ function pristineRootForArgs(args, tempRoot) {
|
|
|
679
694
|
// (observed: 87 roots, 6.1GB, alongside 57 live postmasters). Roots a live
|
|
680
695
|
// postmaster still owns are kept, and the same uptime margin that protects a
|
|
681
696
|
// running harness from the process sweep protects its directory here.
|
|
682
|
-
function sweepAbandonedPristineRoots(tempRoot, liveRoots, minAgeMs) {
|
|
697
|
+
async function sweepAbandonedPristineRoots(tempRoot, liveRoots, minAgeMs) {
|
|
683
698
|
let removed = 0;
|
|
684
699
|
let entries;
|
|
685
700
|
try {
|
|
686
|
-
entries =
|
|
701
|
+
entries = await readdir(tempRoot, { withFileTypes: true });
|
|
687
702
|
} catch {
|
|
688
703
|
return 0;
|
|
689
704
|
}
|
|
@@ -693,12 +708,12 @@ function sweepAbandonedPristineRoots(tempRoot, liveRoots, minAgeMs) {
|
|
|
693
708
|
const full = join(tempRoot, entry.name);
|
|
694
709
|
if (liveRoots.has(normalizePathForMatch(full))) continue;
|
|
695
710
|
try {
|
|
696
|
-
if (
|
|
711
|
+
if ((await stat(full)).mtimeMs > cutoff) continue;
|
|
697
712
|
} catch {
|
|
698
713
|
continue;
|
|
699
714
|
}
|
|
700
715
|
try {
|
|
701
|
-
|
|
716
|
+
await rm(full, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
|
|
702
717
|
removed += 1;
|
|
703
718
|
} catch {
|
|
704
719
|
/* a process this sweep did not reap still holds it */
|
|
@@ -709,18 +724,18 @@ function sweepAbandonedPristineRoots(tempRoot, liveRoots, minAgeMs) {
|
|
|
709
724
|
|
|
710
725
|
/** Best-effort machine hygiene: reap orphan temp postmasters, then drop the
|
|
711
726
|
* throwaway roots no live postmaster owns. Runs periodically, not once. */
|
|
712
|
-
export function sweepOrphanTempPostmasters({ tempRoot, minUptimeSec } = {}) {
|
|
727
|
+
export async function sweepOrphanTempPostmasters({ tempRoot, minUptimeSec } = {}) {
|
|
713
728
|
let reaped = 0;
|
|
714
729
|
try {
|
|
715
730
|
const root = tempRoot || process.env.TMPDIR || process.env.TEMP || process.env.TMP || '/tmp';
|
|
716
731
|
const liveRoots = new Set();
|
|
717
|
-
for (const row of listPostmasterRows()) {
|
|
732
|
+
for (const row of await listPostmasterRows()) {
|
|
718
733
|
if (!row.pid || row.pid === process.pid) continue;
|
|
719
734
|
if (
|
|
720
735
|
!classifyOrphanTempPostmaster({
|
|
721
736
|
...row,
|
|
722
737
|
tempRoot: root,
|
|
723
|
-
ownerAlive: tempPostmasterOwnerAlive(row.args),
|
|
738
|
+
ownerAlive: await tempPostmasterOwnerAlive(row.args),
|
|
724
739
|
minUptimeSec,
|
|
725
740
|
})
|
|
726
741
|
) {
|
|
@@ -737,7 +752,7 @@ export function sweepOrphanTempPostmasters({ tempRoot, minUptimeSec } = {}) {
|
|
|
737
752
|
}
|
|
738
753
|
}
|
|
739
754
|
const minAgeMs = Math.max(60, Number(minUptimeSec) || ORPHAN_TEMP_PG_MIN_UPTIME_SEC) * 1000;
|
|
740
|
-
const removed = sweepAbandonedPristineRoots(root, liveRoots, minAgeMs);
|
|
755
|
+
const removed = await sweepAbandonedPristineRoots(root, liveRoots, minAgeMs);
|
|
741
756
|
if (removed > 0) __mixdogMemoryLog(`[pg-process] removed ${removed} abandoned pristine root(s)\n`);
|
|
742
757
|
} catch {
|
|
743
758
|
/* hygiene is best-effort */
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Async child run with spawnSync's result shape ({ status, signal, stdout,
|
|
5
|
+
* stderr, error }) so the PG runtime's tar / initdb / pg_ctl steps never block
|
|
6
|
+
* the event loop of the process hosting them (the daemon serves every client
|
|
7
|
+
* from one loop). `timeoutMs` > 0 kills the child and reports ETIMEDOUT, like
|
|
8
|
+
* spawnSync's `timeout`. Never rejects.
|
|
9
|
+
*/
|
|
10
|
+
export function runChild(file, args, { timeoutMs = 0, ...options } = {}) {
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
const stdout = [];
|
|
13
|
+
const stderr = [];
|
|
14
|
+
let settled = false;
|
|
15
|
+
let timer = null;
|
|
16
|
+
let error = null;
|
|
17
|
+
const finish = (status, signal) => {
|
|
18
|
+
if (settled) return;
|
|
19
|
+
settled = true;
|
|
20
|
+
if (timer) clearTimeout(timer);
|
|
21
|
+
resolve({ status, signal, stdout: Buffer.concat(stdout), stderr: Buffer.concat(stderr), error });
|
|
22
|
+
};
|
|
23
|
+
let child;
|
|
24
|
+
try {
|
|
25
|
+
child = spawn(file, args, { windowsHide: true, ...options, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
26
|
+
} catch (spawnError) {
|
|
27
|
+
error = spawnError;
|
|
28
|
+
finish(null, null);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
child.stdout?.on('data', (chunk) => stdout.push(chunk));
|
|
32
|
+
child.stderr?.on('data', (chunk) => stderr.push(chunk));
|
|
33
|
+
child.once('error', (childError) => {
|
|
34
|
+
error ??= childError;
|
|
35
|
+
finish(null, null);
|
|
36
|
+
});
|
|
37
|
+
// 'close' (not 'exit'): stdio is flushed, so captured output is complete.
|
|
38
|
+
child.once('close', (status, signal) => finish(status, signal));
|
|
39
|
+
if (timeoutMs > 0) {
|
|
40
|
+
timer = setTimeout(() => {
|
|
41
|
+
error = Object.assign(new Error(`${file} timed out after ${timeoutMs}ms`), { code: 'ETIMEDOUT' });
|
|
42
|
+
try {
|
|
43
|
+
child.kill();
|
|
44
|
+
} catch {}
|
|
45
|
+
finish(null, 'SIGTERM');
|
|
46
|
+
}, timeoutMs);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -6,6 +6,9 @@ import { __mixdogMemoryLog } from '../memory-log.mjs';
|
|
|
6
6
|
* Public API:
|
|
7
7
|
* ensurePgInstance(dataDir) → Promise<{ host, port, runtimeDir, pgdataDir }>
|
|
8
8
|
* stopPgForShutdown() → Promise<void> — call from server-main.mjs shutdown()
|
|
9
|
+
* stopOwnedPgForShutdown() → Promise<boolean> — a host stopping the cluster it ensured
|
|
10
|
+
* installOwnedPgExitStop() → void — last-chance sync stop on process exit
|
|
11
|
+
* pgClusterExists(dataDir) → boolean — initdb already ran for dataDir
|
|
9
12
|
*
|
|
10
13
|
* Uses process.mjs for startPg / stopPg / healthcheckPg (lazy-imported).
|
|
11
14
|
*
|
|
@@ -17,7 +20,7 @@ import { __mixdogMemoryLog } from '../memory-log.mjs';
|
|
|
17
20
|
*/
|
|
18
21
|
|
|
19
22
|
import { createServer } from 'node:net';
|
|
20
|
-
import { unlinkSync, readFileSync, writeFileSync, renameSync, statSync, mkdirSync } from 'node:fs';
|
|
23
|
+
import { existsSync, unlinkSync, readFileSync, writeFileSync, renameSync, statSync, mkdirSync } from 'node:fs';
|
|
21
24
|
import { join, resolve } from 'node:path';
|
|
22
25
|
import {
|
|
23
26
|
discoveryPath as _pgDiscoveryPath,
|
|
@@ -51,6 +54,10 @@ let _live = null;
|
|
|
51
54
|
let _ensureInFlight = null;
|
|
52
55
|
/** Per-process flag — postgresql.conf reconcile attempted once per supervisor. */
|
|
53
56
|
let _v2ReconcileTried = false;
|
|
57
|
+
/** Set by stopOwnedPgForShutdown: this process is exiting, no further ensure. */
|
|
58
|
+
let _ownedShutdownStarted = false;
|
|
59
|
+
/** installOwnedPgExitStop registers its 'exit' hook once. */
|
|
60
|
+
let _ownedExitStopInstalled = false;
|
|
54
61
|
|
|
55
62
|
// ── Constants ────────────────────────────────────────────────────────────────
|
|
56
63
|
const PG_PORT_MIN = 55432;
|
|
@@ -372,6 +379,16 @@ function readPostmasterInfo(pgdata) {
|
|
|
372
379
|
}
|
|
373
380
|
}
|
|
374
381
|
|
|
382
|
+
// Async child run (never blocks the daemon loop): stdout on exit 0, else null.
|
|
383
|
+
async function _execFileText(file, args) {
|
|
384
|
+
const { execFile } = await import('node:child_process');
|
|
385
|
+
return new Promise((resolveText) => {
|
|
386
|
+
execFile(file, args, { encoding: 'utf8', windowsHide: true, timeout: 15_000 }, (error, stdout) => {
|
|
387
|
+
resolveText(error ? null : String(stdout || ''));
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
375
392
|
/**
|
|
376
393
|
* Best-effort process-name check: confirms the pid's comm name contains 'postgres'.
|
|
377
394
|
* Falls back to true (alive) when the name cannot be determined.
|
|
@@ -383,19 +400,12 @@ async function isPostgresPid(pid) {
|
|
|
383
400
|
return comm.includes('postgres');
|
|
384
401
|
}
|
|
385
402
|
if (process.platform === 'darwin') {
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
if (r.status === 0) return (r.stdout || '').trim().includes('postgres');
|
|
389
|
-
return true;
|
|
403
|
+
const out = await _execFileText('ps', ['-o', 'comm=', '-p', String(pid)]);
|
|
404
|
+
return out === null ? true : out.trim().includes('postgres');
|
|
390
405
|
}
|
|
391
406
|
if (process.platform === 'win32') {
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
encoding: 'utf8',
|
|
395
|
-
windowsHide: true,
|
|
396
|
-
});
|
|
397
|
-
if (r.status === 0) return (r.stdout || '').toLowerCase().includes('postgres');
|
|
398
|
-
return true;
|
|
407
|
+
const out = await _execFileText('tasklist', ['/FI', `PID eq ${pid}`, '/FO', 'CSV', '/NH']);
|
|
408
|
+
return out === null ? true : out.toLowerCase().includes('postgres');
|
|
399
409
|
}
|
|
400
410
|
} catch {
|
|
401
411
|
/* cannot read — fall back to alive */
|
|
@@ -463,8 +473,8 @@ function scheduleOrphanTempPostmasterSweep() {
|
|
|
463
473
|
_orphanSweepStarted = true;
|
|
464
474
|
const runSweep = () => {
|
|
465
475
|
void _getPgProc()
|
|
466
|
-
.then(({ sweepOrphanTempPostmasters }) => {
|
|
467
|
-
const reaped = sweepOrphanTempPostmasters?.() || 0;
|
|
476
|
+
.then(async ({ sweepOrphanTempPostmasters }) => {
|
|
477
|
+
const reaped = (await sweepOrphanTempPostmasters?.()) || 0;
|
|
468
478
|
if (reaped > 0) __mixdogMemoryLog(`[supervisor-pg] orphan sweep reaped ${reaped} temp postmaster(s)\n`);
|
|
469
479
|
})
|
|
470
480
|
.catch(() => {
|
|
@@ -534,9 +544,20 @@ async function tryReusePgInstance({ pgdata, runtimeDir, healthcheckPg, source =
|
|
|
534
544
|
|
|
535
545
|
// ── Internal: full ensure logic (runs exclusively via _ensureInFlight) ────────
|
|
536
546
|
|
|
547
|
+
function _pgdataDir(dataDir) {
|
|
548
|
+
return join(dataDir, 'pgdata');
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/** True once `dataDir` holds an initialized cluster (initdb ran). Before that
|
|
552
|
+
* no store in it can hold rows, so a read may answer empty without starting
|
|
553
|
+
* PostgreSQL — the first start downloads/extracts the runtime and runs initdb. */
|
|
554
|
+
export function pgClusterExists(dataDir) {
|
|
555
|
+
return existsSync(join(_pgdataDir(dataDir), 'PG_VERSION'));
|
|
556
|
+
}
|
|
557
|
+
|
|
537
558
|
async function _doEnsure(dataDir) {
|
|
538
559
|
const { healthcheckPg } = await _getPgProc();
|
|
539
|
-
const pgdata =
|
|
560
|
+
const pgdata = _pgdataDir(dataDir);
|
|
540
561
|
|
|
541
562
|
// Resolve runtimeDir via runtime-fetcher (cache-hits immediately when already downloaded).
|
|
542
563
|
const { ensureRuntime } = await import('../runtime-fetcher.mjs');
|
|
@@ -549,7 +570,7 @@ async function _doEnsure(dataDir) {
|
|
|
549
570
|
_v2ReconcileTried = true;
|
|
550
571
|
try {
|
|
551
572
|
const { reconcileConfV2 } = await _getPgProc();
|
|
552
|
-
reconcileConfV2(runtimeDir, pgdata);
|
|
573
|
+
await reconcileConfV2(runtimeDir, pgdata);
|
|
553
574
|
} catch (e) {
|
|
554
575
|
__mixdogMemoryLog(`[supervisor-pg] reconcileConfV2 error (non-fatal): ${e?.message}\n`);
|
|
555
576
|
}
|
|
@@ -696,6 +717,12 @@ async function _doEnsure(dataDir) {
|
|
|
696
717
|
* @returns {Promise<{ host: string, port: number, runtimeDir: string, pgdataDir: string }>}
|
|
697
718
|
*/
|
|
698
719
|
export function ensurePgInstance(dataDir) {
|
|
720
|
+
// Once this process began stopping the cluster it owns, nothing may start
|
|
721
|
+
// or re-attach one: a late connection-loss recovery would otherwise bring a
|
|
722
|
+
// postmaster back up right after the stop and leave it running past exit.
|
|
723
|
+
if (_ownedShutdownStarted) {
|
|
724
|
+
return Promise.reject(new Error('[supervisor-pg] process is shutting down; PG start refused'));
|
|
725
|
+
}
|
|
699
726
|
// Every route to a live PG passes here — start, attach, reuse and the
|
|
700
727
|
// in-process fast path — so this is the one place the sweep cannot be
|
|
701
728
|
// skipped. Latched internally, so repeated calls schedule it only once.
|
|
@@ -803,3 +830,84 @@ export function stopPgForShutdownSync() {
|
|
|
803
830
|
// async path: never another live owner's); otherwise pg.json outlives the run.
|
|
804
831
|
patchActiveInstance({ pg_port: null, pg_started_at: null, pg_pgdata: null, pg_runtime_dir: null }, { timeoutMs: 1000 });
|
|
805
832
|
}
|
|
833
|
+
|
|
834
|
+
// ── Owned-cluster shutdown (a host that starts PG through its stores) ────────
|
|
835
|
+
//
|
|
836
|
+
// `pg_ctl start` daemonizes the postmaster outside the starting process tree,
|
|
837
|
+
// so nothing ends it when that process exits: the process that brought a
|
|
838
|
+
// cluster up (or adopted one whose owner died) owns stopping it. Unlike
|
|
839
|
+
// stopPgForShutdown above, these never act on a cluster this process did not
|
|
840
|
+
// ensure, and never on one a different live process owns (pg_owner_pid) —
|
|
841
|
+
// that owner stops it on its own exit.
|
|
842
|
+
|
|
843
|
+
/** `_live` cluster's advert is held by another live process. */
|
|
844
|
+
function _liveClusterForeignOwned(snap) {
|
|
845
|
+
let advert = null;
|
|
846
|
+
try {
|
|
847
|
+
advert = _readPgAdvert();
|
|
848
|
+
} catch {}
|
|
849
|
+
if (!advert?.pg_pgdata || resolve(advert.pg_pgdata) !== resolve(snap.pgdata)) return false;
|
|
850
|
+
return _pgAdvertIsForeignLive(advert);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* Graceful, bounded stop (`pg_ctl stop -m fast`) of the cluster this process
|
|
855
|
+
* ensured. Latches the process into shutdown first, so no later ensure can
|
|
856
|
+
* start another one. Resolves true when a cluster was stopped; rejects when
|
|
857
|
+
* pg_ctl failed (the 'exit' hook then retries synchronously).
|
|
858
|
+
*/
|
|
859
|
+
export async function stopOwnedPgForShutdown({ timeoutMs = 6_000 } = {}) {
|
|
860
|
+
_ownedShutdownStarted = true;
|
|
861
|
+
// A start still under way (pg_ctl start already spawned, readiness pending)
|
|
862
|
+
// publishes its cluster only when it settles; stopping before that would
|
|
863
|
+
// miss the postmaster it is about to own. Bounded: a cold first start can
|
|
864
|
+
// be a download.
|
|
865
|
+
if (_ensureInFlight) {
|
|
866
|
+
let timer = null;
|
|
867
|
+
await Promise.race([
|
|
868
|
+
_ensureInFlight.catch(() => {}),
|
|
869
|
+
new Promise((resolveWait) => {
|
|
870
|
+
timer = setTimeout(resolveWait, timeoutMs);
|
|
871
|
+
timer.unref?.();
|
|
872
|
+
}),
|
|
873
|
+
]);
|
|
874
|
+
clearTimeout(timer);
|
|
875
|
+
}
|
|
876
|
+
const snap = _live;
|
|
877
|
+
if (!snap) return false;
|
|
878
|
+
if (_liveClusterForeignOwned(snap)) {
|
|
879
|
+
__mixdogMemoryLog(`[supervisor-pg] PG on port ${snap.port} is owned by another live process; leaving it running\n`);
|
|
880
|
+
_live = null;
|
|
881
|
+
return false;
|
|
882
|
+
}
|
|
883
|
+
const { stopPg } = await _getPgProc();
|
|
884
|
+
await stopPg({ runtimeDir: snap.runtimeDir, pgdataDir: snap.pgdata, timeoutMs });
|
|
885
|
+
_live = null;
|
|
886
|
+
__mixdogMemoryLog(`[supervisor-pg] owned PG on port ${snap.port} stopped on shutdown\n`);
|
|
887
|
+
patchActiveInstance(
|
|
888
|
+
{ pg_port: null, pg_started_at: null, pg_pgdata: null, pg_runtime_dir: null },
|
|
889
|
+
{ timeoutMs: 2000, syncRetries: 1 }
|
|
890
|
+
);
|
|
891
|
+
return true;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/** Last-chance synchronous stop for a process 'exit' hook: acts only while the
|
|
895
|
+
* cluster this process ensured is still recorded as live (an exit the async
|
|
896
|
+
* stop never reached, or one where it failed). */
|
|
897
|
+
export function stopOwnedPgForShutdownSync() {
|
|
898
|
+
if (!_live) return;
|
|
899
|
+
stopPgForShutdownSync();
|
|
900
|
+
_live = null;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/** Register stopOwnedPgForShutdownSync on process 'exit', once. Cheap when this
|
|
904
|
+
* process never ensures a cluster: the hook is then a no-op. */
|
|
905
|
+
export function installOwnedPgExitStop() {
|
|
906
|
+
if (_ownedExitStopInstalled) return;
|
|
907
|
+
_ownedExitStopInstalled = true;
|
|
908
|
+
process.on('exit', () => {
|
|
909
|
+
try {
|
|
910
|
+
stopOwnedPgForShutdownSync();
|
|
911
|
+
} catch {}
|
|
912
|
+
});
|
|
913
|
+
}
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
} from 'node:fs';
|
|
33
33
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
34
34
|
import { fileURLToPath } from 'node:url';
|
|
35
|
-
import {
|
|
35
|
+
import { runChild } from './pg/run-child.mjs';
|
|
36
36
|
import { renameWithRetrySync, writeFileAtomicSync, writeJsonAtomicSync } from '../../shared/atomic-file.mjs';
|
|
37
37
|
import { downloadToFileWithRetry } from '../../shared/bounded-download.mjs';
|
|
38
38
|
import { platformKey, platformKeyCandidates, verifySha256File } from '../../shared/native-asset.mjs';
|
|
@@ -250,21 +250,23 @@ export function _validateRuntimeTarEntries(entries, details, stagingBase) {
|
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
-
|
|
253
|
+
// Async throughout: the first boot extracts the whole PG runtime here, and a
|
|
254
|
+
// synchronous tar froze the daemon loop for seconds.
|
|
255
|
+
export async function _extractRuntimeTarGz(tarPath, destDir, stagingBase) {
|
|
254
256
|
mkdirSync(destDir, { recursive: true });
|
|
255
257
|
const resolvedTarPath = resolve(tarPath);
|
|
256
|
-
const tarOptions = { cwd: dirname(resolvedTarPath)
|
|
258
|
+
const tarOptions = { cwd: dirname(resolvedTarPath) };
|
|
257
259
|
const tarName = basename(resolvedTarPath);
|
|
258
260
|
|
|
259
261
|
// List entries first and validate — reject traversal, external links, and
|
|
260
262
|
// special files before the extractor can materialize archive content.
|
|
261
263
|
// Keep the archive argument relative to cwd: GNU tar otherwise interprets
|
|
262
264
|
// an absolute Windows path such as C:\runtime.tar.gz as a remote host path.
|
|
263
|
-
const listResult =
|
|
265
|
+
const listResult = await runChild('tar', ['-tzf', tarName], tarOptions);
|
|
264
266
|
if (listResult.status !== 0) {
|
|
265
267
|
throw new Error(`[runtime-fetcher] tar list failed: ${listResult.stderr?.toString() || 'unknown'}`);
|
|
266
268
|
}
|
|
267
|
-
const detailResult =
|
|
269
|
+
const detailResult = await runChild('tar', ['-tvzf', tarName], tarOptions);
|
|
268
270
|
if (detailResult.status !== 0) {
|
|
269
271
|
throw new Error(`[runtime-fetcher] tar detail list failed: ${detailResult.stderr?.toString() || 'unknown'}`);
|
|
270
272
|
}
|
|
@@ -274,10 +276,7 @@ export function _extractRuntimeTarGz(tarPath, destDir, stagingBase) {
|
|
|
274
276
|
|
|
275
277
|
const resolvedDestDir = resolve(destDir);
|
|
276
278
|
const extractionArchive = relative(resolvedDestDir, resolvedTarPath);
|
|
277
|
-
const r =
|
|
278
|
-
...tarOptions,
|
|
279
|
-
cwd: resolvedDestDir,
|
|
280
|
-
});
|
|
279
|
+
const r = await runChild('tar', ['-xzf', extractionArchive], { cwd: resolvedDestDir });
|
|
281
280
|
if (r.status !== 0) {
|
|
282
281
|
throw new Error(`[runtime-fetcher] tar extraction failed: ${r.stderr?.toString() || 'unknown error'}`);
|
|
283
282
|
}
|
|
@@ -438,9 +437,9 @@ const runtimeCache = new Map();
|
|
|
438
437
|
|
|
439
438
|
// One-shot tar availability probe; result cached after first call.
|
|
440
439
|
let _tarProbed = false;
|
|
441
|
-
function probeTar() {
|
|
440
|
+
async function probeTar() {
|
|
442
441
|
if (_tarProbed) return;
|
|
443
|
-
const r =
|
|
442
|
+
const r = await runChild('tar', ['--version']);
|
|
444
443
|
if (r.status !== 0 || r.error) {
|
|
445
444
|
throw new Error(
|
|
446
445
|
'[runtime-fetcher] `tar` not found or not executable. ' +
|
|
@@ -532,7 +531,7 @@ async function installRuntimeVersion({ runtimeBaseDir, url, sha256, size, versio
|
|
|
532
531
|
// tar is only required for the download/extract path. Probe here (not at
|
|
533
532
|
// ensureRuntime entry) so a machine without tar can still reuse an
|
|
534
533
|
// already-extracted, sha-matching cached runtime via the fast path.
|
|
535
|
-
probeTar();
|
|
534
|
+
await probeTar();
|
|
536
535
|
|
|
537
536
|
__mixdogMemoryLog(`[runtime-fetcher] downloading runtime ${version} for ${pkey} (~${size} bytes) …\n`);
|
|
538
537
|
|
|
@@ -574,7 +573,7 @@ async function installRuntimeVersion({ runtimeBaseDir, url, sha256, size, versio
|
|
|
574
573
|
}
|
|
575
574
|
}
|
|
576
575
|
if (!downloadOk && lastDownloadErr) throw lastDownloadErr;
|
|
577
|
-
_extractRuntimeTarGz(tarPath, stagingDir, stagingDir);
|
|
576
|
+
await _extractRuntimeTarGz(tarPath, stagingDir, stagingDir);
|
|
578
577
|
} finally {
|
|
579
578
|
try {
|
|
580
579
|
rmSync(tarPath, { force: true });
|
|
@@ -53,7 +53,21 @@ export function createTranscriptIngest({
|
|
|
53
53
|
return run;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/** A deleted session's transcripts: wait out any ingest of each file, then
|
|
57
|
+
* drop its persisted read position. Returns the number of entries removed. */
|
|
58
|
+
async function forgetTranscripts(transcriptPaths) {
|
|
59
|
+
let removed = 0;
|
|
60
|
+
for (const transcriptPath of transcriptPaths) {
|
|
61
|
+
const resolved = path.resolve(transcriptPath);
|
|
62
|
+
await tails.get(resolved);
|
|
63
|
+
for (const key of new Set([transcriptPath, resolved])) if (offsets.forget(key)) removed += 1;
|
|
64
|
+
}
|
|
65
|
+
if (removed > 0) await offsets.persist();
|
|
66
|
+
return removed;
|
|
67
|
+
}
|
|
68
|
+
|
|
56
69
|
return {
|
|
70
|
+
forgetTranscripts,
|
|
57
71
|
loadTranscriptOffsets: offsets.load,
|
|
58
72
|
persistTranscriptOffsets: offsets.persist,
|
|
59
73
|
ingestTranscriptFile,
|