@intentic/sandbox-contract 1.344.0 → 1.346.0

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 (418) hide show
  1. package/README.md +27 -9
  2. package/dist/contracts/accounts.contract.d.ts +24 -80
  3. package/dist/contracts/accounts.contract.d.ts.map +1 -1
  4. package/dist/contracts/agent.contract.d.ts +247 -8
  5. package/dist/contracts/agent.contract.d.ts.map +1 -1
  6. package/dist/contracts/agent.contract.js +13 -2
  7. package/dist/contracts/agent.contract.js.map +1 -1
  8. package/dist/contracts/agents.contract.d.ts +1570 -33
  9. package/dist/contracts/agents.contract.d.ts.map +1 -1
  10. package/dist/contracts/agents.contract.js +47 -4
  11. package/dist/contracts/agents.contract.js.map +1 -1
  12. package/dist/contracts/device.contract.d.ts +244 -1
  13. package/dist/contracts/device.contract.d.ts.map +1 -1
  14. package/dist/contracts/device.contract.js +5 -1
  15. package/dist/contracts/device.contract.js.map +1 -1
  16. package/dist/contracts/endpoints.contract.d.ts +2 -1
  17. package/dist/contracts/endpoints.contract.d.ts.map +1 -1
  18. package/dist/contracts/endpoints.contract.js +1 -0
  19. package/dist/contracts/endpoints.contract.js.map +1 -1
  20. package/dist/contracts/providers.contract.d.ts +0 -1
  21. package/dist/contracts/providers.contract.d.ts.map +1 -1
  22. package/dist/contracts/push.contract.d.ts +11 -0
  23. package/dist/contracts/push.contract.d.ts.map +1 -1
  24. package/dist/contracts/push.contract.js +10 -1
  25. package/dist/contracts/push.contract.js.map +1 -1
  26. package/dist/contracts/runner.contract.d.ts +149 -123
  27. package/dist/contracts/runner.contract.d.ts.map +1 -1
  28. package/dist/contracts/secrets.contract.d.ts +169 -0
  29. package/dist/contracts/secrets.contract.d.ts.map +1 -1
  30. package/dist/contracts/secrets.contract.js +35 -0
  31. package/dist/contracts/secrets.contract.js.map +1 -1
  32. package/dist/contracts/sessions.contract.d.ts +2 -0
  33. package/dist/contracts/sessions.contract.d.ts.map +1 -1
  34. package/dist/contracts/settings.contract.d.ts +37 -0
  35. package/dist/contracts/settings.contract.d.ts.map +1 -1
  36. package/dist/contracts/system.contract.d.ts +235 -31
  37. package/dist/contracts/system.contract.d.ts.map +1 -1
  38. package/dist/contracts/translator.contract.d.ts +24 -80
  39. package/dist/contracts/translator.contract.d.ts.map +1 -1
  40. package/dist/contracts/workspace.contract.d.ts +11 -0
  41. package/dist/contracts/workspace.contract.d.ts.map +1 -1
  42. package/dist/contracts/workspace.contract.js +11 -1
  43. package/dist/contracts/workspace.contract.js.map +1 -1
  44. package/dist/events/agent-events.d.ts +60 -0
  45. package/dist/events/agent-events.d.ts.map +1 -1
  46. package/dist/events/agent-events.js +44 -38
  47. package/dist/events/agent-events.js.map +1 -1
  48. package/dist/events/agent-transcript.d.ts +727 -0
  49. package/dist/events/agent-transcript.d.ts.map +1 -0
  50. package/dist/events/agent-transcript.js +34 -0
  51. package/dist/events/agent-transcript.js.map +1 -0
  52. package/dist/events/agent-words.d.ts +5 -0
  53. package/dist/events/agent-words.d.ts.map +1 -1
  54. package/dist/events/agent-words.js +9 -0
  55. package/dist/events/agent-words.js.map +1 -1
  56. package/dist/events/failure-rows.d.ts +14 -0
  57. package/dist/events/failure-rows.d.ts.map +1 -0
  58. package/dist/events/failure-rows.js +109 -0
  59. package/dist/events/failure-rows.js.map +1 -0
  60. package/dist/events/requests.d.ts +143 -0
  61. package/dist/events/requests.d.ts.map +1 -1
  62. package/dist/events/requests.js +35 -1
  63. package/dist/events/requests.js.map +1 -1
  64. package/dist/events/sandbox-notice.d.ts +23 -0
  65. package/dist/events/sandbox-notice.d.ts.map +1 -1
  66. package/dist/events/sandbox-notice.js +2 -0
  67. package/dist/events/sandbox-notice.js.map +1 -1
  68. package/dist/events/system-events.d.ts +156 -0
  69. package/dist/events/system-events.d.ts.map +1 -1
  70. package/dist/events/system-events.js +7 -1
  71. package/dist/events/system-events.js.map +1 -1
  72. package/dist/events/transcript.d.ts +20 -720
  73. package/dist/events/transcript.d.ts.map +1 -1
  74. package/dist/events/transcript.js +17 -19
  75. package/dist/events/transcript.js.map +1 -1
  76. package/dist/ids/conversation-ids.d.ts +2 -0
  77. package/dist/ids/conversation-ids.d.ts.map +1 -1
  78. package/dist/ids/conversation-ids.js +2 -0
  79. package/dist/ids/conversation-ids.js.map +1 -1
  80. package/dist/ids/row-ids.d.ts +3 -0
  81. package/dist/ids/row-ids.d.ts.map +1 -0
  82. package/dist/ids/row-ids.js +11 -0
  83. package/dist/ids/row-ids.js.map +1 -0
  84. package/dist/index.d.ts +2296 -199
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/models/agent-lanes.d.ts.map +1 -1
  87. package/dist/models/agent-lanes.js +6 -7
  88. package/dist/models/agent-lanes.js.map +1 -1
  89. package/dist/models/agent-standing.d.ts +6 -19
  90. package/dist/models/agent-standing.d.ts.map +1 -1
  91. package/dist/models/agent-standing.js +14 -38
  92. package/dist/models/agent-standing.js.map +1 -1
  93. package/dist/models/model-pins.d.ts +3 -0
  94. package/dist/models/model-pins.d.ts.map +1 -1
  95. package/dist/models/model-pins.js +2 -0
  96. package/dist/models/model-pins.js.map +1 -1
  97. package/dist/models/plan-pools.d.ts +26 -4
  98. package/dist/models/plan-pools.d.ts.map +1 -1
  99. package/dist/models/plan-pools.js +159 -40
  100. package/dist/models/plan-pools.js.map +1 -1
  101. package/dist/netd/generated/wire.d.ts +5 -0
  102. package/dist/netd/generated/wire.d.ts.map +1 -1
  103. package/dist/policy/command-classes.d.ts +1 -1
  104. package/dist/policy/command-classes.d.ts.map +1 -1
  105. package/dist/policy/command-classes.js +2 -2
  106. package/dist/policy/command-classes.js.map +1 -1
  107. package/dist/policy/keyring-origins.d.ts +27 -0
  108. package/dist/policy/keyring-origins.d.ts.map +1 -0
  109. package/dist/policy/keyring-origins.js +213 -0
  110. package/dist/policy/keyring-origins.js.map +1 -0
  111. package/dist/policy/keyring-seal.d.ts +9 -0
  112. package/dist/policy/keyring-seal.d.ts.map +1 -1
  113. package/dist/policy/keyring-seal.js +18 -6
  114. package/dist/policy/keyring-seal.js.map +1 -1
  115. package/dist/policy/safety-policy.d.ts +1 -1
  116. package/dist/policy/safety-policy.d.ts.map +1 -1
  117. package/dist/policy/safety-policy.js +1 -1
  118. package/dist/policy/safety-policy.js.map +1 -1
  119. package/dist/policy/turned-away.d.ts +1 -0
  120. package/dist/policy/turned-away.d.ts.map +1 -1
  121. package/dist/policy/turned-away.js +10 -0
  122. package/dist/policy/turned-away.js.map +1 -1
  123. package/dist/protocol/doors/host-protocol.d.ts +1 -0
  124. package/dist/protocol/doors/host-protocol.d.ts.map +1 -1
  125. package/dist/protocol/doors/host-protocol.js +1 -0
  126. package/dist/protocol/doors/host-protocol.js.map +1 -1
  127. package/dist/protocol/doors/runner-protocol.d.ts +1 -0
  128. package/dist/protocol/doors/runner-protocol.d.ts.map +1 -1
  129. package/dist/protocol/doors/runner-protocol.js +1 -0
  130. package/dist/protocol/doors/runner-protocol.js.map +1 -1
  131. package/dist/protocol/doors/webext-links.d.ts +1 -0
  132. package/dist/protocol/doors/webext-links.d.ts.map +1 -1
  133. package/dist/protocol/doors/webext-links.js +1 -0
  134. package/dist/protocol/doors/webext-links.js.map +1 -1
  135. package/dist/protocol/peer-dial.d.ts +2 -0
  136. package/dist/protocol/peer-dial.d.ts.map +1 -1
  137. package/dist/protocol/peer-dial.js +62 -29
  138. package/dist/protocol/peer-dial.js.map +1 -1
  139. package/dist/protocol/raw/raw-json-routes.d.ts +179 -1
  140. package/dist/protocol/raw/raw-json-routes.d.ts.map +1 -1
  141. package/dist/protocol/raw/raw-json-routes.js +5 -0
  142. package/dist/protocol/raw/raw-json-routes.js.map +1 -1
  143. package/dist/protocol/raw/raw-routes.d.ts +35 -0
  144. package/dist/protocol/raw/raw-routes.d.ts.map +1 -1
  145. package/dist/protocol/raw/raw-routes.js +9 -0
  146. package/dist/protocol/raw/raw-routes.js.map +1 -1
  147. package/dist/schemas/agent-next.d.ts +21 -0
  148. package/dist/schemas/agent-next.d.ts.map +1 -0
  149. package/dist/schemas/agent-next.js +21 -0
  150. package/dist/schemas/agent-next.js.map +1 -0
  151. package/dist/schemas/agent-place.d.ts +10 -0
  152. package/dist/schemas/agent-place.d.ts.map +1 -0
  153. package/dist/schemas/agent-place.js +15 -0
  154. package/dist/schemas/agent-place.js.map +1 -0
  155. package/dist/schemas/agent-search.d.ts +45 -0
  156. package/dist/schemas/agent-search.d.ts.map +1 -0
  157. package/dist/schemas/agent-search.js +28 -0
  158. package/dist/schemas/agent-search.js.map +1 -0
  159. package/dist/schemas/agent.d.ts +120 -78
  160. package/dist/schemas/agent.d.ts.map +1 -1
  161. package/dist/schemas/agent.js +40 -49
  162. package/dist/schemas/agent.js.map +1 -1
  163. package/dist/schemas/agents-list.d.ts +54 -0
  164. package/dist/schemas/agents-list.d.ts.map +1 -1
  165. package/dist/schemas/agents.d.ts +165 -59
  166. package/dist/schemas/agents.d.ts.map +1 -1
  167. package/dist/schemas/agents.js +25 -50
  168. package/dist/schemas/agents.js.map +1 -1
  169. package/dist/schemas/attach.d.ts +8 -0
  170. package/dist/schemas/attach.d.ts.map +1 -0
  171. package/dist/schemas/attach.js +14 -0
  172. package/dist/schemas/attach.js.map +1 -0
  173. package/dist/schemas/command-admission.d.ts +71 -0
  174. package/dist/schemas/command-admission.d.ts.map +1 -0
  175. package/dist/schemas/command-admission.js +27 -0
  176. package/dist/schemas/command-admission.js.map +1 -0
  177. package/dist/schemas/compose-overlay.d.ts +9 -0
  178. package/dist/schemas/compose-overlay.d.ts.map +1 -0
  179. package/dist/schemas/compose-overlay.js +11 -0
  180. package/dist/schemas/compose-overlay.js.map +1 -0
  181. package/dist/schemas/composer-drafts.d.ts +169 -0
  182. package/dist/schemas/composer-drafts.d.ts.map +1 -0
  183. package/dist/schemas/composer-drafts.js +60 -0
  184. package/dist/schemas/composer-drafts.js.map +1 -0
  185. package/dist/schemas/conversation-links.d.ts +44 -0
  186. package/dist/schemas/conversation-links.d.ts.map +1 -0
  187. package/dist/schemas/conversation-links.js +35 -0
  188. package/dist/schemas/conversation-links.js.map +1 -0
  189. package/dist/schemas/data-protection.d.ts +1 -1
  190. package/dist/schemas/data-protection.d.ts.map +1 -1
  191. package/dist/schemas/device-commands.d.ts +63 -0
  192. package/dist/schemas/device-commands.d.ts.map +1 -0
  193. package/dist/schemas/device-commands.js +42 -0
  194. package/dist/schemas/device-commands.js.map +1 -0
  195. package/dist/schemas/device-folder.d.ts +11 -0
  196. package/dist/schemas/device-folder.d.ts.map +1 -1
  197. package/dist/schemas/device-folder.js +1 -0
  198. package/dist/schemas/device-folder.js.map +1 -1
  199. package/dist/schemas/devices.d.ts +18 -68
  200. package/dist/schemas/devices.d.ts.map +1 -1
  201. package/dist/schemas/devices.js +8 -45
  202. package/dist/schemas/devices.js.map +1 -1
  203. package/dist/schemas/environment.d.ts.map +1 -1
  204. package/dist/schemas/environment.js.map +1 -1
  205. package/dist/schemas/hosts.d.ts +29 -0
  206. package/dist/schemas/hosts.d.ts.map +1 -1
  207. package/dist/schemas/hosts.js +23 -0
  208. package/dist/schemas/hosts.js.map +1 -1
  209. package/dist/schemas/keyring-native.d.ts +137 -0
  210. package/dist/schemas/keyring-native.d.ts.map +1 -0
  211. package/dist/schemas/keyring-native.js +69 -0
  212. package/dist/schemas/keyring-native.js.map +1 -0
  213. package/dist/schemas/keyring.d.ts +413 -0
  214. package/dist/schemas/keyring.d.ts.map +1 -1
  215. package/dist/schemas/keyring.js +139 -4
  216. package/dist/schemas/keyring.js.map +1 -1
  217. package/dist/schemas/phone-passwords.d.ts +186 -0
  218. package/dist/schemas/phone-passwords.d.ts.map +1 -0
  219. package/dist/schemas/phone-passwords.js +25 -0
  220. package/dist/schemas/phone-passwords.js.map +1 -0
  221. package/dist/schemas/privacy-egress.d.ts +41 -0
  222. package/dist/schemas/privacy-egress.d.ts.map +1 -0
  223. package/dist/schemas/privacy-egress.js +30 -0
  224. package/dist/schemas/privacy-egress.js.map +1 -0
  225. package/dist/schemas/privacy.d.ts +1 -0
  226. package/dist/schemas/privacy.d.ts.map +1 -1
  227. package/dist/schemas/privacy.js +1 -0
  228. package/dist/schemas/privacy.js.map +1 -1
  229. package/dist/schemas/providers/plan-limits.d.ts +76 -130
  230. package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
  231. package/dist/schemas/providers/plan-limits.js +36 -23
  232. package/dist/schemas/providers/plan-limits.js.map +1 -1
  233. package/dist/schemas/providers/provider-oauth.d.ts +24 -82
  234. package/dist/schemas/providers/provider-oauth.d.ts.map +1 -1
  235. package/dist/schemas/providers/provider-oauth.js +1 -6
  236. package/dist/schemas/providers/provider-oauth.js.map +1 -1
  237. package/dist/schemas/providers/refusals.d.ts +64 -0
  238. package/dist/schemas/providers/refusals.d.ts.map +1 -0
  239. package/dist/schemas/providers/refusals.js +33 -0
  240. package/dist/schemas/providers/refusals.js.map +1 -0
  241. package/dist/schemas/providers/usage.d.ts +1 -0
  242. package/dist/schemas/providers/usage.d.ts.map +1 -1
  243. package/dist/schemas/providers/usage.js +1 -0
  244. package/dist/schemas/providers/usage.js.map +1 -1
  245. package/dist/schemas/push.d.ts +13 -0
  246. package/dist/schemas/push.d.ts.map +1 -1
  247. package/dist/schemas/push.js +11 -1
  248. package/dist/schemas/push.js.map +1 -1
  249. package/dist/schemas/sandbox-passwords.d.ts +288 -0
  250. package/dist/schemas/sandbox-passwords.d.ts.map +1 -0
  251. package/dist/schemas/sandbox-passwords.js +66 -0
  252. package/dist/schemas/sandbox-passwords.js.map +1 -0
  253. package/dist/schemas/secrets.d.ts +2 -0
  254. package/dist/schemas/secrets.d.ts.map +1 -1
  255. package/dist/schemas/secrets.js +4 -0
  256. package/dist/schemas/secrets.js.map +1 -1
  257. package/dist/schemas/sessions.js +1 -1
  258. package/dist/schemas/sessions.js.map +1 -1
  259. package/dist/schemas/settings-savings.d.ts +709 -0
  260. package/dist/schemas/settings-savings.d.ts.map +1 -0
  261. package/dist/schemas/settings-savings.js +53 -0
  262. package/dist/schemas/settings-savings.js.map +1 -0
  263. package/dist/schemas/settings.d.ts +7 -671
  264. package/dist/schemas/settings.d.ts.map +1 -1
  265. package/dist/schemas/settings.js +5 -50
  266. package/dist/schemas/settings.js.map +1 -1
  267. package/dist/schemas/system.d.ts +16 -0
  268. package/dist/schemas/system.d.ts.map +1 -1
  269. package/dist/schemas/system.js +11 -0
  270. package/dist/schemas/system.js.map +1 -1
  271. package/dist/schemas/terminal.d.ts +5 -0
  272. package/dist/schemas/terminal.d.ts.map +1 -1
  273. package/dist/schemas/terminal.js +4 -0
  274. package/dist/schemas/terminal.js.map +1 -1
  275. package/dist/schemas/work-assurance.d.ts +24 -0
  276. package/dist/schemas/work-assurance.d.ts.map +1 -0
  277. package/dist/schemas/work-assurance.js +23 -0
  278. package/dist/schemas/work-assurance.js.map +1 -0
  279. package/dist/schemas/workspace/workspace-tree.d.ts +23 -0
  280. package/dist/schemas/workspace/workspace-tree.d.ts.map +1 -1
  281. package/dist/schemas/workspace/workspace-tree.js +20 -0
  282. package/dist/schemas/workspace/workspace-tree.js.map +1 -1
  283. package/dist/state/auth-state.d.ts +4 -0
  284. package/dist/state/auth-state.d.ts.map +1 -0
  285. package/dist/state/auth-state.js +37 -0
  286. package/dist/state/auth-state.js.map +1 -0
  287. package/dist/state/definition.d.ts +12 -0
  288. package/dist/state/definition.d.ts.map +1 -1
  289. package/dist/state/runtime-state.d.ts +3 -0
  290. package/dist/state/runtime-state.d.ts.map +1 -1
  291. package/dist/state/runtime-state.js +1 -0
  292. package/dist/state/runtime-state.js.map +1 -1
  293. package/dist/state/sandbox-build.d.ts +20 -0
  294. package/dist/state/sandbox-build.d.ts.map +1 -0
  295. package/dist/state/sandbox-build.js +43 -0
  296. package/dist/state/sandbox-build.js.map +1 -0
  297. package/dist/state/ways-back.d.ts +4 -0
  298. package/dist/state/ways-back.d.ts.map +1 -0
  299. package/dist/state/ways-back.js +8 -0
  300. package/dist/state/ways-back.js.map +1 -0
  301. package/dist/text/land-rows.d.ts +14 -0
  302. package/dist/text/land-rows.d.ts.map +1 -0
  303. package/dist/text/land-rows.js +86 -0
  304. package/dist/text/land-rows.js.map +1 -0
  305. package/dist/text/transcript-fold.d.ts +4 -2
  306. package/dist/text/transcript-fold.d.ts.map +1 -1
  307. package/dist/text/transcript-fold.js +21 -187
  308. package/dist/text/transcript-fold.js.map +1 -1
  309. package/dist/webext/index.d.ts +1 -0
  310. package/dist/webext/index.d.ts.map +1 -1
  311. package/dist/webext/index.js +1 -0
  312. package/dist/webext/index.js.map +1 -1
  313. package/package.json +7 -6
  314. package/src/contracts/agent.contract.ts +13 -1
  315. package/src/contracts/agents.contract.ts +53 -28
  316. package/src/contracts/device.contract.ts +16 -0
  317. package/src/contracts/endpoints.contract.ts +3 -0
  318. package/src/contracts/push.contract.ts +23 -1
  319. package/src/contracts/secrets.contract.ts +39 -0
  320. package/src/contracts/workspace.contract.ts +16 -0
  321. package/src/events/agent-events.ts +99 -79
  322. package/src/events/agent-transcript.ts +48 -0
  323. package/src/events/agent-words.ts +13 -0
  324. package/src/events/failure-rows.ts +148 -0
  325. package/src/events/requests.ts +41 -3
  326. package/src/events/sandbox-notice.ts +14 -2
  327. package/src/events/system-events.ts +14 -4
  328. package/src/events/transcript.ts +30 -28
  329. package/src/ids/conversation-ids.ts +7 -0
  330. package/src/ids/project-dir.test.ts +2 -2
  331. package/src/ids/project-dir.ts +4 -4
  332. package/src/ids/row-ids.ts +20 -0
  333. package/src/models/agent-lanes.ts +16 -24
  334. package/src/models/agent-runtimes.ts +2 -2
  335. package/src/models/agent-standing.ts +38 -95
  336. package/src/models/model-pins.test.ts +31 -1
  337. package/src/models/model-pins.ts +17 -0
  338. package/src/models/plan-pools.test.ts +274 -31
  339. package/src/models/plan-pools.ts +294 -79
  340. package/src/netd/generated/netd-wire.json +25 -0
  341. package/src/netd/generated/wire.ts +1 -1
  342. package/src/policy/command-classes.ts +2 -2
  343. package/src/policy/keyring-origins.test.ts +132 -0
  344. package/src/policy/keyring-origins.ts +287 -0
  345. package/src/policy/keyring-seal.test.ts +37 -2
  346. package/src/policy/keyring-seal.ts +33 -7
  347. package/src/policy/safety-policy.ts +1 -1
  348. package/src/policy/tests/safety-policy.test.ts +1 -1
  349. package/src/policy/turned-away.ts +15 -0
  350. package/src/protocol/doors/host-protocol.ts +3 -0
  351. package/src/protocol/doors/runner-protocol.ts +4 -1
  352. package/src/protocol/doors/webext-links.ts +3 -0
  353. package/src/protocol/peer-dial.test.ts +90 -4
  354. package/src/protocol/peer-dial.ts +83 -39
  355. package/src/protocol/raw/raw-json-routes.ts +13 -0
  356. package/src/protocol/raw/raw-routes.ts +13 -1
  357. package/src/schemas/agent-next.ts +34 -0
  358. package/src/schemas/agent-place.ts +22 -0
  359. package/src/schemas/agent-search.ts +51 -0
  360. package/src/schemas/agent.ts +63 -90
  361. package/src/schemas/agents.ts +55 -99
  362. package/src/schemas/attach.ts +23 -0
  363. package/src/schemas/command-admission.ts +57 -0
  364. package/src/schemas/compose-overlay.test.ts +35 -0
  365. package/src/schemas/compose-overlay.ts +19 -0
  366. package/src/schemas/composer-drafts.ts +86 -0
  367. package/src/schemas/conversation-links.ts +62 -0
  368. package/src/schemas/data-protection.ts +1 -1
  369. package/src/schemas/device-commands.ts +89 -0
  370. package/src/schemas/device-folder.ts +9 -0
  371. package/src/schemas/devices.ts +28 -110
  372. package/src/schemas/environment.ts +8 -9
  373. package/src/schemas/hosts.ts +60 -0
  374. package/src/schemas/keyring-native.test.ts +13 -0
  375. package/src/schemas/keyring-native.ts +101 -0
  376. package/src/schemas/keyring.ts +202 -4
  377. package/src/schemas/machine-presence.test.ts +33 -0
  378. package/src/schemas/phone-passwords.ts +51 -0
  379. package/src/schemas/privacy-egress.test.ts +83 -0
  380. package/src/schemas/privacy-egress.ts +86 -0
  381. package/src/schemas/privacy.ts +4 -0
  382. package/src/schemas/project-delivery.ts +1 -1
  383. package/src/schemas/providers/plan-limits.test.ts +16 -0
  384. package/src/schemas/providers/plan-limits.ts +55 -49
  385. package/src/schemas/providers/provider-oauth.ts +1 -13
  386. package/src/schemas/providers/refusals.ts +62 -0
  387. package/src/schemas/providers/usage.ts +2 -0
  388. package/src/schemas/push.ts +19 -3
  389. package/src/schemas/sandbox-passwords.ts +88 -0
  390. package/src/schemas/secrets.ts +4 -0
  391. package/src/schemas/sessions.ts +1 -1
  392. package/src/schemas/settings-savings.ts +125 -0
  393. package/src/schemas/settings.ts +10 -116
  394. package/src/schemas/system.ts +15 -0
  395. package/src/schemas/terminal.ts +8 -0
  396. package/src/schemas/work-assurance.ts +36 -0
  397. package/src/schemas/workspace/workspace-tree.ts +32 -0
  398. package/src/state/auth-state.ts +89 -0
  399. package/src/state/runtime-state.ts +10 -4
  400. package/src/state/sandbox-build.test.ts +95 -0
  401. package/src/state/sandbox-build.ts +96 -0
  402. package/src/state/ways-back.test.ts +36 -0
  403. package/src/state/ways-back.ts +19 -0
  404. package/src/state/workspace-state.ts +1 -1
  405. package/src/text/land-rows.ts +111 -0
  406. package/src/text/transcript-fold.test.ts +105 -9
  407. package/src/text/transcript-fold.ts +30 -232
  408. package/src/webext/index.ts +2 -0
  409. package/dist/events/undelivered.d.ts +0 -6
  410. package/dist/events/undelivered.d.ts.map +0 -1
  411. package/dist/events/undelivered.js +0 -9
  412. package/dist/events/undelivered.js.map +0 -1
  413. package/dist/state/history-state.d.ts +0 -3
  414. package/dist/state/history-state.d.ts.map +0 -1
  415. package/dist/state/history-state.js +0 -137
  416. package/dist/state/history-state.js.map +0 -1
  417. package/src/events/undelivered.ts +0 -15
  418. package/src/state/history-state.ts +0 -270
