@tonyclaw/agent-inspector 2.0.25 → 2.0.27

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 (45) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-DVNvS0GQ.js → CompareDrawer-B_p6vmMQ.js} +1 -1
  3. package/.output/public/assets/{ProxyViewerContainer-By3XtQhZ.js → ProxyViewerContainer-DItqh1EO.js} +4 -4
  4. package/.output/public/assets/{ReplayDialog-Cw6bN-NR.js → ReplayDialog-BmprJ6D3.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-DnIsirBE.js → RequestAnatomy-DqO0_SVJ.js} +1 -1
  6. package/.output/public/assets/{ResponseView-ygsUs7FU.js → ResponseView-FZbPNdHa.js} +1 -1
  7. package/.output/public/assets/{StreamingChunkSequence-DMUn8qXi.js → StreamingChunkSequence-BZPzfJWj.js} +1 -1
  8. package/.output/public/assets/_sessionId-y2ATIp51.js +1 -0
  9. package/.output/public/assets/index-CQT4higx.js +1 -0
  10. package/.output/public/assets/{main-kBQ49n13.js → main-CzItFZlM.js} +2 -2
  11. package/.output/server/_libs/modelcontextprotocol__server.mjs +228 -0
  12. package/.output/server/{_sessionId-CYKaI68v.mjs → _sessionId-DZH8SrEZ.mjs} +3 -3
  13. package/.output/server/_ssr/{CompareDrawer-CHk4p41X.mjs → CompareDrawer-L0aE1UV1.mjs} +2 -2
  14. package/.output/server/_ssr/{ProxyViewerContainer-Co9ENCKV.mjs → ProxyViewerContainer-B62RB9ER.mjs} +7 -7
  15. package/.output/server/_ssr/{ReplayDialog-BRs3fk8H.mjs → ReplayDialog-FVWnpx2C.mjs} +3 -3
  16. package/.output/server/_ssr/{RequestAnatomy-Cb68EeZz.mjs → RequestAnatomy-DIyqW-Ny.mjs} +2 -2
  17. package/.output/server/_ssr/{ResponseView-DGdbfEjs.mjs → ResponseView-BX4mxEZ5.mjs} +2 -2
  18. package/.output/server/_ssr/{StreamingChunkSequence-DptYgirK.mjs → StreamingChunkSequence-Cs3nzOor.mjs} +2 -2
  19. package/.output/server/_ssr/{index-Ef5mCuww.mjs → index-Dik2Mc3h.mjs} +2 -2
  20. package/.output/server/_ssr/index.mjs +2 -2
  21. package/.output/server/_ssr/{router-Cvv7SJnd.mjs → router-DCPg8ykx.mjs} +2087 -80
  22. package/.output/server/_tanstack-start-manifest_v-BaoL3JCh.mjs +4 -0
  23. package/.output/server/index.mjs +61 -61
  24. package/README.md +55 -1
  25. package/package.json +1 -1
  26. package/src/lib/runContract.ts +162 -0
  27. package/src/lib/sessionInfoContract.ts +69 -0
  28. package/src/mcp/server.ts +580 -2
  29. package/src/mcp/toolHandlers.ts +181 -0
  30. package/src/proxy/evidenceAnalysis.ts +522 -0
  31. package/src/proxy/evidenceExporter.ts +215 -0
  32. package/src/proxy/logSearch.ts +118 -0
  33. package/src/proxy/runFailures.ts +100 -0
  34. package/src/proxy/runStore.ts +159 -0
  35. package/src/proxy/sessionInfo.ts +222 -0
  36. package/src/proxy/sessionSupervisor.ts +8 -5
  37. package/src/proxy/store.ts +77 -0
  38. package/src/routes/api/logs.ts +16 -0
  39. package/src/routes/api/runs.$runId.evidence.ts +62 -0
  40. package/src/routes/api/runs.$runId.ts +50 -0
  41. package/src/routes/api/runs.ts +58 -0
  42. package/src/routes/api/sessions.ts +29 -2
  43. package/.output/public/assets/_sessionId-DoqvnFx7.js +0 -1
  44. package/.output/public/assets/index-B0YcFeL-.js +0 -1
  45. package/.output/server/_tanstack-start-manifest_v-DQoXxIFQ.mjs +0 -4
@@ -1,11 +1,11 @@
1
1
  import { c as createRouter, a as createRootRoute, b as createFileRoute, l as lazyRouteComponent, O as Outlet, H as HeadContent, S as Scripts } from "../_libs/tanstack__react-router.mjs";
2
2
  import { j as jsxRuntimeExports } from "../_libs/react.mjs";
3
3
  import { S as SWRConfig } from "../_libs/swr.mjs";
4
- import { existsSync, readFileSync, mkdirSync, writeFileSync, renameSync, copyFileSync, unlinkSync, readdirSync, statSync, createReadStream, cpSync, rmSync, createWriteStream } from "node:fs";
4
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, renameSync, copyFileSync, unlinkSync, createReadStream, readdirSync, statSync, cpSync, rmSync, createWriteStream } from "node:fs";
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 } from "node:path";
8
+ import path, { join, dirname, isAbsolute } 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";
@@ -13,9 +13,9 @@ import { randomUUID } from "crypto";
13
13
  import { promisify } from "node:util";
14
14
  import { Worker } from "node:worker_threads";
15
15
  import { randomUUID as randomUUID$1 } from "node:crypto";
16
- import { M as McpServer, W as WebStandardStreamableHTTPServerTransport } from "../_libs/modelcontextprotocol__server.mjs";
16
+ import { M as McpServer, W as WebStandardStreamableHTTPServerTransport, R as ResourceTemplate } from "../_libs/modelcontextprotocol__server.mjs";
17
17
  import { homedir } from "node:os";
18
- import { d as object, b as string, a as array, _ as _enum, u as union, n as number, c as boolean, e as unknown, l as literal, r as record, g as discriminatedUnion, h as _null, k as lazy } from "../_libs/zod.mjs";
18
+ import { d as object, b as string, a as array, r as record, _ as _enum, u as union, n as number, c as boolean, e as unknown, l as literal, k as lazy, g as discriminatedUnion, h as _null } from "../_libs/zod.mjs";
19
19
  import "../_libs/tiny-warning.mjs";
20
20
  import "../_libs/tanstack__router-core.mjs";
21
21
  import "../_libs/cookie-es.mjs";
@@ -51,7 +51,7 @@ import "../_libs/semver.mjs";
51
51
  import "../_libs/uint8array-extras.mjs";
52
52
  const faviconSvg = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2064%2064'%20role='img'%20aria-label='Agent%20Inspector'%3e%3crect%20width='64'%20height='64'%20rx='14'%20fill='%23111827'%20/%3e%3cg%20fill='none'%20stroke='%23f59e0b'%20stroke-width='4.2'%20stroke-linecap='round'%20stroke-linejoin='round'%3e%3cpath%20fill='%23f59e0b'%20d='M15%2036c0-11%207-18%2017-18s17%207%2017%2018c0%208-7%2013-17%2013s-17-5-17-13z'%20/%3e%3cpath%20d='M16%2031c-6-5-12-3-12%204%200%205%206%206%2011%202'%20/%3e%3cpath%20d='M48%2031c6-5%2012-3%2012%204%200%205-6%206-11%202'%20/%3e%3cpath%20d='M27%2019l-3-7'%20/%3e%3cpath%20d='M37%2019l3-7'%20/%3e%3cpath%20d='M19%2045l-6%209'%20/%3e%3cpath%20d='M27%2048l-3%209'%20/%3e%3cpath%20d='M37%2048l3%209'%20/%3e%3cpath%20d='M45%2045l6%209'%20/%3e%3c/g%3e%3cpath%20d='M14%2047l9-8%208%209c-5%203.5-12%203-17-1z'%20fill='%232f6b3f'%20opacity='.95'%20/%3e%3cpath%20d='M18%2046l5-5%205%206c-3%201.6-7%201.4-10-1z'%20fill='%236fb36f'%20opacity='.95'%20/%3e%3cpath%20d='M23%2041v10'%20fill='none'%20stroke='%23d6b45f'%20stroke-width='1.6'%20stroke-linecap='round'%20/%3e%3ccircle%20cx='31'%20cy='48'%20r='1.8'%20fill='%23c2412d'%20/%3e%3cpath%20d='M24%2044l13-11%2014%2013c-8%205.8-18%205.3-27-2z'%20fill='%232f6b3f'%20opacity='.97'%20/%3e%3cpath%20d='M30%2043l7-7%209%209c-5%202.7-11%202.5-16-2z'%20fill='%236fb36f'%20opacity='.95'%20/%3e%3cpath%20d='M37%2036v14'%20fill='none'%20stroke='%23d6b45f'%20stroke-width='2'%20stroke-linecap='round'%20/%3e%3cpath%20d='M30%2043c5-2%2011-1.5%2016%202'%20fill='none'%20stroke='%239fca78'%20stroke-width='1.8'%20stroke-linecap='round'%20/%3e%3ccircle%20cx='51'%20cy='46'%20r='2'%20fill='%23c2412d'%20/%3e%3cpath%20d='M40%2050l8-8%207%208c-4.2%203.2-10%203-15%200z'%20fill='%232f6b3f'%20opacity='.95'%20/%3e%3cpath%20d='M43%2049l5-5%204.5%205.5c-3%201.5-6%201.2-9.5-.5z'%20fill='%236fb36f'%20opacity='.95'%20/%3e%3cpath%20d='M48%2044v9'%20fill='none'%20stroke='%23d6b45f'%20stroke-width='1.5'%20stroke-linecap='round'%20/%3e%3ccircle%20cx='55'%20cy='50'%20r='1.7'%20fill='%23c2412d'%20/%3e%3ccircle%20cx='24'%20cy='11'%20r='3.2'%20fill='%23f59e0b'%20/%3e%3ccircle%20cx='40'%20cy='11'%20r='3.2'%20fill='%23f59e0b'%20/%3e%3ccircle%20cx='25'%20cy='34'%20r='2.1'%20fill='%23111827'%20/%3e%3ccircle%20cx='39'%20cy='34'%20r='2.1'%20fill='%23111827'%20/%3e%3c/svg%3e";
53
53
  const appCss = "/assets/index-DsiKfWCp.css";
