@nylorun/runtime 0.22.0-beta → 0.24.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 (273) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/README.md +95 -30
  3. package/dist/adapters/execution/restate.d.ts +15 -3
  4. package/dist/adapters/execution/restate.js +78 -84
  5. package/dist/adapters/replication/pgoutput.d.ts +2 -0
  6. package/dist/adapters/replication/pgoutput.js +6 -3
  7. package/dist/adapters/sandbox/local.d.ts +9 -1
  8. package/dist/adapters/sandbox/local.js +16 -4
  9. package/dist/adapters/sandbox/virtual.js +4 -1
  10. package/dist/adapters/streams/s2.js +1 -13
  11. package/dist/api/a2a/endpoint.js +5 -1
  12. package/dist/api/a2a/routes.d.ts +2 -0
  13. package/dist/api/a2a/routes.js +2 -2
  14. package/dist/api/a2a/tasks.js +2 -1
  15. package/dist/api/ag-ui/endpoint.js +4 -2
  16. package/dist/api/ag-ui/history.js +4 -1
  17. package/dist/api/ag-ui/routes.d.ts +4 -1
  18. package/dist/api/ag-ui/routes.js +11 -10
  19. package/dist/api/ag-ui/translate.js +3 -2
  20. package/dist/api/components.d.ts +4953 -1204
  21. package/dist/api/components.js +25 -16
  22. package/dist/api/gate/keys.js +7 -1
  23. package/dist/api/gate/routes.d.ts +6 -0
  24. package/dist/api/gate/routes.js +39 -3
  25. package/dist/api/http/app.js +4 -4
  26. package/dist/api/http/define.d.ts +6 -0
  27. package/dist/api/http/define.js +13 -1
  28. package/dist/api/http/respond.js +1 -1
  29. package/dist/api/http/routes/artifacts.js +35 -8
  30. package/dist/api/http/routes/coverage.d.ts +9 -0
  31. package/dist/api/http/routes/coverage.js +34 -0
  32. package/dist/api/http/routes/environments.d.ts +11 -0
  33. package/dist/api/http/routes/environments.js +283 -0
  34. package/dist/api/http/routes/mcp.d.ts +1 -1
  35. package/dist/api/http/routes/mcp.js +2 -2
  36. package/dist/api/http/routes/me.js +1 -1
  37. package/dist/api/http/routes/reads.d.ts +45 -9
  38. package/dist/api/http/routes/reads.js +54 -5
  39. package/dist/api/http/routes/sessions.js +11 -6
  40. package/dist/api/http/routes/tenant.js +32 -10
  41. package/dist/api/openapi.js +31 -26
  42. package/dist/artifacts/disk.d.ts +59 -0
  43. package/dist/artifacts/disk.js +563 -0
  44. package/dist/artifacts/export.d.ts +1 -1
  45. package/dist/artifacts/export.js +2 -3
  46. package/dist/artifacts/folders.d.ts +6 -3
  47. package/dist/artifacts/folders.js +17 -12
  48. package/dist/artifacts/service.d.ts +2 -1
  49. package/dist/artifacts/service.js +6 -1
  50. package/dist/artifacts/workspace.d.ts +1 -1
  51. package/dist/artifacts/workspace.js +1 -1
  52. package/dist/core/flow-host.d.ts +3 -22
  53. package/dist/core/flow-host.js +11 -37
  54. package/dist/core/provider.d.ts +3 -2
  55. package/dist/core/provider.js +5 -37
  56. package/dist/core/sandbox-routes.d.ts +2 -21
  57. package/dist/core/sandbox-routes.js +10 -43
  58. package/dist/execution/types.d.ts +11 -9
  59. package/dist/execution/types.js +5 -7
  60. package/dist/gates/contract.d.ts +3 -24
  61. package/dist/gates/contract.js +1 -36
  62. package/dist/gates/egress-credentials.d.ts +9 -0
  63. package/dist/gates/egress-credentials.js +11 -4
  64. package/dist/gates/egress.d.ts +28 -1
  65. package/dist/gates/egress.js +115 -10
  66. package/dist/gates/fixture-model.d.ts +28 -0
  67. package/dist/gates/fixture-model.js +92 -0
  68. package/dist/gates/handler.js +4 -4
  69. package/dist/gates/http-tool.d.ts +6 -2
  70. package/dist/gates/http-tool.js +3 -5
  71. package/dist/gates/in-process.d.ts +5 -0
  72. package/dist/gates/in-process.js +12 -1
  73. package/dist/gates/journal.d.ts +30 -0
  74. package/dist/gates/journal.js +52 -0
  75. package/dist/gates/mcp-handler.d.ts +4 -0
  76. package/dist/gates/mcp-handler.js +5 -1
  77. package/dist/gates/meter.d.ts +10 -2
  78. package/dist/gates/meter.js +20 -5
  79. package/dist/gates/policy.d.ts +49 -0
  80. package/dist/gates/policy.js +134 -0
  81. package/dist/gates/tenant-vaults.d.ts +7 -7
  82. package/dist/gates/tenant-vaults.js +5 -5
  83. package/dist/gates/tool-calls.d.ts +10 -5
  84. package/dist/gates/tool-calls.js +28 -10
  85. package/dist/gates/tool-client.d.ts +8 -0
  86. package/dist/gates/tool-client.js +27 -11
  87. package/dist/gates/tool-contract.d.ts +10 -68
  88. package/dist/gates/tool-contract.js +1 -85
  89. package/dist/gates/tool-gate.d.ts +1 -1
  90. package/dist/harness/calls.d.ts +12 -5
  91. package/dist/harness/calls.js +21 -12
  92. package/dist/harness/executors.js +12 -9
  93. package/dist/harness/main.js +18 -5
  94. package/dist/harness/pod.d.ts +34 -0
  95. package/dist/harness/pod.js +41 -0
  96. package/dist/harness/service.d.ts +0 -1
  97. package/dist/harness/service.js +13 -5
  98. package/dist/harness/setup.d.ts +27 -0
  99. package/dist/harness/setup.js +150 -0
  100. package/dist/harness/ws-client.js +3 -2
  101. package/dist/harness/ws-port.d.ts +1 -5
  102. package/dist/harness/ws-port.js +1 -5
  103. package/dist/harness-api/in-process.js +3 -3
  104. package/dist/harness-api/record.d.ts +15 -1
  105. package/dist/harness-api/record.js +174 -44
  106. package/dist/harness-api/redact.d.ts +3 -0
  107. package/dist/harness-api/redact.js +45 -0
  108. package/dist/harness-api/server.d.ts +8 -0
  109. package/dist/harness-api/server.js +93 -13
  110. package/dist/harness-api/start.d.ts +10 -3
  111. package/dist/harness-api/start.js +40 -10
  112. package/dist/harness-api/tool-journal.d.ts +96 -0
  113. package/dist/harness-api/tool-journal.js +102 -0
  114. package/dist/harness-api/tool-status.d.ts +13 -0
  115. package/dist/harness-api/tool-status.js +26 -0
  116. package/dist/harness-api/workspace.d.ts +6 -3
  117. package/dist/harness-api/workspace.js +27 -11
  118. package/dist/harness-api/ws-server.d.ts +11 -3
  119. package/dist/harness-api/ws-server.js +94 -15
  120. package/dist/host/execution.d.ts +8 -6
  121. package/dist/host/gates.d.ts +2 -1
  122. package/dist/host/gates.js +10 -2
  123. package/dist/host/main.js +64 -21
  124. package/dist/host/operate.js +67 -5
  125. package/dist/host/stack-config.d.ts +70 -0
  126. package/dist/host/stack-config.js +67 -1
  127. package/dist/infra/execution.d.ts +18 -1
  128. package/dist/infra/execution.js +28 -4
  129. package/dist/infra/index.d.ts +1 -1
  130. package/dist/keys/client.js +4 -1
  131. package/dist/keys/keys.d.ts +5 -5
  132. package/dist/keys/keys.js +5 -7
  133. package/dist/management-openapi.json +999 -2211
  134. package/dist/mcp/connect.d.ts +25 -20
  135. package/dist/mcp/connect.js +29 -30
  136. package/dist/mcp/pool.d.ts +2 -2
  137. package/dist/mcp/pool.js +1 -1
  138. package/dist/mcp/preview.d.ts +3 -3
  139. package/dist/mcp/preview.js +10 -20
  140. package/dist/mcp/snapshot.d.ts +5 -11
  141. package/dist/mcp/snapshot.js +6 -7
  142. package/dist/model/pi-model.js +8 -0
  143. package/dist/openapi.json +6955 -2526
  144. package/dist/reads/types.d.ts +16 -3
  145. package/dist/record/append.d.ts +3 -1
  146. package/dist/record/append.js +1 -0
  147. package/dist/record/envelope.d.ts +5 -1
  148. package/dist/record/envelope.js +7 -3
  149. package/dist/record/environment.d.ts +25 -0
  150. package/dist/record/environment.js +44 -0
  151. package/dist/record/index.d.ts +1 -1
  152. package/dist/record/index.js +1 -1
  153. package/dist/retry.d.ts +21 -0
  154. package/dist/retry.js +34 -0
  155. package/dist/sandbox/disk-fs.d.ts +95 -0
  156. package/dist/sandbox/disk-fs.js +206 -0
  157. package/dist/sandbox/join.d.ts +17 -18
  158. package/dist/sandbox/join.js +46 -20
  159. package/dist/sandbox/manager.d.ts +16 -8
  160. package/dist/sandbox/manager.js +33 -17
  161. package/dist/sandbox/placement.d.ts +13 -12
  162. package/dist/sandbox/placement.js +29 -12
  163. package/dist/sandbox/pods/client.d.ts +6 -1
  164. package/dist/sandbox/pods/lifecycle.d.ts +28 -27
  165. package/dist/sandbox/pods/lifecycle.js +14 -26
  166. package/dist/sandbox/pods/reconcile.js +21 -15
  167. package/dist/sandbox/pods/spec.d.ts +15 -13
  168. package/dist/sandbox/pods/spec.js +16 -20
  169. package/dist/sandbox/records.d.ts +2 -2
  170. package/dist/sandbox/records.js +3 -3
  171. package/dist/sandbox/resolve.d.ts +35 -27
  172. package/dist/sandbox/resolve.js +66 -41
  173. package/dist/sandbox/session-sandbox.js +1 -1
  174. package/dist/sandbox/setup.d.ts +61 -0
  175. package/dist/sandbox/setup.js +123 -0
  176. package/dist/sandbox/share.d.ts +11 -21
  177. package/dist/sandbox/share.js +12 -41
  178. package/dist/sandbox/tenant-config.d.ts +12 -14
  179. package/dist/sandbox/tenant-config.js +12 -17
  180. package/dist/sandbox/types.d.ts +6 -0
  181. package/dist/store/postgres/control.js +8 -1
  182. package/dist/store/postgres/drizzle/0018_wake_outbox_retries.sql +3 -0
  183. package/dist/store/postgres/drizzle/0019_sandbox_signal_outbox.sql +18 -0
  184. package/dist/store/postgres/drizzle/0020_environments.sql +63 -0
  185. package/dist/store/postgres/drizzle/0021_disk_versions.sql +7 -0
  186. package/dist/store/postgres/drizzle/0022_model_usage_timing.sql +5 -0
  187. package/dist/store/postgres/drizzle/0023_artifact_pages.sql +1 -0
  188. package/dist/store/postgres/drizzle/meta/_journal.json +42 -0
  189. package/dist/store/postgres/reads.js +117 -17
  190. package/dist/store/postgres/record-writer.d.ts +4 -4
  191. package/dist/store/postgres/record-writer.js +9 -9
  192. package/dist/store/postgres/record.d.ts +7 -0
  193. package/dist/store/postgres/record.js +23 -2
  194. package/dist/store/postgres/schema.d.ts +1213 -779
  195. package/dist/store/postgres/schema.js +136 -76
  196. package/dist/store/postgres/store.js +283 -117
  197. package/dist/store/types.d.ts +206 -88
  198. package/dist/streams/relay/core.js +1 -13
  199. package/dist/tenant/advance.d.ts +7 -6
  200. package/dist/tenant/advance.js +121 -49
  201. package/dist/tenant/artifact-tool.js +3 -4
  202. package/dist/tenant/commands.d.ts +15 -2
  203. package/dist/tenant/commands.js +94 -14
  204. package/dist/tenant/context.d.ts +67 -33
  205. package/dist/tenant/context.js +4 -15
  206. package/dist/tenant/control.d.ts +8 -1
  207. package/dist/tenant/control.js +2 -0
  208. package/dist/tenant/effects.d.ts +1 -1
  209. package/dist/tenant/effects.js +37 -20
  210. package/dist/tenant/environments.d.ts +167 -0
  211. package/dist/tenant/environments.js +823 -0
  212. package/dist/tenant/harness-tokens.d.ts +21 -0
  213. package/dist/tenant/harness-tokens.js +61 -0
  214. package/dist/tenant/host-token.d.ts +1 -1
  215. package/dist/tenant/host-token.js +2 -4
  216. package/dist/tenant/identity-file.d.ts +4 -4
  217. package/dist/tenant/identity-file.js +17 -8
  218. package/dist/tenant/issuers.js +3 -3
  219. package/dist/tenant/principals.js +1 -1
  220. package/dist/tenant/reset.js +3 -3
  221. package/dist/tenant/runtime.d.ts +6 -1
  222. package/dist/tenant/runtime.js +30 -4
  223. package/dist/tenant/scheduler.d.ts +7 -0
  224. package/dist/tenant/scheduler.js +38 -7
  225. package/dist/tenant/session-streams.d.ts +0 -1
  226. package/dist/tenant/session-streams.js +1 -13
  227. package/dist/tenant/session.d.ts +3 -3
  228. package/dist/tenant/session.js +0 -14
  229. package/dist/tenant/sessions.d.ts +13 -13
  230. package/dist/tenant/sessions.js +61 -138
  231. package/dist/tenant/signing-keys.d.ts +5 -1
  232. package/dist/tenant/status.js +3 -3
  233. package/dist/tenant/streams.js +14 -29
  234. package/dist/tenant/sweep.d.ts +39 -12
  235. package/dist/tenant/sweep.js +168 -63
  236. package/dist/tenant/transcript.d.ts +210 -20
  237. package/dist/tenant/transcript.js +62 -11
  238. package/dist/tenant/turn-timing.d.ts +43 -0
  239. package/dist/tenant/turn-timing.js +39 -0
  240. package/dist/tenant/types.d.ts +2 -0
  241. package/dist/traces/ids.d.ts +42 -0
  242. package/dist/traces/ids.js +85 -0
  243. package/dist/traces/key.d.ts +6 -0
  244. package/dist/traces/key.js +37 -0
  245. package/dist/traces/map.d.ts +113 -0
  246. package/dist/traces/map.js +317 -0
  247. package/dist/traces/options.d.ts +7 -0
  248. package/dist/traces/options.js +1 -0
  249. package/dist/traces/otlp.d.ts +42 -0
  250. package/dist/traces/otlp.js +84 -0
  251. package/dist/traces/projector.d.ts +58 -0
  252. package/dist/traces/projector.js +200 -0
  253. package/dist/traces/read.d.ts +23 -0
  254. package/dist/traces/read.js +16 -0
  255. package/dist/vault/coverage.d.ts +26 -0
  256. package/dist/vault/coverage.js +64 -0
  257. package/dist/vault/error.d.ts +11 -1
  258. package/dist/vault/error.js +5 -1
  259. package/dist/vault/service.d.ts +47 -48
  260. package/dist/vault/service.js +321 -382
  261. package/dist/vault/sources.d.ts +5 -16
  262. package/dist/vault/sources.js +6 -8
  263. package/dist/version.d.ts +1 -1
  264. package/dist/version.js +2 -2
  265. package/package.json +14 -13
  266. package/dist/api/http/routes/sandboxes.d.ts +0 -9
  267. package/dist/api/http/routes/sandboxes.js +0 -153
  268. package/dist/api/http/routes/vaults.d.ts +0 -10
  269. package/dist/api/http/routes/vaults.js +0 -139
  270. package/dist/record/sandbox.d.ts +0 -25
  271. package/dist/record/sandbox.js +0 -43
  272. package/dist/tenant/sandboxes.d.ts +0 -122
  273. package/dist/tenant/sandboxes.js +0 -562
