@intentic/sandbox-contract 1.231.0 → 1.233.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 (500) hide show
  1. package/README.md +13 -2
  2. package/dist/agent-catalog.d.ts +2 -1
  3. package/dist/agent-catalog.d.ts.map +1 -1
  4. package/dist/agent-catalog.js +1 -1
  5. package/dist/agent-catalog.js.map +1 -1
  6. package/dist/chores/chores.d.ts +1 -1
  7. package/dist/chores/chores.d.ts.map +1 -1
  8. package/dist/chores/probes.d.ts +1 -1
  9. package/dist/chores/probes.d.ts.map +1 -1
  10. package/dist/chores/stack.d.ts.map +1 -1
  11. package/dist/chores/stack.js +2 -1
  12. package/dist/chores/stack.js.map +1 -1
  13. package/dist/chores/verdict.d.ts +1 -1
  14. package/dist/chores/verdict.d.ts.map +1 -1
  15. package/dist/command-classes.d.ts +14 -0
  16. package/dist/command-classes.d.ts.map +1 -0
  17. package/dist/command-classes.js +168 -0
  18. package/dist/command-classes.js.map +1 -0
  19. package/dist/contracts/activity.contract.js +1 -1
  20. package/dist/contracts/activity.contract.js.map +1 -1
  21. package/dist/contracts/agent.contract.d.ts +51 -2
  22. package/dist/contracts/agent.contract.d.ts.map +1 -1
  23. package/dist/contracts/agent.contract.js +13 -1
  24. package/dist/contracts/agent.contract.js.map +1 -1
  25. package/dist/contracts/agents.contract.d.ts +24 -0
  26. package/dist/contracts/agents.contract.d.ts.map +1 -1
  27. package/dist/contracts/agents.contract.js +5 -1
  28. package/dist/contracts/agents.contract.js.map +1 -1
  29. package/dist/contracts/automations.contract.js +2 -1
  30. package/dist/contracts/automations.contract.js.map +1 -1
  31. package/dist/contracts/capabilities.contract.d.ts +63 -0
  32. package/dist/contracts/capabilities.contract.d.ts.map +1 -1
  33. package/dist/contracts/capabilities.contract.js +3 -1
  34. package/dist/contracts/capabilities.contract.js.map +1 -1
  35. package/dist/contracts/chores.contract.d.ts.map +1 -1
  36. package/dist/contracts/chores.contract.js +2 -1
  37. package/dist/contracts/chores.contract.js.map +1 -1
  38. package/dist/contracts/ci.contract.js +2 -1
  39. package/dist/contracts/ci.contract.js.map +1 -1
  40. package/dist/contracts/claude.contract.js +2 -1
  41. package/dist/contracts/claude.contract.js.map +1 -1
  42. package/dist/contracts/cursor.contract.js +2 -1
  43. package/dist/contracts/cursor.contract.js.map +1 -1
  44. package/dist/contracts/drafts.contract.d.ts.map +1 -1
  45. package/dist/contracts/drafts.contract.js +2 -1
  46. package/dist/contracts/drafts.contract.js.map +1 -1
  47. package/dist/contracts/endpoints.contract.d.ts.map +1 -1
  48. package/dist/contracts/endpoints.contract.js +2 -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 +2 -1
  52. package/dist/contracts/exit.contract.js.map +1 -1
  53. package/dist/contracts/extensions.contract.d.ts +42 -0
  54. package/dist/contracts/extensions.contract.d.ts.map +1 -1
  55. package/dist/contracts/extensions.contract.js +4 -1
  56. package/dist/contracts/extensions.contract.js.map +1 -1
  57. package/dist/contracts/git.contract.d.ts.map +1 -1
  58. package/dist/contracts/git.contract.js +4 -1
  59. package/dist/contracts/git.contract.js.map +1 -1
  60. package/dist/contracts/grok.contract.d.ts.map +1 -1
  61. package/dist/contracts/grok.contract.js +2 -1
  62. package/dist/contracts/grok.contract.js.map +1 -1
  63. package/dist/contracts/history.contract.d.ts.map +1 -1
  64. package/dist/contracts/history.contract.js +2 -1
  65. package/dist/contracts/history.contract.js.map +1 -1
  66. package/dist/contracts/host.contract.d.ts +11 -0
  67. package/dist/contracts/host.contract.d.ts.map +1 -1
  68. package/dist/contracts/host.contract.js +4 -1
  69. package/dist/contracts/host.contract.js.map +1 -1
  70. package/dist/contracts/intentic.contract.d.ts.map +1 -1
  71. package/dist/contracts/intentic.contract.js +2 -1
  72. package/dist/contracts/intentic.contract.js.map +1 -1
  73. package/dist/contracts/inventory.contract.js +1 -1
  74. package/dist/contracts/inventory.contract.js.map +1 -1
  75. package/dist/contracts/logs.contract.d.ts.map +1 -1
  76. package/dist/contracts/logs.contract.js +1 -1
  77. package/dist/contracts/logs.contract.js.map +1 -1
  78. package/dist/contracts/loops.contract.js +2 -1
  79. package/dist/contracts/loops.contract.js.map +1 -1
  80. package/dist/contracts/panels.contract.d.ts.map +1 -1
  81. package/dist/contracts/panels.contract.js +2 -1
  82. package/dist/contracts/panels.contract.js.map +1 -1
  83. package/dist/contracts/personas.contract.js +2 -1
  84. package/dist/contracts/personas.contract.js.map +1 -1
  85. package/dist/contracts/ports.contract.d.ts.map +1 -1
  86. package/dist/contracts/ports.contract.js +2 -1
  87. package/dist/contracts/ports.contract.js.map +1 -1
  88. package/dist/contracts/prepush.contract.d.ts.map +1 -1
  89. package/dist/contracts/prepush.contract.js +2 -1
  90. package/dist/contracts/prepush.contract.js.map +1 -1
  91. package/dist/contracts/providers.contract.d.ts.map +1 -1
  92. package/dist/contracts/providers.contract.js +2 -1
  93. package/dist/contracts/providers.contract.js.map +1 -1
  94. package/dist/contracts/public.contract.d.ts.map +1 -1
  95. package/dist/contracts/public.contract.js +2 -1
  96. package/dist/contracts/public.contract.js.map +1 -1
  97. package/dist/contracts/push.contract.d.ts.map +1 -1
  98. package/dist/contracts/push.contract.js +2 -1
  99. package/dist/contracts/push.contract.js.map +1 -1
  100. package/dist/contracts/runner.contract.d.ts +151 -43
  101. package/dist/contracts/runner.contract.d.ts.map +1 -1
  102. package/dist/contracts/runner.contract.js +14 -1
  103. package/dist/contracts/runner.contract.js.map +1 -1
  104. package/dist/contracts/secrets.contract.d.ts.map +1 -1
  105. package/dist/contracts/secrets.contract.js +2 -1
  106. package/dist/contracts/secrets.contract.js.map +1 -1
  107. package/dist/contracts/sessions.contract.js +1 -1
  108. package/dist/contracts/sessions.contract.js.map +1 -1
  109. package/dist/contracts/settings.contract.d.ts +20 -0
  110. package/dist/contracts/settings.contract.d.ts.map +1 -1
  111. package/dist/contracts/settings.contract.js +3 -1
  112. package/dist/contracts/settings.contract.js.map +1 -1
  113. package/dist/contracts/share.contract.d.ts.map +1 -1
  114. package/dist/contracts/share.contract.js +2 -1
  115. package/dist/contracts/share.contract.js.map +1 -1
  116. package/dist/contracts/skills.contract.d.ts.map +1 -1
  117. package/dist/contracts/skills.contract.js +2 -1
  118. package/dist/contracts/skills.contract.js.map +1 -1
  119. package/dist/contracts/system.contract.d.ts +35 -16
  120. package/dist/contracts/system.contract.d.ts.map +1 -1
  121. package/dist/contracts/system.contract.js +6 -1
  122. package/dist/contracts/system.contract.js.map +1 -1
  123. package/dist/contracts/translator.contract.d.ts.map +1 -1
  124. package/dist/contracts/translator.contract.js +4 -1
  125. package/dist/contracts/translator.contract.js.map +1 -1
  126. package/dist/contracts/usage.contract.js +1 -1
  127. package/dist/contracts/usage.contract.js.map +1 -1
  128. package/dist/contracts/vpn.contract.d.ts.map +1 -1
  129. package/dist/contracts/vpn.contract.js +2 -1
  130. package/dist/contracts/vpn.contract.js.map +1 -1
  131. package/dist/contracts/webext.contract.d.ts +45 -0
  132. package/dist/contracts/webext.contract.d.ts.map +1 -0
  133. package/dist/contracts/webext.contract.js +12 -0
  134. package/dist/contracts/webext.contract.js.map +1 -0
  135. package/dist/contracts/workflows.contract.js +2 -1
  136. package/dist/contracts/workflows.contract.js.map +1 -1
  137. package/dist/contracts/workspace.contract.d.ts +2 -2
  138. package/dist/contracts/workspace.contract.d.ts.map +1 -1
  139. package/dist/contracts/workspace.contract.js +7 -1
  140. package/dist/contracts/workspace.contract.js.map +1 -1
  141. package/dist/definition-action.d.ts +7 -0
  142. package/dist/definition-action.d.ts.map +1 -0
  143. package/dist/definition-action.js +3 -0
  144. package/dist/definition-action.js.map +1 -0
  145. package/dist/definition.d.ts +1064 -0
  146. package/dist/definition.d.ts.map +1 -0
  147. package/dist/definition.js +93 -0
  148. package/dist/definition.js.map +1 -0
  149. package/dist/events.d.ts +109 -0
  150. package/dist/events.d.ts.map +1 -1
  151. package/dist/events.js +30 -1
  152. package/dist/events.js.map +1 -1
  153. package/dist/fast-tier.d.ts +1 -1
  154. package/dist/fast-tier.d.ts.map +1 -1
  155. package/dist/history-state.d.ts.map +1 -1
  156. package/dist/history-state.js +1 -0
  157. package/dist/history-state.js.map +1 -1
  158. package/dist/index.d.ts +371 -103
  159. package/dist/index.d.ts.map +1 -1
  160. package/dist/index.js +52 -1
  161. package/dist/index.js.map +1 -1
  162. package/dist/listener-protocol.js +1 -1
  163. package/dist/listener-protocol.js.map +1 -1
  164. package/dist/quick-model.d.ts +1 -1
  165. package/dist/quick-model.d.ts.map +1 -1
  166. package/dist/runner-protocol.d.ts +18 -0
  167. package/dist/runner-protocol.d.ts.map +1 -1
  168. package/dist/runner-protocol.js +8 -4
  169. package/dist/runner-protocol.js.map +1 -1
  170. package/dist/schemas/activity.d.ts +112 -0
  171. package/dist/schemas/activity.d.ts.map +1 -0
  172. package/dist/schemas/activity.js +70 -0
  173. package/dist/schemas/activity.js.map +1 -0
  174. package/dist/schemas/agent.d.ts +189 -0
  175. package/dist/schemas/agent.d.ts.map +1 -0
  176. package/dist/schemas/agent.js +170 -0
  177. package/dist/schemas/agent.js.map +1 -0
  178. package/dist/schemas/agents.d.ts +690 -0
  179. package/dist/schemas/agents.d.ts.map +1 -0
  180. package/dist/schemas/agents.js +314 -0
  181. package/dist/schemas/agents.js.map +1 -0
  182. package/dist/schemas/automations.d.ts +736 -0
  183. package/dist/schemas/automations.d.ts.map +1 -0
  184. package/dist/schemas/automations.js +218 -0
  185. package/dist/schemas/automations.js.map +1 -0
  186. package/dist/schemas/capabilities.d.ts +753 -0
  187. package/dist/schemas/capabilities.d.ts.map +1 -0
  188. package/dist/schemas/capabilities.js +269 -0
  189. package/dist/schemas/capabilities.js.map +1 -0
  190. package/dist/schemas/ci.d.ts +179 -0
  191. package/dist/schemas/ci.d.ts.map +1 -0
  192. package/dist/schemas/ci.js +99 -0
  193. package/dist/schemas/ci.js.map +1 -0
  194. package/dist/schemas/claude-gate.d.ts +13 -0
  195. package/dist/schemas/claude-gate.d.ts.map +1 -0
  196. package/dist/schemas/claude-gate.js +8 -0
  197. package/dist/schemas/claude-gate.js.map +1 -0
  198. package/dist/schemas/codebase-health.d.ts +56 -0
  199. package/dist/schemas/codebase-health.d.ts.map +1 -0
  200. package/dist/schemas/codebase-health.js +43 -0
  201. package/dist/schemas/codebase-health.js.map +1 -0
  202. package/dist/schemas/computers.d.ts +386 -0
  203. package/dist/schemas/computers.d.ts.map +1 -0
  204. package/dist/schemas/computers.js +107 -0
  205. package/dist/schemas/computers.js.map +1 -0
  206. package/dist/schemas/drafts.d.ts +84 -0
  207. package/dist/schemas/drafts.d.ts.map +1 -0
  208. package/dist/schemas/drafts.js +44 -0
  209. package/dist/schemas/drafts.js.map +1 -0
  210. package/dist/schemas/environment.d.ts +349 -0
  211. package/dist/schemas/environment.d.ts.map +1 -0
  212. package/dist/schemas/environment.js +111 -0
  213. package/dist/schemas/environment.js.map +1 -0
  214. package/dist/schemas/exit.d.ts +151 -0
  215. package/dist/schemas/exit.d.ts.map +1 -0
  216. package/dist/schemas/exit.js +115 -0
  217. package/dist/schemas/exit.js.map +1 -0
  218. package/dist/schemas/extension-updates.d.ts +1014 -0
  219. package/dist/schemas/extension-updates.d.ts.map +1 -0
  220. package/dist/schemas/extension-updates.js +197 -0
  221. package/dist/schemas/extension-updates.js.map +1 -0
  222. package/dist/schemas/fast-mode.d.ts +8 -0
  223. package/dist/schemas/fast-mode.d.ts.map +1 -0
  224. package/dist/schemas/fast-mode.js +3 -0
  225. package/dist/schemas/fast-mode.js.map +1 -0
  226. package/dist/schemas/git-history.d.ts +238 -0
  227. package/dist/schemas/git-history.d.ts.map +1 -0
  228. package/dist/schemas/git-history.js +152 -0
  229. package/dist/schemas/git-history.js.map +1 -0
  230. package/dist/schemas/git.d.ts +471 -0
  231. package/dist/schemas/git.d.ts.map +1 -0
  232. package/dist/schemas/git.js +204 -0
  233. package/dist/schemas/git.js.map +1 -0
  234. package/dist/schemas/history.d.ts +100 -0
  235. package/dist/schemas/history.d.ts.map +1 -0
  236. package/dist/schemas/history.js +54 -0
  237. package/dist/schemas/history.js.map +1 -0
  238. package/dist/schemas/hosts.d.ts +41 -0
  239. package/dist/schemas/hosts.d.ts.map +1 -0
  240. package/dist/schemas/hosts.js +18 -0
  241. package/dist/schemas/hosts.js.map +1 -0
  242. package/dist/schemas/intentic.d.ts +5 -0
  243. package/dist/schemas/intentic.d.ts.map +1 -0
  244. package/dist/schemas/intentic.js +3 -0
  245. package/dist/schemas/intentic.js.map +1 -0
  246. package/dist/schemas/internal.d.ts +8 -0
  247. package/dist/schemas/internal.d.ts.map +1 -0
  248. package/dist/schemas/internal.js +12 -0
  249. package/dist/schemas/internal.js.map +1 -0
  250. package/dist/schemas/inventory.d.ts +171 -0
  251. package/dist/schemas/inventory.d.ts.map +1 -0
  252. package/dist/schemas/inventory.js +51 -0
  253. package/dist/schemas/inventory.js.map +1 -0
  254. package/dist/schemas/logs.d.ts +67 -0
  255. package/dist/schemas/logs.d.ts.map +1 -0
  256. package/dist/schemas/logs.js +54 -0
  257. package/dist/schemas/logs.js.map +1 -0
  258. package/dist/schemas/loops.d.ts +415 -0
  259. package/dist/schemas/loops.d.ts.map +1 -0
  260. package/dist/schemas/loops.js +188 -0
  261. package/dist/schemas/loops.js.map +1 -0
  262. package/dist/schemas/maintenance.d.ts +557 -0
  263. package/dist/schemas/maintenance.d.ts.map +1 -0
  264. package/dist/schemas/maintenance.js +201 -0
  265. package/dist/schemas/maintenance.js.map +1 -0
  266. package/dist/schemas/marketplace.d.ts +64 -0
  267. package/dist/schemas/marketplace.d.ts.map +1 -0
  268. package/dist/schemas/marketplace.js +17 -0
  269. package/dist/schemas/marketplace.js.map +1 -0
  270. package/dist/schemas/panels.d.ts +61 -0
  271. package/dist/schemas/panels.d.ts.map +1 -0
  272. package/dist/schemas/panels.js +51 -0
  273. package/dist/schemas/panels.js.map +1 -0
  274. package/dist/schemas/personas.d.ts +122 -0
  275. package/dist/schemas/personas.d.ts.map +1 -0
  276. package/dist/schemas/personas.js +102 -0
  277. package/dist/schemas/personas.js.map +1 -0
  278. package/dist/schemas/plan-limits.d.ts +172 -0
  279. package/dist/schemas/plan-limits.d.ts.map +1 -0
  280. package/dist/schemas/plan-limits.js +126 -0
  281. package/dist/schemas/plan-limits.js.map +1 -0
  282. package/dist/schemas/ports.d.ts +71 -0
  283. package/dist/schemas/ports.d.ts.map +1 -0
  284. package/dist/schemas/ports.js +42 -0
  285. package/dist/schemas/ports.js.map +1 -0
  286. package/dist/schemas/provider-oauth.d.ts +128 -0
  287. package/dist/schemas/provider-oauth.d.ts.map +1 -0
  288. package/dist/schemas/provider-oauth.js +91 -0
  289. package/dist/schemas/provider-oauth.js.map +1 -0
  290. package/dist/schemas/provider-subscriptions.d.ts +9 -0
  291. package/dist/schemas/provider-subscriptions.d.ts.map +1 -0
  292. package/dist/schemas/provider-subscriptions.js +3 -0
  293. package/dist/schemas/provider-subscriptions.js.map +1 -0
  294. package/dist/schemas/public.d.ts +32 -0
  295. package/dist/schemas/public.d.ts.map +1 -0
  296. package/dist/schemas/public.js +29 -0
  297. package/dist/schemas/public.js.map +1 -0
  298. package/dist/schemas/push.d.ts +55 -0
  299. package/dist/schemas/push.d.ts.map +1 -0
  300. package/dist/schemas/push.js +60 -0
  301. package/dist/schemas/push.js.map +1 -0
  302. package/dist/schemas/secrets.d.ts +86 -0
  303. package/dist/schemas/secrets.d.ts.map +1 -0
  304. package/dist/schemas/secrets.js +51 -0
  305. package/dist/schemas/secrets.js.map +1 -0
  306. package/dist/schemas/sessions.d.ts +32 -0
  307. package/dist/schemas/sessions.d.ts.map +1 -0
  308. package/dist/schemas/sessions.js +11 -0
  309. package/dist/schemas/sessions.js.map +1 -0
  310. package/dist/schemas/settings.d.ts +548 -0
  311. package/dist/schemas/settings.d.ts.map +1 -0
  312. package/dist/schemas/settings.js +259 -0
  313. package/dist/schemas/settings.js.map +1 -0
  314. package/dist/schemas/share.d.ts +49 -0
  315. package/dist/schemas/share.d.ts.map +1 -0
  316. package/dist/schemas/share.js +26 -0
  317. package/dist/schemas/share.js.map +1 -0
  318. package/dist/schemas/shared.d.ts +22 -0
  319. package/dist/schemas/shared.d.ts.map +1 -0
  320. package/dist/schemas/shared.js +16 -0
  321. package/dist/schemas/shared.js.map +1 -0
  322. package/dist/schemas/system.d.ts +86 -0
  323. package/dist/schemas/system.d.ts.map +1 -0
  324. package/dist/schemas/system.js +64 -0
  325. package/dist/schemas/system.js.map +1 -0
  326. package/dist/schemas/terminal.d.ts +230 -0
  327. package/dist/schemas/terminal.d.ts.map +1 -0
  328. package/dist/schemas/terminal.js +133 -0
  329. package/dist/schemas/terminal.js.map +1 -0
  330. package/dist/schemas/usage.d.ts +113 -0
  331. package/dist/schemas/usage.d.ts.map +1 -0
  332. package/dist/schemas/usage.js +91 -0
  333. package/dist/schemas/usage.js.map +1 -0
  334. package/dist/schemas/vpn.d.ts +229 -0
  335. package/dist/schemas/vpn.d.ts.map +1 -0
  336. package/dist/schemas/vpn.js +115 -0
  337. package/dist/schemas/vpn.js.map +1 -0
  338. package/dist/schemas/webext.d.ts +98 -0
  339. package/dist/schemas/webext.d.ts.map +1 -0
  340. package/dist/schemas/webext.js +36 -0
  341. package/dist/schemas/webext.js.map +1 -0
  342. package/dist/schemas/workflows.d.ts +881 -0
  343. package/dist/schemas/workflows.d.ts.map +1 -0
  344. package/dist/schemas/workflows.js +164 -0
  345. package/dist/schemas/workflows.js.map +1 -0
  346. package/dist/schemas/workspace-repos.d.ts +143 -0
  347. package/dist/schemas/workspace-repos.d.ts.map +1 -0
  348. package/dist/schemas/workspace-repos.js +87 -0
  349. package/dist/schemas/workspace-repos.js.map +1 -0
  350. package/dist/schemas/workspace-search.d.ts +170 -0
  351. package/dist/schemas/workspace-search.d.ts.map +1 -0
  352. package/dist/schemas/workspace-search.js +85 -0
  353. package/dist/schemas/workspace-search.js.map +1 -0
  354. package/dist/schemas/workspace-setup.d.ts +48 -0
  355. package/dist/schemas/workspace-setup.d.ts.map +1 -0
  356. package/dist/schemas/workspace-setup.js +26 -0
  357. package/dist/schemas/workspace-setup.js.map +1 -0
  358. package/dist/schemas/workspace-tree.d.ts +120 -0
  359. package/dist/schemas/workspace-tree.d.ts.map +1 -0
  360. package/dist/schemas/workspace-tree.js +111 -0
  361. package/dist/schemas/workspace-tree.js.map +1 -0
  362. package/dist/webext-links.d.ts +13 -0
  363. package/dist/webext-links.d.ts.map +1 -0
  364. package/dist/webext-links.js +24 -0
  365. package/dist/webext-links.js.map +1 -0
  366. package/dist/webext-protocol.d.ts +8 -0
  367. package/dist/webext-protocol.d.ts.map +1 -0
  368. package/dist/webext-protocol.js +7 -0
  369. package/dist/webext-protocol.js.map +1 -0
  370. package/dist/workflow-faults.d.ts +1 -1
  371. package/dist/workflow-faults.d.ts.map +1 -1
  372. package/dist/workspace-state.d.ts +11 -2
  373. package/dist/workspace-state.d.ts.map +1 -1
  374. package/dist/workspace-state.js +4 -2
  375. package/dist/workspace-state.js.map +1 -1
  376. package/package.json +17 -6
  377. package/src/agent-catalog.test.ts +1 -1
  378. package/src/agent-catalog.ts +4 -11
  379. package/src/chores/chores.ts +1 -1
  380. package/src/chores/probes.ts +1 -1
  381. package/src/chores/stack.test.ts +19 -0
  382. package/src/chores/stack.ts +19 -3
  383. package/src/chores/verdict.test.ts +11 -1
  384. package/src/chores/verdict.ts +1 -1
  385. package/src/command-classes.test.ts +378 -0
  386. package/src/command-classes.ts +378 -0
  387. package/src/contracts/activity.contract.ts +1 -1
  388. package/src/contracts/agent.contract.ts +22 -12
  389. package/src/contracts/agents.contract.ts +6 -6
  390. package/src/contracts/automations.contract.ts +2 -2
  391. package/src/contracts/capabilities.contract.ts +3 -4
  392. package/src/contracts/chores.contract.ts +2 -1
  393. package/src/contracts/ci.contract.ts +2 -2
  394. package/src/contracts/claude.contract.ts +2 -2
  395. package/src/contracts/cursor.contract.ts +2 -2
  396. package/src/contracts/drafts.contract.ts +2 -1
  397. package/src/contracts/endpoints.contract.ts +2 -1
  398. package/src/contracts/exit.contract.ts +2 -1
  399. package/src/contracts/extensions.contract.ts +5 -5
  400. package/src/contracts/git.contract.ts +19 -18
  401. package/src/contracts/grok.contract.ts +2 -1
  402. package/src/contracts/history.contract.ts +2 -1
  403. package/src/contracts/host.contract.ts +4 -1
  404. package/src/contracts/intentic.contract.ts +2 -1
  405. package/src/contracts/inventory.contract.ts +1 -1
  406. package/src/contracts/logs.contract.ts +7 -1
  407. package/src/contracts/loops.contract.ts +2 -2
  408. package/src/contracts/panels.contract.ts +2 -1
  409. package/src/contracts/personas.contract.ts +3 -3
  410. package/src/contracts/ports.contract.ts +3 -2
  411. package/src/contracts/prepush.contract.ts +2 -1
  412. package/src/contracts/providers.contract.ts +2 -1
  413. package/src/contracts/public.contract.ts +3 -2
  414. package/src/contracts/push.contract.ts +2 -1
  415. package/src/contracts/runner.contract.ts +33 -1
  416. package/src/contracts/secrets.contract.ts +2 -1
  417. package/src/contracts/sessions.contract.ts +1 -1
  418. package/src/contracts/settings.contract.ts +3 -9
  419. package/src/contracts/share.contract.ts +3 -2
  420. package/src/contracts/skills.contract.ts +2 -1
  421. package/src/contracts/system.contract.ts +6 -9
  422. package/src/contracts/translator.contract.ts +4 -1
  423. package/src/contracts/usage.contract.ts +1 -1
  424. package/src/contracts/vpn.contract.ts +2 -8
  425. package/src/contracts/webext.contract.ts +37 -0
  426. package/src/contracts/workflows.contract.ts +2 -2
  427. package/src/contracts/workspace.contract.ts +10 -12
  428. package/src/conversation-ids.test.ts +1 -1
  429. package/src/definition-action.ts +8 -0
  430. package/src/definition.ts +235 -0
  431. package/src/events.ts +128 -22
  432. package/src/fast-tier.ts +1 -1
  433. package/src/history-state.ts +5 -0
  434. package/src/host-protocol.ts +1 -1
  435. package/src/hostnames.test.ts +2 -2
  436. package/src/index.ts +61 -1
  437. package/src/listener-protocol.ts +1 -1
  438. package/src/quick-model.ts +1 -1
  439. package/src/runner-protocol.ts +35 -5
  440. package/src/schemas/activity.ts +123 -0
  441. package/src/schemas/agent.ts +409 -0
  442. package/src/schemas/agents.ts +802 -0
  443. package/src/schemas/automations.ts +510 -0
  444. package/src/schemas/capabilities.ts +729 -0
  445. package/src/schemas/ci.ts +235 -0
  446. package/src/schemas/claude-gate.ts +13 -0
  447. package/src/schemas/codebase-health.ts +64 -0
  448. package/src/schemas/computers.ts +287 -0
  449. package/src/schemas/drafts.ts +108 -0
  450. package/src/schemas/environment.ts +276 -0
  451. package/src/schemas/exit.ts +222 -0
  452. package/src/schemas/extension-updates.ts +322 -0
  453. package/src/schemas/fast-mode.ts +8 -0
  454. package/src/schemas/git-history.ts +265 -0
  455. package/src/schemas/git.ts +430 -0
  456. package/src/schemas/history.ts +112 -0
  457. package/src/schemas/hosts.ts +37 -0
  458. package/src/schemas/intentic.ts +2 -0
  459. package/src/schemas/internal.ts +17 -0
  460. package/src/schemas/inventory.ts +68 -0
  461. package/src/schemas/logs.ts +96 -0
  462. package/src/schemas/loops.ts +401 -0
  463. package/src/schemas/maintenance.ts +421 -0
  464. package/src/schemas/marketplace.ts +23 -0
  465. package/src/schemas/panels.ts +99 -0
  466. package/src/schemas/personas.ts +281 -0
  467. package/src/schemas/plan-limits.ts +255 -0
  468. package/src/schemas/ports.ts +84 -0
  469. package/src/schemas/provider-oauth.ts +191 -0
  470. package/src/schemas/provider-subscriptions.ts +11 -0
  471. package/src/schemas/public.ts +52 -0
  472. package/src/schemas/push.ts +112 -0
  473. package/src/schemas/secrets.ts +68 -0
  474. package/src/schemas/sessions.ts +16 -0
  475. package/src/schemas/settings.ts +932 -0
  476. package/src/schemas/share.ts +71 -0
  477. package/src/schemas/shared.ts +32 -0
  478. package/src/schemas/system.ts +156 -0
  479. package/src/schemas/terminal.ts +286 -0
  480. package/src/schemas/usage.ts +303 -0
  481. package/src/{schemas.test.ts → schemas/version-seam.test.ts} +4 -2
  482. package/src/schemas/vpn.ts +215 -0
  483. package/src/schemas/webext.ts +79 -0
  484. package/src/schemas/workflows.ts +413 -0
  485. package/src/schemas/workspace-repos.ts +123 -0
  486. package/src/schemas/workspace-search.ts +141 -0
  487. package/src/schemas/workspace-setup.ts +43 -0
  488. package/src/schemas/workspace-tree.ts +234 -0
  489. package/src/title.test.ts +1 -1
  490. package/src/webext-links.ts +80 -0
  491. package/src/webext-protocol.ts +27 -0
  492. package/src/workflow-faults.test.ts +1 -1
  493. package/src/workflow-faults.ts +1 -1
  494. package/src/workspace-state.test.ts +7 -3
  495. package/src/workspace-state.ts +27 -13
  496. package/dist/schemas.d.ts +0 -9800
  497. package/dist/schemas.d.ts.map +0 -1
  498. package/dist/schemas.js +0 -4252
  499. package/dist/schemas.js.map +0 -1
  500. package/src/schemas.ts +0 -9999
