@nylorun/runtime 0.12.0-beta → 0.14.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 (223) hide show
  1. package/CHANGELOG.md +361 -0
  2. package/README.md +69 -15
  3. package/dist/adapters/execution/restate.d.ts +7 -2
  4. package/dist/adapters/execution/restate.js +38 -8
  5. package/dist/adapters/replication/pgoutput.d.ts +16 -0
  6. package/dist/adapters/replication/pgoutput.js +169 -0
  7. package/dist/adapters/streams/s2.js +37 -20
  8. package/dist/admin-openapi.json +1071 -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 +7825 -0
  32. package/dist/api/components.js +83 -0
  33. package/dist/api/http/app.d.ts +18 -0
  34. package/dist/api/http/app.js +58 -0
  35. package/dist/api/http/body.d.ts +4 -0
  36. package/dist/api/http/body.js +37 -0
  37. package/dist/api/http/define.d.ts +39 -0
  38. package/dist/api/http/define.js +130 -0
  39. package/dist/api/http/respond.d.ts +9 -0
  40. package/dist/api/http/respond.js +44 -0
  41. package/dist/api/http/routes/access.d.ts +24 -0
  42. package/dist/api/http/routes/access.js +259 -0
  43. package/dist/api/http/routes/actions.d.ts +8 -0
  44. package/dist/api/http/routes/actions.js +76 -0
  45. package/dist/api/http/routes/endpoints.d.ts +7 -0
  46. package/dist/api/http/routes/endpoints.js +100 -0
  47. package/dist/api/http/routes/sessions.d.ts +3 -0
  48. package/dist/api/http/routes/sessions.js +205 -0
  49. package/dist/api/http/routes/tenant.d.ts +10 -0
  50. package/dist/api/http/routes/tenant.js +172 -0
  51. package/dist/api/http/routes/vaults.d.ts +8 -0
  52. package/dist/api/http/routes/vaults.js +167 -0
  53. package/dist/api/openapi.d.ts +19 -0
  54. package/dist/api/openapi.js +138 -0
  55. package/dist/api/route.d.ts +12 -0
  56. package/dist/api/route.js +4 -0
  57. package/dist/core/bearer.d.ts +8 -0
  58. package/dist/core/bearer.js +11 -0
  59. package/dist/core/flow-host.d.ts +7 -15
  60. package/dist/core/flow-host.js +19 -41
  61. package/dist/core/sandbox-routes.d.ts +12 -7
  62. package/dist/core/sandbox-routes.js +24 -25
  63. package/dist/execution/memory.d.ts +8 -5
  64. package/dist/execution/memory.js +33 -13
  65. package/dist/execution/types.d.ts +38 -6
  66. package/dist/execution/types.js +12 -3
  67. package/dist/host/admin-api.d.ts +22 -0
  68. package/dist/host/admin-api.js +178 -0
  69. package/dist/host/app.d.ts +34 -0
  70. package/dist/host/app.js +185 -0
  71. package/dist/host/config-for.d.ts +2 -0
  72. package/dist/host/config-for.js +1 -0
  73. package/dist/host/config.d.ts +11 -0
  74. package/dist/host/cors.d.ts +27 -0
  75. package/dist/host/cors.js +64 -0
  76. package/dist/host/create-host.d.ts +31 -3
  77. package/dist/host/create-host.js +168 -327
  78. package/dist/host/http.d.ts +14 -3
  79. package/dist/host/http.js +64 -23
  80. package/dist/host/main.js +55 -4
  81. package/dist/host/stack-config.d.ts +20 -0
  82. package/dist/host/stack-config.js +55 -0
  83. package/dist/mcp/pool.d.ts +19 -1
  84. package/dist/mcp/pool.js +60 -10
  85. package/dist/model/catalog.d.ts +2 -10
  86. package/dist/model/classify.d.ts +13 -0
  87. package/dist/model/classify.js +61 -0
  88. package/dist/model/models.d.ts +10 -1
  89. package/dist/model/models.js +14 -6
  90. package/dist/model/pi-model.d.ts +17 -2
  91. package/dist/model/pi-model.js +366 -222
  92. package/dist/openapi.json +25696 -0
  93. package/dist/sandbox/manager.d.ts +13 -1
  94. package/dist/sandbox/manager.js +8 -11
  95. package/dist/sandbox/resolve.d.ts +33 -0
  96. package/dist/sandbox/resolve.js +81 -0
  97. package/dist/sandbox/session-sandbox.d.ts +11 -0
  98. package/dist/sandbox/session-sandbox.js +51 -0
  99. package/dist/sandbox/share.d.ts +14 -2
  100. package/dist/sandbox/share.js +33 -3
  101. package/dist/sandbox/tenant-config.d.ts +37 -0
  102. package/dist/sandbox/tenant-config.js +51 -0
  103. package/dist/sandbox/tools.d.ts +7 -9
  104. package/dist/sandbox/tools.js +11 -3
  105. package/dist/sandbox/types.d.ts +8 -2
  106. package/dist/store/event.d.ts +19 -0
  107. package/dist/store/event.js +49 -0
  108. package/dist/store/memory.d.ts +20 -8
  109. package/dist/store/memory.js +265 -100
  110. package/dist/store/postgres/connect.d.ts +13 -0
  111. package/dist/store/postgres/connect.js +5 -0
  112. package/dist/store/postgres/migrations/003_subject_tokens.d.ts +8 -0
  113. package/dist/store/postgres/migrations/003_subject_tokens.js +42 -0
  114. package/dist/store/postgres/migrations/004_publishable_keys.d.ts +6 -0
  115. package/dist/store/postgres/migrations/004_publishable_keys.js +18 -0
  116. package/dist/store/postgres/migrations/005_action_endpoints.d.ts +6 -0
  117. package/dist/store/postgres/migrations/005_action_endpoints.js +32 -0
  118. package/dist/store/postgres/migrations/006_drop_executors.d.ts +11 -0
  119. package/dist/store/postgres/migrations/006_drop_executors.js +32 -0
  120. package/dist/store/postgres/migrations/007_event_time.d.ts +7 -0
  121. package/dist/store/postgres/migrations/007_event_time.js +14 -0
  122. package/dist/store/postgres/migrations/008_durable_streams.d.ts +13 -0
  123. package/dist/store/postgres/migrations/008_durable_streams.js +35 -0
  124. package/dist/store/postgres/migrations/index.d.ts +5 -1
  125. package/dist/store/postgres/migrations/index.js +22 -2
  126. package/dist/store/postgres/migrations/shared/index.d.ts +31 -0
  127. package/dist/store/postgres/migrations/shared/index.js +92 -0
  128. package/dist/store/postgres/record.d.ts +11 -0
  129. package/dist/store/postgres/record.js +64 -0
  130. package/dist/store/postgres/store.js +337 -95
  131. package/dist/store/postgres/tenants.js +14 -0
  132. package/dist/store/types.d.ts +176 -81
  133. package/dist/streams/basin.d.ts +18 -1
  134. package/dist/streams/basin.js +34 -2
  135. package/dist/streams/control.d.ts +18 -0
  136. package/dist/streams/control.js +29 -0
  137. package/dist/streams/memory.d.ts +3 -1
  138. package/dist/streams/memory.js +14 -8
  139. package/dist/streams/relay/core.d.ts +35 -0
  140. package/dist/streams/relay/core.js +366 -0
  141. package/dist/streams/relay/memory.d.ts +69 -0
  142. package/dist/streams/relay/memory.js +176 -0
  143. package/dist/streams/relay/types.d.ts +77 -0
  144. package/dist/streams/relay/types.js +7 -0
  145. package/dist/streams/types.d.ts +66 -36
  146. package/dist/streams/types.js +18 -53
  147. package/dist/tenant/access-policy.d.ts +30 -0
  148. package/dist/tenant/access-policy.js +52 -0
  149. package/dist/tenant/actions.d.ts +10 -149
  150. package/dist/tenant/actions.js +5 -167
  151. package/dist/tenant/advance.js +32 -0
  152. package/dist/tenant/auth.d.ts +19 -17
  153. package/dist/tenant/auth.js +90 -93
  154. package/dist/tenant/browser.d.ts +15 -0
  155. package/dist/tenant/browser.js +39 -0
  156. package/dist/tenant/commands.d.ts +18 -2
  157. package/dist/tenant/commands.js +121 -129
  158. package/dist/tenant/context.d.ts +75 -18
  159. package/dist/tenant/context.js +14 -10
  160. package/dist/tenant/delivery-token.d.ts +41 -0
  161. package/dist/tenant/delivery-token.js +105 -0
  162. package/dist/tenant/delivery.d.ts +62 -0
  163. package/dist/tenant/delivery.js +476 -0
  164. package/dist/tenant/effects.d.ts +5 -0
  165. package/dist/tenant/effects.js +138 -48
  166. package/dist/tenant/endpoints.d.ts +67 -0
  167. package/dist/tenant/endpoints.js +90 -0
  168. package/dist/tenant/ephemeral.d.ts +6 -0
  169. package/dist/tenant/ephemeral.js +60 -36
  170. package/dist/tenant/http.d.ts +8 -9
  171. package/dist/tenant/http.js +13 -27
  172. package/dist/tenant/jwt.d.ts +16 -0
  173. package/dist/tenant/jwt.js +50 -0
  174. package/dist/tenant/module.d.ts +3 -0
  175. package/dist/tenant/module.js +6 -12
  176. package/dist/tenant/outbound.d.ts +44 -0
  177. package/dist/tenant/outbound.js +127 -0
  178. package/dist/tenant/pool.d.ts +0 -2
  179. package/dist/tenant/pool.js +0 -18
  180. package/dist/tenant/reset.d.ts +0 -4
  181. package/dist/tenant/reset.js +0 -5
  182. package/dist/tenant/runtime.d.ts +10 -20
  183. package/dist/tenant/runtime.js +59 -42
  184. package/dist/tenant/scheduler.d.ts +7 -2
  185. package/dist/tenant/scheduler.js +7 -3
  186. package/dist/tenant/session-streams.d.ts +125 -0
  187. package/dist/tenant/session-streams.js +399 -0
  188. package/dist/tenant/sessions.d.ts +28 -4
  189. package/dist/tenant/sessions.js +118 -24
  190. package/dist/tenant/signing-keys.d.ts +55 -0
  191. package/dist/tenant/signing-keys.js +172 -0
  192. package/dist/tenant/slim.d.ts +8 -0
  193. package/dist/tenant/slim.js +21 -0
  194. package/dist/tenant/status.d.ts +0 -3
  195. package/dist/tenant/status.js +19 -12
  196. package/dist/tenant/streams.d.ts +60 -67
  197. package/dist/tenant/streams.js +140 -208
  198. package/dist/tenant/subject-limits.d.ts +16 -0
  199. package/dist/tenant/subject-limits.js +45 -0
  200. package/dist/tenant/sweep.d.ts +1 -10
  201. package/dist/tenant/sweep.js +29 -69
  202. package/dist/tenant/tokens.d.ts +14 -0
  203. package/dist/tenant/tokens.js +178 -0
  204. package/dist/tenant/transcript.d.ts +48 -5
  205. package/dist/tenant/transcript.js +58 -1
  206. package/dist/tenant/types.d.ts +29 -7
  207. package/dist/tenant/worker.d.ts +3 -1
  208. package/dist/tenant/worker.js +9 -3
  209. package/dist/vault/service.d.ts +18 -2
  210. package/dist/vault/service.js +57 -13
  211. package/dist/version.d.ts +1 -1
  212. package/dist/version.js +1 -1
  213. package/package.json +20 -8
  214. package/dist/core/executors.d.ts +0 -44
  215. package/dist/core/executors.js +0 -95
  216. package/dist/streams/relay.d.ts +0 -37
  217. package/dist/streams/relay.js +0 -254
  218. package/dist/tenant/live.d.ts +0 -92
  219. package/dist/tenant/live.js +0 -290
  220. package/dist/tenant/routes-tenant.d.ts +0 -12
  221. package/dist/tenant/routes-tenant.js +0 -137
  222. package/dist/tenant/routes.d.ts +0 -9
  223. package/dist/tenant/routes.js +0 -151
