@intentic/sandbox-contract 1.311.0 → 1.313.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 (444) hide show
  1. package/README.md +32 -4
  2. package/dist/chores/chores.js +6 -6
  3. package/dist/chores/chores.js.map +1 -1
  4. package/dist/chores/index.d.ts +0 -1
  5. package/dist/chores/index.d.ts.map +1 -1
  6. package/dist/chores/index.js +0 -1
  7. package/dist/chores/index.js.map +1 -1
  8. package/dist/chores/prompt.d.ts.map +1 -1
  9. package/dist/chores/prompt.js +3 -2
  10. package/dist/chores/prompt.js.map +1 -1
  11. package/dist/contracts/accounts.contract.d.ts +69 -0
  12. package/dist/contracts/accounts.contract.d.ts.map +1 -1
  13. package/dist/contracts/agent.contract.d.ts +436 -0
  14. package/dist/contracts/agent.contract.d.ts.map +1 -1
  15. package/dist/contracts/agent.contract.js +11 -1
  16. package/dist/contracts/agent.contract.js.map +1 -1
  17. package/dist/contracts/agents.contract.d.ts +718 -90
  18. package/dist/contracts/agents.contract.d.ts.map +1 -1
  19. package/dist/contracts/agents.contract.js +30 -2
  20. package/dist/contracts/agents.contract.js.map +1 -1
  21. package/dist/contracts/capabilities.contract.js +1 -1
  22. package/dist/contracts/capabilities.contract.js.map +1 -1
  23. package/dist/contracts/device.contract.d.ts +20 -4
  24. package/dist/contracts/device.contract.d.ts.map +1 -1
  25. package/dist/contracts/device.contract.js +1 -1
  26. package/dist/contracts/device.contract.js.map +1 -1
  27. package/dist/contracts/extensions.contract.d.ts +13 -0
  28. package/dist/contracts/extensions.contract.d.ts.map +1 -1
  29. package/dist/contracts/git.contract.d.ts +2 -0
  30. package/dist/contracts/git.contract.d.ts.map +1 -1
  31. package/dist/contracts/offload.contract.d.ts +107 -0
  32. package/dist/contracts/offload.contract.d.ts.map +1 -0
  33. package/dist/contracts/offload.contract.js +53 -0
  34. package/dist/contracts/offload.contract.js.map +1 -0
  35. package/dist/contracts/personas.contract.d.ts +2 -0
  36. package/dist/contracts/personas.contract.d.ts.map +1 -1
  37. package/dist/contracts/ports.contract.d.ts +5 -0
  38. package/dist/contracts/ports.contract.d.ts.map +1 -1
  39. package/dist/contracts/runner.contract.d.ts +204 -0
  40. package/dist/contracts/runner.contract.d.ts.map +1 -1
  41. package/dist/contracts/runner.contract.js +3 -1
  42. package/dist/contracts/runner.contract.js.map +1 -1
  43. package/dist/contracts/secrets.contract.d.ts.map +1 -1
  44. package/dist/contracts/secrets.contract.js +1 -0
  45. package/dist/contracts/secrets.contract.js.map +1 -1
  46. package/dist/contracts/sessions.contract.d.ts +59 -0
  47. package/dist/contracts/sessions.contract.d.ts.map +1 -1
  48. package/dist/contracts/settings.contract.d.ts +20 -8
  49. package/dist/contracts/settings.contract.d.ts.map +1 -1
  50. package/dist/contracts/settings.contract.js +2 -2
  51. package/dist/contracts/settings.contract.js.map +1 -1
  52. package/dist/contracts/system.contract.d.ts +230 -15
  53. package/dist/contracts/system.contract.d.ts.map +1 -1
  54. package/dist/contracts/translator.contract.d.ts +88 -0
  55. package/dist/contracts/translator.contract.d.ts.map +1 -1
  56. package/dist/contracts/workspace.contract.d.ts +217 -0
  57. package/dist/contracts/workspace.contract.d.ts.map +1 -1
  58. package/dist/contracts/workspace.contract.js +40 -3
  59. package/dist/contracts/workspace.contract.js.map +1 -1
  60. package/dist/documents/conversions.d.ts +81 -0
  61. package/dist/documents/conversions.d.ts.map +1 -0
  62. package/dist/documents/conversions.js +213 -0
  63. package/dist/documents/conversions.js.map +1 -0
  64. package/dist/documents/index.d.ts +4 -0
  65. package/dist/documents/index.d.ts.map +1 -0
  66. package/dist/documents/index.js +4 -0
  67. package/dist/documents/index.js.map +1 -0
  68. package/dist/documents/passthrough.d.ts +13 -0
  69. package/dist/documents/passthrough.d.ts.map +1 -0
  70. package/dist/documents/passthrough.js +101 -0
  71. package/dist/documents/passthrough.js.map +1 -0
  72. package/dist/documents/read.d.ts +24 -0
  73. package/dist/documents/read.d.ts.map +1 -0
  74. package/dist/documents/read.js +81 -0
  75. package/dist/documents/read.js.map +1 -0
  76. package/dist/events/agent-events.d.ts +235 -0
  77. package/dist/events/agent-events.d.ts.map +1 -1
  78. package/dist/events/agent-events.js +3 -0
  79. package/dist/events/agent-events.js.map +1 -1
  80. package/dist/events/child-run.d.ts +9 -0
  81. package/dist/events/child-run.d.ts.map +1 -0
  82. package/dist/events/child-run.js +41 -0
  83. package/dist/events/child-run.js.map +1 -0
  84. package/dist/events/errands.d.ts +5 -0
  85. package/dist/events/errands.d.ts.map +1 -0
  86. package/dist/events/errands.js +19 -0
  87. package/dist/events/errands.js.map +1 -0
  88. package/dist/events/land-breakage.d.ts +3 -0
  89. package/dist/events/land-breakage.d.ts.map +1 -1
  90. package/dist/events/land-breakage.js +3 -0
  91. package/dist/events/land-breakage.js.map +1 -1
  92. package/dist/events/land-conflict.d.ts +3 -0
  93. package/dist/events/land-conflict.d.ts.map +1 -0
  94. package/dist/events/land-conflict.js +4 -0
  95. package/dist/events/land-conflict.js.map +1 -0
  96. package/dist/events/requests.d.ts +152 -0
  97. package/dist/events/requests.d.ts.map +1 -1
  98. package/dist/events/requests.js +26 -1
  99. package/dist/events/requests.js.map +1 -1
  100. package/dist/events/system-events.d.ts +74 -12
  101. package/dist/events/system-events.d.ts.map +1 -1
  102. package/dist/events/system-events.js +3 -1
  103. package/dist/events/system-events.js.map +1 -1
  104. package/dist/events/transcript.d.ts +387 -0
  105. package/dist/events/transcript.d.ts.map +1 -1
  106. package/dist/events/transcript.js +3 -0
  107. package/dist/events/transcript.js.map +1 -1
  108. package/dist/events/verify-nudge.d.ts.map +1 -1
  109. package/dist/events/verify-nudge.js.map +1 -1
  110. package/dist/front/browser-wire.d.ts +6 -0
  111. package/dist/front/browser-wire.d.ts.map +1 -0
  112. package/dist/front/browser-wire.js +4 -0
  113. package/dist/front/browser-wire.js.map +1 -0
  114. package/dist/front/front-wire.d.ts +6 -0
  115. package/dist/front/front-wire.d.ts.map +1 -0
  116. package/dist/front/front-wire.js +5 -0
  117. package/dist/front/front-wire.js.map +1 -0
  118. package/dist/front/generated/browser-wire.d.ts +19 -0
  119. package/dist/front/generated/browser-wire.d.ts.map +1 -0
  120. package/dist/front/generated/browser-wire.js +2 -0
  121. package/dist/front/generated/browser-wire.js.map +1 -0
  122. package/dist/front/generated/wire.d.ts +70 -1
  123. package/dist/front/generated/wire.d.ts.map +1 -1
  124. package/dist/ids/conversation-ids.d.ts +3 -0
  125. package/dist/ids/conversation-ids.d.ts.map +1 -1
  126. package/dist/ids/conversation-ids.js +4 -2
  127. package/dist/ids/conversation-ids.js.map +1 -1
  128. package/dist/ids/session-names.d.ts +3 -0
  129. package/dist/ids/session-names.d.ts.map +1 -1
  130. package/dist/ids/session-names.js +3 -0
  131. package/dist/ids/session-names.js.map +1 -1
  132. package/dist/index.d.ts +2052 -190
  133. package/dist/index.d.ts.map +1 -1
  134. package/dist/index.js +15 -1
  135. package/dist/index.js.map +1 -1
  136. package/dist/models/agent-catalog.d.ts.map +1 -1
  137. package/dist/models/agent-catalog.js +4 -6
  138. package/dist/models/agent-catalog.js.map +1 -1
  139. package/dist/models/agent-runtimes.d.ts +2 -1
  140. package/dist/models/agent-runtimes.d.ts.map +1 -1
  141. package/dist/models/agent-runtimes.js +7 -1
  142. package/dist/models/agent-runtimes.js.map +1 -1
  143. package/dist/models/plan-pools.d.ts +22 -1
  144. package/dist/models/plan-pools.d.ts.map +1 -1
  145. package/dist/models/plan-pools.js +86 -0
  146. package/dist/models/plan-pools.js.map +1 -1
  147. package/dist/policy/request-status.d.ts +1 -1
  148. package/dist/policy/request-status.d.ts.map +1 -1
  149. package/dist/policy/request-status.js +4 -11
  150. package/dist/policy/request-status.js.map +1 -1
  151. package/dist/policy/reserved-servers.d.ts.map +1 -1
  152. package/dist/policy/reserved-servers.js +2 -1
  153. package/dist/policy/reserved-servers.js.map +1 -1
  154. package/dist/policy/turned-away.d.ts.map +1 -1
  155. package/dist/policy/turned-away.js +1 -0
  156. package/dist/policy/turned-away.js.map +1 -1
  157. package/dist/protocol/container-requirements.d.ts +0 -1
  158. package/dist/protocol/container-requirements.d.ts.map +1 -1
  159. package/dist/protocol/container-requirements.js +1 -2
  160. package/dist/protocol/container-requirements.js.map +1 -1
  161. package/dist/protocol/edge-verdict.d.ts +2 -1
  162. package/dist/protocol/edge-verdict.d.ts.map +1 -1
  163. package/dist/protocol/edge-verdict.js +1 -1
  164. package/dist/protocol/edge-verdict.js.map +1 -1
  165. package/dist/protocol/ingress-contract.d.ts +1 -12
  166. package/dist/protocol/ingress-contract.d.ts.map +1 -1
  167. package/dist/protocol/ingress-contract.js +1 -1
  168. package/dist/protocol/ingress-contract.js.map +1 -1
  169. package/dist/protocol/raw-routes.d.ts +36 -26
  170. package/dist/protocol/raw-routes.d.ts.map +1 -1
  171. package/dist/protocol/raw-routes.js +17 -23
  172. package/dist/protocol/raw-routes.js.map +1 -1
  173. package/dist/protocol/route-meta.d.ts +22 -0
  174. package/dist/protocol/route-meta.d.ts.map +1 -1
  175. package/dist/protocol/route-meta.js +16 -0
  176. package/dist/protocol/route-meta.js.map +1 -1
  177. package/dist/protocol/runner-protocol.d.ts +34 -0
  178. package/dist/protocol/runner-protocol.d.ts.map +1 -1
  179. package/dist/protocol/runner-protocol.js +33 -0
  180. package/dist/protocol/runner-protocol.js.map +1 -1
  181. package/dist/protocol/tunnel-bulk.d.ts +2 -0
  182. package/dist/protocol/tunnel-bulk.d.ts.map +1 -0
  183. package/dist/protocol/tunnel-bulk.js +3 -0
  184. package/dist/protocol/tunnel-bulk.js.map +1 -0
  185. package/dist/protocol/x402.d.ts +17 -0
  186. package/dist/protocol/x402.d.ts.map +1 -0
  187. package/dist/protocol/x402.js +37 -0
  188. package/dist/protocol/x402.js.map +1 -0
  189. package/dist/schemas/activity.d.ts.map +1 -1
  190. package/dist/schemas/activity.js.map +1 -1
  191. package/dist/schemas/agent.d.ts +25 -0
  192. package/dist/schemas/agent.d.ts.map +1 -1
  193. package/dist/schemas/agent.js +14 -2
  194. package/dist/schemas/agent.js.map +1 -1
  195. package/dist/schemas/agents.d.ts +90 -18
  196. package/dist/schemas/agents.d.ts.map +1 -1
  197. package/dist/schemas/agents.js +49 -5
  198. package/dist/schemas/agents.js.map +1 -1
  199. package/dist/schemas/automations.d.ts +23 -6
  200. package/dist/schemas/automations.d.ts.map +1 -1
  201. package/dist/schemas/automations.js.map +1 -1
  202. package/dist/schemas/devices.d.ts +134 -7
  203. package/dist/schemas/devices.d.ts.map +1 -1
  204. package/dist/schemas/devices.js +43 -18
  205. package/dist/schemas/devices.js.map +1 -1
  206. package/dist/schemas/extension-updates.d.ts +26 -0
  207. package/dist/schemas/extension-updates.d.ts.map +1 -1
  208. package/dist/schemas/extension-updates.js +4 -0
  209. package/dist/schemas/extension-updates.js.map +1 -1
  210. package/dist/schemas/git/git.d.ts +19 -0
  211. package/dist/schemas/git/git.d.ts.map +1 -1
  212. package/dist/schemas/git/git.js +1 -0
  213. package/dist/schemas/git/git.js.map +1 -1
  214. package/dist/schemas/hosts.d.ts +35 -3
  215. package/dist/schemas/hosts.d.ts.map +1 -1
  216. package/dist/schemas/hosts.js +22 -6
  217. package/dist/schemas/hosts.js.map +1 -1
  218. package/dist/schemas/keep-warm.d.ts +2 -12
  219. package/dist/schemas/keep-warm.d.ts.map +1 -1
  220. package/dist/schemas/keep-warm.js +8 -10
  221. package/dist/schemas/keep-warm.js.map +1 -1
  222. package/dist/schemas/metrics.d.ts +29 -0
  223. package/dist/schemas/metrics.d.ts.map +1 -1
  224. package/dist/schemas/metrics.js +19 -3
  225. package/dist/schemas/metrics.js.map +1 -1
  226. package/dist/schemas/offload.d.ts +62 -0
  227. package/dist/schemas/offload.d.ts.map +1 -0
  228. package/dist/schemas/offload.js +23 -0
  229. package/dist/schemas/offload.js.map +1 -0
  230. package/dist/schemas/personas.d.ts +3 -0
  231. package/dist/schemas/personas.d.ts.map +1 -1
  232. package/dist/schemas/personas.js +12 -3
  233. package/dist/schemas/personas.js.map +1 -1
  234. package/dist/schemas/ports.d.ts +10 -0
  235. package/dist/schemas/ports.d.ts.map +1 -1
  236. package/dist/schemas/ports.js +8 -0
  237. package/dist/schemas/ports.js.map +1 -1
  238. package/dist/schemas/providers/plan-limits.d.ts +169 -0
  239. package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
  240. package/dist/schemas/providers/plan-limits.js +54 -1
  241. package/dist/schemas/providers/plan-limits.js.map +1 -1
  242. package/dist/schemas/providers/provider-oauth.d.ts +69 -0
  243. package/dist/schemas/providers/provider-oauth.d.ts.map +1 -1
  244. package/dist/schemas/providers/provider-oauth.js +6 -1
  245. package/dist/schemas/providers/provider-oauth.js.map +1 -1
  246. package/dist/schemas/settings-history.d.ts +55 -0
  247. package/dist/schemas/settings-history.d.ts.map +1 -0
  248. package/dist/schemas/settings-history.js +73 -0
  249. package/dist/schemas/settings-history.js.map +1 -0
  250. package/dist/schemas/settings.d.ts +188 -4
  251. package/dist/schemas/settings.d.ts.map +1 -1
  252. package/dist/schemas/settings.js +41 -26
  253. package/dist/schemas/settings.js.map +1 -1
  254. package/dist/schemas/speaker.d.ts +36 -0
  255. package/dist/schemas/speaker.d.ts.map +1 -0
  256. package/dist/schemas/speaker.js +62 -0
  257. package/dist/schemas/speaker.js.map +1 -0
  258. package/dist/schemas/state-plan.d.ts +46 -0
  259. package/dist/schemas/state-plan.d.ts.map +1 -0
  260. package/dist/schemas/state-plan.js +31 -0
  261. package/dist/schemas/state-plan.js.map +1 -0
  262. package/dist/schemas/system.d.ts +64 -0
  263. package/dist/schemas/system.d.ts.map +1 -1
  264. package/dist/schemas/system.js +12 -1
  265. package/dist/schemas/system.js.map +1 -1
  266. package/dist/schemas/workspace/mainline.d.ts +513 -0
  267. package/dist/schemas/workspace/mainline.d.ts.map +1 -0
  268. package/dist/schemas/workspace/mainline.js +182 -0
  269. package/dist/schemas/workspace/mainline.js.map +1 -0
  270. package/dist/schemas/workspace/workspace-tree.d.ts +23 -0
  271. package/dist/schemas/workspace/workspace-tree.d.ts.map +1 -1
  272. package/dist/schemas/workspace/workspace-tree.js +34 -0
  273. package/dist/schemas/workspace/workspace-tree.js.map +1 -1
  274. package/dist/state/contract-lock.d.ts +1 -0
  275. package/dist/state/contract-lock.d.ts.map +1 -1
  276. package/dist/state/contract-lock.js +47 -1
  277. package/dist/state/contract-lock.js.map +1 -1
  278. package/dist/state/definition.d.ts +44 -20
  279. package/dist/state/definition.d.ts.map +1 -1
  280. package/dist/state/definition.js.map +1 -1
  281. package/dist/state/fix-stance.d.ts +1 -1
  282. package/dist/state/fix-stance.d.ts.map +1 -1
  283. package/dist/state/fix-stance.js +2 -1
  284. package/dist/state/fix-stance.js.map +1 -1
  285. package/dist/state/history-state.d.ts.map +1 -1
  286. package/dist/state/history-state.js +7 -0
  287. package/dist/state/history-state.js.map +1 -1
  288. package/dist/state/landed-commit.d.ts +1 -0
  289. package/dist/state/landed-commit.d.ts.map +1 -1
  290. package/dist/state/landed-commit.js +2 -0
  291. package/dist/state/landed-commit.js.map +1 -1
  292. package/dist/state/red-streak.d.ts +7 -0
  293. package/dist/state/red-streak.d.ts.map +1 -0
  294. package/dist/state/red-streak.js +11 -0
  295. package/dist/state/red-streak.js.map +1 -0
  296. package/dist/state/runtime-state.d.ts +3 -0
  297. package/dist/state/runtime-state.d.ts.map +1 -1
  298. package/dist/state/runtime-state.js +1 -0
  299. package/dist/state/runtime-state.js.map +1 -1
  300. package/dist/state/workspace-state.d.ts +23 -1
  301. package/dist/state/workspace-state.d.ts.map +1 -1
  302. package/dist/state/workspace-state.js +33 -3
  303. package/dist/state/workspace-state.js.map +1 -1
  304. package/dist/text/shell-regions.d.ts +1 -0
  305. package/dist/text/shell-regions.d.ts.map +1 -1
  306. package/dist/text/shell-regions.js +1 -1
  307. package/dist/text/shell-regions.js.map +1 -1
  308. package/dist/text/transcript-fold.d.ts +1 -0
  309. package/dist/text/transcript-fold.d.ts.map +1 -1
  310. package/dist/text/transcript-fold.js +24 -8
  311. package/dist/text/transcript-fold.js.map +1 -1
  312. package/package.json +42 -42
  313. package/src/chores/chores.ts +6 -6
  314. package/src/chores/index.ts +0 -1
  315. package/src/chores/prompt.ts +3 -2
  316. package/src/contracts/agent.contract.ts +14 -0
  317. package/src/contracts/agents.contract.ts +36 -1
  318. package/src/contracts/capabilities.contract.ts +2 -2
  319. package/src/contracts/device.contract.ts +5 -3
  320. package/src/contracts/offload.contract.ts +60 -0
  321. package/src/contracts/runner.contract.ts +7 -1
  322. package/src/contracts/secrets.contract.ts +2 -0
  323. package/src/contracts/settings.contract.ts +2 -1
  324. package/src/contracts/workspace.contract.ts +54 -2
  325. package/src/documents/conversions.ts +402 -0
  326. package/src/documents/index.ts +7 -0
  327. package/src/documents/passthrough.ts +139 -0
  328. package/src/documents/read.ts +114 -0
  329. package/src/events/agent-events.ts +5 -0
  330. package/src/events/child-run.test.ts +81 -0
  331. package/src/events/child-run.ts +73 -0
  332. package/src/events/errands.test.ts +22 -0
  333. package/src/events/errands.ts +27 -0
  334. package/src/events/land-breakage.ts +12 -0
  335. package/src/events/land-conflict.ts +12 -0
  336. package/src/events/requests.ts +40 -1
  337. package/src/events/system-events.ts +9 -3
  338. package/src/events/transcript.ts +8 -0
  339. package/src/events/verify-nudge.ts +4 -3
  340. package/src/front/browser-wire.ts +20 -0
  341. package/src/front/front-wire.ts +15 -0
  342. package/src/front/generated/browser-wire.json +138 -0
  343. package/src/front/generated/browser-wire.ts +16 -0
  344. package/src/front/generated/front-wire.json +793 -0
  345. package/src/front/generated/tunnel.json +38 -0
  346. package/src/front/generated/wire.ts +37 -9
  347. package/src/front/wire-manifests.test.ts +61 -0
  348. package/src/ids/conversation-ids.ts +13 -5
  349. package/src/ids/session-names.ts +7 -0
  350. package/src/index.ts +15 -2
  351. package/src/models/agent-catalog.test.ts +6 -4
  352. package/src/models/agent-catalog.ts +4 -6
  353. package/src/models/agent-runtimes.ts +22 -7
  354. package/src/models/capability-ledger.test.ts +2 -0
  355. package/src/models/plan-pools.test.ts +121 -2
  356. package/src/models/plan-pools.ts +152 -2
  357. package/src/policy/request-status.ts +4 -12
  358. package/src/policy/reserved-servers.test.ts +6 -0
  359. package/src/policy/reserved-servers.ts +13 -10
  360. package/src/policy/turned-away.ts +3 -0
  361. package/src/protocol/container-requirements.test.ts +2 -3
  362. package/src/protocol/container-requirements.ts +1 -7
  363. package/src/protocol/edge-verdict.ts +13 -11
  364. package/src/protocol/ingress-contract.fixture.json +90 -0
  365. package/src/protocol/ingress-contract.test.ts +18 -26
  366. package/src/protocol/ingress-contract.ts +10 -25
  367. package/src/protocol/raw-routes.ts +22 -26
  368. package/src/protocol/route-meta.ts +39 -0
  369. package/src/protocol/runner-protocol.ts +53 -0
  370. package/src/protocol/tunnel-bulk.test.ts +37 -0
  371. package/src/protocol/tunnel-bulk.ts +7 -0
  372. package/src/protocol/x402.test.ts +29 -0
  373. package/src/protocol/x402.ts +61 -0
  374. package/src/schemas/activity.ts +3 -1
  375. package/src/schemas/agent.ts +28 -3
  376. package/src/schemas/agents.ts +83 -8
  377. package/src/schemas/automations.ts +4 -2
  378. package/src/schemas/devices.ts +106 -39
  379. package/src/schemas/extension-updates.ts +6 -0
  380. package/src/schemas/git/git.ts +5 -0
  381. package/src/schemas/hosts.ts +64 -13
  382. package/src/schemas/ic-shape-args.test.ts +64 -0
  383. package/src/schemas/keep-warm.test.ts +6 -13
  384. package/src/schemas/keep-warm.ts +15 -20
  385. package/src/schemas/metrics.ts +28 -3
  386. package/src/schemas/offload.ts +44 -0
  387. package/src/schemas/personas.ts +18 -3
  388. package/src/schemas/ports.ts +10 -0
  389. package/src/schemas/providers/plan-limits.ts +84 -1
  390. package/src/schemas/providers/provider-oauth.ts +10 -1
  391. package/src/schemas/settings-history.ts +108 -0
  392. package/src/schemas/settings.test.ts +54 -1
  393. package/src/schemas/settings.ts +88 -49
  394. package/src/schemas/speaker.ts +89 -0
  395. package/src/schemas/state-plan.test.ts +69 -0
  396. package/src/schemas/state-plan.ts +47 -0
  397. package/src/schemas/system.ts +24 -3
  398. package/src/schemas/workspace/mainline.test.ts +28 -0
  399. package/src/schemas/workspace/mainline.ts +268 -0
  400. package/src/schemas/workspace/workspace-tree.ts +47 -0
  401. package/src/state/contract-lock.test.ts +65 -4
  402. package/src/state/contract-lock.ts +70 -2
  403. package/src/state/definition.ts +3 -2
  404. package/src/state/fix-stance.test.ts +8 -0
  405. package/src/state/fix-stance.ts +3 -3
  406. package/src/state/history-state.ts +19 -0
  407. package/src/state/landed-commit.test.ts +12 -1
  408. package/src/state/landed-commit.ts +5 -0
  409. package/src/state/red-streak.test.ts +28 -0
  410. package/src/state/red-streak.ts +28 -0
  411. package/src/state/runtime-state.ts +4 -0
  412. package/src/state/workspace-state.test.ts +7 -1
  413. package/src/state/workspace-state.ts +45 -3
  414. package/src/text/shell-regions.test.ts +38 -0
  415. package/src/text/shell-regions.ts +3 -3
  416. package/src/text/transcript-fold.test.ts +46 -0
  417. package/src/text/transcript-fold.ts +31 -9
  418. package/dist/chores/fix-deps.d.ts +0 -10
  419. package/dist/chores/fix-deps.d.ts.map +0 -1
  420. package/dist/chores/fix-deps.js +0 -18
  421. package/dist/chores/fix-deps.js.map +0 -1
  422. package/dist/protocol/ingress-protocol.d.ts +0 -30
  423. package/dist/protocol/ingress-protocol.d.ts.map +0 -1
  424. package/dist/protocol/ingress-protocol.js +0 -300
  425. package/dist/protocol/ingress-protocol.js.map +0 -1
  426. package/dist/protocol/terminal-protocol.d.ts +0 -18
  427. package/dist/protocol/terminal-protocol.d.ts.map +0 -1
  428. package/dist/protocol/terminal-protocol.js +0 -2
  429. package/dist/protocol/terminal-protocol.js.map +0 -1
  430. package/dist/protocol/tunnel-heartbeat.d.ts +0 -19
  431. package/dist/protocol/tunnel-heartbeat.d.ts.map +0 -1
  432. package/dist/protocol/tunnel-heartbeat.js +0 -41
  433. package/dist/protocol/tunnel-heartbeat.js.map +0 -1
  434. package/dist/protocol/upgrade-relay.d.ts +0 -4
  435. package/dist/protocol/upgrade-relay.d.ts.map +0 -1
  436. package/dist/protocol/upgrade-relay.js +0 -25
  437. package/dist/protocol/upgrade-relay.js.map +0 -1
  438. package/src/chores/fix-deps.ts +0 -22
  439. package/src/protocol/ingress-protocol.test.ts +0 -579
  440. package/src/protocol/ingress-protocol.ts +0 -469
  441. package/src/protocol/terminal-protocol.ts +0 -13
  442. package/src/protocol/tunnel-heartbeat.test.ts +0 -75
  443. package/src/protocol/tunnel-heartbeat.ts +0 -67
  444. package/src/protocol/upgrade-relay.ts +0 -28
