@tonyclaw/agent-inspector 2.1.3 → 2.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-BLYcm6Dg.js → CompareDrawer-SAn2KFl2.js} +1 -1
  3. package/.output/public/assets/ProxyViewerContainer-DaiTFf7n.js +117 -0
  4. package/.output/public/assets/{ReplayDialog-_kCy9L9E.js → ReplayDialog-CCwz0YUV.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-DWp5pWsv.js → RequestAnatomy-DXcC4ydl.js} +1 -1
  6. package/.output/public/assets/{ResponseView-Ci92o6mt.js → ResponseView-Bh9t9aZF.js} +1 -1
  7. package/.output/public/assets/{StreamingChunkSequence-C-1ZQl_b.js → StreamingChunkSequence-B69M2rr-.js} +1 -1
  8. package/.output/public/assets/_sessionId-6fF5JpyQ.js +1 -0
  9. package/.output/public/assets/index-Bcq8bZoK.css +1 -0
  10. package/.output/public/assets/index-DBSbw8Gd.js +1 -0
  11. package/.output/public/assets/{main-RgLvmIQk.js → main-DZVKUeg6.js} +2 -2
  12. package/.output/server/_libs/lucide-react.mjs +204 -173
  13. package/.output/server/_libs/radix-ui__react-dialog.mjs +2 -0
  14. package/.output/server/{_sessionId-DnOYbKO7.mjs → _sessionId-COVUf_EG.mjs} +3 -3
  15. package/.output/server/_ssr/{CompareDrawer-D44_F13_.mjs → CompareDrawer-DqQHO4tk.mjs} +3 -3
  16. package/.output/server/_ssr/{ProxyViewerContainer-Cx4p_awW.mjs → ProxyViewerContainer-BEeF4V-C.mjs} +1006 -75
  17. package/.output/server/_ssr/{ReplayDialog-wScCF3Oc.mjs → ReplayDialog-DUZbsd5x.mjs} +4 -4
  18. package/.output/server/_ssr/{RequestAnatomy-Cf7R7PXS.mjs → RequestAnatomy-DKuIxoBj.mjs} +2 -2
  19. package/.output/server/_ssr/{ResponseView-7pVIZLL-.mjs → ResponseView-BB9CYmTJ.mjs} +3 -3
  20. package/.output/server/_ssr/{StreamingChunkSequence-Bs57_GbC.mjs → StreamingChunkSequence-CkY3uTjF.mjs} +3 -3
  21. package/.output/server/_ssr/{index-6eY__la9.mjs → index-CHTchkrO.mjs} +2 -2
  22. package/.output/server/_ssr/index.mjs +2 -2
  23. package/.output/server/_ssr/{router-DRZPhZB6.mjs → router-C8DJ5pQZ.mjs} +1972 -221
  24. package/.output/server/_tanstack-start-manifest_v-Z1ffcxDr.mjs +4 -0
  25. package/.output/server/index.mjs +68 -68
  26. package/package.json +2 -1
  27. package/src/components/ProxyViewer.tsx +87 -1
  28. package/src/components/ProxyViewerContainer.tsx +26 -0
  29. package/src/components/alerts/AlertsDialog.tsx +610 -0
  30. package/src/components/groups/GroupsDialog.tsx +41 -13
  31. package/src/components/proxy-viewer/LogEntry.tsx +324 -51
  32. package/src/components/ui/dialog.tsx +1 -0
  33. package/src/contracts/index.ts +15 -2
  34. package/src/contracts/log.ts +18 -0
  35. package/src/lib/alertContract.ts +79 -0
  36. package/src/lib/apiClient.ts +39 -0
  37. package/src/lib/export-logs.ts +47 -9
  38. package/src/lib/groupContract.ts +1 -0
  39. package/src/lib/logImportContract.ts +12 -0
  40. package/src/lib/useAlerts.ts +82 -0
  41. package/src/lib/useGroupEvidence.ts +6 -2
  42. package/src/lib/useGroups.ts +6 -2
  43. package/src/proxy/alerts.ts +664 -0
  44. package/src/proxy/logBodyChunks.ts +91 -0
  45. package/src/proxy/logImporter.ts +491 -0
  46. package/src/proxy/logIndex.ts +63 -2
  47. package/src/proxy/sqliteLogIndex.ts +612 -0
  48. package/src/proxy/store.ts +32 -7
  49. package/src/routes/api/alerts.summary.ts +24 -0
  50. package/src/routes/api/alerts.ts +52 -0
  51. package/src/routes/api/logs.$id.body.ts +44 -0
  52. package/src/routes/api/logs.import.ts +50 -0
  53. package/src/services/alerts.ts +12 -0
  54. package/src/services/groups.ts +1 -0
  55. package/.output/public/assets/ProxyViewerContainer-Ba5wFn00.js +0 -117
  56. package/.output/public/assets/_sessionId-DKzAmSNP.js +0 -1
  57. package/.output/public/assets/index-B-MZ9lQM.css +0 -1
  58. package/.output/public/assets/index-ZZHwgkWJ.js +0 -1
  59. package/.output/server/_tanstack-start-manifest_v-DfAVFkZ5.mjs +0 -4
