@intentic/sandbox-contract 1.331.0 → 1.333.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 (416) hide show
  1. package/README.md +23 -1
  2. package/dist/chores/probes.d.ts.map +1 -1
  3. package/dist/chores/probes.js.map +1 -1
  4. package/dist/contracts/accounts.contract.d.ts +11 -0
  5. package/dist/contracts/accounts.contract.d.ts.map +1 -1
  6. package/dist/contracts/agent.contract.d.ts +38 -0
  7. package/dist/contracts/agent.contract.d.ts.map +1 -1
  8. package/dist/contracts/agent.contract.js +14 -3
  9. package/dist/contracts/agent.contract.js.map +1 -1
  10. package/dist/contracts/agents.contract.d.ts +29 -0
  11. package/dist/contracts/agents.contract.d.ts.map +1 -1
  12. package/dist/contracts/automations.contract.d.ts +3 -0
  13. package/dist/contracts/automations.contract.d.ts.map +1 -1
  14. package/dist/contracts/device.contract.d.ts +7 -0
  15. package/dist/contracts/device.contract.d.ts.map +1 -1
  16. package/dist/contracts/git.contract.d.ts +2 -0
  17. package/dist/contracts/git.contract.d.ts.map +1 -1
  18. package/dist/contracts/logs.contract.d.ts +10 -0
  19. package/dist/contracts/logs.contract.d.ts.map +1 -1
  20. package/dist/contracts/needs.contract.d.ts +36 -1
  21. package/dist/contracts/needs.contract.d.ts.map +1 -1
  22. package/dist/contracts/needs.contract.js +2 -2
  23. package/dist/contracts/needs.contract.js.map +1 -1
  24. package/dist/contracts/offload.contract.d.ts +70 -0
  25. package/dist/contracts/offload.contract.d.ts.map +1 -1
  26. package/dist/contracts/offload.contract.js +10 -1
  27. package/dist/contracts/offload.contract.js.map +1 -1
  28. package/dist/contracts/privacy.contract.d.ts +14 -0
  29. package/dist/contracts/privacy.contract.d.ts.map +1 -1
  30. package/dist/contracts/privacy.contract.js +11 -1
  31. package/dist/contracts/privacy.contract.js.map +1 -1
  32. package/dist/contracts/providers.contract.d.ts +3 -0
  33. package/dist/contracts/providers.contract.d.ts.map +1 -1
  34. package/dist/contracts/runner.contract.d.ts +103 -2
  35. package/dist/contracts/runner.contract.d.ts.map +1 -1
  36. package/dist/contracts/runner.contract.js +2 -2
  37. package/dist/contracts/runner.contract.js.map +1 -1
  38. package/dist/contracts/sessions.contract.d.ts +1 -0
  39. package/dist/contracts/sessions.contract.d.ts.map +1 -1
  40. package/dist/contracts/settings.contract.d.ts +106 -94
  41. package/dist/contracts/settings.contract.d.ts.map +1 -1
  42. package/dist/contracts/settings.contract.js +19 -1
  43. package/dist/contracts/settings.contract.js.map +1 -1
  44. package/dist/contracts/system.contract.d.ts +17 -0
  45. package/dist/contracts/system.contract.d.ts.map +1 -1
  46. package/dist/contracts/translator.contract.d.ts +4 -0
  47. package/dist/contracts/translator.contract.d.ts.map +1 -1
  48. package/dist/events/agent-events.d.ts +46 -0
  49. package/dist/events/agent-events.d.ts.map +1 -1
  50. package/dist/events/agent-events.js +6 -0
  51. package/dist/events/agent-events.js.map +1 -1
  52. package/dist/events/agent-words.d.ts.map +1 -1
  53. package/dist/events/agent-words.js +1 -1
  54. package/dist/events/agent-words.js.map +1 -1
  55. package/dist/events/resume.d.ts +3 -0
  56. package/dist/events/resume.d.ts.map +1 -1
  57. package/dist/events/resume.js +17 -2
  58. package/dist/events/resume.js.map +1 -1
  59. package/dist/events/sandbox-notice.d.ts +18 -0
  60. package/dist/events/sandbox-notice.d.ts.map +1 -1
  61. package/dist/events/sandbox-notice.js +27 -1
  62. package/dist/events/sandbox-notice.js.map +1 -1
  63. package/dist/events/system-events.d.ts +2 -0
  64. package/dist/events/system-events.d.ts.map +1 -1
  65. package/dist/events/transcript.d.ts +64 -0
  66. package/dist/events/transcript.d.ts.map +1 -1
  67. package/dist/events/transcript.js +17 -1
  68. package/dist/events/transcript.js.map +1 -1
  69. package/dist/index.d.ts +427 -167
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +12 -0
  72. package/dist/index.js.map +1 -1
  73. package/dist/models/agent-catalog.d.ts.map +1 -1
  74. package/dist/models/agent-catalog.js +8 -7
  75. package/dist/models/agent-catalog.js.map +1 -1
  76. package/dist/models/agent-runtimes.d.ts +3 -1
  77. package/dist/models/agent-runtimes.d.ts.map +1 -1
  78. package/dist/models/agent-runtimes.js +10 -0
  79. package/dist/models/agent-runtimes.js.map +1 -1
  80. package/dist/models/model-order.d.ts +1 -0
  81. package/dist/models/model-order.d.ts.map +1 -1
  82. package/dist/models/model-order.js +9 -0
  83. package/dist/models/model-order.js.map +1 -1
  84. package/dist/models/model-pins.d.ts.map +1 -1
  85. package/dist/models/model-pins.js.map +1 -1
  86. package/dist/models/model-roles.d.ts +33 -18
  87. package/dist/models/model-roles.d.ts.map +1 -1
  88. package/dist/models/model-roles.js +31 -27
  89. package/dist/models/model-roles.js.map +1 -1
  90. package/dist/models/model-setups.d.ts +46 -0
  91. package/dist/models/model-setups.d.ts.map +1 -0
  92. package/dist/models/model-setups.js +86 -0
  93. package/dist/models/model-setups.js.map +1 -0
  94. package/dist/models/plan-pools.d.ts +3 -1
  95. package/dist/models/plan-pools.d.ts.map +1 -1
  96. package/dist/models/plan-pools.js +18 -6
  97. package/dist/models/plan-pools.js.map +1 -1
  98. package/dist/models/provider-specs.d.ts +20 -0
  99. package/dist/models/provider-specs.d.ts.map +1 -1
  100. package/dist/models/provider-specs.js +13 -1
  101. package/dist/models/provider-specs.js.map +1 -1
  102. package/dist/policy/command-classes.d.ts.map +1 -1
  103. package/dist/policy/command-classes.js +26 -3
  104. package/dist/policy/command-classes.js.map +1 -1
  105. package/dist/policy/control-scopes.d.ts +47 -0
  106. package/dist/policy/control-scopes.d.ts.map +1 -1
  107. package/dist/policy/control-scopes.js +16 -0
  108. package/dist/policy/control-scopes.js.map +1 -1
  109. package/dist/policy/credential-material.d.ts +6 -0
  110. package/dist/policy/credential-material.d.ts.map +1 -1
  111. package/dist/policy/credential-material.js +37 -15
  112. package/dist/policy/credential-material.js.map +1 -1
  113. package/dist/policy/device-programs.d.ts +28 -0
  114. package/dist/policy/device-programs.d.ts.map +1 -0
  115. package/dist/policy/device-programs.js +79 -0
  116. package/dist/policy/device-programs.js.map +1 -0
  117. package/dist/policy/identity-assertion.d.ts +27 -0
  118. package/dist/policy/identity-assertion.d.ts.map +1 -0
  119. package/dist/policy/identity-assertion.js +77 -0
  120. package/dist/policy/identity-assertion.js.map +1 -0
  121. package/dist/policy/sandbox-power.d.ts +34 -0
  122. package/dist/policy/sandbox-power.d.ts.map +1 -0
  123. package/dist/policy/sandbox-power.js +146 -0
  124. package/dist/policy/sandbox-power.js.map +1 -0
  125. package/dist/policy/search-globs.d.ts +3 -0
  126. package/dist/policy/search-globs.d.ts.map +1 -1
  127. package/dist/policy/search-globs.js +118 -0
  128. package/dist/policy/search-globs.js.map +1 -1
  129. package/dist/policy/secret-hosts.d.ts +1 -0
  130. package/dist/policy/secret-hosts.d.ts.map +1 -1
  131. package/dist/policy/secret-hosts.js +1 -0
  132. package/dist/policy/secret-hosts.js.map +1 -1
  133. package/dist/policy/turned-away.d.ts.map +1 -1
  134. package/dist/policy/turned-away.js +1 -0
  135. package/dist/policy/turned-away.js.map +1 -1
  136. package/dist/protocol/doors/runner-protocol.d.ts +45 -0
  137. package/dist/protocol/doors/runner-protocol.d.ts.map +1 -1
  138. package/dist/protocol/doors/runner-protocol.js +7 -0
  139. package/dist/protocol/doors/runner-protocol.js.map +1 -1
  140. package/dist/protocol/pairing-redeem.d.ts +23 -0
  141. package/dist/protocol/pairing-redeem.d.ts.map +1 -0
  142. package/dist/protocol/pairing-redeem.js +44 -0
  143. package/dist/protocol/pairing-redeem.js.map +1 -0
  144. package/dist/protocol/peer-mcp-server.d.ts.map +1 -1
  145. package/dist/protocol/peer-mcp-server.js +1 -1
  146. package/dist/protocol/peer-mcp-server.js.map +1 -1
  147. package/dist/protocol/raw/raw-json-routes.d.ts +855 -1
  148. package/dist/protocol/raw/raw-json-routes.d.ts.map +1 -1
  149. package/dist/protocol/raw/raw-json-routes.js +61 -1
  150. package/dist/protocol/raw/raw-json-routes.js.map +1 -1
  151. package/dist/protocol/raw/raw-routes.d.ts +17 -0
  152. package/dist/protocol/raw/raw-routes.d.ts.map +1 -1
  153. package/dist/protocol/raw/raw-routes.js +5 -0
  154. package/dist/protocol/raw/raw-routes.js.map +1 -1
  155. package/dist/protocol/sse.d.ts +20 -1
  156. package/dist/protocol/sse.d.ts.map +1 -1
  157. package/dist/protocol/sse.js +112 -17
  158. package/dist/protocol/sse.js.map +1 -1
  159. package/dist/schemas/agent.d.ts +35 -0
  160. package/dist/schemas/agent.d.ts.map +1 -1
  161. package/dist/schemas/agent.js +12 -1
  162. package/dist/schemas/agent.js.map +1 -1
  163. package/dist/schemas/automations.d.ts +7 -0
  164. package/dist/schemas/automations.d.ts.map +1 -1
  165. package/dist/schemas/automations.js +11 -3
  166. package/dist/schemas/automations.js.map +1 -1
  167. package/dist/schemas/device-swaps.d.ts +19 -0
  168. package/dist/schemas/device-swaps.d.ts.map +1 -0
  169. package/dist/schemas/device-swaps.js +11 -0
  170. package/dist/schemas/device-swaps.js.map +1 -0
  171. package/dist/schemas/devices.d.ts +17 -0
  172. package/dist/schemas/devices.d.ts.map +1 -1
  173. package/dist/schemas/devices.js +2 -1
  174. package/dist/schemas/devices.js.map +1 -1
  175. package/dist/schemas/engines.d.ts +8 -1
  176. package/dist/schemas/engines.d.ts.map +1 -1
  177. package/dist/schemas/engines.js +1 -1
  178. package/dist/schemas/engines.js.map +1 -1
  179. package/dist/schemas/environment.d.ts +7 -0
  180. package/dist/schemas/environment.d.ts.map +1 -1
  181. package/dist/schemas/environment.js +2 -0
  182. package/dist/schemas/environment.js.map +1 -1
  183. package/dist/schemas/git/git-scope.d.ts +20 -0
  184. package/dist/schemas/git/git-scope.d.ts.map +1 -0
  185. package/dist/schemas/git/git-scope.js +37 -0
  186. package/dist/schemas/git/git-scope.js.map +1 -0
  187. package/dist/schemas/git/git.d.ts +1 -0
  188. package/dist/schemas/git/git.d.ts.map +1 -1
  189. package/dist/schemas/git/git.js +9 -1
  190. package/dist/schemas/git/git.js.map +1 -1
  191. package/dist/schemas/hosts.d.ts +42 -0
  192. package/dist/schemas/hosts.d.ts.map +1 -1
  193. package/dist/schemas/hosts.js +3 -0
  194. package/dist/schemas/hosts.js.map +1 -1
  195. package/dist/schemas/keep-warm.d.ts +1 -0
  196. package/dist/schemas/keep-warm.d.ts.map +1 -1
  197. package/dist/schemas/keep-warm.js +1 -1
  198. package/dist/schemas/keep-warm.js.map +1 -1
  199. package/dist/schemas/logs.d.ts +21 -0
  200. package/dist/schemas/logs.d.ts.map +1 -1
  201. package/dist/schemas/logs.js +19 -0
  202. package/dist/schemas/logs.js.map +1 -1
  203. package/dist/schemas/members.d.ts +4 -0
  204. package/dist/schemas/members.d.ts.map +1 -1
  205. package/dist/schemas/members.js +5 -0
  206. package/dist/schemas/members.js.map +1 -1
  207. package/dist/schemas/metrics.d.ts +12 -0
  208. package/dist/schemas/metrics.d.ts.map +1 -1
  209. package/dist/schemas/metrics.js +26 -8
  210. package/dist/schemas/metrics.js.map +1 -1
  211. package/dist/schemas/needs.d.ts +97 -1
  212. package/dist/schemas/needs.d.ts.map +1 -1
  213. package/dist/schemas/needs.js +53 -4
  214. package/dist/schemas/needs.js.map +1 -1
  215. package/dist/schemas/offload.d.ts +154 -0
  216. package/dist/schemas/offload.d.ts.map +1 -1
  217. package/dist/schemas/offload.js +34 -0
  218. package/dist/schemas/offload.js.map +1 -1
  219. package/dist/schemas/privacy.d.ts +19 -0
  220. package/dist/schemas/privacy.d.ts.map +1 -1
  221. package/dist/schemas/privacy.js +17 -2
  222. package/dist/schemas/privacy.js.map +1 -1
  223. package/dist/schemas/project-bring-back.d.ts +172 -0
  224. package/dist/schemas/project-bring-back.d.ts.map +1 -0
  225. package/dist/schemas/project-bring-back.js +39 -0
  226. package/dist/schemas/project-bring-back.js.map +1 -0
  227. package/dist/schemas/providers/handoff.d.ts +77 -0
  228. package/dist/schemas/providers/handoff.d.ts.map +1 -0
  229. package/dist/schemas/providers/handoff.js +85 -0
  230. package/dist/schemas/providers/handoff.js.map +1 -0
  231. package/dist/schemas/providers/plan-limits.d.ts +27 -0
  232. package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
  233. package/dist/schemas/providers/plan-limits.js +8 -1
  234. package/dist/schemas/providers/plan-limits.js.map +1 -1
  235. package/dist/schemas/providers/provider-oauth.d.ts +4 -0
  236. package/dist/schemas/providers/provider-oauth.d.ts.map +1 -1
  237. package/dist/schemas/providers/usage.d.ts +3 -1
  238. package/dist/schemas/providers/usage.d.ts.map +1 -1
  239. package/dist/schemas/providers/usage.js +7 -2
  240. package/dist/schemas/providers/usage.js.map +1 -1
  241. package/dist/schemas/restart-intent.d.ts +47 -0
  242. package/dist/schemas/restart-intent.d.ts.map +1 -0
  243. package/dist/schemas/restart-intent.js +19 -0
  244. package/dist/schemas/restart-intent.js.map +1 -0
  245. package/dist/schemas/run-targets.d.ts.map +1 -1
  246. package/dist/schemas/run-targets.js +5 -2
  247. package/dist/schemas/run-targets.js.map +1 -1
  248. package/dist/schemas/settings-history.d.ts +1 -1
  249. package/dist/schemas/settings-history.d.ts.map +1 -1
  250. package/dist/schemas/settings-history.js +21 -0
  251. package/dist/schemas/settings-history.js.map +1 -1
  252. package/dist/schemas/settings.d.ts +101 -100
  253. package/dist/schemas/settings.d.ts.map +1 -1
  254. package/dist/schemas/settings.js +43 -24
  255. package/dist/schemas/settings.js.map +1 -1
  256. package/dist/schemas/short-codes.d.ts +47 -0
  257. package/dist/schemas/short-codes.d.ts.map +1 -0
  258. package/dist/schemas/short-codes.js +30 -0
  259. package/dist/schemas/short-codes.js.map +1 -0
  260. package/dist/schemas/speech.d.ts +4 -0
  261. package/dist/schemas/speech.d.ts.map +1 -1
  262. package/dist/schemas/speech.js +1 -0
  263. package/dist/schemas/speech.js.map +1 -1
  264. package/dist/schemas/system.d.ts +176 -0
  265. package/dist/schemas/system.d.ts.map +1 -1
  266. package/dist/schemas/system.js +55 -2
  267. package/dist/schemas/system.js.map +1 -1
  268. package/dist/schemas/voice.d.ts +171 -0
  269. package/dist/schemas/voice.d.ts.map +1 -0
  270. package/dist/schemas/voice.js +99 -0
  271. package/dist/schemas/voice.js.map +1 -0
  272. package/dist/schemas/workspace/workspace-upload.d.ts +14 -0
  273. package/dist/schemas/workspace/workspace-upload.d.ts.map +1 -0
  274. package/dist/schemas/workspace/workspace-upload.js +6 -0
  275. package/dist/schemas/workspace/workspace-upload.js.map +1 -0
  276. package/dist/state/arrival.d.ts +3 -0
  277. package/dist/state/arrival.d.ts.map +1 -1
  278. package/dist/state/arrival.js +1 -0
  279. package/dist/state/arrival.js.map +1 -1
  280. package/dist/state/definition.d.ts +112 -40
  281. package/dist/state/definition.d.ts.map +1 -1
  282. package/dist/state/history-state.d.ts.map +1 -1
  283. package/dist/state/history-state.js +3 -0
  284. package/dist/state/history-state.js.map +1 -1
  285. package/dist/state/runtime-state.d.ts +5 -2
  286. package/dist/state/runtime-state.d.ts.map +1 -1
  287. package/dist/state/runtime-state.js +3 -2
  288. package/dist/state/runtime-state.js.map +1 -1
  289. package/dist/state/update-probation.d.ts +50 -0
  290. package/dist/state/update-probation.d.ts.map +1 -0
  291. package/dist/state/update-probation.js +81 -0
  292. package/dist/state/update-probation.js.map +1 -0
  293. package/dist/state/versions.d.ts +6 -0
  294. package/dist/state/versions.d.ts.map +1 -1
  295. package/dist/state/versions.js +47 -0
  296. package/dist/state/versions.js.map +1 -1
  297. package/dist/state/workspace-state.d.ts +1 -1
  298. package/dist/state/workspace-state.d.ts.map +1 -1
  299. package/dist/state/workspace-state.js +1 -1
  300. package/dist/state/workspace-state.js.map +1 -1
  301. package/dist/text/embed.d.ts +2 -1
  302. package/dist/text/embed.d.ts.map +1 -1
  303. package/dist/text/embed.js +10 -5
  304. package/dist/text/embed.js.map +1 -1
  305. package/dist/text/title.d.ts.map +1 -1
  306. package/dist/text/title.js +4 -7
  307. package/dist/text/title.js.map +1 -1
  308. package/dist/text/transcript-fold.d.ts +2 -1
  309. package/dist/text/transcript-fold.d.ts.map +1 -1
  310. package/dist/text/transcript-fold.js +21 -6
  311. package/dist/text/transcript-fold.js.map +1 -1
  312. package/package.json +39 -6
  313. package/src/chores/probes.ts +3 -1
  314. package/src/contracts/agent.contract.ts +24 -3
  315. package/src/contracts/needs.contract.ts +2 -2
  316. package/src/contracts/offload.contract.ts +21 -1
  317. package/src/contracts/privacy.contract.ts +13 -0
  318. package/src/contracts/runner.contract.ts +10 -2
  319. package/src/contracts/settings.contract.ts +23 -0
  320. package/src/events/agent-events.ts +13 -0
  321. package/src/events/agent-words.ts +2 -3
  322. package/src/events/resume.test.ts +14 -2
  323. package/src/events/resume.ts +32 -3
  324. package/src/events/sandbox-notice.ts +31 -1
  325. package/src/events/transcript.ts +27 -2
  326. package/src/ids/tunnel-ids.test.ts +30 -0
  327. package/src/index.ts +12 -0
  328. package/src/models/agent-catalog.test.ts +4 -3
  329. package/src/models/agent-catalog.ts +10 -7
  330. package/src/models/agent-runtimes.ts +30 -1
  331. package/src/models/capability-ledger.test.ts +4 -0
  332. package/src/models/model-order.ts +13 -0
  333. package/src/models/model-pins.ts +5 -4
  334. package/src/models/model-roles.test.ts +9 -31
  335. package/src/models/model-roles.ts +45 -50
  336. package/src/models/model-setups.test.ts +100 -0
  337. package/src/models/model-setups.ts +151 -0
  338. package/src/models/plan-pools.test.ts +58 -0
  339. package/src/models/plan-pools.ts +38 -7
  340. package/src/models/provider-specs.ts +20 -2
  341. package/src/policy/command-classes.ts +34 -3
  342. package/src/policy/control-scopes.ts +27 -0
  343. package/src/policy/credential-material.ts +58 -16
  344. package/src/policy/device-programs.ts +137 -0
  345. package/src/policy/identity-assertion.ts +130 -0
  346. package/src/policy/sandbox-power.ts +247 -0
  347. package/src/policy/search-globs.ts +135 -0
  348. package/src/policy/secret-hosts.ts +7 -0
  349. package/src/policy/tests/command-classes.test.ts +35 -0
  350. package/src/policy/tests/credential-material.test.ts +20 -0
  351. package/src/policy/tests/device-programs.test.ts +107 -0
  352. package/src/policy/tests/identity-assertion.test.ts +79 -0
  353. package/src/policy/tests/sandbox-power.test.ts +181 -0
  354. package/src/policy/tests/search-globs.test.ts +46 -1
  355. package/src/policy/tests/secret-hosts.test.ts +17 -1
  356. package/src/policy/turned-away.ts +2 -0
  357. package/src/protocol/doors/runner-protocol.ts +21 -1
  358. package/src/protocol/pairing-redeem.test.ts +86 -0
  359. package/src/protocol/pairing-redeem.ts +79 -0
  360. package/src/protocol/peer-mcp-server.ts +1 -2
  361. package/src/protocol/raw/raw-json-routes.test.ts +87 -0
  362. package/src/protocol/raw/raw-json-routes.ts +127 -5
  363. package/src/protocol/raw/raw-routes.ts +11 -0
  364. package/src/protocol/sse.test.ts +50 -1
  365. package/src/protocol/sse.ts +172 -26
  366. package/src/schemas/agent.ts +23 -2
  367. package/src/schemas/automations.ts +16 -5
  368. package/src/schemas/daemon-health.test.ts +28 -0
  369. package/src/schemas/device-swaps.test.ts +39 -0
  370. package/src/schemas/device-swaps.ts +26 -0
  371. package/src/schemas/devices.ts +11 -2
  372. package/src/schemas/engines.ts +1 -1
  373. package/src/schemas/environment.ts +4 -0
  374. package/src/schemas/git/git-scope.test.ts +91 -0
  375. package/src/schemas/git/git-scope.ts +71 -0
  376. package/src/schemas/git/git.ts +15 -1
  377. package/src/schemas/hosts.ts +7 -0
  378. package/src/schemas/keep-warm.ts +2 -1
  379. package/src/schemas/logs.ts +25 -0
  380. package/src/schemas/members.ts +8 -0
  381. package/src/schemas/metrics.test.ts +80 -0
  382. package/src/schemas/metrics.ts +31 -9
  383. package/src/schemas/needs.ts +80 -4
  384. package/src/schemas/offload.ts +76 -0
  385. package/src/schemas/privacy.ts +27 -3
  386. package/src/schemas/project-bring-back.test.ts +15 -0
  387. package/src/schemas/project-bring-back.ts +78 -0
  388. package/src/schemas/providers/handoff.test.ts +42 -0
  389. package/src/schemas/providers/handoff.ts +128 -0
  390. package/src/schemas/providers/plan-limits.ts +14 -1
  391. package/src/schemas/providers/usage.ts +13 -7
  392. package/src/schemas/restart-intent.test.ts +45 -0
  393. package/src/schemas/restart-intent.ts +47 -0
  394. package/src/schemas/run-targets.ts +5 -2
  395. package/src/schemas/settings-history.ts +28 -0
  396. package/src/schemas/settings.test.ts +74 -21
  397. package/src/schemas/settings.ts +80 -44
  398. package/src/schemas/short-codes.ts +59 -0
  399. package/src/schemas/speech.ts +4 -0
  400. package/src/schemas/system.ts +106 -5
  401. package/src/schemas/voice.ts +161 -0
  402. package/src/schemas/workspace/workspace-upload.ts +11 -0
  403. package/src/state/arrival.ts +3 -0
  404. package/src/state/history-state.ts +9 -0
  405. package/src/state/runtime-state.test.ts +13 -0
  406. package/src/state/runtime-state.ts +11 -5
  407. package/src/state/update-probation.test.ts +45 -0
  408. package/src/state/update-probation.ts +169 -0
  409. package/src/state/versions.test.ts +30 -1
  410. package/src/state/versions.ts +68 -0
  411. package/src/state/workspace-state.ts +4 -1
  412. package/src/text/embed.test.ts +14 -0
  413. package/src/text/embed.ts +19 -10
  414. package/src/text/title.ts +5 -9
  415. package/src/text/transcript-fold.test.ts +61 -0
  416. package/src/text/transcript-fold.ts +31 -6
