@tonyclaw/agent-inspector 3.0.45 → 3.0.47

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 (343) hide show
  1. package/.output/{nitro.json → backend/nitro.json} +3 -3
  2. package/.output/backend-public/assets/index-D4LX-sw-.js +11 -0
  3. package/.output/cli.js +8018 -5451
  4. package/.output/server/_libs/extend.mjs +4 -8
  5. package/.output/server/_libs/jszip.mjs +9 -35
  6. package/.output/server/_libs/react-dom.mjs +2 -3
  7. package/.output/server/_libs/react.mjs +1 -27
  8. package/.output/server/_libs/tanstack__react-router.mjs +1 -329
  9. package/.output/server/_libs/tanstack__router-core.mjs +42 -100
  10. package/.output/server/_libs/use-sync-external-store.mjs +1 -64
  11. package/.output/server/_libs/ws.mjs +1 -1
  12. package/.output/server/_ssr/index.mjs +4 -4
  13. package/.output/server/_ssr/{publicBasePathContext-5264L3rX.mjs → publicBasePathContext-IH2ilOfu.mjs} +52 -24
  14. package/.output/server/_ssr/{router-BJ_ltLim.mjs → router-BwZEsYfS.mjs} +19794 -19444
  15. package/.output/server/_ssr/{start-C6MjPjxp.mjs → start-COQu_AgD.mjs} +1 -1
  16. package/.output/server/_tanstack-start-manifest_v-DnbdNeun.mjs +4 -0
  17. package/.output/server/index.mjs +5 -145
  18. package/.output/{public/assets/CompareDrawer-B3_TLTJh.js → ui/assets/CompareDrawer-V-8lPASC.js} +1 -1
  19. package/.output/{public/assets/InspectorPet-Cpn6hv4P.js → ui/assets/InspectorPet-D4CVnfdf.js} +1 -1
  20. package/.output/ui/assets/ProxyViewerContainer-CO3ZVHLX.js +59 -0
  21. package/.output/{public/assets/ReplayDialog-B3EKVdJk.js → ui/assets/ReplayDialog-CpGyTLtt.js} +1 -1
  22. package/.output/{public/assets/RequestAnatomy-COOQjyUC.js → ui/assets/RequestAnatomy-BLuJL9dZ.js} +1 -1
  23. package/.output/ui/assets/ResponseView-BuSC-CH7.js +2 -0
  24. package/.output/ui/assets/StreamingChunkSequence-DeA-JJKa.js +1 -0
  25. package/.output/{public/assets/_sessionId-zbgYl7ZJ.js → ui/assets/_sessionId-8HuHFhjo.js} +1 -1
  26. package/.output/{public/assets/_sessionId-BJlM3nmt.js → ui/assets/_sessionId-Chp67th6.js} +1 -1
  27. package/.output/ui/assets/favicon-DYEkHSNl.svg +21 -0
  28. package/.output/ui/assets/index-BvXp42al.css +1 -0
  29. package/.output/{public/assets/index-xF_NY8Yb.js → ui/assets/index-DPmKK_5d.js} +5 -5
  30. package/.output/{public/assets/index-BIK4bPTv.js → ui/assets/index-atoQrlb8.js} +1 -1
  31. package/.output/ui/assets/index-nY3E1vxV.js +73 -0
  32. package/.output/{public/assets/index-D2Qw6Ri2.js → ui/assets/index-nl9-dpZn.js} +1 -1
  33. package/.output/ui/assets/json-viewer-B9kIzqeX.js +1 -0
  34. package/.output/{public/assets/jszip.min-B_rfAUCe.js → ui/assets/jszip.min-B7tfyE-I.js} +1 -1
  35. package/.output/ui/index.html +229 -0
  36. package/.output/ui/runtime-config.js +2 -0
  37. package/.output/workers/logFinalizer.worker.js +979 -414
  38. package/.output/workers/sessionWorkerEntry.js +979 -414
  39. package/README.md +182 -43
  40. package/docs/instance-control.openapi.yaml +59 -4
  41. package/package.json +40 -6
  42. package/scripts/generate-theme-scales.mjs +3 -3
  43. package/src/{router.tsx → backend/router.tsx} +3 -15
  44. package/src/backend/routes/$.ts +26 -0
  45. package/src/backend/routes/__root.ts +4 -0
  46. package/src/backend/routes/api/-instances.ts +7 -0
  47. package/src/{routes → backend/routes}/api/alerts.summary.ts +3 -3
  48. package/src/{routes → backend/routes}/api/alerts.ts +4 -4
  49. package/src/{routes → backend/routes}/api/config.paths.ts +1 -1
  50. package/src/{routes → backend/routes}/api/config.ts +11 -2
  51. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.help.ts +1 -1
  52. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.install.ts +1 -1
  53. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.runner-presets.ts +1 -1
  54. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.upgrade.ts +1 -1
  55. package/src/{routes → backend/routes}/api/ecosystem.packages.ts +5 -1
  56. package/src/{routes → backend/routes}/api/ecosystem.recipes.$recipeId.run.ts +1 -1
  57. package/src/{routes → backend/routes}/api/ecosystem.tasks.$taskId.ts +1 -1
  58. package/src/{routes → backend/routes}/api/ecosystem.tasks.ts +1 -1
  59. package/src/{routes → backend/routes}/api/groups.$groupId.evidence.ts +2 -2
  60. package/src/{routes → backend/routes}/api/groups.$groupId.sessions.ts +2 -2
  61. package/src/{routes → backend/routes}/api/groups.$groupId.ts +2 -2
  62. package/src/{routes → backend/routes}/api/groups.ts +2 -2
  63. package/src/{routes → backend/routes}/api/health.ts +3 -3
  64. package/src/{routes → backend/routes}/api/instances.$name.connection.ts +1 -1
  65. package/src/{routes → backend/routes}/api/instances.$name.restart.ts +2 -2
  66. package/src/{routes → backend/routes}/api/instances.$name.start.ts +2 -2
  67. package/src/{routes → backend/routes}/api/instances.$name.stop.ts +2 -2
  68. package/src/{routes → backend/routes}/api/instances.$name.ts +1 -1
  69. package/src/{routes → backend/routes}/api/instances.ts +1 -1
  70. package/src/{routes → backend/routes}/api/knowledge.candidates.$candidateId.promote.ts +2 -2
  71. package/src/{routes → backend/routes}/api/knowledge.candidates.$candidateId.ts +2 -2
  72. package/src/{routes → backend/routes}/api/knowledge.candidates.ts +1 -1
  73. package/src/{routes → backend/routes}/api/knowledge.project-context.ts +2 -2
  74. package/src/{routes → backend/routes}/api/knowledge.search.ts +2 -2
  75. package/src/{routes → backend/routes}/api/knowledge.sessions.$sessionId.candidates.ts +3 -3
  76. package/src/{routes → backend/routes}/api/logs.$id.body.ts +6 -6
  77. package/src/{routes → backend/routes}/api/logs.$id.chunks.ts +5 -4
  78. package/src/{routes → backend/routes}/api/logs.$id.replay.ts +12 -11
  79. package/src/{routes → backend/routes}/api/logs.$id.ts +4 -3
  80. package/src/{routes → backend/routes}/api/logs.import.ts +3 -3
  81. package/src/{routes → backend/routes}/api/logs.stream.ts +11 -7
  82. package/src/{routes → backend/routes}/api/logs.ts +17 -9
  83. package/src/{routes → backend/routes}/api/mcp.ts +1 -1
  84. package/src/{routes → backend/routes}/api/models.ts +1 -1
  85. package/src/{routes → backend/routes}/api/pi-agent.ts +2 -2
  86. package/src/{routes → backend/routes}/api/providers.$providerId.model-metadata.ts +5 -5
  87. package/src/{routes → backend/routes}/api/providers.$providerId.test.log.ts +7 -7
  88. package/src/{routes → backend/routes}/api/providers.$providerId.ts +4 -4
  89. package/src/{routes → backend/routes}/api/providers.export.ts +1 -1
  90. package/src/{routes → backend/routes}/api/providers.import.ts +3 -3
  91. package/src/{routes → backend/routes}/api/providers.scan.ts +2 -2
  92. package/src/{routes → backend/routes}/api/providers.ts +9 -4
  93. package/src/{routes → backend/routes}/api/runs.$runId.evidence.ts +2 -2
  94. package/src/{routes → backend/routes}/api/runs.$runId.ts +2 -2
  95. package/src/{routes → backend/routes}/api/runs.ts +4 -4
  96. package/src/backend/routes/api/runtime.ts +46 -0
  97. package/src/{routes → backend/routes}/api/sessions.ts +3 -3
  98. package/src/{routes → backend/routes}/proxy/$.ts +1 -1
  99. package/src/{routes → backend/routes}/readyz.ts +1 -1
  100. package/src/{start.ts → backend/start.ts} +1 -1
  101. package/src/cli/alias.ts +2 -11
  102. package/src/cli/doctor.ts +32 -9
  103. package/src/cli/instance.ts +36 -13
  104. package/src/cli/instanceApi.ts +213 -0
  105. package/src/cli/instanceArgs.ts +46 -11
  106. package/src/cli/instanceControl.ts +10 -2
  107. package/src/cli/instanceModel.ts +18 -17
  108. package/src/cli/instanceRegistry.ts +0 -9
  109. package/src/cli/networkHints.ts +27 -14
  110. package/src/cli/onboard.ts +69 -2
  111. package/src/cli/onboardTarget.ts +89 -40
  112. package/src/cli/rustBackendPackage.ts +450 -0
  113. package/src/cli/templates/codex-skill-onboard.ts +44 -14
  114. package/src/cli/templates/skill-onboard.ts +64 -28
  115. package/src/cli.ts +341 -75
  116. package/src/components/ProxyViewer.tsx +11 -9
  117. package/src/components/ProxyViewerContainer.tsx +12 -11
  118. package/src/components/pi-agent/PiAgentPanel.tsx +2 -2
  119. package/src/components/providers/ProviderCard.tsx +1 -1
  120. package/src/components/providers/ProviderTestResultsView.tsx +1 -1
  121. package/src/components/providers/SettingsDialog.tsx +159 -144
  122. package/src/components/proxy-viewer/ApplicationBar.tsx +2 -1
  123. package/src/components/proxy-viewer/LogEntry.tsx +1 -1
  124. package/src/components/proxy-viewer/log-formats/index.ts +1 -1
  125. package/src/contracts/index.ts +5 -0
  126. package/src/contracts/log.ts +33 -3
  127. package/src/knowledge/types.ts +3 -2
  128. package/src/lib/alertContract.ts +2 -2
  129. package/src/lib/apiClient.ts +30 -7
  130. package/src/lib/apiFormat.ts +29 -0
  131. package/src/lib/backendImplementation.ts +16 -0
  132. package/src/lib/basePath.ts +29 -11
  133. package/src/lib/browserRuntimeContract.ts +124 -0
  134. package/src/lib/browserRuntimeResolver.ts +244 -0
  135. package/src/{proxy → lib}/claudeCodeStrip.ts +6 -7
  136. package/src/lib/codingAgentEndpoints.ts +38 -0
  137. package/src/lib/instanceContract.ts +30 -11
  138. package/src/lib/managedInstance.ts +9 -1
  139. package/src/lib/piAgentContract.ts +3 -2
  140. package/src/lib/publicBasePathContext.ts +40 -10
  141. package/src/lib/resourceLimits.ts +2 -0
  142. package/src/lib/runContract.ts +3 -3
  143. package/src/lib/sessionInfoContract.ts +3 -2
  144. package/src/mcp/instanceHandlers.ts +2 -0
  145. package/src/mcp/server.ts +19 -24
  146. package/src/proxy/config.ts +25 -30
  147. package/src/proxy/dataDir.ts +43 -7
  148. package/src/proxy/ecosystemTasks.ts +161 -70
  149. package/src/proxy/formats/registry.ts +6 -21
  150. package/src/proxy/handler.ts +129 -29
  151. package/src/proxy/identityProxy.ts +567 -39
  152. package/src/proxy/jsonlRecovery.ts +554 -0
  153. package/src/proxy/logFinalizer.ts +25 -10
  154. package/src/proxy/logImporter.ts +2 -1
  155. package/src/proxy/logIndex.ts +191 -95
  156. package/src/proxy/logger.ts +77 -14
  157. package/src/proxy/platformCommands.ts +41 -0
  158. package/src/proxy/privateDataPath.ts +310 -88
  159. package/src/proxy/rawStreamCapture.ts +194 -13
  160. package/src/proxy/runtimeAdmission.ts +5 -2
  161. package/src/proxy/runtimeHealth.ts +14 -1
  162. package/src/proxy/runtimeShutdown.ts +11 -1
  163. package/src/proxy/schemas.ts +7 -1
  164. package/src/proxy/sessionArchive.ts +70 -14
  165. package/src/proxy/sqliteLogIndex.ts +65 -42
  166. package/src/proxy/store.ts +141 -34
  167. package/src/proxy/uiAssetServer.ts +106 -0
  168. package/src/proxy/upstream.ts +29 -2
  169. package/src/ui/bootstrap.ts +167 -0
  170. package/src/ui/index.html +38 -0
  171. package/src/ui/logsStreamClient.ts +20 -0
  172. package/src/ui/main.tsx +13 -0
  173. package/src/ui/public/runtime-config.js +2 -0
  174. package/src/ui/router.tsx +13 -0
  175. package/src/ui/routes/__root.tsx +69 -0
  176. package/src/{routes → ui/routes}/index.tsx +2 -3
  177. package/src/{routes → ui/routes}/session/$sessionId.tsx +5 -11
  178. package/src/ui/runtimeDiscovery.ts +50 -0
  179. package/styles/globals.css +1 -0
  180. package/.output/public/assets/ProxyViewerContainer-BEAhuGOJ.js +0 -126
  181. package/.output/public/assets/ResponseView-DBCjkwye.js +0 -2
  182. package/.output/public/assets/StreamingChunkSequence-CuXhDgMo.js +0 -1
  183. package/.output/public/assets/index-BZo_8koU.js +0 -70
  184. package/.output/public/assets/index-ZWschkCh.css +0 -1
  185. package/.output/public/assets/json-viewer-B-Ao6pwW.js +0 -1
  186. package/.output/server/_libs/@radix-ui/react-accessible-icon+[...].mjs +0 -1
  187. package/.output/server/_libs/@radix-ui/react-dismissable-layer+[...].mjs +0 -344
  188. package/.output/server/_libs/@radix-ui/react-navigation-menu+[...].mjs +0 -2
  189. package/.output/server/_libs/@radix-ui/react-one-time-password-field+[...].mjs +0 -2
  190. package/.output/server/_libs/@radix-ui/react-password-toggle-field+[...].mjs +0 -2
  191. package/.output/server/_libs/@radix-ui/react-use-callback-ref+[...].mjs +0 -11
  192. package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +0 -69
  193. package/.output/server/_libs/@radix-ui/react-use-effect-event+[...].mjs +0 -1
  194. package/.output/server/_libs/@radix-ui/react-use-is-hydrated+[...].mjs +0 -28
  195. package/.output/server/_libs/@radix-ui/react-use-layout-effect+[...].mjs +0 -6
  196. package/.output/server/_libs/@radix-ui/react-visually-hidden+[...].mjs +0 -34
  197. package/.output/server/_libs/aria-hidden.mjs +0 -122
  198. package/.output/server/_libs/bail.mjs +0 -8
  199. package/.output/server/_libs/character-entities.mjs +0 -2130
  200. package/.output/server/_libs/class-variance-authority.mjs +0 -44
  201. package/.output/server/_libs/clsx.mjs +0 -16
  202. package/.output/server/_libs/comma-separated-tokens.mjs +0 -10
  203. package/.output/server/_libs/decode-named-character-reference+[...].mjs +0 -8
  204. package/.output/server/_libs/dequal.mjs +0 -27
  205. package/.output/server/_libs/detect-node-es.mjs +0 -1
  206. package/.output/server/_libs/devlop.mjs +0 -8
  207. package/.output/server/_libs/diff.mjs +0 -320
  208. package/.output/server/_libs/estree-util-is-identifier-name.mjs +0 -11
  209. package/.output/server/_libs/floating-ui__core.mjs +0 -725
  210. package/.output/server/_libs/floating-ui__dom.mjs +0 -622
  211. package/.output/server/_libs/floating-ui__react-dom.mjs +0 -292
  212. package/.output/server/_libs/floating-ui__utils.mjs +0 -320
  213. package/.output/server/_libs/get-nonce.mjs +0 -9
  214. package/.output/server/_libs/hast-util-to-jsx-runtime.mjs +0 -388
  215. package/.output/server/_libs/hast-util-whitespace.mjs +0 -10
  216. package/.output/server/_libs/html-url-attributes.mjs +0 -26
  217. package/.output/server/_libs/inline-style-parser.mjs +0 -142
  218. package/.output/server/_libs/is-plain-obj.mjs +0 -10
  219. package/.output/server/_libs/lucide-react.mjs +0 -1027
  220. package/.output/server/_libs/mdast-util-from-markdown.mjs +0 -717
  221. package/.output/server/_libs/mdast-util-to-hast.mjs +0 -710
  222. package/.output/server/_libs/mdast-util-to-string.mjs +0 -38
  223. package/.output/server/_libs/micromark-core-commonmark.mjs +0 -2259
  224. package/.output/server/_libs/micromark-factory-destination.mjs +0 -94
  225. package/.output/server/_libs/micromark-factory-label.mjs +0 -63
  226. package/.output/server/_libs/micromark-factory-space.mjs +0 -24
  227. package/.output/server/_libs/micromark-factory-title.mjs +0 -65
  228. package/.output/server/_libs/micromark-factory-whitespace.mjs +0 -22
  229. package/.output/server/_libs/micromark-util-character.mjs +0 -44
  230. package/.output/server/_libs/micromark-util-chunked.mjs +0 -36
  231. package/.output/server/_libs/micromark-util-classify-character+[...].mjs +0 -12
  232. package/.output/server/_libs/micromark-util-combine-extensions+[...].mjs +0 -41
  233. package/.output/server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +0 -19
  234. package/.output/server/_libs/micromark-util-decode-string.mjs +0 -21
  235. package/.output/server/_libs/micromark-util-encode.mjs +0 -1
  236. package/.output/server/_libs/micromark-util-html-tag-name.mjs +0 -69
  237. package/.output/server/_libs/micromark-util-normalize-identifier+[...].mjs +0 -6
  238. package/.output/server/_libs/micromark-util-resolve-all.mjs +0 -15
  239. package/.output/server/_libs/micromark-util-sanitize-uri.mjs +0 -41
  240. package/.output/server/_libs/micromark-util-subtokenize.mjs +0 -346
  241. package/.output/server/_libs/micromark.mjs +0 -906
  242. package/.output/server/_libs/property-information.mjs +0 -1209
  243. package/.output/server/_libs/radix-ui.mjs +0 -1
  244. package/.output/server/_libs/radix-ui__number.mjs +0 -6
  245. package/.output/server/_libs/radix-ui__primitive.mjs +0 -11
  246. package/.output/server/_libs/radix-ui__react-accordion.mjs +0 -1
  247. package/.output/server/_libs/radix-ui__react-alert-dialog.mjs +0 -1
  248. package/.output/server/_libs/radix-ui__react-arrow.mjs +0 -23
  249. package/.output/server/_libs/radix-ui__react-aspect-ratio.mjs +0 -1
  250. package/.output/server/_libs/radix-ui__react-avatar.mjs +0 -1
  251. package/.output/server/_libs/radix-ui__react-checkbox.mjs +0 -1
  252. package/.output/server/_libs/radix-ui__react-collapsible.mjs +0 -144
  253. package/.output/server/_libs/radix-ui__react-collection.mjs +0 -69
  254. package/.output/server/_libs/radix-ui__react-compose-refs.mjs +0 -38
  255. package/.output/server/_libs/radix-ui__react-context-menu.mjs +0 -1
  256. package/.output/server/_libs/radix-ui__react-context.mjs +0 -64
  257. package/.output/server/_libs/radix-ui__react-dialog.mjs +0 -280
  258. package/.output/server/_libs/radix-ui__react-direction.mjs +0 -9
  259. package/.output/server/_libs/radix-ui__react-dropdown-menu.mjs +0 -1
  260. package/.output/server/_libs/radix-ui__react-focus-guards.mjs +0 -39
  261. package/.output/server/_libs/radix-ui__react-focus-scope.mjs +0 -210
  262. package/.output/server/_libs/radix-ui__react-form.mjs +0 -1
  263. package/.output/server/_libs/radix-ui__react-hover-card.mjs +0 -1
  264. package/.output/server/_libs/radix-ui__react-id.mjs +0 -14
  265. package/.output/server/_libs/radix-ui__react-label.mjs +0 -1
  266. package/.output/server/_libs/radix-ui__react-menu.mjs +0 -1
  267. package/.output/server/_libs/radix-ui__react-menubar.mjs +0 -1
  268. package/.output/server/_libs/radix-ui__react-popover.mjs +0 -1
  269. package/.output/server/_libs/radix-ui__react-popper.mjs +0 -340
  270. package/.output/server/_libs/radix-ui__react-portal.mjs +0 -16
  271. package/.output/server/_libs/radix-ui__react-presence.mjs +0 -173
  272. package/.output/server/_libs/radix-ui__react-primitive.mjs +0 -42
  273. package/.output/server/_libs/radix-ui__react-progress.mjs +0 -1
  274. package/.output/server/_libs/radix-ui__react-radio-group.mjs +0 -1
  275. package/.output/server/_libs/radix-ui__react-roving-focus.mjs +0 -235
  276. package/.output/server/_libs/radix-ui__react-scroll-area.mjs +0 -731
  277. package/.output/server/_libs/radix-ui__react-select.mjs +0 -1207
  278. package/.output/server/_libs/radix-ui__react-separator.mjs +0 -28
  279. package/.output/server/_libs/radix-ui__react-slider.mjs +0 -1
  280. package/.output/server/_libs/radix-ui__react-slot.mjs +0 -132
  281. package/.output/server/_libs/radix-ui__react-switch.mjs +0 -1
  282. package/.output/server/_libs/radix-ui__react-tabs.mjs +0 -194
  283. package/.output/server/_libs/radix-ui__react-toast.mjs +0 -2
  284. package/.output/server/_libs/radix-ui__react-toggle-group.mjs +0 -1
  285. package/.output/server/_libs/radix-ui__react-toggle.mjs +0 -1
  286. package/.output/server/_libs/radix-ui__react-toolbar.mjs +0 -1
  287. package/.output/server/_libs/radix-ui__react-tooltip.mjs +0 -498
  288. package/.output/server/_libs/radix-ui__react-use-previous.mjs +0 -14
  289. package/.output/server/_libs/radix-ui__react-use-size.mjs +0 -39
  290. package/.output/server/_libs/react-markdown.mjs +0 -235
  291. package/.output/server/_libs/react-remove-scroll-bar.mjs +0 -82
  292. package/.output/server/_libs/react-remove-scroll.mjs +0 -328
  293. package/.output/server/_libs/react-style-singleton.mjs +0 -69
  294. package/.output/server/_libs/remark-parse.mjs +0 -19
  295. package/.output/server/_libs/remark-rehype.mjs +0 -21
  296. package/.output/server/_libs/space-separated-tokens.mjs +0 -6
  297. package/.output/server/_libs/style-to-js.mjs +0 -72
  298. package/.output/server/_libs/style-to-object.mjs +0 -38
  299. package/.output/server/_libs/swr.mjs +0 -947
  300. package/.output/server/_libs/tailwind-merge.mjs +0 -3255
  301. package/.output/server/_libs/tanstack__react-virtual.mjs +0 -130
  302. package/.output/server/_libs/tanstack__virtual-core.mjs +0 -1319
  303. package/.output/server/_libs/three.mjs +0 -26016
  304. package/.output/server/_libs/trim-lines.mjs +0 -41
  305. package/.output/server/_libs/trough.mjs +0 -85
  306. package/.output/server/_libs/tslib.mjs +0 -1
  307. package/.output/server/_libs/ungap__structured-clone.mjs +0 -212
  308. package/.output/server/_libs/unified.mjs +0 -661
  309. package/.output/server/_libs/unist-util-is.mjs +0 -100
  310. package/.output/server/_libs/unist-util-position.mjs +0 -27
  311. package/.output/server/_libs/unist-util-stringify-position.mjs +0 -27
  312. package/.output/server/_libs/unist-util-visit-parents.mjs +0 -82
  313. package/.output/server/_libs/unist-util-visit.mjs +0 -24
  314. package/.output/server/_libs/use-callback-ref.mjs +0 -66
  315. package/.output/server/_libs/use-sidecar.mjs +0 -106
  316. package/.output/server/_libs/vfile-message.mjs +0 -138
  317. package/.output/server/_libs/vfile.mjs +0 -467
  318. package/.output/server/_sessionId-CJ99gZFV.mjs +0 -84
  319. package/.output/server/_sessionId-DEjScBpK.mjs +0 -146
  320. package/.output/server/_ssr/CompareDrawer-DtOIMeQB.mjs +0 -1060
  321. package/.output/server/_ssr/InspectorPet-CXc4oCBQ.mjs +0 -938
  322. package/.output/server/_ssr/ProxyViewerContainer-DVPlwfkt.mjs +0 -17632
  323. package/.output/server/_ssr/ReplayDialog-BQEA95qC.mjs +0 -725
  324. package/.output/server/_ssr/RequestAnatomy-BMyd8B7Q.mjs +0 -837
  325. package/.output/server/_ssr/ResponseView-qrTaEIM8.mjs +0 -757
  326. package/.output/server/_ssr/StreamingChunkSequence-BFTL8mIy.mjs +0 -274
  327. package/.output/server/_ssr/index-Bcj1Hcy4.mjs +0 -135
  328. package/.output/server/_ssr/index-DW-M7XUG.mjs +0 -84
  329. package/.output/server/_ssr/json-viewer-DiI0Yjsn.mjs +0 -483
  330. package/.output/server/_tanstack-start-manifest_v-C6pXXX9R.mjs +0 -4
  331. package/.output/server/node_modules/tslib/modules/index.js +0 -70
  332. package/.output/server/node_modules/tslib/modules/package.json +0 -3
  333. package/.output/server/node_modules/tslib/package.json +0 -47
  334. package/.output/server/node_modules/tslib/tslib.js +0 -484
  335. package/.output/server/package.json +0 -9
  336. package/src/routes/__root.tsx +0 -170
  337. package/src/routes/api/-instances.ts +0 -109
  338. /package/.output/{public → ui}/assets/agent-inspector-58K1_MsC.ico +0 -0
  339. /package/.output/{public → ui}/assets/alibaba-TTwafVwX.svg +0 -0
  340. /package/.output/{public → ui}/assets/minimax-BPMzvuL-.jpeg +0 -0
  341. /package/.output/{public → ui}/assets/qwen-mMn3f5ul.webp +0 -0
  342. /package/.output/{public → ui}/assets/zhipuai-BPNAnxo-.svg +0 -0
  343. /package/src/{routes → backend/routes}/livez.ts +0 -0
