@nylorun/runtime 0.27.0-beta → 0.29.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 +67 -0
- package/README.md +1 -1
- package/dist/api/a2a/card.d.ts +4 -3
- package/dist/api/a2a/card.js +3 -3
- package/dist/api/a2a/routes.js +18 -7
- package/dist/api/ag-ui/routes.js +4 -7
- package/dist/api/components.d.ts +5854 -2718
- package/dist/api/components.js +39 -30
- package/dist/api/http/app.js +9 -4
- package/dist/api/http/body.d.ts +2 -0
- package/dist/api/http/body.js +20 -0
- package/dist/api/http/define.d.ts +4 -0
- package/dist/api/http/define.js +20 -9
- package/dist/api/http/routes/agent-manifests.d.ts +10 -0
- package/dist/api/http/routes/agent-manifests.js +135 -0
- package/dist/api/http/routes/agents.d.ts +11 -0
- package/dist/api/http/routes/agents.js +418 -0
- package/dist/api/http/routes/artifacts.js +2 -2
- package/dist/api/http/routes/coverage.d.ts +4 -4
- package/dist/api/http/routes/coverage.js +12 -6
- package/dist/api/http/routes/files.js +1 -1
- package/dist/api/http/routes/mcp.js +2 -2
- package/dist/api/http/routes/me.d.ts +1 -1
- package/dist/api/http/routes/me.js +1 -2
- package/dist/api/http/routes/memories.d.ts +9 -0
- package/dist/api/http/routes/memories.js +152 -0
- package/dist/api/http/routes/reads.d.ts +1 -137
- package/dist/api/http/routes/reads.js +31 -12
- package/dist/api/http/routes/sessions.js +21 -50
- package/dist/api/http/routes/tenant.js +13 -10
- package/dist/api/http/routes/triggers.d.ts +10 -0
- package/dist/api/http/routes/triggers.js +183 -0
- package/dist/api/openapi.js +69 -47
- package/dist/artifacts/disk.d.ts +10 -10
- package/dist/artifacts/disk.js +27 -27
- package/dist/artifacts/quota.d.ts +1 -1
- package/dist/artifacts/quota.js +2 -2
- package/dist/artifacts/service.js +2 -2
- package/dist/core/flow-host.d.ts +0 -1
- package/dist/gates/egress-credentials.js +1 -1
- package/dist/gates/egress.d.ts +5 -5
- package/dist/gates/egress.js +6 -6
- package/dist/gates/http-tool.js +2 -2
- package/dist/gates/meter.js +22 -5
- package/dist/gates/policy.d.ts +8 -3
- package/dist/gates/policy.js +7 -2
- package/dist/gates/tenant-vaults.d.ts +1 -1
- package/dist/harness/pod.d.ts +2 -0
- package/dist/harness/setup.js +17 -6
- package/dist/harness-api/record.js +11 -0
- package/dist/harness-api/server.js +4 -0
- package/dist/harness-api/start.d.ts +11 -7
- package/dist/harness-api/start.js +49 -5
- package/dist/harness-api/workspace.js +2 -2
- package/dist/host/config-for.d.ts +2 -0
- package/dist/host/config-for.js +1 -0
- package/dist/host/http.d.ts +2 -2
- package/dist/host/http.js +5 -2
- package/dist/host/main.js +1 -0
- package/dist/keys/keys.d.ts +15 -4
- package/dist/keys/keys.js +16 -0
- package/dist/keys/webhooks.d.ts +25 -0
- package/dist/keys/webhooks.js +62 -0
- package/dist/management-openapi.json +313 -133
- package/dist/mcp/connect.d.ts +2 -2
- package/dist/mcp/connect.js +3 -3
- package/dist/mcp/preview.js +7 -7
- package/dist/memory/service.d.ts +97 -0
- package/dist/memory/service.js +335 -0
- package/dist/memory/tool.d.ts +36 -0
- package/dist/memory/tool.js +84 -0
- package/dist/model/pi-model.js +7 -0
- package/dist/openapi.json +13120 -9034
- package/dist/reads/types.d.ts +3 -3
- package/dist/record/agent.d.ts +27 -0
- package/dist/record/agent.js +46 -0
- package/dist/record/index.d.ts +1 -1
- package/dist/record/index.js +1 -1
- package/dist/sandbox/join.js +5 -5
- package/dist/sandbox/placement.d.ts +1 -1
- package/dist/sandbox/placement.js +3 -3
- package/dist/sandbox/pods/lifecycle.d.ts +6 -6
- package/dist/sandbox/pods/lifecycle.js +6 -6
- package/dist/sandbox/pods/reconcile.js +10 -2
- package/dist/sandbox/resolve.d.ts +3 -3
- package/dist/sandbox/resolve.js +7 -7
- package/dist/sandbox/setup.d.ts +3 -1
- package/dist/sandbox/setup.js +14 -4
- package/dist/store/postgres/drizzle/0032_agents.sql +54 -0
- package/dist/store/postgres/drizzle/0033_agent_versions.sql +7 -0
- package/dist/store/postgres/drizzle/0034_protocol_17_session_queue.sql +13 -0
- package/dist/store/postgres/drizzle/0035_t4_triggers.sql +41 -0
- package/dist/store/postgres/drizzle/0036_t5_memories.sql +30 -0
- package/dist/store/postgres/drizzle/0037_model_usage_cost_split.sql +4 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +42 -0
- package/dist/store/postgres/reads.js +44 -87
- package/dist/store/postgres/record-writer.d.ts +4 -4
- package/dist/store/postgres/record-writer.js +9 -9
- package/dist/store/postgres/schema.d.ts +1408 -123
- package/dist/store/postgres/schema.js +194 -38
- package/dist/store/postgres/store.js +329 -79
- package/dist/store/postgres/tenant.js +1 -2
- package/dist/store/types.d.ts +155 -50
- package/dist/store/types.js +0 -1
- package/dist/tenant/advance.js +20 -8
- package/dist/tenant/agent-manifests.d.ts +72 -0
- package/dist/tenant/agent-manifests.js +359 -0
- package/dist/tenant/agent-versions.d.ts +126 -0
- package/dist/tenant/agent-versions.js +389 -0
- package/dist/tenant/agents.d.ts +230 -0
- package/dist/tenant/{environments.js → agents.js} +464 -320
- package/dist/tenant/auth.d.ts +1 -1
- package/dist/tenant/auth.js +3 -3
- package/dist/tenant/commands.d.ts +63 -2
- package/dist/tenant/commands.js +240 -83
- package/dist/tenant/context.d.ts +46 -19
- package/dist/tenant/context.js +6 -2
- package/dist/tenant/effects.js +7 -16
- package/dist/tenant/identity-file.d.ts +6 -6
- package/dist/tenant/identity-file.js +12 -10
- package/dist/tenant/issuers.js +3 -3
- package/dist/tenant/limits.d.ts +0 -3
- package/dist/tenant/limits.js +0 -3
- package/dist/tenant/memories.d.ts +38 -0
- package/dist/tenant/memories.js +119 -0
- package/dist/tenant/reset.js +2 -2
- package/dist/tenant/run-grants.d.ts +6 -0
- package/dist/tenant/run-grants.js +4 -0
- package/dist/tenant/runtime.d.ts +1 -1
- package/dist/tenant/runtime.js +8 -0
- package/dist/tenant/sessions.d.ts +28 -45
- package/dist/tenant/sessions.js +85 -208
- package/dist/tenant/status.js +6 -7
- package/dist/tenant/sweep.js +12 -3
- package/dist/tenant/transcript.d.ts +16 -16
- package/dist/tenant/triggers.d.ts +143 -0
- package/dist/tenant/triggers.js +715 -0
- package/dist/tenant/turn-timing.d.ts +11 -1
- package/dist/tenant/turn-timing.js +3 -1
- package/dist/tenant/types.d.ts +5 -0
- package/dist/tenant/worker.d.ts +2 -0
- package/dist/tenant/worker.js +5 -0
- package/dist/traces/map.d.ts +1 -1
- package/dist/traces/map.js +7 -1
- package/dist/tuning.d.ts +7 -0
- package/dist/tuning.js +19 -0
- package/dist/vault/service.d.ts +13 -8
- package/dist/vault/service.js +61 -17
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +13 -12
- package/dist/api/http/routes/environment-manifests.d.ts +0 -9
- package/dist/api/http/routes/environment-manifests.js +0 -118
- package/dist/api/http/routes/environments.d.ts +0 -11
- package/dist/api/http/routes/environments.js +0 -332
- package/dist/record/environment.d.ts +0 -27
- package/dist/record/environment.js +0 -46
- package/dist/tenant/environment-manifests.d.ts +0 -57
- package/dist/tenant/environment-manifests.js +0 -208
- package/dist/tenant/environments.d.ts +0 -172
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
52
52
|
import { randomUUID } from "node:crypto";
|
|
53
53
|
import { and, count, desc, eq, getTableColumns, gte, gt, inArray, isNotNull, isNull, lt, lte, ne, or, sql, } from "drizzle-orm";
|
|
54
|
-
import {
|
|
54
|
+
import { appendAgentEvent, appendEvent } from "../../record/index.js";
|
|
55
55
|
import { readRuntimeLimits } from "../../tenant/runtime-limits.js";
|
|
56
56
|
import { OwnershipLostError, PrincipalRoleConflict } from "../ownership.js";
|
|
57
57
|
import { followControlSignals, pruneSignals, writeSignal } from "./control.js";
|
|
@@ -59,8 +59,8 @@ import { database, driverError } from "./db.js";
|
|
|
59
59
|
import { expectedSchemaVersion, readSchemaVersion } from "./migrate.js";
|
|
60
60
|
import { hostTuning } from "../../tuning.js";
|
|
61
61
|
import { createPostgresRecordReader } from "./record.js";
|
|
62
|
-
import {
|
|
63
|
-
import { artifacts, artifactVersions, artifactContent, definitionFiles, definitionFileUses, commands,
|
|
62
|
+
import { postgresAgentRecordWriter, postgresRecordWriter } from "./record-writer.js";
|
|
63
|
+
import { artifacts, artifactVersions, artifactContent, definitionFiles, definitionFileUses, commands, effects, links, modelBudgets, modelUsage, principals, sandboxes, agentEvents, agents, agentManifests, sessionEvents, sessionLogHeads, sessions, sessionQueue, agentTriggers, triggerRuns, memories, memoryVersions, toJson, toolCrossings, signingKeys, tenant, tenantSettings, vaultAudit, vaultCredentials, vaultIdempotency, vaults, sandboxSignals, wakes, } from "./schema.js";
|
|
64
64
|
export function createPostgresSessionStore(options) {
|
|
65
65
|
return new PostgresSessionStore(options);
|
|
66
66
|
}
|
|
@@ -68,7 +68,6 @@ const OPEN_SESSION = ["running", "runnable"];
|
|
|
68
68
|
/** Rows per statement when a folder version or a definition names many files (Host tuning). */
|
|
69
69
|
const contentBatch = () => hostTuning().postgres.contentBatchRows;
|
|
70
70
|
const DOCUMENTS = {
|
|
71
|
-
definitions,
|
|
72
71
|
sessions: sessions,
|
|
73
72
|
commands,
|
|
74
73
|
effects: effects,
|
|
@@ -257,10 +256,15 @@ function environmentOf(row) {
|
|
|
257
256
|
updatedAt: row.updatedAt,
|
|
258
257
|
...(row.diskOp ? { diskOp: row.diskOp } : {}),
|
|
259
258
|
...(row.diskSeed ? { diskSeed: row.diskSeed } : {}),
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
259
|
+
manifestName: row.manifestName,
|
|
260
|
+
manifestVersion: row.manifestVersion,
|
|
261
|
+
manifestRef: row.manifestRef,
|
|
262
|
+
manifestHash: row.manifestHash,
|
|
263
|
+
desiredState: row.desiredState,
|
|
263
264
|
...(row.forkedFrom ? { forkedFrom: row.forkedFrom } : {}),
|
|
265
|
+
...(row.targetVersion !== null ? { targetVersion: row.targetVersion } : {}),
|
|
266
|
+
...(row.failedVersion !== null ? { failedVersion: row.failedVersion } : {}),
|
|
267
|
+
...(row.startCheck ? { startCheck: row.startCheck } : {}),
|
|
264
268
|
...(row.k8sName !== null && row.desired !== null && row.observed !== null
|
|
265
269
|
? {
|
|
266
270
|
pod: {
|
|
@@ -283,7 +287,7 @@ function environmentOf(row) {
|
|
|
283
287
|
: {}),
|
|
284
288
|
};
|
|
285
289
|
}
|
|
286
|
-
function
|
|
290
|
+
function agentManifestOf(row) {
|
|
287
291
|
return {
|
|
288
292
|
name: row.name,
|
|
289
293
|
version: row.version,
|
|
@@ -322,7 +326,7 @@ const SIGNING_KEY_STAMP = {
|
|
|
322
326
|
};
|
|
323
327
|
/** The audit columns a row has (`ord` only orders them). */
|
|
324
328
|
const { ord: _ord, ...AUDIT } = getTableColumns(vaultAudit);
|
|
325
|
-
const SESSION_TABLES = [sessions, commands, effects, links, wakes];
|
|
329
|
+
const SESSION_TABLES = [sessions, commands, effects, links, wakes, sessionQueue];
|
|
326
330
|
/**
|
|
327
331
|
* The outbox's tables, by the kind of request each holds. Their delivery columns (`id`,
|
|
328
332
|
* `created_at`, `attempts`, `retry_at`, `parked_at`) are the same.
|
|
@@ -629,44 +633,40 @@ class PostgresTx {
|
|
|
629
633
|
const rows = await this.db.select({ body: table.body }).from(table).orderBy(table.id);
|
|
630
634
|
return rows.map((row) => row.body);
|
|
631
635
|
}
|
|
632
|
-
async listDefinitions() {
|
|
633
|
-
this.check();
|
|
634
|
-
return this.bodies(definitions);
|
|
635
|
-
}
|
|
636
636
|
async listSandboxes() {
|
|
637
637
|
this.check();
|
|
638
638
|
return this.bodies(sandboxes);
|
|
639
639
|
}
|
|
640
|
-
// ---
|
|
640
|
+
// --- Agents ----------------------------------------------------------------
|
|
641
641
|
async environment(id, options = {}) {
|
|
642
642
|
this.check();
|
|
643
|
-
const query = this.db.select().from(
|
|
643
|
+
const query = this.db.select().from(agents).where(eq(agents.id, id));
|
|
644
644
|
const [row] = options.lock ? await query.for("update") : await query;
|
|
645
645
|
return row && environmentOf(row);
|
|
646
646
|
}
|
|
647
647
|
async createEnvironment(row, limit) {
|
|
648
648
|
this.check();
|
|
649
649
|
// Every create takes this lock, so two never both see room for one more.
|
|
650
|
-
await this.db.execute(sql `SELECT pg_advisory_xact_lock(hashtext('nylorun.
|
|
650
|
+
await this.db.execute(sql `SELECT pg_advisory_xact_lock(hashtext('nylorun.agents'))`);
|
|
651
651
|
const [found] = await this.db
|
|
652
|
-
.select({ id:
|
|
653
|
-
.from(
|
|
654
|
-
.where(eq(
|
|
652
|
+
.select({ id: agents.id })
|
|
653
|
+
.from(agents)
|
|
654
|
+
.where(eq(agents.id, row.id));
|
|
655
655
|
if (found)
|
|
656
656
|
return "exists";
|
|
657
|
-
const [counted] = await this.db.select({ n: count() }).from(
|
|
657
|
+
const [counted] = await this.db.select({ n: count() }).from(agents);
|
|
658
658
|
if ((counted?.n ?? 0) >= limit)
|
|
659
659
|
return "limit";
|
|
660
|
-
const { pod, ownerUserId, diskOp, diskSeed,
|
|
661
|
-
await this.db.insert(
|
|
660
|
+
const { pod, ownerUserId, diskOp, diskSeed, forkedFrom, targetVersion, failedVersion, startCheck, ...environment } = row;
|
|
661
|
+
await this.db.insert(agents).values({
|
|
662
662
|
...environment,
|
|
663
663
|
ownerUserId: ownerUserId ?? null,
|
|
664
664
|
diskOp: diskOp ?? null,
|
|
665
665
|
diskSeed: diskSeed ?? null,
|
|
666
|
-
manifestName: manifestName ?? null,
|
|
667
|
-
manifestVersion: manifestVersion ?? null,
|
|
668
|
-
manifestHash: manifestHash ?? null,
|
|
669
666
|
forkedFrom: forkedFrom ?? null,
|
|
667
|
+
targetVersion: targetVersion ?? null,
|
|
668
|
+
failedVersion: failedVersion ?? null,
|
|
669
|
+
startCheck: startCheck ?? null,
|
|
670
670
|
...(pod ? podColumns(pod) : {}),
|
|
671
671
|
});
|
|
672
672
|
return "created";
|
|
@@ -675,88 +675,88 @@ class PostgresTx {
|
|
|
675
675
|
this.check();
|
|
676
676
|
const columns = Object.fromEntries(Object.entries(patch).filter(([, value]) => value !== undefined));
|
|
677
677
|
await this.db
|
|
678
|
-
.update(
|
|
678
|
+
.update(agents)
|
|
679
679
|
.set({ ...columns, updatedAt })
|
|
680
|
-
.where(eq(
|
|
680
|
+
.where(eq(agents.id, id));
|
|
681
681
|
}
|
|
682
682
|
async updateEnvironmentPod(id, patch, updatedAt) {
|
|
683
683
|
this.check();
|
|
684
684
|
await this.db
|
|
685
|
-
.update(
|
|
685
|
+
.update(agents)
|
|
686
686
|
.set({ ...podColumns(patch), updatedAt })
|
|
687
|
-
.where(eq(
|
|
687
|
+
.where(eq(agents.id, id));
|
|
688
688
|
}
|
|
689
689
|
async deleteEnvironment(id) {
|
|
690
690
|
this.check();
|
|
691
691
|
// Its vault and credentials go with it (ON DELETE CASCADE).
|
|
692
|
-
await this.db.delete(
|
|
692
|
+
await this.db.delete(agents).where(eq(agents.id, id));
|
|
693
693
|
}
|
|
694
694
|
async listEnvironments(filter = {}) {
|
|
695
695
|
this.check();
|
|
696
696
|
const labels = filter.labels;
|
|
697
697
|
const rows = await this.db
|
|
698
698
|
.select()
|
|
699
|
-
.from(
|
|
699
|
+
.from(agents)
|
|
700
700
|
.where(labels === undefined || Object.keys(labels).length === 0
|
|
701
701
|
? undefined
|
|
702
|
-
: sql `nylorun.doc(${
|
|
703
|
-
.orderBy(
|
|
702
|
+
: sql `nylorun.doc(${agents.labels}) @> ${toJson(labels)}::jsonb`)
|
|
703
|
+
.orderBy(agents.id);
|
|
704
704
|
return rows.map(environmentOf);
|
|
705
705
|
}
|
|
706
|
-
// ---
|
|
707
|
-
async
|
|
706
|
+
// --- Agent manifests (protocol 16) ---------------------------------------------
|
|
707
|
+
async lockAgentManifest(name) {
|
|
708
708
|
this.check();
|
|
709
|
-
await this.db.execute(sql `SELECT pg_advisory_xact_lock(hashtext(${`nylorun.
|
|
709
|
+
await this.db.execute(sql `SELECT pg_advisory_xact_lock(hashtext(${`nylorun.agent_manifests:${name}`}))`);
|
|
710
710
|
}
|
|
711
|
-
async
|
|
711
|
+
async agentManifest(name, version) {
|
|
712
712
|
this.check();
|
|
713
713
|
const [row] = await this.db
|
|
714
714
|
.select()
|
|
715
|
-
.from(
|
|
715
|
+
.from(agentManifests)
|
|
716
716
|
.where(version === undefined
|
|
717
|
-
? eq(
|
|
718
|
-
: and(eq(
|
|
719
|
-
.orderBy(desc(
|
|
717
|
+
? eq(agentManifests.name, name)
|
|
718
|
+
: and(eq(agentManifests.name, name), eq(agentManifests.version, version)))
|
|
719
|
+
.orderBy(desc(agentManifests.version))
|
|
720
720
|
.limit(1);
|
|
721
|
-
return row &&
|
|
721
|
+
return row && agentManifestOf(row);
|
|
722
722
|
}
|
|
723
|
-
async
|
|
723
|
+
async insertAgentManifest(row) {
|
|
724
724
|
this.check();
|
|
725
|
-
await this.db.insert(
|
|
725
|
+
await this.db.insert(agentManifests).values(row);
|
|
726
726
|
}
|
|
727
|
-
async
|
|
727
|
+
async listAgentManifests() {
|
|
728
728
|
this.check();
|
|
729
729
|
const rows = await this.db
|
|
730
|
-
.selectDistinctOn([
|
|
731
|
-
.from(
|
|
732
|
-
.orderBy(
|
|
733
|
-
return rows.map(
|
|
730
|
+
.selectDistinctOn([agentManifests.name])
|
|
731
|
+
.from(agentManifests)
|
|
732
|
+
.orderBy(agentManifests.name, desc(agentManifests.version));
|
|
733
|
+
return rows.map(agentManifestOf);
|
|
734
734
|
}
|
|
735
|
-
async
|
|
735
|
+
async agentManifestVersions(name) {
|
|
736
736
|
this.check();
|
|
737
737
|
const rows = await this.db
|
|
738
738
|
.select()
|
|
739
|
-
.from(
|
|
740
|
-
.where(eq(
|
|
741
|
-
.orderBy(desc(
|
|
742
|
-
return rows.map(
|
|
739
|
+
.from(agentManifests)
|
|
740
|
+
.where(eq(agentManifests.name, name))
|
|
741
|
+
.orderBy(desc(agentManifests.version));
|
|
742
|
+
return rows.map(agentManifestOf);
|
|
743
743
|
}
|
|
744
|
-
async
|
|
744
|
+
async deleteAgentManifest(name) {
|
|
745
745
|
this.check();
|
|
746
746
|
const deleted = await this.db
|
|
747
|
-
.delete(
|
|
748
|
-
.where(eq(
|
|
749
|
-
.returning({ version:
|
|
747
|
+
.delete(agentManifests)
|
|
748
|
+
.where(eq(agentManifests.name, name))
|
|
749
|
+
.returning({ version: agentManifests.version });
|
|
750
750
|
return deleted.length;
|
|
751
751
|
}
|
|
752
|
-
async
|
|
752
|
+
async agentsOfManifest(name) {
|
|
753
753
|
this.check();
|
|
754
754
|
const rows = await this.db
|
|
755
|
-
.select({ version:
|
|
756
|
-
.from(
|
|
757
|
-
.where(eq(
|
|
758
|
-
.groupBy(
|
|
759
|
-
return new Map(rows.
|
|
755
|
+
.select({ version: agents.manifestVersion, n: count() })
|
|
756
|
+
.from(agents)
|
|
757
|
+
.where(eq(agents.manifestName, name))
|
|
758
|
+
.groupBy(agents.manifestVersion);
|
|
759
|
+
return new Map(rows.map((row) => [row.version, row.n]));
|
|
760
760
|
}
|
|
761
761
|
async sessionsOnEnvironment(environmentId) {
|
|
762
762
|
this.check();
|
|
@@ -765,11 +765,11 @@ class PostgresTx {
|
|
|
765
765
|
.orderBy(sessions.id);
|
|
766
766
|
return rows.map((row) => storedSession(row));
|
|
767
767
|
}
|
|
768
|
-
async
|
|
768
|
+
async agentEvent(agentId, type, payload) {
|
|
769
769
|
this.check();
|
|
770
|
-
return
|
|
770
|
+
return appendAgentEvent(postgresAgentRecordWriter(this.db), {
|
|
771
771
|
tenantId: this.tenantId,
|
|
772
|
-
|
|
772
|
+
agentId,
|
|
773
773
|
time: this.now(),
|
|
774
774
|
type,
|
|
775
775
|
payload,
|
|
@@ -780,13 +780,13 @@ class PostgresTx {
|
|
|
780
780
|
async maxEntryBytes() {
|
|
781
781
|
return (await readRuntimeLimits(this)).recordEntryBytes;
|
|
782
782
|
}
|
|
783
|
-
async
|
|
783
|
+
async agentEvents(agentId, options = {}) {
|
|
784
784
|
this.check();
|
|
785
785
|
const query = this.db
|
|
786
|
-
.select({ body:
|
|
787
|
-
.from(
|
|
788
|
-
.where(and(eq(
|
|
789
|
-
.orderBy(
|
|
786
|
+
.select({ body: agentEvents.body })
|
|
787
|
+
.from(agentEvents)
|
|
788
|
+
.where(and(eq(agentEvents.agentId, agentId), options.fromSeq === undefined ? undefined : gte(agentEvents.seq, options.fromSeq), options.types === undefined ? undefined : inArray(agentEvents.type, [...options.types]), options.since === undefined ? undefined : gte(agentEvents.committedAt, options.since), options.until === undefined ? undefined : lt(agentEvents.committedAt, options.until)))
|
|
789
|
+
.orderBy(agentEvents.seq)
|
|
790
790
|
.$dynamic();
|
|
791
791
|
const rows = await (options.limit === undefined ? query : query.limit(options.limit));
|
|
792
792
|
return rows.map((row) => row.body);
|
|
@@ -836,13 +836,13 @@ class PostgresTx {
|
|
|
836
836
|
WHERE ${sessions.status} IN ('running', 'runnable'))::int AS running,
|
|
837
837
|
(SELECT count(*) FROM ${effects} WHERE ${effects.status} = 'uncertain')::int AS uncertain,
|
|
838
838
|
(SELECT count(*) FROM ${sandboxes})::int AS sandboxes,
|
|
839
|
-
(SELECT count(*) FROM ${
|
|
839
|
+
(SELECT count(*) FROM ${agentManifests})::int AS manifests`);
|
|
840
840
|
return {
|
|
841
841
|
sessions: row.sessions,
|
|
842
842
|
runningSessions: row.running,
|
|
843
843
|
uncertainEffects: row.uncertain,
|
|
844
844
|
sandboxes: row.sandboxes,
|
|
845
|
-
|
|
845
|
+
agentManifests: row.manifests,
|
|
846
846
|
};
|
|
847
847
|
}
|
|
848
848
|
// --- control bus (D21) ---------------------------------------------------
|
|
@@ -1089,7 +1089,7 @@ class PostgresTx {
|
|
|
1089
1089
|
costUsd: sql `coalesce(sum(${modelUsage.costUsd}), 0)::double precision`.mapWith(Number),
|
|
1090
1090
|
})
|
|
1091
1091
|
.from(modelUsage)
|
|
1092
|
-
.where(and(query.scope === "
|
|
1092
|
+
.where(and(query.scope === "manifest" ? eq(modelUsage.manifestName, query.id ?? "") : undefined, query.scope === "turn" ? eq(modelUsage.turnId, query.id ?? "") : undefined, query.since !== undefined ? gte(modelUsage.createdAt, query.since) : undefined));
|
|
1093
1093
|
return row;
|
|
1094
1094
|
}
|
|
1095
1095
|
async listModelBudgets() {
|
|
@@ -1105,6 +1105,249 @@ class PostgresTx {
|
|
|
1105
1105
|
if (rows.length > 0)
|
|
1106
1106
|
await this.db.insert(modelBudgets).values([...rows]);
|
|
1107
1107
|
}
|
|
1108
|
+
// --- session queues (protocol 17) -------------------------------------------
|
|
1109
|
+
async enqueueMessage(row) {
|
|
1110
|
+
this.check();
|
|
1111
|
+
const [last] = await this.db
|
|
1112
|
+
.select({ seq: sql `max(${sessionQueue.seq})` })
|
|
1113
|
+
.from(sessionQueue)
|
|
1114
|
+
.where(eq(sessionQueue.sessionId, row.sessionId));
|
|
1115
|
+
const seq = Number(last?.seq ?? 0) + 1;
|
|
1116
|
+
await this.db.insert(sessionQueue).values({ ...row, seq });
|
|
1117
|
+
return seq;
|
|
1118
|
+
}
|
|
1119
|
+
async queuedMessages(sessionId) {
|
|
1120
|
+
this.check();
|
|
1121
|
+
return this.db
|
|
1122
|
+
.select()
|
|
1123
|
+
.from(sessionQueue)
|
|
1124
|
+
.where(eq(sessionQueue.sessionId, sessionId))
|
|
1125
|
+
.orderBy(sessionQueue.seq);
|
|
1126
|
+
}
|
|
1127
|
+
async countQueuedMessages(sessionId) {
|
|
1128
|
+
this.check();
|
|
1129
|
+
const [row] = await this.db
|
|
1130
|
+
.select({ n: count() })
|
|
1131
|
+
.from(sessionQueue)
|
|
1132
|
+
.where(eq(sessionQueue.sessionId, sessionId));
|
|
1133
|
+
return Number(row?.n ?? 0);
|
|
1134
|
+
}
|
|
1135
|
+
async deleteQueuedMessages(ids) {
|
|
1136
|
+
this.check();
|
|
1137
|
+
if (ids.length > 0)
|
|
1138
|
+
await this.db.delete(sessionQueue).where(inArray(sessionQueue.id, [...ids]));
|
|
1139
|
+
}
|
|
1140
|
+
// --- triggers (track T4) ----------------------------------------------------
|
|
1141
|
+
async agentTriggers(agentId) {
|
|
1142
|
+
this.check();
|
|
1143
|
+
return this.db
|
|
1144
|
+
.select()
|
|
1145
|
+
.from(agentTriggers)
|
|
1146
|
+
.where(eq(agentTriggers.agentId, agentId))
|
|
1147
|
+
.orderBy(agentTriggers.triggerId);
|
|
1148
|
+
}
|
|
1149
|
+
async agentTrigger(agentId, triggerId, options = {}) {
|
|
1150
|
+
this.check();
|
|
1151
|
+
const query = this.db
|
|
1152
|
+
.select()
|
|
1153
|
+
.from(agentTriggers)
|
|
1154
|
+
.where(and(eq(agentTriggers.agentId, agentId), eq(agentTriggers.triggerId, triggerId)));
|
|
1155
|
+
const [row] = options.lock ? await query.for("update") : await query;
|
|
1156
|
+
return row;
|
|
1157
|
+
}
|
|
1158
|
+
async putAgentTrigger(row) {
|
|
1159
|
+
this.check();
|
|
1160
|
+
const { agentId: _a, triggerId: _t, createdAt: _c, ...set } = row;
|
|
1161
|
+
await this.db
|
|
1162
|
+
.insert(agentTriggers)
|
|
1163
|
+
.values(row)
|
|
1164
|
+
.onConflictDoUpdate({ target: [agentTriggers.agentId, agentTriggers.triggerId], set });
|
|
1165
|
+
}
|
|
1166
|
+
async deleteAgentTrigger(agentId, triggerId) {
|
|
1167
|
+
this.check();
|
|
1168
|
+
await this.db
|
|
1169
|
+
.delete(agentTriggers)
|
|
1170
|
+
.where(and(eq(agentTriggers.agentId, agentId), eq(agentTriggers.triggerId, triggerId)));
|
|
1171
|
+
}
|
|
1172
|
+
async deleteAgentTriggers(agentId) {
|
|
1173
|
+
this.check();
|
|
1174
|
+
await this.db.delete(agentTriggers).where(eq(agentTriggers.agentId, agentId));
|
|
1175
|
+
await this.db.delete(triggerRuns).where(eq(triggerRuns.agentId, agentId));
|
|
1176
|
+
}
|
|
1177
|
+
async activeTriggers(options) {
|
|
1178
|
+
this.check();
|
|
1179
|
+
const conditions = [eq(agentTriggers.status, "active"), isNotNull(agentTriggers.nextFireAt)];
|
|
1180
|
+
if (options.dueBefore !== undefined)
|
|
1181
|
+
conditions.push(lte(agentTriggers.nextFireAt, options.dueBefore));
|
|
1182
|
+
if (options.changedSince !== undefined)
|
|
1183
|
+
conditions.push(gte(agentTriggers.updatedAt, options.changedSince));
|
|
1184
|
+
if (options.after)
|
|
1185
|
+
conditions.push(or(gt(agentTriggers.agentId, options.after.agentId), and(eq(agentTriggers.agentId, options.after.agentId), gt(agentTriggers.triggerId, options.after.triggerId))));
|
|
1186
|
+
return this.db
|
|
1187
|
+
.select()
|
|
1188
|
+
.from(agentTriggers)
|
|
1189
|
+
.where(and(...conditions))
|
|
1190
|
+
.orderBy(agentTriggers.agentId, agentTriggers.triggerId)
|
|
1191
|
+
.limit(options.limit);
|
|
1192
|
+
}
|
|
1193
|
+
async agentTriggerByEndpoint(endpointId) {
|
|
1194
|
+
this.check();
|
|
1195
|
+
const [row] = await this.db.select().from(agentTriggers).where(eq(agentTriggers.endpointId, endpointId));
|
|
1196
|
+
return row;
|
|
1197
|
+
}
|
|
1198
|
+
async nextWakeAt(agentId) {
|
|
1199
|
+
this.check();
|
|
1200
|
+
const [row] = await this.db
|
|
1201
|
+
.select({ at: sql `min(${agentTriggers.nextFireAt})` })
|
|
1202
|
+
.from(agentTriggers)
|
|
1203
|
+
.where(and(eq(agentTriggers.agentId, agentId), eq(agentTriggers.status, "active")));
|
|
1204
|
+
return row?.at ?? null;
|
|
1205
|
+
}
|
|
1206
|
+
async insertTriggerRun(row) {
|
|
1207
|
+
this.check();
|
|
1208
|
+
const inserted = await this.db
|
|
1209
|
+
.insert(triggerRuns)
|
|
1210
|
+
.values(row)
|
|
1211
|
+
.onConflictDoNothing()
|
|
1212
|
+
.returning({ id: triggerRuns.id });
|
|
1213
|
+
return inserted.length === 1;
|
|
1214
|
+
}
|
|
1215
|
+
async triggerRunByKey(agentId, runKey) {
|
|
1216
|
+
this.check();
|
|
1217
|
+
const [row] = await this.db
|
|
1218
|
+
.select()
|
|
1219
|
+
.from(triggerRuns)
|
|
1220
|
+
.where(and(eq(triggerRuns.agentId, agentId), eq(triggerRuns.runKey, runKey)));
|
|
1221
|
+
return row;
|
|
1222
|
+
}
|
|
1223
|
+
async updateTriggerRun(id, patch) {
|
|
1224
|
+
this.check();
|
|
1225
|
+
await this.db.update(triggerRuns).set(patch).where(eq(triggerRuns.id, id));
|
|
1226
|
+
}
|
|
1227
|
+
async countTriggerRuns(agentId, since, statuses) {
|
|
1228
|
+
this.check();
|
|
1229
|
+
const [row] = await this.db
|
|
1230
|
+
.select({ n: count() })
|
|
1231
|
+
.from(triggerRuns)
|
|
1232
|
+
.where(and(eq(triggerRuns.agentId, agentId), gte(triggerRuns.firedAt, since), inArray(triggerRuns.status, [...statuses])));
|
|
1233
|
+
return Number(row?.n ?? 0);
|
|
1234
|
+
}
|
|
1235
|
+
async listTriggerRuns(agentId, options) {
|
|
1236
|
+
this.check();
|
|
1237
|
+
const conditions = [eq(triggerRuns.agentId, agentId)];
|
|
1238
|
+
if (options.triggerId !== undefined)
|
|
1239
|
+
conditions.push(eq(triggerRuns.triggerId, options.triggerId));
|
|
1240
|
+
if (options.before !== undefined)
|
|
1241
|
+
conditions.push(lt(triggerRuns.id, options.before));
|
|
1242
|
+
return this.db
|
|
1243
|
+
.select()
|
|
1244
|
+
.from(triggerRuns)
|
|
1245
|
+
.where(and(...conditions))
|
|
1246
|
+
.orderBy(desc(triggerRuns.id))
|
|
1247
|
+
.limit(options.limit);
|
|
1248
|
+
}
|
|
1249
|
+
async pruneTriggerRuns(before) {
|
|
1250
|
+
this.check();
|
|
1251
|
+
const deleted = await this.db
|
|
1252
|
+
.delete(triggerRuns)
|
|
1253
|
+
.where(lt(triggerRuns.firedAt, before))
|
|
1254
|
+
.returning({ id: triggerRuns.id });
|
|
1255
|
+
return deleted.length;
|
|
1256
|
+
}
|
|
1257
|
+
// --- memory (track T5) ------------------------------------------------------
|
|
1258
|
+
async memory(agentId, path, options = {}) {
|
|
1259
|
+
this.check();
|
|
1260
|
+
// The path's own lock, held whether or not a row has it, so two writes creating it queue.
|
|
1261
|
+
if (options.lock)
|
|
1262
|
+
await this.db.execute(sql `SELECT pg_advisory_xact_lock(hashtext(${`nylorun.memories:${agentId}:${path}`}))`);
|
|
1263
|
+
const query = this.db
|
|
1264
|
+
.select()
|
|
1265
|
+
.from(memories)
|
|
1266
|
+
.where(and(eq(memories.agentId, agentId), eq(memories.path, path)));
|
|
1267
|
+
const [row] = options.lock ? await query.for("update") : await query;
|
|
1268
|
+
return row;
|
|
1269
|
+
}
|
|
1270
|
+
async memories(agentId, options) {
|
|
1271
|
+
this.check();
|
|
1272
|
+
const conditions = [eq(memories.agentId, agentId)];
|
|
1273
|
+
// `starts_with`, not LIKE: a path may hold `%` or `_`.
|
|
1274
|
+
if (options.folder !== undefined)
|
|
1275
|
+
conditions.push(sql `starts_with(${memories.path}, ${`${options.folder}/`})`);
|
|
1276
|
+
if (options.after !== undefined)
|
|
1277
|
+
conditions.push(gt(memories.path, options.after));
|
|
1278
|
+
const ordered = this.db
|
|
1279
|
+
.select()
|
|
1280
|
+
.from(memories)
|
|
1281
|
+
.where(and(...conditions))
|
|
1282
|
+
.orderBy(memories.path);
|
|
1283
|
+
const limited = options.limit === undefined ? ordered : ordered.limit(options.limit);
|
|
1284
|
+
return options.lock ? limited.for("update") : limited;
|
|
1285
|
+
}
|
|
1286
|
+
async countMemories(agentId) {
|
|
1287
|
+
this.check();
|
|
1288
|
+
const [row] = await this.db.select({ n: count() }).from(memories).where(eq(memories.agentId, agentId));
|
|
1289
|
+
return Number(row?.n ?? 0);
|
|
1290
|
+
}
|
|
1291
|
+
async recentMemories(agentId, limit) {
|
|
1292
|
+
this.check();
|
|
1293
|
+
return this.db
|
|
1294
|
+
.select({ path: memories.path, description: memories.description, bytes: memories.bytes })
|
|
1295
|
+
.from(memories)
|
|
1296
|
+
.where(eq(memories.agentId, agentId))
|
|
1297
|
+
.orderBy(desc(memories.updatedAt), memories.path)
|
|
1298
|
+
.limit(limit);
|
|
1299
|
+
}
|
|
1300
|
+
async putMemory(row) {
|
|
1301
|
+
this.check();
|
|
1302
|
+
const { agentId: _a, path: _p, ...set } = row;
|
|
1303
|
+
await this.db
|
|
1304
|
+
.insert(memories)
|
|
1305
|
+
.values(row)
|
|
1306
|
+
.onConflictDoUpdate({ target: [memories.agentId, memories.path], set });
|
|
1307
|
+
}
|
|
1308
|
+
async deleteMemory(agentId, path) {
|
|
1309
|
+
this.check();
|
|
1310
|
+
await this.db.delete(memories).where(and(eq(memories.agentId, agentId), eq(memories.path, path)));
|
|
1311
|
+
}
|
|
1312
|
+
async insertMemoryVersion(row) {
|
|
1313
|
+
this.check();
|
|
1314
|
+
await this.db.insert(memoryVersions).values(row);
|
|
1315
|
+
}
|
|
1316
|
+
async memoryVersions(agentId, options) {
|
|
1317
|
+
this.check();
|
|
1318
|
+
const conditions = [eq(memoryVersions.agentId, agentId)];
|
|
1319
|
+
if (options.path !== undefined)
|
|
1320
|
+
conditions.push(eq(memoryVersions.path, options.path));
|
|
1321
|
+
if (options.before !== undefined)
|
|
1322
|
+
conditions.push(lt(memoryVersions.id, options.before));
|
|
1323
|
+
return this.db
|
|
1324
|
+
.select()
|
|
1325
|
+
.from(memoryVersions)
|
|
1326
|
+
.where(and(...conditions))
|
|
1327
|
+
.orderBy(desc(memoryVersions.id))
|
|
1328
|
+
.limit(options.limit);
|
|
1329
|
+
}
|
|
1330
|
+
async memoryVersion(agentId, id) {
|
|
1331
|
+
this.check();
|
|
1332
|
+
const [row] = await this.db
|
|
1333
|
+
.select()
|
|
1334
|
+
.from(memoryVersions)
|
|
1335
|
+
.where(and(eq(memoryVersions.agentId, agentId), eq(memoryVersions.id, id)));
|
|
1336
|
+
return row;
|
|
1337
|
+
}
|
|
1338
|
+
async copyMemories(from, to, now) {
|
|
1339
|
+
this.check();
|
|
1340
|
+
const copied = await this.db.execute(sql `
|
|
1341
|
+
INSERT INTO ${memories} (agent_id, path, content, content_sha256, bytes, description, version, updated_at)
|
|
1342
|
+
SELECT ${to}, path, content, content_sha256, bytes, description, 1, ${now}
|
|
1343
|
+
FROM ${memories} WHERE agent_id = ${from}`);
|
|
1344
|
+
return Number(copied.count ?? 0);
|
|
1345
|
+
}
|
|
1346
|
+
async deleteAgentMemories(agentId) {
|
|
1347
|
+
this.check();
|
|
1348
|
+
await this.db.delete(memories).where(eq(memories.agentId, agentId));
|
|
1349
|
+
await this.db.delete(memoryVersions).where(eq(memoryVersions.agentId, agentId));
|
|
1350
|
+
}
|
|
1108
1351
|
// --- tool crossings --------------------------------------------------------
|
|
1109
1352
|
async toolCrossing(key) {
|
|
1110
1353
|
this.check();
|
|
@@ -1373,6 +1616,8 @@ class PostgresTx {
|
|
|
1373
1616
|
if (scope === "sessions" || scope === "all") {
|
|
1374
1617
|
for (const table of SESSION_TABLES)
|
|
1375
1618
|
await db.delete(table);
|
|
1619
|
+
// A session's booked wake (T4) goes with it.
|
|
1620
|
+
await db.delete(agentTriggers).where(eq(agentTriggers.kind, "wake"));
|
|
1376
1621
|
await db.delete(toolCrossings);
|
|
1377
1622
|
// A session's artifacts go with it (their versions by cascade).
|
|
1378
1623
|
await db.delete(artifacts).where(isNotNull(artifacts.sessionId));
|
|
@@ -1385,15 +1630,20 @@ class PostgresTx {
|
|
|
1385
1630
|
basinGeneration: sql `${tenant.basinGeneration} + 1`,
|
|
1386
1631
|
});
|
|
1387
1632
|
}
|
|
1388
|
-
if (scope === "
|
|
1633
|
+
if (scope === "agents" || scope === "all") {
|
|
1389
1634
|
await db.delete(sandboxes);
|
|
1390
1635
|
// Their vaults and credentials go with them (ON DELETE CASCADE).
|
|
1391
|
-
await db.delete(
|
|
1392
|
-
await db.delete(
|
|
1636
|
+
await db.delete(agents);
|
|
1637
|
+
await db.delete(agentEvents);
|
|
1638
|
+
await db.delete(agentTriggers);
|
|
1639
|
+
await db.delete(triggerRuns);
|
|
1640
|
+
// Memories belong to the Agents (D72).
|
|
1641
|
+
await db.delete(memories);
|
|
1642
|
+
await db.delete(memoryVersions);
|
|
1393
1643
|
await db.delete(sandboxSignals);
|
|
1394
1644
|
}
|
|
1395
1645
|
if (scope === "all") {
|
|
1396
|
-
await db.delete(
|
|
1646
|
+
await db.delete(agentManifests);
|
|
1397
1647
|
// The files stay, as their bytes do; only what used them goes.
|
|
1398
1648
|
await db.delete(definitionFileUses);
|
|
1399
1649
|
await db.delete(modelUsage);
|
|
@@ -4,7 +4,7 @@ import { TenantEnvelopeSchema, } from "@nylorun/core/contracts";
|
|
|
4
4
|
import { openError, TenantOpenError } from "../../tenant/cause.js";
|
|
5
5
|
import { database, driverError } from "./db.js";
|
|
6
6
|
import { applyMigrations, assertCurrentLayout, isStatementError, lockMigrations, shippedMigrations, } from "./migrate.js";
|
|
7
|
-
import {
|
|
7
|
+
import { principals, sessions, signingKeys, TENANT_SCHEMA, tenant, vaultAudit, vaults, } from "./schema.js";
|
|
8
8
|
import { createPostgresSessionStore } from "./store.js";
|
|
9
9
|
/** The migration that deletes the vault's `oauth` credentials, each with an audit row. */
|
|
10
10
|
const OAUTH_REMOVED_MIGRATION = "0016_mcp_oauth_removed";
|
|
@@ -114,7 +114,6 @@ async function ensureTenant(tx, create, migrated, now) {
|
|
|
114
114
|
const [data] = await tx.execute(sql `
|
|
115
115
|
SELECT EXISTS (SELECT 1 FROM ${principals})
|
|
116
116
|
OR EXISTS (SELECT 1 FROM ${sessions})
|
|
117
|
-
OR EXISTS (SELECT 1 FROM ${definitions})
|
|
118
117
|
OR EXISTS (SELECT 1 FROM ${vaults})
|
|
119
118
|
OR EXISTS (SELECT 1 FROM ${signingKeys}) AS held`);
|
|
120
119
|
if (data?.held)
|