@@ -0,0 +1,729 @@
1
+ // Capabilities: the one `{ id, kind, config }` entry every connection a sandbox holds is written as.
2
+ // The config arm per kind, the discriminated union over them, and what the Capabilities page reads:
3
+ // each row's live status, the probe behind it, and the connect/rename/sign-in inputs.
4
+ import { z } from "zod";
5
+ import { ExitConfigSchema } from "./exit.js";
6
+ import { entryId } from "./internal.js";
7
+ import { ServiceKindSchema } from "./inventory.js";
8
+ import { VpnConfigSchema } from "./vpn.js";
9
+ // Everything a user adds to a sandbox is a capability with an idempotent apply + a status check. The manifest is
10
+ // the source of truth for what's active; `mcp`-kind entries also feed the agent's MCP servers each turn. DevOps
11
+ // is the capability that scaffolds the intent/desired-state repos, until it's active the sandbox is empty.
12
+
13
+ export const CapabilityKindSchema = z.enum([
14
+ "devops",
15
+ "monorepo",
16
+ "mcp",
17
+ "service",
18
+ "integration",
19
+ "cli",
20
+ "plugin",
21
+ "extension",
22
+ "ssh",
23
+ "vpn",
24
+ "exit",
25
+ "docker",
26
+ "browser",
27
+ "identity",
28
+ "host",
29
+ "webext",
30
+ "agent",
31
+ "endpoint",
32
+ "localmodel",
33
+ "wallet",
34
+ ]);
35
+ export type CapabilityKind = z.infer<typeof CapabilityKindSchema>;
36
+ export const CapabilityStateSchema = z.enum(["active", "pending", "error", "inactive"]);
37
+ export type CapabilityState = z.infer<typeof CapabilityStateSchema>;
38
+ // Per-kind config. Secrets (an mcp token) live here and are denylisted like tools.json.
39
+ export const McpConfigSchema = z.object({
40
+ url: z.url().describe("Where the tool server answers."),
41
+ token: z.string().optional().describe("The credential it needs, if any. Stored, never echoed back."),
42
+ });
43
+ export const ServiceConfigSchema = z.object({
44
+ service: ServiceKindSchema.describe("Which service to provision."),
45
+ domain: z.string().min(1).describe("The address it should answer on."),
46
+ on: z.string().min(1).describe("Which machine to put it on."),
47
+ expose: z.string().min(1).describe("How it should be reachable."),
48
+ });
49
+ // External-app credential injected into DEPLOYED apps (i.have.stripe → STRIPE_API_KEY from env). Agent-facing
50
+ // connectors are `cli` capabilities instead (see below), not integrations.
51
+ // Closed, unlike a `cli` provider: this becomes an `i.have.<provider>` entry in deploy.config.ts, and the
52
+ // desired-state resolver only knows the providers in InventoryProviderSchema. So an integration card is NOT
53
+ // extension-contributable, the vocabulary belongs to the deploy engine, not to a manifest.
54
+ export const IntegrationConfigSchema = z.object({
55
+ provider: z.literal("stripe").describe("Which outside service's credential to make available to deployed apps."),
56
+ });
57
+ // A `cli` capability gives the AGENT an authenticated command-line tool (not a deployed-app credential like
58
+ // `integration`): the credential + any non-secret URL are stored here and injected into the agent's env each
59
+ // turn (see cliEnvOf), and an .agents/skills/<id> cheatsheet teaches the agent to use it via curl. The provider
60
+ // data (fields, env, skill, image fragment) is DATA in an installed extension's `contributes.capabilities`, not
61
+ // a per-provider schema arm, so the config is `provider` + arbitrary string fields, validated against the
62
+ // card's declared fields at add-time (see the sandbox's capabilities/contributions.ts) rather than by this schema.
63
+ export const CliConfigSchema = z
64
+ .object({
65
+ provider: z
66
+ .string()
67
+ .min(1)
68
+ .describe(
69
+ "Which tool to give the agent. The rest of the fields are whatever that tool's own card declares it needs, and are checked against it when you connect.",
70
+ ),
71
+ })
72
+ .catchall(z.string());
73
+ // A Claude Code plugin from a git repo. The daemon only owns the checkout; the Agent SDK's plugin loader reads
74
+ // its internals (skills/agents/hooks/commands/.mcp.json). `path` = subdirectory for plugins that live inside a
75
+ // marketplace/monorepo checkout. `token` = https auth for private repos (never echoed; becomes hasToken).
76
+ export const PluginConfigSchema = z.object({
77
+ url: z.url().describe("The repository to take the plugin from."),
78
+ // Branch / tag / commit sha to pin; absent = the default branch's HEAD.
79
+ ref: z.string().min(1).optional().describe("A branch, tag or commit to pin to. Leave it out to follow the default branch."),
80
+ path: z
81
+ .string()
82
+ .min(1)
83
+ .refine((value) => !value.split("/").includes(".."), { message: "path must stay inside the checkout" })
84
+ .optional()
85
+ .describe("Where inside the repository the plugin lives, for one that sits in a larger checkout."),
86
+ token: z.string().min(1).optional().describe("A credential for a private repository. Stored, never echoed back."),
87
+ });
88
+ // An intentic extension from a git repo (an intentic-extension.json checkout. UI bundle + agent contributions
89
+ // + processes). Unlike `plugin`, `ref` is a REQUIRED full commit sha: extension code runs trusted in the
90
+ // owner's browser, so the owner approves exactly the code that runs, pin by construction, updates are explicit
91
+ // re-adds at a new sha. `path`/`token` as in PluginConfigSchema.
92
+ export const ExtensionConfigSchema = z.object({
93
+ url: z.url().describe("The repository to take the extension from."),
94
+ ref: z
95
+ .string()
96
+ .regex(/^[0-9a-f]{40}$/, "ref must be a full 40-character commit sha")
97
+ .describe(
98
+ "The exact commit to install, in full. Required rather than optional because extension code runs with your browser's trust: the owner approves precisely the code that runs, and an update is a deliberate re-install at a new commit.",
99
+ ),
100
+ path: z
101
+ .string()
102
+ .min(1)
103
+ .refine((value) => !value.split("/").includes(".."), { message: "path must stay inside the checkout" })
104
+ .optional()
105
+ .describe("Where inside the repository the extension lives, for one that sits in a larger checkout."),
106
+ token: z.string().min(1).optional().describe("A credential for a private repository. Stored, never echoed back."),
107
+ /* The registry row's tier, copied onto the install by the browse pre-fill. `premium` is what the daemon's
108
+ * two pool duties key off: installing (or updating) donates the owner's credits to the publisher, the
109
+ * gate the apply passes through, and enabling needs the owner's membership. An absent tier means free,
110
+ * donates nothing, and asks for nothing; NO usage is metered or reported either way. Self-declared rather
111
+ * than verified against the registry (the daemon is the owner's own machine; a stripped marker skips a
112
+ * donation the owner was choosing to make, which cheats the creator once, and is exactly the honesty the
113
+ * open-source posture accepts and the docs state). */
114
+ tier: z
115
+ .enum(["free", "premium"])
116
+ .optional()
117
+ .describe(
118
+ "Whether installing this donates credits to its publisher. Absent means free, which donates nothing and asks for nothing. Taken from the listing rather than checked against it, which is the honesty an open-source posture accepts.",
119
+ ),
120
+ /* The registry this install's row lives in, copied on by the browse pre-fill like `tier`, what the update
121
+ * check compares the pinned sha against and reads advisories from. Absent (a hand-typed git install) falls
122
+ * back to the official registry: if the extension is listed there, its updates and its blocked-markings
123
+ * concern this owner exactly as much as anyone's. */
124
+ registry: z
125
+ .url()
126
+ .optional()
127
+ .describe(
128
+ "Which registry this install came from, which is what update checks and security advisories are read against. Absent falls back to the official one.",
129
+ ),
130
+ });
131
+ // A remote machine the AGENT can reach over SSH. One capability = one machine; the id is its ssh-config Host
132
+ // alias, so the agent runs `ssh <id> "…"`. The handler writes a per-machine config block + a 0600 key/password
133
+ // file under ~/.ssh (see the ssh handler), so, unlike `cli`, nothing is injected into the agent's env, and
134
+ // several machines never collide. Discriminated by auth so exactly one credential shape is required.
135
+ export const SshConfigSchema = z.discriminatedUnion("auth", [
136
+ z.object({
137
+ auth: z.literal("key").describe("Sign in with a key."),
138
+ host: z.string().min(1).describe("The machine's address."),
139
+ port: z.coerce.number().default(22).describe("Which port it listens on."),
140
+ user: z.string().min(1).describe("Which user to connect as."),
141
+ privateKey: z.string().min(1).describe("The private key, whole. Stored with tight permissions and never echoed back."),
142
+ }),
143
+ z.object({
144
+ auth: z.literal("password").describe("Sign in with a password."),
145
+ host: z.string().min(1).describe("The machine's address."),
146
+ port: z.coerce.number().default(22).describe("Which port it listens on."),
147
+ user: z.string().min(1).describe("Which user to connect as."),
148
+ password: z.string().min(1).describe("The password. Stored, never echoed back."),
149
+ }),
150
+ ]);
151
+ /* What is OPTIONAL about the in-sandbox Docker Engine. The engine itself takes no configuring, the capability
152
+ * either runs dockerd or it doesn't, so this holds only what a user chooses, and the bar for landing here is
153
+ * that the sandbox works without it. (`--privileged` therefore is not here and never will be: dockerd does not
154
+ * run without it, so a switch would offer a broken sandbox as a choice.)
155
+ *
156
+ * TWO FAMILIES, and which one an option belongs to is the most consequential thing about it, because it is the
157
+ * difference between a five-second change and a five-minute one:
158
+ *
159
+ * IMAGE (`gpu`), rides the environment overlay. Changing it recomposes the Dockerfile, so it costs an
160
+ * owner-approved rebuild and a container recreate. Only `fragment()` may read these.
161
+ * ENGINE (everything below it): /etc/docker/daemon.json, which dockerd reads at start. Changing one
162
+ * rewrites the file and restarts dockerd: no rebuild, no new image, but it DOES stop whatever containers
163
+ * the engine is running, which is why it is disclosed rather than silently applied.
164
+ *
165
+ * Keep the split honest in both directions: an engine option that leaked into the fragment would demand a
166
+ * rebuild for a value dockerd re-reads anyway, and an image option applied by rewriting a file would silently
167
+ * do nothing. The card badges the difference per field (CapabilityField.rebuild).
168
+ *
169
+ * Flat rather than nested, and "on"/"off" rather than booleans, because the capability form carries a flat
170
+ * bag of strings, one spelling of a two-state config across the manifest (the vpn's pfs/aggressive) beats a
171
+ * second one for the same shape. */
172
+ export const DockerConfigSchema = z.object({
173
+ gpu: z.enum(["on", "off"]).default("off"),
174
+ /* A pull-through cache or mirror, for a slow, metered or air-gapped link. The nested engine starts with an
175
+ * empty image store, so the first `docker compose up` in a workspace pulls everything from scratch. */
176
+ registryMirror: z.url().optional(),
177
+ // Registries reachable over plain http or with a self-signed certificate, a LAN registry, or the one a
178
+ // homelab runs beside the sandbox. Space- or comma-separated host:port entries.
179
+ insecureRegistries: z.string().optional(),
180
+ /* The subnet the nested engine carves its container networks out of. Docker's default (172.17/16 and the
181
+ * 172.16/12 pools around it) is the single most common collision with a corporate VPN or a homelab LAN,
182
+ * and the failure it produces is unusually cruel: the sandbox keeps working, dockerd keeps working, and
183
+ * exactly the internal hosts the user was reaching for become unreachable, routed into a bridge instead
184
+ * of down the tunnel. One CIDR, and the pool is carved from it. */
185
+ addressPool: z.string().optional(),
186
+ });
187
+ // A logged-in browser session the AGENT drives via Playwright MCP tools, for social platforms whose APIs can't
188
+ // cover "all the actions" (X reads are paywalled; X community-join and YouTube community-posts have no API). The
189
+ // session lives in a persisted Chromium profile under .intentic/local/browser/<id>, established through the guided-login
190
+ // WebSocket (/system/browser-login) or by the agent signing in itself. Chromium itself rides this kind's
191
+ // Dockerfile fragment, applied on an owner rebuild.
192
+ //
193
+ // ONE CAPABILITY = ONE ACCOUNT, not one platform: several entries may name the same `platform` (reddit-work and
194
+ // reddit-personal), and the ID is what the profile, the login, the passkey and the agent's tool prefix are all
195
+ // keyed by, so each account signs in separately and is disconnected on its own.
196
+ //
197
+ // `platform` is an OPEN slug, not an enum, for the reason `cli`'s `provider` is: a platform is a card, a login URL
198
+ // and a skill in an installed extension's `contributes.capabilities`, so the set of them is not a fact this
199
+ // contract can know. The add route validates it against the contributed entry instead (see contributions.ts).
200
+ //
201
+ // `username`/`password` are the account's SIGN-IN CREDENTIALS, on every card rather than declared per platform
202
+ // (which box a login form wants filled is the same fact everywhere). Both optional: a profile that signed in by
203
+ // hand needs neither, and the password is the entry's SECRET, stored so the daemon can type it into the page on
204
+ // the agent's behalf (the accounts tools), never so the agent can read it. When the agent signs UP it has the
205
+ // daemon generate and store one here, so the credential outlives the profile's cookies.
206
+ //
207
+ // `catchall`, the `cli` precedent, for the card that carries no site at all: a GENERIC browser session, where the
208
+ // page to open and what the account is for are answered on the form instead of pinned in a manifest. A site card
209
+ // pins its URLs and declares no fields; the generic one declares fields and pins nothing, one kind, because
210
+ // nothing downstream of the URLs differs. Which other keys are legal is the CARD's business, checked against its
211
+ // declared fields at add-time (validateContributionConfig), not this schema's.
212
+ //
213
+ // `identity` names the identity capability this account was born from (or was filed under): the account then
214
+ // lives INSIDE that identity's browser, one profile, one set of cookies, which is what makes "Continue with
215
+ // Google" one click instead of a second Google login the platform would block. Absent ⇒ the account keeps its
216
+ // own private profile, exactly as every hand-connected account always has.
217
+ //
218
+ // `purpose` and `openedAt` are the ACCOUNT's own history, core for the same reason `identity` is: what this
219
+ // account was opened for and when are facts about the sandbox's own past, not about any site, and a site card
220
+ // that declared no fields (every one of them, a pinned-URL card declares none) could not carry them otherwise.
221
+ // They are what makes the roster answerable months later, when "do we already have an account here" is asked by
222
+ // a session that was not the one that signed up. Both optional: an account the owner connected by hand has no
223
+ // signup story to tell, and an empty purpose is better than a fabricated one.
224
+ export const BrowserConfigSchema = z
225
+ .object({
226
+ platform: z.string().min(1),
227
+ username: z.string().optional(),
228
+ password: z.string().optional(),
229
+ identity: z.string().optional(),
230
+ purpose: z.string().optional(),
231
+ // ISO-8601 date, stamped when the agent opens the account, absent for one connected by hand.
232
+ openedAt: z.string().optional(),
233
+ /* WHERE THIS ACCOUNT BROWSES FROM: the id of an `exit` capability. Set it and every page this profile
234
+ * opens comes out of that country, with the browser's clock, locale and languages set to match.
235
+ *
236
+ * Only meaningful on an account that owns its OWN profile. An account born from an identity shares
237
+ * that identity's browser, cookies, passkeys and all, so it shares its exit too and this field is
238
+ * ignored for it (see the daemon's browser/browser-exit.ts). That is not a limitation, it is the
239
+ * point: one Google session appearing from Berlin in one tab and Osaka in another is a far louder
240
+ * signal than any address, so the exit belongs to whatever owns the profile. */
241
+ exit: z.string().optional(),
242
+ })
243
+ .catchall(z.string());
244
+ /* ONE EMAIL IDENTITY THE SANDBOX ACTS AS ONLINE, the container platform accounts are born from, and the answer
245
+ * to "who is this sandbox on the internet" being twelve separate logins today.
246
+ *
247
+ * WHAT IT OWNS IS A BROWSER. An identity is one persisted Chromium profile the way a person's own browser is
248
+ * one: Google signed in once (by the OWNER's hand, in the guided window, automated Google logins are exactly
249
+ * what Google blocks), and every account born from it sharing those cookies, so a platform's "Continue with
250
+ * Google" is a click rather than an email round-trip. Browser accounts join it by naming it in their `identity`
251
+ * field; accounts that name no identity keep their own private profile, which is how work and personal stay two
252
+ * containers, two identities, not one profile with a flag.
253
+ *
254
+ * WHY A CAPABILITY AND NOT A PERSONA: this card holds SECRETS (an email password the daemon types but never
255
+ * shows) and a live profile's identity, and the personas file is committed to git precisely because it holds
256
+ * neither (personas-store.ts). A persona is how a session BEHAVES; an identity is who the browser IS SIGNED IN
257
+ * as. A persona may point at accounts that live inside an identity, and neither card needs to know the other
258
+ * exists.
259
+ *
260
+ * `email` is the identity itself, what signup forms get typed into their username box, and how the guided
261
+ * login knows where to start (gmail.com ⇒ accounts.google.com; `loginUrl` overrides for any other provider).
262
+ * `password` is the entry's SECRET, the browser-config precedent: typed by the daemon, never readable.
263
+ * `mailbox` names a connected mail capability (imap, google) the narrow code tool reads, the agent asks for
264
+ * "the latest code from this site" and gets six digits, not an inbox.
265
+ * `openAccounts` is THE consent switch, off by default and a select rather than a boolean (the host-scope
266
+ * precedent, form values arrive as strings): automated signup is against most platforms' terms, so minting
267
+ * accounts unattended is an explicit, per-identity, informed choice, never a silent global default. */
268
+ export const IdentityConfigSchema = z.object({
269
+ email: z.string().min(3),
270
+ password: z.string().optional(),
271
+ mailbox: z.string().optional(),
272
+ loginUrl: z.url().optional(),
273
+ openAccounts: z.enum(["on", "off"]).default("off"),
274
+ /* WHERE THIS IDENTITY LIVES, the id of an `exit` capability. An identity OWNS a browser profile, and every
275
+ * account born from it shares that profile, so setting it here sets it for all of them at once, which is
276
+ * the only coherent place to set it: the shared thing is one browser, and one browser is in one place. */
277
+ exit: z.string().optional(),
278
+ });
279
+ export type IdentityConfig = z.infer<typeof IdentityConfigSchema>;
280
+ /* A connected COMPUTER of the user's own, the inverse of `ssh`, which reaches a server the sandbox can dial.
281
+ * A machine behind NAT can't be dialled, so it dials US: the @intentic/machine agent (installed by a one-liner,
282
+ * enrolled with a single-use pairing token) holds one outbound WebSocket to this daemon and serves an MCP tool
283
+ * surface, shell, files, screenshots, from the far end. The daemon tunnels the agent's JSON-RPC over it and
284
+ * never implements a tool itself, so the machine's capabilities evolve with ITS binary, not with a daemon release.
285
+ *
286
+ * One capability = one machine. The id is the machine's name and namespaces its tools (mcp__laptop__run_command),
287
+ * so several connected machines never collide, the `ssh` precedent. `platform` splits the SKILL pack: a Windows
288
+ * machine is taught PowerShell and a Linux one systemd/D-Bus, and neither carries the other's noise.
289
+ *
290
+ * SCOPES ARE THE GRANT, and they are enforced ON THE MACHINE, never here: the daemon pushes them down on every
291
+ * connect, and the agent refuses out-of-scope calls itself. So a sandbox that is compromised, or an agent talked
292
+ * into it by something it read on the internet, still cannot exceed what the owner ticked. `roots` bounds file
293
+ * reads AND writes to a set of directories (empty ⇒ the user's home).
294
+ *
295
+ * Like a browser `platform`, this is an OPEN slug: an OS is a card plus a skill pack in an installed extension's
296
+ * `contributes.capabilities`, and teaching the agent a new one should not need a daemon release. */
297
+ // on/off rather than a boolean: capability configs arrive from the add form as strings (the vpn autoConnect
298
+ // precedent), and a select is what the form renders for an enum.
299
+ const hostScope = z.enum(["on", "off"]);
300
+ export const HostScopesSchema = z.object({
301
+ // Run commands in a real shell (PowerShell on Windows, the login shell on Linux). Off ⇒ files/screen only.
302
+ shell: hostScope.default("on"),
303
+ // Create, modify and trash files under `roots`. Reads are always allowed within them; this is the write half.
304
+ write: hostScope.default("off"),
305
+ // Capture the screen. Off ⇒ screenshot refuses, and the agent is told so rather than getting a black frame.
306
+ screen: hostScope.default("on"),
307
+ /* Move the pointer, click, type and scroll. GUI work, for the things with no command-line way in. Its own
308
+ * switch rather than part of `screen` because looking and touching are not the same permission: a screenshot
309
+ * is bounded by what is on the display, while one click can confirm a dialog nobody read. Default off, like
310
+ * `write`, and for the same reason, a user who has not thought about it should not discover the agent has
311
+ * been driving their desktop. */
312
+ control: hostScope.default("off"),
313
+ /* Start, stop and restart the Intentic sandboxes running on this machine, the grant that makes one sandbox
314
+ * the machine's supervisor. Its own switch rather than a use of `shell` because it is NARROWER: a user can
315
+ * hand an agent the sandbox fleet without handing it a shell, and the fleet operations are named rather than
316
+ * whatever a model improvises with docker. Default off, like every switch that changes the machine. */
317
+ sandboxes: hostScope.default("off"),
318
+ /* Remove a sandbox from this machine, its container, its network, and the named volumes holding its /work
319
+ * and /history. Its own switch rather than part of `sandboxes` because the two differ in the only way that
320
+ * matters here: everything `sandboxes` grants is undone by doing it again, and this is undone by nothing.
321
+ * A user who delegated "restart my sandboxes when they wedge" did not thereby agree to lose one. */
322
+ sandboxRemove: hostScope.default("off"),
323
+ /* Run a command this machine's agent classifies as destructive: a recursive delete, a formatted disk, a
324
+ * removed Docker volume (command-classes.ts, the same classifier the sandbox's own gate reads).
325
+ *
326
+ * ITS OWN SWITCH UNDER `shell`, and the reason is the asymmetry this whole feature turns on. Inside the
327
+ * sandbox a bad `rm -rf` costs a container that exists to be thrown away, so the gate there can afford to
328
+ * hold only the handful of commands nothing undoes and wave the rest through. This is somebody's laptop.
329
+ * There is no image to recreate it from, no checkpoint, no worktree: `rm -rf ~/projects` is the afternoon
330
+ * everybody remembers. And there is no card to raise either, the machine answers a tool call with a value
331
+ * and cannot park it while somebody thinks, so the honest form of "ask me" here is "refuse until they
332
+ * ticked it", which is exactly what a scope is.
333
+ *
334
+ * Default off, with `shell` default ON, which is the pairing to read carefully: a connected computer runs
335
+ * commands out of the box, because that is what people connect one for, and the ones that delete are the
336
+ * ones they have to say yes to. */
337
+ destructive: hostScope.default("off"),
338
+ // One directory per line. Empty ⇒ the machine's home directory, which is what the agent reports at connect.
339
+ roots: z.string().optional(),
340
+ });
341
+ export type HostScopes = z.infer<typeof HostScopesSchema>;
342
+ export const HostConfigSchema = HostScopesSchema.extend({ platform: z.string().min(1) });
343
+ /* THE USER'S OWN BROWSER, reached through the extension they installed in it: the `webext` capability's config.
344
+ *
345
+ * The sibling of `host` and deliberately not an arm of it. A connected computer runs commands; a connected
346
+ * browser has one power a sandbox's own Chromium can never have, and it is the whole reason this kind exists:
347
+ * it is ALREADY SIGNED IN, as the person, with their passkeys, their hardware second factor, their corporate
348
+ * SSO and their genuine fingerprint. That is the set of sites the sandbox's browser cannot reach at all, and
349
+ * copying a session out of one to fake it is what gets an account locked.
350
+ *
351
+ * WHICH SITES the agent may touch is NOT in here, and that omission is the security design rather than a gap.
352
+ * Origins are Chrome's own optional host permissions, asked for by the extension with the person's hands on
353
+ * the keyboard and revocable in the browser's own UI, so the boundary that matters is enforced by the browser
354
+ * against the extension, one layer below anything a sandbox could reach. What the card carries is the coarser
355
+ * question the sandbox's owner answers once: what KIND of thing may happen on a site they have already allowed.
356
+ *
357
+ * Every switch is enforced IN THE EXTENSION (as every host scope is enforced on the machine): the daemon
358
+ * pushes them on connect and on every edit, and nothing on this side checks one. */
359
+ const webextScope = z.enum(["on", "off"]);
360
+ export const WebExtScopesSchema = z.object({
361
+ // Read a granted page: its elements, its text, its tabs. The floor of usefulness, so it defaults on; with
362
+ // it off the connection is inert and the card says so rather than pretending.
363
+ read: webextScope.default("on"),
364
+ /* Click, type, press keys, navigate. ON by default, unlike a computer's `control`, and the difference is
365
+ * what the two things ARE: driving a desktop is the last resort after every command-line route failed,
366
+ * while driving the page IS this connector — a browser connection that may only look is a worse version
367
+ * of fetching the URL. The grant that actually bounds it is per-site and lives in the browser. */
368
+ act: webextScope.default("on"),
369
+ /* Capture the visible tab as an image. Off by default because it is the one read whose contents nothing
370
+ * here bounds: the page serialization above is a list this extension built and can keep to granted frames,
371
+ * while a screenshot is whatever pixels that window happens to be showing. Worth turning on for canvas
372
+ * apps and PDF viewers, which is exactly when the DOM says nothing. */
373
+ screenshot: webextScope.default("off"),
374
+ /* Hand a site's logged-in session to the sandbox's own browser ("Connect this site"), so a job can carry
375
+ * on overnight with the laptop shut. Off by default and deliberately hard to turn on by accident: it is
376
+ * the only switch here that COPIES a credential rather than borrowing the browser holding it, and some
377
+ * sites answer a session arriving from a new fingerprint by invalidating it. */
378
+ cookies: webextScope.default("off"),
379
+ /* When the person is asked, in the browser, before an action goes through. "sensitive" is the default and
380
+ * the interesting one: a submit on a page that carries a password field, a payment form or a delete
381
+ * confirmation waits for a human click; ordinary navigation and typing do not. "always" makes every action
382
+ * a prompt (correct for a first week, exhausting after it); "never" is the owner saying they will watch
383
+ * instead, which they genuinely can, because they are looking at the tab. */
384
+ confirm: z.enum(["sensitive", "always", "never"]).default("sensitive"),
385
+ });
386
+ export type WebExtScopes = z.infer<typeof WebExtScopesSchema>;
387
+ // Like a host's, `platform` is an OPEN slug naming the card (chrome, firefox): the browser family decides the
388
+ // skill pack's wording and the install link, and teaching the agent a new one should not need a daemon release.
389
+ export const WebExtConfigSchema = WebExtScopesSchema.extend({ platform: z.string().min(1) });
390
+ export type WebExtConfig = z.infer<typeof WebExtConfigSchema>;
391
+ // An ACP (Agent Client Protocol) agent served as a chat provider: the daemon spawns `command` as a long-lived
392
+ // subprocess speaking JSON-RPC over stdio, and the capability id becomes the provider id in the chat picker
393
+ // (see AgentProviderSchema). `command` is split on whitespace, no shell quoting. `env` is a pasted KEY=VALUE
394
+ // block (one per line); credentials ride here, so the whole block is the secret field (echoed as hasSecret),
395
+ // the vpn-conf precedent. `loginCommand` is an interactive login the user completes in a visible terminal
396
+ // (device-code flows); the agent persists credentials in its own store inside the container. `name` is the
397
+ // picker's display label; absent = the id.
398
+ export const AcpAgentConfigSchema = z.object({
399
+ command: z.string().min(1),
400
+ name: z.string().min(1).optional(),
401
+ env: z.string().optional(),
402
+ loginCommand: z.string().min(1).optional(),
403
+ });
404
+ /* A MODEL API THE USER POINTED US AT, one shape for every server that serves models over HTTP, whether it runs
405
+ * beside this container or in another datacentre. There is deliberately NO local/remote axis: an Ollama on the
406
+ * docker host, a vLLM on the GPU box down the hall, a LiteLLM gateway and OpenRouter differ only in the URL, and
407
+ * inventing a distinction would mean two code paths, two cards and two sets of bugs for one concept.
408
+ *
409
+ * `protocol` is the only real fork, and it is about the WIRE, not about where the server lives:
410
+ * openai , the endpoint speaks OpenAI /v1/chat/completions (Ollama, vLLM, llama.cpp, LM Studio, TGI,
411
+ * OpenRouter, most gateways). The Claude Code harness speaks only the Anthropic Messages API, so
412
+ * these are re-served through the bundled translator, which is already in the image for exactly
413
+ * this job (agent/translator.ts). The user's key stays in the translator's config on /history and
414
+ * never reaches the harness, it gets the loopback bearer instead.
415
+ * anthropic, the endpoint already speaks the Anthropic Messages API (LiteLLM's /v1/messages, a Bedrock or
416
+ * Vertex router, a corporate Anthropic gateway). Nothing to translate: the harness is pointed
417
+ * straight at it with the user's own key.
418
+ *
419
+ * `headers` is a pasted `Name: value` block, one per line, the extra headers gateways ask for (a tenant id, a
420
+ * routing hint). The key is the secret field; the header block is not, because it is where non-credential
421
+ * routing metadata lives and hiding it would make a misrouted endpoint undiagnosable. */
422
+ export const EndpointProtocolSchema = z.enum(["openai", "anthropic"]);
423
+ export type EndpointProtocol = z.infer<typeof EndpointProtocolSchema>;
424
+ export const EndpointConfigSchema = z.object({
425
+ // The API root, INCLUDING the version segment the server publishes (…:11434/v1). Taken verbatim rather than
426
+ // normalised: "which suffix does this server want" is the one thing that actually varies between them, and
427
+ // guessing it is how a working URL becomes an unexplainable 404.
428
+ baseUrl: z.url(),
429
+ protocol: EndpointProtocolSchema.default("openai"),
430
+ apiKey: z.string().optional(),
431
+ headers: z.string().optional(),
432
+ });
433
+ /* A MODEL THE SANDBOX RUNS ITSELF, the managed counterpart of `endpoint`. An endpoint points at a server the
434
+ * USER operates; this one names weights, and the daemon does the operating: it downloads the file into the
435
+ * workspace cache, serves it with the image's bundled llama-server on a loopback port it owns, and registers
436
+ * the result exactly as if the user had added an endpoint at that port. Everything downstream (the picker, the
437
+ * translator, quick-model pinning) sees an `endpoint/<id>` provider and never learns the difference, which is
438
+ * why there is no baseUrl here: the URL is derived from the entry's id (the daemon's endpoints/local-model.ts),
439
+ * not a fact anyone typed.
440
+ *
441
+ * `model` is WHICH WEIGHTS, as a Hugging Face path (`owner/repo/file.gguf`, resolved to the repo's own
442
+ * download), so shipping a new recommended model is a catalog-card edit, not a daemon release. The reserved
443
+ * value "custom" defers to `url`, a direct GGUF link for people who know exactly what they want.
444
+ *
445
+ * `gpu` mirrors the docker card's option and rides the same allowlisted `--gpus=all` directive: the ASK lives
446
+ * here, what became of it is SANDBOX_GPU, stamped by the runner (see the docker handler's gpuState). "on"/"off"
447
+ * rather than a boolean for the manifest-wide reason DockerConfigSchema gives.
448
+ *
449
+ * `context`/`contextTokens` are HOW MUCH CONVERSATION the server holds, the `model`/`url` pair's shape for the
450
+ * same reason: a short list of rungs anyone can choose between, and one escape hatch for a person who knows the
451
+ * exact number they want. Resolved to a single token count in exactly one place (the daemon's
452
+ * endpoints/local-model.ts localModelWindow), because the flag llama-server is started with and the number the
453
+ * card promises must never be two opinions. */
454
+ export const LOCAL_MODEL_WINDOWS = ["16384", "32768", "65536", "131072"] as const;
455
+ export type LocalModelWindow = (typeof LOCAL_MODEL_WINDOWS)[number];
456
+ /* THE RUNG A CARD WITH NO OPINION LANDS ON, and the one number in this block that is a product decision rather
457
+ * than an arithmetic one.
458
+ *
459
+ * It is 65,536 because this sandbox runs a TOOL-CALLING AGENT LOOP, and that loop's own fixed cost, its
460
+ * instructions plus one JSON schema per tool it can call, times every capability the owner has connected, is
461
+ * tens of thousands of tokens before the user has typed anything (agent/context-budget.ts holds the measurement
462
+ * and the refusal built on it). A window that cannot hold that cost is not a smaller version of the product; it
463
+ * is a model whose every real turn is refused, which is what the previous flat 32,768 shipped: a 27B model,
464
+ * seventeen gigabytes downloaded, and a first message that died on `36216 tokens exceeds 32768`.
465
+ *
466
+ * So the default is the smallest rung a full turn fits in, and the smaller rungs stay on the list because they
467
+ * are honestly useful: pinned as the quick model (titles, commit messages) a window this size is waste, and the
468
+ * gigabyte it gives back is the difference between running one of these models on an eight-gigabyte laptop and
469
+ * not. What each rung costs in memory is the card's job to say (capability-catalog): roughly 2 GB of quantized
470
+ * cache per 32k of window, on top of the weights. */
471
+ export const LOCAL_MODEL_WINDOW_DEFAULT: LocalModelWindow = "65536";
472
+ /* THE BOUNDS ON THE TYPED NUMBER, and they are bounds against a TYPO rather than against a preference. Below
473
+ * the floor there is no conversation left to have once the loop's own instructions land; above the ceiling is a
474
+ * number no GGUF on offer was trained for, and llama-server would spend minutes reserving a cache for it before
475
+ * failing. Everything between is the owner's call: their machine, their memory. */
476
+ export const LOCAL_MODEL_WINDOW_MIN = 2048;
477
+ export const LOCAL_MODEL_WINDOW_MAX = 1_048_576;
478
+ export const LocalModelConfigSchema = z.object({
479
+ model: z.string().min(1),
480
+ gpu: z.enum(["on", "off"]).default("off"),
481
+ url: z.url().optional(),
482
+ context: z.union([z.enum(LOCAL_MODEL_WINDOWS), z.literal("custom")]).default(LOCAL_MODEL_WINDOW_DEFAULT),
483
+ // Coerced because it arrives from a text field as a string, the ssh card's `port` precedent, and only read
484
+ // when `context` is "custom" (the `url`/`model` relationship exactly).
485
+ contextTokens: z.coerce.number().int().min(LOCAL_MODEL_WINDOW_MIN).max(LOCAL_MODEL_WINDOW_MAX).optional(),
486
+ });
487
+ export type LocalModelConfig = z.infer<typeof LocalModelConfigSchema>;
488
+ /* THE SANDBOX WALLET, a USDC balance the agent can spend on x402-payable endpoints, under owner policy.
489
+ *
490
+ * WHAT IS DELIBERATELY NOT HERE IS A KEY. The signing key lives with the PLATFORM (one wallet per owner,
491
+ * reached with the connect token the agent's grant never covers), the container filesystem is explicitly not
492
+ * a boundary in this codebase's threat model (see the daemon's secret-vault.ts header), so the key does not
493
+ * enter the container at all. `address` is the wallet's PUBLIC address, written back by the handler's apply
494
+ * from the platform's answer, never typed by anyone: it is where the owner sends USDC, and everything the
495
+ * agent may know.
496
+ *
497
+ * POLICY IS THE OWNER'S DELEGATION, and its defaults are the conservative ones: every payment raises an
498
+ * approval card (`autoApproveUnderUsd: "0"`), bounded per payment and per UTC day. The daemon enforces it at
499
+ * the route AND the platform re-validates at the signer, the daemon's check is UX, the signer's is the
500
+ * guarantee, so a compromised container can at worst request what the owner already permitted. Amounts are
501
+ * DECIMAL STRINGS, never floats: the daemon does its arithmetic in the token's atomic units (USDC has six
502
+ * decimals), and a float here would be a rounding bug wearing a type.
503
+ *
504
+ * `allow`/`deny` are hostname lists (comma- or newline-separated). Empty allow = any host, each behind its
505
+ * card; deny wins over allow. One capability per sandbox (singleton card): a second balance would just be a
506
+ * second opinion about the same owner's wallet. */
507
+ const usdAmount = z.string().regex(/^\d+(\.\d{1,6})?$/, "a USD amount like 0.50 (up to six decimals: USDC's own precision)");
508
+ export const WalletNetworkSchema = z.enum(["eip155:8453", "eip155:84532"]);
509
+ export type WalletNetwork = z.infer<typeof WalletNetworkSchema>;
510
+ export const WalletConfigSchema = z.object({
511
+ // The chain payments settle on, CAIP-2. Base mainnet, or Base Sepolia for test mode (faucet USDC, the
512
+ // whole flow, cards, ledger, receipts, with zero real money).
513
+ network: WalletNetworkSchema.default("eip155:8453"),
514
+ // The wallet's public address, the platform's answer at apply time, never a form field.
515
+ address: z.string().optional(),
516
+ // Hard per-payment ceiling: over it the route refuses without raising a card.
517
+ perPaymentMaxUsd: usdAmount.default("1.00"),
518
+ // Payments at or under this settle without a card, inside the daily cap. "0" = every payment is carded.
519
+ autoApproveUnderUsd: usdAmount.default("0"),
520
+ // The UTC-day ceiling across all payments, carded or not.
521
+ dailyCapUsd: usdAmount.default("5.00"),
522
+ allow: z.string().optional(),
523
+ deny: z.string().optional(),
524
+ });
525
+ export type WalletConfig = z.infer<typeof WalletConfigSchema>;
526
+ export type McpConfig = z.infer<typeof McpConfigSchema>;
527
+ export type ServiceConfig = z.infer<typeof ServiceConfigSchema>;
528
+ export type IntegrationConfig = z.infer<typeof IntegrationConfigSchema>;
529
+ export type CliConfig = z.infer<typeof CliConfigSchema>;
530
+ export type PluginConfig = z.infer<typeof PluginConfigSchema>;
531
+ export type ExtensionConfig = z.infer<typeof ExtensionConfigSchema>;
532
+ export type SshConfig = z.infer<typeof SshConfigSchema>;
533
+ export type DockerConfig = z.infer<typeof DockerConfigSchema>;
534
+ export type BrowserConfig = z.infer<typeof BrowserConfigSchema>;
535
+ export type HostConfig = z.infer<typeof HostConfigSchema>;
536
+ export type AcpAgentConfig = z.infer<typeof AcpAgentConfigSchema>;
537
+ export type EndpointConfig = z.infer<typeof EndpointConfigSchema>;
538
+ export const CapabilitySchema = z.discriminatedUnion("kind", [
539
+ z.object({ id: entryId, kind: z.literal("devops"), config: z.object({}) }),
540
+ // A pnpm+turbo monorepo the user scaffolds as its own repo; the `id` is the repo name. No config, apps are
541
+ // added into it afterwards from its operator panel.
542
+ z.object({ id: entryId, kind: z.literal("monorepo"), config: z.object({}) }),
543
+ z.object({ id: entryId, kind: z.literal("mcp"), config: McpConfigSchema }),
544
+ z.object({ id: entryId, kind: z.literal("service"), config: ServiceConfigSchema }),
545
+ z.object({ id: entryId, kind: z.literal("integration"), config: IntegrationConfigSchema }),
546
+ z.object({ id: entryId, kind: z.literal("cli"), config: CliConfigSchema }),
547
+ z.object({ id: entryId, kind: z.literal("plugin"), config: PluginConfigSchema }),
548
+ z.object({ id: entryId, kind: z.literal("extension"), config: ExtensionConfigSchema }),
549
+ z.object({ id: entryId, kind: z.literal("ssh"), config: SshConfigSchema }),
550
+ // No IFNAMSIZ cap on the id: the tunnel's interface name is DERIVED (see the daemon's vpn/vpn-paths.ts
551
+ // interfaceName) rather than being the id itself, so a descriptive name is free.
552
+ z.object({ id: entryId, kind: z.literal("vpn"), config: VpnConfigSchema }),
553
+ // A geo exit (ExitConfigSchema). Same interface-name derivation as vpn, and deliberately NOT a vpn arm:
554
+ // it routes nothing into the main table, so the full-tunnel warning the vpn kind carries stays true.
555
+ z.object({ id: entryId, kind: z.literal("exit"), config: ExitConfigSchema }),
556
+ // The in-sandbox Docker Engine (baked into the base image, dormant by default). Its `--privileged` runtime
557
+ // directive is not in the config and never will be: dockerd does not work without it (see the handler's
558
+ // isPrivileged), so a switch there would offer a broken sandbox as a choice. What IS optional lives in
559
+ // DockerConfigSchema. No remove, the engine's state (/var/lib/docker) and whatever runs on it make a
560
+ // silent de-privilege more destructive than useful.
561
+ z.object({ id: entryId, kind: z.literal("docker"), config: DockerConfigSchema }),
562
+ z.object({ id: entryId, kind: z.literal("browser"), config: BrowserConfigSchema }),
563
+ // One email identity the sandbox acts as online, the browser-owning container accounts are born from
564
+ // (IdentityConfigSchema). Browser entries join it via their `identity` field.
565
+ z.object({ id: entryId, kind: z.literal("identity"), config: IdentityConfigSchema }),
566
+ z.object({ id: entryId, kind: z.literal("host"), config: HostConfigSchema }),
567
+ // The user's own BROWSER, through the extension installed in it (WebExtConfigSchema). The `host` kind's
568
+ // sibling: one capability per browser, the id namespacing its tools, the switches enforced at the far end.
569
+ // Distinct from `browser`, which is the sandbox's OWN Chromium and a profile this container owns — this
570
+ // one is the person's, already signed into everything, and the sandbox only ever borrows it.
571
+ z.object({ id: entryId, kind: z.literal("webext"), config: WebExtConfigSchema }),
572
+ z.object({ id: entryId, kind: z.literal("agent"), config: AcpAgentConfigSchema }),
573
+ // A model API (EndpointConfigSchema). The id becomes `endpoint/<id>` in the chat picker, the `agent` kind's
574
+ // precedent, with the prefix because these two are the only capability kinds that mint providers and they
575
+ // want opposite ability records (an ACP agent owns its own loop; an endpoint runs the full Claude Code one).
576
+ z.object({ id: entryId, kind: z.literal("endpoint"), config: EndpointConfigSchema }),
577
+ // A model the sandbox downloads and serves itself (LocalModelConfigSchema). Deliberately minting the SAME
578
+ // `endpoint/<id>` provider ids as the endpoint kind: to every consumer it IS an endpoint, one the daemon
579
+ // happens to operate, so a second provider namespace would be a second code path for the same turns.
580
+ z.object({ id: entryId, kind: z.literal("localmodel"), config: LocalModelConfigSchema }),
581
+ // The sandbox's USDC wallet (WalletConfigSchema), one per sandbox; the key never enters the container.
582
+ z.object({ id: entryId, kind: z.literal("wallet"), config: WalletConfigSchema }),
583
+ ]);
584
+ export type Capability = z.infer<typeof CapabilitySchema>;
585
+ /* `code` is a credential the owner has to TYPE SOMEWHERE ELSE to finish this connection. WhatsApp's
586
+ * link-a-device code, typed into the phone. It is not part of `detail` because the card does not merely print
587
+ * it: it sets it in a size you can read across a desk, next to a copy button, and replaces it in place when the
588
+ * provider mints a new one. A sentence with a code buried in it cannot be any of those things. */
589
+ export const CapabilityStatusSchema = z.object({
590
+ state: CapabilityStateSchema.describe("Whether it is live, still coming up, broken, or switched off."),
591
+ detail: z.string().optional().describe("What is wrong, in words a person can act on."),
592
+ code: z.string().optional().describe("A short marker for that reason, for anything deciding what to do about it."),
593
+ });
594
+ export type CapabilityStatus = z.infer<typeof CapabilityStatusSchema>;
595
+ /* The list row: manifest entry + live status. Secrets are never returned (an mcp token becomes hasToken).
596
+ *
597
+ * `secrets` NAMES them without carrying them, the config keys this connection is actually holding a credential
598
+ * under. It is what makes an edit form possible at all: `config` is everything the browser may see, so a form
599
+ * seeded from it alone cannot tell "this tunnel has a pre-shared key I'm not allowed to show you" from "this
600
+ * tunnel has no pre-shared key", and both render as an empty required box. Saving one then wipes the
601
+ * credential, which is why changing a routed network used to mean re-typing a key.
602
+ *
603
+ * Keys, never values, and never a boolean per known field: the set is derived from what the entry stores, so a
604
+ * field the user left blank is absent and a card that gained a credential since is present. The form reads it as
605
+ * "show dots, and let blank mean keep" (VAULTED, capability-secrets.ts). */
606
+ export const CapabilitySummarySchema = z.object({
607
+ id: z.string().describe("The connection's id."),
608
+ kind: CapabilityKindSchema.describe("What sort of thing it is."),
609
+ status: CapabilityStatusSchema.describe("Whether it is working."),
610
+ config: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).describe("Its settings, minus anything secret."),
611
+ // Defaulted for the daemon-older-than-browser seam, like `recommendations` below: a required field would
612
+ // fail the whole list parse against a sandbox predating this, taking the page down to hide some dots.
613
+ secrets: z
614
+ .array(z.string())
615
+ .default([])
616
+ .describe("Which credentials it holds, by name. The values are on one route only, and it is not this one."),
617
+ });
618
+ /* A capability the WORKSPACE asks for but the manifest doesn't carry, derived from what is checked out under
619
+ * /work, not from anything the user configured. It exists because the failures it prevents are illegible: a
620
+ * compose-backed dev database (`pnpm db:up`) dies on a missing /var/run/docker.sock, and nothing on that error
621
+ * points at the one-time privileged rebuild that fixes it; a workspace full of GitHub repos gets an agent that
622
+ * cannot read one issue until somebody thinks to go looking for the card.
623
+ *
624
+ * KEYED BY CATALOG CARD, NOT BY KIND, because github, gitlab, komodo and every other connector share the single
625
+ * `cli` kind, a kind cannot say which card to open, and matching on one badged all of them at once.
626
+ *
627
+ * WHAT IS STORED IS WHAT WAS SEEN. `evidence` is the artifact itself, a workspace-relative path, a git remote,
628
+ * rendered verbatim so the claim is checkable rather than believed, and `reason` is the same claim in the user's
629
+ * words with the evidence NOT repeated into it. A recommendation is re-derived on every read rather than
630
+ * remembered, so one whose evidence has since moved simply stops being made.
631
+ *
632
+ * `prefill` is the non-secret config the scan could read (a self-hosted instance url, a Komodo core), it fills
633
+ * the card's form so the user supplies only the credential. Secrets are NEVER in here, even when one is sitting
634
+ * in a checked-in file: the flow points at such a file as evidence, it does not absorb what is in it. */
635
+ export const CapabilityRecommendationSchema = z.object({
636
+ card: z.string().describe("Which connection is being suggested."),
637
+ evidence: z
638
+ .string()
639
+ .describe("What was seen that prompted it: a file, a remote, printed verbatim so the claim can be checked rather than believed."),
640
+ reason: z.string().describe("The same claim in words, without repeating the evidence into it."),
641
+ prefill: z
642
+ .record(z.string(), z.string())
643
+ .describe(
644
+ "Settings the scan could read, to fill the form so you supply only the credential. Never a secret, even when one is sitting in a checked-in file: the suggestion points at such a file, it does not absorb what is in it.",
645
+ ),
646
+ });
647
+ export type CapabilityRecommendation = z.infer<typeof CapabilityRecommendationSchema>;
648
+ export const CapabilitiesListSchema = z.object({
649
+ capabilities: z.array(CapabilitySummarySchema).describe("What this sandbox is connected to."),
650
+ // Defaulted for the daemon-older-than-browser seam: the platform's web app talks to whichever sandbox
651
+ // version the user has, and a required field here would fail the parse, taking the whole Capabilities page
652
+ // down on every sandbox predating this route, to hide a badge.
653
+ recommendations: z
654
+ .array(CapabilityRecommendationSchema)
655
+ .default([])
656
+ .describe(
657
+ "Things worth connecting, worked out from what is actually in the workspace rather than from anything you configured. Re-derived on every read, so one whose evidence has moved simply stops being suggested.",
658
+ ),
659
+ });
660
+ export const CapabilityIdParamSchema = z.object({ id: z.string().describe("Which connection.") });
661
+ /* One capability's config VERBATIM, secrets included, for the connection route (capabilities.connection).
662
+ * The one read on this surface that does not echo secrets as hasToken booleans, which is exactly why it is
663
+ * never served to a browser: its handler refuses any caller with a member identity, leaving only the daemon's
664
+ * header grants (an extension backend's minted token, which must declare the route in permissions.daemon).
665
+ * The values are the strings the capability stored; the caller knows its own kind's field names. */
666
+ export const CapabilityConnectionSchema = z.object({
667
+ id: z.string().describe("The connection's id."),
668
+ kind: z.string().describe("What sort of thing it is."),
669
+ config: z
670
+ .record(z.string(), z.string())
671
+ .describe("Its settings exactly as stored, credentials included. The field names are its own kind's, which the caller already knows."),
672
+ });
673
+ export type CapabilityConnection = z.infer<typeof CapabilityConnectionSchema>;
674
+ // DELETE /capabilities/recommendations/{card}: the user said this one is not wanted. The EVIDENCE it was
675
+ // declined against is recorded daemon-side rather than sent, so the client cannot dismiss a claim other than the
676
+ // one it was shown, and so the recommendation comes back by itself when the workspace changes under it.
677
+ export const CapabilityCardParamSchema = z.object({ card: z.string().describe("Which suggestion to stop making.") });
678
+ // POST /capabilities/{id}/secret body: replace just the capability's secret field (its key is per-kind, see the
679
+ // sandbox's secretField) and re-run its idempotent apply, the /secrets page's edit path.
680
+ export const CapabilitySecretInputSchema = z.object({
681
+ id: z.string().describe("Which connection."),
682
+ value: z.string().min(1).describe("The new credential. Its other settings are left alone."),
683
+ });
684
+ /* POST /capabilities/{id}/rename body: the name this connection should answer to from now on.
685
+ *
686
+ * A capability's id IS the agent's handle for it, its skill file, its tool prefix, its env suffix, the alias
687
+ * `ssh <name>` resolves, so renaming one is a migration and not a label edit. The shape of a name is therefore
688
+ * the same rule the add form enforces, spelled here because the daemon is the gate: letters and digits to start,
689
+ * then hyphens and underscores. Which KINDS may be renamed at all is the handler's own answer (capability.ts
690
+ * `rename`), not something a schema can say. */
691
+ export const CapabilityRenameSchema = z.object({
692
+ id: z.string(),
693
+ to: z
694
+ .string()
695
+ .min(1)
696
+ .max(60)
697
+ .regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/),
698
+ });
699
+ // POST /capabilities/{id}/login response: the interactive tmux session running the agent's loginCommand,
700
+ // which the web surfaces in the terminal panel for the user to complete the sign-in.
701
+ export const CapabilityLoginSchema = z.object({ session: z.string().describe("The terminal the sign-in is happening in. Attach to it to type.") });
702
+ // GET /capabilities/{id}/otp response: one freshly minted TOTP code off the capability's stored seed, what the
703
+ // in-sandbox `otp` command prints. The seed itself never crosses; secondsRemaining is the caller's cue to
704
+ // re-mint rather than submit a code about to die.
705
+ export const CapabilityOtpSchema = z.object({
706
+ code: z.string().describe("The code."),
707
+ secondsRemaining: z
708
+ .number()
709
+ .describe("How long it lasts. Its expiring is what makes handing one to an agent safe, since the seed behind it is never revealed."),
710
+ });
711
+ /* POST /capabilities/probe response: did these settings actually reach the thing, asked BEFORE they are saved.
712
+ *
713
+ * The answer is a sentence rather than a status code because the reader is standing in front of a form: what
714
+ * they need is either the service's own confirmation ("Reached GitHub, authenticated as ada") or the exact
715
+ * refusal ("GitHub answered 401: the token is not valid"), in the place where the box they would fix still is.
716
+ * That is also the whole point of doing it here: every one of these failures is otherwise discovered after the
717
+ * add, on a card that says "not connected" with nothing about which of six answers was wrong.
718
+ *
719
+ * `ok: false` is a REPORTED failure, not a transport error: the probe ran and the service said no. A card whose
720
+ * settings cannot be checked from here at all answers `checked: false`, which is a different thing from a
721
+ * failure and must never be drawn as one. */
722
+ export const CapabilityProbeSchema = z.object({
723
+ checked: z.boolean().describe("Whether this connection can be tested from here at all. False is not a failure: it is 'no test exists'."),
724
+ ok: z.boolean().describe("Whether the service answered as itself."),
725
+ message: z
726
+ .string()
727
+ .describe("What happened, in the words a person standing in front of the form needs: the service's own answer, or its refusal."),
728
+ });
729
+ export type CapabilityProbe = z.infer<typeof CapabilityProbeSchema>;