mixdog 1.3.5 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE.md +2 -2
- package/README.md +2 -2
- package/package.json +2 -11
- package/scripts/check-voice-runtime-inputs.mjs +25 -0
- package/scripts/daemon-stub.mjs +1 -1
- package/scripts/gate-local.mjs +16 -5
- package/scripts/latest-guard.mjs +21 -9
- package/scripts/lib/isolated-test-env.mjs +10 -5
- package/scripts/lib/run-node-tests.mjs +37 -37
- package/scripts/lib/scoped-temp.d.mts +16 -0
- package/scripts/lib/scoped-temp.mjs +123 -0
- package/scripts/lib/stale-temp.mjs +44 -0
- package/scripts/native-release-status.mjs +2 -2
- package/scripts/post-release-check.mjs +1 -6
- package/scripts/pptx-kit-index.mjs +17 -4
- package/scripts/recall-history-cases.json +1 -1
- package/scripts/suite-health.mjs +20 -2
- package/scripts/test-data-dir.mjs +10 -7
- package/scripts/test.mjs +7 -11
- package/src/agents/reviewer/AGENT.md +7 -11
- package/src/agents/reviewer/agent.json +1 -1
- package/src/app.mjs +4 -0
- package/src/defaults/shipped-history.json +22 -1
- package/src/defaults/skills/browser-use/SKILL.md +62 -12
- package/src/defaults/skills/code-tidy/SKILL.md +142 -270
- package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
- package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
- package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
- package/src/defaults/skills/computer-use/SKILL.md +35 -9
- package/src/defaults/skills/setup/SKILL.md +22 -0
- package/src/defaults/skills/setup/references/actions.md +39 -5
- package/src/defaults/skills/setup/references/surfaces.md +11 -2
- package/src/defaults/skills/visualize/SKILL.md +2 -1
- package/src/lib/rules-builder.cjs +39 -20
- package/src/output-styles/common.md +2 -2
- package/src/output-styles/simple.md +1 -1
- package/src/rules/agent/AGENT.md +11 -10
- package/src/rules/lead/LEAD.md +23 -20
- package/src/rules/shared/05-tool-calls.md +5 -6
- package/src/rules/shared/10-tool-workflow.md +4 -0
- package/src/rules/shared/45-destructive.md +0 -2
- package/src/rules/shared/50-execution.md +2 -0
- package/src/rules/shared/60-verification.md +7 -5
- package/src/rules/shared/70-delivery.md +7 -9
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
- package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/config.mjs +17 -1
- package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +73 -68
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +57 -2
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +176 -72
- package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +192 -133
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +159 -69
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +70 -33
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +123 -102
- package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +25 -13
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
- package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
- package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +20 -12
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
- package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
- package/src/runtime/agent/orchestrator/providers/registry.mjs +38 -0
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +49 -16
- package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -6
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +9 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +798 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +280 -45
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
- package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +51 -25
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +37 -3
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +31 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +20 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +5 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +49 -21
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +12 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +24 -14
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +130 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +81 -8
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -8
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +9 -4
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +21 -5
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +24 -9
- package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +150 -0
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +36 -9
- package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
- package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
- package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
- package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +8 -6
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +81 -22
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +92 -3
- package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
- package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +79 -3
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
- package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +31 -6
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +110 -26
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
- package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
- package/src/runtime/browser-bridge/action-schema.mjs +13 -2
- package/src/runtime/browser-bridge/client.mjs +73 -8
- package/src/runtime/browser-bridge/input-fields.mjs +8 -9
- package/src/runtime/browser-bridge/mail-links.mjs +107 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
- package/src/runtime/channels/lib/config.mjs +6 -61
- package/src/runtime/channels/lib/crash-log.mjs +1 -1
- package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
- package/src/runtime/channels/lib/event-queue.mjs +3 -18
- package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
- package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
- package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
- package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
- package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
- package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
- package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
- package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
- package/src/runtime/channels/lib/scheduler.mjs +33 -147
- package/src/runtime/channels/lib/schedules-db.mjs +34 -11
- package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +1 -2
- package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
- package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
- package/src/runtime/channels/lib/whisper-port.mjs +10 -1
- package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
- package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
- package/src/runtime/channels/lib/worker-main.mjs +10 -34
- package/src/runtime/computer-bridge/action-schema.mjs +42 -1
- package/src/runtime/computer-bridge/actions.mjs +9 -1
- package/src/runtime/computer-bridge/client.mjs +180 -8
- package/src/runtime/computer-bridge/core-actions.mjs +2 -2
- package/src/runtime/computer-bridge/error-recovery.mjs +28 -2
- package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
- package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
- package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
- package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
- package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
- package/src/runtime/connectors/gmail-app-password.mjs +172 -39
- package/src/runtime/connectors/google-mcp.mjs +10 -12
- package/src/runtime/local-provider/server-process.mjs +2 -1
- package/src/runtime/media/adapters/gemini-video.mjs +4 -1
- package/src/runtime/media/adapters/xai-media.mjs +2 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
- package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
- package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
- package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
- package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
- package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
- package/src/runtime/office/design/design-art-direction.mjs +29 -27
- package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
- package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
- package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
- package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
- package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
- package/src/runtime/office/quality/design-review.mjs +1 -5
- package/src/runtime/office/quality/document-brief.mjs +1 -5
- package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
- package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
- package/src/runtime/session-search/session-search-db.mjs +73 -9
- package/src/runtime/session-search/session-search-ingest.mjs +110 -20
- package/src/runtime/shared/abort-race.mjs +89 -0
- package/src/runtime/shared/agent-avatar.d.mts +0 -1
- package/src/runtime/shared/agent-kind.mjs +4 -3
- package/src/runtime/shared/bot-owned-work.mjs +56 -0
- package/src/runtime/shared/bounded-download.mjs +8 -3
- package/src/runtime/shared/config.mjs +2 -13
- package/src/runtime/shared/connector-accounts.mjs +20 -10
- package/src/runtime/shared/connector-catalog.d.mts +29 -0
- package/src/runtime/shared/connector-catalog.mjs +150 -0
- package/src/runtime/shared/connector-triggers.mjs +22 -19
- package/src/runtime/shared/defaults-separation.mjs +2 -17
- package/src/runtime/shared/delegated-approval-host.mjs +103 -11
- package/src/runtime/shared/err-text.mjs +24 -15
- package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
- package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
- package/src/runtime/shared/file-permissions.d.mts +0 -8
- package/src/runtime/shared/file-permissions.mjs +0 -11
- package/src/runtime/shared/git-direct.test-support.mjs +19 -0
- package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
- package/src/runtime/shared/handoff-artifacts.mjs +18 -8
- package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
- package/src/runtime/shared/heartbeat-interval.mjs +19 -0
- package/src/runtime/shared/idle-auto-clear-gate.mjs +126 -0
- package/src/runtime/shared/llm/cost.mjs +5 -1
- package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
- package/src/runtime/shared/loopback-listener.mjs +25 -2
- package/src/runtime/shared/mixy-presence.mjs +22 -0
- package/src/runtime/shared/model-route-chain.d.mts +7 -2
- package/src/runtime/shared/model-route-chain.mjs +22 -4
- package/src/runtime/shared/open-url.mjs +10 -0
- package/src/runtime/shared/orchestration.d.mts +2 -0
- package/src/runtime/shared/pg-schema-db.mjs +27 -16
- package/src/runtime/shared/profile-config.cjs +1 -1
- package/src/runtime/shared/prompt-origin.d.mts +4 -0
- package/src/runtime/shared/prompt-origin.mjs +21 -0
- package/src/runtime/shared/provider-accounts.mjs +7 -1
- package/src/runtime/shared/provider-display-name.mjs +29 -0
- package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
- package/src/runtime/shared/routine-trigger.d.mts +2 -0
- package/src/runtime/shared/routine-trigger.mjs +10 -0
- package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
- package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
- package/src/runtime/shared/session-activity-signal.mjs +48 -1
- package/src/runtime/shared/session-scratch.mjs +37 -0
- package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
- package/src/runtime/shared/session-sidebar-host.mjs +7 -3
- package/src/runtime/shared/session-turn-prelude.mjs +18 -0
- package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
- package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
- package/src/runtime/shared/tool-result-summary.mjs +2 -15
- package/src/runtime/shared/tool-surface.mjs +3 -0
- package/src/runtime/shared/tool-work-units.mjs +2 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
- package/src/runtime/shared/worktrees/contract.mjs +4 -1
- package/src/runtime/shared/worktrees/git.mjs +15 -0
- package/src/runtime/shared/worktrees/manager.mjs +87 -38
- package/src/runtime/shared/worktrees/test-support.mjs +14 -13
- package/src/runtime/terminal-bridge/client.mjs +10 -1
- package/src/runtime/visualize-bridge/client.mjs +29 -8
- package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
- package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
- package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
- package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
- package/src/session-runtime/agent-home-delivery.mjs +489 -185
- package/src/session-runtime/agent-task-sessions.mjs +467 -229
- package/src/session-runtime/agent-tool-routing.mjs +8 -41
- package/src/session-runtime/boot/apis.mjs +9 -0
- package/src/session-runtime/boot/infrastructure.mjs +4 -0
- package/src/session-runtime/boot/providers.mjs +3 -0
- package/src/session-runtime/boot/tools.mjs +11 -2
- package/src/session-runtime/bot-status.mjs +12 -6
- package/src/session-runtime/bot-target.mjs +38 -0
- package/src/session-runtime/channel-config-api.mjs +6 -1
- package/src/session-runtime/config-lifecycle.mjs +6 -0
- package/src/session-runtime/connector-merge-migration.mjs +305 -0
- package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
- package/src/session-runtime/conversation-import/discovery.mjs +271 -25
- package/src/session-runtime/conversation-import/service.mjs +26 -5
- package/src/session-runtime/handoff-attachments.mjs +82 -31
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +144 -41
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
- package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
- package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
- package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
- package/src/session-runtime/lifecycle/teardown.mjs +23 -5
- package/src/session-runtime/mcp-glue/server-catalog.mjs +55 -3
- package/src/session-runtime/mcp-oauth-login.mjs +5 -3
- package/src/session-runtime/mixy-heartbeat.mjs +153 -33
- package/src/session-runtime/model-route/set-route.mjs +11 -2
- package/src/session-runtime/notification-bus.mjs +31 -117
- package/src/session-runtime/notification-completion-queue.mjs +54 -0
- package/src/session-runtime/notification-listeners.mjs +76 -0
- package/src/session-runtime/owned-automation-run.mjs +2 -1
- package/src/session-runtime/prewarm/channel-start.mjs +3 -7
- package/src/session-runtime/provider-catalog-cache.mjs +82 -36
- package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
- package/src/session-runtime/provider-models.mjs +4 -2
- package/src/session-runtime/resource-mcp-api.mjs +143 -224
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
- package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
- package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
- package/src/session-runtime/resource-mcp-servers.mjs +78 -0
- package/src/session-runtime/resource-mcp-store.mjs +49 -0
- package/src/session-runtime/resource-recall-api.mjs +6 -1
- package/src/session-runtime/routine-owner.mjs +40 -9
- package/src/session-runtime/runtime-feature-gates.mjs +5 -1
- package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
- package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
- package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
- package/src/session-runtime/services/agent-tool/render.mjs +71 -17
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +13 -4
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
- package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
- package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
- package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
- package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
- package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
- package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
- package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
- package/src/session-runtime/services/channel-admin.mjs +7 -13
- package/src/session-runtime/services/channel-session-router.mjs +4 -2
- package/src/session-runtime/services/provider-admin.mjs +2 -1
- package/src/session-runtime/services/seeds.mjs +0 -28
- package/src/session-runtime/session-compaction-sync.mjs +27 -0
- package/src/session-runtime/session-create/session-options.mjs +1 -0
- package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
- package/src/session-runtime/settings-compaction-api.mjs +2 -40
- package/src/session-runtime/settings-system-api.mjs +13 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
- package/src/session-runtime/setup-tool/executor.mjs +145 -22
- package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
- package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
- package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
- package/src/session-runtime/tool-policy/policy-stage.mjs +17 -2
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
- package/src/session-runtime/workspace/transitions.mjs +25 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
- package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
- package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
- package/src/standalone/channel-transport/client-registry.mjs +24 -163
- package/src/standalone/channel-transport.mjs +1 -1
- package/src/standalone/daemon-channels-loader.mjs +10 -8
- package/src/standalone/daemon-session-control/list-page.mjs +72 -0
- package/src/standalone/daemon-session-control/request.mjs +51 -0
- package/src/standalone/daemon-session-control/room-host.mjs +57 -0
- package/src/standalone/daemon-session-control/route-control.mjs +48 -0
- package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
- package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
- package/src/standalone/daemon-session-control.mjs +142 -190
- package/src/standalone/daemon.mjs +26 -3
- package/src/standalone/session-client.mjs +7 -3
- package/src/standalone/session-protocol.mjs +5 -0
- package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
- package/src/standalone/session-service/quota-resume.mjs +3 -0
- package/src/standalone/session-service/retention/busy.mjs +65 -1
- package/src/standalone/session-service/session-calls/session-action.mjs +30 -2
- package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
- package/src/standalone/session-tool-output.mjs +15 -7
- package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
- package/src/tui/app/app-view/floating-panel.jsx +7 -4
- package/src/tui/app/model-options.mjs +3 -28
- package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
- package/src/tui/app/model-picker/route-selection.mjs +5 -6
- package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
- package/src/tui/app/provider-setup/provider-items.mjs +8 -2
- package/src/tui/app/provider-setup-picker.mjs +9 -5
- package/src/tui/components/QuestionPanel.jsx +13 -4
- package/src/tui/dist/index.mjs +133 -92
- package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
- package/src/tui/session/initial-state.mjs +3 -0
- package/src/tui/session/notice-surface.mjs +7 -1
- package/src/tui/session/queue-helpers.mjs +3 -42
- package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
- package/src/tui/session/session-api/routes-write.mjs +5 -1
- package/src/tui/session/session-api/routes.mjs +1 -0
- package/src/tui/session/session-api/settings.mjs +17 -1
- package/src/tui/session/session-flow/auto-clear.mjs +4 -33
- package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
- package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
- package/src/tui/session/session-flow/drain-retry.mjs +35 -0
- package/src/tui/session/session-flow/drain.mjs +118 -149
- package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
- package/src/tui/session/session-flow/queue/take-entries.mjs +16 -2
- package/src/tui/session/tool-approval-delegation.mjs +64 -0
- package/src/tui/session/tool-approval-request.mjs +39 -0
- package/src/tui/session/tool-approval-settle.mjs +62 -0
- package/src/tui/session/tool-approval.mjs +69 -115
- package/src/tui/session/tui-steering-persist.mjs +8 -1
- package/src/tui/session/turn-stream.mjs +19 -2
- package/src/tui/session/turn.mjs +1 -0
- package/src/ui/statusline-agents.mjs +1 -3
- package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
- package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
- package/scripts/code-graph-description-contract.mjs +0 -188
- package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
import { createHash } from 'node:crypto';
|
|
10
10
|
import keychain from '../../lib/keychain-cjs.cjs';
|
|
11
11
|
import { GMAIL_APP_PASSWORD, gmailAddress } from '../shared/connector-catalog.mjs';
|
|
12
|
-
import {
|
|
12
|
+
import { connectorAccountsInstructions } from '../shared/connector-accounts.mjs';
|
|
13
13
|
|
|
14
14
|
const PROTOCOL_VERSION = '2025-06-18';
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const GMAIL_IMAP = Object.freeze({ host: 'imap.gmail.com', port: 993, secure: true });
|
|
16
|
+
const GMAIL_SMTP = Object.freeze({ host: 'smtp.gmail.com', port: 465, secure: true });
|
|
17
17
|
const TIMEOUTS = { connectionTimeout: 20_000, greetingTimeout: 15_000, socketTimeout: 60_000 };
|
|
18
18
|
const SEARCH_DEFAULT = 10;
|
|
19
19
|
const SEARCH_MAX = 25;
|
|
@@ -48,7 +48,7 @@ const MESSAGES = {
|
|
|
48
48
|
|
|
49
49
|
/** `GMAIL_APP_PASSWORD_<CODE>: <sentence>` — the code is what Desktop shows
|
|
50
50
|
* in the user's language. */
|
|
51
|
-
|
|
51
|
+
class GmailAppPasswordError extends Error {
|
|
52
52
|
constructor(code, detail = '') {
|
|
53
53
|
super(`GMAIL_APP_PASSWORD_${code}: ${MESSAGES[code]}${detail ? ` (${detail})` : ''}`);
|
|
54
54
|
this.code = code;
|
|
@@ -101,7 +101,7 @@ function gmailError(error, password) {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
// ── Clients (tests pass fakes with the same surface) ────────────────────────
|
|
104
|
-
|
|
104
|
+
const gmailClients = {
|
|
105
105
|
async imap({ email, password }) {
|
|
106
106
|
const { ImapFlow } = await import('imapflow');
|
|
107
107
|
const client = new ImapFlow({
|
|
@@ -186,6 +186,64 @@ const addresses = (list) =>
|
|
|
186
186
|
.map((entry) => (entry?.name ? `${entry.name} <${entry.address || ''}>` : entry?.address || ''))
|
|
187
187
|
.filter(Boolean)
|
|
188
188
|
.join(', ');
|
|
189
|
+
/** The From mailboxes as Gmail parsed them, one entry each; a group shows as
|
|
190
|
+
* its name with an empty address (imapflow parseEnvelope). */
|
|
191
|
+
const mailboxes = (list) =>
|
|
192
|
+
(Array.isArray(list) ? list : []).map((entry) => ({
|
|
193
|
+
name: String(entry?.name || ''),
|
|
194
|
+
address: String(entry?.address || ''),
|
|
195
|
+
}));
|
|
196
|
+
|
|
197
|
+
const stripComments = (text) => {
|
|
198
|
+
let out = text;
|
|
199
|
+
for (let previous = ''; previous !== out; ) {
|
|
200
|
+
previous = out;
|
|
201
|
+
out = out.replace(/\([^()]*\)/g, ' ');
|
|
202
|
+
}
|
|
203
|
+
return out;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
/** Gmail's own verdict on the sender, read from the topmost
|
|
207
|
+
* Authentication-Results only, and only when its authserv-id is Gmail's
|
|
208
|
+
* (mx.google.com):
|
|
209
|
+
* - the receiving MTA prepends the field above the trace fields it adds
|
|
210
|
+
* (RFC 8601 §4, §4.1), so the last receiver's copy is the first one;
|
|
211
|
+
* - a conforming MTA deletes any copy that arrives bearing its own
|
|
212
|
+
* authserv-id (RFC 8601 §5), so a sender cannot pass one off as Gmail's;
|
|
213
|
+
* - Gmail directs other mail clients to this field for its verdict (Gmail
|
|
214
|
+
* Help, "Check if your Gmail message is authenticated", answer 180707).
|
|
215
|
+
* A message no MTA delivered — one appended to the mailbox over IMAP — has
|
|
216
|
+
* only the headers its author wrote; that is the account owner's own mail.
|
|
217
|
+
* `{ dmarc: { result, from }, dkim: [{ result, domain }] }` (`domain` is a
|
|
218
|
+
* signature's d= as header.d reports it, RFC 8601 §2.7.1), or null. */
|
|
219
|
+
function gmailAuthentication(headers) {
|
|
220
|
+
const unfolded = String(headers || '').replace(/\r?\n[ \t]+/g, ' ');
|
|
221
|
+
const first = /^authentication-results:(.*)$/im.exec(unfolded)?.[1];
|
|
222
|
+
if (!first) return null;
|
|
223
|
+
const [server = '', ...results] = stripComments(first).split(';');
|
|
224
|
+
if (server.trim().split(/\s+/)[0].toLowerCase() !== 'mx.google.com') return null;
|
|
225
|
+
const verdict = { dmarc: null, dkim: [] };
|
|
226
|
+
for (const entry of results) {
|
|
227
|
+
const method = /^\s*([a-z0-9-]+)\s*=\s*([a-z]+)/i.exec(entry);
|
|
228
|
+
if (!method) continue;
|
|
229
|
+
const props = Object.fromEntries(
|
|
230
|
+
[...entry.matchAll(/([a-z]+\.[a-z]+)\s*=\s*("[^"]*"|[^\s;]+)/gi)].map(([, name, value]) => [
|
|
231
|
+
name.toLowerCase(),
|
|
232
|
+
value.replace(/^"|"$/g, '').toLowerCase(),
|
|
233
|
+
])
|
|
234
|
+
);
|
|
235
|
+
const result = method[2].toLowerCase();
|
|
236
|
+
if (method[1].toLowerCase() === 'dmarc' && !verdict.dmarc)
|
|
237
|
+
verdict.dmarc = { result, from: props['header.from'] || '' };
|
|
238
|
+
// Only the signing domain d= counts toward the From domain (RFC 7489
|
|
239
|
+
// §3.1.1); the agent identity i= is not an alignment identifier.
|
|
240
|
+
if (method[1].toLowerCase() === 'dkim' && props['header.d']) {
|
|
241
|
+
verdict.dkim.push({ result, domain: props['header.d'] });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return verdict;
|
|
245
|
+
}
|
|
246
|
+
|
|
189
247
|
const isoDate = (value) => {
|
|
190
248
|
const date = value ? new Date(value) : null;
|
|
191
249
|
return date && !Number.isNaN(date.getTime()) ? date.toISOString() : '';
|
|
@@ -214,25 +272,30 @@ const isAttachment = (node) =>
|
|
|
214
272
|
String(node.type || '').startsWith('message/') ||
|
|
215
273
|
Boolean(node.dispositionParameters?.filename || node.parameters?.name);
|
|
216
274
|
|
|
217
|
-
/** The text/plain
|
|
218
|
-
|
|
219
|
-
|
|
275
|
+
/** The first text/plain and the first text/html part of the message that are
|
|
276
|
+
* not attachments (part numbers), each null without one. */
|
|
277
|
+
function textParts(root) {
|
|
278
|
+
const found = { plain: null, html: null };
|
|
220
279
|
const walk = (node) => {
|
|
221
|
-
if (!node || String(node.type || '').startsWith('message/')) return
|
|
280
|
+
if (!node || String(node.type || '').startsWith('message/')) return;
|
|
222
281
|
if (node.childNodes?.length) {
|
|
223
|
-
for (const child of node.childNodes)
|
|
224
|
-
|
|
225
|
-
if (found) return found;
|
|
226
|
-
}
|
|
227
|
-
return null;
|
|
282
|
+
for (const child of node.childNodes) walk(child);
|
|
283
|
+
return;
|
|
228
284
|
}
|
|
229
|
-
if (node.disposition === 'attachment') return
|
|
285
|
+
if (node.disposition === 'attachment') return;
|
|
230
286
|
const part = node.part || '1';
|
|
231
|
-
if (node.type === 'text/plain')
|
|
232
|
-
if (node.type === 'text/html' && !html) html =
|
|
233
|
-
return null;
|
|
287
|
+
if (node.type === 'text/plain' && !found.plain) found.plain = part;
|
|
288
|
+
if (node.type === 'text/html' && !found.html) found.html = part;
|
|
234
289
|
};
|
|
235
|
-
|
|
290
|
+
walk(root);
|
|
291
|
+
return found;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** The text/plain part (else text/html) that is the message's body. */
|
|
295
|
+
function bodyPart(root) {
|
|
296
|
+
const { plain, html } = textParts(root);
|
|
297
|
+
if (plain) return { part: plain, html: false };
|
|
298
|
+
return html ? { part: html, html: true } : null;
|
|
236
299
|
}
|
|
237
300
|
|
|
238
301
|
/** Attachments by name, type and (decoded, approximate) size only. */
|
|
@@ -264,16 +327,15 @@ const htmlText = (html) =>
|
|
|
264
327
|
.replace(/ *\n\s*/g, '\n')
|
|
265
328
|
.trim();
|
|
266
329
|
|
|
267
|
-
/** Up to `maxBytes` of
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const download = await client.download(String(uid), picked.part, {
|
|
330
|
+
/** Up to `maxBytes` of part `part`, decoded to UTF-8; `complete` is false when
|
|
331
|
+
* the part may go on past them. */
|
|
332
|
+
async function partText(client, uid, part, maxBytes) {
|
|
333
|
+
const download = await client.download(String(uid), part, {
|
|
272
334
|
uid: true,
|
|
273
335
|
maxBytes,
|
|
274
336
|
chunkSize: Math.min(maxBytes, 65_536),
|
|
275
337
|
});
|
|
276
|
-
if (!download?.content) return '';
|
|
338
|
+
if (!download?.content) return { value: '', complete: true };
|
|
277
339
|
const chunks = [];
|
|
278
340
|
let size = 0;
|
|
279
341
|
for await (const chunk of download.content) {
|
|
@@ -281,10 +343,63 @@ async function bodyText(client, uid, structure, maxBytes) {
|
|
|
281
343
|
size += chunk.length;
|
|
282
344
|
if (size >= maxBytes) break;
|
|
283
345
|
}
|
|
284
|
-
|
|
346
|
+
return { value: Buffer.concat(chunks).subarray(0, maxBytes).toString('utf8'), complete: size < maxBytes };
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** Up to `maxBytes` of the body text, decoded to UTF-8. */
|
|
350
|
+
async function bodyText(client, uid, structure, maxBytes) {
|
|
351
|
+
const picked = bodyPart(structure);
|
|
352
|
+
if (!picked) return '';
|
|
353
|
+
const { value } = await partText(client, uid, picked.part, maxBytes);
|
|
285
354
|
return picked.html ? htmlText(value) : value.trim();
|
|
286
355
|
}
|
|
287
356
|
|
|
357
|
+
let domModule;
|
|
358
|
+
const loadDom = () => (domModule ||= import('jsdom'));
|
|
359
|
+
|
|
360
|
+
/** The targets of an HTML part's hyperlinks (`a` and `area` with href, HTML
|
|
361
|
+
* Standard §4.6 Links) as the HTML parser reads the attribute, character
|
|
362
|
+
* references decoded. A tag the download cut off is no element (HTML
|
|
363
|
+
* tokenization: the eof-in-tag error emits no tag). */
|
|
364
|
+
async function htmlLinks(html) {
|
|
365
|
+
if (!html) return [];
|
|
366
|
+
const { JSDOM } = await loadDom();
|
|
367
|
+
const dom = new JSDOM(html);
|
|
368
|
+
try {
|
|
369
|
+
return [...dom.window.document.querySelectorAll('a[href], area[href]')].map((node) => node.getAttribute('href'));
|
|
370
|
+
} finally {
|
|
371
|
+
dom.window.close();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// An absolute http(s) URI in plain text as RFC 3986 spells one: the scheme,
|
|
376
|
+
// then a run of URI characters — unreserved, reserved or percent-encoded
|
|
377
|
+
// (§2.1-§2.3). Whitespace, quotes and angle brackets are no URI characters,
|
|
378
|
+
// so they end it (Appendix C).
|
|
379
|
+
const TEXT_URI = /https?:\/\/(?:[A-Za-z0-9\-._~:/?#[\]@!$&'()*+,;=]|%[0-9A-Fa-f]{2})+/gi;
|
|
380
|
+
|
|
381
|
+
const isWebUrl = (value) => {
|
|
382
|
+
try {
|
|
383
|
+
return ['http:', 'https:'].includes(new URL(value).protocol);
|
|
384
|
+
} catch {
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
/** The message's link targets, exactly as written: the hyperlinks of its
|
|
390
|
+
* HTML part, then the URIs of its text part (one the download cut off is
|
|
391
|
+
* left out). Absolute http(s) only, each once. */
|
|
392
|
+
function messageLinks(plain, hrefs) {
|
|
393
|
+
const links = [...hrefs];
|
|
394
|
+
if (plain) {
|
|
395
|
+
for (const match of plain.value.matchAll(TEXT_URI)) {
|
|
396
|
+
if (!plain.complete && match.index + match[0].length === plain.value.length) continue;
|
|
397
|
+
links.push(match[0]);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return [...new Set(links.filter((link) => typeof link === 'string' && isWebUrl(link)))];
|
|
401
|
+
}
|
|
402
|
+
|
|
288
403
|
const references = (headers) =>
|
|
289
404
|
String(headers || '')
|
|
290
405
|
.replace(/\r?\n[ \t]+/g, ' ')
|
|
@@ -317,7 +432,9 @@ const LABEL_MAX = 225;
|
|
|
317
432
|
|
|
318
433
|
/** The distinct message ids of `args.ids` (an array, or one id). */
|
|
319
434
|
function messageIds(args) {
|
|
320
|
-
|
|
435
|
+
let raw = [];
|
|
436
|
+
if (Array.isArray(args.ids)) raw = args.ids;
|
|
437
|
+
else if (args.ids != null) raw = [args.ids];
|
|
321
438
|
const ids = [...new Set(raw.map((id) => text(id)))];
|
|
322
439
|
if (!ids.length || ids.some((id) => !id)) throw new ToolInputError('ids must list ids from search_emails');
|
|
323
440
|
if (ids.length > MANAGE_MAX) throw new ToolInputError(`At most ${MANAGE_MAX} emails per call; got ${ids.length}.`);
|
|
@@ -519,7 +636,8 @@ const TOOLS = [
|
|
|
519
636
|
},
|
|
520
637
|
{
|
|
521
638
|
name: 'read_email',
|
|
522
|
-
description:
|
|
639
|
+
description:
|
|
640
|
+
'Read one email by its id from search_emails: headers, text body, link targets, and attachment names and sizes.',
|
|
523
641
|
inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
|
|
524
642
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
525
643
|
async run(ctx, args) {
|
|
@@ -530,22 +648,38 @@ const TOOLS = [
|
|
|
530
648
|
const [message] = uid
|
|
531
649
|
? await client.fetchAll(
|
|
532
650
|
String(uid),
|
|
533
|
-
{
|
|
651
|
+
{
|
|
652
|
+
uid: true,
|
|
653
|
+
envelope: true,
|
|
654
|
+
internalDate: true,
|
|
655
|
+
bodyStructure: true,
|
|
656
|
+
threadId: true,
|
|
657
|
+
headers: ['authentication-results'],
|
|
658
|
+
},
|
|
534
659
|
{ uid: true }
|
|
535
660
|
)
|
|
536
661
|
: [];
|
|
537
662
|
if (!message) throw new ToolInputError(`No email with id ${id}.`);
|
|
538
663
|
const { envelope = {} } = message;
|
|
539
|
-
const
|
|
664
|
+
const parts = textParts(message.bodyStructure);
|
|
665
|
+
const plain = parts.plain ? await partText(client, message.uid, parts.plain, READ_BYTES) : null;
|
|
666
|
+
const html = parts.html ? await partText(client, message.uid, parts.html, READ_BYTES) : null;
|
|
667
|
+
let body = '';
|
|
668
|
+
if (plain) body = plain.value.trim();
|
|
669
|
+
else if (html) body = htmlText(html.value);
|
|
670
|
+
const links = messageLinks(plain, await htmlLinks(html?.value));
|
|
540
671
|
const head = {
|
|
541
672
|
id: message.emailId || id,
|
|
542
673
|
threadId: message.threadId,
|
|
543
674
|
messageId: envelope.messageId,
|
|
544
675
|
from: addresses(envelope.from),
|
|
676
|
+
fromAddresses: mailboxes(envelope.from),
|
|
677
|
+
authentication: gmailAuthentication(message.headers),
|
|
545
678
|
to: addresses(envelope.to),
|
|
546
679
|
cc: addresses(envelope.cc) || undefined,
|
|
547
680
|
date: isoDate(envelope.date || message.internalDate),
|
|
548
681
|
subject: text(envelope.subject),
|
|
682
|
+
links,
|
|
549
683
|
attachments: attachmentsOf(message.bodyStructure),
|
|
550
684
|
};
|
|
551
685
|
const clipped = body.length > READ_CHARS ? `${body.slice(0, READ_CHARS)}\n[truncated]` : body;
|
|
@@ -588,6 +722,12 @@ const TOOLS = [
|
|
|
588
722
|
...MANAGE_TOOLS,
|
|
589
723
|
];
|
|
590
724
|
|
|
725
|
+
/** The tool definitions (name, description, schema, annotations) the server
|
|
726
|
+
* lists; with `hidden`, also the ones only the approval gate calls. */
|
|
727
|
+
export function gmailAppPasswordTools({ hidden = false } = {}) {
|
|
728
|
+
return TOOLS.filter((tool) => hidden || !tool.hidden).map(({ run, hidden: _hidden, ...tool }) => tool);
|
|
729
|
+
}
|
|
730
|
+
|
|
591
731
|
// ── The MCP server at the reserved address ──────────────────────────────────
|
|
592
732
|
const GMAIL_HOST = new URL(GMAIL_APP_PASSWORD.url).host;
|
|
593
733
|
|
|
@@ -616,18 +756,11 @@ async function rpc(session, message) {
|
|
|
616
756
|
protocolVersion: text(params?.protocolVersion) || PROTOCOL_VERSION,
|
|
617
757
|
capabilities: { tools: { listChanged: false } },
|
|
618
758
|
serverInfo: { name: 'mixdog-gmail-app-password', version: '1.0.0' },
|
|
619
|
-
instructions:
|
|
759
|
+
instructions: connectorAccountsInstructions([{ email: session.email }]),
|
|
620
760
|
});
|
|
621
761
|
}
|
|
622
762
|
if (method === 'ping') return reply({});
|
|
623
|
-
if (method === 'tools/list') {
|
|
624
|
-
return reply({
|
|
625
|
-
tools: TOOLS.filter((tool) => !tool.hidden).map(({ run, hidden, ...tool }) => ({
|
|
626
|
-
...tool,
|
|
627
|
-
description: `${tool.description} (Gmail account: ${session.email})`,
|
|
628
|
-
})),
|
|
629
|
-
});
|
|
630
|
-
}
|
|
763
|
+
if (method === 'tools/list') return reply({ tools: gmailAppPasswordTools() });
|
|
631
764
|
if (method === 'tools/call') {
|
|
632
765
|
const tool = TOOLS.find((candidate) => candidate.name === params?.name);
|
|
633
766
|
if (!tool) return { jsonrpc: '2.0', id, error: { code: -32602, message: `Unknown tool: ${params?.name}` } };
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// live in the keychain, and each MCP request carries the access token, which
|
|
10
10
|
// this server spends on the Gmail / Calendar / Drive REST APIs.
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { connectorAccountsInstructions } from '../shared/connector-accounts.mjs';
|
|
13
13
|
|
|
14
14
|
const PROTOCOL_VERSION = '2025-06-18';
|
|
15
15
|
const ORIGIN_SUFFIX = '.mixdog.invalid';
|
|
@@ -329,6 +329,12 @@ const SERVICES = {
|
|
|
329
329
|
drive: { name: 'Google Drive', tools: DRIVE_TOOLS },
|
|
330
330
|
};
|
|
331
331
|
|
|
332
|
+
/** The tool definitions (name, description, schema, annotations) of built-in
|
|
333
|
+
* Google service `service` (`gmail`, `calendar`, `drive`). */
|
|
334
|
+
export function googleMcpTools(service) {
|
|
335
|
+
return (SERVICES[service]?.tools || []).map(({ run, ...tool }) => tool);
|
|
336
|
+
}
|
|
337
|
+
|
|
332
338
|
/** The reserved MCP address of built-in Google service `service`. */
|
|
333
339
|
export function googleMcpUrl(service) {
|
|
334
340
|
return `https://${service}${ORIGIN_SUFFIX}/mcp`;
|
|
@@ -367,20 +373,12 @@ async function rpc(service, token, message) {
|
|
|
367
373
|
protocolVersion: text(params?.protocolVersion) || PROTOCOL_VERSION,
|
|
368
374
|
capabilities: { tools: { listChanged: false } },
|
|
369
375
|
serverInfo: { name: `mixdog-${service.key}`, version: '1.0.0' },
|
|
370
|
-
|
|
376
|
+
// The one account this sign-in is (the connector's accounts server reads it back).
|
|
377
|
+
instructions: connectorAccountsInstructions([{ email: info.email }]),
|
|
371
378
|
});
|
|
372
379
|
}
|
|
373
380
|
if (method === 'ping') return reply({});
|
|
374
|
-
if (method === 'tools/list') {
|
|
375
|
-
// Several accounts of one service are separate servers: each tool says whose it is.
|
|
376
|
-
const { email } = await tokenInfo(token).catch(() => ({ email: '' }));
|
|
377
|
-
return reply({
|
|
378
|
-
tools: service.tools.map(({ run, ...tool }) => ({
|
|
379
|
-
...tool,
|
|
380
|
-
...(email ? { description: `${tool.description} (${service.name} account: ${email})` } : {}),
|
|
381
|
-
})),
|
|
382
|
-
});
|
|
383
|
-
}
|
|
381
|
+
if (method === 'tools/list') return reply({ tools: googleMcpTools(service.key) });
|
|
384
382
|
if (method === 'tools/call') {
|
|
385
383
|
const tool = service.tools.find((candidate) => candidate.name === params?.name);
|
|
386
384
|
if (!tool) return { jsonrpc: '2.0', id, error: { code: -32602, message: `Unknown tool: ${params?.name}` } };
|
|
@@ -4,13 +4,14 @@ import { setTimeout as delay } from 'node:timers/promises';
|
|
|
4
4
|
import { redactedLog, spawnServerState } from './server-process/child-state.mjs';
|
|
5
5
|
import { awaitServerReady } from './server-process/readiness.mjs';
|
|
6
6
|
import { createSerialChain } from './serial-chain.mjs';
|
|
7
|
+
import { listenOnFetchSafePort } from '../shared/fetch-safe-listen.mjs';
|
|
7
8
|
|
|
8
9
|
async function freeLoopbackPort() {
|
|
9
10
|
return new Promise((resolve, reject) => {
|
|
10
11
|
const probe = createServer();
|
|
11
12
|
probe.unref();
|
|
12
13
|
probe.once('error', reject);
|
|
13
|
-
probe
|
|
14
|
+
listenOnFetchSafePort(probe, '127.0.0.1', () => {
|
|
14
15
|
const port = probe.address().port;
|
|
15
16
|
probe.close((error) => (error ? reject(error) : resolve(port)));
|
|
16
17
|
});
|
|
@@ -120,7 +120,10 @@ async function generateViaVeo({ model, prompt, options, references = [], signal,
|
|
|
120
120
|
// Veo reports no usage; it bills per generated second. The response does
|
|
121
121
|
// not echo the length, so the requested one (default 8s) is the billed one.
|
|
122
122
|
// A failed download still leaves the generated seconds billed.
|
|
123
|
-
const usage = {
|
|
123
|
+
const usage = {
|
|
124
|
+
seconds: parameters.durationSeconds || VEO_DEFAULT_SECONDS,
|
|
125
|
+
resolution: parameters.resolution || '720p',
|
|
126
|
+
};
|
|
124
127
|
// The download is part of the generation budget: unbounded, it held an
|
|
125
128
|
// active job slot (and the upstream connection) open indefinitely after
|
|
126
129
|
// the poll loop finished.
|
|
@@ -120,7 +120,8 @@ export async function generateVideo({ lane, model, prompt, options = {}, referen
|
|
|
120
120
|
// let the catalog's per-second rate price it otherwise.
|
|
121
121
|
const seconds = Number(data?.video?.duration) || duration;
|
|
122
122
|
const usage = { ...xaiUsage(data?.usage), seconds, resolution };
|
|
123
|
-
if (!url)
|
|
123
|
+
if (!url)
|
|
124
|
+
throw withReportedUsage(mediaError('xAI video finished without a URL', 'MEDIA_EMPTY_RESULT', 502), usage);
|
|
124
125
|
const bytes = await downloadPublicMedia(url, { signal, label: 'xAI video' }).catch((error) => {
|
|
125
126
|
throw withReportedUsage(error, usage);
|
|
126
127
|
});
|
|
@@ -300,7 +300,11 @@ async function loadTokenizer() {
|
|
|
300
300
|
async function createExtractor(device) {
|
|
301
301
|
const ort = loadOrt();
|
|
302
302
|
const encode = await loadTokenizer();
|
|
303
|
-
const graphPath = await ensureModelGraph(
|
|
303
|
+
const graphPath = await ensureModelGraph(
|
|
304
|
+
MODEL_CACHE_DIR,
|
|
305
|
+
MODEL_ID,
|
|
306
|
+
getEmbeddingModelGraphFile(MODEL_ID, configuredDtype)
|
|
307
|
+
);
|
|
304
308
|
const session = await ort.InferenceSession.create(graphPath, {
|
|
305
309
|
executionProviders: [device],
|
|
306
310
|
intraOpNumThreads: INTRA_OP_THREADS,
|
|
@@ -397,8 +401,7 @@ async function loadExtractor() {
|
|
|
397
401
|
} catch (e) {
|
|
398
402
|
// Unprivileged processes cannot raise priority back (EACCES/EPERM);
|
|
399
403
|
// on Linux that is expected, not a stuck state worth warning about.
|
|
400
|
-
const expectedOnLinux =
|
|
401
|
-
process.platform === 'linux' && /EACCES|EPERM/.test(`${e?.code} ${e?.message}`);
|
|
404
|
+
const expectedOnLinux = process.platform === 'linux' && /EACCES|EPERM/.test(`${e?.code} ${e?.message}`);
|
|
402
405
|
if (!expectedOnLinux) {
|
|
403
406
|
__mixdogMemoryLog(
|
|
404
407
|
`[embed-worker] WARN: process priority stuck below normal (restore failed: ${e?.message || e})\n`
|
|
@@ -6,13 +6,7 @@
|
|
|
6
6
|
import { runFullBackfill } from '../memory-ops-policy.mjs';
|
|
7
7
|
import { throwIfAborted } from '../memory-cycle-shared.mjs';
|
|
8
8
|
|
|
9
|
-
export function createBackfillAction({
|
|
10
|
-
getDb,
|
|
11
|
-
dataDir,
|
|
12
|
-
awaitCycle1Run,
|
|
13
|
-
ingestTranscriptFile,
|
|
14
|
-
cwdFromTranscriptPath,
|
|
15
|
-
}) {
|
|
9
|
+
export function createBackfillAction({ getDb, dataDir, awaitCycle1Run, ingestTranscriptFile, cwdFromTranscriptPath }) {
|
|
16
10
|
// Whole-action backfill mutex (transport-agnostic). memory-cycle1's
|
|
17
11
|
// _runCycle1InFlight only protects cycle1; ingest workers
|
|
18
12
|
// (memory-ops-policy.mjs) can still overlap if a second backfill
|
|
@@ -315,12 +315,7 @@ function createCycle1PacketGenerator({ callLlm, request, signal, inputTokenBudge
|
|
|
315
315
|
|
|
316
316
|
export async function generateCycle1Chunks(
|
|
317
317
|
rows,
|
|
318
|
-
{
|
|
319
|
-
callLlm,
|
|
320
|
-
request = {},
|
|
321
|
-
inputTokenBudget = CYCLE1_INPUT_TOKEN_BUDGET,
|
|
322
|
-
signal = request.signal,
|
|
323
|
-
} = {}
|
|
318
|
+
{ callLlm, request = {}, inputTokenBudget = CYCLE1_INPUT_TOKEN_BUDGET, signal = request.signal } = {}
|
|
324
319
|
) {
|
|
325
320
|
const startedAt = Date.now();
|
|
326
321
|
const stats = { groupingCalls: 0, verificationCalls: 0, llmMs: 0, verificationMs: 0, retries: 0, fragments: 0 };
|
|
@@ -91,6 +91,7 @@ function installPoolErrorHandler(pool, label, { onConnectionLoss } = {}) {
|
|
|
91
91
|
|
|
92
92
|
import { resolve } from 'node:path';
|
|
93
93
|
import { ensurePgInstance as supervisorEnsure, stopOwnedPgForShutdown } from './supervisor.mjs';
|
|
94
|
+
import { createSharedAbortable } from '../../../shared/abort-race.mjs';
|
|
94
95
|
|
|
95
96
|
export { installOwnedPgExitStop, pgClusterExists } from './supervisor.mjs';
|
|
96
97
|
|
|
@@ -195,7 +196,7 @@ export function pendingDurablePgOps() {
|
|
|
195
196
|
* Wait, bounded, until no durable-schema operation is in flight (including
|
|
196
197
|
* ones issued while waiting). Resolves true when drained, false at the bound.
|
|
197
198
|
*/
|
|
198
|
-
|
|
199
|
+
async function drainDurablePgOps({ timeoutMs = 0 } = {}) {
|
|
199
200
|
const deadline = Date.now() + Math.max(0, Number(timeoutMs) || 0);
|
|
200
201
|
while (_durableInFlight.size > 0) {
|
|
201
202
|
const remaining = deadline - Date.now();
|
|
@@ -488,78 +489,84 @@ export async function ensurePgInstance(dataDir, opts = {}) {
|
|
|
488
489
|
const key = instanceKey(dataDir, schema);
|
|
489
490
|
|
|
490
491
|
if (instances.has(key)) return instances.get(key);
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
// check-then-create with a session-level advisory lock held on a single
|
|
517
|
-
// dedicated client. Concurrent first-boot workers block on the same
|
|
518
|
-
// cluster-global key here and only one issues the CREATE; the rest see
|
|
519
|
-
// the row present after the holder releases. Released in finally.
|
|
520
|
-
await withClusterAdvisoryLock(adminPool, ADVISORY_OBJID_CREATE_DB, 'create-db', async (adminClient) => {
|
|
521
|
-
const r = await adminClient.query(`SELECT 1 FROM pg_database WHERE datname = $1`, [PG_DB]);
|
|
522
|
-
if (r.rows.length === 0) {
|
|
523
|
-
await adminClient.query(`CREATE DATABASE ${PG_DB}`);
|
|
524
|
-
__mixdogMemoryLog(`[pg-adapter] created database ${PG_DB}\n`);
|
|
525
|
-
}
|
|
492
|
+
// `opts.signal` withdraws this caller; the shared open is cancelled once
|
|
493
|
+
// every caller has (createSharedAbortable).
|
|
494
|
+
if (opening.has(key)) return opening.get(key).join(opts.signal);
|
|
495
|
+
|
|
496
|
+
const shared = createSharedAbortable(
|
|
497
|
+
async (signal) => {
|
|
498
|
+
// 1. Let supervisor-pg own PG startup and health-checking.
|
|
499
|
+
// Returns { host, port, runtimeDir, pgdataDir }.
|
|
500
|
+
const { host, port, runtimeDir, pgdataDir } = await supervisorEnsure(dataDir, { signal });
|
|
501
|
+
signal.throwIfAborted();
|
|
502
|
+
|
|
503
|
+
// 2. Connect via node-postgres; auto-create the mixdog database if absent.
|
|
504
|
+
const { default: pg } = await import('pg');
|
|
505
|
+
|
|
506
|
+
const PG_USER = 'postgres';
|
|
507
|
+
const PG_DB = 'mixdog';
|
|
508
|
+
|
|
509
|
+
const adminPool = new pg.Pool({
|
|
510
|
+
host,
|
|
511
|
+
port,
|
|
512
|
+
user: PG_USER,
|
|
513
|
+
database: 'postgres',
|
|
514
|
+
password: '',
|
|
515
|
+
max: 1,
|
|
516
|
+
idleTimeoutMillis: 5_000,
|
|
526
517
|
});
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
518
|
+
installPoolErrorHandler(adminPool, 'admin-pool');
|
|
519
|
+
try {
|
|
520
|
+
// CREATE DATABASE cannot run inside a transaction, so guard the
|
|
521
|
+
// check-then-create with a session-level advisory lock held on a single
|
|
522
|
+
// dedicated client. Concurrent first-boot workers block on the same
|
|
523
|
+
// cluster-global key here and only one issues the CREATE; the rest see
|
|
524
|
+
// the row present after the holder releases. Released in finally.
|
|
525
|
+
await withClusterAdvisoryLock(adminPool, ADVISORY_OBJID_CREATE_DB, 'create-db', async (adminClient) => {
|
|
526
|
+
const r = await adminClient.query(`SELECT 1 FROM pg_database WHERE datname = $1`, [PG_DB]);
|
|
527
|
+
if (r.rows.length === 0) {
|
|
528
|
+
await adminClient.query(`CREATE DATABASE ${PG_DB}`);
|
|
529
|
+
__mixdogMemoryLog(`[pg-adapter] created database ${PG_DB}\n`);
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
} finally {
|
|
533
|
+
await adminPool.end();
|
|
534
|
+
}
|
|
530
535
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
// 4. Bootstrap extensions + schemas once (idempotent).
|
|
547
|
-
await bootstrapInstance(pgPool, resolve(dataDir));
|
|
548
|
-
|
|
549
|
-
// 5. Build the compat db shim.
|
|
550
|
-
const db = makeCompatDb(pgPool, schema, dataDir);
|
|
551
|
-
|
|
552
|
-
const result = { db, pool: pgPool, host, port, runtimeDir, pgdataDir };
|
|
553
|
-
instances.set(key, result);
|
|
554
|
-
return result;
|
|
555
|
-
})();
|
|
536
|
+
// 3. Production pool.
|
|
537
|
+
const pgPool = new pg.Pool({
|
|
538
|
+
host,
|
|
539
|
+
port,
|
|
540
|
+
user: PG_USER,
|
|
541
|
+
database: PG_DB,
|
|
542
|
+
password: '',
|
|
543
|
+
max: 5,
|
|
544
|
+
idleTimeoutMillis: 30_000,
|
|
545
|
+
connectionTimeoutMillis: 10_000,
|
|
546
|
+
});
|
|
547
|
+
installPoolErrorHandler(pgPool, `pool:${schema}`, {
|
|
548
|
+
onConnectionLoss: () => _recoverPgConnection(dataDir, schema),
|
|
549
|
+
});
|
|
556
550
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
551
|
+
// 4. Bootstrap extensions + schemas once (idempotent).
|
|
552
|
+
await bootstrapInstance(pgPool, resolve(dataDir));
|
|
553
|
+
|
|
554
|
+
// 5. Build the compat db shim.
|
|
555
|
+
const db = makeCompatDb(pgPool, schema, dataDir);
|
|
556
|
+
|
|
557
|
+
const result = { db, pool: pgPool, host, port, runtimeDir, pgdataDir };
|
|
558
|
+
instances.set(key, result);
|
|
559
|
+
return result;
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
onSettled: () => {
|
|
563
|
+
if (opening.get(key) === shared) opening.delete(key);
|
|
564
|
+
},
|
|
565
|
+
}
|
|
566
|
+
);
|
|
567
|
+
|
|
568
|
+
opening.set(key, shared);
|
|
569
|
+
return shared.join(opts.signal);
|
|
563
570
|
}
|
|
564
571
|
|
|
565
572
|
// ---------------------------------------------------------------------------
|