@maudecode/talaria-web 1.18.0 → 1.20.0

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 (774) hide show
  1. package/_release.json +8 -8
  2. package/dist/api/auth-raw.js +16 -16
  3. package/dist/api/auth-raw.js.map +1 -1
  4. package/dist/api/automation-raw.d.ts +2 -2
  5. package/dist/api/automation-raw.js +93 -38
  6. package/dist/api/automation-raw.js.map +1 -1
  7. package/dist/api/automation-router.d.ts +99 -17
  8. package/dist/api/automation-router.js +22 -14
  9. package/dist/api/automation-router.js.map +1 -1
  10. package/dist/api/chat-router.d.ts +1433 -37
  11. package/dist/api/chat-router.js +205 -90
  12. package/dist/api/chat-router.js.map +1 -1
  13. package/dist/api/git-router.d.ts +6 -2
  14. package/dist/api/git-router.js +30 -30
  15. package/dist/api/git-router.js.map +1 -1
  16. package/dist/api/raw-routes.d.ts +3 -3
  17. package/dist/api/raw-routes.js +76 -62
  18. package/dist/api/raw-routes.js.map +1 -1
  19. package/dist/api/router.d.ts +5 -5
  20. package/dist/api/router.js +22 -22
  21. package/dist/api/router.js.map +1 -1
  22. package/dist/api/session-visibility.d.ts +4 -4
  23. package/dist/api/session-visibility.js +26 -16
  24. package/dist/api/session-visibility.js.map +1 -1
  25. package/dist/api/sessions-router.d.ts +2635 -151
  26. package/dist/api/sessions-router.js +193 -177
  27. package/dist/api/sessions-router.js.map +1 -1
  28. package/dist/api/settings-router.d.ts +450 -11
  29. package/dist/api/settings-router.js +102 -49
  30. package/dist/api/settings-router.js.map +1 -1
  31. package/dist/api/sse-routes.d.ts +2 -2
  32. package/dist/api/sse-routes.js +102 -80
  33. package/dist/api/sse-routes.js.map +1 -1
  34. package/dist/api/tools-raw.d.ts +5 -0
  35. package/dist/api/tools-raw.js +58 -33
  36. package/dist/api/tools-raw.js.map +1 -1
  37. package/dist/api/tools-router.d.ts +963 -109
  38. package/dist/api/tools-router.js +426 -72
  39. package/dist/api/tools-router.js.map +1 -1
  40. package/dist/api/transcribe-stream.d.ts +16 -0
  41. package/dist/api/transcribe-stream.js +172 -0
  42. package/dist/api/transcribe-stream.js.map +1 -0
  43. package/dist/app.d.ts +4 -1
  44. package/dist/app.js +80 -57
  45. package/dist/app.js.map +1 -1
  46. package/dist/auth/crypto.d.ts +3 -0
  47. package/dist/auth/crypto.js +18 -0
  48. package/dist/auth/crypto.js.map +1 -1
  49. package/dist/auth/gate.js +14 -9
  50. package/dist/auth/gate.js.map +1 -1
  51. package/dist/auth/passkeys.d.ts +15 -14
  52. package/dist/auth/passkeys.js +89 -91
  53. package/dist/auth/passkeys.js.map +1 -1
  54. package/dist/auth/store.d.ts +17 -7
  55. package/dist/auth/store.js +93 -52
  56. package/dist/auth/store.js.map +1 -1
  57. package/dist/bin/talaria-web.js +12 -81
  58. package/dist/bin/talaria-web.js.map +1 -1
  59. package/dist/cli/ctl.js +8 -2
  60. package/dist/cli/ctl.js.map +1 -1
  61. package/dist/cli/dotenv.d.ts +5 -0
  62. package/dist/cli/dotenv.js +14 -2
  63. package/dist/cli/dotenv.js.map +1 -1
  64. package/dist/cli/launcher.js +5 -5
  65. package/dist/cli/launcher.js.map +1 -1
  66. package/dist/cli/main.d.ts +1 -0
  67. package/dist/cli/main.js +90 -0
  68. package/dist/cli/main.js.map +1 -0
  69. package/dist/cli/supervise.d.ts +24 -0
  70. package/dist/cli/supervise.js +147 -6
  71. package/dist/cli/supervise.js.map +1 -1
  72. package/dist/config/agent-config.d.ts +37 -11
  73. package/dist/config/agent-config.js +146 -64
  74. package/dist/config/agent-config.js.map +1 -1
  75. package/dist/config.d.ts +2 -0
  76. package/dist/config.js +14 -5
  77. package/dist/config.js.map +1 -1
  78. package/dist/fs/atomic.d.ts +4 -0
  79. package/dist/fs/atomic.js +19 -2
  80. package/dist/fs/atomic.js.map +1 -1
  81. package/dist/fs/stat.d.ts +7 -0
  82. package/dist/fs/stat.js +27 -0
  83. package/dist/fs/stat.js.map +1 -0
  84. package/dist/http/context.d.ts +28 -9
  85. package/dist/http/context.js +77 -25
  86. package/dist/http/context.js.map +1 -1
  87. package/dist/onboarding.d.ts +2 -2
  88. package/dist/onboarding.js +10 -8
  89. package/dist/onboarding.js.map +1 -1
  90. package/dist/profiles/profiles.d.ts +12 -4
  91. package/dist/profiles/profiles.js +78 -30
  92. package/dist/profiles/profiles.js.map +1 -1
  93. package/dist/projects.d.ts +15 -6
  94. package/dist/projects.js +55 -47
  95. package/dist/projects.js.map +1 -1
  96. package/dist/providers/catalog.d.ts +85 -14
  97. package/dist/providers/catalog.js +502 -146
  98. package/dist/providers/catalog.js.map +1 -1
  99. package/dist/providers/env-file.d.ts +10 -3
  100. package/dist/providers/env-file.js +24 -75
  101. package/dist/providers/env-file.js.map +1 -1
  102. package/dist/providers/runtime-env.d.ts +4 -2
  103. package/dist/providers/runtime-env.js +9 -6
  104. package/dist/providers/runtime-env.js.map +1 -1
  105. package/dist/providers/tables.d.ts +3 -1
  106. package/dist/providers/tables.js +4 -2
  107. package/dist/providers/tables.js.map +1 -1
  108. package/dist/redact.js +56 -23
  109. package/dist/redact.js.map +1 -1
  110. package/dist/release.d.ts +7 -0
  111. package/dist/release.js +19 -3
  112. package/dist/release.js.map +1 -1
  113. package/dist/runtime.d.ts +5 -1
  114. package/dist/runtime.js +303 -96
  115. package/dist/runtime.js.map +1 -1
  116. package/dist/server.js +31 -18
  117. package/dist/server.js.map +1 -1
  118. package/dist/sessions/anchor.d.ts +11 -4
  119. package/dist/sessions/anchor.js +33 -37
  120. package/dist/sessions/anchor.js.map +1 -1
  121. package/dist/sessions/background-events.d.ts +2 -0
  122. package/dist/sessions/background-events.js +10 -0
  123. package/dist/sessions/background-events.js.map +1 -0
  124. package/dist/sessions/background-tasks.d.ts +49 -23
  125. package/dist/sessions/background-tasks.js +161 -89
  126. package/dist/sessions/background-tasks.js.map +1 -1
  127. package/dist/sessions/background-updates.d.ts +15 -8
  128. package/dist/sessions/background-updates.js +101 -25
  129. package/dist/sessions/background-updates.js.map +1 -1
  130. package/dist/sessions/cli-sessions.d.ts +23 -6
  131. package/dist/sessions/cli-sessions.js +84 -84
  132. package/dist/sessions/cli-sessions.js.map +1 -1
  133. package/dist/sessions/completions.d.ts +38 -2
  134. package/dist/sessions/completions.js +348 -77
  135. package/dist/sessions/completions.js.map +1 -1
  136. package/dist/sessions/compress.d.ts +8 -0
  137. package/dist/sessions/compress.js +2 -2
  138. package/dist/sessions/compress.js.map +1 -1
  139. package/dist/sessions/drafts.d.ts +14 -10
  140. package/dist/sessions/drafts.js +38 -43
  141. package/dist/sessions/drafts.js.map +1 -1
  142. package/dist/sessions/export.d.ts +2 -0
  143. package/dist/sessions/export.js +6 -2
  144. package/dist/sessions/export.js.map +1 -1
  145. package/dist/sessions/gateway-sessions.d.ts +86 -0
  146. package/dist/sessions/gateway-sessions.js +914 -0
  147. package/dist/sessions/gateway-sessions.js.map +1 -0
  148. package/dist/sessions/gateway-watcher.d.ts +10 -11
  149. package/dist/sessions/gateway-watcher.js +25 -21
  150. package/dist/sessions/gateway-watcher.js.map +1 -1
  151. package/dist/sessions/goals.d.ts +31 -0
  152. package/dist/sessions/goals.js +75 -0
  153. package/dist/sessions/goals.js.map +1 -0
  154. package/dist/sessions/journal.d.ts +5 -79
  155. package/dist/sessions/journal.js +15 -606
  156. package/dist/sessions/journal.js.map +1 -1
  157. package/dist/sessions/list.d.ts +34 -18
  158. package/dist/sessions/list.js +161 -147
  159. package/dist/sessions/list.js.map +1 -1
  160. package/dist/sessions/merge.d.ts +77 -13
  161. package/dist/sessions/merge.js +315 -65
  162. package/dist/sessions/merge.js.map +1 -1
  163. package/dist/sessions/notices.d.ts +22 -0
  164. package/dist/sessions/notices.js +72 -0
  165. package/dist/sessions/notices.js.map +1 -0
  166. package/dist/sessions/pending.d.ts +10 -4
  167. package/dist/sessions/pending.js +83 -7
  168. package/dist/sessions/pending.js.map +1 -1
  169. package/dist/sessions/prefix-index.d.ts +85 -0
  170. package/dist/sessions/prefix-index.js +243 -0
  171. package/dist/sessions/prefix-index.js.map +1 -0
  172. package/dist/sessions/recovery.d.ts +4 -7
  173. package/dist/sessions/recovery.js +100 -283
  174. package/dist/sessions/recovery.js.map +1 -1
  175. package/dist/sessions/relay.d.ts +20 -20
  176. package/dist/sessions/relay.js +205 -127
  177. package/dist/sessions/relay.js.map +1 -1
  178. package/dist/sessions/service.d.ts +251 -78
  179. package/dist/sessions/service.js +1510 -564
  180. package/dist/sessions/service.js.map +1 -1
  181. package/dist/sessions/session.d.ts +20 -2
  182. package/dist/sessions/session.js +33 -5
  183. package/dist/sessions/session.js.map +1 -1
  184. package/dist/sessions/shares.d.ts +7 -6
  185. package/dist/sessions/shares.js +22 -45
  186. package/dist/sessions/shares.js.map +1 -1
  187. package/dist/sessions/source-kind.js +1 -0
  188. package/dist/sessions/source-kind.js.map +1 -1
  189. package/dist/sessions/state-db-identity.d.ts +18 -0
  190. package/dist/sessions/state-db-identity.js +67 -0
  191. package/dist/sessions/state-db-identity.js.map +1 -0
  192. package/dist/sessions/state-db-worker.js +24 -5
  193. package/dist/sessions/state-db-worker.js.map +1 -1
  194. package/dist/sessions/state-db.d.ts +157 -21
  195. package/dist/sessions/state-db.js +1145 -204
  196. package/dist/sessions/state-db.js.map +1 -1
  197. package/dist/sessions/state-saved.d.ts +1 -1
  198. package/dist/sessions/state-saved.js +35 -11
  199. package/dist/sessions/state-saved.js.map +1 -1
  200. package/dist/sessions/storage/backend.d.ts +171 -0
  201. package/dist/sessions/storage/backend.js +19 -0
  202. package/dist/sessions/storage/backend.js.map +1 -0
  203. package/dist/sessions/storage/conformance.d.ts +20 -0
  204. package/dist/sessions/storage/conformance.js +237 -0
  205. package/dist/sessions/storage/conformance.js.map +1 -0
  206. package/dist/sessions/storage/document.d.ts +16 -0
  207. package/dist/sessions/storage/document.js +44 -0
  208. package/dist/sessions/storage/document.js.map +1 -0
  209. package/dist/sessions/storage/json-documents.d.ts +25 -0
  210. package/dist/sessions/storage/json-documents.js +277 -0
  211. package/dist/sessions/storage/json-documents.js.map +1 -0
  212. package/dist/sessions/storage/json-files.d.ts +23 -0
  213. package/dist/sessions/storage/json-files.js +244 -0
  214. package/dist/sessions/storage/json-files.js.map +1 -0
  215. package/dist/sessions/storage/json-installation.d.ts +3 -0
  216. package/dist/sessions/storage/json-installation.js +31 -0
  217. package/dist/sessions/storage/json-installation.js.map +1 -0
  218. package/dist/sessions/storage/json-journal.d.ts +113 -0
  219. package/dist/sessions/storage/json-journal.js +756 -0
  220. package/dist/sessions/storage/json-journal.js.map +1 -0
  221. package/dist/sessions/storage/json-recovery.d.ts +4 -0
  222. package/dist/sessions/storage/json-recovery.js +195 -0
  223. package/dist/sessions/storage/json-recovery.js.map +1 -0
  224. package/dist/sessions/storage/json.d.ts +82 -0
  225. package/dist/sessions/storage/json.js +350 -0
  226. package/dist/sessions/storage/json.js.map +1 -0
  227. package/dist/sessions/storage/row.d.ts +6 -0
  228. package/dist/sessions/storage/row.js +49 -0
  229. package/dist/sessions/storage/row.js.map +1 -0
  230. package/dist/sessions/storage/search-text.d.ts +4 -0
  231. package/dist/sessions/storage/search-text.js +16 -0
  232. package/dist/sessions/storage/search-text.js.map +1 -0
  233. package/dist/sessions/store.d.ts +67 -73
  234. package/dist/sessions/store.js +341 -607
  235. package/dist/sessions/store.js.map +1 -1
  236. package/dist/sessions/streams.d.ts +12 -7
  237. package/dist/sessions/streams.js +58 -15
  238. package/dist/sessions/streams.js.map +1 -1
  239. package/dist/sessions/titles.js +1 -1
  240. package/dist/sessions/titles.js.map +1 -1
  241. package/dist/sessions/todo.d.ts +5 -0
  242. package/dist/sessions/todo.js +28 -21
  243. package/dist/sessions/todo.js.map +1 -1
  244. package/dist/sessions/tool-display.d.ts +8 -5
  245. package/dist/sessions/tool-display.js +28 -16
  246. package/dist/sessions/tool-display.js.map +1 -1
  247. package/dist/sessions/turn-context.d.ts +10 -1
  248. package/dist/sessions/turn-context.js +13 -6
  249. package/dist/sessions/turn-context.js.map +1 -1
  250. package/dist/sessions/turn.d.ts +110 -22
  251. package/dist/sessions/turn.js +918 -380
  252. package/dist/sessions/turn.js.map +1 -1
  253. package/dist/sessions/wakeup-pause.d.ts +8 -13
  254. package/dist/sessions/wakeup-pause.js +31 -49
  255. package/dist/sessions/wakeup-pause.js.map +1 -1
  256. package/dist/sessions/window.js +13 -12
  257. package/dist/sessions/window.js.map +1 -1
  258. package/dist/settings.d.ts +19 -7
  259. package/dist/settings.js +179 -183
  260. package/dist/settings.js.map +1 -1
  261. package/dist/sidecar/client.d.ts +41 -4
  262. package/dist/sidecar/client.js +192 -42
  263. package/dist/sidecar/client.js.map +1 -1
  264. package/dist/sidecar/discover.d.ts +2 -1
  265. package/dist/sidecar/discover.js +93 -10
  266. package/dist/sidecar/discover.js.map +1 -1
  267. package/dist/sidecar/env.d.ts +4 -0
  268. package/dist/sidecar/env.js +3 -0
  269. package/dist/sidecar/env.js.map +1 -0
  270. package/dist/sidecar/fake.d.ts +6 -2
  271. package/dist/sidecar/fake.js +34 -4
  272. package/dist/sidecar/fake.js.map +1 -1
  273. package/dist/spa.d.ts +9 -4
  274. package/dist/spa.js +36 -12
  275. package/dist/spa.js.map +1 -1
  276. package/dist/startup-timing.d.ts +66 -0
  277. package/dist/startup-timing.js +129 -0
  278. package/dist/startup-timing.js.map +1 -0
  279. package/dist/test/env-file.d.ts +5 -0
  280. package/dist/test/env-file.js +88 -0
  281. package/dist/test/env-file.js.map +1 -0
  282. package/dist/test/fs-handles.d.ts +11 -0
  283. package/dist/test/fs-handles.js +31 -0
  284. package/dist/test/fs-handles.js.map +1 -0
  285. package/dist/test/harness.d.ts +1 -0
  286. package/dist/test/harness.js +1 -1
  287. package/dist/test/harness.js.map +1 -1
  288. package/dist/test/passkeys.d.ts +22 -0
  289. package/dist/test/passkeys.js +62 -0
  290. package/dist/test/passkeys.js.map +1 -0
  291. package/dist/test/storage.d.ts +15 -0
  292. package/dist/test/storage.js +26 -0
  293. package/dist/test/storage.js.map +1 -0
  294. package/dist/tools/commands.d.ts +6 -1
  295. package/dist/tools/commands.js +8 -2
  296. package/dist/tools/commands.js.map +1 -1
  297. package/dist/tools/connectors.d.ts +33 -0
  298. package/dist/tools/connectors.js +121 -0
  299. package/dist/tools/connectors.js.map +1 -0
  300. package/dist/tools/crons.d.ts +46 -5
  301. package/dist/tools/crons.js +100 -26
  302. package/dist/tools/crons.js.map +1 -1
  303. package/dist/tools/dashboard-plugins.d.ts +34 -5
  304. package/dist/tools/dashboard-plugins.js +146 -14
  305. package/dist/tools/dashboard-plugins.js.map +1 -1
  306. package/dist/tools/extensions.d.ts +5 -4
  307. package/dist/tools/extensions.js +213 -207
  308. package/dist/tools/extensions.js.map +1 -1
  309. package/dist/tools/gateway-restart.d.ts +82 -0
  310. package/dist/tools/gateway-restart.js +222 -0
  311. package/dist/tools/gateway-restart.js.map +1 -0
  312. package/dist/tools/health.d.ts +28 -4
  313. package/dist/tools/health.js +171 -50
  314. package/dist/tools/health.js.map +1 -1
  315. package/dist/tools/hygiene.d.ts +4 -3
  316. package/dist/tools/hygiene.js +23 -9
  317. package/dist/tools/hygiene.js.map +1 -1
  318. package/dist/tools/insights.d.ts +1 -1
  319. package/dist/tools/insights.js +2 -2
  320. package/dist/tools/insights.js.map +1 -1
  321. package/dist/tools/kanban.d.ts +4 -2
  322. package/dist/tools/kanban.js +28 -19
  323. package/dist/tools/kanban.js.map +1 -1
  324. package/dist/tools/mcp-health.js +74 -24
  325. package/dist/tools/mcp-health.js.map +1 -1
  326. package/dist/tools/mcp.js +19 -1
  327. package/dist/tools/mcp.js.map +1 -1
  328. package/dist/tools/memory-masks.d.ts +5 -0
  329. package/dist/tools/memory-masks.js +256 -0
  330. package/dist/tools/memory-masks.js.map +1 -0
  331. package/dist/tools/memory.d.ts +29 -3
  332. package/dist/tools/memory.js +69 -27
  333. package/dist/tools/memory.js.map +1 -1
  334. package/dist/tools/plugin-oauth.d.ts +14 -0
  335. package/dist/tools/plugin-oauth.js +53 -0
  336. package/dist/tools/plugin-oauth.js.map +1 -0
  337. package/dist/tools/plugin-settings.d.ts +15 -0
  338. package/dist/tools/plugin-settings.js +90 -0
  339. package/dist/tools/plugin-settings.js.map +1 -0
  340. package/dist/tools/prune-advice.d.ts +50 -0
  341. package/dist/tools/prune-advice.js +114 -0
  342. package/dist/tools/prune-advice.js.map +1 -0
  343. package/dist/tools/skills.d.ts +13 -5
  344. package/dist/tools/skills.js +53 -19
  345. package/dist/tools/skills.js.map +1 -1
  346. package/dist/tools/update-notifications.d.ts +102 -17
  347. package/dist/tools/update-notifications.js +400 -82
  348. package/dist/tools/update-notifications.js.map +1 -1
  349. package/dist/tools/update-rollback.d.ts +11 -0
  350. package/dist/tools/update-rollback.js +28 -0
  351. package/dist/tools/update-rollback.js.map +1 -0
  352. package/dist/tools/updates.d.ts +100 -33
  353. package/dist/tools/updates.js +1085 -345
  354. package/dist/tools/updates.js.map +1 -1
  355. package/dist/util.d.ts +2 -0
  356. package/dist/util.js +5 -0
  357. package/dist/util.js.map +1 -1
  358. package/dist/workspace/git.d.ts +16 -1
  359. package/dist/workspace/git.js +291 -11
  360. package/dist/workspace/git.js.map +1 -1
  361. package/dist/workspace/media.d.ts +12 -9
  362. package/dist/workspace/media.js +98 -38
  363. package/dist/workspace/media.js.map +1 -1
  364. package/dist/workspace/preview.js +4 -14
  365. package/dist/workspace/preview.js.map +1 -1
  366. package/dist/workspace/rollback.d.ts +8 -4
  367. package/dist/workspace/rollback.js +22 -12
  368. package/dist/workspace/rollback.js.map +1 -1
  369. package/dist/workspace/workspaces.d.ts +15 -19
  370. package/dist/workspace/workspaces.js +55 -127
  371. package/dist/workspace/workspaces.js.map +1 -1
  372. package/dist/workspace/worktrees.js +170 -32
  373. package/dist/workspace/worktrees.js.map +1 -1
  374. package/package.json +6 -4
  375. package/sidecar/agent_dependency.json +4 -4
  376. package/sidecar/agent_support.json +29 -0
  377. package/sidecar/scripts/record-fixtures.py +12 -3
  378. package/sidecar/scripts/replay_sidecar.py +8 -0
  379. package/sidecar/scripts/suite_runner.py +458 -0
  380. package/sidecar/scripts/test.sh +11 -5
  381. package/sidecar/talaria_sidecar/__init__.py +6 -1
  382. package/sidecar/talaria_sidecar/__main__.py +13 -4
  383. package/sidecar/talaria_sidecar/agent_mutation.py +1111 -0
  384. package/sidecar/talaria_sidecar/agent_services.py +1116 -0
  385. package/sidecar/talaria_sidecar/context_detail.py +59 -0
  386. package/sidecar/talaria_sidecar/dependency_sync.py +85 -0
  387. package/sidecar/talaria_sidecar/home.py +133 -14
  388. package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
  389. package/sidecar/talaria_sidecar/methods/aux.py +211 -41
  390. package/sidecar/talaria_sidecar/methods/chat.py +1378 -200
  391. package/sidecar/talaria_sidecar/methods/checkpoints.py +29 -4
  392. package/sidecar/talaria_sidecar/methods/commands.py +483 -13
  393. package/sidecar/talaria_sidecar/methods/config.py +195 -6
  394. package/sidecar/talaria_sidecar/methods/connectors.py +375 -0
  395. package/sidecar/talaria_sidecar/methods/cron.py +374 -36
  396. package/sidecar/talaria_sidecar/methods/cron_execution.py +284 -0
  397. package/sidecar/talaria_sidecar/methods/dashboard_plugins.py +301 -0
  398. package/sidecar/talaria_sidecar/methods/gateway.py +215 -55
  399. package/sidecar/talaria_sidecar/methods/gateway_sessions.py +418 -0
  400. package/sidecar/talaria_sidecar/methods/goals.py +81 -41
  401. package/sidecar/talaria_sidecar/methods/kanban.py +82 -30
  402. package/sidecar/talaria_sidecar/methods/memory.py +84 -0
  403. package/sidecar/talaria_sidecar/methods/oauth.py +144 -13
  404. package/sidecar/talaria_sidecar/methods/plugin_catalog.py +198 -0
  405. package/sidecar/talaria_sidecar/methods/plugin_oauth.py +234 -0
  406. package/sidecar/talaria_sidecar/methods/process.py +229 -16
  407. package/sidecar/talaria_sidecar/methods/profiles.py +48 -9
  408. package/sidecar/talaria_sidecar/methods/providers.py +196 -26
  409. package/sidecar/talaria_sidecar/methods/runtime.py +71 -1
  410. package/sidecar/talaria_sidecar/methods/skills.py +83 -5
  411. package/sidecar/talaria_sidecar/methods/state_db.py +405 -112
  412. package/sidecar/talaria_sidecar/methods/stt.py +159 -78
  413. package/sidecar/talaria_sidecar/methods/text.py +14 -1
  414. package/sidecar/talaria_sidecar/methods/toolsets.py +51 -0
  415. package/sidecar/talaria_sidecar/methods/usage.py +113 -14
  416. package/sidecar/talaria_sidecar/methods/worktree.py +145 -19
  417. package/sidecar/talaria_sidecar/methods/write_approval.py +181 -0
  418. package/sidecar/talaria_sidecar/rpc.py +70 -7
  419. package/sidecar/talaria_sidecar/runtime.py +122 -14
  420. package/sidecar/talaria_sidecar/speed.py +47 -0
  421. package/sidecar/talaria_sidecar/startup_timing.py +119 -0
  422. package/static/dist/FILES.txt +242 -215
  423. package/static/dist/THIRD_PARTY_LICENSES.md +6 -22
  424. package/static/dist/assets/AppShell-3isgvoro.js +8 -0
  425. package/static/dist/assets/Button-f4n6dapf.js +1 -0
  426. package/static/dist/assets/ChatPage-k7b6xfax.js +4 -0
  427. package/static/dist/assets/ConnectTargets-juqvt5lm.js +1 -0
  428. package/static/dist/assets/Dialog-hta5utr3.js +1 -0
  429. package/static/dist/assets/ExtensionRoute-zrhjn0wm.js +1 -0
  430. package/static/dist/assets/Field-b6ez48oz.js +1 -0
  431. package/static/dist/assets/HubRoute-iwvso2tz.js +2 -0
  432. package/static/dist/assets/LinkCheckContext-ix6zqqkk.js +1 -0
  433. package/static/dist/assets/Markdown-jdeth7zq.js +2 -0
  434. package/static/dist/assets/MarkdownRenderer-fbswsgld.js +302 -0
  435. package/static/dist/assets/MarkdownRenderer-i9o6nr38.css +1 -0
  436. package/static/dist/assets/OAuthSignIn-bpd8hom3.js +1 -0
  437. package/static/dist/assets/ResourceSwitch-odi6dy2o.js +1 -0
  438. package/static/dist/assets/RightPanel-eu6enlo6.js +1 -0
  439. package/static/dist/assets/Select-kr07928w.js +1 -0
  440. package/static/dist/assets/SessionListPanel-etb2i54l.js +1 -0
  441. package/static/dist/assets/SettingsLayout-eu3xxp23.js +1 -0
  442. package/static/dist/assets/States-icab19yg.js +1 -0
  443. package/static/dist/assets/TerminalPanel-hngmsr8b.js +16 -0
  444. package/static/dist/assets/Toaster-nb2uzjw1.js +1 -0
  445. package/static/dist/assets/_app-ga1moszl.js +1 -0
  446. package/static/dist/assets/_app.ext._extensionId-na62087t.js +1 -0
  447. package/static/dist/assets/_app.index-jypgdqoi.js +1 -0
  448. package/static/dist/assets/_app.insights-kabejdtm.js +1 -0
  449. package/static/dist/assets/_app.kanban-m19kfcmx.js +1 -0
  450. package/static/dist/assets/_app.logs-iw4m96sz.js +1 -0
  451. package/static/dist/assets/_app.memory-jq5frwcg.js +1 -0
  452. package/static/dist/assets/_app.profiles-kwa3m7rp.js +1 -0
  453. package/static/dist/assets/_app.session._sessionId-o5lyeipj.js +1 -0
  454. package/static/dist/assets/_app.settings-ckl8hdll.js +1 -0
  455. package/static/dist/assets/_app.settings._section-dtdjk98a.js +3 -0
  456. package/static/dist/assets/_app.skills-n016rihd.js +1 -0
  457. package/static/dist/assets/_app.tasks-n8zuqsqe.js +1 -0
  458. package/static/dist/assets/_app.todos-btew5v2s.js +1 -0
  459. package/static/dist/assets/_app.workspaces-lnfk95ch.js +1 -0
  460. package/static/dist/assets/abnfDiagram-VCTEODGH-dttheegb.js +1 -0
  461. package/static/dist/assets/{angular-html-nu6l31w3.js → angular-html-l28bd5fo.js} +1 -1
  462. package/static/dist/assets/{angular-ts-fbx4f8mm.js → angular-ts-ffzx1vj0.js} +1 -1
  463. package/static/dist/assets/{apl-bx2xr7dh.js → apl-b7h7mmy4.js} +1 -1
  464. package/static/dist/assets/{arc-7popfhbk.js → arc-fk1b21lm.js} +1 -1
  465. package/static/dist/assets/architecture-7GRP2DOG-ic435919.js +1 -0
  466. package/static/dist/assets/architectureDiagram-5GKGNRK7-8r3glgiq.js +36 -0
  467. package/static/dist/assets/archive-il7c6053.js +1 -0
  468. package/static/dist/assets/{astro-g77i97cb.js → astro-k117fy0t.js} +1 -1
  469. package/static/dist/assets/back-gu3nu279.js +1 -0
  470. package/static/dist/assets/{blade-boo0u92y.js → blade-npefsmn8.js} +1 -1
  471. package/static/dist/assets/{blockDiagram-I7D4REHJ-jzthp1wa.js → blockDiagram-I7D4REHJ-jenyos6c.js} +4 -4
  472. package/static/dist/assets/{c-cat0w3sc.js → c-2umg6kbn.js} +1 -1
  473. package/static/dist/assets/{c4Diagram-7LVT6UL2-mwr67mt8.js → c4Diagram-7LVT6UL2-nzlx7yc4.js} +1 -1
  474. package/static/dist/assets/cancel-bsnfo36u.js +1 -0
  475. package/static/dist/assets/channel-dsbtk01y.js +1 -0
  476. package/static/dist/assets/{chunk-4HAMMTFA-gpq1y228.js → chunk-4HAMMTFA-g0cru63b.js} +20 -20
  477. package/static/dist/assets/{chunk-75Z2AOVW-ktje58c4.js → chunk-75Z2AOVW-7rkswkjw.js} +1 -1
  478. package/static/dist/assets/chunk-DU6HZSFF-fn73jzdo.js +128 -0
  479. package/static/dist/assets/{chunk-F27PBJKO-cmev85ti.js → chunk-F27PBJKO-oey8e72m.js} +1 -1
  480. package/static/dist/assets/{chunk-FOHPRMQF-opuflp73.js → chunk-FOHPRMQF-ipewusgj.js} +8 -8
  481. package/static/dist/assets/{chunk-GMAD6QVW-htjgt22y.js → chunk-GMAD6QVW-l2a32wiy.js} +6 -6
  482. package/static/dist/assets/{chunk-GVQU2GXP-qndvoxl7.js → chunk-GVQU2GXP-o9dwzh8h.js} +1 -1
  483. package/static/dist/assets/{chunk-IMKFNOWR-e5bkd5q6.js → chunk-IMKFNOWR-eblwj20b.js} +5 -5
  484. package/static/dist/assets/{chunk-L3NEJ4N5-mj1155r6.js → chunk-L3NEJ4N5-o2mufspb.js} +1 -1
  485. package/static/dist/assets/chunk-OSK3NFVY-ijp9pw29.js +10 -0
  486. package/static/dist/assets/{chunk-P2QGCYS3-f7t54xfm.js → chunk-P2QGCYS3-hs9hqwl4.js} +1 -1
  487. package/static/dist/assets/{chunk-POPQ4Y6H-i927v9oa.js → chunk-POPQ4Y6H-bp9rsqez.js} +1 -1
  488. package/static/dist/assets/{chunk-PWAF6VOD-m84lmnyt.js → chunk-PWAF6VOD-brkprov6.js} +1 -1
  489. package/static/dist/assets/{chunk-SHT3W25Y-mi0tlcu9.js → chunk-SHT3W25Y-gmpv0pxz.js} +13 -13
  490. package/static/dist/assets/{chunk-SVP7TREG-dnau702j.js → chunk-SVP7TREG-oldm08fu.js} +4 -4
  491. package/static/dist/assets/{chunk-TICWLB2K-edem1gkg.js → chunk-TICWLB2K-ooqilfv7.js} +6 -6
  492. package/static/dist/assets/{chunk-XXDRQBXY-n40pf9vy.js → chunk-XXDRQBXY-jkmnqu65.js} +1 -1
  493. package/static/dist/assets/classDiagram-ZZMXUADV-fvgcv24g.js +1 -0
  494. package/static/dist/assets/classDiagram-v2-VYDZK3BY-fvgcv24g.js +1 -0
  495. package/static/dist/assets/{cobol-go622aep.js → cobol-ssf9hkh7.js} +1 -1
  496. package/static/dist/assets/{coffee-jwo1xjhq.js → coffee-lsi9fwae.js} +1 -1
  497. package/static/dist/assets/copied-bz3gkazh.js +1 -0
  498. package/static/dist/assets/copy-jo006coq.js +1 -0
  499. package/static/dist/assets/cose-bilkent-JH36ORCC-layy2r27.js +1 -0
  500. package/static/dist/assets/{cpp-nre7j0kq.js → cpp-lfrh4y55.js} +1 -1
  501. package/static/dist/assets/{crystal-lviso2je.js → crystal-efh4pte7.js} +1 -1
  502. package/static/dist/assets/{css-ywz9j4hb.js → css-i82qdo89.js} +1 -1
  503. package/static/dist/assets/cynefin-OW5HDTMX-d47op7qs.js +1 -0
  504. package/static/dist/assets/cynefinDiagram-5FMLGOSQ-doa7594y.js +62 -0
  505. package/static/dist/assets/cytoscape.esm-fjn2algp.js +321 -0
  506. package/static/dist/assets/dagre-GXQ25YYZ-os768dp4.js +4 -0
  507. package/static/dist/assets/dagre-o5jmjz46.js +1 -0
  508. package/static/dist/assets/{diagram-S7CK7UJ4-l8abxaes.js → diagram-S7CK7UJ4-fyi81nxd.js} +4 -4
  509. package/static/dist/assets/diagram-UQ7AKVKN-j3exggaq.js +41 -0
  510. package/static/dist/assets/{diagram-VSXAHHWV-fnky6lvc.js → diagram-VSXAHHWV-omo1en2l.js} +1 -1
  511. package/static/dist/assets/diagram-VX7I27RA-oro2cyu0.js +24 -0
  512. package/static/dist/assets/diagram-Z3DM3KII-byt9vdb7.js +24 -0
  513. package/static/dist/assets/dist-crc46di9.js +7 -0
  514. package/static/dist/assets/dist-etpoyscq.js +44 -0
  515. package/static/dist/assets/{dist-c4c2m3kv.js → dist-nb670eu6.js} +1 -1
  516. package/static/dist/assets/ebnfDiagram-PWID7BFC-ee3haugh.js +1 -0
  517. package/static/dist/assets/{edge-dvv35byi.js → edge-lrob635n.js} +1 -1
  518. package/static/dist/assets/{elixir-i2f1ki1g.js → elixir-kdpxeq93.js} +1 -1
  519. package/static/dist/assets/{elm-cpepyjm4.js → elm-c8irj9uh.js} +1 -1
  520. package/static/dist/assets/erDiagram-RLTQ6QDP-d1e93yff.js +99 -0
  521. package/static/dist/assets/{erb-idlyfheg.js → erb-flwsv146.js} +1 -1
  522. package/static/dist/assets/eventmodeling-NTZA5JFV-i1ihb40c.js +1 -0
  523. package/static/dist/assets/flowDiagram-HODETNUW-jahfpiod.js +1 -0
  524. package/static/dist/assets/folder-plus-ivoglq98.js +1 -0
  525. package/static/dist/assets/{ganttDiagram-EL5Y4UJY-ngbsyp37.js → ganttDiagram-EL5Y4UJY-m7g0jud4.js} +1 -1
  526. package/static/dist/assets/{git-rebase-d9lpfpbn.js → git-rebase-bli19aoq.js} +1 -1
  527. package/static/dist/assets/gitGraph-4MIJSDKK-hzdj07yy.js +1 -0
  528. package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-eravdti4.js → gitGraphDiagram-WWUBYQGX-dm9agbna.js} +1 -1
  529. package/static/dist/assets/{glimmer-js-39i0urfb.js → glimmer-js-d4icg38p.js} +1 -1
  530. package/static/dist/assets/{glimmer-ts-gdtd6n73.js → glimmer-ts-bk75772u.js} +1 -1
  531. package/static/dist/assets/{glsl-o1djm5pp.js → glsl-lqormp2b.js} +1 -1
  532. package/static/dist/assets/{graphql-kj6rz55t.js → graphql-hw6s5qzd.js} +1 -1
  533. package/static/dist/assets/{hack-pe1qqr2i.js → hack-ozrv6l4d.js} +1 -1
  534. package/static/dist/assets/{haml-cknnbcqs.js → haml-gytfosdt.js} +1 -1
  535. package/static/dist/assets/{handlebars-hwq64orh.js → handlebars-b6g7jffd.js} +1 -1
  536. package/static/dist/assets/highlighted-body-KPVGNVTW-mmgz2czu.js +1 -0
  537. package/static/dist/assets/host-fr6lni1p.js +1 -0
  538. package/static/dist/assets/{html-derivative-h2i2pyjc.js → html-derivative-f1tqcroo.js} +1 -1
  539. package/static/dist/assets/{html-ha05xtvt.js → html-on6oj6yj.js} +1 -1
  540. package/static/dist/assets/{http-cf94zu27.js → http-odhufsl7.js} +1 -1
  541. package/static/dist/assets/{hurl-lslax568.js → hurl-mu680336.js} +1 -1
  542. package/static/dist/assets/index-73649w7o.js +10 -0
  543. package/static/dist/assets/index-lhf1fsdf.css +2 -0
  544. package/static/dist/assets/info-A6RAGUB7-lll1dqmm.js +1 -0
  545. package/static/dist/assets/infoDiagram-27XIBGKW-k7esc639.js +2 -0
  546. package/static/dist/assets/{ishikawaDiagram-5VMMS53U-i6ypq351.js → ishikawaDiagram-5VMMS53U-lgr6k9wo.js} +6 -6
  547. package/static/dist/assets/{java-ndjf2o89.js → java-nxbe8uju.js} +1 -1
  548. package/static/dist/assets/{javascript-coftlwy3.js → javascript-lz3kxcyz.js} +1 -1
  549. package/static/dist/assets/{jinja-etei9wy7.js → jinja-l5pk84gt.js} +1 -1
  550. package/static/dist/assets/{jison-hyekgft1.js → jison-jdutdwyy.js} +1 -1
  551. package/static/dist/assets/{journeyDiagram-3NMN7TZE-lduf1z95.js → journeyDiagram-3NMN7TZE-ndmbxuqk.js} +1 -1
  552. package/static/dist/assets/{json-lok0yaho.js → json-dnge4ssf.js} +1 -1
  553. package/static/dist/assets/{jsx-b64w0nbx.js → jsx-c3lwhkep.js} +1 -1
  554. package/static/dist/assets/{jsx-runtime-eu92a5pu.js → jsx-runtime-o40ukmjl.js} +1 -1
  555. package/static/dist/assets/{julia-jxxzz2yn.js → julia-hwd4rrcb.js} +1 -1
  556. package/static/dist/assets/{just-mc29iw9t.js → just-nka3oxdx.js} +1 -1
  557. package/static/dist/assets/{kanban-definition-UXKFOSKX-e18bu4pg.js → kanban-definition-UXKFOSKX-mjxym0jp.js} +28 -28
  558. package/static/dist/assets/katex-cbdd1ohw.css +1 -0
  559. package/static/dist/assets/katex-pbij2oo6.js +257 -0
  560. package/static/dist/assets/{latex-pafa03ft.js → latex-iqmr7e40.js} +1 -1
  561. package/static/dist/assets/lazyRouteComponent-cwb6l7xh.js +1 -0
  562. package/static/dist/assets/{line-meizqsvn.js → line-mtr5k6il.js} +1 -1
  563. package/static/dist/assets/linear-jlwrvvy6.js +1 -0
  564. package/static/dist/assets/link-iadw4ghw.js +2 -0
  565. package/static/dist/assets/{liquid-hz5c2pov.js → liquid-okmk9tbp.js} +1 -1
  566. package/static/dist/assets/locale-cs-n41hptse.js +5 -0
  567. package/static/dist/assets/locale-de-k7ht47ew.js +5 -0
  568. package/static/dist/assets/locale-es-ih078lco.js +5 -0
  569. package/static/dist/assets/locale-fr-ojabnf41.js +5 -0
  570. package/static/dist/assets/locale-it-kb96jufy.js +5 -0
  571. package/static/dist/assets/locale-ja-jpsp3xs1.js +7 -0
  572. package/static/dist/assets/locale-ko-cpzuy0n7.js +5 -0
  573. package/static/dist/assets/locale-pl-dbap4leu.js +5 -0
  574. package/static/dist/assets/locale-pt-nci7w72x.js +5 -0
  575. package/static/dist/assets/locale-ru-soghe4cs.js +7 -0
  576. package/static/dist/assets/locale-tr-ks00mu68.js +5 -0
  577. package/static/dist/assets/locale-vi-ij7twfm6.js +5 -0
  578. package/static/dist/assets/locale-zh-Hant-feakwr7a.js +5 -0
  579. package/static/dist/assets/locale-zh-ren0vjyv.js +7 -0
  580. package/static/dist/assets/login-lkhtzfcl.js +1 -0
  581. package/static/dist/assets/{lua-leyf8mlb.js → lua-krx4xj5h.js} +1 -1
  582. package/static/dist/assets/{marko-cn1fzpdv.js → marko-gbvpfehz.js} +1 -1
  583. package/static/dist/assets/{mdc-huhy88df.js → mdc-c21zlyi5.js} +1 -1
  584. package/static/dist/assets/mermaid-HWGCJPDP-jx0q5dm2.js +1 -0
  585. package/static/dist/assets/mermaid-parser.core-k91vdnvi.js +7 -0
  586. package/static/dist/assets/micromark-factory-space-fnmnpi6s.js +1 -0
  587. package/static/dist/assets/{mindmap-definition-YA3MSWOX-27b26eok.js → mindmap-definition-YA3MSWOX-ehztfea4.js} +28 -28
  588. package/static/dist/assets/{nginx-e7lvhsmn.js → nginx-f3d68ed4.js} +1 -1
  589. package/static/dist/assets/{nim-dun5d8mk.js → nim-dw0jcy70.js} +1 -1
  590. package/static/dist/assets/onboarding-f67z0a2r.js +1 -0
  591. package/static/dist/assets/packet-AYTQ26CC-ok2xrblz.js +1 -0
  592. package/static/dist/assets/pcmTap.worklet-74EKjWAZ.js +1 -0
  593. package/static/dist/assets/pegDiagram-XKGWAZYB-ec3rpj0m.js +1 -0
  594. package/static/dist/assets/{perl-bj3oqtet.js → perl-3c0r45c8.js} +1 -1
  595. package/static/dist/assets/{php-buzp12r0.js → php-rznhidvp.js} +1 -1
  596. package/static/dist/assets/pie-WAS4IAKB-iwkir23u.js +1 -0
  597. package/static/dist/assets/pieDiagram-E7YTZNPT-fcpqpekh.js +39 -0
  598. package/static/dist/assets/{pug-mjad7he9.js → pug-f2le4f3y.js} +1 -1
  599. package/static/dist/assets/{qml-d1gdp10s.js → qml-e2wbq9cr.js} +1 -1
  600. package/static/dist/assets/{quadrantDiagram-AXDQQJYC-hhue7d8o.js → quadrantDiagram-AXDQQJYC-lqsync43.js} +1 -1
  601. package/static/dist/assets/queryKeys-ed4n2egc.js +9 -0
  602. package/static/dist/assets/{r-o3r9c7ao.js → r-epfzuw6q.js} +1 -1
  603. package/static/dist/assets/radar-RG4KPBEZ-hje4jbk6.js +1 -0
  604. package/static/dist/assets/railroad-74A4TZTK-gll9n6qi.js +1 -0
  605. package/static/dist/assets/railroad-abnf-HS5TGJTU-o6h6lpn4.js +1 -0
  606. package/static/dist/assets/railroad-ebnf-LZEXJU2U-juaefj6o.js +1 -0
  607. package/static/dist/assets/railroad-peg-WCYAUIDC-cgfhvpah.js +1 -0
  608. package/static/dist/assets/railroadDiagram-O6MQD6OU-lp3w7awf.js +1 -0
  609. package/static/dist/assets/{razor-ilc585yr.js → razor-pea1u1d0.js} +1 -1
  610. package/static/dist/assets/refresh-cw-iccm7a4e.js +1 -0
  611. package/static/dist/assets/refresh-i9ya9dvl.js +1 -0
  612. package/static/dist/assets/{regexp-mpjtcvwm.js → regexp-hzte0a5x.js} +1 -1
  613. package/static/dist/assets/registry-g7w3353u.js +1 -0
  614. package/static/dist/assets/requirementDiagram-BXWQKSXE-ha8eibcf.js +84 -0
  615. package/static/dist/assets/rolldown-runtime-bnnw9q4a.js +1 -0
  616. package/static/dist/assets/{rst-jrkwjdyi.js → rst-m6tugj5o.js} +1 -1
  617. package/static/dist/assets/{ruby-n4tjcbpz.js → ruby-l19vq3w1.js} +1 -1
  618. package/static/dist/assets/runtime-nlxpnegr.js +1 -0
  619. package/static/dist/assets/{sankeyDiagram-P5KCCOFB-fqb8xcjf.js → sankeyDiagram-P5KCCOFB-oxf31pr9.js} +1 -1
  620. package/static/dist/assets/{sas-hjo6y93k.js → sas-ktdxo397.js} +1 -1
  621. package/static/dist/assets/{scss-ncidjv48.js → scss-kn3oxfwf.js} +1 -1
  622. package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-dlcz6qjz.js → sequenceDiagram-WJ2MYXX4-jiq8lhyo.js} +11 -11
  623. package/static/dist/assets/settings_section_extensions_title-fadc7o65.js +1 -0
  624. package/static/dist/assets/share._token-o76ki0ly.js +2 -0
  625. package/static/dist/assets/{shellscript-m8whjpqd.js → shellscript-l100jtrg.js} +1 -1
  626. package/static/dist/assets/{shellsession-i5bj6syz.js → shellsession-m5qukwow.js} +1 -1
  627. package/static/dist/assets/sizeCapture-INFHLROL-dk1io13w.js +1 -0
  628. package/static/dist/assets/sliders-horizontal-ddcs88av.js +1 -0
  629. package/static/dist/assets/{soy-hhh36ysq.js → soy-mexlscur.js} +1 -1
  630. package/static/dist/assets/{sql-j21bzcl6.js → sql-o0ho5suh.js} +1 -1
  631. package/static/dist/assets/src-h89s0hs4.js +1 -0
  632. package/static/dist/assets/{stata-bbu2onh1.js → stata-be9ck1q3.js} +1 -1
  633. package/static/dist/assets/stateDiagram-D77RDMKH-f3fslnon.js +1 -0
  634. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-hlt6yphw.js +1 -0
  635. package/static/dist/assets/{surrealql-l5vczuco.js → surrealql-gk1xkkyy.js} +1 -1
  636. package/static/dist/assets/{svelte-idn422g6.js → svelte-k6ecjilf.js} +1 -1
  637. package/static/dist/assets/swimlanes-42K2YHIH-k1hmk7jf.js +1 -0
  638. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-hd5t9tnt.js +8 -0
  639. package/static/dist/assets/{templ-e22qou3c.js → templ-gf3bftk6.js} +1 -1
  640. package/static/dist/assets/{tex-deqpc5ee.js → tex-d21fqv1z.js} +1 -1
  641. package/static/dist/assets/{timeline-definition-24CTP7MA-b1vl2zil.js → timeline-definition-24CTP7MA-n3ncfy9r.js} +8 -8
  642. package/static/dist/assets/toast-liparz2j.js +56 -0
  643. package/static/dist/assets/treeView-Q6P3EWNA-l3y2pejt.js +1 -0
  644. package/static/dist/assets/treemap-WGGIJYW6-jown9axh.js +1 -0
  645. package/static/dist/assets/{ts-tags-ctnibc27.js → ts-tags-n0g61gr6.js} +1 -1
  646. package/static/dist/assets/{tsx-i163n32g.js → tsx-cvv7wv6q.js} +1 -1
  647. package/static/dist/assets/{twig-x0fi7wm8.js → twig-n1f67204.js} +1 -1
  648. package/static/dist/assets/{typescript-g18c34xz.js → typescript-og7o55cx.js} +1 -1
  649. package/static/dist/assets/untitled-mj86r4gz.js +1 -0
  650. package/static/dist/assets/useDebounced-g8r0ggve.js +1 -0
  651. package/static/dist/assets/useForm-o4uu74mx.js +1 -0
  652. package/static/dist/assets/{useParams-b4dq6n4q.js → useParams-ftsveu84.js} +1 -1
  653. package/static/dist/assets/{useSearch-ipfmv3he.js → useSearch-crqueqga.js} +1 -1
  654. package/static/dist/assets/vennDiagram-4TSXK5OY-dh59uq1c.js +34 -0
  655. package/static/dist/assets/{vue-mwu5l1bb.js → vue-ej4xpwqw.js} +1 -1
  656. package/static/dist/assets/{vue-html-nskj3du0.js → vue-html-d48xsjuo.js} +1 -1
  657. package/static/dist/assets/{vue-vine-cqe77smq.js → vue-vine-pev88ip6.js} +1 -1
  658. package/static/dist/assets/wardley-WFR3VGLG-e6d74b0j.js +1 -0
  659. package/static/dist/assets/wardleyDiagram-VM6X3IG4-hnofh78f.js +78 -0
  660. package/static/dist/assets/with-selector-nu1b5z0v.js +1 -0
  661. package/static/dist/assets/{xml-gau7gvrv.js → xml-ifxp6gqb.js} +1 -1
  662. package/static/dist/assets/{xsl-ew09zq8b.js → xsl-fug2cckc.js} +1 -1
  663. package/static/dist/assets/{xychartDiagram-S5SC5T6Z-e49523ab.js → xychartDiagram-S5SC5T6Z-bpymjelb.js} +6 -6
  664. package/static/dist/assets/{yaml-ofnrxtvf.js → yaml-fjmafnlo.js} +1 -1
  665. package/static/dist/extension-sdk.js +1 -1
  666. package/static/dist/index.html +1 -1
  667. package/static/dist/sw.js +180 -136
  668. package/static/dist/assets/AppShell-g3bkgnvt.js +0 -1
  669. package/static/dist/assets/ChatPage-oo7bdan6.js +0 -25
  670. package/static/dist/assets/Dialog-g355qxhp.js +0 -1
  671. package/static/dist/assets/ExtensionRoute-i2y9fvjm.js +0 -1
  672. package/static/dist/assets/HubRoute-lmkrbw47.js +0 -2
  673. package/static/dist/assets/Markdown-j4kut4j2.js +0 -598
  674. package/static/dist/assets/Markdown-lwviulmg.css +0 -1
  675. package/static/dist/assets/RightPanel-ohlsdzfl.js +0 -1
  676. package/static/dist/assets/Select-kddq75qc.js +0 -1
  677. package/static/dist/assets/SessionListPanel-lu3oy0hw.js +0 -1
  678. package/static/dist/assets/SettingsLayout-o9bv4mgb.js +0 -1
  679. package/static/dist/assets/States-eq6fe29m.js +0 -1
  680. package/static/dist/assets/Toaster-pc247u9f.js +0 -1
  681. package/static/dist/assets/_app-f8n1i0m1.js +0 -1
  682. package/static/dist/assets/_app.ext._extensionId-gfbzpoou.js +0 -1
  683. package/static/dist/assets/_app.index-bzzv80hk.js +0 -1
  684. package/static/dist/assets/_app.insights-dzixbryi.js +0 -1
  685. package/static/dist/assets/_app.kanban-c6hep2us.js +0 -1
  686. package/static/dist/assets/_app.logs-i69qds98.js +0 -1
  687. package/static/dist/assets/_app.memory-kl4vf0gd.js +0 -1
  688. package/static/dist/assets/_app.profiles-b8zinmyp.js +0 -1
  689. package/static/dist/assets/_app.session._sessionId-fn1bhde6.js +0 -1
  690. package/static/dist/assets/_app.settings-cg8plwmf.js +0 -1
  691. package/static/dist/assets/_app.settings._section-bkwbi9zn.js +0 -3
  692. package/static/dist/assets/_app.skills-cs47sbvs.js +0 -1
  693. package/static/dist/assets/_app.tasks-dwvk64tc.js +0 -1
  694. package/static/dist/assets/_app.todos-c8xjm8p3.js +0 -1
  695. package/static/dist/assets/_app.workspaces-ehvd1gds.js +0 -1
  696. package/static/dist/assets/abnfDiagram-VCTEODGH-c8kdixke.js +0 -1
  697. package/static/dist/assets/architecture-7GRP2DOG-6crvnylg.js +0 -1
  698. package/static/dist/assets/architectureDiagram-5GKGNRK7-cku06x1p.js +0 -36
  699. package/static/dist/assets/back-h7aun7ew.js +0 -1
  700. package/static/dist/assets/cancel-fhi0vcnu.js +0 -1
  701. package/static/dist/assets/channel-b2zo73z1.js +0 -1
  702. package/static/dist/assets/chunk-DU6HZSFF-ofajdixh.js +0 -128
  703. package/static/dist/assets/chunk-OSK3NFVY-e7gkikcw.js +0 -10
  704. package/static/dist/assets/classDiagram-ZZMXUADV-oe99st8x.js +0 -1
  705. package/static/dist/assets/classDiagram-v2-VYDZK3BY-oe99st8x.js +0 -1
  706. package/static/dist/assets/copied-hcplv46p.js +0 -1
  707. package/static/dist/assets/copy-magtofyp.js +0 -1
  708. package/static/dist/assets/cose-bilkent-JH36ORCC-eclcy6ja.js +0 -1
  709. package/static/dist/assets/cynefin-OW5HDTMX-siqlulkl.js +0 -1
  710. package/static/dist/assets/cynefinDiagram-5FMLGOSQ-i648tah9.js +0 -62
  711. package/static/dist/assets/cytoscape.esm-bqu53u12.js +0 -321
  712. package/static/dist/assets/dagre-GXQ25YYZ-mwn6uc17.js +0 -4
  713. package/static/dist/assets/dagre-lkoknbjn.js +0 -1
  714. package/static/dist/assets/diagram-UQ7AKVKN-mekq7zgq.js +0 -41
  715. package/static/dist/assets/diagram-VX7I27RA-xe6r5qfw.js +0 -24
  716. package/static/dist/assets/diagram-Z3DM3KII-nq07dg7k.js +0 -24
  717. package/static/dist/assets/ebnfDiagram-PWID7BFC-c62ipm3g.js +0 -1
  718. package/static/dist/assets/endpoints-klbxod4t.js +0 -64
  719. package/static/dist/assets/erDiagram-RLTQ6QDP-h39kmnzy.js +0 -99
  720. package/static/dist/assets/eventmodeling-NTZA5JFV-obfede4u.js +0 -1
  721. package/static/dist/assets/flowDiagram-HODETNUW-kdpsp1je.js +0 -1
  722. package/static/dist/assets/gitGraph-4MIJSDKK-fr2i7caa.js +0 -1
  723. package/static/dist/assets/highlighted-body-KPVGNVTW-jwpop9p8.js +0 -1
  724. package/static/dist/assets/host-i828xuo7.js +0 -1
  725. package/static/dist/assets/index-eh8zilnv.css +0 -2
  726. package/static/dist/assets/index-r648f4o1.js +0 -11
  727. package/static/dist/assets/info-A6RAGUB7-h11x0dxo.js +0 -1
  728. package/static/dist/assets/infoDiagram-27XIBGKW-dfh47dw9.js +0 -2
  729. package/static/dist/assets/lazyRouteComponent-e1y4t2i9.js +0 -1
  730. package/static/dist/assets/linear-becwlnhk.js +0 -1
  731. package/static/dist/assets/link-oot2yn73.js +0 -1
  732. package/static/dist/assets/login-gu28l9ou.js +0 -1
  733. package/static/dist/assets/mermaid-HWGCJPDP-mtwrj8fn.js +0 -1
  734. package/static/dist/assets/mermaid-parser.core-ntne45i9.js +0 -7
  735. package/static/dist/assets/not-found-ccnnac39.js +0 -1
  736. package/static/dist/assets/onboarding-lkphzu1s.js +0 -1
  737. package/static/dist/assets/onboarding_model_label-ftpjdwqk.js +0 -1
  738. package/static/dist/assets/packet-AYTQ26CC-l6jdka8p.js +0 -1
  739. package/static/dist/assets/pegDiagram-XKGWAZYB-g1er3bxj.js +0 -1
  740. package/static/dist/assets/pie-WAS4IAKB-i0ylcrt9.js +0 -1
  741. package/static/dist/assets/pieDiagram-E7YTZNPT-fbz1e55z.js +0 -39
  742. package/static/dist/assets/radar-RG4KPBEZ-f9b70aa4.js +0 -1
  743. package/static/dist/assets/railroad-74A4TZTK-lgsuoeda.js +0 -1
  744. package/static/dist/assets/railroad-abnf-HS5TGJTU-nf9d6pdx.js +0 -1
  745. package/static/dist/assets/railroad-ebnf-LZEXJU2U-f7ummkt5.js +0 -1
  746. package/static/dist/assets/railroad-peg-WCYAUIDC-oczq5tqs.js +0 -1
  747. package/static/dist/assets/railroadDiagram-O6MQD6OU-eqpy9fnz.js +0 -1
  748. package/static/dist/assets/refresh-abtu93ke.js +0 -1
  749. package/static/dist/assets/refresh-cw-hlwb02qv.js +0 -1
  750. package/static/dist/assets/requirementDiagram-BXWQKSXE-ogf6puhi.js +0 -84
  751. package/static/dist/assets/rolldown-runtime-b9miacsj.js +0 -1
  752. package/static/dist/assets/root-mbklpuv4.js +0 -1
  753. package/static/dist/assets/saved-girnabkf.js +0 -1
  754. package/static/dist/assets/settings_section_extensions_title-h14cwk6f.js +0 -1
  755. package/static/dist/assets/share._token-kl0r7ido.js +0 -2
  756. package/static/dist/assets/sizeCapture-INFHLROL-g612l48j.js +0 -1
  757. package/static/dist/assets/sliders-horizontal-n1zm34qq.js +0 -1
  758. package/static/dist/assets/src-hjyib4mu.js +0 -1
  759. package/static/dist/assets/sse-iwku0sjo.js +0 -1
  760. package/static/dist/assets/stateDiagram-D77RDMKH-fyoznpcs.js +0 -1
  761. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-ixhx8r46.js +0 -1
  762. package/static/dist/assets/swimlanes-42K2YHIH-oefldhfy.js +0 -1
  763. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-h9ersk2e.js +0 -8
  764. package/static/dist/assets/todoStore-jjr5yr78.js +0 -1
  765. package/static/dist/assets/treeView-Q6P3EWNA-f7hcci5o.js +0 -1
  766. package/static/dist/assets/treemap-WGGIJYW6-nl85071a.js +0 -1
  767. package/static/dist/assets/units-kgxzxfhi.js +0 -1
  768. package/static/dist/assets/untitled-cikfjntn.js +0 -1
  769. package/static/dist/assets/useForm-kqoqg0k9.js +0 -1
  770. package/static/dist/assets/useLocale-o59tbiea.js +0 -1
  771. package/static/dist/assets/vennDiagram-4TSXK5OY-h5hzbk0n.js +0 -34
  772. package/static/dist/assets/wardley-WFR3VGLG-jftvpm3e.js +0 -1
  773. package/static/dist/assets/wardleyDiagram-VM6X3IG4-hedh85ge.js +0 -78
  774. /package/static/dist/assets/{ChatPage-gmu5c7lf.css → TerminalPanel-gmu5c7lf.css} +0 -0
