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.
Files changed (515) hide show
  1. package/NOTICE.md +2 -2
  2. package/README.md +2 -2
  3. package/package.json +2 -11
  4. package/scripts/check-voice-runtime-inputs.mjs +25 -0
  5. package/scripts/daemon-stub.mjs +1 -1
  6. package/scripts/gate-local.mjs +16 -5
  7. package/scripts/latest-guard.mjs +21 -9
  8. package/scripts/lib/isolated-test-env.mjs +10 -5
  9. package/scripts/lib/run-node-tests.mjs +37 -37
  10. package/scripts/lib/scoped-temp.d.mts +16 -0
  11. package/scripts/lib/scoped-temp.mjs +123 -0
  12. package/scripts/lib/stale-temp.mjs +44 -0
  13. package/scripts/native-release-status.mjs +2 -2
  14. package/scripts/post-release-check.mjs +1 -6
  15. package/scripts/pptx-kit-index.mjs +17 -4
  16. package/scripts/recall-history-cases.json +1 -1
  17. package/scripts/suite-health.mjs +20 -2
  18. package/scripts/test-data-dir.mjs +10 -7
  19. package/scripts/test.mjs +7 -11
  20. package/src/agents/reviewer/AGENT.md +7 -11
  21. package/src/agents/reviewer/agent.json +1 -1
  22. package/src/app.mjs +4 -0
  23. package/src/defaults/shipped-history.json +22 -1
  24. package/src/defaults/skills/browser-use/SKILL.md +62 -12
  25. package/src/defaults/skills/code-tidy/SKILL.md +142 -270
  26. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
  27. package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
  28. package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
  29. package/src/defaults/skills/computer-use/SKILL.md +35 -9
  30. package/src/defaults/skills/setup/SKILL.md +22 -0
  31. package/src/defaults/skills/setup/references/actions.md +39 -5
  32. package/src/defaults/skills/setup/references/surfaces.md +11 -2
  33. package/src/defaults/skills/visualize/SKILL.md +2 -1
  34. package/src/lib/rules-builder.cjs +39 -20
  35. package/src/output-styles/common.md +2 -2
  36. package/src/output-styles/simple.md +1 -1
  37. package/src/rules/agent/AGENT.md +11 -10
  38. package/src/rules/lead/LEAD.md +23 -20
  39. package/src/rules/shared/05-tool-calls.md +5 -6
  40. package/src/rules/shared/10-tool-workflow.md +4 -0
  41. package/src/rules/shared/45-destructive.md +0 -2
  42. package/src/rules/shared/50-execution.md +2 -0
  43. package/src/rules/shared/60-verification.md +7 -5
  44. package/src/rules/shared/70-delivery.md +7 -9
  45. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
  46. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
  47. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
  48. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
  49. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
  50. package/src/runtime/agent/orchestrator/config.mjs +17 -1
  51. package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
  52. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +73 -68
  53. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +57 -2
  54. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
  55. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +176 -72
  56. package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
  57. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +5 -2
  58. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +192 -133
  59. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
  60. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
  61. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
  62. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
  63. package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
  66. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  67. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
  70. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +159 -69
  71. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
  72. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +70 -33
  73. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
  74. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
  75. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +123 -102
  76. package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
  77. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
  78. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
  79. package/src/runtime/agent/orchestrator/providers/cursor.mjs +25 -13
  80. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
  81. package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
  82. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
  83. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
  84. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
  85. package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
  87. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
  88. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
  89. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
  90. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
  91. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  92. package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
  93. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -1
  94. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
  96. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
  98. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
  99. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  100. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
  101. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
  102. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  103. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
  104. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
  105. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
  106. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
  107. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
  109. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
  110. package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
  111. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
  112. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
  113. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
  114. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +20 -12
  115. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
  116. package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
  117. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
  118. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
  119. package/src/runtime/agent/orchestrator/providers/registry.mjs +38 -0
  120. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
  121. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
  122. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +49 -16
  123. package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
  124. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
  125. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
  126. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -6
  127. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +9 -3
  128. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
  129. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
  130. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -1
  131. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
  132. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
  133. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +798 -0
  134. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  135. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
  136. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
  137. package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
  138. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +280 -45
  139. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
  140. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
  141. package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
  142. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
  143. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
  144. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
  145. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
  146. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
  147. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -1
  148. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
  149. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
  150. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
  151. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
  152. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +51 -25
  153. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +37 -3
  154. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +3 -1
  155. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
  156. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
  157. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
  158. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +31 -3
  159. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +20 -3
  160. package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
  161. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +5 -4
  162. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  163. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
  164. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +49 -21
  165. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
  166. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +12 -3
  167. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
  168. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
  169. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
  170. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
  171. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
  172. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
  173. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
  174. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +24 -14
  175. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
  176. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
  177. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +130 -0
  178. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
  179. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +81 -8
  180. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
  181. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -8
  182. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
  183. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
  184. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +9 -4
  185. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
  186. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
  187. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +21 -5
  188. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
  189. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +24 -9
  190. package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
  191. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +150 -0
  192. package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
  193. package/src/runtime/agent/orchestrator/session/routine-result.mjs +36 -9
  194. package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
  195. package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
  196. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
  197. package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
  198. package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
  199. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
  200. package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
  201. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
  202. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
  203. package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
  204. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +8 -6
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
  206. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
  207. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +81 -22
  208. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +92 -3
  209. package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
  210. package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
  211. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
  212. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
  213. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +79 -3
  214. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
  215. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
  216. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
  217. package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
  218. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
  219. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
  220. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  221. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
  222. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  223. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
  224. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
  225. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +31 -6
  226. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +110 -26
  227. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
  228. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
  229. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
  230. package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
  231. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
  232. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
  233. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
  234. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
  235. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
  236. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
  237. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
  238. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
  239. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  240. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  241. package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
  242. package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
  243. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
  244. package/src/runtime/browser-bridge/action-schema.mjs +13 -2
  245. package/src/runtime/browser-bridge/client.mjs +73 -8
  246. package/src/runtime/browser-bridge/input-fields.mjs +8 -9
  247. package/src/runtime/browser-bridge/mail-links.mjs +107 -0
  248. package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
  249. package/src/runtime/channels/lib/config.mjs +6 -61
  250. package/src/runtime/channels/lib/crash-log.mjs +1 -1
  251. package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
  252. package/src/runtime/channels/lib/event-queue.mjs +3 -18
  253. package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
  254. package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
  255. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
  256. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
  257. package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
  258. package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
  259. package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
  260. package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
  261. package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
  262. package/src/runtime/channels/lib/scheduler.mjs +33 -147
  263. package/src/runtime/channels/lib/schedules-db.mjs +34 -11
  264. package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
  265. package/src/runtime/channels/lib/tool-dispatch.mjs +1 -2
  266. package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
  267. package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
  268. package/src/runtime/channels/lib/whisper-port.mjs +10 -1
  269. package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
  270. package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
  271. package/src/runtime/channels/lib/worker-main.mjs +10 -34
  272. package/src/runtime/computer-bridge/action-schema.mjs +42 -1
  273. package/src/runtime/computer-bridge/actions.mjs +9 -1
  274. package/src/runtime/computer-bridge/client.mjs +180 -8
  275. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  276. package/src/runtime/computer-bridge/error-recovery.mjs +28 -2
  277. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
  278. package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
  279. package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
  280. package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
  281. package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
  282. package/src/runtime/connectors/gmail-app-password.mjs +172 -39
  283. package/src/runtime/connectors/google-mcp.mjs +10 -12
  284. package/src/runtime/local-provider/server-process.mjs +2 -1
  285. package/src/runtime/media/adapters/gemini-video.mjs +4 -1
  286. package/src/runtime/media/adapters/xai-media.mjs +2 -1
  287. package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
  288. package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
  289. package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
  290. package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
  291. package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
  292. package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
  293. package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
  294. package/src/runtime/office/design/design-art-direction.mjs +29 -27
  295. package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
  296. package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
  297. package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
  298. package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
  299. package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
  300. package/src/runtime/office/quality/design-review.mjs +1 -5
  301. package/src/runtime/office/quality/document-brief.mjs +1 -5
  302. package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
  303. package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
  304. package/src/runtime/session-search/session-search-db.mjs +73 -9
  305. package/src/runtime/session-search/session-search-ingest.mjs +110 -20
  306. package/src/runtime/shared/abort-race.mjs +89 -0
  307. package/src/runtime/shared/agent-avatar.d.mts +0 -1
  308. package/src/runtime/shared/agent-kind.mjs +4 -3
  309. package/src/runtime/shared/bot-owned-work.mjs +56 -0
  310. package/src/runtime/shared/bounded-download.mjs +8 -3
  311. package/src/runtime/shared/config.mjs +2 -13
  312. package/src/runtime/shared/connector-accounts.mjs +20 -10
  313. package/src/runtime/shared/connector-catalog.d.mts +29 -0
  314. package/src/runtime/shared/connector-catalog.mjs +150 -0
  315. package/src/runtime/shared/connector-triggers.mjs +22 -19
  316. package/src/runtime/shared/defaults-separation.mjs +2 -17
  317. package/src/runtime/shared/delegated-approval-host.mjs +103 -11
  318. package/src/runtime/shared/err-text.mjs +24 -15
  319. package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
  320. package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
  321. package/src/runtime/shared/file-permissions.d.mts +0 -8
  322. package/src/runtime/shared/file-permissions.mjs +0 -11
  323. package/src/runtime/shared/git-direct.test-support.mjs +19 -0
  324. package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
  325. package/src/runtime/shared/handoff-artifacts.mjs +18 -8
  326. package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
  327. package/src/runtime/shared/heartbeat-interval.mjs +19 -0
  328. package/src/runtime/shared/idle-auto-clear-gate.mjs +126 -0
  329. package/src/runtime/shared/llm/cost.mjs +5 -1
  330. package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
  331. package/src/runtime/shared/loopback-listener.mjs +25 -2
  332. package/src/runtime/shared/mixy-presence.mjs +22 -0
  333. package/src/runtime/shared/model-route-chain.d.mts +7 -2
  334. package/src/runtime/shared/model-route-chain.mjs +22 -4
  335. package/src/runtime/shared/open-url.mjs +10 -0
  336. package/src/runtime/shared/orchestration.d.mts +2 -0
  337. package/src/runtime/shared/pg-schema-db.mjs +27 -16
  338. package/src/runtime/shared/profile-config.cjs +1 -1
  339. package/src/runtime/shared/prompt-origin.d.mts +4 -0
  340. package/src/runtime/shared/prompt-origin.mjs +21 -0
  341. package/src/runtime/shared/provider-accounts.mjs +7 -1
  342. package/src/runtime/shared/provider-display-name.mjs +29 -0
  343. package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
  344. package/src/runtime/shared/routine-trigger.d.mts +2 -0
  345. package/src/runtime/shared/routine-trigger.mjs +10 -0
  346. package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
  347. package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
  348. package/src/runtime/shared/session-activity-signal.mjs +48 -1
  349. package/src/runtime/shared/session-scratch.mjs +37 -0
  350. package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
  351. package/src/runtime/shared/session-sidebar-host.mjs +7 -3
  352. package/src/runtime/shared/session-turn-prelude.mjs +18 -0
  353. package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
  354. package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
  355. package/src/runtime/shared/tool-result-summary.mjs +2 -15
  356. package/src/runtime/shared/tool-surface.mjs +3 -0
  357. package/src/runtime/shared/tool-work-units.mjs +2 -0
  358. package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
  359. package/src/runtime/shared/worktrees/contract.mjs +4 -1
  360. package/src/runtime/shared/worktrees/git.mjs +15 -0
  361. package/src/runtime/shared/worktrees/manager.mjs +87 -38
  362. package/src/runtime/shared/worktrees/test-support.mjs +14 -13
  363. package/src/runtime/terminal-bridge/client.mjs +10 -1
  364. package/src/runtime/visualize-bridge/client.mjs +29 -8
  365. package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
  366. package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
  367. package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
  368. package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
  369. package/src/session-runtime/agent-home-delivery.mjs +489 -185
  370. package/src/session-runtime/agent-task-sessions.mjs +467 -229
  371. package/src/session-runtime/agent-tool-routing.mjs +8 -41
  372. package/src/session-runtime/boot/apis.mjs +9 -0
  373. package/src/session-runtime/boot/infrastructure.mjs +4 -0
  374. package/src/session-runtime/boot/providers.mjs +3 -0
  375. package/src/session-runtime/boot/tools.mjs +11 -2
  376. package/src/session-runtime/bot-status.mjs +12 -6
  377. package/src/session-runtime/bot-target.mjs +38 -0
  378. package/src/session-runtime/channel-config-api.mjs +6 -1
  379. package/src/session-runtime/config-lifecycle.mjs +6 -0
  380. package/src/session-runtime/connector-merge-migration.mjs +305 -0
  381. package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
  382. package/src/session-runtime/conversation-import/discovery.mjs +271 -25
  383. package/src/session-runtime/conversation-import/service.mjs +26 -5
  384. package/src/session-runtime/handoff-attachments.mjs +82 -31
  385. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +144 -41
  386. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -0
  387. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
  388. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
  389. package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
  390. package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
  391. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  392. package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
  393. package/src/session-runtime/lifecycle/teardown.mjs +23 -5
  394. package/src/session-runtime/mcp-glue/server-catalog.mjs +55 -3
  395. package/src/session-runtime/mcp-oauth-login.mjs +5 -3
  396. package/src/session-runtime/mixy-heartbeat.mjs +153 -33
  397. package/src/session-runtime/model-route/set-route.mjs +11 -2
  398. package/src/session-runtime/notification-bus.mjs +31 -117
  399. package/src/session-runtime/notification-completion-queue.mjs +54 -0
  400. package/src/session-runtime/notification-listeners.mjs +76 -0
  401. package/src/session-runtime/owned-automation-run.mjs +2 -1
  402. package/src/session-runtime/prewarm/channel-start.mjs +3 -7
  403. package/src/session-runtime/provider-catalog-cache.mjs +82 -36
  404. package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
  405. package/src/session-runtime/provider-models.mjs +4 -2
  406. package/src/session-runtime/resource-mcp-api.mjs +143 -224
  407. package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
  408. package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
  409. package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
  410. package/src/session-runtime/resource-mcp-servers.mjs +78 -0
  411. package/src/session-runtime/resource-mcp-store.mjs +49 -0
  412. package/src/session-runtime/resource-recall-api.mjs +6 -1
  413. package/src/session-runtime/routine-owner.mjs +40 -9
  414. package/src/session-runtime/runtime-feature-gates.mjs +5 -1
  415. package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
  416. package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
  417. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
  418. package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
  419. package/src/session-runtime/services/agent-tool/render.mjs +71 -17
  420. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +13 -4
  421. package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
  422. package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
  423. package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
  424. package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
  425. package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
  426. package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
  427. package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
  428. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
  429. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
  430. package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
  431. package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
  432. package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
  433. package/src/session-runtime/services/channel-admin.mjs +7 -13
  434. package/src/session-runtime/services/channel-session-router.mjs +4 -2
  435. package/src/session-runtime/services/provider-admin.mjs +2 -1
  436. package/src/session-runtime/services/seeds.mjs +0 -28
  437. package/src/session-runtime/session-compaction-sync.mjs +27 -0
  438. package/src/session-runtime/session-create/session-options.mjs +1 -0
  439. package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
  440. package/src/session-runtime/settings-compaction-api.mjs +2 -40
  441. package/src/session-runtime/settings-system-api.mjs +13 -1
  442. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
  443. package/src/session-runtime/setup-tool/executor.mjs +145 -22
  444. package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
  445. package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
  446. package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
  447. package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
  448. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
  449. package/src/session-runtime/tool-policy/policy-stage.mjs +17 -2
  450. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  451. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
  452. package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
  453. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
  454. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
  455. package/src/session-runtime/workspace/transitions.mjs +25 -0
  456. package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
  457. package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
  458. package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
  459. package/src/standalone/channel-transport/client-registry.mjs +24 -163
  460. package/src/standalone/channel-transport.mjs +1 -1
  461. package/src/standalone/daemon-channels-loader.mjs +10 -8
  462. package/src/standalone/daemon-session-control/list-page.mjs +72 -0
  463. package/src/standalone/daemon-session-control/request.mjs +51 -0
  464. package/src/standalone/daemon-session-control/room-host.mjs +57 -0
  465. package/src/standalone/daemon-session-control/route-control.mjs +48 -0
  466. package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
  467. package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
  468. package/src/standalone/daemon-session-control.mjs +142 -190
  469. package/src/standalone/daemon.mjs +26 -3
  470. package/src/standalone/session-client.mjs +7 -3
  471. package/src/standalone/session-protocol.mjs +5 -0
  472. package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
  473. package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
  474. package/src/standalone/session-service/quota-resume.mjs +3 -0
  475. package/src/standalone/session-service/retention/busy.mjs +65 -1
  476. package/src/standalone/session-service/session-calls/session-action.mjs +30 -2
  477. package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
  478. package/src/standalone/session-tool-output.mjs +15 -7
  479. package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
  480. package/src/tui/app/app-view/floating-panel.jsx +7 -4
  481. package/src/tui/app/model-options.mjs +3 -28
  482. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
  483. package/src/tui/app/model-picker/route-selection.mjs +5 -6
  484. package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
  485. package/src/tui/app/provider-setup/provider-items.mjs +8 -2
  486. package/src/tui/app/provider-setup-picker.mjs +9 -5
  487. package/src/tui/components/QuestionPanel.jsx +13 -4
  488. package/src/tui/dist/index.mjs +133 -92
  489. package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
  490. package/src/tui/session/initial-state.mjs +3 -0
  491. package/src/tui/session/notice-surface.mjs +7 -1
  492. package/src/tui/session/queue-helpers.mjs +3 -42
  493. package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
  494. package/src/tui/session/session-api/routes-write.mjs +5 -1
  495. package/src/tui/session/session-api/routes.mjs +1 -0
  496. package/src/tui/session/session-api/settings.mjs +17 -1
  497. package/src/tui/session/session-flow/auto-clear.mjs +4 -33
  498. package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
  499. package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
  500. package/src/tui/session/session-flow/drain-retry.mjs +35 -0
  501. package/src/tui/session/session-flow/drain.mjs +118 -149
  502. package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
  503. package/src/tui/session/session-flow/queue/take-entries.mjs +16 -2
  504. package/src/tui/session/tool-approval-delegation.mjs +64 -0
  505. package/src/tui/session/tool-approval-request.mjs +39 -0
  506. package/src/tui/session/tool-approval-settle.mjs +62 -0
  507. package/src/tui/session/tool-approval.mjs +69 -115
  508. package/src/tui/session/tui-steering-persist.mjs +8 -1
  509. package/src/tui/session/turn-stream.mjs +19 -2
  510. package/src/tui/session/turn.mjs +1 -0
  511. package/src/ui/statusline-agents.mjs +1 -3
  512. package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
  513. package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
  514. package/scripts/code-graph-description-contract.mjs +0 -188
  515. 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 { accountInstructions } from '../shared/connector-accounts.mjs';
