@nylorun/runtime 0.9.0-beta → 0.10.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 (129) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +96 -25
  3. package/dist/adapters/media.d.ts +2 -2
  4. package/dist/adapters/media.js +2 -2
  5. package/dist/adapters/observe.d.ts +2 -1
  6. package/dist/adapters/observe.js +2 -3
  7. package/dist/assets.d.ts +2 -2
  8. package/dist/assets.js +5 -3
  9. package/dist/core/executors.d.ts +11 -2
  10. package/dist/core/executors.js +20 -4
  11. package/dist/core/flow-host.d.ts +264 -0
  12. package/dist/core/flow-host.js +783 -0
  13. package/dist/core/limits.d.ts +40 -0
  14. package/dist/core/limits.js +76 -0
  15. package/dist/core/runtime.d.ts +5 -90
  16. package/dist/core/runtime.js +3 -1447
  17. package/dist/core/sandbox-routes.d.ts +35 -0
  18. package/dist/core/sandbox-routes.js +106 -0
  19. package/dist/core/store.d.ts +3 -1
  20. package/dist/core/store.js +14 -73
  21. package/dist/core/turn-manifest.d.ts +50 -0
  22. package/dist/core/turn-manifest.js +66 -0
  23. package/dist/host/config-for.d.ts +16 -0
  24. package/dist/host/config-for.js +45 -0
  25. package/dist/host/config.d.ts +24 -0
  26. package/dist/host/create-host.d.ts +21 -0
  27. package/dist/host/create-host.js +396 -0
  28. package/dist/host/environment.d.ts +13 -0
  29. package/dist/host/environment.js +53 -0
  30. package/dist/host/http.d.ts +35 -0
  31. package/dist/host/http.js +136 -0
  32. package/dist/host/index.d.ts +6 -0
  33. package/dist/host/index.js +5 -0
  34. package/dist/host/logger.d.ts +3 -0
  35. package/dist/host/logger.js +18 -0
  36. package/dist/host/main.d.ts +3 -0
  37. package/dist/host/main.js +142 -0
  38. package/dist/index.d.ts +7 -2
  39. package/dist/index.js +4 -2
  40. package/dist/launcher/commands.d.ts +24 -0
  41. package/dist/launcher/commands.js +260 -0
  42. package/dist/launcher/ensure-config.d.ts +16 -0
  43. package/dist/launcher/ensure-config.js +79 -0
  44. package/dist/launcher/errors.d.ts +12 -0
  45. package/dist/launcher/errors.js +22 -0
  46. package/dist/launcher/host-config.d.ts +17 -0
  47. package/dist/launcher/host-config.js +64 -0
  48. package/dist/launcher/host-state.d.ts +17 -0
  49. package/dist/launcher/host-state.js +61 -0
  50. package/dist/launcher/lifecycle.d.ts +66 -0
  51. package/dist/launcher/lifecycle.js +407 -0
  52. package/dist/launcher/locks.d.ts +26 -0
  53. package/dist/launcher/locks.js +117 -0
  54. package/dist/launcher/logs.d.ts +18 -0
  55. package/dist/launcher/logs.js +117 -0
  56. package/dist/launcher/main.d.ts +2 -0
  57. package/dist/launcher/main.js +66 -0
  58. package/dist/launcher/output.d.ts +11 -0
  59. package/dist/launcher/output.js +68 -0
  60. package/dist/launcher/paths.d.ts +20 -0
  61. package/dist/launcher/paths.js +36 -0
  62. package/dist/launcher/probe.d.ts +23 -0
  63. package/dist/launcher/probe.js +72 -0
  64. package/dist/launcher/protocol.d.ts +62 -0
  65. package/dist/launcher/protocol.js +1 -0
  66. package/dist/launcher/schema-guard.d.ts +6 -0
  67. package/dist/launcher/schema-guard.js +36 -0
  68. package/dist/launcher/spawn.d.ts +22 -0
  69. package/dist/launcher/spawn.js +85 -0
  70. package/dist/launcher/status.d.ts +6 -0
  71. package/dist/launcher/status.js +65 -0
  72. package/dist/mcp/connect.d.ts +1 -0
  73. package/dist/mcp/connect.js +2 -0
  74. package/dist/mcp/pool.d.ts +3 -1
  75. package/dist/mcp/pool.js +2 -1
  76. package/dist/mcp/stdio.d.ts +2 -0
  77. package/dist/mcp/stdio.js +3 -1
  78. package/dist/model/auth-store.d.ts +1 -1
  79. package/dist/model/auth-store.js +2 -2
  80. package/dist/model/models.d.ts +4 -0
  81. package/dist/model/models.js +22 -26
  82. package/dist/model/pi-model.js +1 -1
  83. package/dist/model/settings.d.ts +5 -1
  84. package/dist/model/settings.js +6 -2
  85. package/dist/sandbox/select.d.ts +4 -3
  86. package/dist/sandbox/select.js +15 -9
  87. package/dist/sandbox/share.d.ts +23 -0
  88. package/dist/sandbox/share.js +58 -0
  89. package/dist/tenant/envelope.d.ts +10 -0
  90. package/dist/tenant/envelope.js +54 -0
  91. package/dist/tenant/ephemeral.d.ts +31 -0
  92. package/dist/tenant/ephemeral.js +156 -0
  93. package/dist/tenant/logger.d.ts +9 -0
  94. package/dist/tenant/logger.js +25 -0
  95. package/dist/tenant/migration.d.ts +16 -0
  96. package/dist/tenant/migration.js +116 -0
  97. package/dist/tenant/module.d.ts +17 -0
  98. package/dist/tenant/module.js +315 -0
  99. package/dist/tenant/paths.d.ts +24 -0
  100. package/dist/tenant/paths.js +65 -0
  101. package/dist/tenant/pool.d.ts +10 -0
  102. package/dist/tenant/pool.js +54 -0
  103. package/dist/tenant/principals.d.ts +17 -0
  104. package/dist/tenant/principals.js +44 -0
  105. package/dist/tenant/quarantine-error.d.ts +6 -0
  106. package/dist/tenant/quarantine-error.js +15 -0
  107. package/dist/tenant/quarantine.d.ts +35 -0
  108. package/dist/tenant/quarantine.js +94 -0
  109. package/dist/tenant/reset.d.ts +21 -0
  110. package/dist/tenant/reset.js +125 -0
  111. package/dist/tenant/runtime.d.ts +80 -0
  112. package/dist/tenant/runtime.js +2256 -0
  113. package/dist/tenant/schema.d.ts +23 -0
  114. package/dist/tenant/schema.js +151 -0
  115. package/dist/tenant/status.d.ts +28 -0
  116. package/dist/tenant/status.js +95 -0
  117. package/dist/tenant/store-fs.d.ts +16 -0
  118. package/dist/tenant/store-fs.js +205 -0
  119. package/dist/tenant/store-memory.d.ts +19 -0
  120. package/dist/tenant/store-memory.js +100 -0
  121. package/dist/tenant/types.d.ts +125 -0
  122. package/dist/tenant/types.js +1 -0
  123. package/dist/vault/kek.d.ts +5 -2
  124. package/dist/vault/kek.js +7 -10
  125. package/dist/version.d.ts +1 -1
  126. package/dist/version.js +1 -1
  127. package/package.json +9 -6
  128. package/dist/core/main.js +0 -35
  129. /package/dist/{core/main.d.ts → host/config.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.10.0-beta
4
+
5
+ ### Minor Changes
6
+
7
+ - c49efed: **Runtime Clients and Admin API (supporting packages).**
8
+
9
+ - **core:** `AdminStatusSchema`, Project-link schemas, `ERROR_CODES` (launcher codes include `platform_unsupported`, `launcher_failed`, `downgrade_refused`), `admin-status` feature, `newPrincipalId`, `compareVersions`.
10
+ - **runtime:** `/v1/admin/status` (alias `/v1/admin/host`), loopback/`Origin`/content-type checks; the launcher ships as this package's `nylorun-runtime` bin (source under `src/launcher/`, not in `exports`) and runs the Host on the Node it runs on.
11
+ - **studio:** `nylorun-studio` binary; connects via `resolveConnection`; waits for `dev`; never calls Admin API or writes `.nylorun/`.
12
+
13
+ **Prerequisites:** developers install Node 24+ and `@nylorun/runtime` (`npm install --global @nylorun/runtime`) themselves; no package downloads Node or the Runtime, and there are no per-platform Runtime packages. Launcher commands: `version`, `up`, `down`, `restart`, `run`, `status`, `logs` (launcher protocol 1).
14
+
15
+ - c49efed: **Breaking (pre-1.0 minor):** Replace a single SQLite Runtime per Project or home directory with a **Runtime Host** that serves isolated **Tenants**, selected by `Nylorun-Tenant` and negotiated with `Nylorun-Protocol` (protocol `2`, feature `runtime-tenants`). Vocabulary: Host root + Tenant + Project link.
16
+
17
+ - **core:** `PROTOCOL_VERSION = 2`, `HOST_PROTOCOL`, `TENANT_HEADER`, `PROTOCOL_HEADER`, `newTenantId` / `isTenantId`, `checkCompatibility`; health schema gains `hostId` + `protocol` (`service: "nylorun-runtime"`); Tenant/admin wire schemas; Tenant model routes under `/v1/tenant/*`.
18
+ - **runtime:** Host process + Tenant module; Tenant model routes under `/v1/tenant/*`; `startEphemeralRuntime` for tests/embeds; executors via `PUT /v1/executors`; sandbox prefix `nylorun-<tenant-id>-`.
19
+ - **agents:** `createClient({ url, key, tenant })`; Transport sends Tenant + protocol headers; `/health` compatibility cache; `IncompatibleRuntimeError` with upgrade remedies.
20
+ - **cli:** Host root lifecycle (`runtime up|down|status|logs|restart|run`); Project link (`.nylorun/link.json` + `credentials.json`); `tenant` commands; `runtime status --env` exports `NYLORUN_RUNTIME_URL`, `NYLORUN_SERVER_KEY`, `NYLORUN_TENANT`; removed Project/home SQLite selectors.
21
+ - **studio:** `startStudio({ …, tenant: { id, name } })`; proxy forwards Tenant + protocol headers; UI shows Tenant name/short id.
22
+
23
+ - fd9fd87: Add workflows: compose agents and `tool()` with `Chain`, `Switch`, `Parallel`, `Map`, and `Loop`. A workflow is a registered runnable (`kind: "workflow"`) with the same session API as an agent — `export const agents`, `saveAgent` (saves referenced agents first), `createSession`, `input` (`content` or `data`), `observe({ follow })`, `pending`, `approve`, `cancel`. Slots (`{ run, id?, input? }`) reshape data between nodes. The flow engine (`runFlowDurable`) returns effects only; `harness/src/loop/` and `runDurable` are unchanged.
24
+
25
+ HostEffect gains flow kinds `agent`, `tool` (node), `fn`, and `verify`, each with `path`, `key`, and `iterations`. The Runtime drives agent nodes through the public session contract (linked sessions, shared sandbox via `PutSession.sandbox`), offers `fn` / `verify` again on lease expiry, and routes executor Actions by `(workflowId, key)` with claim-scoped `ctx.sandbox`. Optional `message.manifest` is a turn-only variant of the session pin (turn manifests). Studio shows the manifest tree, live node status, and session links. Examples under `examples/agents/{chain,switch,parallel,map,loop,ship-feature}/`.
26
+
27
+ ### Patch Changes
28
+
29
+ - Pin core to the tested release.
30
+ - Pin harness to the tested release.
31
+ - Updated dependencies [c49efed]
32
+ - Updated dependencies [c49efed]
33
+ - Updated dependencies [fd9fd87]
34
+ - Updated dependencies
35
+ - @nylorun/core@0.5.0-beta
36
+ - @nylorun/harness@0.19.0-beta
37
+
3
38
  ## 0.9.0-beta
4
39
 
5
40
  ### Minor Changes
package/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # @nylorun/runtime
2
2
 
3
- The independent OSS HTTP execution host consumes `@nylorun/harness/run` and `@nylorun/core/contracts`. Cloud installs published `@nylorun/harness` from npm and does not import this Runtime package. Client authoring, sessions and connected executors belong to `@nylorun/agents`.
3
+ The independent OSS **Runtime Host** consumes `@nylorun/harness/run` and
4
+ `@nylorun/core/contracts`. Cloud installs published `@nylorun/harness` from npm
5
+ and does not import this package. Client authoring and sessions belong to
6
+ `@nylorun/agents`; Tenant lifecycle management belongs to `@nylorun/admin`.
7
+ Vocabulary: [src/CONTEXT.md](./src/CONTEXT.md).
4
8
 
5
9
  Requires Node 24+. Build from the repository root:
6
10
 
@@ -9,43 +13,110 @@ npm install
9
13
  npm run build --workspace @nylorun/core
10
14
  npm run build --workspace @nylorun/harness
11
15
  npm run build --workspace @nylorun/runtime
12
- NYLORUN_SERVER_KEY='<separate-secret-at-least-16-characters>' npm start --workspace @nylorun/runtime
13
16
  ```
14
17
 
15
- Default address: `http://127.0.0.1:8787`. `HOST`, `PORT`, and `NYLORUN_SQLITE_PATH` configure binding and storage. Missing server credentials fail startup. The default model is a credential-free scripted development response. It does not simulate conversations, tools or provider compatibility.
18
+ ## Install and the launcher
16
19
 
17
- `NYLORUN_EXECUTORS_JSON` is an array of `{token, agentId, implementationVersion}`. Each secret must differ from the application server key and every other executor token. An optional `manifestHash` on an existing record is ignored and does not hide or reject work. SDK clients use `NYLORUN_RUNTIME_URL`, `NYLORUN_SERVER_KEY`, and `NYLORUN_EXECUTOR_KEY`; runtime executor scope is the agent id, never broadened by client input.
20
+ Developers install the Runtime as a prerequisite; nothing downloads it for
21
+ them ([runtime distribution](../docs/design/runtime-distribution.md)):
18
22
 
19
- For a model gateway, set `NYLORUN_MODEL_GATEWAY_URL`, `NYLORUN_MODEL_GATEWAY_KEY`, and `NYLORUN_MODEL`. The gateway receives `{model, call, context}` and returns a harness ModelCandidate or string. Provider credentials remain host configuration. The generic gateway requires an implementation of this envelope; it does not claim arbitrary provider wire compatibility. Alternatively supply a `ModelProvider` to `startRuntime`.
23
+ ```sh
24
+ node --version # 24 or newer
25
+ npm install --global @nylorun/runtime # provides nylorun-runtime
26
+ nylorun-runtime --version
27
+ ```
20
28
 
21
- ```ts
22
- import { startRuntime } from '@nylorun/runtime/core';
23
- const runtime = await startRuntime({
24
- sqlitePath: './nylorun.sqlite',
25
- serverToken: process.env.NYLORUN_SERVER_KEY!,
26
- executors: [], // Configure exact scoped executor credentials before customer actions.
27
- port: 8787,
28
- });
29
- // Application owns lifecycle.
30
- await runtime.close();
29
+ The **launcher** (`nylorun-runtime`, source in `src/launcher/`, not listed in
30
+ `exports`) starts, stops and restarts the Host on the Node it runs on, from
31
+ this package. Clients (CLI, desktop apps) find it on PATH and run it as a
32
+ process; prefer `nylorun runtime up`. A project may instead add this package
33
+ as a devDependency to pin its Runtime version; do not make it an application
34
+ production dependency. For tests and ephemeral embeds, use
35
+ `startEphemeralRuntime()` from `@nylorun/runtime/core`. See
36
+ [MIGRATION.md](../MIGRATION.md#runtime-clients-and-admin-api-breaking-beta) and
37
+ [building a desktop client](../docs/building-a-desktop-client.md).
38
+
39
+ Default address: loopback port `8787` (persisted in `host.json`). The Host root
40
+ is `NYLORUN_HOME` or `~/.nylorun`. Tenants live under `tenants/<tenantId>/`.
41
+
42
+ ## Layout
43
+
44
+ ```text
45
+ <host root>/
46
+ host.json # format 1: hostId, bind, port, runtimeVersion, …
47
+ host-state.json # pid, url — removed on shutdown
48
+ host-credentials.json # adminKey (0600)
49
+ runtime.log
50
+ tenants/<tenantId>/ # envelope, SQLite, KEK, logs, sandboxes, …
51
+ trash/ # deleted Tenants
31
52
  ```
32
53
 
33
- The session-first `/v1` API is specified in [HOST_CONTRACT.md](../harness/HOST_CONTRACT.md). `/health` confirms HTTP liveness; `/ready` checks SQLite and scheduler availability. Readiness is not a provider connectivity probe or functional correctness claim. Application tokens authorize definition/session APIs, while exact scoped executor credentials authorize authenticated SSE `/v1/executors/connect`, action discovery, claims, renewal and results. Session observers cannot claim actions.
54
+ ## HTTP surface
55
+
56
+ | Route | Auth | Notes |
57
+ | --- | --- | --- |
58
+ | `GET /health` | none | `service: "nylorun-runtime"`, `hostId`, protocol `{min,max,features}` (includes `admin-status`), pid |
59
+ | `GET /ready` | none | Listener up and Tenant discovery finished |
60
+ | `GET /v1/admin/status` | admin key | `AdminStatusSchema`; alias `GET /v1/admin/host` |
61
+ | `/v1/admin/tenants*` | admin key | Create / list / get / delete Tenants |
62
+ | `POST /v1/admin/host/shutdown` | admin key | Launcher-private; not in `@nylorun/admin` |
63
+ | `/v1/*` Tenant routes | application or executor | Require `Nylorun-Tenant` + `Nylorun-Protocol` |
64
+
65
+ Every route checks `Host` first (`421 host_rejected`), rejects any `Origin`
66
+ (`403 origin_rejected`, no CORS headers), and rejects non-JSON bodies with
67
+ `415 unsupported_media_type`. Missing or unsupported protocol → `426` before
68
+ authentication. Unknown, quarantined or rejected Tenant credentials → opaque
69
+ `404` with identical body.
70
+
71
+ ## Embedding and tests
34
72
 
35
- SQLite transactions persist session checkpoints, command receipts, individual effects/actions, waits and canonical history. Actions are committed before notifications. Accepted execution is detached from HTTP request lifetime. Restart schedules persisted runnable sessions; model intents lacking recorded outcomes become uncertain. Expired customer claims become uncertain and are not automatically repeated. `GET /v1/sessions/:id` exposes waits, outstanding actions and uncertain effect summaries. Cancellation ends the current turn; a later message can start another turn from the state preceding the cancelled turn. Canonical events and uncertain external work remain inspectable. Reconciliation operations are not implemented in this pass.
73
+ Tests and ephemeral embeds use `startEphemeralRuntime()` from
74
+ `@nylorun/runtime/core`: private Host on port 0, temporary Host root, one Tenant,
75
+ returns `{ url, tenantId, applicationKey, adminKey, close() }`.
36
76
 
37
- One process owns each SQLite database. A local PID lock rejects concurrent owners and recovers only when the recorded process no longer exists. Shared/network filesystems and multiple replicas are unsupported. Keep the database and its WAL/journal together; do not delete persisted effects separately from checkpoints. Invalid lock contents or PID reuse fail closed and require operator inspection. TLS and external ingress belong to deployment infrastructure.
77
+ Register executors with `PUT /v1/executors` using the application principal
78
+ (application-mode `connectAgents` does this with derived tokens). Model gateway
79
+ and sandbox backend are Tenant configuration (vault / seed), not Host process
80
+ env.
38
81
 
39
- The vault holds two scopes. End-user vaults each belong to one `ownerUserId` and store URL-bound credentials for outbound calls such as MCP. The host scope stores model provider credentials the Runtime uses when it calls a provider; Studio and `nylorun configure` can keep multiple providers and select the active provider/model. Secret values are encrypted with AES-256-GCM under a key-encryption key. Set `NYLORUN_VAULT_KEK` to 32 bytes encoded as base64, or allow the host to create `.nylorun/vault-kek` (mode 0600) on the first write. A database that already holds vault ciphertext will not open without that key. Reads return metadata only.
82
+ ## Session behaviour
40
83
 
41
- Agents that declare `.use(sandbox())` get Runtime-executed `bash`, `read`, `write`, `edit`, `grep` and `glob` tools. The Runtime owns each sandbox: one per session, created on the first sandbox tool call, stopped after its idle timeout, and reattached with its files on the next call, including after a restart. On first use the Runtime probes its backends in order and keeps the choice for its lifetime: a microsandbox microVM (macOS on Apple Silicon, or Linux with `/dev/kvm`), then the in-process virtual shell (`just-bash`). Set `NYLORUN_SANDBOX=microsandbox` or `virtual` to force one; a forced backend never falls back. `GET /v1/host/sandbox` reports the selection, and `nylorun doctor sandbox` shows the same probe. Virtual workspaces live in `sandboxes/` beside the SQLite file. Tool calls emit `sandbox.state` and `sandbox.exec` session events. A sandbox call interrupted by a crash becomes uncertain and is never re-run. `microsandbox` is an optional dependency pinned to an exact version. See [the sandbox design](../docs/design/sandboxes.md) for guarantees and limitations.
84
+ The session-first `/v1` API is specified in
85
+ [HOST_CONTRACT.md](../harness/HOST_CONTRACT.md). Application tokens authorize
86
+ definition/session APIs; executor credentials authorize authenticated SSE
87
+ connect, action discovery, claims, renewal and results. Session observers cannot
88
+ claim actions.
42
89
 
43
- ## Local project workflow
90
+ SQLite transactions persist session checkpoints, command receipts, individual
91
+ effects/actions, waits and canonical history — **per Tenant**. One process owns
92
+ each Tenant database (`.runtime-lock`). Keep the database and its WAL/journal
93
+ together. Vault ciphertext needs that Tenant's own KEK.
44
94
 
45
- Install `@nylorun/cli` (generated projects already include it). Use `nylorun up` and `nylorun dev` in a generated project. It loads the exported `agents` registry from `agents/index.ts`, starts this Runtime in a separate process, and, when no host model credential is stored, prompts in an interactive terminal and writes it to the vault. It then registers manifests, connects scoped SDK executors, and opens Studio. Use `--no-studio` or `--no-open` as needed. Runtime binds loopback on port 8787 (`PORT` override). The CLI creates separate server and executor credentials with mode 0600 in `.nylorun/local-credentials.json`; SQLite defaults to `.nylorun/runtime.sqlite`.
95
+ Agents that declare `.use(sandbox())` get Runtime-executed sandbox tools. The
96
+ Tenant owns each sandbox; backend names are prefixed `nylorun-<tenant-id>-`.
97
+
98
+ ## Local Project workflow
99
+
100
+ Install `@nylorun/cli` as a devDependency. `nylorun runtime up` starts the Host
101
+ via the launcher; `nylorun dev` creates or uses a Project link and runs
102
+ `src/main.ts` under `tsx watch`. Studio is `nylorun-studio` / `npm run studio`.
103
+
104
+ ```sh
105
+ eval "$(npx nylorun runtime status --env)"
106
+ # → NYLORUN_RUNTIME_URL, NYLORUN_SERVER_KEY, NYLORUN_TENANT
107
+ ```
46
108
 
47
- `nylorun serve [entry]` loads `dist/agents/index.js` by default and runs without Studio/watch, attaching to the Runtime that `nylorun up` started. It uses the same first-start prompt. `nylorun configure` replaces the host credential against a Runtime that is already listening. `nylorun studio` attaches using the project's local server credential (or `NYLORUN_SERVER_KEY`). Studio can replace an API-key credential. If `MODEL_PROVIDER`, `MODEL`, and `MODEL_PROVIDER_API_KEY` are already set, the first start seeds the vault from them and does not prompt. Existing `.env` model settings and `.nylorun/auth.json` are imported once the same way. No provider request occurs during that setup. `NYLORUN_DEV_MODEL=fixture` and `NYLORUN_MODEL_GATEWAY_URL` remain separate from the vault.
109
+ ## Troubleshooting
48
110
 
49
- Definitions have no `agent.run()`; applications use `@nylorun/agents`. The previous in-process host (`Runtime`, `serveAgents`, `openSession`, `httpModel`, `localSessions`, and the AG-UI route) has been removed; remove any remaining usage from applications.
111
+ | Symptom | What to do |
112
+ | --- | --- |
113
+ | Tenant `locked` | Another process holds `.runtime-lock`. `nylorun tenant status` prints `repair` |
114
+ | `kek-missing` | Restore the Tenant KEK beside the database |
115
+ | `corrupt` / `migration-failed` / `envelope-invalid` | Follow `nylorun tenant status` repair string |
116
+ | `schema-too-new` / `host_schema_newer` | Upgrade the Host build (`nylorun runtime restart`) |
117
+ | `426 protocol_unsupported` | Upgrade clients or Host to a compatible set |
118
+ | `421 host_rejected` / `403 origin_rejected` | Call from main process / Node; loopback Host only |
119
+ | Port in use | Explicit `--port` fails closed. First setup may pick a free loopback port |
120
+ | Logs | `nylorun runtime logs [--follow]` |
50
121
 
51
- Source changes restart the local stack. Start a new session after definition or implementation edits; live upgrades are deferred. `GET /v1/agents` and `GET /v1/sessions?agentId=...` are server-authenticated local discovery conveniences.
122
+ Definitions have no `agent.run()`; applications use `@nylorun/agents`.
@@ -19,6 +19,6 @@ export declare class MediaStore {
19
19
  private readMetadata;
20
20
  private directory;
21
21
  }
22
- export declare function localMedia(options?: {
23
- root?: string;
22
+ export declare function localMedia(options: {
23
+ root: string;
24
24
  }): MediaStore;
@@ -106,6 +106,6 @@ function omitCreatedAt(asset) {
106
106
  function safe(value) {
107
107
  return value !== "." && value !== ".." && /^[a-zA-Z0-9._-]+$/u.test(value);
108
108
  }
109
- export function localMedia(options = {}) {
110
- return new MediaStore(options.root ?? join(process.cwd(), ".data", "media"));
109
+ export function localMedia(options) {
110
+ return new MediaStore(options.root);
111
111
  }
@@ -2,7 +2,8 @@
2
2
  export declare function jsonlObserver(options: {
3
3
  readonly agentId: string;
4
4
  readonly sessionId: string;
5
- readonly root?: string;
5
+ readonly root: string;
6
+ readonly env?: Readonly<Record<string, string | undefined>>;
6
7
  }): {
7
8
  (event: {
8
9
  readonly type: string;
@@ -4,10 +4,9 @@ import { scrub } from "../redact.js";
4
4
  import { projectSecrets } from "../model/settings.js";
5
5
  /** Local JSONL observer. Writes raw engine events beside session durability files. */
6
6
  export function jsonlObserver(options) {
7
- const root = options.root ?? join(process.cwd(), ".data", "sessions");
8
- const directory = join(root, safe(options.agentId), safe(options.sessionId));
7
+ const directory = join(options.root, safe(options.agentId), safe(options.sessionId));
9
8
  const file = join(directory, "observe.jsonl");
10
- const secrets = projectSecrets();
9
+ const secrets = projectSecrets(options.root, options.env ?? {});
11
10
  return (event) => {
12
11
  const line = `${JSON.stringify(scrub(event, secrets))}\n`;
13
12
  void mkdir(directory, { recursive: true })
package/dist/assets.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- /** Resolve authored catalogs and subprocess assets in source or a built application. */
2
- export declare function projectAsset(path: string): string;
1
+ /** Resolve authored catalogs and subprocess assets under an explicit project root. */
2
+ export declare function projectAsset(path: string, root?: string): string;
package/dist/assets.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { resolve, relative } from "node:path";
3
- /** Resolve authored catalogs and subprocess assets in source or a built application. */
4
- export function projectAsset(path) {
5
- const root = process.cwd();
3
+ /** Resolve authored catalogs and subprocess assets under an explicit project root. */
4
+ export function projectAsset(path, root) {
5
+ if (!root) {
6
+ throw new Error("projectAsset requires an explicit project root");
7
+ }
6
8
  const source = resolve(root, path);
7
9
  if (relative(root, source).startsWith(".."))
8
10
  throw new Error("Asset must be inside the project.");
@@ -7,15 +7,23 @@ import type { ExecutorScope } from "@nylorun/core/contracts";
7
7
  export declare const hashToken: (token: string) => string;
8
8
  export interface ExecutorRecord extends ExecutorScope {
9
9
  readonly tokenHash: string;
10
- /** False for scopes supplied through NYLORUN_EXECUTORS_JSON, which are never written to SQLite. */
10
+ /** Always true under Runtime Tenants; startup env executors are removed (D8). */
11
11
  readonly persisted: boolean;
12
12
  readonly updatedAt: string;
13
+ /** Application principal that last registered this executor, when known. */
14
+ readonly principalId?: string;
13
15
  }
16
+ /**
17
+ * Rejects tokens that are weak, empty of agent identity, or whose hash equals any
18
+ * application principal hash (A5). Replaces the old `serverToken` equality check.
19
+ */
14
20
  export declare function assertExecutorCredential(executor: {
15
21
  agentId: string;
16
22
  implementationVersion: string;
17
23
  token: string;
18
- }, serverToken: string): void;
24
+ }, applicationHashes: readonly string[]): void;
25
+ /** Mint a 32-byte hex application/executor key for tests and CLI bootstrap. */
26
+ export declare function mintBearerToken(): string;
19
27
  export declare class ExecutorRegistry {
20
28
  private readonly byHash;
21
29
  private readonly byAgent;
@@ -26,6 +34,7 @@ export declare class ExecutorRegistry {
26
34
  upsert(record: ExecutorRecord): {
27
35
  rotated: boolean;
28
36
  previousHash?: string;
37
+ previousPrincipalId?: string;
29
38
  };
30
39
  remove(agentId: string): ExecutorRecord | undefined;
31
40
  private write;
@@ -1,4 +1,4 @@
1
- import { createHash, timingSafeEqual } from "node:crypto";
1
+ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
2
2
  const equals = (a, b) => {
3
3
  const aa = Buffer.from(a), bb = Buffer.from(b);
4
4
  return aa.length === bb.length && timingSafeEqual(aa, bb);
@@ -9,13 +9,23 @@ const equals = (a, b) => {
9
9
  * SQLite at rest. This is not a password KDF and must not be used for one.
10
10
  */
11
11
  export const hashToken = (token) => createHash("sha256").update(token, "utf8").digest("hex");
12
- export function assertExecutorCredential(executor, serverToken) {
12
+ /**
13
+ * Rejects tokens that are weak, empty of agent identity, or whose hash equals any
14
+ * application principal hash (A5). Replaces the old `serverToken` equality check.
15
+ */
16
+ export function assertExecutorCredential(executor, applicationHashes) {
17
+ const tokenHash = hashToken(executor.token);
18
+ const matchesApplication = applicationHashes.some((hash) => equals(hash, tokenHash));
13
19
  if (executor.token.length < 16 ||
14
- equals(executor.token, serverToken) ||
20
+ matchesApplication ||
15
21
  !executor.agentId ||
16
22
  !executor.implementationVersion)
17
23
  throw new Error("Executor tokens require independent credentials and an agent id");
18
24
  }
25
+ /** Mint a 32-byte hex application/executor key for tests and CLI bootstrap. */
26
+ export function mintBearerToken() {
27
+ return randomBytes(32).toString("hex");
28
+ }
19
29
  export class ExecutorRegistry {
20
30
  byHash = new Map();
21
31
  byAgent = new Map();
@@ -40,7 +50,13 @@ export class ExecutorRegistry {
40
50
  this.write(record);
41
51
  if (!previous || previous.tokenHash === record.tokenHash)
42
52
  return { rotated: false };
43
- return { rotated: true, previousHash: previous.tokenHash };
53
+ return {
54
+ rotated: true,
55
+ previousHash: previous.tokenHash,
56
+ ...(previous.principalId === undefined
57
+ ? {}
58
+ : { previousPrincipalId: previous.principalId }),
59
+ };
44
60
  }
45
61
  remove(agentId) {
46
62
  const record = this.byAgent.get(agentId);
@@ -0,0 +1,264 @@
1
+ import type { Action, LiveEvent } from "@nylorun/core/contracts";
2
+ import type { JsonValue, WorkflowManifest } from "@nylorun/core/define";
3
+ import type { EffectResolution, HostEffect } from "@nylorun/harness/run";
4
+ import { type FlowLimits } from "./limits.js";
5
+ /** Deterministic agent session id: derive(workflowSessionId, path, …parts). */
6
+ export declare function deriveSessionId(workflowSessionId: string, path: string, ...parts: string[]): string;
7
+ /**
8
+ * Linked agent session id for a flow agent effect.
9
+ * Verifier agents (`context.role === "verify-agent"`) get a unique session per
10
+ * (path, turnId, iterations) so prior verdicts cannot leak across Loop iterations.
11
+ */
12
+ export declare function deriveAgentEffectSessionId(workflowSessionId: string, path: string, request: {
13
+ readonly turnId: string;
14
+ readonly iterations?: string;
15
+ readonly context?: Record<string, unknown>;
16
+ }): string;
17
+ export declare function isWorkflowManifest(manifest: unknown): manifest is WorkflowManifest;
18
+ /** Flow tool-node effect: `tool` with path/key and no agent capabilityId. */
19
+ export declare function isFlowToolEffect(request: HostEffect): boolean;
20
+ export declare function isFlowEffect(request: HostEffect): boolean;
21
+ export type FlowLink = {
22
+ readonly workflowSessionId: string;
23
+ readonly path: string;
24
+ readonly effectId: string;
25
+ readonly turnId: string;
26
+ };
27
+ export type FlowHostStore = {
28
+ get<T = any>(table: string, id: string): T | undefined;
29
+ put(table: string, id: string, body: unknown): void;
30
+ all<T = any>(table: string): T[];
31
+ event(sessionId: string, turnId: string | null, type: string, payload: unknown): LiveEvent;
32
+ };
33
+ export type FlowHostSession = {
34
+ id: string;
35
+ agentId: string;
36
+ ownerUserId: string;
37
+ manifest: any;
38
+ manifestHash: string;
39
+ implementationVersion: string;
40
+ status: string;
41
+ activeTurnId: string | null;
42
+ info?: any;
43
+ vaultIds?: readonly string[];
44
+ credentialSelections?: readonly unknown[];
45
+ creation?: unknown;
46
+ waits?: unknown;
47
+ lastOutput?: JsonValue;
48
+ error?: string;
49
+ };
50
+ export type FlowWait = {
51
+ readonly sessionId: string;
52
+ readonly path: string;
53
+ readonly interactionId: string;
54
+ readonly kind: string;
55
+ readonly invocationId?: string;
56
+ readonly wait?: unknown;
57
+ readonly status?: string;
58
+ };
59
+ export type CancelSiblingResult = {
60
+ readonly cancelledActions: string[];
61
+ readonly uncertainActions: string[];
62
+ readonly agentSessionIds: string[];
63
+ };
64
+ export type CascadeCancelPlan = {
65
+ /** Linked agent sessions to cancel, deepest path first. */
66
+ readonly agentSessionIds: string[];
67
+ readonly pendingActionIds: string[];
68
+ readonly claimedActionIds: string[];
69
+ };
70
+ /**
71
+ * Resolve flow effects: agent (via session contract), tool nodes, fn, verify.
72
+ * Runtime never writes agent session state — only issues commands.
73
+ */
74
+ export declare function resolveFlowEffect(input: {
75
+ readonly request: HostEffect;
76
+ readonly store: FlowHostStore;
77
+ readonly session: FlowHostSession;
78
+ readonly limits?: FlowLimits;
79
+ readonly putLinkedSession: (args: {
80
+ id: string;
81
+ agentId: string;
82
+ ownerUserId: string;
83
+ vaultIds?: readonly string[];
84
+ credentialSelections?: readonly unknown[];
85
+ }) => FlowHostSession;
86
+ readonly sendMessage: (args: {
87
+ sessionId: string;
88
+ content?: string;
89
+ data?: JsonValue;
90
+ idempotencyKey: string;
91
+ requestId: string;
92
+ }) => {
93
+ turnId: string | null;
94
+ };
95
+ readonly readSession: (id: string) => FlowHostSession | undefined;
96
+ readonly publish: (event: LiveEvent) => void;
97
+ readonly notify: () => void;
98
+ readonly schedule: (sessionId: string) => void;
99
+ }): EffectResolution;
100
+ /** Active agent turns + pending/claimed actions for one workflow turn. */
101
+ export declare function countActiveFlowWork(store: FlowHostStore, workflowSessionId: string, turnId: string): number;
102
+ /** After an agent turn settles, wake the owning workflow if linked. */
103
+ export declare function wakeLinkedWorkflow(input: {
104
+ readonly agentSessionId: string;
105
+ readonly store: FlowHostStore;
106
+ readonly output?: JsonValue;
107
+ readonly failed?: boolean;
108
+ readonly cancelled?: boolean;
109
+ readonly error?: string;
110
+ readonly schedule: (sessionId: string) => void;
111
+ readonly publish: (event: LiveEvent) => void;
112
+ }): void;
113
+ /** On Runtime start: settle pending agent effects whose linked turns already finished. */
114
+ export declare function reconcilePendingAgentEffects(input: {
115
+ readonly store: FlowHostStore;
116
+ readonly schedule: (sessionId: string) => void;
117
+ readonly publish: (event: LiveEvent) => void;
118
+ }): void;
119
+ /** Re-offer orphaned fn/verify claims after a process restart (SD-P7 / WF-C9). */
120
+ export declare function reofferOrphanedFnVerifyClaims(store: FlowHostStore): number;
121
+ /**
122
+ * On lease expiry: fn/verify → pending again; tool nodes → uncertain (WF-R35, WF-R38).
123
+ * Returns whether any fn/verify was redelivered.
124
+ */
125
+ export declare function applyFlowLeaseExpiry(input: {
126
+ readonly store: FlowHostStore;
127
+ readonly action: Action;
128
+ readonly now?: number;
129
+ }): "reoffer" | "uncertain" | "skip";
130
+ /** Emit loop.verified / loop.decided after fn/verify actions complete. */
131
+ export declare function emitLoopActionEvents(input: {
132
+ readonly store: FlowHostStore;
133
+ readonly session: FlowHostSession;
134
+ readonly action: Action;
135
+ readonly value: unknown;
136
+ readonly publish: (event: LiveEvent) => void;
137
+ }): void;
138
+ export declare function emitNodeStarted(input: {
139
+ readonly store: FlowHostStore;
140
+ readonly session: FlowHostSession;
141
+ readonly publish: (event: LiveEvent) => void;
142
+ }, payload: {
143
+ path: string;
144
+ kind: string;
145
+ key: string;
146
+ iterations?: string;
147
+ }): LiveEvent;
148
+ export declare function emitNodeCompleted(input: {
149
+ readonly store: FlowHostStore;
150
+ readonly session: FlowHostSession;
151
+ readonly publish: (event: LiveEvent) => void;
152
+ }, payload: {
153
+ path: string;
154
+ iterations?: string;
155
+ }): LiveEvent;
156
+ export declare function emitNodeFailed(input: {
157
+ readonly store: FlowHostStore;
158
+ readonly session: FlowHostSession;
159
+ readonly publish: (event: LiveEvent) => void;
160
+ }, payload: {
161
+ path: string;
162
+ iterations?: string;
163
+ error: {
164
+ code: string;
165
+ message: string;
166
+ path?: string;
167
+ };
168
+ }): LiveEvent;
169
+ export declare function emitSwitchSelected(input: {
170
+ readonly store: FlowHostStore;
171
+ readonly session: FlowHostSession;
172
+ readonly publish: (event: LiveEvent) => void;
173
+ }, payload: {
174
+ path: string;
175
+ case: string;
176
+ }): LiveEvent;
177
+ export declare function emitMapItems(input: {
178
+ readonly store: FlowHostStore;
179
+ readonly session: FlowHostSession;
180
+ readonly publish: (event: LiveEvent) => void;
181
+ }, payload: {
182
+ path: string;
183
+ count: number;
184
+ }): LiveEvent;
185
+ /** Path depth for deepest-first cancel ordering. */
186
+ export declare function pathDepth(path: string): number;
187
+ /**
188
+ * Plan a cancel cascade for a workflow session: linked agents deepest first,
189
+ * then pending → cancelled and claimed → uncertain actions (SD-P11 / WF-R53).
190
+ */
191
+ export declare function planCancelCascade(input: {
192
+ readonly store: FlowHostStore;
193
+ readonly workflowSessionId: string;
194
+ readonly turnId: string | null;
195
+ }): CascadeCancelPlan;
196
+ /**
197
+ * Cancel sibling work under a Parallel/Map parent when one branch fails (PAR-R6).
198
+ * Agent turns listed in `agentSessionIds` are returned for the caller to cancel;
199
+ * pending actions → cancelled, claimed tool actions → uncertain.
200
+ * When `cancelEffectIds` is provided, those effects are marked cancelled and their
201
+ * paths are included in the sibling path set.
202
+ */
203
+ export declare function cancelSiblingWork(input: {
204
+ readonly store: FlowHostStore;
205
+ readonly workflowSessionId: string;
206
+ readonly turnId: string;
207
+ /** Paths of siblings still running (not the failed branch). */
208
+ readonly siblingPaths?: readonly string[];
209
+ /** Effect ids the flow engine marked for fail-fast cancel. */
210
+ readonly cancelEffectIds?: readonly string[];
211
+ }): CancelSiblingResult;
212
+ /**
213
+ * Apply cancel fencing to workflow-session actions (pending→cancelled, claimed→uncertain).
214
+ */
215
+ export declare function fenceWorkflowActions(input: {
216
+ readonly store: FlowHostStore;
217
+ readonly workflowSessionId: string;
218
+ readonly turnId: string | null;
219
+ }): {
220
+ cancelled: string[];
221
+ uncertain: string[];
222
+ };
223
+ /**
224
+ * Aggregate human waits across the workflow session and linked agent sessions (WF-R51).
225
+ */
226
+ export declare function aggregateWaits(input: {
227
+ readonly store: FlowHostStore;
228
+ readonly workflowSessionId: string;
229
+ }): FlowWait[];
230
+ /**
231
+ * Look up which session owns an interaction id. Used for 409 foreign approvals (WF-R52).
232
+ */
233
+ export declare function findInteractionOwner(input: {
234
+ readonly store: FlowHostStore;
235
+ readonly workflowSessionId: string;
236
+ readonly interactionId: string;
237
+ }): {
238
+ sessionId: string;
239
+ path: string;
240
+ } | undefined;
241
+ /**
242
+ * If approve/respond targets an interaction owned by a linked agent session,
243
+ * return a 409 message naming that session (WF-R52 / LOOP-A4).
244
+ */
245
+ export declare function foreignInteractionConflict(input: {
246
+ readonly store: FlowHostStore;
247
+ readonly workflowSessionId: string;
248
+ readonly interactionId: string;
249
+ }): {
250
+ status: 409;
251
+ message: string;
252
+ ownerSessionId: string;
253
+ } | undefined;
254
+ /**
255
+ * When concurrency slots free, re-enter the workflow so queued effects can start
256
+ * (status stays `queued` until `resolveFlowEffect` dispatches them).
257
+ */
258
+ export declare function wakeForQueuedEffects(input: {
259
+ readonly store: FlowHostStore;
260
+ readonly workflowSessionId: string;
261
+ readonly turnId: string;
262
+ readonly limits: FlowLimits;
263
+ readonly schedule: (sessionId: string) => void;
264
+ }): boolean;