mixdog 1.2.3 → 1.2.4

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 (465) hide show
  1. package/package.json +1 -1
  2. package/scripts/gate-local.mjs +6 -4
  3. package/scripts/lib/gate-scheduler.mjs +29 -11
  4. package/scripts/probe-provider-catalog.mjs +31 -0
  5. package/scripts/provider-pingpong-live.mjs +128 -38
  6. package/scripts/session-sweep.mjs +5 -25
  7. package/src/agents/maintainer/AGENT.md +1 -1
  8. package/src/agents/maintainer/agent.json +1 -1
  9. package/src/agents/reviewer/AGENT.md +2 -4
  10. package/src/agents/security/AGENT.md +1 -2
  11. package/src/agents/writer/AGENT.md +2 -3
  12. package/src/defaults/shipped-history.json +6 -1
  13. package/src/defaults/skills/image/SKILL.md +21 -23
  14. package/src/lib/keychain-cjs.cjs +13 -11
  15. package/src/rules/agent/AGENT.md +7 -5
  16. package/src/rules/lead/LEAD.md +7 -9
  17. package/src/rules/shared/45-destructive.md +4 -0
  18. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
  19. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +28 -7
  20. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -11
  21. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +101 -23
  22. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +2 -0
  23. package/src/runtime/agent/orchestrator/providers/anthropic-api-key-fetch.mjs +36 -0
  24. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +1 -1
  25. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -3
  26. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  27. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +26 -44
  28. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +2 -5
  29. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +4 -0
  30. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +41 -12
  31. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +13 -1
  32. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +81 -10
  33. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +109 -5
  34. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +2 -1
  35. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +107 -37
  36. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +4 -1
  37. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +3 -1
  38. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  39. package/src/runtime/agent/orchestrator/providers/cursor-native-context.mjs +48 -0
  40. package/src/runtime/agent/orchestrator/providers/cursor-wire-exec.mjs +144 -34
  41. package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +28 -4
  42. package/src/runtime/agent/orchestrator/providers/cursor-wire-request.mjs +8 -4
  43. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +41 -11
  44. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +67 -5
  45. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +14 -4
  46. package/src/runtime/agent/orchestrator/providers/cursor.mjs +14 -1
  47. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +9 -6
  48. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -2
  49. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +26 -4
  50. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +12 -6
  51. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +14 -6
  52. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +4 -4
  53. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +1 -0
  54. package/src/runtime/agent/orchestrator/providers/gemini-rest-stream/watchdogs.mjs +16 -5
  55. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +6 -1
  56. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +22 -10
  57. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -2
  58. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +33 -7
  59. package/src/runtime/agent/orchestrator/providers/gemini.mjs +11 -2
  60. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  61. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +9 -2
  62. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +9 -5
  63. package/src/runtime/agent/orchestrator/providers/lib/request-compression.mjs +86 -0
  64. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +4 -2
  65. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -5
  66. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +7 -1
  67. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +60 -16
  68. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +11 -13
  69. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +30 -19
  70. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +5 -0
  72. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +42 -5
  73. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +13 -9
  74. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +1 -3
  75. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +18 -7
  76. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +36 -3
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +51 -3
  78. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +11 -1
  79. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +64 -52
  80. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +3 -1
  81. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +2 -1
  82. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +7 -1
  83. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +1 -4
  84. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +0 -1
  85. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +7 -6
  86. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +13 -2
  87. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +4 -1
  88. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +1 -2
  89. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +5 -4
  90. package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -1
  91. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +2 -1
  92. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +11 -4
  93. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +14 -4
  94. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +15 -3
  95. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +6 -2
  96. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +8 -6
  97. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +4 -1
  98. package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +30 -2
  99. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +3 -2
  100. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -3
  101. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +3 -1
  102. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge-contract.mjs +6 -0
  103. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +10 -10
  104. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +3 -1
  105. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +46 -8
  106. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -8
  107. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -2
  108. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +24 -0
  109. package/src/runtime/agent/orchestrator/session/cache/tool-result-reuse.mjs +10 -2
  110. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -3
  111. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -11
  112. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +6 -22
  113. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +2 -0
  114. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +16 -2
  115. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +28 -0
  116. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  117. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +0 -6
  118. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +0 -6
  119. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +1 -1
  120. package/src/runtime/agent/orchestrator/session/historical-session-closure.mjs +10 -0
  121. package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +2 -5
  122. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +2 -4
  123. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +3 -3
  124. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +0 -27
  125. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +1 -1
  126. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -10
  127. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +2 -9
  128. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +4 -1
  129. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +4 -1
  130. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +1 -4
  131. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +76 -0
  132. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +5 -1
  133. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +8 -9
  134. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +6 -0
  135. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -2
  136. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +1 -4
  137. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -4
  138. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +33 -3
  139. package/src/runtime/agent/orchestrator/session/manager/ask-support.mjs +0 -4
  140. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +21 -1
  141. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +38 -13
  142. package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +33 -0
  143. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +5 -0
  144. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +19 -1
  145. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +57 -13
  146. package/src/runtime/agent/orchestrator/session/manager/failed-turn-rewind.mjs +16 -0
  147. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +6 -120
  148. package/src/runtime/agent/orchestrator/session/manager/pending-persist-queue.mjs +1 -1
  149. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +29 -8
  150. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +42 -49
  151. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +50 -0
  152. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +32 -14
  153. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +1 -1
  154. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +17 -33
  155. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +29 -13
  156. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -3
  157. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +45 -0
  158. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -9
  159. package/src/runtime/agent/orchestrator/session/send-instrumentation.mjs +2 -1
  160. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -2
  161. package/src/runtime/agent/orchestrator/session/side-question.mjs +12 -7
  162. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +5 -12
  163. package/src/runtime/agent/orchestrator/session/store/lifecycle-read.mjs +1 -1
  164. package/src/runtime/agent/orchestrator/session/store/listing.mjs +2 -6
  165. package/src/runtime/agent/orchestrator/session/store/live-cache-eviction.mjs +1 -1
  166. package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +46 -37
  167. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +1 -1
  168. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +1 -1
  169. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +5 -11
  170. package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +59 -0
  171. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +1 -1
  172. package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +1 -1
  173. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +27 -32
  174. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +0 -8
  175. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +6 -18
  176. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +33 -106
  177. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +1 -1
  178. package/src/runtime/agent/orchestrator/session/store-agent-worker-pool.mjs +5 -8
  179. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +136 -196
  180. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +50 -116
  181. package/src/runtime/agent/orchestrator/session/store-summary-locations.mjs +3 -29
  182. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +1 -1
  183. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +7 -8
  184. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +9 -20
  185. package/src/runtime/agent/orchestrator/session/store.mjs +3 -16
  186. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +11 -12
  187. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +37 -13
  188. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +15 -4
  189. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +4 -2
  190. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +72 -15
  191. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +45 -0
  192. package/src/runtime/agent/orchestrator/stall-policy.mjs +50 -0
  193. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +5 -1
  194. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +6 -2
  195. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +204 -10
  196. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +160 -39
  197. package/src/runtime/agent/orchestrator/tools/builtin/git-partial-stage.mjs +7 -5
  198. package/src/runtime/agent/orchestrator/tools/builtin/grep-formatting.mjs +14 -5
  199. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +4 -3
  200. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-output.mjs +2 -1
  201. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-path-fanout.mjs +48 -44
  202. package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +10 -3
  203. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +200 -17
  204. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +5 -4
  205. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +6 -1
  206. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +20 -0
  207. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +101 -2
  208. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
  209. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +67 -0
  210. package/src/runtime/agent/orchestrator/tools/lib/process-table-fixture.mjs +50 -0
  211. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +303 -63
  212. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +2 -0
  213. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +108 -18
  214. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +77 -11
  215. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +81 -5
  216. package/src/runtime/attachments/store.d.mts +9 -0
  217. package/src/runtime/attachments/store.mjs +80 -9
  218. package/src/runtime/browser-bridge/browser-action-contract.d.mts +0 -1
  219. package/src/runtime/browser-bridge/browser-action-contract.mjs +0 -2
  220. package/src/runtime/computer-bridge/action-schema.mjs +178 -178
  221. package/src/runtime/computer-bridge/linux-readiness.d.mts +0 -2
  222. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -16
  223. package/src/runtime/effort-judge/judge-client.mjs +21 -4
  224. package/src/runtime/effort-judge/judge-worker.mjs +4 -1
  225. package/src/runtime/github/tool.mjs +118 -17
  226. package/src/runtime/media/adapters/codex-image.mjs +140 -21
  227. package/src/runtime/media/catalog.mjs +4 -4
  228. package/src/runtime/media/chatgpt-usage.mjs +55 -0
  229. package/src/runtime/media/defaults.mjs +37 -0
  230. package/src/runtime/media/jobs.mjs +12 -2
  231. package/src/runtime/media/lanes.mjs +12 -0
  232. package/src/runtime/media/tool-defs.mjs +15 -4
  233. package/src/runtime/media/tool.mjs +41 -12
  234. package/src/runtime/media/upstream-error.mjs +72 -6
  235. package/src/runtime/memory/lib/ingest/transcript-rows.mjs +1 -0
  236. package/src/runtime/memory/lib/pg/process.mjs +5 -7
  237. package/src/runtime/memory/lib/pg/supervisor.mjs +4 -1
  238. package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
  239. package/src/runtime/office/authoring/html-browser.mjs +18 -8
  240. package/src/runtime/office/authoring/pdf-html-runner.mjs +1 -1
  241. package/src/runtime/office/authoring/pptx-html-measure.mjs +1 -1
  242. package/src/runtime/office/authoring/xlsx-html-runner.mjs +1 -1
  243. package/src/runtime/office/core/office-write-ledger.mjs +1 -5
  244. package/src/runtime/office/index.mjs +2 -3
  245. package/src/runtime/office/pdf/pdf-analysis.mjs +0 -68
  246. package/src/runtime/session-search/session-search-ingest.mjs +1 -1
  247. package/src/runtime/shared/agent-reap-state.mjs +13 -0
  248. package/src/runtime/shared/atomic-file.d.mts +1 -1
  249. package/src/runtime/shared/background-tasks.mjs +6 -1
  250. package/src/runtime/shared/bounded-line-diff.mjs +11 -2
  251. package/src/runtime/shared/browser-launch.mjs +15 -0
  252. package/src/runtime/shared/err-text.mjs +42 -4
  253. package/src/runtime/shared/file-lock.mjs +6 -5
  254. package/src/runtime/shared/llm/anthropic-betas.mjs +1 -2
  255. package/src/runtime/shared/llm/provider-model-identities.mjs +1 -0
  256. package/src/runtime/shared/model-route-chain.d.mts +8 -0
  257. package/src/runtime/shared/model-route-chain.mjs +48 -0
  258. package/src/runtime/shared/model-route-fallback.mjs +22 -0
  259. package/src/runtime/shared/pid-liveness.mjs +12 -0
  260. package/src/runtime/shared/provider-api-key.mjs +6 -0
  261. package/src/runtime/shared/provider-timeout-phase.mjs +17 -0
  262. package/src/runtime/shared/quiet-progress.d.mts +35 -0
  263. package/src/runtime/shared/quiet-progress.mjs +101 -0
  264. package/src/runtime/shared/quota-resume-store.mjs +32 -1
  265. package/src/runtime/shared/read-row-numbers.mjs +44 -0
  266. package/src/runtime/shared/remote-intent.mjs +2 -42
  267. package/src/runtime/shared/slow-span.mjs +8 -1
  268. package/src/runtime/shared/statusline/statusline-segments.mjs +24 -2
  269. package/src/runtime/shared/tool-card-model/card-header.mjs +2 -1
  270. package/src/runtime/shared/tool-output-budget.mjs +2 -0
  271. package/src/runtime/shared/tool-result-summary.mjs +16 -2
  272. package/src/runtime/shared/tool-status.mjs +3 -2
  273. package/src/runtime/shared/tool-surface.mjs +2 -3
  274. package/src/runtime/shared/tool-work-units.mjs +22 -15
  275. package/src/runtime/shared/transcript-metadata.mjs +49 -1
  276. package/src/runtime/shared/transcript-turn-keys.mjs +33 -0
  277. package/src/runtime/shared/transcript-writer/transcript-file.mjs +10 -1
  278. package/src/runtime/shared/transcript-writer.mjs +14 -4
  279. package/src/runtime/shared/turn-changes.mjs +2 -1
  280. package/src/runtime/shared/turn-snapshot.mjs +11 -3
  281. package/src/runtime/shared/worktrees/activity-gate.mjs +175 -0
  282. package/src/runtime/shared/worktrees/branch-management.mjs +179 -0
  283. package/src/runtime/shared/worktrees/carry.mjs +6 -4
  284. package/src/runtime/shared/worktrees/contract.mjs +27 -1
  285. package/src/runtime/shared/worktrees/dependencies.mjs +456 -0
  286. package/src/runtime/shared/worktrees/file-recovery.mjs +29 -13
  287. package/src/runtime/shared/worktrees/git-head.mjs +22 -13
  288. package/src/runtime/shared/worktrees/include.mjs +125 -7
  289. package/src/runtime/shared/worktrees/lock.mjs +1 -1
  290. package/src/runtime/shared/worktrees/maintenance.mjs +20 -2
  291. package/src/runtime/shared/worktrees/manager.mjs +378 -32
  292. package/src/runtime/shared/worktrees/owner.mjs +2 -1
  293. package/src/runtime/shared/worktrees/preparation-report.mjs +79 -0
  294. package/src/runtime/shared/worktrees/setup-commands.mjs +232 -0
  295. package/src/runtime/shared/worktrees/workspace-visibility.mjs +17 -0
  296. package/src/runtime/terminal-bridge/client.mjs +14 -10
  297. package/src/session-runtime/boot/apis.mjs +7 -1
  298. package/src/session-runtime/boot/config.mjs +2 -0
  299. package/src/session-runtime/boot/facade.mjs +3 -0
  300. package/src/session-runtime/boot/session.mjs +3 -2
  301. package/src/session-runtime/boot/tools.mjs +3 -0
  302. package/src/session-runtime/conversation-import/service.mjs +1 -1
  303. package/src/session-runtime/goal-facade-api.mjs +2 -2
  304. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +44 -18
  305. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +4 -1
  306. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +22 -8
  307. package/src/session-runtime/internal-tool-executor.mjs +2 -0
  308. package/src/session-runtime/lifecycle/cold-heir.mjs +0 -1
  309. package/src/session-runtime/lifecycle/session-catalog.mjs +14 -16
  310. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +18 -53
  311. package/src/session-runtime/lifecycle/session-switch.mjs +1 -1
  312. package/src/session-runtime/lifecycle/shared.mjs +5 -6
  313. package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -5
  314. package/src/session-runtime/lifecycle/teardown.mjs +7 -16
  315. package/src/session-runtime/media-api.mjs +8 -0
  316. package/src/session-runtime/model-route/route-ownership.mjs +60 -0
  317. package/src/session-runtime/model-route/route-persist.mjs +3 -2
  318. package/src/session-runtime/model-route/set-route.mjs +114 -34
  319. package/src/session-runtime/model-route/web-search-route.mjs +7 -2
  320. package/src/session-runtime/model-route-api.mjs +4 -2
  321. package/src/session-runtime/native-web-search/routes.mjs +37 -24
  322. package/src/session-runtime/native-web-search.mjs +9 -8
  323. package/src/session-runtime/provider-auth/setup.mjs +4 -0
  324. package/src/session-runtime/provider-models/catalog-load.mjs +9 -0
  325. package/src/session-runtime/provider-models/web-search-catalog.mjs +23 -3
  326. package/src/session-runtime/provider-models.mjs +1 -0
  327. package/src/session-runtime/provider-usage.mjs +4 -0
  328. package/src/session-runtime/route-resolution.mjs +3 -1
  329. package/src/session-runtime/runtime-core.mjs +4 -0
  330. package/src/session-runtime/runtime-facade.mjs +6 -0
  331. package/src/session-runtime/services/agent-task-status.mjs +2 -0
  332. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +0 -3
  333. package/src/session-runtime/services/agent-tool/close-flow.mjs +6 -7
  334. package/src/session-runtime/services/agent-tool/execute-send.mjs +2 -2
  335. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +36 -3
  336. package/src/session-runtime/services/agent-tool/execute.mjs +9 -1
  337. package/src/session-runtime/services/agent-tool/helpers.mjs +30 -7
  338. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +7 -2
  339. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +6 -3
  340. package/src/session-runtime/services/agent-tool/lead-worker-index/lead-rows.mjs +3 -4
  341. package/src/session-runtime/services/agent-tool/lead-worker-index/reap-timers.mjs +9 -3
  342. package/src/session-runtime/services/agent-tool/lead-worker-index/row-settlement.mjs +7 -18
  343. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +9 -6
  344. package/src/session-runtime/services/agent-tool/model-select.mjs +82 -0
  345. package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -6
  346. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +3 -1
  347. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -5
  348. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -4
  349. package/src/session-runtime/services/agent-tool/spawn-flow.mjs +2 -1
  350. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
  351. package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +1 -3
  352. package/src/session-runtime/services/agent-tool/tag-registry/tag-refresh.mjs +1 -3
  353. package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -9
  354. package/src/session-runtime/services/agent-tool/terminal-reaper/scan-admission.mjs +8 -41
  355. package/src/session-runtime/services/agent-tool/terminal-reaper/stale-rows.mjs +3 -3
  356. package/src/session-runtime/services/agent-tool/terminal-reaper.mjs +6 -23
  357. package/src/session-runtime/services/agent-tool/terminal-trace.mjs +0 -1
  358. package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
  359. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +32 -20
  360. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -2
  361. package/src/session-runtime/services/agent-tool/worker-index.mjs +28 -8
  362. package/src/session-runtime/services/agent-tool.mjs +4 -0
  363. package/src/session-runtime/services/agent-watchdog-registry.mjs +2 -0
  364. package/src/session-runtime/services/provider-admin.mjs +77 -0
  365. package/src/session-runtime/session-create/session-options.mjs +7 -5
  366. package/src/session-runtime/session-create/session-start-hook.mjs +2 -1
  367. package/src/session-runtime/session-transcript.mjs +4 -5
  368. package/src/session-runtime/session-turn-api.mjs +3 -9
  369. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -6
  370. package/src/session-runtime/setup-tool/executor.mjs +23 -5
  371. package/src/session-runtime/setup-tool/extended-actions.mjs +6 -6
  372. package/src/session-runtime/setup-tool/settings-contract.mjs +7 -2
  373. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -1
  374. package/src/session-runtime/tool-policy-refresh.mjs +83 -32
  375. package/src/session-runtime/turn/agent-ops.mjs +49 -2
  376. package/src/session-runtime/turn/side-question-ops.mjs +1 -4
  377. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +2 -0
  378. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +4 -0
  379. package/src/session-runtime/turn/turn-run/turn-settle.mjs +8 -2
  380. package/src/session-runtime/turn/turn-run.mjs +1 -36
  381. package/src/session-runtime/usage-stats-api.mjs +4 -1
  382. package/src/session-runtime/workflow-agents-api/agent-route.mjs +16 -1
  383. package/src/session-runtime/workspace/admission-gate.mjs +44 -15
  384. package/src/session-runtime/workspace/project-branches.mjs +28 -36
  385. package/src/session-runtime/workspace/transition-steps.mjs +96 -95
  386. package/src/session-runtime/workspace/transitions.mjs +248 -68
  387. package/src/standalone/channel-transport.mjs +3 -4
  388. package/src/standalone/daemon-desktop-runtime.mjs +32 -1
  389. package/src/standalone/daemon-pg-stop.mjs +1 -3
  390. package/src/standalone/daemon-session-control.mjs +88 -0
  391. package/src/standalone/daemon-stored-session-views.mjs +23 -3
  392. package/src/standalone/daemon.mjs +2 -0
  393. package/src/standalone/local-session-runtime.mjs +11 -12
  394. package/src/standalone/session-protocol.mjs +7 -0
  395. package/src/standalone/session-runtime-inline-host.mjs +2 -0
  396. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +91 -49
  397. package/src/standalone/session-service/agent-tree/agent-child.mjs +19 -1
  398. package/src/standalone/session-service/agent-tree/agent-registry.mjs +25 -0
  399. package/src/standalone/session-service/agent-tree/agent-turns.mjs +9 -1
  400. package/src/standalone/session-service/agent-tree.mjs +28 -2
  401. package/src/standalone/session-service/entries/session-loads.mjs +16 -5
  402. package/src/standalone/session-service/entries.mjs +1 -0
  403. package/src/standalone/session-service/projection/transcript-window.mjs +39 -0
  404. package/src/standalone/session-service/quota-resume.mjs +28 -13
  405. package/src/standalone/session-service/session-calls/session-turns.mjs +17 -6
  406. package/src/standalone/session-service/session-calls/session-views.mjs +6 -0
  407. package/src/standalone/session-tool-output.mjs +82 -0
  408. package/src/tui/app/app-view.jsx +2 -1
  409. package/src/tui/app/provider-setup/provider-items.mjs +8 -0
  410. package/src/tui/app/provider-setup-picker.mjs +15 -3
  411. package/src/tui/app/slash-commands.mjs +6 -0
  412. package/src/tui/app/slash-dispatch/session-commands.mjs +49 -0
  413. package/src/tui/components/Spinner.jsx +4 -0
  414. package/src/tui/components/SpinnerByline.jsx +21 -2
  415. package/src/tui/components/status-line/local-line.mjs +10 -2
  416. package/src/tui/dist/index.mjs +478 -251
  417. package/src/tui/session/active-tool-summary.mjs +1 -1
  418. package/src/tui/session/context-state.mjs +0 -3
  419. package/src/tui/session/goal-continuation/continuation-queue.mjs +2 -3
  420. package/src/tui/session/goal-continuation/deadline-delivery.mjs +4 -2
  421. package/src/tui/session/goal-continuation/session-boundary.mjs +21 -0
  422. package/src/tui/session/quota-resume.mjs +140 -61
  423. package/src/tui/session/session-api/commands.mjs +4 -10
  424. package/src/tui/session/session-api/controls.mjs +12 -0
  425. package/src/tui/session/session-api/lifecycle/dispose.mjs +2 -9
  426. package/src/tui/session/session-api/lifecycle/resume.mjs +2 -7
  427. package/src/tui/session/session-api/lifecycle.mjs +13 -11
  428. package/src/tui/session/session-api/media.mjs +2 -0
  429. package/src/tui/session/session-api/routes.mjs +81 -57
  430. package/src/tui/session/session-api/settings.mjs +3 -3
  431. package/src/tui/session/session-api/worktree.mjs +3 -2
  432. package/src/tui/session/session-flow/auto-clear.mjs +1 -3
  433. package/src/tui/session/session-flow/drain.mjs +6 -4
  434. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -0
  435. package/src/tui/session/session-flow/queue/take-entries.mjs +2 -1
  436. package/src/tui/session/session-flow/queue.mjs +1 -1
  437. package/src/tui/session/tool-approval.mjs +17 -8
  438. package/src/tui/session/transcript-store/streaming-tail.mjs +1 -1
  439. package/src/tui/session/tui-steering-persist.mjs +3 -5
  440. package/src/tui/session/turn-quiet.mjs +53 -0
  441. package/src/tui/session/turn-stream.mjs +5 -1
  442. package/src/tui/session/turn.mjs +38 -31
  443. package/src/tui/session/worktree-setup-notice.mjs +38 -0
  444. package/src/tui/spinner-meta.mjs +10 -0
  445. package/src/tui/spinner-verbs.mjs +3 -0
  446. package/src/ui/statusline.mjs +13 -3
  447. package/src/runtime/agent/orchestrator/providers/model-pricing-rates.mjs +0 -1
  448. package/src/runtime/agent/orchestrator/session/manager/foreign-pending-messages.mjs +0 -346
  449. package/src/runtime/agent/orchestrator/session/manager/session-owner-liveness.mjs +0 -117
  450. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +0 -302
  451. package/src/session-runtime/services/agent-tool/terminal-reaper/reap-timers.mjs +0 -142
  452. package/src/session-runtime/turn/share-ops.mjs +0 -77
  453. package/src/tui/session/cross-surface-share.mjs +0 -406
  454. package/src/tui/session/live-share/delta.mjs +0 -153
  455. package/src/tui/session/live-share/mirror.mjs +0 -106
  456. package/src/tui/session/live-share/owner.mjs +0 -254
  457. package/src/tui/session/live-share/retry.mjs +0 -54
  458. package/src/tui/session/live-share/viewer/frame-router.mjs +0 -32
  459. package/src/tui/session/live-share/viewer/pipe-client.mjs +0 -108
  460. package/src/tui/session/live-share/viewer/submit-acks.mjs +0 -65
  461. package/src/tui/session/live-share/viewer/sync-waiters.mjs +0 -53
  462. package/src/tui/session/live-share/viewer.mjs +0 -99
  463. package/src/tui/session/live-share/wire.mjs +0 -44
  464. package/src/tui/session/live-share.mjs +0 -154
  465. package/src/tui/session/shared-dir-watch.mjs +0 -69
