@nylorun/runtime 0.17.0-beta → 0.19.1-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 +155 -0
  2. package/README.md +89 -58
  3. package/dist/adapters/execution/restate.d.ts +1 -3
  4. package/dist/adapters/execution/restate.js +4 -31
  5. package/dist/adapters/replication/pgoutput.js +1 -1
  6. package/dist/adapters/sandbox/local.js +1 -1
  7. package/dist/adapters/sandbox/virtual.js +1 -1
  8. package/dist/api/ag-ui/routes.js +1 -1
  9. package/dist/api/ag-ui/translate.d.ts +1 -3
  10. package/dist/api/ag-ui/translate.js +2 -17
  11. package/dist/api/components.d.ts +4871 -2614
  12. package/dist/api/components.js +12 -11
  13. package/dist/api/gate/routes.d.ts +7 -8
  14. package/dist/api/gate/routes.js +58 -52
  15. package/dist/api/http/app.d.ts +6 -0
  16. package/dist/api/http/app.js +18 -14
  17. package/dist/api/http/define.d.ts +14 -2
  18. package/dist/api/http/define.js +34 -15
  19. package/dist/api/http/routes/access.d.ts +3 -3
  20. package/dist/api/http/routes/access.js +18 -17
  21. package/dist/api/http/routes/artifacts.js +2 -4
  22. package/dist/api/http/routes/files.d.ts +8 -0
  23. package/dist/api/http/routes/files.js +77 -0
  24. package/dist/api/http/routes/keys.d.ts +10 -0
  25. package/dist/api/http/routes/keys.js +64 -0
  26. package/dist/api/http/routes/me.js +5 -2
  27. package/dist/api/http/routes/reads.d.ts +66 -0
  28. package/dist/api/http/routes/reads.js +127 -0
  29. package/dist/api/http/routes/sandboxes.js +7 -4
  30. package/dist/api/http/routes/sessions.js +25 -8
  31. package/dist/api/http/routes/tenant.js +15 -18
  32. package/dist/api/http/routes/vaults.d.ts +10 -9
  33. package/dist/api/http/routes/vaults.js +27 -23
  34. package/dist/api/openapi.d.ts +10 -11
  35. package/dist/api/openapi.js +288 -63
  36. package/dist/artifacts/links.js +1 -1
  37. package/dist/core/flow-host.d.ts +44 -23
  38. package/dist/core/flow-host.js +88 -106
  39. package/dist/core/provider.d.ts +4 -1
  40. package/dist/core/provider.js +6 -1
  41. package/dist/core/sandbox-routes.d.ts +0 -9
  42. package/dist/core/sandbox-routes.js +2 -21
  43. package/dist/execution/memory.d.ts +4 -5
  44. package/dist/execution/memory.js +11 -25
  45. package/dist/execution/types.d.ts +3 -33
  46. package/dist/execution/types.js +2 -12
  47. package/dist/gates/http-tool.d.ts +86 -0
  48. package/dist/gates/http-tool.js +197 -0
  49. package/dist/gates/mcp-handler.d.ts +3 -0
  50. package/dist/gates/mcp-handler.js +1 -3
  51. package/dist/gates/meter.d.ts +2 -2
  52. package/dist/gates/meter.js +6 -0
  53. package/dist/gates/tenant-vaults.d.ts +5 -9
  54. package/dist/gates/tool-calls.d.ts +7 -15
  55. package/dist/gates/tool-calls.js +4 -4
  56. package/dist/gates/tool-client.d.ts +1 -1
  57. package/dist/gates/tool-client.js +23 -47
  58. package/dist/gates/tool-contract.d.ts +27 -39
  59. package/dist/gates/tool-contract.js +30 -21
  60. package/dist/gates/tool-gate.d.ts +25 -24
  61. package/dist/gates/tool-gate.js +11 -7
  62. package/dist/harness/calls.d.ts +10 -4
  63. package/dist/harness/calls.js +15 -6
  64. package/dist/harness/executors.d.ts +1 -1
  65. package/dist/harness/executors.js +17 -12
  66. package/dist/harness/main.d.ts +2 -2
  67. package/dist/harness/main.js +5 -13
  68. package/dist/harness/service.d.ts +4 -5
  69. package/dist/harness/service.js +9 -2
  70. package/dist/harness-api/in-process.js +2 -2
  71. package/dist/harness-api/record.d.ts +6 -3
  72. package/dist/harness-api/record.js +65 -72
  73. package/dist/harness-api/server.d.ts +1 -9
  74. package/dist/harness-api/server.js +19 -17
  75. package/dist/harness-api/start.js +1 -5
  76. package/dist/harness-api/workspace.js +2 -3
  77. package/dist/harness-api/ws-server.js +1 -1
  78. package/dist/host/app.d.ts +2 -9
  79. package/dist/host/app.js +15 -25
  80. package/dist/host/config-for.d.ts +1 -5
  81. package/dist/host/config-for.js +0 -3
  82. package/dist/host/config.d.ts +0 -5
  83. package/dist/host/create-host.d.ts +3 -33
  84. package/dist/host/create-host.js +18 -94
  85. package/dist/host/environment.d.ts +0 -3
  86. package/dist/host/environment.js +0 -9
  87. package/dist/host/gates.d.ts +4 -1
  88. package/dist/host/gates.js +3 -2
  89. package/dist/host/http.d.ts +1 -2
  90. package/dist/host/http.js +1 -7
  91. package/dist/host/index.d.ts +2 -2
  92. package/dist/host/index.js +2 -2
  93. package/dist/host/main.js +27 -34
  94. package/dist/host/operate.d.ts +29 -0
  95. package/dist/host/operate.js +223 -0
  96. package/dist/host/stack-config.d.ts +12 -12
  97. package/dist/host/stack-config.js +5 -32
  98. package/dist/index.d.ts +1 -1
  99. package/dist/index.js +1 -1
  100. package/dist/infra/workers.d.ts +1 -1
  101. package/dist/keys/keys.js +2 -2
  102. package/dist/management-openapi.json +6491 -0
  103. package/dist/mcp/connect.d.ts +8 -4
  104. package/dist/mcp/connect.js +6 -26
  105. package/dist/mcp/pool.d.ts +5 -9
  106. package/dist/mcp/pool.js +8 -19
  107. package/dist/mcp/snapshot.d.ts +8 -2
  108. package/dist/mcp/snapshot.js +29 -2
  109. package/dist/media.d.ts +1 -1
  110. package/dist/media.js +1 -1
  111. package/dist/model/pi-model.js +3 -0
  112. package/dist/openapi.json +9372 -17030
  113. package/dist/reads/access.d.ts +4 -0
  114. package/dist/reads/access.js +12 -0
  115. package/dist/reads/cursor.d.ts +11 -0
  116. package/dist/reads/cursor.js +80 -0
  117. package/dist/reads/types.d.ts +25 -0
  118. package/dist/reads/types.js +1 -0
  119. package/dist/sandbox/manager.d.ts +16 -0
  120. package/dist/sandbox/manager.js +40 -0
  121. package/dist/sandbox/session-sandbox.js +13 -3
  122. package/dist/sandbox/skills.d.ts +13 -0
  123. package/dist/sandbox/skills.js +46 -0
  124. package/dist/sandbox/types.d.ts +2 -2
  125. package/dist/store/ownership.d.ts +9 -0
  126. package/dist/store/ownership.js +13 -0
  127. package/dist/store/postgres/connect.d.ts +2 -0
  128. package/dist/store/postgres/connect.js +29 -0
  129. package/dist/store/postgres/drizzle/0011_key_roles.sql +3 -0
  130. package/dist/store/postgres/drizzle/0012_session_reads.sql +8 -0
  131. package/dist/store/postgres/drizzle/0013_definition_files.sql +16 -0
  132. package/dist/store/postgres/drizzle/0014_action_endpoints_removed.sql +2 -0
  133. package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
  134. package/dist/store/postgres/reads.d.ts +3 -0
  135. package/dist/store/postgres/reads.js +272 -0
  136. package/dist/store/postgres/schema.d.ts +294 -489
  137. package/dist/store/postgres/schema.js +41 -76
  138. package/dist/store/postgres/store.js +51 -154
  139. package/dist/store/postgres/tenant.d.ts +34 -2
  140. package/dist/store/postgres/tenant.js +16 -32
  141. package/dist/store/types.d.ts +27 -96
  142. package/dist/store/types.js +0 -1
  143. package/dist/streams/control.d.ts +0 -2
  144. package/dist/streams/control.js +0 -5
  145. package/dist/streams/types.d.ts +1 -10
  146. package/dist/tenant/advance.d.ts +3 -3
  147. package/dist/tenant/advance.js +10 -7
  148. package/dist/tenant/auth.d.ts +14 -9
  149. package/dist/tenant/auth.js +24 -28
  150. package/dist/tenant/cause.d.ts +3 -3
  151. package/dist/tenant/cause.js +1 -1
  152. package/dist/tenant/commands.d.ts +2 -18
  153. package/dist/tenant/commands.js +35 -172
  154. package/dist/tenant/context.d.ts +14 -24
  155. package/dist/tenant/definition-files.d.ts +24 -0
  156. package/dist/tenant/definition-files.js +103 -0
  157. package/dist/tenant/effects.d.ts +18 -20
  158. package/dist/tenant/effects.js +56 -119
  159. package/dist/tenant/ephemeral.d.ts +7 -6
  160. package/dist/tenant/ephemeral.js +3 -11
  161. package/dist/tenant/host-token.js +1 -1
  162. package/dist/tenant/jwt.js +1 -1
  163. package/dist/tenant/module.d.ts +1 -4
  164. package/dist/tenant/module.js +3 -12
  165. package/dist/tenant/operator-keys.d.ts +34 -8
  166. package/dist/tenant/operator-keys.js +50 -18
  167. package/dist/tenant/outbound.d.ts +10 -1
  168. package/dist/tenant/outbound.js +44 -27
  169. package/dist/tenant/paths.d.ts +1 -1
  170. package/dist/tenant/paths.js +1 -3
  171. package/dist/tenant/principals.d.ts +11 -2
  172. package/dist/tenant/principals.js +27 -8
  173. package/dist/tenant/run-token.js +3 -3
  174. package/dist/tenant/runtime.d.ts +4 -6
  175. package/dist/tenant/runtime.js +19 -18
  176. package/dist/tenant/sandboxes.d.ts +4 -1
  177. package/dist/tenant/sandboxes.js +11 -0
  178. package/dist/tenant/scheduler.d.ts +1 -5
  179. package/dist/tenant/scheduler.js +2 -5
  180. package/dist/tenant/session-streams.d.ts +2 -1
  181. package/dist/tenant/session-streams.js +39 -5
  182. package/dist/tenant/session.d.ts +8 -41
  183. package/dist/tenant/session.js +0 -37
  184. package/dist/tenant/sessions.js +16 -8
  185. package/dist/tenant/signing-keys.d.ts +3 -3
  186. package/dist/tenant/signing-keys.js +5 -5
  187. package/dist/tenant/skill-tool.d.ts +20 -0
  188. package/dist/tenant/skill-tool.js +72 -0
  189. package/dist/tenant/slim.d.ts +1 -1
  190. package/dist/tenant/slim.js +1 -1
  191. package/dist/tenant/status.js +7 -12
  192. package/dist/tenant/store-pg.d.ts +15 -0
  193. package/dist/tenant/store-pg.js +7 -3
  194. package/dist/tenant/streams.d.ts +3 -10
  195. package/dist/tenant/streams.js +1 -24
  196. package/dist/tenant/sweep.js +0 -5
  197. package/dist/tenant/transcript.d.ts +2 -2
  198. package/dist/tenant/transcript.js +2 -2
  199. package/dist/tenant/types.d.ts +15 -22
  200. package/dist/tenant/worker.d.ts +1 -3
  201. package/dist/tenant/worker.js +3 -8
  202. package/dist/vault/sources.d.ts +7 -1
  203. package/dist/vault/sources.js +22 -11
  204. package/dist/version.d.ts +1 -1
  205. package/dist/version.js +1 -1
  206. package/package.json +4 -4
  207. package/dist/admin-openapi.json +0 -904
  208. package/dist/api/http/routes/actions.d.ts +0 -8
  209. package/dist/api/http/routes/actions.js +0 -75
  210. package/dist/api/http/routes/endpoints.d.ts +0 -7
  211. package/dist/api/http/routes/endpoints.js +0 -100
  212. package/dist/host/admin-api.d.ts +0 -30
  213. package/dist/host/admin-api.js +0 -140
  214. package/dist/mcp/stdio.d.ts +0 -17
  215. package/dist/mcp/stdio.js +0 -82
  216. package/dist/tenant/actions.d.ts +0 -14
  217. package/dist/tenant/actions.js +0 -29
  218. package/dist/tenant/delivery-token.d.ts +0 -41
  219. package/dist/tenant/delivery-token.js +0 -104
  220. package/dist/tenant/delivery.d.ts +0 -62
  221. package/dist/tenant/delivery.js +0 -481
  222. package/dist/tenant/endpoints.d.ts +0 -67
  223. package/dist/tenant/endpoints.js +0 -90
