@intentic/sandbox-contract 1.176.2 → 1.209.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 (340) hide show
  1. package/README.md +65 -0
  2. package/dist/agent-catalog.d.ts +15 -2
  3. package/dist/agent-catalog.d.ts.map +1 -1
  4. package/dist/agent-catalog.js +50 -1
  5. package/dist/agent-catalog.js.map +1 -1
  6. package/dist/agent-run-model.d.ts +3 -0
  7. package/dist/agent-run-model.d.ts.map +1 -0
  8. package/dist/agent-run-model.js +10 -0
  9. package/dist/agent-run-model.js.map +1 -0
  10. package/dist/capability-env.d.ts +2 -0
  11. package/dist/capability-env.d.ts.map +1 -0
  12. package/dist/capability-env.js +2 -0
  13. package/dist/capability-env.js.map +1 -0
  14. package/dist/chores/chores.d.ts +9 -0
  15. package/dist/chores/chores.d.ts.map +1 -1
  16. package/dist/chores/chores.js +257 -18
  17. package/dist/chores/chores.js.map +1 -1
  18. package/dist/chores/digest.js +0 -0
  19. package/dist/chores/digest.js.map +1 -1
  20. package/dist/chores/extension-update.d.ts +9 -0
  21. package/dist/chores/extension-update.d.ts.map +1 -0
  22. package/dist/chores/extension-update.js +15 -0
  23. package/dist/chores/extension-update.js.map +1 -0
  24. package/dist/chores/fix-deps.d.ts +10 -0
  25. package/dist/chores/fix-deps.d.ts.map +1 -0
  26. package/dist/chores/fix-deps.js +18 -0
  27. package/dist/chores/fix-deps.js.map +1 -0
  28. package/dist/chores/index.d.ts +7 -2
  29. package/dist/chores/index.d.ts.map +1 -1
  30. package/dist/chores/index.js +4 -1
  31. package/dist/chores/index.js.map +1 -1
  32. package/dist/chores/probes.d.ts +1 -0
  33. package/dist/chores/probes.d.ts.map +1 -1
  34. package/dist/chores/probes.js +131 -3
  35. package/dist/chores/probes.js.map +1 -1
  36. package/dist/chores/stack.d.ts +27 -0
  37. package/dist/chores/stack.d.ts.map +1 -0
  38. package/dist/chores/stack.js +121 -0
  39. package/dist/chores/stack.js.map +1 -0
  40. package/dist/chores/verdict.d.ts +2 -1
  41. package/dist/chores/verdict.d.ts.map +1 -1
  42. package/dist/chores/verdict.js +57 -7
  43. package/dist/chores/verdict.js.map +1 -1
  44. package/dist/contract-lock.d.ts +3 -0
  45. package/dist/contract-lock.d.ts.map +1 -0
  46. package/dist/contract-lock.js +35 -0
  47. package/dist/contract-lock.js.map +1 -0
  48. package/dist/contracts/activity.contract.d.ts +10 -0
  49. package/dist/contracts/activity.contract.d.ts.map +1 -1
  50. package/dist/contracts/agent.contract.d.ts +230 -20
  51. package/dist/contracts/agent.contract.d.ts.map +1 -1
  52. package/dist/contracts/agent.contract.js +2 -1
  53. package/dist/contracts/agent.contract.js.map +1 -1
  54. package/dist/contracts/agents.contract.d.ts +847 -1
  55. package/dist/contracts/agents.contract.d.ts.map +1 -1
  56. package/dist/contracts/agents.contract.js +3 -1
  57. package/dist/contracts/agents.contract.js.map +1 -1
  58. package/dist/contracts/automations.contract.d.ts +89 -0
  59. package/dist/contracts/automations.contract.d.ts.map +1 -1
  60. package/dist/contracts/automations.contract.js +3 -1
  61. package/dist/contracts/automations.contract.js.map +1 -1
  62. package/dist/contracts/capabilities.contract.d.ts +108 -32
  63. package/dist/contracts/capabilities.contract.d.ts.map +1 -1
  64. package/dist/contracts/capabilities.contract.js +5 -1
  65. package/dist/contracts/capabilities.contract.js.map +1 -1
  66. package/dist/contracts/chores.contract.d.ts +43 -0
  67. package/dist/contracts/chores.contract.d.ts.map +1 -1
  68. package/dist/contracts/ci.contract.d.ts +5 -0
  69. package/dist/contracts/ci.contract.d.ts.map +1 -1
  70. package/dist/contracts/ci.contract.js +2 -2
  71. package/dist/contracts/ci.contract.js.map +1 -1
  72. package/dist/contracts/claude.contract.d.ts +3 -14
  73. package/dist/contracts/claude.contract.d.ts.map +1 -1
  74. package/dist/contracts/claude.contract.js +2 -3
  75. package/dist/contracts/claude.contract.js.map +1 -1
  76. package/dist/contracts/drafts.contract.d.ts +4 -0
  77. package/dist/contracts/drafts.contract.d.ts.map +1 -1
  78. package/dist/contracts/endpoints.contract.d.ts +28 -12
  79. package/dist/contracts/endpoints.contract.d.ts.map +1 -1
  80. package/dist/contracts/endpoints.contract.js +9 -0
  81. package/dist/contracts/endpoints.contract.js.map +1 -1
  82. package/dist/contracts/extensions.contract.d.ts +399 -69
  83. package/dist/contracts/extensions.contract.d.ts.map +1 -1
  84. package/dist/contracts/extensions.contract.js +15 -1
  85. package/dist/contracts/extensions.contract.js.map +1 -1
  86. package/dist/contracts/git.contract.d.ts +209 -11
  87. package/dist/contracts/git.contract.d.ts.map +1 -1
  88. package/dist/contracts/git.contract.js +12 -2
  89. package/dist/contracts/git.contract.js.map +1 -1
  90. package/dist/contracts/grok.contract.d.ts +0 -13
  91. package/dist/contracts/grok.contract.d.ts.map +1 -1
  92. package/dist/contracts/grok.contract.js +1 -2
  93. package/dist/contracts/grok.contract.js.map +1 -1
  94. package/dist/contracts/host.contract.d.ts +39 -0
  95. package/dist/contracts/host.contract.d.ts.map +1 -1
  96. package/dist/contracts/host.contract.js +3 -2
  97. package/dist/contracts/host.contract.js.map +1 -1
  98. package/dist/contracts/loops.contract.d.ts +343 -0
  99. package/dist/contracts/loops.contract.d.ts.map +1 -0
  100. package/dist/contracts/loops.contract.js +11 -0
  101. package/dist/contracts/loops.contract.js.map +1 -0
  102. package/dist/contracts/panels.contract.d.ts +6 -0
  103. package/dist/contracts/panels.contract.d.ts.map +1 -1
  104. package/dist/contracts/personas.contract.d.ts +112 -0
  105. package/dist/contracts/personas.contract.d.ts.map +1 -0
  106. package/dist/contracts/personas.contract.js +13 -0
  107. package/dist/contracts/personas.contract.js.map +1 -0
  108. package/dist/contracts/ports.contract.d.ts +1 -0
  109. package/dist/contracts/ports.contract.d.ts.map +1 -1
  110. package/dist/contracts/prepush.contract.d.ts +1 -0
  111. package/dist/contracts/prepush.contract.d.ts.map +1 -1
  112. package/dist/contracts/prepush.contract.js.map +1 -1
  113. package/dist/contracts/{codex.contract.d.ts → providers.contract.d.ts} +11 -3
  114. package/dist/contracts/providers.contract.d.ts.map +1 -0
  115. package/dist/contracts/providers.contract.js +6 -0
  116. package/dist/contracts/providers.contract.js.map +1 -0
  117. package/dist/contracts/public.contract.d.ts +24 -0
  118. package/dist/contracts/public.contract.d.ts.map +1 -0
  119. package/dist/contracts/public.contract.js +8 -0
  120. package/dist/contracts/public.contract.js.map +1 -0
  121. package/dist/contracts/push.contract.d.ts +3 -3
  122. package/dist/contracts/secrets.contract.d.ts +9 -0
  123. package/dist/contracts/secrets.contract.d.ts.map +1 -1
  124. package/dist/contracts/sessions.contract.d.ts +16 -1
  125. package/dist/contracts/sessions.contract.d.ts.map +1 -1
  126. package/dist/contracts/sessions.contract.js +1 -1
  127. package/dist/contracts/sessions.contract.js.map +1 -1
  128. package/dist/contracts/settings.contract.d.ts +352 -58
  129. package/dist/contracts/settings.contract.d.ts.map +1 -1
  130. package/dist/contracts/settings.contract.js +2 -1
  131. package/dist/contracts/settings.contract.js.map +1 -1
  132. package/dist/contracts/share.contract.d.ts +55 -0
  133. package/dist/contracts/share.contract.d.ts.map +1 -0
  134. package/dist/contracts/share.contract.js +9 -0
  135. package/dist/contracts/share.contract.js.map +1 -0
  136. package/dist/contracts/skills.contract.d.ts +41 -0
  137. package/dist/contracts/skills.contract.d.ts.map +1 -0
  138. package/dist/contracts/skills.contract.js +9 -0
  139. package/dist/contracts/skills.contract.js.map +1 -0
  140. package/dist/contracts/system.contract.d.ts +218 -22
  141. package/dist/contracts/system.contract.d.ts.map +1 -1
  142. package/dist/contracts/system.contract.js +10 -2
  143. package/dist/contracts/system.contract.js.map +1 -1
  144. package/dist/contracts/workflows.contract.d.ts +575 -0
  145. package/dist/contracts/workflows.contract.d.ts.map +1 -0
  146. package/dist/contracts/workflows.contract.js +13 -0
  147. package/dist/contracts/workflows.contract.js.map +1 -0
  148. package/dist/contracts/workspace.contract.d.ts +18 -4
  149. package/dist/contracts/workspace.contract.d.ts.map +1 -1
  150. package/dist/contracts/workspace.contract.js +6 -3
  151. package/dist/contracts/workspace.contract.js.map +1 -1
  152. package/dist/conversation-ids.d.ts +2 -0
  153. package/dist/conversation-ids.d.ts.map +1 -0
  154. package/dist/conversation-ids.js +113 -0
  155. package/dist/conversation-ids.js.map +1 -0
  156. package/dist/events.d.ts +523 -17
  157. package/dist/events.d.ts.map +1 -1
  158. package/dist/events.js +106 -13
  159. package/dist/events.js.map +1 -1
  160. package/dist/history-state.d.ts +3 -0
  161. package/dist/history-state.d.ts.map +1 -0
  162. package/dist/history-state.js +53 -0
  163. package/dist/history-state.js.map +1 -0
  164. package/dist/hostnames.d.ts +4 -0
  165. package/dist/hostnames.d.ts.map +1 -1
  166. package/dist/hostnames.js +7 -2
  167. package/dist/hostnames.js.map +1 -1
  168. package/dist/index.d.ts +3793 -478
  169. package/dist/index.d.ts.map +1 -1
  170. package/dist/index.js +41 -17
  171. package/dist/index.js.map +1 -1
  172. package/dist/listener-protocol.d.ts +77 -0
  173. package/dist/listener-protocol.d.ts.map +1 -0
  174. package/dist/listener-protocol.js +33 -0
  175. package/dist/listener-protocol.js.map +1 -0
  176. package/dist/model-order.d.ts +1 -0
  177. package/dist/model-order.d.ts.map +1 -1
  178. package/dist/model-order.js +16 -0
  179. package/dist/model-order.js.map +1 -1
  180. package/dist/output-fields.d.ts +30 -0
  181. package/dist/output-fields.d.ts.map +1 -0
  182. package/dist/output-fields.js +57 -0
  183. package/dist/output-fields.js.map +1 -0
  184. package/dist/path-refs.d.ts.map +1 -1
  185. package/dist/path-refs.js.map +1 -1
  186. package/dist/publish-drafts.d.ts +7 -0
  187. package/dist/publish-drafts.d.ts.map +1 -0
  188. package/dist/publish-drafts.js +20 -0
  189. package/dist/publish-drafts.js.map +1 -0
  190. package/dist/quick-model.d.ts +2 -1
  191. package/dist/quick-model.d.ts.map +1 -1
  192. package/dist/quick-model.js +18 -14
  193. package/dist/quick-model.js.map +1 -1
  194. package/dist/routes.d.ts +3 -0
  195. package/dist/routes.d.ts.map +1 -1
  196. package/dist/routes.js +63 -0
  197. package/dist/routes.js.map +1 -1
  198. package/dist/runtime-state.d.ts +33 -0
  199. package/dist/runtime-state.d.ts.map +1 -0
  200. package/dist/runtime-state.js +14 -0
  201. package/dist/runtime-state.js.map +1 -0
  202. package/dist/schemas.d.ts +4938 -866
  203. package/dist/schemas.d.ts.map +1 -1
  204. package/dist/schemas.js +1011 -192
  205. package/dist/schemas.js.map +1 -1
  206. package/dist/search-globs.d.ts +6 -0
  207. package/dist/search-globs.d.ts.map +1 -0
  208. package/dist/search-globs.js +39 -0
  209. package/dist/search-globs.js.map +1 -0
  210. package/dist/share-paths.d.ts +8 -0
  211. package/dist/share-paths.d.ts.map +1 -0
  212. package/dist/share-paths.js +21 -0
  213. package/dist/share-paths.js.map +1 -0
  214. package/dist/state-portability.d.ts +8 -0
  215. package/dist/state-portability.d.ts.map +1 -0
  216. package/dist/state-portability.js +4 -0
  217. package/dist/state-portability.js.map +1 -0
  218. package/dist/title.d.ts.map +1 -1
  219. package/dist/title.js +1 -1
  220. package/dist/title.js.map +1 -1
  221. package/dist/tunnel-ids.d.ts +1 -0
  222. package/dist/tunnel-ids.d.ts.map +1 -1
  223. package/dist/tunnel-ids.js +1 -0
  224. package/dist/tunnel-ids.js.map +1 -1
  225. package/dist/versions.d.ts +4 -0
  226. package/dist/versions.d.ts.map +1 -0
  227. package/dist/versions.js +15 -0
  228. package/dist/versions.js.map +1 -0
  229. package/dist/workflow-faults.d.ts +4 -0
  230. package/dist/workflow-faults.d.ts.map +1 -0
  231. package/dist/workflow-faults.js +115 -0
  232. package/dist/workflow-faults.js.map +1 -0
  233. package/dist/workspace-state.d.ts +282 -3
  234. package/dist/workspace-state.d.ts.map +1 -1
  235. package/dist/workspace-state.js +198 -12
  236. package/dist/workspace-state.js.map +1 -1
  237. package/package.json +30 -6
  238. package/src/agent-catalog.test.ts +147 -7
  239. package/src/agent-catalog.ts +233 -21
  240. package/src/agent-run-model.test.ts +60 -0
  241. package/src/agent-run-model.ts +51 -0
  242. package/src/capability-env.ts +10 -0
  243. package/src/capability-ledger.test.ts +110 -0
  244. package/src/chores/chores.test.ts +32 -0
  245. package/src/chores/chores.ts +456 -32
  246. package/src/chores/digest.ts +0 -0
  247. package/src/chores/extension-update.ts +37 -0
  248. package/src/chores/fix-deps.ts +40 -0
  249. package/src/chores/index.ts +7 -2
  250. package/src/chores/probes.test.ts +155 -3
  251. package/src/chores/probes.ts +209 -4
  252. package/src/chores/stack.test.ts +126 -0
  253. package/src/chores/stack.ts +244 -0
  254. package/src/chores/verdict.test.ts +357 -19
  255. package/src/chores/verdict.ts +102 -22
  256. package/src/contract-lock.test.ts +17 -0
  257. package/src/contract-lock.ts +66 -0
  258. package/src/contracts/agent.contract.ts +6 -0
  259. package/src/contracts/agents.contract.ts +12 -0
  260. package/src/contracts/automations.contract.ts +17 -1
  261. package/src/contracts/capabilities.contract.ts +22 -0
  262. package/src/contracts/ci.contract.ts +10 -2
  263. package/src/contracts/claude.contract.ts +8 -4
  264. package/src/contracts/endpoints.contract.ts +21 -0
  265. package/src/contracts/extensions.contract.ts +42 -0
  266. package/src/contracts/git.contract.ts +33 -6
  267. package/src/contracts/grok.contract.ts +4 -3
  268. package/src/contracts/host.contract.ts +13 -2
  269. package/src/contracts/loops.contract.ts +62 -0
  270. package/src/contracts/personas.contract.ts +56 -0
  271. package/src/contracts/prepush.contract.ts +4 -3
  272. package/src/contracts/providers.contract.ts +23 -0
  273. package/src/contracts/public.contract.ts +14 -0
  274. package/src/contracts/sessions.contract.ts +5 -1
  275. package/src/contracts/settings.contract.ts +5 -0
  276. package/src/contracts/share.contract.ts +20 -0
  277. package/src/contracts/skills.contract.ts +24 -0
  278. package/src/contracts/system.contract.ts +27 -3
  279. package/src/contracts/workflows.contract.ts +78 -0
  280. package/src/contracts/workspace.contract.ts +10 -2
  281. package/src/conversation-ids.test.ts +25 -0
  282. package/src/conversation-ids.ts +160 -0
  283. package/src/events.test.ts +20 -1
  284. package/src/events.ts +404 -43
  285. package/src/history-state.ts +129 -0
  286. package/src/hostnames.ts +20 -6
  287. package/src/index.ts +60 -17
  288. package/src/listener-protocol.ts +96 -0
  289. package/src/model-order.test.ts +32 -1
  290. package/src/model-order.ts +56 -3
  291. package/src/output-fields.ts +111 -0
  292. package/src/path-refs.test.ts +4 -4
  293. package/src/path-refs.ts +5 -5
  294. package/src/publish-drafts.ts +59 -0
  295. package/src/quick-model.test.ts +70 -27
  296. package/src/quick-model.ts +76 -40
  297. package/src/routes.test.ts +91 -3
  298. package/src/routes.ts +135 -0
  299. package/src/runtime-state.test.ts +45 -0
  300. package/src/runtime-state.ts +124 -0
  301. package/src/schemas.test.ts +16 -29
  302. package/src/schemas.ts +3526 -553
  303. package/src/search-globs.test.ts +46 -0
  304. package/src/search-globs.ts +76 -0
  305. package/src/share-paths.test.ts +48 -0
  306. package/src/share-paths.ts +68 -0
  307. package/src/state-portability.ts +56 -0
  308. package/src/title.test.ts +20 -1
  309. package/src/title.ts +12 -3
  310. package/src/tunnel-ids.ts +15 -1
  311. package/src/versions.test.ts +39 -0
  312. package/src/versions.ts +48 -0
  313. package/src/workflow-faults.test.ts +127 -0
  314. package/src/workflow-faults.ts +188 -0
  315. package/src/workspace-state.test.ts +189 -17
  316. package/src/workspace-state.ts +500 -30
  317. package/dist/contracts/codex.contract.d.ts.map +0 -1
  318. package/dist/contracts/codex.contract.js +0 -6
  319. package/dist/contracts/codex.contract.js.map +0 -1
  320. package/dist/contracts/gemini.contract.d.ts +0 -16
  321. package/dist/contracts/gemini.contract.d.ts.map +0 -1
  322. package/dist/contracts/gemini.contract.js +0 -6
  323. package/dist/contracts/gemini.contract.js.map +0 -1
  324. package/dist/contracts/kimi.contract.d.ts +0 -16
  325. package/dist/contracts/kimi.contract.d.ts.map +0 -1
  326. package/dist/contracts/kimi.contract.js +0 -6
  327. package/dist/contracts/kimi.contract.js.map +0 -1
  328. package/dist/contracts/komodo.contract.d.ts +0 -125
  329. package/dist/contracts/komodo.contract.d.ts.map +0 -1
  330. package/dist/contracts/komodo.contract.js +0 -14
  331. package/dist/contracts/komodo.contract.js.map +0 -1
  332. package/dist/contracts/memory.contract.d.ts +0 -34
  333. package/dist/contracts/memory.contract.d.ts.map +0 -1
  334. package/dist/contracts/memory.contract.js +0 -9
  335. package/dist/contracts/memory.contract.js.map +0 -1
  336. package/src/contracts/codex.contract.ts +0 -10
  337. package/src/contracts/gemini.contract.ts +0 -11
  338. package/src/contracts/kimi.contract.ts +0 -9
  339. package/src/contracts/komodo.contract.ts +0 -36
  340. package/src/contracts/memory.contract.ts +0 -13