@@ -1,7 +1,14 @@
1
1
  import { z } from "zod";
2
+ import { BackendImplementationSchema } from "./backendImplementation";
3
+ import { normalizePublicBasePath } from "./basePath";
2
4
 
3
5
  export const INSTANCE_API_SCHEMA_VERSION = 1;
4
6
 
7
+ function isCanonicalPublicBasePath(value: string): boolean {
8
+ const normalized = normalizePublicBasePath(value);
9
+ return normalized !== null && (normalized.length === 0 ? "/" : normalized) === value;
10
+ }
11
+
5
12
  export const InstanceStateSchema = z.enum(["running", "stopped", "conflict"]);
6
13
  export const InstanceStateReasonSchema = z.enum(["identity_mismatch", "port_conflict"]);
7
14
 
@@ -35,18 +42,36 @@ export const InstanceStatusOutputSchema = z
35
42
  host: z.string().min(1),
36
43
  publicPort: z.number().int().min(1).max(65_535),
37
44
  upstreamPort: z.number().int().min(1).max(65_535),
38
- basePath: z.string().startsWith("/"),
45
+ basePath: z.string().refine(isCanonicalPublicBasePath, "Invalid public Base Path"),
39
46
  captureMode: z.enum(["simple", "full"]),
47
+ backend: BackendImplementationSchema.default("typescript"),
48
+ uiEnabled: z.boolean(),
40
49
  dataDir: z.string().min(1),
