@nylorun/runtime 0.13.0-beta → 0.15.0-beta

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 (309) hide show
  1. package/CHANGELOG.md +434 -0
  2. package/README.md +137 -41
  3. package/dist/adapters/execution/restate.d.ts +7 -2
  4. package/dist/adapters/execution/restate.js +40 -10
  5. package/dist/adapters/replication/pgoutput.d.ts +21 -0
  6. package/dist/adapters/replication/pgoutput.js +170 -0
  7. package/dist/adapters/streams/s2.js +37 -20
  8. package/dist/admin-openapi.json +570 -0
  9. package/dist/api/a2a/card.d.ts +15 -0
  10. package/dist/api/a2a/card.js +24 -0
  11. package/dist/api/a2a/endpoint.d.ts +8 -0
  12. package/dist/api/a2a/endpoint.js +81 -0
  13. package/dist/api/a2a/protocol.d.ts +144 -0
  14. package/dist/api/a2a/protocol.js +188 -0
  15. package/dist/api/a2a/routes.d.ts +19 -0
  16. package/dist/api/a2a/routes.js +289 -0
  17. package/dist/api/a2a/tasks.d.ts +50 -0
  18. package/dist/api/a2a/tasks.js +186 -0
  19. package/dist/api/ag-ui/endpoint.d.ts +8 -0
  20. package/dist/api/ag-ui/endpoint.js +126 -0
  21. package/dist/api/ag-ui/history.d.ts +7 -0
  22. package/dist/api/ag-ui/history.js +60 -0
  23. package/dist/api/ag-ui/routes.d.ts +32 -0
  24. package/dist/api/ag-ui/routes.js +305 -0
  25. package/dist/api/ag-ui/session-id.d.ts +1 -0
  26. package/dist/api/ag-ui/session-id.js +12 -0
  27. package/dist/api/ag-ui/sse.d.ts +9 -0
  28. package/dist/api/ag-ui/sse.js +7 -0
  29. package/dist/api/ag-ui/translate.d.ts +38 -0
  30. package/dist/api/ag-ui/translate.js +222 -0
  31. package/dist/api/components.d.ts +8065 -0
  32. package/dist/api/components.js +82 -0
  33. package/dist/api/gate/keys.d.ts +14 -0
  34. package/dist/api/gate/keys.js +59 -0
  35. package/dist/api/gate/routes.d.ts +34 -0
  36. package/dist/api/gate/routes.js +235 -0
  37. package/dist/api/http/app.d.ts +18 -0
  38. package/dist/api/http/app.js +58 -0
  39. package/dist/api/http/body.d.ts +4 -0
  40. package/dist/api/http/body.js +37 -0
  41. package/dist/api/http/define.d.ts +39 -0
  42. package/dist/api/http/define.js +122 -0
  43. package/dist/api/http/respond.d.ts +9 -0
  44. package/dist/api/http/respond.js +44 -0
  45. package/dist/api/http/routes/access.d.ts +24 -0
  46. package/dist/api/http/routes/access.js +266 -0
  47. package/dist/api/http/routes/actions.d.ts +8 -0
  48. package/dist/api/http/routes/actions.js +76 -0
  49. package/dist/api/http/routes/endpoints.d.ts +7 -0
  50. package/dist/api/http/routes/endpoints.js +100 -0
  51. package/dist/api/http/routes/sessions.d.ts +3 -0
  52. package/dist/api/http/routes/sessions.js +205 -0
  53. package/dist/api/http/routes/tenant.d.ts +11 -0
  54. package/dist/api/http/routes/tenant.js +244 -0
  55. package/dist/api/http/routes/vaults.d.ts +8 -0
  56. package/dist/api/http/routes/vaults.js +167 -0
  57. package/dist/api/openapi.d.ts +19 -0
  58. package/dist/api/openapi.js +137 -0
  59. package/dist/api/route.d.ts +12 -0
  60. package/dist/api/route.js +4 -0
  61. package/dist/core/bearer.d.ts +8 -0
  62. package/dist/core/bearer.js +11 -0
  63. package/dist/core/flow-host.d.ts +7 -15
  64. package/dist/core/flow-host.js +16 -41
  65. package/dist/core/sandbox-routes.d.ts +12 -7
  66. package/dist/core/sandbox-routes.js +24 -25
  67. package/dist/execution/memory.d.ts +8 -5
  68. package/dist/execution/memory.js +33 -13
  69. package/dist/execution/types.d.ts +38 -6
  70. package/dist/execution/types.js +12 -3
  71. package/dist/gates/contract.d.ts +51 -0
  72. package/dist/gates/contract.js +37 -0
  73. package/dist/gates/handler.d.ts +21 -0
  74. package/dist/gates/handler.js +55 -0
  75. package/dist/gates/http-client.d.ts +12 -0
  76. package/dist/gates/http-client.js +160 -0
  77. package/dist/gates/in-process.d.ts +32 -0
  78. package/dist/gates/in-process.js +42 -0
  79. package/dist/gates/inflight.d.ts +39 -0
  80. package/dist/gates/inflight.js +70 -0
  81. package/dist/gates/mcp-handler.d.ts +35 -0
  82. package/dist/gates/mcp-handler.js +142 -0
  83. package/dist/gates/meter.d.ts +24 -0
  84. package/dist/gates/meter.js +165 -0
  85. package/dist/gates/model-gate.d.ts +45 -0
  86. package/dist/gates/model-gate.js +1 -0
  87. package/dist/gates/tenant-vaults.d.ts +64 -0
  88. package/dist/gates/tenant-vaults.js +94 -0
  89. package/dist/gates/tool-calls.d.ts +51 -0
  90. package/dist/gates/tool-calls.js +64 -0
  91. package/dist/gates/tool-client.d.ts +12 -0
  92. package/dist/gates/tool-client.js +201 -0
  93. package/dist/gates/tool-contract.d.ts +116 -0
  94. package/dist/gates/tool-contract.js +64 -0
  95. package/dist/gates/tool-gate.d.ts +45 -0
  96. package/dist/gates/tool-gate.js +10 -0
  97. package/dist/host/admin-api.d.ts +23 -0
  98. package/dist/host/admin-api.js +74 -0
  99. package/dist/host/app.d.ts +34 -0
  100. package/dist/host/app.js +181 -0
  101. package/dist/host/config-for.d.ts +3 -1
  102. package/dist/host/config-for.js +3 -2
  103. package/dist/host/config.d.ts +11 -0
  104. package/dist/host/cors.d.ts +27 -0
  105. package/dist/host/cors.js +64 -0
  106. package/dist/host/create-host.d.ts +36 -8
  107. package/dist/host/create-host.js +161 -330
  108. package/dist/host/execution.d.ts +20 -18
  109. package/dist/host/execution.js +2 -2
  110. package/dist/host/gates.d.ts +58 -0
  111. package/dist/host/gates.js +127 -0
  112. package/dist/host/http.d.ts +20 -4
  113. package/dist/host/http.js +81 -23
  114. package/dist/host/index.d.ts +1 -1
  115. package/dist/host/main.js +189 -58
  116. package/dist/host/stack-config.d.ts +91 -19
  117. package/dist/host/stack-config.js +246 -17
  118. package/dist/infra/database.d.ts +2 -2
  119. package/dist/infra/execution.d.ts +5 -5
  120. package/dist/infra/execution.js +8 -6
  121. package/dist/infra/index.d.ts +1 -1
  122. package/dist/infra/workers.d.ts +17 -17
  123. package/dist/infra/workers.js +9 -9
  124. package/dist/keys/client.d.ts +9 -0
  125. package/dist/keys/client.js +70 -0
  126. package/dist/keys/contract.d.ts +24 -0
  127. package/dist/keys/contract.js +12 -0
  128. package/dist/keys/keys.d.ts +41 -0
  129. package/dist/keys/keys.js +50 -0
  130. package/dist/mcp/connect.d.ts +43 -3
  131. package/dist/mcp/connect.js +12 -5
  132. package/dist/mcp/pool.d.ts +39 -3
  133. package/dist/mcp/pool.js +74 -11
  134. package/dist/model/catalog.d.ts +2 -10
  135. package/dist/model/classify.d.ts +13 -0
  136. package/dist/model/classify.js +61 -0
  137. package/dist/model/models.d.ts +10 -1
  138. package/dist/model/models.js +14 -6
  139. package/dist/model/pi-model.d.ts +17 -2
  140. package/dist/model/pi-model.js +366 -222
  141. package/dist/openapi.json +25889 -0
  142. package/dist/record/append.d.ts +51 -0
  143. package/dist/record/append.js +20 -0
  144. package/dist/record/envelope.d.ts +19 -0
  145. package/dist/record/envelope.js +49 -0
  146. package/dist/record/index.d.ts +11 -0
  147. package/dist/record/index.js +11 -0
  148. package/dist/sandbox/manager.d.ts +13 -1
  149. package/dist/sandbox/manager.js +8 -11
  150. package/dist/sandbox/resolve.d.ts +33 -0
  151. package/dist/sandbox/resolve.js +81 -0
  152. package/dist/sandbox/session-sandbox.d.ts +11 -0
  153. package/dist/sandbox/session-sandbox.js +51 -0
  154. package/dist/sandbox/share.d.ts +14 -2
  155. package/dist/sandbox/share.js +33 -3
  156. package/dist/sandbox/tenant-config.d.ts +37 -0
  157. package/dist/sandbox/tenant-config.js +51 -0
  158. package/dist/sandbox/tools.d.ts +7 -9
  159. package/dist/sandbox/tools.js +11 -3
  160. package/dist/sandbox/types.d.ts +8 -2
  161. package/dist/store/postgres/connect.d.ts +25 -9
  162. package/dist/store/postgres/connect.js +27 -10
  163. package/dist/store/postgres/db.d.ts +16 -0
  164. package/dist/store/postgres/db.js +28 -0
  165. package/dist/store/postgres/drizzle/0000_foundation.sql +22 -0
  166. package/dist/store/postgres/drizzle/0001_baseline.sql +255 -0
  167. package/dist/store/postgres/drizzle/0002_stream_relay.sql +4 -0
  168. package/dist/store/postgres/drizzle/0003_tool_crossings.sql +9 -0
  169. package/dist/store/postgres/drizzle/meta/_journal.json +34 -0
  170. package/dist/store/postgres/migrate.d.ts +68 -0
  171. package/dist/store/postgres/migrate.js +183 -0
  172. package/dist/store/postgres/record-writer.d.ts +4 -0
  173. package/dist/store/postgres/record-writer.js +34 -0
  174. package/dist/store/postgres/record.d.ts +5 -0
  175. package/dist/store/postgres/record.js +60 -0
  176. package/dist/store/postgres/schema.d.ts +3096 -0
  177. package/dist/store/postgres/schema.js +363 -0
  178. package/dist/store/postgres/store.d.ts +2 -4
  179. package/dist/store/postgres/store.js +590 -434
  180. package/dist/store/postgres/tenant.d.ts +56 -0
  181. package/dist/store/postgres/tenant.js +154 -0
  182. package/dist/store/types.d.ts +183 -132
  183. package/dist/store/types.js +0 -1
  184. package/dist/streams/basin.d.ts +18 -1
  185. package/dist/streams/basin.js +34 -2
  186. package/dist/streams/control.d.ts +18 -0
  187. package/dist/streams/control.js +33 -0
  188. package/dist/streams/memory.d.ts +3 -1
  189. package/dist/streams/memory.js +14 -8
  190. package/dist/streams/relay/core.d.ts +35 -0
  191. package/dist/streams/relay/core.js +366 -0
  192. package/dist/streams/relay/types.d.ts +78 -0
  193. package/dist/streams/relay/types.js +8 -0
  194. package/dist/streams/types.d.ts +71 -36
  195. package/dist/streams/types.js +18 -53
  196. package/dist/tenant/access-policy.d.ts +30 -0
  197. package/dist/tenant/access-policy.js +52 -0
  198. package/dist/tenant/actions.d.ts +10 -149
  199. package/dist/tenant/actions.js +5 -167
  200. package/dist/tenant/advance.d.ts +7 -2
  201. package/dist/tenant/advance.js +125 -14
  202. package/dist/tenant/auth.d.ts +19 -17
  203. package/dist/tenant/auth.js +90 -93
  204. package/dist/tenant/browser.d.ts +15 -0
  205. package/dist/tenant/browser.js +39 -0
  206. package/dist/tenant/cause.d.ts +25 -0
  207. package/dist/tenant/cause.js +45 -0
  208. package/dist/tenant/commands.d.ts +18 -2
  209. package/dist/tenant/commands.js +111 -115
  210. package/dist/tenant/context.d.ts +101 -21
  211. package/dist/tenant/context.js +14 -10
  212. package/dist/tenant/delivery-token.d.ts +41 -0
  213. package/dist/tenant/delivery-token.js +104 -0
  214. package/dist/tenant/delivery.d.ts +62 -0
  215. package/dist/tenant/delivery.js +481 -0
  216. package/dist/tenant/effects.d.ts +20 -1
  217. package/dist/tenant/effects.js +159 -42
  218. package/dist/tenant/endpoints.d.ts +67 -0
  219. package/dist/tenant/endpoints.js +90 -0
  220. package/dist/tenant/ephemeral.d.ts +22 -7
  221. package/dist/tenant/ephemeral.js +83 -125
  222. package/dist/tenant/history.d.ts +64 -0
  223. package/dist/tenant/history.js +120 -0
  224. package/dist/tenant/http.d.ts +8 -9
  225. package/dist/tenant/http.js +13 -27
  226. package/dist/tenant/jwt.d.ts +16 -0
  227. package/dist/tenant/jwt.js +50 -0
  228. package/dist/tenant/model-setting.d.ts +2 -2
  229. package/dist/tenant/module.d.ts +17 -28
  230. package/dist/tenant/module.js +133 -279
  231. package/dist/tenant/outbound.d.ts +44 -0
  232. package/dist/tenant/outbound.js +127 -0
  233. package/dist/tenant/paths.d.ts +11 -16
  234. package/dist/tenant/paths.js +24 -68
  235. package/dist/tenant/pool.d.ts +1 -3
  236. package/dist/tenant/pool.js +1 -19
  237. package/dist/tenant/principals.d.ts +23 -11
  238. package/dist/tenant/principals.js +61 -49
  239. package/dist/tenant/reset.d.ts +0 -4
  240. package/dist/tenant/reset.js +0 -5
  241. package/dist/tenant/runtime.d.ts +28 -20
  242. package/dist/tenant/runtime.js +95 -64
  243. package/dist/tenant/scheduler.d.ts +10 -3
  244. package/dist/tenant/scheduler.js +9 -5
  245. package/dist/tenant/session-streams.d.ts +127 -0
  246. package/dist/tenant/session-streams.js +404 -0
  247. package/dist/tenant/sessions.d.ts +28 -4
  248. package/dist/tenant/sessions.js +118 -24
  249. package/dist/tenant/signing-keys.d.ts +55 -0
  250. package/dist/tenant/signing-keys.js +172 -0
  251. package/dist/tenant/slim.d.ts +8 -0
  252. package/dist/tenant/slim.js +21 -0
  253. package/dist/tenant/status.d.ts +3 -3
  254. package/dist/tenant/status.js +20 -13
  255. package/dist/tenant/store-pg.d.ts +9 -6
  256. package/dist/tenant/store-pg.js +60 -125
  257. package/dist/tenant/streams.d.ts +62 -85
  258. package/dist/tenant/streams.js +143 -226
  259. package/dist/tenant/subject-limits.d.ts +16 -0
  260. package/dist/tenant/subject-limits.js +45 -0
  261. package/dist/tenant/sweep.d.ts +1 -10
  262. package/dist/tenant/sweep.js +29 -69
  263. package/dist/tenant/tokens.d.ts +14 -0
  264. package/dist/tenant/tokens.js +180 -0
  265. package/dist/tenant/transcript.d.ts +48 -5
  266. package/dist/tenant/transcript.js +58 -1
  267. package/dist/tenant/types.d.ts +69 -98
  268. package/dist/tenant/types.js +4 -15
  269. package/dist/tenant/worker.d.ts +3 -1
  270. package/dist/tenant/worker.js +13 -7
  271. package/dist/vault/host-model.d.ts +31 -0
  272. package/dist/vault/host-model.js +57 -0
  273. package/dist/vault/service.d.ts +44 -17
  274. package/dist/vault/service.js +126 -121
  275. package/dist/version.d.ts +1 -1
  276. package/dist/version.js +1 -1
  277. package/package.json +25 -9
  278. package/dist/core/executors.d.ts +0 -44
  279. package/dist/core/executors.js +0 -95
  280. package/dist/store/memory.d.ts +0 -57
  281. package/dist/store/memory.js +0 -645
  282. package/dist/store/postgres/migrations/001_initial.d.ts +0 -27
  283. package/dist/store/postgres/migrations/001_initial.js +0 -196
  284. package/dist/store/postgres/migrations/002_session_owner.d.ts +0 -6
  285. package/dist/store/postgres/migrations/002_session_owner.js +0 -13
  286. package/dist/store/postgres/migrations/index.d.ts +0 -49
  287. package/dist/store/postgres/migrations/index.js +0 -76
  288. package/dist/store/postgres/names.d.ts +0 -11
  289. package/dist/store/postgres/names.js +0 -42
  290. package/dist/store/postgres/tenants.d.ts +0 -90
  291. package/dist/store/postgres/tenants.js +0 -214
  292. package/dist/streams/relay.d.ts +0 -37
  293. package/dist/streams/relay.js +0 -254
  294. package/dist/tenant/envelope.d.ts +0 -7
  295. package/dist/tenant/envelope.js +0 -22
  296. package/dist/tenant/live.d.ts +0 -92
  297. package/dist/tenant/live.js +0 -290
  298. package/dist/tenant/quarantine-error.d.ts +0 -6
  299. package/dist/tenant/quarantine-error.js +0 -9
  300. package/dist/tenant/quarantine.d.ts +0 -27
  301. package/dist/tenant/quarantine.js +0 -72
  302. package/dist/tenant/routes-tenant.d.ts +0 -12
  303. package/dist/tenant/routes-tenant.js +0 -137
  304. package/dist/tenant/routes.d.ts +0 -9
  305. package/dist/tenant/routes.js +0 -151
  306. package/dist/tenant/store-memory.d.ts +0 -16
  307. package/dist/tenant/store-memory.js +0 -111
  308. /package/dist/{store → record}/cursor.d.ts +0 -0
  309. /package/dist/{store → record}/cursor.js +0 -0