@@ -5,14 +5,14 @@ import { existsSync, readFileSync, mkdirSync, writeFileSync, renameSync, copyFil
5
5
  import fs, { mkdir, appendFile, readFile, writeFile, readdir, open, stat, unlink, rename } from "node:fs/promises";
6
6
  import { createInterface } from "node:readline";
7
7
  import { Buffer } from "node:buffer";
8
- import path, { join, isAbsolute, dirname, resolve, relative } from "node:path";
8
+ import path, { basename, join, isAbsolute, dirname, resolve, relative } from "node:path";
9
9
  import { execFile, exec, spawn } from "node:child_process";
10
10
  import { fileURLToPath } from "node:url";
11
11
  import { C as Conf } from "../_libs/conf.mjs";
12
12
  import { randomUUID } from "crypto";
13
13
  import { promisify } from "node:util";
14
14
  import { Worker } from "node:worker_threads";
15
- import { randomUUID as randomUUID$1 } from "node:crypto";
15
+ import { randomUUID as randomUUID$1, createHash } from "node:crypto";
16
16
  import { M as McpServer, W as WebStandardStreamableHTTPServerTransport, R as ResourceTemplate } from "../_libs/modelcontextprotocol__server.mjs";
17
17
  import { J as JSZip } from "../_libs/jszip.mjs";
18
18
  import { homedir } from "node:os";
@@ -65,8 +65,8 @@ import "../_libs/immediate.mjs";
65
65
  import "../_libs/setimmediate.mjs";
66
66
  import "../_libs/pako.mjs";
67
67
  const faviconSvg = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20role='img'%20aria-label='Agent%20Inspector'%3e%3cg%20fill='none'%20stroke='%23f59e0b'%20stroke-width='1.5'%20stroke-linecap='round'%20stroke-linejoin='round'%20%3e%3cpath%20d='M5%2013%20C5%209%208%207%2012%207%20C16%207%2019%209%2019%2013%20C19%2016%2016%2018%2012%2018%20C8%2018%205%2016%205%2013%20Z'%20/%3e%3cpath%20d='M5%2011%20C3.5%209.5%201.5%2010%202%2012.5%20C2.5%2014%204%2013.5%205%2012.5'%20/%3e%3cpath%20d='M19%2011%20C20.5%209.5%2022.5%2010%2022%2012.5%20C21.5%2014%2020%2013.5%2019%2012.5'%20/%3e%3cline%20x1='10'%20y1='7'%20x2='9.5'%20y2='5'%20/%3e%3cline%20x1='14'%20y1='7'%20x2='14.5'%20y2='5'%20/%3e%3cline%20x1='6.5'%20y1='16'%20x2='4.5'%20y2='19.5'%20/%3e%3cline%20x1='9'%20y1='17.5'%20x2='8'%20y2='20.5'%20/%3e%3cline%20x1='15'%20y1='17.5'%20x2='16'%20y2='20.5'%20/%3e%3cline%20x1='17.5'%20y1='16'%20x2='19.5'%20y2='19.5'%20/%3e%3c/g%3e%3ccircle%20cx='9.5'%20cy='4.5'%20r='0.9'%20fill='%23f59e0b'%20/%3e%3ccircle%20cx='14.5'%20cy='4.5'%20r='0.9'%20fill='%23f59e0b'%20/%3e%3c/svg%3e";
68
- const appCss = "/assets/index-B-MZ9lQM.css";
69
- const Route$z = createRootRoute({
68
+ const appCss = "/assets/index-Bcq8bZoK.css";
69
+ const Route$D = createRootRoute({
70
70
  head: () => ({
71
71
  meta: [
72
72
  { charSet: "utf-8" },
@@ -108,8 +108,8 @@ function RootDocument({ children }) {
108
108
  ] })
109
109
  ] });
110
110
  }
111
- const $$splitComponentImporter$1 = () => import("./index-6eY__la9.mjs");
112
- const Route$y = createFileRoute("/")({
111
+ const $$splitComponentImporter$1 = () => import("./index-CHTchkrO.mjs");
112
+ const Route$C = createFileRoute("/")({
113
113
  component: lazyRouteComponent($$splitComponentImporter$1, "component")
114
114
  });
115
115
  const B64URL_RE = /^[A-Za-z0-9_-]+$/;
@@ -151,8 +151,8 @@ function decodeSessionIdFromPath(encoded) {
151
151
  function getSessionPath(sessionId) {
152
152
  return `/session/${encodeSessionIdForPath(sessionId)}`;
153
153
  }
154
- const $$splitComponentImporter = () => import("../_sessionId-DnOYbKO7.mjs");
155
- const Route$x = createFileRoute("/session/$sessionId")({
154
+ const $$splitComponentImporter = () => import("../_sessionId-COVUf_EG.mjs");
155
+ const Route$B = createFileRoute("/session/$sessionId")({
156
156
  component: lazyRouteComponent($$splitComponentImporter, "component"),
157
157
  parseParams: (params) => ({
158
158
  sessionId: decodeSessionIdFromPath(params.sessionId)
@@ -385,6 +385,19 @@ const StreamingChunksArraySchema = object({
385
385
  chunks: array(StreamingChunkSchema$1),
386
386
  truncated: boolean().optional().default(false)
387
387
  });
388
+ const LogBodyPartSchema = _enum(["request", "response"]);
389
+ const LogBodyChunkSchema = object({
390
+ logId: number().int().positive(),
391
+ part: LogBodyPartSchema,
392
+ text: string(),
393
+ offset: number().int().nonnegative(),
394
+ limit: number().int().positive(),
395
+ totalBytes: number().int().nonnegative(),
396
+ textBytes: number().int().nonnegative(),
397
+ nextOffset: number().int().nonnegative().nullable(),
398
+ hasMore: boolean(),
399
+ contentMode: _enum(["empty", "partial", "full"])
400
+ });
388
401
  const CapturedLogSchema = object({
389
402
  id: number(),
390
403
  timestamp: string(),
@@ -851,12 +864,530 @@ function readChunks(path2) {
851
864
  return null;
852
865
  }
853
866
  }
867
+ const SQLITE_INDEX_FILE = "inspector.sqlite";
868
+ const SQLITE_PACKAGE_NAME = "better-sqlite3";
869
+ const BUN_SQLITE_MODULE_NAME = "bun:sqlite";
870
+ const CREATE_SCHEMA_SQL = `
871
+ CREATE TABLE IF NOT EXISTS log_index (
872
+ id INTEGER PRIMARY KEY,
873
+ file TEXT NOT NULL,
874
+ byte_offset INTEGER NOT NULL,
875
+ byte_length INTEGER NOT NULL,
876
+ session_id TEXT,
877
+ model TEXT,
878
+ timestamp TEXT NOT NULL,
879
+ method TEXT NOT NULL,
880
+ path TEXT NOT NULL,
881
+ response_status INTEGER,
882
+ input_tokens INTEGER,
883
+ output_tokens INTEGER,
884
+ cache_creation_input_tokens INTEGER,
885
+ cache_read_input_tokens INTEGER,
886
+ elapsed_ms INTEGER,
887
+ first_chunk_ms INTEGER,
888
+ total_stream_ms INTEGER,
889
+ tokens_per_second REAL,
890
+ streaming INTEGER NOT NULL,
891
+ user_agent TEXT,
892
+ origin TEXT,
893
+ api_format TEXT NOT NULL,
894
+ is_test INTEGER NOT NULL,
895
+ replay_of_log_id INTEGER,
896
+ provider_name TEXT,
897
+ client_port INTEGER,
898
+ client_pid INTEGER,
899
+ client_cwd TEXT,
900
+ client_project_folder TEXT,
901
+ streaming_chunks_path TEXT,
902
+ raw_request_body_bytes INTEGER,
903
+ response_text_bytes INTEGER,
904
+ warnings_json TEXT,
905
+ error TEXT
906
+ );
907
+ CREATE INDEX IF NOT EXISTS idx_log_index_session_id_id ON log_index(session_id, id);
908
+ CREATE INDEX IF NOT EXISTS idx_log_index_model_id ON log_index(model, id);
909
+ CREATE INDEX IF NOT EXISTS idx_log_index_timestamp ON log_index(timestamp);
910
+ `;
911
+ const UPSERT_SQL = `
912
+ INSERT INTO log_index (
913
+ id,
914
+ file,
915
+ byte_offset,
916
+ byte_length,
917
+ session_id,
918
+ model,
919
+ timestamp,
920
+ method,
921
+ path,
922
+ response_status,
923
+ input_tokens,
924
+ output_tokens,
925
+ cache_creation_input_tokens,
926
+ cache_read_input_tokens,
927
+ elapsed_ms,
928
+ first_chunk_ms,
929
+ total_stream_ms,
930
+ tokens_per_second,
931
+ streaming,
932
+ user_agent,
933
+ origin,
934
+ api_format,
935
+ is_test,
936
+ replay_of_log_id,
937
+ provider_name,
938
+ client_port,
939
+ client_pid,
940
+ client_cwd,
941
+ client_project_folder,
942
+ streaming_chunks_path,
943
+ raw_request_body_bytes,
944
+ response_text_bytes,
945
+ warnings_json,
946
+ error
947
+ ) VALUES (
948
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
949
+ )
950
+ ON CONFLICT(id) DO UPDATE SET
951
+ file = excluded.file,
952
+ byte_offset = excluded.byte_offset,
953
+ byte_length = excluded.byte_length,
954
+ session_id = excluded.session_id,
955
+ model = excluded.model,
956
+ timestamp = excluded.timestamp,
957
+ method = excluded.method,
958
+ path = excluded.path,
959
+ response_status = excluded.response_status,
960
+ input_tokens = excluded.input_tokens,
961
+ output_tokens = excluded.output_tokens,
962
+ cache_creation_input_tokens = excluded.cache_creation_input_tokens,
963
+ cache_read_input_tokens = excluded.cache_read_input_tokens,
964
+ elapsed_ms = excluded.elapsed_ms,
965
+ first_chunk_ms = excluded.first_chunk_ms,
966
+ total_stream_ms = excluded.total_stream_ms,
967
+ tokens_per_second = excluded.tokens_per_second,
968
+ streaming = excluded.streaming,
969
+ user_agent = excluded.user_agent,
970
+ origin = excluded.origin,
971
+ api_format = excluded.api_format,
972
+ is_test = excluded.is_test,
973
+ replay_of_log_id = excluded.replay_of_log_id,
974
+ provider_name = excluded.provider_name,
975
+ client_port = excluded.client_port,
976
+ client_pid = excluded.client_pid,
977
+ client_cwd = excluded.client_cwd,
978
+ client_project_folder = excluded.client_project_folder,
979
+ streaming_chunks_path = excluded.streaming_chunks_path,
980
+ raw_request_body_bytes = excluded.raw_request_body_bytes,
981
+ response_text_bytes = excluded.response_text_bytes,
982
+ warnings_json = excluded.warnings_json,
983
+ error = excluded.error
984
+ `;
985
+ let sqliteStatePromise = null;
986
+ let sqliteStatePath = null;
987
+ let activeDb = null;
988
+ let didLogUnavailable = false;
989
+ function isSqliteLogIndexEnabled() {
990
+ const configured = process.env["AGENT_INSPECTOR_SQLITE_INDEX"];
991
+ if (configured === "1") return true;
992
+ if (configured === "0") return false;
993
+ return process.env["NODE_ENV"] !== "test";
994
+ }
995
+ function getSqlitePath() {
996
+ return join(resolveLogDir(), SQLITE_INDEX_FILE);
997
+ }
998
+ function objectFromUnknown(value) {
999
+ if (value === null) return null;
1000
+ if (typeof value === "object" || typeof value === "function") return value;
1001
+ return null;
1002
+ }
1003
+ function readProperty$1(value, name) {
1004
+ let current = objectFromUnknown(value);
1005
+ while (current !== null) {
1006
+ const desc = Object.getOwnPropertyDescriptor(current, name);
1007
+ if (desc !== void 0) {
1008
+ const propertyValue = Reflect.get(current, name);
1009
+ return propertyValue;
1010
+ }
1011
+ const nextPrototype = Object.getPrototypeOf(current);
1012
+ current = objectFromUnknown(nextPrototype);
1013
+ }
1014
+ return void 0;
1015
+ }
1016
+ function readFunction(value, name) {
1017
+ const property = readProperty$1(value, name);
1018
+ return typeof property === "function" ? property : null;
1019
+ }
1020
+ function callMethod(target, methodName, args) {
1021
+ const method = readFunction(target, methodName);
1022
+ if (method === null) return void 0;
1023
+ try {
1024
+ return Reflect.apply(method, target, [...args]);
1025
+ } catch (err) {
1026
+ logger.warn("[sqliteLogIndex] SQLite method failed:", methodName, String(err));
1027
+ return void 0;
1028
+ }
1029
+ }
1030
+ function prepareStatement(db, sql) {
1031
+ return callMethod(db, "prepare", [sql]);
1032
+ }
1033
+ function runPrepared(db, sql, params = []) {
1034
+ const statement = prepareStatement(db, sql);
1035
+ if (statement === void 0) return false;
1036
+ const result = callMethod(statement, "run", params);
1037
+ callMethod(statement, "finalize", []);
1038
+ return result !== void 0;
1039
+ }
1040
+ function getPrepared(db, sql, params = []) {
1041
+ const statement = prepareStatement(db, sql);
1042
+ if (statement === void 0) return void 0;
1043
+ const result = callMethod(statement, "get", params);
1044
+ callMethod(statement, "finalize", []);
1045
+ return result;
1046
+ }
1047
+ function allPrepared(db, sql, params = []) {
1048
+ const statement = prepareStatement(db, sql);
1049
+ if (statement === void 0) return null;
1050
+ const result = callMethod(statement, "all", params);
1051
+ callMethod(statement, "finalize", []);
1052
+ return Array.isArray(result) ? result : null;
1053
+ }
1054
+ function execSql(db, sql) {
1055
+ const result = callMethod(db, "exec", [sql]);
1056
+ return result !== void 0;
1057
+ }
1058
+ function sqliteConstructorFromModule(moduleValue, exportName) {
1059
+ const exportedValue = readProperty$1(moduleValue, exportName);
1060
+ if (typeof exportedValue === "function") {
1061
+ return (path2) => Reflect.construct(exportedValue, [path2]);
1062
+ }
1063
+ return null;
1064
+ }
1065
+ async function loadSqliteModuleConstructor(moduleName, exportName) {
1066
+ try {
1067
+ const importedModule = await import(
1068
+ /* @vite-ignore */
1069
+ moduleName
1070
+ );
1071
+ const exportedConstructor = sqliteConstructorFromModule(importedModule, exportName);
1072
+ if (exportedConstructor !== null) return { constructor: exportedConstructor, reason: null };
1073
+ if (exportName === "default" && typeof importedModule === "function") {
1074
+ return {
1075
+ constructor: (path2) => Reflect.construct(importedModule, [path2]),
1076
+ reason: null
1077
+ };
1078
+ }
1079
+ return { constructor: null, reason: `${moduleName} did not expose ${exportName}` };
1080
+ } catch (err) {
1081
+ return { constructor: null, reason: String(err) };
1082
+ }
1083
+ }
1084
+ async function loadSqliteConstructor() {
1085
+ const bunRuntimeDesc = Object.getOwnPropertyDescriptor(process.versions, "bun");
1086
+ if (bunRuntimeDesc !== void 0) {
1087
+ const bunSqlite2 = await loadSqliteModuleConstructor(BUN_SQLITE_MODULE_NAME, "Database");
1088
+ if (bunSqlite2.constructor !== null) return bunSqlite2;
1089
+ const betterSqlite2 = await loadSqliteModuleConstructor(SQLITE_PACKAGE_NAME, "default");
1090
+ if (betterSqlite2.constructor !== null) return betterSqlite2;
1091
+ const reason2 = [bunSqlite2.reason, betterSqlite2.reason].filter((item) => item !== null).join("; ");
1092
+ if (!didLogUnavailable) {
1093
+ didLogUnavailable = true;
1094
+ logger.warn("[sqliteLogIndex] Optional SQLite index unavailable:", reason2);
1095
+ }
1096
+ return { constructor: null, reason: reason2 };
1097
+ }
1098
+ const betterSqlite = await loadSqliteModuleConstructor(SQLITE_PACKAGE_NAME, "default");
1099
+ if (betterSqlite.constructor !== null) return betterSqlite;
1100
+ const bunSqlite = await loadSqliteModuleConstructor(BUN_SQLITE_MODULE_NAME, "Database");
1101
+ if (bunSqlite.constructor !== null) return bunSqlite;
1102
+ const reason = [betterSqlite.reason, bunSqlite.reason].filter((item) => item !== null).join("; ");
1103
+ if (!didLogUnavailable) {
1104
+ didLogUnavailable = true;
1105
+ logger.warn("[sqliteLogIndex] Optional SQLite index unavailable:", reason);
1106
+ }
1107
+ return { constructor: null, reason };
1108
+ }
1109
+ async function initializeSqliteState() {
1110
+ const path2 = getSqlitePath();
1111
+ if (!isSqliteLogIndexEnabled()) {
1112
+ return { status: "unavailable", path: path2, reason: "SQLite log index is disabled" };
1113
+ }
1114
+ const sqliteConstructor = await loadSqliteConstructor();
1115
+ if (sqliteConstructor.constructor === null) {
1116
+ return {
1117
+ status: "unavailable",
1118
+ path: path2,
1119
+ reason: sqliteConstructor.reason ?? "SQLite runtime is not available"
1120
+ };
1121
+ }
1122
+ try {
1123
+ await mkdir(dirname(path2), { recursive: true });
1124
+ const db = sqliteConstructor.constructor(path2);
1125
+ execSql(db, "PRAGMA journal_mode = WAL");
1126
+ execSql(db, "PRAGMA synchronous = NORMAL");
1127
+ if (!execSql(db, CREATE_SCHEMA_SQL)) {
1128
+ return { status: "unavailable", path: path2, reason: "SQLite schema initialization failed" };
1129
+ }
1130
+ activeDb = db;
1131
+ return { status: "ready", db, path: path2 };
1132
+ } catch (err) {
1133
+ logger.warn("[sqliteLogIndex] Failed to initialize SQLite index:", String(err));
1134
+ return { status: "unavailable", path: path2, reason: String(err) };
1135
+ }
1136
+ }
1137
+ async function getSqliteState() {
1138
+ const path2 = getSqlitePath();
1139
+ if (sqliteStatePromise !== null && sqliteStatePath !== path2) {
1140
+ closeSqliteLogIndex();
1141
+ }
1142
+ if (sqliteStatePromise === null) {
1143
+ sqliteStatePath = path2;
1144
+ sqliteStatePromise = initializeSqliteState();
1145
+ }
1146
+ return await sqliteStatePromise;
1147
+ }
1148
+ function closeSqliteLogIndex() {
1149
+ if (activeDb !== null) {
1150
+ callMethod(activeDb, "close", []);
1151
+ }
1152
+ activeDb = null;
1153
+ sqliteStatePromise = null;
1154
+ sqliteStatePath = null;
1155
+ }
1156
+ function warningJson(warnings) {
1157
+ return warnings === void 0 ? null : JSON.stringify(warnings);
1158
+ }
1159
+ function boolToInt(value) {
1160
+ return value ? 1 : 0;
1161
+ }
1162
+ function entryParams(entry) {
1163
+ const summary = entry.summary;
1164
+ return [
1165
+ entry.id,
1166
+ entry.file,
1167
+ entry.byteOffset,
1168
+ entry.byteLength,
1169
+ entry.sessionId ?? summary?.sessionId ?? null,
1170
+ entry.model ?? summary?.model ?? null,
1171
+ summary?.timestamp ?? "",
1172
+ summary?.method ?? "",
1173
+ summary?.path ?? "",
1174
+ summary?.responseStatus ?? null,
1175
+ summary?.inputTokens ?? null,
1176
+ summary?.outputTokens ?? null,
1177
+ summary?.cacheCreationInputTokens ?? null,
1178
+ summary?.cacheReadInputTokens ?? null,
1179
+ summary?.elapsedMs ?? null,
1180
+ summary?.firstChunkMs ?? null,
1181
+ summary?.totalStreamMs ?? null,
1182
+ summary?.tokensPerSecond ?? null,
1183
+ boolToInt(summary?.streaming ?? false),
1184
+ summary?.userAgent ?? null,
1185
+ summary?.origin ?? null,
1186
+ summary?.apiFormat ?? "unknown",
1187
+ boolToInt(summary?.isTest ?? false),
1188
+ summary?.replayOfLogId ?? null,
1189
+ summary?.providerName ?? null,
1190
+ summary?.clientPort ?? null,
1191
+ summary?.clientPid ?? null,
1192
+ summary?.clientCwd ?? null,
1193
+ summary?.clientProjectFolder ?? null,
1194
+ summary?.streamingChunksPath ?? null,
1195
+ summary?.rawRequestBodyBytes ?? null,
1196
+ summary?.responseTextBytes ?? null,
1197
+ warningJson(summary?.warnings),
1198
+ summary?.error ?? null
1199
+ ];
1200
+ }
1201
+ function readNumber(row, name) {
1202
+ const value = readProperty$1(row, name);
1203
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
1204
+ }
1205
+ function readString$1(row, name) {
1206
+ const value = readProperty$1(row, name);
1207
+ return typeof value === "string" ? value : null;
1208
+ }
1209
+ function readBoolean(row, name) {
1210
+ return readNumber(row, name) === 1;
1211
+ }
1212
+ function readApiFormat(row) {
1213
+ const value = readString$1(row, "api_format");
1214
+ switch (value) {
1215
+ case "anthropic":
1216
+ return "anthropic";
1217
+ case "openai":
1218
+ return "openai";
1219
+ case "unknown":
1220
+ case null:
1221
+ return "unknown";
1222
+ default:
1223
+ return "unknown";
1224
+ }
1225
+ }
1226
+ function readWarnings(row) {
1227
+ const value = readString$1(row, "warnings_json");
1228
+ if (value === null) return void 0;
1229
+ try {
1230
+ const parsed = JSON.parse(value);
1231
+ if (!Array.isArray(parsed)) return void 0;
1232
+ const warnings = [];
1233
+ for (const item of parsed) {
1234
+ if (typeof item !== "string") return void 0;
1235
+ warnings.push(item);
1236
+ }
1237
+ return warnings;
1238
+ } catch {
1239
+ return void 0;
1240
+ }
1241
+ }
1242
+ function readRequiredNumber(row, name) {
1243
+ const value = readNumber(row, name);
1244
+ return value === null || !Number.isInteger(value) ? null : value;
1245
+ }
1246
+ function summaryFromRow(row, id) {
1247
+ return {
1248
+ id,
1249
+ timestamp: readString$1(row, "timestamp") ?? "",
1250
+ method: readString$1(row, "method") ?? "",
1251
+ path: readString$1(row, "path") ?? "",
1252
+ model: readString$1(row, "model"),
1253
+ sessionId: readString$1(row, "session_id"),
1254
+ responseStatus: readNumber(row, "response_status"),
1255
+ inputTokens: readNumber(row, "input_tokens"),
1256
+ outputTokens: readNumber(row, "output_tokens"),
1257
+ cacheCreationInputTokens: readNumber(row, "cache_creation_input_tokens"),
1258
+ cacheReadInputTokens: readNumber(row, "cache_read_input_tokens"),
1259
+ elapsedMs: readNumber(row, "elapsed_ms"),
1260
+ firstChunkMs: readNumber(row, "first_chunk_ms"),
1261
+ totalStreamMs: readNumber(row, "total_stream_ms"),
1262
+ tokensPerSecond: readNumber(row, "tokens_per_second"),
1263
+ streaming: readBoolean(row, "streaming"),
1264
+ userAgent: readString$1(row, "user_agent"),
1265
+ origin: readString$1(row, "origin"),
1266
+ apiFormat: readApiFormat(row),
1267
+ isTest: readBoolean(row, "is_test"),
1268
+ replayOfLogId: readNumber(row, "replay_of_log_id"),
1269
+ providerName: readString$1(row, "provider_name"),
1270
+ clientPort: readNumber(row, "client_port"),
1271
+ clientPid: readNumber(row, "client_pid"),
1272
+ clientCwd: readString$1(row, "client_cwd"),
1273
+ clientProjectFolder: readString$1(row, "client_project_folder"),
1274
+ streamingChunksPath: readString$1(row, "streaming_chunks_path"),
1275
+ rawRequestBodyBytes: readNumber(row, "raw_request_body_bytes"),
1276
+ responseTextBytes: readNumber(row, "response_text_bytes"),
1277
+ warnings: readWarnings(row),
1278
+ error: readString$1(row, "error")
1279
+ };
1280
+ }
1281
+ function entryFromRow(row) {
1282
+ const id = readRequiredNumber(row, "id");
1283
+ const file = readString$1(row, "file");
1284
+ const byteOffset = readRequiredNumber(row, "byte_offset");
1285
+ const byteLength = readRequiredNumber(row, "byte_length");
1286
+ if (id === null || file === null || byteOffset === null || byteLength === null) return null;
1287
+ return {
1288
+ id,
1289
+ file,
1290
+ byteOffset,
1291
+ byteLength,
1292
+ sessionId: readString$1(row, "session_id"),
1293
+ model: readString$1(row, "model"),
1294
+ summary: summaryFromRow(row, id)
1295
+ };
1296
+ }
1297
+ async function getSqliteLogIndexMaxId() {
1298
+ const state = await getSqliteState();
1299
+ if (state.status !== "ready") return null;
1300
+ const row = getPrepared(state.db, "SELECT MAX(id) AS max_id FROM log_index");
1301
+ return readNumber(row, "max_id") ?? 0;
1302
+ }
1303
+ async function upsertSqliteLogIndexEntry(entry) {
1304
+ if (entry.summary === void 0) return false;
1305
+ const state = await getSqliteState();
1306
+ if (state.status !== "ready") return false;
1307
+ return runPrepared(state.db, UPSERT_SQL, entryParams(entry));
1308
+ }
1309
+ async function syncSqliteLogIndexEntries(entries) {
1310
+ const state = await getSqliteState();
1311
+ if (state.status !== "ready") return false;
1312
+ if (entries.length === 0) return true;
1313
+ if (!execSql(state.db, "BEGIN IMMEDIATE")) return false;
1314
+ try {
1315
+ for (const entry of entries) {
1316
+ if (entry.summary === void 0) continue;
1317
+ if (!runPrepared(state.db, UPSERT_SQL, entryParams(entry))) {
1318
+ execSql(state.db, "ROLLBACK");
1319
+ return false;
1320
+ }
1321
+ }
1322
+ return execSql(state.db, "COMMIT");
1323
+ } catch (err) {
1324
+ execSql(state.db, "ROLLBACK");
1325
+ logger.warn("[sqliteLogIndex] Failed to sync SQLite index:", String(err));
1326
+ return false;
1327
+ }
1328
+ }
1329
+ async function findSqliteLogIndexEntry(id) {
1330
+ const state = await getSqliteState();
1331
+ if (state.status !== "ready") return null;
1332
+ const row = getPrepared(state.db, "SELECT * FROM log_index WHERE id = ?", [id]);
1333
+ return entryFromRow(row);
1334
+ }
1335
+ async function listSqliteLogIndexEntries({
1336
+ sessionId,
1337
+ model
1338
+ } = {}) {
1339
+ const state = await getSqliteState();
1340
+ if (state.status !== "ready") return null;
1341
+ const clauses = [];
1342
+ const params = [];
1343
+ if (sessionId !== void 0) {
1344
+ clauses.push("session_id = ?");
1345
+ params.push(sessionId);
1346
+ }
1347
+ if (model !== void 0) {
1348
+ clauses.push("model = ?");
1349
+ params.push(model);
1350
+ }
1351
+ const where = clauses.length === 0 ? "" : ` WHERE ${clauses.join(" AND ")}`;
1352
+ const rows = allPrepared(state.db, `SELECT * FROM log_index${where} ORDER BY id ASC`, params);
1353
+ if (rows === null) return null;
1354
+ const entries = [];
1355
+ for (const row of rows) {
1356
+ const entry = entryFromRow(row);
1357
+ if (entry !== null) entries.push(entry);
1358
+ }
1359
+ return entries;
1360
+ }
1361
+ async function replaceSqliteLogIndexEntries(entries) {
1362
+ const state = await getSqliteState();
1363
+ if (state.status !== "ready") return false;
1364
+ if (!execSql(state.db, "BEGIN IMMEDIATE")) return false;
1365
+ try {
1366
+ if (!runPrepared(state.db, "DELETE FROM log_index")) {
1367
+ execSql(state.db, "ROLLBACK");
1368
+ return false;
1369
+ }
1370
+ for (const entry of entries) {
1371
+ if (entry.summary === void 0) continue;
1372
+ if (!runPrepared(state.db, UPSERT_SQL, entryParams(entry))) {
1373
+ execSql(state.db, "ROLLBACK");
1374
+ return false;
1375
+ }
1376
+ }
1377
+ return execSql(state.db, "COMMIT");
1378
+ } catch (err) {
1379
+ execSql(state.db, "ROLLBACK");
1380
+ logger.warn("[sqliteLogIndex] Failed to replace SQLite index:", String(err));
1381
+ return false;
1382
+ }
1383
+ }
854
1384
  const INDEX_VERSION = 1;
855
1385
  const INDEX_FILE = "logs.idx";
856
1386
  function getIndexPath() {
857
1387
  return join(resolveLogDir(), INDEX_FILE);
858
1388
  }
859
1389
  let cachedIndex = null;
1390
+ let sqliteBackfillScheduledMaxId = 0;
860
1391
  function textByteLength$2(value) {
861
1392
  return value === null ? null : Buffer.byteLength(value, "utf8");
862
1393
  }
@@ -950,10 +1481,12 @@ async function saveIndex(index) {
950
1481
  }
951
1482
  async function addToIndex(id, file, byteOffset, byteLength, metadata) {
952
1483
  const index = await loadIndex();
953
- index.entries[id] = metadata === void 0 ? { id, file, byteOffset, byteLength } : { id, file, byteOffset, byteLength, ...metadata };
1484
+ const entry = metadata === void 0 ? { id, file, byteOffset, byteLength } : { id, file, byteOffset, byteLength, ...metadata };
1485
+ index.entries[id] = entry;
954
1486
  if (id > index.maxId) {
955
1487
  index.maxId = id;
956
1488
  }
1489
+ void upsertSqliteLogIndexEntry(entry);
957
1490
  scheduleIndexFlush();
958
1491
  }
959
1492
  let indexFlushScheduled = false;
@@ -981,13 +1514,50 @@ async function flushIndex() {
981
1514
  await saveIndex(index);
982
1515
  }
983
1516
  async function findInIndex(id) {
1517
+ const sqliteEntry = await findSqliteLogIndexEntry(id);
1518
+ if (sqliteEntry !== null) return sqliteEntry;
984
1519
  const index = await loadIndex();
985
1520
  return index.entries[id] ?? null;
986
1521
  }
987
1522
  async function listIndexEntries() {
988
1523
  const index = await loadIndex();
1524
+ const sqliteMaxId = await getSqliteLogIndexMaxId();
1525
+ if (sqliteMaxId !== null && sqliteMaxId >= index.maxId) {
1526
+ const sqliteEntries = await listSqliteLogIndexEntries();
1527
+ if (sqliteEntries !== null) return sqliteEntries;
1528
+ }
1529
+ maybeBackfillSqliteIndex(index, sqliteMaxId);
989
1530
  return Object.values(index.entries).sort((left, right) => left.id - right.id);
990
1531
  }
1532
+ async function listFilteredIndexEntries({
1533
+ sessionId,
1534
+ model
1535
+ }) {
1536
+ const index = await loadIndex();
1537
+ const sqliteMaxId = await getSqliteLogIndexMaxId();
1538
+ if (sqliteMaxId !== null && sqliteMaxId >= index.maxId) {
1539
+ const sqliteEntries = await listSqliteLogIndexEntries({ sessionId, model });
1540
+ if (sqliteEntries !== null) return sqliteEntries;
1541
+ }
1542
+ maybeBackfillSqliteIndex(index, sqliteMaxId);
1543
+ return Object.values(index.entries).sort((left, right) => left.id - right.id).filter((entry) => {
1544
+ if (sessionId !== void 0 && entry.sessionId !== sessionId) return false;
1545
+ if (model !== void 0 && entry.model !== model) return false;
1546
+ return true;
1547
+ });
1548
+ }
1549
+ function maybeBackfillSqliteIndex(index, sqliteMaxId) {
1550
+ if (sqliteMaxId === null) return;
1551
+ if (index.maxId <= sqliteMaxId) return;
1552
+ if (index.maxId <= sqliteBackfillScheduledMaxId) return;
1553
+ sqliteBackfillScheduledMaxId = index.maxId;
1554
+ const entries = Object.values(index.entries);
1555
+ void syncSqliteLogIndexEntries(entries).then((ok) => {
1556
+ if (!ok && sqliteMaxId < sqliteBackfillScheduledMaxId) {
1557
+ sqliteBackfillScheduledMaxId = sqliteMaxId;
1558
+ }
1559
+ });
1560
+ }
991
1561
  function readStringOrNullField(entry, field) {
992
1562
  const desc = Object.getOwnPropertyDescriptor(entry, field);
993
1563
  if (desc === void 0) return null;
@@ -1113,6 +1683,7 @@ async function rebuildIndex() {
1113
1683
  if (!existsSync(logDir)) {
1114
1684
  cachedIndex = newIndex;
1115
1685
  await saveIndex(newIndex);
1686
+ await replaceSqliteLogIndexEntries([]);
1116
1687
  return newIndex;
1117
1688
  }
1118
1689
  const files = (await readdir(logDir)).filter((f) => f.endsWith(".jsonl")).sort();
@@ -1127,6 +1698,7 @@ async function rebuildIndex() {
1127
1698
  }
1128
1699
  cachedIndex = newIndex;
1129
1700
  await saveIndex(newIndex);
1701
+ await replaceSqliteLogIndexEntries(Object.values(newIndex.entries));
1130
1702
  return newIndex;
1131
1703
  }
1132
1704
  let idGenerationPromise = null;
@@ -1155,7 +1727,8 @@ async function getNextLogId() {
1155
1727
  await acquireLock();
1156
1728
  try {
1157
1729
  const index = await loadIndex();
1158
- const nextId = index.maxId + 1;
1730
+ const sqliteMaxId = await getSqliteLogIndexMaxId();
1731
+ const nextId = Math.max(index.maxId, sqliteMaxId ?? 0) + 1;
1159
1732
  index.maxId = nextId;
1160
1733
  cachedIndex = index;
1161
1734
  return nextId;
@@ -1624,7 +2197,7 @@ function rememberLogSession(log) {
1624
2197
  knownLogSessions.set(log.id, sessionId);
1625
2198
  return true;
1626
2199
  }
1627
- function isFailedLog(log) {
2200
+ function isFailedLog$1(log) {
1628
2201
  const hasError = log.error !== null && log.error !== void 0 && log.error.length > 0;
1629
2202
  const hasErrorStatus = log.responseStatus !== null && log.responseStatus >= 400;
1630
2203
  return hasError || hasErrorStatus;
@@ -1648,7 +2221,7 @@ function markSessionFinished(log, sourceOverride) {
1648
2221
  completedLogIds.add(log.id);
1649
2222
  session.completedRequests += 1;
1650
2223
  }
1651
- if (isFailedLog(log) && failedLogIds.has(log.id) === false) {
2224
+ if (isFailedLog$1(log) && failedLogIds.has(log.id) === false) {
1652
2225
  failedLogIds.add(log.id);
1653
2226
  session.failedRequests += 1;
1654
2227
  }
@@ -1798,8 +2371,8 @@ function compactLogForList(log) {
1798
2371
  rawHeaders: void 0,
1799
2372
  headers: void 0,
1800
2373
  streamingChunks: void 0,
1801
- rawRequestBodyBytes: textByteLength$1(log.rawRequestBody),
1802
- responseTextBytes: textByteLength$1(log.responseText),
2374
+ rawRequestBodyBytes: log.rawRequestBodyBytes ?? textByteLength$1(log.rawRequestBody),
2375
+ responseTextBytes: log.responseTextBytes ?? textByteLength$1(log.responseText),
1803
2376
  bodyContentMode: "compact"
1804
2377
  };
1805
2378
  }
@@ -1874,6 +2447,19 @@ async function addTestLogEntry(entry) {
1874
2447
  emitLogUpdate(log);
1875
2448
  return log;
1876
2449
  }
2450
+ async function importCapturedLogs(entries, sessionId) {
2451
+ const imported = [];
2452
+ for (const entry of entries) {
2453
+ const log = await addTestLogEntry({
2454
+ ...entry,
2455
+ sessionId,
2456
+ isTest: false,
2457
+ replayOfLogId: null
2458
+ });
2459
+ imported.push(log);
2460
+ }
2461
+ return imported;
2462
+ }
1877
2463
  async function createLog(method, path2, requestBody, headers, clientInfo, rawHeaders, upstreamHeaders, apiFormat = "unknown", model = null, sessionId = null, preAcquiredId) {
1878
2464
  const userAgent = headers.get("user-agent");
1879
2465
  const origin = headers.get("origin");
@@ -2164,10 +2750,10 @@ function matchesLogIndexEntryFilters(entry, sessionId, model) {
2164
2750
  return true;
2165
2751
  }
2166
2752
  async function buildLogCursorPageIndexFromLogIndex(options, allowRebuild) {
2167
- let entries = await listIndexEntries();
2753
+ let entries = await listFilteredIndexEntries(options);
2168
2754
  if (entries.length === 0 && allowRebuild) {
2169
2755
  await rebuildIndex();
2170
- entries = await listIndexEntries();
2756
+ entries = await listFilteredIndexEntries(options);
2171
2757
  }
2172
2758
  if (entries.some((entry) => !hasLogIndexFilterMetadata(entry))) {
2173
2759
  if (!allowRebuild) return null;
@@ -2325,7 +2911,7 @@ async function collectSessionLogSummaries(sessionId, includeHistory) {
2325
2911
  return [...summariesById.values()];
2326
2912
  }
2327
2913
  async function collectIndexedSessionLogSummaries(sessionId) {
2328
- let entries = await listIndexEntries();
2914
+ let entries = await listFilteredIndexEntries({ sessionId });
2329
2915
  if (entries.length === 0) return [];
2330
2916
  const needsMetadataRebuild = entries.some((entry) => !hasLogIndexFilterMetadata(entry));
2331
2917
  const needsSummaryRebuild = entries.some((entry) => {
@@ -2334,7 +2920,7 @@ async function collectIndexedSessionLogSummaries(sessionId) {
2334
2920
  });
2335
2921
  if (needsMetadataRebuild || needsSummaryRebuild) {
2336
2922
  await rebuildIndex();
2337
- entries = await listIndexEntries();
2923
+ entries = await listFilteredIndexEntries({ sessionId });
2338
2924
  }
2339
2925
  const summaries = [];
2340
2926
  for (const entry of entries) {
@@ -2430,6 +3016,7 @@ function clearAllLogs() {
2430
3016
  memoryCache.clear();
2431
3017
  clearLogCursorPageIndexes();
2432
3018
  clearSessionRegistry();
3019
+ closeSqliteLogIndex();
2433
3020
  return { cleared: count };
2434
3021
  }
2435
3022
  async function deleteMatchingFiles(dir, shouldDelete) {
@@ -2460,7 +3047,7 @@ async function clearPersistedLogStorage() {
2460
3047
  const [logFilesDeleted, chunkFilesDeleted] = await Promise.all([
2461
3048
  deleteMatchingFiles(
2462
3049
  resolveLogDir(),
2463
- (fileName) => fileName.endsWith(".jsonl") || fileName === "logs.idx"
3050
+ (fileName) => fileName.endsWith(".jsonl") || fileName === "logs.idx" || fileName === "inspector.sqlite" || fileName.startsWith("inspector.sqlite-")
2464
3051
  ),
2465
3052
  deleteMatchingFiles(
2466
3053
  getChunksDir(),
@@ -4604,7 +5191,7 @@ function firstChunkMsFromChunks(chunks) {
4604
5191
  if (!Number.isFinite(first.timestamp) || first.timestamp < 0) return null;
4605
5192
  return Math.floor(first.timestamp);
4606
5193
  }
4607
- function parseJson(raw) {
5194
+ function parseJson$1(raw) {
4608
5195
  if (raw === null) return { ok: false };
4609
5196
  try {
4610
5197
  return { ok: true, value: JSON.parse(raw) };
@@ -4963,8 +5550,8 @@ function uniqueWarnings(warnings) {
4963
5550
  return unique2;
4964
5551
  }
4965
5552
  function analyzeToolSchemaWarnings(input) {
4966
- const request = parseJson(input.rawRequestBody);
4967
- const response = parseJson(input.responseText);
5553
+ const request = parseJson$1(input.rawRequestBody);
5554
+ const response = parseJson$1(input.responseText);
4968
5555
  if (!request.ok || !response.ok) return [];
4969
5556
  const toolSchemas = extractToolSchemas(request.value, input.apiFormat);
4970
5557
  const toolCalls = extractToolCalls(response.value, input.apiFormat);
@@ -5794,7 +6381,7 @@ async function handleProxy(req) {
5794
6381
  }
5795
6382
  return handleStreamingResponse(upstreamRes, req, startTime, upstreamUrl, log, captureFullDetails);
5796
6383
  }
5797
- const Route$w = createFileRoute("/proxy/$")({
6384
+ const Route$A = createFileRoute("/proxy/$")({
5798
6385
  server: {
5799
6386
  handlers: {
5800
6387
  GET: ({ request }) => handleProxy(request),
@@ -5806,7 +6393,7 @@ const Route$w = createFileRoute("/proxy/$")({
5806
6393
  }
5807
6394
  }
5808
6395
  });
5809
- function parsePositiveInt$2(value) {
6396
+ function parsePositiveInt$4(value) {
5810
6397
  if (value === null) return void 0;
5811
6398
  const parsed = Number(value);
5812
6399
  if (!Number.isInteger(parsed) || parsed < 1) return void 0;
@@ -5815,7 +6402,7 @@ function parsePositiveInt$2(value) {
5815
6402
  function parseBooleanFlag(value) {
5816
6403
  return value === "1" || value === "true";
5817
6404
  }
5818
- const Route$v = createFileRoute("/api/sessions")({
6405
+ const Route$z = createFileRoute("/api/sessions")({
5819
6406
  server: {
5820
6407
  handlers: {
5821
6408
  GET: async ({ request }) => {
@@ -5828,7 +6415,7 @@ const Route$v = createFileRoute("/api/sessions")({
5828
6415
  const info = await getSessionInfo(sessionId, {
5829
6416
  baseUrl: url.origin,
5830
6417
  includeHistory: parseBooleanFlag(url.searchParams.get("includeHistory")),
5831
- latestLogLimit: parsePositiveInt$2(url.searchParams.get("limit"))
6418
+ latestLogLimit: parsePositiveInt$4(url.searchParams.get("limit"))
5832
6419
  });
5833
6420
  if (info === null) {
5834
6421
  return Response.json({ error: "Session not found" }, { status: 404 });
@@ -6571,6 +7158,7 @@ const InspectorGroupEvidenceSummarySchema = object({
6571
7158
  const GroupEvidenceReadResponseSchema = object({
6572
7159
  group: InspectorGroupSchema,
6573
7160
  evidence: InspectorGroupEvidenceSchema,
7161
+ members: array(InspectorGroupExportMemberSchema).nullable(),
6574
7162
  markdown: string(),
6575
7163
  summary: InspectorGroupEvidenceSummarySchema.nullable(),
6576
7164
  report: EvidenceReportSchema.nullable()
@@ -7246,19 +7834,19 @@ async function readJsonBody$6(request) {
7246
7834
  return { ok: false };
7247
7835
  }
7248
7836
  }
7249
- function parsePositiveInt$1(value, fallback) {
7837
+ function parsePositiveInt$3(value, fallback) {
7250
7838
  if (value === null) return fallback;
7251
7839
  const parsed = Number(value);
7252
7840
  if (!Number.isInteger(parsed) || parsed < 1) return fallback;
7253
7841
  return parsed;
7254
7842
  }
7255
- const Route$u = createFileRoute("/api/runs")({
7843
+ const Route$y = createFileRoute("/api/runs")({
7256
7844
  server: {
7257
7845
  handlers: {
7258
7846
  GET: ({ request }) => {
7259
7847
  const url = new URL(request.url);
7260
7848
  if (url.searchParams.get("failures") === "1") {
7261
- const limit = parsePositiveInt$1(
7849
+ const limit = parsePositiveInt$3(
7262
7850
  url.searchParams.get("limit"),
7263
7851
  RECENT_FAILURES_DEFAULT_LIMIT
7264
7852
  );
@@ -7297,7 +7885,7 @@ const ProviderInputSchema = object({
7297
7885
  modelMetadata: array(ProviderModelMetadataSchema).optional(),
7298
7886
  source: _enum(["company", "personal"]).optional()
7299
7887
  });
7300
- const Route$t = createFileRoute("/api/providers")({
7888
+ const Route$x = createFileRoute("/api/providers")({
7301
7889
  server: {
7302
7890
  handlers: {
7303
7891
  GET: ({ request }) => {
@@ -7331,14 +7919,14 @@ const Route$t = createFileRoute("/api/providers")({
7331
7919
  }
7332
7920
  }
7333
7921
  });
7334
- const Route$s = createFileRoute("/api/models")({
7922
+ const Route$w = createFileRoute("/api/models")({
7335
7923
  server: {
7336
7924
  handlers: {
7337
7925
  GET: () => Response.json(getModels())
7338
7926
  }
7339
7927
  }
7340
7928
  });
7341
- const version = "2.1.3";
7929
+ const version = "2.1.5";
7342
7930
  const packageJson = {
7343
7931
  version
7344
7932
  };
@@ -7483,7 +8071,7 @@ function isCompletedMember(member) {
7483
8071
  const status = memberEffectiveStatus(member);
7484
8072
  return status === "completed";
7485
8073
  }
7486
- function isFailedMember(member) {
8074
+ function isFailedMember$1(member) {
7487
8075
  const status = memberEffectiveStatus(member);
7488
8076
  return status === "failed" || status === "cancelled";
7489
8077
  }
@@ -7527,7 +8115,7 @@ function buildSummary(members) {
7527
8115
  members.flatMap((member) => [member.member.provider, ...member.session?.providers ?? []])
7528
8116
  ),
7529
8117
  completedMembers: members.filter(isCompletedMember).length,
7530
- failedMembers: members.filter(isFailedMember).length,
8118
+ failedMembers: members.filter(isFailedMember$1).length,
7531
8119
  runningMembers: members.filter(isRunningMember).length
7532
8120
  };
7533
8121
  }
@@ -7764,6 +8352,7 @@ function readInspectorGroupEvidence(groupId) {
7764
8352
  return serviceOk({
7765
8353
  group,
7766
8354
  evidence: group.evidence,
8355
+ members: document?.members ?? null,
7767
8356
  markdown,
7768
8357
  summary: document?.summary ?? null,
7769
8358
  report: document?.jenkinsReport ?? null
@@ -9839,7 +10428,7 @@ const PROMPT_NAMES = [
9839
10428
  "inspector_generate_group_report",
9840
10429
  "inspector_extract_repro_steps"
9841
10430
  ];
9842
- const Route$r = createFileRoute("/api/mcp")({
10431
+ const Route$v = createFileRoute("/api/mcp")({
9843
10432
  server: {
9844
10433
  handlers: {
9845
10434
  // The SDK may issue either POST, GET, or DELETE. TanStack Start only
@@ -9855,15 +10444,15 @@ const LOG_SEARCH_DEFAULT_LIMIT = 10;
9855
10444
  const LOG_SEARCH_MAX_LIMIT = 50;
9856
10445
  const LOG_SEARCH_DEFAULT_SCAN_LIMIT = 200;
9857
10446
  const LOG_SEARCH_MAX_SCAN_LIMIT = 1e3;
9858
- function clampPositiveInt(value, fallback, max) {
10447
+ function clampPositiveInt$1(value, fallback, max) {
9859
10448
  if (!Number.isInteger(value) || value < 1) return fallback;
9860
10449
  return Math.min(value, max);
9861
10450
  }
9862
10451
  function clampLogSearchLimit(value) {
9863
- return clampPositiveInt(value, LOG_SEARCH_DEFAULT_LIMIT, LOG_SEARCH_MAX_LIMIT);
10452
+ return clampPositiveInt$1(value, LOG_SEARCH_DEFAULT_LIMIT, LOG_SEARCH_MAX_LIMIT);
9864
10453
  }
9865
10454
  function clampLogSearchScanLimit(value) {
9866
- return clampPositiveInt(value, LOG_SEARCH_DEFAULT_SCAN_LIMIT, LOG_SEARCH_MAX_SCAN_LIMIT);
10455
+ return clampPositiveInt$1(value, LOG_SEARCH_DEFAULT_SCAN_LIMIT, LOG_SEARCH_MAX_SCAN_LIMIT);
9867
10456
  }
9868
10457
  function queryTerms(query) {
9869
10458
  return query.trim().toLowerCase().split(/\s+/).filter((term) => term.length > 0);
@@ -9944,7 +10533,7 @@ function parseNonNegativeInt(value, fallback) {
9944
10533
  if (!Number.isInteger(parsed) || parsed < 0) return fallback;
9945
10534
  return parsed;
9946
10535
  }
9947
- function parsePositiveInt(value, fallback) {
10536
+ function parsePositiveInt$2(value, fallback) {
9948
10537
  if (value === null) return fallback;
9949
10538
  const parsed = Number(value);
9950
10539
  if (!Number.isInteger(parsed) || parsed < 1) return fallback;
@@ -9956,7 +10545,7 @@ function parseOptionalPositiveInt(value) {
9956
10545
  if (!Number.isInteger(parsed) || parsed < 1) return { ok: false };
9957
10546
  return { ok: true, value: parsed };
9958
10547
  }
9959
- const Route$q = createFileRoute("/api/logs")({
10548
+ const Route$u = createFileRoute("/api/logs")({
9960
10549
  server: {
9961
10550
  handlers: {
9962
10551
  GET: async ({ request }) => {
@@ -9978,8 +10567,8 @@ const Route$q = createFileRoute("/api/logs")({
9978
10567
  const query = url.searchParams.get("q") ?? url.searchParams.get("search");
9979
10568
  const offset = parseNonNegativeInt(url.searchParams.get("offset"), 0);
9980
10569
  if (query !== null) {
9981
- const limit2 = parsePositiveInt(url.searchParams.get("limit"), LOG_SEARCH_DEFAULT_LIMIT);
9982
- const scanLimit = parsePositiveInt(
10570
+ const limit2 = parsePositiveInt$2(url.searchParams.get("limit"), LOG_SEARCH_DEFAULT_LIMIT);
10571
+ const scanLimit = parsePositiveInt$2(
9983
10572
  url.searchParams.get("scanLimit"),
9984
10573
  LOG_SEARCH_DEFAULT_SCAN_LIMIT
9985
10574
  );
@@ -9987,7 +10576,7 @@ const Route$q = createFileRoute("/api/logs")({
9987
10576
  await searchLogsPage({ query, sessionId, model, offset, limit: limit2, scanLimit })
9988
10577
  );
9989
10578
  }
9990
- const limit = parsePositiveInt(url.searchParams.get("limit"), 50);
10579
+ const limit = parsePositiveInt$2(url.searchParams.get("limit"), 50);
9991
10580
  const includeBodies = url.searchParams.get("compact") !== "1";
9992
10581
  if (url.searchParams.get("cursor") === "1" || url.searchParams.get("cursor") === "true") {
9993
10582
  const beforeLogId = parseOptionalPositiveInt(url.searchParams.get("beforeLogId"));
@@ -10057,7 +10646,7 @@ const Route$q = createFileRoute("/api/logs")({
10057
10646
  }
10058
10647
  });
10059
10648
  logger.debug("Health endpoint loaded");
10060
- const Route$p = createFileRoute("/api/health")({
10649
+ const Route$t = createFileRoute("/api/health")({
10061
10650
  server: {
10062
10651
  handlers: {
10063
10652
  GET: () => {
@@ -10075,7 +10664,7 @@ async function readJsonBody$5(request) {
10075
10664
  return { ok: false };
10076
10665
  }
10077
10666
  }
10078
- const Route$o = createFileRoute("/api/groups")({
10667
+ const Route$s = createFileRoute("/api/groups")({
10079
10668
  server: {
10080
10669
  handlers: {
10081
10670
  GET: () => Response.json(listInspectorGroups()),
@@ -10105,7 +10694,7 @@ const RuntimeConfigPatchSchema = object({
10105
10694
  }).strict().refine((v) => Object.keys(v).length > 0, {
10106
10695
  message: "At least one field must be provided"
10107
10696
  });
10108
- const Route$n = createFileRoute("/api/config")({
10697
+ const Route$r = createFileRoute("/api/config")({
10109
10698
  server: {
10110
10699
  handlers: {
10111
10700
  GET: () => {
@@ -10134,107 +10723,703 @@ const Route$n = createFileRoute("/api/config")({
10134
10723
  }
10135
10724
  }
10136
10725
  });
10137
- async function readJsonBody$4(request) {
10138
- try {
10139
- const raw = await request.text();
10140
- if (raw.trim().length === 0) return { ok: true, value: {} };
10141
- return { ok: true, value: JSON.parse(raw) };
10142
- } catch {
10143
- return { ok: false };
10144
- }
10145
- }
10146
- const Route$m = createFileRoute("/api/runs/$runId")({
10147
- server: {
10148
- handlers: {
10149
- GET: ({ params }) => {
10150
- const run = getInspectorRun(params.runId);
10151
- if (!run.ok) {
10152
- return Response.json({ error: run.message }, { status: run.status });
10153
- }
10154
- return Response.json(run.value);
10155
- },
10156
- PATCH: async ({ params, request }) => {
10157
- const body = await readJsonBody$4(request);
10158
- if (!body.ok) {
10159
- return Response.json({ error: "Invalid JSON body" }, { status: 400 });
10160
- }
10161
- const parsed = UpdateInspectorRunInputSchema.safeParse(body.value);
10162
- if (!parsed.success) {
10163
- return Response.json(
10164
- { error: "Invalid request body", details: parsed.error.format() },
10165
- { status: 400 }
10166
- );
10167
- }
10168
- const updated = updateInspectorRun(params.runId, parsed.data);
10169
- if (!updated.ok) {
10170
- return Response.json({ error: updated.message }, { status: updated.status });
10171
- }
10172
- return Response.json(updated.value);
10173
- }
10174
- }
10175
- }
10726
+ const AlertSeveritySchema = _enum(["critical", "warning", "notice"]);
10727
+ const AlertStatusSchema = _enum(["open"]);
10728
+ const AlertSourceSchema = _enum(["proxy", "tool-schema", "run", "group"]);
10729
+ const AlertCategorySchema = _enum([
10730
+ "request-failure",
10731
+ "request-timeout",
10732
+ "slow-response",
10733
+ "tool-schema",
10734
+ "run-failure",
10735
+ "group-failure",
10736
+ "group-incomplete"
10737
+ ]);
10738
+ const AlertEvidenceKindSchema = _enum(["log", "session", "run", "group"]);
10739
+ const AlertApiFormatSchema = _enum(["anthropic", "openai", "unknown"]);
10740
+ const AlertEvidenceLinkSchema = object({
10741
+ kind: AlertEvidenceKindSchema,
10742
+ id: string(),
10743
+ label: string(),
10744
+ href: string().nullable()
10176
10745
  });
10177
- const ZHIPU_OPENAI_BASE_URL = "https://open.bigmodel.cn/api/paas/v4";
10178
- const ZHIPU_CODING_OPENAI_BASE_URL = "https://open.bigmodel.cn/api/coding/paas/v4";
10179
- const ZHIPU_CODING_ANTHROPIC_BASE_URL = "https://open.bigmodel.cn/api/anthropic";
10180
- function readJsonSafe(filePath) {
10181
- try {
10182
- if (!existsSync(filePath)) return null;
10183
- const raw = readFileSync(filePath, "utf-8");
10184
- const parsed = JSON.parse(raw);
10185
- return isPlainRecord(parsed) ? parsed : null;
10186
- } catch {
10187
- return null;
10746
+ const AlertSchema = object({
10747
+ id: string(),
10748
+ fingerprint: string(),
10749
+ severity: AlertSeveritySchema,
10750
+ category: AlertCategorySchema,
10751
+ source: AlertSourceSchema,
10752
+ status: AlertStatusSchema,
10753
+ title: string(),
10754
+ message: string(),
10755
+ count: number().int().positive(),
10756
+ firstSeenAt: string(),
10757
+ lastSeenAt: string(),
10758
+ logIds: array(number().int().positive()),
10759
+ sessionIds: array(string()),
10760
+ runIds: array(string()),
10761
+ groupIds: array(string()),
10762
+ provider: string().nullable(),
10763
+ model: string().nullable(),
10764
+ apiFormat: AlertApiFormatSchema.nullable(),
10765
+ evidence: array(AlertEvidenceLinkSchema),
10766
+ metadata: record(string(), JsonValueSchema)
10767
+ });
10768
+ const AlertCategoryCountSchema = object({
10769
+ category: AlertCategorySchema,
10770
+ count: number().int().nonnegative()
10771
+ });
10772
+ const AlertSummarySchema = object({
10773
+ total: number().int().nonnegative(),
10774
+ open: number().int().nonnegative(),
10775
+ critical: number().int().nonnegative(),
10776
+ warning: number().int().nonnegative(),
10777
+ notice: number().int().nonnegative(),
10778
+ byCategory: array(AlertCategoryCountSchema)
10779
+ });
10780
+ const AlertListResponseSchema = object({
10781
+ alerts: array(AlertSchema),
10782
+ total: number().int().nonnegative(),
10783
+ limit: number().int().positive(),
10784
+ summary: AlertSummarySchema
10785
+ });
10786
+ const DEFAULT_ALERT_LIMIT = 50;
10787
+ const MAX_ALERT_LIMIT = 200;
10788
+ const DEFAULT_ALERT_SCAN_LIMIT = 500;
10789
+ const MAX_ALERT_SCAN_LIMIT = 2e3;
10790
+ function clampPositiveInt(value, fallback, max) {
10791
+ if (value === void 0) return fallback;
10792
+ if (!Number.isInteger(value) || value < 1) return fallback;
10793
+ return Math.min(value, max);
10794
+ }
10795
+ function clampAlertLimit(value) {
10796
+ return clampPositiveInt(value, DEFAULT_ALERT_LIMIT, MAX_ALERT_LIMIT);
10797
+ }
10798
+ function clampAlertScanLimit(value) {
10799
+ return clampPositiveInt(value, DEFAULT_ALERT_SCAN_LIMIT, MAX_ALERT_SCAN_LIMIT);
10800
+ }
10801
+ function alertIdFor(value) {
10802
+ const digest = createHash("sha1").update(value).digest("hex").slice(0, 16);
10803
+ return `alert_${digest}`;
10804
+ }
10805
+ function sanitizeFingerprintPart(value) {
10806
+ if (value === null) return "-";
10807
+ return String(value).trim().toLowerCase().replace(/\s+/g, " ").slice(0, 240);
10808
+ }
10809
+ function fingerprint(parts) {
10810
+ return parts.map(sanitizeFingerprintPart).join("|");
10811
+ }
10812
+ function uniqueStrings$1(values) {
10813
+ return [...new Set(values)].sort();
10814
+ }
10815
+ function uniqueNumbers(values) {
10816
+ return [...new Set(values)].sort((left, right) => left - right);
10817
+ }
10818
+ function logEvidence(logId) {
10819
+ const id = String(logId);
10820
+ return { kind: "log", id, label: `Log #${id}`, href: `#log-${id}` };
10821
+ }
10822
+ function sessionEvidence(sessionId) {
10823
+ return {
10824
+ kind: "session",
10825
+ id: sessionId,
10826
+ label: `Session ${sessionId}`,
10827
+ href: getSessionPath(sessionId)
10828
+ };
10829
+ }
10830
+ function runEvidence(runId) {
10831
+ return { kind: "run", id: runId, label: `Run ${runId}`, href: null };
10832
+ }
10833
+ function groupEvidence(groupId) {
10834
+ return { kind: "group", id: groupId, label: `Group ${groupId}`, href: null };
10835
+ }
10836
+ function baseEvidence(logId, sessionId, runId, groupId) {
10837
+ const links = [];
10838
+ if (logId !== null) links.push(logEvidence(logId));
10839
+ if (sessionId !== null) links.push(sessionEvidence(sessionId));
10840
+ if (runId !== null) links.push(runEvidence(runId));
10841
+ if (groupId !== null) links.push(groupEvidence(groupId));
10842
+ return links;
10843
+ }
10844
+ function hasEquivalentEvidence(links, candidate) {
10845
+ return links.some((link) => link.kind === candidate.kind && link.id === candidate.id);
10846
+ }
10847
+ function mergeEvidence(left, right) {
10848
+ const merged = [...left];
10849
+ for (const link of right) {
10850
+ if (!hasEquivalentEvidence(merged, link)) merged.push(link);
10188
10851
  }
10852
+ return merged;
10189
10853
  }
10190
- function getEnvValue(envObj, key) {
10191
- if (!isPlainRecord(envObj)) return void 0;
10192
- const val = envObj[key];
10193
- return typeof val === "string" ? val.trim() : void 0;
10854
+ function firstString(left, right) {
10855
+ return left ?? right;
10194
10856
  }
10195
- const KNOWN_PROVIDER_NAMES = {
10196
- deepseek: "DeepSeek",
10197
- minimaxi: "MiniMax",
10198
- openai: "OpenAI",
10199
- anthropic: "Anthropic",
10200
- openrouter: "OpenRouter",
10201
- groq: "Groq",
10202
- together: "Together",
10203
- fireworks: "Fireworks",
10204
- bedrock: "Bedrock",
10205
- vertexai: "Vertex AI",
10206
- azure: "Azure",
10207
- moonshot: "Moonshot",
10208
- zhipu: "ZhipuAI",
10209
- zhipuai: "ZhipuAI",
10210
- bigmodel: "ZhipuAI",
10211
- zai: "Z.AI",
10212
- qwen: "Qwen",
10213
- baichuan: "Baichuan",
10214
- iflytek: "iFlytek",
10215
- doubao: "Doubao",
10216
- lingyiwanwu: "LingyiWanwu",
10217
- stepfun: "StepFun"
10218
- };
10219
- function deriveNameFromUrl(url) {
10220
- try {
10221
- const hostname = new URL(url).hostname;
10222
- const parts = hostname.split(".");
10223
- const domain = parts.length >= 2 ? parts[parts.length - 2] ?? hostname : hostname;
10224
- const domainLower = domain.toLowerCase();
10225
- if (KNOWN_PROVIDER_NAMES[domainLower] !== void 0) {
10226
- return KNOWN_PROVIDER_NAMES[domainLower];
10227
- }
10228
- return domain.charAt(0).toUpperCase() + domain.slice(1);
10229
- } catch {
10230
- return "Imported Provider";
10857
+ function mergeAlert(existing, observation) {
10858
+ const logIds = observation.logId === null ? existing.logIds : [...existing.logIds, observation.logId];
10859
+ const sessionIds = observation.sessionId === null ? existing.sessionIds : [...existing.sessionIds, observation.sessionId];
10860
+ const runIds = observation.runId === null ? existing.runIds : [...existing.runIds, observation.runId];
10861
+ const groupIds = observation.groupId === null ? existing.groupIds : [...existing.groupIds, observation.groupId];
10862
+ return {
10863
+ ...existing,
10864
+ count: existing.count + 1,
10865
+ firstSeenAt: observation.observedAt < existing.firstSeenAt ? observation.observedAt : existing.firstSeenAt,
10866
+ lastSeenAt: observation.observedAt > existing.lastSeenAt ? observation.observedAt : existing.lastSeenAt,
10867
+ logIds: uniqueNumbers(logIds),
10868
+ sessionIds: uniqueStrings$1(sessionIds),
10869
+ runIds: uniqueStrings$1(runIds),
10870
+ groupIds: uniqueStrings$1(groupIds),
10871
+ provider: firstString(existing.provider, observation.provider),
10872
+ model: firstString(existing.model, observation.model),
10873
+ apiFormat: existing.apiFormat ?? observation.apiFormat,
10874
+ evidence: mergeEvidence(existing.evidence, observation.evidence),
10875
+ metadata: { ...existing.metadata, ...observation.metadata }
10876
+ };
10877
+ }
10878
+ function observationToAlert(observation) {
10879
+ return {
10880
+ id: alertIdFor(observation.fingerprint),
10881
+ fingerprint: observation.fingerprint,
10882
+ severity: observation.severity,
10883
+ category: observation.category,
10884
+ source: observation.source,
10885
+ status: "open",
10886
+ title: observation.title,
10887
+ message: observation.message,
10888
+ count: 1,
10889
+ firstSeenAt: observation.observedAt,
10890
+ lastSeenAt: observation.observedAt,
10891
+ logIds: observation.logId === null ? [] : [observation.logId],
10892
+ sessionIds: observation.sessionId === null ? [] : [observation.sessionId],
10893
+ runIds: observation.runId === null ? [] : [observation.runId],
10894
+ groupIds: observation.groupId === null ? [] : [observation.groupId],
10895
+ provider: observation.provider,
10896
+ model: observation.model,
10897
+ apiFormat: observation.apiFormat,
10898
+ evidence: observation.evidence,
10899
+ metadata: observation.metadata
10900
+ };
10901
+ }
10902
+ function addObservation(map, observation) {
10903
+ const existing = map.get(observation.fingerprint);
10904
+ if (existing === void 0) {
10905
+ map.set(observation.fingerprint, observationToAlert(observation));
10906
+ return;
10231
10907
  }
10908
+ map.set(observation.fingerprint, mergeAlert(existing, observation));
10232
10909
  }
10233
- function detectClaudeCodeProviders() {
10234
- const home = homedir();
10235
- const results = [];
10236
- const globalConfig = readJsonSafe(join(home, ".claude", "settings.json"));
10237
- const localConfig = readJsonSafe(join(home, ".claude", "settings.local.json"));
10910
+ function lowerText(value) {
10911
+ if (value === null || value === void 0) return "";
10912
+ return value.toLowerCase();
10913
+ }
10914
+ function isTimeoutStatus(status) {
10915
+ return status === 408 || status === 504;
10916
+ }
10917
+ function isTimeoutLog(log) {
10918
+ const text = `${lowerText(log.error)} ${lowerText(log.responseText)}`;
10919
+ return isTimeoutStatus(log.responseStatus) || text.includes("timeout") || text.includes("timed out") || text.includes("abort");
10920
+ }
10921
+ function hasFailureStatus(status) {
10922
+ return status !== null && status >= 400;
10923
+ }
10924
+ function isFailedLog(log) {
10925
+ return log.error !== null && log.error !== void 0 && log.error.length > 0 ? true : hasFailureStatus(log.responseStatus);
10926
+ }
10927
+ function statusLabel(status) {
10928
+ return status === null ? "no status" : `HTTP ${String(status)}`;
10929
+ }
10930
+ function errorDetail(log) {
10931
+ if (log.error !== null && log.error !== void 0 && log.error.length > 0) return log.error;
10932
+ return statusLabel(log.responseStatus);
10933
+ }
10934
+ function requestFailureTitle(log, timeout) {
10935
+ if (log.isTest === true && timeout) return "Provider test timed out";
10936
+ if (log.isTest === true) return "Provider test failed";
10937
+ return timeout ? "Request timed out" : "Request failed";
10938
+ }
10939
+ function requestFailureCategory(timeout) {
10940
+ return timeout ? "request-timeout" : "request-failure";
10941
+ }
10942
+ function logProvider(log) {
10943
+ return log.providerName ?? null;
10944
+ }
10945
+ function requestFailureObservation(log) {
10946
+ if (!isFailedLog(log)) return null;
10947
+ const timeout = isTimeoutLog(log);
10948
+ const category = requestFailureCategory(timeout);
10949
+ const message = `${errorDetail(log)} while calling ${log.path}.`;
10950
+ return {
10951
+ fingerprint: fingerprint([
10952
+ "proxy",
10953
+ category,
10954
+ logProvider(log),
10955
+ log.model,
10956
+ log.responseStatus,
10957
+ errorDetail(log)
10958
+ ]),
10959
+ severity: "critical",
10960
+ category,
10961
+ source: "proxy",
10962
+ title: requestFailureTitle(log, timeout),
10963
+ message,
10964
+ observedAt: log.timestamp,
10965
+ logId: log.id,
10966
+ sessionId: log.sessionId,
10967
+ runId: null,
10968
+ groupId: null,
10969
+ provider: logProvider(log),
10970
+ model: log.model,
10971
+ apiFormat: log.apiFormat,
10972
+ evidence: baseEvidence(log.id, log.sessionId, null, null),
10973
+ metadata: {
10974
+ path: log.path,
10975
+ status: log.responseStatus,
10976
+ error: log.error ?? null,
10977
+ isTest: log.isTest,
10978
+ streaming: log.streaming
10979
+ }
10980
+ };
10981
+ }
10982
+ function slowResponseObservation(log, slowResponseThresholdSeconds) {
10983
+ if (slowResponseThresholdSeconds <= 0) return null;
10984
+ if (isFailedLog(log)) return null;
10985
+ if (log.elapsedMs === null) return null;
10986
+ const thresholdMs = slowResponseThresholdSeconds * 1e3;
10987
+ if (log.elapsedMs < thresholdMs) return null;
10988
+ const elapsedSeconds = (log.elapsedMs / 1e3).toFixed(1);
10989
+ return {
10990
+ fingerprint: fingerprint(["proxy", "slow-response", logProvider(log), log.model, log.path]),
10991
+ severity: "warning",
10992
+ category: "slow-response",
10993
+ source: "proxy",
10994
+ title: "Slow model response",
10995
+ message: `Response took ${elapsedSeconds}s, above the ${String(
10996
+ slowResponseThresholdSeconds
10997
+ )}s threshold.`,
10998
+ observedAt: log.timestamp,
10999
+ logId: log.id,
11000
+ sessionId: log.sessionId,
11001
+ runId: null,
11002
+ groupId: null,
11003
+ provider: logProvider(log),
11004
+ model: log.model,
11005
+ apiFormat: log.apiFormat,
11006
+ evidence: baseEvidence(log.id, log.sessionId, null, null),
11007
+ metadata: {
11008
+ path: log.path,
11009
+ elapsedMs: log.elapsedMs,
11010
+ thresholdMs,
11011
+ streaming: log.streaming,
11012
+ firstChunkMs: log.firstChunkMs ?? null,
11013
+ totalStreamMs: log.totalStreamMs ?? null
11014
+ }
11015
+ };
11016
+ }
11017
+ function warningObservation(log, warning) {
11018
+ return {
11019
+ fingerprint: fingerprint(["tool-schema", logProvider(log), log.model, warning]),
11020
+ severity: "warning",
11021
+ category: "tool-schema",
11022
+ source: "tool-schema",
11023
+ title: "Tool calling argument warning",
11024
+ message: warning,
11025
+ observedAt: log.timestamp,
11026
+ logId: log.id,
11027
+ sessionId: log.sessionId,
11028
+ runId: null,
11029
+ groupId: null,
11030
+ provider: logProvider(log),
11031
+ model: log.model,
11032
+ apiFormat: log.apiFormat,
11033
+ evidence: baseEvidence(log.id, log.sessionId, null, null),
11034
+ metadata: {
11035
+ path: log.path,
11036
+ warning
11037
+ }
11038
+ };
11039
+ }
11040
+ function addLogObservations(map, logs, slowResponseThresholdSeconds) {
11041
+ for (const log of logs) {
11042
+ const failure = requestFailureObservation(log);
11043
+ if (failure !== null) addObservation(map, failure);
11044
+ const slow = slowResponseObservation(log, slowResponseThresholdSeconds);
11045
+ if (slow !== null) addObservation(map, slow);
11046
+ for (const warning of log.warnings ?? []) {
11047
+ addObservation(map, warningObservation(log, warning));
11048
+ }
11049
+ }
11050
+ }
11051
+ function alertSeverityFromFailure(failure) {
11052
+ switch (failure.classification.severity) {
11053
+ case "high":
11054
+ return "critical";
11055
+ case "medium":
11056
+ case "low":
11057
+ return "warning";
11058
+ case "none":
11059
+ return "notice";
11060
+ }
11061
+ }
11062
+ function providerFromRun(failure) {
11063
+ const provider = failure.run.metadata["provider"] ?? failure.run.metadata["providerName"];
11064
+ return typeof provider === "string" && provider.length > 0 ? provider : null;
11065
+ }
11066
+ function modelFromRun(failure) {
11067
+ const model = failure.run.metadata["model"] ?? failure.run.metadata["modelName"];
11068
+ return typeof model === "string" && model.length > 0 ? model : null;
11069
+ }
11070
+ function runFailureObservation(failure) {
11071
+ const groupId = failure.run.groupId;
11072
+ const logEvidenceLinks = failure.classification.evidenceLogIds.map(logEvidence);
11073
+ const links = mergeEvidence(
11074
+ baseEvidence(null, failure.sessionId, failure.run.id, groupId),
11075
+ logEvidenceLinks
11076
+ );
11077
+ return {
11078
+ fingerprint: fingerprint(["run", failure.run.id, failure.classification.category]),
11079
+ severity: alertSeverityFromFailure(failure),
11080
+ category: "run-failure",
11081
+ source: "run",
11082
+ title: `Run ${failure.run.status}`,
11083
+ message: failure.classification.summary,
11084
+ observedAt: failure.updatedAt,
11085
+ logId: failure.classification.evidenceLogIds[0] ?? null,
11086
+ sessionId: failure.sessionId,
11087
+ runId: failure.run.id,
11088
+ groupId,
11089
+ provider: providerFromRun(failure),
11090
+ model: modelFromRun(failure),
11091
+ apiFormat: null,
11092
+ evidence: links,
11093
+ metadata: {
11094
+ runStatus: failure.run.status,
11095
+ failureCategory: failure.classification.category,
11096
+ evidenceMarkdownPath: failure.evidenceMarkdownPath
11097
+ }
11098
+ };
11099
+ }
11100
+ function addRunFailureObservations(map, recentFailures) {
11101
+ for (const failure of recentFailures) {
11102
+ addObservation(map, runFailureObservation(failure));
11103
+ }
11104
+ }
11105
+ function memberProvider(member) {
11106
+ return member.provider !== null && member.provider.length > 0 ? member.provider : null;
11107
+ }
11108
+ function memberModel(member) {
11109
+ return member.model !== null && member.model.length > 0 ? member.model : null;
11110
+ }
11111
+ function isFailedMember(member) {
11112
+ return member.status === "failed" || member.status === "cancelled";
11113
+ }
11114
+ function groupFailureObservation(group) {
11115
+ if (group.status !== "failed" && group.status !== "cancelled") return null;
11116
+ return {
11117
+ fingerprint: fingerprint(["group", group.id, group.status]),
11118
+ severity: "critical",
11119
+ category: "group-failure",
11120
+ source: "group",
11121
+ title: `Group ${group.status}`,
11122
+ message: `${group.title} is marked ${group.status}.`,
11123
+ observedAt: group.updatedAt,
11124
+ logId: null,
11125
+ sessionId: null,
11126
+ runId: null,
11127
+ groupId: group.id,
11128
+ provider: null,
11129
+ model: null,
11130
+ apiFormat: null,
11131
+ evidence: baseEvidence(null, null, null, group.id),
11132
+ metadata: {
11133
+ groupStatus: group.status,
11134
+ memberCount: group.members.length
11135
+ }
11136
+ };
11137
+ }
11138
+ function groupMemberFailureObservation(group, member) {
11139
+ return {
11140
+ fingerprint: fingerprint(["group-member", group.id, member.id, member.status]),
11141
+ severity: "critical",
11142
+ category: "group-failure",
11143
+ source: "group",
11144
+ title: "Group member failed",
11145
+ message: `${member.label ?? member.sessionId} is marked ${member.status ?? "unknown"}.`,
11146
+ observedAt: member.updatedAt,
11147
+ logId: null,
11148
+ sessionId: member.sessionId,
11149
+ runId: member.runId,
11150
+ groupId: group.id,
11151
+ provider: memberProvider(member),
11152
+ model: memberModel(member),
11153
+ apiFormat: null,
11154
+ evidence: baseEvidence(null, member.sessionId, member.runId, group.id),
11155
+ metadata: {
11156
+ groupTitle: group.title,
11157
+ memberId: member.id,
11158
+ memberStatus: member.status
11159
+ }
11160
+ };
11161
+ }
11162
+ function groupIncompleteObservation(group, missingStatusCount) {
11163
+ return {
11164
+ fingerprint: fingerprint(["group-incomplete", group.id, missingStatusCount]),
11165
+ severity: "notice",
11166
+ category: "group-incomplete",
11167
+ source: "group",
11168
+ title: "Group has unclassified members",
11169
+ message: `${group.title} has ${String(missingStatusCount)} member(s) without a final status.`,
11170
+ observedAt: group.updatedAt,
11171
+ logId: null,
11172
+ sessionId: null,
11173
+ runId: null,
11174
+ groupId: group.id,
11175
+ provider: null,
11176
+ model: null,
11177
+ apiFormat: null,
11178
+ evidence: baseEvidence(null, null, null, group.id),
11179
+ metadata: {
11180
+ groupTitle: group.title,
11181
+ missingStatusCount
11182
+ }
11183
+ };
11184
+ }
11185
+ function addGroupObservations(map, groups) {
11186
+ for (const group of groups) {
11187
+ const groupFailure = groupFailureObservation(group);
11188
+ if (groupFailure !== null) addObservation(map, groupFailure);
11189
+ let missingStatusCount = 0;
11190
+ for (const member of group.members) {
11191
+ if (isFailedMember(member)) {
11192
+ addObservation(map, groupMemberFailureObservation(group, member));
11193
+ }
11194
+ if (member.status === null) missingStatusCount += 1;
11195
+ }
11196
+ if (missingStatusCount > 0) {
11197
+ addObservation(map, groupIncompleteObservation(group, missingStatusCount));
11198
+ }
11199
+ }
11200
+ }
11201
+ function alertMatchesFilters(alert, filters) {
11202
+ if (filters.severity !== void 0 && alert.severity !== filters.severity) return false;
11203
+ if (filters.category !== void 0 && alert.category !== filters.category) return false;
11204
+ if (filters.source !== void 0 && alert.source !== filters.source) return false;
11205
+ return true;
11206
+ }
11207
+ function sortAlerts(alerts) {
11208
+ return [...alerts].sort((left, right) => {
11209
+ const timeCompare = right.lastSeenAt.localeCompare(left.lastSeenAt);
11210
+ if (timeCompare !== 0) return timeCompare;
11211
+ return left.id.localeCompare(right.id);
11212
+ });
11213
+ }
11214
+ function buildAlertSummary(alerts) {
11215
+ let critical = 0;
11216
+ let warning = 0;
11217
+ let notice = 0;
11218
+ const byCategory = /* @__PURE__ */ new Map();
11219
+ for (const alert of alerts) {
11220
+ switch (alert.severity) {
11221
+ case "critical":
11222
+ critical += 1;
11223
+ break;
11224
+ case "warning":
11225
+ warning += 1;
11226
+ break;
11227
+ case "notice":
11228
+ notice += 1;
11229
+ break;
11230
+ }
11231
+ byCategory.set(alert.category, (byCategory.get(alert.category) ?? 0) + 1);
11232
+ }
11233
+ return {
11234
+ total: alerts.length,
11235
+ open: alerts.length,
11236
+ critical,
11237
+ warning,
11238
+ notice,
11239
+ byCategory: [...byCategory.entries()].map(([category, count]) => ({ category, count })).sort(
11240
+ (left, right) => right.count - left.count || left.category.localeCompare(right.category)
11241
+ )
11242
+ };
11243
+ }
11244
+ function collectAlertsFromEvidence(input) {
11245
+ const map = /* @__PURE__ */ new Map();
11246
+ addLogObservations(map, input.logs, input.slowResponseThresholdSeconds);
11247
+ addRunFailureObservations(map, input.recentFailures);
11248
+ addGroupObservations(map, input.groups);
11249
+ return sortAlerts([...map.values()]);
11250
+ }
11251
+ function filterAlerts(alerts, filters) {
11252
+ return alerts.filter((alert) => alertMatchesFilters(alert, filters));
11253
+ }
11254
+ async function listAlerts(options = {}) {
11255
+ const limit = clampAlertLimit(options.limit);
11256
+ const scanLimit = clampAlertScanLimit(options.scanLimit);
11257
+ const logPage = await listLogsCursorPage({
11258
+ limit: scanLimit,
11259
+ includeBodies: false,
11260
+ anchor: "newest"
11261
+ });
11262
+ const config = getConfig();
11263
+ const alerts = collectAlertsFromEvidence({
11264
+ logs: logPage.logs,
11265
+ recentFailures: listRecentFailures(scanLimit).failures,
11266
+ groups: listGroups(),
11267
+ slowResponseThresholdSeconds: config.slowResponseThresholdSeconds
11268
+ });
11269
+ const filtered = filterAlerts(alerts, options);
11270
+ return {
11271
+ alerts: filtered.slice(0, limit),
11272
+ total: filtered.length,
11273
+ limit,
11274
+ summary: buildAlertSummary(filtered)
11275
+ };
11276
+ }
11277
+ async function getAlertSummary(options = {}) {
11278
+ const response = await listAlerts({
11279
+ limit: MAX_ALERT_LIMIT,
11280
+ scanLimit: options.scanLimit
11281
+ });
11282
+ return response.summary;
11283
+ }
11284
+ function listInspectorAlerts(options) {
11285
+ return listAlerts(options);
11286
+ }
11287
+ function getInspectorAlertSummary(options) {
11288
+ return getAlertSummary(options);
11289
+ }
11290
+ function parsePositiveInt$1(value) {
11291
+ if (value === null) return void 0;
11292
+ const parsed = Number(value);
11293
+ if (!Number.isInteger(parsed) || parsed < 1) return void 0;
11294
+ return parsed;
11295
+ }
11296
+ function parseFilters(url) {
11297
+ const severityResult = AlertSeveritySchema.safeParse(url.searchParams.get("severity"));
11298
+ const categoryResult = AlertCategorySchema.safeParse(url.searchParams.get("category"));
11299
+ const sourceResult = AlertSourceSchema.safeParse(url.searchParams.get("source"));
11300
+ return {
11301
+ severity: severityResult.success ? severityResult.data : void 0,
11302
+ category: categoryResult.success ? categoryResult.data : void 0,
11303
+ source: sourceResult.success ? sourceResult.data : void 0
11304
+ };
11305
+ }
11306
+ const Route$q = createFileRoute("/api/alerts")({
11307
+ server: {
11308
+ handlers: {
11309
+ GET: async ({ request }) => {
11310
+ const url = new URL(request.url);
11311
+ return Response.json(
11312
+ await listInspectorAlerts({
11313
+ ...parseFilters(url),
11314
+ limit: parsePositiveInt$1(url.searchParams.get("limit")),
11315
+ scanLimit: parsePositiveInt$1(url.searchParams.get("scanLimit"))
11316
+ })
11317
+ );
11318
+ }
11319
+ }
11320
+ }
11321
+ });
11322
+ async function readJsonBody$4(request) {
11323
+ try {
11324
+ const raw = await request.text();
11325
+ if (raw.trim().length === 0) return { ok: true, value: {} };
11326
+ return { ok: true, value: JSON.parse(raw) };
11327
+ } catch {
11328
+ return { ok: false };
11329
+ }
11330
+ }
11331
+ const Route$p = createFileRoute("/api/runs/$runId")({
11332
+ server: {
11333
+ handlers: {
11334
+ GET: ({ params }) => {
11335
+ const run = getInspectorRun(params.runId);
11336
+ if (!run.ok) {
11337
+ return Response.json({ error: run.message }, { status: run.status });
11338
+ }
11339
+ return Response.json(run.value);
11340
+ },
11341
+ PATCH: async ({ params, request }) => {
11342
+ const body = await readJsonBody$4(request);
11343
+ if (!body.ok) {
11344
+ return Response.json({ error: "Invalid JSON body" }, { status: 400 });
11345
+ }
11346
+ const parsed = UpdateInspectorRunInputSchema.safeParse(body.value);
11347
+ if (!parsed.success) {
11348
+ return Response.json(
11349
+ { error: "Invalid request body", details: parsed.error.format() },
11350
+ { status: 400 }
11351
+ );
11352
+ }
11353
+ const updated = updateInspectorRun(params.runId, parsed.data);
11354
+ if (!updated.ok) {
11355
+ return Response.json({ error: updated.message }, { status: updated.status });
11356
+ }
11357
+ return Response.json(updated.value);
11358
+ }
11359
+ }
11360
+ }
11361
+ });
11362
+ const ZHIPU_OPENAI_BASE_URL = "https://open.bigmodel.cn/api/paas/v4";
11363
+ const ZHIPU_CODING_OPENAI_BASE_URL = "https://open.bigmodel.cn/api/coding/paas/v4";
11364
+ const ZHIPU_CODING_ANTHROPIC_BASE_URL = "https://open.bigmodel.cn/api/anthropic";
11365
+ function readJsonSafe(filePath) {
11366
+ try {
11367
+ if (!existsSync(filePath)) return null;
11368
+ const raw = readFileSync(filePath, "utf-8");
11369
+ const parsed = JSON.parse(raw);
11370
+ return isPlainRecord(parsed) ? parsed : null;
11371
+ } catch {
11372
+ return null;
11373
+ }
11374
+ }
11375
+ function getEnvValue(envObj, key) {
11376
+ if (!isPlainRecord(envObj)) return void 0;
11377
+ const val = envObj[key];
11378
+ return typeof val === "string" ? val.trim() : void 0;
11379
+ }
11380
+ const KNOWN_PROVIDER_NAMES = {
11381
+ deepseek: "DeepSeek",
11382
+ minimaxi: "MiniMax",
11383
+ openai: "OpenAI",
11384
+ anthropic: "Anthropic",
11385
+ openrouter: "OpenRouter",
11386
+ groq: "Groq",
11387
+ together: "Together",
11388
+ fireworks: "Fireworks",
11389
+ bedrock: "Bedrock",
11390
+ vertexai: "Vertex AI",
11391
+ azure: "Azure",
11392
+ moonshot: "Moonshot",
11393
+ zhipu: "ZhipuAI",
11394
+ zhipuai: "ZhipuAI",
11395
+ bigmodel: "ZhipuAI",
11396
+ zai: "Z.AI",
11397
+ qwen: "Qwen",
11398
+ baichuan: "Baichuan",
11399
+ iflytek: "iFlytek",
11400
+ doubao: "Doubao",
11401
+ lingyiwanwu: "LingyiWanwu",
11402
+ stepfun: "StepFun"
11403
+ };
11404
+ function deriveNameFromUrl(url) {
11405
+ try {
11406
+ const hostname = new URL(url).hostname;
11407
+ const parts = hostname.split(".");
11408
+ const domain = parts.length >= 2 ? parts[parts.length - 2] ?? hostname : hostname;
11409
+ const domainLower = domain.toLowerCase();
11410
+ if (KNOWN_PROVIDER_NAMES[domainLower] !== void 0) {
11411
+ return KNOWN_PROVIDER_NAMES[domainLower];
11412
+ }
11413
+ return domain.charAt(0).toUpperCase() + domain.slice(1);
11414
+ } catch {
11415
+ return "Imported Provider";
11416
+ }
11417
+ }
11418
+ function detectClaudeCodeProviders() {
11419
+ const home = homedir();
11420
+ const results = [];
11421
+ const globalConfig = readJsonSafe(join(home, ".claude", "settings.json"));
11422
+ const localConfig = readJsonSafe(join(home, ".claude", "settings.local.json"));
10238
11423
  const mergedEnv = {};
10239
11424
  if (isPlainRecord(globalConfig)) {
10240
11425
  const env = globalConfig["env"];
@@ -10600,7 +11785,7 @@ function scanExternalProviders() {
10600
11785
  }
10601
11786
  return { providers: filteredProviders, warnings };
10602
11787
  }
10603
- const Route$l = createFileRoute("/api/providers/scan")({
11788
+ const Route$o = createFileRoute("/api/providers/scan")({
10604
11789
  server: {
10605
11790
  handlers: {
10606
11791
  GET: () => {
@@ -10621,7 +11806,7 @@ const Route$l = createFileRoute("/api/providers/scan")({
10621
11806
  }
10622
11807
  });
10623
11808
  union([string(), object({ providers: unknown() })]);
10624
- const Route$k = createFileRoute("/api/providers/import")({
11809
+ const Route$n = createFileRoute("/api/providers/import")({
10625
11810
  server: {
10626
11811
  handlers: {
10627
11812
  POST: async ({ request }) => {
@@ -10654,7 +11839,7 @@ const Route$k = createFileRoute("/api/providers/import")({
10654
11839
  }
10655
11840
  }
10656
11841
  });
10657
- const Route$j = createFileRoute("/api/providers/export")({
11842
+ const Route$m = createFileRoute("/api/providers/export")({
10658
11843
  server: {
10659
11844
  handlers: {
10660
11845
  GET: ({ request }) => {
@@ -10687,7 +11872,7 @@ const ProviderUpdateSchema = object({
10687
11872
  modelMetadata: array(ProviderModelMetadataSchema).optional(),
10688
11873
  source: _enum(["company", "personal"]).optional()
10689
11874
  });
10690
- const Route$i = createFileRoute("/api/providers/$providerId")({
11875
+ const Route$l = createFileRoute("/api/providers/$providerId")({
10691
11876
  server: {
10692
11877
  handlers: {
10693
11878
  GET: ({ params, request }) => {
@@ -10722,7 +11907,7 @@ const Route$i = createFileRoute("/api/providers/$providerId")({
10722
11907
  }
10723
11908
  });
10724
11909
  const INITIAL_STREAM_LOG_LIMIT = 100;
10725
- const Route$h = createFileRoute("/api/logs/stream")({
11910
+ const Route$k = createFileRoute("/api/logs/stream")({
10726
11911
  server: {
10727
11912
  handlers: {
10728
11913
  GET: ({ request }) => {
@@ -10795,7 +11980,411 @@ const Route$h = createFileRoute("/api/logs/stream")({
10795
11980
  }
10796
11981
  }
10797
11982
  });
10798
- const Route$g = createFileRoute("/api/logs/$id")({
11983
+ const ManifestSchema = object({
11984
+ version: string().optional(),
11985
+ exportedAt: string().optional(),
11986
+ mode: string().optional(),
11987
+ redacted: boolean().optional(),
11988
+ logIds: array(number().int().positive()).optional()
11989
+ }).passthrough();
11990
+ const LogsArraySchema = array(CapturedLogSchema);
11991
+ const LogsEnvelopeSchema = object({ logs: array(CapturedLogSchema) }).passthrough();
11992
+ const StreamingChunksImportSchema = object({
11993
+ chunks: array(StreamingChunkSchema$1),
11994
+ truncated: boolean().optional().default(false)
11995
+ });
11996
+ const SEMVER_MAJOR_PATTERN = /^([0-9]+)\.[0-9]+\.[0-9]+(?:[-+].*)?$/;
11997
+ function readRecordProperty(value, name) {
11998
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
11999
+ const desc = Object.getOwnPropertyDescriptor(value, name);
12000
+ return desc === void 0 ? void 0 : desc.value;
12001
+ }
12002
+ function parseJson(text) {
12003
+ if (text === null) return null;
12004
+ try {
12005
+ const parsed = JSON.parse(text);
12006
+ return parsed;
12007
+ } catch {
12008
+ return null;
12009
+ }
12010
+ }
12011
+ function parseJsonLogs(text) {
12012
+ const parsed = parseJson(text);
12013
+ const direct = LogsArraySchema.safeParse(parsed);
12014
+ if (direct.success) return direct.data;
12015
+ const envelope = LogsEnvelopeSchema.safeParse(parsed);
12016
+ if (envelope.success) return envelope.data.logs;
12017
+ return null;
12018
+ }
12019
+ function parseJsonlLogs(text) {
12020
+ const logs = [];
12021
+ let skipped = 0;
12022
+ for (const line of text.split(/\r?\n/)) {
12023
+ if (line.trim() === "") continue;
12024
+ const parsed = CapturedLogSchema.safeParse(parseJson(line));
12025
+ if (parsed.success) {
12026
+ logs.push(parsed.data);
12027
+ } else {
12028
+ skipped += 1;
12029
+ }
12030
+ }
12031
+ return { logs, skipped };
12032
+ }
12033
+ function readStringProperty(value, name) {
12034
+ const property = readRecordProperty(value, name);
12035
+ return typeof property === "string" ? property : null;
12036
+ }
12037
+ function hasProperty(value, name) {
12038
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
12039
+ return Object.getOwnPropertyDescriptor(value, name) !== void 0;
12040
+ }
12041
+ function modelFromRequestText(requestText) {
12042
+ return readStringProperty(parseJson(requestText), "model");
12043
+ }
12044
+ function inferApiFormatFromBody(path2, model, requestText) {
12045
+ const pathFormat = apiFormatForPath(path2);
12046
+ if (pathFormat !== "unknown") return pathFormat;
12047
+ const normalizedModel = model?.toLowerCase() ?? "";
12048
+ if (normalizedModel.startsWith("claude-")) return "anthropic";
12049
+ if (normalizedModel.startsWith("gpt-") || normalizedModel.startsWith("o1-") || normalizedModel.startsWith("o3-") || normalizedModel.startsWith("o4-") || normalizedModel.startsWith("deepseek-") || normalizedModel.startsWith("glm-") || normalizedModel.startsWith("qwen")) {
12050
+ return "openai";
12051
+ }
12052
+ const parsedRequest = parseJson(requestText);
12053
+ if (hasProperty(parsedRequest, "system") || hasProperty(parsedRequest, "max_tokens")) {
12054
+ return "anthropic";
12055
+ }
12056
+ return "openai";
12057
+ }
12058
+ function defaultPathForApiFormat(apiFormat) {
12059
+ switch (apiFormat) {
12060
+ case "anthropic":
12061
+ return "/v1/messages";
12062
+ case "openai":
12063
+ return "/v1/chat/completions";
12064
+ case "unknown":
12065
+ return "/v1/chat/completions";
12066
+ }
12067
+ }
12068
+ function normalizeTimestamp(value, fallback) {
12069
+ return Number.isNaN(new Date(value).getTime()) ? fallback : value;
12070
+ }
12071
+ function responseStatusFromText(responseText) {
12072
+ if (responseText === null) return null;
12073
+ const parsed = parseJson(responseText);
12074
+ return hasProperty(parsed, "error") ? 500 : 200;
12075
+ }
12076
+ function mergeWarnings(existing, generated) {
12077
+ const values = [];
12078
+ for (const warning of existing ?? []) {
12079
+ if (!values.includes(warning)) values.push(warning);
12080
+ }
12081
+ for (const warning of generated) {
12082
+ if (!values.includes(warning)) values.push(warning);
12083
+ }
12084
+ return values.length === 0 ? void 0 : values;
12085
+ }
12086
+ function withToolWarnings(log) {
12087
+ const warnings = analyzeToolSchemaWarnings({
12088
+ rawRequestBody: log.rawRequestBody,
12089
+ responseText: log.responseText,
12090
+ apiFormat: log.apiFormat
12091
+ });
12092
+ return {
12093
+ ...log,
12094
+ warnings: mergeWarnings(log.warnings, warnings)
12095
+ };
12096
+ }
12097
+ function streamingChunksFromText(text) {
12098
+ const parsed = StreamingChunksImportSchema.safeParse(parseJson(text));
12099
+ return parsed.success ? parsed.data : void 0;
12100
+ }
12101
+ function draftFromCapturedLog(log, requestText, responseText, streamingChunks) {
12102
+ const rawRequestBody = requestText ?? log.rawRequestBody;
12103
+ const response = responseText ?? log.responseText;
12104
+ const model = log.model ?? modelFromRequestText(rawRequestBody);
12105
+ const apiFormat = log.apiFormat === "unknown" ? inferApiFormatFromBody(log.path, model, rawRequestBody) : log.apiFormat;
12106
+ const path2 = log.path.trim() === "" ? defaultPathForApiFormat(apiFormat) : log.path;
12107
+ return withToolWarnings({
12108
+ timestamp: normalizeTimestamp(log.timestamp, (/* @__PURE__ */ new Date()).toISOString()),
12109
+ method: log.method.trim() === "" ? "POST" : log.method,
12110
+ path: path2,
12111
+ model,
12112
+ sessionId: null,
12113
+ rawRequestBody,
12114
+ responseStatus: log.responseStatus ?? responseStatusFromText(response),
12115
+ responseText: response,
12116
+ inputTokens: log.inputTokens,
12117
+ outputTokens: log.outputTokens,
12118
+ cacheCreationInputTokens: log.cacheCreationInputTokens,
12119
+ cacheReadInputTokens: log.cacheReadInputTokens,
12120
+ elapsedMs: log.elapsedMs,
12121
+ firstChunkMs: log.firstChunkMs ?? null,
12122
+ totalStreamMs: log.totalStreamMs ?? null,
12123
+ tokensPerSecond: log.tokensPerSecond ?? null,
12124
+ streaming: log.streaming || streamingChunks !== void 0,
12125
+ userAgent: log.userAgent,
12126
+ origin: log.origin,
12127
+ rawHeaders: log.rawHeaders,
12128
+ headers: log.headers,
12129
+ apiFormat,
12130
+ isTest: false,
12131
+ replayOfLogId: null,
12132
+ providerName: log.providerName ?? null,
12133
+ clientPort: log.clientPort ?? null,
12134
+ clientPid: log.clientPid ?? null,
12135
+ clientCwd: log.clientCwd ?? null,
12136
+ clientProjectFolder: log.clientProjectFolder ?? null,
12137
+ streamingChunks,
12138
+ streamingChunksPath: null,
12139
+ rawRequestBodyBytes: log.rawRequestBodyBytes,
12140
+ responseTextBytes: log.responseTextBytes,
12141
+ bodyContentMode: "full",
12142
+ warnings: log.warnings,
12143
+ error: log.error ?? null
12144
+ });
12145
+ }
12146
+ function legacyDraftFromParts(input) {
12147
+ const model = modelFromRequestText(input.requestText);
12148
+ const apiFormat = inferApiFormatFromBody("", model, input.requestText);
12149
+ return withToolWarnings({
12150
+ timestamp: input.exportedAt,
12151
+ method: "POST",
12152
+ path: defaultPathForApiFormat(apiFormat),
12153
+ model,
12154
+ sessionId: null,
12155
+ rawRequestBody: input.requestText,
12156
+ responseStatus: responseStatusFromText(input.responseText),
12157
+ responseText: input.responseText,
12158
+ inputTokens: null,
12159
+ outputTokens: null,
12160
+ cacheCreationInputTokens: null,
12161
+ cacheReadInputTokens: null,
12162
+ elapsedMs: null,
12163
+ firstChunkMs: null,
12164
+ totalStreamMs: null,
12165
+ tokensPerSecond: null,
12166
+ streaming: input.streamingChunks !== void 0,
12167
+ userAgent: "agent-inspector-import",
12168
+ origin: null,
12169
+ apiFormat,
12170
+ isTest: false,
12171
+ replayOfLogId: null,
12172
+ providerName: null,
12173
+ clientPort: null,
12174
+ clientPid: null,
12175
+ clientCwd: null,
12176
+ clientProjectFolder: null,
12177
+ streamingChunks: input.streamingChunks,
12178
+ streamingChunksPath: null,
12179
+ bodyContentMode: "full",
12180
+ error: null
12181
+ });
12182
+ }
12183
+ async function readZipText(zip, path2) {
12184
+ const file = zip.file(path2);
12185
+ return file === null ? null : await file.async("text");
12186
+ }
12187
+ async function readManifest(zip) {
12188
+ const text = await readZipText(zip, "manifest.json");
12189
+ const parsed = ManifestSchema.safeParse(parseJson(text));
12190
+ return parsed.success ? parsed.data : null;
12191
+ }
12192
+ function versionMajor(version2) {
12193
+ const match = SEMVER_MAJOR_PATTERN.exec(version2);
12194
+ if (match === null) return null;
12195
+ const major = match[1];
12196
+ return major === void 0 ? null : Number.parseInt(major, 10);
12197
+ }
12198
+ function validateManifestVersion(manifest) {
12199
+ const exportedVersion = manifest?.version;
12200
+ if (exportedVersion === void 0) {
12201
+ return {
12202
+ ok: true,
12203
+ warning: "Imported legacy export without version metadata. Compatibility could not be verified."
12204
+ };
12205
+ }
12206
+ const exportedMajor = versionMajor(exportedVersion);
12207
+ if (exportedMajor === null) {
12208
+ return {
12209
+ ok: false,
12210
+ message: `Unsupported export version "${exportedVersion}".`
12211
+ };
12212
+ }
12213
+ const currentMajor = versionMajor(packageJson.version);
12214
+ if (currentMajor === null) {
12215
+ return {
12216
+ ok: true,
12217
+ warning: `Current Agent Inspector version "${packageJson.version}" could not be verified against export version "${exportedVersion}".`
12218
+ };
12219
+ }
12220
+ if (exportedMajor !== currentMajor) {
12221
+ return {
12222
+ ok: false,
12223
+ message: `Export version mismatch: archive was created by Agent Inspector v${exportedVersion}, current version is v${packageJson.version}. Import with a matching major version.`
12224
+ };
12225
+ }
12226
+ if (exportedVersion !== packageJson.version) {
12227
+ return {
12228
+ ok: true,
12229
+ warning: `Imported export from Agent Inspector v${exportedVersion}; current version is v${packageJson.version}.`
12230
+ };
12231
+ }
12232
+ return { ok: true, warning: null };
12233
+ }
12234
+ async function parseZipLogImport(data) {
12235
+ const warnings = [];
12236
+ const zip = await JSZip.loadAsync(data);
12237
+ const manifest = await readManifest(zip);
12238
+ const versionCheck = validateManifestVersion(manifest);
12239
+ if (!versionCheck.ok) {
12240
+ return {
12241
+ ok: false,
12242
+ message: versionCheck.message,
12243
+ warnings
12244
+ };
12245
+ }
12246
+ if (versionCheck.warning !== null) {
12247
+ warnings.push(versionCheck.warning);
12248
+ }
12249
+ const exportedAt = normalizeTimestamp(manifest?.exportedAt ?? "", (/* @__PURE__ */ new Date()).toISOString());
12250
+ if (manifest?.redacted === true) {
12251
+ warnings.push("Imported redacted export. Secrets and some replay details may be unavailable.");
12252
+ }
12253
+ const metadataText2 = await readZipText(zip, "logs.json");
12254
+ const metadataLogs = metadataText2 === null ? null : parseJsonLogs(metadataText2);
12255
+ const logs = [];
12256
+ let skipped = 0;
12257
+ if (metadataLogs !== null) {
12258
+ for (const log of metadataLogs) {
12259
+ const requestText = await readZipText(zip, `#${String(log.id)}.Request.json`);
12260
+ const responseText = await readZipText(zip, `#${String(log.id)}.Response.json`);
12261
+ const streamingText = await readZipText(zip, `#${String(log.id)}.SSE.Response.json`);
12262
+ logs.push(
12263
+ draftFromCapturedLog(
12264
+ log,
12265
+ requestText,
12266
+ responseText,
12267
+ streamingChunksFromText(streamingText)
12268
+ )
12269
+ );
12270
+ }
12271
+ return { ok: true, logs, skipped, warnings };
12272
+ }
12273
+ for (const originalId of manifest?.logIds ?? []) {
12274
+ const requestText = await readZipText(zip, `#${String(originalId)}.Request.json`);
12275
+ const responseText = await readZipText(zip, `#${String(originalId)}.Response.json`);
12276
+ const streamingText = await readZipText(zip, `#${String(originalId)}.SSE.Response.json`);
12277
+ if (requestText === null && responseText === null && streamingText === null) {
12278
+ skipped += 1;
12279
+ continue;
12280
+ }
12281
+ logs.push(
12282
+ legacyDraftFromParts({
12283
+ exportedAt,
12284
+ requestText,
12285
+ responseText,
12286
+ streamingChunks: streamingChunksFromText(streamingText)
12287
+ })
12288
+ );
12289
+ }
12290
+ if (logs.length === 0) {
12291
+ return {
12292
+ ok: false,
12293
+ message: "No importable logs were found in the archive.",
12294
+ warnings
12295
+ };
12296
+ }
12297
+ return { ok: true, logs, skipped, warnings };
12298
+ }
12299
+ function looksLikeZip(data, fileName) {
12300
+ if (fileName.toLowerCase().endsWith(".zip")) return true;
12301
+ return data[0] === 80 && data[1] === 75;
12302
+ }
12303
+ function parseTextLogImport(text) {
12304
+ const jsonLogs = parseJsonLogs(text);
12305
+ if (jsonLogs !== null) {
12306
+ return {
12307
+ ok: true,
12308
+ logs: jsonLogs.map((log) => draftFromCapturedLog(log, null, null, void 0)),
12309
+ skipped: 0,
12310
+ warnings: []
12311
+ };
12312
+ }
12313
+ const jsonl = parseJsonlLogs(text);
12314
+ if (jsonl.logs.length > 0) {
12315
+ return {
12316
+ ok: true,
12317
+ logs: jsonl.logs.map((log) => draftFromCapturedLog(log, null, null, void 0)),
12318
+ skipped: jsonl.skipped,
12319
+ warnings: []
12320
+ };
12321
+ }
12322
+ return {
12323
+ ok: false,
12324
+ message: "Unsupported import file. Use an Agent Inspector export ZIP, logs.json, or JSONL file.",
12325
+ warnings: []
12326
+ };
12327
+ }
12328
+ async function parseImportedLogFile(input) {
12329
+ try {
12330
+ if (looksLikeZip(input.data, input.fileName)) {
12331
+ return await parseZipLogImport(input.data);
12332
+ }
12333
+ return parseTextLogImport(new TextDecoder("utf-8").decode(input.data));
12334
+ } catch (err) {
12335
+ return {
12336
+ ok: false,
12337
+ message: err instanceof Error ? err.message : "Failed to parse import file.",
12338
+ warnings: []
12339
+ };
12340
+ }
12341
+ }
12342
+ function sessionNameFromImportFileName(fileName) {
12343
+ const normalized = fileName.replaceAll("\\", "/");
12344
+ const base = basename(normalized).replace(/\.(zip|json|jsonl)$/i, "").trim();
12345
+ return base === "" ? `import-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}` : base;
12346
+ }
12347
+ function isUploadedFile(value) {
12348
+ return value instanceof File;
12349
+ }
12350
+ const Route$j = createFileRoute("/api/logs/import")({
12351
+ server: {
12352
+ handlers: {
12353
+ POST: async ({ request }) => {
12354
+ let formData;
12355
+ try {
12356
+ formData = await request.formData();
12357
+ } catch {
12358
+ return Response.json({ error: "Expected multipart form data" }, { status: 400 });
12359
+ }
12360
+ const upload = formData.get("file");
12361
+ if (!isUploadedFile(upload)) {
12362
+ return Response.json({ error: "Missing import file" }, { status: 400 });
12363
+ }
12364
+ const sessionId = sessionNameFromImportFileName(upload.name);
12365
+ const parsed = await parseImportedLogFile({
12366
+ fileName: upload.name,
12367
+ data: Buffer.from(await upload.arrayBuffer())
12368
+ });
12369
+ if (!parsed.ok) {
12370
+ return Response.json(
12371
+ { error: parsed.message, warnings: parsed.warnings },
12372
+ { status: 400 }
12373
+ );
12374
+ }
12375
+ const imported = await importCapturedLogs(parsed.logs, sessionId);
12376
+ return Response.json({
12377
+ sessionId,
12378
+ imported: imported.length,
12379
+ skipped: parsed.skipped,
12380
+ warnings: parsed.warnings,
12381
+ logs: imported.map((log) => compactLogForList(log))
12382
+ });
12383
+ }
12384
+ }
12385
+ }
12386
+ });
12387
+ const Route$i = createFileRoute("/api/logs/$id")({
10799
12388
  server: {
10800
12389
  handlers: {
10801
12390
  GET: async ({ params }) => {
@@ -11332,7 +12921,7 @@ const SearchBodySchema = object({
11332
12921
  query: string().min(1),
11333
12922
  project: string().optional()
11334
12923
  });
11335
- const Route$f = createFileRoute("/api/knowledge/search")({
12924
+ const Route$h = createFileRoute("/api/knowledge/search")({
11336
12925
  server: {
11337
12926
  handlers: {
11338
12927
  GET: async ({ request }) => {
@@ -11354,7 +12943,7 @@ const Route$f = createFileRoute("/api/knowledge/search")({
11354
12943
  }
11355
12944
  }
11356
12945
  });
11357
- const Route$e = createFileRoute("/api/knowledge/project-context")({
12946
+ const Route$g = createFileRoute("/api/knowledge/project-context")({
11358
12947
  server: {
11359
12948
  handlers: {
11360
12949
  GET: async ({ request }) => {
@@ -11436,7 +13025,7 @@ function updateCandidateDraft(id, updates) {
11436
13025
  candidates.set(id, updated);
11437
13026
  return updated;
11438
13027
  }
11439
- const Route$d = createFileRoute("/api/knowledge/candidates")({
13028
+ const Route$f = createFileRoute("/api/knowledge/candidates")({
11440
13029
  server: {
11441
13030
  handlers: {
11442
13031
  GET: () => Response.json({ candidates: listCandidates() })
@@ -11471,7 +13060,7 @@ function readDeleteEvidenceFlag(request) {
11471
13060
  return { ok: false };
11472
13061
  }
11473
13062
  }
11474
- const Route$c = createFileRoute("/api/groups/$groupId")({
13063
+ const Route$e = createFileRoute("/api/groups/$groupId")({
11475
13064
  server: {
11476
13065
  handlers: {
11477
13066
  GET: ({ params }) => {
@@ -11518,7 +13107,7 @@ const Route$c = createFileRoute("/api/groups/$groupId")({
11518
13107
  }
11519
13108
  }
11520
13109
  });
11521
- const Route$b = createFileRoute("/api/config/paths")({
13110
+ const Route$d = createFileRoute("/api/config/paths")({
11522
13111
  server: {
11523
13112
  handlers: {
11524
13113
  GET: () => {
@@ -11529,6 +13118,26 @@ const Route$b = createFileRoute("/api/config/paths")({
11529
13118
  }
11530
13119
  }
11531
13120
  });
13121
+ function parsePositiveInt(value) {
13122
+ if (value === null) return void 0;
13123
+ const parsed = Number(value);
13124
+ if (!Number.isInteger(parsed) || parsed < 1) return void 0;
13125
+ return parsed;
13126
+ }
13127
+ const Route$c = createFileRoute("/api/alerts/summary")({
13128
+ server: {
13129
+ handlers: {
13130
+ GET: async ({ request }) => {
13131
+ const url = new URL(request.url);
13132
+ return Response.json(
13133
+ await getInspectorAlertSummary({
13134
+ scanLimit: parsePositiveInt(url.searchParams.get("scanLimit"))
13135
+ })
13136
+ );
13137
+ }
13138
+ }
13139
+ }
13140
+ });
11532
13141
  async function readJsonBody$2(request) {
11533
13142
  try {
11534
13143
  const raw = await request.text();
@@ -11538,7 +13147,7 @@ async function readJsonBody$2(request) {
11538
13147
  return { ok: false };
11539
13148
  }
11540
13149
  }
11541
- const Route$a = createFileRoute("/api/runs/$runId/evidence")({
13150
+ const Route$b = createFileRoute("/api/runs/$runId/evidence")({
11542
13151
  server: {
11543
13152
  handlers: {
11544
13153
  GET: ({ params }) => {
@@ -12189,7 +13798,7 @@ async function testStreamingEndpoint(baseUrl, provider, path2, model, isOpenAI)
12189
13798
  clearTimeout(timeoutId);
12190
13799
  }
12191
13800
  }
12192
- const Route$9 = createFileRoute("/api/providers/$providerId/test")({
13801
+ const Route$a = createFileRoute("/api/providers/$providerId/test")({
12193
13802
  server: {
12194
13803
  handlers: {
12195
13804
  POST: async ({ params }) => {
@@ -12283,7 +13892,7 @@ async function readRegistryJson(response) {
12283
13892
  return null;
12284
13893
  }
12285
13894
  }
12286
- const Route$8 = createFileRoute("/api/providers/$providerId/model-metadata")({
13895
+ const Route$9 = createFileRoute("/api/providers/$providerId/model-metadata")({
12287
13896
  server: {
12288
13897
  handlers: {
12289
13898
  POST: async ({ params, request }) => {
@@ -12373,7 +13982,7 @@ const Route$8 = createFileRoute("/api/providers/$providerId/model-metadata")({
12373
13982
  const ReplayRequestSchema = object({
12374
13983
  modifiedBody: string()
12375
13984
  });
12376
- const Route$7 = createFileRoute("/api/logs/$id/replay")({
13985
+ const Route$8 = createFileRoute("/api/logs/$id/replay")({
12377
13986
  server: {
12378
13987
  handlers: {
12379
13988
  POST: async ({ params, request }) => {
@@ -12574,7 +14183,7 @@ const Route$7 = createFileRoute("/api/logs/$id/replay")({
12574
14183
  }
12575
14184
  }
12576
14185
  });
12577
- const Route$6 = createFileRoute("/api/logs/$id/chunks")({
14186
+ const Route$7 = createFileRoute("/api/logs/$id/chunks")({
12578
14187
  server: {
12579
14188
  handlers: {
12580
14189
  GET: async ({ params }) => {
@@ -12600,6 +14209,116 @@ const Route$6 = createFileRoute("/api/logs/$id/chunks")({
12600
14209
  }
12601
14210
  }
12602
14211
  });
14212
+ const DEFAULT_LOG_BODY_CHUNK_BYTES = 256 * 1024;
14213
+ const MAX_LOG_BODY_CHUNK_BYTES = 1024 * 1024;
14214
+ function normalizeLogBodyChunkOffset(offset) {
14215
+ if (!Number.isInteger(offset) || offset < 0) return 0;
14216
+ return offset;
14217
+ }
14218
+ function normalizeLogBodyChunkLimit(limit) {
14219
+ if (!Number.isInteger(limit) || limit < 1) return DEFAULT_LOG_BODY_CHUNK_BYTES;
14220
+ return Math.min(limit, MAX_LOG_BODY_CHUNK_BYTES);
14221
+ }
14222
+ function bodyTextForPart(log, part) {
14223
+ switch (part) {
14224
+ case "request":
14225
+ return log.rawRequestBody;
14226
+ case "response":
14227
+ return log.responseText;
14228
+ }
14229
+ }
14230
+ function isUtf8ContinuationByte(byte) {
14231
+ return byte !== void 0 && (byte & 192) === 128;
14232
+ }
14233
+ function expandToUtf8Boundary(buffer, end) {
14234
+ if (end >= buffer.byteLength) return buffer.byteLength;
14235
+ let safeEnd = end;
14236
+ while (safeEnd < buffer.byteLength && isUtf8ContinuationByte(buffer[safeEnd])) {
14237
+ safeEnd++;
14238
+ }
14239
+ return safeEnd;
14240
+ }
14241
+ function createLogBodyChunk({
14242
+ log,
14243
+ part,
14244
+ offset,
14245
+ limit
14246
+ }) {
14247
+ const text = bodyTextForPart(log, part);
14248
+ const normalizedLimit = normalizeLogBodyChunkLimit(limit);
14249
+ if (text === null || text === "") {
14250
+ return {
14251
+ logId: log.id,
14252
+ part,
14253
+ text: "",
14254
+ offset: 0,
14255
+ limit: normalizedLimit,
14256
+ totalBytes: 0,
14257
+ textBytes: 0,
14258
+ nextOffset: null,
14259
+ hasMore: false,
14260
+ contentMode: "empty"
14261
+ };
14262
+ }
14263
+ const bodyBytes = Buffer.from(text, "utf8");
14264
+ const totalBytes = bodyBytes.byteLength;
14265
+ const normalizedOffset = Math.min(normalizeLogBodyChunkOffset(offset), totalBytes);
14266
+ const end = expandToUtf8Boundary(
14267
+ bodyBytes,
14268
+ Math.min(normalizedOffset + normalizedLimit, totalBytes)
14269
+ );
14270
+ const chunkText = bodyBytes.toString("utf8", normalizedOffset, end);
14271
+ const textBytes = end - normalizedOffset;
14272
+ const hasMore = end < totalBytes;
14273
+ const contentMode = hasMore || normalizedOffset > 0 ? "partial" : "full";
14274
+ return {
14275
+ logId: log.id,
14276
+ part,
14277
+ text: chunkText,
14278
+ offset: normalizedOffset,
14279
+ limit: normalizedLimit,
14280
+ totalBytes,
14281
+ textBytes,
14282
+ nextOffset: hasMore ? end : null,
14283
+ hasMore,
14284
+ contentMode
14285
+ };
14286
+ }
14287
+ function parseIntegerParam(value, fallback) {
14288
+ if (value === null) return fallback;
14289
+ const parsed = Number(value);
14290
+ if (!Number.isInteger(parsed)) return fallback;
14291
+ return parsed;
14292
+ }
14293
+ const Route$6 = createFileRoute("/api/logs/$id/body")({
14294
+ server: {
14295
+ handlers: {
14296
+ GET: async ({ params, request }) => {
14297
+ const id = Number(params.id);
14298
+ if (!Number.isInteger(id) || id <= 0) {
14299
+ return Response.json({ error: "Invalid log ID" }, { status: 400 });
14300
+ }
14301
+ const url = new URL(request.url);
14302
+ const partResult = LogBodyPartSchema.safeParse(url.searchParams.get("part"));
14303
+ if (!partResult.success) {
14304
+ return Response.json({ error: "Invalid body part" }, { status: 400 });
14305
+ }
14306
+ const log = await getLogById(id);
14307
+ if (log === null) {
14308
+ return Response.json({ error: "Log not found" }, { status: 404 });
14309
+ }
14310
+ return Response.json(
14311
+ createLogBodyChunk({
14312
+ log,
14313
+ part: partResult.data,
14314
+ offset: parseIntegerParam(url.searchParams.get("offset"), 0),
14315
+ limit: parseIntegerParam(url.searchParams.get("limit"), 0)
14316
+ })
14317
+ );
14318
+ }
14319
+ }
14320
+ }
14321
+ });
12603
14322
  const CandidateUpdateSchema = object({
12604
14323
  type: KnowledgeCandidateTypeSchema.optional(),
12605
14324
  title: string().trim().min(1).max(160).optional(),
@@ -13144,151 +14863,171 @@ const Route = createFileRoute("/api/knowledge/candidates/$candidateId/promote")(
13144
14863
  }
13145
14864
  }
13146
14865
  });
13147
- const IndexRoute = Route$y.update({
14866
+ const IndexRoute = Route$C.update({
13148
14867
  id: "/",
13149
14868
  path: "/",
13150
- getParentRoute: () => Route$z
14869
+ getParentRoute: () => Route$D
13151
14870
  });
13152
- const SessionSessionIdRoute = Route$x.update({
14871
+ const SessionSessionIdRoute = Route$B.update({
13153
14872
  id: "/session/$sessionId",
13154
14873
  path: "/session/$sessionId",
13155
- getParentRoute: () => Route$z
14874
+ getParentRoute: () => Route$D
13156
14875
  });
13157
- const ProxySplatRoute = Route$w.update({
14876
+ const ProxySplatRoute = Route$A.update({
13158
14877
  id: "/proxy/$",
13159
14878
  path: "/proxy/$",
13160
- getParentRoute: () => Route$z
14879
+ getParentRoute: () => Route$D
13161
14880
  });
13162
- const ApiSessionsRoute = Route$v.update({
14881
+ const ApiSessionsRoute = Route$z.update({
13163
14882
  id: "/api/sessions",
13164
14883
  path: "/api/sessions",
13165
- getParentRoute: () => Route$z
14884
+ getParentRoute: () => Route$D
13166
14885
  });
13167
- const ApiRunsRoute = Route$u.update({
14886
+ const ApiRunsRoute = Route$y.update({
13168
14887
  id: "/api/runs",
13169
14888
  path: "/api/runs",
13170
- getParentRoute: () => Route$z
14889
+ getParentRoute: () => Route$D
13171
14890
  });
13172
- const ApiProvidersRoute = Route$t.update({
14891
+ const ApiProvidersRoute = Route$x.update({
13173
14892
  id: "/api/providers",
13174
14893
  path: "/api/providers",
13175
- getParentRoute: () => Route$z
14894
+ getParentRoute: () => Route$D
13176
14895
  });
13177
- const ApiModelsRoute = Route$s.update({
14896
+ const ApiModelsRoute = Route$w.update({
13178
14897
  id: "/api/models",
13179
14898
  path: "/api/models",
13180
- getParentRoute: () => Route$z
14899
+ getParentRoute: () => Route$D
13181
14900
  });
13182
- const ApiMcpRoute = Route$r.update({
14901
+ const ApiMcpRoute = Route$v.update({
13183
14902
  id: "/api/mcp",
13184
14903
  path: "/api/mcp",
13185
- getParentRoute: () => Route$z
14904
+ getParentRoute: () => Route$D
13186
14905
  });
13187
- const ApiLogsRoute = Route$q.update({
14906
+ const ApiLogsRoute = Route$u.update({
13188
14907
  id: "/api/logs",
13189
14908
  path: "/api/logs",
13190
- getParentRoute: () => Route$z
14909
+ getParentRoute: () => Route$D
13191
14910
  });
13192
- const ApiHealthRoute = Route$p.update({
14911
+ const ApiHealthRoute = Route$t.update({
13193
14912
  id: "/api/health",
13194
14913
  path: "/api/health",
13195
- getParentRoute: () => Route$z
14914
+ getParentRoute: () => Route$D
13196
14915
  });
13197
- const ApiGroupsRoute = Route$o.update({
14916
+ const ApiGroupsRoute = Route$s.update({
13198
14917
  id: "/api/groups",
13199
14918
  path: "/api/groups",
13200
- getParentRoute: () => Route$z
14919
+ getParentRoute: () => Route$D
13201
14920
  });
13202
- const ApiConfigRoute = Route$n.update({
14921
+ const ApiConfigRoute = Route$r.update({
13203
14922
  id: "/api/config",
13204
14923
  path: "/api/config",
13205
- getParentRoute: () => Route$z
14924
+ getParentRoute: () => Route$D
13206
14925
  });
13207
- const ApiRunsRunIdRoute = Route$m.update({
14926
+ const ApiAlertsRoute = Route$q.update({
14927
+ id: "/api/alerts",
14928
+ path: "/api/alerts",
14929
+ getParentRoute: () => Route$D
14930
+ });
14931
+ const ApiRunsRunIdRoute = Route$p.update({
13208
14932
  id: "/$runId",
13209
14933
  path: "/$runId",
13210
14934
  getParentRoute: () => ApiRunsRoute
13211
14935
  });
13212
- const ApiProvidersScanRoute = Route$l.update({
14936
+ const ApiProvidersScanRoute = Route$o.update({
13213
14937
  id: "/scan",
13214
14938
  path: "/scan",
13215
14939
  getParentRoute: () => ApiProvidersRoute
13216
14940
  });
13217
- const ApiProvidersImportRoute = Route$k.update({
14941
+ const ApiProvidersImportRoute = Route$n.update({
13218
14942
  id: "/import",
13219
14943
  path: "/import",
13220
14944
  getParentRoute: () => ApiProvidersRoute
13221
14945
  });
13222
- const ApiProvidersExportRoute = Route$j.update({
14946
+ const ApiProvidersExportRoute = Route$m.update({
13223
14947
  id: "/export",
13224
14948
  path: "/export",
13225
14949
  getParentRoute: () => ApiProvidersRoute
13226
14950
  });
13227
- const ApiProvidersProviderIdRoute = Route$i.update({
14951
+ const ApiProvidersProviderIdRoute = Route$l.update({
13228
14952
  id: "/$providerId",
13229
14953
  path: "/$providerId",
13230
14954
  getParentRoute: () => ApiProvidersRoute
13231
14955
  });
13232
- const ApiLogsStreamRoute = Route$h.update({
14956
+ const ApiLogsStreamRoute = Route$k.update({
13233
14957
  id: "/stream",
13234
14958
  path: "/stream",
13235
14959
  getParentRoute: () => ApiLogsRoute
13236
14960
  });
13237
- const ApiLogsIdRoute = Route$g.update({
14961
+ const ApiLogsImportRoute = Route$j.update({
14962
+ id: "/import",
14963
+ path: "/import",
14964
+ getParentRoute: () => ApiLogsRoute
14965
+ });
14966
+ const ApiLogsIdRoute = Route$i.update({
13238
14967
  id: "/$id",
13239
14968
  path: "/$id",
13240
14969
  getParentRoute: () => ApiLogsRoute
13241
14970
  });
13242
- const ApiKnowledgeSearchRoute = Route$f.update({
14971
+ const ApiKnowledgeSearchRoute = Route$h.update({
13243
14972
  id: "/api/knowledge/search",
13244
14973
  path: "/api/knowledge/search",
13245
- getParentRoute: () => Route$z
14974
+ getParentRoute: () => Route$D
13246
14975
  });
13247
- const ApiKnowledgeProjectContextRoute = Route$e.update({
14976
+ const ApiKnowledgeProjectContextRoute = Route$g.update({
13248
14977
  id: "/api/knowledge/project-context",
13249
14978
  path: "/api/knowledge/project-context",
13250
- getParentRoute: () => Route$z
14979
+ getParentRoute: () => Route$D
13251
14980
  });
13252
- const ApiKnowledgeCandidatesRoute = Route$d.update({
14981
+ const ApiKnowledgeCandidatesRoute = Route$f.update({
13253
14982
  id: "/api/knowledge/candidates",
13254
14983
  path: "/api/knowledge/candidates",
13255
- getParentRoute: () => Route$z
14984
+ getParentRoute: () => Route$D
13256
14985
  });
13257
- const ApiGroupsGroupIdRoute = Route$c.update({
14986
+ const ApiGroupsGroupIdRoute = Route$e.update({
13258
14987
  id: "/$groupId",
13259
14988
  path: "/$groupId",
13260
14989
  getParentRoute: () => ApiGroupsRoute
13261
14990
  });
13262
- const ApiConfigPathsRoute = Route$b.update({
14991
+ const ApiConfigPathsRoute = Route$d.update({
13263
14992
  id: "/paths",
13264
14993
  path: "/paths",
13265
14994
  getParentRoute: () => ApiConfigRoute
13266
14995
  });
13267
- const ApiRunsRunIdEvidenceRoute = Route$a.update({
14996
+ const ApiAlertsSummaryRoute = Route$c.update({
14997
+ id: "/summary",
14998
+ path: "/summary",
14999
+ getParentRoute: () => ApiAlertsRoute
15000
+ });
15001
+ const ApiRunsRunIdEvidenceRoute = Route$b.update({
13268
15002
  id: "/evidence",
13269
15003
  path: "/evidence",
13270
15004
  getParentRoute: () => ApiRunsRunIdRoute
13271
15005
  });
13272
- const ApiProvidersProviderIdTestRoute = Route$9.update({
15006
+ const ApiProvidersProviderIdTestRoute = Route$a.update({
13273
15007
  id: "/test",
13274
15008
  path: "/test",
13275
15009
  getParentRoute: () => ApiProvidersProviderIdRoute
13276
15010
  });
13277
- const ApiProvidersProviderIdModelMetadataRoute = Route$8.update({
15011
+ const ApiProvidersProviderIdModelMetadataRoute = Route$9.update({
13278
15012
  id: "/model-metadata",
13279
15013
  path: "/model-metadata",
13280
15014
  getParentRoute: () => ApiProvidersProviderIdRoute
13281
15015
  });
13282
- const ApiLogsIdReplayRoute = Route$7.update({
15016
+ const ApiLogsIdReplayRoute = Route$8.update({
13283
15017
  id: "/replay",
13284
15018
  path: "/replay",
13285
15019
  getParentRoute: () => ApiLogsIdRoute
13286
15020
  });
13287
- const ApiLogsIdChunksRoute = Route$6.update({
15021
+ const ApiLogsIdChunksRoute = Route$7.update({
13288
15022
  id: "/chunks",
13289
15023
  path: "/chunks",
13290
15024
  getParentRoute: () => ApiLogsIdRoute
13291
15025
  });
15026
+ const ApiLogsIdBodyRoute = Route$6.update({
15027
+ id: "/body",
15028
+ path: "/body",
15029
+ getParentRoute: () => ApiLogsIdRoute
15030
+ });
13292
15031
  const ApiKnowledgeCandidatesCandidateIdRoute = Route$5.update({
13293
15032
  id: "/$candidateId",
13294
15033
  path: "/$candidateId",
@@ -13312,13 +15051,19 @@ const ApiProvidersProviderIdTestLogRoute = Route$2.update({
13312
15051
  const ApiKnowledgeSessionsSessionIdCandidatesRoute = Route$1.update({
13313
15052
  id: "/api/knowledge/sessions/$sessionId/candidates",
13314
15053
  path: "/api/knowledge/sessions/$sessionId/candidates",
13315
- getParentRoute: () => Route$z
15054
+ getParentRoute: () => Route$D
13316
15055
  });
13317
15056
  const ApiKnowledgeCandidatesCandidateIdPromoteRoute = Route.update({
13318
15057
  id: "/promote",
13319
15058
  path: "/promote",
13320
15059
  getParentRoute: () => ApiKnowledgeCandidatesCandidateIdRoute
13321
15060
  });
15061
+ const ApiAlertsRouteChildren = {
15062
+ ApiAlertsSummaryRoute
15063
+ };
15064
+ const ApiAlertsRouteWithChildren = ApiAlertsRoute._addFileChildren(
15065
+ ApiAlertsRouteChildren
15066
+ );
13322
15067
  const ApiConfigRouteChildren = {
13323
15068
  ApiConfigPathsRoute
13324
15069
  };
@@ -13337,6 +15082,7 @@ const ApiGroupsRouteWithChildren = ApiGroupsRoute._addFileChildren(
13337
15082
  ApiGroupsRouteChildren
13338
15083
  );
13339
15084
  const ApiLogsIdRouteChildren = {
15085
+ ApiLogsIdBodyRoute,
13340
15086
  ApiLogsIdChunksRoute,
13341
15087
  ApiLogsIdReplayRoute
13342
15088
  };
@@ -13345,6 +15091,7 @@ const ApiLogsIdRouteWithChildren = ApiLogsIdRoute._addFileChildren(
13345
15091
  );
13346
15092
  const ApiLogsRouteChildren = {
13347
15093
  ApiLogsIdRoute: ApiLogsIdRouteWithChildren,
15094
+ ApiLogsImportRoute,
13348
15095
  ApiLogsStreamRoute
13349
15096
  };
13350
15097
  const ApiLogsRouteWithChildren = ApiLogsRoute._addFileChildren(ApiLogsRouteChildren);
@@ -13394,6 +15141,7 @@ const ApiKnowledgeCandidatesRouteWithChildren = ApiKnowledgeCandidatesRoute._add
13394
15141
  );
13395
15142
  const rootRouteChildren = {
13396
15143
  IndexRoute,
15144
+ ApiAlertsRoute: ApiAlertsRouteWithChildren,
13397
15145
  ApiConfigRoute: ApiConfigRouteWithChildren,
13398
15146
  ApiGroupsRoute: ApiGroupsRouteWithChildren,
13399
15147
  ApiHealthRoute,
@@ -13410,7 +15158,7 @@ const rootRouteChildren = {
13410
15158
  ApiKnowledgeSearchRoute,
13411
15159
  ApiKnowledgeSessionsSessionIdCandidatesRoute
13412
15160
  };
13413
- const routeTree = Route$z._addFileChildren(rootRouteChildren)._addFileTypes();
15161
+ const routeTree = Route$D._addFileChildren(rootRouteChildren)._addFileTypes();
13414
15162
  function getRouter() {
13415
15163
  const router2 = createRouter({
13416
15164
  routeTree,
@@ -13442,32 +15190,35 @@ export {
13442
15190
  GroupEvidenceReadResponseSchema as G,
13443
15191
  InspectorGroupsListResponseSchema as I,
13444
15192
  KnowledgeCandidateSchema as K,
15193
+ LogBodyChunkSchema as L,
13445
15194
  MAX_SLOW_RESPONSE_THRESHOLD_SECONDS as M,
13446
15195
  OpenAIRequestSchema as O,
13447
15196
  ProviderConfigSchema as P,
13448
- Route$x as R,
15197
+ Route$B as R,
13449
15198
  TimeDisplayFormatSchema as T,
13450
15199
  DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS as a,
13451
15200
  DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS as b,
13452
15201
  DEFAULT_TIME_DISPLAY_FORMAT as c,
13453
15202
  RuntimeConfigSchema as d,
13454
15203
  AnthropicRequestSchema as e,
13455
- apiFormatForPath as f,
13456
- getSessionPath as g,
13457
- stripClaudeCodeBillingHeader as h,
13458
- GroupEvidenceExportResultSchema as i,
13459
- DeleteInspectorGroupResponseSchema as j,
13460
- providerHasContextMetadata as k,
13461
- findProviderModelMetadata as l,
13462
- maskApiKey as m,
13463
- createPendingProviderTestResults as n,
13464
- ProviderTestResultsSchema as o,
13465
- parseOpenAIResponse as p,
13466
- createFailedProviderTestResults as q,
13467
- MAX_PROVIDER_TEST_TIMEOUT_SECONDS as r,
15204
+ parseOpenAIResponse as f,
15205
+ apiFormatForPath as g,
15206
+ getSessionPath as h,
15207
+ stripClaudeCodeBillingHeader as i,
15208
+ AlertSummarySchema as j,
15209
+ AlertListResponseSchema as k,
15210
+ GroupEvidenceExportResultSchema as l,
15211
+ DeleteInspectorGroupResponseSchema as m,
15212
+ providerHasContextMetadata as n,
15213
+ findProviderModelMetadata as o,
15214
+ packageJson as p,
15215
+ maskApiKey as q,
15216
+ createPendingProviderTestResults as r,
13468
15217
  safeGetOwnProperty as s,
13469
- resolveProviderContextWindow as t,
13470
- isPlainRecord as u,
13471
- packageJson as v,
13472
- router as w
15218
+ ProviderTestResultsSchema as t,
15219
+ createFailedProviderTestResults as u,
15220
+ MAX_PROVIDER_TEST_TIMEOUT_SECONDS as v,
15221
+ resolveProviderContextWindow as w,
15222
+ isPlainRecord as x,
15223
+ router as y
13473
15224
  };