@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.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-DVNvS0GQ.js → CompareDrawer-B_p6vmMQ.js} +1 -1
- package/.output/public/assets/{ProxyViewerContainer-By3XtQhZ.js → ProxyViewerContainer-DItqh1EO.js} +4 -4
- package/.output/public/assets/{ReplayDialog-Cw6bN-NR.js → ReplayDialog-BmprJ6D3.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-DnIsirBE.js → RequestAnatomy-DqO0_SVJ.js} +1 -1
- package/.output/public/assets/{ResponseView-ygsUs7FU.js → ResponseView-FZbPNdHa.js} +1 -1
- package/.output/public/assets/{StreamingChunkSequence-DMUn8qXi.js → StreamingChunkSequence-BZPzfJWj.js} +1 -1
- package/.output/public/assets/_sessionId-y2ATIp51.js +1 -0
- package/.output/public/assets/index-CQT4higx.js +1 -0
- package/.output/public/assets/{main-kBQ49n13.js → main-CzItFZlM.js} +2 -2
- package/.output/server/_libs/modelcontextprotocol__server.mjs +228 -0
- package/.output/server/{_sessionId-CYKaI68v.mjs → _sessionId-DZH8SrEZ.mjs} +3 -3
- package/.output/server/_ssr/{CompareDrawer-CHk4p41X.mjs → CompareDrawer-L0aE1UV1.mjs} +2 -2
- package/.output/server/_ssr/{ProxyViewerContainer-Co9ENCKV.mjs → ProxyViewerContainer-B62RB9ER.mjs} +7 -7
- package/.output/server/_ssr/{ReplayDialog-BRs3fk8H.mjs → ReplayDialog-FVWnpx2C.mjs} +3 -3
- package/.output/server/_ssr/{RequestAnatomy-Cb68EeZz.mjs → RequestAnatomy-DIyqW-Ny.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-DGdbfEjs.mjs → ResponseView-BX4mxEZ5.mjs} +2 -2
- package/.output/server/_ssr/{StreamingChunkSequence-DptYgirK.mjs → StreamingChunkSequence-Cs3nzOor.mjs} +2 -2
- package/.output/server/_ssr/{index-Ef5mCuww.mjs → index-Dik2Mc3h.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-Cvv7SJnd.mjs → router-DCPg8ykx.mjs} +2087 -80
- package/.output/server/_tanstack-start-manifest_v-BaoL3JCh.mjs +4 -0
- package/.output/server/index.mjs +61 -61
- package/README.md +55 -1
- package/package.json +1 -1
- package/src/lib/runContract.ts +162 -0
- package/src/lib/sessionInfoContract.ts +69 -0
- package/src/mcp/server.ts +580 -2
- package/src/mcp/toolHandlers.ts +181 -0
- package/src/proxy/evidenceAnalysis.ts +522 -0
- package/src/proxy/evidenceExporter.ts +215 -0
- package/src/proxy/logSearch.ts +118 -0
- package/src/proxy/runFailures.ts +100 -0
- package/src/proxy/runStore.ts +159 -0
- package/src/proxy/sessionInfo.ts +222 -0
- package/src/proxy/sessionSupervisor.ts +8 -5
- package/src/proxy/store.ts +77 -0
- package/src/routes/api/logs.ts +16 -0
- package/src/routes/api/runs.$runId.evidence.ts +62 -0
- package/src/routes/api/runs.$runId.ts +50 -0
- package/src/routes/api/runs.ts +58 -0
- package/src/routes/api/sessions.ts +29 -2
- package/.output/public/assets/_sessionId-DoqvnFx7.js +0 -1
- package/.output/public/assets/index-B0YcFeL-.js +0 -1
- package/.output/server/_tanstack-start-manifest_v-DQoXxIFQ.mjs +0 -4
package/src/mcp/toolHandlers.ts
CHANGED
|
@@ -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}`);
|