zidane 7.2.4 → 8.0.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 (376) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +34 -6
  3. package/dist/{acp-D-tVyUjw.js → acp-DQDhi2dj.js} +230 -77
  4. package/dist/acp-DQDhi2dj.js.map +1 -0
  5. package/dist/acp-cli.d.ts +1 -1
  6. package/dist/acp-cli.js +9 -66
  7. package/dist/acp-cli.js.map +1 -1
  8. package/dist/acp.d.ts +10 -590
  9. package/dist/acp.d.ts.map +1 -1
  10. package/dist/acp.js +3 -3
  11. package/dist/{agent-D0zZnGbt.js → agent-DSLNmBp0.js} +14401 -13214
  12. package/dist/agent-DSLNmBp0.js.map +1 -0
  13. package/dist/agent.d.ts +1 -1
  14. package/dist/agent.js +3 -2
  15. package/dist/aliasing-lVFbm81n.js +217 -0
  16. package/dist/aliasing-lVFbm81n.js.map +1 -0
  17. package/dist/{anthropic-C2aole0G.js → anthropic-CD00LhUP.js} +1442 -1264
  18. package/dist/anthropic-CD00LhUP.js.map +1 -0
  19. package/dist/artifacts-CV2hUKnL.js.map +1 -1
  20. package/dist/{atomic-write-B7zr1ddF.js → atomic-write-Ijgf2Mhc.js} +13 -3
  21. package/dist/atomic-write-Ijgf2Mhc.js.map +1 -0
  22. package/dist/call-tool-dHlbGOIY.js +70 -0
  23. package/dist/call-tool-dHlbGOIY.js.map +1 -0
  24. package/dist/chat/pure.d.ts +372 -6
  25. package/dist/chat/pure.d.ts.map +1 -1
  26. package/dist/chat/pure.js +35 -244
  27. package/dist/chat/pure.js.map +1 -1
  28. package/dist/{approvals-DKaJZYlm.js → chat-JPJJ8uu5.js} +15879 -14631
  29. package/dist/chat-JPJJ8uu5.js.map +1 -0
  30. package/dist/chat.d.ts +4 -6
  31. package/dist/chat.js +23 -12
  32. package/dist/cli-shared-C-YCaglL.js +159 -0
  33. package/dist/cli-shared-C-YCaglL.js.map +1 -0
  34. package/dist/client-core-BQSyDALT.d.ts +2115 -0
  35. package/dist/client-core-BQSyDALT.d.ts.map +1 -0
  36. package/dist/codemode-worker.d.ts +1 -0
  37. package/dist/codemode-worker.js +155 -0
  38. package/dist/codemode-worker.js.map +1 -0
  39. package/dist/codemode.d.ts +352 -0
  40. package/dist/codemode.d.ts.map +1 -0
  41. package/dist/codemode.js +5 -0
  42. package/dist/connection-Dr-Js4yZ.d.ts +28 -0
  43. package/dist/connection-Dr-Js4yZ.d.ts.map +1 -0
  44. package/dist/connection-loop-gdh22LTI.js +112 -0
  45. package/dist/connection-loop-gdh22LTI.js.map +1 -0
  46. package/dist/context-segments-BHKlagL6.js +1016 -0
  47. package/dist/context-segments-BHKlagL6.js.map +1 -0
  48. package/dist/{context-transport-T_6AYIvy.js → context-transport-DSMTUsK1.js} +3 -3
  49. package/dist/{context-transport-T_6AYIvy.js.map → context-transport-DSMTUsK1.js.map} +1 -1
  50. package/dist/contexts/daytona.d.ts +19 -8
  51. package/dist/contexts/daytona.d.ts.map +1 -1
  52. package/dist/contexts/daytona.js +1 -440
  53. package/dist/contexts/docker.d.ts +36 -7
  54. package/dist/contexts/docker.d.ts.map +1 -1
  55. package/dist/contexts/docker.js +1 -456
  56. package/dist/contexts/e2b.d.ts +68 -13
  57. package/dist/contexts/e2b.d.ts.map +1 -1
  58. package/dist/contexts/e2b.js +1 -382
  59. package/dist/contexts/sandbox.d.ts +1 -1
  60. package/dist/contexts/sandbox.js +5 -0
  61. package/dist/contexts/sandbox.js.map +1 -1
  62. package/dist/{contexts-CRE9pw07.js → contexts-d7UolYot.js} +1059 -524
  63. package/dist/contexts-d7UolYot.js.map +1 -0
  64. package/dist/contexts.d.ts +3 -3
  65. package/dist/contexts.js +1 -1
  66. package/dist/cred-keys-GpBg71cP.js +105 -0
  67. package/dist/cred-keys-GpBg71cP.js.map +1 -0
  68. package/dist/{auth-BG2rVKSt.js → credentials-JttEuRON.js} +55 -206
  69. package/dist/credentials-JttEuRON.js.map +1 -0
  70. package/dist/{cursor-iojRXHF9.js → cursor-CdjaQTBW.js} +3 -3
  71. package/dist/{cursor-iojRXHF9.js.map → cursor-CdjaQTBW.js.map} +1 -1
  72. package/dist/daemon-cli.d.ts +1 -1
  73. package/dist/daemon-cli.js +3 -2
  74. package/dist/daemon-cli.js.map +1 -1
  75. package/dist/daytona-DG3omipZ.js +513 -0
  76. package/dist/daytona-DG3omipZ.js.map +1 -0
  77. package/dist/defaults-Cx3zvDOV.js +23 -0
  78. package/dist/defaults-Cx3zvDOV.js.map +1 -0
  79. package/dist/dispatcher-BMlu2BmZ.js +4941 -0
  80. package/dist/dispatcher-BMlu2BmZ.js.map +1 -0
  81. package/dist/docker-BZhMMJUp.js +727 -0
  82. package/dist/docker-BZhMMJUp.js.map +1 -0
  83. package/dist/e2b-CEVOdRib.js +480 -0
  84. package/dist/e2b-CEVOdRib.js.map +1 -0
  85. package/dist/edit-utils-EGosADZq.js +234 -0
  86. package/dist/edit-utils-EGosADZq.js.map +1 -0
  87. package/dist/environment-8wsKE2Ln.js +214 -0
  88. package/dist/environment-8wsKE2Ln.js.map +1 -0
  89. package/dist/eval-BNcvle_J.js +1595 -0
  90. package/dist/eval-BNcvle_J.js.map +1 -0
  91. package/dist/eval.d.ts +2 -2
  92. package/dist/eval.js +1 -1489
  93. package/dist/executor-Cx5-8yqi.js +621 -0
  94. package/dist/executor-Cx5-8yqi.js.map +1 -0
  95. package/dist/extension-authoring-DyGGmq9V.js +385 -0
  96. package/dist/extension-authoring-DyGGmq9V.js.map +1 -0
  97. package/dist/extensions.d.ts +2 -2
  98. package/dist/extensions.js +3 -3
  99. package/dist/{fetch-url-DX75gpL0.js → fetch-url-Ct2TjDWt.js} +4 -4
  100. package/dist/{fetch-url-DX75gpL0.js.map → fetch-url-Ct2TjDWt.js.map} +1 -1
  101. package/dist/{format-BF745pLL.js → format-C2NQEVUH.js} +39 -2
  102. package/dist/format-C2NQEVUH.js.map +1 -0
  103. package/dist/format-bytes-C7tfc3Zm.js +55 -0
  104. package/dist/format-bytes-C7tfc3Zm.js.map +1 -0
  105. package/dist/headless-C_rNUuYC.js +960 -0
  106. package/dist/headless-C_rNUuYC.js.map +1 -0
  107. package/dist/headless.d.ts +1 -1
  108. package/dist/headless.js +1 -413
  109. package/dist/{history-search-C4bk1Wz_.js → history-search-CcYzF8-0.js} +7 -3
  110. package/dist/history-search-CcYzF8-0.js.map +1 -0
  111. package/dist/history-search-worker.d.ts +1 -1
  112. package/dist/history-search-worker.js +1 -1
  113. package/dist/host-D7NrI03V.js +378 -0
  114. package/dist/host-D7NrI03V.js.map +1 -0
  115. package/dist/host-signals-BcP5QKUD.js +118 -0
  116. package/dist/host-signals-BcP5QKUD.js.map +1 -0
  117. package/dist/identity-CwfO8K-t.js +26 -0
  118. package/dist/identity-CwfO8K-t.js.map +1 -0
  119. package/dist/{index-BgN1T3lk.d.ts → index-BGe1fQT7.d.ts} +10987 -9320
  120. package/dist/index-BGe1fQT7.d.ts.map +1 -0
  121. package/dist/index-BVQsIKGn.d.ts +448 -0
  122. package/dist/index-BVQsIKGn.d.ts.map +1 -0
  123. package/dist/{index-C6NEd5y_.d.ts → index-CHABJd-L.d.ts} +3386 -502
  124. package/dist/index-CHABJd-L.d.ts.map +1 -0
  125. package/dist/{index-C37_ppWv.d.ts → index-D6a7qXpg.d.ts} +2 -3
  126. package/dist/index-D6a7qXpg.d.ts.map +1 -0
  127. package/dist/index.d.ts +6 -6
  128. package/dist/index.js +27 -23
  129. package/dist/{completion-core-BRo86R_L.js → inline-ghost-BVRQeGoL.js} +50 -2
  130. package/dist/inline-ghost-BVRQeGoL.js.map +1 -0
  131. package/dist/ipc/remote.d.ts +313 -0
  132. package/dist/ipc/remote.d.ts.map +1 -0
  133. package/dist/ipc/remote.js +4 -0
  134. package/dist/ipc.d.ts +6 -964
  135. package/dist/ipc.js +403 -4
  136. package/dist/ipc.js.map +1 -0
  137. package/dist/{isolation-Kfoof0MM.d.ts → isolation-D9L650W5.d.ts} +100 -15
  138. package/dist/isolation-D9L650W5.d.ts.map +1 -0
  139. package/dist/json-rpc--HXQti1E.js +350 -0
  140. package/dist/json-rpc--HXQti1E.js.map +1 -0
  141. package/dist/links-j8rED1sR.js +603 -0
  142. package/dist/links-j8rED1sR.js.map +1 -0
  143. package/dist/local-terminal-D8QThwo_.js +252 -0
  144. package/dist/local-terminal-D8QThwo_.js.map +1 -0
  145. package/dist/{logger--C1VQjwJ.js → logger-Gtqlayx8.js} +199 -173
  146. package/dist/logger-Gtqlayx8.js.map +1 -0
  147. package/dist/logs-view-CWG8aUAG.js.map +1 -1
  148. package/dist/{mcp-CgNd-f3F.js → mcp-_wecH9Qe.js} +608 -562
  149. package/dist/mcp-_wecH9Qe.js.map +1 -0
  150. package/dist/mcp.d.ts +2 -2
  151. package/dist/mcp.js +2 -2
  152. package/dist/media-sniff-UKijEaiQ.js.map +1 -1
  153. package/dist/{messages-2jcOA_5R.js → messages-BqiwcD2Q.js} +1017 -1014
  154. package/dist/messages-BqiwcD2Q.js.map +1 -0
  155. package/dist/{oauth-DxHO17JE.js → oauth-Px_pBB9W.js} +5 -5
  156. package/dist/{oauth-DxHO17JE.js.map → oauth-Px_pBB9W.js.map} +1 -1
  157. package/dist/{openai-compat-Dy56weK8.js → openai-compat-CFrpH7uq.js} +432 -589
  158. package/dist/openai-compat-CFrpH7uq.js.map +1 -0
  159. package/dist/orchestration-BCGmB8Ps.js +511 -0
  160. package/dist/orchestration-BCGmB8Ps.js.map +1 -0
  161. package/dist/output/stream-json.d.ts +1 -2
  162. package/dist/output/stream-json.d.ts.map +1 -1
  163. package/dist/output/stream-json.js +1 -1
  164. package/dist/output/terminal.d.ts +1 -2
  165. package/dist/output/terminal.d.ts.map +1 -1
  166. package/dist/output/terminal.js +13 -7
  167. package/dist/output/terminal.js.map +1 -1
  168. package/dist/{plugins-gbI7SBA6.js → plugins-DS7mahzZ.js} +20 -101
  169. package/dist/plugins-DS7mahzZ.js.map +1 -0
  170. package/dist/plugins.d.ts +1 -1
  171. package/dist/plugins.js +1 -2
  172. package/dist/prelude-Cxo4t74B.js +517 -0
  173. package/dist/prelude-Cxo4t74B.js.map +1 -0
  174. package/dist/presets.d.ts +1 -1
  175. package/dist/presets.js +1 -2
  176. package/dist/project-root-B8zADr4o.js +167 -0
  177. package/dist/project-root-B8zADr4o.js.map +1 -0
  178. package/dist/project-user-paths-BSgtrRxl.js +58 -0
  179. package/dist/project-user-paths-BSgtrRxl.js.map +1 -0
  180. package/dist/{prompts-Pqc3JdW_.d.ts → prompts-DYcvqShf.d.ts} +14 -11
  181. package/dist/prompts-DYcvqShf.d.ts.map +1 -0
  182. package/dist/protocol-DnhqqSmE.js +13 -0
  183. package/dist/protocol-DnhqqSmE.js.map +1 -0
  184. package/dist/providers/anthropic.d.ts +2 -2
  185. package/dist/providers/anthropic.js +1 -2
  186. package/dist/providers/arcee.d.ts +1 -1
  187. package/dist/providers/arcee.js +1 -1
  188. package/dist/providers/baseten.d.ts +1 -1
  189. package/dist/providers/baseten.js +1 -1
  190. package/dist/providers/cerebras.d.ts +1 -1
  191. package/dist/providers/cerebras.js +3 -2
  192. package/dist/providers/cerebras.js.map +1 -1
  193. package/dist/providers/local.d.ts +1 -1
  194. package/dist/providers/local.js +1 -1
  195. package/dist/providers/openai-compat.d.ts +1 -1
  196. package/dist/providers/openai-compat.js +1 -2
  197. package/dist/providers/openai.d.ts +1 -1
  198. package/dist/providers/openai.js +15 -12
  199. package/dist/providers/openai.js.map +1 -1
  200. package/dist/providers/openrouter.d.ts +1 -1
  201. package/dist/providers/openrouter.js +3 -2
  202. package/dist/providers/openrouter.js.map +1 -1
  203. package/dist/providers/xai.d.ts +1 -1
  204. package/dist/providers/xai.js +3 -3
  205. package/dist/{providers-BVhnFyiY.js → providers-XUR7F4Hi.js} +4 -4
  206. package/dist/{providers-BVhnFyiY.js.map → providers-XUR7F4Hi.js.map} +1 -1
  207. package/dist/providers.d.ts +1 -1
  208. package/dist/providers.js +4 -4
  209. package/dist/registry-D6ShBWJU.js +5234 -0
  210. package/dist/registry-D6ShBWJU.js.map +1 -0
  211. package/dist/remote-hosts-BtA_9ZGp.js +97 -0
  212. package/dist/remote-hosts-BtA_9ZGp.js.map +1 -0
  213. package/dist/restate.d.ts +2 -3
  214. package/dist/restate.d.ts.map +1 -1
  215. package/dist/restate.js +631 -572
  216. package/dist/restate.js.map +1 -1
  217. package/dist/run-summary-CklLiZ4y.js +288 -0
  218. package/dist/run-summary-CklLiZ4y.js.map +1 -0
  219. package/dist/{sandbox-BH0WQkXn.d.ts → sandbox-C5dR5DwQ.d.ts} +6 -3
  220. package/dist/sandbox-C5dR5DwQ.d.ts.map +1 -0
  221. package/dist/session/sqlite.d.ts +1 -2
  222. package/dist/session/sqlite.d.ts.map +1 -1
  223. package/dist/session/sqlite.js +1 -616
  224. package/dist/session-factory-BUGY5P40.js +1576 -0
  225. package/dist/session-factory-BUGY5P40.js.map +1 -0
  226. package/dist/{session-BqRP1hS7.js → session-xh9JXu6i.js} +94 -52
  227. package/dist/session-xh9JXu6i.js.map +1 -0
  228. package/dist/session.d.ts +1 -1
  229. package/dist/session.js +2 -2
  230. package/dist/settings-keys-Ckf29NeM.js +677 -0
  231. package/dist/settings-keys-Ckf29NeM.js.map +1 -0
  232. package/dist/shell-path-Dqaoow2J.js +412 -0
  233. package/dist/shell-path-Dqaoow2J.js.map +1 -0
  234. package/dist/{skills-BC3dodWp.js → skills-DKqZJm4X.js} +430 -470
  235. package/dist/skills-DKqZJm4X.js.map +1 -0
  236. package/dist/skills.d.ts +2 -2
  237. package/dist/skills.js +1 -2
  238. package/dist/source-mPExaUm8.js +381 -0
  239. package/dist/source-mPExaUm8.js.map +1 -0
  240. package/dist/sqlite-DV1DQzN2.js +698 -0
  241. package/dist/sqlite-DV1DQzN2.js.map +1 -0
  242. package/dist/{src-CkNQKFKr.js → src-BRMYRJib.js} +903 -1121
  243. package/dist/src-BRMYRJib.js.map +1 -0
  244. package/dist/{stdio-DPpBDPx5.js → stdio-C6YyoW3I.js} +4 -4
  245. package/dist/{stdio-DPpBDPx5.js.map → stdio-C6YyoW3I.js.map} +1 -1
  246. package/dist/{stdio-loader-Bbx19dMZ.js → stdio-loader-C830Vg-Z.js} +4 -4
  247. package/dist/{stdio-loader-Bbx19dMZ.js.map → stdio-loader-C830Vg-Z.js.map} +1 -1
  248. package/dist/system-prompt-MNVHatSD.d.ts.map +1 -1
  249. package/dist/timeout-DEaqslLO.d.ts.map +1 -1
  250. package/dist/todo-edits-Dn251YUP.js +1790 -0
  251. package/dist/todo-edits-Dn251YUP.js.map +1 -0
  252. package/dist/{turn-operations-DrbJlpq9.js → tool-formatters-IQFDrdrs.js} +1456 -2120
  253. package/dist/tool-formatters-IQFDrdrs.js.map +1 -0
  254. package/dist/tool-state-Dku6YqAB.js +989 -0
  255. package/dist/tool-state-Dku6YqAB.js.map +1 -0
  256. package/dist/tools/fetch-url.d.ts +1 -2
  257. package/dist/tools/fetch-url.d.ts.map +1 -1
  258. package/dist/tools/fetch-url.js +1 -2
  259. package/dist/tools/prompts.d.ts +1 -1
  260. package/dist/tools/prompts.js +7 -7
  261. package/dist/tools/prompts.js.map +1 -1
  262. package/dist/tools/web-search.d.ts +1 -2
  263. package/dist/tools/web-search.d.ts.map +1 -1
  264. package/dist/tools/web-search.js +2 -2
  265. package/dist/tools/web-search.js.map +1 -1
  266. package/dist/tools-_DnT3AGM.js +578 -0
  267. package/dist/tools-_DnT3AGM.js.map +1 -0
  268. package/dist/tools.d.ts +3 -3
  269. package/dist/tools.js +4 -2
  270. package/dist/tui.d.ts +534 -517
  271. package/dist/tui.d.ts.map +1 -1
  272. package/dist/tui.js +34933 -27242
  273. package/dist/tui.js.map +1 -1
  274. package/dist/turn-operations-B8sokwi5.js +333 -0
  275. package/dist/turn-operations-B8sokwi5.js.map +1 -0
  276. package/dist/turn-operations-DQX5ZZni.d.ts +3089 -0
  277. package/dist/turn-operations-DQX5ZZni.d.ts.map +1 -0
  278. package/dist/types-C7ZFOztE.d.ts +132 -0
  279. package/dist/types-C7ZFOztE.d.ts.map +1 -0
  280. package/dist/types-CH5XpIHw.js +114 -0
  281. package/dist/types-CH5XpIHw.js.map +1 -0
  282. package/dist/{types-CWgENVLY.js → types-Cu0ZGDu9.js} +10 -6
  283. package/dist/types-Cu0ZGDu9.js.map +1 -0
  284. package/dist/types-IqegbJEE.d.ts +624 -0
  285. package/dist/types-IqegbJEE.d.ts.map +1 -0
  286. package/dist/types.d.ts +4 -4
  287. package/dist/types.js +1 -1
  288. package/dist/url-D8HCl65b.js +1259 -0
  289. package/dist/url-D8HCl65b.js.map +1 -0
  290. package/dist/wasm-BVT-j5cl.js +33 -0
  291. package/dist/wasm-BVT-j5cl.js.map +1 -0
  292. package/dist/workspace-exec-stream-SV2R9d5S.js +355 -0
  293. package/dist/workspace-exec-stream-SV2R9d5S.js.map +1 -0
  294. package/dist/ws-CIHMvlSh.js +952 -0
  295. package/dist/ws-CIHMvlSh.js.map +1 -0
  296. package/dist/ws-client-N7NxzvQp.js +230 -0
  297. package/dist/ws-client-N7NxzvQp.js.map +1 -0
  298. package/dist/{xai-CazjStOK.js → xai-CvJTeLrI.js} +165 -5
  299. package/dist/xai-CvJTeLrI.js.map +1 -0
  300. package/dist/xdg-zweed0-t.js +117 -0
  301. package/dist/xdg-zweed0-t.js.map +1 -0
  302. package/docs/ACP.md +52 -0
  303. package/docs/ARCHITECTURE.md +103 -6
  304. package/docs/ARTIFACTS.md +8 -4
  305. package/docs/CHAT.md +165 -14
  306. package/docs/EXECUTION_CONTEXT.md +8 -0
  307. package/docs/EXTENSIONS.md +123 -2
  308. package/docs/HEADLESS.md +2 -0
  309. package/docs/IPC.md +775 -64
  310. package/docs/MACHINES.md +456 -0
  311. package/docs/PANELS.md +3 -0
  312. package/docs/README.md +4 -0
  313. package/docs/RESTATE.md +3 -2
  314. package/docs/RUN_IN_BACKGROUND.md +10 -2
  315. package/docs/SERVER.md +1085 -0
  316. package/docs/SKILL.md +6 -1
  317. package/docs/TERMINAL.md +75 -0
  318. package/docs/TUI.md +65 -14
  319. package/package.json +58 -26
  320. package/scripts/eval.ts +4 -0
  321. package/dist/acp-D-tVyUjw.js.map +0 -1
  322. package/dist/agent-D0zZnGbt.js.map +0 -1
  323. package/dist/anthropic-C2aole0G.js.map +0 -1
  324. package/dist/approvals-DKaJZYlm.js.map +0 -1
  325. package/dist/atomic-write-B7zr1ddF.js.map +0 -1
  326. package/dist/auth-BG2rVKSt.js.map +0 -1
  327. package/dist/completion-core-BRo86R_L.js.map +0 -1
  328. package/dist/contexts/daytona.js.map +0 -1
  329. package/dist/contexts/docker.js.map +0 -1
  330. package/dist/contexts/e2b.js.map +0 -1
  331. package/dist/contexts-CRE9pw07.js.map +0 -1
  332. package/dist/eval.js.map +0 -1
  333. package/dist/format-BF745pLL.js.map +0 -1
  334. package/dist/glob-DluQFSw1.js +0 -270
  335. package/dist/glob-DluQFSw1.js.map +0 -1
  336. package/dist/headless.js.map +0 -1
  337. package/dist/history-search-C4bk1Wz_.js.map +0 -1
  338. package/dist/index-BgN1T3lk.d.ts.map +0 -1
  339. package/dist/index-C37_ppWv.d.ts.map +0 -1
  340. package/dist/index-C6NEd5y_.d.ts.map +0 -1
  341. package/dist/ipc-zVSyo6K1.js +0 -331
  342. package/dist/ipc-zVSyo6K1.js.map +0 -1
  343. package/dist/ipc.d.ts.map +0 -1
  344. package/dist/isolation-Kfoof0MM.d.ts.map +0 -1
  345. package/dist/json-rpc-rSWMUh9b.js +0 -192
  346. package/dist/json-rpc-rSWMUh9b.js.map +0 -1
  347. package/dist/logger--C1VQjwJ.js.map +0 -1
  348. package/dist/mcp-CgNd-f3F.js.map +0 -1
  349. package/dist/messages-2jcOA_5R.js.map +0 -1
  350. package/dist/openai-compat-Dy56weK8.js.map +0 -1
  351. package/dist/output-cap-C9zukcCj.js +0 -100
  352. package/dist/output-cap-C9zukcCj.js.map +0 -1
  353. package/dist/panels-B6gRk4bf.js +0 -789
  354. package/dist/panels-B6gRk4bf.js.map +0 -1
  355. package/dist/plugins-gbI7SBA6.js.map +0 -1
  356. package/dist/prompts-Pqc3JdW_.d.ts.map +0 -1
  357. package/dist/run-summary-BU_NgwUT.js +0 -269
  358. package/dist/run-summary-BU_NgwUT.js.map +0 -1
  359. package/dist/sandbox-BH0WQkXn.d.ts.map +0 -1
  360. package/dist/session/sqlite.js.map +0 -1
  361. package/dist/session-BqRP1hS7.js.map +0 -1
  362. package/dist/shell-path-h5czzllR.js +0 -1549
  363. package/dist/shell-path-h5czzllR.js.map +0 -1
  364. package/dist/skills-BC3dodWp.js.map +0 -1
  365. package/dist/src-CkNQKFKr.js.map +0 -1
  366. package/dist/todo-edits-UOwgxGKu.js +0 -528
  367. package/dist/todo-edits-UOwgxGKu.js.map +0 -1
  368. package/dist/tool-formatters-DoRLnfuD.d.ts +0 -759
  369. package/dist/tool-formatters-DoRLnfuD.d.ts.map +0 -1
  370. package/dist/transcript-anchors-BeudMbXx.d.ts +0 -1762
  371. package/dist/transcript-anchors-BeudMbXx.d.ts.map +0 -1
  372. package/dist/turn-operations-C8bMBDl_.d.ts +0 -1258
  373. package/dist/turn-operations-C8bMBDl_.d.ts.map +0 -1
  374. package/dist/turn-operations-DrbJlpq9.js.map +0 -1
  375. package/dist/types-CWgENVLY.js.map +0 -1
  376. package/dist/xai-CazjStOK.js.map +0 -1
