@anvia/studio 0.2.11 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -586,6 +586,11 @@ function registerStudioUi(app, options) {
586
586
  app.get(`${options.path}/tracing/*`, async (c) => c.html(await renderShell()));
587
587
  app.get(`${options.path}/sessions`, async (c) => c.html(await renderShell()));
588
588
  app.get(`${options.path}/agents`, async (c) => c.html(await renderShell()));
589
+ app.get(`${options.path}/tools`, async (c) => c.html(await renderShell()));
590
+ app.get(`${options.path}/mcps`, async (c) => c.html(await renderShell()));
591
+ app.get(`${options.path}/pipelines`, async (c) => c.html(await renderShell()));
592
+ app.get(`${options.path}/memory`, async (c) => c.html(await renderShell()));
593
+ app.get(`${options.path}/status`, async (c) => c.html(await renderShell()));
589
594
  app.get(`${options.path}/knowledge`, async (c) => c.html(await renderShell()));
590
595
  app.get(`${options.path}/knowledge/:tab`, async (c) => c.html(await renderShell()));
591
596
  app.get(`${options.path}/knowledge/*`, async (c) => c.html(await renderShell()));
@@ -787,8 +792,277 @@ function escapeHtml(value) {
787
792
  // src/runtime/approvals.ts
788
793
  import { createHook, parseToolArgs } from "@anvia/core";
789
794
 
790
- // src/runtime/shared.ts
791
- import { join } from "path";
795
+ // src/storage/memory-store.ts
796
+ function createInMemoryStudioStore() {
797
+ return new InMemoryStudioStore();
798
+ }
799
+ var InMemoryStudioStore = class {
800
+ kind = "memory";
801
+ sessions = /* @__PURE__ */ new Map();
802
+ traces = /* @__PURE__ */ new Map();
803
+ pipelineLogs = /* @__PURE__ */ new Map();
804
+ pipelineRuns = /* @__PURE__ */ new Map();
805
+ listSessions(options) {
806
+ return [...this.sessions.values()].filter((session) => options.agentId === void 0 || session.agentId === options.agentId).sort((left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt)).slice(0, options.limit).map(sessionSummary);
807
+ }
808
+ createSession(input) {
809
+ const now = (/* @__PURE__ */ new Date()).toISOString();
810
+ const session = {
811
+ id: input.id,
812
+ agentId: input.agentId,
813
+ ...input.title === void 0 ? {} : { title: input.title },
814
+ createdAt: now,
815
+ updatedAt: now,
816
+ messageCount: 0,
817
+ ...input.metadata === void 0 ? {} : { metadata: input.metadata },
818
+ messages: [],
819
+ runs: [],
820
+ logs: []
821
+ };
822
+ this.sessions.set(input.id, session);
823
+ return sessionSummary(session);
824
+ }
825
+ getSession(id) {
826
+ const session = this.sessions.get(id);
827
+ return session === void 0 ? void 0 : materializeSession(session);
828
+ }
829
+ load(context) {
830
+ return Promise.resolve(this.sessions.get(context.sessionId)?.messages ?? []);
831
+ }
832
+ append(input) {
833
+ const session = this.sessions.get(input.context.sessionId);
834
+ if (session !== void 0) {
835
+ session.messages.push(...input.messages);
836
+ session.messageCount = session.messages.length;
837
+ session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
838
+ }
839
+ return Promise.resolve();
840
+ }
841
+ clear(context) {
842
+ const session = this.sessions.get(context.sessionId);
843
+ if (session !== void 0) {
844
+ session.messages = [];
845
+ session.runs = [];
846
+ session.messageCount = 0;
847
+ session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
848
+ }
849
+ return Promise.resolve();
850
+ }
851
+ async recordError(input) {
852
+ await this.saveSessionRunTranscript({
853
+ id: input.context.sessionId,
854
+ runId: studioRunId(input.context) ?? input.runId,
855
+ transcript: transcriptFromMessagesFallback(input.messages),
856
+ status: "error",
857
+ error: serializeJsonError(input.error)
858
+ });
859
+ }
860
+ saveSessionRunTranscript(input) {
861
+ const session = this.sessions.get(input.id);
862
+ if (session === void 0) {
863
+ return void 0;
864
+ }
865
+ const now = (/* @__PURE__ */ new Date()).toISOString();
866
+ const existingIndex = session.runs.findIndex((run2) => run2.runId === input.runId);
867
+ const run = {
868
+ ...input,
869
+ transcript: renumberTranscript(input.transcript),
870
+ createdAt: existingIndex === -1 ? now : session.runs[existingIndex]?.createdAt ?? now,
871
+ updatedAt: now
872
+ };
873
+ if (existingIndex === -1) {
874
+ session.runs.push(run);
875
+ } else {
876
+ session.runs[existingIndex] = run;
877
+ }
878
+ session.updatedAt = now;
879
+ return materializeSession(session);
880
+ }
881
+ appendSessionLog(input) {
882
+ const session = this.sessions.get(input.sessionId);
883
+ const logs = session?.logs ?? [];
884
+ const entry = {
885
+ id: globalThis.crypto.randomUUID(),
886
+ sessionId: input.sessionId,
887
+ ...input.runId === void 0 ? {} : { runId: input.runId },
888
+ sequence: logs.length,
889
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
890
+ level: input.level,
891
+ category: input.category,
892
+ event: input.event,
893
+ message: input.message,
894
+ ...input.metadata === void 0 ? {} : { metadata: input.metadata }
895
+ };
896
+ if (session !== void 0) {
897
+ session.logs.push(entry);
898
+ session.updatedAt = entry.timestamp;
899
+ }
900
+ return entry;
901
+ }
902
+ listSessionLogs(options) {
903
+ return (this.sessions.get(options.sessionId)?.logs ?? []).filter((log) => options.after === void 0 || log.sequence > options.after).slice(0, options.limit);
904
+ }
905
+ deleteSession(id) {
906
+ for (const trace of this.traces.values()) {
907
+ if (trace.sessionId === id) {
908
+ this.traces.delete(trace.id);
909
+ }
910
+ }
911
+ return this.sessions.delete(id);
912
+ }
913
+ listTraces(options) {
914
+ return [...this.traces.values()].filter((trace) => options.sessionId === void 0 || trace.sessionId === options.sessionId).filter((trace) => options.status === void 0 || trace.status === options.status).filter((trace) => options.agentId === void 0 || traceAgentId(trace) === options.agentId).sort((left, right) => Date.parse(right.startedAt) - Date.parse(left.startedAt)).slice(0, options.limit).map(traceSummary);
915
+ }
916
+ listSessionTraces(options) {
917
+ return this.listTraces({ sessionId: options.sessionId, limit: options.limit });
918
+ }
919
+ getTrace(id) {
920
+ return this.traces.get(id);
921
+ }
922
+ saveTrace(trace) {
923
+ this.traces.set(trace.id, trace);
924
+ return trace;
925
+ }
926
+ appendPipelineLog(input) {
927
+ const logs = this.pipelineLogs.get(input.pipelineId) ?? [];
928
+ const entry = {
929
+ id: globalThis.crypto.randomUUID(),
930
+ pipelineId: input.pipelineId,
931
+ ...input.runId === void 0 ? {} : { runId: input.runId },
932
+ sequence: logs.length,
933
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
934
+ level: input.level,
935
+ category: input.category,
936
+ event: input.event,
937
+ message: input.message,
938
+ ...input.metadata === void 0 ? {} : { metadata: input.metadata }
939
+ };
940
+ this.pipelineLogs.set(input.pipelineId, [...logs, entry]);
941
+ return entry;
942
+ }
943
+ listPipelineLogs(options) {
944
+ return (this.pipelineLogs.get(options.pipelineId) ?? []).filter((log) => options.after === void 0 || log.sequence > options.after).slice(0, options.limit);
945
+ }
946
+ savePipelineRun(input) {
947
+ const record = {
948
+ runId: input.runId,
949
+ pipelineId: input.pipelineId,
950
+ status: input.status,
951
+ input: input.input,
952
+ ...input.output === void 0 ? {} : { output: input.output },
953
+ ...input.error === void 0 ? {} : { error: input.error },
954
+ ...input.metadata === void 0 ? {} : { metadata: input.metadata },
955
+ startedAt: input.startedAt,
956
+ ...input.endedAt === void 0 ? {} : { endedAt: input.endedAt },
957
+ ...input.durationMs === void 0 ? {} : { durationMs: input.durationMs }
958
+ };
959
+ this.pipelineRuns.set(input.runId, record);
960
+ return record;
961
+ }
962
+ listPipelineRuns(options) {
963
+ return [...this.pipelineRuns.values()].filter((run) => run.pipelineId === options.pipelineId).sort((left, right) => Date.parse(right.startedAt) - Date.parse(left.startedAt)).slice(0, options.limit);
964
+ }
965
+ };
966
+ function sessionSummary(session) {
967
+ return {
968
+ id: session.id,
969
+ agentId: session.agentId,
970
+ ...session.title === void 0 ? {} : { title: session.title },
971
+ createdAt: session.createdAt,
972
+ updatedAt: session.updatedAt,
973
+ messageCount: session.messages.length,
974
+ ...session.metadata === void 0 ? {} : { metadata: session.metadata }
975
+ };
976
+ }
977
+ function materializeSession(session) {
978
+ return {
979
+ ...sessionSummary(session),
980
+ messages: [...session.messages],
981
+ transcript: renumberTranscript(session.runs.flatMap((run) => run.transcript))
982
+ };
983
+ }
984
+ function traceSummary(trace) {
985
+ return {
986
+ id: trace.id,
987
+ sessionId: trace.sessionId,
988
+ ...trace.name === void 0 ? {} : { name: trace.name },
989
+ status: trace.status,
990
+ startedAt: trace.startedAt,
991
+ ...trace.endedAt === void 0 ? {} : { endedAt: trace.endedAt },
992
+ ...trace.durationMs === void 0 ? {} : { durationMs: trace.durationMs },
993
+ ...trace.output === void 0 ? {} : { output: trace.output },
994
+ ...trace.error === void 0 ? {} : { error: trace.error },
995
+ ...trace.usage === void 0 ? {} : { usage: trace.usage },
996
+ ...trace.metadata === void 0 ? {} : { metadata: trace.metadata },
997
+ observationCount: trace.observations.length
998
+ };
999
+ }
1000
+ function traceAgentId(trace) {
1001
+ const nestedMetadata = trace.metadata?.metadata;
1002
+ return isJsonObject(nestedMetadata) && typeof nestedMetadata.agentId === "string" ? nestedMetadata.agentId : void 0;
1003
+ }
1004
+ function renumberTranscript(entries) {
1005
+ return entries.map((entry, entryId) => ({ ...entry, entryId }));
1006
+ }
1007
+ function studioRunId(context) {
1008
+ const value = context.metadata?.studioRunId;
1009
+ return typeof value === "string" && value.length > 0 ? value : void 0;
1010
+ }
1011
+ function serializeJsonError(error) {
1012
+ if (error instanceof Error) {
1013
+ return {
1014
+ name: error.name,
1015
+ message: error.message
1016
+ };
1017
+ }
1018
+ return isJsonValue(error) ? error : String(error);
1019
+ }
1020
+ function transcriptFromMessagesFallback(messages) {
1021
+ const transcript = [];
1022
+ for (const message of messages) {
1023
+ if (message.role === "system") {
1024
+ continue;
1025
+ }
1026
+ if (message.role === "user") {
1027
+ for (const content of message.content) {
1028
+ if (content.type === "text") {
1029
+ transcript.push({
1030
+ entryId: transcript.length,
1031
+ kind: "message",
1032
+ role: "user",
1033
+ text: content.text
1034
+ });
1035
+ }
1036
+ }
1037
+ continue;
1038
+ }
1039
+ if (message.role === "assistant") {
1040
+ for (const content of message.content) {
1041
+ if (content.type === "text") {
1042
+ transcript.push({
1043
+ entryId: transcript.length,
1044
+ kind: "message",
1045
+ role: "assistant",
1046
+ text: content.text
1047
+ });
1048
+ }
1049
+ }
1050
+ }
1051
+ }
1052
+ return transcript;
1053
+ }
1054
+ function isJsonObject(value) {
1055
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1056
+ }
1057
+ function isJsonValue(value) {
1058
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
1059
+ return true;
1060
+ }
1061
+ if (Array.isArray(value)) {
1062
+ return value.every(isJsonValue);
1063
+ }
1064
+ return isJsonObject(value) && Object.values(value).every(isJsonValue);
1065
+ }
792
1066
 
793
1067
  // src/storage/sqlite-store.ts
794
1068
  import { mkdirSync } from "fs";
@@ -811,8 +1085,8 @@ var SqliteSessionStore = class {
811
1085
  const rows = db.prepare(
812
1086
  `SELECT s.id, s.agent_id, s.title, s.metadata_json, s.created_at, s.updated_at,
813
1087
  COUNT(m.message_index) AS message_count
814
- FROM runner_sessions s
815
- LEFT JOIN runner_session_messages m ON m.session_id = s.id
1088
+ FROM anvia_studio_sessions s
1089
+ LEFT JOIN anvia_studio_session_messages m ON m.session_id = s.id
816
1090
  ${agentClause}
817
1091
  GROUP BY s.id, s.agent_id, s.title, s.metadata_json, s.created_at, s.updated_at
818
1092
  ORDER BY s.updated_at DESC
@@ -827,7 +1101,7 @@ var SqliteSessionStore = class {
827
1101
  const db = this.database();
828
1102
  const now = (/* @__PURE__ */ new Date()).toISOString();
829
1103
  db.prepare(
830
- `INSERT INTO runner_sessions (
1104
+ `INSERT INTO anvia_studio_sessions (
831
1105
  id,
832
1106
  agent_id,
833
1107
  title,
@@ -866,7 +1140,7 @@ var SqliteSessionStore = class {
866
1140
  db.exec("BEGIN IMMEDIATE");
867
1141
  const row = db.prepare(
868
1142
  `SELECT id, agent_id, title, metadata_json, created_at, updated_at
869
- FROM runner_sessions
1143
+ FROM anvia_studio_sessions
870
1144
  WHERE id = $id`
871
1145
  ).get({ $id: input.context.sessionId });
872
1146
  if (row === void 0) {
@@ -877,7 +1151,7 @@ var SqliteSessionStore = class {
877
1151
  const nextIndex = this.nextMessageIndex(input.context.sessionId);
878
1152
  this.insertMessages(input.context.sessionId, input.messages, nextIndex, updatedAt);
879
1153
  db.prepare(
880
- `UPDATE runner_sessions
1154
+ `UPDATE anvia_studio_sessions
881
1155
  SET updated_at = $updatedAt
882
1156
  WHERE id = $id`
883
1157
  ).run({
@@ -899,17 +1173,17 @@ var SqliteSessionStore = class {
899
1173
  try {
900
1174
  db.exec("BEGIN IMMEDIATE");
901
1175
  db.prepare(
902
- `UPDATE runner_sessions
1176
+ `UPDATE anvia_studio_sessions
903
1177
  SET updated_at = $updatedAt
904
1178
  WHERE id = $id`
905
1179
  ).run({
906
1180
  $id: context.sessionId,
907
1181
  $updatedAt: updatedAt
908
1182
  });
909
- db.prepare("DELETE FROM runner_session_messages WHERE session_id = $id").run({
1183
+ db.prepare("DELETE FROM anvia_studio_session_messages WHERE session_id = $id").run({
910
1184
  $id: context.sessionId
911
1185
  });
912
- db.prepare("DELETE FROM runner_session_runs WHERE session_id = $id").run({
1186
+ db.prepare("DELETE FROM anvia_studio_session_runs WHERE session_id = $id").run({
913
1187
  $id: context.sessionId
914
1188
  });
915
1189
  db.exec("COMMIT");
@@ -922,15 +1196,15 @@ var SqliteSessionStore = class {
922
1196
  }
923
1197
  }
924
1198
  async recordError(input) {
925
- const runId = studioRunId(input.context) ?? input.runId;
1199
+ const runId = studioRunId2(input.context) ?? input.runId;
926
1200
  const existing = this.getSessionRun(input.context.sessionId, runId);
927
- const transcript = existing === void 0 || parseJsonArray(existing.transcript_json).length === 0 ? transcriptFromMessagesFallback(input.messages) : parseJsonArray(existing.transcript_json);
1201
+ const transcript = existing === void 0 || parseJsonArray(existing.transcript_json).length === 0 ? transcriptFromMessagesFallback2(input.messages) : parseJsonArray(existing.transcript_json);
928
1202
  await this.saveSessionRunTranscript({
929
1203
  id: input.context.sessionId,
930
1204
  runId,
931
1205
  transcript,
932
1206
  status: "error",
933
- error: serializeJsonError(input.error)
1207
+ error: serializeJsonError2(input.error)
934
1208
  });
935
1209
  }
936
1210
  saveSessionRunTranscript(input) {
@@ -950,7 +1224,7 @@ var SqliteSessionStore = class {
950
1224
  );
951
1225
  const title = current.title ?? input.title;
952
1226
  db.prepare(
953
- `INSERT INTO runner_session_runs (
1227
+ `INSERT INTO anvia_studio_session_runs (
954
1228
  run_id,
955
1229
  session_id,
956
1230
  status,
@@ -971,7 +1245,7 @@ var SqliteSessionStore = class {
971
1245
  )
972
1246
  ON CONFLICT(run_id) DO UPDATE SET
973
1247
  status = excluded.status,
974
- title = COALESCE(runner_session_runs.title, excluded.title),
1248
+ title = COALESCE(anvia_studio_session_runs.title, excluded.title),
975
1249
  transcript_json = excluded.transcript_json,
976
1250
  error_json = excluded.error_json,
977
1251
  updated_at = excluded.updated_at`
@@ -980,12 +1254,12 @@ var SqliteSessionStore = class {
980
1254
  $sessionId: input.id,
981
1255
  $status: input.status,
982
1256
  $title: input.title ?? null,
983
- $transcript: JSON.stringify(renumberTranscript(input.transcript)),
1257
+ $transcript: JSON.stringify(renumberTranscript2(input.transcript)),
984
1258
  $error: input.error === void 0 ? null : JSON.stringify(input.error),
985
1259
  $now: now
986
1260
  });
987
1261
  db.prepare(
988
- `UPDATE runner_sessions
1262
+ `UPDATE anvia_studio_sessions
989
1263
  SET title = $title,
990
1264
  updated_at = $updatedAt
991
1265
  WHERE id = $id`
@@ -1027,7 +1301,7 @@ var SqliteSessionStore = class {
1027
1301
  ...input.metadata === void 0 ? {} : { metadata: input.metadata }
1028
1302
  };
1029
1303
  db.prepare(
1030
- `INSERT INTO runner_session_logs (
1304
+ `INSERT INTO anvia_studio_session_logs (
1031
1305
  id,
1032
1306
  session_id,
1033
1307
  run_id,
@@ -1077,7 +1351,7 @@ var SqliteSessionStore = class {
1077
1351
  const rows = db.prepare(
1078
1352
  `SELECT id, session_id, run_id, sequence, timestamp, level, category, event, message,
1079
1353
  metadata_json
1080
- FROM runner_session_logs
1354
+ FROM anvia_studio_session_logs
1081
1355
  WHERE session_id = $sessionId
1082
1356
  ${afterClause}
1083
1357
  ORDER BY sequence ASC
@@ -1108,7 +1382,7 @@ var SqliteSessionStore = class {
1108
1382
  ...input.metadata === void 0 ? {} : { metadata: input.metadata }
1109
1383
  };
1110
1384
  db.prepare(
1111
- `INSERT INTO runner_pipeline_logs (
1385
+ `INSERT INTO anvia_studio_pipeline_logs (
1112
1386
  id,
1113
1387
  pipeline_id,
1114
1388
  run_id,
@@ -1158,7 +1432,7 @@ var SqliteSessionStore = class {
1158
1432
  const rows = db.prepare(
1159
1433
  `SELECT id, pipeline_id, run_id, sequence, timestamp, level, category, event, message,
1160
1434
  metadata_json
1161
- FROM runner_pipeline_logs
1435
+ FROM anvia_studio_pipeline_logs
1162
1436
  WHERE pipeline_id = $pipelineId
1163
1437
  ${afterClause}
1164
1438
  ORDER BY sequence ASC
@@ -1173,7 +1447,7 @@ var SqliteSessionStore = class {
1173
1447
  savePipelineRun(input) {
1174
1448
  const db = this.database();
1175
1449
  db.prepare(
1176
- `INSERT INTO runner_pipeline_runs (
1450
+ `INSERT INTO anvia_studio_pipeline_runs (
1177
1451
  run_id,
1178
1452
  pipeline_id,
1179
1453
  status,
@@ -1236,7 +1510,7 @@ var SqliteSessionStore = class {
1236
1510
  const rows = db.prepare(
1237
1511
  `SELECT run_id, pipeline_id, status, input_json, output_json, error_json,
1238
1512
  metadata_json, started_at, ended_at, duration_ms
1239
- FROM runner_pipeline_runs
1513
+ FROM anvia_studio_pipeline_runs
1240
1514
  WHERE pipeline_id = $pipelineId
1241
1515
  ORDER BY started_at DESC
1242
1516
  LIMIT $limit`
@@ -1250,10 +1524,10 @@ var SqliteSessionStore = class {
1250
1524
  const db = this.database();
1251
1525
  try {
1252
1526
  db.exec("BEGIN IMMEDIATE");
1253
- db.prepare("DELETE FROM runner_traces WHERE session_id = $id").run({ $id: id });
1254
- db.prepare("DELETE FROM runner_session_runs WHERE session_id = $id").run({ $id: id });
1255
- db.prepare("DELETE FROM runner_session_logs WHERE session_id = $id").run({ $id: id });
1256
- const result = db.prepare("DELETE FROM runner_sessions WHERE id = $id").run({ $id: id });
1527
+ db.prepare("DELETE FROM anvia_studio_traces WHERE session_id = $id").run({ $id: id });
1528
+ db.prepare("DELETE FROM anvia_studio_session_runs WHERE session_id = $id").run({ $id: id });
1529
+ db.prepare("DELETE FROM anvia_studio_session_logs WHERE session_id = $id").run({ $id: id });
1530
+ const result = db.prepare("DELETE FROM anvia_studio_sessions WHERE id = $id").run({ $id: id });
1257
1531
  db.exec("COMMIT");
1258
1532
  return Number(result.changes) > 0;
1259
1533
  } catch (error) {
@@ -1288,8 +1562,8 @@ var SqliteSessionStore = class {
1288
1562
  `SELECT t.id, t.session_id, t.name, t.status, t.trace_json, t.input_json, t.output,
1289
1563
  t.error_json, t.usage_json, t.metadata_json, t.observations_json,
1290
1564
  t.started_at, t.ended_at, t.duration_ms
1291
- FROM runner_traces t
1292
- LEFT JOIN runner_sessions s ON s.id = t.session_id
1565
+ FROM anvia_studio_traces t
1566
+ LEFT JOIN anvia_studio_sessions s ON s.id = t.session_id
1293
1567
  ${whereClause}
1294
1568
  ORDER BY t.started_at DESC
1295
1569
  LIMIT $limit`
@@ -1301,7 +1575,7 @@ var SqliteSessionStore = class {
1301
1575
  const rows = db.prepare(
1302
1576
  `SELECT id, session_id, name, status, trace_json, input_json, output, error_json,
1303
1577
  usage_json, metadata_json, observations_json, started_at, ended_at, duration_ms
1304
- FROM runner_traces
1578
+ FROM anvia_studio_traces
1305
1579
  WHERE session_id = $sessionId
1306
1580
  ORDER BY started_at DESC
1307
1581
  LIMIT $limit`
@@ -1316,7 +1590,7 @@ var SqliteSessionStore = class {
1316
1590
  const row = db.prepare(
1317
1591
  `SELECT id, session_id, name, status, trace_json, input_json, output, error_json,
1318
1592
  usage_json, metadata_json, observations_json, started_at, ended_at, duration_ms
1319
- FROM runner_traces
1593
+ FROM anvia_studio_traces
1320
1594
  WHERE id = $id`
1321
1595
  ).get({ $id: id });
1322
1596
  return row === void 0 ? void 0 : toTrace(row);
@@ -1324,7 +1598,7 @@ var SqliteSessionStore = class {
1324
1598
  saveTrace(trace) {
1325
1599
  const db = this.database();
1326
1600
  db.prepare(
1327
- `INSERT INTO runner_traces (
1601
+ `INSERT INTO anvia_studio_traces (
1328
1602
  id,
1329
1603
  session_id,
1330
1604
  name,
@@ -1404,7 +1678,7 @@ var SqliteSessionStore = class {
1404
1678
  `);
1405
1679
  guardAgainstLegacySessionSchema(db);
1406
1680
  db.exec(`
1407
- CREATE TABLE IF NOT EXISTS runner_sessions (
1681
+ CREATE TABLE IF NOT EXISTS anvia_studio_sessions (
1408
1682
  id TEXT PRIMARY KEY,
1409
1683
  agent_id TEXT NOT NULL,
1410
1684
  title TEXT,
@@ -1412,20 +1686,20 @@ var SqliteSessionStore = class {
1412
1686
  created_at TEXT NOT NULL,
1413
1687
  updated_at TEXT NOT NULL
1414
1688
  ) STRICT;
1415
- CREATE INDEX IF NOT EXISTS runner_sessions_agent_updated_idx
1416
- ON runner_sessions(agent_id, updated_at DESC);
1417
- CREATE TABLE IF NOT EXISTS runner_session_messages (
1689
+ CREATE INDEX IF NOT EXISTS anvia_studio_sessions_agent_updated_idx
1690
+ ON anvia_studio_sessions(agent_id, updated_at DESC);
1691
+ CREATE TABLE IF NOT EXISTS anvia_studio_session_messages (
1418
1692
  session_id TEXT NOT NULL,
1419
1693
  message_index INTEGER NOT NULL,
1420
1694
  role TEXT NOT NULL,
1421
1695
  message_id TEXT,
1422
1696
  created_at TEXT NOT NULL,
1423
1697
  PRIMARY KEY(session_id, message_index),
1424
- FOREIGN KEY(session_id) REFERENCES runner_sessions(id) ON DELETE CASCADE
1698
+ FOREIGN KEY(session_id) REFERENCES anvia_studio_sessions(id) ON DELETE CASCADE
1425
1699
  ) STRICT;
1426
- CREATE INDEX IF NOT EXISTS runner_session_messages_session_idx
1427
- ON runner_session_messages(session_id, message_index ASC);
1428
- CREATE TABLE IF NOT EXISTS runner_session_message_parts (
1700
+ CREATE INDEX IF NOT EXISTS anvia_studio_session_messages_session_idx
1701
+ ON anvia_studio_session_messages(session_id, message_index ASC);
1702
+ CREATE TABLE IF NOT EXISTS anvia_studio_session_message_parts (
1429
1703
  session_id TEXT NOT NULL,
1430
1704
  message_index INTEGER NOT NULL,
1431
1705
  part_index INTEGER NOT NULL,
@@ -1433,10 +1707,10 @@ var SqliteSessionStore = class {
1433
1707
  part_json TEXT NOT NULL,
1434
1708
  PRIMARY KEY(session_id, message_index, part_index),
1435
1709
  FOREIGN KEY(session_id, message_index)
1436
- REFERENCES runner_session_messages(session_id, message_index)
1710
+ REFERENCES anvia_studio_session_messages(session_id, message_index)
1437
1711
  ON DELETE CASCADE
1438
1712
  ) STRICT;
1439
- CREATE TABLE IF NOT EXISTS runner_session_runs (
1713
+ CREATE TABLE IF NOT EXISTS anvia_studio_session_runs (
1440
1714
  run_id TEXT PRIMARY KEY,
1441
1715
  session_id TEXT NOT NULL,
1442
1716
  status TEXT NOT NULL,
@@ -1445,11 +1719,11 @@ var SqliteSessionStore = class {
1445
1719
  error_json TEXT,
1446
1720
  created_at TEXT NOT NULL,
1447
1721
  updated_at TEXT NOT NULL,
1448
- FOREIGN KEY(session_id) REFERENCES runner_sessions(id) ON DELETE CASCADE
1722
+ FOREIGN KEY(session_id) REFERENCES anvia_studio_sessions(id) ON DELETE CASCADE
1449
1723
  ) STRICT;
1450
- CREATE INDEX IF NOT EXISTS runner_session_runs_session_created_idx
1451
- ON runner_session_runs(session_id, created_at ASC);
1452
- CREATE TABLE IF NOT EXISTS runner_session_logs (
1724
+ CREATE INDEX IF NOT EXISTS anvia_studio_session_runs_session_created_idx
1725
+ ON anvia_studio_session_runs(session_id, created_at ASC);
1726
+ CREATE TABLE IF NOT EXISTS anvia_studio_session_logs (
1453
1727
  id TEXT PRIMARY KEY,
1454
1728
  session_id TEXT NOT NULL,
1455
1729
  run_id TEXT,
@@ -1461,11 +1735,11 @@ var SqliteSessionStore = class {
1461
1735
  message TEXT NOT NULL,
1462
1736
  metadata_json TEXT,
1463
1737
  UNIQUE(session_id, sequence),
1464
- FOREIGN KEY(session_id) REFERENCES runner_sessions(id) ON DELETE CASCADE
1738
+ FOREIGN KEY(session_id) REFERENCES anvia_studio_sessions(id) ON DELETE CASCADE
1465
1739
  ) STRICT;
1466
- CREATE INDEX IF NOT EXISTS runner_session_logs_session_sequence_idx
1467
- ON runner_session_logs(session_id, sequence ASC);
1468
- CREATE TABLE IF NOT EXISTS runner_pipeline_logs (
1740
+ CREATE INDEX IF NOT EXISTS anvia_studio_session_logs_session_sequence_idx
1741
+ ON anvia_studio_session_logs(session_id, sequence ASC);
1742
+ CREATE TABLE IF NOT EXISTS anvia_studio_pipeline_logs (
1469
1743
  id TEXT PRIMARY KEY,
1470
1744
  pipeline_id TEXT NOT NULL,
1471
1745
  run_id TEXT,
@@ -1478,9 +1752,9 @@ var SqliteSessionStore = class {
1478
1752
  metadata_json TEXT,
1479
1753
  UNIQUE(pipeline_id, sequence)
1480
1754
  ) STRICT;
1481
- CREATE INDEX IF NOT EXISTS runner_pipeline_logs_pipeline_sequence_idx
1482
- ON runner_pipeline_logs(pipeline_id, sequence ASC);
1483
- CREATE TABLE IF NOT EXISTS runner_pipeline_runs (
1755
+ CREATE INDEX IF NOT EXISTS anvia_studio_pipeline_logs_pipeline_sequence_idx
1756
+ ON anvia_studio_pipeline_logs(pipeline_id, sequence ASC);
1757
+ CREATE TABLE IF NOT EXISTS anvia_studio_pipeline_runs (
1484
1758
  run_id TEXT PRIMARY KEY,
1485
1759
  pipeline_id TEXT NOT NULL,
1486
1760
  status TEXT NOT NULL,
@@ -1492,9 +1766,9 @@ var SqliteSessionStore = class {
1492
1766
  ended_at TEXT,
1493
1767
  duration_ms INTEGER
1494
1768
  ) STRICT;
1495
- CREATE INDEX IF NOT EXISTS runner_pipeline_runs_pipeline_started_idx
1496
- ON runner_pipeline_runs(pipeline_id, started_at DESC);
1497
- CREATE TABLE IF NOT EXISTS runner_traces (
1769
+ CREATE INDEX IF NOT EXISTS anvia_studio_pipeline_runs_pipeline_started_idx
1770
+ ON anvia_studio_pipeline_runs(pipeline_id, started_at DESC);
1771
+ CREATE TABLE IF NOT EXISTS anvia_studio_traces (
1498
1772
  id TEXT PRIMARY KEY,
1499
1773
  session_id TEXT NOT NULL,
1500
1774
  name TEXT,
@@ -1510,8 +1784,8 @@ var SqliteSessionStore = class {
1510
1784
  ended_at TEXT,
1511
1785
  duration_ms INTEGER
1512
1786
  ) STRICT;
1513
- CREATE INDEX IF NOT EXISTS runner_traces_session_started_idx
1514
- ON runner_traces(session_id, started_at DESC);
1787
+ CREATE INDEX IF NOT EXISTS anvia_studio_traces_session_started_idx
1788
+ ON anvia_studio_traces(session_id, started_at DESC);
1515
1789
  `);
1516
1790
  this.db = db;
1517
1791
  return db;
@@ -1519,21 +1793,21 @@ var SqliteSessionStore = class {
1519
1793
  getSessionRow(id) {
1520
1794
  return this.database().prepare(
1521
1795
  `SELECT id, agent_id, title, metadata_json, created_at, updated_at
1522
- FROM runner_sessions
1796
+ FROM anvia_studio_sessions
1523
1797
  WHERE id = $id`
1524
1798
  ).get({ $id: id });
1525
1799
  }
1526
1800
  getSessionRun(sessionId, runId) {
1527
1801
  return this.database().prepare(
1528
1802
  `SELECT run_id, session_id, status, title, transcript_json, error_json, created_at, updated_at
1529
- FROM runner_session_runs
1803
+ FROM anvia_studio_session_runs
1530
1804
  WHERE session_id = $sessionId AND run_id = $runId`
1531
1805
  ).get({ $sessionId: sessionId, $runId: runId });
1532
1806
  }
1533
1807
  listSessionRunRows(sessionId) {
1534
1808
  return this.database().prepare(
1535
1809
  `SELECT run_id, session_id, status, title, transcript_json, error_json, created_at, updated_at
1536
- FROM runner_session_runs
1810
+ FROM anvia_studio_session_runs
1537
1811
  WHERE session_id = $sessionId
1538
1812
  ORDER BY created_at ASC`
1539
1813
  ).all({ $sessionId: sessionId });
@@ -1541,7 +1815,7 @@ var SqliteSessionStore = class {
1541
1815
  nextSessionLogSequence(sessionId) {
1542
1816
  const row = this.database().prepare(
1543
1817
  `SELECT COALESCE(MAX(sequence) + 1, 0) AS next_sequence
1544
- FROM runner_session_logs
1818
+ FROM anvia_studio_session_logs
1545
1819
  WHERE session_id = $sessionId`
1546
1820
  ).get({ $sessionId: sessionId });
1547
1821
  return row.next_sequence;
@@ -1549,7 +1823,7 @@ var SqliteSessionStore = class {
1549
1823
  nextPipelineLogSequence(pipelineId) {
1550
1824
  const row = this.database().prepare(
1551
1825
  `SELECT COALESCE(MAX(sequence) + 1, 0) AS next_sequence
1552
- FROM runner_pipeline_logs
1826
+ FROM anvia_studio_pipeline_logs
1553
1827
  WHERE pipeline_id = $pipelineId`
1554
1828
  ).get({ $pipelineId: pipelineId });
1555
1829
  return row.next_sequence;
@@ -1558,7 +1832,7 @@ var SqliteSessionStore = class {
1558
1832
  const db = this.database();
1559
1833
  const messageRows = db.prepare(
1560
1834
  `SELECT session_id, message_index, role, message_id, created_at
1561
- FROM runner_session_messages
1835
+ FROM anvia_studio_session_messages
1562
1836
  WHERE session_id = $sessionId
1563
1837
  ORDER BY message_index ASC`
1564
1838
  ).all({ $sessionId: sessionId });
@@ -1567,7 +1841,7 @@ var SqliteSessionStore = class {
1567
1841
  }
1568
1842
  const partRows = db.prepare(
1569
1843
  `SELECT session_id, message_index, part_index, type, part_json
1570
- FROM runner_session_message_parts
1844
+ FROM anvia_studio_session_message_parts
1571
1845
  WHERE session_id = $sessionId
1572
1846
  ORDER BY message_index ASC, part_index ASC`
1573
1847
  ).all({ $sessionId: sessionId });
@@ -1584,7 +1858,7 @@ var SqliteSessionStore = class {
1584
1858
  nextMessageIndex(sessionId) {
1585
1859
  const row = this.database().prepare(
1586
1860
  `SELECT COALESCE(MAX(message_index) + 1, 0) AS next_index
1587
- FROM runner_session_messages
1861
+ FROM anvia_studio_session_messages
1588
1862
  WHERE session_id = $sessionId`
1589
1863
  ).get({ $sessionId: sessionId });
1590
1864
  return row.next_index;
@@ -1592,7 +1866,7 @@ var SqliteSessionStore = class {
1592
1866
  insertMessages(sessionId, messages, startIndex, createdAt) {
1593
1867
  const db = this.database();
1594
1868
  const insertMessage = db.prepare(
1595
- `INSERT INTO runner_session_messages (
1869
+ `INSERT INTO anvia_studio_session_messages (
1596
1870
  session_id,
1597
1871
  message_index,
1598
1872
  role,
@@ -1601,7 +1875,7 @@ var SqliteSessionStore = class {
1601
1875
  ) VALUES ($sessionId, $messageIndex, $role, $messageId, $createdAt)`
1602
1876
  );
1603
1877
  const insertPart = db.prepare(
1604
- `INSERT INTO runner_session_message_parts (
1878
+ `INSERT INTO anvia_studio_session_message_parts (
1605
1879
  session_id,
1606
1880
  message_index,
1607
1881
  part_index,
@@ -1638,7 +1912,7 @@ function toSession(row, messages, runRows = []) {
1638
1912
  return {
1639
1913
  ...summary,
1640
1914
  messages,
1641
- transcript: renumberTranscript(runTranscript)
1915
+ transcript: renumberTranscript2(runTranscript)
1642
1916
  };
1643
1917
  }
1644
1918
  function toSessionSummary(row) {
@@ -1743,7 +2017,7 @@ function systemContentFromParts(parts) {
1743
2017
  return "";
1744
2018
  }
1745
2019
  function guardAgainstLegacySessionSchema(db) {
1746
- const columns = db.prepare("PRAGMA table_info('runner_sessions')").all();
2020
+ const columns = db.prepare("PRAGMA table_info('anvia_studio_sessions')").all();
1747
2021
  if (columns.some((column) => column.name === "messages_json")) {
1748
2022
  throw new Error(
1749
2023
  "Existing Studio SQLite DB uses the legacy messages_json schema. Delete or recreate the Studio SQLite DB to use normalized session messages."
@@ -1806,14 +2080,14 @@ function parseJsonValue(value) {
1806
2080
  }
1807
2081
  return JSON.parse(value);
1808
2082
  }
1809
- function renumberTranscript(entries) {
2083
+ function renumberTranscript2(entries) {
1810
2084
  return entries.map((entry, entryId) => ({ ...entry, entryId }));
1811
2085
  }
1812
- function studioRunId(context) {
2086
+ function studioRunId2(context) {
1813
2087
  const value = context.metadata?.studioRunId;
1814
2088
  return typeof value === "string" && value.length > 0 ? value : void 0;
1815
2089
  }
1816
- function serializeJsonError(error) {
2090
+ function serializeJsonError2(error) {
1817
2091
  if (error instanceof Error) {
1818
2092
  return {
1819
2093
  name: error.name,
@@ -1825,7 +2099,7 @@ function serializeJsonError(error) {
1825
2099
  }
1826
2100
  return String(error);
1827
2101
  }
1828
- function transcriptFromMessagesFallback(messages) {
2102
+ function transcriptFromMessagesFallback2(messages) {
1829
2103
  const transcript = [];
1830
2104
  for (const message of messages) {
1831
2105
  if (message.role === "system") {
@@ -1903,6 +2177,39 @@ function formatJson(value) {
1903
2177
  }
1904
2178
  }
1905
2179
 
2180
+ // src/runtime/json.ts
2181
+ function toJsonValue2(value) {
2182
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
2183
+ return value;
2184
+ }
2185
+ if (value === void 0) {
2186
+ return null;
2187
+ }
2188
+ if (Array.isArray(value)) {
2189
+ return value.map((item) => toJsonValue2(item));
2190
+ }
2191
+ if (typeof value === "object") {
2192
+ return compactJsonObject2(value);
2193
+ }
2194
+ return String(value);
2195
+ }
2196
+ function compactJsonObject2(values) {
2197
+ const entries = Object.entries(values).flatMap(
2198
+ ([key, value]) => value === void 0 ? [] : [[key, toJsonValue2(value)]]
2199
+ );
2200
+ return Object.fromEntries(entries);
2201
+ }
2202
+ function serializeUnknown(error) {
2203
+ if (error instanceof Error) {
2204
+ return compactJsonObject2({
2205
+ name: error.name,
2206
+ message: error.message,
2207
+ stack: error.stack
2208
+ });
2209
+ }
2210
+ return toJsonValue2(error);
2211
+ }
2212
+
1906
2213
  // src/runtime/tool-metadata.ts
1907
2214
  import { ToolSet } from "@anvia/core";
1908
2215
  var MCP_TOOL_METADATA_KEY = /* @__PURE__ */ Symbol.for("anvia.mcp.tool.metadata");
@@ -1944,8 +2251,7 @@ function agentHasMcpTools(agent) {
1944
2251
 
1945
2252
  // src/runtime/shared.ts
1946
2253
  function resolveStores(options) {
1947
- const defaultPath = process.env.ANVIA_STUDIO_DB ?? process.env.AION_STUDIO_DB ?? join(process.cwd(), ".anvia-studio", `${safeFileName(runnerId(options))}.sqlite`);
1948
- const defaultStore = createSqliteSessionStore({ path: defaultPath });
2254
+ const defaultStore = defaultStudioStore();
1949
2255
  const sessions = resolveSessionStore(options, defaultStore);
1950
2256
  const traces = resolveTraceStore(options, sessions, defaultStore);
1951
2257
  const pipelineLogs = resolvePipelineLogStore(options, sessions, defaultStore);
@@ -1957,6 +2263,10 @@ function resolveStores(options) {
1957
2263
  ...pipelineRuns === void 0 ? {} : { pipelineRuns }
1958
2264
  };
1959
2265
  }
2266
+ function defaultStudioStore() {
2267
+ const sqlitePath = process.env.ANVIA_STUDIO_DB ?? process.env.AION_STUDIO_DB;
2268
+ return sqlitePath === void 0 ? createInMemoryStudioStore() : createSqliteSessionStore({ path: sqlitePath });
2269
+ }
1960
2270
  function resolveSessionStore(options, defaultStore) {
1961
2271
  if (options.stores?.sessions === false) {
1962
2272
  return void 0;
@@ -2059,6 +2369,7 @@ function buildConfig(options, agents, pipelines, stores) {
2059
2369
  ...options.version === void 0 ? {} : { version: options.version },
2060
2370
  agents: agents.map(agentConfig),
2061
2371
  pipelines: pipelines.map(pipelineConfig),
2372
+ evals: options.evals.map(evalConfig),
2062
2373
  chat: {
2063
2374
  quickPrompts: Object.fromEntries(agents.map((agent) => [agent.id, agent.quickPrompts ?? []]))
2064
2375
  },
@@ -2080,6 +2391,30 @@ function agentConfig(agent) {
2080
2391
  ...agent.metadata === void 0 ? {} : { metadata: agent.metadata }
2081
2392
  };
2082
2393
  }
2394
+ function agentRuntimeSummary(agent) {
2395
+ const tools = agentToolItems(agent);
2396
+ const name = agent.name ?? agent.agent.name;
2397
+ const description = agent.description ?? agent.agent.description;
2398
+ return {
2399
+ id: agent.id,
2400
+ ...name === void 0 ? {} : { name },
2401
+ ...description === void 0 ? {} : { description },
2402
+ model: toJsonValue2(agent.agent.model),
2403
+ toolCount: tools.length,
2404
+ staticToolCount: tools.filter((item) => item.source === "static").length,
2405
+ dynamicToolCount: tools.filter((item) => item.source === "dynamic").length,
2406
+ approvalToolCount: tools.filter((item) => item.tool.approval !== void 0).length,
2407
+ mcpToolCount: tools.filter((item) => mcpServerName(item.tool) !== void 0).length,
2408
+ staticContextCount: agent.agent.staticContext.length,
2409
+ dynamicContextCount: agent.agent.dynamicContexts.length,
2410
+ observerCount: agent.agent.observers.length,
2411
+ hasMemory: agent.agent.memory !== void 0,
2412
+ hasHook: agent.agent.hook !== void 0,
2413
+ hasOutputSchema: agent.agent.outputSchema !== void 0,
2414
+ ...agent.agent.defaultMaxTurns === void 0 ? {} : { defaultMaxTurns: agent.agent.defaultMaxTurns },
2415
+ ...agent.metadata === void 0 ? {} : { metadata: agent.metadata }
2416
+ };
2417
+ }
2083
2418
  function pipelineConfig(pipeline) {
2084
2419
  const graph = pipeline.pipeline.graph();
2085
2420
  const stageNodes = graph.nodes.filter((node) => node.kind !== "input" && node.kind !== "output");
@@ -2097,10 +2432,13 @@ function pipelineConfig(pipeline) {
2097
2432
  }
2098
2433
  function capabilityConfig(_options, agents, pipelines, stores) {
2099
2434
  const capabilities = {
2100
- agents: { enabled: true }
2435
+ agents: { enabled: true },
2436
+ observability: { enabled: true },
2437
+ status: { enabled: true }
2101
2438
  };
2102
2439
  if (stores.sessions !== void 0) {
2103
2440
  capabilities.sessions = { enabled: true };
2441
+ capabilities.memory = { enabled: true };
2104
2442
  }
2105
2443
  if (stores.traces !== void 0) {
2106
2444
  capabilities.traces = { enabled: true };
@@ -2108,6 +2446,9 @@ function capabilityConfig(_options, agents, pipelines, stores) {
2108
2446
  if (pipelines.length > 0) {
2109
2447
  capabilities.pipelines = { enabled: true };
2110
2448
  }
2449
+ if (_options.evals.length > 0) {
2450
+ capabilities.evals = { enabled: true };
2451
+ }
2111
2452
  if (agents.some(
2112
2453
  (agent) => agent.agent.toolSet.values().length > 0 || agent.agent.dynamicTools.length > 0
2113
2454
  )) {
@@ -2116,9 +2457,6 @@ function capabilityConfig(_options, agents, pipelines, stores) {
2116
2457
  if (agents.some(agentHasMcpTools)) {
2117
2458
  capabilities.mcps = { enabled: true };
2118
2459
  }
2119
- if (agents.some((agent) => agent.agent.observers.length > 0)) {
2120
- capabilities.observability = { enabled: true };
2121
- }
2122
2460
  if (agents.some(
2123
2461
  (agent) => agent.agent.hook !== void 0 || agent.agent.toolSet.values().some((tool) => tool.approval)
2124
2462
  )) {
@@ -2131,6 +2469,17 @@ function capabilityConfig(_options, agents, pipelines, stores) {
2131
2469
  }
2132
2470
  return capabilities;
2133
2471
  }
2472
+ function evalConfig(suite) {
2473
+ return {
2474
+ id: suite.id ?? suite.name,
2475
+ name: suite.name,
2476
+ ...suite.description === void 0 ? {} : { description: suite.description },
2477
+ caseCount: suite.cases.length,
2478
+ metricNames: suite.metrics.map((metric) => metric.name),
2479
+ ...suite.concurrency === void 0 ? {} : { concurrency: suite.concurrency },
2480
+ ...suite.metadata === void 0 ? {} : { metadata: suite.metadata }
2481
+ };
2482
+ }
2134
2483
  function optionalQueryString(value) {
2135
2484
  const trimmed = value?.trim();
2136
2485
  return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
@@ -2152,9 +2501,6 @@ function parseTraceStatus(value) {
2152
2501
  }
2153
2502
  return status === "running" || status === "success" || status === "error" ? status : false;
2154
2503
  }
2155
- function safeFileName(value) {
2156
- return value.replace(/[^a-zA-Z0-9._-]+/g, "_") || "anvia-studio";
2157
- }
2158
2504
  function isMessageInput(value) {
2159
2505
  return typeof value === "string" || isMessage(value);
2160
2506
  }
@@ -2173,17 +2519,17 @@ function isMessage(value) {
2173
2519
  function isObject(value) {
2174
2520
  return typeof value === "object" && value !== null && !Array.isArray(value);
2175
2521
  }
2176
- function isJsonObject(value) {
2177
- return isObject(value) && Object.values(value).every(isJsonValue);
2522
+ function isJsonObject2(value) {
2523
+ return isObject(value) && Object.values(value).every(isJsonValue2);
2178
2524
  }
2179
- function isJsonValue(value) {
2525
+ function isJsonValue2(value) {
2180
2526
  if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
2181
2527
  return true;
2182
2528
  }
2183
2529
  if (Array.isArray(value)) {
2184
- return value.every(isJsonValue);
2530
+ return value.every(isJsonValue2);
2185
2531
  }
2186
- return isJsonObject(value);
2532
+ return isJsonObject2(value);
2187
2533
  }
2188
2534
  function isAgentTraceOptions(value) {
2189
2535
  if (!isObject(value)) {
@@ -2238,7 +2584,7 @@ function serializeError2(error) {
2238
2584
  ...error.stack === void 0 ? {} : { stack: error.stack }
2239
2585
  };
2240
2586
  }
2241
- return isJsonValue(error) ? error : String(error);
2587
+ return isJsonValue2(error) ? error : String(error);
2242
2588
  }
2243
2589
 
2244
2590
  // src/runtime/approvals.ts
@@ -2484,27 +2830,70 @@ function publicApproval(approval) {
2484
2830
  return rest;
2485
2831
  }
2486
2832
 
2487
- // src/runtime/json.ts
2488
- function toJsonValue2(value) {
2489
- if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
2490
- return value;
2491
- }
2492
- if (value === void 0) {
2493
- return null;
2833
+ // src/runtime/evals.ts
2834
+ import { runEvalSuite } from "@anvia/core";
2835
+ function registerEvalRoutes(app, props) {
2836
+ app.get(
2837
+ "/evals",
2838
+ (c) => c.json({
2839
+ evals: props.evals.map(evalConfig)
2840
+ })
2841
+ );
2842
+ app.get("/evals/:evalId", (c) => {
2843
+ const suite = props.evalMap.get(c.req.param("evalId"));
2844
+ if (suite === void 0) {
2845
+ return errorResponse(c, 404, "not_found", "Eval suite not found");
2846
+ }
2847
+ return c.json(evalConfig(suite));
2848
+ });
2849
+ app.post("/evals/:evalId/runs", async (c) => {
2850
+ const suite = props.evalMap.get(c.req.param("evalId"));
2851
+ if (suite === void 0) {
2852
+ return errorResponse(c, 404, "not_found", "Eval suite not found");
2853
+ }
2854
+ const body = await parseEvalRunRequest(c);
2855
+ if ("error" in body) {
2856
+ return body.error;
2857
+ }
2858
+ const runId = globalThis.crypto.randomUUID();
2859
+ const startedAt = Date.now();
2860
+ const result = await runEvalSuite({
2861
+ ...suite,
2862
+ ...body.concurrency === void 0 ? {} : { concurrency: body.concurrency }
2863
+ });
2864
+ const endedAt = Date.now();
2865
+ const jsonResult = toJsonValue2(result);
2866
+ const response = {
2867
+ runId,
2868
+ suiteId: suite.id ?? suite.name,
2869
+ startedAt: new Date(startedAt).toISOString(),
2870
+ endedAt: new Date(endedAt).toISOString(),
2871
+ durationMs: endedAt - startedAt,
2872
+ result: isJsonObject2(jsonResult) ? jsonResult : { value: jsonResult }
2873
+ };
2874
+ return c.json(response);
2875
+ });
2876
+ }
2877
+ async function parseEvalRunRequest(c) {
2878
+ let body = {};
2879
+ try {
2880
+ body = await c.req.json();
2881
+ } catch {
2882
+ body = {};
2494
2883
  }
2495
- if (Array.isArray(value)) {
2496
- return value.map((item) => toJsonValue2(item));
2884
+ if (!isObject(body)) {
2885
+ return { error: errorResponse(c, 400, "bad_request", "Request body must be an object") };
2497
2886
  }
2498
- if (typeof value === "object") {
2499
- return compactJsonObject2(value);
2887
+ const request = {};
2888
+ if ("concurrency" in body) {
2889
+ if (!isPositiveInteger(body.concurrency)) {
2890
+ return {
2891
+ error: errorResponse(c, 400, "bad_request", "concurrency must be a positive integer")
2892
+ };
2893
+ }
2894
+ request.concurrency = body.concurrency;
2500
2895
  }
2501
- return String(value);
2502
- }
2503
- function compactJsonObject2(values) {
2504
- const entries = Object.entries(values).flatMap(
2505
- ([key, value]) => value === void 0 ? [] : [[key, toJsonValue2(value)]]
2506
- );
2507
- return Object.fromEntries(entries);
2896
+ return request;
2508
2897
  }
2509
2898
 
2510
2899
  // src/runtime/knowledge.ts
@@ -2878,62 +3267,343 @@ function jsonObjectFromRecord(value) {
2878
3267
  function isRecord2(value) {
2879
3268
  return typeof value === "object" && value !== null && !Array.isArray(value);
2880
3269
  }
2881
-
2882
- // src/runtime/mcps.ts
2883
- function registerMcpRoutes(app, props) {
2884
- app.get("/agents/:agentId/mcps", async (c) => {
2885
- const agentId = c.req.param("agentId");
2886
- const agent = props.agentMap.get(agentId);
2887
- if (agent === void 0) {
2888
- return errorResponse(c, 404, "not_found", "Agent not found");
3270
+
3271
+ // src/runtime/mcps.ts
3272
+ function registerMcpRoutes(app, props) {
3273
+ app.get("/agents/:agentId/mcps", async (c) => {
3274
+ const agentId = c.req.param("agentId");
3275
+ const agent = props.agentMap.get(agentId);
3276
+ if (agent === void 0) {
3277
+ return errorResponse(c, 404, "not_found", "Agent not found");
3278
+ }
3279
+ return c.json({
3280
+ agentId,
3281
+ servers: await agentMcpMetadata(agent)
3282
+ });
3283
+ });
3284
+ }
3285
+ async function agentMcpMetadata(agent) {
3286
+ const servers = /* @__PURE__ */ new Map();
3287
+ const seen = /* @__PURE__ */ new Set();
3288
+ for (const { tool, source } of agentToolItems(agent)) {
3289
+ const serverName = mcpServerName(tool);
3290
+ if (serverName === void 0) {
3291
+ continue;
3292
+ }
3293
+ const definition = await tool.definition("");
3294
+ const key = `${serverName}:${source}:${definition.name}`;
3295
+ if (seen.has(key)) {
3296
+ continue;
3297
+ }
3298
+ seen.add(key);
3299
+ const tools = servers.get(serverName) ?? [];
3300
+ tools.push({
3301
+ name: definition.name,
3302
+ description: definition.description,
3303
+ parameters: definition.parameters,
3304
+ source
3305
+ });
3306
+ servers.set(serverName, tools);
3307
+ }
3308
+ return [...servers.entries()].map(([name, tools]) => {
3309
+ const sortedTools = tools.sort((left, right) => {
3310
+ if (left.source !== right.source) {
3311
+ return left.source === "static" ? -1 : 1;
3312
+ }
3313
+ return left.name.localeCompare(right.name);
3314
+ });
3315
+ return {
3316
+ agentId: agent.id,
3317
+ name,
3318
+ toolCount: sortedTools.length,
3319
+ tools: sortedTools
3320
+ };
3321
+ }).sort((left, right) => left.name.localeCompare(right.name));
3322
+ }
3323
+
3324
+ // src/runtime/memory.ts
3325
+ var DEFAULT_USER_ID = "default";
3326
+ function registerMemoryRoutes(app, props) {
3327
+ app.get("/memory/users", async (c) => {
3328
+ const limit = parseLimit(c.req.query("limit"));
3329
+ if (limit === void 0) {
3330
+ return errorResponse(c, 400, "bad_request", "limit must be a positive integer");
3331
+ }
3332
+ const sessions = await props.sessionStore.listSessions({ limit: 100 });
3333
+ const users = /* @__PURE__ */ new Map();
3334
+ for (const session of sessions) {
3335
+ const userId = sessionUserId(session);
3336
+ const existing = users.get(userId);
3337
+ if (existing === void 0) {
3338
+ users.set(userId, {
3339
+ userId,
3340
+ conversationCount: 1,
3341
+ agentIds: [session.agentId],
3342
+ lastInteractionAt: session.updatedAt
3343
+ });
3344
+ continue;
3345
+ }
3346
+ existing.conversationCount += 1;
3347
+ if (!existing.agentIds.includes(session.agentId)) {
3348
+ existing.agentIds.push(session.agentId);
3349
+ }
3350
+ if (new Date(session.updatedAt).getTime() > new Date(existing.lastInteractionAt).getTime()) {
3351
+ existing.lastInteractionAt = session.updatedAt;
3352
+ }
3353
+ }
3354
+ const page = [...users.values()].sort(
3355
+ (left, right) => new Date(right.lastInteractionAt).getTime() - new Date(left.lastInteractionAt).getTime()
3356
+ ).slice(0, limit);
3357
+ return c.json({ users: page, total: users.size });
3358
+ });
3359
+ app.get("/memory/conversations", async (c) => {
3360
+ const limit = parseLimit(c.req.query("limit"));
3361
+ if (limit === void 0) {
3362
+ return errorResponse(c, 400, "bad_request", "limit must be a positive integer");
3363
+ }
3364
+ const agentId = optionalQueryString(c.req.query("agentId"));
3365
+ const userId = optionalQueryString(c.req.query("userId"));
3366
+ const sessions = await props.sessionStore.listSessions({
3367
+ ...agentId === void 0 ? {} : { agentId },
3368
+ limit: 100
3369
+ });
3370
+ const conversations = sessions.map(memoryConversationSummary).filter((session) => userId === void 0 || session.userId === userId).slice(0, limit);
3371
+ return c.json({
3372
+ conversations,
3373
+ total: conversations.length
3374
+ });
3375
+ });
3376
+ app.get("/memory/conversations/:conversationId/messages", async (c) => {
3377
+ const session = await props.sessionStore.getSession(c.req.param("conversationId"));
3378
+ if (session === void 0) {
3379
+ return errorResponse(c, 404, "not_found", "Conversation not found");
3380
+ }
3381
+ return c.json({
3382
+ conversation: memoryConversationSummary(session),
3383
+ messages: session.messages,
3384
+ transcript: session.transcript
3385
+ });
3386
+ });
3387
+ app.get("/memory/conversations/:conversationId/steps", async (c) => {
3388
+ const session = await props.sessionStore.getSession(c.req.param("conversationId"));
3389
+ if (session === void 0) {
3390
+ return errorResponse(c, 404, "not_found", "Conversation not found");
3391
+ }
3392
+ return c.json({
3393
+ conversation: memoryConversationSummary(session),
3394
+ steps: session.transcript
3395
+ });
3396
+ });
3397
+ }
3398
+ function memoryConversationSummary(session) {
3399
+ return {
3400
+ id: session.id,
3401
+ userId: sessionUserId(session),
3402
+ agentId: session.agentId,
3403
+ ...session.title === void 0 ? {} : { title: session.title },
3404
+ createdAt: session.createdAt,
3405
+ updatedAt: session.updatedAt,
3406
+ messageCount: session.messageCount,
3407
+ ...session.metadata === void 0 ? {} : { metadata: session.metadata }
3408
+ };
3409
+ }
3410
+ function sessionUserId(session) {
3411
+ const userId = session.metadata?.userId;
3412
+ return typeof userId === "string" && userId.trim().length > 0 ? userId : DEFAULT_USER_ID;
3413
+ }
3414
+
3415
+ // src/runtime/observability.ts
3416
+ import { stream as streamResponse } from "hono/streaming";
3417
+ var defaultBufferSize = 1e3;
3418
+ var StudioObservabilityHub = class {
3419
+ subscriptions = /* @__PURE__ */ new Set();
3420
+ emit(event) {
3421
+ for (const subscription of this.subscriptions) {
3422
+ subscription.push(event);
3423
+ }
3424
+ }
3425
+ subscribe(options = {}) {
3426
+ const subscription = createSubscription(options.types);
3427
+ this.subscriptions.add(subscription);
3428
+ return {
3429
+ close: () => {
3430
+ subscription.close();
3431
+ this.subscriptions.delete(subscription);
3432
+ },
3433
+ next: subscription.next,
3434
+ push: subscription.push
3435
+ };
3436
+ }
3437
+ };
3438
+ function observeStores(stores, hub) {
3439
+ return {
3440
+ ...stores,
3441
+ ...stores.sessions === void 0 ? {} : { sessions: observeSessionStore(stores.sessions, hub) },
3442
+ ...stores.traces === void 0 ? {} : { traces: observeTraceStore(stores.traces, hub) },
3443
+ ...stores.pipelineLogs === void 0 ? {} : { pipelineLogs: observePipelineLogStore(stores.pipelineLogs, hub) }
3444
+ };
3445
+ }
3446
+ function registerObservabilityRoutes(app, hub) {
3447
+ app.get("/observability/events", (c) => {
3448
+ const types = parseEventTypes(c.req.query("type"));
3449
+ if (types === false) {
3450
+ return c.json(
3451
+ {
3452
+ error: {
3453
+ code: "bad_request",
3454
+ message: "type must include session_log, pipeline_log, or trace"
3455
+ }
3456
+ },
3457
+ 400
3458
+ );
3459
+ }
3460
+ c.header("content-type", "application/x-ndjson; charset=utf-8");
3461
+ c.header("cache-control", "no-cache, no-transform");
3462
+ c.header("connection", "keep-alive");
3463
+ c.header("transfer-encoding", "chunked");
3464
+ c.header("x-accel-buffering", "no");
3465
+ return streamResponse(c, async (stream) => {
3466
+ const subscription = hub.subscribe(types === void 0 ? {} : { types });
3467
+ try {
3468
+ while (true) {
3469
+ const next = await subscription.next();
3470
+ if (next.done === true) {
3471
+ break;
3472
+ }
3473
+ await stream.write(`${JSON.stringify(next.value)}
3474
+ `);
3475
+ }
3476
+ } finally {
3477
+ subscription.close();
3478
+ }
3479
+ });
3480
+ });
3481
+ }
3482
+ function createSubscription(types) {
3483
+ const values = [];
3484
+ const resolvers = [];
3485
+ let closed = false;
3486
+ return {
3487
+ close() {
3488
+ closed = true;
3489
+ for (const resolve2 of resolvers.splice(0)) {
3490
+ resolve2({ done: true, value: void 0 });
3491
+ }
3492
+ },
3493
+ next() {
3494
+ const value = values.shift();
3495
+ if (value !== void 0) {
3496
+ return Promise.resolve({ done: false, value });
3497
+ }
3498
+ if (closed) {
3499
+ return Promise.resolve({ done: true, value: void 0 });
3500
+ }
3501
+ return new Promise((resolve2) => resolvers.push(resolve2));
3502
+ },
3503
+ push(event) {
3504
+ if (closed || types !== void 0 && !types.has(event.type)) {
3505
+ return;
3506
+ }
3507
+ const resolve2 = resolvers.shift();
3508
+ if (resolve2 !== void 0) {
3509
+ resolve2({ done: false, value: event });
3510
+ return;
3511
+ }
3512
+ if (values.length >= defaultBufferSize) {
3513
+ values.shift();
3514
+ }
3515
+ values.push(event);
3516
+ }
3517
+ };
3518
+ }
3519
+ function observeSessionStore(store, hub) {
3520
+ return new Proxy(store, {
3521
+ get(target, property, receiver) {
3522
+ if (property !== "appendSessionLog") {
3523
+ return boundProperty(target, property, receiver);
3524
+ }
3525
+ const appendSessionLog2 = target.appendSessionLog?.bind(target);
3526
+ if (appendSessionLog2 === void 0) {
3527
+ return void 0;
3528
+ }
3529
+ return async (...args) => {
3530
+ const log = await appendSessionLog2(...args);
3531
+ hub.emit({ type: "session_log", log });
3532
+ return log;
3533
+ };
2889
3534
  }
2890
- return c.json({
2891
- agentId,
2892
- servers: await agentMcpMetadata(agent)
2893
- });
2894
3535
  });
2895
3536
  }
2896
- async function agentMcpMetadata(agent) {
2897
- const servers = /* @__PURE__ */ new Map();
2898
- const seen = /* @__PURE__ */ new Set();
2899
- for (const { tool, source } of agentToolItems(agent)) {
2900
- const serverName = mcpServerName(tool);
2901
- if (serverName === void 0) {
2902
- continue;
3537
+ function observePipelineLogStore(store, hub) {
3538
+ return new Proxy(store, {
3539
+ get(target, property, receiver) {
3540
+ if (property !== "appendPipelineLog") {
3541
+ return boundProperty(target, property, receiver);
3542
+ }
3543
+ const appendPipelineLog2 = target.appendPipelineLog.bind(target);
3544
+ return async (...args) => {
3545
+ const log = await appendPipelineLog2(...args);
3546
+ hub.emit({ type: "pipeline_log", log });
3547
+ return log;
3548
+ };
2903
3549
  }
2904
- const definition = await tool.definition("");
2905
- const key = `${serverName}:${source}:${definition.name}`;
2906
- if (seen.has(key)) {
2907
- continue;
3550
+ });
3551
+ }
3552
+ function observeTraceStore(store, hub) {
3553
+ return new Proxy(store, {
3554
+ get(target, property, receiver) {
3555
+ if (property !== "saveTrace") {
3556
+ return boundProperty(target, property, receiver);
3557
+ }
3558
+ const saveTrace = target.saveTrace.bind(target);
3559
+ return async (...args) => {
3560
+ const trace = await saveTrace(...args);
3561
+ hub.emit({ type: "trace", trace: traceSummary2(trace) });
3562
+ return trace;
3563
+ };
2908
3564
  }
2909
- seen.add(key);
2910
- const tools = servers.get(serverName) ?? [];
2911
- tools.push({
2912
- name: definition.name,
2913
- description: definition.description,
2914
- parameters: definition.parameters,
2915
- source
2916
- });
2917
- servers.set(serverName, tools);
3565
+ });
3566
+ }
3567
+ function boundProperty(target, property, receiver) {
3568
+ const value = Reflect.get(target, property, receiver);
3569
+ return typeof value === "function" ? value.bind(target) : value;
3570
+ }
3571
+ function parseEventTypes(value) {
3572
+ if (value === void 0 || value.trim().length === 0) {
3573
+ return void 0;
2918
3574
  }
2919
- return [...servers.entries()].map(([name, tools]) => {
2920
- const sortedTools = tools.sort((left, right) => {
2921
- if (left.source !== right.source) {
2922
- return left.source === "static" ? -1 : 1;
2923
- }
2924
- return left.name.localeCompare(right.name);
2925
- });
2926
- return {
2927
- agentId: agent.id,
2928
- name,
2929
- toolCount: sortedTools.length,
2930
- tools: sortedTools
2931
- };
2932
- }).sort((left, right) => left.name.localeCompare(right.name));
3575
+ const types = /* @__PURE__ */ new Set();
3576
+ for (const type of value.split(",")) {
3577
+ const trimmed = type.trim();
3578
+ if (!isEventType(trimmed)) {
3579
+ return false;
3580
+ }
3581
+ types.add(trimmed);
3582
+ }
3583
+ return types;
3584
+ }
3585
+ function isEventType(value) {
3586
+ return value === "session_log" || value === "pipeline_log" || value === "trace";
3587
+ }
3588
+ function traceSummary2(trace) {
3589
+ return {
3590
+ id: trace.id,
3591
+ sessionId: trace.sessionId,
3592
+ ...trace.name === void 0 ? {} : { name: trace.name },
3593
+ status: trace.status,
3594
+ startedAt: trace.startedAt,
3595
+ ...trace.endedAt === void 0 ? {} : { endedAt: trace.endedAt },
3596
+ ...trace.durationMs === void 0 ? {} : { durationMs: trace.durationMs },
3597
+ ...trace.output === void 0 ? {} : { output: trace.output },
3598
+ ...trace.error === void 0 ? {} : { error: trace.error },
3599
+ ...trace.usage === void 0 ? {} : { usage: trace.usage },
3600
+ ...trace.metadata === void 0 ? {} : { metadata: trace.metadata },
3601
+ observationCount: trace.observations.length
3602
+ };
2933
3603
  }
2934
3604
 
2935
3605
  // src/runtime/pipelines.ts
2936
- import { stream as streamResponse2 } from "hono/streaming";
3606
+ import { stream as streamResponse3 } from "hono/streaming";
2937
3607
 
2938
3608
  // src/runtime/pipeline-logs.ts
2939
3609
  async function appendPipelineLog(store, input) {
@@ -3083,7 +3753,7 @@ function formatUnknown(value) {
3083
3753
  }
3084
3754
 
3085
3755
  // src/runtime/runs.ts
3086
- import { stream as streamResponse } from "hono/streaming";
3756
+ import { stream as streamResponse2 } from "hono/streaming";
3087
3757
  var AsyncEventQueue = class {
3088
3758
  values = [];
3089
3759
  resolvers = [];
@@ -3162,7 +3832,7 @@ function streamAgentRunEvents(c, events) {
3162
3832
  c.header("connection", "keep-alive");
3163
3833
  c.header("transfer-encoding", "chunked");
3164
3834
  c.header("x-accel-buffering", "no");
3165
- return streamResponse(
3835
+ return streamResponse2(
3166
3836
  c,
3167
3837
  async (stream) => {
3168
3838
  for await (const event of events) {
@@ -3662,7 +4332,7 @@ async function parseRunRequest(c) {
3662
4332
  request.toolConcurrency = body.toolConcurrency;
3663
4333
  }
3664
4334
  if ("metadata" in body) {
3665
- if (!isJsonObject(body.metadata)) {
4335
+ if (!isJsonObject2(body.metadata)) {
3666
4336
  return { error: errorResponse(c, 400, "bad_request", "metadata must be an object") };
3667
4337
  }
3668
4338
  request.metadata = body.metadata;
@@ -3756,96 +4426,135 @@ function registerPipelineRoutes(app, props) {
3756
4426
  if ("error" in body) {
3757
4427
  return body.error;
3758
4428
  }
3759
- const runId = globalThis.crypto.randomUUID();
3760
- const startedAt = Date.now();
3761
- const startedAtIso = new Date(startedAt).toISOString();
4429
+ return executePipelineRun(c, props, pipeline, body);
4430
+ });
4431
+ app.post("/pipelines/:pipelineId/runs/:runId/replay", async (c) => {
4432
+ const pipeline = props.pipelineMap.get(c.req.param("pipelineId"));
4433
+ if (pipeline === void 0) {
4434
+ return errorResponse(c, 404, "not_found", "Pipeline not found");
4435
+ }
4436
+ if (props.runStore === void 0) {
4437
+ return errorResponse(
4438
+ c,
4439
+ 501,
4440
+ "unsupported_capability",
4441
+ 'Capability "pipelines.runs" is not implemented by this runner',
4442
+ { capability: "pipelines", operation: "runs" }
4443
+ );
4444
+ }
4445
+ const body = await parsePipelineReplayRequest(c);
4446
+ if ("error" in body) {
4447
+ return body.error;
4448
+ }
4449
+ const sourceRunId = c.req.param("runId");
4450
+ const runs = await props.runStore.listPipelineRuns({
4451
+ pipelineId: pipeline.id,
4452
+ limit: 1e3
4453
+ });
4454
+ const sourceRun = runs.find((run) => run.runId === sourceRunId);
4455
+ if (sourceRun === void 0) {
4456
+ return errorResponse(c, 404, "not_found", "Pipeline run not found");
4457
+ }
4458
+ if (sourceRun.status === "running") {
4459
+ return errorResponse(c, 409, "conflict", "Cannot replay a running pipeline run");
4460
+ }
4461
+ return executePipelineRun(c, props, pipeline, {
4462
+ input: sourceRun.input,
4463
+ ...body.stream === void 0 ? {} : { stream: body.stream },
4464
+ metadata: replayMetadata(sourceRun.metadata, body.metadata, sourceRun.runId)
4465
+ });
4466
+ });
4467
+ }
4468
+ async function executePipelineRun(c, props, pipeline, body) {
4469
+ const runId = globalThis.crypto.randomUUID();
4470
+ const startedAt = Date.now();
4471
+ const startedAtIso = new Date(startedAt).toISOString();
4472
+ await appendPipelineLog(
4473
+ props.logStore,
4474
+ pipelineRunReceivedLog({
4475
+ pipeline,
4476
+ runId,
4477
+ stream: body.stream === true,
4478
+ input: body.input,
4479
+ ...body.metadata === void 0 ? {} : { metadata: body.metadata }
4480
+ })
4481
+ );
4482
+ await savePipelineRun(props.runStore, {
4483
+ runId,
4484
+ pipelineId: pipeline.id,
4485
+ status: "running",
4486
+ input: body.input,
4487
+ ...body.metadata === void 0 ? {} : { metadata: body.metadata },
4488
+ startedAt: startedAtIso
4489
+ });
4490
+ if (body.stream === true) {
4491
+ return streamPipelineRun(c, {
4492
+ pipeline,
4493
+ runId,
4494
+ input: body.input,
4495
+ startedAt,
4496
+ startedAtIso,
4497
+ ...body.metadata === void 0 ? {} : { metadata: body.metadata },
4498
+ ...props.logStore === void 0 ? {} : { logStore: props.logStore },
4499
+ ...props.runStore === void 0 ? {} : { runStore: props.runStore }
4500
+ });
4501
+ }
4502
+ try {
4503
+ await appendPipelineLog(props.logStore, pipelineRunStartedLog(pipeline, runId));
4504
+ const output = await pipeline.pipeline.run(body.input, {
4505
+ observer: {
4506
+ async onEvent(event) {
4507
+ await appendPipelineLog(props.logStore, pipelineStageLog(pipeline.id, runId, event));
4508
+ }
4509
+ }
4510
+ });
4511
+ const jsonOutput = toJsonValue3(output);
4512
+ const endedAt = Date.now();
4513
+ await savePipelineRun(props.runStore, {
4514
+ runId,
4515
+ pipelineId: pipeline.id,
4516
+ status: "success",
4517
+ input: body.input,
4518
+ output: jsonOutput,
4519
+ ...body.metadata === void 0 ? {} : { metadata: body.metadata },
4520
+ startedAt: startedAtIso,
4521
+ endedAt: new Date(endedAt).toISOString(),
4522
+ durationMs: endedAt - startedAt
4523
+ });
3762
4524
  await appendPipelineLog(
3763
4525
  props.logStore,
3764
- pipelineRunReceivedLog({
3765
- pipeline,
4526
+ pipelineRunCompletedLog({
4527
+ pipelineId: pipeline.id,
3766
4528
  runId,
3767
- stream: body.stream === true,
3768
- input: body.input,
3769
- ...body.metadata === void 0 ? {} : { metadata: body.metadata }
4529
+ durationMs: endedAt - startedAt,
4530
+ output: jsonOutput
3770
4531
  })
3771
4532
  );
4533
+ const response = {
4534
+ runId,
4535
+ pipelineId: pipeline.id,
4536
+ output: jsonOutput
4537
+ };
4538
+ return c.json(response);
4539
+ } catch (error) {
4540
+ const endedAt = Date.now();
3772
4541
  await savePipelineRun(props.runStore, {
3773
4542
  runId,
3774
4543
  pipelineId: pipeline.id,
3775
- status: "running",
4544
+ status: "error",
3776
4545
  input: body.input,
4546
+ error: serializeError2(error),
3777
4547
  ...body.metadata === void 0 ? {} : { metadata: body.metadata },
3778
- startedAt: startedAtIso
4548
+ startedAt: startedAtIso,
4549
+ endedAt: new Date(endedAt).toISOString(),
4550
+ durationMs: endedAt - startedAt
3779
4551
  });
3780
- if (body.stream === true) {
3781
- return streamPipelineRun(c, {
3782
- pipeline,
3783
- runId,
3784
- input: body.input,
3785
- startedAt,
3786
- startedAtIso,
3787
- ...body.metadata === void 0 ? {} : { metadata: body.metadata },
3788
- ...props.logStore === void 0 ? {} : { logStore: props.logStore },
3789
- ...props.runStore === void 0 ? {} : { runStore: props.runStore }
3790
- });
3791
- }
3792
- try {
3793
- await appendPipelineLog(props.logStore, pipelineRunStartedLog(pipeline, runId));
3794
- const output = await pipeline.pipeline.run(body.input, {
3795
- observer: {
3796
- async onEvent(event) {
3797
- await appendPipelineLog(props.logStore, pipelineStageLog(pipeline.id, runId, event));
3798
- }
3799
- }
3800
- });
3801
- const jsonOutput = toJsonValue3(output);
3802
- const endedAt = Date.now();
3803
- await savePipelineRun(props.runStore, {
3804
- runId,
3805
- pipelineId: pipeline.id,
3806
- status: "success",
3807
- input: body.input,
3808
- output: jsonOutput,
3809
- ...body.metadata === void 0 ? {} : { metadata: body.metadata },
3810
- startedAt: startedAtIso,
3811
- endedAt: new Date(endedAt).toISOString(),
3812
- durationMs: endedAt - startedAt
3813
- });
3814
- await appendPipelineLog(
3815
- props.logStore,
3816
- pipelineRunCompletedLog({
3817
- pipelineId: pipeline.id,
3818
- runId,
3819
- durationMs: endedAt - startedAt,
3820
- output: jsonOutput
3821
- })
3822
- );
3823
- const response = {
3824
- runId,
3825
- pipelineId: pipeline.id,
3826
- output: jsonOutput
3827
- };
3828
- return c.json(response);
3829
- } catch (error) {
3830
- const endedAt = Date.now();
3831
- await savePipelineRun(props.runStore, {
3832
- runId,
3833
- pipelineId: pipeline.id,
3834
- status: "error",
3835
- input: body.input,
3836
- error: serializeError2(error),
3837
- ...body.metadata === void 0 ? {} : { metadata: body.metadata },
3838
- startedAt: startedAtIso,
3839
- endedAt: new Date(endedAt).toISOString(),
3840
- durationMs: endedAt - startedAt
3841
- });
3842
- await appendPipelineLog(
3843
- props.logStore,
3844
- pipelineRunFailedLog(pipeline.id, runId, error, startedAt)
3845
- );
3846
- return errorResponse(c, 500, "internal_error", "Pipeline run failed", serializeError2(error));
3847
- }
3848
- });
4552
+ await appendPipelineLog(
4553
+ props.logStore,
4554
+ pipelineRunFailedLog(pipeline.id, runId, error, startedAt)
4555
+ );
4556
+ return errorResponse(c, 500, "internal_error", "Pipeline run failed", serializeError2(error));
4557
+ }
3849
4558
  }
3850
4559
  function pipelineDetail(pipeline) {
3851
4560
  const graph = pipeline.pipeline.graph();
@@ -3861,7 +4570,7 @@ function streamPipelineRun(c, props) {
3861
4570
  c.header("connection", "keep-alive");
3862
4571
  c.header("transfer-encoding", "chunked");
3863
4572
  c.header("x-accel-buffering", "no");
3864
- return streamResponse2(
4573
+ return streamResponse3(
3865
4574
  c,
3866
4575
  async (stream) => {
3867
4576
  for await (const event of pipelineRunEvents(props)) {
@@ -3969,7 +4678,7 @@ async function parsePipelineRunRequest(c) {
3969
4678
  if (!isObject(body)) {
3970
4679
  return { error: errorResponse(c, 400, "bad_request", "Request body must be an object") };
3971
4680
  }
3972
- if (!("input" in body) || !isJsonValue2(body.input)) {
4681
+ if (!("input" in body) || !isJsonValue3(body.input)) {
3973
4682
  return { error: errorResponse(c, 400, "bad_request", "input must be JSON-compatible") };
3974
4683
  }
3975
4684
  const request = {
@@ -3982,13 +4691,45 @@ async function parsePipelineRunRequest(c) {
3982
4691
  request.stream = body.stream;
3983
4692
  }
3984
4693
  if ("metadata" in body) {
3985
- if (!isJsonObject(body.metadata)) {
4694
+ if (!isJsonObject2(body.metadata)) {
4695
+ return { error: errorResponse(c, 400, "bad_request", "metadata must be an object") };
4696
+ }
4697
+ request.metadata = body.metadata;
4698
+ }
4699
+ return request;
4700
+ }
4701
+ async function parsePipelineReplayRequest(c) {
4702
+ let body;
4703
+ try {
4704
+ body = await c.req.json();
4705
+ } catch {
4706
+ return { error: errorResponse(c, 400, "bad_request", "Request body must be JSON") };
4707
+ }
4708
+ if (!isObject(body)) {
4709
+ return { error: errorResponse(c, 400, "bad_request", "Request body must be an object") };
4710
+ }
4711
+ const request = {};
4712
+ if ("stream" in body) {
4713
+ if (typeof body.stream !== "boolean") {
4714
+ return { error: errorResponse(c, 400, "bad_request", "stream must be a boolean") };
4715
+ }
4716
+ request.stream = body.stream;
4717
+ }
4718
+ if ("metadata" in body) {
4719
+ if (!isJsonObject2(body.metadata)) {
3986
4720
  return { error: errorResponse(c, 400, "bad_request", "metadata must be an object") };
3987
4721
  }
3988
4722
  request.metadata = body.metadata;
3989
4723
  }
3990
4724
  return request;
3991
4725
  }
4726
+ function replayMetadata(sourceMetadata, requestMetadata, sourceRunId) {
4727
+ return {
4728
+ ...sourceMetadata ?? {},
4729
+ ...requestMetadata ?? {},
4730
+ replayOf: sourceRunId
4731
+ };
4732
+ }
3992
4733
  function parsePipelineLogLimit(value) {
3993
4734
  if (value === void 0 || value.trim().length === 0) {
3994
4735
  return 200;
@@ -4009,20 +4750,20 @@ function parsePipelineLogAfter(value) {
4009
4750
  }
4010
4751
  return after;
4011
4752
  }
4012
- function isJsonValue2(value) {
4753
+ function isJsonValue3(value) {
4013
4754
  if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
4014
4755
  return Number.isFinite(value) || typeof value !== "number";
4015
4756
  }
4016
4757
  if (Array.isArray(value)) {
4017
- return value.every(isJsonValue2);
4758
+ return value.every(isJsonValue3);
4018
4759
  }
4019
4760
  if (isObject(value)) {
4020
- return Object.values(value).every((item) => item === void 0 || isJsonValue2(item));
4761
+ return Object.values(value).every((item) => item === void 0 || isJsonValue3(item));
4021
4762
  }
4022
4763
  return false;
4023
4764
  }
4024
4765
  function toJsonValue3(value) {
4025
- if (isJsonValue2(value)) {
4766
+ if (isJsonValue3(value)) {
4026
4767
  return value;
4027
4768
  }
4028
4769
  if (value === void 0) {
@@ -4030,7 +4771,7 @@ function toJsonValue3(value) {
4030
4771
  }
4031
4772
  try {
4032
4773
  const parsed = JSON.parse(JSON.stringify(value));
4033
- return isJsonValue2(parsed) ? parsed : String(value);
4774
+ return isJsonValue3(parsed) ? parsed : String(value);
4034
4775
  } catch {
4035
4776
  return String(value);
4036
4777
  }
@@ -4916,7 +5657,7 @@ async function parseCreateSessionRequest(c) {
4916
5657
  }
4917
5658
  }
4918
5659
  if ("metadata" in body) {
4919
- if (!isJsonObject(body.metadata)) {
5660
+ if (!isJsonObject2(body.metadata)) {
4920
5661
  return { error: errorResponse(c, 400, "bad_request", "metadata must be an object") };
4921
5662
  }
4922
5663
  request.metadata = body.metadata;
@@ -4924,6 +5665,44 @@ async function parseCreateSessionRequest(c) {
4924
5665
  return request;
4925
5666
  }
4926
5667
 
5668
+ // src/runtime/status.ts
5669
+ function registerStatusRoutes(app, props) {
5670
+ app.get("/status", async (c) => {
5671
+ const summary = {
5672
+ runner: {
5673
+ id: runnerId(props.options),
5674
+ ...props.options.name === void 0 ? {} : { name: props.options.name },
5675
+ ...props.options.version === void 0 ? {} : { version: props.options.version }
5676
+ },
5677
+ storage: {
5678
+ ...props.stores.sessions?.kind === void 0 ? {} : { sessions: props.stores.sessions.kind },
5679
+ ...props.stores.traces?.kind === void 0 ? {} : { traces: props.stores.traces.kind },
5680
+ ...props.stores.pipelineLogs === void 0 ? {} : { pipelineLogs: "available" },
5681
+ ...props.stores.pipelineRuns === void 0 ? {} : { pipelineRuns: "available" }
5682
+ },
5683
+ counts: {
5684
+ agents: props.agents.length,
5685
+ pipelines: props.pipelines.length,
5686
+ ...props.stores.sessions === void 0 ? {} : { sessions: (await props.stores.sessions.listSessions({ limit: 100 })).length },
5687
+ ...props.stores.traces?.listTraces === void 0 ? {} : { traces: (await props.stores.traces.listTraces({ limit: 100 })).length },
5688
+ ...props.stores.pipelineRuns === void 0 || props.pipelines.length === 0 ? {} : {
5689
+ pipelineRuns: (await Promise.all(
5690
+ props.pipelines.map(
5691
+ (pipeline) => props.stores.pipelineRuns?.listPipelineRuns({
5692
+ pipelineId: pipeline.id,
5693
+ limit: 100
5694
+ })
5695
+ )
5696
+ )).reduce((sum, runs) => sum + (runs?.length ?? 0), 0)
5697
+ }
5698
+ },
5699
+ capabilities: capabilityConfig(props.options, props.agents, props.pipelines, props.stores),
5700
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
5701
+ };
5702
+ return c.json(summary);
5703
+ });
5704
+ }
5705
+
4927
5706
  // src/runtime/tools.ts
4928
5707
  function registerToolRoutes(app, props) {
4929
5708
  app.get("/agents/:agentId/tools", async (c) => {
@@ -4937,6 +5716,83 @@ function registerToolRoutes(app, props) {
4937
5716
  tools: await agentToolMetadata(agent)
4938
5717
  });
4939
5718
  });
5719
+ app.post("/agents/:agentId/tools/:toolName/runs", async (c) => {
5720
+ const agentId = c.req.param("agentId");
5721
+ const toolName = c.req.param("toolName");
5722
+ const agent = props.agentMap.get(agentId);
5723
+ if (agent === void 0) {
5724
+ return errorResponse(c, 404, "not_found", "Agent not found");
5725
+ }
5726
+ if (agent.agent.getTool(toolName) === void 0) {
5727
+ return errorResponse(c, 404, "not_found", "Tool not found");
5728
+ }
5729
+ const body = await parseToolRunRequest(c);
5730
+ if ("error" in body) {
5731
+ return body.error;
5732
+ }
5733
+ const started = Date.now();
5734
+ const startedAt = new Date(started).toISOString();
5735
+ const events = [];
5736
+ try {
5737
+ const result = await agent.agent.callTool(toolName, JSON.stringify(body.args), {
5738
+ emitStreamEvent(event) {
5739
+ events.push(event);
5740
+ }
5741
+ });
5742
+ const ended = Date.now();
5743
+ return c.json({
5744
+ agentId,
5745
+ toolName,
5746
+ status: "success",
5747
+ result: toJsonValue2(result),
5748
+ durationMs: ended - started,
5749
+ startedAt,
5750
+ endedAt: new Date(ended).toISOString(),
5751
+ events: events.map(toJsonValue2)
5752
+ });
5753
+ } catch (error) {
5754
+ const ended = Date.now();
5755
+ return c.json(
5756
+ {
5757
+ agentId,
5758
+ toolName,
5759
+ status: "error",
5760
+ error: serializeUnknown(error),
5761
+ durationMs: ended - started,
5762
+ startedAt,
5763
+ endedAt: new Date(ended).toISOString(),
5764
+ events: events.map(toJsonValue2)
5765
+ },
5766
+ 500
5767
+ );
5768
+ }
5769
+ });
5770
+ }
5771
+ async function parseToolRunRequest(c) {
5772
+ let body;
5773
+ try {
5774
+ body = await c.req.json();
5775
+ } catch {
5776
+ return { error: errorResponse(c, 400, "bad_request", "Request body must be JSON") };
5777
+ }
5778
+ if (body === void 0 || body === null) {
5779
+ return { args: {} };
5780
+ }
5781
+ if (!isJsonObject2(body)) {
5782
+ return { error: errorResponse(c, 400, "bad_request", "Request body must be an object") };
5783
+ }
5784
+ const args = Object.hasOwn(body, "args") ? body.args : {};
5785
+ if (!isJsonValue2(args)) {
5786
+ return { error: errorResponse(c, 400, "bad_request", "args must be JSON-compatible") };
5787
+ }
5788
+ const request = { args };
5789
+ if (Object.hasOwn(body, "context")) {
5790
+ if (!isJsonObject2(body.context)) {
5791
+ return { error: errorResponse(c, 400, "bad_request", "context must be an object") };
5792
+ }
5793
+ request.context = body.context;
5794
+ }
5795
+ return request;
4940
5796
  }
4941
5797
  async function agentToolMetadata(agent) {
4942
5798
  const seen = /* @__PURE__ */ new Set();
@@ -5067,11 +5923,15 @@ var Studio = class {
5067
5923
  function studioOptionsFromTargets(targets, options) {
5068
5924
  const agents = targets.filter((target) => target instanceof Agent);
5069
5925
  const pipelines = targets.filter(
5926
+ // biome-ignore lint/suspicious/noExplicitAny: Studio accepts heterogeneous user pipelines.
5070
5927
  (target) => target instanceof Pipeline
5071
5928
  );
5072
5929
  return {
5073
5930
  agents: inferStudioAgents(agents, options.quickPrompts ?? {}),
5074
- pipelines: inferStudioPipelines(pipelines)
5931
+ pipelines: inferStudioPipelines(pipelines),
5932
+ evals: options.evals ?? [],
5933
+ ...options.stores === void 0 ? {} : { stores: options.stores },
5934
+ ...options.ui === void 0 ? {} : { ui: options.ui }
5075
5935
  };
5076
5936
  }
5077
5937
  function inferStudioAgents(agents, quickPrompts) {
@@ -5122,11 +5982,13 @@ function agentMetadata(agent) {
5122
5982
  };
5123
5983
  }
5124
5984
  function createStudioApp(options) {
5125
- const stores = resolveStores(options);
5985
+ const observabilityHub = new StudioObservabilityHub();
5986
+ const stores = observeStores(resolveStores(options), observabilityHub);
5126
5987
  const agents = normalizeAgents(options.agents).map((agent) => withStudioSessionMemory(agent, stores.sessions)).map((agent) => withStudioTraceObserver(agent, stores.traces));
5127
5988
  const pipelines = normalizePipelines(options.pipelines);
5128
5989
  const agentMap = new Map(agents.map((agent) => [agent.id, agent]));
5129
5990
  const pipelineMap = new Map(pipelines.map((pipeline) => [pipeline.id, pipeline]));
5991
+ const evalMap = new Map(options.evals.map((suite) => [suite.id ?? suite.name, suite]));
5130
5992
  const approvalRuntime = createApprovalRuntime();
5131
5993
  const questionRuntime = createQuestionRuntime();
5132
5994
  const app = new HonoApp();
@@ -5149,6 +6011,7 @@ function createStudioApp(options) {
5149
6011
  })
5150
6012
  );
5151
6013
  app.get("/config", (c) => c.json(buildConfig(options, agents, pipelines, stores)));
6014
+ registerStatusRoutes(app, { options, agents, pipelines, stores });
5152
6015
  app.get("/agents", (c) => c.json({ agents: agents.map(agentConfig) }));
5153
6016
  app.get("/agents/:agentId", (c) => {
5154
6017
  const agent = agentMap.get(c.req.param("agentId"));
@@ -5157,10 +6020,22 @@ function createStudioApp(options) {
5157
6020
  }
5158
6021
  return c.json(agentConfig(agent));
5159
6022
  });
6023
+ app.get("/agents/:agentId/runtime", (c) => {
6024
+ const agent = agentMap.get(c.req.param("agentId"));
6025
+ if (agent === void 0) {
6026
+ return errorResponse(c, 404, "not_found", "Agent not found");
6027
+ }
6028
+ return c.json(agentRuntimeSummary(agent));
6029
+ });
5160
6030
  registerMcpRoutes(app, { agentMap });
5161
6031
  registerToolRoutes(app, { agentMap });
5162
6032
  registerApprovalRoutes(app, approvalRuntime);
5163
6033
  registerQuestionRoutes(app, questionRuntime);
6034
+ registerObservabilityRoutes(app, observabilityHub);
6035
+ registerEvalRoutes(app, {
6036
+ evals: options.evals,
6037
+ evalMap
6038
+ });
5164
6039
  registerKnowledgeRoutes(app, {
5165
6040
  agents,
5166
6041
  ...stores.traces === void 0 ? {} : { traceStore: stores.traces }
@@ -5348,6 +6223,9 @@ function createStudioApp(options) {
5348
6223
  }
5349
6224
  });
5350
6225
  if (stores.sessions !== void 0) {
6226
+ registerMemoryRoutes(app, {
6227
+ sessionStore: stores.sessions
6228
+ });
5351
6229
  registerSessionRoutes(app, {
5352
6230
  agentMap,
5353
6231
  sessionStore: stores.sessions,
@@ -5474,6 +6352,7 @@ function approvalRequestHandler(hook) {
5474
6352
  export {
5475
6353
  Studio,
5476
6354
  StudioTraceObserver,
6355
+ createInMemoryStudioStore,
5477
6356
  createSqliteSessionStore
5478
6357
  };
5479
6358
  //# sourceMappingURL=index.js.map