@@ -1,80 +1,266 @@
1
- export const WORKSPACE_STATE_FILES = [
2
- { path: ".intentic/capabilities.json", invalidates: ["capabilities", "environment", "panels"] },
3
- { path: ".intentic/environment.", invalidates: ["environment"] },
4
- { path: ".intentic/settings.json", invalidates: ["settings"] },
5
- { path: ".intentic/drafts/", invalidates: ["drafts"] },
1
+ import { STATE_DIR } from "@intentic/constants";
2
+ const STATE_FILES = [
3
+ { path: ".intentic/capabilities.json", invalidates: ["capabilities", "environment", "panels", "manifests"], portability: "secret" },
4
+ { path: ".intentic/capability-dismissals.json", invalidates: ["capabilities"], portability: "carry", versioned: true },
5
+ { path: ".intentic/secret-uses.json", invalidates: ["secrets"], portability: "carry" },
6
+ { path: ".intentic/personas.json", invalidates: ["personas", "capabilities", "manifests"], portability: "carry", versioned: true },
7
+ { path: ".intentic/environment.custom.Dockerfile", invalidates: ["environment"], portability: "carry", versioned: true },
8
+ { path: ".intentic/environment.Dockerfile", invalidates: ["environment"], portability: "carry", versioned: true },
9
+ { path: ".intentic/environment.d/", invalidates: ["environment"], portability: "carry", versioned: true },
10
+ {
11
+ path: ".intentic/environment.approved.Dockerfile",
12
+ invalidates: ["environment"],
13
+ portability: "derived",
14
+ note: "The target composes its own overlay on first boot; rebuild it there to install the tools it names.",
15
+ },
16
+ { path: ".intentic/settings.json", invalidates: ["settings", "manifests"], portability: "carry", versioned: true },
17
+ {
18
+ path: ".intentic/rule-firings.json",
19
+ invalidates: ["rule-firings"],
20
+ portability: "derived",
21
+ note: "Stamps of when each rule last did something; the new sandbox starts its own record.",
22
+ },
23
+ { path: ".intentic/drafts/", invalidates: ["drafts"], portability: "carry", authored: true },
6
24
  {
7
25
  path: ".intentic/automations.json",
8
26
  invalidates: [],
9
27
  why: "Declared by the intentic.automations extension's contributes.files — `automations` is its query key, not core's.",
28
+ portability: "carry",
29
+ versioned: true,
30
+ },
31
+ {
32
+ path: ".intentic/automation-runs.json",
33
+ invalidates: [],
34
+ why: "Declared by the intentic.automations extension's contributes.files — `automations` is its query key, not core's.",
35
+ portability: "carry",
10
36
  },
11
37
  {
12
38
  path: ".intentic/approvals/",
13
39
  invalidates: [],
14
40
  why: "Declared by the intentic.automations extension's contributes.files — `automation-approvals` is its query key, not core's.",
41
+ portability: "carry",
42
+ },
43
+ {
44
+ path: ".intentic/automations.seeded.json",
45
+ invalidates: [],
46
+ why: "Which default automations this workspace has been offered (default-automations.ts); nothing renders it — it exists so deleting a seeded automation is final.",
47
+ portability: "carry",
48
+ versioned: true,
49
+ },
50
+ {
51
+ path: ".intentic/chores/",
52
+ invalidates: [],
53
+ why: "Declared by the intentic.maintenance extension's contributes.files — `maintenance-report`/`maintenance-runs` are its query keys, not core's.",
54
+ portability: "carry",
55
+ },
56
+ {
57
+ path: ".intentic/docs/",
58
+ invalidates: [],
59
+ why: "Declared by the intentic.documentation extension's contributes.files — `documentation`/`documentation-runs` are its query keys, not core's.",
60
+ portability: "carry",
61
+ authored: true,
62
+ outsideWriter: "the intentic.documentation extension's staging writes (its paths.ts)",
63
+ },
64
+ { path: ".intentic/workflows.json", invalidates: ["workflows"], portability: "carry", versioned: true },
65
+ { path: ".intentic/workflow-runs.json", invalidates: ["workflows", "workflow-runs"], portability: "carry" },
66
+ { path: ".intentic/loop-designs.json", invalidates: ["loop-designs"], portability: "carry", versioned: true },
67
+ {
68
+ path: ".intentic/loops.json",
69
+ invalidates: [],
70
+ why: "Ralph loops and their iteration history. Nothing observes it: where a RUNNING loop stands rides on the fleet roster (AgentSummary.loop), which the /events stream already pushes about once a second, and a second source invalidating on this file could only ever disagree with the card beside it. The iteration list of an ENDED loop is an on-demand read — nothing renders it until someone opens it (web's useLoops, which holds no query for exactly this reason).",
71
+ portability: "carry",
15
72
  },
16
73
  {
17
74
  path: ".intentic/webchat-installs.json",
18
75
  invalidates: [],
19
76
  why: "Which origins have loaded a Doorbell's widget, written on a 30s flush timer while a customer's site serves page views. The install panel that renders it fetches on open and polls itself while it is on screen, which is the whole window in which the answer changes for anyone. Pushing instead would bill every connected browser a refetch per flush, for a panel almost nobody has open.",
77
+ portability: "carry",
20
78
  },
21
79
  {
22
80
  path: ".intentic/thread-sessions.json",
23
81
  invalidates: [],
24
82
  why: "Thread bookkeeping (an inbound thread — a Doorbell visitor, a Discord or Slack channel — → sandbox conversation + provider session), written on EVERY inbound message. Nothing in the browser reads it: what a thread produces is a conversation, and the fleet board already learns about that from the agent registry's own push. Naming a key here would bill every connected browser a refetch per inbound message — the request storm this table's own note warns about — to refresh nothing it can see.",
83
+ portability: "carry",
25
84
  },
26
85
  {
27
86
  path: ".intentic/extension-settings.json",
28
87
  invalidates: [],
29
88
  why: "Held in a module-level shallowRef store per extension (web's extensionSettingsStore) with no query observer, and deliberately so: api.settings.get must answer SYNCHRONOUSLY from an extension's first activate() line, and the store outlives every component scope. A module-level QueryObserver is the one shape that would make invalidation refetch, and this app already ruled it out — it detaches on the queryClient.clear() at logout (see useSandbox's sandbox-list mirror). So a remote member's setting edit reaches this browser on its next load, not live.",
89
+ portability: "secret",
90
+ note: "Re-enter each extension's settings on the Extensions tab.",
30
91
  },
31
92
  {
32
93
  path: ".intentic/extension-enablement.json",
33
94
  invalidates: ["extensions"],
95
+ portability: "carry",
96
+ versioned: true,
97
+ },
98
+ { path: ".intentic/workspace-extensions/", invalidates: ["extensions"], portability: "carry", authored: true },
99
+ { path: ".intentic/extension-updates.json", invalidates: ["extensions"], portability: "carry" },
100
+ { path: ".intentic/extension-update-policy.json", invalidates: ["extensions"], portability: "carry", versioned: true },
101
+ {
102
+ path: ".intentic/extension-usage.json",
103
+ invalidates: [],
104
+ why: "Which of the routes each extension DECLARED it has actually called — the evidence behind the permissions list on its row. The one entry here whose empty set is a RATE decision rather than an architectural one: every browser with the app open reports its batch on a timer, so wiring this to the `extensions` query would refetch the whole list every few seconds for a figure nobody is watching change. The tab reads it when it loads, which is when anyone is reading it.",
105
+ portability: "carry",
34
106
  },
35
107
  {
36
108
  path: ".intentic/members.json",
37
109
  invalidates: [],
38
110
  why: "Not this view's source at all: SandboxAccess renders the PLATFORM's invite records (apiClient.invite.list), and this file is the daemon's ENFORCED copy — written first so a grant the enforcer never got is never recorded, then never read back. A change here means the two disagreed, which the write order makes fail-closed rather than stale.",
111
+ portability: "identity",
112
+ note: "Re-invite collaborators from the Access tab — a grant is the platform's record, and the target enforces its own copy.",
113
+ },
114
+ {
115
+ path: ".intentic/auth/",
116
+ invalidates: [],
117
+ why: "AI-provider credentials and runtime homes; each account is rendered through owner-gated provider routes.",
118
+ portability: "secret",
119
+ note: "Sign the agent's AI accounts in again on the Agent tab.",
39
120
  },
40
121
  {
41
- path: ".intentic/claude/",
122
+ path: ".intentic/sessions/claude/",
42
123
  invalidates: [],
43
124
  why: "Agent session transcripts — see the note above on why the memory notes under it stay polled.",
125
+ portability: "carry",
126
+ },
127
+ {
128
+ path: ".intentic/artifacts/",
129
+ invalidates: [],
130
+ why: "Durable outputs owned by conversations and extension runs: attachments, browser captures, generated images, acceptance reports, workflow step reports, voice transcripts, and loop ledgers.",
131
+ portability: "carry",
132
+ },
133
+ {
134
+ path: ".intentic/cache/",
135
+ invalidates: [],
136
+ why: "Rebuildable indexes and caches — the iq index and its vector sidecar, the whisper model; ignored by the watcher and recreated from carried workspace content.",
137
+ portability: "derived",
138
+ },
139
+ {
140
+ path: ".intentic/runtime/",
141
+ invalidates: [],
142
+ why: "Extension runtime scratch (watermarks, cached short-lived tokens); nothing renders it and gateways re-derive it.",
143
+ portability: "derived",
144
+ outsideWriter: "extensions, through extensionRuntimeDir below",
145
+ },
146
+ {
147
+ path: ".intentic/tmp/",
148
+ invalidates: [],
149
+ why: "Scratch that agents and tools leave behind (build logs, demo checkouts); nothing reads it after the turn that wrote it. The state janitor empties it at boot.",
150
+ portability: "derived",
151
+ },
152
+ {
153
+ path: ".intentic/.pnpm-store/",
154
+ invalidates: [],
155
+ why: "pnpm's content-addressable store, auto-created by installs run from under .intentic; the next install rebuilds it.",
156
+ portability: "derived",
157
+ outsideWriter: "pnpm itself, when an install runs from under .intentic",
158
+ },
159
+ {
160
+ path: ".intentic/newest-run.json",
161
+ invalidates: [],
162
+ why: "The newest daemon version that ever ran this workspace (store/newest-run.ts) — a downgrade tripwire, about THIS sandbox the way rule-firings is.",
163
+ portability: "derived",
164
+ note: "The target stamps its own daemon version on first boot.",
165
+ },
166
+ {
167
+ path: ".intentic/verify.json",
168
+ invalidates: [],
169
+ why: "The dependency verifier's verdict memory; nothing renders it directly — outcomes reach the owner as activity entries and workspace events.",
170
+ portability: "carry",
171
+ },
172
+ {
173
+ path: ".intentic/verify/",
174
+ invalidates: [],
175
+ why: "A running check's wrapper artifacts (log + exit status), read once by the daemon when the panel finishes.",
176
+ portability: "derived",
44
177
  },
45
178
  {
46
179
  path: ".intentic/ci.json",
47
180
  invalidates: [],
48
181
  why: "Webhook secret + conclusion memory; the Pipelines view reads it through /ci/runs, not off disk.",
182
+ portability: "secret",
183
+ note: "Re-add the CI webhook on the Pipelines view — its secret is per-sandbox.",
49
184
  },
50
185
  {
51
- path: ".intentic/komodo.json",
186
+ path: ".intentic/control-tokens.json",
52
187
  invalidates: [],
53
- why: "Per-connection 'when the owner last looked at Deployments'; the view reads it through /komodo/{capability}/overview, not off disk — and it is written BY that view being opened, so invalidating on it would refetch the board in answer to the browser's own click.",
188
+ why: "Hashed control tokens (the ACP editor bridge, and anything else driving this sandbox from outside), listed on demand by the owner.",
189
+ portability: "identity",
190
+ note: "Mint fresh control tokens — the old ones authenticate against the source sandbox.",
54
191
  },
55
- { path: ".intentic/bridge-tokens.json", invalidates: [], why: "Hashed ACP bridge tokens, listed on demand by the owner." },
56
192
  {
57
193
  path: ".intentic/owner.json",
58
194
  invalidates: [],
59
195
  why: "Bound once on first use; a change here means the sandbox was re-owned, which re-authenticates anyway.",
196
+ portability: "identity",
197
+ },
198
+ {
199
+ path: ".intentic/workspace.json",
200
+ invalidates: [],
201
+ why: "The workspace identity, read from the /events hello frame rather than as a file.",
202
+ portability: "identity",
203
+ },
204
+ {
205
+ path: ".intentic/templates.json",
206
+ invalidates: [],
207
+ why: "Scaffold templates, read when the scaffold dialog opens.",
208
+ portability: "carry",
209
+ versioned: true,
60
210
  },
61
- { path: ".intentic/workspace.json", invalidates: [], why: "The workspace identity, read from the /events hello frame rather than as a file." },
62
- { path: ".intentic/templates.json", invalidates: [], why: "Scaffold templates, read when the scaffold dialog opens." },
63
211
  {
64
212
  path: ".intentic/browser/",
65
213
  invalidates: [],
66
214
  why: "Browser-login profiles: Chromium rewrites these constantly. Descent-ignored by the watcher outright.",
215
+ portability: "derived",
216
+ note: "Log the agent's browser back into any site it needs — profiles do not travel.",
67
217
  },
68
218
  {
69
219
  path: ".intentic/extensions/",
70
220
  invalidates: [],
71
221
  why: "Extension checkouts — whole git clones. The `extensions` query is driven by the capability manifest above, not by their contents.",
222
+ portability: "derived",
223
+ note: "Extensions re-clone from the capability manifest on the target's next reconcile.",
72
224
  },
73
- { path: ".intentic/plugins/", invalidates: [], why: "Agent plugin dirs, read by the SDK's loader each turn." },
225
+ { path: ".intentic/plugins/", invalidates: [], why: "Agent plugin dirs, read by the SDK's loader each turn.", portability: "carry" },
226
+ { path: ".intentic/skills/", invalidates: ["skills"], portability: "carry", versioned: true },
227
+ { path: ".intentic/personas/", invalidates: ["personas"], portability: "carry", versioned: true },
74
228
  ];
229
+ export const WORKSPACE_STATE_FILES = STATE_FILES;
230
+ export const VERSIONED_STATE_PATHS = WORKSPACE_STATE_FILES.filter((file) => file.versioned).map((file) => file.path);
231
+ export const SEARCHABLE_STATE_PATHS = WORKSPACE_STATE_FILES.filter((file) => file.versioned || file.authored).map((file) => file.path);
232
+ export const extensionRuntimeDir = (extension) => `${STATE_DIR}/runtime/extensions/${extension.replaceAll(/[^a-zA-Z0-9._-]/g, "_")}`;
233
+ export const REPORTED_MANIFEST_PATHS = WORKSPACE_STATE_FILES.filter((file) => file.invalidates.includes("manifests")).map((file) => file.path);
234
+ export const isReportedManifest = (relPath) => REPORTED_MANIFEST_PATHS.includes(relPath.replaceAll("\\", "/"));
235
+ export const RETIRED_WORKSPACE_STATE_DIRS = {
236
+ secret: ["claude", "codex", "kimi", "opencode", "cliproxy"],
237
+ derived: ["iq", "extensions-runtime", "whisper", "browser/output"],
238
+ artifacts: ["attachments", "acceptance", "loops", "workflow-runs", "transcripts"],
239
+ };
240
+ const LOCKED_STATE_ENTRIES = new Set([
241
+ "owner.json",
242
+ "members.json",
243
+ "capabilities.json",
244
+ "ci.json",
245
+ "claude.json",
246
+ "auth",
247
+ "sessions",
248
+ "browser",
249
+ ...RETIRED_WORKSPACE_STATE_DIRS.secret,
250
+ ]);
251
+ export const isLockedWorkspacePath = (relPath) => {
252
+ const segments = relPath.split(/[\\/]/).filter((segment) => segment !== "" && segment !== ".");
253
+ if (segments[0] === ".git") {
254
+ return true;
255
+ }
256
+ return segments.length >= 2 && segments[0] === STATE_DIR && LOCKED_STATE_ENTRIES.has(segments[1] ?? "");
257
+ };
75
258
  export const staleQueryKeys = (paths, contributed) => [
76
259
  ...new Set([...WORKSPACE_STATE_FILES, ...contributed]
77
260
  .filter((file) => file.invalidates.length > 0 && paths.some((path) => path.startsWith(file.path)))
78
261
  .flatMap((file) => file.invalidates)),
79
262
  ];
263
+ export const fileBoundQueryKeys = (contributed) => [
264
+ ...new Set([...WORKSPACE_STATE_FILES, ...contributed].flatMap((file) => file.invalidates)),
265
+ ];
80
266
  //# sourceMappingURL=workspace-state.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-state.js","sourceRoot":"","sources":["../src/workspace-state.ts"],"names":[],"mappings":"AAkDA,MAAM,CAAC,MAAM,qBAAqB,GAAkC;IAEhE,EAAE,IAAI,EAAE,6BAA6B,EAAE,WAAW,EAAE,CAAC,cAAc,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE;IAC/F,EAAE,IAAI,EAAE,wBAAwB,EAAE,WAAW,EAAE,CAAC,aAAa,CAAC,EAAE;IAChE,EAAE,IAAI,EAAE,yBAAyB,EAAE,WAAW,EAAE,CAAC,UAAU,CAAC,EAAE;IAG9D,EAAE,IAAI,EAAE,mBAAmB,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,EAAE;IAKtD;QACI,IAAI,EAAE,4BAA4B;QAClC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,kHAAkH;KAC1H;IACD;QACI,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,2HAA2H;KACnI;IAQD;QACI,IAAI,EAAE,iCAAiC;QACvC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,gYAAgY;KACxY;IACD;QACI,IAAI,EAAE,gCAAgC;QACtC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,+eAA+e;KACvf;IACD;QACI,IAAI,EAAE,mCAAmC;QACzC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,2iBAA2iB;KACnjB;IAKD;QACI,IAAI,EAAE,qCAAqC;QAC3C,WAAW,EAAE,CAAC,YAAY,CAAC;KAC9B;IACD;QACI,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,sVAAsV;KAC9V;IAYD;QACI,IAAI,EAAE,mBAAmB;QACzB,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,8FAA8F;KACtG;IACD;QACI,IAAI,EAAE,mBAAmB;QACzB,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,iGAAiG;KACzG;IACD;QACI,IAAI,EAAE,uBAAuB;QAC7B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,sQAAsQ;KAC9Q;IACD,EAAE,IAAI,EAAE,8BAA8B,EAAE,WAAW,EAAE,EAAE,EAAE,GAAG,EAAE,0DAA0D,EAAE;IAC1H;QACI,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,uGAAuG;KAC/G;IACD,EAAE,IAAI,EAAE,0BAA0B,EAAE,WAAW,EAAE,EAAE,EAAE,GAAG,EAAE,kFAAkF,EAAE;IAC9I,EAAE,IAAI,EAAE,0BAA0B,EAAE,WAAW,EAAE,EAAE,EAAE,GAAG,EAAE,0DAA0D,EAAE;IACtH;QACI,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,sGAAsG;KAC9G;IACD;QACI,IAAI,EAAE,uBAAuB;QAC7B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,mIAAmI;KAC3I;IACD,EAAE,IAAI,EAAE,oBAAoB,EAAE,WAAW,EAAE,EAAE,EAAE,GAAG,EAAE,wDAAwD,EAAE;CACjH,CAAC;AAcF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAwB,EAAE,WAAwC,EAAqB,EAAE,CAAC;IACrH,GAAG,IAAI,GAAG,CACN,CAAC,GAAG,qBAAqB,EAAE,GAAG,WAAW,CAAC;SACrC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACjG,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAC3C;CACJ,CAAC"}
1
+ {"version":3,"file":"workspace-state.js","sourceRoot":"","sources":["../src/workspace-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AA6FhD,MAAM,WAAW,GAAG;IAQhB,EAAE,IAAI,EAAE,6BAA6B,EAAE,WAAW,EAAE,CAAC,cAAc,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE;IAOnI,EAAE,IAAI,EAAE,sCAAsC,EAAE,WAAW,EAAE,CAAC,cAAc,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IAOtH,EAAE,IAAI,EAAE,4BAA4B,EAAE,WAAW,EAAE,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE;IActF,EAAE,IAAI,EAAE,yBAAyB,EAAE,WAAW,EAAE,CAAC,UAAU,EAAE,cAAc,EAAE,WAAW,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IAYlI,EAAE,IAAI,EAAE,yCAAyC,EAAE,WAAW,EAAE,CAAC,aAAa,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IACxH,EAAE,IAAI,EAAE,kCAAkC,EAAE,WAAW,EAAE,CAAC,aAAa,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IACjH,EAAE,IAAI,EAAE,0BAA0B,EAAE,WAAW,EAAE,CAAC,aAAa,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IACzG;QACI,IAAI,EAAE,2CAA2C;QACjD,WAAW,EAAE,CAAC,aAAa,CAAC;QAC5B,WAAW,EAAE,SAAS;QACtB,IAAI,EAAE,oGAAoG;KAC7G;IAED,EAAE,IAAI,EAAE,yBAAyB,EAAE,WAAW,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IAIlH;QACI,IAAI,EAAE,6BAA6B;QACnC,WAAW,EAAE,CAAC,cAAc,CAAC;QAC7B,WAAW,EAAE,SAAS;QACtB,IAAI,EAAE,qFAAqF;KAC9F;IAID,EAAE,IAAI,EAAE,mBAAmB,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE;IAK5F;QACI,IAAI,EAAE,4BAA4B;QAClC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,kHAAkH;QACvH,WAAW,EAAE,OAAO;QACpB,SAAS,EAAE,IAAI;KAClB;IAcD;QACI,IAAI,EAAE,gCAAgC;QACtC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,kHAAkH;QACvH,WAAW,EAAE,OAAO;KACvB;IACD;QACI,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,2HAA2H;QAChI,WAAW,EAAE,OAAO;KACvB;IACD;QACI,IAAI,EAAE,mCAAmC;QACzC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,8JAA8J;QACnK,WAAW,EAAE,OAAO;QACpB,SAAS,EAAE,IAAI;KAClB;IAMD;QACI,IAAI,EAAE,mBAAmB;QACzB,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,8IAA8I;QACnJ,WAAW,EAAE,OAAO;KACvB;IAKD;QACI,IAAI,EAAE,iBAAiB;QACvB,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,6IAA6I;QAClJ,WAAW,EAAE,OAAO;QACpB,QAAQ,EAAE,IAAI;QACd,aAAa,EAAE,sEAAsE;KACxF;IAQD,EAAE,IAAI,EAAE,0BAA0B,EAAE,WAAW,EAAE,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IACvG,EAAE,IAAI,EAAE,8BAA8B,EAAE,WAAW,EAAE,CAAC,WAAW,EAAE,eAAe,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE;IAO3G,EAAE,IAAI,EAAE,6BAA6B,EAAE,WAAW,EAAE,CAAC,cAAc,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IAC7G;QACI,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,4cAA4c;QACjd,WAAW,EAAE,OAAO;KACvB;IAQD;QACI,IAAI,EAAE,iCAAiC;QACvC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,gYAAgY;QACrY,WAAW,EAAE,OAAO;KACvB;IACD;QACI,IAAI,EAAE,gCAAgC;QACtC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,+eAA+e;QACpf,WAAW,EAAE,OAAO;KACvB;IAKD;QACI,IAAI,EAAE,mCAAmC;QACzC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,2iBAA2iB;QAChjB,WAAW,EAAE,QAAQ;QACrB,IAAI,EAAE,2DAA2D;KACpE;IAKD;QACI,IAAI,EAAE,qCAAqC;QAC3C,WAAW,EAAE,CAAC,YAAY,CAAC;QAC3B,WAAW,EAAE,OAAO;QACpB,SAAS,EAAE,IAAI;KAClB;IAOD,EAAE,IAAI,EAAE,iCAAiC,EAAE,WAAW,EAAE,CAAC,YAAY,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE;IAI9G,EAAE,IAAI,EAAE,kCAAkC,EAAE,WAAW,EAAE,CAAC,YAAY,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE;IAG/F,EAAE,IAAI,EAAE,wCAAwC,EAAE,WAAW,EAAE,CAAC,YAAY,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IAGtH;QACI,IAAI,EAAE,gCAAgC;QACtC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,qdAAqd;QAC1d,WAAW,EAAE,OAAO;KACvB;IACD;QACI,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,sVAAsV;QAC3V,WAAW,EAAE,UAAU;QACvB,IAAI,EAAE,uHAAuH;KAChI;IAOD;QACI,IAAI,EAAE,iBAAiB;QACvB,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,0GAA0G;QAC/G,WAAW,EAAE,QAAQ;QACrB,IAAI,EAAE,yDAAyD;KAClE;IAWD;QACI,IAAI,EAAE,4BAA4B;QAClC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,8FAA8F;QACnG,WAAW,EAAE,OAAO;KACvB;IACD;QACI,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,6LAA6L;QAClM,WAAW,EAAE,OAAO;KACvB;IACD;QACI,IAAI,EAAE,kBAAkB;QACxB,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,+JAA+J;QACpK,WAAW,EAAE,SAAS;KACzB;IAMD;QACI,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,kHAAkH;QACvH,WAAW,EAAE,SAAS;QACtB,aAAa,EAAE,+CAA+C;KACjE;IACD;QACI,IAAI,EAAE,gBAAgB;QACtB,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,+JAA+J;QACpK,WAAW,EAAE,SAAS;KACzB;IAMD;QACI,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,oHAAoH;QACzH,WAAW,EAAE,SAAS;QACtB,aAAa,EAAE,wDAAwD;KAC1E;IACD;QACI,IAAI,EAAE,2BAA2B;QACjC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,kJAAkJ;QACvJ,WAAW,EAAE,SAAS;QACtB,IAAI,EAAE,yDAAyD;KAClE;IACD;QACI,IAAI,EAAE,uBAAuB;QAC7B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,4IAA4I;QACjJ,WAAW,EAAE,OAAO;KACvB;IACD;QACI,IAAI,EAAE,mBAAmB;QACzB,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,2GAA2G;QAChH,WAAW,EAAE,SAAS;KACzB;IACD;QACI,IAAI,EAAE,mBAAmB;QACzB,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,iGAAiG;QACtG,WAAW,EAAE,QAAQ;QACrB,IAAI,EAAE,0EAA0E;KACnF;IACD;QACI,IAAI,EAAE,+BAA+B;QACrC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,oIAAoI;QACzI,WAAW,EAAE,UAAU;QACvB,IAAI,EAAE,mFAAmF;KAC5F;IACD;QACI,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,uGAAuG;QAC5G,WAAW,EAAE,UAAU;KAC1B;IACD;QACI,IAAI,EAAE,0BAA0B;QAChC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,kFAAkF;QACvF,WAAW,EAAE,UAAU;KAC1B;IACD;QACI,IAAI,EAAE,0BAA0B;QAChC,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,0DAA0D;QAC/D,WAAW,EAAE,OAAO;QACpB,SAAS,EAAE,IAAI;KAClB;IAKD;QACI,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,sGAAsG;QAC3G,WAAW,EAAE,SAAS;QACtB,IAAI,EAAE,+EAA+E;KACxF;IACD;QACI,IAAI,EAAE,uBAAuB;QAC7B,WAAW,EAAE,EAAE;QACf,GAAG,EAAE,mIAAmI;QACxI,WAAW,EAAE,SAAS;QACtB,IAAI,EAAE,kFAAkF;KAC3F;IACD,EAAE,IAAI,EAAE,oBAAoB,EAAE,WAAW,EAAE,EAAE,EAAE,GAAG,EAAE,wDAAwD,EAAE,WAAW,EAAE,OAAO,EAAE;IASpI,EAAE,IAAI,EAAE,mBAAmB,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IAc7F,EAAE,IAAI,EAAE,qBAAqB,EAAE,WAAW,EAAE,CAAC,UAAU,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;CACnD,CAAC;AAEnD,MAAM,CAAC,MAAM,qBAAqB,GAAkC,WAAW,CAAC;AAShF,MAAM,CAAC,MAAM,qBAAqB,GAAsB,qBAAqB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAWxI,MAAM,CAAC,MAAM,sBAAsB,GAAsB,qBAAqB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAChI,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CACtB,CAAC;AAUF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,SAAiB,EAAU,EAAE,CAAC,GAAG,SAAS,uBAAuB,SAAS,CAAC,UAAU,CAAC,kBAAkB,EAAE,GAAG,CAAC,EAAE,CAAC;AAmBrJ,MAAM,CAAC,MAAM,uBAAuB,GAAsB,qBAAqB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CACxI,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CACtB,CAAC;AAIF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,OAAe,EAAW,EAAE,CAAC,uBAAuB,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;AAShI,MAAM,CAAC,MAAM,4BAA4B,GAAG;IACxC,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC;IAC3D,OAAO,EAAE,CAAC,IAAI,EAAE,oBAAoB,EAAE,SAAS,EAAE,gBAAgB,CAAC;IAClE,SAAS,EAAE,CAAC,aAAa,EAAE,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,aAAa,CAAC;CAC3E,CAAC;AAcX,MAAM,oBAAoB,GAAwB,IAAI,GAAG,CAAC;IACtD,YAAY;IACZ,cAAc;IACd,mBAAmB;IACnB,SAAS;IACT,aAAa;IACb,MAAM;IACN,UAAU;IACV,SAAS;IACT,GAAG,4BAA4B,CAAC,MAAM;CACzC,CAAC,CAAC;AAWH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,OAAe,EAAW,EAAE;IAC9D,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,GAAG,CAAC,CAAC;IAC/F,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,oBAAoB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC5G,CAAC,CAAC;AA2BF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAwB,EAAE,WAAwC,EAAqB,EAAE,CAAC;IACrH,GAAG,IAAI,GAAG,CACN,CAAC,GAAG,qBAAqB,EAAE,GAAG,WAAW,CAAC;SACrC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACjG,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAC3C;CACJ,CAAC;AAOF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,WAAwC,EAAqB,EAAE,CAAC;IAC/F,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,qBAAqB,EAAE,GAAG,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;CAC7F,CAAC"}
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@intentic/sandbox-contract",
3
- "version": "1.176.2",
3
+ "version": "1.209.0",
4
4
  "description": "oRPC wire contract for the intentic sandbox daemon — shared by the daemon and its browser client",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://gitlab.com/radarsu/intentic.git",
10
- "directory": "_libs/sandbox-contract"
9
+ "url": "git+https://github.com/intentic/intentic.git",
10
+ "directory": "_sandbox/sandbox-contract"
11
11
  },
12
12
  "files": [
13
13
  "dist",
@@ -51,6 +51,28 @@
51
51
  "default": "./dist/tunnel-ids.js"
52
52
  }
53
53
  },