@@ -0,0 +1,19 @@
1
+ import { z } from "zod";
2
+
3
+ // WHAT A TARGET IMAGE COMPOSES FOR A SANDBOX, as the host reads it: the one line the image's own composer prints
4
+ // (`node /opt/sandbox/dist/environment/compose-overlay.js --base <image>`, _sandbox/sandbox/src/environment/compose-overlay.ts) over read-only
5
+ // mounts of the sandbox's volumes before a swap onto it, which `ic` builds instead of the overlay the running daemon
6
+ // composed (_sandbox/ic/src/sandbox/composition.rs). golden/compose-overlay.json holds examples the Rust test reads.
7
+
8
+ // The one format `ic` reads; any other, or none, sends it back to the running daemon's overlay.
9
+ export const COMPOSE_OVERLAY_FORMAT = 1;
10
+
11
+ export const ComposedOverlayLineSchema = z.object({
12
+ compose: z.literal(COMPOSE_OVERLAY_FORMAT).describe("The format of this line. A reader takes no other."),
13
+ overlay: z
14
+ .string()
15
+ .nullable()
16
+ .describe("The Dockerfile the image's daemon composes on its first boot, byte for byte; null when the sandbox wants no overlay on it."),
17
+ hash: z.string().optional().describe("sha256 of `overlay`, what the new container is stamped with (SANDBOX_ENVIRONMENT_HASH). Absent with no overlay."),
18
+ });
19
+ export type ComposedOverlayLine = z.infer<typeof ComposedOverlayLineSchema>;
@@ -0,0 +1,86 @@
1
+ import { z } from "zod";
2
+ import { ATTACHMENT_LIMIT, ConversationIdSchema, ForkOfSchema, FreshOfSchema, QueuedServingSchema } from "./agent.js";
3
+
4
+ // A PERSON'S UNSENT WORDS FOR ONE CONVERSATION, held by the sandbox. Until 2026-10-20 a composer's words lived only in the
5
+ // browser: in each window's tab snapshot, moved into a per-browser "closed drafts" set when the tab closed and back out
6
+ // when it reopened, and broadcast between windows as previews. Every move was a chance to lose or duplicate them: a
7
+ // popped-out chat and the board each held a copy for a round trip, two windows wrote the same chat's words over each
8
+ // other, and another device never saw them at all. Now the sandbox holds the one record, per person and conversation, and
9
+ // a browser keeps only an optimistic copy of it. A draft never goes by itself (AgentSummary.next never names one), and it
10
+ // is not egress: no model reads it until the person sends it, when it becomes the message the privacy shield judges.
11
+
12
+ // The words as the composer holds them: what the message will say, its files, and who it will go to when it opens a
13
+ // conversation (the pick a draft for a conversation the sandbox has never seen has nowhere else to live).
14
+ const draftWords = {
15
+ text: z.string().max(200_000).describe("The words, exactly as typed."),
16
+ attachments: z
17
+ .array(z.string().min(1))
18
+ .max(ATTACHMENT_LIMIT)
19
+ .optional()
20
+ .describe("Files staged with it, as workspace paths already uploaded."),
21
+ serves: QueuedServingSchema.optional().describe(
22
+ "Who the message is to go to: the provider, loop, model, effort and account picked in the composer. Absent when nothing was picked.",
23
+ ),
24
+ forkOf: ForkOfSchema.optional().describe("Where the conversation this draft opens is cut from, when it is a fork no turn has started yet."),
25
+ freshOf: FreshOfSchema.optional().describe("The work a fresh session this draft opens starts from, when no turn has started it yet."),
26
+ };
27
+
28
+ export const ComposerDraftSchema = z.object({
29
+ conversationId: ConversationIdSchema.describe("The conversation the words are for. It may not exist yet: a new chat's words are a draft before its first turn."),
30
+ ...draftWords,
31
+ since: z.number().describe("When the composer first held these words unsent, in milliseconds."),
32
+ revision: z
33
+ .number()
34
+ .int()
35
+ .positive()
36
+ .describe("Moves with every write of any of your drafts. A write or a clear naming an older one is answered with the newer draft instead."),
37
+ });
38
+ export type ComposerDraft = z.infer<typeof ComposerDraftSchema>;
39
+
40
+ export const ComposerDraftsSchema = z.object({
41
+ drafts: z.array(ComposerDraftSchema).describe("Every draft you hold in this sandbox, newest first. Only your own: another member's unsent words are theirs."),
42
+ revision: z
43
+ .number()
44
+ .int()
45
+ .nonnegative()
46
+ .optional()
47
+ .describe(
48
+ "The newest revision handed out when this list was read. A draft you hold at a revision past it was written after the read, so its absence here says nothing about it.",
49
+ ),
50
+ });
51
+ export type ComposerDrafts = z.infer<typeof ComposerDraftsSchema>;
52
+
53
+ export const DraftWriteSchema = z.object({
54
+ id: ConversationIdSchema.describe("The conversation the words are for."),
55
+ ...draftWords,
56
+ since: z.number().optional().describe("When the words were first held unsent. Leave it out to keep the draft's own, or to start it now."),
57
+ base: z
58
+ .number()
59
+ .int()
60
+ .positive()
61
+ .optional()
62
+ .describe(
63
+ "The draft's revision these words were edited from. When the draft has been written since (another window, another device), nothing is written and the newer draft comes back with `conflict`, so no one's words are overwritten unseen. Leave it out to write a draft you have never read.",
64
+ ),
65
+ });
66
+ export type DraftWrite = z.infer<typeof DraftWriteSchema>;
67
+
68
+ export const DraftWrittenSchema = z.object({
69
+ draft: ComposerDraftSchema.optional().describe("The draft as it now stands. Absent when there is none: it was cleared, or sent, before this write arrived."),
70
+ conflict: z
71
+ .literal(true)
72
+ .optional()
73
+ .describe("Nothing was written: the draft had moved past `base`. Merge your words into this one and write again from its revision."),
74
+ });
75
+ export type DraftWritten = z.infer<typeof DraftWrittenSchema>;
76
+
77
+ export const DraftClearSchema = z.object({
78
+ id: ConversationIdSchema.describe("The conversation whose draft to clear."),
79
+ revision: z
80
+ .number()
81
+ .int()
82
+ .positive()
83
+ .optional()
84
+ .describe("Clear it only while it is still this revision, so words written since in another window survive. Leave it out to clear whatever is there."),
85
+ });
86
+ export type DraftClear = z.infer<typeof DraftClearSchema>;
@@ -0,0 +1,62 @@
1
+ import { z } from "zod";
2
+ import { CONVERSATION_ID } from "../ids/conversation-ids.js";
3
+
4
+ // HOW ONE CONVERSATION RELATES TO ANOTHER at its birth, latched on its registry entry and carried on its card: the
5
+ // conversation a fork was cut from, and the work a fresh session was opened on. Re-exported by agent.ts, whose turn
6
+ // names them on a conversation's first turn.
7
+
8
+ // agent.ts's ConversationIdSchema, spelled here rather than imported, since agent.ts re-exports this module.
9
+ const ConversationIdSchema = z.string().regex(CONVERSATION_ID);
10
+
11
+ // Where a conversation was cut from, the durable half of a fork, carried on the registry entry. `index` is the message
12
+ // the cut sat above in the source, so its transcript can put the mark back.
13
+ export const ForkedFromSchema = z.object({
14
+ conversationId: ConversationIdSchema,
15
+ index: z.number().int().nonnegative(),
16
+ // Which files this fork opened on; kept so the fork can still say it later, not only right after the click.
17
+ files: z.enum(["then", "now"]),
18
+ });
19
+ export type ForkedFrom = z.infer<typeof ForkedFromSchema>;
20
+ // A FRESH SESSION on another conversation's work: a code review, a test pass, or a person's own ask, started from that
21
+ // conversation's work as it stands now and with none of its messages. A harness's own reviewer runs this way (Claude
22
+ // Code's /code-review in a forked subagent, Codex's /review in a thread of its own) so it judges the diff rather than
23
+ // the reasoning that produced it. Its first turn names the origin; the daemon then treats it as the origin's child: cut
24
+ // from its current work, its edits going back into it, folded under its card. It is still a person's chat, never woken
25
+ // for nor waking its origin: findings reach the origin only when a person sends them.
26
+ export const FRESH_KINDS = ["review", "test", "ask"] as const;
27
+ export const FreshKindSchema = z.enum(FRESH_KINDS);
28
+ export type FreshKind = z.infer<typeof FreshKindSchema>;
29
+ export const FreshOfSchema = z.object({
30
+ conversationId: ConversationIdSchema.describe("The conversation whose work this one starts from."),
31
+ kind: FreshKindSchema.describe("What it is opened for: a code review, a test pass, or the person's own ask."),
32
+ });
33
+ export type FreshOf = z.infer<typeof FreshOfSchema>;
34
+ // The durable half, latched on the entry by the first turn and never cleared: the relationship, not a pending state.
35
+ export const FreshSessionSchema = z.object({
36
+ of: ConversationIdSchema.describe("The conversation whose work it started from."),
37
+ kind: FreshKindSchema.describe("What it was opened for: a code review, a test pass, or the person's own ask."),
38
+ at: z.number().describe("When it started, in milliseconds. Work its origin changed after this is work it has not seen."),
39
+ });
40
+ export type FreshSession = z.infer<typeof FreshSessionSchema>;
41
+
42
+ export const FRESH_OF_DESCRIPTION =
43
+ "Open this conversation on another conversation's work as it stands now, with none of that conversation's messages: for a review or a test that should judge the work, not the reasoning behind it. Its edits go back into that work. First turn only, and only on a private copy.";
44
+
45
+ // The turn's own rules for `freshOf`: a new private copy of its own, never beside a fork's copy of a chat, never on itself.
46
+ export const freshOfIssues = (
47
+ turn: { readonly freshOf?: FreshOf | undefined; readonly conversationId?: string | undefined; readonly isolated?: boolean | undefined; readonly forkOf?: unknown },
48
+ ctx: z.RefinementCtx,
49
+ ): void => {
50
+ if (turn.freshOf === undefined) {
51
+ return;
52
+ }
53
+ if (turn.conversationId === undefined || turn.isolated !== true) {
54
+ ctx.addIssue({ code: "custom", message: "freshOf requires an isolated conversationId" });
55
+ }
56
+ if (turn.forkOf !== undefined) {
57
+ ctx.addIssue({ code: "custom", message: "freshOf and forkOf are two different starts: a fork carries the conversation, a fresh session carries none of it" });
58
+ }
59
+ if (turn.freshOf.conversationId === turn.conversationId) {
60
+ ctx.addIssue({ code: "custom", message: "a conversation cannot start fresh on its own work" });
61
+ }
62
+ };
@@ -4,7 +4,7 @@
4
4
  // behaviour except for restore points: that copy grows without bound on disk. Thirty days still covers recovering a