@@ -56,7 +56,7 @@ export const AgentAttentionSchema = z.object({
56
56
  plan: z.boolean().describe("It has proposed a plan and is waiting for a yes."),
57
57
  question: z.boolean().describe("It has asked you something."),
58
58
  permission: z.boolean().describe("It wants to use a tool it needs permission for."),
59
- // A missing capability (capabilities/capability-offer.ts); lets the lane say "setup needed" rather than a generic
59
+ // A missing capability (capabilities/offers/capability-offer.ts); lets the lane say "setup needed" rather than a generic
60
60
  // pause.
61
61
  capability: z.boolean().describe("It needs something connected that is not connected yet."),
62
62
  // A gated credential parked on a named person's click; the one pause the board's reader may not be able to clear
@@ -83,10 +83,30 @@ export const UnfinishedWorkSchema = z.object({
83
83
  })
84
84
  .optional()
85
85
  .describe("The agent's own checklist where that turn left it. Absent for a conversation that kept no list."),
86
- // Name of the `turn.ending` check still red when the turn ended, after its two repair rounds ran out.
87
- check: z.string().optional().describe("The end-of-turn check that was still failing when the turn ended, by name."),
86
+ // Name of the `turn.ending` check still red when the turn ended. Retired with that check: nothing writes it now, and
87
+ // it stays so entries written before still read.
88
+ check: z
89
+ .string()
90
+ .optional()
91
+ .describe("The end-of-turn check that was still failing when the turn ended, by name. No longer written: checks run after work lands."),
88
92
  });