@@ -14,7 +14,7 @@
14
14
  * error `fn` threw.
15
15
  * 2. **Session-scoped writes lock the session row first.** `lockSession` takes
16
16
  * a row lock (`SELECT … FOR UPDATE`) held until the transaction ends. Effect
17
- * intent and outcome, Action transitions, checkpoint settlement and event
17
+ * intent and outcome, checkpoint settlement and event
18
18
  * writes for one session are serialized through it. Idempotency comparisons
19
19
  * (`canonical(...)`) run inside the same locked transaction. `event` and the
20
20
  * ownership methods take the lock themselves.
@@ -25,7 +25,7 @@
25
25
  * generation, `streams/basin.ts`), and the cursor is
26
26
  * `base64url("<sessionId>:<seq>")` (see `record/cursor.ts`).
27
27
  * 4. **No external I/O inside `fn`.** No model, tool, MCP, sandbox, Restate or
28
- * S2 call, and no `fetch`, runs inside a transaction. Wakes and deliveries go
28
+ * S2 call, and no `fetch`, runs inside a transaction. Wakes go
29
29
  * through `afterCommit`, and events are delivered to commit listeners after
30
30
  * commit (seam rule 1 and 2).
31
31
  * 5. **No nested transactions.** Calling `store.tx` from inside `fn` rejects.
