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
@@ -5,7 +5,12 @@
5
5
  */
6
6
  import { clean } from '../../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
7
7
  import { forgetSessionSearch } from '../../../runtime/session-search/session-search-ingest.mjs';
8
- import { listLeadSessions } from '../session-catalog.mjs';
8
+ import { leadSessionRow } from '../session-catalog.mjs';
9
+ import { getStoreDir } from '../../../runtime/agent/orchestrator/session/store/paths-heartbeat.mjs';
10
+ import {
11
+ STORED_SESSION_UNREADABLE,
12
+ _storedSessionFromFile,
13
+ } from '../../../runtime/agent/orchestrator/session/store/serialize.mjs';
9
14
  import { SESSION_ID_PATTERN } from '../shared.mjs';
10
15
  import { replacementSessionCwd } from '../../workspace/workspace-state.mjs';
11
16
 
@@ -50,6 +55,15 @@ export function createSessionDelete(deps, { cancelBackgroundTasks }) {
50
55
  }
51
56
  }
52
57
 
58
+ // Availability is one strict read of the session's own file, filtered like
59
+ // the Lead catalog (an unreadable, foreign, closed or non-Lead record is not
60
+ // deletable). No catalog rebuild, no folder scan.
61
+ function isAvailableLead(sessionId) {
62
+ const stored = _storedSessionFromFile(getStoreDir(), `${sessionId}.json`);
63
+ if (!stored || stored === STORED_SESSION_UNREADABLE || stored.closed === true) return false;
64
+ return leadSessionRow(stored, new Map()) !== null;
65
+ }
66
+
53
67
  // The deleted task's worktree: the autoCleanup setting decides (clean and
54
68
  // merged → removed; otherwise kept). Off the deletion path; never fails it.
