mixdog 1.2.2 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (415) hide show
  1. package/README.md +18 -0
  2. package/package.json +2 -1
  3. package/scripts/provider-pingpong-live.mjs +509 -0
  4. package/src/agents/advisor/AGENT.md +1 -1
  5. package/src/agents/advisor/agent.json +1 -1
  6. package/src/defaults/shipped-history.json +5 -0
  7. package/src/defaults/skills/setup/SKILL.md +6 -1
  8. package/src/defaults/skills/setup/references/actions.md +44 -22
  9. package/src/defaults/skills/setup/references/surfaces.md +10 -8
  10. package/src/headless-exec.mjs +15 -8
  11. package/src/lib/keychain-cjs.cjs +7 -4
  12. package/src/output-styles/common.md +11 -10
  13. package/src/rules/agent/AGENT.md +5 -5
  14. package/src/rules/lead/LEAD.md +5 -5
  15. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +92 -61
  16. package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
  17. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
  18. package/src/runtime/agent/orchestrator/config.mjs +5 -0
  19. package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
  20. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
  21. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
  22. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
  23. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +2 -0
  24. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +24 -17
  25. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +7 -6
  26. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  27. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +29 -10
  28. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-blocks.mjs +20 -1
  29. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
  30. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
  31. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
  32. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
  33. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
  34. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
  35. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -0
  36. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
  37. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +6 -0
  38. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +58 -7
  39. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +29 -10
  40. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +16 -3
  41. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
  42. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +12 -6
  43. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  44. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +114 -72
  45. package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
  46. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +37 -5
  47. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +1 -0
  49. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
  50. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +3 -0
  51. package/src/runtime/agent/orchestrator/providers/cursor.mjs +9 -2
  52. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
  53. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
  54. package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
  55. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
  56. package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
  57. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -1
  58. package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
  59. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +25 -8
  60. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +87 -5
  61. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +41 -5
  62. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +13 -13
  63. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +355 -37
  64. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -2
  65. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +93 -47
  66. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +55 -10
  67. package/src/runtime/agent/orchestrator/providers/gemini.mjs +26 -8
  68. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  69. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +1 -0
  70. package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
  71. package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
  72. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
  73. package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
  74. package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
  75. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
  76. package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
  77. package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
  78. package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
  79. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
  80. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
  81. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
  82. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
  83. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +1 -1
  84. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  85. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
  86. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
  88. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +121 -61
  89. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
  90. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +5 -2
  91. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
  92. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
  93. package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +1 -1
  94. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +47 -2
  95. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
  96. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
  97. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
  98. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +42 -24
  99. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +63 -25
  100. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
  101. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
  102. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +53 -5
  103. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +7 -1
  104. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +103 -26
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +23 -4
  107. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +11 -6
  108. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -1
  109. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +15 -23
  110. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +23 -10
  111. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +25 -4
  112. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
  113. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
  114. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +4 -0
  115. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +5 -0
  116. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
  117. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +80 -0
  118. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +3 -1
  119. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +7 -0
  120. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +7 -1
  121. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +71 -24
  122. package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
  123. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
  124. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
  125. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
  126. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
  127. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
  128. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +12 -0
  129. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
  130. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +11 -3
  131. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
  132. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
  133. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
  134. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +147 -8
  135. package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
  136. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +5 -0
  137. package/src/runtime/agent/orchestrator/providers/transport-scope.d.mts +6 -0
  138. package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
  139. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
  140. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
  141. package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
  142. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
  143. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
  144. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
  145. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
  146. package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
  147. package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
  148. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
  149. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
  150. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  151. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
  152. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
  153. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
  154. package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
  155. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -1
  156. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
  157. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
  158. package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
  159. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
  160. package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
  161. package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
  162. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
  163. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
  164. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
  165. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
  166. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
  167. package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
  168. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
  169. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
  170. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
  171. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
  172. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +63 -20
  173. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +2 -1
  174. package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
  175. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
  176. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
  177. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
  178. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
  179. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +4 -1
  180. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
  181. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
  182. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +16 -0
  183. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
  184. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  185. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  186. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
  187. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
  188. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +15 -3
  189. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
  190. package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
  191. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
  192. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
  193. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
  194. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
  195. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
  196. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +13 -0
  197. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
  198. package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
  199. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
  200. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
  201. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
  202. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
  203. package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
  204. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
  205. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
  206. package/src/runtime/agent/orchestrator/session/store-summary-archived-agent.mjs +34 -15
  207. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
  208. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +8 -0
  209. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +10 -1
  210. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
  211. package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
  212. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
  213. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
  214. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
  215. package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
  216. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
  217. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
  218. package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
  219. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
  220. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
  221. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
  222. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
  223. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
  224. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
  225. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
  226. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
  227. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
  228. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
  229. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
  230. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
  231. package/src/runtime/computer-bridge/action-schema.mjs +44 -21
  232. package/src/runtime/github/tool.mjs +92 -2
  233. package/src/runtime/memory/index.mjs +1 -0
  234. package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
  235. package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
  236. package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
  237. package/src/runtime/memory/lib/pg/process.mjs +31 -26
  238. package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
  239. package/src/runtime/memory/lib/pg/supervisor.mjs +108 -3
  240. package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
  241. package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
  242. package/src/runtime/office/README.md +1 -1
  243. package/src/runtime/office/authoring/html-document-author.mjs +2 -0
  244. package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
  245. package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
  246. package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
  247. package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
  248. package/src/runtime/office/core/office-actions-read.mjs +3 -0
  249. package/src/runtime/office/core/office-render-preview.mjs +2 -0
  250. package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
  251. package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
  252. package/src/runtime/office/core/office-sessions.mjs +5 -0
  253. package/src/runtime/office/core/office-transactions.mjs +3 -0
  254. package/src/runtime/office/core/office-write-ledger.mjs +98 -0
  255. package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
  256. package/src/runtime/office/index.mjs +30 -0
  257. package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
  258. package/src/runtime/office/pdf/pdf-render.mjs +23 -7
  259. package/src/runtime/office/pdf/pdf-security.mjs +2 -0
  260. package/src/runtime/office/portable/portable-opc.mjs +4 -1
  261. package/src/runtime/office/quality/visual-diff.mjs +2 -0
  262. package/src/runtime/shared/bounded-file-read.mjs +13 -0
  263. package/src/runtime/shared/bounded-line-diff.mjs +182 -0
  264. package/src/runtime/shared/buffered-appender.mjs +18 -0
  265. package/src/runtime/shared/config.mjs +78 -18
  266. package/src/runtime/shared/err-text.mjs +6 -0
  267. package/src/runtime/shared/file-permissions.d.mts +6 -1
  268. package/src/runtime/shared/file-permissions.mjs +54 -6
  269. package/src/runtime/shared/llm/anthropic-effort.mjs +6 -0
  270. package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
  271. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
  272. package/src/runtime/shared/loopback-listener.mjs +9 -0
  273. package/src/runtime/shared/provider-api-key.mjs +2 -0
  274. package/src/runtime/shared/review-diff.mjs +52 -0
  275. package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
  276. package/src/runtime/shared/session-sidebar-host.mjs +38 -0
  277. package/src/runtime/shared/slow-span.mjs +114 -0
  278. package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
  279. package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
  280. package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
  281. package/src/runtime/shared/tool-card-model.mjs +5 -2
  282. package/src/runtime/shared/tool-output-limit.mjs +4 -0
  283. package/src/runtime/shared/tool-primitives.mjs +13 -0
  284. package/src/runtime/shared/tool-result-summary.mjs +8 -1
  285. package/src/runtime/shared/tool-surface.mjs +18 -3
  286. package/src/runtime/shared/tool-work-units.mjs +40 -4
  287. package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
  288. package/src/runtime/shared/transcript-writer.mjs +10 -2
  289. package/src/runtime/shared/turn-changes.mjs +268 -100
  290. package/src/runtime/shared/turn-review-reply.mjs +8 -39
  291. package/src/runtime/shared/turn-snapshot-store.mjs +19 -108
  292. package/src/runtime/shared/turn-snapshot.mjs +857 -683
  293. package/src/runtime/shared/turn-worktree-snapshot.mjs +183 -255
  294. package/src/runtime/shared/user-data-guard.mjs +262 -49
  295. package/src/runtime/shared/worktrees/carry.mjs +531 -0
  296. package/src/runtime/shared/worktrees/contract.mjs +34 -0
  297. package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
  298. package/src/runtime/shared/worktrees/git-head.mjs +205 -0
  299. package/src/runtime/shared/worktrees/git.mjs +8 -3
  300. package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
  301. package/src/runtime/shared/worktrees/manager.mjs +156 -8
  302. package/src/runtime/shared/worktrees/merge.mjs +31 -21
  303. package/src/runtime/shared/worktrees/paths.d.mts +6 -0
  304. package/src/runtime/terminal-bridge/client.mjs +44 -11
  305. package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
  306. package/src/runtime/tidy/apply.mjs +47 -2
  307. package/src/runtime/tidy/run-engines.mjs +11 -5
  308. package/src/session-runtime/agent-tool-routing.mjs +19 -13
  309. package/src/session-runtime/boot/apis.mjs +13 -5
  310. package/src/session-runtime/boot/facade.mjs +1 -0
  311. package/src/session-runtime/boot/infrastructure.mjs +6 -1
  312. package/src/session-runtime/boot/session.mjs +0 -1
  313. package/src/session-runtime/boot/tools.mjs +4 -1
  314. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
  315. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
  316. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
  317. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
  318. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
  319. package/src/session-runtime/internal-tool-executor.mjs +6 -2
  320. package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
  321. package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
  322. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -3
  323. package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
  324. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
  325. package/src/session-runtime/lifecycle/teardown.mjs +72 -7
  326. package/src/session-runtime/lifecycle-api.mjs +1 -0
  327. package/src/session-runtime/mcp-glue/connect.mjs +17 -5
  328. package/src/session-runtime/model-route/set-route.mjs +2 -2
  329. package/src/session-runtime/native-web-search-usage.mjs +51 -0
  330. package/src/session-runtime/native-web-search.mjs +10 -1
  331. package/src/session-runtime/new-session-config.mjs +4 -2
  332. package/src/session-runtime/provider-auth/login.mjs +4 -0
  333. package/src/session-runtime/resource-scope-decorate.mjs +10 -3
  334. package/src/session-runtime/runtime-review-api.mjs +8 -2
  335. package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
  336. package/src/session-runtime/services/agent-tool/lead-worker-index/forgotten-leads.mjs +29 -0
  337. package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
  338. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
  339. package/src/session-runtime/services/agent-tool/tag-registry.mjs +15 -1
  340. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +10 -1
  341. package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
  342. package/src/session-runtime/services/agent-tool.mjs +3 -0
  343. package/src/session-runtime/services/channel-admin.mjs +19 -3
  344. package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
  345. package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
  346. package/src/session-runtime/services/provider-admin.mjs +81 -15
  347. package/src/session-runtime/session-transcript-purge.mjs +32 -0
  348. package/src/session-runtime/session-turn-api.mjs +14 -5
  349. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
  350. package/src/session-runtime/setup-tool/executor.mjs +36 -6
  351. package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
  352. package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
  353. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
  354. package/src/session-runtime/skills-api.mjs +3 -5
  355. package/src/session-runtime/tool-surface.mjs +5 -1
  356. package/src/session-runtime/turn/compaction-gate.mjs +25 -0
  357. package/src/session-runtime/turn/session-ops.mjs +7 -5
  358. package/src/session-runtime/turn/side-question-ops.mjs +22 -0
  359. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
  360. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
  361. package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
  362. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  363. package/src/session-runtime/turn/turn-run.mjs +25 -9
  364. package/src/session-runtime/usage-stats-api.mjs +43 -11
  365. package/src/session-runtime/workspace/project-branches.mjs +7 -5
  366. package/src/session-runtime/workspace/transition-steps.mjs +129 -31
  367. package/src/session-runtime/workspace/transitions.mjs +190 -15
  368. package/src/standalone/daemon-agent-control.mjs +9 -1
  369. package/src/standalone/daemon-local-session-bridge.mjs +14 -1
  370. package/src/standalone/daemon-owner.mjs +17 -4
  371. package/src/standalone/daemon-pg-stop.mjs +51 -0
  372. package/src/standalone/daemon-stop-budget.d.mts +2 -0
  373. package/src/standalone/daemon-stop-budget.mjs +18 -0
  374. package/src/standalone/daemon-stored-session-views.mjs +2 -0
  375. package/src/standalone/daemon-telemetry.mjs +19 -0
  376. package/src/standalone/daemon.mjs +61 -1
  377. package/src/standalone/desktop-service-registry.mjs +5 -0
  378. package/src/standalone/local-session-runtime.mjs +70 -19
  379. package/src/standalone/session-client.mjs +42 -0
  380. package/src/standalone/session-protocol.mjs +7 -0
  381. package/src/standalone/session-proxy/projection.mjs +41 -4
  382. package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
  383. package/src/standalone/session-runtime-inline-host.mjs +1 -0
  384. package/src/standalone/session-service/session-calls/session-action.mjs +18 -3
  385. package/src/standalone/session-service-api.mjs +6 -1
  386. package/src/standalone/session-state-patch.d.mts +2 -1
  387. package/src/standalone/session-state-patch.mjs +41 -1
  388. package/src/tui/App.jsx +25 -0
  389. package/src/tui/app/app-view/floating-panel.jsx +26 -1
  390. package/src/tui/app/btw-controller.mjs +118 -0
  391. package/src/tui/app/create-app-pickers.mjs +2 -0
  392. package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
  393. package/src/tui/app/shell-layout/surfaces.mjs +3 -1
  394. package/src/tui/app/slash-commands.mjs +13 -3
  395. package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
  396. package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
  397. package/src/tui/app/use-global-key-input.mjs +5 -12
  398. package/src/tui/app/use-prompt-handlers.mjs +11 -0
  399. package/src/tui/components/BtwPanel.jsx +53 -0
  400. package/src/tui/components/QuestionPanel.jsx +275 -0
  401. package/src/tui/components/question-panel-model.mjs +156 -0
  402. package/src/tui/dist/index.mjs +2413 -1465
  403. package/src/tui/session/context-state.mjs +4 -2
  404. package/src/tui/session/session-api/commands.mjs +10 -0
  405. package/src/tui/session/session-api/controls.mjs +2 -2
  406. package/src/tui/session/session-api/integrations.mjs +9 -2
  407. package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
  408. package/src/tui/session/session-api/lifecycle.mjs +2 -1
  409. package/src/tui/session/session-api/settings.mjs +6 -0
  410. package/src/tui/session/session-api.mjs +1 -0
  411. package/src/tui/session/tool-approval.mjs +10 -7
  412. package/src/tui/session/transcript-spill.mjs +14 -7
  413. package/src/tui/session/turn-stream.mjs +174 -31
  414. package/src/tui/session/turn.mjs +12 -5
  415. package/src/workflows/default/WORKFLOW.md +3 -1
