@adhdev/daemon-core 0.9.82-rc.45 → 0.9.82-rc.451

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 (429) hide show
  1. package/dist/boot/daemon-lifecycle.d.ts +9 -0
  2. package/dist/boot/process-hardening.d.ts +50 -0
  3. package/dist/build-info.d.ts +37 -0
  4. package/dist/chat/source-machine.d.ts +166 -0
  5. package/dist/chat/source-resolver.d.ts +104 -0
  6. package/dist/chat/subscription-updates.d.ts +1 -0
  7. package/dist/cli-adapter-types.d.ts +68 -2
  8. package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
  9. package/dist/cli-adapters/cli-state-engine.d.ts +200 -0
  10. package/dist/cli-adapters/provider-cli-adapter.d.ts +126 -64
  11. package/dist/cli-adapters/provider-cli-parse.d.ts +5 -0
  12. package/dist/cli-adapters/provider-cli-runtime.d.ts +34 -0
  13. package/dist/cli-adapters/provider-cli-shared.d.ts +74 -0
  14. package/dist/cli-adapters/pty-write-chunking.d.ts +34 -0
  15. package/dist/cli-adapters/raw-terminal-io.d.ts +37 -0
  16. package/dist/cli-adapters/resolve-executable.d.ts +69 -0
  17. package/dist/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +6 -3
  18. package/dist/cli-adapters/terminal-backends/types.d.ts +10 -2
  19. package/dist/cli-adapters/terminal-screen.d.ts +4 -11
  20. package/dist/commands/chat-commands-debug-bundle.d.ts +14 -0
  21. package/dist/commands/chat-commands-read.d.ts +7 -0
  22. package/dist/commands/chat-commands-scope.d.ts +39 -0
  23. package/dist/commands/chat-commands-shared.d.ts +33 -0
  24. package/dist/commands/chat-commands-write.d.ts +14 -0
  25. package/dist/commands/chat-commands.d.ts +10 -23
  26. package/dist/commands/cli-manager.d.ts +25 -1
  27. package/dist/commands/handler.d.ts +125 -0
  28. package/dist/commands/high-family/index.d.ts +3 -0
  29. package/dist/commands/high-family/mesh-coordinator-launch.d.ts +2 -0
  30. package/dist/commands/high-family/mesh-events.d.ts +2 -0
  31. package/dist/commands/high-family/mesh-status.d.ts +2 -0
  32. package/dist/commands/high-family/types.d.ts +64 -0
  33. package/dist/commands/low-family/coordinator-prompt.d.ts +9 -0
  34. package/dist/commands/low-family/daemon-lifecycle.d.ts +2 -0
  35. package/dist/commands/low-family/diagnostics.d.ts +2 -0
  36. package/dist/commands/low-family/index.d.ts +3 -0
  37. package/dist/commands/low-family/mesh-ledger.d.ts +10 -0
  38. package/dist/commands/low-family/mesh-node-logs.d.ts +2 -0
  39. package/dist/commands/low-family/notification.d.ts +2 -0
  40. package/dist/commands/low-family/refine-config.d.ts +2 -0
  41. package/dist/commands/low-family/session-host.d.ts +2 -0
  42. package/dist/commands/low-family/spec-providerdev.d.ts +11 -0
  43. package/dist/commands/low-family/status-meta.d.ts +2 -0
  44. package/dist/commands/low-family/types.d.ts +33 -0
  45. package/dist/commands/med-family/cli-agent.d.ts +2 -0
  46. package/dist/commands/med-family/fast-forward.d.ts +2 -0
  47. package/dist/commands/med-family/ide.d.ts +10 -0
  48. package/dist/commands/med-family/index.d.ts +3 -0
  49. package/dist/commands/med-family/mesh-crud.d.ts +29 -0
  50. package/dist/commands/med-family/mesh-host-pairing.d.ts +2 -0
  51. package/dist/commands/med-family/mesh-queue.d.ts +2 -0
  52. package/dist/commands/med-family/mesh-restart.d.ts +2 -0
  53. package/dist/commands/med-family/types.d.ts +152 -0
  54. package/dist/commands/mesh-coordinator.d.ts +85 -1
  55. package/dist/commands/router.d.ts +279 -2
  56. package/dist/commands/upgrade-helper.d.ts +41 -1
  57. package/dist/config/chat-history.d.ts +9 -0
  58. package/dist/config/config.d.ts +5 -0
  59. package/dist/config/mesh-config.d.ts +129 -1
  60. package/dist/config/mesh-json-config.d.ts +199 -0
  61. package/dist/config/repo-settings.d.ts +77 -0
  62. package/dist/daemon/dev-server.d.ts +0 -2
  63. package/dist/detection/ide-detector.d.ts +13 -0
  64. package/dist/detection/win32-ide-version.d.ts +37 -0
  65. package/dist/git/change-impact-config.d.ts +159 -0
  66. package/dist/git/git-commands.d.ts +12 -1
  67. package/dist/git/git-diff.d.ts +6 -0
  68. package/dist/git/git-executor.d.ts +11 -0
  69. package/dist/git/git-status.d.ts +57 -0
  70. package/dist/git/git-types.d.ts +2 -50
  71. package/dist/git/git-worktree.d.ts +71 -1
  72. package/dist/git/index.d.ts +3 -1
  73. package/dist/index.d.ts +66 -14
  74. package/dist/index.js +49753 -18414
  75. package/dist/index.js.map +1 -1
  76. package/dist/index.mjs +49614 -18438
  77. package/dist/index.mjs.map +1 -1
  78. package/dist/installer.d.ts +1 -4
  79. package/dist/ipc/local-ipc-server.d.ts +91 -0
  80. package/dist/launch.d.ts +1 -1
  81. package/dist/logging/async-batch-writer.d.ts +10 -0
  82. package/dist/logging/log-redactor.d.ts +24 -0
  83. package/dist/logging/log-tail-reader.d.ts +81 -0
  84. package/dist/logging/logger.d.ts +1 -1
  85. package/dist/mesh/contracts.d.ts +164 -0
  86. package/dist/mesh/coordinator-prompt.d.ts +89 -0
  87. package/dist/mesh/coordinator-registry.d.ts +59 -0
  88. package/dist/mesh/mesh-active-work.d.ts +174 -0
  89. package/dist/mesh/mesh-clone-grace.d.ts +8 -0
  90. package/dist/mesh/mesh-coordinator-config.d.ts +21 -0
  91. package/dist/mesh/mesh-delivery-policy.d.ts +131 -0
  92. package/dist/mesh/mesh-event-classify.d.ts +5 -0
  93. package/dist/mesh/mesh-event-forwarding.d.ts +41 -0
  94. package/dist/mesh/mesh-event-trace.d.ts +21 -0
  95. package/dist/mesh/mesh-events-coordinator.d.ts +4 -0
  96. package/dist/mesh/mesh-events-pending.d.ts +69 -0
  97. package/dist/mesh/mesh-events-stale.d.ts +61 -0
  98. package/dist/mesh/mesh-events-utils.d.ts +75 -0
  99. package/dist/mesh/mesh-events.d.ts +6 -49
  100. package/dist/mesh/mesh-fast-forward.d.ts +81 -0
  101. package/dist/mesh/mesh-host-ownership.d.ts +29 -0
  102. package/dist/mesh/mesh-init.d.ts +128 -0
  103. package/dist/mesh/mesh-ledger-reconciliation.d.ts +23 -4
  104. package/dist/mesh/mesh-ledger.d.ts +115 -1
  105. package/dist/mesh/mesh-magi-status.d.ts +63 -0
  106. package/dist/mesh/mesh-missions.d.ts +189 -0
  107. package/dist/mesh/mesh-node-identity.d.ts +306 -0
  108. package/dist/mesh/mesh-queue-assignment.d.ts +113 -0
  109. package/dist/mesh/mesh-reconcile-loop.d.ts +79 -0
  110. package/dist/mesh/mesh-refine-batch.d.ts +68 -0
  111. package/dist/mesh/mesh-refine-gates.d.ts +428 -0
  112. package/dist/mesh/mesh-refine-status.d.ts +62 -0
  113. package/dist/mesh/mesh-review-inbox.d.ts +90 -0
  114. package/dist/mesh/mesh-routing.d.ts +70 -0
  115. package/dist/mesh/mesh-runtime-store.d.ts +450 -0
  116. package/dist/mesh/mesh-scheduling-runtime.d.ts +78 -0
  117. package/dist/mesh/mesh-task-inflight.d.ts +46 -0
  118. package/dist/mesh/mesh-task-stats.d.ts +49 -0
  119. package/dist/mesh/mesh-unresolved-forward-outbox.d.ts +30 -0
  120. package/dist/mesh/mesh-warmup-deadline.d.ts +68 -0
  121. package/dist/mesh/mesh-work-queue.d.ts +300 -5
  122. package/dist/mesh/preview-freshness.d.ts +18 -0
  123. package/dist/mesh/refine-config.d.ts +216 -0
  124. package/dist/mesh/worktree-bootstrap-config.d.ts +145 -0
  125. package/dist/providers/acp-provider-instance.d.ts +5 -0
  126. package/dist/providers/approval-utils.d.ts +20 -0
  127. package/dist/providers/chat-message-normalization.d.ts +32 -1
  128. package/dist/providers/cli-provider-instance.d.ts +254 -3
  129. package/dist/providers/contracts.d.ts +149 -6
  130. package/dist/providers/external-sources.d.ts +71 -0
  131. package/dist/providers/manual-attendance.d.ts +63 -0
  132. package/dist/providers/native-history/antigravity-cli-transcript.d.ts +126 -0
  133. package/dist/providers/native-history/claude-cli-transcript.d.ts +70 -0
  134. package/dist/providers/native-history/codex-cli-transcript.d.ts +73 -0
  135. package/dist/providers/native-history/constants.d.ts +12 -0
  136. package/dist/providers/native-history/dispatcher.d.ts +27 -0
  137. package/dist/providers/native-history/hermes-cli-transcript.d.ts +30 -0
  138. package/dist/providers/native-history/index.d.ts +13 -0
  139. package/dist/providers/provider-instance-manager.d.ts +29 -0
  140. package/dist/providers/provider-instance.d.ts +23 -1
  141. package/dist/providers/provider-loader.d.ts +26 -4
  142. package/dist/providers/provider-trust.d.ts +31 -0
  143. package/dist/providers/read-chat-contract.d.ts +29 -0
  144. package/dist/providers/sdk/v1/builders/acp/detect-status.d.ts +68 -0
  145. package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +86 -0
  146. package/dist/providers/sdk/v1/builders/cli/parse-approval-squash.d.ts +59 -0
  147. package/dist/providers/sdk/v1/builders/cli/parse-approval.d.ts +65 -0
  148. package/dist/providers/sdk/v1/builders/cli/parse-session.d.ts +91 -0
  149. package/dist/providers/sdk/v1/builders/cli/visible-region.d.ts +42 -0
  150. package/dist/providers/sdk/v1/fixture-tooling/format.d.ts +126 -0
  151. package/dist/providers/sdk/v1/fixture-tooling/index.d.ts +8 -0
  152. package/dist/providers/sdk/v1/fixture-tooling/replay.d.ts +38 -0
  153. package/dist/providers/sdk/v1/index.d.ts +30 -0
  154. package/dist/providers/sdk/v1/sandbox/README-design.d.ts +193 -0
  155. package/dist/providers/sdk/v1/sandbox/require-whitelist.d.ts +74 -0
  156. package/dist/providers/sdk/v1/sandbox/script-runner.d.ts +98 -0
  157. package/dist/providers/sdk/v1/types/cli/index.d.ts +277 -0
  158. package/dist/providers/sdk/v1/types/common/index.d.ts +203 -0
  159. package/dist/providers/sdk/v1/validators/index.d.ts +5 -0
  160. package/dist/providers/sdk/v1/validators/manifest.d.ts +40 -0
  161. package/dist/providers/sdk/v1/validators/taint.d.ts +52 -0
  162. package/dist/providers/spec/adapter.d.ts +98 -0
  163. package/dist/providers/spec/cli-adapter.d.ts +223 -0
  164. package/dist/providers/spec/evaluator.d.ts +45 -0
  165. package/dist/providers/spec/fsm-driver.d.ts +444 -0
  166. package/dist/providers/spec/fsm-evaluator.d.ts +93 -0
  167. package/dist/providers/spec/fsm-loader.d.ts +14 -0
  168. package/dist/providers/spec/fsm-types.d.ts +202 -0
  169. package/dist/providers/spec/native-history-executor.d.ts +41 -0
  170. package/dist/providers/spec/pre-launch-trust.d.ts +16 -0
  171. package/dist/providers/spec/route.d.ts +4 -0
  172. package/dist/providers/spec/types.d.ts +222 -0
  173. package/dist/providers/status-monitor.d.ts +7 -7
  174. package/dist/providers/transcript-v2.d.ts +176 -0
  175. package/dist/providers/types/interactive-prompt.d.ts +84 -0
  176. package/dist/providers/working-dir.d.ts +17 -0
  177. package/dist/repo-mesh-types.d.ts +519 -13
  178. package/dist/runtime-defaults.d.ts +2 -0
  179. package/dist/session-host/managed-host.d.ts +64 -0
  180. package/dist/session-host/runtime-surface.d.ts +10 -16
  181. package/dist/sessions/registry.d.ts +6 -0
  182. package/dist/shared-types-extra.d.ts +2 -4
  183. package/dist/shared-types.d.ts +86 -55
  184. package/dist/status/normalize.d.ts +1 -1
  185. package/dist/status/normalize.js +1 -0
  186. package/dist/status/normalize.js.map +1 -1
  187. package/dist/status/normalize.mjs +1 -0
  188. package/dist/status/normalize.mjs.map +1 -1
  189. package/dist/status/reporter.d.ts +2 -0
  190. package/dist/status/snapshot.d.ts +26 -0
  191. package/dist/system/hash.d.ts +8 -0
  192. package/dist/system/load-better-sqlite3.d.ts +21 -0
  193. package/dist/types.d.ts +5 -0
  194. package/package.json +7 -3
  195. package/src/agent-stream/poller.ts +2 -3
  196. package/src/agent-stream/provider-adapter.ts +1 -1
  197. package/src/boot/daemon-lifecycle.ts +63 -12
  198. package/src/boot/process-hardening.ts +89 -0
  199. package/src/build-info.ts +73 -0
  200. package/src/chat/source-machine.ts +534 -0
  201. package/src/chat/source-resolver.ts +0 -0
  202. package/src/chat/subscription-updates.ts +20 -1
  203. package/src/cli-adapter-types.d.ts +3 -1
  204. package/src/cli-adapter-types.ts +68 -2
  205. package/src/cli-adapters/cli-script-runner.ts +421 -0
  206. package/src/cli-adapters/cli-state-engine.ts +1246 -0
  207. package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
  208. package/src/cli-adapters/provider-cli-adapter.ts +914 -1144
  209. package/src/cli-adapters/provider-cli-parse.d.ts +2 -0
  210. package/src/cli-adapters/provider-cli-parse.ts +19 -0
  211. package/src/cli-adapters/provider-cli-runtime.ts +56 -7
  212. package/src/cli-adapters/provider-cli-shared.d.ts +4 -0
  213. package/src/cli-adapters/provider-cli-shared.ts +121 -11
  214. package/src/cli-adapters/pty-transport.ts +2 -1
  215. package/src/cli-adapters/pty-write-chunking.ts +106 -0
  216. package/src/cli-adapters/raw-terminal-io.ts +252 -0
  217. package/src/cli-adapters/resolve-executable.ts +204 -0
  218. package/src/cli-adapters/session-host-transport.ts +2 -1
  219. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +63 -29
  220. package/src/cli-adapters/terminal-backends/types.ts +10 -3
  221. package/src/cli-adapters/terminal-screen.ts +16 -81
  222. package/src/commands/WINDOWS-UPGRADE-LOCK-FAILURE.md +198 -0
  223. package/src/commands/chat-commands-debug-bundle.ts +398 -0
  224. package/src/commands/chat-commands-read.ts +2488 -0
  225. package/src/commands/chat-commands-scope.ts +54 -0
  226. package/src/commands/chat-commands-shared.ts +114 -0
  227. package/src/commands/chat-commands-write.ts +891 -0
  228. package/src/commands/chat-commands.ts +19 -1841
  229. package/src/commands/cli-manager.ts +596 -27
  230. package/src/commands/handler.ts +841 -2
  231. package/src/commands/high-family/index.ts +28 -0
  232. package/src/commands/high-family/mesh-coordinator-launch.ts +680 -0
  233. package/src/commands/high-family/mesh-events.ts +89 -0
  234. package/src/commands/high-family/mesh-status.ts +822 -0
  235. package/src/commands/high-family/types.ts +80 -0
  236. package/src/commands/low-family/coordinator-prompt.ts +72 -0
  237. package/src/commands/low-family/daemon-lifecycle.ts +107 -0
  238. package/src/commands/low-family/diagnostics.ts +57 -0
  239. package/src/commands/low-family/index.ts +37 -0
  240. package/src/commands/low-family/mesh-ledger.ts +62 -0
  241. package/src/commands/low-family/mesh-node-logs.ts +87 -0
  242. package/src/commands/low-family/notification.ts +116 -0
  243. package/src/commands/low-family/refine-config.ts +106 -0
  244. package/src/commands/low-family/session-host.ts +274 -0
  245. package/src/commands/low-family/spec-providerdev.ts +217 -0
  246. package/src/commands/low-family/status-meta.ts +112 -0
  247. package/src/commands/low-family/types.ts +39 -0
  248. package/src/commands/med-family/cli-agent.ts +238 -0
  249. package/src/commands/med-family/fast-forward.ts +230 -0
  250. package/src/commands/med-family/ide.ts +163 -0
  251. package/src/commands/med-family/index.ts +37 -0
  252. package/src/commands/med-family/mesh-crud.ts +1253 -0
  253. package/src/commands/med-family/mesh-host-pairing.ts +234 -0
  254. package/src/commands/med-family/mesh-queue.ts +167 -0
  255. package/src/commands/med-family/mesh-restart.ts +92 -0
  256. package/src/commands/med-family/types.ts +153 -0
  257. package/src/commands/mesh-coordinator.ts +334 -124
  258. package/src/commands/router.ts +2893 -3536
  259. package/src/commands/stream-commands.ts +8 -0
  260. package/src/commands/upgrade-helper.ts +310 -45
  261. package/src/config/chat-history.ts +493 -24
  262. package/src/config/config.ts +12 -0
  263. package/src/config/mesh-config.ts +619 -23
  264. package/src/config/mesh-json-config.ts +376 -0
  265. package/src/config/recent-activity.ts +8 -2
  266. package/src/config/repo-settings.ts +111 -0
  267. package/src/daemon/dev-auto-implement.ts +3 -2
  268. package/src/daemon/dev-cli-debug.ts +10 -1
  269. package/src/daemon/dev-server.ts +0 -541
  270. package/src/detection/cli-detector.ts +28 -9
  271. package/src/detection/ide-detector.ts +55 -16
  272. package/src/detection/win32-ide-version.ts +106 -0
  273. package/src/git/change-impact-config.ts +354 -0
  274. package/src/git/git-commands.ts +78 -15
  275. package/src/git/git-diff.ts +81 -11
  276. package/src/git/git-executor.ts +12 -0
  277. package/src/git/git-status.ts +767 -48
  278. package/src/git/git-types.ts +14 -62
  279. package/src/git/git-worktree.ts +261 -4
  280. package/src/git/index.ts +17 -0
  281. package/src/index.ts +198 -12
  282. package/src/installer.d.ts +1 -1
  283. package/src/installer.ts +8 -6
  284. package/src/ipc/local-ipc-server.ts +278 -0
  285. package/src/launch.d.ts +1 -1
  286. package/src/launch.ts +37 -28
  287. package/src/logging/async-batch-writer.ts +55 -0
  288. package/src/logging/command-log.ts +7 -5
  289. package/src/logging/log-redactor.ts +100 -0
  290. package/src/logging/log-tail-reader.ts +341 -0
  291. package/src/logging/logger.ts +14 -7
  292. package/src/mesh/contracts.ts +338 -0
  293. package/src/mesh/coordinator-prompt.ts +416 -34
  294. package/src/mesh/coordinator-registry.ts +121 -0
  295. package/src/mesh/mesh-active-work.ts +645 -0
  296. package/src/mesh/mesh-clone-grace.ts +68 -0
  297. package/src/mesh/mesh-coordinator-config.ts +97 -0
  298. package/src/mesh/mesh-delivery-policy.ts +315 -0
  299. package/src/mesh/mesh-event-classify.ts +51 -0
  300. package/src/mesh/mesh-event-forwarding.ts +1897 -0
  301. package/src/mesh/mesh-event-trace.ts +67 -0
  302. package/src/mesh/mesh-events-coordinator.ts +32 -0
  303. package/src/mesh/mesh-events-pending.ts +656 -0
  304. package/src/mesh/mesh-events-stale.ts +389 -0
  305. package/src/mesh/mesh-events-utils.ts +443 -0
  306. package/src/mesh/mesh-events.ts +33 -1035
  307. package/src/mesh/mesh-fast-forward.ts +843 -0
  308. package/src/mesh/mesh-host-ownership.ts +111 -0
  309. package/src/mesh/mesh-init.ts +350 -0
  310. package/src/mesh/mesh-ledger-reconciliation.ts +12 -7
  311. package/src/mesh/mesh-ledger.ts +841 -102
  312. package/src/mesh/mesh-magi-status.ts +223 -0
  313. package/src/mesh/mesh-missions.ts +449 -0
  314. package/src/mesh/mesh-node-identity.ts +1985 -0
  315. package/src/mesh/mesh-queue-assignment.ts +1924 -0
  316. package/src/mesh/mesh-reconcile-loop.ts +2018 -0
  317. package/src/mesh/mesh-refine-batch.ts +205 -0
  318. package/src/mesh/mesh-refine-gates.ts +1673 -0
  319. package/src/mesh/mesh-refine-status.ts +231 -0
  320. package/src/mesh/mesh-review-inbox.ts +307 -0
  321. package/src/mesh/mesh-routing.ts +291 -0
  322. package/src/mesh/mesh-runtime-store.ts +1853 -0
  323. package/src/mesh/mesh-scheduling-runtime.ts +198 -0
  324. package/src/mesh/mesh-task-inflight.ts +70 -0
  325. package/src/mesh/mesh-task-stats.ts +161 -0
  326. package/src/mesh/mesh-unresolved-forward-outbox.ts +185 -0
  327. package/src/mesh/mesh-warmup-deadline.ts +152 -0
  328. package/src/mesh/mesh-work-queue.ts +1254 -141
  329. package/src/mesh/preview-freshness.ts +118 -0
  330. package/src/mesh/refine-config.ts +423 -0
  331. package/src/mesh/worktree-bootstrap-config.ts +450 -0
  332. package/src/providers/acp-provider-instance.ts +43 -10
  333. package/src/providers/approval-utils.d.ts +5 -0
  334. package/src/providers/approval-utils.ts +57 -5
  335. package/src/providers/chat-message-normalization.ts +92 -4
  336. package/src/providers/cli-provider-instance.ts +2177 -107
  337. package/src/providers/contracts.d.ts +55 -0
  338. package/src/providers/contracts.ts +160 -6
  339. package/src/providers/extension-provider-instance.ts +12 -7
  340. package/src/providers/external-sources.ts +218 -0
  341. package/src/providers/ide-provider-instance.ts +35 -12
  342. package/src/providers/manual-attendance.ts +85 -0
  343. package/src/providers/native-history/antigravity-cli-transcript.ts +1043 -0
  344. package/src/providers/native-history/claude-cli-transcript.ts +396 -0
  345. package/src/providers/native-history/codex-cli-transcript.ts +479 -0
  346. package/src/providers/native-history/constants.ts +19 -0
  347. package/src/providers/native-history/dispatcher.ts +373 -0
  348. package/src/providers/native-history/hermes-cli-transcript.ts +260 -0
  349. package/src/providers/native-history/index.ts +30 -0
  350. package/src/providers/provider-instance-manager.ts +71 -0
  351. package/src/providers/provider-instance.ts +19 -1
  352. package/src/providers/provider-loader.ts +668 -50
  353. package/src/providers/provider-schema.ts +87 -14
  354. package/src/providers/provider-trust.ts +114 -0
  355. package/src/providers/read-chat-contract.ts +76 -16
  356. package/src/providers/sdk/README.md +49 -0
  357. package/src/providers/sdk/v1/builders/acp/detect-status.ts +144 -0
  358. package/src/providers/sdk/v1/builders/cli/detect-status.ts +313 -0
  359. package/src/providers/sdk/v1/builders/cli/parse-approval-squash.ts +158 -0
  360. package/src/providers/sdk/v1/builders/cli/parse-approval.ts +250 -0
  361. package/src/providers/sdk/v1/builders/cli/parse-session.ts +276 -0
  362. package/src/providers/sdk/v1/builders/cli/visible-region.ts +143 -0
  363. package/src/providers/sdk/v1/fixture-tooling/format.ts +130 -0
  364. package/src/providers/sdk/v1/fixture-tooling/index.ts +22 -0
  365. package/src/providers/sdk/v1/fixture-tooling/replay.ts +352 -0
  366. package/src/providers/sdk/v1/index.ts +152 -0
  367. package/src/providers/sdk/v1/sandbox/README-design.ts +195 -0
  368. package/src/providers/sdk/v1/sandbox/require-whitelist.ts +472 -0
  369. package/src/providers/sdk/v1/sandbox/script-runner.ts +150 -0
  370. package/src/providers/sdk/v1/schemas/cli/provider.schema.json +509 -0
  371. package/src/providers/sdk/v1/schemas/primitives/acp-session-protocol-v1.json +131 -0
  372. package/src/providers/sdk/v1/schemas/primitives/native-history-codex-rollout-v1.json +66 -0
  373. package/src/providers/sdk/v1/schemas/primitives/tui-approval-squash-v1.json +91 -0
  374. package/src/providers/sdk/v1/schemas/primitives/tui-assistant-block-v1.json +91 -0
  375. package/src/providers/sdk/v1/schemas/primitives/tui-cue-ordering-v1.json +47 -0
  376. package/src/providers/sdk/v1/schemas/primitives/tui-dispatch-order-v1.json +32 -0
  377. package/src/providers/sdk/v1/schemas/primitives/tui-footer-chrome-v1.json +42 -0
  378. package/src/providers/sdk/v1/schemas/primitives/tui-index-finder-v1.json +27 -0
  379. package/src/providers/sdk/v1/schemas/primitives/tui-modal-v1.json +125 -0
  380. package/src/providers/sdk/v1/schemas/primitives/tui-prompt-marker-v1.json +45 -0
  381. package/src/providers/sdk/v1/schemas/primitives/tui-session-id-extraction-v1.json +46 -0
  382. package/src/providers/sdk/v1/schemas/primitives/tui-settled-prompt-v1.json +71 -0
  383. package/src/providers/sdk/v1/schemas/primitives/tui-spinner-v1.json +83 -0
  384. package/src/providers/sdk/v1/schemas/primitives/tui-transcript-pty-v1.json +83 -0
  385. package/src/providers/sdk/v1/schemas/primitives/tui-visible-region-v1.json +57 -0
  386. package/src/providers/sdk/v1/schemas/primitives/tui-welcome-screen-v1.json +35 -0
  387. package/src/providers/sdk/v1/types/cli/index.ts +365 -0
  388. package/src/providers/sdk/v1/types/common/index.ts +229 -0
  389. package/src/providers/sdk/v1/validators/index.ts +19 -0
  390. package/src/providers/sdk/v1/validators/manifest.ts +110 -0
  391. package/src/providers/sdk/v1/validators/taint.ts +309 -0
  392. package/src/providers/spec/adapter.ts +246 -0
  393. package/src/providers/spec/cli-adapter.ts +1064 -0
  394. package/src/providers/spec/evaluator.ts +407 -0
  395. package/src/providers/spec/fsm-driver.ts +1481 -0
  396. package/src/providers/spec/fsm-evaluator.ts +290 -0
  397. package/src/providers/spec/fsm-loader.ts +128 -0
  398. package/src/providers/spec/fsm-types.ts +301 -0
  399. package/src/providers/spec/native-history-executor.ts +1174 -0
  400. package/src/providers/spec/pre-launch-trust.ts +104 -0
  401. package/src/providers/spec/route.ts +51 -0
  402. package/src/providers/spec/types.ts +262 -0
  403. package/src/providers/status-monitor.d.ts +7 -7
  404. package/src/providers/status-monitor.ts +37 -22
  405. package/src/providers/transcript-v2.ts +567 -0
  406. package/src/providers/types/interactive-prompt.ts +536 -0
  407. package/src/providers/version-archive.ts +64 -24
  408. package/src/providers/working-dir.ts +23 -0
  409. package/src/repo-mesh-types.ts +758 -14
  410. package/src/runtime-defaults.ts +39 -0
  411. package/src/session-host/managed-host.ts +218 -0
  412. package/src/session-host/runtime-surface.ts +20 -80
  413. package/src/sessions/registry.ts +6 -0
  414. package/src/shared-types-extra.ts +2 -4
  415. package/src/shared-types.d.ts +8 -0
  416. package/src/shared-types.ts +124 -55
  417. package/src/status/builders.ts +26 -6
  418. package/src/status/normalize.ts +2 -0
  419. package/src/status/reporter.ts +19 -1
  420. package/src/status/snapshot.ts +95 -26
  421. package/src/system/hash.ts +23 -0
  422. package/src/system/host-memory.ts +29 -12
  423. package/src/system/load-better-sqlite3.ts +68 -0
  424. package/src/types.ts +5 -0
  425. package/dist/cli-adapters/terminal-backends/xterm-backend.d.ts +0 -17
  426. package/dist/mesh/mesh-sync.d.ts +0 -53
  427. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +0 -16
  428. package/src/cli-adapters/terminal-backends/xterm-backend.ts +0 -97
  429. package/src/mesh/mesh-sync.ts +0 -111
