@nylorun/runtime 0.13.0-beta → 0.14.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 +330 -0
- package/README.md +69 -15
- package/dist/adapters/execution/restate.d.ts +7 -2
- package/dist/adapters/execution/restate.js +38 -8
- package/dist/adapters/replication/pgoutput.d.ts +16 -0
- package/dist/adapters/replication/pgoutput.js +169 -0
- package/dist/adapters/streams/s2.js +37 -20
- package/dist/admin-openapi.json +1071 -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 +7825 -0
- package/dist/api/components.js +83 -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 +130 -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 +259 -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 +10 -0
- package/dist/api/http/routes/tenant.js +172 -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 +138 -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/host/admin-api.d.ts +22 -0
- package/dist/host/admin-api.js +178 -0
- package/dist/host/app.d.ts +34 -0
- package/dist/host/app.js +185 -0
- package/dist/host/config-for.d.ts +2 -0
- package/dist/host/config-for.js +1 -0
- 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 +31 -3
- package/dist/host/create-host.js +168 -327
- package/dist/host/http.d.ts +14 -3
- package/dist/host/http.js +64 -23
- package/dist/host/main.js +55 -4
- package/dist/host/stack-config.d.ts +20 -0
- package/dist/host/stack-config.js +55 -0
- package/dist/mcp/pool.d.ts +19 -1
- package/dist/mcp/pool.js +60 -10
- 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 +25696 -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/event.d.ts +19 -0
- package/dist/store/event.js +49 -0
- package/dist/store/memory.d.ts +20 -8
- package/dist/store/memory.js +265 -100
- package/dist/store/postgres/connect.d.ts +13 -0
- package/dist/store/postgres/connect.js +5 -0
- package/dist/store/postgres/migrations/003_subject_tokens.d.ts +8 -0
- package/dist/store/postgres/migrations/003_subject_tokens.js +42 -0
- package/dist/store/postgres/migrations/004_publishable_keys.d.ts +6 -0
- package/dist/store/postgres/migrations/004_publishable_keys.js +18 -0
- package/dist/store/postgres/migrations/005_action_endpoints.d.ts +6 -0
- package/dist/store/postgres/migrations/005_action_endpoints.js +32 -0
- package/dist/store/postgres/migrations/006_drop_executors.d.ts +11 -0
- package/dist/store/postgres/migrations/006_drop_executors.js +32 -0
- package/dist/store/postgres/migrations/007_event_time.d.ts +7 -0
- package/dist/store/postgres/migrations/007_event_time.js +14 -0
- package/dist/store/postgres/migrations/008_durable_streams.d.ts +13 -0
- package/dist/store/postgres/migrations/008_durable_streams.js +35 -0
- package/dist/store/postgres/migrations/index.d.ts +5 -1
- package/dist/store/postgres/migrations/index.js +22 -2
- package/dist/store/postgres/migrations/shared/index.d.ts +31 -0
- package/dist/store/postgres/migrations/shared/index.js +92 -0
- package/dist/store/postgres/record.d.ts +11 -0
- package/dist/store/postgres/record.js +64 -0
- package/dist/store/postgres/store.js +337 -95
- package/dist/store/postgres/tenants.js +14 -0
- package/dist/store/types.d.ts +176 -81
- 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 +29 -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/memory.d.ts +69 -0
- package/dist/streams/relay/memory.js +176 -0
- package/dist/streams/relay/types.d.ts +77 -0
- package/dist/streams/relay/types.js +7 -0
- package/dist/streams/types.d.ts +66 -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.js +32 -0
- 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/commands.d.ts +18 -2
- package/dist/tenant/commands.js +107 -113
- package/dist/tenant/context.d.ts +75 -18
- package/dist/tenant/context.js +14 -10
- package/dist/tenant/delivery-token.d.ts +41 -0
- package/dist/tenant/delivery-token.js +105 -0
- package/dist/tenant/delivery.d.ts +62 -0
- package/dist/tenant/delivery.js +476 -0
- package/dist/tenant/effects.d.ts +5 -0
- package/dist/tenant/effects.js +78 -37
- package/dist/tenant/endpoints.d.ts +67 -0
- package/dist/tenant/endpoints.js +90 -0
- package/dist/tenant/ephemeral.d.ts +6 -0
- package/dist/tenant/ephemeral.js +60 -36
- 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/module.d.ts +3 -0
- package/dist/tenant/module.js +6 -12
- package/dist/tenant/outbound.d.ts +44 -0
- package/dist/tenant/outbound.js +127 -0
- package/dist/tenant/pool.d.ts +0 -2
- package/dist/tenant/pool.js +0 -18
- package/dist/tenant/reset.d.ts +0 -4
- package/dist/tenant/reset.js +0 -5
- package/dist/tenant/runtime.d.ts +10 -20
- package/dist/tenant/runtime.js +59 -42
- package/dist/tenant/scheduler.d.ts +7 -2
- package/dist/tenant/scheduler.js +7 -3
- package/dist/tenant/session-streams.d.ts +125 -0
- package/dist/tenant/session-streams.js +399 -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 +0 -3
- package/dist/tenant/status.js +19 -12
- package/dist/tenant/streams.d.ts +60 -67
- package/dist/tenant/streams.js +140 -208
- 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 +178 -0
- package/dist/tenant/transcript.d.ts +48 -5
- package/dist/tenant/transcript.js +58 -1
- package/dist/tenant/types.d.ts +29 -7
- package/dist/tenant/worker.d.ts +3 -1
- package/dist/tenant/worker.js +9 -3
- package/dist/vault/service.d.ts +18 -2
- package/dist/vault/service.js +57 -13
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +20 -8
- package/dist/core/executors.d.ts +0 -44
- package/dist/core/executors.js +0 -95
- package/dist/streams/relay.d.ts +0 -37
- package/dist/streams/relay.js +0 -254
- package/dist/tenant/live.d.ts +0 -92
- package/dist/tenant/live.js +0 -290
- 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/store/memory.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { MemoryRecord } from "../streams/relay/memory.js";
|
|
4
|
+
import { buildEvent } from "./event.js";
|
|
3
5
|
import { encodeCursor } from "./cursor.js";
|
|
4
6
|
import { OwnershipLostError } from "./ownership.js";
|
|
5
7
|
import { DOC_TABLES, } from "./types.js";
|
|
@@ -9,14 +11,19 @@ function emptyState() {
|
|
|
9
11
|
return {
|
|
10
12
|
docs: Object.fromEntries(DOC_TABLES.map((table) => [table, new Map()])),
|
|
11
13
|
sessionMeta: new Map(),
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
heads: new Map(),
|
|
15
|
+
basin: { current: 0, retired: [] },
|
|
16
|
+
endpoints: new Map(),
|
|
14
17
|
principals: new Map(),
|
|
15
18
|
vaults: new Map(),
|
|
16
19
|
credentials: new Map(),
|
|
17
20
|
audit: [],
|
|
18
21
|
idempotency: new Map(),
|
|
19
22
|
settings: new Map(),
|
|
23
|
+
signingKeys: new Map(),
|
|
24
|
+
subjectEpochs: new Map(),
|
|
25
|
+
subjectUsage: new Map(),
|
|
26
|
+
publishableKeys: new Map(),
|
|
20
27
|
};
|
|
21
28
|
}
|
|
22
29
|
const OWNERSHIP_KEYS = new Set(["owner", "epoch", "ownerExpiresAt"]);
|
|
@@ -32,13 +39,15 @@ const copy = (value) => structuredClone(value);
|
|
|
32
39
|
export class MemoryStoreData {
|
|
33
40
|
/** @internal */ state = emptyState();
|
|
34
41
|
/** @internal */ queue = Promise.resolve();
|
|
42
|
+
/** The record of committed events, read back by the stream relay. */
|
|
43
|
+
record = new MemoryRecord();
|
|
35
44
|
}
|
|
36
45
|
/**
|
|
37
46
|
* In-memory `SessionStore` for unit tests. Not a supported profile.
|
|
38
47
|
*
|
|
39
48
|
* Transactions are serialized (one at a time), each works on a copy of the
|
|
40
49
|
* state and replaces it on commit, so a throw rolls back everything: documents,
|
|
41
|
-
* sequences,
|
|
50
|
+
* sequences, record rows and `afterCommit` callbacks. Commit listeners see
|
|
42
51
|
* only this store's commits, as with a Postgres connection.
|
|
43
52
|
*/
|
|
44
53
|
export class MemorySessionStore {
|
|
@@ -79,11 +88,17 @@ export class MemorySessionStore {
|
|
|
79
88
|
t.closed = true;
|
|
80
89
|
}
|
|
81
90
|
this.data.state = working;
|
|
82
|
-
|
|
91
|
+
for (const change of t.recordChanges)
|
|
92
|
+
if (change.kind === "rows")
|
|
93
|
+
this.data.record.commit(change.rows);
|
|
94
|
+
else {
|
|
95
|
+
this.data.record.deleteRows(this.tenantId);
|
|
96
|
+
this.data.record.setGeneration(this.tenantId, change.generation);
|
|
97
|
+
}
|
|
98
|
+
if (t.events.length > 0) {
|
|
83
99
|
const commit = {
|
|
84
100
|
events: t.events,
|
|
85
|
-
|
|
86
|
-
workAvailable: t.workAvailable,
|
|
101
|
+
generations: t.generations,
|
|
87
102
|
};
|
|
88
103
|
for (const listener of this.listeners) {
|
|
89
104
|
try {
|
|
@@ -112,6 +127,9 @@ export class MemorySessionStore {
|
|
|
112
127
|
this.listeners.add(listener);
|
|
113
128
|
return () => this.listeners.delete(listener);
|
|
114
129
|
}
|
|
130
|
+
record() {
|
|
131
|
+
return this.data.record;
|
|
132
|
+
}
|
|
115
133
|
async health() {
|
|
116
134
|
return {
|
|
117
135
|
ok: !this.closed,
|
|
@@ -137,9 +155,9 @@ class MemoryTx {
|
|
|
137
155
|
now;
|
|
138
156
|
closed = false;
|
|
139
157
|
events = [];
|
|
140
|
-
|
|
158
|
+
generations = [];
|
|
159
|
+
recordChanges = [];
|
|
141
160
|
callbacks = [];
|
|
142
|
-
workAvailable = false;
|
|
143
161
|
constructor(s, tenantId, now) {
|
|
144
162
|
this.s = s;
|
|
145
163
|
this.tenantId = tenantId;
|
|
@@ -167,7 +185,6 @@ class MemoryTx {
|
|
|
167
185
|
owner: null,
|
|
168
186
|
epoch: 0,
|
|
169
187
|
ownerExpiresAt: null,
|
|
170
|
-
nextSeq: 0,
|
|
171
188
|
});
|
|
172
189
|
}
|
|
173
190
|
const json = JSON.stringify(value);
|
|
@@ -178,10 +195,9 @@ class MemoryTx {
|
|
|
178
195
|
async delete(table, id) {
|
|
179
196
|
this.check();
|
|
180
197
|
this.s.docs[table].delete(id);
|
|
181
|
-
|
|
198
|
+
// The session's log head stays: a session created again with this id continues its log.
|
|
199
|
+
if (table === "sessions")
|
|
182
200
|
this.s.sessionMeta.delete(id);
|
|
183
|
-
this.s.outbox.delete(id);
|
|
184
|
-
}
|
|
185
201
|
}
|
|
186
202
|
session(id) {
|
|
187
203
|
const raw = this.s.docs.sessions.get(id);
|
|
@@ -215,34 +231,32 @@ class MemoryTx {
|
|
|
215
231
|
const meta = this.s.sessionMeta.get(sessionId);
|
|
216
232
|
if (!meta)
|
|
217
233
|
throw new Error(`Session ${sessionId} not found`);
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
234
|
+
let head = this.s.heads.get(sessionId);
|
|
235
|
+
if (!head)
|
|
236
|
+
this.s.heads.set(sessionId, (head = { head: 0, generation: this.s.basin.current }));
|
|
237
|
+
const event = buildEvent({
|
|
222
238
|
tenantId: this.tenantId,
|
|
239
|
+
sessionId,
|
|
223
240
|
turnId,
|
|
224
|
-
|
|
225
|
-
|
|
241
|
+
seq: head.head,
|
|
242
|
+
epoch: meta.epoch,
|
|
243
|
+
time: this.now(),
|
|
226
244
|
type,
|
|
227
245
|
payload,
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
246
|
+
});
|
|
247
|
+
const seq = head.head++;
|
|
248
|
+
this.recordChanges.push({
|
|
249
|
+
kind: "rows",
|
|
250
|
+
rows: [{ tenantId: this.tenantId, sessionId, seq, generation: head.generation, body: event }],
|
|
251
|
+
});
|
|
233
252
|
this.events.push(event);
|
|
234
|
-
|
|
235
|
-
this.incarnations.push(doc.streamIncarnation ?? null);
|
|
253
|
+
this.generations.push(head.generation);
|
|
236
254
|
return copy(event);
|
|
237
255
|
}
|
|
238
256
|
afterCommit(fn) {
|
|
239
257
|
this.check();
|
|
240
258
|
this.callbacks.push(fn);
|
|
241
259
|
}
|
|
242
|
-
signalWork() {
|
|
243
|
-
this.check();
|
|
244
|
-
this.workAvailable = true;
|
|
245
|
-
}
|
|
246
260
|
// --- ownership -----------------------------------------------------------
|
|
247
261
|
async takeOwnership(sessionId, claim) {
|
|
248
262
|
this.check();
|
|
@@ -330,19 +344,29 @@ class MemoryTx {
|
|
|
330
344
|
actions() {
|
|
331
345
|
return this.docs("actions").map(([, doc]) => doc);
|
|
332
346
|
}
|
|
333
|
-
async
|
|
347
|
+
async pendingActions(agentId) {
|
|
348
|
+
this.check();
|
|
349
|
+
return this.actions().filter((a) => a.status === "pending" && a.agentId === agentId);
|
|
350
|
+
}
|
|
351
|
+
async deliveringCount(agentId) {
|
|
352
|
+
this.check();
|
|
353
|
+
return this.actions().filter((a) => a.status === "delivering" && a.agentId === agentId).length;
|
|
354
|
+
}
|
|
355
|
+
async pendingActionsWithEndpoint(limit) {
|
|
334
356
|
this.check();
|
|
335
357
|
return this.actions()
|
|
336
|
-
.filter((a) => a.status === "
|
|
337
|
-
a.leaseExpiresAt !== null &&
|
|
338
|
-
Date.parse(a.leaseExpiresAt) <= now.getTime())
|
|
339
|
-
.sort((a, b) => Date.parse(a.leaseExpiresAt) - Date.parse(b.leaseExpiresAt) ||
|
|
340
|
-
(a.actionId < b.actionId ? -1 : 1))
|
|
358
|
+
.filter((a) => a.status === "pending" && this.s.endpoints.has(a.agentId))
|
|
341
359
|
.slice(0, limit);
|
|
342
360
|
}
|
|
343
|
-
async
|
|
361
|
+
async expiredDeliveries(now, limit) {
|
|
344
362
|
this.check();
|
|
345
|
-
return this.actions()
|
|
363
|
+
return this.actions()
|
|
364
|
+
.filter((a) => a.status === "delivering" &&
|
|
365
|
+
typeof a.deadlineAt === "string" &&
|
|
366
|
+
Date.parse(a.deadlineAt) <= now.getTime())
|
|
367
|
+
.sort((a, b) => Date.parse(a.deadlineAt) - Date.parse(b.deadlineAt) ||
|
|
368
|
+
(a.actionId < b.actionId ? -1 : 1))
|
|
369
|
+
.slice(0, limit);
|
|
346
370
|
}
|
|
347
371
|
async actionsForSession(sessionId, filter = {}) {
|
|
348
372
|
this.check();
|
|
@@ -394,86 +418,51 @@ class MemoryTx {
|
|
|
394
418
|
sessions: sessions.length,
|
|
395
419
|
runningSessions: sessions.filter((s) => OPEN_SESSION.has(s.status))
|
|
396
420
|
.length,
|
|
397
|
-
pendingActions: this.actions().filter((a) => a.status === "pending" || a.status === "
|
|
421
|
+
pendingActions: this.actions().filter((a) => a.status === "pending" || a.status === "delivering").length,
|
|
398
422
|
uncertainEffects: this.effects().filter((e) => e.status === "uncertain")
|
|
399
423
|
.length,
|
|
400
424
|
sandboxes: this.s.docs.sandboxes.size,
|
|
401
425
|
definitions: this.s.docs.definitions.size,
|
|
402
426
|
};
|
|
403
427
|
}
|
|
404
|
-
// ---
|
|
405
|
-
async
|
|
428
|
+
// --- basin generations --------------------------------------------------
|
|
429
|
+
async basinGenerations() {
|
|
406
430
|
this.check();
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
if (rows.length >= limit)
|
|
413
|
-
return rows;
|
|
414
|
-
rows.push({ sessionId, seq, event: JSON.parse(bySeq.get(seq)) });
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
return rows;
|
|
418
|
-
}
|
|
419
|
-
async deleteOutbox(sessionId, throughSeq, incarnation) {
|
|
420
|
-
this.check();
|
|
421
|
-
const rows = this.s.outbox.get(sessionId);
|
|
422
|
-
if (!rows)
|
|
423
|
-
return 0;
|
|
424
|
-
if (incarnation !== undefined) {
|
|
425
|
-
const doc = this.s.docs.sessions.get(sessionId);
|
|
426
|
-
if (doc === undefined ||
|
|
427
|
-
(JSON.parse(doc).streamIncarnation ?? null) !== incarnation)
|
|
428
|
-
return 0;
|
|
429
|
-
}
|
|
430
|
-
let n = 0;
|
|
431
|
-
for (const seq of [...rows.keys()])
|
|
432
|
-
if (seq <= throughSeq) {
|
|
433
|
-
rows.delete(seq);
|
|
434
|
-
n += 1;
|
|
435
|
-
}
|
|
436
|
-
if (rows.size === 0)
|
|
437
|
-
this.s.outbox.delete(sessionId);
|
|
438
|
-
return n;
|
|
439
|
-
}
|
|
440
|
-
async outboxStats() {
|
|
441
|
-
this.check();
|
|
442
|
-
let depth = 0;
|
|
443
|
-
let oldestCreatedAt = null;
|
|
444
|
-
for (const rows of this.s.outbox.values())
|
|
445
|
-
for (const body of rows.values()) {
|
|
446
|
-
depth += 1;
|
|
447
|
-
const createdAt = JSON.parse(body).createdAt;
|
|
448
|
-
if (oldestCreatedAt === null || createdAt < oldestCreatedAt)
|
|
449
|
-
oldestCreatedAt = createdAt;
|
|
450
|
-
}
|
|
451
|
-
return { depth, oldestCreatedAt };
|
|
431
|
+
return copy(this.s.basin);
|
|
432
|
+
}
|
|
433
|
+
async forgetRetiredGeneration(generation) {
|
|
434
|
+
this.check();
|
|
435
|
+
this.s.basin.retired = this.s.basin.retired.filter((g) => g !== generation);
|
|
452
436
|
}
|
|
453
|
-
// ---
|
|
454
|
-
async
|
|
437
|
+
// --- Action endpoints -----------------------------------------------------
|
|
438
|
+
async listEndpoints() {
|
|
455
439
|
this.check();
|
|
456
|
-
return byId(this.s.
|
|
440
|
+
return byId(this.s.endpoints).map(([, row]) => copy(row));
|
|
457
441
|
}
|
|
458
|
-
async
|
|
442
|
+
async getEndpoint(agentId) {
|
|
459
443
|
this.check();
|
|
460
|
-
const row = this.s.
|
|
444
|
+
const row = this.s.endpoints.get(agentId);
|
|
461
445
|
return row && copy(row);
|
|
462
446
|
}
|
|
463
|
-
async
|
|
447
|
+
async putEndpoint(row) {
|
|
464
448
|
this.check();
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
const existing = this.s.executors.get(row.agentId);
|
|
469
|
-
this.s.executors.set(row.agentId, {
|
|
449
|
+
const existing = this.s.endpoints.get(row.agentId);
|
|
450
|
+
const health = existing && existing.url === row.url ? healthOf(existing) : { consecutiveFailures: 0 };
|
|
451
|
+
this.s.endpoints.set(row.agentId, {
|
|
470
452
|
...copy(row),
|
|
453
|
+
...health,
|
|
471
454
|
createdAt: existing?.createdAt ?? row.updatedAt,
|
|
472
455
|
});
|
|
473
456
|
}
|
|
474
|
-
async
|
|
457
|
+
async deleteEndpoint(agentId) {
|
|
475
458
|
this.check();
|
|
476
|
-
this.s.
|
|
459
|
+
this.s.endpoints.delete(agentId);
|
|
460
|
+
}
|
|
461
|
+
async recordEndpointHealth(agentId, update) {
|
|
462
|
+
this.check();
|
|
463
|
+
const row = this.s.endpoints.get(agentId);
|
|
464
|
+
if (row)
|
|
465
|
+
this.s.endpoints.set(agentId, withHealth(row, update));
|
|
477
466
|
}
|
|
478
467
|
// --- principals ----------------------------------------------------------
|
|
479
468
|
async insertPrincipal(row) {
|
|
@@ -602,6 +591,119 @@ class MemoryTx {
|
|
|
602
591
|
throw new Error("vault_idempotency.id must be unique");
|
|
603
592
|
this.s.idempotency.set(row.id, copy(row));
|
|
604
593
|
}
|
|
594
|
+
// --- subject tokens ------------------------------------------------------
|
|
595
|
+
/** Transactions are serialized already. */
|
|
596
|
+
async lockSigningKeys() {
|
|
597
|
+
this.check();
|
|
598
|
+
}
|
|
599
|
+
async insertSigningKey(row) {
|
|
600
|
+
this.check();
|
|
601
|
+
if (this.s.signingKeys.has(row.id))
|
|
602
|
+
throw new Error("signing_keys.id must be unique");
|
|
603
|
+
if (row.state !== "revoked")
|
|
604
|
+
for (const other of this.s.signingKeys.values())
|
|
605
|
+
if (other.state === row.state)
|
|
606
|
+
throw new Error(`signing_keys allows one ${row.state} key`);
|
|
607
|
+
this.s.signingKeys.set(row.id, copy(row));
|
|
608
|
+
}
|
|
609
|
+
async signingKey(id) {
|
|
610
|
+
this.check();
|
|
611
|
+
const row = this.s.signingKeys.get(id);
|
|
612
|
+
return row && copy(row);
|
|
613
|
+
}
|
|
614
|
+
async signingKeys(states) {
|
|
615
|
+
this.check();
|
|
616
|
+
return [...this.s.signingKeys.values()]
|
|
617
|
+
.filter((row) => states === undefined || states.includes(row.state))
|
|
618
|
+
.sort(byCreated)
|
|
619
|
+
.map(copy);
|
|
620
|
+
}
|
|
621
|
+
async setSigningKeyState(id, from, to, at) {
|
|
622
|
+
this.check();
|
|
623
|
+
const row = this.s.signingKeys.get(id);
|
|
624
|
+
if (!row || row.state !== from)
|
|
625
|
+
return false;
|
|
626
|
+
if (to !== "revoked")
|
|
627
|
+
for (const other of this.s.signingKeys.values())
|
|
628
|
+
if (other.id !== id && other.state === to)
|
|
629
|
+
throw new Error(`signing_keys allows one ${to} key`);
|
|
630
|
+
this.s.signingKeys.set(id, { ...row, ...signingKeyStamp(to, at), state: to });
|
|
631
|
+
return true;
|
|
632
|
+
}
|
|
633
|
+
async countSigningKeys() {
|
|
634
|
+
this.check();
|
|
635
|
+
return this.s.signingKeys.size;
|
|
636
|
+
}
|
|
637
|
+
async subjectEpoch(subject) {
|
|
638
|
+
this.check();
|
|
639
|
+
return this.s.subjectEpochs.get(subject) ?? 0;
|
|
640
|
+
}
|
|
641
|
+
async subjectEpochs(subjects) {
|
|
642
|
+
this.check();
|
|
643
|
+
const out = new Map();
|
|
644
|
+
for (const subject of subjects) {
|
|
645
|
+
const epoch = this.s.subjectEpochs.get(subject);
|
|
646
|
+
if (epoch !== undefined)
|
|
647
|
+
out.set(subject, epoch);
|
|
648
|
+
}
|
|
649
|
+
return out;
|
|
650
|
+
}
|
|
651
|
+
async bumpSubjectEpoch(subject, _at) {
|
|
652
|
+
this.check();
|
|
653
|
+
const epoch = (this.s.subjectEpochs.get(subject) ?? 0) + 1;
|
|
654
|
+
this.s.subjectEpochs.set(subject, epoch);
|
|
655
|
+
return epoch;
|
|
656
|
+
}
|
|
657
|
+
async lockSubjectUsage(initial) {
|
|
658
|
+
this.check();
|
|
659
|
+
// Transactions are serialized, so the row is as good as locked.
|
|
660
|
+
const row = this.s.subjectUsage.get(initial.subject);
|
|
661
|
+
if (row)
|
|
662
|
+
return copy(row);
|
|
663
|
+
this.s.subjectUsage.set(initial.subject, copy(initial));
|
|
664
|
+
return copy(initial);
|
|
665
|
+
}
|
|
666
|
+
async putSubjectUsage(row) {
|
|
667
|
+
this.check();
|
|
668
|
+
this.s.subjectUsage.set(row.subject, copy(row));
|
|
669
|
+
}
|
|
670
|
+
async countOwnerSessions(ownerUserId, statuses) {
|
|
671
|
+
this.check();
|
|
672
|
+
return this.sessions().filter((s) => s.ownerUserId === ownerUserId &&
|
|
673
|
+
statuses.includes(s.status ?? "")).length;
|
|
674
|
+
}
|
|
675
|
+
// --- publishable keys ----------------------------------------------------
|
|
676
|
+
async insertPublishableKey(row) {
|
|
677
|
+
this.check();
|
|
678
|
+
for (const other of this.s.publishableKeys.values())
|
|
679
|
+
if (other.id === row.id || other.key === row.key || other.name === row.name)
|
|
680
|
+
throw new Error("publishable_keys id, key and name must be unique");
|
|
681
|
+
this.s.publishableKeys.set(row.id, copy(row));
|
|
682
|
+
}
|
|
683
|
+
async publishableKeyByKey(key) {
|
|
684
|
+
this.check();
|
|
685
|
+
for (const row of this.s.publishableKeys.values())
|
|
686
|
+
if (row.key === key)
|
|
687
|
+
return copy(row);
|
|
688
|
+
return undefined;
|
|
689
|
+
}
|
|
690
|
+
async publishableKey(id) {
|
|
691
|
+
this.check();
|
|
692
|
+
const row = this.s.publishableKeys.get(id);
|
|
693
|
+
return row && copy(row);
|
|
694
|
+
}
|
|
695
|
+
async publishableKeys() {
|
|
696
|
+
this.check();
|
|
697
|
+
return [...this.s.publishableKeys.values()].sort(byCreated).map(copy);
|
|
698
|
+
}
|
|
699
|
+
async updatePublishableKey(id, patch) {
|
|
700
|
+
this.check();
|
|
701
|
+
const row = this.s.publishableKeys.get(id);
|
|
702
|
+
if (!row)
|
|
703
|
+
return false;
|
|
704
|
+
this.s.publishableKeys.set(id, { ...row, ...copy(patch) });
|
|
705
|
+
return true;
|
|
706
|
+
}
|
|
605
707
|
// --- settings ------------------------------------------------------------
|
|
606
708
|
async getSetting(key) {
|
|
607
709
|
this.check();
|
|
@@ -625,21 +727,84 @@ class MemoryTx {
|
|
|
625
727
|
])
|
|
626
728
|
this.s.docs[table].clear();
|
|
627
729
|
this.s.sessionMeta.clear();
|
|
628
|
-
this.s.
|
|
730
|
+
this.s.heads.clear();
|
|
731
|
+
this.s.subjectUsage.clear();
|
|
732
|
+
this.s.basin = {
|
|
733
|
+
current: this.s.basin.current + 1,
|
|
734
|
+
retired: [...this.s.basin.retired, this.s.basin.current],
|
|
735
|
+
};
|
|
736
|
+
this.recordChanges.push({ kind: "reset", generation: this.s.basin.current });
|
|
629
737
|
}
|
|
630
738
|
if (scope === "sandboxes" || scope === "all")
|
|
631
739
|
this.s.docs.sandboxes.clear();
|
|
632
740
|
if (scope === "all") {
|
|
633
741
|
this.s.docs.definitions.clear();
|
|
634
|
-
this.s.
|
|
742
|
+
this.s.endpoints.clear();
|
|
635
743
|
for (const vault of [...this.s.vaults.values()])
|
|
636
744
|
if (vault.scope !== "host")
|
|
637
745
|
await this.deleteVault(vault.id);
|
|
638
746
|
}
|
|
639
747
|
}
|
|
640
748
|
}
|
|
749
|
+
/** The time column a signing key's new state stamps. */
|
|
750
|
+
export function signingKeyStamp(state, at) {
|
|
751
|
+
if (state === "current")
|
|
752
|
+
return { activatedAt: at };
|
|
753
|
+
if (state === "previous")
|
|
754
|
+
return { retiredAt: at };
|
|
755
|
+
if (state === "revoked")
|
|
756
|
+
return { revokedAt: at };
|
|
757
|
+
return {};
|
|
758
|
+
}
|
|
641
759
|
function byCreated(a, b) {
|
|
642
760
|
if (a.createdAt !== b.createdAt)
|
|
643
761
|
return a.createdAt < b.createdAt ? -1 : 1;
|
|
644
762
|
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
645
763
|
}
|
|
764
|
+
const HEALTH_KEYS = [
|
|
765
|
+
"lastDeliveryAt",
|
|
766
|
+
"lastSuccessAt",
|
|
767
|
+
"lastErrorCode",
|
|
768
|
+
"lastErrorMessage",
|
|
769
|
+
"consecutiveFailures",
|
|
770
|
+
"servedImplementationVersion",
|
|
771
|
+
"servedManifestHash",
|
|
772
|
+
];
|
|
773
|
+
/** The health fields of an endpoint row. */
|
|
774
|
+
function healthOf(row) {
|
|
775
|
+
const health = {};
|
|
776
|
+
for (const key of HEALTH_KEYS)
|
|
777
|
+
if (row[key] !== undefined)
|
|
778
|
+
health[key] = row[key];
|
|
779
|
+
return { ...health, consecutiveFailures: row.consecutiveFailures };
|
|
780
|
+
}
|
|
781
|
+
/** `row` after one health observation (the same rules as the Postgres store). */
|
|
782
|
+
function withHealth(row, update) {
|
|
783
|
+
switch (update.kind) {
|
|
784
|
+
case "success": {
|
|
785
|
+
const { lastErrorCode: _c, lastErrorMessage: _m, ...rest } = row;
|
|
786
|
+
return {
|
|
787
|
+
...rest,
|
|
788
|
+
lastDeliveryAt: update.at,
|
|
789
|
+
lastSuccessAt: update.at,
|
|
790
|
+
consecutiveFailures: 0,
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
case "failure":
|
|
794
|
+
return {
|
|
795
|
+
...row,
|
|
796
|
+
lastDeliveryAt: update.at,
|
|
797
|
+
lastErrorCode: update.code,
|
|
798
|
+
lastErrorMessage: update.message,
|
|
799
|
+
consecutiveFailures: row.consecutiveFailures + 1,
|
|
800
|
+
};
|
|
801
|
+
case "served": {
|
|
802
|
+
const { servedManifestHash: _h, ...rest } = row;
|
|
803
|
+
return {
|
|
804
|
+
...rest,
|
|
805
|
+
servedImplementationVersion: update.implementationVersion,
|
|
806
|
+
...(update.manifestHash === undefined ? {} : { servedManifestHash: update.manifestHash }),
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
@@ -9,6 +9,16 @@ export interface PostgresClientOptions {
|
|
|
9
9
|
connectTimeoutSeconds?: number;
|
|
10
10
|
/** `application_name` on every connection. Default `nylorun-runtime`. */
|
|
11
11
|
applicationName?: string;
|
|
12
|
+
/**
|
|
13
|
+
* `statement_timeout`: a statement running or waiting for a lock longer fails. Default
|
|
14
|
+
* 60 s; 0 turns it off. Schema migrations lift it (`lockSchema`).
|
|
15
|
+
*/
|
|
16
|
+
statementTimeoutMs?: number;
|
|
17
|
+
/**
|
|
18
|
+
* `idle_in_transaction_session_timeout`: a transaction left idle this long is ended and its
|
|
19
|
+
* connection closed, releasing its locks. Default 60 s; 0 turns it off.
|
|
20
|
+
*/
|
|
21
|
+
idleInTransactionTimeoutMs?: number;
|
|
12
22
|
}
|
|
13
23
|
/**
|
|
14
24
|
* Opens a connection pool for Session Stores and the Tenant catalog. One pool
|
|
@@ -21,5 +31,8 @@ export interface PostgresClientOptions {
|
|
|
21
31
|
* named prepared statements would pile up on every pooled connection with the
|
|
22
32
|
* number of Tenants a Host serves. Unnamed statements still go in one round
|
|
23
33
|
* trip; the queries are simple enough that re-planning them costs little.
|
|
34
|
+
*
|
|
35
|
+
* The pool is shared by every Tenant, so a statement or transaction that hangs must not hold
|
|
36
|
+
* a connection forever: both are bounded by default.
|
|
24
37
|
*/
|
|
25
38
|
export declare function createPostgresClient(url: string, options?: PostgresClientOptions): PostgresClient;
|
|
@@ -10,6 +10,9 @@ import postgres, {} from "postgres";
|
|
|
10
10
|
* named prepared statements would pile up on every pooled connection with the
|
|
11
11
|
* number of Tenants a Host serves. Unnamed statements still go in one round
|
|
12
12
|
* trip; the queries are simple enough that re-planning them costs little.
|
|
13
|
+
*
|
|
14
|
+
* The pool is shared by every Tenant, so a statement or transaction that hangs must not hold
|
|
15
|
+
* a connection forever: both are bounded by default.
|
|
13
16
|
*/
|
|
14
17
|
export function createPostgresClient(url, options = {}) {
|
|
15
18
|
return postgres(url, {
|
|
@@ -20,6 +23,8 @@ export function createPostgresClient(url, options = {}) {
|
|
|
20
23
|
onnotice: () => { },
|
|
21
24
|
connection: {
|
|
22
25
|
application_name: options.applicationName ?? "nylorun-runtime",
|
|
26
|
+
statement_timeout: options.statementTimeoutMs ?? 60_000,
|
|
27
|
+
idle_in_transaction_session_timeout: options.idleInTransactionTimeoutMs ?? 60_000,
|
|
23
28
|
},
|
|
24
29
|
});
|
|
25
30
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Migration } from "./index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Subject tokens (Host feature `subject-tokens`): the Tenant's signing keys (private halves
|
|
4
|
+
* sealed with the vault KEK, like credentials), each subject's revocation epoch, each
|
|
5
|
+
* subject's turn bucket. Counting a subject's sessions by status uses the owner index of
|
|
6
|
+
* migration 2; a second index on status would be written on every session transition.
|
|
7
|
+
*/
|
|
8
|
+
export declare const subjectTokens: Migration;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subject tokens (Host feature `subject-tokens`): the Tenant's signing keys (private halves
|
|
3
|
+
* sealed with the vault KEK, like credentials), each subject's revocation epoch, each
|
|
4
|
+
* subject's turn bucket. Counting a subject's sessions by status uses the owner index of
|
|
5
|
+
* migration 2; a second index on status would be written on every session transition.
|
|
6
|
+
*/
|
|
7
|
+
export const subjectTokens = {
|
|
8
|
+
version: 3,
|
|
9
|
+
name: "subject_tokens",
|
|
10
|
+
up: (s) => `
|
|
11
|
+
CREATE TABLE ${s}.signing_keys (
|
|
12
|
+
id text COLLATE "C" PRIMARY KEY,
|
|
13
|
+
state text NOT NULL CHECK (state IN ('standby', 'current', 'previous', 'revoked')),
|
|
14
|
+
alg text NOT NULL CHECK (alg = 'ES256'),
|
|
15
|
+
public_jwk text NOT NULL,
|
|
16
|
+
kek_id text NOT NULL,
|
|
17
|
+
nonce bytea NOT NULL,
|
|
18
|
+
ciphertext bytea NOT NULL,
|
|
19
|
+
wrapped_dek bytea NOT NULL,
|
|
20
|
+
created_at text NOT NULL,
|
|
21
|
+
activated_at text,
|
|
22
|
+
retired_at text,
|
|
23
|
+
revoked_at text
|
|
24
|
+
);
|
|
25
|
+
CREATE UNIQUE INDEX signing_keys_one_standby ON ${s}.signing_keys (state) WHERE state = 'standby';
|
|
26
|
+
CREATE UNIQUE INDEX signing_keys_one_current ON ${s}.signing_keys (state) WHERE state = 'current';
|
|
27
|
+
CREATE UNIQUE INDEX signing_keys_one_previous ON ${s}.signing_keys (state) WHERE state = 'previous';
|
|
28
|
+
|
|
29
|
+
CREATE TABLE ${s}.subject_epochs (
|
|
30
|
+
subject text COLLATE "C" PRIMARY KEY,
|
|
31
|
+
epoch bigint NOT NULL,
|
|
32
|
+
revoked_at text NOT NULL
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
CREATE TABLE ${s}.subject_usage (
|
|
36
|
+
subject text COLLATE "C" PRIMARY KEY,
|
|
37
|
+
turn_tokens double precision NOT NULL,
|
|
38
|
+
refilled_at text NOT NULL
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
`,
|
|
42
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Publishable keys (Host feature `browser-access`): public by design, so stored as they are,
|
|
3
|
+
* with the origins a browser may use each from.
|
|
4
|
+
*/
|
|
5
|
+
export const publishableKeys = {
|
|
6
|
+
version: 4,
|
|
7
|
+
name: "publishable_keys",
|
|
8
|
+
up: (s) => `
|
|
9
|
+
CREATE TABLE ${s}.publishable_keys (
|
|
10
|
+
id text COLLATE "C" PRIMARY KEY,
|
|
11
|
+
key text COLLATE "C" NOT NULL UNIQUE,
|
|
12
|
+
name text NOT NULL UNIQUE,
|
|
13
|
+
origins_json text NOT NULL,
|
|
14
|
+
created_at text NOT NULL,
|
|
15
|
+
revoked_at text
|
|
16
|
+
);
|
|
17
|
+
`,
|
|
18
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Migration } from "./index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Action endpoints: where the Runtime delivers each agent's Actions over HTTP, with the health
|
|
4
|
+
* recent deliveries report, and the deadline of an Action being delivered.
|
|
5
|
+
*/
|
|
6
|
+
export declare const actionEndpoints: Migration;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action endpoints: where the Runtime delivers each agent's Actions over HTTP, with the health
|
|
3
|
+
* recent deliveries report, and the deadline of an Action being delivered.
|
|
4
|
+
*/
|
|
5
|
+
export const actionEndpoints = {
|
|
6
|
+
version: 5,
|
|
7
|
+
name: "action_endpoints",
|
|
8
|
+
up: (s) => `
|
|
9
|
+
CREATE TABLE ${s}.endpoints (
|
|
10
|
+
agent_id text COLLATE "C" PRIMARY KEY,
|
|
11
|
+
url text NOT NULL,
|
|
12
|
+
implementation_version text NOT NULL,
|
|
13
|
+
manifest_hash text,
|
|
14
|
+
timeout_ms integer NOT NULL,
|
|
15
|
+
max_concurrent integer NOT NULL,
|
|
16
|
+
principal_id text,
|
|
17
|
+
last_delivery_at text,
|
|
18
|
+
last_success_at text,
|
|
19
|
+
last_error_code text,
|
|
20
|
+
last_error_message text,
|
|
21
|
+
consecutive_failures integer NOT NULL DEFAULT 0,
|
|
22
|
+
served_implementation_version text,
|
|
23
|
+
served_manifest_hash text,
|
|
24
|
+
created_at text NOT NULL,
|
|
25
|
+
updated_at text NOT NULL
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
ALTER TABLE ${s}.actions
|
|
29
|
+
ADD COLUMN deadline_at text GENERATED ALWAYS AS (${s}.doc(body)->>'deadlineAt') STORED;
|
|
30
|
+
CREATE INDEX actions_deadline ON ${s}.actions (status, deadline_at);
|
|
31
|
+
`,
|
|
32
|
+
};
|