@intentic/sandbox-contract 1.310.0 → 1.312.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 (304) hide show
  1. package/README.md +43 -159
  2. package/dist/chores/chores.js +6 -6
  3. package/dist/chores/chores.js.map +1 -1
  4. package/dist/chores/fix-deps.d.ts.map +1 -1
  5. package/dist/chores/fix-deps.js +3 -2
  6. package/dist/chores/fix-deps.js.map +1 -1
  7. package/dist/chores/prompt.d.ts.map +1 -1
  8. package/dist/chores/prompt.js +3 -2
  9. package/dist/chores/prompt.js.map +1 -1
  10. package/dist/contracts/accounts.contract.d.ts +15 -0
  11. package/dist/contracts/accounts.contract.d.ts.map +1 -1
  12. package/dist/contracts/agent.contract.d.ts +15 -0
  13. package/dist/contracts/agent.contract.d.ts.map +1 -1
  14. package/dist/contracts/agents.contract.d.ts +1228 -32
  15. package/dist/contracts/agents.contract.d.ts.map +1 -1
  16. package/dist/contracts/agents.contract.js +42 -3
  17. package/dist/contracts/agents.contract.js.map +1 -1
  18. package/dist/contracts/capabilities.contract.d.ts +2 -0
  19. package/dist/contracts/capabilities.contract.d.ts.map +1 -1
  20. package/dist/contracts/device.contract.d.ts +2 -0
  21. package/dist/contracts/device.contract.d.ts.map +1 -1
  22. package/dist/contracts/extensions.contract.d.ts +2 -0
  23. package/dist/contracts/extensions.contract.d.ts.map +1 -1
  24. package/dist/contracts/offload.contract.d.ts +107 -0
  25. package/dist/contracts/offload.contract.d.ts.map +1 -0
  26. package/dist/contracts/offload.contract.js +53 -0
  27. package/dist/contracts/offload.contract.js.map +1 -0
  28. package/dist/contracts/ports.contract.d.ts +5 -0
  29. package/dist/contracts/ports.contract.d.ts.map +1 -1
  30. package/dist/contracts/runner.contract.d.ts +111 -0
  31. package/dist/contracts/runner.contract.d.ts.map +1 -1
  32. package/dist/contracts/runner.contract.js +3 -1
  33. package/dist/contracts/runner.contract.js.map +1 -1
  34. package/dist/contracts/settings.contract.d.ts +16 -0
  35. package/dist/contracts/settings.contract.d.ts.map +1 -1
  36. package/dist/contracts/system.contract.d.ts +154 -0
  37. package/dist/contracts/system.contract.d.ts.map +1 -1
  38. package/dist/contracts/translator.contract.d.ts +16 -0
  39. package/dist/contracts/translator.contract.d.ts.map +1 -1
  40. package/dist/contracts/workspace.contract.d.ts +146 -0
  41. package/dist/contracts/workspace.contract.d.ts.map +1 -1
  42. package/dist/contracts/workspace.contract.js +40 -3
  43. package/dist/contracts/workspace.contract.js.map +1 -1
  44. package/dist/documents/conversions.d.ts +81 -0
  45. package/dist/documents/conversions.d.ts.map +1 -0
  46. package/dist/documents/conversions.js +213 -0
  47. package/dist/documents/conversions.js.map +1 -0
  48. package/dist/documents/index.d.ts +3 -0
  49. package/dist/documents/index.d.ts.map +1 -0
  50. package/dist/documents/index.js +3 -0
  51. package/dist/documents/index.js.map +1 -0
  52. package/dist/documents/passthrough.d.ts +13 -0
  53. package/dist/documents/passthrough.d.ts.map +1 -0
  54. package/dist/documents/passthrough.js +101 -0
  55. package/dist/documents/passthrough.js.map +1 -0
  56. package/dist/events/agent-events.d.ts +20 -0
  57. package/dist/events/agent-events.d.ts.map +1 -1
  58. package/dist/events/agent-events.js +6 -1
  59. package/dist/events/agent-events.js.map +1 -1
  60. package/dist/events/land-breakage.d.ts +3 -0
  61. package/dist/events/land-breakage.d.ts.map +1 -1
  62. package/dist/events/land-breakage.js +3 -0
  63. package/dist/events/land-breakage.js.map +1 -1
  64. package/dist/events/land-conflict.d.ts +3 -0
  65. package/dist/events/land-conflict.d.ts.map +1 -0
  66. package/dist/events/land-conflict.js +4 -0
  67. package/dist/events/land-conflict.js.map +1 -0
  68. package/dist/events/system-events.d.ts +114 -0
  69. package/dist/events/system-events.d.ts.map +1 -1
  70. package/dist/events/system-events.js +3 -1
  71. package/dist/events/system-events.js.map +1 -1
  72. package/dist/events/transcript.d.ts +5 -0
  73. package/dist/events/transcript.d.ts.map +1 -1
  74. package/dist/events/transcript.js +9 -1
  75. package/dist/events/transcript.js.map +1 -1
  76. package/dist/events/verify-nudge.d.ts.map +1 -1
  77. package/dist/events/verify-nudge.js.map +1 -1
  78. package/dist/front/generated/wire.d.ts +137 -0
  79. package/dist/front/generated/wire.d.ts.map +1 -0
  80. package/dist/front/generated/wire.js +2 -0
  81. package/dist/front/generated/wire.js.map +1 -0
  82. package/dist/front/terminal-frames.d.ts +17 -0
  83. package/dist/front/terminal-frames.d.ts.map +1 -0
  84. package/dist/front/terminal-frames.js +79 -0
  85. package/dist/front/terminal-frames.js.map +1 -0
  86. package/dist/ids/conversation-ids.d.ts +2 -0
  87. package/dist/ids/conversation-ids.d.ts.map +1 -1
  88. package/dist/ids/conversation-ids.js +2 -0
  89. package/dist/ids/conversation-ids.js.map +1 -1
  90. package/dist/index.d.ts +1773 -96
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +8 -1
  93. package/dist/index.js.map +1 -1
  94. package/dist/policy/command-run.d.ts +1 -1
  95. package/dist/policy/command-run.d.ts.map +1 -1
  96. package/dist/policy/command-run.js +7 -4
  97. package/dist/policy/command-run.js.map +1 -1
  98. package/dist/protocol/container-requirements.d.ts +0 -1
  99. package/dist/protocol/container-requirements.d.ts.map +1 -1
  100. package/dist/protocol/container-requirements.js +1 -2
  101. package/dist/protocol/container-requirements.js.map +1 -1
  102. package/dist/protocol/ingress-contract.d.ts +0 -11
  103. package/dist/protocol/ingress-contract.d.ts.map +1 -1
  104. package/dist/protocol/raw-routes.d.ts +36 -4
  105. package/dist/protocol/raw-routes.d.ts.map +1 -1
  106. package/dist/protocol/raw-routes.js +18 -13
  107. package/dist/protocol/raw-routes.js.map +1 -1
  108. package/dist/protocol/route-meta.d.ts +2 -0
  109. package/dist/protocol/route-meta.d.ts.map +1 -1
  110. package/dist/protocol/route-meta.js.map +1 -1
  111. package/dist/protocol/runner-protocol.d.ts +34 -0
  112. package/dist/protocol/runner-protocol.d.ts.map +1 -1
  113. package/dist/protocol/runner-protocol.js +33 -0
  114. package/dist/protocol/runner-protocol.js.map +1 -1
  115. package/dist/protocol/tunnel-lanes.d.ts +10 -0
  116. package/dist/protocol/tunnel-lanes.d.ts.map +1 -0
  117. package/dist/protocol/tunnel-lanes.js +16 -0
  118. package/dist/protocol/tunnel-lanes.js.map +1 -0
  119. package/dist/schemas/activity.d.ts.map +1 -1
  120. package/dist/schemas/activity.js.map +1 -1
  121. package/dist/schemas/agents.d.ts +145 -3
  122. package/dist/schemas/agents.d.ts.map +1 -1
  123. package/dist/schemas/agents.js +43 -9
  124. package/dist/schemas/agents.js.map +1 -1
  125. package/dist/schemas/automations.d.ts +43 -0
  126. package/dist/schemas/automations.d.ts.map +1 -1
  127. package/dist/schemas/capabilities.d.ts +3 -0
  128. package/dist/schemas/capabilities.d.ts.map +1 -1
  129. package/dist/schemas/capabilities.js +4 -0
  130. package/dist/schemas/capabilities.js.map +1 -1
  131. package/dist/schemas/devices.d.ts +34 -6
  132. package/dist/schemas/devices.d.ts.map +1 -1
  133. package/dist/schemas/devices.js +8 -6
  134. package/dist/schemas/devices.js.map +1 -1
  135. package/dist/schemas/extension-updates.d.ts +4 -0
  136. package/dist/schemas/extension-updates.d.ts.map +1 -1
  137. package/dist/schemas/git/git.d.ts +16 -0
  138. package/dist/schemas/git/git.d.ts.map +1 -1
  139. package/dist/schemas/git/git.js +1 -0
  140. package/dist/schemas/git/git.js.map +1 -1
  141. package/dist/schemas/hosts.d.ts +8 -0
  142. package/dist/schemas/hosts.d.ts.map +1 -1
  143. package/dist/schemas/hosts.js +3 -0
  144. package/dist/schemas/hosts.js.map +1 -1
  145. package/dist/schemas/keep-warm.d.ts +68 -0
  146. package/dist/schemas/keep-warm.d.ts.map +1 -0
  147. package/dist/schemas/keep-warm.js +74 -0
  148. package/dist/schemas/keep-warm.js.map +1 -0
  149. package/dist/schemas/metrics.d.ts +7 -0
  150. package/dist/schemas/metrics.d.ts.map +1 -1
  151. package/dist/schemas/metrics.js +1 -0
  152. package/dist/schemas/metrics.js.map +1 -1
  153. package/dist/schemas/offload.d.ts +62 -0
  154. package/dist/schemas/offload.d.ts.map +1 -0
  155. package/dist/schemas/offload.js +23 -0
  156. package/dist/schemas/offload.js.map +1 -0
  157. package/dist/schemas/personas.js +3 -3
  158. package/dist/schemas/personas.js.map +1 -1
  159. package/dist/schemas/ports.d.ts +10 -0
  160. package/dist/schemas/ports.d.ts.map +1 -1
  161. package/dist/schemas/ports.js +8 -0
  162. package/dist/schemas/ports.js.map +1 -1
  163. package/dist/schemas/providers/plan-limits.d.ts +29 -0
  164. package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
  165. package/dist/schemas/providers/plan-limits.js +7 -0
  166. package/dist/schemas/providers/plan-limits.js.map +1 -1
  167. package/dist/schemas/providers/provider-oauth.d.ts +15 -0
  168. package/dist/schemas/providers/provider-oauth.d.ts.map +1 -1
  169. package/dist/schemas/providers/provider-oauth.js +4 -0
  170. package/dist/schemas/providers/provider-oauth.js.map +1 -1
  171. package/dist/schemas/providers/usage.d.ts +6 -0
  172. package/dist/schemas/providers/usage.d.ts.map +1 -1
  173. package/dist/schemas/providers/usage.js +4 -0
  174. package/dist/schemas/providers/usage.js.map +1 -1
  175. package/dist/schemas/settings-history.d.ts +28 -0
  176. package/dist/schemas/settings-history.d.ts.map +1 -0
  177. package/dist/schemas/settings-history.js +39 -0
  178. package/dist/schemas/settings-history.js.map +1 -0
  179. package/dist/schemas/settings.d.ts +8 -0
  180. package/dist/schemas/settings.d.ts.map +1 -1
  181. package/dist/schemas/settings.js +33 -3
  182. package/dist/schemas/settings.js.map +1 -1
  183. package/dist/schemas/system.d.ts +24 -0
  184. package/dist/schemas/system.d.ts.map +1 -1
  185. package/dist/schemas/system.js +15 -0
  186. package/dist/schemas/system.js.map +1 -1
  187. package/dist/schemas/workspace/mainline.d.ts +371 -0
  188. package/dist/schemas/workspace/mainline.d.ts.map +1 -0
  189. package/dist/schemas/workspace/mainline.js +134 -0
  190. package/dist/schemas/workspace/mainline.js.map +1 -0
  191. package/dist/schemas/workspace/workspace-tree.d.ts +23 -0
  192. package/dist/schemas/workspace/workspace-tree.d.ts.map +1 -1
  193. package/dist/schemas/workspace/workspace-tree.js +34 -0
  194. package/dist/schemas/workspace/workspace-tree.js.map +1 -1
  195. package/dist/state/definition.d.ts +36 -4
  196. package/dist/state/definition.d.ts.map +1 -1
  197. package/dist/state/definition.js.map +1 -1
  198. package/dist/state/fix-stance.d.ts.map +1 -1
  199. package/dist/state/fix-stance.js +1 -0
  200. package/dist/state/fix-stance.js.map +1 -1
  201. package/dist/state/history-state.d.ts.map +1 -1
  202. package/dist/state/history-state.js +5 -0
  203. package/dist/state/history-state.js.map +1 -1
  204. package/dist/state/runtime-state.d.ts +6 -0
  205. package/dist/state/runtime-state.d.ts.map +1 -1
  206. package/dist/state/runtime-state.js +2 -0
  207. package/dist/state/runtime-state.js.map +1 -1
  208. package/dist/state/workspace-state.d.ts +22 -1
  209. package/dist/state/workspace-state.d.ts.map +1 -1
  210. package/dist/state/workspace-state.js +27 -1
  211. package/dist/state/workspace-state.js.map +1 -1
  212. package/dist/text/transcript-fold.d.ts +3 -0
  213. package/dist/text/transcript-fold.d.ts.map +1 -1
  214. package/dist/text/transcript-fold.js +30 -8
  215. package/dist/text/transcript-fold.js.map +1 -1
  216. package/dist/time/zone.d.ts +1 -0
  217. package/dist/time/zone.d.ts.map +1 -1
  218. package/dist/time/zone.js +5 -0
  219. package/dist/time/zone.js.map +1 -1
  220. package/package.json +34 -22
  221. package/src/chores/chores.ts +6 -6
  222. package/src/chores/fix-deps.ts +3 -2
  223. package/src/chores/prompt.ts +3 -2
  224. package/src/contracts/agents.contract.ts +50 -3
  225. package/src/contracts/offload.contract.ts +60 -0
  226. package/src/contracts/runner.contract.ts +7 -1
  227. package/src/contracts/workspace.contract.ts +54 -2
  228. package/src/documents/conversions.ts +402 -0
  229. package/src/documents/index.ts +5 -0
  230. package/src/documents/passthrough.ts +139 -0
  231. package/src/events/agent-events.ts +9 -1
  232. package/src/events/land-breakage.ts +12 -0
  233. package/src/events/land-conflict.ts +12 -0
  234. package/src/events/system-events.ts +9 -3
  235. package/src/events/transcript.ts +14 -3
  236. package/src/events/verify-nudge.ts +4 -3
  237. package/src/front/generated/wire.ts +104 -0
  238. package/src/front/terminal-frames.fixture.json +55 -0
  239. package/src/front/terminal-frames.test.ts +63 -0
  240. package/src/front/terminal-frames.ts +102 -0
  241. package/src/ids/conversation-ids.ts +7 -0
  242. package/src/ids/hostnames.fixture.json +20 -0
  243. package/src/ids/hostnames.test.ts +20 -0
  244. package/src/index.ts +8 -1
  245. package/src/policy/command-run.test.ts +35 -0
  246. package/src/policy/command-run.ts +10 -5
  247. package/src/protocol/container-requirements.test.ts +2 -3
  248. package/src/protocol/container-requirements.ts +1 -7
  249. package/src/protocol/ingress-contract.fixture.json +77 -0
  250. package/src/protocol/ingress-contract.test.ts +18 -26
  251. package/src/protocol/ingress-contract.ts +3 -22
  252. package/src/protocol/raw-routes.ts +21 -14
  253. package/src/protocol/route-meta.ts +4 -0
  254. package/src/protocol/runner-protocol.ts +53 -0
  255. package/src/protocol/tunnel-lanes.json +136 -0
  256. package/src/protocol/tunnel-lanes.test.ts +30 -0
  257. package/src/protocol/tunnel-lanes.ts +32 -0
  258. package/src/schemas/activity.ts +3 -1
  259. package/src/schemas/agents.ts +66 -10
  260. package/src/schemas/capabilities.ts +6 -0
  261. package/src/schemas/devices.ts +16 -9
  262. package/src/schemas/git/git.ts +5 -0
  263. package/src/schemas/hosts.ts +8 -0
  264. package/src/schemas/keep-warm.test.ts +68 -0
  265. package/src/schemas/keep-warm.ts +124 -0
  266. package/src/schemas/metrics.ts +2 -0
  267. package/src/schemas/offload.ts +44 -0
  268. package/src/schemas/personas.ts +3 -3
  269. package/src/schemas/ports.ts +10 -0
  270. package/src/schemas/providers/plan-limits.ts +13 -0
  271. package/src/schemas/providers/provider-oauth.ts +6 -0
  272. package/src/schemas/providers/usage.ts +7 -0
  273. package/src/schemas/settings-history.ts +61 -0
  274. package/src/schemas/settings.ts +70 -19
  275. package/src/schemas/system.ts +17 -0
  276. package/src/schemas/workspace/mainline.ts +201 -0
  277. package/src/schemas/workspace/workspace-tree.ts +47 -0
  278. package/src/state/definition.ts +3 -2
  279. package/src/state/fix-stance.test.ts +14 -0
  280. package/src/state/fix-stance.ts +2 -0
  281. package/src/state/history-state.ts +15 -0
  282. package/src/state/runtime-state.ts +8 -0
  283. package/src/state/workspace-state.ts +35 -1
  284. package/src/text/transcript-fold.test.ts +53 -1
  285. package/src/text/transcript-fold.ts +53 -10
  286. package/src/time/zone.ts +7 -0
  287. package/src/webext/index.ts +1 -1
  288. package/dist/protocol/ingress-protocol.d.ts +0 -30
  289. package/dist/protocol/ingress-protocol.d.ts.map +0 -1
  290. package/dist/protocol/ingress-protocol.js +0 -285
  291. package/dist/protocol/ingress-protocol.js.map +0 -1
  292. package/dist/protocol/terminal-protocol.d.ts +0 -18
  293. package/dist/protocol/terminal-protocol.d.ts.map +0 -1
  294. package/dist/protocol/terminal-protocol.js +0 -2
  295. package/dist/protocol/terminal-protocol.js.map +0 -1
  296. package/dist/protocol/tunnel-heartbeat.d.ts +0 -19
  297. package/dist/protocol/tunnel-heartbeat.d.ts.map +0 -1
  298. package/dist/protocol/tunnel-heartbeat.js +0 -41
  299. package/dist/protocol/tunnel-heartbeat.js.map +0 -1
  300. package/src/protocol/ingress-protocol.test.ts +0 -535
  301. package/src/protocol/ingress-protocol.ts +0 -448
  302. package/src/protocol/terminal-protocol.ts +0 -13
  303. package/src/protocol/tunnel-heartbeat.test.ts +0 -75
  304. package/src/protocol/tunnel-heartbeat.ts +0 -67