@@ -1,21 +1,10 @@
1
+ import { markedStateDbGeneration } from './state-db-identity.js';
1
2
  import { str } from '../util.js';
2
- /**
3
- * The WebUI session store (Python `api/models.py`): `sessions/<sid>.json`
4
- * sidecars written atomically with a `.bak` on shrink, the sorted
5
- * `sessions/_index.json` sidebar index with incremental patching, a bounded
6
- * LRU of resident sessions that never evicts active or unsaved work, the
7
- * deleted-session tombstone, composer draft overlays, and per-session
8
- * mutation locks.
9
- *
10
- * The state.db projection (CLI sessions, lineage metadata, transcript
11
- * reconciliation) is not part of this store; it layers on in the sidebar
12
- * builder once the read-only SQLite projection lands.
13
- */
14
- import { closeSync, existsSync, fstatSync, fsyncSync, mkdirSync, openSync, readdirSync, readFileSync, renameSync, rmSync, statSync, unlinkSync } from 'node:fs';
15
- import { writeFully } from '../fs/atomic.js';
16
- import { basename, join } from 'node:path';
17
3
  import { sanitizeSessionRow } from './list.js';
4
+ import { withBackgroundDisplayKinds } from './background-updates.js';
18
5
  import { anchorSceneIndexFromRecords, collapseAdjacentDuplicatePartials, isSafeSessionId, Session } from './session.js';
