@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
@@ -0,0 +1,554 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { Buffer } from "node:buffer";
3
+ import { constants, existsSync, lstatSync } from "node:fs";
4
+ import { link, lstat, open, readFile, readdir, stat, unlink } from "node:fs/promises";
5
+ import { basename, join, resolve } from "node:path";
6
+ import { CapturedLogSchema } from "./schemas";
7
+ import {
8
+ ensurePrivateDirectorySync,
9
+ PRIVATE_FILE_MODE,
10
+ securePrivateFileSync,
11
+ } from "./privateDataPath";
12
+
13
+ const READ_CHUNK_BYTES = 64 * 1024;
14
+ const QUARANTINE_DIRECTORY = "quarantine";
15
+
16
+ export type CapturedLogRecoveryLevel = "ok" | "degraded" | "unavailable";
17
+
18
+ export type CapturedLogRecoveryHealth = {
19
+ level: CapturedLogRecoveryLevel;
20
+ diagnostic: string | null;
21
+ repairedFiles: number;
22
+ delimiterRepairs: number;
23
+ quarantinedTails: number;
24
+ };
25
+
26
+ type FileSignature = {
27
+ size: number;
28
+ modifiedAt: number;
29
+ changedAt: number;
30
+ device: number;
31
+ inode: number;
32
+ };
33
+
34
+ type RecoveryContext = {
35
+ root: string;
36
+ health: CapturedLogRecoveryHealth;
37
+ startup: Promise<CapturedLogRecoveryHealth> | null;
38
+ startupComplete: boolean;
39
+ safeFiles: Map<string, FileSignature>;
40
+ };
41
+
42
+ type RecordValidation =
43
+ | { valid: true }
44
+ | { valid: false; reason: "invalid-json" | "invalid-log-schema" };
45
+
46
+ type InvalidRecordReason = "invalid-json" | "invalid-log-schema";
47
+
48
+ type FileRecoveryResult =
49
+ | { status: "clean"; signature: FileSignature }
50
+ | { status: "delimiter-appended"; signature: FileSignature }
51
+ | { status: "tail-quarantined"; signature: FileSignature }
52
+ | {
53
+ status: "interior-corruption";
54
+ line: number;
55
+ byteOffset: number;
56
+ recordHash: string;
57
+ reason: InvalidRecordReason;
58
+ }
59
+ | { status: "failed"; reason: string };
60
+
61
+ type ScanResult =
62
+ | {
63
+ status: "complete";
64
+ pending: Buffer;
65
+ pendingOffset: number;
66
+ nextLine: number;
67
+ }
68
+ | {
69
+ status: "interior-corruption";
70
+ line: number;
71
+ byteOffset: number;
72
+ recordHash: string;
73
+ reason: InvalidRecordReason;
74
+ }
75
+ | { status: "failed"; reason: string };
76
+
77
+ type QuarantineRecord = {
78
+ schemaVersion: 1;
79
+ reason: "invalid-final-jsonl-record";
80
+ sourceFile: string;
81
+ sourcePathHash: string;
82
+ recoveredAt: string;
83
+ tailOffset: number;
84
+ tailBytes: number;
85
+ tailSha256: string;
86
+ tailBase64: string;
87
+ };
88
+
89
+ type ExistingQuarantineState = "missing" | "matching" | "conflicting";
90
+
91
+ let activeContext: RecoveryContext | null = null;
92
+
93
+ function emptyHealth(): CapturedLogRecoveryHealth {
94
+ return {
95
+ level: "ok",
96
+ diagnostic: null,
97
+ repairedFiles: 0,
98
+ delimiterRepairs: 0,
99
+ quarantinedTails: 0,
100
+ };
101
+ }
102
+
103
+ function contextFor(logDir: string): RecoveryContext {
104
+ const root = resolve(logDir);
105
+ if (activeContext === null || activeContext.root !== root) {
106
+ activeContext = {
107
+ root,
108
+ health: emptyHealth(),
109
+ startup: null,
110
+ startupComplete: false,
111
+ safeFiles: new Map(),
112
+ };
113
+ }
114
+ return activeContext;
115
+ }
116
+
117
+ function copyHealth(health: CapturedLogRecoveryHealth): CapturedLogRecoveryHealth {
118
+ return { ...health };
119
+ }
120
+
121
+ function hashBytes(value: Uint8Array): string {
122
+ return createHash("sha256").update(value).digest("hex");
123
+ }
124
+
125
+ function signatureFromStats(stats: {
126
+ size: number;
127
+ mtimeMs: number;
128
+ ctimeMs: number;
129
+ dev: number;
130
+ ino: number;
131
+ }): FileSignature {
132
+ return {
133
+ size: stats.size,
134
+ modifiedAt: stats.mtimeMs,
135
+ changedAt: stats.ctimeMs,
136
+ device: stats.dev,
137
+ inode: stats.ino,
138
+ };
139
+ }
140
+
141
+ function signaturesMatch(left: FileSignature, right: FileSignature): boolean {
142
+ return (
143
+ left.size === right.size &&
144
+ left.modifiedAt === right.modifiedAt &&
145
+ left.changedAt === right.changedAt &&
146
+ left.device === right.device &&
147
+ left.inode === right.inode
148
+ );
149
+ }
150
+
151
+ function validateRecord(bytes: Buffer): RecordValidation {
152
+ const withoutCarriageReturn =
153
+ bytes.length > 0 && bytes[bytes.length - 1] === 0x0d ? bytes.subarray(0, -1) : bytes;
154
+ const text = withoutCarriageReturn.toString("utf-8");
155
+ if (text.trim() === "") return { valid: true };
156
+
157
+ let parsed: unknown;
158
+ try {
159
+ parsed = JSON.parse(text);
160
+ } catch {
161
+ return { valid: false, reason: "invalid-json" };
162
+ }
163
+ return CapturedLogSchema.safeParse(parsed).success
164
+ ? { valid: true }
165
+ : { valid: false, reason: "invalid-log-schema" };
166
+ }
167
+
168
+ async function scanFile(
169
+ handle: Awaited<ReturnType<typeof open>>,
170
+ size: number,
171
+ ): Promise<ScanResult> {
172
+ const readBuffer = Buffer.alloc(READ_CHUNK_BYTES);
173
+ let position = 0;
174
+ let pending = Buffer.alloc(0);
175
+ let pendingOffset = 0;
176
+ let line = 1;
177
+
178
+ while (position < size) {
179
+ const length = Math.min(READ_CHUNK_BYTES, size - position);
180
+ let bytesRead: number;
181
+ try {
182
+ const result = await handle.read(readBuffer, 0, length, position);
183
+ bytesRead = result.bytesRead;
184
+ } catch (error) {
185
+ return { status: "failed", reason: `read failed: ${String(error)}` };
186
+ }
187
+ if (bytesRead === 0) {
188
+ return { status: "failed", reason: "captured-log file changed while being scanned" };
189
+ }
190
+
191
+ const chunk = readBuffer.subarray(0, bytesRead);
192
+ const combined = pending.length === 0 ? Buffer.from(chunk) : Buffer.concat([pending, chunk]);
193
+ let consumed = 0;
194
+ let newline = combined.indexOf(0x0a, consumed);
195
+ while (newline !== -1) {
196
+ const record = combined.subarray(consumed, newline);
197
+ const validation = validateRecord(record);
198
+ if (!validation.valid) {
199
+ return {
200
+ status: "interior-corruption",
201
+ line,
202
+ byteOffset: pendingOffset + consumed,
203
+ recordHash: hashBytes(record),
204
+ reason: validation.reason,
205
+ };
206
+ }
207
+ consumed = newline + 1;
208
+ line += 1;
209
+ newline = combined.indexOf(0x0a, consumed);
210
+ }
211
+
212
+ pending = Buffer.from(combined.subarray(consumed));
213
+ pendingOffset += consumed;
214
+ position += bytesRead;
215
+ }
216
+
217
+ return { status: "complete", pending, pendingOffset, nextLine: line };
218
+ }
219
+
220
+ async function syncDirectory(directory: string): Promise<void> {
221
+ let handle: Awaited<ReturnType<typeof open>> | null = null;
222
+ try {
223
+ handle = await open(directory, "r");
224
+ await handle.sync();
225
+ } catch {
226
+ // Directory fsync is unavailable on some supported Windows filesystems.
227
+ } finally {
228
+ await handle?.close().catch(() => undefined);
229
+ }
230
+ }
231
+
232
+ async function removeTemporaryFile(filePath: string): Promise<void> {
233
+ await unlink(filePath).catch(() => undefined);
234
+ }
235
+
236
+ function readOwnProperty(value: unknown, name: string): unknown {
237
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
238
+ return Object.getOwnPropertyDescriptor(value, name)?.value;
239
+ }
240
+
241
+ async function inspectExistingQuarantine(
242
+ filePath: string,
243
+ expected: QuarantineRecord,
244
+ ): Promise<ExistingQuarantineState> {
245
+ if (!existsSync(filePath)) return "missing";
246
+ try {
247
+ const parsed: unknown = JSON.parse(await readFile(filePath, "utf-8"));
248
+ return readOwnProperty(parsed, "schemaVersion") === expected.schemaVersion &&
249
+ readOwnProperty(parsed, "reason") === expected.reason &&
250
+ readOwnProperty(parsed, "sourceFile") === expected.sourceFile &&
251
+ readOwnProperty(parsed, "sourcePathHash") === expected.sourcePathHash &&
252
+ readOwnProperty(parsed, "tailOffset") === expected.tailOffset &&
253
+ readOwnProperty(parsed, "tailBytes") === expected.tailBytes &&
254
+ readOwnProperty(parsed, "tailSha256") === expected.tailSha256 &&
255
+ readOwnProperty(parsed, "tailBase64") === expected.tailBase64
256
+ ? "matching"
257
+ : "conflicting";
258
+ } catch {
259
+ return "conflicting";
260
+ }
261
+ }
262
+
263
+ async function quarantineTail(
264
+ context: RecoveryContext,
265
+ sourceFile: string,
266
+ tail: Buffer,
267
+ tailOffset: number,
268
+ ): Promise<string | null> {
269
+ const quarantineDirectory = join(context.root, QUARANTINE_DIRECTORY);
270
+ if (!ensurePrivateDirectorySync(quarantineDirectory)) return null;
271
+
272
+ const tailSha256 = hashBytes(tail);
273
+ const sourceFileName = basename(sourceFile);
274
+ const destination = join(quarantineDirectory, `${sourceFileName}.tail-${tailSha256}.json`);
275
+ const temporary = join(
276
+ quarantineDirectory,
277
+ `.${sourceFileName}.${String(process.pid)}.${randomUUID()}.tmp`,
278
+ );
279
+ const record: QuarantineRecord = {
280
+ schemaVersion: 1,
281
+ reason: "invalid-final-jsonl-record",
282
+ sourceFile: sourceFileName,
283
+ sourcePathHash: hashBytes(Buffer.from(resolve(sourceFile), "utf-8")),
284
+ recoveredAt: new Date().toISOString(),
285
+ tailOffset,
286
+ tailBytes: tail.length,
287
+ tailSha256,
288
+ tailBase64: tail.toString("base64"),
289
+ };
290
+
291
+ const existing = await inspectExistingQuarantine(destination, record);
292
+ if (existing === "matching") {
293
+ return securePrivateFileSync(destination) ? destination : null;
294
+ }
295
+ if (existing === "conflicting") return null;
296
+
297
+ let handle: Awaited<ReturnType<typeof open>> | null = null;
298
+ try {
299
+ handle = await open(temporary, "wx", PRIVATE_FILE_MODE);
300
+ await handle.writeFile(`${JSON.stringify(record)}\n`, "utf-8");
301
+ await handle.sync();
302
+ await handle.close();
303
+ handle = null;
304
+ if (!securePrivateFileSync(temporary)) {
305
+ await removeTemporaryFile(temporary);
306
+ return null;
307
+ }
308
+ // A hard-link install has create-if-absent semantics on every supported platform. Unlike
309
+ // rename(), it cannot overwrite a conflicting quarantine artifact that appears after the
310
+ // preflight check on POSIX.
311
+ await link(temporary, destination);
312
+ await unlink(temporary);
313
+ if (!securePrivateFileSync(destination)) return null;
314
+ await syncDirectory(quarantineDirectory);
315
+ return destination;
316
+ } catch {
317
+ await handle?.close().catch(() => undefined);
318
+ await removeTemporaryFile(temporary);
319
+ const racedExisting = await inspectExistingQuarantine(destination, record);
320
+ return racedExisting === "matching" && securePrivateFileSync(destination) ? destination : null;
321
+ }
322
+ }
323
+
324
+ async function recoverFile(
325
+ context: RecoveryContext,
326
+ filePath: string,
327
+ ): Promise<FileRecoveryResult> {
328
+ if (!existsSync(filePath)) {
329
+ context.safeFiles.delete(filePath);
330
+ return {
331
+ status: "clean",
332
+ signature: { size: 0, modifiedAt: 0, changedAt: 0, device: 0, inode: 0 },
333
+ };
334
+ }
335
+
336
+ let fileStats: Awaited<ReturnType<typeof lstat>>;
337
+ try {
338
+ fileStats = await lstat(filePath);
339
+ } catch (error) {
340
+ return { status: "failed", reason: `stat failed: ${String(error)}` };
341
+ }
342
+ if (fileStats.isSymbolicLink() || !fileStats.isFile()) {
343
+ return { status: "failed", reason: "captured-log path is not a regular private file" };
344
+ }
345
+
346
+ const initialSignature = signatureFromStats(fileStats);
347
+ const safeSignature = context.safeFiles.get(filePath);
348
+ if (safeSignature !== undefined && signaturesMatch(safeSignature, initialSignature)) {
349
+ return { status: "clean", signature: initialSignature };
350
+ }
351
+
352
+ let handle: Awaited<ReturnType<typeof open>> | null = null;
353
+ try {
354
+ handle = await open(filePath, constants.O_RDWR | constants.O_NOFOLLOW);
355
+ const openedStats = await handle.stat();
356
+ if (!openedStats.isFile()) {
357
+ return { status: "failed", reason: "captured-log handle is not a regular file" };
358
+ }
359
+ const openedSignature = signatureFromStats(openedStats);
360
+ if (!signaturesMatch(initialSignature, openedSignature)) {
361
+ return {
362
+ status: "failed",
363
+ reason: "captured-log path changed between metadata validation and open",
364
+ };
365
+ }
366
+ const scan = await scanFile(handle, openedStats.size);
367
+ const scannedSignature = signatureFromStats(await handle.stat());
368
+ if (!signaturesMatch(openedSignature, scannedSignature)) {
369
+ return {
370
+ status: "failed",
371
+ reason: "captured-log file changed while recovery was scanning it",
372
+ };
373
+ }
374
+ switch (scan.status) {
375
+ case "failed":
376
+ return scan;
377
+ case "interior-corruption":
378
+ return scan;
379
+ case "complete": {
380
+ if (scan.pending.length === 0) {
381
+ return { status: "clean", signature: signatureFromStats(await handle.stat()) };
382
+ }
383
+
384
+ const validation = validateRecord(scan.pending);
385
+ if (validation.valid) {
386
+ const write = await handle.write(Buffer.from("\n", "utf-8"), 0, 1, openedStats.size);
387
+ if (write.bytesWritten !== 1) {
388
+ return { status: "failed", reason: "captured-log delimiter append was incomplete" };
389
+ }
390
+ await handle.sync();
391
+ return {
392
+ status: "delimiter-appended",
393
+ signature: signatureFromStats(await handle.stat()),
394
+ };
395
+ }
396
+
397
+ const quarantinePath = await quarantineTail(
398
+ context,
399
+ filePath,
400
+ scan.pending,
401
+ scan.pendingOffset,
402
+ );
403
+ if (quarantinePath === null) {
404
+ return { status: "failed", reason: "invalid tail could not be privately quarantined" };
405
+ }
406
+ const beforeTruncate = signatureFromStats(await handle.stat());
407
+ if (!signaturesMatch(openedSignature, beforeTruncate)) {
408
+ return {
409
+ status: "failed",
410
+ reason: "captured-log file changed before invalid-tail truncation",
411
+ };
412
+ }
413
+ await handle.truncate(scan.pendingOffset);
414
+ await handle.sync();
415
+ return {
416
+ status: "tail-quarantined",
417
+ signature: signatureFromStats(await handle.stat()),
418
+ };
419
+ }
420
+ }
421
+ return { status: "failed", reason: "unsupported captured-log scan state" };
422
+ } catch (error) {
423
+ return { status: "failed", reason: String(error) };
424
+ } finally {
425
+ await handle?.close().catch(() => undefined);
426
+ }
427
+ }
428
+
429
+ function applyFileResult(
430
+ context: RecoveryContext,
431
+ filePath: string,
432
+ result: FileRecoveryResult,
433
+ ): void {
434
+ const file = basename(filePath);
435
+ switch (result.status) {
436
+ case "clean":
437
+ context.safeFiles.set(filePath, result.signature);
438
+ return;
439
+ case "delimiter-appended":
440
+ context.safeFiles.set(filePath, result.signature);
441
+ context.health.delimiterRepairs += 1;
442
+ context.health.repairedFiles += 1;
443
+ return;
444
+ case "tail-quarantined":
445
+ context.safeFiles.set(filePath, result.signature);
446
+ context.health.quarantinedTails += 1;
447
+ context.health.repairedFiles += 1;
448
+ if (context.health.level !== "unavailable") {
449
+ context.health.level = "degraded";
450
+ context.health.diagnostic = `Recovered and quarantined an invalid JSONL tail in ${file}`;
451
+ }
452
+ return;
453
+ case "interior-corruption":
454
+ context.safeFiles.delete(filePath);
455
+ context.health.level = "unavailable";
456
+ context.health.diagnostic =
457
+ `JSONL corruption in ${file} at line ${String(result.line)} ` +
458
+ `(offset ${String(result.byteOffset)}, ${result.reason}, sha256 ${result.recordHash})`;
459
+ return;
460
+ case "failed":
461
+ context.safeFiles.delete(filePath);
462
+ context.health.level = "unavailable";
463
+ context.health.diagnostic = `JSONL recovery failed for ${file}: ${result.reason}`;
464
+ }
465
+ }
466
+
467
+ async function runStartupRecovery(context: RecoveryContext): Promise<CapturedLogRecoveryHealth> {
468
+ if (!ensurePrivateDirectorySync(context.root)) {
469
+ context.health.level = "unavailable";
470
+ context.health.diagnostic = "Captured-log directory could not be made private";
471
+ return copyHealth(context.health);
472
+ }
473
+
474
+ let entries: string[];
475
+ try {
476
+ entries = await readdir(context.root);
477
+ } catch (error) {
478
+ context.health.level = "unavailable";
479
+ context.health.diagnostic = `Captured-log directory could not be listed: ${String(error)}`;
480
+ return copyHealth(context.health);
481
+ }
482
+
483
+ const files = entries.filter((entry) => entry.endsWith(".jsonl")).sort();
484
+ for (const file of files) {
485
+ const filePath = join(context.root, file);
486
+ const result = await recoverFile(context, filePath);
487
+ applyFileResult(context, filePath, result);
488
+ if (context.health.level === "unavailable") break;
489
+ }
490
+ return copyHealth(context.health);
491
+ }
492
+
493
+ /** Recover all captured-log ledgers once before startup reads, indexing, readiness, or appends. */
494
+ export async function recoverCapturedLogStorage(
495
+ logDir: string,
496
+ ): Promise<CapturedLogRecoveryHealth> {
497
+ const context = contextFor(logDir);
498
+ if (context.startup === null) {
499
+ context.startup = runStartupRecovery(context).then((health) => {
500
+ context.startupComplete = true;
501
+ return health;
502
+ });
503
+ }
504
+ return await context.startup;
505
+ }
506
+
507
+ /** Re-check a ledger immediately before appending; unchanged files use a stat-only fast path. */
508
+ export async function recoverCapturedLogFileBeforeAppend(
509
+ logDir: string,
510
+ filePath: string,
511
+ ): Promise<CapturedLogRecoveryHealth> {
512
+ const context = contextFor(logDir);
513
+ await recoverCapturedLogStorage(logDir);
514
+ if (context.health.level === "unavailable") return copyHealth(context.health);
515
+
516
+ const result = await recoverFile(context, filePath);
517
+ applyFileResult(context, filePath, result);
518
+ return copyHealth(context.health);
519
+ }
520
+
521
+ /** Refresh the trusted signature after this process successfully appends a complete JSONL record. */
522
+ export async function markCapturedLogFileAppended(logDir: string, filePath: string): Promise<void> {
523
+ const context = contextFor(logDir);
524
+ try {
525
+ const fileStats = await stat(filePath);
526
+ context.safeFiles.set(filePath, signatureFromStats(fileStats));
527
+ } catch {
528
+ context.safeFiles.delete(filePath);
529
+ }
530
+ }
531
+
532
+ export function getCapturedLogRecoveryHealth(logDir: string): CapturedLogRecoveryHealth {
533
+ return copyHealth(contextFor(logDir).health);
534
+ }
535
+
536
+ /** Fail-closed guard for the process-exit writer, where asynchronous recovery is unavailable. */
537
+ export function canAppendCapturedLogFileOnExit(logDir: string, filePath: string): boolean {
538
+ const context = contextFor(logDir);
539
+ if (!context.startupComplete || context.health.level === "unavailable") return false;
540
+ const trusted = context.safeFiles.get(filePath);
541
+ if (trusted === undefined) return false;
542
+
543
+ try {
544
+ const observed = lstatSync(filePath);
545
+ if (observed === undefined || observed.isSymbolicLink() || !observed.isFile()) return false;
546
+ return signaturesMatch(trusted, signatureFromStats(observed));
547
+ } catch {
548
+ return false;
549
+ }
550
+ }
551
+
552
+ export function _resetCapturedLogRecoveryForTests(): void {
553
+ activeContext = null;
554
+ }
@@ -13,7 +13,7 @@ import {
13
13
  getSessionProcessPendingCount,
14
14
  isSessionProcessAvailable,
15
15
  } from "./sessionProcess";