41
50
  supervisorPid: z.number().int().positive().nullable(),
42
51
  urls: InstanceUrlsSchema,
43
52
  })
44
53
  .strict();
45
54
 
55
+ export const InstanceConnectionSurfacesSchema = z
56
+ .object({
57
+ ui: z
58
+ .object({
59
+ enabled: z.boolean(),
60
+ baseUrl: z.url().nullable(),
61
+ })
62
+ .strict(),
63
+ control: z.object({ baseUrl: z.url() }).strict(),
64
+ agent: z.object({ baseUrl: z.url() }).strict(),
65
+ })
66
+ .strict();
67
+
46
68
  export const InstanceConnectionOutputSchema = z
47
69
  .object({
48
70
  schemaVersion: z.literal(INSTANCE_API_SCHEMA_VERSION),
71
+ uiEnabled: z.boolean(),
49
72
  instance: InstanceStatusOutputSchema,
73
+ surfaces: InstanceConnectionSurfacesSchema,
74
+ /** @deprecated Use surfaces.control.baseUrl or surfaces.agent.baseUrl. */
50
75
  baseUrl: z.url(),
51
76
  endpoints: InstanceUrlsSchema,
52
77
  environment: z
@@ -82,12 +107,8 @@ export const InstanceStartRequestSchema = z
82
107
  .min(1)
83
108
  .max(2_048)
84
109
  .refine(
85
- (value) =>
86
- value.startsWith("/") &&
87
- !value.includes("?") &&
88
- !value.includes("#") &&
89
- !value.includes("\\"),
90
- "Base Path must be an absolute URL path without query, hash, or backslash",
110
+ (value) => normalizePublicBasePath(value) !== null,
111
+ "Base Path must use URL-safe path segments without traversal, encoded separators, or markup characters",
91
112
  )
92
113
  .optional(),
93
114
  host: z
@@ -105,6 +126,8 @@ export const InstanceStartRequestSchema = z
105
126
  )
106
127
  .optional(),
107
128
  captureMode: z.enum(["simple", "full"]).optional(),
129
+ backend: BackendImplementationSchema.optional(),
130
+ uiEnabled: z.boolean().optional(),
108
131
  dataDir: z.string().trim().min(1).max(32_767).optional(),
109
132
  })
