@nylorun/runtime 0.40.0-beta → 0.41.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 +15 -0
- package/dist/adapters/streams/s2.js +9 -2
- package/dist/api/components.d.ts +131 -0
- package/dist/api/components.js +3 -1
- package/dist/api/http/app.js +2 -0
- package/dist/api/http/routes/runs.d.ts +8 -0
- package/dist/api/http/routes/runs.js +30 -0
- package/dist/api/http/routes/triggers.d.ts +2 -1
- package/dist/api/http/routes/triggers.js +32 -4
- package/dist/api/openapi.js +3 -1
- package/dist/gates/policy.d.ts +6 -4
- package/dist/gates/policy.js +10 -2
- package/dist/harness-api/record.js +51 -6
- package/dist/harness-api/start.js +15 -9
- package/dist/harness-api/tool-journal.js +3 -0
- package/dist/management-openapi.json +11 -6
- package/dist/openapi.json +506 -12
- package/dist/store/postgres/drizzle/0043_agent_triggers_origin.sql +11 -0
- package/dist/store/postgres/drizzle/0044_agent_triggers_origin_backfill.sql +8 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +14 -0
- package/dist/store/postgres/schema.d.ts +151 -4
- package/dist/store/postgres/schema.js +25 -4
- package/dist/store/postgres/store.js +20 -2
- package/dist/store/types.d.ts +10 -0
- package/dist/streams/types.d.ts +16 -1
- package/dist/streams/types.js +8 -1
- package/dist/tenant/advance.js +7 -0
- package/dist/tenant/agent-triggers.d.ts +60 -0
- package/dist/tenant/agent-triggers.js +179 -0
- package/dist/tenant/agents.d.ts +1 -1
- package/dist/tenant/agents.js +5 -1
- package/dist/tenant/limits.d.ts +26 -7
- package/dist/tenant/limits.js +39 -6
- package/dist/tenant/runs-stream.d.ts +51 -0
- package/dist/tenant/runs-stream.js +164 -0
- package/dist/tenant/sweep.js +3 -0
- package/dist/tenant/triggers.d.ts +31 -5
- package/dist/tenant/triggers.js +168 -48
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
ALTER TABLE "nylorun"."agent_triggers" ADD COLUMN "origin" text DEFAULT 'manifest' NOT NULL;--> statement-breakpoint
|
|
2
|
+
ALTER TABLE "nylorun"."agent_triggers" ADD COLUMN "title" text;--> statement-breakpoint
|
|
3
|
+
ALTER TABLE "nylorun"."agent_triggers" ADD COLUMN "definition" json;--> statement-breakpoint
|
|
4
|
+
ALTER TABLE "nylorun"."agent_triggers" ADD COLUMN "created_session_id" text COLLATE "C";--> statement-breakpoint
|
|
5
|
+
ALTER TABLE "nylorun"."agent_triggers" ADD COLUMN "created_turn_id" text COLLATE "C";--> statement-breakpoint
|
|
6
|
+
ALTER TABLE "nylorun"."trigger_runs" ADD COLUMN "origin" text DEFAULT 'manifest' NOT NULL;--> statement-breakpoint
|
|
7
|
+
ALTER TABLE "nylorun"."trigger_runs" ADD COLUMN "title" text;--> statement-breakpoint
|
|
8
|
+
ALTER TABLE "nylorun"."trigger_runs" ADD COLUMN "streamed_at" text COLLATE "C";--> statement-breakpoint
|
|
9
|
+
CREATE INDEX "agent_triggers_created_session" ON "nylorun"."agent_triggers" USING btree ("agent_id","created_session_id") WHERE created_session_id IS NOT NULL;--> statement-breakpoint
|
|
10
|
+
CREATE INDEX "trigger_runs_unstreamed" ON "nylorun"."trigger_runs" USING btree ("fired_at") WHERE streamed_at IS NULL;--> statement-breakpoint
|
|
11
|
+
ALTER TABLE "nylorun"."agent_triggers" ADD CONSTRAINT "agent_triggers_origin_check" CHECK ("nylorun"."agent_triggers"."origin" IN ('manifest', 'agent', 'wake'));
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- Custom SQL (a data change): the origin of the trigger rows and runs written before 0043 (D83). A
|
|
2
|
+
-- booked wake's is `wake`; every other came from its manifest (`manifest`, the columns' default).
|
|
3
|
+
-- Runs recorded before the Tenant runs stream are marked streamed, so the sweep does not append
|
|
4
|
+
-- the history of the last 30 days to the new `runs` stream: it starts with the runs made from here
|
|
5
|
+
-- on.
|
|
6
|
+
UPDATE "nylorun"."agent_triggers" SET "origin" = 'wake' WHERE "kind" = 'wake';--> statement-breakpoint
|
|
7
|
+
UPDATE "nylorun"."trigger_runs" SET "origin" = 'wake' WHERE "kind" = 'wake';--> statement-breakpoint
|
|
8
|
+
UPDATE "nylorun"."trigger_runs" SET "streamed_at" = "fired_at" WHERE "streamed_at" IS NULL;
|
|
@@ -302,6 +302,20 @@
|
|
|
302
302
|
"when": 1793509192153,
|
|
303
303
|
"tag": "0042_worker_engine_image",
|
|
304
304
|
"breakpoints": true
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
"idx": 43,
|
|
308
|
+
"version": "7",
|
|
309
|
+
"when": 1793548856412,
|
|
310
|
+
"tag": "0043_agent_triggers_origin",
|
|
311
|
+
"breakpoints": true
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
"idx": 44,
|
|
315
|
+
"version": "7",
|
|
316
|
+
"when": 1793548858191,
|
|
317
|
+
"tag": "0044_agent_triggers_origin_backfill",
|
|
318
|
+
"breakpoints": true
|
|
305
319
|
}
|
|
306
320
|
]
|
|
307
321
|
}
|
|
@@ -1526,9 +1526,11 @@ export declare const agents: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1526
1526
|
/**
|
|
1527
1527
|
* An Agent's triggers (track T4, D74): what its manifest's `runtime.triggers` declares is the
|
|
1528
1528
|
* manifest's; whether each one is on and when it fires next is the Agent's, here. A booked wake
|
|
1529
|
-
* (D77) is a row of kind `wake` with id `wake:<sessionId>`.
|
|
1530
|
-
*
|
|
1531
|
-
*
|
|
1529
|
+
* (D77) is a row of kind `wake` with id `wake:<sessionId>`. A trigger an agent made with the
|
|
1530
|
+
* `triggers` tool (D83) is a row of origin `agent`, its definition (a `Trigger`, as a manifest
|
|
1531
|
+
* entry holds it) and title on the row, with the session and turn that made it. Postgres holds
|
|
1532
|
+
* the truth: one durable timer per active trigger is armed from `nextFireAt`, and the sweep
|
|
1533
|
+
* re-arms an overdue one, so the execution's timers can be rebuilt from these rows.
|
|
1532
1534
|
*/
|
|
1533
1535
|
export declare const agentTriggers: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1534
1536
|
name: "agent_triggers";
|
|
@@ -1769,6 +1771,97 @@ export declare const agentTriggers: import("drizzle-orm/pg-core").PgTableWithCol
|
|
|
1769
1771
|
identity: undefined;
|
|
1770
1772
|
generated: undefined;
|
|
1771
1773
|
}, {}, {}>;
|
|
1774
|
+
origin: import("drizzle-orm/pg-core").PgColumn<{
|
|
1775
|
+
name: "origin";
|
|
1776
|
+
tableName: "agent_triggers";
|
|
1777
|
+
dataType: "string";
|
|
1778
|
+
columnType: "PgText";
|
|
1779
|
+
data: "agent" | "manifest" | "wake";
|
|
1780
|
+
driverParam: string;
|
|
1781
|
+
notNull: true;
|
|
1782
|
+
hasDefault: true;
|
|
1783
|
+
isPrimaryKey: false;
|
|
1784
|
+
isAutoincrement: false;
|
|
1785
|
+
hasRuntimeDefault: false;
|
|
1786
|
+
enumValues: ["manifest", "agent", "wake"];
|
|
1787
|
+
baseColumn: never;
|
|
1788
|
+
identity: undefined;
|
|
1789
|
+
generated: undefined;
|
|
1790
|
+
}, {}, {}>;
|
|
1791
|
+
title: import("drizzle-orm/pg-core").PgColumn<{
|
|
1792
|
+
name: "title";
|
|
1793
|
+
tableName: "agent_triggers";
|
|
1794
|
+
dataType: "string";
|
|
1795
|
+
columnType: "PgText";
|
|
1796
|
+
data: string;
|
|
1797
|
+
driverParam: string;
|
|
1798
|
+
notNull: false;
|
|
1799
|
+
hasDefault: false;
|
|
1800
|
+
isPrimaryKey: false;
|
|
1801
|
+
isAutoincrement: false;
|
|
1802
|
+
hasRuntimeDefault: false;
|
|
1803
|
+
enumValues: [string, ...string[]];
|
|
1804
|
+
baseColumn: never;
|
|
1805
|
+
identity: undefined;
|
|
1806
|
+
generated: undefined;
|
|
1807
|
+
}, {}, {}>;
|
|
1808
|
+
definition: import("drizzle-orm/pg-core").PgColumn<{
|
|
1809
|
+
name: "definition";
|
|
1810
|
+
tableName: "agent_triggers";
|
|
1811
|
+
dataType: "custom";
|
|
1812
|
+
columnType: "PgCustomColumn";
|
|
1813
|
+
data: unknown;
|
|
1814
|
+
driverParam: unknown;
|
|
1815
|
+
notNull: false;
|
|
1816
|
+
hasDefault: false;
|
|
1817
|
+
isPrimaryKey: false;
|
|
1818
|
+
isAutoincrement: false;
|
|
1819
|
+
hasRuntimeDefault: false;
|
|
1820
|
+
enumValues: undefined;
|
|
1821
|
+
baseColumn: never;
|
|
1822
|
+
identity: undefined;
|
|
1823
|
+
generated: undefined;
|
|
1824
|
+
}, {}, {
|
|
1825
|
+
pgColumnBuilderBrand: 'PgCustomColumnBuilderBrand';
|
|
1826
|
+
}>;
|
|
1827
|
+
createdSessionId: import("drizzle-orm/pg-core").PgColumn<{
|
|
1828
|
+
name: "createdSessionId";
|
|
1829
|
+
tableName: "agent_triggers";
|
|
1830
|
+
dataType: "custom";
|
|
1831
|
+
columnType: "PgCustomColumn";
|
|
1832
|
+
data: string;
|
|
1833
|
+
driverParam: string;
|
|
1834
|
+
notNull: false;
|
|
1835
|
+
hasDefault: false;
|
|
1836
|
+
isPrimaryKey: false;
|
|
1837
|
+
isAutoincrement: false;
|
|
1838
|
+
hasRuntimeDefault: false;
|
|
1839
|
+
enumValues: undefined;
|
|
1840
|
+
baseColumn: never;
|
|
1841
|
+
identity: undefined;
|
|
1842
|
+
generated: undefined;
|
|
1843
|
+
}, {}, {
|
|
1844
|
+
pgColumnBuilderBrand: 'PgCustomColumnBuilderBrand';
|
|
1845
|
+
}>;
|
|
1846
|
+
createdTurnId: import("drizzle-orm/pg-core").PgColumn<{
|
|
1847
|
+
name: "createdTurnId";
|
|
1848
|
+
tableName: "agent_triggers";
|
|
1849
|
+
dataType: "custom";
|
|
1850
|
+
columnType: "PgCustomColumn";
|
|
1851
|
+
data: string;
|
|
1852
|
+
driverParam: string;
|
|
1853
|
+
notNull: false;
|
|
1854
|
+
hasDefault: false;
|
|
1855
|
+
isPrimaryKey: false;
|
|
1856
|
+
isAutoincrement: false;
|
|
1857
|
+
hasRuntimeDefault: false;
|
|
1858
|
+
enumValues: undefined;
|
|
1859
|
+
baseColumn: never;
|
|
1860
|
+
identity: undefined;
|
|
1861
|
+
generated: undefined;
|
|
1862
|
+
}, {}, {
|
|
1863
|
+
pgColumnBuilderBrand: 'PgCustomColumnBuilderBrand';
|
|
1864
|
+
}>;
|
|
1772
1865
|
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
1773
1866
|
name: "createdAt";
|
|
1774
1867
|
tableName: "agent_triggers";
|
|
@@ -2219,7 +2312,8 @@ export declare const memoryVersions: import("drizzle-orm/pg-core").PgTableWithCo
|
|
|
2219
2312
|
* Each run of an Agent's triggers (D74): one per fire, delivery or run by hand, keyed by what
|
|
2220
2313
|
* caused it (`runKey`: `fire:<trigger>:<due ms>`, `webhook:<trigger>:<delivery id>`,
|
|
2221
2314
|
* `manual:<trigger>:<idempotency key>`), so a repeated cause makes no second run. Kept for the
|
|
2222
|
-
* Runtime limit `triggerRunRetentionMs`.
|
|
2315
|
+
* Runtime limit `triggerRunRetentionMs`. `streamedAt` is the Tenant runs stream's outbox (D83):
|
|
2316
|
+
* set once the run is in the `runs` stream; the sweep appends a run still unset.
|
|
2223
2317
|
*/
|
|
2224
2318
|
export declare const triggerRuns: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
2225
2319
|
name: "trigger_runs";
|
|
@@ -2464,6 +2558,59 @@ export declare const triggerRuns: import("drizzle-orm/pg-core").PgTableWithColum
|
|
|
2464
2558
|
}, {}, {
|
|
2465
2559
|
pgColumnBuilderBrand: 'PgCustomColumnBuilderBrand';
|
|
2466
2560
|
}>;
|
|
2561
|
+
origin: import("drizzle-orm/pg-core").PgColumn<{
|
|
2562
|
+
name: "origin";
|
|
2563
|
+
tableName: "trigger_runs";
|
|
2564
|
+
dataType: "string";
|
|
2565
|
+
columnType: "PgText";
|
|
2566
|
+
data: "agent" | "manifest" | "wake";
|
|
2567
|
+
driverParam: string;
|
|
2568
|
+
notNull: true;
|
|
2569
|
+
hasDefault: true;
|
|
2570
|
+
isPrimaryKey: false;
|
|
2571
|
+
isAutoincrement: false;
|
|
2572
|
+
hasRuntimeDefault: false;
|
|
2573
|
+
enumValues: ["manifest", "agent", "wake"];
|
|
2574
|
+
baseColumn: never;
|
|
2575
|
+
identity: undefined;
|
|
2576
|
+
generated: undefined;
|
|
2577
|
+
}, {}, {}>;
|
|
2578
|
+
title: import("drizzle-orm/pg-core").PgColumn<{
|
|
2579
|
+
name: "title";
|
|
2580
|
+
tableName: "trigger_runs";
|
|
2581
|
+
dataType: "string";
|
|
2582
|
+
columnType: "PgText";
|
|
2583
|
+
data: string;
|
|
2584
|
+
driverParam: string;
|
|
2585
|
+
notNull: false;
|
|
2586
|
+
hasDefault: false;
|
|
2587
|
+
isPrimaryKey: false;
|
|
2588
|
+
isAutoincrement: false;
|
|
2589
|
+
hasRuntimeDefault: false;
|
|
2590
|
+
enumValues: [string, ...string[]];
|
|
2591
|
+
baseColumn: never;
|
|
2592
|
+
identity: undefined;
|
|
2593
|
+
generated: undefined;
|
|
2594
|
+
}, {}, {}>;
|
|
2595
|
+
streamedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
2596
|
+
name: "streamedAt";
|
|
2597
|
+
tableName: "trigger_runs";
|
|
2598
|
+
dataType: "custom";
|
|
2599
|
+
columnType: "PgCustomColumn";
|
|
2600
|
+
data: string;
|
|
2601
|
+
driverParam: string;
|
|
2602
|
+
notNull: false;
|
|
2603
|
+
hasDefault: false;
|
|
2604
|
+
isPrimaryKey: false;
|
|
2605
|
+
isAutoincrement: false;
|
|
2606
|
+
hasRuntimeDefault: false;
|
|
2607
|
+
enumValues: undefined;
|
|
2608
|
+
baseColumn: never;
|
|
2609
|
+
identity: undefined;
|
|
2610
|
+
generated: undefined;
|
|
2611
|
+
}, {}, {
|
|
2612
|
+
pgColumnBuilderBrand: 'PgCustomColumnBuilderBrand';
|
|
2613
|
+
}>;
|
|
2467
2614
|
};
|
|
2468
2615
|
dialect: 'pg';
|
|
2469
2616
|
}>;
|
|
@@ -217,9 +217,11 @@ export const agents = nylorun.table("agents", {
|
|
|
217
217
|
/**
|
|
218
218
|
* An Agent's triggers (track T4, D74): what its manifest's `runtime.triggers` declares is the
|
|
219
219
|
* manifest's; whether each one is on and when it fires next is the Agent's, here. A booked wake
|
|
220
|
-
* (D77) is a row of kind `wake` with id `wake:<sessionId>`.
|
|
221
|
-
*
|
|
222
|
-
*
|
|
220
|
+
* (D77) is a row of kind `wake` with id `wake:<sessionId>`. A trigger an agent made with the
|
|
221
|
+
* `triggers` tool (D83) is a row of origin `agent`, its definition (a `Trigger`, as a manifest
|
|
222
|
+
* entry holds it) and title on the row, with the session and turn that made it. Postgres holds
|
|
223
|
+
* the truth: one durable timer per active trigger is armed from `nextFireAt`, and the sweep
|
|
224
|
+
* re-arms an overdue one, so the execution's timers can be rebuilt from these rows.
|
|
223
225
|
*/
|
|
224
226
|
export const agentTriggers = nylorun.table("agent_triggers", {
|
|
225
227
|
agentId: textC().notNull(),
|
|
@@ -244,10 +246,22 @@ export const agentTriggers = nylorun.table("agent_triggers", {
|
|
|
244
246
|
sessionId: textC(),
|
|
245
247
|
note: text(),
|
|
246
248
|
effectId: text(),
|
|
249
|
+
/**
|
|
250
|
+
* Where its definition comes from (D83): `manifest` (the version's `runtime.triggers`),
|
|
251
|
+
* `agent` (`definition`, made with the `triggers` tool) or `wake` (a booked wake).
|
|
252
|
+
*/
|
|
253
|
+
origin: text({ enum: ["manifest", "agent", "wake"] }).notNull().default("manifest"),
|
|
254
|
+
/** An agent-made trigger's title, its definition, and the session and turn that made it. */
|
|
255
|
+
title: text(),
|
|
256
|
+
definition: jsonText(),
|
|
257
|
+
createdSessionId: textC(),
|
|
258
|
+
createdTurnId: textC(),
|
|
247
259
|
createdAt: textC().notNull(),
|
|
248
260
|
updatedAt: textC().notNull(),
|
|
249
261
|
}, (t) => [
|
|
262
|
+
check("agent_triggers_origin_check", sql `${t.origin} IN ('manifest', 'agent', 'wake')`),
|
|
250
263
|
primaryKey({ name: "agent_triggers_pkey", columns: [t.agentId, t.triggerId] }),
|
|
264
|
+
index("agent_triggers_created_session").on(t.agentId, t.createdSessionId).where(sql `created_session_id IS NOT NULL`),
|
|
251
265
|
index("agent_triggers_due").on(t.status, t.nextFireAt),
|
|
252
266
|
uniqueIndex("agent_triggers_endpoint").on(t.endpointId).where(sql `endpoint_id IS NOT NULL`),
|
|
253
267
|
]);
|
|
@@ -293,7 +307,8 @@ export const memoryVersions = nylorun.table("memory_versions", {
|
|
|
293
307
|
* Each run of an Agent's triggers (D74): one per fire, delivery or run by hand, keyed by what
|
|
294
308
|
* caused it (`runKey`: `fire:<trigger>:<due ms>`, `webhook:<trigger>:<delivery id>`,
|
|
295
309
|
* `manual:<trigger>:<idempotency key>`), so a repeated cause makes no second run. Kept for the
|
|
296
|
-
* Runtime limit `triggerRunRetentionMs`.
|
|
310
|
+
* Runtime limit `triggerRunRetentionMs`. `streamedAt` is the Tenant runs stream's outbox (D83):
|
|
311
|
+
* set once the run is in the `runs` stream; the sweep appends a run still unset.
|
|
297
312
|
*/
|
|
298
313
|
export const triggerRuns = nylorun.table("trigger_runs", {
|
|
299
314
|
id: textC().primaryKey(),
|
|
@@ -309,10 +324,16 @@ export const triggerRuns = nylorun.table("trigger_runs", {
|
|
|
309
324
|
sessionId: textC(),
|
|
310
325
|
turnId: textC(),
|
|
311
326
|
queuedMessageId: textC(),
|
|
327
|
+
/** Its trigger's origin and title when it ran (D83), for the runs stream after the trigger is gone. */
|
|
328
|
+
origin: text({ enum: ["manifest", "agent", "wake"] }).notNull().default("manifest"),
|
|
329
|
+
title: text(),
|
|
330
|
+
/** When it was appended to the Tenant's `runs` stream (ISO), or null until then. */
|
|
331
|
+
streamedAt: textC(),
|
|
312
332
|
}, (t) => [
|
|
313
333
|
uniqueIndex("trigger_runs_key").on(t.agentId, t.runKey),
|
|
314
334
|
index("trigger_runs_agent").on(t.agentId, t.id),
|
|
315
335
|
index("trigger_runs_fired").on(t.firedAt),
|
|
336
|
+
index("trigger_runs_unstreamed").on(t.firedAt).where(sql `streamed_at IS NULL`),
|
|
316
337
|
]);
|
|
317
338
|
/**
|
|
318
339
|
* An agent manifest's versions (protocol 16): a named, versioned document with a harness and a
|
|
@@ -1345,6 +1345,24 @@ class PostgresTx {
|
|
|
1345
1345
|
.returning({ id: triggerRuns.id });
|
|
1346
1346
|
return inserted.length === 1;
|
|
1347
1347
|
}
|
|
1348
|
+
async unstreamedTriggerRuns(options) {
|
|
1349
|
+
this.check();
|
|
1350
|
+
return this.db
|
|
1351
|
+
.select()
|
|
1352
|
+
.from(triggerRuns)
|
|
1353
|
+
.where(and(isNull(triggerRuns.streamedAt), lt(triggerRuns.firedAt, options.firedBefore)))
|
|
1354
|
+
.orderBy(triggerRuns.firedAt, triggerRuns.id)
|
|
1355
|
+
.limit(options.limit);
|
|
1356
|
+
}
|
|
1357
|
+
async markTriggerRunsStreamed(ids, at) {
|
|
1358
|
+
this.check();
|
|
1359
|
+
if (ids.length === 0)
|
|
1360
|
+
return;
|
|
1361
|
+
await this.db
|
|
1362
|
+
.update(triggerRuns)
|
|
1363
|
+
.set({ streamedAt: at })
|
|
1364
|
+
.where(and(inArray(triggerRuns.id, [...ids]), isNull(triggerRuns.streamedAt)));
|
|
1365
|
+
}
|
|
1348
1366
|
async triggerRunByKey(agentId, runKey) {
|
|
1349
1367
|
this.check();
|
|
1350
1368
|
const [row] = await this.db
|
|
@@ -1763,8 +1781,8 @@ class PostgresTx {
|
|
|
1763
1781
|
if (scope === "sessions" || scope === "all") {
|
|
1764
1782
|
for (const table of SESSION_TABLES)
|
|
1765
1783
|
await db.delete(table);
|
|
1766
|
-
// A session's booked wake (T4) goes with it.
|
|
1767
|
-
await db.delete(agentTriggers).where(eq(agentTriggers.kind, "wake"));
|
|
1784
|
+
// A session's booked wake (T4) goes with it, and the triggers its agents made (D83).
|
|
1785
|
+
await db.delete(agentTriggers).where(or(eq(agentTriggers.kind, "wake"), inArray(agentTriggers.origin, ["wake", "agent"])));
|
|
1768
1786
|
await db.delete(toolCrossings);
|
|
1769
1787
|
// A session's artifacts go with it (their versions by cascade).
|
|
1770
1788
|
await db.delete(artifacts).where(isNotNull(artifacts.sessionId));
|
package/dist/store/types.d.ts
CHANGED
|
@@ -776,6 +776,16 @@ export interface Tx {
|
|
|
776
776
|
}): Promise<TriggerRunRow[]>;
|
|
777
777
|
/** Deletes runs fired before `before`; returns how many. */
|
|
778
778
|
pruneTriggerRuns(before: string): Promise<number>;
|
|
779
|
+
/**
|
|
780
|
+
* Runs of every Agent not yet on the Tenant's runs stream (D83), fired before `firedBefore`,
|
|
781
|
+
* oldest first.
|
|
782
|
+
*/
|
|
783
|
+
unstreamedTriggerRuns(options: {
|
|
784
|
+
firedBefore: string;
|
|
785
|
+
limit: number;
|
|
786
|
+
}): Promise<TriggerRunRow[]>;
|
|
787
|
+
/** Marks runs as on the runs stream, at `at`; one already marked keeps its time. */
|
|
788
|
+
markTriggerRunsStreamed(ids: readonly string[], at: string): Promise<void>;
|
|
779
789
|
/** One memory; with `lock`, its path is locked until the transaction ends, whether or not a row has it. */
|
|
780
790
|
memory(agentId: string, path: string, options?: {
|
|
781
791
|
lock?: boolean;
|
package/dist/streams/types.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* | Stream | Contents | Written by | Read by |
|
|
8
8
|
* | --- | --- | --- | --- |
|
|
9
9
|
* | `sessions/<sessionId>` | every event of the session, in sequence | the stream relay, from the record | history and session SSE |
|
|
10
|
+
| `runs` | every run of the Tenant's triggers (D83), kept 7 days | `tenant/runs-stream.ts`, after the commit that made the run, and the sweep | `GET /v1/runs/stream` |
|
|
10
11
|
*
|
|
11
12
|
* S2 serves API listeners only (history, SSE, AG-UI, A2A): nothing inside the Runtime signals
|
|
12
13
|
* through it or waits on it (D48). Signals between processes go over the control bus on
|
|
@@ -15,7 +16,9 @@
|
|
|
15
16
|
* **Basin generations.** A session id is unique within its Tenant's basin generation. A
|
|
16
17
|
* Tenant reset, the only path that frees ids, moves the Tenant to the next generation, so
|
|
17
18
|
* a session created again with the same id starts in an empty basin at sequence 0; the old
|
|
18
|
-
* basin is deleted afterwards. The cursor is `base64url("<sessionId>:<seq>")`.
|
|
19
|
+
* basin is deleted afterwards. The cursor is `base64url("<sessionId>:<seq>")`. The `runs`
|
|
20
|
+
* stream starts at 0 in each generation too; its cursor names the generation
|
|
21
|
+
* (`base64url("runs:<generation>:<seq>")`), so one from an older basin is refused.
|
|
19
22
|
*
|
|
20
23
|
* S2 is the supported implementation (`adapters/streams/s2.ts`, the only file
|
|
21
24
|
* importing the S2 SDK); `streams/memory.ts` is the in-memory fake.
|
|
@@ -100,6 +103,14 @@ export interface DurableStreams {
|
|
|
100
103
|
}): Promise<void>;
|
|
101
104
|
/** Deletes one stream. Idempotent. */
|
|
102
105
|
deleteStream(tenantId: string, stream: string): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Creates `stream` if it does not exist, or sets its configuration, keeping its records for
|
|
108
|
+
* `retentionSecs` (the basin's default keeps them forever). Absent for in-process
|
|
109
|
+
* implementations, which keep what they hold until they close.
|
|
110
|
+
*/
|
|
111
|
+
ensureStream?(tenantId: string, stream: string, options: {
|
|
112
|
+
retentionSecs: number;
|
|
113
|
+
}): Promise<void>;
|
|
103
114
|
/**
|
|
104
115
|
* Names of the Tenant's streams starting with `prefix`, in name order,
|
|
105
116
|
* without streams being deleted. Empty when the Tenant has no basin.
|
|
@@ -114,5 +125,9 @@ export interface DurableStreams {
|
|
|
114
125
|
probe?(signal: AbortSignal): Promise<void>;
|
|
115
126
|
}
|
|
116
127
|
export declare const SESSION_STREAM_PREFIX = "sessions/";
|
|
128
|
+
/** The Tenant's runs stream (D83): every run of its triggers, beside the session streams. */
|
|
129
|
+
export declare const RUNS_STREAM = "runs";
|
|
130
|
+
/** How long the runs stream keeps a run: 7 days. */
|
|
131
|
+
export declare const RUNS_STREAM_RETENTION_SECS: number;
|
|
117
132
|
/** The stream of a session: `sessions/<sessionId>`. */
|
|
118
133
|
export declare function sessionStream(sessionId: string): string;
|
package/dist/streams/types.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* | Stream | Contents | Written by | Read by |
|
|
8
8
|
* | --- | --- | --- | --- |
|
|
9
9
|
* | `sessions/<sessionId>` | every event of the session, in sequence | the stream relay, from the record | history and session SSE |
|
|
10
|
+
| `runs` | every run of the Tenant's triggers (D83), kept 7 days | `tenant/runs-stream.ts`, after the commit that made the run, and the sweep | `GET /v1/runs/stream` |
|
|
10
11
|
*
|
|
11
12
|
* S2 serves API listeners only (history, SSE, AG-UI, A2A): nothing inside the Runtime signals
|
|
12
13
|
* through it or waits on it (D48). Signals between processes go over the control bus on
|
|
@@ -15,7 +16,9 @@
|
|
|
15
16
|
* **Basin generations.** A session id is unique within its Tenant's basin generation. A
|
|
16
17
|
* Tenant reset, the only path that frees ids, moves the Tenant to the next generation, so
|
|
17
18
|
* a session created again with the same id starts in an empty basin at sequence 0; the old
|
|
18
|
-
* basin is deleted afterwards. The cursor is `base64url("<sessionId>:<seq>")`.
|
|
19
|
+
* basin is deleted afterwards. The cursor is `base64url("<sessionId>:<seq>")`. The `runs`
|
|
20
|
+
* stream starts at 0 in each generation too; its cursor names the generation
|
|
21
|
+
* (`base64url("runs:<generation>:<seq>")`), so one from an older basin is refused.
|
|
19
22
|
*
|
|
20
23
|
* S2 is the supported implementation (`adapters/streams/s2.ts`, the only file
|
|
21
24
|
* importing the S2 SDK); `streams/memory.ts` is the in-memory fake.
|
|
@@ -44,6 +47,10 @@
|
|
|
44
47
|
// ---------------------------------------------------------------------------
|
|
45
48
|
// Stream names
|
|
46
49
|
export const SESSION_STREAM_PREFIX = "sessions/";
|
|
50
|
+
/** The Tenant's runs stream (D83): every run of its triggers, beside the session streams. */
|
|
51
|
+
export const RUNS_STREAM = "runs";
|
|
52
|
+
/** How long the runs stream keeps a run: 7 days. */
|
|
53
|
+
export const RUNS_STREAM_RETENTION_SECS = 7 * 24 * 3600;
|
|
47
54
|
/** The stream of a session: `sessions/<sessionId>`. */
|
|
48
55
|
export function sessionStream(sessionId) {
|
|
49
56
|
if (!sessionId)
|
package/dist/tenant/advance.js
CHANGED
|
@@ -621,6 +621,13 @@ async function heldCalls(t, effectIds) {
|
|
|
621
621
|
...(row.server === undefined ? {} : { server: row.server }),
|
|
622
622
|
...(row.tool === undefined ? {} : { tool: row.tool }),
|
|
623
623
|
input: request.input ?? null,
|
|
624
|
+
// What a client renders the approval from (D83): the tool's identity and the call's input.
|
|
625
|
+
metadata: {
|
|
626
|
+
tool: row.tool ?? toolName,
|
|
627
|
+
...(row.toolKind === undefined ? {} : { kind: row.toolKind }),
|
|
628
|
+
...(row.server === undefined ? {} : { server: row.server }),
|
|
629
|
+
input: request.input ?? null,
|
|
630
|
+
},
|
|
624
631
|
},
|
|
625
632
|
status: "interaction",
|
|
626
633
|
toolName,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `triggers` tool (D83): an agent whose version declares `harness.engine.triggers` lists,
|
|
3
|
+
* creates and deletes its own triggers. The Runtime serves each call as a `runtime` effect
|
|
4
|
+
* (operation `triggers`), in the call's own transaction, keyed by the effect id, so a replayed turn
|
|
5
|
+
* changes nothing twice (`harness-api/record.ts`). The Tenant's tool policy decides it first (a
|
|
6
|
+
* rule of `kind: "runtime"`, `tool: "triggers"`), so an `ask` rule can hold a create for a person.
|
|
7
|
+
*
|
|
8
|
+
* A trigger an agent makes is a row of `agent_triggers` with origin `agent`: its definition (a
|
|
9
|
+
* `Trigger`, as a manifest entry holds it) and title on the row, with the session and turn that
|
|
10
|
+
* made it. It fires through `fireTimer` and `deliverRun` like a manifest's (`triggers.ts`), and an
|
|
11
|
+
* upgrade never touches it.
|
|
12
|
+
*
|
|
13
|
+
* - **Where it runs.** `session: "this"` runs it in the calling session (a follow-up); `"own"` in
|
|
14
|
+
* a session named from the calling one, `<session>_t_<id>`, opened at its first run and kept.
|
|
15
|
+
* - **Scope.** A session lists and deletes the triggers made from it, and a trigger's own session
|
|
16
|
+
* may list and delete it too. Creating with an id the session's trigger has replaces it; an id
|
|
17
|
+
* another session's trigger, a manifest's or a wake holds is `tool.conflict`.
|
|
18
|
+
* - **Runs can't multiply.** A turn a trigger or a booked wake started may list and delete, never
|
|
19
|
+
* create.
|
|
20
|
+
* - **Checks.** Every create runs the checks a manifest save runs (`triggerIssues`): cron and
|
|
21
|
+
* zone, the minimum interval, `at` in the future, the prompt's length; and the count of the
|
|
22
|
+
* Agent's agent-made triggers. Each against the limits in force: the manifest's
|
|
23
|
+
* `runtime.limits.triggers` within the Tenant's (`triggerLimitsInForce`).
|
|
24
|
+
* - **Arming.** No I/O in the transaction: the call returns the rows to arm, which the record arms
|
|
25
|
+
* after its commit; the sweep's reconcile arms any it missed.
|
|
26
|
+
*/
|
|
27
|
+
import { type TriggerToolOutput } from "@nylorun/core/contracts";
|
|
28
|
+
import type { AgentTriggerRow, Tx } from "../store/types.js";
|
|
29
|
+
import type { Session } from "./context.js";
|
|
30
|
+
/** What a served call settles as, and the rows to arm once its transaction commits. */
|
|
31
|
+
export interface ServedTriggers {
|
|
32
|
+
readonly outcome: {
|
|
33
|
+
kind: "completed";
|
|
34
|
+
output: TriggerToolOutput;
|
|
35
|
+
} | {
|
|
36
|
+
kind: "failed";
|
|
37
|
+
code: string;
|
|
38
|
+
message: string;
|
|
39
|
+
};
|
|
40
|
+
readonly arm: readonly AgentTriggerRow[];
|
|
41
|
+
}
|
|
42
|
+
/** The session an agent-made trigger runs in: the calling one, or its own, `<session>_t_<id>`. */
|
|
43
|
+
export declare function triggerSessionOf(callingSessionId: string, id: string, session: "this" | "own"): string;
|
|
44
|
+
/**
|
|
45
|
+
* Why session `s` cannot make `triggers` call `request` at all, before the Tenant's tool policy
|
|
46
|
+
* is asked (off means off, ADR-100): its version does not declare `engine.triggers`
|
|
47
|
+
* (`tool.unavailable`), or an agent used as a tool made it (`tool.forbidden`). Undefined when it can.
|
|
48
|
+
*/
|
|
49
|
+
export declare function triggersRefusal(t: Tx, s: Session, request: {
|
|
50
|
+
agent?: unknown;
|
|
51
|
+
}): Promise<ServedTriggers["outcome"] | undefined>;
|
|
52
|
+
/**
|
|
53
|
+
* Serves one `triggers` call of session `s`'s active turn in `t` (see the module comment). The
|
|
54
|
+
* call's `agent` is the agent used as a tool that made it, absent for the root agent.
|
|
55
|
+
*/
|
|
56
|
+
export declare function serveTriggersTool(t: Tx, s: Session, request: {
|
|
57
|
+
effectId: string;
|
|
58
|
+
input?: unknown;
|
|
59
|
+
agent?: unknown;
|
|
60
|
+
}): Promise<ServedTriggers>;
|