@intentic/sandbox-contract 1.247.0 → 1.249.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 (883) hide show
  1. package/README.md +18 -15
  2. package/dist/chores/chores.d.ts.map +1 -1
  3. package/dist/chores/chores.js.map +1 -1
  4. package/dist/chores/digest.d.ts.map +1 -1
  5. package/dist/chores/digest.js.map +1 -1
  6. package/dist/chores/extension-update.d.ts.map +1 -1
  7. package/dist/chores/extension-update.js.map +1 -1
  8. package/dist/chores/fix-deps.d.ts.map +1 -1
  9. package/dist/chores/fix-deps.js.map +1 -1
  10. package/dist/chores/index.d.ts +2 -2
  11. package/dist/chores/index.d.ts.map +1 -1
  12. package/dist/chores/index.js +1 -1
  13. package/dist/chores/index.js.map +1 -1
  14. package/dist/chores/probes.d.ts.map +1 -1
  15. package/dist/chores/probes.js.map +1 -1
  16. package/dist/chores/prompt.d.ts.map +1 -1
  17. package/dist/chores/prompt.js.map +1 -1
  18. package/dist/chores/stack.d.ts.map +1 -1
  19. package/dist/chores/stack.js.map +1 -1
  20. package/dist/chores/verdict.d.ts +7 -1
  21. package/dist/chores/verdict.d.ts.map +1 -1
  22. package/dist/chores/verdict.js +8 -0
  23. package/dist/chores/verdict.js.map +1 -1
  24. package/dist/contracts/accounts.contract.d.ts.map +1 -1
  25. package/dist/contracts/accounts.contract.js.map +1 -1
  26. package/dist/contracts/activity.contract.d.ts +1 -0
  27. package/dist/contracts/activity.contract.d.ts.map +1 -1
  28. package/dist/contracts/agent.contract.d.ts +13 -205
  29. package/dist/contracts/agent.contract.d.ts.map +1 -1
  30. package/dist/contracts/agent.contract.js +2 -1
  31. package/dist/contracts/agent.contract.js.map +1 -1
  32. package/dist/contracts/agents.contract.d.ts +228 -54
  33. package/dist/contracts/agents.contract.d.ts.map +1 -1
  34. package/dist/contracts/agents.contract.js +11 -2
  35. package/dist/contracts/agents.contract.js.map +1 -1
  36. package/dist/contracts/automations.contract.d.ts +35 -17
  37. package/dist/contracts/automations.contract.d.ts.map +1 -1
  38. package/dist/contracts/automations.contract.js +10 -1
  39. package/dist/contracts/automations.contract.js.map +1 -1
  40. package/dist/contracts/capabilities.contract.d.ts +0 -12
  41. package/dist/contracts/capabilities.contract.d.ts.map +1 -1
  42. package/dist/contracts/capabilities.contract.js +1 -1
  43. package/dist/contracts/capabilities.contract.js.map +1 -1
  44. package/dist/contracts/chores.contract.d.ts.map +1 -1
  45. package/dist/contracts/chores.contract.js.map +1 -1
  46. package/dist/contracts/ci.contract.d.ts +9 -0
  47. package/dist/contracts/ci.contract.d.ts.map +1 -1
  48. package/dist/contracts/endpoints.contract.d.ts.map +1 -1
  49. package/dist/contracts/endpoints.contract.js.map +1 -1
  50. package/dist/contracts/exit.contract.d.ts.map +1 -1
  51. package/dist/contracts/exit.contract.js +1 -1
  52. package/dist/contracts/exit.contract.js.map +1 -1
  53. package/dist/contracts/extensions.contract.d.ts.map +1 -1
  54. package/dist/contracts/extensions.contract.js.map +1 -1
  55. package/dist/contracts/git.contract.d.ts +45 -6
  56. package/dist/contracts/git.contract.d.ts.map +1 -1
  57. package/dist/contracts/git.contract.js +9 -9
  58. package/dist/contracts/git.contract.js.map +1 -1
  59. package/dist/contracts/host.contract.d.ts +2 -0
  60. package/dist/contracts/host.contract.d.ts.map +1 -1
  61. package/dist/contracts/host.contract.js.map +1 -1
  62. package/dist/contracts/intentic.contract.js +1 -1
  63. package/dist/contracts/intentic.contract.js.map +1 -1
  64. package/dist/contracts/logs.contract.d.ts.map +1 -1
  65. package/dist/contracts/logs.contract.js.map +1 -1
  66. package/dist/contracts/loops.contract.d.ts.map +1 -1
  67. package/dist/contracts/loops.contract.js.map +1 -1
  68. package/dist/contracts/personas.contract.d.ts +39 -4
  69. package/dist/contracts/personas.contract.d.ts.map +1 -1
  70. package/dist/contracts/personas.contract.js +10 -1
  71. package/dist/contracts/personas.contract.js.map +1 -1
  72. package/dist/contracts/providers.contract.d.ts.map +1 -1
  73. package/dist/contracts/providers.contract.js.map +1 -1
  74. package/dist/contracts/runner.contract.d.ts +6 -80
  75. package/dist/contracts/runner.contract.d.ts.map +1 -1
  76. package/dist/contracts/runner.contract.js +2 -2
  77. package/dist/contracts/runner.contract.js.map +1 -1
  78. package/dist/contracts/safety.contract.d.ts.map +1 -1
  79. package/dist/contracts/safety.contract.js +1 -1
  80. package/dist/contracts/safety.contract.js.map +1 -1
  81. package/dist/contracts/secrets.contract.d.ts.map +1 -1
  82. package/dist/contracts/secrets.contract.js.map +1 -1
  83. package/dist/contracts/sessions.contract.d.ts +2 -41
  84. package/dist/contracts/sessions.contract.d.ts.map +1 -1
  85. package/dist/contracts/sessions.contract.js +1 -1
  86. package/dist/contracts/sessions.contract.js.map +1 -1
  87. package/dist/contracts/settings.contract.d.ts +10 -10
  88. package/dist/contracts/settings.contract.d.ts.map +1 -1
  89. package/dist/contracts/settings.contract.js.map +1 -1
  90. package/dist/contracts/share.contract.d.ts.map +1 -1
  91. package/dist/contracts/share.contract.js.map +1 -1
  92. package/dist/contracts/skills.contract.d.ts.map +1 -1
  93. package/dist/contracts/skills.contract.js.map +1 -1
  94. package/dist/contracts/system.contract.d.ts +49 -73
  95. package/dist/contracts/system.contract.d.ts.map +1 -1
  96. package/dist/contracts/system.contract.js +12 -2
  97. package/dist/contracts/system.contract.js.map +1 -1
  98. package/dist/contracts/usage.contract.d.ts.map +1 -1
  99. package/dist/contracts/usage.contract.js.map +1 -1
  100. package/dist/contracts/vpn.contract.d.ts.map +1 -1
  101. package/dist/contracts/vpn.contract.js +1 -1
  102. package/dist/contracts/vpn.contract.js.map +1 -1
  103. package/dist/contracts/webext.contract.d.ts.map +1 -1
  104. package/dist/contracts/webext.contract.js.map +1 -1
  105. package/dist/contracts/workflows.contract.d.ts +7 -6
  106. package/dist/contracts/workflows.contract.d.ts.map +1 -1
  107. package/dist/contracts/workflows.contract.js +12 -3
  108. package/dist/contracts/workflows.contract.js.map +1 -1
  109. package/dist/contracts/workspace.contract.d.ts.map +1 -1
  110. package/dist/contracts/workspace.contract.js.map +1 -1
  111. package/dist/events/agent-events.d.ts +1237 -0
  112. package/dist/events/agent-events.d.ts.map +1 -0
  113. package/dist/events/agent-events.js +226 -0
  114. package/dist/events/agent-events.js.map +1 -0
  115. package/dist/events/cards.d.ts +404 -0
  116. package/dist/events/cards.d.ts.map +1 -0
  117. package/dist/events/cards.js +179 -0
  118. package/dist/events/cards.js.map +1 -0
  119. package/dist/events/resume.d.ts +23 -0
  120. package/dist/events/resume.d.ts.map +1 -0
  121. package/dist/events/resume.js +31 -0
  122. package/dist/events/resume.js.map +1 -0
  123. package/dist/events/system-events.d.ts +615 -0
  124. package/dist/events/system-events.d.ts.map +1 -0
  125. package/dist/events/system-events.js +64 -0
  126. package/dist/events/system-events.js.map +1 -0
  127. package/dist/events/transcript.d.ts +1755 -0
  128. package/dist/events/transcript.d.ts.map +1 -0
  129. package/dist/events/transcript.js +205 -0
  130. package/dist/events/transcript.js.map +1 -0
  131. package/dist/ids/conversation-ids.d.ts +8 -0
  132. package/dist/ids/conversation-ids.d.ts.map +1 -0
  133. package/dist/{conversation-ids.js → ids/conversation-ids.js} +13 -0
  134. package/dist/ids/conversation-ids.js.map +1 -0
  135. package/dist/ids/hostnames.d.ts.map +1 -0
  136. package/dist/ids/hostnames.js.map +1 -0
  137. package/dist/ids/session-names.d.ts.map +1 -0
  138. package/dist/ids/session-names.js.map +1 -0
  139. package/dist/ids/share-paths.d.ts.map +1 -0
  140. package/dist/ids/share-paths.js.map +1 -0
  141. package/dist/{tunnel-ids.d.ts → ids/tunnel-ids.d.ts} +1 -0
  142. package/dist/ids/tunnel-ids.d.ts.map +1 -0
  143. package/dist/{tunnel-ids.js → ids/tunnel-ids.js} +1 -0
  144. package/dist/ids/tunnel-ids.js.map +1 -0
  145. package/dist/index.d.ts +464 -447
  146. package/dist/index.d.ts.map +1 -1
  147. package/dist/index.js +54 -47
  148. package/dist/index.js.map +1 -1
  149. package/dist/{agent-catalog.d.ts → models/agent-catalog.d.ts} +2 -2
  150. package/dist/models/agent-catalog.d.ts.map +1 -0
  151. package/dist/models/agent-catalog.js.map +1 -0
  152. package/dist/models/agent-runtimes.d.ts.map +1 -0
  153. package/dist/models/agent-runtimes.js.map +1 -0
  154. package/dist/{fast-tier.d.ts → models/fast-tier.d.ts} +1 -1
  155. package/dist/models/fast-tier.d.ts.map +1 -0
  156. package/dist/models/fast-tier.js.map +1 -0
  157. package/dist/models/model-order.d.ts.map +1 -0
  158. package/dist/models/model-order.js.map +1 -0
  159. package/dist/{model-pins.d.ts → models/model-pins.d.ts} +2 -4
  160. package/dist/models/model-pins.d.ts.map +1 -0
  161. package/dist/models/model-pins.js +22 -0
  162. package/dist/models/model-pins.js.map +1 -0
  163. package/dist/{model-roles.d.ts → models/model-roles.d.ts} +25 -30
  164. package/dist/models/model-roles.d.ts.map +1 -0
  165. package/dist/{model-roles.js → models/model-roles.js} +30 -27
  166. package/dist/models/model-roles.js.map +1 -0
  167. package/dist/{plan-pools.d.ts → models/plan-pools.d.ts} +1 -1
  168. package/dist/models/plan-pools.d.ts.map +1 -0
  169. package/dist/models/plan-pools.js.map +1 -0
  170. package/dist/models/prompt-complexity.d.ts.map +1 -0
  171. package/dist/models/prompt-complexity.js.map +1 -0
  172. package/dist/models/provider-specs.d.ts.map +1 -0
  173. package/dist/models/provider-specs.js.map +1 -0
  174. package/dist/policy/approvals-execution.d.ts.map +1 -0
  175. package/dist/policy/approvals-execution.js.map +1 -0
  176. package/dist/{batch-runs.d.ts → policy/batch-runs.d.ts} +5 -1
  177. package/dist/policy/batch-runs.d.ts.map +1 -0
  178. package/dist/{batch-runs.js → policy/batch-runs.js} +10 -7
  179. package/dist/policy/batch-runs.js.map +1 -0
  180. package/dist/policy/capability-env.d.ts.map +1 -0
  181. package/dist/policy/capability-env.js.map +1 -0
  182. package/dist/policy/capability-secrets.d.ts.map +1 -0
  183. package/dist/policy/capability-secrets.js.map +1 -0
  184. package/dist/{card-status.d.ts → policy/card-status.d.ts} +2 -2
  185. package/dist/policy/card-status.d.ts.map +1 -0
  186. package/dist/{card-status.js → policy/card-status.js} +1 -5
  187. package/dist/policy/card-status.js.map +1 -0
  188. package/dist/{command-classes.d.ts → policy/command-classes.d.ts} +6 -2
  189. package/dist/policy/command-classes.d.ts.map +1 -0
  190. package/dist/{command-classes.js → policy/command-classes.js} +48 -11
  191. package/dist/policy/command-classes.js.map +1 -0
  192. package/dist/{command-run.d.ts → policy/command-run.d.ts} +1 -1
  193. package/dist/policy/command-run.d.ts.map +1 -0
  194. package/dist/policy/command-run.js.map +1 -0
  195. package/dist/policy/control-scopes.d.ts +16 -0
  196. package/dist/policy/control-scopes.d.ts.map +1 -0
  197. package/dist/policy/control-scopes.js +26 -0
  198. package/dist/policy/control-scopes.js.map +1 -0
  199. package/dist/policy/credential-material.d.ts.map +1 -0
  200. package/dist/policy/credential-material.js.map +1 -0
  201. package/dist/policy/needs-action.d.ts.map +1 -0
  202. package/dist/policy/needs-action.js.map +1 -0
  203. package/dist/policy/output-fields.d.ts.map +1 -0
  204. package/dist/policy/output-fields.js.map +1 -0
  205. package/dist/policy/overlay-lint.d.ts.map +1 -0
  206. package/dist/policy/overlay-lint.js.map +1 -0
  207. package/dist/policy/owner-ticket.d.ts.map +1 -0
  208. package/dist/{owner-ticket.js → policy/owner-ticket.js} +1 -1
  209. package/dist/policy/owner-ticket.js.map +1 -0
  210. package/dist/{safety-policy.d.ts → policy/safety-policy.d.ts} +6 -5
  211. package/dist/policy/safety-policy.d.ts.map +1 -0
  212. package/dist/{safety-policy.js → policy/safety-policy.js} +12 -12
  213. package/dist/policy/safety-policy.js.map +1 -0
  214. package/dist/policy/search-globs.d.ts.map +1 -0
  215. package/dist/policy/search-globs.js.map +1 -0
  216. package/dist/protocol/container-requirements.d.ts +20 -0
  217. package/dist/protocol/container-requirements.d.ts.map +1 -0
  218. package/dist/protocol/container-requirements.js +23 -0
  219. package/dist/protocol/container-requirements.js.map +1 -0
  220. package/dist/{host-protocol.d.ts → protocol/host-protocol.d.ts} +1 -0
  221. package/dist/protocol/host-protocol.d.ts.map +1 -0
  222. package/dist/{host-protocol.js → protocol/host-protocol.js} +1 -0
  223. package/dist/protocol/host-protocol.js.map +1 -0
  224. package/dist/protocol/ingress-contract.d.ts.map +1 -0
  225. package/dist/{ingress-contract.js → protocol/ingress-contract.js} +1 -1
  226. package/dist/protocol/ingress-contract.js.map +1 -0
  227. package/dist/protocol/ingress-protocol.d.ts.map +1 -0
  228. package/dist/protocol/ingress-protocol.js.map +1 -0
  229. package/dist/protocol/listener-protocol.d.ts.map +1 -0
  230. package/dist/{listener-protocol.js → protocol/listener-protocol.js} +1 -1
  231. package/dist/protocol/listener-protocol.js.map +1 -0
  232. package/dist/{peer-dial.d.ts → protocol/peer-dial.d.ts} +4 -1
  233. package/dist/protocol/peer-dial.d.ts.map +1 -0
  234. package/dist/{peer-dial.js → protocol/peer-dial.js} +51 -10
  235. package/dist/protocol/peer-dial.js.map +1 -0
  236. package/dist/protocol/peer-mcp-server.d.ts.map +1 -0
  237. package/dist/protocol/peer-mcp-server.js.map +1 -0
  238. package/dist/protocol/request-id.d.ts.map +1 -0
  239. package/dist/protocol/request-id.js.map +1 -0
  240. package/dist/protocol/routes.d.ts.map +1 -0
  241. package/dist/protocol/routes.js.map +1 -0
  242. package/dist/{runner-protocol.d.ts → protocol/runner-protocol.d.ts} +1 -0
  243. package/dist/protocol/runner-protocol.d.ts.map +1 -0
  244. package/dist/{runner-protocol.js → protocol/runner-protocol.js} +2 -1
  245. package/dist/protocol/runner-protocol.js.map +1 -0
  246. package/dist/protocol/sse.d.ts.map +1 -0
  247. package/dist/protocol/sse.js.map +1 -0
  248. package/dist/{terminal-protocol.d.ts → protocol/terminal-protocol.d.ts} +1 -3
  249. package/dist/protocol/terminal-protocol.d.ts.map +1 -0
  250. package/dist/protocol/terminal-protocol.js.map +1 -0
  251. package/dist/protocol/webext-links.d.ts.map +1 -0
  252. package/dist/protocol/webext-links.js.map +1 -0
  253. package/dist/{webext-protocol.d.ts → protocol/webext-protocol.d.ts} +1 -0
  254. package/dist/protocol/webext-protocol.d.ts.map +1 -0
  255. package/dist/{webext-protocol.js → protocol/webext-protocol.js} +1 -0
  256. package/dist/protocol/webext-protocol.js.map +1 -0
  257. package/dist/schemas/activity.d.ts +2 -0
  258. package/dist/schemas/activity.d.ts.map +1 -1
  259. package/dist/schemas/activity.js +4 -0
  260. package/dist/schemas/activity.js.map +1 -1
  261. package/dist/schemas/agent.d.ts +17 -4
  262. package/dist/schemas/agent.d.ts.map +1 -1
  263. package/dist/schemas/agent.js +21 -4
  264. package/dist/schemas/agent.js.map +1 -1
  265. package/dist/schemas/agents.d.ts +17 -4
  266. package/dist/schemas/agents.d.ts.map +1 -1
  267. package/dist/schemas/agents.js +15 -4
  268. package/dist/schemas/agents.js.map +1 -1
  269. package/dist/schemas/approvals.d.ts.map +1 -1
  270. package/dist/schemas/approvals.js.map +1 -1
  271. package/dist/schemas/automations.d.ts +54 -28
  272. package/dist/schemas/automations.d.ts.map +1 -1
  273. package/dist/schemas/automations.js +26 -8
  274. package/dist/schemas/automations.js.map +1 -1
  275. package/dist/schemas/capabilities.d.ts +0 -8
  276. package/dist/schemas/capabilities.d.ts.map +1 -1
  277. package/dist/schemas/capabilities.js +0 -4
  278. package/dist/schemas/capabilities.js.map +1 -1
  279. package/dist/schemas/ci.d.ts +10 -0
  280. package/dist/schemas/ci.d.ts.map +1 -1
  281. package/dist/schemas/ci.js +9 -1
  282. package/dist/schemas/ci.js.map +1 -1
  283. package/dist/schemas/codebase-health.d.ts.map +1 -1
  284. package/dist/schemas/codebase-health.js.map +1 -1
  285. package/dist/schemas/devices.d.ts +90 -0
  286. package/dist/schemas/devices.d.ts.map +1 -1
  287. package/dist/schemas/devices.js +10 -1
  288. package/dist/schemas/devices.js.map +1 -1
  289. package/dist/schemas/engines.d.ts.map +1 -1
  290. package/dist/schemas/engines.js.map +1 -1
  291. package/dist/schemas/environment.d.ts.map +1 -1
  292. package/dist/schemas/environment.js.map +1 -1
  293. package/dist/schemas/exit.d.ts.map +1 -1
  294. package/dist/schemas/exit.js.map +1 -1
  295. package/dist/schemas/extension-updates.d.ts.map +1 -1
  296. package/dist/schemas/extension-updates.js.map +1 -1
  297. package/dist/schemas/git-history.d.ts.map +1 -1
  298. package/dist/schemas/git-history.js.map +1 -1
  299. package/dist/schemas/git.d.ts +80 -16
  300. package/dist/schemas/git.d.ts.map +1 -1
  301. package/dist/schemas/git.js +37 -25
  302. package/dist/schemas/git.js.map +1 -1
  303. package/dist/schemas/history.d.ts.map +1 -1
  304. package/dist/schemas/history.js.map +1 -1
  305. package/dist/schemas/hosts.d.ts.map +1 -1
  306. package/dist/schemas/hosts.js.map +1 -1
  307. package/dist/schemas/inventory.d.ts.map +1 -1
  308. package/dist/schemas/inventory.js.map +1 -1
  309. package/dist/schemas/issues.d.ts +0 -1
  310. package/dist/schemas/issues.d.ts.map +1 -1
  311. package/dist/schemas/issues.js +0 -1
  312. package/dist/schemas/issues.js.map +1 -1
  313. package/dist/schemas/logs.d.ts.map +1 -1
  314. package/dist/schemas/logs.js.map +1 -1
  315. package/dist/schemas/loops.d.ts.map +1 -1
  316. package/dist/schemas/loops.js +1 -1
  317. package/dist/schemas/loops.js.map +1 -1
  318. package/dist/schemas/maintenance.d.ts.map +1 -1
  319. package/dist/schemas/maintenance.js.map +1 -1
  320. package/dist/schemas/marketplace.d.ts +0 -4
  321. package/dist/schemas/marketplace.d.ts.map +1 -1
  322. package/dist/schemas/panels.d.ts.map +1 -1
  323. package/dist/schemas/panels.js.map +1 -1
  324. package/dist/schemas/personas.d.ts +49 -4
  325. package/dist/schemas/personas.d.ts.map +1 -1
  326. package/dist/schemas/personas.js +32 -5
  327. package/dist/schemas/personas.js.map +1 -1
  328. package/dist/schemas/plan-limits.d.ts +2 -4
  329. package/dist/schemas/plan-limits.d.ts.map +1 -1
  330. package/dist/schemas/plan-limits.js +5 -8
  331. package/dist/schemas/plan-limits.js.map +1 -1
  332. package/dist/schemas/ports.d.ts.map +1 -1
  333. package/dist/schemas/ports.js.map +1 -1
  334. package/dist/schemas/provider-oauth.d.ts.map +1 -1
  335. package/dist/schemas/provider-oauth.js.map +1 -1
  336. package/dist/schemas/provider-subscriptions.d.ts +1 -1
  337. package/dist/schemas/provider-subscriptions.d.ts.map +1 -1
  338. package/dist/schemas/provider-subscriptions.js +1 -1
  339. package/dist/schemas/provider-subscriptions.js.map +1 -1
  340. package/dist/schemas/public.d.ts.map +1 -1
  341. package/dist/schemas/public.js.map +1 -1
  342. package/dist/schemas/push.d.ts.map +1 -1
  343. package/dist/schemas/push.js.map +1 -1
  344. package/dist/schemas/secrets.d.ts.map +1 -1
  345. package/dist/schemas/secrets.js.map +1 -1
  346. package/dist/schemas/settings.d.ts +7 -5
  347. package/dist/schemas/settings.d.ts.map +1 -1
  348. package/dist/schemas/settings.js +18 -8
  349. package/dist/schemas/settings.js.map +1 -1
  350. package/dist/schemas/share.d.ts.map +1 -1
  351. package/dist/schemas/share.js.map +1 -1
  352. package/dist/schemas/shared.d.ts +4 -0
  353. package/dist/schemas/shared.d.ts.map +1 -1
  354. package/dist/schemas/shared.js +3 -0
  355. package/dist/schemas/shared.js.map +1 -1
  356. package/dist/schemas/system.d.ts +6 -0
  357. package/dist/schemas/system.d.ts.map +1 -1
  358. package/dist/schemas/system.js +10 -0
  359. package/dist/schemas/system.js.map +1 -1
  360. package/dist/schemas/terminal.d.ts.map +1 -1
  361. package/dist/schemas/terminal.js.map +1 -1
  362. package/dist/schemas/usage.d.ts.map +1 -1
  363. package/dist/schemas/usage.js.map +1 -1
  364. package/dist/schemas/vpn.d.ts.map +1 -1
  365. package/dist/schemas/vpn.js.map +1 -1
  366. package/dist/schemas/webext.d.ts.map +1 -1
  367. package/dist/schemas/webext.js.map +1 -1
  368. package/dist/schemas/workflows.d.ts +66 -9
  369. package/dist/schemas/workflows.d.ts.map +1 -1
  370. package/dist/schemas/workflows.js +6 -5
  371. package/dist/schemas/workflows.js.map +1 -1
  372. package/dist/schemas/workspace-repos.d.ts.map +1 -1
  373. package/dist/schemas/workspace-repos.js.map +1 -1
  374. package/dist/schemas/workspace-search.d.ts.map +1 -1
  375. package/dist/schemas/workspace-search.js.map +1 -1
  376. package/dist/schemas/workspace-setup.d.ts.map +1 -1
  377. package/dist/schemas/workspace-setup.js.map +1 -1
  378. package/dist/schemas/workspace-tree.d.ts.map +1 -1
  379. package/dist/schemas/workspace-tree.js.map +1 -1
  380. package/dist/state/arrival.d.ts.map +1 -0
  381. package/dist/{arrival.js → state/arrival.js} +1 -1
  382. package/dist/state/arrival.js.map +1 -0
  383. package/dist/state/contract-lock.d.ts.map +1 -0
  384. package/dist/{contract-lock.js → state/contract-lock.js} +1 -1
  385. package/dist/state/contract-lock.js.map +1 -0
  386. package/dist/{definition.d.ts → state/definition.d.ts} +21 -17
  387. package/dist/state/definition.d.ts.map +1 -0
  388. package/dist/{definition.js → state/definition.js} +4 -4
  389. package/dist/state/definition.js.map +1 -0
  390. package/dist/state/history-state.d.ts.map +1 -0
  391. package/dist/{history-state.js → state/history-state.js} +1 -0
  392. package/dist/state/history-state.js.map +1 -0
  393. package/dist/state/runtime-state.d.ts.map +1 -0
  394. package/dist/state/runtime-state.js.map +1 -0
  395. package/dist/state/starter.d.ts.map +1 -0
  396. package/dist/state/starter.js.map +1 -0
  397. package/dist/state/state-portability.d.ts.map +1 -0
  398. package/dist/state/state-portability.js.map +1 -0
  399. package/dist/state/versions.d.ts.map +1 -0
  400. package/dist/state/versions.js.map +1 -0
  401. package/dist/{workspace-state.d.ts → state/workspace-state.d.ts} +8 -5
  402. package/dist/state/workspace-state.d.ts.map +1 -0
  403. package/dist/{workspace-state.js → state/workspace-state.js} +25 -1
  404. package/dist/state/workspace-state.js.map +1 -0
  405. package/dist/{documents.d.ts → text/documents.d.ts} +1 -1
  406. package/dist/text/documents.d.ts.map +1 -0
  407. package/dist/{documents.js → text/documents.js} +1 -1
  408. package/dist/text/documents.js.map +1 -0
  409. package/dist/text/embed.d.ts.map +1 -0
  410. package/dist/text/embed.js.map +1 -0
  411. package/dist/text/mentions.d.ts.map +1 -0
  412. package/dist/text/mentions.js.map +1 -0
  413. package/dist/text/model-answer.d.ts +3 -0
  414. package/dist/text/model-answer.d.ts.map +1 -0
  415. package/dist/text/model-answer.js +3 -0
  416. package/dist/text/model-answer.js.map +1 -0
  417. package/dist/text/path-refs.d.ts.map +1 -0
  418. package/dist/text/path-refs.js.map +1 -0
  419. package/dist/{shell-regions.d.ts → text/shell-regions.d.ts} +1 -1
  420. package/dist/text/shell-regions.d.ts.map +1 -0
  421. package/dist/text/shell-regions.js.map +1 -0
  422. package/dist/text/title.d.ts.map +1 -0
  423. package/dist/text/title.js.map +1 -0
  424. package/dist/{transcript-fold.d.ts → text/transcript-fold.d.ts} +2 -1
  425. package/dist/text/transcript-fold.d.ts.map +1 -0
  426. package/dist/{transcript-fold.js → text/transcript-fold.js} +2 -16
  427. package/dist/text/transcript-fold.js.map +1 -0
  428. package/dist/text/whisper.d.ts +3 -0
  429. package/dist/text/whisper.d.ts.map +1 -0
  430. package/dist/text/whisper.js +11 -0
  431. package/dist/text/whisper.js.map +1 -0
  432. package/dist/{workflow-faults.d.ts → text/workflow-faults.d.ts} +1 -1
  433. package/dist/text/workflow-faults.d.ts.map +1 -0
  434. package/dist/{workflow-faults.js → text/workflow-faults.js} +1 -1
  435. package/dist/text/workflow-faults.js.map +1 -0
  436. package/package.json +70 -70
  437. package/src/chores/chores.test.ts +4 -10
  438. package/src/chores/chores.ts +148 -392
  439. package/src/chores/digest.ts +8 -28
  440. package/src/chores/extension-update.ts +3 -8
  441. package/src/chores/fix-deps.ts +4 -18
  442. package/src/chores/index.ts +2 -2
  443. package/src/chores/probes.test.ts +7 -43
  444. package/src/chores/probes.ts +62 -167
  445. package/src/chores/prompt.ts +10 -34
  446. package/src/chores/stack.test.ts +6 -45
  447. package/src/chores/stack.ts +27 -118
  448. package/src/chores/verdict.test.ts +101 -80
  449. package/src/chores/verdict.ts +54 -99
  450. package/src/contracts/accounts.contract.ts +4 -19
  451. package/src/contracts/agent.contract.ts +7 -22
  452. package/src/contracts/agents.contract.ts +25 -71
  453. package/src/contracts/automations.contract.ts +14 -30
  454. package/src/contracts/capabilities.contract.ts +8 -35
  455. package/src/contracts/chores.contract.ts +4 -15
  456. package/src/contracts/endpoints.contract.ts +5 -22
  457. package/src/contracts/exit.contract.ts +7 -30
  458. package/src/contracts/extensions.contract.ts +8 -30
  459. package/src/contracts/git.contract.ts +25 -49
  460. package/src/contracts/host.contract.ts +9 -44
  461. package/src/contracts/intentic.contract.ts +1 -1
  462. package/src/contracts/logs.contract.ts +3 -13
  463. package/src/contracts/loops.contract.ts +9 -40
  464. package/src/contracts/personas.contract.ts +22 -30
  465. package/src/contracts/providers.contract.ts +4 -17
  466. package/src/contracts/runner.contract.ts +12 -39
  467. package/src/contracts/safety.contract.ts +5 -14
  468. package/src/contracts/secrets.contract.ts +4 -22
  469. package/src/contracts/sessions.contract.ts +1 -1
  470. package/src/contracts/settings.contract.ts +4 -10
  471. package/src/contracts/share.contract.ts +3 -9
  472. package/src/contracts/skills.contract.ts +4 -15
  473. package/src/contracts/system.contract.ts +26 -52
  474. package/src/contracts/usage.contract.ts +7 -21
  475. package/src/contracts/vpn.contract.ts +9 -16
  476. package/src/contracts/webext.contract.ts +9 -25
  477. package/src/contracts/workflows.contract.ts +34 -58
  478. package/src/contracts/workspace.contract.ts +19 -50
  479. package/src/events/agent-events.ts +345 -0
  480. package/src/events/cards.ts +286 -0
  481. package/src/{events.test.ts → events/resume.test.ts} +4 -19
  482. package/src/events/resume.ts +60 -0
  483. package/src/events/system-events.ts +139 -0
  484. package/src/events/transcript.ts +320 -0
  485. package/src/{conversation-ids.test.ts → ids/conversation-ids.test.ts} +25 -13
  486. package/src/ids/conversation-ids.ts +178 -0
  487. package/src/ids/hostnames.ts +129 -0
  488. package/src/ids/session-names.ts +29 -0
  489. package/src/ids/share-paths.ts +43 -0
  490. package/src/{tunnel-ids.test.ts → ids/tunnel-ids.test.ts} +1 -14
  491. package/src/ids/tunnel-ids.ts +29 -0
  492. package/src/index.ts +71 -88
  493. package/src/{agent-catalog.test.ts → models/agent-catalog.test.ts} +19 -93
  494. package/src/models/agent-catalog.ts +174 -0
  495. package/src/models/agent-runtimes.ts +201 -0
  496. package/src/models/capability-ledger.test.ts +84 -0
  497. package/src/{fast-tier.test.ts → models/fast-tier.test.ts} +7 -18
  498. package/src/models/fast-tier.ts +36 -0
  499. package/src/{model-order.test.ts → models/model-order.test.ts} +19 -40
  500. package/src/models/model-order.ts +163 -0
  501. package/src/models/model-pins.test.ts +87 -0
  502. package/src/models/model-pins.ts +57 -0
  503. package/src/models/model-roles.test.ts +38 -0
  504. package/src/models/model-roles.ts +180 -0
  505. package/src/{plan-pools.test.ts → models/plan-pools.test.ts} +6 -11
  506. package/src/models/plan-pools.ts +65 -0
  507. package/src/{prompt-complexity.test.ts → models/prompt-complexity.test.ts} +8 -60
  508. package/src/models/prompt-complexity.ts +215 -0
  509. package/src/{provider-specs.test.ts → models/provider-specs.test.ts} +18 -47
  510. package/src/models/provider-specs.ts +272 -0
  511. package/src/policy/approvals-execution.ts +58 -0
  512. package/src/{batch-runs.test.ts → policy/batch-runs.test.ts} +4 -20
  513. package/src/policy/batch-runs.ts +137 -0
  514. package/src/policy/capability-secrets.ts +5 -0
  515. package/src/{card-status.ts → policy/card-status.ts} +11 -26
  516. package/src/{command-classes.test.ts → policy/command-classes.test.ts} +11 -118
  517. package/src/policy/command-classes.ts +395 -0
  518. package/src/policy/command-run.ts +65 -0
  519. package/src/policy/control-scopes.ts +38 -0
  520. package/src/{credential-material.test.ts → policy/credential-material.test.ts} +4 -26
  521. package/src/policy/credential-material.ts +91 -0
  522. package/src/policy/needs-action.ts +5 -0
  523. package/src/policy/output-fields.ts +86 -0
  524. package/src/policy/overlay-lint.ts +100 -0
  525. package/src/{owner-ticket.test.ts → policy/owner-ticket.test.ts} +1 -1
  526. package/src/{owner-ticket.ts → policy/owner-ticket.ts} +9 -31
  527. package/src/policy/safety-policy.test.ts +84 -0
  528. package/src/policy/safety-policy.ts +142 -0
  529. package/src/policy/search-globs.ts +60 -0
  530. package/src/protocol/container-requirements.test.ts +72 -0
  531. package/src/protocol/container-requirements.ts +58 -0
  532. package/src/protocol/host-protocol.ts +29 -0
  533. package/src/protocol/ingress-contract.ts +103 -0
  534. package/src/{ingress-protocol.test.ts → protocol/ingress-protocol.test.ts} +25 -90
  535. package/src/protocol/ingress-protocol.ts +441 -0
  536. package/src/protocol/listener-protocol.ts +75 -0
  537. package/src/{peer-dial.test.ts → protocol/peer-dial.test.ts} +52 -1
  538. package/src/protocol/peer-dial.ts +207 -0
  539. package/src/{peer-mcp-server.ts → protocol/peer-mcp-server.ts} +16 -41
  540. package/src/protocol/request-id.ts +5 -0
  541. package/src/{routes.test.ts → protocol/routes.test.ts} +14 -22
  542. package/src/protocol/routes.ts +159 -0
  543. package/src/protocol/runner-protocol.ts +197 -0
  544. package/src/protocol/terminal-protocol.ts +13 -0
  545. package/src/protocol/webext-links.ts +50 -0
  546. package/src/protocol/webext-protocol.ts +24 -0
  547. package/src/schemas/activity.ts +22 -30
  548. package/src/schemas/agent.ts +130 -238
  549. package/src/schemas/agents.ts +165 -483
  550. package/src/schemas/approvals.ts +23 -71
  551. package/src/schemas/automations.ts +145 -249
  552. package/src/schemas/capabilities.ts +127 -414
  553. package/src/schemas/ci.ts +56 -108
  554. package/src/schemas/codebase-health.ts +7 -11
  555. package/src/schemas/devices.ts +98 -321
  556. package/src/schemas/engines.ts +16 -44
  557. package/src/schemas/environment.ts +41 -91
  558. package/src/schemas/exit.ts +24 -81
  559. package/src/schemas/extension-updates.ts +42 -87
  560. package/src/schemas/git-history.ts +30 -84
  561. package/src/schemas/git.ts +141 -256
  562. package/src/schemas/history.ts +14 -40
  563. package/src/schemas/hosts.ts +9 -15
  564. package/src/schemas/inventory.ts +8 -10
  565. package/src/schemas/issues.ts +66 -134
  566. package/src/schemas/logs.ts +15 -34
  567. package/src/schemas/loops.ts +46 -163
  568. package/src/schemas/maintenance.ts +47 -172
  569. package/src/schemas/panels.ts +20 -45
  570. package/src/schemas/personas.ts +85 -168
  571. package/src/schemas/plan-limits.ts +58 -197
  572. package/src/schemas/ports.ts +14 -32
  573. package/src/schemas/provider-oauth.ts +23 -76
  574. package/src/schemas/provider-subscriptions.ts +4 -13
  575. package/src/schemas/public.ts +4 -15
  576. package/src/schemas/push.ts +8 -36
  577. package/src/schemas/secrets.ts +20 -64
  578. package/src/schemas/settings.ts +177 -597
  579. package/src/schemas/share.ts +14 -32
  580. package/src/schemas/shared.ts +14 -13
  581. package/src/schemas/system.ts +46 -78
  582. package/src/schemas/terminal.ts +41 -120
  583. package/src/schemas/usage.ts +44 -233
  584. package/src/schemas/version-seam.test.ts +9 -26
  585. package/src/schemas/vpn.ts +27 -64
  586. package/src/schemas/webext.ts +22 -53
  587. package/src/schemas/workflows.ts +57 -183
  588. package/src/schemas/workspace-repos.ts +12 -24
  589. package/src/schemas/workspace-search.ts +17 -35
  590. package/src/schemas/workspace-setup.ts +4 -11
  591. package/src/schemas/workspace-tree.ts +32 -91
  592. package/src/state/arrival.ts +109 -0
  593. package/src/state/contract-lock.test.ts +17 -0
  594. package/src/state/contract-lock.ts +49 -0
  595. package/src/state/definition.ts +143 -0
  596. package/src/state/history-state.ts +103 -0
  597. package/src/{runtime-state.test.ts → state/runtime-state.test.ts} +3 -10
  598. package/src/state/runtime-state.ts +62 -0
  599. package/src/state/starter.ts +5 -0
  600. package/src/state/state-portability.ts +27 -0
  601. package/src/state/versions.ts +26 -0
  602. package/src/{workspace-state.test.ts → state/workspace-state.test.ts} +65 -173
  603. package/src/state/workspace-state.ts +669 -0
  604. package/src/{documents.test.ts → text/documents.test.ts} +1 -1
  605. package/src/text/documents.ts +42 -0
  606. package/src/text/embed.ts +132 -0
  607. package/src/text/mentions.ts +21 -0
  608. package/src/text/model-answer.ts +9 -0
  609. package/src/text/path-refs.ts +39 -0
  610. package/src/text/shell-regions.ts +224 -0
  611. package/src/{title.test.ts → text/title.test.ts} +13 -32
  612. package/src/text/title.ts +202 -0
  613. package/src/{transcript-fold.test.ts → text/transcript-fold.test.ts} +5 -83
  614. package/src/{transcript-fold.ts → text/transcript-fold.ts} +65 -170
  615. package/src/text/whisper.test.ts +19 -0
  616. package/src/text/whisper.ts +17 -0
  617. package/src/{workflow-faults.test.ts → text/workflow-faults.test.ts} +6 -24
  618. package/src/{workflow-faults.ts → text/workflow-faults.ts} +20 -60
  619. package/dist/agent-catalog.d.ts.map +0 -1
  620. package/dist/agent-catalog.js.map +0 -1
  621. package/dist/agent-runtimes.d.ts.map +0 -1
  622. package/dist/agent-runtimes.js.map +0 -1
  623. package/dist/approvals-execution.d.ts.map +0 -1
  624. package/dist/approvals-execution.js.map +0 -1
  625. package/dist/arrival.d.ts.map +0 -1
  626. package/dist/arrival.js.map +0 -1
  627. package/dist/batch-runs.d.ts.map +0 -1
  628. package/dist/batch-runs.js.map +0 -1
  629. package/dist/capability-env.d.ts.map +0 -1
  630. package/dist/capability-env.js.map +0 -1
  631. package/dist/capability-secrets.d.ts.map +0 -1
  632. package/dist/capability-secrets.js.map +0 -1
  633. package/dist/card-status.d.ts.map +0 -1
  634. package/dist/card-status.js.map +0 -1
  635. package/dist/command-classes.d.ts.map +0 -1
  636. package/dist/command-classes.js.map +0 -1
  637. package/dist/command-run.d.ts.map +0 -1
  638. package/dist/command-run.js.map +0 -1
  639. package/dist/contract-lock.d.ts.map +0 -1
  640. package/dist/contract-lock.js.map +0 -1
  641. package/dist/conversation-ids.d.ts +0 -4
  642. package/dist/conversation-ids.d.ts.map +0 -1
  643. package/dist/conversation-ids.js.map +0 -1
  644. package/dist/credential-material.d.ts.map +0 -1
  645. package/dist/credential-material.js.map +0 -1
  646. package/dist/definition.d.ts.map +0 -1
  647. package/dist/definition.js.map +0 -1
  648. package/dist/documents.d.ts.map +0 -1
  649. package/dist/documents.js.map +0 -1
  650. package/dist/embed.d.ts.map +0 -1
  651. package/dist/embed.js.map +0 -1
  652. package/dist/events.d.ts +0 -4336
  653. package/dist/events.d.ts.map +0 -1
  654. package/dist/events.js +0 -725
  655. package/dist/events.js.map +0 -1
  656. package/dist/fast-tier.d.ts.map +0 -1
  657. package/dist/fast-tier.js.map +0 -1
  658. package/dist/history-state.d.ts.map +0 -1
  659. package/dist/history-state.js.map +0 -1
  660. package/dist/host-protocol.d.ts.map +0 -1
  661. package/dist/host-protocol.js.map +0 -1
  662. package/dist/hostnames.d.ts.map +0 -1
  663. package/dist/hostnames.js.map +0 -1
  664. package/dist/ingress-contract.d.ts.map +0 -1
  665. package/dist/ingress-contract.js.map +0 -1
  666. package/dist/ingress-protocol.d.ts.map +0 -1
  667. package/dist/ingress-protocol.js.map +0 -1
  668. package/dist/listener-protocol.d.ts.map +0 -1
  669. package/dist/listener-protocol.js.map +0 -1
  670. package/dist/mentions.d.ts.map +0 -1
  671. package/dist/mentions.js.map +0 -1
  672. package/dist/model-order.d.ts.map +0 -1
  673. package/dist/model-order.js.map +0 -1
  674. package/dist/model-pins.d.ts.map +0 -1
  675. package/dist/model-pins.js +0 -45
  676. package/dist/model-pins.js.map +0 -1
  677. package/dist/model-roles.d.ts.map +0 -1
  678. package/dist/model-roles.js.map +0 -1
  679. package/dist/needs-action.d.ts.map +0 -1
  680. package/dist/needs-action.js.map +0 -1
  681. package/dist/output-fields.d.ts.map +0 -1
  682. package/dist/output-fields.js.map +0 -1
  683. package/dist/overlay-lint.d.ts.map +0 -1
  684. package/dist/overlay-lint.js.map +0 -1
  685. package/dist/owner-ticket.d.ts.map +0 -1
  686. package/dist/owner-ticket.js.map +0 -1
  687. package/dist/path-refs.d.ts.map +0 -1
  688. package/dist/path-refs.js.map +0 -1
  689. package/dist/peer-dial.d.ts.map +0 -1
  690. package/dist/peer-dial.js.map +0 -1
  691. package/dist/peer-mcp-server.d.ts.map +0 -1
  692. package/dist/peer-mcp-server.js.map +0 -1
  693. package/dist/plan-pools.d.ts.map +0 -1
  694. package/dist/plan-pools.js.map +0 -1
  695. package/dist/prompt-complexity.d.ts.map +0 -1
  696. package/dist/prompt-complexity.js.map +0 -1
  697. package/dist/provider-specs.d.ts.map +0 -1
  698. package/dist/provider-specs.js.map +0 -1
  699. package/dist/request-id.d.ts.map +0 -1
  700. package/dist/request-id.js.map +0 -1
  701. package/dist/routes.d.ts.map +0 -1
  702. package/dist/routes.js.map +0 -1
  703. package/dist/runner-protocol.d.ts.map +0 -1
  704. package/dist/runner-protocol.js.map +0 -1
  705. package/dist/runtime-state.d.ts.map +0 -1
  706. package/dist/runtime-state.js.map +0 -1
  707. package/dist/safety-policy.d.ts.map +0 -1
  708. package/dist/safety-policy.js.map +0 -1
  709. package/dist/schemas/context.d.ts +0 -30
  710. package/dist/schemas/context.d.ts.map +0 -1
  711. package/dist/schemas/context.js +0 -34
  712. package/dist/schemas/context.js.map +0 -1
  713. package/dist/search-globs.d.ts.map +0 -1
  714. package/dist/search-globs.js.map +0 -1
  715. package/dist/session-names.d.ts.map +0 -1
  716. package/dist/session-names.js.map +0 -1
  717. package/dist/share-paths.d.ts.map +0 -1
  718. package/dist/share-paths.js.map +0 -1
  719. package/dist/shell-regions.d.ts.map +0 -1
  720. package/dist/shell-regions.js.map +0 -1
  721. package/dist/sse.d.ts.map +0 -1
  722. package/dist/sse.js.map +0 -1
  723. package/dist/starter.d.ts.map +0 -1
  724. package/dist/starter.js.map +0 -1
  725. package/dist/state-portability.d.ts.map +0 -1
  726. package/dist/state-portability.js.map +0 -1
  727. package/dist/terminal-protocol.d.ts.map +0 -1
  728. package/dist/terminal-protocol.js.map +0 -1
  729. package/dist/title.d.ts.map +0 -1
  730. package/dist/title.js.map +0 -1
  731. package/dist/transcript-fold.d.ts.map +0 -1
  732. package/dist/transcript-fold.js.map +0 -1
  733. package/dist/tunnel-ids.d.ts.map +0 -1
  734. package/dist/tunnel-ids.js.map +0 -1
  735. package/dist/versions.d.ts.map +0 -1
  736. package/dist/versions.js.map +0 -1
  737. package/dist/webext-links.d.ts.map +0 -1
  738. package/dist/webext-links.js.map +0 -1
  739. package/dist/webext-protocol.d.ts.map +0 -1
  740. package/dist/webext-protocol.js.map +0 -1
  741. package/dist/workflow-faults.d.ts.map +0 -1
  742. package/dist/workflow-faults.js.map +0 -1
  743. package/dist/workspace-state.d.ts.map +0 -1
  744. package/dist/workspace-state.js.map +0 -1
  745. package/src/agent-catalog.ts +0 -316
  746. package/src/agent-runtimes.ts +0 -419
  747. package/src/approvals-execution.ts +0 -96
  748. package/src/arrival.ts +0 -160
  749. package/src/batch-runs.ts +0 -188
  750. package/src/capability-ledger.test.ts +0 -140
  751. package/src/capability-secrets.ts +0 -20
  752. package/src/command-classes.ts +0 -617
  753. package/src/command-run.ts +0 -78
  754. package/src/contract-lock.test.ts +0 -23
  755. package/src/contract-lock.ts +0 -66
  756. package/src/conversation-ids.ts +0 -207
  757. package/src/credential-material.ts +0 -181
  758. package/src/definition.ts +0 -207
  759. package/src/documents.ts +0 -67
  760. package/src/embed.ts +0 -164
  761. package/src/events.ts +0 -1870
  762. package/src/fast-tier.ts +0 -72
  763. package/src/history-state.ts +0 -174
  764. package/src/host-protocol.ts +0 -36
  765. package/src/hostnames.ts +0 -180
  766. package/src/ingress-contract.ts +0 -157
  767. package/src/ingress-protocol.ts +0 -625
  768. package/src/listener-protocol.ts +0 -96
  769. package/src/mentions.ts +0 -25
  770. package/src/model-order.ts +0 -262
  771. package/src/model-pins.test.ts +0 -202
  772. package/src/model-pins.ts +0 -183
  773. package/src/model-roles.ts +0 -224
  774. package/src/needs-action.ts +0 -14
  775. package/src/output-fields.ts +0 -111
  776. package/src/overlay-lint.ts +0 -116
  777. package/src/path-refs.ts +0 -59
  778. package/src/peer-dial.ts +0 -163
  779. package/src/plan-pools.ts +0 -92
  780. package/src/prompt-complexity.ts +0 -334
  781. package/src/provider-specs.ts +0 -432
  782. package/src/request-id.ts +0 -41
  783. package/src/routes.ts +0 -219
  784. package/src/runner-protocol.ts +0 -239
  785. package/src/runtime-state.ts +0 -140
  786. package/src/safety-policy.test.ts +0 -88
  787. package/src/safety-policy.ts +0 -258
  788. package/src/schemas/context.ts +0 -87
  789. package/src/search-globs.ts +0 -76
  790. package/src/session-names.ts +0 -44
  791. package/src/share-paths.ts +0 -68
  792. package/src/shell-regions.ts +0 -289
  793. package/src/starter.ts +0 -13
  794. package/src/state-portability.ts +0 -56
  795. package/src/terminal-protocol.ts +0 -16
  796. package/src/title.ts +0 -267
  797. package/src/tunnel-ids.ts +0 -57
  798. package/src/versions.ts +0 -48
  799. package/src/webext-links.ts +0 -90
  800. package/src/webext-protocol.ts +0 -27
  801. package/src/workspace-state.ts +0 -1096
  802. /package/dist/{hostnames.d.ts → ids/hostnames.d.ts} +0 -0
  803. /package/dist/{hostnames.js → ids/hostnames.js} +0 -0
  804. /package/dist/{session-names.d.ts → ids/session-names.d.ts} +0 -0
  805. /package/dist/{session-names.js → ids/session-names.js} +0 -0
  806. /package/dist/{share-paths.d.ts → ids/share-paths.d.ts} +0 -0
  807. /package/dist/{share-paths.js → ids/share-paths.js} +0 -0
  808. /package/dist/{agent-catalog.js → models/agent-catalog.js} +0 -0
  809. /package/dist/{agent-runtimes.d.ts → models/agent-runtimes.d.ts} +0 -0
  810. /package/dist/{agent-runtimes.js → models/agent-runtimes.js} +0 -0
  811. /package/dist/{fast-tier.js → models/fast-tier.js} +0 -0
  812. /package/dist/{model-order.d.ts → models/model-order.d.ts} +0 -0
  813. /package/dist/{model-order.js → models/model-order.js} +0 -0
  814. /package/dist/{plan-pools.js → models/plan-pools.js} +0 -0
  815. /package/dist/{prompt-complexity.d.ts → models/prompt-complexity.d.ts} +0 -0
  816. /package/dist/{prompt-complexity.js → models/prompt-complexity.js} +0 -0
  817. /package/dist/{provider-specs.d.ts → models/provider-specs.d.ts} +0 -0
  818. /package/dist/{provider-specs.js → models/provider-specs.js} +0 -0
  819. /package/dist/{approvals-execution.d.ts → policy/approvals-execution.d.ts} +0 -0
  820. /package/dist/{approvals-execution.js → policy/approvals-execution.js} +0 -0
  821. /package/dist/{capability-env.d.ts → policy/capability-env.d.ts} +0 -0
  822. /package/dist/{capability-env.js → policy/capability-env.js} +0 -0
  823. /package/dist/{capability-secrets.d.ts → policy/capability-secrets.d.ts} +0 -0
  824. /package/dist/{capability-secrets.js → policy/capability-secrets.js} +0 -0
  825. /package/dist/{command-run.js → policy/command-run.js} +0 -0
  826. /package/dist/{credential-material.d.ts → policy/credential-material.d.ts} +0 -0
  827. /package/dist/{credential-material.js → policy/credential-material.js} +0 -0
  828. /package/dist/{needs-action.d.ts → policy/needs-action.d.ts} +0 -0
  829. /package/dist/{needs-action.js → policy/needs-action.js} +0 -0
  830. /package/dist/{output-fields.d.ts → policy/output-fields.d.ts} +0 -0
  831. /package/dist/{output-fields.js → policy/output-fields.js} +0 -0
  832. /package/dist/{overlay-lint.d.ts → policy/overlay-lint.d.ts} +0 -0
  833. /package/dist/{overlay-lint.js → policy/overlay-lint.js} +0 -0
  834. /package/dist/{owner-ticket.d.ts → policy/owner-ticket.d.ts} +0 -0
  835. /package/dist/{search-globs.d.ts → policy/search-globs.d.ts} +0 -0
  836. /package/dist/{search-globs.js → policy/search-globs.js} +0 -0
  837. /package/dist/{ingress-contract.d.ts → protocol/ingress-contract.d.ts} +0 -0
  838. /package/dist/{ingress-protocol.d.ts → protocol/ingress-protocol.d.ts} +0 -0
  839. /package/dist/{ingress-protocol.js → protocol/ingress-protocol.js} +0 -0
  840. /package/dist/{listener-protocol.d.ts → protocol/listener-protocol.d.ts} +0 -0
  841. /package/dist/{peer-mcp-server.d.ts → protocol/peer-mcp-server.d.ts} +0 -0
  842. /package/dist/{peer-mcp-server.js → protocol/peer-mcp-server.js} +0 -0
  843. /package/dist/{request-id.d.ts → protocol/request-id.d.ts} +0 -0
  844. /package/dist/{request-id.js → protocol/request-id.js} +0 -0
  845. /package/dist/{routes.d.ts → protocol/routes.d.ts} +0 -0
  846. /package/dist/{routes.js → protocol/routes.js} +0 -0
  847. /package/dist/{sse.d.ts → protocol/sse.d.ts} +0 -0
  848. /package/dist/{sse.js → protocol/sse.js} +0 -0
  849. /package/dist/{terminal-protocol.js → protocol/terminal-protocol.js} +0 -0
  850. /package/dist/{webext-links.d.ts → protocol/webext-links.d.ts} +0 -0
  851. /package/dist/{webext-links.js → protocol/webext-links.js} +0 -0
  852. /package/dist/{arrival.d.ts → state/arrival.d.ts} +0 -0
  853. /package/dist/{contract-lock.d.ts → state/contract-lock.d.ts} +0 -0
  854. /package/dist/{history-state.d.ts → state/history-state.d.ts} +0 -0
  855. /package/dist/{runtime-state.d.ts → state/runtime-state.d.ts} +0 -0
  856. /package/dist/{runtime-state.js → state/runtime-state.js} +0 -0
  857. /package/dist/{starter.d.ts → state/starter.d.ts} +0 -0
  858. /package/dist/{starter.js → state/starter.js} +0 -0
  859. /package/dist/{state-portability.d.ts → state/state-portability.d.ts} +0 -0
  860. /package/dist/{state-portability.js → state/state-portability.js} +0 -0
  861. /package/dist/{versions.d.ts → state/versions.d.ts} +0 -0
  862. /package/dist/{versions.js → state/versions.js} +0 -0
  863. /package/dist/{embed.d.ts → text/embed.d.ts} +0 -0
  864. /package/dist/{embed.js → text/embed.js} +0 -0
  865. /package/dist/{mentions.d.ts → text/mentions.d.ts} +0 -0
  866. /package/dist/{mentions.js → text/mentions.js} +0 -0
  867. /package/dist/{path-refs.d.ts → text/path-refs.d.ts} +0 -0
  868. /package/dist/{path-refs.js → text/path-refs.js} +0 -0
  869. /package/dist/{shell-regions.js → text/shell-regions.js} +0 -0
  870. /package/dist/{title.d.ts → text/title.d.ts} +0 -0
  871. /package/dist/{title.js → text/title.js} +0 -0
  872. /package/src/{hostnames.test.ts → ids/hostnames.test.ts} +0 -0
  873. /package/src/{share-paths.test.ts → ids/share-paths.test.ts} +0 -0
  874. /package/src/{capability-env.ts → policy/capability-env.ts} +0 -0
  875. /package/src/{overlay-lint.test.ts → policy/overlay-lint.test.ts} +0 -0
  876. /package/src/{search-globs.test.ts → policy/search-globs.test.ts} +0 -0
  877. /package/src/{ingress-contract.test.ts → protocol/ingress-contract.test.ts} +0 -0
  878. /package/src/{peer-mcp-server.test.ts → protocol/peer-mcp-server.test.ts} +0 -0
  879. /package/src/{sse.ts → protocol/sse.ts} +0 -0
  880. /package/src/{versions.test.ts → state/versions.test.ts} +0 -0
  881. /package/src/{embed.test.ts → text/embed.test.ts} +0 -0
  882. /package/src/{mentions.test.ts → text/mentions.test.ts} +0 -0
  883. /package/src/{path-refs.test.ts → text/path-refs.test.ts} +0 -0
