@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
@@ -17,6 +17,14 @@ import { Buffer } from "node:buffer";
17
17
  import { z } from "zod";
18
18
  import { LoopbackTimeoutError, type CallApiOptions } from "./loopback";
19
19
  import { extractLastUserMessagePreview, extractResponsePreview } from "./previewExtractor";
20
+ import {
21
+ EvidenceExportResultSchema,
22
+ InspectorRunSchema,
23
+ RecentFailuresResponseSchema,
24
+ type CreateInspectorRunInput,
25
+ type UpdateInspectorRunInput,
26
+ } from "../lib/runContract";
27
+ import { SessionInfoSchema } from "../lib/sessionInfoContract";
20
28
  import { CapturedLogSchema, type CapturedLog } from "../proxy/schemas";
21
29
 
22
30
  /** Minimal callApi contract: same shape as the real `callApi` in loopback.ts. */
@@ -36,6 +44,15 @@ const LogsListResponseSchema = z.object({
36
44
  limit: z.number().optional(),
37
45
  });
38
46
 
47
+ const SearchLogsResponseSchema = z.object({
48
+ query: z.string(),
49
+ logs: z.array(CapturedLogSchema),
50
+ total: z.number(),
51
+ offset: z.number(),
52
+ limit: z.number(),
53
+ scanned: z.number().optional(),
54
+ });
55
+
39
56
  const PAGINATION_DEFAULTS = { offset: 0, limit: 3 };
40
57
  export const LIMIT_HARD_CAP = 5;
41
58
  const LOG_DETAIL_TEXT_LIMIT = 200_000;
@@ -168,6 +185,47 @@ export async function listLogsImpl(callApi: CallApiFn, args: ListLogsArgs): Prom
168
185
  return textJson(logs.map(buildLogSummary));
169
186
  }
170
187
 
188
+ export type SearchLogsArgs = {
189
+ query?: string;
190
+ offset?: number;
191
+ limit?: number;
192
+ scanLimit?: number;
193
+ sessionId?: string;
194
+ model?: string;
195
+ };
196
+
197
+ export async function searchLogsImpl(
198
+ callApi: CallApiFn,
199
+ args: SearchLogsArgs,
200
+ ): Promise<ToolResult> {
201
+ const offset = args.offset ?? PAGINATION_DEFAULTS.offset;
202
+ const limit = clampLimit(args.limit);
203
+ const params = new URLSearchParams({
204
+ q: args.query ?? "",
205
+ offset: String(offset),
206
+ limit: String(limit),
207
+ });
208
+ if (args.scanLimit !== undefined) {
209
+ params.set("scanLimit", String(args.scanLimit));
210
+ }
211
+ if (args.sessionId !== undefined && args.sessionId.length > 0) {
212
+ params.set("sessionId", args.sessionId);
213
+ }
214
+ if (args.model !== undefined && args.model.length > 0) {
215
+ params.set("model", args.model);
216
+ }
217
+
218
+ const res = await callApi(`/api/logs?${params.toString()}`);
219
+ if (!res.ok) return toolError(`GET /api/logs search returned ${res.status}`);
220
+ const rawBody: unknown = await res.json();
221
+ const parsed = SearchLogsResponseSchema.safeParse(rawBody);
222
+ if (!parsed.success) return toolError("GET /api/logs search returned an unparseable shape");
223
+ return textJson({
224
+ ...parsed.data,
225
+ logs: parsed.data.logs.map(buildLogSummary),
226
+ });
227
+ }
228
+
171
229
  export async function getLogImpl(callApi: CallApiFn, id: number): Promise<ToolResult> {
172
230
  const res = await callApi(`/api/logs/${id}`);
173
231
  if (!res.ok) return toolError(`GET /api/logs/${id} returned ${res.status}`);
@@ -191,6 +249,129 @@ export async function listSessionsImpl(callApi: CallApiFn): Promise<ToolResult>
191
249
  return textJson(await res.json());
192
250
  }
193
251
 
