@tonyclaw/agent-inspector 4.0.1 → 4.0.2

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 (373) hide show
  1. package/.output/backend/nitro.json +1 -1
  2. package/.output/cli.js +192 -109
  3. package/.output/server/_ssr/index.mjs +1 -1
  4. package/.output/server/_ssr/{router-CTHqmroT.mjs → router-BBY_zWRK.mjs} +91 -3
  5. package/.output/server/index.mjs +1 -1
  6. package/.output/ui/assets/{CompareDrawer-L3n4J-Or.js → CompareDrawer-rtNElN-B.js} +1 -1
  7. package/.output/ui/assets/{InspectorPet-D5o_3OZS.js → InspectorPet-QaAiuF3d.js} +1 -1
  8. package/.output/ui/assets/{ProxyViewerContainer-Ocagx75S.js → ProxyViewerContainer-6twNZHwl.js} +4 -4
  9. package/.output/ui/assets/{ReplayDialog-Bk7x1h-y.js → ReplayDialog-B7KlziO2.js} +1 -1
  10. package/.output/ui/assets/{RequestAnatomy-U4lzL7Sc.js → RequestAnatomy-2XVFBrW4.js} +1 -1
  11. package/.output/ui/assets/{ResponseView-LOWfvBLK.js → ResponseView-BULgzLVB.js} +1 -1
  12. package/.output/ui/assets/{StreamingChunkSequence-DPfsrrXQ.js → StreamingChunkSequence-DSkvkbns.js} +1 -1
  13. package/.output/ui/assets/{_sessionId-OdX2Jvt1.js → _sessionId-D2dmJi3G.js} +1 -1
  14. package/.output/ui/assets/{_sessionId-0MlWS-CO.js → _sessionId-DcS0s1Mr.js} +1 -1
  15. package/.output/ui/assets/{index-Du4qNOOt.js → index-B0TN1KKO.js} +2 -2
  16. package/.output/ui/assets/{index-gvFj_9K6.js → index-Bva1zHEs.js} +1 -1
  17. package/.output/ui/assets/{index-Ea-c8hPg.js → index-BzBExOxu.js} +1 -1
  18. package/.output/ui/assets/{index-zVVS53Hd.js → index-k6sRQz55.js} +1 -1
  19. package/.output/ui/assets/{json-viewer-C1WAMzg3.js → json-viewer-CWM4tY-j.js} +1 -1
  20. package/.output/ui/assets/{jszip.min-B8AKu2zm.js → jszip.min-BkWvRFlo.js} +1 -1
  21. package/.output/ui/index.html +1 -1
  22. package/package.json +5 -10
  23. package/src/assets/IDE/claudecode-color.svg +0 -1
  24. package/src/assets/IDE/codex-color.svg +0 -1
  25. package/src/assets/IDE/opencode.svg +0 -1
  26. package/src/assets/IDE/xiaomimimo.svg +0 -1
  27. package/src/assets/favicon.svg +0 -21
  28. package/src/assets/logos/alibaba.svg +0 -1
  29. package/src/assets/logos/anthropic.svg +0 -1
  30. package/src/assets/logos/claude-code.svg +0 -4
  31. package/src/assets/logos/deepseek.svg +0 -1
  32. package/src/assets/logos/minimax.jpeg +0 -0
  33. package/src/assets/logos/openai.svg +0 -1
  34. package/src/assets/logos/opencode.svg +0 -4
  35. package/src/assets/logos/qwen.webp +0 -0
  36. package/src/assets/logos/zhipuai.svg +0 -219
  37. package/src/backend/router.tsx +0 -36
  38. package/src/backend/routes/$.ts +0 -26
  39. package/src/backend/routes/__root.ts +0 -4
  40. package/src/backend/routes/api/-instances.ts +0 -7
  41. package/src/backend/routes/api/alerts.summary.ts +0 -28
  42. package/src/backend/routes/api/alerts.ts +0 -70
  43. package/src/backend/routes/api/config.paths.ts +0 -14
  44. package/src/backend/routes/api/config.ts +0 -73
  45. package/src/backend/routes/api/ecosystem.packages.$packageId.help.ts +0 -11
  46. package/src/backend/routes/api/ecosystem.packages.$packageId.install.ts +0 -11
  47. package/src/backend/routes/api/ecosystem.packages.$packageId.runner-presets.ts +0 -11
  48. package/src/backend/routes/api/ecosystem.packages.$packageId.upgrade.ts +0 -11
  49. package/src/backend/routes/api/ecosystem.packages.ts +0 -21
  50. package/src/backend/routes/api/ecosystem.recipes.$recipeId.run.ts +0 -11
  51. package/src/backend/routes/api/ecosystem.tasks.$taskId.ts +0 -16
  52. package/src/backend/routes/api/ecosystem.tasks.ts +0 -12
  53. package/src/backend/routes/api/groups.$groupId.evidence.ts +0 -55
  54. package/src/backend/routes/api/groups.$groupId.sessions.ts +0 -50
  55. package/src/backend/routes/api/groups.$groupId.ts +0 -93
  56. package/src/backend/routes/api/groups.ts +0 -79
  57. package/src/backend/routes/api/health.ts +0 -22
  58. package/src/backend/routes/api/instances.$name.connection.ts +0 -12
  59. package/src/backend/routes/api/instances.$name.restart.ts +0 -23
  60. package/src/backend/routes/api/instances.$name.start.ts +0 -18
  61. package/src/backend/routes/api/instances.$name.stop.ts +0 -23
  62. package/src/backend/routes/api/instances.$name.ts +0 -12
  63. package/src/backend/routes/api/instances.ts +0 -16
  64. package/src/backend/routes/api/knowledge.candidates.$candidateId.promote.ts +0 -32
  65. package/src/backend/routes/api/knowledge.candidates.$candidateId.ts +0 -50
  66. package/src/backend/routes/api/knowledge.candidates.ts +0 -10
  67. package/src/backend/routes/api/knowledge.project-context.ts +0 -29
  68. package/src/backend/routes/api/knowledge.search.ts +0 -62
  69. package/src/backend/routes/api/knowledge.sessions.$sessionId.candidates.ts +0 -26
  70. package/src/backend/routes/api/logs.$id.body.ts +0 -58
  71. package/src/backend/routes/api/logs.$id.chunks.ts +0 -37
  72. package/src/backend/routes/api/logs.$id.replay.ts +0 -297
  73. package/src/backend/routes/api/logs.$id.ts +0 -23
  74. package/src/backend/routes/api/logs.import.ts +0 -39
  75. package/src/backend/routes/api/logs.stream.ts +0 -174
  76. package/src/backend/routes/api/logs.ts +0 -214
  77. package/src/backend/routes/api/mcp.ts +0 -25
  78. package/src/backend/routes/api/models.ts +0 -10
  79. package/src/backend/routes/api/pi-agent.ts +0 -29
  80. package/src/backend/routes/api/providers.$providerId.model-metadata.ts +0 -173
  81. package/src/backend/routes/api/providers.$providerId.test.log.ts +0 -342
  82. package/src/backend/routes/api/providers.$providerId.ts +0 -111
  83. package/src/backend/routes/api/providers.export.ts +0 -44
  84. package/src/backend/routes/api/providers.import.ts +0 -81
  85. package/src/backend/routes/api/providers.scan.ts +0 -28
  86. package/src/backend/routes/api/providers.ts +0 -102
  87. package/src/backend/routes/api/runs.$runId.evidence.ts +0 -55
  88. package/src/backend/routes/api/runs.$runId.ts +0 -50
  89. package/src/backend/routes/api/runs.ts +0 -62
  90. package/src/backend/routes/api/runtime.ts +0 -46
  91. package/src/backend/routes/api/sessions.ts +0 -51
  92. package/src/backend/routes/api/storage.ts +0 -56
  93. package/src/backend/routes/livez.ts +0 -13
  94. package/src/backend/routes/metrics.ts +0 -14
  95. package/src/backend/routes/proxy/$.ts +0 -15
  96. package/src/backend/routes/readyz.ts +0 -18
  97. package/src/backend/start.ts +0 -10
  98. package/src/cli/alias.ts +0 -431
  99. package/src/cli/detect-tools.ts +0 -123
  100. package/src/cli/doctor.ts +0 -836
  101. package/src/cli/instance.ts +0 -1071
  102. package/src/cli/instanceApi.ts +0 -213
  103. package/src/cli/instanceArgs.ts +0 -301
  104. package/src/cli/instanceControl.ts +0 -191
  105. package/src/cli/instanceModel.ts +0 -276
  106. package/src/cli/instanceRegistry.ts +0 -731
  107. package/src/cli/networkHints.ts +0 -170
  108. package/src/cli/onboard.ts +0 -1668
  109. package/src/cli/onboardTarget.ts +0 -228
  110. package/src/cli/rustBackendPackage.ts +0 -458
  111. package/src/cli/startupOutput.ts +0 -16
  112. package/src/cli/templates/codex-skill-onboard.ts +0 -320
  113. package/src/cli/templates/command-onboard.ts +0 -25
  114. package/src/cli/templates/skill-onboard.ts +0 -625
  115. package/src/cli.ts +0 -1433
  116. package/src/components/OnboardingBanner.tsx +0 -115
  117. package/src/components/ProxyViewer.tsx +0 -2259
  118. package/src/components/ProxyViewerContainer.tsx +0 -1076
  119. package/src/components/alerts/AlertsDialog.tsx +0 -615
  120. package/src/components/clients/ClientLogo.tsx +0 -175
  121. package/src/components/ecosystem/AgentLabDialog.tsx +0 -947
  122. package/src/components/errors/SafeErrorBoundary.tsx +0 -201
  123. package/src/components/groups/GroupsDialog.tsx +0 -1304
  124. package/src/components/inspector-pet/InspectorPet.tsx +0 -398
  125. package/src/components/inspector-pet/InspectorPetCrab3D.tsx +0 -455
  126. package/src/components/inspector-pet/inspectorPetLogic.ts +0 -272
  127. package/src/components/pi-agent/PiAgentPanel.tsx +0 -446
  128. package/src/components/pi-agent/piAgentChatLogic.ts +0 -273
  129. package/src/components/providers/ImportWizardDialog.tsx +0 -281
  130. package/src/components/providers/ProviderCard.tsx +0 -347
  131. package/src/components/providers/ProviderForm.tsx +0 -1183
  132. package/src/components/providers/ProviderLogo.tsx +0 -122
  133. package/src/components/providers/ProviderTestResultsView.tsx +0 -734
  134. package/src/components/providers/ProvidersPanel.tsx +0 -714
  135. package/src/components/providers/SettingsDialog.tsx +0 -1042
  136. package/src/components/providers/providerFormCapabilities.ts +0 -36
  137. package/src/components/proxy-viewer/AgentTraceSummary.tsx +0 -902
  138. package/src/components/proxy-viewer/AnswerMarkdown.tsx +0 -51
  139. package/src/components/proxy-viewer/ApplicationBar.tsx +0 -123
  140. package/src/components/proxy-viewer/CompareDrawer.tsx +0 -984
  141. package/src/components/proxy-viewer/ConversationGroup.tsx +0 -208
  142. package/src/components/proxy-viewer/ConversationGroupList.tsx +0 -652
  143. package/src/components/proxy-viewer/ConversationHeader.tsx +0 -429
  144. package/src/components/proxy-viewer/LiveConnectionStatus.tsx +0 -186
  145. package/src/components/proxy-viewer/LogEntry.tsx +0 -1419
  146. package/src/components/proxy-viewer/LogEntryHeader.tsx +0 -596
  147. package/src/components/proxy-viewer/ProviderLogoStack.tsx +0 -57
  148. package/src/components/proxy-viewer/RawExportWarning.tsx +0 -77
  149. package/src/components/proxy-viewer/ReplayDialog.tsx +0 -636
  150. package/src/components/proxy-viewer/RequestToolsPanel.tsx +0 -299
  151. package/src/components/proxy-viewer/ResponseView.tsx +0 -202
  152. package/src/components/proxy-viewer/SessionLoadProgressBar.tsx +0 -61
  153. package/src/components/proxy-viewer/StreamingChunkSequence.tsx +0 -265
  154. package/src/components/proxy-viewer/ThreadConnector.tsx +0 -165
  155. package/src/components/proxy-viewer/ToolTraceEvents.tsx +0 -64
  156. package/src/components/proxy-viewer/TurnGroup.tsx +0 -383
  157. package/src/components/proxy-viewer/TurnGroupList.tsx +0 -156
  158. package/src/components/proxy-viewer/accessibility.ts +0 -8
  159. package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +0 -887
  160. package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +0 -222
  161. package/src/components/proxy-viewer/anatomy/contextIntelligence.ts +0 -737
  162. package/src/components/proxy-viewer/anatomy/sessionContextSummary.ts +0 -268
  163. package/src/components/proxy-viewer/anatomy/tokenEstimate.ts +0 -53
  164. package/src/components/proxy-viewer/anatomy/types.ts +0 -41
  165. package/src/components/proxy-viewer/anatomy/useAnatomyJump.ts +0 -114
  166. package/src/components/proxy-viewer/bodyHydration.ts +0 -123
  167. package/src/components/proxy-viewer/cacheTrend.ts +0 -50
  168. package/src/components/proxy-viewer/diff/DiffView.tsx +0 -321
  169. package/src/components/proxy-viewer/diff/computeDiff.ts +0 -178
  170. package/src/components/proxy-viewer/diff/index.ts +0 -3
  171. package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +0 -194
  172. package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +0 -81
  173. package/src/components/proxy-viewer/formats/anthropic/thinkingExtract.ts +0 -21
  174. package/src/components/proxy-viewer/formats/index.tsx +0 -47
  175. package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +0 -442
  176. package/src/components/proxy-viewer/index.ts +0 -9
  177. package/src/components/proxy-viewer/lazy.ts +0 -41
  178. package/src/components/proxy-viewer/liveConnectionState.ts +0 -158
  179. package/src/components/proxy-viewer/log-formats/anthropic.ts +0 -191
  180. package/src/components/proxy-viewer/log-formats/index.ts +0 -23
  181. package/src/components/proxy-viewer/log-formats/openai.ts +0 -256
  182. package/src/components/proxy-viewer/log-formats/types.ts +0 -40
  183. package/src/components/proxy-viewer/log-formats/unknown.ts +0 -18
  184. package/src/components/proxy-viewer/logEntryVisibility.ts +0 -73
  185. package/src/components/proxy-viewer/logFocus.ts +0 -54
  186. package/src/components/proxy-viewer/proxyViewerContainerLogic.ts +0 -124
  187. package/src/components/proxy-viewer/proxyViewerLogic.ts +0 -488
  188. package/src/components/proxy-viewer/replayComparison.ts +0 -131
  189. package/src/components/proxy-viewer/requestDiff.ts +0 -277
  190. package/src/components/proxy-viewer/requestTools.ts +0 -220
  191. package/src/components/proxy-viewer/useCopyFeedback.ts +0 -38
  192. package/src/components/proxy-viewer/useKeyboardNavigation.ts +0 -232
  193. package/src/components/proxy-viewer/viewerState.ts +0 -176
  194. package/src/components/ui/badge.tsx +0 -47
  195. package/src/components/ui/button.tsx +0 -47
  196. package/src/components/ui/collapsible.tsx +0 -21
  197. package/src/components/ui/confirm-dialog.tsx +0 -51
  198. package/src/components/ui/crab-logo.tsx +0 -45
  199. package/src/components/ui/crab-variants.tsx +0 -467
  200. package/src/components/ui/dialog.tsx +0 -130
  201. package/src/components/ui/icon-trigger.ts +0 -2
  202. package/src/components/ui/json-viewer-bulk.ts +0 -132
  203. package/src/components/ui/json-viewer.tsx +0 -508
  204. package/src/components/ui/scroll-area.tsx +0 -54
  205. package/src/components/ui/select.tsx +0 -178
  206. package/src/components/ui/separator.tsx +0 -28
  207. package/src/components/ui/tabs.tsx +0 -88
  208. package/src/components/ui/tooltip.tsx +0 -51
  209. package/src/components/ui/transient-toast.tsx +0 -105
  210. package/src/contracts/anthropic.ts +0 -230
  211. package/src/contracts/index.ts +0 -59
  212. package/src/contracts/json.ts +0 -37
  213. package/src/contracts/log.ts +0 -139
  214. package/src/contracts/openai.ts +0 -267
  215. package/src/index.css +0 -11
  216. package/src/knowledge/candidateStore.ts +0 -94
  217. package/src/knowledge/distiller.ts +0 -98
  218. package/src/knowledge/openclawClient.ts +0 -161
  219. package/src/knowledge/openclawGatewayClient.ts +0 -249
  220. package/src/knowledge/openclawMarkdown.ts +0 -146
  221. package/src/knowledge/redactor.ts +0 -80
  222. package/src/knowledge/types.ts +0 -83
  223. package/src/lib/alertContract.ts +0 -76
  224. package/src/lib/apiClient.ts +0 -166
  225. package/src/lib/apiFormat.ts +0 -29
  226. package/src/lib/backendImplementation.ts +0 -16
  227. package/src/lib/basePath.ts +0 -107
  228. package/src/lib/browserRuntimeContract.ts +0 -124
  229. package/src/lib/browserRuntimeResolver.ts +0 -333
  230. package/src/lib/chunkLoadRecovery.ts +0 -83
  231. package/src/lib/claudeCodeStrip.ts +0 -98
  232. package/src/lib/clipboard.ts +0 -67
  233. package/src/lib/codingAgentEndpoints.ts +0 -54
  234. package/src/lib/ecosystemContract.ts +0 -130
  235. package/src/lib/export-logs.ts +0 -287
  236. package/src/lib/groupContract.ts +0 -166
  237. package/src/lib/instanceContract.ts +0 -239
  238. package/src/lib/jsonFile.ts +0 -46
  239. package/src/lib/logImportContract.ts +0 -12
  240. package/src/lib/managedInstance.ts +0 -112
  241. package/src/lib/mask.ts +0 -4
  242. package/src/lib/objectUtils.ts +0 -22
  243. package/src/lib/piAgentContract.ts +0 -102
  244. package/src/lib/providerContract.ts +0 -125
  245. package/src/lib/providerImportContract.ts +0 -27
  246. package/src/lib/providerModelMetadata.ts +0 -458
  247. package/src/lib/providerTestContract.ts +0 -170
  248. package/src/lib/providerTestPrompt.ts +0 -100
  249. package/src/lib/publicBasePathContext.ts +0 -137
  250. package/src/lib/resourceLimits.ts +0 -345
  251. package/src/lib/runContract.ts +0 -164
  252. package/src/lib/runtimeConfig.ts +0 -43
  253. package/src/lib/safeDiagnostic.ts +0 -38
  254. package/src/lib/safeFetch.ts +0 -508
  255. package/src/lib/serverPort.ts +0 -47
  256. package/src/lib/sessionInfoContract.ts +0 -78
  257. package/src/lib/sessionUrl.ts +0 -46
  258. package/src/lib/ssrfGuard.ts +0 -133
  259. package/src/lib/stopReason.ts +0 -130
  260. package/src/lib/themeMode.ts +0 -41
  261. package/src/lib/timeDisplay.ts +0 -22
  262. package/src/lib/toolTrace.ts +0 -290
  263. package/src/lib/uiScale.ts +0 -117
  264. package/src/lib/unknown.ts +0 -27
  265. package/src/lib/upstreamUrl.ts +0 -150
  266. package/src/lib/useAlerts.ts +0 -82
  267. package/src/lib/useGroupEvidence.ts +0 -37
  268. package/src/lib/useGroups.ts +0 -32
  269. package/src/lib/useOnboarding.ts +0 -93
  270. package/src/lib/useProviders.ts +0 -30
  271. package/src/lib/useStripConfig.ts +0 -154
  272. package/src/lib/useThemeMode.ts +0 -40
  273. package/src/lib/useUiScale.ts +0 -52
  274. package/src/lib/utils.ts +0 -78
  275. package/src/mcp/currentContext.ts +0 -65
  276. package/src/mcp/instanceHandlers.ts +0 -151
  277. package/src/mcp/loopback.ts +0 -88
  278. package/src/mcp/mode.ts +0 -75
  279. package/src/mcp/previewExtractor.ts +0 -233
  280. package/src/mcp/server.ts +0 -2274
  281. package/src/mcp/toolHandlers.ts +0 -1533
  282. package/src/proxy/alerts.ts +0 -664
  283. package/src/proxy/chunkStorage.ts +0 -123
  284. package/src/proxy/config.ts +0 -218
  285. package/src/proxy/constants.ts +0 -66
  286. package/src/proxy/dataDir.ts +0 -147
  287. package/src/proxy/ecosystemExecutionConfirmation.ts +0 -114
  288. package/src/proxy/ecosystemExecutionRoute.ts +0 -116
  289. package/src/proxy/ecosystemRegistry.ts +0 -296
  290. package/src/proxy/ecosystemTasks.ts +0 -415
  291. package/src/proxy/evidenceAnalysis.ts +0 -526
  292. package/src/proxy/evidenceExporter.ts +0 -235
  293. package/src/proxy/formats/anthropic/anthropicProvider.ts +0 -75
  294. package/src/proxy/formats/anthropic/handler.ts +0 -75
  295. package/src/proxy/formats/anthropic/index.ts +0 -13
  296. package/src/proxy/formats/anthropic/register.ts +0 -4
  297. package/src/proxy/formats/anthropic/schemas.ts +0 -6
  298. package/src/proxy/formats/anthropic/stream.ts +0 -205
  299. package/src/proxy/formats/handler.ts +0 -46
  300. package/src/proxy/formats/index.ts +0 -13
  301. package/src/proxy/formats/jsonSchema.ts +0 -2
  302. package/src/proxy/formats/openai/alibabaProvider.ts +0 -34
  303. package/src/proxy/formats/openai/codexMetadata.ts +0 -61
  304. package/src/proxy/formats/openai/handler.ts +0 -133
  305. package/src/proxy/formats/openai/index.ts +0 -33
  306. package/src/proxy/formats/openai/provider.ts +0 -50
  307. package/src/proxy/formats/openai/register.ts +0 -4
  308. package/src/proxy/formats/openai/schemas.ts +0 -25
  309. package/src/proxy/formats/openai/stream.ts +0 -600
  310. package/src/proxy/formats/openai/zhipuProvider.ts +0 -31
  311. package/src/proxy/formats/providerAdapter.ts +0 -50
  312. package/src/proxy/formats/providerRegistry.ts +0 -51
  313. package/src/proxy/formats/providers/index.ts +0 -3
  314. package/src/proxy/formats/registry.ts +0 -65
  315. package/src/proxy/groupEvidenceExporter.ts +0 -371
  316. package/src/proxy/groupStore.ts +0 -336
  317. package/src/proxy/handler.ts +0 -729
  318. package/src/proxy/identityProxy.ts +0 -1842
  319. package/src/proxy/jsonlRecovery.ts +0 -554
  320. package/src/proxy/logBodyChunks.ts +0 -91
  321. package/src/proxy/logFinalizer.ts +0 -777
  322. package/src/proxy/logFinalizer.worker.ts +0 -24
  323. package/src/proxy/logImportUpload.ts +0 -128
  324. package/src/proxy/logImporter.ts +0 -813
  325. package/src/proxy/logIndex.ts +0 -663
  326. package/src/proxy/logSearch.ts +0 -122
  327. package/src/proxy/logger.ts +0 -537
  328. package/src/proxy/openaiOrphanToolStrip.ts +0 -142
  329. package/src/proxy/platformCommands.ts +0 -258
  330. package/src/proxy/privateDataPath.ts +0 -419
  331. package/src/proxy/providerImporters.ts +0 -578
  332. package/src/proxy/providerScanStore.ts +0 -87
  333. package/src/proxy/providerSecretStore.ts +0 -216
  334. package/src/proxy/providers.ts +0 -998
  335. package/src/proxy/rawStreamCapture.ts +0 -342
  336. package/src/proxy/runFailures.ts +0 -94
  337. package/src/proxy/runStore.ts +0 -214
  338. package/src/proxy/runtimeAdmission.ts +0 -55
  339. package/src/proxy/runtimeHealth.ts +0 -221
  340. package/src/proxy/runtimeMetrics.ts +0 -149
  341. package/src/proxy/runtimeShutdown.ts +0 -157
  342. package/src/proxy/schemas.ts +0 -112
  343. package/src/proxy/sessionArchive.ts +0 -479
  344. package/src/proxy/sessionInfo.ts +0 -266
  345. package/src/proxy/sessionProcess.ts +0 -194
  346. package/src/proxy/sessionRuntime.ts +0 -121
  347. package/src/proxy/sessionSupervisor.ts +0 -305
  348. package/src/proxy/sessionWorkerEntry.ts +0 -26
  349. package/src/proxy/shutdownCoordinator.ts +0 -90
  350. package/src/proxy/socketTracker.ts +0 -639
  351. package/src/proxy/sqliteLogIndex.ts +0 -733
  352. package/src/proxy/storageLifecycle.ts +0 -432
  353. package/src/proxy/store.ts +0 -1773
  354. package/src/proxy/streamTiming.ts +0 -31
  355. package/src/proxy/toolSchemaWarnings.ts +0 -529
  356. package/src/proxy/uiAssetServer.ts +0 -106
  357. package/src/proxy/upstream.ts +0 -127
  358. package/src/services/alerts.ts +0 -12
  359. package/src/services/groups.ts +0 -119
  360. package/src/services/piAgent.ts +0 -421
  361. package/src/services/piAgentRetrieval.ts +0 -1180
  362. package/src/services/result.ts +0 -17
  363. package/src/services/runs.ts +0 -76
  364. package/src/ui/bootstrap.ts +0 -167
  365. package/src/ui/index.html +0 -38
  366. package/src/ui/logsStreamClient.ts +0 -20
  367. package/src/ui/main.tsx +0 -13
  368. package/src/ui/public/runtime-config.js +0 -2
  369. package/src/ui/router.tsx +0 -13
  370. package/src/ui/routes/__root.tsx +0 -69
  371. package/src/ui/routes/index.tsx +0 -35
  372. package/src/ui/routes/session/$sessionId.tsx +0 -50
  373. package/src/ui/runtimeDiscovery.ts +0 -50