@@ -1,12 +1,15 @@
1
1
  /**
2
2
  * The Lead session workspace transition authority. Every workspace change of
3
3
  * the Lead this runtime hosts — entering its retained worktree (created lazily
4
- * on the first message of a worktree task), returning to a project-folder
5
- * branch, removing the worktree — goes through applyWorkspaceTransition: one
4
+ * on the first message of a worktree task), selecting a branch (it runs where
5
+ * that branch is checked out — the project folder, a linked checkout, or a
6
+ * managed worktree made for it — and no existing folder is switched),
7
+ * removing the worktree — goes through applyWorkspaceTransition: one
6
8
  * serialized, revisioned commit that retargets the runtime cwd (and the turn's
7
9
  * cwd override), stores the durable `workspace` record, re-roots the prompt
8
- * context of the Lead and its idle workers, and persists. Failures are typed
9
- * WorktreeErrors and leave the previous workspace intact.
10
+ * context of the Lead and its idle workers, and persists, awaiting each save
11
+ * before it reports success. Failures are typed WorktreeErrors and leave the
12
+ * previous workspace intact.
10
13
  *
11
14
  * Git and registry work is delegated to the managed-worktree backend
12
15
  * (runtime/shared/worktrees/manager.mjs), loaded on first use.
@@ -21,12 +24,24 @@ import {
21
24
  normalizeSessionWorkspace,
22
25
  worktreeError,
23
26
  worktreeNameError,
27
+ worktreeRefusal,
24
28
  } from '../../runtime/shared/worktrees/contract.mjs';
25
29
  import { normalizeWorktreeSettings } from '../../runtime/shared/worktrees/settings.mjs';
26
30
  import * as maintenance from '../../runtime/shared/worktrees/maintenance.mjs';
27
31
  import { inspectRepo, throwIfAborted } from '../../runtime/shared/worktrees/git.mjs';
32
+ import { worktreePreparationNote } from '../../runtime/shared/worktrees/preparation-report.mjs';
28
33
  import { isWithin, samePath } from '../../runtime/shared/worktrees/paths.mjs';
29
34
  import { readGitHead } from '../../runtime/shared/worktrees/git-head.mjs';
35
+ import {
36
+ holdWorkspaceActivity,
37
+ isExternalCheckout,
38
+ releaseAll,
39
+ } from '../../runtime/shared/worktrees/activity-gate.mjs';
40
+ import { mergeConfirmedTarget } from '../../runtime/shared/worktrees/branch-management.mjs';
41
+ import {
42
+ clearReadDedupSession,
43
+ resetReadStateAfterCompaction,
44
+ } from '../../runtime/agent/orchestrator/session/cache/read-cache.mjs';
30
45
  import { sessionHasConversationMessages } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
31
46
  import { buildWorkspacePromptContext } from '../../runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs';
32
47
  import {
@@ -36,7 +51,7 @@ import {
36
51
  import { AGENT_OWNER } from '../../runtime/agent/orchestrator/agent-owner.mjs';
37
52
  import { _liveSessions } from '../../runtime/agent/orchestrator/session/store/live-state.mjs';
38
53
  import { admitTurn, beginWorkspaceTransition, holdCheckoutFolder } from './admission-gate.mjs';
39
- import { assertMergeTarget, createProjectBranchOps } from './project-branches.mjs';
54
+ import { createProjectBranchOps } from './project-branches.mjs';
40
55
  import { assertRetainedFits, createTransitionSteps, undoAll } from './transition-steps.mjs';
41
56
  import { readSessionWorkspace } from './workspace-state.mjs';
42
57
 
@@ -65,6 +80,17 @@ async function defaultRegisteredProject(path) {
65
80
  return listProjectsStrict().find((entry) => samePath(entry.path, path))?.path ?? null;
66
81
  }
67
82
 
83
+ /** The dependency installs a new worktree folder still needs (dependencies.mjs). */
84
+ async function defaultPlanWorktreeSetup(input) {
85
+ const { planDependencySetup } = await import('../../runtime/shared/worktrees/dependencies.mjs');
86
+ return planDependencySetup(input);
87
+ }
88
+
89
+ async function defaultRunWorktreeSetup(input) {
90
+ const { runWorktreeSetupCommands } = await import('../../runtime/shared/worktrees/setup-commands.mjs');
91
+ return runWorktreeSetupCommands(input);
92
+ }
93
+
68
94
  /**
69
95
  * Re-root one session's prompt at `cwd`: environment facts and instruction
70
96
  * files for every session, project memory for the Lead only (workers never
@@ -74,6 +100,10 @@ async function defaultRegisteredProject(path) {
74
100
  * a worktree) adds the uncommitted-changes and path lines to that notice.
75
101
  */
