@amaster.ai/pi-storage 0.1.0-beta.0 → 0.1.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.
Files changed (72) hide show
  1. package/README.md +1 -1
  2. package/dist/artifact-stores.d.ts +1 -1
  3. package/dist/artifact-stores.d.ts.map +1 -1
  4. package/dist/artifact-stores.js +2 -2
  5. package/dist/artifact-stores.js.map +1 -1
  6. package/dist/db-migrations.js +24 -24
  7. package/dist/db-migrations.js.map +1 -1
  8. package/dist/db-runtime-storage.d.ts +1 -1
  9. package/dist/db-runtime-storage.d.ts.map +1 -1
  10. package/dist/db-runtime-storage.js +175 -123
  11. package/dist/db-runtime-storage.js.map +1 -1
  12. package/dist/db.d.ts +3 -2
  13. package/dist/db.d.ts.map +1 -1
  14. package/dist/db.js +3 -2
  15. package/dist/db.js.map +1 -1
  16. package/dist/event-stores.d.ts +1 -1
  17. package/dist/event-stores.d.ts.map +1 -1
  18. package/dist/event-stores.js +22 -5
  19. package/dist/event-stores.js.map +1 -1
  20. package/dist/index.d.ts +1 -2
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +1 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/internal.d.ts +3 -3
  25. package/dist/internal.js +3 -3
  26. package/dist/json-file.d.ts +7 -0
  27. package/dist/json-file.d.ts.map +1 -1
  28. package/dist/json-file.js +6 -6
  29. package/dist/json-file.js.map +1 -1
  30. package/dist/json.d.ts +5 -4
  31. package/dist/json.d.ts.map +1 -1
  32. package/dist/json.js +5 -4
  33. package/dist/json.js.map +1 -1
  34. package/dist/migration-main.js +4 -4
  35. package/dist/redis-locks.js +7 -7
  36. package/dist/runtime-scope.d.ts +1 -1
  37. package/dist/runtime-scope.d.ts.map +1 -1
  38. package/dist/runtime-storage-json.d.ts +18 -0
  39. package/dist/runtime-storage-json.d.ts.map +1 -0
  40. package/dist/runtime-storage-json.js +19 -0
  41. package/dist/runtime-storage-json.js.map +1 -0
  42. package/dist/{scheduled-task-stores.d.ts → scheduler-db.d.ts} +2 -24
  43. package/dist/scheduler-db.d.ts.map +1 -0
  44. package/dist/{scheduled-task-stores.js → scheduler-db.js} +32 -171
  45. package/dist/scheduler-db.js.map +1 -0
  46. package/dist/scheduler-json.d.ts +24 -0
  47. package/dist/scheduler-json.d.ts.map +1 -0
  48. package/dist/scheduler-json.js +134 -0
  49. package/dist/scheduler-json.js.map +1 -0
  50. package/dist/session-stores.d.ts +2 -1
  51. package/dist/session-stores.d.ts.map +1 -1
  52. package/dist/session-stores.js +41 -24
  53. package/dist/session-stores.js.map +1 -1
  54. package/dist/session-workspaces.d.ts +22 -0
  55. package/dist/session-workspaces.d.ts.map +1 -0
  56. package/dist/session-workspaces.js +75 -0
  57. package/dist/session-workspaces.js.map +1 -0
  58. package/dist/subagent-store.d.ts +2 -2
  59. package/dist/subagent-store.d.ts.map +1 -1
  60. package/dist/subagent-store.js +18 -18
  61. package/dist/subagent-store.js.map +1 -1
  62. package/package.json +21 -8
  63. package/dist/runtime-storage.d.ts +0 -30
  64. package/dist/runtime-storage.d.ts.map +0 -1
  65. package/dist/runtime-storage.js +0 -60
  66. package/dist/runtime-storage.js.map +0 -1
  67. package/dist/scheduled-task-stores.d.ts.map +0 -1
  68. package/dist/scheduled-task-stores.js.map +0 -1
  69. package/dist/scheduler.d.ts +0 -2
  70. package/dist/scheduler.d.ts.map +0 -1
  71. package/dist/scheduler.js +0 -2
  72. package/dist/scheduler.js.map +0 -1
@@ -5,23 +5,23 @@
5
5
  * packages/storage/prisma/schema.prisma. JSON mode remains the local developer
6
6
  * adapter; DB mode uses these stores directly and never falls back silently.
7
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";
8
+ import { createHash, randomUUID } from 'node:crypto';
9
+ import { Prisma, PrismaClient } from '@prisma/client';
10
+ import { RedisLockManager } from './redis-locks.js';
11
+ import { isTerminalSubagentStatus } from './subagent-store.js';
12
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",
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
25
  ];
