@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,14 +1,36 @@
1
- import { claimedFnVerifyActions, pendingAgentEffects, reconcilePendingAgentEffect, reofferFnVerifyClaim, } from "../core/flow-host.js";
1
+ /**
2
+ * The Tenant sweep (architecture §12.3, "Timers"): one self-re-arming pass per Tenant, armed
3
+ * with `DurableExecution.armSweep` when the Tenant opens. It replaces every in-process timer
4
+ * and startup scan the Tenant runtime used to keep:
5
+ *
6
+ * 1. **Linked agents.** Pending workflow `agent` effects whose linked turn already settled
7
+ * are completed and the workflow is woken (a crash between the two, or data written by an
8
+ * older Runtime). One transaction per effect, child session locked before its workflow.
9
+ * 2. **Orphaned sessions.** `running` or `runnable` sessions with no owner, or an owner whose
10
+ * lease expired, are woken with reason `recover`: a wake lost between commit and send, or
11
+ * a Worker that died mid-advance (the advance then takes over, §11.4).
12
+ * 3. **Sandboxes.** Idle sandboxes are stopped, records of compute this process no longer
13
+ * holds are marked stopped, and sandboxes whose session is gone are removed. Idle MCP
14
+ * connections are closed.
15
+ * 4. **Hooks.** Callbacks registered with `ctx.onSweep`.
16
+ * 5. **Deliveries.** Deliveries to Action endpoints whose deadline passed without an answer are
17
+ * lost (`delivery.ts` `loseAction`), and pending Actions of agents with an endpoint are sent
18
+ * again, in case a send was lost between a commit and the execution.
19
+ *
20
+ * Every step runs one transaction per session it changes (a linked agent and its workflow
21
+ * share one, child first), so the sweep follows the lock order in `store/types.ts`. A failing
22
+ * step does not stop the others; the first error is rethrown at the end so the execution
23
+ * reports it.
24
+ */
25
+ import { pendingAgentEffects, reconcilePendingAgentEffect, } from "../core/flow-host.js";
26
+ import { sweepDeliveries } from "./delivery.js";
2
27
  const BATCH = 100;