@@ -1,6 +1,7 @@
1
1
  import { splitBridgeToolArgs } from '../shared/bridge-tool-args.mjs';
2
2
  import { hasOwn } from '../shared/object.mjs';
3
3
  import { schemaValueError } from '../shared/schema-value-error.mjs';
4
+ import { advertiseActionInputUnion } from '../agent/orchestrator/providers/lib/action-input-contract.mjs';
4
5
  import {
5
6
  COMPUTER_CORE_ACTION_SCHEMA,
6
7
  COMPUTER_DEFAULT_DELIVERY,
@@ -149,25 +150,11 @@ const captureProperties = {
149
150
  },
150
151
  };
151
152
 
152
- export const COMPUTER_INPUT_SCHEMA = {
153
- type: 'object',
154
- description:
155
- 'One compact Computer Use operation. Use act.actions to execute 1-6 simple actions and receive one fresh observation.',
156
- properties: {
157
- action: {
158
- type: 'string',
159
- enum: ACTIONS,
160
- description:
161
- 'Use capture to observe, act for simple input actions, and the remaining operations only for their named advanced capability.',
162
- },
163
- input: {
164
- type: 'object',
165
- description: 'Fields for the selected action.',
166
- },
167
- },
168
- required: ['action'],
169
- additionalProperties: false,
170
- oneOf: [
153
+ // The strict per-action contract: which `input` each action accepts. The host
154
+ // validates every call against it (validateComputerToolArgs), and the model-facing
155
+ // schema below derives its `input` from it. It is not part of the advertised
156
+ // root: provider tool schemas must be an object root without anyOf/oneOf/allOf.
157
+ export const COMPUTER_ACTION_CONTRACTS = [
171
158
  branch(
172
159
  'wait_for_user',
173
160
  input({
@@ -360,11 +347,47 @@ export const COMPUTER_INPUT_SCHEMA = {
360
347
  ['app']
361
348
  )
362
349
  ),
363
- ],
350
+ ];
351
+
352
+ // A JSON Schema 2020-12 object root with no root combinator (Anthropic
353
+ // input_schema rule) whose `input` offers each action's own input as an anyOf
354
+ // alternative titled with that action. The tool advertises its flat projection
355
+ // (flattenActionInputUnion); only provider paths verified to accept the nested
356
+ // alternatives (first-party Anthropic, Claude via Antigravity) send this form
357
+ // (actionInputUnionFor). validateComputerToolArgs enforces the strict
358
+ // per-action contract.
359
+ const COMPUTER_ACTION_INPUT_UNION = {
360
+ type: 'object',
361
+ description:
362
+ 'One compact Computer Use operation. Use act.actions to execute 1-6 simple actions and receive one fresh observation.',
363
+ properties: {
364
+ action: {
365
+ type: 'string',
366
+ enum: ACTIONS,
367
+ description:
368
+ 'Use capture to observe, act for simple input actions, and the remaining operations only for their named advanced capability.',
369
+ },
370
+ input: {
371
+ type: 'object',
372
+ description: 'Fields for the selected action.',
373
+ anyOf: COMPUTER_ACTION_CONTRACTS.map(({ properties }) => {
374
+ const [action] = properties.action.enum;
375
+ return {
376
+ title: action,
377
+ description: `Input when action="${action}".`,
378
+ ...structuredClone(properties.input),
379
+ };
380
+ }),
381
+ },
382
+ },
383
+ required: ['action'],
384
+ additionalProperties: false,
364
385
  };
365
386
 
387
+ export const COMPUTER_INPUT_SCHEMA = advertiseActionInputUnion(COMPUTER_ACTION_INPUT_UNION);
388
+
366
389
  const COMPUTER_ACTION_BRANCHES = new Map();
367
- for (const actionBranch of COMPUTER_INPUT_SCHEMA.oneOf) {
390
+ for (const actionBranch of COMPUTER_ACTION_CONTRACTS) {
368
391
  for (const action of actionBranch.properties?.action?.enum || []) {
369
392
  COMPUTER_ACTION_BRANCHES.set(action, actionBranch);
370
393
  }
@@ -61,6 +61,96 @@ function reduceGithubData(value, key, parentKey) {
61
61
  return reduced;
62
62
  }
63
63
 
64
+ const EXCERPT_MAX = 140;
65
+ const day = (value) => (typeof value === 'string' ? value.slice(0, 10) : undefined);
66
+ const login = (value) => userLogin(value) ?? (typeof value === 'string' ? value : undefined);
67
+
68
+ function excerpt(body) {
69
+ if (typeof body !== 'string') return undefined;
70
+ const text = body.replace(/\s+/g, ' ').trim();
71
+ if (!text) return undefined;
72
+ return text.length > EXCERPT_MAX ? `${text.slice(0, EXCERPT_MAX - 1)}…` : text;
73
+ }
74
+
75
+ function labelNames(labels) {
76
+ const names = (Array.isArray(labels) ? labels : [])
77
+ .map((label) => (typeof label === 'string' ? label : label?.name))
78
+ .filter(Boolean);
79
+ return names.length ? names : undefined;
80
+ }
81
+
82
+ // Compact per-row projections for *.list actions: only what picks the next item
83
+ // to open. Undefined fields are dropped by JSON.stringify.
84
+ const LIST_PROJECTORS = {
85
+ 'issue.list': (item) => ({
86
+ number: item.number,
87
+ title: item.title,
88
+ state: item.state,
89
+ author: login(item.user),
90
+ updated: day(item.updated_at),
91
+ labels: labelNames(item.labels),
92
+ comments: item.comments || undefined,
93
+ excerpt: excerpt(item.body),
94
+ }),
95
+ 'pr.list': (item) => ({
96
+ number: item.number,
97
+ title: item.title,
98
+ state: item.merged_at ? 'merged' : item.state,
99
+ draft: item.draft || undefined,
100
+ author: login(item.user),
101
+ updated: day(item.updated_at),
102
+ head: item.head?.ref,
103
+ base: item.base?.ref,
104
+ labels: labelNames(item.labels),
105
+ excerpt: excerpt(item.body),
106
+ }),
107
+ 'repo.list': (item) => ({
108
+ name: item.full_name,
109
+ description: excerpt(item.description),
110
+ visibility: item.visibility || (item.private ? 'private' : 'public'),
111
+ language: item.language || undefined,
112
+ fork: item.fork || undefined,
113
+ archived: item.archived || undefined,
114
+ updated: day(item.pushed_at || item.updated_at),
115
+ }),
116
+ 'run.list': (item) => ({
117
+ id: item.id,
118
+ name: item.display_title || item.name,
119
+ workflow: item.name,
120
+ status: item.status,
121
+ conclusion: item.conclusion || undefined,
122
+ branch: item.head_branch,
123
+ event: item.event,
124
+ actor: login(item.actor),
125
+ updated: day(item.updated_at),
126
+ }),
127
+ 'release.list': (item) => ({
128
+ id: item.id,
129
+ tag: item.tag_name,
130
+ name: item.name || undefined,
131
+ draft: item.draft || undefined,
132
+ prerelease: item.prerelease || undefined,
133
+ author: login(item.author),
134
+ published: day(item.published_at || item.created_at),
135
+ }),
136
+ 'notification.list': (item) => ({
137
+ id: item.id,
138
+ unread: item.unread,
139
+ reason: item.reason,
140
+ type: item.subject?.type,
141
+ title: item.subject?.title,
142
+ number: Number(/\/(?:issues|pulls)\/(\d+)$/.exec(item.subject?.url || '')?.[1]) || undefined,
143
+ repo: typeof item.repository === 'string' ? item.repository : item.repository?.full_name,
144
+ updated: day(item.updated_at),
145
+ }),
146
+ 'workflow.list': (item) => ({ id: item.id, name: item.name, path: item.path, state: item.state }),
147
+ };
148
+
149
+ function projectList(action, data) {
150
+ const project = LIST_PROJECTORS[action];
151
+ return project && Array.isArray(data) ? data.map(project) : undefined;
152
+ }
153
+
64
154
  const fits = (text) => Buffer.byteLength(text, 'utf8') <= GITHUB_OUTPUT_MAX_BYTES;
65
155
 
66
156
  function clampStrings(value, max) {
@@ -213,7 +303,7 @@ export const GITHUB_TOOL_DEF = {
213
303
  },
214
304
  trigger: 'GitHub repos, issues, PRs, Actions, releases; not local Git history, diffs or staging (git).',
215
305
  description:
216
- 'GitHub repositories, issues, PRs and reviews, Actions/logs, releases, and notifications through the signed-in GitHub CLI. Local Git history/diffs/staging belong to git. One action per call; writes are serialized and never retried. Obtain user approval before writes; workflow runs and published releases may deploy. repo is owner/name; omit to resolve the current Project. Lists use page/limit. Review/merge requires the current PR head sha. Install/connect in Extensions → Plugin → Git & GitHub. Results omit API-only URLs and node ids and name users/repos by login/full_name; `raw` is a readable file with the full response, and long results state what is shown.',
306
+ 'GitHub repositories, issues, PRs and reviews, Actions/logs, releases, and notifications through the signed-in GitHub CLI. Local Git history/diffs/staging belong to git. One action per call; writes are serialized and never retried. Obtain user approval before writes; workflow runs and published releases may deploy. repo is owner/name; omit to resolve the current Project. Lists use page/limit and return compact rows (issues/PRs: number, title, state, author, updated, labels, 140-char excerpt; other lists: ids and status fields); open a row with its *.view action for bodies and details. Review/merge requires the current PR head sha. Install/connect in Extensions → Plugin → Git & GitHub. Results omit API-only URLs and node ids and name users/repos by login/full_name; `raw` is a readable file with the full response, and long results state what is shown.',
217
307
  inputSchema: {
218
308
  type: 'object',
219
309
  properties: {
@@ -267,7 +357,7 @@ export const GITHUB_TOOL_DEF = {
267
357
  export async function executeGithubTool(args, cwd, options = {}) {
268
358
  try {
269
359
  const result = await executeGithubRequest(args, cwd, options);
270
- const reduced = { ...result, data: reduceGithubData(result.data) };
360
+ const reduced = { ...result, data: projectList(args.action, result.data) ?? reduceGithubData(result.data) };
271
361
  const text = JSON.stringify(reduced);
272
362
  if (text === JSON.stringify(result) && fits(text)) return text;
273
363
  const raw = persistToolResultArtifactSync({
@@ -532,6 +532,7 @@ const _httpRouter = createHttpRouter({
532
532
  _traceDb = v;
533
533
  },
534
534
  ingestTranscriptFile,
535
+ forgetTranscripts: _transcriptIngest.forgetTranscripts,
535
536
  getTranscriptOffset: (fp) => _transcriptIngest.getOffset(fp),
536
537
  parseTsToMs,
537
538
  refreshCoreMemoryFile: refreshCoreMemorySnapshot,
@@ -15,7 +15,14 @@ const INSERT_ENTRY = `
15
15
  RETURNING id
16
16
  `;
17
17
 
18
- export function createIngestHandler({ getDb, log, ingestTranscriptFile, getTranscriptOffset, parseTsToMs }) {
18
+ export function createIngestHandler({
19
+ getDb,
20
+ log,
21
+ ingestTranscriptFile,
22
+ forgetTranscripts,
23
+ getTranscriptOffset,
24
+ parseTsToMs,
25
+ }) {
19
26
  const entry = async (body, res) => {
20
27
  const db = getDb();
21
28
  const role = String(body.role ?? 'user');
@@ -89,9 +96,23 @@ export function createIngestHandler({ getDb, log, ingestTranscriptFile, getTrans
89
96
  }
90
97
  };
91
98
 
99
+ const forgetTranscript = async (body, res) => {
100
+ const filePaths = body.filePaths;
101
+ if (!Array.isArray(filePaths) || filePaths.some((filePath) => typeof filePath !== 'string' || !filePath)) {
102
+ sendJson(res, { error: 'filePaths required' }, 400);
103
+ return;
104
+ }
105
+ try {
106
+ sendJson(res, { ok: true, removed: await forgetTranscripts(filePaths) });
107
+ } catch (e) {
108
+ sendJson(res, { error: e.message }, 500);
109
+ }
110
+ };
111
+
92
112
  const routes = {
93
113
  '/entry': entry,
94
114
  '/ingest-transcript': ingestTranscript,
115
+ '/transcript/forget': forgetTranscript,
95
116
  '/transcript/ingest-sync': ingestSync,
96
117
  };
97
118
 
@@ -47,6 +47,8 @@ export function createTranscriptOffsets({ loadMeta, persistMeta, log }) {
47
47
  set: (transcriptPath, position) => {
48
48
  offsets.set(transcriptPath, position);
49
49
  },
50
+ /** Drop one file's read position; true when an entry existed. */
51
+ forget: (transcriptPath) => offsets.delete(transcriptPath),
50
52
  reset: () => {
51
53
  offsets = new Map();
52
54
  },
@@ -90,7 +90,9 @@ function installPoolErrorHandler(pool, label, { onConnectionLoss } = {}) {
90
90
  // db.transaction(async tx => …) → auto BEGIN/COMMIT, ROLLBACK on throw
91
91
 
92
92
  import { resolve } from 'node:path';
93
- import { ensurePgInstance as supervisorEnsure } from './supervisor.mjs';
93
+ import { ensurePgInstance as supervisorEnsure, stopOwnedPgForShutdown } from './supervisor.mjs';
94
+
95
+ export { installOwnedPgExitStop, pgClusterExists } from './supervisor.mjs';
94
96
 
95
97
  // ---------------------------------------------------------------------------
96
98
  // One-shot bootstrap guard — keyed by resolved dataDir (cluster-level, not schema)
@@ -164,6 +166,52 @@ export async function checkedConnect(pgPool, schema) {
164
166
  return client;
165
167
  }
166
168
 
169
+ // ---------------------------------------------------------------------------
170
+ // Durable-operation tracking — a shutdown lets these settle before PG stops
171
+ // ---------------------------------------------------------------------------
172
+
173
+ /** Schemas holding only data rebuilt from another source of truth: the
174
+ * session-search copy (re-ingested from the session files by its backfill)
175
+ * and trace diagnostics. Every other schema (scheduler, webhooks, memory)
176
+ * holds the only copy, so its in-flight operations are drained first. */
177
+ const DERIVED_SCHEMAS = new Set(['session_search', 'trace']);
178
+ const _durableInFlight = new Set();
179
+
180
+ function trackDurable(schema, run) {
181
+ if (DERIVED_SCHEMAS.has(schema)) return run();
182
+ const op = run();
183
+ _durableInFlight.add(op);
184
+ const settle = () => _durableInFlight.delete(op);
185
+ op.then(settle, settle);
186
+ return op;
187
+ }
188
+
189
+ /** In-flight operations on durable schemas right now. */
190
+ export function pendingDurablePgOps() {
191
+ return _durableInFlight.size;
192
+ }
193
+
194
+ /**
195
+ * Wait, bounded, until no durable-schema operation is in flight (including
196
+ * ones issued while waiting). Resolves true when drained, false at the bound.
197
+ */
198
+ export async function drainDurablePgOps({ timeoutMs = 0 } = {}) {
199
+ const deadline = Date.now() + Math.max(0, Number(timeoutMs) || 0);
200
+ while (_durableInFlight.size > 0) {
201
+ const remaining = deadline - Date.now();
202
+ if (remaining <= 0) return false;
203
+ let timer = null;
204
+ await Promise.race([
205
+ Promise.allSettled([..._durableInFlight]),
206
+ new Promise((resolveBound) => {
207
+ timer = setTimeout(resolveBound, remaining);
208
+ }),
209
+ ]);
210
+ clearTimeout(timer);
211
+ }
212
+ return true;
213
+ }
214
+
167
215
  // ---------------------------------------------------------------------------
168
216
  // native PG db shim
169
217
  // ---------------------------------------------------------------------------
@@ -253,6 +301,12 @@ function makeCompatDb(pgPool, schema, dataDir) {
253
301
  return currentPool();
254
302
  },
255
303
  };
304
+ // Every statement entry point is tracked, so a shutdown can let durable
305
+ // writes settle before it stops the cluster.
306
+ const { query, exec, transaction } = db;
307
+ db.query = (sql, params) => trackDurable(schema, () => query(sql, params));
308
+ db.exec = (sql) => trackDurable(schema, () => exec(sql));
309
+ db.transaction = (fn) => trackDurable(schema, () => transaction(fn));
256
310
  return db;
257
311
  }
258
312
 
@@ -522,3 +576,46 @@ export async function closePgInstance(dataDir, opts = {}) {
522
576
  } catch {}
523
577
  instances.delete(key);
524
578
  }
579
+
580
+ // ---------------------------------------------------------------------------
581
+ // shutdownOwnedPg — process exit: every pool, then the cluster this process owns
582
+ // ---------------------------------------------------------------------------
583
+
584
+ export const POOL_CLOSE_BOUND_MS = 500;
585
+
586
+ /**
587
+ * For a host whose stores start PostgreSQL (the daemon's schedules/webhooks/
588
+ * session-search): stop the cluster it ensured (supervisor
589
+ * stopOwnedPgForShutdown, `pg_ctl stop -m fast`) while every pool this process
590
+ * opened ends alongside — a pool waiting on a checked-out client never holds
591
+ * the stop back. The stop latches the supervisor first (synchronously), so a
592
+ * pooled client that sees fast shutdown terminate its backend cannot restart
593
+ * PG through connection-loss recovery; the pools are dropped from the cache,
594
+ * and their close is waited on only for a short bound after the stop.
595
+ */
596
+ export async function shutdownOwnedPg({ timeoutMs, drainMs = 0 } = {}) {
597
+ // Durable writes still in flight (schedule/webhook state) commit first,
598
+ // bounded by `drainMs`; derived writes (session search, trace) are not
599
+ // waited on — their sources rebuild them.
600
+ if (!(await drainDurablePgOps({ timeoutMs: drainMs }))) {
601
+ __mixdogMemoryLog(
602
+ `[pg-adapter] stopping PG with ${_durableInFlight.size} durable operation(s) still in flight after ${drainMs}ms\n`
603
+ );
604
+ }
605
+ const pools = [...instances.values()].map((inst) => inst.pool);
606
+ instances.clear();
607
+ const stopping = stopOwnedPgForShutdown({ timeoutMs });
608
+ const closing = Promise.allSettled(pools.map((pool) => pool.end()));
609
+ try {
610
+ return await stopping;
611
+ } finally {
612
+ let timer = null;
613
+ await Promise.race([
614
+ closing,
615
+ new Promise((resolveBound) => {
616
+ timer = setTimeout(resolveBound, POOL_CLOSE_BOUND_MS);
617
+ }),
618
+ ]);
619
+ clearTimeout(timer);
620
+ }
621
+ }
@@ -5,8 +5,11 @@ import { sleep as delay } from '../../../shared/sleep.mjs';
5
5
  //
6
6
  // Public API:
7
7
  // startPg({ runtimeDir, pgdataDir, port?, logPath? }) → { pid, port }
8
- // stopPg({ runtimeDir, pgdataDir }) → void
8
+ // stopPg({ runtimeDir, pgdataDir, timeoutMs? }) → void
9
9
  // healthcheckPg({ port, host? }) → boolean
10
+ //
11
+ // Every child on the start/stop path (initdb, pg_ctl start/reload/status/stop)
12
+ // runs async; only stopPgSync — a process 'exit' hook — may block.
10
13
 
11
14
  import {
12
15
  existsSync,
@@ -24,6 +27,7 @@ import { join } from 'node:path';
24
27
  import { execFile, spawn, spawnSync } from 'node:child_process';
25
28
  import { createConnection, createServer } from 'node:net';
26
29
  import { isPidAlive } from '../../../shared/pid-liveness.mjs';
30
+ import { runChild } from './run-child.mjs';
27
31
 
28
32
  // ---------------------------------------------------------------------------
29
33
  // Helpers
@@ -201,14 +205,12 @@ function ensureConfV2(pgdataDir) {
201
205
  // callers route through this rather than re-implementing the conf/reload pair.
202
206
  // Reload-compatible settings apply immediately; postmaster settings such as
203
207
  // max_connections apply on the next normal PG start (never restart live work).
204
- export function reconcileConfV2(runtimeDir, pgdataDir) {
208
+ export async function reconcileConfV2(runtimeDir, pgdataDir) {
205
209
  const applied = ensureConfV2(pgdataDir);
206
210
  if (!applied) return { applied: false, reloaded: false };
207
- const reload = spawnSync(pgBin(runtimeDir, 'pg_ctl'), ['reload', '-D', pgdataDir], {
211
+ const reload = await runChild(pgBin(runtimeDir, 'pg_ctl'), ['reload', '-D', pgdataDir], {
208
212
  env: libEnv(runtimeDir),
209
- stdio: 'pipe',
210
- timeout: 5_000,
211
- windowsHide: true,
213
+ timeoutMs: 5_000,
212
214
  });
213
215
  const reloaded = reload.status === 0;
214
216
  if (reloaded) {
@@ -238,7 +240,7 @@ async function attachExistingPostmaster({ runtimeDir, pgdataDir, existingWaitMs,
238
240
  __mixdogMemoryLog(`[pg-process] attaching to existing PG pid=${existing.pid} port=${existing.port}\n`);
239
241
  // Route through the single reconcile entry point so v1 → v2 conf
240
242
  // upgrades land on already-running instances without restart.
241
- if (v2Applied) reconcileConfV2(runtimeDir, pgdataDir);
243
+ if (v2Applied) await reconcileConfV2(runtimeDir, pgdataDir);
242
244
  return { pid: existing.pid, port: existing.port, attached: true };
243
245
  }
244
246
  if (existing.state === 'alive-not-ready') {
@@ -259,6 +261,10 @@ async function attachExistingPostmaster({ runtimeDir, pgdataDir, existingWaitMs,
259
261
  // default_transaction_isolation: native PG default is read committed.
260
262
  // Native PG does not default to serializable; set isolation level explicitly
261
263
  // so behaviour is unambiguous across PG major versions.
264
+ // Carries every versioned block ensureConfV2 checks for, so a fresh cluster
265
+ // already holds what later boots expect: none of them rewrites the conf and
266
+ // pg_ctl-reloads it (a postmaster-only setting such as shared_buffers cannot
267
+ // change by reload, and every such reload logged an error).
262
268
  function freshClusterConfAppend() {
263
269
  return (
264
270
  [
@@ -279,7 +285,8 @@ function freshClusterConfAppend() {
279
285
  ].join('\n') +
280
286
  '\n' +
281
287
  buildV2Block() +
282
- buildV3Block()
288
+ buildV3Block() +
289
+ buildV4Block()
283
290
  );
284
291
  }
285
292
 
@@ -290,14 +297,14 @@ function freshClusterConfAppend() {
290
297
  * user profile). Such a share dir is passed through an ASCII junction under
291
298
  * %ProgramData% that is removed afterwards.
292
299
  */
293
- export function withInitdbShareDir(runtimeDir, run) {
300
+ export async function withInitdbShareDir(runtimeDir, run) {
294
301
  const shareDir = join(runtimeDir, 'share');
295
302
  if (process.platform !== 'win32' || /^[\x20-\x7e]*$/.test(shareDir)) return run(null);
296
303
  const linkRoot = mkdtempSync(join(process.env.ProgramData, 'mixdog-initdb-'));
297
304
  const link = join(linkRoot, 'share');
298
305
  try {
299
306
  symlinkSync(shareDir, link, 'junction');
300
- return run(link);
307
+ return await run(link);
301
308
  } finally {
302
309
  // Cleanup must not replace initdb's own result or error. Both removals are
303
310
  // non-recursive, so the junction target (the runtime share) is never touched.
@@ -312,11 +319,11 @@ export function withInitdbShareDir(runtimeDir, run) {
312
319
  }
313
320
 
314
321
  /** initdb if pgdata is not yet initialised (no PG_VERSION file). */
315
- function initClusterIfNeeded({ runtimeDir, pgdataDir, env }) {
322
+ async function initClusterIfNeeded({ runtimeDir, pgdataDir, env }) {
316
323
  if (existsSync(join(pgdataDir, 'PG_VERSION'))) return;
317
324
  __mixdogMemoryLog(`[pg-process] initdb → ${pgdataDir}\n`);
318
- const r = withInitdbShareDir(runtimeDir, (shareDir) =>
319
- spawnSync(
325
+ const r = await withInitdbShareDir(runtimeDir, (shareDir) =>
326
+ runChild(
320
327
  pgBin(runtimeDir, 'initdb'),
321
328
  [
322
329
  '-D',
@@ -329,7 +336,7 @@ function initClusterIfNeeded({ runtimeDir, pgdataDir, env }) {
329
336
  '-U',
330
337
  'postgres',
331
338
  ],
332
- { env, stdio: 'pipe', windowsHide: true }
339
+ { env }
333
340
  )
334
341
  );
335
342
  if (r.status !== 0) {
@@ -435,12 +442,7 @@ async function startAndWaitReady({ pgctl, startArgs, env, pgdataDir, port }) {
435
442
  */
436
443
  async function attachRaceWinner({ pgdataDir, pgctl, env, existingWaitMs }) {
437
444
  __mixdogMemoryLog(`[pg-process] pg_ctl start: "another server might be running" — probing status\n`);
438
- const statusR = spawnSync(pgctl, ['status', '-D', pgdataDir], {
439
- env,
440
- stdio: 'pipe',
441
- timeout: 3_000,
442
- windowsHide: true,
443
- });
445
+ const statusR = await runChild(pgctl, ['status', '-D', pgdataDir], { env, timeoutMs: 3_000 });
444
446
  __mixdogMemoryLog(
445
447
  `[pg-process] pg_ctl status: ${statusR.stdout?.toString() || statusR.stderr?.toString() || 'no output'}\n`
446
448
  );
@@ -480,7 +482,7 @@ export async function startPg({
480
482
  if (attached) return attached;
481
483
 
482
484
  const pgctl = pgBin(runtimeDir, 'pg_ctl');
483
- initClusterIfNeeded({ runtimeDir, pgdataDir, env });
485
+ await initClusterIfNeeded({ runtimeDir, pgdataDir, env });
484
486
  // PostgreSQL requires a completely empty target on first init. Creating the
485
487
  // Spotlight marker before initdb makes every fresh macOS cluster fail with
486
488
  // "directory exists but is not empty"; add it only after initialization.
@@ -511,15 +513,18 @@ export async function startPg({
511
513
  // stopPg
512
514
  // ---------------------------------------------------------------------------
513
515
 
514
- export async function stopPg({ runtimeDir, pgdataDir }) {
516
+ // Bounded graceful stop. `-m fast` rolls back open transactions and
517
+ // disconnects clients; pg_ctl's own wait (-t, whole seconds) and the child
518
+ // timeout share `timeoutMs`, so a wedged stop rejects instead of holding a
519
+ // shutdown sequence open.
520
+ export async function stopPg({ runtimeDir, pgdataDir, timeoutMs = 15_000 }) {
515
521
  const pgctl = pgBin(runtimeDir, 'pg_ctl');
516
522
  const env = libEnv(runtimeDir);
523
+ const waitSeconds = Math.max(1, Math.ceil(timeoutMs / 1000));
517
524
 
518
- const r = spawnSync(pgctl, ['stop', '-m', 'fast', '-w', '-D', pgdataDir], {
525
+ const r = await runChild(pgctl, ['stop', '-m', 'fast', '-w', '-t', String(waitSeconds), '-D', pgdataDir], {
519
526
  env,
520
- stdio: 'pipe',
521
- timeout: 15_000,
522
- windowsHide: true,
527
+ timeoutMs: waitSeconds * 1000 + 1_000,
523
528
  });
524
529
 
525
530
  if (r.status !== 0) {
@@ -0,0 +1,49 @@
1
+ import { spawn } from 'node:child_process';
2
+
3
+ /**
4
+ * Async child run with spawnSync's result shape ({ status, signal, stdout,
5
+ * stderr, error }) so the PG runtime's tar / initdb / pg_ctl steps never block
6
+ * the event loop of the process hosting them (the daemon serves every client
7
+ * from one loop). `timeoutMs` > 0 kills the child and reports ETIMEDOUT, like
8
+ * spawnSync's `timeout`. Never rejects.
9
+ */
10
+ export function runChild(file, args, { timeoutMs = 0, ...options } = {}) {
11
+ return new Promise((resolve) => {
12
+ const stdout = [];
13
+ const stderr = [];
14
+ let settled = false;
15
+ let timer = null;
16
+ let error = null;
17
+ const finish = (status, signal) => {
18
+ if (settled) return;
19
+ settled = true;
20
+ if (timer) clearTimeout(timer);
21
+ resolve({ status, signal, stdout: Buffer.concat(stdout), stderr: Buffer.concat(stderr), error });
22
+ };
23
+ let child;
24
+ try {
25
+ child = spawn(file, args, { windowsHide: true, ...options, stdio: ['ignore', 'pipe', 'pipe'] });
26
+ } catch (spawnError) {
27
+ error = spawnError;
28
+ finish(null, null);
29
+ return;
30
+ }
31
+ child.stdout?.on('data', (chunk) => stdout.push(chunk));
32
+ child.stderr?.on('data', (chunk) => stderr.push(chunk));
33
+ child.once('error', (childError) => {
34
+ error ??= childError;
35
+ finish(null, null);
36
+ });
37
+ // 'close' (not 'exit'): stdio is flushed, so captured output is complete.
38
+ child.once('close', (status, signal) => finish(status, signal));
39
+ if (timeoutMs > 0) {
40
+ timer = setTimeout(() => {
41
+ error = Object.assign(new Error(`${file} timed out after ${timeoutMs}ms`), { code: 'ETIMEDOUT' });
42
+ try {
43
+ child.kill();
44
+ } catch {}
45
+ finish(null, 'SIGTERM');
46
+ }, timeoutMs);
47
+ }
48
+ });
49
+ }