55
69
  function releaseDeletedWorktree(sessionId) {
@@ -61,12 +75,18 @@ export function createSessionDelete(deps, { cancelBackgroundTasks }) {
61
75
  async function deleteSession(id) {
62
76
  const sessionId = clean(id);
63
77
  if (!sessionId || !SESSION_ID_PATTERN.test(sessionId)) return false;
64
- const available = listLeadSessions(mgr, { refreshFromStorage: true }).some((row) => row.id === sessionId);
65
- if (!available) return false;
78
+ if (!isAvailableLead(sessionId)) return false;
66
79
  const childIds = ownedAgentSessionIds(sessionId);
67
80
  const current = getSession();
81
+ const cleanupReason = 'desktop-session-delete';
68
82
  if (current?.id !== sessionId) {
69
- const deleted = mgr.deleteSession(sessionId) === true;
83
+ // A turn can still be running in a session that is no longer current.
84
+ // Stop its runtime first (controller abort, shells, generation bump) and
85
+ // release its lead row, agents and tasks, so nothing can write it back.
86
+ if (mgr.closeSession(sessionId, cleanupReason, { tombstone: false }) !== true) return false;
87
+ agentTool?.forgetLeadSession?.(sessionId);
88
+ releaseSessionWork(sessionId, cleanupReason);
89
+ const deleted = mgr.deleteSession(sessionId, { fenceWrites: true }) === true;
70
90
  if (!deleted) return false;
71
91
  void forgetSessionSearch(sessionId);
72
92
  // The parent is already irreversibly gone, so child cleanup is
@@ -74,7 +94,7 @@ export function createSessionDelete(deps, { cancelBackgroundTasks }) {
74
94
  // because its retained-parent proof disappeared with the parent.
75
95
  for (const childId of childIds) {
76
96
  try {
77
- mgr.deleteSession(childId);
97
+ mgr.deleteSession(childId, { fenceWrites: true });
78
98
  } catch {}
79
99
  }
80
100
  void releaseDeletedWorktree(sessionId);
@@ -83,12 +103,15 @@ export function createSessionDelete(deps, { cancelBackgroundTasks }) {
83
103
  // Leaving a worktree-bound Lead: the replacement starts in the Project
84
104
  // folder, and the worktree can go once nothing runs in it.
85
105
  const projectFolder = replacementSessionCwd(current);
86
- const cleanupReason = 'desktop-session-delete';
87
106
  releaseSessionWork(sessionId, cleanupReason);
88
107
  // Active sessions retain a tombstone until the normal sweep. Unlinking
89
108
  // immediately would let a late provider/save continuation resurrect the
90
109
  // deleted conversation after the user has moved to its replacement.
91
110
  if (mgr.closeSession(sessionId, cleanupReason, { tombstone: true }) !== true) return false;
111
+ // The unwinding turn's late saves and its idle lead-row upsert must not
112
+ // touch the deleted conversation (an unguarded save would pass the tombstone).
113
+ mgr.fenceSessionWrites?.(sessionId);
114
+ agentTool?.forgetLeadSession?.(sessionId);
92
115
  void forgetSessionSearch(sessionId);
93
116
  // Active parent deletion uses the same durable tombstone boundary for
94
117
  // every linked child. Their files then mature with the parent instead of
@@ -7,11 +7,13 @@
7
7
  */
8
8
  import { SessionClosedError } from '../../runtime/agent/orchestrator/session/manager/session-errors.mjs';
9
9
  import { forgetSessionSaveBaseline } from '../../runtime/agent/orchestrator/session/store/save-worker.mjs';
10
+ import { flushAllIndexWrites } from '../services/agent-tool/index-write-queue.mjs';
10
11
  import { unregisterLiveSession } from '../../runtime/shared/staged-update.mjs';
11
12
  import { abortRuntime, closeCanonicalSession, closeOwnSession, dispatchSessionEnd } from './teardown/session-close.mjs';
12
13
  import { settleAll, settleDetached, startRuntimeStops, startWorkStops } from './teardown/runtime-stops.mjs';
13
14
 
14
- const PROCESS_EXIT_REASON = /exit|quit|shutdown|sighup|sigint|sigterm/;
15
+ const CLOSE_ALL_DEADLINE_MS = 5000;
16
+ const PROCESS_EXIT_REASON =/exit|quit|shutdown|sighup|sigint|sigterm/;
15
17
  const WARMUP_TIMER_KEYS = [
16
18
  'providerSetupWarmupTimer',
17
19
  'providerWarmupTimer',
@@ -118,6 +120,45 @@ export function createTeardown(deps, { ingestSessionIntoMemory, closeSurfaceSess
118
120
  } catch {}
119
121
  }
120
122
 
123
+ // Agent closes that have not settled yet (one per close invocation, kept
124
+ // past the bounded wait if it overran).
125
+ const outstandingCloses = new Set();
126
+
127
+ /** Persist every queued index write. A close only ever drains: the shared
128
+ * indexes stay registered (exit hook included) for the closes still to come. */
129
+ async function drainIndexWrites() {
130
+ try {
131
+ await agentTool.flushIndexWrites?.();
132
+ await flushAllIndexWrites();
133
+ } catch {}
134
+ }
135
+
136
+ // The disposal in progress (if any). New closes wait for it before starting
137
+ // their own closeAll, so no close produces writes while registrations are
138
+ // being released.
139
+ let indexDisposal = null;
140
+
141
+ /** Release this runtime's per-runtime index registrations (exit callbacks,
142
+ * approval writer, store refs) once its drain has completed. Idempotent. A
143
+ * drain that cannot complete tears nothing down: the registrations stay until
144
+ * a later close drains successfully. Exit flushing itself is process-owned. */
145
+ function disposeIndexes(settle) {
146
+ // Assign before disposal runs (deferred), clear on settlement: a synchronous
147
+ // dispose must not clear the slot before it is assigned.
148
+ indexDisposal ??= Promise.resolve()
149
+ .then(async () => {
150
+ if (typeof agentTool.disposeAsync === 'function') await agentTool.disposeAsync({ settle });
151
+ else agentTool.dispose?.({ settle });
152
+ })
153
+ .catch(() => {
154
+ /* drain failed: registrations are kept for a later retry */
155
+ })
156
+ .finally(() => {
157
+ indexDisposal = null;
158
+ });
159
+ return indexDisposal;
160
+ }
161
+
121
162
  async function close(reason = 'cli-exit', options = {}) {
122
163
  quiesce(reason);
123
164
  const scope = teardownScope(reason, options, String(getSession()?.id || ''));
@@ -160,16 +201,40 @@ export function createTeardown(deps, { ingestSessionIntoMemory, closeSurfaceSess
160
201
  // memory and must leave workers/rows for the re-materialized owner.
161
202
  // Scoped teardown closes only THIS session's workers — the worker index
162
203
  // is shared across every Lead in the process.
204
+ let closing = null;
205
+ while (indexDisposal) await indexDisposal;
163
206
  if (scope.teardownReapsWork) {
207
+ // Every close runs its OWN scope's closeAll: a scoped close for one Lead
208
+ // never stands in for a process-wide close. Closing produces final index
209
+ // writes (settled rows, tombstones); the deadline only bounds how long
210
+ // teardown waits and does not stop the close.
211
+ const settled = () => {
212
+ outstandingCloses.delete(closing);
213
+ };
214
+ closing = new Promise((resolve) =>
215
+ resolve(agentTool.closeAll(reason, scope.scopedTeardown ? { callerSessionId: scope.closingSessionId } : {}))
216
+ ).then(settled, settled);
217
+ outstandingCloses.add(closing);
164
218
  try {
165
- agentTool.closeAll(reason, scope.scopedTeardown ? { callerSessionId: scope.closingSessionId } : {});
219
+ await withTeardownDeadline(closing, CLOSE_ALL_DEADLINE_MS, false);
166
220
  } catch {}
167
221
  }
168
- // Release the worker/lead index exit registrations (persists pending rows
169
- // first). Idempotent, so repeated closes of one runtime dispose once.
170
- try {
171
- agentTool.dispose?.({ settle: scope.teardownReapsWork === true });
172
- } catch {}
222
+ // Drain after the closes settle, then release this runtime's index
223
+ // registrations. A close that overran its deadline chains this onto its
224
+ // settlement (teardown may return before then). The live outstanding set is
225
+ // re-checked after every wait and drain, so the single disposal runs only
226
+ // when no close is outstanding; the check and the disposal start share one
227
+ // tick, and closes admitted afterwards wait for it.
228
+ const finish = async () => {
229
+ for (;;) {
230
+ while (outstandingCloses.size > 0) await Promise.allSettled([...outstandingCloses]);
231
+ await drainIndexWrites();
232
+ if (outstandingCloses.size === 0) break;
233
+ }
234
+ await disposeIndexes(scope.teardownReapsWork === true);
235
+ };
236
+ if (outstandingCloses.size > 0) void finish();
237
+ else await finish();
173
238
  const { ok, stops } = await inOwnLoopTurn(() => {
174
239
  const runtimeStops = startRuntimeStops(deps, reason, scope);
175
240
  const closed = closeOwnSession(deps, closeSurfaceSession, reason);
@@ -42,5 +42,6 @@ export function createLifecycleApi(deps) {
42
42
  resume: switching.resume,
43
43
  inheritancePreflight: inheritance.inheritancePreflight,
44
44
  inheritFrom: inheritance.inheritFrom,
45
+ inheritToNewSession: inheritance.inheritToNewSession,
45
46
  };
46
47
  }
@@ -209,19 +209,31 @@ export function createMcpConnect({
209
209
  budgetMs = Math.min(budgetMs, requestedMaxWaitMs);
210
210
  }
211
211
  }
212
- // Swallow the in-flight rejection: failures are already captured in
213
- // state.mcpFailures, and this gate must never reject the turn.
214
- const settled = Promise.resolve(inFlight).catch(() => {});
215
212
  // Budget disabled (0/off) = no per-server startup timeout, so the connect
216
213
  // promise may never settle; never gate the turn on it — fall back to the
217
214
  // legacy fire-and-forget behavior (late servers use the deferred path).
218
215
  if (!(budgetMs > 0)) return;
219
216
  let timer = null;
217
+ let expired = false;
220
218
  const budget = new Promise((resolveBudget) => {
221
- timer = setTimeout(resolveBudget, budgetMs);
219
+ timer = setTimeout(() => {
220
+ expired = true;
221
+ resolveBudget();
222
+ }, budgetMs);
222
223
  });
223
224
  try {
224
- await Promise.race([settled, budget]);
225
+ // Follow replacement connects (a reconnect queued behind the captured
226
+ // run installs a new in-flight promise) inside ONE fixed deadline.
227
+ // Failures are already captured in state.mcpFailures, so rejections are
228
+ // swallowed: this gate must never reject the turn.
229
+ let current = inFlight;
230
+ while (current && !expired) {
231
+ await Promise.race([Promise.resolve(current).catch(() => {}), budget]);
232
+ if (expired) break;
233
+ // Let a queued reconnect install its promise before re-checking.
234
+ await Promise.race([new Promise((resolveTick) => setImmediate(resolveTick)), budget]);
235
+ current = state.mcpConnectInFlight;
236
+ }
225
237
  } finally {
226
238
  if (timer) clearTimeout(timer);
227
239
  }
@@ -20,7 +20,7 @@ import {
20
20
  sessionRouteCommitted,
21
21
  shouldRecreateEmptySessionForRouteChange,
22
22
  } from '../../runtime/agent/orchestrator/runtime-core/session-route-policy.mjs';
23
- import { rebuildDeferredToolSurfaceForProvider } from '../../runtime/agent/orchestrator/runtime-core/tool-catalog.mjs';
23
+ import { rebuildToolSurfaceForProviderWithMigration } from '../../runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs';
24
24
 
25
25
  /** The request, completed from the live route where the caller left gaps. */
26
26
  function completeRequest(deps, next) {
@@ -114,7 +114,7 @@ function updateSessionInPlace(deps, session, updateOptions) {
114
114
  const updated = mgr.updateSessionRoute?.(session.id, fields, updateOptions);
115
115
  if (updated) setSession(updated);
116
116
  else Object.assign(session, fields);
117
- rebuildDeferredToolSurfaceForProvider(getSession(), route.provider);
117
+ rebuildToolSurfaceForProviderWithMigration(getSession(), route.provider);
118
118
  writeStatuslineRoute(statusRoutes, getSession(), route);
119
119
  }
120
120
 
@@ -0,0 +1,51 @@
1
+ // Per-session sink for usage spent by native web-search sends. The search runs
2
+ // as its own provider send under a derived session id, so the main stream
3
+ // never reports it; a turn's caller opts in with `onNativeWebSearchUsage` and
4
+ // receives the same delta shape `onUsageDelta` carries.
5
+ //
6
+ // Keyed by the owning session id, not async-local state: tools can execute
7
+ // from stream callbacks whose async context belongs to a pooled transport
8
+ // connection, not to the ask that is waiting on them.
9
+ //
10
+ // Each registration is its own entry that only its own release removes, so
11
+ // overlapping turns on one session id never restore or drop each other. The
12
+ // session lock serializes turns, so the most recent live entry belongs to the
13
+ // turn that is running; callers register once their turn owns the session.
14
+ import { usageDeltaEvent } from '../runtime/agent/orchestrator/session/loop/usage.mjs';
15
+
16
+ const entriesBySession = new Map();
17
+
18
+ /** Adds an owned sink entry for `sessionId`; the returned release removes exactly that entry. */
19
+ export function registerNativeWebSearchUsageSink(sessionId, sink) {
20
+ if (typeof sink !== 'function' || !sessionId) return () => {};
21
+ const entry = { sink };
22
+ let entries = entriesBySession.get(sessionId);
23
+ if (!entries) {
24
+ entries = [];
25
+ entriesBySession.set(sessionId, entries);
26
+ }
27
+ entries.push(entry);
28
+ return () => {
29
+ const live = entriesBySession.get(sessionId);
30
+ const index = live ? live.indexOf(entry) : -1;
31
+ if (index === -1) return;
32
+ live.splice(index, 1);
33
+ if (live.length === 0) entriesBySession.delete(sessionId);
34
+ };
35
+ }
36
+
37
+ export function reportNativeWebSearchUsage({ ownerSessionId, sessionId, requestedModel, model, usage }) {
38
+ const entries = entriesBySession.get(ownerSessionId);
39
+ const sink = entries?.[entries.length - 1]?.sink;
40
+ if (!sink || !usage) return;
41
+ try {
42
+ const pending = sink({
43
+ ...usageDeltaEvent({ sessionId, iterationIndex: 0, requestedModel, model, usage }),
44
+ source: 'native_web_search',
45
+ agentRole: 'web-search',
46
+ });
47
+ if (typeof pending?.then === 'function') pending.then(undefined, () => {});
48
+ } catch {
49
+ /* telemetry must never fail the search */
50
+ }
51
+ }
@@ -13,6 +13,7 @@ import {
13
13
  normalizeWebSearchAllowedDomain,
14
14
  } from './native-web-search/tool-shape.mjs';
15
15
  import { createWebSearchRouteCandidates } from './native-web-search/routes.mjs';
16
+ import { reportNativeWebSearchUsage } from './native-web-search-usage.mjs';
16
17
 
17
18
  export function createNativeWebSearch(deps) {
18
19
  const {
@@ -58,10 +59,11 @@ export function createNativeWebSearch(deps) {
58
59
  }
59
60
  const family = webSearchProviderFamily(candidate.provider);
60
61
  const startedAt = Date.now();
62
+ const sendSessionId = `${getSession()?.id || 'web_search'}:native-web-search:${Date.now().toString(36)}`;
61
63
  const result = await providerImpl.send(nativeWebSearchMessages(webSearchArgs), candidate.model, undefined, {
62
64
  signal,
63
65
  role: 'web-search',
64
- sessionId: `${getSession()?.id || 'web_search'}:native-web-search:${Date.now().toString(36)}`,
66
+ sessionId: sendSessionId,
65
67
  sourceType: 'native-web-search',
66
68
  sourceName: 'web_search',
67
69
  nativeTools: [toolFor(webSearchArgs, toolType, candidate.provider)],
@@ -73,6 +75,13 @@ export function createNativeWebSearch(deps) {
73
75
  onStageChange: () => {},
74
76
  onStreamDelta: () => {},
75
77
  });
78
+ reportNativeWebSearchUsage({
79
+ ownerSessionId: getSession()?.id,
80
+ sessionId: sendSessionId,
81
+ requestedModel: candidate.model,
82
+ model: result?.model || candidate.model,
83
+ usage: result?.usage,
84
+ });
76
85
  return {
77
86
  content: String(result?.content || '').trim(),
78
87
  provider: candidate.provider,
@@ -12,7 +12,6 @@ export function createNewSessionConfig({
12
12
  initialRouteExplicit = false,
13
13
  invalidatePreSessionToolSurface,
14
14
  invalidateOutputStyleStatusCache,
15
- invalidateSkills,
16
15
  connectConfiguredMcp,
17
16
  configureEmbedding,
18
17
  }) {
@@ -33,7 +32,10 @@ export function createNewSessionConfig({
33
32
  }
34
33
  invalidateOutputStyleStatusCache();
35
34
  invalidatePreSessionToolSurface();
36
- invalidateSkills();
35
+ // The installed-skill list is NOT dropped here: its cache is change-gated
36
+ // (every skill root's mtime signature, kept by change watchers), so a new
37
+ // session already sees any skill edit. Clearing it made every session
38
+ // creation re-read and re-parse every SKILL.md on the event loop.
37
39
  await configureEmbedding(sharedCfgMod.readSection('memory')?.embedding || {});
38
40
  if (!firstSession || !isDeepStrictEqual(previousMcp, mcpSettings())) {
39
41
  // Reused runtimes may still own old connections. This reset is scoped to
@@ -12,6 +12,7 @@ import {
12
12
  removeCustomProvider,
13
13
  updateCustomProviderModel,
14
14
  testCustomProvider,
15
+ checkCustomProviderKeyEnv,
15
16
  discoverCustomProviderModels,
16
17
  } from '../services/provider-admin.mjs';
17
18
 
@@ -96,6 +97,9 @@ export function createLoginApi({ cfgMod, awaitKeychainPrewarm, reloadFullConfig,
96
97
  await awaitKeychainPrewarm();
97
98
  return await testCustomProvider(cfgMod, input);
98
99
  },
100
+ checkCustomProviderKeyEnv(name) {
101
+ return checkCustomProviderKeyEnv(name);
102
+ },
99
103
  async discoverCustomProviderModels(input) {
100
104
  await awaitKeychainPrewarm();
101
105
  return await discoverCustomProviderModels(cfgMod, input);
@@ -14,8 +14,13 @@ function listOf(status, key) {
14
14
  }
15
15
 
16
16
  export function createScopeDecorators({ getConfig, getCurrentCwd }) {
17
- function scopeInfo(kind, name, pluginId = '', cwd = getCurrentCwd()) {
18
- const scopes = extensionScopesFromConfig(getConfig());
17
+ function scopeInfo(
18
+ kind,
19
+ name,
20
+ pluginId = '',
21
+ cwd = getCurrentCwd(),
22
+ scopes = extensionScopesFromConfig(getConfig())
23
+ ) {
19
24
  const scope = extensionScopeProjects(scopes, kind, name);
20
25
  const inheritedScope = pluginId ? extensionScopeProjects(scopes, 'plugins', pluginId) : null;
21
26
  const activeHere =
@@ -35,11 +40,13 @@ export function createScopeDecorators({ getConfig, getCurrentCwd }) {
35
40
  },
36
41
  /** `cwd`: decide `activeHere` for that folder instead of the current one. */
37
42
  skills(status, cwd = getCurrentCwd()) {
43
+ // One scope read for the whole list, not one per skill.
44
+ const scopes = extensionScopesFromConfig(getConfig());
38
45
  return {
39
46
  ...status,
40
47
  skills: listOf(status, 'skills').map((skill) => ({
41
48
  ...skill,
42
- ...scopeInfo('skills', skill.name, skill?.owner?.kind === 'plugin' ? clean(skill.owner.id) : '', cwd),
49
+ ...scopeInfo('skills', skill.name, skill?.owner?.kind === 'plugin' ? clean(skill.owner.id) : '', cwd, scopes),
43
50
  })),
44
51
  };
45
52
  },
@@ -3,8 +3,10 @@ import { turnReviewReply } from '../runtime/shared/turn-review-reply.mjs';
3
3
  import {
4
4
  clearSessionChangeHistory,
5
5
  getSessionReviewDiff,
6
+ getSessionReviewFilePatch,
6
7
  getTurnChanges,
7
8
  getTurnReviewDiff,
9
+ getTurnReviewFilePatch,
8
10
  reapplyTurnChanges,
9
11
  revertTurnChanges,
10
12
  revertTurnReview,
@@ -24,8 +26,9 @@ registerSessionPurgeHook((sessionId) => {
24
26
  });
25
27
 
26
28
  // Tool writes (apply_patch, mutating shell commands, git, watcher events)
27
- // reach the live review snapshot as its change signal: a review poll right
28
- // after them captures the worktree, an unsignaled poll may reuse the last one.
29
+ // reach the turn's worktree snapshot as its change signal: a session review
30
+ // read right after them captures the worktree, an unsignaled one may reuse
31
+ // the last capture.
29
32
  registerCacheInvalidationListener((paths) => noteTurnWorktreeChange(paths));
30
33
 
31
34
  export function createRuntimeReviewApi({ getCwd, getSessionId }) {
@@ -35,6 +38,9 @@ export function createRuntimeReviewApi({ getCwd, getSessionId }) {
35
38
  getTurnReviewDiff: (options = {}) =>
36
39
  turnReviewReply((reviewOptions) => getTurnReviewDiff(getCwd(), getSessionId(), reviewOptions), options),
37
40
  getSessionReviewDiff: () => getSessionReviewDiff(getCwd(), getSessionId()),
41
+ // One listed file's whole patch (a reply lists a large one `patchOmitted`).
42
+ getSessionReviewFilePatch: (file, oldPath) => getSessionReviewFilePatch(getCwd(), getSessionId(), file, oldPath),
43
+ getTurnReviewFilePatch: (file) => getTurnReviewFilePatch(getCwd(), getSessionId(), file),
38
44
  revertTurnReview: (checkpointId) => revertTurnReview(getCwd(), getSessionId(), checkpointId),
39
45
  revertTurnReviewFile: (file, checkpointId) => revertTurnReviewFile(getCwd(), getSessionId(), file, checkpointId),
40
46
  // Historical per-turn changes, addressed by checkpointId (TurnChangesReply).
@@ -20,6 +20,7 @@ import { registerJsonProjection } from '../../../runtime/shared/pending-json-pro
20
20
  import { sleep } from '../../../runtime/shared/sleep.mjs';
21
21
 
22
22
  const RETRY_DELAYS_MS = [50, 250, 1000];
23
+ const RETRY_AFTER_FAILURE_MS = 15_000;
23
24
  const unsettled = new Set(); // queues holding unpersisted writes
24
25
  // Exit is the one place a bounded synchronous lock wait is acceptable: the
25
26
  // loop is gone, and a dropped settlement would leave rows "running" forever.
@@ -122,8 +123,15 @@ function replayBatch(batch, args, mergeSet) {
122
123
  replayRun();
123
124
  }
124
125
 
126
+ // A write that cannot reach disk loses a state change other processes rely on
127
+ // (e.g. a deleted Lead row that never leaves the pool), so every failure is
128
+ // reported. In the daemon stderr IS daemon.log (daemon-log.mjs installRedirect).
125
129
  function reportFailure(file, what, error) {
126
- process.stderr.write(`[index-write] ${file}: ${what}: ${error?.message || error}\n`);
130
+ try {
131
+ process.stderr.write(`[index-write] ${file}: ${what}: ${error?.message || error}\n`);
132
+ } catch {
133
+ /* logging is best effort */
134
+ }
127
135
  }
128
136
 
129
137
  /**
@@ -151,9 +159,15 @@ export function createIndexWriteQueue({
151
159
  let pending = []; // op sets not yet handed to a job
152
160
  let inflight = []; // op sets of the running job
153
161
  let running = null;
162
+ const failures = new WeakMap(); // settled job → the error that failed it
154
163
  let timer = null;
164
+ let retryTimer = null;
155
165
 
156
166
  function clearTimer() {
167
+ if (retryTimer) {
168
+ clearTimeout(retryTimer);
169
+ retryTimer = null;
170
+ }
157
171
  if (!timer) return;
158
172
  clearImmediate(timer);
159
173
  timer = null;
@@ -173,8 +187,8 @@ export function createIndexWriteQueue({
173
187
  return;
174
188
  } catch (error) {
175
189
  if (attempt >= RETRY_DELAYS_MS.length) {
176
- reportFailure(file, `dropped ${batch.length} unpersisted write(s)`, error);
177
- return;
190
+ reportFailure(file, `${batch.length} unpersisted write(s) kept queued after retries`, error);
191
+ throw error;
178
192
  }
179
193
  await sleep(RETRY_DELAYS_MS[attempt]);
180
194
  }
@@ -187,23 +201,45 @@ export function createIndexWriteQueue({
187
201
  inflight = pending;
188
202
  pending = [];
189
203
  const batch = inflight;
190
- running = persist(batch).then(() => {
191
- inflight = [];
192
- running = null;
193
- onPersisted();
194
- if (pending.length === 0) {
195
- projection = null;
196
- unsettled.delete(self);
204
+ const job = persist(batch).then(
205
+ () => {
206
+ inflight = [];
207
+ running = null;
208
+ onPersisted();
209
+ if (pending.length === 0) {
210
+ projection = null;
211
+ unsettled.delete(self);
212
+ }
213
+ schedule();
214
+ },
215
+ (error) => {
216
+ // Persistent failure: nothing reached disk. Requeue the batch ahead of
217
+ // newer writes (the projection and `unsettled` keep covering it) so a
218
+ // later flush, write or exit flush retries. Do not reschedule at once,
219
+ // a held foreign lock would otherwise be hammered; a slow unref'd retry
220
+ // keeps a failed LAST write (a delete) from waiting for unrelated traffic.
221
+ failures.set(job, error);
222
+ pending = [...batch, ...pending];
223
+ inflight = [];
224
+ running = null;
225
+ retryTimer = setTimeout(() => {
226
+ retryTimer = null;
227
+ schedule();
228
+ }, RETRY_AFTER_FAILURE_MS);
229
+ retryTimer.unref?.();
197
230
  }
198
- schedule();
199
- });
231
+ );
232
+ running = job;
200
233
  }
201
234
 
202
- /** Resolves once everything written so far is on disk (or dropped). */
235
+ /** Resolves once everything written so far is on disk; rejects when a persist
236
+ * failed for good (the writes stay queued for a later flush). */
203
237
  async function flush() {
204
238
  for (;;) {
205
239
  if (running) {
206
- await running;
240
+ const job = running;
241
+ await job;
242
+ if (failures.has(job)) throw failures.get(job);
207
243
  continue;
208
244
  }
209
245
  if (pending.length === 0) return;
@@ -212,10 +248,16 @@ export function createIndexWriteQueue({
212
248
  }
213
249
  }
214
250
 
215
- /** Exit path: persist inflight + pending ops synchronously (bounded lock wait). */
251
+ /** Persist pending ops synchronously (bounded lock wait). While an async job
252
+ * runs it owns the lock (and its ops are in flight): never wait on it, take
253
+ * over its lock, or replay its ops — report false and let that job and its
254
+ * follow-ups drain. At the last-resort process 'exit' hook that is
255
+ * best-effort: the graceful shutdown path drains before exit. */
216
256
  function flushSync() {
217
- const ops = [...inflight, ...pending];
218
- if (!file || ops.length === 0) return true;
257
+ if (!file) return true;
258
+ if (running) return false;
259
+ const ops = pending;
260
+ if (ops.length === 0) return true;
219
261
  clearTimer();
220
262
  pending = [];
221
263
  try {
@@ -225,10 +267,10 @@ export function createIndexWriteQueue({
225
267
  });
226
268
  } catch (error) {
227
269
  reportFailure(file, `synchronous flush of ${ops.length} write(s) failed; kept queued`, error);
228
- pending = ops;
270
+ pending = [...ops, ...pending];
271
+ schedule();
229
272
  return false;
230
273
  }
231
- inflight = [];
232
274
  projection = null;
233
275
  unsettled.delete(self);
234
276
  onPersisted();
@@ -251,9 +293,11 @@ export function createIndexWriteQueue({
251
293
  version += 1;
252
294
  pending.push(rerun ? mutator : ops);
253
295
  unsettled.add(self);
296
+ installProcessExitFlush();
254
297
  schedule();
255
298
  return next;
256
- } catch {
299
+ } catch (error) {
300
+ reportFailure(file, 'write rejected (mutator or document rewrite threw); nothing queued', error);
257
301
  return null;
258
302
  }
259
303
  },
@@ -266,6 +310,23 @@ export function createIndexWriteQueue({
266
310
  };
267
311
  }
268
312
 
313
+ let processExitFlushInstalled = false;
314
+
315
+ /** The process owns last-resort exit flushing of every queue holding unpersisted
316
+ * writes, so a disposed (e.g. evicted) runtime needs no exit callback of its
317
+ * own. Best effort: a persist still running is left to the graceful drain. */
318
+ function installProcessExitFlush() {
319
+ if (processExitFlushInstalled) return;
320
+ processExitFlushInstalled = true;
321
+ process.on('exit', () => {
322
+ try {
323
+ flushAllIndexWritesSync();
324
+ } catch {
325
+ /* exit flush is best effort */
326
+ }
327
+ });
328
+ }
329
+
269
330
  /** Synchronous variant (exit paths, tests that read the file directly). */
270
331
  export function flushAllIndexWritesSync() {
271
332
  for (const queue of [...unsettled]) queue.flushSync();
@@ -0,0 +1,29 @@
1
+ // Ids of deleted Leads, shared by every lead-index instance of the process that
2
+ // uses one data dir. The delete runs in the control runtime while the deleted
3
+ // session's own (still loaded) runtime keeps settling turns through ITS index
4
+ // instance; a per-instance set never sees the delete, so that late idle upsert
5
+ // wrote the row back. The store's write fence (explicit deletes) is consulted
6
+ // as well, so a fenced id can never be added whichever instance asks.
7
+ import { resolve } from 'node:path';
8
+ import { isSessionWriteFenced } from '../../../../runtime/agent/orchestrator/session/store/live-state.mjs';
9
+
10
+ const FORGOTTEN_LEADS_MAX = 1024;
11
+ const registries = new Map(); // resolved data dir -> Set<sessionId>
12
+
13
+ export function createForgottenLeads(dataDir) {
14
+ const key = dataDir ? resolve(dataDir) : '';
15
+ let ids = registries.get(key);
16
+ if (!ids) {
17
+ ids = new Set();
18
+ registries.set(key, ids);
19
+ }
20
+ return {
21
+ /** A deleted Lead must never be written to the index again. */
22
+ forget(sessionId) {
23
+ ids.delete(sessionId);
24
+ ids.add(sessionId);
25
+ if (ids.size > FORGOTTEN_LEADS_MAX) ids.delete(ids.values().next().value);
26
+ },
27
+ isForgotten: (sessionId) => ids.has(sessionId) || isSessionWriteFenced(sessionId),
28
+ };
29
+ }
@@ -42,7 +42,7 @@ export function createLeadIndexFile({ dataDir }) {
42
42
  return parsed ? normalizeLeadRows(parsed) : [];
43
43
  }
44
44
 
45
- const write = (mutator) => queue.write(mutator);
45
+ const write = (mutator, options) => queue.write(mutator, options);
46
46
 
47
47
  return { path, read, write, flush: queue.flush, flushSync: queue.flushSync };
48
48
  }