@anvia/studio 0.1.2 → 0.2.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.d.ts +150 -6
- package/dist/index.js +1996 -341
- package/dist/index.js.map +1 -1
- package/dist/ui/assets/index-BY0kc1ar.js +91 -0
- package/dist/ui/assets/index-BY0kc1ar.js.map +1 -0
- package/dist/ui/assets/index-DcPwH55d.css +1 -0
- package/dist/ui/index.html +6 -2
- package/package.json +6 -5
- package/dist/ui/assets/index-Dy72BB4f.js +0 -85
- package/dist/ui/assets/index-Dy72BB4f.js.map +0 -1
- package/dist/ui/assets/index-VMCHaVgC.css +0 -1
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Agent,
|
|
4
4
|
createHook as createHook3,
|
|
5
5
|
Message,
|
|
6
|
+
Pipeline,
|
|
6
7
|
resolveMemoryOptions
|
|
7
8
|
} from "@anvia/core";
|
|
8
9
|
import { serve } from "@hono/node-server";
|
|
@@ -692,12 +693,15 @@ var SqliteSessionStore = class {
|
|
|
692
693
|
db;
|
|
693
694
|
listSessions(options) {
|
|
694
695
|
const db = this.database();
|
|
695
|
-
const agentClause = options.agentId === void 0 ? "" : "WHERE agent_id = $agentId";
|
|
696
|
+
const agentClause = options.agentId === void 0 ? "" : "WHERE s.agent_id = $agentId";
|
|
696
697
|
const rows = db.prepare(
|
|
697
|
-
`SELECT id, agent_id, title, metadata_json,
|
|
698
|
-
|
|
698
|
+
`SELECT s.id, s.agent_id, s.title, s.metadata_json, s.created_at, s.updated_at,
|
|
699
|
+
COUNT(m.message_index) AS message_count
|
|
700
|
+
FROM runner_sessions s
|
|
701
|
+
LEFT JOIN runner_session_messages m ON m.session_id = s.id
|
|
699
702
|
${agentClause}
|
|
700
|
-
|
|
703
|
+
GROUP BY s.id, s.agent_id, s.title, s.metadata_json, s.created_at, s.updated_at
|
|
704
|
+
ORDER BY s.updated_at DESC
|
|
701
705
|
LIMIT $limit`
|
|
702
706
|
).all({
|
|
703
707
|
$agentId: options.agentId ?? null,
|
|
@@ -714,11 +718,9 @@ var SqliteSessionStore = class {
|
|
|
714
718
|
agent_id,
|
|
715
719
|
title,
|
|
716
720
|
metadata_json,
|
|
717
|
-
messages_json,
|
|
718
|
-
transcript_json,
|
|
719
721
|
created_at,
|
|
720
722
|
updated_at
|
|
721
|
-
) VALUES ($id, $agentId, $title, $metadata,
|
|
723
|
+
) VALUES ($id, $agentId, $title, $metadata, $now, $now)`
|
|
722
724
|
).run({
|
|
723
725
|
$id: input.id,
|
|
724
726
|
$agentId: input.agentId,
|
|
@@ -738,7 +740,7 @@ var SqliteSessionStore = class {
|
|
|
738
740
|
}
|
|
739
741
|
getSession(id) {
|
|
740
742
|
const row = this.getSessionRow(id);
|
|
741
|
-
return row === void 0 ? void 0 : toSession(row, this.listSessionRunRows(id));
|
|
743
|
+
return row === void 0 ? void 0 : toSession(row, this.listSessionMessages(id), this.listSessionRunRows(id));
|
|
742
744
|
}
|
|
743
745
|
load(context) {
|
|
744
746
|
const session = this.getSession(context.sessionId);
|
|
@@ -749,7 +751,7 @@ var SqliteSessionStore = class {
|
|
|
749
751
|
try {
|
|
750
752
|
db.exec("BEGIN IMMEDIATE");
|
|
751
753
|
const row = db.prepare(
|
|
752
|
-
`SELECT id, agent_id, title, metadata_json,
|
|
754
|
+
`SELECT id, agent_id, title, metadata_json, created_at, updated_at
|
|
753
755
|
FROM runner_sessions
|
|
754
756
|
WHERE id = $id`
|
|
755
757
|
).get({ $id: input.context.sessionId });
|
|
@@ -757,17 +759,15 @@ var SqliteSessionStore = class {
|
|
|
757
759
|
db.exec("ROLLBACK");
|
|
758
760
|
return Promise.resolve();
|
|
759
761
|
}
|
|
760
|
-
const current = toSession(row);
|
|
761
|
-
const messages = [...current.messages, ...input.messages];
|
|
762
762
|
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
763
|
+
const nextIndex = this.nextMessageIndex(input.context.sessionId);
|
|
764
|
+
this.insertMessages(input.context.sessionId, input.messages, nextIndex, updatedAt);
|
|
763
765
|
db.prepare(
|
|
764
766
|
`UPDATE runner_sessions
|
|
765
|
-
SET
|
|
766
|
-
updated_at = $updatedAt
|
|
767
|
+
SET updated_at = $updatedAt
|
|
767
768
|
WHERE id = $id`
|
|
768
769
|
).run({
|
|
769
770
|
$id: input.context.sessionId,
|
|
770
|
-
$messages: JSON.stringify(messages),
|
|
771
771
|
$updatedAt: updatedAt
|
|
772
772
|
});
|
|
773
773
|
db.exec("COMMIT");
|
|
@@ -786,14 +786,15 @@ var SqliteSessionStore = class {
|
|
|
786
786
|
db.exec("BEGIN IMMEDIATE");
|
|
787
787
|
db.prepare(
|
|
788
788
|
`UPDATE runner_sessions
|
|
789
|
-
SET
|
|
790
|
-
transcript_json = '[]',
|
|
791
|
-
updated_at = $updatedAt
|
|
789
|
+
SET updated_at = $updatedAt
|
|
792
790
|
WHERE id = $id`
|
|
793
791
|
).run({
|
|
794
792
|
$id: context.sessionId,
|
|
795
793
|
$updatedAt: updatedAt
|
|
796
794
|
});
|
|
795
|
+
db.prepare("DELETE FROM runner_session_messages WHERE session_id = $id").run({
|
|
796
|
+
$id: context.sessionId
|
|
797
|
+
});
|
|
797
798
|
db.prepare("DELETE FROM runner_session_runs WHERE session_id = $id").run({
|
|
798
799
|
$id: context.sessionId
|
|
799
800
|
});
|
|
@@ -828,7 +829,11 @@ var SqliteSessionStore = class {
|
|
|
828
829
|
db.exec("ROLLBACK");
|
|
829
830
|
return void 0;
|
|
830
831
|
}
|
|
831
|
-
const current = toSession(
|
|
832
|
+
const current = toSession(
|
|
833
|
+
row,
|
|
834
|
+
this.listSessionMessages(input.id),
|
|
835
|
+
this.listSessionRunRows(input.id)
|
|
836
|
+
);
|
|
832
837
|
const title = current.title ?? input.title;
|
|
833
838
|
db.prepare(
|
|
834
839
|
`INSERT INTO runner_session_runs (
|
|
@@ -885,12 +890,179 @@ var SqliteSessionStore = class {
|
|
|
885
890
|
throw error;
|
|
886
891
|
}
|
|
887
892
|
}
|
|
893
|
+
appendSessionLog(input) {
|
|
894
|
+
const db = this.database();
|
|
895
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
896
|
+
try {
|
|
897
|
+
db.exec("BEGIN IMMEDIATE");
|
|
898
|
+
const row = this.getSessionRow(input.sessionId);
|
|
899
|
+
if (row === void 0) {
|
|
900
|
+
throw new Error("Session not found");
|
|
901
|
+
}
|
|
902
|
+
const sequence = this.nextSessionLogSequence(input.sessionId);
|
|
903
|
+
const entry = {
|
|
904
|
+
id: globalThis.crypto.randomUUID(),
|
|
905
|
+
sessionId: input.sessionId,
|
|
906
|
+
...input.runId === void 0 ? {} : { runId: input.runId },
|
|
907
|
+
sequence,
|
|
908
|
+
timestamp: now,
|
|
909
|
+
level: input.level,
|
|
910
|
+
category: input.category,
|
|
911
|
+
event: input.event,
|
|
912
|
+
message: input.message,
|
|
913
|
+
...input.metadata === void 0 ? {} : { metadata: input.metadata }
|
|
914
|
+
};
|
|
915
|
+
db.prepare(
|
|
916
|
+
`INSERT INTO runner_session_logs (
|
|
917
|
+
id,
|
|
918
|
+
session_id,
|
|
919
|
+
run_id,
|
|
920
|
+
sequence,
|
|
921
|
+
timestamp,
|
|
922
|
+
level,
|
|
923
|
+
category,
|
|
924
|
+
event,
|
|
925
|
+
message,
|
|
926
|
+
metadata_json
|
|
927
|
+
) VALUES (
|
|
928
|
+
$id,
|
|
929
|
+
$sessionId,
|
|
930
|
+
$runId,
|
|
931
|
+
$sequence,
|
|
932
|
+
$timestamp,
|
|
933
|
+
$level,
|
|
934
|
+
$category,
|
|
935
|
+
$event,
|
|
936
|
+
$message,
|
|
937
|
+
$metadata
|
|
938
|
+
)`
|
|
939
|
+
).run({
|
|
940
|
+
$id: entry.id,
|
|
941
|
+
$sessionId: entry.sessionId,
|
|
942
|
+
$runId: entry.runId ?? null,
|
|
943
|
+
$sequence: entry.sequence,
|
|
944
|
+
$timestamp: entry.timestamp,
|
|
945
|
+
$level: entry.level,
|
|
946
|
+
$category: entry.category,
|
|
947
|
+
$event: entry.event,
|
|
948
|
+
$message: entry.message,
|
|
949
|
+
$metadata: entry.metadata === void 0 ? null : JSON.stringify(entry.metadata)
|
|
950
|
+
});
|
|
951
|
+
db.exec("COMMIT");
|
|
952
|
+
return entry;
|
|
953
|
+
} catch (error) {
|
|
954
|
+
if (db.isTransaction) {
|
|
955
|
+
db.exec("ROLLBACK");
|
|
956
|
+
}
|
|
957
|
+
throw error;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
listSessionLogs(options) {
|
|
961
|
+
const db = this.database();
|
|
962
|
+
const afterClause = options.after === void 0 ? "" : "AND sequence > $after";
|
|
963
|
+
const rows = db.prepare(
|
|
964
|
+
`SELECT id, session_id, run_id, sequence, timestamp, level, category, event, message,
|
|
965
|
+
metadata_json
|
|
966
|
+
FROM runner_session_logs
|
|
967
|
+
WHERE session_id = $sessionId
|
|
968
|
+
${afterClause}
|
|
969
|
+
ORDER BY sequence ASC
|
|
970
|
+
LIMIT $limit`
|
|
971
|
+
).all({
|
|
972
|
+
$sessionId: options.sessionId,
|
|
973
|
+
$after: options.after ?? null,
|
|
974
|
+
$limit: options.limit
|
|
975
|
+
});
|
|
976
|
+
return rows.map(toSessionLog);
|
|
977
|
+
}
|
|
978
|
+
appendPipelineLog(input) {
|
|
979
|
+
const db = this.database();
|
|
980
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
981
|
+
try {
|
|
982
|
+
db.exec("BEGIN IMMEDIATE");
|
|
983
|
+
const sequence = this.nextPipelineLogSequence(input.pipelineId);
|
|
984
|
+
const entry = {
|
|
985
|
+
id: globalThis.crypto.randomUUID(),
|
|
986
|
+
pipelineId: input.pipelineId,
|
|
987
|
+
...input.runId === void 0 ? {} : { runId: input.runId },
|
|
988
|
+
sequence,
|
|
989
|
+
timestamp: now,
|
|
990
|
+
level: input.level,
|
|
991
|
+
category: input.category,
|
|
992
|
+
event: input.event,
|
|
993
|
+
message: input.message,
|
|
994
|
+
...input.metadata === void 0 ? {} : { metadata: input.metadata }
|
|
995
|
+
};
|
|
996
|
+
db.prepare(
|
|
997
|
+
`INSERT INTO runner_pipeline_logs (
|
|
998
|
+
id,
|
|
999
|
+
pipeline_id,
|
|
1000
|
+
run_id,
|
|
1001
|
+
sequence,
|
|
1002
|
+
timestamp,
|
|
1003
|
+
level,
|
|
1004
|
+
category,
|
|
1005
|
+
event,
|
|
1006
|
+
message,
|
|
1007
|
+
metadata_json
|
|
1008
|
+
) VALUES (
|
|
1009
|
+
$id,
|
|
1010
|
+
$pipelineId,
|
|
1011
|
+
$runId,
|
|
1012
|
+
$sequence,
|
|
1013
|
+
$timestamp,
|
|
1014
|
+
$level,
|
|
1015
|
+
$category,
|
|
1016
|
+
$event,
|
|
1017
|
+
$message,
|
|
1018
|
+
$metadata
|
|
1019
|
+
)`
|
|
1020
|
+
).run({
|
|
1021
|
+
$id: entry.id,
|
|
1022
|
+
$pipelineId: entry.pipelineId,
|
|
1023
|
+
$runId: entry.runId ?? null,
|
|
1024
|
+
$sequence: entry.sequence,
|
|
1025
|
+
$timestamp: entry.timestamp,
|
|
1026
|
+
$level: entry.level,
|
|
1027
|
+
$category: entry.category,
|
|
1028
|
+
$event: entry.event,
|
|
1029
|
+
$message: entry.message,
|
|
1030
|
+
$metadata: entry.metadata === void 0 ? null : JSON.stringify(entry.metadata)
|
|
1031
|
+
});
|
|
1032
|
+
db.exec("COMMIT");
|
|
1033
|
+
return entry;
|
|
1034
|
+
} catch (error) {
|
|
1035
|
+
if (db.isTransaction) {
|
|
1036
|
+
db.exec("ROLLBACK");
|
|
1037
|
+
}
|
|
1038
|
+
throw error;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
listPipelineLogs(options) {
|
|
1042
|
+
const db = this.database();
|
|
1043
|
+
const afterClause = options.after === void 0 ? "" : "AND sequence > $after";
|
|
1044
|
+
const rows = db.prepare(
|
|
1045
|
+
`SELECT id, pipeline_id, run_id, sequence, timestamp, level, category, event, message,
|
|
1046
|
+
metadata_json
|
|
1047
|
+
FROM runner_pipeline_logs
|
|
1048
|
+
WHERE pipeline_id = $pipelineId
|
|
1049
|
+
${afterClause}
|
|
1050
|
+
ORDER BY sequence ASC
|
|
1051
|
+
LIMIT $limit`
|
|
1052
|
+
).all({
|
|
1053
|
+
$pipelineId: options.pipelineId,
|
|
1054
|
+
$after: options.after ?? null,
|
|
1055
|
+
$limit: options.limit
|
|
1056
|
+
});
|
|
1057
|
+
return rows.map(toPipelineLog);
|
|
1058
|
+
}
|
|
888
1059
|
deleteSession(id) {
|
|
889
1060
|
const db = this.database();
|
|
890
1061
|
try {
|
|
891
1062
|
db.exec("BEGIN IMMEDIATE");
|
|
892
1063
|
db.prepare("DELETE FROM runner_traces WHERE session_id = $id").run({ $id: id });
|
|
893
1064
|
db.prepare("DELETE FROM runner_session_runs WHERE session_id = $id").run({ $id: id });
|
|
1065
|
+
db.prepare("DELETE FROM runner_session_logs WHERE session_id = $id").run({ $id: id });
|
|
894
1066
|
const result = db.prepare("DELETE FROM runner_sessions WHERE id = $id").run({ $id: id });
|
|
895
1067
|
db.exec("COMMIT");
|
|
896
1068
|
return Number(result.changes) > 0;
|
|
@@ -1039,18 +1211,41 @@ var SqliteSessionStore = class {
|
|
|
1039
1211
|
db.exec(`
|
|
1040
1212
|
PRAGMA journal_mode = WAL;
|
|
1041
1213
|
PRAGMA foreign_keys = ON;
|
|
1214
|
+
`);
|
|
1215
|
+
guardAgainstLegacySessionSchema(db);
|
|
1216
|
+
db.exec(`
|
|
1042
1217
|
CREATE TABLE IF NOT EXISTS runner_sessions (
|
|
1043
1218
|
id TEXT PRIMARY KEY,
|
|
1044
1219
|
agent_id TEXT NOT NULL,
|
|
1045
1220
|
title TEXT,
|
|
1046
1221
|
metadata_json TEXT,
|
|
1047
|
-
messages_json TEXT NOT NULL,
|
|
1048
|
-
transcript_json TEXT NOT NULL,
|
|
1049
1222
|
created_at TEXT NOT NULL,
|
|
1050
1223
|
updated_at TEXT NOT NULL
|
|
1051
1224
|
) STRICT;
|
|
1052
1225
|
CREATE INDEX IF NOT EXISTS runner_sessions_agent_updated_idx
|
|
1053
1226
|
ON runner_sessions(agent_id, updated_at DESC);
|
|
1227
|
+
CREATE TABLE IF NOT EXISTS runner_session_messages (
|
|
1228
|
+
session_id TEXT NOT NULL,
|
|
1229
|
+
message_index INTEGER NOT NULL,
|
|
1230
|
+
role TEXT NOT NULL,
|
|
1231
|
+
message_id TEXT,
|
|
1232
|
+
created_at TEXT NOT NULL,
|
|
1233
|
+
PRIMARY KEY(session_id, message_index),
|
|
1234
|
+
FOREIGN KEY(session_id) REFERENCES runner_sessions(id) ON DELETE CASCADE
|
|
1235
|
+
) STRICT;
|
|
1236
|
+
CREATE INDEX IF NOT EXISTS runner_session_messages_session_idx
|
|
1237
|
+
ON runner_session_messages(session_id, message_index ASC);
|
|
1238
|
+
CREATE TABLE IF NOT EXISTS runner_session_message_parts (
|
|
1239
|
+
session_id TEXT NOT NULL,
|
|
1240
|
+
message_index INTEGER NOT NULL,
|
|
1241
|
+
part_index INTEGER NOT NULL,
|
|
1242
|
+
type TEXT NOT NULL,
|
|
1243
|
+
part_json TEXT NOT NULL,
|
|
1244
|
+
PRIMARY KEY(session_id, message_index, part_index),
|
|
1245
|
+
FOREIGN KEY(session_id, message_index)
|
|
1246
|
+
REFERENCES runner_session_messages(session_id, message_index)
|
|
1247
|
+
ON DELETE CASCADE
|
|
1248
|
+
) STRICT;
|
|
1054
1249
|
CREATE TABLE IF NOT EXISTS runner_session_runs (
|
|
1055
1250
|
run_id TEXT PRIMARY KEY,
|
|
1056
1251
|
session_id TEXT NOT NULL,
|
|
@@ -1064,6 +1259,37 @@ var SqliteSessionStore = class {
|
|
|
1064
1259
|
) STRICT;
|
|
1065
1260
|
CREATE INDEX IF NOT EXISTS runner_session_runs_session_created_idx
|
|
1066
1261
|
ON runner_session_runs(session_id, created_at ASC);
|
|
1262
|
+
CREATE TABLE IF NOT EXISTS runner_session_logs (
|
|
1263
|
+
id TEXT PRIMARY KEY,
|
|
1264
|
+
session_id TEXT NOT NULL,
|
|
1265
|
+
run_id TEXT,
|
|
1266
|
+
sequence INTEGER NOT NULL,
|
|
1267
|
+
timestamp TEXT NOT NULL,
|
|
1268
|
+
level TEXT NOT NULL,
|
|
1269
|
+
category TEXT NOT NULL,
|
|
1270
|
+
event TEXT NOT NULL,
|
|
1271
|
+
message TEXT NOT NULL,
|
|
1272
|
+
metadata_json TEXT,
|
|
1273
|
+
UNIQUE(session_id, sequence),
|
|
1274
|
+
FOREIGN KEY(session_id) REFERENCES runner_sessions(id) ON DELETE CASCADE
|
|
1275
|
+
) STRICT;
|
|
1276
|
+
CREATE INDEX IF NOT EXISTS runner_session_logs_session_sequence_idx
|
|
1277
|
+
ON runner_session_logs(session_id, sequence ASC);
|
|
1278
|
+
CREATE TABLE IF NOT EXISTS runner_pipeline_logs (
|
|
1279
|
+
id TEXT PRIMARY KEY,
|
|
1280
|
+
pipeline_id TEXT NOT NULL,
|
|
1281
|
+
run_id TEXT,
|
|
1282
|
+
sequence INTEGER NOT NULL,
|
|
1283
|
+
timestamp TEXT NOT NULL,
|
|
1284
|
+
level TEXT NOT NULL,
|
|
1285
|
+
category TEXT NOT NULL,
|
|
1286
|
+
event TEXT NOT NULL,
|
|
1287
|
+
message TEXT NOT NULL,
|
|
1288
|
+
metadata_json TEXT,
|
|
1289
|
+
UNIQUE(pipeline_id, sequence)
|
|
1290
|
+
) STRICT;
|
|
1291
|
+
CREATE INDEX IF NOT EXISTS runner_pipeline_logs_pipeline_sequence_idx
|
|
1292
|
+
ON runner_pipeline_logs(pipeline_id, sequence ASC);
|
|
1067
1293
|
CREATE TABLE IF NOT EXISTS runner_traces (
|
|
1068
1294
|
id TEXT PRIMARY KEY,
|
|
1069
1295
|
session_id TEXT NOT NULL,
|
|
@@ -1088,7 +1314,7 @@ var SqliteSessionStore = class {
|
|
|
1088
1314
|
}
|
|
1089
1315
|
getSessionRow(id) {
|
|
1090
1316
|
return this.database().prepare(
|
|
1091
|
-
`SELECT id, agent_id, title, metadata_json,
|
|
1317
|
+
`SELECT id, agent_id, title, metadata_json, created_at, updated_at
|
|
1092
1318
|
FROM runner_sessions
|
|
1093
1319
|
WHERE id = $id`
|
|
1094
1320
|
).get({ $id: id });
|
|
@@ -1108,21 +1334,110 @@ var SqliteSessionStore = class {
|
|
|
1108
1334
|
ORDER BY created_at ASC`
|
|
1109
1335
|
).all({ $sessionId: sessionId });
|
|
1110
1336
|
}
|
|
1337
|
+
nextSessionLogSequence(sessionId) {
|
|
1338
|
+
const row = this.database().prepare(
|
|
1339
|
+
`SELECT COALESCE(MAX(sequence) + 1, 0) AS next_sequence
|
|
1340
|
+
FROM runner_session_logs
|
|
1341
|
+
WHERE session_id = $sessionId`
|
|
1342
|
+
).get({ $sessionId: sessionId });
|
|
1343
|
+
return row.next_sequence;
|
|
1344
|
+
}
|
|
1345
|
+
nextPipelineLogSequence(pipelineId) {
|
|
1346
|
+
const row = this.database().prepare(
|
|
1347
|
+
`SELECT COALESCE(MAX(sequence) + 1, 0) AS next_sequence
|
|
1348
|
+
FROM runner_pipeline_logs
|
|
1349
|
+
WHERE pipeline_id = $pipelineId`
|
|
1350
|
+
).get({ $pipelineId: pipelineId });
|
|
1351
|
+
return row.next_sequence;
|
|
1352
|
+
}
|
|
1353
|
+
listSessionMessages(sessionId) {
|
|
1354
|
+
const db = this.database();
|
|
1355
|
+
const messageRows = db.prepare(
|
|
1356
|
+
`SELECT session_id, message_index, role, message_id, created_at
|
|
1357
|
+
FROM runner_session_messages
|
|
1358
|
+
WHERE session_id = $sessionId
|
|
1359
|
+
ORDER BY message_index ASC`
|
|
1360
|
+
).all({ $sessionId: sessionId });
|
|
1361
|
+
if (messageRows.length === 0) {
|
|
1362
|
+
return [];
|
|
1363
|
+
}
|
|
1364
|
+
const partRows = db.prepare(
|
|
1365
|
+
`SELECT session_id, message_index, part_index, type, part_json
|
|
1366
|
+
FROM runner_session_message_parts
|
|
1367
|
+
WHERE session_id = $sessionId
|
|
1368
|
+
ORDER BY message_index ASC, part_index ASC`
|
|
1369
|
+
).all({ $sessionId: sessionId });
|
|
1370
|
+
const partsByMessage = /* @__PURE__ */ new Map();
|
|
1371
|
+
for (const partRow of partRows) {
|
|
1372
|
+
const parts = partsByMessage.get(partRow.message_index) ?? [];
|
|
1373
|
+
parts.push(partRow);
|
|
1374
|
+
partsByMessage.set(partRow.message_index, parts);
|
|
1375
|
+
}
|
|
1376
|
+
return messageRows.map(
|
|
1377
|
+
(row) => messageFromRows(row, partsByMessage.get(row.message_index) ?? [])
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1380
|
+
nextMessageIndex(sessionId) {
|
|
1381
|
+
const row = this.database().prepare(
|
|
1382
|
+
`SELECT COALESCE(MAX(message_index) + 1, 0) AS next_index
|
|
1383
|
+
FROM runner_session_messages
|
|
1384
|
+
WHERE session_id = $sessionId`
|
|
1385
|
+
).get({ $sessionId: sessionId });
|
|
1386
|
+
return row.next_index;
|
|
1387
|
+
}
|
|
1388
|
+
insertMessages(sessionId, messages, startIndex, createdAt) {
|
|
1389
|
+
const db = this.database();
|
|
1390
|
+
const insertMessage = db.prepare(
|
|
1391
|
+
`INSERT INTO runner_session_messages (
|
|
1392
|
+
session_id,
|
|
1393
|
+
message_index,
|
|
1394
|
+
role,
|
|
1395
|
+
message_id,
|
|
1396
|
+
created_at
|
|
1397
|
+
) VALUES ($sessionId, $messageIndex, $role, $messageId, $createdAt)`
|
|
1398
|
+
);
|
|
1399
|
+
const insertPart = db.prepare(
|
|
1400
|
+
`INSERT INTO runner_session_message_parts (
|
|
1401
|
+
session_id,
|
|
1402
|
+
message_index,
|
|
1403
|
+
part_index,
|
|
1404
|
+
type,
|
|
1405
|
+
part_json
|
|
1406
|
+
) VALUES ($sessionId, $messageIndex, $partIndex, $type, $partJson)`
|
|
1407
|
+
);
|
|
1408
|
+
messages.forEach((message, messageOffset) => {
|
|
1409
|
+
const messageIndex = startIndex + messageOffset;
|
|
1410
|
+
insertMessage.run({
|
|
1411
|
+
$sessionId: sessionId,
|
|
1412
|
+
$messageIndex: messageIndex,
|
|
1413
|
+
$role: message.role,
|
|
1414
|
+
$messageId: message.role === "assistant" ? message.id ?? null : null,
|
|
1415
|
+
$createdAt: createdAt
|
|
1416
|
+
});
|
|
1417
|
+
messageParts(message).forEach((part, partIndex) => {
|
|
1418
|
+
insertPart.run({
|
|
1419
|
+
$sessionId: sessionId,
|
|
1420
|
+
$messageIndex: messageIndex,
|
|
1421
|
+
$partIndex: partIndex,
|
|
1422
|
+
$type: part.type,
|
|
1423
|
+
$partJson: JSON.stringify(part.value)
|
|
1424
|
+
});
|
|
1425
|
+
});
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1111
1428
|
};
|
|
1112
|
-
function toSession(row, runRows = []) {
|
|
1113
|
-
const summary = toSessionSummary(row);
|
|
1114
|
-
const legacyTranscript = parseJsonArray(row.transcript_json);
|
|
1429
|
+
function toSession(row, messages, runRows = []) {
|
|
1430
|
+
const summary = toSessionSummary({ ...row, message_count: messages.length });
|
|
1115
1431
|
const runTranscript = runRows.flatMap(
|
|
1116
1432
|
(runRow) => parseJsonArray(runRow.transcript_json)
|
|
1117
1433
|
);
|
|
1118
1434
|
return {
|
|
1119
1435
|
...summary,
|
|
1120
|
-
messages
|
|
1121
|
-
transcript: renumberTranscript(
|
|
1436
|
+
messages,
|
|
1437
|
+
transcript: renumberTranscript(runTranscript)
|
|
1122
1438
|
};
|
|
1123
1439
|
}
|
|
1124
1440
|
function toSessionSummary(row) {
|
|
1125
|
-
const messages = parseJsonArray(row.messages_json);
|
|
1126
1441
|
const metadata = parseJsonValue(row.metadata_json);
|
|
1127
1442
|
return {
|
|
1128
1443
|
id: row.id,
|
|
@@ -1130,10 +1445,90 @@ function toSessionSummary(row) {
|
|
|
1130
1445
|
...row.title === null ? {} : { title: row.title },
|
|
1131
1446
|
createdAt: row.created_at,
|
|
1132
1447
|
updatedAt: row.updated_at,
|
|
1133
|
-
messageCount:
|
|
1448
|
+
messageCount: row.message_count,
|
|
1449
|
+
...metadata === void 0 ? {} : { metadata }
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
function toSessionLog(row) {
|
|
1453
|
+
const metadata = parseJsonValue(row.metadata_json);
|
|
1454
|
+
return {
|
|
1455
|
+
id: row.id,
|
|
1456
|
+
sessionId: row.session_id,
|
|
1457
|
+
...row.run_id === null ? {} : { runId: row.run_id },
|
|
1458
|
+
sequence: row.sequence,
|
|
1459
|
+
timestamp: row.timestamp,
|
|
1460
|
+
level: row.level,
|
|
1461
|
+
category: row.category,
|
|
1462
|
+
event: row.event,
|
|
1463
|
+
message: row.message,
|
|
1464
|
+
...metadata === void 0 ? {} : { metadata }
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
function toPipelineLog(row) {
|
|
1468
|
+
const metadata = parseJsonValue(row.metadata_json);
|
|
1469
|
+
return {
|
|
1470
|
+
id: row.id,
|
|
1471
|
+
pipelineId: row.pipeline_id,
|
|
1472
|
+
...row.run_id === null ? {} : { runId: row.run_id },
|
|
1473
|
+
sequence: row.sequence,
|
|
1474
|
+
timestamp: row.timestamp,
|
|
1475
|
+
level: row.level,
|
|
1476
|
+
category: row.category,
|
|
1477
|
+
event: row.event,
|
|
1478
|
+
message: row.message,
|
|
1134
1479
|
...metadata === void 0 ? {} : { metadata }
|
|
1135
1480
|
};
|
|
1136
1481
|
}
|
|
1482
|
+
function messageParts(message) {
|
|
1483
|
+
if (message.role === "system") {
|
|
1484
|
+
return [{ type: "text", value: { type: "text", text: message.content } }];
|
|
1485
|
+
}
|
|
1486
|
+
return message.content.map((content) => ({
|
|
1487
|
+
type: content.type,
|
|
1488
|
+
value: content
|
|
1489
|
+
}));
|
|
1490
|
+
}
|
|
1491
|
+
function messageFromRows(row, partRows) {
|
|
1492
|
+
const parts = partRows.map((partRow) => JSON.parse(partRow.part_json));
|
|
1493
|
+
if (row.role === "system") {
|
|
1494
|
+
return { role: "system", content: systemContentFromParts(parts) };
|
|
1495
|
+
}
|
|
1496
|
+
if (row.role === "user") {
|
|
1497
|
+
return {
|
|
1498
|
+
role: "user",
|
|
1499
|
+
content: parts
|
|
1500
|
+
};
|
|
1501
|
+
}
|
|
1502
|
+
if (row.role === "assistant") {
|
|
1503
|
+
return {
|
|
1504
|
+
role: "assistant",
|
|
1505
|
+
...row.message_id === null ? {} : { id: row.message_id },
|
|
1506
|
+
content: parts
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
if (row.role === "tool") {
|
|
1510
|
+
return {
|
|
1511
|
+
role: "tool",
|
|
1512
|
+
content: parts
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
throw new Error(`Unsupported stored message role: ${row.role}`);
|
|
1516
|
+
}
|
|
1517
|
+
function systemContentFromParts(parts) {
|
|
1518
|
+
const first = parts[0];
|
|
1519
|
+
if (typeof first === "object" && first !== null && "type" in first && first.type === "text" && "text" in first && typeof first.text === "string") {
|
|
1520
|
+
return first.text;
|
|
1521
|
+
}
|
|
1522
|
+
return "";
|
|
1523
|
+
}
|
|
1524
|
+
function guardAgainstLegacySessionSchema(db) {
|
|
1525
|
+
const columns = db.prepare("PRAGMA table_info('runner_sessions')").all();
|
|
1526
|
+
if (columns.some((column) => column.name === "messages_json")) {
|
|
1527
|
+
throw new Error(
|
|
1528
|
+
"Existing Studio SQLite DB uses the legacy messages_json schema. Delete or recreate the Studio SQLite DB to use normalized session messages."
|
|
1529
|
+
);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1137
1532
|
function toTrace(row) {
|
|
1138
1533
|
const trace = parseJsonValue(row.trace_json);
|
|
1139
1534
|
const input = parseJsonValue(row.input_json);
|
|
@@ -1268,15 +1663,56 @@ function formatJson(value) {
|
|
|
1268
1663
|
}
|
|
1269
1664
|
}
|
|
1270
1665
|
|
|
1666
|
+
// src/runtime/tool-metadata.ts
|
|
1667
|
+
import { ToolSet } from "@anvia/core";
|
|
1668
|
+
var MCP_TOOL_METADATA_KEY = /* @__PURE__ */ Symbol.for("anvia.mcp.tool.metadata");
|
|
1669
|
+
function agentToolItems(agent) {
|
|
1670
|
+
return [
|
|
1671
|
+
...agent.agent.toolSet.values().map((tool) => ({ tool, source: "static" })),
|
|
1672
|
+
...agent.agent.dynamicTools.flatMap((registration) => {
|
|
1673
|
+
const maybeToolSet = registration.index.toolSet;
|
|
1674
|
+
if (!(maybeToolSet instanceof ToolSet)) {
|
|
1675
|
+
return [];
|
|
1676
|
+
}
|
|
1677
|
+
return maybeToolSet.values().map((tool) => ({ tool, source: "dynamic" }));
|
|
1678
|
+
})
|
|
1679
|
+
];
|
|
1680
|
+
}
|
|
1681
|
+
function approvalMetadata(tool) {
|
|
1682
|
+
const approval = tool.approval;
|
|
1683
|
+
if (approval === void 0 || typeof approval !== "object" || approval === null) {
|
|
1684
|
+
return { required: false };
|
|
1685
|
+
}
|
|
1686
|
+
const policy = approval;
|
|
1687
|
+
return {
|
|
1688
|
+
required: true,
|
|
1689
|
+
...typeof policy.reason === "string" ? { reason: policy.reason } : {},
|
|
1690
|
+
...typeof policy.rejectMessage === "string" ? { rejectMessage: policy.rejectMessage } : {}
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
function mcpServerName(tool) {
|
|
1694
|
+
const metadata = tool[MCP_TOOL_METADATA_KEY];
|
|
1695
|
+
if (typeof metadata !== "object" || metadata === null) {
|
|
1696
|
+
return void 0;
|
|
1697
|
+
}
|
|
1698
|
+
const serverName = metadata.serverName;
|
|
1699
|
+
return typeof serverName === "string" && serverName.length > 0 ? serverName : void 0;
|
|
1700
|
+
}
|
|
1701
|
+
function agentHasMcpTools(agent) {
|
|
1702
|
+
return agentToolItems(agent).some(({ tool }) => mcpServerName(tool) !== void 0);
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1271
1705
|
// src/runtime/shared.ts
|
|
1272
1706
|
function resolveStores(options) {
|
|
1273
1707
|
const defaultPath = process.env.ANVIA_STUDIO_DB ?? process.env.AION_STUDIO_DB ?? join(process.cwd(), ".anvia-studio", `${safeFileName(runnerId(options))}.sqlite`);
|
|
1274
1708
|
const defaultStore = createSqliteSessionStore({ path: defaultPath });
|
|
1275
1709
|
const sessions = resolveSessionStore(options, defaultStore);
|
|
1276
1710
|
const traces = resolveTraceStore(options, sessions, defaultStore);
|
|
1711
|
+
const pipelineLogs = resolvePipelineLogStore(options, sessions, defaultStore);
|
|
1277
1712
|
return {
|
|
1278
1713
|
...sessions === void 0 ? {} : { sessions },
|
|
1279
|
-
...traces === void 0 ? {} : { traces }
|
|
1714
|
+
...traces === void 0 ? {} : { traces },
|
|
1715
|
+
...pipelineLogs === void 0 ? {} : { pipelineLogs }
|
|
1280
1716
|
};
|
|
1281
1717
|
}
|
|
1282
1718
|
function resolveSessionStore(options, defaultStore) {
|
|
@@ -1300,10 +1736,26 @@ function resolveTraceStore(options, sessionStore, defaultStore) {
|
|
|
1300
1736
|
}
|
|
1301
1737
|
return defaultStore;
|
|
1302
1738
|
}
|
|
1739
|
+
function resolvePipelineLogStore(options, sessionStore, defaultStore) {
|
|
1740
|
+
if (options.stores?.pipelineLogs === false) {
|
|
1741
|
+
return void 0;
|
|
1742
|
+
}
|
|
1743
|
+
if (options.stores?.pipelineLogs !== void 0) {
|
|
1744
|
+
return options.stores.pipelineLogs;
|
|
1745
|
+
}
|
|
1746
|
+
if (sessionStore !== void 0 && isPipelineLogStore(sessionStore)) {
|
|
1747
|
+
return sessionStore;
|
|
1748
|
+
}
|
|
1749
|
+
return defaultStore;
|
|
1750
|
+
}
|
|
1303
1751
|
function isTraceStore(store) {
|
|
1304
1752
|
const candidate = store;
|
|
1305
1753
|
return typeof candidate.listSessionTraces === "function" && typeof candidate.getTrace === "function" && typeof candidate.saveTrace === "function";
|
|
1306
1754
|
}
|
|
1755
|
+
function isPipelineLogStore(store) {
|
|
1756
|
+
const candidate = store;
|
|
1757
|
+
return typeof candidate.appendPipelineLog === "function" && typeof candidate.listPipelineLogs === "function";
|
|
1758
|
+
}
|
|
1307
1759
|
function unsupportedCapabilities(stores) {
|
|
1308
1760
|
return [
|
|
1309
1761
|
...stores.sessions === void 0 ? ["sessions"] : [],
|
|
@@ -1324,17 +1776,32 @@ function normalizeAgents(agents) {
|
|
|
1324
1776
|
return { ...agent, id };
|
|
1325
1777
|
});
|
|
1326
1778
|
}
|
|
1327
|
-
function
|
|
1779
|
+
function normalizePipelines(pipelines) {
|
|
1780
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1781
|
+
return pipelines.map((pipeline) => {
|
|
1782
|
+
const id = pipeline.id.trim();
|
|
1783
|
+
if (id.length === 0) {
|
|
1784
|
+
throw new Error("Studio pipeline id cannot be empty");
|
|
1785
|
+
}
|
|
1786
|
+
if (ids.has(id)) {
|
|
1787
|
+
throw new Error(`Duplicate Studio pipeline id: ${id}`);
|
|
1788
|
+
}
|
|
1789
|
+
ids.add(id);
|
|
1790
|
+
return { ...pipeline, id };
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
1793
|
+
function buildConfig(options, agents, pipelines, stores) {
|
|
1328
1794
|
return {
|
|
1329
1795
|
id: runnerId(options),
|
|
1330
1796
|
...options.name === void 0 ? {} : { name: options.name },
|
|
1331
1797
|
...options.description === void 0 ? {} : { description: options.description },
|
|
1332
1798
|
...options.version === void 0 ? {} : { version: options.version },
|
|
1333
1799
|
agents: agents.map(agentConfig),
|
|
1800
|
+
pipelines: pipelines.map(pipelineConfig),
|
|
1334
1801
|
chat: {
|
|
1335
1802
|
quickPrompts: Object.fromEntries(agents.map((agent) => [agent.id, agent.quickPrompts ?? []]))
|
|
1336
1803
|
},
|
|
1337
|
-
capabilities: capabilityConfig(options, agents, stores),
|
|
1804
|
+
capabilities: capabilityConfig(options, agents, pipelines, stores),
|
|
1338
1805
|
unsupportedCapabilities: unsupportedCapabilities(stores)
|
|
1339
1806
|
};
|
|
1340
1807
|
}
|
|
@@ -1352,7 +1819,22 @@ function agentConfig(agent) {
|
|
|
1352
1819
|
...agent.metadata === void 0 ? {} : { metadata: agent.metadata }
|
|
1353
1820
|
};
|
|
1354
1821
|
}
|
|
1355
|
-
function
|
|
1822
|
+
function pipelineConfig(pipeline) {
|
|
1823
|
+
const graph = pipeline.pipeline.graph();
|
|
1824
|
+
const stageNodes = graph.nodes.filter((node) => node.kind !== "input" && node.kind !== "output");
|
|
1825
|
+
return {
|
|
1826
|
+
id: pipeline.id,
|
|
1827
|
+
...pipeline.name === void 0 ? {} : { name: pipeline.name },
|
|
1828
|
+
...pipeline.description === void 0 ? {} : { description: pipeline.description },
|
|
1829
|
+
...pipeline.metadata === void 0 ? {} : { metadata: pipeline.metadata },
|
|
1830
|
+
stageCount: stageNodes.length,
|
|
1831
|
+
edgeCount: graph.edges.length,
|
|
1832
|
+
hasParallelStages: graph.nodes.some((node) => node.kind === "parallel"),
|
|
1833
|
+
agentCount: graph.nodes.filter((node) => node.kind === "agent").length,
|
|
1834
|
+
extractorCount: graph.nodes.filter((node) => node.kind === "extractor").length
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
function capabilityConfig(_options, agents, pipelines, stores) {
|
|
1356
1838
|
const capabilities = {
|
|
1357
1839
|
agents: { enabled: true }
|
|
1358
1840
|
};
|
|
@@ -1362,10 +1844,23 @@ function capabilityConfig(_options, agents, stores) {
|
|
|
1362
1844
|
if (stores.traces !== void 0) {
|
|
1363
1845
|
capabilities.traces = { enabled: true };
|
|
1364
1846
|
}
|
|
1847
|
+
if (pipelines.length > 0) {
|
|
1848
|
+
capabilities.pipelines = { enabled: true };
|
|
1849
|
+
}
|
|
1850
|
+
if (agents.some(
|
|
1851
|
+
(agent) => agent.agent.toolSet.values().length > 0 || agent.agent.dynamicTools.length > 0
|
|
1852
|
+
)) {
|
|
1853
|
+
capabilities.tools = { enabled: true };
|
|
1854
|
+
}
|
|
1855
|
+
if (agents.some(agentHasMcpTools)) {
|
|
1856
|
+
capabilities.mcps = { enabled: true };
|
|
1857
|
+
}
|
|
1365
1858
|
if (agents.some((agent) => agent.agent.observers.length > 0)) {
|
|
1366
1859
|
capabilities.observability = { enabled: true };
|
|
1367
1860
|
}
|
|
1368
|
-
if (agents.some(
|
|
1861
|
+
if (agents.some(
|
|
1862
|
+
(agent) => agent.agent.hook !== void 0 || agent.agent.toolSet.values().some((tool) => tool.approval)
|
|
1863
|
+
)) {
|
|
1369
1864
|
capabilities.approvals = { enabled: true };
|
|
1370
1865
|
}
|
|
1371
1866
|
if (agents.some(
|
|
@@ -1560,45 +2055,62 @@ function createApprovalRuntime() {
|
|
|
1560
2055
|
return {
|
|
1561
2056
|
approvals,
|
|
1562
2057
|
createHook(context) {
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
runId: context.runId,
|
|
1581
|
-
...context.sessionId === void 0 ? {} : { sessionId: context.sessionId },
|
|
1582
|
-
...context.metadata === void 0 ? {} : { metadata: context.metadata }
|
|
2058
|
+
const handleApprovalRequest = async ({ toolName, toolCallId, internalCallId, args, tool: control }, request) => {
|
|
2059
|
+
const decision = await requestApproval(approvals, context, {
|
|
2060
|
+
toolName,
|
|
2061
|
+
...toolCallId === void 0 ? {} : { toolCallId },
|
|
2062
|
+
internalCallId,
|
|
2063
|
+
args,
|
|
2064
|
+
...request.reason === void 0 ? {} : { reason: request.reason },
|
|
2065
|
+
...request.rejectMessage === void 0 ? {} : { rejectMessage: request.rejectMessage }
|
|
2066
|
+
});
|
|
2067
|
+
return decision.approved ? control.run() : control.skip(decision.reason ?? request.rejectMessage ?? "Rejected in Anvia Studio.");
|
|
2068
|
+
};
|
|
2069
|
+
return {
|
|
2070
|
+
...createHook({
|
|
2071
|
+
async onToolCall({ toolName, toolCallId, internalCallId, args, tool: control }) {
|
|
2072
|
+
const registeredTool = context.getTool(toolName);
|
|
2073
|
+
if (registeredTool?.approval === void 0) {
|
|
2074
|
+
return control.run();
|
|
1583
2075
|
}
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
2076
|
+
const approval = registeredTool.approval;
|
|
2077
|
+
const rawParsedArgs = parseToolArgs(args);
|
|
2078
|
+
const parsedArgs = registeredTool.parseApprovalArgs?.(rawParsedArgs) ?? rawParsedArgs;
|
|
2079
|
+
const approvalContext = {
|
|
2080
|
+
toolName,
|
|
2081
|
+
args: parsedArgs,
|
|
2082
|
+
rawArgs: args,
|
|
2083
|
+
...toolCallId === void 0 ? {} : { toolCallId },
|
|
2084
|
+
internalCallId,
|
|
2085
|
+
run: {
|
|
2086
|
+
agentId: context.agentId,
|
|
2087
|
+
runId: context.runId,
|
|
2088
|
+
...context.sessionId === void 0 ? {} : { sessionId: context.sessionId },
|
|
2089
|
+
...context.metadata === void 0 ? {} : { metadata: context.metadata }
|
|
2090
|
+
}
|
|
2091
|
+
};
|
|
2092
|
+
const required = await approval.when(approvalContext);
|
|
2093
|
+
if (!required) {
|
|
2094
|
+
return control.run();
|
|
2095
|
+
}
|
|
2096
|
+
const reason = await resolveApprovalText(approval.reason, approvalContext);
|
|
2097
|
+
const rejectMessage = await resolveApprovalText(
|
|
2098
|
+
approval.rejectMessage,
|
|
2099
|
+
approvalContext
|
|
2100
|
+
);
|
|
2101
|
+
const decision = await requestApproval(approvals, context, {
|
|
2102
|
+
toolName,
|
|
2103
|
+
...toolCallId === void 0 ? {} : { toolCallId },
|
|
2104
|
+
internalCallId,
|
|
2105
|
+
args,
|
|
2106
|
+
...reason === void 0 ? {} : { reason },
|
|
2107
|
+
...rejectMessage === void 0 ? {} : { rejectMessage }
|
|
2108
|
+
});
|
|
2109
|
+
return decision.approved ? control.run() : control.skip(decision.reason ?? rejectMessage ?? "Rejected in Anvia Studio.");
|
|
1588
2110
|
}
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
toolName,
|
|
1593
|
-
...toolCallId === void 0 ? {} : { toolCallId },
|
|
1594
|
-
internalCallId,
|
|
1595
|
-
args,
|
|
1596
|
-
...reason === void 0 ? {} : { reason },
|
|
1597
|
-
...rejectMessage === void 0 ? {} : { rejectMessage }
|
|
1598
|
-
});
|
|
1599
|
-
return decision.approved ? control.run() : control.skip(decision.reason ?? rejectMessage ?? "Rejected in Anvia Studio.");
|
|
1600
|
-
}
|
|
1601
|
-
});
|
|
2111
|
+
}),
|
|
2112
|
+
handleApprovalRequest
|
|
2113
|
+
};
|
|
1602
2114
|
},
|
|
1603
2115
|
list(options) {
|
|
1604
2116
|
return [...approvals.values()].filter((approval) => {
|
|
@@ -1894,243 +2406,207 @@ function isRecord2(value) {
|
|
|
1894
2406
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1895
2407
|
}
|
|
1896
2408
|
|
|
1897
|
-
// src/runtime/
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
const
|
|
1902
|
-
if (
|
|
1903
|
-
return errorResponse(c,
|
|
1904
|
-
}
|
|
1905
|
-
const options = {};
|
|
1906
|
-
const runId = optionalQueryString(c.req.query("runId"));
|
|
1907
|
-
const agentId = optionalQueryString(c.req.query("agentId"));
|
|
1908
|
-
const sessionId = optionalQueryString(c.req.query("sessionId"));
|
|
1909
|
-
if (status !== void 0) {
|
|
1910
|
-
options.status = status;
|
|
1911
|
-
}
|
|
1912
|
-
if (runId !== void 0) {
|
|
1913
|
-
options.runId = runId;
|
|
1914
|
-
}
|
|
1915
|
-
if (agentId !== void 0) {
|
|
1916
|
-
options.agentId = agentId;
|
|
1917
|
-
}
|
|
1918
|
-
if (sessionId !== void 0) {
|
|
1919
|
-
options.sessionId = sessionId;
|
|
2409
|
+
// src/runtime/mcps.ts
|
|
2410
|
+
function registerMcpRoutes(app, props) {
|
|
2411
|
+
app.get("/agents/:agentId/mcps", async (c) => {
|
|
2412
|
+
const agentId = c.req.param("agentId");
|
|
2413
|
+
const agent = props.agentMap.get(agentId);
|
|
2414
|
+
if (agent === void 0) {
|
|
2415
|
+
return errorResponse(c, 404, "not_found", "Agent not found");
|
|
1920
2416
|
}
|
|
1921
|
-
return c.json({
|
|
2417
|
+
return c.json({
|
|
2418
|
+
agentId,
|
|
2419
|
+
servers: await agentMcpMetadata(agent)
|
|
2420
|
+
});
|
|
1922
2421
|
});
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
const
|
|
1929
|
-
if (
|
|
1930
|
-
|
|
2422
|
+
}
|
|
2423
|
+
async function agentMcpMetadata(agent) {
|
|
2424
|
+
const servers = /* @__PURE__ */ new Map();
|
|
2425
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2426
|
+
for (const { tool, source } of agentToolItems(agent)) {
|
|
2427
|
+
const serverName = mcpServerName(tool);
|
|
2428
|
+
if (serverName === void 0) {
|
|
2429
|
+
continue;
|
|
1931
2430
|
}
|
|
1932
|
-
|
|
1933
|
-
|
|
2431
|
+
const definition = await tool.definition("");
|
|
2432
|
+
const key = `${serverName}:${source}:${definition.name}`;
|
|
2433
|
+
if (seen.has(key)) {
|
|
2434
|
+
continue;
|
|
1934
2435
|
}
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
2436
|
+
seen.add(key);
|
|
2437
|
+
const tools = servers.get(serverName) ?? [];
|
|
2438
|
+
tools.push({
|
|
2439
|
+
name: definition.name,
|
|
2440
|
+
description: definition.description,
|
|
2441
|
+
parameters: definition.parameters,
|
|
2442
|
+
source
|
|
2443
|
+
});
|
|
2444
|
+
servers.set(serverName, tools);
|
|
1942
2445
|
}
|
|
1943
|
-
return
|
|
2446
|
+
return [...servers.entries()].map(([name, tools]) => {
|
|
2447
|
+
const sortedTools = tools.sort((left, right) => {
|
|
2448
|
+
if (left.source !== right.source) {
|
|
2449
|
+
return left.source === "static" ? -1 : 1;
|
|
2450
|
+
}
|
|
2451
|
+
return left.name.localeCompare(right.name);
|
|
2452
|
+
});
|
|
2453
|
+
return {
|
|
2454
|
+
agentId: agent.id,
|
|
2455
|
+
name,
|
|
2456
|
+
toolCount: sortedTools.length,
|
|
2457
|
+
tools: sortedTools
|
|
2458
|
+
};
|
|
2459
|
+
}).sort((left, right) => left.name.localeCompare(right.name));
|
|
1944
2460
|
}
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
if (
|
|
1956
|
-
|
|
1957
|
-
}
|
|
1958
|
-
const answers = [];
|
|
1959
|
-
for (const answer of body.answers) {
|
|
1960
|
-
if (!isObject(answer)) {
|
|
1961
|
-
return { error: errorResponse(c, 400, "bad_request", "answers must contain objects") };
|
|
1962
|
-
}
|
|
1963
|
-
if (typeof answer.questionId !== "string" || answer.questionId.trim().length === 0) {
|
|
1964
|
-
return { error: errorResponse(c, 400, "bad_request", "questionId must be a string") };
|
|
1965
|
-
}
|
|
1966
|
-
if (typeof answer.answer !== "string" || answer.answer.trim().length === 0) {
|
|
1967
|
-
return { error: errorResponse(c, 400, "bad_request", "answer must be a string") };
|
|
1968
|
-
}
|
|
1969
|
-
if ("choice" in answer && typeof answer.choice !== "string") {
|
|
1970
|
-
return { error: errorResponse(c, 400, "bad_request", "choice must be a string") };
|
|
1971
|
-
}
|
|
1972
|
-
if ("custom" in answer && typeof answer.custom !== "boolean") {
|
|
1973
|
-
return { error: errorResponse(c, 400, "bad_request", "custom must be a boolean") };
|
|
1974
|
-
}
|
|
1975
|
-
answers.push({
|
|
1976
|
-
questionId: answer.questionId.trim(),
|
|
1977
|
-
answer: answer.answer.trim(),
|
|
1978
|
-
...typeof answer.choice === "string" ? { choice: answer.choice } : {},
|
|
1979
|
-
...typeof answer.custom === "boolean" ? { custom: answer.custom } : {}
|
|
1980
|
-
});
|
|
2461
|
+
|
|
2462
|
+
// src/runtime/pipelines.ts
|
|
2463
|
+
import { stream as streamResponse2 } from "hono/streaming";
|
|
2464
|
+
|
|
2465
|
+
// src/runtime/pipeline-logs.ts
|
|
2466
|
+
async function appendPipelineLog(store, input) {
|
|
2467
|
+
return store?.appendPipelineLog(input);
|
|
2468
|
+
}
|
|
2469
|
+
async function* emitPipelineLog(store, input) {
|
|
2470
|
+
const log = await appendPipelineLog(store, input);
|
|
2471
|
+
if (log !== void 0) {
|
|
2472
|
+
yield { type: "pipeline_log", log };
|
|
1981
2473
|
}
|
|
1982
|
-
return { answers };
|
|
1983
2474
|
}
|
|
1984
|
-
function
|
|
1985
|
-
const questions = /* @__PURE__ */ new Map();
|
|
2475
|
+
function pipelineRunReceivedLog(props) {
|
|
1986
2476
|
return {
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
const answers = await requestQuestion(questions, context, {
|
|
1999
|
-
toolName,
|
|
2000
|
-
...toolCallId === void 0 ? {} : { toolCallId },
|
|
2001
|
-
internalCallId,
|
|
2002
|
-
args,
|
|
2003
|
-
questions: prompts.questions
|
|
2004
|
-
});
|
|
2005
|
-
return control.skip(JSON.stringify({ answers }));
|
|
2006
|
-
}
|
|
2007
|
-
});
|
|
2008
|
-
},
|
|
2009
|
-
list(options) {
|
|
2010
|
-
return [...questions.values()].filter((question) => {
|
|
2011
|
-
if (options.status === "pending" && question.status !== "pending") {
|
|
2012
|
-
return false;
|
|
2013
|
-
}
|
|
2014
|
-
if (options.status === "resolved" && question.status === "pending") {
|
|
2015
|
-
return false;
|
|
2016
|
-
}
|
|
2017
|
-
if (options.runId !== void 0 && question.runId !== options.runId) {
|
|
2018
|
-
return false;
|
|
2019
|
-
}
|
|
2020
|
-
if (options.agentId !== void 0 && question.agentId !== options.agentId) {
|
|
2021
|
-
return false;
|
|
2022
|
-
}
|
|
2023
|
-
if (options.sessionId !== void 0 && question.sessionId !== options.sessionId) {
|
|
2024
|
-
return false;
|
|
2025
|
-
}
|
|
2026
|
-
return true;
|
|
2027
|
-
}).map(publicQuestion);
|
|
2028
|
-
},
|
|
2029
|
-
answer(id, answers) {
|
|
2030
|
-
const question = questions.get(id);
|
|
2031
|
-
if (question === void 0) {
|
|
2032
|
-
return "missing";
|
|
2033
|
-
}
|
|
2034
|
-
if (!isPendingQuestion(question)) {
|
|
2035
|
-
return "resolved";
|
|
2036
|
-
}
|
|
2037
|
-
const resolved = resolveQuestion(question, answers);
|
|
2038
|
-
questions.set(id, resolved);
|
|
2039
|
-
question.emit?.({ type: "tool_question_result", question: resolved });
|
|
2040
|
-
question.resolve(answers);
|
|
2041
|
-
return publicQuestion(resolved);
|
|
2042
|
-
}
|
|
2477
|
+
pipelineId: props.pipeline.id,
|
|
2478
|
+
runId: props.runId,
|
|
2479
|
+
level: "info",
|
|
2480
|
+
category: "api",
|
|
2481
|
+
event: "pipeline.run_received",
|
|
2482
|
+
message: "Pipeline run request received",
|
|
2483
|
+
metadata: cleanMetadata({
|
|
2484
|
+
stream: props.stream,
|
|
2485
|
+
inputBytes: byteLength(formatUnknown(props.input)),
|
|
2486
|
+
metadataKeys: Object.keys(props.metadata ?? {})
|
|
2487
|
+
})
|
|
2043
2488
|
};
|
|
2044
2489
|
}
|
|
2045
|
-
|
|
2046
|
-
const
|
|
2047
|
-
|
|
2048
|
-
id,
|
|
2049
|
-
runId
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2059
|
-
...context.emit === void 0 ? {} : { emit: context.emit },
|
|
2060
|
-
resolve: () => {
|
|
2061
|
-
}
|
|
2490
|
+
function pipelineRunStartedLog(pipeline, runId) {
|
|
2491
|
+
const graph = pipeline.pipeline.graph();
|
|
2492
|
+
return {
|
|
2493
|
+
pipelineId: pipeline.id,
|
|
2494
|
+
runId,
|
|
2495
|
+
level: "info",
|
|
2496
|
+
category: "run",
|
|
2497
|
+
event: "pipeline.run_started",
|
|
2498
|
+
message: "Pipeline run started",
|
|
2499
|
+
metadata: cleanMetadata({
|
|
2500
|
+
stageCount: graph.nodes.filter((node) => node.kind !== "input" && node.kind !== "output").length,
|
|
2501
|
+
edgeCount: graph.edges.length
|
|
2502
|
+
})
|
|
2062
2503
|
};
|
|
2063
|
-
const answer = new Promise((resolve2) => {
|
|
2064
|
-
question.resolve = (answers) => {
|
|
2065
|
-
resolve2(answers);
|
|
2066
|
-
};
|
|
2067
|
-
});
|
|
2068
|
-
questions.set(id, question);
|
|
2069
|
-
context.emit?.({ type: "tool_question_request", question: publicQuestion(question) });
|
|
2070
|
-
return answer;
|
|
2071
2504
|
}
|
|
2072
|
-
function
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
error: "ask_question requires every question to include text and at least one choice."
|
|
2086
|
-
};
|
|
2087
|
-
}
|
|
2088
|
-
questions.push(normalized);
|
|
2089
|
-
}
|
|
2090
|
-
return { questions };
|
|
2505
|
+
function pipelineRunCompletedLog(props) {
|
|
2506
|
+
return {
|
|
2507
|
+
pipelineId: props.pipelineId,
|
|
2508
|
+
runId: props.runId,
|
|
2509
|
+
level: "info",
|
|
2510
|
+
category: "run",
|
|
2511
|
+
event: "pipeline.run_completed",
|
|
2512
|
+
message: "Pipeline run completed",
|
|
2513
|
+
metadata: cleanMetadata({
|
|
2514
|
+
durationMs: props.durationMs,
|
|
2515
|
+
outputBytes: byteLength(formatUnknown(props.output))
|
|
2516
|
+
})
|
|
2517
|
+
};
|
|
2091
2518
|
}
|
|
2092
|
-
function
|
|
2093
|
-
if (!isObject(value) || typeof value.question !== "string" || value.question.trim().length === 0) {
|
|
2094
|
-
return void 0;
|
|
2095
|
-
}
|
|
2096
|
-
const choices = Array.isArray(value.choices) ? value.choices.map(normalizeQuestionChoice).filter((choice) => choice !== void 0) : [];
|
|
2097
|
-
if (choices.length === 0) {
|
|
2098
|
-
return void 0;
|
|
2099
|
-
}
|
|
2519
|
+
function pipelineRunFailedLog(pipelineId, runId, error, startedAt) {
|
|
2100
2520
|
return {
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2521
|
+
pipelineId,
|
|
2522
|
+
runId,
|
|
2523
|
+
level: "error",
|
|
2524
|
+
category: "run",
|
|
2525
|
+
event: "pipeline.run_failed",
|
|
2526
|
+
message: "Pipeline run failed",
|
|
2527
|
+
metadata: cleanMetadata({
|
|
2528
|
+
durationMs: Date.now() - startedAt,
|
|
2529
|
+
error: serializeError2(error)
|
|
2530
|
+
})
|
|
2104
2531
|
};
|
|
2105
2532
|
}
|
|
2106
|
-
function
|
|
2107
|
-
|
|
2108
|
-
|
|
2533
|
+
function pipelineStageLog(pipelineId, runId, event) {
|
|
2534
|
+
const category = stageCategory(event.node);
|
|
2535
|
+
if (event.type === "stage_started") {
|
|
2536
|
+
return {
|
|
2537
|
+
pipelineId,
|
|
2538
|
+
runId,
|
|
2539
|
+
level: "debug",
|
|
2540
|
+
category,
|
|
2541
|
+
event: `${event.node.kind}.started`,
|
|
2542
|
+
message: `${event.node.label} started`,
|
|
2543
|
+
metadata: nodeMetadata(event.node)
|
|
2544
|
+
};
|
|
2109
2545
|
}
|
|
2110
|
-
if (
|
|
2111
|
-
return
|
|
2546
|
+
if (event.type === "stage_completed") {
|
|
2547
|
+
return {
|
|
2548
|
+
pipelineId,
|
|
2549
|
+
runId,
|
|
2550
|
+
level: "debug",
|
|
2551
|
+
category,
|
|
2552
|
+
event: `${event.node.kind}.completed`,
|
|
2553
|
+
message: `${event.node.label} completed`,
|
|
2554
|
+
metadata: cleanMetadata({
|
|
2555
|
+
...nodeMetadata(event.node),
|
|
2556
|
+
durationMs: event.durationMs
|
|
2557
|
+
})
|
|
2558
|
+
};
|
|
2112
2559
|
}
|
|
2113
2560
|
return {
|
|
2114
|
-
|
|
2115
|
-
|
|
2561
|
+
pipelineId,
|
|
2562
|
+
runId,
|
|
2563
|
+
level: "error",
|
|
2564
|
+
category,
|
|
2565
|
+
event: `${event.node.kind}.failed`,
|
|
2566
|
+
message: `${event.node.label} failed`,
|
|
2567
|
+
metadata: cleanMetadata({
|
|
2568
|
+
...nodeMetadata(event.node),
|
|
2569
|
+
durationMs: event.durationMs,
|
|
2570
|
+
error: serializeError2(event.error)
|
|
2571
|
+
})
|
|
2116
2572
|
};
|
|
2117
2573
|
}
|
|
2118
|
-
function
|
|
2119
|
-
|
|
2574
|
+
function stageCategory(node) {
|
|
2575
|
+
if (node.kind === "parallel" || node.kind === "branch") {
|
|
2576
|
+
return "parallel";
|
|
2577
|
+
}
|
|
2578
|
+
if (node.kind === "agent") {
|
|
2579
|
+
return "agent";
|
|
2580
|
+
}
|
|
2581
|
+
if (node.kind === "extractor") {
|
|
2582
|
+
return "extractor";
|
|
2583
|
+
}
|
|
2584
|
+
return "stage";
|
|
2120
2585
|
}
|
|
2121
|
-
function
|
|
2122
|
-
return
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2586
|
+
function nodeMetadata(node) {
|
|
2587
|
+
return cleanMetadata({
|
|
2588
|
+
nodeId: node.id,
|
|
2589
|
+
kind: node.kind,
|
|
2590
|
+
label: node.label,
|
|
2591
|
+
agentId: node.agentId,
|
|
2592
|
+
pipelineId: node.pipelineId,
|
|
2593
|
+
branchKey: node.branchKey
|
|
2127
2594
|
});
|
|
2128
2595
|
}
|
|
2129
|
-
function
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2596
|
+
function cleanMetadata(value) {
|
|
2597
|
+
return Object.fromEntries(
|
|
2598
|
+
Object.entries(value).filter(([, item]) => item !== void 0)
|
|
2599
|
+
);
|
|
2600
|
+
}
|
|
2601
|
+
function byteLength(value) {
|
|
2602
|
+
return value === void 0 ? void 0 : new TextEncoder().encode(value).length;
|
|
2603
|
+
}
|
|
2604
|
+
function formatUnknown(value) {
|
|
2605
|
+
try {
|
|
2606
|
+
return JSON.stringify(value);
|
|
2607
|
+
} catch {
|
|
2608
|
+
return void 0;
|
|
2609
|
+
}
|
|
2134
2610
|
}
|
|
2135
2611
|
|
|
2136
2612
|
// src/runtime/runs.ts
|
|
@@ -2719,69 +3195,1102 @@ async function parseRunRequest(c) {
|
|
|
2719
3195
|
return request;
|
|
2720
3196
|
}
|
|
2721
3197
|
|
|
2722
|
-
// src/runtime/
|
|
2723
|
-
function
|
|
2724
|
-
app.get(
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
}
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
...agentId === void 0 ? {} : { agentId },
|
|
2735
|
-
limit
|
|
2736
|
-
});
|
|
2737
|
-
return c.json({ sessions });
|
|
2738
|
-
});
|
|
2739
|
-
app.post("/sessions", async (c) => {
|
|
2740
|
-
const body = await parseCreateSessionRequest(c);
|
|
2741
|
-
if ("error" in body) {
|
|
2742
|
-
return body.error;
|
|
2743
|
-
}
|
|
2744
|
-
if (!props.agentMap.has(body.agentId)) {
|
|
2745
|
-
return errorResponse(c, 404, "not_found", "Agent not found");
|
|
3198
|
+
// src/runtime/pipelines.ts
|
|
3199
|
+
function registerPipelineRoutes(app, props) {
|
|
3200
|
+
app.get(
|
|
3201
|
+
"/pipelines",
|
|
3202
|
+
(c) => c.json({
|
|
3203
|
+
pipelines: props.pipelines.map(pipelineConfig)
|
|
3204
|
+
})
|
|
3205
|
+
);
|
|
3206
|
+
app.get("/pipelines/:pipelineId", (c) => {
|
|
3207
|
+
const pipeline = props.pipelineMap.get(c.req.param("pipelineId"));
|
|
3208
|
+
if (pipeline === void 0) {
|
|
3209
|
+
return errorResponse(c, 404, "not_found", "Pipeline not found");
|
|
2746
3210
|
}
|
|
2747
|
-
|
|
2748
|
-
id: globalThis.crypto.randomUUID(),
|
|
2749
|
-
agentId: body.agentId,
|
|
2750
|
-
...body.title === void 0 ? {} : { title: body.title },
|
|
2751
|
-
...body.metadata === void 0 ? {} : { metadata: body.metadata }
|
|
2752
|
-
});
|
|
2753
|
-
return c.json(session, 201);
|
|
3211
|
+
return c.json(pipelineDetail(pipeline));
|
|
2754
3212
|
});
|
|
2755
|
-
app.get("/
|
|
2756
|
-
const
|
|
2757
|
-
if (
|
|
2758
|
-
return errorResponse(c, 404, "not_found", "
|
|
3213
|
+
app.get("/pipelines/:pipelineId/logs", async (c) => {
|
|
3214
|
+
const pipelineId = c.req.param("pipelineId");
|
|
3215
|
+
if (!props.pipelineMap.has(pipelineId)) {
|
|
3216
|
+
return errorResponse(c, 404, "not_found", "Pipeline not found");
|
|
2759
3217
|
}
|
|
2760
|
-
|
|
2761
|
-
});
|
|
2762
|
-
app.delete("/sessions/:sessionId", async (c) => {
|
|
2763
|
-
if (props.sessionStore.deleteSession === void 0) {
|
|
3218
|
+
if (props.store === void 0) {
|
|
2764
3219
|
return errorResponse(
|
|
2765
3220
|
c,
|
|
2766
3221
|
501,
|
|
2767
3222
|
"unsupported_capability",
|
|
2768
|
-
'Capability "
|
|
2769
|
-
{ capability: "
|
|
3223
|
+
'Capability "pipelines.logs" is not implemented by this runner',
|
|
3224
|
+
{ capability: "pipelines", operation: "logs" }
|
|
2770
3225
|
);
|
|
2771
3226
|
}
|
|
2772
|
-
const
|
|
2773
|
-
if (
|
|
2774
|
-
return errorResponse(c,
|
|
3227
|
+
const limit = parsePipelineLogLimit(c.req.query("limit"));
|
|
3228
|
+
if (limit === void 0) {
|
|
3229
|
+
return errorResponse(c, 400, "bad_request", "limit must be a positive integer");
|
|
2775
3230
|
}
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
if (props.traceStore === void 0) {
|
|
2780
|
-
return unsupportedCapability(c, "traces");
|
|
3231
|
+
const after = parsePipelineLogAfter(c.req.query("after"));
|
|
3232
|
+
if (after === false) {
|
|
3233
|
+
return errorResponse(c, 400, "bad_request", "after must be a non-negative integer");
|
|
2781
3234
|
}
|
|
2782
|
-
const
|
|
2783
|
-
|
|
2784
|
-
|
|
3235
|
+
const logs = await props.store.listPipelineLogs({
|
|
3236
|
+
pipelineId,
|
|
3237
|
+
limit,
|
|
3238
|
+
...after === void 0 ? {} : { after }
|
|
3239
|
+
});
|
|
3240
|
+
const last = logs.at(-1);
|
|
3241
|
+
return c.json({
|
|
3242
|
+
logs,
|
|
3243
|
+
...logs.length === limit && last !== void 0 ? { nextCursor: last.sequence } : {}
|
|
3244
|
+
});
|
|
3245
|
+
});
|
|
3246
|
+
app.post("/pipelines/:pipelineId/runs", async (c) => {
|
|
3247
|
+
const pipeline = props.pipelineMap.get(c.req.param("pipelineId"));
|
|
3248
|
+
if (pipeline === void 0) {
|
|
3249
|
+
return errorResponse(c, 404, "not_found", "Pipeline not found");
|
|
3250
|
+
}
|
|
3251
|
+
const body = await parsePipelineRunRequest(c);
|
|
3252
|
+
if ("error" in body) {
|
|
3253
|
+
return body.error;
|
|
3254
|
+
}
|
|
3255
|
+
const runId = globalThis.crypto.randomUUID();
|
|
3256
|
+
const startedAt = Date.now();
|
|
3257
|
+
await appendPipelineLog(
|
|
3258
|
+
props.store,
|
|
3259
|
+
pipelineRunReceivedLog({
|
|
3260
|
+
pipeline,
|
|
3261
|
+
runId,
|
|
3262
|
+
stream: body.stream === true,
|
|
3263
|
+
input: body.input,
|
|
3264
|
+
...body.metadata === void 0 ? {} : { metadata: body.metadata }
|
|
3265
|
+
})
|
|
3266
|
+
);
|
|
3267
|
+
if (body.stream === true) {
|
|
3268
|
+
return streamPipelineRun(c, {
|
|
3269
|
+
pipeline,
|
|
3270
|
+
runId,
|
|
3271
|
+
input: body.input,
|
|
3272
|
+
startedAt,
|
|
3273
|
+
...props.store === void 0 ? {} : { store: props.store }
|
|
3274
|
+
});
|
|
3275
|
+
}
|
|
3276
|
+
try {
|
|
3277
|
+
await appendPipelineLog(props.store, pipelineRunStartedLog(pipeline, runId));
|
|
3278
|
+
const output = await pipeline.pipeline.run(body.input, {
|
|
3279
|
+
observer: {
|
|
3280
|
+
async onEvent(event) {
|
|
3281
|
+
await appendPipelineLog(props.store, pipelineStageLog(pipeline.id, runId, event));
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
});
|
|
3285
|
+
const jsonOutput = toJsonValue3(output);
|
|
3286
|
+
await appendPipelineLog(
|
|
3287
|
+
props.store,
|
|
3288
|
+
pipelineRunCompletedLog({
|
|
3289
|
+
pipelineId: pipeline.id,
|
|
3290
|
+
runId,
|
|
3291
|
+
durationMs: Date.now() - startedAt,
|
|
3292
|
+
output: jsonOutput
|
|
3293
|
+
})
|
|
3294
|
+
);
|
|
3295
|
+
const response = {
|
|
3296
|
+
runId,
|
|
3297
|
+
pipelineId: pipeline.id,
|
|
3298
|
+
output: jsonOutput
|
|
3299
|
+
};
|
|
3300
|
+
return c.json(response);
|
|
3301
|
+
} catch (error) {
|
|
3302
|
+
await appendPipelineLog(
|
|
3303
|
+
props.store,
|
|
3304
|
+
pipelineRunFailedLog(pipeline.id, runId, error, startedAt)
|
|
3305
|
+
);
|
|
3306
|
+
return errorResponse(c, 500, "internal_error", "Pipeline run failed", serializeError2(error));
|
|
3307
|
+
}
|
|
3308
|
+
});
|
|
3309
|
+
}
|
|
3310
|
+
function pipelineDetail(pipeline) {
|
|
3311
|
+
const graph = pipeline.pipeline.graph();
|
|
3312
|
+
graph.id = pipeline.id;
|
|
3313
|
+
return {
|
|
3314
|
+
...pipelineConfig(pipeline),
|
|
3315
|
+
graph
|
|
3316
|
+
};
|
|
3317
|
+
}
|
|
3318
|
+
function streamPipelineRun(c, props) {
|
|
3319
|
+
c.header("content-type", "application/x-ndjson; charset=utf-8");
|
|
3320
|
+
c.header("cache-control", "no-cache, no-transform");
|
|
3321
|
+
c.header("connection", "keep-alive");
|
|
3322
|
+
c.header("transfer-encoding", "chunked");
|
|
3323
|
+
c.header("x-accel-buffering", "no");
|
|
3324
|
+
return streamResponse2(
|
|
3325
|
+
c,
|
|
3326
|
+
async (stream) => {
|
|
3327
|
+
for await (const event of pipelineRunEvents(props)) {
|
|
3328
|
+
await stream.write(`${JSON.stringify(event)}
|
|
3329
|
+
`);
|
|
3330
|
+
}
|
|
3331
|
+
},
|
|
3332
|
+
async (error, stream) => {
|
|
3333
|
+
await stream.write(`${JSON.stringify({ type: "error", error: serializeError2(error) })}
|
|
3334
|
+
`);
|
|
3335
|
+
}
|
|
3336
|
+
);
|
|
3337
|
+
}
|
|
3338
|
+
async function* pipelineRunEvents(props) {
|
|
3339
|
+
yield* emitPipelineLog(props.store, pipelineRunStartedLog(props.pipeline, props.runId));
|
|
3340
|
+
const events = new AsyncEventQueue();
|
|
3341
|
+
const run = props.pipeline.pipeline.run(props.input, {
|
|
3342
|
+
observer: {
|
|
3343
|
+
async onEvent(event) {
|
|
3344
|
+
const log = await appendPipelineLog(
|
|
3345
|
+
props.store,
|
|
3346
|
+
pipelineStageLog(props.pipeline.id, props.runId, event)
|
|
3347
|
+
);
|
|
3348
|
+
if (log !== void 0) {
|
|
3349
|
+
events.push({ type: "pipeline_log", log });
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
}).then(async (output) => {
|
|
3354
|
+
const jsonOutput = toJsonValue3(output);
|
|
3355
|
+
const log = await appendPipelineLog(
|
|
3356
|
+
props.store,
|
|
3357
|
+
pipelineRunCompletedLog({
|
|
3358
|
+
pipelineId: props.pipeline.id,
|
|
3359
|
+
runId: props.runId,
|
|
3360
|
+
durationMs: Date.now() - props.startedAt,
|
|
3361
|
+
output: jsonOutput
|
|
3362
|
+
})
|
|
3363
|
+
);
|
|
3364
|
+
if (log !== void 0) {
|
|
3365
|
+
events.push({ type: "pipeline_log", log });
|
|
3366
|
+
}
|
|
3367
|
+
events.push({
|
|
3368
|
+
type: "pipeline_final",
|
|
3369
|
+
runId: props.runId,
|
|
3370
|
+
pipelineId: props.pipeline.id,
|
|
3371
|
+
output: jsonOutput
|
|
3372
|
+
});
|
|
3373
|
+
}).catch(async (error) => {
|
|
3374
|
+
const log = await appendPipelineLog(
|
|
3375
|
+
props.store,
|
|
3376
|
+
pipelineRunFailedLog(props.pipeline.id, props.runId, error, props.startedAt)
|
|
3377
|
+
);
|
|
3378
|
+
if (log !== void 0) {
|
|
3379
|
+
events.push({ type: "pipeline_log", log });
|
|
3380
|
+
}
|
|
3381
|
+
events.push({ type: "error", error: serializeError2(error) });
|
|
3382
|
+
}).finally(() => events.close());
|
|
3383
|
+
try {
|
|
3384
|
+
while (true) {
|
|
3385
|
+
const next = await events.next();
|
|
3386
|
+
if (next.done === true) {
|
|
3387
|
+
break;
|
|
3388
|
+
}
|
|
3389
|
+
yield next.value;
|
|
3390
|
+
}
|
|
3391
|
+
} finally {
|
|
3392
|
+
await run;
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
3395
|
+
async function parsePipelineRunRequest(c) {
|
|
3396
|
+
let body;
|
|
3397
|
+
try {
|
|
3398
|
+
body = await c.req.json();
|
|
3399
|
+
} catch {
|
|
3400
|
+
return { error: errorResponse(c, 400, "bad_request", "Request body must be JSON") };
|
|
3401
|
+
}
|
|
3402
|
+
if (!isObject(body)) {
|
|
3403
|
+
return { error: errorResponse(c, 400, "bad_request", "Request body must be an object") };
|
|
3404
|
+
}
|
|
3405
|
+
if (!("input" in body) || !isJsonValue2(body.input)) {
|
|
3406
|
+
return { error: errorResponse(c, 400, "bad_request", "input must be JSON-compatible") };
|
|
3407
|
+
}
|
|
3408
|
+
const request = {
|
|
3409
|
+
input: body.input
|
|
3410
|
+
};
|
|
3411
|
+
if ("stream" in body) {
|
|
3412
|
+
if (typeof body.stream !== "boolean") {
|
|
3413
|
+
return { error: errorResponse(c, 400, "bad_request", "stream must be a boolean") };
|
|
3414
|
+
}
|
|
3415
|
+
request.stream = body.stream;
|
|
3416
|
+
}
|
|
3417
|
+
if ("metadata" in body) {
|
|
3418
|
+
if (!isJsonObject(body.metadata)) {
|
|
3419
|
+
return { error: errorResponse(c, 400, "bad_request", "metadata must be an object") };
|
|
3420
|
+
}
|
|
3421
|
+
request.metadata = body.metadata;
|
|
3422
|
+
}
|
|
3423
|
+
return request;
|
|
3424
|
+
}
|
|
3425
|
+
function parsePipelineLogLimit(value) {
|
|
3426
|
+
if (value === void 0 || value.trim().length === 0) {
|
|
3427
|
+
return 200;
|
|
3428
|
+
}
|
|
3429
|
+
const limit = Number(value);
|
|
3430
|
+
if (!Number.isInteger(limit) || limit <= 0) {
|
|
3431
|
+
return void 0;
|
|
3432
|
+
}
|
|
3433
|
+
return Math.min(limit, 1e3);
|
|
3434
|
+
}
|
|
3435
|
+
function parsePipelineLogAfter(value) {
|
|
3436
|
+
if (value === void 0 || value.trim().length === 0) {
|
|
3437
|
+
return void 0;
|
|
3438
|
+
}
|
|
3439
|
+
const after = Number(value);
|
|
3440
|
+
if (!Number.isInteger(after) || after < 0) {
|
|
3441
|
+
return false;
|
|
3442
|
+
}
|
|
3443
|
+
return after;
|
|
3444
|
+
}
|
|
3445
|
+
function isJsonValue2(value) {
|
|
3446
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
3447
|
+
return Number.isFinite(value) || typeof value !== "number";
|
|
3448
|
+
}
|
|
3449
|
+
if (Array.isArray(value)) {
|
|
3450
|
+
return value.every(isJsonValue2);
|
|
3451
|
+
}
|
|
3452
|
+
if (isObject(value)) {
|
|
3453
|
+
return Object.values(value).every((item) => item === void 0 || isJsonValue2(item));
|
|
3454
|
+
}
|
|
3455
|
+
return false;
|
|
3456
|
+
}
|
|
3457
|
+
function toJsonValue3(value) {
|
|
3458
|
+
if (isJsonValue2(value)) {
|
|
3459
|
+
return value;
|
|
3460
|
+
}
|
|
3461
|
+
if (value === void 0) {
|
|
3462
|
+
return null;
|
|
3463
|
+
}
|
|
3464
|
+
try {
|
|
3465
|
+
const parsed = JSON.parse(JSON.stringify(value));
|
|
3466
|
+
return isJsonValue2(parsed) ? parsed : String(value);
|
|
3467
|
+
} catch {
|
|
3468
|
+
return String(value);
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
|
|
3472
|
+
// src/runtime/questions.ts
|
|
3473
|
+
import { createHook as createHook2, parseToolArgs as parseToolArgs2 } from "@anvia/core";
|
|
3474
|
+
function registerQuestionRoutes(app, questions) {
|
|
3475
|
+
app.get("/questions", (c) => {
|
|
3476
|
+
const status = parseQuestionStatus(c.req.query("status"));
|
|
3477
|
+
if (status === false) {
|
|
3478
|
+
return errorResponse(c, 400, "bad_request", "status must be pending or resolved");
|
|
3479
|
+
}
|
|
3480
|
+
const options = {};
|
|
3481
|
+
const runId = optionalQueryString(c.req.query("runId"));
|
|
3482
|
+
const agentId = optionalQueryString(c.req.query("agentId"));
|
|
3483
|
+
const sessionId = optionalQueryString(c.req.query("sessionId"));
|
|
3484
|
+
if (status !== void 0) {
|
|
3485
|
+
options.status = status;
|
|
3486
|
+
}
|
|
3487
|
+
if (runId !== void 0) {
|
|
3488
|
+
options.runId = runId;
|
|
3489
|
+
}
|
|
3490
|
+
if (agentId !== void 0) {
|
|
3491
|
+
options.agentId = agentId;
|
|
3492
|
+
}
|
|
3493
|
+
if (sessionId !== void 0) {
|
|
3494
|
+
options.sessionId = sessionId;
|
|
3495
|
+
}
|
|
3496
|
+
return c.json({ questions: questions.list(options) });
|
|
3497
|
+
});
|
|
3498
|
+
app.post("/questions/:questionId/answer", async (c) => {
|
|
3499
|
+
const body = await parseQuestionAnswerRequest(c);
|
|
3500
|
+
if ("error" in body) {
|
|
3501
|
+
return body.error;
|
|
3502
|
+
}
|
|
3503
|
+
const result = questions.answer(c.req.param("questionId"), body.answers);
|
|
3504
|
+
if (result === "missing") {
|
|
3505
|
+
return errorResponse(c, 404, "not_found", "Question not found");
|
|
3506
|
+
}
|
|
3507
|
+
if (result === "resolved") {
|
|
3508
|
+
return errorResponse(c, 409, "conflict", "Question is already answered");
|
|
3509
|
+
}
|
|
3510
|
+
return c.json(result);
|
|
3511
|
+
});
|
|
3512
|
+
}
|
|
3513
|
+
function parseQuestionStatus(value) {
|
|
3514
|
+
const status = optionalQueryString(value);
|
|
3515
|
+
if (status === void 0) {
|
|
3516
|
+
return void 0;
|
|
3517
|
+
}
|
|
3518
|
+
return status === "pending" || status === "resolved" ? status : false;
|
|
3519
|
+
}
|
|
3520
|
+
async function parseQuestionAnswerRequest(c) {
|
|
3521
|
+
let body;
|
|
3522
|
+
try {
|
|
3523
|
+
body = await c.req.json();
|
|
3524
|
+
} catch {
|
|
3525
|
+
return { error: errorResponse(c, 400, "bad_request", "Request body must be JSON") };
|
|
3526
|
+
}
|
|
3527
|
+
if (!isObject(body)) {
|
|
3528
|
+
return { error: errorResponse(c, 400, "bad_request", "Request body must be an object") };
|
|
3529
|
+
}
|
|
3530
|
+
if (!Array.isArray(body.answers)) {
|
|
3531
|
+
return { error: errorResponse(c, 400, "bad_request", "answers must be an array") };
|
|
3532
|
+
}
|
|
3533
|
+
const answers = [];
|
|
3534
|
+
for (const answer of body.answers) {
|
|
3535
|
+
if (!isObject(answer)) {
|
|
3536
|
+
return { error: errorResponse(c, 400, "bad_request", "answers must contain objects") };
|
|
3537
|
+
}
|
|
3538
|
+
if (typeof answer.questionId !== "string" || answer.questionId.trim().length === 0) {
|
|
3539
|
+
return { error: errorResponse(c, 400, "bad_request", "questionId must be a string") };
|
|
3540
|
+
}
|
|
3541
|
+
if (typeof answer.answer !== "string" || answer.answer.trim().length === 0) {
|
|
3542
|
+
return { error: errorResponse(c, 400, "bad_request", "answer must be a string") };
|
|
3543
|
+
}
|
|
3544
|
+
if ("choice" in answer && typeof answer.choice !== "string") {
|
|
3545
|
+
return { error: errorResponse(c, 400, "bad_request", "choice must be a string") };
|
|
3546
|
+
}
|
|
3547
|
+
if ("custom" in answer && typeof answer.custom !== "boolean") {
|
|
3548
|
+
return { error: errorResponse(c, 400, "bad_request", "custom must be a boolean") };
|
|
3549
|
+
}
|
|
3550
|
+
answers.push({
|
|
3551
|
+
questionId: answer.questionId.trim(),
|
|
3552
|
+
answer: answer.answer.trim(),
|
|
3553
|
+
...typeof answer.choice === "string" ? { choice: answer.choice } : {},
|
|
3554
|
+
...typeof answer.custom === "boolean" ? { custom: answer.custom } : {}
|
|
3555
|
+
});
|
|
3556
|
+
}
|
|
3557
|
+
return { answers };
|
|
3558
|
+
}
|
|
3559
|
+
function createQuestionRuntime() {
|
|
3560
|
+
const questions = /* @__PURE__ */ new Map();
|
|
3561
|
+
return {
|
|
3562
|
+
questions,
|
|
3563
|
+
createHook(context) {
|
|
3564
|
+
return createHook2({
|
|
3565
|
+
async onToolCall({ toolName, toolCallId, internalCallId, args, tool: control }) {
|
|
3566
|
+
if (toolName !== "ask_question") {
|
|
3567
|
+
return control.run();
|
|
3568
|
+
}
|
|
3569
|
+
const prompts = normalizeQuestionPrompts(parseToolArgs2(args));
|
|
3570
|
+
if ("error" in prompts) {
|
|
3571
|
+
return control.skip(prompts.error);
|
|
3572
|
+
}
|
|
3573
|
+
const answers = await requestQuestion(questions, context, {
|
|
3574
|
+
toolName,
|
|
3575
|
+
...toolCallId === void 0 ? {} : { toolCallId },
|
|
3576
|
+
internalCallId,
|
|
3577
|
+
args,
|
|
3578
|
+
questions: prompts.questions
|
|
3579
|
+
});
|
|
3580
|
+
return control.skip(JSON.stringify({ answers }));
|
|
3581
|
+
}
|
|
3582
|
+
});
|
|
3583
|
+
},
|
|
3584
|
+
list(options) {
|
|
3585
|
+
return [...questions.values()].filter((question) => {
|
|
3586
|
+
if (options.status === "pending" && question.status !== "pending") {
|
|
3587
|
+
return false;
|
|
3588
|
+
}
|
|
3589
|
+
if (options.status === "resolved" && question.status === "pending") {
|
|
3590
|
+
return false;
|
|
3591
|
+
}
|
|
3592
|
+
if (options.runId !== void 0 && question.runId !== options.runId) {
|
|
3593
|
+
return false;
|
|
3594
|
+
}
|
|
3595
|
+
if (options.agentId !== void 0 && question.agentId !== options.agentId) {
|
|
3596
|
+
return false;
|
|
3597
|
+
}
|
|
3598
|
+
if (options.sessionId !== void 0 && question.sessionId !== options.sessionId) {
|
|
3599
|
+
return false;
|
|
3600
|
+
}
|
|
3601
|
+
return true;
|
|
3602
|
+
}).map(publicQuestion);
|
|
3603
|
+
},
|
|
3604
|
+
answer(id, answers) {
|
|
3605
|
+
const question = questions.get(id);
|
|
3606
|
+
if (question === void 0) {
|
|
3607
|
+
return "missing";
|
|
3608
|
+
}
|
|
3609
|
+
if (!isPendingQuestion(question)) {
|
|
3610
|
+
return "resolved";
|
|
3611
|
+
}
|
|
3612
|
+
const resolved = resolveQuestion(question, answers);
|
|
3613
|
+
questions.set(id, resolved);
|
|
3614
|
+
question.emit?.({ type: "tool_question_result", question: resolved });
|
|
3615
|
+
question.resolve(answers);
|
|
3616
|
+
return publicQuestion(resolved);
|
|
3617
|
+
}
|
|
3618
|
+
};
|
|
3619
|
+
}
|
|
3620
|
+
async function requestQuestion(questions, context, request) {
|
|
3621
|
+
const id = globalThis.crypto.randomUUID();
|
|
3622
|
+
const question = {
|
|
3623
|
+
id,
|
|
3624
|
+
runId: context.runId,
|
|
3625
|
+
agentId: context.agentId,
|
|
3626
|
+
...context.sessionId === void 0 ? {} : { sessionId: context.sessionId },
|
|
3627
|
+
toolName: request.toolName,
|
|
3628
|
+
...request.toolCallId === void 0 ? {} : { callId: request.toolCallId },
|
|
3629
|
+
internalCallId: request.internalCallId,
|
|
3630
|
+
args: request.args,
|
|
3631
|
+
questions: request.questions,
|
|
3632
|
+
status: "pending",
|
|
3633
|
+
requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3634
|
+
...context.emit === void 0 ? {} : { emit: context.emit },
|
|
3635
|
+
resolve: () => {
|
|
3636
|
+
}
|
|
3637
|
+
};
|
|
3638
|
+
const answer = new Promise((resolve2) => {
|
|
3639
|
+
question.resolve = (answers) => {
|
|
3640
|
+
resolve2(answers);
|
|
3641
|
+
};
|
|
3642
|
+
});
|
|
3643
|
+
questions.set(id, question);
|
|
3644
|
+
context.emit?.({ type: "tool_question_request", question: publicQuestion(question) });
|
|
3645
|
+
return answer;
|
|
3646
|
+
}
|
|
3647
|
+
function normalizeQuestionPrompts(args) {
|
|
3648
|
+
if (!isObject(args)) {
|
|
3649
|
+
return { error: "ask_question requires a JSON object with questions." };
|
|
3650
|
+
}
|
|
3651
|
+
const rawQuestions = Array.isArray(args.questions) ? args.questions : [args];
|
|
3652
|
+
if (rawQuestions.length === 0) {
|
|
3653
|
+
return { error: "ask_question requires at least one question." };
|
|
3654
|
+
}
|
|
3655
|
+
const questions = [];
|
|
3656
|
+
for (const [index, question] of rawQuestions.entries()) {
|
|
3657
|
+
const normalized = normalizeQuestionPrompt(question, index);
|
|
3658
|
+
if (normalized === void 0) {
|
|
3659
|
+
return {
|
|
3660
|
+
error: "ask_question requires every question to include text and at least one choice."
|
|
3661
|
+
};
|
|
3662
|
+
}
|
|
3663
|
+
questions.push(normalized);
|
|
3664
|
+
}
|
|
3665
|
+
return { questions };
|
|
3666
|
+
}
|
|
3667
|
+
function normalizeQuestionPrompt(value, index) {
|
|
3668
|
+
if (!isObject(value) || typeof value.question !== "string" || value.question.trim().length === 0) {
|
|
3669
|
+
return void 0;
|
|
3670
|
+
}
|
|
3671
|
+
const choices = Array.isArray(value.choices) ? value.choices.map(normalizeQuestionChoice).filter((choice) => choice !== void 0) : [];
|
|
3672
|
+
if (choices.length === 0) {
|
|
3673
|
+
return void 0;
|
|
3674
|
+
}
|
|
3675
|
+
return {
|
|
3676
|
+
id: typeof value.id === "string" && value.id.trim().length > 0 ? value.id.trim() : `question_${index + 1}`,
|
|
3677
|
+
question: value.question.trim(),
|
|
3678
|
+
choices
|
|
3679
|
+
};
|
|
3680
|
+
}
|
|
3681
|
+
function normalizeQuestionChoice(value) {
|
|
3682
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
3683
|
+
return { label: value.trim(), value: value.trim() };
|
|
3684
|
+
}
|
|
3685
|
+
if (!isObject(value) || typeof value.label !== "string" || value.label.trim().length === 0) {
|
|
3686
|
+
return void 0;
|
|
3687
|
+
}
|
|
3688
|
+
return {
|
|
3689
|
+
label: value.label.trim(),
|
|
3690
|
+
value: typeof value.value === "string" && value.value.trim().length > 0 ? value.value.trim() : value.label.trim()
|
|
3691
|
+
};
|
|
3692
|
+
}
|
|
3693
|
+
function isPendingQuestion(question) {
|
|
3694
|
+
return question !== void 0 && question.status === "pending" && "resolve" in question;
|
|
3695
|
+
}
|
|
3696
|
+
function resolveQuestion(question, answers) {
|
|
3697
|
+
return publicQuestion({
|
|
3698
|
+
...question,
|
|
3699
|
+
status: "answered",
|
|
3700
|
+
answeredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3701
|
+
answers
|
|
3702
|
+
});
|
|
3703
|
+
}
|
|
3704
|
+
function publicQuestion(question) {
|
|
3705
|
+
const { emit, resolve: resolve2, ...rest } = question;
|
|
3706
|
+
void emit;
|
|
3707
|
+
void resolve2;
|
|
3708
|
+
return rest;
|
|
3709
|
+
}
|
|
3710
|
+
|
|
3711
|
+
// src/runtime/session-logs.ts
|
|
3712
|
+
async function appendSessionLog(store, input) {
|
|
3713
|
+
return store?.appendSessionLog?.(input);
|
|
3714
|
+
}
|
|
3715
|
+
async function* streamSessionRunLogs(props) {
|
|
3716
|
+
yield* emitLog(props.store, runStartedLog(props.session, props.runId));
|
|
3717
|
+
yield* emitLog(props.store, memoryLoadedLog(props.session, props.runId));
|
|
3718
|
+
try {
|
|
3719
|
+
for await (const event of props.stream) {
|
|
3720
|
+
for (const input of logsFromStreamEvent({
|
|
3721
|
+
event,
|
|
3722
|
+
runId: props.runId,
|
|
3723
|
+
sessionId: props.session.id,
|
|
3724
|
+
startedAt: props.startedAt
|
|
3725
|
+
})) {
|
|
3726
|
+
yield* emitLog(props.store, input);
|
|
3727
|
+
}
|
|
3728
|
+
yield event;
|
|
3729
|
+
}
|
|
3730
|
+
} catch (error) {
|
|
3731
|
+
yield* emitLog(
|
|
3732
|
+
props.store,
|
|
3733
|
+
runFailedLog(props.session.id, props.runId, error, props.startedAt)
|
|
3734
|
+
);
|
|
3735
|
+
throw error;
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
function sessionCreatedLog(session) {
|
|
3739
|
+
return {
|
|
3740
|
+
sessionId: session.id,
|
|
3741
|
+
level: "info",
|
|
3742
|
+
category: "session",
|
|
3743
|
+
event: "session.created",
|
|
3744
|
+
message: "Session created",
|
|
3745
|
+
metadata: cleanMetadata2({
|
|
3746
|
+
agentId: session.agentId,
|
|
3747
|
+
hasTitle: session.title !== void 0,
|
|
3748
|
+
titleLength: session.title?.length ?? 0
|
|
3749
|
+
})
|
|
3750
|
+
};
|
|
3751
|
+
}
|
|
3752
|
+
function runReceivedLog(props) {
|
|
3753
|
+
return {
|
|
3754
|
+
sessionId: props.sessionId,
|
|
3755
|
+
runId: props.runId,
|
|
3756
|
+
level: "info",
|
|
3757
|
+
category: "api",
|
|
3758
|
+
event: "run.received",
|
|
3759
|
+
message: "Run request received",
|
|
3760
|
+
metadata: cleanMetadata2({
|
|
3761
|
+
agentId: props.agentId,
|
|
3762
|
+
stream: props.stream,
|
|
3763
|
+
message: messageSummary(props.message),
|
|
3764
|
+
maxTurns: props.maxTurns,
|
|
3765
|
+
toolConcurrency: props.toolConcurrency,
|
|
3766
|
+
hasTrace: props.hasTrace,
|
|
3767
|
+
metadataKeys: Object.keys(props.metadata ?? {})
|
|
3768
|
+
})
|
|
3769
|
+
};
|
|
3770
|
+
}
|
|
3771
|
+
function runStartedLog(session, runId) {
|
|
3772
|
+
return {
|
|
3773
|
+
sessionId: session.id,
|
|
3774
|
+
runId,
|
|
3775
|
+
level: "info",
|
|
3776
|
+
category: "run",
|
|
3777
|
+
event: "run.started",
|
|
3778
|
+
message: "Run started",
|
|
3779
|
+
metadata: cleanMetadata2({
|
|
3780
|
+
agentId: session.agentId,
|
|
3781
|
+
existingMessageCount: session.messageCount
|
|
3782
|
+
})
|
|
3783
|
+
};
|
|
3784
|
+
}
|
|
3785
|
+
function memoryLoadedLog(session, runId) {
|
|
3786
|
+
return {
|
|
3787
|
+
sessionId: session.id,
|
|
3788
|
+
runId,
|
|
3789
|
+
level: "debug",
|
|
3790
|
+
category: "memory",
|
|
3791
|
+
event: "memory.loaded",
|
|
3792
|
+
message: "Session memory loaded",
|
|
3793
|
+
metadata: cleanMetadata2({
|
|
3794
|
+
messageCount: session.messageCount,
|
|
3795
|
+
transcriptEntries: session.transcript.length
|
|
3796
|
+
})
|
|
3797
|
+
};
|
|
3798
|
+
}
|
|
3799
|
+
function runCompletedLog(props) {
|
|
3800
|
+
return {
|
|
3801
|
+
sessionId: props.sessionId,
|
|
3802
|
+
runId: props.runId,
|
|
3803
|
+
level: "info",
|
|
3804
|
+
category: "run",
|
|
3805
|
+
event: "run.completed",
|
|
3806
|
+
message: "Run completed",
|
|
3807
|
+
metadata: cleanMetadata2({
|
|
3808
|
+
durationMs: props.durationMs,
|
|
3809
|
+
usage: usageSummary(props.usage),
|
|
3810
|
+
outputBytes: byteLength2(props.output),
|
|
3811
|
+
messageCount: props.messageCount
|
|
3812
|
+
})
|
|
3813
|
+
};
|
|
3814
|
+
}
|
|
3815
|
+
function memorySavedLog(props) {
|
|
3816
|
+
return {
|
|
3817
|
+
sessionId: props.sessionId,
|
|
3818
|
+
runId: props.runId,
|
|
3819
|
+
level: "debug",
|
|
3820
|
+
category: "memory",
|
|
3821
|
+
event: "memory.saved",
|
|
3822
|
+
message: "Session memory saved",
|
|
3823
|
+
metadata: cleanMetadata2({
|
|
3824
|
+
messageCount: props.messageCount
|
|
3825
|
+
})
|
|
3826
|
+
};
|
|
3827
|
+
}
|
|
3828
|
+
function runFailedLog(sessionId, runId, error, startedAt) {
|
|
3829
|
+
return {
|
|
3830
|
+
sessionId,
|
|
3831
|
+
runId,
|
|
3832
|
+
level: "error",
|
|
3833
|
+
category: "run",
|
|
3834
|
+
event: "run.failed",
|
|
3835
|
+
message: "Run failed",
|
|
3836
|
+
metadata: cleanMetadata2({
|
|
3837
|
+
durationMs: Date.now() - startedAt,
|
|
3838
|
+
error: serializeError2(error)
|
|
3839
|
+
})
|
|
3840
|
+
};
|
|
3841
|
+
}
|
|
3842
|
+
function logsFromStreamEvent(props) {
|
|
3843
|
+
const { event, sessionId, runId } = props;
|
|
3844
|
+
if (event.type === "turn_start") {
|
|
3845
|
+
return [
|
|
3846
|
+
{
|
|
3847
|
+
sessionId,
|
|
3848
|
+
runId,
|
|
3849
|
+
level: "debug",
|
|
3850
|
+
category: "prompt",
|
|
3851
|
+
event: "prompt.prepared",
|
|
3852
|
+
message: `Turn ${event.turn} prompt prepared`,
|
|
3853
|
+
metadata: cleanMetadata2({
|
|
3854
|
+
turn: event.turn,
|
|
3855
|
+
prompt: messageSummary(event.prompt),
|
|
3856
|
+
historyCount: event.history.length
|
|
3857
|
+
})
|
|
3858
|
+
}
|
|
3859
|
+
];
|
|
3860
|
+
}
|
|
3861
|
+
if (event.type === "tool_call") {
|
|
3862
|
+
return [
|
|
3863
|
+
{
|
|
3864
|
+
sessionId,
|
|
3865
|
+
runId,
|
|
3866
|
+
level: "info",
|
|
3867
|
+
category: "tool",
|
|
3868
|
+
event: "tool.called",
|
|
3869
|
+
message: `Tool ${event.toolCall.function.name} called`,
|
|
3870
|
+
metadata: cleanMetadata2({
|
|
3871
|
+
turn: event.turn,
|
|
3872
|
+
toolName: event.toolCall.function.name,
|
|
3873
|
+
callId: event.toolCall.callId ?? event.toolCall.id,
|
|
3874
|
+
argumentBytes: byteLength2(formatUnknown2(event.toolCall.function.arguments))
|
|
3875
|
+
})
|
|
3876
|
+
}
|
|
3877
|
+
];
|
|
3878
|
+
}
|
|
3879
|
+
if (event.type === "tool_result") {
|
|
3880
|
+
return [
|
|
3881
|
+
{
|
|
3882
|
+
sessionId,
|
|
3883
|
+
runId,
|
|
3884
|
+
level: "info",
|
|
3885
|
+
category: "tool",
|
|
3886
|
+
event: "tool.completed",
|
|
3887
|
+
message: `Tool ${event.toolName} completed`,
|
|
3888
|
+
metadata: cleanMetadata2({
|
|
3889
|
+
turn: event.turn,
|
|
3890
|
+
toolName: event.toolName,
|
|
3891
|
+
callId: event.toolCallId,
|
|
3892
|
+
internalCallId: event.internalCallId,
|
|
3893
|
+
argumentBytes: byteLength2(event.args),
|
|
3894
|
+
resultBytes: byteLength2(event.result)
|
|
3895
|
+
})
|
|
3896
|
+
}
|
|
3897
|
+
];
|
|
3898
|
+
}
|
|
3899
|
+
if (event.type === "turn_end") {
|
|
3900
|
+
return [
|
|
3901
|
+
{
|
|
3902
|
+
sessionId,
|
|
3903
|
+
runId,
|
|
3904
|
+
level: "debug",
|
|
3905
|
+
category: "model",
|
|
3906
|
+
event: "model.turn.completed",
|
|
3907
|
+
message: `Model turn ${event.turn} completed`,
|
|
3908
|
+
metadata: cleanMetadata2({
|
|
3909
|
+
turn: event.turn,
|
|
3910
|
+
contentCount: event.response.choice.length,
|
|
3911
|
+
usage: usageSummary(event.response.usage)
|
|
3912
|
+
})
|
|
3913
|
+
}
|
|
3914
|
+
];
|
|
3915
|
+
}
|
|
3916
|
+
if (event.type === "final") {
|
|
3917
|
+
return [
|
|
3918
|
+
runCompletedLog({
|
|
3919
|
+
sessionId,
|
|
3920
|
+
runId,
|
|
3921
|
+
durationMs: Date.now() - props.startedAt,
|
|
3922
|
+
usage: event.usage,
|
|
3923
|
+
output: event.output,
|
|
3924
|
+
messageCount: event.messages.length
|
|
3925
|
+
}),
|
|
3926
|
+
memorySavedLog({ sessionId, runId, messageCount: event.messages.length })
|
|
3927
|
+
];
|
|
3928
|
+
}
|
|
3929
|
+
if (event.type === "error") {
|
|
3930
|
+
return [runFailedLog(sessionId, runId, event.error, props.startedAt)];
|
|
3931
|
+
}
|
|
3932
|
+
if (event.type === "tool_approval_request") {
|
|
3933
|
+
return [
|
|
3934
|
+
{
|
|
3935
|
+
sessionId,
|
|
3936
|
+
runId,
|
|
3937
|
+
level: "info",
|
|
3938
|
+
category: "approval",
|
|
3939
|
+
event: "approval.requested",
|
|
3940
|
+
message: `Approval requested for ${event.approval.toolName}`,
|
|
3941
|
+
metadata: cleanMetadata2({
|
|
3942
|
+
approvalId: event.approval.id,
|
|
3943
|
+
toolName: event.approval.toolName,
|
|
3944
|
+
callId: event.approval.callId,
|
|
3945
|
+
status: event.approval.status,
|
|
3946
|
+
hasReason: event.approval.reason !== void 0,
|
|
3947
|
+
argumentBytes: byteLength2(event.approval.args)
|
|
3948
|
+
})
|
|
3949
|
+
}
|
|
3950
|
+
];
|
|
3951
|
+
}
|
|
3952
|
+
if (event.type === "tool_approval_result") {
|
|
3953
|
+
return [
|
|
3954
|
+
{
|
|
3955
|
+
sessionId,
|
|
3956
|
+
runId,
|
|
3957
|
+
level: event.approval.status === "approved" ? "info" : "warn",
|
|
3958
|
+
category: "approval",
|
|
3959
|
+
event: "approval.resolved",
|
|
3960
|
+
message: `Approval ${event.approval.status} for ${event.approval.toolName}`,
|
|
3961
|
+
metadata: cleanMetadata2({
|
|
3962
|
+
approvalId: event.approval.id,
|
|
3963
|
+
toolName: event.approval.toolName,
|
|
3964
|
+
callId: event.approval.callId,
|
|
3965
|
+
status: event.approval.status,
|
|
3966
|
+
hasReason: event.approval.reason !== void 0
|
|
3967
|
+
})
|
|
3968
|
+
}
|
|
3969
|
+
];
|
|
3970
|
+
}
|
|
3971
|
+
if (event.type === "tool_question_request") {
|
|
3972
|
+
return [
|
|
3973
|
+
{
|
|
3974
|
+
sessionId,
|
|
3975
|
+
runId,
|
|
3976
|
+
level: "info",
|
|
3977
|
+
category: "question",
|
|
3978
|
+
event: "question.requested",
|
|
3979
|
+
message: `Question requested by ${event.question.toolName}`,
|
|
3980
|
+
metadata: cleanMetadata2({
|
|
3981
|
+
questionId: event.question.id,
|
|
3982
|
+
toolName: event.question.toolName,
|
|
3983
|
+
callId: event.question.callId,
|
|
3984
|
+
status: event.question.status,
|
|
3985
|
+
questionCount: event.question.questions.length,
|
|
3986
|
+
argumentBytes: byteLength2(event.question.args)
|
|
3987
|
+
})
|
|
3988
|
+
}
|
|
3989
|
+
];
|
|
3990
|
+
}
|
|
3991
|
+
if (event.type === "tool_question_result") {
|
|
3992
|
+
return [
|
|
3993
|
+
{
|
|
3994
|
+
sessionId,
|
|
3995
|
+
runId,
|
|
3996
|
+
level: "info",
|
|
3997
|
+
category: "question",
|
|
3998
|
+
event: "question.answered",
|
|
3999
|
+
message: `Question answered for ${event.question.toolName}`,
|
|
4000
|
+
metadata: cleanMetadata2({
|
|
4001
|
+
questionId: event.question.id,
|
|
4002
|
+
toolName: event.question.toolName,
|
|
4003
|
+
callId: event.question.callId,
|
|
4004
|
+
status: event.question.status,
|
|
4005
|
+
answerCount: event.question.answers?.length ?? 0
|
|
4006
|
+
})
|
|
4007
|
+
}
|
|
4008
|
+
];
|
|
4009
|
+
}
|
|
4010
|
+
if (event.type === "agent_tool_event") {
|
|
4011
|
+
return childAgentLog(event, sessionId, runId);
|
|
4012
|
+
}
|
|
4013
|
+
return [];
|
|
4014
|
+
}
|
|
4015
|
+
async function* emitLog(store, input) {
|
|
4016
|
+
const log = await appendSessionLog(store, input);
|
|
4017
|
+
if (log !== void 0) {
|
|
4018
|
+
yield { type: "session_log", log };
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
function childAgentLog(event, sessionId, runId) {
|
|
4022
|
+
const child = event.event;
|
|
4023
|
+
if (child.type === "tool_call") {
|
|
4024
|
+
return [
|
|
4025
|
+
{
|
|
4026
|
+
sessionId,
|
|
4027
|
+
runId,
|
|
4028
|
+
level: "debug",
|
|
4029
|
+
category: "tool",
|
|
4030
|
+
event: "child_tool.called",
|
|
4031
|
+
message: `Child agent ${event.agentName ?? event.agentId} called ${child.toolCall.function.name}`,
|
|
4032
|
+
metadata: cleanMetadata2({
|
|
4033
|
+
parentToolName: event.toolName,
|
|
4034
|
+
agentId: event.agentId,
|
|
4035
|
+
hasAgentName: event.agentName !== void 0,
|
|
4036
|
+
turn: event.turn,
|
|
4037
|
+
childTurn: child.turn,
|
|
4038
|
+
toolName: child.toolCall.function.name,
|
|
4039
|
+
callId: child.toolCall.callId ?? child.toolCall.id,
|
|
4040
|
+
argumentBytes: byteLength2(formatUnknown2(child.toolCall.function.arguments))
|
|
4041
|
+
})
|
|
4042
|
+
}
|
|
4043
|
+
];
|
|
4044
|
+
}
|
|
4045
|
+
if (child.type === "tool_result") {
|
|
4046
|
+
return [
|
|
4047
|
+
{
|
|
4048
|
+
sessionId,
|
|
4049
|
+
runId,
|
|
4050
|
+
level: "debug",
|
|
4051
|
+
category: "tool",
|
|
4052
|
+
event: "child_tool.completed",
|
|
4053
|
+
message: `Child agent ${event.agentName ?? event.agentId} completed ${child.toolName}`,
|
|
4054
|
+
metadata: cleanMetadata2({
|
|
4055
|
+
parentToolName: event.toolName,
|
|
4056
|
+
agentId: event.agentId,
|
|
4057
|
+
hasAgentName: event.agentName !== void 0,
|
|
4058
|
+
turn: event.turn,
|
|
4059
|
+
childTurn: child.turn,
|
|
4060
|
+
toolName: child.toolName,
|
|
4061
|
+
callId: child.toolCallId,
|
|
4062
|
+
resultBytes: byteLength2(child.result)
|
|
4063
|
+
})
|
|
4064
|
+
}
|
|
4065
|
+
];
|
|
4066
|
+
}
|
|
4067
|
+
if (child.type === "turn_start") {
|
|
4068
|
+
return [
|
|
4069
|
+
{
|
|
4070
|
+
sessionId,
|
|
4071
|
+
runId,
|
|
4072
|
+
level: "debug",
|
|
4073
|
+
category: "run",
|
|
4074
|
+
event: "child_agent.turn_started",
|
|
4075
|
+
message: `Child agent ${event.agentName ?? event.agentId} turn ${child.turn} started`,
|
|
4076
|
+
metadata: cleanMetadata2({
|
|
4077
|
+
parentToolName: event.toolName,
|
|
4078
|
+
agentId: event.agentId,
|
|
4079
|
+
hasAgentName: event.agentName !== void 0,
|
|
4080
|
+
childTurn: child.turn,
|
|
4081
|
+
historyCount: child.history.length
|
|
4082
|
+
})
|
|
4083
|
+
}
|
|
4084
|
+
];
|
|
4085
|
+
}
|
|
4086
|
+
if (child.type === "final") {
|
|
4087
|
+
return [
|
|
4088
|
+
{
|
|
4089
|
+
sessionId,
|
|
4090
|
+
runId,
|
|
4091
|
+
level: "debug",
|
|
4092
|
+
category: "run",
|
|
4093
|
+
event: "child_agent.completed",
|
|
4094
|
+
message: `Child agent ${event.agentName ?? event.agentId} completed`,
|
|
4095
|
+
metadata: cleanMetadata2({
|
|
4096
|
+
parentToolName: event.toolName,
|
|
4097
|
+
agentId: event.agentId,
|
|
4098
|
+
hasAgentName: event.agentName !== void 0,
|
|
4099
|
+
usage: usageSummary(child.usage),
|
|
4100
|
+
outputBytes: byteLength2(child.output),
|
|
4101
|
+
messageCount: child.messages.length
|
|
4102
|
+
})
|
|
4103
|
+
}
|
|
4104
|
+
];
|
|
4105
|
+
}
|
|
4106
|
+
if (child.type === "error") {
|
|
4107
|
+
return [
|
|
4108
|
+
{
|
|
4109
|
+
sessionId,
|
|
4110
|
+
runId,
|
|
4111
|
+
level: "error",
|
|
4112
|
+
category: "run",
|
|
4113
|
+
event: "child_agent.failed",
|
|
4114
|
+
message: `Child agent ${event.agentName ?? event.agentId} failed`,
|
|
4115
|
+
metadata: cleanMetadata2({
|
|
4116
|
+
parentToolName: event.toolName,
|
|
4117
|
+
agentId: event.agentId,
|
|
4118
|
+
hasAgentName: event.agentName !== void 0,
|
|
4119
|
+
error: serializeError2(child.error)
|
|
4120
|
+
})
|
|
4121
|
+
}
|
|
4122
|
+
];
|
|
4123
|
+
}
|
|
4124
|
+
return [];
|
|
4125
|
+
}
|
|
4126
|
+
function messageSummary(message) {
|
|
4127
|
+
if (typeof message === "string") {
|
|
4128
|
+
return {
|
|
4129
|
+
role: "user",
|
|
4130
|
+
contentKind: "text",
|
|
4131
|
+
byteLength: byteLength2(message)
|
|
4132
|
+
};
|
|
4133
|
+
}
|
|
4134
|
+
return {
|
|
4135
|
+
role: message.role,
|
|
4136
|
+
contentKind: Array.isArray(message.content) ? "parts" : "text",
|
|
4137
|
+
partCount: Array.isArray(message.content) ? message.content.length : 1,
|
|
4138
|
+
byteLength: byteLength2(formatUnknown2(message.content))
|
|
4139
|
+
};
|
|
4140
|
+
}
|
|
4141
|
+
function usageSummary(value) {
|
|
4142
|
+
if (value === void 0 || value === null || typeof value !== "object") {
|
|
4143
|
+
return void 0;
|
|
4144
|
+
}
|
|
4145
|
+
const record = value;
|
|
4146
|
+
return cleanMetadata2({
|
|
4147
|
+
inputTokens: numericValue(record.inputTokens),
|
|
4148
|
+
outputTokens: numericValue(record.outputTokens),
|
|
4149
|
+
totalTokens: numericValue(record.totalTokens),
|
|
4150
|
+
cachedInputTokens: numericValue(record.cachedInputTokens),
|
|
4151
|
+
cacheCreationInputTokens: numericValue(record.cacheCreationInputTokens)
|
|
4152
|
+
});
|
|
4153
|
+
}
|
|
4154
|
+
function cleanMetadata2(value) {
|
|
4155
|
+
const cleaned = {};
|
|
4156
|
+
for (const [key, item] of Object.entries(value)) {
|
|
4157
|
+
if (item === void 0) {
|
|
4158
|
+
continue;
|
|
4159
|
+
}
|
|
4160
|
+
const jsonValue = cleanJsonValue(item);
|
|
4161
|
+
if (jsonValue !== void 0) {
|
|
4162
|
+
cleaned[key] = jsonValue;
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4165
|
+
return cleaned;
|
|
4166
|
+
}
|
|
4167
|
+
function cleanJsonValue(value) {
|
|
4168
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
4169
|
+
return value;
|
|
4170
|
+
}
|
|
4171
|
+
if (Array.isArray(value)) {
|
|
4172
|
+
return value.map((item) => cleanJsonValue(item)).filter((item) => item !== void 0);
|
|
4173
|
+
}
|
|
4174
|
+
if (typeof value === "object" && value !== null) {
|
|
4175
|
+
return cleanMetadata2(value);
|
|
4176
|
+
}
|
|
4177
|
+
return void 0;
|
|
4178
|
+
}
|
|
4179
|
+
function numericValue(value) {
|
|
4180
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
4181
|
+
}
|
|
4182
|
+
function byteLength2(value) {
|
|
4183
|
+
return value === void 0 ? 0 : new TextEncoder().encode(value).byteLength;
|
|
4184
|
+
}
|
|
4185
|
+
function formatUnknown2(value) {
|
|
4186
|
+
if (typeof value === "string") {
|
|
4187
|
+
return value;
|
|
4188
|
+
}
|
|
4189
|
+
try {
|
|
4190
|
+
return JSON.stringify(value);
|
|
4191
|
+
} catch {
|
|
4192
|
+
return String(value);
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
|
|
4196
|
+
// src/runtime/sessions.ts
|
|
4197
|
+
function registerSessionRoutes(app, props) {
|
|
4198
|
+
app.get("/sessions", async (c) => {
|
|
4199
|
+
const agentId = optionalQueryString(c.req.query("agentId"));
|
|
4200
|
+
if (agentId !== void 0 && !props.agentMap.has(agentId)) {
|
|
4201
|
+
return errorResponse(c, 404, "not_found", "Agent not found");
|
|
4202
|
+
}
|
|
4203
|
+
const limit = parseLimit(c.req.query("limit"));
|
|
4204
|
+
if (limit === void 0) {
|
|
4205
|
+
return errorResponse(c, 400, "bad_request", "limit must be a positive integer");
|
|
4206
|
+
}
|
|
4207
|
+
const sessions = await props.sessionStore.listSessions({
|
|
4208
|
+
...agentId === void 0 ? {} : { agentId },
|
|
4209
|
+
limit
|
|
4210
|
+
});
|
|
4211
|
+
return c.json({ sessions });
|
|
4212
|
+
});
|
|
4213
|
+
app.post("/sessions", async (c) => {
|
|
4214
|
+
const body = await parseCreateSessionRequest(c);
|
|
4215
|
+
if ("error" in body) {
|
|
4216
|
+
return body.error;
|
|
4217
|
+
}
|
|
4218
|
+
if (!props.agentMap.has(body.agentId)) {
|
|
4219
|
+
return errorResponse(c, 404, "not_found", "Agent not found");
|
|
4220
|
+
}
|
|
4221
|
+
const session = await props.sessionStore.createSession({
|
|
4222
|
+
id: globalThis.crypto.randomUUID(),
|
|
4223
|
+
agentId: body.agentId,
|
|
4224
|
+
...body.title === void 0 ? {} : { title: body.title },
|
|
4225
|
+
...body.metadata === void 0 ? {} : { metadata: body.metadata }
|
|
4226
|
+
});
|
|
4227
|
+
await appendSessionLog(props.sessionStore, sessionCreatedLog(session));
|
|
4228
|
+
return c.json(session, 201);
|
|
4229
|
+
});
|
|
4230
|
+
app.get("/sessions/:sessionId", async (c) => {
|
|
4231
|
+
const session = await props.sessionStore.getSession(c.req.param("sessionId"));
|
|
4232
|
+
if (session === void 0) {
|
|
4233
|
+
return errorResponse(c, 404, "not_found", "Session not found");
|
|
4234
|
+
}
|
|
4235
|
+
return c.json(session);
|
|
4236
|
+
});
|
|
4237
|
+
app.get("/sessions/:sessionId/logs", async (c) => {
|
|
4238
|
+
const sessionId = c.req.param("sessionId");
|
|
4239
|
+
const session = await props.sessionStore.getSession(sessionId);
|
|
4240
|
+
if (session === void 0) {
|
|
4241
|
+
return errorResponse(c, 404, "not_found", "Session not found");
|
|
4242
|
+
}
|
|
4243
|
+
if (props.sessionStore.listSessionLogs === void 0) {
|
|
4244
|
+
return errorResponse(
|
|
4245
|
+
c,
|
|
4246
|
+
501,
|
|
4247
|
+
"unsupported_capability",
|
|
4248
|
+
'Capability "sessions.logs" is not implemented by this runner',
|
|
4249
|
+
{ capability: "sessions", operation: "logs" }
|
|
4250
|
+
);
|
|
4251
|
+
}
|
|
4252
|
+
const limit = parseSessionLogLimit(c.req.query("limit"));
|
|
4253
|
+
if (limit === void 0) {
|
|
4254
|
+
return errorResponse(c, 400, "bad_request", "limit must be a positive integer");
|
|
4255
|
+
}
|
|
4256
|
+
const after = parseSessionLogAfter(c.req.query("after"));
|
|
4257
|
+
if (after === false) {
|
|
4258
|
+
return errorResponse(c, 400, "bad_request", "after must be a non-negative integer");
|
|
4259
|
+
}
|
|
4260
|
+
const logs = await props.sessionStore.listSessionLogs({
|
|
4261
|
+
sessionId,
|
|
4262
|
+
limit,
|
|
4263
|
+
...after === void 0 ? {} : { after }
|
|
4264
|
+
});
|
|
4265
|
+
const last = logs.at(-1);
|
|
4266
|
+
return c.json({
|
|
4267
|
+
logs,
|
|
4268
|
+
...logs.length === limit && last !== void 0 ? { nextCursor: last.sequence } : {}
|
|
4269
|
+
});
|
|
4270
|
+
});
|
|
4271
|
+
app.delete("/sessions/:sessionId", async (c) => {
|
|
4272
|
+
if (props.sessionStore.deleteSession === void 0) {
|
|
4273
|
+
return errorResponse(
|
|
4274
|
+
c,
|
|
4275
|
+
501,
|
|
4276
|
+
"unsupported_capability",
|
|
4277
|
+
'Capability "sessions.delete" is not implemented by this runner',
|
|
4278
|
+
{ capability: "sessions", operation: "delete" }
|
|
4279
|
+
);
|
|
4280
|
+
}
|
|
4281
|
+
const deleted = await props.sessionStore.deleteSession(c.req.param("sessionId"));
|
|
4282
|
+
if (!deleted) {
|
|
4283
|
+
return errorResponse(c, 404, "not_found", "Session not found");
|
|
4284
|
+
}
|
|
4285
|
+
return c.body(null, 204);
|
|
4286
|
+
});
|
|
4287
|
+
app.get("/sessions/:sessionId/traces", async (c) => {
|
|
4288
|
+
if (props.traceStore === void 0) {
|
|
4289
|
+
return unsupportedCapability(c, "traces");
|
|
4290
|
+
}
|
|
4291
|
+
const sessionId = c.req.param("sessionId");
|
|
4292
|
+
const session = await props.sessionStore.getSession(sessionId);
|
|
4293
|
+
if (session === void 0) {
|
|
2785
4294
|
return errorResponse(c, 404, "not_found", "Session not found");
|
|
2786
4295
|
}
|
|
2787
4296
|
const limit = parseLimit(c.req.query("limit"));
|
|
@@ -2792,6 +4301,26 @@ function registerSessionRoutes(app, props) {
|
|
|
2792
4301
|
return c.json({ traces });
|
|
2793
4302
|
});
|
|
2794
4303
|
}
|
|
4304
|
+
function parseSessionLogLimit(value) {
|
|
4305
|
+
if (value === void 0 || value.trim().length === 0) {
|
|
4306
|
+
return 200;
|
|
4307
|
+
}
|
|
4308
|
+
const limit = Number(value);
|
|
4309
|
+
if (!Number.isInteger(limit) || limit <= 0) {
|
|
4310
|
+
return void 0;
|
|
4311
|
+
}
|
|
4312
|
+
return Math.min(limit, 1e3);
|
|
4313
|
+
}
|
|
4314
|
+
function parseSessionLogAfter(value) {
|
|
4315
|
+
if (value === void 0 || value.trim().length === 0) {
|
|
4316
|
+
return void 0;
|
|
4317
|
+
}
|
|
4318
|
+
const after = Number(value);
|
|
4319
|
+
if (!Number.isInteger(after) || after < 0) {
|
|
4320
|
+
return false;
|
|
4321
|
+
}
|
|
4322
|
+
return after;
|
|
4323
|
+
}
|
|
2795
4324
|
async function parseCreateSessionRequest(c) {
|
|
2796
4325
|
let body;
|
|
2797
4326
|
try {
|
|
@@ -2826,6 +4355,47 @@ async function parseCreateSessionRequest(c) {
|
|
|
2826
4355
|
return request;
|
|
2827
4356
|
}
|
|
2828
4357
|
|
|
4358
|
+
// src/runtime/tools.ts
|
|
4359
|
+
function registerToolRoutes(app, props) {
|
|
4360
|
+
app.get("/agents/:agentId/tools", async (c) => {
|
|
4361
|
+
const agentId = c.req.param("agentId");
|
|
4362
|
+
const agent = props.agentMap.get(agentId);
|
|
4363
|
+
if (agent === void 0) {
|
|
4364
|
+
return errorResponse(c, 404, "not_found", "Agent not found");
|
|
4365
|
+
}
|
|
4366
|
+
return c.json({
|
|
4367
|
+
agentId,
|
|
4368
|
+
tools: await agentToolMetadata(agent)
|
|
4369
|
+
});
|
|
4370
|
+
});
|
|
4371
|
+
}
|
|
4372
|
+
async function agentToolMetadata(agent) {
|
|
4373
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4374
|
+
const metadata = [];
|
|
4375
|
+
for (const { tool, source } of agentToolItems(agent)) {
|
|
4376
|
+
const key = `${source}:${tool.name}`;
|
|
4377
|
+
if (seen.has(key)) {
|
|
4378
|
+
continue;
|
|
4379
|
+
}
|
|
4380
|
+
seen.add(key);
|
|
4381
|
+
const definition = await tool.definition("");
|
|
4382
|
+
metadata.push({
|
|
4383
|
+
agentId: agent.id,
|
|
4384
|
+
name: definition.name,
|
|
4385
|
+
description: definition.description,
|
|
4386
|
+
parameters: definition.parameters,
|
|
4387
|
+
source,
|
|
4388
|
+
approval: approvalMetadata(tool)
|
|
4389
|
+
});
|
|
4390
|
+
}
|
|
4391
|
+
return metadata.sort((left, right) => {
|
|
4392
|
+
if (left.source !== right.source) {
|
|
4393
|
+
return left.source === "static" ? -1 : 1;
|
|
4394
|
+
}
|
|
4395
|
+
return left.name.localeCompare(right.name);
|
|
4396
|
+
});
|
|
4397
|
+
}
|
|
4398
|
+
|
|
2829
4399
|
// src/runtime/trace-routes.ts
|
|
2830
4400
|
function registerTraceRoutes(app, traceStore) {
|
|
2831
4401
|
app.get("/traces", async (c) => {
|
|
@@ -2871,8 +4441,8 @@ var Studio = class {
|
|
|
2871
4441
|
studio;
|
|
2872
4442
|
server;
|
|
2873
4443
|
sigintHandler;
|
|
2874
|
-
constructor(
|
|
2875
|
-
this.options =
|
|
4444
|
+
constructor(targets = [], options = {}) {
|
|
4445
|
+
this.options = studioOptionsFromTargets(targets, options);
|
|
2876
4446
|
this.studio = createStudioApp(this.options);
|
|
2877
4447
|
}
|
|
2878
4448
|
get app() {
|
|
@@ -2925,9 +4495,14 @@ var Studio = class {
|
|
|
2925
4495
|
this.studio.close();
|
|
2926
4496
|
}
|
|
2927
4497
|
};
|
|
2928
|
-
function
|
|
4498
|
+
function studioOptionsFromTargets(targets, options) {
|
|
4499
|
+
const agents = targets.filter((target) => target instanceof Agent);
|
|
4500
|
+
const pipelines = targets.filter(
|
|
4501
|
+
(target) => target instanceof Pipeline
|
|
4502
|
+
);
|
|
2929
4503
|
return {
|
|
2930
|
-
agents: inferStudioAgents(agents, options.quickPrompts ?? {})
|
|
4504
|
+
agents: inferStudioAgents(agents, options.quickPrompts ?? {}),
|
|
4505
|
+
pipelines: inferStudioPipelines(pipelines)
|
|
2931
4506
|
};
|
|
2932
4507
|
}
|
|
2933
4508
|
function inferStudioAgents(agents, quickPrompts) {
|
|
@@ -2942,6 +4517,19 @@ function inferStudioAgents(agents, quickPrompts) {
|
|
|
2942
4517
|
};
|
|
2943
4518
|
});
|
|
2944
4519
|
}
|
|
4520
|
+
function inferStudioPipelines(pipelines) {
|
|
4521
|
+
const ids = /* @__PURE__ */ new Set();
|
|
4522
|
+
return pipelines.map((pipeline) => {
|
|
4523
|
+
const id = uniqueAgentId(pipeline.id || "pipeline", ids);
|
|
4524
|
+
return {
|
|
4525
|
+
id,
|
|
4526
|
+
pipeline,
|
|
4527
|
+
...pipeline.name === void 0 ? {} : { name: pipeline.name },
|
|
4528
|
+
...pipeline.description === void 0 ? {} : { description: pipeline.description },
|
|
4529
|
+
...pipeline.metadata === void 0 ? {} : { metadata: pipeline.metadata }
|
|
4530
|
+
};
|
|
4531
|
+
});
|
|
4532
|
+
}
|
|
2945
4533
|
function uniqueAgentId(baseId, ids) {
|
|
2946
4534
|
let id = baseId;
|
|
2947
4535
|
let suffix = 2;
|
|
@@ -2959,6 +4547,7 @@ function agentMetadata(agent) {
|
|
|
2959
4547
|
dynamicContextCount: agent.dynamicContexts.length,
|
|
2960
4548
|
dynamicToolCount: agent.dynamicTools.length,
|
|
2961
4549
|
hasOutputSchema: agent.outputSchema !== void 0,
|
|
4550
|
+
hasHook: agent.hook !== void 0,
|
|
2962
4551
|
observerCount: agent.observers.length,
|
|
2963
4552
|
approvalToolCount: agent.toolSet.values().filter((tool) => tool.approval !== void 0).length
|
|
2964
4553
|
};
|
|
@@ -2966,7 +4555,9 @@ function agentMetadata(agent) {
|
|
|
2966
4555
|
function createStudioApp(options) {
|
|
2967
4556
|
const stores = resolveStores(options);
|
|
2968
4557
|
const agents = normalizeAgents(options.agents).map((agent) => withStudioSessionMemory(agent, stores.sessions)).map((agent) => withStudioTraceObserver(agent, stores.traces));
|
|
4558
|
+
const pipelines = normalizePipelines(options.pipelines);
|
|
2969
4559
|
const agentMap = new Map(agents.map((agent) => [agent.id, agent]));
|
|
4560
|
+
const pipelineMap = new Map(pipelines.map((pipeline) => [pipeline.id, pipeline]));
|
|
2970
4561
|
const approvalRuntime = createApprovalRuntime();
|
|
2971
4562
|
const questionRuntime = createQuestionRuntime();
|
|
2972
4563
|
const app = new HonoApp();
|
|
@@ -2988,7 +4579,7 @@ function createStudioApp(options) {
|
|
|
2988
4579
|
}
|
|
2989
4580
|
})
|
|
2990
4581
|
);
|
|
2991
|
-
app.get("/config", (c) => c.json(buildConfig(options, agents, stores)));
|
|
4582
|
+
app.get("/config", (c) => c.json(buildConfig(options, agents, pipelines, stores)));
|
|
2992
4583
|
app.get("/agents", (c) => c.json({ agents: agents.map(agentConfig) }));
|
|
2993
4584
|
app.get("/agents/:agentId", (c) => {
|
|
2994
4585
|
const agent = agentMap.get(c.req.param("agentId"));
|
|
@@ -2997,12 +4588,19 @@ function createStudioApp(options) {
|
|
|
2997
4588
|
}
|
|
2998
4589
|
return c.json(agentConfig(agent));
|
|
2999
4590
|
});
|
|
4591
|
+
registerMcpRoutes(app, { agentMap });
|
|
4592
|
+
registerToolRoutes(app, { agentMap });
|
|
3000
4593
|
registerApprovalRoutes(app, approvalRuntime);
|
|
3001
4594
|
registerQuestionRoutes(app, questionRuntime);
|
|
3002
4595
|
registerKnowledgeRoutes(app, {
|
|
3003
4596
|
agents,
|
|
3004
4597
|
...stores.traces === void 0 ? {} : { traceStore: stores.traces }
|
|
3005
4598
|
});
|
|
4599
|
+
registerPipelineRoutes(app, {
|
|
4600
|
+
pipelines,
|
|
4601
|
+
pipelineMap,
|
|
4602
|
+
...stores.pipelineLogs === void 0 ? {} : { store: stores.pipelineLogs }
|
|
4603
|
+
});
|
|
3006
4604
|
app.post("/agents/:agentId/runs", async (c) => {
|
|
3007
4605
|
const agentId = c.req.param("agentId");
|
|
3008
4606
|
const agent = agentMap.get(agentId);
|
|
@@ -3024,6 +4622,23 @@ function createStudioApp(options) {
|
|
|
3024
4622
|
return errorResponse(c, 400, "bad_request", "Session belongs to another agent");
|
|
3025
4623
|
}
|
|
3026
4624
|
const runId = globalThis.crypto.randomUUID();
|
|
4625
|
+
const runStartedAt = Date.now();
|
|
4626
|
+
if (session !== void 0) {
|
|
4627
|
+
await appendSessionLog(
|
|
4628
|
+
stores.sessions,
|
|
4629
|
+
runReceivedLog({
|
|
4630
|
+
sessionId: session.id,
|
|
4631
|
+
runId,
|
|
4632
|
+
agentId,
|
|
4633
|
+
message: body.message,
|
|
4634
|
+
stream: body.stream === true,
|
|
4635
|
+
...body.maxTurns === void 0 ? {} : { maxTurns: body.maxTurns },
|
|
4636
|
+
...body.toolConcurrency === void 0 ? {} : { toolConcurrency: body.toolConcurrency },
|
|
4637
|
+
hasTrace: body.trace !== void 0,
|
|
4638
|
+
...body.metadata === void 0 ? {} : { metadata: body.metadata }
|
|
4639
|
+
})
|
|
4640
|
+
);
|
|
4641
|
+
}
|
|
3027
4642
|
const memoryMetadata = {
|
|
3028
4643
|
agentId,
|
|
3029
4644
|
...body.metadata ?? {},
|
|
@@ -3070,7 +4685,13 @@ function createStudioApp(options) {
|
|
|
3070
4685
|
}
|
|
3071
4686
|
const runStream = mergeRunAndApprovalEvents(request.stream(), runtimeEvents);
|
|
3072
4687
|
const stream = session === void 0 || stores.sessions === void 0 ? runStream : persistStreamingSessionTranscript({
|
|
3073
|
-
stream:
|
|
4688
|
+
stream: streamSessionRunLogs({
|
|
4689
|
+
stream: runStream,
|
|
4690
|
+
store: stores.sessions,
|
|
4691
|
+
session,
|
|
4692
|
+
runId,
|
|
4693
|
+
startedAt: runStartedAt
|
|
4694
|
+
}),
|
|
3074
4695
|
store: stores.sessions,
|
|
3075
4696
|
session,
|
|
3076
4697
|
message: body.message,
|
|
@@ -3079,6 +4700,10 @@ function createStudioApp(options) {
|
|
|
3079
4700
|
return streamAgentRunEvents(c, stream);
|
|
3080
4701
|
}
|
|
3081
4702
|
try {
|
|
4703
|
+
if (session !== void 0) {
|
|
4704
|
+
await appendSessionLog(stores.sessions, runStartedLog(session, runId));
|
|
4705
|
+
await appendSessionLog(stores.sessions, memoryLoadedLog(session, runId));
|
|
4706
|
+
}
|
|
3082
4707
|
const effectiveHook = composeHooks(
|
|
3083
4708
|
composeHooks(
|
|
3084
4709
|
agent.agent.hook,
|
|
@@ -3109,6 +4734,25 @@ function createStudioApp(options) {
|
|
|
3109
4734
|
transcript: transcriptFromMessages(response.messages),
|
|
3110
4735
|
status: "success"
|
|
3111
4736
|
});
|
|
4737
|
+
await appendSessionLog(
|
|
4738
|
+
stores.sessions,
|
|
4739
|
+
runCompletedLog({
|
|
4740
|
+
sessionId: session.id,
|
|
4741
|
+
runId,
|
|
4742
|
+
durationMs: Date.now() - runStartedAt,
|
|
4743
|
+
usage: response.usage,
|
|
4744
|
+
output: response.output,
|
|
4745
|
+
messageCount: response.messages.length
|
|
4746
|
+
})
|
|
4747
|
+
);
|
|
4748
|
+
await appendSessionLog(
|
|
4749
|
+
stores.sessions,
|
|
4750
|
+
memorySavedLog({
|
|
4751
|
+
sessionId: session.id,
|
|
4752
|
+
runId,
|
|
4753
|
+
messageCount: response.messages.length
|
|
4754
|
+
})
|
|
4755
|
+
);
|
|
3112
4756
|
}
|
|
3113
4757
|
return c.json(response);
|
|
3114
4758
|
} catch (error) {
|
|
@@ -3125,6 +4769,10 @@ function createStudioApp(options) {
|
|
|
3125
4769
|
status: "error",
|
|
3126
4770
|
error: serializeError2(error)
|
|
3127
4771
|
});
|
|
4772
|
+
await appendSessionLog(
|
|
4773
|
+
stores.sessions,
|
|
4774
|
+
runFailedLog(session.id, runId, error, runStartedAt)
|
|
4775
|
+
);
|
|
3128
4776
|
}
|
|
3129
4777
|
return errorResponse(c, 500, "internal_error", "Agent run failed", serializeError2(error));
|
|
3130
4778
|
}
|
|
@@ -3149,7 +4797,7 @@ function createStudioApp(options) {
|
|
|
3149
4797
|
return app.fetch(request);
|
|
3150
4798
|
},
|
|
3151
4799
|
config() {
|
|
3152
|
-
return buildConfig(options, agents, stores);
|
|
4800
|
+
return buildConfig(options, agents, pipelines, stores);
|
|
3153
4801
|
},
|
|
3154
4802
|
close() {
|
|
3155
4803
|
},
|
|
@@ -3237,6 +4885,9 @@ function composeHooks(first, second) {
|
|
|
3237
4885
|
if (firstAction?.type === "skip" || firstAction?.type === "terminate") {
|
|
3238
4886
|
return firstAction;
|
|
3239
4887
|
}
|
|
4888
|
+
if (firstAction?.type === "approval_request") {
|
|
4889
|
+
return await approvalRequestHandler(second)?.(args, firstAction) ?? firstAction;
|
|
4890
|
+
}
|
|
3240
4891
|
const secondAction = await second.onToolCall?.(args);
|
|
3241
4892
|
return secondAction ?? firstAction ?? void 0;
|
|
3242
4893
|
},
|
|
@@ -3246,6 +4897,10 @@ function composeHooks(first, second) {
|
|
|
3246
4897
|
}
|
|
3247
4898
|
});
|
|
3248
4899
|
}
|
|
4900
|
+
function approvalRequestHandler(hook) {
|
|
4901
|
+
const candidate = hook;
|
|
4902
|
+
return typeof candidate.handleApprovalRequest === "function" ? candidate.handleApprovalRequest : void 0;
|
|
4903
|
+
}
|
|
3249
4904
|
export {
|
|
3250
4905
|
Studio,
|
|
3251
4906
|
StudioTraceObserver,
|