89
93
  export type UnfinishedWork = z.infer<typeof UnfinishedWorkSchema>;
94
+ // What the last turn showed of its own work, read off its tool calls and never asked of the model: the record a card
95
+ // badges instead of sending the turn back to prove anything. Checks run after the work lands (mainline.ts).
96
+ export const TurnProofSchema = z.object({
97
+ at: z.number().describe("When the turn that left this ended, in milliseconds."),
98
+ verification: z
99
+ .enum(["verified", "unproven", "failing", "no-code"])
100
+ .describe(
101
+ "Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to.",
102
+ ),
103
+ check: z.string().optional().describe("The command that spoke, when one did, so a targeted test is never read as the whole suite."),
104
+ unviewed: z
105
+ .number()
106
+ .optional()
107
+ .describe("How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none."),
108
+ });
109
+ export type TurnProof = z.infer<typeof TurnProofSchema>;
90
110
  // A landing's commit message: subject plus the two trailer sentences a changelog repo gets. One schema, not three
91
111
  // duplicated fields, since both the live roster and the review carrier must read the same shape and can't disagree.
92
112
  export const LandedMessageSchema = z.object({
@@ -116,6 +136,12 @@ export const LandedMessageSchema = z.object({
116
136
  .string()
117
137
  .optional()
118
138
  .describe("Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent."),
139
+ // Present only when the conversation declared an exception a check should grant this change (`Allow: <check> — <reason>`);
140
+ // the check after the land and the push read it off the commit.
141
+ allows: z
142
+ .array(z.string())
143
+ .optional()
144
+ .describe("Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent."),
119
145
  });
120
146
  export type LandedMessage = z.infer<typeof LandedMessageSchema>;
121
147
  // One model's turn in the drafting walk (agent/role-model.ts tries connected models in order), in spend order:
@@ -238,6 +264,12 @@ export const AgentSummarySchema = z.object({
238
264
  // Both latched at the first turn, so a board scoped to one project can say which conversations are its own.
239
265
  startIn: z.string().optional().describe("Which folder it opened in, relative to the workspace root. Absent means the root."),
240
266
  actsAs: z.string().optional().describe("Which persona its first turn acted as. Absent for an ordinary chat."),
267
+ lastActsAs: z
268
+ .string()
269
+ .optional()
270
+ .describe(
271
+ "Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.",
272
+ ),
241
273
  // Per-conversation, so opening it restores its own choices rather than another tab's; `fast` here is what was asked
242
274
  // for, not what was served.
243
275
  model: z
@@ -336,10 +368,12 @@ export const AgentSummarySchema = z.object({
336
368
  .describe(
337
369
  "When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.",
338
370
  ),
339
- keepable: z
340
- .boolean()
371
+ keepableUntil: z
372
+ .number()
341
373
  .optional()
342
- .describe("Whether this sandbox can keep this cache warm: it holds the last turn's exact request to replay, on a Claude subscription account."),
374
+ .describe(
375
+ "Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.",
376
+ ),
343
377
  })
344
378
  .optional()
345
379
  .describe(
@@ -385,7 +419,7 @@ export const AgentSummarySchema = z.object({
385
419
  "When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.",
386
420
  ),
387
421
  attention: AgentAttentionSchema.describe("Which kinds of waiting-for-you it is doing."),
388
- // Only the causes that still hold, re-read live (agents/land/standing.ts), never the stored report's own list: a
422
+ // Only the causes that still hold, re-read live (conversations/land/standing.ts), never the stored report's own list: a
389
423
  // blocker the user has since cleared is not something to offer them an action about.
390
424
  conflictCauses: z
391
425
  .array(LandConflictReasonSchema)
@@ -396,7 +430,11 @@ export const AgentSummarySchema = z.object({
396
430
  // Beside `attention` since a reader asks both at a glance, but opposite in shape: a turn gone, not one still parked
397
431
  // and waiting.
398
432
  unfinished: UnfinishedWorkSchema.optional().describe(
399
- "What its last turn left open: steps it never completed, a check still failing. Absent for a turn that finished what it started.",
433
+ "What its last turn left open: steps it never completed. Absent for a turn that finished what it started.",
434
+ ),
435
+ // Written by the turn's own settle from its tool calls; nothing is asked of the model to earn or clear it.
436
+ proof: TurnProofSchema.optional().describe(
437
+ "What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.",
400
438
  ),
401
439
  // Completed turns and lifetime tool calls, the card's msgs/tools counters.
402
440
  turns: z.number().optional().describe("Turns it has finished."),
@@ -458,6 +496,14 @@ export const AgentSummarySchema = z.object({
458
496
  .describe(
459
497
  "The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.",
460
498
  ),
499
+ // The one reading of "runs again by itself" (the daemon's awaitingWake, conversation-state.ts): status, fixStance and
500
+ // the card read it, never the watch list, which is only what the card lists. Absent from a daemon older than it.
501
+ awaitingWake: z
502
+ .boolean()
503
+ .optional()
504
+ .describe(
505
+ "Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.",
506
+ ),
461
507
  // Outside conditions a conversation is parked on; a finished-looking card can restart itself when one fires, and an
462
508
  // armed watch keeps a hosted machine (and its bill) awake. The check command itself never rides the wire, since it
463
509
  // may hold a secret.
@@ -484,6 +530,27 @@ export const AgentSummarySchema = z.object({
484
530
  startedAt: z.number().describe("When it started, in milliseconds."),
485
531
  endedAt: z.number().optional().describe("When the command exited, in milliseconds. Absent while it runs."),
486
532
  exitCode: z.number().int().optional().describe("The code it exited with. Absent while it runs, or when its exit left none."),
533
+ // What the turn that left it running decided about it (agent/tools/jobs/job-fates.ts): one of these, or none
534
+ // while its turn is still going.
535
+ watch: z
536
+ .string()
537
+ .optional()
538
+ .describe(
539
+ "The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.",
540
+ ),
541
+ handed: z
542
+ .boolean()
543
+ .optional()
544
+ .describe(
545
+ "Left running for the person: the turn's reply gave its address, so it outlives the turn, wakes nothing and is theirs to stop.",
546
+ ),
547
+ ports: z.array(z.number().int()).optional().describe("The ports it was listening on when its turn ended."),
548
+ stoppedBy: z
549
+ .enum(["turn", "person", "agent"])
550
+ .optional()
551
+ .describe(
552
+ "Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.",
553
+ ),
487
554
  }),
488
555
  )
489
556
  .optional()
@@ -500,6 +567,10 @@ export const AgentSummarySchema = z.object({
500
567
  ),
501
568
  });
502
569
  export type AgentSummary = z.infer<typeof AgentSummarySchema>;
570
+ // Whether a conversation runs again by itself: the daemon's own reading, or, from a daemon older than it, whether a watch
571
+ // is armed, which was all that one could say.
572
+ export const awaitsWake = (agent: { readonly awaitingWake?: boolean | undefined; readonly watches?: readonly unknown[] | undefined }): boolean =>
573
+ agent.awaitingWake ?? (agent.watches?.length ?? 0) > 0;
503
574
  // One armed watch as a card carries it; derived from AgentSummarySchema.watches so the two shapes can't drift apart.
504
575
  export type AgentWatch = NonNullable<AgentSummary["watches"]>[number];
505
576
  // One background job as a card carries it.
@@ -514,6 +585,10 @@ export const AgentStopWatchingSchema = AgentIdSchema.extend({
514
585
  watchId: z.string().optional().describe("Which watch to disarm. Absent disarms every watch this conversation is parked on."),
515
586
  });
516
587
 
588
+ export const AgentStopJobSchema = AgentIdSchema.extend({
589
+ jobId: z.string().min(1).describe("Which of its background jobs, by the id its card and transcript row carry."),
590
+ });
591
+
517
592
  // Pages a transcript: a read returns its most recent turns and where they start (`from`); handing that back as `before`
518
593
  // asks for the page above. A stale cursor (a rewind, a fork) is clamped, never refused.
519
594
  export const AgentTranscriptQuerySchema = AgentIdSchema.extend({
@@ -297,8 +297,10 @@ export const AutomationSchema = z.object({
297
297
  .describe(
298
298
  "Which models this automation may run on, best first. Required, and nothing is chosen for you: work that fires while nobody is watching spends a real allowance, so it names the models it spends rather than inheriting one. Tried in order, so a spent account does not silently stop the job.",
299
299
  ),
300
- // Absent means the provider's first account; pinning matters more here than for a chat, since nobody is watching to
301
- // notice a stuck one.
300
+ // Absent means the daemon's pick: the account its conversation runs on for a continuing thread (moved off one that
301
+ // can no longer serve), else the one that can serve with the most room. Named, it is a pin, run whatever its state,
302
+ // so a refused account says so rather than being swapped; pinning matters more here than for a chat, since nobody
303
+ // is watching to notice a stuck one.
302
304
  account: z.string().optional().describe("Which account pays for it."),
303
305
  // Absent means the wake reaches no logged-in account at all, the strictest default here, since nobody is at the
304
306
  // composer when it fires.
@@ -1,6 +1,6 @@
1
1
  // What one of the user's own machines is running.
2
2
  import { z } from "zod";
3
- import { hostEntryOf, hostEnvironmentOf, type DeviceFacts, DeviceFactsSchema, userDistrosOf, WslEnvironmentSchema } from "./hosts.js";
3
+ import { parseHostConnection, type DeviceFacts, DeviceFactsSchema, MachineIdSchema, userDistrosOf, WslEnvironmentSchema } from "./hosts.js";
4
4
  import { DEV_VERSION } from "../state/versions.js";
5
5
  // Desktop-sync report shape shared by the agent, daemon and browser, produced only by the agent's own `deviceReport`.
6
6
  // The agent never reports `sandboxes`; the docker half is filled in by whoever reads the report, scoped to the reader's
@@ -9,14 +9,32 @@ import { DEV_VERSION } from "../state/versions.js";
9
9
  // One sandbox's resource share as docker currently enforces it, read off the container by the machine agent.
10
10
  // `overlayRuntime` is the environment's locked demand; `hostRuntime` is the owner's addition; `privileged`/`gpu` are
11
11
  // docker's enforced truth.
12
- // Reshape request, turned into `ic sandbox reshape` flags: absent means leave it, `null` on the two caps means back to
13
- // the default; at least one key must be set.
12
+ // A delta against what runs: absent means leave it, `null` on the two caps means back to the default; at least one key
13
+ // must be set. What the old `reshape` op carries (`ic sandbox reshape` flags); `set-shape` carries a whole shape instead.
14
14
  export const SandboxResourcesAskFieldsSchema = z.object({
15
15
  memoryGib: z.int().positive().nullable().optional(),
16
16
  cpus: z.int().positive().nullable().optional(),
17
17
  privileged: z.boolean().optional(),
18
18
  gpu: z.boolean().optional(),
19
19
  });
20
+ // A sandbox's shape, WHOLE: the owner's own ask for its share of the machine, every field a value. `null` on a cap is
21
+ // the contract's default (the share derived from the engine, or every core). The switches are the owner's ask, not
22
+ // docker's answer: a directive the approved environment demands rides beside it (`overlayRuntime`), and a host
23
+ // without the NVIDIA runtime drops the GPU. `ic` stores it, validates it against the image's run contract when it is
24
+ // saved, and prints it in `ic sandbox list --json`; nobody else merges anything into it.
25
+ export const SandboxShapeSchema = z.object({
26
+ memoryGib: z.int().positive().nullable(),
27
+ cpus: z.int().positive().nullable(),
28
+ privileged: z.boolean(),
29
+ gpu: z.boolean(),
30
+ });
31
+ export type SandboxShape = z.infer<typeof SandboxShapeSchema>;
32
+ // When a `set-shape` takes effect: `now` restarts the sandbox onto it; `nextRestart` saves it in ic's record and
33
+ // restarts nothing, and the next restart through ic applies it — Restart or Start from any door, an update, a rollback
34
+ // or a rebuild. Docker restarting the container by itself (after a crash or a reboot) does not: only ic recreates one.
35
+ export const SandboxShapeWhenSchema = z.enum(["now", "nextRestart"]);
36
+ export type SandboxShapeWhen = z.infer<typeof SandboxShapeWhenSchema>;
37
+
20
38
  export const SandboxResourcesSchema = z.object({
21
39
  // The cgroup memory ceiling in bytes; absent when docker imposes none (the hosted shape).
22
40
  memoryBytes: z.number().optional(),
@@ -26,14 +44,19 @@ export const SandboxResourcesSchema = z.object({
26
44
  gpu: z.boolean(),
27
45
  hostRuntime: z.array(z.string()),
28
46
  overlayRuntime: z.array(z.string()),
29
- // A reshape saved for the sandbox's next restart (`ic sandbox reshape --later`) and not yet in force: the next
30
- // update, rollback, rebuild, reshape or Restart applies it. Absent when nothing is saved.
47
+ // The shape the container runs with (the owner's ask it carries). Absent from an agent older than `set-shape`.
48
+ shape: SandboxShapeSchema.optional(),
49
+ // The shape saved for the sandbox's next restart through ic, and not yet in force. Absent when nothing is saved.
50
+ desired: SandboxShapeSchema.optional(),
51
+ // READ-ONLY, from agents older than `set-shape`: their saved share, a delta against what runs. Nothing new writes
52
+ // it; kept one release so a newer page can still show what an older agent saved. Remove with `later`.
31
53
  saved: SandboxResourcesAskFieldsSchema.optional(),
32
54
  });
33
55
  export type SandboxResources = z.infer<typeof SandboxResourcesSchema>;
34
56
 
35
57
  // Fields also exported separately for callers that compose them and enforce the at-least-one rule themselves.
36
- export const SandboxResourcesAskSchema = SandboxResourcesAskFieldsSchema.refine((ask) => Object.values(ask).some((value) => value !== undefined), {
58
+ // Strict like every device input (DeviceSandboxFlowSchema says why): it is only ever sent to a device.
59
+ export const SandboxResourcesAskSchema = SandboxResourcesAskFieldsSchema.strict().refine((ask) => Object.values(ask).some((value) => value !== undefined), {
37
60
  message: "a reshape must change at least one thing",
38
61
  });
39
62
  export type SandboxResourcesAsk = z.infer<typeof SandboxResourcesAskSchema>;
@@ -50,10 +73,14 @@ export const DeviceSandboxSchema = z.object({
50
73
  tunnelRunning: z.boolean().optional(),
51
74
  // Absent when the reader only ran a cheap `docker ps` listing without inspecting the container.
52
75
  resources: SandboxResourcesSchema.optional(),
76
+ // The update `ic sandbox prepare` built and left waiting for this sandbox; absent when nothing is staged.
77
+ staged: z.object({ image: z.string(), version: z.string().optional(), channel: z.string().optional() }).optional(),
53
78
  });
54
79
  export type DeviceSandbox = z.infer<typeof DeviceSandboxSchema>;
55
80
  // One operation on one sandbox, streamed as lines ending in a `result` or `error` frame. `prepare` builds the pending
56
- // update without touching the container; `reshape` changes only its resources and privileges, not the image.
81
+ // update without touching the container. `set-shape` sets its resources and privileges, now or for the next restart;
82
+ // `forget-shape` drops a shape saved for the next restart. `start`/`restart` apply a saved shape (all through `ic`).
83
+ // `reshape` is the older spelling of `set-shape`, kept one release for pages served by older sandboxes.
57
84
  // `runner-up`/`runner-remove` act on a runner (this sandbox's own container), not a person's sandbox; gated by the
58
85
  // `sandboxes` switch, not removal, since a runner holds no separate workspace to lose.
59
86
  export const DeviceSandboxOpSchema = z.enum([
@@ -65,6 +92,8 @@ export const DeviceSandboxOpSchema = z.enum([
65
92
  "rebuild",
66
93
  "rollback",
67
94
  "reshape",
95
+ "set-shape",
96
+ "forget-shape",
68
97
  "remove",
69
98
  "logs",
70
99
  // The two ops that redeem a fresh setup code; every other one recreates a container from its own existing env.
@@ -76,7 +105,11 @@ export const DeviceSandboxOpSchema = z.enum([
76
105
  "runner-remove",
77
106
  ]);
78
107
  export type DeviceSandboxOp = z.infer<typeof DeviceSandboxOpSchema>;
79
- export const DeviceSandboxFlowSchema = z.object({
108
+ // STRICT, like every input a device is sent: an agent rejects a field it does not know instead of dropping it and doing
109
+ // something else (an older agent once dropped `later` and restarted a sandbox it had been asked to leave running). New
110
+ // behaviour is a new `op`, which an older agent refuses by name, and the daemon refuses an op the agent has not
111
+ // advertised before sending it (device-reports.ts).
112
+ export const DeviceSandboxFlowSchema = z.strictObject({
80
113
  op: DeviceSandboxOpSchema,
81
114
  // Sandbox slug, the runner's name for the runner ops (as `/system/runners`/`ic runner list` know it), or for
82
115
  // `create` the name the claim is about to produce — the one op where the slug is an expectation rather than a
@@ -84,11 +117,14 @@ export const DeviceSandboxFlowSchema = z.object({
84
117
  slug: z.string().min(1),
85
118
  // Approved overlay's sha256, required only by `rebuild`; only content matching it is ever built.
86
119
  hash: z.string().optional(),
87
- // What `reshape` should change, meaningless to the rest. Absent on a reshape applies what is saved for the next
88
- // restart now; absent with `later` forgets what is saved.
120
+ // `set-shape` only, both required by it: the whole shape, and when it takes effect.
121
+ // Strict too, unlike the same shape read off a report (where a newer ic's extra field must not fail an older
122
+ // reader): an order carrying a field this agent does not know is refused rather than carried out without it.
123
+ shape: z.strictObject(SandboxShapeSchema.shape).optional(),
124
+ when: SandboxShapeWhenSchema.optional(),
125
+ // READ-ONLY for one release, from pages served by sandboxes older than `set-shape`: the old `reshape` op's delta,
126
+ // and `later` to save it for the next restart instead. Nothing new sends either.
89
127
  resources: SandboxResourcesAskSchema.optional(),
90
- // `reshape` only: save `resources` for the sandbox's next restart instead of restarting it now, replacing
91
- // whatever was saved before (`ic sandbox reshape --later`).
92
128
  later: z.boolean().optional(),
93
129
  // `runner-up` only, daemon-filled, never by the caller: the browser never holds the pairing credential.
94
130
  parentUrl: z.string().optional(),
@@ -104,6 +140,26 @@ export const DeviceSandboxFlowSchema = z.object({
104
140
  overlayHash: z.string().optional(),
105
141
  });
106
142
  export type DeviceSandboxFlow = z.infer<typeof DeviceSandboxFlowSchema>;
143
+ // The `ic` argv for the shape and power ops, the one place any caller spells them: the machine agent runs it and the
144
+ // Devices view prints it for a person to type when the app's route to the machine is shut. The shape travels as the
145
+ // contract's own object, one `--set <field>=<JSON value>` per field, so no caller maps a field onto a flag of its own:
146
+ // ic reads the pairs back into the contract's shape and refuses a field it does not know, as every device input does.
147
+ // `when` is the contract's own word too. A field left out keeps what ic already has for it (the shape saved for the
148
+ // next restart, else what runs): the Devices view always sends the whole shape, a model's tool call may name one
149
+ // field. Nothing is judged here; ic checks the shape against the image's run contract. Needs an ic that takes `--set`
150
+ // (the `set-shape` feature is advertised only then).
151
+ export type SandboxShapeFields = { readonly [K in keyof SandboxShape]?: SandboxShape[K] | undefined };
152
+ export const icShapeArgs = (slug: string, fields: SandboxShapeFields, when: SandboxShapeWhen): string[] => [
153
+ "sandbox",
154
+ "shape",
155
+ slug,
156
+ ...Object.entries(fields).flatMap(([field, value]) => (value === undefined ? [] : ["--set", `${field}=${JSON.stringify(value)}`])),
157
+ "--when",
158
+ when,
159
+ ];
160
+ export const icForgetShapeArgs = (slug: string): string[] => ["sandbox", "shape", slug, "--forget"];
161
+ // Power goes through ic too, so a Start or Restart from any door applies the shape saved for the next restart.
162
+ export const icPowerArgs = (op: "start" | "stop" | "restart", slug: string): string[] => ["sandbox", op, slug];
107
163
  // Adds the device id to the flow input; the daemon looks up machines by id.
108
164
  export const DeviceSandboxFlowInputSchema = DeviceSandboxFlowSchema.extend({ id: z.string().min(1) });
109
165
  export type DeviceSandboxFlowInput = z.infer<typeof DeviceSandboxFlowInputSchema>;
@@ -123,7 +179,7 @@ export type DeviceFlowLine = z.infer<typeof DeviceFlowLineSchema>;
123
179
  // dropping one is always somebody's decision.
124
180
  export const DeviceAgentOpSchema = z.enum(["upgrade", "restart", "forget-unreachable"]);
125
181
  export type DeviceAgentOp = z.infer<typeof DeviceAgentOpSchema>;
126
- export const DeviceAgentFlowSchema = z.object({ op: DeviceAgentOpSchema });
182
+ export const DeviceAgentFlowSchema = z.strictObject({ op: DeviceAgentOpSchema });
127
183
  export type DeviceAgentFlow = z.infer<typeof DeviceAgentFlowSchema>;
128
184
  // Adds the device id to the flow input.
129
185
  export const DeviceAgentFlowInputSchema = DeviceAgentFlowSchema.extend({ id: z.string().min(1) });
@@ -362,8 +418,10 @@ export const AGENT_STALL_AFTER_MS = 60_000;
362
418
  export const agentStalled = (agent: DeviceAgent, now: number): boolean =>
363
419
  agent.running && agent.lastTickAt !== undefined && now - agent.lastTickAt > AGENT_STALL_AFTER_MS;
364
420
  export const DeviceReportSchema = z.object({
365
- // OS hostname; the join key that dedupes a machine seen via sync and via its `host` capability. Not unique on its
366
- // own: a WSL distro inherits the Windows machine's name, so `wsl` below is what tells those apart.
421
+ // The computer this reading is from (MachineIdSchema): what a sync enrollment is stamped with when its agent posts,
422
+ // and so what joins a machine seen through sync to the same machine seen through its card. Absent from an older agent.
423
+ machineId: MachineIdSchema.optional(),
424
+ // OS hostname, for the owner to read; not a join key (a WSL distro inherits the Windows machine's name).
367
425
  hostname: z.string(),
368
426
  os: z.string(),
369
427
  // Present only inside a WSL distro; the same fact rides the connect-time facts, which no scope can withhold.
@@ -420,9 +478,8 @@ export const DeviceGapSchema = z.enum([
420
478
  "unreported",
421
479
  ]);
422
480
  export type DeviceGap = z.infer<typeof DeviceGapSchema>;
423
- // A machine may be reachable via desktop sync and a host capability at once; the two are reconciled on `hostname` plus
424
- // the environment it came from (`environmentOf`), and left as separate rows when there is nothing to reconcile them by
425
- // or when the environments positively disagree.
481
+ // A machine may be reachable via desktop sync and a host capability at once; the two are one row when both say the same
482
+ // `machineId` and the same environment, and separate rows whenever either has not said (an agent too old to carry it).
426
483
  // `machine` is the enrollment's name for the box (the ssh key's comment): what reports are filed under and what the
427
484
  // revoke route takes. Two machines sharing a key comment share one enrollment identity.
428
485
  export const DeviceSyncSchema = z.object({
@@ -431,6 +488,10 @@ export const DeviceSyncSchema = z.object({
431
488
  mode: z.enum(["sync", "mirror"]),
432
489
  // When this machine last used its enrollment; absent means never, not merely unknown, and is not healthy.
433
490
  seenAt: z.number().optional(),
491
+ // The computer and the environment on it that hold this enrollment, as its agent said when enrolling or in a report
492
+ // since; absent until an agent new enough to say has done so.
493
+ machineId: MachineIdSchema.optional(),
494
+ environment: z.string().optional(),
434
495
  });
435
496
  export type DeviceSync = z.infer<typeof DeviceSyncSchema>;
436
497
  export const DeviceSchema = z.object({
@@ -440,8 +501,14 @@ export const DeviceSchema = z.object({
440
501
  label: z.string(),
441
502
  // Desktop-sync enrollment with this sandbox; absent when reached only via a host capability.
442
503
  sync: DeviceSyncSchema.optional(),
443
- // The host capability's id, when this machine is also a connected device. Absent otherwise.
504
+ // The host connection's key, when this machine is also a connected device. Absent otherwise.
444
505
  hostId: z.string().optional(),
506
+ // The card that connection is of, beside the key rather than parsed out of it: the machine's name, a label. Absent
507
+ // with `hostId`, and from a daemon older than the field (`deviceCard` reads the key then).
508
+ card: z.string().optional(),
509
+ // The computer this row is an environment of, from whichever door has said (MachineIdSchema): what `machinesOf`
510
+ // groups by. Absent when no door has, which leaves the row a computer of its own.
511
+ machineId: MachineIdSchema.optional(),
445
512
  // Host-capability liveness; absent when there is no host capability, not the same as offline.
446
513
  online: z.boolean().optional(),
447
514
  // Carried beside the report so a device with no report still has an OS; `platform` is the normalised slug, `facts`
@@ -480,7 +547,7 @@ export const deviceHostname = (device: Device): string | undefined => device.fac
480
547
  // and that is all an environment nobody has reached since this daemon booted can say about itself: liveness resets on
481
548
  // restart, so facts and reports are absent on a side that is merely asleep.
482
549
  export const deviceEnvironment = (device: Device): string | undefined =>
483
- environmentOf(device.facts, device.report) ?? (device.hostId === undefined ? undefined : hostEnvironmentOf(device.hostId));
550
+ environmentOf(device.facts, device.report) ?? (device.hostId === undefined ? undefined : parseHostConnection(device.hostId).environment);
484
551
 
485
552
  const WSL_PREFIX = "wsl:";
486
553
  export const isWslDevice = (device: Device): boolean => deviceEnvironment(device)?.startsWith(WSL_PREFIX) === true;
@@ -495,24 +562,25 @@ export const deviceDistro = (device: Device): string | undefined => {
495
562
  const byEnvironment = (a: Device, b: Device): number =>
496
563
  Number(isWslDevice(a)) - Number(isWslDevice(b)) || (deviceDistro(a) ?? a.label).localeCompare(deviceDistro(b) ?? b.label);
497
564
 
498
- const hostnameKey = (device: Device): string | undefined => deviceHostname(device)?.toLowerCase();
499
-
500
565
  // The card a device's door hangs off: one card is one computer, so two doors naming the same card are one machine
501
566
  // whatever either has said about itself — which is the whole of what an environment that has never connected says.
502
- const cardKey = (device: Device): string | undefined => (device.hostId === undefined ? undefined : hostEntryOf(device.hostId));
503
-
504
- // What makes two devices one computer: a shared card, or a shared hostname where a distro answers to it, since WSL
505
- // hands a distro the Windows machine's name. Two native installs that merely share a name stay two machines.
506
- const tokensOf = (device: Device, distroNames: ReadonlySet<string>): string[] => {
507
- const card = cardKey(device);
508
- const hostname = hostnameKey(device);
509
- return [
510
- ...(card === undefined ? [] : [`card:${card.toLowerCase()}`]),
511
- ...(hostname !== undefined && distroNames.has(hostname) ? [`host:${hostname}`] : []),
512
- ];
567
+ // The row's own label, else its key read by the one parser there is (a row from a daemon older than `card`).
568
+ export const deviceCard = (device: Pick<Device, "card" | "hostId">): string | undefined =>
569
+ device.card ?? (device.hostId === undefined ? undefined : parseHostConnection(device.hostId).card);
570
+
571
+ // The computer a device is on, by whichever door said: its row, its connect-time facts, its report, its sync enrollment.
572
+ export const deviceMachineId = (device: Device): string | undefined =>
573
+ device.machineId ?? device.facts?.machineId ?? device.report?.machineId ?? device.sync?.machineId;
574
+
575
+ // What makes two devices one computer: a shared card, or a shared machine id. Nothing else: a hostname is shared by a
576
+ // PC and its WSL distros and by two unrelated machines with the same name, so it joined things by accident.
577
+ const tokensOf = (device: Device): string[] => {
578
+ const card = deviceCard(device);
579
+ const machine = deviceMachineId(device);
580
+ return [...(card === undefined ? [] : [`card:${card.toLowerCase()}`]), ...(machine === undefined ? [] : [`machine:${machine}`])];
513
581
  };
514
582
 
515
- // Components over both joins at once: a device holding a card token and a hostname token is the evidence that merges
583
+ // Components over both joins at once: a device holding a card token and a machine token is the evidence that merges
516
584
  // the machines those tokens named, so a distro connected under a card of its own still lands on the PC it runs on.
517
585
  interface Component {
518
586
  readonly tokens: Set<string>;
@@ -520,10 +588,9 @@ interface Component {
520
588
  }
521
589
 
522
590
  const componentsOf = (devices: readonly Device[]): Component[] => {
523
- const distroNames = new Set(devices.filter(isWslDevice).map(hostnameKey).filter((name) => name !== undefined));
524
591
  const components: Component[] = [];
525
592
  for (const device of devices) {
526
- const tokens = tokensOf(device, distroNames);
593
+ const tokens = tokensOf(device);
527
594
  const [head, ...merged] = components.filter((component) => tokens.some((token) => component.tokens.has(token)));
528
595
  if (head === undefined) {
529
596
  components.push({ tokens: new Set(tokens), devices: [device] });
@@ -544,7 +611,7 @@ const componentsOf = (devices: readonly Device[]): Component[] => {
544
611
 
545
612
  // Addressed by the card of the side that owns the screen, an address that does not change when a second environment
546
613
  // connects — unlike a row key, which is a hostname another environment can take first. An uncarded fold has only the
547
- // hostname its doors share, and an uncarded lone device its own key. Called what the owner calls it: the name the
614
+ // machine id its doors share, and an uncarded lone device its own key. Called what the owner calls it: the name the
548
615
  // leading environment carries, unless that is nothing but its door id, which reads as a PC named after one of its
549
616
  // own sides.
550
617
  const machineOf = (environments: readonly Device[]): Machine => {
@@ -553,8 +620,8 @@ const machineOf = (environments: readonly Device[]): Machine => {
553
620
  if (first === undefined) {
554
621
  return { key: "", label: "", environments };
555
622
  }
556
- const named = environments.map(cardKey).find((key) => key !== undefined) ?? (rest.length === 0 ? undefined : deviceHostname(first));
557
- const label = first.label === first.hostId ? (cardKey(first) ?? first.label) : first.label;
623
+ const named = environments.map(deviceCard).find((key) => key !== undefined) ?? (rest.length === 0 ? undefined : deviceMachineId(first));
624
+ const label = first.label === first.hostId ? (deviceCard(first) ?? first.label) : first.label;
558
625
  return { key: named ?? first.key, label, environments };
559
626
  };
560
627
 
@@ -177,6 +177,12 @@ export const ExtensionSummarySchema = z.object({
177
177
  })
178
178
  .optional()
179
179
  .describe("Present only for an extension that ships a server half."),
180
+ problems: z
181
+ .array(z.string())
182
+ .optional()
183
+ .describe(
184
+ "Declarations in its manifest the sandbox refused at load, each a sentence saying what and why, such as a listener for a provider another extension already owns. The rest of it still loads. Absent when nothing was refused.",
185
+ ),
180
186
  // Update lifecycle, present only for a git-installed extension (absent for builtin/workspace):
181
187
  // update: the badge.
182
188
  // advisory: the alarm.
@@ -450,6 +450,11 @@ export const AgentChangesSchema = z.object({
450
450
  });
451
451
  export type AgentChanges = z.infer<typeof AgentChangesSchema>;
452
452
 
453
+ // The review's `conflicts` alone, re-derived the same way. The whole review also takes a code-only line count per
454
+ // file, and on a branch of hundreds of files that count, not the conflict check, is what takes seconds.
455
+ export const AgentConflictsSchema = AgentChangesSchema.pick({ conflicts: true });
456
+ export type AgentConflicts = z.infer<typeof AgentConflictsSchema>;
457
+
453
458
  // Names scratch exactly as `AgentChanges.scratch` lists it; anything else is refused, so a stale list cannot reach a
454
459
  // file that was never scratch.
455
460
  export const AgentScratchSchema = z.object({