@adhdev/daemon-core 0.9.82-rc.44 → 0.9.82-rc.441

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 (425) hide show
  1. package/dist/boot/daemon-lifecycle.d.ts +9 -0
  2. package/dist/boot/process-hardening.d.ts +50 -0
  3. package/dist/build-info.d.ts +37 -0
  4. package/dist/chat/source-machine.d.ts +166 -0
  5. package/dist/chat/source-resolver.d.ts +104 -0
  6. package/dist/chat/subscription-updates.d.ts +1 -0
  7. package/dist/cli-adapter-types.d.ts +68 -2
  8. package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
  9. package/dist/cli-adapters/cli-state-engine.d.ts +200 -0
  10. package/dist/cli-adapters/provider-cli-adapter.d.ts +126 -64
  11. package/dist/cli-adapters/provider-cli-parse.d.ts +5 -0
  12. package/dist/cli-adapters/provider-cli-runtime.d.ts +34 -0
  13. package/dist/cli-adapters/provider-cli-shared.d.ts +74 -0
  14. package/dist/cli-adapters/pty-write-chunking.d.ts +34 -0
  15. package/dist/cli-adapters/raw-terminal-io.d.ts +37 -0
  16. package/dist/cli-adapters/resolve-executable.d.ts +69 -0
  17. package/dist/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +6 -3
  18. package/dist/cli-adapters/terminal-backends/types.d.ts +10 -2
  19. package/dist/cli-adapters/terminal-screen.d.ts +4 -11
  20. package/dist/commands/chat-commands-debug-bundle.d.ts +14 -0
  21. package/dist/commands/chat-commands-read.d.ts +7 -0
  22. package/dist/commands/chat-commands-scope.d.ts +39 -0
  23. package/dist/commands/chat-commands-shared.d.ts +33 -0
  24. package/dist/commands/chat-commands-write.d.ts +14 -0
  25. package/dist/commands/chat-commands.d.ts +10 -23
  26. package/dist/commands/cli-manager.d.ts +15 -1
  27. package/dist/commands/handler.d.ts +125 -0
  28. package/dist/commands/high-family/index.d.ts +3 -0
  29. package/dist/commands/high-family/mesh-coordinator-launch.d.ts +2 -0
  30. package/dist/commands/high-family/mesh-events.d.ts +2 -0
  31. package/dist/commands/high-family/mesh-status.d.ts +2 -0
  32. package/dist/commands/high-family/types.d.ts +60 -0
  33. package/dist/commands/low-family/coordinator-prompt.d.ts +9 -0
  34. package/dist/commands/low-family/daemon-lifecycle.d.ts +2 -0
  35. package/dist/commands/low-family/diagnostics.d.ts +2 -0
  36. package/dist/commands/low-family/index.d.ts +3 -0
  37. package/dist/commands/low-family/mesh-ledger.d.ts +10 -0
  38. package/dist/commands/low-family/mesh-node-logs.d.ts +2 -0
  39. package/dist/commands/low-family/notification.d.ts +2 -0
  40. package/dist/commands/low-family/refine-config.d.ts +2 -0
  41. package/dist/commands/low-family/session-host.d.ts +2 -0
  42. package/dist/commands/low-family/spec-providerdev.d.ts +11 -0
  43. package/dist/commands/low-family/status-meta.d.ts +2 -0
  44. package/dist/commands/low-family/types.d.ts +33 -0
  45. package/dist/commands/med-family/cli-agent.d.ts +2 -0
  46. package/dist/commands/med-family/fast-forward.d.ts +2 -0
  47. package/dist/commands/med-family/ide.d.ts +10 -0
  48. package/dist/commands/med-family/index.d.ts +3 -0
  49. package/dist/commands/med-family/mesh-crud.d.ts +29 -0
  50. package/dist/commands/med-family/mesh-host-pairing.d.ts +2 -0
  51. package/dist/commands/med-family/mesh-queue.d.ts +2 -0
  52. package/dist/commands/med-family/mesh-restart.d.ts +2 -0
  53. package/dist/commands/med-family/types.d.ts +142 -0
  54. package/dist/commands/mesh-coordinator.d.ts +85 -1
  55. package/dist/commands/router.d.ts +276 -2
  56. package/dist/commands/upgrade-helper.d.ts +41 -1
  57. package/dist/config/chat-history.d.ts +9 -0
  58. package/dist/config/config.d.ts +5 -0
  59. package/dist/config/mesh-config.d.ts +120 -1
  60. package/dist/config/mesh-json-config.d.ts +199 -0
  61. package/dist/config/repo-settings.d.ts +77 -0
  62. package/dist/daemon/dev-server.d.ts +0 -2
  63. package/dist/detection/ide-detector.d.ts +13 -0
  64. package/dist/detection/win32-ide-version.d.ts +37 -0
  65. package/dist/git/change-impact-config.d.ts +159 -0
  66. package/dist/git/git-commands.d.ts +11 -1
  67. package/dist/git/git-diff.d.ts +6 -0
  68. package/dist/git/git-executor.d.ts +11 -0
  69. package/dist/git/git-status.d.ts +57 -0
  70. package/dist/git/git-types.d.ts +2 -50
  71. package/dist/git/git-worktree.d.ts +71 -1
  72. package/dist/git/index.d.ts +3 -1
  73. package/dist/index.d.ts +64 -14
  74. package/dist/index.js +50719 -20120
  75. package/dist/index.js.map +1 -1
  76. package/dist/index.mjs +50317 -19882
  77. package/dist/index.mjs.map +1 -1
  78. package/dist/installer.d.ts +1 -4
  79. package/dist/ipc/local-ipc-server.d.ts +91 -0
  80. package/dist/launch.d.ts +1 -1
  81. package/dist/logging/async-batch-writer.d.ts +10 -0
  82. package/dist/logging/log-redactor.d.ts +24 -0
  83. package/dist/logging/log-tail-reader.d.ts +81 -0
  84. package/dist/logging/logger.d.ts +1 -1
  85. package/dist/mesh/contracts.d.ts +164 -0
  86. package/dist/mesh/coordinator-prompt.d.ts +89 -0
  87. package/dist/mesh/coordinator-registry.d.ts +59 -0
  88. package/dist/mesh/mesh-active-work.d.ts +174 -0
  89. package/dist/mesh/mesh-clone-grace.d.ts +8 -0
  90. package/dist/mesh/mesh-coordinator-config.d.ts +21 -0
  91. package/dist/mesh/mesh-delivery-policy.d.ts +131 -0
  92. package/dist/mesh/mesh-event-classify.d.ts +5 -0
  93. package/dist/mesh/mesh-event-forwarding.d.ts +41 -0
  94. package/dist/mesh/mesh-event-trace.d.ts +21 -0
  95. package/dist/mesh/mesh-events-coordinator.d.ts +4 -0
  96. package/dist/mesh/mesh-events-pending.d.ts +69 -0
  97. package/dist/mesh/mesh-events-stale.d.ts +61 -0
  98. package/dist/mesh/mesh-events-utils.d.ts +75 -0
  99. package/dist/mesh/mesh-events.d.ts +6 -49
  100. package/dist/mesh/mesh-fast-forward.d.ts +81 -0
  101. package/dist/mesh/mesh-host-ownership.d.ts +29 -0
  102. package/dist/mesh/mesh-init.d.ts +128 -0
  103. package/dist/mesh/mesh-ledger-reconciliation.d.ts +23 -4
  104. package/dist/mesh/mesh-ledger.d.ts +77 -1
  105. package/dist/mesh/mesh-magi-status.d.ts +63 -0
  106. package/dist/mesh/mesh-missions.d.ts +189 -0
  107. package/dist/mesh/mesh-node-identity.d.ts +289 -0
  108. package/dist/mesh/mesh-queue-assignment.d.ts +113 -0
  109. package/dist/mesh/mesh-reconcile-loop.d.ts +65 -0
  110. package/dist/mesh/mesh-refine-batch.d.ts +68 -0
  111. package/dist/mesh/mesh-refine-gates.d.ts +428 -0
  112. package/dist/mesh/mesh-refine-status.d.ts +62 -0
  113. package/dist/mesh/mesh-review-inbox.d.ts +90 -0
  114. package/dist/mesh/mesh-routing.d.ts +70 -0
  115. package/dist/mesh/mesh-runtime-store.d.ts +450 -0
  116. package/dist/mesh/mesh-scheduling-runtime.d.ts +78 -0
  117. package/dist/mesh/mesh-task-inflight.d.ts +46 -0
  118. package/dist/mesh/mesh-task-stats.d.ts +49 -0
  119. package/dist/mesh/mesh-unresolved-forward-outbox.d.ts +30 -0
  120. package/dist/mesh/mesh-warmup-deadline.d.ts +68 -0
  121. package/dist/mesh/mesh-work-queue.d.ts +300 -5
  122. package/dist/mesh/preview-freshness.d.ts +18 -0
  123. package/dist/mesh/refine-config.d.ts +216 -0
  124. package/dist/mesh/worktree-bootstrap-config.d.ts +145 -0
  125. package/dist/providers/acp-provider-instance.d.ts +5 -0
  126. package/dist/providers/approval-utils.d.ts +20 -0
  127. package/dist/providers/chat-message-normalization.d.ts +31 -1
  128. package/dist/providers/cli-provider-instance.d.ts +252 -3
  129. package/dist/providers/contracts.d.ts +149 -6
  130. package/dist/providers/external-sources.d.ts +71 -0
  131. package/dist/providers/manual-attendance.d.ts +63 -0
  132. package/dist/providers/native-history/antigravity-cli-transcript.d.ts +100 -0
  133. package/dist/providers/native-history/claude-cli-transcript.d.ts +70 -0
  134. package/dist/providers/native-history/codex-cli-transcript.d.ts +73 -0
  135. package/dist/providers/native-history/constants.d.ts +12 -0
  136. package/dist/providers/native-history/dispatcher.d.ts +27 -0
  137. package/dist/providers/native-history/hermes-cli-transcript.d.ts +30 -0
  138. package/dist/providers/native-history/index.d.ts +13 -0
  139. package/dist/providers/provider-instance-manager.d.ts +29 -0
  140. package/dist/providers/provider-instance.d.ts +23 -1
  141. package/dist/providers/provider-loader.d.ts +26 -4
  142. package/dist/providers/provider-trust.d.ts +31 -0
  143. package/dist/providers/read-chat-contract.d.ts +29 -0
  144. package/dist/providers/sdk/v1/builders/acp/detect-status.d.ts +68 -0
  145. package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +86 -0
  146. package/dist/providers/sdk/v1/builders/cli/parse-approval-squash.d.ts +59 -0
  147. package/dist/providers/sdk/v1/builders/cli/parse-approval.d.ts +65 -0
  148. package/dist/providers/sdk/v1/builders/cli/parse-session.d.ts +91 -0
  149. package/dist/providers/sdk/v1/builders/cli/visible-region.d.ts +42 -0
  150. package/dist/providers/sdk/v1/fixture-tooling/format.d.ts +126 -0
  151. package/dist/providers/sdk/v1/fixture-tooling/index.d.ts +8 -0
  152. package/dist/providers/sdk/v1/fixture-tooling/replay.d.ts +38 -0
  153. package/dist/providers/sdk/v1/index.d.ts +30 -0
  154. package/dist/providers/sdk/v1/sandbox/README-design.d.ts +193 -0
  155. package/dist/providers/sdk/v1/sandbox/require-whitelist.d.ts +74 -0
  156. package/dist/providers/sdk/v1/sandbox/script-runner.d.ts +98 -0
  157. package/dist/providers/sdk/v1/types/cli/index.d.ts +277 -0
  158. package/dist/providers/sdk/v1/types/common/index.d.ts +203 -0
  159. package/dist/providers/sdk/v1/validators/index.d.ts +5 -0
  160. package/dist/providers/sdk/v1/validators/manifest.d.ts +40 -0
  161. package/dist/providers/sdk/v1/validators/taint.d.ts +52 -0
  162. package/dist/providers/spec/adapter.d.ts +91 -0
  163. package/dist/providers/spec/cli-adapter.d.ts +223 -0
  164. package/dist/providers/spec/evaluator.d.ts +45 -0
  165. package/dist/providers/spec/fsm-driver.d.ts +422 -0
  166. package/dist/providers/spec/fsm-evaluator.d.ts +76 -0
  167. package/dist/providers/spec/fsm-loader.d.ts +14 -0
  168. package/dist/providers/spec/fsm-types.d.ts +174 -0
  169. package/dist/providers/spec/native-history-executor.d.ts +41 -0
  170. package/dist/providers/spec/pre-launch-trust.d.ts +16 -0
  171. package/dist/providers/spec/route.d.ts +4 -0
  172. package/dist/providers/spec/types.d.ts +222 -0
  173. package/dist/providers/status-monitor.d.ts +7 -7
  174. package/dist/providers/transcript-v2.d.ts +176 -0
  175. package/dist/providers/types/interactive-prompt.d.ts +84 -0
  176. package/dist/providers/working-dir.d.ts +17 -0
  177. package/dist/repo-mesh-types.d.ts +509 -13
  178. package/dist/runtime-defaults.d.ts +2 -0
  179. package/dist/sessions/registry.d.ts +6 -0
  180. package/dist/shared-types-extra.d.ts +2 -4
  181. package/dist/shared-types.d.ts +59 -2
  182. package/dist/status/normalize.d.ts +1 -1
  183. package/dist/status/normalize.js +1 -0
  184. package/dist/status/normalize.js.map +1 -1
  185. package/dist/status/normalize.mjs +1 -0
  186. package/dist/status/normalize.mjs.map +1 -1
  187. package/dist/status/reporter.d.ts +2 -0
  188. package/dist/status/snapshot.d.ts +26 -0
  189. package/dist/system/hash.d.ts +8 -0
  190. package/dist/system/load-better-sqlite3.d.ts +21 -0
  191. package/dist/types.d.ts +5 -0
  192. package/package.json +7 -3
  193. package/src/agent-stream/poller.ts +2 -3
  194. package/src/agent-stream/provider-adapter.ts +1 -1
  195. package/src/boot/daemon-lifecycle.ts +63 -12
  196. package/src/boot/process-hardening.ts +89 -0
  197. package/src/build-info.ts +73 -0
  198. package/src/chat/source-machine.ts +534 -0
  199. package/src/chat/source-resolver.ts +0 -0
  200. package/src/chat/subscription-updates.ts +20 -1
  201. package/src/cli-adapter-types.d.ts +3 -1
  202. package/src/cli-adapter-types.ts +68 -2
  203. package/src/cli-adapters/cli-script-runner.ts +421 -0
  204. package/src/cli-adapters/cli-state-engine.ts +1246 -0
  205. package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
  206. package/src/cli-adapters/provider-cli-adapter.ts +914 -1144
  207. package/src/cli-adapters/provider-cli-parse.d.ts +2 -0
  208. package/src/cli-adapters/provider-cli-parse.ts +19 -0
  209. package/src/cli-adapters/provider-cli-runtime.ts +56 -7
  210. package/src/cli-adapters/provider-cli-shared.d.ts +4 -0
  211. package/src/cli-adapters/provider-cli-shared.ts +121 -11
  212. package/src/cli-adapters/pty-transport.ts +2 -1
  213. package/src/cli-adapters/pty-write-chunking.ts +106 -0
  214. package/src/cli-adapters/raw-terminal-io.ts +252 -0
  215. package/src/cli-adapters/resolve-executable.ts +204 -0
  216. package/src/cli-adapters/session-host-transport.ts +2 -1
  217. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +63 -29
  218. package/src/cli-adapters/terminal-backends/types.ts +10 -3
  219. package/src/cli-adapters/terminal-screen.ts +16 -81
  220. package/src/commands/WINDOWS-UPGRADE-LOCK-FAILURE.md +198 -0
  221. package/src/commands/chat-commands-debug-bundle.ts +398 -0
  222. package/src/commands/chat-commands-read.ts +2447 -0
  223. package/src/commands/chat-commands-scope.ts +54 -0
  224. package/src/commands/chat-commands-shared.ts +114 -0
  225. package/src/commands/chat-commands-write.ts +891 -0
  226. package/src/commands/chat-commands.ts +19 -1841
  227. package/src/commands/cli-manager.ts +572 -27
  228. package/src/commands/handler.ts +841 -2
  229. package/src/commands/high-family/index.ts +28 -0
  230. package/src/commands/high-family/mesh-coordinator-launch.ts +678 -0
  231. package/src/commands/high-family/mesh-events.ts +80 -0
  232. package/src/commands/high-family/mesh-status.ts +714 -0
  233. package/src/commands/high-family/types.ts +76 -0
  234. package/src/commands/low-family/coordinator-prompt.ts +72 -0
  235. package/src/commands/low-family/daemon-lifecycle.ts +107 -0
  236. package/src/commands/low-family/diagnostics.ts +57 -0
  237. package/src/commands/low-family/index.ts +37 -0
  238. package/src/commands/low-family/mesh-ledger.ts +62 -0
  239. package/src/commands/low-family/mesh-node-logs.ts +87 -0
  240. package/src/commands/low-family/notification.ts +116 -0
  241. package/src/commands/low-family/refine-config.ts +106 -0
  242. package/src/commands/low-family/session-host.ts +274 -0
  243. package/src/commands/low-family/spec-providerdev.ts +217 -0
  244. package/src/commands/low-family/status-meta.ts +112 -0
  245. package/src/commands/low-family/types.ts +39 -0
  246. package/src/commands/med-family/cli-agent.ts +238 -0
  247. package/src/commands/med-family/fast-forward.ts +230 -0
  248. package/src/commands/med-family/ide.ts +163 -0
  249. package/src/commands/med-family/index.ts +37 -0
  250. package/src/commands/med-family/mesh-crud.ts +1242 -0
  251. package/src/commands/med-family/mesh-host-pairing.ts +234 -0
  252. package/src/commands/med-family/mesh-queue.ts +167 -0
  253. package/src/commands/med-family/mesh-restart.ts +92 -0
  254. package/src/commands/med-family/types.ts +143 -0
  255. package/src/commands/mesh-coordinator.ts +334 -124
  256. package/src/commands/router.ts +2846 -3484
  257. package/src/commands/stream-commands.ts +8 -0
  258. package/src/commands/upgrade-helper.ts +310 -45
  259. package/src/config/chat-history.ts +493 -24
  260. package/src/config/config.ts +12 -0
  261. package/src/config/mesh-config.ts +593 -22
  262. package/src/config/mesh-json-config.ts +376 -0
  263. package/src/config/recent-activity.ts +8 -2
  264. package/src/config/repo-settings.ts +111 -0
  265. package/src/daemon/dev-auto-implement.ts +3 -2
  266. package/src/daemon/dev-cli-debug.ts +10 -1
  267. package/src/daemon/dev-server.ts +0 -541
  268. package/src/detection/cli-detector.ts +28 -9
  269. package/src/detection/ide-detector.ts +55 -16
  270. package/src/detection/win32-ide-version.ts +106 -0
  271. package/src/git/change-impact-config.ts +354 -0
  272. package/src/git/git-commands.ts +59 -15
  273. package/src/git/git-diff.ts +81 -11
  274. package/src/git/git-executor.ts +12 -0
  275. package/src/git/git-status.ts +767 -48
  276. package/src/git/git-types.ts +14 -62
  277. package/src/git/git-worktree.ts +261 -4
  278. package/src/git/index.ts +17 -0
  279. package/src/index.ts +196 -12
  280. package/src/installer.d.ts +1 -1
  281. package/src/installer.ts +8 -6
  282. package/src/ipc/local-ipc-server.ts +278 -0
  283. package/src/launch.d.ts +1 -1
  284. package/src/launch.ts +37 -28
  285. package/src/logging/async-batch-writer.ts +55 -0
  286. package/src/logging/command-log.ts +7 -5
  287. package/src/logging/log-redactor.ts +100 -0
  288. package/src/logging/log-tail-reader.ts +341 -0
  289. package/src/logging/logger.ts +14 -7
  290. package/src/mesh/contracts.ts +338 -0
  291. package/src/mesh/coordinator-prompt.ts +415 -34
  292. package/src/mesh/coordinator-registry.ts +121 -0
  293. package/src/mesh/mesh-active-work.ts +645 -0
  294. package/src/mesh/mesh-clone-grace.ts +68 -0
  295. package/src/mesh/mesh-coordinator-config.ts +97 -0
  296. package/src/mesh/mesh-delivery-policy.ts +315 -0
  297. package/src/mesh/mesh-event-classify.ts +51 -0
  298. package/src/mesh/mesh-event-forwarding.ts +1897 -0
  299. package/src/mesh/mesh-event-trace.ts +67 -0
  300. package/src/mesh/mesh-events-coordinator.ts +32 -0
  301. package/src/mesh/mesh-events-pending.ts +656 -0
  302. package/src/mesh/mesh-events-stale.ts +389 -0
  303. package/src/mesh/mesh-events-utils.ts +443 -0
  304. package/src/mesh/mesh-events.ts +33 -1035
  305. package/src/mesh/mesh-fast-forward.ts +843 -0
  306. package/src/mesh/mesh-host-ownership.ts +111 -0
  307. package/src/mesh/mesh-init.ts +350 -0
  308. package/src/mesh/mesh-ledger-reconciliation.ts +12 -7
  309. package/src/mesh/mesh-ledger.ts +656 -102
  310. package/src/mesh/mesh-magi-status.ts +223 -0
  311. package/src/mesh/mesh-missions.ts +449 -0
  312. package/src/mesh/mesh-node-identity.ts +1887 -0
  313. package/src/mesh/mesh-queue-assignment.ts +1924 -0
  314. package/src/mesh/mesh-reconcile-loop.ts +1997 -0
  315. package/src/mesh/mesh-refine-batch.ts +205 -0
  316. package/src/mesh/mesh-refine-gates.ts +1673 -0
  317. package/src/mesh/mesh-refine-status.ts +231 -0
  318. package/src/mesh/mesh-review-inbox.ts +307 -0
  319. package/src/mesh/mesh-routing.ts +291 -0
  320. package/src/mesh/mesh-runtime-store.ts +1853 -0
  321. package/src/mesh/mesh-scheduling-runtime.ts +198 -0
  322. package/src/mesh/mesh-task-inflight.ts +70 -0
  323. package/src/mesh/mesh-task-stats.ts +161 -0
  324. package/src/mesh/mesh-unresolved-forward-outbox.ts +185 -0
  325. package/src/mesh/mesh-warmup-deadline.ts +152 -0
  326. package/src/mesh/mesh-work-queue.ts +1254 -141
  327. package/src/mesh/preview-freshness.ts +118 -0
  328. package/src/mesh/refine-config.ts +423 -0
  329. package/src/mesh/worktree-bootstrap-config.ts +450 -0
  330. package/src/providers/acp-provider-instance.ts +43 -10
  331. package/src/providers/approval-utils.d.ts +5 -0
  332. package/src/providers/approval-utils.ts +57 -5
  333. package/src/providers/chat-message-normalization.ts +92 -4
  334. package/src/providers/cli-provider-instance.ts +2001 -106
  335. package/src/providers/contracts.d.ts +55 -0
  336. package/src/providers/contracts.ts +160 -6
  337. package/src/providers/extension-provider-instance.ts +12 -7
  338. package/src/providers/external-sources.ts +218 -0
  339. package/src/providers/ide-provider-instance.ts +35 -12
  340. package/src/providers/manual-attendance.ts +85 -0
  341. package/src/providers/native-history/antigravity-cli-transcript.ts +643 -0
  342. package/src/providers/native-history/claude-cli-transcript.ts +396 -0
  343. package/src/providers/native-history/codex-cli-transcript.ts +479 -0
  344. package/src/providers/native-history/constants.ts +19 -0
  345. package/src/providers/native-history/dispatcher.ts +345 -0
  346. package/src/providers/native-history/hermes-cli-transcript.ts +260 -0
  347. package/src/providers/native-history/index.ts +30 -0
  348. package/src/providers/provider-instance-manager.ts +71 -0
  349. package/src/providers/provider-instance.ts +19 -1
  350. package/src/providers/provider-loader.ts +668 -50
  351. package/src/providers/provider-schema.ts +87 -14
  352. package/src/providers/provider-trust.ts +114 -0
  353. package/src/providers/read-chat-contract.ts +76 -16
  354. package/src/providers/sdk/README.md +49 -0
  355. package/src/providers/sdk/v1/builders/acp/detect-status.ts +144 -0
  356. package/src/providers/sdk/v1/builders/cli/detect-status.ts +313 -0
  357. package/src/providers/sdk/v1/builders/cli/parse-approval-squash.ts +158 -0
  358. package/src/providers/sdk/v1/builders/cli/parse-approval.ts +250 -0
  359. package/src/providers/sdk/v1/builders/cli/parse-session.ts +276 -0
  360. package/src/providers/sdk/v1/builders/cli/visible-region.ts +143 -0
  361. package/src/providers/sdk/v1/fixture-tooling/format.ts +130 -0
  362. package/src/providers/sdk/v1/fixture-tooling/index.ts +22 -0
  363. package/src/providers/sdk/v1/fixture-tooling/replay.ts +352 -0
  364. package/src/providers/sdk/v1/index.ts +152 -0
  365. package/src/providers/sdk/v1/sandbox/README-design.ts +195 -0
  366. package/src/providers/sdk/v1/sandbox/require-whitelist.ts +472 -0
  367. package/src/providers/sdk/v1/sandbox/script-runner.ts +150 -0
  368. package/src/providers/sdk/v1/schemas/cli/provider.schema.json +509 -0
  369. package/src/providers/sdk/v1/schemas/primitives/acp-session-protocol-v1.json +131 -0
  370. package/src/providers/sdk/v1/schemas/primitives/native-history-codex-rollout-v1.json +66 -0
  371. package/src/providers/sdk/v1/schemas/primitives/tui-approval-squash-v1.json +91 -0
  372. package/src/providers/sdk/v1/schemas/primitives/tui-assistant-block-v1.json +91 -0
  373. package/src/providers/sdk/v1/schemas/primitives/tui-cue-ordering-v1.json +47 -0
  374. package/src/providers/sdk/v1/schemas/primitives/tui-dispatch-order-v1.json +32 -0
  375. package/src/providers/sdk/v1/schemas/primitives/tui-footer-chrome-v1.json +42 -0
  376. package/src/providers/sdk/v1/schemas/primitives/tui-index-finder-v1.json +27 -0
  377. package/src/providers/sdk/v1/schemas/primitives/tui-modal-v1.json +125 -0
  378. package/src/providers/sdk/v1/schemas/primitives/tui-prompt-marker-v1.json +45 -0
  379. package/src/providers/sdk/v1/schemas/primitives/tui-session-id-extraction-v1.json +46 -0
  380. package/src/providers/sdk/v1/schemas/primitives/tui-settled-prompt-v1.json +71 -0
  381. package/src/providers/sdk/v1/schemas/primitives/tui-spinner-v1.json +83 -0
  382. package/src/providers/sdk/v1/schemas/primitives/tui-transcript-pty-v1.json +83 -0
  383. package/src/providers/sdk/v1/schemas/primitives/tui-visible-region-v1.json +57 -0
  384. package/src/providers/sdk/v1/schemas/primitives/tui-welcome-screen-v1.json +35 -0
  385. package/src/providers/sdk/v1/types/cli/index.ts +365 -0
  386. package/src/providers/sdk/v1/types/common/index.ts +229 -0
  387. package/src/providers/sdk/v1/validators/index.ts +19 -0
  388. package/src/providers/sdk/v1/validators/manifest.ts +110 -0
  389. package/src/providers/sdk/v1/validators/taint.ts +309 -0
  390. package/src/providers/spec/adapter.ts +235 -0
  391. package/src/providers/spec/cli-adapter.ts +1056 -0
  392. package/src/providers/spec/evaluator.ts +407 -0
  393. package/src/providers/spec/fsm-driver.ts +1410 -0
  394. package/src/providers/spec/fsm-evaluator.ts +272 -0
  395. package/src/providers/spec/fsm-loader.ts +120 -0
  396. package/src/providers/spec/fsm-types.ts +273 -0
  397. package/src/providers/spec/native-history-executor.ts +1143 -0
  398. package/src/providers/spec/pre-launch-trust.ts +104 -0
  399. package/src/providers/spec/route.ts +51 -0
  400. package/src/providers/spec/types.ts +262 -0
  401. package/src/providers/status-monitor.d.ts +7 -7
  402. package/src/providers/status-monitor.ts +37 -22
  403. package/src/providers/transcript-v2.ts +567 -0
  404. package/src/providers/types/interactive-prompt.ts +536 -0
  405. package/src/providers/version-archive.ts +64 -24
  406. package/src/providers/working-dir.ts +23 -0
  407. package/src/repo-mesh-types.ts +748 -14
  408. package/src/runtime-defaults.ts +39 -0
  409. package/src/sessions/registry.ts +6 -0
  410. package/src/shared-types-extra.ts +2 -4
  411. package/src/shared-types.d.ts +8 -0
  412. package/src/shared-types.ts +64 -1
  413. package/src/status/builders.ts +26 -6
  414. package/src/status/normalize.ts +2 -0
  415. package/src/status/reporter.ts +19 -1
  416. package/src/status/snapshot.ts +95 -26
  417. package/src/system/hash.ts +23 -0
  418. package/src/system/host-memory.ts +29 -12
  419. package/src/system/load-better-sqlite3.ts +68 -0
  420. package/src/types.ts +5 -0
  421. package/dist/cli-adapters/terminal-backends/xterm-backend.d.ts +0 -17
  422. package/dist/mesh/mesh-sync.d.ts +0 -53
  423. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +0 -16
  424. package/src/cli-adapters/terminal-backends/xterm-backend.ts +0 -97
  425. package/src/mesh/mesh-sync.ts +0 -111
