@amaster.ai/pi-storage 0.1.0-beta.0
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/LICENSE +201 -0
- package/README.md +56 -0
- package/dist/artifact-stores.d.ts +13 -0
- package/dist/artifact-stores.d.ts.map +1 -0
- package/dist/artifact-stores.js +84 -0
- package/dist/artifact-stores.js.map +1 -0
- package/dist/db-migrations.d.ts +12 -0
- package/dist/db-migrations.d.ts.map +1 -0
- package/dist/db-migrations.js +151 -0
- package/dist/db-migrations.js.map +1 -0
- package/dist/db-runtime-storage.d.ts +15 -0
- package/dist/db-runtime-storage.d.ts.map +1 -0
- package/dist/db-runtime-storage.js +1051 -0
- package/dist/db-runtime-storage.js.map +1 -0
- package/dist/db.d.ts +3 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +3 -0
- package/dist/db.js.map +1 -0
- package/dist/event-stores.d.ts +69 -0
- package/dist/event-stores.d.ts.map +1 -0
- package/dist/event-stores.js +196 -0
- package/dist/event-stores.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +4 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +4 -0
- package/dist/internal.js.map +1 -0
- package/dist/json-file.d.ts +4 -0
- package/dist/json-file.d.ts.map +1 -0
- package/dist/json-file.js +59 -0
- package/dist/json-file.js.map +1 -0
- package/dist/json.d.ts +5 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +5 -0
- package/dist/json.js.map +1 -0
- package/dist/migration-main.d.ts +2 -0
- package/dist/migration-main.d.ts.map +1 -0
- package/dist/migration-main.js +12 -0
- package/dist/migration-main.js.map +1 -0
- package/dist/redis-locks.d.ts +18 -0
- package/dist/redis-locks.d.ts.map +1 -0
- package/dist/redis-locks.js +76 -0
- package/dist/redis-locks.js.map +1 -0
- package/dist/runtime-scope.d.ts +2 -0
- package/dist/runtime-scope.d.ts.map +1 -0
- package/dist/runtime-scope.js +2 -0
- package/dist/runtime-scope.js.map +1 -0
- package/dist/runtime-storage.d.ts +30 -0
- package/dist/runtime-storage.d.ts.map +1 -0
- package/dist/runtime-storage.js +60 -0
- package/dist/runtime-storage.js.map +1 -0
- package/dist/scheduled-task-stores.d.ts +49 -0
- package/dist/scheduled-task-stores.d.ts.map +1 -0
- package/dist/scheduled-task-stores.js +381 -0
- package/dist/scheduled-task-stores.js.map +1 -0
- package/dist/scheduler.d.ts +2 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +2 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/session-stores.d.ts +47 -0
- package/dist/session-stores.d.ts.map +1 -0
- package/dist/session-stores.js +260 -0
- package/dist/session-stores.js.map +1 -0
- package/dist/subagent-store.d.ts +26 -0
- package/dist/subagent-store.d.ts.map +1 -0
- package/dist/subagent-store.js +167 -0
- package/dist/subagent-store.js.map +1 -0
- package/package.json +70 -0
- package/prisma/migrations/mysql/20260514113000_init_runtime/migration.sql +351 -0
- package/prisma/schema.prisma +351 -0
|
@@ -0,0 +1,1051 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MySQL-backed runtime stores for platform mode.
|
|
3
|
+
*
|
|
4
|
+
* These adapters map the runtime store interfaces onto the platform schema in
|
|
5
|
+
* packages/storage/prisma/schema.prisma. JSON mode remains the local developer
|
|
6
|
+
* adapter; DB mode uses these stores directly and never falls back silently.
|
|
7
|
+
*/
|
|
8
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
9
|
+
import { Prisma, PrismaClient } from "@prisma/client";
|
|
10
|
+
import { isTerminalSubagentStatus, } from "./subagent-store.js";
|
|
11
|
+
import { RedisLockManager } from "./redis-locks.js";
|
|
12
|
+
const REQUIRED_DB_TABLES = [
|
|
13
|
+
"pi_agent_sessions",
|
|
14
|
+
"pi_agent_turns",
|
|
15
|
+
"pi_agent_messages",
|
|
16
|
+
"pi_agent_turn_queue",
|
|
17
|
+
"pi_agent_turn_signals",
|
|
18
|
+
"pi_agent_events",
|
|
19
|
+
"pi_agent_subagent_runs",
|
|
20
|
+
"pi_agent_approvals",
|
|
21
|
+
"pi_agent_memory",
|
|
22
|
+
"pi_agent_scheduled_tasks",
|
|
23
|
+
"pi_agent_task_runs",
|
|
24
|
+
"pi_agent_artifacts",
|
|
25
|
+
];
|
|
26
|
+
export function createDbRuntimeStores(databaseUrl, redisUrl) {
|
|
27
|
+
const context = new DbRuntimeContext(databaseUrl, redisUrl);
|
|
28
|
+
const timelineEvents = new DbRuntimeTimelineEventStore(context);
|
|
29
|
+
return {
|
|
30
|
+
store: new DbRuntimeSessionStore(context),
|
|
31
|
+
transcripts: new DbTranscriptStore(context),
|
|
32
|
+
memory: new DbMemoryStore(context),
|
|
33
|
+
runtimeEvents: new DbRuntimeEventStore(timelineEvents),
|
|
34
|
+
toolEvents: new DbToolEventStore(timelineEvents),
|
|
35
|
+
llmGenerationEvents: new DbLlmGenerationEventStore(timelineEvents),
|
|
36
|
+
timelineEvents,
|
|
37
|
+
subagents: new DbSubagentRunStore(context),
|
|
38
|
+
artifacts: new DbArtifactStore(context),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export async function verifyDbRuntimeSchema(databaseUrl) {
|
|
42
|
+
const prisma = new PrismaClient({ datasources: { db: { url: databaseUrl } } });
|
|
43
|
+
try {
|
|
44
|
+
const rows = await prisma.$queryRaw(Prisma.sql `SELECT TABLE_NAME AS table_name
|
|
45
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
46
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
47
|
+
AND TABLE_NAME IN (${Prisma.join([...REQUIRED_DB_TABLES])})`);
|
|
48
|
+
const found = new Set(rows.map((row) => row.table_name));
|
|
49
|
+
const missing = REQUIRED_DB_TABLES.filter((table) => !found.has(table));
|
|
50
|
+
if (missing.length > 0) {
|
|
51
|
+
throw new Error(`STORAGE_MODE=db schema is missing required table(s): ${missing.join(", ")}. ` +
|
|
52
|
+
"Apply the pi runtime DB migration before starting server.");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
await prisma.$disconnect();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
class DbRuntimeContext {
|
|
60
|
+
prisma;
|
|
61
|
+
locks;
|
|
62
|
+
constructor(databaseUrl, redisUrl) {
|
|
63
|
+
this.prisma = new PrismaClient({
|
|
64
|
+
datasources: {
|
|
65
|
+
db: { url: databaseUrl },
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
this.locks = new RedisLockManager(redisUrl);
|
|
69
|
+
}
|
|
70
|
+
async resolveIdentity(sessionId, tx = this.prisma) {
|
|
71
|
+
const row = await tx.piAgentSession.findFirst({
|
|
72
|
+
where: { sessionId: { in: identityLookupSessionIds(sessionId) }, deletedAt: null },
|
|
73
|
+
orderBy: { updatedAt: "desc" },
|
|
74
|
+
select: { tenantId: true, userId: true, workspaceId: true },
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
tenantId: row?.tenantId ?? "default",
|
|
78
|
+
userId: row?.userId ?? "system",
|
|
79
|
+
...(row?.workspaceId ? { workspaceId: row.workspaceId } : {}),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function identityLookupSessionIds(sessionId) {
|
|
84
|
+
const subagentMarker = ":subagent:";
|
|
85
|
+
const markerIndex = sessionId.indexOf(subagentMarker);
|
|
86
|
+
if (markerIndex <= 0) {
|
|
87
|
+
return [sessionId];
|
|
88
|
+
}
|
|
89
|
+
return [sessionId, sessionId.slice(0, markerIndex)];
|
|
90
|
+
}
|
|
91
|
+
class DbRuntimeSessionStore {
|
|
92
|
+
db;
|
|
93
|
+
constructor(db) {
|
|
94
|
+
this.db = db;
|
|
95
|
+
}
|
|
96
|
+
async getRuntimeSession(scope, sessionId) {
|
|
97
|
+
const row = await this.db.prisma.piAgentSession.findFirst({
|
|
98
|
+
where: { sessionId, deletedAt: null, ...sessionScopeWhere(scope) },
|
|
99
|
+
orderBy: { updatedAt: "desc" },
|
|
100
|
+
});
|
|
101
|
+
return row ? sessionFromPrisma(row) : undefined;
|
|
102
|
+
}
|
|
103
|
+
async saveRuntimeSession(session) {
|
|
104
|
+
const now = new Date().toISOString();
|
|
105
|
+
const tenantId = session.tenantId ?? "default";
|
|
106
|
+
const userId = session.userId ?? "system";
|
|
107
|
+
const rowId = stableRowId("session", tenantId, session.sessionId);
|
|
108
|
+
await this.db.prisma.piAgentSession.upsert({
|
|
109
|
+
where: { id: rowId },
|
|
110
|
+
create: {
|
|
111
|
+
id: rowId,
|
|
112
|
+
tenantId,
|
|
113
|
+
userId,
|
|
114
|
+
workspaceId: session.workspaceId ?? null,
|
|
115
|
+
sessionId: session.sessionId,
|
|
116
|
+
conversationId: session.conversationId,
|
|
117
|
+
parentSessionId: session.parentSessionId ?? null,
|
|
118
|
+
childSessionId: session.childSessionId ?? null,
|
|
119
|
+
taskRunId: session.taskRunId ?? null,
|
|
120
|
+
runId: session.runId ?? null,
|
|
121
|
+
spawnBatchId: session.spawnBatchId ?? null,
|
|
122
|
+
triggerType: "user",
|
|
123
|
+
status: "active",
|
|
124
|
+
modelProvider: session.model.provider,
|
|
125
|
+
modelName: session.model.model,
|
|
126
|
+
thinkingLevel: session.model.thinkingLevel ?? null,
|
|
127
|
+
toolPolicyProfile: session.toolPolicyProfile,
|
|
128
|
+
sandboxSessionId: session.sandboxSessionId ?? null,
|
|
129
|
+
sandboxStatus: session.sandboxStatus,
|
|
130
|
+
piSessionRef: session.piSessionFile ?? null,
|
|
131
|
+
metadataJson: jsonInput({ session, model: session.model }),
|
|
132
|
+
createdAt: toDate(now),
|
|
133
|
+
updatedAt: toDate(now),
|
|
134
|
+
lastActiveAt: toDate(now),
|
|
135
|
+
},
|
|
136
|
+
update: {
|
|
137
|
+
userId,
|
|
138
|
+
workspaceId: session.workspaceId ?? null,
|
|
139
|
+
conversationId: session.conversationId,
|
|
140
|
+
parentSessionId: session.parentSessionId ?? null,
|
|
141
|
+
childSessionId: session.childSessionId ?? null,
|
|
142
|
+
taskRunId: session.taskRunId ?? null,
|
|
143
|
+
runId: session.runId ?? null,
|
|
144
|
+
spawnBatchId: session.spawnBatchId ?? null,
|
|
145
|
+
modelProvider: session.model.provider,
|
|
146
|
+
modelName: session.model.model,
|
|
147
|
+
thinkingLevel: session.model.thinkingLevel ?? null,
|
|
148
|
+
toolPolicyProfile: session.toolPolicyProfile,
|
|
149
|
+
sandboxSessionId: session.sandboxSessionId ?? null,
|
|
150
|
+
sandboxStatus: session.sandboxStatus,
|
|
151
|
+
piSessionRef: session.piSessionFile ?? null,
|
|
152
|
+
metadataJson: jsonInput({ session, model: session.model }),
|
|
153
|
+
updatedAt: toDate(now),
|
|
154
|
+
lastActiveAt: toDate(now),
|
|
155
|
+
deletedAt: null,
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
async listRuntimeSessions(scope) {
|
|
160
|
+
const rows = await this.db.prisma.piAgentSession.findMany({
|
|
161
|
+
where: { deletedAt: null, ...sessionScopeWhere(scope) },
|
|
162
|
+
orderBy: [{ lastMessageAt: "desc" }, { updatedAt: "desc" }],
|
|
163
|
+
});
|
|
164
|
+
return rows.map(sessionFromPrisma);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
class DbTranscriptStore {
|
|
168
|
+
db;
|
|
169
|
+
constructor(db) {
|
|
170
|
+
this.db = db;
|
|
171
|
+
}
|
|
172
|
+
async appendTurn(turn) {
|
|
173
|
+
const identity = await this.db.resolveIdentity(turn.sessionId);
|
|
174
|
+
await this.db.locks.withLock({
|
|
175
|
+
key: `pi:turns:${identity.tenantId}:${turn.sessionId}`,
|
|
176
|
+
ttlMs: 10_000,
|
|
177
|
+
timeoutMs: 5_000,
|
|
178
|
+
task: async () => {
|
|
179
|
+
await this.db.prisma.$transaction(async (tx) => {
|
|
180
|
+
const [turnSeq, messageSeq] = await Promise.all([
|
|
181
|
+
nextBigIntSeq(tx.piAgentTurn.aggregate({
|
|
182
|
+
where: { tenantId: identity.tenantId, sessionId: turn.sessionId },
|
|
183
|
+
_max: { turnSeq: true },
|
|
184
|
+
}), "turnSeq"),
|
|
185
|
+
nextBigIntSeq(tx.piAgentMessage.aggregate({
|
|
186
|
+
where: { tenantId: identity.tenantId, sessionId: turn.sessionId },
|
|
187
|
+
_max: { messageSeq: true },
|
|
188
|
+
}), "messageSeq"),
|
|
189
|
+
]);
|
|
190
|
+
await tx.piAgentTurn.create({
|
|
191
|
+
data: {
|
|
192
|
+
id: turn.id,
|
|
193
|
+
tenantId: identity.tenantId,
|
|
194
|
+
userId: identity.userId,
|
|
195
|
+
workspaceId: identity.workspaceId ?? null,
|
|
196
|
+
sessionId: turn.sessionId,
|
|
197
|
+
conversationId: turn.conversationId,
|
|
198
|
+
traceId: turn.traceId ?? null,
|
|
199
|
+
turnSeq,
|
|
200
|
+
sourceType: "user",
|
|
201
|
+
status: "completed",
|
|
202
|
+
inputText: turn.userMessage,
|
|
203
|
+
outputText: turn.assistantMessage,
|
|
204
|
+
modelJson: jsonInput(turn.model),
|
|
205
|
+
completedAt: toDate(turn.createdAt),
|
|
206
|
+
createdAt: toDate(turn.createdAt),
|
|
207
|
+
updatedAt: toDate(turn.createdAt),
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
await tx.piAgentMessage.createMany({
|
|
211
|
+
data: [
|
|
212
|
+
messageCreateInput(identity, turn, `${turn.id}:user`, "user", turn.userMessage, messageSeq),
|
|
213
|
+
messageCreateInput(identity, turn, `${turn.id}:assistant`, "assistant", turn.assistantMessage, messageSeq + 1n),
|
|
214
|
+
],
|
|
215
|
+
});
|
|
216
|
+
await tx.piAgentSession.updateMany({
|
|
217
|
+
where: { tenantId: identity.tenantId, sessionId: turn.sessionId, deletedAt: null },
|
|
218
|
+
data: {
|
|
219
|
+
turnCount: { increment: 1 },
|
|
220
|
+
title: firstLine(turn.userMessage),
|
|
221
|
+
firstUserMessage: turn.userMessage,
|
|
222
|
+
lastUserMessage: turn.userMessage,
|
|
223
|
+
lastAssistantMessage: turn.assistantMessage,
|
|
224
|
+
lastMessageAt: toDate(turn.createdAt),
|
|
225
|
+
updatedAt: toDate(turn.createdAt),
|
|
226
|
+
lastActiveAt: toDate(turn.createdAt),
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
async listTurns(scope, sessionId) {
|
|
234
|
+
const rows = await this.db.prisma.piAgentTurn.findMany({
|
|
235
|
+
where: { ...(sessionId ? { sessionId } : {}), ...sessionScopeWhere(scope) },
|
|
236
|
+
orderBy: [{ createdAt: "asc" }, { turnSeq: "asc" }],
|
|
237
|
+
});
|
|
238
|
+
return rows.map(turnFromPrisma);
|
|
239
|
+
}
|
|
240
|
+
async listMessages(scope, sessionId) {
|
|
241
|
+
const [rows, turns] = await Promise.all([
|
|
242
|
+
this.db.prisma.piAgentMessage.findMany({
|
|
243
|
+
where: { sessionId, deletedAt: null, ...sessionScopeWhere(scope) },
|
|
244
|
+
orderBy: { messageSeq: "asc" },
|
|
245
|
+
}),
|
|
246
|
+
this.db.prisma.piAgentTurn.findMany({
|
|
247
|
+
where: { sessionId, ...sessionScopeWhere(scope) },
|
|
248
|
+
select: { id: true, traceId: true },
|
|
249
|
+
}),
|
|
250
|
+
]);
|
|
251
|
+
const traceIdsByTurnId = new Map(turns
|
|
252
|
+
.filter((turn) => Boolean(turn.traceId))
|
|
253
|
+
.map((turn) => [turn.id, turn.traceId]));
|
|
254
|
+
return rows.map((row) => messageFromPrisma(row, traceIdsByTurnId));
|
|
255
|
+
}
|
|
256
|
+
async listSessionSummaries(scope, sessions) {
|
|
257
|
+
if (sessions.length === 0) {
|
|
258
|
+
return [];
|
|
259
|
+
}
|
|
260
|
+
const ids = sessions.map((session) => session.sessionId);
|
|
261
|
+
const rows = await this.db.prisma.piAgentSession.findMany({
|
|
262
|
+
where: { sessionId: { in: ids }, deletedAt: null, ...sessionScopeWhere(scope) },
|
|
263
|
+
});
|
|
264
|
+
const bySessionId = new Map(rows.map((row) => [row.sessionId, row]));
|
|
265
|
+
return sessions.map((session) => summaryFromPrismaSession(session, bySessionId.get(session.sessionId)));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
class DbRuntimeTimelineEventStore {
|
|
269
|
+
db;
|
|
270
|
+
constructor(db) {
|
|
271
|
+
this.db = db;
|
|
272
|
+
}
|
|
273
|
+
async append(event) {
|
|
274
|
+
const identity = await this.db.resolveIdentity(event.sessionId);
|
|
275
|
+
await this.db.locks.withLock({
|
|
276
|
+
key: `pi:events:${identity.tenantId}:${event.sessionId}`,
|
|
277
|
+
ttlMs: 10_000,
|
|
278
|
+
timeoutMs: 5_000,
|
|
279
|
+
task: async () => {
|
|
280
|
+
await this.db.prisma.$transaction(async (tx) => {
|
|
281
|
+
const existing = await tx.piAgentEvent.findUnique({ where: { id: event.eventId }, select: { id: true } });
|
|
282
|
+
if (existing) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const aggregate = await tx.piAgentEvent.aggregate({
|
|
286
|
+
where: { tenantId: identity.tenantId, sessionId: event.sessionId },
|
|
287
|
+
_max: { eventSeq: true },
|
|
288
|
+
});
|
|
289
|
+
await tx.piAgentEvent.create({
|
|
290
|
+
data: {
|
|
291
|
+
id: event.eventId,
|
|
292
|
+
tenantId: identity.tenantId,
|
|
293
|
+
userId: identity.userId,
|
|
294
|
+
workspaceId: identity.workspaceId ?? null,
|
|
295
|
+
sessionId: event.sessionId,
|
|
296
|
+
turnId: event.turnId ?? null,
|
|
297
|
+
traceId: event.traceId ?? null,
|
|
298
|
+
eventSeq: (aggregate._max.eventSeq ?? 0n) + 1n,
|
|
299
|
+
eventSource: event.eventSource,
|
|
300
|
+
eventType: event.eventType,
|
|
301
|
+
eventName: event.eventName,
|
|
302
|
+
severity: "info",
|
|
303
|
+
payloadJson: jsonInput(event.payload),
|
|
304
|
+
createdAt: toDate(event.createdAt),
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
async list(input) {
|
|
312
|
+
const limit = positiveLimit(input.limit);
|
|
313
|
+
const rows = await this.db.prisma.piAgentEvent.findMany({
|
|
314
|
+
where: timelineWhere(input),
|
|
315
|
+
orderBy: [{ createdAt: "desc" }, { id: "desc" }],
|
|
316
|
+
take: limit,
|
|
317
|
+
});
|
|
318
|
+
return rows.map(timelineEventFromPrisma).reverse();
|
|
319
|
+
}
|
|
320
|
+
async listBySource(eventSource, input = {}) {
|
|
321
|
+
const rows = await this.db.prisma.piAgentEvent.findMany({
|
|
322
|
+
where: {
|
|
323
|
+
eventSource,
|
|
324
|
+
...timelineWhere(input),
|
|
325
|
+
...(input.eventType ? { eventType: input.eventType } : {}),
|
|
326
|
+
},
|
|
327
|
+
orderBy: [{ eventSeq: "desc" }, { createdAt: "desc" }],
|
|
328
|
+
take: positiveLimit(input.limit),
|
|
329
|
+
});
|
|
330
|
+
return rows.map(timelineEventFromPrisma).reverse();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
class DbRuntimeEventStore {
|
|
334
|
+
timeline;
|
|
335
|
+
constructor(timeline) {
|
|
336
|
+
this.timeline = timeline;
|
|
337
|
+
}
|
|
338
|
+
async append(event) {
|
|
339
|
+
await this.timeline.append(runtimeEventToTimeline(event));
|
|
340
|
+
}
|
|
341
|
+
async list(input = {}) {
|
|
342
|
+
const events = await this.timeline.listBySource("runtime", runtimeListInput(input));
|
|
343
|
+
return events.map((event) => event.payload);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
class DbToolEventStore {
|
|
347
|
+
timeline;
|
|
348
|
+
constructor(timeline) {
|
|
349
|
+
this.timeline = timeline;
|
|
350
|
+
}
|
|
351
|
+
async append(event) {
|
|
352
|
+
await this.timeline.append(toolEventToTimeline(event));
|
|
353
|
+
}
|
|
354
|
+
async list(input = {}) {
|
|
355
|
+
const events = await this.timeline.listBySource("tool", timelineListInput(input));
|
|
356
|
+
return events.map((event) => event.payload);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
class DbLlmGenerationEventStore {
|
|
360
|
+
timeline;
|
|
361
|
+
constructor(timeline) {
|
|
362
|
+
this.timeline = timeline;
|
|
363
|
+
}
|
|
364
|
+
async append(event) {
|
|
365
|
+
await this.timeline.append(llmEventToTimeline(event));
|
|
366
|
+
}
|
|
367
|
+
async list(input = {}) {
|
|
368
|
+
const events = await this.timeline.listBySource("llm", timelineListInput(input));
|
|
369
|
+
return events.map((event) => event.payload);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
class DbMemoryStore {
|
|
373
|
+
db;
|
|
374
|
+
constructor(db) {
|
|
375
|
+
this.db = db;
|
|
376
|
+
}
|
|
377
|
+
async write(input) {
|
|
378
|
+
const identity = await this.db.resolveIdentity(input.sessionId);
|
|
379
|
+
const now = new Date().toISOString();
|
|
380
|
+
const record = {
|
|
381
|
+
id: randomUUID(),
|
|
382
|
+
text: input.text,
|
|
383
|
+
...(input.tags ? { tags: input.tags } : {}),
|
|
384
|
+
createdAt: now,
|
|
385
|
+
...(input.metadata ? { metadata: input.metadata } : {}),
|
|
386
|
+
};
|
|
387
|
+
await this.db.prisma.piAgentMemory.create({
|
|
388
|
+
data: {
|
|
389
|
+
id: record.id,
|
|
390
|
+
tenantId: identity.tenantId,
|
|
391
|
+
userId: identity.userId,
|
|
392
|
+
workspaceId: identity.workspaceId ?? null,
|
|
393
|
+
sessionId: input.sessionId,
|
|
394
|
+
scope: "session",
|
|
395
|
+
text: input.text,
|
|
396
|
+
tagsJson: jsonInput(input.tags ?? []),
|
|
397
|
+
metadataJson: jsonInput(input.metadata ?? {}),
|
|
398
|
+
createdAt: toDate(now),
|
|
399
|
+
updatedAt: toDate(now),
|
|
400
|
+
},
|
|
401
|
+
});
|
|
402
|
+
return record;
|
|
403
|
+
}
|
|
404
|
+
async search(input) {
|
|
405
|
+
const identity = await this.db.resolveIdentity(input.sessionId);
|
|
406
|
+
const query = input.query.trim();
|
|
407
|
+
const rows = await this.db.prisma.piAgentMemory.findMany({
|
|
408
|
+
where: {
|
|
409
|
+
tenantId: identity.tenantId,
|
|
410
|
+
sessionId: input.sessionId,
|
|
411
|
+
deletedAt: null,
|
|
412
|
+
...(query ? { text: { contains: query } } : {}),
|
|
413
|
+
},
|
|
414
|
+
orderBy: { createdAt: "desc" },
|
|
415
|
+
take: positiveLimit(input.limit),
|
|
416
|
+
});
|
|
417
|
+
return rows.map(memoryFromPrisma);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
class DbSubagentRunStore {
|
|
421
|
+
db;
|
|
422
|
+
constructor(db) {
|
|
423
|
+
this.db = db;
|
|
424
|
+
}
|
|
425
|
+
async create(input) {
|
|
426
|
+
const parentIdentity = await this.db.resolveIdentity(input.parentSessionId);
|
|
427
|
+
const identity = {
|
|
428
|
+
tenantId: input.tenantId,
|
|
429
|
+
userId: input.userId ?? parentIdentity.userId,
|
|
430
|
+
...(parentIdentity.workspaceId ? { workspaceId: parentIdentity.workspaceId } : {}),
|
|
431
|
+
};
|
|
432
|
+
const now = new Date().toISOString();
|
|
433
|
+
const runId = randomUUID();
|
|
434
|
+
const run = {
|
|
435
|
+
runId,
|
|
436
|
+
taskRunId: input.taskRunId ?? runId,
|
|
437
|
+
...(input.spawnBatchId ? { spawnBatchId: input.spawnBatchId } : {}),
|
|
438
|
+
...(input.traceId ? { traceId: input.traceId } : {}),
|
|
439
|
+
parentSessionId: input.parentSessionId,
|
|
440
|
+
childSessionId: input.childSessionId,
|
|
441
|
+
...(input.parentToolCallId ? { parentToolCallId: input.parentToolCallId } : {}),
|
|
442
|
+
task: input.task,
|
|
443
|
+
...(input.agent ? { agent: input.agent } : {}),
|
|
444
|
+
...(input.label ? { label: input.label } : {}),
|
|
445
|
+
status: "pending",
|
|
446
|
+
depth: input.depth,
|
|
447
|
+
model: input.model,
|
|
448
|
+
toolPolicyProfile: input.toolPolicyProfile,
|
|
449
|
+
context: input.context,
|
|
450
|
+
createdAt: now,
|
|
451
|
+
updatedAt: now,
|
|
452
|
+
events: [
|
|
453
|
+
{ type: "subagent_spawning", at: now },
|
|
454
|
+
{ type: "subagent_spawned", at: now },
|
|
455
|
+
],
|
|
456
|
+
};
|
|
457
|
+
await this.db.prisma.piAgentSubagentRun.create({
|
|
458
|
+
data: {
|
|
459
|
+
id: stableRowId("subagent", identity.tenantId, runId),
|
|
460
|
+
tenantId: identity.tenantId,
|
|
461
|
+
userId: identity.userId,
|
|
462
|
+
workspaceId: identity.workspaceId ?? null,
|
|
463
|
+
runId: run.runId,
|
|
464
|
+
taskRunId: run.taskRunId ?? null,
|
|
465
|
+
spawnBatchId: run.spawnBatchId ?? null,
|
|
466
|
+
traceId: run.traceId ?? null,
|
|
467
|
+
parentSessionId: run.parentSessionId,
|
|
468
|
+
childSessionId: run.childSessionId,
|
|
469
|
+
parentToolCallId: run.parentToolCallId ?? null,
|
|
470
|
+
agentName: run.agent ?? null,
|
|
471
|
+
label: run.label ?? null,
|
|
472
|
+
taskText: run.task,
|
|
473
|
+
status: run.status,
|
|
474
|
+
depth: run.depth,
|
|
475
|
+
modelJson: jsonInput(run.model),
|
|
476
|
+
toolPolicyProfile: run.toolPolicyProfile,
|
|
477
|
+
createdAt: toDate(run.createdAt),
|
|
478
|
+
updatedAt: toDate(run.updatedAt),
|
|
479
|
+
},
|
|
480
|
+
});
|
|
481
|
+
return run;
|
|
482
|
+
}
|
|
483
|
+
async list(scope, parentSessionId) {
|
|
484
|
+
const rows = await this.db.prisma.piAgentSubagentRun.findMany({
|
|
485
|
+
where: { ...sessionScopeWhere(scope), ...(parentSessionId ? { parentSessionId } : {}) },
|
|
486
|
+
orderBy: { createdAt: "asc" },
|
|
487
|
+
});
|
|
488
|
+
return rows.map(subagentFromPrisma);
|
|
489
|
+
}
|
|
490
|
+
async get(scope, runId) {
|
|
491
|
+
const row = await this.db.prisma.piAgentSubagentRun.findFirst({
|
|
492
|
+
where: { runId, ...sessionScopeWhere(scope) },
|
|
493
|
+
orderBy: { updatedAt: "desc" },
|
|
494
|
+
});
|
|
495
|
+
return row ? subagentFromPrisma(row) : undefined;
|
|
496
|
+
}
|
|
497
|
+
async getDepthForSession(scope, sessionId) {
|
|
498
|
+
const row = await this.db.prisma.piAgentSubagentRun.findFirst({
|
|
499
|
+
where: { childSessionId: sessionId, ...sessionScopeWhere(scope) },
|
|
500
|
+
orderBy: { updatedAt: "desc" },
|
|
501
|
+
select: { depth: true },
|
|
502
|
+
});
|
|
503
|
+
return row?.depth ?? 0;
|
|
504
|
+
}
|
|
505
|
+
async countActiveChildren(scope, parentSessionId) {
|
|
506
|
+
return await this.db.prisma.piAgentSubagentRun.count({
|
|
507
|
+
where: { parentSessionId, status: { in: ["pending", "running"] }, ...sessionScopeWhere(scope) },
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
async markRunning(scope, runId) {
|
|
511
|
+
return await this.patch(scope, runId, (run, now) => ({
|
|
512
|
+
...run,
|
|
513
|
+
status: "running",
|
|
514
|
+
startedAt: run.startedAt ?? now,
|
|
515
|
+
updatedAt: now,
|
|
516
|
+
events: [...run.events, { type: "subagent_started", at: now }],
|
|
517
|
+
}));
|
|
518
|
+
}
|
|
519
|
+
async markCompleted(scope, runId, result) {
|
|
520
|
+
return await this.patch(scope, runId, (run, now) => {
|
|
521
|
+
if (run.status === "cancelled") {
|
|
522
|
+
return run;
|
|
523
|
+
}
|
|
524
|
+
return {
|
|
525
|
+
...omitSubagentError(run),
|
|
526
|
+
status: "completed",
|
|
527
|
+
result,
|
|
528
|
+
endedAt: now,
|
|
529
|
+
updatedAt: now,
|
|
530
|
+
events: [...run.events, { type: "subagent_ended", at: now, reason: "completed" }],
|
|
531
|
+
};
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
async markFailed(scope, runId, error) {
|
|
535
|
+
return await this.patch(scope, runId, (run, now) => {
|
|
536
|
+
if (run.status === "cancelled") {
|
|
537
|
+
return run;
|
|
538
|
+
}
|
|
539
|
+
return {
|
|
540
|
+
...run,
|
|
541
|
+
status: "failed",
|
|
542
|
+
error,
|
|
543
|
+
endedAt: now,
|
|
544
|
+
updatedAt: now,
|
|
545
|
+
events: [...run.events, { type: "subagent_ended", at: now, reason: "failed" }],
|
|
546
|
+
};
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
async markCancelled(scope, runId, reason = "cancelled") {
|
|
550
|
+
return await this.patch(scope, runId, (run, now) => {
|
|
551
|
+
if (isTerminalSubagentStatus(run.status)) {
|
|
552
|
+
return run;
|
|
553
|
+
}
|
|
554
|
+
return {
|
|
555
|
+
...run,
|
|
556
|
+
status: "cancelled",
|
|
557
|
+
error: reason,
|
|
558
|
+
endedAt: now,
|
|
559
|
+
updatedAt: now,
|
|
560
|
+
events: [...run.events, { type: "subagent_ended", at: now, reason: "cancelled" }],
|
|
561
|
+
};
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
async patch(scope, runId, mutator) {
|
|
565
|
+
const existing = await this.get(scope, runId);
|
|
566
|
+
if (!existing) {
|
|
567
|
+
return undefined;
|
|
568
|
+
}
|
|
569
|
+
const next = mutator(existing, new Date().toISOString());
|
|
570
|
+
await this.db.prisma.piAgentSubagentRun.updateMany({
|
|
571
|
+
where: { runId: next.runId, ...sessionScopeWhere(scope) },
|
|
572
|
+
data: {
|
|
573
|
+
status: next.status,
|
|
574
|
+
resultText: next.result ?? null,
|
|
575
|
+
errorJson: next.error ? jsonInput({ message: next.error }) : Prisma.JsonNull,
|
|
576
|
+
startedAt: next.startedAt ? toDate(next.startedAt) : null,
|
|
577
|
+
endedAt: next.endedAt ? toDate(next.endedAt) : null,
|
|
578
|
+
updatedAt: toDate(next.updatedAt),
|
|
579
|
+
},
|
|
580
|
+
});
|
|
581
|
+
return next;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
class DbArtifactStore {
|
|
585
|
+
db;
|
|
586
|
+
constructor(db) {
|
|
587
|
+
this.db = db;
|
|
588
|
+
}
|
|
589
|
+
async create(input) {
|
|
590
|
+
const identity = await this.db.resolveIdentity(input.sessionId);
|
|
591
|
+
const artifact = {
|
|
592
|
+
id: input.id ?? randomUUID(),
|
|
593
|
+
tenantId: input.tenantId ?? identity.tenantId,
|
|
594
|
+
userId: input.userId ?? identity.userId,
|
|
595
|
+
...(input.workspaceId ?? identity.workspaceId ? { workspaceId: input.workspaceId ?? identity.workspaceId } : {}),
|
|
596
|
+
sessionId: input.sessionId,
|
|
597
|
+
...(input.turnId ? { turnId: input.turnId } : {}),
|
|
598
|
+
...(input.toolCallId ? { toolCallId: input.toolCallId } : {}),
|
|
599
|
+
artifactType: input.artifactType,
|
|
600
|
+
...(input.name ? { name: input.name } : {}),
|
|
601
|
+
...(input.mimeType ? { mimeType: input.mimeType } : {}),
|
|
602
|
+
...(input.sizeBytes !== undefined ? { sizeBytes: input.sizeBytes } : {}),
|
|
603
|
+
...(input.sha256 ? { sha256: input.sha256 } : {}),
|
|
604
|
+
storageUri: input.storageUri,
|
|
605
|
+
...(input.previewUri ? { previewUri: input.previewUri } : {}),
|
|
606
|
+
...(input.metadata ? { metadata: input.metadata } : {}),
|
|
607
|
+
createdAt: input.createdAt ?? new Date().toISOString(),
|
|
608
|
+
};
|
|
609
|
+
await this.db.prisma.piAgentArtifact.create({
|
|
610
|
+
data: {
|
|
611
|
+
id: artifact.id,
|
|
612
|
+
tenantId: artifact.tenantId ?? "default",
|
|
613
|
+
userId: artifact.userId ?? "system",
|
|
614
|
+
workspaceId: artifact.workspaceId ?? null,
|
|
615
|
+
sessionId: artifact.sessionId,
|
|
616
|
+
turnId: artifact.turnId ?? null,
|
|
617
|
+
toolCallId: artifact.toolCallId ?? null,
|
|
618
|
+
artifactType: artifact.artifactType,
|
|
619
|
+
name: artifact.name ?? null,
|
|
620
|
+
mimeType: artifact.mimeType ?? null,
|
|
621
|
+
sizeBytes: artifact.sizeBytes !== undefined ? BigInt(artifact.sizeBytes) : null,
|
|
622
|
+
sha256: artifact.sha256 ?? null,
|
|
623
|
+
storageUri: artifact.storageUri,
|
|
624
|
+
previewUri: artifact.previewUri ?? null,
|
|
625
|
+
metadataJson: jsonInput(artifact.metadata ?? {}),
|
|
626
|
+
createdAt: toDate(artifact.createdAt),
|
|
627
|
+
},
|
|
628
|
+
});
|
|
629
|
+
return artifact;
|
|
630
|
+
}
|
|
631
|
+
async get(scope, id) {
|
|
632
|
+
const row = await this.db.prisma.piAgentArtifact.findFirst({
|
|
633
|
+
where: { id, deletedAt: null, ...artifactScopeWhere(scope) },
|
|
634
|
+
});
|
|
635
|
+
return row ? artifactFromPrisma(row) : undefined;
|
|
636
|
+
}
|
|
637
|
+
async list(input) {
|
|
638
|
+
const rows = await this.db.prisma.piAgentArtifact.findMany({
|
|
639
|
+
where: {
|
|
640
|
+
deletedAt: null,
|
|
641
|
+
...artifactScopeWhere(input),
|
|
642
|
+
...(input.sessionId ? { sessionId: input.sessionId } : {}),
|
|
643
|
+
...(input.turnId ? { turnId: input.turnId } : {}),
|
|
644
|
+
...(input.toolCallId ? { toolCallId: input.toolCallId } : {}),
|
|
645
|
+
},
|
|
646
|
+
orderBy: { createdAt: "desc" },
|
|
647
|
+
take: positiveLimit(input.limit),
|
|
648
|
+
});
|
|
649
|
+
return rows.map(artifactFromPrisma).reverse();
|
|
650
|
+
}
|
|
651
|
+
async delete(scope, id) {
|
|
652
|
+
const result = await this.db.prisma.piAgentArtifact.updateMany({
|
|
653
|
+
where: { id, deletedAt: null, ...artifactScopeWhere(scope) },
|
|
654
|
+
data: { deletedAt: new Date() },
|
|
655
|
+
});
|
|
656
|
+
return result.count > 0;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
async function nextBigIntSeq(aggregatePromise, key) {
|
|
660
|
+
const aggregate = await aggregatePromise;
|
|
661
|
+
return (aggregate._max?.[key] ?? 0n) + 1n;
|
|
662
|
+
}
|
|
663
|
+
function messageCreateInput(identity, turn, id, role, text, messageSeq) {
|
|
664
|
+
return {
|
|
665
|
+
id,
|
|
666
|
+
tenantId: identity.tenantId,
|
|
667
|
+
userId: identity.userId,
|
|
668
|
+
workspaceId: identity.workspaceId ?? null,
|
|
669
|
+
sessionId: turn.sessionId,
|
|
670
|
+
conversationId: turn.conversationId,
|
|
671
|
+
turnId: turn.id,
|
|
672
|
+
role,
|
|
673
|
+
contentText: text,
|
|
674
|
+
modelProvider: turn.model.provider,
|
|
675
|
+
modelName: turn.model.model,
|
|
676
|
+
messageSeq,
|
|
677
|
+
createdAt: toDate(turn.createdAt),
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
function timelineWhere(input) {
|
|
681
|
+
const clauses = [];
|
|
682
|
+
if (input.tenantId)
|
|
683
|
+
clauses.push({ tenantId: input.tenantId });
|
|
684
|
+
if (input.sessionId) {
|
|
685
|
+
clauses.push({
|
|
686
|
+
OR: [
|
|
687
|
+
{ sessionId: input.sessionId },
|
|
688
|
+
{ payloadJson: { path: "$.parentSessionId", equals: input.sessionId } },
|
|
689
|
+
{ payloadJson: { path: "$.childSessionId", equals: input.sessionId } },
|
|
690
|
+
],
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
if (input.traceId)
|
|
694
|
+
clauses.push({ traceId: input.traceId });
|
|
695
|
+
if (input.afterSeq !== undefined || input.beforeSeq !== undefined) {
|
|
696
|
+
clauses.push({
|
|
697
|
+
eventSeq: {
|
|
698
|
+
...(input.afterSeq !== undefined ? { gt: BigInt(input.afterSeq) } : {}),
|
|
699
|
+
...(input.beforeSeq !== undefined ? { lt: BigInt(input.beforeSeq) } : {}),
|
|
700
|
+
},
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
if (input.cursor) {
|
|
704
|
+
clauses.push({
|
|
705
|
+
OR: [
|
|
706
|
+
{ createdAt: { lt: toDate(input.cursor.createdAt) } },
|
|
707
|
+
{ createdAt: toDate(input.cursor.createdAt), id: { lt: input.cursor.eventId } },
|
|
708
|
+
],
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
return clauses.length > 0 ? { AND: clauses } : {};
|
|
712
|
+
}
|
|
713
|
+
function sessionScopeWhere(scope) {
|
|
714
|
+
return {
|
|
715
|
+
tenantId: scope.tenantId,
|
|
716
|
+
...(scope.userId ? { userId: scope.userId } : {}),
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
function artifactScopeWhere(scope) {
|
|
720
|
+
return {
|
|
721
|
+
tenantId: scope.tenantId,
|
|
722
|
+
...(scope.userId ? { userId: scope.userId } : {}),
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
function sessionFromPrisma(row) {
|
|
726
|
+
const metadata = parseJsonObject(row.metadataJson);
|
|
727
|
+
const metadataModel = isJsonObject(metadata.model) ? metadata.model : {};
|
|
728
|
+
const metadataSession = isJsonObject(metadata.session) ? metadata.session : {};
|
|
729
|
+
const piSessionFilesByModel = parseStringRecord(metadataSession.piSessionFilesByModel);
|
|
730
|
+
return {
|
|
731
|
+
sessionId: row.sessionId,
|
|
732
|
+
conversationId: row.conversationId,
|
|
733
|
+
tenantId: row.tenantId,
|
|
734
|
+
userId: row.userId,
|
|
735
|
+
...(row.workspaceId ? { workspaceId: row.workspaceId } : {}),
|
|
736
|
+
...(row.parentSessionId ? { parentSessionId: row.parentSessionId } : {}),
|
|
737
|
+
...(row.childSessionId ? { childSessionId: row.childSessionId } : {}),
|
|
738
|
+
...(row.runId ? { runId: row.runId } : {}),
|
|
739
|
+
...(row.spawnBatchId ? { spawnBatchId: row.spawnBatchId } : {}),
|
|
740
|
+
...(row.taskRunId ? { taskRunId: row.taskRunId } : {}),
|
|
741
|
+
...(row.sandboxSessionId ? { sandboxSessionId: row.sandboxSessionId } : {}),
|
|
742
|
+
sandboxStatus: row.sandboxStatus,
|
|
743
|
+
model: {
|
|
744
|
+
provider: row.modelProvider,
|
|
745
|
+
model: row.modelName,
|
|
746
|
+
...(row.thinkingLevel ? { thinkingLevel: row.thinkingLevel } : {}),
|
|
747
|
+
...(typeof metadataModel.authProfileId === "string" ? { authProfileId: metadataModel.authProfileId } : {}),
|
|
748
|
+
...(metadataModel.reasoning === true ? { reasoning: true } : {}),
|
|
749
|
+
},
|
|
750
|
+
...(row.piSessionRef ? { piSessionFile: row.piSessionRef } : {}),
|
|
751
|
+
...(Object.keys(piSessionFilesByModel).length > 0 ? { piSessionFilesByModel } : {}),
|
|
752
|
+
toolPolicyProfile: row.toolPolicyProfile,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
function summaryFromPrismaSession(session, row) {
|
|
756
|
+
return {
|
|
757
|
+
...session,
|
|
758
|
+
turnCount: Number(row?.turnCount ?? 0),
|
|
759
|
+
...(row?.title ? { title: row.title } : {}),
|
|
760
|
+
...(row ? { updatedAt: toIso(row.updatedAt) } : {}),
|
|
761
|
+
...(row?.firstUserMessage ? { firstUserMessage: row.firstUserMessage } : {}),
|
|
762
|
+
...(row?.lastUserMessage ? { lastUserMessage: row.lastUserMessage } : {}),
|
|
763
|
+
...(row?.lastAssistantMessage ? { lastAssistantMessage: row.lastAssistantMessage } : {}),
|
|
764
|
+
...(row?.lastMessageAt ? { lastMessageAt: toIso(row.lastMessageAt) } : {}),
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
function turnFromPrisma(row) {
|
|
768
|
+
return {
|
|
769
|
+
id: row.id,
|
|
770
|
+
sessionId: row.sessionId,
|
|
771
|
+
conversationId: row.conversationId,
|
|
772
|
+
userMessage: row.inputText ?? "",
|
|
773
|
+
assistantMessage: row.outputText ?? "",
|
|
774
|
+
model: parseModel(row.modelJson),
|
|
775
|
+
...(row.traceId ? { traceId: row.traceId } : {}),
|
|
776
|
+
createdAt: toIso(row.createdAt),
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
function messageFromPrisma(row, traceIdsByTurnId = new Map()) {
|
|
780
|
+
const metadata = parseJsonObject(row.contentJson);
|
|
781
|
+
const model = row.modelProvider && row.modelName
|
|
782
|
+
? { provider: row.modelProvider, model: row.modelName }
|
|
783
|
+
: undefined;
|
|
784
|
+
const traceId = row.turnId ? traceIdsByTurnId.get(row.turnId) : undefined;
|
|
785
|
+
return {
|
|
786
|
+
id: row.id,
|
|
787
|
+
sessionId: row.sessionId,
|
|
788
|
+
conversationId: row.conversationId,
|
|
789
|
+
...(row.turnId ? { turnId: row.turnId } : {}),
|
|
790
|
+
role: row.role,
|
|
791
|
+
text: row.contentText ?? "",
|
|
792
|
+
...(model ? { model } : {}),
|
|
793
|
+
...(traceId ? { traceId } : {}),
|
|
794
|
+
createdAt: toIso(row.createdAt),
|
|
795
|
+
...(Object.keys(metadata).length > 0 ? { metadata } : {}),
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
function timelineEventFromPrisma(row) {
|
|
799
|
+
const payload = parseJsonValue(row.payloadJson);
|
|
800
|
+
const payloadObject = isJsonObject(payload) ? payload : {};
|
|
801
|
+
return {
|
|
802
|
+
eventId: row.id,
|
|
803
|
+
eventSeq: Number(row.eventSeq),
|
|
804
|
+
eventName: row.eventName,
|
|
805
|
+
eventType: row.eventType,
|
|
806
|
+
eventSource: row.eventSource,
|
|
807
|
+
sessionId: row.sessionId,
|
|
808
|
+
...(row.turnId ? { turnId: row.turnId } : {}),
|
|
809
|
+
...(row.traceId ? { traceId: row.traceId } : {}),
|
|
810
|
+
...(typeof payloadObject.conversationId === "string" ? { conversationId: payloadObject.conversationId } : {}),
|
|
811
|
+
...(typeof payloadObject.toolCallId === "string" ? { toolCallId: payloadObject.toolCallId } : {}),
|
|
812
|
+
...(typeof payloadObject.parentSessionId === "string" ? { parentSessionId: payloadObject.parentSessionId } : {}),
|
|
813
|
+
...(typeof payloadObject.childSessionId === "string" ? { childSessionId: payloadObject.childSessionId } : {}),
|
|
814
|
+
...(typeof payloadObject.runId === "string" ? { runId: payloadObject.runId } : {}),
|
|
815
|
+
...(typeof payloadObject.spawnBatchId === "string" ? { spawnBatchId: payloadObject.spawnBatchId } : {}),
|
|
816
|
+
...(typeof payloadObject.taskRunId === "string" ? { taskRunId: payloadObject.taskRunId } : {}),
|
|
817
|
+
createdAt: toIso(row.createdAt),
|
|
818
|
+
payload,
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
function memoryFromPrisma(row) {
|
|
822
|
+
const tags = parseStringArray(row.tagsJson);
|
|
823
|
+
const metadata = parseJsonObject(row.metadataJson);
|
|
824
|
+
return {
|
|
825
|
+
id: row.id,
|
|
826
|
+
text: row.text,
|
|
827
|
+
...(tags.length > 0 ? { tags } : {}),
|
|
828
|
+
createdAt: toIso(row.createdAt),
|
|
829
|
+
...(Object.keys(metadata).length > 0 ? { metadata } : {}),
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
function subagentFromPrisma(row) {
|
|
833
|
+
const error = parseJsonObject(row.errorJson);
|
|
834
|
+
return {
|
|
835
|
+
runId: row.runId,
|
|
836
|
+
taskRunId: row.taskRunId ?? row.runId,
|
|
837
|
+
...(row.spawnBatchId ? { spawnBatchId: row.spawnBatchId } : {}),
|
|
838
|
+
...(row.traceId ? { traceId: row.traceId } : {}),
|
|
839
|
+
parentSessionId: row.parentSessionId,
|
|
840
|
+
childSessionId: row.childSessionId,
|
|
841
|
+
...(row.parentToolCallId ? { parentToolCallId: row.parentToolCallId } : {}),
|
|
842
|
+
task: row.taskText,
|
|
843
|
+
...(row.agentName ? { agent: row.agentName } : {}),
|
|
844
|
+
...(row.label ? { label: row.label } : {}),
|
|
845
|
+
status: row.status,
|
|
846
|
+
depth: row.depth,
|
|
847
|
+
model: parseModel(row.modelJson),
|
|
848
|
+
toolPolicyProfile: row.toolPolicyProfile ?? "default",
|
|
849
|
+
context: "isolated",
|
|
850
|
+
createdAt: toIso(row.createdAt),
|
|
851
|
+
updatedAt: toIso(row.updatedAt),
|
|
852
|
+
...(row.startedAt ? { startedAt: toIso(row.startedAt) } : {}),
|
|
853
|
+
...(row.endedAt ? { endedAt: toIso(row.endedAt) } : {}),
|
|
854
|
+
...(row.resultText ? { result: row.resultText } : {}),
|
|
855
|
+
...(typeof error.message === "string" ? { error: error.message } : {}),
|
|
856
|
+
events: subagentLifecycleEvents(row.status, row.createdAt, row.startedAt, row.endedAt),
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
function artifactFromPrisma(row) {
|
|
860
|
+
const metadata = parseJsonObject(row.metadataJson);
|
|
861
|
+
return {
|
|
862
|
+
id: row.id,
|
|
863
|
+
tenantId: row.tenantId,
|
|
864
|
+
userId: row.userId,
|
|
865
|
+
...(row.workspaceId ? { workspaceId: row.workspaceId } : {}),
|
|
866
|
+
sessionId: row.sessionId,
|
|
867
|
+
...(row.turnId ? { turnId: row.turnId } : {}),
|
|
868
|
+
...(row.toolCallId ? { toolCallId: row.toolCallId } : {}),
|
|
869
|
+
artifactType: row.artifactType,
|
|
870
|
+
...(row.name ? { name: row.name } : {}),
|
|
871
|
+
...(row.mimeType ? { mimeType: row.mimeType } : {}),
|
|
872
|
+
...(row.sizeBytes !== null && row.sizeBytes !== undefined ? { sizeBytes: Number(row.sizeBytes) } : {}),
|
|
873
|
+
...(row.sha256 ? { sha256: row.sha256 } : {}),
|
|
874
|
+
storageUri: row.storageUri,
|
|
875
|
+
...(row.previewUri ? { previewUri: row.previewUri } : {}),
|
|
876
|
+
...(Object.keys(metadata).length > 0 ? { metadata } : {}),
|
|
877
|
+
createdAt: toIso(row.createdAt),
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
function subagentLifecycleEvents(status, createdAt, startedAt, endedAt) {
|
|
881
|
+
const created = toIso(createdAt);
|
|
882
|
+
const events = [
|
|
883
|
+
{ type: "subagent_spawning", at: created },
|
|
884
|
+
{ type: "subagent_spawned", at: created },
|
|
885
|
+
];
|
|
886
|
+
if (startedAt) {
|
|
887
|
+
events.push({ type: "subagent_started", at: toIso(startedAt) });
|
|
888
|
+
}
|
|
889
|
+
if (endedAt) {
|
|
890
|
+
const reason = isTerminalSubagentStatus(status) ? status : "completed";
|
|
891
|
+
events.push({ type: "subagent_ended", at: toIso(endedAt), reason });
|
|
892
|
+
}
|
|
893
|
+
return events;
|
|
894
|
+
}
|
|
895
|
+
function runtimeEventToTimeline(event) {
|
|
896
|
+
return {
|
|
897
|
+
eventId: event.id,
|
|
898
|
+
eventName: "runtime_event",
|
|
899
|
+
eventType: event.type,
|
|
900
|
+
eventSource: "runtime",
|
|
901
|
+
sessionId: event.sessionId,
|
|
902
|
+
...(event.conversationId ? { conversationId: event.conversationId } : {}),
|
|
903
|
+
...(event.traceId ? { traceId: event.traceId } : {}),
|
|
904
|
+
...(event.parentSessionId ? { parentSessionId: event.parentSessionId } : {}),
|
|
905
|
+
...(event.childSessionId ? { childSessionId: event.childSessionId } : {}),
|
|
906
|
+
...(event.runId ? { runId: event.runId } : {}),
|
|
907
|
+
...(event.spawnBatchId ? { spawnBatchId: event.spawnBatchId } : {}),
|
|
908
|
+
...(event.taskRunId ? { taskRunId: event.taskRunId } : {}),
|
|
909
|
+
createdAt: event.createdAt,
|
|
910
|
+
payload: event,
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
function toolEventToTimeline(event) {
|
|
914
|
+
return {
|
|
915
|
+
eventId: event.id,
|
|
916
|
+
eventName: `tool_call_${event.status}`,
|
|
917
|
+
eventType: event.status,
|
|
918
|
+
eventSource: "tool",
|
|
919
|
+
sessionId: event.sessionId,
|
|
920
|
+
conversationId: event.conversationId,
|
|
921
|
+
...(event.traceId ? { traceId: event.traceId } : {}),
|
|
922
|
+
toolCallId: event.toolCallId,
|
|
923
|
+
...(event.parentSessionId ? { parentSessionId: event.parentSessionId } : {}),
|
|
924
|
+
...(event.childSessionId ? { childSessionId: event.childSessionId } : {}),
|
|
925
|
+
...(event.runId ? { runId: event.runId } : {}),
|
|
926
|
+
...(event.spawnBatchId ? { spawnBatchId: event.spawnBatchId } : {}),
|
|
927
|
+
...(event.taskRunId ? { taskRunId: event.taskRunId } : {}),
|
|
928
|
+
createdAt: event.createdAt,
|
|
929
|
+
payload: event,
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
function llmEventToTimeline(event) {
|
|
933
|
+
return {
|
|
934
|
+
eventId: event.id,
|
|
935
|
+
eventName: `llm_generation_${event.status}`,
|
|
936
|
+
eventType: event.status,
|
|
937
|
+
eventSource: "llm",
|
|
938
|
+
sessionId: event.sessionId,
|
|
939
|
+
conversationId: event.conversationId,
|
|
940
|
+
...(event.traceId ? { traceId: event.traceId } : {}),
|
|
941
|
+
...(event.parentSessionId ? { parentSessionId: event.parentSessionId } : {}),
|
|
942
|
+
...(event.childSessionId ? { childSessionId: event.childSessionId } : {}),
|
|
943
|
+
...(event.runId ? { runId: event.runId } : {}),
|
|
944
|
+
...(event.spawnBatchId ? { spawnBatchId: event.spawnBatchId } : {}),
|
|
945
|
+
...(event.taskRunId ? { taskRunId: event.taskRunId } : {}),
|
|
946
|
+
createdAt: event.createdAt,
|
|
947
|
+
payload: event,
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
function timelineListInput(input) {
|
|
951
|
+
return {
|
|
952
|
+
...(input.sessionId ? { sessionId: input.sessionId } : {}),
|
|
953
|
+
...(input.traceId ? { traceId: input.traceId } : {}),
|
|
954
|
+
...(input.limit !== undefined ? { limit: input.limit } : {}),
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
function runtimeListInput(input) {
|
|
958
|
+
return {
|
|
959
|
+
...(input.sessionId ? { sessionId: input.sessionId } : {}),
|
|
960
|
+
...(input.traceId ? { traceId: input.traceId } : {}),
|
|
961
|
+
...(input.type ? { eventType: input.type } : {}),
|
|
962
|
+
...(input.limit !== undefined ? { limit: input.limit } : {}),
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
function parseModel(value) {
|
|
966
|
+
const model = parseJsonObject(value);
|
|
967
|
+
return {
|
|
968
|
+
provider: typeof model.provider === "string" ? model.provider : "unknown",
|
|
969
|
+
model: typeof model.model === "string" ? model.model : "unknown",
|
|
970
|
+
...(isThinkingLevel(model.thinkingLevel) ? { thinkingLevel: model.thinkingLevel } : {}),
|
|
971
|
+
...(typeof model.authProfileId === "string" ? { authProfileId: model.authProfileId } : {}),
|
|
972
|
+
...(model.reasoning === true ? { reasoning: true } : {}),
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
function isThinkingLevel(value) {
|
|
976
|
+
return (value === "off" ||
|
|
977
|
+
value === "minimal" ||
|
|
978
|
+
value === "low" ||
|
|
979
|
+
value === "medium" ||
|
|
980
|
+
value === "high" ||
|
|
981
|
+
value === "xhigh");
|
|
982
|
+
}
|
|
983
|
+
function parseJsonObject(value) {
|
|
984
|
+
const parsed = parseJsonValue(value);
|
|
985
|
+
return isJsonObject(parsed) ? parsed : {};
|
|
986
|
+
}
|
|
987
|
+
function parseJsonValue(value) {
|
|
988
|
+
if (typeof value === "string") {
|
|
989
|
+
try {
|
|
990
|
+
return JSON.parse(value);
|
|
991
|
+
}
|
|
992
|
+
catch {
|
|
993
|
+
return value;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return isJsonValue(value) ? value : null;
|
|
997
|
+
}
|
|
998
|
+
function parseStringArray(value) {
|
|
999
|
+
const parsed = parseJsonValue(value);
|
|
1000
|
+
return Array.isArray(parsed) ? parsed.filter((entry) => typeof entry === "string") : [];
|
|
1001
|
+
}
|
|
1002
|
+
function parseStringRecord(value) {
|
|
1003
|
+
const parsed = parseJsonValue(value);
|
|
1004
|
+
if (!isJsonObject(parsed)) {
|
|
1005
|
+
return {};
|
|
1006
|
+
}
|
|
1007
|
+
return Object.fromEntries(Object.entries(parsed).filter((entry) => typeof entry[1] === "string"));
|
|
1008
|
+
}
|
|
1009
|
+
function isJsonObject(value) {
|
|
1010
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1011
|
+
}
|
|
1012
|
+
function isJsonValue(value) {
|
|
1013
|
+
if (value === null) {
|
|
1014
|
+
return true;
|
|
1015
|
+
}
|
|
1016
|
+
const type = typeof value;
|
|
1017
|
+
if (type === "string" || type === "number" || type === "boolean") {
|
|
1018
|
+
return true;
|
|
1019
|
+
}
|
|
1020
|
+
if (Array.isArray(value)) {
|
|
1021
|
+
return value.every(isJsonValue);
|
|
1022
|
+
}
|
|
1023
|
+
if (isJsonObject(value)) {
|
|
1024
|
+
return Object.values(value).every((entry) => entry === undefined || isJsonValue(entry));
|
|
1025
|
+
}
|
|
1026
|
+
return false;
|
|
1027
|
+
}
|
|
1028
|
+
function jsonInput(value) {
|
|
1029
|
+
return JSON.parse(JSON.stringify(value ?? null));
|
|
1030
|
+
}
|
|
1031
|
+
function toDate(value) {
|
|
1032
|
+
return value instanceof Date ? value : new Date(value);
|
|
1033
|
+
}
|
|
1034
|
+
function toIso(value) {
|
|
1035
|
+
return value instanceof Date ? value.toISOString() : value;
|
|
1036
|
+
}
|
|
1037
|
+
function firstLine(value) {
|
|
1038
|
+
return value.trim().split(/\r?\n/, 1)[0]?.slice(0, 120) ?? "";
|
|
1039
|
+
}
|
|
1040
|
+
function stableRowId(prefix, ...parts) {
|
|
1041
|
+
const hash = createHash("sha256").update(parts.join("\0")).digest("hex").slice(0, 48);
|
|
1042
|
+
return `${prefix}_${hash}`.slice(0, 64);
|
|
1043
|
+
}
|
|
1044
|
+
function positiveLimit(limit) {
|
|
1045
|
+
return limit && limit > 0 ? limit : 200;
|
|
1046
|
+
}
|
|
1047
|
+
function omitSubagentError(run) {
|
|
1048
|
+
const { error: _error, ...rest } = run;
|
|
1049
|
+
return rest;
|
|
1050
|
+
}
|
|
1051
|
+
//# sourceMappingURL=db-runtime-storage.js.map
|