@@ -45,29 +45,28 @@
45
45
  * ## Typed queries, no scans
46
46
  *
47
47
  * There is no generic table scan. Every read the Runtime needs is a typed
48
- * method on `Tx` (`sessionsWithStatus`, `expiredDeliveries`, `pendingActions`,
49
- * `effectsForTurn`, `linkedSessions`, `counts`, …) that an implementation can
50
- * back with an index, and principals, endpoints, vaults and Tenant settings
48
+ * method on `Tx` (`sessionsWithStatus`, `effectsForTurn`, `linkedSessions`,
49
+ * `counts`, …) that an implementation can back with an index, and principals,
50
+ * vaults and Tenant settings
51
51
  * have their own methods rather than raw SQL outside the store. Session history
52
52
  * is not read from the store: every event is written to the record (Postgres
53
53
  * `nylorun_streams.session_events`) in its transaction, the stream relay
54
54
  * (`streams/relay/`) feeds Durable Streams from it, and history and SSE read
55
55
  * them there (`tenant/session-streams.ts`).
56
56
  */
57
+ import type { KeyRole } from "@nylorun/core/compatibility";
57
58
  import type { RecordReader } from "../streams/relay/types.js";
58
- import type { Action, EventPayload, EventType, LiveEvent, SandboxEvent, SandboxEventPayload, SandboxEventType, SandboxKind, SandboxPodDesired, SandboxPodObserved, SessionEventOf } from "@nylorun/core/contracts";
59
+ import type { EventPayload, EventType, LiveEvent, SandboxEvent, SandboxEventPayload, SandboxEventType, SandboxKind, SandboxPodDesired, SandboxPodObserved, SessionEventOf } from "@nylorun/core/contracts";
59
60
  import type { SandboxManifest } from "@nylorun/core/define";
60
61
  import type { HostEffect } from "@nylorun/harness/run";
61
- import type { ArtifactRow, ArtifactVersionRow, ModelBudgetRow, ModelUsageRow, PrincipalRow, ToolCrossingRow, SigningKeyRow, VaultAuditRow, VaultCredentialRow, VaultIdempotencyRow, VaultRow, OAuthPendingRow } from "./postgres/schema.js";
62
+ import type { ArtifactRow, ArtifactVersionRow, DefinitionFileRow, ModelBudgetRow, ModelUsageRow, ModelUsageWrite, PrincipalRow, ToolCrossingRow, SigningKeyRow, VaultAuditRow, VaultCredentialRow, VaultIdempotencyRow, VaultRow, OAuthPendingRow } from "./postgres/schema.js";
62
63
  /** Tables holding JSON documents keyed by `id`. */
63
- export type DocTable = "definitions" | "sessions" | "commands" | "effects" | "actions" | "sandboxes" | "links";
64
+ export type DocTable = "definitions" | "sessions" | "commands" | "effects" | "sandboxes" | "links";
64
65
  export declare const DOC_TABLES: readonly DocTable[];
65
66
  /** Session lifecycle (architecture §10.2). */
66
67
  export type SessionStatus = "idle" | "runnable" | "running" | "waiting" | "paused" | "uncertain" | "completed" | "failed" | "cancelled";
67
68
  /** Effect states (§10.3), plus the flow host's `queued` and `cancelled`. */
68
69
  export type EffectStatus = "pending" | "invoking" | "completed" | "uncertain" | "queued" | "cancelled";
69
- export type ActionStatus = Action["status"];
70
- export type ActionKind = Action["kind"];
71
70
  export type EffectKind = HostEffect["kind"];
