@tonyclaw/agent-inspector 3.1.19 → 3.1.21
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/backend/nitro.json +1 -1
- package/.output/cli.js +140 -20
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-0qlvqhVh.mjs → router-Be6BLuut.mjs} +2454 -1135
- package/.output/server/_tanstack-start-manifest_v-ClTaMkEj.mjs +4 -0
- package/.output/server/index.mjs +1 -1
- package/.output/ui/assets/{CompareDrawer-BOk9hd-Z.js → CompareDrawer-CGk2HFI1.js} +1 -1
- package/.output/ui/assets/{InspectorPet-DhHDBpk7.js → InspectorPet-CDM7SdOT.js} +1 -1
- package/.output/ui/assets/ProxyViewerContainer-CaWD78tw.js +60 -0
- package/.output/ui/assets/{ReplayDialog-DJhKtuNB.js → ReplayDialog-DqaeV1He.js} +1 -1
- package/.output/ui/assets/{RequestAnatomy-hP0tsqDC.js → RequestAnatomy-BtHl9P0k.js} +1 -1
- package/.output/ui/assets/{ResponseView-BSiUkwCb.js → ResponseView-C6t1vpxs.js} +1 -1
- package/.output/ui/assets/{StreamingChunkSequence-CzQqwv4X.js → StreamingChunkSequence-R4mgd3GQ.js} +1 -1
- package/.output/ui/assets/{_sessionId-BU-i6h0V.js → _sessionId-CL3hdLlQ.js} +1 -1
- package/.output/ui/assets/{_sessionId-C2Pq4Jb0.js → _sessionId-HS666S72.js} +1 -1
- package/.output/ui/assets/index-B-Qod-aA.css +1 -0
- package/.output/ui/assets/{index-D8C-t-2U.js → index-Bd9jPL4n.js} +1 -1
- package/.output/ui/assets/{index-zHpVF0jk.js → index-C4NHrclw.js} +1 -1
- package/.output/ui/assets/{index-UUnRwbOJ.js → index-UF1PJEcq.js} +1 -1
- package/.output/ui/assets/{index-B7NeIBDf.js → index-tX38BNV2.js} +2 -2
- package/.output/ui/assets/{json-viewer-DOzS5rUT.js → json-viewer-KSgzN_Ov.js} +1 -1
- package/.output/ui/assets/{jszip.min-CCCY7qNk.js → jszip.min-BiMDxghN.js} +1 -1
- package/.output/ui/index.html +2 -2
- package/.output/workers/logFinalizer.worker.js +261 -0
- package/.output/workers/sessionWorkerEntry.js +261 -0
- package/package.json +13 -6
- package/src/backend/routes/api/logs.$id.replay.ts +6 -1
- package/src/backend/routes/api/logs.stream.ts +7 -0
- package/src/backend/routes/api/providers.$providerId.model-metadata.ts +15 -4
- package/src/backend/routes/api/storage.ts +56 -0
- package/src/backend/routes/metrics.ts +14 -0
- package/src/cli/doctor.ts +135 -2
- package/src/cli.ts +7 -4
- package/src/components/ProxyViewerContainer.tsx +41 -24
- package/src/components/providers/SettingsDialog.tsx +261 -70
- package/src/components/proxy-viewer/LogEntry.tsx +19 -5
- package/src/components/proxy-viewer/bodyHydration.ts +67 -0
- package/src/components/proxy-viewer/proxyViewerContainerLogic.ts +78 -0
- package/src/contracts/index.ts +2 -0
- package/src/contracts/log.ts +5 -0
- package/src/knowledge/openclawClient.ts +11 -2
- package/src/knowledge/openclawGatewayClient.ts +6 -1
- package/src/lib/resourceLimits.ts +191 -0
- package/src/lib/safeFetch.ts +428 -20
- package/src/lib/stopReason.ts +66 -39
- package/src/proxy/ecosystemTasks.ts +20 -0
- package/src/proxy/handler.ts +17 -4
- package/src/proxy/identityProxy.ts +36 -16
- package/src/proxy/logIndex.ts +57 -6
- package/src/proxy/logger.ts +4 -0
- package/src/proxy/rawStreamCapture.ts +16 -1
- package/src/proxy/runtimeHealth.ts +4 -2
- package/src/proxy/runtimeMetrics.ts +149 -0
- package/src/proxy/schemas.ts +2 -0
- package/src/proxy/sessionRuntime.ts +38 -0
- package/src/proxy/sqliteLogIndex.ts +78 -2
- package/src/proxy/storageLifecycle.ts +432 -0
- package/src/proxy/store.ts +69 -13
- package/.output/server/_tanstack-start-manifest_v-DnbdNeun.mjs +0 -4
- package/.output/ui/assets/ProxyViewerContainer-BRYf987G.js +0 -59
- package/.output/ui/assets/index-IBEDJoV_.css +0 -1
|
@@ -2,19 +2,21 @@ import { c as createRouter, a as createRootRoute, b as createFileRoute } from ".
|
|
|
2
2
|
import { h as getRouterBasePath, r as runWithRequestPublicBasePath, g as getPublicBasePath, b as appendBasePathToOrigin, c as getPublicOrigin, w as withBasePath, s as stripBasePath, a as getConfiguredBasePath, n as normalizePublicBasePath, f as AGENT_INSPECTOR_BASE_PATH_ENV, d as getPublicPort, A as AGENT_INSPECTOR_PUBLIC_BASE_PATH_HEADER, e as AGENT_INSPECTOR_PUBLIC_PORT_HEADER } from "./publicBasePathContext-IH2ilOfu.mjs";
|
|
3
3
|
import { randomUUID, timingSafeEqual, createHash, createDecipheriv, randomBytes, createCipheriv } from "node:crypto";
|
|
4
4
|
import fs, { readdir, open, readFile, unlink, appendFile, writeFile, rename, stat, lstat, link, rm, statfs } from "node:fs/promises";
|
|
5
|
-
import path, {
|
|
6
|
-
import { existsSync, readFileSync, writeFileSync, renameSync,
|
|
5
|
+
import path, { dirname, join, basename, relative, resolve, isAbsolute, win32, posix, parse, sep } from "node:path";
|
|
6
|
+
import { unlinkSync, existsSync, readFileSync, writeFileSync, renameSync, readdirSync, lstatSync, rmSync, cpSync, statSync, createReadStream, mkdirSync, copyFileSync, constants, realpathSync, openSync, fstatSync, fchmodSync, closeSync, writeSync, fsyncSync, createWriteStream } from "node:fs";
|
|
7
7
|
import { spawn, execFileSync, execFile, spawnSync } from "node:child_process";
|
|
8
8
|
import { Buffer } from "node:buffer";
|
|
9
9
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
10
10
|
import { Worker } from "node:worker_threads";
|
|
11
11
|
import { createInterface } from "node:readline";
|
|
12
|
+
import assert from "node:assert/strict";
|
|
12
13
|
import { C as Conf } from "../_libs/conf.mjs";
|
|
13
14
|
import { randomUUID as randomUUID$1 } from "crypto";
|
|
14
15
|
import { promisify } from "node:util";
|
|
15
16
|
import { lookup } from "node:dns/promises";
|
|
17
|
+
import { request as request$1 } from "node:http";
|
|
18
|
+
import { request } from "node:https";
|
|
16
19
|
import { isIP, createServer } from "node:net";
|
|
17
|
-
import assert from "node:assert/strict";
|
|
18
20
|
import { M as McpServer, W as WebStandardStreamableHTTPServerTransport, R as ResourceTemplate } from "../_libs/modelcontextprotocol__server.mjs";
|
|
19
21
|
import { J as JSZip } from "../_libs/jszip.mjs";
|
|
20
22
|
import { homedir } from "node:os";
|
|
@@ -67,7 +69,7 @@ import "../_libs/lie.mjs";
|
|
|
67
69
|
import "../_libs/immediate.mjs";
|
|
68
70
|
import "../_libs/setimmediate.mjs";
|
|
69
71
|
import "../_libs/pako.mjs";
|
|
70
|
-
const Route$
|
|
72
|
+
const Route$W = createRootRoute();
|
|
71
73
|
const PRIVATE_DIRECTORY_MODE = 448;
|
|
72
74
|
const PRIVATE_FILE_MODE = 384;
|
|
73
75
|
const securedPathFingerprints = /* @__PURE__ */ new Map();
|
|
@@ -449,6 +451,8 @@ const ToolTraceEventSchema = object({
|
|
|
449
451
|
argumentsText: string().nullable(),
|
|
450
452
|
argumentsPreview: string().nullable()
|
|
451
453
|
});
|
|
454
|
+
const StopReasonValueSchema = _enum(["end_turn", "tool_use", "stop", "length"]);
|
|
455
|
+
const StopReasonSchema = StopReasonValueSchema.nullable();
|
|
452
456
|
const StreamingChunksArraySchema = object({
|
|
453
457
|
chunks: array(StreamingChunkSchema$1),
|
|
454
458
|
truncated: boolean().optional().default(false)
|
|
@@ -509,6 +513,7 @@ const CapturedLogSchema = object({
|
|
|
509
513
|
captureIncompleteReason: CaptureIncompleteReasonSchema.nullable().optional(),
|
|
510
514
|
warnings: array(string()).optional(),
|
|
511
515
|
toolTraceEvents: array(ToolTraceEventSchema).optional(),
|
|
516
|
+
stopReason: StopReasonSchema.optional(),
|
|
512
517
|
/** Error message from streaming response (e.g., SSE error event) */
|
|
513
518
|
error: string().nullable().optional()
|
|
514
519
|
});
|
|
@@ -1297,8 +1302,8 @@ function canAppendCapturedLogFileOnExit(logDir, filePath) {
|
|
|
1297
1302
|
return false;
|
|
1298
1303
|
}
|
|
1299
1304
|
}
|
|
1300
|
-
const DAY_MS = 24 * 60 * 60 * 1e3;
|
|
1301
|
-
const DEFAULT_RETENTION_DAYS = 7;
|
|
1305
|
+
const DAY_MS$1 = 24 * 60 * 60 * 1e3;
|
|
1306
|
+
const DEFAULT_RETENTION_DAYS$1 = 7;
|
|
1302
1307
|
const DEFAULT_APP_LOG_MAX_BYTES = 10 * 1024 * 1024;
|
|
1303
1308
|
const DEFAULT_APP_LOG_MAX_FILES = 5;
|
|
1304
1309
|
function writeStderrFallback(message, error) {
|
|
@@ -1326,6 +1331,9 @@ function getInspectorLogPath() {
|
|
|
1326
1331
|
}
|
|
1327
1332
|
return path.join(getDataDir(), "logs", "inspector.log");
|
|
1328
1333
|
}
|
|
1334
|
+
function resolveApplicationLogPath() {
|
|
1335
|
+
return getInspectorLogPath();
|
|
1336
|
+
}
|
|
1329
1337
|
function readBoundedInteger(name2, fallback, minimum, maximum) {
|
|
1330
1338
|
const raw = process.env[name2];
|
|
1331
1339
|
if (raw === void 0 || raw === "") return fallback;
|
|
@@ -1334,7 +1342,7 @@ function readBoundedInteger(name2, fallback, minimum, maximum) {
|
|
|
1334
1342
|
return parsed;
|
|
1335
1343
|
}
|
|
1336
1344
|
function getApplicationLogPolicy() {
|
|
1337
|
-
const retentionDays = readBoundedInteger("LOG_RETENTION_DAYS", DEFAULT_RETENTION_DAYS, 1, 3650);
|
|
1345
|
+
const retentionDays = readBoundedInteger("LOG_RETENTION_DAYS", DEFAULT_RETENTION_DAYS$1, 1, 3650);
|
|
1338
1346
|
return {
|
|
1339
1347
|
maxBytes: readBoundedInteger(
|
|
1340
1348
|
"AGENT_INSPECTOR_APP_LOG_MAX_BYTES",
|
|
@@ -1348,7 +1356,7 @@ function getApplicationLogPolicy() {
|
|
|
1348
1356
|
1,
|
|
1349
1357
|
100
|
|
1350
1358
|
),
|
|
1351
|
-
retentionMs: retentionDays * DAY_MS
|
|
1359
|
+
retentionMs: retentionDays * DAY_MS$1
|
|
1352
1360
|
};
|
|
1353
1361
|
}
|
|
1354
1362
|
function getRotatedGeneration(entry, baseName) {
|
|
@@ -3167,6 +3175,98 @@ function extractToolTraceEvents(log) {
|
|
|
3167
3175
|
return [];
|
|
3168
3176
|
}
|
|
3169
3177
|
}
|
|
3178
|
+
function normalizeAnthropicStopReason(value) {
|
|
3179
|
+
switch (value) {
|
|
3180
|
+
case "end_turn":
|
|
3181
|
+
case "tool_use":
|
|
3182
|
+
return value;
|
|
3183
|
+
case "max_tokens":
|
|
3184
|
+
return "length";
|
|
3185
|
+
default:
|
|
3186
|
+
return null;
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
function extractStopReasonFromJsonValue(value) {
|
|
3190
|
+
if (!isRecord$2(value)) return null;
|
|
3191
|
+
if (typeof value.stop_reason === "string") {
|
|
3192
|
+
return normalizeAnthropicStopReason(value.stop_reason);
|
|
3193
|
+
}
|
|
3194
|
+
if (isRecord$2(value.delta) && typeof value.delta.stop_reason === "string") {
|
|
3195
|
+
return normalizeAnthropicStopReason(value.delta.stop_reason);
|
|
3196
|
+
}
|
|
3197
|
+
if (Array.isArray(value.choices)) {
|
|
3198
|
+
for (const choice of value.choices) {
|
|
3199
|
+
if (!isRecord$2(choice) || typeof choice.finish_reason !== "string") continue;
|
|
3200
|
+
const reason = normalizeOpenAIFinishReason(choice.finish_reason);
|
|
3201
|
+
if (reason !== null) return reason;
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
if (Array.isArray(value.output)) {
|
|
3205
|
+
for (const item of value.output) {
|
|
3206
|
+
if (isRecord$2(item) && item.type === "function_call") {
|
|
3207
|
+
return "tool_use";
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
if (value.status === "completed") {
|
|
3211
|
+
return "stop";
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
if (value.status === "incomplete" && isRecord$2(value.incomplete_details)) {
|
|
3215
|
+
const reason = value.incomplete_details.reason;
|
|
3216
|
+
if (typeof reason === "string" && isResponsesLengthLimitReason(reason)) {
|
|
3217
|
+
return "length";
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
return null;
|
|
3221
|
+
}
|
|
3222
|
+
function extractStopReasonFromSseText(text) {
|
|
3223
|
+
let stopReason = null;
|
|
3224
|
+
for (const rawLine of text.split("\n")) {
|
|
3225
|
+
const line = rawLine.trim();
|
|
3226
|
+
if (!line.startsWith("data: ")) continue;
|
|
3227
|
+
const data = line.slice(6).trim();
|
|
3228
|
+
if (data === "[DONE]") break;
|
|
3229
|
+
try {
|
|
3230
|
+
const reason = extractStopReasonFromJsonValue(JSON.parse(data));
|
|
3231
|
+
if (reason !== null) stopReason = reason;
|
|
3232
|
+
} catch {
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
return stopReason;
|
|
3236
|
+
}
|
|
3237
|
+
function normalizeOpenAIFinishReason(value) {
|
|
3238
|
+
switch (value) {
|
|
3239
|
+
case "stop":
|
|
3240
|
+
case "length":
|
|
3241
|
+
return value;
|
|
3242
|
+
case "max_tokens":
|
|
3243
|
+
return "length";
|
|
3244
|
+
default:
|
|
3245
|
+
return null;
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
function isResponsesLengthLimitReason(value) {
|
|
3249
|
+
switch (value) {
|
|
3250
|
+
case "max_output_tokens":
|
|
3251
|
+
case "max_tokens":
|
|
3252
|
+
return true;
|
|
3253
|
+
default:
|
|
3254
|
+
return false;
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
function extractStopReason(log) {
|
|
3258
|
+
if (log.stopReason !== void 0) return log.stopReason;
|
|
3259
|
+
if (log.responseText === null) return null;
|
|
3260
|
+
try {
|
|
3261
|
+
let json = JSON.parse(log.responseText);
|
|
3262
|
+
if (typeof json === "string") {
|
|
3263
|
+
json = JSON.parse(json);
|
|
3264
|
+
}
|
|
3265
|
+
return extractStopReasonFromJsonValue(json);
|
|
3266
|
+
} catch {
|
|
3267
|
+
return extractStopReasonFromSseText(log.responseText);
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3170
3270
|
const SQLITE_INDEX_FILE = "inspector.sqlite";
|
|
3171
3271
|
const SQLITE_PACKAGE_NAME$1 = "better-sqlite3";
|
|
3172
3272
|
const BUN_SQLITE_MODULE_NAME$1 = "bun:sqlite";
|
|
@@ -3209,6 +3309,7 @@ CREATE TABLE IF NOT EXISTS log_index (
|
|
|
3209
3309
|
dropped_bytes INTEGER NOT NULL DEFAULT 0,
|
|
3210
3310
|
capture_incomplete_reason TEXT,
|
|
3211
3311
|
warnings_json TEXT,
|
|
3312
|
+
stop_reason TEXT,
|
|
3212
3313
|
tool_trace_events_json TEXT,
|
|
3213
3314
|
error TEXT
|
|
3214
3315
|
);
|
|
@@ -3255,10 +3356,11 @@ INSERT INTO log_index (
|
|
|
3255
3356
|
dropped_bytes,
|
|
3256
3357
|
capture_incomplete_reason,
|
|
3257
3358
|
warnings_json,
|
|
3359
|
+
stop_reason,
|
|
3258
3360
|
tool_trace_events_json,
|
|
3259
3361
|
error
|
|
3260
3362
|
) VALUES (
|
|
3261
|
-
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
|
3363
|
+
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
|
3262
3364
|
)
|
|
3263
3365
|
ON CONFLICT(id) DO UPDATE SET
|
|
3264
3366
|
file = excluded.file,
|
|
@@ -3297,6 +3399,7 @@ ON CONFLICT(id) DO UPDATE SET
|
|
|
3297
3399
|
dropped_bytes = excluded.dropped_bytes,
|
|
3298
3400
|
capture_incomplete_reason = excluded.capture_incomplete_reason,
|
|
3299
3401
|
warnings_json = excluded.warnings_json,
|
|
3402
|
+
stop_reason = excluded.stop_reason,
|
|
3300
3403
|
tool_trace_events_json = excluded.tool_trace_events_json,
|
|
3301
3404
|
error = excluded.error
|
|
3302
3405
|
`;
|
|
@@ -3378,6 +3481,7 @@ const REQUIRED_SCHEMA_COLUMNS = [
|
|
|
3378
3481
|
{ name: "dropped_chunks", definition: "INTEGER NOT NULL DEFAULT 0" },
|
|
3379
3482
|
{ name: "dropped_bytes", definition: "INTEGER NOT NULL DEFAULT 0" },
|
|
3380
3483
|
{ name: "capture_incomplete_reason", definition: "TEXT" },
|
|
3484
|
+
{ name: "stop_reason", definition: "TEXT" },
|
|
3381
3485
|
{ name: "tool_trace_events_json", definition: "TEXT" }
|
|
3382
3486
|
];
|
|
3383
3487
|
function ensureSchemaColumns(db) {
|
|
@@ -3559,6 +3663,7 @@ function entryParams(entry) {
|
|
|
3559
3663
|
summary?.droppedBytes ?? 0,
|
|
3560
3664
|
summary?.captureIncompleteReason ?? null,
|
|
3561
3665
|
warningJson(summary?.warnings),
|
|
3666
|
+
summary?.stopReason ?? null,
|
|
3562
3667
|
toolTraceEventsJson(summary),
|
|
3563
3668
|
summary?.error ?? null
|
|
3564
3669
|
];
|
|
@@ -3567,6 +3672,143 @@ function readNumber$1(row, name2) {
|
|
|
3567
3672
|
const value = readProperty$2(row, name2);
|
|
3568
3673
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
3569
3674
|
}
|
|
3675
|
+
function readString$2(row, name2) {
|
|
3676
|
+
const value = readProperty$2(row, name2);
|
|
3677
|
+
return typeof value === "string" ? value : null;
|
|
3678
|
+
}
|
|
3679
|
+
function readBoolean(row, name2) {
|
|
3680
|
+
return readNumber$1(row, name2) === 1;
|
|
3681
|
+
}
|
|
3682
|
+
function readApiFormat(row) {
|
|
3683
|
+
const value = readString$2(row, "api_format");
|
|
3684
|
+
switch (value) {
|
|
3685
|
+
case "anthropic":
|
|
3686
|
+
return "anthropic";
|
|
3687
|
+
case "openai":
|
|
3688
|
+
return "openai";
|
|
3689
|
+
case "unknown":
|
|
3690
|
+
case null:
|
|
3691
|
+
return "unknown";
|
|
3692
|
+
default:
|
|
3693
|
+
return "unknown";
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
function readCaptureIncompleteReason(row) {
|
|
3697
|
+
const value = readString$2(row, "capture_incomplete_reason");
|
|
3698
|
+
switch (value) {
|
|
3699
|
+
case "capacity-exceeded":
|
|
3700
|
+
case "pipeline-closed":
|
|
3701
|
+
case "write-failed":
|
|
3702
|
+
case "finalize-failed":
|
|
3703
|
+
case "upstream-stream-error":
|
|
3704
|
+
case "shutdown":
|
|
3705
|
+
return value;
|
|
3706
|
+
case null:
|
|
3707
|
+
default:
|
|
3708
|
+
return null;
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
function readStopReason(row) {
|
|
3712
|
+
const value = readString$2(row, "stop_reason");
|
|
3713
|
+
switch (value) {
|
|
3714
|
+
case "end_turn":
|
|
3715
|
+
case "tool_use":
|
|
3716
|
+
case "stop":
|
|
3717
|
+
case "length":
|
|
3718
|
+
return value;
|
|
3719
|
+
case null:
|
|
3720
|
+
default:
|
|
3721
|
+
return null;
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
function readWarnings(row) {
|
|
3725
|
+
const value = readString$2(row, "warnings_json");
|
|
3726
|
+
if (value === null) return void 0;
|
|
3727
|
+
try {
|
|
3728
|
+
const parsed = JSON.parse(value);
|
|
3729
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
3730
|
+
const warnings = [];
|
|
3731
|
+
for (const item of parsed) {
|
|
3732
|
+
if (typeof item !== "string") return void 0;
|
|
3733
|
+
warnings.push(item);
|
|
3734
|
+
}
|
|
3735
|
+
return warnings;
|
|
3736
|
+
} catch {
|
|
3737
|
+
return void 0;
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
function readToolTraceEvents(row) {
|
|
3741
|
+
const value = readString$2(row, "tool_trace_events_json");
|
|
3742
|
+
if (value === null) return void 0;
|
|
3743
|
+
try {
|
|
3744
|
+
const parsed = JSON.parse(value);
|
|
3745
|
+
const result = ToolTraceEventSchema.array().safeParse(parsed);
|
|
3746
|
+
return result.success ? result.data : void 0;
|
|
3747
|
+
} catch {
|
|
3748
|
+
return void 0;
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
function readRequiredNumber(row, name2) {
|
|
3752
|
+
const value = readNumber$1(row, name2);
|
|
3753
|
+
return value === null || !Number.isInteger(value) ? null : value;
|
|
3754
|
+
}
|
|
3755
|
+
function summaryFromRow(row, id) {
|
|
3756
|
+
return {
|
|
3757
|
+
id,
|
|
3758
|
+
timestamp: readString$2(row, "timestamp") ?? "",
|
|
3759
|
+
method: readString$2(row, "method") ?? "",
|
|
3760
|
+
path: readString$2(row, "path") ?? "",
|
|
3761
|
+
model: readString$2(row, "model"),
|
|
3762
|
+
sessionId: readString$2(row, "session_id"),
|
|
3763
|
+
responseStatus: readNumber$1(row, "response_status"),
|
|
3764
|
+
inputTokens: readNumber$1(row, "input_tokens"),
|
|
3765
|
+
outputTokens: readNumber$1(row, "output_tokens"),
|
|
3766
|
+
cacheCreationInputTokens: readNumber$1(row, "cache_creation_input_tokens"),
|
|
3767
|
+
cacheReadInputTokens: readNumber$1(row, "cache_read_input_tokens"),
|
|
3768
|
+
elapsedMs: readNumber$1(row, "elapsed_ms"),
|
|
3769
|
+
firstChunkMs: readNumber$1(row, "first_chunk_ms"),
|
|
3770
|
+
totalStreamMs: readNumber$1(row, "total_stream_ms"),
|
|
3771
|
+
tokensPerSecond: readNumber$1(row, "tokens_per_second"),
|
|
3772
|
+
streaming: readBoolean(row, "streaming"),
|
|
3773
|
+
userAgent: readString$2(row, "user_agent"),
|
|
3774
|
+
origin: readString$2(row, "origin"),
|
|
3775
|
+
apiFormat: readApiFormat(row),
|
|
3776
|
+
isTest: readBoolean(row, "is_test"),
|
|
3777
|
+
replayOfLogId: readNumber$1(row, "replay_of_log_id"),
|
|
3778
|
+
providerName: readString$2(row, "provider_name"),
|
|
3779
|
+
clientPort: readNumber$1(row, "client_port"),
|
|
3780
|
+
clientPid: readNumber$1(row, "client_pid"),
|
|
3781
|
+
clientCwd: readString$2(row, "client_cwd"),
|
|
3782
|
+
clientProjectFolder: readString$2(row, "client_project_folder"),
|
|
3783
|
+
streamingChunksPath: readString$2(row, "streaming_chunks_path"),
|
|
3784
|
+
rawRequestBodyBytes: readNumber$1(row, "raw_request_body_bytes"),
|
|
3785
|
+
responseTextBytes: readNumber$1(row, "response_text_bytes"),
|
|
3786
|
+
captureIncomplete: readBoolean(row, "capture_incomplete"),
|
|
3787
|
+
droppedChunks: readNumber$1(row, "dropped_chunks") ?? 0,
|
|
3788
|
+
droppedBytes: readNumber$1(row, "dropped_bytes") ?? 0,
|
|
3789
|
+
captureIncompleteReason: readCaptureIncompleteReason(row),
|
|
3790
|
+
warnings: readWarnings(row),
|
|
3791
|
+
stopReason: readStopReason(row),
|
|
3792
|
+
toolTraceEvents: readToolTraceEvents(row),
|
|
3793
|
+
error: readString$2(row, "error")
|
|
3794
|
+
};
|
|
3795
|
+
}
|
|
3796
|
+
function entryFromRow(row) {
|
|
3797
|
+
const id = readRequiredNumber(row, "id");
|
|
3798
|
+
const file = readString$2(row, "file");
|
|
3799
|
+
const byteOffset = readRequiredNumber(row, "byte_offset");
|
|
3800
|
+
const byteLength = readRequiredNumber(row, "byte_length");
|
|
3801
|
+
if (id === null || file === null || byteOffset === null || byteLength === null) return null;
|
|
3802
|
+
return {
|
|
3803
|
+
id,
|
|
3804
|
+
file,
|
|
3805
|
+
byteOffset,
|
|
3806
|
+
byteLength,
|
|
3807
|
+
sessionId: readString$2(row, "session_id"),
|
|
3808
|
+
model: readString$2(row, "model"),
|
|
3809
|
+
summary: summaryFromRow(row, id)
|
|
3810
|
+
};
|
|
3811
|
+
}
|
|
3570
3812
|
async function getSqliteLogIndexMaxId() {
|
|
3571
3813
|
const state = await getSqliteState();
|
|
3572
3814
|
if (state.status !== "ready") return null;
|
|
@@ -3579,6 +3821,44 @@ async function upsertSqliteLogIndexEntry(entry) {
|
|
|
3579
3821
|
if (state.status !== "ready") return false;
|
|
3580
3822
|
return runPrepared$1(state.db, UPSERT_SQL$1, entryParams(entry));
|
|
3581
3823
|
}
|
|
3824
|
+
function sqliteCursorWhereClause(filters) {
|
|
3825
|
+
const clauses = [];
|
|
3826
|
+
const params = [];
|
|
3827
|
+
if (filters.sessionId !== void 0) {
|
|
3828
|
+
clauses.push("session_id = ?");
|
|
3829
|
+
params.push(filters.sessionId);
|
|
3830
|
+
}
|
|
3831
|
+
if (filters.model !== void 0) {
|
|
3832
|
+
clauses.push("model = ?");
|
|
3833
|
+
params.push(filters.model);
|
|
3834
|
+
}
|
|
3835
|
+
if (filters.clientPid !== void 0) {
|
|
3836
|
+
clauses.push("client_pid = ?");
|
|
3837
|
+
params.push(filters.clientPid);
|
|
3838
|
+
}
|
|
3839
|
+
return {
|
|
3840
|
+
sql: clauses.length === 0 ? "" : ` WHERE ${clauses.join(" AND ")}`,
|
|
3841
|
+
params
|
|
3842
|
+
};
|
|
3843
|
+
}
|
|
3844
|
+
async function listSqliteLogCursorEntries(filters) {
|
|
3845
|
+
const state = await getSqliteState();
|
|
3846
|
+
if (state.status !== "ready") return null;
|
|
3847
|
+
const where = sqliteCursorWhereClause(filters);
|
|
3848
|
+
const rows = allPrepared$1(
|
|
3849
|
+
state.db,
|
|
3850
|
+
`SELECT * FROM log_index${where.sql} ORDER BY id ASC`,
|
|
3851
|
+
where.params
|
|
3852
|
+
);
|
|
3853
|
+
if (rows === null) return null;
|
|
3854
|
+
const entries = [];
|
|
3855
|
+
for (const row of rows) {
|
|
3856
|
+
const entry = entryFromRow(row);
|
|
3857
|
+
if (entry === null || entry.summary === void 0) return null;
|
|
3858
|
+
entries.push(entry);
|
|
3859
|
+
}
|
|
3860
|
+
return entries;
|
|
3861
|
+
}
|
|
3582
3862
|
async function syncSqliteLogIndexEntries(entries) {
|
|
3583
3863
|
const state = await getSqliteState();
|
|
3584
3864
|
if (state.status !== "ready") return false;
|
|
@@ -4064,6 +4344,7 @@ function createLogIndexEntryMetadata(log) {
|
|
|
4064
4344
|
droppedBytes: log.droppedBytes ?? 0,
|
|
4065
4345
|
captureIncompleteReason: log.captureIncompleteReason ?? null,
|
|
4066
4346
|
warnings: log.warnings,
|
|
4347
|
+
stopReason: log.stopReason ?? extractStopReason(log),
|
|
4067
4348
|
toolTraceEvents: extractToolTraceEvents(log),
|
|
4068
4349
|
error: log.error ?? null
|
|
4069
4350
|
}
|
|
@@ -4076,8 +4357,33 @@ function createEmptyIndex() {
|
|
|
4076
4357
|
maxId: 0
|
|
4077
4358
|
};
|
|
4078
4359
|
}
|
|
4360
|
+
function createIndexFromEntries(entries) {
|
|
4361
|
+
const index = createEmptyIndex();
|
|
4362
|
+
for (const entry of entries) {
|
|
4363
|
+
index.entries[entry.id] = entry;
|
|
4364
|
+
index.maxId = Math.max(index.maxId, entry.id);
|
|
4365
|
+
}
|
|
4366
|
+
return index;
|
|
4367
|
+
}
|
|
4368
|
+
function sqliteProjectionReferencesExistingLedgers(entries) {
|
|
4369
|
+
const logDir = resolveLogDir();
|
|
4370
|
+
for (const entry of entries) {
|
|
4371
|
+
if (!entry.file.endsWith(".jsonl")) return false;
|
|
4372
|
+
if (!existsSync(join(logDir, entry.file))) return false;
|
|
4373
|
+
}
|
|
4374
|
+
return true;
|
|
4375
|
+
}
|
|
4079
4376
|
async function loadIndex() {
|
|
4080
4377
|
if (cachedIndex !== null) return cachedIndex;
|
|
4378
|
+
const sqliteEntries = await listSqliteLogCursorEntries({});
|
|
4379
|
+
if (sqliteEntries !== null) {
|
|
4380
|
+
if (sqliteProjectionReferencesExistingLedgers(sqliteEntries)) {
|
|
4381
|
+
cachedIndex = createIndexFromEntries(sqliteEntries);
|
|
4382
|
+
allocatedMaxId = Math.max(allocatedMaxId, cachedIndex.maxId);
|
|
4383
|
+
return cachedIndex;
|
|
4384
|
+
}
|
|
4385
|
+
logger.warn("[logIndex] SQLite projection references missing ledgers; rebuilding from JSONL");
|
|
4386
|
+
}
|
|
4081
4387
|
return await rebuildIndex();
|
|
4082
4388
|
}
|
|
4083
4389
|
async function saveIndex(index) {
|
|
@@ -4114,8 +4420,10 @@ async function addToIndex(id, file, byteOffset, byteLength, metadata) {
|
|
|
4114
4420
|
if (id > index.maxId) {
|
|
4115
4421
|
index.maxId = id;
|
|
4116
4422
|
}
|
|
4117
|
-
|
|
4118
|
-
|
|
4423
|
+
const sqliteUpdated = await upsertSqliteLogIndexEntry(entry);
|
|
4424
|
+
if (!sqliteUpdated) {
|
|
4425
|
+
scheduleIndexFlush();
|
|
4426
|
+
}
|
|
4119
4427
|
}
|
|
4120
4428
|
async function updateLogIndexEntryMetadata(log) {
|
|
4121
4429
|
const index = await loadIndex();
|
|
@@ -4126,14 +4434,15 @@ async function updateLogIndexEntryMetadata(log) {
|
|
|
4126
4434
|
...createLogIndexEntryMetadata(log)
|
|
4127
4435
|
};
|
|
4128
4436
|
index.entries[log.id] = updated;
|
|
4129
|
-
await upsertSqliteLogIndexEntry(updated);
|
|
4130
|
-
scheduleIndexFlush();
|
|
4437
|
+
const sqliteUpdated = await upsertSqliteLogIndexEntry(updated);
|
|
4438
|
+
if (!sqliteUpdated) scheduleIndexFlush();
|
|
4131
4439
|
}
|
|
4132
4440
|
let indexFlushScheduled = false;
|
|
4133
4441
|
let indexFlushTimeout = null;
|
|
4134
4442
|
async function flushIndexAsync() {
|
|
4135
4443
|
indexFlushScheduled = false;
|
|
4136
4444
|
indexFlushTimeout = null;
|
|
4445
|
+
if (await listSqliteLogCursorEntries({}) !== null) return;
|
|
4137
4446
|
const index = await loadIndex();
|
|
4138
4447
|
await saveIndex(index);
|
|
4139
4448
|
}
|
|
@@ -4150,6 +4459,7 @@ async function flushIndex() {
|
|
|
4150
4459
|
indexFlushTimeout = null;
|
|
4151
4460
|
}
|
|
4152
4461
|
indexFlushScheduled = false;
|
|
4462
|
+
if (await listSqliteLogCursorEntries({}) !== null) return;
|
|
4153
4463
|
const index = await loadIndex();
|
|
4154
4464
|
await saveIndex(index);
|
|
4155
4465
|
}
|
|
@@ -4236,6 +4546,19 @@ function readCaptureIncompleteReasonField(entry) {
|
|
|
4236
4546
|
return null;
|
|
4237
4547
|
}
|
|
4238
4548
|
}
|
|
4549
|
+
function readStopReasonField(entry) {
|
|
4550
|
+
const value = readStringOrNullField(entry, "stopReason");
|
|
4551
|
+
switch (value) {
|
|
4552
|
+
case "end_turn":
|
|
4553
|
+
case "tool_use":
|
|
4554
|
+
case "stop":
|
|
4555
|
+
case "length":
|
|
4556
|
+
return value;
|
|
4557
|
+
case null:
|
|
4558
|
+
default:
|
|
4559
|
+
return null;
|
|
4560
|
+
}
|
|
4561
|
+
}
|
|
4239
4562
|
function readStringArrayField(entry, field) {
|
|
4240
4563
|
const desc = Object.getOwnPropertyDescriptor(entry, field);
|
|
4241
4564
|
if (desc === void 0 || !Array.isArray(desc.value)) return void 0;
|
|
@@ -4287,6 +4610,7 @@ function readLogIndexSummary(entry, id) {
|
|
|
4287
4610
|
droppedBytes: readNumberOrNullField(entry, "droppedBytes") ?? 0,
|
|
4288
4611
|
captureIncompleteReason: readCaptureIncompleteReasonField(entry),
|
|
4289
4612
|
warnings: readStringArrayField(entry, "warnings"),
|
|
4613
|
+
stopReason: readStopReasonField(entry),
|
|
4290
4614
|
error: readStringOrNullField(entry, "error")
|
|
4291
4615
|
};
|
|
4292
4616
|
}
|
|
@@ -4886,94 +5210,876 @@ function getSessionIds() {
|
|
|
4886
5210
|
function getSessionSnapshots() {
|
|
4887
5211
|
return [...sessions$1.values()].map((session) => ({ ...session }));
|
|
4888
5212
|
}
|
|
4889
|
-
const
|
|
4890
|
-
const
|
|
4891
|
-
const
|
|
4892
|
-
const
|
|
4893
|
-
|
|
4894
|
-
const
|
|
4895
|
-
const
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
})
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
}
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
}
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
5213
|
+
const MAX_REST_LOG_PAGE_SIZE = 200;
|
|
5214
|
+
const MAX_SEARCH_QUERY_CHARS = 512;
|
|
5215
|
+
const MAX_SEARCH_PROJECT_CHARS = 256;
|
|
5216
|
+
const MAX_PROVIDER_MODELS = 64;
|
|
5217
|
+
const MAX_PROVIDER_MODEL_NAME_CHARS = 256;
|
|
5218
|
+
const MAX_PROVIDER_MODEL_METADATA = 64;
|
|
5219
|
+
const MAX_SSE_CLIENTS = 32;
|
|
5220
|
+
const MAX_ACTIVE_PROVIDER_TESTS = 2;
|
|
5221
|
+
const MAX_PROVIDER_TEST_CONCURRENCY = 4;
|
|
5222
|
+
const MAX_ACTIVE_PROXY_REQUESTS = 16;
|
|
5223
|
+
const positiveInteger = number().int().positive();
|
|
5224
|
+
const RuntimeBudgetPolicySchema = object({
|
|
5225
|
+
proxy: object({
|
|
5226
|
+
requestBytes: positiveInteger,
|
|
5227
|
+
responseBytes: positiveInteger,
|
|
5228
|
+
headerBytes: positiveInteger,
|
|
5229
|
+
deadlineMs: positiveInteger,
|
|
5230
|
+
idleTimeoutMs: positiveInteger,
|
|
5231
|
+
activeRequests: positiveInteger
|
|
5232
|
+
}),
|
|
5233
|
+
control: object({
|
|
5234
|
+
requestBytes: positiveInteger,
|
|
5235
|
+
responseBytes: positiveInteger,
|
|
5236
|
+
headerBytes: positiveInteger,
|
|
5237
|
+
deadlineMs: positiveInteger,
|
|
5238
|
+
idleTimeoutMs: positiveInteger,
|
|
5239
|
+
activeRequests: positiveInteger
|
|
5240
|
+
}),
|
|
5241
|
+
upstream: object({
|
|
5242
|
+
fetchResponseBytes: positiveInteger,
|
|
5243
|
+
fetchDeadlineMs: positiveInteger,
|
|
5244
|
+
replayResponseBytes: positiveInteger,
|
|
5245
|
+
replayDeadlineMs: positiveInteger,
|
|
5246
|
+
metadataResponseBytes: positiveInteger,
|
|
5247
|
+
metadataDeadlineMs: positiveInteger,
|
|
5248
|
+
connectTimeoutMs: positiveInteger,
|
|
5249
|
+
idleTimeoutMs: positiveInteger
|
|
5250
|
+
}),
|
|
5251
|
+
pagination: object({
|
|
5252
|
+
restPageSize: positiveInteger,
|
|
5253
|
+
searchQueryChars: positiveInteger,
|
|
5254
|
+
searchProjectChars: positiveInteger
|
|
5255
|
+
}),
|
|
5256
|
+
sse: object({
|
|
5257
|
+
clients: positiveInteger,
|
|
5258
|
+
initLogs: positiveInteger,
|
|
5259
|
+
heartbeatMs: positiveInteger
|
|
5260
|
+
}),
|
|
5261
|
+
imports: object({
|
|
5262
|
+
archiveBytes: positiveInteger,
|
|
5263
|
+
entries: positiveInteger,
|
|
5264
|
+
entryBytes: positiveInteger,
|
|
5265
|
+
uncompressedBytes: positiveInteger,
|
|
5266
|
+
expansionRatio: positiveInteger
|
|
5267
|
+
}),
|
|
5268
|
+
fanOut: object({
|
|
5269
|
+
providerModels: positiveInteger,
|
|
5270
|
+
providerModelMetadata: positiveInteger,
|
|
5271
|
+
providerTestConcurrency: positiveInteger,
|
|
5272
|
+
activeProviderTests: positiveInteger
|
|
5273
|
+
}),
|
|
5274
|
+
queues: object({
|
|
5275
|
+
proxyRequests: positiveInteger,
|
|
5276
|
+
finalizerJobs: positiveInteger,
|
|
5277
|
+
rawStreamMessages: positiveInteger,
|
|
5278
|
+
rawStreamBytes: positiveInteger,
|
|
5279
|
+
backgroundWorkers: positiveInteger
|
|
5280
|
+
})
|
|
5281
|
+
}).superRefine((policy, context) => {
|
|
5282
|
+
if (policy.control.requestBytes > policy.proxy.requestBytes) {
|
|
5283
|
+
context.addIssue({
|
|
5284
|
+
code: "custom",
|
|
5285
|
+
path: ["control", "requestBytes"],
|
|
5286
|
+
message: "control request budget must not exceed proxy request budget"
|
|
5287
|
+
});
|
|
4943
5288
|
}
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
index.ids = index.ids.filter((id) => id !== log.id);
|
|
4951
|
-
}
|
|
4952
|
-
index.entriesById.delete(log.id);
|
|
4953
|
-
continue;
|
|
4954
|
-
}
|
|
4955
|
-
index.logsById.set(log.id, log);
|
|
4956
|
-
insertSortedLogId(index.ids, log.id);
|
|
5289
|
+
if (policy.control.responseBytes > policy.proxy.responseBytes) {
|
|
5290
|
+
context.addIssue({
|
|
5291
|
+
code: "custom",
|
|
5292
|
+
path: ["control", "responseBytes"],
|
|
5293
|
+
message: "control response budget must not exceed proxy response budget"
|
|
5294
|
+
});
|
|
4957
5295
|
}
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
index.entriesById.delete(id);
|
|
4965
|
-
}
|
|
4966
|
-
index.ids = index.ids.filter((id) => !ids.has(id));
|
|
5296
|
+
if (policy.control.activeRequests > policy.proxy.activeRequests) {
|
|
5297
|
+
context.addIssue({
|
|
5298
|
+
code: "custom",
|
|
5299
|
+
path: ["control", "activeRequests"],
|
|
5300
|
+
message: "control active request budget must not exceed proxy active request budget"
|
|
5301
|
+
});
|
|
4967
5302
|
}
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
5303
|
+
if (policy.upstream.fetchResponseBytes > policy.proxy.responseBytes) {
|
|
5304
|
+
context.addIssue({
|
|
5305
|
+
code: "custom",
|
|
5306
|
+
path: ["upstream", "fetchResponseBytes"],
|
|
5307
|
+
message: "upstream fetch response budget must not exceed proxy response budget"
|
|
5308
|
+
});
|
|
5309
|
+
}
|
|
5310
|
+
if (policy.upstream.replayResponseBytes > policy.proxy.responseBytes) {
|
|
5311
|
+
context.addIssue({
|
|
5312
|
+
code: "custom",
|
|
5313
|
+
path: ["upstream", "replayResponseBytes"],
|
|
5314
|
+
message: "upstream replay response budget must not exceed proxy response budget"
|
|
5315
|
+
});
|
|
5316
|
+
}
|
|
5317
|
+
if (policy.upstream.metadataResponseBytes > policy.control.responseBytes) {
|
|
5318
|
+
context.addIssue({
|
|
5319
|
+
code: "custom",
|
|
5320
|
+
path: ["upstream", "metadataResponseBytes"],
|
|
5321
|
+
message: "upstream metadata response budget must not exceed control response budget"
|
|
5322
|
+
});
|
|
5323
|
+
}
|
|
5324
|
+
if (policy.upstream.metadataDeadlineMs > policy.control.deadlineMs) {
|
|
5325
|
+
context.addIssue({
|
|
5326
|
+
code: "custom",
|
|
5327
|
+
path: ["upstream", "metadataDeadlineMs"],
|
|
5328
|
+
message: "upstream metadata deadline must not exceed control deadline"
|
|
5329
|
+
});
|
|
5330
|
+
}
|
|
5331
|
+
if (policy.queues.proxyRequests < policy.proxy.activeRequests) {
|
|
5332
|
+
context.addIssue({
|
|
5333
|
+
code: "custom",
|
|
5334
|
+
path: ["queues", "proxyRequests"],
|
|
5335
|
+
message: "proxy request queue budget must cover active proxy requests"
|
|
5336
|
+
});
|
|
5337
|
+
}
|
|
5338
|
+
});
|
|
5339
|
+
const DEFAULT_RUNTIME_BUDGET_POLICY = RuntimeBudgetPolicySchema.parse({
|
|
5340
|
+
proxy: {
|
|
5341
|
+
requestBytes: 64 * 1024 * 1024,
|
|
5342
|
+
responseBytes: 256 * 1024 * 1024,
|
|
5343
|
+
headerBytes: 64 * 1024,
|
|
5344
|
+
deadlineMs: 12e4,
|
|
5345
|
+
idleTimeoutMs: 3e4,
|
|
5346
|
+
activeRequests: MAX_ACTIVE_PROXY_REQUESTS
|
|
5347
|
+
},
|
|
5348
|
+
control: {
|
|
5349
|
+
requestBytes: 8 * 1024 * 1024,
|
|
5350
|
+
responseBytes: 64 * 1024 * 1024,
|
|
5351
|
+
headerBytes: 32 * 1024,
|
|
5352
|
+
deadlineMs: 3e4,
|
|
5353
|
+
idleTimeoutMs: 1e4,
|
|
5354
|
+
activeRequests: 8
|
|
5355
|
+
},
|
|
5356
|
+
upstream: {
|
|
5357
|
+
fetchResponseBytes: 256 * 1024 * 1024,
|
|
5358
|
+
fetchDeadlineMs: 12e4,
|
|
5359
|
+
replayResponseBytes: 256 * 1024 * 1024,
|
|
5360
|
+
replayDeadlineMs: 12e4,
|
|
5361
|
+
metadataResponseBytes: 10 * 1024 * 1024,
|
|
5362
|
+
metadataDeadlineMs: 3e4,
|
|
5363
|
+
connectTimeoutMs: 1e4,
|
|
5364
|
+
idleTimeoutMs: 3e4
|
|
5365
|
+
},
|
|
5366
|
+
pagination: {
|
|
5367
|
+
restPageSize: MAX_REST_LOG_PAGE_SIZE,
|
|
5368
|
+
searchQueryChars: MAX_SEARCH_QUERY_CHARS,
|
|
5369
|
+
searchProjectChars: MAX_SEARCH_PROJECT_CHARS
|
|
5370
|
+
},
|
|
5371
|
+
sse: {
|
|
5372
|
+
clients: MAX_SSE_CLIENTS,
|
|
5373
|
+
initLogs: 500,
|
|
5374
|
+
heartbeatMs: 15e3
|
|
5375
|
+
},
|
|
5376
|
+
imports: {
|
|
5377
|
+
archiveBytes: 64 * 1024 * 1024,
|
|
5378
|
+
entries: 5e3,
|
|
5379
|
+
entryBytes: 32 * 1024 * 1024,
|
|
5380
|
+
uncompressedBytes: 256 * 1024 * 1024,
|
|
5381
|
+
expansionRatio: 20
|
|
5382
|
+
},
|
|
5383
|
+
fanOut: {
|
|
5384
|
+
providerModels: MAX_PROVIDER_MODELS,
|
|
5385
|
+
providerModelMetadata: MAX_PROVIDER_MODEL_METADATA,
|
|
5386
|
+
providerTestConcurrency: MAX_PROVIDER_TEST_CONCURRENCY,
|
|
5387
|
+
activeProviderTests: MAX_ACTIVE_PROVIDER_TESTS
|
|
5388
|
+
},
|
|
5389
|
+
queues: {
|
|
5390
|
+
proxyRequests: MAX_ACTIVE_PROXY_REQUESTS,
|
|
5391
|
+
finalizerJobs: 1e3,
|
|
5392
|
+
rawStreamMessages: 16384,
|
|
5393
|
+
rawStreamBytes: 64 * 1024 * 1024,
|
|
5394
|
+
backgroundWorkers: 4
|
|
5395
|
+
}
|
|
5396
|
+
});
|
|
5397
|
+
const ResourceLimitErrorCodeSchema = _enum([
|
|
5398
|
+
"invalid-page-request",
|
|
5399
|
+
"search-limit-exceeded",
|
|
5400
|
+
"provider-model-limit-exceeded",
|
|
5401
|
+
"sse-capacity-exceeded",
|
|
5402
|
+
"provider-test-capacity-exceeded",
|
|
5403
|
+
"proxy-capacity-exceeded"
|
|
5404
|
+
]);
|
|
5405
|
+
const ResourceLimitStatusSchema = union([
|
|
5406
|
+
literal(400),
|
|
5407
|
+
literal(413),
|
|
5408
|
+
literal(429),
|
|
5409
|
+
literal(503)
|
|
5410
|
+
]);
|
|
5411
|
+
const ResourceLimitErrorSchema = object({
|
|
5412
|
+
error: string().min(1).max(300),
|
|
5413
|
+
code: ResourceLimitErrorCodeSchema,
|
|
5414
|
+
status: ResourceLimitStatusSchema,
|
|
5415
|
+
resource: string().min(1).max(80),
|
|
5416
|
+
limit: number().int().positive(),
|
|
5417
|
+
retryable: boolean()
|
|
5418
|
+
});
|
|
5419
|
+
function createResourceLimitError(input) {
|
|
5420
|
+
return ResourceLimitErrorSchema.parse(input);
|
|
5421
|
+
}
|
|
5422
|
+
function resourceLimitResponse(input) {
|
|
5423
|
+
const body = createResourceLimitError(input);
|
|
5424
|
+
const headers = new Headers({ "cache-control": "no-store" });
|
|
5425
|
+
if (input.retryAfterSeconds !== void 0) {
|
|
5426
|
+
headers.set("retry-after", String(input.retryAfterSeconds));
|
|
5427
|
+
}
|
|
5428
|
+
return Response.json(body, { status: input.status, headers });
|
|
5429
|
+
}
|
|
5430
|
+
function parseBoundedIntegerParameter(input) {
|
|
5431
|
+
if (input.value === null) return { ok: true, value: input.fallback };
|
|
5432
|
+
const parsed = Number(input.value);
|
|
5433
|
+
if (!Number.isSafeInteger(parsed) || parsed < input.minimum || parsed > input.maximum) {
|
|
5434
|
+
return {
|
|
5435
|
+
ok: false,
|
|
5436
|
+
response: resourceLimitResponse({
|
|
5437
|
+
error: `Query parameter "${input.parameter}" must be an integer between ${String(input.minimum)} and ${String(input.maximum)}.`,
|
|
5438
|
+
code: "invalid-page-request",
|
|
5439
|
+
status: 400,
|
|
5440
|
+
resource: input.resource,
|
|
5441
|
+
limit: input.maximum,
|
|
5442
|
+
retryable: false
|
|
5443
|
+
})
|
|
5444
|
+
};
|
|
5445
|
+
}
|
|
5446
|
+
return { ok: true, value: parsed };
|
|
5447
|
+
}
|
|
5448
|
+
function createCapacityLimiter(maxActive) {
|
|
5449
|
+
const capacity = Number.isSafeInteger(maxActive) && maxActive > 0 ? maxActive : 1;
|
|
5450
|
+
let active = 0;
|
|
5451
|
+
return {
|
|
5452
|
+
capacity,
|
|
5453
|
+
activeCount: () => active,
|
|
5454
|
+
tryAcquire: () => {
|
|
5455
|
+
if (active >= capacity) return null;
|
|
5456
|
+
active += 1;
|
|
5457
|
+
let released = false;
|
|
5458
|
+
return {
|
|
5459
|
+
release: () => {
|
|
5460
|
+
if (released) return;
|
|
5461
|
+
released = true;
|
|
5462
|
+
active = Math.max(0, active - 1);
|
|
5463
|
+
}
|
|
5464
|
+
};
|
|
5465
|
+
}
|
|
5466
|
+
};
|
|
5467
|
+
}
|
|
5468
|
+
async function runWithConcurrencyLimit(tasks2, maxConcurrent) {
|
|
5469
|
+
if (tasks2.length === 0) return;
|
|
5470
|
+
const concurrency = Number.isSafeInteger(maxConcurrent) && maxConcurrent > 0 ? maxConcurrent : 1;
|
|
5471
|
+
const workerCount = Math.min(concurrency, tasks2.length);
|
|
5472
|
+
let nextIndex = 0;
|
|
5473
|
+
const runWorker = async () => {
|
|
5474
|
+
while (nextIndex < tasks2.length) {
|
|
5475
|
+
const index = nextIndex;
|
|
5476
|
+
nextIndex += 1;
|
|
5477
|
+
const task = tasks2[index];
|
|
5478
|
+
if (task !== void 0) await task();
|
|
5479
|
+
}
|
|
5480
|
+
};
|
|
5481
|
+
await Promise.all(Array.from({ length: workerCount }, runWorker));
|
|
5482
|
+
}
|
|
5483
|
+
const DEFAULT_MEMORY_LIMIT_BYTES = 1024 * 1024;
|
|
5484
|
+
const FILE_WRITE_BATCH_BYTES = 64 * 1024;
|
|
5485
|
+
const DEFAULT_QUEUE_MAX_MESSAGES = 256;
|
|
5486
|
+
const DEFAULT_QUEUE_MAX_BYTES = 8 * 1024 * 1024;
|
|
5487
|
+
const DEFAULT_GLOBAL_QUEUE_MAX_MESSAGES = 4096;
|
|
5488
|
+
const DEFAULT_GLOBAL_QUEUE_MAX_BYTES = 64 * 1024 * 1024;
|
|
5489
|
+
const activeCaptures = /* @__PURE__ */ new Set();
|
|
5490
|
+
function resolveMemoryLimitBytes() {
|
|
5491
|
+
const raw = process.env["AGENT_INSPECTOR_STREAM_MEMORY_LIMIT_BYTES"];
|
|
5492
|
+
if (raw === void 0 || raw === "") return DEFAULT_MEMORY_LIMIT_BYTES;
|
|
5493
|
+
const parsed = Number(raw);
|
|
5494
|
+
if (!Number.isInteger(parsed) || parsed < 0) return DEFAULT_MEMORY_LIMIT_BYTES;
|
|
5495
|
+
return parsed;
|
|
5496
|
+
}
|
|
5497
|
+
function resolvePositiveIntegerEnv(name2, fallback) {
|
|
5498
|
+
const raw = process.env[name2];
|
|
5499
|
+
if (raw === void 0 || raw === "") return fallback;
|
|
5500
|
+
const parsed = Number(raw);
|
|
5501
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) return fallback;
|
|
5502
|
+
return parsed;
|
|
5503
|
+
}
|
|
5504
|
+
function defaultQueueMaxMessages() {
|
|
5505
|
+
return resolvePositiveIntegerEnv(
|
|
5506
|
+
"AGENT_INSPECTOR_OBSERVATION_QUEUE_MESSAGES",
|
|
5507
|
+
DEFAULT_QUEUE_MAX_MESSAGES
|
|
5508
|
+
);
|
|
5509
|
+
}
|
|
5510
|
+
function defaultQueueMaxBytes() {
|
|
5511
|
+
return resolvePositiveIntegerEnv(
|
|
5512
|
+
"AGENT_INSPECTOR_OBSERVATION_QUEUE_BYTES",
|
|
5513
|
+
DEFAULT_QUEUE_MAX_BYTES
|
|
5514
|
+
);
|
|
5515
|
+
}
|
|
5516
|
+
class RawStreamObservationBudget {
|
|
5517
|
+
constructor(maxMessages, maxBytes) {
|
|
5518
|
+
this.maxMessages = maxMessages;
|
|
5519
|
+
this.maxBytes = maxBytes;
|
|
5520
|
+
}
|
|
5521
|
+
maxMessages;
|
|
5522
|
+
maxBytes;
|
|
5523
|
+
messages = 0;
|
|
5524
|
+
bytes = 0;
|
|
5525
|
+
tryAcquire(bytes) {
|
|
5526
|
+
if (this.messages + 1 > this.maxMessages || this.bytes + bytes > this.maxBytes) return null;
|
|
5527
|
+
this.messages += 1;
|
|
5528
|
+
this.bytes += bytes;
|
|
5529
|
+
let released = false;
|
|
5530
|
+
return () => {
|
|
5531
|
+
if (released) return;
|
|
5532
|
+
released = true;
|
|
5533
|
+
this.messages -= 1;
|
|
5534
|
+
this.bytes -= bytes;
|
|
5535
|
+
};
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5538
|
+
const globalObservationBudget = new RawStreamObservationBudget(
|
|
5539
|
+
resolvePositiveIntegerEnv(
|
|
5540
|
+
"AGENT_INSPECTOR_OBSERVATION_GLOBAL_QUEUE_MESSAGES",
|
|
5541
|
+
DEFAULT_GLOBAL_QUEUE_MAX_MESSAGES
|
|
5542
|
+
),
|
|
5543
|
+
resolvePositiveIntegerEnv(
|
|
5544
|
+
"AGENT_INSPECTOR_OBSERVATION_GLOBAL_QUEUE_BYTES",
|
|
5545
|
+
DEFAULT_GLOBAL_QUEUE_MAX_BYTES
|
|
5546
|
+
)
|
|
5547
|
+
);
|
|
5548
|
+
function getRawStreamDir() {
|
|
5549
|
+
return join(getDataDir(), "raw-streams");
|
|
5550
|
+
}
|
|
5551
|
+
function createRawStreamPath() {
|
|
5552
|
+
return join(getRawStreamDir(), `${Date.now()}-${randomUUID()}.stream.tmp`);
|
|
5553
|
+
}
|
|
5554
|
+
class RawStreamCapture {
|
|
5555
|
+
memoryLimitBytes;
|
|
5556
|
+
queueMaxMessages;
|
|
5557
|
+
queueMaxBytes;
|
|
5558
|
+
globalBudget;
|
|
5559
|
+
chunks = [];
|
|
5560
|
+
byteLength = 0;
|
|
5561
|
+
filePath = null;
|
|
5562
|
+
fileHandle = null;
|
|
5563
|
+
pendingFileChunks = [];
|
|
5564
|
+
pendingFileBytes = 0;
|
|
5565
|
+
operation = Promise.resolve();
|
|
5566
|
+
accepting = true;
|
|
5567
|
+
closed = false;
|
|
5568
|
+
sawChunk = false;
|
|
5569
|
+
queuedMessages = 0;
|
|
5570
|
+
queuedBytes = 0;
|
|
5571
|
+
incomplete = false;
|
|
5572
|
+
storageFailed = false;
|
|
5573
|
+
droppedChunks = 0;
|
|
5574
|
+
droppedBytes = 0;
|
|
5575
|
+
gapReason = null;
|
|
5576
|
+
constructor(options = {}) {
|
|
5577
|
+
this.memoryLimitBytes = options.memoryLimitBytes ?? resolveMemoryLimitBytes();
|
|
5578
|
+
this.queueMaxMessages = options.queueMaxMessages ?? defaultQueueMaxMessages();
|
|
5579
|
+
this.queueMaxBytes = options.queueMaxBytes ?? defaultQueueMaxBytes();
|
|
5580
|
+
this.globalBudget = options.globalBudget ?? globalObservationBudget;
|
|
5581
|
+
activeCaptures.add(this);
|
|
5582
|
+
}
|
|
5583
|
+
get hasChunks() {
|
|
5584
|
+
return this.sawChunk;
|
|
5585
|
+
}
|
|
5586
|
+
get status() {
|
|
5587
|
+
return {
|
|
5588
|
+
incomplete: this.incomplete,
|
|
5589
|
+
droppedChunks: this.droppedChunks,
|
|
5590
|
+
droppedBytes: this.droppedBytes,
|
|
5591
|
+
reason: this.gapReason
|
|
5592
|
+
};
|
|
5593
|
+
}
|
|
5594
|
+
recordDroppedObservation(reason, observedBytes) {
|
|
5595
|
+
const safeObservedBytes = Number.isSafeInteger(observedBytes) && observedBytes >= 0 ? observedBytes : 0;
|
|
5596
|
+
this.recordGap(reason, safeObservedBytes, safeObservedBytes > 0);
|
|
5597
|
+
}
|
|
5598
|
+
async append(text) {
|
|
5599
|
+
if (text === "") return;
|
|
5600
|
+
const next = this.operation.then(() => this.appendInternal(text));
|
|
5601
|
+
this.operation = next.catch(() => void 0);
|
|
5602
|
+
return next;
|
|
5603
|
+
}
|
|
5604
|
+
/**
|
|
5605
|
+
* Submit an observation without waiting for file I/O. Once capacity is exceeded, capture remains
|
|
5606
|
+
* incomplete and all later chunks are counted as dropped so the persisted value is an honest
|
|
5607
|
+
* prefix rather than a misleading stream with an unmarked hole.
|
|
5608
|
+
*/
|
|
5609
|
+
tryAppend(text, observedBytes) {
|
|
5610
|
+
const safeObservedBytes = Number.isSafeInteger(observedBytes) && observedBytes >= 0 ? observedBytes : 0;
|
|
5611
|
+
const queuedBytes = Math.max(safeObservedBytes, Buffer.byteLength(text, "utf-8"));
|
|
5612
|
+
if (!this.accepting || this.closed) {
|
|
5613
|
+
this.recordGap("pipeline-closed", safeObservedBytes, safeObservedBytes > 0 || text !== "");
|
|
5614
|
+
return false;
|
|
5615
|
+
}
|
|
5616
|
+
if (this.incomplete) {
|
|
5617
|
+
this.recordGap("capacity-exceeded", safeObservedBytes, safeObservedBytes > 0 || text !== "");
|
|
5618
|
+
return false;
|
|
5619
|
+
}
|
|
5620
|
+
if (text === "") return true;
|
|
5621
|
+
if (this.queuedMessages + 1 > this.queueMaxMessages || this.queuedBytes + queuedBytes > this.queueMaxBytes) {
|
|
5622
|
+
this.recordGap("capacity-exceeded", safeObservedBytes, true);
|
|
5623
|
+
return false;
|
|
5624
|
+
}
|
|
5625
|
+
const releaseGlobal = this.globalBudget.tryAcquire(queuedBytes);
|
|
5626
|
+
if (releaseGlobal === null) {
|
|
5627
|
+
this.recordGap("capacity-exceeded", safeObservedBytes, true);
|
|
5628
|
+
return false;
|
|
5629
|
+
}
|
|
5630
|
+
this.queuedMessages += 1;
|
|
5631
|
+
this.queuedBytes += queuedBytes;
|
|
5632
|
+
this.operation = this.operation.then(async () => {
|
|
5633
|
+
if (this.storageFailed) {
|
|
5634
|
+
this.recordGap("write-failed", safeObservedBytes, true);
|
|
5635
|
+
return;
|
|
5636
|
+
}
|
|
5637
|
+
await this.appendInternal(text);
|
|
5638
|
+
}).catch(() => {
|
|
5639
|
+
this.storageFailed = true;
|
|
5640
|
+
this.recordGap("write-failed", safeObservedBytes, true);
|
|
5641
|
+
}).finally(() => {
|
|
5642
|
+
this.queuedMessages -= 1;
|
|
5643
|
+
this.queuedBytes -= queuedBytes;
|
|
5644
|
+
releaseGlobal();
|
|
5645
|
+
});
|
|
5646
|
+
return true;
|
|
5647
|
+
}
|
|
5648
|
+
async finalize() {
|
|
5649
|
+
this.accepting = false;
|
|
5650
|
+
const next = this.operation.then(() => this.finalizeInternal());
|
|
5651
|
+
this.operation = next.then(
|
|
5652
|
+
() => void 0,
|
|
5653
|
+
() => void 0
|
|
5654
|
+
);
|
|
5655
|
+
return next.finally(() => activeCaptures.delete(this));
|
|
5656
|
+
}
|
|
5657
|
+
async appendInternal(text) {
|
|
5658
|
+
if (this.closed) return;
|
|
5659
|
+
this.sawChunk = true;
|
|
5660
|
+
const nextBytes = Buffer.byteLength(text, "utf-8");
|
|
5661
|
+
if (this.filePath === null && this.byteLength + nextBytes <= this.memoryLimitBytes) {
|
|
5662
|
+
this.chunks.push(text);
|
|
5663
|
+
this.byteLength += nextBytes;
|
|
5664
|
+
return;
|
|
5665
|
+
}
|
|
5666
|
+
if (this.filePath === null) {
|
|
5667
|
+
await this.spillToFile();
|
|
5668
|
+
}
|
|
5669
|
+
if (this.filePath !== null) {
|
|
5670
|
+
this.pendingFileChunks.push(text);
|
|
5671
|
+
this.pendingFileBytes += nextBytes;
|
|
5672
|
+
this.byteLength += nextBytes;
|
|
5673
|
+
if (this.pendingFileBytes >= FILE_WRITE_BATCH_BYTES) await this.flushFileBuffer();
|
|
5674
|
+
}
|
|
5675
|
+
}
|
|
5676
|
+
recordGap(reason, observedBytes, countChunk) {
|
|
5677
|
+
this.incomplete = true;
|
|
5678
|
+
this.gapReason ??= reason;
|
|
5679
|
+
if (countChunk) this.droppedChunks += 1;
|
|
5680
|
+
this.droppedBytes += observedBytes;
|
|
5681
|
+
}
|
|
5682
|
+
async finalizeInternal() {
|
|
5683
|
+
if (!this.closed) {
|
|
5684
|
+
this.closed = true;
|
|
5685
|
+
await this.flushFileBuffer();
|
|
5686
|
+
if (this.fileHandle !== null) {
|
|
5687
|
+
const handle = this.fileHandle;
|
|
5688
|
+
this.fileHandle = null;
|
|
5689
|
+
try {
|
|
5690
|
+
await handle.sync();
|
|
5691
|
+
} finally {
|
|
5692
|
+
await handle.close();
|
|
5693
|
+
}
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
if (this.filePath !== null) {
|
|
5697
|
+
return { type: "file", path: this.filePath };
|
|
5698
|
+
}
|
|
5699
|
+
return { type: "memory", rawStream: this.chunks.join("") };
|
|
5700
|
+
}
|
|
5701
|
+
async spillToFile() {
|
|
5702
|
+
const path2 = createRawStreamPath();
|
|
5703
|
+
assert(ensurePrivateDirectorySync(getRawStreamDir()), "Could not secure raw-stream directory");
|
|
5704
|
+
const handle = await open(path2, "wx", PRIVATE_FILE_MODE);
|
|
5705
|
+
let committed = false;
|
|
5706
|
+
try {
|
|
5707
|
+
const memoryText = this.chunks.join("");
|
|
5708
|
+
if (memoryText.length > 0) await handle.write(memoryText, void 0, "utf-8");
|
|
5709
|
+
assert(securePrivateFileSync(path2), "Could not secure raw-stream file");
|
|
5710
|
+
this.filePath = path2;
|
|
5711
|
+
this.fileHandle = handle;
|
|
5712
|
+
this.chunks = [];
|
|
5713
|
+
committed = true;
|
|
5714
|
+
} finally {
|
|
5715
|
+
if (!committed) {
|
|
5716
|
+
await handle.close().catch(() => void 0);
|
|
5717
|
+
await unlink(path2).catch(() => void 0);
|
|
5718
|
+
}
|
|
5719
|
+
}
|
|
5720
|
+
}
|
|
5721
|
+
async flushFileBuffer() {
|
|
5722
|
+
if (this.fileHandle === null || this.pendingFileChunks.length === 0) return;
|
|
5723
|
+
const text = this.pendingFileChunks.join("");
|
|
5724
|
+
this.pendingFileChunks = [];
|
|
5725
|
+
this.pendingFileBytes = 0;
|
|
5726
|
+
await this.fileHandle.write(text, void 0, "utf-8");
|
|
5727
|
+
}
|
|
5728
|
+
}
|
|
5729
|
+
async function flushAllRawStreamCaptures() {
|
|
5730
|
+
await Promise.all([...activeCaptures].map((capture) => capture.finalize()));
|
|
5731
|
+
}
|
|
5732
|
+
const MAX_INVENTORY_DEPTH = 8;
|
|
5733
|
+
const DAY_MS = 24 * 60 * 60 * 1e3;
|
|
5734
|
+
const DEFAULT_RETENTION_DAYS = 7;
|
|
5735
|
+
const DEFAULT_MAX_TOTAL_BYTES = 2 * 1024 * 1024 * 1024;
|
|
5736
|
+
let lastStorageLifecycleGcResult = null;
|
|
5737
|
+
function emptyStats() {
|
|
5738
|
+
return {
|
|
5739
|
+
fileCount: 0,
|
|
5740
|
+
bytes: 0,
|
|
5741
|
+
oldestModifiedMs: null,
|
|
5742
|
+
newestModifiedMs: null
|
|
5743
|
+
};
|
|
5744
|
+
}
|
|
5745
|
+
function recordFile(stats, bytes, modifiedMs) {
|
|
5746
|
+
stats.fileCount += 1;
|
|
5747
|
+
stats.bytes += bytes;
|
|
5748
|
+
stats.oldestModifiedMs = stats.oldestModifiedMs === null ? modifiedMs : Math.min(stats.oldestModifiedMs, modifiedMs);
|
|
5749
|
+
stats.newestModifiedMs = stats.newestModifiedMs === null ? modifiedMs : Math.max(stats.newestModifiedMs, modifiedMs);
|
|
5750
|
+
}
|
|
5751
|
+
function isoFromMs(value) {
|
|
5752
|
+
return value === null ? null : new Date(value).toISOString();
|
|
5753
|
+
}
|
|
5754
|
+
function isPathInsideRoot(root, path2) {
|
|
5755
|
+
const relativePath = relative(resolve(root), resolve(path2));
|
|
5756
|
+
return relativePath.length === 0 || !relativePath.startsWith("..") && !relativePath.startsWith("/") && !relativePath.startsWith("\\");
|
|
5757
|
+
}
|
|
5758
|
+
function collectRuleFiles(rule) {
|
|
5759
|
+
const files = [];
|
|
5760
|
+
if (!existsSync(rule.root)) return files;
|
|
5761
|
+
const visit = (currentDir, depth) => {
|
|
5762
|
+
if (depth > MAX_INVENTORY_DEPTH) return;
|
|
5763
|
+
let entries;
|
|
5764
|
+
try {
|
|
5765
|
+
entries = readdirSync(currentDir);
|
|
5766
|
+
} catch {
|
|
5767
|
+
return;
|
|
5768
|
+
}
|
|
5769
|
+
for (const entry of entries) {
|
|
5770
|
+
const path2 = join(currentDir, entry);
|
|
5771
|
+
if (!isPathInsideRoot(rule.root, path2)) continue;
|
|
5772
|
+
try {
|
|
5773
|
+
const fileStats = lstatSync(path2);
|
|
5774
|
+
if (fileStats.isDirectory()) {
|
|
5775
|
+
visit(path2, depth + 1);
|
|
5776
|
+
continue;
|
|
5777
|
+
}
|
|
5778
|
+
if (!fileStats.isFile() || !rule.shouldCount(entry, path2)) continue;
|
|
5779
|
+
files.push({ rule, path: path2, bytes: fileStats.size, modifiedMs: fileStats.mtimeMs });
|
|
5780
|
+
} catch {
|
|
5781
|
+
}
|
|
5782
|
+
}
|
|
5783
|
+
};
|
|
5784
|
+
visit(rule.root, 0);
|
|
5785
|
+
return files;
|
|
5786
|
+
}
|
|
5787
|
+
function collectRuleStats(rule) {
|
|
5788
|
+
const stats = emptyStats();
|
|
5789
|
+
for (const file of collectRuleFiles(rule)) {
|
|
5790
|
+
recordFile(stats, file.bytes, file.modifiedMs);
|
|
5791
|
+
}
|
|
5792
|
+
return stats;
|
|
5793
|
+
}
|
|
5794
|
+
function isAutoDeletableCleanupBehavior(behavior) {
|
|
5795
|
+
switch (behavior) {
|
|
5796
|
+
case "retention-gc":
|
|
5797
|
+
case "orphan-reclaim":
|
|
5798
|
+
return true;
|
|
5799
|
+
case "rotation":
|
|
5800
|
+
case "manual-owner-delete":
|
|
5801
|
+
return false;
|
|
5802
|
+
}
|
|
5803
|
+
}
|
|
5804
|
+
function summarizeRule(rule) {
|
|
5805
|
+
const stats = collectRuleStats(rule);
|
|
5806
|
+
return {
|
|
5807
|
+
kind: rule.kind,
|
|
5808
|
+
root: rule.root,
|
|
5809
|
+
sensitivity: rule.sensitivity,
|
|
5810
|
+
cleanupBehavior: rule.cleanupBehavior,
|
|
5811
|
+
fileCount: stats.fileCount,
|
|
5812
|
+
bytes: stats.bytes,
|
|
5813
|
+
oldestModifiedAt: isoFromMs(stats.oldestModifiedMs),
|
|
5814
|
+
newestModifiedAt: isoFromMs(stats.newestModifiedMs)
|
|
5815
|
+
};
|
|
5816
|
+
}
|
|
5817
|
+
function isSessionArchiveFile(fileName) {
|
|
5818
|
+
return fileName === "session.sqlite" || fileName.startsWith("session.sqlite-");
|
|
5819
|
+
}
|
|
5820
|
+
function isSqliteLogIndexFile(fileName) {
|
|
5821
|
+
return fileName === "inspector.sqlite" || fileName.startsWith("inspector.sqlite-");
|
|
5822
|
+
}
|
|
5823
|
+
function isBackupFile(fileName) {
|
|
5824
|
+
return fileName.endsWith(".bak") || fileName.endsWith(".backup");
|
|
5825
|
+
}
|
|
5826
|
+
function applicationLogMatcher(logPath) {
|
|
5827
|
+
const directory = dirname(logPath);
|
|
5828
|
+
const baseName = basename(logPath);
|
|
5829
|
+
return (fileName, path2) => {
|
|
5830
|
+
if (dirname(path2) !== directory) return false;
|
|
5831
|
+
if (fileName === baseName) return true;
|
|
5832
|
+
return fileName.startsWith(`${baseName}.`) && /^\d+$/.test(fileName.slice(baseName.length + 1));
|
|
5833
|
+
};
|
|
5834
|
+
}
|
|
5835
|
+
function parsePositiveIntegerEnv(name2, fallback) {
|
|
5836
|
+
const value = process.env[name2];
|
|
5837
|
+
if (value === void 0) return fallback;
|
|
5838
|
+
const parsed = Number.parseInt(value, 10);
|
|
5839
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
|
|
5840
|
+
return parsed;
|
|
5841
|
+
}
|
|
5842
|
+
function resolveStorageLifecycleQuotaPolicy() {
|
|
5843
|
+
const retentionDays = parsePositiveIntegerEnv(
|
|
5844
|
+
"AGENT_INSPECTOR_STORAGE_RETENTION_DAYS",
|
|
5845
|
+
parsePositiveIntegerEnv("LOG_RETENTION_DAYS", DEFAULT_RETENTION_DAYS)
|
|
5846
|
+
);
|
|
5847
|
+
return {
|
|
5848
|
+
maxAgeMs: retentionDays * DAY_MS,
|
|
5849
|
+
maxTotalBytes: parsePositiveIntegerEnv(
|
|
5850
|
+
"AGENT_INSPECTOR_STORAGE_MAX_BYTES",
|
|
5851
|
+
DEFAULT_MAX_TOTAL_BYTES
|
|
5852
|
+
)
|
|
5853
|
+
};
|
|
5854
|
+
}
|
|
5855
|
+
function getStorageLifecycleInventoryRules() {
|
|
5856
|
+
const applicationLogPath = resolveApplicationLogPath();
|
|
5857
|
+
return [
|
|
5858
|
+
{
|
|
5859
|
+
kind: "captured-log-jsonl",
|
|
5860
|
+
root: resolveLogDir(),
|
|
5861
|
+
sensitivity: "captured-data",
|
|
5862
|
+
cleanupBehavior: "retention-gc",
|
|
5863
|
+
shouldCount: (fileName) => fileName.endsWith(".jsonl")
|
|
5864
|
+
},
|
|
5865
|
+
{
|
|
5866
|
+
kind: "sqlite-log-index",
|
|
5867
|
+
root: resolveLogDir(),
|
|
5868
|
+
sensitivity: "captured-data",
|
|
5869
|
+
cleanupBehavior: "orphan-reclaim",
|
|
5870
|
+
shouldCount: isSqliteLogIndexFile
|
|
5871
|
+
},
|
|
5872
|
+
{
|
|
5873
|
+
kind: "session-archive",
|
|
5874
|
+
root: getSessionArchiveRoot(),
|
|
5875
|
+
sensitivity: "captured-data",
|
|
5876
|
+
cleanupBehavior: "retention-gc",
|
|
5877
|
+
shouldCount: isSessionArchiveFile
|
|
5878
|
+
},
|
|
5879
|
+
{
|
|
5880
|
+
kind: "streaming-chunk",
|
|
5881
|
+
root: getChunksDir(),
|
|
5882
|
+
sensitivity: "captured-data",
|
|
5883
|
+
cleanupBehavior: "retention-gc",
|
|
5884
|
+
shouldCount: (fileName) => fileName.endsWith(".json")
|
|
5885
|
+
},
|
|
5886
|
+
{
|
|
5887
|
+
kind: "raw-stream-temp",
|
|
5888
|
+
root: getRawStreamDir(),
|
|
5889
|
+
sensitivity: "captured-data",
|
|
5890
|
+
cleanupBehavior: "orphan-reclaim",
|
|
5891
|
+
shouldCount: () => true
|
|
5892
|
+
},
|
|
5893
|
+
{
|
|
5894
|
+
kind: "application-log",
|
|
5895
|
+
root: dirname(applicationLogPath),
|
|
5896
|
+
sensitivity: "operational",
|
|
5897
|
+
cleanupBehavior: "rotation",
|
|
5898
|
+
shouldCount: applicationLogMatcher(applicationLogPath)
|
|
5899
|
+
},
|
|
5900
|
+
{
|
|
5901
|
+
kind: "evidence",
|
|
5902
|
+
root: join(getDataDir(), "evidence"),
|
|
5903
|
+
sensitivity: "captured-data",
|
|
5904
|
+
cleanupBehavior: "manual-owner-delete",
|
|
5905
|
+
shouldCount: () => true
|
|
5906
|
+
},
|
|
5907
|
+
{
|
|
5908
|
+
kind: "backup",
|
|
5909
|
+
root: getDataDir(),
|
|
5910
|
+
sensitivity: "secret-adjacent",
|
|
5911
|
+
cleanupBehavior: "orphan-reclaim",
|
|
5912
|
+
shouldCount: isBackupFile
|
|
5913
|
+
}
|
|
5914
|
+
];
|
|
5915
|
+
}
|
|
5916
|
+
function getStorageLifecycleInventory(rules = getStorageLifecycleInventoryRules()) {
|
|
5917
|
+
const artifacts = rules.map(summarizeRule);
|
|
5918
|
+
return {
|
|
5919
|
+
checkedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5920
|
+
totalFiles: artifacts.reduce((sum, artifact) => sum + artifact.fileCount, 0),
|
|
5921
|
+
totalBytes: artifacts.reduce((sum, artifact) => sum + artifact.bytes, 0),
|
|
5922
|
+
artifacts
|
|
5923
|
+
};
|
|
5924
|
+
}
|
|
5925
|
+
function collectLifecycleFiles(rules = getStorageLifecycleInventoryRules()) {
|
|
5926
|
+
return rules.flatMap((rule) => collectRuleFiles(rule));
|
|
5927
|
+
}
|
|
5928
|
+
function selectGcCandidates(files, policy, nowMs) {
|
|
5929
|
+
const autoDeletableFiles = files.filter(
|
|
5930
|
+
(file) => isAutoDeletableCleanupBehavior(file.rule.cleanupBehavior)
|
|
5931
|
+
);
|
|
5932
|
+
const expiredFiles = autoDeletableFiles.filter(
|
|
5933
|
+
(file) => nowMs - file.modifiedMs > policy.maxAgeMs
|
|
5934
|
+
);
|
|
5935
|
+
const expiredPaths = new Set(expiredFiles.map((file) => file.path));
|
|
5936
|
+
const candidates2 = [...expiredFiles];
|
|
5937
|
+
let totalBytes = files.reduce((sum, file) => sum + file.bytes, 0) - expiredFiles.reduce((sum, file) => sum + file.bytes, 0);
|
|
5938
|
+
if (totalBytes <= policy.maxTotalBytes) return candidates2;
|
|
5939
|
+
const quotaCandidates = autoDeletableFiles.filter((file) => !expiredPaths.has(file.path)).sort((a, b) => a.modifiedMs - b.modifiedMs);
|
|
5940
|
+
for (const file of quotaCandidates) {
|
|
5941
|
+
if (totalBytes <= policy.maxTotalBytes) break;
|
|
5942
|
+
candidates2.push(file);
|
|
5943
|
+
totalBytes -= file.bytes;
|
|
5944
|
+
}
|
|
5945
|
+
return candidates2;
|
|
5946
|
+
}
|
|
5947
|
+
function deleteLifecycleFile(file) {
|
|
5948
|
+
if (!isPathInsideRoot(file.rule.root, file.path)) {
|
|
5949
|
+
return { kind: file.rule.kind, path: file.path, message: "path outside lifecycle root" };
|
|
5950
|
+
}
|
|
5951
|
+
try {
|
|
5952
|
+
unlinkSync(file.path);
|
|
5953
|
+
return null;
|
|
5954
|
+
} catch (err) {
|
|
5955
|
+
if (err instanceof Error && err.message.includes("ENOENT")) return null;
|
|
5956
|
+
return { kind: file.rule.kind, path: file.path, message: String(err) };
|
|
5957
|
+
}
|
|
5958
|
+
}
|
|
5959
|
+
function runStorageLifecycleGc(policy = resolveStorageLifecycleQuotaPolicy(), rules = getStorageLifecycleInventoryRules(), nowMs = Date.now()) {
|
|
5960
|
+
const before = getStorageLifecycleInventory(rules);
|
|
5961
|
+
const files = collectLifecycleFiles(rules);
|
|
5962
|
+
const candidates2 = selectGcCandidates(files, policy, nowMs);
|
|
5963
|
+
const errors = [];
|
|
5964
|
+
let filesDeleted = 0;
|
|
5965
|
+
let bytesDeleted = 0;
|
|
5966
|
+
for (const file of candidates2) {
|
|
5967
|
+
const error = deleteLifecycleFile(file);
|
|
5968
|
+
if (error !== null) {
|
|
5969
|
+
errors.push(error);
|
|
5970
|
+
continue;
|
|
5971
|
+
}
|
|
5972
|
+
filesDeleted += 1;
|
|
5973
|
+
bytesDeleted += file.bytes;
|
|
5974
|
+
}
|
|
5975
|
+
const after = getStorageLifecycleInventory(rules);
|
|
5976
|
+
const autoDeletableBytes = after.artifacts.filter((artifact) => isAutoDeletableCleanupBehavior(artifact.cleanupBehavior)).reduce((sum, artifact) => sum + artifact.bytes, 0);
|
|
5977
|
+
const result = {
|
|
5978
|
+
checkedAt: new Date(nowMs).toISOString(),
|
|
5979
|
+
policy,
|
|
5980
|
+
before,
|
|
5981
|
+
after,
|
|
5982
|
+
filesScanned: files.length,
|
|
5983
|
+
bytesScanned: files.reduce((sum, file) => sum + file.bytes, 0),
|
|
5984
|
+
filesDeleted,
|
|
5985
|
+
bytesDeleted,
|
|
5986
|
+
reclaimableBytes: autoDeletableBytes,
|
|
5987
|
+
errors
|
|
5988
|
+
};
|
|
5989
|
+
lastStorageLifecycleGcResult = result;
|
|
5990
|
+
return result;
|
|
5991
|
+
}
|
|
5992
|
+
function getLastStorageLifecycleGcResult() {
|
|
5993
|
+
return lastStorageLifecycleGcResult;
|
|
5994
|
+
}
|
|
5995
|
+
const MAX_MEMORY_CACHE = 100;
|
|
5996
|
+
const memoryCache = /* @__PURE__ */ new Map();
|
|
5997
|
+
const MAX_CURSOR_PAGE_INDEXES = 16;
|
|
5998
|
+
const logCursorPageIndexes = /* @__PURE__ */ new Map();
|
|
5999
|
+
let sessionArchiveQueue = Promise.resolve();
|
|
6000
|
+
const SESSION_ARCHIVE_BATCH_SIZE = 128;
|
|
6001
|
+
const SESSION_ARCHIVE_FLUSH_DELAY_MS = 10;
|
|
6002
|
+
let sessionArchiveBuffer = [];
|
|
6003
|
+
let sessionArchiveFlushTimeout = null;
|
|
6004
|
+
function drainSessionArchiveBuffer() {
|
|
6005
|
+
const logs = sessionArchiveBuffer;
|
|
6006
|
+
sessionArchiveBuffer = [];
|
|
6007
|
+
return logs;
|
|
6008
|
+
}
|
|
6009
|
+
function queueSessionArchiveFlush() {
|
|
6010
|
+
const logs = drainSessionArchiveBuffer();
|
|
6011
|
+
if (logs.length === 0) return;
|
|
6012
|
+
sessionArchiveQueue = sessionArchiveQueue.catch(() => void 0).then(async () => {
|
|
6013
|
+
await archiveSessionLogs(logs);
|
|
6014
|
+
}).catch((err) => {
|
|
6015
|
+
logger.warn("[store] Failed to archive session logs:", String(err));
|
|
6016
|
+
});
|
|
6017
|
+
}
|
|
6018
|
+
function cancelSessionArchiveFlushTimer() {
|
|
6019
|
+
if (sessionArchiveFlushTimeout === null) return;
|
|
6020
|
+
clearTimeout(sessionArchiveFlushTimeout);
|
|
6021
|
+
sessionArchiveFlushTimeout = null;
|
|
6022
|
+
}
|
|
6023
|
+
function scheduleSessionArchiveFlush() {
|
|
6024
|
+
if (sessionArchiveFlushTimeout !== null) return;
|
|
6025
|
+
sessionArchiveFlushTimeout = setTimeout(() => {
|
|
6026
|
+
sessionArchiveFlushTimeout = null;
|
|
6027
|
+
queueSessionArchiveFlush();
|
|
6028
|
+
}, SESSION_ARCHIVE_FLUSH_DELAY_MS);
|
|
6029
|
+
}
|
|
6030
|
+
function cursorPageIndexKey(options) {
|
|
6031
|
+
return JSON.stringify({
|
|
6032
|
+
sessionId: options.sessionId ?? null,
|
|
6033
|
+
model: options.model ?? null,
|
|
6034
|
+
clientPid: options.clientPid ?? null
|
|
6035
|
+
});
|
|
6036
|
+
}
|
|
6037
|
+
function matchesCursorIndexFilters(log, index) {
|
|
6038
|
+
if (index.sessionId !== void 0 && getLogSessionId(log) !== index.sessionId) return false;
|
|
6039
|
+
if (index.model !== void 0 && log.model !== index.model) return false;
|
|
6040
|
+
if (index.clientPid !== void 0 && log.clientPid !== index.clientPid) return false;
|
|
6041
|
+
return true;
|
|
6042
|
+
}
|
|
6043
|
+
function insertSortedLogId(ids, id) {
|
|
6044
|
+
if (ids.includes(id)) return;
|
|
6045
|
+
const insertionIndex = ids.findIndex((candidate) => candidate > id);
|
|
6046
|
+
if (insertionIndex === -1) {
|
|
6047
|
+
ids.push(id);
|
|
6048
|
+
return;
|
|
6049
|
+
}
|
|
6050
|
+
ids.splice(insertionIndex, 0, id);
|
|
6051
|
+
}
|
|
6052
|
+
function upsertLogCursorPageIndexes(log) {
|
|
6053
|
+
for (const index of logCursorPageIndexes.values()) {
|
|
6054
|
+
if (!matchesCursorIndexFilters(log, index)) {
|
|
6055
|
+
if (index.logsById.delete(log.id)) {
|
|
6056
|
+
index.ids = index.ids.filter((id) => id !== log.id);
|
|
6057
|
+
}
|
|
6058
|
+
index.entriesById.delete(log.id);
|
|
6059
|
+
continue;
|
|
6060
|
+
}
|
|
6061
|
+
index.logsById.set(log.id, log);
|
|
6062
|
+
insertSortedLogId(index.ids, log.id);
|
|
6063
|
+
}
|
|
6064
|
+
}
|
|
6065
|
+
function removeLogsFromCursorPageIndexes(ids) {
|
|
6066
|
+
if (ids.size === 0) return;
|
|
6067
|
+
for (const index of logCursorPageIndexes.values()) {
|
|
6068
|
+
for (const id of ids) {
|
|
6069
|
+
index.logsById.delete(id);
|
|
6070
|
+
index.entriesById.delete(id);
|
|
6071
|
+
}
|
|
6072
|
+
index.ids = index.ids.filter((id) => !ids.has(id));
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
6075
|
+
function clearLogCursorPageIndexes() {
|
|
6076
|
+
logCursorPageIndexes.clear();
|
|
6077
|
+
}
|
|
6078
|
+
function evictOldestIfNeeded() {
|
|
6079
|
+
while (memoryCache.size > MAX_MEMORY_CACHE) {
|
|
6080
|
+
const oldestKey = memoryCache.keys().next().value;
|
|
6081
|
+
if (oldestKey === void 0) break;
|
|
6082
|
+
memoryCache.delete(oldestKey);
|
|
4977
6083
|
}
|
|
4978
6084
|
}
|
|
4979
6085
|
function normalizeLog(log) {
|
|
@@ -4984,7 +6090,8 @@ function normalizeLog(log) {
|
|
|
4984
6090
|
const droppedChunks = log.droppedChunks ?? 0;
|
|
4985
6091
|
const droppedBytes = log.droppedBytes ?? 0;
|
|
4986
6092
|
const captureIncompleteReason = log.captureIncompleteReason ?? null;
|
|
4987
|
-
|
|
6093
|
+
const stopReason = log.stopReason ?? extractStopReason(log);
|
|
6094
|
+
if (sessionId === log.sessionId && apiFormat === log.apiFormat && log.captureIncomplete !== void 0 && log.droppedChunks !== void 0 && log.droppedBytes !== void 0 && log.captureIncompleteReason !== void 0 && log.stopReason !== void 0) {
|
|
4988
6095
|
return log;
|
|
4989
6096
|
}
|
|
4990
6097
|
return {
|
|
@@ -4994,7 +6101,8 @@ function normalizeLog(log) {
|
|
|
4994
6101
|
captureIncomplete,
|
|
4995
6102
|
droppedChunks,
|
|
4996
6103
|
droppedBytes,
|
|
4997
|
-
captureIncompleteReason
|
|
6104
|
+
captureIncompleteReason,
|
|
6105
|
+
stopReason
|
|
4998
6106
|
};
|
|
4999
6107
|
}
|
|
5000
6108
|
function addToCache(log) {
|
|
@@ -5033,6 +6141,7 @@ function compactLogForList(log) {
|
|
|
5033
6141
|
rawHeaders: void 0,
|
|
5034
6142
|
headers: void 0,
|
|
5035
6143
|
streamingChunks: void 0,
|
|
6144
|
+
stopReason: log.stopReason ?? extractStopReason(log),
|
|
5036
6145
|
toolTraceEvents: log.toolTraceEvents ?? extractToolTraceEvents(log),
|
|
5037
6146
|
rawRequestBodyBytes: log.rawRequestBodyBytes ?? textByteLength(log.rawRequestBody),
|
|
5038
6147
|
responseTextBytes: log.responseTextBytes ?? textByteLength(log.responseText),
|
|
@@ -5078,6 +6187,7 @@ function compactLogFromIndexSummary(summary) {
|
|
|
5078
6187
|
droppedBytes: summary.droppedBytes,
|
|
5079
6188
|
captureIncompleteReason: summary.captureIncompleteReason,
|
|
5080
6189
|
warnings: summary.warnings,
|
|
6190
|
+
stopReason: summary.stopReason,
|
|
5081
6191
|
toolTraceEvents: summary.toolTraceEvents,
|
|
5082
6192
|
error: summary.error
|
|
5083
6193
|
});
|
|
@@ -5440,17 +6550,42 @@ async function listLogsPage(options) {
|
|
|
5440
6550
|
limit: options.limit
|
|
5441
6551
|
};
|
|
5442
6552
|
}
|
|
5443
|
-
function
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
6553
|
+
function lowerBoundLogId(ids, target) {
|
|
6554
|
+
let low = 0;
|
|
6555
|
+
let high = ids.length;
|
|
6556
|
+
while (low < high) {
|
|
6557
|
+
const middle = Math.floor((low + high) / 2);
|
|
6558
|
+
const id = ids[middle];
|
|
6559
|
+
if (id === void 0 || id >= target) {
|
|
6560
|
+
high = middle;
|
|
6561
|
+
continue;
|
|
6562
|
+
}
|
|
6563
|
+
low = middle + 1;
|
|
6564
|
+
}
|
|
6565
|
+
return low;
|
|
6566
|
+
}
|
|
6567
|
+
function upperBoundLogId(ids, target) {
|
|
6568
|
+
let low = 0;
|
|
6569
|
+
let high = ids.length;
|
|
6570
|
+
while (low < high) {
|
|
6571
|
+
const middle = Math.floor((low + high) / 2);
|
|
6572
|
+
const id = ids[middle];
|
|
6573
|
+
if (id === void 0 || id > target) {
|
|
6574
|
+
high = middle;
|
|
6575
|
+
continue;
|
|
6576
|
+
}
|
|
6577
|
+
low = middle + 1;
|
|
6578
|
+
}
|
|
6579
|
+
return low;
|
|
5447
6580
|
}
|
|
5448
6581
|
function selectCursorPageLogs(ids, options) {
|
|
5449
|
-
const
|
|
6582
|
+
const windowStart = options.afterLogId === void 0 ? 0 : upperBoundLogId(ids, options.afterLogId);
|
|
6583
|
+
const windowEnd = options.beforeLogId === void 0 ? ids.length : lowerBoundLogId(ids, options.beforeLogId);
|
|
6584
|
+
if (windowStart >= windowEnd) return [];
|
|
5450
6585
|
if (options.afterLogId !== void 0 || options.anchor === "oldest") {
|
|
5451
|
-
return
|
|
6586
|
+
return ids.slice(windowStart, Math.min(windowEnd, windowStart + options.limit));
|
|
5452
6587
|
}
|
|
5453
|
-
return
|
|
6588
|
+
return ids.slice(Math.max(windowStart, windowEnd - options.limit), windowEnd);
|
|
5454
6589
|
}
|
|
5455
6590
|
function rememberLogCursorPageIndex(index) {
|
|
5456
6591
|
if (logCursorPageIndexes.has(index.key)) {
|
|
@@ -5482,11 +6617,18 @@ function matchesLogIndexEntryFilters(entry, sessionId, model, clientPid) {
|
|
|
5482
6617
|
return true;
|
|
5483
6618
|
}
|
|
5484
6619
|
async function buildLogCursorPageIndexFromLogIndex(options, allowRebuild) {
|
|
5485
|
-
|
|
5486
|
-
|
|
6620
|
+
let entries = await listSqliteLogCursorEntries(options);
|
|
6621
|
+
if (entries === null) {
|
|
6622
|
+
if (options.clientPid !== void 0) return null;
|
|
6623
|
+
entries = await listFilteredIndexEntries(options);
|
|
6624
|
+
}
|
|
5487
6625
|
if (entries.length === 0 && allowRebuild) {
|
|
5488
6626
|
await rebuildIndex();
|
|
5489
|
-
entries = await
|
|
6627
|
+
entries = await listSqliteLogCursorEntries(options);
|
|
6628
|
+
if (entries === null) {
|
|
6629
|
+
if (options.clientPid !== void 0) return null;
|
|
6630
|
+
entries = await listFilteredIndexEntries(options);
|
|
6631
|
+
}
|
|
5490
6632
|
}
|
|
5491
6633
|
if (entries.some((entry) => !hasLogIndexFilterMetadata(entry))) {
|
|
5492
6634
|
if (!allowRebuild) return null;
|
|
@@ -6301,9 +7443,9 @@ function validateValueAgainstSchema(schema, value, toolName, path2, warnings) {
|
|
|
6301
7443
|
}
|
|
6302
7444
|
}
|
|
6303
7445
|
}
|
|
6304
|
-
function extractAnthropicToolSchemas(
|
|
7446
|
+
function extractAnthropicToolSchemas(request2) {
|
|
6305
7447
|
const tools = /* @__PURE__ */ new Map();
|
|
6306
|
-
for (const tool of readArray(
|
|
7448
|
+
for (const tool of readArray(request2, "tools")) {
|
|
6307
7449
|
if (!isRecord$2(tool)) continue;
|
|
6308
7450
|
const name2 = readString(tool, "name");
|
|
6309
7451
|
if (name2 === null || name2 === "") continue;
|
|
@@ -6314,9 +7456,9 @@ function extractAnthropicToolSchemas(request) {
|
|
|
6314
7456
|
}
|
|
6315
7457
|
return tools;
|
|
6316
7458
|
}
|
|
6317
|
-
function extractOpenAIToolSchemas(
|
|
7459
|
+
function extractOpenAIToolSchemas(request2) {
|
|
6318
7460
|
const tools = /* @__PURE__ */ new Map();
|
|
6319
|
-
for (const tool of readArray(
|
|
7461
|
+
for (const tool of readArray(request2, "tools")) {
|
|
6320
7462
|
const functionDefinition = readObject(tool, "function");
|
|
6321
7463
|
const name2 = functionDefinition === null ? readString(tool, "name") : readString(functionDefinition, "name") ?? readString(tool, "name");
|
|
6322
7464
|
if (name2 === null || name2 === "") continue;
|
|
@@ -6335,16 +7477,16 @@ function mergeToolSchemas(primary, secondary) {
|
|
|
6335
7477
|
}
|
|
6336
7478
|
return merged;
|
|
6337
7479
|
}
|
|
6338
|
-
function extractToolSchemas(
|
|
7480
|
+
function extractToolSchemas(request2, apiFormat) {
|
|
6339
7481
|
switch (apiFormat) {
|
|
6340
7482
|
case "anthropic":
|
|
6341
|
-
return extractAnthropicToolSchemas(
|
|
7483
|
+
return extractAnthropicToolSchemas(request2);
|
|
6342
7484
|
case "openai":
|
|
6343
|
-
return extractOpenAIToolSchemas(
|
|
7485
|
+
return extractOpenAIToolSchemas(request2);
|
|
6344
7486
|
case "unknown":
|
|
6345
7487
|
return mergeToolSchemas(
|
|
6346
|
-
extractOpenAIToolSchemas(
|
|
6347
|
-
extractAnthropicToolSchemas(
|
|
7488
|
+
extractOpenAIToolSchemas(request2),
|
|
7489
|
+
extractAnthropicToolSchemas(request2)
|
|
6348
7490
|
);
|
|
6349
7491
|
}
|
|
6350
7492
|
}
|
|
@@ -6451,10 +7593,10 @@ function uniqueWarnings(warnings) {
|
|
|
6451
7593
|
return unique2;
|
|
6452
7594
|
}
|
|
6453
7595
|
function analyzeToolSchemaWarnings(input) {
|
|
6454
|
-
const
|
|
7596
|
+
const request2 = parseJson$1(input.rawRequestBody);
|
|
6455
7597
|
const response = parseJson$1(input.responseText);
|
|
6456
|
-
if (!
|
|
6457
|
-
const toolSchemas = extractToolSchemas(
|
|
7598
|
+
if (!request2.ok || !response.ok) return [];
|
|
7599
|
+
const toolSchemas = extractToolSchemas(request2.value, input.apiFormat);
|
|
6458
7600
|
const toolCalls = extractToolCalls(response.value, input.apiFormat);
|
|
6459
7601
|
if (toolCalls.length === 0) return [];
|
|
6460
7602
|
const warnings = [];
|
|
@@ -7061,6 +8203,84 @@ async function closeLogFinalizerWorkers() {
|
|
|
7061
8203
|
function forceCloseLogFinalizerWorkers() {
|
|
7062
8204
|
for (const worker of detachLogFinalizerWorkers()) void worker.terminate();
|
|
7063
8205
|
}
|
|
8206
|
+
const UNASSIGNED_SESSION_QUEUE = "__unassigned__";
|
|
8207
|
+
const queues = /* @__PURE__ */ new Map();
|
|
8208
|
+
let queuedTaskCount = 0;
|
|
8209
|
+
class SessionTaskQueueRejectedError extends Error {
|
|
8210
|
+
constructor(limit) {
|
|
8211
|
+
super(`Session task queue is full; limit=${String(limit)}`);
|
|
8212
|
+
this.name = "SessionTaskQueueRejectedError";
|
|
8213
|
+
}
|
|
8214
|
+
}
|
|
8215
|
+
function queueKeyForSession(sessionId) {
|
|
8216
|
+
return sessionId ?? UNASSIGNED_SESSION_QUEUE;
|
|
8217
|
+
}
|
|
8218
|
+
function resolveSessionTaskQueueLimit() {
|
|
8219
|
+
const configured = Number(process.env["SESSION_TASK_QUEUE_LIMIT"]);
|
|
8220
|
+
if (Number.isSafeInteger(configured) && configured > 0) return configured;
|
|
8221
|
+
return DEFAULT_RUNTIME_BUDGET_POLICY.queues.finalizerJobs;
|
|
8222
|
+
}
|
|
8223
|
+
function errorMessage$1(err) {
|
|
8224
|
+
return err instanceof Error ? err.message : String(err);
|
|
8225
|
+
}
|
|
8226
|
+
async function runSessionTask(sessionId, taskName, task) {
|
|
8227
|
+
markSessionTaskStarted(sessionId);
|
|
8228
|
+
try {
|
|
8229
|
+
const result = await task();
|
|
8230
|
+
markSessionTaskFinished(sessionId, null);
|
|
8231
|
+
return result;
|
|
8232
|
+
} catch (err) {
|
|
8233
|
+
const message = errorMessage$1(err);
|
|
8234
|
+
logger.error(`[sessionRuntime] ${taskName} failed for session ${sessionId ?? "unassigned"}`);
|
|
8235
|
+
markSessionTaskFinished(sessionId, message);
|
|
8236
|
+
return Promise.reject(err);
|
|
8237
|
+
}
|
|
8238
|
+
}
|
|
8239
|
+
function enqueueSessionTask(sessionId, taskName, task) {
|
|
8240
|
+
const queueLimit = resolveSessionTaskQueueLimit();
|
|
8241
|
+
if (queuedTaskCount >= queueLimit) {
|
|
8242
|
+
logger.warn(
|
|
8243
|
+
`[sessionRuntime] Rejecting ${taskName} for session ${sessionId ?? "unassigned"}: queue full (${String(queuedTaskCount)}/${String(queueLimit)})`
|
|
8244
|
+
);
|
|
8245
|
+
return Promise.reject(new SessionTaskQueueRejectedError(queueLimit));
|
|
8246
|
+
}
|
|
8247
|
+
const queueKey = queueKeyForSession(sessionId);
|
|
8248
|
+
const previous = queues.get(queueKey) ?? Promise.resolve();
|
|
8249
|
+
queuedTaskCount += 1;
|
|
8250
|
+
markSessionTaskQueued(sessionId);
|
|
8251
|
+
const current = previous.catch(() => void 0).then(() => runSessionTask(sessionId, taskName, task));
|
|
8252
|
+
const settled = current.then(
|
|
8253
|
+
() => void 0,
|
|
8254
|
+
() => void 0
|
|
8255
|
+
);
|
|
8256
|
+
queues.set(queueKey, settled);
|
|
8257
|
+
void settled.then(() => {
|
|
8258
|
+
queuedTaskCount = Math.max(0, queuedTaskCount - 1);
|
|
8259
|
+
if (queues.get(queueKey) === settled) {
|
|
8260
|
+
queues.delete(queueKey);
|
|
8261
|
+
}
|
|
8262
|
+
});
|
|
8263
|
+
return current;
|
|
8264
|
+
}
|
|
8265
|
+
function enqueueFinalizeLogJob(job) {
|
|
8266
|
+
return enqueueSessionTask(
|
|
8267
|
+
getLogSessionId(job.log),
|
|
8268
|
+
"finalize-log",
|
|
8269
|
+
() => executeFinalizeLogJob(job)
|
|
8270
|
+
);
|
|
8271
|
+
}
|
|
8272
|
+
async function flushSessionTasks() {
|
|
8273
|
+
while (queues.size > 0) {
|
|
8274
|
+
await Promise.all([...queues.values()]);
|
|
8275
|
+
}
|
|
8276
|
+
}
|
|
8277
|
+
function getSessionRuntimeQueueHealth() {
|
|
8278
|
+
return {
|
|
8279
|
+
activeQueues: queues.size,
|
|
8280
|
+
queuedTasks: queuedTaskCount,
|
|
8281
|
+
queueLimit: resolveSessionTaskQueueLimit()
|
|
8282
|
+
};
|
|
8283
|
+
}
|
|
7064
8284
|
const DEFAULT_CACHE_MS = 2e3;
|
|
7065
8285
|
const DEFAULT_MIN_DISK_HEADROOM_BYTES = 256 * 1024 * 1024;
|
|
7066
8286
|
const DEFAULT_MAX_FINALIZER_QUEUE = 256;
|
|
@@ -7099,6 +8319,7 @@ async function checkDiskHeadroom() {
|
|
|
7099
8319
|
}
|
|
7100
8320
|
function finalizerChecks() {
|
|
7101
8321
|
const health = getFinalizerRuntimeHealth();
|
|
8322
|
+
const sessionQueue = getSessionRuntimeQueueHealth();
|
|
7102
8323
|
const runtimeArtifactMissing = health.runtime !== "inline" && !health.artifactAvailable;
|
|
7103
8324
|
const initializedWorkerMissing = health.runtime === "worker" && health.activeWorkers !== null && health.activeWorkers < health.configuredWorkers;
|
|
7104
8325
|
const workers = runtimeArtifactMissing || initializedWorkerMissing ? "unavailable" : health.runtime === "inline" ? "degraded" : "ok";
|
|
@@ -7107,7 +8328,7 @@ function finalizerChecks() {
|
|
|
7107
8328
|
DEFAULT_MAX_FINALIZER_QUEUE
|
|
7108
8329
|
);
|
|
7109
8330
|
const warningThreshold = Math.max(1, Math.floor(queueLimit * 0.8));
|
|
7110
|
-
const queue = health.pendingJobs >= queueLimit ? "unavailable" : health.pendingJobs >= warningThreshold ? "degraded" : "ok";
|
|
8331
|
+
const queue = health.pendingJobs + sessionQueue.queuedTasks >= queueLimit ? "unavailable" : health.pendingJobs + sessionQueue.queuedTasks >= warningThreshold ? "degraded" : "ok";
|
|
7111
8332
|
return { workers, queue };
|
|
7112
8333
|
}
|
|
7113
8334
|
function checkWorkers() {
|
|
@@ -7189,151 +8410,313 @@ function setRuntimeShuttingDown(shuttingDown) {
|
|
|
7189
8410
|
runtimeShuttingDown = shuttingDown;
|
|
7190
8411
|
readinessService.invalidate();
|
|
7191
8412
|
}
|
|
7192
|
-
async function readinessResponse() {
|
|
7193
|
-
const readiness = await getRuntimeReadiness();
|
|
7194
|
-
return Response.json(readiness, {
|
|
7195
|
-
status: readiness.status === "unavailable" ? 503 : 200,
|
|
7196
|
-
headers: { "cache-control": "no-store" }
|
|
8413
|
+
async function readinessResponse() {
|
|
8414
|
+
const readiness = await getRuntimeReadiness();
|
|
8415
|
+
return Response.json(readiness, {
|
|
8416
|
+
status: readiness.status === "unavailable" ? 503 : 200,
|
|
8417
|
+
headers: { "cache-control": "no-store" }
|
|
8418
|
+
});
|
|
8419
|
+
}
|
|
8420
|
+
const Route$V = createFileRoute("/readyz")({
|
|
8421
|
+
server: {
|
|
8422
|
+
handlers: {
|
|
8423
|
+
GET: readinessResponse
|
|
8424
|
+
}
|
|
8425
|
+
}
|
|
8426
|
+
});
|
|
8427
|
+
const INITIAL_STREAM_LOG_LIMIT = 100;
|
|
8428
|
+
const UPDATE_COALESCE_MS = 50;
|
|
8429
|
+
const sseClientLimiter = createCapacityLimiter(MAX_SSE_CLIENTS);
|
|
8430
|
+
function getLogsStreamMetrics() {
|
|
8431
|
+
return {
|
|
8432
|
+
activeClients: sseClientLimiter.activeCount(),
|
|
8433
|
+
capacity: sseClientLimiter.capacity
|
|
8434
|
+
};
|
|
8435
|
+
}
|
|
8436
|
+
function streamIncludesBodies(url2) {
|
|
8437
|
+
return url2.searchParams.get("compact") === "0";
|
|
8438
|
+
}
|
|
8439
|
+
function parseLastEventLogId(value) {
|
|
8440
|
+
if (value === null) return void 0;
|
|
8441
|
+
return parseLogId(value) ?? void 0;
|
|
8442
|
+
}
|
|
8443
|
+
function encodeSse(value, id) {
|
|
8444
|
+
const idLine = id === void 0 ? "" : `id: ${String(id)}
|
|
8445
|
+
`;
|
|
8446
|
+
return new TextEncoder().encode(`${idLine}data: ${JSON.stringify(value)}
|
|
8447
|
+
|
|
8448
|
+
`);
|
|
8449
|
+
}
|
|
8450
|
+
function createLogsStreamResponse(request2, clientLimiter = sseClientLimiter) {
|
|
8451
|
+
const lastEventIdHeader = request2.headers.get("last-event-id");
|
|
8452
|
+
const lastEventLogId = parseLastEventLogId(lastEventIdHeader);
|
|
8453
|
+
if (lastEventIdHeader !== null && lastEventLogId === void 0) {
|
|
8454
|
+
return Response.json({ error: "Invalid Last-Event-ID" }, { status: 400 });
|
|
8455
|
+
}
|
|
8456
|
+
const lease = clientLimiter.tryAcquire();
|
|
8457
|
+
if (lease === null) {
|
|
8458
|
+
return resourceLimitResponse({
|
|
8459
|
+
error: `SSE client capacity reached. At most ${String(clientLimiter.capacity)} live clients are allowed; retry after a connection closes.`,
|
|
8460
|
+
code: "sse-capacity-exceeded",
|
|
8461
|
+
status: 503,
|
|
8462
|
+
resource: "sse-clients",
|
|
8463
|
+
limit: clientLimiter.capacity,
|
|
8464
|
+
retryable: true,
|
|
8465
|
+
retryAfterSeconds: 1
|
|
8466
|
+
});
|
|
8467
|
+
}
|
|
8468
|
+
const url2 = new URL(request2.url);
|
|
8469
|
+
const sessionId = url2.searchParams.get("sessionId") ?? void 0;
|
|
8470
|
+
const model = url2.searchParams.get("model") ?? void 0;
|
|
8471
|
+
const includeBodies = streamIncludesBodies(url2);
|
|
8472
|
+
let controllerRef = null;
|
|
8473
|
+
let cleanedUp = false;
|
|
8474
|
+
let updateTimer = null;
|
|
8475
|
+
const pendingUpdates = /* @__PURE__ */ new Map();
|
|
8476
|
+
const flushPendingUpdates = () => {
|
|
8477
|
+
updateTimer = null;
|
|
8478
|
+
const controller = controllerRef;
|
|
8479
|
+
if (controller === null) return;
|
|
8480
|
+
if (controller.desiredSize !== null && controller.desiredSize <= -16) {
|
|
8481
|
+
controller.close();
|
|
8482
|
+
cleanup();
|
|
8483
|
+
return;
|
|
8484
|
+
}
|
|
8485
|
+
for (const log of pendingUpdates.values()) {
|
|
8486
|
+
if (controller.desiredSize !== null && controller.desiredSize <= -16) {
|
|
8487
|
+
controller.close();
|
|
8488
|
+
cleanup();
|
|
8489
|
+
return;
|
|
8490
|
+
}
|
|
8491
|
+
const payloadLog = includeBodies ? log : compactLogForList(log);
|
|
8492
|
+
controller.enqueue(encodeSse({ type: "update", log: payloadLog }, log.id));
|
|
8493
|
+
}
|
|
8494
|
+
pendingUpdates.clear();
|
|
8495
|
+
};
|
|
8496
|
+
const unsubscribe = onLogUpdate((log) => {
|
|
8497
|
+
if (controllerRef === null) return;
|
|
8498
|
+
if (sessionId !== void 0 && getLogSessionId(log) !== sessionId) return;
|
|
8499
|
+
if (model !== void 0 && log.model !== model) return;
|
|
8500
|
+
pendingUpdates.set(log.id, log);
|
|
8501
|
+
if (updateTimer === null) updateTimer = setTimeout(flushPendingUpdates, UPDATE_COALESCE_MS);
|
|
7197
8502
|
});
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
8503
|
+
const cleanup = () => {
|
|
8504
|
+
if (cleanedUp) return;
|
|
8505
|
+
cleanedUp = true;
|
|
8506
|
+
clearInterval(heartbeat);
|
|
8507
|
+
if (updateTimer !== null) clearTimeout(updateTimer);
|
|
8508
|
+
updateTimer = null;
|
|
8509
|
+
pendingUpdates.clear();
|
|
8510
|
+
unsubscribe();
|
|
8511
|
+
lease.release();
|
|
8512
|
+
controllerRef = null;
|
|
8513
|
+
request2.signal.removeEventListener("abort", cleanup);
|
|
8514
|
+
};
|
|
8515
|
+
const heartbeat = setInterval(() => {
|
|
8516
|
+
if (controllerRef !== null) {
|
|
8517
|
+
try {
|
|
8518
|
+
controllerRef.enqueue(new TextEncoder().encode(": heartbeat\n\n"));
|
|
8519
|
+
} catch {
|
|
8520
|
+
cleanup();
|
|
8521
|
+
}
|
|
7203
8522
|
}
|
|
7204
|
-
}
|
|
7205
|
-
});
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
8523
|
+
}, 3e4);
|
|
8524
|
+
request2.signal.addEventListener("abort", cleanup, { once: true });
|
|
8525
|
+
const stream = new ReadableStream({
|
|
8526
|
+
async start(controller) {
|
|
8527
|
+
controllerRef = controller;
|
|
8528
|
+
try {
|
|
8529
|
+
const result = await listLogsCursorPage({
|
|
8530
|
+
sessionId,
|
|
8531
|
+
model,
|
|
8532
|
+
limit: INITIAL_STREAM_LOG_LIMIT,
|
|
8533
|
+
includeBodies,
|
|
8534
|
+
afterLogId: lastEventLogId,
|
|
8535
|
+
anchor: lastEventLogId === void 0 ? "newest" : void 0
|
|
8536
|
+
});
|
|
8537
|
+
if (cleanedUp) return;
|
|
8538
|
+
const logs = result.logs;
|
|
8539
|
+
const newestLogId = logs.at(-1)?.id;
|
|
8540
|
+
controller.enqueue(encodeSse({ type: "init", logs }, newestLogId));
|
|
8541
|
+
} catch (error) {
|
|
8542
|
+
cleanup();
|
|
8543
|
+
controller.error(error);
|
|
8544
|
+
}
|
|
8545
|
+
},
|
|
8546
|
+
cancel() {
|
|
8547
|
+
cleanup();
|
|
8548
|
+
}
|
|
8549
|
+
});
|
|
8550
|
+
return new Response(stream, {
|
|
8551
|
+
headers: {
|
|
8552
|
+
"Content-Type": "text/event-stream",
|
|
8553
|
+
"Cache-Control": "no-cache",
|
|
8554
|
+
Connection: "keep-alive",
|
|
8555
|
+
"X-Accel-Buffering": "no"
|
|
7213
8556
|
}
|
|
7214
|
-
}
|
|
7215
|
-
});
|
|
7216
|
-
const BACKEND_NOT_FOUND_BODY = {
|
|
7217
|
-
error: "not_found",
|
|
7218
|
-
message: "Agent Inspector backend does not serve the web UI."
|
|
7219
|
-
};
|
|
7220
|
-
function backendNotFoundResponse() {
|
|
7221
|
-
return Response.json(BACKEND_NOT_FOUND_BODY, {
|
|
7222
|
-
status: 404,
|
|
7223
|
-
headers: { "cache-control": "no-store" }
|
|
7224
8557
|
});
|
|
7225
8558
|
}
|
|
7226
|
-
const Route$
|
|
8559
|
+
const Route$U = createFileRoute("/api/logs/stream")({
|
|
7227
8560
|
server: {
|
|
7228
8561
|
handlers: {
|
|
7229
|
-
GET:
|
|
7230
|
-
POST: backendNotFoundResponse,
|
|
7231
|
-
PUT: backendNotFoundResponse,
|
|
7232
|
-
PATCH: backendNotFoundResponse,
|
|
7233
|
-
DELETE: backendNotFoundResponse,
|
|
7234
|
-
OPTIONS: backendNotFoundResponse
|
|
8562
|
+
GET: ({ request: request2 }) => createLogsStreamResponse(request2)
|
|
7235
8563
|
}
|
|
7236
8564
|
}
|
|
7237
8565
|
});
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
8566
|
+
let acceptingRequests = true;
|
|
8567
|
+
let activeRequests = 0;
|
|
8568
|
+
const drainWaiters = /* @__PURE__ */ new Set();
|
|
8569
|
+
function notifyDrained() {
|
|
8570
|
+
if (activeRequests !== 0) return;
|
|
8571
|
+
for (const resolve2 of drainWaiters) resolve2();
|
|
8572
|
+
drainWaiters.clear();
|
|
7241
8573
|
}
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
"search-limit-exceeded",
|
|
7255
|
-
"provider-model-limit-exceeded",
|
|
7256
|
-
"sse-capacity-exceeded",
|
|
7257
|
-
"provider-test-capacity-exceeded",
|
|
7258
|
-
"proxy-capacity-exceeded"
|
|
7259
|
-
]);
|
|
7260
|
-
const ResourceLimitStatusSchema = union([
|
|
7261
|
-
literal(400),
|
|
7262
|
-
literal(413),
|
|
7263
|
-
literal(429),
|
|
7264
|
-
literal(503)
|
|
7265
|
-
]);
|
|
7266
|
-
const ResourceLimitErrorSchema = object({
|
|
7267
|
-
error: string().min(1).max(300),
|
|
7268
|
-
code: ResourceLimitErrorCodeSchema,
|
|
7269
|
-
status: ResourceLimitStatusSchema,
|
|
7270
|
-
resource: string().min(1).max(80),
|
|
7271
|
-
limit: number().int().positive(),
|
|
7272
|
-
retryable: boolean()
|
|
7273
|
-
});
|
|
7274
|
-
function createResourceLimitError(input) {
|
|
7275
|
-
return ResourceLimitErrorSchema.parse(input);
|
|
8574
|
+
function acquireRuntimeRequest() {
|
|
8575
|
+
if (!acceptingRequests || activeRequests >= MAX_ACTIVE_PROXY_REQUESTS) return null;
|
|
8576
|
+
activeRequests += 1;
|
|
8577
|
+
let released = false;
|
|
8578
|
+
return {
|
|
8579
|
+
release() {
|
|
8580
|
+
if (released) return;
|
|
8581
|
+
released = true;
|
|
8582
|
+
activeRequests = Math.max(0, activeRequests - 1);
|
|
8583
|
+
notifyDrained();
|
|
8584
|
+
}
|
|
8585
|
+
};
|
|
7276
8586
|
}
|
|
7277
|
-
function
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
if (input.retryAfterSeconds !== void 0) {
|
|
7281
|
-
headers.set("retry-after", String(input.retryAfterSeconds));
|
|
7282
|
-
}
|
|
7283
|
-
return Response.json(body, { status: input.status, headers });
|
|
8587
|
+
function stopRuntimeAdmission() {
|
|
8588
|
+
acceptingRequests = false;
|
|
8589
|
+
notifyDrained();
|
|
7284
8590
|
}
|
|
7285
|
-
function
|
|
7286
|
-
if (
|
|
7287
|
-
|
|
7288
|
-
|
|
8591
|
+
async function waitForRuntimeTrafficDrain() {
|
|
8592
|
+
if (activeRequests === 0) return;
|
|
8593
|
+
await new Promise((resolve2) => {
|
|
8594
|
+
drainWaiters.add(resolve2);
|
|
8595
|
+
});
|
|
8596
|
+
}
|
|
8597
|
+
function getRuntimeAdmissionState() {
|
|
8598
|
+
return { acceptingRequests, activeRequests, capacity: MAX_ACTIVE_PROXY_REQUESTS };
|
|
8599
|
+
}
|
|
8600
|
+
const PERFORMANCE_METRICS_SCHEMA = "agent-inspector.performance-metrics/v1";
|
|
8601
|
+
const OPERATIONAL_METRICS_SCHEMA = "agent-inspector.operational-metrics/v1";
|
|
8602
|
+
async function getTruncationMetrics() {
|
|
8603
|
+
const entries = await listSqliteLogCursorEntries({});
|
|
8604
|
+
if (entries === null) {
|
|
7289
8605
|
return {
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
status: 400,
|
|
7295
|
-
resource: input.resource,
|
|
7296
|
-
limit: input.maximum,
|
|
7297
|
-
retryable: false
|
|
7298
|
-
})
|
|
8606
|
+
captureIncompleteLogs: 0,
|
|
8607
|
+
droppedChunks: 0,
|
|
8608
|
+
droppedBytes: 0,
|
|
8609
|
+
source: "unavailable"
|
|
7299
8610
|
};
|
|
7300
8611
|
}
|
|
7301
|
-
return {
|
|
8612
|
+
return {
|
|
8613
|
+
captureIncompleteLogs: entries.filter((entry) => entry.summary?.captureIncomplete === true).length,
|
|
8614
|
+
droppedChunks: entries.reduce((sum, entry) => sum + (entry.summary?.droppedChunks ?? 0), 0),
|
|
8615
|
+
droppedBytes: entries.reduce((sum, entry) => sum + (entry.summary?.droppedBytes ?? 0), 0),
|
|
8616
|
+
source: "sqlite"
|
|
8617
|
+
};
|
|
7302
8618
|
}
|
|
7303
|
-
function
|
|
7304
|
-
const
|
|
7305
|
-
|
|
8619
|
+
async function getSafeOperationalMetrics(sse) {
|
|
8620
|
+
const [readiness, truncation] = await Promise.all([
|
|
8621
|
+
getRuntimeReadiness(),
|
|
8622
|
+
getTruncationMetrics()
|
|
8623
|
+
]);
|
|
8624
|
+
const admission = getRuntimeAdmissionState();
|
|
8625
|
+
const workers = getFinalizerRuntimeHealth();
|
|
8626
|
+
const sessionQueue = getSessionRuntimeQueueHealth();
|
|
8627
|
+
const storageInventory = getStorageLifecycleInventory();
|
|
8628
|
+
const lastGc = getLastStorageLifecycleGcResult();
|
|
8629
|
+
const reclaimableBytes = storageInventory.artifacts.filter(
|
|
8630
|
+
(artifact) => artifact.cleanupBehavior === "retention-gc" || artifact.cleanupBehavior === "orphan-reclaim"
|
|
8631
|
+
).reduce((sum, artifact) => sum + artifact.bytes, 0);
|
|
8632
|
+
const queueDepth = workers.pendingJobs + sessionQueue.queuedTasks;
|
|
7306
8633
|
return {
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
8634
|
+
schema: PERFORMANCE_METRICS_SCHEMA,
|
|
8635
|
+
activeUpstream: admission.activeRequests,
|
|
8636
|
+
queueDepth,
|
|
8637
|
+
queueBytes: 0,
|
|
8638
|
+
incompleteCaptures: truncation.captureIncompleteLogs,
|
|
8639
|
+
finalizerLagMs: 0,
|
|
8640
|
+
persistenceLagMs: 0,
|
|
8641
|
+
activeLeases: sse.activeClients,
|
|
8642
|
+
runtime: {
|
|
8643
|
+
schema: OPERATIONAL_METRICS_SCHEMA,
|
|
8644
|
+
collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8645
|
+
readiness,
|
|
8646
|
+
requests: {
|
|
8647
|
+
activeProxyRequests: admission.activeRequests,
|
|
8648
|
+
proxyCapacity: admission.capacity,
|
|
8649
|
+
acceptingRequests: admission.acceptingRequests
|
|
8650
|
+
},
|
|
8651
|
+
limits: DEFAULT_RUNTIME_BUDGET_POLICY,
|
|
8652
|
+
queues: {
|
|
8653
|
+
finalizerPendingJobs: workers.pendingJobs,
|
|
8654
|
+
sessionQueuedTasks: sessionQueue.queuedTasks,
|
|
8655
|
+
sessionActiveQueues: sessionQueue.activeQueues,
|
|
8656
|
+
sessionQueueLimit: sessionQueue.queueLimit
|
|
8657
|
+
},
|
|
8658
|
+
workers,
|
|
8659
|
+
storage: {
|
|
8660
|
+
totalFiles: storageInventory.totalFiles,
|
|
8661
|
+
totalBytes: storageInventory.totalBytes,
|
|
8662
|
+
reclaimableBytes,
|
|
8663
|
+
lastGc: lastGc === null ? null : {
|
|
8664
|
+
checkedAt: lastGc.checkedAt,
|
|
8665
|
+
filesDeleted: lastGc.filesDeleted,
|
|
8666
|
+
bytesDeleted: lastGc.bytesDeleted,
|
|
8667
|
+
errors: lastGc.errors.length
|
|
7318
8668
|
}
|
|
7319
|
-
}
|
|
8669
|
+
},
|
|
8670
|
+
sse,
|
|
8671
|
+
truncation
|
|
8672
|
+
}
|
|
8673
|
+
};
|
|
8674
|
+
}
|
|
8675
|
+
const Route$T = createFileRoute("/metrics")({
|
|
8676
|
+
server: {
|
|
8677
|
+
handlers: {
|
|
8678
|
+
GET: async () => {
|
|
8679
|
+
const metrics = await getSafeOperationalMetrics(getLogsStreamMetrics());
|
|
8680
|
+
return Response.json(metrics, { headers: { "cache-control": "no-store" } });
|
|
8681
|
+
}
|
|
8682
|
+
}
|
|
8683
|
+
}
|
|
8684
|
+
});
|
|
8685
|
+
function livenessResponse() {
|
|
8686
|
+
return Response.json({ status: "ok" }, { headers: { "cache-control": "no-store" } });
|
|
8687
|
+
}
|
|
8688
|
+
const Route$S = createFileRoute("/livez")({
|
|
8689
|
+
server: {
|
|
8690
|
+
handlers: {
|
|
8691
|
+
GET: livenessResponse
|
|
7320
8692
|
}
|
|
7321
|
-
}
|
|
8693
|
+
}
|
|
8694
|
+
});
|
|
8695
|
+
const BACKEND_NOT_FOUND_BODY = {
|
|
8696
|
+
error: "not_found",
|
|
8697
|
+
message: "Agent Inspector backend does not serve the web UI."
|
|
8698
|
+
};
|
|
8699
|
+
function backendNotFoundResponse() {
|
|
8700
|
+
return Response.json(BACKEND_NOT_FOUND_BODY, {
|
|
8701
|
+
status: 404,
|
|
8702
|
+
headers: { "cache-control": "no-store" }
|
|
8703
|
+
});
|
|
7322
8704
|
}
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
const task = tasks2[index];
|
|
7333
|
-
if (task !== void 0) await task();
|
|
8705
|
+
const Route$R = createFileRoute("/$")({
|
|
8706
|
+
server: {
|
|
8707
|
+
handlers: {
|
|
8708
|
+
GET: backendNotFoundResponse,
|
|
8709
|
+
POST: backendNotFoundResponse,
|
|
8710
|
+
PUT: backendNotFoundResponse,
|
|
8711
|
+
PATCH: backendNotFoundResponse,
|
|
8712
|
+
DELETE: backendNotFoundResponse,
|
|
8713
|
+
OPTIONS: backendNotFoundResponse
|
|
7334
8714
|
}
|
|
7335
|
-
}
|
|
7336
|
-
|
|
8715
|
+
}
|
|
8716
|
+
});
|
|
8717
|
+
function maskApiKey(apiKey) {
|
|
8718
|
+
if (apiKey.length <= 8) return "••••••••";
|
|
8719
|
+
return apiKey.slice(0, 4) + "••••••••" + apiKey.slice(-4);
|
|
7337
8720
|
}
|
|
7338
8721
|
function httpUrl() {
|
|
7339
8722
|
return string().refine((val) => {
|
|
@@ -8677,16 +10060,16 @@ function setCache(port, info) {
|
|
|
8677
10060
|
cache.set(port, { ...info, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
8678
10061
|
evictCacheIfNeeded();
|
|
8679
10062
|
}
|
|
8680
|
-
function extractRemotePort(
|
|
10063
|
+
function extractRemotePort(request2) {
|
|
8681
10064
|
const candidateSockets = [];
|
|
8682
|
-
const requestAsUnknown =
|
|
10065
|
+
const requestAsUnknown = request2;
|
|
8683
10066
|
const ctx = pick(requestAsUnknown, "context");
|
|
8684
10067
|
const nitro = pick(ctx, "nitro");
|
|
8685
10068
|
const node = pick(nitro, "node");
|
|
8686
10069
|
const innerReq = pick(node, "req");
|
|
8687
10070
|
const sock = pick(innerReq, "socket") ?? pick(innerReq, "connection");
|
|
8688
10071
|
if (sock !== void 0) candidateSockets.push(sock);
|
|
8689
|
-
const descriptor = Object.getOwnPropertyDescriptor(
|
|
10072
|
+
const descriptor = Object.getOwnPropertyDescriptor(request2, "socket");
|
|
8690
10073
|
if (descriptor !== void 0 && descriptor.get === void 0) {
|
|
8691
10074
|
candidateSockets.push(descriptor.value);
|
|
8692
10075
|
}
|
|
@@ -8890,12 +10273,12 @@ function readIntHeader(headers, name2) {
|
|
|
8890
10273
|
if (!Number.isInteger(parsed) || parsed < 0) return null;
|
|
8891
10274
|
return parsed;
|
|
8892
10275
|
}
|
|
8893
|
-
function readClientHeaders(
|
|
8894
|
-
const pid = readIntHeader(
|
|
10276
|
+
function readClientHeaders(request2) {
|
|
10277
|
+
const pid = readIntHeader(request2.headers, CLIENT_HEADERS.pid);
|
|
8895
10278
|
if (pid === null || pid === 0) return null;
|
|
8896
|
-
const port = readIntHeader(
|
|
8897
|
-
const cwd =
|
|
8898
|
-
const projectFolder =
|
|
10279
|
+
const port = readIntHeader(request2.headers, CLIENT_HEADERS.port);
|
|
10280
|
+
const cwd = request2.headers.get(CLIENT_HEADERS.cwd);
|
|
10281
|
+
const projectFolder = request2.headers.get(CLIENT_HEADERS.projectFolder);
|
|
8899
10282
|
return {
|
|
8900
10283
|
pid,
|
|
8901
10284
|
port: port !== null && port > 0 ? port : null,
|
|
@@ -8903,10 +10286,10 @@ function readClientHeaders(request) {
|
|
|
8903
10286
|
projectFolder: projectFolder !== null && projectFolder !== "" ? projectFolder : null
|
|
8904
10287
|
};
|
|
8905
10288
|
}
|
|
8906
|
-
async function getClientInfo(
|
|
8907
|
-
const fromHeaders = readClientHeaders(
|
|
10289
|
+
async function getClientInfo(request2) {
|
|
10290
|
+
const fromHeaders = readClientHeaders(request2);
|
|
8908
10291
|
if (fromHeaders !== null) return fromHeaders;
|
|
8909
|
-
const remotePort = extractRemotePort(
|
|
10292
|
+
const remotePort = extractRemotePort(request2);
|
|
8910
10293
|
if (remotePort !== null) {
|
|
8911
10294
|
const cachedPort = getFromCache(remotePort);
|
|
8912
10295
|
if (cachedPort !== null) return cachedPort;
|
|
@@ -9213,585 +10596,569 @@ function isLoopbackHost(hostname) {
|
|
|
9213
10596
|
if (LOOPBACK_HOSTNAMES.has(lower2)) return true;
|
|
9214
10597
|
if (lower2 === "::1") return true;
|
|
9215
10598
|
if (lower2.startsWith("::ffff:127.")) return true;
|
|
9216
|
-
if (lower2.startsWith("::ffff:7f00:")) return true;
|
|
9217
|
-
return /^127\./.test(lower2);
|
|
9218
|
-
}
|
|
9219
|
-
function mappedIpv4Address(hostname) {
|
|
9220
|
-
const lower2 = hostname.toLowerCase();
|
|
9221
|
-
if (!lower2.startsWith("::ffff:")) return null;
|
|
9222
|
-
const suffix = lower2.slice("::ffff:".length);
|
|
9223
|
-
if (/^\d{1,3}(?:\.\d{1,3}){3}$/.test(suffix)) return suffix;
|
|
9224
|
-
const match = /^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(suffix);
|
|
9225
|
-
if (match === null) return null;
|
|
9226
|
-
const highText = match[1];
|
|
9227
|
-
const lowText = match[2];
|
|
9228
|
-
if (highText === void 0 || lowText === void 0) return null;
|
|
9229
|
-
const high = Number.parseInt(highText, 16);
|
|
9230
|
-
const low = Number.parseInt(lowText, 16);
|
|
9231
|
-
return `${String(high >> 8)}.${String(high & 255)}.${String(low >> 8)}.${String(low & 255)}`;
|
|
9232
|
-
}
|
|
9233
|
-
function isInternalHost(hostname) {
|
|
9234
|
-
if (LOOPBACK_HOSTNAMES.has(hostname.toLowerCase())) return true;
|
|
9235
|
-
const mappedIpv4 = mappedIpv4Address(hostname);
|
|
9236
|
-
if (mappedIpv4 !== null) return isInternalHost(mappedIpv4);
|
|
9237
|
-
const ipv4Match = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(hostname);
|
|
9238
|
-
if (ipv4Match !== null) {
|
|
9239
|
-
const [, a, b] = ipv4Match;
|
|
9240
|
-
if (a === void 0 || b === void 0) return false;
|
|
9241
|
-
const first = Number.parseInt(a, 10);
|
|
9242
|
-
const second = Number.parseInt(b, 10);
|
|
9243
|
-
if (first === 127) return true;
|
|
9244
|
-
if (first === 10) return true;
|
|
9245
|
-
if (first === 172 && second >= 16 && second <= 31) return true;
|
|
9246
|
-
if (first === 192 && second === 168) return true;
|
|
9247
|
-
if (first === 169 && second === 254) return true;
|
|
9248
|
-
if (first === 0) return true;
|
|
9249
|
-
if (first === 100 && second >= 64 && second <= 127) return true;
|
|
9250
|
-
if (first >= 224) return true;
|
|
9251
|
-
if (first === 198 && (second === 18 || second === 19)) return true;
|
|
9252
|
-
return false;
|
|
9253
|
-
}
|
|
9254
|
-
const lower2 = hostname.toLowerCase();
|
|
9255
|
-
if (lower2 === "::") return true;
|
|
9256
|
-
if (lower2 === "::1") return true;
|
|
9257
|
-
if (/^fe[89ab]/.test(lower2)) return true;
|
|
9258
|
-
if (lower2.startsWith("fc") || lower2.startsWith("fd")) return true;
|
|
9259
|
-
if (lower2.startsWith("ff")) return true;
|
|
9260
|
-
return false;
|
|
9261
|
-
}
|
|
9262
|
-
function evaluateUpstreamUrl(rawUrl, options = {}) {
|
|
9263
|
-
if (options.bypass === true) return { allow: true };
|
|
9264
|
-
let parsed;
|
|
9265
|
-
try {
|
|
9266
|
-
parsed = new URL(rawUrl);
|
|
9267
|
-
} catch {
|
|
9268
|
-
return { allow: false, reason: "Not a valid URL" };
|
|
9269
|
-
}
|
|
9270
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
9271
|
-
return { allow: false, reason: `Unsupported scheme "${parsed.protocol}"` };
|
|
9272
|
-
}
|
|
9273
|
-
if (options.allowLoopback === true && parsed.hostname === "localhost") {
|
|
9274
|
-
return { allow: true };
|
|
9275
|
-
}
|
|
9276
|
-
const hostname = parsed.hostname.replace(/^\[|]$/g, "");
|
|
9277
|
-
if (isInternalHost(hostname)) {
|
|
9278
|
-
if (options.allowLoopback === true) {
|
|
9279
|
-
if (isLoopbackHost(hostname)) return { allow: true };
|
|
9280
|
-
}
|
|
9281
|
-
return {
|
|
9282
|
-
allow: false,
|
|
9283
|
-
reason: `Refusing to proxy to internal address "${hostname}"`
|
|
9284
|
-
};
|
|
9285
|
-
}
|
|
9286
|
-
return { allow: true };
|
|
9287
|
-
}
|
|
9288
|
-
async function defaultResolver(hostname) {
|
|
9289
|
-
return lookup(hostname, { all: true, verbatim: true });
|
|
9290
|
-
}
|
|
9291
|
-
function isLoopbackAddress(hostname) {
|
|
9292
|
-
const lower2 = hostname.toLowerCase();
|
|
9293
|
-
return lower2 === "::1" || lower2.startsWith("::ffff:127.") || lower2.startsWith("::ffff:7f00:") || /^127\./.test(lower2);
|
|
9294
|
-
}
|
|
9295
|
-
async function evaluateResolvedUpstreamUrl(rawUrl, options = {}) {
|
|
9296
|
-
const initial = evaluateUpstreamUrl(rawUrl, options);
|
|
9297
|
-
if (!initial.allow || options.bypass === true) return initial;
|
|
9298
|
-
const parsed = new URL(rawUrl);
|
|
9299
|
-
const hostname = parsed.hostname.replace(/^\[|]$/g, "");
|
|
9300
|
-
if (isIP(hostname) !== 0 || hostname.toLowerCase() === "localhost") return initial;
|
|
9301
|
-
let addresses;
|
|
9302
|
-
try {
|
|
9303
|
-
addresses = await (options.resolver ?? defaultResolver)(hostname);
|
|
9304
|
-
} catch (error) {
|
|
9305
|
-
return {
|
|
9306
|
-
allow: false,
|
|
9307
|
-
reason: `Could not safely resolve upstream host "${hostname}": ${String(error)}`
|
|
9308
|
-
};
|
|
9309
|
-
}
|
|
9310
|
-
if (addresses.length === 0) {
|
|
9311
|
-
return { allow: false, reason: `Upstream host "${hostname}" resolved to no addresses` };
|
|
9312
|
-
}
|
|
9313
|
-
for (const resolved of addresses) {
|
|
9314
|
-
if (!isInternalHost(resolved.address)) continue;
|
|
9315
|
-
if (options.allowLoopback === true && isLoopbackAddress(resolved.address)) continue;
|
|
9316
|
-
return {
|
|
9317
|
-
allow: false,
|
|
9318
|
-
reason: `Refusing upstream host "${hostname}" because it resolves to internal address "${resolved.address}"`
|
|
9319
|
-
};
|
|
9320
|
-
}
|
|
9321
|
-
return { allow: true };
|
|
9322
|
-
}
|
|
9323
|
-
function isRedirectStatus(status) {
|
|
9324
|
-
return status >= 300 && status < 400;
|
|
9325
|
-
}
|
|
9326
|
-
async function safeFetch(rawUrl, init = {}, options = {}) {
|
|
9327
|
-
const decision = await evaluateResolvedUpstreamUrl(rawUrl, options);
|
|
9328
|
-
if (!decision.allow) {
|
|
9329
|
-
return { ok: false, kind: "network-policy", reason: decision.reason };
|
|
9330
|
-
}
|
|
9331
|
-
const response = await fetch(rawUrl, { ...init, redirect: "manual" });
|
|
9332
|
-
if (!isRedirectStatus(response.status)) return { ok: true, response };
|
|
9333
|
-
if (response.body !== null) {
|
|
9334
|
-
await response.body.cancel().catch(() => void 0);
|
|
9335
|
-
}
|
|
9336
|
-
const location = response.headers.get("location");
|
|
9337
|
-
return {
|
|
9338
|
-
ok: false,
|
|
9339
|
-
kind: "redirect",
|
|
9340
|
-
reason: location === null ? `Upstream returned blocked redirect HTTP ${String(response.status)}` : `Upstream redirect to "${location}" is blocked; configure the final API URL directly`
|
|
9341
|
-
};
|
|
9342
|
-
}
|
|
9343
|
-
const BILLING_HEADER_PREFIX = "x-anthropic-billing-header:";
|
|
9344
|
-
function isClaudeCodeBillingHeaderBlock(text) {
|
|
9345
|
-
const trimmed = text.trimStart();
|
|
9346
|
-
return trimmed.toLowerCase().startsWith(BILLING_HEADER_PREFIX);
|
|
9347
|
-
}
|
|
9348
|
-
function isObjectWithSystem(value) {
|
|
9349
|
-
if (!isPlainRecord(value)) return false;
|
|
9350
|
-
return Object.prototype.hasOwnProperty.call(value, "system");
|
|
9351
|
-
}
|
|
9352
|
-
function isBillingHeaderTextBlock(block) {
|
|
9353
|
-
if (!isPlainRecord(block)) return false;
|
|
9354
|
-
const typeVal = safeGetOwnProperty(block, "type");
|
|
9355
|
-
const textVal = safeGetOwnProperty(block, "text");
|
|
9356
|
-
if (typeof typeVal !== "string" || typeVal !== "text") return false;
|
|
9357
|
-
if (typeof textVal !== "string") return false;
|
|
9358
|
-
return isClaudeCodeBillingHeaderBlock(textVal);
|
|
9359
|
-
}
|
|
9360
|
-
function stripClaudeCodeBillingHeader(rawBody) {
|
|
9361
|
-
let parsed;
|
|
9362
|
-
try {
|
|
9363
|
-
parsed = JSON.parse(rawBody);
|
|
9364
|
-
} catch {
|
|
9365
|
-
return { body: rawBody, removed: 0 };
|
|
9366
|
-
}
|
|
9367
|
-
if (!isObjectWithSystem(parsed)) {
|
|
9368
|
-
return { body: rawBody, removed: 0 };
|
|
9369
|
-
}
|
|
9370
|
-
const system = parsed.system;
|
|
9371
|
-
if (!Array.isArray(system) || system.length === 0) {
|
|
9372
|
-
return { body: rawBody, removed: 0 };
|
|
9373
|
-
}
|
|
9374
|
-
const kept = [];
|
|
9375
|
-
let removed = 0;
|
|
9376
|
-
for (const block of system) {
|
|
9377
|
-
if (isBillingHeaderTextBlock(block)) {
|
|
9378
|
-
removed += 1;
|
|
9379
|
-
continue;
|
|
9380
|
-
}
|
|
9381
|
-
kept.push(block);
|
|
9382
|
-
}
|
|
9383
|
-
if (removed === 0) {
|
|
9384
|
-
return { body: rawBody, removed: 0 };
|
|
9385
|
-
}
|
|
9386
|
-
if (kept.length === 0) {
|
|
9387
|
-
delete parsed.system;
|
|
9388
|
-
} else {
|
|
9389
|
-
parsed.system = kept;
|
|
9390
|
-
}
|
|
9391
|
-
return { body: JSON.stringify(parsed), removed };
|
|
9392
|
-
}
|
|
9393
|
-
const ROLE_ASSISTANT = "assistant";
|
|
9394
|
-
const ROLE_TOOL = "tool";
|
|
9395
|
-
function isObjectWithMessages(value) {
|
|
9396
|
-
if (!isPlainRecord(value)) return false;
|
|
9397
|
-
if (!Object.prototype.hasOwnProperty.call(value, "messages")) return false;
|
|
9398
|
-
const messages = safeGetOwnProperty(value, "messages");
|
|
9399
|
-
return Array.isArray(messages);
|
|
9400
|
-
}
|
|
9401
|
-
function collectAssistantToolCallIds(message, into) {
|
|
9402
|
-
if (!isPlainRecord(message)) return;
|
|
9403
|
-
const toolCalls = safeGetOwnProperty(message, "tool_calls");
|
|
9404
|
-
if (!Array.isArray(toolCalls)) return;
|
|
9405
|
-
for (const tc of toolCalls) {
|
|
9406
|
-
if (!isPlainRecord(tc)) continue;
|
|
9407
|
-
const id = safeGetOwnProperty(tc, "id");
|
|
9408
|
-
if (typeof id === "string" && id.length > 0) {
|
|
9409
|
-
into.add(id);
|
|
9410
|
-
}
|
|
9411
|
-
}
|
|
9412
|
-
}
|
|
9413
|
-
function findOrphanToolMessageIndices(messages) {
|
|
9414
|
-
const knownToolCallIds = /* @__PURE__ */ new Set();
|
|
9415
|
-
const indices = [];
|
|
9416
|
-
const orphanIds = [];
|
|
9417
|
-
for (let i = 0; i < messages.length; i++) {
|
|
9418
|
-
const msg = messages[i];
|
|
9419
|
-
if (!isPlainRecord(msg)) continue;
|
|
9420
|
-
const role = safeGetOwnProperty(msg, "role");
|
|
9421
|
-
if (role === ROLE_ASSISTANT) {
|
|
9422
|
-
collectAssistantToolCallIds(msg, knownToolCallIds);
|
|
9423
|
-
continue;
|
|
9424
|
-
}
|
|
9425
|
-
if (role === ROLE_TOOL) {
|
|
9426
|
-
const id = safeGetOwnProperty(msg, "tool_call_id");
|
|
9427
|
-
if (typeof id !== "string" || id.length === 0) {
|
|
9428
|
-
indices.push(i);
|
|
9429
|
-
orphanIds.push(null);
|
|
9430
|
-
continue;
|
|
9431
|
-
}
|
|
9432
|
-
if (!knownToolCallIds.has(id)) {
|
|
9433
|
-
indices.push(i);
|
|
9434
|
-
orphanIds.push(id);
|
|
9435
|
-
}
|
|
9436
|
-
}
|
|
10599
|
+
if (lower2.startsWith("::ffff:7f00:")) return true;
|
|
10600
|
+
return /^127\./.test(lower2);
|
|
10601
|
+
}
|
|
10602
|
+
function mappedIpv4Address(hostname) {
|
|
10603
|
+
const lower2 = hostname.toLowerCase();
|
|
10604
|
+
if (!lower2.startsWith("::ffff:")) return null;
|
|
10605
|
+
const suffix = lower2.slice("::ffff:".length);
|
|
10606
|
+
if (/^\d{1,3}(?:\.\d{1,3}){3}$/.test(suffix)) return suffix;
|
|
10607
|
+
const match = /^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(suffix);
|
|
10608
|
+
if (match === null) return null;
|
|
10609
|
+
const highText = match[1];
|
|
10610
|
+
const lowText = match[2];
|
|
10611
|
+
if (highText === void 0 || lowText === void 0) return null;
|
|
10612
|
+
const high = Number.parseInt(highText, 16);
|
|
10613
|
+
const low = Number.parseInt(lowText, 16);
|
|
10614
|
+
return `${String(high >> 8)}.${String(high & 255)}.${String(low >> 8)}.${String(low & 255)}`;
|
|
10615
|
+
}
|
|
10616
|
+
function isInternalHost(hostname) {
|
|
10617
|
+
if (LOOPBACK_HOSTNAMES.has(hostname.toLowerCase())) return true;
|
|
10618
|
+
const mappedIpv4 = mappedIpv4Address(hostname);
|
|
10619
|
+
if (mappedIpv4 !== null) return isInternalHost(mappedIpv4);
|
|
10620
|
+
const ipv4Match = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(hostname);
|
|
10621
|
+
if (ipv4Match !== null) {
|
|
10622
|
+
const [, a, b] = ipv4Match;
|
|
10623
|
+
if (a === void 0 || b === void 0) return false;
|
|
10624
|
+
const first = Number.parseInt(a, 10);
|
|
10625
|
+
const second = Number.parseInt(b, 10);
|
|
10626
|
+
if (first === 127) return true;
|
|
10627
|
+
if (first === 10) return true;
|
|
10628
|
+
if (first === 172 && second >= 16 && second <= 31) return true;
|
|
10629
|
+
if (first === 192 && second === 168) return true;
|
|
10630
|
+
if (first === 169 && second === 254) return true;
|
|
10631
|
+
if (first === 0) return true;
|
|
10632
|
+
if (first === 100 && second >= 64 && second <= 127) return true;
|
|
10633
|
+
if (first >= 224) return true;
|
|
10634
|
+
if (first === 198 && (second === 18 || second === 19)) return true;
|
|
10635
|
+
return false;
|
|
9437
10636
|
}
|
|
9438
|
-
|
|
10637
|
+
const lower2 = hostname.toLowerCase();
|
|
10638
|
+
if (lower2 === "::") return true;
|
|
10639
|
+
if (lower2 === "::1") return true;
|
|
10640
|
+
if (/^fe[89ab]/.test(lower2)) return true;
|
|
10641
|
+
if (lower2.startsWith("fc") || lower2.startsWith("fd")) return true;
|
|
10642
|
+
if (lower2.startsWith("ff")) return true;
|
|
10643
|
+
return false;
|
|
9439
10644
|
}
|
|
9440
|
-
function
|
|
10645
|
+
function evaluateUpstreamUrl(rawUrl, options = {}) {
|
|
10646
|
+
if (options.bypass === true) return { allow: true };
|
|
9441
10647
|
let parsed;
|
|
9442
10648
|
try {
|
|
9443
|
-
parsed =
|
|
10649
|
+
parsed = new URL(rawUrl);
|
|
9444
10650
|
} catch {
|
|
9445
|
-
return {
|
|
10651
|
+
return { allow: false, reason: "Not a valid URL" };
|
|
9446
10652
|
}
|
|
9447
|
-
if (
|
|
9448
|
-
return {
|
|
10653
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
10654
|
+
return { allow: false, reason: `Unsupported scheme "${parsed.protocol}"` };
|
|
9449
10655
|
}
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
if (indices.length === 0) {
|
|
9453
|
-
return { body: rawBody, removed: 0, orphanIds: [] };
|
|
10656
|
+
if (options.allowLoopback === true && parsed.hostname === "localhost") {
|
|
10657
|
+
return { allow: true };
|
|
9454
10658
|
}
|
|
9455
|
-
const
|
|
9456
|
-
|
|
9457
|
-
|
|
9458
|
-
|
|
10659
|
+
const hostname = parsed.hostname.replace(/^\[|]$/g, "");
|
|
10660
|
+
if (isInternalHost(hostname)) {
|
|
10661
|
+
if (options.allowLoopback === true) {
|
|
10662
|
+
if (isLoopbackHost(hostname)) return { allow: true };
|
|
10663
|
+
}
|
|
10664
|
+
return {
|
|
10665
|
+
allow: false,
|
|
10666
|
+
reason: `Refusing to proxy to internal address "${hostname}"`
|
|
10667
|
+
};
|
|
9459
10668
|
}
|
|
9460
|
-
|
|
9461
|
-
return { body: JSON.stringify(parsed), removed: indices.length, orphanIds };
|
|
10669
|
+
return { allow: true };
|
|
9462
10670
|
}
|
|
9463
|
-
|
|
9464
|
-
|
|
9465
|
-
function queueKeyForSession(sessionId) {
|
|
9466
|
-
return sessionId ?? UNASSIGNED_SESSION_QUEUE;
|
|
10671
|
+
async function defaultResolver(hostname) {
|
|
10672
|
+
return lookup(hostname, { all: true, verbatim: true });
|
|
9467
10673
|
}
|
|
9468
|
-
function
|
|
9469
|
-
|
|
10674
|
+
function isLoopbackAddress(hostname) {
|
|
10675
|
+
const lower2 = hostname.toLowerCase();
|
|
10676
|
+
return lower2 === "::1" || lower2.startsWith("::ffff:127.") || lower2.startsWith("::ffff:7f00:") || /^127\./.test(lower2);
|
|
9470
10677
|
}
|
|
9471
|
-
async function
|
|
9472
|
-
|
|
10678
|
+
async function evaluateResolvedUpstreamUrlForFetch(rawUrl, options) {
|
|
10679
|
+
const initial = evaluateUpstreamUrl(rawUrl, options);
|
|
10680
|
+
if (!initial.allow) return initial;
|
|
10681
|
+
const parsed = new URL(rawUrl);
|
|
10682
|
+
const hostname = parsed.hostname.replace(/^\[|]$/g, "");
|
|
10683
|
+
if (options.bypass === true) return { allow: true, hostname, addresses: [] };
|
|
10684
|
+
const ipFamily = isIP(hostname);
|
|
10685
|
+
if (ipFamily !== 0)
|
|
10686
|
+
return { allow: true, hostname, addresses: [{ address: hostname, family: ipFamily }] };
|
|
10687
|
+
let addresses;
|
|
9473
10688
|
try {
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
return
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
|
|
9480
|
-
markSessionTaskFinished(sessionId, message);
|
|
9481
|
-
return Promise.reject(err);
|
|
10689
|
+
addresses = await (options.resolver ?? defaultResolver)(hostname);
|
|
10690
|
+
} catch (error) {
|
|
10691
|
+
return {
|
|
10692
|
+
allow: false,
|
|
10693
|
+
reason: `Could not safely resolve upstream host "${hostname}": ${String(error)}`
|
|
10694
|
+
};
|
|
9482
10695
|
}
|
|
10696
|
+
if (addresses.length === 0) {
|
|
10697
|
+
return { allow: false, reason: `Upstream host "${hostname}" resolved to no addresses` };
|
|
10698
|
+
}
|
|
10699
|
+
for (const resolved of addresses) {
|
|
10700
|
+
if (!isInternalHost(resolved.address)) continue;
|
|
10701
|
+
if (options.allowLoopback === true && isLoopbackAddress(resolved.address)) continue;
|
|
10702
|
+
return {
|
|
10703
|
+
allow: false,
|
|
10704
|
+
reason: `Refusing upstream host "${hostname}" because it resolves to internal address "${resolved.address}"`
|
|
10705
|
+
};
|
|
10706
|
+
}
|
|
10707
|
+
return { allow: true, hostname, addresses };
|
|
9483
10708
|
}
|
|
9484
|
-
function
|
|
9485
|
-
|
|
9486
|
-
const previous = queues.get(queueKey) ?? Promise.resolve();
|
|
9487
|
-
markSessionTaskQueued(sessionId);
|
|
9488
|
-
const current = previous.catch(() => void 0).then(() => runSessionTask(sessionId, taskName, task));
|
|
9489
|
-
const settled = current.then(
|
|
9490
|
-
() => void 0,
|
|
9491
|
-
() => void 0
|
|
9492
|
-
);
|
|
9493
|
-
queues.set(queueKey, settled);
|
|
9494
|
-
void settled.then(() => {
|
|
9495
|
-
if (queues.get(queueKey) === settled) {
|
|
9496
|
-
queues.delete(queueKey);
|
|
9497
|
-
}
|
|
9498
|
-
});
|
|
9499
|
-
return current;
|
|
9500
|
-
}
|
|
9501
|
-
function enqueueFinalizeLogJob(job) {
|
|
9502
|
-
return enqueueSessionTask(
|
|
9503
|
-
getLogSessionId(job.log),
|
|
9504
|
-
"finalize-log",
|
|
9505
|
-
() => executeFinalizeLogJob(job)
|
|
9506
|
-
);
|
|
10709
|
+
function isRedirectStatus(status) {
|
|
10710
|
+
return status >= 300 && status < 400;
|
|
9507
10711
|
}
|
|
9508
|
-
|
|
9509
|
-
|
|
9510
|
-
await Promise.all([...queues.values()]);
|
|
9511
|
-
}
|
|
10712
|
+
function formatByteLimit(limit) {
|
|
10713
|
+
return `${String(limit)} bytes`;
|
|
9512
10714
|
}
|
|
9513
|
-
|
|
9514
|
-
|
|
9515
|
-
const
|
|
9516
|
-
|
|
9517
|
-
const DEFAULT_GLOBAL_QUEUE_MAX_MESSAGES = 4096;
|
|
9518
|
-
const DEFAULT_GLOBAL_QUEUE_MAX_BYTES = 64 * 1024 * 1024;
|
|
9519
|
-
const activeCaptures = /* @__PURE__ */ new Set();
|
|
9520
|
-
function resolveMemoryLimitBytes() {
|
|
9521
|
-
const raw = process.env["AGENT_INSPECTOR_STREAM_MEMORY_LIMIT_BYTES"];
|
|
9522
|
-
if (raw === void 0 || raw === "") return DEFAULT_MEMORY_LIMIT_BYTES;
|
|
9523
|
-
const parsed = Number(raw);
|
|
9524
|
-
if (!Number.isInteger(parsed) || parsed < 0) return DEFAULT_MEMORY_LIMIT_BYTES;
|
|
10715
|
+
function parseContentLength(value) {
|
|
10716
|
+
if (value === null) return null;
|
|
10717
|
+
const parsed = Number(value);
|
|
10718
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
|
|
9525
10719
|
return parsed;
|
|
9526
10720
|
}
|
|
9527
|
-
function
|
|
9528
|
-
const
|
|
9529
|
-
if (
|
|
9530
|
-
|
|
9531
|
-
if (!Number.isSafeInteger(parsed) || parsed < 1) return fallback;
|
|
9532
|
-
return parsed;
|
|
10721
|
+
function isPinnedDnsEnabled(options) {
|
|
10722
|
+
const disabledForTest = typeof process !== "undefined" && process.env["NODE_ENV"] === "test" && process.env["AGENT_INSPECTOR_DISABLE_DNS_PINNING_FOR_TESTS"] === "1";
|
|
10723
|
+
if (disabledForTest) return false;
|
|
10724
|
+
return options.pinDns !== false && options.bypass !== true;
|
|
9533
10725
|
}
|
|
9534
|
-
function
|
|
9535
|
-
return
|
|
9536
|
-
"AGENT_INSPECTOR_OBSERVATION_QUEUE_MESSAGES",
|
|
9537
|
-
DEFAULT_QUEUE_MAX_MESSAGES
|
|
9538
|
-
);
|
|
10726
|
+
function choosePinnedAddress(addresses) {
|
|
10727
|
+
return addresses[0] ?? null;
|
|
9539
10728
|
}
|
|
9540
|
-
function
|
|
9541
|
-
return
|
|
9542
|
-
"AGENT_INSPECTOR_OBSERVATION_QUEUE_BYTES",
|
|
9543
|
-
DEFAULT_QUEUE_MAX_BYTES
|
|
9544
|
-
);
|
|
10729
|
+
function hostHeaderValue(parsed) {
|
|
10730
|
+
return parsed.port.length === 0 ? parsed.host : `${parsed.hostname}:${parsed.port}`;
|
|
9545
10731
|
}
|
|
9546
|
-
|
|
9547
|
-
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
}
|
|
9551
|
-
maxMessages;
|
|
9552
|
-
maxBytes;
|
|
9553
|
-
messages = 0;
|
|
9554
|
-
bytes = 0;
|
|
9555
|
-
tryAcquire(bytes) {
|
|
9556
|
-
if (this.messages + 1 > this.maxMessages || this.bytes + bytes > this.maxBytes) return null;
|
|
9557
|
-
this.messages += 1;
|
|
9558
|
-
this.bytes += bytes;
|
|
9559
|
-
let released = false;
|
|
9560
|
-
return () => {
|
|
9561
|
-
if (released) return;
|
|
9562
|
-
released = true;
|
|
9563
|
-
this.messages -= 1;
|
|
9564
|
-
this.bytes -= bytes;
|
|
9565
|
-
};
|
|
10732
|
+
function headersToRecord$1(headers, parsed) {
|
|
10733
|
+
const normalized = new Headers(headers);
|
|
10734
|
+
if (!normalized.has("host")) {
|
|
10735
|
+
normalized.set("host", hostHeaderValue(parsed));
|
|
9566
10736
|
}
|
|
10737
|
+
const result = {};
|
|
10738
|
+
normalized.forEach((value, key) => {
|
|
10739
|
+
result[key] = value;
|
|
10740
|
+
});
|
|
10741
|
+
return result;
|
|
9567
10742
|
}
|
|
9568
|
-
|
|
9569
|
-
|
|
9570
|
-
|
|
9571
|
-
|
|
9572
|
-
),
|
|
9573
|
-
resolvePositiveIntegerEnv(
|
|
9574
|
-
"AGENT_INSPECTOR_OBSERVATION_GLOBAL_QUEUE_BYTES",
|
|
9575
|
-
DEFAULT_GLOBAL_QUEUE_MAX_BYTES
|
|
9576
|
-
)
|
|
9577
|
-
);
|
|
9578
|
-
function getRawStreamDir() {
|
|
9579
|
-
return join(getDataDir(), "raw-streams");
|
|
9580
|
-
}
|
|
9581
|
-
function createRawStreamPath() {
|
|
9582
|
-
return join(getRawStreamDir(), `${Date.now()}-${randomUUID()}.stream.tmp`);
|
|
9583
|
-
}
|
|
9584
|
-
class RawStreamCapture {
|
|
9585
|
-
memoryLimitBytes;
|
|
9586
|
-
queueMaxMessages;
|
|
9587
|
-
queueMaxBytes;
|
|
9588
|
-
globalBudget;
|
|
9589
|
-
chunks = [];
|
|
9590
|
-
byteLength = 0;
|
|
9591
|
-
filePath = null;
|
|
9592
|
-
fileHandle = null;
|
|
9593
|
-
pendingFileChunks = [];
|
|
9594
|
-
pendingFileBytes = 0;
|
|
9595
|
-
operation = Promise.resolve();
|
|
9596
|
-
accepting = true;
|
|
9597
|
-
closed = false;
|
|
9598
|
-
sawChunk = false;
|
|
9599
|
-
queuedMessages = 0;
|
|
9600
|
-
queuedBytes = 0;
|
|
9601
|
-
incomplete = false;
|
|
9602
|
-
storageFailed = false;
|
|
9603
|
-
droppedChunks = 0;
|
|
9604
|
-
droppedBytes = 0;
|
|
9605
|
-
gapReason = null;
|
|
9606
|
-
constructor(options = {}) {
|
|
9607
|
-
this.memoryLimitBytes = options.memoryLimitBytes ?? resolveMemoryLimitBytes();
|
|
9608
|
-
this.queueMaxMessages = options.queueMaxMessages ?? defaultQueueMaxMessages();
|
|
9609
|
-
this.queueMaxBytes = options.queueMaxBytes ?? defaultQueueMaxBytes();
|
|
9610
|
-
this.globalBudget = options.globalBudget ?? globalObservationBudget;
|
|
9611
|
-
activeCaptures.add(this);
|
|
9612
|
-
}
|
|
9613
|
-
get hasChunks() {
|
|
9614
|
-
return this.sawChunk;
|
|
10743
|
+
async function requestBodyToUint8Array(body) {
|
|
10744
|
+
if (body === void 0 || body === null) return { ok: true, body: null, contentType: null };
|
|
10745
|
+
if (typeof body === "string") {
|
|
10746
|
+
return { ok: true, body: new TextEncoder().encode(body), contentType: null };
|
|
9615
10747
|
}
|
|
9616
|
-
|
|
10748
|
+
if (body instanceof URLSearchParams) {
|
|
9617
10749
|
return {
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
reason: this.gapReason
|
|
10750
|
+
ok: true,
|
|
10751
|
+
body: new TextEncoder().encode(body.toString()),
|
|
10752
|
+
contentType: "application/x-www-form-urlencoded;charset=UTF-8"
|
|
9622
10753
|
};
|
|
9623
10754
|
}
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
10755
|
+
if (body instanceof Uint8Array) return { ok: true, body, contentType: null };
|
|
10756
|
+
if (body instanceof ArrayBuffer) {
|
|
10757
|
+
return { ok: true, body: new Uint8Array(body), contentType: null };
|
|
9627
10758
|
}
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
const next = this.operation.then(() => this.appendInternal(text));
|
|
9631
|
-
this.operation = next.catch(() => void 0);
|
|
9632
|
-
return next;
|
|
10759
|
+
if (body instanceof Blob) {
|
|
10760
|
+
return { ok: true, body: new Uint8Array(await body.arrayBuffer()), contentType: body.type };
|
|
9633
10761
|
}
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
|
|
9638
|
-
|
|
9639
|
-
|
|
9640
|
-
|
|
9641
|
-
|
|
9642
|
-
|
|
9643
|
-
|
|
9644
|
-
return false;
|
|
9645
|
-
}
|
|
9646
|
-
if (this.incomplete) {
|
|
9647
|
-
this.recordGap("capacity-exceeded", safeObservedBytes, safeObservedBytes > 0 || text !== "");
|
|
9648
|
-
return false;
|
|
9649
|
-
}
|
|
9650
|
-
if (text === "") return true;
|
|
9651
|
-
if (this.queuedMessages + 1 > this.queueMaxMessages || this.queuedBytes + queuedBytes > this.queueMaxBytes) {
|
|
9652
|
-
this.recordGap("capacity-exceeded", safeObservedBytes, true);
|
|
9653
|
-
return false;
|
|
10762
|
+
return {
|
|
10763
|
+
ok: false,
|
|
10764
|
+
reason: "Pinned upstream fetch only supports buffered request bodies"
|
|
10765
|
+
};
|
|
10766
|
+
}
|
|
10767
|
+
function pinnedLookup(pinnedAddress) {
|
|
10768
|
+
return (_hostname, options, callback) => {
|
|
10769
|
+
if (options.all === true) {
|
|
10770
|
+
callback(null, [pinnedAddress]);
|
|
10771
|
+
return;
|
|
9654
10772
|
}
|
|
9655
|
-
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
10773
|
+
callback(null, pinnedAddress.address, pinnedAddress.family);
|
|
10774
|
+
};
|
|
10775
|
+
}
|
|
10776
|
+
function requestOptionsForPinnedFetch(parsed, init, pinnedAddress, contentType) {
|
|
10777
|
+
const headers = headersToRecord$1(init.headers, parsed);
|
|
10778
|
+
if (contentType !== null && !new Headers(headers).has("content-type")) {
|
|
10779
|
+
headers["content-type"] = contentType;
|
|
10780
|
+
}
|
|
10781
|
+
return {
|
|
10782
|
+
protocol: parsed.protocol,
|
|
10783
|
+
hostname: parsed.hostname,
|
|
10784
|
+
port: parsed.port,
|
|
10785
|
+
path: `${parsed.pathname}${parsed.search}`,
|
|
10786
|
+
method: init.method ?? "GET",
|
|
10787
|
+
headers,
|
|
10788
|
+
lookup: pinnedLookup(pinnedAddress)
|
|
10789
|
+
};
|
|
10790
|
+
}
|
|
10791
|
+
function streamFromIncomingMessage(incoming, maxBytes) {
|
|
10792
|
+
let seenBytes = 0;
|
|
10793
|
+
return new ReadableStream({
|
|
10794
|
+
start(controller) {
|
|
10795
|
+
incoming.on("data", (chunk) => {
|
|
10796
|
+
seenBytes += chunk.byteLength;
|
|
10797
|
+
if (maxBytes !== void 0 && seenBytes > maxBytes) {
|
|
10798
|
+
incoming.destroy(
|
|
10799
|
+
new Error(
|
|
10800
|
+
`Upstream response exceeded configured limit of ${formatByteLimit(maxBytes)}`
|
|
10801
|
+
)
|
|
10802
|
+
);
|
|
10803
|
+
controller.error(
|
|
10804
|
+
new Error(
|
|
10805
|
+
`Upstream response exceeded configured limit of ${formatByteLimit(maxBytes)}`
|
|
10806
|
+
)
|
|
10807
|
+
);
|
|
10808
|
+
return;
|
|
10809
|
+
}
|
|
10810
|
+
controller.enqueue(chunk);
|
|
10811
|
+
});
|
|
10812
|
+
incoming.on("end", () => {
|
|
10813
|
+
controller.close();
|
|
10814
|
+
});
|
|
10815
|
+
incoming.on("error", (error) => {
|
|
10816
|
+
controller.error(error);
|
|
10817
|
+
});
|
|
10818
|
+
},
|
|
10819
|
+
cancel() {
|
|
10820
|
+
incoming.destroy();
|
|
9659
10821
|
}
|
|
9660
|
-
|
|
9661
|
-
|
|
9662
|
-
|
|
9663
|
-
|
|
9664
|
-
|
|
10822
|
+
});
|
|
10823
|
+
}
|
|
10824
|
+
async function cancelBody(response) {
|
|
10825
|
+
if (response.body === null) return;
|
|
10826
|
+
await response.body.cancel().catch(() => void 0);
|
|
10827
|
+
}
|
|
10828
|
+
function responseWithByteLimit(response, maxBytes) {
|
|
10829
|
+
if (response.body === null) return response;
|
|
10830
|
+
let seenBytes = 0;
|
|
10831
|
+
const reader = response.body.getReader();
|
|
10832
|
+
const body = new ReadableStream({
|
|
10833
|
+
async pull(controller) {
|
|
10834
|
+
const chunk = await reader.read();
|
|
10835
|
+
if (chunk.done) {
|
|
10836
|
+
controller.close();
|
|
9665
10837
|
return;
|
|
9666
10838
|
}
|
|
9667
|
-
|
|
9668
|
-
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
10839
|
+
seenBytes += chunk.value.byteLength;
|
|
10840
|
+
if (seenBytes > maxBytes) {
|
|
10841
|
+
void reader.cancel().catch(() => void 0);
|
|
10842
|
+
controller.error(
|
|
10843
|
+
new Error(`Upstream response exceeded configured limit of ${formatByteLimit(maxBytes)}`)
|
|
10844
|
+
);
|
|
10845
|
+
return;
|
|
10846
|
+
}
|
|
10847
|
+
controller.enqueue(chunk.value);
|
|
10848
|
+
},
|
|
10849
|
+
cancel(reason) {
|
|
10850
|
+
return reader.cancel(reason);
|
|
10851
|
+
}
|
|
10852
|
+
});
|
|
10853
|
+
return new Response(body, {
|
|
10854
|
+
status: response.status,
|
|
10855
|
+
statusText: response.statusText,
|
|
10856
|
+
headers: response.headers
|
|
10857
|
+
});
|
|
10858
|
+
}
|
|
10859
|
+
function shouldUseBudgetedSignal(init, deadlineMs) {
|
|
10860
|
+
return deadlineMs !== void 0 || init.signal !== void 0;
|
|
10861
|
+
}
|
|
10862
|
+
async function pinnedNodeFetch(rawUrl, init, options, decision) {
|
|
10863
|
+
const parsed = new URL(rawUrl);
|
|
10864
|
+
const pinnedAddress = choosePinnedAddress(decision.addresses);
|
|
10865
|
+
if (pinnedAddress === null) {
|
|
10866
|
+
return {
|
|
10867
|
+
ok: false,
|
|
10868
|
+
kind: "network-policy",
|
|
10869
|
+
reason: `Could not pin a resolved upstream address for "${decision.hostname}"`
|
|
10870
|
+
};
|
|
9677
10871
|
}
|
|
9678
|
-
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
|
|
9683
|
-
|
|
10872
|
+
const encodedBody = await requestBodyToUint8Array(init.body);
|
|
10873
|
+
if (!encodedBody.ok) {
|
|
10874
|
+
return { ok: false, kind: "resource-limit", reason: encodedBody.reason };
|
|
10875
|
+
}
|
|
10876
|
+
return await new Promise((resolve2, reject) => {
|
|
10877
|
+
const linkedSignal = init.signal ?? void 0;
|
|
10878
|
+
const transport = parsed.protocol === "https:" ? request : request$1;
|
|
10879
|
+
const requestOptions = requestOptionsForPinnedFetch(
|
|
10880
|
+
parsed,
|
|
10881
|
+
init,
|
|
10882
|
+
pinnedAddress,
|
|
10883
|
+
encodedBody.contentType
|
|
9684
10884
|
);
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
|
|
10885
|
+
const request$2 = transport(requestOptions, (incoming) => {
|
|
10886
|
+
const status = incoming.statusCode ?? 0;
|
|
10887
|
+
if (isRedirectStatus(status)) {
|
|
10888
|
+
incoming.resume();
|
|
10889
|
+
const location = incoming.headers.location;
|
|
10890
|
+
resolve2({
|
|
10891
|
+
ok: false,
|
|
10892
|
+
kind: "redirect",
|
|
10893
|
+
reason: location === void 0 ? `Upstream returned blocked redirect HTTP ${String(status)}` : `Upstream redirect to "${location}" is blocked; configure the final API URL directly`
|
|
10894
|
+
});
|
|
10895
|
+
return;
|
|
10896
|
+
}
|
|
10897
|
+
const maxBytes = options.responseBytes;
|
|
10898
|
+
const headers = new Headers();
|
|
10899
|
+
for (const [key, value] of Object.entries(incoming.headers)) {
|
|
10900
|
+
if (Array.isArray(value)) {
|
|
10901
|
+
headers.set(key, value.join(", "));
|
|
10902
|
+
continue;
|
|
10903
|
+
}
|
|
10904
|
+
if (value !== void 0) headers.set(key, value);
|
|
10905
|
+
}
|
|
10906
|
+
if (maxBytes !== void 0) {
|
|
10907
|
+
const declaredBytes = parseContentLength(headers.get("content-length"));
|
|
10908
|
+
if (declaredBytes !== null && declaredBytes > maxBytes) {
|
|
10909
|
+
incoming.destroy();
|
|
10910
|
+
resolve2({
|
|
10911
|
+
ok: false,
|
|
10912
|
+
kind: "resource-limit",
|
|
10913
|
+
reason: `Upstream declared ${String(declaredBytes)} response bytes, exceeding configured limit of ${formatByteLimit(maxBytes)}`
|
|
10914
|
+
});
|
|
10915
|
+
return;
|
|
10916
|
+
}
|
|
10917
|
+
}
|
|
10918
|
+
resolve2({
|
|
10919
|
+
ok: true,
|
|
10920
|
+
response: new Response(streamFromIncomingMessage(incoming, maxBytes), {
|
|
10921
|
+
status,
|
|
10922
|
+
statusText: incoming.statusMessage,
|
|
10923
|
+
headers
|
|
10924
|
+
})
|
|
10925
|
+
});
|
|
10926
|
+
});
|
|
10927
|
+
let timedOut = false;
|
|
10928
|
+
let timeout;
|
|
10929
|
+
const finishAbort = () => {
|
|
10930
|
+
request$2.destroy(new DOMException("Safe fetch aborted", "AbortError"));
|
|
10931
|
+
};
|
|
10932
|
+
if (linkedSignal !== void 0) {
|
|
10933
|
+
if (linkedSignal.aborted) {
|
|
10934
|
+
finishAbort();
|
|
10935
|
+
} else {
|
|
10936
|
+
linkedSignal.addEventListener("abort", finishAbort, { once: true });
|
|
10937
|
+
}
|
|
9695
10938
|
}
|
|
9696
|
-
if (
|
|
9697
|
-
|
|
10939
|
+
if (options.deadlineMs !== void 0) {
|
|
10940
|
+
timeout = setTimeout(() => {
|
|
10941
|
+
timedOut = true;
|
|
10942
|
+
request$2.destroy(new DOMException("Safe fetch deadline exceeded", "TimeoutError"));
|
|
10943
|
+
}, options.deadlineMs);
|
|
9698
10944
|
}
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
|
|
10945
|
+
request$2.on("error", (error) => {
|
|
10946
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
10947
|
+
if (linkedSignal !== void 0) {
|
|
10948
|
+
linkedSignal.removeEventListener("abort", finishAbort);
|
|
10949
|
+
}
|
|
10950
|
+
if (timedOut) {
|
|
10951
|
+
resolve2({
|
|
10952
|
+
ok: false,
|
|
10953
|
+
kind: "timeout",
|
|
10954
|
+
reason: `Upstream request exceeded configured deadline of ${String(options.deadlineMs)} ms`
|
|
10955
|
+
});
|
|
10956
|
+
return;
|
|
10957
|
+
}
|
|
10958
|
+
reject(error);
|
|
10959
|
+
});
|
|
10960
|
+
request$2.on("close", () => {
|
|
10961
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
10962
|
+
if (linkedSignal !== void 0) {
|
|
10963
|
+
linkedSignal.removeEventListener("abort", finishAbort);
|
|
10964
|
+
}
|
|
10965
|
+
});
|
|
10966
|
+
if (encodedBody.body !== null) request$2.write(encodedBody.body);
|
|
10967
|
+
request$2.end();
|
|
10968
|
+
});
|
|
10969
|
+
}
|
|
10970
|
+
async function safeFetch(rawUrl, init = {}, options = {}) {
|
|
10971
|
+
const resolvedDecision = await evaluateResolvedUpstreamUrlForFetch(rawUrl, options);
|
|
10972
|
+
if (!resolvedDecision.allow) {
|
|
10973
|
+
return { ok: false, kind: "network-policy", reason: resolvedDecision.reason };
|
|
10974
|
+
}
|
|
10975
|
+
if (isPinnedDnsEnabled(options)) {
|
|
10976
|
+
return await pinnedNodeFetch(rawUrl, init, options, resolvedDecision);
|
|
10977
|
+
}
|
|
10978
|
+
const deadlineMs = options.deadlineMs;
|
|
10979
|
+
const useBudgetedSignal = shouldUseBudgetedSignal(init, deadlineMs);
|
|
10980
|
+
const controller = useBudgetedSignal ? new AbortController() : void 0;
|
|
10981
|
+
let timedOut = false;
|
|
10982
|
+
let timeout;
|
|
10983
|
+
const linkedSignal = init.signal ?? void 0;
|
|
10984
|
+
const forwardAbort = () => {
|
|
10985
|
+
controller?.abort(linkedSignal?.reason);
|
|
10986
|
+
};
|
|
10987
|
+
if (controller !== void 0 && linkedSignal !== void 0) {
|
|
10988
|
+
if (linkedSignal.aborted) {
|
|
10989
|
+
forwardAbort();
|
|
10990
|
+
} else {
|
|
10991
|
+
linkedSignal.addEventListener("abort", forwardAbort, { once: true });
|
|
9704
10992
|
}
|
|
9705
10993
|
}
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
10994
|
+
if (controller !== void 0 && deadlineMs !== void 0) {
|
|
10995
|
+
timeout = setTimeout(() => {
|
|
10996
|
+
timedOut = true;
|
|
10997
|
+
controller.abort(new DOMException("Safe fetch deadline exceeded", "TimeoutError"));
|
|
10998
|
+
}, deadlineMs);
|
|
9711
10999
|
}
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
11000
|
+
try {
|
|
11001
|
+
const response = await fetch(rawUrl, {
|
|
11002
|
+
...init,
|
|
11003
|
+
redirect: "manual",
|
|
11004
|
+
signal: controller?.signal ?? init.signal
|
|
11005
|
+
});
|
|
11006
|
+
if (!isRedirectStatus(response.status)) {
|
|
11007
|
+
const maxBytes = options.responseBytes;
|
|
11008
|
+
if (maxBytes === void 0) return { ok: true, response };
|
|
11009
|
+
const declaredBytes = parseContentLength(response.headers.get("content-length"));
|
|
11010
|
+
if (declaredBytes !== null && declaredBytes > maxBytes) {
|
|
11011
|
+
await cancelBody(response);
|
|
11012
|
+
return {
|
|
11013
|
+
ok: false,
|
|
11014
|
+
kind: "resource-limit",
|
|
11015
|
+
reason: `Upstream declared ${String(declaredBytes)} response bytes, exceeding configured limit of ${formatByteLimit(maxBytes)}`
|
|
11016
|
+
};
|
|
9724
11017
|
}
|
|
11018
|
+
return { ok: true, response: responseWithByteLimit(response, maxBytes) };
|
|
9725
11019
|
}
|
|
9726
|
-
|
|
9727
|
-
|
|
11020
|
+
await cancelBody(response);
|
|
11021
|
+
const location = response.headers.get("location");
|
|
11022
|
+
return {
|
|
11023
|
+
ok: false,
|
|
11024
|
+
kind: "redirect",
|
|
11025
|
+
reason: location === null ? `Upstream returned blocked redirect HTTP ${String(response.status)}` : `Upstream redirect to "${location}" is blocked; configure the final API URL directly`
|
|
11026
|
+
};
|
|
11027
|
+
} catch (error) {
|
|
11028
|
+
if (timedOut) {
|
|
11029
|
+
return {
|
|
11030
|
+
ok: false,
|
|
11031
|
+
kind: "timeout",
|
|
11032
|
+
reason: deadlineMs === void 0 ? "Upstream request exceeded its configured deadline" : `Upstream request exceeded configured deadline of ${String(deadlineMs)} ms`
|
|
11033
|
+
};
|
|
11034
|
+
}
|
|
11035
|
+
return Promise.reject(error);
|
|
11036
|
+
} finally {
|
|
11037
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
11038
|
+
if (controller !== void 0 && linkedSignal !== void 0) {
|
|
11039
|
+
linkedSignal.removeEventListener("abort", forwardAbort);
|
|
9728
11040
|
}
|
|
9729
|
-
return { type: "memory", rawStream: this.chunks.join("") };
|
|
9730
11041
|
}
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
|
|
9742
|
-
|
|
9743
|
-
|
|
9744
|
-
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
11042
|
+
}
|
|
11043
|
+
const BILLING_HEADER_PREFIX = "x-anthropic-billing-header:";
|
|
11044
|
+
function isClaudeCodeBillingHeaderBlock(text) {
|
|
11045
|
+
const trimmed = text.trimStart();
|
|
11046
|
+
return trimmed.toLowerCase().startsWith(BILLING_HEADER_PREFIX);
|
|
11047
|
+
}
|
|
11048
|
+
function isObjectWithSystem(value) {
|
|
11049
|
+
if (!isPlainRecord(value)) return false;
|
|
11050
|
+
return Object.prototype.hasOwnProperty.call(value, "system");
|
|
11051
|
+
}
|
|
11052
|
+
function isBillingHeaderTextBlock(block) {
|
|
11053
|
+
if (!isPlainRecord(block)) return false;
|
|
11054
|
+
const typeVal = safeGetOwnProperty(block, "type");
|
|
11055
|
+
const textVal = safeGetOwnProperty(block, "text");
|
|
11056
|
+
if (typeof typeVal !== "string" || typeVal !== "text") return false;
|
|
11057
|
+
if (typeof textVal !== "string") return false;
|
|
11058
|
+
return isClaudeCodeBillingHeaderBlock(textVal);
|
|
11059
|
+
}
|
|
11060
|
+
function stripClaudeCodeBillingHeader(rawBody) {
|
|
11061
|
+
let parsed;
|
|
11062
|
+
try {
|
|
11063
|
+
parsed = JSON.parse(rawBody);
|
|
11064
|
+
} catch {
|
|
11065
|
+
return { body: rawBody, removed: 0 };
|
|
11066
|
+
}
|
|
11067
|
+
if (!isObjectWithSystem(parsed)) {
|
|
11068
|
+
return { body: rawBody, removed: 0 };
|
|
11069
|
+
}
|
|
11070
|
+
const system = parsed.system;
|
|
11071
|
+
if (!Array.isArray(system) || system.length === 0) {
|
|
11072
|
+
return { body: rawBody, removed: 0 };
|
|
11073
|
+
}
|
|
11074
|
+
const kept = [];
|
|
11075
|
+
let removed = 0;
|
|
11076
|
+
for (const block of system) {
|
|
11077
|
+
if (isBillingHeaderTextBlock(block)) {
|
|
11078
|
+
removed += 1;
|
|
11079
|
+
continue;
|
|
9749
11080
|
}
|
|
11081
|
+
kept.push(block);
|
|
9750
11082
|
}
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
const text = this.pendingFileChunks.join("");
|
|
9754
|
-
this.pendingFileChunks = [];
|
|
9755
|
-
this.pendingFileBytes = 0;
|
|
9756
|
-
await this.fileHandle.write(text, void 0, "utf-8");
|
|
11083
|
+
if (removed === 0) {
|
|
11084
|
+
return { body: rawBody, removed: 0 };
|
|
9757
11085
|
}
|
|
11086
|
+
if (kept.length === 0) {
|
|
11087
|
+
delete parsed.system;
|
|
11088
|
+
} else {
|
|
11089
|
+
parsed.system = kept;
|
|
11090
|
+
}
|
|
11091
|
+
return { body: JSON.stringify(parsed), removed };
|
|
9758
11092
|
}
|
|
9759
|
-
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
const
|
|
9765
|
-
|
|
9766
|
-
if (activeRequests !== 0) return;
|
|
9767
|
-
for (const resolve2 of drainWaiters) resolve2();
|
|
9768
|
-
drainWaiters.clear();
|
|
11093
|
+
const ROLE_ASSISTANT = "assistant";
|
|
11094
|
+
const ROLE_TOOL = "tool";
|
|
11095
|
+
function isObjectWithMessages(value) {
|
|
11096
|
+
if (!isPlainRecord(value)) return false;
|
|
11097
|
+
if (!Object.prototype.hasOwnProperty.call(value, "messages")) return false;
|
|
11098
|
+
const messages = safeGetOwnProperty(value, "messages");
|
|
11099
|
+
return Array.isArray(messages);
|
|
9769
11100
|
}
|
|
9770
|
-
function
|
|
9771
|
-
if (!
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
|
|
9778
|
-
|
|
9779
|
-
notifyDrained();
|
|
11101
|
+
function collectAssistantToolCallIds(message, into) {
|
|
11102
|
+
if (!isPlainRecord(message)) return;
|
|
11103
|
+
const toolCalls = safeGetOwnProperty(message, "tool_calls");
|
|
11104
|
+
if (!Array.isArray(toolCalls)) return;
|
|
11105
|
+
for (const tc of toolCalls) {
|
|
11106
|
+
if (!isPlainRecord(tc)) continue;
|
|
11107
|
+
const id = safeGetOwnProperty(tc, "id");
|
|
11108
|
+
if (typeof id === "string" && id.length > 0) {
|
|
11109
|
+
into.add(id);
|
|
9780
11110
|
}
|
|
9781
|
-
}
|
|
9782
|
-
}
|
|
9783
|
-
function stopRuntimeAdmission() {
|
|
9784
|
-
acceptingRequests = false;
|
|
9785
|
-
notifyDrained();
|
|
11111
|
+
}
|
|
9786
11112
|
}
|
|
9787
|
-
|
|
9788
|
-
|
|
9789
|
-
|
|
9790
|
-
|
|
9791
|
-
|
|
11113
|
+
function findOrphanToolMessageIndices(messages) {
|
|
11114
|
+
const knownToolCallIds = /* @__PURE__ */ new Set();
|
|
11115
|
+
const indices = [];
|
|
11116
|
+
const orphanIds = [];
|
|
11117
|
+
for (let i = 0; i < messages.length; i++) {
|
|
11118
|
+
const msg = messages[i];
|
|
11119
|
+
if (!isPlainRecord(msg)) continue;
|
|
11120
|
+
const role = safeGetOwnProperty(msg, "role");
|
|
11121
|
+
if (role === ROLE_ASSISTANT) {
|
|
11122
|
+
collectAssistantToolCallIds(msg, knownToolCallIds);
|
|
11123
|
+
continue;
|
|
11124
|
+
}
|
|
11125
|
+
if (role === ROLE_TOOL) {
|
|
11126
|
+
const id = safeGetOwnProperty(msg, "tool_call_id");
|
|
11127
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
11128
|
+
indices.push(i);
|
|
11129
|
+
orphanIds.push(null);
|
|
11130
|
+
continue;
|
|
11131
|
+
}
|
|
11132
|
+
if (!knownToolCallIds.has(id)) {
|
|
11133
|
+
indices.push(i);
|
|
11134
|
+
orphanIds.push(id);
|
|
11135
|
+
}
|
|
11136
|
+
}
|
|
11137
|
+
}
|
|
11138
|
+
return { indices, orphanIds };
|
|
9792
11139
|
}
|
|
9793
|
-
function
|
|
9794
|
-
|
|
11140
|
+
function stripOpenAIOrphanToolMessages(rawBody) {
|
|
11141
|
+
let parsed;
|
|
11142
|
+
try {
|
|
11143
|
+
parsed = JSON.parse(rawBody);
|
|
11144
|
+
} catch {
|
|
11145
|
+
return { body: rawBody, removed: 0, orphanIds: [] };
|
|
11146
|
+
}
|
|
11147
|
+
if (!isObjectWithMessages(parsed)) {
|
|
11148
|
+
return { body: rawBody, removed: 0, orphanIds: [] };
|
|
11149
|
+
}
|
|
11150
|
+
const messages = parsed.messages;
|
|
11151
|
+
const { indices, orphanIds } = findOrphanToolMessageIndices(messages);
|
|
11152
|
+
if (indices.length === 0) {
|
|
11153
|
+
return { body: rawBody, removed: 0, orphanIds: [] };
|
|
11154
|
+
}
|
|
11155
|
+
const dropSet = new Set(indices);
|
|
11156
|
+
const kept = [];
|
|
11157
|
+
for (let i = 0; i < messages.length; i++) {
|
|
11158
|
+
if (!dropSet.has(i)) kept.push(messages[i]);
|
|
11159
|
+
}
|
|
11160
|
+
parsed.messages = kept;
|
|
11161
|
+
return { body: JSON.stringify(parsed), removed: indices.length, orphanIds };
|
|
9795
11162
|
}
|
|
9796
11163
|
const PATH_V1_CHAT_COMPLETIONS = "/v1/chat/completions";
|
|
9797
11164
|
function buildUpstreamUrl(upstreamBase, normalizedPath) {
|
|
@@ -10281,10 +11648,14 @@ async function handleAdmittedProxy(req) {
|
|
|
10281
11648
|
body: bodyToForward,
|
|
10282
11649
|
signal: req.signal
|
|
10283
11650
|
},
|
|
10284
|
-
{
|
|
11651
|
+
{
|
|
11652
|
+
allowLoopback: ALLOW_LOOPBACK$2,
|
|
11653
|
+
deadlineMs: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.fetchDeadlineMs,
|
|
11654
|
+
responseBytes: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.fetchResponseBytes
|
|
11655
|
+
}
|
|
10285
11656
|
);
|
|
10286
11657
|
if (!fetchResult.ok) {
|
|
10287
|
-
const status = fetchResult.kind === "network-policy" ? STATUS_FORBIDDEN : STATUS_BAD_GATEWAY;
|
|
11658
|
+
const status = fetchResult.kind === "network-policy" ? STATUS_FORBIDDEN : fetchResult.kind === "resource-limit" ? STATUS_PAYLOAD_TOO_LARGE : STATUS_BAD_GATEWAY;
|
|
10288
11659
|
logger.warn(`[handler] Safe fetch blocked ${upstreamUrl}: ${fetchResult.reason}`);
|
|
10289
11660
|
log.elapsedMs = Date.now() - startTime;
|
|
10290
11661
|
log.responseStatus = status;
|
|
@@ -10292,7 +11663,7 @@ async function handleAdmittedProxy(req) {
|
|
|
10292
11663
|
appendLogEntry({ ...buildFileLogEntry(log, upstreamUrl), error: fetchResult.reason });
|
|
10293
11664
|
await persistFinalizedLogUpdate(log);
|
|
10294
11665
|
return new Response(
|
|
10295
|
-
fetchResult.kind === "network-policy" ? "Forbidden: upstream URL is not permitted" : "Proxy error: upstream redirects are not permitted",
|
|
11666
|
+
fetchResult.kind === "network-policy" ? "Forbidden: upstream URL is not permitted" : fetchResult.kind === "resource-limit" ? "Proxy error: upstream response exceeded configured limits" : fetchResult.kind === "timeout" ? "Proxy error: upstream request timed out" : "Proxy error: upstream redirects are not permitted",
|
|
10296
11667
|
{ status }
|
|
10297
11668
|
);
|
|
10298
11669
|
}
|
|
@@ -10394,23 +11765,62 @@ async function handleProxy(req) {
|
|
|
10394
11765
|
const Route$Q = createFileRoute("/proxy/$")({
|
|
10395
11766
|
server: {
|
|
10396
11767
|
handlers: {
|
|
10397
|
-
GET: ({ request }) => handleProxy(
|
|
10398
|
-
POST: ({ request }) => handleProxy(
|
|
10399
|
-
PUT: ({ request }) => handleProxy(
|
|
10400
|
-
DELETE: ({ request }) => handleProxy(
|
|
10401
|
-
PATCH: ({ request }) => handleProxy(
|
|
10402
|
-
OPTIONS: ({ request }) => handleProxy(
|
|
11768
|
+
GET: ({ request: request2 }) => handleProxy(request2),
|
|
11769
|
+
POST: ({ request: request2 }) => handleProxy(request2),
|
|
11770
|
+
PUT: ({ request: request2 }) => handleProxy(request2),
|
|
11771
|
+
DELETE: ({ request: request2 }) => handleProxy(request2),
|
|
11772
|
+
PATCH: ({ request: request2 }) => handleProxy(request2),
|
|
11773
|
+
OPTIONS: ({ request: request2 }) => handleProxy(request2)
|
|
11774
|
+
}
|
|
11775
|
+
}
|
|
11776
|
+
});
|
|
11777
|
+
function summarizeGcResult(result) {
|
|
11778
|
+
if (result === null) return null;
|
|
11779
|
+
return {
|
|
11780
|
+
checkedAt: result.checkedAt,
|
|
11781
|
+
filesDeleted: result.filesDeleted,
|
|
11782
|
+
bytesDeleted: result.bytesDeleted,
|
|
11783
|
+
errors: result.errors.length,
|
|
11784
|
+
beforeTotalBytes: result.before.totalBytes,
|
|
11785
|
+
afterTotalBytes: result.after.totalBytes
|
|
11786
|
+
};
|
|
11787
|
+
}
|
|
11788
|
+
function getStorageResponse() {
|
|
11789
|
+
const logStats = getLogStorageStats();
|
|
11790
|
+
const inventory = getStorageLifecycleInventory();
|
|
11791
|
+
const policy = resolveStorageLifecycleQuotaPolicy();
|
|
11792
|
+
const reclaimableBytes = inventory.artifacts.filter(
|
|
11793
|
+
(artifact) => artifact.cleanupBehavior === "retention-gc" || artifact.cleanupBehavior === "orphan-reclaim"
|
|
11794
|
+
).reduce((sum, artifact) => sum + artifact.bytes, 0);
|
|
11795
|
+
return {
|
|
11796
|
+
memoryCount: logStats.memoryCount,
|
|
11797
|
+
inventory,
|
|
11798
|
+
policy,
|
|
11799
|
+
reclaimableBytes,
|
|
11800
|
+
lastGc: summarizeGcResult(getLastStorageLifecycleGcResult())
|
|
11801
|
+
};
|
|
11802
|
+
}
|
|
11803
|
+
const Route$P = createFileRoute("/api/storage")({
|
|
11804
|
+
server: {
|
|
11805
|
+
handlers: {
|
|
11806
|
+
GET: () => {
|
|
11807
|
+
return Response.json(getStorageResponse(), { headers: { "cache-control": "no-store" } });
|
|
11808
|
+
},
|
|
11809
|
+
POST: () => {
|
|
11810
|
+
runStorageLifecycleGc();
|
|
11811
|
+
return Response.json(getStorageResponse(), { headers: { "cache-control": "no-store" } });
|
|
11812
|
+
}
|
|
10403
11813
|
}
|
|
10404
11814
|
}
|
|
10405
11815
|
});
|
|
10406
11816
|
function parseBooleanFlag(value) {
|
|
10407
11817
|
return value === "1" || value === "true";
|
|
10408
11818
|
}
|
|
10409
|
-
const Route$
|
|
11819
|
+
const Route$O = createFileRoute("/api/sessions")({
|
|
10410
11820
|
server: {
|
|
10411
11821
|
handlers: {
|
|
10412
|
-
GET: async ({ request }) => {
|
|
10413
|
-
const url2 = new URL(
|
|
11822
|
+
GET: async ({ request: request2 }) => {
|
|
11823
|
+
const url2 = new URL(request2.url);
|
|
10414
11824
|
const sessionId = url2.searchParams.get("sessionId");
|
|
10415
11825
|
if (sessionId !== null) {
|
|
10416
11826
|
if (sessionId.length === 0) {
|
|
@@ -10539,7 +11949,7 @@ function runtimeDiscoveryResponse(env = process.env) {
|
|
|
10539
11949
|
headers: { "cache-control": "no-store" }
|
|
10540
11950
|
});
|
|
10541
11951
|
}
|
|
10542
|
-
const Route$
|
|
11952
|
+
const Route$N = createFileRoute("/api/runtime")({
|
|
10543
11953
|
server: {
|
|
10544
11954
|
handlers: {
|
|
10545
11955
|
GET: () => runtimeDiscoveryResponse()
|
|
@@ -11948,20 +13358,20 @@ function readInspectorRunEvidence(runId) {
|
|
|
11948
13358
|
}
|
|
11949
13359
|
return serviceOk({ run, evidence: run.evidence, markdown });
|
|
11950
13360
|
}
|
|
11951
|
-
async function readJsonBody$6(
|
|
13361
|
+
async function readJsonBody$6(request2) {
|
|
11952
13362
|
try {
|
|
11953
|
-
const raw = await
|
|
13363
|
+
const raw = await request2.text();
|
|
11954
13364
|
if (raw.trim().length === 0) return { ok: true, value: void 0 };
|
|
11955
13365
|
return { ok: true, value: JSON.parse(raw) };
|
|
11956
13366
|
} catch {
|
|
11957
13367
|
return { ok: false };
|
|
11958
13368
|
}
|
|
11959
13369
|
}
|
|
11960
|
-
const Route$
|
|
13370
|
+
const Route$M = createFileRoute("/api/runs")({
|
|
11961
13371
|
server: {
|
|
11962
13372
|
handlers: {
|
|
11963
|
-
GET: ({ request }) => {
|
|
11964
|
-
const url2 = new URL(
|
|
13373
|
+
GET: ({ request: request2 }) => {
|
|
13374
|
+
const url2 = new URL(request2.url);
|
|
11965
13375
|
if (url2.searchParams.get("failures") === "1") {
|
|
11966
13376
|
const limitResult = parseBoundedIntegerParameter({
|
|
11967
13377
|
value: url2.searchParams.get("limit"),
|
|
@@ -11976,8 +13386,8 @@ const Route$N = createFileRoute("/api/runs")({
|
|
|
11976
13386
|
}
|
|
11977
13387
|
return Response.json(listInspectorRuns());
|
|
11978
13388
|
},
|
|
11979
|
-
POST: async ({ request }) => {
|
|
11980
|
-
const body = await readJsonBody$6(
|
|
13389
|
+
POST: async ({ request: request2 }) => {
|
|
13390
|
+
const body = await readJsonBody$6(request2);
|
|
11981
13391
|
if (!body.ok) {
|
|
11982
13392
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
11983
13393
|
}
|
|
@@ -12009,16 +13419,16 @@ const ProviderInputSchema = object({
|
|
|
12009
13419
|
modelMetadata: ProviderModelMetadataListSchema.optional(),
|
|
12010
13420
|
source: _enum(["company", "personal"]).optional()
|
|
12011
13421
|
});
|
|
12012
|
-
const Route$
|
|
13422
|
+
const Route$L = createFileRoute("/api/providers")({
|
|
12013
13423
|
server: {
|
|
12014
13424
|
handlers: {
|
|
12015
13425
|
GET: () => {
|
|
12016
13426
|
return Response.json(redactProviders(getProviders()));
|
|
12017
13427
|
},
|
|
12018
|
-
POST: async ({ request }) => {
|
|
13428
|
+
POST: async ({ request: request2 }) => {
|
|
12019
13429
|
let body;
|
|
12020
13430
|
try {
|
|
12021
|
-
body = await
|
|
13431
|
+
body = await request2.json();
|
|
12022
13432
|
} catch {
|
|
12023
13433
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
12024
13434
|
}
|
|
@@ -13637,8 +15047,8 @@ function firstModel(provider) {
|
|
|
13637
15047
|
}
|
|
13638
15048
|
return void 0;
|
|
13639
15049
|
}
|
|
13640
|
-
function providerCandidates(
|
|
13641
|
-
if (
|
|
15050
|
+
function providerCandidates(request2) {
|
|
15051
|
+
if (request2.providerId === void 0) {
|
|
13642
15052
|
return getProviders().toSorted((left, right) => {
|
|
13643
15053
|
const leftCompanyRank = left.source === "company" ? 0 : 1;
|
|
13644
15054
|
const rightCompanyRank = right.source === "company" ? 0 : 1;
|
|
@@ -13646,12 +15056,12 @@ function providerCandidates(request) {
|
|
|
13646
15056
|
return left.name.localeCompare(right.name);
|
|
13647
15057
|
});
|
|
13648
15058
|
}
|
|
13649
|
-
const provider = getProvider(
|
|
15059
|
+
const provider = getProvider(request2.providerId);
|
|
13650
15060
|
return provider === void 0 ? [] : [provider];
|
|
13651
15061
|
}
|
|
13652
|
-
function selectProvider(
|
|
13653
|
-
for (const provider of providerCandidates(
|
|
13654
|
-
const modelName =
|
|
15062
|
+
function selectProvider(request2) {
|
|
15063
|
+
for (const provider of providerCandidates(request2)) {
|
|
15064
|
+
const modelName = request2.model ?? firstModel(provider);
|
|
13655
15065
|
if (modelName === void 0) continue;
|
|
13656
15066
|
const responsesBaseUrl = nonEmpty$1(provider.openaiResponsesBaseUrl);
|
|
13657
15067
|
if (responsesBaseUrl !== void 0) {
|
|
@@ -13694,8 +15104,8 @@ function truncateContext(value, maxLength) {
|
|
|
13694
15104
|
return `${value.slice(0, maxLength)}
|
|
13695
15105
|
...[truncated ${String(value.length - maxLength)} chars]`;
|
|
13696
15106
|
}
|
|
13697
|
-
function lastUserQuestion(
|
|
13698
|
-
const history =
|
|
15107
|
+
function lastUserQuestion(request2) {
|
|
15108
|
+
const history = request2.history ?? [];
|
|
13699
15109
|
for (let index = history.length - 1; index >= 0; index -= 1) {
|
|
13700
15110
|
const message = history[index];
|
|
13701
15111
|
if (message !== void 0 && message.role === "user") return message.content;
|
|
@@ -13716,13 +15126,13 @@ function emptySummary() {
|
|
|
13716
15126
|
sourceCounts: { log: 0, knowledge: 0, alert: 0, run: 0, group: 0, provider: 0 }
|
|
13717
15127
|
};
|
|
13718
15128
|
}
|
|
13719
|
-
async function retrieveInspectorContext(
|
|
15129
|
+
async function retrieveInspectorContext(request2) {
|
|
13720
15130
|
try {
|
|
13721
15131
|
return await retrievePiAgentEvidence({
|
|
13722
|
-
question:
|
|
13723
|
-
sessionId:
|
|
13724
|
-
recentUserQuestion: lastUserQuestion(
|
|
13725
|
-
limits:
|
|
15132
|
+
question: request2.question,
|
|
15133
|
+
sessionId: request2.sessionId,
|
|
15134
|
+
recentUserQuestion: lastUserQuestion(request2),
|
|
15135
|
+
limits: request2.logLimit === void 0 ? void 0 : { maxEvidence: request2.logLimit }
|
|
13726
15136
|
});
|
|
13727
15137
|
} catch (err) {
|
|
13728
15138
|
return {
|
|
@@ -13872,11 +15282,11 @@ function lastAssistantMessage(messages) {
|
|
|
13872
15282
|
}
|
|
13873
15283
|
return null;
|
|
13874
15284
|
}
|
|
13875
|
-
function boundedHistory(
|
|
13876
|
-
const history = (
|
|
15285
|
+
function boundedHistory(request2) {
|
|
15286
|
+
const history = (request2.history ?? []).slice(-12).map((message) => `${message.role}: ${message.content.slice(0, MAX_HISTORY_CHARS)}`);
|
|
13877
15287
|
return history.length > 0 ? history.join("\n\n") : "No prior conversation in this request.";
|
|
13878
15288
|
}
|
|
13879
|
-
function buildContext(
|
|
15289
|
+
function buildContext(request2, retrieval) {
|
|
13880
15290
|
return {
|
|
13881
15291
|
systemPrompt: buildSystemPrompt(),
|
|
13882
15292
|
messages: [
|
|
@@ -13884,16 +15294,16 @@ function buildContext(request, retrieval) {
|
|
|
13884
15294
|
role: "user",
|
|
13885
15295
|
content: [
|
|
13886
15296
|
"Bounded conversation history:",
|
|
13887
|
-
boundedHistory(
|
|
15297
|
+
boundedHistory(request2),
|
|
13888
15298
|
"",
|
|
13889
|
-
buildUserPrompt(
|
|
15299
|
+
buildUserPrompt(request2.question, retrieval)
|
|
13890
15300
|
].join("\n"),
|
|
13891
15301
|
timestamp: Date.now()
|
|
13892
15302
|
}
|
|
13893
15303
|
]
|
|
13894
15304
|
};
|
|
13895
15305
|
}
|
|
13896
|
-
async function askPi(selected,
|
|
15306
|
+
async function askPi(selected, request2, retrieval) {
|
|
13897
15307
|
const { Agent } = await import(
|
|
13898
15308
|
/* @vite-ignore */
|
|
13899
15309
|
"../_libs/mariozechner__pi-agent-core.mjs"
|
|
@@ -13909,7 +15319,7 @@ async function askPi(selected, request, retrieval) {
|
|
|
13909
15319
|
sessionId: selected.provider.id,
|
|
13910
15320
|
maxRetryDelayMs: 1e4
|
|
13911
15321
|
});
|
|
13912
|
-
const context = buildContext(
|
|
15322
|
+
const context = buildContext(request2, retrieval);
|
|
13913
15323
|
await agent.prompt(context.messages);
|
|
13914
15324
|
const assistantMessage = lastAssistantMessage(agent.state.messages);
|
|
13915
15325
|
if (assistantMessage === null) {
|
|
@@ -13919,9 +15329,9 @@ async function askPi(selected, request, retrieval) {
|
|
|
13919
15329
|
if (answer !== "") return stripReasoningTags(answer);
|
|
13920
15330
|
return assistantMessage.errorMessage ?? "Inspector Assistant received an empty provider response.";
|
|
13921
15331
|
}
|
|
13922
|
-
async function runPiAgent(
|
|
13923
|
-
const retrieval = await retrieveInspectorContext(
|
|
13924
|
-
const selected = selectProvider(
|
|
15332
|
+
async function runPiAgent(request2) {
|
|
15333
|
+
const retrieval = await retrieveInspectorContext(request2);
|
|
15334
|
+
const selected = selectProvider(request2);
|
|
13925
15335
|
if (selected === null) {
|
|
13926
15336
|
return {
|
|
13927
15337
|
answer: "Inspector Assistant needs at least one Provider with OpenAI Responses, OpenAI Chat, or Anthropic Messages configured.",
|
|
@@ -13937,7 +15347,7 @@ async function runPiAgent(request) {
|
|
|
13937
15347
|
}
|
|
13938
15348
|
let answer;
|
|
13939
15349
|
try {
|
|
13940
|
-
answer = await askPi(selected,
|
|
15350
|
+
answer = await askPi(selected, request2, retrieval);
|
|
13941
15351
|
} catch (err) {
|
|
13942
15352
|
answer = `Inspector Assistant provider request failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
13943
15353
|
}
|
|
@@ -13953,13 +15363,13 @@ async function runPiAgent(request) {
|
|
|
13953
15363
|
queryStats: retrieval.queryStats
|
|
13954
15364
|
};
|
|
13955
15365
|
}
|
|
13956
|
-
const Route$
|
|
15366
|
+
const Route$K = createFileRoute("/api/pi-agent")({
|
|
13957
15367
|
server: {
|
|
13958
15368
|
handlers: {
|
|
13959
|
-
POST: async ({ request }) => {
|
|
15369
|
+
POST: async ({ request: request2 }) => {
|
|
13960
15370
|
let body;
|
|
13961
15371
|
try {
|
|
13962
|
-
body = await
|
|
15372
|
+
body = await request2.json();
|
|
13963
15373
|
} catch {
|
|
13964
15374
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
13965
15375
|
}
|
|
@@ -13976,7 +15386,7 @@ const Route$L = createFileRoute("/api/pi-agent")({
|
|
|
13976
15386
|
}
|
|
13977
15387
|
}
|
|
13978
15388
|
});
|
|
13979
|
-
const Route$
|
|
15389
|
+
const Route$J = createFileRoute("/api/models")({
|
|
13980
15390
|
server: {
|
|
13981
15391
|
handlers: {
|
|
13982
15392
|
GET: () => Response.json(getModels())
|
|
@@ -13984,7 +15394,7 @@ const Route$K = createFileRoute("/api/models")({
|
|
|
13984
15394
|
}
|
|
13985
15395
|
});
|
|
13986
15396
|
const name = "@tonyclaw/agent-inspector";
|
|
13987
|
-
const version = "3.1.
|
|
15397
|
+
const version = "3.1.21";
|
|
13988
15398
|
const packageJson = {
|
|
13989
15399
|
name,
|
|
13990
15400
|
version
|
|
@@ -14321,8 +15731,8 @@ async function getInstanceConnectionImpl(callApi2, name2) {
|
|
|
14321
15731
|
}
|
|
14322
15732
|
async function startInstanceImpl(callApi2, input) {
|
|
14323
15733
|
const path2 = instancePath(input.name, "/start");
|
|
14324
|
-
const
|
|
14325
|
-
const response = await callApi2(path2, jsonPost(InstanceStartRequestSchema.parse(
|
|
15734
|
+
const request2 = lifecycleRequest(input);
|
|
15735
|
+
const response = await callApi2(path2, jsonPost(InstanceStartRequestSchema.parse(request2)));
|
|
14326
15736
|
return validatedInstanceResponse(response, InstanceOperationOutputSchema, `POST ${path2}`);
|
|
14327
15737
|
}
|
|
14328
15738
|
async function stopInstanceImpl(callApi2, name2) {
|
|
@@ -14332,8 +15742,8 @@ async function stopInstanceImpl(callApi2, name2) {
|
|
|
14332
15742
|
}
|
|
14333
15743
|
async function restartInstanceImpl(callApi2, input) {
|
|
14334
15744
|
const path2 = instancePath(input.name, "/restart");
|
|
14335
|
-
const
|
|
14336
|
-
const response = await callApi2(path2, jsonPost(InstanceStartRequestSchema.parse(
|
|
15745
|
+
const request2 = lifecycleRequest(input);
|
|
15746
|
+
const response = await callApi2(path2, jsonPost(InstanceStartRequestSchema.parse(request2)));
|
|
14337
15747
|
return validatedInstanceResponse(response, InstanceOperationOutputSchema, `POST ${path2}`);
|
|
14338
15748
|
}
|
|
14339
15749
|
function groupEvidenceDir(groupId) {
|
|
@@ -15992,10 +17402,10 @@ async function getServer() {
|
|
|
15992
17402
|
}
|
|
15993
17403
|
return initPromise;
|
|
15994
17404
|
}
|
|
15995
|
-
async function handleMcpRequest(
|
|
15996
|
-
return runWithRequestPublicBasePath(
|
|
17405
|
+
async function handleMcpRequest(request2) {
|
|
17406
|
+
return runWithRequestPublicBasePath(request2, async () => {
|
|
15997
17407
|
const { transport } = await getServer();
|
|
15998
|
-
return transport.handleRequest(
|
|
17408
|
+
return transport.handleRequest(request2);
|
|
15999
17409
|
});
|
|
16000
17410
|
}
|
|
16001
17411
|
const TOOL_LIST_LOGS_DESC = `List recent captured agent/model proxy logs in reverse-chronological order with parsed previews. Useful for "what did I just send?" discovery.
|
|
@@ -17645,15 +19055,15 @@ const PROMPT_NAMES = [
|
|
|
17645
19055
|
"inspector_generate_group_report",
|
|
17646
19056
|
"inspector_extract_repro_steps"
|
|
17647
19057
|
];
|
|
17648
|
-
const Route$
|
|
19058
|
+
const Route$I = createFileRoute("/api/mcp")({
|
|
17649
19059
|
server: {
|
|
17650
19060
|
handlers: {
|
|
17651
19061
|
// The SDK may issue either POST, GET, or DELETE. TanStack Start only
|
|
17652
19062
|
// requires us to declare the methods we accept; routing by method is
|
|
17653
19063
|
// handled inside the transport.
|
|
17654
|
-
POST: ({ request }) => handleMcpRequest(
|
|
17655
|
-
GET: ({ request }) => handleMcpRequest(
|
|
17656
|
-
DELETE: ({ request }) => handleMcpRequest(
|
|
19064
|
+
POST: ({ request: request2 }) => handleMcpRequest(request2),
|
|
19065
|
+
GET: ({ request: request2 }) => handleMcpRequest(request2),
|
|
19066
|
+
DELETE: ({ request: request2 }) => handleMcpRequest(request2)
|
|
17657
19067
|
}
|
|
17658
19068
|
}
|
|
17659
19069
|
});
|
|
@@ -17763,11 +19173,11 @@ function parseOptionalLogId(value) {
|
|
|
17763
19173
|
const parsed = parseLogId(value);
|
|
17764
19174
|
return parsed === null ? { ok: false } : { ok: true, value: parsed };
|
|
17765
19175
|
}
|
|
17766
|
-
const Route$
|
|
19176
|
+
const Route$H = createFileRoute("/api/logs")({
|
|
17767
19177
|
server: {
|
|
17768
19178
|
handlers: {
|
|
17769
|
-
GET: async ({ request }) => {
|
|
17770
|
-
const url2 = new URL(
|
|
19179
|
+
GET: async ({ request: request2 }) => {
|
|
19180
|
+
const url2 = new URL(request2.url);
|
|
17771
19181
|
if (url2.searchParams.get("stats") === "1") {
|
|
17772
19182
|
return Response.json(getLogStorageStats());
|
|
17773
19183
|
}
|
|
@@ -17871,10 +19281,10 @@ const Route$I = createFileRoute("/api/logs")({
|
|
|
17871
19281
|
await listLogsPage({ sessionId, model, clientPid, offset, limit, includeBodies })
|
|
17872
19282
|
);
|
|
17873
19283
|
},
|
|
17874
|
-
DELETE: async ({ request }) => {
|
|
19284
|
+
DELETE: async ({ request: request2 }) => {
|
|
17875
19285
|
let body = void 0;
|
|
17876
19286
|
try {
|
|
17877
|
-
const raw = await
|
|
19287
|
+
const raw = await request2.text();
|
|
17878
19288
|
if (raw !== "") {
|
|
17879
19289
|
const parsed = JSON.parse(raw);
|
|
17880
19290
|
const result2 = DeleteLogsBodySchema.safeParse(parsed);
|
|
@@ -19681,10 +21091,10 @@ function instanceErrorResponse(error) {
|
|
|
19681
21091
|
function instanceResultResponse(result, successStatus = 200) {
|
|
19682
21092
|
return result.kind === "error" ? instanceErrorResponse(result.error) : Response.json(result.value, { status: successStatus, headers: NO_STORE_HEADERS$1 });
|
|
19683
21093
|
}
|
|
19684
|
-
async function parseInstanceBody(
|
|
21094
|
+
async function parseInstanceBody(request2, schema) {
|
|
19685
21095
|
let value;
|
|
19686
21096
|
try {
|
|
19687
|
-
const text = await
|
|
21097
|
+
const text = await request2.text();
|
|
19688
21098
|
value = text.trim().length === 0 ? {} : JSON.parse(text);
|
|
19689
21099
|
} catch {
|
|
19690
21100
|
return {
|
|
@@ -19723,7 +21133,7 @@ function instanceListResponse(instances) {
|
|
|
19723
21133
|
{ headers: NO_STORE_HEADERS$1 }
|
|
19724
21134
|
);
|
|
19725
21135
|
}
|
|
19726
|
-
const Route$
|
|
21136
|
+
const Route$G = createFileRoute("/api/instances")({
|
|
19727
21137
|
server: {
|
|
19728
21138
|
handlers: {
|
|
19729
21139
|
GET: async () => {
|
|
@@ -20033,6 +21443,11 @@ const tasks = /* @__PURE__ */ new Map();
|
|
|
20033
21443
|
const activeTasks = /* @__PURE__ */ new Map();
|
|
20034
21444
|
let acceptingTasks = true;
|
|
20035
21445
|
let shutdownForceTimer = null;
|
|
21446
|
+
function activeTaskLimit() {
|
|
21447
|
+
const configured = Number(process.env["ECOSYSTEM_TASK_ACTIVE_LIMIT"]);
|
|
21448
|
+
if (Number.isSafeInteger(configured) && configured > 0) return configured;
|
|
21449
|
+
return DEFAULT_RUNTIME_BUDGET_POLICY.queues.backgroundWorkers;
|
|
21450
|
+
}
|
|
20036
21451
|
function nowIso() {
|
|
20037
21452
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
20038
21453
|
}
|
|
@@ -20227,6 +21642,7 @@ function listEcosystemTasks() {
|
|
|
20227
21642
|
}
|
|
20228
21643
|
function startEcosystemTask(packageId, action) {
|
|
20229
21644
|
if (!acceptingTasks) return null;
|
|
21645
|
+
if (activeTasks.size >= activeTaskLimit()) return null;
|
|
20230
21646
|
if (action === "recipe") return null;
|
|
20231
21647
|
const definition = action === "runner-presets" ? findEcosystemPackage("eval-harness") : findEcosystemPackage(packageId);
|
|
20232
21648
|
if (definition === null) return null;
|
|
@@ -20253,6 +21669,7 @@ function startEcosystemTask(packageId, action) {
|
|
|
20253
21669
|
}
|
|
20254
21670
|
function startEcosystemRecipeTask(recipeId) {
|
|
20255
21671
|
if (!acceptingTasks) return null;
|
|
21672
|
+
if (activeTasks.size >= activeTaskLimit()) return null;
|
|
20256
21673
|
const recipe = findEcosystemRecipe(recipeId);
|
|
20257
21674
|
if (recipe === null) return null;
|
|
20258
21675
|
if (!recipe.runnable) return null;
|
|
@@ -20415,7 +21832,7 @@ function installRuntimeShutdownSignalHandlers() {
|
|
|
20415
21832
|
}
|
|
20416
21833
|
installRuntimeShutdownSignalHandlers();
|
|
20417
21834
|
logger.debug("Health endpoint loaded");
|
|
20418
|
-
const Route$
|
|
21835
|
+
const Route$F = createFileRoute("/api/health")({
|
|
20419
21836
|
server: {
|
|
20420
21837
|
handlers: {
|
|
20421
21838
|
GET: async () => {
|
|
@@ -20428,18 +21845,18 @@ const Route$G = createFileRoute("/api/health")({
|
|
|
20428
21845
|
}
|
|
20429
21846
|
}
|
|
20430
21847
|
});
|
|
20431
|
-
async function readJsonBody$5(
|
|
21848
|
+
async function readJsonBody$5(request2) {
|
|
20432
21849
|
try {
|
|
20433
|
-
const raw = await
|
|
21850
|
+
const raw = await request2.text();
|
|
20434
21851
|
if (raw.trim().length === 0) return { ok: true, value: void 0 };
|
|
20435
21852
|
return { ok: true, value: JSON.parse(raw) };
|
|
20436
21853
|
} catch {
|
|
20437
21854
|
return { ok: false };
|
|
20438
21855
|
}
|
|
20439
21856
|
}
|
|
20440
|
-
function readDeleteEvidenceFlag$1(
|
|
21857
|
+
function readDeleteEvidenceFlag$1(request2) {
|
|
20441
21858
|
try {
|
|
20442
|
-
const value = new URL(
|
|
21859
|
+
const value = new URL(request2.url).searchParams.get("deleteEvidence");
|
|
20443
21860
|
switch (value) {
|
|
20444
21861
|
case null:
|
|
20445
21862
|
return { ok: true, value: void 0 };
|
|
@@ -20456,12 +21873,12 @@ function readDeleteEvidenceFlag$1(request) {
|
|
|
20456
21873
|
return { ok: false };
|
|
20457
21874
|
}
|
|
20458
21875
|
}
|
|
20459
|
-
const Route$
|
|
21876
|
+
const Route$E = createFileRoute("/api/groups")({
|
|
20460
21877
|
server: {
|
|
20461
21878
|
handlers: {
|
|
20462
21879
|
GET: () => Response.json(listInspectorGroups()),
|
|
20463
|
-
POST: async ({ request }) => {
|
|
20464
|
-
const body = await readJsonBody$5(
|
|
21880
|
+
POST: async ({ request: request2 }) => {
|
|
21881
|
+
const body = await readJsonBody$5(request2);
|
|
20465
21882
|
if (!body.ok) {
|
|
20466
21883
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
20467
21884
|
}
|
|
@@ -20474,8 +21891,8 @@ const Route$F = createFileRoute("/api/groups")({
|
|
|
20474
21891
|
}
|
|
20475
21892
|
return Response.json(createInspectorGroup(parsed.data), { status: 201 });
|
|
20476
21893
|
},
|
|
20477
|
-
DELETE: ({ request }) => {
|
|
20478
|
-
const deleteEvidence = readDeleteEvidenceFlag$1(
|
|
21894
|
+
DELETE: ({ request: request2 }) => {
|
|
21895
|
+
const deleteEvidence = readDeleteEvidenceFlag$1(request2);
|
|
20479
21896
|
if (!deleteEvidence.ok) {
|
|
20480
21897
|
return Response.json(
|
|
20481
21898
|
{ error: "deleteEvidence must be one of true, false, 1, or 0" },
|
|
@@ -20500,16 +21917,16 @@ const RuntimeConfigPatchSchema = object({
|
|
|
20500
21917
|
}).strict().refine((v) => Object.keys(v).length > 0, {
|
|
20501
21918
|
message: "At least one field must be provided"
|
|
20502
21919
|
});
|
|
20503
|
-
const Route$
|
|
21920
|
+
const Route$D = createFileRoute("/api/config")({
|
|
20504
21921
|
server: {
|
|
20505
21922
|
handlers: {
|
|
20506
21923
|
GET: () => {
|
|
20507
21924
|
return Response.json(getConfig());
|
|
20508
21925
|
},
|
|
20509
|
-
PATCH: async ({ request }) => {
|
|
21926
|
+
PATCH: async ({ request: request2 }) => {
|
|
20510
21927
|
let body;
|
|
20511
21928
|
try {
|
|
20512
|
-
body = await
|
|
21929
|
+
body = await request2.json();
|
|
20513
21930
|
} catch {
|
|
20514
21931
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
20515
21932
|
}
|
|
@@ -20614,11 +22031,11 @@ function parseFilters(url2) {
|
|
|
20614
22031
|
source: sourceResult.success ? sourceResult.data : void 0
|
|
20615
22032
|
};
|
|
20616
22033
|
}
|
|
20617
|
-
const Route$
|
|
22034
|
+
const Route$C = createFileRoute("/api/alerts")({
|
|
20618
22035
|
server: {
|
|
20619
22036
|
handlers: {
|
|
20620
|
-
GET: async ({ request }) => {
|
|
20621
|
-
const url2 = new URL(
|
|
22037
|
+
GET: async ({ request: request2 }) => {
|
|
22038
|
+
const url2 = new URL(request2.url);
|
|
20622
22039
|
const limitResult = parseBoundedIntegerParameter({
|
|
20623
22040
|
value: url2.searchParams.get("limit"),
|
|
20624
22041
|
parameter: "limit",
|
|
@@ -20648,16 +22065,16 @@ const Route$D = createFileRoute("/api/alerts")({
|
|
|
20648
22065
|
}
|
|
20649
22066
|
}
|
|
20650
22067
|
});
|
|
20651
|
-
async function readJsonBody$4(
|
|
22068
|
+
async function readJsonBody$4(request2) {
|
|
20652
22069
|
try {
|
|
20653
|
-
const raw = await
|
|
22070
|
+
const raw = await request2.text();
|
|
20654
22071
|
if (raw.trim().length === 0) return { ok: true, value: {} };
|
|
20655
22072
|
return { ok: true, value: JSON.parse(raw) };
|
|
20656
22073
|
} catch {
|
|
20657
22074
|
return { ok: false };
|
|
20658
22075
|
}
|
|
20659
22076
|
}
|
|
20660
|
-
const Route$
|
|
22077
|
+
const Route$B = createFileRoute("/api/runs/$runId")({
|
|
20661
22078
|
server: {
|
|
20662
22079
|
handlers: {
|
|
20663
22080
|
GET: ({ params }) => {
|
|
@@ -20667,8 +22084,8 @@ const Route$C = createFileRoute("/api/runs/$runId")({
|
|
|
20667
22084
|
}
|
|
20668
22085
|
return Response.json(run.value);
|
|
20669
22086
|
},
|
|
20670
|
-
PATCH: async ({ params, request }) => {
|
|
20671
|
-
const body = await readJsonBody$4(
|
|
22087
|
+
PATCH: async ({ params, request: request2 }) => {
|
|
22088
|
+
const body = await readJsonBody$4(request2);
|
|
20672
22089
|
if (!body.ok) {
|
|
20673
22090
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
20674
22091
|
}
|
|
@@ -21190,7 +22607,7 @@ function consumeProviderScan(scanId, selectionIds, now = Date.now()) {
|
|
|
21190
22607
|
}
|
|
21191
22608
|
return selected;
|
|
21192
22609
|
}
|
|
21193
|
-
const Route$
|
|
22610
|
+
const Route$A = createFileRoute("/api/providers/scan")({
|
|
21194
22611
|
server: {
|
|
21195
22612
|
handlers: {
|
|
21196
22613
|
POST: () => {
|
|
@@ -21236,12 +22653,12 @@ const ProviderScanImportRequestSchema = object({
|
|
|
21236
22653
|
selectionIds: array(string().uuid()).min(1).max(100)
|
|
21237
22654
|
});
|
|
21238
22655
|
const ProviderFileImportSchema = union([string(), object({ providers: unknown() })]);
|
|
21239
|
-
const Route$
|
|
22656
|
+
const Route$z = createFileRoute("/api/providers/import")({
|
|
21240
22657
|
server: {
|
|
21241
22658
|
handlers: {
|
|
21242
|
-
POST: async ({ request }) => {
|
|
22659
|
+
POST: async ({ request: request2 }) => {
|
|
21243
22660
|
try {
|
|
21244
|
-
const rawBody = await
|
|
22661
|
+
const rawBody = await request2.text();
|
|
21245
22662
|
let jsonContent;
|
|
21246
22663
|
try {
|
|
21247
22664
|
const parsedBody = JSON.parse(rawBody);
|
|
@@ -21308,7 +22725,7 @@ function exportResponse(json, filename) {
|
|
|
21308
22725
|
}
|
|
21309
22726
|
});
|
|
21310
22727
|
}
|
|
21311
|
-
const Route$
|
|
22728
|
+
const Route$y = createFileRoute("/api/providers/export")({
|
|
21312
22729
|
server: {
|
|
21313
22730
|
handlers: {
|
|
21314
22731
|
GET: () => {
|
|
@@ -21317,8 +22734,8 @@ const Route$z = createFileRoute("/api/providers/export")({
|
|
|
21317
22734
|
`agent-inspector-providers-safe-${Date.now()}.json`
|
|
21318
22735
|
);
|
|
21319
22736
|
},
|
|
21320
|
-
POST: async ({ request }) => {
|
|
21321
|
-
const body = await
|
|
22737
|
+
POST: async ({ request: request2 }) => {
|
|
22738
|
+
const body = await request2.json().catch(() => null);
|
|
21322
22739
|
const parsed = SecretExportRequestSchema.safeParse(body);
|
|
21323
22740
|
if (!parsed.success) {
|
|
21324
22741
|
return Response.json(
|
|
@@ -21351,7 +22768,7 @@ const ProviderUpdateSchema = object({
|
|
|
21351
22768
|
modelMetadata: ProviderModelMetadataListSchema.optional(),
|
|
21352
22769
|
source: _enum(["company", "personal"]).optional()
|
|
21353
22770
|
});
|
|
21354
|
-
const Route$
|
|
22771
|
+
const Route$x = createFileRoute("/api/providers/$providerId")({
|
|
21355
22772
|
server: {
|
|
21356
22773
|
handlers: {
|
|
21357
22774
|
GET: ({ params }) => {
|
|
@@ -21362,10 +22779,10 @@ const Route$y = createFileRoute("/api/providers/$providerId")({
|
|
|
21362
22779
|
}
|
|
21363
22780
|
return Response.json(redactProvider(provider));
|
|
21364
22781
|
},
|
|
21365
|
-
PUT: async ({ params, request }) => {
|
|
22782
|
+
PUT: async ({ params, request: request2 }) => {
|
|
21366
22783
|
let body;
|
|
21367
22784
|
try {
|
|
21368
|
-
body = await
|
|
22785
|
+
body = await request2.json();
|
|
21369
22786
|
} catch {
|
|
21370
22787
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
21371
22788
|
}
|
|
@@ -21416,139 +22833,6 @@ const Route$y = createFileRoute("/api/providers/$providerId")({
|
|
|
21416
22833
|
}
|
|
21417
22834
|
}
|
|
21418
22835
|
});
|
|
21419
|
-
const INITIAL_STREAM_LOG_LIMIT = 100;
|
|
21420
|
-
const UPDATE_COALESCE_MS = 50;
|
|
21421
|
-
const sseClientLimiter = createCapacityLimiter(MAX_SSE_CLIENTS);
|
|
21422
|
-
function streamIncludesBodies(url2) {
|
|
21423
|
-
return url2.searchParams.get("compact") === "0";
|
|
21424
|
-
}
|
|
21425
|
-
function parseLastEventLogId(value) {
|
|
21426
|
-
if (value === null) return void 0;
|
|
21427
|
-
return parseLogId(value) ?? void 0;
|
|
21428
|
-
}
|
|
21429
|
-
function encodeSse(value, id) {
|
|
21430
|
-
const idLine = id === void 0 ? "" : `id: ${String(id)}
|
|
21431
|
-
`;
|
|
21432
|
-
return new TextEncoder().encode(`${idLine}data: ${JSON.stringify(value)}
|
|
21433
|
-
|
|
21434
|
-
`);
|
|
21435
|
-
}
|
|
21436
|
-
function createLogsStreamResponse(request, clientLimiter = sseClientLimiter) {
|
|
21437
|
-
const lastEventIdHeader = request.headers.get("last-event-id");
|
|
21438
|
-
const lastEventLogId = parseLastEventLogId(lastEventIdHeader);
|
|
21439
|
-
if (lastEventIdHeader !== null && lastEventLogId === void 0) {
|
|
21440
|
-
return Response.json({ error: "Invalid Last-Event-ID" }, { status: 400 });
|
|
21441
|
-
}
|
|
21442
|
-
const lease = clientLimiter.tryAcquire();
|
|
21443
|
-
if (lease === null) {
|
|
21444
|
-
return resourceLimitResponse({
|
|
21445
|
-
error: `SSE client capacity reached. At most ${String(clientLimiter.capacity)} live clients are allowed; retry after a connection closes.`,
|
|
21446
|
-
code: "sse-capacity-exceeded",
|
|
21447
|
-
status: 503,
|
|
21448
|
-
resource: "sse-clients",
|
|
21449
|
-
limit: clientLimiter.capacity,
|
|
21450
|
-
retryable: true,
|
|
21451
|
-
retryAfterSeconds: 1
|
|
21452
|
-
});
|
|
21453
|
-
}
|
|
21454
|
-
const url2 = new URL(request.url);
|
|
21455
|
-
const sessionId = url2.searchParams.get("sessionId") ?? void 0;
|
|
21456
|
-
const model = url2.searchParams.get("model") ?? void 0;
|
|
21457
|
-
const includeBodies = streamIncludesBodies(url2);
|
|
21458
|
-
let controllerRef = null;
|
|
21459
|
-
let cleanedUp = false;
|
|
21460
|
-
let updateTimer = null;
|
|
21461
|
-
const pendingUpdates = /* @__PURE__ */ new Map();
|
|
21462
|
-
const flushPendingUpdates = () => {
|
|
21463
|
-
updateTimer = null;
|
|
21464
|
-
const controller = controllerRef;
|
|
21465
|
-
if (controller === null) return;
|
|
21466
|
-
if (controller.desiredSize !== null && controller.desiredSize <= -16) {
|
|
21467
|
-
controller.close();
|
|
21468
|
-
cleanup();
|
|
21469
|
-
return;
|
|
21470
|
-
}
|
|
21471
|
-
for (const log of pendingUpdates.values()) {
|
|
21472
|
-
if (controller.desiredSize !== null && controller.desiredSize <= -16) {
|
|
21473
|
-
controller.close();
|
|
21474
|
-
cleanup();
|
|
21475
|
-
return;
|
|
21476
|
-
}
|
|
21477
|
-
const payloadLog = includeBodies ? log : compactLogForList(log);
|
|
21478
|
-
controller.enqueue(encodeSse({ type: "update", log: payloadLog }, log.id));
|
|
21479
|
-
}
|
|
21480
|
-
pendingUpdates.clear();
|
|
21481
|
-
};
|
|
21482
|
-
const unsubscribe = onLogUpdate((log) => {
|
|
21483
|
-
if (controllerRef === null) return;
|
|
21484
|
-
if (sessionId !== void 0 && getLogSessionId(log) !== sessionId) return;
|
|
21485
|
-
if (model !== void 0 && log.model !== model) return;
|
|
21486
|
-
pendingUpdates.set(log.id, log);
|
|
21487
|
-
if (updateTimer === null) updateTimer = setTimeout(flushPendingUpdates, UPDATE_COALESCE_MS);
|
|
21488
|
-
});
|
|
21489
|
-
const cleanup = () => {
|
|
21490
|
-
if (cleanedUp) return;
|
|
21491
|
-
cleanedUp = true;
|
|
21492
|
-
clearInterval(heartbeat);
|
|
21493
|
-
if (updateTimer !== null) clearTimeout(updateTimer);
|
|
21494
|
-
updateTimer = null;
|
|
21495
|
-
pendingUpdates.clear();
|
|
21496
|
-
unsubscribe();
|
|
21497
|
-
lease.release();
|
|
21498
|
-
controllerRef = null;
|
|
21499
|
-
request.signal.removeEventListener("abort", cleanup);
|
|
21500
|
-
};
|
|
21501
|
-
const heartbeat = setInterval(() => {
|
|
21502
|
-
if (controllerRef !== null) {
|
|
21503
|
-
try {
|
|
21504
|
-
controllerRef.enqueue(new TextEncoder().encode(": heartbeat\n\n"));
|
|
21505
|
-
} catch {
|
|
21506
|
-
cleanup();
|
|
21507
|
-
}
|
|
21508
|
-
}
|
|
21509
|
-
}, 3e4);
|
|
21510
|
-
request.signal.addEventListener("abort", cleanup, { once: true });
|
|
21511
|
-
const stream = new ReadableStream({
|
|
21512
|
-
async start(controller) {
|
|
21513
|
-
controllerRef = controller;
|
|
21514
|
-
try {
|
|
21515
|
-
const result = await listLogsCursorPage({
|
|
21516
|
-
sessionId,
|
|
21517
|
-
model,
|
|
21518
|
-
limit: INITIAL_STREAM_LOG_LIMIT,
|
|
21519
|
-
includeBodies,
|
|
21520
|
-
afterLogId: lastEventLogId,
|
|
21521
|
-
anchor: lastEventLogId === void 0 ? "newest" : void 0
|
|
21522
|
-
});
|
|
21523
|
-
if (cleanedUp) return;
|
|
21524
|
-
const logs = result.logs;
|
|
21525
|
-
const newestLogId = logs.at(-1)?.id;
|
|
21526
|
-
controller.enqueue(encodeSse({ type: "init", logs }, newestLogId));
|
|
21527
|
-
} catch (error) {
|
|
21528
|
-
cleanup();
|
|
21529
|
-
controller.error(error);
|
|
21530
|
-
}
|
|
21531
|
-
},
|
|
21532
|
-
cancel() {
|
|
21533
|
-
cleanup();
|
|
21534
|
-
}
|
|
21535
|
-
});
|
|
21536
|
-
return new Response(stream, {
|
|
21537
|
-
headers: {
|
|
21538
|
-
"Content-Type": "text/event-stream",
|
|
21539
|
-
"Cache-Control": "no-cache",
|
|
21540
|
-
Connection: "keep-alive",
|
|
21541
|
-
"X-Accel-Buffering": "no"
|
|
21542
|
-
}
|
|
21543
|
-
});
|
|
21544
|
-
}
|
|
21545
|
-
const Route$x = createFileRoute("/api/logs/stream")({
|
|
21546
|
-
server: {
|
|
21547
|
-
handlers: {
|
|
21548
|
-
GET: ({ request }) => createLogsStreamResponse(request)
|
|
21549
|
-
}
|
|
21550
|
-
}
|
|
21551
|
-
});
|
|
21552
22836
|
const DEFAULT_LOG_IMPORT_LIMITS = {
|
|
21553
22837
|
maxCompressedBytes: 200 * 1024 * 1024,
|
|
21554
22838
|
maxEntries: 1e4,
|
|
@@ -22150,8 +23434,8 @@ function declaredContentLength(value) {
|
|
|
22150
23434
|
const parsed = Number.parseInt(value, 10);
|
|
22151
23435
|
return Number.isSafeInteger(parsed) ? parsed : "invalid";
|
|
22152
23436
|
}
|
|
22153
|
-
async function readBoundedBody(
|
|
22154
|
-
const declaredBytes = declaredContentLength(
|
|
23437
|
+
async function readBoundedBody(request2, maxBytes) {
|
|
23438
|
+
const declaredBytes = declaredContentLength(request2.headers.get("content-length"));
|
|
22155
23439
|
if (declaredBytes === "invalid") {
|
|
22156
23440
|
return { ok: false, status: 400, message: "Invalid Content-Length header" };
|
|
22157
23441
|
}
|
|
@@ -22162,7 +23446,7 @@ async function readBoundedBody(request, maxBytes) {
|
|
|
22162
23446
|
message: `Import request exceeds maximum size of ${String(maxBytes)} bytes.`
|
|
22163
23447
|
};
|
|
22164
23448
|
}
|
|
22165
|
-
const body =
|
|
23449
|
+
const body = request2.body;
|
|
22166
23450
|
if (body === null) {
|
|
22167
23451
|
return { ok: false, status: 400, message: "Expected multipart form data" };
|
|
22168
23452
|
}
|
|
@@ -22198,14 +23482,14 @@ async function readBoundedBody(request, maxBytes) {
|
|
|
22198
23482
|
function isUploadedFile(value) {
|
|
22199
23483
|
return value instanceof File;
|
|
22200
23484
|
}
|
|
22201
|
-
async function readBoundedLogImportUpload(
|
|
22202
|
-
const contentType =
|
|
23485
|
+
async function readBoundedLogImportUpload(request2, options = {}) {
|
|
23486
|
+
const contentType = request2.headers.get("content-type");
|
|
22203
23487
|
if (contentType === null || !contentType.toLowerCase().startsWith("multipart/form-data")) {
|
|
22204
23488
|
return { ok: false, status: 400, message: "Expected multipart form data" };
|
|
22205
23489
|
}
|
|
22206
23490
|
const maxFileBytes = options.maxFileBytes ?? DEFAULT_LOG_IMPORT_LIMITS.maxCompressedBytes;
|
|
22207
23491
|
const maxRequestBytes = options.maxRequestBytes ?? MAX_LOG_IMPORT_REQUEST_BYTES;
|
|
22208
|
-
const body = await readBoundedBody(
|
|
23492
|
+
const body = await readBoundedBody(request2, maxRequestBytes);
|
|
22209
23493
|
if (!body.ok) return body;
|
|
22210
23494
|
let formData;
|
|
22211
23495
|
try {
|
|
@@ -22235,8 +23519,8 @@ async function readBoundedLogImportUpload(request, options = {}) {
|
|
|
22235
23519
|
const Route$w = createFileRoute("/api/logs/import")({
|
|
22236
23520
|
server: {
|
|
22237
23521
|
handlers: {
|
|
22238
|
-
POST: async ({ request }) => {
|
|
22239
|
-
const upload = await readBoundedLogImportUpload(
|
|
23522
|
+
POST: async ({ request: request2 }) => {
|
|
23523
|
+
const upload = await readBoundedLogImportUpload(request2);
|
|
22240
23524
|
if (!upload.ok) {
|
|
22241
23525
|
return Response.json({ error: upload.message }, { status: upload.status });
|
|
22242
23526
|
}
|
|
@@ -22501,7 +23785,11 @@ async function searchOpenClawGateway(query, project) {
|
|
|
22501
23785
|
sessionKey: getOpenClawSessionKey()
|
|
22502
23786
|
})
|
|
22503
23787
|
},
|
|
22504
|
-
{
|
|
23788
|
+
{
|
|
23789
|
+
bypass: true,
|
|
23790
|
+
deadlineMs: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataDeadlineMs,
|
|
23791
|
+
responseBytes: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataResponseBytes
|
|
23792
|
+
}
|
|
22505
23793
|
);
|
|
22506
23794
|
if (!fetchResult.ok) return { results: [], warning: fetchResult.reason };
|
|
22507
23795
|
const response = fetchResult.response;
|
|
@@ -22704,7 +23992,11 @@ async function promoteToOpenClaw(candidate) {
|
|
|
22704
23992
|
headers: authHeaders(),
|
|
22705
23993
|
body: JSON.stringify(buildOpenClawPayload(candidate))
|
|
22706
23994
|
},
|
|
22707
|
-
{
|
|
23995
|
+
{
|
|
23996
|
+
bypass: true,
|
|
23997
|
+
deadlineMs: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataDeadlineMs,
|
|
23998
|
+
responseBytes: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataResponseBytes
|
|
23999
|
+
}
|
|
22708
24000
|
);
|
|
22709
24001
|
if (!fetchResult.ok) return { success: false, error: fetchResult.reason };
|
|
22710
24002
|
const response = fetchResult.response;
|
|
@@ -22736,7 +24028,11 @@ async function searchOpenClaw(query, project) {
|
|
|
22736
24028
|
method: "GET",
|
|
22737
24029
|
headers: authHeaders()
|
|
22738
24030
|
},
|
|
22739
|
-
{
|
|
24031
|
+
{
|
|
24032
|
+
bypass: true,
|
|
24033
|
+
deadlineMs: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataDeadlineMs,
|
|
24034
|
+
responseBytes: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataResponseBytes
|
|
24035
|
+
}
|
|
22740
24036
|
);
|
|
22741
24037
|
if (!fetchResult.ok) return { results: [], warning: fetchResult.reason };
|
|
22742
24038
|
const response = fetchResult.response;
|
|
@@ -22776,8 +24072,8 @@ function bodySearchQuery(value) {
|
|
|
22776
24072
|
const Route$u = createFileRoute("/api/knowledge/search")({
|
|
22777
24073
|
server: {
|
|
22778
24074
|
handlers: {
|
|
22779
|
-
GET: async ({ request }) => {
|
|
22780
|
-
const url2 = new URL(
|
|
24075
|
+
GET: async ({ request: request2 }) => {
|
|
24076
|
+
const url2 = new URL(request2.url);
|
|
22781
24077
|
const query = url2.searchParams.get("q") ?? "";
|
|
22782
24078
|
const project = url2.searchParams.get("project") ?? void 0;
|
|
22783
24079
|
if (query === "") {
|
|
@@ -22786,10 +24082,10 @@ const Route$u = createFileRoute("/api/knowledge/search")({
|
|
|
22786
24082
|
if (query.length > MAX_SEARCH_QUERY_CHARS) return searchLimitResponse();
|
|
22787
24083
|
return Response.json(await searchOpenClaw(query, project));
|
|
22788
24084
|
},
|
|
22789
|
-
POST: async ({ request }) => {
|
|
24085
|
+
POST: async ({ request: request2 }) => {
|
|
22790
24086
|
let body;
|
|
22791
24087
|
try {
|
|
22792
|
-
body = await
|
|
24088
|
+
body = await request2.json();
|
|
22793
24089
|
} catch {
|
|
22794
24090
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
22795
24091
|
}
|
|
@@ -22809,8 +24105,8 @@ const Route$u = createFileRoute("/api/knowledge/search")({
|
|
|
22809
24105
|
const Route$t = createFileRoute("/api/knowledge/project-context")({
|
|
22810
24106
|
server: {
|
|
22811
24107
|
handlers: {
|
|
22812
|
-
GET: async ({ request }) => {
|
|
22813
|
-
const url2 = new URL(
|
|
24108
|
+
GET: async ({ request: request2 }) => {
|
|
24109
|
+
const url2 = new URL(request2.url);
|
|
22814
24110
|
const project = url2.searchParams.get("project") ?? "";
|
|
22815
24111
|
if (project === "") {
|
|
22816
24112
|
return Response.json({ error: "Missing project search parameter" }, { status: 400 });
|
|
@@ -22845,18 +24141,18 @@ const Route$r = createFileRoute("/api/instances/$name")({
|
|
|
22845
24141
|
}
|
|
22846
24142
|
}
|
|
22847
24143
|
});
|
|
22848
|
-
async function readJsonBody$3(
|
|
24144
|
+
async function readJsonBody$3(request2) {
|
|
22849
24145
|
try {
|
|
22850
|
-
const raw = await
|
|
24146
|
+
const raw = await request2.text();
|
|
22851
24147
|
if (raw.trim().length === 0) return { ok: true, value: {} };
|
|
22852
24148
|
return { ok: true, value: JSON.parse(raw) };
|
|
22853
24149
|
} catch {
|
|
22854
24150
|
return { ok: false };
|
|
22855
24151
|
}
|
|
22856
24152
|
}
|
|
22857
|
-
function readDeleteEvidenceFlag(
|
|
24153
|
+
function readDeleteEvidenceFlag(request2) {
|
|
22858
24154
|
try {
|
|
22859
|
-
const value = new URL(
|
|
24155
|
+
const value = new URL(request2.url).searchParams.get("deleteEvidence");
|
|
22860
24156
|
switch (value) {
|
|
22861
24157
|
case null:
|
|
22862
24158
|
return { ok: true, value: void 0 };
|
|
@@ -22883,8 +24179,8 @@ const Route$q = createFileRoute("/api/groups/$groupId")({
|
|
|
22883
24179
|
}
|
|
22884
24180
|
return Response.json(group.value);
|
|
22885
24181
|
},
|
|
22886
|
-
PATCH: async ({ params, request }) => {
|
|
22887
|
-
const body = await readJsonBody$3(
|
|
24182
|
+
PATCH: async ({ params, request: request2 }) => {
|
|
24183
|
+
const body = await readJsonBody$3(request2);
|
|
22888
24184
|
if (!body.ok) {
|
|
22889
24185
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
22890
24186
|
}
|
|
@@ -22901,8 +24197,8 @@ const Route$q = createFileRoute("/api/groups/$groupId")({
|
|
|
22901
24197
|
}
|
|
22902
24198
|
return Response.json(updated.value);
|
|
22903
24199
|
},
|
|
22904
|
-
DELETE: ({ params, request }) => {
|
|
22905
|
-
const deleteEvidence = readDeleteEvidenceFlag(
|
|
24200
|
+
DELETE: ({ params, request: request2 }) => {
|
|
24201
|
+
const deleteEvidence = readDeleteEvidenceFlag(request2);
|
|
22906
24202
|
if (!deleteEvidence.ok) {
|
|
22907
24203
|
return Response.json(
|
|
22908
24204
|
{ error: "deleteEvidence must be one of true, false, 1, or 0" },
|
|
@@ -22957,8 +24253,8 @@ const Route$n = createFileRoute("/api/config/paths")({
|
|
|
22957
24253
|
const Route$m = createFileRoute("/api/alerts/summary")({
|
|
22958
24254
|
server: {
|
|
22959
24255
|
handlers: {
|
|
22960
|
-
GET: async ({ request }) => {
|
|
22961
|
-
const url2 = new URL(
|
|
24256
|
+
GET: async ({ request: request2 }) => {
|
|
24257
|
+
const url2 = new URL(request2.url);
|
|
22962
24258
|
const scanLimitResult = parseBoundedIntegerParameter({
|
|
22963
24259
|
value: url2.searchParams.get("scanLimit"),
|
|
22964
24260
|
parameter: "scanLimit",
|
|
@@ -22977,9 +24273,9 @@ const Route$m = createFileRoute("/api/alerts/summary")({
|
|
|
22977
24273
|
}
|
|
22978
24274
|
}
|
|
22979
24275
|
});
|
|
22980
|
-
async function readJsonBody$2(
|
|
24276
|
+
async function readJsonBody$2(request2) {
|
|
22981
24277
|
try {
|
|
22982
|
-
const raw = await
|
|
24278
|
+
const raw = await request2.text();
|
|
22983
24279
|
if (raw.trim().length === 0) return { ok: true, value: void 0 };
|
|
22984
24280
|
return { ok: true, value: JSON.parse(raw) };
|
|
22985
24281
|
} catch {
|
|
@@ -22996,8 +24292,8 @@ const Route$l = createFileRoute("/api/runs/$runId/evidence")({
|
|
|
22996
24292
|
}
|
|
22997
24293
|
return Response.json(evidence.value);
|
|
22998
24294
|
},
|
|
22999
|
-
POST: async ({ params, request }) => {
|
|
23000
|
-
const body = await readJsonBody$2(
|
|
24295
|
+
POST: async ({ params, request: request2 }) => {
|
|
24296
|
+
const body = await readJsonBody$2(request2);
|
|
23001
24297
|
if (!body.ok) {
|
|
23002
24298
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
23003
24299
|
}
|
|
@@ -23011,7 +24307,7 @@ const Route$l = createFileRoute("/api/runs/$runId/evidence")({
|
|
|
23011
24307
|
const exported = await exportInspectorRunEvidence(
|
|
23012
24308
|
params.runId,
|
|
23013
24309
|
parsed.data,
|
|
23014
|
-
new URL(
|
|
24310
|
+
new URL(request2.url).origin
|
|
23015
24311
|
);
|
|
23016
24312
|
if (!exported.ok) {
|
|
23017
24313
|
return Response.json({ error: exported.message }, { status: exported.status });
|
|
@@ -23919,12 +25215,12 @@ async function readRegistryJson(response) {
|
|
|
23919
25215
|
const Route$j = createFileRoute("/api/providers/$providerId/model-metadata")({
|
|
23920
25216
|
server: {
|
|
23921
25217
|
handlers: {
|
|
23922
|
-
POST: async ({ params, request }) => {
|
|
25218
|
+
POST: async ({ params, request: request2 }) => {
|
|
23923
25219
|
const provider = getProvider(params.providerId);
|
|
23924
25220
|
if (provider === void 0) {
|
|
23925
25221
|
return Response.json({ error: "Provider not found" }, { status: 404 });
|
|
23926
25222
|
}
|
|
23927
|
-
const parsed = RefreshModelMetadataInputSchema.safeParse(await
|
|
25223
|
+
const parsed = RefreshModelMetadataInputSchema.safeParse(await request2.json());
|
|
23928
25224
|
if (!parsed.success) {
|
|
23929
25225
|
return Response.json({ error: parsed.error.message }, { status: 400 });
|
|
23930
25226
|
}
|
|
@@ -23958,9 +25254,16 @@ const Route$j = createFileRoute("/api/providers/$providerId/model-metadata")({
|
|
|
23958
25254
|
}
|
|
23959
25255
|
let response;
|
|
23960
25256
|
try {
|
|
23961
|
-
const fetchResult = await safeFetch(
|
|
23962
|
-
|
|
23963
|
-
|
|
25257
|
+
const fetchResult = await safeFetch(
|
|
25258
|
+
sourceUrl,
|
|
25259
|
+
{
|
|
25260
|
+
headers: { Accept: "application/json" }
|
|
25261
|
+
},
|
|
25262
|
+
{
|
|
25263
|
+
deadlineMs: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataDeadlineMs,
|
|
25264
|
+
responseBytes: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataResponseBytes
|
|
25265
|
+
}
|
|
25266
|
+
);
|
|
23964
25267
|
if (!fetchResult.ok) {
|
|
23965
25268
|
return Response.json(
|
|
23966
25269
|
{ error: `Model metadata URL blocked: ${fetchResult.reason}` },
|
|
@@ -24034,14 +25337,14 @@ const ReplayRequestSchema = object({
|
|
|
24034
25337
|
const Route$i = createFileRoute("/api/logs/$id/replay")({
|
|
24035
25338
|
server: {
|
|
24036
25339
|
handlers: {
|
|
24037
|
-
POST: async ({ params, request }) => {
|
|
25340
|
+
POST: async ({ params, request: request2 }) => {
|
|
24038
25341
|
const id = parseLogId(params.id);
|
|
24039
25342
|
if (id === null) {
|
|
24040
25343
|
return Response.json({ error: "Invalid log ID" }, { status: 400 });
|
|
24041
25344
|
}
|
|
24042
25345
|
let replayRequest;
|
|
24043
25346
|
try {
|
|
24044
|
-
const raw = await
|
|
25347
|
+
const raw = await request2.json();
|
|
24045
25348
|
const parsed = ReplayRequestSchema.safeParse(raw);
|
|
24046
25349
|
if (!parsed.success) {
|
|
24047
25350
|
return Response.json({ error: "Invalid request body" }, { status: 400 });
|
|
@@ -24099,7 +25402,11 @@ const Route$i = createFileRoute("/api/logs/$id/replay")({
|
|
|
24099
25402
|
headers,
|
|
24100
25403
|
body: modifiedBody
|
|
24101
25404
|
},
|
|
24102
|
-
{
|
|
25405
|
+
{
|
|
25406
|
+
allowLoopback: process.env["AGENT_INSPECTOR_ALLOW_LOOPBACK"] === "1",
|
|
25407
|
+
deadlineMs: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.replayDeadlineMs,
|
|
25408
|
+
responseBytes: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.replayResponseBytes
|
|
25409
|
+
}
|
|
24103
25410
|
);
|
|
24104
25411
|
if (!fetchResult.ok) {
|
|
24105
25412
|
return Response.json({
|
|
@@ -24353,12 +25660,12 @@ function parseIntegerParam(value, fallback) {
|
|
|
24353
25660
|
const Route$g = createFileRoute("/api/logs/$id/body")({
|
|
24354
25661
|
server: {
|
|
24355
25662
|
handlers: {
|
|
24356
|
-
GET: async ({ params, request }) => {
|
|
25663
|
+
GET: async ({ params, request: request2 }) => {
|
|
24357
25664
|
const id = parseLogId(params.id);
|
|
24358
25665
|
if (id === null) {
|
|
24359
25666
|
return Response.json({ error: "Invalid log ID" }, { status: 400 });
|
|
24360
25667
|
}
|
|
24361
|
-
const url2 = new URL(
|
|
25668
|
+
const url2 = new URL(request2.url);
|
|
24362
25669
|
const partResult = LogBodyPartSchema.safeParse(url2.searchParams.get("part"));
|
|
24363
25670
|
if (!partResult.success) {
|
|
24364
25671
|
return Response.json({ error: "Invalid body part" }, { status: 400 });
|
|
@@ -24400,7 +25707,7 @@ object({
|
|
|
24400
25707
|
const Route$f = createFileRoute("/api/knowledge/candidates/$candidateId")({
|
|
24401
25708
|
server: {
|
|
24402
25709
|
handlers: {
|
|
24403
|
-
PATCH: async ({ params, request }) => {
|
|
25710
|
+
PATCH: async ({ params, request: request2 }) => {
|
|
24404
25711
|
const existing = getCandidate(params.candidateId);
|
|
24405
25712
|
if (existing === null) {
|
|
24406
25713
|
return Response.json({ error: "Knowledge candidate not found" }, { status: 404 });
|
|
@@ -24411,7 +25718,7 @@ const Route$f = createFileRoute("/api/knowledge/candidates/$candidateId")({
|
|
|
24411
25718
|
{ status: 409 }
|
|
24412
25719
|
);
|
|
24413
25720
|
}
|
|
24414
|
-
const raw = await
|
|
25721
|
+
const raw = await request2.json().catch(() => null);
|
|
24415
25722
|
const parsed = CandidateUpdateSchema.safeParse(raw);
|
|
24416
25723
|
if (!parsed.success) {
|
|
24417
25724
|
return Response.json({ error: parsed.error.message }, { status: 400 });
|
|
@@ -24428,8 +25735,8 @@ const Route$f = createFileRoute("/api/knowledge/candidates/$candidateId")({
|
|
|
24428
25735
|
const Route$e = createFileRoute("/api/instances/$name/stop")({
|
|
24429
25736
|
server: {
|
|
24430
25737
|
handlers: {
|
|
24431
|
-
POST: async ({ params, request }) => {
|
|
24432
|
-
const body = await parseInstanceBody(
|
|
25738
|
+
POST: async ({ params, request: request2 }) => {
|
|
25739
|
+
const body = await parseInstanceBody(request2, InstanceEmptyRequestSchema);
|
|
24433
25740
|
if (!body.ok) return body.response;
|
|
24434
25741
|
const selfError = selfLifecycleError(params.name);
|
|
24435
25742
|
if (selfError !== null) return instanceErrorResponse(selfError);
|
|
@@ -24441,8 +25748,8 @@ const Route$e = createFileRoute("/api/instances/$name/stop")({
|
|
|
24441
25748
|
const Route$d = createFileRoute("/api/instances/$name/start")({
|
|
24442
25749
|
server: {
|
|
24443
25750
|
handlers: {
|
|
24444
|
-
POST: async ({ params, request }) => {
|
|
24445
|
-
const body = await parseInstanceBody(
|
|
25751
|
+
POST: async ({ params, request: request2 }) => {
|
|
25752
|
+
const body = await parseInstanceBody(request2, InstanceStartRequestSchema);
|
|
24446
25753
|
if (!body.ok) return body.response;
|
|
24447
25754
|
const result = await startNamedInstance(params.name, body.value);
|
|
24448
25755
|
const status = result.kind === "success" && result.value.outcome === "created" ? 201 : 200;
|
|
@@ -24454,8 +25761,8 @@ const Route$d = createFileRoute("/api/instances/$name/start")({
|
|
|
24454
25761
|
const Route$c = createFileRoute("/api/instances/$name/restart")({
|
|
24455
25762
|
server: {
|
|
24456
25763
|
handlers: {
|
|
24457
|
-
POST: async ({ params, request }) => {
|
|
24458
|
-
const body = await parseInstanceBody(
|
|
25764
|
+
POST: async ({ params, request: request2 }) => {
|
|
25765
|
+
const body = await parseInstanceBody(request2, InstanceStartRequestSchema);
|
|
24459
25766
|
if (!body.ok) return body.response;
|
|
24460
25767
|
const selfError = selfLifecycleError(params.name);
|
|
24461
25768
|
if (selfError !== null) return instanceErrorResponse(selfError);
|
|
@@ -24471,9 +25778,9 @@ const Route$b = createFileRoute("/api/instances/$name/connection")({
|
|
|
24471
25778
|
}
|
|
24472
25779
|
}
|
|
24473
25780
|
});
|
|
24474
|
-
async function readJsonBody$1(
|
|
25781
|
+
async function readJsonBody$1(request2) {
|
|
24475
25782
|
try {
|
|
24476
|
-
const raw = await
|
|
25783
|
+
const raw = await request2.text();
|
|
24477
25784
|
if (raw.trim().length === 0) return { ok: true, value: {} };
|
|
24478
25785
|
return { ok: true, value: JSON.parse(raw) };
|
|
24479
25786
|
} catch {
|
|
@@ -24490,8 +25797,8 @@ const Route$a = createFileRoute("/api/groups/$groupId/sessions")({
|
|
|
24490
25797
|
}
|
|
24491
25798
|
return Response.json(sessions2.value);
|
|
24492
25799
|
},
|
|
24493
|
-
POST: async ({ params, request }) => {
|
|
24494
|
-
const body = await readJsonBody$1(
|
|
25800
|
+
POST: async ({ params, request: request2 }) => {
|
|
25801
|
+
const body = await readJsonBody$1(request2);
|
|
24495
25802
|
if (!body.ok) {
|
|
24496
25803
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
24497
25804
|
}
|
|
@@ -24511,9 +25818,9 @@ const Route$a = createFileRoute("/api/groups/$groupId/sessions")({
|
|
|
24511
25818
|
}
|
|
24512
25819
|
}
|
|
24513
25820
|
});
|
|
24514
|
-
async function readJsonBody(
|
|
25821
|
+
async function readJsonBody(request2) {
|
|
24515
25822
|
try {
|
|
24516
|
-
const raw = await
|
|
25823
|
+
const raw = await request2.text();
|
|
24517
25824
|
if (raw.trim().length === 0) return { ok: true, value: void 0 };
|
|
24518
25825
|
return { ok: true, value: JSON.parse(raw) };
|
|
24519
25826
|
} catch {
|
|
@@ -24530,8 +25837,8 @@ const Route$9 = createFileRoute("/api/groups/$groupId/evidence")({
|
|
|
24530
25837
|
}
|
|
24531
25838
|
return Response.json(evidence.value);
|
|
24532
25839
|
},
|
|
24533
|
-
POST: async ({ params, request }) => {
|
|
24534
|
-
const body = await readJsonBody(
|
|
25840
|
+
POST: async ({ params, request: request2 }) => {
|
|
25841
|
+
const body = await readJsonBody(request2);
|
|
24535
25842
|
if (!body.ok) {
|
|
24536
25843
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
24537
25844
|
}
|
|
@@ -24545,7 +25852,7 @@ const Route$9 = createFileRoute("/api/groups/$groupId/evidence")({
|
|
|
24545
25852
|
const exported = await exportInspectorGroupEvidence(
|
|
24546
25853
|
params.groupId,
|
|
24547
25854
|
parsed.data,
|
|
24548
|
-
new URL(
|
|
25855
|
+
new URL(request2.url).origin
|
|
24549
25856
|
);
|
|
24550
25857
|
if (!exported.ok) {
|
|
24551
25858
|
return Response.json({ error: exported.message }, { status: exported.status });
|
|
@@ -24885,14 +26192,14 @@ async function logModelResults(displayName, providerName, providerSessionId, ant
|
|
|
24885
26192
|
const Route$7 = createFileRoute("/api/providers/$providerId/test/log")({
|
|
24886
26193
|
server: {
|
|
24887
26194
|
handlers: {
|
|
24888
|
-
POST: async ({ params, request }) => {
|
|
26195
|
+
POST: async ({ params, request: request2 }) => {
|
|
24889
26196
|
const provider = getProvider(params.providerId);
|
|
24890
26197
|
if (!provider) {
|
|
24891
26198
|
return Response.json({ error: "Provider not found" }, { status: 404 });
|
|
24892
26199
|
}
|
|
24893
26200
|
let body;
|
|
24894
26201
|
try {
|
|
24895
|
-
body = await
|
|
26202
|
+
body = await request2.json();
|
|
24896
26203
|
} catch {
|
|
24897
26204
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
24898
26205
|
}
|
|
@@ -25210,12 +26517,12 @@ const NO_STORE_HEADERS = { "Cache-Control": "no-store" };
|
|
|
25210
26517
|
function noStoreJson(body, status = 200) {
|
|
25211
26518
|
return Response.json(body, { status, headers: NO_STORE_HEADERS });
|
|
25212
26519
|
}
|
|
25213
|
-
async function readExecutionRequest(
|
|
25214
|
-
const body = await
|
|
26520
|
+
async function readExecutionRequest(request2) {
|
|
26521
|
+
const body = await request2.json().catch(() => null);
|
|
25215
26522
|
return EcosystemExecutionRequestSchema.safeParse(body);
|
|
25216
26523
|
}
|
|
25217
|
-
async function handleEcosystemPackageExecution(
|
|
25218
|
-
const parsed = await readExecutionRequest(
|
|
26524
|
+
async function handleEcosystemPackageExecution(request2, packageId, action, startTask = startEcosystemTask) {
|
|
26525
|
+
const parsed = await readExecutionRequest(request2);
|
|
25219
26526
|
if (!parsed.success) {
|
|
25220
26527
|
auditEcosystemExecution("confirmation_rejected", action, null, "invalid_request");
|
|
25221
26528
|
return noStoreJson({ error: "Explicit local command confirmation is required" }, 400);
|
|
@@ -25247,9 +26554,9 @@ async function handleEcosystemPackageExecution(request, packageId, action, start
|
|
|
25247
26554
|
auditEcosystemExecution("execution_started", action, plan.targetId, "none");
|
|
25248
26555
|
return noStoreJson({ task });
|
|
25249
26556
|
}
|
|
25250
|
-
async function handleEcosystemRecipeExecution(
|
|
26557
|
+
async function handleEcosystemRecipeExecution(request2, recipeId, startTask = startEcosystemRecipeTask) {
|
|
25251
26558
|
const action = "recipe";
|
|
25252
|
-
const parsed = await readExecutionRequest(
|
|
26559
|
+
const parsed = await readExecutionRequest(request2);
|
|
25253
26560
|
if (!parsed.success) {
|
|
25254
26561
|
auditEcosystemExecution("confirmation_rejected", action, null, "invalid_request");
|
|
25255
26562
|
return noStoreJson({ error: "Explicit local command confirmation is required" }, 400);
|
|
@@ -25284,149 +26591,159 @@ async function handleEcosystemRecipeExecution(request, recipeId, startTask = sta
|
|
|
25284
26591
|
const Route$4 = createFileRoute("/api/ecosystem/recipes/$recipeId/run")({
|
|
25285
26592
|
server: {
|
|
25286
26593
|
handlers: {
|
|
25287
|
-
POST: ({ params, request }) => handleEcosystemRecipeExecution(
|
|
26594
|
+
POST: ({ params, request: request2 }) => handleEcosystemRecipeExecution(request2, params.recipeId)
|
|
25288
26595
|
}
|
|
25289
26596
|
}
|
|
25290
26597
|
});
|
|
25291
26598
|
const Route$3 = createFileRoute("/api/ecosystem/packages/$packageId/upgrade")({
|
|
25292
26599
|
server: {
|
|
25293
26600
|
handlers: {
|
|
25294
|
-
POST: ({ params, request }) => handleEcosystemPackageExecution(
|
|
26601
|
+
POST: ({ params, request: request2 }) => handleEcosystemPackageExecution(request2, params.packageId, "upgrade")
|
|
25295
26602
|
}
|
|
25296
26603
|
}
|
|
25297
26604
|
});
|
|
25298
26605
|
const Route$2 = createFileRoute("/api/ecosystem/packages/$packageId/runner-presets")({
|
|
25299
26606
|
server: {
|
|
25300
26607
|
handlers: {
|
|
25301
|
-
POST: ({ params, request }) => handleEcosystemPackageExecution(
|
|
26608
|
+
POST: ({ params, request: request2 }) => handleEcosystemPackageExecution(request2, params.packageId, "runner-presets")
|
|
25302
26609
|
}
|
|
25303
26610
|
}
|
|
25304
26611
|
});
|
|
25305
26612
|
const Route$1 = createFileRoute("/api/ecosystem/packages/$packageId/install")({
|
|
25306
26613
|
server: {
|
|
25307
26614
|
handlers: {
|
|
25308
|
-
POST: ({ params, request }) => handleEcosystemPackageExecution(
|
|
26615
|
+
POST: ({ params, request: request2 }) => handleEcosystemPackageExecution(request2, params.packageId, "install")
|
|
25309
26616
|
}
|
|
25310
26617
|
}
|
|
25311
26618
|
});
|
|
25312
26619
|
const Route = createFileRoute("/api/ecosystem/packages/$packageId/help")({
|
|
25313
26620
|
server: {
|
|
25314
26621
|
handlers: {
|
|
25315
|
-
POST: ({ params, request }) => handleEcosystemPackageExecution(
|
|
26622
|
+
POST: ({ params, request: request2 }) => handleEcosystemPackageExecution(request2, params.packageId, "help")
|
|
25316
26623
|
}
|
|
25317
26624
|
}
|
|
25318
26625
|
});
|
|
25319
|
-
const ReadyzRoute = Route$
|
|
26626
|
+
const ReadyzRoute = Route$V.update({
|
|
25320
26627
|
id: "/readyz",
|
|
25321
26628
|
path: "/readyz",
|
|
25322
|
-
getParentRoute: () => Route$
|
|
26629
|
+
getParentRoute: () => Route$W
|
|
26630
|
+
});
|
|
26631
|
+
const MetricsRoute = Route$T.update({
|
|
26632
|
+
id: "/metrics",
|
|
26633
|
+
path: "/metrics",
|
|
26634
|
+
getParentRoute: () => Route$W
|
|
25323
26635
|
});
|
|
25324
26636
|
const LivezRoute = Route$S.update({
|
|
25325
26637
|
id: "/livez",
|
|
25326
26638
|
path: "/livez",
|
|
25327
|
-
getParentRoute: () => Route$
|
|
26639
|
+
getParentRoute: () => Route$W
|
|
25328
26640
|
});
|
|
25329
26641
|
const SplatRoute = Route$R.update({
|
|
25330
26642
|
id: "/$",
|
|
25331
26643
|
path: "/$",
|
|
25332
|
-
getParentRoute: () => Route$
|
|
26644
|
+
getParentRoute: () => Route$W
|
|
25333
26645
|
});
|
|
25334
26646
|
const ProxySplatRoute = Route$Q.update({
|
|
25335
26647
|
id: "/proxy/$",
|
|
25336
26648
|
path: "/proxy/$",
|
|
25337
|
-
getParentRoute: () => Route$
|
|
26649
|
+
getParentRoute: () => Route$W
|
|
26650
|
+
});
|
|
26651
|
+
const ApiStorageRoute = Route$P.update({
|
|
26652
|
+
id: "/api/storage",
|
|
26653
|
+
path: "/api/storage",
|
|
26654
|
+
getParentRoute: () => Route$W
|
|
25338
26655
|
});
|
|
25339
|
-
const ApiSessionsRoute = Route$
|
|
26656
|
+
const ApiSessionsRoute = Route$O.update({
|
|
25340
26657
|
id: "/api/sessions",
|
|
25341
26658
|
path: "/api/sessions",
|
|
25342
|
-
getParentRoute: () => Route$
|
|
26659
|
+
getParentRoute: () => Route$W
|
|
25343
26660
|
});
|
|
25344
|
-
const ApiRuntimeRoute = Route$
|
|
26661
|
+
const ApiRuntimeRoute = Route$N.update({
|
|
25345
26662
|
id: "/api/runtime",
|
|
25346
26663
|
path: "/api/runtime",
|
|
25347
|
-
getParentRoute: () => Route$
|
|
26664
|
+
getParentRoute: () => Route$W
|
|
25348
26665
|
});
|
|
25349
|
-
const ApiRunsRoute = Route$
|
|
26666
|
+
const ApiRunsRoute = Route$M.update({
|
|
25350
26667
|
id: "/api/runs",
|
|
25351
26668
|
path: "/api/runs",
|
|
25352
|
-
getParentRoute: () => Route$
|
|
26669
|
+
getParentRoute: () => Route$W
|
|
25353
26670
|
});
|
|
25354
|
-
const ApiProvidersRoute = Route$
|
|
26671
|
+
const ApiProvidersRoute = Route$L.update({
|
|
25355
26672
|
id: "/api/providers",
|
|
25356
26673
|
path: "/api/providers",
|
|
25357
|
-
getParentRoute: () => Route$
|
|
26674
|
+
getParentRoute: () => Route$W
|
|
25358
26675
|
});
|
|
25359
|
-
const ApiPiAgentRoute = Route$
|
|
26676
|
+
const ApiPiAgentRoute = Route$K.update({
|
|
25360
26677
|
id: "/api/pi-agent",
|
|
25361
26678
|
path: "/api/pi-agent",
|
|
25362
|
-
getParentRoute: () => Route$
|
|
26679
|
+
getParentRoute: () => Route$W
|
|
25363
26680
|
});
|
|
25364
|
-
const ApiModelsRoute = Route$
|
|
26681
|
+
const ApiModelsRoute = Route$J.update({
|
|
25365
26682
|
id: "/api/models",
|
|
25366
26683
|
path: "/api/models",
|
|
25367
|
-
getParentRoute: () => Route$
|
|
26684
|
+
getParentRoute: () => Route$W
|
|
25368
26685
|
});
|
|
25369
|
-
const ApiMcpRoute = Route$
|
|
26686
|
+
const ApiMcpRoute = Route$I.update({
|
|
25370
26687
|
id: "/api/mcp",
|
|
25371
26688
|
path: "/api/mcp",
|
|
25372
|
-
getParentRoute: () => Route$
|
|
26689
|
+
getParentRoute: () => Route$W
|
|
25373
26690
|
});
|
|
25374
|
-
const ApiLogsRoute = Route$
|
|
26691
|
+
const ApiLogsRoute = Route$H.update({
|
|
25375
26692
|
id: "/api/logs",
|
|
25376
26693
|
path: "/api/logs",
|
|
25377
|
-
getParentRoute: () => Route$
|
|
26694
|
+
getParentRoute: () => Route$W
|
|
25378
26695
|
});
|
|
25379
|
-
const ApiInstancesRoute = Route$
|
|
26696
|
+
const ApiInstancesRoute = Route$G.update({
|
|
25380
26697
|
id: "/api/instances",
|
|
25381
26698
|
path: "/api/instances",
|
|
25382
|
-
getParentRoute: () => Route$
|
|
26699
|
+
getParentRoute: () => Route$W
|
|
25383
26700
|
});
|
|
25384
|
-
const ApiHealthRoute = Route$
|
|
26701
|
+
const ApiHealthRoute = Route$F.update({
|
|
25385
26702
|
id: "/api/health",
|
|
25386
26703
|
path: "/api/health",
|
|
25387
|
-
getParentRoute: () => Route$
|
|
26704
|
+
getParentRoute: () => Route$W
|
|
25388
26705
|
});
|
|
25389
|
-
const ApiGroupsRoute = Route$
|
|
26706
|
+
const ApiGroupsRoute = Route$E.update({
|
|
25390
26707
|
id: "/api/groups",
|
|
25391
26708
|
path: "/api/groups",
|
|
25392
|
-
getParentRoute: () => Route$
|
|
26709
|
+
getParentRoute: () => Route$W
|
|
25393
26710
|
});
|
|
25394
|
-
const ApiConfigRoute = Route$
|
|
26711
|
+
const ApiConfigRoute = Route$D.update({
|
|
25395
26712
|
id: "/api/config",
|
|
25396
26713
|
path: "/api/config",
|
|
25397
|
-
getParentRoute: () => Route$
|
|
26714
|
+
getParentRoute: () => Route$W
|
|
25398
26715
|
});
|
|
25399
|
-
const ApiAlertsRoute = Route$
|
|
26716
|
+
const ApiAlertsRoute = Route$C.update({
|
|
25400
26717
|
id: "/api/alerts",
|
|
25401
26718
|
path: "/api/alerts",
|
|
25402
|
-
getParentRoute: () => Route$
|
|
26719
|
+
getParentRoute: () => Route$W
|
|
25403
26720
|
});
|
|
25404
|
-
const ApiRunsRunIdRoute = Route$
|
|
26721
|
+
const ApiRunsRunIdRoute = Route$B.update({
|
|
25405
26722
|
id: "/$runId",
|
|
25406
26723
|
path: "/$runId",
|
|
25407
26724
|
getParentRoute: () => ApiRunsRoute
|
|
25408
26725
|
});
|
|
25409
|
-
const ApiProvidersScanRoute = Route$
|
|
26726
|
+
const ApiProvidersScanRoute = Route$A.update({
|
|
25410
26727
|
id: "/scan",
|
|
25411
26728
|
path: "/scan",
|
|
25412
26729
|
getParentRoute: () => ApiProvidersRoute
|
|
25413
26730
|
});
|
|
25414
|
-
const ApiProvidersImportRoute = Route$
|
|
26731
|
+
const ApiProvidersImportRoute = Route$z.update({
|
|
25415
26732
|
id: "/import",
|
|
25416
26733
|
path: "/import",
|
|
25417
26734
|
getParentRoute: () => ApiProvidersRoute
|
|
25418
26735
|
});
|
|
25419
|
-
const ApiProvidersExportRoute = Route$
|
|
26736
|
+
const ApiProvidersExportRoute = Route$y.update({
|
|
25420
26737
|
id: "/export",
|
|
25421
26738
|
path: "/export",
|
|
25422
26739
|
getParentRoute: () => ApiProvidersRoute
|
|
25423
26740
|
});
|
|
25424
|
-
const ApiProvidersProviderIdRoute = Route$
|
|
26741
|
+
const ApiProvidersProviderIdRoute = Route$x.update({
|
|
25425
26742
|
id: "/$providerId",
|
|
25426
26743
|
path: "/$providerId",
|
|
25427
26744
|
getParentRoute: () => ApiProvidersRoute
|
|
25428
26745
|
});
|
|
25429
|
-
const ApiLogsStreamRoute = Route$
|
|
26746
|
+
const ApiLogsStreamRoute = Route$U.update({
|
|
25430
26747
|
id: "/stream",
|
|
25431
26748
|
path: "/stream",
|
|
25432
26749
|
getParentRoute: () => ApiLogsRoute
|
|
@@ -25444,17 +26761,17 @@ const ApiLogsIdRoute = Route$v.update({
|
|
|
25444
26761
|
const ApiKnowledgeSearchRoute = Route$u.update({
|
|
25445
26762
|
id: "/api/knowledge/search",
|
|
25446
26763
|
path: "/api/knowledge/search",
|
|
25447
|
-
getParentRoute: () => Route$
|
|
26764
|
+
getParentRoute: () => Route$W
|
|
25448
26765
|
});
|
|
25449
26766
|
const ApiKnowledgeProjectContextRoute = Route$t.update({
|
|
25450
26767
|
id: "/api/knowledge/project-context",
|
|
25451
26768
|
path: "/api/knowledge/project-context",
|
|
25452
|
-
getParentRoute: () => Route$
|
|
26769
|
+
getParentRoute: () => Route$W
|
|
25453
26770
|
});
|
|
25454
26771
|
const ApiKnowledgeCandidatesRoute = Route$s.update({
|
|
25455
26772
|
id: "/api/knowledge/candidates",
|
|
25456
26773
|
path: "/api/knowledge/candidates",
|
|
25457
|
-
getParentRoute: () => Route$
|
|
26774
|
+
getParentRoute: () => Route$W
|
|
25458
26775
|
});
|
|
25459
26776
|
const ApiInstancesNameRoute = Route$r.update({
|
|
25460
26777
|
id: "/$name",
|
|
@@ -25469,12 +26786,12 @@ const ApiGroupsGroupIdRoute = Route$q.update({
|
|
|
25469
26786
|
const ApiEcosystemTasksRoute = Route$p.update({
|
|
25470
26787
|
id: "/api/ecosystem/tasks",
|
|
25471
26788
|
path: "/api/ecosystem/tasks",
|
|
25472
|
-
getParentRoute: () => Route$
|
|
26789
|
+
getParentRoute: () => Route$W
|
|
25473
26790
|
});
|
|
25474
26791
|
const ApiEcosystemPackagesRoute = Route$o.update({
|
|
25475
26792
|
id: "/api/ecosystem/packages",
|
|
25476
26793
|
path: "/api/ecosystem/packages",
|
|
25477
|
-
getParentRoute: () => Route$
|
|
26794
|
+
getParentRoute: () => Route$W
|
|
25478
26795
|
});
|
|
25479
26796
|
const ApiConfigPathsRoute = Route$n.update({
|
|
25480
26797
|
id: "/paths",
|
|
@@ -25564,7 +26881,7 @@ const ApiProvidersProviderIdTestLogRoute = Route$7.update({
|
|
|
25564
26881
|
const ApiKnowledgeSessionsSessionIdCandidatesRoute = Route$6.update({
|
|
25565
26882
|
id: "/api/knowledge/sessions/$sessionId/candidates",
|
|
25566
26883
|
path: "/api/knowledge/sessions/$sessionId/candidates",
|
|
25567
|
-
getParentRoute: () => Route$
|
|
26884
|
+
getParentRoute: () => Route$W
|
|
25568
26885
|
});
|
|
25569
26886
|
const ApiKnowledgeCandidatesCandidateIdPromoteRoute = Route$5.update({
|
|
25570
26887
|
id: "/promote",
|
|
@@ -25574,7 +26891,7 @@ const ApiKnowledgeCandidatesCandidateIdPromoteRoute = Route$5.update({
|
|
|
25574
26891
|
const ApiEcosystemRecipesRecipeIdRunRoute = Route$4.update({
|
|
25575
26892
|
id: "/api/ecosystem/recipes/$recipeId/run",
|
|
25576
26893
|
path: "/api/ecosystem/recipes/$recipeId/run",
|
|
25577
|
-
getParentRoute: () => Route$
|
|
26894
|
+
getParentRoute: () => Route$W
|
|
25578
26895
|
});
|
|
25579
26896
|
const ApiEcosystemPackagesPackageIdUpgradeRoute = Route$3.update({
|
|
25580
26897
|
id: "/$packageId/upgrade",
|
|
@@ -25704,6 +27021,7 @@ const ApiKnowledgeCandidatesRouteWithChildren = ApiKnowledgeCandidatesRoute._add
|
|
|
25704
27021
|
const rootRouteChildren = {
|
|
25705
27022
|
SplatRoute,
|
|
25706
27023
|
LivezRoute,
|
|
27024
|
+
MetricsRoute,
|
|
25707
27025
|
ReadyzRoute,
|
|
25708
27026
|
ApiAlertsRoute: ApiAlertsRouteWithChildren,
|
|
25709
27027
|
ApiConfigRoute: ApiConfigRouteWithChildren,
|
|
@@ -25718,6 +27036,7 @@ const rootRouteChildren = {
|
|
|
25718
27036
|
ApiRunsRoute: ApiRunsRouteWithChildren,
|
|
25719
27037
|
ApiRuntimeRoute,
|
|
25720
27038
|
ApiSessionsRoute,
|
|
27039
|
+
ApiStorageRoute,
|
|
25721
27040
|
ProxySplatRoute,
|
|
25722
27041
|
ApiEcosystemPackagesRoute: ApiEcosystemPackagesRouteWithChildren,
|
|
25723
27042
|
ApiEcosystemTasksRoute: ApiEcosystemTasksRouteWithChildren,
|
|
@@ -25727,7 +27046,7 @@ const rootRouteChildren = {
|
|
|
25727
27046
|
ApiEcosystemRecipesRecipeIdRunRoute,
|
|
25728
27047
|
ApiKnowledgeSessionsSessionIdCandidatesRoute
|
|
25729
27048
|
};
|
|
25730
|
-
const routeTree = Route$
|
|
27049
|
+
const routeTree = Route$W._addFileChildren(rootRouteChildren)._addFileTypes();
|
|
25731
27050
|
function defaultRouterBasePath() {
|
|
25732
27051
|
return getRouterBasePath(typeof process === "undefined" ? void 0 : process.env);
|
|
25733
27052
|
}
|