@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,998 +0,0 @@
1
- import { z } from "zod";
2
- import Conf from "conf";
3
- import { randomUUID } from "crypto";
4
- import { existsSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
5
- import { dirname, join } from "node:path";
6
- import { maskApiKey } from "../lib/mask";
7
- import { logger } from "./logger";
8
- import { getDataDir, hasExplicitDataDir } from "./dataDir";
9
- import {
10
- ProviderConfigSchema,
11
- ProviderModelMetadataListSchema,
12
- ProviderModelNameSchema,
13
- ProviderModelsSchema,
14
- REDACTED_PROVIDER_API_KEY,
15
- toRedactedProviderDto,
16
- toRedactedProviderDtos,
17
- type ProviderConfig,
18
- type ProviderModelMetadata,
19
- type RedactedProviderDto,
20
- } from "../lib/providerContract";
21
- import { withBuiltinProviderModelMetadata } from "../lib/providerModelMetadata";
22
- import {
23
- createProviderSecretStore,
24
- secretValuesEqual,
25
- type ProviderSecretStore,
26
- } from "./providerSecretStore";
27
- import {
28
- ensurePrivateDirectorySync,
29
- PRIVATE_FILE_MODE,
30
- securePrivateFileSync,
31
- } from "./privateDataPath";
32
-
33
- export { ProviderConfigSchema };
34
- export type { ProviderConfig };
35
-
36
- const ProvidersStoreSchema = z.object({
37
- providers: z.array(ProviderConfigSchema),
38
- });
39
-
40
- type ProvidersStore = z.infer<typeof ProvidersStoreSchema>;
41
-
42
- function normalizeMetadataModelName(value: string): string {
43
- return value.trim().toLowerCase().replace(/\s+/g, "-");
44
- }
45
-
46
- function mergeModelMetadata(
47
- existing: readonly ProviderModelMetadata[] | undefined,
48
- incoming: readonly ProviderModelMetadata[] | undefined,
49
- ): ProviderModelMetadata[] | undefined {
50
- if (existing === undefined && incoming === undefined) return undefined;
51
-
52
- const merged = new Map<string, ProviderModelMetadata>();
53
- for (const metadata of existing ?? []) {
54
- merged.set(normalizeMetadataModelName(metadata.model), metadata);
55
- }
56
- for (const metadata of incoming ?? []) {
57
- merged.set(normalizeMetadataModelName(metadata.model), metadata);
58
- }
59
-
60
- return [...merged.values()].toSorted((left, right) => left.model.localeCompare(right.model));
61
- }
62
-
63
- const LegacyProviderConfigSchema = z.object({
64
- id: z.string().optional(),
65
- name: z.string().optional(),
66
- type: z.enum(["anthropic", "openai"]).optional(),
67
- format: z.enum(["anthropic", "openai"]).optional(),
68
- apiKey: z.string().optional(),
69
- model: ProviderModelNameSchema.optional(),
70
- models: ProviderModelsSchema.optional(),
71
- baseUrl: z.string().optional(),
72
- anthropicBaseUrl: z.string().optional(),
73
- openaiBaseUrl: z.string().optional(),
74
- openaiResponsesBaseUrl: z.string().optional(),
75
- authHeader: z.enum(["bearer", "x-api-key"]).optional(),
76
- apiDocsUrl: z.string().optional(),
77
- modelMetadataUrl: z.string().optional(),
78
- modelMetadata: ProviderModelMetadataListSchema.optional(),
79
- source: z.enum(["company", "personal"]).optional(),
80
- createdAt: z.string().optional(),
81
- updatedAt: z.string().optional(),
82
- });
83
-
84
- export type LegacyProviderConfig = z.infer<typeof LegacyProviderConfigSchema>;
85
-
86
- const LegacyProviderStoreSchema = z.object({
87
- providers: z.array(LegacyProviderConfigSchema),
88
- });
89
-
90
- function legacyModels(provider: LegacyProviderConfig): string[] {
91
- const modelList = provider.models?.map((model) => model.trim()).filter((model) => model !== "");
92
- if (modelList !== undefined && modelList.length > 0) return modelList;
93
- const model = provider.model?.trim();
94
- if (model !== undefined && model !== "") return [model];
95
- return [""];
96
- }
97
-
98
- export function migrateLegacyProvider(
99
- provider: LegacyProviderConfig,
100
- index: number,
101
- now: string,
102
- ): ProviderConfig | null {
103
- const apiKey = normalizeApiKey(provider.apiKey ?? "");
104
- if (apiKey === "") return null;
105
-
106
- const format = provider.format ?? provider.type;
107
- const fallbackName =
108
- format === "openai" ? "OpenAI" : format === "anthropic" ? "Anthropic" : "Legacy Provider";
109
- const trimmedName = provider.name?.trim();
110
- const name =
111
- trimmedName !== undefined && trimmedName !== "" ? trimmedName : `${fallbackName} ${index + 1}`;
112
- const baseUrl = provider.baseUrl;
113
- const anthropicBaseUrl =
114
- provider.anthropicBaseUrl ?? (format === "anthropic" && baseUrl !== undefined ? baseUrl : "");
115
- const openaiBaseUrl =
116
- provider.openaiBaseUrl ?? (format === "openai" && baseUrl !== undefined ? baseUrl : "");
117
- const openaiResponsesBaseUrl = provider.openaiResponsesBaseUrl ?? "";
118
-
119
- const candidate: ProviderConfig = {
120
- id: provider.id ?? randomUUID(),
121
- name,
122
- apiKey,
123
- model: provider.model,
124
- models: legacyModels(provider),
125
- format,
126
- baseUrl,
127
- anthropicBaseUrl,
128
- openaiBaseUrl,
129
- openaiResponsesBaseUrl,
130
- authHeader: provider.authHeader ?? "bearer",
131
- apiDocsUrl: provider.apiDocsUrl,
132
- modelMetadataUrl: provider.modelMetadataUrl,
133
- modelMetadata: provider.modelMetadata,
134
- source: provider.source,
135
- createdAt: provider.createdAt ?? now,
136
- updatedAt: provider.updatedAt ?? now,
137
- };
138
-
139
- const parsed = ProviderConfigSchema.safeParse(candidate);
140
- return parsed.success ? withBuiltinProviderModelMetadata(parsed.data, now) : null;
141
- }
142
-
143
- function migrateFromDataDirConfig(targetStore: Conf<ProvidersStore>, dataDirPath: string): void {
144
- if (process.env["AGENT_INSPECTOR_CONFIG_PATH"] !== undefined) return;
145
- if (targetStore.get("providers", []).length > 0) return;
146
-
147
- const legacyConfigPath = join(dataDirPath, "config.json");
148
- if (!existsSync(legacyConfigPath)) return;
149
-
150
- try {
151
- const raw = readFileSync(legacyConfigPath, "utf-8");
152
- const parsedJson: unknown = JSON.parse(raw);
153
- const parsed = LegacyProviderStoreSchema.safeParse(parsedJson);
154
- if (!parsed.success) return;
155
-
156
- const now = new Date().toISOString();
157
- const migrated: ProviderConfig[] = [];
158
- for (let index = 0; index < parsed.data.providers.length; index++) {
159
- const provider = parsed.data.providers[index];
160
- if (provider === undefined) continue;
161
- const current = migrateLegacyProvider(provider, index, now);
162
- if (current !== null) migrated.push(current);
163
- }
164
- if (migrated.length === 0) return;
165
-
166
- if (!persistProviders(migrated)) {
167
- logger.warn(`[providers] Data-dir config migration left ${targetStore.path} unchanged`);
168
- return;
169
- }
170
- logger.info(
171
- `[providers] Migrated ${migrated.length} provider(s) from ${legacyConfigPath} to ${targetStore.path}`,
172
- );
173
- } catch (err) {
174
- logger.warn(`[providers] Data-dir config migration failed: ${String(err)}`);
175
- }
176
- }
177
-
178
- /**
179
- * One-time migration from the legacy conf v15 default location
180
- * (`%APPDATA%\agent-inspector-nodejs\Config\config.json` on Windows,
181
- * `$XDG_CONFIG_HOME/agent-inspector-nodejs/Config/config.json` on Linux).
182
- * Older versions of agent-inspector let the `conf` library pick the path,
183
- * which produced a project-suffixed directory under the OS user-config
184
- * root. Users who upgrade now have their providers copied to the new
185
- * `<dataDir>/providers.json` location; the legacy file is left in
186
- * place for safety and can be deleted manually.
187
- *
188
- * The check is "new store is still empty" rather than "new file doesn't
189
- * exist" because `Conf` materialises the defaults file on construction
190
- * — so the file is always present immediately after the `new Conf()`
191
- * call above, even on a brand-new install.
192
- */
193
- function migrateFromLegacyConfLocation(targetStore: Conf<ProvidersStore>): void {
194
- if (process.env["AGENT_INSPECTOR_CONFIG_PATH"] !== undefined) return;
195
- if (hasExplicitDataDir()) return;
196
- if (targetStore.get("providers", []).length > 0) return;
197
- let legacyStore: Conf<ProvidersStore> | undefined;
198
- try {
199
- legacyStore = new Conf<ProvidersStore>({
200
- projectName: "agent-inspector",
201
- defaults: { providers: [] },
202
- });
203
- const legacyProviders = legacyStore.get("providers", []);
204
- if (legacyProviders.length === 0) return;
205
- const now = new Date().toISOString();
206
- const migrated = legacyProviders.map((provider) =>
207
- withBuiltinProviderModelMetadata(provider, now),
208
- );
209
- if (!persistProviders(migrated)) {
210
- logger.warn(`[providers] Legacy migration left ${targetStore.path} unchanged`);
211
- return;
212
- }
213
- logger.info(
214
- `[providers] Migrated ${legacyProviders.length} provider(s) from ${legacyStore.path} to ${targetStore.path}`,
215
- );
216
- } catch (err) {
217
- logger.warn(`[providers] Legacy migration failed: ${String(err)}`);
218
- } finally {
219
- legacyStore?._closeWatcher();
220
- }
221
- }
222
-
223
- // Provider metadata uses Conf; API keys are stored separately by ProviderSecretStore.
224
- const configPath = process.env["AGENT_INSPECTOR_CONFIG_PATH"];
225
-
226
- // The data directory is shared with the runtime config (`config.ts`).
227
- // We pass `cwd` so conf writes the file as `<cwd>/<configName>.<ext>`,
228
- // i.e. `<dataDir>/providers.json`. The runtime config lives at
229
- // `<dataDir>/config.json`; the different filenames keep the two stores
230
- // from overwriting each other.
231
- const dataDir = getDataDir();
232
- const providerDataDir = configPath === undefined ? dataDir : dirname(configPath);
233
- if (!ensurePrivateDirectorySync(providerDataDir)) {
234
- logger.error(`[providers] Could not secure provider data directory ${providerDataDir}`);
235
- }
236
-
237
- export const store = new Conf<ProvidersStore>({
238
- cwd: dataDir,
239
- configName: "providers",
240
- defaults: {
241
- providers: [],
242
- },
243
- ...(configPath !== undefined ? { path: configPath } : {}),
244
- });
245
-
246
- const providerSecretStore: ProviderSecretStore = createProviderSecretStore(dirname(store.path));
247
-
248
- function providerMetadataStorageIsPrivate(forceFile = false): boolean {
249
- if (!ensurePrivateDirectorySync(dirname(store.path))) return false;
250
- if (!existsSync(store.path)) return true;
251
- return securePrivateFileSync(store.path, forceFile);
252
- }
253
-
254
- function writeProviderMetadata(providers: readonly ProviderConfig[]): boolean {
255
- if (!providerMetadataStorageIsPrivate()) {
256
- logger.error(`[providers] Refusing provider metadata write to unsafe path ${store.path}`);
257
- return false;
258
- }
259
- try {
260
- store.set("providers", [...providers]);
261
- } catch (error) {
262
- logger.error(`[providers] Could not persist provider metadata: ${String(error)}`);
263
- return false;
264
- }
265
- if (securePrivateFileSync(store.path, true)) return true;
266
- logger.error(`[providers] Could not secure provider metadata at ${store.path}`);
267
- return false;
268
- }
269
-
270
- if (!providerMetadataStorageIsPrivate(true)) {
271
- logger.error(`[providers] Provider metadata path is not private: ${store.path}`);
272
- }
273
-
274
- function providerWithResolvedSecret(provider: ProviderConfig): ProviderConfig | null {
275
- if (!providerSecretStore.isReference(provider.apiKey)) {
276
- logger.error(
277
- `[providers] Refusing plaintext secret for provider ${provider.id}; secure migration is required`,
278
- );
279
- return null;
280
- }
281
- const apiKey = providerSecretStore.read(provider.apiKey);
282
- if (apiKey !== null) return { ...provider, apiKey };
283
- logger.error(`[providers] Secret is missing or unreadable for provider "${provider.name}"`);
284
- return null;
285
- }
286
-
287
- function persistProviders(
288
- providers: readonly ProviderConfig[],
289
- removedProviderIds: ReadonlySet<string> = new Set(),
290
- ): boolean {
291
- const existingReferences = new Map<string, string>();
292
- const unresolvedRecords = new Map<string, ProviderConfig>();
293
- for (const candidate of store.get("providers", [])) {
294
- const parsed = ProviderConfigSchema.safeParse(candidate);
295
- if (parsed.success && providerSecretStore.isReference(parsed.data.apiKey)) {
296
- existingReferences.set(parsed.data.id, parsed.data.apiKey);
297
- if (providerSecretStore.read(parsed.data.apiKey) === null) {
298
- unresolvedRecords.set(parsed.data.id, parsed.data);
299
- }
300
- }
301
- }
302
-
303
- const persisted: ProviderConfig[] = [];
304
- for (const provider of providers) {
305
- const existingReference = existingReferences.get(provider.id);
306
- if (existingReference !== undefined) {
307
- const existingSecret = providerSecretStore.read(existingReference);
308
- if (existingSecret !== null && secretValuesEqual(existingSecret, provider.apiKey)) {
309
- persisted.push({ ...provider, apiKey: existingReference });
310
- continue;
311
- }
312
- }
313
-
314
- const reference = providerSecretStore.write(provider.id, provider.apiKey);
315
- if (reference !== null) {
316
- persisted.push({ ...provider, apiKey: reference });
317
- continue;
318
- }
319
- logger.error(
320
- `[providers] Secret store unavailable; refusing to persist provider ${provider.id}`,
321
- );
322
- return false;
323
- }
324
- const persistedIds = new Set(persisted.map((provider) => provider.id));
325
- for (const [id, provider] of unresolvedRecords) {
326
- if (!persistedIds.has(id) && !removedProviderIds.has(id)) persisted.push(provider);
327
- }
328
- return writeProviderMetadata(persisted);
329
- }
330
-
331
- function migratePlaintextProviderSecrets(): boolean {
332
- const raw = store.get("providers", []);
333
- let changed = false;
334
- const migrated: ProviderConfig[] = [];
335
- for (const candidate of raw) {
336
- const parsed = ProviderConfigSchema.safeParse(candidate);
337
- if (!parsed.success) {
338
- migrated.push(candidate);
339
- continue;
340
- }
341
- if (providerSecretStore.isReference(parsed.data.apiKey)) {
342
- migrated.push(parsed.data);
343
- continue;
344
- }
345
- const reference = providerSecretStore.write(parsed.data.id, parsed.data.apiKey);
346
- if (reference === null) {
347
- logger.error(
348
- `[providers] Could not migrate provider ${parsed.data.id} to secure storage; leaving the existing file unchanged`,
349
- );
350
- return false;
351
- }
352
- changed = true;
353
- migrated.push({ ...parsed.data, apiKey: reference });
354
- }
355
- if (!changed) return true;
356
- if (writeProviderMetadata(migrated)) return true;
357
- logger.error("[providers] Could not finish provider secret migration");
358
- return false;
359
- }
360
-
361
- function sanitizeProviderBackupValue(value: unknown): unknown {
362
- if (Array.isArray(value)) return value.map(sanitizeProviderBackupValue);
363
- if (value === null || typeof value !== "object") return value;
364
-
365
- return Object.fromEntries(
366
- Object.entries(value).map(([key, entry]) => {
367
- if (key === "apiKey" && typeof entry === "string") {
368
- return [key, "redacted"];
369
- }
370
- return [key, sanitizeProviderBackupValue(entry)];
371
- }),
372
- );
373
- }
374
-
375
- function sanitizeProviderBackup(): void {
376
- const backupPath = `${store.path}.bak`;
377
- if (!existsSync(backupPath)) return;
378
-
379
- const temporaryPath = `${backupPath}.${String(process.pid)}.tmp`;
380
- try {
381
- const parsed: unknown = JSON.parse(readFileSync(backupPath, "utf-8"));
382
- const sanitized = sanitizeProviderBackupValue(parsed);
383
- writeFileSync(temporaryPath, JSON.stringify(sanitized, null, 2), {
384
- encoding: "utf8",
385
- mode: PRIVATE_FILE_MODE,
386
- });
387
- if (!securePrivateFileSync(temporaryPath, true)) {
388
- rmSync(temporaryPath, { force: true });
389
- rmSync(backupPath, { force: true });
390
- logger.error("[providers] Removed provider backup after temporary permission failure");
391
- return;
392
- }
393
- renameSync(temporaryPath, backupPath);
394
- if (!securePrivateFileSync(backupPath, true)) {
395
- rmSync(backupPath, { force: true });
396
- logger.error("[providers] Removed provider backup after permission failure");
397
- }
398
- } catch (error) {
399
- rmSync(temporaryPath, { force: true });
400
- rmSync(backupPath, { force: true });
401
- logger.error(
402
- `[providers] Removed provider backup that could not be sanitized: ${String(error)}`,
403
- );
404
- }
405
- }
406
-
407
- // Older builds stored providers in `<dataDir>/config.json`. Migrate that
408
- // shape before checking the older Conf-managed default location.
409
- migrateFromDataDirConfig(store, dataDir);
410
-
411
- // Run the legacy migration once on module load. No-op when the new file
412
- // already has data, when the user has set AGENT_INSPECTOR_CONFIG_PATH or
413
- // an explicit data dir, or when the legacy file is empty.
414
- migrateFromLegacyConfLocation(store);
415
- sanitizeProviderBackup();
416
- const providerSecretsReady = migratePlaintextProviderSecrets();
417
-
418
- /**
419
- * Expands the old `{ format, baseUrl }` representation into the matching
420
- * format-specific URL. Once either format-specific field exists, the record is
421
- * already on the current schema and must be preserved exactly: an empty or
422
- * missing counterpart means that API format was intentionally not configured.
423
- */
424
- export function migrateProvider(p: ProviderConfig): ProviderConfig {
425
- if (
426
- p.anthropicBaseUrl !== undefined ||
427
- p.openaiBaseUrl !== undefined ||
428
- p.openaiResponsesBaseUrl !== undefined
429
- ) {
430
- return p;
431
- }
432
- if (p.format === undefined || p.baseUrl === undefined || p.baseUrl === "") {
433
- return p;
434
- }
435
-
436
- return p.format === "openai"
437
- ? { ...p, anthropicBaseUrl: "", openaiBaseUrl: p.baseUrl, openaiResponsesBaseUrl: "" }
438
- : { ...p, anthropicBaseUrl: p.baseUrl, openaiBaseUrl: "", openaiResponsesBaseUrl: "" };
439
- }
440
-
441
- /**
442
- * Migrates existing provider configs to preserve Anthropic and OpenAI routing URLs.
443
- */
444
- function migrateProviders(): void {
445
- const providers = getProviders();
446
- const updated = providers.map(migrateProvider);
447
- const migrated = updated.some((p, i) => JSON.stringify(p) !== JSON.stringify(providers[i]));
448
-
449
- if (migrated) {
450
- void persistProviders(updated);
451
- }
452
- }
453
-
454
- // Run migration on module load
455
- if (providerSecretsReady) migrateProviders();
456
-
457
- /**
458
- * Migrate: merge providers with identical API key and routing URLs.
459
- * into multi-model providers. Existing single-model providers get their `model`
460
- * value promoted to `models: [model]`.
461
- */
462
- function migrateMultiModel(): void {
463
- const providers = getProviders();
464
- if (providers.length === 0) return;
465
- let changed = false;
466
-
467
- // Step 1: Promote single model to models array for any provider missing models
468
- const promoted = providers.map((p) => {
469
- if (p.models !== undefined && p.models.length > 0) return p;
470
- const models = p.model !== undefined && p.model !== "" ? [p.model] : [];
471
- changed = true;
472
- return { ...p, models };
473
- });
474
-
475
- // Step 2: Merge providers with the same API key and routing URLs.
476
- const groups = new Map<string, ProviderConfig[]>();
477
- for (const p of promoted) {
478
- const key = `${p.apiKey}::${p.anthropicBaseUrl ?? ""}::${p.openaiBaseUrl ?? ""}::${p.openaiResponsesBaseUrl ?? ""}`;
479
- const group = groups.get(key);
480
- if (group !== undefined) {
481
- group.push(p);
482
- } else {
483
- groups.set(key, [p]);
484
- }
485
- }
486
-
487
- const merged: ProviderConfig[] = [];
488
- for (const group of groups.values()) {
489
- const first = group[0];
490
- if (group.length === 1 && first !== undefined) {
491
- merged.push(first);
492
- continue;
493
- }
494
- changed = true;
495
- // Merge: keep the earliest provider's metadata, combine all models (deduplicated)
496
- const sorted = group.toSorted(
497
- (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(),
498
- );
499
- const earliest = sorted[0];
500
- if (earliest === undefined) continue;
501
- const allModels = new Set<string>();
502
- for (const p of sorted) {
503
- for (const m of p.models ?? []) {
504
- if (m !== "") allModels.add(m);
505
- }
506
- }
507
- // Take first non-empty source
508
- const mergedSource = sorted.find((p) => p.source !== undefined)?.source;
509
- merged.push({
510
- ...earliest,
511
- models: [...allModels],
512
- source: mergedSource,
513
- updatedAt: new Date().toISOString(),
514
- });
515
- }
516
-
517
- if (changed && merged.length > 0) {
518
- // Safety backup before writing
519
- try {
520
- writeFileSync(`${store.path}.bak`, readFileSync(store.path, "utf-8"), {
521
- mode: PRIVATE_FILE_MODE,
522
- });
523
- sanitizeProviderBackup();
524
- } catch {
525
- // best-effort, ignore
526
- }
527
- void persistProviders(merged);
528
- }
529
- }
530
-
531
- if (providerSecretsReady) migrateMultiModel();
532
-
533
- function migrateBuiltinModelMetadata(): void {
534
- const providers = getProviders();
535
- if (providers.length === 0) return;
536
-
537
- const now = new Date().toISOString();
538
- const updated = providers.map((provider) => withBuiltinProviderModelMetadata(provider, now));
539
- const changed = updated.some(
540
- (provider, index) => JSON.stringify(provider) !== JSON.stringify(providers[index]),
541
- );
542
-
543
- if (changed) {
544
- void persistProviders(updated);
545
- }
546
- }
547
-
548
- if (providerSecretsReady) migrateBuiltinModelMetadata();
549
-
550
- // Override with JSON env var if provided (for testing)
551
- const providersJson = process.env["AGENT_INSPECTOR_PROVIDERS_JSON"];
552
- if (providersJson !== undefined) {
553
- try {
554
- const parsed = ProviderConfigSchema.array().safeParse(JSON.parse(providersJson));
555
- if (parsed.success) {
556
- // Apply migration to JSON providers (e.g., convert baseUrl to openaiBaseUrl/anthropicBaseUrl)
557
- const now = new Date().toISOString();
558
- const migrated = parsed.data
559
- .map(migrateProvider)
560
- .map((provider) => withBuiltinProviderModelMetadata(provider, now));
561
- void persistProviders(migrated);
562
- }
563
- } catch {
564
- // Ignore invalid JSON
565
- }
566
- }
567
-
568
- export function getProviders(): ProviderConfig[] {
569
- const raw = store.get("providers", []);
570
- // Validate the on-disk store entry-by-entry so a single corrupted record
571
- // cannot wipe out the entire provider list. The whole-store safeParse was
572
- // too strict — once any legacy/in-flight provider failed validation the
573
- // whole list was discarded, which broke `updateProvider` and routing.
574
- const valid: ProviderConfig[] = [];
575
- for (const candidate of raw) {
576
- const entry = ProviderConfigSchema.safeParse(candidate);
577
- if (entry.success) {
578
- const resolved = providerWithResolvedSecret(entry.data);
579
- if (resolved !== null) valid.push(resolved);
580
- continue;
581
- }
582
- let candidateName = "<unknown>";
583
- if (typeof candidate === "object" && candidate !== null) {
584
- const nameResult = z.object({ name: z.string() }).safeParse(candidate);
585
- if (nameResult.success) candidateName = nameResult.data.name;
586
- }
587
- logger.warn(
588
- `[providers] Skipping invalid on-disk provider "${candidateName}": ${entry.error.message}`,
589
- );
590
- }
591
- return valid;
592
- }
593
-
594
- export function getProvider(id: string): ProviderConfig | undefined {
595
- const providers = getProviders();
596
- return providers.find((p) => p.id === id);
597
- }
598
-
599
- export function redactProvider(provider: ProviderConfig): RedactedProviderDto {
600
- return toRedactedProviderDto(provider);
601
- }
602
-
603
- export function redactProviders(providers: readonly ProviderConfig[]): RedactedProviderDto[] {
604
- return toRedactedProviderDtos(providers);
605
- }
606
-
607
- /**
608
- * Normalizes an API key by stripping "Bearer " prefix if present.
609
- */
610
- export function normalizeApiKey(apiKey: string): string {
611
- return apiKey.replace(/^Bearer\s+/i, "").trim();
612
- }
613
-
614
- function providerUpdateApiKey(candidate: string | undefined, existingApiKey: string): string {
615
- if (candidate === undefined) return existingApiKey;
616
- const normalized = normalizeApiKey(candidate);
617
- return normalized === REDACTED_PROVIDER_API_KEY ? existingApiKey : normalized;
618
- }
619
-
620
- export function addProvider(
621
- name: string,
622
- apiKey: string,
623
- format?: "anthropic" | "openai",
624
- baseUrl?: string,
625
- models?: string[],
626
- authHeader?: "bearer" | "x-api-key",
627
- apiDocsUrl?: string,
628
- anthropicBaseUrl?: string,
629
- openaiBaseUrl?: string,
630
- source?: "company" | "personal",
631
- modelMetadataUrl?: string,
632
- modelMetadata?: ProviderModelMetadata[],
633
- openaiResponsesBaseUrl?: string,
634
- ): ProviderConfig | null {
635
- const providers = getProviders();
636
- const normalizedKey = normalizeApiKey(apiKey);
637
- const resolvedAnthropicUrl =
638
- anthropicBaseUrl ?? (format === "anthropic" && baseUrl !== undefined ? baseUrl : "");
639
- const resolvedOpenaiUrl =
640
- openaiBaseUrl ?? (format === "openai" && baseUrl !== undefined ? baseUrl : "");
641
- const resolvedOpenaiResponsesUrl = openaiResponsesBaseUrl ?? "";
642
- const now = new Date().toISOString();
643
-
644
- // If a provider with the same API key and routing URLs already exists,
645
- // merge the new models into it instead of creating a duplicate.
646
- const existing = providers.find(
647
- (p) =>
648
- secretValuesEqual(p.apiKey, normalizedKey) &&
649
- (p.anthropicBaseUrl ?? "") === (resolvedAnthropicUrl ?? "") &&
650
- (p.openaiBaseUrl ?? "") === (resolvedOpenaiUrl ?? "") &&
651
- (p.openaiResponsesBaseUrl ?? "") === (resolvedOpenaiResponsesUrl ?? ""),
652
- );
653
- if (existing) {
654
- const existingIndex = providers.findIndex((p) => p.id === existing.id);
655
- const newModels = (models ?? []).filter((m) => m !== "");
656
- existing.modelMetadataUrl = modelMetadataUrl ?? existing.modelMetadataUrl;
657
- existing.modelMetadata = mergeModelMetadata(existing.modelMetadata, modelMetadata);
658
- if (newModels.length > 0) {
659
- const mergedModels = new Set(existing.models ?? []);
660
- for (const m of newModels) mergedModels.add(m);
661
- existing.models = [...mergedModels];
662
- }
663
- existing.updatedAt = now;
664
- const enriched = withBuiltinProviderModelMetadata(existing, now);
665
- if (existingIndex >= 0) {
666
- providers[existingIndex] = enriched;
667
- }
668
- if (!persistProviders(providers)) return null;
669
- return enriched;
670
- }
671
-
672
- const newProvider: ProviderConfig = withBuiltinProviderModelMetadata(
673
- {
674
- id: randomUUID(),
675
- name,
676
- apiKey: normalizedKey,
677
- format:
678
- format ??
679
- (anthropicBaseUrl !== undefined
680
- ? "anthropic"
681
- : openaiBaseUrl !== undefined || openaiResponsesBaseUrl !== undefined
682
- ? "openai"
683
- : undefined),
684
- baseUrl,
685
- models: models ?? [],
686
- authHeader: authHeader ?? "bearer",
687
- apiDocsUrl,
688
- modelMetadataUrl,
689
- modelMetadata,
690
- createdAt: now,
691
- updatedAt: now,
692
- anthropicBaseUrl: resolvedAnthropicUrl,
693
- openaiBaseUrl: resolvedOpenaiUrl,
694
- openaiResponsesBaseUrl: resolvedOpenaiResponsesUrl,
695
- source,
696
- },
697
- now,
698
- );
699
- providers.push(newProvider);
700
- if (!persistProviders(providers)) return null;
701
- return newProvider;
702
- }
703
-
704
- export function updateProvider(
705
- id: string,
706
- updates: Partial<Omit<ProviderConfig, "id" | "createdAt">>,
707
- ): ProviderConfig | null {
708
- const providers = getProviders();
709
- const existing = providers.find((p) => p.id === id);
710
- if (!existing) return null;
711
-
712
- const updatedAt = new Date().toISOString();
713
- const updated: ProviderConfig = withBuiltinProviderModelMetadata(
714
- {
715
- id: existing.id,
716
- name: updates.name ?? existing.name,
717
- apiKey: providerUpdateApiKey(updates.apiKey, existing.apiKey),
718
- models: updates.models !== undefined ? updates.models : existing.models,
719
- format: updates.format ?? existing.format,
720
- baseUrl: updates.baseUrl !== undefined ? updates.baseUrl : existing.baseUrl,
721
- authHeader: updates.authHeader ?? existing.authHeader,
722
- apiDocsUrl: updates.apiDocsUrl ?? existing.apiDocsUrl,
723
- modelMetadataUrl:
724
- updates.modelMetadataUrl !== undefined
725
- ? updates.modelMetadataUrl
726
- : existing.modelMetadataUrl,
727
- modelMetadata:
728
- updates.modelMetadata !== undefined ? updates.modelMetadata : existing.modelMetadata,
729
- createdAt: existing.createdAt,
730
- updatedAt,
731
- // Handle format-specific URLs
732
- anthropicBaseUrl:
733
- updates.anthropicBaseUrl !== undefined
734
- ? updates.anthropicBaseUrl
735
- : existing.anthropicBaseUrl,
736
- openaiBaseUrl:
737
- updates.openaiBaseUrl !== undefined ? updates.openaiBaseUrl : existing.openaiBaseUrl,
738
- openaiResponsesBaseUrl:
739
- updates.openaiResponsesBaseUrl !== undefined
740
- ? updates.openaiResponsesBaseUrl
741
- : existing.openaiResponsesBaseUrl,
742
- source: updates.source !== undefined ? updates.source : existing.source,
743
- },
744
- updatedAt,
745
- );
746
- const index = providers.findIndex((p) => p.id === id);
747
- providers[index] = updated;
748
- if (!persistProviders(providers)) return null;
749
- return updated;
750
- }
751
-
752
- export function deleteProvider(id: string): boolean {
753
- const providers = getProviders();
754
- const filtered = providers.filter((p) => p.id !== id);
755
- if (filtered.length === providers.length) return false;
756
-
757
- if (!persistProviders(filtered, new Set([id]))) return false;
758
- providerSecretStore.delete(id);
759
- return true;
760
- }
761
-
762
- /**
763
- * Export all providers as a JSON string (without API keys for security).
764
- */
765
- export function exportProviders(): string {
766
- const providers = getProviders();
767
- // Mask API keys for security
768
- const safeProviders = providers.map(({ apiKey, ...rest }) => ({
769
- ...rest,
770
- apiKey: maskApiKey(apiKey),
771
- }));
772
- return JSON.stringify(safeProviders, null, 2);
773
- }
774
-
775
- /**
776
- * Export all providers as a JSON string including API keys (user must opt-in).
777
- */
778
- export function exportProvidersWithKeys(): string {
779
- return JSON.stringify(getProviders(), null, 2);
780
- }
781
-
782
- /**
783
- * Import providers from a JSON string.
784
- * Returns { imported: number, errors: string[] }
785
- */
786
- export function importProviders(json: string): { imported: number; errors: string[] } {
787
- const errors: string[] = [];
788
- let imported = 0;
789
-
790
- try {
791
- const parsed: unknown = JSON.parse(json);
792
-
793
- // Use Zod to validate the structure
794
- const ImportSchema = z.union([
795
- z.array(ProviderConfigSchema),
796
- z.object({
797
- providers: z.array(ProviderConfigSchema),
798
- }),
799
- ]);
800
-
801
- const parseResult = ImportSchema.safeParse(parsed);
802
- if (!parseResult.success) {
803
- return {
804
- imported: 0,
805
- errors: [`Invalid format: ${parseResult.error.message}`],
806
- };
807
- }
808
-
809
- const providerArray = Array.isArray(parseResult.data)
810
- ? parseResult.data
811
- : parseResult.data.providers;
812
-
813
- // Snapshot existing providers once
814
- const existingProviders = getProviders();
815
-
816
- // Build a Set of existing (name, anthropicBaseUrl) keys for O(1) duplicate lookup
817
- const existingKeys = new Set<string>();
818
- for (const p of existingProviders) {
819
- existingKeys.add(`${p.name}::${p.anthropicBaseUrl ?? ""}`);
820
- }
821
-
822
- const newProviders: ProviderConfig[] = [];
823
-
824
- for (let i = 0; i < providerArray.length; i++) {
825
- const item = providerArray[i];
826
- const result = ProviderConfigSchema.safeParse(item);
827
-
828
- if (!result.success) {
829
- errors.push(`Provider ${i + 1}: ${result.error.message}`);
830
- continue;
831
- }
832
-
833
- const provider = result.data;
834
- const key = `${provider.name}::${provider.anthropicBaseUrl ?? ""}`;
835
-
836
- if (existingKeys.has(key)) {
837
- errors.push(`Provider "${provider.name}" already exists, skipping`);
838
- continue;
839
- }
840
-
841
- // Generate new ID and add
842
- const now = new Date().toISOString();
843
- const newProvider: ProviderConfig = withBuiltinProviderModelMetadata(
844
- {
845
- ...provider,
846
- id: randomUUID(),
847
- createdAt: now,
848
- updatedAt: now,
849
- },
850
- now,
851
- );
852
-
853
- newProviders.push(newProvider);
854
- existingKeys.add(key);
855
- }
856
-
857
- // Single write: append all new providers at once
858
- if (newProviders.length > 0) {
859
- if (!persistProviders([...existingProviders, ...newProviders])) {
860
- return {
861
- imported: 0,
862
- errors: ["Secure provider storage is unavailable; no providers were imported"],
863
- };
864
- }
865
- imported = newProviders.length;
866
- }
867
- } catch (err) {
868
- return {
869
- imported: 0,
870
- errors: [`Failed to parse JSON: ${err instanceof Error ? err.message : String(err)}`],
871
- };
872
- }
873
-
874
- return { imported, errors };
875
- }
876
-
877
- /**
878
- * Converts display model name to API usage name based on provider-specific rules.
879
- * Default behavior: returns model as-is.
880
- * MiniMax: replaces spaces with hyphens.
881
- */
882
- export function getModelUsageName(model: string, providerName?: string): string {
883
- if (
884
- providerName !== undefined &&
885
- providerName !== "" &&
886
- providerName.toLowerCase().includes("minimax")
887
- ) {
888
- return model.replace(/ /g, "-");
889
- }
890
- return model;
891
- }
892
-
893
- /**
894
- * Normalize a model name for comparison: lowercase, replace whitespace with hyphens.
895
- */
896
- function normalizeModelName(name: string): string {
897
- return name.toLowerCase().replace(/\s+/g, "-");
898
- }
899
-
900
- const ZHIPU_MODEL_PREFIXES = ["glm-", "charglm-", "cogview-", "embedding-", "rerank-"];
901
- const ZHIPU_PROVIDER_ALIASES = ["zhipu", "zhipuai", "bigmodel", "zai", "glm"];
902
- const ZHIPU_BASE_URL_MARKERS = [
903
- "open.bigmodel.cn/api/paas/v4",
904
- "open.bigmodel.cn/api/coding/paas/v4",
905
- "open.bigmodel.cn/api/anthropic",
906
- ];
907
-
908
- function normalizeProviderAlias(name: string): string {
909
- return name.toLowerCase().replace(/[^a-z0-9]/g, "");
910
- }
911
-
912
- function providerRoutingUrls(provider: ProviderConfig): string[] {
913
- const urls: string[] = [];
914
- if (provider.anthropicBaseUrl !== undefined && provider.anthropicBaseUrl.trim() !== "") {
915
- urls.push(provider.anthropicBaseUrl);
916
- }
917
- if (provider.openaiBaseUrl !== undefined && provider.openaiBaseUrl.trim() !== "") {
918
- urls.push(provider.openaiBaseUrl);
919
- }
920
- if (
921
- provider.openaiResponsesBaseUrl !== undefined &&
922
- provider.openaiResponsesBaseUrl.trim() !== ""
923
- ) {
924
- urls.push(provider.openaiResponsesBaseUrl);
925
- }
926
- if (provider.baseUrl !== undefined && provider.baseUrl.trim() !== "") {
927
- urls.push(provider.baseUrl);
928
- }
929
- return urls;
930
- }
931
-
932
- function isZhipuProvider(provider: ProviderConfig): boolean {
933
- const normalizedName = normalizeProviderAlias(provider.name);
934
- const nameMatches = ZHIPU_PROVIDER_ALIASES.some((alias) => {
935
- const normalizedAlias = normalizeProviderAlias(alias);
936
- return normalizedName.includes(normalizedAlias) || normalizedAlias.includes(normalizedName);
937
- });
938
- if (nameMatches) return true;
939
-
940
- return providerRoutingUrls(provider).some((url) => {
941
- const normalizedUrl = url.toLowerCase().replace(/\/+$/, "");
942
- return ZHIPU_BASE_URL_MARKERS.some((marker) => normalizedUrl.includes(marker));
943
- });
944
- }
945
-
946
- function modelMatchesKnownProviderPrefix(
947
- modelNormalized: string,
948
- provider: ProviderConfig,
949
- ): boolean {
950
- if (isZhipuProvider(provider)) {
951
- return ZHIPU_MODEL_PREFIXES.some((prefix) => modelNormalized.startsWith(prefix));
952
- }
953
- return false;
954
- }
955
-
956
- /**
957
- * Finds a provider by model name using three strategies:
958
- * 1. Case-insensitive prefix match against "{provider.name}-" (e.g., "deepseek-" matches "deepseek-*")
959
- * 2. Case-insensitive match against provider.model field (with whitespace/hyphen normalization)
960
- * 3. Fallback: concatenate provider name with provider's model and check if the request model matches
961
- * this concatenation (handles "MiniMax" + "M3" → "MiniMax-M3" pattern when case differs)
962
- */
963
- export function findProviderByModel(model: string): ProviderConfig | null {
964
- const providers = getProviders();
965
- const modelLower = model.toLowerCase();
966
- const modelNormalized = normalizeModelName(model);
967
-
968
- for (const provider of providers) {
969
- // Strategy 1: provider name prefix match
970
- const providerPrefix = (provider.name + "-").toLowerCase();
971
- if (modelLower.startsWith(providerPrefix)) {
972
- return provider;
973
- }
974
- // Strategy 1b: known model-family prefixes for providers whose public
975
- // model names do not start with the provider brand, e.g. ZhipuAI -> glm-*.
976
- if (modelMatchesKnownProviderPrefix(modelNormalized, provider)) {
977
- return provider;
978
- }
979
- // Strategy 2: match against provider.models array with normalization
980
- const modelList = provider.models ?? (provider.model !== undefined ? [provider.model] : []);
981
- for (const m of modelList) {
982
- if (m !== "" && modelNormalized === normalizeModelName(m)) {
983
- return provider;
984
- }
985
- }
986
- // Strategy 3: fallback - concatenate provider name with model part and compare
987
- for (const m of modelList) {
988
- if (m !== "") {
989
- const modelPart = modelNormalized.replace(normalizeModelName(provider.name), "");
990
- const concatenated = normalizeModelName(provider.name) + modelPart;
991
- if (modelNormalized === concatenated) {
992
- return provider;
993
- }
994
- }
995
- }
996
- }
997
- return null;
998
- }