6
+ import { RevisionConflict } from './storage/backend.js';
7
+ import { sessionSearchMatches, sessionSearchMessageText } from './storage/search-text.js';
19
8
  export class SessionNotFound extends Error {
20
9
  sid;
21
10
  constructor(sid) {
@@ -24,175 +13,54 @@ export class SessionNotFound extends Error {
24
13
  this.name = 'SessionNotFound';
25
14
  }
26
15
  }
27
- const LOAD_STABLE_READ_ATTEMPTS = 3;
28
16
  const UNSAVED_SHELL_GRACE_S = 1800;
29
- const STALE_TMP_AGE_S = 3600;
30
- const DELETED_TOMBSTONE_CAP = 1000;
31
- const DELETED_TOMBSTONE_VERSION = 1;
32
- let tmpCounter = 0;
33
- function tmpName(path) {
34
- return `${path}.tmp.${process.pid}.${++tmpCounter}`;
35
- }
36
- function writeAtomic(path, text) {
37
- const tmp = tmpName(path);
38
- const fd = openSync(tmp, 'w');
39
- try {
40
- writeFully(fd, text);
41
- fsyncSync(fd);
42
- closeSync(fd);
43
- renameSync(tmp, path);
44
- }
45
- catch (error) {
46
- try {
47
- closeSync(fd);
48
- }
49
- catch { /* closed */ }
50
- try {
51
- unlinkSync(tmp);
52
- }
53
- catch { /* gone */ }
54
- throw error;
55
- }
56
- }
57
- export function statSignature(path) {
58
- try {
59
- const st = statSync(path, { bigint: true });
60
- return `${path}:${st.mtimeNs}:${st.size}:${st.ctimeNs}`;
61
- }
62
- catch {
63
- return null;
64
- }
65
- }
66
- /** Python `_read_metadata_json_prefix`: everything before the top-level `messages` (or `anchor_activity_scenes`) key. */
67
- export function readMetadataJsonPrefix(path, maxBytes = 1024 * 1024) {
68
- return readMetadataJsonPrefixWithSignature(path, maxBytes).prefix;
69
- }
70
- /**
71
- * The metadata prefix together with the signature of the inode it was read from (fstat of the open descriptor), so a
72
- * file atomically replaced during the read is never cached under the replacement's identity.
73
- */
74
- export function readMetadataJsonPrefixWithSignature(path, maxBytes = 1024 * 1024) {
75
- const fd = openSync(path, 'r');
76
- try {
77
- const st = fstatSync(fd, { bigint: true });
78
- const signature = `${path}:${st.mtimeNs}:${st.size}:${st.ctimeNs}`;
79
- return { prefix: readPrefixFromFd(fd, maxBytes, Math.min(64 * 1024, maxBytes)), signature };
80
- }
81
- finally {
82
- closeSync(fd);
83
- }
84
- }
85
- function readPrefixFromFd(fd, maxBytes, initialStage) {
86
- let stage = initialStage;
87
- {
88
- let raw = Buffer.alloc(0);
89
- let text = '';
90
- let stopPos = null;
91
- while (raw.length < maxBytes) {
92
- const chunk = Buffer.alloc(Math.min(stage, maxBytes - raw.length));
93
- const n = readSyncFully(fd, chunk);
94
- if (n === 0)
95
- return null;
96
- raw = Buffer.concat([raw, chunk.subarray(0, n)]);
97
- text = raw.toString('utf8');
98
- const messagesPos = findTopLevelJsonKey(text, 'messages');
99
- const scenesPos = findTopLevelJsonKey(text, 'anchor_activity_scenes');
100
- stopPos = messagesPos;
101
- if (scenesPos !== null && (stopPos === null || scenesPos < stopPos))
102
- stopPos = scenesPos;
103
- if (stopPos !== null)
104
- break;
105
- stage *= 2;
106
- }
107
- if (stopPos === null)
108
- return null;
109
- let prefix = text.slice(0, stopPos).trimEnd();
110
- if (prefix.endsWith(','))
111
- prefix = prefix.slice(0, -1).trimEnd();
112
- return `${prefix}\n}`;
113
- }
114
- }
115
- import { readSync } from 'node:fs';
116
- function readSyncFully(fd, buf) {
117
- let total = 0;
118
- while (total < buf.length) {
119
- const n = readSync(fd, buf, total, buf.length - total, null);
120
- if (n === 0)
121
- break;
122
- total += n;
123
- }
124
- return total;
125
- }
126
- /** Offset of `"key":` at JSON nesting depth 1, or null. */
127
- export function findTopLevelJsonKey(text, key) {
128
- let depth = 0;
129
- let inString = false;
130
- let escape = false;
131
- let stringStart = -1;
132
- for (let i = 0; i < text.length; i += 1) {
133
- const ch = text[i];
134
- if (inString) {
135
- if (escape)
136
- escape = false;
137
- else if (ch === '\\')
138
- escape = true;
139
- else if (ch === '"') {
140
- inString = false;
141
- if (depth === 1 && text.slice(stringStart + 1, i) === key) {
142
- let j = i + 1;
143
- while (j < text.length && /\s/.test(text[j] ?? ''))
144
- j += 1;
145
- if (text[j] === ':')
146
- return stringStart;
147
- }
148
- }
149
- continue;
150
- }
151
- if (ch === '"') {
152
- inString = true;
153
- stringStart = i;
154
- continue;
155
- }
156
- if (ch === '{' || ch === '[')
157
- depth += 1;
158
- else if (ch === '}' || ch === ']')
159
- depth -= 1;
160
- }
161
- return null;
162
- }
163
- export function readPersistedMessageCount(path) {
164
- try {
165
- const prefix = readMetadataJsonPrefix(path);
166
- if (!prefix)
167
- return null;
168
- const value = JSON.parse(prefix).message_count;
169
- if (typeof value === 'boolean')
170
- return null;
171
- const n = Number(value);
172
- return Number.isInteger(n) && n >= 0 ? n : null;
173
- }
174
- catch {
175
- return null;
176
- }
177
- }
178
17
  export class SessionStore {
179
18
  deps;
180
- sessionDir;
181
- indexFile;
182
- /** Resident sessions in LRU order (oldest first). */
183
19
  sessions = new Map();
184
20
  locks = new Map();
185
- persistedIdsCache = null;
186
- parsedIndexCache = null;
187
- indexFlush = null;
21
+ writes = new Map();
22
+ resolutions = new Map();
23
+ persistedMessages = new WeakMap();
24
+ indexWriter = Promise.resolve();
188
25
  indexPending = new Map();
26
+ indexFlush = null;
27
+ indexFailure = null;
189
28
  constructor(deps) {
190
29
  this.deps = deps;
191
- this.sessionDir = deps.sessionDir;
192
- this.indexFile = join(deps.sessionDir, '_index.json');
193
30
  }
194
- pathFor(sid) {
195
- return join(this.sessionDir, `${sid}.json`);
31
+ get backend() { return this.deps.storage.sessions; }
32
+ async exists(sid) { return this.backend.exists(sid); }
33
+ async revision(sid) { return this.backend.revision(sid); }
34
+ readMessages(sid, range) { return this.backend.readMessages(sid, range); }
35
+ /** Readers retry rows a failed batch left queued; a still-failing index reads as unreadable, never as current. */
36
+ async index() {
37
+ await this.queueIndexUpdate([]);
38
+ // The joined flush may itself have failed; retry what it left queued once.
39
+ if (this.indexPending.size)
40
+ await this.queueIndexUpdate([]);
41
+ if (this.indexFailure && this.indexPending.size)
42
+ throw this.indexFailure;
43
+ return this.backend.index();
44
+ }
45
+ async hasIndex() { return this.backend.hasIndex(); }
46
+ get shrinkBackup() { return this.backend.shrinkBackup; }
47
+ async diagnose() { return this.backend.diagnose(); }
48
+ async repair(issue) {
49
+ const repaired = await this.backend.repair(issue);
50
+ if (repaired)
51
+ this.sessions.delete(issue.session_id);
52
+ return repaired;
53
+ }
54
+ async close() {
55
+ try {
56
+ await Promise.allSettled(this.resolutions.values());
57
+ await Promise.allSettled(this.writes.values());
58
+ await this.indexFlush;
59
+ await this.indexWriter;
60
+ }
61
+ finally {
62
+ await this.deps.storage.close();
63
+ }
196
64
  }
197
65
  // ── locks ────────────────────────────────────────────────────────────────
198
66
  /** Serialize one session's read-modify-write sequences (Python `_get_session_agent_lock`). */
@@ -222,17 +90,17 @@ export class SessionStore {
222
90
  }
223
91
  }
224
92
  // ── creation / resolution ────────────────────────────────────────────────
225
- construct(init, profile) {
226
- const session = new Session(init, this.deps.defaults(profile));
93
+ async construct(init, profile) {
94
+ const session = new Session(init, (await this.deps.defaults(profile)));
227
95
  // New-session defaults must not replace the title supplied by the read projection.
228
96
  if (typeof init.title === 'string')
229
97
  session.title = init.title;
230
98
  return session;
231
99
  }
232
100
  /** In-memory only until the first message is persisted (Python `new_session`). */
233
- newSession(opts = {}) {
101
+ async newSession(opts = {}) {
234
102
  const profile = opts.profile ?? null;
235
- const defaults = this.deps.defaults(profile);
103
+ const defaults = (await this.deps.defaults(profile));
236
104
  const wt = opts.worktree ?? null;
237
105
  const s = new Session({
238
106
  workspace: (wt ? wt.path : opts.workspace) || defaults.workspace,
@@ -247,97 +115,82 @@ export class SessionStore {
247
115
  worktree_created_at: wt?.created_at ?? null,
248
116
  enabled_toolsets: opts.enabledToolsets ?? null,
249
117
  }, defaults);
250
- this.clearDeletedTombstone(s.session_id);
251
- this.touch(s);
118
+ await this.clearDeletedTombstone(s.session_id);
119
+ await this.touch(s);
252
120
  if (wt)
253
- this.save(s);
121
+ (await this.save(s));
254
122
  return s;
255
123
  }
256
124
  /** Insert or refresh a session in the LRU and evict over the cap. */
257
- touch(session) {
125
+ async touch(session) {
258
126
  this.sessions.delete(session.session_id);
259
127
  this.sessions.set(session.session_id, session);
260
- this.evictOverCap();
128
+ await this.evictOverCap();
261
129
  }
262
- load(sid) {
130
+ async load(sid, opts = {}) {
263
131
  if (!isSafeSessionId(sid))
264
132
  return null;
265
- const path = this.pathFor(sid);
266
- if (!existsSync(path))
267
- return null;
268
- // Another process may replace the file atomically while we read it: retry until one read is bracketed by the
269
- // same signature. A read that never stabilises keeps a null signature, which `cachedLagsDisk` treats as stale.
270
- let data = {};
271
- let signature = null;
272
- for (let attempt = 0; attempt < LOAD_STABLE_READ_ATTEMPTS; attempt += 1) {
273
- const preSig = statSignature(path);
274
- data = JSON.parse(readFileSync(path, 'utf8'));
275
- const postSig = statSignature(path);
276
- if (preSig !== null && preSig === postSig) {
277
- signature = postSig;
278
- break;
133
+ for (let attempt = 0; attempt < 3; attempt += 1) {
134
+ const stored = await this.backend.readDocument(sid);
135
+ if (!stored)
136
+ return null;
137
+ const data = sanitizeSessionRow(stored.doc, this, sid);
138
+ if (data?.session_id !== sid)
139
+ return null;
140
+ const messages = stored.messages;
141
+ if (stored.revision === null || stored.revision !== await this.revision(sid))
142
+ continue;
143
+ const [rows, collapsed] = collapseAdjacentDuplicatePartials(messages);
144
+ // Keep the parsed baseline independent of in-place edits to the resident session.
145
+ data.messages = structuredClone(rows);
146
+ const session = (await this.construct(data, data.profile ?? null));
147
+ session.sidecarLoadedSignature = stored.revision;
148
+ this.persistedMessages.set(session, { rows: messages });
149
+ if (collapsed && (opts.repairDuplicates ?? true)) {
150
+ try {
151
+ await this.save(session, { touchUpdatedAt: false, skipIndex: true });
152
+ }
153
+ catch { /* best effort */ }
279
154
  }
280
- }
281
- const clean = sanitizeSessionRow(data, this, basename(path));
282
- if (!clean)
283
- return null;
284
- data = clean;
285
- const [messages, collapsed] = collapseAdjacentDuplicatePartials(data.messages);
286
- data.messages = messages;
287
- const session = this.construct(data, data.profile ?? null);
288
- if (collapsed) {
289
155
  try {
290
- this.save(session, { touchUpdatedAt: false, skipIndex: true });
156
+ if (opts.drafts ?? true)
157
+ session.composer_draft = (await this.deps.drafts.read(sid, session.composer_draft));
291
158
  }
292
- catch { /* best effort */ }
293
- }
294
- else {
295
- session.sidecarLoadedSignature = signature;
296
- }
297
- try {
298
- session.composer_draft = this.deps.drafts.read(sid, session.composer_draft);
159
+ catch { /* ignore */ }
160
+ return session;
299
161
  }
300
- catch { /* ignore */ }
301
- return session;
162
+ throw new RevisionConflict(sid);
302
163
  }
303
- loadMetadataOnly(sid, opts = {}) {
164
+ async loadMetadataOnly(sid, opts = {}) {
304
165
  if (!isSafeSessionId(sid))
305
166
  return null;
306
- const path = this.pathFor(sid);
307
- if (!existsSync(path))
308
- return null;
309
167
  try {
310
- const { prefix, signature } = readMetadataJsonPrefixWithSignature(path);
311
- if (!prefix)
312
- return this.load(sid);
313
- const parsed = sanitizeSessionRow(JSON.parse(prefix), this, basename(path));
314
- if (!parsed)
168
+ const stored = await this.backend.readDocument(sid, { metadataOnly: true });
169
+ if (!stored)
170
+ return null;
171
+ if (!stored.metadataOnly)
172
+ return (await this.load(sid));
173
+ const parsed = sanitizeSessionRow(stored.doc, this, sid);
174
+ if (parsed?.session_id !== sid)
315
175
  return null;
316
- for (const key of ['session_id', 'title', 'created_at', 'updated_at'])
317
- if (!(key in parsed))
318
- return this.load(sid);
319
- const sidecarCount = parsed.message_count;
320
- const modernCount = typeof sidecarCount === 'number' && Number.isInteger(sidecarCount) && sidecarCount >= 0 ? sidecarCount : null;
321
- if (modernCount === null && !('anchor_scene_index' in parsed))
322
- return this.load(sid);
323
176
  parsed.messages = [];
324
177
  parsed.tool_calls = [];
325
- const session = this.construct(parsed, parsed.profile ?? null);
326
- let indexCount = null;
327
- if (modernCount === null)
328
- indexCount = (opts.indexMessageCounts ?? this.indexMessageCounts()).get(sid) ?? null;
329
- const known = [indexCount, modernCount].filter((c) => c !== null);
330
- session.metadataMessageCount = known.length ? Math.max(...known) : null;
178
+ const session = (await this.construct(parsed, parsed.profile ?? null));
179
+ session.metadataMessageCount = stored.messageCount ?? (opts.indexMessageCounts ?? await this.indexMessageCounts()).get(sid) ?? null;
331
180
  session.loadedMetadataOnly = true;
332
- session.sidecarLoadedSignature = signature;
181
+ session.sidecarLoadedSignature = stored.revision;
333
182
  return session;
334
183
  }
335
184
  catch {
336
- return this.load(sid);
185
+ return (await this.load(sid));
337
186
  }
338
187
  }
339
- /** Resolve a session through the cache with disk-freshness checks (Python `get_session`). Throws `SessionNotFound`. */
340
188
  get(sid, opts = {}) {
189
+ return this.serializeSession(sid, () => this.getNow(sid, opts), this.resolutions);
190
+ }
191
+ async getNow(sid, opts) {
192
+ // A local commit updates the resident revision before a reader can mistake it for an external edit.
193
+ await this.writes.get(sid)?.catch(() => { });
341
194
  const cached = this.sessions.get(sid);
342
195
  if (cached) {
343
196
  if (cached.session_id !== sid) {
@@ -349,15 +202,13 @@ export class SessionStore {
349
202
  this.sessions.delete(sid);
350
203
  this.sessions.set(sid, cached);
351
204
  }
352
- const lags = this.cachedLagsDisk(cached);
353
- // A persisted session whose file vanished was deleted underneath us: evict it so nothing recreates the sidecar.
354
- if (lags && !existsSync(this.pathFor(sid))) {
205
+ const lags = await this.cachedLagsStorage(cached);
206
+ if (lags && !await this.exists(sid)) {
355
207
  this.sessions.delete(sid);
356
208
  throw new SessionNotFound(sid);
357
209
  }
358
- // A sidebar-only stub (metadata load) must be upgraded to the full transcript when messages are requested.
359
210
  if (!opts.metadataOnly && (cached.loadedMetadataOnly || lags)) {
360
- const fresh = this.load(sid);
211
+ const fresh = await this.load(sid);
361
212
  if (fresh) {
362
213
  this.sessions.set(sid, fresh);
363
214
  return fresh;
@@ -366,381 +217,292 @@ export class SessionStore {
366
217
  return cached;
367
218
  }
368
219
  }
369
- const loaded = opts.metadataOnly ? this.loadMetadataOnly(sid) : this.load(sid);
220
+ const loaded = opts.metadataOnly ? await this.loadMetadataOnly(sid) : await this.load(sid);
370
221
  if (!loaded)
371
222
  throw new SessionNotFound(sid);
372
223
  if (opts.cacheOnMiss ?? true)
373
- this.touch(loaded);
224
+ await this.touch(loaded);
374
225
  return loaded;
375
226
  }
376
- /** Reload a metadata-only stub before mutating persisted fields (Python `_ensure_full_session_before_mutation`). */
377
- ensureFull(sid, session) {
227
+ async ensureFull(sid, session) {
378
228
  if (!session.loadedMetadataOnly)
379
229
  return session;
380
- const full = this.load(sid);
230
+ const full = await this.load(sid);
381
231
  if (!full)
382
232
  throw new SessionNotFound(sid);
383
- this.touch(full);
233
+ await this.touch(full);
384
234
  return full;
385
235
  }
386
- /** A cached full session is stale when its sidecar changed on disk and it carries no unsaved runtime state. */
387
- cachedLagsDisk(cached) {
236
+ async cachedLagsStorage(cached) {
388
237
  if (cached.active_stream_id || cached.pending_user_message || cached.pending_started_at)
389
238
  return false;
390
- const current = statSignature(this.pathFor(cached.session_id));
391
- // A session that was persisted and whose file has since vanished was deleted underneath us: it is stale, and the
392
- // reload's miss evicts it rather than letting a later mutation recreate the deleted sidecar.
393
- if (current === null)
394
- return cached.sidecarLoadedSignature !== null;
395
- // An unknown read identity (the file changed underneath the load) must not be trusted: reload it.
396
- if (cached.sidecarLoadedSignature === null)
397
- return true;
398
- return current !== cached.sidecarLoadedSignature;
239
+ return await this.revision(cached.session_id) !== cached.sidecarLoadedSignature;
399
240
  }
400
- // ── persistence ──────────────────────────────────────────────────────────
401
241
  save(session, opts = {}) {
402
- if (!isSafeSessionId(session.session_id))
403
- throw new Error(`Unsafe session_id ${JSON.stringify(session.session_id)}; refusing to write outside session store`);
404
- if (session.loadedMetadataOnly) {
405
- throw new Error(`Refusing to save metadata-only session ${JSON.stringify(session.session_id)}: would atomically overwrite on-disk messages with []. Reload with metadata_only=False before mutating state. See #1558.`);
406
- }
407
- // TAL-493: a save carries a current state.db marker forward and never revives a stale one (an older release's save
408
- // moved `updated_at` past its stamp); only a fresh marker is made current.
242
+ return this.serializeSession(session.session_id, () => this.saveNow(session, opts));
243
+ }
244
+ serializeSession(sid, fn, queue = this.writes) {
245
+ const result = (queue.get(sid) ?? Promise.resolve()).then(fn);
246
+ const settled = result.then(() => { }, () => { });
247
+ queue.set(sid, settled);
248
+ return result.finally(() => { if (queue.get(sid) === settled)
249
+ queue.delete(sid); });
250
+ }
251
+ async saveNow(session, opts) {
252
+ const sid = session.session_id;
253
+ if (!isSafeSessionId(sid))
254
+ throw new Error(`Unsafe session_id ${JSON.stringify(sid)}; refusing to write outside session store`);
255
+ if (session.loadedMetadataOnly)
256
+ throw new Error(`Refusing to save metadata-only session ${JSON.stringify(sid)}: would overwrite messages with []. Reload before mutating state.`);
409
257
  const markerCurrent = session.state_db_seen_stamp === session.updated_at;
410
258
  if (opts.touchUpdatedAt ?? true)
411
259
  session.updated_at = this.deps.now();
412
260
  if (markerCurrent)
413
261
  session.state_db_seen_stamp = session.updated_at;
414
- const payload = JSON.stringify(session.toDocument(), null, 2);
415
- const path = session.path = this.pathFor(session.session_id);
416
- mkdirSync(this.sessionDir, { recursive: true });
417
- try {
418
- if (existsSync(path)) {
419
- const incoming = session.messages.length;
420
- let existingCount = readPersistedMessageCount(path);
421
- let existingText = null;
422
- if (existingCount === null) {
423
- existingText = readFileSync(path, 'utf8');
424
- try {
425
- const existing = JSON.parse(existingText);
426
- existingCount = Array.isArray(existing.messages) ? existing.messages.length : 0;
427
- }
428
- catch {
429
- existingCount = -1;
430
- }
431
- }
432
- if (existingCount > 0 && incoming === 0 && (session.active_stream_id || session.pending_user_message)) {
433
- this.deps.log(`[webui] WARNING: refusing to overwrite session ${session.session_id} messages with empty active/pending snapshot (existing=${existingCount}, incoming=${incoming}, stream=${session.active_stream_id ?? ''})`);
434
- return;
435
- }
436
- if (existingCount > incoming) {
437
- existingText ??= readFileSync(path, 'utf8');
438
- try {
439
- writeAtomic(`${path}.bak`, existingText);
440
- }
441
- catch { /* backup is best effort */ }
262
+ session.messages = withBackgroundDisplayKinds(session.messages, session);
263
+ const liveRows = session.messages, liveCalls = session.tool_calls, callCount = liveCalls.length;
264
+ const prefix = session.prefixIndex;
265
+ let appendOnly = Boolean(prefix && session.messages.length >= prefix.length && prefix.generation === markedStateDbGeneration(session) && prefix.matchesToolCalls(session.tool_calls));
266
+ const doc = structuredClone(session.toDocument());
267
+ const rows = doc.messages;
268
+ delete doc.messages;
269
+ // One serialization per row serves both the prefix check and the diff; the backend serializes the document once.
270
+ const snapshot = rows.map((row) => JSON.stringify(row));
271
+ if (appendOnly)
272
+ for (let i = 0; i < prefix.length; i += 1)
273
+ if (!prefix.matchesRow(i, snapshot[i])) {
274
+ appendOnly = false;
275
+ break;
442
276
  }
277
+ const indexEntry = session.compact();
278
+ const persisted = this.persistedMessages.get(session);
279
+ const previous = persisted && ('rows' in persisted ? persisted.rows.map((row) => JSON.stringify(row)) : persisted.serialized);
280
+ if (previous)
281
+ this.persistedMessages.set(session, { serialized: previous });
282
+ let messages = { kind: 'replace', rows };
283
+ if (previous) {
284
+ let from = 0;
285
+ while (from < previous.length && from < snapshot.length && previous[from] === snapshot[from])
286
+ from += 1;
287
+ if (from === previous.length)
288
+ messages = from === snapshot.length ? { kind: 'unchanged' } : { kind: 'append', rows: rows.slice(from) };
289
+ else if (snapshot.length >= previous.length) {
290
+ let tail = 0;
291
+ while (tail < previous.length - from && tail < snapshot.length - from && previous[previous.length - 1 - tail] === snapshot[snapshot.length - 1 - tail])
292
+ tail += 1;
293
+ messages = { kind: 'splice', from, deleteCount: previous.length - from - tail, rows: rows.slice(from, rows.length - tail) };
443
294
  }
444
295
  }
445
- catch { /* stat failures never block the save */ }
446
- writeAtomic(path, payload);
447
- session.sidecarLoadedSignature = statSignature(path);
448
- this.invalidatePersistedIds();
449
- if (!(opts.skipIndex ?? false))
450
- this.queueIndexUpdate([session]);
451
- if (session.messages.length)
452
- this.clearDeletedTombstone(session.session_id);
453
- }
454
- // ── index ────────────────────────────────────────────────────────────────
455
- cleanupStaleTmpFiles() {
456
- const cutoff = this.deps.now() - STALE_TMP_AGE_S;
457
- try {
458
- for (const name of readdirSync(this.sessionDir)) {
459
- if (!name.includes('.tmp.'))
460
- continue;
461
- const p = join(this.sessionDir, name);
462
- try {
463
- if (statSync(p).mtimeMs / 1000 < cutoff)
464
- unlinkSync(p);
465
- }
466
- catch { /* best effort */ }
296
+ const expected = session.sidecarLoadedSignature;
297
+ if (!rows.length && (session.active_stream_id || session.pending_user_message)) {
298
+ const stored = await this.backend.readDocument(sid, { metadataOnly: true });
299
+ if ((stored?.messageCount ?? 0) > 0) {
300
+ this.deps.log(`[webui] WARNING: refusing to overwrite session ${sid} messages with empty active/pending snapshot`);
301
+ return;
467
302
  }
468
303
  }
469
- catch { /* dir may not exist */ }
470
- }
471
- invalidatePersistedIds() {
472
- this.persistedIdsCache = null;
473
- }
474
- persistedIds() {
475
- let mtimeNs = null;
476
304
  try {
477
- mtimeNs = statSync(this.sessionDir, { bigint: true }).mtimeNs;
305
+ session.sidecarLoadedSignature = await this.backend.write(sid, { doc, messages, all: rows }, expected);
478
306
  }
479
- catch {
480
- mtimeNs = null;
307
+ catch (error) {
308
+ if (error instanceof RevisionConflict) {
309
+ this.sessions.delete(sid);
310
+ const fresh = await this.load(sid, { repairDuplicates: false });
311
+ if (fresh)
312
+ this.sessions.set(sid, fresh);
313
+ }
314
+ throw error;
481
315
  }
482
- if (this.persistedIdsCache?.mtimeNs === mtimeNs && mtimeNs !== null)
483
- return this.persistedIdsCache.ids;
484
- const ids = new Set();
485
- try {
486
- for (const name of readdirSync(this.sessionDir))
487
- if (name.endsWith('.json') && !name.startsWith('_'))
488
- ids.add(name.slice(0, -5));
316
+ this.persistedMessages.set(session, { serialized: snapshot });
317
+ if (appendOnly && session.messages === liveRows && liveRows.length === rows.length && session.tool_calls === liveCalls && liveCalls.length === callCount) {
318
+ prefix.savedAppend(liveRows, session.sidecarLoadedSignature, liveCalls);
489
319
  }
490
- catch { /* empty */ }
491
- this.persistedIdsCache = { mtimeNs, ids };
492
- return ids;
493
- }
494
- hasPersistedSessionFiles() {
495
- return this.persistedIds().size > 0;
496
- }
497
- indexSignature() {
498
- return statSignature(this.indexFile);
499
- }
500
- /** Parsed `_index.json` rows via the stat-validated cache; throws on a corrupt index. */
501
- readIndexEntries() {
502
- const signature = this.indexSignature();
503
- if (this.parsedIndexCache && signature !== null && this.parsedIndexCache.signature === signature)
504
- return [...this.parsedIndexCache.entries];
505
- const entries = JSON.parse(readFileSync(this.indexFile, 'utf8'));
506
- if (!Array.isArray(entries))
507
- throw new Error('session index must be a list');
508
- if (signature !== null && signature === this.indexSignature())
509
- this.parsedIndexCache = { signature, entries: entries };
510
- return [...entries];
511
- }
512
- indexMessageCounts(entries) {
513
- const out = new Map();
320
+ else
321
+ delete session.prefixIndex;
322
+ // The document is committed; the batched index flush must not delay the caller (the turn's `done`).
323
+ if (!(opts.skipIndex ?? false))
324
+ void this.queueIndexUpdate([indexEntry]);
325
+ if (rows.length)
326
+ await this.clearDeletedTombstone(sid);
327
+ }
328
+ async searchMessages(sids, terms, depth) {
329
+ const matches = new Map();
330
+ const remaining = [];
331
+ for (const sid of sids) {
332
+ const cached = this.sessions.get(sid);
333
+ if (!cached || cached.loadedMetadataOnly || await this.cachedLagsStorage(cached)) {
334
+ remaining.push(sid);
335
+ continue;
336
+ }
337
+ const rows = depth ? cached.messages.slice(0, depth) : cached.messages;
338
+ const idx = rows.findIndex((row) => sessionSearchMatches(sessionSearchMessageText(row), terms));
339
+ if (idx >= 0)
340
+ matches.set(sid, { idx, text: sessionSearchMessageText(rows[idx]) });
341
+ }
342
+ for (const [sid, match] of await this.backend.searchMessages(remaining, terms, depth))
343
+ matches.set(sid, match);
344
+ return matches;
345
+ }
346
+ async persistedIds(opts = {}) { return this.backend.ids(opts); }
347
+ async hasPersistedSessionFiles() { return (await this.persistedIds()).size > 0; }
348
+ async readIndexEntries() { return (await this.index()).entries; }
349
+ async indexMessageCounts(entries) {
514
350
  let rows = entries;
515
351
  if (!rows) {
516
352
  try {
517
- rows = existsSync(this.indexFile) ? this.readIndexEntries() : [];
353
+ rows = await this.readIndexEntries();
518
354
  }
519
355
  catch {
520
356
  rows = [];
521
357
  }
522
358
  }
359
+ const out = new Map();
523
360
  for (const row of rows) {
524
- const sid = str(row.session_id);
525
- const n = Number(row.message_count);
361
+ const sid = str(row.session_id), n = Number(row.message_count);
526
362
  if (sid && Number.isInteger(n) && n >= 0)
527
363
  out.set(sid, n);
528
364
  }
529
365
  return out;
530
366
  }
531
- writeIndexPayload(entries) {
532
- writeAtomic(this.indexFile, JSON.stringify(entries));
533
- const signature = this.indexSignature();
534
- if (signature !== null)
535
- this.parsedIndexCache = { signature, entries };
536
- }
537
- loadSessionFromPath(path) {
538
- try {
539
- const data = sanitizeSessionRow(JSON.parse(readFileSync(path, 'utf8')), this, basename(path));
540
- if (!data)
541
- return null;
542
- const [messages] = collapseAdjacentDuplicatePartials(data.messages);
543
- data.messages = messages;
544
- return this.construct(data, data.profile ?? null);
545
- }
546
- catch {
547
- return null;
548
- }
367
+ async serializeIndex(fn) {
368
+ const pending = this.indexWriter.then(fn, fn);
369
+ this.indexWriter = pending.catch(() => { });
370
+ return pending;
549
371
  }
550
- /**
551
- * Rebuild `_index.json` from scratch. Resident sessions without a sidecar join it unless `diskOnly` (TAL-259: a recovery
552
- * rebuild lists only persisted sessions, so an unsaved shell never reads as index drift).
553
- */
554
- rebuildIndex(opts = {}) {
555
- mkdirSync(this.sessionDir, { recursive: true });
556
- this.cleanupStaleTmpFiles();
557
- const entryMap = new Map();
558
- for (const name of readdirSync(this.sessionDir)) {
559
- if (!name.endsWith('.json') || name.startsWith('_'))
560
- continue;
561
- const s = this.loadSessionFromPath(join(this.sessionDir, name));
562
- if (!s)
563
- continue;
564
- const c = s.compact();
565
- const sid = str(c.session_id);
566
- if (!sid)
567
- continue;
568
- const existing = entryMap.get(sid);
569
- if (!existing || Number(c.message_count ?? 0) > Number(existing.message_count ?? 0))
570
- entryMap.set(sid, c);
372
+ async rebuildIndexNow(opts = {}) {
373
+ const map = new Map();
374
+ for (const sid of await this.persistedIds()) {
375
+ try {
376
+ const session = await this.load(sid, { drafts: false, repairDuplicates: false });
377
+ if (session)
378
+ map.set(session.session_id, session.compact());
379
+ }
380
+ catch { /* corrupt sessions are omitted */ }
571
381
  }
572
- const entries = [...entryMap.values()];
573
382
  if (!opts.diskOnly)
574
383
  for (const s of this.sessions.values())
575
- if (!entryMap.has(s.session_id))
576
- entries.push(s.compact());
577
- entries.sort((a, b) => Number(b.updated_at ?? 0) - Number(a.updated_at ?? 0));
578
- this.writeIndexPayload(entries);
579
- }
580
- /** Rebuild or patch `_index.json` (Python `_write_session_index`). */
581
- writeIndex(updates) {
582
- mkdirSync(this.sessionDir, { recursive: true });
583
- if (!updates || !existsSync(this.indexFile)) {
584
- this.rebuildIndex();
585
- return;
586
- }
587
- try {
588
- const onDisk = this.persistedIds();
589
- let existing = this.readIndexEntries();
590
- const inMemory = new Set(this.sessions.keys());
591
- const updatedMap = new Map();
592
- for (const update of updates) {
593
- if (update instanceof Session)
594
- updatedMap.set(update.session_id, update.compact());
595
- else if (typeof update.session_id === 'string' && update.session_id)
596
- updatedMap.set(update.session_id, update);
384
+ if (!map.has(s.session_id))
385
+ map.set(s.session_id, s.compact());
386
+ await this.backend.writeIndex([...map.values()].sort((a, b) => Number(b.updated_at ?? 0) - Number(a.updated_at ?? 0)));
387
+ }
388
+ async rebuildIndex(opts = {}) { await this.serializeIndex(async () => { await this.rebuildIndexNow(opts); }); }
389
+ async writeIndex(updates) {
390
+ const snapshots = updates?.map((s) => s instanceof Session ? s.compact() : s);
391
+ await this.serializeIndex(async () => {
392
+ if (!snapshots || !await this.backend.hasIndex()) {
393
+ await this.rebuildIndexNow();
394
+ return;
395
+ }
396
+ let rows;
397
+ try {
398
+ rows = (await this.backend.index()).entries;
399
+ }
400
+ catch {
401
+ await this.rebuildIndexNow();
402
+ return;
597
403
  }
598
- const filtered = existing.filter((e) => inMemory.has(String(e.session_id)) || onDisk.has(String(e.session_id)));
599
- let changed = filtered.length !== existing.length;
600
- existing = filtered;
601
- const positions = new Map(existing.map((e, i) => [String(e.session_id), i]));
602
- for (const [sid, entry] of updatedMap) {
603
- const position = positions.get(sid);
604
- if (position === undefined) {
605
- existing.push(entry);
606
- positions.set(sid, existing.length - 1);
404
+ const ids = await this.persistedIds();
405
+ const live = (sid) => ids.has(sid) || this.sessions.has(sid);
406
+ const kept = rows.filter((r) => live(str(r.session_id)));
407
+ // Only the pruned and upserted entries can differ, so compare those instead of serializing the whole index.
408
+ let changed = kept.length !== rows.length;
409
+ rows = kept;
410
+ const order = rows.map((r) => str(r.session_id));
411
+ const positions = new Map(rows.map((r, i) => [str(r.session_id), i]));
412
+ for (const row of snapshots) {
413
+ const sid = str(row.session_id);
414
+ // A queued row can outlive its session (deleted while a failed batch waited for a retry).
415
+ if (!sid || !live(sid))
416
+ continue;
417
+ const i = positions.get(sid);
418
+ if (i === undefined) {
419
+ positions.set(sid, rows.length);
420
+ rows.push(row);
607
421
  changed = true;
608
422
  }
609
- else if (JSON.stringify(existing[position]) !== JSON.stringify(entry)) {
610
- existing[position] = entry;
611
- changed = true;
423
+ else {
424
+ changed ||= JSON.stringify(rows[i]) !== JSON.stringify(row);
425
+ rows[i] = row;
612
426
  }
613
427
  }
614
- const oldOrder = existing.map((e) => e.session_id).join('\u0000');
615
- existing.sort((a, b) => Number(b.updated_at ?? 0) - Number(a.updated_at ?? 0));
616
- if (oldOrder !== existing.map((e) => e.session_id).join('\u0000'))
617
- changed = true;
618
- if (!changed)
619
- return;
620
- this.writeIndexPayload(existing);
621
- }
622
- catch {
623
- this.writeIndex();
428
+ rows.sort((a, b) => Number(b.updated_at ?? 0) - Number(a.updated_at ?? 0));
429
+ changed ||= rows.some((r, i) => str(r.session_id) !== order[i]);
430
+ if (changed)
431
+ await this.backend.writeIndex(rows);
432
+ });
433
+ }
434
+ queueIndexUpdate(updates) {
435
+ for (const update of updates) {
436
+ const row = update instanceof Session ? update.compact() : update;
437
+ const sid = str(row.session_id);
438
+ if (sid)
439
+ this.indexPending.set(sid, row);
624
440
  }
441
+ this.indexFlush ??= Promise.resolve().then(async () => {
442
+ try {
443
+ while (this.indexPending.size) {
444
+ const batch = new Map(this.indexPending);
445
+ this.indexPending.clear();
446
+ try {
447
+ await this.writeIndex([...batch.values()]);
448
+ this.indexFailure = null;
449
+ }
450
+ catch (error) {
451
+ // Nobody awaits the flush, so a failed batch stays queued for the next save or index read instead of being lost.
452
+ for (const [sid, row] of batch)
453
+ if (!this.indexPending.has(sid))
454
+ this.indexPending.set(sid, row);
455
+ this.indexFailure = error instanceof Error ? error : new Error(String(error));
456
+ this.deps.log(`[webui] WARNING: session index update failed: ${this.indexFailure.message}`);
457
+ break;
458
+ }
459
+ }
460
+ }
461
+ finally {
462
+ this.indexFlush = null;
463
+ }
464
+ });
465
+ return this.indexFlush;
625
466
  }
626
- /** Coalesce index rewrites from concurrent saves (Python `_queue_session_index_update`). */
627
- queueIndexUpdate(sessions) {
628
- for (const s of sessions)
629
- this.indexPending.set(s.session_id, s.compact());
630
- this.flushIndexNow();
631
- }
632
- flushIndexNow() {
633
- const pending = [...this.indexPending.values()];
634
- this.indexPending.clear();
635
- if (pending.length)
636
- this.writeIndex(pending);
637
- }
638
- pruneFromIndex(sid) {
639
- if (!sid || !existsSync(this.indexFile))
640
- return;
641
- try {
642
- const existing = JSON.parse(readFileSync(this.indexFile, 'utf8'));
643
- if (!Array.isArray(existing))
644
- throw new Error('session index must be a list');
645
- const pruned = existing.filter((e) => e.session_id !== sid);
646
- if (pruned.length === existing.length)
467
+ async pruneFromIndex(sid) {
468
+ await this.serializeIndex(async () => {
469
+ if (!sid || !await this.backend.hasIndex())
647
470
  return;
648
- this.writeIndexPayload(pruned);
649
- }
650
- catch {
651
- this.writeIndex();
652
- }
653
- }
654
- // ── tombstones ───────────────────────────────────────────────────────────
655
- get deletedTombstoneFile() {
656
- return join(this.sessionDir, '_deleted_webui_sessions.json');
657
- }
658
- loadDeletedTombstone() {
659
- try {
660
- if (!existsSync(this.deletedTombstoneFile))
661
- return new Set();
662
- const raw = JSON.parse(readFileSync(this.deletedTombstoneFile, 'utf8'));
663
- if (!raw || typeof raw !== 'object' || Number(raw.version) !== DELETED_TOMBSTONE_VERSION || !Array.isArray(raw.ids))
664
- return new Set();
665
- return new Set(raw.ids.map((v) => String(v ?? '').trim()).filter(Boolean));
666
- }
667
- catch {
668
- return new Set();
669
- }
670
- }
671
- saveDeletedTombstone(ids) {
672
- let sorted = [...ids].sort();
673
- if (sorted.length > DELETED_TOMBSTONE_CAP)
674
- sorted = sorted.slice(-DELETED_TOMBSTONE_CAP);
675
- try {
676
- mkdirSync(this.sessionDir, { recursive: true });
677
- writeAtomic(this.deletedTombstoneFile, JSON.stringify({ version: DELETED_TOMBSTONE_VERSION, ids: sorted }, null, 2));
678
- }
679
- catch { /* best effort */ }
680
- }
681
- recordDeletedTombstone(sid) {
682
- const ids = this.loadDeletedTombstone();
683
- if (ids.has(sid))
684
- return;
685
- ids.add(sid);
686
- this.saveDeletedTombstone(ids);
687
- }
688
- clearDeletedTombstone(sid) {
689
- const ids = this.loadDeletedTombstone();
690
- if (!ids.has(sid))
691
- return;
692
- ids.delete(sid);
693
- if (ids.size)
694
- this.saveDeletedTombstone(ids);
695
- else
696
- rmSync(this.deletedTombstoneFile, { force: true });
697
- }
698
- /** The index marked this id as a deleted WebUI session (Python `_marks_deleted_webui_session`, tombstone half). */
699
- wasDeleted(sid) {
700
- return this.loadDeletedTombstone().has(sid);
701
- }
702
- // ── eviction ─────────────────────────────────────────────────────────────
703
- persistedMessageCount(sid) {
704
- const path = this.pathFor(sid);
705
- if (!existsSync(path))
706
- return null;
707
- const count = readPersistedMessageCount(path);
708
- if (count !== null)
709
- return count;
710
- try {
711
- const data = JSON.parse(readFileSync(path, 'utf8'));
712
- return Array.isArray(data.messages) ? data.messages.length : 0;
713
- }
714
- catch {
715
- return null;
716
- }
471
+ try {
472
+ const rows = (await this.backend.index()).entries;
473
+ const pruned = rows.filter((r) => r.session_id !== sid);
474
+ if (pruned.length !== rows.length)
475
+ await this.backend.writeIndex(pruned);
476
+ }
477
+ catch {
478
+ await this.rebuildIndexNow();
479
+ }
480
+ });
717
481
  }
718
- /** Python `_session_is_evictable`: never drop active, pending, or unsaved sessions. */
719
- isEvictable(s) {
482
+ async loadDeletedTombstone() { return this.backend.tombstones.all(); }
483
+ async recordDeletedTombstone(sid) { return this.backend.tombstones.add(sid); }
484
+ async clearDeletedTombstone(sid) { return this.backend.tombstones.remove(sid); }
485
+ async wasDeleted(sid) { return this.backend.tombstones.has(sid); }
486
+ async isEvictable(s) {
720
487
  if (!s)
721
488
  return true;
722
- if (s.active_stream_id || s.pending_user_message || s.pending_started_at)
723
- return false;
724
- if (!s.session_id)
489
+ if (s.active_stream_id || s.pending_user_message || s.pending_started_at || !s.session_id)
725
490
  return false;
726
491
  if (s.loadedMetadataOnly)
727
492
  return true;
728
- const inMemory = s.messages.length;
729
- const diskCount = this.persistedMessageCount(s.session_id);
730
- if (diskCount === null) {
731
- if (inMemory > 0)
732
- return false;
733
- if (Object.keys(s.composer_draft).length)
734
- return false;
735
- if (existsSync(this.pathFor(s.session_id)))
493
+ let count = null;
494
+ try {
495
+ count = (await this.backend.readDocument(s.session_id, { metadataOnly: true }))?.messageCount ?? null;
496
+ }
497
+ catch { /* corrupt */ }
498
+ if (count === null) {
499
+ if (s.messages.length || Object.keys(s.composer_draft).length || await this.exists(s.session_id))
736
500
  return false;
737
501
  return this.deps.now() - s.created_at > UNSAVED_SHELL_GRACE_S;
738
502
  }
739
- if (inMemory === 0)
740
- return true;
741
- return diskCount >= inMemory;
503
+ return count >= s.messages.length;
742
504
  }
743
- evictOverCap(cap) {
505
+ async evictOverCap(cap) {
744
506
  let limit = cap ?? this.deps.cacheMax();
745
507
  if (!Number.isInteger(limit) || limit < 1)
746
508
  limit = 100;
@@ -748,63 +510,35 @@ export class SessionStore {
748
510
  for (const sid of [...this.sessions.keys()]) {
749
511
  if (this.sessions.size <= limit)
750
512
  break;
751
- if (this.isEvictable(this.sessions.get(sid))) {
513
+ const session = this.sessions.get(sid);
514
+ if (await this.isEvictable(session) && this.sessions.get(sid) === session && !session?.active_stream_id && !session?.pending_user_message && !session?.pending_started_at) {
752
515
  this.sessions.delete(sid);
753
516
  evicted += 1;
754
517
  }
755
518
  }
756
519
  return evicted;
757
520
  }
758
- // ── deletion ─────────────────────────────────────────────────────────────
759
- /** Remove the sidecar, backup, draft, index row, and tombstone the id. Returns false when the sidecar survived. */
760
- deleteFiles(sid, opts = {}) {
761
- const path = this.pathFor(sid);
762
- try {
763
- rmSync(path, { force: true });
764
- }
765
- catch {
766
- return false;
767
- }
768
- if (existsSync(path))
769
- return false;
770
- this.sessions.delete(sid);
771
- this.invalidatePersistedIds();
772
- try {
773
- this.pruneFromIndex(sid);
774
- }
775
- catch { /* ignore */ }
776
- try {
777
- rmSync(`${path}.bak`, { force: true });
778
- }
779
- catch { /* ignore */ }
780
- try {
781
- this.deps.drafts.delete(sid);
782
- }
783
- catch { /* ignore */ }
784
- // TAL-372: the session's background work records (`BackgroundTaskStore`) go with it.
785
- try {
786
- rmSync(join(this.sessionDir, '_background', `${sid}.json`), { force: true });
787
- }
788
- catch { /* ignore */ }
789
- this.deps.onDeleted?.(sid);
790
- if (opts.tombstone ?? true)
791
- this.recordDeletedTombstone(sid);
792
- return true;
521
+ delete(sid, opts = {}) {
522
+ return this.serializeSession(sid, async () => {
523
+ if (!await this.backend.delete(sid, { tombstone: opts.tombstone ?? true }))
524
+ return false;
525
+ this.sessions.delete(sid);
526
+ try {
527
+ await this.pruneFromIndex(sid);
528
+ }
529
+ catch { /* ignore */ }
530
+ try {
531
+ (await this.deps.drafts.delete(sid));
532
+ }
533
+ catch { /* ignore */ }
534
+ await this.deps.onDeleted?.(sid);
535
+ return true;
536
+ });
793
537
  }
794
- /** Every persisted session as a fresh full load (Python full-scan paths). */
795
- scanAll() {
538
+ async scanAll() {
796
539
  const out = [];
797
- let names = [];
798
- try {
799
- names = readdirSync(this.sessionDir);
800
- }
801
- catch {
802
- return out;
803
- }
804
- for (const name of names) {
805
- if (!name.endsWith('.json') || name.startsWith('_'))
806
- continue;
807
- const s = this.load(name.slice(0, -5));
540
+ for (const sid of await this.persistedIds()) {
541
+ const s = await this.load(sid);
808
542
  if (s)
809
543
  out.push(s);
810
544
  }