mixdog 1.2.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (557) hide show
  1. package/README.md +18 -0
  2. package/package.json +2 -1
  3. package/scripts/gate-local.mjs +19 -3
  4. package/scripts/model-catalog-audit.mjs +1 -15
  5. package/scripts/provider-pingpong-live.mjs +509 -0
  6. package/src/agents/advisor/AGENT.md +1 -1
  7. package/src/agents/advisor/agent.json +1 -1
  8. package/src/agents/reviewer/AGENT.md +3 -3
  9. package/src/agents/reviewer/agent.json +1 -1
  10. package/src/defaults/shipped-history.json +6 -0
  11. package/src/defaults/skills/setup/SKILL.md +6 -1
  12. package/src/defaults/skills/setup/references/actions.md +44 -22
  13. package/src/defaults/skills/setup/references/surfaces.md +10 -8
  14. package/src/headless-exec.mjs +15 -8
  15. package/src/lib/keychain-cjs.cjs +7 -4
  16. package/src/output-styles/common.md +11 -10
  17. package/src/repl/slash-commands.mjs +16 -1
  18. package/src/rules/agent/AGENT.md +5 -5
  19. package/src/rules/lead/LEAD.md +5 -5
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -4
  21. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
  22. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +92 -61
  23. package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
  24. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
  25. package/src/runtime/agent/orchestrator/config.mjs +5 -0
  26. package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
  27. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
  28. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
  29. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +36 -7
  30. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +49 -10
  31. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +40 -3
  32. package/src/runtime/agent/orchestrator/mcp/client.mjs +230 -96
  33. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
  34. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
  35. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
  36. package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
  37. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
  38. package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
  39. package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
  40. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
  41. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +188 -29
  42. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
  43. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
  44. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
  45. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +98 -0
  46. package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
  47. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
  48. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +48 -37
  49. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +10 -7
  50. package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
  51. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
  52. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  53. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
  54. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
  55. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +102 -45
  58. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
  59. package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
  60. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-blocks.mjs +20 -1
  61. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
  62. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
  63. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
  65. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
  66. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +10 -1
  68. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +40 -7
  70. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
  71. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
  72. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +88 -21
  73. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +30 -10
  74. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +106 -0
  75. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
  76. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +94 -15
  77. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  78. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +117 -72
  79. package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
  80. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +75 -10
  81. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  82. package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
  83. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +103 -23
  84. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
  85. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
  86. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +32 -6
  87. package/src/runtime/agent/orchestrator/providers/cursor.mjs +27 -3
  88. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
  89. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
  90. package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
  91. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
  92. package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
  93. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +80 -18
  94. package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
  95. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +46 -8
  96. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +273 -0
  97. package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
  99. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +41 -5
  100. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
  101. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
  102. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +369 -112
  103. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +3 -1
  104. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +140 -35
  105. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +70 -24
  106. package/src/runtime/agent/orchestrator/providers/gemini.mjs +80 -29
  107. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  108. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  109. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +69 -19
  110. package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
  111. package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
  112. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
  113. package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
  114. package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
  115. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
  116. package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
  117. package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
  118. package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
  119. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
  120. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
  121. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
  122. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
  123. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
  124. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
  125. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +1 -1
  126. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  127. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
  128. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
  129. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
  130. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
  131. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +123 -61
  132. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
  133. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +27 -2
  134. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
  135. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
  136. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
  137. package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
  138. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +47 -2
  139. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
  140. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
  141. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
  142. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +64 -23
  143. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +79 -33
  144. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
  145. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
  146. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
  147. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +144 -15
  148. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
  149. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
  150. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +9 -2
  151. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
  152. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +109 -26
  153. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +27 -6
  154. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +34 -20
  155. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +23 -4
  156. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +37 -28
  157. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
  158. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
  159. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +23 -10
  160. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +43 -15
  161. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
  162. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
  163. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +5 -2
  164. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
  165. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +7 -3
  166. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
  167. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
  168. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
  169. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +80 -0
  170. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +37 -4
  171. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +12 -1
  172. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
  173. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +12 -1
  174. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +76 -33
  175. package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
  176. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
  177. package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
  178. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
  179. package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
  180. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
  181. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
  182. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
  183. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
  184. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
  185. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +12 -0
  186. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
  187. package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
  188. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +41 -22
  189. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
  190. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
  191. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
  192. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +720 -0
  193. package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
  194. package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
  195. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +123 -219
  196. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
  197. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
  198. package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
  199. package/src/runtime/agent/orchestrator/providers/transport-scope.d.mts +6 -0
  200. package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
  201. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
  202. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
  203. package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
  204. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
  205. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
  206. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
  207. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
  208. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
  209. package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +0 -18
  210. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
  211. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
  212. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -97
  213. package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
  214. package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
  215. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
  216. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
  217. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  218. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
  219. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
  220. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
  221. package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
  222. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +5 -1
  223. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
  224. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
  225. package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
  226. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
  227. package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
  228. package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
  229. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
  230. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
  231. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
  232. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
  233. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
  234. package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
  235. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
  236. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
  237. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
  238. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
  239. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +67 -20
  240. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +104 -19
  241. package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
  242. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
  243. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
  244. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
  245. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
  246. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +7 -2
  247. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +30 -2
  248. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +11 -2
  249. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
  250. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
  251. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +57 -21
  252. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
  253. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  254. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  255. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
  256. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +5 -10
  257. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
  258. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +16 -4
  259. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
  260. package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
  261. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
  262. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
  263. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
  264. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
  265. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
  266. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
  267. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +168 -29
  268. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
  269. package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
  270. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
  271. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
  272. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
  273. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
  274. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
  275. package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
  276. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
  277. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
  278. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
  279. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
  280. package/src/runtime/agent/orchestrator/session/store-summary-archived-agent.mjs +34 -15
  281. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +6 -0
  282. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +13 -0
  283. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +13 -1
  284. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
  285. package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
  286. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
  287. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
  288. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
  289. package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
  290. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
  291. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
  292. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +20 -1
  293. package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
  294. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
  295. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
  296. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
  297. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
  298. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
  299. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
  300. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
  301. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
  302. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
  303. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
  304. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
  305. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
  306. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
  307. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
  308. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
  309. package/src/runtime/computer-bridge/action-schema.mjs +44 -21
  310. package/src/runtime/github/tool.mjs +92 -2
  311. package/src/runtime/memory/index.mjs +1 -0
  312. package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
  313. package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
  314. package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
  315. package/src/runtime/memory/lib/pg/process.mjs +65 -50
  316. package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
  317. package/src/runtime/memory/lib/pg/supervisor.mjs +124 -16
  318. package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
  319. package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
  320. package/src/runtime/office/README.md +2 -2
  321. package/src/runtime/office/authoring/html-document-author.mjs +2 -0
  322. package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
  323. package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
  324. package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
  325. package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
  326. package/src/runtime/office/core/office-actions-read.mjs +3 -0
  327. package/src/runtime/office/core/office-render-preview.mjs +2 -0
  328. package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
  329. package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
  330. package/src/runtime/office/core/office-sessions.mjs +5 -0
  331. package/src/runtime/office/core/office-transactions.mjs +3 -0
  332. package/src/runtime/office/core/office-write-ledger.mjs +98 -0
  333. package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
  334. package/src/runtime/office/index.mjs +30 -0
  335. package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
  336. package/src/runtime/office/pdf/pdf-render.mjs +23 -7
  337. package/src/runtime/office/pdf/pdf-security.mjs +2 -0
  338. package/src/runtime/office/portable/portable-opc.mjs +4 -1
  339. package/src/runtime/office/quality/visual-diff.mjs +2 -0
  340. package/src/runtime/office/shared/values.mjs +0 -5
  341. package/src/runtime/shared/background-tasks.mjs +92 -14
  342. package/src/runtime/shared/bounded-file-read.mjs +13 -0
  343. package/src/runtime/shared/bounded-line-diff.mjs +182 -0
  344. package/src/runtime/shared/buffered-appender.mjs +18 -0
  345. package/src/runtime/shared/config.mjs +78 -18
  346. package/src/runtime/shared/err-text.mjs +31 -0
  347. package/src/runtime/shared/error-presentation.mjs +159 -19
  348. package/src/runtime/shared/file-permissions.d.mts +6 -1
  349. package/src/runtime/shared/file-permissions.mjs +54 -6
  350. package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
  351. package/src/runtime/shared/llm/anthropic-effort.mjs +129 -134
  352. package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
  353. package/src/runtime/shared/llm/model-catalog.mjs +53 -40
  354. package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
  355. package/src/runtime/shared/llm/provider-catalog-registry.mjs +11 -0
  356. package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
  357. package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
  358. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
  359. package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
  360. package/src/runtime/shared/loopback-listener.mjs +9 -0
  361. package/src/runtime/shared/model-display.mjs +13 -0
  362. package/src/runtime/shared/provider-api-key.mjs +2 -0
  363. package/src/runtime/shared/quota-resume-store.mjs +11 -0
  364. package/src/runtime/shared/review-diff.mjs +52 -0
  365. package/src/runtime/shared/session-baseline-store.mjs +960 -0
  366. package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
  367. package/src/runtime/shared/session-sidebar-host.mjs +38 -0
  368. package/src/runtime/shared/slow-span.mjs +114 -0
  369. package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
  370. package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
  371. package/src/runtime/shared/task-notification-envelope.mjs +2 -2
  372. package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
  373. package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
  374. package/src/runtime/shared/tool-card-model.mjs +5 -2
  375. package/src/runtime/shared/tool-output-limit.mjs +4 -0
  376. package/src/runtime/shared/tool-primitives.mjs +13 -0
  377. package/src/runtime/shared/tool-result-summary.mjs +8 -1
  378. package/src/runtime/shared/tool-surface.mjs +18 -3
  379. package/src/runtime/shared/tool-work-units.mjs +40 -4
  380. package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
  381. package/src/runtime/shared/transcript-writer.mjs +10 -2
  382. package/src/runtime/shared/turn-changes.mjs +670 -274
  383. package/src/runtime/shared/turn-review-reply.mjs +8 -39
  384. package/src/runtime/shared/turn-snapshot-store.mjs +94 -2
  385. package/src/runtime/shared/turn-snapshot.mjs +986 -690
  386. package/src/runtime/shared/turn-worktree-snapshot.mjs +279 -262
  387. package/src/runtime/shared/user-data-guard.mjs +262 -49
  388. package/src/runtime/shared/worktrees/carry.mjs +531 -0
  389. package/src/runtime/shared/worktrees/contract.mjs +34 -24
  390. package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
  391. package/src/runtime/shared/worktrees/git-head.mjs +205 -0
  392. package/src/runtime/shared/worktrees/git.mjs +8 -3
  393. package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
  394. package/src/runtime/shared/worktrees/manager.mjs +156 -8
  395. package/src/runtime/shared/worktrees/merge.mjs +31 -21
  396. package/src/runtime/shared/worktrees/paths.d.mts +6 -0
  397. package/src/runtime/shared/worktrees/test-support.mjs +19 -4
  398. package/src/runtime/terminal-bridge/client.mjs +44 -11
  399. package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
  400. package/src/runtime/tidy/apply.mjs +47 -2
  401. package/src/runtime/tidy/run-engines.mjs +11 -5
  402. package/src/session-runtime/agent-tool-routing.mjs +19 -13
  403. package/src/session-runtime/boot/apis.mjs +19 -5
  404. package/src/session-runtime/boot/facade.mjs +1 -0
  405. package/src/session-runtime/boot/infrastructure.mjs +12 -1
  406. package/src/session-runtime/boot/session.mjs +0 -1
  407. package/src/session-runtime/boot/tools.mjs +4 -1
  408. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
  409. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
  410. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
  411. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
  412. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
  413. package/src/session-runtime/internal-tool-executor.mjs +6 -2
  414. package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
  415. package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
  416. package/src/session-runtime/lifecycle/session-catalog.mjs +14 -3
  417. package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
  418. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
  419. package/src/session-runtime/lifecycle/teardown.mjs +73 -8
  420. package/src/session-runtime/lifecycle-api.mjs +1 -0
  421. package/src/session-runtime/mcp-glue/connect.mjs +46 -14
  422. package/src/session-runtime/mcp-glue/server-catalog.mjs +20 -3
  423. package/src/session-runtime/mcp-glue.mjs +3 -0
  424. package/src/session-runtime/mcp-oauth-login.mjs +174 -0
  425. package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
  426. package/src/session-runtime/model-route/set-route.mjs +85 -17
  427. package/src/session-runtime/model-route/web-search-route.mjs +1 -1
  428. package/src/session-runtime/native-web-search/routes.mjs +22 -3
  429. package/src/session-runtime/native-web-search-usage.mjs +51 -0
  430. package/src/session-runtime/native-web-search.mjs +10 -1
  431. package/src/session-runtime/new-session-config.mjs +4 -2
  432. package/src/session-runtime/provider-auth/login.mjs +4 -0
  433. package/src/session-runtime/provider-models/model-meta.mjs +12 -3
  434. package/src/session-runtime/quick-model-rows/web-search-rows.mjs +13 -8
  435. package/src/session-runtime/quick-web-search-models.mjs +13 -38
  436. package/src/session-runtime/resource-extension-sync.mjs +4 -0
  437. package/src/session-runtime/resource-mcp-api.mjs +98 -2
  438. package/src/session-runtime/resource-scope-decorate.mjs +10 -3
  439. package/src/session-runtime/runtime-review-api.mjs +21 -2
  440. package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
  441. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
  442. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
  443. package/src/session-runtime/services/agent-tool/lead-worker-index/forgotten-leads.mjs +29 -0
  444. package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
  445. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
  446. package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
  447. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
  448. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
  449. package/src/session-runtime/services/agent-tool/tag-registry.mjs +15 -1
  450. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
  451. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
  452. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +10 -1
  453. package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
  454. package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
  455. package/src/session-runtime/services/agent-tool.mjs +3 -0
  456. package/src/session-runtime/services/channel-admin.mjs +19 -3
  457. package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
  458. package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
  459. package/src/session-runtime/services/provider-admin.mjs +81 -15
  460. package/src/session-runtime/session-transcript-purge.mjs +32 -0
  461. package/src/session-runtime/session-turn-api.mjs +14 -5
  462. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
  463. package/src/session-runtime/settings-compaction-api.mjs +1 -0
  464. package/src/session-runtime/setup-tool/executor.mjs +36 -6
  465. package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
  466. package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
  467. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
  468. package/src/session-runtime/skills-api.mjs +3 -5
  469. package/src/session-runtime/tool-surface.mjs +5 -1
  470. package/src/session-runtime/turn/compaction-gate.mjs +25 -0
  471. package/src/session-runtime/turn/session-ops.mjs +7 -5
  472. package/src/session-runtime/turn/side-question-ops.mjs +22 -0
  473. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
  474. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
  475. package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
  476. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  477. package/src/session-runtime/turn/turn-run.mjs +25 -9
  478. package/src/session-runtime/usage-stats-api.mjs +43 -11
  479. package/src/session-runtime/workspace/project-branches.mjs +7 -5
  480. package/src/session-runtime/workspace/transition-steps.mjs +129 -31
  481. package/src/session-runtime/workspace/transitions.mjs +190 -15
  482. package/src/standalone/daemon-agent-control.mjs +9 -1
  483. package/src/standalone/daemon-drain-policy.mjs +8 -0
  484. package/src/standalone/daemon-local-session-bridge.mjs +14 -1
  485. package/src/standalone/daemon-owner.mjs +17 -4
  486. package/src/standalone/daemon-pg-stop.mjs +51 -0
  487. package/src/standalone/daemon-stop-budget.d.mts +2 -0
  488. package/src/standalone/daemon-stop-budget.mjs +18 -0
  489. package/src/standalone/daemon-stored-session-views.mjs +2 -0
  490. package/src/standalone/daemon-telemetry.mjs +74 -0
  491. package/src/standalone/daemon.mjs +64 -8
  492. package/src/standalone/desktop-service-registry.mjs +5 -0
  493. package/src/standalone/local-session-runtime.mjs +70 -19
  494. package/src/standalone/session-client.mjs +42 -0
  495. package/src/standalone/session-protocol.mjs +14 -0
  496. package/src/standalone/session-proxy/projection.mjs +41 -4
  497. package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
  498. package/src/standalone/session-runtime-inline-host.mjs +1 -0
  499. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
  500. package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
  501. package/src/standalone/session-service/agent-tree.mjs +12 -0
  502. package/src/standalone/session-service/quota-resume.mjs +8 -0
  503. package/src/standalone/session-service/retention/busy.mjs +15 -1
  504. package/src/standalone/session-service/session-calls/session-action.mjs +18 -3
  505. package/src/standalone/session-service-api.mjs +11 -1
  506. package/src/standalone/session-service.mjs +1 -0
  507. package/src/standalone/session-state-patch.d.mts +2 -1
  508. package/src/standalone/session-state-patch.mjs +41 -1
  509. package/src/tui/App.jsx +25 -0
  510. package/src/tui/app/app-view/floating-panel.jsx +26 -1
  511. package/src/tui/app/app-view.jsx +1 -0
  512. package/src/tui/app/btw-controller.mjs +118 -0
  513. package/src/tui/app/create-app-pickers.mjs +6 -0
  514. package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
  515. package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
  516. package/src/tui/app/extension-pickers.mjs +2 -1
  517. package/src/tui/app/model-options.mjs +11 -16
  518. package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
  519. package/src/tui/app/shell-layout/surfaces.mjs +3 -1
  520. package/src/tui/app/slash-commands.mjs +20 -5
  521. package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
  522. package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
  523. package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
  524. package/src/tui/app/use-global-key-input.mjs +5 -12
  525. package/src/tui/app/use-prompt-handlers.mjs +11 -0
  526. package/src/tui/components/BtwPanel.jsx +53 -0
  527. package/src/tui/components/QuestionPanel.jsx +275 -0
  528. package/src/tui/components/StatusLine.jsx +2 -0
  529. package/src/tui/components/question-panel-model.mjs +156 -0
  530. package/src/tui/components/status-line/local-line.mjs +5 -2
  531. package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
  532. package/src/tui/dist/index.mjs +3221 -1500
  533. package/src/tui/session/context-state.mjs +4 -2
  534. package/src/tui/session/quota-resume.mjs +46 -14
  535. package/src/tui/session/session-api/commands.mjs +43 -24
  536. package/src/tui/session/session-api/controls.mjs +2 -2
  537. package/src/tui/session/session-api/extensions.mjs +22 -0
  538. package/src/tui/session/session-api/integrations.mjs +16 -2
  539. package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
  540. package/src/tui/session/session-api/lifecycle.mjs +7 -2
  541. package/src/tui/session/session-api/routes.mjs +71 -7
  542. package/src/tui/session/session-api/settings.mjs +11 -14
  543. package/src/tui/session/session-api.mjs +1 -0
  544. package/src/tui/session/session-flow/drain.mjs +7 -1
  545. package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
  546. package/src/tui/session/tool-approval.mjs +10 -7
  547. package/src/tui/session/transcript-spill.mjs +14 -7
  548. package/src/tui/session/turn-stream.mjs +174 -31
  549. package/src/tui/session/turn.mjs +46 -16
  550. package/src/tui/statusline-ansi-bridge.mjs +1 -1
  551. package/src/ui/statusline-agents.mjs +15 -4
  552. package/src/ui/statusline.mjs +7 -3
  553. package/src/ui/streaming-markdown-heal.mjs +34 -3
  554. package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
  555. package/src/workflows/default/WORKFLOW.md +3 -1
  556. package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
  557. package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
