@tonyclaw/agent-inspector 3.0.44 → 3.0.46

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 (347) 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 +22707 -17099
  4. package/.output/server/_libs/extend.mjs +4 -8
  5. package/.output/server/_libs/jszip.mjs +9 -35
  6. package/.output/server/_libs/modelcontextprotocol__core.mjs +25 -25
  7. package/.output/server/_libs/modelcontextprotocol__server.mjs +2 -2
  8. package/.output/server/_libs/react-dom.mjs +2 -3
  9. package/.output/server/_libs/react.mjs +1 -27
  10. package/.output/server/_libs/tanstack__react-router.mjs +1 -329
  11. package/.output/server/_libs/tanstack__router-core.mjs +42 -100
  12. package/.output/server/_libs/use-sync-external-store.mjs +1 -64
  13. package/.output/server/_libs/ws.mjs +1 -1
  14. package/.output/server/_libs/zod.mjs +6 -6
  15. package/.output/server/_ssr/index.mjs +5 -4
  16. package/.output/server/_ssr/publicBasePathContext-IH2ilOfu.mjs +176 -0
  17. package/.output/server/_ssr/{router-DWcUFPR3.mjs → router-D18yUq36.mjs} +7877 -5597
  18. package/.output/server/_ssr/start-COQu_AgD.mjs +57 -0
  19. package/.output/server/_tanstack-start-manifest_v-DnbdNeun.mjs +4 -0
  20. package/.output/server/index.mjs +5 -145
  21. package/.output/{public/assets/CompareDrawer-DQHjxFO6.js → ui/assets/CompareDrawer-DAUuIJ6G.js} +1 -1
  22. package/.output/{public/assets/InspectorPet-D1A4505B.js → ui/assets/InspectorPet-BRBVjOWI.js} +1 -1
  23. package/.output/ui/assets/ProxyViewerContainer-D7Sq0ctc.js +59 -0
  24. package/.output/{public/assets/ReplayDialog-C5vmbecv.js → ui/assets/ReplayDialog-nDLsjOhs.js} +1 -1
  25. package/.output/{public/assets/RequestAnatomy-CPGNB8lk.js → ui/assets/RequestAnatomy-zd4BDgL0.js} +1 -1
  26. package/.output/ui/assets/ResponseView-AdIXGcSn.js +2 -0
  27. package/.output/ui/assets/StreamingChunkSequence-CesTz8He.js +1 -0
  28. package/.output/{public/assets/_sessionId-CDhRgzf6.js → ui/assets/_sessionId-BAfzrhSU.js} +1 -1
  29. package/.output/{public/assets/_sessionId-mw3aA8Bp.js → ui/assets/_sessionId-QnBomgPD.js} +1 -1
  30. package/.output/ui/assets/favicon-DYEkHSNl.svg +21 -0
  31. package/.output/{public/assets/index-B2eoeT-Z.js → ui/assets/index-BcsdxBAd.js} +1 -1
  32. package/.output/ui/assets/index-BvXp42al.css +1 -0
  33. package/.output/{public/assets/index-CX7_rf7o.js → ui/assets/index-D0rtCN9V.js} +1 -1
  34. package/.output/ui/assets/index-DHpwr08Z.js +73 -0
  35. package/.output/{public/assets/index-BfRhTvOd.js → ui/assets/index-dNuk2dsU.js} +1 -1
  36. package/.output/ui/assets/json-viewer-BvFHglMb.js +1 -0
  37. package/.output/{public/assets/jszip.min-C9rCFoU3.js → ui/assets/jszip.min-B5Z7gNZB.js} +1 -1
  38. package/.output/ui/index.html +229 -0
  39. package/.output/ui/runtime-config.js +2 -0
  40. package/.output/workers/logFinalizer.worker.js +983 -414
  41. package/.output/workers/sessionWorkerEntry.js +983 -414
  42. package/README.md +363 -35
  43. package/docs/instance-control.openapi.yaml +361 -0
  44. package/package.json +28 -5
  45. package/scripts/generate-theme-scales.mjs +3 -3
  46. package/src/{router.tsx → backend/router.tsx} +3 -15
  47. package/src/backend/routes/$.ts +26 -0
  48. package/src/backend/routes/__root.ts +4 -0
  49. package/src/backend/routes/api/-instances.ts +109 -0
  50. package/src/{routes → backend/routes}/api/alerts.summary.ts +3 -3
  51. package/src/{routes → backend/routes}/api/alerts.ts +4 -4
  52. package/src/{routes → backend/routes}/api/config.paths.ts +1 -1
  53. package/src/{routes → backend/routes}/api/config.ts +11 -2
  54. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.help.ts +1 -1
  55. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.install.ts +1 -1
  56. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.runner-presets.ts +1 -1
  57. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.upgrade.ts +1 -1
  58. package/src/{routes → backend/routes}/api/ecosystem.packages.ts +5 -1
  59. package/src/{routes → backend/routes}/api/ecosystem.recipes.$recipeId.run.ts +1 -1
  60. package/src/{routes → backend/routes}/api/ecosystem.tasks.$taskId.ts +1 -1
  61. package/src/{routes → backend/routes}/api/ecosystem.tasks.ts +1 -1
  62. package/src/{routes → backend/routes}/api/groups.$groupId.evidence.ts +2 -2
  63. package/src/{routes → backend/routes}/api/groups.$groupId.sessions.ts +2 -2
  64. package/src/{routes → backend/routes}/api/groups.$groupId.ts +2 -2
  65. package/src/{routes → backend/routes}/api/groups.ts +2 -2
  66. package/src/{routes → backend/routes}/api/health.ts +3 -3
  67. package/src/backend/routes/api/instances.$name.connection.ts +12 -0
  68. package/src/backend/routes/api/instances.$name.restart.ts +23 -0
  69. package/src/backend/routes/api/instances.$name.start.ts +18 -0
  70. package/src/backend/routes/api/instances.$name.stop.ts +23 -0
  71. package/src/backend/routes/api/instances.$name.ts +12 -0
  72. package/src/backend/routes/api/instances.ts +16 -0
  73. package/src/{routes → backend/routes}/api/knowledge.candidates.$candidateId.promote.ts +2 -2
  74. package/src/{routes → backend/routes}/api/knowledge.candidates.$candidateId.ts +2 -2
  75. package/src/{routes → backend/routes}/api/knowledge.candidates.ts +1 -1
  76. package/src/{routes → backend/routes}/api/knowledge.project-context.ts +2 -2
  77. package/src/{routes → backend/routes}/api/knowledge.search.ts +2 -2
  78. package/src/{routes → backend/routes}/api/knowledge.sessions.$sessionId.candidates.ts +3 -3
  79. package/src/{routes → backend/routes}/api/logs.$id.body.ts +6 -6
  80. package/src/{routes → backend/routes}/api/logs.$id.chunks.ts +5 -4
  81. package/src/{routes → backend/routes}/api/logs.$id.replay.ts +12 -11
  82. package/src/{routes → backend/routes}/api/logs.$id.ts +4 -3
  83. package/src/{routes → backend/routes}/api/logs.import.ts +3 -3
  84. package/src/{routes → backend/routes}/api/logs.stream.ts +11 -7
  85. package/src/{routes → backend/routes}/api/logs.ts +17 -9
  86. package/src/{routes → backend/routes}/api/mcp.ts +1 -1
  87. package/src/{routes → backend/routes}/api/models.ts +1 -1
  88. package/src/{routes → backend/routes}/api/pi-agent.ts +2 -2
  89. package/src/{routes → backend/routes}/api/providers.$providerId.model-metadata.ts +5 -5
  90. package/src/{routes → backend/routes}/api/providers.$providerId.test.log.ts +7 -7
  91. package/src/{routes → backend/routes}/api/providers.$providerId.ts +4 -4
  92. package/src/{routes → backend/routes}/api/providers.export.ts +1 -1
  93. package/src/{routes → backend/routes}/api/providers.import.ts +3 -3
  94. package/src/{routes → backend/routes}/api/providers.scan.ts +2 -2
  95. package/src/{routes → backend/routes}/api/providers.ts +9 -4
  96. package/src/{routes → backend/routes}/api/runs.$runId.evidence.ts +2 -2
  97. package/src/{routes → backend/routes}/api/runs.$runId.ts +2 -2
  98. package/src/{routes → backend/routes}/api/runs.ts +4 -4
  99. package/src/backend/routes/api/runtime.ts +46 -0
  100. package/src/{routes → backend/routes}/api/sessions.ts +3 -3
  101. package/src/{routes → backend/routes}/proxy/$.ts +1 -1
  102. package/src/{routes → backend/routes}/readyz.ts +1 -1
  103. package/src/backend/start.ts +10 -0
  104. package/src/cli/alias.ts +431 -0
  105. package/src/cli/doctor.ts +32 -9
  106. package/src/cli/instance.ts +1050 -0
  107. package/src/cli/instanceArgs.ts +285 -0
  108. package/src/cli/instanceControl.ts +181 -0
  109. package/src/cli/instanceModel.ts +267 -0
  110. package/src/cli/instanceRegistry.ts +731 -0
  111. package/src/cli/networkHints.ts +27 -14
  112. package/src/cli/onboard.ts +190 -35
  113. package/src/cli/onboardTarget.ts +228 -0
  114. package/src/cli/templates/codex-skill-onboard.ts +109 -31
  115. package/src/cli/templates/skill-onboard.ts +140 -70
  116. package/src/cli.ts +338 -77
  117. package/src/components/ProxyViewer.tsx +20 -10
  118. package/src/components/ProxyViewerContainer.tsx +12 -11
  119. package/src/components/pi-agent/PiAgentPanel.tsx +2 -2
  120. package/src/components/providers/ProviderCard.tsx +1 -1
  121. package/src/components/providers/ProviderTestResultsView.tsx +1 -1
  122. package/src/components/providers/SettingsDialog.tsx +160 -140
  123. package/src/components/proxy-viewer/ApplicationBar.tsx +2 -1
  124. package/src/components/proxy-viewer/LogEntry.tsx +1 -1
  125. package/src/components/proxy-viewer/log-formats/index.ts +1 -1
  126. package/src/contracts/index.ts +5 -0
  127. package/src/contracts/log.ts +33 -3
  128. package/src/knowledge/types.ts +3 -2
  129. package/src/lib/alertContract.ts +2 -2
  130. package/src/lib/apiClient.ts +30 -7
  131. package/src/lib/apiFormat.ts +29 -0
  132. package/src/lib/basePath.ts +31 -7
  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 +54 -0
  137. package/src/lib/instanceContract.ts +236 -0
  138. package/src/lib/managedInstance.ts +104 -0
  139. package/src/lib/piAgentContract.ts +3 -2
  140. package/src/lib/publicBasePathContext.ts +137 -0
  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 +150 -0
  145. package/src/mcp/loopback.ts +16 -5
  146. package/src/mcp/mode.ts +25 -0
  147. package/src/mcp/server.ts +274 -39
  148. package/src/mcp/toolHandlers.ts +2 -2
  149. package/src/proxy/config.ts +25 -30
  150. package/src/proxy/dataDir.ts +43 -7
  151. package/src/proxy/ecosystemTasks.ts +161 -70
  152. package/src/proxy/formats/registry.ts +6 -21
  153. package/src/proxy/handler.ts +129 -29
  154. package/src/proxy/identityProxy.ts +1049 -50
  155. package/src/proxy/jsonlRecovery.ts +554 -0
  156. package/src/proxy/logFinalizer.ts +25 -10
  157. package/src/proxy/logImporter.ts +2 -1
  158. package/src/proxy/logIndex.ts +191 -95
  159. package/src/proxy/logger.ts +77 -14
  160. package/src/proxy/platformCommands.ts +41 -0
  161. package/src/proxy/privateDataPath.ts +310 -88
  162. package/src/proxy/rawStreamCapture.ts +194 -13
  163. package/src/proxy/runtimeAdmission.ts +5 -2
  164. package/src/proxy/runtimeHealth.ts +14 -1
  165. package/src/proxy/runtimeShutdown.ts +84 -2
  166. package/src/proxy/schemas.ts +7 -1
  167. package/src/proxy/sessionArchive.ts +78 -14
  168. package/src/proxy/sessionInfo.ts +5 -4
  169. package/src/proxy/sqliteLogIndex.ts +65 -2
  170. package/src/proxy/store.ts +141 -34
  171. package/src/proxy/uiAssetServer.ts +106 -0
  172. package/src/proxy/upstream.ts +29 -2
  173. package/src/ui/bootstrap.ts +167 -0
  174. package/src/ui/index.html +38 -0
  175. package/src/ui/logsStreamClient.ts +20 -0
  176. package/src/ui/main.tsx +13 -0
  177. package/src/ui/public/runtime-config.js +2 -0
  178. package/src/ui/router.tsx +13 -0
  179. package/src/ui/routes/__root.tsx +69 -0
  180. package/src/{routes → ui/routes}/index.tsx +2 -3
  181. package/src/{routes → ui/routes}/session/$sessionId.tsx +5 -11
  182. package/src/ui/runtimeDiscovery.ts +50 -0
  183. package/styles/globals.css +1 -0
  184. package/.output/public/assets/ProxyViewerContainer-BhpIgfBi.js +0 -126
  185. package/.output/public/assets/ResponseView-DUfrajCm.js +0 -2
  186. package/.output/public/assets/StreamingChunkSequence-nbeCQqOz.js +0 -1
  187. package/.output/public/assets/index-BWjeqxbk.css +0 -1
  188. package/.output/public/assets/index-C6qZwYlR.js +0 -70
  189. package/.output/public/assets/json-viewer-BLmF1sOZ.js +0 -1
  190. package/.output/server/_libs/@radix-ui/react-accessible-icon+[...].mjs +0 -1
  191. package/.output/server/_libs/@radix-ui/react-dismissable-layer+[...].mjs +0 -344
  192. package/.output/server/_libs/@radix-ui/react-navigation-menu+[...].mjs +0 -2
  193. package/.output/server/_libs/@radix-ui/react-one-time-password-field+[...].mjs +0 -2
  194. package/.output/server/_libs/@radix-ui/react-password-toggle-field+[...].mjs +0 -2
  195. package/.output/server/_libs/@radix-ui/react-use-callback-ref+[...].mjs +0 -11
  196. package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +0 -69
  197. package/.output/server/_libs/@radix-ui/react-use-effect-event+[...].mjs +0 -1
  198. package/.output/server/_libs/@radix-ui/react-use-is-hydrated+[...].mjs +0 -28
  199. package/.output/server/_libs/@radix-ui/react-use-layout-effect+[...].mjs +0 -6
  200. package/.output/server/_libs/@radix-ui/react-visually-hidden+[...].mjs +0 -34
  201. package/.output/server/_libs/aria-hidden.mjs +0 -122
  202. package/.output/server/_libs/bail.mjs +0 -8
  203. package/.output/server/_libs/character-entities.mjs +0 -2130
  204. package/.output/server/_libs/class-variance-authority.mjs +0 -44
  205. package/.output/server/_libs/clsx.mjs +0 -16
  206. package/.output/server/_libs/comma-separated-tokens.mjs +0 -10
  207. package/.output/server/_libs/decode-named-character-reference+[...].mjs +0 -8
  208. package/.output/server/_libs/dequal.mjs +0 -27
  209. package/.output/server/_libs/detect-node-es.mjs +0 -1
  210. package/.output/server/_libs/devlop.mjs +0 -8
  211. package/.output/server/_libs/diff.mjs +0 -320
  212. package/.output/server/_libs/estree-util-is-identifier-name.mjs +0 -11
  213. package/.output/server/_libs/floating-ui__core.mjs +0 -725
  214. package/.output/server/_libs/floating-ui__dom.mjs +0 -622
  215. package/.output/server/_libs/floating-ui__react-dom.mjs +0 -292
  216. package/.output/server/_libs/floating-ui__utils.mjs +0 -320
  217. package/.output/server/_libs/get-nonce.mjs +0 -9
  218. package/.output/server/_libs/hast-util-to-jsx-runtime.mjs +0 -388
  219. package/.output/server/_libs/hast-util-whitespace.mjs +0 -10
  220. package/.output/server/_libs/html-url-attributes.mjs +0 -26
  221. package/.output/server/_libs/inline-style-parser.mjs +0 -142
  222. package/.output/server/_libs/is-plain-obj.mjs +0 -10
  223. package/.output/server/_libs/lucide-react.mjs +0 -1027
  224. package/.output/server/_libs/mdast-util-from-markdown.mjs +0 -717
  225. package/.output/server/_libs/mdast-util-to-hast.mjs +0 -710
  226. package/.output/server/_libs/mdast-util-to-string.mjs +0 -38
  227. package/.output/server/_libs/micromark-core-commonmark.mjs +0 -2259
  228. package/.output/server/_libs/micromark-factory-destination.mjs +0 -94
  229. package/.output/server/_libs/micromark-factory-label.mjs +0 -63
  230. package/.output/server/_libs/micromark-factory-space.mjs +0 -24
  231. package/.output/server/_libs/micromark-factory-title.mjs +0 -65
  232. package/.output/server/_libs/micromark-factory-whitespace.mjs +0 -22
  233. package/.output/server/_libs/micromark-util-character.mjs +0 -44
  234. package/.output/server/_libs/micromark-util-chunked.mjs +0 -36
  235. package/.output/server/_libs/micromark-util-classify-character+[...].mjs +0 -12
  236. package/.output/server/_libs/micromark-util-combine-extensions+[...].mjs +0 -41
  237. package/.output/server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +0 -19
  238. package/.output/server/_libs/micromark-util-decode-string.mjs +0 -21
  239. package/.output/server/_libs/micromark-util-encode.mjs +0 -1
  240. package/.output/server/_libs/micromark-util-html-tag-name.mjs +0 -69
  241. package/.output/server/_libs/micromark-util-normalize-identifier+[...].mjs +0 -6
  242. package/.output/server/_libs/micromark-util-resolve-all.mjs +0 -15
  243. package/.output/server/_libs/micromark-util-sanitize-uri.mjs +0 -41
  244. package/.output/server/_libs/micromark-util-subtokenize.mjs +0 -346
  245. package/.output/server/_libs/micromark.mjs +0 -906
  246. package/.output/server/_libs/property-information.mjs +0 -1209
  247. package/.output/server/_libs/radix-ui.mjs +0 -1
  248. package/.output/server/_libs/radix-ui__number.mjs +0 -6
  249. package/.output/server/_libs/radix-ui__primitive.mjs +0 -11
  250. package/.output/server/_libs/radix-ui__react-accordion.mjs +0 -1
  251. package/.output/server/_libs/radix-ui__react-alert-dialog.mjs +0 -1
  252. package/.output/server/_libs/radix-ui__react-arrow.mjs +0 -23
  253. package/.output/server/_libs/radix-ui__react-aspect-ratio.mjs +0 -1
  254. package/.output/server/_libs/radix-ui__react-avatar.mjs +0 -1
  255. package/.output/server/_libs/radix-ui__react-checkbox.mjs +0 -1
  256. package/.output/server/_libs/radix-ui__react-collapsible.mjs +0 -144
  257. package/.output/server/_libs/radix-ui__react-collection.mjs +0 -69
  258. package/.output/server/_libs/radix-ui__react-compose-refs.mjs +0 -38
  259. package/.output/server/_libs/radix-ui__react-context-menu.mjs +0 -1
  260. package/.output/server/_libs/radix-ui__react-context.mjs +0 -64
  261. package/.output/server/_libs/radix-ui__react-dialog.mjs +0 -280
  262. package/.output/server/_libs/radix-ui__react-direction.mjs +0 -9
  263. package/.output/server/_libs/radix-ui__react-dropdown-menu.mjs +0 -1
  264. package/.output/server/_libs/radix-ui__react-focus-guards.mjs +0 -39
  265. package/.output/server/_libs/radix-ui__react-focus-scope.mjs +0 -210
  266. package/.output/server/_libs/radix-ui__react-form.mjs +0 -1
  267. package/.output/server/_libs/radix-ui__react-hover-card.mjs +0 -1
  268. package/.output/server/_libs/radix-ui__react-id.mjs +0 -14
  269. package/.output/server/_libs/radix-ui__react-label.mjs +0 -1
  270. package/.output/server/_libs/radix-ui__react-menu.mjs +0 -1
  271. package/.output/server/_libs/radix-ui__react-menubar.mjs +0 -1
  272. package/.output/server/_libs/radix-ui__react-popover.mjs +0 -1
  273. package/.output/server/_libs/radix-ui__react-popper.mjs +0 -340
  274. package/.output/server/_libs/radix-ui__react-portal.mjs +0 -16
  275. package/.output/server/_libs/radix-ui__react-presence.mjs +0 -173
  276. package/.output/server/_libs/radix-ui__react-primitive.mjs +0 -42
  277. package/.output/server/_libs/radix-ui__react-progress.mjs +0 -1
  278. package/.output/server/_libs/radix-ui__react-radio-group.mjs +0 -1
  279. package/.output/server/_libs/radix-ui__react-roving-focus.mjs +0 -235
  280. package/.output/server/_libs/radix-ui__react-scroll-area.mjs +0 -731
  281. package/.output/server/_libs/radix-ui__react-select.mjs +0 -1207
  282. package/.output/server/_libs/radix-ui__react-separator.mjs +0 -28
  283. package/.output/server/_libs/radix-ui__react-slider.mjs +0 -1
  284. package/.output/server/_libs/radix-ui__react-slot.mjs +0 -132
  285. package/.output/server/_libs/radix-ui__react-switch.mjs +0 -1
  286. package/.output/server/_libs/radix-ui__react-tabs.mjs +0 -194
  287. package/.output/server/_libs/radix-ui__react-toast.mjs +0 -2
  288. package/.output/server/_libs/radix-ui__react-toggle-group.mjs +0 -1
  289. package/.output/server/_libs/radix-ui__react-toggle.mjs +0 -1
  290. package/.output/server/_libs/radix-ui__react-toolbar.mjs +0 -1
  291. package/.output/server/_libs/radix-ui__react-tooltip.mjs +0 -498
  292. package/.output/server/_libs/radix-ui__react-use-previous.mjs +0 -14
  293. package/.output/server/_libs/radix-ui__react-use-size.mjs +0 -39
  294. package/.output/server/_libs/react-markdown.mjs +0 -235
  295. package/.output/server/_libs/react-remove-scroll-bar.mjs +0 -82
  296. package/.output/server/_libs/react-remove-scroll.mjs +0 -328
  297. package/.output/server/_libs/react-style-singleton.mjs +0 -69
  298. package/.output/server/_libs/remark-parse.mjs +0 -19
  299. package/.output/server/_libs/remark-rehype.mjs +0 -21
  300. package/.output/server/_libs/space-separated-tokens.mjs +0 -6
  301. package/.output/server/_libs/style-to-js.mjs +0 -72
  302. package/.output/server/_libs/style-to-object.mjs +0 -38
  303. package/.output/server/_libs/swr.mjs +0 -947
  304. package/.output/server/_libs/tailwind-merge.mjs +0 -3255
  305. package/.output/server/_libs/tanstack__react-virtual.mjs +0 -130
  306. package/.output/server/_libs/tanstack__virtual-core.mjs +0 -1319
  307. package/.output/server/_libs/three.mjs +0 -26016
  308. package/.output/server/_libs/trim-lines.mjs +0 -41
  309. package/.output/server/_libs/trough.mjs +0 -85
  310. package/.output/server/_libs/tslib.mjs +0 -1
  311. package/.output/server/_libs/ungap__structured-clone.mjs +0 -212
  312. package/.output/server/_libs/unified.mjs +0 -661
  313. package/.output/server/_libs/unist-util-is.mjs +0 -100
  314. package/.output/server/_libs/unist-util-position.mjs +0 -27
  315. package/.output/server/_libs/unist-util-stringify-position.mjs +0 -27
  316. package/.output/server/_libs/unist-util-visit-parents.mjs +0 -82
  317. package/.output/server/_libs/unist-util-visit.mjs +0 -24
  318. package/.output/server/_libs/use-callback-ref.mjs +0 -66
  319. package/.output/server/_libs/use-sidecar.mjs +0 -106
  320. package/.output/server/_libs/vfile-message.mjs +0 -138
  321. package/.output/server/_libs/vfile.mjs +0 -467
  322. package/.output/server/_sessionId-E_Dmopc-.mjs +0 -144
  323. package/.output/server/_sessionId-U54BAqdp.mjs +0 -82
  324. package/.output/server/_ssr/CompareDrawer-CksatPed.mjs +0 -1058
  325. package/.output/server/_ssr/InspectorPet-DNc4GQ7z.mjs +0 -936
  326. package/.output/server/_ssr/ProxyViewerContainer-B02Ey0p_.mjs +0 -17607
  327. package/.output/server/_ssr/ReplayDialog-DdMvHJF1.mjs +0 -723
  328. package/.output/server/_ssr/RequestAnatomy--vh2Z8__.mjs +0 -835
  329. package/.output/server/_ssr/ResponseView-Dhxc-cKC.mjs +0 -755
  330. package/.output/server/_ssr/StreamingChunkSequence-BG0EQmaD.mjs +0 -272
  331. package/.output/server/_ssr/index-D0At9nfd.mjs +0 -82
  332. package/.output/server/_ssr/index-lcfulIXa.mjs +0 -133
  333. package/.output/server/_ssr/json-viewer-My1J1_5R.mjs +0 -481
  334. package/.output/server/_ssr/start-HYkvq4Ni.mjs +0 -4
  335. package/.output/server/_tanstack-start-manifest_v-B2chR8ef.mjs +0 -4
  336. package/.output/server/node_modules/tslib/modules/index.js +0 -70
  337. package/.output/server/node_modules/tslib/modules/package.json +0 -3
  338. package/.output/server/node_modules/tslib/package.json +0 -47
  339. package/.output/server/node_modules/tslib/tslib.js +0 -484
  340. package/.output/server/package.json +0 -9
  341. package/src/routes/__root.tsx +0 -170
  342. /package/.output/{public → ui}/assets/agent-inspector-58K1_MsC.ico +0 -0
  343. /package/.output/{public → ui}/assets/alibaba-TTwafVwX.svg +0 -0
  344. /package/.output/{public → ui}/assets/minimax-BPMzvuL-.jpeg +0 -0
  345. /package/.output/{public → ui}/assets/qwen-mMn3f5ul.webp +0 -0
  346. /package/.output/{public → ui}/assets/zhipuai-BPNAnxo-.svg +0 -0
  347. /package/src/{routes → backend/routes}/livez.ts +0 -0