252
+ export type GetSessionArgs = {
253
+ sessionId: string;
254
+ includeHistory?: boolean;
255
+ latestLogLimit?: number;
256
+ };
257
+
258
+ export async function getSessionImpl(
259
+ callApi: CallApiFn,
260
+ args: GetSessionArgs,
261
+ ): Promise<ToolResult> {
262
+ const params = new URLSearchParams({ sessionId: args.sessionId });
263
+ if (args.latestLogLimit !== undefined) {
264
+ params.set("limit", String(args.latestLogLimit));
265
+ }
266
+ if (args.includeHistory === true) {
267
+ params.set("includeHistory", "1");
268
+ }
269
+ const path = `/api/sessions?${params.toString()}`;
270
+ const res = await callApi(path);
271
+ if (!res.ok) return toolError(`GET ${path} returned ${res.status}`);
272
+ const rawBody: unknown = await res.json();
273
+ const parsed = SessionInfoSchema.safeParse(rawBody);
274
+ if (!parsed.success) return toolError("GET /api/sessions returned an unparseable session info");
275
+ return textJson(parsed.data);
276
+ }
277
+
278
+ export async function createRunImpl(
279
+ callApi: CallApiFn,
280
+ args: CreateInspectorRunInput,
281
+ ): Promise<ToolResult> {
282
+ const res = await callApi("/api/runs", {
283
+ method: "POST",
284
+ headers: { "content-type": "application/json" },
285
+ body: JSON.stringify(args ?? {}),
286
+ });
287
+ if (!res.ok) return toolError(`POST /api/runs returned ${res.status}`);
288
+ const rawBody: unknown = await res.json();
289
+ const parsed = InspectorRunSchema.safeParse(rawBody);
290
+ if (!parsed.success) return toolError("POST /api/runs returned an unparseable run");
291
+ return textJson(parsed.data);
292
+ }
293
+
294
+ export type GetRunArgs = {
295
+ runId: string;
296
+ };
297
+
298
+ export async function getRunImpl(callApi: CallApiFn, args: GetRunArgs): Promise<ToolResult> {
299
+ const path = `/api/runs/${encodeURIComponent(args.runId)}`;
300
+ const res = await callApi(path);
301
+ if (!res.ok) return toolError(`GET ${path} returned ${res.status}`);
302
+ const rawBody: unknown = await res.json();
303
+ const parsed = InspectorRunSchema.safeParse(rawBody);
304
+ if (!parsed.success) return toolError("GET /api/runs/{runId} returned an unparseable run");
305
+ return textJson(parsed.data);
306
+ }
307
+
308
+ export type UpdateRunArgs = {
309
+ runId: string;
310
+ } & UpdateInspectorRunInput;
311
+
312
+ export async function updateRunImpl(callApi: CallApiFn, args: UpdateRunArgs): Promise<ToolResult> {
313
+ const { runId, ...patch } = args;
314
+ const path = `/api/runs/${encodeURIComponent(runId)}`;
315
+ const res = await callApi(path, {
316
+ method: "PATCH",
317
+ headers: { "content-type": "application/json" },
318
+ body: JSON.stringify(patch),
319
+ });
320
+ if (!res.ok) return toolError(`PATCH ${path} returned ${res.status}`);
321
+ const rawBody: unknown = await res.json();
322
+ const parsed = InspectorRunSchema.safeParse(rawBody);
323
+ if (!parsed.success) return toolError("PATCH /api/runs/{runId} returned an unparseable run");
324
+ return textJson(parsed.data);
325
+ }
326
+
327
+ export type GetRecentFailuresArgs = {
328
+ limit?: number;
329
+ };
330
+
331
+ export async function getRecentFailuresImpl(
332
+ callApi: CallApiFn,
333
+ args: GetRecentFailuresArgs,
334
+ ): Promise<ToolResult> {
335
+ const limit = args.limit ?? 5;
336
+ const params = new URLSearchParams({ failures: "1", limit: String(limit) });
337
+ const res = await callApi(`/api/runs?${params.toString()}`);
338
+ if (!res.ok) return toolError(`GET /api/runs?failures=1 returned ${res.status}`);
339
+ const rawBody: unknown = await res.json();
340
+ const parsed = RecentFailuresResponseSchema.safeParse(rawBody);
341
+ if (!parsed.success) {
342
+ return toolError("GET /api/runs?failures=1 returned an unparseable failure list");
343
+ }
344
+ return textJson(parsed.data);
345
+ }
346
+
347
+ export type ExportEvidenceArgs = {
348
+ runId: string;
349
+ includeHistory?: boolean;
350
+ latestLogLimit?: number;
351
+ };
352
+
353
+ export async function exportEvidenceImpl(
354
+ callApi: CallApiFn,
355
+ args: ExportEvidenceArgs,
356
+ ): Promise<ToolResult> {
357
+ const path = `/api/runs/${encodeURIComponent(args.runId)}/evidence`;
358
+ const res = await callApi(path, {
359
+ method: "POST",
360
+ headers: { "content-type": "application/json" },
361
+ body: JSON.stringify({
362
+ includeHistory: args.includeHistory,
363
+ latestLogLimit: args.latestLogLimit,
364
+ }),
365
+ });
366
+ if (!res.ok) return toolError(`POST ${path} returned ${res.status}`);
367
+ const rawBody: unknown = await res.json();
368
+ const parsed = EvidenceExportResultSchema.safeParse(rawBody);
369
+ if (!parsed.success) {
370
+ return toolError("POST /api/runs/{runId}/evidence returned an unparseable evidence result");
371
+ }
372
+ return textJson(parsed.data);
373
+ }
374
+
194
375
  export async function listModelsImpl(callApi: CallApiFn): Promise<ToolResult> {
195
376
  const res = await callApi("/api/models");
196
377
  if (!res.ok) return toolError(`GET /api/models returned ${res.status}`);