@@ -0,0 +1,77 @@
1
+ {
2
+ "publicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAfiMlxPB/8tnjh6TYS3rVde63OXB/wY77KWWtJ39XjBg=\n-----END PUBLIC KEY-----\n",
3
+ "grants": [
4
+ { "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.wdpBpqmMl21mqAZvRSYi4WfDejreTKDxHfOQp_sBKOTQB0_N2trL6iFcRxFUDrGBnGHmYnv-x66FgU_j57ZECA", "claim": {"sandboxId": "abcdef012345", "issuedAt": 1700000000} },
5
+ { "token": "ig1.eyJzdWIiOiIwMTIzNDU2Nzg5YWIiLCJpYXQiOjE3NTcwMDAwMDAsImV4dHJhIjoiaWdub3JlZCJ9.NK2BR2L_ycCACdmnbZgkg7sovw-Nhlt5vIL6ldArAx_jN9XOh-k1wisviROAgU40YGBgUASIgA6Wdn5SyYHHCA", "claim": {"sandboxId": "0123456789ab", "issuedAt": 1757000000} },
6
+ { "token": "ig1.eyJpYXQiOjE3MDAwMDAwMDAsInN1YiI6ImFiY2RlZjAxMjM0NSJ9.AenzYQn3Opfc-Sg_vkBrMEIOnCFZ3a46oSzjABAWyoVe3GP0vXlIM8MFgddHDQc7yow4kdfn1kHcUcsOoKkLCg", "claim": {"sandboxId": "abcdef012345", "issuedAt": 1700000000} },
7
+ { "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.wNpBpqmMl21mqAZvRSYi4WfDejreTKDxHfOQp_sBKOTQB0_N2trL6iFcRxFUDrGBnGHmYnv-x66FgU_j57ZECA", "claim": null },
8
+ { "token": "ig1.eiJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.wdpBpqmMl21mqAZvRSYi4WfDejreTKDxHfOQp_sBKOTQB0_N2trL6iFcRxFUDrGBnGHmYnv-x66FgU_j57ZECA", "claim": null },
9
+ { "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.Ac2ofn7IxD3M51pchXEycOPg4TP_kiTzu-aTVowDLTirjVzYrvfJ-ntrzPbQg-3RpBhmc53yP7s7iWyOgGWuAw", "claim": null },
10
+ { "token": "ig2.eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.wdpBpqmMl21mqAZvRSYi4WfDejreTKDxHfOQp_sBKOTQB0_N2trL6iFcRxFUDrGBnGHmYnv-x66FgU_j57ZECA", "claim": null },
11
+ { "token": "eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.wdpBpqmMl21mqAZvRSYi4WfDejreTKDxHfOQp_sBKOTQB0_N2trL6iFcRxFUDrGBnGHmYnv-x66FgU_j57ZECA", "claim": null },
12
+ { "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.wdpBpqmMl21mqAZvRSYi4WfDejreTKDxHfOQp_sBKOTQB0_N2trL6iFcRxFUDrGBnGHmYnv-x66FgU_j57ZECA.wdpBpqmMl21mqAZvRSYi4WfDejreTKDxHfOQp_sBKOTQB0_N2trL6iFcRxFUDrGBnGHmYnv-x66FgU_j57ZECA", "claim": null },
13
+ { "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.", "claim": null },
14
+ { "token": "", "claim": null },
15
+ { "token": "ig1", "claim": null },
16
+ { "token": "ig1..", "claim": null },
17
+ { "token": "ig1.%%%.%%%", "claim": null },
18
+ { "token": "ig1.eyJzdWIiOiJBQkNERUYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.CbPEciFki-GXJnUcS5F9_9uXUOw5tWhv3iS69BjuQpSmsA72_xnvYUIjhAMiBPI98XBQ9-zKc9GSPJIFGQqaDA", "claim": null },
19
+ { "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNCIsImlhdCI6MTcwMDAwMDAwMH0.qfJbxpVtbcymaweaCu9fUounkldkMy4HSdllEDlHs5Yq8b11XxiHLguDyvqubXcMGCuVVygCeg2MjU3i0vEuDA", "claim": null },
20
+ { "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOiIxNzAwMDAwMDAwIn0.BI6UT1aTaN01bGZyI9SHc_qIf9S-JlpmNtiBd8by_giZCq8pQIJCBKIutYZW0mmqCF0YHf0NHc9icq1h9Wr7BA", "claim": null },
21
+ { "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNDUifQ.Aa5FeLKazhsr2IvYZ2p5BJmLL1qvQWReXeCKt5gxCq9PqSaAnn8Pa6PJp0jiaXY5FIbc_S9MFrUCeEfQ6vnkBQ", "claim": null },
22
+ { "token": "ig1.eyJzdWIiOjEyMzQ1Njc4OTAxMiwiaWF0IjoxNzAwMDAwMDAwfQ.kb_lMTBvFS4nzTmqLf-T7l9IF8PHaPPkP5HB1T0yd0IRWqYJnIZH2eoKexv7e6aA2PkqCpe-B1FL8Nbu4qPvDg", "claim": null },
23
+ { "token": "ig1.bm90IGpzb24.Ffec27cYSbvKvjlxtSUIFFX7sGo3Zp4BxotW02rFwcJHfJikM51Zfzn3_0Rg8coR4y43iOUA2u-ZOcBJg4mmDA", "claim": null },
24
+ { "token": "ig1.bnVsbA.lFa3FNSkMcf3WCyjdb87YRVdt8h3aSzt4cignx9Rkwq6drwl12m0rlrmzdPkLjsdrQdL1oWZ2E2MGuo2CMeSBw", "claim": null },
25
+ { "token": "ig1.WyJhYmNkZWYwMTIzNDUiLDE3MDAwMDAwMDBd.BWFTD0bHKDBAEWwhYq6F-cWo_HXEC8UvDWkrTmafOycvTZUHvVOjWy_97OYl_pF-svCO1vbeumhF89ybhqv4Dw", "claim": null }
26
+ ],
27
+ "owners": [
28
+ { "host": "sandbox-abcdef012345.sbx.example.test", "owner": "abcdef012345" },
29
+ { "host": "sandbox-abcdef012345.sbx.example.test:443", "owner": "abcdef012345" },
30
+ { "host": "preview-web-app-abcdef012345.sbx.example.test:443", "owner": "abcdef012345" },
31
+ { "host": "port-0f0f0f0f0f0f-abcdef012345.sbx.example.dev", "owner": "abcdef012345" },
32
+ { "host": "public-1a2b3c4d5e6f-abcdef012345.sbx.example.dev", "owner": "abcdef012345" },
33
+ { "host": "SANDBOX-abcdef012345.sbx.example.test", "owner": "abcdef012345" },
34
+ { "host": "-abcdef012345.example.com", "owner": "abcdef012345" },
35
+ { "host": "sandbox-abcdef012345", "owner": "abcdef012345" },
36
+ { "host": "ingress.sbx.example.test", "owner": null },
37
+ { "host": "sbx.example.test", "owner": null },
38
+ { "host": "abcdef012345.local.sbx.example.dev", "owner": null },
39
+ { "host": "sandbox-ABCDEF012345.sbx.example.test", "owner": null },
40
+ { "host": "sandbox-abcdef01234.sbx.example.test", "owner": null },
41
+ { "host": "sandbox-abcdef012345x.sbx.example.test", "owner": null },
42
+ { "host": "sandbox-abcdef012345-.sbx.example.test", "owner": null },
43
+ { "host": "[::1]:8788", "owner": null },
44
+ { "host": "127.0.0.1:8788", "owner": null },
45
+ { "host": "", "owner": null }
46
+ ],
47
+ "lanes": [
48
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/workspace/media?path=a.mp4", "lane": "bulk" },
49
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "POST", "path": "/workspace/upload?path=a", "lane": "bulk" },
50
+ { "host": "sandbox-abcdef012345.sbx.example.test:443", "method": "GET", "path": "/system/runners/git/app/info/refs?service=git-upload-pack", "lane": "bulk" },
51
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "POST", "path": "/system/runners/git/app/git-receive-pack", "lane": "bulk" },
52
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/system/sync/ssh", "lane": "bulk" },
53
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/extensions/abc/bundle", "lane": "bulk" },
54
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/diff/raw?a=1?b=2", "lane": "bulk" },
55
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/bundles/download?ticket=x", "lane": "bulk" },
56
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "POST", "path": "/workspace/upload-archive", "lane": "bulk" },
57
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "HEAD", "path": "/workspace/raw?path=a", "lane": "bulk" },
58
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "OPTIONS", "path": "/workspace/raw", "lane": "bulk" },
59
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "OPTIONS", "path": "/system/runners/git/app/git-upload-pack", "lane": "bulk" },
60
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "get", "path": "/workspace/raw?path=a", "lane": "bulk" },
61
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/workspace/thumb?path=a.png", "lane": "interactive" },
62
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "POST", "path": "/workspace/upload-diff", "lane": "interactive" },
63
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/agents", "lane": "interactive" },
64
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/system/terminal?ticket=t", "lane": "interactive" },
65
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "POST", "path": "/workspace/raw", "lane": "interactive" },
66
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/workspace/raw/", "lane": "interactive" },
67
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/system/runners/git//info/refs", "lane": "interactive" },
68
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/x/anything/at/all", "lane": "interactive" },
69
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/", "lane": "interactive" },
70
+ { "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "", "lane": "interactive" },
71
+ { "host": "preview-web-abcdef012345.sbx.example.test", "method": "GET", "path": "/agents", "lane": "bulk" },
72
+ { "host": "port-1a2b3c4d5e6f-abcdef012345.sbx.example.test", "method": "GET", "path": "/", "lane": "bulk" },
73
+ { "host": "Sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/agents", "lane": "bulk" },
74
+ { "host": "sbx.example.test", "method": "GET", "path": "/agents", "lane": "bulk" },
75
+ { "host": "", "method": "GET", "path": "/agents", "lane": "bulk" }
76
+ ]
77
+ }
@@ -1,5 +1,14 @@
1
1
  import { generateKeyPairSync } from "node:crypto";