@@ -1,3 +1,43 @@
1
+ /**
2
+ * The Durable Streams seam (Durable Streams §8).
3
+ *
4
+ * Durable Streams deliver what the record holds. Each Tenant has one S2 basin per basin
5
+ * generation (`streams/basin.ts`); inside it:
6
+ *
7
+ * | Stream | Contents | Written by | Read by |
8
+ * | --- | --- | --- | --- |
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 |
11
+ *
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>")`.
16
+ *
17
+ * S2 is the supported implementation (`adapters/streams/s2.ts`, the only file
18
+ * importing the S2 SDK); `streams/memory.ts` is the in-memory fake.
19
+ *
20
+ * ## Guarantees every implementation keeps
21
+ *
22
+ * - **Sequence numbers.** Each stream numbers its records from 0 without gaps.
23
+ * The tail is the sequence the next appended record gets (0 for a stream
24
+ * that does not exist yet).
25
+ * - **Conditional appends.** With `matchSeq`, a batch is appended only when the
26
+ * tail equals `matchSeq`; otherwise nothing is written and the result is a
27
+ * `SeqMismatch` carrying the current tail. The relay appends a session
28
+ * stream with `matchSeq` set to the record row's sequence, so a retried
29
+ * append after an unacknowledged success is detected and never duplicated.
30
+ * - **Batches are atomic.** A batch is appended entirely or not at all.
31
+ * - **Reads resume.** `read` from `fromSeq` yields every record with
32
+ * `seq >= fromSeq` in order, with no gap between history and the live tail.
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).
38
+ * - **Records are JSON.** Bodies are JSON-serializable values; implementations
39
+ * encode them as they need.
40
+ */
1
41
  /** A record as read from a stream. */