@@ -0,0 +1,71 @@
1
+ import { type GitChange, type GitDiffSide, type GitScope, UNATTRIBUTED_ORIGIN } from "./git.js";
2
+
3
+ // What a scope names, resolved against a repository's own status. One definition, read by the daemon when it acts on a
4
+ // scope and by the Changes panel when it asks the owner about one, so the question and the action cannot disagree.
5
+ // Sides are the verb's, not the scope's: staging reads the two not yet in the index; a scope narrows, never widens.
6
+
7
+ export interface ChangedSides {
8
+ readonly conflicted: readonly GitChange[];
9
+ readonly staged: readonly GitChange[];
10
+ readonly unstaged: readonly GitChange[];
11
+ }
12
+
13
+ // Who landed each path, newest first, as `RepoChanges.origins` ships it; a rename's old path is keyed too.
14
+ export type ChangeOrigins = Readonly<Record<string, readonly string[]>>;
15
+
16
+ // What each verb can move; `git add` on an unmerged path settles a merge, so conflicts are stageable too.
17
+ export const STAGEABLE_SIDES: readonly GitDiffSide[] = ["unstaged", "conflicted"];
18
+ export const UNSTAGEABLE_SIDES: readonly GitDiffSide[] = ["staged"];
19
+ // Discard rewrites the worktree, which every side has one of.
20
+ export const DISCARDABLE_SIDES: readonly GitDiffSide[] = ["conflicted", "staged", "unstaged"];
21
+
22
+ // The paths one row stands for on disk: a rename is two, and no verb acts on half a move.
23
+ export const legsOf = (change: Pick<GitChange, "path" | "from">): readonly string[] =>
24
+ change.from === undefined ? [change.path] : [change.path, change.from];
25
+
26
+ // Every conversation that landed either leg of a row, newest first per leg, each once.
27
+ export const originsOfChange = (change: Pick<GitChange, "path" | "from">, origins: ChangeOrigins): readonly string[] => [
28
+ ...new Set(legsOf(change).flatMap((path) => origins[path] ?? [])),
29
+ ];
30
+
31
+ // Whether a row is that origin's work: a conversation that landed either leg of it, or for the unattributed origin a row
32
+ // no conversation landed any leg of. A rename out of an agent's file is the agent's row, whoever renamed it, so the
33
+ // panel counts it, the scope moves it and the discard question names it under the same chip.
34
+ export const changeIsFrom = (change: Pick<GitChange, "path" | "from">, origin: string, origins: ChangeOrigins): boolean => {
35
+ const ids = originsOfChange(change, origins);
36
+ return origin === UNATTRIBUTED_ORIGIN ? ids.length === 0 : ids.includes(origin);
37
+ };
38
+
39
+ // Paths a scope resolves to, for a verb that moves `sides`; both legs of a rename included.
40
+ // Deduplicated: a path staged and edited again is two rows over one file, and a rename's legs can be named twice.
41
+ export const scopedPaths = (changed: ChangedSides, sides: readonly GitDiffSide[], scope: GitScope, origins: ChangeOrigins = {}): readonly string[] => {
42
+ const origin = scope.origin;
43
+ const reading = scope.side === undefined ? sides : sides.filter((side) => side === scope.side);
44
+ const rows = reading.flatMap((side) => changed[side]);
45
+ const named = origin === undefined ? rows : rows.filter((change) => changeIsFrom(change, origin, origins));
46
+ return [...new Set(named.flatMap(legsOf))];
47
+ };
48
+
49
+ // A scope that narrows nothing is the whole repository; two of the three verbs say so to git directly, unenumerated.
50
+ export const isWholeRepo = (scope: GitScope): boolean => scope.side === undefined && scope.origin === undefined;
51
+
52
+ // What discarding `paths` does to each, read off the same status: a path the last commit does not hold leaves the disk
53
+ // (an untracked file, a staged new file, a rename's new leg), every other returns to its committed state. Mirrors the
54
+ // daemon's discard, which unstages first and then cleans what reads untracked.
55
+ export const discardOutcome = (changed: ChangedSides, paths: readonly string[]): { readonly deletes: readonly string[]; readonly restores: readonly string[] } => {
56
+ const notInHead = new Set<string>();
57
+ for (const change of changed.staged) {
58
+ if (change.status === "added" || (change.status === "renamed" && change.from !== undefined)) {
59
+ notInHead.add(change.path);
60
+ }
61
+ }
62
+ // Untracked, unless the index deleted it from the last commit: that path is in HEAD and comes back.
63
+ const indexDeleted = new Set(changed.staged.filter((change) => change.status === "deleted").map((change) => change.path));
64
+ for (const change of changed.unstaged) {
65
+ if (change.status === "added" && !indexDeleted.has(change.path)) {
66
+ notInHead.add(change.path);
67
+ }
68
+ }
69
+ const unique = [...new Set(paths)];
70
+ return { deletes: unique.filter((path) => notInHead.has(path)), restores: unique.filter((path) => !notInHead.has(path)) };
71
+ };
@@ -78,9 +78,23 @@ export const DiscardSchema = RepoParamSchema.extend(GitTargetSchema.shape)
78
78
  .refine(oneTarget, ONE_TARGET);