@@ -0,0 +1,456 @@
1
+ # Machines
2
+
3
+ A **machine** lends execution (files, shell, background tasks, terminals,
4
+ live processes) to a **host** over a WebSocket it opens itself, or, for a
5
+ local host that can't be dialed, over the host's own client connection
6
+ (the reverse direction). As built: the protocol, the host registry, the
7
+ remote execution context, the machine agent and the offline semantics
8
+ (slice 4A), then the reverse direction, `execStream`, project files read
9
+ on the machine, the machine's own sandbox, and the TUI / desktop app
10
+ surfaces (slice 4B). Everything here is part of remote hosts: off, nothing
11
+ of it loads.
12
+
13
+ Contents: [Roles](#roles) · [Connection](#connection) · [Protocol](#protocol) · [Reverse direction](#reverse-direction) · [Host side](#host-side) · [Folders](#folders) · [Project files](#project-files) · [Machine side](#machine-side) · [Sandbox](#sandbox) · [Offline](#offline) · [Client UI](#client-ui) · [Security model](#security-model) · [Limits](#limits) · [Not in v1](#not-in-v1)
14
+
15
+ ## Roles
16
+
17
+ | Role | Owns | Runs |
18
+ |---|---|---|
19
+ | **Host** | sessions, settings, credentials, approvals, the transcript | the agent loop, the model calls, every tool's logic |
20
+ | **Machine** | its own declared folders (*roots*) and its data dir | the tools' I/O only: `exec`, file reads/writes/listings, background tasks, PTYs |
21
+
22
+ Any zidane process can play either role for another. A session whose
23
+ environment is `local@laptop` or `worktree@laptop` runs on the host, and every
24
+ `ExecutionContext` call its tools make goes to the machine named `laptop`.
25
+ No credential crosses to a machine: it sees commands, paths and file contents,
26
+ never a provider key, a token or the transcript.
27
+
28
+ ## Connection
29
+
30
+ - **The machine dials the host** (`wss://host/…`), with a token of scope
31
+ `machine` created on the host (`zidane server token create --name laptop --scope machine`).
32
+ A laptop needs no inbound port; NAT and firewalls are irrelevant.
33
+ - Same WebSocket transport, same JSON-RPC framing, heartbeat (`$/ping`, WS
34
+ pings) and `ipc.hello` handshake as a client (the machine pins the host id
35
+ after its first hello, like a saved host). Then the direction of the
36
+ execution calls is reversed: the **host sends requests to the machine** over
37
+ the machine's connection (`createJsonRpcEndpoint` is bidirectional).
38
+ - A machine connection is **not a client**: it is never added to the host's
39
+ peer set (it receives no signal, no session data) and may call only
40
+ `ipc.hello`, `$/ping` and `machine.*`. Anything else is refused with
41
+ `MethodNotAllowed`. A client connection calling `machine.*` gets the same.
42
+ - Reconnect: the machine agent reuses the hosts' connection loop
43
+ (`keepHostConnected`: backoff 1 s → 30 s; a refused token or another host
44
+ answering is "for good" until restarted).
45
+
46
+ A zidane-server can be a machine of another zidane-server with the same
47
+ agent (it dials). A **local** host (daemon, in-process) has no listener, so
48
+ for `local@<server>` the roles are kept and the dialing is turned around:
49
+ see [Reverse direction](#reverse-direction).
50
+
51
+ ## Protocol
52
+
53
+ Wire types: `src/chat/machine-protocol.ts`, re-exported by `src/ipc/protocol.ts`.
54
+ Versioned on its own: `MACHINE_PROTOCOL_VERSION` (currently `1`). The host
55
+ refuses a `machine.register` with another major version
56
+ (`MachineErrorCode.VersionSkew`), naming both versions.
57
+
58
+ **Machine → host** (requests on the machine's connection):
59
+
60
+ | Method | Params → result |
61
+ |---|---|
62
+ | `ipc.hello`, `$/ping` | as for clients |
63
+ | `machine.register` | `{ machineId, name, roots: [{ path, label? }], capabilities, version, protocolVersion }` → `{ machineId, hostId? }` |
64
+
65
+ `machineId` is stable, generated once and kept in `<dataDir>/machine-id`.
66
+ `name` is the display name and the suffix of its environment kinds; one name
67
+ belongs to one machine id per host (a second machine claiming it is refused).
68
+
69
+ `capabilities`: `shell`, `terminals`, `background`, `worktrees`, `stream`
70
+ (`exec.stream*`; absent on an older machine, whose context then has no
71
+ `execStream`) and `sandboxed` (it runs every command under its own OS
72
+ sandbox: the context reports `isolated`).
73
+
74
+ **Machine → host** (notifications): `terminal.data { terminalId, data (base64) }`, `terminal.exit { terminalId, code }`, `task.exit { taskId, exit: TaskExitInfo }`, `exec.stream.data { callId, stream: 'stdout' | 'stderr', data (base64) }`, `exec.stream.exit { callId, code }`.
75
+
76
+ **Host → machine** notifications: `exec.cancel { callId }` (the abort of a sent `exec.run`, or the kill of a live `exec.stream`).
77
+
78
+ **Host → machine** (requests), one per `ExecutionContext` method; every call
79
+ names its handle as `{ id, cwd }` so a machine that restarted rebuilds it:
80
+
81
+ | Method | Mirrors |
82
+ |---|---|
83
+ | `machine.ping` | liveness |
84
+ | `exec.spawn { handle }` | `spawn` (checks `cwd` against the roots) |
85
+ | `exec.run { handle, callId, command, cwd?, env?, timeout? }` | `exec` (timeout in seconds, like the interface) |
86
+ | `exec.cancel { callId }` | the `signal` of `exec`; kills an `exec.stream` |
87
+ | `exec.stream { handle, callId, command, cwd?, env? }` → `{ pid }` | `execStream`: output as `exec.stream.data`, then one `exec.stream.exit` once stdout and stderr are drained |
88
+ | `exec.stream.write { callId, data (base64) }` | the stream's stdin |
89
+ | `exec.destroy { handle }` | `destroy` |
90
+ | `fs.read { handle, path, binary? }` | `readFile` / `readFileBinary` (binary as base64) |
91
+ | `fs.write { handle, path, content (base64) }` | `writeFile` |
92
+ | `fs.list { handle, path, options? }` | `listFiles` |
93
+ | `fs.size { handle, path }` | `fileSize` |
94
+ | `bg.start { handle, command, cwd?, env?, maxOutputBytes? }` | `execBackground` (output under the machine's data dir) |
95
+ | `bg.list` / `bg.kill { taskId }` / `bg.wait { taskId, timeoutMs? }` | `listBackground` / `killBackground` / `waitBackground` |
96
+ | `terminal.open { handle, cols, rows, cwd? }` | `openTerminal` |
97
+ | `terminal.attach { terminalId }` | `reconnectTerminal` (returns the buffered output and the byte total, so an automatic reattach after a reconnect replays only what the host missed) |
98
+ | `terminal.write` / `terminal.resize` / `terminal.kill` / `terminal.detach` | `TerminalProcess` |
99
+ | `env.checkFolder { cwd }` | the machine's verdict on a session folder |
100
+ | `env.prepareWorktree { cwd, branch, base?, config }` / `env.teardownWorktree { ref, config }` | the worktree provider's `prepare` / `teardown`, run machine-side |
101
+
102
+ `execStream` is what context-scoped MCP servers (`scope: 'context'`) run
103
+ on: a machine session's stdio servers run on the machine. The host side
104
+ registers a stream's queues before the call, so no early byte is lost; a
105
+ stream whose link drops ends with exit `-1` (the machine kills it: a live
106
+ protocol can't survive lost output). Not mirrored: `getMappedPort`;
107
+ `reassignBackgroundTasks` is done host-side (same-handle rebinding).
108
+
109
+ ## Reverse direction
110
+
111
+ `local@<server>` on a local host (the laptop is the host, a saved
112
+ zidane-server the machine). The local host already has what a window has
113
+ for that server (its saved entry in `hosts.json`, its client token under
114
+ `hosts:<id>`), so it dials it as a client and asks it to lend itself:
115
+
116
+ 1. `machine.attach { hostId }` (a client method): the server answers with
117
+ the registration a machine would send (`MachineRegisterParams`: its id,
118
+ its name made machine-safe, its workspace roots as roots, its
119
+ capabilities). `hostId` names the asking host process, so a reconnect
120
+ finds its tasks and terminals again (each local host process uses its
121
+ own instance id: two windows' in-process hosts never take over each
122
+ other's executor). The server keys an executor by the client's token
123
+ AND that id: the id is the client's word, so another token naming it
124
+ gets an executor of its own, never the first one's tasks, terminals or
125
+ exits. A token holds at most 4 executors there and only ever evicts its
126
+ own idle ones.
127
+ 2. The local host registers that in its own machine registry, with a
128
+ connection that sends every host→machine request as
129
+ `machine.call { method, params }` (no client deadline: a call lasts as
130
+ long as its command; a dead server is the heartbeat's to catch) and
131
+ every notification as a `machine.call` notification.
132
+ 3. The server serves them with the same confined executor a shared
133
+ machine runs (`createMachineExecutor`, loaded on the first attach),
134
+ inside its workspace roots, its data dir at `<data dir>/machine-self`.
135
+ Its notifications go back to that connection only, as
136
+ `machine:notification` signals (sent before the attach reply too: the
137
+ local host buffers them until it has registered).
138
+ 4. A dropped connection is the machine going offline (same semantics as
139
+ below); the loop reconnects (backoff), a refused token, another server
140
+ at the URL, or a refused attach (`allowAsMachine` off, a name taken
141
+ here) stop it until the user acts. A busy server (no room for this
142
+ token's executor now: `MachineErrorCode.Busy`) or a dropped link is
143
+ retried with the backoff.
144
+
145
+ The server must allow it: `allowAsMachine` (`--allow-as-machine`,
146
+ `ZIDANE_SERVER_ALLOW_AS_MACHINE=1`, `server.json`), off by default.
147
+ `ipc.hello` says so (`machineServe: true`) and the windows offer "use as a
148
+ machine" only then. Security: [SERVER.md § Security model](./SERVER.md#security-model).
149
+
150
+ ## Host side
151
+
152
+ - `src/chat/host/machines/` (engine feature, `host.machines`): the registry of
153
+ known machines (persisted in `<dataDir>/machines.json`: id, name, roots,
154
+ token id, last seen) and their live links. Made only where machines
155
+ exist: always on a zidane-server (remote by nature), on a local host only
156
+ while remote hosts are on (off: `machines.json` is never read and no
157
+ machine kind is registered; turned off later, the known machines read
158
+ as absent) (`online` / `offline`), the remote
159
+ context (`createMachineContext`), the offline wait, and one
160
+ `local@<name>` + `worktree@<name>` environment provider per known machine,
161
+ registered on the host's environment registry when the machine is first
162
+ known. A forgotten machine's providers report unavailable and refuse to run.
163
+ - Transport-free: the engine sees a `MachineLink` (`request`, `notify`,
164
+ `close`, `onNotification`); `src/ipc/daemon/machine-gateway.ts` builds it
165
+ over the WS connection and enforces the method allowlist.
166
+ - `environments.list` lists the machine kinds with `machine: { id, name, status }`
167
+ (a forgotten machine's kinds are not listed); `machines.list` /
168
+ `machines.forget` are client methods; `machine:status { machineId, name, status }`
169
+ is broadcast to clients.
170
+ - Execution: a machine context is `sandbox`-typed (its filesystem is not the
171
+ host's), so the host's own workspace checks on `sessions.workspace*` leave
172
+ paths to the machine, and the session's cwd is not "movable" like a local one.
173
+ - Session metadata: the environment's `ref` holds `{ machineId }` (and the
174
+ worktree's `path` / `repoRoot` on the machine).
175
+ - `sessions.create` with a machine kind: the host's own workspace confinement
176
+ does not apply (the folder is on the machine); instead the folder must be
177
+ absolute and lexically inside a declared root (pre-check), then the machine
178
+ re-checks it on its filesystem (`env.checkFolder`, real paths) during
179
+ provisioning.
180
+
181
+ ## Folders
182
+
183
+ A machine kind's folders are the machine's: every entry point taking a
184
+ folder checks one against that machine's roots (lexically here, then on
185
+ the machine with real paths where it touches it), never against this
186
+ host's own workspace confinement (a confined server would refuse every
187
+ machine path; an unconfined local host would read its own filesystem at a
188
+ path that only means something on the machine). `ChatHost.resolveFolder`
189
+ decides: a machine kind given (`kind` on `environments.list`,
190
+ `commands.list` / `commands.completeArgs`, `catalog.*`, `models.list`),
191
+ else this host's confinement, else (an older client naming no kind) the
192
+ one known machine whose roots hold it.
193
+
194
+ - `environments.list` for a machine's folder: its machine's kinds as
195
+ usual, this host's own kinds unavailable ("The folder is on laptop."). A
196
+ machine kind's availability does not depend on the folder (pickers ask
197
+ the kind first, then a folder there, `machines.listDirs`).
198
+ - `commands.list`, `catalog.*`, `models.list`: this host's user scope,
199
+ read from a folder of its own (below), plus for skills and MCP servers
200
+ the project scope read on the machine.
201
+ - `sessions.setFolder` on a machine session: checked by its machine
202
+ (`env.checkFolder`, never waits for an offline one); launch overrides'
203
+ `cwd` likewise.
204
+
205
+ ## Project files
206
+
207
+ A machine session's folder is on the machine, so is its project. The host
208
+ reads its **user scope** (`~/.agents`, `~/.zidane`, user extensions and
209
+ plugins, user skills and MCP servers, `~/.zidane/SYSTEM.md`) as for any
210
+ session, but from a folder of its own with no project
211
+ (`<data dir>/machine-sessions`, `ActiveRun.discoveryCwd`), never the
212
+ machine's path on this filesystem. The **project scope** is read on the
213
+ machine, through the session's own execution (`readMachineProject`:
214
+ `fs.size` / `fs.read` / `fs.list`, so the machine's roots and data-dir
215
+ rules apply), at agent build:
216
+
217
+ | What | Where on the machine |
218
+ |---|---|
219
+ | project root | `git rev-parse --show-toplevel` when inside the roots, else the session folder |
220
+ | instructions | `AGENTS.md`, `.agents/AGENTS.md`, `.<prefix>/AGENTS.md`, `CLAUDE.md` (64 KiB each), after the user's, under `userInstructionsScope` |
221
+ | `SYSTEM.md` | `<root>/SYSTEM.md`, beating the user's |
222
+ | skills | `.agents/skills/*/SKILL.md`, `.<prefix>/skills/*/SKILL.md` (64 at most, 256 KiB each), ahead of the user's on a name |
223
+ | MCP servers | `.agents/mcp(s).json`, `.<prefix>/mcp(s).json` (256 KiB each) |
224
+
225
+ - **No code from a machine.** Project extensions and Agent Plugins are
226
+ never loaded for a machine's folder (they are code; the host never runs
227
+ what a machine provides), so project `/` commands, which extensions
228
+ contribute, are not either: a machine session has the host's built-in
229
+ and user-extension commands.
230
+ - **MCP servers run on the machine.** A project stdio server is kept with
231
+ `scope: 'context'` (it runs over `execStream`, on the machine); every
232
+ `${VAR}` placeholder in it is resolved first to its `:-default` or to
233
+ nothing, so this host's environment (provider keys) is never expanded
234
+ into anything a machine named. A URL server is dropped with a notice
235
+ (connecting would happen from the host).
236
+ - **Offline at build**: the run is not held. The session gets
237
+ "laptop is offline: its project instructions, skills and MCP servers
238
+ load when it is back.", the agent is built with the user scope, and is
239
+ rebuilt (at run end when busy) once the machine is back.
240
+ - Not read on a machine: Ruler and foreign agents' configs (`.ruler/`,
241
+ `.claude/…`), skill resources besides `SKILL.md` (their paths are the
242
+ machine's; `skills_read` of one is not proxied).
243
+
244
+ ## Machine side
245
+
246
+ `startMachineAgent({ hostUrl, token, name, roots, dataDir })` (`src/ipc/machine/`),
247
+ CLI `zidane machine share --host wss://… --token-file … --name laptop --root ~/code [--root …]`.
248
+
249
+ - Serves execution from one `process` context per handle, through a guard
250
+ (`createMachineExecutor`) that never trusts the host: every path and `cwd` is
251
+ resolved against the handle's cwd, then **real-path contained** in a root
252
+ (`createWorkspaceConfinement`, the same rule as a zidane-server's workspace
253
+ roots): `..`, absolute escapes and symlinks pointing out are refused; the
254
+ machine's data dir (its `machine-id`, token file, task logs) is unreachable
255
+ by file ops even when a root contains it. Exception: `<dataDir>/worktrees/`
256
+ (an implicit root: `worktree@<machine>` makes its worktrees there).
257
+ - **Private folders**, unreachable by file ops whatever the roots (sharing
258
+ `--root ~` included; `machinePrivateDirs`): zidane's own folders as the
259
+ machine's process resolves them (data, config, state and cache dirs, and
260
+ `~/.zidane`: credentials, tokens, extensions, plugins, skills, `mcps.json`,
261
+ the user-scope `SYSTEM.md`, the machine's data, sync files), the zidane
262
+ data dir it was given (a server's `--data-dir`), and the credential
263
+ folders the sandbox hides (`~/.ssh`, `~/.aws`, `~/.gnupg`, …). A root
264
+ that is, or is inside, one of them is refused when sharing (`zidane
265
+ machine share`, a share link); a server's own default root
266
+ (`<data dir>/workspaces`) stays usable when it lends itself. A listing
267
+ (`fs.list`, globs) only returns entries whose real path is reachable, and
268
+ a pattern can't climb with `..`.
269
+ - **Task logs** (`<dataDir>/tasks/`, owner-only): a log of a background task
270
+ this machine started is readable by its path, opened without following a
271
+ symlink, and only when it is still that regular file inside the tasks
272
+ folder (`./task-logs.ts`); logs are created exclusively, never through a
273
+ link planted at their path. A command run for the host can't make the
274
+ machine process read anything else through "its log".
275
+ - A handle id named again with another folder destroys the old handle (its
276
+ background tasks stop); a second call under a `callId` still running is
277
+ refused.
278
+ - Bounded: `exec` output by the process context's own cap, background
279
+ output capped per task (8 MiB), at most 64 running tasks and 16
280
+ terminals, terminal backlogs 256 KiB, at most 256 queued task exits.
281
+ - Path checks and use are not atomic (a shell command racing a symlink in
282
+ between could redirect one file op); the shell can reach as much anyway.
283
+ - Live processes (`exec.stream`): at most 16, output sent in chunks of at
284
+ most 256 KiB, stdin written 256 KiB at a time; all killed when the host
285
+ link drops. A kill sends TERM to the process group, and KILL after the
286
+ grace if its pipes are still open (a TERM landing while the shell
287
+ forks a child would otherwise leave that child, and the stream, alive).
288
+ - Background tasks keep running across a disconnect (they're the machine's
289
+ children); their exits are queued and re-sent on reconnect, and `bg.list`
290
+ answers by id after a reconnect. Terminals keep running for a grace period
291
+ (default 5 min) after their host link drops, buffering output; `terminal.attach`
292
+ reattaches with the backlog; past the grace they are killed.
293
+
294
+ ## Sandbox
295
+
296
+ The machine owner decides: a machine runs every command it is sent
297
+ (`exec`, background tasks, streams, terminals) under **its own user's**
298
+ `sandbox*` settings (`sandbox`, `sandboxNetwork`, `sandboxAllowWrite`:
299
+ the same OS sandbox as local sessions, macOS Seatbelt / Linux bwrap,
300
+ failing closed when unavailable), over every root it shares (not its task
301
+ logs: the machine process writes them), with its user's zidane credential
302
+ files hidden. A host can't turn
303
+ it off, and its own `sandbox*` settings don't apply to a machine session
304
+ (they describe the host's computer). `zidane machine share` reads its
305
+ user's `state.json`; a share run from a local host uses that host's
306
+ settings; a server lending itself uses its own. The register says
307
+ `sandboxed`, the host's context reports `isolated`.
308
+
309
+ ## Offline
310
+
311
+ A machine is `offline` from the moment its connection drops (close, or two
312
+ missed heartbeats) until it registers again. Every host→machine call goes
313
+ through `MachineLink.call`:
314
+
315
+ | When | What happens |
316
+ |---|---|
317
+ | Offline before the call is sent | The call **waits** for the machine (not sent, so safe to send later). The session gets a notice "waiting for laptop…" once, and `machine:status` is broadcast. On reconnect the call is sent; notice "laptop is back". |
318
+ | Sent, then the connection drops before the reply | **Not re-sent** (it may have run). The tool fails with `result lost: laptop disconnected during the call` and the model decides. Idempotent calls (`MACHINE_IDEMPOTENT_METHODS`: reads `fs.read` / `fs.list` / `fs.size` / `bg.list` / `bg.wait` / `env.checkFolder` / `terminal.attach` / `machine.ping`, and the handle calls `exec.spawn` / `exec.destroy` / `exec.cancel`, keyed by the host's handle id) are the exception: they are re-sent after the reconnect like an unsent call. |
319
+ | Still offline after `machineWaitMinutes` (setting, default 15) | The waiting calls fail with `laptop is unavailable`, the session's run is interrupted and ends with the notice "laptop stayed offline for 15 min: run stopped". |
320
+ | Aborted while waiting (user interrupt) | The call is dropped, nothing is sent. |
321
+ | The host drains (shutdown) | Waiting calls fail at once (`MachineRegistry.close`), so a run or a provisioning waiting on an offline machine never holds the drain. |
322
+
323
+ The wait is the run pausing: the tool call is simply still in flight, so the
324
+ model never sees a broken tool. Timers are injectable (`MachineRegistryOptions.timers`) for tests.
325
+
326
+ ## Client UI
327
+
328
+ Behind remote hosts (Settings → Remote hosts, `ZIDANE_NO_REMOTE` beats
329
+ it): off, no surface, and This computer's host loads no machine code,
330
+ reads no `machine-links.json`, no `machine:` token, dials nothing
331
+ (`test/tui/app-remote-containment.test.tsx`, `test/tui/remote-lazy-graph.test.ts`,
332
+ `gui/src/main/chat/hosts/gate.containment.test.ts`). `zidane machine share`
333
+ refuses under `ZIDANE_NO_REMOTE=1` / `--no-remote` too.
334
+
335
+ **This computer's host** (in-process or the daemon; the dispatcher's
336
+ handlers, which the desktop app's embedded host calls directly) offers:
337
+
338
+ | Method | What |
339
+ |---|---|
340
+ | `machines.share.start { hostId, name, roots, token? }` | share this computer with a saved server (`token`: a machine token to keep, as `machine:<hostId>` in its credentials; else the one kept) |
341
+ | `machines.share.stop { hostId }` / `machines.useServer { hostId, enabled }` | stop sharing / use a saved server as a machine ([Reverse direction](#reverse-direction)) |
342
+ | `machines.links.status` | both, with their state (`connecting`, `connected`, `reconnecting`, `offline`, `auth-refused`, `identity`, `refused`) and why; `machine:links` signals each change |
343
+ | `machines.list` / `machines.forget` / `machines.listDirs { machineId, path? }` | the machines it knows, forget one, a machine's folders (absent `path`: its first root) through it |
344
+
345
+ and a server `tokens.createMachineToken { name }` ([SERVER.md § Tokens](./SERVER.md#tokens)), so sharing is one step: the window
346
+ asks the local host, which has no token for that server, so the window
347
+ mints one there and hands it over (a server that can't asks for one to be
348
+ pasted).
349
+
350
+ The wanted links live in `<data dir>/machine-links.json` (no token in it),
351
+ any process writes it, and they resume with the host while remote hosts
352
+ are on. **Shares** run in one process for the computer (the holder of
353
+ `machine-links.lock`, re-checked every 2 s: the daemon, or the first
354
+ window's in-process host; the others show its state from
355
+ `machine-links-status.json`): two processes sharing one machine id would
356
+ keep replacing each other's connection. The lock is exclusive
357
+ (`src/ipc/machine/share-lock.ts`): taken by an exclusive create; its record
358
+ names the holder's pid and a per-process nonce; the holder touches it on
359
+ every check, so it is stale once its pid is gone or it went untouched for
360
+ 30 s (a reused pid); a stale lock is moved aside atomically and checked
361
+ before it is retaken. A process that finds another nonce in it stops its
362
+ shares at once, and only the holder removes it. A share's folders are
363
+ checked like the CLI's: absolute, existing, and not zidane's own data or a
364
+ credentials folder (real paths). **Uses** run in every host process
365
+ (each has its own machine registry). Turning remote hosts off stops them
366
+ all.
367
+
368
+ **TUI**: Settings → Hosts → `ctrl+b` on a saved server opens its machines
369
+ (`src/tui/machines/`): share this computer (name, folders, start / stop),
370
+ use it as a machine (inert unless the server lends itself), its machines
371
+ (status, folders, last seen; `ctrl+x` forgets, asks first). New session:
372
+ a server's machine kinds (and This computer's, when it uses a server) come
373
+ after the host's own, grouped by machine with its status (an offline one
374
+ listed, not pickable), then a folder on that machine. The chat title shows
375
+ `local@laptop` / `worktree@laptop ⎇ branch`, and `waiting for laptop…`
376
+ while a run is paused on it (from the session's notices, cleared by
377
+ `machine:status` online).
378
+
379
+ **Desktop app**: the same, in Settings › Hosts (a server card's
380
+ *Machines*: switches, folders from the native dialog), the composer's
381
+ environment picker and a machine folder picker, and a "Waiting for
382
+ laptop…" banner over the composer; main relays `machine:status` /
383
+ `machine:links` as `machines:status` / `machines:links`
384
+ ([IPC.md § Desktop app](./IPC.md)).
385
+
386
+ ## Security model
387
+
388
+ - **Sharing a machine gives the host a shell on it**, with the machine user's
389
+ permissions. Root containment covers file operations and every `cwd`; a
390
+ shell command can still read anything the machine user can (`cat ~/.ssh/id_ed25519`),
391
+ exactly like a zidane-server's own shell (see `SERVER.md` § Security). Share
392
+ with hosts you trust, as a user that owns little.
393
+ - What the machine enforces itself (a malicious host can't bypass it): roots
394
+ for paths and `cwd`, data-dir exclusion, symlink escapes, size and count
395
+ bounds, the method set it serves.
396
+ - What the host enforces: machine tokens can only register (never read
397
+ sessions, settings or signals); client tokens can't register; a revoked
398
+ token drops the machine's connection (it goes offline, its sessions wait
399
+ then stop); environments on unknown or forgotten machines are refused.
400
+ - **A machine id is bound to its token.** Ids are not secrets (the CLI
401
+ prints them, `machines.list` and `machine:status` show them), so the
402
+ first registration binds the id to the token it used (`principal` in
403
+ `machines.json`). Another machine token registering that id is refused
404
+ (`MachineErrorCode.IdBound`) and the machine's connection is left alone;
405
+ once the bound token is revoked, or the machine forgotten, a new token
406
+ may take the id.
407
+ - **Forgetting sticks.** `machines.forget` records the (machine id, token)
408
+ pair: that token registering the id again is refused for good (the agent
409
+ stops retrying), across restarts, until the share uses a new machine
410
+ token. The token itself stays (it may serve other machines): revoke it
411
+ on the server when it was only that machine's.
412
+ - A window mints at most 16 live machine tokens per client token
413
+ (`tokens.createMachineToken`, each recorded with `createdBy` and
414
+ audited, refusals included); revoke some to mint more.
415
+ - Safe-mode approvals are unchanged: they are asked of the host's clients
416
+ before the call ever reaches the machine.
417
+ - Nothing a machine provides runs on the host: no project extension,
418
+ plugin or URL MCP server from a machine's folder, and its stdio MCP
419
+ servers run on the machine without this host's environment
420
+ ([Project files](#project-files)).
421
+ - Reverse direction: the server's own executor confines a client's
422
+ `machine.call` like a machine confines its host's (its data dir private
423
+ even when an operator root contains it); with `allowAsMachine` on, a
424
+ client token can do there what terminals already let it do.
425
+ - A zidane-server has no machine links: `machines.share.*`,
426
+ `machines.useServer` and `machines.links.status` are refused there.
427
+ - A machine token minted by a window is shown to that window only, kept by
428
+ its local host in its credentials store, never in `machine-links.json`,
429
+ never in a log or a status detail.
430
+
431
+ ## Limits
432
+
433
+ | What | Bound |
434
+ |---|---|
435
+ | Reply / request size | the WS transport's (32 MiB frame); file content base64 |
436
+ | Machine connection rate | not rate-limited per message (terminal output and task exits stream as notifications; the connection can only register) |
437
+ | Roots per machine | 32 |
438
+ | `exec` output | the process context's cap (combined stdout+stderr) |
439
+ | Background tasks | 64 running per machine, output file capped at 8 MiB |
440
+ | Live processes (`exec.stream`) | 16 per machine, 256 KiB per chunk |
441
+ | `!` in a machine session | on any host (a local one too), streamed from the machine (`sessions.workspaceExecStream` runs the session's machine context's `execStream`, i.e. `exec.stream*`; esc kills it on the machine, `exec.cancel`); counts against the 16 live processes; 1 MiB of output per command on the host's side |
442
+ | Project files read from a machine | 64 KiB per instruction file, 256 KiB per `SKILL.md` / MCP file, 64 skills |
443
+ | Hosts a server lends itself to at once | 32, at most 4 per client token (a token's idle one is let go for its new one; full: `Busy`, retried) |
444
+ | Terminals | 16 per machine, 256 KiB backlog, 5 min grace |
445
+ | Offline wait | `machineWaitMinutes` (default 15) |
446
+
447
+ ## Not in v1
448
+
449
+ - Durable background tasks across a machine **restart** (they die with the
450
+ machine agent's process).
451
+ - Project scope from a machine for Ruler and foreign agents' configs, and
452
+ skill resources other than `SKILL.md` (above).
453
+ - A machine's URL MCP servers (dropped: the host would connect).
454
+ - Moving a share between the processes of one computer while it runs: the
455
+ lock holder keeps it until it exits; another process takes over within
456
+ 2 s after (30 s if it died without releasing it).
package/docs/PANELS.md CHANGED
@@ -9,6 +9,7 @@ Side-panel tabs next to the transcript, in both the TUI (docked rail) and the GU
9
9
  | Artifacts | What the session produced (PRs, branches, commits, recorded files); see [ARTIFACTS.md](./ARTIFACTS.md) | TUI + GUI |
10
10
  | Diffs | Two sources: **Session** (edits the agent made, from the transcript) and **Working tree** (`git diff HEAD` plus untracked files, live). Unified / split, wrap. On working-tree diffs, selecting lines opens a review-comment draft; `Mod+Enter` attaches `file + range + fenced excerpt + note` to the prompt | GUI |
11
11
  | Files | The session workspace as a tree with git status; opening a file shows a read-only viewer (or the editor, per the setting below) | GUI |
12
+ | Terminal | The session's terminals (also in the bottom drawer), running in the session's environment; see [TERMINAL.md](./TERMINAL.md) | GUI |
12
13
 
13
14
  Diffs and Files render with [`@pierre/diffs`](https://diffs.com) and [`@pierre/trees`](https://trees.software) (Apache-2.0), themed from the `--zg-*` tokens through `gui/src/renderer/src/components/code/pierre-theme.ts`; `DiffView` / `FileView` / `FileTree` are the only places the packages are touched. Filesystem and git access is read-only, on the host at the session cwd, confined to it (`gui/src/main/chat/workspace.ts`).
14
15
 
@@ -57,6 +58,8 @@ ctx.registerPanel({
57
58
  });
58
59
  ```
59
60
 
61
+ `load` and `onAction` get `{ sessionId?, cwd, focus? }`. `focus` (`{ kind?, uri? }`) is set when a session badge opened the panel (`badge.panel`, see [EXTENSIONS.md → Session badges](./EXTENSIONS.md#session-badges)) and stays on the panel's reloads until it is opened for something else. Both run scoped to `sessionId`: `ctx.artifacts.list()` answers that chat's artifacts (GUI main and TUI alike).
62
+
60
63
  ### `PanelContent`
61
64
 
62
65
  | Field | Meaning |
package/docs/README.md CHANGED
@@ -15,7 +15,10 @@ Each topic has exactly one owner doc; the others link to it. If two docs seem to
15
15
  | Chat + surfaces | [CHAT.md](./CHAT.md) | `zidane/chat`: `resolveConfig`, session store + metadata, settings, safe mode, per-edit approval, todos, built-in commands, completion providers, theme, OAuth. |
16
16
  | Chat + surfaces | [TUI.md](./TUI.md) | `zidane/tui`: screens, **keybindings table**, modals, transcript rendering, code highlighting, embedding, file layout. |
17
17
  | Chat + surfaces | [IPC.md](./IPC.md) | `zidane/ipc`: daemon lifecycle, **every RPC and signal**, multi-window convergence, TUI/GUI parity matrix, `zidane attach`. |
18
+ | Chat + surfaces | [SERVER.md](./SERVER.md) | `zidane-server`: run the host on a remote machine. Docker, configuration, TLS / proxies, tokens, security model, operations. |
19
+ | Chat + surfaces | [MACHINES.md](./MACHINES.md) | Machines: computers lending a host their folders and shell (`local@laptop`), and a server lending itself to a local host (`local@server`). Protocol, offline semantics, project files, sandbox, client UI, security. |
18
20
  | Chat + surfaces | [ARTIFACTS.md](./ARTIFACTS.md) | Session artifacts: model, producers, storage, extension kinds, surfaces, RPCs, lazy refresh. |
21
+ | Chat + surfaces | [TERMINAL.md](./TERMINAL.md) | Terminals: PTYs in the session's environment (host, Docker, E2B, Daytona), host manager + pacing, IPC, agent `terminal_*` tools, background tasks in terminals, GUI drawer/tab. |
19
22
  | Chat + surfaces | [PANELS.md](./PANELS.md) | Side panels: the declarative content model, `ctx.registerPanel`, catalog + open-state rules shared by the TUI rail and the GUI side panel. |
20
23
  | Chat + surfaces | [EXTENSIONS.md](./EXTENSIONS.md) | Extensions: `defineExtension`, **the full `ctx` API**, discovery, install, collisions, settings UI, authoring patterns. |
21
24
  | Integrations | [PLUGINS.md](./PLUGINS.md) | Agent Plugins 1.0.0 loader: manifest, validation, filesystem boundary. |
@@ -33,6 +36,7 @@ Each topic has exactly one owner doc; the others link to it. If two docs seem to
33
36
  | Session persistence, `SessionStore` backends, sqlite schema | CHAT.md § Sessions |
34
37
  | Artifacts (PRs, files a session produced) | ARTIFACTS.md |
35
38
  | A side-panel tab (extension or built-in) | PANELS.md |
39
+ | Terminals (drawer, `terminal_*` tools, `openTerminal`) | TERMINAL.md |
36
40
  | Settings keys and defaults | CHAT.md § Settings |
37
41
  | Safe mode, approvals, per-edit approval | CHAT.md § Safe mode / Per-edit approval |
38
42
  | Todos (`todowrite`) contract | CHAT.md § Todos |
package/docs/RESTATE.md CHANGED
@@ -307,7 +307,7 @@ createAgent({
307
307
 
308
308
  ### Child events
309
309
 
310
- Hook bubbling can't cross an invocation boundary, so each child's transcript is **replayed onto the parent's bus when it merges** (`child:stream:text`, `child:tool:before/after/error`, `child:turn:after`, `child:stream:end`) — existing `child:tool:*` listeners need no changes. Every event carries `childId`, `depth`, and (tool events) `runId`/`parentRunId`/`parentCallId`; blocks stay contiguous per child in submission order. Not synthesized: `tool:gate`, `tool:transform`, `tool:dispatched`, `tool:cancelled`, `background:*` (decisions taken inside another process).
310
+ Hook bubbling can't cross an invocation boundary, so each child's transcript is **replayed onto the parent's bus when it merges** (`child:stream:text`, `child:tool:before/after/error`, `child:turn:after`, `child:stream:end`) — existing `child:tool:*` listeners need no changes. Every event carries `childId`, `depth`, and (tool events) `runId`/`parentRunId`/`parentCallId`; blocks stay contiguous per child in submission order. Not synthesized: `tool:gate`, `tool:transform`, `tool:dispatched`, `tool:cancelled`, `background:*` (decisions taken inside another process). A child's own merged descendants are announced as `child:spawn:before` → their events → (`child:spawn:error`) → `child:spawn:complete`, with `parentChildId` set to the merged label of their spawner — the lifecycle an in-process nested spawn bubbles.
311
311
 
312
312
  They **re-fire on a parent replay** — dedupe by `callId`/`turnId`, or gate on `createReplayTracker`. Disable with `emitChildEvents: false`.
313
313
 
@@ -367,7 +367,8 @@ createAgent({
367
367
  | `disableBackgroundTasks: 'non-durable'` | Background tasks (`shell` with `run_in_background`) stay enabled only when the context declares `capabilities.detachedTasks: 'durable'` — host-local tasks aren't journaled. |
368
368
  | `cache: true` | Stable system/tool prefixes reward prompt-cache breakpoints, cutting cold-start cost on retries. |
369
369
  | `shouldRethrowToolError: shouldRethrowRestateControlError` | Restate terminal/suspension control errors escape the tool-result path instead of becoming a fake tool error. |
370
- | `compactStrategy: 'tail'`, `compactThreshold: 128 KiB`, `compactKeepTurns: 8`, `elideStaleReads: 'stub'`, `mediaKeepTurns: 8`, `toolOutputBudget: 64 KiB`, `autoCompact: { threshold: 0.85 }` | Shrinks the per-turn provider payload — journal weight and latency, not just tokens. |
370
+ | `compactStrategy: 'tail'`, `compactThreshold: 128 KiB`, `compactKeepTurns: 8`, `elideStaleReads: 'stub'`, `toolOutputBudget: 64 KiB`, `autoCompact: { threshold: 0.85 }` | Shrinks the per-turn provider payload — journal weight and latency, not just tokens. |
371
+ | `mediaBudget: { maxItems: 80, maxBytes: 20 MiB }`, `maxImageEdge: 2000` | Media stays inline until near the provider's caps, so a long image-heavy run isn't re-billed once per aged screenshot; oversize PNGs shrink once when they enter the session. Requests carry more media bytes than the old 8-message window; pass `mediaKeepTurns: 8` to get that window back (it takes precedence). |
371
372
 
372
373
  Add `maxCostUsd`/`maxTotalTokens` as a server-side budget breaker: throws `AgentBudgetExceededError` and finalizes the run as `aborted` — durable replay+retry otherwise turns a runaway loop into a runaway bill. See `docs/ARCHITECTURE.md` § run-level budget circuit breaker.
373
374
 
@@ -99,7 +99,7 @@ All optional on `ExecutionContext` (`src/contexts/types.ts`) — a context imple
99
99
  | `killBackground(handle, taskId)` | SIGTERMs the whole process group; resolves once the process has exited and its output stream is flushed+closed. Returns `null` on an unknown/already-cleaned-up id — idempotent |
100
100
  | `listBackground(handle)` | Snapshot of every task (running + terminated) owned by `handle`. Terminated entries stay until context `destroy()` |
101
101
  | `waitBackground(handle, taskId, { timeoutMs?, signal? })` | Resolves with `TaskExitInfo` on exit, or `null` on timeout/abort/unknown id. In-process contexts implement it on `onExit`; durable hosts implement it as an awakeable park |
102
- | `reassignBackgroundTasks(fromHandle, toHandle, newOnExit?)` | Transfers ownership of every still-running task between handles, rewriting `onExit` to the new owner's callback. Returns the reassigned entries. Optional — without it, `destroy()` kills the source handle's tasks instead |
102
+ | `reassignBackgroundTasks(fromHandle, toHandle, newOnExit?, newOnStall?)` | Transfers ownership of every still-running task between handles, replacing the owner's `onExit` / `onStall` with the new owner's callbacks (in-flight kill/wait resolvers are kept). With `fromHandle === toHandle` only the callbacks move — how a host hands tasks to a rebuilt agent. Returns the reassigned entries. Optional — without it, `destroy()` kills the source handle's tasks instead |
103
103
 
104
104
  `TaskExitInfo`: `{ taskId, status: 'exited' | 'killed', exitCode, signal?, outputPath, durationMs, command }`. `exitCode` is `128 + signal-number` on signal-killed children (POSIX convention — `143` for SIGTERM).
105
105
 
@@ -138,7 +138,15 @@ wake → next agent.run() drains the queue → <task-notification> prepended t
138
138
 
139
139
  Every step is bounded by `destroyTimeoutMs`; a step that doesn't settle in time is abandoned (logged under `ZIDANE_DEBUG`) rather than hanging teardown.
140
140
 
141
- No detach-on-destroy option: destroy always kills every background task. Tasks that should outlive the zidane process are the user's job (`tmux`, `nohup`), not this feature's.
141
+ No detach-on-destroy option: destroying the handle always kills its background tasks. Tasks that should outlive the zidane process are the user's job (`tmux`, `nohup`), not this feature's.
142
+
143
+ ### Surviving agent rebuilds (chat hosts)
144
+
145
+ The daemon / GUI host and the TUI rebuild a session's agent for reasons unrelated to execution: model or profile swap, settings changes (extensions, MCPs, skills, plugins), MCP login, idle eviction. They build agents on a **host-owned handle** from `src/chat/execution-cache.ts` (`AgentOptions.handle`, lazy form), so step 4 above is skipped and running tasks survive the rebuild:
146
+
147
+ - The rebuilt agent `adopt`s them: exit/stall callbacks are rebound to its hooks and each task is announced as `background:adopt`, which hosts register like `background:start`. A task that exited while no agent owned it reaches the model through the next run's pull reconcile.
148
+ - The handle is released, killing its tasks, when the **session** is done with it: deleted, idle-evicted with nothing running, host drain (process exit), or — in the TUI — switching sessions.
149
+ - A changed context configuration (cwd, sandbox policy) replaces the context and ends its tasks: they were started under the old configuration.
142
150
 
143
151
  ## Subagents
144
152