16
- import { finalizeLogUpdate } from "./store";
16
+ import { persistFinalizedLogUpdate } from "./store";
17
17
  import { computeTokensPerSecond } from "./streamTiming";
18
18
  import { analyzeToolSchemaWarnings } from "./toolSchemaWarnings";
19
19
  import { isPlainRecord, safeGetOwnProperty } from "../lib/objectUtils";
@@ -37,6 +37,7 @@ export type FinalizeStreamingLogJob = BaseFinalizeLogJob & {
37
37
  collectStreamingChunks: boolean;
38
38
  firstChunkMs: number | null;
39
39
  totalStreamMs: number;
40
+ terminalError?: string | null;
40
41
  };
41
42
 
42
43
  export type FinalizeStreamAbortLogJob = BaseFinalizeLogJob & {
@@ -99,6 +100,10 @@ export function buildFileLogEntry(log: CapturedLog, upstreamUrl: string): Record
99
100
  clientProjectFolder: log.clientProjectFolder,
100
101
  streamingChunks: log.streamingChunks,
101
102
  streamingChunksPath: log.streamingChunksPath,
103
+ captureIncomplete: log.captureIncomplete ?? false,
104
+ droppedChunks: log.droppedChunks ?? 0,
105
+ droppedBytes: log.droppedBytes ?? 0,
106
+ captureIncompleteReason: log.captureIncompleteReason ?? null,
102
107
  warnings: log.warnings,
103
108
  error: log.error,
104
109
  };