54
+ "./share-paths": {
55
+ "types": "./dist/share-paths.d.ts",
56
+ "import": {
57
+ "@intentic/src": "./src/share-paths.ts",
58
+ "default": "./dist/share-paths.js"
59
+ },
60
+ "require": {
61
+ "@intentic/src": "./src/share-paths.ts",
62
+ "default": "./dist/share-paths.js"
63
+ }
64
+ },
65
+ "./capability-env": {
66
+ "types": "./dist/capability-env.d.ts",
67
+ "import": {
68
+ "@intentic/src": "./src/capability-env.ts",
69
+ "default": "./dist/capability-env.js"
70
+ },
71
+ "require": {
72
+ "@intentic/src": "./src/capability-env.ts",
73
+ "default": "./dist/capability-env.js"
74
+ }
75
+ },
54
76
  "./session-names": {
55
77
  "types": "./dist/session-names.d.ts",
56
78
  "import": {
@@ -67,8 +89,9 @@
67
89
  "@orpc/contract": "1.14.13",
68
90
  "tslib": "2.8.1",
69
91
  "zod": "4.4.3",
70
- "@intentic/extension-api": "1.176.2",
71
- "@intentic/registry": "0.0.0"
92
+ "@intentic/constants": "1.209.0",
93
+ "@intentic/extension-manifest": "1.209.0",
94
+ "@intentic/registry": "1.209.0"
72
95
  },
73
96
  "devDependencies": {
74
97
  "@types/node": "24.13.2",
@@ -81,6 +104,7 @@
81
104
  "typecheck": "tsgo --noEmit -p tsconfig.test.json",
82
105
  "watch": "tsgo --build --watch",
83
106
  "test": "vitest run",
84
- "test:watch": "vitest"
107
+ "test:watch": "vitest",
108
+ "lock": "tsgo -b && node scripts/write-lock.mjs"
85
109
  }
86
110
  }