2
42
  export interface StreamRecord<T = unknown> {
3
43
  seq: number;
@@ -48,9 +88,14 @@ export interface DurableStreams {
48
88
  tail(tenantId: string, stream: string): Promise<number>;
49
89
  /** Creates the Tenant's basin. Idempotent. */
50
90
  ensureTenant(tenantId: string): Promise<void>;
51
- /** Deletes the Tenant's basin and all its streams. Idempotent. */
52
- deleteTenant(tenantId: string): Promise<void>;
53
- /** Deletes one stream (an abandoned session incarnation). Idempotent. */
91
+ /**
92
+ * Deletes the basin and all its streams. Idempotent. With `allGenerations`, given a Tenant
93
+ * id, also every later generation's basin (Tenant deletion).
94
+ */
95
+ deleteTenant(tenantId: string, options?: {
96
+ allGenerations?: boolean;
97
+ }): Promise<void>;
98
+ /** Deletes one stream. Idempotent. */
54
99
  deleteStream(tenantId: string, stream: string): Promise<void>;
55
100
  /**
56
101
  * Names of the Tenant's streams starting with `prefix`, in name order,
@@ -66,50 +111,35 @@ export interface DurableStreams {
66
111
  probe?(signal: AbortSignal): Promise<void>;
67
112
  }
68
113
  export declare const SESSION_STREAM_PREFIX = "sessions/";
69
- /** `work_available` signals for executor connections. */
70
- export declare const WORK_STREAM = "tenant/work";
71
114
  /** `session.cancel` and `sessions.reset` signals for every process with the Tenant open. */
72
115
  export declare const CONTROL_STREAM = "tenant/control";
116
+ /** The stream of a session: `sessions/<sessionId>`. */
117
+ export declare function sessionStream(sessionId: string): string;
73
118
  /**
74
- * The incarnation of a session document written before incarnations existed.
75
- * Random incarnations are 12 characters, so never this.
76
- */
77
- export declare const LEGACY_INCARNATION = "0";
78
- /** A new session incarnation: 12 random base64url characters. */
79
- export declare function newStreamIncarnation(): string;
80
- /** The stream of one incarnation of a session: `sessions/<sessionId>/<incarnation>`. */
81
- export declare function sessionStream(sessionId: string, incarnation: string): string;
82
- /** The fields of a session document that name its stream. */
83
- export interface SessionStreamRef {
84
- id: string;
85
- /** Set when the session is created (`newStreamIncarnation`); never changed. */
86
- streamIncarnation?: string;
87
- }
88
- /** The stream of a session as stored now. */
89
- export declare function streamOfSession(session: SessionStreamRef): string;
90
- /** The session id and incarnation of a session stream, or undefined for other streams. */
91
- export declare function parseSessionStream(stream: string): {
92
- sessionId: string;
93
- incarnation: string;
94
- } | undefined;
95
- /**
96
- * Signals are not canonical events: they are appended without the outbox, and
97
- * a lost signal costs latency, not correctness.
119
+ * Signals are not events: they are appended directly, never recorded, and a
120
+ * lost signal costs latency, not correctness.
98
121
  */
99
- export interface WorkSignal {
100
- type: "work_available";
101
- }
102
122
  /** Ends the advance of `sessionId` on the process running it. */
103
123
  export interface SessionCancelSignal {
104
124
  type: "session.cancel";
105
125
  sessionId: string;
106
126
  }
107
127
  /**
108
- * Session streams were abandoned (a Tenant reset): each process checks its
109
- * session feeds and ends those whose session is gone or has a new incarnation.
128
+ * The Tenant was reset and moved to basin generation `generation`. Appended to the old
129
+ * generation's `tenant/control`: each process switches its readers to the new basin and
130
+ * ends the streams of sessions the reset deleted.
110
131
  */
111
132
  export interface SessionsResetSignal {
112
133
  type: "sessions.reset";
134
+ generation?: number;
135
+ }
136
+ /**
137
+ * A subject's tokens older than `epoch` were revoked: each process ends that subject's
138
+ * streams opened with them.
139
+ */
140
+ export interface SubjectRevokedSignal {
141
+ type: "subject.revoked";
142
+ subject: string;
143
+ epoch: number;
113
144
  }
114
- export type ControlSignal = SessionCancelSignal | SessionsResetSignal;
115
- export declare const WORK_AVAILABLE: Readonly<WorkSignal>;
145
+ export type ControlSignal = SessionCancelSignal | SessionsResetSignal | SubjectRevokedSignal;
@@ -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 {};