@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.
- package/CHANGELOG.md +35 -0
- package/README.md +96 -25
- package/dist/adapters/media.d.ts +2 -2
- package/dist/adapters/media.js +2 -2
- package/dist/adapters/observe.d.ts +2 -1
- package/dist/adapters/observe.js +2 -3
- package/dist/assets.d.ts +2 -2
- package/dist/assets.js +5 -3
- package/dist/core/executors.d.ts +11 -2
- package/dist/core/executors.js +20 -4
- package/dist/core/flow-host.d.ts +264 -0
- package/dist/core/flow-host.js +783 -0
- package/dist/core/limits.d.ts +40 -0
- package/dist/core/limits.js +76 -0
- package/dist/core/runtime.d.ts +5 -90
- package/dist/core/runtime.js +3 -1447
- package/dist/core/sandbox-routes.d.ts +35 -0
- package/dist/core/sandbox-routes.js +106 -0
- package/dist/core/store.d.ts +3 -1
- package/dist/core/store.js +14 -73
- package/dist/core/turn-manifest.d.ts +50 -0
- package/dist/core/turn-manifest.js +66 -0
- package/dist/host/config-for.d.ts +16 -0
- package/dist/host/config-for.js +45 -0
- package/dist/host/config.d.ts +24 -0
- package/dist/host/create-host.d.ts +21 -0
- package/dist/host/create-host.js +396 -0
- package/dist/host/environment.d.ts +13 -0
- package/dist/host/environment.js +53 -0
- package/dist/host/http.d.ts +35 -0
- package/dist/host/http.js +136 -0
- package/dist/host/index.d.ts +6 -0
- package/dist/host/index.js +5 -0
- package/dist/host/logger.d.ts +3 -0
- package/dist/host/logger.js +18 -0
- package/dist/host/main.d.ts +3 -0
- package/dist/host/main.js +142 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +4 -2
- package/dist/launcher/commands.d.ts +24 -0
- package/dist/launcher/commands.js +260 -0
- package/dist/launcher/ensure-config.d.ts +16 -0
- package/dist/launcher/ensure-config.js +79 -0
- package/dist/launcher/errors.d.ts +12 -0
- package/dist/launcher/errors.js +22 -0
- package/dist/launcher/host-config.d.ts +17 -0
- package/dist/launcher/host-config.js +64 -0
- package/dist/launcher/host-state.d.ts +17 -0
- package/dist/launcher/host-state.js +61 -0
- package/dist/launcher/lifecycle.d.ts +66 -0
- package/dist/launcher/lifecycle.js +407 -0
- package/dist/launcher/locks.d.ts +26 -0
- package/dist/launcher/locks.js +117 -0
- package/dist/launcher/logs.d.ts +18 -0
- package/dist/launcher/logs.js +117 -0
- package/dist/launcher/main.d.ts +2 -0
- package/dist/launcher/main.js +66 -0
- package/dist/launcher/output.d.ts +11 -0
- package/dist/launcher/output.js +68 -0
- package/dist/launcher/paths.d.ts +20 -0
- package/dist/launcher/paths.js +36 -0
- package/dist/launcher/probe.d.ts +23 -0
- package/dist/launcher/probe.js +72 -0
- package/dist/launcher/protocol.d.ts +62 -0
- package/dist/launcher/protocol.js +1 -0
- package/dist/launcher/schema-guard.d.ts +6 -0
- package/dist/launcher/schema-guard.js +36 -0
- package/dist/launcher/spawn.d.ts +22 -0
- package/dist/launcher/spawn.js +85 -0
- package/dist/launcher/status.d.ts +6 -0
- package/dist/launcher/status.js +65 -0
- package/dist/mcp/connect.d.ts +1 -0
- package/dist/mcp/connect.js +2 -0
- package/dist/mcp/pool.d.ts +3 -1
- package/dist/mcp/pool.js +2 -1
- package/dist/mcp/stdio.d.ts +2 -0
- package/dist/mcp/stdio.js +3 -1
- package/dist/model/auth-store.d.ts +1 -1
- package/dist/model/auth-store.js +2 -2
- package/dist/model/models.d.ts +4 -0
- package/dist/model/models.js +22 -26
- package/dist/model/pi-model.js +1 -1
- package/dist/model/settings.d.ts +5 -1
- package/dist/model/settings.js +6 -2
- package/dist/sandbox/select.d.ts +4 -3
- package/dist/sandbox/select.js +15 -9
- package/dist/sandbox/share.d.ts +23 -0
- package/dist/sandbox/share.js +58 -0
- package/dist/tenant/envelope.d.ts +10 -0
- package/dist/tenant/envelope.js +54 -0
- package/dist/tenant/ephemeral.d.ts +31 -0
- package/dist/tenant/ephemeral.js +156 -0
- package/dist/tenant/logger.d.ts +9 -0
- package/dist/tenant/logger.js +25 -0
- package/dist/tenant/migration.d.ts +16 -0
- package/dist/tenant/migration.js +116 -0
- package/dist/tenant/module.d.ts +17 -0
- package/dist/tenant/module.js +315 -0
- package/dist/tenant/paths.d.ts +24 -0
- package/dist/tenant/paths.js +65 -0
- package/dist/tenant/pool.d.ts +10 -0
- package/dist/tenant/pool.js +54 -0
- package/dist/tenant/principals.d.ts +17 -0
- package/dist/tenant/principals.js +44 -0
- package/dist/tenant/quarantine-error.d.ts +6 -0
- package/dist/tenant/quarantine-error.js +15 -0
- package/dist/tenant/quarantine.d.ts +35 -0
- package/dist/tenant/quarantine.js +94 -0
- package/dist/tenant/reset.d.ts +21 -0
- package/dist/tenant/reset.js +125 -0
- package/dist/tenant/runtime.d.ts +80 -0
- package/dist/tenant/runtime.js +2256 -0
- package/dist/tenant/schema.d.ts +23 -0
- package/dist/tenant/schema.js +151 -0
- package/dist/tenant/status.d.ts +28 -0
- package/dist/tenant/status.js +95 -0
- package/dist/tenant/store-fs.d.ts +16 -0
- package/dist/tenant/store-fs.js +205 -0
- package/dist/tenant/store-memory.d.ts +19 -0
- package/dist/tenant/store-memory.js +100 -0
- package/dist/tenant/types.d.ts +125 -0
- package/dist/tenant/types.js +1 -0
- package/dist/vault/kek.d.ts +5 -2
- package/dist/vault/kek.js +7 -10
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +9 -6
- package/dist/core/main.js +0 -35
- /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
|
|
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
|
-
|
|
18
|
+
## Install and the launcher
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
Developers install the Runtime as a prerequisite; nothing downloads it for
|
|
21
|
+
them ([runtime distribution](../docs/design/runtime-distribution.md)):
|
|
18
22
|
|
|
19
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
82
|
+
## Session behaviour
|
|
40
83
|
|
|
41
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
109
|
+
## Troubleshooting
|
|
48
110
|
|
|
49
|
-
|
|
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
|
-
|
|
122
|
+
Definitions have no `agent.run()`; applications use `@nylorun/agents`.
|
package/dist/adapters/media.d.ts
CHANGED
package/dist/adapters/media.js
CHANGED
|
@@ -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
|
|
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
|
|
5
|
+
readonly root: string;
|
|
6
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
6
7
|
}): {
|
|
7
8
|
(event: {
|
|
8
9
|
readonly type: string;
|
package/dist/adapters/observe.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
4
|
-
export function projectAsset(path) {
|
|
5
|
-
|
|
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.");
|
package/dist/core/executors.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
-
},
|
|
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;
|
package/dist/core/executors.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 {
|
|
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;
|