@@ -1,24 +1,18 @@
1
1
  /**
2
- * The Durable Streams seam (architecture §12.4).
2
+ * The Durable Streams seam (Durable Streams §8).
3
3
  *
4
- * Durable Streams tell everyone else what the Session Store recorded. Each
5
- * Tenant has one S2 basin; inside it:
4
+ * Durable Streams deliver what the record holds. Each Tenant has one S2 basin per basin
5
+ * generation (`streams/basin.ts`); inside it:
6
6
  *
7
7
  * | Stream | Contents | Written by | Read by |
8
8
  * | --- | --- | --- | --- |
9
- * | `sessions/<sessionId>/<incarnation>` | every `LiveEvent` of one incarnation of the session, in sequence | the relay, from the outbox | history and session SSE |
10
- * | `tenant/work` | `work_available` signals | API nodes and Workers after committing pending Actions | executor SSE on every API node |
11
- * | `tenant/control` | `session.cancel` and `sessions.reset` signals | API nodes | every process with the Tenant open |
9
+ * | `sessions/<sessionId>` | every event of the session, in sequence | the stream relay, from the record | history and session SSE |
10
+ * | `tenant/control` | `session.cancel`, `sessions.reset` and `subject.revoked` signals | API nodes | every process with the Tenant open |
12
11
  *
13
- * **Incarnations.** A session's stream name carries an incarnation, a random
14
- * id stored on the session document when the session is created
15
- * (`streamIncarnation`, see `streamOfSession`). A session deleted by a reset
16
- * and created again with the same id gets a new incarnation, so a new stream
17
- * starting at sequence 0: nothing ever deletes and re-creates the same stream.
18
- * Abandoned streams are deleted best effort afterwards
19
- * (`collectSessionStreams` in `tenant/streams.ts`). The cursor stays
20
- * `base64url("<sessionId>:<seq>")`; readers resolve the incarnation from the
21
- * session.
12
+ * **Basin generations.** A session id is unique within its Tenant's basin generation. A
13
+ * Tenant reset, the only path that frees ids, moves the Tenant to the next generation, so
14
+ * a session created again with the same id starts in an empty basin at sequence 0; the old
15
+ * basin is deleted afterwards. The cursor is `base64url("<sessionId>:<seq>")`.
22
16
  *
23
17
  * S2 is the supported implementation (`adapters/streams/s2.ts`, the only file
24
18
  * importing the S2 SDK); `streams/memory.ts` is the in-memory fake.
@@ -31,56 +25,27 @@
31
25
  * - **Conditional appends.** With `matchSeq`, a batch is appended only when the
32
26
  * tail equals `matchSeq`; otherwise nothing is written and the result is a
33
27
  * `SeqMismatch` carrying the current tail. The relay appends a session
34
- * stream with `matchSeq` set to the outbox row's sequence, so a retried
28
+ * stream with `matchSeq` set to the record row's sequence, so a retried
35
29
  * append after an unacknowledged success is detected and never duplicated.
36
30
  * - **Batches are atomic.** A batch is appended entirely or not at all.
37
31
  * - **Reads resume.** `read` from `fromSeq` yields every record with
38
32
  * `seq >= fromSeq` in order, with no gap between history and the live tail.
39
- * - **Tenant scope.** `ensureTenant` creates the Tenant's basin (idempotent)
40
- * and must run before appends; streams are created on first append.
41
- * `deleteTenant` removes the basin and every stream in it.
33
+ * - **Tenant scope.** Methods take a Tenant id, or a basin key from `basinOf`
34
+ * for a later generation. `ensureTenant` creates the basin (idempotent) and
35
+ * must run before appends; streams are created on first append or read.
36
+ * `deleteTenant` removes the basin and every stream in it (with
37
+ * `allGenerations`, every generation's basin of the Tenant).
42
38
  * - **Records are JSON.** Bodies are JSON-serializable values; implementations
43
39
  * encode them as they need.
44
40
  */