26
26
  export function createDbRuntimeStores(databaseUrl, redisUrl) {
27
27
  const context = new DbRuntimeContext(databaseUrl, redisUrl);
@@ -48,8 +48,8 @@ export async function verifyDbRuntimeSchema(databaseUrl) {
48
48
  const found = new Set(rows.map((row) => row.table_name));
49
49
  const missing = REQUIRED_DB_TABLES.filter((table) => !found.has(table));
50
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.");
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
53
  }
54
54
  }
55
55
  finally {
@@ -70,18 +70,18 @@ class DbRuntimeContext {
70
70
  async resolveIdentity(sessionId, tx = this.prisma) {
71
71
  const row = await tx.piAgentSession.findFirst({
72
72
  where: { sessionId: { in: identityLookupSessionIds(sessionId) }, deletedAt: null },
73
- orderBy: { updatedAt: "desc" },
73
+ orderBy: { updatedAt: 'desc' },
74
74
  select: { tenantId: true, userId: true, workspaceId: true },
75
75
  });
76
76
  return {
77
- tenantId: row?.tenantId ?? "default",
78
- userId: row?.userId ?? "system",
77
+ tenantId: row?.tenantId ?? 'default',
78
+ userId: row?.userId ?? 'system',
79
79
  ...(row?.workspaceId ? { workspaceId: row.workspaceId } : {}),
80
80
  };
81
81
  }
82
82
  }
83
83
  function identityLookupSessionIds(sessionId) {
84
- const subagentMarker = ":subagent:";
84
+ const subagentMarker = ':subagent:';
85
85
  const markerIndex = sessionId.indexOf(subagentMarker);
86
86
  if (markerIndex <= 0) {
87
87
  return [sessionId];
@@ -96,15 +96,15 @@ class DbRuntimeSessionStore {
96
96
  async getRuntimeSession(scope, sessionId) {
97
97
  const row = await this.db.prisma.piAgentSession.findFirst({
98
98
  where: { sessionId, deletedAt: null, ...sessionScopeWhere(scope) },
99
- orderBy: { updatedAt: "desc" },
99
+ orderBy: { updatedAt: 'desc' },
100
100
  });
101
101
  return row ? sessionFromPrisma(row) : undefined;
102
102
  }
103
103
  async saveRuntimeSession(session) {
104
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);
105
+ const tenantId = session.tenantId ?? 'default';
106
+ const userId = session.userId ?? 'system';
107
+ const rowId = stableRowId('session', tenantId, session.sessionId);
108
108
  await this.db.prisma.piAgentSession.upsert({
109
109
  where: { id: rowId },
110
110
  create: {
@@ -119,8 +119,8 @@ class DbRuntimeSessionStore {
119
119
  taskRunId: session.taskRunId ?? null,
120
120
  runId: session.runId ?? null,
121
121
  spawnBatchId: session.spawnBatchId ?? null,
122
- triggerType: "user",
123
- status: "active",
122
+ triggerType: 'user',
123
+ status: 'active',
124
124
  modelProvider: session.model.provider,
125
125
  modelName: session.model.model,
126
126
  thinkingLevel: session.model.thinkingLevel ?? null,
@@ -159,7 +159,7 @@ class DbRuntimeSessionStore {
159
159
  async listRuntimeSessions(scope) {
160
160
  const rows = await this.db.prisma.piAgentSession.findMany({
161
161
  where: { deletedAt: null, ...sessionScopeWhere(scope) },
162
- orderBy: [{ lastMessageAt: "desc" }, { updatedAt: "desc" }],
162
+ orderBy: [{ lastMessageAt: 'desc' }, { updatedAt: 'desc' }],
163
163
  });
164
164
  return rows.map(sessionFromPrisma);
165
165
  }
@@ -181,11 +181,11 @@ class DbTranscriptStore {
181
181
  nextBigIntSeq(tx.piAgentTurn.aggregate({
182
182
  where: { tenantId: identity.tenantId, sessionId: turn.sessionId },
183
183
  _max: { turnSeq: true },
184
- }), "turnSeq"),
184
+ }), 'turnSeq'),
185
185
  nextBigIntSeq(tx.piAgentMessage.aggregate({
186
186
  where: { tenantId: identity.tenantId, sessionId: turn.sessionId },
187
187
  _max: { messageSeq: true },
188
- }), "messageSeq"),
188
+ }), 'messageSeq'),
189
189
  ]);
190
190
  await tx.piAgentTurn.create({
191
191
  data: {
@@ -197,8 +197,8 @@ class DbTranscriptStore {
197
197
  conversationId: turn.conversationId,
198
198
  traceId: turn.traceId ?? null,
199
199
  turnSeq,
200
- sourceType: "user",
201
- status: "completed",
200
+ sourceType: 'user',
201
+ status: 'completed',
202
202
  inputText: turn.userMessage,
203
203
  outputText: turn.assistantMessage,
204
204
  modelJson: jsonInput(turn.model),
@@ -209,8 +209,8 @@ class DbTranscriptStore {
209
209
  });
210
210
  await tx.piAgentMessage.createMany({
211
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),
212
+ messageCreateInput(identity, turn, `${turn.id}:user`, 'user', turn.userMessage, messageSeq),
213
+ messageCreateInput(identity, turn, `${turn.id}:assistant`, 'assistant', turn.assistantMessage, messageSeq + 1n),
214
214
  ],
215
215
  });
216
216
  await tx.piAgentSession.updateMany({
@@ -233,7 +233,7 @@ class DbTranscriptStore {
233
233
  async listTurns(scope, sessionId) {
234
234
  const rows = await this.db.prisma.piAgentTurn.findMany({
235
235
  where: { ...(sessionId ? { sessionId } : {}), ...sessionScopeWhere(scope) },
236
- orderBy: [{ createdAt: "asc" }, { turnSeq: "asc" }],
236
+ orderBy: [{ createdAt: 'asc' }, { turnSeq: 'asc' }],
237
237
  });
238
238
  return rows.map(turnFromPrisma);
239
239
  }
@@ -241,16 +241,14 @@ class DbTranscriptStore {
241
241
  const [rows, turns] = await Promise.all([
242
242
  this.db.prisma.piAgentMessage.findMany({
243
243
  where: { sessionId, deletedAt: null, ...sessionScopeWhere(scope) },
244
- orderBy: { messageSeq: "asc" },
244
+ orderBy: { messageSeq: 'asc' },
245
245
  }),
246
246
  this.db.prisma.piAgentTurn.findMany({
247
247
  where: { sessionId, ...sessionScopeWhere(scope) },
248
248
  select: { id: true, traceId: true },
249
249
  }),
250
250
  ]);
251
- const traceIdsByTurnId = new Map(turns
252
- .filter((turn) => Boolean(turn.traceId))
253
- .map((turn) => [turn.id, turn.traceId]));
251
+ const traceIdsByTurnId = new Map(turns.filter((turn) => Boolean(turn.traceId)).map((turn) => [turn.id, turn.traceId]));
254
252
  return rows.map((row) => messageFromPrisma(row, traceIdsByTurnId));
255
253
  }
256
254
  async listSessionSummaries(scope, sessions) {
@@ -264,6 +262,12 @@ class DbTranscriptStore {
264
262
  const bySessionId = new Map(rows.map((row) => [row.sessionId, row]));
265
263
  return sessions.map((session) => summaryFromPrismaSession(session, bySessionId.get(session.sessionId)));
266
264
  }
265
+ async updateSessionTitle(scope, sessionId, title) {
266
+ await this.db.prisma.piAgentSession.updateMany({
267
+ where: { sessionId, deletedAt: null, ...sessionScopeWhere(scope) },
268
+ data: { title, updatedAt: new Date() },
269
+ });
270
+ }
267
271
  }
268
272
  class DbRuntimeTimelineEventStore {
269
273
  db;
@@ -278,7 +282,10 @@ class DbRuntimeTimelineEventStore {
278
282
  timeoutMs: 5_000,
279
283
  task: async () => {
280
284
  await this.db.prisma.$transaction(async (tx) => {
281
- const existing = await tx.piAgentEvent.findUnique({ where: { id: event.eventId }, select: { id: true } });
285
+ const existing = await tx.piAgentEvent.findUnique({
286
+ where: { id: event.eventId },
287
+ select: { id: true },
288
+ });
282
289
  if (existing) {
283
290
  return;
284
291
  }
@@ -299,7 +306,7 @@ class DbRuntimeTimelineEventStore {
299
306
  eventSource: event.eventSource,
300
307
  eventType: event.eventType,
301
308
  eventName: event.eventName,
302
- severity: "info",
309
+ severity: 'info',
303
310
  payloadJson: jsonInput(event.payload),
304
311
  createdAt: toDate(event.createdAt),
305
312
  },
@@ -310,13 +317,23 @@ class DbRuntimeTimelineEventStore {
310
317
  }
311
318
  async list(input) {
312
319
  const limit = positiveLimit(input.limit);
320
+ const cursorEventSeq = input.cursor
321
+ ? await this.findCursorEventSeq(input.cursor.eventId)
322
+ : undefined;
313
323
  const rows = await this.db.prisma.piAgentEvent.findMany({
314
- where: timelineWhere(input),
315
- orderBy: [{ createdAt: "desc" }, { id: "desc" }],
324
+ where: timelineWhere(input, cursorEventSeq),
325
+ orderBy: [{ createdAt: 'desc' }, { eventSeq: 'desc' }, { id: 'desc' }],
316
326
  take: limit,
317
327
  });
318
328
  return rows.map(timelineEventFromPrisma).reverse();
319
329
  }
330
+ async findCursorEventSeq(eventId) {
331
+ const row = await this.db.prisma.piAgentEvent.findUnique({
332
+ where: { id: eventId },
333
+ select: { eventSeq: true },
334
+ });
335
+ return row?.eventSeq;
336
+ }
320
337
  async listBySource(eventSource, input = {}) {
321
338
  const rows = await this.db.prisma.piAgentEvent.findMany({
322
339
  where: {
@@ -324,7 +341,7 @@ class DbRuntimeTimelineEventStore {
324
341
  ...timelineWhere(input),
325
342
  ...(input.eventType ? { eventType: input.eventType } : {}),
326
343
  },
327
- orderBy: [{ eventSeq: "desc" }, { createdAt: "desc" }],
344
+ orderBy: [{ createdAt: 'desc' }, { eventSeq: 'desc' }, { id: 'desc' }],
328
345
  take: positiveLimit(input.limit),
329
346
  });
330
347
  return rows.map(timelineEventFromPrisma).reverse();
@@ -339,7 +356,7 @@ class DbRuntimeEventStore {
339
356
  await this.timeline.append(runtimeEventToTimeline(event));
340
357
  }
341
358
  async list(input = {}) {
342
- const events = await this.timeline.listBySource("runtime", runtimeListInput(input));
359
+ const events = await this.timeline.listBySource('runtime', runtimeListInput(input));
343
360
  return events.map((event) => event.payload);
344
361
  }
345
362
  }
@@ -352,7 +369,7 @@ class DbToolEventStore {
352
369
  await this.timeline.append(toolEventToTimeline(event));
353
370
  }
354
371
  async list(input = {}) {
355
- const events = await this.timeline.listBySource("tool", timelineListInput(input));
372
+ const events = await this.timeline.listBySource('tool', timelineListInput(input));
356
373
  return events.map((event) => event.payload);
357
374
  }
358
375
  }
@@ -365,7 +382,7 @@ class DbLlmGenerationEventStore {
365
382
  await this.timeline.append(llmEventToTimeline(event));
366
383
  }
367
384
  async list(input = {}) {
368
- const events = await this.timeline.listBySource("llm", timelineListInput(input));
385
+ const events = await this.timeline.listBySource('llm', timelineListInput(input));
369
386
  return events.map((event) => event.payload);
370
387
  }
371
388
  }
@@ -391,7 +408,7 @@ class DbMemoryStore {
391
408
  userId: identity.userId,
392
409
  workspaceId: identity.workspaceId ?? null,
393
410
  sessionId: input.sessionId,
394
- scope: "session",
411
+ scope: 'session',
395
412
  text: input.text,
396
413
  tagsJson: jsonInput(input.tags ?? []),
397
414
  metadataJson: jsonInput(input.metadata ?? {}),
@@ -411,7 +428,7 @@ class DbMemoryStore {
411
428
  deletedAt: null,
412
429
  ...(query ? { text: { contains: query } } : {}),
413
430
  },
414
- orderBy: { createdAt: "desc" },
431
+ orderBy: { createdAt: 'desc' },
415
432
  take: positiveLimit(input.limit),
416
433
  });
417
434
  return rows.map(memoryFromPrisma);
@@ -442,7 +459,7 @@ class DbSubagentRunStore {
442
459
  task: input.task,
443
460
  ...(input.agent ? { agent: input.agent } : {}),
444
461
  ...(input.label ? { label: input.label } : {}),
445
- status: "pending",
462
+ status: 'pending',
446
463
  depth: input.depth,
447
464
  model: input.model,
448
465
  toolPolicyProfile: input.toolPolicyProfile,
@@ -450,13 +467,13 @@ class DbSubagentRunStore {
450
467
  createdAt: now,
451
468
  updatedAt: now,
452
469
  events: [
453
- { type: "subagent_spawning", at: now },
454
- { type: "subagent_spawned", at: now },
470
+ { type: 'subagent_spawning', at: now },
471
+ { type: 'subagent_spawned', at: now },
455
472
  ],
456
473
  };
457
474
  await this.db.prisma.piAgentSubagentRun.create({
458
475
  data: {
459
- id: stableRowId("subagent", identity.tenantId, runId),
476
+ id: stableRowId('subagent', identity.tenantId, runId),
460
477
  tenantId: identity.tenantId,
461
478
  userId: identity.userId,
462
479
  workspaceId: identity.workspaceId ?? null,
@@ -483,81 +500,85 @@ class DbSubagentRunStore {
483
500
  async list(scope, parentSessionId) {
484
501
  const rows = await this.db.prisma.piAgentSubagentRun.findMany({
485
502
  where: { ...sessionScopeWhere(scope), ...(parentSessionId ? { parentSessionId } : {}) },
486
- orderBy: { createdAt: "asc" },
503
+ orderBy: { createdAt: 'asc' },
487
504
  });
488
505
  return rows.map(subagentFromPrisma);
489
506
  }
490
507
  async get(scope, runId) {
491
508
  const row = await this.db.prisma.piAgentSubagentRun.findFirst({
492
509
  where: { runId, ...sessionScopeWhere(scope) },
493
- orderBy: { updatedAt: "desc" },
510
+ orderBy: { updatedAt: 'desc' },
494
511
  });
495
512
  return row ? subagentFromPrisma(row) : undefined;
496
513
  }
497
514
  async getDepthForSession(scope, sessionId) {
498
515
  const row = await this.db.prisma.piAgentSubagentRun.findFirst({
499
516
  where: { childSessionId: sessionId, ...sessionScopeWhere(scope) },
500
- orderBy: { updatedAt: "desc" },
517
+ orderBy: { updatedAt: 'desc' },
501
518
  select: { depth: true },
502
519
  });
503
520
  return row?.depth ?? 0;
504
521
  }
505
522
  async countActiveChildren(scope, parentSessionId) {
506
523
  return await this.db.prisma.piAgentSubagentRun.count({
507
- where: { parentSessionId, status: { in: ["pending", "running"] }, ...sessionScopeWhere(scope) },
524
+ where: {
525
+ parentSessionId,
526
+ status: { in: ['pending', 'running'] },
527
+ ...sessionScopeWhere(scope),
528
+ },
508
529
  });
509
530
  }
510
531
  async markRunning(scope, runId) {
511
532
  return await this.patch(scope, runId, (run, now) => ({
512
533
  ...run,
513
- status: "running",
534
+ status: 'running',
514
535
  startedAt: run.startedAt ?? now,
515
536
  updatedAt: now,
516
- events: [...run.events, { type: "subagent_started", at: now }],
537
+ events: [...run.events, { type: 'subagent_started', at: now }],
517
538
  }));
518
539
  }
519
540
  async markCompleted(scope, runId, result) {
520
541
  return await this.patch(scope, runId, (run, now) => {
521
- if (run.status === "cancelled") {
542
+ if (run.status === 'cancelled') {
522
543
  return run;
523
544
  }
524
545
  return {
525
546
  ...omitSubagentError(run),
526
- status: "completed",
547
+ status: 'completed',
527
548
  result,
528
549
  endedAt: now,
529
550
  updatedAt: now,
530
- events: [...run.events, { type: "subagent_ended", at: now, reason: "completed" }],
551
+ events: [...run.events, { type: 'subagent_ended', at: now, reason: 'completed' }],
531
552
  };
532
553
  });
533
554
  }
534
555
  async markFailed(scope, runId, error) {
535
556
  return await this.patch(scope, runId, (run, now) => {
536
- if (run.status === "cancelled") {
557
+ if (run.status === 'cancelled') {
537
558
  return run;
538
559
  }
539
560
  return {
540
561
  ...run,
541
- status: "failed",
562
+ status: 'failed',
542
563
  error,
543
564
  endedAt: now,
544
565
  updatedAt: now,
545
- events: [...run.events, { type: "subagent_ended", at: now, reason: "failed" }],
566
+ events: [...run.events, { type: 'subagent_ended', at: now, reason: 'failed' }],
546
567
  };
547
568
  });
548
569
  }
549
- async markCancelled(scope, runId, reason = "cancelled") {
570
+ async markCancelled(scope, runId, reason = 'cancelled') {
550
571
  return await this.patch(scope, runId, (run, now) => {
551
572
  if (isTerminalSubagentStatus(run.status)) {
552
573
  return run;
553
574
  }
554
575
  return {
555
576
  ...run,
556
- status: "cancelled",
577
+ status: 'cancelled',
557
578
  error: reason,
558
579
  endedAt: now,
559
580
  updatedAt: now,
560
- events: [...run.events, { type: "subagent_ended", at: now, reason: "cancelled" }],
581
+ events: [...run.events, { type: 'subagent_ended', at: now, reason: 'cancelled' }],
561
582
  };
562
583
  });
563
584
  }
@@ -592,7 +613,9 @@ class DbArtifactStore {
592
613
  id: input.id ?? randomUUID(),
593
614
  tenantId: input.tenantId ?? identity.tenantId,
594
615
  userId: input.userId ?? identity.userId,
595
- ...(input.workspaceId ?? identity.workspaceId ? { workspaceId: input.workspaceId ?? identity.workspaceId } : {}),
616
+ ...((input.workspaceId ?? identity.workspaceId)
617
+ ? { workspaceId: input.workspaceId ?? identity.workspaceId }
618
+ : {}),
596
619
  sessionId: input.sessionId,
597
620
  ...(input.turnId ? { turnId: input.turnId } : {}),
598
621
  ...(input.toolCallId ? { toolCallId: input.toolCallId } : {}),
@@ -609,8 +632,8 @@ class DbArtifactStore {
609
632
  await this.db.prisma.piAgentArtifact.create({
610
633
  data: {
611
634
  id: artifact.id,
612
- tenantId: artifact.tenantId ?? "default",
613
- userId: artifact.userId ?? "system",
635
+ tenantId: artifact.tenantId ?? 'default',
636
+ userId: artifact.userId ?? 'system',
614
637
  workspaceId: artifact.workspaceId ?? null,
615
638
  sessionId: artifact.sessionId,
616
639
  turnId: artifact.turnId ?? null,
@@ -643,7 +666,7 @@ class DbArtifactStore {
643
666
  ...(input.turnId ? { turnId: input.turnId } : {}),
644
667
  ...(input.toolCallId ? { toolCallId: input.toolCallId } : {}),
645
668
  },
646
- orderBy: { createdAt: "desc" },
669
+ orderBy: { createdAt: 'desc' },
647
670
  take: positiveLimit(input.limit),
648
671
  });
649
672
  return rows.map(artifactFromPrisma).reverse();
@@ -657,7 +680,7 @@ class DbArtifactStore {
657
680
  }
658
681
  }
659
682
  async function nextBigIntSeq(aggregatePromise, key) {
660
- const aggregate = await aggregatePromise;
683
+ const aggregate = (await aggregatePromise);
661
684
  return (aggregate._max?.[key] ?? 0n) + 1n;
662
685
  }
663
686
  function messageCreateInput(identity, turn, id, role, text, messageSeq) {
@@ -677,7 +700,7 @@ function messageCreateInput(identity, turn, id, role, text, messageSeq) {
677
700
  createdAt: toDate(turn.createdAt),
678
701
  };
679
702
  }
680
- function timelineWhere(input) {
703
+ function timelineWhere(input, cursorEventSeq) {
681
704
  const clauses = [];
682
705
  if (input.tenantId)
683
706
  clauses.push({ tenantId: input.tenantId });
@@ -685,8 +708,8 @@ function timelineWhere(input) {
685
708
  clauses.push({
686
709
  OR: [
687
710
  { sessionId: input.sessionId },
688
- { payloadJson: { path: "$.parentSessionId", equals: input.sessionId } },
689
- { payloadJson: { path: "$.childSessionId", equals: input.sessionId } },
711
+ { payloadJson: { path: '$.parentSessionId', equals: input.sessionId } },
712
+ { payloadJson: { path: '$.childSessionId', equals: input.sessionId } },
690
713
  ],
691
714
  });
692
715
  }
@@ -701,12 +724,25 @@ function timelineWhere(input) {
701
724
  });
702
725
  }
703
726
  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
- });
727
+ const cursorCreatedAt = toDate(input.cursor.createdAt);
728
+ clauses.push(cursorEventSeq === undefined
729
+ ? {
730
+ OR: [
731
+ { createdAt: { lt: cursorCreatedAt } },
732
+ { createdAt: cursorCreatedAt, id: { lt: input.cursor.eventId } },
733
+ ],
734
+ }
735
+ : {
736
+ OR: [
737
+ { createdAt: { lt: cursorCreatedAt } },
738
+ { createdAt: cursorCreatedAt, eventSeq: { lt: cursorEventSeq } },
739
+ {
740
+ createdAt: cursorCreatedAt,
741
+ eventSeq: cursorEventSeq,
742
+ id: { lt: input.cursor.eventId },
743
+ },
744
+ ],
745
+ });
710
746
  }
711
747
  return clauses.length > 0 ? { AND: clauses } : {};
712
748
  }
@@ -744,7 +780,9 @@ function sessionFromPrisma(row) {
744
780
  provider: row.modelProvider,
745
781
  model: row.modelName,
746
782
  ...(row.thinkingLevel ? { thinkingLevel: row.thinkingLevel } : {}),
747
- ...(typeof metadataModel.authProfileId === "string" ? { authProfileId: metadataModel.authProfileId } : {}),
783
+ ...(typeof metadataModel.authProfileId === 'string'
784
+ ? { authProfileId: metadataModel.authProfileId }
785
+ : {}),
748
786
  ...(metadataModel.reasoning === true ? { reasoning: true } : {}),
749
787
  },
750
788
  ...(row.piSessionRef ? { piSessionFile: row.piSessionRef } : {}),
@@ -769,8 +807,8 @@ function turnFromPrisma(row) {
769
807
  id: row.id,
770
808
  sessionId: row.sessionId,
771
809
  conversationId: row.conversationId,
772
- userMessage: row.inputText ?? "",
773
- assistantMessage: row.outputText ?? "",
810
+ userMessage: row.inputText ?? '',
811
+ assistantMessage: row.outputText ?? '',
774
812
  model: parseModel(row.modelJson),
775
813
  ...(row.traceId ? { traceId: row.traceId } : {}),
776
814
  createdAt: toIso(row.createdAt),
@@ -788,7 +826,7 @@ function messageFromPrisma(row, traceIdsByTurnId = new Map()) {
788
826
  conversationId: row.conversationId,
789
827
  ...(row.turnId ? { turnId: row.turnId } : {}),
790
828
  role: row.role,
791
- text: row.contentText ?? "",
829
+ text: row.contentText ?? '',
792
830
  ...(model ? { model } : {}),
793
831
  ...(traceId ? { traceId } : {}),
794
832
  createdAt: toIso(row.createdAt),
@@ -807,13 +845,23 @@ function timelineEventFromPrisma(row) {
807
845
  sessionId: row.sessionId,
808
846
  ...(row.turnId ? { turnId: row.turnId } : {}),
809
847
  ...(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 } : {}),
848
+ ...(typeof payloadObject.conversationId === 'string'
849
+ ? { conversationId: payloadObject.conversationId }
850
+ : {}),
851
+ ...(typeof payloadObject.toolCallId === 'string'
852
+ ? { toolCallId: payloadObject.toolCallId }
853
+ : {}),
854
+ ...(typeof payloadObject.parentSessionId === 'string'
855
+ ? { parentSessionId: payloadObject.parentSessionId }
856
+ : {}),
857
+ ...(typeof payloadObject.childSessionId === 'string'
858
+ ? { childSessionId: payloadObject.childSessionId }
859
+ : {}),
860
+ ...(typeof payloadObject.runId === 'string' ? { runId: payloadObject.runId } : {}),
861
+ ...(typeof payloadObject.spawnBatchId === 'string'
862
+ ? { spawnBatchId: payloadObject.spawnBatchId }
863
+ : {}),
864
+ ...(typeof payloadObject.taskRunId === 'string' ? { taskRunId: payloadObject.taskRunId } : {}),
817
865
  createdAt: toIso(row.createdAt),
818
866
  payload,
819
867
  };
@@ -845,14 +893,14 @@ function subagentFromPrisma(row) {
845
893
  status: row.status,
846
894
  depth: row.depth,
847
895
  model: parseModel(row.modelJson),
848
- toolPolicyProfile: row.toolPolicyProfile ?? "default",
849
- context: "isolated",
896
+ toolPolicyProfile: row.toolPolicyProfile ?? 'default',
897
+ context: 'isolated',
850
898
  createdAt: toIso(row.createdAt),
851
899
  updatedAt: toIso(row.updatedAt),
852
900
  ...(row.startedAt ? { startedAt: toIso(row.startedAt) } : {}),
853
901
  ...(row.endedAt ? { endedAt: toIso(row.endedAt) } : {}),
854
902
  ...(row.resultText ? { result: row.resultText } : {}),
855
- ...(typeof error.message === "string" ? { error: error.message } : {}),
903
+ ...(typeof error.message === 'string' ? { error: error.message } : {}),
856
904
  events: subagentLifecycleEvents(row.status, row.createdAt, row.startedAt, row.endedAt),
857
905
  };
858
906
  }
@@ -869,7 +917,9 @@ function artifactFromPrisma(row) {
869
917
  artifactType: row.artifactType,
870
918
  ...(row.name ? { name: row.name } : {}),
871
919
  ...(row.mimeType ? { mimeType: row.mimeType } : {}),
872
- ...(row.sizeBytes !== null && row.sizeBytes !== undefined ? { sizeBytes: Number(row.sizeBytes) } : {}),
920
+ ...(row.sizeBytes !== null && row.sizeBytes !== undefined
921
+ ? { sizeBytes: Number(row.sizeBytes) }
922
+ : {}),
873
923
  ...(row.sha256 ? { sha256: row.sha256 } : {}),
874
924
  storageUri: row.storageUri,
875
925
  ...(row.previewUri ? { previewUri: row.previewUri } : {}),
@@ -880,24 +930,24 @@ function artifactFromPrisma(row) {
880
930
  function subagentLifecycleEvents(status, createdAt, startedAt, endedAt) {
881
931
  const created = toIso(createdAt);
882
932
  const events = [
883
- { type: "subagent_spawning", at: created },
884
- { type: "subagent_spawned", at: created },
933
+ { type: 'subagent_spawning', at: created },
934
+ { type: 'subagent_spawned', at: created },
885
935
  ];
886
936
  if (startedAt) {
887
- events.push({ type: "subagent_started", at: toIso(startedAt) });
937
+ events.push({ type: 'subagent_started', at: toIso(startedAt) });
888
938
  }
889
939
  if (endedAt) {
890
- const reason = isTerminalSubagentStatus(status) ? status : "completed";
891
- events.push({ type: "subagent_ended", at: toIso(endedAt), reason });
940
+ const reason = isTerminalSubagentStatus(status) ? status : 'completed';
941
+ events.push({ type: 'subagent_ended', at: toIso(endedAt), reason });
892
942
  }
893
943
  return events;
894
944
  }
895
945
  function runtimeEventToTimeline(event) {
896
946
  return {
897
947
  eventId: event.id,
898
- eventName: "runtime_event",
948
+ eventName: 'runtime_event',
899
949
  eventType: event.type,
900
- eventSource: "runtime",
950
+ eventSource: 'runtime',
901
951
  sessionId: event.sessionId,
902
952
  ...(event.conversationId ? { conversationId: event.conversationId } : {}),
903
953
  ...(event.traceId ? { traceId: event.traceId } : {}),
@@ -915,7 +965,7 @@ function toolEventToTimeline(event) {
915
965
  eventId: event.id,
916
966
  eventName: `tool_call_${event.status}`,
917
967
  eventType: event.status,
918
- eventSource: "tool",
968
+ eventSource: 'tool',
919
969
  sessionId: event.sessionId,
920
970
  conversationId: event.conversationId,
921
971
  ...(event.traceId ? { traceId: event.traceId } : {}),
@@ -934,7 +984,7 @@ function llmEventToTimeline(event) {
934
984
  eventId: event.id,
935
985
  eventName: `llm_generation_${event.status}`,
936
986
  eventType: event.status,
937
- eventSource: "llm",
987
+ eventSource: 'llm',
938
988
  sessionId: event.sessionId,
939
989
  conversationId: event.conversationId,
940
990
  ...(event.traceId ? { traceId: event.traceId } : {}),
@@ -965,27 +1015,27 @@ function runtimeListInput(input) {
965
1015
  function parseModel(value) {
966
1016
  const model = parseJsonObject(value);
967
1017
  return {
968
- provider: typeof model.provider === "string" ? model.provider : "unknown",
969
- model: typeof model.model === "string" ? model.model : "unknown",
1018
+ provider: typeof model.provider === 'string' ? model.provider : 'unknown',
1019
+ model: typeof model.model === 'string' ? model.model : 'unknown',
970
1020
  ...(isThinkingLevel(model.thinkingLevel) ? { thinkingLevel: model.thinkingLevel } : {}),
971
- ...(typeof model.authProfileId === "string" ? { authProfileId: model.authProfileId } : {}),
1021
+ ...(typeof model.authProfileId === 'string' ? { authProfileId: model.authProfileId } : {}),
972
1022
  ...(model.reasoning === true ? { reasoning: true } : {}),
973
1023
  };
974
1024
  }
975
1025
  function isThinkingLevel(value) {
976
- return (value === "off" ||
977
- value === "minimal" ||
978
- value === "low" ||
979
- value === "medium" ||
980
- value === "high" ||
981
- value === "xhigh");
1026
+ return (value === 'off' ||
1027
+ value === 'minimal' ||
1028
+ value === 'low' ||
1029
+ value === 'medium' ||
1030
+ value === 'high' ||
1031
+ value === 'xhigh');
982
1032
  }
983
1033
  function parseJsonObject(value) {
984
1034
  const parsed = parseJsonValue(value);
985
1035
  return isJsonObject(parsed) ? parsed : {};
986
1036
  }
987
1037
  function parseJsonValue(value) {
988
- if (typeof value === "string") {
1038
+ if (typeof value === 'string') {
989
1039
  try {
990
1040
  return JSON.parse(value);
991
1041
  }
@@ -997,24 +1047,26 @@ function parseJsonValue(value) {
997
1047
  }
998
1048
  function parseStringArray(value) {
999
1049
  const parsed = parseJsonValue(value);
1000
- return Array.isArray(parsed) ? parsed.filter((entry) => typeof entry === "string") : [];
1050
+ return Array.isArray(parsed)
1051
+ ? parsed.filter((entry) => typeof entry === 'string')
1052
+ : [];
1001
1053
  }
1002
1054
  function parseStringRecord(value) {
1003
1055
  const parsed = parseJsonValue(value);
1004
1056
  if (!isJsonObject(parsed)) {
1005
1057
  return {};
1006
1058
  }
1007
- return Object.fromEntries(Object.entries(parsed).filter((entry) => typeof entry[1] === "string"));
1059
+ return Object.fromEntries(Object.entries(parsed).filter((entry) => typeof entry[1] === 'string'));
1008
1060
  }
1009
1061
  function isJsonObject(value) {
1010
- return typeof value === "object" && value !== null && !Array.isArray(value);
1062
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
1011
1063
  }
1012
1064
  function isJsonValue(value) {
1013
1065
  if (value === null) {
1014
1066
  return true;
1015
1067
  }
1016
1068
  const type = typeof value;
1017
- if (type === "string" || type === "number" || type === "boolean") {
1069
+ if (type === 'string' || type === 'number' || type === 'boolean') {
1018
1070
  return true;
1019
1071
  }
1020
1072
  if (Array.isArray(value)) {
@@ -1035,10 +1087,10 @@ function toIso(value) {
1035
1087
  return value instanceof Date ? value.toISOString() : value;
1036
1088
  }
1037
1089
  function firstLine(value) {
1038
- return value.trim().split(/\r?\n/, 1)[0]?.slice(0, 120) ?? "";
1090
+ return value.trim().split(/\r?\n/, 1)[0]?.slice(0, 120) ?? '';
1039
1091
  }
1040
1092
  function stableRowId(prefix, ...parts) {
1041
- const hash = createHash("sha256").update(parts.join("\0")).digest("hex").slice(0, 48);
1093
+ const hash = createHash('sha256').update(parts.join('\0')).digest('hex').slice(0, 48);
1042
1094
  return `${prefix}_${hash}`.slice(0, 64);
1043
1095
  }
1044
1096
  function positiveLimit(limit) {