@tonyclaw/agent-inspector 3.0.45 → 3.0.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (343) hide show
  1. package/.output/{nitro.json → backend/nitro.json} +3 -3
  2. package/.output/backend-public/assets/index-D4LX-sw-.js +11 -0
  3. package/.output/cli.js +8018 -5451
  4. package/.output/server/_libs/extend.mjs +4 -8
  5. package/.output/server/_libs/jszip.mjs +9 -35
  6. package/.output/server/_libs/react-dom.mjs +2 -3
  7. package/.output/server/_libs/react.mjs +1 -27
  8. package/.output/server/_libs/tanstack__react-router.mjs +1 -329
  9. package/.output/server/_libs/tanstack__router-core.mjs +42 -100
  10. package/.output/server/_libs/use-sync-external-store.mjs +1 -64
  11. package/.output/server/_libs/ws.mjs +1 -1
  12. package/.output/server/_ssr/index.mjs +4 -4
  13. package/.output/server/_ssr/{publicBasePathContext-5264L3rX.mjs → publicBasePathContext-IH2ilOfu.mjs} +52 -24
  14. package/.output/server/_ssr/{router-BJ_ltLim.mjs → router-BwZEsYfS.mjs} +19794 -19444
  15. package/.output/server/_ssr/{start-C6MjPjxp.mjs → start-COQu_AgD.mjs} +1 -1
  16. package/.output/server/_tanstack-start-manifest_v-DnbdNeun.mjs +4 -0
  17. package/.output/server/index.mjs +5 -145
  18. package/.output/{public/assets/CompareDrawer-B3_TLTJh.js → ui/assets/CompareDrawer-V-8lPASC.js} +1 -1
  19. package/.output/{public/assets/InspectorPet-Cpn6hv4P.js → ui/assets/InspectorPet-D4CVnfdf.js} +1 -1
  20. package/.output/ui/assets/ProxyViewerContainer-CO3ZVHLX.js +59 -0
  21. package/.output/{public/assets/ReplayDialog-B3EKVdJk.js → ui/assets/ReplayDialog-CpGyTLtt.js} +1 -1
  22. package/.output/{public/assets/RequestAnatomy-COOQjyUC.js → ui/assets/RequestAnatomy-BLuJL9dZ.js} +1 -1
  23. package/.output/ui/assets/ResponseView-BuSC-CH7.js +2 -0
  24. package/.output/ui/assets/StreamingChunkSequence-DeA-JJKa.js +1 -0
  25. package/.output/{public/assets/_sessionId-zbgYl7ZJ.js → ui/assets/_sessionId-8HuHFhjo.js} +1 -1
  26. package/.output/{public/assets/_sessionId-BJlM3nmt.js → ui/assets/_sessionId-Chp67th6.js} +1 -1
  27. package/.output/ui/assets/favicon-DYEkHSNl.svg +21 -0
  28. package/.output/ui/assets/index-BvXp42al.css +1 -0
  29. package/.output/{public/assets/index-xF_NY8Yb.js → ui/assets/index-DPmKK_5d.js} +5 -5
  30. package/.output/{public/assets/index-BIK4bPTv.js → ui/assets/index-atoQrlb8.js} +1 -1
  31. package/.output/ui/assets/index-nY3E1vxV.js +73 -0
  32. package/.output/{public/assets/index-D2Qw6Ri2.js → ui/assets/index-nl9-dpZn.js} +1 -1
  33. package/.output/ui/assets/json-viewer-B9kIzqeX.js +1 -0
  34. package/.output/{public/assets/jszip.min-B_rfAUCe.js → ui/assets/jszip.min-B7tfyE-I.js} +1 -1
  35. package/.output/ui/index.html +229 -0
  36. package/.output/ui/runtime-config.js +2 -0
  37. package/.output/workers/logFinalizer.worker.js +979 -414
  38. package/.output/workers/sessionWorkerEntry.js +979 -414
  39. package/README.md +182 -43
  40. package/docs/instance-control.openapi.yaml +59 -4
  41. package/package.json +40 -6
  42. package/scripts/generate-theme-scales.mjs +3 -3
  43. package/src/{router.tsx → backend/router.tsx} +3 -15
  44. package/src/backend/routes/$.ts +26 -0
  45. package/src/backend/routes/__root.ts +4 -0
  46. package/src/backend/routes/api/-instances.ts +7 -0
  47. package/src/{routes → backend/routes}/api/alerts.summary.ts +3 -3
  48. package/src/{routes → backend/routes}/api/alerts.ts +4 -4
  49. package/src/{routes → backend/routes}/api/config.paths.ts +1 -1
  50. package/src/{routes → backend/routes}/api/config.ts +11 -2
  51. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.help.ts +1 -1
  52. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.install.ts +1 -1
  53. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.runner-presets.ts +1 -1
  54. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.upgrade.ts +1 -1
  55. package/src/{routes → backend/routes}/api/ecosystem.packages.ts +5 -1
  56. package/src/{routes → backend/routes}/api/ecosystem.recipes.$recipeId.run.ts +1 -1
  57. package/src/{routes → backend/routes}/api/ecosystem.tasks.$taskId.ts +1 -1
  58. package/src/{routes → backend/routes}/api/ecosystem.tasks.ts +1 -1
  59. package/src/{routes → backend/routes}/api/groups.$groupId.evidence.ts +2 -2
  60. package/src/{routes → backend/routes}/api/groups.$groupId.sessions.ts +2 -2
  61. package/src/{routes → backend/routes}/api/groups.$groupId.ts +2 -2
  62. package/src/{routes → backend/routes}/api/groups.ts +2 -2
  63. package/src/{routes → backend/routes}/api/health.ts +3 -3
  64. package/src/{routes → backend/routes}/api/instances.$name.connection.ts +1 -1
  65. package/src/{routes → backend/routes}/api/instances.$name.restart.ts +2 -2
  66. package/src/{routes → backend/routes}/api/instances.$name.start.ts +2 -2
  67. package/src/{routes → backend/routes}/api/instances.$name.stop.ts +2 -2
  68. package/src/{routes → backend/routes}/api/instances.$name.ts +1 -1
  69. package/src/{routes → backend/routes}/api/instances.ts +1 -1
  70. package/src/{routes → backend/routes}/api/knowledge.candidates.$candidateId.promote.ts +2 -2
  71. package/src/{routes → backend/routes}/api/knowledge.candidates.$candidateId.ts +2 -2
  72. package/src/{routes → backend/routes}/api/knowledge.candidates.ts +1 -1
  73. package/src/{routes → backend/routes}/api/knowledge.project-context.ts +2 -2
  74. package/src/{routes → backend/routes}/api/knowledge.search.ts +2 -2
  75. package/src/{routes → backend/routes}/api/knowledge.sessions.$sessionId.candidates.ts +3 -3
  76. package/src/{routes → backend/routes}/api/logs.$id.body.ts +6 -6
  77. package/src/{routes → backend/routes}/api/logs.$id.chunks.ts +5 -4
  78. package/src/{routes → backend/routes}/api/logs.$id.replay.ts +12 -11
  79. package/src/{routes → backend/routes}/api/logs.$id.ts +4 -3
  80. package/src/{routes → backend/routes}/api/logs.import.ts +3 -3
  81. package/src/{routes → backend/routes}/api/logs.stream.ts +11 -7
  82. package/src/{routes → backend/routes}/api/logs.ts +17 -9
  83. package/src/{routes → backend/routes}/api/mcp.ts +1 -1
  84. package/src/{routes → backend/routes}/api/models.ts +1 -1
  85. package/src/{routes → backend/routes}/api/pi-agent.ts +2 -2
  86. package/src/{routes → backend/routes}/api/providers.$providerId.model-metadata.ts +5 -5
  87. package/src/{routes → backend/routes}/api/providers.$providerId.test.log.ts +7 -7
  88. package/src/{routes → backend/routes}/api/providers.$providerId.ts +4 -4
  89. package/src/{routes → backend/routes}/api/providers.export.ts +1 -1
  90. package/src/{routes → backend/routes}/api/providers.import.ts +3 -3
  91. package/src/{routes → backend/routes}/api/providers.scan.ts +2 -2
  92. package/src/{routes → backend/routes}/api/providers.ts +9 -4
  93. package/src/{routes → backend/routes}/api/runs.$runId.evidence.ts +2 -2
  94. package/src/{routes → backend/routes}/api/runs.$runId.ts +2 -2
  95. package/src/{routes → backend/routes}/api/runs.ts +4 -4
  96. package/src/backend/routes/api/runtime.ts +46 -0
  97. package/src/{routes → backend/routes}/api/sessions.ts +3 -3
  98. package/src/{routes → backend/routes}/proxy/$.ts +1 -1
  99. package/src/{routes → backend/routes}/readyz.ts +1 -1
  100. package/src/{start.ts → backend/start.ts} +1 -1
  101. package/src/cli/alias.ts +2 -11
  102. package/src/cli/doctor.ts +32 -9
  103. package/src/cli/instance.ts +36 -13
  104. package/src/cli/instanceApi.ts +213 -0
  105. package/src/cli/instanceArgs.ts +46 -11
  106. package/src/cli/instanceControl.ts +10 -2
  107. package/src/cli/instanceModel.ts +18 -17
  108. package/src/cli/instanceRegistry.ts +0 -9
  109. package/src/cli/networkHints.ts +27 -14
  110. package/src/cli/onboard.ts +69 -2
  111. package/src/cli/onboardTarget.ts +89 -40
  112. package/src/cli/rustBackendPackage.ts +450 -0
  113. package/src/cli/templates/codex-skill-onboard.ts +44 -14
  114. package/src/cli/templates/skill-onboard.ts +64 -28
  115. package/src/cli.ts +341 -75
  116. package/src/components/ProxyViewer.tsx +11 -9
  117. package/src/components/ProxyViewerContainer.tsx +12 -11
  118. package/src/components/pi-agent/PiAgentPanel.tsx +2 -2
  119. package/src/components/providers/ProviderCard.tsx +1 -1
  120. package/src/components/providers/ProviderTestResultsView.tsx +1 -1
  121. package/src/components/providers/SettingsDialog.tsx +159 -144
  122. package/src/components/proxy-viewer/ApplicationBar.tsx +2 -1
  123. package/src/components/proxy-viewer/LogEntry.tsx +1 -1
  124. package/src/components/proxy-viewer/log-formats/index.ts +1 -1
  125. package/src/contracts/index.ts +5 -0
  126. package/src/contracts/log.ts +33 -3
  127. package/src/knowledge/types.ts +3 -2
  128. package/src/lib/alertContract.ts +2 -2
  129. package/src/lib/apiClient.ts +30 -7
  130. package/src/lib/apiFormat.ts +29 -0
  131. package/src/lib/backendImplementation.ts +16 -0
  132. package/src/lib/basePath.ts +29 -11
  133. package/src/lib/browserRuntimeContract.ts +124 -0
  134. package/src/lib/browserRuntimeResolver.ts +244 -0
  135. package/src/{proxy → lib}/claudeCodeStrip.ts +6 -7
  136. package/src/lib/codingAgentEndpoints.ts +38 -0
  137. package/src/lib/instanceContract.ts +30 -11
  138. package/src/lib/managedInstance.ts +9 -1
  139. package/src/lib/piAgentContract.ts +3 -2
  140. package/src/lib/publicBasePathContext.ts +40 -10
  141. package/src/lib/resourceLimits.ts +2 -0
  142. package/src/lib/runContract.ts +3 -3
  143. package/src/lib/sessionInfoContract.ts +3 -2
  144. package/src/mcp/instanceHandlers.ts +2 -0
  145. package/src/mcp/server.ts +19 -24
  146. package/src/proxy/config.ts +25 -30
  147. package/src/proxy/dataDir.ts +43 -7
  148. package/src/proxy/ecosystemTasks.ts +161 -70
  149. package/src/proxy/formats/registry.ts +6 -21
  150. package/src/proxy/handler.ts +129 -29
  151. package/src/proxy/identityProxy.ts +567 -39
  152. package/src/proxy/jsonlRecovery.ts +554 -0
  153. package/src/proxy/logFinalizer.ts +25 -10
  154. package/src/proxy/logImporter.ts +2 -1
  155. package/src/proxy/logIndex.ts +191 -95
  156. package/src/proxy/logger.ts +77 -14
  157. package/src/proxy/platformCommands.ts +41 -0
  158. package/src/proxy/privateDataPath.ts +310 -88
  159. package/src/proxy/rawStreamCapture.ts +194 -13
  160. package/src/proxy/runtimeAdmission.ts +5 -2
  161. package/src/proxy/runtimeHealth.ts +14 -1
  162. package/src/proxy/runtimeShutdown.ts +11 -1
  163. package/src/proxy/schemas.ts +7 -1
  164. package/src/proxy/sessionArchive.ts +70 -14
  165. package/src/proxy/sqliteLogIndex.ts +65 -42
  166. package/src/proxy/store.ts +141 -34
  167. package/src/proxy/uiAssetServer.ts +106 -0
  168. package/src/proxy/upstream.ts +29 -2
  169. package/src/ui/bootstrap.ts +167 -0
  170. package/src/ui/index.html +38 -0
  171. package/src/ui/logsStreamClient.ts +20 -0
  172. package/src/ui/main.tsx +13 -0
  173. package/src/ui/public/runtime-config.js +2 -0
  174. package/src/ui/router.tsx +13 -0
  175. package/src/ui/routes/__root.tsx +69 -0
  176. package/src/{routes → ui/routes}/index.tsx +2 -3
  177. package/src/{routes → ui/routes}/session/$sessionId.tsx +5 -11
  178. package/src/ui/runtimeDiscovery.ts +50 -0
  179. package/styles/globals.css +1 -0
  180. package/.output/public/assets/ProxyViewerContainer-BEAhuGOJ.js +0 -126
  181. package/.output/public/assets/ResponseView-DBCjkwye.js +0 -2
  182. package/.output/public/assets/StreamingChunkSequence-CuXhDgMo.js +0 -1
  183. package/.output/public/assets/index-BZo_8koU.js +0 -70
  184. package/.output/public/assets/index-ZWschkCh.css +0 -1
  185. package/.output/public/assets/json-viewer-B-Ao6pwW.js +0 -1
  186. package/.output/server/_libs/@radix-ui/react-accessible-icon+[...].mjs +0 -1
  187. package/.output/server/_libs/@radix-ui/react-dismissable-layer+[...].mjs +0 -344
  188. package/.output/server/_libs/@radix-ui/react-navigation-menu+[...].mjs +0 -2
  189. package/.output/server/_libs/@radix-ui/react-one-time-password-field+[...].mjs +0 -2
  190. package/.output/server/_libs/@radix-ui/react-password-toggle-field+[...].mjs +0 -2
  191. package/.output/server/_libs/@radix-ui/react-use-callback-ref+[...].mjs +0 -11
  192. package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +0 -69
  193. package/.output/server/_libs/@radix-ui/react-use-effect-event+[...].mjs +0 -1
  194. package/.output/server/_libs/@radix-ui/react-use-is-hydrated+[...].mjs +0 -28
  195. package/.output/server/_libs/@radix-ui/react-use-layout-effect+[...].mjs +0 -6
  196. package/.output/server/_libs/@radix-ui/react-visually-hidden+[...].mjs +0 -34
  197. package/.output/server/_libs/aria-hidden.mjs +0 -122
  198. package/.output/server/_libs/bail.mjs +0 -8
  199. package/.output/server/_libs/character-entities.mjs +0 -2130
  200. package/.output/server/_libs/class-variance-authority.mjs +0 -44
  201. package/.output/server/_libs/clsx.mjs +0 -16
  202. package/.output/server/_libs/comma-separated-tokens.mjs +0 -10
  203. package/.output/server/_libs/decode-named-character-reference+[...].mjs +0 -8
  204. package/.output/server/_libs/dequal.mjs +0 -27
  205. package/.output/server/_libs/detect-node-es.mjs +0 -1
  206. package/.output/server/_libs/devlop.mjs +0 -8
  207. package/.output/server/_libs/diff.mjs +0 -320
  208. package/.output/server/_libs/estree-util-is-identifier-name.mjs +0 -11
  209. package/.output/server/_libs/floating-ui__core.mjs +0 -725
  210. package/.output/server/_libs/floating-ui__dom.mjs +0 -622
  211. package/.output/server/_libs/floating-ui__react-dom.mjs +0 -292
  212. package/.output/server/_libs/floating-ui__utils.mjs +0 -320
  213. package/.output/server/_libs/get-nonce.mjs +0 -9
  214. package/.output/server/_libs/hast-util-to-jsx-runtime.mjs +0 -388
  215. package/.output/server/_libs/hast-util-whitespace.mjs +0 -10
  216. package/.output/server/_libs/html-url-attributes.mjs +0 -26
  217. package/.output/server/_libs/inline-style-parser.mjs +0 -142
  218. package/.output/server/_libs/is-plain-obj.mjs +0 -10
  219. package/.output/server/_libs/lucide-react.mjs +0 -1027
  220. package/.output/server/_libs/mdast-util-from-markdown.mjs +0 -717
  221. package/.output/server/_libs/mdast-util-to-hast.mjs +0 -710
  222. package/.output/server/_libs/mdast-util-to-string.mjs +0 -38
  223. package/.output/server/_libs/micromark-core-commonmark.mjs +0 -2259
  224. package/.output/server/_libs/micromark-factory-destination.mjs +0 -94
  225. package/.output/server/_libs/micromark-factory-label.mjs +0 -63
  226. package/.output/server/_libs/micromark-factory-space.mjs +0 -24
  227. package/.output/server/_libs/micromark-factory-title.mjs +0 -65
  228. package/.output/server/_libs/micromark-factory-whitespace.mjs +0 -22
  229. package/.output/server/_libs/micromark-util-character.mjs +0 -44
  230. package/.output/server/_libs/micromark-util-chunked.mjs +0 -36
  231. package/.output/server/_libs/micromark-util-classify-character+[...].mjs +0 -12
  232. package/.output/server/_libs/micromark-util-combine-extensions+[...].mjs +0 -41
  233. package/.output/server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +0 -19
  234. package/.output/server/_libs/micromark-util-decode-string.mjs +0 -21
  235. package/.output/server/_libs/micromark-util-encode.mjs +0 -1
  236. package/.output/server/_libs/micromark-util-html-tag-name.mjs +0 -69
  237. package/.output/server/_libs/micromark-util-normalize-identifier+[...].mjs +0 -6
  238. package/.output/server/_libs/micromark-util-resolve-all.mjs +0 -15
  239. package/.output/server/_libs/micromark-util-sanitize-uri.mjs +0 -41
  240. package/.output/server/_libs/micromark-util-subtokenize.mjs +0 -346
  241. package/.output/server/_libs/micromark.mjs +0 -906
  242. package/.output/server/_libs/property-information.mjs +0 -1209
  243. package/.output/server/_libs/radix-ui.mjs +0 -1
  244. package/.output/server/_libs/radix-ui__number.mjs +0 -6
  245. package/.output/server/_libs/radix-ui__primitive.mjs +0 -11
  246. package/.output/server/_libs/radix-ui__react-accordion.mjs +0 -1
  247. package/.output/server/_libs/radix-ui__react-alert-dialog.mjs +0 -1
  248. package/.output/server/_libs/radix-ui__react-arrow.mjs +0 -23
  249. package/.output/server/_libs/radix-ui__react-aspect-ratio.mjs +0 -1
  250. package/.output/server/_libs/radix-ui__react-avatar.mjs +0 -1
  251. package/.output/server/_libs/radix-ui__react-checkbox.mjs +0 -1
  252. package/.output/server/_libs/radix-ui__react-collapsible.mjs +0 -144
  253. package/.output/server/_libs/radix-ui__react-collection.mjs +0 -69
  254. package/.output/server/_libs/radix-ui__react-compose-refs.mjs +0 -38
  255. package/.output/server/_libs/radix-ui__react-context-menu.mjs +0 -1
  256. package/.output/server/_libs/radix-ui__react-context.mjs +0 -64
  257. package/.output/server/_libs/radix-ui__react-dialog.mjs +0 -280
  258. package/.output/server/_libs/radix-ui__react-direction.mjs +0 -9
  259. package/.output/server/_libs/radix-ui__react-dropdown-menu.mjs +0 -1
  260. package/.output/server/_libs/radix-ui__react-focus-guards.mjs +0 -39
  261. package/.output/server/_libs/radix-ui__react-focus-scope.mjs +0 -210
  262. package/.output/server/_libs/radix-ui__react-form.mjs +0 -1
  263. package/.output/server/_libs/radix-ui__react-hover-card.mjs +0 -1
  264. package/.output/server/_libs/radix-ui__react-id.mjs +0 -14
  265. package/.output/server/_libs/radix-ui__react-label.mjs +0 -1
  266. package/.output/server/_libs/radix-ui__react-menu.mjs +0 -1
  267. package/.output/server/_libs/radix-ui__react-menubar.mjs +0 -1
  268. package/.output/server/_libs/radix-ui__react-popover.mjs +0 -1
  269. package/.output/server/_libs/radix-ui__react-popper.mjs +0 -340
  270. package/.output/server/_libs/radix-ui__react-portal.mjs +0 -16
  271. package/.output/server/_libs/radix-ui__react-presence.mjs +0 -173
  272. package/.output/server/_libs/radix-ui__react-primitive.mjs +0 -42
  273. package/.output/server/_libs/radix-ui__react-progress.mjs +0 -1
  274. package/.output/server/_libs/radix-ui__react-radio-group.mjs +0 -1
  275. package/.output/server/_libs/radix-ui__react-roving-focus.mjs +0 -235
  276. package/.output/server/_libs/radix-ui__react-scroll-area.mjs +0 -731
  277. package/.output/server/_libs/radix-ui__react-select.mjs +0 -1207
  278. package/.output/server/_libs/radix-ui__react-separator.mjs +0 -28
  279. package/.output/server/_libs/radix-ui__react-slider.mjs +0 -1
  280. package/.output/server/_libs/radix-ui__react-slot.mjs +0 -132
  281. package/.output/server/_libs/radix-ui__react-switch.mjs +0 -1
  282. package/.output/server/_libs/radix-ui__react-tabs.mjs +0 -194
  283. package/.output/server/_libs/radix-ui__react-toast.mjs +0 -2
  284. package/.output/server/_libs/radix-ui__react-toggle-group.mjs +0 -1
  285. package/.output/server/_libs/radix-ui__react-toggle.mjs +0 -1
  286. package/.output/server/_libs/radix-ui__react-toolbar.mjs +0 -1
  287. package/.output/server/_libs/radix-ui__react-tooltip.mjs +0 -498
  288. package/.output/server/_libs/radix-ui__react-use-previous.mjs +0 -14
  289. package/.output/server/_libs/radix-ui__react-use-size.mjs +0 -39
  290. package/.output/server/_libs/react-markdown.mjs +0 -235
  291. package/.output/server/_libs/react-remove-scroll-bar.mjs +0 -82
  292. package/.output/server/_libs/react-remove-scroll.mjs +0 -328
  293. package/.output/server/_libs/react-style-singleton.mjs +0 -69
  294. package/.output/server/_libs/remark-parse.mjs +0 -19
  295. package/.output/server/_libs/remark-rehype.mjs +0 -21
  296. package/.output/server/_libs/space-separated-tokens.mjs +0 -6
  297. package/.output/server/_libs/style-to-js.mjs +0 -72
  298. package/.output/server/_libs/style-to-object.mjs +0 -38
  299. package/.output/server/_libs/swr.mjs +0 -947
  300. package/.output/server/_libs/tailwind-merge.mjs +0 -3255
  301. package/.output/server/_libs/tanstack__react-virtual.mjs +0 -130
  302. package/.output/server/_libs/tanstack__virtual-core.mjs +0 -1319
  303. package/.output/server/_libs/three.mjs +0 -26016
  304. package/.output/server/_libs/trim-lines.mjs +0 -41
  305. package/.output/server/_libs/trough.mjs +0 -85
  306. package/.output/server/_libs/tslib.mjs +0 -1
  307. package/.output/server/_libs/ungap__structured-clone.mjs +0 -212
  308. package/.output/server/_libs/unified.mjs +0 -661
  309. package/.output/server/_libs/unist-util-is.mjs +0 -100
  310. package/.output/server/_libs/unist-util-position.mjs +0 -27
  311. package/.output/server/_libs/unist-util-stringify-position.mjs +0 -27
  312. package/.output/server/_libs/unist-util-visit-parents.mjs +0 -82
  313. package/.output/server/_libs/unist-util-visit.mjs +0 -24
  314. package/.output/server/_libs/use-callback-ref.mjs +0 -66
  315. package/.output/server/_libs/use-sidecar.mjs +0 -106
  316. package/.output/server/_libs/vfile-message.mjs +0 -138
  317. package/.output/server/_libs/vfile.mjs +0 -467
  318. package/.output/server/_sessionId-CJ99gZFV.mjs +0 -84
  319. package/.output/server/_sessionId-DEjScBpK.mjs +0 -146
  320. package/.output/server/_ssr/CompareDrawer-DtOIMeQB.mjs +0 -1060
  321. package/.output/server/_ssr/InspectorPet-CXc4oCBQ.mjs +0 -938
  322. package/.output/server/_ssr/ProxyViewerContainer-DVPlwfkt.mjs +0 -17632
  323. package/.output/server/_ssr/ReplayDialog-BQEA95qC.mjs +0 -725
  324. package/.output/server/_ssr/RequestAnatomy-BMyd8B7Q.mjs +0 -837
  325. package/.output/server/_ssr/ResponseView-qrTaEIM8.mjs +0 -757
  326. package/.output/server/_ssr/StreamingChunkSequence-BFTL8mIy.mjs +0 -274
  327. package/.output/server/_ssr/index-Bcj1Hcy4.mjs +0 -135
  328. package/.output/server/_ssr/index-DW-M7XUG.mjs +0 -84
  329. package/.output/server/_ssr/json-viewer-DiI0Yjsn.mjs +0 -483
  330. package/.output/server/_tanstack-start-manifest_v-C6pXXX9R.mjs +0 -4
  331. package/.output/server/node_modules/tslib/modules/index.js +0 -70
  332. package/.output/server/node_modules/tslib/modules/package.json +0 -3
  333. package/.output/server/node_modules/tslib/package.json +0 -47
  334. package/.output/server/node_modules/tslib/tslib.js +0 -484
  335. package/.output/server/package.json +0 -9
  336. package/src/routes/__root.tsx +0 -170
  337. package/src/routes/api/-instances.ts +0 -109
  338. /package/.output/{public → ui}/assets/agent-inspector-58K1_MsC.ico +0 -0
  339. /package/.output/{public → ui}/assets/alibaba-TTwafVwX.svg +0 -0
  340. /package/.output/{public → ui}/assets/minimax-BPMzvuL-.jpeg +0 -0
  341. /package/.output/{public → ui}/assets/qwen-mMn3f5ul.webp +0 -0
  342. /package/.output/{public → ui}/assets/zhipuai-BPNAnxo-.svg +0 -0
  343. /package/src/{routes → backend/routes}/livez.ts +0 -0
@@ -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