@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.
Files changed (61) hide show
  1. package/.output/backend/nitro.json +1 -1
  2. package/.output/cli.js +140 -20
  3. package/.output/server/_ssr/index.mjs +2 -2
  4. package/.output/server/_ssr/{router-0qlvqhVh.mjs → router-Be6BLuut.mjs} +2454 -1135
  5. package/.output/server/_tanstack-start-manifest_v-ClTaMkEj.mjs +4 -0
  6. package/.output/server/index.mjs +1 -1
  7. package/.output/ui/assets/{CompareDrawer-BOk9hd-Z.js → CompareDrawer-CGk2HFI1.js} +1 -1
  8. package/.output/ui/assets/{InspectorPet-DhHDBpk7.js → InspectorPet-CDM7SdOT.js} +1 -1
  9. package/.output/ui/assets/ProxyViewerContainer-CaWD78tw.js +60 -0
  10. package/.output/ui/assets/{ReplayDialog-DJhKtuNB.js → ReplayDialog-DqaeV1He.js} +1 -1
  11. package/.output/ui/assets/{RequestAnatomy-hP0tsqDC.js → RequestAnatomy-BtHl9P0k.js} +1 -1
  12. package/.output/ui/assets/{ResponseView-BSiUkwCb.js → ResponseView-C6t1vpxs.js} +1 -1
  13. package/.output/ui/assets/{StreamingChunkSequence-CzQqwv4X.js → StreamingChunkSequence-R4mgd3GQ.js} +1 -1
  14. package/.output/ui/assets/{_sessionId-BU-i6h0V.js → _sessionId-CL3hdLlQ.js} +1 -1
  15. package/.output/ui/assets/{_sessionId-C2Pq4Jb0.js → _sessionId-HS666S72.js} +1 -1
  16. package/.output/ui/assets/index-B-Qod-aA.css +1 -0
  17. package/.output/ui/assets/{index-D8C-t-2U.js → index-Bd9jPL4n.js} +1 -1
  18. package/.output/ui/assets/{index-zHpVF0jk.js → index-C4NHrclw.js} +1 -1
  19. package/.output/ui/assets/{index-UUnRwbOJ.js → index-UF1PJEcq.js} +1 -1
  20. package/.output/ui/assets/{index-B7NeIBDf.js → index-tX38BNV2.js} +2 -2
  21. package/.output/ui/assets/{json-viewer-DOzS5rUT.js → json-viewer-KSgzN_Ov.js} +1 -1
  22. package/.output/ui/assets/{jszip.min-CCCY7qNk.js → jszip.min-BiMDxghN.js} +1 -1
  23. package/.output/ui/index.html +2 -2
  24. package/.output/workers/logFinalizer.worker.js +261 -0
  25. package/.output/workers/sessionWorkerEntry.js +261 -0
  26. package/package.json +13 -6
  27. package/src/backend/routes/api/logs.$id.replay.ts +6 -1
  28. package/src/backend/routes/api/logs.stream.ts +7 -0
  29. package/src/backend/routes/api/providers.$providerId.model-metadata.ts +15 -4
  30. package/src/backend/routes/api/storage.ts +56 -0
  31. package/src/backend/routes/metrics.ts +14 -0
  32. package/src/cli/doctor.ts +135 -2
  33. package/src/cli.ts +7 -4
  34. package/src/components/ProxyViewerContainer.tsx +41 -24
  35. package/src/components/providers/SettingsDialog.tsx +261 -70
  36. package/src/components/proxy-viewer/LogEntry.tsx +19 -5
  37. package/src/components/proxy-viewer/bodyHydration.ts +67 -0
  38. package/src/components/proxy-viewer/proxyViewerContainerLogic.ts +78 -0
  39. package/src/contracts/index.ts +2 -0
  40. package/src/contracts/log.ts +5 -0
  41. package/src/knowledge/openclawClient.ts +11 -2
  42. package/src/knowledge/openclawGatewayClient.ts +6 -1
  43. package/src/lib/resourceLimits.ts +191 -0
  44. package/src/lib/safeFetch.ts +428 -20
  45. package/src/lib/stopReason.ts +66 -39
  46. package/src/proxy/ecosystemTasks.ts +20 -0
  47. package/src/proxy/handler.ts +17 -4
  48. package/src/proxy/identityProxy.ts +36 -16
  49. package/src/proxy/logIndex.ts +57 -6
  50. package/src/proxy/logger.ts +4 -0
  51. package/src/proxy/rawStreamCapture.ts +16 -1
  52. package/src/proxy/runtimeHealth.ts +4 -2
  53. package/src/proxy/runtimeMetrics.ts +149 -0
  54. package/src/proxy/schemas.ts +2 -0
  55. package/src/proxy/sessionRuntime.ts +38 -0
  56. package/src/proxy/sqliteLogIndex.ts +78 -2
  57. package/src/proxy/storageLifecycle.ts +432 -0
  58. package/src/proxy/store.ts +69 -13
  59. package/.output/server/_tanstack-start-manifest_v-DnbdNeun.mjs +0 -4
  60. package/.output/ui/assets/ProxyViewerContainer-BRYf987G.js +0 -59
  61. package/.output/ui/assets/index-IBEDJoV_.css +0 -1