45
- import { randomBytes } from "node:crypto";
46
41
  // ---------------------------------------------------------------------------
47
42
  // Stream names and signal records
48
43
  export const SESSION_STREAM_PREFIX = "sessions/";
49
- /** `work_available` signals for executor connections. */
50
- export const WORK_STREAM = "tenant/work";
51
44
  /** `session.cancel` and `sessions.reset` signals for every process with the Tenant open. */
52
45
  export const CONTROL_STREAM = "tenant/control";
53
- /**
54
- * The incarnation of a session document written before incarnations existed.
55
- * Random incarnations are 12 characters, so never this.
56
- */
57
- export const LEGACY_INCARNATION = "0";
58
- /** A new session incarnation: 12 random base64url characters. */
59
- export function newStreamIncarnation() {
60
- return randomBytes(9).toString("base64url");
61
- }
62
- /** The stream of one incarnation of a session: `sessions/<sessionId>/<incarnation>`. */
63
- export function sessionStream(sessionId, incarnation) {
46
+ /** The stream of a session: `sessions/<sessionId>`. */
47
+ export function sessionStream(sessionId) {
64
48
  if (!sessionId)
65
49
  throw new Error("sessionId is required");
66
- if (!incarnation || incarnation.includes("/"))
67
- throw new Error("incarnation must be non-empty and contain no '/'");
68
- return `${SESSION_STREAM_PREFIX}${sessionId}/${incarnation}`;
69
- }
70
- /** The stream of a session as stored now. */
71
- export function streamOfSession(session) {
72
- return sessionStream(session.id, session.streamIncarnation ?? LEGACY_INCARNATION);
73
- }
74
- /** The session id and incarnation of a session stream, or undefined for other streams. */
75
- export function parseSessionStream(stream) {
76
- if (!stream.startsWith(SESSION_STREAM_PREFIX))
77
- return undefined;
78
- const rest = stream.slice(SESSION_STREAM_PREFIX.length);
79
- const slash = rest.lastIndexOf("/");
80
- if (slash <= 0 || slash === rest.length - 1)
81
- return undefined;
82
- return { sessionId: rest.slice(0, slash), incarnation: rest.slice(slash + 1) };
50
+ return `${SESSION_STREAM_PREFIX}${sessionId}`;
83
51
  }
84
- export const WORK_AVAILABLE = Object.freeze({
85
- type: "work_available",
86
- });
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The Tenant's access policy (Host feature `subject-tokens`): the roles subject tokens are
3
+ * minted for, what a publishable key grants alone (`anon`), and how long tokens live. One
4
+ * validated JSON document in the Tenant settings; a Tenant that never set one has no roles,
5
+ * so nothing can be minted.
6
+ *
7
+ * A token's permissions are resolved from the policy on every request, so editing a role
8
+ * changes outstanding tokens at their next request and removing one ends them.
9
+ */
10
+ import { type AccessPolicy, type RoleLimits, type TokenScope } from "@nylorun/core/contracts";
11
+ import type { Tx } from "../store/types.js";
12
+ export declare const ACCESS_POLICY_SETTING = "access.policy";
13
+ export declare function readPolicy(t: Tx): Promise<AccessPolicy>;
14
+ export declare function writePolicy(t: Tx, policy: AccessPolicy): Promise<void>;
15
+ /** What a token may do right now: its role narrowed by what the mint asked for. */
16
+ export interface EffectiveAccess {
17
+ readonly role: string;
18
+ readonly scopes: ReadonlySet<TokenScope>;
19
+ readonly agents: ReadonlySet<string> | "*";
20
+ readonly limits?: RoleLimits;
21
+ }
22
+ /** Agents both lists allow; `"*"` allows every agent. */
23
+ export declare function intersectAgents(a: readonly string[] | "*", b: readonly string[] | "*" | undefined): ReadonlySet<string> | "*";
24
+ /**
25
+ * The role's permissions narrowed to `scopes` and `agents`, or undefined when the role no
26
+ * longer exists.
27
+ */
28
+ export declare function resolveRole(policy: AccessPolicy, role: string, scopes: readonly TokenScope[], agents?: readonly string[]): EffectiveAccess | undefined;
29
+ /** True when `agentId` is allowed by `agents`. */
30
+ export declare function agentAllowed(agents: ReadonlySet<string> | "*", agentId: string): boolean;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The Tenant's access policy (Host feature `subject-tokens`): the roles subject tokens are
3
+ * minted for, what a publishable key grants alone (`anon`), and how long tokens live. One
4
+ * validated JSON document in the Tenant settings; a Tenant that never set one has no roles,
5
+ * so nothing can be minted.
6
+ *
7
+ * A token's permissions are resolved from the policy on every request, so editing a role
8
+ * changes outstanding tokens at their next request and removing one ends them.
9
+ */
10
+ import { AccessPolicySchema, DEFAULT_ACCESS_POLICY, } from "@nylorun/core/contracts";
11
+ export const ACCESS_POLICY_SETTING = "access.policy";
12
+ export async function readPolicy(t) {
13
+ const stored = await t.getSetting(ACCESS_POLICY_SETTING);
14
+ if (stored === undefined)
15
+ return DEFAULT_ACCESS_POLICY;
16
+ // Only `writePolicy` stores it, after validation.
17
+ return AccessPolicySchema.parse(JSON.parse(stored));
18
+ }
19
+ export async function writePolicy(t, policy) {
20
+ await t.putSetting(ACCESS_POLICY_SETTING, JSON.stringify(AccessPolicySchema.parse(policy)));
21
+ }
22
+ /** Agents both lists allow; `"*"` allows every agent. */
23
+ export function intersectAgents(a, b) {
24
+ if (b === undefined || b === "*")
25
+ return a === "*" ? "*" : new Set(a);
26
+ if (a === "*")
27
+ return new Set(b);
28
+ const allowed = new Set(a);
29
+ return new Set(b.filter((agent) => allowed.has(agent)));
30
+ }
31
+ /**
32
+ * The role's permissions narrowed to `scopes` and `agents`, or undefined when the role no
33
+ * longer exists.
34
+ */
35
+ export function resolveRole(policy, role, scopes, agents) {
36
+ const entry = Object.hasOwn(policy.roles, role)
37
+ ? policy.roles[role]
38
+ : undefined;
39
+ if (!entry)
40
+ return undefined;
41
+ const allowed = new Set(entry.scopes);
42
+ return {
43
+ role,
44
+ scopes: new Set(scopes.filter((scope) => allowed.has(scope))),
45
+ agents: intersectAgents(entry.agents, agents),
46
+ ...(entry.limits ? { limits: entry.limits } : {}),
47
+ };
48
+ }
49
+ /** True when `agentId` is allowed by `agents`. */
50
+ export function agentAllowed(agents, agentId) {
51
+ return agents === "*" || agents.has(agentId);
52
+ }
@@ -1,153 +1,14 @@
1
- import type { IncomingMessage } from "node:http";
2
- import { type RegisterExecutorsRequestSchema } from "@nylorun/core/contracts";
3
- import { type ExecutorRecord } from "../core/executors.js";
4
1
  import { type SandboxRouteDeps } from "../core/sandbox-routes.js";
5
- import { type AuthScope, type TenantContext } from "./context.js";
6
- type RegisterExecutorsRequest = ReturnType<typeof RegisterExecutorsRequestSchema.parse>;
2
+ import { type TenantContext } from "./context.js";
3
+ import type { DeliveryScope } from "./delivery-token.js";
7
4
  /** Session and Action lookups for the sandbox route handlers. */
8
5
  export declare function sandboxRouteDeps(ctx: TenantContext): SandboxRouteDeps;
9
- /** `GET /v1/actions`: the pending Actions of the executor's agent. */
10
- export declare function listPendingActions(ctx: TenantContext, executor: ExecutorRecord): Promise<{
11
- actions: ({
12
- actionId: string;
13
- sessionId: string;
14
- turnId: string;
15
- agentId: string;
16
- manifestHash: string;
17
- implementationVersion: string;
18
- input: unknown;
19
- context: Record<string, unknown>;
20
- status: "cancelled" | "claimed" | "completed" | "pending" | "uncertain";
21
- generation: number;
22
- claimId: string | null;
23
- leaseExpiresAt: string | null;
24
- agent?: {
25
- id: string;
26
- path: string;
27
- delegationId?: string | undefined;
28
- } | undefined;
29
- kind: "tool";
30
- capabilityId: string;
31
- toolName: string;
32
- inputSchema?: Record<string, unknown> | undefined;
33
- outputSchema?: Record<string, unknown> | undefined;
34
- } | {
35
- actionId: string;
36
- sessionId: string;
37
- turnId: string;
38
- agentId: string;
39
- manifestHash: string;
40
- implementationVersion: string;
41
- input: unknown;
42
- context: Record<string, unknown>;
43
- status: "cancelled" | "claimed" | "completed" | "pending" | "uncertain";
44
- generation: number;
45
- claimId: string | null;
46
- leaseExpiresAt: string | null;
47
- agent?: {
48
- id: string;
49
- path: string;
50
- delegationId?: string | undefined;
51
- } | undefined;
52
- kind: "tool";
53
- path: string;
54
- key: string;
55
- inputSchema?: Record<string, unknown> | undefined;
56
- outputSchema?: Record<string, unknown> | undefined;
57
- } | {
58
- actionId: string;
59
- sessionId: string;
60
- turnId: string;
61
- agentId: string;
62
- manifestHash: string;
63
- implementationVersion: string;
64
- input: unknown;
65
- context: Record<string, unknown>;
66
- status: "cancelled" | "claimed" | "completed" | "pending" | "uncertain";
67
- generation: number;
68
- claimId: string | null;
69
- leaseExpiresAt: string | null;
70
- agent?: {
71
- id: string;
72
- path: string;
73
- delegationId?: string | undefined;
74
- } | undefined;
75
- kind: "hook";
76
- hook: {
77
- at: "after" | "before";
78
- scope: "step" | "turn";
79
- capabilityIds: string[];
80
- };
81
- } | {
82
- actionId: string;
83
- sessionId: string;
84
- turnId: string;
85
- agentId: string;
86
- manifestHash: string;
87
- implementationVersion: string;
88
- input: unknown;
89
- context: Record<string, unknown>;
90
- status: "cancelled" | "claimed" | "completed" | "pending" | "uncertain";
91
- generation: number;
92
- claimId: string | null;
93
- leaseExpiresAt: string | null;
94
- agent?: {
95
- id: string;
96
- path: string;
97
- delegationId?: string | undefined;
98
- } | undefined;
99
- kind: "fn";
100
- path: string;
101
- key: string;
102
- } | {
103
- actionId: string;
104
- sessionId: string;
105
- turnId: string;
106
- agentId: string;
107
- manifestHash: string;
108
- implementationVersion: string;
109
- input: unknown;
110
- context: Record<string, unknown>;
111
- status: "cancelled" | "claimed" | "completed" | "pending" | "uncertain";
112
- generation: number;
113
- claimId: string | null;
114
- leaseExpiresAt: string | null;
115
- agent?: {
116
- id: string;
117
- path: string;
118
- delegationId?: string | undefined;
119
- } | undefined;
120
- kind: "verify";
121
- path: string;
122
- key: string;
123
- })[];
6
+ /** `POST /v1/actions/:id/sandbox/:tool`: a sandbox tool call for the Action being delivered. */
7
+ export declare function actionSandboxTool(ctx: TenantContext, scope: DeliveryScope, actionId: string, toolName: string, body: () => Promise<unknown>, signal: AbortSignal): Promise<{
8
+ kind: "completed";
9
+ output: unknown;
10
+ } | {
11
+ kind: "failed";
12
+ code: string;
13
+ message: string;
124
14
  }>;
125
- /** `POST /v1/actions/:id/sandbox/:tool`: a claim-scoped sandbox tool call. */
126
- export declare function actionSandboxTool(ctx: TenantContext, scope: AuthScope, actionId: string, toolName: string, request: IncomingMessage): Promise<import("../sandbox/tools.js").SandboxToolOutcome>;
127
- /** `POST /v1/actions/:id/claim` and `/heartbeat`; anything else under the Action is a 404. */
128
- export declare function updateAction(ctx: TenantContext, scope: AuthScope, actionId: string, method: string | undefined, operation: string | undefined, body: unknown): Promise<unknown>;
129
- /** `GET /v1/executors`. */
130
- export declare function listExecutors(ctx: TenantContext): {
131
- executors: {
132
- agentId: string;
133
- implementationVersion: string;
134
- manifestHash?: string | undefined;
135
- connected: boolean;
136
- updatedAt: string;
137
- }[];
138
- };
139
- /** `PUT /v1/executors`: persist the batch, then update the registry and end rotated streams. */
140
- export declare function registerExecutors(ctx: TenantContext, principalId: string, body: RegisterExecutorsRequest): Promise<{
141
- executors: {
142
- agentId: string;
143
- implementationVersion: string;
144
- rotated: boolean;
145
- replacedBy?: "different-credential" | undefined;
146
- }[];
147
- }>;
148
- /** `DELETE /v1/executors/:agentId`: forget the executor and end its streams. */
149
- export declare function deleteExecutor(ctx: TenantContext, agentId: string): Promise<{
150
- agentId: string;
151
- deleted: boolean;
152
- }>;
153
- export {};
@@ -1,21 +1,7 @@
1
- /**
2
- * The Action service and the executor registry: discover pending Actions, claim, heartbeat,
3
- * claim-scoped sandbox tool calls, and executor registration, listing and removal.
4
- *
5
- * A claim or heartbeat locks the Action's session and commits in one transaction; the
6
- * registry is updated only after the executors table commits.
7
- *
8
- * Later waves: Wave 2 / Y reads the executor `connected` flag from the work stream instead
9
- * of `executorStreams`.
10
- */
11
- import { randomUUID } from "node:crypto";
12
- import { ActionClaimRequestSchema, ActionHeartbeatRequestSchema, } from "@nylorun/core/contracts";
13
- import { assertExecutorCredential, hashToken, } from "../core/executors.js";
14
1
  import { SandboxRouteError, handleActionSandboxTool, } from "../core/sandbox-routes.js";
15
- import { lockedSession, sandboxLookup, sessionOf, } from "./context.js";
16
- import { fail, readBody, requestAborted } from "./http.js";
17
2
  import { scoped } from "./auth.js";
18
- import { endExecutorStreams, executorConnected } from "./live.js";
3
+ import { sandboxLookup, sessionOf } from "./context.js";
4
+ import { fail } from "./http.js";
19
5
  /** Session and Action lookups for the sandbox route handlers. */
20
6
  export function sandboxRouteDeps(ctx) {
21
7
  return {
@@ -27,19 +13,13 @@ export function sandboxRouteDeps(ctx) {
27
13
  getAction: (id) => ctx.store.tx((t) => t.get("actions", id)),
28
14
  };
29
15
  }
30
- /** `GET /v1/actions`: the pending Actions of the executor's agent. */
31
- export async function listPendingActions(ctx, executor) {
32
- return {
33
- actions: await ctx.store.tx((t) => t.pendingActions(executor.agentId)),
34
- };
35
- }
36
- /** `POST /v1/actions/:id/sandbox/:tool`: a claim-scoped sandbox tool call. */
37
- export async function actionSandboxTool(ctx, scope, actionId, toolName, request) {
16
+ /** `POST /v1/actions/:id/sandbox/:tool`: a sandbox tool call for the Action being delivered. */
17
+ export async function actionSandboxTool(ctx, scope, actionId, toolName, body, signal) {
38
18
  const action = (await ctx.store.tx((t) => t.get("actions", actionId))) ??
39
19
  fail(404, "Action not found");
40
20
  scoped(scope, action);
41
21
  try {
42
- return await handleActionSandboxTool(sandboxRouteDeps(ctx), actionId, toolName, await readBody(request), requestAborted(request));
22
+ return await handleActionSandboxTool(sandboxRouteDeps(ctx), actionId, toolName, await body(), signal, { generation: scope.generation });
43
23
  }
44
24
  catch (error) {
45
25
  if (error instanceof SandboxRouteError)
@@ -47,145 +27,3 @@ export async function actionSandboxTool(ctx, scope, actionId, toolName, request)
47
27
  throw error;
48
28
  }
49
29
  }
50
- /** `POST /v1/actions/:id/claim` and `/heartbeat`; anything else under the Action is a 404. */
51
- export function updateAction(ctx, scope, actionId, method, operation, body) {
52
- return ctx.store.tx(async (t) => {
53
- const found = (await t.get("actions", actionId)) ??
54
- fail(404, "Action not found");
55
- scoped(scope, found);
56
- const s = await lockedSession(t, found.sessionId);
57
- // Read again under the session lock.
58
- const action = (await t.get("actions", actionId)) ??
59
- fail(404, "Action not found");
60
- if (method === "POST" && operation === "claim") {
61
- ActionClaimRequestSchema.parse(body);
62
- if (action.status !== "pending" ||
63
- s.status === "cancelled" ||
64
- s.activeTurnId !== action.turnId)
65
- fail(409, "Action unavailable");
66
- action.status = "claimed";
67
- action.generation++;
68
- action.claimId = randomUUID();
69
- action.leaseExpiresAt = new Date(Date.now() + (ctx.config.leaseMs ?? 30000)).toISOString();
70
- await t.put("actions", actionId, action);
71
- await t.event(action.sessionId, action.turnId, "action.claimed", {
72
- actionId,
73
- generation: action.generation,
74
- ...(action.agent ? { agent: action.agent } : {}),
75
- });
76
- return {
77
- action,
78
- claimId: action.claimId,
79
- generation: action.generation,
80
- leaseExpiresAt: action.leaseExpiresAt,
81
- };
82
- }
83
- if (method === "POST" && operation === "heartbeat") {
84
- const beat = ActionHeartbeatRequestSchema.parse(body);
85
- if (s.activeTurnId !== action.turnId ||
86
- action.status !== "claimed" ||
87
- action.claimId !== beat.claimId ||
88
- action.generation !== beat.generation ||
89
- Date.parse(action.leaseExpiresAt) <= Date.now())
90
- fail(409, "Stale or expired claim");
91
- action.leaseExpiresAt = new Date(Date.now() + (ctx.config.leaseMs ?? 30000)).toISOString();
92
- await t.put("actions", actionId, action);
93
- return { leaseExpiresAt: action.leaseExpiresAt };
94
- }
95
- return fail(404, "Route not found");
96
- });
97
- }
98
- /** `GET /v1/executors`. */
99
- export function listExecutors(ctx) {
100
- return {
101
- executors: ctx.registry.list().map((e) => ({
102
- agentId: e.agentId,
103
- implementationVersion: e.implementationVersion,
104
- ...(e.manifestHash === undefined ? {} : { manifestHash: e.manifestHash }),
105
- connected: executorConnected(ctx.live, e.tokenHash),
106
- updatedAt: e.updatedAt,
107
- })),
108
- };
109
- }
110
- /** `PUT /v1/executors`: persist the batch, then update the registry and end rotated streams. */
111
- export async function registerExecutors(ctx, principalId, body) {
112
- const { store, registry } = ctx;
113
- // Credential rules are shared with startup validation, which throws plainly; on the
114
- // wire a rejected registration is a bad request, not a server fault.
115
- const applicationHashes = await store.tx((t) => t.applicationTokenHashes());
116
- try {
117
- for (const executor of body.executors)
118
- assertExecutorCredential(executor, applicationHashes);
119
- }
120
- catch (error) {
121
- fail(400, error instanceof Error ? error.message : String(error));
122
- }
123
- if (new Set(body.executors.map((e) => e.agentId)).size !==
124
- body.executors.length ||
125
- new Set(body.executors.map((e) => e.token)).size !== body.executors.length)
126
- fail(400, "Executor registrations must be unique per agent and token");
127
- const updatedAt = new Date().toISOString();
128
- const records = body.executors.map((executor) => ({
129
- agentId: executor.agentId,
130
- implementationVersion: executor.implementationVersion,
131
- ...(executor.manifestHash === undefined
132
- ? {}
133
- : { manifestHash: executor.manifestHash }),
134
- tokenHash: hashToken(executor.token),
135
- persisted: true,
136
- updatedAt,
137
- principalId,
138
- }));
139
- for (const record of records) {
140
- const collision = registry.find(record.tokenHash);
141
- if (collision && collision.agentId !== record.agentId)
142
- fail(409, "Executor tokens must be unique");
143
- // A token hash may not appear in both principals and executors (D3).
144
- if (applicationHashes.includes(record.tokenHash))
145
- fail(400, "Executor tokens require independent credentials and an agent id");
146
- }
147
- // Persist the whole batch first; the in-memory registry must never run ahead of the store.
148
- await store.tx(async (t) => {
149
- for (const record of records)
150
- await t.putExecutor({
151
- agentId: record.agentId,
152
- tokenHash: record.tokenHash,
153
- implementationVersion: record.implementationVersion,
154
- ...(record.manifestHash === undefined
155
- ? {}
156
- : { manifestHash: record.manifestHash }),
157
- principalId: record.principalId,
158
- updatedAt,
159
- });
160
- });
161
- const results = records.map((record) => {
162
- const previous = registry.get(record.agentId);
163
- const { rotated, previousHash } = registry.upsert(record);
164
- if (rotated && previousHash)
165
- endExecutorStreams(ctx.live, previousHash);
166
- const replacedByDifferent = previous?.principalId !== undefined &&
167
- previous.principalId !== principalId;
168
- if (replacedByDifferent)
169
- ctx.config.logger.warn("executor registration replaced by different application credential", {
170
- agentId: record.agentId,
171
- previousPrincipalId: previous.principalId,
172
- });
173
- return {
174
- agentId: record.agentId,
175
- implementationVersion: record.implementationVersion,
176
- rotated,
177
- ...(replacedByDifferent
178
- ? { replacedBy: "different-credential" }
179
- : {}),
180
- };
181
- });
182
- return { executors: results };
183
- }
184
- /** `DELETE /v1/executors/:agentId`: forget the executor and end its streams. */
185
- export async function deleteExecutor(ctx, agentId) {
186
- const existing = ctx.registry.get(agentId) ?? fail(404, "Executor not found");
187
- await ctx.store.tx((t) => t.deleteExecutor(agentId));
188
- ctx.registry.remove(agentId);
189
- endExecutorStreams(ctx.live, existing.tokenHash);
190
- return { agentId, deleted: true };
191
- }
@@ -8,8 +8,13 @@ import type { Session, TenantContext } from "./context.js";
8
8
  export declare function advance(ctx: TenantContext, id: string, signal: AbortSignal): Promise<AdvanceResult>;
9
9
  /**
10
10
  * Takeover (§10.5 step 2, §11.4), inside the transaction that took ownership: the dead
11
- * owner's `invoking` effects become `uncertain` and are never invoked again. When one belongs
11
+ * owner's `invoking` effects become `uncertain` and are never invoked again, except model
12
+ * calls when the gate recovers them (P1.2) and remote MCP calls when the Tool Gate does (F4.1
13
+ * G3): those stay `invoking`, and the replay re-sends them. When one belongs
12
14
  * to the active turn, the session becomes `uncertain` too, with an `effect.uncertain` event.
13
15
  * Mutates and writes `s`. Returns the effect ids it marked.
14
16
  */
15
- export declare function takeOver(t: Tx, s: Session): Promise<string[]>;
17
+ export declare function takeOver(t: Tx, s: Session, options?: {
18
+ recoversModelCalls?: boolean;
19
+ recoversMcpCalls?: boolean;
20
+ }): Promise<string[]>;