@tonyclaw/agent-inspector 2.1.17 → 2.1.19
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-B9sLBHw5.js → CompareDrawer-CHXN5HYU.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-TZJV7HhE.js +106 -0
- package/.output/public/assets/{ReplayDialog-D9I9W9n3.js → ReplayDialog-DQ7nS5Rr.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-DGXK_Rii.js → RequestAnatomy-DXWweH_2.js} +1 -1
- package/.output/public/assets/{ResponseView-df_JzwsS.js → ResponseView-3PimPfiD.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-i3DM5IKL.js → StreamingChunkSequence-4v52v-7a.js} +1 -1
- package/.output/public/assets/_sessionId-DfkBPvCC.js +1 -0
- package/.output/public/assets/{index-Dqc2r1ea.js → index-BuhFRImh.js} +1 -1
- package/.output/public/assets/index-ChgjUix0.css +1 -0
- package/.output/public/assets/index-Uf87L52J.js +1 -0
- package/.output/public/assets/{json-viewer-D3QWQlSB.js → json-viewer-d2gWBDLO.js} +1 -1
- package/.output/public/assets/{main-ZhxhOCmF.js → main-Do-E-46G.js} +2 -2
- package/.output/server/{_sessionId-YFrP0lZP.mjs → _sessionId-B82aMW2A.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-qC3adEeq.mjs → CompareDrawer-Bud_yIrM.mjs} +2 -2
- package/.output/server/_ssr/{ProxyViewerContainer-DCZIJC2b.mjs → ProxyViewerContainer-DqgVb0So.mjs} +1976 -1988
- package/.output/server/_ssr/{ReplayDialog-CvX4XYCi.mjs → ReplayDialog-DKbV5fnW.mjs} +3 -3
- package/.output/server/_ssr/{RequestAnatomy-HQfrxjO1.mjs → RequestAnatomy-MStxp1Wp.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-QSlSD11e.mjs → ResponseView-HLTCpJcF.mjs} +2 -2
- package/.output/server/_ssr/{StreamingChunkSequence-DTAiwlnI.mjs → StreamingChunkSequence-DEiyiUs_.mjs} +2 -2
- package/.output/server/_ssr/{index-D_EzfXaN.mjs → index-DamOPFDI.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-CrIX0e0Q.mjs → json-viewer-MmTWKxqh.mjs} +2 -2
- package/.output/server/_ssr/{router-__o2wrfO.mjs → router-50BIB6ED.mjs} +545 -22
- package/.output/server/{_tanstack-start-manifest_v-DbqyLpsI.mjs → _tanstack-start-manifest_v-TSwb1vFc.mjs} +1 -1
- package/.output/server/index.mjs +66 -66
- package/package.json +1 -1
- package/src/components/proxy-viewer/ConversationGroupList.tsx +11 -11
- package/src/components/proxy-viewer/ConversationHeader.tsx +7 -7
- package/src/components/proxy-viewer/LogEntry.tsx +15 -26
- package/src/components/proxy-viewer/LogEntryHeader.tsx +11 -14
- package/src/components/proxy-viewer/RequestToolsPanel.tsx +15 -15
- package/src/components/proxy-viewer/ThreadConnector.tsx +75 -91
- package/src/components/proxy-viewer/ToolTraceEvents.tsx +1 -1
- package/src/components/proxy-viewer/TurnGroup.tsx +11 -14
- package/src/mcp/server.ts +275 -0
- package/src/mcp/toolHandlers.ts +380 -3
- package/src/proxy/logSearch.ts +3 -0
- package/src/proxy/store.ts +40 -15
- package/src/routes/api/logs.ts +8 -2
- package/.output/public/assets/ProxyViewerContainer-DbWjc_BE.js +0 -106
- package/.output/public/assets/_sessionId-CTpCHdFh.js +0 -1
- package/.output/public/assets/index-Blqvndcn.js +0 -1
- package/.output/public/assets/index-Cs79WRQj.css +0 -1
|
@@ -65,7 +65,7 @@ import "../_libs/immediate.mjs";
|
|
|
65
65
|
import "../_libs/setimmediate.mjs";
|
|
66
66
|
import "../_libs/pako.mjs";
|
|
67
67
|
const faviconSvg = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20role='img'%20aria-label='Agent%20Inspector'%3e%3cg%20fill='none'%20stroke='%23f59e0b'%20stroke-width='1.5'%20stroke-linecap='round'%20stroke-linejoin='round'%20%3e%3cpath%20d='M5%2013%20C5%209%208%207%2012%207%20C16%207%2019%209%2019%2013%20C19%2016%2016%2018%2012%2018%20C8%2018%205%2016%205%2013%20Z'%20/%3e%3cpath%20d='M5%2011%20C3.5%209.5%201.5%2010%202%2012.5%20C2.5%2014%204%2013.5%205%2012.5'%20/%3e%3cpath%20d='M19%2011%20C20.5%209.5%2022.5%2010%2022%2012.5%20C21.5%2014%2020%2013.5%2019%2012.5'%20/%3e%3cline%20x1='10'%20y1='7'%20x2='9.5'%20y2='5'%20/%3e%3cline%20x1='14'%20y1='7'%20x2='14.5'%20y2='5'%20/%3e%3cline%20x1='6.5'%20y1='16'%20x2='4.5'%20y2='19.5'%20/%3e%3cline%20x1='9'%20y1='17.5'%20x2='8'%20y2='20.5'%20/%3e%3cline%20x1='15'%20y1='17.5'%20x2='16'%20y2='20.5'%20/%3e%3cline%20x1='17.5'%20y1='16'%20x2='19.5'%20y2='19.5'%20/%3e%3c/g%3e%3ccircle%20cx='9.5'%20cy='4.5'%20r='0.9'%20fill='%23f59e0b'%20/%3e%3ccircle%20cx='14.5'%20cy='4.5'%20r='0.9'%20fill='%23f59e0b'%20/%3e%3c/svg%3e";
|
|
68
|
-
const appCss = "/assets/index-
|
|
68
|
+
const appCss = "/assets/index-ChgjUix0.css";
|
|
69
69
|
const Route$D = createRootRoute({
|
|
70
70
|
head: () => ({
|
|
71
71
|
meta: [
|
|
@@ -108,7 +108,7 @@ function RootDocument({ children }) {
|
|
|
108
108
|
] })
|
|
109
109
|
] });
|
|
110
110
|
}
|
|
111
|
-
const $$splitComponentImporter$1 = () => import("./index-
|
|
111
|
+
const $$splitComponentImporter$1 = () => import("./index-DamOPFDI.mjs");
|
|
112
112
|
const Route$C = createFileRoute("/")({
|
|
113
113
|
component: lazyRouteComponent($$splitComponentImporter$1, "component")
|
|
114
114
|
});
|
|
@@ -151,7 +151,7 @@ function decodeSessionIdFromPath(encoded) {
|
|
|
151
151
|
function getSessionPath(sessionId) {
|
|
152
152
|
return `/session/${encodeSessionIdForPath(sessionId)}`;
|
|
153
153
|
}
|
|
154
|
-
const $$splitComponentImporter = () => import("../_sessionId-
|
|
154
|
+
const $$splitComponentImporter = () => import("../_sessionId-B82aMW2A.mjs");
|
|
155
155
|
const Route$B = createFileRoute("/session/$sessionId")({
|
|
156
156
|
component: lazyRouteComponent($$splitComponentImporter, "component"),
|
|
157
157
|
parseParams: (params) => ({
|
|
@@ -2734,12 +2734,14 @@ let sessionArchiveQueue = Promise.resolve();
|
|
|
2734
2734
|
function cursorPageIndexKey(options) {
|
|
2735
2735
|
return JSON.stringify({
|
|
2736
2736
|
sessionId: options.sessionId ?? null,
|
|
2737
|
-
model: options.model ?? null
|
|
2737
|
+
model: options.model ?? null,
|
|
2738
|
+
clientPid: options.clientPid ?? null
|
|
2738
2739
|
});
|
|
2739
2740
|
}
|
|
2740
2741
|
function matchesCursorIndexFilters(log, index) {
|
|
2741
2742
|
if (index.sessionId !== void 0 && getLogSessionId(log) !== index.sessionId) return false;
|
|
2742
2743
|
if (index.model !== void 0 && log.model !== index.model) return false;
|
|
2744
|
+
if (index.clientPid !== void 0 && log.clientPid !== index.clientPid) return false;
|
|
2743
2745
|
return true;
|
|
2744
2746
|
}
|
|
2745
2747
|
function insertSortedLogId(ids, id) {
|
|
@@ -3056,14 +3058,15 @@ async function scanLogFileForId(filePath, id) {
|
|
|
3056
3058
|
}
|
|
3057
3059
|
return lastMatch;
|
|
3058
3060
|
}
|
|
3059
|
-
function getFilteredLogs(sessionId, model) {
|
|
3061
|
+
function getFilteredLogs(sessionId, model, clientPid) {
|
|
3060
3062
|
return [...memoryCache.values()].filter((l) => {
|
|
3061
|
-
return matchesLogFilters(l, sessionId, model);
|
|
3063
|
+
return matchesLogFilters(l, sessionId, model, clientPid);
|
|
3062
3064
|
});
|
|
3063
3065
|
}
|
|
3064
|
-
function matchesLogFilters(log, sessionId, model) {
|
|
3066
|
+
function matchesLogFilters(log, sessionId, model, clientPid) {
|
|
3065
3067
|
if (sessionId !== void 0 && getLogSessionId(log) !== sessionId) return false;
|
|
3066
3068
|
if (model !== void 0 && log.model !== model) return false;
|
|
3069
|
+
if (clientPid !== void 0 && log.clientPid !== clientPid) return false;
|
|
3067
3070
|
return true;
|
|
3068
3071
|
}
|
|
3069
3072
|
async function visitPersistedLogs(visitor) {
|
|
@@ -3113,7 +3116,7 @@ async function visitArchivedSessionLogs(sessionId, visitor) {
|
|
|
3113
3116
|
}
|
|
3114
3117
|
}
|
|
3115
3118
|
function canUseIndexedListPage(options) {
|
|
3116
|
-
return options.sessionId === void 0 && options.model === void 0;
|
|
3119
|
+
return options.sessionId === void 0 && options.model === void 0 && options.clientPid === void 0;
|
|
3117
3120
|
}
|
|
3118
3121
|
function hasUsableIndexOffset(entry) {
|
|
3119
3122
|
return entry.byteOffset >= 0 && entry.byteLength > 0;
|
|
@@ -3158,7 +3161,7 @@ async function listLogsPage(options) {
|
|
|
3158
3161
|
const includeBodies = options.includeBodies !== false;
|
|
3159
3162
|
let total = 0;
|
|
3160
3163
|
const visitLog = (log) => {
|
|
3161
|
-
if (!matchesLogFilters(log, options.sessionId, options.model)) return;
|
|
3164
|
+
if (!matchesLogFilters(log, options.sessionId, options.model, options.clientPid)) return;
|
|
3162
3165
|
if (!seenIds.has(log.id)) {
|
|
3163
3166
|
seenIds.add(log.id);
|
|
3164
3167
|
const position = total;
|
|
@@ -3223,12 +3226,14 @@ function hasLogIndexFilterMetadata(entry) {
|
|
|
3223
3226
|
function hasLogIndexSummary(entry) {
|
|
3224
3227
|
return entry.summary !== void 0;
|
|
3225
3228
|
}
|
|
3226
|
-
function matchesLogIndexEntryFilters(entry, sessionId, model) {
|
|
3229
|
+
function matchesLogIndexEntryFilters(entry, sessionId, model, clientPid) {
|
|
3227
3230
|
if (sessionId !== void 0 && entry.sessionId !== sessionId) return false;
|
|
3228
3231
|
if (model !== void 0 && entry.model !== model) return false;
|
|
3232
|
+
if (clientPid !== void 0 && entry.summary?.clientPid !== clientPid) return false;
|
|
3229
3233
|
return true;
|
|
3230
3234
|
}
|
|
3231
3235
|
async function buildLogCursorPageIndexFromLogIndex(options, allowRebuild) {
|
|
3236
|
+
if (options.clientPid !== void 0) return null;
|
|
3232
3237
|
let entries = await listFilteredIndexEntries(options);
|
|
3233
3238
|
if (entries.length === 0 && allowRebuild) {
|
|
3234
3239
|
await rebuildIndex();
|
|
@@ -3240,7 +3245,7 @@ async function buildLogCursorPageIndexFromLogIndex(options, allowRebuild) {
|
|
|
3240
3245
|
return await buildLogCursorPageIndexFromLogIndex(options, false);
|
|
3241
3246
|
}
|
|
3242
3247
|
const matchingEntries = entries.filter(
|
|
3243
|
-
(entry) => matchesLogIndexEntryFilters(entry, options.sessionId, options.model)
|
|
3248
|
+
(entry) => matchesLogIndexEntryFilters(entry, options.sessionId, options.model, options.clientPid)
|
|
3244
3249
|
);
|
|
3245
3250
|
if (matchingEntries.some((entry) => !hasLogIndexSummary(entry))) {
|
|
3246
3251
|
if (!allowRebuild) return null;
|
|
@@ -3260,7 +3265,7 @@ async function buildLogCursorPageIndexFromLogIndex(options, allowRebuild) {
|
|
|
3260
3265
|
}
|
|
3261
3266
|
const logsById = /* @__PURE__ */ new Map();
|
|
3262
3267
|
for (const log of memoryCache.values()) {
|
|
3263
|
-
if (!matchesLogFilters(log, options.sessionId, options.model)) continue;
|
|
3268
|
+
if (!matchesLogFilters(log, options.sessionId, options.model, options.clientPid)) continue;
|
|
3264
3269
|
idsById.add(log.id);
|
|
3265
3270
|
logsById.set(log.id, log);
|
|
3266
3271
|
}
|
|
@@ -3269,7 +3274,7 @@ async function buildLogCursorPageIndexFromLogIndex(options, allowRebuild) {
|
|
|
3269
3274
|
const archivedLogs = await listArchivedSessionLogs(options.sessionId);
|
|
3270
3275
|
for (const archivedLog of archivedLogs) {
|
|
3271
3276
|
const log = normalizeLog(archivedLog);
|
|
3272
|
-
if (!matchesLogFilters(log, options.sessionId, options.model)) continue;
|
|
3277
|
+
if (!matchesLogFilters(log, options.sessionId, options.model, options.clientPid)) continue;
|
|
3273
3278
|
observeSessionLog(log);
|
|
3274
3279
|
idsById.add(log.id);
|
|
3275
3280
|
logsById.set(log.id, log);
|
|
@@ -3279,6 +3284,7 @@ async function buildLogCursorPageIndexFromLogIndex(options, allowRebuild) {
|
|
|
3279
3284
|
key: cursorPageIndexKey(options),
|
|
3280
3285
|
sessionId: options.sessionId,
|
|
3281
3286
|
model: options.model,
|
|
3287
|
+
clientPid: options.clientPid,
|
|
3282
3288
|
ids: [...idsById].sort((left, right) => left - right),
|
|
3283
3289
|
logsById,
|
|
3284
3290
|
entriesById
|
|
@@ -3290,7 +3296,7 @@ async function buildLogCursorPageIndex(options) {
|
|
|
3290
3296
|
const key = cursorPageIndexKey(options);
|
|
3291
3297
|
const logsById = /* @__PURE__ */ new Map();
|
|
3292
3298
|
const visitLog = (log) => {
|
|
3293
|
-
if (!matchesLogFilters(log, options.sessionId, options.model)) return;
|
|
3299
|
+
if (!matchesLogFilters(log, options.sessionId, options.model, options.clientPid)) return;
|
|
3294
3300
|
logsById.set(log.id, log);
|
|
3295
3301
|
};
|
|
3296
3302
|
await visitPersistedLogs((log) => {
|
|
@@ -3305,6 +3311,7 @@ async function buildLogCursorPageIndex(options) {
|
|
|
3305
3311
|
key,
|
|
3306
3312
|
sessionId: options.sessionId,
|
|
3307
3313
|
model: options.model,
|
|
3314
|
+
clientPid: options.clientPid,
|
|
3308
3315
|
ids: [...logsById.keys()].sort((left, right) => left - right),
|
|
3309
3316
|
logsById,
|
|
3310
3317
|
entriesById: /* @__PURE__ */ new Map()
|
|
@@ -3415,7 +3422,7 @@ async function collectIndexedSessionLogSummaries(sessionId) {
|
|
|
3415
3422
|
if (entries.length === 0) return [];
|
|
3416
3423
|
const needsMetadataRebuild = entries.some((entry) => !hasLogIndexFilterMetadata(entry));
|
|
3417
3424
|
const needsSummaryRebuild = entries.some((entry) => {
|
|
3418
|
-
if (!matchesLogIndexEntryFilters(entry, sessionId, void 0)) return false;
|
|
3425
|
+
if (!matchesLogIndexEntryFilters(entry, sessionId, void 0, void 0)) return false;
|
|
3419
3426
|
return !hasLogIndexSummary(entry);
|
|
3420
3427
|
});
|
|
3421
3428
|
if (needsMetadataRebuild || needsSummaryRebuild) {
|
|
@@ -3424,7 +3431,7 @@ async function collectIndexedSessionLogSummaries(sessionId) {
|
|
|
3424
3431
|
}
|
|
3425
3432
|
const summaries = [];
|
|
3426
3433
|
for (const entry of entries) {
|
|
3427
|
-
if (!matchesLogIndexEntryFilters(entry, sessionId, void 0)) continue;
|
|
3434
|
+
if (!matchesLogIndexEntryFilters(entry, sessionId, void 0, void 0)) continue;
|
|
3428
3435
|
if (entry.summary === void 0) continue;
|
|
3429
3436
|
summaries.push(buildSessionLogSummary(compactLogFromIndexSummary(entry.summary)));
|
|
3430
3437
|
}
|
|
@@ -9087,7 +9094,7 @@ const Route$w = createFileRoute("/api/models")({
|
|
|
9087
9094
|
}
|
|
9088
9095
|
}
|
|
9089
9096
|
});
|
|
9090
|
-
const version = "2.1.
|
|
9097
|
+
const version = "2.1.19";
|
|
9091
9098
|
const packageJson = {
|
|
9092
9099
|
version
|
|
9093
9100
|
};
|
|
@@ -9721,11 +9728,18 @@ const SearchLogsResponseSchema = object({
|
|
|
9721
9728
|
const PAGINATION_DEFAULTS = { offset: 0, limit: 3 };
|
|
9722
9729
|
const LIMIT_HARD_CAP = 5;
|
|
9723
9730
|
const LOG_DETAIL_TEXT_LIMIT = 2e5;
|
|
9731
|
+
const PROCESS_LOG_LIMIT_DEFAULT = 50;
|
|
9732
|
+
const PROCESS_LOG_LIMIT_MAX = 200;
|
|
9724
9733
|
function clampLimit(input) {
|
|
9725
9734
|
if (input === void 0) return PAGINATION_DEFAULTS.limit;
|
|
9726
9735
|
if (!Number.isFinite(input) || input < 1) return 1;
|
|
9727
9736
|
return Math.min(Math.floor(input), LIMIT_HARD_CAP);
|
|
9728
9737
|
}
|
|
9738
|
+
function clampProcessLogLimit(input) {
|
|
9739
|
+
if (input === void 0) return PROCESS_LOG_LIMIT_DEFAULT;
|
|
9740
|
+
if (!Number.isFinite(input) || input < 1) return 1;
|
|
9741
|
+
return Math.min(Math.floor(input), PROCESS_LOG_LIMIT_MAX);
|
|
9742
|
+
}
|
|
9729
9743
|
function textJson(data) {
|
|
9730
9744
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
9731
9745
|
}
|
|
@@ -9771,8 +9785,11 @@ function buildBoundedLogDetail(log) {
|
|
|
9771
9785
|
responseTextTruncated: boundedResponse.truncated
|
|
9772
9786
|
};
|
|
9773
9787
|
}
|
|
9788
|
+
function logHasError(log) {
|
|
9789
|
+
return log.error !== null && log.error !== void 0 && log.error.length > 0 || log.responseStatus !== null && log.responseStatus !== void 0 && log.responseStatus >= 400;
|
|
9790
|
+
}
|
|
9774
9791
|
function buildLogSummary(log) {
|
|
9775
|
-
const hasError = log
|
|
9792
|
+
const hasError = logHasError(log);
|
|
9776
9793
|
const rawRequestBodyBytes = log.rawRequestBodyBytes ?? textByteLength(log.rawRequestBody);
|
|
9777
9794
|
const responseTextBytes = log.responseTextBytes ?? textByteLength(log.responseText);
|
|
9778
9795
|
return {
|
|
@@ -9809,6 +9826,74 @@ function buildLogSummary(log) {
|
|
|
9809
9826
|
responseTextBytes
|
|
9810
9827
|
};
|
|
9811
9828
|
}
|
|
9829
|
+
function sortedUniqueStrings(values) {
|
|
9830
|
+
return [
|
|
9831
|
+
...new Set(
|
|
9832
|
+
values.filter(
|
|
9833
|
+
(value) => value !== null && value !== void 0 && value !== ""
|
|
9834
|
+
)
|
|
9835
|
+
)
|
|
9836
|
+
].sort((left, right) => left.localeCompare(right));
|
|
9837
|
+
}
|
|
9838
|
+
function sortedUniqueNumbers(values) {
|
|
9839
|
+
return [
|
|
9840
|
+
...new Set(
|
|
9841
|
+
values.filter(
|
|
9842
|
+
(value) => value !== null && value !== void 0 && Number.isFinite(value)
|
|
9843
|
+
)
|
|
9844
|
+
)
|
|
9845
|
+
].sort((left, right) => left - right);
|
|
9846
|
+
}
|
|
9847
|
+
function inferAgentFromUserAgent(userAgent) {
|
|
9848
|
+
if (userAgent === null || userAgent === void 0) return null;
|
|
9849
|
+
const lower2 = userAgent.toLowerCase();
|
|
9850
|
+
if (lower2.includes("codex")) return "Codex";
|
|
9851
|
+
if (lower2.includes("opencode")) return "OpenCode";
|
|
9852
|
+
if (lower2.includes("claude")) return "Claude Code";
|
|
9853
|
+
if (lower2.includes("mimocode")) return "MiMoCode";
|
|
9854
|
+
if (lower2.includes("ai-sdk")) return "AI SDK";
|
|
9855
|
+
return null;
|
|
9856
|
+
}
|
|
9857
|
+
function processSummary(clientPid, logs, total, limit) {
|
|
9858
|
+
const sortedLogs = [...logs].sort((left, right) => left.id - right.id);
|
|
9859
|
+
const first = sortedLogs[0];
|
|
9860
|
+
const last = sortedLogs[sortedLogs.length - 1];
|
|
9861
|
+
const userAgents = sortedUniqueStrings(sortedLogs.map((log) => log.userAgent));
|
|
9862
|
+
const agents = sortedUniqueStrings(userAgents.map(inferAgentFromUserAgent));
|
|
9863
|
+
const sessions2 = sortedUniqueStrings(sortedLogs.map((log) => log.sessionId));
|
|
9864
|
+
return {
|
|
9865
|
+
clientPid,
|
|
9866
|
+
total,
|
|
9867
|
+
limit,
|
|
9868
|
+
logCount: sortedLogs.length,
|
|
9869
|
+
sessionCount: sessions2.length,
|
|
9870
|
+
sessions: sessions2,
|
|
9871
|
+
agents,
|
|
9872
|
+
userAgents,
|
|
9873
|
+
clientPorts: sortedUniqueNumbers(sortedLogs.map((log) => log.clientPort)),
|
|
9874
|
+
clientCwds: sortedUniqueStrings(sortedLogs.map((log) => log.clientCwd)),
|
|
9875
|
+
clientProjectFolders: sortedUniqueStrings(sortedLogs.map((log) => log.clientProjectFolder)),
|
|
9876
|
+
models: sortedUniqueStrings(sortedLogs.map((log) => log.model)),
|
|
9877
|
+
providers: sortedUniqueStrings(sortedLogs.map((log) => log.providerName)),
|
|
9878
|
+
errorCount: sortedLogs.filter(logHasError).length,
|
|
9879
|
+
startedAt: first?.timestamp ?? null,
|
|
9880
|
+
updatedAt: last?.timestamp ?? null,
|
|
9881
|
+
latestLogs: sortedLogs.slice(-Math.min(10, sortedLogs.length)).reverse().map(buildLogSummary)
|
|
9882
|
+
};
|
|
9883
|
+
}
|
|
9884
|
+
async function readCompactLogsPage(callApi2, params) {
|
|
9885
|
+
const path2 = `/api/logs?${params.toString()}`;
|
|
9886
|
+
const res = await callApi2(path2);
|
|
9887
|
+
if (!res.ok) return toolError(`GET ${path2} returned ${res.status}`);
|
|
9888
|
+
const rawBody = await res.json();
|
|
9889
|
+
const parsed = LogsListResponseSchema.safeParse(rawBody);
|
|
9890
|
+
if (!parsed.success) return toolError("GET /api/logs returned an unparseable logs shape");
|
|
9891
|
+
const logs = parsed.data.logs ?? [];
|
|
9892
|
+
return { logs, total: parsed.data.total ?? logs.length };
|
|
9893
|
+
}
|
|
9894
|
+
function isToolResult(value) {
|
|
9895
|
+
return typeof value === "object" && value !== null && Object.getOwnPropertyDescriptor(value, "content") !== void 0;
|
|
9896
|
+
}
|
|
9812
9897
|
async function listLogsImpl(callApi2, args) {
|
|
9813
9898
|
const offset = args.offset ?? PAGINATION_DEFAULTS.offset;
|
|
9814
9899
|
const limit = clampLimit(args.limit);
|
|
@@ -9823,6 +9908,9 @@ async function listLogsImpl(callApi2, args) {
|
|
|
9823
9908
|
if (args.model !== void 0 && args.model.length > 0) {
|
|
9824
9909
|
params.set("model", args.model);
|
|
9825
9910
|
}
|
|
9911
|
+
if (args.clientPid !== void 0) {
|
|
9912
|
+
params.set("clientPid", String(args.clientPid));
|
|
9913
|
+
}
|
|
9826
9914
|
const res = await callApi2(`/api/logs?${params.toString()}`);
|
|
9827
9915
|
if (!res.ok) return toolError(`GET /api/logs returned ${res.status}`);
|
|
9828
9916
|
const rawBody = await res.json();
|
|
@@ -9848,6 +9936,9 @@ async function searchLogsImpl(callApi2, args) {
|
|
|
9848
9936
|
if (args.model !== void 0 && args.model.length > 0) {
|
|
9849
9937
|
params.set("model", args.model);
|
|
9850
9938
|
}
|
|
9939
|
+
if (args.clientPid !== void 0) {
|
|
9940
|
+
params.set("clientPid", String(args.clientPid));
|
|
9941
|
+
}
|
|
9851
9942
|
const res = await callApi2(`/api/logs?${params.toString()}`);
|
|
9852
9943
|
if (!res.ok) return toolError(`GET /api/logs search returned ${res.status}`);
|
|
9853
9944
|
const rawBody = await res.json();
|
|
@@ -10023,6 +10114,225 @@ async function getSessionTimelineImpl(callApi2, args) {
|
|
|
10023
10114
|
buildSessionTimeline(args.sessionId, logs, parsed.data.total ?? logs.length, limit)
|
|
10024
10115
|
);
|
|
10025
10116
|
}
|
|
10117
|
+
async function listProcessesImpl(callApi2, args = {}) {
|
|
10118
|
+
const scanLimit = clampProcessLogLimit(args.scanLimit ?? args.limit);
|
|
10119
|
+
const params = new URLSearchParams({
|
|
10120
|
+
q: "",
|
|
10121
|
+
offset: "0",
|
|
10122
|
+
limit: String(Math.min(scanLimit, 50)),
|
|
10123
|
+
scanLimit: String(scanLimit)
|
|
10124
|
+
});
|
|
10125
|
+
const res = await callApi2(`/api/logs?${params.toString()}`);
|
|
10126
|
+
if (!res.ok) return toolError(`GET /api/logs process scan returned ${res.status}`);
|
|
10127
|
+
const rawBody = await res.json();
|
|
10128
|
+
const parsed = SearchLogsResponseSchema.safeParse(rawBody);
|
|
10129
|
+
if (!parsed.success) return toolError("GET /api/logs returned an unparseable process scan shape");
|
|
10130
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
10131
|
+
for (const log of parsed.data.logs) {
|
|
10132
|
+
if (log.clientPid === null || log.clientPid === void 0) continue;
|
|
10133
|
+
const existing = grouped.get(log.clientPid) ?? [];
|
|
10134
|
+
existing.push(log);
|
|
10135
|
+
grouped.set(log.clientPid, existing);
|
|
10136
|
+
}
|
|
10137
|
+
const processes = [...grouped.entries()].map(([clientPid, logs]) => processSummary(clientPid, logs, logs.length, logs.length)).sort((left, right) => {
|
|
10138
|
+
const leftUpdated = typeof left["updatedAt"] === "string" ? left["updatedAt"] : "";
|
|
10139
|
+
const rightUpdated = typeof right["updatedAt"] === "string" ? right["updatedAt"] : "";
|
|
10140
|
+
return rightUpdated.localeCompare(leftUpdated);
|
|
10141
|
+
});
|
|
10142
|
+
return textJson({
|
|
10143
|
+
scanned: parsed.data.scanned ?? parsed.data.logs.length,
|
|
10144
|
+
processCount: processes.length,
|
|
10145
|
+
processes
|
|
10146
|
+
});
|
|
10147
|
+
}
|
|
10148
|
+
async function getProcessImpl(callApi2, args) {
|
|
10149
|
+
const limit = clampProcessLogLimit(args.limit);
|
|
10150
|
+
const offset = args.offset ?? 0;
|
|
10151
|
+
const params = new URLSearchParams({
|
|
10152
|
+
clientPid: String(args.clientPid),
|
|
10153
|
+
offset: String(offset),
|
|
10154
|
+
limit: String(limit),
|
|
10155
|
+
compact: "1"
|
|
10156
|
+
});
|
|
10157
|
+
const result = await readCompactLogsPage(callApi2, params);
|
|
10158
|
+
if (isToolResult(result)) return result;
|
|
10159
|
+
return textJson(processSummary(args.clientPid, result.logs, result.total, limit));
|
|
10160
|
+
}
|
|
10161
|
+
async function getProcessTimelineImpl(callApi2, args) {
|
|
10162
|
+
const limit = clampTimelineLimit(args.limit);
|
|
10163
|
+
const offset = args.offset ?? 0;
|
|
10164
|
+
const params = new URLSearchParams({
|
|
10165
|
+
clientPid: String(args.clientPid),
|
|
10166
|
+
offset: String(offset),
|
|
10167
|
+
limit: String(limit),
|
|
10168
|
+
compact: "1"
|
|
10169
|
+
});
|
|
10170
|
+
const result = await readCompactLogsPage(callApi2, params);
|
|
10171
|
+
if (isToolResult(result)) return result;
|
|
10172
|
+
const sortedLogs = [...result.logs].sort((left, right) => left.id - right.id);
|
|
10173
|
+
return textJson({
|
|
10174
|
+
clientPid: args.clientPid,
|
|
10175
|
+
logCoverage: "bounded",
|
|
10176
|
+
total: result.total,
|
|
10177
|
+
limit,
|
|
10178
|
+
eventCount: sortedLogs.length,
|
|
10179
|
+
requestCount: sortedLogs.length,
|
|
10180
|
+
errorCount: sortedLogs.filter(logHasError).length,
|
|
10181
|
+
startedAt: sortedLogs[0]?.timestamp ?? null,
|
|
10182
|
+
updatedAt: sortedLogs.at(-1)?.timestamp ?? null,
|
|
10183
|
+
sessions: sortedUniqueStrings(sortedLogs.map((log) => log.sessionId)),
|
|
10184
|
+
models: sortedUniqueStrings(sortedLogs.map((log) => log.model)),
|
|
10185
|
+
providers: sortedUniqueStrings(sortedLogs.map((log) => log.providerName)),
|
|
10186
|
+
events: sortedLogs.map((log, index) => ({
|
|
10187
|
+
sequence: index + 1,
|
|
10188
|
+
kind: "request",
|
|
10189
|
+
logId: log.id,
|
|
10190
|
+
...buildLogSummary(log)
|
|
10191
|
+
}))
|
|
10192
|
+
});
|
|
10193
|
+
}
|
|
10194
|
+
async function getLogHeadersImpl(callApi2, id) {
|
|
10195
|
+
const res = await callApi2(`/api/logs/${id}`);
|
|
10196
|
+
if (!res.ok) return toolError(`GET /api/logs/${id} returned ${res.status}`);
|
|
10197
|
+
const rawBody = await res.json();
|
|
10198
|
+
const parsed = CapturedLogSchema.safeParse(rawBody);
|
|
10199
|
+
if (!parsed.success) {
|
|
10200
|
+
return toolError(`GET /api/logs/${id} returned an unparseable CapturedLog`);
|
|
10201
|
+
}
|
|
10202
|
+
const log = parsed.data;
|
|
10203
|
+
return textJson({
|
|
10204
|
+
id: log.id,
|
|
10205
|
+
sessionId: log.sessionId,
|
|
10206
|
+
userAgent: log.userAgent,
|
|
10207
|
+
origin: log.origin,
|
|
10208
|
+
clientPid: log.clientPid ?? null,
|
|
10209
|
+
clientPort: log.clientPort ?? null,
|
|
10210
|
+
clientCwd: log.clientCwd ?? null,
|
|
10211
|
+
clientProjectFolder: log.clientProjectFolder ?? null,
|
|
10212
|
+
apiFormat: log.apiFormat,
|
|
10213
|
+
method: log.method,
|
|
10214
|
+
path: log.path,
|
|
10215
|
+
model: log.model,
|
|
10216
|
+
rawHeaders: log.rawHeaders ?? null,
|
|
10217
|
+
headers: log.headers ?? null,
|
|
10218
|
+
hasRawHeaders: log.rawHeaders !== void 0 && Object.keys(log.rawHeaders).length > 0,
|
|
10219
|
+
hasHeaders: log.headers !== void 0 && Object.keys(log.headers).length > 0,
|
|
10220
|
+
bodyContentMode: log.bodyContentMode ?? null
|
|
10221
|
+
});
|
|
10222
|
+
}
|
|
10223
|
+
async function getRuntimeConfigImpl(callApi2) {
|
|
10224
|
+
const configRes = await callApi2("/api/config");
|
|
10225
|
+
if (!configRes.ok) return toolError(`GET /api/config returned ${configRes.status}`);
|
|
10226
|
+
const statsRes = await callApi2("/api/logs?stats=1");
|
|
10227
|
+
if (!statsRes.ok) return toolError(`GET /api/logs?stats=1 returned ${statsRes.status}`);
|
|
10228
|
+
const config = await configRes.json();
|
|
10229
|
+
const storage = await statsRes.json();
|
|
10230
|
+
return textJson({
|
|
10231
|
+
config,
|
|
10232
|
+
storage
|
|
10233
|
+
});
|
|
10234
|
+
}
|
|
10235
|
+
async function diagnoseCaptureImpl(callApi2, args = {}) {
|
|
10236
|
+
const runtime = await getRuntimeConfigImpl(callApi2);
|
|
10237
|
+
if (runtime.isError === true) return runtime;
|
|
10238
|
+
const runtimeText = runtime.content[0]?.text ?? "{}";
|
|
10239
|
+
const runtimePayload = JSON.parse(runtimeText);
|
|
10240
|
+
const runtimeRecord = isRecord(runtimePayload) ? runtimePayload : {};
|
|
10241
|
+
const configRecord = isRecord(runtimeRecord["config"]) ? runtimeRecord["config"] : {};
|
|
10242
|
+
const captureMode = stringField(configRecord, "captureMode");
|
|
10243
|
+
let log = null;
|
|
10244
|
+
if (args.id !== void 0) {
|
|
10245
|
+
const logRes = await callApi2(`/api/logs/${args.id}`);
|
|
10246
|
+
if (!logRes.ok) return toolError(`GET /api/logs/${args.id} returned ${logRes.status}`);
|
|
10247
|
+
const rawLog = await logRes.json();
|
|
10248
|
+
const parsed = CapturedLogSchema.safeParse(rawLog);
|
|
10249
|
+
if (!parsed.success) return toolError(`GET /api/logs/${args.id} returned an unparseable log`);
|
|
10250
|
+
log = parsed.data;
|
|
10251
|
+
}
|
|
10252
|
+
const findings = [];
|
|
10253
|
+
const recommendations = [];
|
|
10254
|
+
if (captureMode !== "full") {
|
|
10255
|
+
findings.push(
|
|
10256
|
+
"Runtime captureMode is not full, so request/response bodies and headers may be compacted."
|
|
10257
|
+
);
|
|
10258
|
+
recommendations.push(
|
|
10259
|
+
"Restart or launch Agent Inspector with full capture mode when header/body evidence is required."
|
|
10260
|
+
);
|
|
10261
|
+
}
|
|
10262
|
+
if (log !== null) {
|
|
10263
|
+
if (log.rawHeaders === void 0 || Object.keys(log.rawHeaders).length === 0) {
|
|
10264
|
+
findings.push(`Log ${String(log.id)} has no rawHeaders stored.`);
|
|
10265
|
+
}
|
|
10266
|
+
if (log.headers === void 0 || Object.keys(log.headers).length === 0) {
|
|
10267
|
+
findings.push(`Log ${String(log.id)} has no upstream headers stored.`);
|
|
10268
|
+
}
|
|
10269
|
+
if (log.rawRequestBody === null) {
|
|
10270
|
+
findings.push(`Log ${String(log.id)} does not include rawRequestBody in this response.`);
|
|
10271
|
+
}
|
|
10272
|
+
if (log.bodyContentMode === "compact") {
|
|
10273
|
+
findings.push(`Log ${String(log.id)} is in compact body mode.`);
|
|
10274
|
+
recommendations.push(
|
|
10275
|
+
"Use inspector_get_log_headers for header-only checks and enable full capture for future body inspection."
|
|
10276
|
+
);
|
|
10277
|
+
}
|
|
10278
|
+
}
|
|
10279
|
+
return textJson({
|
|
10280
|
+
captureMode,
|
|
10281
|
+
logId: log?.id ?? null,
|
|
10282
|
+
hasLog: log !== null,
|
|
10283
|
+
findings,
|
|
10284
|
+
recommendations,
|
|
10285
|
+
runtime: runtimeRecord
|
|
10286
|
+
});
|
|
10287
|
+
}
|
|
10288
|
+
async function addGroupProcessImpl(callApi2, args) {
|
|
10289
|
+
const params = new URLSearchParams({
|
|
10290
|
+
clientPid: String(args.clientPid),
|
|
10291
|
+
offset: "0",
|
|
10292
|
+
limit: String(PROCESS_LOG_LIMIT_MAX),
|
|
10293
|
+
compact: "1"
|
|
10294
|
+
});
|
|
10295
|
+
const result = await readCompactLogsPage(callApi2, params);
|
|
10296
|
+
if (isToolResult(result)) return result;
|
|
10297
|
+
const bySession = /* @__PURE__ */ new Map();
|
|
10298
|
+
for (const log of result.logs) {
|
|
10299
|
+
if (log.sessionId === null || log.sessionId === "") continue;
|
|
10300
|
+
const existing = bySession.get(log.sessionId) ?? [];
|
|
10301
|
+
existing.push(log);
|
|
10302
|
+
bySession.set(log.sessionId, existing);
|
|
10303
|
+
}
|
|
10304
|
+
if (bySession.size === 0) {
|
|
10305
|
+
return toolError(`No sessions found for clientPid ${String(args.clientPid)}`);
|
|
10306
|
+
}
|
|
10307
|
+
let groupResult = null;
|
|
10308
|
+
const attached = [];
|
|
10309
|
+
for (const [sessionId, logs] of bySession.entries()) {
|
|
10310
|
+
const first = logs[0];
|
|
10311
|
+
const last = logs.at(-1);
|
|
10312
|
+
const userAgent = last?.userAgent ?? first?.userAgent ?? null;
|
|
10313
|
+
const agent = inferAgentFromUserAgent(userAgent);
|
|
10314
|
+
groupResult = addInspectorGroupSession(args.groupId, {
|
|
10315
|
+
sessionId,
|
|
10316
|
+
label: `${args.labelPrefix ?? `PID ${String(args.clientPid)}`} - ${sessionId}`,
|
|
10317
|
+
agent,
|
|
10318
|
+
provider: last?.providerName ?? first?.providerName ?? null,
|
|
10319
|
+
model: last?.model ?? first?.model ?? null,
|
|
10320
|
+
metadata: {
|
|
10321
|
+
clientPid: args.clientPid,
|
|
10322
|
+
processLogCount: logs.length,
|
|
10323
|
+
userAgent: userAgent ?? ""
|
|
10324
|
+
}
|
|
10325
|
+
});
|
|
10326
|
+
if (!groupResult.ok) return toolError(groupResult.message);
|
|
10327
|
+
attached.push({ sessionId, logCount: logs.length, agent, userAgent });
|
|
10328
|
+
}
|
|
10329
|
+
if (groupResult === null || !groupResult.ok) return toolError("Group process attach failed");
|
|
10330
|
+
return textJson({
|
|
10331
|
+
group: groupResult.value,
|
|
10332
|
+
clientPid: args.clientPid,
|
|
10333
|
+
attached
|
|
10334
|
+
});
|
|
10335
|
+
}
|
|
10026
10336
|
function fileBytes(path2) {
|
|
10027
10337
|
if (!existsSync(path2)) return null;
|
|
10028
10338
|
try {
|
|
@@ -10469,6 +10779,9 @@ const UpdateProviderInputSchema = object({
|
|
|
10469
10779
|
const LogResourceListResponseSchema = object({
|
|
10470
10780
|
logs: array(object({ id: number().int().positive() }))
|
|
10471
10781
|
});
|
|
10782
|
+
const ProcessResourceListResponseSchema = object({
|
|
10783
|
+
logs: array(object({ clientPid: number().int().positive().nullable().optional() }))
|
|
10784
|
+
});
|
|
10472
10785
|
function textResource(uri, mimeType, text) {
|
|
10473
10786
|
return {
|
|
10474
10787
|
contents: [{ uri: uri.href, mimeType, text }]
|
|
@@ -10509,6 +10822,7 @@ const WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
10509
10822
|
"inspector_update_group",
|
|
10510
10823
|
"inspector_delete_group",
|
|
10511
10824
|
"inspector_add_group_session",
|
|
10825
|
+
"inspector_add_group_process",
|
|
10512
10826
|
"inspector_export_group_evidence",
|
|
10513
10827
|
"inspector_export_group_evidence_bundle",
|
|
10514
10828
|
"inspector_set_current_context",
|
|
@@ -10687,6 +11001,40 @@ async function listLogResources() {
|
|
|
10687
11001
|
return listResult([]);
|
|
10688
11002
|
}
|
|
10689
11003
|
}
|
|
11004
|
+
async function listProcessResources() {
|
|
11005
|
+
try {
|
|
11006
|
+
const response = await callApi("/api/logs?q=&offset=0&limit=50&scanLimit=200");
|
|
11007
|
+
if (!response.ok) return listResult([]);
|
|
11008
|
+
const parsed = ProcessResourceListResponseSchema.safeParse(await response.json());
|
|
11009
|
+
if (!parsed.success) return listResult([]);
|
|
11010
|
+
const pids = [
|
|
11011
|
+
...new Set(
|
|
11012
|
+
parsed.data.logs.map((log) => log.clientPid ?? null).filter((pid) => pid !== null)
|
|
11013
|
+
)
|
|
11014
|
+
].sort((left, right) => left - right);
|
|
11015
|
+
return listResult(
|
|
11016
|
+
pids.map((pid) => ({
|
|
11017
|
+
uri: `inspector://processes/${String(pid)}`,
|
|
11018
|
+
name: `process:${String(pid)}`,
|
|
11019
|
+
title: `Inspector process ${String(pid)}`,
|
|
11020
|
+
mimeType: "application/json"
|
|
11021
|
+
}))
|
|
11022
|
+
);
|
|
11023
|
+
} catch {
|
|
11024
|
+
return listResult([]);
|
|
11025
|
+
}
|
|
11026
|
+
}
|
|
11027
|
+
async function listProcessTimelineResources() {
|
|
11028
|
+
const processResources = await listProcessResources();
|
|
11029
|
+
return listResult(
|
|
11030
|
+
processResources.resources.map((resource) => ({
|
|
11031
|
+
uri: `${resource.uri}/timeline`,
|
|
11032
|
+
name: `${resource.name}:timeline`,
|
|
11033
|
+
title: `${resource.title ?? resource.name} timeline`,
|
|
11034
|
+
mimeType: "application/json"
|
|
11035
|
+
}))
|
|
11036
|
+
);
|
|
11037
|
+
}
|
|
10690
11038
|
function listRunResources() {
|
|
10691
11039
|
return listResult(
|
|
10692
11040
|
listInspectorRuns().runs.map((run) => ({
|
|
@@ -10784,7 +11132,8 @@ function registerTools(server) {
|
|
|
10784
11132
|
offset: number().int().nonnegative().optional().describe("Skip this many entries."),
|
|
10785
11133
|
limit: number().int().positive().optional().describe("How many summaries to return. Hard-capped at 5."),
|
|
10786
11134
|
sessionId: string().optional().describe("Filter by session/affinity id."),
|
|
10787
|
-
model: string().optional().describe("Filter by model name.")
|
|
11135
|
+
model: string().optional().describe("Filter by model name."),
|
|
11136
|
+
clientPid: number().int().positive().optional().describe("Filter by originating client process PID.")
|
|
10788
11137
|
})
|
|
10789
11138
|
},
|
|
10790
11139
|
(args) => safeCall(() => listLogsImpl(callApi, args))
|
|
@@ -10800,7 +11149,8 @@ function registerTools(server) {
|
|
|
10800
11149
|
limit: number().int().positive().optional().describe("How many summaries to return. Hard-capped at 5 by the MCP handler."),
|
|
10801
11150
|
scanLimit: number().int().positive().max(1e3).optional().describe("How many recent compact logs to scan before filtering. Default 200."),
|
|
10802
11151
|
sessionId: string().optional().describe("Optional session/affinity id filter."),
|
|
10803
|
-
model: string().optional().describe("Optional model name filter.")
|
|
11152
|
+
model: string().optional().describe("Optional model name filter."),
|
|
11153
|
+
clientPid: number().int().positive().optional().describe("Optional originating client process PID filter.")
|
|
10804
11154
|
})
|
|
10805
11155
|
},
|
|
10806
11156
|
(args) => safeCall(() => searchLogsImpl(callApi, args))
|
|
@@ -10827,6 +11177,55 @@ function registerTools(server) {
|
|
|
10827
11177
|
},
|
|
10828
11178
|
({ id }) => safeCall(() => getLogChunksImpl(callApi, id))
|
|
10829
11179
|
);
|
|
11180
|
+
server.registerTool(
|
|
11181
|
+
"inspector_get_log_headers",
|
|
11182
|
+
{
|
|
11183
|
+
title: "Get captured log headers",
|
|
11184
|
+
description: "Returns header-focused evidence for one log without large request/response bodies: raw headers, upstream headers, user-agent, origin, client PID/port, session id, model, and API path.",
|
|
11185
|
+
inputSchema: object({
|
|
11186
|
+
id: number().int().positive().describe("The log id.")
|
|
11187
|
+
})
|
|
11188
|
+
},
|
|
11189
|
+
({ id }) => safeCall(() => getLogHeadersImpl(callApi, id))
|
|
11190
|
+
);
|
|
11191
|
+
server.registerTool(
|
|
11192
|
+
"inspector_list_processes",
|
|
11193
|
+
{
|
|
11194
|
+
title: "List observed client processes",
|
|
11195
|
+
description: "Groups recent compact logs by originating client PID and returns sessions, agents, user-agents, models, providers, ports, error counts, and latest logs. Useful for IDE -> process -> session navigation.",
|
|
11196
|
+
inputSchema: object({
|
|
11197
|
+
scanLimit: number().int().positive().max(200).optional().describe("How many recent logs to scan. Default 50, max 200."),
|
|
11198
|
+
limit: number().int().positive().max(200).optional().describe("Alias for scanLimit for simpler MCP clients.")
|
|
11199
|
+
})
|
|
11200
|
+
},
|
|
11201
|
+
(args) => safeCall(() => listProcessesImpl(callApi, args))
|
|
11202
|
+
);
|
|
11203
|
+
server.registerTool(
|
|
11204
|
+
"inspector_get_process",
|
|
11205
|
+
{
|
|
11206
|
+
title: "Get process summary",
|
|
11207
|
+
description: "Returns a bounded summary for one originating client PID across sessions, including user-agents, ports, models, providers, errors, and latest compact logs.",
|
|
11208
|
+
inputSchema: object({
|
|
11209
|
+
clientPid: number().int().positive().describe("Originating client process PID."),
|
|
11210
|
+
offset: number().int().nonnegative().optional().describe("Skip this many matching logs."),
|
|
11211
|
+
limit: number().int().positive().max(200).optional().describe("Maximum logs to summarize. Default 50, max 200.")
|
|
11212
|
+
})
|
|
11213
|
+
},
|
|
11214
|
+
(args) => safeCall(() => getProcessImpl(callApi, args))
|
|
11215
|
+
);
|
|
11216
|
+
server.registerTool(
|
|
11217
|
+
"inspector_get_process_timeline",
|
|
11218
|
+
{
|
|
11219
|
+
title: "Get process timeline",
|
|
11220
|
+
description: "Returns a bounded chronological timeline for one originating client PID, spanning all sessions observed for that process.",
|
|
11221
|
+
inputSchema: object({
|
|
11222
|
+
clientPid: number().int().positive().describe("Originating client process PID."),
|
|
11223
|
+
offset: number().int().nonnegative().optional().describe("Skip this many matching logs."),
|
|
11224
|
+
limit: number().int().positive().max(50).optional().describe("Maximum timeline events to return. Default 20, max 50.")
|
|
11225
|
+
})
|
|
11226
|
+
},
|
|
11227
|
+
(args) => safeCall(() => getProcessTimelineImpl(callApi, args))
|
|
11228
|
+
);
|
|
10830
11229
|
server.registerTool(
|
|
10831
11230
|
"inspector_list_sessions",
|
|
10832
11231
|
{
|
|
@@ -10890,6 +11289,26 @@ function registerTools(server) {
|
|
|
10890
11289
|
},
|
|
10891
11290
|
(args) => safeWriteCall("inspector_set_current_context", () => setCurrentContextImpl(args))
|
|
10892
11291
|
);
|
|
11292
|
+
server.registerTool(
|
|
11293
|
+
"inspector_get_runtime_config",
|
|
11294
|
+
{
|
|
11295
|
+
title: "Get Inspector runtime config",
|
|
11296
|
+
description: "Read-only runtime diagnostics: current proxy config plus log storage stats, including captureMode and log/chunk directories. Does not expose provider secrets.",
|
|
11297
|
+
inputSchema: object({})
|
|
11298
|
+
},
|
|
11299
|
+
() => safeCall(() => getRuntimeConfigImpl(callApi))
|
|
11300
|
+
);
|
|
11301
|
+
server.registerTool(
|
|
11302
|
+
"inspector_diagnose_capture",
|
|
11303
|
+
{
|
|
11304
|
+
title: "Diagnose capture detail availability",
|
|
11305
|
+
description: "Explains why headers or bodies may be missing for the current runtime or a specific log, including captureMode, compact body mode, rawHeaders presence, and next-step recommendations.",
|
|
11306
|
+
inputSchema: object({
|
|
11307
|
+
id: number().int().positive().optional().describe("Optional log id to inspect.")
|
|
11308
|
+
})
|
|
11309
|
+
},
|
|
11310
|
+
(args) => safeCall(() => diagnoseCaptureImpl(callApi, args))
|
|
11311
|
+
);
|
|
10893
11312
|
server.registerTool(
|
|
10894
11313
|
"inspector_create_run",
|
|
10895
11314
|
{
|
|
@@ -11051,6 +11470,19 @@ function registerTools(server) {
|
|
|
11051
11470
|
},
|
|
11052
11471
|
(args) => safeWriteCall("inspector_add_group_session", () => addGroupSessionImpl(args))
|
|
11053
11472
|
);
|
|
11473
|
+
server.registerTool(
|
|
11474
|
+
"inspector_add_group_process",
|
|
11475
|
+
{
|
|
11476
|
+
title: "Attach a process to an Inspector group",
|
|
11477
|
+
description: "Finds sessions observed for one client PID and attaches each session to a group with inferred agent/model/provider metadata. Useful when a process has both process fallback and explicit sessions.",
|
|
11478
|
+
inputSchema: object({
|
|
11479
|
+
groupId: string().min(1).describe("The Inspector group id."),
|
|
11480
|
+
clientPid: number().int().positive().describe("Originating client process PID."),
|
|
11481
|
+
labelPrefix: string().min(1).optional().describe("Optional label prefix. Defaults to PID <clientPid>.")
|
|
11482
|
+
})
|
|
11483
|
+
},
|
|
11484
|
+
(args) => safeWriteCall("inspector_add_group_process", () => addGroupProcessImpl(callApi, args))
|
|
11485
|
+
);
|
|
11054
11486
|
server.registerTool(
|
|
11055
11487
|
"inspector_export_group_evidence",
|
|
11056
11488
|
{
|
|
@@ -11279,6 +11711,21 @@ function registerResources(server) {
|
|
|
11279
11711
|
},
|
|
11280
11712
|
(uri) => jsonResource(uri, getCurrentContext())
|
|
11281
11713
|
);
|
|
11714
|
+
server.registerResource(
|
|
11715
|
+
"inspector_runtime_config",
|
|
11716
|
+
"inspector://runtime/config",
|
|
11717
|
+
{
|
|
11718
|
+
title: "Inspector runtime config",
|
|
11719
|
+
description: "Read-only runtime config and log storage stats.",
|
|
11720
|
+
mimeType: "application/json"
|
|
11721
|
+
},
|
|
11722
|
+
async (uri) => {
|
|
11723
|
+
const result = await getRuntimeConfigImpl(callApi);
|
|
11724
|
+
const first = result.content[0];
|
|
11725
|
+
const text = first?.text ?? "";
|
|
11726
|
+
return textResource(uri, result.isError === true ? "text/plain" : "application/json", text);
|
|
11727
|
+
}
|
|
11728
|
+
);
|
|
11282
11729
|
server.registerResource(
|
|
11283
11730
|
"inspector_sessions",
|
|
11284
11731
|
"inspector://sessions",
|
|
@@ -11343,6 +11790,63 @@ function registerResources(server) {
|
|
|
11343
11790
|
return textResource(uri, result.isError === true ? "text/plain" : "application/json", text);
|
|
11344
11791
|
}
|
|
11345
11792
|
);
|
|
11793
|
+
server.registerResource(
|
|
11794
|
+
"inspector_processes",
|
|
11795
|
+
"inspector://processes",
|
|
11796
|
+
{
|
|
11797
|
+
title: "Inspector client processes",
|
|
11798
|
+
description: "Observed originating client process PIDs grouped from recent logs.",
|
|
11799
|
+
mimeType: "application/json"
|
|
11800
|
+
},
|
|
11801
|
+
async (uri) => {
|
|
11802
|
+
const result = await listProcessesImpl(callApi, { scanLimit: 200 });
|
|
11803
|
+
const first = result.content[0];
|
|
11804
|
+
const text = first?.text ?? "";
|
|
11805
|
+
return textResource(uri, result.isError === true ? "text/plain" : "application/json", text);
|
|
11806
|
+
}
|
|
11807
|
+
);
|
|
11808
|
+
server.registerResource(
|
|
11809
|
+
"inspector_process",
|
|
11810
|
+
new ResourceTemplate("inspector://processes/{clientPid}", { list: listProcessResources }),
|
|
11811
|
+
{
|
|
11812
|
+
title: "Inspector process",
|
|
11813
|
+
description: "One originating client process summary with sessions, agents, user-agents, ports, models, providers, errors, and latest logs.",
|
|
11814
|
+
mimeType: "application/json"
|
|
11815
|
+
},
|
|
11816
|
+
async (uri, variables) => {
|
|
11817
|
+
const clientPid = variableString(variables, "clientPid");
|
|
11818
|
+
const parsed = clientPid === null ? Number.NaN : Number(clientPid);
|
|
11819
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
11820
|
+
return textResource(uri, "text/plain", "Invalid clientPid");
|
|
11821
|
+
}
|
|
11822
|
+
const result = await getProcessImpl(callApi, { clientPid: parsed });
|
|
11823
|
+
const first = result.content[0];
|
|
11824
|
+
const text = first?.text ?? "";
|
|
11825
|
+
return textResource(uri, result.isError === true ? "text/plain" : "application/json", text);
|
|
11826
|
+
}
|
|
11827
|
+
);
|
|
11828
|
+
server.registerResource(
|
|
11829
|
+
"inspector_process_timeline",
|
|
11830
|
+
new ResourceTemplate("inspector://processes/{clientPid}/timeline", {
|
|
11831
|
+
list: listProcessTimelineResources
|
|
11832
|
+
}),
|
|
11833
|
+
{
|
|
11834
|
+
title: "Inspector process timeline",
|
|
11835
|
+
description: "Bounded chronological timeline for one originating client process PID.",
|
|
11836
|
+
mimeType: "application/json"
|
|
11837
|
+
},
|
|
11838
|
+
async (uri, variables) => {
|
|
11839
|
+
const clientPid = variableString(variables, "clientPid");
|
|
11840
|
+
const parsed = clientPid === null ? Number.NaN : Number(clientPid);
|
|
11841
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
11842
|
+
return textResource(uri, "text/plain", "Invalid clientPid");
|
|
11843
|
+
}
|
|
11844
|
+
const result = await getProcessTimelineImpl(callApi, { clientPid: parsed, limit: 50 });
|
|
11845
|
+
const first = result.content[0];
|
|
11846
|
+
const text = first?.text ?? "";
|
|
11847
|
+
return textResource(uri, result.isError === true ? "text/plain" : "application/json", text);
|
|
11848
|
+
}
|
|
11849
|
+
);
|
|
11346
11850
|
server.registerResource(
|
|
11347
11851
|
"inspector_runs",
|
|
11348
11852
|
"inspector://runs",
|
|
@@ -11615,11 +12119,17 @@ const TOOL_NAMES = [
|
|
|
11615
12119
|
"inspector_search_logs",
|
|
11616
12120
|
"inspector_get_log",
|
|
11617
12121
|
"inspector_get_log_chunks",
|
|
12122
|
+
"inspector_get_log_headers",
|
|
12123
|
+
"inspector_list_processes",
|
|
12124
|
+
"inspector_get_process",
|
|
12125
|
+
"inspector_get_process_timeline",
|
|
11618
12126
|
"inspector_list_sessions",
|
|
11619
12127
|
"inspector_get_session",
|
|
11620
12128
|
"inspector_get_session_timeline",
|
|
11621
12129
|
"inspector_get_current_context",
|
|
11622
12130
|
"inspector_set_current_context",
|
|
12131
|
+
"inspector_get_runtime_config",
|
|
12132
|
+
"inspector_diagnose_capture",
|
|
11623
12133
|
"inspector_create_run",
|
|
11624
12134
|
"inspector_get_run",
|
|
11625
12135
|
"inspector_update_run",
|
|
@@ -11633,6 +12143,7 @@ const TOOL_NAMES = [
|
|
|
11633
12143
|
"inspector_update_group",
|
|
11634
12144
|
"inspector_delete_group",
|
|
11635
12145
|
"inspector_add_group_session",
|
|
12146
|
+
"inspector_add_group_process",
|
|
11636
12147
|
"inspector_export_group_evidence",
|
|
11637
12148
|
"inspector_get_group_evidence_files",
|
|
11638
12149
|
"inspector_export_group_evidence_bundle",
|
|
@@ -11655,10 +12166,14 @@ const RESOURCE_NAMES = [
|
|
|
11655
12166
|
"inspector_mcp_capabilities",
|
|
11656
12167
|
"inspector_mcp_config",
|
|
11657
12168
|
"inspector_current_context",
|
|
12169
|
+
"inspector_runtime_config",
|
|
11658
12170
|
"inspector_sessions",
|
|
11659
12171
|
"inspector_session",
|
|
11660
12172
|
"inspector_session_timeline",
|
|
11661
12173
|
"inspector_log",
|
|
12174
|
+
"inspector_processes",
|
|
12175
|
+
"inspector_process",
|
|
12176
|
+
"inspector_process_timeline",
|
|
11662
12177
|
"inspector_runs",
|
|
11663
12178
|
"inspector_recent_failures",
|
|
11664
12179
|
"inspector_run",
|
|
@@ -11750,6 +12265,7 @@ async function searchLogsPage(options) {
|
|
|
11750
12265
|
const firstPage = await listLogsPage({
|
|
11751
12266
|
sessionId: options.sessionId,
|
|
11752
12267
|
model: options.model,
|
|
12268
|
+
clientPid: options.clientPid,
|
|
11753
12269
|
offset: 0,
|
|
11754
12270
|
limit: 1,
|
|
11755
12271
|
includeBodies: false
|
|
@@ -11758,6 +12274,7 @@ async function searchLogsPage(options) {
|
|
|
11758
12274
|
const scanPage = await listLogsPage({
|
|
11759
12275
|
sessionId: options.sessionId,
|
|
11760
12276
|
model: options.model,
|
|
12277
|
+
clientPid: options.clientPid,
|
|
11761
12278
|
offset: scanOffset,
|
|
11762
12279
|
limit: scanLimit,
|
|
11763
12280
|
includeBodies: false
|
|
@@ -11813,6 +12330,11 @@ const Route$u = createFileRoute("/api/logs")({
|
|
|
11813
12330
|
}
|
|
11814
12331
|
const sessionId = url.searchParams.get("sessionId") ?? void 0;
|
|
11815
12332
|
const model = url.searchParams.get("model") ?? void 0;
|
|
12333
|
+
const clientPidResult = parseOptionalPositiveInt(url.searchParams.get("clientPid"));
|
|
12334
|
+
if (!clientPidResult.ok) {
|
|
12335
|
+
return Response.json({ error: "Invalid clientPid" }, { status: 400 });
|
|
12336
|
+
}
|
|
12337
|
+
const clientPid = clientPidResult.value ?? void 0;
|
|
11816
12338
|
const query = url.searchParams.get("q") ?? url.searchParams.get("search");
|
|
11817
12339
|
const offset = parseNonNegativeInt(url.searchParams.get("offset"), 0);
|
|
11818
12340
|
if (query !== null) {
|
|
@@ -11822,7 +12344,7 @@ const Route$u = createFileRoute("/api/logs")({
|
|
|
11822
12344
|
LOG_SEARCH_DEFAULT_SCAN_LIMIT
|
|
11823
12345
|
);
|
|
11824
12346
|
return Response.json(
|
|
11825
|
-
await searchLogsPage({ query, sessionId, model, offset, limit: limit2, scanLimit })
|
|
12347
|
+
await searchLogsPage({ query, sessionId, model, clientPid, offset, limit: limit2, scanLimit })
|
|
11826
12348
|
);
|
|
11827
12349
|
}
|
|
11828
12350
|
const limit = parsePositiveInt$2(url.searchParams.get("limit"), 50);
|
|
@@ -11844,6 +12366,7 @@ const Route$u = createFileRoute("/api/logs")({
|
|
|
11844
12366
|
await listLogsCursorPage({
|
|
11845
12367
|
sessionId,
|
|
11846
12368
|
model,
|
|
12369
|
+
clientPid,
|
|
11847
12370
|
limit,
|
|
11848
12371
|
includeBodies,
|
|
11849
12372
|
beforeLogId: beforeLogId.value ?? void 0,
|
|
@@ -11853,7 +12376,7 @@ const Route$u = createFileRoute("/api/logs")({
|
|
|
11853
12376
|
);
|
|
11854
12377
|
}
|
|
11855
12378
|
return Response.json(
|
|
11856
|
-
await listLogsPage({ sessionId, model, offset, limit, includeBodies })
|
|
12379
|
+
await listLogsPage({ sessionId, model, clientPid, offset, limit, includeBodies })
|
|
11857
12380
|
);
|
|
11858
12381
|
},
|
|
11859
12382
|
DELETE: async ({ request }) => {
|