@nylorun/runtime 0.36.0-beta → 0.37.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 +60 -0
- package/README.md +10 -0
- package/dist/api/a2a/card.d.ts +10 -4
- package/dist/api/a2a/card.js +18 -6
- package/dist/api/a2a/endpoint.d.ts +5 -2
- package/dist/api/a2a/endpoint.js +31 -9
- package/dist/api/a2a/extensions.d.ts +41 -0
- package/dist/api/a2a/extensions.js +92 -0
- package/dist/api/a2a/list.d.ts +60 -0
- package/dist/api/a2a/list.js +169 -0
- package/dist/api/a2a/protocol.d.ts +105 -9
- package/dist/api/a2a/protocol.js +169 -18
- package/dist/api/a2a/push.d.ts +76 -0
- package/dist/api/a2a/push.js +305 -0
- package/dist/api/a2a/routes.d.ts +12 -6
- package/dist/api/a2a/routes.js +409 -112
- package/dist/api/a2a/tasks.d.ts +72 -4
- package/dist/api/a2a/tasks.js +305 -27
- package/dist/api/components.d.ts +532 -0
- package/dist/api/components.js +3 -1
- package/dist/api/http/app.js +2 -0
- package/dist/api/http/routes/artifacts.js +19 -0
- package/dist/api/http/routes/turns.d.ts +18 -0
- package/dist/api/http/routes/turns.js +67 -0
- package/dist/api/inbound-files.d.ts +12 -1
- package/dist/api/inbound-files.js +44 -0
- package/dist/api/openapi.js +7 -2
- package/dist/host/config-for.d.ts +2 -0
- package/dist/host/config-for.js +1 -0
- package/dist/host/main.js +1 -0
- package/dist/host/stack-config.d.ts +16 -0
- package/dist/host/stack-config.js +30 -0
- package/dist/management-openapi.json +5 -2
- package/dist/openapi.json +1485 -738
- package/dist/reads/types.d.ts +19 -0
- package/dist/store/postgres/drizzle/0039_a2a_push.sql +32 -0
- package/dist/store/postgres/drizzle/0040_turn_index.sql +23 -0
- package/dist/store/postgres/drizzle/0041_turn_index_backfill.sql +88 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +21 -0
- package/dist/store/postgres/reads.js +41 -1
- package/dist/store/postgres/schema.d.ts +1296 -534
- package/dist/store/postgres/schema.js +92 -0
- package/dist/store/postgres/store.js +133 -3
- package/dist/store/postgres/turns.d.ts +62 -0
- package/dist/store/postgres/turns.js +153 -0
- package/dist/store/types.d.ts +73 -3
- package/dist/tenant/runtime.js +7 -5
- package/dist/tenant/session-model.d.ts +11 -1
- package/dist/tenant/session-model.js +34 -5
- package/dist/tenant/sessions.d.ts +2 -1
- package/dist/tenant/sessions.js +8 -2
- package/dist/tenant/sweep.d.ts +3 -0
- package/dist/tenant/sweep.js +39 -11
- package/dist/tenant/types.d.ts +23 -0
- package/dist/tuning.d.ts +9 -0
- package/dist/tuning.js +11 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
package/dist/reads/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ArtifactKind, ArtifactPage, ModelCallExportPage, ModelCallsPage, ToolCallsPage, SessionManifestView, SessionPage, SessionListItem, SessionUsageTotals, LiveEvent } from "@nylorun/core/contracts";
|
|
2
|
+
import type { TurnRow } from "../store/types.js";
|
|
2
3
|
export type ReadAccess = {
|
|
3
4
|
owner?: string;
|
|
4
5
|
agents?: readonly string[];
|
|
@@ -14,6 +15,12 @@ export type PageOptions = {
|
|
|
14
15
|
limit: number;
|
|
15
16
|
cursor?: string;
|
|
16
17
|
};
|
|
18
|
+
/** A page of the turn change feed: rows in the order they changed, and where to read on. */
|
|
19
|
+
export type TurnChangeRows = {
|
|
20
|
+
turns: TurnRow[];
|
|
21
|
+
cursor: string;
|
|
22
|
+
hasMore: boolean;
|
|
23
|
+
};
|
|
17
24
|
export type ArtifactFilters = {
|
|
18
25
|
sessionId?: string;
|
|
19
26
|
kind?: ArtifactKind;
|
|
@@ -34,5 +41,17 @@ export interface ReadStore {
|
|
|
34
41
|
* for a turn the session never had.
|
|
35
42
|
*/
|
|
36
43
|
turnEvents(id: string, turnId: string, access: ReadAccess): Promise<LiveEvent[]>;
|
|
44
|
+
/**
|
|
45
|
+
* The turn index's change feed (Host feature `turns`): the turns `access` reaches (of one
|
|
46
|
+
* session with `sessionId`), in the order their transactions changed them, after `changedAfter`
|
|
47
|
+
* (a cursor it returned). Only changes below every transaction still running are read, so a
|
|
48
|
+
* change committed later never lands behind the cursor.
|
|
49
|
+
*/
|
|
50
|
+
turnChanges(filters: {
|
|
51
|
+
sessionId?: string;
|
|
52
|
+
}, page: {
|
|
53
|
+
limit: number;
|
|
54
|
+
changedAfter?: string;
|
|
55
|
+
}, access: ReadAccess): Promise<TurnChangeRows>;
|
|
37
56
|
close(): Promise<void>;
|
|
38
57
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
CREATE TABLE "nylorun"."a2a_push_configs" (
|
|
2
|
+
"session_id" text COLLATE "C" NOT NULL,
|
|
3
|
+
"task_id" text COLLATE "C" NOT NULL,
|
|
4
|
+
"config_id" text COLLATE "C" NOT NULL,
|
|
5
|
+
"context_id" text NOT NULL,
|
|
6
|
+
"turn_id" text COLLATE "C" NOT NULL,
|
|
7
|
+
"url" text NOT NULL,
|
|
8
|
+
"token" text,
|
|
9
|
+
"authentication" json,
|
|
10
|
+
"base_url" text NOT NULL,
|
|
11
|
+
"extensions" json NOT NULL,
|
|
12
|
+
"trace_id" text,
|
|
13
|
+
"next_seq" bigint NOT NULL,
|
|
14
|
+
"created_at" timestamp with time zone DEFAULT clock_timestamp() NOT NULL,
|
|
15
|
+
"done_at" timestamp with time zone,
|
|
16
|
+
CONSTRAINT "a2a_push_configs_pkey" PRIMARY KEY("session_id","task_id","config_id")
|
|
17
|
+
);
|
|
18
|
+
--> statement-breakpoint
|
|
19
|
+
CREATE TABLE "nylorun"."a2a_pushes" (
|
|
20
|
+
"id" text COLLATE "C" PRIMARY KEY NOT NULL,
|
|
21
|
+
"session_id" text COLLATE "C" NOT NULL,
|
|
22
|
+
"task_id" text COLLATE "C" NOT NULL,
|
|
23
|
+
"config_id" text COLLATE "C" NOT NULL,
|
|
24
|
+
"body" text NOT NULL,
|
|
25
|
+
"created_at" timestamp with time zone DEFAULT clock_timestamp() NOT NULL,
|
|
26
|
+
"attempts" integer DEFAULT 0 NOT NULL,
|
|
27
|
+
"retry_at" timestamp with time zone,
|
|
28
|
+
"parked_at" timestamp with time zone
|
|
29
|
+
);
|
|
30
|
+
--> statement-breakpoint
|
|
31
|
+
CREATE INDEX "a2a_push_configs_done_at" ON "nylorun"."a2a_push_configs" USING btree ("done_at");--> statement-breakpoint
|
|
32
|
+
CREATE INDEX "a2a_pushes_created_at" ON "nylorun"."a2a_pushes" USING btree ("created_at");
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
CREATE TABLE "nylorun"."turns" (
|
|
2
|
+
"session_id" text COLLATE "C" NOT NULL,
|
|
3
|
+
"turn_id" text COLLATE "C" NOT NULL,
|
|
4
|
+
"agent_id" text COLLATE "C" NOT NULL,
|
|
5
|
+
"owner_user_id" text COLLATE "C",
|
|
6
|
+
"a2a_context_id" text COLLATE "C",
|
|
7
|
+
"status" text NOT NULL,
|
|
8
|
+
"started_at" text COLLATE "C" NOT NULL,
|
|
9
|
+
"ended_at" text COLLATE "C",
|
|
10
|
+
"changed_at" text COLLATE "C" NOT NULL,
|
|
11
|
+
"totals" json,
|
|
12
|
+
"waiting_for" text,
|
|
13
|
+
"queued_message_ids" text[] DEFAULT '{}' NOT NULL,
|
|
14
|
+
"first_seq" bigint NOT NULL,
|
|
15
|
+
"last_seq" bigint NOT NULL,
|
|
16
|
+
"txid" "xid8" DEFAULT pg_current_xact_id() NOT NULL,
|
|
17
|
+
CONSTRAINT "turns_pkey" PRIMARY KEY("session_id","turn_id")
|
|
18
|
+
);
|
|
19
|
+
--> statement-breakpoint
|
|
20
|
+
CREATE INDEX "turns_turn" ON "nylorun"."turns" USING btree ("turn_id");--> statement-breakpoint
|
|
21
|
+
CREATE INDEX "turns_changes" ON "nylorun"."turns" USING btree ("txid","session_id","turn_id");--> statement-breakpoint
|
|
22
|
+
CREATE INDEX "turns_owner_changes" ON "nylorun"."turns" USING btree ("owner_user_id","txid");--> statement-breakpoint
|
|
23
|
+
CREATE INDEX "turns_owner_agent" ON "nylorun"."turns" USING btree ("owner_user_id","agent_id","changed_at");
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
-- Custom SQL (a data change): the turn index (0040) for the turns the record already holds. Each
|
|
2
|
+
-- turn's row is what `Tx.event` would have written for its lifecycle events (`command.message`,
|
|
3
|
+
-- `command.approve`, `command.respond`, `turn.paused`, `turn.completed`, `turn.failed`,
|
|
4
|
+
-- `turn.cancelled`): its status from the last of them (and, paused, what its first open
|
|
5
|
+
-- interaction asks for), its start from the first, and the queued messages it took. Its session's
|
|
6
|
+
-- Agent, owner and A2A context come from the session row as it is now. Rows written from here on
|
|
7
|
+
-- come from `Tx.event`, in the transaction of the event.
|
|
8
|
+
WITH lifecycle AS (
|
|
9
|
+
SELECT "session_id", "seq", "type", nylorun.doc("body") AS doc
|
|
10
|
+
FROM "nylorun_streams"."session_events"
|
|
11
|
+
WHERE "type" IN (
|
|
12
|
+
'command.message', 'command.approve', 'command.respond',
|
|
13
|
+
'turn.paused', 'turn.completed', 'turn.failed', 'turn.cancelled'
|
|
14
|
+
)
|
|
15
|
+
), turned AS (
|
|
16
|
+
SELECT "session_id", doc->>'turnId' AS turn_id, "seq", "type", doc
|
|
17
|
+
FROM lifecycle
|
|
18
|
+
WHERE doc->>'turnId' IS NOT NULL
|
|
19
|
+
), bounds AS (
|
|
20
|
+
SELECT "session_id", turn_id, min("seq") AS first_seq,
|
|
21
|
+
min(doc->>'time') AS started_at,
|
|
22
|
+
coalesce(
|
|
23
|
+
array_remove(
|
|
24
|
+
array_agg(doc->'payload'->>'queuedMessageId' ORDER BY "seq")
|
|
25
|
+
FILTER (WHERE "type" = 'command.message'),
|
|
26
|
+
NULL
|
|
27
|
+
),
|
|
28
|
+
'{}'
|
|
29
|
+
) AS queued_message_ids
|
|
30
|
+
FROM turned
|
|
31
|
+
GROUP BY "session_id", turn_id
|
|
32
|
+
), latest AS (
|
|
33
|
+
-- A turn's further messages (a queued batch) change nothing but its queued messages.
|
|
34
|
+
SELECT DISTINCT ON (t."session_id", t.turn_id) t."session_id", t.turn_id, t."seq", t."type", t.doc
|
|
35
|
+
FROM turned t
|
|
36
|
+
JOIN bounds b ON b."session_id" = t."session_id" AND b.turn_id = t.turn_id
|
|
37
|
+
WHERE NOT (t."type" = 'command.message' AND t."seq" > b.first_seq)
|
|
38
|
+
ORDER BY t."session_id", t.turn_id, t."seq" DESC
|
|
39
|
+
)
|
|
40
|
+
INSERT INTO "nylorun"."turns" (
|
|
41
|
+
"session_id", "turn_id", "agent_id", "owner_user_id", "a2a_context_id", "status",
|
|
42
|
+
"started_at", "ended_at", "changed_at", "totals", "waiting_for", "queued_message_ids",
|
|
43
|
+
"first_seq", "last_seq"
|
|
44
|
+
)
|
|
45
|
+
SELECT
|
|
46
|
+
b."session_id",
|
|
47
|
+
b.turn_id,
|
|
48
|
+
coalesce(s."agent_id", ''),
|
|
49
|
+
s."owner_user_id",
|
|
50
|
+
nylorun.doc(s."body")->'info'->'a2a'->>'contextId',
|
|
51
|
+
CASE l."type"
|
|
52
|
+
WHEN 'turn.completed' THEN 'completed'
|
|
53
|
+
WHEN 'turn.failed' THEN 'failed'
|
|
54
|
+
WHEN 'turn.cancelled' THEN 'cancelled'
|
|
55
|
+
WHEN 'turn.paused' THEN 'paused'
|
|
56
|
+
ELSE 'running'
|
|
57
|
+
END,
|
|
58
|
+
b.started_at,
|
|
59
|
+
CASE WHEN l."type" IN ('turn.completed', 'turn.failed', 'turn.cancelled') THEN l.doc->>'time' END,
|
|
60
|
+
l.doc->>'time',
|
|
61
|
+
CASE WHEN l."type" IN ('turn.completed', 'turn.failed', 'turn.cancelled')
|
|
62
|
+
AND jsonb_typeof(l.doc->'payload'->'totals'->'steps') = 'number'
|
|
63
|
+
AND jsonb_typeof(l.doc->'payload'->'totals'->'tokens') = 'number'
|
|
64
|
+
AND jsonb_typeof(l.doc->'payload'->'totals'->'costUsd') = 'number'
|
|
65
|
+
THEN json_build_object(
|
|
66
|
+
'steps', l.doc->'payload'->'totals'->'steps',
|
|
67
|
+
'tokens', l.doc->'payload'->'totals'->'tokens',
|
|
68
|
+
'costUsd', l.doc->'payload'->'totals'->'costUsd'
|
|
69
|
+
) END,
|
|
70
|
+
CASE WHEN l."type" = 'turn.paused' AND jsonb_typeof(l.doc->'payload'->'interactions') = 'array' THEN (
|
|
71
|
+
SELECT CASE
|
|
72
|
+
WHEN x.e->'interaction'->>'kind' = 'connection'
|
|
73
|
+
OR x.e->'interaction'->'metadata'->>'kind' = 'connection' THEN 'connection'
|
|
74
|
+
WHEN x.e->'interaction'->>'kind' = 'approval' THEN 'approval'
|
|
75
|
+
ELSE 'question'
|
|
76
|
+
END
|
|
77
|
+
FROM jsonb_array_elements(l.doc->'payload'->'interactions') WITH ORDINALITY AS x(e, i)
|
|
78
|
+
WHERE (x.e->>'status' IS NULL OR x.e->>'status' = 'interaction')
|
|
79
|
+
AND jsonb_typeof(x.e->'interaction'->'id') = 'string'
|
|
80
|
+
ORDER BY x.i
|
|
81
|
+
LIMIT 1
|
|
82
|
+
) END,
|
|
83
|
+
b.queued_message_ids,
|
|
84
|
+
b.first_seq,
|
|
85
|
+
l."seq"
|
|
86
|
+
FROM bounds b
|
|
87
|
+
JOIN latest l ON l."session_id" = b."session_id" AND l.turn_id = b.turn_id
|
|
88
|
+
LEFT JOIN "nylorun"."sessions" s ON s."id" = b."session_id";
|
|
@@ -274,6 +274,27 @@
|
|
|
274
274
|
"when": 1792484344984,
|
|
275
275
|
"tag": "0038_t6_agent_manifest_source",
|
|
276
276
|
"breakpoints": true
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"idx": 39,
|
|
280
|
+
"version": "7",
|
|
281
|
+
"when": 1793445550433,
|
|
282
|
+
"tag": "0039_a2a_push",
|
|
283
|
+
"breakpoints": true
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"idx": 40,
|
|
287
|
+
"version": "7",
|
|
288
|
+
"when": 1793448351698,
|
|
289
|
+
"tag": "0040_turn_index",
|
|
290
|
+
"breakpoints": true
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
"idx": 41,
|
|
294
|
+
"version": "7",
|
|
295
|
+
"when": 1793448353003,
|
|
296
|
+
"tag": "0041_turn_index_backfill",
|
|
297
|
+
"breakpoints": true
|
|
277
298
|
}
|
|
278
299
|
]
|
|
279
300
|
}
|
|
@@ -11,7 +11,7 @@ import { database, driverError } from "./db.js";
|
|
|
11
11
|
import { createPostgresReadClient } from "./connect.js";
|
|
12
12
|
import { hostTuning } from "../../tuning.js";
|
|
13
13
|
import { toolCallDuration, toolCallStatus } from "../../harness-api/tool-status.js";
|
|
14
|
-
import { effects, artifacts, modelUsage, sandboxes, sessions, sessionEvents, tenantSettings, } from "./schema.js";
|
|
14
|
+
import { effects, artifacts, modelUsage, sandboxes, sessions, sessionEvents, tenantSettings, turns, } from "./schema.js";
|
|
15
15
|
const sessionFields = {
|
|
16
16
|
id: sessions.id,
|
|
17
17
|
agentId: sessions.agentId,
|
|
@@ -101,6 +101,20 @@ function publicToolCall(body) {
|
|
|
101
101
|
durationMs: toolCallDuration(body) ?? null,
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
|
+
/** The change feed's cursor key: the last row's `[txid, sessionId, turnId]`. */
|
|
105
|
+
function turnChangeKey(key) {
|
|
106
|
+
if (key === undefined)
|
|
107
|
+
return undefined;
|
|
108
|
+
const [txid, sessionId, turnId] = key;
|
|
109
|
+
if (key.length !== 3 ||
|
|
110
|
+
typeof txid !== "string" ||
|
|
111
|
+
typeof sessionId !== "string" ||
|
|
112
|
+
typeof turnId !== "string" ||
|
|
113
|
+
!/^\d{1,20}$/.test(txid) ||
|
|
114
|
+
BigInt(txid) > 18446744073709551615n)
|
|
115
|
+
return fail(400, "Invalid cursor key", { code: "cursor_invalid" });
|
|
116
|
+
return [txid, sessionId, turnId];
|
|
117
|
+
}
|
|
104
118
|
function keyOf(key, length, timestamp = false, nullable = false) {
|
|
105
119
|
if (key &&
|
|
106
120
|
(key.length !== length ||
|
|
@@ -316,6 +330,32 @@ export function createPostgresReadStore(source, tenantId) {
|
|
|
316
330
|
.orderBy(asc(sessionEvents.seq));
|
|
317
331
|
return rows.map((row) => row.body);
|
|
318
332
|
}),
|
|
333
|
+
turnChanges: (filters, page, access) => read(async (tx) => {
|
|
334
|
+
if (filters.sessionId !== undefined)
|
|
335
|
+
await requireSession(tx, filters.sessionId, access);
|
|
336
|
+
const cursor = readCursor(tenantId, "turns", { sessionId: filters.sessionId ?? null });
|
|
337
|
+
const key = turnChangeKey(cursor.decode(page.changedAfter));
|
|
338
|
+
const rows = await tx
|
|
339
|
+
.select()
|
|
340
|
+
.from(turns)
|
|
341
|
+
.where(and(
|
|
342
|
+
// Below every transaction still running: one that commits later sorts after.
|
|
343
|
+
sql `${turns.txid} < pg_snapshot_xmin(pg_current_snapshot())`, key
|
|
344
|
+
? sql `(${turns.txid}, ${turns.sessionId}, ${turns.turnId}) > (${key[0]}::xid8, ${key[1]}, ${key[2]})`
|
|
345
|
+
: undefined, filters.sessionId === undefined ? undefined : eq(turns.sessionId, filters.sessionId), access.owner === undefined ? undefined : eq(turns.ownerUserId, access.owner), access.agents === undefined ? undefined : grantsWhere(turns.agentId, access.agents)))
|
|
346
|
+
.orderBy(asc(turns.txid), asc(turns.sessionId), asc(turns.turnId))
|
|
347
|
+
.limit(page.limit + 1);
|
|
348
|
+
const items = rows.slice(0, page.limit);
|
|
349
|
+
const last = items.at(-1);
|
|
350
|
+
return {
|
|
351
|
+
turns: items,
|
|
352
|
+
// Echoed when nothing changed; a first read starts before every row.
|
|
353
|
+
cursor: last
|
|
354
|
+
? cursor.encode([last.txid, last.sessionId, last.turnId])
|
|
355
|
+
: (page.changedAfter ?? cursor.encode(["0", "", ""])),
|
|
356
|
+
hasMore: rows.length > page.limit,
|
|
357
|
+
};
|
|
358
|
+
}),
|
|
319
359
|
exportModel: (after, limit) => read(async (tx) => {
|
|
320
360
|
const cursor = readCursor(tenantId, "model-export", {});
|
|
321
361
|
const key = keyOf(cursor.decode(after), 2);
|