@anvia/studio 0.5.8 → 0.5.10

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/dist/index.js CHANGED
@@ -4,7 +4,6 @@ import {
4
4
  } from "@anvia/core/agent";
5
5
  import { Message } from "@anvia/core/completion";
6
6
  import { Agent } from "@anvia/core/internal/agent";
7
- import { resolveMemoryOptions } from "@anvia/core/memory";
8
7
  import { Pipeline } from "@anvia/core/pipeline";
9
8
  import { serve } from "@hono/node-server";
10
9
  import { Hono as HonoApp } from "hono";
@@ -695,7 +694,7 @@ function renderLegacyStudioUiShell(props) {
695
694
  '<meta charset="utf-8">',
696
695
  '<meta name="viewport" content="width=device-width, initial-scale=1">',
697
696
  `<title>${title}</title>`,
698
- '<script>(()=>{try{if((localStorage.getItem("anvia-studio-theme")??localStorage.getItem("aion-studio-theme"))==="light"){document.documentElement.classList.remove("dark")}}catch{}})();</script>',
697
+ '<script>(()=>{try{if(localStorage.getItem("anvia-studio-theme")==="light"){document.documentElement.classList.remove("dark")}}catch{}})();</script>',
699
698
  `<link rel="icon" type="image/png" href="${escapeHtml(props.compatUiPath)}/assets/logo.png">`,
700
699
  `<link rel="stylesheet" href="${escapeHtml(props.stylesheet)}">`,
701
700
  "</head>",
@@ -1146,1827 +1145,780 @@ function isJsonValue(value) {
1146
1145
  return isJsonObject(value) && Object.values(value).every(isJsonValue);
1147
1146
  }
1148
1147
 
1149
- // src/storage/sqlite-store.ts
1150
- import { mkdirSync } from "fs";
1151
- import { createRequire } from "module";
1152
- import { dirname, resolve } from "path";
1153
- var DatabaseSync;
1154
- function createSqliteSessionStore(options = {}) {
1155
- return new SqliteSessionStore(options.path ?? ":memory:");
1148
+ // src/runtime/tool-metadata.ts
1149
+ import { ToolSet } from "@anvia/core/tool";
1150
+ var MCP_TOOL_METADATA_KEY = /* @__PURE__ */ Symbol.for("anvia.mcp.tool.metadata");
1151
+ function agentToolItems(agent) {
1152
+ return [
1153
+ ...agent.agent.toolSet.values().map((tool) => ({ tool, source: "static" })),
1154
+ ...agent.agent.dynamicTools.flatMap((registration) => {
1155
+ const maybeToolSet = registration.index.toolSet;
1156
+ if (!(maybeToolSet instanceof ToolSet)) {
1157
+ return [];
1158
+ }
1159
+ return maybeToolSet.values().map((tool) => ({ tool, source: "dynamic" }));
1160
+ })
1161
+ ];
1156
1162
  }
1157
- var SqliteSessionStore = class {
1158
- constructor(path) {
1159
- this.path = path;
1163
+ function approvalMetadata(tool) {
1164
+ const approval = tool.approval;
1165
+ if (approval === void 0 || typeof approval !== "object" || approval === null) {
1166
+ return { required: false };
1160
1167
  }
1161
- path;
1162
- kind = "sqlite";
1163
- db;
1164
- listSessions(options) {
1165
- const db = this.database();
1166
- const agentClause = options.agentId === void 0 ? "" : "WHERE s.agent_id = $agentId";
1167
- const rows = db.prepare(
1168
- `SELECT s.id, s.agent_id, s.title, s.metadata_json, s.created_at, s.updated_at,
1169
- COUNT(m.message_index) AS message_count
1170
- FROM anvia_studio_sessions s
1171
- LEFT JOIN anvia_studio_session_messages m ON m.session_id = s.id
1172
- ${agentClause}
1173
- GROUP BY s.id, s.agent_id, s.title, s.metadata_json, s.created_at, s.updated_at
1174
- ORDER BY s.updated_at DESC
1175
- LIMIT $limit`
1176
- ).all({
1177
- $agentId: options.agentId ?? null,
1178
- $limit: options.limit
1179
- });
1180
- return rows.map(toSessionSummary);
1168
+ const policy = approval;
1169
+ return {
1170
+ required: true,
1171
+ ...typeof policy.reason === "string" ? { reason: policy.reason } : {},
1172
+ ...typeof policy.rejectMessage === "string" ? { rejectMessage: policy.rejectMessage } : {}
1173
+ };
1174
+ }
1175
+ function mcpServerName(tool) {
1176
+ const metadata = tool[MCP_TOOL_METADATA_KEY];
1177
+ if (typeof metadata !== "object" || metadata === null) {
1178
+ return void 0;
1181
1179
  }
1182
- createSession(input) {
1183
- const db = this.database();
1184
- const now = (/* @__PURE__ */ new Date()).toISOString();
1185
- db.prepare(
1186
- `INSERT INTO anvia_studio_sessions (
1187
- id,
1188
- agent_id,
1189
- title,
1190
- metadata_json,
1191
- created_at,
1192
- updated_at
1193
- ) VALUES ($id, $agentId, $title, $metadata, $now, $now)`
1194
- ).run({
1195
- $id: input.id,
1196
- $agentId: input.agentId,
1197
- $title: input.title ?? null,
1198
- $metadata: input.metadata === void 0 ? null : JSON.stringify(input.metadata),
1199
- $now: now
1200
- });
1201
- return {
1202
- id: input.id,
1203
- agentId: input.agentId,
1204
- ...input.title === void 0 ? {} : { title: input.title },
1205
- createdAt: now,
1206
- updatedAt: now,
1207
- messageCount: 0,
1208
- ...input.metadata === void 0 ? {} : { metadata: input.metadata }
1209
- };
1180
+ const serverName = metadata.serverName;
1181
+ return typeof serverName === "string" && serverName.length > 0 ? serverName : void 0;
1182
+ }
1183
+ function agentHasMcpTools(agent) {
1184
+ return agentToolItems(agent).some(({ tool }) => mcpServerName(tool) !== void 0);
1185
+ }
1186
+
1187
+ // src/runtime/shared.ts
1188
+ function resolveStores(options) {
1189
+ const defaultStore = defaultStudioStore();
1190
+ const sessions = resolveSessionStore(options, defaultStore);
1191
+ const traces = resolveTraceStore(options, sessions, defaultStore);
1192
+ const pipelineLogs = resolvePipelineLogStore(options, sessions, defaultStore);
1193
+ const pipelineRuns = resolvePipelineRunStore(options, sessions, pipelineLogs, defaultStore);
1194
+ return {
1195
+ ...sessions === void 0 ? {} : { sessions },
1196
+ ...traces === void 0 ? {} : { traces },
1197
+ ...pipelineLogs === void 0 ? {} : { pipelineLogs },
1198
+ ...pipelineRuns === void 0 ? {} : { pipelineRuns }
1199
+ };
1200
+ }
1201
+ function defaultStudioStore() {
1202
+ return createInMemoryStudioStore();
1203
+ }
1204
+ function resolveSessionStore(options, defaultStore) {
1205
+ if (options.stores?.sessions === false) {
1206
+ return void 0;
1210
1207
  }
1211
- getSession(id) {
1212
- const row = this.getSessionRow(id);
1213
- return row === void 0 ? void 0 : toSession(row, this.listSessionMessages(id), this.listSessionRunRows(id));
1208
+ if (options.stores?.sessions !== void 0) {
1209
+ return options.stores.sessions;
1214
1210
  }
1215
- load(context) {
1216
- const session = this.getSession(context.sessionId);
1217
- return Promise.resolve(session?.messages ?? []);
1211
+ return defaultStore;
1212
+ }
1213
+ function resolveTraceStore(options, sessionStore, defaultStore) {
1214
+ if (options.stores?.traces !== void 0) {
1215
+ return options.stores.traces;
1218
1216
  }
1219
- append(input) {
1220
- const db = this.database();
1221
- try {
1222
- db.exec("BEGIN IMMEDIATE");
1223
- const row = db.prepare(
1224
- `SELECT id, agent_id, title, metadata_json, created_at, updated_at
1225
- FROM anvia_studio_sessions
1226
- WHERE id = $id`
1227
- ).get({ $id: input.context.sessionId });
1228
- if (row === void 0) {
1229
- db.exec("ROLLBACK");
1230
- return Promise.resolve();
1231
- }
1232
- const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1233
- const nextIndex = this.nextMessageIndex(input.context.sessionId);
1234
- this.insertMessages(input.context.sessionId, input.messages, nextIndex, updatedAt);
1235
- db.prepare(
1236
- `UPDATE anvia_studio_sessions
1237
- SET updated_at = $updatedAt
1238
- WHERE id = $id`
1239
- ).run({
1240
- $id: input.context.sessionId,
1241
- $updatedAt: updatedAt
1242
- });
1243
- db.exec("COMMIT");
1244
- return Promise.resolve();
1245
- } catch (error) {
1246
- if (db.isTransaction) {
1247
- db.exec("ROLLBACK");
1248
- }
1249
- throw error;
1250
- }
1217
+ if (sessionStore === void 0) {
1218
+ return void 0;
1251
1219
  }
1252
- clear(context) {
1253
- const db = this.database();
1254
- const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1255
- try {
1256
- db.exec("BEGIN IMMEDIATE");
1257
- db.prepare(
1258
- `UPDATE anvia_studio_sessions
1259
- SET updated_at = $updatedAt
1260
- WHERE id = $id`
1261
- ).run({
1262
- $id: context.sessionId,
1263
- $updatedAt: updatedAt
1264
- });
1265
- db.prepare("DELETE FROM anvia_studio_session_messages WHERE session_id = $id").run({
1266
- $id: context.sessionId
1267
- });
1268
- db.prepare("DELETE FROM anvia_studio_session_runs WHERE session_id = $id").run({
1269
- $id: context.sessionId
1270
- });
1271
- db.exec("COMMIT");
1272
- return Promise.resolve();
1273
- } catch (error) {
1274
- if (db.isTransaction) {
1275
- db.exec("ROLLBACK");
1276
- }
1277
- throw error;
1278
- }
1220
+ if (isTraceStore(sessionStore)) {
1221
+ return sessionStore;
1279
1222
  }
1280
- async recordError(input) {
1281
- const runId = studioRunId2(input.context) ?? input.runId;
1282
- const existing = this.getSessionRun(input.context.sessionId, runId);
1283
- const transcript = existing === void 0 || parseJsonArray(existing.transcript_json).length === 0 ? transcriptFromMessages(input.messages) : parseJsonArray(existing.transcript_json);
1284
- await this.saveSessionRunTranscript({
1285
- id: input.context.sessionId,
1286
- runId,
1287
- transcript,
1288
- status: "error",
1289
- error: serializeJsonError2(input.error)
1290
- });
1223
+ return defaultStore;
1224
+ }
1225
+ function resolvePipelineLogStore(options, sessionStore, defaultStore) {
1226
+ if (options.stores?.pipelineLogs === false) {
1227
+ return void 0;
1291
1228
  }
1292
- saveSessionRunTranscript(input) {
1293
- const db = this.database();
1294
- const now = (/* @__PURE__ */ new Date()).toISOString();
1295
- try {
1296
- db.exec("BEGIN IMMEDIATE");
1297
- const row = this.getSessionRow(input.id);
1298
- if (row === void 0) {
1299
- db.exec("ROLLBACK");
1300
- return void 0;
1301
- }
1302
- const current = toSession(
1303
- row,
1304
- this.listSessionMessages(input.id),
1305
- this.listSessionRunRows(input.id)
1306
- );
1307
- const title = current.title ?? input.title;
1308
- db.prepare(
1309
- `INSERT INTO anvia_studio_session_runs (
1310
- run_id,
1311
- session_id,
1312
- status,
1313
- title,
1314
- transcript_json,
1315
- error_json,
1316
- created_at,
1317
- updated_at
1318
- ) VALUES (
1319
- $runId,
1320
- $sessionId,
1321
- $status,
1322
- $title,
1323
- $transcript,
1324
- $error,
1325
- $now,
1326
- $now
1327
- )
1328
- ON CONFLICT(run_id) DO UPDATE SET
1329
- status = excluded.status,
1330
- title = COALESCE(anvia_studio_session_runs.title, excluded.title),
1331
- transcript_json = excluded.transcript_json,
1332
- error_json = excluded.error_json,
1333
- updated_at = excluded.updated_at`
1334
- ).run({
1335
- $runId: input.runId,
1336
- $sessionId: input.id,
1337
- $status: input.status,
1338
- $title: input.title ?? null,
1339
- $transcript: JSON.stringify(renumberTranscript(input.transcript)),
1340
- $error: input.error === void 0 ? null : JSON.stringify(input.error),
1341
- $now: now
1342
- });
1343
- db.prepare(
1344
- `UPDATE anvia_studio_sessions
1345
- SET title = $title,
1346
- updated_at = $updatedAt
1347
- WHERE id = $id`
1348
- ).run({
1349
- $id: input.id,
1350
- $title: title ?? null,
1351
- $updatedAt: now
1352
- });
1353
- db.exec("COMMIT");
1354
- const updated = this.getSession(input.id);
1355
- return updated;
1356
- } catch (error) {
1357
- if (db.isTransaction) {
1358
- db.exec("ROLLBACK");
1359
- }
1360
- throw error;
1361
- }
1229
+ if (options.stores?.pipelineLogs !== void 0) {
1230
+ return options.stores.pipelineLogs;
1362
1231
  }
1363
- appendSessionLog(input) {
1364
- const db = this.database();
1365
- const now = (/* @__PURE__ */ new Date()).toISOString();
1366
- try {
1367
- db.exec("BEGIN IMMEDIATE");
1368
- const row = this.getSessionRow(input.sessionId);
1369
- if (row === void 0) {
1370
- throw new Error("Session not found");
1371
- }
1372
- const sequence = this.nextSessionLogSequence(input.sessionId);
1373
- const entry = {
1374
- id: globalThis.crypto.randomUUID(),
1375
- sessionId: input.sessionId,
1376
- ...input.runId === void 0 ? {} : { runId: input.runId },
1377
- sequence,
1378
- timestamp: now,
1379
- level: input.level,
1380
- category: input.category,
1381
- event: input.event,
1382
- message: input.message,
1383
- ...input.metadata === void 0 ? {} : { metadata: input.metadata }
1384
- };
1385
- db.prepare(
1386
- `INSERT INTO anvia_studio_session_logs (
1387
- id,
1388
- session_id,
1389
- run_id,
1390
- sequence,
1391
- timestamp,
1392
- level,
1393
- category,
1394
- event,
1395
- message,
1396
- metadata_json
1397
- ) VALUES (
1398
- $id,
1399
- $sessionId,
1400
- $runId,
1401
- $sequence,
1402
- $timestamp,
1403
- $level,
1404
- $category,
1405
- $event,
1406
- $message,
1407
- $metadata
1408
- )`
1409
- ).run({
1410
- $id: entry.id,
1411
- $sessionId: entry.sessionId,
1412
- $runId: entry.runId ?? null,
1413
- $sequence: entry.sequence,
1414
- $timestamp: entry.timestamp,
1415
- $level: entry.level,
1416
- $category: entry.category,
1417
- $event: entry.event,
1418
- $message: entry.message,
1419
- $metadata: entry.metadata === void 0 ? null : JSON.stringify(entry.metadata)
1420
- });
1421
- db.exec("COMMIT");
1422
- return entry;
1423
- } catch (error) {
1424
- if (db.isTransaction) {
1425
- db.exec("ROLLBACK");
1426
- }
1427
- throw error;
1428
- }
1232
+ if (sessionStore !== void 0 && isPipelineLogStore(sessionStore)) {
1233
+ return sessionStore;
1429
1234
  }
1430
- listSessionLogs(options) {
1431
- const db = this.database();
1432
- const afterClause = options.after === void 0 ? "" : "AND sequence > $after";
1433
- const rows = db.prepare(
1434
- `SELECT id, session_id, run_id, sequence, timestamp, level, category, event, message,
1435
- metadata_json
1436
- FROM anvia_studio_session_logs
1437
- WHERE session_id = $sessionId
1438
- ${afterClause}
1439
- ORDER BY sequence ASC
1440
- LIMIT $limit`
1441
- ).all({
1442
- $sessionId: options.sessionId,
1443
- $after: options.after ?? null,
1444
- $limit: options.limit
1445
- });
1446
- return rows.map(toSessionLog);
1235
+ return defaultStore;
1236
+ }
1237
+ function resolvePipelineRunStore(options, sessionStore, pipelineLogStore, defaultStore) {
1238
+ if (options.stores?.pipelineRuns === false) {
1239
+ return void 0;
1447
1240
  }
1448
- appendPipelineLog(input) {
1449
- const db = this.database();
1450
- const now = (/* @__PURE__ */ new Date()).toISOString();
1451
- try {
1452
- db.exec("BEGIN IMMEDIATE");
1453
- const sequence = this.nextPipelineLogSequence(input.pipelineId);
1454
- const entry = {
1455
- id: globalThis.crypto.randomUUID(),
1456
- pipelineId: input.pipelineId,
1457
- ...input.runId === void 0 ? {} : { runId: input.runId },
1458
- sequence,
1459
- timestamp: now,
1460
- level: input.level,
1461
- category: input.category,
1462
- event: input.event,
1463
- message: input.message,
1464
- ...input.metadata === void 0 ? {} : { metadata: input.metadata }
1465
- };
1466
- db.prepare(
1467
- `INSERT INTO anvia_studio_pipeline_logs (
1468
- id,
1469
- pipeline_id,
1470
- run_id,
1471
- sequence,
1472
- timestamp,
1473
- level,
1474
- category,
1475
- event,
1476
- message,
1477
- metadata_json
1478
- ) VALUES (
1479
- $id,
1480
- $pipelineId,
1481
- $runId,
1482
- $sequence,
1483
- $timestamp,
1484
- $level,
1485
- $category,
1486
- $event,
1487
- $message,
1488
- $metadata
1489
- )`
1490
- ).run({
1491
- $id: entry.id,
1492
- $pipelineId: entry.pipelineId,
1493
- $runId: entry.runId ?? null,
1494
- $sequence: entry.sequence,
1495
- $timestamp: entry.timestamp,
1496
- $level: entry.level,
1497
- $category: entry.category,
1498
- $event: entry.event,
1499
- $message: entry.message,
1500
- $metadata: entry.metadata === void 0 ? null : JSON.stringify(entry.metadata)
1501
- });
1502
- db.exec("COMMIT");
1503
- return entry;
1504
- } catch (error) {
1505
- if (db.isTransaction) {
1506
- db.exec("ROLLBACK");
1507
- }
1508
- throw error;
1509
- }
1241
+ if (options.stores?.pipelineRuns !== void 0) {
1242
+ return options.stores.pipelineRuns;
1510
1243
  }
1511
- listPipelineLogs(options) {
1512
- const db = this.database();
1513
- const afterClause = options.after === void 0 ? "" : "AND sequence > $after";
1514
- const rows = db.prepare(
1515
- `SELECT id, pipeline_id, run_id, sequence, timestamp, level, category, event, message,
1516
- metadata_json
1517
- FROM anvia_studio_pipeline_logs
1518
- WHERE pipeline_id = $pipelineId
1519
- ${afterClause}
1520
- ORDER BY sequence ASC
1521
- LIMIT $limit`
1522
- ).all({
1523
- $pipelineId: options.pipelineId,
1524
- $after: options.after ?? null,
1525
- $limit: options.limit
1526
- });
1527
- return rows.map(toPipelineLog);
1244
+ if (sessionStore !== void 0 && isPipelineRunStore(sessionStore)) {
1245
+ return sessionStore;
1528
1246
  }
1529
- savePipelineRun(input) {
1530
- const db = this.database();
1531
- db.prepare(
1532
- `INSERT INTO anvia_studio_pipeline_runs (
1533
- run_id,
1534
- pipeline_id,
1535
- status,
1536
- input_json,
1537
- output_json,
1538
- error_json,
1539
- metadata_json,
1540
- started_at,
1541
- ended_at,
1542
- duration_ms
1543
- ) VALUES (
1544
- $runId,
1545
- $pipelineId,
1546
- $status,
1547
- $input,
1548
- $output,
1549
- $error,
1550
- $metadata,
1551
- $startedAt,
1552
- $endedAt,
1553
- $durationMs
1554
- )
1555
- ON CONFLICT(run_id) DO UPDATE SET
1556
- pipeline_id = excluded.pipeline_id,
1557
- status = excluded.status,
1558
- input_json = excluded.input_json,
1559
- output_json = excluded.output_json,
1560
- error_json = excluded.error_json,
1561
- metadata_json = excluded.metadata_json,
1562
- started_at = excluded.started_at,
1563
- ended_at = excluded.ended_at,
1564
- duration_ms = excluded.duration_ms`
1565
- ).run({
1566
- $runId: input.runId,
1567
- $pipelineId: input.pipelineId,
1568
- $status: input.status,
1569
- $input: JSON.stringify(input.input),
1570
- $output: input.output === void 0 ? null : JSON.stringify(input.output),
1571
- $error: input.error === void 0 ? null : JSON.stringify(input.error),
1572
- $metadata: input.metadata === void 0 ? null : JSON.stringify(input.metadata),
1573
- $startedAt: input.startedAt,
1574
- $endedAt: input.endedAt ?? null,
1575
- $durationMs: input.durationMs ?? null
1576
- });
1577
- return {
1578
- runId: input.runId,
1579
- pipelineId: input.pipelineId,
1580
- status: input.status,
1581
- input: input.input,
1582
- ...input.output === void 0 ? {} : { output: input.output },
1583
- ...input.error === void 0 ? {} : { error: input.error },
1584
- ...input.metadata === void 0 ? {} : { metadata: input.metadata },
1585
- startedAt: input.startedAt,
1586
- ...input.endedAt === void 0 ? {} : { endedAt: input.endedAt },
1587
- ...input.durationMs === void 0 ? {} : { durationMs: input.durationMs }
1588
- };
1247
+ if (pipelineLogStore !== void 0 && isPipelineRunStore(pipelineLogStore)) {
1248
+ return pipelineLogStore;
1589
1249
  }
1590
- getPipelineRun(options) {
1591
- const db = this.database();
1592
- const row = db.prepare(
1593
- `SELECT run_id, pipeline_id, status, input_json, output_json, error_json,
1594
- metadata_json, started_at, ended_at, duration_ms
1595
- FROM anvia_studio_pipeline_runs
1596
- WHERE pipeline_id = $pipelineId AND run_id = $runId`
1597
- ).get({
1598
- $pipelineId: options.pipelineId,
1599
- $runId: options.runId
1600
- });
1601
- return row === void 0 ? void 0 : toPipelineRun(row);
1602
- }
1603
- listPipelineRuns(options) {
1604
- const db = this.database();
1605
- const rows = db.prepare(
1606
- `SELECT run_id, pipeline_id, status, input_json, output_json, error_json,
1607
- metadata_json, started_at, ended_at, duration_ms
1608
- FROM anvia_studio_pipeline_runs
1609
- WHERE pipeline_id = $pipelineId
1610
- ORDER BY started_at DESC
1611
- LIMIT $limit`
1612
- ).all({
1613
- $pipelineId: options.pipelineId,
1614
- $limit: options.limit
1615
- });
1616
- return rows.map(toPipelineRun);
1617
- }
1618
- deleteSession(id) {
1619
- const db = this.database();
1620
- try {
1621
- db.exec("BEGIN IMMEDIATE");
1622
- db.prepare("DELETE FROM anvia_studio_traces WHERE session_id = $id").run({ $id: id });
1623
- db.prepare("DELETE FROM anvia_studio_session_runs WHERE session_id = $id").run({ $id: id });
1624
- db.prepare("DELETE FROM anvia_studio_session_logs WHERE session_id = $id").run({ $id: id });
1625
- const result = db.prepare("DELETE FROM anvia_studio_sessions WHERE id = $id").run({ $id: id });
1626
- db.exec("COMMIT");
1627
- return Number(result.changes) > 0;
1628
- } catch (error) {
1629
- if (db.isTransaction) {
1630
- db.exec("ROLLBACK");
1631
- }
1632
- throw error;
1250
+ return defaultStore;
1251
+ }
1252
+ function isTraceStore(store) {
1253
+ const candidate = store;
1254
+ return typeof candidate.listSessionTraces === "function" && typeof candidate.getTrace === "function" && typeof candidate.saveTrace === "function";
1255
+ }
1256
+ function isPipelineLogStore(store) {
1257
+ const candidate = store;
1258
+ return typeof candidate.appendPipelineLog === "function" && typeof candidate.listPipelineLogs === "function";
1259
+ }
1260
+ function isPipelineRunStore(store) {
1261
+ const candidate = store;
1262
+ return typeof candidate.savePipelineRun === "function" && typeof candidate.getPipelineRun === "function" && typeof candidate.listPipelineRuns === "function";
1263
+ }
1264
+ function unsupportedCapabilities(stores) {
1265
+ return [
1266
+ ...stores.sessions === void 0 ? ["sessions"] : [],
1267
+ ...stores.traces === void 0 ? ["traces"] : []
1268
+ ];
1269
+ }
1270
+ function normalizeAgents(agents) {
1271
+ const ids = /* @__PURE__ */ new Set();
1272
+ return agents.map((agent) => {
1273
+ const id = agent.id.trim();
1274
+ if (id.length === 0) {
1275
+ throw new Error("Studio agent id cannot be empty");
1633
1276
  }
1634
- }
1635
- listTraces(options) {
1636
- const db = this.database();
1637
- const filters = [];
1638
- const values = {
1639
- $limit: options.limit
1640
- };
1641
- if (options.agentId !== void 0) {
1642
- filters.push(
1643
- "(s.agent_id = $agentId OR json_extract(t.metadata_json, '$.metadata.agentId') = $agentId)"
1644
- );
1645
- values.$agentId = options.agentId;
1277
+ if (ids.has(id)) {
1278
+ throw new Error(`Duplicate runner agent id: ${id}`);
1646
1279
  }
1647
- if (options.sessionId !== void 0) {
1648
- filters.push("t.session_id = $sessionId");
1649
- values.$sessionId = options.sessionId;
1280
+ ids.add(id);
1281
+ return { ...agent, id };
1282
+ });
1283
+ }
1284
+ function normalizePipelines(pipelines) {
1285
+ const ids = /* @__PURE__ */ new Set();
1286
+ return pipelines.map((pipeline) => {
1287
+ const id = pipeline.id.trim();
1288
+ if (id.length === 0) {
1289
+ throw new Error("Studio pipeline id cannot be empty");
1650
1290
  }
1651
- if (options.status !== void 0) {
1652
- filters.push("t.status = $status");
1653
- values.$status = options.status;
1291
+ if (ids.has(id)) {
1292
+ throw new Error(`Duplicate Studio pipeline id: ${id}`);
1654
1293
  }
1655
- const whereClause = filters.length === 0 ? "" : `WHERE ${filters.join(" AND ")}`;
1656
- const rows = db.prepare(
1657
- `SELECT t.id, t.session_id, t.name, t.status, t.trace_json, t.input_json, t.output,
1658
- t.error_json, t.usage_json, t.metadata_json, t.observations_json,
1659
- t.started_at, t.ended_at, t.duration_ms
1660
- FROM anvia_studio_traces t
1661
- LEFT JOIN anvia_studio_sessions s ON s.id = t.session_id
1662
- ${whereClause}
1663
- ORDER BY t.started_at DESC
1664
- LIMIT $limit`
1665
- ).all(values);
1666
- return rows.map(toTraceSummary);
1294
+ ids.add(id);
1295
+ return { ...pipeline, id };
1296
+ });
1297
+ }
1298
+ function buildConfig(options, agents, pipelines, stores) {
1299
+ return {
1300
+ id: runnerId(options),
1301
+ ...options.name === void 0 ? {} : { name: options.name },
1302
+ ...options.description === void 0 ? {} : { description: options.description },
1303
+ ...options.version === void 0 ? {} : { version: options.version },
1304
+ agents: agents.map(agentConfig),
1305
+ pipelines: pipelines.map(pipelineConfig),
1306
+ evals: options.evals.map(evalConfig),
1307
+ chat: {
1308
+ quickPrompts: Object.fromEntries(agents.map((agent) => [agent.id, agent.quickPrompts ?? []]))
1309
+ },
1310
+ capabilities: capabilityConfig(options, agents, pipelines, stores),
1311
+ unsupportedCapabilities: unsupportedCapabilities(stores)
1312
+ };
1313
+ }
1314
+ function runnerId(options) {
1315
+ return options.id ?? "anvia-studio";
1316
+ }
1317
+ function agentConfig(agent) {
1318
+ const name = agent.name ?? agent.agent.name;
1319
+ const description = agent.description ?? agent.agent.description;
1320
+ return {
1321
+ id: agent.id,
1322
+ ...name === void 0 ? {} : { name },
1323
+ ...description === void 0 ? {} : { description },
1324
+ quickPrompts: agent.quickPrompts ?? [],
1325
+ ...agent.metadata === void 0 ? {} : { metadata: agent.metadata }
1326
+ };
1327
+ }
1328
+ function agentRuntimeSummary(agent) {
1329
+ const tools = agentToolItems(agent);
1330
+ const name = agent.name ?? agent.agent.name;
1331
+ const description = agent.description ?? agent.agent.description;
1332
+ return {
1333
+ id: agent.id,
1334
+ ...name === void 0 ? {} : { name },
1335
+ ...description === void 0 ? {} : { description },
1336
+ model: toJsonValue(agent.agent.model),
1337
+ toolCount: tools.length,
1338
+ staticToolCount: tools.filter((item) => item.source === "static").length,
1339
+ dynamicToolCount: tools.filter((item) => item.source === "dynamic").length,
1340
+ approvalToolCount: tools.filter((item) => item.tool.approval !== void 0).length,
1341
+ mcpToolCount: tools.filter((item) => mcpServerName(item.tool) !== void 0).length,
1342
+ staticContextCount: agent.agent.staticContext.length,
1343
+ dynamicContextCount: agent.agent.dynamicContexts.length,
1344
+ observerCount: agent.agent.observers.length,
1345
+ hasMemory: agent.agent.memory !== void 0,
1346
+ hasHook: agent.agent.hook !== void 0,
1347
+ hasOutputSchema: agent.agent.outputSchema !== void 0,
1348
+ ...agent.agent.defaultMaxTurns === void 0 ? {} : { defaultMaxTurns: agent.agent.defaultMaxTurns },
1349
+ ...agent.metadata === void 0 ? {} : { metadata: agent.metadata }
1350
+ };
1351
+ }
1352
+ function pipelineConfig(pipeline) {
1353
+ const graph = pipeline.pipeline.graph();
1354
+ const stageNodes = graph.nodes.filter((node) => node.kind !== "input" && node.kind !== "output");
1355
+ return {
1356
+ id: pipeline.id,
1357
+ ...pipeline.name === void 0 ? {} : { name: pipeline.name },
1358
+ ...pipeline.description === void 0 ? {} : { description: pipeline.description },
1359
+ ...pipeline.metadata === void 0 ? {} : { metadata: pipeline.metadata },
1360
+ stageCount: stageNodes.length,
1361
+ edgeCount: graph.edges.length,
1362
+ hasParallelStages: graph.nodes.some((node) => node.kind === "parallel"),
1363
+ agentCount: graph.nodes.filter((node) => node.kind === "agent").length,
1364
+ extractorCount: graph.nodes.filter((node) => node.kind === "extractor").length
1365
+ };
1366
+ }
1367
+ function capabilityConfig(_options, agents, pipelines, stores) {
1368
+ const capabilities = {
1369
+ agents: { enabled: true },
1370
+ observability: { enabled: true },
1371
+ status: { enabled: true }
1372
+ };
1373
+ if (stores.sessions !== void 0) {
1374
+ capabilities.sessions = { enabled: true };
1375
+ capabilities.memory = { enabled: true };
1667
1376
  }
1668
- listSessionTraces(options) {
1669
- const db = this.database();
1670
- const rows = db.prepare(
1671
- `SELECT id, session_id, name, status, trace_json, input_json, output, error_json,
1672
- usage_json, metadata_json, observations_json, started_at, ended_at, duration_ms
1673
- FROM anvia_studio_traces
1674
- WHERE session_id = $sessionId
1675
- ORDER BY started_at DESC
1676
- LIMIT $limit`
1677
- ).all({
1678
- $sessionId: options.sessionId,
1679
- $limit: options.limit
1680
- });
1681
- return rows.map(toTraceSummary);
1377
+ if (stores.traces !== void 0) {
1378
+ capabilities.traces = { enabled: true };
1682
1379
  }
1683
- getTrace(id) {
1684
- const db = this.database();
1685
- const row = db.prepare(
1686
- `SELECT id, session_id, name, status, trace_json, input_json, output, error_json,
1687
- usage_json, metadata_json, observations_json, started_at, ended_at, duration_ms
1688
- FROM anvia_studio_traces
1689
- WHERE id = $id`
1690
- ).get({ $id: id });
1691
- return row === void 0 ? void 0 : toTrace(row);
1380
+ if (pipelines.length > 0) {
1381
+ capabilities.pipelines = { enabled: true };
1692
1382
  }
1693
- saveTrace(trace) {
1694
- const db = this.database();
1695
- db.prepare(
1696
- `INSERT INTO anvia_studio_traces (
1697
- id,
1698
- session_id,
1699
- name,
1700
- status,
1701
- trace_json,
1702
- input_json,
1703
- output,
1704
- error_json,
1705
- usage_json,
1706
- metadata_json,
1707
- observations_json,
1708
- started_at,
1709
- ended_at,
1710
- duration_ms
1711
- ) VALUES (
1712
- $id,
1713
- $sessionId,
1714
- $name,
1715
- $status,
1716
- $trace,
1717
- $input,
1718
- $output,
1719
- $error,
1720
- $usage,
1721
- $metadata,
1722
- $observations,
1723
- $startedAt,
1724
- $endedAt,
1725
- $durationMs
1726
- )
1727
- ON CONFLICT(id) DO UPDATE SET
1728
- session_id = excluded.session_id,
1729
- name = excluded.name,
1730
- status = excluded.status,
1731
- trace_json = excluded.trace_json,
1732
- input_json = excluded.input_json,
1733
- output = excluded.output,
1734
- error_json = excluded.error_json,
1735
- usage_json = excluded.usage_json,
1736
- metadata_json = excluded.metadata_json,
1737
- observations_json = excluded.observations_json,
1738
- started_at = excluded.started_at,
1739
- ended_at = excluded.ended_at,
1740
- duration_ms = excluded.duration_ms`
1741
- ).run({
1742
- $id: trace.id,
1743
- $sessionId: trace.sessionId,
1744
- $name: trace.name ?? null,
1745
- $status: trace.status,
1746
- $trace: trace.trace === void 0 ? null : JSON.stringify(trace.trace),
1747
- $input: trace.input === void 0 ? null : JSON.stringify(trace.input),
1748
- $output: trace.output ?? null,
1749
- $error: trace.error === void 0 ? null : JSON.stringify(trace.error),
1750
- $usage: trace.usage === void 0 ? null : JSON.stringify(trace.usage),
1751
- $metadata: trace.metadata === void 0 ? null : JSON.stringify(trace.metadata),
1752
- $observations: JSON.stringify(trace.observations),
1753
- $startedAt: trace.startedAt,
1754
- $endedAt: trace.endedAt ?? null,
1755
- $durationMs: trace.durationMs ?? null
1756
- });
1757
- return trace;
1383
+ if (_options.evals.length > 0) {
1384
+ capabilities.evals = { enabled: true };
1758
1385
  }
1759
- database() {
1760
- if (this.db !== void 0) {
1761
- return this.db;
1762
- }
1763
- if (this.path !== ":memory:") {
1764
- mkdirSync(dirname(resolve(this.path)), { recursive: true });
1765
- }
1766
- const db = new (loadDatabaseSync())(this.path, {
1767
- allowUnknownNamedParameters: true,
1768
- timeout: 5e3
1769
- });
1770
- db.exec(`
1771
- PRAGMA journal_mode = WAL;
1772
- PRAGMA foreign_keys = ON;
1773
- `);
1774
- guardAgainstLegacySessionSchema(db);
1775
- db.exec(`
1776
- CREATE TABLE IF NOT EXISTS anvia_studio_sessions (
1777
- id TEXT PRIMARY KEY,
1778
- agent_id TEXT NOT NULL,
1779
- title TEXT,
1780
- metadata_json TEXT,
1781
- created_at TEXT NOT NULL,
1782
- updated_at TEXT NOT NULL
1783
- ) STRICT;
1784
- CREATE INDEX IF NOT EXISTS anvia_studio_sessions_agent_updated_idx
1785
- ON anvia_studio_sessions(agent_id, updated_at DESC);
1786
- CREATE TABLE IF NOT EXISTS anvia_studio_session_messages (
1787
- session_id TEXT NOT NULL,
1788
- message_index INTEGER NOT NULL,
1789
- role TEXT NOT NULL,
1790
- message_id TEXT,
1791
- created_at TEXT NOT NULL,
1792
- PRIMARY KEY(session_id, message_index),
1793
- FOREIGN KEY(session_id) REFERENCES anvia_studio_sessions(id) ON DELETE CASCADE
1794
- ) STRICT;
1795
- CREATE INDEX IF NOT EXISTS anvia_studio_session_messages_session_idx
1796
- ON anvia_studio_session_messages(session_id, message_index ASC);
1797
- CREATE TABLE IF NOT EXISTS anvia_studio_session_message_parts (
1798
- session_id TEXT NOT NULL,
1799
- message_index INTEGER NOT NULL,
1800
- part_index INTEGER NOT NULL,
1801
- type TEXT NOT NULL,
1802
- part_json TEXT NOT NULL,
1803
- PRIMARY KEY(session_id, message_index, part_index),
1804
- FOREIGN KEY(session_id, message_index)
1805
- REFERENCES anvia_studio_session_messages(session_id, message_index)
1806
- ON DELETE CASCADE
1807
- ) STRICT;
1808
- CREATE TABLE IF NOT EXISTS anvia_studio_session_runs (
1809
- run_id TEXT PRIMARY KEY,
1810
- session_id TEXT NOT NULL,
1811
- status TEXT NOT NULL,
1812
- title TEXT,
1813
- transcript_json TEXT NOT NULL,
1814
- error_json TEXT,
1815
- created_at TEXT NOT NULL,
1816
- updated_at TEXT NOT NULL,
1817
- FOREIGN KEY(session_id) REFERENCES anvia_studio_sessions(id) ON DELETE CASCADE
1818
- ) STRICT;
1819
- CREATE INDEX IF NOT EXISTS anvia_studio_session_runs_session_created_idx
1820
- ON anvia_studio_session_runs(session_id, created_at ASC);
1821
- CREATE TABLE IF NOT EXISTS anvia_studio_session_logs (
1822
- id TEXT PRIMARY KEY,
1823
- session_id TEXT NOT NULL,
1824
- run_id TEXT,
1825
- sequence INTEGER NOT NULL,
1826
- timestamp TEXT NOT NULL,
1827
- level TEXT NOT NULL,
1828
- category TEXT NOT NULL,
1829
- event TEXT NOT NULL,
1830
- message TEXT NOT NULL,
1831
- metadata_json TEXT,
1832
- UNIQUE(session_id, sequence),
1833
- FOREIGN KEY(session_id) REFERENCES anvia_studio_sessions(id) ON DELETE CASCADE
1834
- ) STRICT;
1835
- CREATE INDEX IF NOT EXISTS anvia_studio_session_logs_session_sequence_idx
1836
- ON anvia_studio_session_logs(session_id, sequence ASC);
1837
- CREATE TABLE IF NOT EXISTS anvia_studio_pipeline_logs (
1838
- id TEXT PRIMARY KEY,
1839
- pipeline_id TEXT NOT NULL,
1840
- run_id TEXT,
1841
- sequence INTEGER NOT NULL,
1842
- timestamp TEXT NOT NULL,
1843
- level TEXT NOT NULL,
1844
- category TEXT NOT NULL,
1845
- event TEXT NOT NULL,
1846
- message TEXT NOT NULL,
1847
- metadata_json TEXT,
1848
- UNIQUE(pipeline_id, sequence)
1849
- ) STRICT;
1850
- CREATE INDEX IF NOT EXISTS anvia_studio_pipeline_logs_pipeline_sequence_idx
1851
- ON anvia_studio_pipeline_logs(pipeline_id, sequence ASC);
1852
- CREATE TABLE IF NOT EXISTS anvia_studio_pipeline_runs (
1853
- run_id TEXT PRIMARY KEY,
1854
- pipeline_id TEXT NOT NULL,
1855
- status TEXT NOT NULL,
1856
- input_json TEXT NOT NULL,
1857
- output_json TEXT,
1858
- error_json TEXT,
1859
- metadata_json TEXT,
1860
- started_at TEXT NOT NULL,
1861
- ended_at TEXT,
1862
- duration_ms INTEGER
1863
- ) STRICT;
1864
- CREATE INDEX IF NOT EXISTS anvia_studio_pipeline_runs_pipeline_started_idx
1865
- ON anvia_studio_pipeline_runs(pipeline_id, started_at DESC);
1866
- CREATE TABLE IF NOT EXISTS anvia_studio_traces (
1867
- id TEXT PRIMARY KEY,
1868
- session_id TEXT NOT NULL,
1869
- name TEXT,
1870
- status TEXT NOT NULL,
1871
- trace_json TEXT,
1872
- input_json TEXT,
1873
- output TEXT,
1874
- error_json TEXT,
1875
- usage_json TEXT,
1876
- metadata_json TEXT,
1877
- observations_json TEXT NOT NULL,
1878
- started_at TEXT NOT NULL,
1879
- ended_at TEXT,
1880
- duration_ms INTEGER
1881
- ) STRICT;
1882
- CREATE INDEX IF NOT EXISTS anvia_studio_traces_session_started_idx
1883
- ON anvia_studio_traces(session_id, started_at DESC);
1884
- `);
1885
- this.db = db;
1886
- return db;
1386
+ if (agents.some(
1387
+ (agent) => agent.agent.toolSet.values().length > 0 || agent.agent.dynamicTools.length > 0
1388
+ )) {
1389
+ capabilities.tools = { enabled: true };
1887
1390
  }
1888
- getSessionRow(id) {
1889
- return this.database().prepare(
1890
- `SELECT id, agent_id, title, metadata_json, created_at, updated_at
1891
- FROM anvia_studio_sessions
1892
- WHERE id = $id`
1893
- ).get({ $id: id });
1391
+ if (agents.some(agentHasMcpTools)) {
1392
+ capabilities.mcps = { enabled: true };
1894
1393
  }
1895
- getSessionRun(sessionId, runId) {
1896
- return this.database().prepare(
1897
- `SELECT run_id, session_id, status, title, transcript_json, error_json, created_at, updated_at
1898
- FROM anvia_studio_session_runs
1899
- WHERE session_id = $sessionId AND run_id = $runId`
1900
- ).get({ $sessionId: sessionId, $runId: runId });
1394
+ if (agents.some(
1395
+ (agent) => agent.agent.hook !== void 0 || agent.agent.toolSet.values().some((tool) => tool.approval)
1396
+ )) {
1397
+ capabilities.approvals = { enabled: true };
1901
1398
  }
1902
- listSessionRunRows(sessionId) {
1903
- return this.database().prepare(
1904
- `SELECT run_id, session_id, status, title, transcript_json, error_json, created_at, updated_at
1905
- FROM anvia_studio_session_runs
1906
- WHERE session_id = $sessionId
1907
- ORDER BY created_at ASC`
1908
- ).all({ $sessionId: sessionId });
1399
+ if (agents.some(
1400
+ (agent) => agent.agent.staticContext.length > 0 || agent.agent.dynamicContexts.length > 0 || agent.agent.dynamicTools.length > 0
1401
+ )) {
1402
+ capabilities.knowledge = { enabled: true };
1909
1403
  }
1910
- nextSessionLogSequence(sessionId) {
1911
- const row = this.database().prepare(
1912
- `SELECT COALESCE(MAX(sequence) + 1, 0) AS next_sequence
1913
- FROM anvia_studio_session_logs
1914
- WHERE session_id = $sessionId`
1915
- ).get({ $sessionId: sessionId });
1916
- return row.next_sequence;
1404
+ return capabilities;
1405
+ }
1406
+ function evalConfig(suite) {
1407
+ return {
1408
+ id: suite.id ?? suite.name,
1409
+ name: suite.name,
1410
+ ...suite.description === void 0 ? {} : { description: suite.description },
1411
+ caseCount: suite.cases.length,
1412
+ metricNames: suite.metrics.map((metric) => metric.name),
1413
+ ...suite.concurrency === void 0 ? {} : { concurrency: suite.concurrency },
1414
+ ...suite.metadata === void 0 ? {} : { metadata: suite.metadata }
1415
+ };
1416
+ }
1417
+ function optionalQueryString(value) {
1418
+ const trimmed = value?.trim();
1419
+ return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
1420
+ }
1421
+ function parseLimit(value) {
1422
+ if (value === void 0 || value.trim().length === 0) {
1423
+ return 50;
1917
1424
  }
1918
- nextPipelineLogSequence(pipelineId) {
1919
- const row = this.database().prepare(
1920
- `SELECT COALESCE(MAX(sequence) + 1, 0) AS next_sequence
1921
- FROM anvia_studio_pipeline_logs
1922
- WHERE pipeline_id = $pipelineId`
1923
- ).get({ $pipelineId: pipelineId });
1924
- return row.next_sequence;
1425
+ const limit = Number(value);
1426
+ if (!Number.isInteger(limit) || limit <= 0) {
1427
+ return void 0;
1925
1428
  }
1926
- listSessionMessages(sessionId) {
1927
- const db = this.database();
1928
- const messageRows = db.prepare(
1929
- `SELECT session_id, message_index, role, message_id, created_at
1930
- FROM anvia_studio_session_messages
1931
- WHERE session_id = $sessionId
1932
- ORDER BY message_index ASC`
1933
- ).all({ $sessionId: sessionId });
1934
- if (messageRows.length === 0) {
1935
- return [];
1936
- }
1937
- const partRows = db.prepare(
1938
- `SELECT session_id, message_index, part_index, type, part_json
1939
- FROM anvia_studio_session_message_parts
1940
- WHERE session_id = $sessionId
1941
- ORDER BY message_index ASC, part_index ASC`
1942
- ).all({ $sessionId: sessionId });
1943
- const partsByMessage = /* @__PURE__ */ new Map();
1944
- for (const partRow of partRows) {
1945
- const parts = partsByMessage.get(partRow.message_index) ?? [];
1946
- parts.push(partRow);
1947
- partsByMessage.set(partRow.message_index, parts);
1948
- }
1949
- return messageRows.map(
1950
- (row) => messageFromRows(row, partsByMessage.get(row.message_index) ?? [])
1951
- );
1952
- }
1953
- nextMessageIndex(sessionId) {
1954
- const row = this.database().prepare(
1955
- `SELECT COALESCE(MAX(message_index) + 1, 0) AS next_index
1956
- FROM anvia_studio_session_messages
1957
- WHERE session_id = $sessionId`
1958
- ).get({ $sessionId: sessionId });
1959
- return row.next_index;
1960
- }
1961
- insertMessages(sessionId, messages, startIndex, createdAt) {
1962
- const db = this.database();
1963
- const insertMessage = db.prepare(
1964
- `INSERT INTO anvia_studio_session_messages (
1965
- session_id,
1966
- message_index,
1967
- role,
1968
- message_id,
1969
- created_at
1970
- ) VALUES ($sessionId, $messageIndex, $role, $messageId, $createdAt)`
1971
- );
1972
- const insertPart = db.prepare(
1973
- `INSERT INTO anvia_studio_session_message_parts (
1974
- session_id,
1975
- message_index,
1976
- part_index,
1977
- type,
1978
- part_json
1979
- ) VALUES ($sessionId, $messageIndex, $partIndex, $type, $partJson)`
1980
- );
1981
- messages.forEach((message, messageOffset) => {
1982
- const messageIndex = startIndex + messageOffset;
1983
- insertMessage.run({
1984
- $sessionId: sessionId,
1985
- $messageIndex: messageIndex,
1986
- $role: message.role,
1987
- $messageId: message.role === "assistant" ? message.id ?? null : null,
1988
- $createdAt: createdAt
1989
- });
1990
- messageParts(message).forEach((part, partIndex) => {
1991
- insertPart.run({
1992
- $sessionId: sessionId,
1993
- $messageIndex: messageIndex,
1994
- $partIndex: partIndex,
1995
- $type: part.type,
1996
- $partJson: JSON.stringify(part.value)
1997
- });
1998
- });
1999
- });
2000
- }
2001
- };
2002
- function toSession(row, messages, runRows = []) {
2003
- const summary = toSessionSummary({ ...row, message_count: messages.length });
2004
- const runTranscript = runRows.flatMap(
2005
- (runRow) => parseJsonArray(runRow.transcript_json)
2006
- );
2007
- return {
2008
- ...summary,
2009
- messages,
2010
- transcript: renumberTranscript(runTranscript)
2011
- };
2012
- }
2013
- function toSessionSummary(row) {
2014
- const metadata = parseJsonValue(row.metadata_json);
2015
- return {
2016
- id: row.id,
2017
- agentId: row.agent_id,
2018
- ...row.title === null ? {} : { title: row.title },
2019
- createdAt: row.created_at,
2020
- updatedAt: row.updated_at,
2021
- messageCount: row.message_count,
2022
- ...metadata === void 0 ? {} : { metadata }
2023
- };
2024
- }
2025
- function toSessionLog(row) {
2026
- const metadata = parseJsonValue(row.metadata_json);
2027
- return {
2028
- id: row.id,
2029
- sessionId: row.session_id,
2030
- ...row.run_id === null ? {} : { runId: row.run_id },
2031
- sequence: row.sequence,
2032
- timestamp: row.timestamp,
2033
- level: row.level,
2034
- category: row.category,
2035
- event: row.event,
2036
- message: row.message,
2037
- ...metadata === void 0 ? {} : { metadata }
2038
- };
2039
- }
2040
- function toPipelineLog(row) {
2041
- const metadata = parseJsonValue(row.metadata_json);
2042
- return {
2043
- id: row.id,
2044
- pipelineId: row.pipeline_id,
2045
- ...row.run_id === null ? {} : { runId: row.run_id },
2046
- sequence: row.sequence,
2047
- timestamp: row.timestamp,
2048
- level: row.level,
2049
- category: row.category,
2050
- event: row.event,
2051
- message: row.message,
2052
- ...metadata === void 0 ? {} : { metadata }
2053
- };
2054
- }
2055
- function toPipelineRun(row) {
2056
- const output = parseJsonValue(row.output_json);
2057
- const error = parseJsonValue(row.error_json);
2058
- const metadata = parseJsonValue(row.metadata_json);
2059
- return {
2060
- runId: row.run_id,
2061
- pipelineId: row.pipeline_id,
2062
- status: row.status,
2063
- input: JSON.parse(row.input_json),
2064
- ...output === void 0 ? {} : { output },
2065
- ...error === void 0 ? {} : { error },
2066
- ...metadata === void 0 ? {} : { metadata },
2067
- startedAt: row.started_at,
2068
- ...row.ended_at === null ? {} : { endedAt: row.ended_at },
2069
- ...row.duration_ms === null ? {} : { durationMs: row.duration_ms }
2070
- };
1429
+ return Math.min(limit, 100);
2071
1430
  }
2072
- function messageParts(message) {
2073
- if (message.role === "system") {
2074
- return [{ type: "text", value: { type: "text", text: message.content } }];
1431
+ function parseTraceStatus(value) {
1432
+ const status = optionalQueryString(value);
1433
+ if (status === void 0) {
1434
+ return void 0;
2075
1435
  }
2076
- return message.content.map((content) => ({
2077
- type: content.type,
2078
- value: content
2079
- }));
1436
+ return status === "running" || status === "success" || status === "error" ? status : false;
2080
1437
  }
2081
- function messageFromRows(row, partRows) {
2082
- const parts = partRows.map((partRow) => JSON.parse(partRow.part_json));
2083
- if (row.role === "system") {
2084
- return { role: "system", content: systemContentFromParts(parts) };
2085
- }
2086
- if (row.role === "user") {
2087
- return {
2088
- role: "user",
2089
- content: parts
2090
- };
1438
+ function isMessageInput(value) {
1439
+ return typeof value === "string" || isMessage(value);
1440
+ }
1441
+ function isMessage(value) {
1442
+ if (!isObject(value) || typeof value.role !== "string") {
1443
+ return false;
2091
1444
  }
2092
- if (row.role === "assistant") {
2093
- return {
2094
- role: "assistant",
2095
- ...row.message_id === null ? {} : { id: row.message_id },
2096
- content: parts
2097
- };
1445
+ if (value.role === "system") {
1446
+ return typeof value.content === "string";
2098
1447
  }
2099
- if (row.role === "tool") {
2100
- return {
2101
- role: "tool",
2102
- content: parts
2103
- };
1448
+ if (value.role === "user" || value.role === "assistant" || value.role === "tool") {
1449
+ return Array.isArray(value.content);
2104
1450
  }
2105
- throw new Error(`Unsupported stored message role: ${row.role}`);
1451
+ return false;
2106
1452
  }
2107
- function systemContentFromParts(parts) {
2108
- const first = parts[0];
2109
- if (typeof first === "object" && first !== null && "type" in first && first.type === "text" && "text" in first && typeof first.text === "string") {
2110
- return first.text;
2111
- }
2112
- return "";
1453
+ function isObject(value) {
1454
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2113
1455
  }
2114
- function guardAgainstLegacySessionSchema(db) {
2115
- const columns = db.prepare("PRAGMA table_info('anvia_studio_sessions')").all();
2116
- if (columns.some((column) => column.name === "messages_json")) {
2117
- throw new Error(
2118
- "Existing Studio SQLite DB uses the legacy messages_json schema. Delete or recreate the Studio SQLite DB to use normalized session messages."
2119
- );
2120
- }
1456
+ function isJsonObject2(value) {
1457
+ return isObject(value) && Object.values(value).every(isJsonValue2);
2121
1458
  }
2122
- function loadDatabaseSync() {
2123
- if (DatabaseSync !== void 0) {
2124
- return DatabaseSync;
1459
+ function isJsonValue2(value) {
1460
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
1461
+ return true;
2125
1462
  }
2126
- try {
2127
- ({ DatabaseSync } = createRequire(import.meta.url)(
2128
- "node:sqlite"
2129
- ));
2130
- return DatabaseSync;
2131
- } catch (error) {
2132
- throw new Error(
2133
- "The default Studio SQLite store requires Node.js with node:sqlite support. Provide custom Studio stores or disable persisted stores when running Studio in a runtime without node:sqlite.",
2134
- { cause: error }
2135
- );
1463
+ if (Array.isArray(value)) {
1464
+ return value.every(isJsonValue2);
2136
1465
  }
1466
+ return isJsonObject2(value);
2137
1467
  }
2138
- function toTrace(row) {
2139
- const trace = parseJsonValue(row.trace_json);
2140
- const input = parseJsonValue(row.input_json);
2141
- return {
2142
- ...toTraceSummary(row),
2143
- ...trace === void 0 ? {} : { trace },
2144
- ...input === void 0 ? {} : { input },
2145
- observations: parseJsonArray(row.observations_json)
2146
- };
1468
+ function isAgentTraceOptions(value) {
1469
+ if (!isObject(value)) {
1470
+ return false;
1471
+ }
1472
+ return optionalString(value.name) && optionalString(value.userId) && optionalString(value.sessionId) && optionalString(value.version) && optionalString(value.traceId) && optionalBoolean(value.failOnObserverError) && optionalStringArray(value.tags) && optionalObject(value.metadata);
2147
1473
  }
2148
- function toTraceSummary(row) {
2149
- const observations = parseJsonArray(row.observations_json);
2150
- const error = parseJsonValue(row.error_json);
2151
- const usage = parseJsonValue(row.usage_json);
2152
- const metadata = parseJsonValue(row.metadata_json);
2153
- return {
2154
- id: row.id,
2155
- sessionId: row.session_id,
2156
- ...row.name === null ? {} : { name: row.name },
2157
- status: row.status,
2158
- startedAt: row.started_at,
2159
- ...row.ended_at === null ? {} : { endedAt: row.ended_at },
2160
- ...row.duration_ms === null ? {} : { durationMs: row.duration_ms },
2161
- ...row.output === null ? {} : { output: row.output },
2162
- ...error === void 0 ? {} : { error },
2163
- ...usage === void 0 ? {} : { usage },
2164
- ...metadata === void 0 ? {} : { metadata },
2165
- observationCount: observations.length
2166
- };
1474
+ function optionalString(value) {
1475
+ return value === void 0 || typeof value === "string";
2167
1476
  }
2168
- function parseJsonArray(value) {
2169
- const parsed = JSON.parse(value);
2170
- return Array.isArray(parsed) ? parsed : [];
1477
+ function optionalBoolean(value) {
1478
+ return value === void 0 || typeof value === "boolean";
2171
1479
  }
2172
- function parseJsonValue(value) {
2173
- if (value === null) {
2174
- return void 0;
2175
- }
2176
- return JSON.parse(value);
1480
+ function optionalStringArray(value) {
1481
+ return value === void 0 || Array.isArray(value) && value.every((item) => typeof item === "string");
2177
1482
  }
2178
- function studioRunId2(context) {
2179
- const value = context.metadata?.studioRunId;
2180
- return typeof value === "string" && value.length > 0 ? value : void 0;
1483
+ function optionalObject(value) {
1484
+ return value === void 0 || isObject(value);
2181
1485
  }
2182
- function serializeJsonError2(error) {
2183
- if (error instanceof Error) {
2184
- return {
2185
- name: error.name,
2186
- message: error.message
2187
- };
2188
- }
2189
- if (error === null || typeof error === "string" || typeof error === "number" || typeof error === "boolean") {
2190
- return error;
2191
- }
2192
- return String(error);
1486
+ function isNonNegativeInteger(value) {
1487
+ return Number.isInteger(value) && typeof value === "number" && value >= 0;
2193
1488
  }
2194
-
2195
- // src/runtime/tool-metadata.ts
2196
- import { ToolSet } from "@anvia/core/tool";
2197
- var MCP_TOOL_METADATA_KEY = /* @__PURE__ */ Symbol.for("anvia.mcp.tool.metadata");
2198
- function agentToolItems(agent) {
2199
- return [
2200
- ...agent.agent.toolSet.values().map((tool) => ({ tool, source: "static" })),
2201
- ...agent.agent.dynamicTools.flatMap((registration) => {
2202
- const maybeToolSet = registration.index.toolSet;
2203
- if (!(maybeToolSet instanceof ToolSet)) {
2204
- return [];
2205
- }
2206
- return maybeToolSet.values().map((tool) => ({ tool, source: "dynamic" }));
2207
- })
2208
- ];
1489
+ function isPositiveInteger(value) {
1490
+ return Number.isInteger(value) && typeof value === "number" && value > 0;
2209
1491
  }
2210
- function approvalMetadata(tool) {
2211
- const approval = tool.approval;
2212
- if (approval === void 0 || typeof approval !== "object" || approval === null) {
2213
- return { required: false };
2214
- }
2215
- const policy = approval;
2216
- return {
2217
- required: true,
2218
- ...typeof policy.reason === "string" ? { reason: policy.reason } : {},
2219
- ...typeof policy.rejectMessage === "string" ? { rejectMessage: policy.rejectMessage } : {}
2220
- };
1492
+ function unsupportedCapability(c, capability) {
1493
+ return errorResponse(
1494
+ c,
1495
+ 501,
1496
+ "unsupported_capability",
1497
+ `Capability "${capability}" is not implemented by this runner`,
1498
+ { capability }
1499
+ );
2221
1500
  }
2222
- function mcpServerName(tool) {
2223
- const metadata = tool[MCP_TOOL_METADATA_KEY];
2224
- if (typeof metadata !== "object" || metadata === null) {
2225
- return void 0;
1501
+ function errorResponse(c, status, code, message, details) {
1502
+ const body = {
1503
+ error: {
1504
+ code,
1505
+ message
1506
+ }
1507
+ };
1508
+ if (details !== void 0) {
1509
+ body.error.details = details;
2226
1510
  }
2227
- const serverName = metadata.serverName;
2228
- return typeof serverName === "string" && serverName.length > 0 ? serverName : void 0;
1511
+ return c.json(body, status);
2229
1512
  }
2230
- function agentHasMcpTools(agent) {
2231
- return agentToolItems(agent).some(({ tool }) => mcpServerName(tool) !== void 0);
1513
+ function serializeError(error) {
1514
+ return serializeUnknown(error);
2232
1515
  }
2233
1516
 
2234
- // src/runtime/shared.ts
2235
- function resolveStores(options) {
2236
- const defaultStore = defaultStudioStore();
2237
- const sessions = resolveSessionStore(options, defaultStore);
2238
- const traces = resolveTraceStore(options, sessions, defaultStore);
2239
- const pipelineLogs = resolvePipelineLogStore(options, sessions, defaultStore);
2240
- const pipelineRuns = resolvePipelineRunStore(options, sessions, pipelineLogs, defaultStore);
2241
- return {
2242
- ...sessions === void 0 ? {} : { sessions },
2243
- ...traces === void 0 ? {} : { traces },
2244
- ...pipelineLogs === void 0 ? {} : { pipelineLogs },
2245
- ...pipelineRuns === void 0 ? {} : { pipelineRuns }
2246
- };
2247
- }
2248
- function defaultStudioStore() {
2249
- const sqlitePath = process.env.ANVIA_STUDIO_DB ?? process.env.AION_STUDIO_DB;
2250
- return sqlitePath === void 0 ? createInMemoryStudioStore() : createSqliteSessionStore({ path: sqlitePath });
2251
- }
2252
- function resolveSessionStore(options, defaultStore) {
2253
- if (options.stores?.sessions === false) {
2254
- return void 0;
2255
- }
2256
- if (options.stores?.sessions !== void 0) {
2257
- return options.stores.sessions;
2258
- }
2259
- return defaultStore;
2260
- }
2261
- function resolveTraceStore(options, sessionStore, defaultStore) {
2262
- if (options.stores?.traces !== void 0) {
2263
- return options.stores.traces;
2264
- }
2265
- if (sessionStore === void 0) {
2266
- return void 0;
2267
- }
2268
- if (isTraceStore(sessionStore)) {
2269
- return sessionStore;
2270
- }
2271
- return defaultStore;
1517
+ // src/runtime/approvals.ts
1518
+ function registerApprovalRoutes(app, approvals) {
1519
+ app.get("/approvals", (c) => {
1520
+ const status = parseApprovalStatus(c.req.query("status"));
1521
+ if (status === false) {
1522
+ return errorResponse(c, 400, "bad_request", "status must be pending or resolved");
1523
+ }
1524
+ const options = {};
1525
+ const runId = optionalQueryString(c.req.query("runId"));
1526
+ const agentId = optionalQueryString(c.req.query("agentId"));
1527
+ const sessionId = optionalQueryString(c.req.query("sessionId"));
1528
+ if (status !== void 0) {
1529
+ options.status = status;
1530
+ }
1531
+ if (runId !== void 0) {
1532
+ options.runId = runId;
1533
+ }
1534
+ if (agentId !== void 0) {
1535
+ options.agentId = agentId;
1536
+ }
1537
+ if (sessionId !== void 0) {
1538
+ options.sessionId = sessionId;
1539
+ }
1540
+ return c.json({
1541
+ approvals: approvals.list(options)
1542
+ });
1543
+ });
1544
+ app.post("/approvals/:approvalId/decision", async (c) => {
1545
+ const body = await parseApprovalDecisionRequest(c);
1546
+ if ("error" in body) {
1547
+ return body.error;
1548
+ }
1549
+ const result = approvals.decide(c.req.param("approvalId"), body);
1550
+ if (result === "missing") {
1551
+ return errorResponse(c, 404, "not_found", "Approval not found");
1552
+ }
1553
+ if (result === "resolved") {
1554
+ return errorResponse(c, 409, "conflict", "Approval is already resolved");
1555
+ }
1556
+ return c.json(result);
1557
+ });
2272
1558
  }
2273
- function resolvePipelineLogStore(options, sessionStore, defaultStore) {
2274
- if (options.stores?.pipelineLogs === false) {
1559
+ function parseApprovalStatus(value) {
1560
+ const status = optionalQueryString(value);
1561
+ if (status === void 0) {
2275
1562
  return void 0;
2276
1563
  }
2277
- if (options.stores?.pipelineLogs !== void 0) {
2278
- return options.stores.pipelineLogs;
2279
- }
2280
- if (sessionStore !== void 0 && isPipelineLogStore(sessionStore)) {
2281
- return sessionStore;
2282
- }
2283
- return defaultStore;
1564
+ return status === "pending" || status === "resolved" ? status : false;
2284
1565
  }
2285
- function resolvePipelineRunStore(options, sessionStore, pipelineLogStore, defaultStore) {
2286
- if (options.stores?.pipelineRuns === false) {
2287
- return void 0;
1566
+ async function parseApprovalDecisionRequest(c) {
1567
+ let body;
1568
+ try {
1569
+ body = await c.req.json();
1570
+ } catch {
1571
+ return { error: errorResponse(c, 400, "bad_request", "Request body must be JSON") };
2288
1572
  }
2289
- if (options.stores?.pipelineRuns !== void 0) {
2290
- return options.stores.pipelineRuns;
1573
+ if (!isObject(body)) {
1574
+ return { error: errorResponse(c, 400, "bad_request", "Request body must be an object") };
2291
1575
  }
2292
- if (sessionStore !== void 0 && isPipelineRunStore(sessionStore)) {
2293
- return sessionStore;
1576
+ if (typeof body.approved !== "boolean") {
1577
+ return { error: errorResponse(c, 400, "bad_request", "approved must be a boolean") };
2294
1578
  }
2295
- if (pipelineLogStore !== void 0 && isPipelineRunStore(pipelineLogStore)) {
2296
- return pipelineLogStore;
1579
+ if ("reason" in body && typeof body.reason !== "string") {
1580
+ return { error: errorResponse(c, 400, "bad_request", "reason must be a string") };
2297
1581
  }
2298
- return defaultStore;
2299
- }
2300
- function isTraceStore(store) {
2301
- const candidate = store;
2302
- return typeof candidate.listSessionTraces === "function" && typeof candidate.getTrace === "function" && typeof candidate.saveTrace === "function";
2303
- }
2304
- function isPipelineLogStore(store) {
2305
- const candidate = store;
2306
- return typeof candidate.appendPipelineLog === "function" && typeof candidate.listPipelineLogs === "function";
2307
- }
2308
- function isPipelineRunStore(store) {
2309
- const candidate = store;
2310
- return typeof candidate.savePipelineRun === "function" && typeof candidate.getPipelineRun === "function" && typeof candidate.listPipelineRuns === "function";
2311
- }
2312
- function unsupportedCapabilities(stores) {
2313
- return [
2314
- ...stores.sessions === void 0 ? ["sessions"] : [],
2315
- ...stores.traces === void 0 ? ["traces"] : []
2316
- ];
2317
- }
2318
- function normalizeAgents(agents) {
2319
- const ids = /* @__PURE__ */ new Set();
2320
- return agents.map((agent) => {
2321
- const id = agent.id.trim();
2322
- if (id.length === 0) {
2323
- throw new Error("Studio agent id cannot be empty");
2324
- }
2325
- if (ids.has(id)) {
2326
- throw new Error(`Duplicate runner agent id: ${id}`);
2327
- }
2328
- ids.add(id);
2329
- return { ...agent, id };
2330
- });
2331
- }
2332
- function normalizePipelines(pipelines) {
2333
- const ids = /* @__PURE__ */ new Set();
2334
- return pipelines.map((pipeline) => {
2335
- const id = pipeline.id.trim();
2336
- if (id.length === 0) {
2337
- throw new Error("Studio pipeline id cannot be empty");
2338
- }
2339
- if (ids.has(id)) {
2340
- throw new Error(`Duplicate Studio pipeline id: ${id}`);
2341
- }
2342
- ids.add(id);
2343
- return { ...pipeline, id };
2344
- });
2345
- }
2346
- function buildConfig(options, agents, pipelines, stores) {
2347
1582
  return {
2348
- id: runnerId(options),
2349
- ...options.name === void 0 ? {} : { name: options.name },
2350
- ...options.description === void 0 ? {} : { description: options.description },
2351
- ...options.version === void 0 ? {} : { version: options.version },
2352
- agents: agents.map(agentConfig),
2353
- pipelines: pipelines.map(pipelineConfig),
2354
- evals: options.evals.map(evalConfig),
2355
- chat: {
2356
- quickPrompts: Object.fromEntries(agents.map((agent) => [agent.id, agent.quickPrompts ?? []]))
2357
- },
2358
- capabilities: capabilityConfig(options, agents, pipelines, stores),
2359
- unsupportedCapabilities: unsupportedCapabilities(stores)
1583
+ approved: body.approved,
1584
+ ...typeof body.reason === "string" && body.reason.trim().length > 0 ? { reason: body.reason.trim() } : {}
2360
1585
  };
2361
1586
  }
2362
- function runnerId(options) {
2363
- return options.id ?? "anvia-studio";
2364
- }
2365
- function agentConfig(agent) {
2366
- const name = agent.name ?? agent.agent.name;
2367
- const description = agent.description ?? agent.agent.description;
1587
+ function createApprovalRuntime() {
1588
+ const approvals = /* @__PURE__ */ new Map();
2368
1589
  return {
2369
- id: agent.id,
2370
- ...name === void 0 ? {} : { name },
2371
- ...description === void 0 ? {} : { description },
2372
- quickPrompts: agent.quickPrompts ?? [],
2373
- ...agent.metadata === void 0 ? {} : { metadata: agent.metadata }
2374
- };
2375
- }
2376
- function agentRuntimeSummary(agent) {
2377
- const tools = agentToolItems(agent);
2378
- const name = agent.name ?? agent.agent.name;
2379
- const description = agent.description ?? agent.agent.description;
2380
- return {
2381
- id: agent.id,
2382
- ...name === void 0 ? {} : { name },
2383
- ...description === void 0 ? {} : { description },
2384
- model: toJsonValue(agent.agent.model),
2385
- toolCount: tools.length,
2386
- staticToolCount: tools.filter((item) => item.source === "static").length,
2387
- dynamicToolCount: tools.filter((item) => item.source === "dynamic").length,
2388
- approvalToolCount: tools.filter((item) => item.tool.approval !== void 0).length,
2389
- mcpToolCount: tools.filter((item) => mcpServerName(item.tool) !== void 0).length,
2390
- staticContextCount: agent.agent.staticContext.length,
2391
- dynamicContextCount: agent.agent.dynamicContexts.length,
2392
- observerCount: agent.agent.observers.length,
2393
- hasMemory: agent.agent.memory !== void 0,
2394
- hasHook: agent.agent.hook !== void 0,
2395
- hasOutputSchema: agent.agent.outputSchema !== void 0,
2396
- ...agent.agent.defaultMaxTurns === void 0 ? {} : { defaultMaxTurns: agent.agent.defaultMaxTurns },
2397
- ...agent.metadata === void 0 ? {} : { metadata: agent.metadata }
1590
+ approvals,
1591
+ createHook(context) {
1592
+ const handleApprovalRequest = async ({ toolName, toolCallId, internalCallId, args, tool: control }, request) => {
1593
+ const decision = await requestApproval(approvals, context, {
1594
+ toolName,
1595
+ ...toolCallId === void 0 ? {} : { toolCallId },
1596
+ internalCallId,
1597
+ args,
1598
+ ...request.reason === void 0 ? {} : { reason: request.reason },
1599
+ ...request.rejectMessage === void 0 ? {} : { rejectMessage: request.rejectMessage }
1600
+ });
1601
+ return decision.approved ? control.run() : control.skip(decision.reason ?? request.rejectMessage ?? "Rejected in Anvia Studio.");
1602
+ };
1603
+ return {
1604
+ ...createHook({
1605
+ async onToolCall({ toolName, toolCallId, internalCallId, args, tool: control }) {
1606
+ const registeredTool = context.getTool(toolName);
1607
+ if (registeredTool?.approval === void 0) {
1608
+ return control.run();
1609
+ }
1610
+ const approval = registeredTool.approval;
1611
+ const rawParsedArgs = parseToolArgs(args);
1612
+ const parsedArgs = registeredTool.parseApprovalArgs?.(rawParsedArgs) ?? rawParsedArgs;
1613
+ const approvalContext = {
1614
+ toolName,
1615
+ args: parsedArgs,
1616
+ rawArgs: args,
1617
+ ...toolCallId === void 0 ? {} : { toolCallId },
1618
+ internalCallId,
1619
+ run: {
1620
+ agentId: context.agentId,
1621
+ runId: context.runId,
1622
+ ...context.sessionId === void 0 ? {} : { sessionId: context.sessionId },
1623
+ ...context.metadata === void 0 ? {} : { metadata: context.metadata }
1624
+ }
1625
+ };
1626
+ const required = await approval.when(approvalContext);
1627
+ if (!required) {
1628
+ return control.run();
1629
+ }
1630
+ const reason = await resolveApprovalText(approval.reason, approvalContext);
1631
+ const rejectMessage = await resolveApprovalText(
1632
+ approval.rejectMessage,
1633
+ approvalContext
1634
+ );
1635
+ const decision = await requestApproval(approvals, context, {
1636
+ toolName,
1637
+ ...toolCallId === void 0 ? {} : { toolCallId },
1638
+ internalCallId,
1639
+ args,
1640
+ ...reason === void 0 ? {} : { reason },
1641
+ ...rejectMessage === void 0 ? {} : { rejectMessage }
1642
+ });
1643
+ return decision.approved ? control.run() : control.skip(decision.reason ?? rejectMessage ?? "Rejected in Anvia Studio.");
1644
+ }
1645
+ }),
1646
+ handleApprovalRequest
1647
+ };
1648
+ },
1649
+ list(options) {
1650
+ return [...approvals.values()].filter((approval) => {
1651
+ if (options.status === "pending" && approval.status !== "pending") {
1652
+ return false;
1653
+ }
1654
+ if (options.status === "resolved" && approval.status === "pending") {
1655
+ return false;
1656
+ }
1657
+ if (options.runId !== void 0 && approval.runId !== options.runId) {
1658
+ return false;
1659
+ }
1660
+ if (options.agentId !== void 0 && approval.agentId !== options.agentId) {
1661
+ return false;
1662
+ }
1663
+ if (options.sessionId !== void 0 && approval.sessionId !== options.sessionId) {
1664
+ return false;
1665
+ }
1666
+ return true;
1667
+ }).map(publicApproval);
1668
+ },
1669
+ decide(id, decision) {
1670
+ const approval = approvals.get(id);
1671
+ if (approval === void 0) {
1672
+ return "missing";
1673
+ }
1674
+ if (!isPendingApproval(approval)) {
1675
+ return "resolved";
1676
+ }
1677
+ const reason = decision.approved ? decision.reason : decision.reason ?? approval.rejectMessage ?? "Rejected in Anvia Studio.";
1678
+ const resolved = resolveApproval(approval, decision.approved ? "approved" : "rejected", {
1679
+ ...reason === void 0 ? {} : { reason }
1680
+ });
1681
+ approvals.set(id, resolved);
1682
+ approval.emit?.({ type: "tool_approval_result", approval: resolved });
1683
+ approval.resolve({
1684
+ approved: decision.approved,
1685
+ ...reason === void 0 ? {} : { reason }
1686
+ });
1687
+ return publicApproval(resolved);
1688
+ }
2398
1689
  };
2399
1690
  }
2400
- function pipelineConfig(pipeline) {
2401
- const graph = pipeline.pipeline.graph();
2402
- const stageNodes = graph.nodes.filter((node) => node.kind !== "input" && node.kind !== "output");
2403
- return {
2404
- id: pipeline.id,
2405
- ...pipeline.name === void 0 ? {} : { name: pipeline.name },
2406
- ...pipeline.description === void 0 ? {} : { description: pipeline.description },
2407
- ...pipeline.metadata === void 0 ? {} : { metadata: pipeline.metadata },
2408
- stageCount: stageNodes.length,
2409
- edgeCount: graph.edges.length,
2410
- hasParallelStages: graph.nodes.some((node) => node.kind === "parallel"),
2411
- agentCount: graph.nodes.filter((node) => node.kind === "agent").length,
2412
- extractorCount: graph.nodes.filter((node) => node.kind === "extractor").length
1691
+ async function requestApproval(approvals, context, request) {
1692
+ const id = globalThis.crypto.randomUUID();
1693
+ const approval = {
1694
+ id,
1695
+ runId: context.runId,
1696
+ agentId: context.agentId,
1697
+ ...context.sessionId === void 0 ? {} : { sessionId: context.sessionId },
1698
+ toolName: request.toolName,
1699
+ ...request.toolCallId === void 0 ? {} : { callId: request.toolCallId },
1700
+ internalCallId: request.internalCallId,
1701
+ args: request.args,
1702
+ status: "pending",
1703
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
1704
+ ...request.reason === void 0 ? {} : { reason: request.reason },
1705
+ ...request.rejectMessage === void 0 ? {} : { rejectMessage: request.rejectMessage },
1706
+ ...context.emit === void 0 ? {} : { emit: context.emit },
1707
+ resolve: () => {
1708
+ }
2413
1709
  };
1710
+ const decision = new Promise((resolve2) => {
1711
+ approval.resolve = (decision2) => {
1712
+ const current = approvals.get(id);
1713
+ if (!isPendingApproval(current)) {
1714
+ if (current !== void 0) {
1715
+ resolve2({
1716
+ approved: current.status === "approved",
1717
+ ...current.reason === void 0 ? {} : { reason: current.reason }
1718
+ });
1719
+ }
1720
+ return;
1721
+ }
1722
+ const reason = decision2.approved ? decision2.reason : decision2.reason ?? request.rejectMessage ?? "Rejected in Anvia Studio.";
1723
+ const resolved = resolveApproval(current, decision2.approved ? "approved" : "rejected", {
1724
+ ...reason === void 0 ? {} : { reason }
1725
+ });
1726
+ approvals.set(id, resolved);
1727
+ context.emit?.({ type: "tool_approval_result", approval: resolved });
1728
+ resolve2({
1729
+ approved: decision2.approved,
1730
+ ...reason === void 0 ? {} : { reason }
1731
+ });
1732
+ };
1733
+ });
1734
+ approvals.set(id, approval);
1735
+ context.emit?.({ type: "tool_approval_request", approval: publicApproval(approval) });
1736
+ return decision;
2414
1737
  }
2415
- function capabilityConfig(_options, agents, pipelines, stores) {
2416
- const capabilities = {
2417
- agents: { enabled: true },
2418
- observability: { enabled: true },
2419
- status: { enabled: true }
2420
- };
2421
- if (stores.sessions !== void 0) {
2422
- capabilities.sessions = { enabled: true };
2423
- capabilities.memory = { enabled: true };
2424
- }
2425
- if (stores.traces !== void 0) {
2426
- capabilities.traces = { enabled: true };
2427
- }
2428
- if (pipelines.length > 0) {
2429
- capabilities.pipelines = { enabled: true };
2430
- }
2431
- if (_options.evals.length > 0) {
2432
- capabilities.evals = { enabled: true };
2433
- }
2434
- if (agents.some(
2435
- (agent) => agent.agent.toolSet.values().length > 0 || agent.agent.dynamicTools.length > 0
2436
- )) {
2437
- capabilities.tools = { enabled: true };
2438
- }
2439
- if (agents.some(agentHasMcpTools)) {
2440
- capabilities.mcps = { enabled: true };
2441
- }
2442
- if (agents.some(
2443
- (agent) => agent.agent.hook !== void 0 || agent.agent.toolSet.values().some((tool) => tool.approval)
2444
- )) {
2445
- capabilities.approvals = { enabled: true };
2446
- }
2447
- if (agents.some(
2448
- (agent) => agent.agent.staticContext.length > 0 || agent.agent.dynamicContexts.length > 0 || agent.agent.dynamicTools.length > 0
2449
- )) {
2450
- capabilities.knowledge = { enabled: true };
2451
- }
2452
- return capabilities;
1738
+ async function resolveApprovalText(value, context) {
1739
+ return typeof value === "function" ? value(context) : value;
2453
1740
  }
2454
- function evalConfig(suite) {
2455
- return {
2456
- id: suite.id ?? suite.name,
2457
- name: suite.name,
2458
- ...suite.description === void 0 ? {} : { description: suite.description },
2459
- caseCount: suite.cases.length,
2460
- metricNames: suite.metrics.map((metric) => metric.name),
2461
- ...suite.concurrency === void 0 ? {} : { concurrency: suite.concurrency },
2462
- ...suite.metadata === void 0 ? {} : { metadata: suite.metadata }
2463
- };
1741
+ function isPendingApproval(approval) {
1742
+ return approval !== void 0 && approval.status === "pending" && "resolve" in approval;
2464
1743
  }
2465
- function optionalQueryString(value) {
2466
- const trimmed = value?.trim();
2467
- return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
1744
+ function resolveApproval(approval, status, options = {}) {
1745
+ return publicApproval({
1746
+ ...approval,
1747
+ status,
1748
+ resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
1749
+ ...options.reason === void 0 ? {} : { reason: options.reason }
1750
+ });
2468
1751
  }
2469
- function parseLimit(value) {
2470
- if (value === void 0 || value.trim().length === 0) {
2471
- return 50;
2472
- }
2473
- const limit = Number(value);
2474
- if (!Number.isInteger(limit) || limit <= 0) {
2475
- return void 0;
2476
- }
2477
- return Math.min(limit, 100);
2478
- }
2479
- function parseTraceStatus(value) {
2480
- const status = optionalQueryString(value);
2481
- if (status === void 0) {
2482
- return void 0;
2483
- }
2484
- return status === "running" || status === "success" || status === "error" ? status : false;
2485
- }
2486
- function isMessageInput(value) {
2487
- return typeof value === "string" || isMessage(value);
2488
- }
2489
- function isMessage(value) {
2490
- if (!isObject(value) || typeof value.role !== "string") {
2491
- return false;
2492
- }
2493
- if (value.role === "system") {
2494
- return typeof value.content === "string";
2495
- }
2496
- if (value.role === "user" || value.role === "assistant" || value.role === "tool") {
2497
- return Array.isArray(value.content);
2498
- }
2499
- return false;
2500
- }
2501
- function isObject(value) {
2502
- return typeof value === "object" && value !== null && !Array.isArray(value);
2503
- }
2504
- function isJsonObject2(value) {
2505
- return isObject(value) && Object.values(value).every(isJsonValue2);
2506
- }
2507
- function isJsonValue2(value) {
2508
- if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
2509
- return true;
2510
- }
2511
- if (Array.isArray(value)) {
2512
- return value.every(isJsonValue2);
2513
- }
2514
- return isJsonObject2(value);
2515
- }
2516
- function isAgentTraceOptions(value) {
2517
- if (!isObject(value)) {
2518
- return false;
2519
- }
2520
- return optionalString(value.name) && optionalString(value.userId) && optionalString(value.sessionId) && optionalString(value.version) && optionalString(value.traceId) && optionalBoolean(value.failOnObserverError) && optionalStringArray(value.tags) && optionalObject(value.metadata);
2521
- }
2522
- function optionalString(value) {
2523
- return value === void 0 || typeof value === "string";
2524
- }
2525
- function optionalBoolean(value) {
2526
- return value === void 0 || typeof value === "boolean";
2527
- }
2528
- function optionalStringArray(value) {
2529
- return value === void 0 || Array.isArray(value) && value.every((item) => typeof item === "string");
2530
- }
2531
- function optionalObject(value) {
2532
- return value === void 0 || isObject(value);
2533
- }
2534
- function isNonNegativeInteger(value) {
2535
- return Number.isInteger(value) && typeof value === "number" && value >= 0;
2536
- }
2537
- function isPositiveInteger(value) {
2538
- return Number.isInteger(value) && typeof value === "number" && value > 0;
2539
- }
2540
- function unsupportedCapability(c, capability) {
2541
- return errorResponse(
2542
- c,
2543
- 501,
2544
- "unsupported_capability",
2545
- `Capability "${capability}" is not implemented by this runner`,
2546
- { capability }
2547
- );
2548
- }
2549
- function errorResponse(c, status, code, message, details) {
2550
- const body = {
2551
- error: {
2552
- code,
2553
- message
2554
- }
2555
- };
2556
- if (details !== void 0) {
2557
- body.error.details = details;
2558
- }
2559
- return c.json(body, status);
2560
- }
2561
- function serializeError(error) {
2562
- return serializeUnknown(error);
1752
+ function publicApproval(approval) {
1753
+ const { emit, rejectMessage, resolve: resolve2, ...rest } = approval;
1754
+ void emit;
1755
+ void rejectMessage;
1756
+ void resolve2;
1757
+ return rest;
2563
1758
  }
2564
1759
 
2565
- // src/runtime/approvals.ts
2566
- function registerApprovalRoutes(app, approvals) {
2567
- app.get("/approvals", (c) => {
2568
- const status = parseApprovalStatus(c.req.query("status"));
2569
- if (status === false) {
2570
- return errorResponse(c, 400, "bad_request", "status must be pending or resolved");
2571
- }
2572
- const options = {};
2573
- const runId = optionalQueryString(c.req.query("runId"));
2574
- const agentId = optionalQueryString(c.req.query("agentId"));
2575
- const sessionId = optionalQueryString(c.req.query("sessionId"));
2576
- if (status !== void 0) {
2577
- options.status = status;
2578
- }
2579
- if (runId !== void 0) {
2580
- options.runId = runId;
2581
- }
2582
- if (agentId !== void 0) {
2583
- options.agentId = agentId;
2584
- }
2585
- if (sessionId !== void 0) {
2586
- options.sessionId = sessionId;
1760
+ // src/runtime/evals.ts
1761
+ import { runEvalSuite } from "@anvia/core/evals";
1762
+ function registerEvalRoutes(app, props) {
1763
+ app.get(
1764
+ "/evals",
1765
+ (c) => c.json({
1766
+ evals: props.evals.map(evalConfig)
1767
+ })
1768
+ );
1769
+ app.get("/evals/:evalId", (c) => {
1770
+ const suite = props.evalMap.get(c.req.param("evalId"));
1771
+ if (suite === void 0) {
1772
+ return errorResponse(c, 404, "not_found", "Eval suite not found");
2587
1773
  }
2588
- return c.json({
2589
- approvals: approvals.list(options)
2590
- });
1774
+ return c.json(evalConfig(suite));
2591
1775
  });
2592
- app.post("/approvals/:approvalId/decision", async (c) => {
2593
- const body = await parseApprovalDecisionRequest(c);
1776
+ app.post("/evals/:evalId/runs", async (c) => {
1777
+ const suite = props.evalMap.get(c.req.param("evalId"));
1778
+ if (suite === void 0) {
1779
+ return errorResponse(c, 404, "not_found", "Eval suite not found");
1780
+ }
1781
+ const body = await parseEvalRunRequest(c);
2594
1782
  if ("error" in body) {
2595
1783
  return body.error;
2596
1784
  }
2597
- const result = approvals.decide(c.req.param("approvalId"), body);
2598
- if (result === "missing") {
2599
- return errorResponse(c, 404, "not_found", "Approval not found");
2600
- }
2601
- if (result === "resolved") {
2602
- return errorResponse(c, 409, "conflict", "Approval is already resolved");
2603
- }
2604
- return c.json(result);
1785
+ const runId = globalThis.crypto.randomUUID();
1786
+ const startedAt = Date.now();
1787
+ const result = await runEvalSuite({
1788
+ ...suite,
1789
+ ...body.concurrency === void 0 ? {} : { concurrency: body.concurrency }
1790
+ });
1791
+ const endedAt = Date.now();
1792
+ const jsonResult = toJsonValue(result);
1793
+ const response = {
1794
+ runId,
1795
+ suiteId: suite.id ?? suite.name,
1796
+ startedAt: new Date(startedAt).toISOString(),
1797
+ endedAt: new Date(endedAt).toISOString(),
1798
+ durationMs: endedAt - startedAt,
1799
+ result: isJsonObject2(jsonResult) ? jsonResult : { value: jsonResult }
1800
+ };
1801
+ return c.json(response);
2605
1802
  });
2606
1803
  }
2607
- function parseApprovalStatus(value) {
2608
- const status = optionalQueryString(value);
2609
- if (status === void 0) {
2610
- return void 0;
2611
- }
2612
- return status === "pending" || status === "resolved" ? status : false;
2613
- }
2614
- async function parseApprovalDecisionRequest(c) {
2615
- let body;
1804
+ async function parseEvalRunRequest(c) {
1805
+ let body = {};
2616
1806
  try {
2617
1807
  body = await c.req.json();
2618
1808
  } catch {
2619
- return { error: errorResponse(c, 400, "bad_request", "Request body must be JSON") };
1809
+ body = {};
2620
1810
  }
2621
1811
  if (!isObject(body)) {
2622
1812
  return { error: errorResponse(c, 400, "bad_request", "Request body must be an object") };
2623
1813
  }
2624
- if (typeof body.approved !== "boolean") {
2625
- return { error: errorResponse(c, 400, "bad_request", "approved must be a boolean") };
2626
- }
2627
- if ("reason" in body && typeof body.reason !== "string") {
2628
- return { error: errorResponse(c, 400, "bad_request", "reason must be a string") };
1814
+ const request = {};
1815
+ if ("concurrency" in body) {
1816
+ if (!isPositiveInteger(body.concurrency)) {
1817
+ return {
1818
+ error: errorResponse(c, 400, "bad_request", "concurrency must be a positive integer")
1819
+ };
1820
+ }
1821
+ request.concurrency = body.concurrency;
2629
1822
  }
1823
+ return request;
1824
+ }
1825
+
1826
+ // src/runtime/knowledge.ts
1827
+ function registerKnowledgeRoutes(app, props) {
1828
+ app.get("/knowledge", async (c) => {
1829
+ const limit = parseLimit(c.req.query("limit"));
1830
+ if (limit === void 0) {
1831
+ return errorResponse(c, 400, "bad_request", "Invalid limit");
1832
+ }
1833
+ const summary = {
1834
+ agents: await Promise.all(props.agents.map(agentKnowledgeConfig)),
1835
+ evidence: await recentKnowledgeEvidence(props.traceStore, limit)
1836
+ };
1837
+ return c.json(summary);
1838
+ });
1839
+ app.get("/knowledge/items", async (c) => {
1840
+ const limit = parseLimit(c.req.query("limit"));
1841
+ if (limit === void 0) {
1842
+ return errorResponse(c, 400, "bad_request", "Invalid limit");
1843
+ }
1844
+ const agentId = optionalQueryString(c.req.query("agentId"));
1845
+ const sourceId = optionalQueryString(c.req.query("sourceId"));
1846
+ if (agentId === void 0 || sourceId === void 0) {
1847
+ return errorResponse(c, 400, "bad_request", "agentId and sourceId are required");
1848
+ }
1849
+ const agent = props.agents.find((item) => item.id === agentId);
1850
+ if (agent === void 0) {
1851
+ return errorResponse(c, 404, "not_found", "Agent not found");
1852
+ }
1853
+ const page = await knowledgeItemsPage(agent, sourceId, {
1854
+ limit,
1855
+ cursor: optionalQueryString(c.req.query("cursor"))
1856
+ });
1857
+ if (page === void 0) {
1858
+ return errorResponse(c, 404, "not_found", "Knowledge source not found");
1859
+ }
1860
+ return c.json(page);
1861
+ });
1862
+ }
1863
+ async function agentKnowledgeConfig(agent) {
1864
+ const agentName = agent.name ?? agent.agent.name;
2630
1865
  return {
2631
- approved: body.approved,
2632
- ...typeof body.reason === "string" && body.reason.trim().length > 0 ? { reason: body.reason.trim() } : {}
1866
+ agentId: agent.id,
1867
+ ...agentName === void 0 ? {} : { agentName },
1868
+ sources: await knowledgeSources(agent),
1869
+ staticContext: agent.agent.staticContext.map((document) => ({
1870
+ id: document.id,
1871
+ text: document.text,
1872
+ ...document.additionalProps === void 0 ? {} : { additionalProps: jsonObjectFromRecord(document.additionalProps) }
1873
+ }))
2633
1874
  };
2634
1875
  }
2635
- function createApprovalRuntime() {
2636
- const approvals = /* @__PURE__ */ new Map();
2637
- return {
2638
- approvals,
2639
- createHook(context) {
2640
- const handleApprovalRequest = async ({ toolName, toolCallId, internalCallId, args, tool: control }, request) => {
2641
- const decision = await requestApproval(approvals, context, {
2642
- toolName,
2643
- ...toolCallId === void 0 ? {} : { toolCallId },
2644
- internalCallId,
2645
- args,
2646
- ...request.reason === void 0 ? {} : { reason: request.reason },
2647
- ...request.rejectMessage === void 0 ? {} : { rejectMessage: request.rejectMessage }
2648
- });
2649
- return decision.approved ? control.run() : control.skip(decision.reason ?? request.rejectMessage ?? "Rejected in Anvia Studio.");
2650
- };
2651
- return {
2652
- ...createHook({
2653
- async onToolCall({ toolName, toolCallId, internalCallId, args, tool: control }) {
2654
- const registeredTool = context.getTool(toolName);
2655
- if (registeredTool?.approval === void 0) {
2656
- return control.run();
2657
- }
2658
- const approval = registeredTool.approval;
2659
- const rawParsedArgs = parseToolArgs(args);
2660
- const parsedArgs = registeredTool.parseApprovalArgs?.(rawParsedArgs) ?? rawParsedArgs;
2661
- const approvalContext = {
2662
- toolName,
2663
- args: parsedArgs,
2664
- rawArgs: args,
2665
- ...toolCallId === void 0 ? {} : { toolCallId },
2666
- internalCallId,
2667
- run: {
2668
- agentId: context.agentId,
2669
- runId: context.runId,
2670
- ...context.sessionId === void 0 ? {} : { sessionId: context.sessionId },
2671
- ...context.metadata === void 0 ? {} : { metadata: context.metadata }
2672
- }
2673
- };
2674
- const required = await approval.when(approvalContext);
2675
- if (!required) {
2676
- return control.run();
2677
- }
2678
- const reason = await resolveApprovalText(approval.reason, approvalContext);
2679
- const rejectMessage = await resolveApprovalText(
2680
- approval.rejectMessage,
2681
- approvalContext
2682
- );
2683
- const decision = await requestApproval(approvals, context, {
2684
- toolName,
2685
- ...toolCallId === void 0 ? {} : { toolCallId },
2686
- internalCallId,
2687
- args,
2688
- ...reason === void 0 ? {} : { reason },
2689
- ...rejectMessage === void 0 ? {} : { rejectMessage }
2690
- });
2691
- return decision.approved ? control.run() : control.skip(decision.reason ?? rejectMessage ?? "Rejected in Anvia Studio.");
2692
- }
2693
- }),
2694
- handleApprovalRequest
2695
- };
2696
- },
2697
- list(options) {
2698
- return [...approvals.values()].filter((approval) => {
2699
- if (options.status === "pending" && approval.status !== "pending") {
2700
- return false;
2701
- }
2702
- if (options.status === "resolved" && approval.status === "pending") {
2703
- return false;
2704
- }
2705
- if (options.runId !== void 0 && approval.runId !== options.runId) {
2706
- return false;
2707
- }
2708
- if (options.agentId !== void 0 && approval.agentId !== options.agentId) {
2709
- return false;
2710
- }
2711
- if (options.sessionId !== void 0 && approval.sessionId !== options.sessionId) {
2712
- return false;
2713
- }
2714
- return true;
2715
- }).map(publicApproval);
2716
- },
2717
- decide(id, decision) {
2718
- const approval = approvals.get(id);
2719
- if (approval === void 0) {
2720
- return "missing";
2721
- }
2722
- if (!isPendingApproval(approval)) {
2723
- return "resolved";
2724
- }
2725
- const reason = decision.approved ? decision.reason : decision.reason ?? approval.rejectMessage ?? "Rejected in Anvia Studio.";
2726
- const resolved = resolveApproval(approval, decision.approved ? "approved" : "rejected", {
2727
- ...reason === void 0 ? {} : { reason }
2728
- });
2729
- approvals.set(id, resolved);
2730
- approval.emit?.({ type: "tool_approval_result", approval: resolved });
2731
- approval.resolve({
2732
- approved: decision.approved,
2733
- ...reason === void 0 ? {} : { reason }
2734
- });
2735
- return publicApproval(resolved);
2736
- }
2737
- };
2738
- }
2739
- async function requestApproval(approvals, context, request) {
2740
- const id = globalThis.crypto.randomUUID();
2741
- const approval = {
2742
- id,
2743
- runId: context.runId,
2744
- agentId: context.agentId,
2745
- ...context.sessionId === void 0 ? {} : { sessionId: context.sessionId },
2746
- toolName: request.toolName,
2747
- ...request.toolCallId === void 0 ? {} : { callId: request.toolCallId },
2748
- internalCallId: request.internalCallId,
2749
- args: request.args,
2750
- status: "pending",
2751
- requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
2752
- ...request.reason === void 0 ? {} : { reason: request.reason },
2753
- ...request.rejectMessage === void 0 ? {} : { rejectMessage: request.rejectMessage },
2754
- ...context.emit === void 0 ? {} : { emit: context.emit },
2755
- resolve: () => {
1876
+ async function knowledgeSources(agent) {
1877
+ const sources = [
1878
+ {
1879
+ sourceId: staticSourceId(),
1880
+ kind: "static_context",
1881
+ label: "Static context",
1882
+ count: agent.agent.staticContext.length,
1883
+ inspectable: true,
1884
+ itemCount: agent.agent.staticContext.length
2756
1885
  }
2757
- };
2758
- const decision = new Promise((resolve2) => {
2759
- approval.resolve = (decision2) => {
2760
- const current = approvals.get(id);
2761
- if (!isPendingApproval(current)) {
2762
- if (current !== void 0) {
2763
- resolve2({
2764
- approved: current.status === "approved",
2765
- ...current.reason === void 0 ? {} : { reason: current.reason }
2766
- });
2767
- }
2768
- return;
2769
- }
2770
- const reason = decision2.approved ? decision2.reason : decision2.reason ?? request.rejectMessage ?? "Rejected in Anvia Studio.";
2771
- const resolved = resolveApproval(current, decision2.approved ? "approved" : "rejected", {
2772
- ...reason === void 0 ? {} : { reason }
2773
- });
2774
- approvals.set(id, resolved);
2775
- context.emit?.({ type: "tool_approval_result", approval: resolved });
2776
- resolve2({
2777
- approved: decision2.approved,
2778
- ...reason === void 0 ? {} : { reason }
2779
- });
2780
- };
2781
- });
2782
- approvals.set(id, approval);
2783
- context.emit?.({ type: "tool_approval_request", approval: publicApproval(approval) });
2784
- return decision;
2785
- }
2786
- async function resolveApprovalText(value, context) {
2787
- return typeof value === "function" ? value(context) : value;
2788
- }
2789
- function isPendingApproval(approval) {
2790
- return approval !== void 0 && approval.status === "pending" && "resolve" in approval;
2791
- }
2792
- function resolveApproval(approval, status, options = {}) {
2793
- return publicApproval({
2794
- ...approval,
2795
- status,
2796
- resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
2797
- ...options.reason === void 0 ? {} : { reason: options.reason }
2798
- });
2799
- }
2800
- function publicApproval(approval) {
2801
- const { emit, rejectMessage, resolve: resolve2, ...rest } = approval;
2802
- void emit;
2803
- void rejectMessage;
2804
- void resolve2;
2805
- return rest;
2806
- }
2807
-
2808
- // src/runtime/evals.ts
2809
- import { runEvalSuite } from "@anvia/core/evals";
2810
- function registerEvalRoutes(app, props) {
2811
- app.get(
2812
- "/evals",
2813
- (c) => c.json({
2814
- evals: props.evals.map(evalConfig)
1886
+ ];
1887
+ const dynamicContextSources = await Promise.all(
1888
+ agent.agent.dynamicContexts.map(async (registration, index) => {
1889
+ const inspect = inspectFn(registration.index);
1890
+ const count = await inspectableCount(inspect, registration.options.filter);
1891
+ return {
1892
+ sourceId: dynamicContextSourceId(index),
1893
+ kind: "dynamic_context",
1894
+ label: `Dynamic context ${index + 1}`,
1895
+ count: 1,
1896
+ registrationIndex: index,
1897
+ topK: registration.options.topK,
1898
+ ...registration.options.threshold === void 0 ? {} : { threshold: registration.options.threshold },
1899
+ inspectable: inspect !== void 0,
1900
+ ...count === void 0 ? {} : { itemCount: count }
1901
+ };
2815
1902
  })
2816
1903
  );
2817
- app.get("/evals/:evalId", (c) => {
2818
- const suite = props.evalMap.get(c.req.param("evalId"));
2819
- if (suite === void 0) {
2820
- return errorResponse(c, 404, "not_found", "Eval suite not found");
2821
- }
2822
- return c.json(evalConfig(suite));
2823
- });
2824
- app.post("/evals/:evalId/runs", async (c) => {
2825
- const suite = props.evalMap.get(c.req.param("evalId"));
2826
- if (suite === void 0) {
2827
- return errorResponse(c, 404, "not_found", "Eval suite not found");
2828
- }
2829
- const body = await parseEvalRunRequest(c);
2830
- if ("error" in body) {
2831
- return body.error;
2832
- }
2833
- const runId = globalThis.crypto.randomUUID();
2834
- const startedAt = Date.now();
2835
- const result = await runEvalSuite({
2836
- ...suite,
2837
- ...body.concurrency === void 0 ? {} : { concurrency: body.concurrency }
2838
- });
2839
- const endedAt = Date.now();
2840
- const jsonResult = toJsonValue(result);
2841
- const response = {
2842
- runId,
2843
- suiteId: suite.id ?? suite.name,
2844
- startedAt: new Date(startedAt).toISOString(),
2845
- endedAt: new Date(endedAt).toISOString(),
2846
- durationMs: endedAt - startedAt,
2847
- result: isJsonObject2(jsonResult) ? jsonResult : { value: jsonResult }
2848
- };
2849
- return c.json(response);
2850
- });
2851
- }
2852
- async function parseEvalRunRequest(c) {
2853
- let body = {};
2854
- try {
2855
- body = await c.req.json();
2856
- } catch {
2857
- body = {};
2858
- }
2859
- if (!isObject(body)) {
2860
- return { error: errorResponse(c, 400, "bad_request", "Request body must be an object") };
2861
- }
2862
- const request = {};
2863
- if ("concurrency" in body) {
2864
- if (!isPositiveInteger(body.concurrency)) {
1904
+ const dynamicToolSources = await Promise.all(
1905
+ agent.agent.dynamicTools.map(async (registration, index) => {
1906
+ const inspect = inspectFn(registration.index);
1907
+ const count = await inspectableCount(inspect, registration.options.filter);
2865
1908
  return {
2866
- error: errorResponse(c, 400, "bad_request", "concurrency must be a positive integer")
1909
+ sourceId: dynamicToolsSourceId(index),
1910
+ kind: "dynamic_tools",
1911
+ label: `Dynamic tools ${index + 1}`,
1912
+ count: 1,
1913
+ registrationIndex: index,
1914
+ topK: registration.options.topK,
1915
+ ...registration.options.threshold === void 0 ? {} : { threshold: registration.options.threshold },
1916
+ inspectable: inspect !== void 0,
1917
+ ...count === void 0 ? {} : { itemCount: count }
2867
1918
  };
2868
- }
2869
- request.concurrency = body.concurrency;
2870
- }
2871
- return request;
2872
- }
2873
-
2874
- // src/runtime/knowledge.ts
2875
- function registerKnowledgeRoutes(app, props) {
2876
- app.get("/knowledge", async (c) => {
2877
- const limit = parseLimit(c.req.query("limit"));
2878
- if (limit === void 0) {
2879
- return errorResponse(c, 400, "bad_request", "Invalid limit");
2880
- }
2881
- const summary = {
2882
- agents: await Promise.all(props.agents.map(agentKnowledgeConfig)),
2883
- evidence: await recentKnowledgeEvidence(props.traceStore, limit)
2884
- };
2885
- return c.json(summary);
2886
- });
2887
- app.get("/knowledge/items", async (c) => {
2888
- const limit = parseLimit(c.req.query("limit"));
2889
- if (limit === void 0) {
2890
- return errorResponse(c, 400, "bad_request", "Invalid limit");
2891
- }
2892
- const agentId = optionalQueryString(c.req.query("agentId"));
2893
- const sourceId = optionalQueryString(c.req.query("sourceId"));
2894
- if (agentId === void 0 || sourceId === void 0) {
2895
- return errorResponse(c, 400, "bad_request", "agentId and sourceId are required");
2896
- }
2897
- const agent = props.agents.find((item) => item.id === agentId);
2898
- if (agent === void 0) {
2899
- return errorResponse(c, 404, "not_found", "Agent not found");
2900
- }
2901
- const page = await knowledgeItemsPage(agent, sourceId, {
2902
- limit,
2903
- cursor: optionalQueryString(c.req.query("cursor"))
2904
- });
2905
- if (page === void 0) {
2906
- return errorResponse(c, 404, "not_found", "Knowledge source not found");
2907
- }
2908
- return c.json(page);
2909
- });
2910
- }
2911
- async function agentKnowledgeConfig(agent) {
2912
- const agentName = agent.name ?? agent.agent.name;
2913
- return {
2914
- agentId: agent.id,
2915
- ...agentName === void 0 ? {} : { agentName },
2916
- sources: await knowledgeSources(agent),
2917
- staticContext: agent.agent.staticContext.map((document) => ({
2918
- id: document.id,
2919
- text: document.text,
2920
- ...document.additionalProps === void 0 ? {} : { additionalProps: jsonObjectFromRecord(document.additionalProps) }
2921
- }))
2922
- };
2923
- }
2924
- async function knowledgeSources(agent) {
2925
- const sources = [
2926
- {
2927
- sourceId: staticSourceId(),
2928
- kind: "static_context",
2929
- label: "Static context",
2930
- count: agent.agent.staticContext.length,
2931
- inspectable: true,
2932
- itemCount: agent.agent.staticContext.length
2933
- }
2934
- ];
2935
- const dynamicContextSources = await Promise.all(
2936
- agent.agent.dynamicContexts.map(async (registration, index) => {
2937
- const inspect = inspectFn(registration.index);
2938
- const count = await inspectableCount(inspect, registration.options.filter);
2939
- return {
2940
- sourceId: dynamicContextSourceId(index),
2941
- kind: "dynamic_context",
2942
- label: `Dynamic context ${index + 1}`,
2943
- count: 1,
2944
- registrationIndex: index,
2945
- topK: registration.options.topK,
2946
- ...registration.options.threshold === void 0 ? {} : { threshold: registration.options.threshold },
2947
- inspectable: inspect !== void 0,
2948
- ...count === void 0 ? {} : { itemCount: count }
2949
- };
2950
- })
2951
- );
2952
- const dynamicToolSources = await Promise.all(
2953
- agent.agent.dynamicTools.map(async (registration, index) => {
2954
- const inspect = inspectFn(registration.index);
2955
- const count = await inspectableCount(inspect, registration.options.filter);
2956
- return {
2957
- sourceId: dynamicToolsSourceId(index),
2958
- kind: "dynamic_tools",
2959
- label: `Dynamic tools ${index + 1}`,
2960
- count: 1,
2961
- registrationIndex: index,
2962
- topK: registration.options.topK,
2963
- ...registration.options.threshold === void 0 ? {} : { threshold: registration.options.threshold },
2964
- inspectable: inspect !== void 0,
2965
- ...count === void 0 ? {} : { itemCount: count }
2966
- };
2967
- })
2968
- );
2969
- return [...sources, ...dynamicContextSources, ...dynamicToolSources];
1919
+ })
1920
+ );
1921
+ return [...sources, ...dynamicContextSources, ...dynamicToolSources];
2970
1922
  }
2971
1923
  async function inspectableCount(inspect, filter) {
2972
1924
  if (inspect === void 0) {
@@ -3884,6 +2836,15 @@ async function* persistStreamingSessionTranscript(props) {
3884
2836
  if (nextSession === void 0) {
3885
2837
  throw new Error("Session not found");
3886
2838
  }
2839
+ const generatedMessages = event.type === "final" ? event.messages.slice(1) : [];
2840
+ if (props.persistGeneratedMessages === true && generatedMessages.length > 0) {
2841
+ await props.store.append({
2842
+ context: { sessionId: props.session.id },
2843
+ runId: props.runId,
2844
+ turn: 1,
2845
+ messages: generatedMessages
2846
+ });
2847
+ }
3887
2848
  yield event;
3888
2849
  }
3889
2850
  } catch (error) {
@@ -5918,7 +4879,9 @@ function agentMetadata(agent) {
5918
4879
  function createStudioApp(options) {
5919
4880
  const observabilityHub = new StudioObservabilityHub();
5920
4881
  const stores = observeStores(resolveStores(options), observabilityHub);
5921
- const agents = normalizeAgents(options.agents).map((agent) => withStudioSessionMemory(agent, stores.sessions)).map((agent) => withStudioTraceObserver(agent, stores.traces));
4882
+ const agents = normalizeAgents(options.agents).map(
4883
+ (agent) => withStudioTraceObserver(agent, stores.traces)
4884
+ );
5922
4885
  const pipelines = normalizePipelines(options.pipelines);
5923
4886
  const agentMap = new Map(agents.map((agent) => [agent.id, agent]));
5924
4887
  const pipelineMap = new Map(pipelines.map((pipeline) => [pipeline.id, pipeline]));
@@ -6000,288 +4963,1343 @@ function createStudioApp(options) {
6000
4963
  if (session !== void 0 && session.agentId !== agentId) {
6001
4964
  return errorResponse(c, 400, "bad_request", "Session belongs to another agent");
6002
4965
  }
6003
- const runId = globalThis.crypto.randomUUID();
6004
- const runStartedAt = Date.now();
6005
- if (session !== void 0) {
6006
- await appendSessionLog(
6007
- stores.sessions,
6008
- runReceivedLog({
6009
- sessionId: session.id,
6010
- runId,
6011
- agentId,
6012
- message: body.message,
6013
- stream: body.stream === true,
6014
- ...body.maxTurns === void 0 ? {} : { maxTurns: body.maxTurns },
6015
- ...body.toolConcurrency === void 0 ? {} : { toolConcurrency: body.toolConcurrency },
6016
- hasTrace: body.trace !== void 0,
6017
- ...body.metadata === void 0 ? {} : { metadata: body.metadata }
6018
- })
6019
- );
4966
+ const runId = globalThis.crypto.randomUUID();
4967
+ const runStartedAt = Date.now();
4968
+ if (session !== void 0) {
4969
+ await appendSessionLog(
4970
+ stores.sessions,
4971
+ runReceivedLog({
4972
+ sessionId: session.id,
4973
+ runId,
4974
+ agentId,
4975
+ message: body.message,
4976
+ stream: body.stream === true,
4977
+ ...body.maxTurns === void 0 ? {} : { maxTurns: body.maxTurns },
4978
+ ...body.toolConcurrency === void 0 ? {} : { toolConcurrency: body.toolConcurrency },
4979
+ hasTrace: body.trace !== void 0,
4980
+ ...body.metadata === void 0 ? {} : { metadata: body.metadata }
4981
+ })
4982
+ );
4983
+ }
4984
+ const memoryMetadata = {
4985
+ agentId,
4986
+ ...body.metadata ?? {},
4987
+ studioRunId: runId
4988
+ };
4989
+ const promptMessage = normalizePromptMessage(body.message);
4990
+ const sessionStore = stores.sessions;
4991
+ const shouldPersistSessionMessages = session !== void 0 && sessionStore !== void 0 && !usesStoreAsAgentMemory(agent.agent, sessionStore);
4992
+ if (shouldPersistSessionMessages) {
4993
+ await sessionStore.append({
4994
+ context: { sessionId: session.id, metadata: memoryMetadata },
4995
+ runId,
4996
+ turn: 1,
4997
+ messages: [promptMessage]
4998
+ });
4999
+ }
5000
+ const request = session !== void 0 ? agent.agent.memory === void 0 ? agent.agent.prompt([...session.messages, promptMessage]) : agent.agent.session(session.id, { metadata: memoryMetadata }).prompt(body.message) : agent.agent.prompt(
5001
+ body.history !== void 0 ? [...body.history, promptMessage] : body.message
5002
+ );
5003
+ if (body.maxTurns !== void 0) {
5004
+ request.maxTurns(body.maxTurns);
5005
+ }
5006
+ if (body.toolConcurrency !== void 0) {
5007
+ request.withToolConcurrency(body.toolConcurrency);
5008
+ }
5009
+ if (body.trace !== void 0) {
5010
+ request.withTrace(traceForRun(body.trace, agentId, session));
5011
+ } else if (session !== void 0) {
5012
+ request.withTrace(traceForRun(void 0, agentId, session));
5013
+ }
5014
+ if (body.stream === true) {
5015
+ const runtimeEvents = new AsyncEventQueue();
5016
+ const effectiveHook = composeHooks(
5017
+ composeHooks(
5018
+ agent.agent.hook,
5019
+ approvalRuntime.createHook({
5020
+ runId,
5021
+ agentId,
5022
+ ...session?.id === void 0 ? {} : { sessionId: session.id },
5023
+ ...body.metadata === void 0 ? {} : { metadata: body.metadata },
5024
+ getTool: (toolName) => agent.agent.getTool(toolName),
5025
+ emit: (event) => runtimeEvents.push(event)
5026
+ })
5027
+ ),
5028
+ questionRuntime.createHook({
5029
+ runId,
5030
+ agentId,
5031
+ ...session?.id === void 0 ? {} : { sessionId: session.id },
5032
+ ...body.metadata === void 0 ? {} : { metadata: body.metadata },
5033
+ emit: (event) => runtimeEvents.push(event)
5034
+ })
5035
+ );
5036
+ if (effectiveHook !== void 0) {
5037
+ request.requestHook(effectiveHook);
5038
+ }
5039
+ const runStream = mergeRunAndApprovalEvents(request.stream(), runtimeEvents);
5040
+ const stream = session === void 0 || sessionStore === void 0 ? runStream : persistStreamingSessionTranscript({
5041
+ stream: streamSessionRunLogs({
5042
+ stream: runStream,
5043
+ store: sessionStore,
5044
+ session,
5045
+ runId,
5046
+ startedAt: runStartedAt
5047
+ }),
5048
+ store: sessionStore,
5049
+ session,
5050
+ message: body.message,
5051
+ runId,
5052
+ persistGeneratedMessages: shouldPersistSessionMessages
5053
+ });
5054
+ return streamAgentRunEvents(c, stream);
5055
+ }
5056
+ try {
5057
+ if (session !== void 0) {
5058
+ await appendSessionLog(sessionStore, runStartedLog(session, runId));
5059
+ await appendSessionLog(sessionStore, memoryLoadedLog(session, runId));
5060
+ }
5061
+ const effectiveHook = composeHooks(
5062
+ composeHooks(
5063
+ agent.agent.hook,
5064
+ approvalRuntime.createHook({
5065
+ runId,
5066
+ agentId,
5067
+ ...session?.id === void 0 ? {} : { sessionId: session.id },
5068
+ ...body.metadata === void 0 ? {} : { metadata: body.metadata },
5069
+ getTool: (toolName) => agent.agent.getTool(toolName)
5070
+ })
5071
+ ),
5072
+ questionRuntime.createHook({
5073
+ runId,
5074
+ agentId,
5075
+ ...session?.id === void 0 ? {} : { sessionId: session.id },
5076
+ ...body.metadata === void 0 ? {} : { metadata: body.metadata }
5077
+ })
5078
+ );
5079
+ if (effectiveHook !== void 0) {
5080
+ request.requestHook(effectiveHook);
5081
+ }
5082
+ const response = await request.send();
5083
+ if (session !== void 0 && sessionStore !== void 0) {
5084
+ if (shouldPersistSessionMessages) {
5085
+ const generatedMessages = response.messages.slice(1);
5086
+ if (generatedMessages.length > 0) {
5087
+ await sessionStore.append({
5088
+ context: { sessionId: session.id, metadata: memoryMetadata },
5089
+ runId,
5090
+ turn: 1,
5091
+ messages: generatedMessages
5092
+ });
5093
+ }
5094
+ }
5095
+ await sessionStore.saveSessionRunTranscript({
5096
+ id: session.id,
5097
+ runId,
5098
+ ...optionalTitle(body.message),
5099
+ transcript: transcriptFromMessages(response.messages),
5100
+ status: "success"
5101
+ });
5102
+ await appendSessionLog(
5103
+ sessionStore,
5104
+ runCompletedLog({
5105
+ sessionId: session.id,
5106
+ runId,
5107
+ durationMs: Date.now() - runStartedAt,
5108
+ usage: response.usage,
5109
+ output: response.output,
5110
+ messageCount: response.messages.length
5111
+ })
5112
+ );
5113
+ await appendSessionLog(
5114
+ sessionStore,
5115
+ memorySavedLog({
5116
+ sessionId: session.id,
5117
+ runId,
5118
+ messageCount: response.messages.length
5119
+ })
5120
+ );
5121
+ }
5122
+ return c.json(response);
5123
+ } catch (error) {
5124
+ if (session !== void 0 && sessionStore !== void 0) {
5125
+ const messages = await sessionStore.load({
5126
+ sessionId: session.id,
5127
+ metadata: memoryMetadata
5128
+ });
5129
+ await sessionStore.saveSessionRunTranscript({
5130
+ id: session.id,
5131
+ runId,
5132
+ ...optionalTitle(body.message),
5133
+ transcript: transcriptFromMessages(messages.slice(session.messageCount)),
5134
+ status: "error",
5135
+ error: serializeError(error)
5136
+ });
5137
+ await appendSessionLog(sessionStore, runFailedLog(session.id, runId, error, runStartedAt));
5138
+ }
5139
+ return errorResponse(c, 500, "internal_error", "Agent run failed", serializeError(error));
5140
+ }
5141
+ });
5142
+ if (stores.sessions !== void 0) {
5143
+ registerMemoryRoutes(app, {
5144
+ sessionStore: stores.sessions
5145
+ });
5146
+ registerSessionRoutes(app, {
5147
+ agentMap,
5148
+ sessionStore: stores.sessions,
5149
+ ...stores.traces === void 0 ? {} : { traceStore: stores.traces }
5150
+ });
5151
+ }
5152
+ if (stores.traces !== void 0) {
5153
+ registerTraceRoutes(app, stores.traces);
5154
+ }
5155
+ for (const capability of unsupportedCapabilities(stores)) {
5156
+ app.all(`/${capability}`, (c) => unsupportedCapability(c, capability));
5157
+ app.all(`/${capability}/*`, (c) => unsupportedCapability(c, capability));
5158
+ }
5159
+ return {
5160
+ app,
5161
+ fetch(request) {
5162
+ return app.fetch(request);
5163
+ },
5164
+ config() {
5165
+ return buildConfig(options, agents, pipelines, stores);
5166
+ },
5167
+ close() {
5168
+ },
5169
+ ...stores.sessions === void 0 ? {} : { sessionStore: stores.sessions },
5170
+ ...stores.traces === void 0 ? {} : { traceStore: stores.traces }
5171
+ };
5172
+ }
5173
+ function normalizePromptMessage(message) {
5174
+ return typeof message === "string" ? Message.user(message) : message;
5175
+ }
5176
+ function usesStoreAsAgentMemory(agent, store) {
5177
+ return agent.memory?.store === store;
5178
+ }
5179
+ function withStudioTraceObserver(studioAgent, traceStore) {
5180
+ if (traceStore === void 0 || hasStudioTraceObserver(studioAgent.agent)) {
5181
+ return studioAgent;
5182
+ }
5183
+ return {
5184
+ ...studioAgent,
5185
+ agent: cloneAgent(studioAgent.agent, {
5186
+ observers: [
5187
+ ...studioAgent.agent.observers,
5188
+ { observer: new StudioTraceObserver({ store: traceStore }) }
5189
+ ]
5190
+ })
5191
+ };
5192
+ }
5193
+ function cloneAgent(agent, overrides = {}) {
5194
+ return new Agent({
5195
+ id: agent.id,
5196
+ name: agent.name,
5197
+ description: agent.description,
5198
+ model: agent.model,
5199
+ instructions: agent.instructions,
5200
+ staticContext: agent.staticContext,
5201
+ temperature: agent.temperature,
5202
+ maxTokens: agent.maxTokens,
5203
+ additionalParams: agent.additionalParams,
5204
+ toolSet: agent.toolSet,
5205
+ toolChoice: agent.toolChoice,
5206
+ defaultMaxTurns: agent.defaultMaxTurns,
5207
+ hook: agent.hook,
5208
+ outputSchema: agent.outputSchema,
5209
+ observers: agent.observers,
5210
+ dynamicContexts: agent.dynamicContexts,
5211
+ dynamicTools: agent.dynamicTools,
5212
+ memory: agent.memory,
5213
+ ...overrides
5214
+ });
5215
+ }
5216
+ function hasStudioTraceObserver(agent) {
5217
+ return agent.observers.some(
5218
+ (registration) => registration.observer instanceof StudioTraceObserver
5219
+ );
5220
+ }
5221
+ function composeHooks(first, second) {
5222
+ if (first === void 0) {
5223
+ return second;
5224
+ }
5225
+ if (second === void 0) {
5226
+ return first;
5227
+ }
5228
+ return createHook3({
5229
+ async onCompletionCall(args) {
5230
+ const firstAction = await first.onCompletionCall?.(args);
5231
+ return firstAction?.type === "terminate" ? firstAction : await second.onCompletionCall?.(args) ?? void 0;
5232
+ },
5233
+ async onCompletionResponse(args) {
5234
+ const firstAction = await first.onCompletionResponse?.(args);
5235
+ return firstAction?.type === "terminate" ? firstAction : await second.onCompletionResponse?.(args) ?? void 0;
5236
+ },
5237
+ async onToolCall(args) {
5238
+ const firstAction = await first.onToolCall?.(args);
5239
+ if (firstAction?.type === "skip" || firstAction?.type === "terminate") {
5240
+ return firstAction;
5241
+ }
5242
+ if (firstAction?.type === "approval_request") {
5243
+ return await approvalRequestHandler(second)?.(args, firstAction) ?? firstAction;
5244
+ }
5245
+ const secondAction = await second.onToolCall?.(args);
5246
+ return secondAction ?? firstAction ?? void 0;
5247
+ },
5248
+ async onToolResult(args) {
5249
+ const firstAction = await first.onToolResult?.(args);
5250
+ return firstAction?.type === "terminate" ? firstAction : await second.onToolResult?.(args) ?? void 0;
5251
+ }
5252
+ });
5253
+ }
5254
+ function approvalRequestHandler(hook) {
5255
+ const candidate = hook;
5256
+ return typeof candidate.handleApprovalRequest === "function" ? candidate.handleApprovalRequest : void 0;
5257
+ }
5258
+
5259
+ // src/storage/sqlite-store.ts
5260
+ import { mkdirSync } from "fs";
5261
+ import { createRequire } from "module";
5262
+ import { dirname, resolve } from "path";
5263
+ var DatabaseSync;
5264
+ function createSqliteSessionStore(options = {}) {
5265
+ return new SqliteSessionStore(options.path ?? ":memory:");
5266
+ }
5267
+ var SqliteSessionStore = class {
5268
+ constructor(path) {
5269
+ this.path = path;
5270
+ }
5271
+ path;
5272
+ kind = "sqlite";
5273
+ db;
5274
+ listSessions(options) {
5275
+ const db = this.database();
5276
+ const agentClause = options.agentId === void 0 ? "" : "WHERE s.agent_id = $agentId";
5277
+ const rows = db.prepare(
5278
+ `SELECT s.id, s.agent_id, s.title, s.metadata_json, s.created_at, s.updated_at,
5279
+ COUNT(m.message_index) AS message_count
5280
+ FROM anvia_studio_sessions s
5281
+ LEFT JOIN anvia_studio_session_messages m ON m.session_id = s.id
5282
+ ${agentClause}
5283
+ GROUP BY s.id, s.agent_id, s.title, s.metadata_json, s.created_at, s.updated_at
5284
+ ORDER BY s.updated_at DESC
5285
+ LIMIT $limit`
5286
+ ).all({
5287
+ $agentId: options.agentId ?? null,
5288
+ $limit: options.limit
5289
+ });
5290
+ return rows.map(toSessionSummary);
5291
+ }
5292
+ createSession(input) {
5293
+ const db = this.database();
5294
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5295
+ db.prepare(
5296
+ `INSERT INTO anvia_studio_sessions (
5297
+ id,
5298
+ agent_id,
5299
+ title,
5300
+ metadata_json,
5301
+ created_at,
5302
+ updated_at
5303
+ ) VALUES ($id, $agentId, $title, $metadata, $now, $now)`
5304
+ ).run({
5305
+ $id: input.id,
5306
+ $agentId: input.agentId,
5307
+ $title: input.title ?? null,
5308
+ $metadata: input.metadata === void 0 ? null : JSON.stringify(input.metadata),
5309
+ $now: now
5310
+ });
5311
+ return {
5312
+ id: input.id,
5313
+ agentId: input.agentId,
5314
+ ...input.title === void 0 ? {} : { title: input.title },
5315
+ createdAt: now,
5316
+ updatedAt: now,
5317
+ messageCount: 0,
5318
+ ...input.metadata === void 0 ? {} : { metadata: input.metadata }
5319
+ };
5320
+ }
5321
+ getSession(id) {
5322
+ const row = this.getSessionRow(id);
5323
+ return row === void 0 ? void 0 : toSession(row, this.listSessionMessages(id), this.listSessionRunRows(id));
5324
+ }
5325
+ load(context) {
5326
+ const session = this.getSession(context.sessionId);
5327
+ return Promise.resolve(session?.messages ?? []);
5328
+ }
5329
+ append(input) {
5330
+ const db = this.database();
5331
+ try {
5332
+ db.exec("BEGIN IMMEDIATE");
5333
+ const row = db.prepare(
5334
+ `SELECT id, agent_id, title, metadata_json, created_at, updated_at
5335
+ FROM anvia_studio_sessions
5336
+ WHERE id = $id`
5337
+ ).get({ $id: input.context.sessionId });
5338
+ if (row === void 0) {
5339
+ db.exec("ROLLBACK");
5340
+ return Promise.resolve();
5341
+ }
5342
+ const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
5343
+ const nextIndex = this.nextMessageIndex(input.context.sessionId);
5344
+ this.insertMessages(input.context.sessionId, input.messages, nextIndex, updatedAt);
5345
+ db.prepare(
5346
+ `UPDATE anvia_studio_sessions
5347
+ SET updated_at = $updatedAt
5348
+ WHERE id = $id`
5349
+ ).run({
5350
+ $id: input.context.sessionId,
5351
+ $updatedAt: updatedAt
5352
+ });
5353
+ db.exec("COMMIT");
5354
+ return Promise.resolve();
5355
+ } catch (error) {
5356
+ if (db.isTransaction) {
5357
+ db.exec("ROLLBACK");
5358
+ }
5359
+ throw error;
5360
+ }
5361
+ }
5362
+ clear(context) {
5363
+ const db = this.database();
5364
+ const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
5365
+ try {
5366
+ db.exec("BEGIN IMMEDIATE");
5367
+ db.prepare(
5368
+ `UPDATE anvia_studio_sessions
5369
+ SET updated_at = $updatedAt
5370
+ WHERE id = $id`
5371
+ ).run({
5372
+ $id: context.sessionId,
5373
+ $updatedAt: updatedAt
5374
+ });
5375
+ db.prepare("DELETE FROM anvia_studio_session_messages WHERE session_id = $id").run({
5376
+ $id: context.sessionId
5377
+ });
5378
+ db.prepare("DELETE FROM anvia_studio_session_runs WHERE session_id = $id").run({
5379
+ $id: context.sessionId
5380
+ });
5381
+ db.exec("COMMIT");
5382
+ return Promise.resolve();
5383
+ } catch (error) {
5384
+ if (db.isTransaction) {
5385
+ db.exec("ROLLBACK");
5386
+ }
5387
+ throw error;
5388
+ }
5389
+ }
5390
+ async recordError(input) {
5391
+ const runId = studioRunId2(input.context) ?? input.runId;
5392
+ const existing = this.getSessionRun(input.context.sessionId, runId);
5393
+ const transcript = existing === void 0 || parseJsonArray(existing.transcript_json).length === 0 ? transcriptFromMessages(input.messages) : parseJsonArray(existing.transcript_json);
5394
+ await this.saveSessionRunTranscript({
5395
+ id: input.context.sessionId,
5396
+ runId,
5397
+ transcript,
5398
+ status: "error",
5399
+ error: serializeJsonError2(input.error)
5400
+ });
5401
+ }
5402
+ saveSessionRunTranscript(input) {
5403
+ const db = this.database();
5404
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5405
+ try {
5406
+ db.exec("BEGIN IMMEDIATE");
5407
+ const row = this.getSessionRow(input.id);
5408
+ if (row === void 0) {
5409
+ db.exec("ROLLBACK");
5410
+ return void 0;
5411
+ }
5412
+ const current = toSession(
5413
+ row,
5414
+ this.listSessionMessages(input.id),
5415
+ this.listSessionRunRows(input.id)
5416
+ );
5417
+ const title = current.title ?? input.title;
5418
+ db.prepare(
5419
+ `INSERT INTO anvia_studio_session_runs (
5420
+ run_id,
5421
+ session_id,
5422
+ status,
5423
+ title,
5424
+ transcript_json,
5425
+ error_json,
5426
+ created_at,
5427
+ updated_at
5428
+ ) VALUES (
5429
+ $runId,
5430
+ $sessionId,
5431
+ $status,
5432
+ $title,
5433
+ $transcript,
5434
+ $error,
5435
+ $now,
5436
+ $now
5437
+ )
5438
+ ON CONFLICT(run_id) DO UPDATE SET
5439
+ status = excluded.status,
5440
+ title = COALESCE(anvia_studio_session_runs.title, excluded.title),
5441
+ transcript_json = excluded.transcript_json,
5442
+ error_json = excluded.error_json,
5443
+ updated_at = excluded.updated_at`
5444
+ ).run({
5445
+ $runId: input.runId,
5446
+ $sessionId: input.id,
5447
+ $status: input.status,
5448
+ $title: input.title ?? null,
5449
+ $transcript: JSON.stringify(renumberTranscript(input.transcript)),
5450
+ $error: input.error === void 0 ? null : JSON.stringify(input.error),
5451
+ $now: now
5452
+ });
5453
+ db.prepare(
5454
+ `UPDATE anvia_studio_sessions
5455
+ SET title = $title,
5456
+ updated_at = $updatedAt
5457
+ WHERE id = $id`
5458
+ ).run({
5459
+ $id: input.id,
5460
+ $title: title ?? null,
5461
+ $updatedAt: now
5462
+ });
5463
+ db.exec("COMMIT");
5464
+ const updated = this.getSession(input.id);
5465
+ return updated;
5466
+ } catch (error) {
5467
+ if (db.isTransaction) {
5468
+ db.exec("ROLLBACK");
5469
+ }
5470
+ throw error;
5471
+ }
5472
+ }
5473
+ appendSessionLog(input) {
5474
+ const db = this.database();
5475
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5476
+ try {
5477
+ db.exec("BEGIN IMMEDIATE");
5478
+ const row = this.getSessionRow(input.sessionId);
5479
+ if (row === void 0) {
5480
+ throw new Error("Session not found");
5481
+ }
5482
+ const sequence = this.nextSessionLogSequence(input.sessionId);
5483
+ const entry = {
5484
+ id: globalThis.crypto.randomUUID(),
5485
+ sessionId: input.sessionId,
5486
+ ...input.runId === void 0 ? {} : { runId: input.runId },
5487
+ sequence,
5488
+ timestamp: now,
5489
+ level: input.level,
5490
+ category: input.category,
5491
+ event: input.event,
5492
+ message: input.message,
5493
+ ...input.metadata === void 0 ? {} : { metadata: input.metadata }
5494
+ };
5495
+ db.prepare(
5496
+ `INSERT INTO anvia_studio_session_logs (
5497
+ id,
5498
+ session_id,
5499
+ run_id,
5500
+ sequence,
5501
+ timestamp,
5502
+ level,
5503
+ category,
5504
+ event,
5505
+ message,
5506
+ metadata_json
5507
+ ) VALUES (
5508
+ $id,
5509
+ $sessionId,
5510
+ $runId,
5511
+ $sequence,
5512
+ $timestamp,
5513
+ $level,
5514
+ $category,
5515
+ $event,
5516
+ $message,
5517
+ $metadata
5518
+ )`
5519
+ ).run({
5520
+ $id: entry.id,
5521
+ $sessionId: entry.sessionId,
5522
+ $runId: entry.runId ?? null,
5523
+ $sequence: entry.sequence,
5524
+ $timestamp: entry.timestamp,
5525
+ $level: entry.level,
5526
+ $category: entry.category,
5527
+ $event: entry.event,
5528
+ $message: entry.message,
5529
+ $metadata: entry.metadata === void 0 ? null : JSON.stringify(entry.metadata)
5530
+ });
5531
+ db.exec("COMMIT");
5532
+ return entry;
5533
+ } catch (error) {
5534
+ if (db.isTransaction) {
5535
+ db.exec("ROLLBACK");
5536
+ }
5537
+ throw error;
5538
+ }
5539
+ }
5540
+ listSessionLogs(options) {
5541
+ const db = this.database();
5542
+ const afterClause = options.after === void 0 ? "" : "AND sequence > $after";
5543
+ const rows = db.prepare(
5544
+ `SELECT id, session_id, run_id, sequence, timestamp, level, category, event, message,
5545
+ metadata_json
5546
+ FROM anvia_studio_session_logs
5547
+ WHERE session_id = $sessionId
5548
+ ${afterClause}
5549
+ ORDER BY sequence ASC
5550
+ LIMIT $limit`
5551
+ ).all({
5552
+ $sessionId: options.sessionId,
5553
+ $after: options.after ?? null,
5554
+ $limit: options.limit
5555
+ });
5556
+ return rows.map(toSessionLog);
5557
+ }
5558
+ appendPipelineLog(input) {
5559
+ const db = this.database();
5560
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5561
+ try {
5562
+ db.exec("BEGIN IMMEDIATE");
5563
+ const sequence = this.nextPipelineLogSequence(input.pipelineId);
5564
+ const entry = {
5565
+ id: globalThis.crypto.randomUUID(),
5566
+ pipelineId: input.pipelineId,
5567
+ ...input.runId === void 0 ? {} : { runId: input.runId },
5568
+ sequence,
5569
+ timestamp: now,
5570
+ level: input.level,
5571
+ category: input.category,
5572
+ event: input.event,
5573
+ message: input.message,
5574
+ ...input.metadata === void 0 ? {} : { metadata: input.metadata }
5575
+ };
5576
+ db.prepare(
5577
+ `INSERT INTO anvia_studio_pipeline_logs (
5578
+ id,
5579
+ pipeline_id,
5580
+ run_id,
5581
+ sequence,
5582
+ timestamp,
5583
+ level,
5584
+ category,
5585
+ event,
5586
+ message,
5587
+ metadata_json
5588
+ ) VALUES (
5589
+ $id,
5590
+ $pipelineId,
5591
+ $runId,
5592
+ $sequence,
5593
+ $timestamp,
5594
+ $level,
5595
+ $category,
5596
+ $event,
5597
+ $message,
5598
+ $metadata
5599
+ )`
5600
+ ).run({
5601
+ $id: entry.id,
5602
+ $pipelineId: entry.pipelineId,
5603
+ $runId: entry.runId ?? null,
5604
+ $sequence: entry.sequence,
5605
+ $timestamp: entry.timestamp,
5606
+ $level: entry.level,
5607
+ $category: entry.category,
5608
+ $event: entry.event,
5609
+ $message: entry.message,
5610
+ $metadata: entry.metadata === void 0 ? null : JSON.stringify(entry.metadata)
5611
+ });
5612
+ db.exec("COMMIT");
5613
+ return entry;
5614
+ } catch (error) {
5615
+ if (db.isTransaction) {
5616
+ db.exec("ROLLBACK");
5617
+ }
5618
+ throw error;
5619
+ }
5620
+ }
5621
+ listPipelineLogs(options) {
5622
+ const db = this.database();
5623
+ const afterClause = options.after === void 0 ? "" : "AND sequence > $after";
5624
+ const rows = db.prepare(
5625
+ `SELECT id, pipeline_id, run_id, sequence, timestamp, level, category, event, message,
5626
+ metadata_json
5627
+ FROM anvia_studio_pipeline_logs
5628
+ WHERE pipeline_id = $pipelineId
5629
+ ${afterClause}
5630
+ ORDER BY sequence ASC
5631
+ LIMIT $limit`
5632
+ ).all({
5633
+ $pipelineId: options.pipelineId,
5634
+ $after: options.after ?? null,
5635
+ $limit: options.limit
5636
+ });
5637
+ return rows.map(toPipelineLog);
5638
+ }
5639
+ savePipelineRun(input) {
5640
+ const db = this.database();
5641
+ db.prepare(
5642
+ `INSERT INTO anvia_studio_pipeline_runs (
5643
+ run_id,
5644
+ pipeline_id,
5645
+ status,
5646
+ input_json,
5647
+ output_json,
5648
+ error_json,
5649
+ metadata_json,
5650
+ started_at,
5651
+ ended_at,
5652
+ duration_ms
5653
+ ) VALUES (
5654
+ $runId,
5655
+ $pipelineId,
5656
+ $status,
5657
+ $input,
5658
+ $output,
5659
+ $error,
5660
+ $metadata,
5661
+ $startedAt,
5662
+ $endedAt,
5663
+ $durationMs
5664
+ )
5665
+ ON CONFLICT(run_id) DO UPDATE SET
5666
+ pipeline_id = excluded.pipeline_id,
5667
+ status = excluded.status,
5668
+ input_json = excluded.input_json,
5669
+ output_json = excluded.output_json,
5670
+ error_json = excluded.error_json,
5671
+ metadata_json = excluded.metadata_json,
5672
+ started_at = excluded.started_at,
5673
+ ended_at = excluded.ended_at,
5674
+ duration_ms = excluded.duration_ms`
5675
+ ).run({
5676
+ $runId: input.runId,
5677
+ $pipelineId: input.pipelineId,
5678
+ $status: input.status,
5679
+ $input: JSON.stringify(input.input),
5680
+ $output: input.output === void 0 ? null : JSON.stringify(input.output),
5681
+ $error: input.error === void 0 ? null : JSON.stringify(input.error),
5682
+ $metadata: input.metadata === void 0 ? null : JSON.stringify(input.metadata),
5683
+ $startedAt: input.startedAt,
5684
+ $endedAt: input.endedAt ?? null,
5685
+ $durationMs: input.durationMs ?? null
5686
+ });
5687
+ return {
5688
+ runId: input.runId,
5689
+ pipelineId: input.pipelineId,
5690
+ status: input.status,
5691
+ input: input.input,
5692
+ ...input.output === void 0 ? {} : { output: input.output },
5693
+ ...input.error === void 0 ? {} : { error: input.error },
5694
+ ...input.metadata === void 0 ? {} : { metadata: input.metadata },
5695
+ startedAt: input.startedAt,
5696
+ ...input.endedAt === void 0 ? {} : { endedAt: input.endedAt },
5697
+ ...input.durationMs === void 0 ? {} : { durationMs: input.durationMs }
5698
+ };
5699
+ }
5700
+ getPipelineRun(options) {
5701
+ const db = this.database();
5702
+ const row = db.prepare(
5703
+ `SELECT run_id, pipeline_id, status, input_json, output_json, error_json,
5704
+ metadata_json, started_at, ended_at, duration_ms
5705
+ FROM anvia_studio_pipeline_runs
5706
+ WHERE pipeline_id = $pipelineId AND run_id = $runId`
5707
+ ).get({
5708
+ $pipelineId: options.pipelineId,
5709
+ $runId: options.runId
5710
+ });
5711
+ return row === void 0 ? void 0 : toPipelineRun(row);
5712
+ }
5713
+ listPipelineRuns(options) {
5714
+ const db = this.database();
5715
+ const rows = db.prepare(
5716
+ `SELECT run_id, pipeline_id, status, input_json, output_json, error_json,
5717
+ metadata_json, started_at, ended_at, duration_ms
5718
+ FROM anvia_studio_pipeline_runs
5719
+ WHERE pipeline_id = $pipelineId
5720
+ ORDER BY started_at DESC
5721
+ LIMIT $limit`
5722
+ ).all({
5723
+ $pipelineId: options.pipelineId,
5724
+ $limit: options.limit
5725
+ });
5726
+ return rows.map(toPipelineRun);
5727
+ }
5728
+ deleteSession(id) {
5729
+ const db = this.database();
5730
+ try {
5731
+ db.exec("BEGIN IMMEDIATE");
5732
+ db.prepare("DELETE FROM anvia_studio_traces WHERE session_id = $id").run({ $id: id });
5733
+ db.prepare("DELETE FROM anvia_studio_session_runs WHERE session_id = $id").run({ $id: id });
5734
+ db.prepare("DELETE FROM anvia_studio_session_logs WHERE session_id = $id").run({ $id: id });
5735
+ const result = db.prepare("DELETE FROM anvia_studio_sessions WHERE id = $id").run({ $id: id });
5736
+ db.exec("COMMIT");
5737
+ return Number(result.changes) > 0;
5738
+ } catch (error) {
5739
+ if (db.isTransaction) {
5740
+ db.exec("ROLLBACK");
5741
+ }
5742
+ throw error;
5743
+ }
5744
+ }
5745
+ listTraces(options) {
5746
+ const db = this.database();
5747
+ const filters = [];
5748
+ const values = {
5749
+ $limit: options.limit
5750
+ };
5751
+ if (options.agentId !== void 0) {
5752
+ filters.push(
5753
+ "(s.agent_id = $agentId OR json_extract(t.metadata_json, '$.metadata.agentId') = $agentId)"
5754
+ );
5755
+ values.$agentId = options.agentId;
5756
+ }
5757
+ if (options.sessionId !== void 0) {
5758
+ filters.push("t.session_id = $sessionId");
5759
+ values.$sessionId = options.sessionId;
5760
+ }
5761
+ if (options.status !== void 0) {
5762
+ filters.push("t.status = $status");
5763
+ values.$status = options.status;
5764
+ }
5765
+ const whereClause = filters.length === 0 ? "" : `WHERE ${filters.join(" AND ")}`;
5766
+ const rows = db.prepare(
5767
+ `SELECT t.id, t.session_id, t.name, t.status, t.trace_json, t.input_json, t.output,
5768
+ t.error_json, t.usage_json, t.metadata_json, t.observations_json,
5769
+ t.started_at, t.ended_at, t.duration_ms
5770
+ FROM anvia_studio_traces t
5771
+ LEFT JOIN anvia_studio_sessions s ON s.id = t.session_id
5772
+ ${whereClause}
5773
+ ORDER BY t.started_at DESC
5774
+ LIMIT $limit`
5775
+ ).all(values);
5776
+ return rows.map(toTraceSummary);
5777
+ }
5778
+ listSessionTraces(options) {
5779
+ const db = this.database();
5780
+ const rows = db.prepare(
5781
+ `SELECT id, session_id, name, status, trace_json, input_json, output, error_json,
5782
+ usage_json, metadata_json, observations_json, started_at, ended_at, duration_ms
5783
+ FROM anvia_studio_traces
5784
+ WHERE session_id = $sessionId
5785
+ ORDER BY started_at DESC
5786
+ LIMIT $limit`
5787
+ ).all({
5788
+ $sessionId: options.sessionId,
5789
+ $limit: options.limit
5790
+ });
5791
+ return rows.map(toTraceSummary);
5792
+ }
5793
+ getTrace(id) {
5794
+ const db = this.database();
5795
+ const row = db.prepare(
5796
+ `SELECT id, session_id, name, status, trace_json, input_json, output, error_json,
5797
+ usage_json, metadata_json, observations_json, started_at, ended_at, duration_ms
5798
+ FROM anvia_studio_traces
5799
+ WHERE id = $id`
5800
+ ).get({ $id: id });
5801
+ return row === void 0 ? void 0 : toTrace(row);
5802
+ }
5803
+ saveTrace(trace) {
5804
+ const db = this.database();
5805
+ db.prepare(
5806
+ `INSERT INTO anvia_studio_traces (
5807
+ id,
5808
+ session_id,
5809
+ name,
5810
+ status,
5811
+ trace_json,
5812
+ input_json,
5813
+ output,
5814
+ error_json,
5815
+ usage_json,
5816
+ metadata_json,
5817
+ observations_json,
5818
+ started_at,
5819
+ ended_at,
5820
+ duration_ms
5821
+ ) VALUES (
5822
+ $id,
5823
+ $sessionId,
5824
+ $name,
5825
+ $status,
5826
+ $trace,
5827
+ $input,
5828
+ $output,
5829
+ $error,
5830
+ $usage,
5831
+ $metadata,
5832
+ $observations,
5833
+ $startedAt,
5834
+ $endedAt,
5835
+ $durationMs
5836
+ )
5837
+ ON CONFLICT(id) DO UPDATE SET
5838
+ session_id = excluded.session_id,
5839
+ name = excluded.name,
5840
+ status = excluded.status,
5841
+ trace_json = excluded.trace_json,
5842
+ input_json = excluded.input_json,
5843
+ output = excluded.output,
5844
+ error_json = excluded.error_json,
5845
+ usage_json = excluded.usage_json,
5846
+ metadata_json = excluded.metadata_json,
5847
+ observations_json = excluded.observations_json,
5848
+ started_at = excluded.started_at,
5849
+ ended_at = excluded.ended_at,
5850
+ duration_ms = excluded.duration_ms`
5851
+ ).run({
5852
+ $id: trace.id,
5853
+ $sessionId: trace.sessionId,
5854
+ $name: trace.name ?? null,
5855
+ $status: trace.status,
5856
+ $trace: trace.trace === void 0 ? null : JSON.stringify(trace.trace),
5857
+ $input: trace.input === void 0 ? null : JSON.stringify(trace.input),
5858
+ $output: trace.output ?? null,
5859
+ $error: trace.error === void 0 ? null : JSON.stringify(trace.error),
5860
+ $usage: trace.usage === void 0 ? null : JSON.stringify(trace.usage),
5861
+ $metadata: trace.metadata === void 0 ? null : JSON.stringify(trace.metadata),
5862
+ $observations: JSON.stringify(trace.observations),
5863
+ $startedAt: trace.startedAt,
5864
+ $endedAt: trace.endedAt ?? null,
5865
+ $durationMs: trace.durationMs ?? null
5866
+ });
5867
+ return trace;
5868
+ }
5869
+ database() {
5870
+ if (this.db !== void 0) {
5871
+ return this.db;
5872
+ }
5873
+ if (this.path !== ":memory:") {
5874
+ mkdirSync(dirname(resolve(this.path)), { recursive: true });
5875
+ }
5876
+ const db = new (loadDatabaseSync())(this.path, {
5877
+ allowUnknownNamedParameters: true,
5878
+ timeout: 5e3
5879
+ });
5880
+ db.exec(`
5881
+ PRAGMA journal_mode = WAL;
5882
+ PRAGMA foreign_keys = ON;
5883
+ `);
5884
+ guardAgainstLegacySessionSchema(db);
5885
+ db.exec(`
5886
+ CREATE TABLE IF NOT EXISTS anvia_studio_sessions (
5887
+ id TEXT PRIMARY KEY,
5888
+ agent_id TEXT NOT NULL,
5889
+ title TEXT,
5890
+ metadata_json TEXT,
5891
+ created_at TEXT NOT NULL,
5892
+ updated_at TEXT NOT NULL
5893
+ ) STRICT;
5894
+ CREATE INDEX IF NOT EXISTS anvia_studio_sessions_agent_updated_idx
5895
+ ON anvia_studio_sessions(agent_id, updated_at DESC);
5896
+ CREATE TABLE IF NOT EXISTS anvia_studio_session_messages (
5897
+ session_id TEXT NOT NULL,
5898
+ message_index INTEGER NOT NULL,
5899
+ role TEXT NOT NULL,
5900
+ message_id TEXT,
5901
+ created_at TEXT NOT NULL,
5902
+ PRIMARY KEY(session_id, message_index),
5903
+ FOREIGN KEY(session_id) REFERENCES anvia_studio_sessions(id) ON DELETE CASCADE
5904
+ ) STRICT;
5905
+ CREATE INDEX IF NOT EXISTS anvia_studio_session_messages_session_idx
5906
+ ON anvia_studio_session_messages(session_id, message_index ASC);
5907
+ CREATE TABLE IF NOT EXISTS anvia_studio_session_message_parts (
5908
+ session_id TEXT NOT NULL,
5909
+ message_index INTEGER NOT NULL,
5910
+ part_index INTEGER NOT NULL,
5911
+ type TEXT NOT NULL,
5912
+ part_json TEXT NOT NULL,
5913
+ PRIMARY KEY(session_id, message_index, part_index),
5914
+ FOREIGN KEY(session_id, message_index)
5915
+ REFERENCES anvia_studio_session_messages(session_id, message_index)
5916
+ ON DELETE CASCADE
5917
+ ) STRICT;
5918
+ CREATE TABLE IF NOT EXISTS anvia_studio_session_runs (
5919
+ run_id TEXT PRIMARY KEY,
5920
+ session_id TEXT NOT NULL,
5921
+ status TEXT NOT NULL,
5922
+ title TEXT,
5923
+ transcript_json TEXT NOT NULL,
5924
+ error_json TEXT,
5925
+ created_at TEXT NOT NULL,
5926
+ updated_at TEXT NOT NULL,
5927
+ FOREIGN KEY(session_id) REFERENCES anvia_studio_sessions(id) ON DELETE CASCADE
5928
+ ) STRICT;
5929
+ CREATE INDEX IF NOT EXISTS anvia_studio_session_runs_session_created_idx
5930
+ ON anvia_studio_session_runs(session_id, created_at ASC);
5931
+ CREATE TABLE IF NOT EXISTS anvia_studio_session_logs (
5932
+ id TEXT PRIMARY KEY,
5933
+ session_id TEXT NOT NULL,
5934
+ run_id TEXT,
5935
+ sequence INTEGER NOT NULL,
5936
+ timestamp TEXT NOT NULL,
5937
+ level TEXT NOT NULL,
5938
+ category TEXT NOT NULL,
5939
+ event TEXT NOT NULL,
5940
+ message TEXT NOT NULL,
5941
+ metadata_json TEXT,
5942
+ UNIQUE(session_id, sequence),
5943
+ FOREIGN KEY(session_id) REFERENCES anvia_studio_sessions(id) ON DELETE CASCADE
5944
+ ) STRICT;
5945
+ CREATE INDEX IF NOT EXISTS anvia_studio_session_logs_session_sequence_idx
5946
+ ON anvia_studio_session_logs(session_id, sequence ASC);
5947
+ CREATE TABLE IF NOT EXISTS anvia_studio_pipeline_logs (
5948
+ id TEXT PRIMARY KEY,
5949
+ pipeline_id TEXT NOT NULL,
5950
+ run_id TEXT,
5951
+ sequence INTEGER NOT NULL,
5952
+ timestamp TEXT NOT NULL,
5953
+ level TEXT NOT NULL,
5954
+ category TEXT NOT NULL,
5955
+ event TEXT NOT NULL,
5956
+ message TEXT NOT NULL,
5957
+ metadata_json TEXT,
5958
+ UNIQUE(pipeline_id, sequence)
5959
+ ) STRICT;
5960
+ CREATE INDEX IF NOT EXISTS anvia_studio_pipeline_logs_pipeline_sequence_idx
5961
+ ON anvia_studio_pipeline_logs(pipeline_id, sequence ASC);
5962
+ CREATE TABLE IF NOT EXISTS anvia_studio_pipeline_runs (
5963
+ run_id TEXT PRIMARY KEY,
5964
+ pipeline_id TEXT NOT NULL,
5965
+ status TEXT NOT NULL,
5966
+ input_json TEXT NOT NULL,
5967
+ output_json TEXT,
5968
+ error_json TEXT,
5969
+ metadata_json TEXT,
5970
+ started_at TEXT NOT NULL,
5971
+ ended_at TEXT,
5972
+ duration_ms INTEGER
5973
+ ) STRICT;
5974
+ CREATE INDEX IF NOT EXISTS anvia_studio_pipeline_runs_pipeline_started_idx
5975
+ ON anvia_studio_pipeline_runs(pipeline_id, started_at DESC);
5976
+ CREATE TABLE IF NOT EXISTS anvia_studio_traces (
5977
+ id TEXT PRIMARY KEY,
5978
+ session_id TEXT NOT NULL,
5979
+ name TEXT,
5980
+ status TEXT NOT NULL,
5981
+ trace_json TEXT,
5982
+ input_json TEXT,
5983
+ output TEXT,
5984
+ error_json TEXT,
5985
+ usage_json TEXT,
5986
+ metadata_json TEXT,
5987
+ observations_json TEXT NOT NULL,
5988
+ started_at TEXT NOT NULL,
5989
+ ended_at TEXT,
5990
+ duration_ms INTEGER
5991
+ ) STRICT;
5992
+ CREATE INDEX IF NOT EXISTS anvia_studio_traces_session_started_idx
5993
+ ON anvia_studio_traces(session_id, started_at DESC);
5994
+ `);
5995
+ this.db = db;
5996
+ return db;
5997
+ }
5998
+ getSessionRow(id) {
5999
+ return this.database().prepare(
6000
+ `SELECT id, agent_id, title, metadata_json, created_at, updated_at
6001
+ FROM anvia_studio_sessions
6002
+ WHERE id = $id`
6003
+ ).get({ $id: id });
6004
+ }
6005
+ getSessionRun(sessionId, runId) {
6006
+ return this.database().prepare(
6007
+ `SELECT run_id, session_id, status, title, transcript_json, error_json, created_at, updated_at
6008
+ FROM anvia_studio_session_runs
6009
+ WHERE session_id = $sessionId AND run_id = $runId`
6010
+ ).get({ $sessionId: sessionId, $runId: runId });
6011
+ }
6012
+ listSessionRunRows(sessionId) {
6013
+ return this.database().prepare(
6014
+ `SELECT run_id, session_id, status, title, transcript_json, error_json, created_at, updated_at
6015
+ FROM anvia_studio_session_runs
6016
+ WHERE session_id = $sessionId
6017
+ ORDER BY created_at ASC`
6018
+ ).all({ $sessionId: sessionId });
6019
+ }
6020
+ nextSessionLogSequence(sessionId) {
6021
+ const row = this.database().prepare(
6022
+ `SELECT COALESCE(MAX(sequence) + 1, 0) AS next_sequence
6023
+ FROM anvia_studio_session_logs
6024
+ WHERE session_id = $sessionId`
6025
+ ).get({ $sessionId: sessionId });
6026
+ return row.next_sequence;
6027
+ }
6028
+ nextPipelineLogSequence(pipelineId) {
6029
+ const row = this.database().prepare(
6030
+ `SELECT COALESCE(MAX(sequence) + 1, 0) AS next_sequence
6031
+ FROM anvia_studio_pipeline_logs
6032
+ WHERE pipeline_id = $pipelineId`
6033
+ ).get({ $pipelineId: pipelineId });
6034
+ return row.next_sequence;
6035
+ }
6036
+ listSessionMessages(sessionId) {
6037
+ const db = this.database();
6038
+ const messageRows = db.prepare(
6039
+ `SELECT session_id, message_index, role, message_id, created_at
6040
+ FROM anvia_studio_session_messages
6041
+ WHERE session_id = $sessionId
6042
+ ORDER BY message_index ASC`
6043
+ ).all({ $sessionId: sessionId });
6044
+ if (messageRows.length === 0) {
6045
+ return [];
6046
+ }
6047
+ const partRows = db.prepare(
6048
+ `SELECT session_id, message_index, part_index, type, part_json
6049
+ FROM anvia_studio_session_message_parts
6050
+ WHERE session_id = $sessionId
6051
+ ORDER BY message_index ASC, part_index ASC`
6052
+ ).all({ $sessionId: sessionId });
6053
+ const partsByMessage = /* @__PURE__ */ new Map();
6054
+ for (const partRow of partRows) {
6055
+ const parts = partsByMessage.get(partRow.message_index) ?? [];
6056
+ parts.push(partRow);
6057
+ partsByMessage.set(partRow.message_index, parts);
6020
6058
  }
6021
- const memoryMetadata = {
6022
- agentId,
6023
- ...body.metadata ?? {},
6024
- studioRunId: runId
6025
- };
6026
- const request = session !== void 0 ? agent.agent.session(session.id, { metadata: memoryMetadata }).prompt(body.message) : agent.agent.prompt(
6027
- body.history !== void 0 ? [...body.history, normalizePromptMessage(body.message)] : body.message
6059
+ return messageRows.map(
6060
+ (row) => messageFromRows(row, partsByMessage.get(row.message_index) ?? [])
6028
6061
  );
6029
- if (body.maxTurns !== void 0) {
6030
- request.maxTurns(body.maxTurns);
6031
- }
6032
- if (body.toolConcurrency !== void 0) {
6033
- request.withToolConcurrency(body.toolConcurrency);
6034
- }
6035
- if (body.trace !== void 0) {
6036
- request.withTrace(traceForRun(body.trace, agentId, session));
6037
- } else if (session !== void 0) {
6038
- request.withTrace(traceForRun(void 0, agentId, session));
6039
- }
6040
- if (body.stream === true) {
6041
- const runtimeEvents = new AsyncEventQueue();
6042
- const effectiveHook = composeHooks(
6043
- composeHooks(
6044
- agent.agent.hook,
6045
- approvalRuntime.createHook({
6046
- runId,
6047
- agentId,
6048
- ...session?.id === void 0 ? {} : { sessionId: session.id },
6049
- ...body.metadata === void 0 ? {} : { metadata: body.metadata },
6050
- getTool: (toolName) => agent.agent.getTool(toolName),
6051
- emit: (event) => runtimeEvents.push(event)
6052
- })
6053
- ),
6054
- questionRuntime.createHook({
6055
- runId,
6056
- agentId,
6057
- ...session?.id === void 0 ? {} : { sessionId: session.id },
6058
- ...body.metadata === void 0 ? {} : { metadata: body.metadata },
6059
- emit: (event) => runtimeEvents.push(event)
6060
- })
6061
- );
6062
- if (effectiveHook !== void 0) {
6063
- request.requestHook(effectiveHook);
6064
- }
6065
- const runStream = mergeRunAndApprovalEvents(request.stream(), runtimeEvents);
6066
- const stream = session === void 0 || stores.sessions === void 0 ? runStream : persistStreamingSessionTranscript({
6067
- stream: streamSessionRunLogs({
6068
- stream: runStream,
6069
- store: stores.sessions,
6070
- session,
6071
- runId,
6072
- startedAt: runStartedAt
6073
- }),
6074
- store: stores.sessions,
6075
- session,
6076
- message: body.message,
6077
- runId
6062
+ }
6063
+ nextMessageIndex(sessionId) {
6064
+ const row = this.database().prepare(
6065
+ `SELECT COALESCE(MAX(message_index) + 1, 0) AS next_index
6066
+ FROM anvia_studio_session_messages
6067
+ WHERE session_id = $sessionId`
6068
+ ).get({ $sessionId: sessionId });
6069
+ return row.next_index;
6070
+ }
6071
+ insertMessages(sessionId, messages, startIndex, createdAt) {
6072
+ const db = this.database();
6073
+ const insertMessage = db.prepare(
6074
+ `INSERT INTO anvia_studio_session_messages (
6075
+ session_id,
6076
+ message_index,
6077
+ role,
6078
+ message_id,
6079
+ created_at
6080
+ ) VALUES ($sessionId, $messageIndex, $role, $messageId, $createdAt)`
6081
+ );
6082
+ const insertPart = db.prepare(
6083
+ `INSERT INTO anvia_studio_session_message_parts (
6084
+ session_id,
6085
+ message_index,
6086
+ part_index,
6087
+ type,
6088
+ part_json
6089
+ ) VALUES ($sessionId, $messageIndex, $partIndex, $type, $partJson)`
6090
+ );
6091
+ messages.forEach((message, messageOffset) => {
6092
+ const messageIndex = startIndex + messageOffset;
6093
+ insertMessage.run({
6094
+ $sessionId: sessionId,
6095
+ $messageIndex: messageIndex,
6096
+ $role: message.role,
6097
+ $messageId: message.role === "assistant" ? message.id ?? null : null,
6098
+ $createdAt: createdAt
6078
6099
  });
6079
- return streamAgentRunEvents(c, stream);
6080
- }
6081
- try {
6082
- if (session !== void 0) {
6083
- await appendSessionLog(stores.sessions, runStartedLog(session, runId));
6084
- await appendSessionLog(stores.sessions, memoryLoadedLog(session, runId));
6085
- }
6086
- const effectiveHook = composeHooks(
6087
- composeHooks(
6088
- agent.agent.hook,
6089
- approvalRuntime.createHook({
6090
- runId,
6091
- agentId,
6092
- ...session?.id === void 0 ? {} : { sessionId: session.id },
6093
- ...body.metadata === void 0 ? {} : { metadata: body.metadata },
6094
- getTool: (toolName) => agent.agent.getTool(toolName)
6095
- })
6096
- ),
6097
- questionRuntime.createHook({
6098
- runId,
6099
- agentId,
6100
- ...session?.id === void 0 ? {} : { sessionId: session.id },
6101
- ...body.metadata === void 0 ? {} : { metadata: body.metadata }
6102
- })
6103
- );
6104
- if (effectiveHook !== void 0) {
6105
- request.requestHook(effectiveHook);
6106
- }
6107
- const response = await request.send();
6108
- if (session !== void 0 && stores.sessions !== void 0) {
6109
- await stores.sessions.saveSessionRunTranscript({
6110
- id: session.id,
6111
- runId,
6112
- ...optionalTitle(body.message),
6113
- transcript: transcriptFromMessages(response.messages),
6114
- status: "success"
6115
- });
6116
- await appendSessionLog(
6117
- stores.sessions,
6118
- runCompletedLog({
6119
- sessionId: session.id,
6120
- runId,
6121
- durationMs: Date.now() - runStartedAt,
6122
- usage: response.usage,
6123
- output: response.output,
6124
- messageCount: response.messages.length
6125
- })
6126
- );
6127
- await appendSessionLog(
6128
- stores.sessions,
6129
- memorySavedLog({
6130
- sessionId: session.id,
6131
- runId,
6132
- messageCount: response.messages.length
6133
- })
6134
- );
6135
- }
6136
- return c.json(response);
6137
- } catch (error) {
6138
- if (session !== void 0 && stores.sessions !== void 0) {
6139
- const messages = await stores.sessions.load({
6140
- sessionId: session.id,
6141
- metadata: memoryMetadata
6142
- });
6143
- await stores.sessions.saveSessionRunTranscript({
6144
- id: session.id,
6145
- runId,
6146
- ...optionalTitle(body.message),
6147
- transcript: transcriptFromMessages(messages.slice(session.messageCount)),
6148
- status: "error",
6149
- error: serializeError(error)
6100
+ messageParts(message).forEach((part, partIndex) => {
6101
+ insertPart.run({
6102
+ $sessionId: sessionId,
6103
+ $messageIndex: messageIndex,
6104
+ $partIndex: partIndex,
6105
+ $type: part.type,
6106
+ $partJson: JSON.stringify(part.value)
6150
6107
  });
6151
- await appendSessionLog(
6152
- stores.sessions,
6153
- runFailedLog(session.id, runId, error, runStartedAt)
6154
- );
6155
- }
6156
- return errorResponse(c, 500, "internal_error", "Agent run failed", serializeError(error));
6157
- }
6158
- });
6159
- if (stores.sessions !== void 0) {
6160
- registerMemoryRoutes(app, {
6161
- sessionStore: stores.sessions
6162
- });
6163
- registerSessionRoutes(app, {
6164
- agentMap,
6165
- sessionStore: stores.sessions,
6166
- ...stores.traces === void 0 ? {} : { traceStore: stores.traces }
6108
+ });
6167
6109
  });
6168
6110
  }
6169
- if (stores.traces !== void 0) {
6170
- registerTraceRoutes(app, stores.traces);
6111
+ };
6112
+ function toSession(row, messages, runRows = []) {
6113
+ const summary = toSessionSummary({ ...row, message_count: messages.length });
6114
+ const runTranscript = runRows.flatMap(
6115
+ (runRow) => parseJsonArray(runRow.transcript_json)
6116
+ );
6117
+ return {
6118
+ ...summary,
6119
+ messages,
6120
+ transcript: renumberTranscript(runTranscript)
6121
+ };
6122
+ }
6123
+ function toSessionSummary(row) {
6124
+ const metadata = parseJsonValue(row.metadata_json);
6125
+ return {
6126
+ id: row.id,
6127
+ agentId: row.agent_id,
6128
+ ...row.title === null ? {} : { title: row.title },
6129
+ createdAt: row.created_at,
6130
+ updatedAt: row.updated_at,
6131
+ messageCount: row.message_count,
6132
+ ...metadata === void 0 ? {} : { metadata }
6133
+ };
6134
+ }
6135
+ function toSessionLog(row) {
6136
+ const metadata = parseJsonValue(row.metadata_json);
6137
+ return {
6138
+ id: row.id,
6139
+ sessionId: row.session_id,
6140
+ ...row.run_id === null ? {} : { runId: row.run_id },
6141
+ sequence: row.sequence,
6142
+ timestamp: row.timestamp,
6143
+ level: row.level,
6144
+ category: row.category,
6145
+ event: row.event,
6146
+ message: row.message,
6147
+ ...metadata === void 0 ? {} : { metadata }
6148
+ };
6149
+ }
6150
+ function toPipelineLog(row) {
6151
+ const metadata = parseJsonValue(row.metadata_json);
6152
+ return {
6153
+ id: row.id,
6154
+ pipelineId: row.pipeline_id,
6155
+ ...row.run_id === null ? {} : { runId: row.run_id },
6156
+ sequence: row.sequence,
6157
+ timestamp: row.timestamp,
6158
+ level: row.level,
6159
+ category: row.category,
6160
+ event: row.event,
6161
+ message: row.message,
6162
+ ...metadata === void 0 ? {} : { metadata }
6163
+ };
6164
+ }
6165
+ function toPipelineRun(row) {
6166
+ const output = parseJsonValue(row.output_json);
6167
+ const error = parseJsonValue(row.error_json);
6168
+ const metadata = parseJsonValue(row.metadata_json);
6169
+ return {
6170
+ runId: row.run_id,
6171
+ pipelineId: row.pipeline_id,
6172
+ status: row.status,
6173
+ input: JSON.parse(row.input_json),
6174
+ ...output === void 0 ? {} : { output },
6175
+ ...error === void 0 ? {} : { error },
6176
+ ...metadata === void 0 ? {} : { metadata },
6177
+ startedAt: row.started_at,
6178
+ ...row.ended_at === null ? {} : { endedAt: row.ended_at },
6179
+ ...row.duration_ms === null ? {} : { durationMs: row.duration_ms }
6180
+ };
6181
+ }
6182
+ function messageParts(message) {
6183
+ if (message.role === "system") {
6184
+ return [{ type: "text", value: { type: "text", text: message.content } }];
6185
+ }
6186
+ return message.content.map((content) => ({
6187
+ type: content.type,
6188
+ value: content
6189
+ }));
6190
+ }
6191
+ function messageFromRows(row, partRows) {
6192
+ const parts = partRows.map((partRow) => JSON.parse(partRow.part_json));
6193
+ if (row.role === "system") {
6194
+ return { role: "system", content: systemContentFromParts(parts) };
6171
6195
  }
6172
- for (const capability of unsupportedCapabilities(stores)) {
6173
- app.all(`/${capability}`, (c) => unsupportedCapability(c, capability));
6174
- app.all(`/${capability}/*`, (c) => unsupportedCapability(c, capability));
6196
+ if (row.role === "user") {
6197
+ return {
6198
+ role: "user",
6199
+ content: parts
6200
+ };
6175
6201
  }
6176
- return {
6177
- app,
6178
- fetch(request) {
6179
- return app.fetch(request);
6180
- },
6181
- config() {
6182
- return buildConfig(options, agents, pipelines, stores);
6183
- },
6184
- close() {
6185
- },
6186
- ...stores.sessions === void 0 ? {} : { sessionStore: stores.sessions },
6187
- ...stores.traces === void 0 ? {} : { traceStore: stores.traces }
6188
- };
6202
+ if (row.role === "assistant") {
6203
+ return {
6204
+ role: "assistant",
6205
+ ...row.message_id === null ? {} : { id: row.message_id },
6206
+ content: parts
6207
+ };
6208
+ }
6209
+ if (row.role === "tool") {
6210
+ return {
6211
+ role: "tool",
6212
+ content: parts
6213
+ };
6214
+ }
6215
+ throw new Error(`Unsupported stored message role: ${row.role}`);
6189
6216
  }
6190
- function normalizePromptMessage(message) {
6191
- return typeof message === "string" ? Message.user(message) : message;
6217
+ function systemContentFromParts(parts) {
6218
+ const first = parts[0];
6219
+ if (typeof first === "object" && first !== null && "type" in first && first.type === "text" && "text" in first && typeof first.text === "string") {
6220
+ return first.text;
6221
+ }
6222
+ return "";
6192
6223
  }
6193
- function withStudioSessionMemory(studioAgent, sessionStore) {
6194
- if (sessionStore === void 0) {
6195
- return studioAgent;
6224
+ function guardAgainstLegacySessionSchema(db) {
6225
+ const columns = db.prepare("PRAGMA table_info('anvia_studio_sessions')").all();
6226
+ if (columns.some((column) => column.name === "messages_json")) {
6227
+ throw new Error(
6228
+ "Existing Studio SQLite DB uses the legacy messages_json schema. Delete or recreate the Studio SQLite DB to use normalized session messages."
6229
+ );
6230
+ }
6231
+ }
6232
+ function loadDatabaseSync() {
6233
+ if (DatabaseSync !== void 0) {
6234
+ return DatabaseSync;
6235
+ }
6236
+ try {
6237
+ ({ DatabaseSync } = createRequire(import.meta.url)(
6238
+ "node:sqlite"
6239
+ ));
6240
+ return DatabaseSync;
6241
+ } catch (error) {
6242
+ throw new Error(
6243
+ "The default Studio SQLite store requires Node.js with node:sqlite support. Provide custom Studio stores or disable persisted stores when running Studio in a runtime without node:sqlite.",
6244
+ { cause: error }
6245
+ );
6196
6246
  }
6247
+ }
6248
+ function toTrace(row) {
6249
+ const trace = parseJsonValue(row.trace_json);
6250
+ const input = parseJsonValue(row.input_json);
6197
6251
  return {
6198
- ...studioAgent,
6199
- agent: cloneAgent(studioAgent.agent, {
6200
- memory: {
6201
- store: sessionStore,
6202
- options: resolveMemoryOptions({ savePolicy: "message" })
6203
- }
6204
- })
6252
+ ...toTraceSummary(row),
6253
+ ...trace === void 0 ? {} : { trace },
6254
+ ...input === void 0 ? {} : { input },
6255
+ observations: parseJsonArray(row.observations_json)
6205
6256
  };
6206
6257
  }
6207
- function withStudioTraceObserver(studioAgent, traceStore) {
6208
- if (traceStore === void 0 || hasStudioTraceObserver(studioAgent.agent)) {
6209
- return studioAgent;
6210
- }
6258
+ function toTraceSummary(row) {
6259
+ const observations = parseJsonArray(row.observations_json);
6260
+ const error = parseJsonValue(row.error_json);
6261
+ const usage = parseJsonValue(row.usage_json);
6262
+ const metadata = parseJsonValue(row.metadata_json);
6211
6263
  return {
6212
- ...studioAgent,
6213
- agent: cloneAgent(studioAgent.agent, {
6214
- observers: [
6215
- ...studioAgent.agent.observers,
6216
- { observer: new StudioTraceObserver({ store: traceStore }) }
6217
- ]
6218
- })
6264
+ id: row.id,
6265
+ sessionId: row.session_id,
6266
+ ...row.name === null ? {} : { name: row.name },
6267
+ status: row.status,
6268
+ startedAt: row.started_at,
6269
+ ...row.ended_at === null ? {} : { endedAt: row.ended_at },
6270
+ ...row.duration_ms === null ? {} : { durationMs: row.duration_ms },
6271
+ ...row.output === null ? {} : { output: row.output },
6272
+ ...error === void 0 ? {} : { error },
6273
+ ...usage === void 0 ? {} : { usage },
6274
+ ...metadata === void 0 ? {} : { metadata },
6275
+ observationCount: observations.length
6219
6276
  };
6220
6277
  }
6221
- function cloneAgent(agent, overrides = {}) {
6222
- return new Agent({
6223
- id: agent.id,
6224
- name: agent.name,
6225
- description: agent.description,
6226
- model: agent.model,
6227
- instructions: agent.instructions,
6228
- staticContext: agent.staticContext,
6229
- temperature: agent.temperature,
6230
- maxTokens: agent.maxTokens,
6231
- additionalParams: agent.additionalParams,
6232
- toolSet: agent.toolSet,
6233
- toolChoice: agent.toolChoice,
6234
- defaultMaxTurns: agent.defaultMaxTurns,
6235
- hook: agent.hook,
6236
- outputSchema: agent.outputSchema,
6237
- observers: agent.observers,
6238
- dynamicContexts: agent.dynamicContexts,
6239
- dynamicTools: agent.dynamicTools,
6240
- memory: agent.memory,
6241
- ...overrides
6242
- });
6278
+ function parseJsonArray(value) {
6279
+ const parsed = JSON.parse(value);
6280
+ return Array.isArray(parsed) ? parsed : [];
6243
6281
  }
6244
- function hasStudioTraceObserver(agent) {
6245
- return agent.observers.some(
6246
- (registration) => registration.observer instanceof StudioTraceObserver
6247
- );
6282
+ function parseJsonValue(value) {
6283
+ if (value === null) {
6284
+ return void 0;
6285
+ }
6286
+ return JSON.parse(value);
6248
6287
  }
6249
- function composeHooks(first, second) {
6250
- if (first === void 0) {
6251
- return second;
6288
+ function studioRunId2(context) {
6289
+ const value = context.metadata?.studioRunId;
6290
+ return typeof value === "string" && value.length > 0 ? value : void 0;
6291
+ }
6292
+ function serializeJsonError2(error) {
6293
+ if (error instanceof Error) {
6294
+ return {
6295
+ name: error.name,
6296
+ message: error.message
6297
+ };
6252
6298
  }
6253
- if (second === void 0) {
6254
- return first;
6299
+ if (error === null || typeof error === "string" || typeof error === "number" || typeof error === "boolean") {
6300
+ return error;
6255
6301
  }
6256
- return createHook3({
6257
- async onCompletionCall(args) {
6258
- const firstAction = await first.onCompletionCall?.(args);
6259
- return firstAction?.type === "terminate" ? firstAction : await second.onCompletionCall?.(args) ?? void 0;
6260
- },
6261
- async onCompletionResponse(args) {
6262
- const firstAction = await first.onCompletionResponse?.(args);
6263
- return firstAction?.type === "terminate" ? firstAction : await second.onCompletionResponse?.(args) ?? void 0;
6264
- },
6265
- async onToolCall(args) {
6266
- const firstAction = await first.onToolCall?.(args);
6267
- if (firstAction?.type === "skip" || firstAction?.type === "terminate") {
6268
- return firstAction;
6269
- }
6270
- if (firstAction?.type === "approval_request") {
6271
- return await approvalRequestHandler(second)?.(args, firstAction) ?? firstAction;
6272
- }
6273
- const secondAction = await second.onToolCall?.(args);
6274
- return secondAction ?? firstAction ?? void 0;
6275
- },
6276
- async onToolResult(args) {
6277
- const firstAction = await first.onToolResult?.(args);
6278
- return firstAction?.type === "terminate" ? firstAction : await second.onToolResult?.(args) ?? void 0;
6279
- }
6280
- });
6281
- }
6282
- function approvalRequestHandler(hook) {
6283
- const candidate = hook;
6284
- return typeof candidate.handleApprovalRequest === "function" ? candidate.handleApprovalRequest : void 0;
6302
+ return String(error);
6285
6303
  }
6286
6304
  export {
6287
6305
  Studio,