package/CHANGELOG.md CHANGED
@@ -1,5 +1,166 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.24.0-beta
4
+
5
+ ### Minor Changes
6
+
7
+ - e917bd6: **Bring your own harness: harness ids, harness-owned state, and who wrote each event (R4.1, R4.2, R4.4, R4.9).**
8
+
9
+ - **Harness ids and routing.** An Environment's `harness` takes any harness id (`^[a-z][a-z0-9-]{1,62}$`). It is no longer only `nylorun`.
10
+ - **The pin.** A session pins its harness when it opens: its Environment's harness, else `nylorun`. A flow always runs `nylorun`. `GET /v1/sessions/{id}` returns the pin as `harness`.
11
+ - **Routing.** Only a connection whose `hello.name` is that id takes the session's runs.
12
+ - **Our harness's name.** It now says `nylorun` in `hello`. A harness that says `@nylorun/harness` is refused with a message naming the new id, so an older harness image needs this Runtime's.
13
+ - **Placement.** It is keyed by the real harness id, and `"*": {hosts: []}` refuses any harness the map does not name.
14
+ - **Drift.** A turn on an Environment that now runs another harness is `409 harness_mismatch`; open a new session. For a pod Environment, a changed `harness` applies at the pod's next wake, like its image.
15
+ - **Per-harness tokens.** `nylorun-operate harness token put <id>` makes a custom harness container's own Harness API credential, bound to its id; `list` and `rm` manage them. `NYLORUN_HARNESS_TOKEN` stays ours, bound to `nylorun`.
16
+ - **A custom harness as a pod's image.** A pod Environment whose harness is not ours runs its `image` as the harness: the image's own entrypoint (`entrypoint: "image"` to the sandboxes service), with nothing copied in. It needs `image`.
17
+ - **Harness-owned state.** Core stores a harness's turn state as sent and never reads it. Our engine now aligns its state with the turn's manifest itself.
18
+ - **Limits for a custom harness.** Its state is at most 256 KiB, or its turn fails. It cannot pause for approvals in Harness API 2.
19
+ - **Native tools.** It may declare the tools it runs itself in `hello` (`capabilities.tools`). Their calls (capability `harness`) are journaled like built-ins, and the log records a real `tool.completed` instead of `tool.unavailable`.
20
+ - **Who wrote each event.** The writer sets an event's `source` and `evidence`; the event's type no longer decides them. Events written before this release still say `loop` or `api`.
21
+ - **`client`**: a command, with a new `principal` taken from its credential.
22
+ - **`harness`**: what a harness reported, with its harness id and `runId`. Evidence `claimed`.
23
+ - **`core`**: core's own logic. Evidence `observed`.
24
+ - **`effectId` on model calls.** `GET /v1/sessions/{id}/calls/model` returns each call's `effectId`, so a metered call joins the effect journal.
25
+ - **The published contract grows.** It adds `HarnessCheckpointSchema` (the checkpoint fields a harness reads), `NativeToolSchema`, the harness id helpers, `HARNESS_TOOLS_CAPABILITY`, `MAX_HARNESS_STATE_BYTES`, and the gate error codes `key_required` and `effect_not_journaled`. It also documents the effect context keys, the model request hash rule and how a turn's status maps to its output method.
26
+
27
+ - 5165451: **A pod's disk can start prepared, and every Environment's disk has versions (R2d steps 2 and 3).** Before, a new pod volume was empty and a disk could only be kept or reset. Now:
28
+
29
+ - **Setup and input files** (pods, Host feature `environment-setup`): `PUT /v1/environments/{id}` takes `files` (versions of Tenant-wide artifacts, copied in: a file at its `path`, a folder under it) and `setup` (commands run in order with `/bin/sh -c`, `cwd` default `/workspace`, `timeout` default 10m and at most 30m, at most 16 of each). They run once per new disk, after the pod's engine joins and before its first turn, with the Environment's variables, secrets (as the sentinel) and egress. A failed command fails the pod (`observed: failed`, reason `setup: command N …`, an `environment.failed` event with the exit code and the end of the output) and the turn waiting for it; the next turn start runs the setup again. A virtual Environment refuses both with a `400`.
30
+ - **Disk versions** (Host feature `environment-disk-versions`): `POST /v1/environments/{id}/snapshot` keeps a version of the disk between turns (`/workspace`, and a pod's `/harness` but the engine's own files) as a version of a folder artifact the Environment owns, with modes, modification times, links and empty directories (folder manifest `nylorun.folder.v2`); each file's bytes are stored once, and an unchanged disk makes no version. `POST .../restore` puts a version back: a virtual Environment at once, a pod on a new volume seeded before its next turn (`disk.operation` in the view meanwhile; turns wait). `PUT` with `from: { environment, version }` creates an Environment from another's version. `GET .../versions` and `DELETE .../versions/{n}` list and delete them; their trees, files, diffs and zips read through `/v1/artifacts`. A lost pod volume with a version is recovered from the latest one instead of refusing turns until a reset. Snapshot and restore during a turn are `409 environment_busy`. `EnvironmentView.disk` gives the latest version and the copy in progress. Migration `0021_disk_versions`.
31
+ - `@nylorun/agents`: `client.environments.snapshot`, `restore`, `versions` and `deleteVersion`; `put` takes `setup`, `files` and `from`.
32
+
33
+ - 35e29b7: **Environments replace sandboxes and vaults (protocol 11). This is a fresh start: upgrading deletes your sandboxes, their disks and every vault except the model's, and closes every open session (its history stays readable).** List what you have before upgrading; see "Environments replace sandboxes and vaults" in MIGRATION.md.
34
+
35
+ An Environment is where sessions run: one sandbox (kind `virtual`, `pod` or `none`) with a recipe that can change, and one vault of secrets for its shell, MCP servers and HTTP tools. The app decides who uses which Environment; the Runtime keeps it by id and holds its lifecycle.
36
+
37
+ - `@nylorun/core`: protocol 11 (the Host accepts 11 only; older clients get `426`), required feature `environments`. `PutEnvironmentRequestSchema`, `EnvironmentViewSchema` (`recipe`, `version`, `appliedVersion`, `ownerUserId`, `lastUsedAt`), `PutEnvironmentSecretRequestSchema`, `EnvironmentSecretViewSchema`, `ENVIRONMENT_EVENT_TYPES`, `TenantEnvironmentsConfigSchema`, `environmentGranted`. `PutSessionRequest.environment` replaces `sandbox`, `vaultIds` and `credentialSelections`; `SessionView.environment` replaces the session's sandbox and vault fields. Error codes `environment_busy`, `environment_unavailable`, `environment_lost`, `environment_not_granted`, `environment_owner_mismatch` and `session_closed` (the `sandbox_*` codes and `sandbox_expired` are gone); session events `environment.state`, `environment.exec` and `environment.attached`; scope `environments:write`. Credential coverage takes `environmentId` and answers `covered` or `missing`; the MCP preview takes `environmentId`. The vault Management schemas are removed.
38
+ - `@nylorun/runtime`: `/v1/environments/{id}` replaces `/v1/sandboxes/{id}`: `PUT` creates, finds or changes one (`kind` and `storage` fixed; a recipe change bumps `version`; a pod gets a new image or resources at its next start from stopped, a new allowlist within seconds), with list by label, events (`nylorun.environment-event/1`), stop, reset and delete. Each Environment owns one vault: `PUT|GET|DELETE /v1/environments/{id}/secrets/{name}` on the Runtime API, write-only, with a second credential for a host or URL refused at the write (`409 credential_conflict`). An optional owner guard (`ownerUserId`) is checked when a session opens and at every turn start. `lifecycle.deleteAfter` deletes an Environment unused that long; TTL and `onExpiry` are gone. Kind `none` holds secrets for MCP-only agents. `/v1/tenant/vaults`, user and installation vaults, the Tenant's default sandbox, inline sandboxes and `{ session }` sharing are removed. The Tenant setting is `environments.config` (`limits.environments`). Identity files grant `environments:` (an old `sandboxes:` key, or `sandboxes:write`, stops the Runtime with a message naming the new key). Migration `0020_environments`.
39
+ - `@nylorun/harness`: the Harness API claims `environment.state` and `environment.exec`, and its workspace fields name `environmentId`.
40
+ - `@nylorun/agents`: `client.environments` (`put`, `get`, `list`, `page`, `stop`, `reset`, `delete`, `events`, `secrets.put|list|get|delete`, `forSession`) replaces `client.sandboxes`; `createSession({ environment })`; `sessions.page({ environmentId })` filters by Environment. The AG-UI handler's session options take `environment`.
41
+ - `@nylorun/admin`: `admin.coverage({ agentId, environmentId })` replaces `admin.vaults`; `admin.mcp.preview({ url, environmentId })`.
42
+ - `@nylorun/studio`: Environments replace Sandboxes, with a create form, stop, reset, delete and their secrets; the Credentials forms write an Environment's secrets; New session picks an Environment and shows its coverage.
43
+ - `nylorun`: `nylorun sandbox ls|rm` list and delete Environments; `nylorun mcp inspect --environment <id>` replaces `--vault`; `nylo reset --environments` replaces `--sandboxes`; `nylo doctor sandbox` reports the Environment limits.
44
+
45
+ - e917bd6: **The Harness API is a published contract (R4.5).** Everything a harness sends or reads now has a schema in `@nylorun/core/harness-api`, and the Runtime validates with those same schemas:
46
+ - the transport and the pod join (`HARNESS_API_PATH`, `HARNESS_API_HEADER`, `HOST_JOIN_PATH`, `HostJoinRequestSchema`, `HostJoinAnswerSchema`);
47
+ - the Model and Tool Gate bodies (`ModelCallBodySchema`, `ToolCallBodySchema`, `HttpCallBodySchema`, the MCP bodies, `GATE_ERROR_CODES`);
48
+ - the MCP snapshot (`McpSnapshotSchema`);
49
+ - transcript entries (`TranscriptEntrySchema`);
50
+ - the effect id rule (`effectIdOf`, `isEffectIdOf`).
51
+
52
+ The build also writes them as one JSON Schema document, `@nylorun/core/harness-api/schema.json`, for harnesses written in other languages.
53
+
54
+ Other changes:
55
+ - `TurnStart` gains an optional `trace` (W3C `traceparent`) for the turn's trace context. Nothing fills it yet.
56
+ - Core refuses a `hello` whose `api` is not `2`.
57
+ - **The fixture model answers at the Model Gate (R4.3).** With a Tenant's `fixtureModel` seeded, the gate answers model calls itself and meters each one, like any model: a `model_usage` row with provider `fixture` and cost 0, readable at `GET /v1/sessions/{id}/calls/model`. Our harness no longer answers it locally.
58
+ - The fixture model also answers scripted markers: `fixture:text`, `fixture:tool <name> <json>`, `fixture:budget` and `fixture:slow <seconds>`.
59
+ - **The sandboxes service can run a harness image (R4.4, Go).** `PUT /v1/pods/{name}` accepts `entrypoint: "image"`: the workload image's own `ENTRYPOINT` and `CMD` run, with no engine copied in and no `harnessImage`. The default, `entrypoint: "engine"`, is unchanged. The Runtime starts sending it with the harness pin in a later change.
60
+
61
+ - 3013501: **Tool calls are delivered at least once, and every one is on the record (R5 step 1).** A gateway restart used to park the sessions it interrupted as `uncertain` until someone cancelled them, a harness could call tools with no record, and nothing listed a session's tool calls. Now every tool call ends in the log with its outcome, a crash sends it again with the same key, and the gateway runs only calls the effect journal recorded.
62
+
63
+ - **Every outcome in the log.** Each tool call ends in one `tool.completed` with `status` (`completed`: the tool answered; `failed`: the Runtime ended it, `tool.disabled`, `tool.unavailable` or `tool.interrupted`; `cancelled`), `kind` (`mcp`, `http`, `sandbox`, `platform`), `server`, `attempts` and `durationMs`. Refused and cancelled calls are in the log for the first time.
64
+ - **`GET /v1/sessions/{id}/calls/tool`** pages a session's tool calls from the effect journal, in the order they were first sent: tool, input, status, sends, error and timing. Same credentials and paging as `/calls/model`; `@nylorun/agents` adds `session.toolCalls()`. Host feature `tool-calls`.
65
+ - **At least once, with the same key.** An MCP or HTTP tool call that a crash, a shutdown or a lost gateway hop interrupted is sent again with its effect id as the key, up to 3 sends; then it fails to the model as `tool.interrupted` ("may have run") and the session continues. A gateway that restarts mid call runs the call again under its key instead of answering `uncertain`. HTTP tools get the key as `Idempotency-Key`, as before; MCP servers now get it in `_meta["nylorun/idempotency-key"]` on `tools/call`. A tool that honors the key acts once; `attempts` above 1 marks where one may have acted twice.
66
+ - **Sandbox commands are never run twice.** A sandbox or core-served call a crash interrupted has no key to dedupe on, so it fails to the model as `tool.interrupted` at once, and the model decides whether to run it again.
67
+ - **No tool call parks a session.** The `uncertain` state for tool calls, its cancel-only exit and R2b's read-only/idempotent `mcp.lost` exception are gone: a lost MCP answer is `mcp.lost` to the model for every tool. Model calls keep their own rules.
68
+ - **The gateway runs only journaled calls.** Under a run token, `/nylorun/v1/tool-calls` and `/nylorun/v1/http-calls` need the call's effect id as `Idempotency-Key` (`400 key_required`), and the effect journal must hold it running, for the run's turn, the same tool and the same input (`409 effect_not_journaled`). A harness that skips the journal gets nothing run.
69
+ - `@nylorun/harness`: `HarnessExecutors.resends(effect, error, run)` says a gate hop lost a call's answer; the harness then asks core again with the same intent and sends the call again.
70
+ - **Approval** stays in the manifest and our harness still asks for it. Only the Runtime's own harness asks: one built from the Harness API can skip it until tool policy moves to the gateway (R5 step 2).
71
+
72
+ - 6061a3a: **Tool approvals are the Tenant's tool policy (R5 step 2).** An agent's manifest used to say which tool calls wait for a person, and only the Runtime's own harness asked; a harness built from the Harness API could skip it. Now the operator sets one tool policy for every agent and every harness, and the Runtime enforces it before anything is sent.
73
+
74
+ - **`GET`/`PUT /v1/tenant/tool-policy`** (the Management API; `admin.toolPolicy.get()`/`.put({ rules })`): rules `{ effect: "allow" | "ask" | "deny", tool?, server?, kind?, agent?, when?, reason? }`. A rule matches a call when every selector it sets matches (`tool` and `server` take `*` globs; `when` tests input fields by dotted path with `equals`, `in`, `gt`, `gte`, `lt`, `lte` or `matches`). The strictest matching rule decides; a call no rule matches runs.
75
+ - **Enforced for every harness.** Core decides an MCP or HTTP tool call as it journals it. `deny` fails it to the model as `tool.denied`, unsent. `ask` holds the call (`held` at `/calls/tool`) and pauses the turn with one `approval` interaction per held call (`turn.paused`, with the tool, its input and the reason). `approve` sends that exact call, or fails it as `tool.denied`. The Tool Gate runs only calls the journal holds running, so a held or refused call cannot run. Nothing holds a process while it waits: an approval survives a restart. A flow's HTTP stages are decided the same way.
76
+ - **`approval` leaves the manifest.** `PUT /v1/agents/:id` and `saveAgent()` refuse a definition that sets `approval` on an HTTP tool, an MCP server or an MCP tool's settings; `http({ approval })` and `.mcp({ … approval })` throw; a turn manifest can no longer add one. The engine never asks for an HTTP or MCP tool's approval itself. A session pinned to a manifest saved with `approval: "always"` keeps it, as an ask.
77
+ - `@nylorun/studio`: a paused session shows an approval card for each held call, with its tool, why it is held and its input, and **Approve** / **Deny**.
78
+ - Migration: move each `approval: "always"` to an `ask` rule (see MIGRATION.md).
79
+
80
+ - 7d496e4: **Every connection a pod Environment makes to the internet is on the record (R7, egress events).** egress-gate used to keep only log lines, so a session's log could not say which hosts its turn reached or what the gate refused. Now each connection through egress-gate is one event, written by the gate itself.
81
+
82
+ - **`egress.connection`** in the session log of the turn that held the Environment when the connection opened, with that turn's id even if the turn has ended by the time it closes. Source `egress-gate`, evidence `observed`.
83
+ - **`environment.egress`**, the same payload, on the Environment's own stream (`GET /v1/environments/{id}/events`) for traffic while no turn holds the Environment.
84
+ - **The payload:** `environmentId`, `host`, `port`, `outcome` (`allowed` or `refused`), `reason` and `status` for a refusal, `mode` (`tunnel` or `credential`), `credentialBound`, `bytesUp` and `bytesDown`, `startedAt` (the gate's clock at CONNECT) and `durationMs`. A host a shell credential is bound to adds `requests` and the `credentialIds` the gate set. It never carries a header value, a URL path or a body.
85
+ - **One event per connection:** written when it closes, or at once when the gate refuses it (a host not in `network.allowedHosts`, a port other than 443 or 80, a private address, too many tunnels, a name that does not resolve). A refusal before the Environment is known (no token, an expired or stale one) stays a log line only.
86
+ - **Never in the way:** a write happens after the connection closes or is answered, and a failed write is logged as `egress_record_failed` without touching the tunnel. The gateway's `egress_refused` and `egress_terminated` log lines stay.
87
+
88
+ - 81c783e: **Every row that ends a call or a turn carries its own timing (R7.1).** The session log said what happened but not how long it took: a model call's outcome had no time at all, a tool call's only core's window, a flow's HTTP stage had no outcome row, and a turn's end had neither its duration nor its cost. Now each one is a span on its own, ready for the traces that follow, with no protocol change.
89
+
90
+ - **Model calls.** `message.assistant`, `model.failed` and `context.compacted` carry the call's `effectId` and `durationMs` (from the intent to the outcome). When the Model Gate metered the call they also carry the gate's own `gate: { startedAt, endedAt }`, `firstTokenMs` (to the provider's first stream event) and `meteredUsage` (`inputTokens`, `outputTokens`, `totalTokens`, and `cachedTokens` and `costUsd` when not zero), beside the `usage` the harness reported. The usage ledger keeps the gate's times (migration 0022: `model_usage.started_at`, `ended_at`, `first_token_ms`).
91
+ - **One row per model call.** A step that said nothing (no text, no tool calls, as a structured answer) now writes `message.assistant` with `text: ""`, and each partial summary of a long compaction writes `context.compacted` with `partial: true`. The AG-UI and A2A histories skip both; `isEmptyAssistantMessage` in `@nylorun/core/contracts` tells your own UI which steps said nothing.
92
+ - **Tool calls.** `tool.completed` carries the call's `effectId`, the Tool Gate's own `gate: { startedAt, endedAt }` for an MCP or HTTP call it ran (absent for sandbox and core-served tools), and `policy: { decision: "ask" | "deny", reason, approvedBy? }` when the Tenant's tool policy held or refused the call.
93
+ - **Flow HTTP stages.** A new event type, `node.completed` (`path`, `kind`, `key`, `iterations`, `effectId`, `status`, `durationMs`, `gate`), ends each HTTP stage and HTTP verifier that `node.started` began, and a tool stage the Runtime refused to run. Clients ignore event types they do not know.
94
+ - **Turns.** `turn.completed`, `turn.failed` and `turn.cancelled` carry `durationMs` (from the turn's start to its end, waits for a person included), `pausedMs` (how much of that it waited paused), `agentId` and `totals: { steps, tokens, costUsd }`, the same totals `GET /v1/tenant/usage?scope=turn` returns. A turn started before this release has no `durationMs`.
95
+
96
+ - 7800358: **Each turn is one OpenTelemetry trace, projected from the session log (R7).** An operator could not see what a turn did or where its time went without grepping container logs. Now every turn is a trace with a span for each model call, tool call and egress tunnel, exported over OTLP to any collector, readable per turn from the Runtime, and joined to the trace of the app that started it. No protocol change.
97
+
98
+ - **Traces from the log.** A turn's end is its root span, `invoke_agent <agent>`; each model call is a `chat <model>` span, each tool call and flow HTTP stage an `execute_tool <tool>` span, each egress-gate tunnel a `CONNECT <host>` span, and each pause and answer a marker. A model or gate tool span is the gate's own start and end; usage and cost are the gate's (`nylorun.usage.mismatch` when the harness reported other numbers). Attribute names follow the OTel GenAI conventions at one pinned commit, recorded as `nylorun.semconv.genai` on the resource. Ids are derived from the log under a key the Tenant keeps (setting `trace.key`), so every export and read of a turn gives the same ids.
99
+ - **OTLP export, off until you set an endpoint.** With `OTEL_EXPORTER_OTLP_ENDPOINT` (or `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`) the Runtime posts OTLP/HTTP JSON from a replication slot of its own, `nylorun_trace_export`, in batches of up to 512 spans or every 2 s; `OTEL_EXPORTER_OTLP_HEADERS`, `OTEL_SERVICE_NAME` and `OTEL_RESOURCE_ATTRIBUTES` work as usual. No turn waits for it. A collector that is down costs only its spans: after three retries a batch is dropped and logged as `trace_export_dropped`, and the slot moves on, so Postgres keeps no WAL for it. `nylorun start` passes these variables to the runtime container.
100
+ - **References by default.** Spans point at log events (`nylorun.event.cursor`) and carry no content unless you opt in per kind with `NYLORUN_TRACE_CONTENT` (`user`, `outputs`, `tool-results`, `errors`), or all of them with `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`, each cut at 8 KiB. A span never carries a credential value, a prompt, a person's id, an identity header or an idempotency key.
101
+ - **`GET /v1/sessions/{id}/turns/{turnId}/trace`** answers a turn's spans as OTLP JSON, the same spans the exporter posts: look at a turn without a collector, or post it to one to backfill. Application keys, as the `/calls` reads. Host feature `traces`; `@nylorun/agents` adds `session.trace(turnId)`, and `@nylorun/core/contracts` `TurnTraceSchema`.
102
+ - **Trace context in and out.** A `traceparent` (and `tracestate`) on `POST /v1/sessions/{id}/commands`, an AG-UI run or an A2A message puts the turn in the caller's trace; `command.message` records it as `trace`. The turn hands its context on: to the harness in `TurnStart.trace`, to HTTP tools as `traceparent` beside `Idempotency-Key`, to MCP servers as `traceparent` on the call's requests, and to sandbox commands as `TRACEPARENT`.
103
+ - **Harness API** (additive): the `effect.intent` answer `execute` may carry the tool call's `traceparent`. `@nylorun/harness`: `HarnessExecutors.tool` takes it as a fourth argument (`ToolCallContext`).
104
+
105
+ ### Patch Changes
106
+
107
+ - 17a97e9: **An artifact names the Environment whose disk it holds.** `ArtifactView` (and so `ArtifactListItem` in artifact pages) gains optional `environmentId`: set on the folder artifact that holds an Environment's disk versions (R2d step 3), absent on a session's or the Tenant's own artifacts. Before, such an artifact read as a Tenant-owned one with no way to tell whose disk it was.
108
+ - b6214d9: Add opt-in artifact metadata pagination to `GET /v1/artifacts?limit=…`, shared
109
+ `ArtifactListItem`/`ArtifactPage` contracts and SDK `artifacts.page()` (default 50,
110
+ maximum 200). Hosts advertise `artifact-reads`. Pages order by creation time and
111
+ ID descending, filter by session, kind and labels, bind cursors to the Tenant and
112
+ filters, and reapply ownership and agent grants on each read. Legacy unpaged
113
+ responses remain unchanged. A Drizzle migration adds the creation-order index;
114
+ pages reuse the existing bounded read-only pool. Studio and every other Runtime
115
+ client can consume this public API without database access.
116
+ - a708b27: **The example harness, on harness ids (R4.8).**
117
+
118
+ - **A new fixture marker.** `fixture:unoffered <name> <json>` makes the fixture model call a tool the turn does not offer, so a test can see that such a call runs nothing and the turn goes on. The other markers are unchanged.
119
+ - **The example harness.** `examples/harness-minimal` says `minimal` in `hello`, connects with its own token from `nylorun-operate harness token put minimal`, and runs beside ours: sessions on an Environment whose `harness` is `minimal` go to it. As a pod Environment's `image` it is the pod's harness.
120
+ - **A session's workspace is its own harness's.** Off a pod, a session's sandbox routes, `save_artifact` and turn-end export go only to a connection of the session's harness that serves workspaces. Before, a custom harness's session was served from our harness's workspace, which is not that harness's files. Without such a connection the request is refused. Sweeps and removes still go to ours.
121
+ - **The Harness API reference and guide.** `guides/HARNESS_API.md` covers every section a harness implements, with a walkthrough for building one.
122
+
123
+ - dec813c: **A gateway shutdown no longer leaves an unhandled rejection.** When a keyed MCP or HTTP tool call was re-sent and the re-send's client went away before the gateway joined the running call, closing the gateway mid call rejected that join with nobody listening ("The gateway is shutting down"), which Node reports as an unhandled rejection. The gateway now handles it: the call still stops on shutdown and the next gateway sends it again under its key, as before.
124
+ - 11c3b2a: **The Runtime's own tokens never reach a session's record from what a harness reports.** A harness's commands hold the egress token in their proxy variables, and a command that prints its environment, or a model that repeats it, would put that token into a tool result. Core now replaces every Nylorun-signed token before recording it:
125
+ - where it looks: tool and model outcomes, error messages, claimed sandbox events, and a turn's output, state and transcript edits;
126
+ - what a token is: a compact JWT whose `typ` starts with `nylorun-`, such as egress, run or host tokens;
127
+ - what replaces it: `[nylorun token redacted]`.
128
+
129
+ This applies to every harness, ours included. The leak showed when the example harness ran as a sandbox pod's image. Other values are unchanged.
130
+
131
+ - 02c2d24: **The runtime image keeps the repository's new layout.** The Runtime's code moved to `runtime/server/` and the harness to `runtime/harness/` in the repository, and the image follows it: the entry point is `/app/runtime/server/dist/host/main.js` (was `/app/runtime/dist/host/main.js`). The image's `ENTRYPOINT`, `nylorun-operate` and the sandboxes service (`ghcr.io/nylorun/sandboxes`, released with the Runtime) use the new path; anything that runs a file under `/app/runtime/dist/` directly needs the new one.
132
+ - a0cbca1: **Clearer wording when a definition runs your code.** The refusal for a code tool or a flow's tool stage, the `hooks were removed` error and `nylo endpoints` now say that the Runtime runs agents from their manifests alone, instead of that it runs no code of yours during a session. Only the message text changed; anything matching the old wording (for example `runs no code of yours during a session`) needs the new text.
133
+ - Pin core to the tested release.
134
+ - Pin harness to the tested release.
135
+ - Updated dependencies [17a97e9]
136
+ - Updated dependencies [b6214d9]
137
+ - Updated dependencies [e917bd6]
138
+ - Updated dependencies [5165451]
139
+ - Updated dependencies [35e29b7]
140
+ - Updated dependencies [e917bd6]
141
+ - Updated dependencies [a0cbca1]
142
+ - Updated dependencies [3013501]
143
+ - Updated dependencies [6061a3a]
144
+ - Updated dependencies [7d496e4]
145
+ - Updated dependencies [81c783e]
146
+ - Updated dependencies [7800358]
147
+ - Updated dependencies
148
+ - @nylorun/core@0.20.0-beta
149
+ - @nylorun/harness@0.26.0-beta
150
+
151
+ ## 0.23.0-beta
152
+
153
+ ### Minor Changes
154
+
155
+ - 72a4b52: The outbox no longer stalls the Tenant sweep, holds up commands or loses a pod sandbox's signals, and Workers register versioned Restate deployments.
156
+
157
+ - A wake that keeps failing (Restate refuses it, or an older Runtime does not know its reason) is retried with backoff and, after ten failures, parked: logged once at error and kept in the outbox for an operator. It no longer fails the sweep, so Restate never pauses the Tenant's sweep object over it. Migration 0018 adds `attempts`, `retry_at` and `parked_at` to `nylorun.wakes`.
158
+ - The sweep drains the whole outbox and pages through every orphaned session in each pass, within a 2 s budget, instead of 100 rows per pass.
159
+ - A command's wake is sent after commit without the request waiting for it; a failed send is logged at warn and sent again by the sweep's next pass. The sweep's grace is compared on the database's clock and is now 30 s.
160
+ - A pod sandbox's reconcile and idle timer go through the outbox (migration 0019, `nylorun.sandbox_signals`), so a crash after commit no longer loses them, and a host revocation goes over the control bus (`host.revoked`), so it reaches every process with the Tenant open. `Tx.afterCommit` is removed.
161
+ - Sends to Restate's ingress use `@restatedev/restate-sdk-clients` 1.17.2, each with an idempotency key.
162
+ - A Worker registers `<NYLORUN_WORKER_URL>/nylorun/<version>`, the version being the new `NYLORUN_WORKER_VERSION` or the Runtime's own, without forcing in a container, so a rolling upgrade keeps the old deployment for the invocations it started. A build whose Worker code changed under the same Runtime version sets its own `NYLORUN_WORKER_VERSION`.
163
+
3
164
  ## 0.22.0-beta
4
165
 
5
166
  ### Minor Changes
package/README.md CHANGED
@@ -4,7 +4,7 @@ The independent OSS **Runtime Host** consumes `@nylorun/harness/run` and
4
4
  `@nylorun/core/contracts`. Cloud installs published `@nylorun/harness` from npm
5
5
  and does not import this package. Every Tenant serves two APIs on one URL: the
6
6
  **Runtime API** for developers (agents, sessions with AG-UI and
7
- A2A, sandboxes, artifacts), whose client is `@nylorun/agents`, and the
7
+ A2A, Environments and their secrets, artifacts), whose client is `@nylorun/agents`, and the
8
8
  **Management API** for operators (`/v1/tenant/*`), whose client is `@nylorun/admin`.
9
9
  Vocabulary: [src/CONTEXT.md](./src/CONTEXT.md).
10
10
 
@@ -24,7 +24,7 @@ Restate and S2: `nylorun start` runs them, as a local Tenant, on a developer mac
24
24
  package is a library with no bin; its Host entry is `@nylorun/runtime/server`
25
25
  (`dist/host/main.js`), which requires `NYLORUN_DATABASE_URL`. For tests and
26
26
  ephemeral embeds, use `startEphemeralRuntime()` from `@nylorun/runtime/core`.
27
- See [MIGRATION.md](../MIGRATION.md).
27
+ See [MIGRATION.md](../../guides/MIGRATION.md).
28
28
 
29
29
  A Runtime serves one **Tenant**: an installation is one Runtime with its own
30
30
  Postgres database, Restate and S2 basin, and two Tenants are two installations. No
@@ -44,8 +44,9 @@ or deleted key stops authenticating at once. A database written by a Runtime tha
44
44
  database (`tenant_<id>` schemas), or by a pre-release build of one Tenant per database
45
45
  (`schema_version` tables), is refused: this release starts fresh on a new database. Restate runs one advance of a session at a time, delivers its wakes and holds
46
46
  its timers and the Tenant's sweep (Durable Session Execution), and journals no effect:
47
- checkpoints, effects, events and the wake outbox are the Session Store's, and a wake is
48
- written to the outbox in the transaction that causes it, so a commit never loses it; every session's events are relayed from the record to
47
+ checkpoints, effects, events and the outbox are the Session Store's, and a wake or a pod
48
+ Environment's reconcile is written to the outbox in the transaction that causes it, so a commit
49
+ never loses it ([src/CONTEXT.md](src/CONTEXT.md)); every session's events are relayed from the record to
49
50
  its own S2 stream, which history and SSE read (Durable Streams). One image runs every **service**,
50
51
  and `--service` picks what a process runs: `core` serves the Runtime API, the
51
52
  Management API and SSE and runs the stream relay, `loop` runs advances (the Worker), and
@@ -65,19 +66,32 @@ core's process. A `harness` process needs `NYLORUN_HARNESS_URL`,
65
66
  `/harness`), answers `/health` on `127.0.0.1:4300`, and refuses to start with a
66
67
  database, Restate, keys or gates credential in its environment. A local Tenant
67
68
  runs it in the `harness` container, on a network with only the runtime and the
68
- gateway ([DEPLOYMENT.md](../DEPLOYMENT.md#the-harness-agent-turns-mcp-servers-and-workspaces)).
69
+ gateway ([DEPLOYMENT.md](../../guides/DEPLOYMENT.md#the-harness-agent-turns-mcp-servers-and-workspaces)).
69
70
 
70
71
  The container is configured by its environment, which a local Tenant's Compose file
71
72
  sets: `NYLORUN_DATABASE_URL` (required), `NYLORUN_RESTATE_INGRESS_URL`,
72
- `NYLORUN_RESTATE_ADMIN_URL`, `NYLORUN_WORKER_URL` and
73
+ `NYLORUN_RESTATE_ADMIN_URL`, `NYLORUN_WORKER_URL`, `NYLORUN_WORKER_VERSION` and
73
74
  `NYLORUN_RESTATE_IDENTITY_KEY` (Restate), `NYLORUN_S2_ENDPOINT` and
74
75
  `NYLORUN_S2_TOKEN`, and in container mode `NYLORUN_LISTEN_HOST`,
75
76
  `NYLORUN_LISTEN_PORT`, `NYLORUN_ALLOWED_HOSTS` and `NYLORUN_PUBLIC_URL`.
77
+ A Worker registers its Restate deployment at a versioned URL,
78
+ `<NYLORUN_WORKER_URL>/nylorun/<version>`, where the version is `NYLORUN_WORKER_VERSION`
79
+ (letters, digits, `.`, `_`, `-`) or, by default, the Runtime's own. In a container it never
80
+ replaces a deployment already registered there (`force: false`): during a rolling upgrade
81
+ the new version's Workers register next to the old ones, Restate sends new invocations to
82
+ the new deployment and lets the old one finish what it started, and every Worker of one
83
+ version shares its deployment through one load-balanced `NYLORUN_WORKER_URL`. A build that
84
+ changes the Worker's code without changing the Runtime's version (an image built from a
85
+ branch) sets its own `NYLORUN_WORKER_VERSION`: without force Restate keeps the deployment it
86
+ already has at a URL and does not discover the Worker again, so the changed code would run
87
+ under the old deployment's registration, and a Worker whose services that deployment lacks
88
+ refuses to start. Outside a container (a development
89
+ Host) the registration replaces the deployment at that URL.
76
90
  `NYLORUN_MANAGEMENT_KEY_FILE` names the bootstrap secret (below).
77
91
  `NYLORUN_IDENTITY_FILE` names the identity
78
92
  file, a YAML list of the trusted issuers whose JWTs the Runtime API accepts
79
93
  (Host feature `trusted-issuers`), read once at boot; a malformed file stops the
80
- boot ([DEPLOYMENT.md](../DEPLOYMENT.md#trusted-issuers)). A process that runs `loop` sends its model
94
+ boot ([DEPLOYMENT.md](../../guides/DEPLOYMENT.md#trusted-issuers)). A process that runs `loop` sends its model
81
95
  calls to the gate at `NYLORUN_GATES_URL`; in a container it refuses to start
82
96
  without it and `NYLORUN_GATES_TOKEN`. That token is core's credential: the gate
83
97
  accepts only it for vault writes and token signing. Model, HTTP tool and
@@ -89,16 +103,16 @@ is refused with `409 run_stale`. A `gates` process needs only
89
103
  (`NYLORUN_GATES_LISTEN_HOST`, `NYLORUN_GATES_LISTEN_PORT`, default 4100, and
90
104
  `NYLORUN_GATES_ALLOWED_HOSTS`) and the Host's `tenant/` directory, which it
91
105
  never writes; it serves the database's one Tenant. A session's MCP servers and
92
- HTTP tools get their credentials from its attached vaults only: a `bearer` token
106
+ HTTP tools get their credentials from the vault of its Environment only: a `bearer` token
93
107
  or a `headers` map per URL, sent to an MCP gateway instead when it has `via`,
94
108
  with the session owner in its identity header
95
- ([DEPLOYMENT.md](../DEPLOYMENT.md#mcp-servers-and-http-tools)); the credential resolver is gone
109
+ ([DEPLOYMENT.md](../../guides/DEPLOYMENT.md#mcp-servers-and-http-tools)); the credential resolver is gone
96
110
  (protocol 10), and a process that still sets a `NYLORUN_RESOLVER_*` variable logs
97
111
  `resolver_removed` and ignores it. With `egress`
98
112
  (`--service gates,keys,egress`, for pod sandboxes) the same process runs
99
113
  egress-gate on `NYLORUN_EGRESS_LISTEN_HOST`:`NYLORUN_EGRESS_LISTEN_PORT`
100
114
  (default `0.0.0.0:4200`): a CONNECT proxy that admits a pod's egress token and
101
- tunnels only to the hosts its sandbox spec allows, on 443 or 80, never to a
115
+ tunnels only to the hosts its Environment allows (`network.allowedHosts`), on 443 or 80, never to a
102
116
  private address. `NYLORUN_PACKING` (`combined` or `split`) is logged at startup.
103
117
 
104
118
  The Host has one listener, which serves both APIs. Host work runs on the machine
@@ -155,9 +169,12 @@ with `415 unsupported_media_type`, except an artifact upload (`POST /v1/artifact
155
169
  presents a trusted issuer's token; keys (application and management)
156
170
  are refused from browsers before they are looked up. The Runtime sends no CORS headers, and answers
157
171
  `OPTIONS` with `204` and `Allow` only: the operator's reverse proxy answers preflights
158
- ([DEPLOYMENT.md](../DEPLOYMENT.md#calling-the-runtime-from-browsers-and-apps)). Missing or
172
+ ([DEPLOYMENT.md](../../guides/DEPLOYMENT.md#calling-the-runtime-from-browsers-and-apps)). Missing or
159
173
  unsupported protocol → `426` before authentication, unless the request sends neither
160
- `Nylorun-Protocol` nor `Authorization` (the Host serves protocols 4 to 10; protocol 10 removes
174
+ `Nylorun-Protocol` nor `Authorization` (the Host serves protocol 11 only; protocol 11 replaces
175
+ sandbox resources, the Tenant's default sandbox, inline sandboxes and user and installation
176
+ vaults with Environments, each one sandbox and one vault, under `/v1/environments`, and
177
+ removes the Management API's vault routes, so a protocol 10 client gets `426`; protocol 10 removes
161
178
  the MCP OAuth connect, the vault's `oauth` credentials and the credential resolver, and adds
162
179
  `headers` credentials, `via` and the identity header, manifest v6 and
163
180
  `POST /v1/tenant/mcp/preview`; protocol 9 makes the Runtime API an OAuth 2.1 resource server: a missing or rejected credential is `401
@@ -168,8 +185,7 @@ the Admin API, and those old paths answer `404` with no alias; protocol 7 remove
168
185
  derived principals, whose routes answer `404`; protocol 6 adds file and folder artifacts and message `parts`; at each turn's end the
169
186
  Runtime exports `/workspace/outputs` of the session's sandbox as a version of its
170
187
  `outputs` folder, read at `/v1/artifacts/{id}/versions/{n|latest}/tree`, `/files/{path}`,
171
- `/diff?from=` and `/zip`). Protocol 5 and later clients name no Tenant. A protocol 4 client's `Nylorun-Tenant` naming
172
- another Tenant (or malformed) and a Tenant that
188
+ `/diff?from=` and `/zip`). Clients name no Tenant (since protocol 5). A Tenant that
173
189
  could not be opened → opaque `404` with identical body. A path or method no route serves is `404 Route not found`
174
190
  once the caller is known.
175
191
 
@@ -193,13 +209,13 @@ https://cdn.jsdelivr.net/npm/@nylorun/runtime@<version>/dist/management-openapi.
193
209
 
194
210
  Each document has described tags, in the order a developer uses them. The Runtime API's are
195
211
  grouped (`x-tagGroups`): Agents (Agents, Definition files), Sessions (Sessions API, AG-UI,
196
- A2A), Sandboxes, Artifacts and Service (health, readiness, `/v1/me`, the JWKS and the
212
+ A2A), Environments, Artifacts and Service (health, readiness, `/v1/me`, the JWKS and the
197
213
  document; the `/openapi.json` alias is served but not listed). The Management API's are
198
- Tenant, Application keys, Models, Vaults, Signing keys and Settings. Each operation's
214
+ Tenant, Application keys, Models, Credential coverage, MCP servers, Signing keys and Settings. Each operation's
199
215
  `security` says which credentials it takes (application key and trusted issuer's token in
200
216
  the Runtime API; management key in the Management API), and its
201
217
  `x-nylorun-credentials` and `x-nylorun-scopes` (the subject scopes that reach it) fields say
202
- who may call it. Event streams are `text/event-stream` with an `itemSchema`. `runtime/openapi/` holds the committed snapshots: a change to a route changes
218
+ who may call it. Event streams are `text/event-stream` with an `itemSchema`. `runtime/server/openapi/` holds the committed snapshots: a change to a route changes
203
219
  them (`node scripts/build-openapi.mjs --write`), and `check-package` fails until they are
204
220
  updated.
205
221
 
@@ -237,18 +253,22 @@ const runtime = await startEphemeralRuntime({
237
253
 
238
254
  The package's own tests run every Session Store on Postgres: `npm test` gives each
239
255
  test file a database on the Docker test stack (`test/stack/compose.yaml`) and starts
240
- the stack when it is down; see [CONTRIBUTING.md](../CONTRIBUTING.md).
256
+ the stack when it is down; see [CONTRIBUTING.md](../../CONTRIBUTING.md).
241
257
 
242
258
  Save agents with `PUT /v1/agents/{agentId}` using the application principal (`saveAgent`
243
259
  in `@nylorun/agents`). The Runtime runs the agent from its manifest and never calls your
244
260
  code during a session: a definition with a tool that would (`tool({ run })`, a flow's tool
245
261
  stage) is refused. Your services are reached as tools: HTTP tools, POSTed from the Runtime,
246
- so their URLs must be reachable from it, and remote MCP servers. The operator gives
247
- each URL a vault credential, previews an MCP server's tools (`nylorun mcp inspect`),
248
- and the manifest says which tools are enabled, need approval or are deferred
249
- ([DEPLOYMENT.md](../DEPLOYMENT.md#mcp-servers-and-http-tools)). Model gateway
250
- and sandbox backend are Tenant configuration (vault / seed), not Host process
251
- env.
262
+ so their URLs must be reachable from it, and remote MCP servers. Each URL gets a
263
+ credential in the vault of the Environment a session names (`PUT
264
+ /v1/environments/{id}/secrets/{name}`), the operator previews an MCP server's tools
265
+ (`nylorun mcp inspect --environment <id>`),
266
+ and the manifest says which tools are enabled or deferred
267
+ ([DEPLOYMENT.md](../../guides/DEPLOYMENT.md#mcp-servers-and-http-tools)). Which calls wait for a
268
+ person, or are refused, is the Tenant's tool policy
269
+ ([DEPLOYMENT.md](../../guides/DEPLOYMENT.md#tool-policy)). Model gateway
270
+ and sandbox backend are Tenant configuration (the Tenant's own vault, Tenant
271
+ settings and seed), not Host process env.
252
272
 
253
273
  ## Session behaviour
254
274
 
@@ -259,11 +279,12 @@ definition/session APIs.
259
279
  Postgres transactions persist session checkpoints, command receipts, individual
260
280
  effects, waits and the record of session events — in the Tenant's database; history is
261
281
  read from Durable Streams (S2). An advance owns its session through a lease with
262
- an epoch; a Worker that takes over after a crash marks in-flight effects
263
- `uncertain`. Vault ciphertext needs that Tenant's own KEK.
282
+ an epoch; a Worker that takes over after a crash marks in-flight model effects
283
+ `uncertain`, and sends an interrupted MCP or HTTP tool call again with the same key (R5). Vault ciphertext needs that Tenant's own KEK.
264
284
 
265
- Agents that declare `.use(sandbox())` get Runtime-executed sandbox tools. The
266
- Tenant owns each sandbox; backend names are prefixed `nylorun-<tenant-id>-`.
285
+ A session that names an Environment of kind `virtual` or `pod` gets Runtime-executed
286
+ sandbox tools; the agent declares nothing. The Tenant owns each Environment; backend
287
+ names are prefixed `nylorun-<tenant-id>-`.
267
288
 
268
289
  ## Local Project workflow
269
290
 
@@ -293,9 +314,53 @@ Definitions have no `agent.run()`; applications use `@nylorun/agents`.
293
314
  ## Session reads
294
315
 
295
316
  Hosts advertising `session-reads` serve pinned session manifests, recorded usage, model-call
296
- pages and opt-in session/history/sandbox pagination. `calls-export` adds the Management API's
317
+ pages and opt-in session/history/Environment pagination. `calls-export` adds the Management API's
297
318
  unfiltered, resumable model ledger export (`GET /v1/tenant/calls/model`, a management key) in
298
- safe transaction order. Existing list/history
319
+ safe transaction order. `tool-calls` adds `GET /v1/sessions/{id}/calls/tool`: each tool call of
320
+ the session from the effect journal, with its input, status and sends. Existing list/history
299
321
  requests without `limit` retain their response shapes. These are public APIs for Studio,
300
322
  CLI and custom clients; clients never need database access. See the
301
323
  [API contracts, authorization, SDK examples and Studio handoff](https://github.com/nylorun/agents/blob/main/runtime/docs/session-reads.md).
324
+
325
+ ## Artifact pages
326
+
327
+ Hosts advertising `artifact-reads` accept `GET /v1/artifacts?limit=50`, returning
328
+ `{ artifacts: ArtifactListItem[], nextCursor: string | null }`. An explicit `limit`
329
+ of 1–200 opts into paging; without it the existing `{ artifacts: ArtifactView[] }`
330
+ list remains oldest first. Pages order by `(createdAt DESC, artifactId DESC)` and
331
+ contain metadata only: id, kind, name, current content type, latest version number,
332
+ optional session and labels, and creation/update timestamps. Use the existing
333
+ artifact detail, numbered version and download APIs for history and bytes.
334
+
335
+ Paged reads accept equality filters `sessionId`, `kind=file|folder` and repeated
336
+ `label=key=value` (all labels must match). `sessionId` includes only that session's
337
+ artifacts, excluding Tenant-owned artifacts. An application key acting as itself
338
+ sees every artifact; subjects and issuer tokens see only artifacts of their owned
339
+ sessions and permitted agents. Missing or inaccessible session filters return 404.
340
+ Management keys cannot use this Runtime API route.
341
+
342
+ Send `nextCursor` as `cursor` with the same normalized filters until it is null.
343
+ Cursors are bound to the Tenant, route and filters; invalid or mismatched cursors
344
+ return 400 (`cursor_invalid` or `cursor_mismatch`). Each page reapplies current
345
+ authorization and is an independent read: it is not a frozen snapshot. Newer
346
+ inserts require a fresh traversal; deleted rows disappear and versions/metadata
347
+ may change between pages. Creation time and ID remain the paging position.
348
+
349
+ These pages reuse the separate bounded, read-only Drizzle pool (four connections,
350
+ two-second statement timeout). Migration `0023_artifact_pages` adds only the
351
+ creation-order index; the existing session index also serves session pages in
352
+ reverse order. No data backfill, protocol bump or direct client database access is
353
+ required. Studio can discover `artifact-reads` through `/health` before enabling
354
+ a tenant-wide table; `@nylorun/agents` exposes the same API as `artifacts.page()`.
355
+
356
+ [Recorded query plans](docs/artifact-pages-evidence.json) use a disposable local
357
+ Postgres 17 fixture: the public SDK drained 10,006 artifacts in 201 pages of at
358
+ most 50 rows, without gaps or duplicates, in 796 ms. First/deep page queries used
359
+ backward index scans with a tuple index condition on the deep page (0.014/0.028 ms).
360
+ These are fixture timings, not production throughput estimates. Reproduce after
361
+ building dependencies with:
362
+
363
+ ```sh
364
+ ARTIFACT_READS_EVIDENCE="$PWD/runtime/server/docs/artifact-pages-evidence.json" \
365
+ npm test --workspace @nylorun/runtime -- test/http/artifact-pages.test.ts
366
+ ```
@@ -1,4 +1,10 @@
1
1
  import { type DurableExecution, type SandboxSignal, type StuckInvocation, type Wake, type WorkerHandlers } from "../../execution/types.js";
2
+ /**
3
+ * How long one send through the ingress keeps retrying while the ingress is unreachable,
4
+ * overloaded, or answers 404 because a just-registered service has not reached it yet. The
5
+ * outbox's grace (`OUTBOX_GRACE_MS` in `tenant/sweep.ts`) is longer.
6
+ */
7
+ export declare const SEND_RETRY_MS = 10000;
2
8
  export interface RestateTimeouts {
3
9
  /**
4
10
  * How long an invocation may run without journal progress before Restate
@@ -60,8 +66,8 @@ export interface RestateExecutionOptions {
60
66
  /**
61
67
  * Overwrite a deployment already registered at `workerAdvertisedUrl`, so a
62
68
  * restarted Worker with changed code takes effect. Default true, which suits
63
- * a stable URL on a developer machine. Rolling upgrades should advertise a
64
- * versioned URL instead (§14.6).
69
+ * a stable URL on a developer machine. A Host in a container advertises a
70
+ * versioned URL without it (`workerDeployment` in `infra/execution.ts`, §14.6).
65
71
  */
66
72
  forceRegistration?: boolean;
67
73
  /** How long `start` keeps retrying registration while Restate comes up. Default 60000. */
@@ -88,6 +94,7 @@ export declare class RestateExecution implements DurableExecution {
88
94
  private readonly inflight;
89
95
  private readonly names;
90
96
  private readonly ingressUrl;
97
+ private readonly ingress;
91
98
  private readonly adminUrl;
92
99
  private readonly sweepIntervalMs;
93
100
  constructor(options: RestateExecutionOptions);
@@ -123,7 +130,12 @@ export declare class RestateExecution implements DurableExecution {
123
130
  private runTracked;
124
131
  private staleSweepMs;
125
132
  private serviceOptions;
126
- /** One-way send through the ingress. Retries while the ingress is unreachable or overloaded. */
133
+ /**
134
+ * One-way send through the ingress (`@restatedev/restate-sdk-clients`). Every send carries
135
+ * an idempotency key, so the client's retries (`SEND_RETRY_MS`), and a send repeated after
136
+ * its answer was lost, start one invocation: the request's own key where it has one (an
137
+ * outbox row's, a timer's time), or one made for this call.
138
+ */
127
139
  private send;
128
140
  /** Registers the Worker endpoint with the admin API, retrying until Restate is up. */
129
141
  private register;