54
- const Route$s = createRootRoute({
54
+ const Route$v = createRootRoute({
55
55
  head: () => ({
56
56
  meta: [
57
57
  { charSet: "utf-8" },
@@ -93,8 +93,8 @@ function RootDocument({ children }) {
93
93
  ] })
94
94
  ] });
95
95
  }
96
- const $$splitComponentImporter$1 = () => import("./index-Ef5mCuww.mjs");
97
- const Route$r = createFileRoute("/")({
96
+ const $$splitComponentImporter$1 = () => import("./index-Dik2Mc3h.mjs");
97
+ const Route$u = createFileRoute("/")({
98
98
  component: lazyRouteComponent($$splitComponentImporter$1, "component")
99
99
  });
100
100
  const B64URL_RE = /^[A-Za-z0-9_-]+$/;
@@ -136,8 +136,8 @@ function decodeSessionIdFromPath(encoded) {
136
136
  function getSessionPath(sessionId) {
137
137
  return `/session/${encodeSessionIdForPath(sessionId)}`;
138
138
  }
139
- const $$splitComponentImporter = () => import("../_sessionId-CYKaI68v.mjs");
140
- const Route$q = createFileRoute("/session/$sessionId")({
139
+ const $$splitComponentImporter = () => import("../_sessionId-DZH8SrEZ.mjs");
140
+ const Route$t = createFileRoute("/session/$sessionId")({
141
141
  component: lazyRouteComponent($$splitComponentImporter, "component"),
142
142
  parseParams: (params) => ({
143
143
  sessionId: decodeSessionIdFromPath(params.sessionId)
@@ -1030,6 +1030,184 @@ function getCurrentLogFile() {
1030
1030
  const dd = String(now.getUTCDate()).padStart(2, "0");
1031
1031
  return `${yyyy}-${mm}-${dd}.jsonl`;
1032
1032
  }
1033
+ const SESSION_INFO_DEFAULT_LATEST_LOG_LIMIT = 10;
1034
+ const SESSION_INFO_MAX_LATEST_LOG_LIMIT = 50;
1035
+ function clampSessionInfoLogLimit(input) {
1036
+ if (input === void 0) return SESSION_INFO_DEFAULT_LATEST_LOG_LIMIT;
1037
+ if (!Number.isFinite(input) || input < 1) return 1;
1038
+ return Math.min(Math.floor(input), SESSION_INFO_MAX_LATEST_LOG_LIMIT);
1039
+ }
1040
+ function normalizeString(value) {
1041
+ if (value === null || value === void 0 || value.length === 0) return null;
1042
+ return value;
1043
+ }
1044
+ function truncateError(value) {
1045
+ const normalized = normalizeString(value);
1046
+ if (normalized === null) return null;
1047
+ if (normalized.length <= 500) return normalized;
1048
+ return `${normalized.slice(0, 500)}...`;
1049
+ }
1050
+ function hasLogError(log) {
1051
+ const error = normalizeString(log.error);
1052
+ if (error !== null) return true;
1053
+ return log.responseStatus !== null && log.responseStatus >= 400;
1054
+ }
1055
+ function buildSessionLogSummary(log) {
1056
+ return {
1057
+ id: log.id,
1058
+ timestamp: log.timestamp,
1059
+ provider: normalizeString(log.providerName),
1060
+ model: normalizeString(log.model),
1061
+ apiFormat: log.apiFormat,
1062
+ method: log.method,
1063
+ path: log.path,
1064
+ status: log.responseStatus,
1065
+ isStreaming: log.streaming,
1066
+ hasError: hasLogError(log),
1067
+ error: truncateError(log.error),
1068
+ latencyMs: log.elapsedMs,
1069
+ tokens: {
1070
+ input: log.inputTokens,
1071
+ output: log.outputTokens,
1072
+ cacheCreate: log.cacheCreationInputTokens,
1073
+ cacheRead: log.cacheReadInputTokens
1074
+ },
1075
+ sessionId: normalizeString(log.sessionId)
1076
+ };
1077
+ }
1078
+ function nonNegativeInteger(value) {
1079
+ if (value === null || !Number.isFinite(value) || value < 0) return 0;
1080
+ return Math.floor(value);
1081
+ }
1082
+ function addTokenUsage(total, summary) {
1083
+ const input = nonNegativeInteger(summary.tokens.input);
1084
+ const output = nonNegativeInteger(summary.tokens.output);
1085
+ const cacheCreate = nonNegativeInteger(summary.tokens.cacheCreate);
1086
+ const cacheRead = nonNegativeInteger(summary.tokens.cacheRead);
1087
+ return {
1088
+ input: total.input + input,
1089
+ output: total.output + output,
1090
+ cacheCreate: total.cacheCreate + cacheCreate,
1091
+ cacheRead: total.cacheRead + cacheRead,
1092
+ total: total.total + input + output + cacheCreate + cacheRead
1093
+ };
1094
+ }
1095
+ function buildRelativePath(pathname, params) {
1096
+ const query = params.toString();
1097
+ return query.length > 0 ? `${pathname}?${query}` : pathname;
1098
+ }
1099
+ function buildAbsoluteUrl(baseUrl, path2) {
1100
+ try {
1101
+ return new URL(path2, baseUrl).toString();
1102
+ } catch {
1103
+ return path2;
1104
+ }
1105
+ }
1106
+ function buildApiPath(sessionId) {
1107
+ return buildRelativePath("/api/sessions", new URLSearchParams({ sessionId }));
1108
+ }
1109
+ function buildCompactLogsPath(sessionId) {
1110
+ return buildRelativePath("/api/logs", new URLSearchParams({ compact: "1", sessionId }));
1111
+ }
1112
+ function collectSortedValues(values) {
1113
+ const set = /* @__PURE__ */ new Set();
1114
+ for (const value of values) {
1115
+ if (value !== null && value.length > 0) {
1116
+ set.add(value);
1117
+ }
1118
+ }
1119
+ return [...set].sort();
1120
+ }
1121
+ function countCompletedLogs(summaries) {
1122
+ let count = 0;
1123
+ for (const summary of summaries) {
1124
+ if (summary.status !== null || summary.latencyMs !== null) {
1125
+ count += 1;
1126
+ }
1127
+ }
1128
+ return count;
1129
+ }
1130
+ function countErrorLogs(summaries) {
1131
+ let count = 0;
1132
+ for (const summary of summaries) {
1133
+ if (summary.hasError) {
1134
+ count += 1;
1135
+ }
1136
+ }
1137
+ return count;
1138
+ }
1139
+ function deriveSessionStatus(input) {
1140
+ if (input.activeRequests > 0 || input.runningTasks > 0) return "active";
1141
+ if (input.failedRequests > 0 || input.errorCount > 0 || input.lastTaskError !== null) {
1142
+ return "failed";
1143
+ }
1144
+ if (input.completedRequests > 0 || input.logCount > 0) return "completed";
1145
+ return "empty";
1146
+ }
1147
+ function buildSessionInfo(input) {
1148
+ const latestLogLimit = clampSessionInfoLogLimit(input.latestLogLimit);
1149
+ const summaries = [...input.summaries].sort((left, right) => right.id - left.id);
1150
+ const latestLog = summaries[0] ?? null;
1151
+ const oldestLog = summaries.length > 0 ? summaries[summaries.length - 1] : null;
1152
+ const logCount = input.logCoverage === "history" ? summaries.length : Math.max(input.snapshot?.requestCount ?? 0, summaries.length);
1153
+ const errorCount = Math.max(countErrorLogs(summaries), input.snapshot?.failedRequests ?? 0);
1154
+ const completedLogCount = countCompletedLogs(summaries);
1155
+ const tokenUsage = summaries.reduce(
1156
+ (total, summary) => addTokenUsage(total, summary),
1157
+ { input: 0, output: 0, cacheCreate: 0, cacheRead: 0, total: 0 }
1158
+ );
1159
+ const requestCount = Math.max(input.snapshot?.requestCount ?? 0, logCount);
1160
+ const activeRequests = input.snapshot?.activeRequests ?? 0;
1161
+ const completedRequests = Math.max(input.snapshot?.completedRequests ?? 0, completedLogCount);
1162
+ const failedRequests = Math.max(input.snapshot?.failedRequests ?? 0, errorCount);
1163
+ const queuedTasks = input.snapshot?.queuedTasks ?? 0;
1164
+ const runningTasks = input.snapshot?.runningTasks ?? 0;
1165
+ const lastTaskError = normalizeString(input.snapshot?.lastTaskError);
1166
+ const inspectorPath = getSessionPath(input.sessionId);
1167
+ const apiPath = buildApiPath(input.sessionId);
1168
+ const compactLogsPath = buildCompactLogsPath(input.sessionId);
1169
+ return {
1170
+ id: input.sessionId,
1171
+ status: deriveSessionStatus({
1172
+ activeRequests,
1173
+ runningTasks,
1174
+ failedRequests,
1175
+ errorCount,
1176
+ lastTaskError,
1177
+ completedRequests,
1178
+ logCount
1179
+ }),
1180
+ logCoverage: input.logCoverage,
1181
+ source: input.snapshot?.source ?? null,
1182
+ runtimeMode: input.snapshot?.runtimeMode ?? null,
1183
+ createdAt: input.snapshot?.createdAt ?? oldestLog?.timestamp ?? null,
1184
+ updatedAt: input.snapshot?.updatedAt ?? latestLog?.timestamp ?? null,
1185
+ requestCount,
1186
+ activeRequests,
1187
+ completedRequests,
1188
+ failedRequests,
1189
+ queuedTasks,
1190
+ runningTasks,
1191
+ lastTaskError,
1192
+ logCount,
1193
+ errorCount,
1194
+ models: collectSortedValues(summaries.map((summary) => summary.model)),
1195
+ providers: collectSortedValues(summaries.map((summary) => summary.provider)),
1196
+ tokenUsage,
1197
+ lastLogId: input.snapshot?.lastLogId ?? latestLog?.id ?? null,
1198
+ lastModel: normalizeString(input.snapshot?.lastModel) ?? latestLog?.model ?? null,
1199
+ clientPid: input.snapshot?.clientPid ?? null,
1200
+ clientProjectFolder: normalizeString(input.snapshot?.clientProjectFolder),
1201
+ inspectorPath,
1202
+ inspectorUrl: buildAbsoluteUrl(input.baseUrl, inspectorPath),
1203
+ apiPath,
1204
+ apiUrl: buildAbsoluteUrl(input.baseUrl, apiPath),
1205
+ compactLogsPath,
1206
+ compactLogsUrl: buildAbsoluteUrl(input.baseUrl, compactLogsPath),
1207
+ latestLogLimit,
1208
+ latestLogs: summaries.slice(0, latestLogLimit)
1209
+ };
1210
+ }
1033
1211
  const IDLE_TIMEOUT_MS = Number(process.env["SESSION_PROCESS_IDLE_MS"]) || 5 * 60 * 1e3;
1034
1212
  const MAX_RESTARTS = 3;
1035
1213
  const _processes = /* @__PURE__ */ new Map();
@@ -1222,11 +1400,14 @@ function upsertSession(log, sourceOverride) {
1222
1400
  if (source === null) return null;
1223
1401
  const existing = sessions.get(identity.id);
1224
1402
  const session = existing ?? createSessionRecord(identity.id, source, log);
1225
- session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1226
- session.lastLogId = log.id;
1227
- session.lastModel = log.model ?? session.lastModel;
1228
- session.clientPid = log.clientPid ?? session.clientPid;
1229
- session.clientProjectFolder = log.clientProjectFolder ?? session.clientProjectFolder;
1403
+ const isSameOrNewerLog = session.lastLogId === null || log.id >= session.lastLogId;
1404
+ if (isSameOrNewerLog) {
1405
+ session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1406
+ session.lastLogId = log.id;
1407
+ session.lastModel = log.model ?? session.lastModel;
1408
+ session.clientPid = log.clientPid ?? session.clientPid;
1409
+ session.clientProjectFolder = log.clientProjectFolder ?? session.clientProjectFolder;
1410
+ }
1230
1411
  sessions.set(identity.id, session);
1231
1412
  return session;
1232
1413
  }
@@ -1644,6 +1825,53 @@ async function listLogsPage(options) {
1644
1825
  limit: options.limit
1645
1826
  };
1646
1827
  }
1828
+ function findSessionSnapshot(sessionId) {
1829
+ for (const snapshot of getSessionSnapshots()) {
1830
+ if (snapshot.id === sessionId) return snapshot;
1831
+ }
1832
+ return null;
1833
+ }
1834
+ async function collectSessionLogSummaries(sessionId, includeHistory) {
1835
+ const summariesById = /* @__PURE__ */ new Map();
1836
+ const observeMatchingLog = (log) => {
1837
+ if (!matchesLogFilters(log, sessionId, void 0)) return;
1838
+ summariesById.set(log.id, buildSessionLogSummary(log));
1839
+ };
1840
+ if (includeHistory) {
1841
+ await visitPersistedLogs((log) => {
1842
+ observeSessionLog(log);
1843
+ observeMatchingLog(log);
1844
+ });
1845
+ }
1846
+ for (const log of memoryCache.values()) {
1847
+ observeMatchingLog(log);
1848
+ }
1849
+ return [...summariesById.values()];
1850
+ }
1851
+ async function includeSnapshotLastLogSummary(sessionId, snapshot, summaries) {
1852
+ if (snapshot === null || snapshot.lastLogId === null) return summaries;
1853
+ for (const summary of summaries) {
1854
+ if (summary.id === snapshot.lastLogId) return summaries;
1855
+ }
1856
+ const log = await getLogById(snapshot.lastLogId);
1857
+ if (log === null || !matchesLogFilters(log, sessionId, void 0)) return summaries;
1858
+ return [...summaries, buildSessionLogSummary(log)];
1859
+ }
1860
+ async function getSessionInfo(sessionId, options) {
1861
+ const includeHistory = options.includeHistory === true;
1862
+ const collectedSummaries = await collectSessionLogSummaries(sessionId, includeHistory);
1863
+ const snapshot = findSessionSnapshot(sessionId);
1864
+ const summaries = includeHistory ? collectedSummaries : await includeSnapshotLastLogSummary(sessionId, snapshot, collectedSummaries);
1865
+ if (snapshot === null && summaries.length === 0) return null;
1866
+ return buildSessionInfo({
1867
+ sessionId,
1868
+ baseUrl: options.baseUrl,
1869
+ snapshot,
1870
+ summaries,
1871
+ logCoverage: includeHistory ? "history" : "recent",
1872
+ latestLogLimit: options.latestLogLimit
1873
+ });
1874
+ }
1647
1875
  function getSessions() {
1648
1876
  return getSessionIds();
1649
1877
  }
@@ -4644,7 +4872,7 @@ async function handleProxy(req) {
4644
4872
  }
4645
4873
  return handleStreamingResponse(upstreamRes, req, startTime, upstreamUrl, log, captureFullDetails);
4646
4874
  }
4647
- const Route$p = createFileRoute("/proxy/$")({
4875
+ const Route$s = createFileRoute("/proxy/$")({
4648
4876
  server: {
4649
4877
  handlers: {
4650
4878
  GET: ({ request }) => handleProxy(request),
@@ -4656,11 +4884,35 @@ const Route$p = createFileRoute("/proxy/$")({
4656
4884
  }
4657
4885
  }
4658
4886
  });
4659
- const Route$o = createFileRoute("/api/sessions")({
4887
+ function parsePositiveInt$2(value) {
4888
+ if (value === null) return void 0;
4889
+ const parsed = Number(value);
4890
+ if (!Number.isInteger(parsed) || parsed < 1) return void 0;
4891
+ return parsed;
4892
+ }
4893
+ function parseBooleanFlag(value) {
4894
+ return value === "1" || value === "true";
4895
+ }
4896
+ const Route$r = createFileRoute("/api/sessions")({
4660
4897
  server: {
4661
4898
  handlers: {
4662
- GET: ({ request }) => {
4899
+ GET: async ({ request }) => {
4663
4900
  const url = new URL(request.url);
4901
+ const sessionId = url.searchParams.get("sessionId");
4902
+ if (sessionId !== null) {
4903
+ if (sessionId.length === 0) {
4904
+ return Response.json({ error: "sessionId is required" }, { status: 400 });
4905
+ }
4906
+ const info = await getSessionInfo(sessionId, {
4907
+ baseUrl: url.origin,
4908
+ includeHistory: parseBooleanFlag(url.searchParams.get("includeHistory")),
4909
+ latestLogLimit: parsePositiveInt$2(url.searchParams.get("limit"))
4910
+ });
4911
+ if (info === null) {
4912
+ return Response.json({ error: "Session not found" }, { status: 404 });
4913
+ }
4914
+ return Response.json(info);
4915
+ }
4664
4916
  const details = url.searchParams.get("details");
4665
4917
  if (details === "true" || details === "1") {
4666
4918
  return Response.json(getSessionSnapshots());
@@ -4670,6 +4922,415 @@ const Route$o = createFileRoute("/api/sessions")({
4670
4922
  }
4671
4923
  }
4672
4924
  });
4925
+ const SessionTokenUsageSchema = object({
4926
+ input: number().int().nonnegative(),
4927
+ output: number().int().nonnegative(),
4928
+ cacheCreate: number().int().nonnegative(),
4929
+ cacheRead: number().int().nonnegative(),
4930
+ total: number().int().nonnegative()
4931
+ });
4932
+ const SessionLogSummarySchema = object({
4933
+ id: number().int().positive(),
4934
+ timestamp: string(),
4935
+ provider: string().nullable(),
4936
+ model: string().nullable(),
4937
+ apiFormat: _enum(["anthropic", "openai", "unknown"]),
4938
+ method: string(),
4939
+ path: string(),
4940
+ status: number().nullable(),
4941
+ isStreaming: boolean(),
4942
+ hasError: boolean(),
4943
+ error: string().nullable(),
4944
+ latencyMs: number().nullable(),
4945
+ tokens: object({
4946
+ input: number().nullable(),
4947
+ output: number().nullable(),
4948
+ cacheCreate: number().nullable(),
4949
+ cacheRead: number().nullable()
4950
+ }),
4951
+ sessionId: string().nullable()
4952
+ });
4953
+ const SessionInfoSchema = object({
4954
+ id: string(),
4955
+ status: _enum(["active", "failed", "completed", "empty"]),
4956
+ logCoverage: _enum(["recent", "history"]),
4957
+ source: _enum(["explicit", "client-process", "client-connection", "provider-test"]).nullable(),
4958
+ runtimeMode: _enum(["in-process", "worker-thread", "child-process"]).nullable(),
4959
+ createdAt: string().nullable(),
4960
+ updatedAt: string().nullable(),
4961
+ requestCount: number().int().nonnegative(),
4962
+ activeRequests: number().int().nonnegative(),
4963
+ completedRequests: number().int().nonnegative(),
4964
+ failedRequests: number().int().nonnegative(),
4965
+ queuedTasks: number().int().nonnegative(),
4966
+ runningTasks: number().int().nonnegative(),
4967
+ lastTaskError: string().nullable(),
4968
+ logCount: number().int().nonnegative(),
4969
+ errorCount: number().int().nonnegative(),
4970
+ models: array(string()),
4971
+ providers: array(string()),
4972
+ tokenUsage: SessionTokenUsageSchema,
4973
+ lastLogId: number().nullable(),
4974
+ lastModel: string().nullable(),
4975
+ clientPid: number().nullable(),
4976
+ clientProjectFolder: string().nullable(),
4977
+ inspectorPath: string(),
4978
+ inspectorUrl: string(),
4979
+ apiPath: string(),
4980
+ apiUrl: string(),
4981
+ compactLogsPath: string(),
4982
+ compactLogsUrl: string(),
4983
+ latestLogLimit: number().int().positive(),
4984
+ latestLogs: array(SessionLogSummarySchema)
4985
+ });
4986
+ const InspectorRunStatusSchema = _enum([
4987
+ "created",
4988
+ "running",
4989
+ "completed",
4990
+ "failed",
4991
+ "cancelled"
4992
+ ]);
4993
+ const InspectorRunEvidenceSchema = object({
4994
+ jsonPath: string(),
4995
+ markdownPath: string(),
4996
+ htmlPath: string(),
4997
+ exportedAt: string()
4998
+ });
4999
+ const RunTimelineEventKindSchema = _enum([
5000
+ "run-created",
5001
+ "session-started",
5002
+ "request",
5003
+ "session-finished",
5004
+ "evidence-exported"
5005
+ ]);
5006
+ const RunTimelineEventSeveritySchema = _enum(["info", "success", "warning", "error"]);
5007
+ const RunTimelineEventSchema = object({
5008
+ timestamp: string(),
5009
+ kind: RunTimelineEventKindSchema,
5010
+ severity: RunTimelineEventSeveritySchema,
5011
+ title: string(),
5012
+ details: string(),
5013
+ logId: number().int().positive().nullable(),
5014
+ status: number().nullable(),
5015
+ model: string().nullable(),
5016
+ provider: string().nullable(),
5017
+ latencyMs: number().nullable(),
5018
+ isStreaming: boolean().nullable()
5019
+ });
5020
+ const FailureCategorySchema = _enum([
5021
+ "none",
5022
+ "active",
5023
+ "provider-timeout",
5024
+ "provider-http-error",
5025
+ "provider-auth-error",
5026
+ "provider-rate-limit",
5027
+ "network-or-container",
5028
+ "streaming-mismatch",
5029
+ "context-pressure",
5030
+ "runtime-task-error",
5031
+ "model-output-error",
5032
+ "unknown-failure",
5033
+ "no-session-data"
5034
+ ]);
5035
+ const FailureClassificationSchema = object({
5036
+ outcome: _enum(["success", "failure", "active", "unknown"]),
5037
+ severity: _enum(["none", "low", "medium", "high"]),
5038
+ category: FailureCategorySchema,
5039
+ summary: string(),
5040
+ evidenceLogIds: array(number().int().positive()),
5041
+ hints: array(string())
5042
+ });
5043
+ const JenkinsReportSchema = object({
5044
+ status: _enum(["PASS", "FAIL", "RUNNING", "UNKNOWN"]),
5045
+ summary: string(),
5046
+ markdown: string()
5047
+ });
5048
+ const InspectorRunSchema = object({
5049
+ id: string(),
5050
+ sessionId: string(),
5051
+ title: string(),
5052
+ task: string().nullable(),
5053
+ project: string().nullable(),
5054
+ agent: string().nullable(),
5055
+ status: InspectorRunStatusSchema,
5056
+ tags: array(string()),
5057
+ metadata: record(string(), JsonValueSchema),
5058
+ evidence: InspectorRunEvidenceSchema.nullable(),
5059
+ createdAt: string(),
5060
+ updatedAt: string()
5061
+ });
5062
+ const CreateInspectorRunInputSchema = object({
5063
+ runId: string().min(1).optional(),
5064
+ sessionId: string().min(1).optional(),
5065
+ title: string().min(1).optional(),
5066
+ task: string().nullable().optional(),
5067
+ project: string().nullable().optional(),
5068
+ agent: string().nullable().optional(),
5069
+ status: InspectorRunStatusSchema.optional(),
5070
+ tags: array(string()).optional(),
5071
+ metadata: record(string(), JsonValueSchema).optional()
5072
+ }).optional();
5073
+ const UpdateInspectorRunInputSchema = object({
5074
+ sessionId: string().min(1).optional(),
5075
+ title: string().min(1).optional(),
5076
+ task: string().nullable().optional(),
5077
+ project: string().nullable().optional(),
5078
+ agent: string().nullable().optional(),
5079
+ status: InspectorRunStatusSchema.optional(),
5080
+ tags: array(string()).optional(),
5081
+ metadata: record(string(), JsonValueSchema).optional()
5082
+ });
5083
+ const EvidenceExportOptionsSchema = object({
5084
+ includeHistory: boolean().optional(),
5085
+ latestLogLimit: number().int().positive().max(50).optional()
5086
+ }).optional();
5087
+ const EvidenceExportResultSchema = object({
5088
+ run: InspectorRunSchema,
5089
+ evidence: InspectorRunEvidenceSchema,
5090
+ session: SessionInfoSchema.nullable(),
5091
+ timeline: array(RunTimelineEventSchema),
5092
+ classification: FailureClassificationSchema,
5093
+ jenkinsReport: JenkinsReportSchema
5094
+ });
5095
+ const RecentFailureSchema = object({
5096
+ run: InspectorRunSchema,
5097
+ classification: FailureClassificationSchema,
5098
+ evidence: InspectorRunEvidenceSchema.nullable(),
5099
+ jenkinsReport: JenkinsReportSchema.nullable(),
5100
+ sessionId: string(),
5101
+ inspectorUrl: string().nullable(),
5102
+ evidenceMarkdownPath: string().nullable(),
5103
+ updatedAt: string()
5104
+ });
5105
+ const RecentFailuresResponseSchema = object({
5106
+ failures: array(RecentFailureSchema),
5107
+ total: number().int().nonnegative(),
5108
+ limit: number().int().positive()
5109
+ });
5110
+ const RunsFileSchema = object({
5111
+ runs: array(InspectorRunSchema)
5112
+ });
5113
+ function runsFilePath() {
5114
+ return join(getDataDir(), "runs.json");
5115
+ }
5116
+ function nowIso() {
5117
+ return (/* @__PURE__ */ new Date()).toISOString();
5118
+ }
5119
+ function stableId(input) {
5120
+ return input.trim().replace(/[^A-Za-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
5121
+ }
5122
+ function createRunId() {
5123
+ return `run_${Date.now().toString(36)}_${randomUUID$1().slice(0, 8)}`;
5124
+ }
5125
+ function normalizeRunId(input) {
5126
+ if (input === void 0) return createRunId();
5127
+ const normalized = stableId(input);
5128
+ return normalized.length > 0 ? normalized : createRunId();
5129
+ }
5130
+ function normalizeSessionId(input, runId) {
5131
+ if (input === void 0 || input.trim().length === 0) return runId;
5132
+ return input.trim();
5133
+ }
5134
+ function readRuns() {
5135
+ const filePath = runsFilePath();
5136
+ if (!existsSync(filePath)) return [];
5137
+ try {
5138
+ const parsed = JSON.parse(readFileSync(filePath, "utf8"));
5139
+ const result = RunsFileSchema.safeParse(parsed);
5140
+ return result.success ? result.data.runs : [];
5141
+ } catch {
5142
+ return [];
5143
+ }
5144
+ }
5145
+ function writeRuns(runs) {
5146
+ const filePath = runsFilePath();
5147
+ mkdirSync(dirname(filePath), { recursive: true });
5148
+ writeFileSync(filePath, `${JSON.stringify({ runs }, null, 2)}
5149
+ `, "utf8");
5150
+ }
5151
+ function listRuns() {
5152
+ return readRuns().sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
5153
+ }
5154
+ function getRun(runId) {
5155
+ return readRuns().find((run) => run.id === runId) ?? null;
5156
+ }
5157
+ function createRun(input) {
5158
+ const parsed = CreateInspectorRunInputSchema.parse(input);
5159
+ const runId = normalizeRunId(parsed?.runId);
5160
+ const existing = getRun(runId);
5161
+ if (existing !== null) return existing;
5162
+ const timestamp = nowIso();
5163
+ const sessionId = normalizeSessionId(parsed?.sessionId, runId);
5164
+ const run = {
5165
+ id: runId,
5166
+ sessionId,
5167
+ title: parsed?.title ?? `Inspector Run ${runId}`,
5168
+ task: parsed?.task ?? null,
5169
+ project: parsed?.project ?? null,
5170
+ agent: parsed?.agent ?? null,
5171
+ status: parsed?.status ?? "created",
5172
+ tags: parsed?.tags ?? [],
5173
+ metadata: parsed?.metadata ?? {},
5174
+ evidence: null,
5175
+ createdAt: timestamp,
5176
+ updatedAt: timestamp
5177
+ };
5178
+ writeRuns([...readRuns(), run]);
5179
+ return run;
5180
+ }
5181
+ function updateRunEvidence(runId, evidence) {
5182
+ const runs = readRuns();
5183
+ let updatedRun = null;
5184
+ const updatedRuns = runs.map((run) => {
5185
+ if (run.id !== runId) return run;
5186
+ updatedRun = {
5187
+ ...run,
5188
+ evidence,
5189
+ updatedAt: evidence.exportedAt
5190
+ };
5191
+ return updatedRun;
5192
+ });
5193
+ if (updatedRun === null) return null;
5194
+ writeRuns(updatedRuns);
5195
+ return updatedRun;
5196
+ }
5197
+ function updateRun(runId, input) {
5198
+ const parsed = UpdateInspectorRunInputSchema.parse(input);
5199
+ const runs = readRuns();
5200
+ let updatedRun = null;
5201
+ const updatedAt = nowIso();
5202
+ const updatedRuns = runs.map((run) => {
5203
+ if (run.id !== runId) return run;
5204
+ updatedRun = {
5205
+ ...run,
5206
+ sessionId: parsed.sessionId === void 0 ? run.sessionId : normalizeSessionId(parsed.sessionId, run.id),
5207
+ title: parsed.title ?? run.title,
5208
+ task: parsed.task === void 0 ? run.task : parsed.task,
5209
+ project: parsed.project === void 0 ? run.project : parsed.project,
5210
+ agent: parsed.agent === void 0 ? run.agent : parsed.agent,
5211
+ status: parsed.status ?? run.status,
5212
+ tags: parsed.tags ?? run.tags,
5213
+ metadata: parsed.metadata === void 0 ? run.metadata : { ...run.metadata, ...parsed.metadata },
5214
+ updatedAt
5215
+ };
5216
+ return updatedRun;
5217
+ });
5218
+ if (updatedRun === null) return null;
5219
+ writeRuns(updatedRuns);
5220
+ return updatedRun;
5221
+ }
5222
+ const RECENT_FAILURES_DEFAULT_LIMIT = 5;
5223
+ const RECENT_FAILURES_MAX_LIMIT = 20;
5224
+ function clampRecentFailuresLimit(value) {
5225
+ if (!Number.isInteger(value) || value < 1) return RECENT_FAILURES_DEFAULT_LIMIT;
5226
+ return Math.min(value, RECENT_FAILURES_MAX_LIMIT);
5227
+ }
5228
+ function readEvidenceDocument(run) {
5229
+ const path2 = run.evidence?.jsonPath;
5230
+ if (path2 === void 0 || !existsSync(path2)) return null;
5231
+ try {
5232
+ const parsed = JSON.parse(readFileSync(path2, "utf8"));
5233
+ const result = EvidenceExportResultSchema.safeParse(parsed);
5234
+ return result.success ? result.data : null;
5235
+ } catch {
5236
+ return null;
5237
+ }
5238
+ }
5239
+ function syntheticFailedClassification(run) {
5240
+ return {
5241
+ outcome: "failure",
5242
+ severity: "high",
5243
+ category: "unknown-failure",
5244
+ summary: `Run ${run.id} is marked ${run.status}, but no exported evidence document was available.`,
5245
+ evidenceLogIds: [],
5246
+ hints: [
5247
+ "Call inspector_export_evidence for this run to collect session metrics, failing log ids, and artifact paths.",
5248
+ "Confirm the run's sessionId matches the coding agent traffic captured by Inspector."
5249
+ ]
5250
+ };
5251
+ }
5252
+ function shouldIncludeRun(run, document, classification) {
5253
+ if (run.status === "failed" || run.status === "cancelled") return true;
5254
+ if (document === null) return false;
5255
+ return classification.outcome !== "success";
5256
+ }
5257
+ function evidenceFor(run, document) {
5258
+ return document?.evidence ?? run.evidence;
5259
+ }
5260
+ function jenkinsReportFor(document) {
5261
+ return document?.jenkinsReport ?? null;
5262
+ }
5263
+ function failureFromRun(run) {
5264
+ const document = readEvidenceDocument(run);
5265
+ const classification = document === null ? syntheticFailedClassification(run) : document.classification;
5266
+ if (!shouldIncludeRun(run, document, classification)) return null;
5267
+ const evidence = evidenceFor(run, document);
5268
+ return {
5269
+ run,
5270
+ classification,
5271
+ evidence,
5272
+ jenkinsReport: jenkinsReportFor(document),
5273
+ sessionId: run.sessionId,
5274
+ inspectorUrl: document?.session?.inspectorUrl ?? null,
5275
+ evidenceMarkdownPath: evidence?.markdownPath ?? null,
5276
+ updatedAt: evidence?.exportedAt ?? run.updatedAt
5277
+ };
5278
+ }
5279
+ function listRecentFailures(limit) {
5280
+ const failures = listRuns().map(failureFromRun).filter((failure) => failure !== null).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
5281
+ const clampedLimit = clampRecentFailuresLimit(limit);
5282
+ return {
5283
+ failures: failures.slice(0, clampedLimit),
5284
+ total: failures.length,
5285
+ limit: clampedLimit
5286
+ };
5287
+ }
5288
+ async function readJsonBody$2(request) {
5289
+ try {
5290
+ const raw = await request.text();
5291
+ if (raw.trim().length === 0) return { ok: true, value: void 0 };
5292
+ return { ok: true, value: JSON.parse(raw) };
5293
+ } catch {
5294
+ return { ok: false };
5295
+ }
5296
+ }
5297
+ function parsePositiveInt$1(value, fallback) {
5298
+ if (value === null) return fallback;
5299
+ const parsed = Number(value);
5300
+ if (!Number.isInteger(parsed) || parsed < 1) return fallback;
5301
+ return parsed;
5302
+ }
5303
+ const Route$q = createFileRoute("/api/runs")({
5304
+ server: {
5305
+ handlers: {
5306
+ GET: ({ request }) => {
5307
+ const url = new URL(request.url);
5308
+ if (url.searchParams.get("failures") === "1") {
5309
+ const limit = parsePositiveInt$1(
5310
+ url.searchParams.get("limit"),
5311
+ RECENT_FAILURES_DEFAULT_LIMIT
5312
+ );
5313
+ return Response.json(listRecentFailures(limit));
5314
+ }
5315
+ return Response.json({ runs: listRuns() });
5316
+ },
5317
+ POST: async ({ request }) => {
5318
+ const body = await readJsonBody$2(request);
5319
+ if (!body.ok) {
5320
+ return Response.json({ error: "Invalid JSON body" }, { status: 400 });
5321
+ }
5322
+ const parsed = CreateInspectorRunInputSchema.safeParse(body.value);
5323
+ if (!parsed.success) {
5324
+ return Response.json(
5325
+ { error: "Invalid request body", details: parsed.error.format() },
5326
+ { status: 400 }
5327
+ );
5328
+ }
5329
+ return Response.json(createRun(parsed.data), { status: 201 });
5330
+ }
5331
+ }
5332
+ }
5333
+ });
4673
5334
  const ProviderInputSchema = object({
4674
5335
  name: string().min(1, "Name is required"),
4675
5336
  apiKey: string().min(1, "API key is required"),
@@ -4684,7 +5345,7 @@ const ProviderInputSchema = object({
4684
5345
  modelMetadata: array(ProviderModelMetadataSchema).optional(),
4685
5346
  source: _enum(["company", "personal"]).optional()
4686
5347
  });
4687
- const Route$n = createFileRoute("/api/providers")({
5348
+ const Route$p = createFileRoute("/api/providers")({
4688
5349
  server: {
4689
5350
  handlers: {
4690
5351
  GET: ({ request }) => {
@@ -4718,7 +5379,7 @@ const Route$n = createFileRoute("/api/providers")({
4718
5379
  }
4719
5380
  }
4720
5381
  });
4721
- const Route$m = createFileRoute("/api/models")({
5382
+ const Route$o = createFileRoute("/api/models")({
4722
5383
  server: {
4723
5384
  handlers: {
4724
5385
  GET: () => Response.json(getModels())
@@ -4881,6 +5542,14 @@ const LogsListResponseSchema = object({
4881
5542
  offset: number().optional(),
4882
5543
  limit: number().optional()
4883
5544
  });
5545
+ const SearchLogsResponseSchema = object({
5546
+ query: string(),
5547
+ logs: array(CapturedLogSchema),
5548
+ total: number(),
5549
+ offset: number(),
5550
+ limit: number(),
5551
+ scanned: number().optional()
5552
+ });
4884
5553
  const PAGINATION_DEFAULTS = { offset: 0, limit: 3 };
4885
5554
  const LIMIT_HARD_CAP = 5;
4886
5555
  const LOG_DETAIL_TEXT_LIMIT = 2e5;
@@ -4985,6 +5654,33 @@ async function listLogsImpl(callApi2, args) {
4985
5654
  const logs = parsed.data.logs ?? [];
4986
5655
  return textJson(logs.map(buildLogSummary));
4987
5656
  }
5657
+ async function searchLogsImpl(callApi2, args) {
5658
+ const offset = args.offset ?? PAGINATION_DEFAULTS.offset;
5659
+ const limit = clampLimit(args.limit);
5660
+ const params = new URLSearchParams({
5661
+ q: args.query ?? "",
5662
+ offset: String(offset),
5663
+ limit: String(limit)
5664
+ });
5665
+ if (args.scanLimit !== void 0) {
5666
+ params.set("scanLimit", String(args.scanLimit));
5667
+ }
5668
+ if (args.sessionId !== void 0 && args.sessionId.length > 0) {
5669
+ params.set("sessionId", args.sessionId);
5670
+ }
5671
+ if (args.model !== void 0 && args.model.length > 0) {
5672
+ params.set("model", args.model);
5673
+ }
5674
+ const res = await callApi2(`/api/logs?${params.toString()}`);
5675
+ if (!res.ok) return toolError(`GET /api/logs search returned ${res.status}`);
5676
+ const rawBody = await res.json();
5677
+ const parsed = SearchLogsResponseSchema.safeParse(rawBody);
5678
+ if (!parsed.success) return toolError("GET /api/logs search returned an unparseable shape");
5679
+ return textJson({
5680
+ ...parsed.data,
5681
+ logs: parsed.data.logs.map(buildLogSummary)
5682
+ });
5683
+ }
4988
5684
  async function getLogImpl(callApi2, id) {
4989
5685
  const res = await callApi2(`/api/logs/${id}`);
4990
5686
  if (!res.ok) return toolError(`GET /api/logs/${id} returned ${res.status}`);
@@ -5005,6 +5701,87 @@ async function listSessionsImpl(callApi2) {
5005
5701
  if (!res.ok) return toolError(`GET /api/sessions returned ${res.status}`);
5006
5702
  return textJson(await res.json());
5007
5703
  }
5704
+ async function getSessionImpl(callApi2, args) {
5705
+ const params = new URLSearchParams({ sessionId: args.sessionId });
5706
+ if (args.latestLogLimit !== void 0) {
5707
+ params.set("limit", String(args.latestLogLimit));
5708
+ }
5709
+ if (args.includeHistory === true) {
5710
+ params.set("includeHistory", "1");
5711
+ }
5712
+ const path2 = `/api/sessions?${params.toString()}`;
5713
+ const res = await callApi2(path2);
5714
+ if (!res.ok) return toolError(`GET ${path2} returned ${res.status}`);
5715
+ const rawBody = await res.json();
5716
+ const parsed = SessionInfoSchema.safeParse(rawBody);
5717
+ if (!parsed.success) return toolError("GET /api/sessions returned an unparseable session info");
5718
+ return textJson(parsed.data);
5719
+ }
5720
+ async function createRunImpl(callApi2, args) {
5721
+ const res = await callApi2("/api/runs", {
5722
+ method: "POST",
5723
+ headers: { "content-type": "application/json" },
5724
+ body: JSON.stringify(args ?? {})
5725
+ });
5726
+ if (!res.ok) return toolError(`POST /api/runs returned ${res.status}`);
5727
+ const rawBody = await res.json();
5728
+ const parsed = InspectorRunSchema.safeParse(rawBody);
5729
+ if (!parsed.success) return toolError("POST /api/runs returned an unparseable run");
5730
+ return textJson(parsed.data);
5731
+ }
5732
+ async function getRunImpl(callApi2, args) {
5733
+ const path2 = `/api/runs/${encodeURIComponent(args.runId)}`;
5734
+ const res = await callApi2(path2);
5735
+ if (!res.ok) return toolError(`GET ${path2} returned ${res.status}`);
5736
+ const rawBody = await res.json();
5737
+ const parsed = InspectorRunSchema.safeParse(rawBody);
5738
+ if (!parsed.success) return toolError("GET /api/runs/{runId} returned an unparseable run");
5739
+ return textJson(parsed.data);
5740
+ }
5741
+ async function updateRunImpl(callApi2, args) {
5742
+ const { runId, ...patch } = args;
5743
+ const path2 = `/api/runs/${encodeURIComponent(runId)}`;
5744
+ const res = await callApi2(path2, {
5745
+ method: "PATCH",
5746
+ headers: { "content-type": "application/json" },
5747
+ body: JSON.stringify(patch)
5748
+ });
5749
+ if (!res.ok) return toolError(`PATCH ${path2} returned ${res.status}`);
5750
+ const rawBody = await res.json();
5751
+ const parsed = InspectorRunSchema.safeParse(rawBody);
5752
+ if (!parsed.success) return toolError("PATCH /api/runs/{runId} returned an unparseable run");
5753
+ return textJson(parsed.data);
5754
+ }
5755
+ async function getRecentFailuresImpl(callApi2, args) {
5756
+ const limit = args.limit ?? 5;
5757
+ const params = new URLSearchParams({ failures: "1", limit: String(limit) });
5758
+ const res = await callApi2(`/api/runs?${params.toString()}`);
5759
+ if (!res.ok) return toolError(`GET /api/runs?failures=1 returned ${res.status}`);
5760
+ const rawBody = await res.json();
5761
+ const parsed = RecentFailuresResponseSchema.safeParse(rawBody);
5762
+ if (!parsed.success) {
5763
+ return toolError("GET /api/runs?failures=1 returned an unparseable failure list");
5764
+ }
5765
+ return textJson(parsed.data);
5766
+ }
5767
+ async function exportEvidenceImpl(callApi2, args) {
5768
+ const path2 = `/api/runs/${encodeURIComponent(args.runId)}/evidence`;
5769
+ const res = await callApi2(path2, {
5770
+ method: "POST",
5771
+ headers: { "content-type": "application/json" },
5772
+ body: JSON.stringify({
5773
+ includeHistory: args.includeHistory,
5774
+ latestLogLimit: args.latestLogLimit
5775
+ })
5776
+ });
5777
+ if (!res.ok) return toolError(`POST ${path2} returned ${res.status}`);
5778
+ const rawBody = await res.json();
5779
+ const parsed = EvidenceExportResultSchema.safeParse(rawBody);
5780
+ if (!parsed.success) {
5781
+ return toolError("POST /api/runs/{runId}/evidence returned an unparseable evidence result");
5782
+ }
5783
+ return textJson(parsed.data);
5784
+ }
5008
5785
  async function listModelsImpl(callApi2) {
5009
5786
  const res = await callApi2("/api/models");
5010
5787
  if (!res.ok) return toolError(`GET /api/models returned ${res.status}`);
@@ -5135,9 +5912,11 @@ let initPromise = null;
5135
5912
  function buildServer() {
5136
5913
  const server = new McpServer(
5137
5914
  { name: "agent-inspector", version: "1.0.0" },
5138
- { capabilities: { tools: {} } }
5915
+ { capabilities: { tools: {}, resources: {}, prompts: {} } }
5139
5916
  );
5140
5917
  registerTools(server);
5918
+ registerResources(server);
5919
+ registerPrompts(server);
5141
5920
  const transport = new WebStandardStreamableHTTPServerTransport({
5142
5921
  sessionIdGenerator: void 0
5143
5922
  // stateless — see module docstring
@@ -5185,6 +5964,129 @@ Returns: array of "thick summary" objects, each containing:
5185
5964
 
5186
5965
  Either preview field is null when the body is unknown format, unparseable, or the relevant content is non-text (e.g., image-only).`;
5187
5966
  const PROVIDER_WRITE_WARNING = "⚠ This tool mutates provider configuration. Confirm with the user before invoking.";
5967
+ const RunsListResponseSchema = object({
5968
+ runs: array(InspectorRunSchema)
5969
+ });
5970
+ const LogResourceListResponseSchema = object({
5971
+ logs: array(object({ id: number().int().positive() }))
5972
+ });
5973
+ const EvidenceReadResponseSchema = object({
5974
+ markdown: string()
5975
+ });
5976
+ function textResource(uri, mimeType, text) {
5977
+ return {
5978
+ contents: [{ uri: uri.href, mimeType, text }]
5979
+ };
5980
+ }
5981
+ function jsonResource(uri, data) {
5982
+ return textResource(uri, "application/json", JSON.stringify(data, null, 2));
5983
+ }
5984
+ function variableString(variables, name) {
5985
+ const value = variables[name];
5986
+ if (Array.isArray(value)) return value[0] ?? null;
5987
+ return value ?? null;
5988
+ }
5989
+ function listResult(resources) {
5990
+ return { resources };
5991
+ }
5992
+ async function readApiJsonResource(uri, path2) {
5993
+ try {
5994
+ const response = await callApi(path2);
5995
+ if (!response.ok) {
5996
+ return textResource(uri, "text/plain", `GET ${path2} returned ${response.status}`);
5997
+ }
5998
+ return jsonResource(uri, await response.json());
5999
+ } catch (err) {
6000
+ const message = err instanceof Error ? err.message : String(err);
6001
+ return textResource(uri, "text/plain", `Resource read failed: ${message}`);
6002
+ }
6003
+ }
6004
+ async function listSessionResources() {
6005
+ try {
6006
+ const response = await callApi("/api/sessions");
6007
+ if (!response.ok) return listResult([]);
6008
+ const parsed = array(string()).safeParse(await response.json());
6009
+ if (!parsed.success) return listResult([]);
6010
+ return listResult(
6011
+ parsed.data.map((sessionId) => ({
6012
+ uri: `inspector://sessions/${encodeURIComponent(sessionId)}`,
6013
+ name: `session:${sessionId}`,
6014
+ title: `Inspector session ${sessionId}`,
6015
+ mimeType: "application/json"
6016
+ }))
6017
+ );
6018
+ } catch {
6019
+ return listResult([]);
6020
+ }
6021
+ }
6022
+ async function listLogResources() {
6023
+ try {
6024
+ const response = await callApi("/api/logs?offset=0&limit=5&compact=1");
6025
+ if (!response.ok) return listResult([]);
6026
+ const parsed = LogResourceListResponseSchema.safeParse(await response.json());
6027
+ if (!parsed.success) return listResult([]);
6028
+ return listResult(
6029
+ parsed.data.logs.map((log) => ({
6030
+ uri: `inspector://logs/${String(log.id)}`,
6031
+ name: `log:${String(log.id)}`,
6032
+ title: `Inspector log ${String(log.id)}`,
6033
+ mimeType: "application/json"
6034
+ }))
6035
+ );
6036
+ } catch {
6037
+ return listResult([]);
6038
+ }
6039
+ }
6040
+ async function listRunResources() {
6041
+ try {
6042
+ const response = await callApi("/api/runs");
6043
+ if (!response.ok) return listResult([]);
6044
+ const parsed = RunsListResponseSchema.safeParse(await response.json());
6045
+ if (!parsed.success) return listResult([]);
6046
+ return listResult(
6047
+ parsed.data.runs.map((run) => ({
6048
+ uri: `inspector://runs/${encodeURIComponent(run.id)}`,
6049
+ name: `run:${run.id}`,
6050
+ title: run.title,
6051
+ mimeType: "application/json"
6052
+ }))
6053
+ );
6054
+ } catch {
6055
+ return listResult([]);
6056
+ }
6057
+ }
6058
+ async function listEvidenceResources() {
6059
+ try {
6060
+ const response = await callApi("/api/runs");
6061
+ if (!response.ok) return listResult([]);
6062
+ const parsed = RunsListResponseSchema.safeParse(await response.json());
6063
+ if (!parsed.success) return listResult([]);
6064
+ return listResult(
6065
+ parsed.data.runs.filter((run) => run.evidence !== null).map((run) => ({
6066
+ uri: `inspector://evidence/${encodeURIComponent(run.id)}`,
6067
+ name: `evidence:${run.id}`,
6068
+ title: `${run.title} evidence pack`,
6069
+ mimeType: "text/markdown",
6070
+ description: run.evidence?.markdownPath
6071
+ }))
6072
+ );
6073
+ } catch {
6074
+ return listResult([]);
6075
+ }
6076
+ }
6077
+ function userPrompt(text) {
6078
+ return {
6079
+ messages: [
6080
+ {
6081
+ role: "user",
6082
+ content: {
6083
+ type: "text",
6084
+ text
6085
+ }
6086
+ }
6087
+ ]
6088
+ };
6089
+ }
5188
6090
  function registerTools(server) {
5189
6091
  server.registerTool(
5190
6092
  "inspector_list_logs",
@@ -5201,12 +6103,28 @@ function registerTools(server) {
5201
6103
  (args) => safeCall(() => listLogsImpl(callApi, args))
5202
6104
  );
5203
6105
  server.registerTool(
5204
- "inspector_get_log",
6106
+ "inspector_search_logs",
5205
6107
  {
5206
- title: "Get a single captured log by id",
5207
- description: "Returns the full CapturedLog for the given id, including rawRequestBody and responseText with no truncation. SSE streaming chunks are NOT included use inspector_get_log_chunks for those. Returns an error if the id does not exist.",
6108
+ title: "Search captured logs",
6109
+ description: "Searches recent compact proxy logs by id, session, model, provider, API path, status, error text, and client context. Results are bounded summaries; call inspector_get_log for full details after locating a candidate id.",
5208
6110
  inputSchema: object({
5209
- id: number().int().positive().describe("The log id.")
6111
+ query: string().optional().describe("Search terms. Empty or omitted returns the newest scanned logs."),
6112
+ offset: number().int().nonnegative().optional().describe("Skip this many matches."),
6113
+ limit: number().int().positive().optional().describe("How many summaries to return. Hard-capped at 5 by the MCP handler."),
6114
+ scanLimit: number().int().positive().max(1e3).optional().describe("How many recent compact logs to scan before filtering. Default 200."),
6115
+ sessionId: string().optional().describe("Optional session/affinity id filter."),
6116
+ model: string().optional().describe("Optional model name filter.")
6117
+ })
6118
+ },
6119
+ (args) => safeCall(() => searchLogsImpl(callApi, args))
6120
+ );
6121
+ server.registerTool(
6122
+ "inspector_get_log",
6123
+ {
6124
+ title: "Get a single captured log by id",
6125
+ description: "Returns the full CapturedLog for the given id, including rawRequestBody and responseText with no truncation. SSE streaming chunks are NOT included — use inspector_get_log_chunks for those. Returns an error if the id does not exist.",
6126
+ inputSchema: object({
6127
+ id: number().int().positive().describe("The log id.")
5210
6128
  })
5211
6129
  },
5212
6130
  ({ id }) => safeCall(() => getLogImpl(callApi, id))
@@ -5231,6 +6149,84 @@ function registerTools(server) {
5231
6149
  },
5232
6150
  () => safeCall(() => listSessionsImpl(callApi))
5233
6151
  );
6152
+ server.registerTool(
6153
+ "inspector_get_session",
6154
+ {
6155
+ title: "Get Inspector session info",
6156
+ description: "Returns a lightweight, evidence-friendly summary for one Inspector session: clickable inspectorUrl, compact logs URL, status, request/error counts, token totals, models/providers, and the latest compact log summaries. Defaults to recent in-memory/cache data for speed; set includeHistory=true only when you explicitly want to scan persisted jsonl history. It avoids raw request/response bodies so it is safe to use in evaluation artifacts.",
6157
+ inputSchema: object({
6158
+ sessionId: string().min(1).describe("The Inspector session id."),
6159
+ includeHistory: boolean().optional().describe("Scan persisted jsonl history for this session. Slower; default false."),
6160
+ latestLogLimit: number().int().positive().max(50).optional().describe("How many latest compact log summaries to include. Max 50, default 10.")
6161
+ })
6162
+ },
6163
+ (args) => safeCall(() => getSessionImpl(callApi, args))
6164
+ );
6165
+ server.registerTool(
6166
+ "inspector_create_run",
6167
+ {
6168
+ title: "Create or declare an Inspector run",
6169
+ description: "Creates an evaluation-friendly run record before a coding-agent task starts. Use this when a benchmark or Jenkins job wants a stable runId/sessionId pair instead of inferring a task after the fact from logs.",
6170
+ inputSchema: object({
6171
+ runId: string().min(1).optional().describe("Stable run id. Generated when omitted."),
6172
+ sessionId: string().min(1).optional().describe("Inspector session id to attach. Defaults to the run id."),
6173
+ title: string().min(1).optional().describe("Human-readable run title."),
6174
+ task: string().nullable().optional().describe("Task statement or benchmark case."),
6175
+ project: string().nullable().optional().describe("Project or repository under test."),
6176
+ agent: string().nullable().optional().describe("Coding agent name, e.g. Codex/OpenCode."),
6177
+ status: InspectorRunStatusSchema.optional().describe("Initial run status."),
6178
+ tags: array(string()).optional().describe("Free-form run labels."),
6179
+ metadata: record(string(), JsonValueSchema).optional().describe("JSON metadata.")
6180
+ })
6181
+ },
6182
+ (args) => safeCall(() => createRunImpl(callApi, args))
6183
+ );
6184
+ server.registerTool(
6185
+ "inspector_get_run",
6186
+ {
6187
+ title: "Get an Inspector run",
6188
+ description: "Returns one declared Inspector run, including its session binding and exported evidence paths when available.",
6189
+ inputSchema: object({
6190
+ runId: string().min(1).describe("The Inspector run id.")
6191
+ })
6192
+ },
6193
+ (args) => safeCall(() => getRunImpl(callApi, args))
6194
+ );
6195
+ server.registerTool(
6196
+ "inspector_update_run",
6197
+ {
6198
+ title: "Update an Inspector run",
6199
+ description: "PATCH-style update for a declared run. Use this during a coding-agent task to mark status transitions such as running, failed, completed, or to attach refined task metadata. Supplied metadata keys are merged into existing metadata.",
6200
+ inputSchema: UpdateInspectorRunInputSchema.extend({
6201
+ runId: string().min(1).describe("The Inspector run id.")
6202
+ })
6203
+ },
6204
+ (args) => safeCall(() => updateRunImpl(callApi, args))
6205
+ );
6206
+ server.registerTool(
6207
+ "inspector_export_evidence",
6208
+ {
6209
+ title: "Export an Inspector evidence pack",
6210
+ description: "Exports one run's evidence pack to local JSON, Markdown, and HTML files under <dataDir>/evidence/<runId> and returns the paths plus session summary.",
6211
+ inputSchema: object({
6212
+ runId: string().min(1).describe("The Inspector run id."),
6213
+ includeHistory: boolean().optional().describe("Scan persisted jsonl history for the bound session. Slower; default false."),
6214
+ latestLogLimit: number().int().positive().max(50).optional().describe("How many compact log summaries to include in the evidence pack. Max 50.")
6215
+ })
6216
+ },
6217
+ (args) => safeCall(() => exportEvidenceImpl(callApi, args))
6218
+ );
6219
+ server.registerTool(
6220
+ "inspector_get_recent_failures",
6221
+ {
6222
+ title: "Get recent Inspector failures",
6223
+ description: "Returns recent declared runs that are failed/cancelled or have non-success exported evidence, including classification, Jenkins summary, Inspector link, and evidence paths when available.",
6224
+ inputSchema: object({
6225
+ limit: number().int().positive().max(20).optional().describe("Maximum failures to return. Default 5, max 20.")
6226
+ })
6227
+ },
6228
+ (args) => safeCall(() => getRecentFailuresImpl(callApi, args))
6229
+ );
5234
6230
  server.registerTool(
5235
6231
  "inspector_list_models",
5236
6232
  {
@@ -5382,7 +6378,211 @@ PATCH-style update: only the fields you supply are changed. Returns the updated
5382
6378
  (args) => safeCall(() => getProjectContextImpl(callApi, args))
5383
6379
  );
5384
6380
  }
5385
- const Route$l = createFileRoute("/api/mcp")({
6381
+ function registerResources(server) {
6382
+ server.registerResource(
6383
+ "inspector_sessions",
6384
+ "inspector://sessions",
6385
+ {
6386
+ title: "Inspector sessions",
6387
+ description: "Observed Inspector session ids.",
6388
+ mimeType: "application/json"
6389
+ },
6390
+ (uri) => readApiJsonResource(uri, "/api/sessions")
6391
+ );
6392
+ server.registerResource(
6393
+ "inspector_session",
6394
+ new ResourceTemplate("inspector://sessions/{sessionId}", { list: listSessionResources }),
6395
+ {
6396
+ title: "Inspector session",
6397
+ description: "Evidence-friendly session summary with Inspector URL, compact logs URL, counts, models, providers, token totals, and recent compact logs.",
6398
+ mimeType: "application/json"
6399
+ },
6400
+ (uri, variables) => {
6401
+ const sessionId = variableString(variables, "sessionId");
6402
+ if (sessionId === null) return textResource(uri, "text/plain", "Missing sessionId");
6403
+ const params = new URLSearchParams({ sessionId, limit: "10" });
6404
+ return readApiJsonResource(uri, `/api/sessions?${params.toString()}`);
6405
+ }
6406
+ );
6407
+ server.registerResource(
6408
+ "inspector_log",
6409
+ new ResourceTemplate("inspector://logs/{id}", { list: listLogResources }),
6410
+ {
6411
+ title: "Inspector log",
6412
+ description: "Single captured proxy log by numeric id.",
6413
+ mimeType: "application/json"
6414
+ },
6415
+ async (uri, variables) => {
6416
+ const id = variableString(variables, "id");
6417
+ const parsed = id === null ? Number.NaN : Number(id);
6418
+ if (!Number.isInteger(parsed) || parsed <= 0) {
6419
+ return textResource(uri, "text/plain", "Invalid log id");
6420
+ }
6421
+ const result = await getLogImpl(callApi, parsed);
6422
+ const first = result.content[0];
6423
+ const text = first?.text ?? "";
6424
+ return textResource(uri, result.isError === true ? "text/plain" : "application/json", text);
6425
+ }
6426
+ );
6427
+ server.registerResource(
6428
+ "inspector_runs",
6429
+ "inspector://runs",
6430
+ {
6431
+ title: "Inspector runs",
6432
+ description: "Declared Inspector evaluation runs.",
6433
+ mimeType: "application/json"
6434
+ },
6435
+ (uri) => readApiJsonResource(uri, "/api/runs")
6436
+ );
6437
+ server.registerResource(
6438
+ "inspector_recent_failures",
6439
+ "inspector://failures/recent",
6440
+ {
6441
+ title: "Recent Inspector failures",
6442
+ description: "Recent declared runs with failed/cancelled status or non-success exported evidence.",
6443
+ mimeType: "application/json"
6444
+ },
6445
+ (uri) => readApiJsonResource(uri, "/api/runs?failures=1&limit=10")
6446
+ );
6447
+ server.registerResource(
6448
+ "inspector_run",
6449
+ new ResourceTemplate("inspector://runs/{runId}", { list: listRunResources }),
6450
+ {
6451
+ title: "Inspector run",
6452
+ description: "One declared Inspector run, including session binding and evidence paths.",
6453
+ mimeType: "application/json"
6454
+ },
6455
+ (uri, variables) => {
6456
+ const runId = variableString(variables, "runId");
6457
+ if (runId === null) return textResource(uri, "text/plain", "Missing runId");
6458
+ return readApiJsonResource(uri, `/api/runs/${encodeURIComponent(runId)}`);
6459
+ }
6460
+ );
6461
+ server.registerResource(
6462
+ "inspector_evidence",
6463
+ new ResourceTemplate("inspector://evidence/{runId}", { list: listEvidenceResources }),
6464
+ {
6465
+ title: "Inspector evidence pack",
6466
+ description: "Markdown evidence pack for a declared Inspector run. Generate it first with inspector_export_evidence.",
6467
+ mimeType: "text/markdown"
6468
+ },
6469
+ async (uri, variables) => {
6470
+ const runId = variableString(variables, "runId");
6471
+ if (runId === null) return textResource(uri, "text/plain", "Missing runId");
6472
+ const path2 = `/api/runs/${encodeURIComponent(runId)}/evidence`;
6473
+ try {
6474
+ const response = await callApi(path2);
6475
+ if (!response.ok) {
6476
+ return textResource(uri, "text/plain", `GET ${path2} returned ${response.status}`);
6477
+ }
6478
+ const parsed = EvidenceReadResponseSchema.safeParse(await response.json());
6479
+ if (!parsed.success) {
6480
+ return textResource(uri, "text/plain", "Evidence endpoint returned an unparseable body");
6481
+ }
6482
+ return textResource(uri, "text/markdown", parsed.data.markdown);
6483
+ } catch (err) {
6484
+ const message = err instanceof Error ? err.message : String(err);
6485
+ return textResource(uri, "text/plain", `Resource read failed: ${message}`);
6486
+ }
6487
+ }
6488
+ );
6489
+ }
6490
+ function registerPrompts(server) {
6491
+ server.registerPrompt(
6492
+ "inspector_analyze_session_failure",
6493
+ {
6494
+ title: "Analyze session failure",
6495
+ description: "Use Inspector session/log resources to explain why a coding-agent session failed.",
6496
+ argsSchema: object({
6497
+ sessionId: string().min(1).describe("The Inspector session id."),
6498
+ includeHistory: boolean().optional().describe("When true, call inspector_get_session with includeHistory=true if needed.")
6499
+ })
6500
+ },
6501
+ ({ sessionId, includeHistory }) => userPrompt(`Analyze this Agent Inspector session and explain why it failed.
6502
+
6503
+ Evidence to read:
6504
+ - Session resource: inspector://sessions/${encodeURIComponent(sessionId)}
6505
+ - Relevant log resources: inspector://logs/{id} for the failing or suspicious log ids
6506
+
6507
+ If includeHistory is ${includeHistory === true ? "true" : "false"} or the session summary looks incomplete, call inspector_get_session with includeHistory=true and latestLogLimit=50.
6508
+
6509
+ Return:
6510
+ 1. Outcome and severity
6511
+ 2. Most likely root cause
6512
+ 3. Evidence bullets with log ids, models, providers, status codes, and error text
6513
+ 4. Concrete fix or next diagnostic step
6514
+ 5. Confidence and what evidence is still missing`)
6515
+ );
6516
+ server.registerPrompt(
6517
+ "inspector_triage_recent_failures",
6518
+ {
6519
+ title: "Triage recent failures",
6520
+ description: "Use the recent failures resource and log search tool to prioritize coding-agent failures.",
6521
+ argsSchema: object({
6522
+ limit: number().int().positive().max(20).optional().describe("How many failures to triage.")
6523
+ })
6524
+ },
6525
+ ({ limit }) => userPrompt(`Triage recent Agent Inspector failures.
6526
+
6527
+ Evidence to read:
6528
+ - Recent failures resource: inspector://failures/recent
6529
+ - For missing details, call inspector_get_recent_failures with limit=${String(limit ?? 5)}
6530
+ - For suspicious sessions/models/providers, call inspector_search_logs and then inspector_get_log for cited ids
6531
+
6532
+ Return:
6533
+ 1. Highest-priority failure first
6534
+ 2. Run id, session id, category, severity, and evidence links
6535
+ 3. Likely root cause and concrete next action
6536
+ 4. Whether evidence is sufficient or inspector_export_evidence should be run again`)
6537
+ );
6538
+ server.registerPrompt(
6539
+ "inspector_generate_jenkins_report",
6540
+ {
6541
+ title: "Generate Jenkins report",
6542
+ description: "Generate a Jenkins-friendly Markdown report from an Inspector run and evidence pack.",
6543
+ argsSchema: object({
6544
+ runId: string().min(1).describe("The Inspector run id.")
6545
+ })
6546
+ },
6547
+ ({ runId }) => userPrompt(`Generate a Jenkins-ready Markdown report for this Inspector run.
6548
+
6549
+ Evidence to read:
6550
+ - Run resource: inspector://runs/${encodeURIComponent(runId)}
6551
+ - Evidence pack resource: inspector://evidence/${encodeURIComponent(runId)}
6552
+
6553
+ If the evidence pack resource says it has not been exported, call inspector_export_evidence for this run first.
6554
+
6555
+ The report should include:
6556
+ 1. Build/run status summary
6557
+ 2. Run id, session id, Inspector URLs, and artifact paths
6558
+ 3. Models/providers used, token totals, request counts, and error counts
6559
+ 4. Failure analysis or success evidence
6560
+ 5. Reproducible next action for a developer`)
6561
+ );
6562
+ server.registerPrompt(
6563
+ "inspector_extract_repro_steps",
6564
+ {
6565
+ title: "Extract reproducible steps",
6566
+ description: "Turn an Inspector session into concise repro steps for a developer or evaluator.",
6567
+ argsSchema: object({
6568
+ sessionId: string().min(1).describe("The Inspector session id.")
6569
+ })
6570
+ },
6571
+ ({ sessionId }) => userPrompt(`Extract reproducible steps from this Agent Inspector session.
6572
+
6573
+ Evidence to read:
6574
+ - Session resource: inspector://sessions/${encodeURIComponent(sessionId)}
6575
+ - Relevant log resources: inspector://logs/{id}
6576
+
6577
+ Return:
6578
+ 1. Preconditions and environment assumptions
6579
+ 2. Ordered user/agent actions that led to the behavior
6580
+ 3. Exact model/provider/API path details needed to reproduce
6581
+ 4. Expected vs actual result
6582
+ 5. Minimal verification command or UI check`)
6583
+ );
6584
+ }
6585
+ const Route$n = createFileRoute("/api/mcp")({
5386
6586
  server: {
5387
6587
  handlers: {
5388
6588
  // The SDK may issue either POST, GET, or DELETE. TanStack Start only
@@ -5394,6 +6594,89 @@ const Route$l = createFileRoute("/api/mcp")({
5394
6594
  }
5395
6595
  }
5396
6596
  });
6597
+ const LOG_SEARCH_DEFAULT_LIMIT = 10;
6598
+ const LOG_SEARCH_MAX_LIMIT = 50;
6599
+ const LOG_SEARCH_DEFAULT_SCAN_LIMIT = 200;
6600
+ const LOG_SEARCH_MAX_SCAN_LIMIT = 1e3;
6601
+ function clampPositiveInt(value, fallback, max) {
6602
+ if (!Number.isInteger(value) || value < 1) return fallback;
6603
+ return Math.min(value, max);
6604
+ }
6605
+ function clampLogSearchLimit(value) {
6606
+ return clampPositiveInt(value, LOG_SEARCH_DEFAULT_LIMIT, LOG_SEARCH_MAX_LIMIT);
6607
+ }
6608
+ function clampLogSearchScanLimit(value) {
6609
+ return clampPositiveInt(value, LOG_SEARCH_DEFAULT_SCAN_LIMIT, LOG_SEARCH_MAX_SCAN_LIMIT);
6610
+ }
6611
+ function queryTerms(query) {
6612
+ return query.trim().toLowerCase().split(/\s+/).filter((term) => term.length > 0);
6613
+ }
6614
+ function optionalText(value) {
6615
+ if (value === null || value === void 0) return null;
6616
+ return String(value);
6617
+ }
6618
+ function searchableFields(log) {
6619
+ return [
6620
+ String(log.id),
6621
+ log.timestamp,
6622
+ log.method,
6623
+ log.path,
6624
+ optionalText(log.model),
6625
+ optionalText(log.sessionId),
6626
+ optionalText(log.responseStatus),
6627
+ optionalText(log.elapsedMs),
6628
+ optionalText(log.streaming),
6629
+ log.streaming ? "streaming" : "non-streaming",
6630
+ log.apiFormat,
6631
+ optionalText(log.providerName),
6632
+ optionalText(log.userAgent),
6633
+ optionalText(log.origin),
6634
+ optionalText(log.clientPid),
6635
+ optionalText(log.clientCwd),
6636
+ optionalText(log.clientProjectFolder),
6637
+ optionalText(log.error)
6638
+ ].filter((value) => value !== null && value.length > 0);
6639
+ }
6640
+ function matchesTerms(log, terms) {
6641
+ if (terms.length === 0) return true;
6642
+ const haystack = searchableFields(log).join("\n").toLowerCase();
6643
+ return terms.every((term) => haystack.includes(term));
6644
+ }
6645
+ function sortNewestFirst(logs) {
6646
+ return logs.sort((left, right) => {
6647
+ const timestampCompare = right.timestamp.localeCompare(left.timestamp);
6648
+ return timestampCompare !== 0 ? timestampCompare : right.id - left.id;
6649
+ });
6650
+ }
6651
+ async function searchLogsPage(options) {
6652
+ const limit = clampLogSearchLimit(options.limit);
6653
+ const scanLimit = clampLogSearchScanLimit(Math.max(options.scanLimit, options.offset + limit));
6654
+ const firstPage = await listLogsPage({
6655
+ sessionId: options.sessionId,
6656
+ model: options.model,
6657
+ offset: 0,
6658
+ limit: 1,
6659
+ includeBodies: false
6660
+ });
6661
+ const scanOffset = Math.max(firstPage.total - scanLimit, 0);
6662
+ const scanPage = await listLogsPage({
6663
+ sessionId: options.sessionId,
6664
+ model: options.model,
6665
+ offset: scanOffset,
6666
+ limit: scanLimit,
6667
+ includeBodies: false
6668
+ });
6669
+ const terms = queryTerms(options.query);
6670
+ const matches = sortNewestFirst(scanPage.logs.filter((log) => matchesTerms(log, terms)));
6671
+ return {
6672
+ query: options.query,
6673
+ logs: matches.slice(options.offset, options.offset + limit),
6674
+ total: matches.length,
6675
+ offset: options.offset,
6676
+ limit,
6677
+ scanned: scanPage.logs.length
6678
+ };
6679
+ }
5397
6680
  const DeleteBodySchema = object({
5398
6681
  ids: array(number().int().positive()).optional()
5399
6682
  }).optional();
@@ -5409,7 +6692,7 @@ function parsePositiveInt(value, fallback) {
5409
6692
  if (!Number.isInteger(parsed) || parsed < 1) return fallback;
5410
6693
  return parsed;
5411
6694
  }
5412
- const Route$k = createFileRoute("/api/logs")({
6695
+ const Route$m = createFileRoute("/api/logs")({
5413
6696
  server: {
5414
6697
  handlers: {
5415
6698
  GET: async ({ request }) => {
@@ -5428,7 +6711,18 @@ const Route$k = createFileRoute("/api/logs")({
5428
6711
  }
5429
6712
  const sessionId = url.searchParams.get("sessionId") ?? void 0;
5430
6713
  const model = url.searchParams.get("model") ?? void 0;
6714
+ const query = url.searchParams.get("q") ?? url.searchParams.get("search");
5431
6715
  const offset = parseNonNegativeInt(url.searchParams.get("offset"), 0);
6716
+ if (query !== null) {
6717
+ const limit2 = parsePositiveInt(url.searchParams.get("limit"), LOG_SEARCH_DEFAULT_LIMIT);
6718
+ const scanLimit = parsePositiveInt(
6719
+ url.searchParams.get("scanLimit"),
6720
+ LOG_SEARCH_DEFAULT_SCAN_LIMIT
6721
+ );
6722
+ return Response.json(
6723
+ await searchLogsPage({ query, sessionId, model, offset, limit: limit2, scanLimit })
6724
+ );
6725
+ }
5432
6726
  const limit = parsePositiveInt(url.searchParams.get("limit"), 50);
5433
6727
  const includeBodies = url.searchParams.get("compact") !== "1";
5434
6728
  return Response.json(
@@ -5474,7 +6768,7 @@ const Route$k = createFileRoute("/api/logs")({
5474
6768
  }
5475
6769
  });
5476
6770
  logger.debug("Health endpoint loaded");
5477
- const Route$j = createFileRoute("/api/health")({
6771
+ const Route$l = createFileRoute("/api/health")({
5478
6772
  server: {
5479
6773
  handlers: {
5480
6774
  GET: () => {
@@ -5492,7 +6786,7 @@ const RuntimeConfigPatchSchema = object({
5492
6786
  }).strict().refine((v) => Object.keys(v).length > 0, {
5493
6787
  message: "At least one field must be provided"
5494
6788
  });
5495
- const Route$i = createFileRoute("/api/config")({
6789
+ const Route$k = createFileRoute("/api/config")({
5496
6790
  server: {
5497
6791
  handlers: {
5498
6792
  GET: () => {
@@ -5521,6 +6815,46 @@ const Route$i = createFileRoute("/api/config")({
5521
6815
  }
5522
6816
  }
5523
6817
  });
6818
+ async function readJsonBody$1(request) {
6819
+ try {
6820
+ const raw = await request.text();
6821
+ if (raw.trim().length === 0) return { ok: true, value: {} };
6822
+ return { ok: true, value: JSON.parse(raw) };
6823
+ } catch {
6824
+ return { ok: false };
6825
+ }
6826
+ }
6827
+ const Route$j = createFileRoute("/api/runs/$runId")({
6828
+ server: {
6829
+ handlers: {
6830
+ GET: ({ params }) => {
6831
+ const run = getRun(params.runId);
6832
+ if (run === null) {
6833
+ return Response.json({ error: "Run not found" }, { status: 404 });
6834
+ }
6835
+ return Response.json(run);
6836
+ },
6837
+ PATCH: async ({ params, request }) => {
6838
+ const body = await readJsonBody$1(request);
6839
+ if (!body.ok) {
6840
+ return Response.json({ error: "Invalid JSON body" }, { status: 400 });
6841
+ }
6842
+ const parsed = UpdateInspectorRunInputSchema.safeParse(body.value);
6843
+ if (!parsed.success) {
6844
+ return Response.json(
6845
+ { error: "Invalid request body", details: parsed.error.format() },
6846
+ { status: 400 }
6847
+ );
6848
+ }
6849
+ const updated = updateRun(params.runId, parsed.data);
6850
+ if (updated === null) {
6851
+ return Response.json({ error: "Run not found" }, { status: 404 });
6852
+ }
6853
+ return Response.json(updated);
6854
+ }
6855
+ }
6856
+ }
6857
+ });
5524
6858
  const ZHIPU_OPENAI_BASE_URL = "https://open.bigmodel.cn/api/paas/v4";
5525
6859
  const ZHIPU_CODING_OPENAI_BASE_URL = "https://open.bigmodel.cn/api/coding/paas/v4";
5526
6860
  const ZHIPU_CODING_ANTHROPIC_BASE_URL = "https://open.bigmodel.cn/api/anthropic";
@@ -5947,7 +7281,7 @@ function scanExternalProviders() {
5947
7281
  }
5948
7282
  return { providers: filteredProviders, warnings };
5949
7283
  }
5950
- const Route$h = createFileRoute("/api/providers/scan")({
7284
+ const Route$i = createFileRoute("/api/providers/scan")({
5951
7285
  server: {
5952
7286
  handlers: {
5953
7287
  GET: () => {
@@ -5968,7 +7302,7 @@ const Route$h = createFileRoute("/api/providers/scan")({
5968
7302
  }
5969
7303
  });
5970
7304
  union([string(), object({ providers: unknown() })]);
5971
- const Route$g = createFileRoute("/api/providers/import")({
7305
+ const Route$h = createFileRoute("/api/providers/import")({
5972
7306
  server: {
5973
7307
  handlers: {
5974
7308
  POST: async ({ request }) => {
@@ -6001,7 +7335,7 @@ const Route$g = createFileRoute("/api/providers/import")({
6001
7335
  }
6002
7336
  }
6003
7337
  });
6004
- const Route$f = createFileRoute("/api/providers/export")({
7338
+ const Route$g = createFileRoute("/api/providers/export")({
6005
7339
  server: {
6006
7340
  handlers: {
6007
7341
  GET: ({ request }) => {
@@ -6034,7 +7368,7 @@ const ProviderUpdateSchema = object({
6034
7368
  modelMetadata: array(ProviderModelMetadataSchema).optional(),
6035
7369
  source: _enum(["company", "personal"]).optional()
6036
7370
  });
6037
- const Route$e = createFileRoute("/api/providers/$providerId")({
7371
+ const Route$f = createFileRoute("/api/providers/$providerId")({
6038
7372
  server: {
6039
7373
  handlers: {
6040
7374
  GET: ({ params, request }) => {
@@ -6069,7 +7403,7 @@ const Route$e = createFileRoute("/api/providers/$providerId")({
6069
7403
  }
6070
7404
  });
6071
7405
  const INITIAL_STREAM_LOG_LIMIT = 100;
6072
- const Route$d = createFileRoute("/api/logs/stream")({
7406
+ const Route$e = createFileRoute("/api/logs/stream")({
6073
7407
  server: {
6074
7408
  handlers: {
6075
7409
  GET: ({ request }) => {
@@ -6142,7 +7476,7 @@ const Route$d = createFileRoute("/api/logs/stream")({
6142
7476
  }
6143
7477
  }
6144
7478
  });
6145
- const Route$c = createFileRoute("/api/logs/$id")({
7479
+ const Route$d = createFileRoute("/api/logs/$id")({
6146
7480
  server: {
6147
7481
  handlers: {
6148
7482
  GET: async ({ params }) => {
@@ -6484,10 +7818,10 @@ function normalizePathSegment(value, fallback) {
6484
7818
  const segment = value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 96);
6485
7819
  return segment === "" ? fallback : segment;
6486
7820
  }
6487
- function formatNullable(value) {
7821
+ function formatNullable$1(value) {
6488
7822
  return value === null || value === "" ? "unknown" : value;
6489
7823
  }
6490
- function formatList(values) {
7824
+ function formatList$2(values) {
6491
7825
  return values.length === 0 ? "none" : values.join(", ");
6492
7826
  }
6493
7827
  function buildEvidenceLink(candidate) {
@@ -6510,16 +7844,16 @@ function buildOpenClawMemoryMarkdown(candidate) {
6510
7844
  "",
6511
7845
  `- Type: ${sanitized.type}`,
6512
7846
  `- Status: ${sanitized.status}`,
6513
- `- Project: ${formatNullable(sanitized.evidence.project)}`,
7847
+ `- Project: ${formatNullable$1(sanitized.evidence.project)}`,
6514
7848
  `- Session: ${sanitized.sessionId}`,
6515
7849
  `- Log IDs: ${sanitized.logIds.join(", ")}`,
6516
- `- Models: ${formatList(sanitized.evidence.models)}`,
6517
- `- Tags: ${formatList(sanitized.tags)}`,
7850
+ `- Models: ${formatList$2(sanitized.evidence.models)}`,
7851
+ `- Tags: ${formatList$2(sanitized.tags)}`,
6518
7852
  `- Created: ${sanitized.createdAt}`,
6519
7853
  `- Updated: ${sanitized.updatedAt}`,
6520
7854
  `- Evidence: ${buildEvidenceLink(sanitized)}`,
6521
7855
  `- Redacted: ${String(sanitized.redaction.redacted)}`,
6522
- `- Redaction patterns: ${formatList(sanitized.redaction.patterns)}`,
7856
+ `- Redaction patterns: ${formatList$2(sanitized.redaction.patterns)}`,
6523
7857
  "",
6524
7858
  "## Durable Knowledge Candidate",
6525
7859
  "",
@@ -6679,7 +8013,7 @@ const SearchBodySchema = object({
6679
8013
  query: string().min(1),
6680
8014
  project: string().optional()
6681
8015
  });
6682
- const Route$b = createFileRoute("/api/knowledge/search")({
8016
+ const Route$c = createFileRoute("/api/knowledge/search")({
6683
8017
  server: {
6684
8018
  handlers: {
6685
8019
  GET: async ({ request }) => {
@@ -6701,7 +8035,7 @@ const Route$b = createFileRoute("/api/knowledge/search")({
6701
8035
  }
6702
8036
  }
6703
8037
  });
6704
- const Route$a = createFileRoute("/api/knowledge/project-context")({
8038
+ const Route$b = createFileRoute("/api/knowledge/project-context")({
6705
8039
  server: {
6706
8040
  handlers: {
6707
8041
  GET: async ({ request }) => {
@@ -6783,14 +8117,14 @@ function updateCandidateDraft(id, updates) {
6783
8117
  candidates.set(id, updated);
6784
8118
  return updated;
6785
8119
  }
6786
- const Route$9 = createFileRoute("/api/knowledge/candidates")({
8120
+ const Route$a = createFileRoute("/api/knowledge/candidates")({
6787
8121
  server: {
6788
8122
  handlers: {
6789
8123
  GET: () => Response.json({ candidates: listCandidates() })
6790
8124
  }
6791
8125
  }
6792
8126
  });
6793
- const Route$8 = createFileRoute("/api/config/paths")({
8127
+ const Route$9 = createFileRoute("/api/config/paths")({
6794
8128
  server: {
6795
8129
  handlers: {
6796
8130
  GET: () => {
@@ -6801,6 +8135,653 @@ const Route$8 = createFileRoute("/api/config/paths")({
6801
8135
  }
6802
8136
  }
6803
8137
  });
8138
+ function formatNullable(value) {
8139
+ return value === null ? "n/a" : String(value);
8140
+ }
8141
+ function formatList$1(values) {
8142
+ return values.length > 0 ? values.join(", ") : "n/a";
8143
+ }
8144
+ function lower(value) {
8145
+ return value.toLowerCase();
8146
+ }
8147
+ function logEvidenceText(log) {
8148
+ return lower(
8149
+ [
8150
+ log.error,
8151
+ log.path,
8152
+ log.model,
8153
+ log.provider,
8154
+ log.status === null ? null : String(log.status),
8155
+ log.isStreaming ? "streaming" : "non-streaming"
8156
+ ].filter((value) => value !== null).join(" ")
8157
+ );
8158
+ }
8159
+ function collectErrorCandidates(session) {
8160
+ const candidates2 = session.latestLogs.filter((log) => log.hasError).map((log) => ({ log, text: logEvidenceText(log) }));
8161
+ if (session.lastTaskError !== null) {
8162
+ candidates2.push({ log: null, text: lower(session.lastTaskError) });
8163
+ }
8164
+ return candidates2;
8165
+ }
8166
+ function hasText(candidates2, patterns) {
8167
+ return candidates2.some(
8168
+ (candidate) => patterns.some((pattern) => candidate.text.includes(pattern))
8169
+ );
8170
+ }
8171
+ function hasStatus(session, predicate) {
8172
+ return session.latestLogs.some((log) => log.status !== null && predicate(log.status));
8173
+ }
8174
+ function evidenceIds(candidates2) {
8175
+ const ids = /* @__PURE__ */ new Set();
8176
+ for (const candidate of candidates2) {
8177
+ if (candidate.log !== null) {
8178
+ ids.add(candidate.log.id);
8179
+ }
8180
+ }
8181
+ return [...ids].sort((left, right) => left - right);
8182
+ }
8183
+ function hasStreamingMismatch(session) {
8184
+ for (const failed of session.latestLogs.filter((log) => log.hasError)) {
8185
+ const matchingSuccess = session.latestLogs.some(
8186
+ (log) => !log.hasError && log.model === failed.model && log.provider === failed.provider && log.isStreaming !== failed.isStreaming
8187
+ );
8188
+ if (matchingSuccess) return true;
8189
+ }
8190
+ return false;
8191
+ }
8192
+ function categorySummary(category) {
8193
+ switch (category) {
8194
+ case "none":
8195
+ return "Session completed without captured errors.";
8196
+ case "active":
8197
+ return "Session still has active requests or running tasks.";
8198
+ case "provider-timeout":
8199
+ return "Provider call appears to have timed out.";
8200
+ case "provider-http-error":
8201
+ return "Provider returned an HTTP error.";
8202
+ case "provider-auth-error":
8203
+ return "Provider authentication or API key configuration appears invalid.";
8204
+ case "provider-rate-limit":
8205
+ return "Provider rejected the request because of rate limit or quota pressure.";
8206
+ case "network-or-container":
8207
+ return "Network connectivity looks suspicious; check container boundaries and localhost use.";
8208
+ case "streaming-mismatch":
8209
+ return "Streaming and non-streaming behavior differ for the same provider/model.";
8210
+ case "context-pressure":
8211
+ return "The failure looks related to context window or token pressure.";
8212
+ case "runtime-task-error":
8213
+ return "Inspector session runtime reported a task error.";
8214
+ case "model-output-error":
8215
+ return "The model output or upstream payload shape appears invalid.";
8216
+ case "unknown-failure":
8217
+ return "Inspector captured errors, but the root cause is not yet classified.";
8218
+ case "no-session-data":
8219
+ return "No Inspector session data was available for this run.";
8220
+ }
8221
+ }
8222
+ function categoryHints(category) {
8223
+ switch (category) {
8224
+ case "none":
8225
+ return ["Keep the evidence pack as the success proof for CI or release notes."];
8226
+ case "active":
8227
+ return ["Wait for the session to settle, then export evidence again."];
8228
+ case "provider-timeout":
8229
+ return [
8230
+ "Increase the provider test timeout for slow models.",
8231
+ "Compare streaming and non-streaming probes for the same model."
8232
+ ];
8233
+ case "provider-http-error":
8234
+ return [
8235
+ "Open the cited log ids and inspect upstream status, response body, and provider URL."
8236
+ ];
8237
+ case "provider-auth-error":
8238
+ return ["Verify the provider API key, auth header mode, and configured base URL."];
8239
+ case "provider-rate-limit":
8240
+ return [
8241
+ "Retry after provider quota recovers or switch to a less constrained provider/model."
8242
+ ];
8243
+ case "network-or-container":
8244
+ return [
8245
+ "If the coding agent and Inspector are in different containers, replace localhost with a reachable host.",
8246
+ "Check DNS, proxy, firewall, and container network routes."
8247
+ ];
8248
+ case "streaming-mismatch":
8249
+ return [
8250
+ "Compare the streaming and non-streaming captured logs side by side.",
8251
+ "Check whether the provider's OpenAI-compatible endpoint handles stream=false differently."
8252
+ ];
8253
+ case "context-pressure":
8254
+ return [
8255
+ "Reduce prompt/tool schema/history size or configure the model context window metadata."
8256
+ ];
8257
+ case "runtime-task-error":
8258
+ return ["Inspect the session runtime error and retry after clearing stuck session tasks."];
8259
+ case "model-output-error":
8260
+ return ["Inspect the raw response and schema parsing details for the cited log ids."];
8261
+ case "unknown-failure":
8262
+ return [
8263
+ "Open the cited logs and inspect error/status/latency fields to refine the diagnosis."
8264
+ ];
8265
+ case "no-session-data":
8266
+ return [
8267
+ "Confirm the run's sessionId matches captured traffic and that history scanning is enabled when needed."
8268
+ ];
8269
+ }
8270
+ }
8271
+ function classifyCategory(session, candidates2) {
8272
+ if (session.status === "active") return "active";
8273
+ if (session.errorCount === 0 && session.failedRequests === 0 && session.lastTaskError === null) {
8274
+ return "none";
8275
+ }
8276
+ if (hasStreamingMismatch(session)) return "streaming-mismatch";
8277
+ if (hasText(candidates2, [
8278
+ "timeout",
8279
+ "timed out",
8280
+ "etimedout",
8281
+ "deadline",
8282
+ "aborterror",
8283
+ "econnaborted"
8284
+ ])) {
8285
+ return "provider-timeout";
8286
+ }
8287
+ if (hasText(candidates2, ["localhost", "econnrefused", "enotfound", "network", "fetch failed"])) {
8288
+ return "network-or-container";
8289
+ }
8290
+ if (hasStatus(session, (status) => status === 401 || status === 403)) {
8291
+ return "provider-auth-error";
8292
+ }
8293
+ if (hasText(candidates2, ["unauthorized", "forbidden", "invalid api key", "api key"])) {
8294
+ return "provider-auth-error";
8295
+ }
8296
+ if (hasStatus(session, (status) => status === 429)) return "provider-rate-limit";
8297
+ if (hasText(candidates2, ["rate limit", "quota"])) return "provider-rate-limit";
8298
+ if (hasText(candidates2, ["context", "too many tokens", "token limit", "maximum context"])) {
8299
+ return "context-pressure";
8300
+ }
8301
+ if (session.lastTaskError !== null) return "runtime-task-error";
8302
+ if (hasText(candidates2, ["invalid json", "schema", "parse", "malformed"])) {
8303
+ return "model-output-error";
8304
+ }
8305
+ if (hasStatus(session, (status) => status >= 400)) return "provider-http-error";
8306
+ return "unknown-failure";
8307
+ }
8308
+ function severityFor(category) {
8309
+ switch (category) {
8310
+ case "none":
8311
+ return "none";
8312
+ case "active":
8313
+ return "low";
8314
+ case "provider-timeout":
8315
+ case "provider-http-error":
8316
+ case "provider-auth-error":
8317
+ case "provider-rate-limit":
8318
+ case "network-or-container":
8319
+ case "streaming-mismatch":
8320
+ case "runtime-task-error":
8321
+ return "high";
8322
+ case "context-pressure":
8323
+ case "model-output-error":
8324
+ case "unknown-failure":
8325
+ case "no-session-data":
8326
+ return "medium";
8327
+ }
8328
+ }
8329
+ function outcomeFor$1(category) {
8330
+ switch (category) {
8331
+ case "none":
8332
+ return "success";
8333
+ case "active":
8334
+ return "active";
8335
+ case "no-session-data":
8336
+ return "unknown";
8337
+ case "provider-timeout":
8338
+ case "provider-http-error":
8339
+ case "provider-auth-error":
8340
+ case "provider-rate-limit":
8341
+ case "network-or-container":
8342
+ case "streaming-mismatch":
8343
+ case "context-pressure":
8344
+ case "runtime-task-error":
8345
+ case "model-output-error":
8346
+ case "unknown-failure":
8347
+ return "failure";
8348
+ }
8349
+ }
8350
+ function classifyRunFailure(run, session) {
8351
+ if (session === null) {
8352
+ const category2 = "no-session-data";
8353
+ return {
8354
+ outcome: outcomeFor$1(category2),
8355
+ severity: severityFor(category2),
8356
+ category: category2,
8357
+ summary: `${categorySummary(category2)} Run ${run.id} is bound to session ${run.sessionId}.`,
8358
+ evidenceLogIds: [],
8359
+ hints: categoryHints(category2)
8360
+ };
8361
+ }
8362
+ const candidates2 = collectErrorCandidates(session);
8363
+ const category = classifyCategory(session, candidates2);
8364
+ const summary = category === "none" ? `${categorySummary(category)} ${String(session.requestCount)} request(s), ${String(
8365
+ session.tokenUsage.total
8366
+ )} token(s).` : `${categorySummary(category)} ${String(session.errorCount)} error log(s), ${String(
8367
+ session.failedRequests
8368
+ )} failed request(s).`;
8369
+ return {
8370
+ outcome: outcomeFor$1(category),
8371
+ severity: severityFor(category),
8372
+ category,
8373
+ summary,
8374
+ evidenceLogIds: evidenceIds(candidates2),
8375
+ hints: categoryHints(category)
8376
+ };
8377
+ }
8378
+ function logTitle(log) {
8379
+ if (log.hasError) return `Request failed: log ${String(log.id)}`;
8380
+ if (log.status !== null && log.status >= 200 && log.status < 400) {
8381
+ return `Request succeeded: log ${String(log.id)}`;
8382
+ }
8383
+ return `Request captured: log ${String(log.id)}`;
8384
+ }
8385
+ function logDetails(log) {
8386
+ const parts = [
8387
+ `path=${log.path}`,
8388
+ `status=${formatNullable(log.status)}`,
8389
+ `model=${formatNullable(log.model)}`,
8390
+ `provider=${formatNullable(log.provider)}`,
8391
+ `streaming=${log.isStreaming ? "yes" : "no"}`,
8392
+ `latencyMs=${formatNullable(log.latencyMs)}`
8393
+ ];
8394
+ if (log.error !== null) {
8395
+ parts.push(`error=${log.error}`);
8396
+ }
8397
+ return parts.join(", ");
8398
+ }
8399
+ function buildRunTimeline(run, session, exportedAt) {
8400
+ const events = [
8401
+ {
8402
+ timestamp: run.createdAt,
8403
+ kind: "run-created",
8404
+ severity: "info",
8405
+ title: "Run declared",
8406
+ details: `Run ${run.id} was declared for session ${run.sessionId}.`,
8407
+ logId: null,
8408
+ status: null,
8409
+ model: null,
8410
+ provider: null,
8411
+ latencyMs: null,
8412
+ isStreaming: null
8413
+ }
8414
+ ];
8415
+ if (session !== null && session.createdAt !== null) {
8416
+ events.push({
8417
+ timestamp: session.createdAt,
8418
+ kind: "session-started",
8419
+ severity: "info",
8420
+ title: "Session started",
8421
+ details: `Inspector session ${session.id} started with source ${session.source ?? "unknown"}.`,
8422
+ logId: null,
8423
+ status: null,
8424
+ model: null,
8425
+ provider: null,
8426
+ latencyMs: null,
8427
+ isStreaming: null
8428
+ });
8429
+ }
8430
+ if (session !== null) {
8431
+ const logs = [...session.latestLogs].sort(
8432
+ (left, right) => left.timestamp.localeCompare(right.timestamp)
8433
+ );
8434
+ for (const log of logs) {
8435
+ events.push({
8436
+ timestamp: log.timestamp,
8437
+ kind: "request",
8438
+ severity: log.hasError ? "error" : "success",
8439
+ title: logTitle(log),
8440
+ details: logDetails(log),
8441
+ logId: log.id,
8442
+ status: log.status,
8443
+ model: log.model,
8444
+ provider: log.provider,
8445
+ latencyMs: log.latencyMs,
8446
+ isStreaming: log.isStreaming
8447
+ });
8448
+ }
8449
+ if (session.updatedAt !== null) {
8450
+ events.push({
8451
+ timestamp: session.updatedAt,
8452
+ kind: "session-finished",
8453
+ severity: session.status === "failed" ? "error" : session.status === "active" ? "warning" : "success",
8454
+ title: `Session ${session.status}`,
8455
+ details: `${String(session.requestCount)} request(s), ${String(
8456
+ session.errorCount
8457
+ )} error(s), ${String(session.tokenUsage.total)} token(s).`,
8458
+ logId: session.lastLogId,
8459
+ status: null,
8460
+ model: session.lastModel,
8461
+ provider: null,
8462
+ latencyMs: null,
8463
+ isStreaming: null
8464
+ });
8465
+ }
8466
+ }
8467
+ events.push({
8468
+ timestamp: exportedAt,
8469
+ kind: "evidence-exported",
8470
+ severity: "info",
8471
+ title: "Evidence exported",
8472
+ details: `Evidence pack exported at ${exportedAt}.`,
8473
+ logId: null,
8474
+ status: null,
8475
+ model: null,
8476
+ provider: null,
8477
+ latencyMs: null,
8478
+ isStreaming: null
8479
+ });
8480
+ return events.sort((left, right) => left.timestamp.localeCompare(right.timestamp));
8481
+ }
8482
+ function reportStatus(classification) {
8483
+ switch (classification.outcome) {
8484
+ case "success":
8485
+ return "PASS";
8486
+ case "failure":
8487
+ return "FAIL";
8488
+ case "active":
8489
+ return "RUNNING";
8490
+ case "unknown":
8491
+ return "UNKNOWN";
8492
+ }
8493
+ }
8494
+ function timelineMarkdown(timeline) {
8495
+ if (timeline.length === 0) return ["No timeline events were available."];
8496
+ return [
8497
+ "| Time | Event | Severity | Evidence |",
8498
+ "| --- | --- | --- | --- |",
8499
+ ...timeline.map((event) => {
8500
+ const evidence = event.logId === null ? "n/a" : `log ${String(event.logId)}`;
8501
+ return `| ${event.timestamp} | ${event.title} | ${event.severity} | ${evidence} |`;
8502
+ })
8503
+ ];
8504
+ }
8505
+ function buildJenkinsReport(input) {
8506
+ const status = reportStatus(input.classification);
8507
+ const session = input.session;
8508
+ const summary = `[${status}] ${input.classification.summary}`;
8509
+ const lines = [
8510
+ `# Jenkins Report: ${input.run.title}`,
8511
+ "",
8512
+ `- Status: ${status}`,
8513
+ `- Run ID: ${input.run.id}`,
8514
+ `- Session ID: ${input.run.sessionId}`,
8515
+ `- Inspector URL: ${session?.inspectorUrl ?? "n/a"}`,
8516
+ `- Category: ${input.classification.category}`,
8517
+ `- Severity: ${input.classification.severity}`,
8518
+ `- Summary: ${input.classification.summary}`,
8519
+ `- Evidence Logs: ${input.classification.evidenceLogIds.length > 0 ? input.classification.evidenceLogIds.map((id) => `#${String(id)}`).join(", ") : "n/a"}`,
8520
+ "",
8521
+ "## Session Metrics",
8522
+ "",
8523
+ `- Requests: ${String(session?.requestCount ?? 0)}`,
8524
+ `- Errors: ${String(session?.errorCount ?? 0)}`,
8525
+ `- Models: ${formatList$1(session?.models ?? [])}`,
8526
+ `- Providers: ${formatList$1(session?.providers ?? [])}`,
8527
+ `- Tokens: ${String(session?.tokenUsage.total ?? 0)}`,
8528
+ "",
8529
+ "## Timeline",
8530
+ "",
8531
+ ...timelineMarkdown(input.timeline),
8532
+ "",
8533
+ "## Artifacts",
8534
+ "",
8535
+ `- JSON: ${input.evidencePaths.jsonPath}`,
8536
+ `- Markdown: ${input.evidencePaths.markdownPath}`,
8537
+ `- HTML: ${input.evidencePaths.htmlPath}`,
8538
+ "",
8539
+ "## Next Actions",
8540
+ "",
8541
+ ...input.classification.hints.map((hint) => `- ${hint}`),
8542
+ ""
8543
+ ];
8544
+ return {
8545
+ status,
8546
+ summary,
8547
+ markdown: lines.join("\n")
8548
+ };
8549
+ }
8550
+ function analyzeEvidence(input) {
8551
+ const classification = classifyRunFailure(input.run, input.session);
8552
+ const timeline = buildRunTimeline(input.run, input.session, input.exportedAt);
8553
+ const jenkinsReport = buildJenkinsReport({
8554
+ run: input.run,
8555
+ session: input.session,
8556
+ timeline,
8557
+ classification,
8558
+ evidencePaths: input.evidencePaths
8559
+ });
8560
+ return { classification, timeline, jenkinsReport };
8561
+ }
8562
+ function evidenceDir(runId) {
8563
+ return join(getDataDir(), "evidence", runId);
8564
+ }
8565
+ function jsonPath(runId) {
8566
+ return join(evidenceDir(runId), "evidence.json");
8567
+ }
8568
+ function markdownPath(runId) {
8569
+ return join(evidenceDir(runId), "evidence.md");
8570
+ }
8571
+ function htmlPath(runId) {
8572
+ return join(evidenceDir(runId), "evidence.html");
8573
+ }
8574
+ function formatList(values) {
8575
+ return values.length > 0 ? values.join(", ") : "n/a";
8576
+ }
8577
+ function escapeHtml(value) {
8578
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
8579
+ }
8580
+ function buildMarkdown(document) {
8581
+ const session = document.session;
8582
+ const lines = [
8583
+ `# ${document.run.title}`,
8584
+ "",
8585
+ `- Run ID: ${document.run.id}`,
8586
+ `- Session ID: ${document.run.sessionId}`,
8587
+ `- Status: ${document.run.status}`,
8588
+ `- Project: ${document.run.project ?? "n/a"}`,
8589
+ `- Agent: ${document.run.agent ?? "n/a"}`,
8590
+ `- Created: ${document.run.createdAt}`,
8591
+ "",
8592
+ `- Outcome: ${document.classification.outcome}`,
8593
+ `- Failure Category: ${document.classification.category}`,
8594
+ `- Severity: ${document.classification.severity}`,
8595
+ `- Evidence JSON: ${document.evidence.jsonPath}`,
8596
+ `- Evidence HTML: ${document.evidence.htmlPath}`,
8597
+ ""
8598
+ ];
8599
+ if (document.run.task !== null) {
8600
+ lines.push("## Task", "", document.run.task, "");
8601
+ }
8602
+ if (session === null) {
8603
+ lines.push("## Session", "", "No Inspector session data was available for this run.", "");
8604
+ } else {
8605
+ lines.push(
8606
+ "## Session",
8607
+ "",
8608
+ `- Inspector URL: ${session.inspectorUrl}`,
8609
+ `- Compact Logs URL: ${session.compactLogsUrl}`,
8610
+ `- Status: ${session.status}`,
8611
+ `- Coverage: ${session.logCoverage}`,
8612
+ `- Requests: ${session.requestCount}`,
8613
+ `- Errors: ${session.errorCount}`,
8614
+ `- Models: ${formatList(session.models)}`,
8615
+ `- Providers: ${formatList(session.providers)}`,
8616
+ `- Tokens: ${session.tokenUsage.total}`,
8617
+ "",
8618
+ "## Failure Classification",
8619
+ "",
8620
+ document.classification.summary,
8621
+ "",
8622
+ "### Evidence Logs",
8623
+ "",
8624
+ document.classification.evidenceLogIds.length > 0 ? document.classification.evidenceLogIds.map((id) => `- #${id}`).join("\n") : "No failing log ids were identified.",
8625
+ "",
8626
+ "### Suggested Next Actions",
8627
+ "",
8628
+ ...document.classification.hints.map((hint) => `- ${hint}`),
8629
+ "",
8630
+ "## Run Timeline",
8631
+ "",
8632
+ "| Time | Event | Severity | Evidence |",
8633
+ "| --- | --- | --- | --- |",
8634
+ ...document.timeline.map((event) => {
8635
+ const evidence = event.logId === null ? "n/a" : `log ${event.logId}`;
8636
+ return `| ${event.timestamp} | ${event.title} | ${event.severity} | ${evidence} |`;
8637
+ }),
8638
+ "",
8639
+ "## Latest Logs",
8640
+ ""
8641
+ );
8642
+ if (session.latestLogs.length === 0) {
8643
+ lines.push("No compact logs were available.", "");
8644
+ } else {
8645
+ lines.push(
8646
+ "| ID | Status | Model | Latency | Path | Error |",
8647
+ "| --- | --- | --- | --- | --- | --- |"
8648
+ );
8649
+ for (const log of session.latestLogs) {
8650
+ lines.push(
8651
+ `| ${log.id} | ${log.status ?? "n/a"} | ${log.model ?? "n/a"} | ${log.latencyMs ?? "n/a"} | ${log.path} | ${log.hasError ? "yes" : "no"} |`
8652
+ );
8653
+ }
8654
+ lines.push("");
8655
+ }
8656
+ }
8657
+ lines.push("## Jenkins Report", "", document.jenkinsReport.markdown, "");
8658
+ return `${lines.join("\n")}
8659
+ `;
8660
+ }
8661
+ function buildHtml(markdown, title) {
8662
+ return `<!doctype html>
8663
+ <html lang="en">
8664
+ <head>
8665
+ <meta charset="utf-8">
8666
+ <title>${escapeHtml(title)}</title>
8667
+ <style>
8668
+ body { font-family: system-ui, sans-serif; max-width: 960px; margin: 40px auto; line-height: 1.5; }
8669
+ pre { white-space: pre-wrap; background: #f6f8fa; padding: 16px; border-radius: 8px; }
8670
+ </style>
8671
+ </head>
8672
+ <body>
8673
+ <pre>${escapeHtml(markdown)}</pre>
8674
+ </body>
8675
+ </html>
8676
+ `;
8677
+ }
8678
+ async function exportRunEvidence(run, options, baseUrl) {
8679
+ const session = await getSessionInfo(run.sessionId, {
8680
+ baseUrl,
8681
+ includeHistory: options?.includeHistory,
8682
+ latestLogLimit: options?.latestLogLimit
8683
+ });
8684
+ const exportedAt = (/* @__PURE__ */ new Date()).toISOString();
8685
+ const evidence = {
8686
+ jsonPath: jsonPath(run.id),
8687
+ markdownPath: markdownPath(run.id),
8688
+ htmlPath: htmlPath(run.id),
8689
+ exportedAt
8690
+ };
8691
+ const documentRun = {
8692
+ ...run,
8693
+ evidence,
8694
+ updatedAt: exportedAt
8695
+ };
8696
+ const analysis = analyzeEvidence({
8697
+ run: documentRun,
8698
+ session,
8699
+ exportedAt,
8700
+ evidencePaths: {
8701
+ jsonPath: evidence.jsonPath,
8702
+ markdownPath: evidence.markdownPath,
8703
+ htmlPath: evidence.htmlPath
8704
+ }
8705
+ });
8706
+ const document = {
8707
+ run: documentRun,
8708
+ evidence,
8709
+ session,
8710
+ timeline: analysis.timeline,
8711
+ classification: analysis.classification,
8712
+ jenkinsReport: analysis.jenkinsReport
8713
+ };
8714
+ const markdown = buildMarkdown(document);
8715
+ mkdirSync(evidenceDir(run.id), { recursive: true });
8716
+ writeFileSync(
8717
+ evidence.jsonPath,
8718
+ `${JSON.stringify({ ...document, evidence }, null, 2)}
8719
+ `,
8720
+ "utf8"
8721
+ );
8722
+ writeFileSync(evidence.markdownPath, markdown, "utf8");
8723
+ writeFileSync(evidence.htmlPath, buildHtml(markdown, run.title), "utf8");
8724
+ const updatedRun = updateRunEvidence(run.id, evidence) ?? documentRun;
8725
+ return { ...document, run: updatedRun };
8726
+ }
8727
+ function readEvidenceMarkdown(run) {
8728
+ const path2 = run.evidence?.markdownPath;
8729
+ if (path2 === void 0 || !existsSync(path2)) return null;
8730
+ try {
8731
+ return readFileSync(path2, "utf8");
8732
+ } catch {
8733
+ return null;
8734
+ }
8735
+ }
8736
+ async function readJsonBody(request) {
8737
+ try {
8738
+ const raw = await request.text();
8739
+ if (raw.trim().length === 0) return { ok: true, value: void 0 };
8740
+ return { ok: true, value: JSON.parse(raw) };
8741
+ } catch {
8742
+ return { ok: false };
8743
+ }
8744
+ }
8745
+ const Route$8 = createFileRoute("/api/runs/$runId/evidence")({
8746
+ server: {
8747
+ handlers: {
8748
+ GET: ({ params }) => {
8749
+ const run = getRun(params.runId);
8750
+ if (run === null) {
8751
+ return Response.json({ error: "Run not found" }, { status: 404 });
8752
+ }
8753
+ const markdown = readEvidenceMarkdown(run);
8754
+ if (run.evidence === null || markdown === null) {
8755
+ return Response.json(
8756
+ { error: "Evidence has not been exported for this run" },
8757
+ { status: 404 }
8758
+ );
8759
+ }
8760
+ return Response.json({ run, evidence: run.evidence, markdown });
8761
+ },
8762
+ POST: async ({ params, request }) => {
8763
+ const run = getRun(params.runId);
8764
+ if (run === null) {
8765
+ return Response.json({ error: "Run not found" }, { status: 404 });
8766
+ }
8767
+ const body = await readJsonBody(request);
8768
+ if (!body.ok) {
8769
+ return Response.json({ error: "Invalid JSON body" }, { status: 400 });
8770
+ }
8771
+ const parsed = EvidenceExportOptionsSchema.safeParse(body.value);
8772
+ if (!parsed.success) {
8773
+ return Response.json(
8774
+ { error: "Invalid request body", details: parsed.error.format() },
8775
+ { status: 400 }
8776
+ );
8777
+ }
8778
+ return Response.json(
8779
+ await exportRunEvidence(run, parsed.data, new URL(request.url).origin)
8780
+ );
8781
+ }
8782
+ }
8783
+ }
8784
+ });
6804
8785
  const MEMORY_PROBE_FACTS = [
6805
8786
  "Project fact: Agent Inspector captures LLM traffic and turns it into Raw Trace, Session Episode, and Memory Candidate layers.",
6806
8787
  "Procedure: before release, run bun test, npm run typecheck, npm run lint, npm run build, and openspec validate --all --strict.",
@@ -8220,106 +10201,121 @@ const Route = createFileRoute("/api/knowledge/candidates/$candidateId/promote")(
8220
10201
  }
8221
10202
  }
8222
10203
  });
8223
- const IndexRoute = Route$r.update({
10204
+ const IndexRoute = Route$u.update({
8224
10205
  id: "/",
8225
10206
  path: "/",
8226
- getParentRoute: () => Route$s
10207
+ getParentRoute: () => Route$v
8227
10208
  });
8228
- const SessionSessionIdRoute = Route$q.update({
10209
+ const SessionSessionIdRoute = Route$t.update({
8229
10210
  id: "/session/$sessionId",
8230
10211
  path: "/session/$sessionId",
8231
- getParentRoute: () => Route$s
10212
+ getParentRoute: () => Route$v
8232
10213
  });
8233
- const ProxySplatRoute = Route$p.update({
10214
+ const ProxySplatRoute = Route$s.update({
8234
10215
  id: "/proxy/$",
8235
10216
  path: "/proxy/$",
8236
- getParentRoute: () => Route$s
10217
+ getParentRoute: () => Route$v
8237
10218
  });
8238
- const ApiSessionsRoute = Route$o.update({
10219
+ const ApiSessionsRoute = Route$r.update({
8239
10220
  id: "/api/sessions",
8240
10221
  path: "/api/sessions",
8241
- getParentRoute: () => Route$s
10222
+ getParentRoute: () => Route$v
8242
10223
  });
8243
- const ApiProvidersRoute = Route$n.update({
10224
+ const ApiRunsRoute = Route$q.update({
10225
+ id: "/api/runs",
10226
+ path: "/api/runs",
10227
+ getParentRoute: () => Route$v
10228
+ });
10229
+ const ApiProvidersRoute = Route$p.update({
8244
10230
  id: "/api/providers",
8245
10231
  path: "/api/providers",
8246
- getParentRoute: () => Route$s
10232
+ getParentRoute: () => Route$v
8247
10233
  });
8248
- const ApiModelsRoute = Route$m.update({
10234
+ const ApiModelsRoute = Route$o.update({
8249
10235
  id: "/api/models",
8250
10236
  path: "/api/models",
8251
- getParentRoute: () => Route$s
10237
+ getParentRoute: () => Route$v
8252
10238
  });
8253
- const ApiMcpRoute = Route$l.update({
10239
+ const ApiMcpRoute = Route$n.update({
8254
10240
  id: "/api/mcp",
8255
10241
  path: "/api/mcp",
8256
- getParentRoute: () => Route$s
10242
+ getParentRoute: () => Route$v
8257
10243
  });
8258
- const ApiLogsRoute = Route$k.update({
10244
+ const ApiLogsRoute = Route$m.update({
8259
10245
  id: "/api/logs",
8260
10246
  path: "/api/logs",
8261
- getParentRoute: () => Route$s
10247
+ getParentRoute: () => Route$v
8262
10248
  });
8263
- const ApiHealthRoute = Route$j.update({
10249
+ const ApiHealthRoute = Route$l.update({
8264
10250
  id: "/api/health",
8265
10251
  path: "/api/health",
8266
- getParentRoute: () => Route$s
10252
+ getParentRoute: () => Route$v
8267
10253
  });
8268
- const ApiConfigRoute = Route$i.update({
10254
+ const ApiConfigRoute = Route$k.update({
8269
10255
  id: "/api/config",
8270
10256
  path: "/api/config",
8271
- getParentRoute: () => Route$s
10257
+ getParentRoute: () => Route$v
10258
+ });
10259
+ const ApiRunsRunIdRoute = Route$j.update({
10260
+ id: "/$runId",
10261
+ path: "/$runId",
10262
+ getParentRoute: () => ApiRunsRoute
8272
10263
  });
8273
- const ApiProvidersScanRoute = Route$h.update({
10264
+ const ApiProvidersScanRoute = Route$i.update({
8274
10265
  id: "/scan",
8275
10266
  path: "/scan",
8276
10267
  getParentRoute: () => ApiProvidersRoute
8277
10268
  });
8278
- const ApiProvidersImportRoute = Route$g.update({
10269
+ const ApiProvidersImportRoute = Route$h.update({
8279
10270
  id: "/import",
8280
10271
  path: "/import",
8281
10272
  getParentRoute: () => ApiProvidersRoute
8282
10273
  });
8283
- const ApiProvidersExportRoute = Route$f.update({
10274
+ const ApiProvidersExportRoute = Route$g.update({
8284
10275
  id: "/export",
8285
10276
  path: "/export",
8286
10277
  getParentRoute: () => ApiProvidersRoute
8287
10278
  });
8288
- const ApiProvidersProviderIdRoute = Route$e.update({
10279
+ const ApiProvidersProviderIdRoute = Route$f.update({
8289
10280
  id: "/$providerId",
8290
10281
  path: "/$providerId",
8291
10282
  getParentRoute: () => ApiProvidersRoute
8292
10283
  });
8293
- const ApiLogsStreamRoute = Route$d.update({
10284
+ const ApiLogsStreamRoute = Route$e.update({
8294
10285
  id: "/stream",
8295
10286
  path: "/stream",
8296
10287
  getParentRoute: () => ApiLogsRoute
8297
10288
  });
8298
- const ApiLogsIdRoute = Route$c.update({
10289
+ const ApiLogsIdRoute = Route$d.update({
8299
10290
  id: "/$id",
8300
10291
  path: "/$id",
8301
10292
  getParentRoute: () => ApiLogsRoute
8302
10293
  });
8303
- const ApiKnowledgeSearchRoute = Route$b.update({
10294
+ const ApiKnowledgeSearchRoute = Route$c.update({
8304
10295
  id: "/api/knowledge/search",
8305
10296
  path: "/api/knowledge/search",
8306
- getParentRoute: () => Route$s
10297
+ getParentRoute: () => Route$v
8307
10298
  });
8308
- const ApiKnowledgeProjectContextRoute = Route$a.update({
10299
+ const ApiKnowledgeProjectContextRoute = Route$b.update({
8309
10300
  id: "/api/knowledge/project-context",
8310
10301
  path: "/api/knowledge/project-context",
8311
- getParentRoute: () => Route$s
10302
+ getParentRoute: () => Route$v
8312
10303
  });
8313
- const ApiKnowledgeCandidatesRoute = Route$9.update({
10304
+ const ApiKnowledgeCandidatesRoute = Route$a.update({
8314
10305
  id: "/api/knowledge/candidates",
8315
10306
  path: "/api/knowledge/candidates",
8316
- getParentRoute: () => Route$s
10307
+ getParentRoute: () => Route$v
8317
10308
  });
8318
- const ApiConfigPathsRoute = Route$8.update({
10309
+ const ApiConfigPathsRoute = Route$9.update({
8319
10310
  id: "/paths",
8320
10311
  path: "/paths",
8321
10312
  getParentRoute: () => ApiConfigRoute
8322
10313
  });
10314
+ const ApiRunsRunIdEvidenceRoute = Route$8.update({
10315
+ id: "/evidence",
10316
+ path: "/evidence",
10317
+ getParentRoute: () => ApiRunsRunIdRoute
10318
+ });
8323
10319
  const ApiProvidersProviderIdTestRoute = Route$7.update({
8324
10320
  id: "/test",
8325
10321
  path: "/test",
@@ -8353,7 +10349,7 @@ const ApiProvidersProviderIdTestLogRoute = Route$2.update({
8353
10349
  const ApiKnowledgeSessionsSessionIdCandidatesRoute = Route$1.update({
8354
10350
  id: "/api/knowledge/sessions/$sessionId/candidates",
8355
10351
  path: "/api/knowledge/sessions/$sessionId/candidates",
8356
- getParentRoute: () => Route$s
10352
+ getParentRoute: () => Route$v
8357
10353
  });
8358
10354
  const ApiKnowledgeCandidatesCandidateIdPromoteRoute = Route.update({
8359
10355
  id: "/promote",
@@ -8400,6 +10396,16 @@ const ApiProvidersRouteChildren = {
8400
10396
  const ApiProvidersRouteWithChildren = ApiProvidersRoute._addFileChildren(
8401
10397
  ApiProvidersRouteChildren
8402
10398
  );
10399
+ const ApiRunsRunIdRouteChildren = {
10400
+ ApiRunsRunIdEvidenceRoute
10401
+ };
10402
+ const ApiRunsRunIdRouteWithChildren = ApiRunsRunIdRoute._addFileChildren(
10403
+ ApiRunsRunIdRouteChildren
10404
+ );
10405
+ const ApiRunsRouteChildren = {
10406
+ ApiRunsRunIdRoute: ApiRunsRunIdRouteWithChildren
10407
+ };
10408
+ const ApiRunsRouteWithChildren = ApiRunsRoute._addFileChildren(ApiRunsRouteChildren);
8403
10409
  const ApiKnowledgeCandidatesCandidateIdRouteChildren = {
8404
10410
  ApiKnowledgeCandidatesCandidateIdPromoteRoute
8405
10411
  };
@@ -8420,6 +10426,7 @@ const rootRouteChildren = {
8420
10426
  ApiMcpRoute,
8421
10427
  ApiModelsRoute,
8422
10428
  ApiProvidersRoute: ApiProvidersRouteWithChildren,
10429
+ ApiRunsRoute: ApiRunsRouteWithChildren,
8423
10430
  ApiSessionsRoute,
8424
10431
  ProxySplatRoute,
8425
10432
  SessionSessionIdRoute,
@@ -8428,7 +10435,7 @@ const rootRouteChildren = {
8428
10435
  ApiKnowledgeSearchRoute,
8429
10436
  ApiKnowledgeSessionsSessionIdCandidatesRoute
8430
10437
  };
8431
- const routeTree = Route$s._addFileChildren(rootRouteChildren)._addFileTypes();
10438
+ const routeTree = Route$v._addFileChildren(rootRouteChildren)._addFileTypes();
8432
10439
  function getRouter() {
8433
10440
  const router2 = createRouter({
8434
10441
  routeTree,
@@ -8448,7 +10455,7 @@ export {
8448
10455
  MAX_SLOW_RESPONSE_THRESHOLD_SECONDS as M,
8449
10456
  OpenAIRequestSchema as O,
8450
10457
  ProviderConfigSchema as P,
8451
- Route$q as R,
10458
+ Route$t as R,
8452
10459
  TimeDisplayFormatSchema as T,
8453
10460
  DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS as a,
8454
10461
  DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS as b,