@@ -7,6 +7,8 @@ import {
7
7
  effortAllowed,
8
8
  endpointIdOf,
9
9
  endpointProvider,
10
+ fastAllowed,
11
+ FREE_PROVIDERS,
10
12
  HARNESSES,
11
13
  isEndpointProvider,
12
14
  limitationsOf,
@@ -37,10 +39,16 @@ describe("every provider/harness pair declares what it can do", () => {
37
39
 
38
40
  // Nothing may be left to inference: a `permissions` a surface can't read, or a runtime nobody serves,
39
41
  // is the drift this record exists to end.
40
- expect(["claude-code", "codex", "opencode", "acp"]).toContain(capabilities.runtime);
42
+ expect(["claude-code", "codex", "opencode", "opencode-gemini", "acp", "pi"]).toContain(capabilities.runtime);
41
43
  expect(["modes", "plan"]).toContain(capabilities.permissions);
42
44
  expect(["full", "http", "none"]).toContain(capabilities.mcp);
45
+ // Every runtime executes SOMETHING — a record listing no backend would hide the shell every loop has.
46
+ expect(capabilities.execution).toContain("shell");
47
+ for (const backend of capabilities.execution) {
48
+ expect(["shell", "js"]).toContain(backend);
49
+ }
43
50
  expect(["namespace", "cwd"]).toContain(capabilities.isolation);
51
+ expect(["replace", "append", "none"]).toContain(capabilities.instructions);
44
52
  // The permission modes offered must include the mode a clamp falls back to — a floor that isn't in the
45
53
  // list would leave the composer showing a posture the runtime can't hold.
46
54
  expect(modesFor(capabilities)).toContain(clampMode("default", capabilities));
@@ -55,16 +63,60 @@ test("an unknown provider id is an ACP agent, on either harness", () => {
55
63
  }
56
64
  });
57
65
 
58
- /* The harness axis is real for exactly two providers. Claude is always its own Claude Code loop, and kimi/gemini
59
- * have no native runtime at all (Moonshot speaks the Anthropic protocol directly; Google is re-served through
60
- * the translator), so all three run it whatever the client sent which is why "is the harness claude-code" was
61
- * never the question worth asking. */
66
+ /* THE `pi` ID IS RESERVED for the Pi coding agent's own RPC runtime an `agent`-kind capability like any ACP
67
+ * agent, but served over Pi's JSONL protocol, which carries abilities the ACP floor cannot: real mid-turn
68
+ * steering, the thinking-level scale, a published command list. Falling to the ACP record instead would strip
69
+ * exactly the abilities that justify a fifth runtime. */
70
+ describe("the pi provider", () => {
71
+ it("runs the pi runtime on either harness — pi is its own loop", () => {
72
+ for (const harness of HARNESSES) {
73
+ expect(capabilitiesOf("pi", harness.value).runtime).toBe("pi");
74
+ }
75
+ });
76
+
77
+ it("sits above the ACP floor: steering, effort and commands are real; terminals and MCP are not", () => {
78
+ const pi = capabilitiesOf("pi", "native");
79
+ expect(pi.steering).toBe(true);
80
+ expect(pi.effort).toBe(true);
81
+ expect(pi.commands).toBe(true);
82
+ const limitations = limitationsOf(pi);
83
+ expect(limitations).toContain("no MCP tools or plugins");
84
+ expect(limitations).toContain("no terminal panel");
85
+ expect(limitations).not.toContain("no mid-turn steering");
86
+ expect(limitations).not.toContain("no effort control");
87
+ expect(limitations).not.toContain("no slash commands");
88
+ });
89
+
90
+ it("is an installed capability, so it carries no access requirement to connect", () => {
91
+ expect(accessFor("pi")).toBeUndefined();
92
+ });
93
+ });
94
+
95
+ /* The harness axis is real for exactly two providers, and the three exceptions are each a different shape of
96
+ * "there is nothing to choose". Claude is always its own Claude Code loop. Kimi has no native runtime at all
97
+ * (Moonshot speaks the Anthropic protocol directly), so it runs Claude Code whatever the client sent. Gemini is
98
+ * Kimi's mirror: it has no CLAUDE CODE road, because that loop announces itself in every request and Google's
99
+ * Antigravity channel refuses on the announcement — every account, every time, reported as a spent quota it
100
+ * never was. */
62
101
  test("only codex and grok change runtime with the harness", () => {
63
102
  const switched = PROVIDERS.filter((provider) => capabilitiesOf(provider.value, "native") !== capabilitiesOf(provider.value, "claude-code"));
64
103
 
65
104
  expect(switched.map((provider) => provider.value)).toEqual(["codex", "grok"]);
66
105
  });
67
106
 
107
+ /* THE RULE THAT KEEPS CLAUDE CODE TRAFFIC AWAY FROM GOOGLE, asserted where it is decided rather than at each of
108
+ * the surfaces that obey it. Everything downstream — the adapter that serves a turn, the transcript store, the
109
+ * quick helper's choice of loop — reads the runtime off this record, so a Gemini turn asking for Claude Code and
110
+ * getting it back would put the refused loop on the road again everywhere at once. */
111
+ test("gemini answers with its own runtime whatever harness is asked for", () => {
112
+ const native = capabilitiesOf("gemini", "native");
113
+
114
+ expect(native.runtime).toBe("opencode-gemini");
115
+ expect(capabilitiesOf("gemini", "claude-code")).toEqual(native);
116
+ // Same abilities as the Grok loop it shares — only the runtime id, which keys adapter health, differs.
117
+ expect({ ...native, runtime: "opencode" }).toEqual(capabilitiesOf("grok", "native"));
118
+ });
119
+
68
120
  test("codex and grok under the Claude Code harness get the full ceiling — it is the same loop", () => {
69
121
  for (const provider of ["codex", "grok"] as const) {
70
122
  expect(capabilitiesOf(provider, "claude-code")).toEqual(capabilitiesOf("claude", "native"));
@@ -112,15 +164,46 @@ test("every axis a record can lack has words for it", () => {
112
164
  permissions: "plan",
113
165
  questions: false,
114
166
  mcp: "none",
167
+ execution: ["shell"],
115
168
  effort: false,
169
+ fastMode: false,
116
170
  isolation: "cwd",
117
171
  commands: false,
118
172
  terminals: false,
119
173
  recovery: false,
174
+ instructions: "none",
120
175
  };
121
176
 
122
- // Nine axes, nine sentences: an axis added to the interface without one would silently never be disclosed.
123
- expect(limitationsOf(nothing)).toHaveLength(9);
177
+ // Eleven DISCLOSABLE axes, eleven sentences: an axis added to the interface without one would silently
178
+ // never be disclosed. fastMode is the deliberate twelfth — a record alone can't tell the truth about it (a
179
+ // translator-routed turn reads true here and still can't go fast), so it is answered by fastAllowed
180
+ // instead. Anything else added to the interface has to move this number.
181
+ expect(limitationsOf(nothing)).toHaveLength(11);
182
+ expect(limitationsOf(nothing).join(" ")).not.toContain("fast");
183
+ });
184
+
185
+ /* The instruction axis has THREE values and only two of them are worth a sentence, which is the one shape the
186
+ * count above cannot check: a middle value that discloses the same words as the floor would tell a Grok user
187
+ * their prompt is ignored when it is in fact being sent. */
188
+ test("the instruction axis discloses its two weaker answers, differently", () => {
189
+ const grok = limitationsOf(capabilitiesOf("grok", "native")).join(" ");
190
+ const acp = limitationsOf(capabilitiesOf("some-installed-agent", "native")).join(" ");
191
+
192
+ expect(grok).toContain("added to theirs");
193
+ expect(grok).not.toContain("isn't applied");
194
+ expect(acp).toContain("isn't applied");
195
+ // Codex on its own runtime replaces, like the Claude Code loop — so it has nothing to disclose here.
196
+ expect(limitationsOf(capabilitiesOf("codex", "native")).join(" ")).not.toContain("system prompt");
197
+ });
198
+
199
+ /* The JS backend is hosted by the one loop the daemon can put its own execution seam through. Pinned as a test
200
+ * rather than left to the records because turn planning gates the backend on this axis — a runtime gaining it
201
+ * here without a seam behind it would advertise code runs that can never execute. */
202
+ test("only the Claude Code loop hosts the js execution backend", () => {
203
+ for (const { provider, harness } of pairs) {
204
+ const capabilities = capabilitiesOf(provider, harness);
205
+ expect(capabilities.execution.includes("js")).toBe(capabilities.runtime === "claude-code");
206
+ }
124
207
  });
125
208
 
126
209
  // The mode vocabulary is the contract's own PermissionMode, so a mode added to the wire can't be quietly absent
@@ -182,3 +265,60 @@ describe("a configured model endpoint", () => {
182
265
  expect(accessFor("endpoint/gpu-box")).toBeUndefined();
183
266
  });
184
267
  });
268
+
269
+ /* FAST SPEED IS OFFERED ON THREE CONDITIONS AT ONCE, and the interesting cases are the ones where two of them
270
+ * hold. A translator-routed provider runs the Claude Code loop — same record, same ceiling — and still cannot go
271
+ * fast, because the harness refuses a non-Anthropic endpoint; a Claude model that publishes no `fast` badge
272
+ * cannot either. Both would be silent failures if the composer offered the control anyway: the turn runs, the
273
+ * answer arrives, and only the bill says it was standard speed. */
274
+ describe("offering fast speed", () => {
275
+ it("is offered for a Claude model that publishes the badge", () => {
276
+ expect(fastAllowed(capabilitiesOf("claude", "native"), "claude", ["reasoning", "fast"])).toBe(true);
277
+ });
278
+
279
+ it("is refused for a routed provider on the Claude Code loop, whose endpoint is not first-party", () => {
280
+ // Grok under the claude-code harness reads the FULL Claude Code record — the capability alone would say
281
+ // yes. It is served through the sandbox's translator, so the harness would report `not_first_party`.
282
+ expect(capabilitiesOf("grok", "claude-code").fastMode).toBe(true);
283
+ expect(fastAllowed(capabilitiesOf("grok", "claude-code"), "grok", ["fast"])).toBe(false);
284
+ // A user's own model endpoint is the same story for the same reason.
285
+ expect(fastAllowed(capabilitiesOf("endpoint/gpu-box", "native"), "endpoint/gpu-box", ["fast"])).toBe(false);
286
+ });
287
+
288
+ it("is refused for a Claude model whose catalog row doesn't publish it", () => {
289
+ expect(fastAllowed(capabilitiesOf("claude", "native"), "claude", ["reasoning"])).toBe(false);
290
+ // The seed floor and any provider that reports ids only — no capabilities published, nothing claimed.
291
+ expect(fastAllowed(capabilitiesOf("claude", "native"), "claude", undefined)).toBe(false);
292
+ });
293
+
294
+ it("is refused by every runtime that isn't the Claude Code loop", () => {
295
+ for (const provider of ["codex", "grok"] as const) {
296
+ expect(fastAllowed(capabilitiesOf(provider, "native"), provider, ["fast"])).toBe(false);
297
+ }
298
+ expect(fastAllowed(capabilitiesOf("some-installed-agent", "native"), "some-installed-agent", ["fast"])).toBe(false);
299
+ });
300
+ });
301
+
302
+ /* THE FREE ROW IS THE PRODUCT'S FRONT DOOR, so the list of free providers is derived from the access table and
303
+ * guarded here rather than typed out where it is used. Two things can break it and both are silent: the table
304
+ * losing its last `free` row (the connect gate then has no headline and quietly falls back to pitching a paid
305
+ * subscription to a user who has none), and a `free` row acquiring a requirement that costs money. */
306
+ describe("the free providers", () => {
307
+ it("is exactly the access table's free rows, and is never empty", () => {
308
+ expect(FREE_PROVIDERS.length).toBeGreaterThan(0);
309
+ for (const provider of PROVIDERS) {
310
+ const isFree = accessFor(provider.value)?.kind === "free";
311
+ expect(FREE_PROVIDERS.includes(provider.value)).toBe(isFree);
312
+ }
313
+ });
314
+
315
+ it("carries the words the connect gate puts on screen", () => {
316
+ // The gate names the provider and states what connecting it runs; a row with either missing would put an
317
+ // empty headline or a dangling sentence in front of the user who has connected nothing.
318
+ for (const provider of FREE_PROVIDERS) {
319
+ const access = accessFor(provider);
320
+ expect(access?.requirement).toBeTruthy();
321
+ expect(access?.runs).toBeTruthy();
322
+ }
323
+ });
324
+ });