@@ -198,7 +203,7 @@ async function runToolSchemaWarningAnalysis(job: ToolSchemaWarningAnalysisJob):
198
203
  if (updatedLog === null) return;
199
204
 
200
205
  appendLogEntry({ ...buildFileLogEntry(updatedLog, job.upstreamUrl), error: job.error });
201
- finalizeLogUpdate(updatedLog);
206
+ await persistFinalizedLogUpdate(updatedLog);
202
207
  }
203
208
 
204
209
  function scheduleToolSchemaWarningAnalysis(job: ToolSchemaWarningAnalysisJob): void {
@@ -455,8 +460,13 @@ export function buildFinalizeLogResult(job: FinalizeLogJob): FinalizeLogResult {
455
460
  switch (job.type) {
456
461
  case "non-streaming":
457
462
  return finalizeNonStreaming(job, log);
458
- case "streaming":
459
- return finalizeStreaming(job, log);
463
+ case "streaming": {
464
+ const result = finalizeStreaming(job, log);
465
+ if (job.terminalError !== undefined && job.terminalError !== null) {
466
+ result.log.error = job.terminalError;
467
+ }
468
+ return result;
469
+ }
460
470
  case "stream-abort":
461
471
  return finalizeStreamAbort(job, log);
462
472
  default: {
@@ -472,13 +482,18 @@ export function buildFinalizeLogResult(job: FinalizeLogJob): FinalizeLogResult {
472
482
  }
473
483
  }
474
484
 
475
- export function commitFinalizeLogResult(result: FinalizeLogResult): void {
476
- appendLogEntry({ ...buildFileLogEntry(result.log, result.upstreamUrl), error: result.error });
477
- finalizeLogUpdate(result.log);
485
+ export async function commitFinalizeLogResult(result: FinalizeLogResult): Promise<void> {
486
+ const persistedError =
487
+ result.log.captureIncompleteReason === "upstream-stream-error"
488
+ ? (result.log.error ?? result.error ?? null)
489
+ : (result.error ?? result.log.error ?? null);
490
+ result.log.error = persistedError;
491
+ appendLogEntry({ ...buildFileLogEntry(result.log, result.upstreamUrl), error: persistedError });
492
+ await persistFinalizedLogUpdate(result.log);
478
493
  scheduleToolSchemaWarningAnalysis({
479
494
  log: cloneLog(result.log),
480
495
  upstreamUrl: result.upstreamUrl,
481
- error: result.error,
496
+ error: persistedError,
482
497
  });
483
498
  if (result.cleanupRawStreamPath !== undefined && result.cleanupRawStreamPath !== null) {
484
499
  try {
@@ -665,8 +680,8 @@ export function executeBuildInWorker(job: FinalizeLogJob): Promise<FinalizeLogRe
665
680
  }
666
681
 
667
682
  export function executeFinalizeLogJob(job: FinalizeLogJob): Promise<void> {
668
- return resolveBuildPromise(job).then((result) => {
669
- commitFinalizeLogResult(result);
683
+ return resolveBuildPromise(job).then(async (result) => {
684
+ await commitFinalizeLogResult(result);
670
685
  if (result.error !== null && result.error !== "Client aborted") {
671
686
  return Promise.reject(new Error(result.error));
672
687
  }
@@ -4,6 +4,7 @@ import JSZip from "jszip";
4
4
  import { z } from "zod";
5
5
  import {
6
6
  CapturedLogSchema,
7
+ LogIdSchema,
7
8
  StreamingChunkSchema,
8
9
  type ApiFormat,
9
10
  type CapturedLog,
@@ -58,7 +59,7 @@ const ManifestSchema = z
58
59
  exportedAt: z.string().optional(),
59
60
  mode: z.string().optional(),
60
61
  redacted: z.boolean().optional(),
61
- logIds: z.array(z.number().int().positive()).optional(),
62
+ logIds: z.array(LogIdSchema).optional(),
62
63
  })
63
64
  .passthrough();
64
65