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
@@ -246,6 +246,7 @@ export function daemonOwnerIsStale(
246
246
  * - No successful refresh for `fenceMs`: fenced, never renewed (`onFenced`).
247
247
  * - The record names another pid: `onSuperseded(record)`.
248
248
  * - A turn gap over `stallMs`: `admit()` refuses work until a refresh succeeds.
249
+ * The gap is logged once, with `describeStall(fromMono, toMono)` appended.
249
250
  * Both callbacks must make this process stop serving.
250
251
  */
251
252
  export function createOwnerHeartbeat({
@@ -259,6 +260,7 @@ export function createOwnerHeartbeat({
259
260
  monoNow = () => performance.now(),
260
261
  refresh = refreshSingletonOwner,
261
262
  log = () => {},
263
+ describeStall = null,
262
264
  onBeat = () => {},
263
265
  onSuperseded,
264
266
  onFenced,
@@ -290,11 +292,22 @@ export function createOwnerHeartbeat({
290
292
  }
291
293
 
292
294
  function noteTurn(t) {
293
- if (t - lastTurnAt > stallMs && !needsVerify) {
294
- needsVerify = true;
295
- log(`event loop stalled ${Math.round(t - lastTurnAt)}ms — owner lease must be re-verified before serving`);
296
- }
295
+ const from = lastTurnAt;
296
+ // Rebased before reporting: one long block is one gap, however many
297
+ // callers (tick, admit) observe it afterwards.
297
298
  lastTurnAt = t;
299
+ if (t - from <= stallMs) return;
300
+ needsVerify = true;
301
+ let detail = '';
302
+ try {
303
+ detail = describeStall?.(from, t) || '';
304
+ } catch {
305
+ /* attribution must not disturb the lease */
306
+ }
307
+ log(
308
+ `event loop stalled ${Math.round(t - from)}ms — owner lease must be re-verified before serving` +
309
+ (detail ? ` | ${detail}` : '')
310
+ );
298
311
  }
299
312
 
300
313
  function beat() {
@@ -0,0 +1,51 @@
1
+ // The daemon's PostgreSQL stop inside its shutdown. The normal teardown stops
2
+ // PostgreSQL after every user of it has closed, letting durable writes still in
3
+ // flight commit first; a fallback timer stops it regardless just before the
4
+ // launcher's kill (PG_FORCED_STOP_AT_MS), because a daemon killed before
5
+ // `pg_ctl stop` leaves the daemonized postmaster running.
6
+ import { PG_FORCED_STOP_AT_MS } from './daemon-stop-budget.mjs';
7
+
8
+ const ADAPTER = '../runtime/memory/lib/pg/adapter.mjs';
9
+
10
+ /**
11
+ * Arm at shutdown start. `finish()` is the ordered teardown step: it drains
12
+ * durable writes until the fallback point, then stops. Idempotent: whichever
13
+ * of finish/fallback comes first runs the one stop; the other joins it.
14
+ */
15
+ export function createDaemonPgStop({
16
+ log,
17
+ stopTimeoutMs,
18
+ forcedStopAtMs = PG_FORCED_STOP_AT_MS,
19
+ loadAdapter = () => import(ADAPTER),
20
+ }) {
21
+ const startedAt = Date.now();
22
+ let stopping = null;
23
+ // The drain budget is what remains until the fallback point once the adapter
24
+ // has loaded, so a slow load never stretches the drain past that point.
25
+ const drainBudget = () => Math.max(0, startedAt + forcedStopAtMs - Date.now());
26
+ const run = (drain) => {
27
+ stopping ??= loadAdapter()
28
+ .then(({ shutdownOwnedPg }) =>
29
+ shutdownOwnedPg({ timeoutMs: stopTimeoutMs, drainMs: drain ? drainBudget() : 0 })
30
+ )
31
+ .then(
32
+ (stopped) => {
33
+ if (stopped) log('postgres stopped');
34
+ },
35
+ (e) => log(`postgres stop failed: ${e?.message || e}`)
36
+ );
37
+ return stopping;
38
+ };
39
+ const fallback = setTimeout(() => {
40
+ if (stopping) return; // the ordered stop is draining; its bound is this same point
41
+ log(`teardown still running ${forcedStopAtMs}ms into shutdown; stopping postgres before the launcher's kill`);
42
+ void run(false);
43
+ }, forcedStopAtMs);
44
+ fallback.unref?.();
45
+ return {
46
+ finish() {
47
+ clearTimeout(fallback);
48
+ return run(true);
49
+ },
50
+ };
51
+ }
@@ -0,0 +1,2 @@
1
+ export const IN_TREE_DAEMON_STOP_GRACE_MS: number;
2
+ export const PG_FORCED_STOP_AT_MS: number;
@@ -0,0 +1,18 @@
1
+ // Time budget of an in-tree daemon's stop, shared by the launcher that waits
2
+ // for it (desktop quit) and the daemon that has to finish inside it.
3
+
4
+ /** How long a launcher waits for an in-tree daemon to exit after asking it to
5
+ * stop. Past this the launcher exits and the daemon dies with its process
6
+ * tree — on Windows without any exit hook. A healthy daemon stop (PostgreSQL
7
+ * included) measures ~0.6 s; the rest is room for a slow teardown to finish
8
+ * its writes. The desktop window is hidden before this wait begins. */
9
+ export const IN_TREE_DAEMON_STOP_GRACE_MS = 5_000;
10
+
11
+ /** `pg_ctl stop` spawn + signal delivery. Once signalled, the postmaster
12
+ * finishes its shutdown on its own even if the daemon is then killed. */
13
+ const PG_STOP_SIGNAL_MARGIN_MS = 800;
14
+
15
+ /** Fallback point (from shutdown start) at which the daemon stops PostgreSQL
16
+ * even while earlier teardown steps still run: just before the launcher's
17
+ * kill, so a slow-but-healthy teardown keeps its writes until then. */
18
+ export const PG_FORCED_STOP_AT_MS = IN_TREE_DAEMON_STOP_GRACE_MS - PG_STOP_SIGNAL_MARGIN_MS;
@@ -38,6 +38,8 @@ export function createStoredSessionViews({ desktopRuntime, dataDir }) {
38
38
  const review = createRuntimeReviewApi({ getCwd: () => metadata.cwd || '', getSessionId: () => sessionId });
39
39
  // The runtime review API answers `known`/`summary` itself (turnReviewReply).
40
40
  if (action === 'getSessionReviewDiff') return review.getSessionReviewDiff();
41
+ if (action === 'getSessionReviewFilePatch') return review.getSessionReviewFilePatch(args[0], args[1]);
42
+ if (action === 'getTurnReviewFilePatch') return review.getTurnReviewFilePatch(args[0]);
41
43
  // A cold session runs no turn: its change record is final.
42
44
  if (action === 'getTurnChanges') return review.getTurnChanges(args[0]);
43
45
  return review.getTurnReviewDiff(args[0]);
@@ -139,6 +139,25 @@ export function createPostGcHeapTracker({
139
139
  };
140
140
  }
141
141
 
142
+ /**
143
+ * Builds the `describeStall(fromMono, toMono)` the owner heartbeat appends to
144
+ * its stall line: the spans that overlapped the stall window plus the in-flight
145
+ * work snapshot at report time. Counters and labels only.
146
+ */
147
+ export function createStallDescriber({ spans, getWork = () => ({}), limit = 3 } = {}) {
148
+ return (fromMono, toMono) => {
149
+ const parts = [];
150
+ const overlapping = spans?.describe(fromMono, toMono, limit);
151
+ if (overlapping) parts.push(overlapping);
152
+ const work = workCounts(getWork());
153
+ parts.push(
154
+ `in-flight: activeCalls=${work.activeCalls} queuedCalls=${work.queuedCalls}` +
155
+ ` busySessions=${work.busySessions} busyMemoryAgents=${work.busyMemoryAgents}`
156
+ );
157
+ return parts.join(' | ');
158
+ };
159
+ }
160
+
142
161
  export function formatDaemonTelemetry(record, reason = 'periodic') {
143
162
  const row = record && typeof record === 'object' ? record : collectDaemonTelemetry();
144
163
  return [
@@ -66,6 +66,7 @@ import {
66
66
  import { createAgentDispatchBroker } from './agent-dispatch-broker.mjs';
67
67
  import { createChannelTransport } from './channel-transport.mjs';
68
68
  import { drainDeferral, idleGcEligible } from './daemon-drain-policy.mjs';
69
+ import { createDaemonPgStop } from './daemon-pg-stop.mjs';
69
70
  import { createChannelSessionRouter } from '../session-runtime/services/channel-session-router.mjs';
70
71
  import { createSessionTransport } from './session-transport.mjs';
71
72
  import { createSessionService } from './session-service.mjs';
@@ -75,6 +76,7 @@ import * as quotaResumeStore from '../runtime/shared/quota-resume-store.mjs';
75
76
  import { registerWorktreeCacheCleanups } from '../runtime/shared/worktrees/cache-cleanups.mjs';
76
77
  import { createInlineSessionRuntimeHost } from './session-runtime-inline-host.mjs';
77
78
  import { getStandaloneMemoryRuntime } from '../session-runtime/services/memory-runtime-proxy.mjs';
79
+ import { registerTranscriptPurgeHook } from '../session-runtime/session-transcript-purge.mjs';
78
80
  import { createBootPhaseProfiler } from './boot-phase-profiler.mjs';
79
81
  import { createDaemonBootCoordinator } from './daemon-boot-coordinator.mjs';
80
82
  import { createDaemonLog } from './daemon-log.mjs';
@@ -83,11 +85,13 @@ import {
83
85
  installDaemonExitRecorder,
84
86
  reconcileLostDaemonCaptures,
85
87
  } from '../session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs';
86
- import { createDaemonTelemetry } from './daemon-telemetry.mjs';
88
+ import { createDaemonTelemetry, createStallDescriber } from './daemon-telemetry.mjs';
89
+ import { slowSpans } from '../runtime/shared/slow-span.mjs';
87
90
  import { createLagProfiler } from './daemon-lag-profiler.mjs';
88
91
  import { createChannelsRuntimeLoader } from './daemon-channels-loader.mjs';
89
92
  import { createDesktopRuntime } from './daemon-desktop-runtime.mjs';
90
93
  import { createCanonicalAgentControl } from './daemon-agent-control.mjs';
94
+ import { flushAllIndexWrites } from '../session-runtime/services/agent-tool/index-write-queue.mjs';
91
95
  import { registerAutomationAgentTool } from '../session-runtime/automation-agents.mjs';
92
96
  import {
93
97
  compareRuntimeVersions,
@@ -132,7 +136,9 @@ let sessionTransport = null;
132
136
  let sessionService = null;
133
137
  let sessionRuntimeHost = null;
134
138
  let localSessionBridge = null;
139
+ let canonicalAgentControl = null;
135
140
  let memoryRuntime = null;
141
+ let transcriptPurgeUnregister = null;
136
142
  let agentDispatchBroker = null;
137
143
  let remoteSessionState = { enabled: false, sessionId: null };
138
144
  let shuttingDown = false;
@@ -161,6 +167,13 @@ function registerMemoryRuntimeLazy() {
161
167
  // real memory call (memory/recall tools, the Memory install) starts the
162
168
  // singleton; nothing warms it at boot. The PG cluster itself usually starts
163
169
  // earlier: the channels schedules/webhooks stores open it at boot.
170
+ // Deleting a session removes its transcript file here, and its memory ingest
171
+ // offsets once the memory runtime exists (none when memory is skipped).
172
+ if (!transcriptPurgeUnregister) {
173
+ transcriptPurgeUnregister = registerTranscriptPurgeHook({
174
+ forgetOffsets: (transcriptPaths) => memoryRuntime?.forgetTranscripts(transcriptPaths),
175
+ });
176
+ }
164
177
  if (process.env.MIXDOG_DAEMON_SKIP_MEMORY === '1') {
165
178
  log('memory runtime skipped (MIXDOG_DAEMON_SKIP_MEMORY=1)');
166
179
  return;
@@ -201,6 +214,8 @@ function eventLoopStatus() {
201
214
  /** Ceiling for the whole teardown sequence, above the slowest individual stop
202
215
  * (channels/memory) so a healthy shutdown always finishes on its own. */
203
216
  const SHUTDOWN_BUDGET_MS = 15_000;
217
+ /** Graceful PostgreSQL stop bound inside that budget (pg_ctl stop -m fast). */
218
+ const PG_STOP_TIMEOUT_MS = 6_000;
204
219
 
205
220
  async function shutdown(reason, code = 0) {
206
221
  if (shuttingDown) return;
@@ -221,6 +236,9 @@ async function shutdown(reason, code = 0) {
221
236
  process.exit(code);
222
237
  }, SHUTDOWN_BUDGET_MS);
223
238
  forcedExit.unref?.();
239
+ // PostgreSQL stops after its users below (durable writes drained first), or
240
+ // at the fallback point just before the launcher's kill, whichever is first.
241
+ const pgStop = createDaemonPgStop({ log, stopTimeoutMs: PG_STOP_TIMEOUT_MS });
224
242
  try {
225
243
  setChannelNotifySink(null);
226
244
  } catch {}
@@ -241,6 +259,15 @@ async function shutdown(reason, code = 0) {
241
259
  } catch (e) {
242
260
  log(`session runtime host stop failed: ${e?.message || e}`);
243
261
  }
262
+ // Every runtime is closed: drain the canonical agent's index writers (and any
263
+ // other queued index write) before process.exit, whose 'exit' hook cannot
264
+ // finish async work.
265
+ try {
266
+ await canonicalAgentControl?.dispose?.();
267
+ await flushAllIndexWrites();
268
+ } catch (e) {
269
+ log(`agent index drain failed: ${e?.message || e}`);
270
+ }
244
271
  // Host-side Computer Use workers and target claims are reaped only on an
245
272
  // explicit release; the runtime's deferred timer is unref'd and never fires
246
273
  // once this process exits.
@@ -271,6 +298,11 @@ async function shutdown(reason, code = 0) {
271
298
  } catch (e) {
272
299
  log(`memory.stop failed: ${e?.message || e}`);
273
300
  }
301
+ // PostgreSQL this daemon started through its stores (schedules, webhooks,
302
+ // session search) after every user of it above is closed. `pg_ctl start`
303
+ // daemonizes the postmaster outside this process tree, so without this stop
304
+ // it outlives the daemon. A cluster another live process owns is left to it.
305
+ await pgStop.finish();
274
306
  try {
275
307
  agentDispatchBroker?.close?.(reason);
276
308
  } catch (e) {
@@ -315,6 +347,9 @@ function inFlightWork() {
315
347
 
316
348
  // Opt-in CPU profiling of lag windows while DATA_DIR/lag-profile.on exists.
317
349
  const lagProfiler = createLagProfiler({ dataDir: DATA_DIR, log });
350
+ // Request spans share the heartbeat's performance.now() clock; `[slow-op]` lines
351
+ // and stall attribution come from the same recorder.
352
+ slowSpans.setLog(log);
318
353
 
319
354
  daemonTelemetry = createDaemonTelemetry({
320
355
  log,
@@ -506,6 +541,7 @@ async function claimDaemonOwnership(bootPhases) {
506
541
  tickMs: timings.tickMs,
507
542
  stallMs: timings.stallMs,
508
543
  log,
544
+ describeStall: createStallDescriber({ spans: slowSpans, getWork: inFlightWork }),
509
545
  onBeat: republishMissingDiscovery,
510
546
  onSuperseded: (record) => fenceDaemon(`owner claim superseded by pid=${record?.pid}`),
511
547
  onFenced: ({ lastRefreshAt, sinceMs }) =>
@@ -696,6 +732,22 @@ function announceDaemonReady({ port, token, startedAt, bootPhases }) {
696
732
  });
697
733
  }
698
734
 
735
+ /** After ready, off every request path: arm the last-chance PostgreSQL stop on
736
+ * exit, then open the schedules/webhooks stores. On an existing cluster that
737
+ * starts PostgreSQL here, so the first catalog read (the desktop sidebar's
738
+ * channel setup) joins this start instead of being the one that runs it; a
739
+ * data dir with no cluster yet starts nothing (channel-admin). Loaded after
740
+ * the stderr redirect, so the PG layer's logs reach daemon.log. */
741
+ function startPostgresInBackground() {
742
+ void import('../runtime/memory/lib/pg/adapter.mjs')
743
+ .then((pg) => {
744
+ pg.installOwnedPgExitStop();
745
+ if (shuttingDown) return null;
746
+ return import('../session-runtime/services/channel-admin.mjs').then((admin) => admin.hasActiveAutomation());
747
+ })
748
+ .catch((error) => log(`postgres background start failed: ${error?.message || error}`));
749
+ }
750
+
699
751
  /** The channels front door: pointer-routed calls over HTTP+SSE. Creating the
700
752
  * transport also wires the channels notification sink (session-bound
701
753
  * notifications reach the session service, the rest fan out to channel
@@ -844,6 +896,7 @@ async function main() {
844
896
  getSessionRuntimeHost: () => sessionRuntimeHost,
845
897
  cwd: CWD,
846
898
  });
899
+ canonicalAgentControl = agentControl;
847
900
  // Schedules and webhooks run headless here; their sessions reach the same
848
901
  // canonical agent control through the automation tool scope.
849
902
  registerAutomationAgentTool(agentControl.execute);
@@ -938,6 +991,13 @@ async function main() {
938
991
  log(`session front door on 127.0.0.1:${sessionEndpoint.port}`);
939
992
 
940
993
  announceDaemonReady({ port, token, startedAt, bootPhases });
994
+ // After ready, off the boot path: load the session-runtime module tree so
995
+ // the first session.create does not block the loop on synchronous module
996
+ // compilation. Failures fall back to the lazy import inside create.
997
+ setImmediate(() => {
998
+ void sessionRuntimeHost?.warmLocalRuntime?.();
999
+ });
1000
+ startPostgresInBackground();
941
1001
 
942
1002
  // Automation may spawn the shared daemon; keep schedules/webhooks live.
943
1003
  // A TUI-spawned daemon keeps the historical eager start (its channels client
@@ -1,6 +1,7 @@
1
1
  import { createRequire } from 'node:module';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import { sanitizeForWire } from './session-wire-values.mjs';
4
+ import { annotateSpan } from '../runtime/shared/slow-span.mjs';
4
5
  import { modelProxyClientMode } from '../runtime/shared/model-proxy-mode.mjs';
5
6
  import { getModelProxyChannel } from '../runtime/agent/orchestrator/providers/model-proxy-channel.mjs';
6
7
 
@@ -217,6 +218,10 @@ export class DesktopServiceRegistry {
217
218
  const { service } = this.#requireSubscribed(desktopId, ctx);
218
219
  const name = String(method || '');
219
220
  if (!name) throw new TypeError('desktop service method is required');
221
+ // `[slow-op] desktop.invoke` names the call it timed: a desktop operation
222
+ // (Git reads) or a session capability, not only the generic route.
223
+ const target = name === 'invokeDesktopOperation' || name === 'invokeCapability' ? String(args?.[0] || '') : '';
224
+ annotateSpan(target ? `${name}:${target}` : name);
220
225
  return sanitizeForWire(await service.instance.invoke(name, Array.isArray(args) ? args : [])) ?? null;
221
226
  }
222
227
 
@@ -19,6 +19,7 @@ import { createContextState } from '../tui/session/context-state.mjs';
19
19
  import { createSessionFlow } from '../tui/session/session-flow.mjs';
20
20
  import { createRunTurn } from '../tui/session/turn.mjs';
21
21
  import { createQuotaResumeLane } from '../tui/session/quota-resume.mjs';
22
+ import { createGitHeadWatcher } from '../runtime/shared/worktrees/git-head.mjs';
22
23
  import { publicQuotaResume, quotaResumeEntry } from '../runtime/shared/quota-resume-store.mjs';
23
24
  import { createSessionApi } from '../tui/session/session-api.mjs';
24
25
  import { createSessionDraftStore } from '../tui/session/draft-store.mjs';
@@ -33,18 +34,54 @@ import { formatDoctorReport, runDoctorChecks } from '../tui/app/doctor.mjs';
33
34
  // tuiDebug is an opt-in diagnostic trace for the hang chain (runTurn start/end,
34
35
  // busy-queue enqueue/drain). Quiet by default so it can never tear through
35
36
  // the alternate-screen render; enable with MIXDOG_TUI_DEBUG=1.
36
- import { tuiDebug, nextId, createTranscriptSpillBuffer } from '../tui/session/transcript-spill.mjs';
37
+ import {
38
+ tuiDebug,
39
+ nextId,
40
+ createTranscriptSpillBuffer,
41
+ sweepStaleTranscriptSpillDirsOnce,
42
+ } from '../tui/session/transcript-spill.mjs';
37
43
 
38
44
  const SESSION_RUNTIME_MODULE = '../mixdog-session-runtime.mjs';
39
45
 
40
46
  // Shared across create calls so concurrent session startups import the
41
47
  // runtime graph once.
42
48
  let sessionRuntimeModulePromise = null;
49
+ let sessionRuntimeImporter = () => import(SESSION_RUNTIME_MODULE);
43
50
  function importSessionRuntimeModule() {
44
- sessionRuntimeModulePromise ??= import(SESSION_RUNTIME_MODULE);
51
+ // A failed import is forgotten so the next caller retries lazily.
52
+ sessionRuntimeModulePromise ??= Promise.resolve()
53
+ .then(() => sessionRuntimeImporter())
54
+ .catch((error) => {
55
+ sessionRuntimeModulePromise = null;
56
+ throw error;
57
+ });
45
58
  return sessionRuntimeModulePromise;
46
59
  }
47
60
 
61
+ /**
62
+ * Background warm-up for the daemon (scheduled after the ready signal): loads
63
+ * the session-runtime module tree and runs the one-per-process transcript
64
+ * spill sweep, so the first session.create pays for neither. Failures are
65
+ * swallowed — create falls back to the same lazy import.
66
+ */
67
+ export async function warmLocalSessionRuntime() {
68
+ try {
69
+ await importSessionRuntimeModule();
70
+ // Separate turn: let queued I/O run between the import and the sync sweep.
71
+ await new Promise((resolve) => setImmediate(resolve));
72
+ sweepStaleTranscriptSpillDirsOnce();
73
+ return true;
74
+ } catch {
75
+ return false;
76
+ }
77
+ }
78
+
79
+ /** Test seam: replace the dynamic import of the session-runtime module. */
80
+ export function setSessionRuntimeImporterForTest(importer) {
81
+ sessionRuntimeImporter = importer || (() => import(SESSION_RUNTIME_MODULE));
82
+ sessionRuntimeModulePromise = null;
83
+ }
84
+
48
85
  const TOOL_APPROVAL_TIMEOUT_MS = (() => {
49
86
  const value = Number(process.env.MIXDOG_TOOL_APPROVAL_TIMEOUT_MS);
50
87
  return Number.isFinite(value) && value > 0 ? Math.max(1000, Math.round(value)) : 120_000;
@@ -98,7 +135,7 @@ export function createRuntimePulse({
98
135
  if (!set(changed)) emit();
99
136
  return true;
100
137
  };
101
- return () => {
138
+ const pulse = () => {
102
139
  if (flags.disposed) return;
103
140
  if (flags.pendingSessionReset) return;
104
141
  // Attached viewer with a live pipe: the owner's frames are authoritative
@@ -115,6 +152,12 @@ export function createRuntimePulse({
115
152
  ...agentStatusState(),
116
153
  });
117
154
  };
155
+ /** Publishes just the workspace (a HEAD change): no stats or agent recompute. */
156
+ pulse.publishWorkspace = () => {
157
+ if (flags.disposed || flags.pendingSessionReset) return;
158
+ publishChanged({ workspace: routeState().workspace });
159
+ };
160
+ return pulse;
118
161
  }
119
162
 
120
163
  export async function createLocalSessionRuntime({
@@ -277,20 +320,29 @@ export async function createLocalSessionRuntime({
277
320
  timeoutMs: TOOL_APPROVAL_TIMEOUT_MS,
278
321
  }
279
322
  );
280
- lifecycle.runtimePulseTimer = setInterval(
281
- createRuntimePulse({
282
- flags,
283
- liveShareMirroring: () => bag.liveShareMirroring?.() === true,
284
- getState,
285
- getPublishedState,
286
- set,
287
- emit: store.emit,
288
- routeState,
289
- agentStatusState,
290
- syncContextStats,
291
- }),
292
- 2000
293
- );
323
+ const runtimePulse = createRuntimePulse({
324
+ flags,
325
+ liveShareMirroring: () => bag.liveShareMirroring?.() === true,
326
+ getState,
327
+ getPublishedState,
328
+ set,
329
+ emit: store.emit,
330
+ routeState,
331
+ agentStatusState,
332
+ syncContextStats,
333
+ });
334
+ // A `git switch` from any terminal reaches the island within ~a frame; the
335
+ // pulse retargets the watch when the session's folder moves.
336
+ const headWatch = createGitHeadWatcher({
337
+ getCwd: () => runtime.getLiveWorkspace?.()?.cwd || runtime.cwd,
338
+ onChange: runtimePulse.publishWorkspace,
339
+ });
340
+ headWatch.retarget();
341
+ lifecycle.unsubscribeGitHeadWatch = headWatch.dispose;
342
+ lifecycle.runtimePulseTimer = setInterval(() => {
343
+ headWatch.retarget();
344
+ runtimePulse();
345
+ }, 2000);
294
346
  lifecycle.runtimePulseTimer.unref?.();
295
347
 
296
348
  const {
@@ -405,8 +457,7 @@ export async function createLocalSessionRuntime({
405
457
  Object.assign(bag, createSessionFlow(bag));
406
458
  // Runtime-driven completion wakes (no attached surface acked the completion)
407
459
  // get the same idle auto-clear as a user submit.
408
- lifecycle.unsubscribeCompletionWakeGate =
409
- runtime.setCompletionWakeGate?.(() => bag.autoClearBeforeWake()) || null;
460
+ lifecycle.unsubscribeCompletionWakeGate = runtime.setCompletionWakeGate?.(() => bag.autoClearBeforeWake()) || null;
410
461
  Object.assign(
411
462
  bag,
412
463
  createGoalContinuation({
@@ -21,6 +21,7 @@ import { withHeapCap } from '../runtime/shared/heap-cap.mjs';
21
21
  import { daemonCrashCaptureDir, daemonDataDir } from '../session-runtime/services/daemon-crash-capture.mjs';
22
22
  import { forkDaemonCandidate } from '../session-runtime/services/daemon-candidate.mjs';
23
23
  import { daemonOwnerIsStale, proveDaemonOwnerRecord } from './daemon-owner.mjs';
24
+ import { IN_TREE_DAEMON_STOP_GRACE_MS } from './daemon-stop-budget.mjs';
24
25
  import { createAttachmentCalls } from './session-client/attachment-calls.mjs';
25
26
  import { daemonUnavailableError } from './session-client/daemon-unavailable.mjs';
26
27
  import { createLeaseHeartbeat } from './session-client/lease-heartbeat.mjs';
@@ -324,6 +325,12 @@ async function replaceLowerDaemon(discovery, initialHealth, { log, onUpgradeWait
324
325
  * caller then re-reads discovery and attaches to whoever won. The shared fork
325
326
  * and crash capture live in ./daemon-candidate.mjs.
326
327
  * `entry` is a test seam; production always forks the real daemon. */
328
+ // Daemons this process forked into its own process tree (daemonShouldDetach
329
+ // false: the packaged Windows Desktop). Such a child is killed with its
330
+ // launcher (libuv's kill-on-close job), which skips its whole teardown —
331
+ // PostgreSQL stop included — so the launcher's quit stops it first.
332
+ const inTreeDaemons = new Map();
333
+
327
334
  export function spawnDaemonCandidate({ cwd, log, timeoutMs = 30_000, entry = daemonEntry() }) {
328
335
  const t0 = performance.now();
329
336
  const at = () => Math.round(performance.now() - t0);
@@ -353,6 +360,10 @@ export function spawnDaemonCandidate({ cwd, log, timeoutMs = 30_000, entry = dae
353
360
  const forkMs = at();
354
361
  child.once('spawn', () => {
355
362
  log(`daemon fork: forkMs=${forkMs} spawnEventMs=${at()}`);
363
+ if (!detached && child.pid) {
364
+ inTreeDaemons.set(child.pid, child);
365
+ child.once('exit', () => inTreeDaemons.delete(child.pid));
366
+ }
356
367
  });
357
368
  },
358
369
  onReady: () => log(`daemon ready at=${at()}ms`),
@@ -634,6 +645,37 @@ export async function shutdownDaemon(
634
645
  return true;
635
646
  }
636
647
 
648
+ /** Launcher quit: a daemon this process forked into its own tree would die
649
+ * with it, so ask the serving one to stop (forced — its work cannot outlive
650
+ * this tree anyway) and wait, bounded, for every such child's graceful exit.
651
+ * One that already unpublished its endpoint is mid-teardown: only waited on.
652
+ * False when no such child is alive (detached, or another launcher's). */
653
+ export async function stopInTreeDaemon({
654
+ timeoutMs = IN_TREE_DAEMON_STOP_GRACE_MS,
655
+ discovery = readSessionDiscovery(),
656
+ } = {}) {
657
+ const pids = [...inTreeDaemons.keys()].filter((pid) => isPidAlive(pid));
658
+ if (!pids.length) return false;
659
+ if (discovery?.port && pids.includes(Number(discovery.pid))) {
660
+ try {
661
+ await request({
662
+ port: discovery.port,
663
+ token: discovery.token,
664
+ method: 'POST',
665
+ path: '/stop',
666
+ body: { force: true },
667
+ timeoutMs: 3000,
668
+ control: true,
669
+ });
670
+ } catch {
671
+ // Past its front door already: only the wait remains.
672
+ }
673
+ }
674
+ const deadline = Date.now() + Math.max(1, Number(timeoutMs) || IN_TREE_DAEMON_STOP_GRACE_MS);
675
+ while (pids.some((pid) => isPidAlive(pid)) && Date.now() < deadline) await delay(50);
676
+ return !pids.some((pid) => isPidAlive(pid));
677
+ }
678
+
637
679
  export async function shutdownDaemonForRuntimeRoot(root, options = {}) {
638
680
  const runtimeRoot = String(root || '').trim();
639
681
  if (!runtimeRoot) return false;
@@ -30,6 +30,7 @@ export const SESSION_READ_ACTIONS = Object.freeze([
30
30
  'getQuotaAutoResume',
31
31
  'getQuotaHistory',
32
32
  'getRecapSettings',
33
+ 'getSessionTitleSettings',
33
34
  'getWebSearchRoute',
34
35
  'getSettingsSnapshot',
35
36
  'getSystemShell',
@@ -41,9 +42,11 @@ export const SESSION_READ_ACTIONS = Object.freeze([
41
42
  'inspectHuggingFaceModel',
42
43
  'getLocalProviderModelDetails',
43
44
  'getSessionReviewDiff',
45
+ 'getSessionReviewFilePatch',
44
46
  'getSessionUsage',
45
47
  'getTurnChanges',
46
48
  'getTurnReviewDiff',
49
+ 'getTurnReviewFilePatch',
47
50
  'getTurnLiveness',
48
51
  'getUpdateSettings',
49
52
  'getUpdateStatus',
@@ -84,6 +87,7 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
84
87
  'addPlugin',
85
88
  'addSkill',
86
89
  'agentControl',
90
+ 'askSide',
87
91
  'authenticateProvider',
88
92
  'beginOAuthProviderLogin',
89
93
  'beginMcpOAuthLogin',
@@ -93,6 +97,7 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
93
97
  'cancelOAuthProviderLogin',
94
98
  'cancelMcpOAuthLogin',
95
99
  'cancelConversationImport',
100
+ 'cancelSide',
96
101
  'checkForUpdate',
97
102
  'clear',
98
103
  'compact',
@@ -110,6 +115,7 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
110
115
  'removeCustomProvider',
111
116
  'updateCustomProviderModel',
112
117
  'testCustomProvider',
118
+ 'checkCustomProviderKeyEnv',
113
119
  'discoverCustomProviderModels',
114
120
  'deleteSession',
115
121
  'deleteWebhook',
@@ -194,6 +200,7 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
194
200
  'setProfile',
195
201
  'setProgressHint',
196
202
  'setRecapEnabled',
203
+ 'setAiSessionTitles',
197
204
  'setRoute',
198
205
  'setScheduleEnabled',
199
206
  'setWebSearchRoute',