3
- export async function sweep(ctx, options = {}) {
28
+ export async function sweep(ctx) {
4
29
  if (ctx.closing || ctx.closed)
5
30
  return;
6
31
  const now = new Date();
7
32
  const steps = [
8
- ...(options.afterOpen
9
- ? [["reoffer", () => reofferFnVerifyClaims(ctx)]]
10
- : []),
11
- ["claims", () => expireClaims(ctx, now)],
33
+ ["deliveries", () => sweepDeliveries(ctx, now)],
12
34
  ["linked", () => reconcileLinkedAgents(ctx)],
13
35
  ["orphans", () => wakeOrphanedSessions(ctx, now)],
14
36
  [
@@ -18,6 +40,7 @@ export async function sweep(ctx, options = {}) {
18
40
  sessionExists: async (id) => !!(await ctx.store.tx((t) => t.get("sessions", id))),
19
41
  }),
20
42
  ],
43
+ ["mcp", () => ctx.mcp.sweep()],
21
44
  ...[...ctx.sweepHooks].map((hook) => ["hook", hook]),
22
45
  ];
23
46
  let failure;
@@ -38,69 +61,6 @@ export async function sweep(ctx, options = {}) {
38
61
  if (failure)
39
62
  throw failure.error;
40
63
  }
41
- /**
42
- * Expire lapsed Action claims: hooks, fn and verify are offered again, tools become
43
- * `uncertain`. One transaction per claim, so each locks only its own session.
44
- */
45
- export async function expireClaims(ctx, now = new Date()) {
46
- const { store } = ctx;
47
- const seen = new Set();
48
- let expiredCount = 0;
49
- for (;;) {
50
- const expired = (await store.tx((t) => t.expiredClaims(now, BATCH))).filter((action) => !seen.has(action.actionId));
51
- for (const found of expired) {
52
- seen.add(found.actionId);
53
- const changed = await store.tx(async (t) => {
54
- const s = await t.lockSession(found.sessionId);
55
- // Read again under the session lock: a heartbeat or result may have won.
56
- const action = await t.get("actions", found.actionId);
57
- if (!action ||
58
- action.status !== "claimed" ||
59
- Date.parse(action.leaseExpiresAt) > now.getTime())
60
- return false;
61
- if (action.kind === "hook" ||
62
- action.kind === "fn" ||
63
- action.kind === "verify") {
64
- action.status = "pending";
65
- action.claimId = null;
66
- action.leaseExpiresAt = null;
67
- await t.put("actions", action.actionId, action);
68
- t.signalWork();
69
- return true;
70
- }
71
- action.status = "uncertain";
72
- await t.put("actions", action.actionId, action);
73
- const effect = await t.get("effects", action.actionId);
74
- if (effect) {
75
- effect.status = "uncertain";
76
- await t.put("effects", action.actionId, effect);
77
- }
78
- if (s && s.status !== "cancelled" && s.activeTurnId === action.turnId) {
79
- s.status = "uncertain";
80
- await t.put("sessions", s.id, s);
81
- await t.event(s.id, s.activeTurnId, "action.uncertain", {
82
- actionId: action.actionId,
83
- ...(action.agent ? { agent: action.agent } : {}),
84
- });
85
- }
86
- return true;
87
- });
88
- if (changed)
89
- expiredCount += 1;
90
- }
91
- if (expired.length < BATCH)
92
- return expiredCount;
93
- }
94
- }
95
- /** Offer claimed `fn`/`verify` Actions again, one transaction each. Returns how many. */
96
- export async function reofferFnVerifyClaims(ctx) {
97
- const claimed = await ctx.store.tx((t) => claimedFnVerifyActions(t));
98
- let count = 0;
99
- for (const action of claimed)
100
- if (await ctx.store.tx((t) => reofferFnVerifyClaim(t, action.actionId)))
101
- count += 1;
102
- return count;
103
- }
104
64
  /** Settle pending workflow `agent` effects whose linked turn already finished. */
105
65
  export async function reconcileLinkedAgents(ctx) {
106
66
  const { store } = ctx;
@@ -0,0 +1,14 @@
1
+ import { type CreateTokenRequest, type CreateTokenResponse } from "@nylorun/core/contracts";
2
+ import type { AuthScope, TenantContext } from "./context.js";
3
+ export { looksLikeToken } from "./jwt.js";
4
+ export declare function mintToken(ctx: TenantContext, body: CreateTokenRequest): Promise<CreateTokenResponse>;
5
+ export type TokenScopeAuth = Extract<AuthScope, {
6
+ kind: "token";
7
+ }>;
8
+ /**
9
+ * Verifies a subject token. Returns the request's scope, answers `401 token_expired` for
10
+ * the cases a new token fixes, and the opaque 404 for everything else.
11
+ */
12
+ export declare function verifySubjectToken(ctx: TenantContext, raw: string): Promise<TokenScopeAuth>;
13
+ /** True when a token caller may reach `agentId`. Other callers always may. */
14
+ export declare function mayUseAgent(scope: AuthScope, agentId: string): boolean;
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Subject tokens (Host feature `subject-tokens`): ES256 JWTs minted by `POST /v1/tokens` with
3
+ * an application key, for one subject and one role of the access policy.
4
+ *
5
+ * Verification order matters: everything a forger controls is checked before any signature
6
+ * work, and nothing but the signature-verified cases reveals more than the opaque 404.
7
+ * Expired tokens, a stale revocation epoch, a revoked key or a removed role answer
8
+ * `401 token_expired`, so clients always react the same way: get a new token.
9
+ */
10
+ import { randomUUID } from "node:crypto";
11
+ import { errors, jwtVerify } from "jose";
12
+ import { isSubject, SUBJECT_TOKEN_AUDIENCE, SUBJECT_TOKEN_TYPE, subjectTokenIssuer, TOKEN_SCOPES, TOKEN_TTL_MIN_SECONDS, } from "@nylorun/core/contracts";
13
+ import { agentAllowed, readPolicy, resolveRole } from "./access-policy.js";
14
+ import { fail, failOpaque, HttpError } from "./http.js";
15
+ import { CLOCK_TOLERANCE_SECONDS, tokenKeyId } from "./jwt.js";
16
+ /** Allowed spread between `iat` and `exp` beyond the policy's longest lifetime. */
17
+ const LIFETIME_SLACK_SECONDS = 60;
18
+ export { looksLikeToken } from "./jwt.js";
19
+ /** The 401 every "get a new token" case answers. The reason is logged, never returned. */
20
+ function expired(ctx, reason, jti) {
21
+ ctx.config.logger.warn("subject token refused", {
22
+ reason,
23
+ ...(jti ? { jti } : {}),
24
+ });
25
+ throw new HttpError(401, "The subject token has expired or was revoked", { code: "token_expired" }, { "www-authenticate": 'Bearer error="invalid_token"' });
26
+ }
27
+ function rejected(ctx, reason) {
28
+ ctx.config.logger.warn("credential rejected", { reason });
29
+ return failOpaque();
30
+ }
31
+ export async function mintToken(ctx, body) {
32
+ const tenantId = ctx.config.tenantId;
33
+ const { access, epoch, ttl } = await ctx.store.tx(async (t) => {
34
+ const policy = await readPolicy(t);
35
+ const role = Object.hasOwn(policy.roles, body.role)
36
+ ? policy.roles[body.role]
37
+ : undefined;
38
+ if (!role)
39
+ return fail(400, `Role ${body.role} is not in the access policy`, {
40
+ code: "invalid_request",
41
+ });
42
+ const scopes = body.scopes ?? role.scopes;
43
+ const outside = scopes.filter((scope) => !role.scopes.includes(scope));
44
+ if (outside.length > 0)
45
+ fail(400, `Role ${body.role} does not allow ${outside.join(", ")}`, {
46
+ code: "invalid_request",
47
+ });
48
+ if (body.agents && role.agents !== "*") {
49
+ const allowed = new Set(role.agents);
50
+ const extra = body.agents.filter((agent) => !allowed.has(agent));
51
+ if (extra.length > 0)
52
+ fail(400, `Role ${body.role} does not allow agent ${extra.join(", ")}`, {
53
+ code: "invalid_request",
54
+ });
55
+ }
56
+ const maxTtl = policy.tokens.maxTtlSeconds;
57
+ const ttl = Math.min(body.ttlSeconds ?? maxTtl, maxTtl);
58
+ if (ttl < TOKEN_TTL_MIN_SECONDS)
59
+ fail(400, `ttlSeconds must be at least ${TOKEN_TTL_MIN_SECONDS}`);
60
+ return {
61
+ access: resolveRole(policy, body.role, scopes, body.agents),
62
+ epoch: await t.subjectEpoch(body.subject),
63
+ ttl,
64
+ };
65
+ });
66
+ const iat = Math.floor(Date.now() / 1000);
67
+ const jti = randomUUID();
68
+ const scopes = [...access.scopes];
69
+ const claims = {
70
+ tnt: tenantId,
71
+ role: body.role,
72
+ scp: scopes.join(" "),
73
+ ...(body.agents ? { agt: [...body.agents] } : {}),
74
+ epc: epoch,
75
+ };
76
+ // Signed by the keys service (F4.2): this process never holds the private key.
77
+ const { token, keyId } = await ctx.keys.sign({
78
+ typ: SUBJECT_TOKEN_TYPE,
79
+ claims: {
80
+ ...claims,
81
+ iss: subjectTokenIssuer(tenantId),
82
+ aud: SUBJECT_TOKEN_AUDIENCE,
83
+ sub: body.subject,
84
+ iat,
85
+ exp: iat + ttl,
86
+ jti,
87
+ },
88
+ });
89
+ const signer = { id: keyId };
90
+ ctx.config.logger.info("subject token minted", {
91
+ jti,
92
+ kid: signer.id,
93
+ role: body.role,
94
+ });
95
+ return {
96
+ token,
97
+ expiresAt: new Date((iat + ttl) * 1000).toISOString(),
98
+ subject: body.subject,
99
+ role: body.role,
100
+ scopes,
101
+ agents: access.agents === "*" ? "*" : [...access.agents],
102
+ keyId: signer.id,
103
+ };
104
+ }
105
+ /**
106
+ * Verifies a subject token. Returns the request's scope, answers `401 token_expired` for
107
+ * the cases a new token fixes, and the opaque 404 for everything else.
108
+ */
109
+ export async function verifySubjectToken(ctx, raw) {
110
+ const checked = tokenKeyId(raw, SUBJECT_TOKEN_TYPE);
111
+ if ("refused" in checked)
112
+ return rejected(ctx, checked.refused === "malformed" ? "token_malformed" : "token_header");
113
+ const { kid } = checked;
114
+ const { row, policy } = await ctx.store.tx(async (t) => ({
115
+ row: await t.signingKey(kid),
116
+ policy: await readPolicy(t),
117
+ }));
118
+ if (!row)
119
+ return rejected(ctx, "token_key_unknown");
120
+ const tenantId = ctx.config.tenantId;
121
+ let claims;
122
+ try {
123
+ const verified = await jwtVerify(raw, await ctx.signingKeys.publicKey(row), {
124
+ algorithms: ["ES256"],
125
+ issuer: subjectTokenIssuer(tenantId),
126
+ audience: SUBJECT_TOKEN_AUDIENCE,
127
+ typ: SUBJECT_TOKEN_TYPE,
128
+ clockTolerance: CLOCK_TOLERANCE_SECONDS,
129
+ requiredClaims: ["sub", "iat", "exp", "jti"],
130
+ });
131
+ claims = verified.payload;
132
+ }
133
+ catch (error) {
134
+ if (error instanceof errors.JWTExpired) {
135
+ // The signature was valid; only now may the answer differ from a forgery.
136
+ return expired(ctx, "expired");
137
+ }
138
+ return rejected(ctx, "token_invalid");
139
+ }
140
+ // The key was revoked after it signed: a new token uses the current key.
141
+ if (row.state === "revoked")
142
+ return expired(ctx, "key_revoked", claims.jti);
143
+ const scopeNames = typeof claims.scp === "string" ? claims.scp.split(" ").filter(Boolean) : [];
144
+ if (claims.tnt !== tenantId ||
145
+ !isSubject(claims.sub) ||
146
+ typeof claims.role !== "string" ||
147
+ typeof claims.epc !== "number" ||
148
+ !Number.isInteger(claims.epc) ||
149
+ claims.exp - claims.iat >
150
+ policy.tokens.maxTtlSeconds + LIFETIME_SLACK_SECONDS ||
151
+ scopeNames.some((name) => !TOKEN_SCOPES.includes(name)) ||
152
+ (claims.agt !== undefined &&
153
+ (!Array.isArray(claims.agt) ||
154
+ claims.agt.some((agent) => typeof agent !== "string"))))
155
+ return rejected(ctx, "token_claims");
156
+ const epoch = await ctx.store.tx((t) => t.subjectEpoch(claims.sub));
157
+ if (claims.epc !== epoch)
158
+ return expired(ctx, "revoked", claims.jti);
159
+ const access = resolveRole(policy, claims.role, scopeNames, claims.agt);
160
+ if (!access)
161
+ return expired(ctx, "role_removed", claims.jti);
162
+ if (access.scopes.size === 0)
163
+ return expired(ctx, "role_narrowed", claims.jti);
164
+ return {
165
+ kind: "token",
166
+ subject: claims.sub,
167
+ scopes: access.scopes,
168
+ agents: access.agents,
169
+ role: access.role,
170
+ ...(access.limits ? { limits: access.limits } : {}),
171
+ epoch,
172
+ expiresAt: claims.exp * 1000,
173
+ tokenId: claims.jti,
174
+ keyId: kid,
175
+ };
176
+ }
177
+ /** True when a token caller may reach `agentId`. Other callers always may. */
178
+ export function mayUseAgent(scope, agentId) {
179
+ return scope.kind !== "token" || agentAllowed(scope.agents, agentId);
180
+ }
@@ -14,7 +14,7 @@ export declare function toolIds(context: unknown): {
14
14
  * provider returns a `ModelCandidate` or a bare string; anything else writes no event.
15
15
  */
16
16
  export declare function assistantMessage(request: HostEffect, value: unknown): {
17
- agent: import("@nylorun/core").AgentRef;
17
+ agent: import("@nylorun/core/define").AgentRef;
18
18
  invocationId: string;
19
19
  text: string;
20
20
  toolCalls: {
@@ -22,6 +22,12 @@ export declare function assistantMessage(request: HostEffect, value: unknown): {
22
22
  name: string;
23
23
  input: unknown;
24
24
  }[];
25
+ model?: {
26
+ provider: string;
27
+ model: string;
28
+ } | undefined;
29
+ finishReason?: string | undefined;
30
+ usage?: object | undefined;
25
31
  } | {
26
32
  agent?: undefined;
27
33
  invocationId: string;
@@ -31,10 +37,47 @@ export declare function assistantMessage(request: HostEffect, value: unknown): {
31
37
  name: string;
32
38
  input: unknown;
33
39
  }[];
40
+ model?: {
41
+ provider: string;
42
+ model: string;
43
+ } | undefined;
44
+ finishReason?: string | undefined;
45
+ usage?: object | undefined;
46
+ } | undefined;
47
+ /**
48
+ * `context.compacted` for a model effect that summarized history (Model Calls §8): the
49
+ * engine replaced the older transcript with that summary.
50
+ */
51
+ export declare function contextCompacted(request: HostEffect, value: unknown): {
52
+ agent: import("@nylorun/core/define").AgentRef;
53
+ invocationId?: string | undefined;
54
+ trigger: "overflow" | "threshold";
55
+ tokensBefore: number;
56
+ tokensAfter: number;
57
+ } | {
58
+ agent?: undefined;
59
+ invocationId?: string | undefined;
60
+ trigger: "overflow" | "threshold";
61
+ tokensBefore: number;
62
+ tokensAfter: number;
63
+ } | undefined;
64
+ /** `model.failed` for a model effect that completed with a failure outcome (Model Calls §6.4). */
65
+ export declare function modelFailed(request: HostEffect, value: unknown): {
66
+ agent: import("@nylorun/core/define").AgentRef;
67
+ invocationId?: string | undefined;
68
+ code: import("@nylorun/core/define").ModelFailureCode;
69
+ message: string;
70
+ retryable: boolean;
71
+ } | {
72
+ agent?: undefined;
73
+ invocationId?: string | undefined;
74
+ code: import("@nylorun/core/define").ModelFailureCode;
75
+ message: string;
76
+ retryable: boolean;
34
77
  } | undefined;
35
78
  /** `tool.completed` for an MCP or sandbox tool: its output, or the tool error it reported. */
36
79
  export declare function toolCompleted(request: HostEffect, value: unknown): {
37
- agent: import("@nylorun/core").AgentRef;
80
+ agent: import("@nylorun/core/define").AgentRef;
38
81
  error: {
39
82
  code: string;
40
83
  message: string;
@@ -52,7 +95,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown): {
52
95
  capabilityId: string;
53
96
  toolName: string;
54
97
  } | {
55
- agent: import("@nylorun/core").AgentRef;
98
+ agent: import("@nylorun/core/define").AgentRef;
56
99
  error?: undefined;
57
100
  output: unknown;
58
101
  capabilityId: string;
@@ -66,7 +109,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown): {
66
109
  } | {
67
110
  callId: string;
68
111
  invocationId: string;
69
- agent: import("@nylorun/core").AgentRef;
112
+ agent: import("@nylorun/core/define").AgentRef;
70
113
  error: {
71
114
  code: string;
72
115
  message: string;
@@ -88,7 +131,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown): {
88
131
  } | {
89
132
  callId: string;
90
133
  invocationId: string;
91
- agent: import("@nylorun/core").AgentRef;
134
+ agent: import("@nylorun/core/define").AgentRef;
92
135
  error?: undefined;
93
136
  output: unknown;
94
137
  capabilityId: string;
@@ -1,3 +1,4 @@
1
+ import { isModelFailureOutcome } from "@nylorun/core/define";
1
2
  /** The model's tool call id and the harness invocation, for tool effects and tool actions. */
2
3
  export function toolIds(context) {
3
4
  const c = context;
@@ -32,7 +33,63 @@ export function assistantMessage(request, value) {
32
33
  }
33
34
  if (!text && toolCalls.length === 0)
34
35
  return undefined;
35
- return { invocationId, text, toolCalls, ...agentOf(request) };
36
+ const candidate = (typeof value === "object" && value !== null ? value : {});
37
+ const producer = candidate.evidence?.extras?.producer;
38
+ return {
39
+ invocationId,
40
+ text,
41
+ toolCalls,
42
+ ...(typeof producer?.provider === "string" && typeof producer.model === "string"
43
+ ? { model: { provider: producer.provider, model: producer.model } }
44
+ : {}),
45
+ ...(typeof candidate.finishReason === "string"
46
+ ? { finishReason: candidate.finishReason }
47
+ : {}),
48
+ ...(candidate.usage && typeof candidate.usage === "object"
49
+ ? { usage: candidate.usage }
50
+ : {}),
51
+ ...agentOf(request),
52
+ };
53
+ }
54
+ /**
55
+ * `context.compacted` for a model effect that summarized history (Model Calls §8): the
56
+ * engine replaced the older transcript with that summary.
57
+ */
58
+ export function contextCompacted(request, value) {
59
+ const compaction = request.context?.compaction;
60
+ if (!compaction || compaction.partial || isModelFailureOutcome(value))
61
+ return undefined;
62
+ const summary = typeof value === "string"
63
+ ? value
64
+ : Array.isArray(value?.output)
65
+ ? value.output
66
+ .map((block) => (block.type === "text" && typeof block.text === "string" ? block.text : ""))
67
+ .join("")
68
+ : "";
69
+ const invocationId = request.context?.invocationId;
70
+ const tokensBefore = Number(compaction.tokensBefore) || 0;
71
+ const keptTokens = Number(compaction.keptTokens) || 0;
72
+ return {
73
+ ...(typeof invocationId === "string" ? { invocationId } : {}),
74
+ trigger: compaction.trigger === "overflow" ? "overflow" : "threshold",
75
+ tokensBefore,
76
+ tokensAfter: keptTokens + Math.ceil(summary.trim().length / 4),
77
+ ...agentOf(request),
78
+ };
79
+ }
80
+ /** `model.failed` for a model effect that completed with a failure outcome (Model Calls §6.4). */
81
+ export function modelFailed(request, value) {
82
+ if (!isModelFailureOutcome(value))
83
+ return undefined;
84
+ const invocationId = request.context
85
+ ?.invocationId;
86
+ return {
87
+ ...(typeof invocationId === "string" ? { invocationId } : {}),
88
+ code: value.code,
89
+ message: value.message,
90
+ retryable: value.retryable,
91
+ ...agentOf(request),
92
+ };
36
93
  }
37
94
  /** `tool.completed` for an MCP or sandbox tool: its output, or the tool error it reported. */
38
95
  export function toolCompleted(request, value) {