79
79
  // Index-only moves; nothing on disk changes, so neither needs a checkpoint. An empty target means the whole repository
80
80
  // (`git add -A`, or the entire index).
81
+ // `chip` is the reversible form of an origin's move (the Changes panel's origin chips): staging records what the index
82
+ // held at every path it moves, and unstaging the same chip writes exactly that back, so a partial `git add -p` or a
83
+ // merge's clean result survives one on/off. It names its own target, so it goes alone.
84
+ const oneIndexMove = (move: GitTarget & { readonly chip?: string | undefined }): boolean =>
85
+ oneTarget(move) && (move.chip === undefined || (move.paths === undefined && move.scope === undefined));
81
86
  export const GitIndexMoveSchema = RepoParamSchema.extend(GitTargetSchema.shape)
87
+ .extend({
88
+ chip: z
89
+ .string()
90
+ .min(1)
91
+ .optional()
92
+ .describe(
93
+ `Stage one origin's unstaged files (a conversation id, or "${UNATTRIBUTED_ORIGIN}" for the files none landed) so the move can be taken back exactly: unstaging with the same value later restores what the index held at each of those paths before, partial staging included, and leaves alone any path staged differently since or a commit that already took them. Goes without paths or a scope.`,
94
+ ),
95
+ })
82
96
  .describe("What to move across the index. Nothing on disk changes either way.")