12
+ import { connectorAccountsInstructions } from '../shared/connector-accounts.mjs';
13
13
 
14
14
  const PROTOCOL_VERSION = '2025-06-18';
15
- export const GMAIL_IMAP = Object.freeze({ host: 'imap.gmail.com', port: 993, secure: true });
16
- export const GMAIL_SMTP = Object.freeze({ host: 'smtp.gmail.com', port: 465, secure: true });
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
- export class GmailAppPasswordError extends Error {
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
- export const gmailClients = {
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 part (else text/html) that is the message's body. */
218
- function bodyPart(root) {
219
- let html = null;
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 null;
280
+ if (!node || String(node.type || '').startsWith('message/')) return;
222
281
  if (node.childNodes?.length) {
223
- for (const child of node.childNodes) {
224
- const found = walk(child);
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 null;
285
+ if (node.disposition === 'attachment') return;
230
286
  const part = node.part || '1';
231
- if (node.type === 'text/plain') return { part, html: false };
232
- if (node.type === 'text/html' && !html) html = { part, html: true };
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
- return walk(root) || html;
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 the body text, decoded to UTF-8. */
268
- async function bodyText(client, uid, structure, maxBytes) {
269
- const picked = bodyPart(structure);
270
- if (!picked) return '';
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
- const value = Buffer.concat(chunks).subarray(0, maxBytes).toString('utf8');
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
- const raw = Array.isArray(args.ids) ? args.ids : args.ids == null ? [] : [args.ids];
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: 'Read one email by its id from search_emails: headers, text body, and attachment names and sizes.',
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
- { uid: true, envelope: true, internalDate: true, bodyStructure: true, threadId: true },
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 body = await bodyText(client, message.uid, message.bodyStructure, READ_BYTES);
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: accountInstructions('Gmail', session.email),
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 { accountInstructions } from '../shared/connector-accounts.mjs';
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
- instructions: accountInstructions(service.name, info.email),
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.listen(0, '127.0.0.1', () => {
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 = { seconds: parameters.durationSeconds || VEO_DEFAULT_SECONDS, resolution: parameters.resolution || '720p' };
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) throw withReportedUsage(mediaError('xAI video finished without a URL', 'MEDIA_EMPTY_RESULT', 502), usage);
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(MODEL_CACHE_DIR, MODEL_ID, getEmbeddingModelGraphFile(MODEL_ID, configuredDtype));
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
- export async function drainDurablePgOps({ timeoutMs = 0 } = {}) {
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
- if (opening.has(key)) return opening.get(key);
492
-
493
- const promise = (async () => {
494
- // 1. Let supervisor-pg own PG startup and health-checking.
495
- // Returns { host, port, runtimeDir, pgdataDir }.
496
- const { host, port, runtimeDir, pgdataDir } = await supervisorEnsure(dataDir);
497
-
498
- // 2. Connect via node-postgres; auto-create the mixdog database if absent.
499
- const { default: pg } = await import('pg');
500
-
501
- const PG_USER = 'postgres';
502
- const PG_DB = 'mixdog';
503
-
504
- const adminPool = new pg.Pool({
505
- host,
506
- port,
507
- user: PG_USER,
508
- database: 'postgres',
509
- password: '',
510
- max: 1,
511
- idleTimeoutMillis: 5_000,
512
- });
513
- installPoolErrorHandler(adminPool, 'admin-pool');
514
- try {
515
- // CREATE DATABASE cannot run inside a transaction, so guard the
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
- } finally {
528
- await adminPool.end();
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
- // 3. Production pool.
532
- const pgPool = new pg.Pool({
533
- host,
534
- port,
535
- user: PG_USER,
536
- database: PG_DB,
537
- password: '',
538
- max: 5,
539
- idleTimeoutMillis: 30_000,
540
- connectionTimeoutMillis: 10_000,
541
- });
542
- installPoolErrorHandler(pgPool, `pool:${schema}`, {
543
- onConnectionLoss: () => _recoverPgConnection(dataDir, schema),
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
- opening.set(key, promise);
558
- try {
559
- return await promise;
560
- } finally {
561
- opening.delete(key);
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
  // ---------------------------------------------------------------------------