110
133
  .strict();
@@ -175,15 +198,11 @@ export const InstanceErrorOutputSchema = z
175
198
  })
176
199
  .strict();
177
200
 
178
- export type InstanceState = z.infer<typeof InstanceStateSchema>;
179
- export type InstanceStateReason = z.infer<typeof InstanceStateReasonSchema>;
180
201
  export type InstanceStatusOutput = z.infer<typeof InstanceStatusOutputSchema>;
181
202
  export type InstanceConnectionOutput = z.infer<typeof InstanceConnectionOutputSchema>;
182
203
  export type InstanceStartRequest = z.infer<typeof InstanceStartRequestSchema>;
183
- export type InstanceOperation = z.infer<typeof InstanceOperationSchema>;
184
204
  export type InstanceOperationOutcome = z.infer<typeof InstanceOperationOutcomeSchema>;
185
205
  export type InstanceOperationOutput = z.infer<typeof InstanceOperationOutputSchema>;
186
- export type InstanceListOutput = z.infer<typeof InstanceListOutputSchema>;
187
206
  export type InstancePublicErrorCode = z.infer<typeof InstancePublicErrorCodeSchema>;
188
207
  export type InstancePublicError = z.infer<typeof InstancePublicErrorSchema>;
189
208
  export type InstanceErrorOutput = z.infer<typeof InstanceErrorOutputSchema>;