5
5
  // deleted file; zero remains available for people who want every snapshot. The public-office preset sets the rest.
6
6
  import { z } from "zod";
7
- import type { AdmissionRule } from "./agent.js";
7
+ import type { AdmissionRule } from "./command-admission.js";
8
8
 
9
9
  export const AgentRetentionDaysSchema = z
10
10
  .number()
@@ -0,0 +1,89 @@
1
+ import { z } from "zod";
2
+
3
+ // Closed set of command names; the daemon builds argv from the name alone (hosts/device-commands.ts). Never a free-text
4
+ // command: the same socket carries `run_command`, which would grant a shell on the user's machine.
5
+ // `sync-unpair` runs the machine's own `sync uninstall --sandbox` (terminates both Mutagen sessions, drops the pairing,
6
+ // self-revokes enrollment); revoking from the sandbox side is a different route.
7
+ // `dev-restart`, `dev-rebuild` and `sync-install` are the three whose argv the daemon fills from what only it knows — the
8
+ // dev checkout on that machine, a freshly minted pairing token — which is also why none takes a path or a token from the
9
+ // caller. The two dev commands are also why a machine-side op is the wrong shape for them: this door carries a COMMAND
10
+ // STRING to a tool every released agent already has, so a sandbox can drive a machine whose agent predates the feature.
11
+ // `dev-rebuild-log` is the read side of `dev-rebuild`: the build is detached out there, so its log is the only place its
12
+ // progress exists, and polling this is what turns a fired-and-forgotten command into something with a progress bar.
13
+ // `sync-clean` removes the build output left in directories the sandbox has deleted, which is what stops those
14
+ // deletions from ever landing. It deletes only content the session already ignores, so it is the one command here whose
15
+ // worst outcome is a rebuild — which is why it is a button and not a turn.
16
+ // `mirror-ignore`/`mirror-unignore` are `mirror-off`'s per-port form, and the reason they exist: a port number that is
17
+ // permanently taken on one machine's localhost has no answer in an all-or-nothing switch, so the choice lives where the
18
+ // conflict does — one port, one device, durable — instead of muting every port the pairing serves.
19
+ export const DeviceCommandSchema = z.enum([
20
+ "mirror-off",
21
+ "mirror-on",
22
+ "mirror-ignore",
23
+ "mirror-unignore",
24
+ "sync-pause",
25
+ "sync-resume",
26
+ "sync-unpair",
27
+ "sync-clean",
28
+ "dev-restart",
29
+ "dev-rebuild",
30
+ "dev-rebuild-log",
31
+ "sync-install",
32
+ ]);
33
+ export type DeviceCommand = z.infer<typeof DeviceCommandSchema>;
34
+ // The reversible sync switches: the subset a device's own row drives with a pair of buttons, as opposed to the two
35
+ // commands a card elsewhere issues once. Its own type so those button tables stay total without carrying entries for
36
+ // commands they can never send.
37
+ export const DeviceSyncSwitchSchema = DeviceCommandSchema.exclude(["dev-restart", "dev-rebuild", "dev-rebuild-log", "sync-install", "sync-clean"]);
38
+ export type DeviceSyncSwitch = z.infer<typeof DeviceSyncSwitchSchema>;
39
+
40
+ // Where a detached rebuild reports itself, and the reader of that report (dev-rebuild-log.ts).
41
+ export { DEV_REBUILD_EXIT_MARK, DEV_REBUILD_QUIET_MARK, type DevRebuildLog, devRebuildLogPath, readDevRebuildLog } from "./dev-rebuild-log.js";
42
+
43
+ // Machine's sandbox id (absent = every paired sandbox); must start alphanumeric, else it parses as a CLI flag.
44
+ export const DeviceSandboxIdSchema = z
45
+ .string()
46
+ .max(200)
47
+ .regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/);
48
+ // A folder on the DEVICE for `sync-install` to keep in step. The only caller-supplied string that reaches a command
49
+ // line, so its shape is the guard: `~`, an absolute POSIX path or a drive letter, and none of the characters that would
50
+ // end the argument it sits in. `~` is expanded by the daemon, which is why `$` is not allowed to arrive here. The line
51
+ // may be PowerShell, which reads the typographic quotes “ ” „ as double quotes and ‘ ’ ‚ ‛ as single quotes beside the
52
+ // ASCII ones, so those are refused too: the daemon quotes the folder, and this keeps a second guard where one quoter
53
+ // misses a character.
54
+ export const DeviceLocalDirSchema = z
55
+ .string()
56
+ .min(1)
57
+ .max(4096)
58
+ // The curly quotes as escapes: bun's transpiler rewrites a literal one in a regex to its escape and tsgo keeps it,
59
+ // so spelled literally the pattern reads differently from source and from dist, and contract.lock.json can match
60
+ // only one of them.
61
+ .regex(/^(?:~|\/|[A-Za-z]:[\\/])[^"'`$;|&\n\r\u201C\u201D\u201E\u2018\u2019\u201A\u201B]*$/);
62
+ // A TCP port number, the one value a caller supplies that reaches a command line as a number rather than a string.
63
+ export const PortNumberSchema = z.number().int().min(1).max(65535);
64
+ export const DeviceCommandInputSchema = z.object({
65
+ id: z.string().min(1),
66
+ command: DeviceCommandSchema,
67
+ sandboxId: DeviceSandboxIdSchema.optional(),
68
+ // `sync-install` only: which half to enroll (a member's "sync" still comes back mirror, decided daemon-side) and,
69
+ // for file sync, the folder on that device. The pairing token is minted by the daemon; no caller ever carries one.
70
+ mode: z.enum(["sync", "mirror"]).optional(),
71
+ localDir: DeviceLocalDirSchema.optional(),
72
+ // The two per-port mirror switches only. A number rather than a string, so nothing a caller sends can widen the
73
+ // command line it lands in.
74
+ port: PortNumberSchema.optional(),
75
+ });
76
+ export type DeviceCommandInput = z.infer<typeof DeviceCommandInputSchema>;
77
+ // `ok` is the command's own exit status, not this route's: a refusal or non-zero exit is a real answer, not a thrown
78
+ // error. Only an unreachable machine throws. `output` is the command's own printed text.
79
+ export const DeviceCommandResultSchema = z.object({
80
+ ok: z.boolean(),
81
+ message: z.string(),
82
+ output: z.string().optional(),
83
+ // WHICH KIND OF `ok: false` THIS IS, because a caller that repeats itself needs to tell them apart: the device
84
+ // turning the command away (a switch of its own is off, the path is out of its reach) will turn it away again,
85
+ // whereas a command it accepted and then killed at its deadline says nothing except that this attempt was too slow.
86
+ refused: z.boolean(),
87
+ });
88
+ export type DeviceCommandResult = z.infer<typeof DeviceCommandResultSchema>;
89
+
@@ -33,6 +33,15 @@ export type DeviceConflict = z.infer<typeof DeviceConflictSchema>;
33
33
  export const clearableOnDevice = (conflict: DeviceConflict): boolean =>
34
34
  conflict.nature === "derived-leftover" && conflict.local === "untracked" && conflict.sandbox === "deleted";
35
35
 
36
+ // WHY A FOLDER'S FILE SYNC IS HELD STILL, AND BY WHOM (2026-10-20). The machine agent reads each off a fact it keeps
37
+ // on the pairing and makes Mutagen agree every pass (its sync/holds.ts), so a pause never outlives its reason and a
38
+ // resume never undoes somebody else's. `owner` is a person's Pause, the only one a person lifts; every other is the
39
+ // agent's own and lifts by itself once its reason ends. `transfer` (2026-10-21) is a bring-back, a restore or a delivery
40
+ // of landed work writing into the folder. Strings on the wire, so a reason a newer agent adds still reads as held to an
41
+ // older page.
42
+ export const DeviceSyncHoldSchema = z.enum(["owner", "gone", "container-missing", "container-trashed", "swap", "unreachable", "low-disk", "transfer"]);
43
+ export type DeviceSyncHold = z.infer<typeof DeviceSyncHoldSchema>;
44
+
36
45
  // The drive reading that paused a pairing for want of room: when (epoch ms), and how much was free of how much.
37
46
  export const DeviceLowDiskSchema = z.object({
38
47
  since: z.number(),
@@ -1,7 +1,8 @@
1
1
  // What one of the user's own machines is running.
2
2
  import { z } from "zod";
3
+ import { PortNumberSchema } from "./device-commands.js";
3
4
  import { DeviceBridgeHoldSchema, DeviceConflictSchema, DeviceLowDiskSchema } from "./device-folder.js";
4
- import { parseHostConnection, type DeviceFacts, DeviceFactsSchema, MachineIdSchema, userDistrosOf, WslEnvironmentSchema } from "./hosts.js";
5
+ import { parseHostConnection, type DeviceFacts, DeviceFactsSchema, MachineIdentitySchema, MachineIdSchema, userDistrosOf, WslEnvironmentSchema } from "./hosts.js";
5
6
  import { RollbackTargetSchema, UpdateOutcomeSchema } from "./updates.js";
6
7
  import { DEV_VERSION } from "../state/versions.js";
7
8
  // Desktop-sync report shape shared by the agent, daemon and browser, produced only by the agent's own `deviceReport`.
@@ -117,13 +118,13 @@ export type DeviceSandbox = z.infer<typeof DeviceSandboxSchema>;
117
118
  // One operation on one sandbox, streamed as lines ending in a `result` or `error` frame. `prepare` builds the pending
118
119
  // update without touching the container. `set-shape` sets its resources and privileges, now or for the next restart;
119
120
  // `forget-shape` drops a shape saved for the next restart. `start`/`restart` apply a saved shape (all through `ic`).
120
- // `reshape` is the older spelling of `set-shape`, kept one release for pages served by older sandboxes.
121
- // `runner-up`/`runner-remove` act on a runner (this sandbox's own container), not a person's sandbox; gated by the
122
- // `sandboxes` switch, not removal, since a runner holds no separate workspace to lose.
121
+ // `reshape` is `set-shape`'s older spelling, kept one release. `runner-up`/`runner-remove` act on a runner (this
122
+ // sandbox's own container); gated by the `sandboxes` switch, not removal, as a runner holds no workspace to lose.
123
123
  export const DeviceSandboxOpSchema = z.enum([
124
124
  "start",
125
125
  "stop",
126
126
  "restart",
127
+ "sleep",
127
128
  "prepare",
128
129
  // `prepare` under the unattended rules (`ic sandbox prepare --auto`): nobody pressed anything, so low disk is "not
129
130
  // now" and a pinned sandbox or the version it went back from is left alone. What the update card sends by itself
@@ -203,7 +204,7 @@ export const icShapeArgs = (slug: string, fields: SandboxShapeFields, when: Sand
203
204
  ];
204
205
  export const icForgetShapeArgs = (slug: string): string[] => ["sandbox", "shape", slug, "--forget"];
205
206
  // Power goes through ic too, so a Start or Restart from any door applies the shape saved for the next restart.
206
- export const icPowerArgs = (op: "start" | "stop" | "restart", slug: string): string[] => ["sandbox", op, slug];
207
+ export const icPowerArgs = (op: "start" | "stop" | "restart" | "sleep", slug: string): string[] => ["sandbox", op, slug];
207
208
  // Adds the device id to the flow input; the daemon looks up machines by id. `resumeTurns` is the owner saying, as they
208
209
  // restart the sandbox relaying this, that the turns it cuts should run again once it is back; the daemon keeps it for
209
210
  // its next boot and never forwards it to the machine. A sandbox older than it ignores it, and the cut turns wait.
@@ -230,92 +231,7 @@ export type DeviceAgentFlow = z.infer<typeof DeviceAgentFlowSchema>;
230
231
  // Adds the device id to the flow input.
231
232
  export const DeviceAgentFlowInputSchema = DeviceAgentFlowSchema.extend({ id: z.string().min(1) });
232
233
  export type DeviceAgentFlowInput = z.infer<typeof DeviceAgentFlowInputSchema>;
233
- // Closed set of command names; the daemon builds argv from the name alone (hosts/device-commands.ts). Never a free-text
234
- // command: the same socket carries `run_command`, which would grant a shell on the user's machine.
235
- // `sync-unpair` runs the machine's own `sync uninstall --sandbox` (terminates both Mutagen sessions, drops the pairing,
236
- // self-revokes enrollment); revoking from the sandbox side is a different route.
237
- // `dev-restart`, `dev-rebuild` and `sync-install` are the three whose argv the daemon fills from what only it knows — the
238
- // dev checkout on that machine, a freshly minted pairing token — which is also why none takes a path or a token from the
239
- // caller. The two dev commands are also why a machine-side op is the wrong shape for them: this door carries a COMMAND
240
- // STRING to a tool every released agent already has, so a sandbox can drive a machine whose agent predates the feature.
241
- // `dev-rebuild-log` is the read side of `dev-rebuild`: the build is detached out there, so its log is the only place its
242
- // progress exists, and polling this is what turns a fired-and-forgotten command into something with a progress bar.
243
- // `sync-clean` removes the build output left in directories the sandbox has deleted, which is what stops those
244
- // deletions from ever landing. It deletes only content the session already ignores, so it is the one command here whose
245
- // worst outcome is a rebuild — which is why it is a button and not a turn.
246
- // `mirror-ignore`/`mirror-unignore` are `mirror-off`'s per-port form, and the reason they exist: a port number that is
247
- // permanently taken on one machine's localhost has no answer in an all-or-nothing switch, so the choice lives where the
248
- // conflict does — one port, one device, durable — instead of muting every port the pairing serves.
249
- export const DeviceCommandSchema = z.enum([
250
- "mirror-off",
251
- "mirror-on",
252
- "mirror-ignore",
253
- "mirror-unignore",
254
- "sync-pause",
255
- "sync-resume",
256
- "sync-unpair",
257
- "sync-clean",
258
- "dev-restart",
259
- "dev-rebuild",
260
- "dev-rebuild-log",
261
- "sync-install",
262
- ]);
263
- export type DeviceCommand = z.infer<typeof DeviceCommandSchema>;
264
- // The reversible sync switches: the subset a device's own row drives with a pair of buttons, as opposed to the two
265
- // commands a card elsewhere issues once. Its own type so those button tables stay total without carrying entries for
266
- // commands they can never send.
267
- export const DeviceSyncSwitchSchema = DeviceCommandSchema.exclude(["dev-restart", "dev-rebuild", "dev-rebuild-log", "sync-install", "sync-clean"]);
268
- export type DeviceSyncSwitch = z.infer<typeof DeviceSyncSwitchSchema>;
269
-
270
- // Where a detached rebuild reports itself, and the reader of that report (dev-rebuild-log.ts).
271
- export { DEV_REBUILD_EXIT_MARK, DEV_REBUILD_QUIET_MARK, type DevRebuildLog, devRebuildLogPath, readDevRebuildLog } from "./dev-rebuild-log.js";
272
-
273
- // Machine's sandbox id (absent = every paired sandbox); must start alphanumeric, else it parses as a CLI flag.
274
- export const DeviceSandboxIdSchema = z
275
- .string()
276
- .max(200)
277
- .regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/);
278
- // A folder on the DEVICE for `sync-install` to keep in step. The only caller-supplied string that reaches a command
279
- // line, so its shape is the guard: `~`, an absolute POSIX path or a drive letter, and none of the characters that would
280
- // end the argument it sits in. `~` is expanded by the daemon, which is why `$` is not allowed to arrive here. The line
281
- // may be PowerShell, which reads the typographic quotes “ ” „ as double quotes and ‘ ’ ‚ ‛ as single quotes beside the
282
- // ASCII ones, so those are refused too: the daemon quotes the folder, and this keeps a second guard where one quoter
283
- // misses a character.
284
- export const DeviceLocalDirSchema = z
285
- .string()
286
- .min(1)
287
- .max(4096)
288
- // The curly quotes as escapes: bun's transpiler rewrites a literal one in a regex to its escape and tsgo keeps it,
289
- // so spelled literally the pattern reads differently from source and from dist, and contract.lock.json can match
290
- // only one of them.
291
- .regex(/^(?:~|\/|[A-Za-z]:[\\/])[^"'`$;|&\n\r\u201C\u201D\u201E\u2018\u2019\u201A\u201B]*$/);
292
- // A TCP port number, the one value a caller supplies that reaches a command line as a number rather than a string.
293
- export const PortNumberSchema = z.number().int().min(1).max(65535);
294
- export const DeviceCommandInputSchema = z.object({
295
- id: z.string().min(1),
296
- command: DeviceCommandSchema,
297
- sandboxId: DeviceSandboxIdSchema.optional(),
298
- // `sync-install` only: which half to enroll (a member's "sync" still comes back mirror, decided daemon-side) and,
299
- // for file sync, the folder on that device. The pairing token is minted by the daemon; no caller ever carries one.
300
- mode: z.enum(["sync", "mirror"]).optional(),
301
- localDir: DeviceLocalDirSchema.optional(),
302
- // The two per-port mirror switches only. A number rather than a string, so nothing a caller sends can widen the
303
- // command line it lands in.
304
- port: PortNumberSchema.optional(),
305
- });
306
- export type DeviceCommandInput = z.infer<typeof DeviceCommandInputSchema>;
307
- // `ok` is the command's own exit status, not this route's: a refusal or non-zero exit is a real answer, not a thrown
308
- // error. Only an unreachable machine throws. `output` is the command's own printed text.
309
- export const DeviceCommandResultSchema = z.object({
310
- ok: z.boolean(),
311
- message: z.string(),
312
- output: z.string().optional(),
313
- // WHICH KIND OF `ok: false` THIS IS, because a caller that repeats itself needs to tell them apart: the device
314
- // turning the command away (a switch of its own is off, the path is out of its reach) will turn it away again,
315
- // whereas a command it accepted and then killed at its deadline says nothing except that this attempt was too slow.
316
- refused: z.boolean(),
317
- });
318
- export type DeviceCommandResult = z.infer<typeof DeviceCommandResultSchema>;
234
+ export * from "./device-commands.js";
319
235
 
320
236
  // What a synced folder reports beyond Mutagen's word, its conflicts and what the device holds back (device-folder.ts).
321
237
  export * from "./device-folder.js";
@@ -323,8 +239,7 @@ export * from "./device-folder.js";
323
239
  // One paired sandbox as the local agent holds it; `localDir` is which folder on that device holds this sandbox's /work.
324
240
  export const DevicePairingSchema = z.object({
325
241
  sandboxId: z.string(),
326
- // The account's name for the sandbox, stamped by the editor that reads the report (withSandboxNames), as a
327
- // container's `name` is. A machine never sends one: all it knows is the folder, named once at setup.
242
+ // The account's name for the sandbox, stamped by the editor that reads the report (withSandboxNames); never a machine's.
328
243
  name: z.string().optional(),
329
244
  mode: z.enum(["sync", "mirror"]),
330
245
  // Set only for mode "sync", and only for the sandbox being reported to.
@@ -332,12 +247,13 @@ export const DevicePairingSchema = z.object({
332
247
  // Which sandbox folder `localDir` holds when it is not /work itself: a project pairing's `/work/<name>`, the
333
248
  // owner's own folder, which carries no state backup and no git bridge. Absent means /work.
334
249
  remoteDir: z.string().optional(),
335
- // The folderless pairing that holds this machine's sync token for its own local sandbox, which project folders
336
- // attach to (`intentic-machine sync attach`). Absent on every other pairing.
337
- projectsHost: z.boolean().optional(),
338
- // Whether landed work in this project folder is delivered to `localDir` by itself (`deliverProject`). Only a
339
- // folder attached to the machine's own sandbox says "auto"; absent means Bring back by hand, as before.
250
+ // Whether landed work in this project folder is delivered to `localDir` by itself (`deliverProject`). A project
251
+ // sandbox's folder says "auto"; absent means Bring back by hand, as before. (2026-10-21) The folderless
252
+ // `projectsHost` pairing of the desktop app's one sandbox per computer went with that sandbox; an older agent's report
253
+ // that still carries it parses, the key dropped.
340
254
  deliver: z.enum(["auto", "off"]).optional(),
255
+ // (2026-10-20) The slug this machine's ic knows the sandbox by: how the desktop app starts or forgets a folder's sandbox.
256
+ slug: z.string().optional(),
341
257
  // Machine-owned switch (agent's own `sync mirror off`); absent reads as "on", not merely unknown.
342
258
  mirroring: z.enum(["on", "off"]).optional(),
343
259
  // Mutagen's own status word, kept verbatim rather than reduced to a traffic light.
@@ -346,7 +262,9 @@ export const DevicePairingSchema = z.object({
346
262
  conflicts: z.number().int().nonnegative().optional(),
347
263
  // Conflicted paths, capped at `CONFLICT_PATHS_MAX`; `conflicts` above is the true, uncapped count.
348
264
  conflictedPaths: z.array(DeviceConflictSchema).optional(),
265
+ // Mutagen's word, and why the agent holds it still, owner first (DeviceSyncHoldSchema); agreeing by its next pass.
349
266
  paused: z.boolean().optional(),
267
+ heldBy: z.array(z.string()).optional(),
350
268
  // Second session's status (the state-dir backup mirror), reported separately: the two fail independently.
351
269
  backupStatus: z.string().optional(),
352
270
  // (2026-10-18) While the drive holding `localDir` is nearly full, the reading that paused its sync and git bridge; and the
@@ -488,12 +406,11 @@ export const SshHostKeySchema = z
488
406
 
489
407
  // POST /system/authorized-key: a machine's agent enrolling its SSH key for desktop sync, redeeming a pairing token.
490
408
  // Every field past `key` is optional: an older agent sends none of them and is still enrolled.
491
- export const SyncEnrollmentRequestSchema = z.object({
409
+ // The key, and the computer and environment enrolling it (the same identity a device redeem sends), so the
410
+ // enrollment joins that machine's card.
411
+ export const SyncEnrollmentRequestSchema = MachineIdentitySchema.extend({
492
412
  // One authorized_keys line; its comment is the name the enrollment goes by (`DeviceSync.machine`).
493
413
  key: z.string(),
494
- // The computer and the environment on it that are enrolling, so the enrollment joins that machine's card.
495
- machineId: MachineIdSchema.optional(),
496
- environment: z.string().min(1).optional(),
497
414
  });
498
415
  export type SyncEnrollmentRequest = z.infer<typeof SyncEnrollmentRequestSchema>;
499
416
 
@@ -546,6 +463,10 @@ export const DeviceSchema = z.object({
546
463
  // (a switch that is off, an `ic` too old to list). Absent when it listed, or when nothing answered at all. What a
547
464
  // card falling back to a command says instead of "connect the computer": that computer IS connected (2026-10-07).
548
465
  sandboxesUnread: z.string().optional(),
466
+ // Whether its card's "Manage sandboxes on this device" switch is on (HostSummary.manages). Not the same fact as
467
+ // `sandboxes`: "Run commands" alone lists the containers, and only this switch lets one be made, started or removed,
468
+ // so a screen offering such a press asks here first. Absent on a sync-only row and from a daemon older than it.
469
+ manages: z.boolean().optional(),
549
470
  gap: DeviceGapSchema.optional(),
550
471
  });
551
472
  export type Device = z.infer<typeof DeviceSchema>;
@@ -659,14 +580,11 @@ const doorsRunningSandbox = (devices: readonly Device[], slug: string | undefine
659
580
  ? []
660
581
  : devices.filter((device) => device.hostId !== undefined && device.online === true && (device.sandboxes ?? []).some((box) => box.slug === slug));
661
582
 
662
- // The connected, online device whose docker reports a given sandbox slug: the machine that sandbox RUNS ON, as opposed
663
- // to any machine merely paired with it. Answers the question every "run it there instead of asking the owner to type
664
- // it" path starts from, and is `undefined` for a sync-only agent, which reports no containers at all.
665
- // Reads the row's own container list, not the report's: a card that grants sandbox management without "Run commands"
666
- // can run every swap this answer leads to while refusing to describe itself, and judging it on the report would print
667
- // a terminal command for a machine one click could have done it on.
668
- // Answers with a door, and a container verb may take any door of the machine; a command written for a PATH may not
669
- // (`hostHoldingPath`).
583
+ // The connected, online device whose docker reports a given sandbox slug: the machine that sandbox RUNS ON, not any
584
+ // machine merely paired with it, and `undefined` for a sync-only agent, which reports no containers. Read off the row's
585
+ // own container list, not the report's: a card granting sandbox management without "Run commands" refuses to describe
586
+ // itself yet runs every swap this answer leads to, so judging it on the report printed a command one click could do.
587
+ // Answers with a door, any of which a container verb may take; a command written for a PATH may not (`hostHoldingPath`).
670
588
  export const hostRunningSandbox = (devices: readonly Device[], slug: string | undefined): string | undefined =>
671
589
  doorsRunningSandbox(devices, slug)[0]?.hostId;
672
590
 
@@ -1,9 +1,8 @@
1
1
  // environment: the overlay Dockerfile extending the sandbox image
2
2
  import { z } from "zod";
3
- // The approved file is daemon-composed (pinned FROM + capability fragments + the owner-approved custom section). Status
4
- // is derived, never stored:
5
- // applied: sha256(approved) === appliedHash.
6
- // pending rebuild: approved present but hashes differ.
3
+ // The approved overlay is daemon-composed (pinned FROM + capability fragments + the owner-approved custom section) from its
4
+ // inputs on every read. Status is derived, never stored:
5
+ // pending rebuild: rebuildPending(approved.hash, appliedHash) (state/sandbox-build.ts); applied otherwise.
7
6
  // proposed: proposal present with a hash different from custom's.
8
7
 
9
8
  const environmentFileSchema = z.object({ content: z.string(), hash: z.string() });
@@ -45,10 +44,10 @@ export type RuntimeInstall = z.infer<typeof RuntimeInstallSchema>;
45
44
  // (`environment propose`) clears its tool's entries.
46
45
  export const SettledDraftSchema = z.object({ tool: z.string(), hash: z.string(), at: z.number() });
47
46
  export type SettledDraft = z.infer<typeof SettledDraftSchema>;
48
- // One overlay the daemon composed, by the hash a rebuild is stamped with, and the custom-section blocks it carried (each
49
- // by name and the hash of its trimmed steps). Kept so "is this tool in the image" is answered against the overlay the
50
- // running container was actually built from, which is rarely the newest one composed: every approval, capability change
51
- // and daemon update composes again, and a rebuild builds whatever was newest when it started.
47
+ // One overlay the daemon composed, by the hash a rebuild is stamped with, and the sections it carried, each by the hash
48
+ // of its trimmed steps: capability fragments unnamed, custom-section blocks by name. Kept so "is this in the image" is
49
+ // answered against the overlay the running container was actually built from, which is rarely the newest one composed:
50
+ // every approval, capability change and daemon update composes again, and a rebuild builds whatever was newest then.
52
51
  export const EnvironmentCompositionSchema = z.object({
53
52
  hash: z.string(),
54
53
  at: z.number(),
@@ -196,7 +195,7 @@ export type EnvironmentRemove = z.infer<typeof EnvironmentRemoveSchema>;
196
195
  export const EnvironmentContentsSchema = z.object({ items: z.array(EnvironmentItemSchema) });
197
196
  export type EnvironmentContents = z.infer<typeof EnvironmentContentsSchema>;
198
197
  // A sandbox is four stores: /work, /history, the container, and the AI-provider credential root. A bundle carries only
199
- // the first two (WORKSPACE_STATE_FILES/HISTORY_STATE_FILES); it cannot rebuild the container or credentials, so
198
+ // the first two (WORKSPACE_STATE_FILES here, the daemon's HISTORY_STATE_FILES); it cannot rebuild the container or credentials, so
200
199
  // importing one is a report, not a claim of equivalence. Only the outbound half is here; inbound is one of arrival.ts's
201
200
  // four sources.
202
201