@@ -14948,6 +14948,8 @@ var ToolTraceEventSchema = external_exports.object({
14948
14948
  argumentsText: external_exports.string().nullable(),
14949
14949
  argumentsPreview: external_exports.string().nullable()
14950
14950
  });
14951
+ var StopReasonValueSchema = external_exports.enum(["end_turn", "tool_use", "stop", "length"]);
14952
+ var StopReasonSchema = StopReasonValueSchema.nullable();
14951
14953
  var StreamingChunksArraySchema = external_exports.object({
14952
14954
  chunks: external_exports.array(StreamingChunkSchema),
14953
14955
  truncated: external_exports.boolean().optional().default(false)
@@ -15008,6 +15010,7 @@ var CapturedLogSchema = external_exports.object({
15008
15010
  captureIncompleteReason: CaptureIncompleteReasonSchema.nullable().optional(),
15009
15011
  warnings: external_exports.array(external_exports.string()).optional(),
15010
15012
  toolTraceEvents: external_exports.array(ToolTraceEventSchema).optional(),
15013
+ stopReason: StopReasonSchema.optional(),
15011
15014
  /** Error message from streaming response (e.g., SSE error event) */
15012
15015
  error: external_exports.string().nullable().optional()
15013
15016
  });
@@ -17164,6 +17167,264 @@ var idGenerationChain = Promise.resolve();
17164
17167
  import { AsyncLocalStorage } from "node:async_hooks";
17165
17168
  var publicRequestStorage = new AsyncLocalStorage();
17166
17169
 
17170
+ // src/lib/resourceLimits.ts
17171
+ var MAX_REST_LOG_PAGE_SIZE = 200;
17172
+ var MAX_SEARCH_QUERY_CHARS = 512;
17173
+ var MAX_SEARCH_PROJECT_CHARS = 256;
17174
+ var MAX_PROVIDER_MODELS = 64;
17175
+ var MAX_PROVIDER_MODEL_METADATA = 64;
17176
+ var MAX_SSE_CLIENTS = 32;
17177
+ var MAX_ACTIVE_PROVIDER_TESTS = 2;
17178
+ var MAX_PROVIDER_TEST_CONCURRENCY = 4;
17179
+ var MAX_ACTIVE_PROXY_REQUESTS = 16;
17180
+ var positiveInteger = external_exports.number().int().positive();
17181
+ var RuntimeBudgetPolicySchema = external_exports.object({
17182
+ proxy: external_exports.object({
17183
+ requestBytes: positiveInteger,
17184
+ responseBytes: positiveInteger,
17185
+ headerBytes: positiveInteger,
17186
+ deadlineMs: positiveInteger,
17187
+ idleTimeoutMs: positiveInteger,
17188
+ activeRequests: positiveInteger
17189
+ }),
17190
+ control: external_exports.object({
17191
+ requestBytes: positiveInteger,
17192
+ responseBytes: positiveInteger,
17193
+ headerBytes: positiveInteger,
17194
+ deadlineMs: positiveInteger,
17195
+ idleTimeoutMs: positiveInteger,
17196
+ activeRequests: positiveInteger
17197
+ }),
17198
+ upstream: external_exports.object({
17199
+ fetchResponseBytes: positiveInteger,
17200
+ fetchDeadlineMs: positiveInteger,
17201
+ replayResponseBytes: positiveInteger,
17202
+ replayDeadlineMs: positiveInteger,
17203
+ metadataResponseBytes: positiveInteger,
17204
+ metadataDeadlineMs: positiveInteger,
17205
+ connectTimeoutMs: positiveInteger,
17206
+ idleTimeoutMs: positiveInteger
17207
+ }),
17208
+ pagination: external_exports.object({
17209
+ restPageSize: positiveInteger,
17210
+ searchQueryChars: positiveInteger,
17211
+ searchProjectChars: positiveInteger
17212
+ }),
17213
+ sse: external_exports.object({
17214
+ clients: positiveInteger,
17215
+ initLogs: positiveInteger,
17216
+ heartbeatMs: positiveInteger
17217
+ }),
17218
+ imports: external_exports.object({
17219
+ archiveBytes: positiveInteger,
17220
+ entries: positiveInteger,
17221
+ entryBytes: positiveInteger,
17222
+ uncompressedBytes: positiveInteger,
17223
+ expansionRatio: positiveInteger
17224
+ }),
17225
+ fanOut: external_exports.object({
17226
+ providerModels: positiveInteger,
17227
+ providerModelMetadata: positiveInteger,
17228
+ providerTestConcurrency: positiveInteger,
17229
+ activeProviderTests: positiveInteger
17230
+ }),
17231
+ queues: external_exports.object({
17232
+ proxyRequests: positiveInteger,
17233
+ finalizerJobs: positiveInteger,
17234
+ rawStreamMessages: positiveInteger,
17235
+ rawStreamBytes: positiveInteger,
17236
+ backgroundWorkers: positiveInteger
17237
+ })
17238
+ }).superRefine((policy, context) => {
17239
+ if (policy.control.requestBytes > policy.proxy.requestBytes) {
17240
+ context.addIssue({
17241
+ code: "custom",
17242
+ path: ["control", "requestBytes"],
17243
+ message: "control request budget must not exceed proxy request budget"
17244
+ });
17245
+ }
17246
+ if (policy.control.responseBytes > policy.proxy.responseBytes) {
17247
+ context.addIssue({
17248
+ code: "custom",
17249
+ path: ["control", "responseBytes"],
17250
+ message: "control response budget must not exceed proxy response budget"
17251
+ });
17252
+ }
17253
+ if (policy.control.activeRequests > policy.proxy.activeRequests) {
17254
+ context.addIssue({
17255
+ code: "custom",
17256
+ path: ["control", "activeRequests"],
17257
+ message: "control active request budget must not exceed proxy active request budget"
17258
+ });
17259
+ }
17260
+ if (policy.upstream.fetchResponseBytes > policy.proxy.responseBytes) {
17261
+ context.addIssue({
17262
+ code: "custom",
17263
+ path: ["upstream", "fetchResponseBytes"],
17264
+ message: "upstream fetch response budget must not exceed proxy response budget"
17265
+ });
17266
+ }
17267
+ if (policy.upstream.replayResponseBytes > policy.proxy.responseBytes) {
17268
+ context.addIssue({
17269
+ code: "custom",
17270
+ path: ["upstream", "replayResponseBytes"],
17271
+ message: "upstream replay response budget must not exceed proxy response budget"
17272
+ });
17273
+ }
17274
+ if (policy.upstream.metadataResponseBytes > policy.control.responseBytes) {
17275
+ context.addIssue({
17276
+ code: "custom",
17277
+ path: ["upstream", "metadataResponseBytes"],
17278
+ message: "upstream metadata response budget must not exceed control response budget"
17279
+ });
17280
+ }
17281
+ if (policy.upstream.metadataDeadlineMs > policy.control.deadlineMs) {
17282
+ context.addIssue({
17283
+ code: "custom",
17284
+ path: ["upstream", "metadataDeadlineMs"],
17285
+ message: "upstream metadata deadline must not exceed control deadline"
17286
+ });
17287
+ }
17288
+ if (policy.queues.proxyRequests < policy.proxy.activeRequests) {
17289
+ context.addIssue({
17290
+ code: "custom",
17291
+ path: ["queues", "proxyRequests"],
17292
+ message: "proxy request queue budget must cover active proxy requests"
17293
+ });
17294
+ }
17295
+ });
17296
+ var DEFAULT_RUNTIME_BUDGET_POLICY = RuntimeBudgetPolicySchema.parse({
17297
+ proxy: {
17298
+ requestBytes: 64 * 1024 * 1024,
17299
+ responseBytes: 256 * 1024 * 1024,
17300
+ headerBytes: 64 * 1024,
17301
+ deadlineMs: 12e4,
17302
+ idleTimeoutMs: 3e4,
17303
+ activeRequests: MAX_ACTIVE_PROXY_REQUESTS
17304
+ },
17305
+ control: {
17306
+ requestBytes: 8 * 1024 * 1024,
17307
+ responseBytes: 64 * 1024 * 1024,
17308
+ headerBytes: 32 * 1024,
17309
+ deadlineMs: 3e4,
17310
+ idleTimeoutMs: 1e4,
17311
+ activeRequests: 8
17312
+ },
17313
+ upstream: {
17314
+ fetchResponseBytes: 256 * 1024 * 1024,
17315
+ fetchDeadlineMs: 12e4,
17316
+ replayResponseBytes: 256 * 1024 * 1024,
17317
+ replayDeadlineMs: 12e4,
17318
+ metadataResponseBytes: 10 * 1024 * 1024,
17319
+ metadataDeadlineMs: 3e4,
17320
+ connectTimeoutMs: 1e4,
17321
+ idleTimeoutMs: 3e4
17322
+ },
17323
+ pagination: {
17324
+ restPageSize: MAX_REST_LOG_PAGE_SIZE,
17325
+ searchQueryChars: MAX_SEARCH_QUERY_CHARS,
17326
+ searchProjectChars: MAX_SEARCH_PROJECT_CHARS
17327
+ },
17328
+ sse: {
17329
+ clients: MAX_SSE_CLIENTS,
17330
+ initLogs: 500,
17331
+ heartbeatMs: 15e3
17332
+ },
17333
+ imports: {
17334
+ archiveBytes: 64 * 1024 * 1024,
17335
+ entries: 5e3,
17336
+ entryBytes: 32 * 1024 * 1024,
17337
+ uncompressedBytes: 256 * 1024 * 1024,
17338
+ expansionRatio: 20
17339
+ },
17340
+ fanOut: {
17341
+ providerModels: MAX_PROVIDER_MODELS,
17342
+ providerModelMetadata: MAX_PROVIDER_MODEL_METADATA,
17343
+ providerTestConcurrency: MAX_PROVIDER_TEST_CONCURRENCY,
17344
+ activeProviderTests: MAX_ACTIVE_PROVIDER_TESTS
17345
+ },
17346
+ queues: {
17347
+ proxyRequests: MAX_ACTIVE_PROXY_REQUESTS,
17348
+ finalizerJobs: 1e3,
17349
+ rawStreamMessages: 16384,
17350
+ rawStreamBytes: 64 * 1024 * 1024,
17351
+ backgroundWorkers: 4
17352
+ }
17353
+ });
17354
+ var ResourceLimitErrorCodeSchema = external_exports.enum([
17355
+ "invalid-page-request",
17356
+ "search-limit-exceeded",
17357
+ "provider-model-limit-exceeded",
17358
+ "sse-capacity-exceeded",
17359
+ "provider-test-capacity-exceeded",
17360
+ "proxy-capacity-exceeded"
17361
+ ]);
17362
+ var ResourceLimitStatusSchema = external_exports.union([
17363
+ external_exports.literal(400),
17364
+ external_exports.literal(413),
17365
+ external_exports.literal(429),
17366
+ external_exports.literal(503)
17367
+ ]);
17368
+ var ResourceLimitErrorSchema = external_exports.object({
17369
+ error: external_exports.string().min(1).max(300),
17370
+ code: ResourceLimitErrorCodeSchema,
17371
+ status: ResourceLimitStatusSchema,
17372
+ resource: external_exports.string().min(1).max(80),
17373
+ limit: external_exports.number().int().positive(),
17374
+ retryable: external_exports.boolean()
17375
+ });
17376
+
17377
+ // src/proxy/rawStreamCapture.ts
17378
+ var DEFAULT_MEMORY_LIMIT_BYTES = 1024 * 1024;
17379
+ var FILE_WRITE_BATCH_BYTES = 64 * 1024;
17380
+ var DEFAULT_QUEUE_MAX_BYTES = 8 * 1024 * 1024;
17381
+ var DEFAULT_GLOBAL_QUEUE_MAX_MESSAGES = 4096;
17382
+ var DEFAULT_GLOBAL_QUEUE_MAX_BYTES = 64 * 1024 * 1024;
17383
+ function resolvePositiveIntegerEnv(name, fallback) {
17384
+ const raw = process.env[name];
17385
+ if (raw === void 0 || raw === "") return fallback;
17386
+ const parsed = Number(raw);
17387
+ if (!Number.isSafeInteger(parsed) || parsed < 1) return fallback;
17388
+ return parsed;
17389
+ }
17390
+ var RawStreamObservationBudget = class {
17391
+ constructor(maxMessages, maxBytes) {
17392
+ this.maxMessages = maxMessages;
17393
+ this.maxBytes = maxBytes;
17394
+ }
17395
+ maxMessages;
17396
+ maxBytes;
17397
+ messages = 0;
17398
+ bytes = 0;
17399
+ tryAcquire(bytes) {
17400
+ if (this.messages + 1 > this.maxMessages || this.bytes + bytes > this.maxBytes) return null;
17401
+ this.messages += 1;
17402
+ this.bytes += bytes;
17403
+ let released = false;
17404
+ return () => {
17405
+ if (released) return;
17406
+ released = true;
17407
+ this.messages -= 1;
17408
+ this.bytes -= bytes;
17409
+ };
17410
+ }
17411
+ };
17412
+ var globalObservationBudget = new RawStreamObservationBudget(
17413
+ resolvePositiveIntegerEnv(
17414
+ "AGENT_INSPECTOR_OBSERVATION_GLOBAL_QUEUE_MESSAGES",
17415
+ DEFAULT_GLOBAL_QUEUE_MAX_MESSAGES
17416
+ ),
17417
+ resolvePositiveIntegerEnv(
17418
+ "AGENT_INSPECTOR_OBSERVATION_GLOBAL_QUEUE_BYTES",
17419
+ DEFAULT_GLOBAL_QUEUE_MAX_BYTES
17420
+ )
17421
+ );
17422
+
17423
+ // src/proxy/storageLifecycle.ts
17424
+ var DAY_MS2 = 24 * 60 * 60 * 1e3;
17425
+ var DEFAULT_MAX_TOTAL_BYTES = 2 * 1024 * 1024 * 1024;
17426
+ var DEFAULT_MAINTENANCE_INTERVAL_MS = 60 * 60 * 1e3;
17427
+
17167
17428
  // src/proxy/store.ts
17168
17429
  var sessionArchiveQueue = Promise.resolve();
17169
17430
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tonyclaw/agent-inspector",
3
- "version": "3.1.19",
3
+ "version": "3.1.21",
4
4
  "type": "module",
5
5
  "description": "Agent observability and knowledge capture layer for AI coding tools.",
6
6
  "license": "MIT",
@@ -64,6 +64,7 @@
64
64
  "test:integration": "node scripts/run-local-bun.mjs scripts/run-test-suite.mjs integration",
65
65
  "test:changed": "node scripts/run-local-bun.mjs scripts/run-test-suite.mjs changed",
66
66
  "test:all": "node scripts/run-local-bun.mjs scripts/run-test-suite.mjs all",
67
+ "test:hostile-inputs": "bun test src/proxy/identityProxy.test.ts src/proxy/handler.cancellationCompatibility.test.ts src/proxy/logImporter.test.ts src/backend/routes/api/-logs.stream.test.ts src/proxy/handler.admission.test.ts src/proxy/runtimeHealth.test.ts src/proxy/storageLifecycle.test.ts",
67
68
  "test:compatibility-tooling": "node scripts/run-compatibility-tests.mjs ultra",
68
69
  "test:compatibility-tooling:extended": "node scripts/run-compatibility-tests.mjs extended",
69
70
  "test:compatibility-conformance": "node scripts/run-compatibility-tests.mjs conformance",
@@ -82,6 +83,9 @@
82
83
  "smoke:ui-preview": "node scripts/smoke-decoupled-ui-preview.mjs",
83
84
  "check:bundle": "node scripts/check-client-bundle.mjs",
84
85
  "check:boundaries": "node scripts/check-runtime-boundaries.mjs",
86
+ "check:commercial-performance": "node scripts/check-commercial-performance-budgets.mjs",
87
+ "check:hostile-inputs": "node scripts/check-hostile-input-coverage.mjs",
88
+ "check:supply-chain": "node scripts/check-commercial-supply-chain.mjs",
85
89
  "compatibility:bdd": "node scripts/lint-bdd-catalog.mjs",
86
90
  "compatibility:compare": "node scripts/compatibility/cli.mjs compare",
87
91
  "compatibility:drift": "node scripts/check-backend-compatibility.mjs",
@@ -98,7 +102,10 @@
98
102
  "rust:pack-package": "node scripts/rust/pack-platform.mjs",
99
103
  "rust:native-artifact": "node scripts/rust/create-native-artifact.mjs",
100
104
  "rust:native:aggregate": "node scripts/rust/aggregate-native-artifacts.mjs",
101
- "test:rust-packaging": "bun test scripts/rust/npmPackaging.test.ts scripts/rust/nativeReleaseContract.test.ts scripts/rust/nativeStartupSmoke.test.ts scripts/releaseCore.test.ts",
105
+ "rust:release-gate:preflight": "node scripts/rust/release-gate-preflight.mjs",
106
+ "commercial:release-gate:preflight": "node scripts/commercial-release-preflight.mjs",
107
+ "test:commercial-release-gate": "bun test scripts/commercialReleasePreflight.test.ts",
108
+ "test:rust-packaging": "bun test scripts/rust/npmPackaging.test.ts scripts/rust/nativeReleaseContract.test.ts scripts/rust/nativeStartupSmoke.test.ts scripts/rust/releaseGatePreflight.test.ts scripts/releaseCore.test.ts",
102
109
  "rust:packages:check": "node scripts/rust/sync-package-versions.mjs",
103
110
  "rust:packages:sync": "node scripts/rust/sync-package-versions.mjs --write",
104
111
  "themes:generate": "node scripts/generate-theme-scales.mjs",
@@ -116,7 +123,7 @@
116
123
  "extension:release": "node scripts/release-chrome-extension.mjs",
117
124
  "release": "node scripts/release.mjs",
118
125
  "knip": "knip",
119
- "check": "bun scripts/run-static-checks.mjs --format && bun check:boundaries && bun compatibility:bdd && bun rust:packages:check",
126
+ "check": "bun scripts/run-static-checks.mjs --format && bun check:boundaries && bun check:commercial-performance && bun check:hostile-inputs && bun compatibility:bdd && bun rust:packages:check",
120
127
  "prepare": "husky",
121
128
  "ladle": "ladle serve",
122
129
  "ladle:build": "ladle build"
@@ -146,9 +153,9 @@
146
153
  "zod": "^4.4.3"
147
154
  },
148
155
  "optionalDependencies": {
149
- "@tonyclaw/agent-inspector-backend-darwin-arm64": "3.1.19",
150
- "@tonyclaw/agent-inspector-backend-linux-x64-gnu": "3.1.19",
151
- "@tonyclaw/agent-inspector-backend-win32-x64-msvc": "3.1.19",
156
+ "@tonyclaw/agent-inspector-backend-darwin-arm64": "3.1.21",
157
+ "@tonyclaw/agent-inspector-backend-linux-x64-gnu": "3.1.21",
158
+ "@tonyclaw/agent-inspector-backend-win32-x64-msvc": "3.1.21",
152
159
  "better-sqlite3": "^12.11.1",
153
160
  "rcedit": "4.0.1"
154
161
  },
@@ -24,6 +24,7 @@ import {
24
24
  } from "../../../proxy/upstream";
25
25
  import { computeTokensPerSecond } from "../../../proxy/streamTiming";
26
26
  import { safeFetch } from "../../../lib/safeFetch";
27
+ import { DEFAULT_RUNTIME_BUDGET_POLICY } from "../../../lib/resourceLimits";
27
28
 
28
29
  type ReplayRequest = {
29
30
  modifiedBody: string;
@@ -136,7 +137,11 @@ export const Route = createFileRoute("/api/logs/$id/replay")({
136
137
  headers,
137
138
  body: modifiedBody,
138
139
  },
139
- { allowLoopback: process.env["AGENT_INSPECTOR_ALLOW_LOOPBACK"] === "1" },
140
+ {
141
+ allowLoopback: process.env["AGENT_INSPECTOR_ALLOW_LOOPBACK"] === "1",
142
+ deadlineMs: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.replayDeadlineMs,
143
+ responseBytes: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.replayResponseBytes,
144
+ },
140
145
  );
141
146
  if (!fetchResult.ok) {
142
147
  return Response.json({
@@ -18,6 +18,13 @@ const UPDATE_COALESCE_MS = 50;
18
18
  const MAX_BUFFERED_UPDATE_EVENTS = 16;
19
19
  const sseClientLimiter = createCapacityLimiter(MAX_SSE_CLIENTS);
20
20
 
21
+ export function getLogsStreamMetrics(): { activeClients: number; capacity: number } {
22
+ return {
23
+ activeClients: sseClientLimiter.activeCount(),
24
+ capacity: sseClientLimiter.capacity,
25
+ };
26
+ }
27
+
21
28
  export function streamIncludesBodies(url: URL): boolean {
22
29
  return url.searchParams.get("compact") === "0";
23
30
  }
@@ -9,7 +9,11 @@ import {
9
9
  ProviderModelRegistrySchema,
10
10
  } from "../../../lib/providerModelMetadata";
11
11
  import { safeFetch } from "../../../lib/safeFetch";
12
- import { MAX_PROVIDER_MODELS, resourceLimitResponse } from "../../../lib/resourceLimits";
12
+ import {
13
+ DEFAULT_RUNTIME_BUDGET_POLICY,
14
+ MAX_PROVIDER_MODELS,
15
+ resourceLimitResponse,
16
+ } from "../../../lib/resourceLimits";
13
17
 
14
18
  const RefreshModelMetadataInputSchema = z.object({
15
19
  url: z.string().optional(),
@@ -85,9 +89,16 @@ export const Route = createFileRoute("/api/providers/$providerId/model-metadata"
85
89
 
86
90
  let response: Response;
87
91
  try {
88
- const fetchResult = await safeFetch(sourceUrl, {
89
- headers: { Accept: "application/json" },
90
- });
92
+ const fetchResult = await safeFetch(
93
+ sourceUrl,
94
+ {
95
+ headers: { Accept: "application/json" },
96
+ },
97
+ {
98
+ deadlineMs: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataDeadlineMs,
99
+ responseBytes: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataResponseBytes,
100
+ },
101
+ );
91
102
  if (!fetchResult.ok) {
92
103
  return Response.json(
93
104
  { error: `Model metadata URL blocked: ${fetchResult.reason}` },
@@ -0,0 +1,56 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import { getLogStorageStats } from "../../../proxy/store";
3
+ import {
4
+ getLastStorageLifecycleGcResult,
5
+ getStorageLifecycleInventory,
6
+ resolveStorageLifecycleQuotaPolicy,
7
+ runStorageLifecycleGc,
8
+ type StorageLifecycleGcResult,
9
+ } from "../../../proxy/storageLifecycle";
10
+
11
+ function summarizeGcResult(result: StorageLifecycleGcResult | null) {
12
+ if (result === null) return null;
13
+ return {
14
+ checkedAt: result.checkedAt,
15
+ filesDeleted: result.filesDeleted,
16
+ bytesDeleted: result.bytesDeleted,
17
+ errors: result.errors.length,
18
+ beforeTotalBytes: result.before.totalBytes,
19
+ afterTotalBytes: result.after.totalBytes,
20
+ };
21
+ }
22
+
23
+ function getStorageResponse() {
24
+ const logStats = getLogStorageStats();
25
+ const inventory = getStorageLifecycleInventory();
26
+ const policy = resolveStorageLifecycleQuotaPolicy();
27
+ const reclaimableBytes = inventory.artifacts
28
+ .filter(
29
+ (artifact) =>
30
+ artifact.cleanupBehavior === "retention-gc" ||
31
+ artifact.cleanupBehavior === "orphan-reclaim",
32
+ )
33
+ .reduce((sum, artifact) => sum + artifact.bytes, 0);
34
+
35
+ return {
36
+ memoryCount: logStats.memoryCount,
37
+ inventory,
38
+ policy,
39
+ reclaimableBytes,
40
+ lastGc: summarizeGcResult(getLastStorageLifecycleGcResult()),
41
+ };
42
+ }
43
+
44
+ export const Route = createFileRoute("/api/storage")({
45
+ server: {
46
+ handlers: {
47
+ GET: () => {
48
+ return Response.json(getStorageResponse(), { headers: { "cache-control": "no-store" } });
49
+ },
50
+ POST: () => {
51
+ runStorageLifecycleGc();
52
+ return Response.json(getStorageResponse(), { headers: { "cache-control": "no-store" } });
53
+ },
54
+ },
55
+ },
56
+ });
@@ -0,0 +1,14 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import { getLogsStreamMetrics } from "./api/logs.stream";
3
+ import { getSafeOperationalMetrics } from "../../proxy/runtimeMetrics";
4
+
5
+ export const Route = createFileRoute("/metrics")({
6
+ server: {
7
+ handlers: {
8
+ GET: async () => {
9
+ const metrics = await getSafeOperationalMetrics(getLogsStreamMetrics());
10
+ return Response.json(metrics, { headers: { "cache-control": "no-store" } });
11
+ },
12
+ },
13
+ },
14
+ });