@anvia/studio 0.2.0 → 0.2.2

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
@@ -51,11 +51,7 @@ var StudioRunTraceObserver = class {
51
51
  startedAt,
52
52
  input: toJsonValue(args.request),
53
53
  output: toJsonValue(endArgs.response),
54
- metadata: {
55
- model: args.request.model ?? "default",
56
- toolCount: args.request.tools.length,
57
- ...endArgs.firstDeltaMs === void 0 ? {} : { firstDeltaMs: endArgs.firstDeltaMs }
58
- }
54
+ metadata: generationMetadata(args, endArgs)
59
55
  })
60
56
  );
61
57
  },
@@ -69,10 +65,7 @@ var StudioRunTraceObserver = class {
69
65
  startedAt,
70
66
  input: toJsonValue(args.request),
71
67
  error: serializeError(errorArgs.error),
72
- metadata: {
73
- model: args.request.model ?? "default",
74
- toolCount: args.request.tools.length
75
- }
68
+ metadata: generationMetadata(args)
76
69
  })
77
70
  );
78
71
  }
@@ -94,7 +87,7 @@ var StudioRunTraceObserver = class {
94
87
  startedAt,
95
88
  input: parseOrString(args.args),
96
89
  output: parseOrString(endArgs.result),
97
- metadata: toolMetadata(args, endArgs.skipped)
90
+ metadata: toolMetadata(args, endArgs.skipped, endArgs.result)
98
91
  });
99
92
  this.observations.push(parentObservation);
100
93
  this.observations.push(...childTrace.observations(parentObservation.id));
@@ -372,13 +365,134 @@ function traceMetadata(args, messages) {
372
365
  messages
373
366
  });
374
367
  }