83
- .refine(oneTarget, ONE_TARGET);
97
+ .refine(oneIndexMove, { message: "name paths, a scope or a chip, not more than one" });
84
98
  // No separate "set upstream" flag: the daemon runs `push -u` exactly when the branch has none yet, which is never
85
99
  // destructive.
86
100
  export const PushSchema = RepoParamSchema.extend({
@@ -76,6 +76,13 @@ export const DeviceFactsSchema = z.object({
76
76
  // The agent's last upkeep pass in this environment (DeviceUpkeepSchema): whether a release brought this machine to
77
77
  // the current shape, read rather than assumed. Absent before the first pass, and from an agent older than the field.
78
78
  upkeep: DeviceUpkeepSchema.optional(),
79
+ // How the agent before this one ended, as one sentence (watchdog kill, crash, update, reboot), so a device that
80
+ // dropped off says why once it is back. Absent on a first start, and from an agent older than the field.
81
+ previousExit: z.string().optional(),
82
+ // Which ic answers on this machine and what steers it: an old ic.exe installed from a leftover IC_URL looked like
83
+ // a daemon bug for an afternoon. Override values are the agent's own redaction (secret-looking ones by length).
84
+ ic: z.object({ path: z.string(), version: z.string(), modified: z.string().optional() }).optional(),
85
+ icOverrides: z.array(z.string()).optional(),
79
86
  });
80
87
  export type DeviceFacts = z.infer<typeof DeviceFactsSchema>;
81
88
 
@@ -88,7 +88,8 @@ export const PromptCacheOpeningSchema = z.object({
88
88
  });
89
89
  export type PromptCacheOpening = z.infer<typeof PromptCacheOpeningSchema>;
90
90
 
91
- const tokensLabel = (tokens: number): string => (tokens >= 1_000 ? `${Math.round(tokens / 1_000)}k` : String(tokens));
91
+ /** A token count as the transcript words it: "305k", or the bare count under a thousand. */
92
+ export const tokensLabel = (tokens: number): string => (tokens >= 1_000 ? `${Math.round(tokens / 1_000)}k` : String(tokens));
92
93
 
93
94
  const spanLabel = (ms: number): string => {
94
95
  const minutes = Math.max(1, Math.round(ms / 60_000));
@@ -52,10 +52,35 @@ export const ClientDiagnosticSchema = z.object({
52
52
  .describe("Whatever else was worth keeping."),
53
53
  });
54
54
  export type ClientDiagnostic = z.infer<typeof ClientDiagnosticSchema>;
55
+ // Which page a batch came from, said once per batch rather than on every event. Without it two tabs' reports read as
56
+ // one stream, and an agent spent dozens of calls learning a page was the owner's own dev server rather than the build
57
+ // it had just changed.
58
+ export const ClientPageSchema = z.object({
59
+ pageId: z.string().min(1).max(64).describe("This page load's own id: every report from one load shares it, and a reload gets a new one."),
60
+ tabId: z.string().max(64).optional().describe("The browser tab's id, which a reload of the same tab keeps."),
61
+ userAgent: z.string().max(300).optional().describe("The browser's own description of itself."),
62
+ viewport: z.string().max(40).optional().describe("The window's size in CSS pixels and its pixel ratio, e.g. 390x844@3."),
63
+ // The join key into PostHog's session replay: absent where analytics is off (dev, self-hosted) or not loaded yet.
64
+ replay: z.string().max(100).optional().describe("The PostHog session id, whose replay shows what the person did."),
65
+ origin: z.string().max(200).optional().describe("The address the page was loaded from."),
66
+ served: z
67
+ .string()
68
+ .max(300)
69
+ .optional()
70
+ .describe("How the page was served: `dev` (a Vite dev server, with the checkout, branch and who started it), `desktop` (the installed app, with its version), `local` (a window on a folder) or `hosted`."),
71
+ commit: z
72
+ .string()
73
+ .max(40)
74
+ .optional()
75
+ .describe("The short commit the page's build was made from (a dev server's: the one it started on). Absent where the build could not read it."),
76
+ });
77
+ export type ClientPage = z.infer<typeof ClientPageSchema>;
55
78
  // Capped batch: the client already coalesces and drops its own side, and an unbounded array here is a way to fill a
56
79
  // disk with one request.
57
80
  export const ClientDiagnosticsReportSchema = z.object({
58
81
  events: z.array(ClientDiagnosticSchema).min(1).max(50).describe("What the browser has to report, oldest first."),
82
+ // Optional: a page from before it sends none.
83
+ page: ClientPageSchema.optional().describe("Which page load sent the batch."),
59
84
  });
60
85
  export type ClientDiagnosticsReport = z.infer<typeof ClientDiagnosticsReportSchema>;
61
86
  export const ClientDiagnosticsAcceptedSchema = z.object({
@@ -8,6 +8,10 @@ export const MemberSchema = z.object({
8
8
  email: z.string().describe("Who, lowercased."),
9
9
  role: GrantedRoleSchema.describe("The tier they were granted."),
10
10
  areas: z.array(z.string()).optional().describe("The areas they are fenced to. Absent means the whole workspace."),
11
+ emailSignIn: z
12
+ .boolean()
13
+ .optional()
14
+ .describe("Whether they may sign in with an email code the platform confirmed, rather than only with Google or a passkey. Absent means no."),
11
15
  });
12
16
  export type Member = z.infer<typeof MemberSchema>;
13
17
 
@@ -22,10 +26,14 @@ export type MembersRoster = z.infer<typeof MembersRosterSchema>;
22
26
 
23
27
  // POST /members: a grant is a role decision, so the role is required. Areas are optional, their absence being the whole
24
28
  // workspace; a writer and a guest are refused without them, since for those two tiers the fence is the tier.
29
+ // `emailSignIn` lets the platform's identity assertion open this sandbox for them (policy/identity-assertion.ts): true
30
+ // allows it, false takes it back, absent keeps what the row already says, so a re-grade from an editor that predates
31
+ // the field never drops it.
25
32
  export const MemberGrantSchema = z.object({
26
33
  email: z.string(),
27
34
  role: GrantedRoleSchema,
28
35
  areas: z.array(z.string().min(1)).max(20).optional(),
36
+ emailSignIn: z.boolean().optional(),
29
37
  });
30
38
  export type MemberGrant = z.infer<typeof MemberGrantSchema>;
31
39
 
@@ -0,0 +1,80 @@
1
+ import { type SandboxMetrics, SandboxMetricsSchema } from "./metrics.js";
2
+
3
+ // A daemon payload from before memoryBreakdown and the other optional accounting fields existed.
4
+ const legacyMetrics = {
5
+ at: 1_000_000,
6
+ sandbox: {
7
+ cores: 2,
8
+ memoryBytes: 7 * 2 ** 30,
9
+ memoryLimitBytes: 16 * 2 ** 30,
10
+ loadAverage: [1.5, 1.25, 1],
11
+ processes: 3,
12
+ },
13
+ daemon: { rssBytes: 300 * 2 ** 20, heapUsedBytes: 120 * 2 ** 20 },
14
+ sessions: { "conv-a": { processes: 2, rssBytes: 400 * 2 ** 20 } },
15
+ roles: {
16
+ agentRuntime: { processes: 1, rssBytes: 300 * 2 ** 20 },
17
+ terminal: { processes: 1, rssBytes: 100 * 2 ** 20 },
18
+ },
19
+ } satisfies SandboxMetrics;
20
+
21
+ describe("the metrics memory accounting contract", () => {
22
+ test("an older payload parses unchanged without the optional breakdown or its categories", () => {
23
+ const parsed = SandboxMetricsSchema.parse(legacyMetrics);
24
+ expect(parsed).toEqual(legacyMetrics);
25
+ expect(Object.hasOwn(parsed.sandbox, "memoryBreakdown")).toBe(false);
26
+ });
27
+
28
+ test("a partial new breakdown preserves zero and leaves unmeasured categories absent", () => {
29
+ const payload = {
30
+ ...legacyMetrics,
31
+ sandbox: {
32
+ ...legacyMetrics.sandbox,
33
+ memoryBreakdown: { anonymousBytes: 0, inactiveFileCacheBytes: 3 * 2 ** 30 },
34
+ },
35
+ };
36
+ expect(SandboxMetricsSchema.parse(payload)).toEqual(payload);
37
+ });
38
+
39
+ test("each category is independently optional, with no zero defaults for the others", () => {
40
+ for (const memoryBreakdown of [
41
+ {},
42
+ { anonymousBytes: 0 },
43
+ { countedFileCacheBytes: 0 },
44
+ { kernelBytes: 0 },
45
+ { inactiveFileCacheBytes: 0 },
46
+ ]) {
47
+ const payload = { ...legacyMetrics, sandbox: { ...legacyMetrics.sandbox, memoryBreakdown } };
48
+ expect(SandboxMetricsSchema.parse(payload)).toEqual(payload);
49
+ }
50
+ });
51
+
52
+ test("measured resident categories remain separate from a headline that also counts full swap", () => {
53
+ const payload = {
54
+ ...legacyMetrics,
55
+ sandbox: {
56
+ ...legacyMetrics.sandbox,
57
+ memoryBytes: 16.5 * 2 ** 30,
58
+ swapBytes: 9.5 * 2 ** 30,
59
+ swapLimitBytes: 10 * 2 ** 30,
60
+ swapFull: true,
61
+ memoryBreakdown: {
62
+ anonymousBytes: 5 * 2 ** 30,
63
+ countedFileCacheBytes: 2 ** 30,
64
+ kernelBytes: 2 ** 30,
65
+ inactiveFileCacheBytes: 3 * 2 ** 30,
66
+ },
67
+ },
68
+ };
69
+ expect(SandboxMetricsSchema.parse(payload)).toEqual(payload);
70
+ });
71
+
72
+ test("present categories must be byte numbers, not numeric strings or non-finite values", () => {
73
+ for (const field of ["anonymousBytes", "countedFileCacheBytes", "kernelBytes", "inactiveFileCacheBytes"]) {
74
+ for (const invalid of ["0", null, Number.NaN, Number.POSITIVE_INFINITY]) {
75
+ const payload = { ...legacyMetrics, sandbox: { ...legacyMetrics.sandbox, memoryBreakdown: { [field]: invalid } } };
76
+ expect(SandboxMetricsSchema.safeParse(payload).success).toBe(false);
77
+ }
78
+ }
79
+ });
80
+ });
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
 
3
3
  // What the sandbox is using right now, measured only when asked (GET /system/metrics). CPU percentages are of ONE core
4
- // unless the field says otherwise; memory is resident bytes.
4
+ // unless the field says otherwise; memory is in bytes, with admission accounting and process RSS distinguished below.
5
5
 
6
6
  // The kinds of process a sandbox runs, as the daemon classifies them by command line.
7
7
  export const PROCESS_ROLES = [
@@ -30,7 +30,7 @@ export const ProcessGroupMetricsSchema = z.object({
30
30
  rssBytes: z
31
31
  .number()
32
32
  .describe(
33
- "Their resident memory added up, in bytes. Memory two processes share is counted in each, so this can exceed what they cost together.",
33
+ "Their process resident memory (RSS) added up, in bytes, including file mappings and shared pages counted once per process. This can exceed what they cost together. It excludes unmapped file cache, kernel memory and swapped-out pages.",
34
34
  ),
35
35
  });
36
36
  export type ProcessGroupMetrics = z.infer<typeof ProcessGroupMetricsSchema>;
@@ -84,24 +84,46 @@ export const SandboxUsageSchema = z.object({
84
84
  memoryBytes: z
85
85
  .number()
86
86
  .describe(
87
- "Memory counted against the limit, as the daemon admits work by it: resident memory less the file cache the kernel takes back on demand, and once swap is full (`swapFull`) what was pushed to swap as well.",
87
+ "Memory counted against the limit, as the daemon admits work by it: the cgroup's resident charge, including the daemon, kernel memory and file cache except inactive_file, plus swapped bytes only when swap is nearly full (`swapFull`). Falls back to the machine's used memory when the budget cannot measure it.",
88
88
  ),
89
89
  memoryLimitBytes: z
90
90
  .number()
91
91
  .describe(
92
92
  "The memory limit work is admitted against: where the kernel starts throttling the container (memory.high), else its hard limit, else the machine's memory.",
93
93
  ),
94
+ memoryBreakdown: z
95
+ .object({
96
+ anonymousBytes: z.number().optional().describe("Anonymous resident memory charged to the cgroup (memory.stat anon), in bytes."),
97
+ countedFileCacheBytes: z
98
+ .number()
99
+ .optional()
100
+ .describe("File cache counted in resident usage (memory.stat file less inactive_file), in bytes. Absent unless both counters are valid."),
101
+ kernelBytes: z
102
+ .number()
103
+ .optional()
104
+ .describe("Kernel memory charged to the cgroup (memory.stat kernel), in bytes. Absent on kernels without that aggregate."),
105
+ inactiveFileCacheBytes: z
106
+ .number()
107
+ .optional()
108
+ .describe("Inactive file cache (memory.stat inactive_file), in bytes, excluded from memoryBytes."),
109
+ })
110
+ .optional()
111
+ .describe(
112
+ "Approximate measured memory.stat categories, not an RSS subtraction or an exact sum of memoryBytes: admission is sampled separately and may also count swap. Missing or invalid categories are omitted, not zero. Absent without cgroup memory, on machine-memory fallback, when no category is valid, and from older daemons.",
113
+ ),
94
114
  swapBytes: z
95
115
  .number()
96
116
  .optional()
97
117
  .describe(
98
- "What was pushed out to swap, counted in `memoryBytes` only once swap is full. Absent where the sandbox cannot see its own memory.",
118
+ "What was pushed out to swap, counted in `memoryBytes` only when swap is nearly full under admission policy. Absent where the sandbox cannot see its own memory.",
99
119
  ),
100
120
  swapLimitBytes: z.number().optional().describe("What swap can hold. Absent where nothing bounds it, and from a daemon that predates it."),
101
121
  swapFull: z
102
122
  .boolean()
103
123
  .optional()
104
- .describe("Swap is nearly at `swapLimitBytes`: nothing more can be parked there, so what is swapped counts against the limit too."),
124
+ .describe(
125
+ "Swap has reached the admission policy's nearly-full threshold against `swapLimitBytes`, so swapped bytes count against the memory limit too, even if swap space remains.",
126
+ ),
105
127
  memoryRoom: MemoryRoomSchema.optional().describe("What admission reads off the same reading. Absent from a daemon that predates it."),
106
128
  diskBytes: z.number().optional().describe("Space used on the volume the workspace lives on. Absent when the volume would not say."),
107
129
  diskTotalBytes: z.number().optional().describe("That volume's size. Absent when the volume would not say."),
@@ -114,7 +136,7 @@ export const SandboxUsageSchema = z.object({
114
136
  .describe(
115
137
  "Cores the machine's load average reads against: every core the sandbox may be scheduled on, before any quota. Absent from a daemon that predates it.",
116
138
  ),
117
- processes: z.number().describe("How many processes are running in the sandbox."),
139
+ processes: z.number().describe("How many processes are running in the sandbox, including the daemon."),
118
140
  pressure: PressureMetricsSchema.optional().describe(
119
141
  "How much work waited on CPU, memory or disk lately. Absent where the kernel does not report it.",
120
142
  ),
@@ -140,12 +162,12 @@ export const SandboxMetricsSchema = z.object({
140
162
  .number()
141
163
  .optional()
142
164
  .describe("How long the CPU figures were measured over, in milliseconds. Absent on a first reading, and then so is every CPU figure."),
143
- sandbox: SandboxUsageSchema.describe("The sandbox as a whole."),
144
- daemon: DaemonUsageSchema.describe("The daemon that runs it, which none of the other figures include."),
165
+ sandbox: SandboxUsageSchema.describe("The sandbox as a whole, including the daemon."),
166
+ daemon: DaemonUsageSchema.describe("The daemon's own usage, included in the sandbox total and process count but excluded from roles and session sums."),
145
167
  sessions: z
146
168
  .record(z.string(), SessionMetricsSchema)
147
169
  .describe(
148
- "What each conversation's processes use, by conversation id: the agent's own process and everything it started. Only conversations with processes running, and only those the caller may see.",
170
+ "What each conversation's processes use, by conversation id: the agent's own process and everything it started, excluding the daemon. These group many of the same process readings as roles, not additional memory. Only conversations with processes running, and only those the caller may see.",
149
171
  ),
150
172
  roles: z
151
173
  .partialRecord(ProcessRoleSchema, ProcessGroupMetricsSchema)
@@ -57,6 +57,13 @@ export type CapabilityNeed = z.infer<typeof CapabilityNeedSchema>;
57
57
  // The alphabet a `{{secret:NAME}}` reference resolves; a pasted value is stored under exactly this name.
58
58
  export const SECRET_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
59
59
 
60
+ // A `{{secret:name}}` reference wherever it stands in text, capturing the name. Wider than SECRET_NAME, since a
61
+ // reference also names the other stores' entries (`<capability>/<field>`). Not global, so a shared instance carries no
62
+ // `lastIndex` between callers: a site that replaces or spans every one derives its own /g copy from it. The terminal
63
+ // filter (@intentic/output-cleaners, builtins only) keeps a copy, pinned to this one by the daemon's
64
+ // src/secrets/terminal-masking-parity.test.ts.
65
+ export const SECRET_REFERENCE = /\{\{secret:([A-Za-z0-9_./-]+)\}\}/;
66
+
60
67
  export const SecretNeedSchema = z.object({
61
68
  kind: z.literal("secret"),
62
69
  name: z.string().regex(SECRET_NAME).describe("The name it is stored under, and what `{{secret:NAME}}` will resolve."),
@@ -283,6 +290,16 @@ export const AgentNeedSchema = z.object({
283
290
  });
284
291
  export type AgentNeed = z.infer<typeof AgentNeedSchema>;
285
292
 
293
+ // A credential gateway card's "Allow requests like this with <name> in this conversation": the `ask` rule it answered
294
+ // stops asking that conversation, until somebody takes it back. Kept by the rule's own content, so editing the rule
295
+ // asks again.
296
+ export const BrokerRulePassSchema = z.object({
297
+ capability: z.string().describe("The capability whose rule asked, by id."),
298
+ rule: z.string().describe("The rule's own identity (its content), so an edited rule is a different one and asks again."),
299
+ said: z.string().describe('The request in a person\'s words, as the card put it: "GET api.github.com/repos/x with GitHub".'),
300
+ });
301
+ export type BrokerRulePass = z.infer<typeof BrokerRulePassSchema>;
302
+
286
303
  // What a person allowed one conversation beyond its persona and area, from a grant need's card
287
304
  // (personas/conversation-grants.ts). Honoured from that conversation's next turn on, and revocable on the Grants page.
288
305
  export const ConversationGrantSchema = z.object({
@@ -306,11 +323,48 @@ export const ConversationGrantSchema = z.object({
306
323
  .boolean()
307
324
  .default(false)
308
325
  .describe("Whether every request an allow-once could settle is allowed without asking, until somebody takes it back."),
326
+ // Answered on a credential gateway card, read live by the gateway as each request its rules would ask about arrives.
327
+ rules: z
328
+ .array(BrokerRulePassSchema)
329
+ .default([])
330
+ .describe("Credential gateway rules that no longer ask before its requests, each with the words a person allowed it in."),
309
331
  updatedAt: z.number().describe("When it last changed, in milliseconds."),
310
332
  by: z.string().optional().describe("Who last allowed something here."),
311
333
  });
312
334
  export type ConversationGrant = z.infer<typeof ConversationGrantSchema>;
313
335
 
336
+ // A yes that names no conversation: it applies to the whole sandbox, so it is listed beside the per-conversation ones and
337
+ // taken back from the same place (docs/architecture/needs.md). Each is kept off the workspace, where no agent writes.
338
+ export const OWNER_GRANT_KINDS = ["command-policy", "hook-set", "extension"] as const;
339
+ export const OwnerGrantKindSchema = z.enum(OWNER_GRANT_KINDS);
340
+ export type OwnerGrantKind = z.infer<typeof OwnerGrantKindSchema>;
341
+
342
+ export const OwnerGrantSchema = z.discriminatedUnion("kind", [
343
+ // A permission card's "Always: <line>" on a command: the line joins the owner's policy for the safety judge, in every
344
+ // conversation, until somebody takes it back.
345
+ z.object({
346
+ kind: z.literal("command-policy"),
347
+ line: z.string().describe("The policy line the judge reads, exactly as it was accepted on the card."),
348
+ at: z.number().describe("When it was allowed, in milliseconds."),
349
+ by: z.string().optional().describe("Who allowed it, off the verified reply."),
350
+ }),
351
+ // A set of Claude Code hooks approved by its digest (guard/hook-approvals.ts): turns run its hooks until it is taken back.
352
+ z.object({
353
+ kind: z.literal("hook-set"),
354
+ digest: z.string().describe("The set's fingerprint, what taking it back names."),
355
+ hooks: z.array(z.string()).describe("Each hook in the set in the daemon's words: when it runs and what it runs."),
356
+ at: z.number().describe("When it was approved, in milliseconds."),
357
+ }),
358
+ // A workspace extension approved for the powers it declared then (extensions/extension-approvals.ts).
359
+ z.object({
360
+ kind: z.literal("extension"),
361
+ id: z.string().describe("The extension, by its publisher.name id."),
362
+ powers: z.array(z.string()).describe("What it was approved to do, one power per entry."),
363
+ at: z.number().describe("When it was approved, in milliseconds."),
364
+ }),
365
+ ]);
366
+ export type OwnerGrant = z.infer<typeof OwnerGrantSchema>;
367
+
314
368
  // Every yes still standing, by conversation: what its grants widened past its persona or area, and which gated
315
369
  // credentials a named person released to it. Reviewed and taken back from Needs you.
316
370
  export const StandingGrantsSchema = z.object({
@@ -323,6 +377,7 @@ export const StandingGrantsSchema = z.object({
323
377
  installs: z.boolean().default(false).describe("Whether its own dependency installs run without asking."),
324
378
  secrets: z.array(z.string()).default([]).describe("Secrets it may send past their host guard without asking."),
325
379
  everything: z.boolean().default(false).describe("Whether every request an allow-once could settle is allowed without asking."),
380
+ rules: z.array(BrokerRulePassSchema).default([]).describe("Credential gateway rules that no longer ask before its requests."),
326
381
  by: z.string().optional().describe("Who last allowed one of those."),
327
382
  updatedAt: z.number().optional().describe("When one of those last changed, in milliseconds."),
328
383
  releases: z
@@ -330,18 +385,39 @@ export const StandingGrantsSchema = z.object({
330
385
  .describe("Gated credentials released to it, until somebody takes one back."),
331
386
  }),
332
387
  ),
388
+ ownerWide: z
389
+ .array(OwnerGrantSchema)
390
+ .default([])
391
+ .describe("Yeses that name no conversation: command policy lines allowed with Always, approved hook sets, approved workspace extensions."),
392
+ unreadable: z
393
+ .array(OwnerGrantKindSchema)
394
+ .default([])
395
+ .describe("Kinds of owner-wide yes whose record this build could not read: none of that kind stands, and none is listed, until the file is fixed."),
333
396
  });
334
397
  export type StandingGrants = z.infer<typeof StandingGrantsSchema>;
335
398
 
336
- export const GrantRevokeSchema = z.object({
399
+ export const ConversationGrantRevokeSchema = z.object({
337
400
  conversationId: z.string(),
338
401
  kind: z
339
- .enum(["capability", "folder", "shelf", "release", "install", "secret", "everything"])
402
+ .enum(["capability", "folder", "shelf", "release", "install", "secret", "everything", "rule"])
340
403
  .describe(
341
- "Which kind of yes: a grant past the persona or area, a credential's release, letting its installs run unasked, a secret sent past its host guard, or allowing everything.",
404
+ "Which kind of yes: a grant past the persona or area, a credential's release, letting its installs run unasked, a secret sent past its host guard, allowing everything, or a credential gateway rule that stopped asking.",
342
405
  ),
343
- what: z.string().describe("The capability id, folder, shelf, released credential or secret it named; empty for installs and everything."),
406
+ what: z
407
+ .string()
408
+ .describe("The capability id, folder, shelf, released credential or secret it named, or the rule's identity; empty for installs and everything."),
409
+ capability: z.string().optional().describe("For a rule, the capability whose rule it is; a rule's identity alone does not say."),
410
+ });
411
+ export type ConversationGrantRevoke = z.infer<typeof ConversationGrantRevokeSchema>;
412
+
413
+ // Taking back a yes that names no conversation: the kind says which list, `what` which entry of it.
414
+ export const OwnerGrantRevokeSchema = z.object({
415
+ kind: OwnerGrantKindSchema.describe("Which kind of owner-wide yes: a command policy line, an approved hook set, or an approved extension."),
416
+ what: z.string().min(1).describe("The policy line, the hook set's digest, or the extension's id."),
344
417
  });
418
+ export type OwnerGrantRevoke = z.infer<typeof OwnerGrantRevokeSchema>;
419
+
420
+ export const GrantRevokeSchema = z.union([ConversationGrantRevokeSchema, OwnerGrantRevokeSchema]);
345
421
  export type GrantRevoke = z.infer<typeof GrantRevokeSchema>;
346
422
 
347
423
  // The file the needs store keeps: every open need and the recent closed ones, by id.
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { RunnerCommandFrameSchema, RunnerCommandSchema } from "../protocol/doors/runner-protocol.js";
3
+ import { AgentHarnessSchema, AgentProviderSchema } from "./agent.js";
3
4
 
4
5
  // OFFLOADED WORK, as the in-sandbox `offload-run` command and the editor see it (settings `offload`): a heavy line this
5
6
  // sandbox hands a runner on one of the owner's machines instead of running it itself.
@@ -42,3 +43,78 @@ export type OffloadRecord = z.infer<typeof OffloadRecordSchema>;
42
43
  // `offload.commands` keys it) and the pattern that sorts a line into it.
43
44
  export const OffloadKindSchema = z.object({ id: z.string().min(1), pattern: z.string() });
44
45
  export type OffloadKind = z.infer<typeof OffloadKindSchema>;
46
+
47
+ // NEW SESSIONS ACROSS THE OWNER'S MACHINES (settings `offload.sessions`, GET /offload/placement). One measure for every
48
+ // machine, this sandbox and each runner alike, and one judgement over it (runners/fleet-advice.ts), so the composer's
49
+ // suggestion, the placement menu and an automatic placement can never disagree about which machine has room.
50
+
51
+ // What happens to a new session while this machine is busy and another has room: `suggest` says so above the
52
+ // composer, `auto` starts it there unless the person keeps it here, `off` says nothing.
53
+ export const SessionsPlacementSchema = z.enum(["suggest", "auto", "off"]);
54
+ export type SessionsPlacement = z.infer<typeof SessionsPlacementSchema>;
55
+
56
+ // `calm` takes another agent without anyone noticing; `busy` takes one at a cost to the agents already running; `full`
57
+ // would hold or warn a person's next turn (the turn door's own figure).
58
+ export const MachineStandingSchema = z.enum(["calm", "busy", "full"]);
59
+ export type MachineStanding = z.infer<typeof MachineStandingSchema>;
60
+
61
+ // What made a machine busy or full: memory short of what an agent needs, memory stalling, CPUs saturated, or as many
62
+ // agents running as it has slots for.
63
+ export const MachineStrainSchema = z.enum(["memory", "memory-stall", "cpu", "agents"]);
64
+ export type MachineStrain = z.infer<typeof MachineStrainSchema>;
65
+
66
+ export const MachineLoadSchema = z.object({
67
+ standing: MachineStandingSchema,
68
+ // Why it is not calm, strongest first; empty when it is.
69
+ strain: z.array(MachineStrainSchema),
70
+ cpus: z.number().int().positive(),
71
+ // The one-minute load average over the cores: 1 is every core busy.
72
+ load: z.number().nonnegative(),
73
+ // What its resource budget has free, and the limit that is measured against; absent where nothing measures it.
74
+ memoryFreeMb: z.number().int().nonnegative().optional(),
75
+ memoryLimitMb: z.number().int().positive().optional(),
76
+ // Conversations running a turn there now, and how many it holds at once (the fan-out scheduler's own slots).
77
+ agents: z.number().int().nonnegative(),
78
+ slots: z.number().int().positive(),
79
+ // The share of the machine still free by its tightest measure (slots, memory, CPU), so two machines of different
80
+ // sizes compare: 0 is none, 1 is idle.
81
+ headroom: z.number().min(0).max(1),
82
+ });
83
+ export type MachineLoad = z.infer<typeof MachineLoadSchema>;
84
+
85
+ // Why a new session could not start on a runner right now: the machine is asleep, it has never said how big it is, the
86
+ // draft's model signs in with a credential that does not travel, the privacy shield keeps it here, or the person is
87
+ // limited to areas a runner cannot fence.
88
+ export const FleetUnfitSchema = z.enum(["offline", "unmeasured", "credentials", "privacy", "areas"]);
89
+ export type FleetUnfit = z.infer<typeof FleetUnfitSchema>;
90
+
91
+ export const FleetMachineSchema = z.object({
92
+ runner: z.string().min(1),
93
+ // The machine the runner sits on, the word the owner knows it by.
94
+ name: z.string().min(1),
95
+ online: z.boolean(),
96
+ // Absent while it is offline or has never been measured.
97
+ load: MachineLoadSchema.optional(),
98
+ // Absent when a new session could start there now.
99
+ unfit: FleetUnfitSchema.optional(),
100
+ });
101
+ export type FleetMachine = z.infer<typeof FleetMachineSchema>;
102
+
103
+ export const FleetAdviceInputSchema = z.object({
104
+ // The draft's provider and harness, which decide whether its credential can reach a runner; absent reads as the
105
+ // sandbox's defaults.
106
+ agent: AgentProviderSchema.optional(),
107
+ harness: AgentHarnessSchema.optional(),
108
+ // The draft's conversation, which the privacy shield may trust on its own.
109
+ conversationId: z.string().min(1).optional(),
110
+ });
111
+ export type FleetAdviceInput = z.infer<typeof FleetAdviceInputSchema>;
112
+
113
+ export const FleetAdviceSchema = z.object({
114
+ mode: SessionsPlacementSchema,
115
+ here: MachineLoadSchema,
116
+ machines: z.array(FleetMachineSchema),
117
+ // Where a new session should start instead of here: absent while here is calm, or when no machine is better off.
118
+ suggestion: z.object({ runner: z.string().min(1), name: z.string().min(1) }).optional(),
119
+ });
120
+ export type FleetAdvice = z.infer<typeof FleetAdviceSchema>;