@nylorun/runtime 0.13.0-beta → 0.15.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +434 -0
- package/README.md +137 -41
- package/dist/adapters/execution/restate.d.ts +7 -2
- package/dist/adapters/execution/restate.js +40 -10
- package/dist/adapters/replication/pgoutput.d.ts +21 -0
- package/dist/adapters/replication/pgoutput.js +170 -0
- package/dist/adapters/streams/s2.js +37 -20
- package/dist/admin-openapi.json +570 -0
- package/dist/api/a2a/card.d.ts +15 -0
- package/dist/api/a2a/card.js +24 -0
- package/dist/api/a2a/endpoint.d.ts +8 -0
- package/dist/api/a2a/endpoint.js +81 -0
- package/dist/api/a2a/protocol.d.ts +144 -0
- package/dist/api/a2a/protocol.js +188 -0
- package/dist/api/a2a/routes.d.ts +19 -0
- package/dist/api/a2a/routes.js +289 -0
- package/dist/api/a2a/tasks.d.ts +50 -0
- package/dist/api/a2a/tasks.js +186 -0
- package/dist/api/ag-ui/endpoint.d.ts +8 -0
- package/dist/api/ag-ui/endpoint.js +126 -0
- package/dist/api/ag-ui/history.d.ts +7 -0
- package/dist/api/ag-ui/history.js +60 -0
- package/dist/api/ag-ui/routes.d.ts +32 -0
- package/dist/api/ag-ui/routes.js +305 -0
- package/dist/api/ag-ui/session-id.d.ts +1 -0
- package/dist/api/ag-ui/session-id.js +12 -0
- package/dist/api/ag-ui/sse.d.ts +9 -0
- package/dist/api/ag-ui/sse.js +7 -0
- package/dist/api/ag-ui/translate.d.ts +38 -0
- package/dist/api/ag-ui/translate.js +222 -0
- package/dist/api/components.d.ts +8065 -0
- package/dist/api/components.js +82 -0
- package/dist/api/gate/keys.d.ts +14 -0
- package/dist/api/gate/keys.js +59 -0
- package/dist/api/gate/routes.d.ts +34 -0
- package/dist/api/gate/routes.js +235 -0
- package/dist/api/http/app.d.ts +18 -0
- package/dist/api/http/app.js +58 -0
- package/dist/api/http/body.d.ts +4 -0
- package/dist/api/http/body.js +37 -0
- package/dist/api/http/define.d.ts +39 -0
- package/dist/api/http/define.js +122 -0
- package/dist/api/http/respond.d.ts +9 -0
- package/dist/api/http/respond.js +44 -0
- package/dist/api/http/routes/access.d.ts +24 -0
- package/dist/api/http/routes/access.js +266 -0
- package/dist/api/http/routes/actions.d.ts +8 -0
- package/dist/api/http/routes/actions.js +76 -0
- package/dist/api/http/routes/endpoints.d.ts +7 -0
- package/dist/api/http/routes/endpoints.js +100 -0
- package/dist/api/http/routes/sessions.d.ts +3 -0
- package/dist/api/http/routes/sessions.js +205 -0
- package/dist/api/http/routes/tenant.d.ts +11 -0
- package/dist/api/http/routes/tenant.js +244 -0
- package/dist/api/http/routes/vaults.d.ts +8 -0
- package/dist/api/http/routes/vaults.js +167 -0
- package/dist/api/openapi.d.ts +19 -0
- package/dist/api/openapi.js +137 -0
- package/dist/api/route.d.ts +12 -0
- package/dist/api/route.js +4 -0
- package/dist/core/bearer.d.ts +8 -0
- package/dist/core/bearer.js +11 -0
- package/dist/core/flow-host.d.ts +7 -15
- package/dist/core/flow-host.js +16 -41
- package/dist/core/sandbox-routes.d.ts +12 -7
- package/dist/core/sandbox-routes.js +24 -25
- package/dist/execution/memory.d.ts +8 -5
- package/dist/execution/memory.js +33 -13
- package/dist/execution/types.d.ts +38 -6
- package/dist/execution/types.js +12 -3
- package/dist/gates/contract.d.ts +51 -0
- package/dist/gates/contract.js +37 -0
- package/dist/gates/handler.d.ts +21 -0
- package/dist/gates/handler.js +55 -0
- package/dist/gates/http-client.d.ts +12 -0
- package/dist/gates/http-client.js +160 -0
- package/dist/gates/in-process.d.ts +32 -0
- package/dist/gates/in-process.js +42 -0
- package/dist/gates/inflight.d.ts +39 -0
- package/dist/gates/inflight.js +70 -0
- package/dist/gates/mcp-handler.d.ts +35 -0
- package/dist/gates/mcp-handler.js +142 -0
- package/dist/gates/meter.d.ts +24 -0
- package/dist/gates/meter.js +165 -0
- package/dist/gates/model-gate.d.ts +45 -0
- package/dist/gates/model-gate.js +1 -0
- package/dist/gates/tenant-vaults.d.ts +64 -0
- package/dist/gates/tenant-vaults.js +94 -0
- package/dist/gates/tool-calls.d.ts +51 -0
- package/dist/gates/tool-calls.js +64 -0
- package/dist/gates/tool-client.d.ts +12 -0
- package/dist/gates/tool-client.js +201 -0
- package/dist/gates/tool-contract.d.ts +116 -0
- package/dist/gates/tool-contract.js +64 -0
- package/dist/gates/tool-gate.d.ts +45 -0
- package/dist/gates/tool-gate.js +10 -0
- package/dist/host/admin-api.d.ts +23 -0
- package/dist/host/admin-api.js +74 -0
- package/dist/host/app.d.ts +34 -0
- package/dist/host/app.js +181 -0
- package/dist/host/config-for.d.ts +3 -1
- package/dist/host/config-for.js +3 -2
- package/dist/host/config.d.ts +11 -0
- package/dist/host/cors.d.ts +27 -0
- package/dist/host/cors.js +64 -0
- package/dist/host/create-host.d.ts +36 -8
- package/dist/host/create-host.js +161 -330
- package/dist/host/execution.d.ts +20 -18
- package/dist/host/execution.js +2 -2
- package/dist/host/gates.d.ts +58 -0
- package/dist/host/gates.js +127 -0
- package/dist/host/http.d.ts +20 -4
- package/dist/host/http.js +81 -23
- package/dist/host/index.d.ts +1 -1
- package/dist/host/main.js +189 -58
- package/dist/host/stack-config.d.ts +91 -19
- package/dist/host/stack-config.js +246 -17
- package/dist/infra/database.d.ts +2 -2
- package/dist/infra/execution.d.ts +5 -5
- package/dist/infra/execution.js +8 -6
- package/dist/infra/index.d.ts +1 -1
- package/dist/infra/workers.d.ts +17 -17
- package/dist/infra/workers.js +9 -9
- package/dist/keys/client.d.ts +9 -0
- package/dist/keys/client.js +70 -0
- package/dist/keys/contract.d.ts +24 -0
- package/dist/keys/contract.js +12 -0
- package/dist/keys/keys.d.ts +41 -0
- package/dist/keys/keys.js +50 -0
- package/dist/mcp/connect.d.ts +43 -3
- package/dist/mcp/connect.js +12 -5
- package/dist/mcp/pool.d.ts +39 -3
- package/dist/mcp/pool.js +74 -11
- package/dist/model/catalog.d.ts +2 -10
- package/dist/model/classify.d.ts +13 -0
- package/dist/model/classify.js +61 -0
- package/dist/model/models.d.ts +10 -1
- package/dist/model/models.js +14 -6
- package/dist/model/pi-model.d.ts +17 -2
- package/dist/model/pi-model.js +366 -222
- package/dist/openapi.json +25889 -0
- package/dist/record/append.d.ts +51 -0
- package/dist/record/append.js +20 -0
- package/dist/record/envelope.d.ts +19 -0
- package/dist/record/envelope.js +49 -0
- package/dist/record/index.d.ts +11 -0
- package/dist/record/index.js +11 -0
- package/dist/sandbox/manager.d.ts +13 -1
- package/dist/sandbox/manager.js +8 -11
- package/dist/sandbox/resolve.d.ts +33 -0
- package/dist/sandbox/resolve.js +81 -0
- package/dist/sandbox/session-sandbox.d.ts +11 -0
- package/dist/sandbox/session-sandbox.js +51 -0
- package/dist/sandbox/share.d.ts +14 -2
- package/dist/sandbox/share.js +33 -3
- package/dist/sandbox/tenant-config.d.ts +37 -0
- package/dist/sandbox/tenant-config.js +51 -0
- package/dist/sandbox/tools.d.ts +7 -9
- package/dist/sandbox/tools.js +11 -3
- package/dist/sandbox/types.d.ts +8 -2
- package/dist/store/postgres/connect.d.ts +25 -9
- package/dist/store/postgres/connect.js +27 -10
- package/dist/store/postgres/db.d.ts +16 -0
- package/dist/store/postgres/db.js +28 -0
- package/dist/store/postgres/drizzle/0000_foundation.sql +22 -0
- package/dist/store/postgres/drizzle/0001_baseline.sql +255 -0
- package/dist/store/postgres/drizzle/0002_stream_relay.sql +4 -0
- package/dist/store/postgres/drizzle/0003_tool_crossings.sql +9 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +34 -0
- package/dist/store/postgres/migrate.d.ts +68 -0
- package/dist/store/postgres/migrate.js +183 -0
- package/dist/store/postgres/record-writer.d.ts +4 -0
- package/dist/store/postgres/record-writer.js +34 -0
- package/dist/store/postgres/record.d.ts +5 -0
- package/dist/store/postgres/record.js +60 -0
- package/dist/store/postgres/schema.d.ts +3096 -0
- package/dist/store/postgres/schema.js +363 -0
- package/dist/store/postgres/store.d.ts +2 -4
- package/dist/store/postgres/store.js +590 -434
- package/dist/store/postgres/tenant.d.ts +56 -0
- package/dist/store/postgres/tenant.js +154 -0
- package/dist/store/types.d.ts +183 -132
- package/dist/store/types.js +0 -1
- package/dist/streams/basin.d.ts +18 -1
- package/dist/streams/basin.js +34 -2
- package/dist/streams/control.d.ts +18 -0
- package/dist/streams/control.js +33 -0
- package/dist/streams/memory.d.ts +3 -1
- package/dist/streams/memory.js +14 -8
- package/dist/streams/relay/core.d.ts +35 -0
- package/dist/streams/relay/core.js +366 -0
- package/dist/streams/relay/types.d.ts +78 -0
- package/dist/streams/relay/types.js +8 -0
- package/dist/streams/types.d.ts +71 -36
- package/dist/streams/types.js +18 -53
- package/dist/tenant/access-policy.d.ts +30 -0
- package/dist/tenant/access-policy.js +52 -0
- package/dist/tenant/actions.d.ts +10 -149
- package/dist/tenant/actions.js +5 -167
- package/dist/tenant/advance.d.ts +7 -2
- package/dist/tenant/advance.js +125 -14
- package/dist/tenant/auth.d.ts +19 -17
- package/dist/tenant/auth.js +90 -93
- package/dist/tenant/browser.d.ts +15 -0
- package/dist/tenant/browser.js +39 -0
- package/dist/tenant/cause.d.ts +25 -0
- package/dist/tenant/cause.js +45 -0
- package/dist/tenant/commands.d.ts +18 -2
- package/dist/tenant/commands.js +111 -115
- package/dist/tenant/context.d.ts +101 -21
- package/dist/tenant/context.js +14 -10
- package/dist/tenant/delivery-token.d.ts +41 -0
- package/dist/tenant/delivery-token.js +104 -0
- package/dist/tenant/delivery.d.ts +62 -0
- package/dist/tenant/delivery.js +481 -0
- package/dist/tenant/effects.d.ts +20 -1
- package/dist/tenant/effects.js +159 -42
- package/dist/tenant/endpoints.d.ts +67 -0
- package/dist/tenant/endpoints.js +90 -0
- package/dist/tenant/ephemeral.d.ts +22 -7
- package/dist/tenant/ephemeral.js +83 -125
- package/dist/tenant/history.d.ts +64 -0
- package/dist/tenant/history.js +120 -0
- package/dist/tenant/http.d.ts +8 -9
- package/dist/tenant/http.js +13 -27
- package/dist/tenant/jwt.d.ts +16 -0
- package/dist/tenant/jwt.js +50 -0
- package/dist/tenant/model-setting.d.ts +2 -2
- package/dist/tenant/module.d.ts +17 -28
- package/dist/tenant/module.js +133 -279
- package/dist/tenant/outbound.d.ts +44 -0
- package/dist/tenant/outbound.js +127 -0
- package/dist/tenant/paths.d.ts +11 -16
- package/dist/tenant/paths.js +24 -68
- package/dist/tenant/pool.d.ts +1 -3
- package/dist/tenant/pool.js +1 -19
- package/dist/tenant/principals.d.ts +23 -11
- package/dist/tenant/principals.js +61 -49
- package/dist/tenant/reset.d.ts +0 -4
- package/dist/tenant/reset.js +0 -5
- package/dist/tenant/runtime.d.ts +28 -20
- package/dist/tenant/runtime.js +95 -64
- package/dist/tenant/scheduler.d.ts +10 -3
- package/dist/tenant/scheduler.js +9 -5
- package/dist/tenant/session-streams.d.ts +127 -0
- package/dist/tenant/session-streams.js +404 -0
- package/dist/tenant/sessions.d.ts +28 -4
- package/dist/tenant/sessions.js +118 -24
- package/dist/tenant/signing-keys.d.ts +55 -0
- package/dist/tenant/signing-keys.js +172 -0
- package/dist/tenant/slim.d.ts +8 -0
- package/dist/tenant/slim.js +21 -0
- package/dist/tenant/status.d.ts +3 -3
- package/dist/tenant/status.js +20 -13
- package/dist/tenant/store-pg.d.ts +9 -6
- package/dist/tenant/store-pg.js +60 -125
- package/dist/tenant/streams.d.ts +62 -85
- package/dist/tenant/streams.js +143 -226
- package/dist/tenant/subject-limits.d.ts +16 -0
- package/dist/tenant/subject-limits.js +45 -0
- package/dist/tenant/sweep.d.ts +1 -10
- package/dist/tenant/sweep.js +29 -69
- package/dist/tenant/tokens.d.ts +14 -0
- package/dist/tenant/tokens.js +180 -0
- package/dist/tenant/transcript.d.ts +48 -5
- package/dist/tenant/transcript.js +58 -1
- package/dist/tenant/types.d.ts +69 -98
- package/dist/tenant/types.js +4 -15
- package/dist/tenant/worker.d.ts +3 -1
- package/dist/tenant/worker.js +13 -7
- package/dist/vault/host-model.d.ts +31 -0
- package/dist/vault/host-model.js +57 -0
- package/dist/vault/service.d.ts +44 -17
- package/dist/vault/service.js +126 -121
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +25 -9
- package/dist/core/executors.d.ts +0 -44
- package/dist/core/executors.js +0 -95
- package/dist/store/memory.d.ts +0 -57
- package/dist/store/memory.js +0 -645
- package/dist/store/postgres/migrations/001_initial.d.ts +0 -27
- package/dist/store/postgres/migrations/001_initial.js +0 -196
- package/dist/store/postgres/migrations/002_session_owner.d.ts +0 -6
- package/dist/store/postgres/migrations/002_session_owner.js +0 -13
- package/dist/store/postgres/migrations/index.d.ts +0 -49
- package/dist/store/postgres/migrations/index.js +0 -76
- package/dist/store/postgres/names.d.ts +0 -11
- package/dist/store/postgres/names.js +0 -42
- package/dist/store/postgres/tenants.d.ts +0 -90
- package/dist/store/postgres/tenants.js +0 -214
- package/dist/streams/relay.d.ts +0 -37
- package/dist/streams/relay.js +0 -254
- package/dist/tenant/envelope.d.ts +0 -7
- package/dist/tenant/envelope.js +0 -22
- package/dist/tenant/live.d.ts +0 -92
- package/dist/tenant/live.js +0 -290
- package/dist/tenant/quarantine-error.d.ts +0 -6
- package/dist/tenant/quarantine-error.js +0 -9
- package/dist/tenant/quarantine.d.ts +0 -27
- package/dist/tenant/quarantine.js +0 -72
- package/dist/tenant/routes-tenant.d.ts +0 -12
- package/dist/tenant/routes-tenant.js +0 -137
- package/dist/tenant/routes.d.ts +0 -9
- package/dist/tenant/routes.js +0 -151
- package/dist/tenant/store-memory.d.ts +0 -16
- package/dist/tenant/store-memory.js +0 -111
- /package/dist/{store → record}/cursor.d.ts +0 -0
- /package/dist/{store → record}/cursor.js +0 -0
package/dist/store/types.d.ts
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
* The Session Store seam (architecture §12.2).
|
|
3
3
|
*
|
|
4
4
|
* The Session Store records what happened. It is async, transactional and
|
|
5
|
-
* tenant-scoped: one `SessionStore` per Tenant. Postgres is the
|
|
6
|
-
* implementation
|
|
5
|
+
* tenant-scoped: one `SessionStore` per Tenant. Postgres is the
|
|
6
|
+
* implementation (`store/postgres/store.ts`); tests run on it too.
|
|
7
7
|
*
|
|
8
8
|
* ## Invariants every implementation keeps
|
|
9
9
|
*
|
|
10
10
|
* 1. **One transaction per `tx` call, READ COMMITTED or stronger.** Nothing a
|
|
11
11
|
* transaction wrote is visible to others before it commits, and nothing is
|
|
12
|
-
* kept when `fn` throws: document writes, event sequences,
|
|
13
|
-
* `afterCommit` callbacks
|
|
14
|
-
*
|
|
12
|
+
* kept when `fn` throws: document writes, event sequences, record rows,
|
|
13
|
+
* and `afterCommit` callbacks are all discarded, and `tx` rejects with the
|
|
14
|
+
* error `fn` threw.
|
|
15
15
|
* 2. **Session-scoped writes lock the session row first.** `lockSession` takes
|
|
16
16
|
* a row lock (`SELECT … FOR UPDATE`) held until the transaction ends. Effect
|
|
17
17
|
* intent and outcome, Action transitions, checkpoint settlement and event
|
|
@@ -21,18 +21,17 @@
|
|
|
21
21
|
* 3. **Per-session event sequence.** `event` allocates the session's next
|
|
22
22
|
* sequence under the session lock, starting at 0, without gaps across
|
|
23
23
|
* committed transactions. The sequence is also the S2 sequence number of the
|
|
24
|
-
* event in the session's stream (`sessions/<id
|
|
25
|
-
* `streams/
|
|
26
|
-
* (see `
|
|
24
|
+
* event in the session's stream (`sessions/<id>` in the Tenant's basin
|
|
25
|
+
* generation, `streams/basin.ts`), and the cursor is
|
|
26
|
+
* `base64url("<sessionId>:<seq>")` (see `record/cursor.ts`).
|
|
27
27
|
* 4. **No external I/O inside `fn`.** No model, tool, MCP, sandbox, Restate or
|
|
28
|
-
* S2 call, and no `fetch`, runs inside a transaction. Wakes go
|
|
29
|
-
* `afterCommit`,
|
|
30
|
-
*
|
|
28
|
+
* S2 call, and no `fetch`, runs inside a transaction. Wakes and deliveries go
|
|
29
|
+
* through `afterCommit`, and events are delivered to commit listeners after
|
|
30
|
+
* commit (seam rule 1 and 2).
|
|
31
31
|
* 5. **No nested transactions.** Calling `store.tx` from inside `fn` rejects.
|
|
32
32
|
* A `Tx` must not be used after its `tx` call settles.
|
|
33
33
|
* 6. **Post-commit order.** After a commit, the store first calls every commit
|
|
34
|
-
* listener once with the transaction's events (in allocation order)
|
|
35
|
-
* `signalWork` flag, then runs the `afterCommit` callbacks in registration
|
|
34
|
+
* listener once with the transaction's events (in allocation order), then runs the `afterCommit` callbacks in registration
|
|
36
35
|
* order and awaits them. Listener and callback failures are reported to the
|
|
37
36
|
* store's error hook; they never reject `tx`, because the commit stands.
|
|
38
37
|
* Recovery from a lost post-commit step is the Tenant sweep's job.
|
|
@@ -46,17 +45,21 @@
|
|
|
46
45
|
* ## Typed queries, no scans
|
|
47
46
|
*
|
|
48
47
|
* There is no generic table scan. Every read the Runtime needs is a typed
|
|
49
|
-
* method on `Tx` (`sessionsWithStatus`, `
|
|
48
|
+
* method on `Tx` (`sessionsWithStatus`, `expiredDeliveries`, `pendingActions`,
|
|
50
49
|
* `effectsForTurn`, `linkedSessions`, `counts`, …) that an implementation can
|
|
51
|
-
* back with an index, and principals,
|
|
50
|
+
* back with an index, and principals, endpoints, vaults and Tenant settings
|
|
52
51
|
* have their own methods rather than raw SQL outside the store. Session history
|
|
53
|
-
* is not read from the store:
|
|
54
|
-
*
|
|
52
|
+
* is not read from the store: every event is written to the record (Postgres
|
|
53
|
+
* `nylorun_streams.session_events`) in its transaction, the stream relay
|
|
54
|
+
* (`streams/relay/`) feeds Durable Streams from it, and history and SSE read
|
|
55
|
+
* them there (`tenant/session-streams.ts`).
|
|
55
56
|
*/
|
|
56
|
-
import type {
|
|
57
|
+
import type { RecordReader } from "../streams/relay/types.js";
|
|
58
|
+
import type { Action, EventPayload, EventType, LiveEvent, SessionEventOf } from "@nylorun/core/contracts";
|
|
57
59
|
import type { HostEffect } from "@nylorun/harness/run";
|
|
60
|
+
import type { ModelBudgetRow, ModelUsageRow, PrincipalRow, ToolCrossingRow, PublishableKeyRow, SigningKeyRow, SubjectUsageRow, VaultAuditRow, VaultCredentialRow, VaultIdempotencyRow, VaultRow } from "./postgres/schema.js";
|
|
58
61
|
/** Tables holding JSON documents keyed by `id`. */
|
|
59
|
-
export type DocTable = "definitions" | "sessions" | "commands" | "
|
|
62
|
+
export type DocTable = "definitions" | "sessions" | "commands" | "effects" | "actions" | "sandboxes" | "links";
|
|
60
63
|
export declare const DOC_TABLES: readonly DocTable[];
|
|
61
64
|
/** Session lifecycle (architecture §10.2). */
|
|
62
65
|
export type SessionStatus = "idle" | "runnable" | "running" | "waiting" | "paused" | "uncertain" | "completed" | "failed" | "cancelled";
|
|
@@ -87,18 +90,13 @@ export interface SessionDoc {
|
|
|
87
90
|
agentId: string;
|
|
88
91
|
status: SessionStatus | (string & {});
|
|
89
92
|
activeTurnId: string | null;
|
|
90
|
-
/**
|
|
91
|
-
* Names the session's event stream (`sessions/<id>/<incarnation>`, `streams/types.ts`).
|
|
92
|
-
* Set when the session is created; `event` reports it with each event (`Commit`).
|
|
93
|
-
*/
|
|
94
|
-
streamIncarnation?: string;
|
|
95
93
|
}
|
|
96
94
|
/** The fields of an effect document the store indexes (`request.sessionId`, `request.turnId`, `request.kind`, `status`). */
|
|
97
95
|
export interface EffectDoc {
|
|
98
96
|
request: HostEffect;
|
|
99
97
|
status: EffectStatus | (string & {});
|
|
100
98
|
}
|
|
101
|
-
/** Actions are stored as the wire `Action`. Indexed: `sessionId`, `turnId`, `agentId`, `status`, `kind`, `
|
|
99
|
+
/** Actions are stored as the wire `Action`. Indexed: `sessionId`, `turnId`, `agentId`, `status`, `kind`, `deadlineAt`. */
|
|
102
100
|
export type ActionDoc = Action;
|
|
103
101
|
/** A workflow → agent session link, keyed by the linked agent session id. Indexed: `workflowSessionId`. */
|
|
104
102
|
export interface LinkDoc {
|
|
@@ -125,95 +123,93 @@ export interface LinkedSession<S extends SessionDoc = SessionDoc> {
|
|
|
125
123
|
link: LinkDoc;
|
|
126
124
|
session: StoredSession<S>;
|
|
127
125
|
}
|
|
128
|
-
/** One committed-but-not-yet-relayed event (§12.4). Deleted once S2 has it. */
|
|
129
|
-
export interface OutboxRow {
|
|
130
|
-
sessionId: string;
|
|
131
|
-
seq: number;
|
|
132
|
-
event: LiveEvent;
|
|
133
|
-
}
|
|
134
|
-
export interface OutboxStats {
|
|
135
|
-
/** Unrelayed events. */
|
|
136
|
-
depth: number;
|
|
137
|
-
/** ISO `createdAt` of the oldest unrelayed event, or null when the outbox is empty. */
|
|
138
|
-
oldestCreatedAt: string | null;
|
|
139
|
-
}
|
|
140
126
|
/** What one commit produced, delivered to commit listeners after commit. */
|
|
141
127
|
export interface Commit {
|
|
142
128
|
/** Events written by the transaction, in allocation order. */
|
|
143
129
|
readonly events: readonly LiveEvent[];
|
|
144
130
|
/**
|
|
145
|
-
* The
|
|
146
|
-
*
|
|
147
|
-
* each event to that incarnation's stream.
|
|
131
|
+
* The basin generation of each event, aligned with `events`: the stream relay appends it to
|
|
132
|
+
* `sessions/<id>` in that generation's basin.
|
|
148
133
|
*/
|
|
149
|
-
readonly
|
|
150
|
-
|
|
151
|
-
|
|
134
|
+
readonly generations: readonly number[];
|
|
135
|
+
}
|
|
136
|
+
/** A Tenant's basin generations (Durable Streams §8.1). */
|
|
137
|
+
export interface BasinGenerations {
|
|
138
|
+
/** The basin session streams are written to and read from. */
|
|
139
|
+
current: number;
|
|
140
|
+
/** Earlier generations whose basins are still to be deleted. */
|
|
141
|
+
retired: number[];
|
|
152
142
|
}
|
|
153
143
|
export type CommitListener = (commit: Commit) => void;
|
|
154
|
-
/**
|
|
155
|
-
|
|
144
|
+
/**
|
|
145
|
+
* An Action endpoint: the URL the Runtime delivers one agent's Actions to, and what recent
|
|
146
|
+
* deliveries and the last ping say about it.
|
|
147
|
+
*/
|
|
148
|
+
export interface EndpointRow {
|
|
156
149
|
agentId: string;
|
|
157
|
-
|
|
150
|
+
url: string;
|
|
158
151
|
implementationVersion: string;
|
|
159
152
|
manifestHash?: string;
|
|
153
|
+
timeoutMs: number;
|
|
154
|
+
maxConcurrent: number;
|
|
155
|
+
/** Application principal that registered it, when known. */
|
|
160
156
|
principalId?: string;
|
|
157
|
+
lastDeliveryAt?: string;
|
|
158
|
+
lastSuccessAt?: string;
|
|
159
|
+
lastErrorCode?: string;
|
|
160
|
+
lastErrorMessage?: string;
|
|
161
|
+
consecutiveFailures: number;
|
|
162
|
+
/** What the endpoint reported serving on the last ping. */
|
|
163
|
+
servedImplementationVersion?: string;
|
|
164
|
+
servedManifestHash?: string;
|
|
161
165
|
createdAt: string;
|
|
162
166
|
updatedAt: string;
|
|
163
167
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
/** The registration part of an endpoint, as `putEndpoint` writes it. */
|
|
169
|
+
export type EndpointRegistrationRow = Pick<EndpointRow, "agentId" | "url" | "implementationVersion" | "manifestHash" | "timeoutMs" | "maxConcurrent" | "principalId" | "updatedAt">;
|
|
170
|
+
/** One observation about an endpoint (`recordEndpointHealth`). */
|
|
171
|
+
export type EndpointHealthUpdate =
|
|
172
|
+
/** A delivery was answered. */
|
|
173
|
+
{
|
|
174
|
+
kind: "success";
|
|
175
|
+
at: string;
|
|
170
176
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
createdAt: string;
|
|
178
|
-
scope: "user" | "host";
|
|
177
|
+
/** A delivery failed: not reached, refused, or lost. */
|
|
178
|
+
| {
|
|
179
|
+
kind: "failure";
|
|
180
|
+
at: string;
|
|
181
|
+
code: string;
|
|
182
|
+
message: string;
|
|
179
183
|
}
|
|
180
|
-
/**
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
/** A ping was answered with what the endpoint serves. */
|
|
185
|
+
| {
|
|
186
|
+
kind: "served";
|
|
187
|
+
implementationVersion: string;
|
|
188
|
+
manifestHash?: string;
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* The rows of the typed tables, inferred from the tables Drizzle defines
|
|
192
|
+
* (`store/postgres/schema.ts`): a principal, a vault and its credentials (secrets sealed in
|
|
193
|
+
* `bytea` columns, never inside a JSON body), the vault's audit and idempotency records, the
|
|
194
|
+
* Tenant's signing keys, a subject's turn bucket, a publishable key, a model call in the usage
|
|
195
|
+
* ledger and a model budget.
|
|
196
|
+
*/
|
|
197
|
+
export type { ModelBudgetRow, ModelUsageRow, ToolCrossingRow, PrincipalRow, PublishableKeyRow, SigningKeyRow, SubjectUsageRow, VaultAuditRow, VaultCredentialRow, VaultIdempotencyRow, VaultRow, };
|
|
198
|
+
/** Which rows of the usage ledger a total covers. */
|
|
199
|
+
export interface ModelUsageQuery {
|
|
200
|
+
scope: "tenant" | "agent" | "turn";
|
|
201
|
+
/** The agent or turn id; ignored for `tenant`. */
|
|
202
|
+
id?: string;
|
|
203
|
+
/** Only rows created at or after this ISO time. */
|
|
204
|
+
since?: string;
|
|
186
205
|
}
|
|
187
|
-
export interface
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
type: "bearer" | "oauth" | "model";
|
|
192
|
-
/** JSON text of the non-secret binding (url, or provider and model). */
|
|
193
|
-
bindingJson: string;
|
|
194
|
-
expiresAt: string | null;
|
|
195
|
-
createdAt: string;
|
|
196
|
-
rotatedAt: string | null;
|
|
206
|
+
export interface ModelUsageTotals {
|
|
207
|
+
calls: number;
|
|
208
|
+
tokens: number;
|
|
209
|
+
costUsd: number;
|
|
197
210
|
}
|
|
198
211
|
/** Fields `updateCredential` may change. */
|
|
199
212
|
export type VaultCredentialPatch = Partial<Omit<VaultCredentialRow, "id" | "vaultId" | "createdAt">>;
|
|
200
|
-
export interface VaultAuditRow {
|
|
201
|
-
id: string;
|
|
202
|
-
at: string;
|
|
203
|
-
actor: string;
|
|
204
|
-
action: string;
|
|
205
|
-
vaultId: string | null;
|
|
206
|
-
credentialId: string | null;
|
|
207
|
-
sessionId: string | null;
|
|
208
|
-
target: string | null;
|
|
209
|
-
outcome: string;
|
|
210
|
-
}
|
|
211
|
-
export interface VaultIdempotencyRow {
|
|
212
|
-
id: string;
|
|
213
|
-
bodyHash: string;
|
|
214
|
-
/** JSON text of the original response. */
|
|
215
|
-
response: string;
|
|
216
|
-
}
|
|
217
213
|
export interface SessionEffectFilter {
|
|
218
214
|
/** Only this turn. Absent means every turn. */
|
|
219
215
|
turnId?: string;
|
|
@@ -229,7 +225,7 @@ export interface StoreCounts {
|
|
|
229
225
|
sessions: number;
|
|
230
226
|
/** Sessions `running` or `runnable`. */
|
|
231
227
|
runningSessions: number;
|
|
232
|
-
/** Actions `pending` or `
|
|
228
|
+
/** Actions `pending` or `delivering`. */
|
|
233
229
|
pendingActions: number;
|
|
234
230
|
/** Effects `uncertain`. */
|
|
235
231
|
uncertainEffects: number;
|
|
@@ -263,10 +259,12 @@ export interface SessionStore {
|
|
|
263
259
|
tx<T>(fn: (t: Tx) => Promise<T>): Promise<T>;
|
|
264
260
|
/**
|
|
265
261
|
* Registers a listener called once per committed transaction that wrote
|
|
266
|
-
* events
|
|
267
|
-
* relay (`streams
|
|
262
|
+
* events. Returns an unsubscribe function. A Tenant without the Host's stream
|
|
263
|
+
* relay relays its own commits from here (`tenant/streams.ts`).
|
|
268
264
|
*/
|
|
269
265
|
onCommit(listener: CommitListener): () => void;
|
|
266
|
+
/** Reads the record back, for the stream relay's refills and reconciliation. */
|
|
267
|
+
record(): RecordReader;
|
|
270
268
|
/** Reachability and schema check for Tenant status and `/ready`. Never throws. */
|
|
271
269
|
health(): Promise<StoreHealth>;
|
|
272
270
|
close(): Promise<void>;
|
|
@@ -279,7 +277,7 @@ export interface StoreHealth {
|
|
|
279
277
|
/** Options every implementation accepts. */
|
|
280
278
|
export interface SessionStoreOptions {
|
|
281
279
|
tenantId: string;
|
|
282
|
-
/** Clock for event `
|
|
280
|
+
/** Clock for event `time`. Defaults to `() => new Date()`. */
|
|
283
281
|
now?: () => Date;
|
|
284
282
|
/** Receives listener and `afterCommit` failures, which never reject `tx`. */
|
|
285
283
|
onError?: (error: unknown) => void;
|
|
@@ -290,9 +288,9 @@ export interface Tx {
|
|
|
290
288
|
/** Insert or replace. For `sessions`, ownership fields in `body` are ignored. */
|
|
291
289
|
put(table: DocTable, id: string, body: unknown): Promise<void>;
|
|
292
290
|
/**
|
|
293
|
-
* Deletes a document. Deleting a session
|
|
294
|
-
* session
|
|
295
|
-
*
|
|
291
|
+
* Deletes a document. Deleting a session keeps its record rows and log head
|
|
292
|
+
* (per-session record deletion is deferred, Durable Streams §15), so a session
|
|
293
|
+
* created again with the same id continues its log and stream.
|
|
296
294
|
*/
|
|
297
295
|
delete(table: DocTable, id: string): Promise<void>;
|
|
298
296
|
/**
|
|
@@ -301,7 +299,7 @@ export interface Tx {
|
|
|
301
299
|
* Locking twice in one transaction is a no-op.
|
|
302
300
|
*
|
|
303
301
|
* Lock order, so concurrent transactions cannot deadlock (Postgres takes row
|
|
304
|
-
* locks in statement order
|
|
302
|
+
* locks in statement order):
|
|
305
303
|
* - a linked agent (child) session is locked before its workflow (parent)
|
|
306
304
|
* session, never after it. `t.event` on a session takes its lock, so an
|
|
307
305
|
* event on a child after the parent is locked breaks the rule too. Work
|
|
@@ -313,14 +311,15 @@ export interface Tx {
|
|
|
313
311
|
lockSession<T extends SessionDoc = SessionDoc>(id: string): Promise<StoredSession<T> | undefined>;
|
|
314
312
|
/**
|
|
315
313
|
* Allocates the session's next sequence (from 0) under the session lock,
|
|
316
|
-
*
|
|
317
|
-
*
|
|
314
|
+
* builds the event on the `nylorun.event/2` envelope, checks it against the
|
|
315
|
+
* event catalog (`InvalidEventError` when it does not match), writes it to
|
|
316
|
+
* the record in the Tenant's current basin generation and buffers it for
|
|
317
|
+
* commit listeners. Rejects when the session does not exist. The returned
|
|
318
|
+
* cursor is final.
|
|
318
319
|
*/
|
|
319
|
-
event(sessionId: string, turnId: string | null, type:
|
|
320
|
+
event<T extends EventType>(sessionId: string, turnId: string | null, type: T, payload: EventPayload<T>): Promise<SessionEventOf<T>>;
|
|
320
321
|
/** Runs `fn` after a successful commit (never on rollback). Used for wakes. */
|
|
321
322
|
afterCommit(fn: () => void | Promise<void>): void;
|
|
322
|
-
/** Signals `work_available` to executors after a successful commit. */
|
|
323
|
-
signalWork(): void;
|
|
324
323
|
/**
|
|
325
324
|
* Locks the session and takes ownership when it has no owner or the owner's
|
|
326
325
|
* lease ended at or before `now`; then increments `epoch` and sets the lease
|
|
@@ -354,10 +353,14 @@ export interface Tx {
|
|
|
354
353
|
}): Promise<StoredSession<T>[]>;
|
|
355
354
|
listDefinitions<T extends DefinitionDoc = DefinitionDoc>(): Promise<T[]>;
|
|
356
355
|
listSandboxes<T extends SandboxDoc = SandboxDoc>(): Promise<T[]>;
|
|
357
|
-
/**
|
|
358
|
-
expiredClaims(now: Date, limit: number): Promise<ActionDoc[]>;
|
|
359
|
-
/** `pending` actions offered to one agent's executor. */
|
|
356
|
+
/** One agent's `pending` actions, delivered when its endpoint is registered. */
|
|
360
357
|
pendingActions(agentId: string): Promise<ActionDoc[]>;
|
|
358
|
+
/** How many of one agent's actions are `delivering` (Action endpoints). */
|
|
359
|
+
deliveringCount(agentId: string): Promise<number>;
|
|
360
|
+
/** `pending` actions of agents that have an Action endpoint, by id. */
|
|
361
|
+
pendingActionsWithEndpoint(limit: number): Promise<ActionDoc[]>;
|
|
362
|
+
/** `delivering` actions whose deadline is at or before `now`, earliest first. */
|
|
363
|
+
expiredDeliveries(now: Date, limit: number): Promise<ActionDoc[]>;
|
|
361
364
|
actionsForSession(sessionId: string, filter?: SessionActionFilter): Promise<ActionDoc[]>;
|
|
362
365
|
actionsWithStatus(statuses: readonly ActionStatus[], filter?: {
|
|
363
366
|
kinds?: readonly ActionKind[];
|
|
@@ -372,27 +375,19 @@ export interface Tx {
|
|
|
372
375
|
/** Links of one workflow session joined to their existing agent sessions, ordered by agent session id. */
|
|
373
376
|
linkedSessions<S extends SessionDoc = SessionDoc>(workflowSessionId: string): Promise<LinkedSession<S>[]>;
|
|
374
377
|
counts(): Promise<StoreCounts>;
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
378
|
+
basinGenerations(): Promise<BasinGenerations>;
|
|
379
|
+
/** Forgets a retired generation once its basin is deleted. */
|
|
380
|
+
forgetRetiredGeneration(generation: number): Promise<void>;
|
|
381
|
+
listEndpoints(): Promise<EndpointRow[]>;
|
|
382
|
+
getEndpoint(agentId: string): Promise<EndpointRow | undefined>;
|
|
379
383
|
/**
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
* (null: none), in one statement: a relay that appended to an abandoned incarnation's
|
|
383
|
-
* stream never deletes the rows of a session created again with the same id.
|
|
384
|
+
* Inserts or updates by `agentId`, keeping `createdAt`. Health is kept, except that a new
|
|
385
|
+
* `url` starts with none.
|
|
384
386
|
*/
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
getExecutor(agentId: string): Promise<ExecutorRow | undefined>;
|
|
390
|
-
/**
|
|
391
|
-
* Inserts or updates by `agentId`, keeping `createdAt` on update. Rejects
|
|
392
|
-
* when another agent already uses `tokenHash`.
|
|
393
|
-
*/
|
|
394
|
-
putExecutor(row: Omit<ExecutorRow, "createdAt">): Promise<void>;
|
|
395
|
-
deleteExecutor(agentId: string): Promise<void>;
|
|
387
|
+
putEndpoint(row: EndpointRegistrationRow): Promise<void>;
|
|
388
|
+
deleteEndpoint(agentId: string): Promise<void>;
|
|
389
|
+
/** Records one observation. Nothing happens when the endpoint does not exist. */
|
|
390
|
+
recordEndpointHealth(agentId: string, update: EndpointHealthUpdate): Promise<void>;
|
|
396
391
|
/** Rejects when the id or token hash already exists. */
|
|
397
392
|
insertPrincipal(row: PrincipalRow): Promise<void>;
|
|
398
393
|
principalByTokenHash(tokenHash: string): Promise<PrincipalRow | undefined>;
|
|
@@ -428,15 +423,71 @@ export interface Tx {
|
|
|
428
423
|
getVaultIdempotency(id: string): Promise<VaultIdempotencyRow | undefined>;
|
|
429
424
|
/** Rejects when the id exists. */
|
|
430
425
|
insertVaultIdempotency(row: VaultIdempotencyRow): Promise<void>;
|
|
426
|
+
/**
|
|
427
|
+
* Serializes signing key changes: held until the transaction ends. Take it before reading
|
|
428
|
+
* the keys a change depends on, so concurrent first uses or rotations do not collide.
|
|
429
|
+
*/
|
|
430
|
+
lockSigningKeys(): Promise<void>;
|
|
431
|
+
/** Rejects on a duplicate id, or a second key in `standby`, `current` or `previous`. */
|
|
432
|
+
insertSigningKey(row: SigningKeyRow): Promise<void>;
|
|
433
|
+
signingKey(id: string): Promise<SigningKeyRow | undefined>;
|
|
434
|
+
/** Keys in the given states (all when omitted), ordered by `createdAt`, then id. */
|
|
435
|
+
signingKeys(states?: readonly SigningKeyRow["state"][]): Promise<SigningKeyRow[]>;
|
|
436
|
+
/**
|
|
437
|
+
* Moves a key from `from` to `to` and stamps the matching time (`activatedAt` for
|
|
438
|
+
* `current`, `retiredAt` for `previous`, `revokedAt` for `revoked`). Returns false when the
|
|
439
|
+
* key is not in `from`.
|
|
440
|
+
*/
|
|
441
|
+
setSigningKeyState(id: string, from: SigningKeyRow["state"], to: SigningKeyRow["state"], at: string): Promise<boolean>;
|
|
442
|
+
countSigningKeys(): Promise<number>;
|
|
443
|
+
/** The subject's revocation epoch; 0 when it was never revoked. */
|
|
444
|
+
subjectEpoch(subject: string): Promise<number>;
|
|
445
|
+
/** The epochs of several subjects; subjects never revoked are absent. */
|
|
446
|
+
subjectEpochs(subjects: readonly string[]): Promise<Map<string, number>>;
|
|
447
|
+
/** Adds one to the subject's epoch and returns the new value. */
|
|
448
|
+
bumpSubjectEpoch(subject: string, at: string): Promise<number>;
|
|
449
|
+
/**
|
|
450
|
+
* The subject's turn bucket, created from `initial` when missing, locked until the
|
|
451
|
+
* transaction ends so concurrent commands of one subject serialize.
|
|
452
|
+
*/
|
|
453
|
+
lockSubjectUsage(initial: SubjectUsageRow): Promise<SubjectUsageRow>;
|
|
454
|
+
putSubjectUsage(row: SubjectUsageRow): Promise<void>;
|
|
455
|
+
/** How many sessions of `ownerUserId` are in one of `statuses`. */
|
|
456
|
+
countOwnerSessions(ownerUserId: string, statuses: readonly string[]): Promise<number>;
|
|
457
|
+
/** Rejects on a duplicate id, key or name. */
|
|
458
|
+
insertPublishableKey(row: PublishableKeyRow): Promise<void>;
|
|
459
|
+
publishableKeyByKey(key: string): Promise<PublishableKeyRow | undefined>;
|
|
460
|
+
publishableKey(id: string): Promise<PublishableKeyRow | undefined>;
|
|
461
|
+
/** All keys, revoked ones included, ordered by `createdAt`, then id. */
|
|
462
|
+
publishableKeys(): Promise<PublishableKeyRow[]>;
|
|
463
|
+
/** Returns false when the key does not exist. */
|
|
464
|
+
updatePublishableKey(id: string, patch: Partial<Pick<PublishableKeyRow, "originsJson" | "revokedAt">>): Promise<boolean>;
|
|
465
|
+
/** Appends a row, setting `duplicate` when one with the same `effectKey` exists; returns it. */
|
|
466
|
+
recordModelUsage(row: Omit<ModelUsageRow, "duplicate">): Promise<ModelUsageRow>;
|
|
467
|
+
modelUsageTotals(query: ModelUsageQuery): Promise<ModelUsageTotals>;
|
|
468
|
+
/** Every budget, ordered by scope, then scope id. */
|
|
469
|
+
listModelBudgets(): Promise<ModelBudgetRow[]>;
|
|
470
|
+
/** Replaces every budget with `rows`. */
|
|
471
|
+
putModelBudgets(rows: readonly ModelBudgetRow[]): Promise<void>;
|
|
472
|
+
toolCrossing(key: string): Promise<ToolCrossingRow | undefined>;
|
|
473
|
+
/** Inserts the running call's row; false when a row with `key` already exists. */
|
|
474
|
+
startToolCrossing(row: Pick<ToolCrossingRow, "key" | "hash" | "startedAt">): Promise<boolean>;
|
|
475
|
+
/** Records the call's answer. */
|
|
476
|
+
settleToolCrossing(key: string, answer: unknown, settledAt: string): Promise<void>;
|
|
477
|
+
/** Deletes rows that settled before `before`; returns how many. */
|
|
478
|
+
pruneToolCrossings(before: string): Promise<number>;
|
|
431
479
|
getSetting(key: string): Promise<string | undefined>;
|
|
432
480
|
putSetting(key: string, value: string): Promise<void>;
|
|
433
481
|
/**
|
|
434
482
|
* Deletes Tenant state by scope, in this transaction:
|
|
435
|
-
* - `sessions`: sessions, commands,
|
|
483
|
+
* - `sessions`: sessions, commands, effects, actions, links, subject turn
|
|
484
|
+
* buckets and the Tenant's record rows and log heads. The Tenant moves to the next basin
|
|
485
|
+
* generation and the current one is retired, so session ids it frees start again in an
|
|
486
|
+
* empty basin;
|
|
436
487
|
* - `sandboxes`: sandbox records;
|
|
437
|
-
* - `all`: both, plus definitions,
|
|
438
|
-
*
|
|
439
|
-
* idempotency rows stay.
|
|
488
|
+
* - `all`: both, plus definitions, Action endpoints, user vaults with their credentials,
|
|
489
|
+
* the model usage ledger and the model budgets. The host vault, principals, signing keys, subject epochs,
|
|
490
|
+
* publishable keys, settings, audit and vault idempotency rows stay.
|
|
440
491
|
*/
|
|
441
492
|
reset(scope: ResetScope): Promise<void>;
|
|
442
493
|
}
|
package/dist/store/types.js
CHANGED
package/dist/streams/basin.d.ts
CHANGED
|
@@ -20,4 +20,21 @@
|
|
|
20
20
|
*/
|
|
21
21
|
export declare const MAX_BASIN_PREFIX_LENGTH = 16;
|
|
22
22
|
export declare function validateBasinPrefix(prefix: string): string;
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* The basin of one generation of a Tenant (Durable Streams §8.1). A Tenant reset moves the
|
|
25
|
+
* Tenant to a new generation, so a session id it frees starts again in an empty basin.
|
|
26
|
+
* `DurableStreams` methods take this key where they take a Tenant id; generation 0 is the
|
|
27
|
+
* Tenant id itself, so existing basins keep their names.
|
|
28
|
+
*/
|
|
29
|
+
export declare function basinOf(tenantId: string, generation: number): string;
|
|
30
|
+
/** The Tenant id and generation of a basin key from `basinOf`. */
|
|
31
|
+
export declare function parseBasin(basin: string): {
|
|
32
|
+
tenantId: string;
|
|
33
|
+
generation: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The S2 basin of a Tenant, or of one of its generations (`basinOf`). Generation `g` > 0 of
|
|
37
|
+
* a canonical Tenant appends `-<g mod 1296 in base36>`, at most 48 characters with a
|
|
38
|
+
* 16-character prefix; other ids fold the generation into their hash.
|
|
39
|
+
*/
|
|
40
|
+
export declare function tenantBasinName(basin: string, prefix?: string): string;
|
package/dist/streams/basin.js
CHANGED
|
@@ -28,13 +28,42 @@ export function validateBasinPrefix(prefix) {
|
|
|
28
28
|
throw new Error(`S2 basin prefix must be at most ${MAX_BASIN_PREFIX_LENGTH} lowercase letters, digits or hyphens, starting with a letter or digit`);
|
|
29
29
|
return prefix;
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
/**
|
|
32
|
+
* The basin of one generation of a Tenant (Durable Streams §8.1). A Tenant reset moves the
|
|
33
|
+
* Tenant to a new generation, so a session id it frees starts again in an empty basin.
|
|
34
|
+
* `DurableStreams` methods take this key where they take a Tenant id; generation 0 is the
|
|
35
|
+
* Tenant id itself, so existing basins keep their names.
|
|
36
|
+
*/
|
|
37
|
+
export function basinOf(tenantId, generation) {
|
|
38
|
+
if (!Number.isInteger(generation) || generation < 0)
|
|
39
|
+
throw new Error("A basin generation is a non-negative integer");
|
|
40
|
+
return generation === 0 ? tenantId : `${tenantId}${GENERATION_SEPARATOR}${generation}`;
|
|
41
|
+
}
|
|
42
|
+
/** The Tenant id and generation of a basin key from `basinOf`. */
|
|
43
|
+
export function parseBasin(basin) {
|
|
44
|
+
const at = basin.lastIndexOf(GENERATION_SEPARATOR);
|
|
45
|
+
const generation = at < 0 ? NaN : Number(basin.slice(at + 1));
|
|
46
|
+
return Number.isInteger(generation) && generation > 0
|
|
47
|
+
? { tenantId: basin.slice(0, at), generation }
|
|
48
|
+
: { tenantId: basin, generation: 0 };
|
|
49
|
+
}
|
|
50
|
+
/** Never in a Tenant id (`TENANT_ID_PATTERN`). */
|
|
51
|
+
const GENERATION_SEPARATOR = "#";
|
|
52
|
+
/**
|
|
53
|
+
* The S2 basin of a Tenant, or of one of its generations (`basinOf`). Generation `g` > 0 of
|
|
54
|
+
* a canonical Tenant appends `-<g mod 1296 in base36>`, at most 48 characters with a
|
|
55
|
+
* 16-character prefix; other ids fold the generation into their hash.
|
|
56
|
+
*/
|
|
57
|
+
export function tenantBasinName(basin, prefix = "") {
|
|
32
58
|
validateBasinPrefix(prefix);
|
|
59
|
+
const { tenantId, generation } = parseBasin(basin);
|
|
33
60
|
if (!tenantId)
|
|
34
61
|
throw new Error("tenantId is required");
|
|
35
62
|
let name;
|
|
36
63
|
if (TENANT_ID_PATTERN.test(tenantId)) {
|
|
37
64
|
name = `${prefix}tn-${tenantId.slice(3)}`;
|
|
65
|
+
if (generation > 0)
|
|
66
|
+
name += `-${(generation % 1296).toString(36)}`;
|
|
38
67
|
}
|
|
39
68
|
else {
|
|
40
69
|
const slug = tenantId
|
|
@@ -42,7 +71,10 @@ export function tenantBasinName(tenantId, prefix = "") {
|
|
|
42
71
|
.replace(/[^a-z0-9]+/g, "-")
|
|
43
72
|
.slice(0, 12)
|
|
44
73
|
.replace(/^-+|-+$/g, "");
|
|
45
|
-
const hash = createHash("sha256")
|
|
74
|
+
const hash = createHash("sha256")
|
|
75
|
+
.update(generation > 0 ? basin : tenantId)
|
|
76
|
+
.digest("hex")
|
|
77
|
+
.slice(0, 16);
|
|
46
78
|
name = `${prefix}x-${slug ? `${slug}-` : ""}${hash}`;
|
|
47
79
|
}
|
|
48
80
|
if (!BASIN_NAME.test(name))
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signals on a basin's `tenant/control` stream (Durable Streams §8). They are appended
|
|
3
|
+
* directly, never recorded: a lost signal costs latency, never correctness (the epoch fence
|
|
4
|
+
* stops a cancelled turn's effects; readers also check their sessions periodically).
|
|
5
|
+
*/
|
|
6
|
+
import { type DurableStreams } from "./types.js";
|
|
7
|
+
/** Appends a `session.cancel` signal: the process running the session's advance aborts it. */
|
|
8
|
+
export declare function signalCancel(streams: DurableStreams, basin: string, sessionId: string, turnId?: string): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Appends a `sessions.reset` signal to the old generation's basin: every process with the
|
|
11
|
+
* Tenant open moves its readers to `generation` and ends the streams of deleted sessions.
|
|
12
|
+
*/
|
|
13
|
+
export declare function signalSessionsReset(streams: DurableStreams, basin: string, generation: number): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Appends a `subject.revoked` signal: every process with the Tenant open ends the subject's
|
|
16
|
+
* streams opened with a token older than `epoch`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function signalSubjectRevoked(streams: DurableStreams, basin: string, subject: string, epoch: number): Promise<void>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signals on a basin's `tenant/control` stream (Durable Streams §8). They are appended
|
|
3
|
+
* directly, never recorded: a lost signal costs latency, never correctness (the epoch fence
|
|
4
|
+
* stops a cancelled turn's effects; readers also check their sessions periodically).
|
|
5
|
+
*/
|
|
6
|
+
import { CONTROL_STREAM } from "./types.js";
|
|
7
|
+
/** Appends a `session.cancel` signal: the process running the session's advance aborts it. */
|
|
8
|
+
export async function signalCancel(streams, basin, sessionId, turnId) {
|
|
9
|
+
if (!sessionId)
|
|
10
|
+
throw new Error("sessionId is required");
|
|
11
|
+
const signal = {
|
|
12
|
+
type: "session.cancel",
|
|
13
|
+
sessionId,
|
|
14
|
+
...(turnId ? { turnId } : {}),
|
|
15
|
+
};
|
|
16
|
+
await streams.append(basin, CONTROL_STREAM, [signal]);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Appends a `sessions.reset` signal to the old generation's basin: every process with the
|
|
20
|
+
* Tenant open moves its readers to `generation` and ends the streams of deleted sessions.
|
|
21
|
+
*/
|
|
22
|
+
export async function signalSessionsReset(streams, basin, generation) {
|
|
23
|
+
const signal = { type: "sessions.reset", generation };
|
|
24
|
+
await streams.append(basin, CONTROL_STREAM, [signal]);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Appends a `subject.revoked` signal: every process with the Tenant open ends the subject's
|
|
28
|
+
* streams opened with a token older than `epoch`.
|
|
29
|
+
*/
|
|
30
|
+
export async function signalSubjectRevoked(streams, basin, subject, epoch) {
|
|
31
|
+
const signal = { type: "subject.revoked", subject, epoch };
|
|
32
|
+
await streams.append(basin, CONTROL_STREAM, [signal]);
|
|
33
|
+
}
|
package/dist/streams/memory.d.ts
CHANGED
|
@@ -14,7 +14,9 @@ export declare class MemoryStreams implements DurableStreams {
|
|
|
14
14
|
read<T = unknown>(tenantId: string, stream: string, fromSeq: number, options?: ReadOptions): AsyncIterable<StreamRecord<T>>;
|
|
15
15
|
tail(tenantId: string, stream: string): Promise<number>;
|
|
16
16
|
ensureTenant(tenantId: string): Promise<void>;
|
|
17
|
-
deleteTenant(tenantId: string
|
|
17
|
+
deleteTenant(tenantId: string, options?: {
|
|
18
|
+
allGenerations?: boolean;
|
|
19
|
+
}): Promise<void>;
|
|
18
20
|
deleteStream(tenantId: string, stream: string): Promise<void>;
|
|
19
21
|
listStreams(tenantId: string, prefix: string): Promise<string[]>;
|
|
20
22
|
close(): Promise<void>;
|