76
102
  export async function refreshWorkspacePrompt(session, cwd, { loadCoreMemoryContext, move = null } = {}) {
103
+ // A new checkout invalidates observed-file snapshots as well as read/search
104
+ // results. Conversation text remains historical evidence, not a file cache.
105
+ clearReadDedupSession(session?.id);
106
+ resetReadStateAfterCompaction(session?.id);
77
107
  const { environmentContext, instructionFilesContext } = buildWorkspacePromptContext(session, cwd);
78
108
  const coreMemoryContext =
79
109
  session?.owner === AGENT_OWNER || typeof loadCoreMemoryContext !== 'function'
@@ -149,7 +179,7 @@ const TRANSITION_STATE = { worktree: 'provisioning', branch: 'switching', 'remov
149
179
  * @param {() => string} deps.getCurrentCwd
150
180
  * @param {(cwd: string, options?: { persistProjectSelection?: boolean }) => unknown} deps.applyResolvedCwd Retargets rt cwd, session.cwd and the cwd override (and, persisting, the Project selection).
151
181
  * @param {(desktopSession: any) => unknown} [deps.setDesktopSession] Restores the runtime's desktop metadata on a rollback.
152
- * @param {(session: any) => unknown} deps.persistSession
182
+ * @param {(session: any) => Promise<unknown>|unknown} deps.persistSession Durable save: resolves once the session is on disk, rejects (throws) when it is not.
153
183
  * @param {(session: any, cwd: string, move?: { from: string, changes: 'carried'|'left'|null }|null) => Promise<unknown>} deps.refreshWorkspaceContext
154
184
  * @param {() => number} deps.getActiveTurnCount
155
185
  * @param {() => { agentWorkers?: any[], agentJobs?: any[] }} deps.agentStatusState
@@ -179,11 +209,24 @@ export function createWorkspaceTransitions({
179
209
  inspectProject = inspectRepo,
180
210
  findRegisteredProject = defaultRegisteredProject,
181
211
  listStoredSessionIds = maintenance.storedSessionIds,
212
+ listWorkspaceConsumers = maintenance.storedWorkspaceConsumers,
213
+ listWorkspaceJobs = async () => {
214
+ const { listShellJobRecords } = await import(
215
+ '../../runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs'
216
+ );
217
+ return listShellJobRecords({ strict: true });
218
+ },
182
219
  runtimeToken = randomUUID(),
183
220
  newTxId = randomUUID,
221
+ planWorktreeSetup = defaultPlanWorktreeSetup,
222
+ runWorktreeSetup = defaultRunWorktreeSetup,
184
223
  }) {
185
224
  const worktreeSettings = () => normalizeWorktreeSettings(getConfig?.()?.worktree);
186
225
  let pendingIntent = creationIntent(initialIntent, worktreeSettings());
226
+ // The new worktree whose dependencies have not been prepared yet, bound to
227
+ // that worktree (worktreeId + cwd): it runs only while the session is there.
228
+ let pendingSetup = null;
229
+ const setupListeners = new Set();
187
230
  let managerPromise = null;
188
231
  const backend = () => {
189
232
  if (manager) return Promise.resolve(manager);
@@ -300,25 +343,19 @@ export function createWorkspaceTransitions({
300
343
  * wait (admission gate). `sessionId` null: a session-less (draft) change.
301
344
  * @returns {() => void}
302
345
  */
303
- function holdProjectFolder(sessionId, folder) {
346
+ function holdProjectFolder(sessionId, folder, { hold = true } = {}) {
304
347
  const users = listLiveSessions().filter(
305
348
  (candidate) =>
306
349
  candidate?.id &&
307
350
  candidate.id !== sessionId &&
308
351
  candidate.closed !== true &&
309
- candidate.remoteAttached !== true &&
310
352
  (!sessionId || (candidate.ownerSessionId !== sessionId && candidate.parentSessionId !== sessionId)) &&
311
353
  typeof candidate.cwd === 'string' &&
312
354
  isWithin(folder, candidate.cwd) &&
313
355
  isSessionActive(candidate)
314
356
  );
315
- if (users.length) {
316
- throw worktreeError('WORKTREE_BUSY_JOBS', 'Other sessions are working in the project folder.', {
317
- reason: 'folder-in-use',
318
- sessions: users.map((candidate) => candidate.id),
319
- });
320
- }
321
- return holdCheckoutFolder(sessionId, folder);
357
+ if (users.length) throw worktreeRefusal('folder-in-use', { sessions: users.map((candidate) => candidate.id) });
358
+ return holdCheckoutFolder(sessionId, folder, { hold });
322
359
  }
323
360
 
324
361
  /** Run `fn` holding the checkout `folder` (holdProjectFolder); released when it settles. */
@@ -352,12 +389,49 @@ export function createWorkspaceTransitions({
352
389
  .map((candidate) => candidate.id);
353
390
  if (!sharing.length) return;
354
391
  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 }
392
+ if (shellJobs > 0) throw worktreeRefusal('folder-shell-jobs', { shellJobs });
393
+ }
394
+
395
+ /**
396
+ * The shared admission of every merge and folder cleanup (manageProjectBranch):
397
+ * refuses while a session or worker runs in an affected folder, or a
398
+ * background command does; `removing` refuses while any session still uses
399
+ * one. Then holds the folders — Mixdog checkouts only; an external checkout
400
+ * is never lock-blocked — so new Mixdog work there waits until `run`
401
+ * settles. `actor`: the session acting on its own checkout, whose own turn
402
+ * and workers its caller already checked (assertLeadIdle).
403
+ */
404
+ async function withManagementFolders(folders, removing, run, { actor = null } = {}) {
405
+ const release = [];
406
+ try {
407
+ for (const folder of [...new Set(folders)].sort()) {
408
+ release.push(holdProjectFolder(actor, folder, { hold: !isExternalCheckout(folder) }));
409
+ }
410
+ release.push(holdWorkspaceActivity(folders, { sessionId: actor }));
411
+ const stored = await listWorkspaceConsumers();
412
+ const consumers = new Map(stored.map((session) => [session.id, session]));
413
+ for (const session of listLiveSessions()) if (session?.closed !== true) consumers.set(session.id, session);
414
+ const affected = [...consumers.values()].filter((session) =>
415
+ folders.some((folder) => session.cwd && isWithin(folder, session.cwd))
360
416
  );
417
+ const blocking = removing
418
+ ? affected
419
+ : affected.filter(
420
+ (session) =>
421
+ !(actor && [session.id, session.ownerSessionId, session.parentSessionId].includes(actor)) &&
422
+ isSessionActive(session)
423
+ );
424
+ if (blocking.length) throw worktreeRefusal('folder-in-use', { sessions: blocking.map((session) => session.id) });
425
+ const jobs = await listWorkspaceJobs();
426
+ if (jobs.some((job) => !job.terminal && folders.some((folder) => isWithin(folder, job.cwd)))) {
427
+ throw worktreeRefusal('folder-shell-jobs');
428
+ }
429
+ if ((await countOwnShellJobs(affected.map((session) => session.id))) > 0) {
430
+ throw worktreeRefusal('folder-shell-jobs');
431
+ }
432
+ return await run([...consumers.keys()]);
433
+ } finally {
434
+ releaseAll(release);
361
435
  }
362
436
  }
363
437
 
@@ -382,6 +456,7 @@ export function createWorkspaceTransitions({
382
456
  inspectProject,
383
457
  findRegisteredProject,
384
458
  withProjectFolder,
459
+ withManagementFolders,
385
460
  });
386
461
 
387
462
  async function transition(session, target, options = {}) {
@@ -406,22 +481,48 @@ export function createWorkspaceTransitions({
406
481
  await assertLeadIdle(session, { ownTurn: options.ownTurn === true });
407
482
  const run = { signal: options.signal, txId, journal };
408
483
  if (target.kind === 'worktree') {
409
- return await steps.enterWorktree(session, previous, {
484
+ const { created, ...entered } = await steps.enterWorktree(session, previous, {
410
485
  ...run,
411
486
  name: target.name,
412
487
  nameHint: options.nameHint,
413
488
  confirmLarge: options.confirmLarge,
414
489
  changes: options.changes,
415
490
  });
491
+ // A new checkout gets its dependencies prepared at the start of the
492
+ // session's next turn there (prepareTurnWorkspace).
493
+ if (created) {
494
+ pendingSetup = {
495
+ ...created,
496
+ sessionId: session.id,
497
+ worktreeId: entered.workspace.worktreeId,
498
+ cwd: entered.workspace.cwd,
499
+ mainDir: previous.projectPath,
500
+ };
501
+ }
502
+ return entered;
416
503
  }
417
504
  if (target.kind === 'branch') {
418
- return await steps.enterProjectBranch(session, previous, {
505
+ const { created, ...entered } = await steps.enterProjectBranch(session, previous, {
419
506
  ...run,
420
507
  branch: target.branch,
421
508
  create: target.create,
509
+ confirmLarge: options.confirmLarge,
422
510
  });
511
+ if (created && entered.workspace?.worktreeId) {
512
+ pendingSetup = {
513
+ ...created,
514
+ sessionId: session.id,
515
+ worktreeId: entered.workspace.worktreeId,
516
+ cwd: entered.workspace.cwd,
517
+ mainDir: previous.projectPath,
518
+ };
519
+ }
520
+ return entered;
423
521
  }
424
- return await steps.removeRetainedWorktree(session, previous, { ...run, force: target.force });
522
+ const removed = await steps.removeRetainedWorktree(session, previous, { ...run, force: target.force });
523
+ // The session's retained worktree is gone: its pending setup goes with it.
524
+ if (pendingSetup?.sessionId === session.id) pendingSetup = null;
525
+ return removed;
425
526
  } catch (error) {
426
527
  // Whatever already moved (cwd, record, prompts, worker roots, lease)
427
528
  // goes back exactly; the previous workspace stays authoritative.
@@ -495,21 +596,77 @@ export function createWorkspaceTransitions({
495
596
  );
496
597
  }
497
598
  const intent = intentFor(session);
498
- if (!intent) return null;
599
+ let result = null;
600
+ if (intent) {
601
+ try {
602
+ result = await provisionIntent(session, intent, submissionId, prompt, {
603
+ confirmLarge: intent.confirmLarge || confirmLarge === true,
604
+ signal,
605
+ ownTurn: true,
606
+ });
607
+ } catch (error) {
608
+ // The settings default never blocks a task outside a git repository.
609
+ if (!intent.explicit && isWorktreeError(error, 'WORKTREE_UNSUPPORTED_REPO')) {
610
+ if (pendingIntent === intent) pendingIntent = null;
611
+ return null;
612
+ }
613
+ throw error;
614
+ }
615
+ }
616
+ await runPendingSetup(session, signal);
617
+ return result;
618
+ }
619
+
620
+ /**
621
+ * Prepare the session's new worktree, once, under the turn (its admission
622
+ * keeps the folder from being merged or removed; its signal — the Stop
623
+ * button — cancels): the frozen installs the links made at creation did
624
+ * not cover. While an install runs, every onWorktreeSetup listener gets its
625
+ * one-line progress; nothing else reaches the user. The agent gets one
626
+ * compact note with this turn (`session.pendingSetupNotice`): the main
627
+ * checkout, what was copied, linked and installed, what failed (with the
628
+ * output tail) and what was skipped. A failure or timeout is in the note
629
+ * and the turn goes on; a cancel aborts the turn like any other stop. The
630
+ * worktree stays either way. The setup belongs to the worktree it was made
631
+ * for: while the session works elsewhere (it returned to a project-folder
632
+ * branch before its first turn there) it is deferred, never run in another
633
+ * folder, and runs on the first turn after the session re-enters it.
634
+ */
635
+ async function runPendingSetup(session, signal) {
636
+ const pending = pendingSetup;
637
+ if (!pending || pending.sessionId !== session.id) return;
638
+ const here = readSessionWorkspace(session);
639
+ if (!isWorktreeBound(here) || here.worktreeId !== pending.worktreeId || !samePath(here.cwd, pending.cwd)) return;
640
+ pendingSetup = null;
641
+ const id = newTxId();
642
+ const report = { root: pending.root, prepared: pending.prepared, dependencyNotes: [], installs: null, notes: [] };
643
+ const emit = (event) => {
644
+ for (const listener of setupListeners) {
645
+ try {
646
+ listener({ ...event, id, sessionId: session.id, cwd: pending.cwd });
647
+ } catch {
648
+ /* a viewer's failure never stops the setup */
649
+ }
650
+ }
651
+ };
499
652
  try {
500
- return await provisionIntent(session, intent, submissionId, prompt, {
501
- confirmLarge: intent.confirmLarge || confirmLarge === true,
502
- signal,
503
- ownTurn: true,
504
- });
505
- } catch (error) {
506
- // The settings default never blocks a task outside a git repository.
507
- if (!intent.explicit && isWorktreeError(error, 'WORKTREE_UNSUPPORTED_REPO')) {
508
- if (pendingIntent === intent) pendingIntent = null;
509
- return null;
653
+ const plan = await planWorktreeSetup({ worktreeDir: pending.cwd, mainDir: pending.mainDir, signal });
654
+ report.dependencyNotes = plan.notes;
655
+ if (plan.commands.length) {
656
+ report.installs = await runWorktreeSetup({ commands: plan.commands, signal, onProgress: emit });
510
657
  }
511
- throw error;
658
+ } catch (error) {
659
+ if (!signal?.aborted) report.notes.push(`Dependencies could not be prepared: ${error?.message || error}`);
512
660
  }
661
+ session.pendingSetupNotice = { revision: Date.now(), content: worktreePreparationNote(report) };
662
+ throwIfAborted(signal);
663
+ }
664
+
665
+ /** Subscribe to worktree setup progress; returns the unsubscribe. */
666
+ function onWorktreeSetup(listener) {
667
+ if (typeof listener !== 'function') return () => {};
668
+ setupListeners.add(listener);
669
+ return () => setupListeners.delete(listener);
513
670
  }
514
671
 
515
672
  async function getBranchMenu() {
@@ -521,14 +678,16 @@ export function createWorkspaceTransitions({
521
678
  projectPath: workspace.projectPath,
522
679
  sessionId: session?.id || null,
523
680
  });
524
- // A session in the project folder (never moved, or back from a worktree)
681
+ const selectedEntry = workspace.worktreeId ? await (await backend()).findWorktreeContaining(workspace.cwd) : null;
682
+ // A session outside a managed worktree (the project folder — never moved,
683
+ // or back from a worktree — or a linked checkout of the selected branch)
525
684
  // is on whatever that folder has checked out now.
526
685
  // Its repository is the project's (a never-moved session has no stored
527
686
  // record), and a detached checkout shows branch null with the commit in
528
687
  // baseCommit — never mistaken for "not a repository".
529
688
  let view = workspace;
530
689
  if (!isWorktreeBound(workspace)) {
531
- const project = await inspectProject(workspace.projectPath);
690
+ const project = await inspectProject(workspace.cwd);
532
691
  const checkedOut = project.currentBranch ?? null;
533
692
  view = {
534
693
  ...workspace,
@@ -542,7 +701,7 @@ export function createWorkspaceTransitions({
542
701
  pendingWorktree: pendingIntent ? { name: pendingIntent.name } : null,
543
702
  branches: branchMenuEntries(menu.branches, menu.occupancy),
544
703
  occupancy: menu.occupancy,
545
- retainedWorktree: menu.retainedWorktree ?? null,
704
+ retainedWorktree: selectedEntry ?? menu.retainedWorktree ?? null,
546
705
  // Creation notes of the retained worktree (submodules, include entries).
547
706
  warnings: Array.isArray(menu.retainedWorktree?.warnings) ? [...menu.retainedWorktree.warnings] : [],
548
707
  };
@@ -581,11 +740,13 @@ export function createWorkspaceTransitions({
581
740
 
582
741
  async function selectBranch({ branch, create = false, expectedRevision, confirmLarge = false } = {}) {
583
742
  const session = await ensureSession();
584
- const retained = await (await backend()).getSessionWorktree(session.id);
585
- const target =
586
- retained && retained.branch === branch && create !== true
587
- ? { kind: 'worktree' }
588
- : { kind: 'branch', branch, create: create === true };
743
+ const current = currentWorkspace(session);
744
+ if (current.branch === branch && current.worktreeId) {
745
+ assertRevision(current.revision, validRevision(expectedRevision));
746
+ pendingIntent = null;
747
+ return { revision: current.revision, workspace: current };
748
+ }
749
+ const target = { kind: 'branch', branch, create: create === true };
589
750
  const result = await applyWorkspaceTransition({ sessionId: session.id }, target, {
590
751
  expectedRevision,
591
752
  confirmLarge,
@@ -695,14 +856,24 @@ export function createWorkspaceTransitions({
695
856
  cleanup: { removed: false, reason: 'auto-cleanup-off', message: 'Auto cleanup is off; the worktree is kept.' },
696
857
  };
697
858
  }
859
+ // The cleanup never moves a session: while this session still works in the
860
+ // merged folder (or any other session uses it) the removal is refused.
698
861
  try {
699
- const removed = await removeWorktree({ force: false }, { ownTurn });
862
+ if (isWorktreeBound(currentWorkspace(session))) {
863
+ throw worktreeRefusal('folder-in-use', { sessions: [session.id] });
864
+ }
865
+ const entry = await (await backend()).getSessionWorktree(session.id);
866
+ const removed = await withManagementFolders(
867
+ entry ? [entry.path] : [],
868
+ true,
869
+ () => removeWorktree({ force: false }, { ownTurn }),
870
+ { actor: session.id }
871
+ );
700
872
  return { ...merged, cleanup: removed.removed, revision: removed.revision, workspace: removed.workspace };
701
873
  } catch (error) {
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).
874
+ // The merge stands: a refused or incomplete cleanup (in use, dirty,
875
+ // unmerged, retained file recovery data, partly removed, …) is
876
+ // reported, never thrown, with the workspace as it is now.
706
877
  const workspace = currentWorkspace(session);
707
878
  const code = typeof error?.code === 'string' ? error.code : '';
708
879
  return {
@@ -722,8 +893,9 @@ export function createWorkspaceTransitions({
722
893
  /**
723
894
  * The merge itself. The retained worktree must belong to this session's
724
895
  * Project (same repository and Project folder — a Project switch may have
725
- * left it behind): its recorded project folder is the destination that is
726
- * guarded, compared with the confirmed target and merged into.
896
+ * left it behind); its branch, or the branch of the linked checkout the
897
+ * session works in, is merged into the project folder through the shared
898
+ * guarded path (mergeConfirmedTarget): both folders validated and held.
727
899
  */
728
900
  async function mergeRetainedWorktree(session, { target, ownTurn, expectedRevision }) {
729
901
  const workspace = currentWorkspace(session);
@@ -731,26 +903,31 @@ export function createWorkspaceTransitions({
731
903
  const leave = beginWorkspaceTransition(session.id, `merge:${newTxId()}`);
732
904
  try {
733
905
  await assertLeadIdle(session, { ownTurn: ownTurn === true });
734
- const backendApi = await backend();
735
- const entry = await backendApi.getSessionWorktree(session.id);
736
- if (!entry) throw worktreeError('WORKTREE_MISSING', 'This session has no worktree to merge.');
737
906
  const project = await inspectProject(workspace.projectPath);
738
- assertRetainedFits(entry, project, workspace.projectPath);
739
- if (!samePath(entry.projectPath, workspace.projectPath)) {
740
- throw worktreeError('WORKTREE_PROJECT_MISMATCH', undefined, {
741
- worktreeId: entry.id,
742
- worktreeProject: entry.projectPath,
743
- projectPath: workspace.projectPath,
744
- });
907
+ let branch;
908
+ if (!workspace.worktreeId && !samePath(workspace.cwd, workspace.projectPath)) {
909
+ const source = await inspectProject(workspace.cwd);
910
+ if (!samePath(source.repoCommonDir, project.repoCommonDir)) throw worktreeError('WORKTREE_PROJECT_MISMATCH');
911
+ branch = source.currentBranch;
912
+ } else {
913
+ const backendApi = await backend();
914
+ const entry = workspace.worktreeId
915
+ ? await backendApi.findWorktreeContaining(workspace.cwd)
916
+ : await backendApi.getSessionWorktree(session.id);
917
+ if (!entry) throw worktreeError('WORKTREE_MISSING', 'This session has no worktree to merge.');
918
+ assertRetainedFits(entry, project, workspace.projectPath);
919
+ if (!samePath(entry.projectPath, workspace.projectPath)) {
920
+ throw worktreeError('WORKTREE_PROJECT_MISMATCH', undefined, {
921
+ worktreeId: entry.id,
922
+ worktreeProject: entry.projectPath,
923
+ projectPath: workspace.projectPath,
924
+ });
925
+ }
926
+ branch = entry.branch;
745
927
  }
746
- assertMergeTarget(project.currentBranch, target);
747
- return await withProjectFolder(session.id, project.topLevel || workspace.projectPath, () =>
748
- backendApi.mergeWorktreeIntoProject({
749
- entryId: entry.id,
750
- target: project.currentBranch,
751
- confirmed: true,
752
- projectPath: entry.projectPath,
753
- })
928
+ return await mergeConfirmedTarget(
929
+ { projectPath: workspace.projectPath, branch, target },
930
+ (folders, removing, run) => withManagementFolders(folders, removing, run, { actor: session.id })
754
931
  );
755
932
  } finally {
756
933
  leave();
@@ -815,6 +992,8 @@ export function createWorkspaceTransitions({
815
992
  selectBranch,
816
993
  selectProjectBranch: projectBranches.selectProjectBranch,
817
994
  mergeProjectBranch: projectBranches.mergeProjectBranch,
995
+ inspectProjectBranch: projectBranches.inspectProjectBranch,
996
+ manageProjectBranch: projectBranches.manageProjectBranch,
818
997
  requestWorktree,
819
998
  mergeWorkspace,
820
999
  removeWorktree,
@@ -822,5 +1001,6 @@ export function createWorkspaceTransitions({
822
1001
  listWorktrees,
823
1002
  pruneWorktrees,
824
1003
  cleanupDeletedSession,
1004
+ onWorktreeSetup,
825
1005
  };
826
1006
  }
@@ -8,10 +8,9 @@
8
8
  // (targeted routing, never broadcast — see channel-transport/remote-notify.mjs).
9
9
  //
10
10
  // This module owns ONLY the transport (sockets, client registry, notify
11
- // routing and lifecycle). The channels runtime (tool dispatch,
12
- // Discord provider, transcript bind/steal) is injected via `handleCall` so the
13
- // same transport is exercised by the real daemon entry AND the smoke harness
14
- // (stub runtime, no Discord token).
11
+ // routing and lifecycle). The channels runtime is injected via `handleCall`
12
+ // so the same transport is exercised by the real daemon entry AND the smoke
13
+ // harness (stub runtime, no Discord token).
15
14
  //
16
15
  // Composition root: the shared transport state below is read and written by
17
16
  // the sub-services in channel-transport/ (remote binding, client registry,
@@ -8,7 +8,31 @@
8
8
  // created after this surface is handed to the session service).
9
9
  // Output: the desktopRuntime object consumed by session-service.
10
10
 
11
+ /** The stored-session reader, with the Agents pool's running state taken from
12
+ * this daemon's in-memory turn activity (every session runs inline here, so
13
+ * it is the single source of truth; worker index files only declare work). */
14
+ async function loadSessionStoreWithRuntimeActivity() {
15
+ const [store, runtime, progress] = await Promise.all([
16
+ import('../runtime/agent/orchestrator/session/store-summary-reader.mjs'),
17
+ // The import-free leaf holding the runtime map: no session manager graph.
18
+ import('../runtime/agent/orchestrator/session/manager/runtime-state.mjs'),
19
+ import('../runtime/agent/orchestrator/session/agent-pool-progress.mjs'),
20
+ ]);
21
+ return {
22
+ ...store,
23
+ // Running rows also carry their turn's progress stamp and stage (the
24
+ // quiet-time cue), read from the same runtime map.
25
+ listStoredAgentWorkers: () =>
26
+ progress.withTurnProgress(
27
+ store.listStoredAgentWorkers({ runningSessionIds: runtime.inFlightSessionActivity().working }),
28
+ (sessionId) => runtime.runtimeState.get(sessionId),
29
+ runtime.isTurnInFlight
30
+ ),
31
+ };
32
+ }
33
+
11
34
  export function createDesktopRuntime({ getLocalSessionBridge }) {
35
+ let sessionStore = null;
12
36
  return {
13
37
  async attachSessionClient(options = {}) {
14
38
  const bridge = getLocalSessionBridge();
@@ -17,7 +41,14 @@ export function createDesktopRuntime({ getLocalSessionBridge }) {
17
41
  },
18
42
  loadProjects: () => import('../runtime/shared/projects.mjs'),
19
43
  loadWorktrees: () => import('../runtime/shared/worktrees/manager.mjs'),
20
- loadSessionStore: () => import('../runtime/agent/orchestrator/session/store-summary-reader.mjs'),
44
+ loadSessionStore() {
45
+ // A failed import is forgotten so the next caller retries.
46
+ sessionStore ??= loadSessionStoreWithRuntimeActivity().catch((error) => {
47
+ sessionStore = null;
48
+ throw error;
49
+ });
50
+ return sessionStore;
51
+ },
21
52
  loadSessionSearch: () => import('../runtime/session-search/session-search-db.mjs'),
22
53
  loadStatuslineSegments: () => import('../runtime/shared/statusline/statusline-segments.mjs'),
23
54
  loadConfig: () => import('../runtime/shared/config.mjs'),
@@ -25,9 +25,7 @@ export function createDaemonPgStop({
25
25
  const drainBudget = () => Math.max(0, startedAt + forcedStopAtMs - Date.now());
26
26
  const run = (drain) => {
27
27
  stopping ??= loadAdapter()
28
- .then(({ shutdownOwnedPg }) =>
29
- shutdownOwnedPg({ timeoutMs: stopTimeoutMs, drainMs: drain ? drainBudget() : 0 })
30
- )
28
+ .then(({ shutdownOwnedPg }) => shutdownOwnedPg({ timeoutMs: stopTimeoutMs, drainMs: drain ? drainBudget() : 0 }))
31
29
  .then(
32
30
  (stopped) => {
33
31
  if (stopped) log('postgres stopped');
@@ -0,0 +1,88 @@
1
+ import { SESSION_ID_PATTERN } from '../runtime/agent/orchestrator/runtime-core/session-id.mjs';
2
+ import { sessionListOutput, sessionReadOutput, SESSION_TOOL_OUTPUT_BYTES } from './session-tool-output.mjs';
3
+
4
+ const TARGET_ACTIONS = new Set(['status', 'read', 'send', 'cancel', 'delete']);
5
+ const MUTATING_ACTIONS = new Set(['send', 'cancel', 'delete']);
6
+
7
+ /** Model-facing session operations over the same service used by desktop and TUI. */
8
+ export function createSessionControl({ getSessionService }) {
9
+ return async function executeSessionControl(args = {}, context = {}) {
10
+ const action = args.action;
11
+ if (action !== 'list' && !TARGET_ACTIONS.has(action)) {
12
+ throw new Error(`session: unsupported remote action "${action}"`);
13
+ }
14
+ const caller = context.callerSessionId;
15
+ if (typeof caller !== 'string' || !SESSION_ID_PATTERN.test(caller)) {
16
+ throw new Error('session: caller session is required');
17
+ }
18
+ const sessionId = typeof args.session_id === 'string' ? args.session_id.trim() : '';
19
+ if (TARGET_ACTIONS.has(action) && (typeof sessionId !== 'string' || !SESSION_ID_PATTERN.test(sessionId))) {
20
+ throw new Error(`session: session_id is required for action=${action}`);
21
+ }
22
+ if (MUTATING_ACTIONS.has(action) && sessionId === caller) {
23
+ throw new Error(`session: ${action} must target another session`);
24
+ }
25
+ const limit = args.limit ?? 25;
26
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
27
+ throw new Error('session: limit must be an integer from 1 to 100');
28
+ }
29
+ const offset = args.offset ?? 0;
30
+ const textOffset = args.text_offset ?? 0;
31
+ if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(offset + limit)) {
32
+ throw new Error('session: offset must be a nonnegative safe integer');
33
+ }
34
+ if (!Number.isSafeInteger(textOffset) || textOffset < 0) throw new Error('session: invalid text_offset');
35
+ if (args.text_offset !== undefined && (args.offset === undefined || limit !== 1 || !args.item_id)) {
36
+ throw new Error('session: text_offset requires offset, limit:1 and item_id from read');
37
+ }
38
+ const service = getSessionService();
39
+ if (!service) throw new Error('session: session service is unavailable');
40
+ if (action === 'list') {
41
+ const page = await service.listSessionCatalog({ page: { limit, cursor: args.cursor || null } });
42
+ return sessionListOutput(page, limit);
43
+ }
44
+ if (action === 'send') {
45
+ if (typeof args.prompt !== 'string' || !args.prompt.trim()) {
46
+ throw new Error('session: prompt is required for action=send');
47
+ }
48
+ const result = await service.submitSession({
49
+ sessionId,
50
+ prompt: args.prompt,
51
+ options: { mode: 'prompt', transcriptMeta: { sender: 'lead' } },
52
+ });
53
+ return { sessionId, accepted: result.accepted === true };
54
+ }
55
+ if (action === 'cancel') {
56
+ const result = await service.abortSession({ sessionId });
57
+ return { sessionId, aborted: result.aborted === true };
58
+ }
59
+ if (action === 'delete') {
60
+ const result = await service.configureSession(
61
+ { sessionId, action: 'deleteSession', args: [sessionId] },
62
+ { revision: 1 }
63
+ );
64
+ return { sessionId, deleted: result.value === true };
65
+ }
66
+ if (action === 'read') {
67
+ const result = await service.readSession({ sessionId, transcriptRange: { offset, limit } });
68
+ return sessionReadOutput(sessionId, result.range, { textOffset, itemId: args.item_id });
69
+ }
70
+ const result = await service.readSession({
71
+ sessionId,
72
+ transcriptItemLimit: action === 'status' ? 1 : limit,
73
+ transcriptByteBudget: SESSION_TOOL_OUTPUT_BYTES,
74
+ });
75
+ const snapshot = result.full;
76
+ if (!snapshot) throw new Error(`session: ${sessionId} is unavailable`);
77
+ const status = {
78
+ sessionId,
79
+ title: snapshot.title ?? null,
80
+ cwd: snapshot.cwd ?? null,
81
+ provider: snapshot.provider ?? null,
82
+ model: snapshot.model ?? null,
83
+ busy: snapshot.busy === true,
84
+ quotaResume: snapshot.quotaResume ?? null,
85
+ };
86
+ return status;
87
+ };
88
+ }