@@ -12,8 +12,28 @@ import {
12
12
 
13
13
  const DEFAULT_MEMORY_LIMIT_BYTES = 1024 * 1024;
14
14
  const FILE_WRITE_BATCH_BYTES = 64 * 1024;
15
+ const DEFAULT_QUEUE_MAX_MESSAGES = 256;
16
+ const DEFAULT_QUEUE_MAX_BYTES = 8 * 1024 * 1024;
17
+ const DEFAULT_GLOBAL_QUEUE_MAX_MESSAGES = 4096;
18
+ const DEFAULT_GLOBAL_QUEUE_MAX_BYTES = 64 * 1024 * 1024;
15
19
  const activeCaptures = new Set<RawStreamCapture>();
16
20
 
21
+ export type RawStreamCaptureGapReason = "capacity-exceeded" | "pipeline-closed" | "write-failed";
22
+
23
+ export type RawStreamCaptureStatus = {
24
+ incomplete: boolean;
25
+ droppedChunks: number;
26
+ droppedBytes: number;
27
+ reason: RawStreamCaptureGapReason | null;
28
+ };
29
+
30
+ export type RawStreamCaptureOptions = {
31
+ memoryLimitBytes?: number;
32
+ queueMaxMessages?: number;
33
+ queueMaxBytes?: number;
34
+ globalBudget?: RawStreamObservationBudget;
35
+ };
36
+
17
37
  export type RawStreamSource =
18
38
  | {
19
39
  type: "memory";
@@ -32,6 +52,62 @@ function resolveMemoryLimitBytes(): number {
32
52
  return parsed;
33
53
  }
34
54
 
55
+ function resolvePositiveIntegerEnv(name: string, fallback: number): number {
56
+ const raw = process.env[name];
57
+ if (raw === undefined || raw === "") return fallback;
58
+ const parsed = Number(raw);
59
+ if (!Number.isSafeInteger(parsed) || parsed < 1) return fallback;
60
+ return parsed;
61
+ }
62
+
63
+ function defaultQueueMaxMessages(): number {
64
+ return resolvePositiveIntegerEnv(
65
+ "AGENT_INSPECTOR_OBSERVATION_QUEUE_MESSAGES",
66
+ DEFAULT_QUEUE_MAX_MESSAGES,
67
+ );
68
+ }
69
+
70
+ function defaultQueueMaxBytes(): number {
71
+ return resolvePositiveIntegerEnv(
72
+ "AGENT_INSPECTOR_OBSERVATION_QUEUE_BYTES",
73
+ DEFAULT_QUEUE_MAX_BYTES,
74
+ );
75
+ }
76
+
77
+ export class RawStreamObservationBudget {
78
+ private messages = 0;
79
+ private bytes = 0;
80
+
81
+ constructor(
82
+ private readonly maxMessages: number,
83
+ private readonly maxBytes: number,
84
+ ) {}
85
+
86
+ tryAcquire(bytes: number): (() => void) | null {
87
+ if (this.messages + 1 > this.maxMessages || this.bytes + bytes > this.maxBytes) return null;
88
+ this.messages += 1;
89
+ this.bytes += bytes;
90
+ let released = false;
91
+ return () => {
92
+ if (released) return;
93
+ released = true;
94
+ this.messages -= 1;
95
+ this.bytes -= bytes;
96
+ };
97
+ }
98
+ }
99
+
100
+ const globalObservationBudget = new RawStreamObservationBudget(
101
+ resolvePositiveIntegerEnv(
102
+ "AGENT_INSPECTOR_OBSERVATION_GLOBAL_QUEUE_MESSAGES",
103
+ DEFAULT_GLOBAL_QUEUE_MAX_MESSAGES,
104
+ ),
105
+ resolvePositiveIntegerEnv(
106
+ "AGENT_INSPECTOR_OBSERVATION_GLOBAL_QUEUE_BYTES",
107
+ DEFAULT_GLOBAL_QUEUE_MAX_BYTES,
108
+ ),
109
+ );
110
+
35
111
  function getRawStreamDir(): string {
36
112
  return join(getDataDir(), "raw-streams");
37
113
  }
@@ -42,6 +118,9 @@ function createRawStreamPath(): string {
42
118
 
43
119
  export class RawStreamCapture {
44
120
  private readonly memoryLimitBytes: number;
121
+ private readonly queueMaxMessages: number;
122
+ private readonly queueMaxBytes: number;
123
+ private readonly globalBudget: RawStreamObservationBudget;
45
124
  private chunks: string[] = [];
46
125
  private byteLength = 0;
47
126
  private filePath: string | null = null;
@@ -49,11 +128,22 @@ export class RawStreamCapture {
49
128
  private pendingFileChunks: string[] = [];
50
129
  private pendingFileBytes = 0;
51
130
  private operation = Promise.resolve();
131
+ private accepting = true;
52
132
  private closed = false;
53
133
  private sawChunk = false;
134
+ private queuedMessages = 0;
135
+ private queuedBytes = 0;
136
+ private incomplete = false;
137
+ private storageFailed = false;
138
+ private droppedChunks = 0;
139
+ private droppedBytes = 0;
140
+ private gapReason: RawStreamCaptureGapReason | null = null;
54
141
 
55
- constructor(memoryLimitBytes: number = resolveMemoryLimitBytes()) {
56
- this.memoryLimitBytes = memoryLimitBytes;
142
+ constructor(options: RawStreamCaptureOptions = {}) {
143
+ this.memoryLimitBytes = options.memoryLimitBytes ?? resolveMemoryLimitBytes();
144
+ this.queueMaxMessages = options.queueMaxMessages ?? defaultQueueMaxMessages();
145
+ this.queueMaxBytes = options.queueMaxBytes ?? defaultQueueMaxBytes();
146
+ this.globalBudget = options.globalBudget ?? globalObservationBudget;
57
147
  activeCaptures.add(this);
58
148
  }
59
149
 
@@ -61,6 +151,21 @@ export class RawStreamCapture {
61
151
  return this.sawChunk;
62
152
  }
63
153
 
154
+ get status(): RawStreamCaptureStatus {
155
+ return {
156
+ incomplete: this.incomplete,
157
+ droppedChunks: this.droppedChunks,
158
+ droppedBytes: this.droppedBytes,
159
+ reason: this.gapReason,
160
+ };
161
+ }
162
+
163
+ recordDroppedObservation(reason: RawStreamCaptureGapReason, observedBytes: number): void {
164
+ const safeObservedBytes =
165
+ Number.isSafeInteger(observedBytes) && observedBytes >= 0 ? observedBytes : 0;
166
+ this.recordGap(reason, safeObservedBytes, safeObservedBytes > 0);
167
+ }
168
+
64
169
  async append(text: string): Promise<void> {
65
170
  if (text === "") return;
66
171
  const next = this.operation.then(() => this.appendInternal(text));
@@ -68,7 +173,63 @@ export class RawStreamCapture {
68
173
  return next;
69
174
  }
70
175
 
176
+ /**
177
+ * Submit an observation without waiting for file I/O. Once capacity is exceeded, capture remains
178
+ * incomplete and all later chunks are counted as dropped so the persisted value is an honest
179
+ * prefix rather than a misleading stream with an unmarked hole.
180
+ */
181
+ tryAppend(text: string, observedBytes: number): boolean {
182
+ const safeObservedBytes =
183
+ Number.isSafeInteger(observedBytes) && observedBytes >= 0 ? observedBytes : 0;
184
+ const queuedBytes = Math.max(safeObservedBytes, Buffer.byteLength(text, "utf-8"));
185
+
186
+ if (!this.accepting || this.closed) {
187
+ this.recordGap("pipeline-closed", safeObservedBytes, safeObservedBytes > 0 || text !== "");
188
+ return false;
189
+ }
190
+ if (this.incomplete) {
191
+ this.recordGap("capacity-exceeded", safeObservedBytes, safeObservedBytes > 0 || text !== "");
192
+ return false;
193
+ }
194
+ if (text === "") return true;
195
+ if (
196
+ this.queuedMessages + 1 > this.queueMaxMessages ||
197
+ this.queuedBytes + queuedBytes > this.queueMaxBytes
198
+ ) {
199
+ this.recordGap("capacity-exceeded", safeObservedBytes, true);
200
+ return false;
201
+ }
202
+
203
+ const releaseGlobal = this.globalBudget.tryAcquire(queuedBytes);
204
+ if (releaseGlobal === null) {
205
+ this.recordGap("capacity-exceeded", safeObservedBytes, true);
206
+ return false;
207
+ }
208
+
209
+ this.queuedMessages += 1;
210
+ this.queuedBytes += queuedBytes;
211
+ this.operation = this.operation
212
+ .then(async () => {
213
+ if (this.storageFailed) {
214
+ this.recordGap("write-failed", safeObservedBytes, true);
215
+ return;
216
+ }
217
+ await this.appendInternal(text);
218
+ })
219
+ .catch(() => {
220
+ this.storageFailed = true;
221
+ this.recordGap("write-failed", safeObservedBytes, true);
222
+ })
223
+ .finally(() => {
224
+ this.queuedMessages -= 1;
225
+ this.queuedBytes -= queuedBytes;
226
+ releaseGlobal();
227
+ });
228
+ return true;
229
+ }
230
+
71
231
  async finalize(): Promise<RawStreamSource> {
232
+ this.accepting = false;
72
233
  const next = this.operation.then(() => this.finalizeInternal());
73
234
  this.operation = next.then(
74
235
  () => undefined,
@@ -100,14 +261,29 @@ export class RawStreamCapture {
100
261
  }
101
262
  }
102
263
 
264
+ private recordGap(
265
+ reason: RawStreamCaptureGapReason,
266
+ observedBytes: number,
267
+ countChunk: boolean,
268
+ ): void {
269
+ this.incomplete = true;
270
+ this.gapReason ??= reason;
271
+ if (countChunk) this.droppedChunks += 1;
272
+ this.droppedBytes += observedBytes;
273
+ }
274
+
103
275
  private async finalizeInternal(): Promise<RawStreamSource> {
104
276
  if (!this.closed) {
105
277
  this.closed = true;
106
278
  await this.flushFileBuffer();
107
279
  if (this.fileHandle !== null) {
108
- await this.fileHandle.sync();
109
- await this.fileHandle.close();
280
+ const handle = this.fileHandle;
110
281
  this.fileHandle = null;
282
+ try {
283
+ await handle.sync();
284
+ } finally {
285
+ await handle.close();
286
+ }
111
287
  }
112
288
  }
113
289
  if (this.filePath !== null) {
@@ -120,16 +296,21 @@ export class RawStreamCapture {
120
296
  const path = createRawStreamPath();
121
297
  assert(ensurePrivateDirectorySync(getRawStreamDir()), "Could not secure raw-stream directory");
122
298
  const handle = await open(path, "wx", PRIVATE_FILE_MODE);
123
- const memoryText = this.chunks.join("");
124
- if (memoryText.length > 0) await handle.write(memoryText, undefined, "utf-8");
125
- if (!securePrivateFileSync(path)) {
126
- await handle.close();
127
- await unlink(path);
128
- assert(false, "Could not secure raw-stream file");
299
+ let committed = false;
300
+ try {
301
+ const memoryText = this.chunks.join("");
302
+ if (memoryText.length > 0) await handle.write(memoryText, undefined, "utf-8");
303
+ assert(securePrivateFileSync(path), "Could not secure raw-stream file");
304
+ this.filePath = path;
305
+ this.fileHandle = handle;
306
+ this.chunks = [];
307
+ committed = true;
308
+ } finally {
309
+ if (!committed) {
310
+ await handle.close().catch(() => undefined);
311
+ await unlink(path).catch(() => undefined);
312
+ }
129
313
  }
130
- this.filePath = path;
131
- this.fileHandle = handle;
132
- this.chunks = [];
133
314
  }
134
315
 
135
316
  private async flushFileBuffer(): Promise<void> {
@@ -1,3 +1,5 @@
1
+ import { MAX_ACTIVE_PROXY_REQUESTS } from "../lib/resourceLimits";
2
+
1
3
  export type RuntimeRequestLease = {
2
4
  release: () => void;
3
5
  };
@@ -13,7 +15,7 @@ function notifyDrained(): void {
13
15
  }
14
16
 
15
17
  export function acquireRuntimeRequest(): RuntimeRequestLease | null {
16
- if (!acceptingRequests) return null;
18
+ if (!acceptingRequests || activeRequests >= MAX_ACTIVE_PROXY_REQUESTS) return null;
17
19
  activeRequests += 1;
18
20
  let released = false;
19
21
  return {
@@ -41,8 +43,9 @@ export async function waitForRuntimeTrafficDrain(): Promise<void> {
41
43
  export function getRuntimeAdmissionState(): {
42
44
  acceptingRequests: boolean;
43
45
  activeRequests: number;
46
+ capacity: number;
44
47
  } {
45
- return { acceptingRequests, activeRequests };
48
+ return { acceptingRequests, activeRequests, capacity: MAX_ACTIVE_PROXY_REQUESTS };
46
49
  }
47
50
 
48
51
  export function _resetRuntimeAdmissionForTests(): void {
@@ -5,6 +5,9 @@ import { getDataDir } from "./dataDir";
5
5
  import { getFinalizerRuntimeHealth } from "./logFinalizer";
6
6
  import { ensurePrivateDirectorySync, PRIVATE_FILE_MODE } from "./privateDataPath";
7
7
  import { isSqliteLogIndexReady } from "./sqliteLogIndex";
8
+ import { recoverCapturedLogStorage } from "./jsonlRecovery";
9
+ import { resolveLogDir } from "./logger";
10
+ import { loadIndex } from "./logIndex";
8
11
 
9
12
  const DEFAULT_CACHE_MS = 2_000;
10
13
  const DEFAULT_MIN_DISK_HEADROOM_BYTES = 256 * 1024 * 1024;
@@ -118,7 +121,17 @@ function checkQueue(): Promise<ReadinessCheckLevel> {
118
121
  }
119
122
 
120
123
  async function checkIndex(): Promise<ReadinessCheckLevel> {
121
- return (await isSqliteLogIndexReady()) ? "ok" : "degraded";
124
+ const recovery = await recoverCapturedLogStorage(resolveLogDir());
125
+ if (recovery.level === "unavailable") return "unavailable";
126
+
127
+ try {
128
+ await loadIndex();
129
+ } catch {
130
+ return "unavailable";
131
+ }
132
+
133
+ const sqliteReady = await isSqliteLogIndexReady();
134
+ return recovery.level === "degraded" || !sqliteReady ? "degraded" : "ok";
122
135
  }
123
136
 
124
137
  function checkShutdown(): Promise<ReadinessCheckLevel> {
@@ -1,11 +1,25 @@
1
+ import { createHash, timingSafeEqual } from "node:crypto";
2
+ import {
3
+ parseManagedInstanceShutdownMessage,
4
+ readManagedInstanceLaunch,
5
+ } from "../lib/managedInstance";
1
6
  import { closeLogFinalizerWorkers, forceCloseLogFinalizerWorkers } from "./logFinalizer";
2
7
  import { flushIndex } from "./logIndex";
3
8
  import { flushApplicationLog, flushLogBuffer } from "./logger";
4
9
  import { flushAllRawStreamCaptures } from "./rawStreamCapture";
5
10
  import { setRuntimeShuttingDown } from "./runtimeHealth";
6
11
  import { stopRuntimeAdmission, waitForRuntimeTrafficDrain } from "./runtimeAdmission";
7
- import { createShutdownCoordinator, createShutdownSignalHandler } from "./shutdownCoordinator";
12
+ import {
13
+ createShutdownCoordinator,
14
+ createShutdownSignalHandler,
15
+ type ShutdownResult,
16
+ } from "./shutdownCoordinator";
8
17
  import { destroyAllSessionProcesses } from "./sessionProcess";
18
+ import {
19
+ beginEcosystemTaskShutdown,
20
+ forceCloseEcosystemTasks,
21
+ waitForEcosystemTasksToClose,
22
+ } from "./ecosystemTasks";
9
23
  import { flushSessionTasks } from "./sessionRuntime";
10
24
  import { closeSqliteLogIndex } from "./sqliteLogIndex";
11
25
  import { flushSessionArchiveWrites } from "./store";
@@ -36,6 +50,7 @@ async function flushPersistence(): Promise<void> {
36
50
  }
37
51
 
38
52
  async function closeResources(): Promise<void> {
53
+ await waitForEcosystemTasksToClose();
39
54
  await closeLogFinalizerWorkers();
40
55
  destroyAllSessionProcesses();
41
56
  closeSqliteLogIndex();
@@ -43,6 +58,7 @@ async function closeResources(): Promise<void> {
43
58
  }
44
59
 
45
60
  function forceCloseResources(): void {
61
+ forceCloseEcosystemTasks();
46
62
  forceCloseLogFinalizerWorkers();
47
63
  destroyAllSessionProcesses();
48
64
  closeSqliteLogIndex();
@@ -50,7 +66,10 @@ function forceCloseResources(): void {
50
66
 
51
67
  const runtimeShutdownCoordinator = createShutdownCoordinator(
52
68
  {
53
- markShuttingDown: () => setRuntimeShuttingDown(true),
69
+ markShuttingDown: () => {
70
+ setRuntimeShuttingDown(true);
71
+ beginEcosystemTaskShutdown();
72
+ },
54
73
  stopAdmission: stopRuntimeAdmission,
55
74
  drainTraffic: waitForRuntimeTrafficDrain,
56
75
  drainFinalizers,
@@ -62,9 +81,72 @@ const runtimeShutdownCoordinator = createShutdownCoordinator(
62
81
  );
63
82
 
64
83
  let signalHandlersInstalled = false;
84
+ let managedInstanceHandlersInstalled = false;
85
+
86
+ export type ManagedRuntimeShutdownController = {
87
+ handleDisconnect: () => void;
88
+ handleMessage: (message: unknown) => void;
89
+ };
90
+
91
+ type ManagedRuntimeShutdownControllerOptions = {
92
+ controlToken: string;
93
+ shutdown: () => Promise<ShutdownResult>;
94
+ exit: (code: number) => void;
95
+ };
96
+
97
+ function managedControlTokensMatch(received: string, expected: string): boolean {
98
+ if (received.length === 0 || expected.length === 0) return false;
99
+ const receivedDigest = createHash("sha256").update(received).digest();
100
+ const expectedDigest = createHash("sha256").update(expected).digest();
101
+ return timingSafeEqual(receivedDigest, expectedDigest);
102
+ }
103
+
104
+ export function createManagedRuntimeShutdownController(
105
+ options: ManagedRuntimeShutdownControllerOptions,
106
+ ): ManagedRuntimeShutdownController {
107
+ let shutdownRequested = false;
108
+ const requestShutdown = (): void => {
109
+ if (shutdownRequested) return;
110
+ shutdownRequested = true;
111
+ void options.shutdown().then(
112
+ (result) => options.exit(result.forced ? 1 : 0),
113
+ () => options.exit(1),
114
+ );
115
+ };
116
+ return {
117
+ handleDisconnect: requestShutdown,
118
+ handleMessage: (message) => {
119
+ const parsed = parseManagedInstanceShutdownMessage(message);
120
+ if (
121
+ parsed === null ||
122
+ !managedControlTokensMatch(parsed.controlToken, options.controlToken)
123
+ ) {
124
+ return;
125
+ }
126
+ requestShutdown();
127
+ },
128
+ };
129
+ }
130
+
131
+ /** Install lifecycle cleanup for the private supervisor/runtime IPC channel. */
132
+ export function installRuntimeManagedInstanceShutdownHandlers(): void {
133
+ if (managedInstanceHandlersInstalled) return;
134
+ const launchResult = readManagedInstanceLaunch(process.env);
135
+ if (launchResult.kind !== "managed") return;
136
+ managedInstanceHandlersInstalled = true;
137
+ const controller = createManagedRuntimeShutdownController({
138
+ controlToken: launchResult.launch.controlToken,
139
+ shutdown: async () => await runtimeShutdownCoordinator.shutdown("manual"),
140
+ exit: (code) => process.exit(code),
141
+ });
142
+ process.on("message", controller.handleMessage);
143
+ process.once("disconnect", controller.handleDisconnect);
144
+ if (process.connected === false) process.nextTick(controller.handleDisconnect);
145
+ }
65
146
 
66
147
  /** Install once in the server runtime; the CLI supervisor owns its own public-ingress signals. */
67
148
  export function installRuntimeShutdownSignalHandlers(): void {
149
+ installRuntimeManagedInstanceShutdownHandlers();
68
150
  if (signalHandlersInstalled) return;
69
151
  signalHandlersInstalled = true;
70
152
  const handleSignal = createShutdownSignalHandler(runtimeShutdownCoordinator, (result) => {
@@ -1,9 +1,15 @@
1
1
  import { z } from "zod";
2
2
 
3
- export { CapturedLogSchema, JsonValueSchema, StreamingChunkSchema } from "../contracts";
3
+ export {
4
+ CaptureIncompleteReasonSchema,
5
+ CapturedLogSchema,
6
+ JsonValueSchema,
7
+ StreamingChunkSchema,
8
+ } from "../contracts";
4
9
  export type {
5
10
  CapturedLog,
6
11
  ApiFormat,
12
+ CaptureIncompleteReason,
7
13
  JsonValue,
8
14
  StreamingChunk,
9
15
  TokenUsage,
@@ -40,6 +40,7 @@ ON CONFLICT(id) DO UPDATE SET
40
40
  `;
41
41
 
42
42
  let didLogUnavailable = false;
43
+ let cachedArchiveMax: { root: string; maxId: number } | null = null;
43
44
 
44
45
  export function getSessionArchiveRoot(): string {
45
46
  return join(resolveLogDir(), SESSION_ARCHIVE_DIR);
@@ -236,23 +237,49 @@ async function openSessionArchiveDb(filePath: string, create: boolean): Promise<
236
237
  }
237
238
  }
238
239
 
239
- export async function archiveSessionLog(log: CapturedLog): Promise<boolean> {
240
- const sessionId = log.sessionId;
241
- if (sessionId === null || sessionId.trim().length === 0) return false;
240
+ export async function archiveSessionLogs(logs: readonly CapturedLog[]): Promise<number> {
241
+ const bySession = new Map<string, CapturedLog[]>();
242
+ for (const log of logs) {
243
+ const sessionId = log.sessionId;
244
+ if (sessionId === null || sessionId.trim().length === 0) continue;
245
+ const existing = bySession.get(sessionId);
246
+ if (existing === undefined) bySession.set(sessionId, [log]);
247
+ else existing.push(log);
248
+ }
242
249
 
243
- const db = await openSessionArchiveDb(getSessionArchivePath(sessionId), true);
244
- if (db === null) return false;
250
+ let archivedCount = 0;
251
+ for (const [sessionId, sessionLogs] of bySession) {
252
+ const db = await openSessionArchiveDb(getSessionArchivePath(sessionId), true);
253
+ if (db === null || !execSql(db, "BEGIN IMMEDIATE")) {
254
+ if (db !== null) closeDatabase(db);
255
+ continue;
256
+ }
245
257
 
246
- try {
247
- return runPrepared(db, UPSERT_SQL, [
248
- log.id,
249
- sessionId,
250
- JSON.stringify(log),
251
- new Date().toISOString(),
252
- ]);
253
- } finally {
254
- closeDatabase(db);
258
+ let committed = false;
259
+ try {
260
+ const updatedAt = new Date().toISOString();
261
+ const archived = sessionLogs.every((log) =>
262
+ runPrepared(db, UPSERT_SQL, [log.id, sessionId, JSON.stringify(log), updatedAt]),
263
+ );
264
+ if (archived && execSql(db, "COMMIT")) {
265
+ committed = true;
266
+ archivedCount += sessionLogs.length;
267
+ if (cachedArchiveMax?.root === getSessionArchiveRoot()) {
268
+ for (const log of sessionLogs) {
269
+ cachedArchiveMax.maxId = Math.max(cachedArchiveMax.maxId, log.id);
270
+ }
271
+ }
272
+ }
273
+ } finally {
274
+ if (!committed) void execSql(db, "ROLLBACK");
275
+ closeDatabase(db);
276
+ }
255
277
  }
278
+ return archivedCount;
279
+ }
280
+
281
+ export async function archiveSessionLog(log: CapturedLog): Promise<boolean> {
282
+ return (await archiveSessionLogs([log])) === 1;
256
283
  }
257
284
 
258
285
  function readString(row: unknown, name: string): string | null {
@@ -343,6 +370,36 @@ async function listArchiveDatabasePaths(): Promise<string[]> {
343
370
  }
344
371
  }
345
372
 
373
+ /**
374
+ * Return the highest ID retained by an authoritative session archive. The global SQLite index is a
375
+ * rebuildable projection and must never influence ID allocation, while archive IDs must remain
376
+ * reserved even after their corresponding global JSONL records have aged out.
377
+ */
378
+ export async function getSessionArchiveMaxLogId(): Promise<number> {
379
+ const root = getSessionArchiveRoot();
380
+ if (cachedArchiveMax?.root === root) return cachedArchiveMax.maxId;
381
+
382
+ let maxId = 0;
383
+ const archivePaths = await listArchiveDatabasePaths();
384
+ for (const archivePath of archivePaths) {
385
+ const db = await openSessionArchiveDb(archivePath, false);
386
+ if (db === null) continue;
387
+
388
+ try {
389
+ const rows = allPrepared(db, "SELECT MAX(id) AS max_id FROM session_logs");
390
+ const archiveMax = readNumber(rows[0], "max_id");
391
+ if (archiveMax !== null && Number.isSafeInteger(archiveMax) && archiveMax >= 0) {
392
+ maxId = Math.max(maxId, archiveMax);
393
+ }
394
+ } finally {
395
+ closeDatabase(db);
396
+ }
397
+ }
398
+
399
+ cachedArchiveMax = { root, maxId };
400
+ return maxId;
401
+ }
402
+
346
403
  export async function findArchivedLogById(id: number): Promise<CapturedLog | null> {
347
404
  const archivePaths = await listArchiveDatabasePaths();
348
405
  for (const archivePath of archivePaths) {
@@ -405,6 +462,8 @@ export async function deleteArchivedLogsByIds(ids: ReadonlySet<number>): Promise
405
462
  }
406
463
  }
407
464
 
465
+ if (removedIds.size > 0) cachedArchiveMax = null;
466
+
408
467
  return removedIds;
409
468
  }
410
469
 
@@ -415,9 +474,14 @@ export async function clearSessionArchives(): Promise<number> {
415
474
  const archivePaths = await listArchiveDatabasePaths();
416
475
  try {
417
476
  await rm(root, { recursive: true, force: true });
477
+ cachedArchiveMax = null;
418
478
  return archivePaths.length;
419
479
  } catch (err) {
420
480
  logger.warn("[sessionArchive] Failed to clear session archives:", String(err));
421
481
  return 0;
422
482
  }
423
483
  }
484
+
485
+ export function _resetSessionArchiveCacheForTests(): void {
486
+ cachedArchiveMax = null;
487
+ }
@@ -1,5 +1,6 @@
1
1
  import type { SessionInfo, SessionLogSummary, SessionTokenUsage } from "../lib/sessionInfoContract";
2
- import { getConfiguredBasePath, withBasePath } from "../lib/basePath";
2
+ import { withBasePath } from "../lib/basePath";
3
+ import { getPublicBasePath } from "../lib/publicBasePathContext";
3
4
  import { getSessionPath } from "../lib/sessionUrl";
4
5
  import type { CapturedLog } from "./schemas";
5
6
  import type { SessionSnapshot } from "./sessionSupervisor";
@@ -106,14 +107,14 @@ function buildAbsoluteUrl(baseUrl: string, path: string): string {
106
107
 
107
108
  function buildApiPath(sessionId: string): string {
108
109
  return buildRelativePath(
109
- withBasePath("/api/sessions", getConfiguredBasePath(process.env)),
110
+ withBasePath("/api/sessions", getPublicBasePath()),
110
111
  new URLSearchParams({ sessionId }),
111
112
  );
112
113
  }
113
114
 
114
115
  function buildCompactLogsPath(sessionId: string): string {
115
116
  return buildRelativePath(
116
- withBasePath("/api/logs", getConfiguredBasePath(process.env)),
117
+ withBasePath("/api/logs", getPublicBasePath()),
117
118
  new URLSearchParams({ compact: "1", sessionId }),
118
119
  );
119
120
  }
@@ -211,7 +212,7 @@ export function buildSessionInfo(input: BuildSessionInfoInput): SessionInfo {
211
212
  const queuedTasks = input.snapshot?.queuedTasks ?? 0;
212
213
  const runningTasks = input.snapshot?.runningTasks ?? 0;
213
214
  const lastTaskError = normalizeString(input.snapshot?.lastTaskError);
214
- const inspectorPath = getSessionPath(input.sessionId, getConfiguredBasePath(process.env));
215
+ const inspectorPath = getSessionPath(input.sessionId, getPublicBasePath());
215
216
  const apiPath = buildApiPath(input.sessionId);
216
217
  const compactLogsPath = buildCompactLogsPath(input.sessionId);
217
218
  const latestLogs = summaries.slice(0, latestLogLimit).map((summary, index) => ({