@@ -18,13 +18,15 @@ import { loadConfig } from '../config/config.js';
18
18
  import { loadState, saveState } from '../config/state-store.js';
19
19
  import { getWorkspaceState, resolveLaunchDirectory } from '../config/workspaces.js';
20
20
  import { appendRecentActivity } from '../config/recent-activity.js';
21
+ import { shortHash } from '../system/hash.js';
22
+ import { unregisterMeshCoordinator, getCoordinatorForSession, listCoordinatorsForWorkspace } from '../mesh/coordinator-registry.js';
21
23
  import { upsertSavedProviderSession } from '../config/saved-sessions.js';
22
24
  import { buildLegacyModelModeSummaryMetadata, normalizeProviderSummaryMetadata } from '../providers/summary-metadata.js';
23
25
  import { CliProviderInstance } from '../providers/cli-provider-instance.js';
24
26
  import { AcpProviderInstance } from '../providers/acp-provider-instance.js';
25
27
  import type { ProviderInstanceManager } from '../providers/provider-instance-manager.js';
26
28
  import { ProviderLoader } from '../providers/provider-loader.js';
27
- import { normalizeInputEnvelope, type ProviderModule, type ProviderResumeCapability } from '../providers/contracts.js';
29
+ import { normalizeInputEnvelope, type MeshCoordinatorDelegatedWorkerIsolation, type ProviderModule, type ProviderResumeCapability } from '../providers/contracts.js';
28
30
  import { assertProviderSupportsDeclaredInput, assertTextOnlyInput } from '../providers/provider-input-support.js';