72
71
  /**
73
72
  * Ownership of a session by a Worker (§10.6). Read with the session; written
@@ -103,8 +102,6 @@ export interface EffectDoc {
103
102
  request: HostEffect;
104
103
  status: EffectStatus | (string & {});
105
104
  }
106
- /** Actions are stored as the wire `Action`. Indexed: `sessionId`, `turnId`, `agentId`, `status`, `kind`, `deadlineAt`. */
107
- export type ActionDoc = Action;
108
105
  /** A workflow → agent session link, keyed by the linked agent session id. Indexed: `workflowSessionId`. */
109
106
  export interface LinkDoc {
110
107
  workflowSessionId: string;
@@ -192,59 +189,13 @@ export interface BasinGenerations {
192
189
  retired: number[];
193
190
  }
194
191
  export type CommitListener = (commit: Commit) => void;
195
- /**
196
- * An Action endpoint: the URL the Runtime delivers one agent's Actions to, and what recent
197
- * deliveries and the last ping say about it.
198
- */
199
- export interface EndpointRow {
200
- agentId: string;
201
- url: string;
202
- implementationVersion: string;
203
- manifestHash?: string;
204
- timeoutMs: number;
205
- maxConcurrent: number;
206
- /** Application principal that registered it, when known. */
207
- principalId?: string;
208
- lastDeliveryAt?: string;
209
- lastSuccessAt?: string;
210
- lastErrorCode?: string;
211
- lastErrorMessage?: string;
212
- consecutiveFailures: number;
213
- /** What the endpoint reported serving on the last ping. */
214
- servedImplementationVersion?: string;
215
- servedManifestHash?: string;
216
- createdAt: string;
217
- updatedAt: string;
218
- }
219
- /** The registration part of an endpoint, as `putEndpoint` writes it. */
220
- export type EndpointRegistrationRow = Pick<EndpointRow, "agentId" | "url" | "implementationVersion" | "manifestHash" | "timeoutMs" | "maxConcurrent" | "principalId" | "updatedAt">;
221
- /** One observation about an endpoint (`recordEndpointHealth`). */
222
- export type EndpointHealthUpdate =
223
- /** A delivery was answered. */
224
- {
225
- kind: "success";
226
- at: string;
227
- }
228
- /** A delivery failed: not reached, refused, or lost. */
229
- | {
230
- kind: "failure";
231
- at: string;
232
- code: string;
233
- message: string;
234
- }
235
- /** A ping was answered with what the endpoint serves. */
236
- | {
237
- kind: "served";
238
- implementationVersion: string;
239
- manifestHash?: string;
240
- };
241
192
  /**
242
193
  * The rows of the typed tables, inferred from the tables Drizzle defines
243
194
  * (`store/postgres/schema.ts`): a principal, a vault and its credentials (secrets sealed in
244
195
  * `bytea` columns, never inside a JSON body), the vault's audit and idempotency records, the
245
196
  * Tenant's signing keys, a model call in the usage ledger and a model budget.
246
197
  */
247
- export type { ArtifactRow, ArtifactVersionRow, ModelBudgetRow, ModelUsageRow, ToolCrossingRow, PrincipalRow, SigningKeyRow, VaultAuditRow, VaultCredentialRow, VaultIdempotencyRow, VaultRow, OAuthPendingRow, };
198
+ export type { ArtifactRow, ArtifactVersionRow, DefinitionFileRow, ModelBudgetRow, ModelUsageRow, ModelUsageWrite, ToolCrossingRow, PrincipalRow, SigningKeyRow, VaultAuditRow, VaultCredentialRow, VaultIdempotencyRow, VaultRow, OAuthPendingRow, };
248
199
  /** Which rows of the usage ledger a total covers. */
249
200
  export interface ModelUsageQuery {
250
201
  scope: "tenant" | "agent" | "turn";
@@ -265,18 +216,11 @@ export interface SessionEffectFilter {
265
216
  turnId?: string;
266
217
  statuses?: readonly (EffectStatus | (string & {}))[];
267
218
  }
268
- export interface SessionActionFilter {
269
- /** Only this turn. Absent means every turn. */
270
- turnId?: string;
271
- statuses?: readonly ActionStatus[];
272
- }
273
219
  /** Counts used by Tenant status, `summary` and `drain`. */
274
220
  export interface StoreCounts {
275
221
  sessions: number;
276
222
  /** Sessions `running` or `runnable`. */
277
223
  runningSessions: number;
278
- /** Actions `pending` or `delivering`. */
279
- pendingActions: number;
280
224
  /** Effects `uncertain`. */
281
225
  uncertainEffects: number;
282
226
  sandboxes: number;
@@ -439,18 +383,6 @@ export interface Tx {
439
383
  fromSeq?: number;
440
384
  limit?: number;
441
385
  }): Promise<SandboxEvent[]>;
442
- /** One agent's `pending` actions, delivered when its endpoint is registered. */
443
- pendingActions(agentId: string): Promise<ActionDoc[]>;
444
- /** How many of one agent's actions are `delivering` (Action endpoints). */
445
- deliveringCount(agentId: string): Promise<number>;
446
- /** `pending` actions of agents that have an Action endpoint, by id. */
447
- pendingActionsWithEndpoint(limit: number): Promise<ActionDoc[]>;
448
- /** `delivering` actions whose deadline is at or before `now`, earliest first. */
449
- expiredDeliveries(now: Date, limit: number): Promise<ActionDoc[]>;
450
- actionsForSession(sessionId: string, filter?: SessionActionFilter): Promise<ActionDoc[]>;
451
- actionsWithStatus(statuses: readonly ActionStatus[], filter?: {
452
- kinds?: readonly ActionKind[];
453
- }): Promise<ActionDoc[]>;
454
386
  /** `invoking` effects of one session; takeover turns them `uncertain`. */
455
387
  invokingEffects<T extends EffectDoc = EffectDoc>(sessionId: string): Promise<T[]>;
456
388
  effectsForSession<T extends EffectDoc = EffectDoc>(sessionId: string, filter?: SessionEffectFilter): Promise<T[]>;
@@ -464,29 +396,19 @@ export interface Tx {
464
396
  basinGenerations(): Promise<BasinGenerations>;
465
397
  /** Forgets a retired generation once its basin is deleted. */
466
398
  forgetRetiredGeneration(generation: number): Promise<void>;
467
- listEndpoints(): Promise<EndpointRow[]>;
468
- getEndpoint(agentId: string): Promise<EndpointRow | undefined>;
469
- /**
470
- * Inserts or updates by `agentId`, keeping `createdAt`. Health is kept, except that a new
471
- * `url` starts with none.
472
- */
473
- putEndpoint(row: EndpointRegistrationRow): Promise<void>;
474
- deleteEndpoint(agentId: string): Promise<void>;
475
- /** Records one observation. Nothing happens when the endpoint does not exist. */
476
- recordEndpointHealth(agentId: string, update: EndpointHealthUpdate): Promise<void>;
477
399
  /** Rejects when the id or token hash already exists. */
478
400
  insertPrincipal(row: PrincipalRow): Promise<void>;
479
401
  principalByTokenHash(tokenHash: string): Promise<PrincipalRow | undefined>;
480
402
  principalById(id: string): Promise<PrincipalRow | undefined>;
481
- applicationTokenHashes(): Promise<string[]>;
482
403
  /** Every principal, ordered by id. */
483
404
  listPrincipals(): Promise<PrincipalRow[]>;
484
405
  /**
485
- * Creates application principal `id` with `tokenHash`, or gives an existing one this hash
486
- * (and `createdAt`): its previous key stops authenticating. Rejects when another principal
487
- * holds the hash.
406
+ * Creates principal `id` with `tokenHash` and `role` (default `application`), or gives an
407
+ * existing one this hash (and `createdAt`): its previous key stops authenticating. Rejects
408
+ * when another principal holds the hash, and with `PrincipalRoleConflict` when `id` holds
409
+ * another role.
488
410
  */
489
- putPrincipal(id: string, tokenHash: string, createdAt: string): Promise<PrincipalRow>;
411
+ putPrincipal(id: string, tokenHash: string, createdAt: string, role?: KeyRole): Promise<PrincipalRow>;
490
412
  /** Deletes principal `id`; false when there was none. */
491
413
  deletePrincipal(id: string): Promise<boolean>;
492
414
  /** Rejects on a duplicate id, or a second `host` vault. */
@@ -545,7 +467,7 @@ export interface Tx {
545
467
  setSigningKeyState(id: string, from: SigningKeyRow["state"], to: SigningKeyRow["state"], at: string): Promise<boolean>;
546
468
  countSigningKeys(): Promise<number>;
547
469
  /** Appends a row, setting `duplicate` when one with the same `effectKey` exists; returns it. */
548
- recordModelUsage(row: Omit<ModelUsageRow, "duplicate">): Promise<ModelUsageRow>;
470
+ recordModelUsage(row: ModelUsageWrite): Promise<ModelUsageRow>;
549
471
  modelUsageTotals(query: ModelUsageQuery): Promise<ModelUsageTotals>;
550
472
  /** Every budget, ordered by scope, then scope id. */
551
473
  listModelBudgets(): Promise<ModelBudgetRow[]>;
@@ -607,16 +529,25 @@ export interface Tx {
607
529
  artifactContentShas(scope: "sessions" | "all" | {
608
530
  artifactId: string;
609
531
  }): Promise<string[]>;
532
+ /** The definition file `sha256` (`sha256:<hex>`) names, once its bytes are stored. */
533
+ definitionFile(sha256: string): Promise<DefinitionFileRow | undefined>;
534
+ /** Records a stored definition file; false when the Tenant held it already. */
535
+ insertDefinitionFile(row: DefinitionFileRow): Promise<boolean>;
536
+ /** Those of `shas` the Tenant holds. */
537
+ heldDefinitionFiles(shas: readonly string[]): Promise<Set<string>>;
538
+ /** Records that version `manifestHash` of definition `agentId` names each of `shas`. */
539
+ insertDefinitionFileUses(agentId: string, manifestHash: string, shas: readonly string[]): Promise<void>;
610
540
  getSetting(key: string): Promise<string | undefined>;
611
541
  putSetting(key: string, value: string): Promise<void>;
612
542
  /**
613
543
  * Deletes Tenant state by scope, in this transaction:
614
- * - `sessions`: sessions, commands, effects, actions, links, the artifacts of sessions (their blobs are the caller's to delete) and the
544
+ * - `sessions`: sessions, commands, effects, links, the artifacts of sessions (their blobs are the caller's to delete) and the
615
545
  * Tenant's record rows and log heads. The Tenant moves to the next basin
616
546
  * generation and the current one is retired, so session ids it frees start again in an
617
547
  * empty basin;
618
548
  * - `sandboxes`: sandbox records, sandbox resources and their lifecycle streams;
619
- * - `all`: both, plus definitions, Action endpoints, user vaults with their credentials,
549
+ * - `all`: both, plus definitions (and which definition files they use),
550
+ * user vaults with their credentials,
620
551
  * the model usage ledger, the model budgets and Tenant-wide artifacts. The host vault, principals, signing keys,
621
552
  * settings, audit and vault idempotency rows stay.
622
553
  */
@@ -3,7 +3,6 @@ export const DOC_TABLES = [
3
3
  "sessions",
4
4
  "commands",
5
5
  "effects",
6
- "actions",
7
6
  "sandboxes",
8
7
  "links",
9
8
  ];
@@ -6,8 +6,6 @@
6
6
  import { type DurableStreams } from "./types.js";
7
7
  /** Appends a `session.cancel` signal: the process running the session's advance aborts it. */
8
8
  export declare function signalCancel(streams: DurableStreams, basin: string, sessionId: string, turnId?: string): Promise<void>;
9
- /** Appends an `action.resolved` signal: a run held for the Action goes on with its outcome. */
10
- export declare function signalActionResolved(streams: DurableStreams, basin: string, sessionId: string, actionId: string): Promise<void>;
11
9
  /**
12
10
  * Appends a `sessions.reset` signal to the old generation's basin: every process with the
13
11
  * Tenant open moves its readers to `generation` and ends the streams of deleted sessions.
@@ -15,11 +15,6 @@ export async function signalCancel(streams, basin, sessionId, turnId) {
15
15
  };
16
16
  await streams.append(basin, CONTROL_STREAM, [signal]);
17
17
  }
18
- /** Appends an `action.resolved` signal: a run held for the Action goes on with its outcome. */
19
- export async function signalActionResolved(streams, basin, sessionId, actionId) {
20
- const signal = { type: "action.resolved", sessionId, actionId };
21
- await streams.append(basin, CONTROL_STREAM, [signal]);
22
- }
23
18
  /**
24
19
  * Appends a `sessions.reset` signal to the old generation's basin: every process with the
25
20
  * Tenant open moves its readers to `generation` and ends the streams of deleted sessions.
@@ -138,13 +138,4 @@ export interface SessionsResetSignal {
138
138
  type: "sessions.reset";
139
139
  generation?: number;
140
140
  }
141
- /**
142
- * An Action of `sessionId` has its outcome (F6.2): the process whose harness holds the
143
- * session's run while the Action is pending passes it on (`effect.resolved`).
144
- */
145
- export interface ActionResolvedSignal {
146
- type: "action.resolved";
147
- sessionId: string;
148
- actionId: string;
149
- }
150
- export type ControlSignal = SessionCancelSignal | SessionsResetSignal | ActionResolvedSignal;
141
+ export type ControlSignal = SessionCancelSignal | SessionsResetSignal;
@@ -9,12 +9,12 @@ export declare function advance(ctx: TenantContext, id: string, signal: AbortSig
9
9
  /**
10
10
  * Takeover (§10.5 step 2, §11.4), inside the transaction that took ownership: the dead
11
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
+ * calls when the gate recovers them (P1.2) and remote MCP and HTTP tool calls when the Tool
13
+ * Gate does (F4.1 G3): those stay `invoking`, and the replay re-sends them. When one belongs
14
14
  * to the active turn, the session becomes `uncertain` too, with an `effect.uncertain` event.
15
15
  * Mutates and writes `s`. Returns the effect ids it marked.
16
16
  */
17
17
  export declare function takeOver(t: Tx, s: Session, options?: {
18
18
  recoversModelCalls?: boolean;
19
- recoversMcpCalls?: boolean;
19
+ recoversToolCalls?: boolean;
20
20
  }): Promise<string[]>;
@@ -42,8 +42,7 @@
42
42
  import { runDurable, runFlowDurable, } from "@nylorun/harness/run";
43
43
  import { aggregateWaits, cancelSiblingWork, isWorkflowManifest, wakeLinkedWorkflow, } from "../core/flow-host.js";
44
44
  import { isOwnershipLost, ownedTx } from "../store/ownership.js";
45
- import { linkedAgentOutput } from "./session.js";
46
- import { isRemoteMcpEffect, recoversMcpCalls, recoversModelCalls } from "./effects.js";
45
+ import { isGateToolEffect, recoversModelCalls, recoversToolCalls } from "./effects.js";
47
46
  import { slimModelEffects } from "./slim.js";
48
47
  import { applyUpdates, leanState, transcriptShadow, withTranscript, } from "./history.js";
49
48
  import { command } from "./commands.js";
@@ -232,7 +231,7 @@ async function takeOwnership(ctx, id) {
232
231
  if (taken.takeover)
233
232
  await takeOver(t, s, {
234
233
  recoversModelCalls: recoversModelCalls(ctx),
235
- recoversMcpCalls: recoversMcpCalls(ctx),
234
+ recoversToolCalls: recoversToolCalls(ctx),
236
235
  });
237
236
  if (!s.checkpoint || !["running", "runnable"].includes(s.status)) {
238
237
  await t.releaseOwnership(id, lease.owner, lease.epoch);
@@ -246,8 +245,8 @@ async function takeOwnership(ctx, id) {
246
245
  /**
247
246
  * Takeover (§10.5 step 2, §11.4), inside the transaction that took ownership: the dead
248
247
  * owner's `invoking` effects become `uncertain` and are never invoked again, except model
249
- * calls when the gate recovers them (P1.2) and remote MCP calls when the Tool Gate does (F4.1
250
- * G3): those stay `invoking`, and the replay re-sends them. When one belongs
248
+ * calls when the gate recovers them (P1.2) and remote MCP and HTTP tool calls when the Tool
249
+ * Gate does (F4.1 G3): those stay `invoking`, and the replay re-sends them. When one belongs
251
250
  * to the active turn, the session becomes `uncertain` too, with an `effect.uncertain` event.
252
251
  * Mutates and writes `s`. Returns the effect ids it marked.
253
252
  */
@@ -257,7 +256,7 @@ export async function takeOver(t, s, options = {}) {
257
256
  // Still running at the gate, or finished there: the replay re-sends it (P1.2).
258
257
  if (options.recoversModelCalls && effect.request.kind === "model")
259
258
  continue;
260
- if (options.recoversMcpCalls && isRemoteMcpEffect(s, effect.request))
259
+ if (options.recoversToolCalls && isGateToolEffect(s, effect.request))
261
260
  continue;
262
261
  effect.status = "uncertain";
263
262
  effect.error ??= "The Worker running this effect stopped before its outcome was recorded";
@@ -390,7 +389,7 @@ async function wakeWorkflowOf(ctx, t, agent, turnId, type, payload) {
390
389
  agentSessionId: agent.id,
391
390
  turnId,
392
391
  output: type === "turn.completed"
393
- ? linkedAgentOutput(agent, payload.output)
392
+ ? payload.output ?? null
394
393
  : undefined,
395
394
  failed: type === "turn.failed",
396
395
  error: type === "turn.failed"
@@ -526,9 +525,13 @@ async function settle(ctx, lease, s, result, updates = [], cursor) {
526
525
  ? {
527
526
  interactions: (result.result.pending ?? []).map((call) => ({
528
527
  invocationId: call.invocationId,
528
+ ...(typeof call.callId === "string" ? { callId: call.callId } : {}),
529
529
  interaction: call.interaction,
530
530
  wait: call.wait,
531
531
  status: call.status,
532
+ // A flow's tool node that asked.
533
+ ...(typeof call.path === "string" ? { path: call.path } : {}),
534
+ ...(typeof call.toolName === "string" ? { toolName: call.toolName } : {}),
532
535
  })),
533
536
  }
534
537
  : result.status === "failed"
@@ -1,17 +1,27 @@
1
1
  /**
2
2
  * Tenant request authentication and authorization: a bearer token resolves to an application
3
- * principal, a trusted issuer's token or a delivery token; anything else is the opaque 404
3
+ * principal or a trusted issuer's token; anything else is the opaque 404
4
4
  * (D5). An application principal may act for a subject (`Nylorun-Subject`, `Nylorun-Scopes`);
5
5
  * an issuer token names its subject itself. `requireScopes` limits them to the routes their
6
6
  * scopes allow, decided from the route alone.
7
7
  *
8
8
  * Browsers: a request with `Origin` may carry an issuer token (CORS is the operator's proxy's),
9
- * never an application key or a delivery token, which are server secrets.
9
+ * never an application key, which is a server secret.
10
10
  */
11
11
  import type { IncomingMessage } from "node:http";
12
- import { type Action, type SubjectScope } from "@nylorun/core/contracts";
12
+ import { type SubjectScope } from "@nylorun/core/contracts";
13
13
  import type { AuthScope, SessionAccess, TenantContext } from "./context.js";
14
- export declare function authenticate(ctx: TenantContext, request: IncomingMessage): Promise<AuthScope>;
14
+ /**
15
+ * Which keys a route takes (protocol 8): an application key (alone or acting for a subject) on
16
+ * the Runtime API, a management key on the Management API. `/v1/me` takes both.
17
+ */
18
+ export interface KeyAccess {
19
+ readonly application: boolean;
20
+ readonly management: boolean;
21
+ }
22
+ /** Every route that does not say otherwise is a Runtime API route. */
23
+ export declare const RUNTIME_KEYS: KeyAccess;
24
+ export declare function authenticate(ctx: TenantContext, request: IncomingMessage, keys?: KeyAccess): Promise<AuthScope>;
15
25
  /**
16
26
  * What a subject needs for a route: any one of the scopes, nothing grants it (`never`), or
17
27
  * any caller may (`any`, public data such as the JWKS). Each route declares it
@@ -28,11 +38,6 @@ export declare function accessOf(scope: AuthScope): SessionAccess | undefined;
28
38
  export declare function ownerOf(scope: AuthScope): string | undefined;
29
39
  /** The application principal behind the request, acting for a subject or not. */
30
40
  export declare function requirePrincipal(scope: AuthScope): string;
31
- /**
32
- * The caller may act on `action`: only the delivery token minted for it. Whether its generation
33
- * is still the Action's is checked by each callback.
34
- */
35
- export declare function scoped(scope: AuthScope, action: Action): void;
36
41
  /** True when a token caller may reach `agentId` (its issuer's allowlist). Other callers always may. */
37
42
  export declare function mayUseAgent(scope: AuthScope, agentId: string): boolean;
38
43
  export declare function requireApplication(scope: AuthScope): string;
@@ -1,10 +1,9 @@
1
1
  import { SCOPES_HEADER, SUBJECT_HEADER, } from "@nylorun/core/compatibility";
2
- import { DELIVERY_TOKEN_TYPE, parseSubjectHeaders, } from "@nylorun/core/contracts";
2
+ import { parseSubjectHeaders } from "@nylorun/core/contracts";
3
3
  import { hashToken } from "../core/bearer.js";
4
4
  import { fail, failOpaque } from "./http.js";
5
- import { verifyDeliveryToken } from "./delivery-token.js";
6
5
  import { verifyIssuerToken } from "./issuers.js";
7
- import { looksLikeToken, tokenType } from "./jwt.js";
6
+ import { looksLikeToken } from "./jwt.js";
8
7
  const SUBJECT_INVALID = { code: "subject_invalid" };
9
8
  /** One header's value, or `undefined`; a header sent twice is refused. */
10
9
  function singleHeader(request, name) {
@@ -17,7 +16,9 @@ function singleHeader(request, name) {
17
16
  return fail(400, `${name} must be sent once`, SUBJECT_INVALID);
18
17
  return values[0];
19
18
  }
20
- export async function authenticate(ctx, request) {
19
+ /** Every route that does not say otherwise is a Runtime API route. */
20
+ export const RUNTIME_KEYS = { application: true, management: false };
21
+ export async function authenticate(ctx, request, keys = RUNTIME_KEYS) {
21
22
  const header = request.headers.authorization;
22
23
  const token = header?.startsWith("Bearer ") ? header.slice(7) : "";
23
24
  if (!token || !header?.startsWith("Bearer ")) {
@@ -26,16 +27,6 @@ export async function authenticate(ctx, request) {
26
27
  });
27
28
  return failOpaque();
28
29
  }
29
- if (looksLikeToken(token) && tokenType(token) === DELIVERY_TOKEN_TYPE) {
30
- // Delivery tokens come back from the application's server, never from a browser.
31
- if (request.headers.origin !== undefined)
32
- fail(403, "Delivery tokens are not accepted from browsers", { code: "origin_rejected" });
33
- const scope = await verifyDeliveryToken(ctx, token);
34
- if (singleHeader(request, SUBJECT_HEADER) !== undefined ||
35
- singleHeader(request, SCOPES_HEADER) !== undefined)
36
- fail(403, "A delivery token cannot act for a subject");
37
- return scope;
38
- }
39
30
  // A trusted issuer's token (Host feature `trusted-issuers`), from a server or a browser: its
40
31
  // unverified `iss` names an issuer of the identity file, and only that issuer verifies it.
41
32
  const issuer = ctx.config.issuers?.claimed(token);
@@ -68,6 +59,22 @@ export async function authenticate(ctx, request) {
68
59
  // Read only after authentication, so an unknown caller sees the opaque 404 either way.
69
60
  const subject = singleHeader(request, SUBJECT_HEADER);
70
61
  const scopes = singleHeader(request, SCOPES_HEADER);
62
+ // A management key acts as itself on the Management API; Studio's key does too on a route
63
+ // that takes no application key. Neither acts for a subject there.
64
+ const asManagement = principal.role === "management" || (principal.role === "studio" && !keys.application);
65
+ if (asManagement) {
66
+ if (!keys.management)
67
+ fail(403, "A management key reaches only the Management API (/v1/tenant/*)", {
68
+ code: "key_role_mismatch",
69
+ });
70
+ if (subject !== undefined || scopes !== undefined)
71
+ fail(403, "A management key acts as itself, never for a subject", SUBJECT_INVALID);
72
+ return { kind: "management", principalId: principal.id };
73
+ }
74
+ if (!keys.application)
75
+ fail(403, "This is the Management API: it takes a management key, not an application key", {
76
+ code: "key_role_mismatch",
77
+ });
71
78
  if (subject === undefined) {
72
79
  if (scopes !== undefined)
73
80
  fail(400, `${SCOPES_HEADER} requires ${SUBJECT_HEADER}`, SUBJECT_INVALID);
@@ -108,6 +115,9 @@ export function accessOf(scope) {
108
115
  switch (scope.kind) {
109
116
  case "application":
110
117
  return undefined;
118
+ // A management key reaches the Management API only: no session is its.
119
+ case "management":
120
+ return fail(404, "Not found");
111
121
  case "subject":
112
122
  return { owner: scope.subject };
113
123
  case "token":
@@ -118,9 +128,6 @@ export function accessOf(scope) {
118
128
  // A request with no credential, on a route that serves public data, owns nothing.
119
129
  case "anonymous":
120
130
  return fail(404, "Not found");
121
- // A delivery token reaches its Action's callbacks, never a session or vault.
122
- case "delivery":
123
- return fail(404, "Not found");
124
131
  default: {
125
132
  const unknown = scope;
126
133
  return fail(404, `Unknown credential ${String(unknown.kind)}`);
@@ -137,17 +144,6 @@ export function requirePrincipal(scope) {
137
144
  return scope.principalId;
138
145
  return fail(403, "Application credential required");
139
146
  }
140
- /**
141
- * The caller may act on `action`: only the delivery token minted for it. Whether its generation
142
- * is still the Action's is checked by each callback.
143
- */
144
- export function scoped(scope, action) {
145
- if (scope.kind === "delivery" &&
146
- scope.actionId === action.actionId &&
147
- scope.agentId === action.agentId)
148
- return;
149
- fail(403, "This delivery token is for another Action");
150
- }
151
147
  /** True when a token caller may reach `agentId` (its issuer's allowlist). Other callers always may. */
152
148
  export function mayUseAgent(scope, agentId) {
153
149
  return scope.kind !== "token" || scope.agents === "*" || scope.agents.has(agentId);
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Why the Host's Tenant could not be opened (tenancy.md §5). A cause is about the Tenant's
3
3
  * own data or files (its database is newer than this Runtime, its vault key is missing, the
4
- * database has an old layout): the Host fails readiness, names the cause in
5
- * `/v1/admin/status` and the logs, and answers Tenant requests with the opaque 404 until it is
4
+ * database has an old layout): the Host fails readiness, names the cause in the logs (and
5
+ * `nylorun-operate status` reads it from the database), and answers Tenant requests with the opaque 404 until it is
6
6
  * repaired and the Host restarted. A failure outside the Tenant (Postgres unreachable) is not
7
7
  * a cause: it is `TenantUnavailableError`, and the next request tries again.
8
8
  */
@@ -12,7 +12,7 @@ export type { TenantCause };
12
12
  export declare class TenantOpenError extends Error {
13
13
  readonly code: TenantCause["code"];
14
14
  readonly repair: string;
15
- /** The Tenant, when it was read before opening failed (`/v1/admin/status` names it). */
15
+ /** The Tenant, when it was read before opening failed. */
16
16
  envelope?: TenantEnvelope;
17
17
  constructor(fields: TenantCause);
18
18
  toCause(): TenantCause;
@@ -2,7 +2,7 @@
2
2
  export class TenantOpenError extends Error {
3
3
  code;
4
4
  repair;
5
- /** The Tenant, when it was read before opening failed (`/v1/admin/status` names it). */
5
+ /** The Tenant, when it was read before opening failed. */
6
6
  envelope;
7
7
  constructor(fields) {
8
8
  super(fields.message);
@@ -1,19 +1,3 @@
1
- import type { Action, ActionOutcome, LiveEvent, SessionCommand } from "@nylorun/core/contracts";
2
- import type { Tx } from "../store/types.js";
3
- import { type AuthScope, type Session, type TenantContext } from "./context.js";
4
- /** `outcome`, or a failed one when a tool's output does not match its stored output schema. */
5
- export declare function acceptedOutcome(action: Action, outcome: ActionOutcome): ActionOutcome;
1
+ import type { SessionCommand } from "@nylorun/core/contracts";
2
+ import { type AuthScope, type TenantContext } from "./context.js";
6
3
  export declare function command(ctx: TenantContext, id: string, input: SessionCommand, scope: AuthScope): Promise<unknown>;
7
- /**
8
- * Completes a delivered Action with `outcome`, in the caller's transaction, which
9
- * holds the lock of the Action's session `s`: the Action and its effect, the
10
- * `action.completed` event (and `loop.verified` or `loop.decided`), the wake that resumes the
11
- * turn, and queued workflow effects. The Action deliverer and the background-result callback
12
- * both record outcomes here. It sets `s.status`; the caller writes `s`.
13
- */
14
- export declare function recordActionOutcome(t: Tx, ctx: TenantContext, s: Session, action: Action, received: ActionOutcome, options?: {
15
- requestId?: string;
16
- }): Promise<{
17
- event: LiveEvent;
18
- receipt: Record<string, unknown>;
19
- }>;