375
- function toolMetadata(args, skipped) {
368
+ function generationMetadata(args, endArgs) {
369
+ const request = args.request;
370
+ const response = endArgs?.response;
371
+ const rawResponse = isRecord(response?.rawResponse) ? response.rawResponse : void 0;
372
+ const effectiveModel = request.model ?? stringValue(rawResponse?.model) ?? args.modelInfo?.defaultModel ?? "default";
373
+ const providerResponse = providerResponseSummary(rawResponse);
374
+ const usage = response?.usage;
375
+ return compactJsonObject({
376
+ provider: args.modelInfo?.provider,
377
+ model: effectiveModel,
378
+ requestedModel: request.model,
379
+ defaultModel: args.modelInfo?.defaultModel,
380
+ messageId: response?.messageId,
381
+ usage,
382
+ toolCount: request.tools.length,
383
+ toolNames: request.tools.map((tool) => tool.name),
384
+ documentCount: request.documents.length,
385
+ historyCount: request.chatHistory.length,
386
+ temperature: request.temperature,
387
+ maxTokens: request.maxTokens,
388
+ toolChoice: request.toolChoice,
389
+ additionalParamKeys: isRecord(request.additionalParams) ? Object.keys(request.additionalParams).sort() : void 0,
390
+ hasOutputSchema: request.outputSchema !== void 0,
391
+ firstDeltaMs: endArgs?.firstDeltaMs,
392
+ providerResponse,
393
+ modelInfo: compactJsonObject({
394
+ provider: args.modelInfo?.provider,
395
+ model: effectiveModel,
396
+ requestedModel: request.model,
397
+ defaultModel: args.modelInfo?.defaultModel,
398
+ capabilities: args.modelInfo?.capabilities
399
+ }),
400
+ modelCall: compactJsonObject({
401
+ request: completionRequestSummary(request),
402
+ providerRequest: args.providerRequest
403
+ }),
404
+ response: compactJsonObject({
405
+ messageId: response?.messageId,
406
+ usage,
407
+ contentTypes: response?.choice.map((item) => item.type),
408
+ providerResponse
409
+ }),
410
+ tools: compactJsonObject({
411
+ count: request.tools.length,
412
+ names: request.tools.map((tool) => tool.name),
413
+ toolChoice: request.toolChoice,
414
+ hasOutputSchema: request.outputSchema !== void 0
415
+ }),
416
+ timing: compactJsonObject({
417
+ firstDeltaMs: endArgs?.firstDeltaMs
418
+ })
419
+ });
420
+ }
421
+ function completionRequestSummary(request) {
422
+ return compactJsonObject({
423
+ model: request.model,
424
+ instructions: request.instructions === void 0 ? void 0 : { present: true },
425
+ messageCount: request.chatHistory.length,
426
+ documentCount: request.documents.length,
427
+ documentIds: request.documents.map((document) => document.id),
428
+ toolCount: request.tools.length,
429
+ toolNames: request.tools.map((tool) => tool.name),
430
+ temperature: request.temperature,
431
+ maxTokens: request.maxTokens,
432
+ toolChoice: request.toolChoice,
433
+ additionalParamKeys: isRecord(request.additionalParams) ? Object.keys(request.additionalParams).sort() : void 0,
434
+ hasOutputSchema: request.outputSchema !== void 0
435
+ });
436
+ }
437
+ function providerResponseSummary(rawResponse) {
438
+ if (rawResponse === void 0) {
439
+ return void 0;
440
+ }
441
+ const reasoning = isRecord(rawResponse.reasoning) ? rawResponse.reasoning : void 0;
442
+ const text = isRecord(rawResponse.text) ? rawResponse.text : void 0;
443
+ const toolUsage = isRecord(rawResponse.tool_usage) ? rawResponse.tool_usage : void 0;
444
+ const webSearch = isRecord(toolUsage?.web_search) ? toolUsage.web_search : void 0;
445
+ const summary = compactJsonObject({
446
+ id: rawResponse.id,
447
+ status: rawResponse.status,
448
+ serviceTier: rawResponse.service_tier,
449
+ store: rawResponse.store,
450
+ parallelToolCalls: rawResponse.parallel_tool_calls,
451
+ promptCacheKey: rawResponse.prompt_cache_key,
452
+ promptCacheRetention: rawResponse.prompt_cache_retention,
453
+ reasoningEffort: reasoning?.effort,
454
+ textVerbosity: text?.verbosity,
455
+ webSearchRequestCount: webSearch?.num_requests
456
+ });
457
+ return Object.keys(summary).length === 0 ? void 0 : summary;
458
+ }
459
+ function toolMetadata(args, skipped, result) {
460
+ const schema = isRecord(args.toolDefinition?.parameters) ? args.toolDefinition.parameters : {};
461
+ const properties = isRecord(schema.properties) ? schema.properties : {};
462
+ const required = Array.isArray(schema.required) ? schema.required.filter((item) => typeof item === "string") : [];
376
463
  return compactJsonObject({
377
464
  internalCallId: args.internalCallId,
378
465
  toolCallId: args.toolCallId,
379
- skipped
466
+ skipped,
467
+ argumentBytes: byteLength(args.args),
468
+ resultBytes: result === void 0 ? void 0 : byteLength(result),
469
+ hasCallSignature: args.toolCall.signature !== void 0,
470
+ hasAdditionalParams: args.toolCall.additionalParams !== void 0,
471
+ toolDescription: args.toolDefinition?.description,
472
+ parameterKeys: Object.keys(properties).sort(),
473
+ requiredParameterKeys: required,
474
+ approvalRequired: args.toolMetadata?.approvalRequired,
475
+ mcpServerName: args.toolMetadata?.mcpServerName,
476
+ tools: compactJsonObject({
477
+ name: args.toolName,
478
+ internalCallId: args.internalCallId,
479
+ toolCallId: args.toolCallId,
480
+ skipped,
481
+ description: args.toolDefinition?.description,
482
+ parameterKeys: Object.keys(properties).sort(),
483
+ requiredParameterKeys: required,
484
+ approvalRequired: args.toolMetadata?.approvalRequired,
485
+ mcpServerName: args.toolMetadata?.mcpServerName,
486
+ argumentBytes: byteLength(args.args),
487
+ resultBytes: result === void 0 ? void 0 : byteLength(result),
488
+ hasCallSignature: args.toolCall.signature !== void 0,
489
+ hasAdditionalParams: args.toolCall.additionalParams !== void 0
490
+ })
380
491
  });
381
492
  }
493
+ function byteLength(value) {
494
+ return new TextEncoder().encode(value).length;
495
+ }
382
496
  function compactJsonObject(values) {
383
497
  const entries = Object.entries(values).flatMap(
384
498
  ([key, value]) => value === void 0 ? [] : [[key, toJsonValue(value)]]
@@ -473,6 +587,8 @@ function registerStudioUi(app, options) {
473
587
  app.get(`${options.path}/sessions`, async (c) => c.html(await renderShell()));
474
588
  app.get(`${options.path}/agents`, async (c) => c.html(await renderShell()));
475
589
  app.get(`${options.path}/knowledge`, async (c) => c.html(await renderShell()));
590
+ app.get(`${options.path}/knowledge/:tab`, async (c) => c.html(await renderShell()));
591
+ app.get(`${options.path}/knowledge/*`, async (c) => c.html(await renderShell()));
476
592
  if (options.rootRoutes) {
477
593
  app.get("/playground", async (c) => c.html(await renderRootShell()));
478
594
  app.get("/playground/:sessionId", async (c) => c.html(await renderRootShell()));
@@ -1056,6 +1172,82 @@ var SqliteSessionStore = class {
1056
1172
  });
1057
1173
  return rows.map(toPipelineLog);
1058
1174
  }
1175
+ savePipelineRun(input) {
1176
+ const db = this.database();
1177
+ db.prepare(
1178
+ `INSERT INTO runner_pipeline_runs (
1179
+ run_id,
1180
+ pipeline_id,
1181
+ status,
1182
+ input_json,
1183
+ output_json,
1184
+ error_json,
1185
+ metadata_json,
1186
+ started_at,
1187
+ ended_at,
1188
+ duration_ms
1189
+ ) VALUES (
1190
+ $runId,
1191
+ $pipelineId,
1192
+ $status,
1193
+ $input,
1194
+ $output,
1195
+ $error,
1196
+ $metadata,
1197
+ $startedAt,
1198
+ $endedAt,
1199
+ $durationMs
1200
+ )
1201
+ ON CONFLICT(run_id) DO UPDATE SET
1202
+ pipeline_id = excluded.pipeline_id,
1203
+ status = excluded.status,
1204
+ input_json = excluded.input_json,
1205
+ output_json = excluded.output_json,
1206
+ error_json = excluded.error_json,
1207
+ metadata_json = excluded.metadata_json,
1208
+ started_at = excluded.started_at,
1209
+ ended_at = excluded.ended_at,
1210
+ duration_ms = excluded.duration_ms`
1211
+ ).run({
1212
+ $runId: input.runId,
1213
+ $pipelineId: input.pipelineId,
1214
+ $status: input.status,
1215
+ $input: JSON.stringify(input.input),
1216
+ $output: input.output === void 0 ? null : JSON.stringify(input.output),
1217
+ $error: input.error === void 0 ? null : JSON.stringify(input.error),
1218
+ $metadata: input.metadata === void 0 ? null : JSON.stringify(input.metadata),
1219
+ $startedAt: input.startedAt,
1220
+ $endedAt: input.endedAt ?? null,
1221
+ $durationMs: input.durationMs ?? null
1222
+ });
1223
+ return {
1224
+ runId: input.runId,
1225
+ pipelineId: input.pipelineId,
1226
+ status: input.status,
1227
+ input: input.input,
1228
+ ...input.output === void 0 ? {} : { output: input.output },
1229
+ ...input.error === void 0 ? {} : { error: input.error },
1230
+ ...input.metadata === void 0 ? {} : { metadata: input.metadata },
1231
+ startedAt: input.startedAt,
1232
+ ...input.endedAt === void 0 ? {} : { endedAt: input.endedAt },
1233
+ ...input.durationMs === void 0 ? {} : { durationMs: input.durationMs }
1234
+ };
1235
+ }
1236
+ listPipelineRuns(options) {
1237
+ const db = this.database();
1238
+ const rows = db.prepare(
1239
+ `SELECT run_id, pipeline_id, status, input_json, output_json, error_json,
1240
+ metadata_json, started_at, ended_at, duration_ms
1241
+ FROM runner_pipeline_runs
1242
+ WHERE pipeline_id = $pipelineId
1243
+ ORDER BY started_at DESC
1244
+ LIMIT $limit`
1245
+ ).all({
1246
+ $pipelineId: options.pipelineId,
1247
+ $limit: options.limit
1248
+ });
1249
+ return rows.map(toPipelineRun);
1250
+ }
1059
1251
  deleteSession(id) {
1060
1252
  const db = this.database();
1061
1253
  try {
@@ -1290,6 +1482,20 @@ var SqliteSessionStore = class {
1290
1482
  ) STRICT;
1291
1483
  CREATE INDEX IF NOT EXISTS runner_pipeline_logs_pipeline_sequence_idx
1292
1484
  ON runner_pipeline_logs(pipeline_id, sequence ASC);
1485
+ CREATE TABLE IF NOT EXISTS runner_pipeline_runs (
1486
+ run_id TEXT PRIMARY KEY,
1487
+ pipeline_id TEXT NOT NULL,
1488
+ status TEXT NOT NULL,
1489
+ input_json TEXT NOT NULL,
1490
+ output_json TEXT,
1491
+ error_json TEXT,
1492
+ metadata_json TEXT,
1493
+ started_at TEXT NOT NULL,
1494
+ ended_at TEXT,
1495
+ duration_ms INTEGER
1496
+ ) STRICT;
1497
+ CREATE INDEX IF NOT EXISTS runner_pipeline_runs_pipeline_started_idx
1498
+ ON runner_pipeline_runs(pipeline_id, started_at DESC);
1293
1499
  CREATE TABLE IF NOT EXISTS runner_traces (
1294
1500
  id TEXT PRIMARY KEY,
1295
1501
  session_id TEXT NOT NULL,
@@ -1479,6 +1685,23 @@ function toPipelineLog(row) {
1479
1685
  ...metadata === void 0 ? {} : { metadata }
1480
1686
  };
1481
1687
  }
1688
+ function toPipelineRun(row) {
1689
+ const output = parseJsonValue(row.output_json);
1690
+ const error = parseJsonValue(row.error_json);
1691
+ const metadata = parseJsonValue(row.metadata_json);
1692
+ return {
1693
+ runId: row.run_id,
1694
+ pipelineId: row.pipeline_id,
1695
+ status: row.status,
1696
+ input: JSON.parse(row.input_json),
1697
+ ...output === void 0 ? {} : { output },
1698
+ ...error === void 0 ? {} : { error },
1699
+ ...metadata === void 0 ? {} : { metadata },
1700
+ startedAt: row.started_at,
1701
+ ...row.ended_at === null ? {} : { endedAt: row.ended_at },
1702
+ ...row.duration_ms === null ? {} : { durationMs: row.duration_ms }
1703
+ };
1704
+ }
1482
1705
  function messageParts(message) {
1483
1706
  if (message.role === "system") {
1484
1707
  return [{ type: "text", value: { type: "text", text: message.content } }];
@@ -1709,10 +1932,12 @@ function resolveStores(options) {
1709
1932
  const sessions = resolveSessionStore(options, defaultStore);
1710
1933
  const traces = resolveTraceStore(options, sessions, defaultStore);
1711
1934
  const pipelineLogs = resolvePipelineLogStore(options, sessions, defaultStore);
1935
+ const pipelineRuns = resolvePipelineRunStore(options, sessions, pipelineLogs, defaultStore);
1712
1936
  return {
1713
1937
  ...sessions === void 0 ? {} : { sessions },
1714
1938
  ...traces === void 0 ? {} : { traces },
1715
- ...pipelineLogs === void 0 ? {} : { pipelineLogs }
1939
+ ...pipelineLogs === void 0 ? {} : { pipelineLogs },
1940
+ ...pipelineRuns === void 0 ? {} : { pipelineRuns }
1716
1941
  };
1717
1942
  }
1718
1943
  function resolveSessionStore(options, defaultStore) {
@@ -1748,6 +1973,21 @@ function resolvePipelineLogStore(options, sessionStore, defaultStore) {
1748
1973
  }
1749
1974
  return defaultStore;
1750
1975
  }
1976
+ function resolvePipelineRunStore(options, sessionStore, pipelineLogStore, defaultStore) {
1977
+ if (options.stores?.pipelineRuns === false) {
1978
+ return void 0;
1979
+ }
1980
+ if (options.stores?.pipelineRuns !== void 0) {
1981
+ return options.stores.pipelineRuns;
1982
+ }
1983
+ if (sessionStore !== void 0 && isPipelineRunStore(sessionStore)) {
1984
+ return sessionStore;
1985
+ }
1986
+ if (pipelineLogStore !== void 0 && isPipelineRunStore(pipelineLogStore)) {
1987
+ return pipelineLogStore;
1988
+ }
1989
+ return defaultStore;
1990
+ }
1751
1991
  function isTraceStore(store) {
1752
1992
  const candidate = store;
1753
1993
  return typeof candidate.listSessionTraces === "function" && typeof candidate.getTrace === "function" && typeof candidate.saveTrace === "function";
@@ -1756,6 +1996,10 @@ function isPipelineLogStore(store) {
1756
1996
  const candidate = store;
1757
1997
  return typeof candidate.appendPipelineLog === "function" && typeof candidate.listPipelineLogs === "function";
1758
1998
  }
1999
+ function isPipelineRunStore(store) {
2000
+ const candidate = store;
2001
+ return typeof candidate.savePipelineRun === "function" && typeof candidate.listPipelineRuns === "function";
2002
+ }
1759
2003
  function unsupportedCapabilities(stores) {
1760
2004
  return [
1761
2005
  ...stores.sessions === void 0 ? ["sessions"] : [],
@@ -2254,22 +2498,41 @@ function registerKnowledgeRoutes(app, props) {
2254
2498
  return errorResponse(c, 400, "bad_request", "Invalid limit");
2255
2499
  }
2256
2500
  const summary = {
2257
- agents: props.agents.map(agentKnowledgeConfig),
2501
+ agents: await Promise.all(props.agents.map(agentKnowledgeConfig)),
2258
2502
  evidence: await recentKnowledgeEvidence(props.traceStore, limit)
2259
2503
  };
2260
2504
  return c.json(summary);
2261
2505
  });
2506
+ app.get("/knowledge/items", async (c) => {
2507
+ const limit = parseLimit(c.req.query("limit"));
2508
+ if (limit === void 0) {
2509
+ return errorResponse(c, 400, "bad_request", "Invalid limit");
2510
+ }
2511
+ const agentId = optionalQueryString(c.req.query("agentId"));
2512
+ const sourceId = optionalQueryString(c.req.query("sourceId"));
2513
+ if (agentId === void 0 || sourceId === void 0) {
2514
+ return errorResponse(c, 400, "bad_request", "agentId and sourceId are required");
2515
+ }
2516
+ const agent = props.agents.find((item) => item.id === agentId);
2517
+ if (agent === void 0) {
2518
+ return errorResponse(c, 404, "not_found", "Agent not found");
2519
+ }
2520
+ const page = await knowledgeItemsPage(agent, sourceId, {
2521
+ limit,
2522
+ cursor: optionalQueryString(c.req.query("cursor"))
2523
+ });
2524
+ if (page === void 0) {
2525
+ return errorResponse(c, 404, "not_found", "Knowledge source not found");
2526
+ }
2527
+ return c.json(page);
2528
+ });
2262
2529
  }
2263
- function agentKnowledgeConfig(agent) {
2530
+ async function agentKnowledgeConfig(agent) {
2264
2531
  const agentName = agent.name ?? agent.agent.name;
2265
2532
  return {
2266
2533
  agentId: agent.id,
2267
2534
  ...agentName === void 0 ? {} : { agentName },
2268
- sources: [
2269
- { kind: "static_context", count: agent.agent.staticContext.length },
2270
- { kind: "dynamic_context", count: agent.agent.dynamicContexts.length },
2271
- { kind: "dynamic_tools", count: agent.agent.dynamicTools.length }
2272
- ],
2535
+ sources: await knowledgeSources(agent),
2273
2536
  staticContext: agent.agent.staticContext.map((document) => ({
2274
2537
  id: document.id,
2275
2538
  text: document.text,
@@ -2277,6 +2540,199 @@ function agentKnowledgeConfig(agent) {
2277
2540
  }))
2278
2541
  };
2279
2542
  }
2543
+ async function knowledgeSources(agent) {
2544
+ const sources = [
2545
+ {
2546
+ sourceId: staticSourceId(),
2547
+ kind: "static_context",
2548
+ label: "Static context",
2549
+ count: agent.agent.staticContext.length,
2550
+ inspectable: true,
2551
+ itemCount: agent.agent.staticContext.length
2552
+ }
2553
+ ];
2554
+ const dynamicContextSources = await Promise.all(
2555
+ agent.agent.dynamicContexts.map(async (registration, index) => {
2556
+ const inspect = inspectFn(registration.index);
2557
+ const count = await inspectableCount(inspect, registration.options.filter);
2558
+ return {
2559
+ sourceId: dynamicContextSourceId(index),
2560
+ kind: "dynamic_context",
2561
+ label: `Dynamic context ${index + 1}`,
2562
+ count: 1,
2563
+ registrationIndex: index,
2564
+ topK: registration.options.topK,
2565
+ ...registration.options.threshold === void 0 ? {} : { threshold: registration.options.threshold },
2566
+ inspectable: inspect !== void 0,
2567
+ ...count === void 0 ? {} : { itemCount: count }
2568
+ };
2569
+ })
2570
+ );
2571
+ const dynamicToolSources = await Promise.all(
2572
+ agent.agent.dynamicTools.map(async (registration, index) => {
2573
+ const inspect = inspectFn(registration.index);
2574
+ const count = await inspectableCount(inspect, registration.options.filter);
2575
+ return {
2576
+ sourceId: dynamicToolsSourceId(index),
2577
+ kind: "dynamic_tools",
2578
+ label: `Dynamic tools ${index + 1}`,
2579
+ count: 1,
2580
+ registrationIndex: index,
2581
+ topK: registration.options.topK,
2582
+ ...registration.options.threshold === void 0 ? {} : { threshold: registration.options.threshold },
2583
+ inspectable: inspect !== void 0,
2584
+ ...count === void 0 ? {} : { itemCount: count }
2585
+ };
2586
+ })
2587
+ );
2588
+ return [...sources, ...dynamicContextSources, ...dynamicToolSources];
2589
+ }
2590
+ async function inspectableCount(inspect, filter) {
2591
+ if (inspect === void 0) {
2592
+ return void 0;
2593
+ }
2594
+ const page = await inspect({ limit: 1, filter });
2595
+ return page.totalCount;
2596
+ }
2597
+ async function knowledgeItemsPage(agent, sourceId, request) {
2598
+ if (sourceId === staticSourceId()) {
2599
+ return staticKnowledgeItemsPage(agent, request);
2600
+ }
2601
+ const dynamicContextIndex = dynamicSourceIndex(sourceId, "dynamic_context");
2602
+ if (dynamicContextIndex !== void 0) {
2603
+ const registration = agent.agent.dynamicContexts[dynamicContextIndex];
2604
+ if (registration === void 0) {
2605
+ return void 0;
2606
+ }
2607
+ const inspect = inspectFn(registration.index);
2608
+ if (inspect === void 0) {
2609
+ return nonInspectablePage(agent.id, sourceId, "dynamic_context");
2610
+ }
2611
+ const page = await inspect({
2612
+ limit: request.limit,
2613
+ cursor: request.cursor,
2614
+ filter: registration.options.filter
2615
+ });
2616
+ return {
2617
+ agentId: agent.id,
2618
+ sourceId,
2619
+ kind: "dynamic_context",
2620
+ inspectable: true,
2621
+ items: page.items.map((item) => dynamicContextItem(item)),
2622
+ ...page.nextCursor === void 0 ? {} : { nextCursor: page.nextCursor },
2623
+ ...page.totalCount === void 0 ? {} : { totalCount: page.totalCount }
2624
+ };
2625
+ }
2626
+ const dynamicToolsIndex = dynamicSourceIndex(sourceId, "dynamic_tools");
2627
+ if (dynamicToolsIndex !== void 0) {
2628
+ const registration = agent.agent.dynamicTools[dynamicToolsIndex];
2629
+ if (registration === void 0) {
2630
+ return void 0;
2631
+ }
2632
+ const inspect = inspectFn(registration.index);
2633
+ if (inspect === void 0) {
2634
+ return nonInspectablePage(agent.id, sourceId, "dynamic_tools");
2635
+ }
2636
+ const page = await inspect({
2637
+ limit: request.limit,
2638
+ cursor: request.cursor,
2639
+ filter: registration.options.filter
2640
+ });
2641
+ return {
2642
+ agentId: agent.id,
2643
+ sourceId,
2644
+ kind: "dynamic_tools",
2645
+ inspectable: true,
2646
+ items: page.items.map((item) => dynamicToolItem(item)),
2647
+ ...page.nextCursor === void 0 ? {} : { nextCursor: page.nextCursor },
2648
+ ...page.totalCount === void 0 ? {} : { totalCount: page.totalCount }
2649
+ };
2650
+ }
2651
+ return void 0;
2652
+ }
2653
+ function inspectFn(index) {
2654
+ if (!isRecord2(index) || typeof index.inspect !== "function") {
2655
+ return void 0;
2656
+ }
2657
+ const inspect = index.inspect;
2658
+ return (request) => inspect.call(index, request);
2659
+ }
2660
+ function staticKnowledgeItemsPage(agent, request) {
2661
+ const start = Math.max(0, Math.trunc(Number(request.cursor ?? "0")));
2662
+ const page = agent.agent.staticContext.slice(start, start + request.limit);
2663
+ const nextOffset = start + page.length;
2664
+ return {
2665
+ agentId: agent.id,
2666
+ sourceId: staticSourceId(),
2667
+ kind: "static_context",
2668
+ inspectable: true,
2669
+ items: page.map((document) => ({
2670
+ id: document.id,
2671
+ kind: "static_context",
2672
+ text: document.text,
2673
+ ...document.additionalProps === void 0 ? {} : { metadata: jsonObjectFromRecord(document.additionalProps) }
2674
+ })),
2675
+ ...nextOffset < agent.agent.staticContext.length ? { nextCursor: String(nextOffset) } : {},
2676
+ totalCount: agent.agent.staticContext.length
2677
+ };
2678
+ }
2679
+ function nonInspectablePage(agentId, sourceId, kind) {
2680
+ return {
2681
+ agentId,
2682
+ sourceId,
2683
+ kind,
2684
+ inspectable: false,
2685
+ items: [],
2686
+ message: "This source can be searched at runtime, but it does not expose browseable chunks."
2687
+ };
2688
+ }
2689
+ function dynamicContextItem(item) {
2690
+ const text = isRecord2(item.document) && typeof item.document.text === "string" ? item.document.text : typeof item.document === "string" ? item.document : void 0;
2691
+ return {
2692
+ id: item.id,
2693
+ kind: "dynamic_context",
2694
+ ...text === void 0 ? { document: toJsonValue2(item.document) } : { text },
2695
+ ...item.metadata === void 0 ? {} : { metadata: jsonObjectFromRecord(item.metadata) }
2696
+ };
2697
+ }
2698
+ function dynamicToolItem(item) {
2699
+ const document = isRecord2(item.document) ? item.document : {};
2700
+ const definition = isRecord2(document.definition) ? document.definition : {};
2701
+ const toolName = typeof document.toolName === "string" ? document.toolName : typeof definition.name === "string" ? definition.name : item.id;
2702
+ const description = typeof definition.description === "string" ? definition.description : "";
2703
+ return {
2704
+ id: item.id,
2705
+ kind: "dynamic_tool",
2706
+ toolName,
2707
+ description,
2708
+ parameterKeys: parameterKeys(definition.parameters),
2709
+ document: toJsonValue2(item.document),
2710
+ ...item.metadata === void 0 ? {} : { metadata: jsonObjectFromRecord(item.metadata) }
2711
+ };
2712
+ }
2713
+ function parameterKeys(parameters) {
2714
+ if (!isRecord2(parameters) || !isRecord2(parameters.properties)) {
2715
+ return [];
2716
+ }
2717
+ return Object.keys(parameters.properties);
2718
+ }
2719
+ function staticSourceId() {
2720
+ return "static-context";
2721
+ }
2722
+ function dynamicContextSourceId(index) {
2723
+ return `dynamic-context-${index}`;
2724
+ }
2725
+ function dynamicToolsSourceId(index) {
2726
+ return `dynamic-tools-${index}`;
2727
+ }
2728
+ function dynamicSourceIndex(sourceId, kind) {
2729
+ const prefix = kind === "dynamic_context" ? "dynamic-context-" : "dynamic-tools-";
2730
+ if (!sourceId.startsWith(prefix)) {
2731
+ return void 0;
2732
+ }
2733
+ const index = Number(sourceId.slice(prefix.length));
2734
+ return Number.isInteger(index) && index >= 0 ? index : void 0;
2735
+ }
2280
2736
  async function recentKnowledgeEvidence(traceStore, limit) {
2281
2737
  if (traceStore?.listTraces === void 0) {
2282
2738
  return [];
@@ -2482,7 +2938,7 @@ function pipelineRunReceivedLog(props) {
2482
2938
  message: "Pipeline run request received",
2483
2939
  metadata: cleanMetadata({
2484
2940
  stream: props.stream,
2485
- inputBytes: byteLength(formatUnknown(props.input)),
2941
+ inputBytes: byteLength2(formatUnknown(props.input)),
2486
2942
  metadataKeys: Object.keys(props.metadata ?? {})
2487
2943
  })
2488
2944
  };
@@ -2512,7 +2968,7 @@ function pipelineRunCompletedLog(props) {
2512
2968
  message: "Pipeline run completed",
2513
2969
  metadata: cleanMetadata({
2514
2970
  durationMs: props.durationMs,
2515
- outputBytes: byteLength(formatUnknown(props.output))
2971
+ outputBytes: byteLength2(formatUnknown(props.output))
2516
2972
  })
2517
2973
  };
2518
2974
  }
@@ -2598,7 +3054,7 @@ function cleanMetadata(value) {
2598
3054
  Object.entries(value).filter(([, item]) => item !== void 0)
2599
3055
  );
2600
3056
  }
2601
- function byteLength(value) {
3057
+ function byteLength2(value) {
2602
3058
  return value === void 0 ? void 0 : new TextEncoder().encode(value).length;
2603
3059
  }
2604
3060
  function formatUnknown(value) {
@@ -3215,7 +3671,7 @@ function registerPipelineRoutes(app, props) {
3215
3671
  if (!props.pipelineMap.has(pipelineId)) {
3216
3672
  return errorResponse(c, 404, "not_found", "Pipeline not found");
3217
3673
  }
3218
- if (props.store === void 0) {
3674
+ if (props.logStore === void 0) {
3219
3675
  return errorResponse(
3220
3676
  c,
3221
3677
  501,
@@ -3232,7 +3688,7 @@ function registerPipelineRoutes(app, props) {
3232
3688
  if (after === false) {
3233
3689
  return errorResponse(c, 400, "bad_request", "after must be a non-negative integer");
3234
3690
  }
3235
- const logs = await props.store.listPipelineLogs({
3691
+ const logs = await props.logStore.listPipelineLogs({
3236
3692
  pipelineId,
3237
3693
  limit,
3238
3694
  ...after === void 0 ? {} : { after }
@@ -3243,6 +3699,27 @@ function registerPipelineRoutes(app, props) {
3243
3699
  ...logs.length === limit && last !== void 0 ? { nextCursor: last.sequence } : {}
3244
3700
  });
3245
3701
  });
3702
+ app.get("/pipelines/:pipelineId/runs", async (c) => {
3703
+ const pipelineId = c.req.param("pipelineId");
3704
+ if (!props.pipelineMap.has(pipelineId)) {
3705
+ return errorResponse(c, 404, "not_found", "Pipeline not found");
3706
+ }
3707
+ if (props.runStore === void 0) {
3708
+ return errorResponse(
3709
+ c,
3710
+ 501,
3711
+ "unsupported_capability",
3712
+ 'Capability "pipelines.runs" is not implemented by this runner',
3713
+ { capability: "pipelines", operation: "runs" }
3714
+ );
3715
+ }
3716
+ const limit = parsePipelineLogLimit(c.req.query("limit"));
3717
+ if (limit === void 0) {
3718
+ return errorResponse(c, 400, "bad_request", "limit must be a positive integer");
3719
+ }
3720
+ const runs = await props.runStore.listPipelineRuns({ pipelineId, limit });
3721
+ return c.json({ runs });
3722
+ });
3246
3723
  app.post("/pipelines/:pipelineId/runs", async (c) => {
3247
3724
  const pipeline = props.pipelineMap.get(c.req.param("pipelineId"));
3248
3725
  if (pipeline === void 0) {
@@ -3254,8 +3731,9 @@ function registerPipelineRoutes(app, props) {
3254
3731
  }
3255
3732
  const runId = globalThis.crypto.randomUUID();
3256
3733
  const startedAt = Date.now();
3734
+ const startedAtIso = new Date(startedAt).toISOString();
3257
3735
  await appendPipelineLog(
3258
- props.store,
3736
+ props.logStore,
3259
3737
  pipelineRunReceivedLog({
3260
3738
  pipeline,
3261
3739
  runId,
@@ -3264,31 +3742,54 @@ function registerPipelineRoutes(app, props) {
3264
3742
  ...body.metadata === void 0 ? {} : { metadata: body.metadata }
3265
3743
  })
3266
3744
  );
3745
+ await savePipelineRun(props.runStore, {
3746
+ runId,
3747
+ pipelineId: pipeline.id,
3748
+ status: "running",
3749
+ input: body.input,
3750
+ ...body.metadata === void 0 ? {} : { metadata: body.metadata },
3751
+ startedAt: startedAtIso
3752
+ });
3267
3753
  if (body.stream === true) {
3268
3754
  return streamPipelineRun(c, {
3269
3755
  pipeline,
3270
3756
  runId,
3271
3757
  input: body.input,
3272
3758
  startedAt,
3273
- ...props.store === void 0 ? {} : { store: props.store }
3759
+ startedAtIso,
3760
+ ...body.metadata === void 0 ? {} : { metadata: body.metadata },
3761
+ ...props.logStore === void 0 ? {} : { logStore: props.logStore },
3762
+ ...props.runStore === void 0 ? {} : { runStore: props.runStore }
3274
3763
  });
3275
3764
  }
3276
3765
  try {
3277
- await appendPipelineLog(props.store, pipelineRunStartedLog(pipeline, runId));
3766
+ await appendPipelineLog(props.logStore, pipelineRunStartedLog(pipeline, runId));
3278
3767
  const output = await pipeline.pipeline.run(body.input, {
3279
3768
  observer: {
3280
3769
  async onEvent(event) {
3281
- await appendPipelineLog(props.store, pipelineStageLog(pipeline.id, runId, event));
3770
+ await appendPipelineLog(props.logStore, pipelineStageLog(pipeline.id, runId, event));
3282
3771
  }
3283
3772
  }
3284
3773
  });
3285
3774
  const jsonOutput = toJsonValue3(output);
3775
+ const endedAt = Date.now();
3776
+ await savePipelineRun(props.runStore, {
3777
+ runId,
3778
+ pipelineId: pipeline.id,
3779
+ status: "success",
3780
+ input: body.input,
3781
+ output: jsonOutput,
3782
+ ...body.metadata === void 0 ? {} : { metadata: body.metadata },
3783
+ startedAt: startedAtIso,
3784
+ endedAt: new Date(endedAt).toISOString(),
3785
+ durationMs: endedAt - startedAt
3786
+ });
3286
3787
  await appendPipelineLog(
3287
- props.store,
3788
+ props.logStore,
3288
3789
  pipelineRunCompletedLog({
3289
3790
  pipelineId: pipeline.id,
3290
3791
  runId,
3291
- durationMs: Date.now() - startedAt,
3792
+ durationMs: endedAt - startedAt,
3292
3793
  output: jsonOutput
3293
3794
  })
3294
3795
  );
@@ -3299,8 +3800,20 @@ function registerPipelineRoutes(app, props) {
3299
3800
  };
3300
3801
  return c.json(response);
3301
3802
  } catch (error) {
3803
+ const endedAt = Date.now();
3804
+ await savePipelineRun(props.runStore, {
3805
+ runId,
3806
+ pipelineId: pipeline.id,
3807
+ status: "error",
3808
+ input: body.input,
3809
+ error: serializeError2(error),
3810
+ ...body.metadata === void 0 ? {} : { metadata: body.metadata },
3811
+ startedAt: startedAtIso,
3812
+ endedAt: new Date(endedAt).toISOString(),
3813
+ durationMs: endedAt - startedAt
3814
+ });
3302
3815
  await appendPipelineLog(
3303
- props.store,
3816
+ props.logStore,
3304
3817
  pipelineRunFailedLog(pipeline.id, runId, error, startedAt)
3305
3818
  );
3306
3819
  return errorResponse(c, 500, "internal_error", "Pipeline run failed", serializeError2(error));
@@ -3336,13 +3849,13 @@ function streamPipelineRun(c, props) {
3336
3849
  );
3337
3850
  }
3338
3851
  async function* pipelineRunEvents(props) {
3339
- yield* emitPipelineLog(props.store, pipelineRunStartedLog(props.pipeline, props.runId));
3852
+ yield* emitPipelineLog(props.logStore, pipelineRunStartedLog(props.pipeline, props.runId));
3340
3853
  const events = new AsyncEventQueue();
3341
3854
  const run = props.pipeline.pipeline.run(props.input, {
3342
3855
  observer: {
3343
3856
  async onEvent(event) {
3344
3857
  const log = await appendPipelineLog(
3345
- props.store,
3858
+ props.logStore,
3346
3859
  pipelineStageLog(props.pipeline.id, props.runId, event)
3347
3860
  );
3348
3861
  if (log !== void 0) {
@@ -3352,12 +3865,24 @@ async function* pipelineRunEvents(props) {
3352
3865
  }
3353
3866
  }).then(async (output) => {
3354
3867
  const jsonOutput = toJsonValue3(output);
3868
+ const endedAt = Date.now();
3869
+ await savePipelineRun(props.runStore, {
3870
+ runId: props.runId,
3871
+ pipelineId: props.pipeline.id,
3872
+ status: "success",
3873
+ input: props.input,
3874
+ output: jsonOutput,
3875
+ ...props.metadata === void 0 ? {} : { metadata: props.metadata },
3876
+ startedAt: props.startedAtIso,
3877
+ endedAt: new Date(endedAt).toISOString(),
3878
+ durationMs: endedAt - props.startedAt
3879
+ });
3355
3880
  const log = await appendPipelineLog(
3356
- props.store,
3881
+ props.logStore,
3357
3882
  pipelineRunCompletedLog({
3358
3883
  pipelineId: props.pipeline.id,
3359
3884
  runId: props.runId,
3360
- durationMs: Date.now() - props.startedAt,
3885
+ durationMs: endedAt - props.startedAt,
3361
3886
  output: jsonOutput
3362
3887
  })
3363
3888
  );
@@ -3371,8 +3896,20 @@ async function* pipelineRunEvents(props) {
3371
3896
  output: jsonOutput
3372
3897
  });
3373
3898
  }).catch(async (error) => {
3899
+ const endedAt = Date.now();
3900
+ await savePipelineRun(props.runStore, {
3901
+ runId: props.runId,
3902
+ pipelineId: props.pipeline.id,
3903
+ status: "error",
3904
+ input: props.input,
3905
+ error: serializeError2(error),
3906
+ ...props.metadata === void 0 ? {} : { metadata: props.metadata },
3907
+ startedAt: props.startedAtIso,
3908
+ endedAt: new Date(endedAt).toISOString(),
3909
+ durationMs: endedAt - props.startedAt
3910
+ });
3374
3911
  const log = await appendPipelineLog(
3375
- props.store,
3912
+ props.logStore,
3376
3913
  pipelineRunFailedLog(props.pipeline.id, props.runId, error, props.startedAt)
3377
3914
  );
3378
3915
  if (log !== void 0) {
@@ -3392,6 +3929,9 @@ async function* pipelineRunEvents(props) {
3392
3929
  await run;
3393
3930
  }
3394
3931
  }
3932
+ async function savePipelineRun(store, input) {
3933
+ return store?.savePipelineRun(input);
3934
+ }
3395
3935
  async function parsePipelineRunRequest(c) {
3396
3936
  let body;
3397
3937
  try {
@@ -3807,7 +4347,7 @@ function runCompletedLog(props) {
3807
4347
  metadata: cleanMetadata2({
3808
4348
  durationMs: props.durationMs,
3809
4349
  usage: usageSummary(props.usage),
3810
- outputBytes: byteLength2(props.output),
4350
+ outputBytes: byteLength3(props.output),
3811
4351
  messageCount: props.messageCount
3812
4352
  })
3813
4353
  };
@@ -3871,7 +4411,7 @@ function logsFromStreamEvent(props) {
3871
4411
  turn: event.turn,
3872
4412
  toolName: event.toolCall.function.name,
3873
4413
  callId: event.toolCall.callId ?? event.toolCall.id,
3874
- argumentBytes: byteLength2(formatUnknown2(event.toolCall.function.arguments))
4414
+ argumentBytes: byteLength3(formatUnknown2(event.toolCall.function.arguments))
3875
4415
  })
3876
4416
  }
3877
4417
  ];
@@ -3890,8 +4430,8 @@ function logsFromStreamEvent(props) {
3890
4430
  toolName: event.toolName,
3891
4431
  callId: event.toolCallId,
3892
4432
  internalCallId: event.internalCallId,
3893
- argumentBytes: byteLength2(event.args),
3894
- resultBytes: byteLength2(event.result)
4433
+ argumentBytes: byteLength3(event.args),
4434
+ resultBytes: byteLength3(event.result)
3895
4435
  })
3896
4436
  }
3897
4437
  ];
@@ -3944,7 +4484,7 @@ function logsFromStreamEvent(props) {
3944
4484
  callId: event.approval.callId,
3945
4485
  status: event.approval.status,
3946
4486
  hasReason: event.approval.reason !== void 0,
3947
- argumentBytes: byteLength2(event.approval.args)
4487
+ argumentBytes: byteLength3(event.approval.args)
3948
4488
  })
3949
4489
  }
3950
4490
  ];
@@ -3983,7 +4523,7 @@ function logsFromStreamEvent(props) {
3983
4523
  callId: event.question.callId,
3984
4524
  status: event.question.status,
3985
4525
  questionCount: event.question.questions.length,
3986
- argumentBytes: byteLength2(event.question.args)
4526
+ argumentBytes: byteLength3(event.question.args)
3987
4527
  })
3988
4528
  }
3989
4529
  ];
@@ -4037,7 +4577,7 @@ function childAgentLog(event, sessionId, runId) {
4037
4577
  childTurn: child.turn,
4038
4578
  toolName: child.toolCall.function.name,
4039
4579
  callId: child.toolCall.callId ?? child.toolCall.id,
4040
- argumentBytes: byteLength2(formatUnknown2(child.toolCall.function.arguments))
4580
+ argumentBytes: byteLength3(formatUnknown2(child.toolCall.function.arguments))
4041
4581
  })
4042
4582
  }
4043
4583
  ];
@@ -4059,7 +4599,7 @@ function childAgentLog(event, sessionId, runId) {
4059
4599
  childTurn: child.turn,
4060
4600
  toolName: child.toolName,
4061
4601
  callId: child.toolCallId,
4062
- resultBytes: byteLength2(child.result)
4602
+ resultBytes: byteLength3(child.result)
4063
4603
  })
4064
4604
  }
4065
4605
  ];
@@ -4097,7 +4637,7 @@ function childAgentLog(event, sessionId, runId) {
4097
4637
  agentId: event.agentId,
4098
4638
  hasAgentName: event.agentName !== void 0,
4099
4639
  usage: usageSummary(child.usage),
4100
- outputBytes: byteLength2(child.output),
4640
+ outputBytes: byteLength3(child.output),
4101
4641
  messageCount: child.messages.length
4102
4642
  })
4103
4643
  }
@@ -4128,14 +4668,14 @@ function messageSummary(message) {
4128
4668
  return {
4129
4669
  role: "user",
4130
4670
  contentKind: "text",
4131
- byteLength: byteLength2(message)
4671
+ byteLength: byteLength3(message)
4132
4672
  };
4133
4673
  }
4134
4674
  return {
4135
4675
  role: message.role,
4136
4676
  contentKind: Array.isArray(message.content) ? "parts" : "text",
4137
4677
  partCount: Array.isArray(message.content) ? message.content.length : 1,
4138
- byteLength: byteLength2(formatUnknown2(message.content))
4678
+ byteLength: byteLength3(formatUnknown2(message.content))
4139
4679
  };
4140
4680
  }
4141
4681
  function usageSummary(value) {
@@ -4179,7 +4719,7 @@ function cleanJsonValue(value) {
4179
4719
  function numericValue(value) {
4180
4720
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
4181
4721
  }
4182
- function byteLength2(value) {
4722
+ function byteLength3(value) {
4183
4723
  return value === void 0 ? 0 : new TextEncoder().encode(value).byteLength;
4184
4724
  }
4185
4725
  function formatUnknown2(value) {
@@ -4599,7 +5139,8 @@ function createStudioApp(options) {
4599
5139
  registerPipelineRoutes(app, {
4600
5140
  pipelines,
4601
5141
  pipelineMap,
4602
- ...stores.pipelineLogs === void 0 ? {} : { store: stores.pipelineLogs }
5142
+ ...stores.pipelineLogs === void 0 ? {} : { logStore: stores.pipelineLogs },
5143
+ ...stores.pipelineRuns === void 0 ? {} : { runStore: stores.pipelineRuns }
4603
5144
  });
4604
5145
  app.post("/agents/:agentId/runs", async (c) => {
4605
5146
  const agentId = c.req.param("agentId");