29
31
  import type { CliAdapter } from '../cli-adapter-types.js';
30
32
  import type { PtyTransportFactory } from '../cli-adapters/pty-transport.js';
@@ -80,6 +82,126 @@ export interface CliManagerDeps {
80
82
 
81
83
  type CommandResult = { success: boolean;[key: string]: unknown };
82
84
 
85
+ const BUSY_AGENT_STATUSES = new Set(['generating', 'running', 'streaming', 'starting', 'busy', 'waiting', 'waiting_approval', 'no_progress', 'long_generating']);
86
+ const ZERO_MESSAGE_STARTING_SEND_WAIT_MS = 2_000;
87
+
88
+ function normalizeAgentStatus(value: unknown): string {
89
+ return typeof value === 'string' ? value.trim().toLowerCase() : '';
90
+ }
91
+
92
+ function hasNonEmptyModalButtons(activeModal: unknown): boolean {
93
+ const buttons = (activeModal as any)?.buttons;
94
+ return Array.isArray(buttons) && buttons.some((button) => String(button || '').trim().length > 0);
95
+ }
96
+
97
+ function hasAdapterPendingResponse(adapter: any): boolean {
98
+ if (adapter?.isWaitingForResponse === true) return true;
99
+ if (adapter?.currentTurnScope) return true;
100
+ try {
101
+ if (typeof adapter?.isProcessing === 'function' && adapter.isProcessing()) return true;
102
+ } catch { /* defensive: send guard should not fail on diagnostics */ }
103
+ try {
104
+ const partial = typeof adapter?.getPartialResponse === 'function' ? adapter.getPartialResponse() : '';
105
+ if (typeof partial === 'string' && partial.trim()) return true;
106
+ } catch { /* defensive: missing partial means no pending evidence */ }
107
+ return false;
108
+ }
109
+
110
+ function countMessages(value: unknown): number {
111
+ return Array.isArray(value) ? value.length : 0;
112
+ }
113
+
114
+ function hasFinalAssistantMessage(value: unknown): boolean {
115
+ const messages = Array.isArray(value) ? value : [];
116
+ const last = messages[messages.length - 1] as any;
117
+ if (!last || last.role !== 'assistant') return false;
118
+ if (last.bubbleState === 'streaming') return false;
119
+ if (last.meta?.streaming === true) return false;
120
+ return typeof last.content === 'string' && last.content.trim().length > 0;
121
+ }
122
+
123
+ function hasZeroMessageStartingLaunch(adapter: any): boolean {
124
+ const adapterStatus = adapter?.getStatus?.({ allowParse: false }) ?? adapter?.getStatus?.() ?? {};
125
+ const parsedStatus = typeof adapter?.getScriptParsedStatus === 'function'
126
+ ? adapter.getScriptParsedStatus()
127
+ : {};
128
+ const adapterRawStatus = normalizeAgentStatus(adapterStatus?.status);
129
+ const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
130
+ if (adapterRawStatus !== 'starting') return false;
131
+ if (parsedRawStatus && parsedRawStatus !== 'starting' && parsedRawStatus !== 'generating') return false;
132
+ if (hasNonEmptyModalButtons(adapterStatus?.activeModal ?? adapterStatus?.modal ?? parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
133
+ if (countMessages(adapterStatus?.messages) > 0 || countMessages(parsedStatus?.messages) > 0) return false;
134
+ return !hasAdapterPendingResponse(adapter);
135
+ }
136
+
137
+ function hasCompletedStartingLaunch(adapter: any): boolean {
138
+ const adapterStatus = adapter?.getStatus?.({ allowParse: false }) ?? adapter?.getStatus?.() ?? {};
139
+ const adapterRawStatus = normalizeAgentStatus(adapterStatus?.status);
140
+ if (adapterRawStatus !== 'starting') return false;
141
+ if (hasAdapterPendingResponse(adapter)) return false;
142
+
143
+ const parsedStatus = typeof adapter?.getScriptParsedStatus === 'function'
144
+ ? adapter.getScriptParsedStatus()
145
+ : {};
146
+ const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
147
+ if (parsedRawStatus !== 'idle') return false;
148
+ if (hasNonEmptyModalButtons(adapterStatus?.activeModal ?? adapterStatus?.modal ?? parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
149
+ return hasFinalAssistantMessage(parsedStatus?.messages);
150
+ }
151
+
152
+ /**
153
+ * WTCLAIM (B): compare two workspace paths for node scoping. Normalizes separator
154
+ * style, trailing slashes, and case (Windows paths are case-insensitive; the
155
+ * coordinator-supplied node.workspace and the adapter's launch workingDir can
156
+ * differ only in those) so a base node and a worktree clone are still told apart
157
+ * by their distinct workspace roots.
158
+ */
159
+ function normalizeDirForCompare(dir?: string): string {
160
+ if (typeof dir !== 'string') return '';
161
+ return dir.trim().replace(/[\\/]+/g, '/').replace(/\/+$/, '').toLowerCase();
162
+ }
163
+
164
+ function shouldSuppressStaleParsedBusyStatus(adapterStatus: string, parsedStatus: any, adapter: any): boolean {
165
+ const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
166
+ if (!BUSY_AGENT_STATUSES.has(parsedRawStatus)) return false;
167
+ if (adapterStatus !== 'idle') return false;
168
+ if (hasNonEmptyModalButtons(parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
169
+ return !hasAdapterPendingResponse(adapter);
170
+ }
171
+
172
+ function getEffectiveAgentSendStatus(adapter: any): string {
173
+ const adapterStatus = normalizeAgentStatus(adapter?.getStatus?.({ allowParse: false })?.status ?? adapter?.getStatus?.()?.status);
174
+ if (adapterStatus === 'starting' && hasCompletedStartingLaunch(adapter)) return 'idle';
175
+ if (adapterStatus && adapterStatus !== 'idle') return adapterStatus;
176
+ if (adapterStatus !== 'idle') return adapterStatus;
177
+
178
+ if (typeof adapter?.getScriptParsedStatus !== 'function') return adapterStatus;
179
+ try {
180
+ const parsedStatus = adapter.getScriptParsedStatus();
181
+ const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
182
+ if (BUSY_AGENT_STATUSES.has(parsedRawStatus) && !shouldSuppressStaleParsedBusyStatus(adapterStatus, parsedStatus, adapter)) {
183
+ return parsedRawStatus;
184
+ }
185
+ } catch {
186
+ return adapterStatus;
187
+ }
188
+ return adapterStatus;
189
+ }
190
+
191
+ async function waitForZeroMessageStartingLaunch(adapter: any): Promise<boolean> {
192
+ try {
193
+ if (!hasZeroMessageStartingLaunch(adapter)) return false;
194
+ } catch {
195
+ return false;
196
+ }
197
+ await new Promise(resolve => setTimeout(resolve, ZERO_MESSAGE_STARTING_SEND_WAIT_MS));
198
+ try {
199
+ return hasZeroMessageStartingLaunch(adapter);
200
+ } catch {
201
+ return false;
202
+ }
203
+ }
204
+
83
205
  export interface CliTransportFactoryParams {
84
206
  runtimeId: string;
85
207
  providerType: string;
@@ -138,18 +260,19 @@ type CliStartOptions = {
138
260
  extraEnv?: Record<string, string>;
139
261
  };
140
262
 
141
- const COORDINATOR_DELEGATED_ENV_UNSETS: Record<string, string> = {
142
- ADHDEV_INLINE_MESH: '',
143
- ADHDEV_MCP_TRANSPORT: '',
144
- ADHDEV_MESH_ID: '',
145
- HERMES_EPHEMERAL_SYSTEM_PROMPT: '',
146
- };
263
+ const DEFAULT_COORDINATOR_DELEGATED_ENV_UNSETS = [
264
+ 'ADHDEV_INLINE_MESH',
265
+ 'ADHDEV_MCP_TRANSPORT',
266
+ 'ADHDEV_MESH_ID',
267
+ 'HERMES_EPHEMERAL_SYSTEM_PROMPT',
268
+ ] as const;
147
269
 
148
270
  export interface CoordinatorDelegatedCliLaunchOptionsInput {
149
271
  cliType: string;
150
272
  workspace: string;
151
273
  cliArgs?: string[];
152
274
  env?: Record<string, string>;
275
+ isolation?: MeshCoordinatorDelegatedWorkerIsolation;
153
276
  }
154
277
 
155
278
  export interface CoordinatorDelegatedCliLaunchOptions {
@@ -161,10 +284,25 @@ function hasCliArg(args: string[], flag: string): boolean {
161
284
  return args.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
162
285
  }
163
286
 
287
+ function hasConfigOverride(args: string[], key: string): boolean {
288
+ for (let index = 0; index < args.length; index += 1) {
289
+ const arg = args[index];
290
+ const next = args[index + 1];
291
+ if ((arg === '-c' || arg === '--config') && typeof next === 'string') {
292
+ if (next === key || next.startsWith(`${key}=`) || next.startsWith(`${key}.`)) return true;
293
+ }
294
+ if (arg.startsWith('--config=')) {
295
+ const value = arg.slice('--config='.length);
296
+ if (value === key || value.startsWith(`${key}=`) || value.startsWith(`${key}.`)) return true;
297
+ }
298
+ }
299
+ return false;
300
+ }
301
+
164
302
  function ensureEmptyDelegatedMcpConfig(workspace: string): string {
165
303
  const baseDir = path.join(os.tmpdir(), 'adhdev-delegated-agent-empty-mcp');
166
304
  mkdirSync(baseDir, { recursive: true });
167
- const workspaceHash = crypto.createHash('sha256').update(path.resolve(workspace || os.tmpdir())).digest('hex').slice(0, 16);
305
+ const workspaceHash = shortHash(path.resolve(workspace || os.tmpdir()));
168
306
  const filePath = path.join(baseDir, `${workspaceHash}.json`);
169
307
  writeFileSync(filePath, JSON.stringify({ mcpServers: {} }, null, 2), 'utf-8');
170
308
  return filePath;
@@ -173,12 +311,31 @@ function ensureEmptyDelegatedMcpConfig(workspace: string): string {
173
311
  export function buildCoordinatorDelegatedCliLaunchOptions(
174
312
  input: CoordinatorDelegatedCliLaunchOptionsInput,
175
313
  ): CoordinatorDelegatedCliLaunchOptions {
176
- const cliType = String(input.cliType || '').trim();
177
314
  const cliArgs = Array.isArray(input.cliArgs) ? [...input.cliArgs] : [];
178
- const env: Record<string, string> = { ...(input.env || {}), ...COORDINATOR_DELEGATED_ENV_UNSETS };
315
+ const env: Record<string, string> = { ...(input.env || {}) };
316
+ const envUnsets = new Set<string>(DEFAULT_COORDINATOR_DELEGATED_ENV_UNSETS);
317
+ for (const key of input.isolation?.env?.unset || []) {
318
+ if (typeof key === 'string' && key.trim()) envUnsets.add(key.trim());
319
+ }
320
+ for (const key of envUnsets) env[key] = '';
179
321
 
180
- if (cliType === 'claude-cli' && !hasCliArg(cliArgs, '--mcp-config')) {
181
- cliArgs.unshift('--mcp-config', ensureEmptyDelegatedMcpConfig(input.workspace));
322
+ for (const rule of input.isolation?.args || []) {
323
+ if (!rule || typeof rule !== 'object') continue;
324
+ if (rule.mode === 'empty_mcp_config') {
325
+ if (rule.flag && !hasCliArg(cliArgs, rule.flag)) {
326
+ cliArgs.unshift(rule.flag, ensureEmptyDelegatedMcpConfig(input.workspace));
327
+ }
328
+ if (rule.strictFlag && !hasCliArg(cliArgs, rule.strictFlag)) {
329
+ cliArgs.unshift(rule.strictFlag);
330
+ }
331
+ continue;
332
+ }
333
+ if (rule.mode === 'config_override') {
334
+ const key = String(rule.dedupeKey || rule.key || '').trim();
335
+ const flag = String(rule.flag || '').trim();
336
+ if (!key || !flag || hasConfigOverride(cliArgs, key)) continue;
337
+ cliArgs.unshift(flag, `${rule.key}=${rule.value}`);
338
+ }
182
339
  }
183
340
 
184
341
  return { cliArgs, env };
@@ -212,6 +369,18 @@ function expandResumeArgs(template: string[] | undefined, sessionId: string): st
212
369
  return template.map((part) => part === '{{id}}' ? sessionId : part);
213
370
  }
214
371
 
372
+ /**
373
+ * Expand a provider's `modelLaunchArgs` template with the requested model, mirroring
374
+ * expandResumeArgs. `{{model}}` → the trimmed model string. Returns undefined when
375
+ * there is no template or no model (a model request without a template is a no-op —
376
+ * see startSession, where the caller logs the skip). MAGI kind-panel model axis.
377
+ */
378
+ function expandModelLaunchArgs(template: string[] | undefined, model: string | undefined): string[] | undefined {
379
+ const m = typeof model === 'string' ? model.trim() : '';
380
+ if (!m || !Array.isArray(template) || template.length === 0) return undefined;
381
+ return template.map((part) => part === '{{model}}' ? m : part);
382
+ }
383
+
215
384
  function readSubcommandSessionId(args: string[], subcommands: string[]): string | undefined {
216
385
  const resumeIndex = args.findIndex((arg) => subcommands.includes(arg));
217
386
  if (resumeIndex < 0) return undefined;
@@ -255,10 +424,14 @@ function detectExplicitProviderSessionId(
255
424
 
256
425
  const subcommands = resume?.sessionIdFromSubcommand;
257
426
  if (Array.isArray(subcommands) && subcommands.length > 0) {
427
+ const hasResumeSubcommand = args.some((arg) => subcommands.includes(arg));
258
428
  const subcommandSessionId = readSubcommandSessionId(args, subcommands);
259
429
  if (subcommandSessionId) {
260
430
  return { providerSessionId: subcommandSessionId, launchMode: 'resume' };
261
431
  }
432
+ if (hasResumeSubcommand) {
433
+ return { launchMode: 'resume' };
434
+ }
262
435
  }
263
436
 
264
437
  return { launchMode: 'manual' };
@@ -292,6 +465,12 @@ export function resolveCliSessionBinding(
292
465
  launchMode: explicit.launchMode,
293
466
  };
294
467
  }
468
+ if (explicit.launchMode === 'resume') {
469
+ return {
470
+ cliArgs: baseArgs,
471
+ launchMode: 'resume',
472
+ };
473
+ }
295
474
  if (explicit.launchMode === 'manual' && hasArg(baseArgs || [], ['--session-id'])) {
296
475
  return {
297
476
  cliArgs: baseArgs,
@@ -435,7 +614,9 @@ export class DaemonCliManager {
435
614
  providerSessionId,
436
615
  attachExisting,
437
616
  );
438
- return new ProviderCliAdapter(resolvedProvider as CliProviderModule, workingDir, cliArgs, extraEnv || {}, transportFactory);
617
+ const adapter = new ProviderCliAdapter(resolvedProvider as CliProviderModule, workingDir, cliArgs, extraEnv || {}, transportFactory);
618
+ if (providerSessionId) adapter.updateRuntimeMeta({ providerSessionId });
619
+ return adapter;
439
620
  }
440
621
 
441
622
  throw new Error(`No CLI provider found for '${cliType}'. Create a provider.js in providers/cli/${cliType}/`);
@@ -457,6 +638,7 @@ export class DaemonCliManager {
457
638
  this.deps.removeAgentTracking(key);
458
639
  sessionRegistry?.unregisterByInstanceKey(key);
459
640
  instanceManager?.removeInstance(key);
641
+ unregisterMeshCoordinator(key);
460
642
  LOG.info('CLI', `🧹 Auto-cleaned ${status.status} CLI: ${cliType}`);
461
643
  this.deps.onStatusChange();
462
644
  }
@@ -516,6 +698,18 @@ export class DaemonCliManager {
516
698
  transport: 'pty',
517
699
  adapterKey: key,
518
700
  instanceKey: key,
701
+ workspace: resolvedDir,
702
+ // attachExisting === true means we're restoring an already-spawned
703
+ // hosted runtime after a daemon restart, not starting a fresh PTY.
704
+ // The real spawn time is in the past and we don't have it on the
705
+ // restored descriptor; pinning spawnedAtMs to Date.now() in that
706
+ // case would push the native-history session-floor cutoff past
707
+ // every existing transcript file, so the agy/hermes/claude reader
708
+ // would return null even though the transcript on disk is fresh.
709
+ // 0 disables the floor for this session — recent_window_ms in the
710
+ // spec still bounds how far back we look. Fresh launches still
711
+ // get a proper floor so prior-session leak protection holds.
712
+ spawnedAtMs: attachExisting ? 0 : Date.now(),
519
713
  });
520
714
  } catch (spawnErr: any) {
521
715
  LOG.error('CLI', `[${cliType}] Spawn failed: ${spawnErr?.message}`);
@@ -524,6 +718,37 @@ export class DaemonCliManager {
524
718
  }
525
719
 
526
720
  this.adapters.set(key, cliInstance.getAdapter());
721
+
722
+ // WTDISPATCH (no_node_binding): a coordinator-launched worker carries its mesh node
723
+ // binding on the CLI-instance settings, but the session-host RECORD meta was never
724
+ // stamped with it — `updateRuntimeSettings` only mutates in-memory runtime settings and
725
+ // `updateRuntimeMeta` was only ever called with providerSessionId. So mesh_cleanup_sessions
726
+ // matched these worker sessions to a node by workspace ALONE
727
+ // (`live_session_matched_by_workspace_only_no_node_binding`), which on a daemon hosting
728
+ // sibling worktree nodes cannot tell two co-located clones apart. Push the launch-time node
729
+ // binding to the record meta so the record is 1:1 bound to its node (the spawned pty exists
730
+ // by now, so updateMeta reaches the session-host store). Best-effort; guarded.
731
+ const launchMeshNodeId = typeof settings?.meshNodeId === 'string' ? settings.meshNodeId.trim() : '';
732
+ const launchMeshNodeFor = typeof settings?.meshNodeFor === 'string' ? settings.meshNodeFor.trim() : '';
733
+ // Auto-launch attribution: the queue stamps the task id it auto-launched this
734
+ // worker FOR (autoLaunchedForQueueTaskId). Mirror it onto the record meta so a
735
+ // later cleanup (e.g. MAGI post-review auto-cleanup) can verify a session was
736
+ // auto-launched for a SPECIFIC replica task before stopping/deleting it — a reused
737
+ // idle session never carries this marker, so it is preserved.
738
+ const launchAutoLaunchedForQueueTaskId = typeof settings?.autoLaunchedForQueueTaskId === 'string'
739
+ ? settings.autoLaunchedForQueueTaskId.trim()
740
+ : '';
741
+ if (launchMeshNodeId || launchMeshNodeFor || launchAutoLaunchedForQueueTaskId) {
742
+ try {
743
+ cliInstance.getAdapter().updateRuntimeMeta?.({
744
+ ...(launchMeshNodeId ? { meshNodeId: launchMeshNodeId } : {}),
745
+ ...(launchMeshNodeFor ? { meshNodeFor: launchMeshNodeFor } : {}),
746
+ ...(settings?.launchedByCoordinator === true ? { launchedByCoordinator: true } : {}),
747
+ ...(launchAutoLaunchedForQueueTaskId ? { autoLaunchedForQueueTaskId: launchAutoLaunchedForQueueTaskId } : {}),
748
+ });
749
+ } catch { /* best-effort — record-meta stamp is cleanup hygiene, not on the dispatch path */ }
750
+ }
751
+
527
752
  this.startCliExitMonitor(key, cliType);
528
753
  }
529
754
 
@@ -556,6 +781,24 @@ export class DaemonCliManager {
556
781
 
557
782
  // Create UUID-based key (allows separate instances even for same type+dir)
558
783
  const key = crypto.randomUUID();
784
+
785
+ // (3) Session-anchored mesh routing: when launching a mesh COORDINATOR session
786
+ // (settings.meshCoordinatorFor set), expose this session's OWN runtime id to its MCP
787
+ // server via env. The MCP server is spawned by the CLI as a child and inherits this
788
+ // env, so the MCP layer can stamp ADHDEV_COORDINATOR_SESSION_ID as the originating
789
+ // coordinator on every dispatch (→ MeshContext.coordinatorSessionId → worker
790
+ // meshCoordinatorSessionId → completion targetCoordinatorSessionId → strict route).
791
+ // `key` IS the instance id findLiveCoordinators matches on, so the stamp and the live
792
+ // session agree. Re-applied on every (re)launch, so it always reflects the current id;
793
+ // a stale value only survives if the CLI process outlives a daemon restart, in which
794
+ // case routing falls back to the daemon level (no wedge — see mesh-reconcile-loop).
795
+ {
796
+ const coordinatorMeshId = (options?.settingsOverride as Record<string, unknown> | undefined)?.meshCoordinatorFor;
797
+ if (typeof coordinatorMeshId === 'string' && coordinatorMeshId.trim()) {
798
+ options = { ...options, extraEnv: { ...(options?.extraEnv || {}), ADHDEV_COORDINATOR_SESSION_ID: key } };
799
+ }
800
+ }
801
+
559
802
  const sessionRegistry = this.deps.getSessionRegistry?.() || null;
560
803
 
561
804
  // ─── ACP category handle ───
@@ -589,6 +832,7 @@ export class DaemonCliManager {
589
832
  transport: 'acp',
590
833
  adapterKey: key,
591
834
  instanceKey: key,
835
+ workspace: resolvedDir,
592
836
  });
593
837
 
594
838
  // Register ACP entry in adapter map (getStatus queries from acpInstance in real-time)
@@ -663,8 +907,23 @@ export class DaemonCliManager {
663
907
  console.log(colorize('cyan', ` 📦 Using provider: ${provider.name} (${provider.type})`));
664
908
  }
665
909
 
910
+ // ─── Model axis (MAGI kind-panel): expand initialModel → launch args ───
911
+ // For a plain CLI provider the model is selected at spawn time via the manifest's
912
+ // modelLaunchArgs template ('{{model}}' → the requested model). ACP providers took
913
+ // the setConfigOption path above and never reach here. A provider with no template,
914
+ // or no requested model, is a no-op — model selection is best-effort and must never
915
+ // fail a launch. The model args are prepended so a caller's explicit cliArgs (e.g. a
916
+ // resume flag) still win positionally where order matters.
917
+ const modelLaunchArgs = expandModelLaunchArgs(provider?.modelLaunchArgs, initialModel);
918
+ const cliArgsWithModel = modelLaunchArgs
919
+ ? [...modelLaunchArgs, ...(cliArgs || [])]
920
+ : cliArgs;
921
+ if (initialModel && !modelLaunchArgs) {
922
+ LOG.warn('CLI', `[${normalizedType}] initialModel='${initialModel}' requested but provider declares no modelLaunchArgs template — launching without model selection.`);
923
+ }
924
+
666
925
  // ─── Resolve launch options → provider session binding ───
667
- const sessionBinding = resolveCliSessionBinding(provider, normalizedType, cliArgs, options?.resumeSessionId);
926
+ const sessionBinding = resolveCliSessionBinding(provider, normalizedType, cliArgsWithModel, options?.resumeSessionId);
668
927
  const resolvedCliArgs = sessionBinding.cliArgs;
669
928
 
670
929
  // If InstanceManager exists, manage as CliProviderInstance unified
@@ -783,6 +1042,7 @@ export class DaemonCliManager {
783
1042
  this.deps.removeAgentTracking(key);
784
1043
  this.deps.getSessionRegistry?.()?.unregisterByInstanceKey(key);
785
1044
  this.deps.getInstanceManager()?.removeInstance(key);
1045
+ unregisterMeshCoordinator(key);
786
1046
  LOG.info('CLI', `🛑 Agent stopped: ${adapter.cliType} in ${adapter.workingDir}`);
787
1047
  this.deps.onStatusChange();
788
1048
  } else {
@@ -792,6 +1052,7 @@ export class DaemonCliManager {
792
1052
  this.deps.getSessionRegistry?.()?.unregisterByInstanceKey(key);
793
1053
  im.removeInstance(key);
794
1054
  this.deps.removeAgentTracking(key);
1055
+ unregisterMeshCoordinator(key);
795
1056
  LOG.warn('CLI', `🧹 Force-removed orphan entry: ${key}`);
796
1057
  this.deps.onStatusChange();
797
1058
  }
@@ -819,6 +1080,29 @@ export class DaemonCliManager {
819
1080
  const restoredBindings = new Set<string>();
820
1081
  const managerTag = this.deps.hostedRuntimeManagerTag;
821
1082
 
1083
+ // CORDBADGE worker-overbind guard pre-pass: the workspace-scoped coordinator
1084
+ // rebind fallback (below) recovers a coordinator's mark when its runtimeId
1085
+ // changed across restart. But a delegated WORKER session that shares the
1086
+ // coordinator's workspace+cliType ALSO misses the exact by-id lookup, so the
1087
+ // fallback would wrongly stamp it with the lone registered coordinator's mesh
1088
+ // mark (the reported bug: a worker shown with role:coordinator after restart).
1089
+ // Two batch-level signals let the fallback refuse to mark a worker:
1090
+ // - restoredRuntimeIds: every runtimeId in this restore batch. If a
1091
+ // registered coordinator's own sessionId appears here, that coordinator is
1092
+ // being restored under its known id (the exact match binds it), so ANY
1093
+ // other same-workspace session is a worker — not the renamed coordinator.
1094
+ // - workspaceTypeCounts: how many sessions in the batch share a
1095
+ // workspace+cliType. >1 means we cannot tell the coordinator from a worker
1096
+ // even if the coordinator's id changed, so we stay unbound (ambiguous).
1097
+ const restoredRuntimeIds = new Set<string>();
1098
+ const workspaceTypeCounts = new Map<string, number>();
1099
+ for (const r of sessions) {
1100
+ if (!r?.runtimeId || !r?.cliType || !r?.workspace) continue;
1101
+ restoredRuntimeIds.add(r.runtimeId);
1102
+ const key = `${r.workspace}::${r.cliType}`;
1103
+ workspaceTypeCounts.set(key, (workspaceTypeCounts.get(key) || 0) + 1);
1104
+ }
1105
+
822
1106
  for (const record of sessions) {
823
1107
  if (!record?.runtimeId || !record?.cliType || !record?.workspace) continue;
824
1108
  if (!shouldRestoreHostedRuntime(record, managerTag)) {
@@ -852,6 +1136,74 @@ export class DaemonCliManager {
852
1136
  );
853
1137
  continue;
854
1138
  }
1139
+ // Re-establish the launch-time settings a fresh launch applies. startSession
1140
+ // seeds every new instance with { ...providerLoader.getSettings(type), ...override };
1141
+ // passing a bare {} here on restart silently dropped TWO launch settings, so a
1142
+ // restored session diverged from a freshly-launched one:
1143
+ // - autoApprove (a provider/machine setting from getSettings) → a restored
1144
+ // coordinator self-session lost auto-approve and re-prompted on every tool call.
1145
+ // - meshCoordinatorFor (the coordinator launch's settingsOverride) → the restored
1146
+ // session was no longer recognized as this daemon's live CLI coordinator by
1147
+ // findLiveCoordinators (so pending mesh events stopped draining into its PTY) nor
1148
+ // surfaced with the coordinator badge via settings. The persisted coordinator
1149
+ // registry (loaded on boot) is the source of truth to rebuild that mark.
1150
+ // Both restores are provider-agnostic — getSettings is keyed by provider type and the
1151
+ // registry mark is type-independent.
1152
+ const restoredSettings: Record<string, any> = { ...this.providerLoader.getSettings(normalizedType) };
1153
+ // Primary rebind: exact persisted-registry match by runtimeId (stable across
1154
+ // restart, see session-host runtimeId = runtimeRecord.sessionId).
1155
+ let coordinatorEntry = getCoordinatorForSession(record.runtimeId);
1156
+ // CORDBADGE fallback: the by-id match misses when a coordinator's runtime
1157
+ // re-attaches under a different runtimeId than the one it was registered with
1158
+ // (the registry survived, but its key no longer lines up). Without a rebind the
1159
+ // restored session silently loses meshCoordinatorFor → the coordinator badge and
1160
+ // selfIdentification block vanish and pending mesh events stop draining into its
1161
+ // PTY, and the only recovery is a manual coordinator restart. Recover the mark
1162
+ // from the persisted registry scoped to this exact workspace, but ONLY when it is
1163
+ // UNAMBIGUOUS: exactly one registered coordinator for this workspace AND its
1164
+ // cliType matches the restored session's type.
1165
+ //
1166
+ // WORKER-OVERBIND guard: "exactly one registered coordinator" is NOT enough —
1167
+ // a delegated worker session sharing the coordinator's workspace+cliType also
1168
+ // misses the by-id lookup, and the registry holding only the (single) real
1169
+ // coordinator does NOT stop the fallback from projecting that coordinator's
1170
+ // mark onto the worker record. So before adopting the mark we positively rule
1171
+ // the worker out:
1172
+ // (a) coordinatorPresentById — the registered coordinator's own sessionId is
1173
+ // in this restore batch, i.e. it is being restored under its known id and
1174
+ // the exact match already binds it. Then THIS record (which missed) is a
1175
+ // worker, not the renamed coordinator → do not rebind.
1176
+ // (b) siblingCount > 1 — more than one session shares this workspace+cliType,
1177
+ // so even if the coordinator's id changed we cannot tell it from a worker
1178
+ // → stay unbound (ambiguous).
1179
+ // Anything ambiguous stays unbound (we would rather miss a badge than
1180
+ // mis-attribute one).
1181
+ if (!coordinatorEntry?.meshId && record.workspace) {
1182
+ const workspaceCoordinators = listCoordinatorsForWorkspace(record.workspace)
1183
+ .filter(e => e.meshId && (!e.cliType || e.cliType === record.cliType));
1184
+ if (workspaceCoordinators.length === 1) {
1185
+ const candidate = workspaceCoordinators[0];
1186
+ const coordinatorPresentById = !!candidate.sessionId && restoredRuntimeIds.has(candidate.sessionId);
1187
+ const siblingCount = workspaceTypeCounts.get(`${record.workspace}::${record.cliType}`) || 1;
1188
+ if (!coordinatorPresentById && siblingCount === 1) {
1189
+ coordinatorEntry = candidate;
1190
+ LOG.info(
1191
+ 'CLI',
1192
+ `↻ Rebound coordinator mark by workspace for ${record.runtimeKey || record.runtimeId} (mesh ${candidate.meshId} @ ${record.workspace}); registry key did not match runtimeId`
1193
+ );
1194
+ } else {
1195
+ LOG.info(
1196
+ 'CLI',
1197
+ `↷ Skipping workspace coordinator rebind for ${record.runtimeKey || record.runtimeId} (${record.cliType} @ ${record.workspace}): ${coordinatorPresentById
1198
+ ? 'registered coordinator is restoring under its own id — this is a delegated worker'
1199
+ : `ambiguous (${siblingCount} sessions share this workspace+cliType)`}`
1200
+ );
1201
+ }
1202
+ }
1203
+ }
1204
+ if (coordinatorEntry?.meshId) {
1205
+ restoredSettings.meshCoordinatorFor = coordinatorEntry.meshId;
1206
+ }
855
1207
  try {
856
1208
  await this.registerCliInstance(
857
1209
  record.runtimeId,
@@ -860,7 +1212,7 @@ export class DaemonCliManager {
860
1212
  record.workspace,
861
1213
  record.cliArgs,
862
1214
  resolvedProvider,
863
- {},
1215
+ restoredSettings,
864
1216
  true,
865
1217
  {
866
1218
  providerSessionId: sessionBinding.providerSessionId,
@@ -899,18 +1251,36 @@ export class DaemonCliManager {
899
1251
  const adapter = this.adapters.get(ik);
900
1252
  if (adapter) return { adapter, key: ik };
901
1253
  }
902
- // 1. agentType + dir match
903
- if (opts?.dir) {
1254
+ // 1. agentType + dir match.
1255
+ // FAIL-CLOSED when an explicit instanceKey/targetSessionId was named (step 0) but
1256
+ // did not resolve: the caller pinned a SPECIFIC session, so healing by workspace must
1257
+ // not silently redirect the command into a co-located SIBLING worktree session. The
1258
+ // remote mesh relay (ipcDispatchToRemoteAgent) carries `dir: node.workspace` alongside
1259
+ // targetSessionId for the sessionless-scope case; when a session WAS named, that dir
1260
+ // fallback is the WTDISPATCH-FANOUT (a) leak — a stale/relaunched session_id would
1261
+ // dir-match whatever session lives in that workspace instead of failing. The sessionless
1262
+ // node-scoped path uses findMeshNodeAdapter, not this fallback, so gating dir on
1263
+ // !instanceKey loses no legitimate routing. Mirror step 2's fail-closed rule.
1264
+ if (opts?.dir && !opts?.instanceKey) {
904
1265
  for (const [k, a] of this.adapters) {
905
1266
  if (a.cliType === agentType && a.workingDir === opts.dir) {
906
1267
  return { adapter: a, key: k };
907
1268
  }
908
1269
  }
909
1270
  }
910
- // 2. Fuzzy match (returns first of multiple sessions — may be inaccurate)
911
- for (const [k, a] of this.adapters) {
912
- if (a.cliType === agentType) {
913
- return { adapter: a, key: k };
1271
+ // 2. Fuzzy match (returns first of multiple sessions — may be inaccurate).
1272
+ // FAIL-CLOSED: only when NO explicit instanceKey/targetSessionId was requested.
1273
+ // When a specific session WAS named (step 0) but is not hosted on this daemon,
1274
+ // falling back to the first same-cliType adapter silently redirects the command
1275
+ // into an UNRELATED session — e.g. a relayed/misrouted mesh send_chat lands in the
1276
+ // coordinator's own CLI session, echoing the dispatched task body back to the
1277
+ // coordinator (TASKECHO self-inject). Returning null instead makes the caller
1278
+ // surface an explicit "not running" error rather than mis-delivering the message.
1279
+ if (!opts?.instanceKey) {
1280
+ for (const [k, a] of this.adapters) {
1281
+ if (a.cliType === agentType) {
1282
+ return { adapter: a, key: k };
1283
+ }
914
1284
  }
915
1285
  }
916
1286
  return null;
@@ -925,6 +1295,40 @@ export class DaemonCliManager {
925
1295
  return adapter ? { adapter, key: ik } : null;
926
1296
  }
927
1297
 
1298
+ /**
1299
+ * WTCLAIM (B): resolve the adapter for a mesh dispatch that named a node
1300
+ * (meshContext.nodeId) but carried no explicit session. Matches by the
1301
+ * instance's bound mesh node id (settings.meshNodeId, falling back to the
1302
+ * sticky meshLastNodeId) first, then by the node workspace (workingDir).
1303
+ *
1304
+ * Unlike findAdapter's step-2 fuzzy fallback, this NEVER degrades to a
1305
+ * provider-only first-match: on a daemon hosting BOTH a base node and a
1306
+ * cloned worktree node (same daemonId), that fuzzy match could land a
1307
+ * worktree-targeted task on the base session. Returns null when no session
1308
+ * is bound to this node so the caller fails closed.
1309
+ */
1310
+ private findMeshNodeAdapter(agentType: string, nodeId: string, dir?: string): { adapter: CliAdapter; key: string } | null {
1311
+ const instanceManager = this.deps.getInstanceManager();
1312
+ const targetDir = normalizeDirForCompare(dir);
1313
+ let workspaceMatch: { adapter: CliAdapter; key: string } | null = null;
1314
+ for (const [k, a] of this.adapters) {
1315
+ if (a.cliType !== agentType) continue;
1316
+ const settings = (instanceManager?.getInstance(k) as any)?.getState?.()?.settings as Record<string, unknown> | undefined;
1317
+ const boundNodeId = (typeof settings?.meshNodeId === 'string' && settings.meshNodeId.trim())
1318
+ ? settings.meshNodeId.trim()
1319
+ : (typeof settings?.meshLastNodeId === 'string' ? settings.meshLastNodeId.trim() : '');
1320
+ // Exact node binding wins immediately (most precise).
1321
+ if (boundNodeId && boundNodeId === nodeId) return { adapter: a, key: k };
1322
+ // Workspace identity is the secondary signal for a session not (yet)
1323
+ // stamped with a node id — a base node and a worktree clone always have
1324
+ // distinct workspaces, so this still separates them.
1325
+ if (!workspaceMatch && targetDir && normalizeDirForCompare(a.workingDir) === targetDir) {
1326
+ workspaceMatch = { adapter: a, key: k };
1327
+ }
1328
+ }
1329
+ return workspaceMatch;
1330
+ }
1331
+
928
1332
  // ─── CLI command handling ────────────────────────────
929
1333
 
930
1334
  async handleCliCommand(cmd: string, args: any): Promise<CommandResult | null> {
@@ -955,6 +1359,8 @@ export class DaemonCliManager {
955
1359
  const launchSource = resolved.source;
956
1360
  if (!cliType) throw new Error('cliType required');
957
1361
 
1362
+ const providerType = this.providerLoader.resolveAlias(cliType);
1363
+ const provLookup = this.providerLoader.getMeta(providerType) as ProviderModule | undefined;
958
1364
  const settingsOverride = args?.settings && typeof args.settings === 'object' ? args.settings : undefined;
959
1365
  const delegatedLaunch = settingsOverride?.launchedByCoordinator === true
960
1366
  ? buildCoordinatorDelegatedCliLaunchOptions({
@@ -962,8 +1368,28 @@ export class DaemonCliManager {
962
1368
  workspace: dir,
963
1369
  cliArgs: args?.cliArgs,
964
1370
  env: args?.env,
1371
+ isolation: provLookup?.meshCoordinator?.delegatedWorkerIsolation,
965
1372
  })
966
1373
  : null;
1374
+ // Untrusted-provider gate: an external source that ships JS
1375
+ // hooks needs explicit user confirmation before its first
1376
+ // launch. Dashboards add `confirmExternalUntrusted: true` to
1377
+ // the launch args after showing the trust modal. Without
1378
+ // that ack we refuse to spawn and tell the caller why.
1379
+ const provMeta = provLookup as any;
1380
+ const provTrust = provMeta?._sourceTrust;
1381
+ if (provTrust === 'external-untrusted' && args?.confirmExternalUntrusted !== true) {
1382
+ return {
1383
+ success: false,
1384
+ error: 'untrusted_external_provider',
1385
+ provider: {
1386
+ type: provLookup?.type ?? cliType,
1387
+ sourceName: provMeta?._sourceName ?? null,
1388
+ trust: provTrust,
1389
+ },
1390
+ hint: 'Resend launch_cli with confirmExternalUntrusted=true after the user explicitly approves running JavaScript from this 3rd-party source.',
1391
+ };
1392
+ }
967
1393
  const started = await this.startSession(
968
1394
  cliType,
969
1395
  dir,
@@ -1030,6 +1456,45 @@ export class DaemonCliManager {
1030
1456
  this.deps.onStatusChange();
1031
1457
  return { success: true, id: found.key, mode };
1032
1458
  }
1459
+ case 'record_provider_pty': {
1460
+ const cliType = args?.type || args?.cliType;
1461
+ if (!cliType) {
1462
+ return { success: false, error: '`type` (provider type) is required', code: 'MISSING_TYPE' };
1463
+ }
1464
+ const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId : '';
1465
+ const dir = args?.dir || '';
1466
+ const found = (targetSessionId ? this.findAdapterBySessionId(targetSessionId) : null)
1467
+ || this.findAdapter(cliType, { instanceKey: targetSessionId, dir });
1468
+ if (!found) {
1469
+ return {
1470
+ success: false,
1471
+ error: `No running ${cliType} session. Launch one first (adhdev launch ${cliType}) or pass --target-session-id.`,
1472
+ code: 'NO_RUNNING_SESSION',
1473
+ };
1474
+ }
1475
+ const instance = this.deps.getInstanceManager()?.getInstance(found.key);
1476
+ if (!(instance instanceof CliProviderInstance)) {
1477
+ return { success: false, error: 'CLI instance not available', code: 'CLI_INSTANCE_NOT_FOUND' };
1478
+ }
1479
+ const adapter = instance.getAdapter();
1480
+ if (!adapter || typeof (adapter as any).getAccumulatedRawBuffer !== 'function') {
1481
+ return { success: false, error: 'Adapter does not expose PTY buffer', code: 'ADAPTER_NOT_RECORDABLE' };
1482
+ }
1483
+ const buffer = (adapter as any).getAccumulatedRawBuffer() as { text: string; droppedChars: number };
1484
+ const maxBytes = Number(args?.maxBytes) > 0 ? Number(args.maxBytes) : 262144;
1485
+ const truncated = buffer.text.length > maxBytes;
1486
+ const ptyBytes = truncated ? buffer.text.slice(-maxBytes) : buffer.text;
1487
+ return {
1488
+ success: true,
1489
+ cliType,
1490
+ sessionId: found.key,
1491
+ ptyBytes,
1492
+ bytes: ptyBytes.length,
1493
+ truncated,
1494
+ droppedChars: buffer.droppedChars,
1495
+ capturedAt: Date.now(),
1496
+ };
1497
+ }
1033
1498
  case 'restart_session': {
1034
1499
  const cliType = args?.cliType || args?.agentType || args?.ideType;
1035
1500
  const cfg = loadConfig();
@@ -1065,14 +1530,64 @@ export class DaemonCliManager {
1065
1530
  const action = args?.action;
1066
1531
  if (!agentType || !action) throw new Error('agentType and action required');
1067
1532
 
1068
- const found = this.findAdapter(agentType, {
1069
- dir: args?.dir,
1070
- instanceKey: args?.targetSessionId,
1071
- });
1533
+ // WTCLAIM (B): a mesh dispatch that named a node (meshContext.nodeId)
1534
+ // but resolved no explicit session must be scoped to THAT node's
1535
+ // session — never routed by findAdapter's provider-only fuzzy fallback,
1536
+ // which on a daemon hosting both a base node and a cloned worktree node
1537
+ // (same daemonId) could land a worktree task on the base session. Fail
1538
+ // closed when no session is bound to the node so the coordinator
1539
+ // launches/retries instead of mis-landing the work.
1540
+ const meshScopeNodeId = (() => {
1541
+ const mc = (args as any)?.meshContext;
1542
+ return mc && typeof mc === 'object' && typeof mc.nodeId === 'string' ? mc.nodeId.trim() : '';
1543
+ })();
1544
+ let found: { adapter: CliAdapter; key: string } | null;
1545
+ if (meshScopeNodeId && !args?.targetSessionId) {
1546
+ found = this.findMeshNodeAdapter(agentType, meshScopeNodeId, args?.dir);
1547
+ if (!found) {
1548
+ throw new Error(`No mesh worker session bound to node '${meshScopeNodeId}' for agent '${agentType}' on this daemon; refusing provider-only fuzzy match to avoid cross-node dispatch`);
1549
+ }
1550
+ } else {
1551
+ found = this.findAdapter(agentType, {
1552
+ dir: args?.dir,
1553
+ instanceKey: args?.targetSessionId,
1554
+ });
1555
+ }
1072
1556
  if (!found) throw new Error(`CLI agent not running: ${agentType}`);
1073
1557
  const { adapter, key } = found;
1074
1558
 
1075
1559
  if (action === 'send_chat') {
1560
+ let currentStatus = getEffectiveAgentSendStatus(adapter);
1561
+ if (currentStatus === 'starting' && await waitForZeroMessageStartingLaunch(adapter)) {
1562
+ currentStatus = 'idle';
1563
+ } else if (currentStatus === 'starting') {
1564
+ currentStatus = getEffectiveAgentSendStatus(adapter);
1565
+ }
1566
+ // Stamp mesh direct-dispatch assignment on the target
1567
+ // instance BEFORE sending the prompt so the completion
1568
+ // event has a routing marker by the time it fires.
1569
+ // mesh_send_task --direct ships meshContext for plain CLI
1570
+ // sessions that were never launched as mesh delegates.
1571
+ const meshContext = (args as any)?.meshContext;
1572
+ if (meshContext && typeof meshContext === 'object' && typeof meshContext.meshId === 'string' && meshContext.meshId) {
1573
+ const targetInstanceId = key;
1574
+ let stampResult: { stamped: boolean; reason?: string } | undefined;
1575
+ try {
1576
+ stampResult = this.deps.getInstanceManager()?.attachMeshAssignmentToInstance(targetInstanceId, {
1577
+ meshId: meshContext.meshId,
1578
+ ...(typeof meshContext.nodeId === 'string' && meshContext.nodeId ? { nodeId: meshContext.nodeId } : {}),
1579
+ ...(typeof meshContext.taskId === 'string' && meshContext.taskId ? { taskId: meshContext.taskId } : {}),
1580
+ ...(typeof meshContext.coordinatorDaemonId === 'string' && meshContext.coordinatorDaemonId ? { coordinatorDaemonId: meshContext.coordinatorDaemonId } : {}),
1581
+ });
1582
+ } catch { /* best-effort — stamping is a routing aid, not a hard requirement */ }
1583
+ // DOUBLE-DISPATCH stamp guard: the instance manager refused this stamp because
1584
+ // the SAME task is already running on another live session on this daemon.
1585
+ // Sending the prompt anyway would double-execute the task — fail closed so the
1586
+ // coordinator does not duplicate the work onto a second session.
1587
+ if (stampResult && stampResult.stamped === false && stampResult.reason === 'task_already_stamped_on_live_instance') {
1588
+ throw new Error(`Refusing duplicate mesh dispatch: task ${meshContext.taskId} is already being worked by a live session on this daemon`);
1589
+ }
1590
+ }
1076
1591
  const input = normalizeInputEnvelope(args?.input ? { input: args.input } : args);
1077
1592
  const provider = this.providerLoader.resolve(agentType) || this.providerLoader.getMeta(agentType);
1078
1593
  if (provider?.category === 'acp') {
@@ -1082,8 +1597,38 @@ export class DaemonCliManager {
1082
1597
  }
1083
1598
  const message = input.textFallback;
1084
1599
  if (!message) throw new Error('message required for send_chat');
1085
- await adapter.sendMessage(message);
1086
- return { success: true, status: 'generating' };
1600
+ // ARCH-REFACTOR R1: thread the dispatched task's id into the turn so the
1601
+ // worker's completion event is bound to THIS task (per-turn identity),
1602
+ // not the last-write-wins session scalar. Carried for both local and
1603
+ // remote (P2P-echoed meshContext) dispatch; absent for plain ad-hoc chat.
1604
+ const meshTaskId = (meshContext && typeof meshContext === 'object'
1605
+ && typeof (meshContext as any).taskId === 'string' && (meshContext as any).taskId.trim())
1606
+ ? (meshContext as any).taskId as string
1607
+ : undefined;
1608
+ const forceSend = args?.force === true || args?.forceSend === true;
1609
+ // Preserve the exact prior call shape when there is no taskId (plain
1610
+ // ad-hoc chat / non-mesh dispatch); only thread the per-turn taskId when
1611
+ // present, so existing non-mesh callers and their contracts are unchanged.
1612
+ if (forceSend && typeof (adapter as any).forceSendMessage === 'function') {
1613
+ if (meshTaskId) await (adapter as any).forceSendMessage(message, meshTaskId);
1614
+ else await (adapter as any).forceSendMessage(message);
1615
+ } else if (forceSend) {
1616
+ await adapter.sendMessage(message, meshTaskId ? { force: true, meshTaskId } : { force: true });
1617
+ } else if (meshTaskId) {
1618
+ await adapter.sendMessage(message, { meshTaskId });
1619
+ } else {
1620
+ await adapter.sendMessage(message);
1621
+ }
1622
+ const targetInstance = this.deps.getInstanceManager()?.getInstance(key) as
1623
+ | { recordAcknowledgedUserInput?: (input: unknown) => void }
1624
+ | undefined;
1625
+ targetInstance?.recordAcknowledgedUserInput?.(input);
1626
+ return {
1627
+ success: true,
1628
+ status: BUSY_AGENT_STATUSES.has(currentStatus) ? currentStatus : 'generating',
1629
+ ...(BUSY_AGENT_STATUSES.has(currentStatus) ? { queued: true, queuedReason: 'agent_runtime_busy' } : {}),
1630
+ ...(forceSend ? { forceSent: true, queued: false } : {}),
1631
+ };
1087
1632
  } else if (action === 'clear_history') {
1088
1633
  if (typeof adapter.clearHistory === 'function') adapter.clearHistory();
1089
1634
  return { success: true, cleared: true };