@@ -5,6 +5,7 @@ export const MANAGED_INSTANCE_SHUTDOWN_MESSAGE_TYPE = "agent-inspector:managed-s
5
5
  export const AGENT_INSPECTOR_CLI_ENTRY_ENV = "AGENT_INSPECTOR_CLI_ENTRY";
6
6
 
7
7
  export const MANAGED_INSTANCE_ENV = {
8
+ backend: "AGENT_INSPECTOR_INSTANCE_BACKEND",
8
9
  name: "AGENT_INSPECTOR_INSTANCE_NAME",
9
10
  instanceId: "AGENT_INSPECTOR_INSTANCE_ID",
10
11
  launchId: "AGENT_INSPECTOR_INSTANCE_LAUNCH_ID",
@@ -16,6 +17,7 @@ export const MANAGED_INSTANCE_ENV = {
16
17
  type ManagedInstanceEnv = Readonly<Record<string, string | undefined>>;
17
18
 
18
19
  export type ManagedInstanceLaunch = {
20
+ backend: BackendImplementation;
19
21
  name: string;
20
22
  instanceId: string;
21
23
  launchId: string;
@@ -47,6 +49,7 @@ export function readManagedInstanceLaunch(
47
49
  env: ManagedInstanceEnv = process.env,
48
50
  ): ManagedInstanceLaunchResult {
49
51
  const values = [
52
+ env[MANAGED_INSTANCE_ENV.backend],
50
53
  env[MANAGED_INSTANCE_ENV.name],
51
54
  env[MANAGED_INSTANCE_ENV.instanceId],
52
55
  env[MANAGED_INSTANCE_ENV.launchId],
@@ -59,6 +62,9 @@ export function readManagedInstanceLaunch(
59
62
  }
60
63
 
61
64
  const name = nonEmpty(env[MANAGED_INSTANCE_ENV.name]);
65
+ const backend = parseBackendImplementation(
66
+ nonEmpty(env[MANAGED_INSTANCE_ENV.backend]) ?? undefined,
67
+ );
62
68
  const instanceId = nonEmpty(env[MANAGED_INSTANCE_ENV.instanceId]);
63
69
  const launchId = nonEmpty(env[MANAGED_INSTANCE_ENV.launchId]);
64
70
  const controlToken = nonEmpty(env[MANAGED_INSTANCE_ENV.controlToken]);
@@ -66,6 +72,7 @@ export function readManagedInstanceLaunch(
66
72
  const upstreamPort = Number(env[MANAGED_INSTANCE_ENV.upstreamPort]);
67
73
  if (
68
74
  name === null ||
75
+ backend === null ||
69
76
  instanceId === null ||
70
77
  launchId === null ||
71
78
  controlToken === null ||
@@ -83,7 +90,7 @@ export function readManagedInstanceLaunch(
83
90
  }
84
91
  return {
85
92
  kind: "managed",
86
- launch: { name, instanceId, launchId, controlToken, startedAt, upstreamPort },
93
+ launch: { backend, name, instanceId, launchId, controlToken, startedAt, upstreamPort },
87
94
  };
88
95
  }
89
96
 
@@ -102,3 +109,4 @@ export function parseManagedInstanceShutdownMessage(
102
109
  ? { type: MANAGED_INSTANCE_SHUTDOWN_MESSAGE_TYPE, controlToken: value["controlToken"] }
103
110
  : null;
104
111
  }
112
+ import { parseBackendImplementation, type BackendImplementation } from "./backendImplementation";
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { LogIdSchema } from "../contracts";
2
3
 
3
4
  export const PiAgentHistoryMessageSchema = z.object({
4
5
  role: z.enum(["user", "assistant"]),
@@ -55,9 +56,9 @@ export const PiAgentQueryStatsSchema = z.object({
55
56
  export const PiAgentEvidenceSchema = z.object({
56
57
  source: PiAgentEvidenceSourceSchema,
57
58
  id: z.string(),
58
- logId: z.number().int().positive().optional(),
59
+ logId: LogIdSchema.optional(),
59
60
  knowledgeId: z.string().optional(),
60
- logIds: z.array(z.number().int().positive()).optional(),
61
+ logIds: z.array(LogIdSchema).optional(),
61
62
  timestamp: z.string().optional(),
62
63
  sessionId: z.string().optional(),
63
64
  model: z.string().optional(),
@@ -1,9 +1,10 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import {
3
3
  AGENT_INSPECTOR_PUBLIC_BASE_PATH_HEADER,
4
+ AGENT_INSPECTOR_PUBLIC_ORIGIN_HEADER,
4
5
  AGENT_INSPECTOR_PUBLIC_PORT_HEADER,
5
6
  getConfiguredBasePath,
6
- normalizeBasePath,
7
+ normalizePublicBasePath,
7
8
  } from "./basePath";
8
9
  import { getCurrentPort } from "./serverPort";
9
10
 
@@ -11,17 +12,12 @@ type BasePathEnv = Readonly<Record<string, string | undefined>>;
11
12
 
12
13
  type PublicRequestContext = {
13
14
  basePath: string;
15
+ publicOrigin: string | null;
14
16
  publicPort: number | null;
15
17
  };
16
18
 
17
19
  const publicRequestStorage = new AsyncLocalStorage<PublicRequestContext>();
18
20
 
19
- function isSafePublicBasePath(value: string): boolean {
20
- if (value.length === 0) return true;
21
- if (!/^\/[A-Za-z0-9._~-]+(?:\/[A-Za-z0-9._~-]+)*$/u.test(value)) return false;
22
- return !value.split("/").some((segment) => segment === "." || segment === "..");
23
- }
24
-
25
21
  /**
26
22
  * Resolve the public Base Path attached by the trusted ingress. Invalid or
27
23
  * missing values fall back to the process's canonical Base Path.
@@ -29,8 +25,8 @@ function isSafePublicBasePath(value: string): boolean {
29
25
  export function resolveRequestPublicBasePath(request: Request, env?: BasePathEnv): string {
30
26
  const rawValue = request.headers.get(AGENT_INSPECTOR_PUBLIC_BASE_PATH_HEADER);
31
27
  if (rawValue !== null) {
32
- const normalized = normalizeBasePath(rawValue);
33
- if (isSafePublicBasePath(normalized)) return normalized;
28
+ const normalized = normalizePublicBasePath(rawValue);
29
+ if (normalized !== null) return normalized;
34
30
  }
35
31
  return getConfiguredBasePath(env ?? process.env);
36
32
  }
@@ -59,6 +55,29 @@ export function resolveRequestPublicPort(request: Request): number | null {
59
55
  }
60
56
  }
61
57
 
58
+ function normalizedPublicOrigin(value: string): string | null {
59
+ try {
60
+ const parsed = new URL(value);
61
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
62
+ if (parsed.username.length > 0 || parsed.password.length > 0) return null;
63
+ if (parsed.pathname !== "/" || parsed.search.length > 0 || parsed.hash.length > 0) return null;
64
+ return parsed.origin;
65
+ } catch {
66
+ return null;
67
+ }
68
+ }
69
+
70
+ /** Resolve the public origin set by trusted ingress without consulting forwarded headers. */
71
+ export function resolveRequestPublicOrigin(request: Request): string | null {
72
+ const headerOrigin = request.headers.get(AGENT_INSPECTOR_PUBLIC_ORIGIN_HEADER);
73
+ if (headerOrigin !== null) return normalizedPublicOrigin(headerOrigin);
74
+ try {
75
+ return normalizedPublicOrigin(new URL(request.url).origin);
76
+ } catch {
77
+ return null;
78
+ }
79
+ }
80
+
62
81
  /** Return the originating request's public Base Path, or the canonical path outside a request. */
63
82
  export function getPublicBasePath(env?: BasePathEnv): string {
64
83
  return publicRequestStorage.getStore()?.basePath ?? getConfiguredBasePath(env ?? process.env);
@@ -69,12 +88,21 @@ export function getPublicPort(): number {
69
88
  return publicRequestStorage.getStore()?.publicPort ?? getCurrentPort();
70
89
  }
71
90
 
91
+ /** Return the trusted public origin, with a loopback fallback outside an ingress request. */
92
+ export function getPublicOrigin(): string {
93
+ return (
94
+ publicRequestStorage.getStore()?.publicOrigin ?? `http://localhost:${String(getCurrentPort())}`
95
+ );
96
+ }
97
+
72
98
  /** Run work with an explicitly selected public Base Path. Primarily useful for internal calls. */
73
99
  export function runWithPublicBasePath<T>(basePath: string, callback: () => T): T {
74
100
  const current = publicRequestStorage.getStore();
101
+ const normalized = normalizePublicBasePath(basePath) ?? getConfiguredBasePath(process.env);
75
102
  return publicRequestStorage.run(
76
103
  {
77
- basePath: normalizeBasePath(basePath),
104
+ basePath: normalized,
105
+ publicOrigin: current?.publicOrigin ?? null,
78
106
  publicPort: current?.publicPort ?? null,
79
107
  },
80
108
  callback,
@@ -89,6 +117,7 @@ export function runWithPublicPort<T>(publicPort: number, callback: () => T): T {
89
117
  return publicRequestStorage.run(
90
118
  {
91
119
  basePath: current?.basePath ?? getConfiguredBasePath(process.env),
120
+ publicOrigin: current?.publicOrigin ?? null,
92
121
  publicPort: normalizedPort,
93
122
  },
94
123
  callback,
@@ -100,6 +129,7 @@ export function runWithRequestPublicBasePath<T>(request: Request, callback: () =
100
129
  return publicRequestStorage.run(
101
130
  {
102
131
  basePath: resolveRequestPublicBasePath(request),
132
+ publicOrigin: resolveRequestPublicOrigin(request),
103
133
  publicPort: resolveRequestPublicPort(request),
104
134
  },
105
135
  callback,
@@ -9,6 +9,7 @@ export const MAX_PROVIDER_MODEL_METADATA = 64;
9
9
  export const MAX_SSE_CLIENTS = 32;
10
10
  export const MAX_ACTIVE_PROVIDER_TESTS = 2;
11
11
  export const MAX_PROVIDER_TEST_CONCURRENCY = 4;
12
+ export const MAX_ACTIVE_PROXY_REQUESTS = 16;
12
13
 
13
14
  export const ResourceLimitErrorCodeSchema = z.enum([
14
15
  "invalid-page-request",
@@ -16,6 +17,7 @@ export const ResourceLimitErrorCodeSchema = z.enum([
16
17
  "provider-model-limit-exceeded",
17
18
  "sse-capacity-exceeded",
18
19
  "provider-test-capacity-exceeded",
20
+ "proxy-capacity-exceeded",
19
21
  ]);
20
22
 
21
23
  export const ResourceLimitStatusSchema = z.union([
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { JsonValueSchema } from "../contracts";
2
+ import { JsonValueSchema, LogIdSchema } from "../contracts";
3
3
  import { SessionInfoSchema } from "./sessionInfoContract";
4
4
 
5
5
  export const InspectorRunStatusSchema = z.enum([
@@ -33,7 +33,7 @@ export const RunTimelineEventSchema = z.object({
33
33
  severity: RunTimelineEventSeveritySchema,
34
34
  title: z.string(),
35
35
  details: z.string(),
36
- logId: z.number().int().positive().nullable(),
36
+ logId: LogIdSchema.nullable(),
37
37
  status: z.number().nullable(),
38
38
  model: z.string().nullable(),
39
39
  provider: z.string().nullable(),
@@ -62,7 +62,7 @@ export const FailureClassificationSchema = z.object({
62
62
  severity: z.enum(["none", "low", "medium", "high"]),
63
63
  category: FailureCategorySchema,
64
64
  summary: z.string(),
65
- evidenceLogIds: z.array(z.number().int().positive()),
65
+ evidenceLogIds: z.array(LogIdSchema),
66
66
  hints: z.array(z.string()),
67
67
  });
68
68
 
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { LogIdSchema } from "../contracts";
2
3
 
3
4
  export const SessionTokenUsageSchema = z.object({
4
5
  input: z.number().int().nonnegative(),
@@ -9,7 +10,7 @@ export const SessionTokenUsageSchema = z.object({
9
10
  });
10
11
 
11
12
  export const SessionLogSummarySchema = z.object({
12
- id: z.number().int().positive(),
13
+ id: LogIdSchema,
13
14
  sessionLogNumber: z.number().int().positive().optional(),
14
15
  timestamp: z.string(),
15
16
  provider: z.string().nullable(),
@@ -57,7 +58,7 @@ export const SessionInfoSchema = z.object({
57
58
  models: z.array(z.string()),
58
59
  providers: z.array(z.string()),
59
60
  tokenUsage: SessionTokenUsageSchema,
60
- lastLogId: z.number().nullable(),
61
+ lastLogId: LogIdSchema.nullable(),
61
62
  lastModel: z.string().nullable(),
62
63
  clientUserAgent: z.string().nullable().optional(),
63
64
  clientPid: z.number().nullable(),
@@ -90,6 +90,8 @@ function lifecycleRequest(input: InstanceLifecycleToolInput): InstanceStartReque
90
90
  basePath: input.basePath,
91
91
  host: input.host,
92
92
  captureMode: input.captureMode,
93
+ backend: input.backend,
94
+ uiEnabled: input.uiEnabled,
93
95
  dataDir: input.dataDir,
94
96
  };
95
97
  }
package/src/mcp/server.ts CHANGED
@@ -36,10 +36,10 @@ import { InspectorRunStatusSchema, UpdateInspectorRunInputSchema } from "../lib/
36
36
  import { withBasePath } from "../lib/basePath";
37
37
  import {
38
38
  getPublicBasePath,
39
- getPublicPort,
39
+ getPublicOrigin,
40
40
  runWithRequestPublicBasePath,
41
41
  } from "../lib/publicBasePathContext";
42
- import { JsonValueSchema } from "../contracts";
42
+ import { JsonValueSchema, LogIdSchema, parseLogId } from "../contracts";
43
43
  import { InstanceListOutputSchema } from "../lib/instanceContract";
44
44
  import {
45
45
  MAX_PROVIDER_MODEL_NAME_CHARS,
@@ -335,7 +335,15 @@ const UpdateProviderInputSchema = z.object({
335
335
  });
336
336
 
337
337
  const LogResourceListResponseSchema = z.object({
338
- logs: z.array(z.object({ id: z.number().int().positive() })),
338
+ logs: z.array(z.object({ id: LogIdSchema })),
339
+ });
340
+
341
+ export const McpLogIdInputSchema = z.object({
342
+ id: LogIdSchema.describe("The log id."),
343
+ });
344
+
345
+ export const McpOptionalLogIdInputSchema = z.object({
346
+ id: LogIdSchema.optional().describe("Optional log id to inspect."),
339
347
  });
340
348
 
341
349
  const ProcessResourceListResponseSchema = z.object({
@@ -361,10 +369,7 @@ function jsonResource(uri: URL, data: unknown): ReadResourceResult {
361
369
  }
362
370
 
363
371
  function mcpEndpointUrl(): string {
364
- return `http://localhost:${String(getPublicPort())}${withBasePath(
365
- "/api/mcp",
366
- getPublicBasePath(),
367
- )}`;
372
+ return `${getPublicOrigin()}${withBasePath("/api/mcp", getPublicBasePath())}`;
368
373
  }
369
374
 
370
375
  function mcpHealth(): Record<string, unknown> {
@@ -968,9 +973,7 @@ function registerTools(server: McpServer): void {
968
973
  title: "Get a single captured log by id",
969
974
  description:
970
975
  "Returns the full CapturedLog for the given id, including rawRequestBody and responseText with no truncation. SSE streaming chunks are NOT included — use inspector_get_log_chunks for those. Returns an error if the id does not exist.",
971
- inputSchema: z.object({
972
- id: z.number().int().positive().describe("The log id."),
973
- }),
976
+ inputSchema: McpLogIdInputSchema,
974
977
  },
975
978
  ({ id }) => safeCall(() => getLogImpl(callApi, id)),
976
979
  );
@@ -981,9 +984,7 @@ function registerTools(server: McpServer): void {
981
984
  title: "Get SSE streaming chunks for a captured log",
982
985
  description:
983
986
  "Returns the SSE chunks array for a streaming log, in the order they were received. Returns an error if the log has no chunks (i.e. was non-streaming or the log id is unknown).",
984
- inputSchema: z.object({
985
- id: z.number().int().positive().describe("The log id."),
986
- }),
987
+ inputSchema: McpLogIdInputSchema,
987
988
  },
988
989
  ({ id }) => safeCall(() => getLogChunksImpl(callApi, id)),
989
990
  );
@@ -994,9 +995,7 @@ function registerTools(server: McpServer): void {
994
995
  title: "Get captured log headers",
995
996
  description:
996
997
  "Returns header-focused evidence for one log without large request/response bodies: raw headers, upstream headers, user-agent, origin, client PID/port, session id, model, and API path.",
997
- inputSchema: z.object({
998
- id: z.number().int().positive().describe("The log id."),
999
- }),
998
+ inputSchema: McpLogIdInputSchema,
1000
999
  },
1001
1000
  ({ id }) => safeCall(() => getLogHeadersImpl(callApi, id)),
1002
1001
  );
@@ -1174,9 +1173,7 @@ function registerTools(server: McpServer): void {
1174
1173
  title: "Diagnose capture detail availability",
1175
1174
  description:
1176
1175
  "Explains why headers or bodies may be missing for the current runtime or a specific log, including captureMode, compact body mode, rawHeaders presence, and next-step recommendations.",
1177
- inputSchema: z.object({
1178
- id: z.number().int().positive().optional().describe("Optional log id to inspect."),
1179
- }),
1176
+ inputSchema: McpOptionalLogIdInputSchema,
1180
1177
  },
1181
1178
  (args) => safeCall(() => diagnoseCaptureImpl(callApi, args)),
1182
1179
  );
@@ -1504,9 +1501,7 @@ function registerTools(server: McpServer): void {
1504
1501
  title: "Replay a captured log against its provider",
1505
1502
  description:
1506
1503
  "Re-sends the captured request body to the upstream LLM and returns the response summary: success flag, status, responseText, input/output token counts, elapsedMs, and whether the response was streaming. Useful for re-running a request after a fix or a transient failure. Returns an error if the log id is unknown or the upstream call fails.",
1507
- inputSchema: z.object({
1508
- id: z.number().int().positive().describe("The log id to replay."),
1509
- }),
1504
+ inputSchema: McpLogIdInputSchema,
1510
1505
  },
1511
1506
  (args) => safeWriteCall("inspector_replay_log", () => replayLogImpl(callApi, args)),
1512
1507
  );
@@ -1815,8 +1810,8 @@ function registerResources(server: McpServer): void {
1815
1810
  },
1816
1811
  async (uri, variables) => {
1817
1812
  const id = variableString(variables, "id");
1818
- const parsed = id === null ? Number.NaN : Number(id);
1819
- if (!Number.isInteger(parsed) || parsed <= 0) {
1813
+ const parsed = id === null ? null : parseLogId(id);
1814
+ if (parsed === null) {
1820
1815
  return textResource(uri, "text/plain", "Invalid log id");
1821
1816
  }
1822
1817
  const result = await getLogImpl(callApi, parsed);
@@ -1,11 +1,4 @@
1
- import {
2
- writeFileSync,
3
- readFileSync,
4
- unlinkSync,
5
- existsSync,
6
- renameSync,
7
- copyFileSync,
8
- } from "node:fs";
1
+ import { writeFileSync, readFileSync, unlinkSync, existsSync, renameSync } from "node:fs";
9
2
  import { join } from "node:path";
10
3
  import { logger } from "./logger";
11
4
  import { getDataDir } from "./dataDir";
@@ -128,19 +121,19 @@ export function getConfig(): RuntimeConfig {
128
121
  }
129
122
 
130
123
  /**
131
- * Applies a partial update to the in-memory config and persists to disk.
132
- * Returns the updated full config. Persistence failures are logged but
133
- * do not throw the in-memory value is always updated so the caller
134
- * sees a consistent response.
124
+ * Applies a partial update after atomically persisting it to disk.
125
+ * Returns the updated full config on success or null when persistence
126
+ * fails. A failed write leaves both the in-memory value and the existing
127
+ * on-disk value unchanged.
135
128
  */
136
- export function setConfig(patch: Partial<MutableRuntimeConfig>): RuntimeConfig {
129
+ export function setConfig(patch: Partial<MutableRuntimeConfig>): RuntimeConfig | null {
137
130
  const next: RuntimeConfig = {
138
131
  ...currentConfig,
139
132
  ...patch,
140
133
  captureMode: currentConfig.captureMode,
141
134
  };
135
+ if (!persistConfig(next)) return null;
142
136
  currentConfig = next;
143
- persistConfig(next);
144
137
  return next;
145
138
  }
146
139
 
@@ -164,8 +157,10 @@ export function _rebootForTests(): void {
164
157
  // --- Atomic write ---
165
158
 
166
159
  /**
167
- * Atomic write: temp file + rename. Mirrors the pattern in chunkStorage.ts.
168
- * Returns true on success, false on failure (logged but not thrown).
160
+ * Atomic write: secure temp file + rename in the same directory.
161
+ * Returns true on success, false on failure (logged but not thrown). There is
162
+ * deliberately no copy fallback because replacing the target through a copy
163
+ * could expose a partially written config after a crash.
169
164
  */
170
165
  function persistConfig(value: RuntimeConfig): boolean {
171
166
  const dir = getDataDir();
@@ -181,13 +176,15 @@ function persistConfig(value: RuntimeConfig): boolean {
181
176
  writeFileSync(tempPath, JSON.stringify(toPersistedConfig(value)), {
182
177
  encoding: "utf-8",
183
178
  mode: PRIVATE_FILE_MODE,
179
+ flush: true,
184
180
  });
185
181
  if (!securePrivateFileSync(tempPath)) {
186
- unlinkSync(tempPath);
182
+ removeConfigTempFile(tempPath);
187
183
  logger.error(`[config] Failed to secure config temp file ${tempPath}`);
188
184
  return false;
189
185
  }
190
186
  } catch (err) {
187
+ removeConfigTempFile(tempPath);
191
188
  logger.error(`[config] Failed to write config temp file: ${String(err)}`);
192
189
  return false;
193
190
  }
@@ -196,19 +193,17 @@ function persistConfig(value: RuntimeConfig): boolean {
196
193
  renameSync(tempPath, targetPath);
197
194
  return true;
198
195
  } catch (err) {
199
- logger.warn(`[config] Rename failed, falling back to copy+delete: ${String(err)}`);
200
- try {
201
- copyFileSync(tempPath, targetPath);
202
- if (!securePrivateFileSync(targetPath)) {
203
- logger.error(`[config] Failed to secure config file ${targetPath}`);
204
- return false;
205
- }
206
- unlinkSync(tempPath);
207
- return true;
208
- } catch (copyErr) {
209
- logger.error(`[config] Failed to copy config file: ${String(copyErr)}`);
210
- return false;
211
- }
196
+ removeConfigTempFile(tempPath);
197
+ logger.error(`[config] Failed to atomically replace config file: ${String(err)}`);
198
+ return false;
199
+ }
200
+ }
201
+
202
+ function removeConfigTempFile(tempPath: string): void {
203
+ try {
204
+ unlinkSync(tempPath);
205
+ } catch {
206
+ // Best effort. A stale temp path is never read as runtime configuration.
212
207
  }
213
208
  }
214
209
 
@@ -1,6 +1,10 @@
1
- import { cpSync, existsSync, rmSync } from "node:fs";
1
+ import { cpSync, existsSync, readdirSync, renameSync, rmSync } from "node:fs";
2
2
  import { isAbsolute, join } from "node:path";
3
- import { ensurePrivateDirectorySync, securePrivateTreeSync } from "./privateDataPath";
3
+ import {
4
+ ensurePrivateDirectorySync,
5
+ isPathTreeFreeOfIndirectionSync,
6
+ securePrivateTreeSync,
7
+ } from "./privateDataPath";
4
8
 
5
9
  const CURRENT_DATA_DIR_NAME = ".agent-inspector";
6
10
  const LEGACY_DATA_DIR_NAMES = [".llm-inspector", ".llm-agent"];
@@ -98,14 +102,46 @@ export function migrateLegacyDataDirs(): void {
98
102
  const legacyDir = join(base, legacyName);
99
103
  if (!existsSync(legacyDir)) continue;
100
104
 
105
+ const stagingDir = join(
106
+ base,
107
+ `${CURRENT_DATA_DIR_NAME}.migration-${String(process.pid)}-${String(Date.now())}`,
108
+ );
109
+
101
110
  try {
102
- if (!ensurePrivateDirectorySync(currentDir)) return;
103
- cpSync(legacyDir, currentDir, { recursive: true, force: true });
104
- if (!securePrivateTreeSync(currentDir)) return;
111
+ // cpSync may dereference junctions on some Windows runtimes. Reject the source tree before
112
+ // copying and recheck it afterwards so migration never imports through an indirection.
113
+ if (!isPathTreeFreeOfIndirectionSync(legacyDir)) return;
114
+ if (existsSync(currentDir)) {
115
+ // Never merge legacy state into an unknown or partially initialized destination. A caller
116
+ // can inspect it and retry after resolving the conflict; the authoritative legacy source
117
+ // remains untouched.
118
+ if (readdirSync(currentDir).length > 0) return;
119
+ rmSync(currentDir, { recursive: true, force: false });
120
+ }
121
+ if (!ensurePrivateDirectorySync(stagingDir)) return;
122
+ for (const entry of readdirSync(legacyDir)) {
123
+ cpSync(join(legacyDir, entry), join(stagingDir, entry), {
124
+ recursive: true,
125
+ force: false,
126
+ errorOnExist: true,
127
+ });
128
+ }
129
+ if (!isPathTreeFreeOfIndirectionSync(legacyDir)) {
130
+ rmSync(stagingDir, { recursive: true, force: true });
131
+ return;
132
+ }
133
+ if (!securePrivateTreeSync(stagingDir)) {
134
+ rmSync(stagingDir, { recursive: true, force: true });
135
+ return;
136
+ }
137
+ renameSync(stagingDir, currentDir);
105
138
  rmSync(legacyDir, { recursive: true, force: true });
139
+ return;
106
140
  } catch {
107
- // Best-effort migration: startup must continue even when the old
108
- // directory is locked or partially unreadable. The next call can retry.
141
+ // A failed staged migration never deletes the legacy source. Remove only this attempt's
142
+ // private sibling and retry on a later startup.
143
+ if (existsSync(stagingDir)) rmSync(stagingDir, { recursive: true, force: true });
144
+ return;
109
145
  }
110
146
  }
111
147
  }