@@ -1,729 +0,0 @@
1
- import { Buffer } from "node:buffer";
2
- import { createLog, getNextLogId, persistFinalizedLogUpdate, type CapturedLog } from "./store";
3
- import { appendLogEntry, logger } from "./logger";
4
- import { extractRequestMetadata } from "./schemas";
5
- import { findProviderByModel, type ProviderConfig } from "./providers";
6
- import { getClientInfo } from "./socketTracker";
7
- import { formatForPath } from "./formats";
8
- import {
9
- PROXY_IDENTITY,
10
- PRESERVE_HEADERS,
11
- PATH_V1_MESSAGES,
12
- HEADER_CONTENT_TYPE,
13
- HEADER_USER_AGENT,
14
- HEADER_X_PROXY_IDENTITY,
15
- HEADER_CONTENT_ENCODING,
16
- HEADER_CONTENT_LENGTH,
17
- CONTENT_TYPE_EVENT_STREAM,
18
- SENSITIVE_HEADERS,
19
- STATUS_FORBIDDEN,
20
- STATUS_BAD_GATEWAY,
21
- STATUS_PAYLOAD_TOO_LARGE,
22
- MAX_REQUEST_BODY_BYTES,
23
- } from "./constants";
24
- import { getConfig } from "./config";
25
- import { safeFetch } from "../lib/safeFetch";
26
-
27
- const ALLOW_LOOPBACK = process.env["AGENT_INSPECTOR_ALLOW_LOOPBACK"] === "1";
28
- const EMPTY_UPSTREAM_BODY_ERROR = "Upstream response did not include a body";
29
- const UPSTREAM_STREAM_ERROR = "Upstream stream terminated unexpectedly";
30
- const UPSTREAM_REQUEST_FAILED = "Proxy error: upstream request failed";
31
- const HEADER_ERROR_CODE = "x-agent-inspector-error-code";
32
- import { stripClaudeCodeBillingHeader } from "../lib/claudeCodeStrip";
33
- import { stripOpenAIOrphanToolMessages } from "./openaiOrphanToolStrip";
34
- import { maskApiKey } from "../lib/mask";
35
- import { buildFileLogEntry, type FinalizeLogJob } from "./logFinalizer";
36
- import { enqueueFinalizeLogJob } from "./sessionRuntime";
37
- import { RawStreamCapture } from "./rawStreamCapture";
38
- import {
39
- acquireRuntimeRequest,
40
- getRuntimeAdmissionState,
41
- type RuntimeRequestLease,
42
- } from "./runtimeAdmission";
43
- import { DEFAULT_RUNTIME_BUDGET_POLICY, resourceLimitResponse } from "../lib/resourceLimits";
44
- import {
45
- buildUpstreamUrl,
46
- describeApiRoute,
47
- getProxyApiPath,
48
- injectProviderAuth,
49
- providerSupportsApiRoute,
50
- selectUpstreamBase,
51
- setUpstreamHost,
52
- } from "./upstream";
53
-
54
- /**
55
- * Strips all custom/non-standard headers from the request and replaces with
56
- * unified proxy identity. Only preserves standard HTTP headers needed for API calls.
57
- */
58
- function buildProxyHeaders(
59
- originalHeaders: Headers,
60
- captureRawHeaders: boolean,
61
- ): {
62
- headers: Headers;
63
- rawHeaders: Record<string, string> | undefined;
64
- } {
65
- const rawHeaders: Record<string, string> | undefined = captureRawHeaders ? {} : undefined;
66
- const headers = new Headers();
67
-
68
- if (rawHeaders !== undefined) {
69
- originalHeaders.forEach((value, key) => {
70
- const lowerKey = key.toLowerCase();
71
- rawHeaders[lowerKey] = SENSITIVE_HEADERS.has(lowerKey) ? maskApiKey(value) : value;
72
- });
73
- }
74
-
75
- headers.set(HEADER_USER_AGENT, PROXY_IDENTITY);
76
- headers.set(HEADER_X_PROXY_IDENTITY, PROXY_IDENTITY);
77
-
78
- for (const name of PRESERVE_HEADERS) {
79
- const value = originalHeaders.get(name);
80
- if (value !== null) {
81
- headers.set(name, value);
82
- }
83
- }
84
-
85
- return { headers, rawHeaders };
86
- }
87
-
88
- type ParsedRequestPath = {
89
- apiPath: string;
90
- isMessages: boolean;
91
- isChatCompletions: boolean;
92
- normalizedPath: string;
93
- };
94
-
95
- function parseRequestPath(req: Request, url: URL): ParsedRequestPath {
96
- const route = describeApiRoute(getProxyApiPath(url));
97
- const isPost = req.method === "POST";
98
- const isChatCompletions = isPost && route.isChatCompletions;
99
- const isMessages = isPost && (route.endpointPath === PATH_V1_MESSAGES || route.isChatCompletions);
100
-
101
- return {
102
- apiPath: route.apiPath,
103
- isMessages,
104
- isChatCompletions,
105
- normalizedPath: route.normalizedPath,
106
- };
107
- }
108
-
109
- function errorMessage(err: unknown): string {
110
- return err instanceof Error ? err.message : String(err);
111
- }
112
-
113
- function protocolLabel(route: ReturnType<typeof describeApiRoute>): string {
114
- if (route.isResponses) return "openai-responses";
115
- if (route.isChatCompletions) return "openai-chat-completions";
116
- return "anthropic-messages";
117
- }
118
-
119
- function requiredProviderUrlField(route: ReturnType<typeof describeApiRoute>): string {
120
- if (route.isResponses) return "openaiResponsesBaseUrl or openaiBaseUrl";
121
- if (route.isChatCompletions) return "openaiBaseUrl";
122
- return "anthropicBaseUrl";
123
- }
124
-
125
- function configuredProviderUrlFields(provider: ProviderConfig): string[] {
126
- const fields: string[] = [];
127
- if (provider.anthropicBaseUrl !== undefined && provider.anthropicBaseUrl.trim() !== "") {
128
- fields.push("anthropicBaseUrl");
129
- }
130
- if (provider.openaiBaseUrl !== undefined && provider.openaiBaseUrl.trim() !== "") {
131
- fields.push("openaiBaseUrl");
132
- }
133
- if (
134
- provider.openaiResponsesBaseUrl !== undefined &&
135
- provider.openaiResponsesBaseUrl.trim() !== ""
136
- ) {
137
- fields.push("openaiResponsesBaseUrl");
138
- }
139
- if (provider.baseUrl !== undefined && provider.baseUrl.trim() !== "") {
140
- fields.push("baseUrl");
141
- }
142
- return fields;
143
- }
144
-
145
- type UnsupportedProviderRoute = {
146
- code: "request-model-missing" | "provider-not-found" | "provider-route-mismatch";
147
- detail: string;
148
- };
149
-
150
- function unsupportedProviderRoute(
151
- model: string | null,
152
- route: ReturnType<typeof describeApiRoute>,
153
- provider: ProviderConfig | null,
154
- ): UnsupportedProviderRoute {
155
- if (model === null) {
156
- const detail = `model could not be extracted; path=${route.normalizedPath}; protocol=${protocolLabel(route)}`;
157
- logger.warn(`[handler] Unsupported provider route: code=request-model-missing; ${detail}`);
158
- return { code: "request-model-missing", detail };
159
- }
160
-
161
- if (provider === null) {
162
- const detail = `no Provider matched model=${model}; path=${route.normalizedPath}; protocol=${protocolLabel(route)}`;
163
- logger.warn(`[handler] Unsupported provider route: code=provider-not-found; ${detail}`);
164
- return { code: "provider-not-found", detail };
165
- }
166
-
167
- const configuredFields = configuredProviderUrlFields(provider);
168
- const detail =
169
- `Provider "${provider.name}" (${provider.id}) matched model=${model}, ` +
170
- `but request path ${route.normalizedPath} requires ${protocolLabel(route)} via ` +
171
- `${requiredProviderUrlField(route)}; configured URL fields=${
172
- configuredFields.length > 0 ? configuredFields.join(",") : "none"
173
- }`;
174
- logger.warn(`[handler] Unsupported provider route: code=provider-route-mismatch; ${detail}`);
175
- return { code: "provider-route-mismatch", detail };
176
- }
177
-
178
- type BodyReadResult = { ok: true; body: string | null } | { ok: false; reason: "too-large" };
179
-
180
- /**
181
- * Reads the request body as text, enforcing a maximum byte limit to prevent
182
- * memory exhaustion from oversized payloads. Returns the body string, or null
183
- * if the request has no body.
184
- */
185
- async function readBodyWithLimit(req: Request, limitBytes: number): Promise<BodyReadResult> {
186
- if (req.body === null || req.method === "GET" || req.method === "HEAD") {
187
- return { ok: true, body: null };
188
- }
189
-
190
- // Fast path: if Content-Length is present and exceeds the limit, reject
191
- // without reading any bytes.
192
- const contentLengthHeader = req.headers.get(HEADER_CONTENT_LENGTH);
193
- if (contentLengthHeader !== null) {
194
- const declared = Number.parseInt(contentLengthHeader, 10);
195
- if (Number.isFinite(declared) && declared > limitBytes) {
196
- return { ok: false, reason: "too-large" };
197
- }
198
- }
199
-
200
- const reader = req.body.getReader();
201
- const decoder = new TextDecoder();
202
- const chunks: string[] = [];
203
- let totalBytes = 0;
204
-
205
- try {
206
- for (;;) {
207
- const { done, value } = await reader.read();
208
- if (done) break;
209
- totalBytes += value.byteLength;
210
- if (totalBytes > limitBytes) {
211
- return { ok: false, reason: "too-large" };
212
- }
213
- chunks.push(decoder.decode(value, { stream: true }));
214
- }
215
- chunks.push(decoder.decode());
216
- return { ok: true, body: chunks.join("") };
217
- } finally {
218
- reader.releaseLock();
219
- }
220
- }
221
-
222
- function scheduleLogFinalization(job: FinalizeLogJob): void {
223
- void enqueueFinalizeLogJob(job).catch((err) => {
224
- logger.error(
225
- `[handler] Session finalization task failed for log #${job.log.id}:`,
226
- errorMessage(err),
227
- );
228
- });
229
- }
230
-
231
- function handleNonStreamingResponse(
232
- upstreamRes: Response,
233
- responseBody: string,
234
- startTime: number,
235
- upstreamUrl: string,
236
- log: CapturedLog,
237
- ): Response {
238
- const elapsedMs = Date.now() - startTime;
239
-
240
- scheduleLogFinalization({
241
- type: "non-streaming",
242
- log,
243
- upstreamUrl,
244
- elapsedMs,
245
- responseStatus: upstreamRes.status,
246
- responseBody,
247
- });
248
-
249
- const responseHeaders = new Headers(upstreamRes.headers);
250
- responseHeaders.delete(HEADER_CONTENT_ENCODING);
251
- responseHeaders.delete(HEADER_CONTENT_LENGTH);
252
-
253
- return new Response(responseBody, {
254
- status: upstreamRes.status,
255
- headers: responseHeaders,
256
- });
257
- }
258
-
259
- function applyRawStreamCaptureStatus(log: CapturedLog, capture: RawStreamCapture): void {
260
- const status = capture.status;
261
- log.captureIncomplete = status.incomplete;
262
- log.droppedChunks = status.droppedChunks;
263
- log.droppedBytes = status.droppedBytes;
264
- log.captureIncompleteReason = status.reason;
265
- }
266
-
267
- function applyUpstreamStreamError(log: CapturedLog): void {
268
- log.captureIncomplete = true;
269
- log.captureIncompleteReason = "upstream-stream-error";
270
- log.error = UPSTREAM_STREAM_ERROR;
271
- }
272
-
273
- async function handleStreamingResponse(
274
- upstreamRes: Response,
275
- req: Request,
276
- startTime: number,
277
- upstreamUrl: string,
278
- log: CapturedLog,
279
- collectStreamingChunks: boolean,
280
- ): Promise<Response> {
281
- log.streaming = true;
282
- log.responseStatus = upstreamRes.status;
283
-
284
- if (upstreamRes.body === null) {
285
- log.elapsedMs = Date.now() - startTime;
286
- log.responseStatus = STATUS_BAD_GATEWAY;
287
- log.responseText = EMPTY_UPSTREAM_BODY_ERROR;
288
- log.captureIncomplete = true;
289
- log.droppedChunks = 0;
290
- log.droppedBytes = 0;
291
- log.captureIncompleteReason = "upstream-stream-error";
292
- log.error = EMPTY_UPSTREAM_BODY_ERROR;
293
- appendLogEntry({ ...buildFileLogEntry(log, upstreamUrl), error: EMPTY_UPSTREAM_BODY_ERROR });
294
- await persistFinalizedLogUpdate(log);
295
- return new Response("No response body", { status: STATUS_BAD_GATEWAY });
296
- }
297
-
298
- const capture = new RawStreamCapture();
299
- const decoder = new TextDecoder();
300
- let finalizationScheduled = false;
301
- let firstChunkMs: number | null = null;
302
- let pendingDecoderBytes = 0;
303
- let downstreamCancelled = false;
304
-
305
- const submitDecodedText = (text: string, flush: boolean = false): void => {
306
- const observedBytes = flush
307
- ? pendingDecoderBytes
308
- : Math.min(pendingDecoderBytes, Buffer.byteLength(text, "utf-8"));
309
- pendingDecoderBytes -= observedBytes;
310
- if (text === "" && observedBytes === 0) return;
311
- capture.tryAppend(text, observedBytes);
312
- applyRawStreamCaptureStatus(log, capture);
313
- };
314
-
315
- const scheduleStreamingFinalization = (
316
- type: "streaming" | "stream-abort",
317
- upstreamStreamFailed: boolean = false,
318
- ): void => {
319
- if (finalizationScheduled) return;
320
- finalizationScheduled = true;
321
-
322
- if (pendingDecoderBytes > 0) {
323
- capture.recordDroppedObservation("pipeline-closed", pendingDecoderBytes);
324
- pendingDecoderBytes = 0;
325
- }
326
-
327
- const totalStreamMs = Date.now() - startTime;
328
- const elapsedMs = totalStreamMs;
329
- void capture
330
- .finalize()
331
- .then((rawStream) => {
332
- applyRawStreamCaptureStatus(log, capture);
333
- if (upstreamStreamFailed) applyUpstreamStreamError(log);
334
- switch (type) {
335
- case "streaming":
336
- scheduleLogFinalization({
337
- type: "streaming",
338
- log,
339
- upstreamUrl,
340
- elapsedMs,
341
- responseStatus: upstreamRes.status,
342
- rawStream,
343
- collectStreamingChunks,
344
- firstChunkMs,
345
- totalStreamMs,
346
- terminalError: upstreamStreamFailed ? UPSTREAM_STREAM_ERROR : null,
347
- });
348
- return;
349
- case "stream-abort":
350
- scheduleLogFinalization({
351
- type: "stream-abort",
352
- log,
353
- upstreamUrl,
354
- elapsedMs,
355
- rawStream,
356
- hasChunks: capture.hasChunks,
357
- collectStreamingChunks,
358
- firstChunkMs,
359
- totalStreamMs,
360
- });
361
- return;
362
- }
363
- })
364
- .catch((error: unknown) => {
365
- const message = error instanceof Error ? error.message : String(error);
366
- applyRawStreamCaptureStatus(log, capture);
367
- if (upstreamStreamFailed) applyUpstreamStreamError(log);
368
- log.captureIncomplete = true;
369
- log.captureIncompleteReason ??= "finalize-failed";
370
- logger.error(
371
- `[handler] Raw stream finalization failed for log #${String(log.id)}:`,
372
- message,
373
- );
374
- scheduleLogFinalization({
375
- type: "stream-abort",
376
- log,
377
- upstreamUrl,
378
- elapsedMs,
379
- rawStream: { type: "memory", rawStream: "" },
380
- hasChunks: capture.hasChunks,
381
- collectStreamingChunks,
382
- firstChunkMs,
383
- totalStreamMs,
384
- });
385
- });
386
- };
387
-
388
- const transform = new TransformStream<Uint8Array, Uint8Array>({
389
- transform(chunk, controller) {
390
- if (firstChunkMs === null) firstChunkMs = Date.now() - startTime;
391
- controller.enqueue(chunk);
392
- pendingDecoderBytes += chunk.byteLength;
393
- submitDecodedText(decoder.decode(chunk, { stream: true }));
394
- },
395
- flush() {
396
- submitDecodedText(decoder.decode(), true);
397
- scheduleStreamingFinalization("streaming");
398
- },
399
- });
400
-
401
- // pipeTo returns a promise that rejects when either side errors. Without
402
- // this catch, an upstream connection drop skips flush() and leaves the log
403
- // permanently un-finalized with a leaked temp file.
404
- void upstreamRes.body.pipeTo(transform.writable).catch(() => {
405
- if (downstreamCancelled || req.signal?.aborted === true) {
406
- scheduleStreamingFinalization("stream-abort");
407
- return;
408
- }
409
- scheduleStreamingFinalization("streaming", true);
410
- });
411
- const transformedReader = transform.readable.getReader();
412
- const loggedStream = new ReadableStream<Uint8Array>({
413
- async pull(controller) {
414
- try {
415
- const next = await transformedReader.read();
416
- if (next.done) {
417
- controller.close();
418
- return;
419
- }
420
- controller.enqueue(next.value);
421
- } catch (error) {
422
- controller.error(error);
423
- }
424
- },
425
- async cancel(reason) {
426
- downstreamCancelled = true;
427
- scheduleStreamingFinalization("stream-abort");
428
- await transformedReader.cancel(reason);
429
- },
430
- });
431
-
432
- if (req.signal === undefined) {
433
- logger.warn("[handler] req.signal is undefined; abort detection disabled for this request");
434
- } else {
435
- req.signal.addEventListener("abort", () => {
436
- if (log.responseText === null) {
437
- logger.info(`[handler] Streaming client aborted: ${log.method} ${log.path}`);
438
- scheduleStreamingFinalization("stream-abort");
439
- }
440
- });
441
- }
442
-
443
- const responseHeaders = new Headers(upstreamRes.headers);
444
- responseHeaders.delete(HEADER_CONTENT_ENCODING);
445
- responseHeaders.delete(HEADER_CONTENT_LENGTH);
446
-
447
- return new Response(loggedStream, {
448
- status: upstreamRes.status,
449
- headers: responseHeaders,
450
- });
451
- }
452
-
453
- async function handleAdmittedProxy(req: Request): Promise<Response> {
454
- const url = new URL(req.url);
455
- const parsed = parseRequestPath(req, url);
456
-
457
- // Read body with a size limit to prevent memory exhaustion from oversized
458
- // payloads. The limit is generous (50 MB) — legitimate LLM API requests are
459
- // well under this, but a malicious client could send a multi-GB body to OOM
460
- // the proxy.
461
- const bodyRead = await readBodyWithLimit(req, MAX_REQUEST_BODY_BYTES);
462
- if (!bodyRead.ok) {
463
- logger.warn(`[handler] Rejected oversized request body: ${req.method} ${parsed.apiPath}`);
464
- return new Response("Request body too large", { status: STATUS_PAYLOAD_TOO_LARGE });
465
- }
466
- const requestBody = bodyRead.body;
467
-
468
- // Opt-in: strip Claude Code's synthetic billing-header text block from the
469
- // system prompt. Only runs for Anthropic-format requests (those whose path
470
- // resolves to /v1/messages). The flag is read once per request from the
471
- // runtime config, so a PATCH to /api/config takes effect on the next
472
- // request without a restart.
473
- //
474
- // Important: the original `requestBody` must be preserved so the log stores
475
- // the unmodified request (the "Raw Request" tab needs the true bytes the
476
- // client sent). The rewritten body is only used for the upstream fetch.
477
- const config = getConfig();
478
- let bodyToForward = requestBody;
479
- if (config.stripClaudeCodeBillingHeader && requestBody !== null && parsed.isMessages) {
480
- const stripped = stripClaudeCodeBillingHeader(requestBody);
481
- if (stripped.removed > 0) {
482
- logger.info(
483
- `[handler] Stripped ${stripped.removed} Claude Code billing-header block(s) from system prompt`,
484
- );
485
- bodyToForward = stripped.body;
486
- }
487
- }
488
-
489
- // Always-on OpenAI Chat Completions format guard: every
490
- // `role: "tool"` message to reference a `tool_call_id` present in a
491
- // preceding assistant message's `tool_calls`. Buggy clients sometimes
492
- // record a tool result without the matching assistant tool_call, which
493
- // makes the upstream reject the request with a 400 ("tool result's tool
494
- // id(call_xxx) not found"). Drop the orphan messages so the proxy can
495
- // forward a valid request; the original body is still kept in the log.
496
- if (bodyToForward !== null && parsed.isChatCompletions) {
497
- const stripped = stripOpenAIOrphanToolMessages(bodyToForward);
498
- if (stripped.removed > 0) {
499
- logger.warn(
500
- `[handler] Dropped ${stripped.removed} orphan OpenAI tool message(s) with tool_call_id(s) ${JSON.stringify(stripped.orphanIds)} — the client sent a tool result with no matching assistant.tool_calls`,
501
- );
502
- bodyToForward = stripped.body;
503
- }
504
- }
505
-
506
- // Parse the request body exactly once. The handler needs the model for
507
- // provider routing and the session id for the log; `createLog` would
508
- // otherwise re-parse the same body to extract the same fields.
509
- const { model, sessionId } = extractRequestMetadata(requestBody, req.headers);
510
-
511
- // Find provider config using already-extracted model (not calling extractModelFromBody again)
512
- const matchedProviderConfig = model !== null ? findProviderByModel(model) : null;
513
- const route = describeApiRoute(parsed.apiPath);
514
-
515
- // Model matching is necessary but not sufficient: the Provider must explicitly expose the
516
- // protocol selected by the request path. This guard stays before log creation and fetch so a
517
- // protocol mismatch has the same zero-side-effect contract as an unconfigured model.
518
- if (
519
- model === null ||
520
- matchedProviderConfig === null ||
521
- !providerSupportsApiRoute(route, matchedProviderConfig)
522
- ) {
523
- const unsupported = unsupportedProviderRoute(model, route, matchedProviderConfig);
524
- return new Response("Forbidden: unsupported provider", {
525
- status: STATUS_FORBIDDEN,
526
- headers: {
527
- [HEADER_ERROR_CODE]: unsupported.code,
528
- },
529
- });
530
- }
531
-
532
- const upstreamBase = selectUpstreamBase(route, matchedProviderConfig);
533
- const upstreamUrl = buildUpstreamUrl(upstreamBase, parsed.normalizedPath);
534
-
535
- const startTime = Date.now();
536
-
537
- const captureFullDetails = config.captureMode === "full";
538
- const { headers: upstreamHeaders, rawHeaders } = buildProxyHeaders(
539
- req.headers,
540
- captureFullDetails,
541
- );
542
- setUpstreamHost(upstreamHeaders, upstreamBase);
543
- injectProviderAuth(upstreamHeaders, matchedProviderConfig);
544
-
545
- // Pick the format handler from the request path. ProviderConfig.format is only a
546
- // hint used as a fallback when no anthropicBaseUrl/openaiBaseUrl is set, and
547
- // does NOT describe what the upstream actually returns: a provider configured
548
- // with format="openai" may still expose an Anthropic-compatible endpoint
549
- // (e.g. MiniMax's /anthropic path), and the response shape always follows
550
- // the URL path the client hit, not the provider.format label.
551
- const formatHandler = formatForPath(parsed.apiPath);
552
- if (formatHandler === null) {
553
- return new Response("Forbidden: unsupported format", { status: STATUS_FORBIDDEN });
554
- }
555
-
556
- // Acquire client info and the next log id in parallel. On Windows
557
- // `getClientInfo` shells out to PowerShell and is the single slowest
558
- // thing on the hot path; serializing it before `createLog` paid the
559
- // full PowerShell round-trip on top of everything else before
560
- // `fetch` is even issued.
561
- const [clientInfo, preAcquiredId] = await Promise.all([getClientInfo(req), getNextLogId()]);
562
- const upstreamHeadersObj: Record<string, string> | undefined = captureFullDetails
563
- ? {}
564
- : undefined;
565
- if (upstreamHeadersObj !== undefined) {
566
- upstreamHeaders.forEach((value, key) => {
567
- const lowerKey = key.toLowerCase();
568
- upstreamHeadersObj[lowerKey] = SENSITIVE_HEADERS.has(lowerKey) ? maskApiKey(value) : value;
569
- });
570
- }
571
- const log = await createLog(
572
- req.method,
573
- parsed.apiPath,
574
- requestBody,
575
- req.headers,
576
- clientInfo,
577
- rawHeaders,
578
- upstreamHeadersObj,
579
- formatHandler.format,
580
- model,
581
- sessionId,
582
- preAcquiredId,
583
- );
584
-
585
- let upstreamRes: Response;
586
- try {
587
- const fetchResult = await safeFetch(
588
- upstreamUrl,
589
- {
590
- method: req.method,
591
- headers: upstreamHeaders,
592
- body: bodyToForward,
593
- signal: req.signal,
594
- },
595
- {
596
- allowLoopback: ALLOW_LOOPBACK,
597
- deadlineMs: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.fetchDeadlineMs,
598
- responseBytes: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.fetchResponseBytes,
599
- },
600
- );
601
- if (!fetchResult.ok) {
602
- const status =
603
- fetchResult.kind === "network-policy"
604
- ? STATUS_FORBIDDEN
605
- : fetchResult.kind === "resource-limit"
606
- ? STATUS_PAYLOAD_TOO_LARGE
607
- : STATUS_BAD_GATEWAY;
608
- logger.warn(`[handler] Safe fetch blocked ${upstreamUrl}: ${fetchResult.reason}`);
609
- log.elapsedMs = Date.now() - startTime;
610
- log.responseStatus = status;
611
- log.responseText = fetchResult.reason;
612
- appendLogEntry({ ...buildFileLogEntry(log, upstreamUrl), error: fetchResult.reason });
613
- await persistFinalizedLogUpdate(log);
614
- return new Response(
615
- fetchResult.kind === "network-policy"
616
- ? "Forbidden: upstream URL is not permitted"
617
- : fetchResult.kind === "resource-limit"
618
- ? "Proxy error: upstream response exceeded configured limits"
619
- : fetchResult.kind === "timeout"
620
- ? "Proxy error: upstream request timed out"
621
- : "Proxy error: upstream redirects are not permitted",
622
- { status },
623
- );
624
- }
625
- upstreamRes = fetchResult.response;
626
- } catch (err) {
627
- log.elapsedMs = Date.now() - startTime;
628
- // Check if it was a client abort (not a proxy error)
629
- if (err instanceof Error && err.name === "AbortError") {
630
- logger.info(`[handler] Client aborted: ${req.method} ${parsed.apiPath}`);
631
- log.responseStatus = 499; // Client Closed Request (non-standard but descriptive)
632
- log.responseText = "Client aborted";
633
- appendLogEntry({ ...buildFileLogEntry(log, upstreamUrl), error: "Client aborted" });
634
- await persistFinalizedLogUpdate(log);
635
- return new Response("Client aborted", { status: 499 });
636
- }
637
- const upstreamError = errorMessage(err);
638
- logger.error(`[handler] Proxy error: ${req.method} ${parsed.apiPath}`, upstreamError);
639
- log.responseStatus = STATUS_BAD_GATEWAY;
640
- log.responseText = upstreamError;
641
- log.error = upstreamError;
642
- appendLogEntry({ ...buildFileLogEntry(log, upstreamUrl), error: upstreamError });
643
- await persistFinalizedLogUpdate(log);
644
- return new Response(UPSTREAM_REQUEST_FAILED, { status: STATUS_BAD_GATEWAY });
645
- }
646
-
647
- const isStream =
648
- upstreamRes.headers.get(HEADER_CONTENT_TYPE)?.includes(CONTENT_TYPE_EVENT_STREAM) ?? false;
649
-
650
- if (!isStream) {
651
- const responseBody = await upstreamRes.text();
652
- return handleNonStreamingResponse(upstreamRes, responseBody, startTime, upstreamUrl, log);
653
- }
654
-
655
- return handleStreamingResponse(upstreamRes, req, startTime, upstreamUrl, log, captureFullDetails);
656
- }
657
-
658
- function responseWithRuntimeLease(response: Response, lease: RuntimeRequestLease): Response {
659
- if (response.body === null) {
660
- lease.release();
661
- return response;
662
- }
663
-
664
- const reader = response.body.getReader();
665
- let released = false;
666
- const release = (): void => {
667
- if (released) return;
668
- released = true;
669
- lease.release();
670
- };
671
- const trackedBody = new ReadableStream<Uint8Array>({
672
- async pull(controller) {
673
- try {
674
- const chunk = await reader.read();
675
- if (chunk.done) {
676
- controller.close();
677
- release();
678
- return;
679
- }
680
- controller.enqueue(chunk.value);
681
- } catch (error) {
682
- controller.error(error);
683
- release();
684
- }
685
- },
686
- async cancel(reason) {
687
- try {
688
- await reader.cancel(reason);
689
- } finally {
690
- release();
691
- }
692
- },
693
- });
694
-
695
- return new Response(trackedBody, {
696
- status: response.status,
697
- statusText: response.statusText,
698
- headers: response.headers,
699
- });
700
- }
701
-
702
- export async function handleProxy(req: Request): Promise<Response> {
703
- const lease = acquireRuntimeRequest();
704
- if (lease === null) {
705
- const admission = getRuntimeAdmissionState();
706
- if (admission.acceptingRequests) {
707
- return resourceLimitResponse({
708
- error: `Proxy request capacity reached. At most ${String(admission.capacity)} active requests are allowed; retry after a response completes.`,
709
- code: "proxy-capacity-exceeded",
710
- status: 503,
711
- resource: "proxy-requests",
712
- limit: admission.capacity,
713
- retryable: true,
714
- retryAfterSeconds: 1,
715
- });
716
- }
717
- return Response.json(
718
- { error: "Agent Inspector is shutting down" },
719
- { status: 503, headers: { "cache-control": "no-store", "retry-after": "5" } },
720
- );
721
- }
722
-
723
- try {
724
- return responseWithRuntimeLease(await handleAdmittedProxy(req), lease);
725
- } catch (error) {
726
- lease.release();
727
- return await Promise.reject(error);
728
- }
729
- }