@@ -0,0 +1,1043 @@
1
+ /**
2
+ * antigravity-cli-transcript — Daemon-side built-in native history adapter for antigravity-cli.
3
+ *
4
+ * Antigravity CLI stores conversations in multiple complementary locations:
5
+ *
6
+ * 1. ~/.gemini/antigravity-cli/history.jsonl
7
+ * Lightweight index of user prompts, one row per turn:
8
+ * { conversationId: string, display: string, workspace: string, timestamp: number }
9
+ *
10
+ * 2. ~/.gemini/antigravity-cli/brain/<uuid>/.system_generated/logs/transcript*.jsonl
11
+ * Per-session full transcript, one row per agent step:
12
+ * { source: 'USER_EXPLICIT'|'MODEL', type: string, content: string, status: 'DONE'|..., created_at: number }
13
+ *
14
+ * 3. ~/.gemini/antigravity-cli/conversations/<uuid>.pb
15
+ * Legacy protobuf binary — schema not publicly documented. Adapter extracts
16
+ * printable UTF-8 text runs as best-effort content (no proto library needed).
17
+ *
18
+ * 4. ~/.gemini/antigravity-cli/conversations/<uuid>.db ← current format
19
+ * Per-session SQLite database. Recent antigravity migrated conversation
20
+ * storage from .pb (+ brain/*.jsonl) to a per-session SQLite db. The
21
+ * schema is a trajectory of `steps`, NOT a simple messages(role,content)
22
+ * table:
23
+ * steps(idx INTEGER PK, step_type INTEGER, status INTEGER,
24
+ * step_payload BLOB [protobuf], ...)
25
+ * Each `step_payload` is a protobuf message. Empirically (introspected
26
+ * from real stores):
27
+ * - step_type 14 → a USER turn. The prompt text is the largest
28
+ * contiguous UTF-8 run inside the payload (field 19 subtree).
29
+ * - step_type 15 → a MODEL/assistant turn. The assistant's final
30
+ * natural-language answer lives at payload field 20 → field 1
31
+ * (identical to field 8). Field 20 → field 3 is the internal
32
+ * reasoning summary and is intentionally NOT surfaced.
33
+ * - other step types are tool calls / ephemeral system context.
34
+ * We read the blobs with a tiny dependency-free protobuf field walker
35
+ * (no proto schema / codegen needed) and map the two message step types.
36
+ * Because the daemon does NOT read this db, native history previously
37
+ * returned 0 rows for these sessions and read_chat fell back to the
38
+ * pty parser (which only echoes the user's own input) — assistant
39
+ * answers appeared lost even though they were on disk.
40
+ *
41
+ * This adapter provides:
42
+ * - Full coverage from a per-session .db (current format) — preferred.
43
+ * - Full coverage when a brain transcript exists (legacy authoritative source).
44
+ * - Partial coverage (user prompts only) from history.jsonl as fallback.
45
+ * - Best-effort raw-string extraction from .pb files when no other source exists.
46
+ *
47
+ * watchPath (from provider.v1.json):
48
+ * ~/.gemini/antigravity-cli/history.jsonl
49
+ * ~/.gemini/antigravity-cli/brain/{uuid}/.system_generated/logs/transcript*.jsonl
50
+ * ~/.gemini/antigravity-cli/conversations/{uuid}.pb
51
+ * ~/.gemini/antigravity-cli/conversations/{uuid}.db
52
+ *
53
+ * OSS code (AGPL-3.0). Must not import from packages/ (proprietary).
54
+ */
55
+
56
+ import * as fs from 'fs';
57
+ import * as path from 'path';
58
+ import * as os from 'os';
59
+ import { loadBetterSqlite3 } from '../../system/load-better-sqlite3.js';
60
+ import { LOG } from '../../logging/logger.js';
61
+
62
+ // ─── Types ─────────────────────────────────────────────────────────────────
63
+
64
+ export type NativeHistoryRole = 'user' | 'assistant' | 'system';
65
+ export type NativeHistoryKind = 'standard' | 'tool' | 'session_start';
66
+
67
+ export interface NativeHistoryMessage {
68
+ ts: string;
69
+ receivedAt: number;
70
+ role: NativeHistoryRole;
71
+ content: string;
72
+ kind: NativeHistoryKind;
73
+ senderName?: string;
74
+ agent: 'antigravity-cli';
75
+ historySessionId: string;
76
+ workspace?: string;
77
+ /** Stable per-message identity (v2 contract). */
78
+ providerUnitKey?: string;
79
+ }
80
+
81
+ export interface NativeHistorySession {
82
+ messages: NativeHistoryMessage[];
83
+ providerSessionId: string;
84
+ source: 'provider-native';
85
+ sourcePath: string;
86
+ sourceMtimeMs: number;
87
+ /**
88
+ * 'full' — brain transcript was available and parsed.
89
+ * 'partial' — only history.jsonl user-prompts are available.
90
+ * 'best-effort' — .pb file bytes were used for raw text extraction.
91
+ */
92
+ nativeHistoryCoverage: 'full' | 'partial' | 'best-effort';
93
+ workspace?: string;
94
+ partialReason?: string;
95
+ }
96
+
97
+ export interface NativeHistorySessionMeta {
98
+ historySessionId: string;
99
+ sessionId: string;
100
+ sourcePath: string;
101
+ sourceMtimeMs: number;
102
+ messageCount: number;
103
+ firstMessageAt: number;
104
+ lastMessageAt: number;
105
+ sessionTitle?: string;
106
+ preview?: string;
107
+ workspace?: string;
108
+ agent: 'antigravity-cli';
109
+ source: 'provider-native';
110
+ nativeHistoryCoverage: 'full' | 'partial' | 'best-effort';
111
+ partialReason?: string;
112
+ }
113
+
114
+ // ─── Internal helpers ───────────────────────────────────────────────────────
115
+
116
+ function extractTimestampValue(value: unknown): number {
117
+ if (typeof value === 'number' && Number.isFinite(value) && value > 0) return value;
118
+ if (typeof value === 'string') {
119
+ const numeric = Number(value);
120
+ if (Number.isFinite(numeric) && numeric > 0) return numeric;
121
+ const parsed = Date.parse(value);
122
+ if (Number.isFinite(parsed) && parsed > 0) return parsed;
123
+ }
124
+ return 0;
125
+ }
126
+
127
+ function statMtimeMs(filePath: string): number {
128
+ try { return fs.statSync(filePath).mtimeMs; } catch { return 0; }
129
+ }
130
+
131
+ function isUuidLike(value: string): boolean {
132
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
133
+ }
134
+
135
+ function antigravityRoot(): string {
136
+ return path.join(os.homedir(), '.gemini', 'antigravity-cli');
137
+ }
138
+
139
+ function historyJsonlPath(): string {
140
+ return path.join(antigravityRoot(), 'history.jsonl');
141
+ }
142
+
143
+ function brainRoot(): string {
144
+ return path.join(antigravityRoot(), 'brain');
145
+ }
146
+
147
+ function conversationsRoot(): string {
148
+ return path.join(antigravityRoot(), 'conversations');
149
+ }
150
+
151
+ function resolvePathInside(root: string, ...segments: string[]): string | null {
152
+ const rootPath = path.resolve(root);
153
+ const targetPath = path.resolve(rootPath, ...segments);
154
+ if (targetPath !== rootPath && !targetPath.startsWith(rootPath + path.sep)) return null;
155
+ return targetPath;
156
+ }
157
+
158
+ /**
159
+ * Find the brain transcript file for a given session UUID.
160
+ * Prefers transcript_full.jsonl over transcript.jsonl when both exist.
161
+ */
162
+ function findBrainTranscriptPath(sessionId: string): string | null {
163
+ if (!isUuidLike(sessionId)) return null;
164
+ const logsRoot = resolvePathInside(brainRoot(), sessionId, '.system_generated', 'logs');
165
+ if (!logsRoot || !fs.existsSync(logsRoot)) return null;
166
+
167
+ const candidates = (['transcript_full.jsonl', 'transcript.jsonl'] as const)
168
+ .map((file) => resolvePathInside(logsRoot, file))
169
+ .filter((p): p is string => p !== null && fs.existsSync(p));
170
+
171
+ if (candidates.length === 0) {
172
+ // Look for any transcript*.jsonl
173
+ let entries: fs.Dirent[] = [];
174
+ try { entries = fs.readdirSync(logsRoot, { withFileTypes: true }); } catch { return null; }
175
+ const transcriptFiles = entries
176
+ .filter((e) => e.isFile() && /^transcript.*\.jsonl$/.test(e.name))
177
+ .map((e) => path.join(logsRoot, e.name));
178
+ if (transcriptFiles.length === 0) return null;
179
+ transcriptFiles.sort((a, b) => statMtimeMs(b) - statMtimeMs(a));
180
+ return transcriptFiles[0];
181
+ }
182
+
183
+ candidates.sort((a, b) => statMtimeMs(b) - statMtimeMs(a));
184
+ return candidates[0];
185
+ }
186
+
187
+ /**
188
+ * List all session UUIDs that have a brain transcript directory.
189
+ */
190
+ function listBrainSessions(): Array<{ sessionId: string; transcriptPath: string; sourceMtimeMs: number }> {
191
+ const root = brainRoot();
192
+ let entries: fs.Dirent[] = [];
193
+ try { entries = fs.readdirSync(root, { withFileTypes: true }); } catch { return []; }
194
+
195
+ const results: Array<{ sessionId: string; transcriptPath: string; sourceMtimeMs: number }> = [];
196
+ for (const entry of entries) {
197
+ if (!entry.isDirectory() || !isUuidLike(entry.name)) continue;
198
+ const transcriptPath = findBrainTranscriptPath(entry.name);
199
+ if (!transcriptPath) continue;
200
+ results.push({
201
+ sessionId: entry.name,
202
+ transcriptPath,
203
+ sourceMtimeMs: statMtimeMs(transcriptPath),
204
+ });
205
+ }
206
+ return results;
207
+ }
208
+
209
+ /**
210
+ * Strip USER_REQUEST XML wrapper from antigravity user prompts.
211
+ * Antigravity wraps structured user input in <USER_REQUEST>...</USER_REQUEST>.
212
+ */
213
+ function extractUserRequestContent(content: string): string {
214
+ const raw = content.trim();
215
+ const match = raw.match(/<USER_REQUEST>\s*([\s\S]*?)\s*<\/USER_REQUEST>/i);
216
+ if (match) return match[1].trim();
217
+ return raw
218
+ .replace(/<ADDITIONAL_METADATA>[\s\S]*?<\/ADDITIONAL_METADATA>/gi, '')
219
+ .replace(/<USER_SETTINGS_CHANGE>[\s\S]*?<\/USER_SETTINGS_CHANGE>/gi, '')
220
+ .replace(/<\/?USER_REQUEST>/gi, '')
221
+ .trim();
222
+ }
223
+
224
+ /**
225
+ * Determine the NativeHistoryKind from an antigravity transcript row type.
226
+ */
227
+ function antigravityRowKind(rowType: string): NativeHistoryKind {
228
+ if (rowType === 'PLANNER_RESPONSE') return 'standard';
229
+ if (rowType && rowType !== 'USER_INPUT') return 'tool';
230
+ return 'standard';
231
+ }
232
+
233
+ // ─── Brain transcript parser ─────────────────────────────────────────────────
234
+
235
+ interface TranscriptRow {
236
+ source?: string;
237
+ type?: string;
238
+ content?: string;
239
+ status?: string;
240
+ created_at?: unknown;
241
+ }
242
+
243
+ /**
244
+ * Parse a brain transcript JSONL file into NativeHistoryMessages.
245
+ * Only rows with status === 'DONE' are processed.
246
+ * USER_EXPLICIT / USER_INPUT rows → user role.
247
+ * MODEL rows → assistant role.
248
+ */
249
+ function parseBrainTranscript(
250
+ filePath: string,
251
+ sessionId: string,
252
+ workspace?: string,
253
+ ): NativeHistoryMessage[] | null {
254
+ let raw: string;
255
+ try { raw = fs.readFileSync(filePath, 'utf-8'); } catch { return null; }
256
+
257
+ const lines = raw.split('\n').filter(Boolean);
258
+ if (lines.length === 0) return null;
259
+
260
+ const records: NativeHistoryMessage[] = [];
261
+ const normalizedWorkspace = typeof workspace === 'string' ? workspace.trim() : '';
262
+ let fallbackTs = statMtimeMs(filePath) || Date.now();
263
+
264
+ for (const line of lines) {
265
+ let row: TranscriptRow | null = null;
266
+ try { row = JSON.parse(line) as TranscriptRow; } catch { continue; }
267
+ if (!row || typeof row !== 'object') continue;
268
+ if (row.status !== 'DONE') continue;
269
+
270
+ const rowSource = String(row.source ?? '').trim();
271
+ const rowType = String(row.type ?? '').trim();
272
+ const rawContent = typeof row.content === 'string' ? row.content : '';
273
+ const receivedAt = extractTimestampValue(row.created_at) || fallbackTs + records.length;
274
+
275
+ if (rowSource === 'USER_EXPLICIT' && rowType === 'USER_INPUT') {
276
+ const content = extractUserRequestContent(rawContent);
277
+ if (!content) continue;
278
+ const msg: NativeHistoryMessage = {
279
+ ts: new Date(receivedAt).toISOString(),
280
+ receivedAt,
281
+ role: 'user',
282
+ content,
283
+ kind: 'standard',
284
+ agent: 'antigravity-cli',
285
+ historySessionId: sessionId,
286
+ };
287
+ if (normalizedWorkspace) msg.workspace = normalizedWorkspace;
288
+ records.push(msg);
289
+ } else if (rowSource === 'MODEL') {
290
+ const content = rawContent.trim();
291
+ if (!content) continue;
292
+ const kind = antigravityRowKind(rowType);
293
+ const msg: NativeHistoryMessage = {
294
+ ts: new Date(receivedAt).toISOString(),
295
+ receivedAt,
296
+ role: 'assistant',
297
+ content,
298
+ kind,
299
+ agent: 'antigravity-cli',
300
+ historySessionId: sessionId,
301
+ };
302
+ if (kind === 'tool') msg.senderName = 'Tool';
303
+ if (normalizedWorkspace) msg.workspace = normalizedWorkspace;
304
+ records.push(msg);
305
+ }
306
+ }
307
+
308
+ return records.length > 0 ? records : null;
309
+ }
310
+
311
+ // ─── History JSONL parser ────────────────────────────────────────────────────
312
+
313
+ interface HistoryRow {
314
+ conversationId: string;
315
+ display: string;
316
+ workspace: string;
317
+ receivedAt: number;
318
+ }
319
+
320
+ function readHistoryRows(): HistoryRow[] {
321
+ const sourcePath = historyJsonlPath();
322
+ let lines: string[] = [];
323
+ try { lines = fs.readFileSync(sourcePath, 'utf-8').split('\n').filter(Boolean); } catch { return []; }
324
+
325
+ const rows: HistoryRow[] = [];
326
+ for (const line of lines) {
327
+ let parsed: unknown = null;
328
+ try { parsed = JSON.parse(line); } catch { continue; }
329
+ if (!parsed || typeof parsed !== 'object') continue;
330
+
331
+ const row = parsed as Record<string, unknown>;
332
+ const conversationId = String(row.conversationId ?? '').trim();
333
+ const display = typeof row.display === 'string' ? row.display.trim() : '';
334
+ const workspace = typeof row.workspace === 'string' ? row.workspace.trim() : '';
335
+ const receivedAt = extractTimestampValue(row.timestamp);
336
+
337
+ if (!conversationId || !isUuidLike(conversationId) || !display || !receivedAt) continue;
338
+ rows.push({ conversationId, display, workspace, receivedAt });
339
+ }
340
+ return rows;
341
+ }
342
+
343
+ // ─── Protobuf best-effort text extraction ────────────────────────────────────
344
+
345
+ const MIN_PRINTABLE_RUN = 8;
346
+
347
+ /**
348
+ * Extract printable UTF-8 text runs from raw binary data (similar to the `strings` command).
349
+ * Runs of printable ASCII/UTF-8 characters of length >= MIN_PRINTABLE_RUN are collected.
350
+ * This is used as a best-effort fallback when the .pb schema is not available.
351
+ */
352
+ function extractStringsFromBuffer(buf: Buffer): string[] {
353
+ const strings: string[] = [];
354
+ let current: number[] = [];
355
+
356
+ for (let i = 0; i < buf.length; i++) {
357
+ const byte = buf[i];
358
+ // Accept printable ASCII (32-126) and common whitespace (9=tab, 10=LF, 13=CR)
359
+ if ((byte >= 32 && byte <= 126) || byte === 9 || byte === 10 || byte === 13) {
360
+ current.push(byte);
361
+ } else {
362
+ if (current.length >= MIN_PRINTABLE_RUN) {
363
+ const str = Buffer.from(current).toString('utf-8').trim();
364
+ if (str) strings.push(str);
365
+ }
366
+ current = [];
367
+ }
368
+ }
369
+ // Flush remaining
370
+ if (current.length >= MIN_PRINTABLE_RUN) {
371
+ const str = Buffer.from(current).toString('utf-8').trim();
372
+ if (str) strings.push(str);
373
+ }
374
+
375
+ return strings;
376
+ }
377
+
378
+ /**
379
+ * Read a .pb conversation file and extract best-effort text content.
380
+ * Returns a single NativeHistoryMessage with the extracted content,
381
+ * or null if nothing readable was found.
382
+ */
383
+ function parsePbFile(
384
+ filePath: string,
385
+ sessionId: string,
386
+ ): NativeHistoryMessage[] | null {
387
+ let buf: Buffer;
388
+ try { buf = fs.readFileSync(filePath); } catch { return null; }
389
+ if (buf.length === 0) return null;
390
+
391
+ const strings = extractStringsFromBuffer(buf);
392
+ // Filter out very short or likely-binary strings
393
+ const meaningful = strings.filter((s) => s.length >= MIN_PRINTABLE_RUN && /\w/.test(s));
394
+ if (meaningful.length === 0) return null;
395
+
396
+ const content = meaningful.join('\n');
397
+ const sourceMtimeMs = statMtimeMs(filePath);
398
+
399
+ return [
400
+ {
401
+ ts: new Date(sourceMtimeMs).toISOString(),
402
+ receivedAt: sourceMtimeMs,
403
+ role: 'assistant',
404
+ content,
405
+ kind: 'standard',
406
+ agent: 'antigravity-cli',
407
+ historySessionId: sessionId,
408
+ },
409
+ ];
410
+ }
411
+
412
+ // ─── SQLite (.db) conversation reader ────────────────────────────────────────
413
+ //
414
+ // Recent antigravity stores each conversation in a per-session SQLite db at
415
+ // conversations/<uuid>.db. See the file header for the schema. We decode the
416
+ // protobuf `step_payload` blobs with a minimal, dependency-free field walker —
417
+ // we only need two leaf strings (user prompt / assistant answer), so a full
418
+ // proto schema is unnecessary.
419
+
420
+ /** Antigravity step_type values that map to a chat message. */
421
+ const AGY_STEP_TYPE_USER = 14;
422
+ const AGY_STEP_TYPE_MODEL = 15;
423
+
424
+ interface ProtoField {
425
+ field: number;
426
+ wireType: number;
427
+ /** For wireType 2 (length-delimited): the raw bytes. */
428
+ bytes?: Buffer;
429
+ /** For wireType 0 (varint): the value. */
430
+ varint?: number;
431
+ }
432
+
433
+ /**
434
+ * Read a base-128 varint starting at `offset`. Returns [value, nextOffset].
435
+ * Values are read as JS numbers (safe: the fields we consume are small).
436
+ */
437
+ function readVarint(buf: Buffer, offset: number): [number, number] {
438
+ let result = 0;
439
+ let shift = 0;
440
+ let i = offset;
441
+ while (i < buf.length) {
442
+ const byte = buf[i];
443
+ i += 1;
444
+ result += (byte & 0x7f) * Math.pow(2, shift);
445
+ if ((byte & 0x80) === 0) return [result, i];
446
+ shift += 7;
447
+ if (shift > 63) break; // malformed / oversized
448
+ }
449
+ return [result, i];
450
+ }
451
+
452
+ /**
453
+ * Decode the top-level fields of a protobuf message. Best-effort: stops on the
454
+ * first malformed byte rather than throwing, so a partially-corrupt blob still
455
+ * yields the fields decoded so far.
456
+ */
457
+ function decodeProtoFields(buf: Buffer): ProtoField[] {
458
+ const fields: ProtoField[] = [];
459
+ let i = 0;
460
+ while (i < buf.length) {
461
+ const [key, afterKey] = readVarint(buf, i);
462
+ if (afterKey === i) break;
463
+ i = afterKey;
464
+ const field = Math.floor(key / 8);
465
+ const wireType = key & 7;
466
+ if (field <= 0) break;
467
+ if (wireType === 0) {
468
+ const [value, next] = readVarint(buf, i);
469
+ if (next === i) break;
470
+ i = next;
471
+ fields.push({ field, wireType, varint: value });
472
+ } else if (wireType === 2) {
473
+ const [len, afterLen] = readVarint(buf, i);
474
+ i = afterLen;
475
+ if (len < 0 || i + len > buf.length) break;
476
+ fields.push({ field, wireType, bytes: buf.subarray(i, i + len) });
477
+ i += len;
478
+ } else if (wireType === 5) {
479
+ i += 4;
480
+ } else if (wireType === 1) {
481
+ i += 8;
482
+ } else {
483
+ break; // wireType 3/4 (groups) — unused by antigravity payloads
484
+ }
485
+ }
486
+ return fields;
487
+ }
488
+
489
+ /** Return the bytes of the first length-delimited field with number `field`. */
490
+ function firstLenField(buf: Buffer, field: number): Buffer | null {
491
+ for (const f of decodeProtoFields(buf)) {
492
+ if (f.field === field && f.wireType === 2 && f.bytes) return f.bytes;
493
+ }
494
+ return null;
495
+ }
496
+
497
+ /** Heuristic: is this buffer (mostly) printable UTF-8 text? */
498
+ function looksLikeText(buf: Buffer): boolean {
499
+ if (buf.length === 0) return false;
500
+ let printable = 0;
501
+ for (let i = 0; i < buf.length; i++) {
502
+ const b = buf[i];
503
+ // ASCII printable + common whitespace, or any high byte (UTF-8 lead/cont).
504
+ if ((b >= 32 && b <= 126) || b === 9 || b === 10 || b === 13 || b >= 0x80) printable += 1;
505
+ }
506
+ return printable / buf.length >= 0.9;
507
+ }
508
+
509
+ /**
510
+ * Antigravity prefixes some assistant answers with a literal `MARKER_V1`
511
+ * sentinel followed by a blank line. Strip it so the user-visible bubble starts
512
+ * at the real text.
513
+ */
514
+ function stripAnswerMarker(text: string): string {
515
+ return text.replace(/^\s*MARKER_V1\s*/, '');
516
+ }
517
+
518
+ /**
519
+ * Extract the assistant's final natural-language answer from a step_type 15
520
+ * payload: field 20 → field 1 (identical to field 8). Field 20 → field 3 is the
521
+ * private reasoning summary and is deliberately skipped. Returns '' if absent
522
+ * (e.g. a pure reasoning / tool-only model step, which carries no user-visible
523
+ * answer text).
524
+ */
525
+ function extractModelAnswer(payload: Buffer): string {
526
+ const inner = firstLenField(payload, 20);
527
+ if (!inner) return '';
528
+ const answer = firstLenField(inner, 1) ?? firstLenField(inner, 8);
529
+ if (!answer || !looksLikeText(answer)) return '';
530
+ return stripAnswerMarker(answer.toString('utf-8')).trim();
531
+ }
532
+
533
+ /**
534
+ * Extract the user prompt from a step_type 14 payload: field 19 → field 2 (the
535
+ * clean prompt text; field 19 → field 3 wraps the same string with a leading
536
+ * newline and is used only as a fallback). The USER_REQUEST XML wrapper, when
537
+ * present, is unwrapped to match the brain-transcript reader's output.
538
+ */
539
+ function extractUserPrompt(payload: Buffer): string {
540
+ const inner = firstLenField(payload, 19);
541
+ if (!inner) return '';
542
+ const raw = firstLenField(inner, 2) ?? firstLenField(inner, 3);
543
+ if (!raw || !looksLikeText(raw)) return '';
544
+ const text = raw.toString('utf-8').trim();
545
+ if (!text) return '';
546
+ return extractUserRequestContent(text);
547
+ }
548
+
549
+ interface AgyDbStepRow {
550
+ idx: number;
551
+ step_type: number;
552
+ step_payload: Buffer | null;
553
+ }
554
+
555
+ /**
556
+ * Parse a per-session conversations/<uuid>.db (SQLite) into NativeHistoryMessages.
557
+ * Returns null when the db is unreadable, empty, or yields no chat messages.
558
+ */
559
+ /**
560
+ * True when an error thrown by better-sqlite3 open/read is a transient
561
+ * SQLITE_BUSY / "database is locked" condition rather than a permanent one.
562
+ *
563
+ * On win32 antigravity holds a mandatory WAL write/checkpoint lock while it
564
+ * persists a step; a readonly open racing that lock throws SQLITE_BUSY. That
565
+ * is transient — the answer IS already on disk — so it must be retried, NOT
566
+ * collapsed to "no session" (which erases the just-written assistant answer on
567
+ * a chat_history re-query). macOS advisory locking + WAL reader-doesn't-block-
568
+ * writer masks this, which is why it is win32-specific.
569
+ */
570
+ function isSqliteBusyError(err: unknown): boolean {
571
+ if (!err) return false;
572
+ const code = (err as any).code;
573
+ if (typeof code === 'string' && code.includes('SQLITE_BUSY')) return true;
574
+ const msg = err instanceof Error ? err.message : String(err);
575
+ return /SQLITE_BUSY|database is locked|database table is locked/i.test(msg);
576
+ }
577
+
578
+ const AGY_DB_BUSY_TIMEOUT_MS = 3000;
579
+ const AGY_DB_MAX_ATTEMPTS = 4;
580
+ const AGY_DB_RETRY_BACKOFF_MS = [50, 100, 150];
581
+
582
+ function sleepBusy(ms: number): void {
583
+ // Synchronous busy-wait: parseConversationDb is a sync function called from a
584
+ // sync read path, and better-sqlite3 itself is synchronous. The waits are
585
+ // tiny (≤150ms) and only occur under genuine lock contention, so a short
586
+ // spin-sleep is acceptable and keeps the call site synchronous.
587
+ const end = Date.now() + ms;
588
+ while (Date.now() < end) { /* spin */ }
589
+ }
590
+
591
+ function parseConversationDb(
592
+ filePath: string,
593
+ sessionId: string,
594
+ workspace?: string,
595
+ ): NativeHistoryMessage[] | null {
596
+ let Database: any;
597
+ try {
598
+ Database = loadBetterSqlite3();
599
+ } catch (err) {
600
+ // better-sqlite3 binding genuinely unavailable (ABI mismatch / not built
601
+ // into this bundle). This is the only true "cannot read at all" case — a
602
+ // real load failure, distinct from transient lock contention below. Warn
603
+ // once at WARN so it is greppable; the reader degrades gracefully (returns
604
+ // null → dispatcher falls back to brain/.pb).
605
+ LOG.warn(
606
+ 'NativeHistory',
607
+ `antigravity .db reader could not load better-sqlite3 for ${path.basename(filePath)}: ${err instanceof Error ? err.message : String(err)} (native binding unavailable — assistant answers in this .db will not surface)`,
608
+ );
609
+ return null;
610
+ }
611
+
612
+ let rows: AgyDbStepRow[] | null = null;
613
+ let lastBusyErr: unknown;
614
+
615
+ for (let attempt = 1; attempt <= AGY_DB_MAX_ATTEMPTS; attempt++) {
616
+ let db: any;
617
+ try {
618
+ db = new Database(filePath, { readonly: true, fileMustExist: true });
619
+ // Ask SQLite itself to wait (rather than failing fast) if the WAL
620
+ // lock is momentarily held by antigravity. Set as early as possible
621
+ // after open so the prepare/all below inherits the wait.
622
+ try { db.pragma(`busy_timeout = ${AGY_DB_BUSY_TIMEOUT_MS}`); } catch { /* ignore */ }
623
+ rows = db
624
+ .prepare(
625
+ `SELECT idx, step_type, step_payload
626
+ FROM steps
627
+ WHERE step_type IN (${AGY_STEP_TYPE_USER}, ${AGY_STEP_TYPE_MODEL})
628
+ ORDER BY idx ASC`,
629
+ )
630
+ .all() as AgyDbStepRow[];
631
+ break; // success
632
+ } catch (err) {
633
+ if (isSqliteBusyError(err)) {
634
+ // Transient WAL lock contention. Do NOT collapse to null on the first
635
+ // failure — the assistant answer is already persisted; treating a busy
636
+ // lock as "no session" is exactly what erased answers on re-query.
637
+ // Retry with a small backoff; only give up after attempts exhausted.
638
+ lastBusyErr = err;
639
+ if (attempt < AGY_DB_MAX_ATTEMPTS) {
640
+ sleepBusy(AGY_DB_RETRY_BACKOFF_MS[attempt - 1] ?? 150);
641
+ continue;
642
+ }
643
+ LOG.warn(
644
+ 'NativeHistory',
645
+ `antigravity .db ${path.basename(filePath)} stayed locked (SQLITE_BUSY) after ${AGY_DB_MAX_ATTEMPTS} attempts: ${err instanceof Error ? err.message : String(err)} (WAL write/checkpoint lock contention — assistant answers may transiently not surface this read)`,
646
+ );
647
+ return null;
648
+ }
649
+ // `steps` table absent / unexpected schema, or a genuine open/parse
650
+ // failure that is not lock contention — a real (but recoverable) shape
651
+ // mismatch. Log at debug so a schema drift in a future antigravity
652
+ // release is diagnosable without spamming logs for every legacy db.
653
+ LOG.debug(
654
+ 'NativeHistory',
655
+ `antigravity .db ${path.basename(filePath)} not readable: ${err instanceof Error ? err.message : String(err)}`,
656
+ );
657
+ return null;
658
+ } finally {
659
+ try { db?.close(); } catch { /* ignore */ }
660
+ }
661
+ }
662
+
663
+ void lastBusyErr; // referenced only for retry bookkeeping above
664
+
665
+ if (!Array.isArray(rows) || rows.length === 0) return null;
666
+
667
+ const normalizedWorkspace = typeof workspace === 'string' ? workspace.trim() : '';
668
+ const baseTs = statMtimeMs(filePath) || Date.now();
669
+ const messages: NativeHistoryMessage[] = [];
670
+
671
+ for (const row of rows) {
672
+ const payload = row.step_payload;
673
+ if (!payload || !Buffer.isBuffer(payload) || payload.length === 0) continue;
674
+
675
+ // Steps are ordered by idx; the db carries no per-step timestamp we can
676
+ // trust as ms, so synthesize a monotonically increasing receivedAt that
677
+ // preserves order (idx-derived) around the file mtime.
678
+ const receivedAt = baseTs + messages.length;
679
+
680
+ if (row.step_type === AGY_STEP_TYPE_USER) {
681
+ const content = extractUserPrompt(payload);
682
+ if (!content) continue;
683
+ const msg: NativeHistoryMessage = {
684
+ ts: new Date(receivedAt).toISOString(),
685
+ receivedAt,
686
+ role: 'user',
687
+ content,
688
+ kind: 'standard',
689
+ agent: 'antigravity-cli',
690
+ historySessionId: sessionId,
691
+ };
692
+ if (normalizedWorkspace) msg.workspace = normalizedWorkspace;
693
+ messages.push(msg);
694
+ } else if (row.step_type === AGY_STEP_TYPE_MODEL) {
695
+ const content = extractModelAnswer(payload);
696
+ if (!content) continue; // reasoning-only / tool-only model step — no answer text
697
+ const msg: NativeHistoryMessage = {
698
+ ts: new Date(receivedAt).toISOString(),
699
+ receivedAt,
700
+ role: 'assistant',
701
+ content,
702
+ kind: 'standard',
703
+ agent: 'antigravity-cli',
704
+ historySessionId: sessionId,
705
+ };
706
+ if (normalizedWorkspace) msg.workspace = normalizedWorkspace;
707
+ messages.push(msg);
708
+ }
709
+ }
710
+
711
+ return messages.length > 0 ? messages : null;
712
+ }
713
+
714
+ // ─── Public API ─────────────────────────────────────────────────────────────
715
+
716
+ /**
717
+ * Read a single Antigravity CLI session by its source file path.
718
+ *
719
+ * `sessionPath` is the absolute path to one of:
720
+ * - A brain transcript JSONL: ~/.gemini/antigravity-cli/brain/<uuid>/.system_generated/logs/transcript*.jsonl
721
+ * - The shared history.jsonl: ~/.gemini/antigravity-cli/history.jsonl
722
+ * - A conversation SQLite db: ~/.gemini/antigravity-cli/conversations/<uuid>.db
723
+ * - A conversation protobuf: ~/.gemini/antigravity-cli/conversations/<uuid>.pb
724
+ *
725
+ * The session UUID is inferred from the directory name (brain), filename (pb), or
726
+ * must be provided explicitly via `sessionId` for history.jsonl paths.
727
+ *
728
+ * Returns `null` when the file is missing, empty, or yields no parseable messages.
729
+ */
730
+ export function readSession(
731
+ sessionPath: string,
732
+ sessionId?: string,
733
+ workspace?: string,
734
+ ): NativeHistorySession | null {
735
+ if (!sessionPath || !path.isAbsolute(sessionPath)) return null;
736
+ if (!fs.existsSync(sessionPath)) return null;
737
+
738
+ const sourceMtimeMs = statMtimeMs(sessionPath);
739
+
740
+ // ── Case 1: brain transcript JSONL ──────────────────────────────────────
741
+ const brainRootPath = brainRoot();
742
+ if (sessionPath.startsWith(brainRootPath + path.sep) && sessionPath.endsWith('.jsonl')) {
743
+ // Extract UUID from the path: brain/<uuid>/.system_generated/...
744
+ const relative = sessionPath.slice(brainRootPath.length + 1);
745
+ const uuidFromPath = relative.split(path.sep)[0];
746
+ const resolvedSessionId = sessionId || (isUuidLike(uuidFromPath) ? uuidFromPath : '');
747
+ if (!resolvedSessionId) return null;
748
+
749
+ const messages = parseBrainTranscript(sessionPath, resolvedSessionId, workspace);
750
+ if (!messages || messages.length === 0) return null;
751
+
752
+ return {
753
+ messages,
754
+ providerSessionId: resolvedSessionId,
755
+ source: 'provider-native',
756
+ sourcePath: sessionPath,
757
+ sourceMtimeMs,
758
+ nativeHistoryCoverage: 'full',
759
+ workspace,
760
+ };
761
+ }
762
+
763
+ // ── Case 2a: .db conversation file (current per-session SQLite format) ───
764
+ if (sessionPath.endsWith('.db')) {
765
+ const dbSessionId = sessionId || path.basename(sessionPath, '.db');
766
+ if (!isUuidLike(dbSessionId)) return null;
767
+
768
+ const messages = parseConversationDb(sessionPath, dbSessionId, workspace);
769
+ if (!messages || messages.length === 0) return null;
770
+
771
+ return {
772
+ messages,
773
+ providerSessionId: dbSessionId,
774
+ source: 'provider-native',
775
+ sourcePath: sessionPath,
776
+ sourceMtimeMs,
777
+ nativeHistoryCoverage: 'full',
778
+ workspace,
779
+ };
780
+ }
781
+
782
+ // ── Case 2b: .pb conversation file (legacy protobuf) ─────────────────────
783
+ if (sessionPath.endsWith('.pb')) {
784
+ const pbSessionId = sessionId || path.basename(sessionPath, '.pb');
785
+ if (!isUuidLike(pbSessionId)) return null;
786
+
787
+ const messages = parsePbFile(sessionPath, pbSessionId);
788
+ if (!messages || messages.length === 0) return null;
789
+
790
+ return {
791
+ messages,
792
+ providerSessionId: pbSessionId,
793
+ source: 'provider-native',
794
+ sourcePath: sessionPath,
795
+ sourceMtimeMs,
796
+ nativeHistoryCoverage: 'best-effort',
797
+ partialReason: 'antigravity_cli_pb_raw_text_extraction',
798
+ };
799
+ }
800
+
801
+ // ── Case 3: history.jsonl (user prompts index) ──────────────────────────
802
+ if (path.basename(sessionPath) === 'history.jsonl') {
803
+ const resolvedSessionId = sessionId || '';
804
+ if (!resolvedSessionId || !isUuidLike(resolvedSessionId)) return null;
805
+
806
+ const rows = readHistoryRows().filter((r) => r.conversationId === resolvedSessionId);
807
+ if (rows.length === 0) return null;
808
+
809
+ rows.sort((a, b) => a.receivedAt - b.receivedAt);
810
+ const firstWorkspace = workspace || rows.find((r) => r.workspace)?.workspace || '';
811
+
812
+ const messages: NativeHistoryMessage[] = [];
813
+ if (firstWorkspace) {
814
+ messages.push({
815
+ ts: new Date(rows[0].receivedAt).toISOString(),
816
+ receivedAt: rows[0].receivedAt,
817
+ role: 'system',
818
+ content: firstWorkspace,
819
+ kind: 'session_start',
820
+ agent: 'antigravity-cli',
821
+ historySessionId: resolvedSessionId,
822
+ workspace: firstWorkspace,
823
+ });
824
+ }
825
+
826
+ for (const row of rows) {
827
+ const msg: NativeHistoryMessage = {
828
+ ts: new Date(row.receivedAt).toISOString(),
829
+ receivedAt: row.receivedAt,
830
+ role: 'user',
831
+ content: row.display,
832
+ kind: 'standard',
833
+ agent: 'antigravity-cli',
834
+ historySessionId: resolvedSessionId,
835
+ };
836
+ if (row.workspace) msg.workspace = row.workspace;
837
+ messages.push(msg);
838
+ }
839
+
840
+ return {
841
+ messages,
842
+ providerSessionId: resolvedSessionId,
843
+ source: 'provider-native',
844
+ sourcePath: sessionPath,
845
+ sourceMtimeMs,
846
+ nativeHistoryCoverage: 'partial',
847
+ partialReason: 'antigravity_cli_history_jsonl_contains_user_prompts_only',
848
+ };
849
+ }
850
+
851
+ return null;
852
+ }
853
+
854
+ /**
855
+ * List all Antigravity CLI sessions found across all watchPath locations.
856
+ *
857
+ * Priority:
858
+ * 1. Brain transcript sessions (full coverage) — preferred source.
859
+ * 2. History JSONL entries without a brain transcript (partial coverage).
860
+ * 3. .pb conversation files without any other source (best-effort).
861
+ *
862
+ * Returns summary metadata sorted by most recently updated first.
863
+ */
864
+ export async function listSessions(_watchPath: string): Promise<NativeHistorySessionMeta[]> {
865
+ const results: NativeHistorySessionMeta[] = [];
866
+ const seen = new Set<string>();
867
+
868
+ // ── Step 1: brain transcripts (full coverage) ──────────────────────────
869
+ const historyRows = readHistoryRows();
870
+ const workspaceBySession = new Map<string, string>();
871
+ for (const row of historyRows) {
872
+ if (row.workspace && !workspaceBySession.has(row.conversationId)) {
873
+ workspaceBySession.set(row.conversationId, row.workspace);
874
+ }
875
+ }
876
+
877
+ for (const { sessionId, transcriptPath, sourceMtimeMs } of listBrainSessions()) {
878
+ const workspace = workspaceBySession.get(sessionId);
879
+ const messages = parseBrainTranscript(transcriptPath, sessionId, workspace);
880
+ if (!messages || messages.length === 0) continue;
881
+
882
+ const visible = messages.filter((m) => m.kind !== 'session_start');
883
+ if (visible.length === 0) continue;
884
+
885
+ const firstMsg = visible[0];
886
+ const lastMsg = visible[visible.length - 1];
887
+
888
+ results.push({
889
+ historySessionId: sessionId,
890
+ sessionId,
891
+ sourcePath: transcriptPath,
892
+ sourceMtimeMs,
893
+ messageCount: visible.length,
894
+ firstMessageAt: firstMsg.receivedAt || sourceMtimeMs,
895
+ lastMessageAt: lastMsg.receivedAt || sourceMtimeMs,
896
+ sessionTitle: lastMsg.content,
897
+ preview: lastMsg.content,
898
+ workspace,
899
+ agent: 'antigravity-cli',
900
+ source: 'provider-native',
901
+ nativeHistoryCoverage: 'full',
902
+ });
903
+ seen.add(sessionId);
904
+ }
905
+
906
+ // ── Step 2: history.jsonl entries without brain transcripts ────────────
907
+ const grouped = new Map<
908
+ string,
909
+ {
910
+ firstMessageAt: number;
911
+ lastMessageAt: number;
912
+ messageCount: number;
913
+ sessionTitle?: string;
914
+ preview?: string;
915
+ workspace?: string;
916
+ }
917
+ >();
918
+ for (const row of historyRows) {
919
+ const existing = grouped.get(row.conversationId) ?? {
920
+ firstMessageAt: row.receivedAt,
921
+ lastMessageAt: row.receivedAt,
922
+ messageCount: 0,
923
+ };
924
+ existing.messageCount += 1;
925
+ existing.firstMessageAt = Math.min(existing.firstMessageAt, row.receivedAt);
926
+ if (row.receivedAt >= existing.lastMessageAt) {
927
+ existing.lastMessageAt = row.receivedAt;
928
+ existing.sessionTitle = row.display;
929
+ existing.preview = row.display;
930
+ if (row.workspace) existing.workspace = row.workspace;
931
+ }
932
+ grouped.set(row.conversationId, existing);
933
+ }
934
+
935
+ const historySrc = historyJsonlPath();
936
+ const historyMtime = statMtimeMs(historySrc);
937
+
938
+ for (const [sessionId, info] of grouped.entries()) {
939
+ if (seen.has(sessionId)) continue;
940
+ results.push({
941
+ historySessionId: sessionId,
942
+ sessionId,
943
+ sourcePath: historySrc,
944
+ sourceMtimeMs: historyMtime,
945
+ messageCount: info.messageCount,
946
+ firstMessageAt: info.firstMessageAt,
947
+ lastMessageAt: info.lastMessageAt,
948
+ sessionTitle: info.sessionTitle,
949
+ preview: info.preview,
950
+ workspace: info.workspace,
951
+ agent: 'antigravity-cli',
952
+ source: 'provider-native',
953
+ nativeHistoryCoverage: 'partial',
954
+ partialReason: 'antigravity_cli_history_jsonl_contains_user_prompts_only',
955
+ });
956
+ seen.add(sessionId);
957
+ }
958
+
959
+ // ── Step 3: conversations/<uuid>.db and .pb without any other source ────
960
+ //
961
+ // Current antigravity writes per-session SQLite dbs; older sessions kept a
962
+ // .pb protobuf. Both can coexist in conversations/. Discover both, but when
963
+ // the same uuid has a .db, prefer it (full coverage) over the .pb (best
964
+ // effort). brain/history sources already in `seen` still win over either.
965
+ const convRoot = conversationsRoot();
966
+ if (fs.existsSync(convRoot)) {
967
+ let entries: fs.Dirent[] = [];
968
+ try { entries = fs.readdirSync(convRoot, { withFileTypes: true }); } catch { /* ignore */ }
969
+
970
+ // Group by uuid so a .db supersedes a sibling .pb of the same session.
971
+ const byUuid = new Map<string, { db?: string; pb?: string }>();
972
+ for (const entry of entries) {
973
+ if (!entry.isFile()) continue;
974
+ const dbMatch = /^([0-9a-f-]+)\.db$/i.exec(entry.name);
975
+ const pbMatch = /^([0-9a-f-]+)\.pb$/i.exec(entry.name);
976
+ if (dbMatch && isUuidLike(dbMatch[1])) {
977
+ const g = byUuid.get(dbMatch[1]) ?? {};
978
+ g.db = path.join(convRoot, entry.name);
979
+ byUuid.set(dbMatch[1], g);
980
+ } else if (pbMatch && isUuidLike(pbMatch[1])) {
981
+ const g = byUuid.get(pbMatch[1]) ?? {};
982
+ g.pb = path.join(convRoot, entry.name);
983
+ byUuid.set(pbMatch[1], g);
984
+ }
985
+ }
986
+
987
+ for (const [uuid, files] of byUuid.entries()) {
988
+ if (seen.has(uuid)) continue;
989
+
990
+ if (files.db) {
991
+ // Parse the db so listSessions reports accurate counts/preview and the
992
+ // session surfaces with full coverage (assistant answers included).
993
+ const workspace = workspaceBySession.get(uuid);
994
+ const messages = parseConversationDb(files.db, uuid, workspace);
995
+ const dbMtime = statMtimeMs(files.db);
996
+ if (messages && messages.length > 0) {
997
+ const lastMsg = messages[messages.length - 1];
998
+ const firstMsg = messages[0];
999
+ results.push({
1000
+ historySessionId: uuid,
1001
+ sessionId: uuid,
1002
+ sourcePath: files.db,
1003
+ sourceMtimeMs: dbMtime,
1004
+ messageCount: messages.length,
1005
+ firstMessageAt: firstMsg.receivedAt || dbMtime,
1006
+ lastMessageAt: lastMsg.receivedAt || dbMtime,
1007
+ sessionTitle: lastMsg.content,
1008
+ preview: lastMsg.content,
1009
+ workspace,
1010
+ agent: 'antigravity-cli',
1011
+ source: 'provider-native',
1012
+ nativeHistoryCoverage: 'full',
1013
+ });
1014
+ seen.add(uuid);
1015
+ continue;
1016
+ }
1017
+ // db unreadable/empty (e.g. sqlite binding unavailable) → fall through
1018
+ // to the .pb best-effort entry below if one exists.
1019
+ }
1020
+
1021
+ if (files.pb) {
1022
+ const pbMtime = statMtimeMs(files.pb);
1023
+ results.push({
1024
+ historySessionId: uuid,
1025
+ sessionId: uuid,
1026
+ sourcePath: files.pb,
1027
+ sourceMtimeMs: pbMtime,
1028
+ messageCount: 0,
1029
+ firstMessageAt: pbMtime,
1030
+ lastMessageAt: pbMtime,
1031
+ agent: 'antigravity-cli',
1032
+ source: 'provider-native',
1033
+ nativeHistoryCoverage: 'best-effort',
1034
+ partialReason: 'antigravity_cli_pb_raw_text_extraction',
1035
+ });
1036
+ seen.add(uuid);
1037
+ }
1038
+ }
1039
+ }
1040
+
1041
+ results.sort((a, b) => (b.lastMessageAt || 0) - (a.lastMessageAt || 0));
1042
+ return results;
1043
+ }