2
- import { hostOwnerId, mintReachabilityGrant, verifyReachabilityGrant } from "./ingress-contract.js";
2
+ import { readFileSync } from "node:fs";
3
+ import { hostOwnerId, type ReachabilityGrant, mintReachabilityGrant, verifyReachabilityGrant } from "./ingress-contract.js";
4
+
5
+ // The edge's rules as both implementations must answer them; the Rust edge reads the same file. Its grants were minted
6
+ // by a throwaway pair whose private half was never kept, so a new grant case takes a new pair and every token again.
7
+ const FIXTURE = JSON.parse(readFileSync(new URL("./ingress-contract.fixture.json", import.meta.url), "utf8")) as {
8
+ readonly publicKey: string;
9
+ readonly grants: readonly { readonly token: string; readonly claim: ReachabilityGrant | null }[];
10
+ readonly owners: readonly { readonly host: string; readonly owner: string | null }[];
11
+ };
3
12
 
4
13
  const pemPair = (): { privateKey: string; publicKey: string } => {
5
14
  const { privateKey, publicKey } = generateKeyPairSync("ed25519");
@@ -31,10 +40,11 @@ describe("reachability grant", () => {
31
40
  expect(verifyReachabilityGrant(keys.publicKey, `${prefix}.${forged}.${signature}`)).toBeUndefined();
32
41
  });
33
42
 
34
- it("answers undefined for garbage rather than throwing", () => {
35
- const keys = pemPair();
36
- for (const junk of ["", "ig1", "ig1..", "nonsense.a.b", "ig2.a.b", "ig1.%%%.%%%"]) {
37
- expect(verifyReachabilityGrant(keys.publicKey, junk)).toBeUndefined();
43
+ it("answers every grant in the shared fixture as it states, and garbage with undefined rather than a throw", () => {
44
+ // Both verdicts, so the loop below can never pass by holding only one kind of case.
45
+ expect(new Set(FIXTURE.grants.map(({ claim }) => (claim === null ? "refused" : "accepted")))).toEqual(new Set(["accepted", "refused"]));
46
+ for (const { token, claim } of FIXTURE.grants) {
47
+ expect({ token, claim: verifyReachabilityGrant(FIXTURE.publicKey, token) ?? null }).toEqual({ token, claim });
38
48
  }
39
49
  });
40
50
 
@@ -44,27 +54,9 @@ describe("reachability grant", () => {
44
54
  });
45
55
 
46
56
  describe("hostOwnerId", () => {
47
- it("owns the daemon's own name and every labelled name", () => {
48
- expect(hostOwnerId(`sandbox-${SANDBOX_ID}.sbx.example.dev`)).toBe(SANDBOX_ID);
49
- expect(hostOwnerId(`preview-operator-${SANDBOX_ID}.sbx.example.dev`)).toBe(SANDBOX_ID);
50
- expect(hostOwnerId(`port-0f0f0f0f0f0f-${SANDBOX_ID}.sbx.example.dev`)).toBe(SANDBOX_ID);
51
- expect(hostOwnerId(`public-1a2b3c4d5e6f-${SANDBOX_ID}.sbx.example.dev`)).toBe(SANDBOX_ID);
52
- });
53
-
54
- it("ignores a port suffix", () => {
55
- expect(hostOwnerId(`sandbox-${SANDBOX_ID}.sbx.example.dev:443`)).toBe(SANDBOX_ID);
56
- });
57
-
58
- it("owns nothing that does not end in a 12-hex tail", () => {
59
- // The ingress's own door, the zone apex, the loopback name's bare-id label, and a near-miss tail.
60
- for (const host of [
61
- "ingress.sbx.example.dev",
62
- "sbx.example.dev",
63
- `${SANDBOX_ID}.local.sbx.example.dev`,
64
- "sandbox-abc123def45.sbx.example.dev",
65
- "",
66
- ]) {
67
- expect(hostOwnerId(host)).toBeUndefined();
57
+ it("owns every host in the shared fixture as it states, port or not", () => {
58
+ for (const { host, owner } of FIXTURE.owners) {
59
+ expect({ host, owner: hostOwnerId(host) ?? null }).toEqual({ host, owner });
68
60
  }
69
61
  });
70
62
  });
@@ -73,31 +73,12 @@ export const INGRESS_GRANT_HEADER = "x-intentic-grant";
73
73
  export const ENV_INGRESS_URL = "INGRESS_URL";
74
74
  export const ENV_SANDBOX_GRANT = "SANDBOX_GRANT";
75
75
 
76
- // The daemon-side surface
77
-
78
- // Daemon-side tunnel behavior this contract requires: dial, register, forward to the loopback listener, and reconnect
79
- // forever with backoff, never giving up. `close()` is for shutdown and tests.
80
- export interface IngressTunnelOptions {
81
- // e.g. https://ingress.<zone>; the daemon derives the wss:// door itself via INGRESS_TUNNEL_PATH.
82
- readonly url: string;
83
- readonly grant: string;
84
- // The daemon's own loopback listener; every h2 stream lands there as a plain HTTP/1.1 request or upgrade.
85
- readonly targetPort: number;
86
- readonly log: (message: string, error?: unknown) => void;
87
- }
88
-
89
- export interface IngressTunnelHandle {
90
- readonly close: () => Promise<void>;
91
- // For /health and the boot log: whether the tunnel currently holds a registered session.
92
- readonly connected: () => boolean;
93
- }
94
-
95
- export type StartIngressTunnel = (options: IngressTunnelOptions) => IngressTunnelHandle;
96
-
97
- // What the ingress side must also honor, enforced by ingress-protocol tests:
76
+ // What the edge (_platform/ingress, Rust) must also honor, held by its suites and by ingress-contract.fixture.json:
98
77
  // - Register: verify the grant offline; if PLATFORM_URL is set, check the sandbox exists (GET /api/reachability/<id>)
99
78
  // and cache the answer, failing open if the platform doesn't respond. A 404 refuses the tunnel.
100
79
  // - Displacement: a new tunnel for an id closes the old session (code 4001) and takes the registration.
80
+ // - Lanes: a tunnel naming the bulk lane (tunnel-lanes.ts) is held beside the interactive one and displaces only its
81
+ // own kind; only the interactive lane is the sandbox's home in the cluster.
101
82
  // - Liveness: WebSocket ping every 15s; a peer silent for 45s is unregistered.
102
83
  // - Routing: host maps via hostOwnerId to a registered tunnel; no tunnel answers 502 naming the sandbox label, with
103
84
  // the verdict header and CORS of edge-verdict.ts, and admits the preflight of the request it is about to refuse.
@@ -8,26 +8,26 @@ import { runnerTranslatorPath } from "./runner-protocol.js";
8
8
  export const RAW_ROUTES = {
9
9
  // The "is a daemon there" probe every flow uses; it names the sandbox and says whether it is still converging.
10
10
  "GET /health": { auth: "door", beforeBoot: true },
11
- "GET /diff/raw": {},
11
+ "GET /diff/raw": { lane: "bulk" },
12
12
  // Arming the dictation model is a read any tier makes; dictating is writing a message, the collaborator's grant.
13
13
  "GET /speech/status": { guest: true },
14
14
  "POST /speech/transcribe": { floor: "collaborator", guest: true },
15
15
  // The bytes behind the workspace reads; each applies the caller's fence itself.
16
- "GET /workspace/raw": { guest: true },
16
+ "GET /workspace/raw": { guest: true, lane: "bulk" },
17
17
  // No door, unlike media: the page fetches a thumbnail itself, so the request carries the header.
18
18
  "GET /workspace/thumb": { guest: true },
19
19
  // Fetched by a <video>/<audio> tag, which sends no header: its scoped ticket is checked in the handler.
20
- "GET /workspace/media": { auth: "door", guest: true },
20
+ "GET /workspace/media": { auth: "door", guest: true, lane: "bulk" },
21
21
  // An attachment is part of the message it rides with; any other target edits the shared tree.
22
- "POST /workspace/upload": { floor: "writer", attachmentFloor: "collaborator" },
22
+ "POST /workspace/upload": { floor: "writer", attachmentFloor: "collaborator", lane: "bulk" },
23
23
  "POST /workspace/upload-diff": {},
24
- "POST /workspace/upload-archive": {},
24
+ "POST /workspace/upload-archive": { lane: "bulk" },
25
25
  // Minting is cheap: each WebSocket upgrade floors its own redemption.
26
26
  "POST /system/ws-ticket": { beforeBoot: true, floor: "collaborator", control: "never" },
27
27
  // A WebSocket upgrade carries no Authorization header; the terminal and both browser wires check a query ticket.
28
- "GET /system/terminal": { auth: "door", beforeBoot: true, control: "never" },
28
+ "GET /system/terminal": { auth: "door", beforeBoot: true, control: "never", front: true },
29
29
  // Desktop sync's byte pipe, guarded again by sshd's own key check against the same enrollment.
30
- "GET /system/sync/ssh": { sync: "pipe", control: "never" },
30
+ "GET /system/sync/ssh": { sync: "pipe", control: "never", lane: "bulk" },
31
31
  "GET /system/browser-profile": { auth: "door", beforeBoot: true, control: "never" },
32
32
  "GET /system/browser-view": { auth: "door", beforeBoot: true, control: "never" },
33
33
  // Deploy-target enrollment, gated by the connect token.
@@ -68,7 +68,7 @@ export const RAW_ROUTES = {
68
68
  "DELETE /bundles": { control: "never" },
69
69
  "POST /bundles/ticket": { control: "never" },
70
70
  // Navigated to by the browser, so no header: its ticket is checked in the handler.
71
- "GET /bundles/download": { auth: "door", control: "never" },
71
+ "GET /bundles/download": { auth: "door", control: "never", lane: "bulk" },
72
72
  "GET /definition": {},
73
73
  "POST /definition/diff": {},
74
74
  "GET /definition/workspace": {},
@@ -78,9 +78,14 @@ export const RAW_ROUTES = {
78
78
  "POST /arrivals/scan": {},
79
79
  "POST /arrivals/apply": {},
80
80
  "DELETE /arrivals": {},
81
- "GET /extensions/{id}/bundle": {},
81
+ "GET /extensions/{id}/bundle": { lane: "bulk" },
82
82
  // An extension backend's own namespace, proxied verbatim.
83
83
  "ALL /x/*": {},
84
+ // An extension card's MCP endpoint (its manifest's `mcp`), forwarded to the backend host; the per-boot token its
85
+ // handler checks is the one the turn's tool config carries.
86
+ "POST /mcp/extensions/{id}": { auth: "door" },
87
+ "GET /mcp/extensions/{id}": { auth: "door" },
88
+ "DELETE /mcp/extensions/{id}": { auth: "door" },
84
89
  // The `capabilities` CLI: discovery by name, and the ask that parks on an owner-decided card.
85
90
  "GET /capabilities/connectable": { floor: "maintainer", agent: true, control: "never" },
86
91
  "POST /capabilities/ask": { floor: "maintainer", agent: true, control: "never" },
@@ -135,16 +140,18 @@ export const RAW_ROUTES = {
135
140
  "GET /system/runners": { control: "never" },
136
141
  "DELETE /system/runners/{id}": { control: "never" },
137
142
  "GET /system/runners/connect": { auth: "door", beforeBoot: true, control: "never" },
138
- // A turn's own browser router, carrying the per-boot browser bridge token.
139
- "POST /system/browser/prepare": { auth: "door", control: "never" },
143
+ // A turn's browser routers, hosted in the daemon; each carries its own per-turn bearer, checked by the handler.
144
+ "POST /mcp/browser/{id}": { auth: "door", control: "never" },
145
+ "GET /mcp/browser/{id}": { auth: "door", control: "never" },
146
+ "DELETE /mcp/browser/{id}": { auth: "door", control: "never" },
140
147
  // A connected browser's credential doors, on its durable token: a sign-in moves in (`session`) or out (`lend`).
141
148
  "POST /system/webext/session": { auth: "door", control: "never" },
142
149
  "POST /system/webext/lend": { auth: "door", control: "never" },
143
150
  "POST /system/runners/{id}/definition/sync": { control: "never" },
144
151
  // A runner's git door and credential doors, each on the runner's own bearer.
145
- "GET /system/runners/git/{repo}/info/refs": { auth: "door", control: "never" },
146
- "POST /system/runners/git/{repo}/git-upload-pack": { auth: "door", control: "never" },
147
- "POST /system/runners/git/{repo}/git-receive-pack": { auth: "door", control: "never" },
152
+ "GET /system/runners/git/{repo}/info/refs": { auth: "door", control: "never", lane: "bulk" },
153
+ "POST /system/runners/git/{repo}/git-upload-pack": { auth: "door", control: "never", lane: "bulk" },
154
+ "POST /system/runners/git/{repo}/git-receive-pack": { auth: "door", control: "never", lane: "bulk" },
148
155
  "POST /system/runners/credentials": { auth: "door", control: "never" },
149
156
  "POST /system/runners/credentials/refresh": { auth: "door", control: "never" },
150
157
  [`ALL ${runnerTranslatorPath}/*` as const]: { auth: "door", control: "never" },
@@ -33,6 +33,10 @@ export interface RouteMeta {
33
33
  // Withheld from the panel token, which reaches every other route: it puts a stored credential in motion.
34
34
  readonly panel?: false;
35
35
  readonly control?: ControlReach;
36
+ // Rides the tunnel's bulk lane: a transfer, kept off the connection every keystroke and call waits on.
37
+ readonly lane?: "bulk";
38
+ // Served by intentic-front itself (_sandbox/front, term/): the daemon only answers the front's question about it.
39
+ readonly front?: true;
36
40
  }
37
41
 
38
42
  // The builder every sandbox procedure starts from, so each carries a RouteMeta and whatever it leaves out defaults.
@@ -188,6 +188,59 @@ export const RunnerTurnSchema = z.object({
188
188
  });
189
189
  export type RunnerTurn = z.infer<typeof RunnerTurnSchema>;
190
190
 
191
+ // OFFLOADED COMMANDS: one shell line the parent hands a runner instead of running it itself (settings `offload`). The
192
+ // code travels as a snapshot commit of the parent's working tree, uncommitted and untracked work included, parked under
193
+ // this ref in the repo's git dir for the runner to fetch through the git door, and deleted once the run ends.
194
+ export const OFFLOAD_REF_PREFIX = "refs/intentic-offload/";
195
+ export const offloadRef = (runId: string): string => `${OFFLOAD_REF_PREFIX}${runId}`;
196
+ const OffloadRunIdSchema = z.string().regex(/^[a-z0-9][a-z0-9-]{7,63}$/u);
197
+ const EnvNameSchema = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/u);
198
+
199
+ export const RunnerCommandSchema = z.object({
200
+ runId: OffloadRunIdSchema,
201
+ // The repo id the git door serves (a /history/gits entry: `root` for the workspace itself).
202
+ repo: z.string().min(1),
203
+ // The snapshot commit's ref in that repo, always under OFFLOAD_REF_PREFIX.
204
+ ref: z.string().startsWith(OFFLOAD_REF_PREFIX),
205
+ // Where in the repo the line runs, repo-relative; "" is the repo root.
206
+ cwd: z.string().refine((path) => !path.startsWith("/") && !path.split("/").includes(".."), "must stay inside the repo"),
207
+ command: z.string().min(1).max(64_000),
208
+ // Values set for the line, beside the runner's own environment. Never a secret: a line that needs one runs here.
209
+ env: z.record(EnvNameSchema, z.string()).default({}),
210
+ // Variables the line writes a file to (a report path): the runner points each at a scratch file and hands the file
211
+ // back with the exit.
212
+ exports: z.array(EnvNameSchema).max(8).default([]),
213
+ // The heavy-command rule it matched here, named in the runner's own output and queue.
214
+ label: z.string().min(1).max(80),
215
+ timeoutMs: z
216
+ .number()
217
+ .int()
218
+ .positive()
219
+ .max(6 * 60 * 60_000)
220
+ .optional(),
221
+ });
222
+ export type RunnerCommand = z.infer<typeof RunnerCommandSchema>;
223
+
224
+ // What a running command streams back: narration of the runner's own steps (fetch, install, queue), the line's output
225
+ // as it comes, and last its end: the exit, every file it changed as a binary git patch against the snapshot, and the
226
+ // exported files, base64.
227
+ export const RunnerCommandFrameSchema = z.union([
228
+ z.object({ kind: z.literal("status"), text: z.string() }),
229
+ z.object({ kind: z.literal("output"), stream: z.enum(["stdout", "stderr"]), text: z.string() }),
230
+ z.object({
231
+ kind: z.literal("exit"),
232
+ code: z.number().int(),
233
+ signal: z.string().optional(),
234
+ // Why the run did not end on its own: stopped, timed out, or never started (a fetch or install that failed).
235
+ failure: z.string().optional(),
236
+ // False when the line never started there, which sends it back to run on the parent.
237
+ ran: z.boolean().default(true),
238
+ patchBase64: z.string().optional(),
239
+ files: z.record(EnvNameSchema, z.string()).default({}),
240
+ }),
241
+ ]);
242
+ export type RunnerCommandFrame = z.infer<typeof RunnerCommandFrameSchema>;
243
+
191
244
  // Where a conversation runs, fixed on its first turn like `isolated` placement: later turns follow the registry, not
192
245
  // the sender. `runner` implies isolation since a remote conversation is branch-anchored; absent means local.
193
246
  export const AgentPlacementSchema = z.union([z.object({ kind: z.literal("local") }), z.object({ kind: z.literal("runner"), id: z.string().min(1) })]);
@@ -0,0 +1,136 @@
1
+ [
2
+ {"method":"GET","path":"/health","lane":"interactive"},
3
+ {"method":"GET","path":"/diff/raw","lane":"bulk"},
4
+ {"method":"GET","path":"/speech/status","lane":"interactive"},
5
+ {"method":"POST","path":"/speech/transcribe","lane":"interactive"},
6
+ {"method":"GET","path":"/workspace/raw","lane":"bulk"},
7
+ {"method":"GET","path":"/workspace/thumb","lane":"interactive"},
8
+ {"method":"GET","path":"/workspace/media","lane":"bulk"},
9
+ {"method":"POST","path":"/workspace/upload","lane":"bulk"},
10
+ {"method":"POST","path":"/workspace/upload-diff","lane":"interactive"},
11
+ {"method":"POST","path":"/workspace/upload-archive","lane":"bulk"},
12
+ {"method":"POST","path":"/system/ws-ticket","lane":"interactive"},
13
+ {"method":"GET","path":"/system/terminal","lane":"interactive"},
14
+ {"method":"GET","path":"/system/sync/ssh","lane":"bulk"},
15
+ {"method":"GET","path":"/system/browser-profile","lane":"interactive"},
16
+ {"method":"GET","path":"/system/browser-view","lane":"interactive"},
17
+ {"method":"POST","path":"/enroll","lane":"interactive"},
18
+ {"method":"POST","path":"/automations/{id}/fire","lane":"interactive"},
19
+ {"method":"POST","path":"/workflows/{id}/gate","lane":"interactive"},
20
+ {"method":"GET","path":"/webchat/widget.js","lane":"interactive"},
21
+ {"method":"GET","path":"/webchat/{id}/config","lane":"interactive"},
22
+ {"method":"GET","path":"/webchat/{id}/challenge","lane":"interactive"},
23
+ {"method":"POST","path":"/webchat/{id}/message","lane":"interactive"},
24
+ {"method":"GET","path":"/webchat/{id}/messages","lane":"interactive"},
25
+ {"method":"GET","path":"/webchat/{id}/installs","lane":"interactive"},
26
+ {"method":"GET","path":"/intake/sdk.js","lane":"interactive"},
27
+ {"method":"GET","path":"/intake/{id}/config","lane":"interactive"},
28
+ {"method":"GET","path":"/intake/{id}/challenge","lane":"interactive"},
29
+ {"method":"POST","path":"/intake/{id}/report","lane":"interactive"},
30
+ {"method":"GET","path":"/members","lane":"interactive"},
31
+ {"method":"POST","path":"/members","lane":"interactive"},
32
+ {"method":"DELETE","path":"/members","lane":"interactive"},
33
+ {"method":"DELETE","path":"/members/self","lane":"interactive"},
34
+ {"method":"GET","path":"/environment","lane":"interactive"},
35
+ {"method":"GET","path":"/environment/contents","lane":"interactive"},
36
+ {"method":"POST","path":"/environment/approve","lane":"interactive"},
37
+ {"method":"POST","path":"/environment/reject","lane":"interactive"},
38
+ {"method":"POST","path":"/environment/runtime-install","lane":"interactive"},
39
+ {"method":"GET","path":"/engines","lane":"interactive"},
40
+ {"method":"POST","path":"/engines/channel","lane":"interactive"},
41
+ {"method":"POST","path":"/engines/update","lane":"interactive"},
42
+ {"method":"POST","path":"/engines/revert","lane":"interactive"},
43
+ {"method":"GET","path":"/bundles","lane":"interactive"},
44
+ {"method":"POST","path":"/bundles","lane":"interactive"},
45
+ {"method":"DELETE","path":"/bundles","lane":"interactive"},
46
+ {"method":"POST","path":"/bundles/ticket","lane":"interactive"},
47
+ {"method":"GET","path":"/bundles/download","lane":"bulk"},
48
+ {"method":"GET","path":"/definition","lane":"interactive"},
49
+ {"method":"POST","path":"/definition/diff","lane":"interactive"},
50
+ {"method":"GET","path":"/definition/workspace","lane":"interactive"},
51
+ {"method":"POST","path":"/definition/workspace/publish","lane":"interactive"},
52
+ {"method":"POST","path":"/arrivals/plan","lane":"interactive"},
53
+ {"method":"GET","path":"/arrivals/hosts","lane":"interactive"},
54
+ {"method":"POST","path":"/arrivals/scan","lane":"interactive"},
55
+ {"method":"POST","path":"/arrivals/apply","lane":"interactive"},
56
+ {"method":"DELETE","path":"/arrivals","lane":"interactive"},
57
+ {"method":"GET","path":"/extensions/{id}/bundle","lane":"bulk"},
58
+ {"method":"ALL","path":"/x/*","lane":"interactive"},
59
+ {"method":"POST","path":"/mcp/extensions/{id}","lane":"interactive"},
60
+ {"method":"GET","path":"/mcp/extensions/{id}","lane":"interactive"},
61
+ {"method":"DELETE","path":"/mcp/extensions/{id}","lane":"interactive"},
62
+ {"method":"GET","path":"/capabilities/connectable","lane":"interactive"},
63
+ {"method":"POST","path":"/capabilities/ask","lane":"interactive"},
64
+ {"method":"GET","path":"/sandboxes","lane":"interactive"},
65
+ {"method":"POST","path":"/sandboxes","lane":"interactive"},
66
+ {"method":"GET","path":"/wallet/status","lane":"interactive"},
67
+ {"method":"POST","path":"/wallet/fetch","lane":"interactive"},
68
+ {"method":"GET","path":"/wallet/history","lane":"interactive"},
69
+ {"method":"POST","path":"/children/spawn","lane":"interactive"},
70
+ {"method":"GET","path":"/children/providers","lane":"interactive"},
71
+ {"method":"POST","path":"/children/wait","lane":"interactive"},
72
+ {"method":"POST","path":"/children/send","lane":"interactive"},
73
+ {"method":"POST","path":"/children/answer","lane":"interactive"},
74
+ {"method":"GET","path":"/children","lane":"interactive"},
75
+ {"method":"GET","path":"/fleet","lane":"interactive"},
76
+ {"method":"POST","path":"/fleet/message","lane":"interactive"},
77
+ {"method":"GET","path":"/fleet/{handle}","lane":"interactive"},
78
+ {"method":"GET","path":"/listeners/{provider}/state","lane":"interactive"},
79
+ {"method":"POST","path":"/listeners/{provider}/dispatch","lane":"interactive"},
80
+ {"method":"POST","path":"/listeners/{provider}/failure","lane":"interactive"},
81
+ {"method":"POST","path":"/listeners/{provider}/status","lane":"interactive"},
82
+ {"method":"POST","path":"/ci/webhook/{host}","lane":"interactive"},
83
+ {"method":"POST","path":"/system/sync/pair","lane":"interactive"},
84
+ {"method":"POST","path":"/system/hosts/pair","lane":"interactive"},
85
+ {"method":"POST","path":"/system/hosts/enroll","lane":"interactive"},
86
+ {"method":"GET","path":"/system/hosts","lane":"interactive"},
87
+ {"method":"DELETE","path":"/system/hosts/{id}","lane":"interactive"},
88
+ {"method":"GET","path":"/system/hosts/connect","lane":"interactive"},
89
+ {"method":"POST","path":"/mcp/hosts/{id}","lane":"interactive"},
90
+ {"method":"GET","path":"/mcp/hosts/{id}","lane":"interactive"},
91
+ {"method":"DELETE","path":"/mcp/hosts/{id}","lane":"interactive"},
92
+ {"method":"POST","path":"/system/webext/pair","lane":"interactive"},
93
+ {"method":"POST","path":"/system/webext/enroll","lane":"interactive"},
94
+ {"method":"GET","path":"/system/webext","lane":"interactive"},
95
+ {"method":"DELETE","path":"/system/webext/{id}","lane":"interactive"},
96
+ {"method":"GET","path":"/system/webext/connect","lane":"interactive"},
97
+ {"method":"POST","path":"/mcp/webext/{id}","lane":"interactive"},
98
+ {"method":"GET","path":"/mcp/webext/{id}","lane":"interactive"},
99
+ {"method":"DELETE","path":"/mcp/webext/{id}","lane":"interactive"},
100
+ {"method":"POST","path":"/system/runners/pair","lane":"interactive"},
101
+ {"method":"POST","path":"/system/runners/enroll","lane":"interactive"},
102
+ {"method":"GET","path":"/system/runners","lane":"interactive"},
103
+ {"method":"DELETE","path":"/system/runners/{id}","lane":"interactive"},
104
+ {"method":"GET","path":"/system/runners/connect","lane":"interactive"},
105
+ {"method":"POST","path":"/mcp/browser/{id}","lane":"interactive"},
106
+ {"method":"GET","path":"/mcp/browser/{id}","lane":"interactive"},
107
+ {"method":"DELETE","path":"/mcp/browser/{id}","lane":"interactive"},
108
+ {"method":"POST","path":"/system/webext/session","lane":"interactive"},
109
+ {"method":"POST","path":"/system/webext/lend","lane":"interactive"},
110
+ {"method":"POST","path":"/system/runners/{id}/definition/sync","lane":"interactive"},
111
+ {"method":"GET","path":"/system/runners/git/{repo}/info/refs","lane":"bulk"},
112
+ {"method":"POST","path":"/system/runners/git/{repo}/git-upload-pack","lane":"bulk"},
113
+ {"method":"POST","path":"/system/runners/git/{repo}/git-receive-pack","lane":"bulk"},
114
+ {"method":"POST","path":"/system/runners/credentials","lane":"interactive"},
115
+ {"method":"POST","path":"/system/runners/credentials/refresh","lane":"interactive"},
116
+ {"method":"ALL","path":"/system/runners/translator/*","lane":"interactive"},
117
+ {"method":"POST","path":"/system/control/tokens","lane":"interactive"},
118
+ {"method":"GET","path":"/system/control/tokens","lane":"interactive"},
119
+ {"method":"DELETE","path":"/system/control/tokens/{id}","lane":"interactive"},
120
+ {"method":"GET","path":"/system/passkeys","lane":"interactive"},
121
+ {"method":"POST","path":"/system/passkeys/register/options","lane":"interactive"},
122
+ {"method":"POST","path":"/system/passkeys/register","lane":"interactive"},
123
+ {"method":"POST","path":"/system/passkeys/assert/options","lane":"interactive"},
124
+ {"method":"POST","path":"/system/passkeys/assert","lane":"interactive"},
125
+ {"method":"POST","path":"/system/passkeys/policy","lane":"interactive"},
126
+ {"method":"POST","path":"/system/passkeys/recovery","lane":"interactive"},
127
+ {"method":"DELETE","path":"/system/passkeys/{id}","lane":"interactive"},
128
+ {"method":"POST","path":"/system/session/recover","lane":"interactive"},
129
+ {"method":"POST","path":"/system/sessions/revoke","lane":"interactive"},
130
+ {"method":"POST","path":"/system/access/disable","lane":"interactive"},
131
+ {"method":"POST","path":"/system/authorized-key","lane":"interactive"},
132
+ {"method":"GET","path":"/system/sync","lane":"interactive"},
133
+ {"method":"POST","path":"/system/sync/report","lane":"interactive"},
134
+ {"method":"DELETE","path":"/system/authorized-key","lane":"interactive"},
135
+ {"method":"DELETE","path":"/system/authorized-key/{machine}","lane":"interactive"}
136
+ ]
@@ -0,0 +1,30 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { type TunnelLane, tunnelLaneNamed, tunnelLaneOf, tunnelLaneTable } from "./tunnel-lanes.js";
3
+
4
+ interface LaneCase {
5
+ readonly host: string;
6
+ readonly method: string;
7
+ readonly path: string;
8
+ readonly lane: TunnelLane;
9
+ }
10
+
11
+ const read = (name: string): unknown => JSON.parse(readFileSync(new URL(name, import.meta.url), "utf8"));
12
+ const CASES = (read("./ingress-contract.fixture.json") as { lanes: LaneCase[] }).lanes;
13
+
14
+ test("the table the Rust edge embeds is the raw routes' lanes as they stand (pnpm --filter @intentic/sandbox-contract lanes)", () => {
15
+ expect(read("./tunnel-lanes.json")).toEqual(tunnelLaneTable());
16
+ });
17
+
18
+ test("every request the shared fixture names rides the lane it states", () => {
19
+ expect(CASES.length).toBeGreaterThan(20);
20
+ for (const { host, method, path, lane } of CASES) {
21
+ expect({ host, method, path, lane: tunnelLaneOf(host, method, path) }).toEqual({ host, method, path, lane });
22
+ }
23
+ });
24
+
25
+ test("a tunnel is interactive unless its upgrade names the bulk lane", () => {
26
+ expect(tunnelLaneNamed("bulk")).toBe("bulk");
27
+ expect(tunnelLaneNamed(undefined)).toBe("interactive");
28
+ expect(tunnelLaneNamed("interactive")).toBe("interactive");
29
+ expect(tunnelLaneNamed(["bulk", "bulk"])).toBe("interactive");
30
+ });
@@ -0,0 +1,32 @@
1
+ import { sandboxSubdomain } from "../ids/hostnames.js";
2
+ import { hostOwnerId } from "./ingress-contract.js";
3
+ import { RAW_ROUTE_LIST } from "./raw-routes.js";
4
+ import { servedRoute } from "./routes.js";
5
+
6
+ // Each sandbox dials two tunnels, and each browser request rides one, so a transfer never queues a keystroke, a call or
7
+ // an event frame behind it on one TCP connection. The front dials both (_sandbox/front tunnel.rs); the edge
8
+ // (_platform/ingress lanes.rs) picks by the table this file emits, and falls back to the other lane while the one picked
9
+ // is not held.
10
+
11
+ export type TunnelLane = "interactive" | "bulk";
12
+
13
+ // Names a tunnel's lane on its upgrade; a tunnel that names none is the interactive one.
14
+ export const INGRESS_LANE_HEADER = "x-intentic-lane";
15
+
16
+ export const tunnelLaneNamed = (header: string | string[] | undefined): TunnelLane => (header === "bulk" ? "bulk" : "interactive");
17
+
18
+ // A preview's dev server (every label but the daemon's own) is bulk: a page load is hundreds of requests and its assets
19
+ // can be large. The daemon's routes are interactive unless their policy says otherwise (RouteMeta `lane`).
20
+ export const tunnelLaneOf = (host: string, method: string, path: string): TunnelLane => {
21
+ const id = hostOwnerId(host);
22
+ const label = host.split(":")[0]?.split(".")[0] ?? "";
23
+ if (id === undefined || label !== sandboxSubdomain(id)) {
24
+ return "bulk";
25
+ }
26
+ return servedRoute(RAW_ROUTE_LIST, [], method, path)?.meta.lane ?? "interactive";
27
+ };
28
+
29
+ // The raw routes in registration order with the lane each rides: what the Rust edge matches a daemon request against,
30
+ // written to tunnel-lanes.json by scripts/write-lanes.mjs since the edge cannot run this file.
31
+ export const tunnelLaneTable = (): { readonly method: string; readonly path: string; readonly lane: TunnelLane }[] =>
32
+ RAW_ROUTE_LIST.map(({ method, path, meta }) => ({ method, path, lane: meta.lane ?? "interactive" }));
@@ -20,7 +20,9 @@ export const ActivityEventSchema = z.object({
20
20
  // in: message.received, voice_utterance.received, voice_transcript.received
21
21
  // out: message.send, reaction.add, messages.read, api.call (unclassified endpoint)
22
22
  // system: gateway.login_failed, dispatch.failed, voice.session_started, voice.session_ended, automation.run,
23
- // turn.started, turn.plan, turn.error, turn.completed, rule.blocked_push, rule.held_work, rule.continued_turn
23
+ // turn.started, turn.plan, turn.error, turn.completed, rule.held_work, deps.verify_green, deps.verify_red,
24
+ // deps.breakage_routed, deps.breakage_fixup, deps.breakage_waiting, deps.breakage_held, deps.breakage_resolved,
25
+ // deps.breakage_spent; rule.blocked_push and rule.continued_turn are retired and only appear in older logs
24
26
  type: z
25
27
  .string()
26
28
  .describe(