@@ -12,7 +12,9 @@
12
12
  * (runtime/shared/worktrees/manager.mjs), loaded on first use.
13
13
  */
14
14
  import { randomUUID } from 'node:crypto';
15
+ import { isDeepStrictEqual } from 'node:util';
15
16
  import {
17
+ WORKTREE_CARRY_CHOICES,
16
18
  branchMenuEntries,
17
19
  isWorktreeBound,
18
20
  isWorktreeError,
@@ -24,6 +26,7 @@ import { normalizeWorktreeSettings } from '../../runtime/shared/worktrees/settin
24
26
  import * as maintenance from '../../runtime/shared/worktrees/maintenance.mjs';
25
27
  import { inspectRepo, throwIfAborted } from '../../runtime/shared/worktrees/git.mjs';
26
28
  import { isWithin, samePath } from '../../runtime/shared/worktrees/paths.mjs';
29
+ import { readGitHead } from '../../runtime/shared/worktrees/git-head.mjs';
27
30
  import { sessionHasConversationMessages } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
28
31
  import { buildWorkspacePromptContext } from '../../runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs';
29
32
  import {
@@ -53,6 +56,9 @@ async function defaultOwnShellJobCount(sessionIds) {
53
56
 
54
57
  const lower = (value) => String(value || '').toLowerCase();
55
58
 
59
+ const WORKTREE_BOUND_SWITCH =
60
+ 'The session works in a managed worktree; exit the worktree first (switch to a project-folder branch, or merge or remove the worktree) before moving it to another project.';
61
+
56
62
  /** The registered Project (projects.json) at `path`, or null. */
57
63
  async function defaultRegisteredProject(path) {
58
64
  const { listProjectsStrict } = await import('../../runtime/shared/projects.mjs');
@@ -62,9 +68,12 @@ async function defaultRegisteredProject(path) {
62
68
  /**
63
69
  * Re-root one session's prompt at `cwd`: environment facts and instruction
64
70
  * files for every session, project memory for the Lead only (workers never
65
- * carried it).
71
+ * carried it). A session with a conversation keeps its system prompt (cache
72
+ * intact) and gets a tail notice instead (refreshSessionWorkspaceContext);
73
+ * `move` ({ from, changes }: the session left the project folder `from` for
74
+ * a worktree) adds the uncommitted-changes and path lines to that notice.
66
75
  */
67
- export async function refreshWorkspacePrompt(session, cwd, { loadCoreMemoryContext } = {}) {
76
+ export async function refreshWorkspacePrompt(session, cwd, { loadCoreMemoryContext, move = null } = {}) {
68
77
  const { environmentContext, instructionFilesContext } = buildWorkspacePromptContext(session, cwd);
69
78
  const coreMemoryContext =
70
79
  session?.owner === AGENT_OWNER || typeof loadCoreMemoryContext !== 'function'
@@ -75,6 +84,7 @@ export async function refreshWorkspacePrompt(session, cwd, { loadCoreMemoryConte
75
84
  environmentContext,
76
85
  coreMemoryContext,
77
86
  instructionFilesContext,
87
+ ...(move ? { move } : {}),
78
88
  });
79
89
  }
80
90
 
@@ -137,9 +147,10 @@ const TRANSITION_STATE = { worktree: 'provisioning', branch: 'switching', 'remov
137
147
  * @param {object} deps
138
148
  * @param {() => any} deps.getSession The Lead session this runtime hosts.
139
149
  * @param {() => string} deps.getCurrentCwd
140
- * @param {(cwd: string) => unknown} deps.applyResolvedCwd Retargets rt cwd, session.cwd and the cwd override.
150
+ * @param {(cwd: string, options?: { persistProjectSelection?: boolean }) => unknown} deps.applyResolvedCwd Retargets rt cwd, session.cwd and the cwd override (and, persisting, the Project selection).
151
+ * @param {(desktopSession: any) => unknown} [deps.setDesktopSession] Restores the runtime's desktop metadata on a rollback.
141
152
  * @param {(session: any) => unknown} deps.persistSession
142
- * @param {(session: any, cwd: string) => Promise<unknown>} deps.refreshWorkspaceContext
153
+ * @param {(session: any, cwd: string, move?: { from: string, changes: 'carried'|'left'|null }|null) => Promise<unknown>} deps.refreshWorkspaceContext
143
154
  * @param {() => number} deps.getActiveTurnCount
144
155
  * @param {() => { agentWorkers?: any[], agentJobs?: any[] }} deps.agentStatusState
145
156
  * @param {(sessionId: string, cwd: string) => unknown} [deps.updateLeadWorkerCwd] Moves the Lead's pool row (lead-workers.json).
@@ -149,6 +160,7 @@ export function createWorkspaceTransitions({
149
160
  getReservedSessionId = () => null,
150
161
  getCurrentCwd,
151
162
  getDesktopSession = () => null,
163
+ setDesktopSession = () => {},
152
164
  getConfig = () => null,
153
165
  createCurrentSession = null,
154
166
  applyResolvedCwd,
@@ -207,6 +219,37 @@ export function createWorkspaceTransitions({
207
219
  return normalizeSessionWorkspace({ projectPath, cwd: getCurrentCwd() || projectPath, revision: 0 });
208
220
  }
209
221
 
222
+ let liveWorkspace = null;
223
+
224
+ /**
225
+ * The workspace the session publishes: a worktree-bound record as stored; a
226
+ * project-folder session (never moved, back from a worktree, or carrying a
227
+ * transition stub) with the folder's checked-out branch / detached commit and
228
+ * repoCommonDir read straight from HEAD — no git process. Null outside a git
229
+ * repository for a session without a record. Identity-stable while equal.
230
+ */
231
+ function getLiveWorkspace() {
232
+ const session = getSession();
233
+ const stored = readSessionWorkspace(session);
234
+ if (isWorktreeBound(stored)) return session.workspace;
235
+ let base;
236
+ try {
237
+ base = stored ?? currentWorkspace(session);
238
+ } catch {
239
+ return session?.workspace ?? null;
240
+ }
241
+ const head = readGitHead(base.cwd);
242
+ if (!head) return session?.workspace ?? null;
243
+ const next = {
244
+ ...base,
245
+ repoCommonDir: head.repoCommonDir,
246
+ branch: head.branch,
247
+ baseCommit: head.branch ? base.baseCommit : head.commit,
248
+ };
249
+ if (!liveWorkspace || !isDeepStrictEqual(liveWorkspace, next)) liveWorkspace = next;
250
+ return liveWorkspace;
251
+ }
252
+
210
253
  function isSessionActive(session) {
211
254
  const runtime = sessionManager?.getSessionRuntime?.(session.id);
212
255
  if (runtime?.controller?.signal && !runtime.controller.signal.aborted) return true;
@@ -223,8 +266,11 @@ export function createWorkspaceTransitions({
223
266
  (candidate.ownerSessionId === session.id || candidate.parentSessionId === session.id)
224
267
  );
225
268
 
226
- /** WORKTREE_BUSY_JOBS while this Lead has a turn, workers or background shells running. */
227
- async function assertLeadIdle(session, { ownTurn = false } = {}) {
269
+ /**
270
+ * WORKTREE_BUSY_JOBS while this Lead has a turn, workers or background
271
+ * shells running (`shellJobs: false` tolerates background shells).
272
+ */
273
+ async function assertLeadIdle(session, { ownTurn = false, shellJobs: checkShellJobs = true } = {}) {
228
274
  if (!ownTurn && getActiveTurnCount() > 0) {
229
275
  throw worktreeError('WORKTREE_BUSY_JOBS', 'The session is running a turn; wait for it to finish.', {
230
276
  reason: 'turn',
@@ -240,6 +286,7 @@ export function createWorkspaceTransitions({
240
286
  if (busyWorkers || busyJobs) {
241
287
  throw worktreeError('WORKTREE_BUSY_JOBS', undefined, { reason: 'workers', workers: busyWorkers, jobs: busyJobs });
242
288
  }
289
+ if (!checkShellJobs) return;
243
290
  const ids = new Set([session.id, ...workers.map((row) => row?.sessionId).filter(Boolean)]);
244
291
  for (const worker of workersOf(session)) ids.add(worker.id);
245
292
  const shellJobs = await countOwnShellJobs([...ids]);
@@ -284,11 +331,44 @@ export function createWorkspaceTransitions({
284
331
  }
285
332
  }
286
333
 
334
+ /**
335
+ * Before a carry clears the shared checkout `folder` (held by the caller,
336
+ * so no new turn starts there): WORKTREE_BUSY_JOBS while any other session
337
+ * working in it — idle or not, this Lead's workers included — owns a
338
+ * running background shell (holdProjectFolder already refused running
339
+ * turns and agents there, assertLeadIdle this Lead's own). Processes
340
+ * outside Mixdog (editors, watchers) cannot be excluded.
341
+ */
342
+ async function assertCheckoutQuiet(session, folder) {
343
+ const sharing = listLiveSessions()
344
+ .filter(
345
+ (candidate) =>
346
+ candidate?.id &&
347
+ candidate.id !== session.id &&
348
+ candidate.closed !== true &&
349
+ typeof candidate.cwd === 'string' &&
350
+ isWithin(folder, candidate.cwd)
351
+ )
352
+ .map((candidate) => candidate.id);
353
+ if (!sharing.length) return;
354
+ const shellJobs = await countOwnShellJobs(sharing);
355
+ if (shellJobs > 0) {
356
+ throw worktreeError(
357
+ 'WORKTREE_BUSY_JOBS',
358
+ 'Background shells of other sessions are running in the project folder; carrying the changes needs it quiet.',
359
+ { reason: 'folder-shell-jobs', shellJobs }
360
+ );
361
+ }
362
+ }
363
+
287
364
  const steps = createTransitionSteps({
365
+ assertCheckoutQuiet,
288
366
  backend,
289
367
  inspectProject,
290
368
  runtimeToken,
291
369
  getCurrentCwd,
370
+ getDesktopSession,
371
+ setDesktopSession,
292
372
  applyResolvedCwd,
293
373
  persistSession,
294
374
  refreshWorkspaceContext,
@@ -331,6 +411,7 @@ export function createWorkspaceTransitions({
331
411
  name: target.name,
332
412
  nameHint: options.nameHint,
333
413
  confirmLarge: options.confirmLarge,
414
+ changes: options.changes,
334
415
  });
335
416
  }
336
417
  if (target.kind === 'branch') {
@@ -442,9 +523,22 @@ export function createWorkspaceTransitions({
442
523
  });
443
524
  // A session in the project folder (never moved, or back from a worktree)
444
525
  // is on whatever that folder has checked out now.
445
- const checkedOut = menu.occupancy.find((row) => row.kind === 'current')?.branch ?? null;
526
+ // Its repository is the project's (a never-moved session has no stored
527
+ // record), and a detached checkout shows branch null with the commit in
528
+ // baseCommit — never mistaken for "not a repository".
529
+ let view = workspace;
530
+ if (!isWorktreeBound(workspace)) {
531
+ const project = await inspectProject(workspace.projectPath);
532
+ const checkedOut = project.currentBranch ?? null;
533
+ view = {
534
+ ...workspace,
535
+ repoCommonDir: project.repoCommonDir ?? workspace.repoCommonDir,
536
+ branch: checkedOut,
537
+ baseCommit: checkedOut ? workspace.baseCommit : (project.headCommit ?? null),
538
+ };
539
+ }
446
540
  return {
447
- workspace: isWorktreeBound(workspace) || !checkedOut ? workspace : { ...workspace, branch: checkedOut },
541
+ workspace: view,
448
542
  pendingWorktree: pendingIntent ? { name: pendingIntent.name } : null,
449
543
  branches: branchMenuEntries(menu.branches, menu.occupancy),
450
544
  occupancy: menu.occupancy,
@@ -510,17 +604,28 @@ export function createWorkspaceTransitions({
510
604
  * reuse it. Typed failures (large repository, disk, busy) therefore reach
511
605
  * the sender while its draft is still unsent, and the turn that follows
512
606
  * finds the session already bound.
607
+ *
608
+ * A provisioning that creates a new worktree decides the project folder's
609
+ * uncommitted changes (tracked and untracked): none → it proceeds; without
610
+ * `carryChanges` it refuses with WORKTREE_DIRTY_CHOICE (`details.count`);
611
+ * `leave` starts the worktree at HEAD and leaves them; `carry` moves them
612
+ * into it (the result's `changes` reports which). Re-entering a retained
613
+ * worktree never touches them.
513
614
  */
514
615
  async function requestWorktree({
515
616
  name = null,
516
617
  confirmLarge = false,
517
618
  provision = false,
619
+ carryChanges,
518
620
  prompt = '',
519
621
  txId = '',
520
622
  expectedRevision,
521
623
  } = {}) {
522
624
  const nameHint = validName(name);
523
625
  const expected = validRevision(expectedRevision);
626
+ if (carryChanges !== undefined && carryChanges !== null && !WORKTREE_CARRY_CHOICES.includes(carryChanges)) {
627
+ throw new TypeError(`carryChanges must be one of ${WORKTREE_CARRY_CHOICES.join(', ')}.`);
628
+ }
524
629
  const session = provision === true ? await ensureSession() : getSession();
525
630
  // No session yet: the menu it was read from had revision 0.
526
631
  const current = session?.id ? currentWorkspace(session) : null;
@@ -536,6 +641,7 @@ export function createWorkspaceTransitions({
536
641
  return { ...entered, pending: false };
537
642
  }
538
643
  }
644
+ const priorIntent = pendingIntent;
539
645
  pendingIntent = {
540
646
  name: nameHint ?? pendingIntent?.name ?? null,
541
647
  confirmLarge: confirmLarge === true || pendingIntent?.confirmLarge === true,
@@ -544,10 +650,21 @@ export function createWorkspaceTransitions({
544
650
  txId: pendingIntent?.txId ?? null,
545
651
  };
546
652
  if (provision === true) {
547
- return {
548
- ...(await provisionIntent(session, pendingIntent, txId, prompt, { expectedRevision: expected })),
549
- pending: false,
550
- };
653
+ const intent = pendingIntent;
654
+ try {
655
+ return {
656
+ ...(await provisionIntent(session, intent, txId, prompt, {
657
+ expectedRevision: expected,
658
+ changes: carryChanges ?? 'ask',
659
+ })),
660
+ pending: false,
661
+ };
662
+ } catch (error) {
663
+ // A refused move (uncommitted changes to decide, busy, …) is never
664
+ // re-armed for the next turn, where nobody could answer it.
665
+ if (pendingIntent === intent) pendingIntent = priorIntent;
666
+ throw error;
667
+ }
551
668
  }
552
669
  const workspace = session?.id ? currentWorkspace(session) : null;
553
670
  return { revision: workspace?.revision ?? 0, workspace, pending: true };
@@ -570,15 +687,35 @@ export function createWorkspaceTransitions({
570
687
  if (cleanup !== true || merged.merged !== true) return merged;
571
688
  // "Merge and clean up": with autoCleanup on, the merged worktree goes now
572
689
  // (a dirty one stays, reported); off, nothing is removed automatically.
690
+ // The merge is committed: a failed or refused cleanup (dirty, retained
691
+ // file recovery data, ...) is a cleanup notice, never a merge failure.
573
692
  if (worktreeSettings().autoCleanup !== true) {
574
- return { ...merged, cleanup: { removed: false, reason: 'auto-cleanup-off' } };
693
+ return {
694
+ ...merged,
695
+ cleanup: { removed: false, reason: 'auto-cleanup-off', message: 'Auto cleanup is off; the worktree is kept.' },
696
+ };
575
697
  }
576
698
  try {
577
699
  const removed = await removeWorktree({ force: false }, { ownTurn });
578
700
  return { ...merged, cleanup: removed.removed, revision: removed.revision, workspace: removed.workspace };
579
701
  } catch (error) {
580
- if (!isWorktreeError(error, 'WORKTREE_DIRTY') && !isWorktreeError(error, 'WORKTREE_UNMERGED')) throw error;
581
- return { ...merged, cleanup: { removed: false, reason: error.code } };
702
+ // The merge stands: a refused or incomplete cleanup (dirty, unmerged,
703
+ // retained file recovery data, partly removed, …) is reported, never
704
+ // thrown, with the workspace as it is now (an incomplete removal already
705
+ // returned to the project folder).
706
+ const workspace = currentWorkspace(session);
707
+ const code = typeof error?.code === 'string' ? error.code : '';
708
+ return {
709
+ ...merged,
710
+ cleanup: {
711
+ removed: false,
712
+ reason: code || 'WORKTREE_CLEANUP_FAILED',
713
+ ...(code ? { code } : {}),
714
+ message: String(error?.message || error),
715
+ },
716
+ revision: workspace.revision,
717
+ workspace,
718
+ };
582
719
  }
583
720
  }
584
721
 
@@ -620,6 +757,41 @@ export function createWorkspaceTransitions({
620
757
  }
621
758
  }
622
759
 
760
+ /**
761
+ * Move the hosted Lead to another Project folder (session set-project, the
762
+ * desktop/remote setCwd): the one authority for a Project switch, admitted
763
+ * like a workspace transition. Refused while a turn runs (`ownTurn`: the
764
+ * Lead's own tool call tolerates its calling turn), while workers run, and
765
+ * while the session works in a managed worktree. Serialized with turns and
766
+ * other workspace changes; the prompt refresh of the Lead and its idle
767
+ * workers completes (or rolls the switch back) before it resolves. Without
768
+ * a session (a draft) only the runtime cwd and the selection move.
769
+ * `classifyByMembership` (the desktop/remote move): a folder outside every
770
+ * registered Project is classified as a task (cwd-apply).
771
+ * @returns {Promise<string>} the runtime cwd after the switch
772
+ */
773
+ async function switchProject(cwd, { ownTurn = false, classifyByMembership = false } = {}) {
774
+ const selection = { classifyByMembership: classifyByMembership === true };
775
+ const session = getSession();
776
+ if (!session?.id) {
777
+ applyResolvedCwd(cwd, { persistProjectSelection: true, ...selection });
778
+ return getCurrentCwd();
779
+ }
780
+ const leave = beginWorkspaceTransition(session.id, `project:${newTxId()}`);
781
+ const journal = [];
782
+ try {
783
+ await assertLeadIdle(session, { ownTurn: ownTurn === true, shellJobs: false });
784
+ if (isWorktreeBound(readSessionWorkspace(session))) throw new Error(WORKTREE_BOUND_SWITCH);
785
+ await steps.switchProject(session, cwd, journal, selection);
786
+ return getCurrentCwd();
787
+ } catch (error) {
788
+ await undoAll(journal, error);
789
+ throw error;
790
+ } finally {
791
+ leave();
792
+ }
793
+ }
794
+
623
795
  async function removeWorktree({ force = false, expectedRevision } = {}, { ownTurn = false } = {}) {
624
796
  const expected = validRevision(expectedRevision);
625
797
  const session = hostedSession(null);
@@ -636,6 +808,8 @@ export function createWorkspaceTransitions({
636
808
  admitTurnWorkspace,
637
809
  /** The durable workspace record of the hosted session (null: project folder, never moved). */
638
810
  getWorkspace: () => getSession()?.workspace ?? null,
811
+ /** What the session snapshot publishes (see getLiveWorkspace). */
812
+ getLiveWorkspace,
639
813
  getBranchMenu,
640
814
  getProjectBranchMenu: projectBranches.getProjectBranchMenu,
641
815
  selectBranch,
@@ -644,6 +818,7 @@ export function createWorkspaceTransitions({
644
818
  requestWorktree,
645
819
  mergeWorkspace,
646
820
  removeWorktree,
821
+ switchProject,
647
822
  listWorktrees,
648
823
  pruneWorktrees,
649
824
  cleanupDeletedSession,
@@ -68,5 +68,13 @@ export function createCanonicalAgentControl({ getSessionService, getSessionRunti
68
68
  return await tool.execute(args, scopedContext);
69
69
  }
70
70
 
71
- return { canonicalAgentTool, execute };
71
+ /** Graceful shutdown: drain and release the canonical agent's index writers.
72
+ * Never builds the tool just to dispose it. */
73
+ async function dispose() {
74
+ if (!canonicalAgentToolPromise) return;
75
+ const tool = await canonicalAgentToolPromise.catch(() => null);
76
+ await tool?.disposeAsync?.({ settle: true });
77
+ }
78
+
79
+ return { canonicalAgentTool, execute, dispose };
72
80
  }
@@ -1,3 +1,11 @@
1
+ /** Whether an idle GC sweep may start: no service call and no running turn
2
+ * (provider stream / tool call). Background-task-only sessions and memory
3
+ * agents do not block it — unlike drainDeferral, which guards shutdown. */
4
+ export function idleGcEligible(work) {
5
+ const { activeCalls = 0, queuedCalls = 0, turnBusySessions = 0 } = work || {};
6
+ return !(activeCalls > 0 || queuedCalls > 0 || turnBusySessions > 0);
7
+ }
8
+
1
9
  /** Whether self-shutdown must wait for in-flight work. A forced replacement
2
10
  * never waits: shutdown() aborts busy sessions, service calls and agent runs. */
3
11
  export function drainDeferral(work, { force = false } = {}) {
@@ -22,9 +22,22 @@ export function createLocalSessionBridge({ getSessionService, log, pid = process
22
22
  return createSessionProtocolClient({
23
23
  call(name, args = {}, options = {}) {
24
24
  if (closed) throw new Error('daemon-local session client is closed');
25
- return getSessionService().handleCall(name, args, {
25
+ const signal = options?.signal;
26
+ signal?.throwIfAborted();
27
+ const result = getSessionService().handleCall(name, args, {
26
28
  clientToken,
27
29
  ...(options?.callId ? { callId: String(options.callId) } : {}),
30
+ ...(signal ? { signal } : {}),
31
+ });
32
+ if (!signal) return result;
33
+ // The in-process service has no cancellation of its own: an abort
34
+ // frees the caller, whose late reply is dropped.
35
+ return new Promise((resolve, reject) => {
36
+ const onAbort = () => reject(signal.reason);
37
+ signal.addEventListener('abort', onAbort, { once: true });
38
+ Promise.resolve(result)
39
+ .then(resolve, reject)
40
+ .finally(() => signal.removeEventListener('abort', onAbort));
28
41
  });
29
42
  },
30
43
  async close(reason = 'local session view closed') {
@@ -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]);
@@ -93,6 +93,71 @@ export function collectDaemonTelemetry({
93
93
  };
94
94
  }
95
95
 
96
+ /**
97
+ * Tracks the smallest heapUsed observed right after a major GC (mark-sweep-
98
+ * compact) — the retained-set size, which heapUsed at an arbitrary instant
99
+ * hides. Sampled per telemetry interval; no per-GC logging. Returns null when
100
+ * PerformanceObserver is unavailable.
101
+ */
102
+ export function createPostGcHeapTracker({
103
+ heapUsed = () => v8.getHeapStatistics().used_heap_size,
104
+ observe = (onGc) => {
105
+ const obs = new PerformanceObserver((list) => {
106
+ for (const entry of list.getEntries()) onGc(entry);
107
+ });
108
+ obs.observe({ entryTypes: ['gc'] });
109
+ return () => obs.disconnect();
110
+ },
111
+ } = {}) {
112
+ let min = -1;
113
+ let disconnect = null;
114
+ try {
115
+ disconnect = observe((entry) => {
116
+ // NODE_PERFORMANCE_GC_MAJOR = 4 (older Node: entry.kind); newer: entry.detail.kind.
117
+ const kind = entry?.detail?.kind ?? entry?.kind;
118
+ if (kind !== 4) return;
119
+ const used = byteCount(heapUsed());
120
+ if (used >= 0 && (min < 0 || used < min)) min = used;
121
+ });
122
+ } catch {
123
+ return null;
124
+ }
125
+ return {
126
+ /** Min post-major-GC heap since the last take; -1 when none ran. */
127
+ take() {
128
+ const value = min;
129
+ min = -1;
130
+ return value;
131
+ },
132
+ stop() {
133
+ try {
134
+ disconnect?.();
135
+ } catch {
136
+ /* already gone */
137
+ }
138
+ },
139
+ };
140
+ }
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
+
96
161
  export function formatDaemonTelemetry(record, reason = 'periodic') {
97
162
  const row = record && typeof record === 'object' ? record : collectDaemonTelemetry();
98
163
  return [
@@ -110,6 +175,8 @@ export function formatDaemonTelemetry(record, reason = 'periodic') {
110
175
  `queuedCalls=${row.queuedCalls}`,
111
176
  `busySessions=${row.busySessions}`,
112
177
  `busyMemoryAgents=${row.busyMemoryAgents}`,
178
+ // Present only when a major GC ran since the previous line.
179
+ ...(byteCount(row.postGcHeapMinBytes) >= 0 ? [`postGcHeapMinBytes=${row.postGcHeapMinBytes}`] : []),
113
180
  ].join(' ');
114
181
  }
115
182
 
@@ -123,8 +190,10 @@ export function createDaemonTelemetry({
123
190
  heapStatistics,
124
191
  setIntervalFn = setInterval,
125
192
  clearIntervalFn = clearInterval,
193
+ createGcTracker = createPostGcHeapTracker,
126
194
  } = {}) {
127
195
  let timer = null;
196
+ let gcTracker = null;
128
197
 
129
198
  function emit(reason = 'periodic') {
130
199
  try {
@@ -141,6 +210,8 @@ export function createDaemonTelemetry({
141
210
  heapStatistics,
142
211
  work,
143
212
  });
213
+ const postGc = gcTracker?.take() ?? -1;
214
+ if (postGc >= 0) record.postGcHeapMinBytes = postGc;
144
215
  log(formatDaemonTelemetry(record, reason));
145
216
  return record;
146
217
  } catch {
@@ -159,6 +230,7 @@ export function createDaemonTelemetry({
159
230
 
160
231
  function start(intervalMs = DAEMON_TELEMETRY_INTERVAL_MS) {
161
232
  if (timer) return timer;
233
+ gcTracker ??= createGcTracker();
162
234
  const delay = Number(intervalMs);
163
235
  const ms = Number.isFinite(delay) && delay > 0 ? delay : DAEMON_TELEMETRY_INTERVAL_MS;
164
236
  const handle = setIntervalFn(() => {
@@ -174,6 +246,8 @@ export function createDaemonTelemetry({
174
246
  if (!timer) return;
175
247
  clearIntervalFn(timer);
176
248
  timer = null;
249
+ gcTracker?.stop();
250
+ gcTracker = null;
177
251
  }
178
252
 
179
253
  return { emit, start, stop };