package/src/events.ts DELETED
@@ -1,1870 +0,0 @@
1
- import { z } from "zod";
2
- import { AgentHarnessSchema, AgentProviderSchema, PermissionModeSchema } from "./schemas/agent.js";
3
- import { AgentSummarySchema, LandConflictSchema } from "./schemas/agents.js";
4
- import { RateLimitInfoSchema } from "./schemas/claude-gate.js";
5
- import { FastModeStateSchema } from "./schemas/fast-mode.js";
6
- import { AccountUsageSchema, AgentReplySchema, ProviderRefusalSchema, UsageWindowSchema } from "./schemas/plan-limits.js";
7
- import { CredentialGateKindSchema, CredentialGateScopeSchema, CredentialLaneSchema } from "./schemas/secrets.js";
8
- import { ShareDetailSchema } from "./schemas/share.js";
9
- import { MemberRoleSchema } from "./schemas/shared.js";
10
- import { SubagentKindSchema, SubagentStatusSchema, SubagentVerificationSchema } from "./schemas/terminal.js";
11
-
12
- // The wire shapes streamed from the daemon's event-iterator procedures. This is their canonical home: the
13
- // daemon yields them and the browser client consumes them from the same schema, so the two can't drift (they
14
- // used to be hand-duplicated across repos). Schemas, not bare types, because oRPC's `eventIterator(...)`
15
- // validates each frame against them.
16
-
17
- // One interactive question the agent asks via the `ask` tool (mirrors AskUserQuestion's input shape).
18
- export const AskOptionSchema = z.object({
19
- label: z.string().describe("The choice, in a few words."),
20
- description: z.string().describe("What picking it means."),
21
- preview: z.string().optional().describe("Something to look at while deciding: a mock-up, a snippet, a layout."),
22
- });
23
- export type AskOption = z.infer<typeof AskOptionSchema>;
24
-
25
- export const AskQuestionSchema = z.object({
26
- question: z.string().describe("What the agent is asking."),
27
- header: z.string().describe("A short label for the question."),
28
- multiSelect: z.boolean().describe("Whether more than one answer can be picked."),
29
- options: z.array(AskOptionSchema).describe("The choices offered. A free-text answer is always possible as well."),
30
- });
31
- export type AskQuestion = z.infer<typeof AskQuestionSchema>;
32
-
33
- /* THE PROGRAM A COMMAND CARD IS HOLDING, as the thing it is rather than as prose about it.
34
- *
35
- * It used to ride in `description`, the field every other permission ask fills with a sentence, which left the
36
- * card with no way to know it was holding four hundred characters of shell: it rendered them as a paragraph,
37
- * wrapped mid-flag, and the fragment that caused the hold was somewhere in the middle of it.
38
- *
39
- * `spans` is where the pattern match fired, computed by the classifier at the moment it did (contract's
40
- * command-classes.ts, matchCommand) and carried rather than re-derived: a browser that re-ran the patterns
41
- * would be a second classifier, and the day the two disagreed the card would be marking a fragment the daemon
42
- * never saw. Offsets are into `text` AFTER truncation, so they are always paintable.
43
- *
44
- * IT IS NOT A CLAIM ABOUT WHY THE CARD EXISTS, and the card no longer presents it as one. The reason is the
45
- * judge's sentence in the title; these are the fragments TRIAGE noticed, all of the matched classes' rather
46
- * than whichever sorts first — the card used to show one class's and label them "Stopped for", so a command
47
- * that cleaned a build directory on its way to publishing offered `rm -rf …` as its reason under a sentence
48
- * about npm. Under the hard rule the title DOES name a class, so there the marks are that class's alone.
49
- *
50
- * `language` is a Shiki grammar id, and the two are the two execution backends the gate reads (command-gate's
51
- * EXECUTION_SOURCES): a shell line and a script. */
52
- export const ProgramAskSchema = z.object({
53
- text: z.string().describe("What would run."),
54
- language: z.enum(["bash", "javascript"]).describe("Which of the two backends it is written for, named as the grammar that colours it."),
55
- truncated: z
56
- .boolean()
57
- .describe(
58
- "Whether this is an excerpt of a longer program, so the card can say so instead of ending mid-word. An excerpt always carries the flagged fragment: the beginning, then a window around the fragment, with any skipped middle written into the text as a bracketed count.",
59
- ),
60
- spans: z
61
- .array(z.object({ start: z.number().int().nonnegative(), end: z.number().int().nonnegative() }))
62
- .describe(
63
- "Which fragments of the text the pattern match fired on: every matched class's, or, under the hard rule, only the class the title names. Offsets into text, in order, never overlapping.",
64
- ),
65
- });
66
- export type ProgramAsk = z.infer<typeof ProgramAskSchema>;
67
-
68
- // One per-tool permission prompt (the SDK's canUseTool callback, surfaced as a card). The daemon passes the
69
- // bridge's own rendered strings through rather than re-deriving them, so the prompt reads exactly as Claude
70
- // Code words it. `alwaysLabel` is present only when the SDK offered rules to persist, without it the card
71
- // shows allow-once / deny alone, because there is nothing an "always" answer could remember.
72
- export const PermissionAskSchema = z.object({
73
- toolName: z.string().describe("Which tool it wants to use."),
74
- // "Claude wants to read foo.txt", the full prompt sentence, when the bridge rendered one.
75
- title: z.string().optional().describe("The whole question, as a sentence, exactly as the runtime words it."),
76
- // Short noun phrase for the allow button ("Read file").
77
- displayName: z.string().optional().describe("A short phrase for the button, such as read file."),
78
- description: z.string().optional().describe("More about what it is asking for."),
79
- // Why the prompt fired ('rule' | 'mode' | 'classifier' | …), shown as the card's muted subline.
80
- reason: z.string().optional().describe("Why it is asking at all: a rule, the current mode, something that looked risky."),
81
- // The file the request is about, when it is about one (workspace-root-relative).
82
- path: z.string().optional().describe("Which file it concerns, when it concerns one."),
83
- alwaysLabel: z
84
- .string()
85
- .optional()
86
- .describe(
87
- "The wording for an always-allow answer. Present only when there is something an always could actually remember; without it the only answers are once and no.",
88
- ),
89
- program: ProgramAskSchema.optional().describe(
90
- "The program this card is holding, when the card is about one. Present on a command gate's card and absent on every other permission ask.",
91
- ),
92
- /* THE JUDGE'S OWN SENTENCE, WHERE THE TITLE IS SOMEBODY ELSE'S. On an ordinary command card the sentence IS
93
- * the title (the judge read the owner's policy and the program, and its account of why this needs asking is
94
- * the only account there is), so this is left off rather than printing the same words twice. It carries the
95
- * sentence on the two cards whose title says something the sentence cannot: the hard rule's, which names the
96
- * consequence that stopped it, and a machine command's, which names the device.
97
- *
98
- * Written by the quick model from the program text and the policy, never by the agent being gated — a card
99
- * whose persuasive half was authored by the thing it is stopping argues for its own approval, and the turns
100
- * that raise cards are exactly the ones whose account of themselves may be a stranger's. */
101
- explain: z
102
- .string()
103
- .optional()
104
- .describe(
105
- "One plain sentence saying what the program does and why it is being asked about, where the title says something else. Written by the judge that read your safety policy, never by the agent being gated.",
106
- ),
107
- });
108
- export type PermissionAsk = z.infer<typeof PermissionAskSchema>;
109
-
110
- /* ONE PRICED SERVICE RUN, OFFERED, the card the daemon raises when the agent asks to run a premium service
111
- * (platform/service-offer.ts). Everything with a number on it is the PLATFORM's answer, relayed verbatim from
112
- * the catalog it serves the daemon: the model that asked contributes `request` (the JSON it wants sent) and
113
- * `why` (its one line of rationale), and nothing else, which is what makes the price on the card impossible
114
- * to misquote, and the click on it the only way the run can happen. */
115
- export const ServiceOfferSchema = z.object({
116
- // The service, as the platform lists it: `<slug>` is what the run names, the rest is the catalog row.
117
- slug: z.string().describe("Which service."),
118
- name: z.string().describe("What it is called."),
119
- publisher: z.string().describe("Who runs it."),
120
- description: z.string().describe("What it does."),
121
- creditsPerRun: z
122
- .number()
123
- .describe(
124
- "What one run costs. Stated by the platform rather than by the agent asking, which is what makes the price impossible to misquote.",
125
- ),
126
- /* Whether the platform still has this listing on probation, a new provider that passed admission's
127
- * mechanical gates but has not yet served enough runs cleanly to graduate. It rides the card because
128
- * probation is the honest form of "listed automatically, not vouched for": the member approving the
129
- * spend is the person who should know that, and the platform is the only party that can say it. */
130
- probation: z
131
- .boolean()
132
- .optional()
133
- .describe(
134
- "The listing is new and has not yet served enough runs cleanly to be trusted. The honest form of listed automatically but not vouched for, and the person approving the spend is who should know it.",
135
- ),
136
- // The owner's meter as the platform stated it with the catalog, what "N left today" renders from. Absent
137
- // when the platform sent none (it answers a meter only to a member, and membership was already checked
138
- // before this card went up, so in practice it is present; the field stays honest about the wire).
139
- credits: z
140
- .object({
141
- allowance: z.number().describe("How many credits the period gives."),
142
- remaining: z.number().describe("How many are left."),
143
- resetsAt: z.string().describe("When they refill."),
144
- })
145
- .optional()
146
- .describe("Your own meter, as the platform stated it."),
147
- // The request body the agent wants forwarded, verbatim, shown so the owner can see what leaves.
148
- request: z.string().describe("Exactly what would be sent, so you can see what leaves before agreeing to it."),
149
- // The agent's one-line case for spending, the only prose on the card that is the model's.
150
- why: z.string().optional().describe("The agent's case for spending, and the only words on this card that are the agent's."),
151
- });
152
- export type ServiceOffer = z.infer<typeof ServiceOfferSchema>;
153
-
154
- /* WHAT A SERVICE STREAMS, the provider's event vocabulary, stated once here and imported by everyone who
155
- * touches it: the platform validates each line of a provider's NDJSON against this before relaying it, the
156
- * daemon turns `status` events into transcript frames, and the editor renders them under the offer card.
157
- * A run is `status` lines (each replaces the last, a spinner label, not a log) ending in exactly one
158
- * `result`, whose `data` is the answer the agent acts on. The union is where future event kinds land when
159
- * services start streaming richer transcript elements; today's two are the smallest honest set. */
160
- export const ServiceStreamEventSchema = z.discriminatedUnion(`event`, [
161
- z.object({
162
- event: z.literal(`status`).describe("Progress. Each one replaces the last: a label, not a log."),
163
- text: z.string().describe("What it is doing."),
164
- }),
165
- z.object({
166
- event: z.literal(`result`).describe("The answer. Exactly one of these ends a run."),
167
- data: z.unknown().describe("The answer itself, in whatever shape that service returns."),
168
- }),
169
- ]);
170
- export type ServiceStreamEvent = z.infer<typeof ServiceStreamEventSchema>;
171
-
172
- /* ONE MISSING CAPABILITY, ASKED FOR, the card the daemon raises when the agent hits something this sandbox
173
- * is not connected to (capabilities/capability-offer.ts). `card` names the catalog card and `name` is that
174
- * card's own title, both resolved by the daemon from the catalog it validates the ask against, the model
175
- * that asked contributes `why` (its one line of rationale) and nothing else, which is what makes the card
176
- * impossible to misrepresent, and the click on it the only way anything gets connected. */
177
- export const CapabilityOfferSchema = z.object({
178
- // The catalog card being asked for, and how the catalog itself titles it ("Notion", "GitHub", "Docker").
179
- card: z.string().describe("Which connection is being asked for."),
180
- name: z.string().describe("What it is called, as the catalogue titles it rather than as the agent named it."),
181
- // The agent's one-line case for connecting it, the only prose on the card that is the model's.
182
- why: z.string().optional().describe("The agent's case for connecting it, and the only words on this card that are the agent's."),
183
- });
184
- export type CapabilityOffer = z.infer<typeof CapabilityOfferSchema>;
185
-
186
- /* The trailer the PLATFORM appends to every relayed run stream, never provider-authored: it is the ledger
187
- * speaking after the stream settled. `ok` means the run served and was charged (`remaining` is the meter
188
- * after); `refunded` means the provider's stream died before its `result` and the charge was reversed. */
189
- export const ServiceRunReceiptSchema = z.object({
190
- event: z
191
- .literal(`receipt`)
192
- .describe("The last line of a run, added by the platform rather than by the service. The ledger speaking after the fact."),
193
- outcome: z.enum([`ok`, `refunded`]).describe("Whether it served and was charged, or died before answering and the charge was reversed."),
194
- credits: z.number().describe("What it cost."),
195
- remaining: z.number().optional().describe("What is left afterwards."),
196
- });
197
- export type ServiceRunReceipt = z.infer<typeof ServiceRunReceiptSchema>;
198
-
199
- /* ONE OUTBOUND USDC PAYMENT, OFFERED, the card the daemon raises when the agent asks to pay an x402
200
- * endpoint out of the sandbox wallet (wallet/payment-offer.ts). Every number on it is the daemon's own
201
- * arithmetic over the ENDPOINT's parsed challenge and the wallet's own ledger, the model that asked
202
- * contributes `why` (its one line of rationale) and nothing else, which is what makes the price on the card
203
- * impossible to misquote, and the click on it the only way the money can move. */
204
- export const PaymentOfferSchema = z.object({
205
- // The paid resource, as the endpoint's challenge stated it.
206
- url: z.string().describe("What is being paid for."),
207
- description: z.string().optional().describe("What the endpoint says it is."),
208
- // Where the money goes, verbatim off the challenge: recipient address, CAIP-2 network, token contract.
209
- payTo: z.string().describe("Where the money goes, taken verbatim from the endpoint's own demand."),
210
- network: z.string().describe("On which network."),
211
- asset: z.string().describe("In which token."),
212
- // The token's display name ("USDC"), dollar-pegged, which is what lets every amount below read as USD.
213
- assetName: z.string().describe("That token's name. It is pegged to the dollar, which is what lets every amount here read as dollars."),
214
- // The exact price in display units ("0.10"), the x402 exact scheme has no ranges, so this is the whole
215
- // spend, not a ceiling.
216
- amountUsd: z.string().describe("The exact price. Not a ceiling: this scheme has no ranges, so this is the whole spend."),
217
- // The wallet's meter as the daemon's ledger states it, what "spent today / cap" renders from.
218
- spentTodayUsd: z.string().describe("What has already gone out today."),
219
- dailyCapUsd: z.string().describe("What may go out in a day."),
220
- // The agent's one-line case for paying, the only prose on the card that is the model's.
221
- why: z.string().optional().describe("The agent's case for paying, and the only words on this card that are the agent's."),
222
- });
223
- export type PaymentOffer = z.infer<typeof PaymentOfferSchema>;
224
-
225
- /* ONE GATED CREDENTIAL, ASKED FOR, the card the daemon raises when the agent reaches for a secret or a
226
- * connected account the owner put behind a named person (secrets/credential-gate.ts).
227
- *
228
- * Every field but `why` is the daemon's own: the subject and its approvers come off the gate policy the owner
229
- * wrote (which lives off the workspace, where the agent cannot edit it), the lane and detail come from the
230
- * exit that was about to spend the credential, and the scope is the policy's, not the asker's. The model
231
- * contributes one line of rationale and nothing else, which is what makes the card impossible to
232
- * misrepresent: a prompt-injected turn can ask for the production password and cannot make the card say it is
233
- * asking for the staging one.
234
- *
235
- * THE APPROVERS ARE ON THE CARD because the card is not addressed to "the owner" the way every other offer
236
- * here is — it is addressed to a LIST, the server checks the clicker's verified identity against it, and a
237
- * click from anybody else is refused with the card left standing. So the names have to be visible: a card
238
- * whose buttons do nothing for the person looking at it must say who it is waiting for. */
239
- export const CredentialOfferSchema = z.object({
240
- // The gate's subject: a secret's reference name (`DATABASE_URL`) or a capability id (`reddit`).
241
- subject: z.string().describe("Which credential is being asked for."),
242
- kind: CredentialGateKindSchema,
243
- lane: CredentialLaneSchema,
244
- // Where it would go, in the reader's terms: the head of the agent's command line, the page's host, or the
245
- // capability's own name. Reference-form by construction on the secret lanes (resolution is what fires the
246
- // ask), so this can be shown without leaking anything.
247
- detail: z
248
- .string()
249
- .optional()
250
- .describe("Where it would go: the start of the command, the site, or what is being mounted. Never a value: the command still reads as a reference at this point."),
251
- // The agent's one-line case, the only prose on the card that is the model's.
252
- why: z.string().optional().describe("The agent's case for using it, and the only words on this card that are the agent's."),
253
- approvers: z.array(z.string()).describe("Who may release it. A click from anyone else is refused and leaves the card standing."),
254
- scope: CredentialGateScopeSchema,
255
- });
256
- export type CredentialOffer = z.infer<typeof CredentialOfferSchema>;
257
-
258
- // One provider-advertised slash command, an ACP agent's available_commands entry, or a Claude Code session's
259
- // supportedCommands() (its built-ins plus the workspace's own .claude/commands and any plugin/skill commands).
260
- // `hint` is the argument placeholder the popover shows after the name.
261
- export const AgentCommandSchema = z.object({
262
- name: z.string().describe("What to type, without the leading slash."),
263
- description: z.string().describe("What it does."),
264
- hint: z.string().optional().describe("What its argument should look like, shown after the name."),
265
- });
266
- export type AgentCommand = z.infer<typeof AgentCommandSchema>;
267
-
268
- // GET /agent/commands, which provider's last-published list to read; absent = claude, matching AgentTurn.
269
- export const AgentCommandsQuerySchema = z.object({
270
- agent: AgentProviderSchema.optional().describe("Whose commands to read. Leave it out for Claude."),
271
- });
272
- export const AgentCommandsSchema = z.object({
273
- commands: z.array(AgentCommandSchema).describe("The shortcut commands, as the provider last published them."),
274
- });
275
-
276
- // One TodoWrite/Task checklist item, surfaced live so the UI shows the agent's plan-of-work (Claude Code style).
277
- export const TodoItemSchema = z.object({
278
- content: z.string().describe("The item, as the agent wrote it."),
279
- status: z.enum(["pending", "in_progress", "completed"]).describe("Where it is."),
280
- activeForm: z
281
- .string()
282
- .optional()
283
- .describe("How to phrase it while it is happening, so a screen can say what the agent is doing rather than what it plans to do."),
284
- });
285
- export type TodoItem = z.infer<typeof TodoItemSchema>;
286
-
287
- // Context-window fill for a conversation: how many tokens the latest request sent vs the model's window, so
288
- // the UI can warn as the chat nears auto-compaction. Per-conversation, unlike the account-wide usage above.
289
- export const ContextUsageSchema = z.object({
290
- tokens: z.number().describe("How much the latest request sent, all told."),
291
- contextWindow: z.number().describe("How much the model can hold. The gap between these two is how close the conversation is to being compacted."),
292
- });
293
- export type ContextUsage = z.infer<typeof ContextUsageSchema>;
294
-
295
- // ACP-aligned tool taxonomy (Agent Client Protocol's ToolKind, verbatim): what a tool call *does*, driving
296
- // the card icon and the live-writes bookkeeping regardless of which backend named the tool.
297
- export const ToolKindSchema = z.enum(["read", "edit", "delete", "move", "search", "execute", "think", "fetch", "other"]);
298
- export type ToolKind = z.infer<typeof ToolKindSchema>;
299
-
300
- export const ToolCallStatusSchema = z.enum(["pending", "in_progress", "completed", "failed"]);
301
- export type ToolCallStatus = z.infer<typeof ToolCallStatusSchema>;
302
-
303
- // A file a tool call touches. Workspace-root-relative, forward-slash (the tree/file route space), adapters
304
- // normalize from the turn's cwd. `line` is 1-based.
305
- export const ToolCallLocationSchema = z.object({
306
- path: z.string().describe("The file, as a workspace path, whatever directory the tool was run from."),
307
- line: z.number().optional().describe("Which line, counting from one."),
308
- });
309
- export type ToolCallLocation = z.infer<typeof ToolCallLocationSchema>;
310
-
311
- // Structured tool output (ACP's ToolCallContent diff shape, verbatim). `diff` is hunk-level for Edit-style
312
- // tools (old_string/new_string) and whole-file for Write; an absent oldText means a new file / unknown
313
- // previous content. Sides are capped daemon-side; `truncated` marks a clipped side.
314
- //
315
- // `image` is a PICTURE THE TOOL PRODUCED, carried as a workspace path rather than as bytes. Browser screenshots
316
- // already live under .intentic/records/artifacts/browser, and provider-generated images are copied into
317
- // .intentic/records/artifacts/imagegen, so the client fetches either from /workspace/raw like any other file. Base64 on
318
- // the wire would bloat the event stream and every stored transcript to show bytes the workspace already serves;
319
- // the path also keeps the picture openable afterwards. Root-relative, forward-slash: the same route space as
320
- // ToolCallLocation.
321
- export const ToolCallContentSchema = z.discriminatedUnion("type", [
322
- z.object({
323
- type: z.literal("text").describe("Plain output."),
324
- text: z.string().describe("What the tool said."),
325
- }),
326
- z.object({
327
- type: z.literal("diff").describe("A change to a file."),
328
- path: z.string().describe("Which file, as a workspace path."),
329
- oldText: z.string().optional().describe("What was there. Absent for a new file, or where the previous contents are not known."),
330
- newText: z.string().describe("What is there now."),
331
- truncated: z.boolean().optional().describe("One of the two sides was too large to send whole."),
332
- }),
333
- z.object({
334
- type: z.literal("image").describe("A picture the tool produced."),
335
- path: z
336
- .string()
337
- .describe(
338
- "Where it is, as a workspace path. A path rather than the bytes, because the workspace already serves it, sending it inline would bloat every stored record, and this way the picture stays openable afterwards.",
339
- ),
340
- }),
341
- ]);
342
- export type ToolCallContent = z.infer<typeof ToolCallContentSchema>;
343
-
344
- /* WHAT A PARKED CARD IS ABOUT: the document the turn wrote and is now asking a question against.
345
- *
346
- * A card asks for a decision; until this it carried no SUBJECT. The commonest shape of a real decision is "I
347
- * analysed this and wrote it up, now choose", and the write-up went into a file whose card had already folded
348
- * itself into `Write · +135 −0` twenty tool calls back. So the reader was asked to choose between options
349
- * describing a document the chat had never shown them.
350
- *
351
- * Carried BY VALUE rather than as a path, for the same reason the diff on a tool call is: the bytes are already
352
- * in hand when the card is raised, a path would make the card's meaning depend on a file that keeps changing
353
- * under it, and a restored or published transcript has no workspace to go read. The path rides along anyway, so
354
- * a document past the wire cap still has somewhere to send the reader.
355
- *
356
- * Nothing is asked of the MODEL for this. It calls `ask` exactly as before; the daemon knows what the turn
357
- * wrote, because every write came past it as a frame (documents.ts decides which of them is a document). A
358
- * harness that can see the answer must not spend prompt on asking the model to repeat it. */
359
- export const CardDocumentSchema = z.object({
360
- path: z.string().describe("Where it lives, as a workspace path."),
361
- title: z.string().describe("What it is called: its opening heading, or its file name."),
362
- markdown: z.string().describe("The document itself."),
363
- truncated: z.boolean().optional().describe("It was clipped at the wire cap; the file on disk has more."),
364
- plan: z.boolean().optional().describe("It is one of the CLI's plan files, written to be approved rather than merely read."),
365
- });
366
- export type CardDocument = z.infer<typeof CardDocumentSchema>;
367
-
368
- /* ONE CARD'S OWN FIELDS, spelled once. Three readers carry the same card and must agree on what it is: the
369
- * frame that raises it (AgentEventSchema below), the journal entry that keeps a parked one across a restart
370
- * (ParkedCardSchema), and the record row that keeps it for good (TranscriptRowSchema's card fields). A shape
371
- * declared inline in each was three shapes with one name. */
372
- const REQUEST_ID = z.string().describe("What to send back when you answer.");
373
- const planCard = {
374
- requestId: REQUEST_ID,
375
- text: z.string().describe("The plan itself."),
376
- // Present when the adjacent plan prose POINTS at a document instead of being one: the model wrote the real
377
- // plan to a file and summarised it there. Absent when the text already is the whole plan.
378
- document: CardDocumentSchema.optional().describe("The write-up this plan refers to, when the plan itself is a pointer to one."),
379
- };
380
- const questionCard = {
381
- requestId: REQUEST_ID,
382
- questions: z.array(AskQuestionSchema).describe("What it wants to know."),
383
- document: CardDocumentSchema.optional().describe("The document this turn wrote and is asking about, so the choice can be read beside it."),
384
- };
385
- const permissionCard = { requestId: REQUEST_ID };
386
- // The agent's browser needs a person: it parked mid-sign-in on something it cannot clear itself (a captcha,
387
- // a password it does not hold, a phone check). `session` names the browser session on /browsers, the card's
388
- // one action is going THERE, where the live stage and Take control already are; the Browsers banner and this
389
- // card resolve the same requestId. `account` is the capability the sign-in is for, so the card can say whose
390
- // login is stuck even after the browser has navigated somewhere unrecognizable.
391
- const browserHelpCard = {
392
- requestId: z.string(),
393
- session: z.string(),
394
- account: z.string(),
395
- message: z.string(),
396
- };
397
- // The agent's TERMINAL needs a person: a command it started is sitting at a prompt it cannot answer (a
398
- // one-time password, a security-key touch, a confirm). `session` names the tmux session on the terminal
399
- // panel, the card's one action is going THERE, where the live pane and its prompt already are, which is
400
- // the same division of labour the browser card has with /browsers.
401
- const terminalHelpCard = {
402
- requestId: z.string(),
403
- session: z.string(),
404
- message: z.string(),
405
- };
406
- const serviceOfferCard = { requestId: z.string(), offer: ServiceOfferSchema };
407
- const capabilityOfferCard = { requestId: z.string(), offer: CapabilityOfferSchema };
408
- const paymentOfferCard = { requestId: z.string(), offer: PaymentOfferSchema };
409
- const credentialOfferCard = { requestId: z.string(), offer: CredentialOfferSchema };
410
-
411
- /* HOW AN OFFER'S ACCEPTED HALF ENDED, the follow-up that lands on the card after the click. Each is the body of
412
- * the frame that reports it (`service_receipt`, `capability_outcome`, `payment_receipt`) and the field the
413
- * record keeps it in, one shape for both, so a receipt reopened tomorrow says exactly what the live card said. */
414
- export const ServiceReceiptSchema = z.object({
415
- outcome: z.enum(["ok", "refunded", "refused"]),
416
- credits: z.number(),
417
- remaining: z.number().optional(),
418
- });
419
- export type ServiceReceipt = z.infer<typeof ServiceReceiptSchema>;
420
- export const CapabilityOutcomeSchema = z.object({
421
- outcome: z.enum(["connected", "unfinished"]),
422
- id: z.string().optional(),
423
- });
424
- export type CapabilityOutcome = z.infer<typeof CapabilityOutcomeSchema>;
425
- export const PaymentReceiptSchema = z.object({
426
- outcome: z.enum(["paid", "failed"]),
427
- amountUsd: z.string(),
428
- transaction: z.string().optional(),
429
- network: z.string().optional(),
430
- });
431
- export type PaymentReceipt = z.infer<typeof PaymentReceiptSchema>;
432
- /* WHO RELEASED A GATED CREDENTIAL, or that a person refused it. `released` carries the approver's own address,
433
- * read off the VERIFIED identity on the reply rather than off anything the click claimed, which is what makes
434
- * the row an audit line rather than a rendering. There is no receipt for a card nobody answered: `resolved`
435
- * already says so, and inventing "refused" for a deadline would put words in a person's mouth. */
436
- export const CredentialReceiptSchema = z.object({
437
- outcome: z.enum(["released", "refused"]),
438
- approvedBy: z.string().optional(),
439
- });
440
- export type CredentialReceipt = z.infer<typeof CredentialReceiptSchema>;
441
-
442
- /* THE THREE RESTORABLE CARDS, named so the turn journal can hold them verbatim: a parked turn's raised cards
443
- * are written down beside its prompt (sandbox turn-journal.ts), and a daemon death under the park restores the
444
- * very same frames instead of ending the turn `interrupted`, the card the user was about to answer survives
445
- * the restart that killed the process holding it. The two handover cards are deliberately not among them:
446
- * `browser_help`'s Chromium and `terminal_help`'s waiting command both die with the container, so those parks
447
- * cannot be restored, only reported. */
448
- const PlanCardSchema = z.object({
449
- kind: z.literal("plan").describe("The agent has written a plan and is waiting for a yes."),
450
- ...planCard,
451
- });
452
- const QuestionCardSchema = z.object({
453
- kind: z.literal("question").describe("The agent has asked you something and is waiting."),
454
- ...questionCard,
455
- });
456
- const PermissionCardSchema = PermissionAskSchema.extend({
457
- kind: z.literal("permission").describe("The agent wants to use a tool it needs permission for."),
458
- ...permissionCard,
459
- });
460
- export const ParkedCardSchema = z.discriminatedUnion("kind", [PlanCardSchema, QuestionCardSchema, PermissionCardSchema]);
461
- export type ParkedCard = z.infer<typeof ParkedCardSchema>;
462
-
463
- // ---- transcript cards ----
464
- /* THE CARDS A TURN PARKED ON, as a transcript row carries them: the card exactly as it was raised, how it was
465
- * settled, and whatever landed on it afterwards (a permission's late explanation, an offer's stream and
466
- * receipt). One shape for the live row and the recorded one, because they are the same row: the daemon folds
467
- * the turn's frames into these rows as they stream (transcript-fold.ts) and writes the same rows down when the
468
- * turn settles, so a chat reopened tomorrow is the chat that was on screen.
469
- *
470
- * The STATUS is settled by the fold, from the reply that released the card (card-status.ts), and rides the
471
- * row rather than the reply it came from: every reader wants the verdict, and the one derivation lives beside
472
- * the fold that applies it. `pending` is a card the turn is still parked on; `cancelled` is nobody answering,
473
- * the turn stopped or died under the card, which is not a decision and does not read back as one. */
474
- export const PlanStatusSchema = z.enum(["pending", "approved", "rejected", "cancelled"]);
475
- export type PlanStatus = z.infer<typeof PlanStatusSchema>;
476
- export const QuestionStatusSchema = z.enum(["pending", "answered", "cancelled"]);
477
- export type QuestionStatus = z.infer<typeof QuestionStatusSchema>;
478
- export const PermissionStatusSchema = z.enum(["pending", "allowed", "always", "denied", "cancelled"]);
479
- export type PermissionStatus = z.infer<typeof PermissionStatusSchema>;
480
- export const HelpStatusSchema = z.enum(["pending", "helped", "declined", "cancelled"]);
481
- export type HelpStatus = z.infer<typeof HelpStatusSchema>;
482
- export const OfferStatusSchema = z.enum(["pending", "approved", "skipped", "cancelled"]);
483
- export type OfferStatus = z.infer<typeof OfferStatusSchema>;
484
- // A yes settles the DECISION, not the ask: the owner is now setting the capability up, so the card moves to
485
- // `connecting` and stays there until the capability_outcome frame says how the setup ended.
486
- export const CapabilityOfferStatusSchema = z.enum(["pending", "connecting", "skipped", "cancelled"]);
487
- export type CapabilityOfferStatus = z.infer<typeof CapabilityOfferStatusSchema>;
488
-
489
- export const TranscriptPlanSchema = z.object({ ...planCard, status: PlanStatusSchema.describe("Where the decision stands.") });
490
- export type TranscriptPlan = z.infer<typeof TranscriptPlanSchema>;
491
- export const TranscriptQuestionSchema = z.object({
492
- ...questionCard,
493
- status: QuestionStatusSchema.describe("Where the answer stands."),
494
- answers: z
495
- .record(z.string(), z.array(z.string()))
496
- .optional()
497
- .describe("What was chosen, keyed by the question, with the chosen labels or the user's own words."),
498
- });
499
- export type TranscriptQuestion = z.infer<typeof TranscriptQuestionSchema>;
500
- // `explain`, the judge's sentence, lands here through PermissionAskSchema; it is on the card from the moment
501
- // it is raised, so nothing patches it in afterwards.
502
- export const TranscriptPermissionSchema = PermissionAskSchema.extend({ ...permissionCard, status: PermissionStatusSchema.describe("Where the decision stands.") });
503
- export type TranscriptPermission = z.infer<typeof TranscriptPermissionSchema>;
504
- export const TranscriptBrowserHelpSchema = z.object({ ...browserHelpCard, status: HelpStatusSchema.describe("How the hand-over ended.") });
505
- export type TranscriptBrowserHelp = z.infer<typeof TranscriptBrowserHelpSchema>;
506
- export const TranscriptTerminalHelpSchema = z.object({ ...terminalHelpCard, status: HelpStatusSchema.describe("How the hand-over ended.") });
507
- export type TranscriptTerminalHelp = z.infer<typeof TranscriptTerminalHelpSchema>;
508
- export const TranscriptServiceOfferSchema = z.object({
509
- ...serviceOfferCard,
510
- status: OfferStatusSchema.describe("Where the decision stands."),
511
- events: z.array(ServiceStreamEventSchema).optional().describe("The approved run's stream, in order (the service_event frames)."),
512
- receipt: ServiceReceiptSchema.optional().describe("How the approved run ended (the service_receipt frame)."),
513
- });
514
- export type TranscriptServiceOffer = z.infer<typeof TranscriptServiceOfferSchema>;
515
- export const TranscriptCapabilityOfferSchema = z.object({
516
- ...capabilityOfferCard,
517
- status: CapabilityOfferStatusSchema.describe("Where the decision stands."),
518
- outcome: CapabilityOutcomeSchema.optional().describe("How an accepted ask's setup ended (the capability_outcome frame)."),
519
- });
520
- export type TranscriptCapabilityOffer = z.infer<typeof TranscriptCapabilityOfferSchema>;
521
- export const TranscriptPaymentOfferSchema = z.object({
522
- ...paymentOfferCard,
523
- status: OfferStatusSchema.describe("Where the decision stands."),
524
- receipt: PaymentReceiptSchema.optional().describe("How the approved payment ended (the payment_receipt frame)."),
525
- });
526
- export type TranscriptPaymentOffer = z.infer<typeof TranscriptPaymentOfferSchema>;
527
- export const TranscriptCredentialOfferSchema = z.object({
528
- ...credentialOfferCard,
529
- status: OfferStatusSchema.describe("Where the decision stands."),
530
- receipt: CredentialReceiptSchema.optional().describe("Who released it, or that somebody refused (the credential_receipt frame)."),
531
- });
532
- export type TranscriptCredentialOffer = z.infer<typeof TranscriptCredentialOfferSchema>;
533
-
534
- // ---- transcript rows ----
535
- // What a conversation is made of, on every surface: the rows the daemon folds a turn's frames into as they
536
- // stream (the live chat renders these, patched as they grow), the rows the record keeps once the turn settles,
537
- // and the rows /agents/{id}/transcript replays into a reopened tab. One shape because it is one thing: a
538
- // reopened chat REDRAWS the transcript the user was looking at rather than paraphrasing it, so a row keeps the
539
- // assistant's thinking and the tool cards its block ran, which is also what lets a runtime handoff carry more
540
- // than bare prose across to a replacement session (see runtime-history.ts).
541
- //
542
- // One tool card. A subagent's own calls and its thinking nest under the Agent card that spawned them, so a
543
- // delegation reads as one unit instead of a flat run of siblings. z.lazy because the shape refers to itself: a
544
- // subagent that delegates nests one level deeper.
545
- export const TranscriptToolSchema: z.ZodType<TranscriptTool> = z.lazy(() =>
546
- z.object({
547
- id: z.string().describe("The call's id."),
548
- name: z.string().describe("Which tool."),
549
- category: ToolKindSchema.describe(
550
- "What kind of thing it does: read, edit, delete, move, search, run, think, fetch. Named the same way whatever the backend called the tool.",
551
- ),
552
- status: ToolCallStatusSchema.describe("How it went."),
553
- target: z.string().optional().describe("What it acted on, in one line: a file, a command, an address."),
554
- locations: z.array(ToolCallLocationSchema).optional().describe("The files it touched."),
555
- content: z.array(ToolCallContentSchema).optional().describe("What it produced: text, a change to a file, or a picture."),
556
- children: z
557
- .array(TranscriptToolSchema)
558
- .optional()
559
- .describe(
560
- "Calls a delegated subagent made, nested under the call that started it, so a reopened conversation redraws the delegation rather than collapsing it into one result.",
561
- ),
562
- thinking: z.string().optional().describe("What the agent was reasoning about around this call."),
563
- subagent: TranscriptSubagentSchema.optional().describe(
564
- "The helper this call started, as the daemon's registry sees it: what it is, how it is going, what it has spent. What a card can say about a backgrounded child whose result is minutes away.",
565
- ),
566
- }),
567
- );
568
- /* THE CHILD A CALL STARTED, on the card whose id the `subagent`/`subagent_update` frames name (that call's own),
569
- * so no correlation is needed: an Agent card wears its subagent's live state, and a Bash card that turned out
570
- * to be a `codex exec` wears its delegate's. The identifying fields arrive once (the `subagent` frame), the
571
- * moving ones (status, spend, what it is doing) replace as each update lands. */
572
- export const TranscriptSubagentSchema = z.object({
573
- kind: SubagentKindSchema,
574
- agentType: z.string().optional(),
575
- description: z.string().optional(),
576
- model: z.string().optional(),
577
- provider: z.string().optional(),
578
- background: z.boolean().optional(),
579
- status: SubagentStatusSchema,
580
- tokens: z.number().optional(),
581
- toolUses: z.number().optional(),
582
- lastTool: z.string().optional(),
583
- summary: z.string().optional(),
584
- error: z.string().optional(),
585
- verification: SubagentVerificationSchema.optional(),
586
- });
587
- export type TranscriptSubagent = z.infer<typeof TranscriptSubagentSchema>;
588
- // Mutable, unlike most of this file: the fold settles a card IN PLACE when its result arrives turns later
589
- // (transcript-fold.ts's `cards` map, readWorkspaceSession's `awaiting`), which is what saves it a second pass.
590
- export interface TranscriptTool {
591
- id: string;
592
- name: string;
593
- category: ToolKind;
594
- status: ToolCallStatus;
595
- target?: string | undefined;
596
- locations?: ToolCallLocation[] | undefined;
597
- content?: ToolCallContent[] | undefined;
598
- children?: TranscriptTool[] | undefined;
599
- thinking?: string | undefined;
600
- subagent?: TranscriptSubagent | undefined;
601
- }
602
-
603
- /* ONE NOTE THE DAEMON PUT IN FRONT OF A USER'S MESSAGE, as both audiences see it: the model reads `text`, and
604
- * the chat draws `title` on a collapsed row that opens to that same `text`. Shared by the live frame and the
605
- * restored transcript so a note reads identically whether the tab watched it arrive or reopened an hour later. */
606
- export const TurnNoteSchema = z.object({
607
- title: z.string().describe("The one line a reader sees, on a row that opens to the text below."),
608
- text: z.string().describe("The note itself, which is also exactly what the model was told."),
609
- });
610
- export type TurnNote = z.infer<typeof TurnNoteSchema>;
611
-
612
- // End-of-turn accounting (assistant rows only, the last bubble of a turn): what the turn cost, attached where
613
- // the answer ended so a reader can see what each exchange spent.
614
- export const TranscriptUsageSchema = z.object({
615
- costUsd: z.number().optional(),
616
- inputTokens: z.number().optional(),
617
- outputTokens: z.number().optional(),
618
- durationMs: z.number().optional(),
619
- numTurns: z.number().optional(),
620
- });
621
- export type TranscriptUsage = z.infer<typeof TranscriptUsageSchema>;
622
-
623
- // One row. Each block of the agent's prose is its own, with the tool cards that block introduced, which is what
624
- // reproduces the way a turn actually unfolded rather than collapsing its whole narration into one bubble with
625
- // every tool hanging off the end.
626
- export const TranscriptRowSchema = z.object({
627
- /* `notice` is neither side of the conversation: it is something that HAPPENED to the turn, a refusal, a
628
- * landed delta, a compaction, a stop, written down so a reopened conversation says it too. The one that
629
- * matters most is a refused turn: a provider that answers "your organization has disabled Claude
630
- * subscription access" produced no assistant text, so a transcript of the two speakers alone ends on the
631
- * user's message and reads as broken. */
632
- role: z
633
- .enum(["user", "assistant", "notice"])
634
- .describe(
635
- "Who said it. A notice is neither side: it is something that happened to the turn, recorded so a reopened conversation can say it. Without those, a turn a provider refused ends on the user's message and reads as broken.",
636
- ),
637
- text: z.string().describe("The words."),
638
- /* WHEN THIS TURN WAS SENT, in epoch milliseconds (user rows only), what the chat shows on the bubble it
639
- * belongs to. The turn's START, not the moment the record was written: a turn that ran for twenty minutes
640
- * was still sent when the user pressed send, and a stamp taken at settlement would say the conversation
641
- * happened at the times its answers finished.
642
- *
643
- * Only the user's row carries one, because it is the only row whose moment the daemon actually knows. A
644
- * turn's frames arrive with no clock of their own, so an assistant bubble could only ever be stamped with
645
- * the whole turn's start or end, a number that says nothing about when that particular block was written.
646
- * Rows recorded before this existed simply have none, and the chat draws nothing for them. */
647
- sentAt: z
648
- .number()
649
- .optional()
650
- .describe(
651
- "When it was sent, in milliseconds. On the user's rows only, because that is the only moment actually known: a turn's own frames arrive with no clock, so stamping the agent's rows could only ever mean the whole turn's start or end.",
652
- ),
653
- // Files the user attached to this turn (user rows only) as workspace-relative paths, the uploads alone:
654
- // a path @-mentioned inline in the text is already visible there and is not drawn as a chip.
655
- attachments: z.array(z.string()).optional().describe("Files attached to this message, as workspace paths."),
656
- /* The checkpoint this message can be rewound to (user rows only), and where this message sits in the
657
- * conversation's record, which is what the rewind route addresses it by. Never stored: both are stamped
658
- * onto the live row by the turn's own `checkpoint` frame and onto a replayed row by the read that serves
659
- * it, looked up from the daemon's rewind points, which a rewind rewrites, so a reopened tab offers exactly
660
- * the turns that are still there to go back to. */
661
- checkpointId: z
662
- .string()
663
- .optional()
664
- .describe(
665
- "The saved point this message can be rewound to. Looked up on each read rather than stored, so what is offered is exactly what is still there to go back to.",
666
- ),
667
- rewindIndex: z
668
- .number()
669
- .int()
670
- .nonnegative()
671
- .optional()
672
- .describe("This message's position in the conversation's record, which is how a rewind names it. Present only beside a checkpoint."),
673
- thinking: z.string().optional().describe("What the agent was reasoning about."),
674
- tools: z.array(TranscriptToolSchema).optional().describe("The tool calls this part of the turn made."),
675
- todos: z.array(TodoItemSchema).optional().describe("The agent's task checklist, as of this bubble."),
676
- usage: TranscriptUsageSchema.optional().describe("What the turn cost, on the bubble its answer ended in."),
677
- /* What the daemon added to this turn's message (user rows only), the same notes the live `preamble` frame
678
- * carries, read off that frame by the fold.
679
- *
680
- * On the message rather than as a row of its own, and that matters twice: they ARE part of what was
681
- * sent, and a record row per turn preamble would break the one-row-per-bubble correspondence a branch counts
682
- * with. */
683
- notes: z
684
- .array(TurnNoteSchema)
685
- .optional()
686
- .describe(
687
- "What the sandbox added to this message before the model saw it. Carried on the message rather than as rows of their own, because they genuinely were part of what was sent.",
688
- ),
689
- /* THE USER WROTE THIS ROW WEARING THE AGENT'S VOICE (assistant rows only), the composer's "as agent" mode
690
- * appending straight into the record, with no turn behind it (agents.place).
691
- *
692
- * The flag exists for exactly one audience: the HUMAN re-reading the transcript, whose bubble carries a
693
- * quiet mark so that months later their own words don't pass as the agent's. The one reader that must
694
- * never see it is the agent itself, a placed line reaches the model only through the handoff that seeds a
695
- * fresh runtime session (agent/runtime-history.ts), which renders role and text alone, so there the line is
696
- * indistinguishable from anything the agent genuinely said. Keep it that way: rendering this flag into any
697
- * agent-facing text would break the feature's whole contract. */
698
- placed: z
699
- .boolean()
700
- .optional()
701
- .describe(
702
- "A person wrote this in the agent's voice, with no turn behind it. Marked for the human re-reading the conversation months later, so their own words do not pass as the agent's. The agent itself never sees the mark.",
703
- ),
704
- /* THE ONE-PRESS OFFER A NOTICE CARRIES (notice rows only), named rather than inferred from its words: the
705
- * landed notice's "keep future work on the branch", the outage notice's "stop resuming these by itself",
706
- * the terminal a dependency install the daemon just started is running in, and the routed turn's "keep
707
- * this chat on my pick". A KIND, not a callback: the chat decides what the press does and whether the
708
- * offer still stands (a chat already holding its pick shows a settled sentence, not a stale button). */
709
- noticeAction: z
710
- .enum(["landHold", "outageOptOut", "depsInstall", "tierHold"])
711
- .optional()
712
- .describe("A one-press follow-up this notice offers, by name. The chat decides what it does and whether it still applies."),
713
- /* A WAIT THIS NOTICE DESCRIBES that had not finished when it was written (notice rows only): the chat draws
714
- * a spinner over it while the wait is on, and the plain line once it is over. A KIND rather than a boolean
715
- * because whether the wait is STILL running is a fact about the conversation now, not about a row in a
716
- * record: the reader pairs the kind with the live state that answers it. */
717
- noticeWait: z.enum(["credentialRenewal"]).optional().describe("The wait this notice describes, by name, so a reader can say whether it is still on."),
718
- /* THE CARD THIS BUBBLE PARKED ON (assistant rows only), at most one: a card closes the bubble it lands in,
719
- * so the next thing the agent says opens a fresh row beneath it. One field per kind rather than one union
720
- * field, so a reader reaches the card it draws by name. */
721
- plan: TranscriptPlanSchema.optional().describe("The plan this row asked approval for, and the answer."),
722
- question: TranscriptQuestionSchema.optional().describe("The questions this row asked, and the picks that answered them."),
723
- permission: TranscriptPermissionSchema.optional().describe("The tool this row asked permission for, and the decision."),
724
- browserHelp: TranscriptBrowserHelpSchema.optional().describe("The browser hand-over this row asked for, and how it ended."),
725
- terminalHelp: TranscriptTerminalHelpSchema.optional().describe("The terminal hand-over this row asked for, and how it ended."),
726
- serviceOffer: TranscriptServiceOfferSchema.optional().describe("The priced service run this row offered, the decision, and the receipt."),
727
- capabilityOffer: TranscriptCapabilityOfferSchema.optional().describe("The capability setup this row asked for, the decision, and the outcome."),
728
- paymentOffer: TranscriptPaymentOfferSchema.optional().describe("The payment this row asked for, the decision, and the receipt."),
729
- credentialOffer: TranscriptCredentialOfferSchema.optional().describe(
730
- "The gated credential this row asked to use, who may release it, and who did.",
731
- ),
732
- });
733
- export type TranscriptRow = z.infer<typeof TranscriptRowSchema>;
734
-
735
- /* THE CARD FIELDS A ROW CAN CARRY, as one list, for every reader that has to ask "does this row hold a card":
736
- * the fold that counts a card-only bubble as a row, the chat's row count (a branch is cut by it), and the
737
- * surfaces that draw whichever card a bubble is waiting on. */
738
- export const CARD_FIELDS = [
739
- "plan",
740
- "question",
741
- "permission",
742
- "browserHelp",
743
- "terminalHelp",
744
- "serviceOffer",
745
- "capabilityOffer",
746
- "paymentOffer",
747
- "credentialOffer",
748
- ] as const;
749
- export type CardField = (typeof CARD_FIELDS)[number];
750
- export type TranscriptCards = Pick<TranscriptRow, CardField>;
751
- // Whether a row holds a card at all, answered or not.
752
- export const holdsCard = (row: TranscriptCards): boolean => CARD_FIELDS.some((field) => row[field] !== undefined);
753
- // Whether a row is holding the turn open on a card nobody has answered.
754
- export const isAwaitingDecision = (row: TranscriptCards): boolean => CARD_FIELDS.some((field) => row[field]?.status === "pending");
755
-
756
- /* ONE CHANGE TO A RUN'S ROWS, what the attach stream carries while a turn runs. The daemon folds each frame
757
- * into its rows (transcript-fold.ts) and says what moved, so a client keeps rows, never frames: it applies
758
- * these to the list it holds and draws it. `index` counts from the run's first row, which the attach head
759
- * places in the conversation.
760
- *
761
- * Prose and thinking arrive as APPENDS to a row rather than as the row again, so the chat can type them out at
762
- * the pace they are written; a tool card arrives whole (`tool`, by id, replacing an earlier copy of the same
763
- * id wherever it nests), because its updates are snapshots already; everything else replaces its row. `drop`
764
- * is the one removal: an assistant row opened for a block that then wrote nothing. */
765
- export const TranscriptPatchSchema = z.discriminatedUnion("op", [
766
- z.object({ op: z.literal("append").describe("A new row at the end."), row: TranscriptRowSchema }),
767
- z.object({ op: z.literal("replace").describe("This row, whole, in place of the one at that index."), index: z.number().int().nonnegative(), row: TranscriptRowSchema }),
768
- z.object({ op: z.literal("drop").describe("The row at that index is gone: it was opened and never written into."), index: z.number().int().nonnegative() }),
769
- z.object({ op: z.literal("text").describe("More of the agent's prose, onto that row's text."), index: z.number().int().nonnegative(), text: z.string() }),
770
- z.object({ op: z.literal("thinking").describe("More of the agent's reasoning, onto that row's thinking."), index: z.number().int().nonnegative(), text: z.string() }),
771
- z.object({
772
- op: z.literal("tool").describe("A tool card, whole: new, or the latest state of one already there, matched by id wherever it nests."),
773
- index: z.number().int().nonnegative(),
774
- tool: TranscriptToolSchema,
775
- parent: z.string().optional().describe("The card this one nests under, when it is a delegated subagent's own call."),
776
- }),
777
- ]);
778
- export type TranscriptPatch = z.infer<typeof TranscriptPatchSchema>;
779
-
780
- export const SessionTranscriptSchema = z.object({
781
- messages: z
782
- .array(TranscriptRowSchema)
783
- .describe(
784
- "The conversation, in order. Each block of the agent's prose is its own message with the tools that block introduced, which is what reproduces the way it actually unfolded.",
785
- ),
786
- });
787
- /* HOW A TURN THAT LEFT WORK BEHIND ENDED, as the daemon has it, for whoever asks and however long after.
788
- *
789
- * One shape for every ending that leaves finished work behind a live session, because they are one situation
790
- * with one answer (a press) and they differ only in what can honestly be said about WHEN and what the press
791
- * DOES. The client folds this straight into its pick-up state (chat/pickUp.ts), which is why the field names
792
- * line up: a second vocabulary between the two halves is how they came to disagree in the first place.
793
- *
794
- * IT USED TO BE A BOOLEAN, and the boolean is what left the longest wait uncovered. One flag could only say
795
- * "a Stop, or a daemon killed under it", so a spent allowance, the one ending that reliably OUTLIVES the
796
- * window that hit it, reached a reopened tab as nothing at all: no strip, no countdown, no press, and the user
797
- * typing the word by hand hours later. It could not say more without these three facts, and each of them
798
- * changes what the surface may promise:
799
- *
800
- * · `resetsAt` is the only honest "not before this" any ending knows, and it is the whole of what a chat
801
- * reopened the next morning wants to be told;
802
- * · `held` is what makes the press cheap. The daemon keeps the refused turn whole, so continuing RE-RUNS it
803
- * and adds nothing to the conversation; without this the same press appends a message reading "Continue",
804
- * which is exactly the transcript pollution the press exists to prevent, and `ran` separates a turn that
805
- * got somewhere from one the allowance refused at the door (two different sentences);
806
- * · `scheduled` says somebody else is already bringing this turn back, so the surface REPORTS a wait instead
807
- * of offering one, and no local automation races the daemon's own pass for it. */
808
- export const TurnEndingSchema = z.object({
809
- reason: z
810
- .enum(["stopped", "limit", "outage"])
811
- .describe(
812
- "Which ending left the work here: a Stop or a daemon killed under the turn, a spent usage allowance, or a provider that refused it.",
813
- ),
814
- resetsAt: z
815
- .number()
816
- .optional()
817
- .describe("When the spent allowance reopens, in epoch seconds. Absent for every ending that names no instant, and for a provider that publishes none."),
818
- held: z
819
- .object({ ran: z.boolean().describe("Whether the held turn got anywhere before it was refused, which is a different sentence from one refused at the door.") })
820
- .optional()
821
- .describe("Present when the daemon still holds the refused turn whole, so a press re-runs it rather than appending a message after it."),
822
- scheduled: z
823
- .boolean()
824
- .optional()
825
- .describe("Whether something other than the user is already booked to send this turn again, so the surface reports the wait instead of offering a press."),
826
- });
827
- export type TurnEnding = z.infer<typeof TurnEndingSchema>;
828
-
829
- /* THE RECORD A REOPENED TAB IS REBUILT FROM: the messages, plus what the session behind them is BOUND to.
830
- *
831
- * A provider session is minted on one runtime under one credential, and it resumes only there, so a client
832
- * deciding whether its next message continues this conversation or starts a fresh one needs all four facts
833
- * together. The client cannot derive the last three: its tab holds the picks the NEXT turn would use, which
834
- * after a mid-chat switch are exactly the ones the session does not belong to. Stamping those onto the session
835
- * is what made switching BACK to the account that minted it announce a fresh session and then retire a
836
- * perfectly resumable one, spending the whole transcript again on a cold prompt cache.
837
- *
838
- * The session fields are all optional, and absent together on a conversation that has no session to resume. */
839
- export const AgentTranscriptSchema = SessionTranscriptSchema.extend({
840
- sessionId: z.string().optional().describe("The provider session behind the last turn, when there is one."),
841
- provider: AgentProviderSchema.optional().describe("Which provider minted that session."),
842
- harness: AgentHarnessSchema.optional().describe("Which runtime minted it: a session resumes only on the loop that opened it."),
843
- account: z
844
- .string()
845
- .optional()
846
- .describe("Which stored account it belongs to, as the daemon resolved it. Absent when no stored account paid for the turn."),
847
- /* AND HOW THE LAST TURN ENDED, for the endings that leave the client something to OFFER rather than
848
- * something to draw: work half done behind a session that is perfectly alive, where the only thing missing
849
- * is somebody saying carry on.
850
- *
851
- * It rides the transcript because the offer used to ride the WINDOW. A chat armed the continue press from
852
- * the stream it was watching when the turn stopped, so the press existed only where somebody had been
853
- * looking: stop an agent from the board with its chat closed, or reopen the tab on another device, or after
854
- * a reload that dropped the tab, and the same stopped session came back with no way on but typing the word
855
- * by hand, which is precisely what the press exists to spare. The daemon is the one party that knows this
856
- * about a conversation whoever asks and however long after, so it is the one that says it. */
857
- ending: TurnEndingSchema.optional().describe(
858
- "How the last turn ended, when it left work behind that one press finishes. Absent for a conversation whose last turn ended on its own, and for the failures that name something to repair first.",
859
- ),
860
- /* WHERE THIS PAGE SITS IN THE CONVERSATION. `messages` is the tail, not the whole record: a conversation
861
- * that ran all week used to be served entire to every tab that opened it and to every card the board warms
862
- * behind it, which is megabytes over a tunnel to redraw a screenful.
863
- *
864
- * `from` is the position of the first message in the WHOLE record, which makes it two things at once: the
865
- * offset every `rewindIndex` in this page is counted against, and the `before` that asks for the page above
866
- * it. `more` says whether there is one, so a client can offer to go back without spending a round trip
867
- * finding out. */
868
- from: z.number().int().nonnegative().describe("Where the first message sits in the whole record, and the `before` that asks for the page above this one."),
869
- more: z.boolean().describe("Whether older messages precede this page."),
870
- });
871
-
872
- /* WHAT A PUBLISHED CONVERSATION'S PAGE IS HANDED, the whole of it, baked into the page as one JSON block.
873
- *
874
- * A share has to keep working with nothing behind it: no daemon, no session, no sandbox that has to still be
875
- * running when the recipient finally opens the link. So the page carries its conversation rather than fetching
876
- * it, which also settles the security question by construction, a page with nothing to ask has no way to ask
877
- * for something it was not given.
878
- *
879
- * The messages are the SAME TranscriptRow rows the app replays a reopened tab from, already filtered to the
880
- * chosen detail level and with every picture path rewritten to the copy published beside the page. That
881
- * sameness is the point: the shared page renders them with the app's own components, so what a recipient sees
882
- * is what the owner saw. */
883
- export const SharePayloadSchema = z.object({
884
- title: z.string(),
885
- // When the snapshot was taken, not when the conversation happened, see SharedConversation.sharedAt.
886
- sharedAt: z.number(),
887
- detail: ShareDetailSchema,
888
- messages: z.array(TranscriptRowSchema),
889
- });
890
- export type SharePayload = z.infer<typeof SharePayloadSchema>;
891
-
892
- // One frame from an agent turn, relayed to the UI. `kind`-discriminated. The daemon normalizes the SDK's
893
- // ~40 SDKMessage types down to this union: high-value block types get a dedicated frame
894
- // (delta/thinking/tool_call/tool_call_update/todos/usage/rate_limit_info/account_usage/context_usage/init/compact); any SDK message
895
- // without a UI mapping is dropped. `plan`/`question`/`permission` pause the turn until the user answers on the
896
- // `POST /agent/reply` side channel, and `resolved` releases the one it names; `mode` reports the live
897
- // permission posture as the agent changes it.
898
- // `parentToolUseId` tags frames produced inside a subagent (Task tool); `subagent`/`subagent_update` report the
899
- // subagent itself, keyed by the same tool_use id those tagged frames carry.
900
- export const AgentEventSchema = z.discriminatedUnion("kind", [
901
- /* THE SESSION THIS TURN IS RUNNING, and the credential it belongs to.
902
- *
903
- * `account` is the account the daemon RESOLVED for the turn, which is not always the one the request named:
904
- * a turn that names none is given the connected account with the most headroom (agent/harness-credentials.ts),
905
- * so "the client's pick" and "who is paying" are different questions and only the daemon can answer the
906
- * second. It rides here because a session belongs to the credential that minted it — that pairing is what
907
- * decides whether the next message resumes this session or opens a fresh one — and a client that stamped its
908
- * own pick onto the session instead would announce a fresh session for the account that actually holds it.
909
- *
910
- * Absent when the turn ran on the container's env token or on a translator subscription, where there is no
911
- * stored account to name. */
912
- z.object({
913
- kind: z.literal("session"),
914
- sessionId: z.string(),
915
- account: z.string().optional().describe("Which stored account this session belongs to, as the daemon resolved it for the turn."),
916
- }),
917
- /* WHERE AN ISOLATED TURN IS STANDING: the conversation's worktree identity, its branch (agent/<id>) and
918
- * the ROOT repo's short base sha. First frame of the turn, before any provider frames, and again each time
919
- * the branch MOVES underneath it, which is why `base` names where the branch sits now rather than the
920
- * moment it was checked out.
921
- *
922
- * `unenforced` marks the degraded container: no CAP_SYS_ADMIN, so the turn's worktree could not be
923
- * bind-mounted over the workspace root and the harness is rewriting tool paths into it instead. That
924
- * fallback covers what arrives as tool input and not what a subprocess computes for itself, so the
925
- * operator needs to know, this state used to be one line in the daemon log at boot, and the way it got
926
- * noticed was files appearing in the main tree from agents that were supposed to be on branches. Repeated
927
- * on every emission, because it describes the turn and a client rebuilds its standing from the last frame.
928
- *
929
- * `sync` reports a rebase (agents/sync.ts) and rides here because this frame is already the turn's "where
930
- * you are standing" announcement. Present only when the branch was BEHIND the main line, `commits` is how
931
- * many main-line commits it gained, `blocked` names the repos whose rebase would not apply and was rolled
932
- * back. Both can be non-empty at once in a multi-repo composition. Two moments produce it: before the turn
933
- * starts, and after a card the turn parked on is answered, a question or a plan approval waits minutes
934
- * for a person, and the main line does not stop moving meanwhile. It is a notice and never a question: the
935
- * user is answering their agent, and the alternative to rebasing is not "stay safe" but "conflict at land
936
- * time", which interrupts them harder. */
937
- z.object({
938
- kind: z.literal("worktree"),
939
- branch: z.string(),
940
- base: z.string(),
941
- unenforced: z.boolean().optional(),
942
- sync: z.object({ commits: z.number(), blocked: z.array(z.string()) }).optional(),
943
- // The runner this turn executes on, when the conversation is placed remotely (runners/): the
944
- // transcript's own statement of where the work is happening. Absent ⇒ this sandbox.
945
- remote: z.string().optional(),
946
- }),
947
- // Emitted after a clean isolated turn whose delta auto-landed (or failed to): landed ⇒ the work is now
948
- // UNCOMMITTED changes in the main tree (the Changes panel is the review); conflicts ⇒ it stayed safely in
949
- // the worktree, and each named path carries WHY it would not apply (see LandConflictSchema) so the report
950
- // can say whether the user's own copy is at risk or the main line simply moved on underneath the agent.
951
- // held ⇒ auto-land is off for this agent: nothing was applied and nothing failed, the delta is waiting
952
- // on the branch for a deliberate Land (landed is false, conflicts absent).
953
- // `deps` rides along when the landed delta left the main tree declaring dependencies it does not have,
954
- // the residue of an agent adding one without installing it, which every LATER turn would inherit through
955
- // the overlay it mounts over the main checkout. The daemon reconciles it rather than asking anyone to
956
- // (workspace/reconcile-deps.ts); this is the receipt, and `deferred` is the honest answer while other turns
957
- // are still running, since an install cannot touch a tree they are mounted on.
958
- z.object({
959
- kind: z.literal("landed"),
960
- landed: z.boolean(),
961
- conflicts: z.array(LandConflictSchema).optional(),
962
- held: z.boolean().optional(),
963
- deps: z.object({ missing: z.number(), started: z.array(z.string()), deferred: z.boolean() }).optional(),
964
- }),
965
- /* WHAT THE DAEMON ADDED TO THE USER'S MESSAGE before the model read it, the exact words, not a summary of
966
- * them.
967
- *
968
- * A turn's prompt is not only what was typed: the daemon prepends notes the model needs and the user did not
969
- * write (agent/turn-preamble.ts owns the list, a rebase that moved the branch, dependencies that are behind,
970
- * workspace context retrieved for this very message, where an unenforced runtime's files really live). Those
971
- * notes change what the agent does, and for a long time the chat's only trace of any of them was one muted
972
- * line paraphrasing the rebase, so a user watching an agent act on instructions they could not see had no
973
- * way to find out what those instructions said. This frame is the fix: the note text verbatim, one entry per
974
- * note, rendered collapsed so it costs a click rather than a scroll.
975
- *
976
- * Emitted from the TYPED notes the wire prompt is serialized from at the same point (turn-preamble.ts,
977
- * composeWirePrompt), so the disclosure and what the model receives cannot drift: a note is in both or in
978
- * neither, and a note nobody thought to title cannot reach the wire unlabelled.
979
- *
980
- * ONE MOMENT, always: the notes went in front of the user's own message before the turn started, so they hang
981
- * off that message and are stored on it, the transcript fold reads this very frame out of the turn's own
982
- * frame log (sessions/turn-transcript.ts), which is how a reopened tab still has them. Nothing is injected
983
- * into a RUNNING turn, the rebase taken while a card sat waiting was the only thing that ever was, and it no
984
- * longer says anything to the model at all (agent/turn-preamble.ts). */
985
- z.object({ kind: z.literal("preamble"), notes: z.array(TurnNoteSchema) }),
986
- // The SDK's init handshake; carries the model it actually resolved for the turn.
987
- z.object({ kind: z.literal("init"), model: z.string() }),
988
- // The pre-turn workspace snapshot's id (the attribution-fence "user" capture), emitted once before the
989
- // provider stream so the client can offer "restore to before this message" on the turn's user bubble.
990
- // Absent on isolated turns (they snapshot nothing) and when the tree was already clean at turn start.
991
- /* The workspace checkpoint capturing the state as this turn FOUND it, what "go back to before this
992
- * message" restores. `index` is the message's position in the conversation's transcript, which the rewind
993
- * route addresses it by; absent on a turn with no conversation behind it (the bench, a one-shot), where
994
- * the id still powers a plain restore but there is no message to rewind to. */
995
- z.object({ kind: z.literal("checkpoint"), id: z.string(), index: z.number().int().nonnegative().optional() }),
996
- /* A MESSAGE THE USER SENT INTO THE TURN WHILE IT RAN, the mid-turn steer, at the point in the stream where
997
- * the daemon accepted it (agent/agent-steering.ts).
998
- *
999
- * A frame rather than a client-local write, because all three things that were wrong about the steer are the
1000
- * same missing fact: nothing in the run's log said WHEN it arrived.
1001
- * - POSITION. The harness injects a steer between tool calls and the model simply keeps writing, with no
1002
- * `result` in between, so there is no `usage` boundary to retire the open bubble. The sending window
1003
- * appended the user's words at the END of its transcript while the turn kept typing into the bubble
1004
- * ABOVE them, and the answer to a question landed over the question.
1005
- * - EVERY OTHER WINDOW. A run is rendered by any number of attached clients; only the one that posted the
1006
- * steer knew about it, so the same conversation read differently in two places.
1007
- * - THE RECORD. The settled turn is written down from this log (sessions/turn-transcript.ts), and one that
1008
- * never held the steer wrote a transcript the message was missing from entirely, which also put the
1009
- * client's row count one ahead of the daemon's for the rest of the conversation, and those counts are
1010
- * what a fork copies a prefix of and a rewind addresses.
1011
- *
1012
- * `text` is what the user typed, never the composed prompt: the editor-context and attachment notes the
1013
- * route wraps around it are protocol, and redrawing them as the user's words is the same lie the stored
1014
- * prompt is unwrapped to avoid. `attachments` are workspace-relative, like the turn's own. `sentAt` is the
1015
- * instant the turn took the message, carried so the bubble wears the same clock live and after a reopen,
1016
- * a turn's own user row is stamped from the daemon's clock too, and a live bubble stamped from the
1017
- * browser's would visibly jump when the record replaced it. */
1018
- z.object({ kind: z.literal("steer"), text: z.string(), sentAt: z.number(), attachments: z.array(z.string()).optional() }),
1019
- z.object({ kind: z.literal("delta"), text: z.string(), parentToolUseId: z.string().optional() }),
1020
- // The prose block the `delta` frames were writing is finished. A turn emits several: the model says what
1021
- // it is about to do, runs tools, reports what it found, runs more, then summarizes, each a separate text
1022
- // block in the SDK stream. Without this boundary the client has no way to tell them apart and glues the
1023
- // whole turn's narration into one paragraph run, so the client retires its current bubble here and lets
1024
- // what follows (the tool calls this block introduced, or the next block of prose) open a fresh one.
1025
- z.object({ kind: z.literal("text_end"), parentToolUseId: z.string().optional() }),
1026
- z.object({ kind: z.literal("thinking"), text: z.string(), parentToolUseId: z.string().optional() }),
1027
- // A tool call starting (or, for backends that only report completions, arriving whole). `content` carries
1028
- // structured output known at call time, an Edit's diff is derived from its input, no result needed.
1029
- z.object({
1030
- kind: z.literal("tool_call"),
1031
- id: z.string(),
1032
- name: z.string(),
1033
- category: ToolKindSchema,
1034
- status: ToolCallStatusSchema,
1035
- target: z.string().optional(),
1036
- locations: z.array(ToolCallLocationSchema).optional(),
1037
- content: z.array(ToolCallContentSchema).optional(),
1038
- parentToolUseId: z.string().optional(),
1039
- }),
1040
- // A later state of a tool call, correlated by `id`. N updates per call: status transitions and/or fresh
1041
- // content/locations, both REPLACE the prior value (snapshot semantics, not append); absent ⇒ unchanged.
1042
- z.object({
1043
- kind: z.literal("tool_call_update"),
1044
- id: z.string(),
1045
- status: ToolCallStatusSchema.optional(),
1046
- content: z.array(ToolCallContentSchema).optional(),
1047
- locations: z.array(ToolCallLocationSchema).optional(),
1048
- }),
1049
- // The agent just started running Bash in its live `agent-<id>` tmux session, the client surfaces that
1050
- // terminal in the global panel. One per turn (the session is reused across a turn's commands, incl. subagents').
1051
- z.object({ kind: z.literal("terminal"), session: z.string() }),
1052
- // The agent just used a browser tool, its Chromium is coming up (or already is) behind a watchable
1053
- // `browser-<id>` session, and the client surfaces it in the same panel as the terminals. One per turn, for
1054
- // the same reason: one browser serves every browser call the turn makes.
1055
- z.object({ kind: z.literal("browser"), session: z.string() }),
1056
- /* THE AGENT STARTED ANOTHER AGENT, an Agent/Task subagent, or a Codex/Grok CLI it drove from its own Bash
1057
- * (see SubagentSessionSchema). One `subagent` frame per child, then `subagent_update` as it works: the same
1058
- * call/update pair `tool_call`/`tool_call_update` uses, and for the same reason, the fields that move
1059
- * (status, spend, what it is doing) arrive many times and must REPLACE, while the fields that identify it are
1060
- * said once.
1061
- *
1062
- * `id` is the SPAWNING TOOL CALL's id, the same id the client already nests the child's inner frames under
1063
- * (`parentToolUseId`), so both frames land on the card that spawned the child by the lookup that is already
1064
- * there (mapToolAnywhere). No second correlation, and nothing to get wrong.
1065
- *
1066
- * These exist because the SDK's task messages were dropped. A BACKGROUNDED child (the Agent tool's default)
1067
- * emits its tool_use and then nothing until its result lands, which for a long child is minutes of a spinner
1068
- * that cannot say whether anything is happening. */
1069
- z.object({
1070
- kind: z.literal("subagent"),
1071
- id: z.string(),
1072
- subagentKind: SubagentKindSchema,
1073
- agentType: z.string().optional(),
1074
- description: z.string().optional(),
1075
- model: z.string().optional(),
1076
- // Which provider serves a `spawned` child (SubagentSessionSchema.provider), absent for an SDK
1077
- // subagent, whose provider is its parent's.
1078
- provider: z.string().optional(),
1079
- background: z.boolean().optional(),
1080
- }),
1081
- z.object({
1082
- kind: z.literal("subagent_update"),
1083
- id: z.string(),
1084
- status: SubagentStatusSchema.optional(),
1085
- tokens: z.number().optional(),
1086
- toolUses: z.number().optional(),
1087
- lastTool: z.string().optional(),
1088
- summary: z.string().optional(),
1089
- error: z.string().optional(),
1090
- // Whether anything checked the work the report describes (SubagentVerificationSchema). Rides the frame
1091
- // that ENDS the child, beside the report itself, so the card carries both at once.
1092
- verification: SubagentVerificationSchema.optional(),
1093
- }),
1094
- z.object({ kind: z.literal("todos"), items: z.array(TodoItemSchema) }),
1095
- // The provider's own slash commands (ACP available_commands_update), replaced whole each time, the
1096
- // composer's `/` popover lists them; invoking one is plain `/name …` prompt text (the ACP convention).
1097
- z.object({ kind: z.literal("commands"), items: z.array(AgentCommandSchema) }),
1098
- z.object({
1099
- kind: z.literal("usage"),
1100
- // The account that served this turn, the client attributes the totals to it (tagged by streamAgent).
1101
- account: z.string().optional(),
1102
- costUsd: z.number().optional(),
1103
- inputTokens: z.number().optional(),
1104
- outputTokens: z.number().optional(),
1105
- // Provider prompt-cache buckets for the turn: tokens served from cache (read) and written to cache
1106
- // (creation). Optional per provider. Codex reports only cached input (read); runtimes/turns that
1107
- // don't report a bucket omit it. Lets the client show cache hit rate = read / (read + input).
1108
- cacheReadTokens: z.number().optional(),
1109
- cacheCreationTokens: z.number().optional(),
1110
- durationMs: z.number().optional(),
1111
- numTurns: z.number().optional(),
1112
- }),
1113
- // The live gate: the provider's answer to "may this turn run", pushed mid-turn. Drives the rate-limited
1114
- // notice, not the headroom readouts, see RateLimitInfoSchema.
1115
- RateLimitInfoSchema.extend({ kind: z.literal("rate_limit_info"), account: z.string().optional() }),
1116
- /* WHAT SPEED THIS TURN ACTUALLY RAN AT, and when it isn't the one asked for, why. Emitted only when the
1117
- * answer CHANGES within a turn, so the ordinary case is one frame at init and nothing after it; a turn that
1118
- * enters cooldown mid-flight (fast mode has its own rate-limit pool, separate from the model's) emits a
1119
- * second.
1120
- *
1121
- * This frame exists because fast mode fails SILENTLY and for a lot of different reasons, the plan is free,
1122
- * extra usage is off, the model doesn't offer it, the turn is routed through the translator and so isn't
1123
- * first-party, an env var disables it, the pool is in cooldown. Asking for it and getting standard speed is
1124
- * indistinguishable, from the outside, from asking for it and getting it: same frames, same text, a bill
1125
- * that differs by 2x. A toggle whose effect can't be observed is worse than no toggle, so the daemon
1126
- * reports the harness's own answer rather than the client's assumption.
1127
- *
1128
- * `reason` is forwarded VERBATIM as the string the harness reported (SDK: FastModeDisabledReason) rather
1129
- * than re-typed as an enum here: the set is the vendor's and grows on their schedule, and a reason this
1130
- * build hasn't heard of should reach the user as an unfamiliar word, not fail schema validation and take
1131
- * the whole frame with it. The client maps the ones it knows to sentences and shows the rest as-is. */
1132
- z.object({
1133
- kind: z.literal("fast_mode"),
1134
- state: FastModeStateSchema,
1135
- // Absent when nothing is blocking fast mode, including on `state: "on"`, and on an `off` that simply
1136
- // wasn't asked for.
1137
- reason: z.string().optional(),
1138
- }),
1139
- /* WHAT THE COMPLEXITY JUDGE MADE OF THIS TURN, emitted once at turn start on every judged turn (that is,
1140
- * whenever settings.autoTier is not "off"), for the same reason fast_mode exists: a mechanism that can
1141
- * change what a turn runs on fails silently unless the daemon says what it decided. One tiny frame per
1142
- * turn, deliberately on the standard verdicts too, because the client's composer preview needs the
1143
- * conversation's LAST verdict (prompt-complexity.ts `afterHardTurn`) and a frame only on the interesting
1144
- * turns would leave it guessing on the common ones.
1145
- *
1146
- * `tier`/`score`/`rules` are the verdict verbatim (judgeComplexity): the rules are the named-feature
1147
- * vocabulary of ComplexityRule, carried as strings so a frame from a build with a rule this client hasn't
1148
- * heard of still parses. `model` is present only when a substitution actually applies to THIS turn, which
1149
- * is `routed` (mode on, verdict fast, something cheaper published) or `held` (the same turn the user pinned
1150
- * to their pick, see AgentTurn.tierHold): measure mode never names one because naming it would cost the
1151
- * catalog read shadow mode exists to avoid.
1152
- *
1153
- * `routed` is what HAPPENED, never implied by the verdict: a fast verdict in measure mode, under a hold, or
1154
- * with nothing cheaper published all run the user's own pick and say `routed: false`. */
1155
- z.object({
1156
- kind: z.literal("tier"),
1157
- tier: z.enum(["fast", "standard"]),
1158
- score: z.number(),
1159
- rules: z.array(z.string()),
1160
- // The cheaper model this turn ran on (routed) or would have run on (held). Absent otherwise.
1161
- model: z.string().optional(),
1162
- routed: z.boolean(),
1163
- // The user pinned this turn to their pick (AgentTurn.tierHold), so a fast verdict moved nothing.
1164
- held: z.boolean().optional(),
1165
- }),
1166
- /* The turn is alive but WAITING on the provider: a request failed transiently (5xx, 529, a dropped socket)
1167
- * and the harness is retrying it inside this same turn. A status, not a failure, nothing has been lost and
1168
- * the turn may still finish normally, so the client renders it where "thinking" goes rather than in the
1169
- * transcript.
1170
- *
1171
- * It exists because the retry budget is deliberately long (see CLAUDE_CODE_RETRY_WATCHDOG in
1172
- * harness-credentials.ts): a turn can now sit silent for minutes riding out an outage, and silence reads as
1173
- * a hang. The one action a user takes against an apparent hang is Stop, which is the only action that
1174
- * actually loses the work, so the wait has to be visible, with its own next-attempt clock.
1175
- *
1176
- * `attempt` is the harness's own counter and `maxAttempts` is the bound that will actually be honoured,
1177
- * which on the Claude path is the daemon's own cap on how deep a storm may get rather than the harness's
1178
- * far longer budget (MAX_IN_TURN_RETRIES in sdk-stream.ts, which ends the turn at the cap and hands the
1179
- * waiting to the outage breaker). `nextAttemptAt` (epoch ms) is when it will try
1180
- * again, so the readout counts down instead of freezing on a number nobody can interpret. BOTH are optional
1181
- * for the same reason, which is that each runtime publishes a different half of the wait and none of them
1182
- * publishes all of it: Claude's harness reports the delay and the bound, Codex says which attempt it is on
1183
- * and nothing else (codex-agent.ts), OpenCode names the next instant but no bound (grok-agent.ts). Inventing
1184
- * the missing half would be a countdown, or a limit, the retry never agreed to. */
1185
- z.object({
1186
- kind: z.literal("provider_retry"),
1187
- attempt: z.number(),
1188
- maxAttempts: z.number().optional(),
1189
- nextAttemptAt: z.number().optional(),
1190
- // The HTTP status behind it when there was one (529 reads as capacity, 429 as a rate limit, 500 as a
1191
- // fault, the client says which). Absent for a transport failure that never got a response, and for a
1192
- // runtime that reports the refusal as prose rather than a code (grok-agent.ts reads it back off that).
1193
- status: z.number().optional(),
1194
- }),
1195
- // Every plan-limit pool for the account that served the turn, read from the CLI's usage endpoint once the
1196
- // turn settles. `account` tags which Claude account it belongs to, so the client keys headroom by account;
1197
- // absent on an env-token turn, which has no account to attribute it to. No `measuredAt` on the wire: both
1198
- // readers stamp it on receipt, which is the read time to within the hop.
1199
- z.object({ kind: z.literal("account_usage"), account: z.string().optional(), windows: z.array(UsageWindowSchema) }),
1200
- ContextUsageSchema.extend({ kind: z.literal("context_usage") }),
1201
- z.object({ kind: z.literal("compact"), trigger: z.string(), preTokens: z.number().optional(), postTokens: z.number().optional() }),
1202
- // The four interactive cards. Each parks the turn until `POST /agent/reply` resolves its `requestId`.
1203
- PlanCardSchema,
1204
- QuestionCardSchema,
1205
- PermissionCardSchema,
1206
- // The agent's browser needs a person (see browserHelpCard for what the card carries). Not journalled for
1207
- // restore: the Chromium holding the page dies with the container.
1208
- z.object({ kind: z.literal("browser_help"), ...browserHelpCard }),
1209
- // The agent's TERMINAL needs a person (see terminalHelpCard). Not journalled for restore, and for the
1210
- // browser card's reason one door along: the pane holding the prompt belongs to a process the restart kills.
1211
- z.object({ kind: z.literal("terminal_help"), ...terminalHelpCard }),
1212
- /* A premium service run awaiting the owner's click. Raised OUTSIDE the turn generator, the daemon's
1213
- * services route parks the agent's own `services run` call and pushes this frame into the live run
1214
- * (platform/service-offer.ts), so unlike the four cards above it is not journalled for restore: its
1215
- * waiter is the CLI's held connection, which dies with the daemon, and a restored card would offer
1216
- * buttons nothing is waiting behind. Settles through the same `POST /agent/reply` as every other card. */
1217
- z.object({ kind: z.literal("service_offer"), ...serviceOfferCard }),
1218
- /* One event off an approved run's stream, pushed as the provider emits it so the settled card shows the
1219
- * run living rather than a spinner of unknowable length. Today that is `status` lines; `result` stays off
1220
- * the transcript on purpose (it is the agent's answer to act on, not the card's to duplicate), the frame
1221
- * carries the whole union so richer event kinds land here without a contract break. */
1222
- z.object({ kind: z.literal("service_event"), requestId: z.string(), event: ServiceStreamEventSchema }),
1223
- /* How an approved run ended, pushed after the platform answered so the card can settle as a receipt
1224
- * rather than a promise: `ok` served and charged, `refunded` failed to answer and charged nothing,
1225
- * `refused` the platform said no after the click (a raced-out allowance). `remaining` is the meter after,
1226
- * when the platform stated one. Skip needs no receipt, nothing happened, and `resolved` already says so. */
1227
- ServiceReceiptSchema.extend({ kind: z.literal("service_receipt"), requestId: z.string() }),
1228
- /* A missing capability asking for the owner's setup, the agent hit something this sandbox is not
1229
- * connected to and raised the card instead of describing manual steps. Raised OUTSIDE the turn generator
1230
- * exactly like the service offer above (the daemon's ask route parks the agent's `capabilities request`
1231
- * call and pushes this frame into the live run; capabilities/capability-offer.ts), so it is not
1232
- * journalled for restore either: its waiter is the CLI's held connection, which dies with the daemon.
1233
- * Settles through the same `POST /agent/reply` as every other card. */
1234
- z.object({ kind: z.literal("capability_offer"), ...capabilityOfferCard }),
1235
- /* How an accepted ask ended, pushed once the daemon stops watching for the connection: `connected`, the
1236
- * capability came live while the agent waited (`id` is the connected instance, the agent's handle for it)
1237
- *, or `unfinished`, the setup did not complete while anyone was waiting (the deadline passed, or the
1238
- * asking command died). A skip needs no outcome frame, nothing was set up, and `resolved` already says
1239
- * so. It is what settles the card's "waiting for you to finish setup" state on every surface. */
1240
- CapabilityOutcomeSchema.extend({ kind: z.literal("capability_outcome"), requestId: z.string() }),
1241
- /* A USDC payment awaiting the owner's click. Raised OUTSIDE the turn generator exactly like the service
1242
- * offer above (the daemon's wallet route parks the agent's `wallet fetch` call and pushes this frame into
1243
- * the live run; wallet/payment-offer.ts), so it is not journalled for restore either: its waiter is the
1244
- * CLI's held connection, which dies with the daemon. Settles through the same `POST /agent/reply`. */
1245
- z.object({ kind: z.literal("payment_offer"), ...paymentOfferCard }),
1246
- /* How an approved (or auto-approved) payment ended, pushed after the endpoint answered so the card can
1247
- * settle as a receipt rather than a promise: `paid`, the endpoint confirmed settlement (`transaction` is
1248
- * the onchain hash when it stated one); `failed`, the payment was refused or settlement failed, in which
1249
- * case the signed authorization expires unused and NOTHING left the wallet. A skip needs no receipt,
1250
- * nothing moved, and `resolved` already says so. */
1251
- PaymentReceiptSchema.extend({ kind: z.literal("payment_receipt"), requestId: z.string() }),
1252
- /* A GATED CREDENTIAL awaiting a NAMED person's click, the one card on this stream that is not addressed to
1253
- * the owner: the daemon holds an exit (a `{{secret:…}}` about to resolve, a browser field about to be
1254
- * typed into, a connected account about to be mounted) parked until one of the gate's approvers releases
1255
- * it (secrets/credential-gate.ts). Raised OUTSIDE the turn generator like the offers above — the exits run
1256
- * inside a PreToolUse hook and inside the daemon's own `secrets request` route — so it is not journalled
1257
- * for restore: its waiter is a held hook or a held connection, both of which die with the daemon, and the
1258
- * next use after a restart simply asks again. Settles through the same `POST /agent/reply`, which is where
1259
- * the clicker's identity is checked against `offer.approvers`. */
1260
- z.object({ kind: z.literal("credential_offer"), ...credentialOfferCard }),
1261
- /* WHO RELEASED IT, pushed the moment a person decided, so the settled card names them rather than saying
1262
- * only that something was approved: `released` with the approver's verified address, or `refused` when a
1263
- * person said no. Nothing is pushed for a card nobody answered — `resolved` already says that, and a
1264
- * deadline is not a refusal by anybody. */
1265
- CredentialReceiptSchema.extend({ kind: z.literal("credential_receipt"), requestId: z.string() }),
1266
- // The card above named by `requestId` is released, the user answered (or dismissed it, or the turn was
1267
- // stopped out from under it), so the turn is executing again. Emitted by whoever parked, the moment its
1268
- // waiter settles, because the park's END is otherwise invisible on this stream: nothing else here says
1269
- // "that card is done", and it cannot be inferred from the next frame that happens along. Frames DO arrive
1270
- // while a turn is parked, the pausing tool's own `tool_call` regularly trails its card (the SDK queues
1271
- // stream messages while dispatching an in-process MCP tool straight off the transport), and a card raised
1272
- // beside a parallel tool call sits through that tool's whole life. See agents-registry.ts, which reads
1273
- // this pair as the fleet's "needs you" state.
1274
- //
1275
- // `reply` says HOW it settled, and is what a transcript rebuilt from this log freezes the card with: a
1276
- // reload replays the run from seq 0 and a second window renders it live, so both would otherwise restore
1277
- // the card pending, offering buttons on a requestId nothing holds any more, under a transcript that has
1278
- // already moved on. It rides verbatim, exactly as the client POSTed it; absent, nobody answered (the turn
1279
- // was stopped, or died under the card), which is not a decision and must not replay as one.
1280
- z.object({ kind: z.literal("resolved"), requestId: z.string(), reply: AgentReplySchema.optional() }),
1281
- /* There was a `permission_note` frame here: a late sentence raced onto a command card that had already gone
1282
- * out, because the explanation was optional and the card must not wait for a one-shot helper rung that might
1283
- * take tens of seconds. It is gone with the setting that made it optional. The judge now decides the
1284
- * verdict, so the sentence is not a decoration arriving afterwards — it is the REASON THE CARD EXISTS, and
1285
- * a card cannot be raised before it is known. Nothing races, and `PermissionAsk.explain` is populated at
1286
- * raise time (guard/command-gate.ts). */
1287
- // The turn's permission mode, whenever it changes, the user's pick at turn start, then every move the
1288
- // AGENT makes on its own (EnterPlanMode on a request that needs thinking through, ExitPlanMode once the
1289
- // user approves). The composer's mode selector follows this, so the UI never lies about the live posture.
1290
- z.object({ kind: z.literal("mode"), mode: PermissionModeSchema }),
1291
- // `code` is a machine-readable discriminator for errors the UI reacts to programmatically (dropping a
1292
- // dead session id so the next send self-heals). Absent on plain failures.
1293
- z.object({
1294
- kind: z.literal("error"),
1295
- message: z.string(),
1296
- code: z
1297
- .enum([
1298
- "session-not-found",
1299
- "rate_limit",
1300
- // Codex ran the turn but warned about it (fallback model metadata), a notice, not a failure.
1301
- "codex-advisory",
1302
- "codex-reauth",
1303
- // The Claude subscription credential is dead (revoked, or its refresh token rejected) and only a
1304
- // reconnect fixes it. Distinct from "no account connected": the account IS there, so the UI can
1305
- // offer reconnect where the user already is and replay the message that bounced.
1306
- "claude-reauth",
1307
- // The API refused this turn's token MID-FLIGHT, nearly always one superseded by a rotation,
1308
- // which Anthropic retires the moment its successor is minted. Distinct from claude-reauth: the
1309
- // account is fine and the daemon re-mints on the spot, so this is usually a notice about a turn
1310
- // that is coming back rather than a request for the user to do anything. `autoResume` says
1311
- // which of the two: "scheduled" means the re-mint-and-re-run is armed, and its absence means
1312
- // nothing is coming (the turn was already a resume, or it ran on a credential with nothing to
1313
- // re-mint from), that is the case where reconnecting really is the fix.
1314
- "claude-token-refused",
1315
- /* THE ACCOUNT IS FINE AND STILL NOT ALLOWED TO RUN, an Anthropic organization that has turned
1316
- * Claude Code off for this seat. The token authenticates, the plan's own usage endpoint answers
1317
- * with real pools, and every turn is refused anyway, which is why it is its own code rather than
1318
- * a member of either neighbour: a spent allowance comes back on a clock and a refused credential
1319
- * comes back on a re-mint, and NEITHER of those is true here. Only an admin re-enabling access
1320
- * is, so nothing is re-run and nothing asks the user to reconnect, the one recovery that looks
1321
- * plausible and is guaranteed to waste their time. */
1322
- "claude-not-entitled",
1323
- /* The model provider itself failed transiently: 500/502/503, a 529 at capacity, a dropped
1324
- * socket, and the harness's own in-turn retries did not outlast it. Nothing about the workspace
1325
- * or the request is wrong, so the daemon remembers the turn and re-runs it on an escalating
1326
- * backoff (provider-health.ts): the frame is a notice about a turn that is coming back, and
1327
- * reaches the client as a plain failure only once the attempts are spent.
1328
- *
1329
- * ONE 4xx JOINS THEM, the provider refusing a request PARAMETER nothing here sends (its own
1330
- * cache-retention default, or one a proxy added). It wears a client error's status code and is
1331
- * still a provider fault: there is no request of the user's to fix, and the same send goes
1332
- * through moments later, so it recovers the same way (agent/failure-sentences.ts). */
1333
- "provider-outage",
1334
- // The platform-owned free-trial pool failed after its bounded key walk. Unlike provider-outage,
1335
- // this is never auto-resumed: failed calls are refunded and the user's message is held to retry.
1336
- "trial-unavailable",
1337
- // The trial answered, but the selected upstream model/request cannot run through this sandbox.
1338
- "trial-model-unavailable",
1339
- // This account's platform-owned daily trial allowance is spent until its UTC reset.
1340
- "trial-exhausted",
1341
- // The harness read the message as a slash command it doesn't have, and discarded everything
1342
- // after the name, the model never saw the message. Nothing was processed, so the client holds
1343
- // the text back instead of leaving the user to retype it (same treatment as claude-reauth).
1344
- "unknown-command",
1345
- "grok-model-invalid",
1346
- "codex-model-invalid",
1347
- /* THE MODEL IS REAL, LISTED, AND NOT THIS PLAN'S TO RUN. A routed provider's catalog is the set
1348
- * the vendor publishes, not the set the connected subscription pays for, so a picker row can be
1349
- * a model the upstream refuses on sight ("Your current subscription does not have access to
1350
- * kimi-for-coding-highspeed. Upgrade to higher-tier Kimi Code plans.").
1351
- *
1352
- * Its own code because every neighbour's recovery is wrong for it. It is not `provider-outage`,
1353
- * though that is what it wore: the translator answers a refused model with a 503, the harness
1354
- * reads a 5xx as an outage, and rides it out for the whole in-turn retry budget — two minutes of
1355
- * a spinner for a request that was refused in five milliseconds and will be refused identically
1356
- * forever, followed by an auto-resume schedule for a turn that cannot come back. It is not
1357
- * `*-model-invalid` either: nothing is misspelled and reloading the catalog re-offers the same
1358
- * row, because the vendor really does serve it — to somebody else.
1359
- *
1360
- * What changes the outcome is picking another model or buying the plan, so the sentence is the
1361
- * upstream's own (it names the tier), the client holds the words, and the daemon files the model
1362
- * as refused so the picker stops offering it (usage/model-refusals.ts). */
1363
- "model-unavailable",
1364
- /* THE MODEL CANNOT HOLD A TURN OF THIS AGENT LOOP, so the daemon refused before sending
1365
- * (agent/context-budget.ts). Its own code because none of the neighbours describes it: nothing is
1366
- * disconnected, nothing is spent, nothing comes back on a clock, and re-sending the same request
1367
- * at the same model fails identically forever. What changes the outcome is the model or the
1368
- * server's context flag, so the message names both and the client HOLDS the words: they never
1369
- * reached anything, and losing them to a configuration fact would be the one part of this that
1370
- * was our fault. */
1371
- "context-window-too-small",
1372
- "subscription-required",
1373
- "agent-busy",
1374
- /* THE SANDBOX HAS NO MEMORY LEFT TO RUN THIS TURN, refused before anything was spawned
1375
- * (platform/memory-admission.ts). Its own code because it is the only refusal here that is
1376
- * about the BOX rather than the request: the prompt, the model and the credential are all
1377
- * fine, and the identical request succeeds once something inside frees room, which is the
1378
- * opposite of context-window-too-small next door. Transient without being on a clock, so
1379
- * there is no resetsAt to offer — what changes the outcome is a turn finishing or a session
1380
- * closing, and the message says so. The client HOLDS the words for the same reason
1381
- * context-window-too-small does: they never reached a provider, and losing them to a
1382
- * capacity fact the user did not cause would be ours to answer for. */
1383
- "sandbox-memory-low",
1384
- /* THE LOOP RAN OUT OF ITERATIONS, not out of work: the runtime hit its own turn ceiling and
1385
- * stopped. Nothing failed, nothing is disconnected and nothing is spent, which is exactly why
1386
- * it needs a code of its own rather than a sentence: it is the one ending that LOOKS like a
1387
- * finished turn from the outside, and a ledger that filed it as an ordinary error told a
1388
- * reader nothing they could act on.
1389
- *
1390
- * The recovery is the user's, not the daemon's: whatever the turn was doing is half done, and
1391
- * re-running it blind would either redo the finished half or resume work nobody looked at. */
1392
- "turn-cap",
1393
- /* THE HARNESS ENDED THE TURN WITHOUT SUCCEEDING and did not say why in terms anything here
1394
- * models: an internal execution error, or a result subtype a later vendor build invented. The
1395
- * sentence carries the subtype verbatim, because that word is the only thing separating two of
1396
- * these, and a code that meant "one of several unrelated things" would be worse than none. It
1397
- * is a real classification all the same: it says the failure came from the LOOP rather than
1398
- * from the provider, the credential or the request, which rules out every recovery next door. */
1399
- "harness-incomplete",
1400
- /* THE ENGINE IS TOO OLD FOR THE MODEL, and the provider says so in the same breath as the
1401
- * version that would work ("Claude Code 2.1.233 does not support this model; version 2.1.251
1402
- * or newer is required"). Its own code because the fix is unlike every neighbour's: nothing is
1403
- * disconnected, nothing is spent, no retry of any length helps, and the thing that has to
1404
- * change is not the request but the PROGRAM running it (schemas/engines.ts).
1405
- *
1406
- * It used to be unfixable from inside a sandbox at all — the engine came with the image, so a
1407
- * whole fleet failed every turn on this model until a new image reached it. Now the daemon can
1408
- * install the version the provider named, which is why this frame carries the numbers rather
1409
- * than only the sentence: `engine` is what the card's Update button acts on. The install is
1410
- * still a person's decision, because the version that satisfies a floor is by definition one
1411
- * nobody has blessed yet. */
1412
- "engine-version-floor",
1413
- ])
1414
- .optional(),
1415
- /* engine-version-floor only: which engine is too old, what it is running, and the floor the provider
1416
- * demanded. On the wire because the recovery is a specific, offerable action — install at or above
1417
- * `floor` — and a client that had only the sentence would have to parse prose to offer it. */
1418
- engine: z
1419
- .object({
1420
- id: z.string().describe("Which engine (e.g. claude)."),
1421
- running: z.string().optional().describe("The version that was refused, when the provider named it."),
1422
- floor: z.string().describe("The lowest version the provider will accept."),
1423
- })
1424
- .optional(),
1425
- // rate_limit only: when the exhausted window reopens (epoch seconds, from the stream's own
1426
- // rate_limit_event or the account's persisted usage windows). Absent when the reset instant is unknown
1427
- // (nothing to schedule against).
1428
- resetsAt: z.number().optional(),
1429
- /* Where the daemon's resume of THIS turn stands, for the three codes that have one (provider-outage,
1430
- * claude-token-refused, rate_limit). "scheduled" = the resume is armed and this turn comes back by
1431
- * itself; "available" = the daemon remembered the failed turn and arming THIS conversation
1432
- * (AgentSummarySchema's resumeAfterOutage / resumeAfterLimit) picks up that same resume, which is what
1433
- * the offer banner hangs off, gated codes only, since a credential renewal is never gated on a posture
1434
- * at all. The two words are read against the effective posture (the conversation's override, else the
1435
- * sandbox default), so a chat armed on its own says "scheduled" while the unarmed board around it says
1436
- * "available". Absent means there is nothing automatic to resume: a limit whose reset instant nobody
1437
- * published has nothing to schedule against, and a refused credential has none once re-minting it has
1438
- * already been tried and failed.
1439
- *
1440
- * A SPENT ALLOWANCE USED TO BE ABSENT HERE BY RULE, and the rule was right about the default and wrong
1441
- * about the ceiling. The budget is the user's, so nothing fires unless they said so, which is what the
1442
- * posture is; what the old absence also cost was the case a press cannot reach, a 2am wall on a board
1443
- * nobody is watching. Both words are now honest for it: unarmed says "available", which is an offer,
1444
- * and armed says "scheduled", which the card counts down to. */
1445
- autoResume: z.enum(["scheduled", "available"]).optional(),
1446
- /* THE DAEMON IS STILL HOLDING THIS EXACT TURN, so the way on is to RE-RUN it rather than to send
1447
- * something after it. rate_limit only, and the counterpart to `autoResume` rather than a member of it:
1448
- * that field answers "is a machine bringing this back", which for a spent allowance is a posture the
1449
- * user sets and defaults to no (the allowance is their own budget to spend, turn-resume.ts). This
1450
- * answers the question that was never asked, "and if the user says go, what happens", which had exactly
1451
- * one possible answer for as long as it went unasked: a new user message reading "Continue".
1452
- *
1453
- * BOTH ANSWERS RUN THROUGH THIS FIELD, which is why it is not folded into the one above: an armed
1454
- * conversation's scheduled fire and an unarmed one's press are the same held turn re-run the same way,
1455
- * and the only difference is who says go.
1456
- *
1457
- * That answer was wrong in a way the chat could not show. The press is not a new instruction, it is the
1458
- * same one again, and appending it said otherwise to the only reader that matters: the provider session
1459
- * grew one "Continue" per press, each with a synthetic "No response requested." above it, so a chat that
1460
- * bounced four times handed the model four turns in which it appeared to have declined to answer. With
1461
- * this field the press re-runs the held turn instead, which is idempotent by construction (a second press
1462
- * finds a live turn and supersedes nothing) and leaves the transcript one row for one press.
1463
- *
1464
- * `ran` is whether the held turn got anywhere before it was refused, and it changes both what the model
1465
- * is told (RESUME_NOTES.limit vs .refused, and telling a model to carry on from work that never happened
1466
- * is how it comes to invent some) and what the strip can honestly say. A spent allowance refuses the
1467
- * FIRST request most of the time, so false is the common case, not the corner. */
1468
- held: z.object({ ran: z.boolean() }).optional(),
1469
- /* provider-outage only: the shape of the wait. `retryAt` (epoch seconds) is when the next attempt is
1470
- * due, not a fixed cadence, because an outage has no reset instant to aim at and hammering a provider
1471
- * that is down only spends tokens on refusals, so each attempt waits longer than the last
1472
- * (provider-health.ts owns the schedule).
1473
- *
1474
- * `attempt`/`maxAttempts` are on the wire so the notice can say the automation is BOUNDED. A retry that
1475
- * gives no account of how long it will keep going is the kind users switch back off the week they turn
1476
- * it on; one that says "attempt 2 of 6" is one they leave on. */
1477
- outage: z.object({ retryAt: z.number(), attempt: z.number(), maxAttempts: z.number() }).optional(),
1478
- }),
1479
- z.object({ kind: z.literal("done") }),
1480
- ]);
1481
- export type AgentEvent = z.infer<typeof AgentEventSchema>;
1482
-
1483
- /* THE FRAMES THAT ARE FACTS ABOUT THE TURN rather than words in it: which session it runs, where it stands,
1484
- * what it costs, how it failed. Everything else an AgentEvent can say is transcript, and reaches a client as
1485
- * rows and patches (TranscriptPatchSchema) after the daemon has folded it; these reach it as themselves,
1486
- * because there is nothing to fold, a client keeps them as state beside the transcript. A frame can be both,
1487
- * a `worktree` that rebased writes a notice AND says where the branch is, so the two lists overlap, and the
1488
- * fold and this list each take the half that is theirs. */
1489
- export const TURN_FACT_KINDS = [
1490
- "session",
1491
- "worktree",
1492
- "init",
1493
- "terminal",
1494
- "browser",
1495
- "commands",
1496
- "usage",
1497
- "rate_limit_info",
1498
- "fast_mode",
1499
- "tier",
1500
- "provider_retry",
1501
- "account_usage",
1502
- "context_usage",
1503
- "mode",
1504
- "error",
1505
- ] as const;
1506
- export type TurnFact = Extract<AgentEvent, { kind: (typeof TURN_FACT_KINDS)[number] }>;
1507
- export const isTurnFact = (event: AgentEvent): event is TurnFact => (TURN_FACT_KINDS as readonly string[]).includes(event.kind);
1508
- // The same members AgentEventSchema declares, picked out rather than declared twice: a fact's shape is the
1509
- // frame's shape, and a second spelling of it would be the drift the list above exists to prevent.
1510
- type AgentEventMember = (typeof AgentEventSchema.options)[number];
1511
- const factMembers = AgentEventSchema.options.filter((member) => (TURN_FACT_KINDS as readonly string[]).includes(member.shape.kind.value)) as unknown as [
1512
- AgentEventMember,
1513
- ...AgentEventMember[],
1514
- ];
1515
- export const TurnFactSchema = z.discriminatedUnion("kind", factMembers) as unknown as z.ZodType<TurnFact>;
1516
-
1517
- /* The /agent/attach stream: a head carrying the run's rows so far, then every change to them and every fact
1518
- * about the turn as each lands, then `end` when the run is over, nothing more coming. A stream that closes
1519
- * WITHOUT `end` was dropped mid-run; the client re-attaches and takes the head's rows again, whole, which is
1520
- * what makes attaching idempotent: a window never re-folds what it has already drawn, it replaces it.
1521
- *
1522
- * Facts REPLAY on every attach (their seq is at or below the head's), because a window joining late still has
1523
- * to learn which session the turn runs and where its branch stands; patches are only ever live (their seq is
1524
- * above the head's), because the head already holds their result. */
1525
- export const AttachFrameSchema = z.discriminatedUnion("kind", [
1526
- z.object({
1527
- kind: z.literal("attached").describe("The first frame, identifying the run you have joined and handing you its transcript so far."),
1528
- run: z.string().describe("The run's id."),
1529
- startedAt: z.number().describe("When it started, in milliseconds, so a window joining late can show how long it has been going."),
1530
- seq: z.number().describe("How many frames the run has produced so far. A fact at or below this number is being replayed; a patch is never."),
1531
- rows: z
1532
- .array(TranscriptRowSchema)
1533
- .describe("The turn's rows as they stand: what was asked, and everything the agent has said and done since. Draw these, then apply the patches that follow."),
1534
- }),
1535
- z.object({
1536
- kind: z.literal("patch").describe("One change to the run's rows."),
1537
- seq: z.number().describe("Its position in the run, counting from one."),
1538
- patch: TranscriptPatchSchema,
1539
- }),
1540
- z.object({
1541
- kind: z.literal("fact").describe("One thing about the turn that is not a row: its session, its branch, its cost, a failure."),
1542
- seq: z.number().describe("Its position in the run, counting from one. At or below the head's number, it is being replayed."),
1543
- fact: TurnFactSchema,
1544
- }),
1545
- z.object({
1546
- kind: z
1547
- .literal("end")
1548
- .describe(
1549
- "The run is over and every frame has been delivered. A stream that closes without this was dropped mid-run, so re-attach rather than assuming the turn finished.",
1550
- ),
1551
- }),
1552
- ]);
1553
- export type AttachFrame = z.infer<typeof AttachFrameSchema>;
1554
-
1555
- /* WHAT A RESUMED TURN'S PROMPT SAYS IT IS. The daemon re-runs a turn something underneath it killed (turn-resume.ts)
1556
- * by sending the original prompt again behind one of these sentences, so the model knows what interrupted it.
1557
- *
1558
- * They live on the wire rather than in the daemon because the CLIENT has to recognise them too: an attach head
1559
- * carries the run's prompt verbatim, and a window joining a resumed run would otherwise render the note as a
1560
- * message the USER wrote, the same words the user already said one run up, with a machine's preamble on them.
1561
- * Recognising the prefix is what lets that window reuse the bubble that is already there instead. */
1562
- // The instruction the three whole-turn re-runs share: what follows the note is the original request, repeated.
1563
- // `answered` deliberately does not carry it, what follows THAT note is not a repetition but the user's answer,
1564
- // and telling the model to "continue from that point instead of starting over" about words it has never seen
1565
- // is how a resume reads as the user contradicting themselves.
1566
- const REPEATED =
1567
- "The interrupted request is repeated below, where part of it was already completed in this session, continue from that point instead of starting over.";
1568
- export const RESUME_NOTES = {
1569
- auth: `The Claude credential that interrupted this conversation has been renewed, and this turn resumed automatically. ${REPEATED}`,
1570
- outage: `The model provider was briefly unavailable and interrupted this conversation; this turn resumed automatically. ${REPEATED}`,
1571
- restart: `The sandbox restarted while this turn was running, which stopped it, and this turn resumed automatically once it came back. ${REPEATED}`,
1572
- /* A SPENT ALLOWANCE STRANDS A TURN IN THREE SHAPES, and they must not share a note.
1573
- *
1574
- * `limit` is the mid-turn one and reads like its three neighbours above: the session holds real work, and
1575
- * carrying on from it is exactly right.
1576
- *
1577
- * `switched` is that same mid-turn stranding picked back up on a DIFFERENT account (or provider, or
1578
- * harness), which is what the composer's account switcher does between the refusal and the press. A session
1579
- * belongs to the credential that minted it, so this one cannot resume: it opens a fresh session seeded from
1580
- * the daemon's record. REPEATED's "already completed in this session" is therefore false where it counts —
1581
- * the work is in the carried-across conversation, not in this session's own history — and a model told to
1582
- * look for it there finds nothing and starts over silently.
1583
- *
1584
- * `refused` is the turn the provider turned away at the door, before the model read one word of it, and it
1585
- * is the COMMONER of the two, because an allowance that is already spent refuses the first request it is
1586
- * asked. REPEATED is actively wrong for it: "part of it was already completed in this session, continue from
1587
- * that point instead of starting over" is an instruction to continue from work that does not exist, and a
1588
- * model handed that instruction answers it by inventing the work. So it says the opposite, plainly.
1589
- *
1590
- * Both are unlike their neighbours in one way worth stating: nothing resumed automatically. A spent
1591
- * allowance is the user's own budget and stays their call to spend (turn-resume.ts), so what re-ran this
1592
- * turn was a person pressing Continue. */
1593
- limit: `The model provider's usage allowance ran out while this turn was running, which stopped it, and it has been sent again. ${REPEATED}`,
1594
- switched:
1595
- "The model provider's usage allowance ran out while this turn was running, which stopped it, and it has been sent again on a different account, which starts a fresh session. The conversation so far has been carried across above, including the part of the request that was already completed: continue from that point instead of starting over.",
1596
- refused:
1597
- "The model provider refused the previous attempt at this request outright, because its usage allowance was spent: no part of the request below was read or acted on, and nothing has been done towards it. It has been sent again, and starts from the beginning.",
1598
- // A turn that was PARKED on the user when the daemon died: nothing re-runs at boot, the card is restored
1599
- // instead, and this is the turn their answer starts (turn-resume.ts). What rides below the note is the
1600
- // answer itself, so the model picks the session back up at exactly the decision it had handed over.
1601
- answered:
1602
- "The sandbox restarted while this conversation was waiting for the user to respond; it is back, and their response follows below: continue from where the session left off.",
1603
- } as const;
1604
-
1605
- // The prompt a resume actually sends: the note (each carries its own account of what the words below are),
1606
- // then them.
1607
- export const withResumeNote = (prompt: string, note: string): string =>
1608
- Object.values(RESUME_NOTES).some((known) => prompt.startsWith(known)) ? prompt : `${note}\n\n${prompt}`;
1609
-
1610
- // The user's own words inside a resumed prompt, the note and its explanation stripped back off. Returns the
1611
- // prompt unchanged when it is not a resume, so a caller can hand every attach head through it.
1612
- export const withoutResumeNote = (prompt: string): string => {
1613
- const note = Object.values(RESUME_NOTES).find((known) => prompt.startsWith(known));
1614
- return note === undefined ? prompt : prompt.slice(prompt.indexOf("\n\n") + 2);
1615
- };
1616
-
1617
- export type ResumeReason = keyof typeof RESUME_NOTES;
1618
-
1619
- /* HOW A RESUMED TURN READS TO THE PERSON, the same interruption the note above tells the model, said in the
1620
- * transcript's own voice instead.
1621
- *
1622
- * Stripping the note out of the user's words is only half the job, and for years it was the only half anyone
1623
- * did: what a reopened conversation showed was a paragraph of machine prose stapled to the front of a message
1624
- * the user had already sent once, directly under their own copy of it. Both halves of that are wrong, it was
1625
- * never their sentence, and the words under it are a REPEAT rather than something new they said.
1626
- *
1627
- * So the two shapes below, which is the whole of what a reader has to be told:
1628
- *
1629
- * `notice`, the three whole-turn re-runs. The words under the note are already in the transcript one turn up,
1630
- * so the repeat is dropped entirely and the interruption takes its place as a muted line, sitting with the
1631
- * failure line it resolves ("Failed to authenticate…") and reading like every other thing that HAPPENED to a
1632
- * turn rather than like something anybody typed.
1633
- *
1634
- * `note`, the answered case, where what rides under the note is the user's actual answer to a card and belongs
1635
- * in the transcript as their words. Nothing is dropped; the explanation rides that message as a collapsed row,
1636
- * the same disclosure every other daemon-written note gets (TurnNote). */
1637
- export type ResumeDisclosure = { readonly kind: "notice"; readonly text: string } | { readonly kind: "note"; readonly note: TurnNote };
1638
-
1639
- const RESUME_DISCLOSURES: Record<ResumeReason, ResumeDisclosure> = {
1640
- auth: { kind: "notice", text: "Claude sign-in renewed, this turn picked up where it left off." },
1641
- outage: { kind: "notice", text: "The model provider came back, this turn picked up where it left off." },
1642
- restart: { kind: "notice", text: "The sandbox came back, this turn picked up where it left off." },
1643
- /* THE THREE NOBODY AUTOMATED, said in the passive voice the other three earn honestly and these do not: a
1644
- * person pressed Continue. Which is the whole reason these rows exist at all. A press used to append the word
1645
- * "Continue" as a message of its own, so a chat that bounced off a spent allowance four times read back as
1646
- * the user saying "Continue" four times to an agent that had answered none of them, and the provider session
1647
- * the model actually reads accumulated all four (plus a synthetic "No response requested." per press). One
1648
- * row for one press was never the problem; a row that claims the user said something new is.
1649
- *
1650
- * `switched` names the account because that is the fact the reader needs: they pressed the same button they
1651
- * pressed a minute ago, and the difference between the press that bounced and the press that worked is who
1652
- * served it. The line is also the only place a retired session is accounted for. */
1653
- limit: { kind: "notice", text: "Sent again after the allowance ran out mid-turn, picking up where it left off." },
1654
- switched: { kind: "notice", text: "Sent again on the switched account after the allowance ran out mid-turn, in a fresh session." },
1655
- refused: { kind: "notice", text: "Sent again after the allowance refused it: nothing had run." },
1656
- answered: { kind: "note", note: { title: "Picked back up after a sandbox restart", text: RESUME_NOTES.answered } },
1657
- };
1658
-
1659
- // What a stored prompt's resume note should be SHOWN as; undefined when the prompt is not a resume at all, so
1660
- // every reader of a stored prompt can ask without first testing whether it is one.
1661
- export const resumeDisclosure = (prompt: string): ResumeDisclosure | undefined => {
1662
- const reason = (Object.keys(RESUME_NOTES) as ResumeReason[]).find((key) => prompt.startsWith(RESUME_NOTES[key]));
1663
- return reason === undefined ? undefined : RESUME_DISCLOSURES[reason];
1664
- };
1665
-
1666
- // One parsed line from `intentic … --output ndjson` (engine events, provider `log`, the terminal `result`).
1667
- // Open-ended by design, the sandbox consumes the wire shape, not @intentic/engine's types, so a string
1668
- // `kind` plus arbitrary extra fields pass through. The apply-events tail (intentic.contract `applyEvents`) rides
1669
- // this same loose shape with three daemon/CLI-minted sentinel kinds alongside the engine ones: {kind:"start"}
1670
- // (first line, written when the run's file is reset), {kind:"exit",code} (last line, on the CLI process exit),
1671
- // and {kind:"heartbeat"} (interleaved by the tail while idle to keep the held-open stream alive).
1672
- export const IntenticLineSchema = z.looseObject({ kind: z.string() });
1673
- export type IntenticLine = z.infer<typeof IntenticLineSchema>;
1674
-
1675
- // The daemon's liveness heartbeat frame: the browser holds the events stream open and trips a watchdog if the
1676
- // frames stop (the tunnel drops the proxied response when the origin dies).
1677
- export const HeartbeatSchema = z.object({ kind: z.literal("heartbeat") });
1678
- export type Heartbeat = z.infer<typeof HeartbeatSchema>;
1679
-
1680
- // One step of the daemon's boot chain. `key` is the stable id the daemon declares it under, `label` the words
1681
- // the browser shows. A step that FAILED is still a step that finished, the boot chain is log-and-continue by
1682
- // design (see main.ts), so a failure degrades one subsystem rather than holding the gate closed forever.
1683
- export const BootStepSchema = z.object({
1684
- key: z.string(),
1685
- label: z.string(),
1686
- state: z.enum(["pending", "running", "done", "failed"]),
1687
- // Elapsed ms, once the step has finished.
1688
- ms: z.number().optional(),
1689
- });
1690
- export type BootStep = z.infer<typeof BootStepSchema>;
1691
-
1692
- /* WHERE THE DAEMON IS IN ITS BOOT. The listeners come up before the state they serve has converged (main.ts:
1693
- * "listen first, converge behind the gate"), which is what stops a restart from reading as an outage, but it
1694
- * also means the daemon spends the first seconds of every boot both reachable and unable to answer, and until
1695
- * this frame existed the browser had no way to tell that apart from a healthy sandbox. It painted an operable
1696
- * workspace off its persisted cache and then parked every request the user made against the readiness gate.
1697
- *
1698
- * The step list is declared UP FRONT and sent whole, pending entries included, so the browser can say "4 of 11,
1699
- * loading the conversation registry" rather than "something is happening", a boot that takes minutes has one
1700
- * slow step, and naming it is the whole point. Snapshot-not-diff, like every other roster on this stream. */
1701
- export const BootProgressSchema = z.object({
1702
- // False only while the chain is still converging. The browser holds every daemon read until this is true.
1703
- ready: z.boolean(),
1704
- // Epoch ms the daemon started converging, so the browser can show a total elapsed that survives a reconnect.
1705
- startedAt: z.number(),
1706
- steps: z.array(BootStepSchema),
1707
- });
1708
- export type BootProgress = z.infer<typeof BootProgressSchema>;
1709
-
1710
- // Pushed on every step transition and once more when the gate opens. Rides /events, which answers before the
1711
- // gate precisely so this can be delivered while everything else waits.
1712
- export const BootSchema = z.object({ kind: z.literal("boot"), ...BootProgressSchema.shape });
1713
- export type Boot = z.infer<typeof BootSchema>;
1714
-
1715
- // The stream's first frame: the workspace's stable identity, minted at the first boot of an empty /work. The
1716
- // browser remembers it per sandbox id and drops that sandbox's persisted query cache when it changes, a wiped
1717
- // and recreated workspace (cleanup.sh + reconnect keeps the same sandbox id) must not be painted from the
1718
- // previous workspace's cache. `build` is the same guard against a different axis: the daemon's own compiled
1719
- // tree, so an image update (or a `pnpm build:sandbox` swap in dev) drops what the browser cached from the
1720
- // PREVIOUS build instead of hydrating payloads the new one no longer shapes that way.
1721
- //
1722
- // It also advertises `routes`, the contract route names (`vpn.list`, `kimi.models`) this daemon actually
1723
- // implements, from ITS build of the contract. A browser is routinely newer than the daemon it talks to (a
1724
- // released app plane serves whatever image each user last pulled; in local dev the web app is always ahead of
1725
- // the last `pnpm build:sandbox`), and that stays fully supported, the browser just compares the two sets so a
1726
- // route the daemon predates surfaces as a named, explained gap instead of a bare 404 nobody can attribute.
1727
- //
1728
- // `shapes` answers the half `routes` structurally cannot: a route BOTH builds have, whose payload changed
1729
- // between them. Names match, so nothing 404s, the call goes out and a field the browser expects is simply
1730
- // missing from the answer. It is a map of route name → a fingerprint of that route's input and output schema
1731
- // (see routes.ts), so a difference is a named route rather than "something, somewhere, moved". Beside `routes`
1732
- // rather than folded into it: existence covers every route, shape covers only the ones that can be expressed.
1733
- //
1734
- // Every added field is optional: a daemon built before one simply says nothing, and the browser's fallback is
1735
- // the pre-existing behaviour, routes all assumed present, shapes all assumed to agree, the daemon assumed
1736
- // ready, the cache left alone. That is also why `routes` keeps its bare-string-array shape: an image already in
1737
- // the wild sends exactly that, and a breaking change here would fail the hello frame's own parse and take the
1738
- // whole event stream down for precisely the skew this frame exists to describe.
1739
- export const HelloSchema = z.object({
1740
- kind: z.literal("hello"),
1741
- workspaceId: z.string(),
1742
- routes: z.array(z.string()).optional(),
1743
- shapes: z.record(z.string(), z.string()).optional(),
1744
- build: z.string().optional(),
1745
- boot: BootProgressSchema.optional(),
1746
- });
1747
- export type Hello = z.infer<typeof HelloSchema>;
1748
-
1749
- // The FULL discovered repo set (sorted root-relative ids), pushed whenever it changes, a clone, a scaffold,
1750
- // or a deleted repo re-frames it. The watcher descent-ignores .git, so no workspaceChanged path pattern can
1751
- // detect a repo appearing; the daemon diffs its own discovery instead. Snapshot-not-diff, last frame wins.
1752
- export const ReposChangedSchema = z.object({ kind: z.literal("reposChanged"), repos: z.array(z.string()) });
1753
- export type ReposChanged = z.infer<typeof ReposChangedSchema>;
1754
-
1755
- // A batch of workspace paths that just changed on disk (created/edited/deleted), pushed on the same /events
1756
- // stream as the heartbeat so the browser refreshes the tree + any open file live, the agent edits files
1757
- // out-of-band (its own Write/Edit/Bash tools), so there's no HTTP mutation to hang an invalidate on. Paths are
1758
- // root-relative, forward-slash (the tree/file route space). An empty array means "something changed, refetch the
1759
- // tree", a burst too large to enumerate, or a reconnect recovery where we don't know what was missed.
1760
- export const WorkspaceChangedSchema = z.object({ kind: z.literal("workspaceChanged"), paths: z.array(z.string()) });
1761
- export type WorkspaceChanged = z.infer<typeof WorkspaceChangedSchema>;
1762
-
1763
- /* THE REPOS WHOSE REFS JUST MOVED, a commit, a checkout, a branch or tag, a rebase started or aborted.
1764
- *
1765
- * A third push for the same reason as the two above, and the reason is structural: a repo's git dir does not
1766
- * live under /work at all (it is relocated onto /history so an isolated turn's worktree can stand in for the
1767
- * workspace root, see git/repo-git-dirs.ts), and the file watcher descent-ignores `.git` besides. So no
1768
- * `workspaceChanged` path can ever say "a ref moved", and a surface built on the commit graph would otherwise
1769
- * be exactly as fresh as the last thing the user clicked.
1770
- *
1771
- * It matters most for the work the user did NOT do: the agent commits, rebases and lands out-of-band, with no
1772
- * HTTP mutation in any browser to hang an invalidation on. Ids are root-relative, "root" being the /work repo.
1773
- * Diff-not-snapshot, unlike reposChanged: this names what moved, and a repo absent from a frame is a repo that
1774
- * did not move rather than one that stopped existing. */
1775
- export const RefsChangedSchema = z.object({ kind: z.literal("refsChanged"), repos: z.array(z.string()) });
1776
- export type RefsChanged = z.infer<typeof RefsChangedSchema>;
1777
-
1778
- /* WHICH RUNNING THINGS JUST MOVED, a session opened or exited, a dev server bound its port, a browser closed,
1779
- * a subagent reported in.
1780
- *
1781
- * The fourth push, and the one that covers what the other three structurally cannot: none of this state is on
1782
- * disk, so no `workspaceChanged` path can name it, and none of it is a ref or a repo. Before it, every view of a
1783
- * running thing polled on its own timer, which is to say each browser asked, forever, a question only the
1784
- * daemon could answer and almost always answered "no change".
1785
- *
1786
- * Diff-not-snapshot, and deliberately thin: the frame carries the DOMAIN that moved, never the roster itself.
1787
- * Invalidation only reaches a query something is observing, so a tab showing none of these pays a frame and no
1788
- * request, whereas a roster on the wire would bill every connected browser the full list whether or not
1789
- * anything on screen renders it. Which query keys a domain stands for is runtime-state.ts's table. */
1790
- export const RuntimeChangedSchema = z.object({ kind: z.literal("runtimeChanged"), domains: z.array(z.string()) });
1791
- export type RuntimeChanged = z.infer<typeof RuntimeChangedSchema>;
1792
-
1793
- // One connected browser tab of a sandbox member. Identity fields come from the caller's verified Google ID
1794
- // token; activity fields from the tab's own /system/presence reports. No timestamps on the wire, an entry's
1795
- // lifetime IS its /events connection's lifetime, so there is nothing to age out or compare clocks over.
1796
- export const PresenceUserSchema = z.object({
1797
- // Per-CONNECTION id, minted by the browser for each /events attempt, never reused across reconnects.
1798
- clientId: z.string(),
1799
- email: z.string(),
1800
- name: z.string().optional(),
1801
- picture: z.string().optional(),
1802
- // The caller's trust tier, resolved by the authorizer at connection time. On the roster so every member
1803
- // can see who may do what, and so a tab knows its OWN role without an owner-only lookup.
1804
- role: MemberRoleSchema,
1805
- idle: z.boolean(),
1806
- // Route/view name the tab is on ("workspace", "automations", "ext:<id>/<key>", …).
1807
- view: z.string().optional(),
1808
- // The chat conversation the tab has active.
1809
- sessionId: z.string().optional(),
1810
- // The workspace file the tab has open (root-relative, forward-slash).
1811
- path: z.string().optional(),
1812
- });
1813
- export type PresenceUser = z.infer<typeof PresenceUserSchema>;
1814
-
1815
- // The FULL roster of connected members, broadcast on every change, snapshots, not diffs, so a reconnecting
1816
- // browser is consistent from its first frame and ordering never matters (last frame wins).
1817
- export const PresenceSchema = z.object({ kind: z.literal("presence"), users: z.array(PresenceUserSchema) });
1818
- export type Presence = z.infer<typeof PresenceSchema>;
1819
-
1820
- // The FULL fleet roster, broadcast on every registry change, same snapshot-not-diff contract as presence:
1821
- // a reconnecting browser is consistent from its first frame. NOT simply "last frame wins", though: `rev` is the
1822
- // registry revision the snapshot was taken at, and the browser applies a frame only if it is newer than the one
1823
- // it already holds. Snapshots race two other sources of the same fact, an explicit GET /agents and the
1824
- // browser's own optimistic writes, and an unordered full replace lets the slowest of them win, which is how an
1825
- // archived card came back. See AgentsListSchema and useAgents.ts.
1826
- export const AgentsSchema = z.object({ kind: z.literal("agents"), agents: z.array(AgentSummarySchema), rev: z.number() });
1827
- export type Agents = z.infer<typeof AgentsSchema>;
1828
-
1829
- /* AN ACCOUNT'S HEADROOM JUST MOVED, the reading itself, keyed the way the daemon's store keys it (a Claude
1830
- * account id, or `${provider}:${authFile}` for a routed subscription).
1831
- *
1832
- * The fifth push, and the one that lets every ring, rail and picker row stop refetching on mount. A reading
1833
- * lands on the daemon for one of four reasons, a turn settled, a plan refused, a screen asked, a provider
1834
- * pushed, and until this frame existed only the window that caused it ever heard: every other window drew the
1835
- * number it had loaded that morning until something in it happened to remount. Snapshot-not-diff per account,
1836
- * last frame wins, and a browser that missed one simply holds the older reading, which is what `measuredAt`
1837
- * is for. `usage` absent ⇒ the account's snapshot was cleared (it was disconnected). */
1838
- export const AccountUsageChangedSchema = z.object({
1839
- kind: z.literal("accountUsage"),
1840
- // The provider whose row this account is, because the key alone does not say (a native id is bare).
1841
- provider: z.string(),
1842
- account: z.string(),
1843
- usage: AccountUsageSchema.optional(),
1844
- });
1845
- export type AccountUsageChanged = z.infer<typeof AccountUsageChangedSchema>;
1846
-
1847
- // A provider's last refusal was recorded or settled. The observed half of "can I run on this" (see
1848
- // ProviderRefusalSchema), pushed for the same reason the reading above is: a refusal at 4am used to reach a
1849
- // window only when it next reloaded its account rows. `refusal` absent ⇒ settled, nothing stands.
1850
- export const ProviderRefusalChangedSchema = z.object({ kind: z.literal("providerRefusal"), provider: z.string(), refusal: ProviderRefusalSchema.optional() });
1851
- export type ProviderRefusalChanged = z.infer<typeof ProviderRefusalChangedSchema>;
1852
-
1853
- // The /events stream union: the hello identity frame, then liveness heartbeats interleaved with boot progress,
1854
- // workspace-change batches, repo-set snapshots, ref-move batches, runtime-domain nudges, presence + fleet
1855
- // roster snapshots, and account headroom / refusal changes. oRPC validates every yielded frame against this,
1856
- // so all kinds must live here.
1857
- export const SystemEventSchema = z.discriminatedUnion("kind", [
1858
- HelloSchema,
1859
- HeartbeatSchema,
1860
- BootSchema,
1861
- WorkspaceChangedSchema,
1862
- ReposChangedSchema,
1863
- RefsChangedSchema,
1864
- RuntimeChangedSchema,
1865
- PresenceSchema,
1866
- AgentsSchema,
1867
- AccountUsageChangedSchema,
1868
- ProviderRefusalChangedSchema,
1869
- ]);
1870
- export type SystemEvent = z.infer<typeof SystemEventSchema>;