@xopcai/xopc 0.0.121 → 0.0.123

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 (309) hide show
  1. package/README.md +23 -3
  2. package/README.zh-CN.md +23 -3
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/telegram/xopc.extension.json +1 -1
  5. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-4AW4JgSU.js → agent-avatar-dicebear-Bicr3ZBg.js} +1 -1
  6. package/dist/gateway/static/root/assets/agents-CxDSFHGx.js +2 -0
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-CE6GkGU2.js → app-management-settings-panel-CbEy9sR1.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-BmaUjghw.js → appearance-settings-CTHja6n9.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-CyZdgqvm.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-CX4_KWut.js → archive-plugin-Bpy64GjU.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-jf-PIIUU.js → attachment-load-DMkp4xSb.js} +1 -1
  12. package/dist/gateway/static/root/assets/automation-suggestion-card-KnYaP7NT.js +1 -0
  13. package/dist/gateway/static/root/assets/automations-page-8VLKM4au.js +1 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-DG9XQikd.js → binary-plugins-DA9C5mCw.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-Up_VIxLJ.js → block-editor-CiSXk_YC.js} +6 -6
  16. package/dist/gateway/static/root/assets/browser-settings-page-5tPg0DcP.js +8 -0
  17. package/dist/gateway/static/root/assets/{capability-presets-api-CGM4OyMw.js → capability-presets-api-CEJ44WCU.js} +1 -1
  18. package/dist/gateway/static/root/assets/{capability-presets-settings-panel-CzWt0LBb.js → capability-presets-settings-panel-BJW3aVST.js} +1 -1
  19. package/dist/gateway/static/root/assets/{channel-recipient-api-BEByL4_o.js → channel-recipient-api-CDhrhfhN.js} +1 -1
  20. package/dist/gateway/static/root/assets/channels-settings-CorUCTrs.js +1 -0
  21. package/dist/gateway/static/root/assets/connectors-page-VKstC3sP.js +2 -0
  22. package/dist/gateway/static/root/assets/directory-picker-path-field-7jKfbH0J.js +1 -0
  23. package/dist/gateway/static/root/assets/dreaming-settings-m1-wmz4o.js +1 -0
  24. package/dist/gateway/static/root/assets/{extension-debug-page-DZxa_j7-.js → extension-debug-page-C9qQQrBS.js} +1 -1
  25. package/dist/gateway/static/root/assets/{extension-page-CseziW53.js → extension-page-DcBBMEGl.js} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-settings-page-B_9J-xyo.js → extension-settings-page-BQ-jMMP9.js} +1 -1
  27. package/dist/gateway/static/root/assets/{fetch-Cs4ck8zA.js → fetch-Dtpbbe0s.js} +1 -1
  28. package/dist/gateway/static/root/assets/{gateway-config-api-Dl223a7r.js → gateway-config-api-BN6pyX2c.js} +1 -1
  29. package/dist/gateway/static/root/assets/gateway-settings-hQtgVmSR.js +1 -0
  30. package/dist/gateway/static/root/assets/{gateway-startup-retry-IvePwjFE.js → gateway-startup-retry-BIET2x3y.js} +1 -1
  31. package/dist/gateway/static/root/assets/goal-create-dialog-BuIMDx6C.js +1 -0
  32. package/dist/gateway/static/root/assets/goal-detail-page-FEypCq74.js +1 -0
  33. package/dist/gateway/static/root/assets/{goals-config-api-CUCP6yCl.js → goals-config-api-CQm2r1Ty.js} +1 -1
  34. package/dist/gateway/static/root/assets/goals-page-C4-2_MLG.js +2 -0
  35. package/dist/gateway/static/root/assets/{goals-settings-Dj87qPxv.js → goals-settings-DDrdRMxB.js} +1 -1
  36. package/dist/gateway/static/root/assets/heartbeat-settings-BJVok3Sl.js +1 -0
  37. package/dist/gateway/static/root/assets/index-CBDr5xjB.css +1 -0
  38. package/dist/gateway/static/root/assets/index-T0SaNd5a.js +191 -0
  39. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-DJgYw0nB.js → keyboard-shortcuts-settings-C2TuQppb.js} +1 -1
  40. package/dist/gateway/static/root/assets/logs-page-DwkwPgid.js +2 -0
  41. package/dist/gateway/static/root/assets/{markdown-split-gnQ_EiGo.js → markdown-split-DhONQ81k.js} +1 -1
  42. package/dist/gateway/static/root/assets/{media-plugins-CQV-mgJn.js → media-plugins-DS44pJzr.js} +1 -1
  43. package/dist/gateway/static/root/assets/messages-avfvyZuQ.js +3 -0
  44. package/dist/gateway/static/root/assets/models-hub-panel-huNY4A1G.js +2 -0
  45. package/dist/gateway/static/root/assets/notes-page-DdfGuEZ1.js +1 -0
  46. package/dist/gateway/static/root/assets/notes-workbench-B0bT9Hqa.js +2 -0
  47. package/dist/gateway/static/root/assets/{preview-open-alternatives-BPMXC-2u.js → preview-open-alternatives-Dej6zHnr.js} +1 -1
  48. package/dist/gateway/static/root/assets/project-detail-page-CjxhPorG.js +1 -0
  49. package/dist/gateway/static/root/assets/projects-page-C4Z5OJMF.js +1 -0
  50. package/dist/gateway/static/root/assets/remote-access-hub-CVCOPUVd.js +1 -0
  51. package/dist/gateway/static/root/assets/{save-bar-controls-BEHqwZjz.js → save-bar-controls-Zu1FzH6z.js} +1 -1
  52. package/dist/gateway/static/root/assets/{schedule-field-CMY25m97.js → schedule-field-BW78mKc0.js} +1 -1
  53. package/dist/gateway/static/root/assets/{schema-form-CxjxsFfg.js → schema-form-Cc-IOydE.js} +1 -1
  54. package/dist/gateway/static/root/assets/segmented-styles-BL8oLBEp.js +1 -0
  55. package/dist/gateway/static/root/assets/sessions-page-B4qExXHC.js +1 -0
  56. package/dist/gateway/static/root/assets/settings-advanced-gate-Ce8yLTBi.js +1 -0
  57. package/dist/gateway/static/root/assets/{settings-collapsible-section-s-kiLAxP.js → settings-collapsible-section-DIpIYht_.js} +1 -1
  58. package/dist/gateway/static/root/assets/{settings-form-section-CIxIEYxU.js → settings-form-section-BFxBNxOG.js} +1 -1
  59. package/dist/gateway/static/root/assets/settings-page-C4fivemT.js +1 -0
  60. package/dist/gateway/static/root/assets/settings-page-layout-CLx4o2PN.js +1 -0
  61. package/dist/gateway/static/root/assets/setup-status-panel-Kyc4eUf0.js +1 -0
  62. package/dist/gateway/static/root/assets/share-preview-page-ZswWCrDu.js +2 -0
  63. package/dist/gateway/static/root/assets/shares-settings-Bu76CMiC.js +1 -0
  64. package/dist/gateway/static/root/assets/skill-reload-api-D-CFg86j.js +1 -0
  65. package/dist/gateway/static/root/assets/skills-page-CUU4f6Ss.js +2 -0
  66. package/dist/gateway/static/root/assets/{system-settings-panel-Czjf0kLJ.js → system-settings-panel-3ZNcGRhS.js} +1 -1
  67. package/dist/gateway/static/root/assets/{typed-models-lib-Cmt8SYdI.js → typed-models-lib-bpzZjiVF.js} +1 -1
  68. package/dist/gateway/static/root/assets/{use-channel-catalog-DyM-ckEQ.js → use-channel-catalog-BCjZLZ3O.js} +1 -1
  69. package/dist/gateway/static/root/assets/{user-profile-settings-panel-BygLv8vm.js → user-profile-settings-panel-CIXafITi.js} +1 -1
  70. package/dist/gateway/static/root/assets/{utils-CloW8coB.js → utils-DXMtz5j_.js} +1 -1
  71. package/dist/gateway/static/root/assets/voice-api-key-field-C1MrQD8R.js +1 -0
  72. package/dist/gateway/static/root/assets/workflow-run-setup-panel-DqizIcY5.js +3 -0
  73. package/dist/gateway/static/root/assets/workflow-start-dialog-B4sZpTDQ.js +1 -0
  74. package/dist/gateway/static/root/assets/workflows-page-B3sVNPFk.js +18 -0
  75. package/dist/gateway/static/root/index.html +5 -5
  76. package/dist/package.js +1 -1
  77. package/dist/src/agent/agent-manager.d.ts +2 -0
  78. package/dist/src/agent/agent-manager.js +47 -7
  79. package/dist/src/agent/agent-manager.js.map +1 -1
  80. package/dist/src/agent/context/project-context.d.ts +19 -0
  81. package/dist/src/agent/context/project-context.js +90 -0
  82. package/dist/src/agent/context/project-context.js.map +1 -0
  83. package/dist/src/agent/goals/goal-locale.js +2 -2
  84. package/dist/src/agent/memory/types.d.ts +1 -0
  85. package/dist/src/agent/prompt/service-prompt-builder.d.ts +1 -0
  86. package/dist/src/agent/prompt/service-prompt-builder.js +2 -0
  87. package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
  88. package/dist/src/agent/prompt/system-prompt-params.d.ts +2 -0
  89. package/dist/src/agent/prompt/system-prompt-params.js +1 -0
  90. package/dist/src/agent/prompt/system-prompt-params.js.map +1 -1
  91. package/dist/src/agent/prompt/system-prompt.d.ts +1 -0
  92. package/dist/src/agent/prompt/system-prompt.js +2 -1
  93. package/dist/src/agent/prompt/system-prompt.js.map +1 -1
  94. package/dist/src/agent/service.js +4 -3
  95. package/dist/src/agent/service.js.map +1 -1
  96. package/dist/src/agent/session/session-config-service.js +6 -1
  97. package/dist/src/agent/session/session-config-service.js.map +1 -1
  98. package/dist/src/agent/session/session-hydrator.js +7 -3
  99. package/dist/src/agent/session/session-hydrator.js.map +1 -1
  100. package/dist/src/agent/session/session-inspector.js +9 -5
  101. package/dist/src/agent/session/session-inspector.js.map +1 -1
  102. package/dist/src/agent/starter-agents.js +5 -34
  103. package/dist/src/agent/starter-agents.js.map +1 -1
  104. package/dist/src/agent/tools/factory.js +1 -1
  105. package/dist/src/agent/tools/factory.js.map +1 -1
  106. package/dist/src/agent/tools/goal-tool.d.ts +5 -1
  107. package/dist/src/agent/tools/goal-tool.js +8 -2
  108. package/dist/src/agent/tools/goal-tool.js.map +1 -1
  109. package/dist/src/agent/tools/index.d.ts +1 -1
  110. package/dist/src/agent/workflow/index.js +2 -2
  111. package/dist/src/agent/workflow/runtime.js +1 -1
  112. package/dist/src/automations/api/routes.js +13 -2
  113. package/dist/src/automations/api/routes.js.map +1 -1
  114. package/dist/src/automations/domain/types.d.ts +2 -0
  115. package/dist/src/automations/domain/validation.d.ts +3 -0
  116. package/dist/src/automations/domain/validation.js +1 -0
  117. package/dist/src/automations/domain/validation.js.map +1 -1
  118. package/dist/src/automations/draft/automation-draft-validator.d.ts +1 -0
  119. package/dist/src/automations/service/action-executor.js +3 -1
  120. package/dist/src/automations/service/action-executor.js.map +1 -1
  121. package/dist/src/automations/service/automation-service.d.ts +4 -1
  122. package/dist/src/automations/service/automation-service.js +2 -2
  123. package/dist/src/automations/service/automation-service.js.map +1 -1
  124. package/dist/src/automations/storage/automation-repository.d.ts +3 -1
  125. package/dist/src/automations/storage/automation-repository.js +7 -5
  126. package/dist/src/automations/storage/automation-repository.js.map +1 -1
  127. package/dist/src/automations/storage/automation-run-repository.d.ts +1 -0
  128. package/dist/src/automations/storage/automation-run-repository.js +12 -1
  129. package/dist/src/automations/storage/automation-run-repository.js.map +1 -1
  130. package/dist/src/chat-commands/builtins/goal.js +4 -1
  131. package/dist/src/chat-commands/builtins/goal.js.map +1 -1
  132. package/dist/src/chat-commands/context.js +2 -1
  133. package/dist/src/chat-commands/context.js.map +1 -1
  134. package/dist/src/cli/command-loaders.js +1 -0
  135. package/dist/src/cli/command-loaders.js.map +1 -1
  136. package/dist/src/cli/command-manifest.js +4 -0
  137. package/dist/src/cli/command-manifest.js.map +1 -1
  138. package/dist/src/cli/commands/agent.js +2 -2
  139. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  140. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  141. package/dist/src/cli/commands/goal.js +27 -4
  142. package/dist/src/cli/commands/goal.js.map +1 -1
  143. package/dist/src/cli/commands/models.js +1 -1
  144. package/dist/src/cli/commands/onboard.js +1 -1
  145. package/dist/src/cli/commands/profile.js +1 -1
  146. package/dist/src/cli/commands/project.d.ts +4 -0
  147. package/dist/src/cli/commands/project.js +239 -0
  148. package/dist/src/cli/commands/project.js.map +1 -0
  149. package/dist/src/cli/commands/tunnel.js +1 -1
  150. package/dist/src/cli/commands/update.js +1 -1
  151. package/dist/src/cli/extension-cli-register.js +1 -1
  152. package/dist/src/config/index.js +1 -1
  153. package/dist/src/gateway/hono/routes/goals.js +51 -4
  154. package/dist/src/gateway/hono/routes/goals.js.map +1 -1
  155. package/dist/src/gateway/hono/routes/index.js +2 -0
  156. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  157. package/dist/src/gateway/hono/routes/memory.js +5 -1
  158. package/dist/src/gateway/hono/routes/memory.js.map +1 -1
  159. package/dist/src/gateway/hono/routes/notes.js +5 -0
  160. package/dist/src/gateway/hono/routes/notes.js.map +1 -1
  161. package/dist/src/gateway/hono/routes/projects.d.ts +3 -0
  162. package/dist/src/gateway/hono/routes/projects.js +662 -0
  163. package/dist/src/gateway/hono/routes/projects.js.map +1 -0
  164. package/dist/src/gateway/hono/routes/sessions.js +47 -7
  165. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  166. package/dist/src/gateway/hono/routes/workflows.js +27 -4
  167. package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
  168. package/dist/src/gateway/run-loop.js +1 -1
  169. package/dist/src/gateway/service.d.ts +3 -0
  170. package/dist/src/gateway/service.js +10 -3
  171. package/dist/src/gateway/service.js.map +1 -1
  172. package/dist/src/goals/goal-service.d.ts +1 -1
  173. package/dist/src/goals/goal-service.js +2 -1
  174. package/dist/src/goals/goal-service.js.map +1 -1
  175. package/dist/src/goals/goal-store.d.ts +1 -1
  176. package/dist/src/goals/goal-store.js +12 -6
  177. package/dist/src/goals/goal-store.js.map +1 -1
  178. package/dist/src/goals/goal-workflow-judge.js +1 -1
  179. package/dist/src/goals/types.d.ts +3 -0
  180. package/dist/src/i18n/goals-bundle.js +1 -1
  181. package/dist/src/i18n/index.js +1 -1
  182. package/dist/src/i18n/share-tool-bundle.js +2 -2
  183. package/dist/src/notes/agent-context.js +2 -2
  184. package/dist/src/projects/automation-bind.d.ts +1 -0
  185. package/dist/src/projects/automation-bind.js +16 -0
  186. package/dist/src/projects/automation-bind.js.map +1 -0
  187. package/dist/src/projects/goal-bind.d.ts +4 -0
  188. package/dist/src/projects/goal-bind.js +40 -0
  189. package/dist/src/projects/goal-bind.js.map +1 -0
  190. package/dist/src/projects/index.d.ts +12 -0
  191. package/dist/src/projects/index.js +12 -0
  192. package/dist/src/projects/memory-bind.d.ts +1 -0
  193. package/dist/src/projects/memory-bind.js +16 -0
  194. package/dist/src/projects/memory-bind.js.map +1 -0
  195. package/dist/src/projects/project-agent.d.ts +10 -0
  196. package/dist/src/projects/project-agent.js +27 -0
  197. package/dist/src/projects/project-agent.js.map +1 -0
  198. package/dist/src/projects/project-loop.d.ts +58 -0
  199. package/dist/src/projects/project-loop.js +118 -0
  200. package/dist/src/projects/project-loop.js.map +1 -0
  201. package/dist/src/projects/project-service.d.ts +40 -0
  202. package/dist/src/projects/project-service.js +187 -0
  203. package/dist/src/projects/project-service.js.map +1 -0
  204. package/dist/src/projects/project-store.d.ts +31 -0
  205. package/dist/src/projects/project-store.js +193 -0
  206. package/dist/src/projects/project-store.js.map +1 -0
  207. package/dist/src/projects/session-bind.d.ts +5 -0
  208. package/dist/src/projects/session-bind.js +46 -0
  209. package/dist/src/projects/session-bind.js.map +1 -0
  210. package/dist/src/projects/types.d.ts +62 -0
  211. package/dist/src/projects/types.js +1 -0
  212. package/dist/src/projects/workflow-bind.d.ts +1 -0
  213. package/dist/src/projects/workflow-bind.js +16 -0
  214. package/dist/src/projects/workflow-bind.js.map +1 -0
  215. package/dist/src/projects/workspace-project.d.ts +22 -0
  216. package/dist/src/projects/workspace-project.js +143 -0
  217. package/dist/src/projects/workspace-project.js.map +1 -0
  218. package/dist/src/projects/workspace.d.ts +5 -0
  219. package/dist/src/projects/workspace.js +25 -0
  220. package/dist/src/projects/workspace.js.map +1 -0
  221. package/dist/src/session/index.d.ts +1 -1
  222. package/dist/src/session/index.js +3 -3
  223. package/dist/src/session/session-workspace.d.ts +4 -2
  224. package/dist/src/session/session-workspace.js +10 -3
  225. package/dist/src/session/session-workspace.js.map +1 -1
  226. package/dist/src/session/types.d.ts +3 -0
  227. package/dist/src/session/types.js.map +1 -1
  228. package/dist/src/storage/sqlite/memory-records-repository.d.ts +3 -0
  229. package/dist/src/storage/sqlite/memory-records-repository.js +16 -5
  230. package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
  231. package/dist/src/storage/sqlite/migrations/019_projects.sql +40 -0
  232. package/dist/src/storage/sqlite/migrations/020_project_default_agent.sql +1 -0
  233. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  234. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  235. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  236. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  237. package/dist/src/storage/sqlite/notes-repository.js +20 -4
  238. package/dist/src/storage/sqlite/notes-repository.js.map +1 -1
  239. package/dist/src/storage/sqlite/row-mappers.d.ts +2 -0
  240. package/dist/src/storage/sqlite/row-mappers.js +2 -0
  241. package/dist/src/storage/sqlite/row-mappers.js.map +1 -1
  242. package/dist/src/storage/sqlite/schema.js +1 -1
  243. package/dist/src/storage/sqlite/session-repository.js +10 -5
  244. package/dist/src/storage/sqlite/session-repository.js.map +1 -1
  245. package/dist/src/storage/sqlite/transcript-repository.js +8 -0
  246. package/dist/src/storage/sqlite/transcript-repository.js.map +1 -1
  247. package/dist/src/tui/backends/embedded-backend.d.ts +7 -1
  248. package/dist/src/tui/backends/embedded-backend.js +41 -2
  249. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  250. package/dist/src/tui/backends/gateway-sse-backend.d.ts +7 -1
  251. package/dist/src/tui/backends/gateway-sse-backend.js +22 -0
  252. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  253. package/dist/src/tui/tui-backend.d.ts +16 -0
  254. package/dist/src/tui/tui-commands.js +145 -0
  255. package/dist/src/tui/tui-commands.js.map +1 -1
  256. package/dist/src/tui/tui.js +21 -0
  257. package/dist/src/tui/tui.js.map +1 -1
  258. package/dist/src/workflows/domain/run.d.ts +1 -0
  259. package/dist/src/workflows/domain/run.js.map +1 -1
  260. package/dist/src/workflows/index.js +1 -1
  261. package/dist/src/workflows/service/workflow-run-service.d.ts +1 -0
  262. package/dist/src/workflows/service/workflow-run-service.js +18 -4
  263. package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
  264. package/dist/src/workflows/service/workflow-run-service.types.d.ts +2 -0
  265. package/dist/src/workflows/service/workflow-session-bridge.d.ts +1 -0
  266. package/dist/src/workflows/service/workflow-session-bridge.js +3 -0
  267. package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
  268. package/dist/src/workflows/store/run-index-store.d.ts +1 -0
  269. package/dist/src/workflows/store/run-index-store.js +13 -3
  270. package/dist/src/workflows/store/run-index-store.js.map +1 -1
  271. package/dist/src/workflows/store/run-store.d.ts +3 -1
  272. package/dist/src/workflows/store/run-store.js +5 -2
  273. package/dist/src/workflows/store/run-store.js.map +1 -1
  274. package/package.json +10 -1
  275. package/dist/gateway/static/root/assets/agents-2SVUUPlp.js +0 -2
  276. package/dist/gateway/static/root/assets/apps-page-BWeQ1I49.js +0 -1
  277. package/dist/gateway/static/root/assets/automation-suggestion-card-BHk5rtJz.js +0 -1
  278. package/dist/gateway/static/root/assets/automations-page-QHotl53D.js +0 -1
  279. package/dist/gateway/static/root/assets/browser-settings-page-Cmwi6IIQ.js +0 -8
  280. package/dist/gateway/static/root/assets/channels-settings-BOMCUe2J.js +0 -1
  281. package/dist/gateway/static/root/assets/connectors-page-C-Dexk_F.js +0 -2
  282. package/dist/gateway/static/root/assets/dreaming-settings-WPEj8pir.js +0 -1
  283. package/dist/gateway/static/root/assets/gateway-settings-DdzbtW9R.js +0 -1
  284. package/dist/gateway/static/root/assets/goal-detail-page-Dxldogms.js +0 -1
  285. package/dist/gateway/static/root/assets/goals-page-DU5Nd6Xp.js +0 -2
  286. package/dist/gateway/static/root/assets/heartbeat-settings-Cgm5JmwH.js +0 -1
  287. package/dist/gateway/static/root/assets/index-BU1pvxmS.js +0 -191
  288. package/dist/gateway/static/root/assets/index-l54WfSqe.css +0 -1
  289. package/dist/gateway/static/root/assets/logs-page-C10aS4XO.js +0 -2
  290. package/dist/gateway/static/root/assets/messages-C6Kc4Jhf.js +0 -3
  291. package/dist/gateway/static/root/assets/models-hub-panel-B35CAfg6.js +0 -2
  292. package/dist/gateway/static/root/assets/note-detail-page-Brlwse8x.js +0 -2
  293. package/dist/gateway/static/root/assets/note-image-lightbox-BthwKjLA.js +0 -1
  294. package/dist/gateway/static/root/assets/note-time-hm6JO_Wi.js +0 -1
  295. package/dist/gateway/static/root/assets/notes-page-CoAwRc7L.js +0 -1
  296. package/dist/gateway/static/root/assets/remote-access-hub-B36RQ05h.js +0 -1
  297. package/dist/gateway/static/root/assets/sessions-page-73THChnj.js +0 -1
  298. package/dist/gateway/static/root/assets/settings-advanced-gate-CR9VsY-R.js +0 -1
  299. package/dist/gateway/static/root/assets/settings-page-BJ1AhfKI.js +0 -1
  300. package/dist/gateway/static/root/assets/settings-page-layout--6qT7ZJa.js +0 -1
  301. package/dist/gateway/static/root/assets/setup-status-panel-C1TtS1vJ.js +0 -1
  302. package/dist/gateway/static/root/assets/share-preview-page-BZADSbS1.js +0 -2
  303. package/dist/gateway/static/root/assets/shares-settings-gqC8Rmhy.js +0 -1
  304. package/dist/gateway/static/root/assets/skill-reload-api-C9P-WYvS.js +0 -1
  305. package/dist/gateway/static/root/assets/skills-page-CEpSvOhF.js +0 -2
  306. package/dist/gateway/static/root/assets/voice-api-key-field-BsoqJba6.js +0 -1
  307. package/dist/gateway/static/root/assets/voice-player-store-DduWlJYV.js +0 -1
  308. package/dist/gateway/static/root/assets/workflow-run-setup-panel-CcQeX6l5.js +0 -3
  309. package/dist/gateway/static/root/assets/workflows-page-CeA8A9Ef.js +0 -18
@@ -10,7 +10,7 @@ const SESSION_COLUMNS = `
10
10
  s.created_at, s.updated_at, s.last_accessed_at, s.session_started_at, s.last_interaction_at,
11
11
  s.source_channel, s.source_chat_id, s.session_type, s.hidden_from_session_list,
12
12
  s.parent_session_key, s.workflow_run_id, s.workflow_definition_id, s.workflow_agent_id, s.workflow_agent_label,
13
- s.routing_json, s.custom_data_json,
13
+ s.project_id, s.routing_json, s.custom_data_json,
14
14
  s.abort_cutoff_timestamp, s.message_count, s.estimated_tokens, s.compacted_count,
15
15
  s.last_flushed_at, s.flush_count,
16
16
  s.thinking_level, s.verbose_level,
@@ -31,7 +31,7 @@ function insertSessionAndTranscript(db, sessionKey, sessionId, cwd, metadata) {
31
31
  created_at, updated_at, last_accessed_at, session_started_at, last_interaction_at,
32
32
  source_channel, source_chat_id, session_type, hidden_from_session_list,
33
33
  parent_session_key, workflow_run_id, workflow_definition_id, workflow_agent_id, workflow_agent_label,
34
- routing_json, custom_data_json,
34
+ project_id, routing_json, custom_data_json,
35
35
  abort_cutoff_timestamp, message_count, estimated_tokens, compacted_count,
36
36
  last_flushed_at, flush_count,
37
37
  thinking_level, verbose_level
@@ -40,10 +40,10 @@ function insertSessionAndTranscript(db, sessionKey, sessionId, cwd, metadata) {
40
40
  ?, ?, ?, ?, ?,
41
41
  ?, ?, ?, ?,
42
42
  ?, ?, ?, ?, ?,
43
- ?, ?,
43
+ ?, ?, ?,
44
44
  ?, ?, ?, ?, ?, ?,
45
45
  ?, ?
46
- )`).run(row.sessionKey, row.agentId, row.sessionId, row.status, row.name, row.tagsJson, row.createdAt, row.updatedAt, row.lastAccessedAt, row.sessionStartedAt, row.lastInteractionAt, row.sourceChannel, row.sourceChatId, row.sessionType, row.hiddenFromSessionList, row.parentSessionKey, row.workflowRunId, row.workflowDefinitionId, row.workflowAgentId, row.workflowAgentLabel, row.routingJson, row.customDataJson, row.abortCutoffTimestamp, row.messageCount, row.estimatedTokens, row.compactedCount, row.lastFlushedAt, row.flushCount, row.thinkingLevel, row.verboseLevel);
46
+ )`).run(row.sessionKey, row.agentId, row.sessionId, row.status, row.name, row.tagsJson, row.createdAt, row.updatedAt, row.lastAccessedAt, row.sessionStartedAt, row.lastInteractionAt, row.sourceChannel, row.sourceChatId, row.sessionType, row.hiddenFromSessionList, row.parentSessionKey, row.workflowRunId, row.workflowDefinitionId, row.workflowAgentId, row.workflowAgentLabel, row.projectId, row.routingJson, row.customDataJson, row.abortCutoffTimestamp, row.messageCount, row.estimatedTokens, row.compactedCount, row.lastFlushedAt, row.flushCount, row.thinkingLevel, row.verboseLevel);
47
47
  const now = Date.now();
48
48
  db.prepare(`INSERT INTO transcripts (session_id, session_key, status, created_at, cwd)
49
49
  VALUES (?, ?, 'active', ?, ?)`).run(sessionId, sessionKey, now, cwd);
@@ -90,6 +90,10 @@ function listSessionMetadata(query = {}) {
90
90
  conditions.push(`s.status IN (${statuses.map(() => "?").join(", ")})`);
91
91
  params.push(...statuses);
92
92
  }
93
+ if (query.projectId) {
94
+ conditions.push(`s.project_id = ?`);
95
+ params.push(query.projectId);
96
+ }
93
97
  if (query.channel) {
94
98
  const rawChannels = query.channel.split(",").map((c) => c.trim().toLowerCase()).filter(Boolean);
95
99
  const channels = [...new Set(rawChannels.flatMap((c) => {
@@ -186,6 +190,7 @@ function patchSessionMetadata(sessionKey, updates) {
186
190
  workflow_definition_id = ?,
187
191
  workflow_agent_id = ?,
188
192
  workflow_agent_label = ?,
193
+ project_id = ?,
189
194
  routing_json = ?,
190
195
  custom_data_json = ?,
191
196
  abort_cutoff_timestamp = ?,
@@ -196,7 +201,7 @@ function patchSessionMetadata(sessionKey, updates) {
196
201
  flush_count = ?,
197
202
  thinking_level = ?,
198
203
  verbose_level = ?
199
- WHERE session_key = ?`).run(merged.status, merged.name ?? null, JSON.stringify(merged.tags ?? []), Date.parse(merged.updatedAt) || now, Date.parse(merged.lastAccessedAt) || now, merged.sessionStartedAt ? Date.parse(merged.sessionStartedAt) : existing.session_started_at, merged.lastInteractionAt ? Date.parse(merged.lastInteractionAt) : existing.last_interaction_at, merged.sourceChannel, merged.sourceChatId, merged.sessionType, merged.hiddenFromSessionList ? 1 : 0, merged.parentSessionKey ?? null, merged.workflowRunId ?? null, merged.workflowDefinitionId ?? null, merged.workflowAgentId ?? null, merged.workflowAgentLabel ?? null, merged.routing ? JSON.stringify(merged.routing) : null, merged.customData ? JSON.stringify(merged.customData) : null, merged.abortCutoffTimestamp ?? null, merged.messageCount, merged.estimatedTokens, merged.compactedCount, merged.lastFlushedAt ?? null, merged.flushCount ?? 0, existing.thinking_level, existing.verbose_level, sessionKey);
204
+ WHERE session_key = ?`).run(merged.status, merged.name ?? null, JSON.stringify(merged.tags ?? []), Date.parse(merged.updatedAt) || now, Date.parse(merged.lastAccessedAt) || now, merged.sessionStartedAt ? Date.parse(merged.sessionStartedAt) : existing.session_started_at, merged.lastInteractionAt ? Date.parse(merged.lastInteractionAt) : existing.last_interaction_at, merged.sourceChannel, merged.sourceChatId, merged.sessionType, merged.hiddenFromSessionList ? 1 : 0, merged.parentSessionKey ?? null, merged.workflowRunId ?? null, merged.workflowDefinitionId ?? null, merged.workflowAgentId ?? null, merged.workflowAgentLabel ?? null, merged.projectId ?? null, merged.routing ? JSON.stringify(merged.routing) : null, merged.customData ? JSON.stringify(merged.customData) : null, merged.abortCutoffTimestamp ?? null, merged.messageCount, merged.estimatedTokens, merged.compactedCount, merged.lastFlushedAt ?? null, merged.flushCount ?? 0, existing.thinking_level, existing.verbose_level, sessionKey);
200
205
  const row = readSessionRow(db, sessionKey);
201
206
  if (!row) throw new Error(`Session not found after patch: ${sessionKey}`);
202
207
  return sessionRowToMetadata(sessionKey, row);
@@ -1 +1 @@
1
- {"version":3,"file":"session-repository.js","names":[],"sources":["../../../../src/storage/sqlite/session-repository.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport { validateSessionId } from '../../session/session-id.js';\nimport type { SessionListQuery, SessionMetadata, PaginatedResult } from '../../session/types.js';\nimport { buildDefaultSessionMetadata, type SessionMetadataSeed } from './session-metadata.js';\nimport {\n buildGlobalSessionStats,\n metadataToSessionInsert,\n sessionRowToMetadata,\n type SessionRow,\n} from './row-mappers.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\nconst SESSION_COLUMNS = `\n s.session_key, s.agent_id, s.session_id, s.status, s.name, s.tags_json,\n s.created_at, s.updated_at, s.last_accessed_at, s.session_started_at, s.last_interaction_at,\n s.source_channel, s.source_chat_id, s.session_type, s.hidden_from_session_list,\n s.parent_session_key, s.workflow_run_id, s.workflow_definition_id, s.workflow_agent_id, s.workflow_agent_label,\n s.routing_json, s.custom_data_json,\n s.abort_cutoff_timestamp, s.message_count, s.estimated_tokens, s.compacted_count,\n s.last_flushed_at, s.flush_count,\n s.thinking_level, s.verbose_level,\n t.cwd AS cwd\n`;\n\nimport { escapeFts5Query } from './fts.js';\n\nconst SESSION_FROM_JOIN = `\n FROM sessions s\n LEFT JOIN transcripts t ON t.session_id = s.session_id\n`;\n\nconst SELECT_SESSION = `SELECT ${SESSION_COLUMNS} ${SESSION_FROM_JOIN} WHERE s.session_key = ?`;\n\nfunction readSessionRow(db: DatabaseSync, sessionKey: string): SessionRow | undefined {\n return db.prepare(SELECT_SESSION).get(sessionKey) as SessionRow | undefined;\n}\n\nfunction insertSessionAndTranscript(\n db: DatabaseSync,\n sessionKey: string,\n sessionId: string,\n cwd: string,\n metadata: SessionMetadata,\n): void {\n const row = metadataToSessionInsert(sessionKey, sessionId, metadata);\n db.prepare(\n `INSERT INTO sessions (\n session_key, agent_id, session_id, status, name, tags_json,\n created_at, updated_at, last_accessed_at, session_started_at, last_interaction_at,\n source_channel, source_chat_id, session_type, hidden_from_session_list,\n parent_session_key, workflow_run_id, workflow_definition_id, workflow_agent_id, workflow_agent_label,\n routing_json, custom_data_json,\n abort_cutoff_timestamp, message_count, estimated_tokens, compacted_count,\n last_flushed_at, flush_count,\n thinking_level, verbose_level\n ) VALUES (\n ?, ?, ?, ?, ?, ?,\n ?, ?, ?, ?, ?,\n ?, ?, ?, ?,\n ?, ?, ?, ?, ?,\n ?, ?,\n ?, ?, ?, ?, ?, ?,\n ?, ?\n )`,\n ).run(\n row.sessionKey,\n row.agentId,\n row.sessionId,\n row.status,\n row.name,\n row.tagsJson,\n row.createdAt,\n row.updatedAt,\n row.lastAccessedAt,\n row.sessionStartedAt,\n row.lastInteractionAt,\n row.sourceChannel,\n row.sourceChatId,\n row.sessionType,\n row.hiddenFromSessionList,\n row.parentSessionKey,\n row.workflowRunId,\n row.workflowDefinitionId,\n row.workflowAgentId,\n row.workflowAgentLabel,\n row.routingJson,\n row.customDataJson,\n row.abortCutoffTimestamp,\n row.messageCount,\n row.estimatedTokens,\n row.compactedCount,\n row.lastFlushedAt,\n row.flushCount,\n row.thinkingLevel,\n row.verboseLevel,\n );\n\n const now = Date.now();\n db.prepare(\n `INSERT INTO transcripts (session_id, session_key, status, created_at, cwd)\n VALUES (?, ?, 'active', ?, ?)`,\n ).run(sessionId, sessionKey, now, cwd);\n}\n\nexport function ensureSessionInTransaction(\n db: DatabaseSync,\n sessionKey: string,\n cwd: string,\n seed?: SessionMetadataSeed,\n): SessionMetadata {\n const existing = readSessionRow(db, sessionKey);\n if (existing) {\n return sessionRowToMetadata(sessionKey, existing);\n }\n\n const sessionId = validateSessionId(randomUUID());\n const metadata = buildDefaultSessionMetadata(sessionKey, seed);\n metadata.sessionId = sessionId;\n insertSessionAndTranscript(db, sessionKey, sessionId, cwd, metadata);\n const row = readSessionRow(db, sessionKey);\n if (!row) {\n throw new Error(`Failed to create session: ${sessionKey}`);\n }\n return sessionRowToMetadata(sessionKey, row);\n}\n\nexport function ensureSessionRecord(\n sessionKey: string,\n cwd: string,\n seed?: SessionMetadataSeed,\n): SessionMetadata {\n return runSqliteWriteTransaction((db) => ensureSessionInTransaction(db, sessionKey, cwd, seed));\n}\n\nexport function readCurrentSessionId(db: DatabaseSync, sessionKey: string): string | null {\n const row = db\n .prepare(`SELECT session_id FROM sessions WHERE session_key = ?`)\n .get(sessionKey) as { session_id?: string } | undefined;\n return row?.session_id ?? null;\n}\n\nexport function getSessionMetadata(sessionKey: string): SessionMetadata | null {\n const db = getSqliteDatabase();\n const row = readSessionRow(db, sessionKey);\n if (!row) {\n return null;\n }\n return sessionRowToMetadata(sessionKey, row);\n}\n\nexport function getCurrentSessionId(sessionKey: string): string | null {\n return readCurrentSessionId(getSqliteDatabase(), sessionKey);\n}\n\nexport function listSessionMetadata(query: SessionListQuery = {}): PaginatedResult<SessionMetadata> {\n const db = getSqliteDatabase();\n const conditions: string[] = [];\n const params: Array<string | number> = [];\n\n if (!query.includeHidden) {\n conditions.push(`s.hidden_from_session_list = 0`);\n }\n\n if (query.sessionTypes?.length) {\n conditions.push(`s.session_type IN (${query.sessionTypes.map(() => '?').join(', ')})`);\n params.push(...query.sessionTypes);\n } else if (!query.includeHidden) {\n conditions.push(`s.session_type = ?`);\n params.push('chat');\n }\n\n if (query.status) {\n const statuses = Array.isArray(query.status) ? query.status : [query.status];\n conditions.push(`s.status IN (${statuses.map(() => '?').join(', ')})`);\n params.push(...statuses);\n }\n\n if (query.channel) {\n const rawChannels = query.channel\n .split(',')\n .map((c) => c.trim().toLowerCase())\n .filter(Boolean);\n const channels = [\n ...new Set(\n rawChannels.flatMap((c) => {\n if (c === 'webchat') return ['webchat', 'ui'];\n if (c === 'gateway') return ['gateway', 'webui'];\n return [c];\n }),\n ),\n ];\n if (channels.length === 0) {\n return { items: [], total: 0, limit: query.limit ?? 50, offset: query.offset ?? 0, hasMore: false };\n }\n conditions.push(`LOWER(s.source_channel) IN (${channels.map(() => '?').join(', ')})`);\n params.push(...channels);\n }\n\n if (query.tags?.length) {\n for (const tag of query.tags) {\n conditions.push(`s.tags_json LIKE ?`);\n params.push(`%\"${tag}\"%`);\n }\n }\n\n let searchKeys: string[] | null = null;\n if (query.search?.trim()) {\n const rawSearch = query.search.trim();\n const ftsQuery = escapeFts5Query(rawSearch);\n const ftsRows = db\n .prepare(\n `SELECT DISTINCT session_key FROM transcript_fts WHERE transcript_fts MATCH ? LIMIT 500`,\n )\n .all(ftsQuery) as Array<{ session_key: string }>;\n const ftsKeys = new Set(ftsRows.map((r) => r.session_key));\n const like = `%${rawSearch.toLowerCase()}%`;\n const metaRows = db\n .prepare(\n `SELECT session_key FROM sessions\n WHERE LOWER(session_key) LIKE ?\n OR LOWER(COALESCE(name, '')) LIKE ?\n OR LOWER(source_channel) LIKE ?\n OR LOWER(source_chat_id) LIKE ?\n OR LOWER(tags_json) LIKE ?`,\n )\n .all(like, like, like, like, like) as Array<{ session_key: string }>;\n searchKeys = [...new Set([...ftsKeys, ...metaRows.map((r) => r.session_key)])];\n if (searchKeys.length === 0) {\n return { items: [], total: 0, limit: query.limit ?? 50, offset: query.offset ?? 0, hasMore: false };\n }\n conditions.push(`s.session_key IN (${searchKeys.map(() => '?').join(', ')})`);\n params.push(...searchKeys);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const sortColumn = sessionSortColumn(query.sortBy);\n const sortOrder = query.sortOrder === 'asc' ? 'ASC' : 'DESC';\n\n const countRow = db\n .prepare(`SELECT COUNT(*) AS total FROM sessions s ${where}`)\n .get(...params) as { total: number };\n const total = countRow.total;\n\n const limit = query.limit ?? 50;\n const offset = query.offset ?? 0;\n const rows = db\n .prepare(\n `SELECT ${SESSION_COLUMNS} ${SESSION_FROM_JOIN} ${where}\n ORDER BY ${sortColumn} ${sortOrder}\n LIMIT ? OFFSET ?`,\n )\n .all(...params, limit, offset) as SessionRow[];\n\n const items = rows.map((row) => sessionRowToMetadata(row.session_key, row));\n return { items, total, limit, offset, hasMore: offset + limit < total };\n}\n\nfunction sessionSortColumn(sortBy: SessionListQuery['sortBy']): string {\n switch (sortBy) {\n case 'createdAt':\n return 's.created_at';\n case 'messageCount':\n return 's.message_count';\n case 'lastAccessedAt':\n return 's.last_accessed_at';\n case 'updatedAt':\n default:\n return 's.updated_at';\n }\n}\n\nexport function patchSessionMetadata(\n sessionKey: string,\n updates: Partial<SessionMetadata>,\n): SessionMetadata {\n return runSqliteWriteTransaction((db) => {\n const existing = readSessionRow(db, sessionKey);\n if (!existing) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n\n const current = sessionRowToMetadata(sessionKey, existing);\n const merged = { ...current, ...updates, key: sessionKey };\n const now = Date.now();\n\n db.prepare(\n `UPDATE sessions SET\n status = ?,\n name = ?,\n tags_json = ?,\n updated_at = ?,\n last_accessed_at = ?,\n session_started_at = ?,\n last_interaction_at = ?,\n source_channel = ?,\n source_chat_id = ?,\n session_type = ?,\n hidden_from_session_list = ?,\n parent_session_key = ?,\n workflow_run_id = ?,\n workflow_definition_id = ?,\n workflow_agent_id = ?,\n workflow_agent_label = ?,\n routing_json = ?,\n custom_data_json = ?,\n abort_cutoff_timestamp = ?,\n message_count = ?,\n estimated_tokens = ?,\n compacted_count = ?,\n last_flushed_at = ?,\n flush_count = ?,\n thinking_level = ?,\n verbose_level = ?\n WHERE session_key = ?`,\n ).run(\n merged.status,\n merged.name ?? null,\n JSON.stringify(merged.tags ?? []),\n Date.parse(merged.updatedAt) || now,\n Date.parse(merged.lastAccessedAt) || now,\n merged.sessionStartedAt ? Date.parse(merged.sessionStartedAt) : existing.session_started_at,\n merged.lastInteractionAt ? Date.parse(merged.lastInteractionAt) : existing.last_interaction_at,\n merged.sourceChannel,\n merged.sourceChatId,\n merged.sessionType,\n merged.hiddenFromSessionList ? 1 : 0,\n merged.parentSessionKey ?? null,\n merged.workflowRunId ?? null,\n merged.workflowDefinitionId ?? null,\n merged.workflowAgentId ?? null,\n merged.workflowAgentLabel ?? null,\n merged.routing ? JSON.stringify(merged.routing) : null,\n merged.customData ? JSON.stringify(merged.customData) : null,\n merged.abortCutoffTimestamp ?? null,\n merged.messageCount,\n merged.estimatedTokens,\n merged.compactedCount,\n merged.lastFlushedAt ?? null,\n merged.flushCount ?? 0,\n existing.thinking_level,\n existing.verbose_level,\n sessionKey,\n );\n\n const row = readSessionRow(db, sessionKey);\n if (!row) {\n throw new Error(`Session not found after patch: ${sessionKey}`);\n }\n return sessionRowToMetadata(sessionKey, row);\n });\n}\n\nexport function updateSessionStats(\n sessionKey: string,\n stats: { messageCount: number; estimatedTokens: number; lastInteractionAt?: number },\n): void {\n runSqliteWriteTransaction((db) => {\n const now = stats.lastInteractionAt ?? Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = ?,\n estimated_tokens = ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(stats.messageCount, stats.estimatedTokens, now, now, now, sessionKey);\n });\n}\n\nexport function incrementSessionStatsOnAppend(sessionKey: string, tokenDelta = 0): void {\n runSqliteWriteTransaction((db) => {\n const now = Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = message_count + 1,\n estimated_tokens = estimated_tokens + ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(tokenDelta, now, now, now, sessionKey);\n });\n}\n\nexport function resetSessionRecord(\n sessionKey: string,\n cwd: string,\n): { sessionId: string; previousSessionId: string } | null {\n return runSqliteWriteTransaction((db) => {\n const existing = readSessionRow(db, sessionKey);\n if (!existing) {\n return null;\n }\n\n const previousSessionId = existing.session_id;\n const now = Date.now();\n db.prepare(\n `UPDATE transcripts SET status = 'archived', archive_reason = 'reset', archived_at = ?\n WHERE session_id = ?`,\n ).run(now, previousSessionId);\n\n const newSessionId = validateSessionId(randomUUID());\n db.prepare(\n `INSERT INTO transcripts (session_id, session_key, status, created_at, cwd)\n VALUES (?, ?, 'active', ?, ?)`,\n ).run(newSessionId, sessionKey, now, cwd);\n\n db.prepare(\n `UPDATE sessions SET\n session_id = ?,\n updated_at = ?,\n session_started_at = ?,\n last_interaction_at = NULL,\n message_count = 0,\n estimated_tokens = 0\n WHERE session_key = ?`,\n ).run(newSessionId, now, now, sessionKey);\n\n return { sessionId: newSessionId, previousSessionId };\n });\n}\n\nexport function deleteSessionRecord(sessionKey: string): boolean {\n return runSqliteWriteTransaction((db) => {\n const existing = readSessionRow(db, sessionKey);\n if (!existing) {\n return false;\n }\n\n const now = Date.now();\n db.prepare(\n `UPDATE transcripts SET status = 'archived', archive_reason = 'delete', archived_at = ?\n WHERE session_id = ?`,\n ).run(now, existing.session_id);\n\n db.prepare(`DELETE FROM sessions WHERE session_key = ?`).run(sessionKey);\n return true;\n });\n}\n\nexport function listSessionsByAgent(agentId: string): SessionMetadata[] {\n const db = getSqliteDatabase();\n const rows = db\n .prepare(\n `SELECT ${SESSION_COLUMNS} ${SESSION_FROM_JOIN}\n WHERE s.agent_id = ?\n ORDER BY s.updated_at DESC`,\n )\n .all(agentId.toLowerCase()) as SessionRow[];\n return rows.map((row) => sessionRowToMetadata(row.session_key, row));\n}\n\nexport function getSessionPersistedLevels(sessionKey: string): {\n thinkingLevel: string | null;\n verboseLevel: string | null;\n} | null {\n const db = getSqliteDatabase();\n const row = db\n .prepare(`SELECT thinking_level, verbose_level FROM sessions WHERE session_key = ?`)\n .get(sessionKey) as { thinking_level: string | null; verbose_level: string | null } | undefined;\n if (!row) {\n return null;\n }\n return { thinkingLevel: row.thinking_level, verboseLevel: row.verbose_level };\n}\n\nexport function findSessionKeyBySessionId(sessionId: string): string | null {\n const db = getSqliteDatabase();\n const row = db\n .prepare(`SELECT session_key FROM sessions WHERE session_id = ?`)\n .get(sessionId) as { session_key?: string } | undefined;\n return row?.session_key ?? null;\n}\n\nexport function getGlobalSessionStats(): ReturnType<typeof buildGlobalSessionStats> {\n const all = listSessionMetadata({ limit: 100_000, offset: 0 });\n return buildGlobalSessionStats(all.items);\n}\n\nexport function resolveSessionAgentId(sessionKey: string): string {\n return getSessionMetadata(sessionKey)?.routing?.agentId ?? 'main';\n}\n"],"mappings":";;;;;;;AAcA,MAAM,kBAAkB;;;;;;;;;;;AAcxB,MAAM,oBAAoB;;;;AAK1B,MAAM,iBAAiB,UAAU,gBAAgB,GAAG,kBAAkB;AAEtE,SAAS,eAAe,IAAkB,YAA4C;AACpF,QAAO,GAAG,QAAQ,eAAe,CAAC,IAAI,WAAW;;AAGnD,SAAS,2BACP,IACA,YACA,WACA,KACA,UACM;CACN,MAAM,MAAM,wBAAwB,YAAY,WAAW,SAAS;AACpE,IAAG,QACD;;;;;;;;;;;;;;;;;OAkBD,CAAC,IACA,IAAI,YACJ,IAAI,SACJ,IAAI,WACJ,IAAI,QACJ,IAAI,MACJ,IAAI,UACJ,IAAI,WACJ,IAAI,WACJ,IAAI,gBACJ,IAAI,kBACJ,IAAI,mBACJ,IAAI,eACJ,IAAI,cACJ,IAAI,aACJ,IAAI,uBACJ,IAAI,kBACJ,IAAI,eACJ,IAAI,sBACJ,IAAI,iBACJ,IAAI,oBACJ,IAAI,aACJ,IAAI,gBACJ,IAAI,sBACJ,IAAI,cACJ,IAAI,iBACJ,IAAI,gBACJ,IAAI,eACJ,IAAI,YACJ,IAAI,eACJ,IAAI,aACL;CAED,MAAM,MAAM,KAAK,KAAK;AACtB,IAAG,QACD;oCAED,CAAC,IAAI,WAAW,YAAY,KAAK,IAAI;;AAGxC,SAAgB,2BACd,IACA,YACA,KACA,MACiB;CACjB,MAAM,WAAW,eAAe,IAAI,WAAW;AAC/C,KAAI,SACF,QAAO,qBAAqB,YAAY,SAAS;CAGnD,MAAM,YAAY,kBAAkB,YAAY,CAAC;CACjD,MAAM,WAAW,4BAA4B,YAAY,KAAK;AAC9D,UAAS,YAAY;AACrB,4BAA2B,IAAI,YAAY,WAAW,KAAK,SAAS;CACpE,MAAM,MAAM,eAAe,IAAI,WAAW;AAC1C,KAAI,CAAC,IACH,OAAM,IAAI,MAAM,6BAA6B,aAAa;AAE5D,QAAO,qBAAqB,YAAY,IAAI;;AAG9C,SAAgB,oBACd,YACA,KACA,MACiB;AACjB,QAAO,2BAA2B,OAAO,2BAA2B,IAAI,YAAY,KAAK,KAAK,CAAC;;AAGjG,SAAgB,qBAAqB,IAAkB,YAAmC;AAIxF,QAHY,GACT,QAAQ,wDAAwD,CAChE,IAAI,WACG,EAAE,cAAc;;AAG5B,SAAgB,mBAAmB,YAA4C;CAE7E,MAAM,MAAM,eADD,mBACkB,EAAE,WAAW;AAC1C,KAAI,CAAC,IACH,QAAO;AAET,QAAO,qBAAqB,YAAY,IAAI;;AAG9C,SAAgB,oBAAoB,YAAmC;AACrE,QAAO,qBAAqB,mBAAmB,EAAE,WAAW;;AAG9D,SAAgB,oBAAoB,QAA0B,EAAE,EAAoC;CAClG,MAAM,KAAK,mBAAmB;CAC9B,MAAM,aAAuB,EAAE;CAC/B,MAAM,SAAiC,EAAE;AAEzC,KAAI,CAAC,MAAM,cACT,YAAW,KAAK,iCAAiC;AAGnD,KAAI,MAAM,cAAc,QAAQ;AAC9B,aAAW,KAAK,sBAAsB,MAAM,aAAa,UAAU,IAAI,CAAC,KAAK,KAAK,CAAC,GAAG;AACtF,SAAO,KAAK,GAAG,MAAM,aAAa;YACzB,CAAC,MAAM,eAAe;AAC/B,aAAW,KAAK,qBAAqB;AACrC,SAAO,KAAK,OAAO;;AAGrB,KAAI,MAAM,QAAQ;EAChB,MAAM,WAAW,MAAM,QAAQ,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,MAAM,OAAO;AAC5E,aAAW,KAAK,gBAAgB,SAAS,UAAU,IAAI,CAAC,KAAK,KAAK,CAAC,GAAG;AACtE,SAAO,KAAK,GAAG,SAAS;;AAG1B,KAAI,MAAM,SAAS;EACjB,MAAM,cAAc,MAAM,QACvB,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC,CAClC,OAAO,QAAQ;EAClB,MAAM,WAAW,CACf,GAAG,IAAI,IACL,YAAY,SAAS,MAAM;AACzB,OAAI,MAAM,UAAW,QAAO,CAAC,WAAW,KAAK;AAC7C,OAAI,MAAM,UAAW,QAAO,CAAC,WAAW,QAAQ;AAChD,UAAO,CAAC,EAAE;IACV,CACH,CACF;AACD,MAAI,SAAS,WAAW,EACtB,QAAO;GAAE,OAAO,EAAE;GAAE,OAAO;GAAG,OAAO,MAAM,SAAS;GAAI,QAAQ,MAAM,UAAU;GAAG,SAAS;GAAO;AAErG,aAAW,KAAK,+BAA+B,SAAS,UAAU,IAAI,CAAC,KAAK,KAAK,CAAC,GAAG;AACrF,SAAO,KAAK,GAAG,SAAS;;AAG1B,KAAI,MAAM,MAAM,OACd,MAAK,MAAM,OAAO,MAAM,MAAM;AAC5B,aAAW,KAAK,qBAAqB;AACrC,SAAO,KAAK,KAAK,IAAI,IAAI;;CAI7B,IAAI,aAA8B;AAClC,KAAI,MAAM,QAAQ,MAAM,EAAE;EACxB,MAAM,YAAY,MAAM,OAAO,MAAM;EACrC,MAAM,WAAW,gBAAgB,UAAU;EAC3C,MAAM,UAAU,GACb,QACC,yFACD,CACA,IAAI,SAAS;EAChB,MAAM,UAAU,IAAI,IAAI,QAAQ,KAAK,MAAM,EAAE,YAAY,CAAC;EAC1D,MAAM,OAAO,IAAI,UAAU,aAAa,CAAC;EACzC,MAAM,WAAW,GACd,QACC;;;;;wCAMD,CACA,IAAI,MAAM,MAAM,MAAM,MAAM,KAAK;AACpC,eAAa,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,SAAS,GAAG,SAAS,KAAK,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;AAC9E,MAAI,WAAW,WAAW,EACxB,QAAO;GAAE,OAAO,EAAE;GAAE,OAAO;GAAG,OAAO,MAAM,SAAS;GAAI,QAAQ,MAAM,UAAU;GAAG,SAAS;GAAO;AAErG,aAAW,KAAK,qBAAqB,WAAW,UAAU,IAAI,CAAC,KAAK,KAAK,CAAC,GAAG;AAC7E,SAAO,KAAK,GAAG,WAAW;;CAG5B,MAAM,QAAQ,WAAW,SAAS,IAAI,SAAS,WAAW,KAAK,QAAQ,KAAK;CAC5E,MAAM,aAAa,kBAAkB,MAAM,OAAO;CAClD,MAAM,YAAY,MAAM,cAAc,QAAQ,QAAQ;CAKtD,MAAM,QAHW,GACd,QAAQ,4CAA4C,QAAQ,CAC5D,IAAI,GAAG,OACY,CAAC;CAEvB,MAAM,QAAQ,MAAM,SAAS;CAC7B,MAAM,SAAS,MAAM,UAAU;AAU/B,QAAO;EAAE,OATI,GACV,QACC,UAAU,gBAAgB,GAAG,kBAAkB,GAAG,MAAM;kBAC5C,WAAW,GAAG,UAAU;yBAErC,CACA,IAAI,GAAG,QAAQ,OAAO,OAEP,CAAC,KAAK,QAAQ,qBAAqB,IAAI,aAAa,IAAI,CAC5D;EAAE;EAAO;EAAO;EAAQ,SAAS,SAAS,QAAQ;EAAO;;AAGzE,SAAS,kBAAkB,QAA4C;AACrE,SAAQ,QAAR;EACE,KAAK,YACH,QAAO;EACT,KAAK,eACH,QAAO;EACT,KAAK,iBACH,QAAO;EAET,QACE,QAAO;;;AAIb,SAAgB,qBACd,YACA,SACiB;AACjB,QAAO,2BAA2B,OAAO;EACvC,MAAM,WAAW,eAAe,IAAI,WAAW;AAC/C,MAAI,CAAC,SACH,OAAM,IAAI,MAAM,sBAAsB,aAAa;EAIrD,MAAM,SAAS;GAAE,GADD,qBAAqB,YAAY,SACtB;GAAE,GAAG;GAAS,KAAK;GAAY;EAC1D,MAAM,MAAM,KAAK,KAAK;AAEtB,KAAG,QACD;;;;;;;;;;;;;;;;;;;;;;;;;;;6BA4BD,CAAC,IACA,OAAO,QACP,OAAO,QAAQ,MACf,KAAK,UAAU,OAAO,QAAQ,EAAE,CAAC,EACjC,KAAK,MAAM,OAAO,UAAU,IAAI,KAChC,KAAK,MAAM,OAAO,eAAe,IAAI,KACrC,OAAO,mBAAmB,KAAK,MAAM,OAAO,iBAAiB,GAAG,SAAS,oBACzE,OAAO,oBAAoB,KAAK,MAAM,OAAO,kBAAkB,GAAG,SAAS,qBAC3E,OAAO,eACP,OAAO,cACP,OAAO,aACP,OAAO,wBAAwB,IAAI,GACnC,OAAO,oBAAoB,MAC3B,OAAO,iBAAiB,MACxB,OAAO,wBAAwB,MAC/B,OAAO,mBAAmB,MAC1B,OAAO,sBAAsB,MAC7B,OAAO,UAAU,KAAK,UAAU,OAAO,QAAQ,GAAG,MAClD,OAAO,aAAa,KAAK,UAAU,OAAO,WAAW,GAAG,MACxD,OAAO,wBAAwB,MAC/B,OAAO,cACP,OAAO,iBACP,OAAO,gBACP,OAAO,iBAAiB,MACxB,OAAO,cAAc,GACrB,SAAS,gBACT,SAAS,eACT,WACD;EAED,MAAM,MAAM,eAAe,IAAI,WAAW;AAC1C,MAAI,CAAC,IACH,OAAM,IAAI,MAAM,kCAAkC,aAAa;AAEjE,SAAO,qBAAqB,YAAY,IAAI;GAC5C;;AAGJ,SAAgB,mBACd,YACA,OACM;AACN,4BAA2B,OAAO;EAChC,MAAM,MAAM,MAAM,qBAAqB,KAAK,KAAK;AACjD,KAAG,QACD;;;;;;6BAOD,CAAC,IAAI,MAAM,cAAc,MAAM,iBAAiB,KAAK,KAAK,KAAK,WAAW;GAC3E;;AAGJ,SAAgB,8BAA8B,YAAoB,aAAa,GAAS;AACtF,4BAA2B,OAAO;EAChC,MAAM,MAAM,KAAK,KAAK;AACtB,KAAG,QACD;;;;;;6BAOD,CAAC,IAAI,YAAY,KAAK,KAAK,KAAK,WAAW;GAC5C;;AAGJ,SAAgB,mBACd,YACA,KACyD;AACzD,QAAO,2BAA2B,OAAO;EACvC,MAAM,WAAW,eAAe,IAAI,WAAW;AAC/C,MAAI,CAAC,SACH,QAAO;EAGT,MAAM,oBAAoB,SAAS;EACnC,MAAM,MAAM,KAAK,KAAK;AACtB,KAAG,QACD;6BAED,CAAC,IAAI,KAAK,kBAAkB;EAE7B,MAAM,eAAe,kBAAkB,YAAY,CAAC;AACpD,KAAG,QACD;sCAED,CAAC,IAAI,cAAc,YAAY,KAAK,IAAI;AAEzC,KAAG,QACD;;;;;;;6BAQD,CAAC,IAAI,cAAc,KAAK,KAAK,WAAW;AAEzC,SAAO;GAAE,WAAW;GAAc;GAAmB;GACrD;;AAGJ,SAAgB,oBAAoB,YAA6B;AAC/D,QAAO,2BAA2B,OAAO;EACvC,MAAM,WAAW,eAAe,IAAI,WAAW;AAC/C,MAAI,CAAC,SACH,QAAO;EAGT,MAAM,MAAM,KAAK,KAAK;AACtB,KAAG,QACD;6BAED,CAAC,IAAI,KAAK,SAAS,WAAW;AAE/B,KAAG,QAAQ,6CAA6C,CAAC,IAAI,WAAW;AACxE,SAAO;GACP;;AAGJ,SAAgB,oBAAoB,SAAoC;AAStE,QARW,mBACI,CACZ,QACC,UAAU,gBAAgB,GAAG,kBAAkB;;mCAGhD,CACA,IAAI,QAAQ,aAAa,CACjB,CAAC,KAAK,QAAQ,qBAAqB,IAAI,aAAa,IAAI,CAAC;;AAGtE,SAAgB,0BAA0B,YAGjC;CAEP,MAAM,MADK,mBACG,CACX,QAAQ,2EAA2E,CACnF,IAAI,WAAW;AAClB,KAAI,CAAC,IACH,QAAO;AAET,QAAO;EAAE,eAAe,IAAI;EAAgB,cAAc,IAAI;EAAe;;AAG/E,SAAgB,0BAA0B,WAAkC;AAK1E,QAJW,mBACG,CACX,QAAQ,wDAAwD,CAChE,IAAI,UACG,EAAE,eAAe;;AAG7B,SAAgB,wBAAoE;AAElF,QAAO,wBADK,oBAAoB;EAAE,OAAO;EAAS,QAAQ;EAAG,CAC3B,CAAC,MAAM;;AAG3C,SAAgB,sBAAsB,YAA4B;AAChE,QAAO,mBAAmB,WAAW,EAAE,SAAS,WAAW"}
1
+ {"version":3,"file":"session-repository.js","names":[],"sources":["../../../../src/storage/sqlite/session-repository.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport { validateSessionId } from '../../session/session-id.js';\nimport type { SessionListQuery, SessionMetadata, PaginatedResult } from '../../session/types.js';\nimport { buildDefaultSessionMetadata, type SessionMetadataSeed } from './session-metadata.js';\nimport {\n buildGlobalSessionStats,\n metadataToSessionInsert,\n sessionRowToMetadata,\n type SessionRow,\n} from './row-mappers.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\nconst SESSION_COLUMNS = `\n s.session_key, s.agent_id, s.session_id, s.status, s.name, s.tags_json,\n s.created_at, s.updated_at, s.last_accessed_at, s.session_started_at, s.last_interaction_at,\n s.source_channel, s.source_chat_id, s.session_type, s.hidden_from_session_list,\n s.parent_session_key, s.workflow_run_id, s.workflow_definition_id, s.workflow_agent_id, s.workflow_agent_label,\n s.project_id, s.routing_json, s.custom_data_json,\n s.abort_cutoff_timestamp, s.message_count, s.estimated_tokens, s.compacted_count,\n s.last_flushed_at, s.flush_count,\n s.thinking_level, s.verbose_level,\n t.cwd AS cwd\n`;\n\nimport { escapeFts5Query } from './fts.js';\n\nconst SESSION_FROM_JOIN = `\n FROM sessions s\n LEFT JOIN transcripts t ON t.session_id = s.session_id\n`;\n\nconst SELECT_SESSION = `SELECT ${SESSION_COLUMNS} ${SESSION_FROM_JOIN} WHERE s.session_key = ?`;\n\nfunction readSessionRow(db: DatabaseSync, sessionKey: string): SessionRow | undefined {\n return db.prepare(SELECT_SESSION).get(sessionKey) as SessionRow | undefined;\n}\n\nfunction insertSessionAndTranscript(\n db: DatabaseSync,\n sessionKey: string,\n sessionId: string,\n cwd: string,\n metadata: SessionMetadata,\n): void {\n const row = metadataToSessionInsert(sessionKey, sessionId, metadata);\n db.prepare(\n `INSERT INTO sessions (\n session_key, agent_id, session_id, status, name, tags_json,\n created_at, updated_at, last_accessed_at, session_started_at, last_interaction_at,\n source_channel, source_chat_id, session_type, hidden_from_session_list,\n parent_session_key, workflow_run_id, workflow_definition_id, workflow_agent_id, workflow_agent_label,\n project_id, routing_json, custom_data_json,\n abort_cutoff_timestamp, message_count, estimated_tokens, compacted_count,\n last_flushed_at, flush_count,\n thinking_level, verbose_level\n ) VALUES (\n ?, ?, ?, ?, ?, ?,\n ?, ?, ?, ?, ?,\n ?, ?, ?, ?,\n ?, ?, ?, ?, ?,\n ?, ?, ?,\n ?, ?, ?, ?, ?, ?,\n ?, ?\n )`,\n ).run(\n row.sessionKey,\n row.agentId,\n row.sessionId,\n row.status,\n row.name,\n row.tagsJson,\n row.createdAt,\n row.updatedAt,\n row.lastAccessedAt,\n row.sessionStartedAt,\n row.lastInteractionAt,\n row.sourceChannel,\n row.sourceChatId,\n row.sessionType,\n row.hiddenFromSessionList,\n row.parentSessionKey,\n row.workflowRunId,\n row.workflowDefinitionId,\n row.workflowAgentId,\n row.workflowAgentLabel,\n row.projectId,\n row.routingJson,\n row.customDataJson,\n row.abortCutoffTimestamp,\n row.messageCount,\n row.estimatedTokens,\n row.compactedCount,\n row.lastFlushedAt,\n row.flushCount,\n row.thinkingLevel,\n row.verboseLevel,\n );\n\n const now = Date.now();\n db.prepare(\n `INSERT INTO transcripts (session_id, session_key, status, created_at, cwd)\n VALUES (?, ?, 'active', ?, ?)`,\n ).run(sessionId, sessionKey, now, cwd);\n}\n\nexport function ensureSessionInTransaction(\n db: DatabaseSync,\n sessionKey: string,\n cwd: string,\n seed?: SessionMetadataSeed,\n): SessionMetadata {\n const existing = readSessionRow(db, sessionKey);\n if (existing) {\n return sessionRowToMetadata(sessionKey, existing);\n }\n\n const sessionId = validateSessionId(randomUUID());\n const metadata = buildDefaultSessionMetadata(sessionKey, seed);\n metadata.sessionId = sessionId;\n insertSessionAndTranscript(db, sessionKey, sessionId, cwd, metadata);\n const row = readSessionRow(db, sessionKey);\n if (!row) {\n throw new Error(`Failed to create session: ${sessionKey}`);\n }\n return sessionRowToMetadata(sessionKey, row);\n}\n\nexport function ensureSessionRecord(\n sessionKey: string,\n cwd: string,\n seed?: SessionMetadataSeed,\n): SessionMetadata {\n return runSqliteWriteTransaction((db) => ensureSessionInTransaction(db, sessionKey, cwd, seed));\n}\n\nexport function readCurrentSessionId(db: DatabaseSync, sessionKey: string): string | null {\n const row = db\n .prepare(`SELECT session_id FROM sessions WHERE session_key = ?`)\n .get(sessionKey) as { session_id?: string } | undefined;\n return row?.session_id ?? null;\n}\n\nexport function getSessionMetadata(sessionKey: string): SessionMetadata | null {\n const db = getSqliteDatabase();\n const row = readSessionRow(db, sessionKey);\n if (!row) {\n return null;\n }\n return sessionRowToMetadata(sessionKey, row);\n}\n\nexport function getCurrentSessionId(sessionKey: string): string | null {\n return readCurrentSessionId(getSqliteDatabase(), sessionKey);\n}\n\nexport function listSessionMetadata(query: SessionListQuery = {}): PaginatedResult<SessionMetadata> {\n const db = getSqliteDatabase();\n const conditions: string[] = [];\n const params: Array<string | number> = [];\n\n if (!query.includeHidden) {\n conditions.push(`s.hidden_from_session_list = 0`);\n }\n\n if (query.sessionTypes?.length) {\n conditions.push(`s.session_type IN (${query.sessionTypes.map(() => '?').join(', ')})`);\n params.push(...query.sessionTypes);\n } else if (!query.includeHidden) {\n conditions.push(`s.session_type = ?`);\n params.push('chat');\n }\n\n if (query.status) {\n const statuses = Array.isArray(query.status) ? query.status : [query.status];\n conditions.push(`s.status IN (${statuses.map(() => '?').join(', ')})`);\n params.push(...statuses);\n }\n\n if (query.projectId) {\n conditions.push(`s.project_id = ?`);\n params.push(query.projectId);\n }\n\n if (query.channel) {\n const rawChannels = query.channel\n .split(',')\n .map((c) => c.trim().toLowerCase())\n .filter(Boolean);\n const channels = [\n ...new Set(\n rawChannels.flatMap((c) => {\n if (c === 'webchat') return ['webchat', 'ui'];\n if (c === 'gateway') return ['gateway', 'webui'];\n return [c];\n }),\n ),\n ];\n if (channels.length === 0) {\n return { items: [], total: 0, limit: query.limit ?? 50, offset: query.offset ?? 0, hasMore: false };\n }\n conditions.push(`LOWER(s.source_channel) IN (${channels.map(() => '?').join(', ')})`);\n params.push(...channels);\n }\n\n if (query.tags?.length) {\n for (const tag of query.tags) {\n conditions.push(`s.tags_json LIKE ?`);\n params.push(`%\"${tag}\"%`);\n }\n }\n\n let searchKeys: string[] | null = null;\n if (query.search?.trim()) {\n const rawSearch = query.search.trim();\n const ftsQuery = escapeFts5Query(rawSearch);\n const ftsRows = db\n .prepare(\n `SELECT DISTINCT session_key FROM transcript_fts WHERE transcript_fts MATCH ? LIMIT 500`,\n )\n .all(ftsQuery) as Array<{ session_key: string }>;\n const ftsKeys = new Set(ftsRows.map((r) => r.session_key));\n const like = `%${rawSearch.toLowerCase()}%`;\n const metaRows = db\n .prepare(\n `SELECT session_key FROM sessions\n WHERE LOWER(session_key) LIKE ?\n OR LOWER(COALESCE(name, '')) LIKE ?\n OR LOWER(source_channel) LIKE ?\n OR LOWER(source_chat_id) LIKE ?\n OR LOWER(tags_json) LIKE ?`,\n )\n .all(like, like, like, like, like) as Array<{ session_key: string }>;\n searchKeys = [...new Set([...ftsKeys, ...metaRows.map((r) => r.session_key)])];\n if (searchKeys.length === 0) {\n return { items: [], total: 0, limit: query.limit ?? 50, offset: query.offset ?? 0, hasMore: false };\n }\n conditions.push(`s.session_key IN (${searchKeys.map(() => '?').join(', ')})`);\n params.push(...searchKeys);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const sortColumn = sessionSortColumn(query.sortBy);\n const sortOrder = query.sortOrder === 'asc' ? 'ASC' : 'DESC';\n\n const countRow = db\n .prepare(`SELECT COUNT(*) AS total FROM sessions s ${where}`)\n .get(...params) as { total: number };\n const total = countRow.total;\n\n const limit = query.limit ?? 50;\n const offset = query.offset ?? 0;\n const rows = db\n .prepare(\n `SELECT ${SESSION_COLUMNS} ${SESSION_FROM_JOIN} ${where}\n ORDER BY ${sortColumn} ${sortOrder}\n LIMIT ? OFFSET ?`,\n )\n .all(...params, limit, offset) as SessionRow[];\n\n const items = rows.map((row) => sessionRowToMetadata(row.session_key, row));\n return { items, total, limit, offset, hasMore: offset + limit < total };\n}\n\nfunction sessionSortColumn(sortBy: SessionListQuery['sortBy']): string {\n switch (sortBy) {\n case 'createdAt':\n return 's.created_at';\n case 'messageCount':\n return 's.message_count';\n case 'lastAccessedAt':\n return 's.last_accessed_at';\n case 'updatedAt':\n default:\n return 's.updated_at';\n }\n}\n\nexport function patchSessionMetadata(\n sessionKey: string,\n updates: Partial<SessionMetadata>,\n): SessionMetadata {\n return runSqliteWriteTransaction((db) => {\n const existing = readSessionRow(db, sessionKey);\n if (!existing) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n\n const current = sessionRowToMetadata(sessionKey, existing);\n const merged = { ...current, ...updates, key: sessionKey };\n const now = Date.now();\n\n db.prepare(\n `UPDATE sessions SET\n status = ?,\n name = ?,\n tags_json = ?,\n updated_at = ?,\n last_accessed_at = ?,\n session_started_at = ?,\n last_interaction_at = ?,\n source_channel = ?,\n source_chat_id = ?,\n session_type = ?,\n hidden_from_session_list = ?,\n parent_session_key = ?,\n workflow_run_id = ?,\n workflow_definition_id = ?,\n workflow_agent_id = ?,\n workflow_agent_label = ?,\n project_id = ?,\n routing_json = ?,\n custom_data_json = ?,\n abort_cutoff_timestamp = ?,\n message_count = ?,\n estimated_tokens = ?,\n compacted_count = ?,\n last_flushed_at = ?,\n flush_count = ?,\n thinking_level = ?,\n verbose_level = ?\n WHERE session_key = ?`,\n ).run(\n merged.status,\n merged.name ?? null,\n JSON.stringify(merged.tags ?? []),\n Date.parse(merged.updatedAt) || now,\n Date.parse(merged.lastAccessedAt) || now,\n merged.sessionStartedAt ? Date.parse(merged.sessionStartedAt) : existing.session_started_at,\n merged.lastInteractionAt ? Date.parse(merged.lastInteractionAt) : existing.last_interaction_at,\n merged.sourceChannel,\n merged.sourceChatId,\n merged.sessionType,\n merged.hiddenFromSessionList ? 1 : 0,\n merged.parentSessionKey ?? null,\n merged.workflowRunId ?? null,\n merged.workflowDefinitionId ?? null,\n merged.workflowAgentId ?? null,\n merged.workflowAgentLabel ?? null,\n merged.projectId ?? null,\n merged.routing ? JSON.stringify(merged.routing) : null,\n merged.customData ? JSON.stringify(merged.customData) : null,\n merged.abortCutoffTimestamp ?? null,\n merged.messageCount,\n merged.estimatedTokens,\n merged.compactedCount,\n merged.lastFlushedAt ?? null,\n merged.flushCount ?? 0,\n existing.thinking_level,\n existing.verbose_level,\n sessionKey,\n );\n\n const row = readSessionRow(db, sessionKey);\n if (!row) {\n throw new Error(`Session not found after patch: ${sessionKey}`);\n }\n return sessionRowToMetadata(sessionKey, row);\n });\n}\n\nexport function updateSessionStats(\n sessionKey: string,\n stats: { messageCount: number; estimatedTokens: number; lastInteractionAt?: number },\n): void {\n runSqliteWriteTransaction((db) => {\n const now = stats.lastInteractionAt ?? Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = ?,\n estimated_tokens = ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(stats.messageCount, stats.estimatedTokens, now, now, now, sessionKey);\n });\n}\n\nexport function incrementSessionStatsOnAppend(sessionKey: string, tokenDelta = 0): void {\n runSqliteWriteTransaction((db) => {\n const now = Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = message_count + 1,\n estimated_tokens = estimated_tokens + ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(tokenDelta, now, now, now, sessionKey);\n });\n}\n\nexport function resetSessionRecord(\n sessionKey: string,\n cwd: string,\n): { sessionId: string; previousSessionId: string } | null {\n return runSqliteWriteTransaction((db) => {\n const existing = readSessionRow(db, sessionKey);\n if (!existing) {\n return null;\n }\n\n const previousSessionId = existing.session_id;\n const now = Date.now();\n db.prepare(\n `UPDATE transcripts SET status = 'archived', archive_reason = 'reset', archived_at = ?\n WHERE session_id = ?`,\n ).run(now, previousSessionId);\n\n const newSessionId = validateSessionId(randomUUID());\n db.prepare(\n `INSERT INTO transcripts (session_id, session_key, status, created_at, cwd)\n VALUES (?, ?, 'active', ?, ?)`,\n ).run(newSessionId, sessionKey, now, cwd);\n\n db.prepare(\n `UPDATE sessions SET\n session_id = ?,\n updated_at = ?,\n session_started_at = ?,\n last_interaction_at = NULL,\n message_count = 0,\n estimated_tokens = 0\n WHERE session_key = ?`,\n ).run(newSessionId, now, now, sessionKey);\n\n return { sessionId: newSessionId, previousSessionId };\n });\n}\n\nexport function deleteSessionRecord(sessionKey: string): boolean {\n return runSqliteWriteTransaction((db) => {\n const existing = readSessionRow(db, sessionKey);\n if (!existing) {\n return false;\n }\n\n const now = Date.now();\n db.prepare(\n `UPDATE transcripts SET status = 'archived', archive_reason = 'delete', archived_at = ?\n WHERE session_id = ?`,\n ).run(now, existing.session_id);\n\n db.prepare(`DELETE FROM sessions WHERE session_key = ?`).run(sessionKey);\n return true;\n });\n}\n\nexport function listSessionsByAgent(agentId: string): SessionMetadata[] {\n const db = getSqliteDatabase();\n const rows = db\n .prepare(\n `SELECT ${SESSION_COLUMNS} ${SESSION_FROM_JOIN}\n WHERE s.agent_id = ?\n ORDER BY s.updated_at DESC`,\n )\n .all(agentId.toLowerCase()) as SessionRow[];\n return rows.map((row) => sessionRowToMetadata(row.session_key, row));\n}\n\nexport function getSessionPersistedLevels(sessionKey: string): {\n thinkingLevel: string | null;\n verboseLevel: string | null;\n} | null {\n const db = getSqliteDatabase();\n const row = db\n .prepare(`SELECT thinking_level, verbose_level FROM sessions WHERE session_key = ?`)\n .get(sessionKey) as { thinking_level: string | null; verbose_level: string | null } | undefined;\n if (!row) {\n return null;\n }\n return { thinkingLevel: row.thinking_level, verboseLevel: row.verbose_level };\n}\n\nexport function findSessionKeyBySessionId(sessionId: string): string | null {\n const db = getSqliteDatabase();\n const row = db\n .prepare(`SELECT session_key FROM sessions WHERE session_id = ?`)\n .get(sessionId) as { session_key?: string } | undefined;\n return row?.session_key ?? null;\n}\n\nexport function getGlobalSessionStats(): ReturnType<typeof buildGlobalSessionStats> {\n const all = listSessionMetadata({ limit: 100_000, offset: 0 });\n return buildGlobalSessionStats(all.items);\n}\n\nexport function resolveSessionAgentId(sessionKey: string): string {\n return getSessionMetadata(sessionKey)?.routing?.agentId ?? 'main';\n}\n"],"mappings":"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"}
@@ -29,6 +29,10 @@ function insertEntry(db, params) {
29
29
  created_at: createdAt
30
30
  };
31
31
  }
32
+ function isUserMessageRow(row) {
33
+ const classified = classifyStoredRow(row);
34
+ return classified.entryKind === "message" && classified.role === "user";
35
+ }
32
36
  function appendTranscriptEntry(sessionKey, row, opts) {
33
37
  return runSqliteWriteTransaction((db) => {
34
38
  const sessionId = opts?.sessionId ?? readCurrentSessionId(db, sessionKey);
@@ -41,9 +45,11 @@ function appendTranscriptEntry(sessionKey, row, opts) {
41
45
  if (classifyStoredRow(row).entryKind === "message") {
42
46
  const tokenDelta = opts?.tokenDelta ?? 0;
43
47
  const now = Date.now();
48
+ const hiddenUpdate = isUserMessageRow(row) ? `hidden_from_session_list = 0,` : "";
44
49
  db.prepare(`UPDATE sessions SET
45
50
  message_count = message_count + 1,
46
51
  estimated_tokens = estimated_tokens + ?,
52
+ ${hiddenUpdate}
47
53
  updated_at = ?,
48
54
  last_accessed_at = ?,
49
55
  last_interaction_at = ?
@@ -84,9 +90,11 @@ function replaceTranscriptRows(sessionKey, rows, opts) {
84
90
  });
85
91
  const llm = buildSessionContextForLlm(toWrite);
86
92
  const now = Date.now();
93
+ const hiddenUpdate = llm.some((message) => message.role === "user") ? `hidden_from_session_list = 0,` : "";
87
94
  db.prepare(`UPDATE sessions SET
88
95
  message_count = ?,
89
96
  estimated_tokens = ?,
97
+ ${hiddenUpdate}
90
98
  updated_at = ?,
91
99
  last_accessed_at = ?,
92
100
  last_interaction_at = ?
@@ -1 +1 @@
1
- {"version":3,"file":"transcript-repository.js","names":[],"sources":["../../../../src/storage/sqlite/transcript-repository.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { CompactionCheckpointDetail, CompactionCheckpointSummary } from '../../session/types.js';\nimport type { TranscriptCompactionRecord } from '../../session/transcript-format.js';\nimport {\n buildSessionContextForLlm,\n type TranscriptStoredRow,\n} from '../../session/session-context-for-llm.js';\nimport {\n classifyStoredRow,\n estimateTokensFromMessages,\n extractFtsContent,\n transcriptEntryRowToStoredRow,\n type TranscriptEntryRow,\n} from './row-mappers.js';\nimport { getCurrentSessionId, readCurrentSessionId } from './session-repository.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\nconst MAX_CHECKPOINTS_PER_TRANSCRIPT = 15;\n\nfunction nextSeq(db: DatabaseSync, sessionId: string): number {\n const row = db\n .prepare(`SELECT COALESCE(MAX(seq), 0) AS max_seq FROM transcript_entries WHERE session_id = ?`)\n .get(sessionId) as { max_seq: number };\n return (row.max_seq ?? 0) + 1;\n}\n\nfunction insertEntry(\n db: DatabaseSync,\n params: {\n sessionId: string;\n sessionKey: string;\n row: TranscriptStoredRow;\n entryId?: string;\n createdAt?: number;\n },\n): TranscriptEntryRow {\n const { entryKind, role } = classifyStoredRow(params.row);\n const entryId = params.entryId ?? randomUUID();\n const seq = nextSeq(db, params.sessionId);\n const createdAt = params.createdAt ?? Date.now();\n const payloadJson = JSON.stringify(params.row);\n\n db.prepare(\n `INSERT INTO transcript_entries (entry_id, session_id, seq, entry_kind, role, payload_json, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n ).run(entryId, params.sessionId, seq, entryKind, role, payloadJson, createdAt);\n\n const content = extractFtsContent(params.row);\n if (content.trim()) {\n db.prepare(\n `INSERT INTO transcript_fts (content, session_key, session_id, entry_id)\n VALUES (?, ?, ?, ?)`,\n ).run(content, params.sessionKey, params.sessionId, entryId);\n }\n\n return {\n entry_id: entryId,\n session_id: params.sessionId,\n seq,\n entry_kind: entryKind,\n role,\n payload_json: payloadJson,\n created_at: createdAt,\n };\n}\n\nexport function appendTranscriptEntry(\n sessionKey: string,\n row: TranscriptStoredRow,\n opts?: { sessionId?: string; tokenDelta?: number },\n): TranscriptEntryRow {\n return runSqliteWriteTransaction((db) => {\n const sessionId = opts?.sessionId ?? readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n const inserted = insertEntry(db, { sessionId, sessionKey, row });\n if (classifyStoredRow(row).entryKind === 'message') {\n const tokenDelta = opts?.tokenDelta ?? 0;\n const now = Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = message_count + 1,\n estimated_tokens = estimated_tokens + ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(tokenDelta, now, now, now, sessionKey);\n }\n return inserted;\n });\n}\n\nexport function loadTranscriptRowsForSession(sessionKey: string): TranscriptStoredRow[] {\n const sessionId = getCurrentSessionId(sessionKey);\n if (!sessionId) {\n return [];\n }\n return loadTranscriptRows(sessionId);\n}\n\nexport function loadTranscriptRows(sessionId: string): TranscriptStoredRow[] {\n const db = getSqliteDatabase();\n const rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries\n WHERE session_id = ?\n ORDER BY seq ASC`,\n )\n .all(sessionId) as TranscriptEntryRow[];\n return rows.map(transcriptEntryRowToStoredRow);\n}\n\nexport function loadLlmMessagesForSession(sessionKey: string): AgentMessage[] {\n const rows = loadTranscriptRowsForSession(sessionKey);\n return buildSessionContextForLlm(rows);\n}\n\nexport function replaceTranscriptRows(\n sessionKey: string,\n rows: TranscriptStoredRow[],\n opts?: { appendCompaction?: TranscriptCompactionRecord },\n): void {\n runSqliteWriteTransaction((db) => {\n const sessionId = readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n\n db.prepare(`DELETE FROM transcript_fts WHERE session_id = ?`).run(sessionId);\n db.prepare(`DELETE FROM transcript_entries WHERE session_id = ?`).run(sessionId);\n\n const toWrite = [...rows];\n if (opts?.appendCompaction) {\n toWrite.push({\n type: 'compaction',\n ...opts.appendCompaction,\n } as unknown as TranscriptStoredRow);\n }\n\n for (const row of toWrite) {\n insertEntry(db, { sessionId, sessionKey, row });\n }\n\n const llm = buildSessionContextForLlm(toWrite);\n const now = Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = ?,\n estimated_tokens = ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(llm.length, estimateTokensFromMessages(llm), now, now, now, sessionKey);\n });\n}\n\nexport function loadCheckpointRows(sessionKey: string, checkpointId: string): TranscriptStoredRow[] {\n const db = getSqliteDatabase();\n const checkpoint = db\n .prepare(\n `SELECT checkpoint_id FROM compaction_checkpoints\n WHERE session_key = ? AND checkpoint_id = ?`,\n )\n .get(sessionKey, checkpointId) as { checkpoint_id: string } | undefined;\n if (!checkpoint) {\n return [];\n }\n const entries = db\n .prepare(\n `SELECT payload_json FROM checkpoint_entries\n WHERE checkpoint_id = ?\n ORDER BY seq ASC`,\n )\n .all(checkpointId) as Array<{ payload_json: string }>;\n return entries.map((entry) => JSON.parse(entry.payload_json) as TranscriptStoredRow);\n}\n\nexport function paginateTranscriptMessages(\n sessionKey: string,\n options: {\n offset?: number;\n limit?: number;\n beforeSeq?: number;\n /** 0-based exclusive end index (legacy cursor from gateway UI). */\n beforeEndIndex?: number;\n includeContext?: boolean;\n } = {},\n): {\n rows: TranscriptStoredRow[];\n messages: AgentMessage[];\n total: number;\n startSeq: number;\n endSeq: number;\n} {\n const sessionId = getCurrentSessionId(sessionKey);\n if (!sessionId) {\n return { rows: [], messages: [], total: 0, startSeq: 0, endSeq: 0 };\n }\n\n const db = getSqliteDatabase();\n const kinds = options.includeContext ? ['message', 'context'] : ['message'];\n const kindPlaceholders = kinds.map(() => '?').join(', ');\n\n const countRow = db\n .prepare(\n `SELECT COUNT(*) AS total FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders})`,\n )\n .get(sessionId, ...kinds) as { total: number };\n const total = countRow.total;\n\n const limit = Math.min(200, Math.max(1, Math.trunc(options.limit ?? 50)));\n const offset = Math.max(0, Math.trunc(options.offset ?? 0));\n\n let rows: TranscriptEntryRow[];\n if (options.beforeEndIndex !== undefined && Number.isFinite(options.beforeEndIndex)) {\n const endExclusive = Math.min(total, Math.max(0, Math.trunc(options.beforeEndIndex)));\n const startInclusive = Math.max(0, endExclusive - limit);\n rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM (\n SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at,\n ROW_NUMBER() OVER (ORDER BY seq ASC) - 1 AS idx\n FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders})\n )\n WHERE idx >= ? AND idx < ?\n ORDER BY seq ASC`,\n )\n .all(sessionId, ...kinds, startInclusive, endExclusive) as TranscriptEntryRow[];\n } else if (options.beforeSeq !== undefined && Number.isFinite(options.beforeSeq)) {\n rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders}) AND seq < ?\n ORDER BY seq DESC\n LIMIT ?`,\n )\n .all(sessionId, ...kinds, options.beforeSeq, limit) as TranscriptEntryRow[];\n rows.reverse();\n } else {\n rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders})\n ORDER BY seq DESC\n LIMIT ? OFFSET ?`,\n )\n .all(sessionId, ...kinds, limit, offset) as TranscriptEntryRow[];\n rows.reverse();\n }\n\n const storedRows = rows.map(transcriptEntryRowToStoredRow);\n const messages = buildSessionContextForLlm(storedRows);\n const startSeq = rows[0]?.seq ?? 0;\n const endSeq = rows[rows.length - 1]?.seq ?? 0;\n return { rows: storedRows, messages, total, startSeq, endSeq };\n}\n\nexport function captureCompactionCheckpoint(sessionKey: string): string | null {\n return runSqliteWriteTransaction((db) => {\n const sessionId = readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n return null;\n }\n\n const entries = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries WHERE session_id = ? ORDER BY seq ASC`,\n )\n .all(sessionId) as TranscriptEntryRow[];\n if (entries.length === 0) {\n return null;\n }\n\n const checkpointId = randomUUID();\n const now = Date.now();\n const messageCount = entries.filter((e) => e.entry_kind === 'message').length;\n const sizeBytes = entries.reduce((sum, e) => sum + e.payload_json.length, 0);\n\n db.prepare(\n `INSERT INTO compaction_checkpoints\n (checkpoint_id, session_id, session_key, created_at, message_count, size_bytes)\n VALUES (?, ?, ?, ?, ?, ?)`,\n ).run(checkpointId, sessionId, sessionKey, now, messageCount, sizeBytes);\n\n const insertCheckpointEntry = db.prepare(\n `INSERT INTO checkpoint_entries (checkpoint_id, seq, entry_kind, role, payload_json)\n VALUES (?, ?, ?, ?, ?)`,\n );\n for (const entry of entries) {\n insertCheckpointEntry.run(\n checkpointId,\n entry.seq,\n entry.entry_kind,\n entry.role,\n entry.payload_json,\n );\n }\n\n pruneCompactionCheckpoints(db, sessionId);\n return checkpointId;\n });\n}\n\nfunction pruneCompactionCheckpoints(db: DatabaseSync, sessionId: string): void {\n const rows = db\n .prepare(\n `SELECT checkpoint_id FROM compaction_checkpoints\n WHERE session_id = ?\n ORDER BY created_at ASC`,\n )\n .all(sessionId) as Array<{ checkpoint_id: string }>;\n if (rows.length <= MAX_CHECKPOINTS_PER_TRANSCRIPT) {\n return;\n }\n const toDelete = rows.slice(0, rows.length - MAX_CHECKPOINTS_PER_TRANSCRIPT);\n const del = db.prepare(`DELETE FROM compaction_checkpoints WHERE checkpoint_id = ?`);\n for (const row of toDelete) {\n del.run(row.checkpoint_id);\n }\n}\n\nexport function listCompactionCheckpoints(sessionKey: string): CompactionCheckpointSummary[] {\n const db = getSqliteDatabase();\n const rows = db\n .prepare(\n `SELECT checkpoint_id, created_at, message_count, size_bytes\n FROM compaction_checkpoints\n WHERE session_key = ?\n ORDER BY created_at DESC`,\n )\n .all(sessionKey) as Array<{\n checkpoint_id: string;\n created_at: number;\n message_count: number;\n size_bytes: number;\n }>;\n\n return rows.map((row) => ({\n id: row.checkpoint_id,\n sizeBytes: row.size_bytes,\n modifiedAt: new Date(row.created_at).toISOString(),\n }));\n}\n\nexport function getCompactionCheckpointDetail(\n sessionKey: string,\n checkpointId: string,\n): CompactionCheckpointDetail | null {\n const db = getSqliteDatabase();\n const row = db\n .prepare(\n `SELECT checkpoint_id, created_at, message_count, size_bytes\n FROM compaction_checkpoints\n WHERE session_key = ? AND checkpoint_id = ?`,\n )\n .get(sessionKey, checkpointId) as\n | {\n checkpoint_id: string;\n created_at: number;\n message_count: number;\n size_bytes: number;\n }\n | undefined;\n if (!row) {\n return null;\n }\n return {\n id: row.checkpoint_id,\n sizeBytes: row.size_bytes,\n modifiedAt: new Date(row.created_at).toISOString(),\n messageCount: row.message_count,\n };\n}\n\nexport function restoreCompactionCheckpoint(sessionKey: string, checkpointId: string): void {\n runSqliteWriteTransaction((db) => {\n const sessionId = readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n\n const checkpoint = db\n .prepare(\n `SELECT checkpoint_id FROM compaction_checkpoints\n WHERE session_key = ? AND checkpoint_id = ?`,\n )\n .get(sessionKey, checkpointId) as { checkpoint_id: string } | undefined;\n if (!checkpoint) {\n throw new Error(`Checkpoint not found: ${checkpointId}`);\n }\n\n const entries = db\n .prepare(\n `SELECT seq, entry_kind, role, payload_json\n FROM checkpoint_entries\n WHERE checkpoint_id = ?\n ORDER BY seq ASC`,\n )\n .all(checkpointId) as Array<{\n seq: number;\n entry_kind: string;\n role: string | null;\n payload_json: string;\n }>;\n\n db.prepare(`DELETE FROM transcript_fts WHERE session_id = ?`).run(sessionId);\n db.prepare(`DELETE FROM transcript_entries WHERE session_id = ?`).run(sessionId);\n\n for (const entry of entries) {\n const payload = JSON.parse(entry.payload_json) as TranscriptStoredRow;\n insertEntry(db, {\n sessionId,\n sessionKey,\n row: payload,\n createdAt: Date.now(),\n });\n }\n\n const llm = buildSessionContextForLlm(entries.map((e) => JSON.parse(e.payload_json) as TranscriptStoredRow));\n const now = Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = ?,\n estimated_tokens = ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(llm.length, estimateTokensFromMessages(llm), now, now, now, sessionKey);\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"transcript-repository.js","names":[],"sources":["../../../../src/storage/sqlite/transcript-repository.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { CompactionCheckpointDetail, CompactionCheckpointSummary } from '../../session/types.js';\nimport type { TranscriptCompactionRecord } from '../../session/transcript-format.js';\nimport {\n buildSessionContextForLlm,\n type TranscriptStoredRow,\n} from '../../session/session-context-for-llm.js';\nimport {\n classifyStoredRow,\n estimateTokensFromMessages,\n extractFtsContent,\n transcriptEntryRowToStoredRow,\n type TranscriptEntryRow,\n} from './row-mappers.js';\nimport { getCurrentSessionId, readCurrentSessionId } from './session-repository.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\nconst MAX_CHECKPOINTS_PER_TRANSCRIPT = 15;\n\nfunction nextSeq(db: DatabaseSync, sessionId: string): number {\n const row = db\n .prepare(`SELECT COALESCE(MAX(seq), 0) AS max_seq FROM transcript_entries WHERE session_id = ?`)\n .get(sessionId) as { max_seq: number };\n return (row.max_seq ?? 0) + 1;\n}\n\nfunction insertEntry(\n db: DatabaseSync,\n params: {\n sessionId: string;\n sessionKey: string;\n row: TranscriptStoredRow;\n entryId?: string;\n createdAt?: number;\n },\n): TranscriptEntryRow {\n const { entryKind, role } = classifyStoredRow(params.row);\n const entryId = params.entryId ?? randomUUID();\n const seq = nextSeq(db, params.sessionId);\n const createdAt = params.createdAt ?? Date.now();\n const payloadJson = JSON.stringify(params.row);\n\n db.prepare(\n `INSERT INTO transcript_entries (entry_id, session_id, seq, entry_kind, role, payload_json, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n ).run(entryId, params.sessionId, seq, entryKind, role, payloadJson, createdAt);\n\n const content = extractFtsContent(params.row);\n if (content.trim()) {\n db.prepare(\n `INSERT INTO transcript_fts (content, session_key, session_id, entry_id)\n VALUES (?, ?, ?, ?)`,\n ).run(content, params.sessionKey, params.sessionId, entryId);\n }\n\n return {\n entry_id: entryId,\n session_id: params.sessionId,\n seq,\n entry_kind: entryKind,\n role,\n payload_json: payloadJson,\n created_at: createdAt,\n };\n}\n\nfunction isUserMessageRow(row: TranscriptStoredRow): boolean {\n const classified = classifyStoredRow(row);\n return classified.entryKind === 'message' && classified.role === 'user';\n}\n\nexport function appendTranscriptEntry(\n sessionKey: string,\n row: TranscriptStoredRow,\n opts?: { sessionId?: string; tokenDelta?: number },\n): TranscriptEntryRow {\n return runSqliteWriteTransaction((db) => {\n const sessionId = opts?.sessionId ?? readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n const inserted = insertEntry(db, { sessionId, sessionKey, row });\n if (classifyStoredRow(row).entryKind === 'message') {\n const tokenDelta = opts?.tokenDelta ?? 0;\n const now = Date.now();\n const hiddenUpdate = isUserMessageRow(row) ? `hidden_from_session_list = 0,` : '';\n db.prepare(\n `UPDATE sessions SET\n message_count = message_count + 1,\n estimated_tokens = estimated_tokens + ?,\n ${hiddenUpdate}\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(tokenDelta, now, now, now, sessionKey);\n }\n return inserted;\n });\n}\n\nexport function loadTranscriptRowsForSession(sessionKey: string): TranscriptStoredRow[] {\n const sessionId = getCurrentSessionId(sessionKey);\n if (!sessionId) {\n return [];\n }\n return loadTranscriptRows(sessionId);\n}\n\nexport function loadTranscriptRows(sessionId: string): TranscriptStoredRow[] {\n const db = getSqliteDatabase();\n const rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries\n WHERE session_id = ?\n ORDER BY seq ASC`,\n )\n .all(sessionId) as TranscriptEntryRow[];\n return rows.map(transcriptEntryRowToStoredRow);\n}\n\nexport function loadLlmMessagesForSession(sessionKey: string): AgentMessage[] {\n const rows = loadTranscriptRowsForSession(sessionKey);\n return buildSessionContextForLlm(rows);\n}\n\nexport function replaceTranscriptRows(\n sessionKey: string,\n rows: TranscriptStoredRow[],\n opts?: { appendCompaction?: TranscriptCompactionRecord },\n): void {\n runSqliteWriteTransaction((db) => {\n const sessionId = readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n\n db.prepare(`DELETE FROM transcript_fts WHERE session_id = ?`).run(sessionId);\n db.prepare(`DELETE FROM transcript_entries WHERE session_id = ?`).run(sessionId);\n\n const toWrite = [...rows];\n if (opts?.appendCompaction) {\n toWrite.push({\n type: 'compaction',\n ...opts.appendCompaction,\n } as unknown as TranscriptStoredRow);\n }\n\n for (const row of toWrite) {\n insertEntry(db, { sessionId, sessionKey, row });\n }\n\n const llm = buildSessionContextForLlm(toWrite);\n const now = Date.now();\n const hasUserMessage = llm.some((message) => message.role === 'user');\n const hiddenUpdate = hasUserMessage ? `hidden_from_session_list = 0,` : '';\n db.prepare(\n `UPDATE sessions SET\n message_count = ?,\n estimated_tokens = ?,\n ${hiddenUpdate}\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(llm.length, estimateTokensFromMessages(llm), now, now, now, sessionKey);\n });\n}\n\nexport function loadCheckpointRows(sessionKey: string, checkpointId: string): TranscriptStoredRow[] {\n const db = getSqliteDatabase();\n const checkpoint = db\n .prepare(\n `SELECT checkpoint_id FROM compaction_checkpoints\n WHERE session_key = ? AND checkpoint_id = ?`,\n )\n .get(sessionKey, checkpointId) as { checkpoint_id: string } | undefined;\n if (!checkpoint) {\n return [];\n }\n const entries = db\n .prepare(\n `SELECT payload_json FROM checkpoint_entries\n WHERE checkpoint_id = ?\n ORDER BY seq ASC`,\n )\n .all(checkpointId) as Array<{ payload_json: string }>;\n return entries.map((entry) => JSON.parse(entry.payload_json) as TranscriptStoredRow);\n}\n\nexport function paginateTranscriptMessages(\n sessionKey: string,\n options: {\n offset?: number;\n limit?: number;\n beforeSeq?: number;\n /** 0-based exclusive end index (legacy cursor from gateway UI). */\n beforeEndIndex?: number;\n includeContext?: boolean;\n } = {},\n): {\n rows: TranscriptStoredRow[];\n messages: AgentMessage[];\n total: number;\n startSeq: number;\n endSeq: number;\n} {\n const sessionId = getCurrentSessionId(sessionKey);\n if (!sessionId) {\n return { rows: [], messages: [], total: 0, startSeq: 0, endSeq: 0 };\n }\n\n const db = getSqliteDatabase();\n const kinds = options.includeContext ? ['message', 'context'] : ['message'];\n const kindPlaceholders = kinds.map(() => '?').join(', ');\n\n const countRow = db\n .prepare(\n `SELECT COUNT(*) AS total FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders})`,\n )\n .get(sessionId, ...kinds) as { total: number };\n const total = countRow.total;\n\n const limit = Math.min(200, Math.max(1, Math.trunc(options.limit ?? 50)));\n const offset = Math.max(0, Math.trunc(options.offset ?? 0));\n\n let rows: TranscriptEntryRow[];\n if (options.beforeEndIndex !== undefined && Number.isFinite(options.beforeEndIndex)) {\n const endExclusive = Math.min(total, Math.max(0, Math.trunc(options.beforeEndIndex)));\n const startInclusive = Math.max(0, endExclusive - limit);\n rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM (\n SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at,\n ROW_NUMBER() OVER (ORDER BY seq ASC) - 1 AS idx\n FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders})\n )\n WHERE idx >= ? AND idx < ?\n ORDER BY seq ASC`,\n )\n .all(sessionId, ...kinds, startInclusive, endExclusive) as TranscriptEntryRow[];\n } else if (options.beforeSeq !== undefined && Number.isFinite(options.beforeSeq)) {\n rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders}) AND seq < ?\n ORDER BY seq DESC\n LIMIT ?`,\n )\n .all(sessionId, ...kinds, options.beforeSeq, limit) as TranscriptEntryRow[];\n rows.reverse();\n } else {\n rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders})\n ORDER BY seq DESC\n LIMIT ? OFFSET ?`,\n )\n .all(sessionId, ...kinds, limit, offset) as TranscriptEntryRow[];\n rows.reverse();\n }\n\n const storedRows = rows.map(transcriptEntryRowToStoredRow);\n const messages = buildSessionContextForLlm(storedRows);\n const startSeq = rows[0]?.seq ?? 0;\n const endSeq = rows[rows.length - 1]?.seq ?? 0;\n return { rows: storedRows, messages, total, startSeq, endSeq };\n}\n\nexport function captureCompactionCheckpoint(sessionKey: string): string | null {\n return runSqliteWriteTransaction((db) => {\n const sessionId = readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n return null;\n }\n\n const entries = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries WHERE session_id = ? ORDER BY seq ASC`,\n )\n .all(sessionId) as TranscriptEntryRow[];\n if (entries.length === 0) {\n return null;\n }\n\n const checkpointId = randomUUID();\n const now = Date.now();\n const messageCount = entries.filter((e) => e.entry_kind === 'message').length;\n const sizeBytes = entries.reduce((sum, e) => sum + e.payload_json.length, 0);\n\n db.prepare(\n `INSERT INTO compaction_checkpoints\n (checkpoint_id, session_id, session_key, created_at, message_count, size_bytes)\n VALUES (?, ?, ?, ?, ?, ?)`,\n ).run(checkpointId, sessionId, sessionKey, now, messageCount, sizeBytes);\n\n const insertCheckpointEntry = db.prepare(\n `INSERT INTO checkpoint_entries (checkpoint_id, seq, entry_kind, role, payload_json)\n VALUES (?, ?, ?, ?, ?)`,\n );\n for (const entry of entries) {\n insertCheckpointEntry.run(\n checkpointId,\n entry.seq,\n entry.entry_kind,\n entry.role,\n entry.payload_json,\n );\n }\n\n pruneCompactionCheckpoints(db, sessionId);\n return checkpointId;\n });\n}\n\nfunction pruneCompactionCheckpoints(db: DatabaseSync, sessionId: string): void {\n const rows = db\n .prepare(\n `SELECT checkpoint_id FROM compaction_checkpoints\n WHERE session_id = ?\n ORDER BY created_at ASC`,\n )\n .all(sessionId) as Array<{ checkpoint_id: string }>;\n if (rows.length <= MAX_CHECKPOINTS_PER_TRANSCRIPT) {\n return;\n }\n const toDelete = rows.slice(0, rows.length - MAX_CHECKPOINTS_PER_TRANSCRIPT);\n const del = db.prepare(`DELETE FROM compaction_checkpoints WHERE checkpoint_id = ?`);\n for (const row of toDelete) {\n del.run(row.checkpoint_id);\n }\n}\n\nexport function listCompactionCheckpoints(sessionKey: string): CompactionCheckpointSummary[] {\n const db = getSqliteDatabase();\n const rows = db\n .prepare(\n `SELECT checkpoint_id, created_at, message_count, size_bytes\n FROM compaction_checkpoints\n WHERE session_key = ?\n ORDER BY created_at DESC`,\n )\n .all(sessionKey) as Array<{\n checkpoint_id: string;\n created_at: number;\n message_count: number;\n size_bytes: number;\n }>;\n\n return rows.map((row) => ({\n id: row.checkpoint_id,\n sizeBytes: row.size_bytes,\n modifiedAt: new Date(row.created_at).toISOString(),\n }));\n}\n\nexport function getCompactionCheckpointDetail(\n sessionKey: string,\n checkpointId: string,\n): CompactionCheckpointDetail | null {\n const db = getSqliteDatabase();\n const row = db\n .prepare(\n `SELECT checkpoint_id, created_at, message_count, size_bytes\n FROM compaction_checkpoints\n WHERE session_key = ? AND checkpoint_id = ?`,\n )\n .get(sessionKey, checkpointId) as\n | {\n checkpoint_id: string;\n created_at: number;\n message_count: number;\n size_bytes: number;\n }\n | undefined;\n if (!row) {\n return null;\n }\n return {\n id: row.checkpoint_id,\n sizeBytes: row.size_bytes,\n modifiedAt: new Date(row.created_at).toISOString(),\n messageCount: row.message_count,\n };\n}\n\nexport function restoreCompactionCheckpoint(sessionKey: string, checkpointId: string): void {\n runSqliteWriteTransaction((db) => {\n const sessionId = readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n\n const checkpoint = db\n .prepare(\n `SELECT checkpoint_id FROM compaction_checkpoints\n WHERE session_key = ? AND checkpoint_id = ?`,\n )\n .get(sessionKey, checkpointId) as { checkpoint_id: string } | undefined;\n if (!checkpoint) {\n throw new Error(`Checkpoint not found: ${checkpointId}`);\n }\n\n const entries = db\n .prepare(\n `SELECT seq, entry_kind, role, payload_json\n FROM checkpoint_entries\n WHERE checkpoint_id = ?\n ORDER BY seq ASC`,\n )\n .all(checkpointId) as Array<{\n seq: number;\n entry_kind: string;\n role: string | null;\n payload_json: string;\n }>;\n\n db.prepare(`DELETE FROM transcript_fts WHERE session_id = ?`).run(sessionId);\n db.prepare(`DELETE FROM transcript_entries WHERE session_id = ?`).run(sessionId);\n\n for (const entry of entries) {\n const payload = JSON.parse(entry.payload_json) as TranscriptStoredRow;\n insertEntry(db, {\n sessionId,\n sessionKey,\n row: payload,\n createdAt: Date.now(),\n });\n }\n\n const llm = buildSessionContextForLlm(entries.map((e) => JSON.parse(e.payload_json) as TranscriptStoredRow));\n const now = Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = ?,\n estimated_tokens = ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(llm.length, estimateTokensFromMessages(llm), now, now, now, sessionKey);\n });\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import type { ExtensionRegistryImpl } from '../../extensions/loader.js';
2
2
  import { type Config } from '../../config/schema.js';
3
3
  import type { ExportFormat } from '../../session/types.js';
4
- import type { ChatSendOptions, HistoryMessage, TuiBackend, TuiCompactionResult, TuiEvent, TuiModelChoice, TuiShareRequest, TuiShareResult, TuiSessionStats, TuiSessionItem, TuiTranscriptTreeEntry, TuiAgentInfo, TuiWorkspaceFileSearchEntry, TuiWorkflowRunStartRequest, TuiWorkflowRunStartResult } from '../tui-backend.js';
4
+ import type { ChatSendOptions, HistoryMessage, TuiBackend, TuiCompactionResult, TuiEvent, TuiModelChoice, TuiShareRequest, TuiShareResult, TuiSessionStats, TuiSessionItem, TuiTranscriptTreeEntry, TuiAgentInfo, TuiWorkspaceFileSearchEntry, TuiWorkflowRunStartRequest, TuiWorkflowRunStartResult, TuiStartupProjectResult } from '../tui-backend.js';
5
5
  import type { SessionInfo } from '../tui-types.js';
6
6
  interface EmbeddedBackendOptions {
7
7
  config?: Config;
@@ -41,6 +41,12 @@ export declare class EmbeddedBackend implements TuiBackend {
41
41
  private activeConfig;
42
42
  getStartupResources(sessionKey: string): Promise<import("../tui-backend.js").TuiStartupResources>;
43
43
  startWorkflowRun(opts: TuiWorkflowRunStartRequest): Promise<TuiWorkflowRunStartResult>;
44
+ resolveStartupProject(opts: {
45
+ workspacePath: string;
46
+ sessionKey: string;
47
+ agentId: string;
48
+ autoCreate?: boolean;
49
+ }): Promise<TuiStartupProjectResult>;
44
50
  searchWorkspaceFiles(sessionKey: string, query: string, options?: {
45
51
  limit?: number;
46
52
  }): Promise<TuiWorkspaceFileSearchEntry[]>;
@@ -4,9 +4,10 @@ import { init_agent_session_key, resolveAgentIdFromSessionKey } from "../../rout
4
4
  import { createLogger } from "../../utils/logger/index.js";
5
5
  import { init_logger } from "../../utils/logger.js";
6
6
  import { loadConfig, saveConfig } from "../../config/loader.js";
7
- import { createCreateShareTool, isShareToolAvailable } from "../../agent/tools/create-share-tool.js";
8
7
  import { openXopcDatabase } from "../../storage/sqlite/connection.js";
8
+ import { getSessionMetadata } from "../../storage/sqlite/session-repository.js";
9
9
  import "../../storage/sqlite/index.js";
10
+ import { createCreateShareTool, isShareToolAvailable } from "../../agent/tools/create-share-tool.js";
10
11
  import { evictEmbeddedSessionRunner } from "../../agent/embedded/session-runner.js";
11
12
  import { prependEnvelopeTimestamp } from "../../channels/envelope-timestamp.js";
12
13
  import { SessionIndex } from "../../session/manager.js";
@@ -21,6 +22,7 @@ import "../../config/index.js";
21
22
  import { buildWorkflowChildTools } from "../../agent/workflow/workflow-child-tools.js";
22
23
  import { WorkflowRunService } from "../../workflows/service/workflow-run-service.js";
23
24
  import { WorkflowSessionBridge } from "../../workflows/service/workflow-session-bridge.js";
25
+ import { ProjectService } from "../../projects/project-service.js";
24
26
  import { ChatStreamMapper } from "../../gateway/chat-stream/mapper.js";
25
27
  import { fuzzySearchWorkspaceFiles } from "../../gateway/workspace-file-search.js";
26
28
  import { sessionMetadataToTuiItem } from "../tui-session-format.js";
@@ -191,6 +193,36 @@ var EmbeddedBackend = class {
191
193
  definitionId: opts.definitionId
192
194
  };
193
195
  }
196
+ async resolveStartupProject(opts) {
197
+ await this.sessionIndexReady;
198
+ if (!this.sessionIndex) return { project: null };
199
+ const projects = new ProjectService();
200
+ const match = projects.resolveOrCreateForWorkspacePath({
201
+ workspacePath: opts.workspacePath,
202
+ agentId: opts.agentId,
203
+ autoCreate: opts.autoCreate !== false
204
+ });
205
+ if (!match) return { project: null };
206
+ if (!getSessionMetadata(opts.sessionKey)) await this.sessionIndex.getStore().resolveTranscriptPath(opts.sessionKey, { metadata: {
207
+ sourceChannel: "tui",
208
+ sourceChatId: `default:direct:${opts.sessionKey}`,
209
+ sessionType: "chat",
210
+ projectId: match.project.id,
211
+ routing: {
212
+ agentId: opts.agentId,
213
+ source: "tui",
214
+ accountId: "default",
215
+ peerKind: "direct",
216
+ peerId: opts.sessionKey
217
+ }
218
+ } });
219
+ projects.attachSession(opts.sessionKey, match.project.id);
220
+ return {
221
+ project: match.project,
222
+ created: match.created,
223
+ reason: match.reason
224
+ };
225
+ }
194
226
  async searchWorkspaceFiles(sessionKey, query, options) {
195
227
  if (!this.agent) return [];
196
228
  try {
@@ -419,7 +451,14 @@ var EmbeddedBackend = class {
419
451
  await this.agent.resetSession(sessionKey);
420
452
  }
421
453
  async patchSession(sessionKey, patch) {
422
- const result = await (await this.ensureAgent()).sessionConfig.patch(sessionKey, {
454
+ const agent = await this.ensureAgent();
455
+ const projectId = typeof patch.projectId === "string" ? patch.projectId.trim() : "";
456
+ if (projectId) {
457
+ await this.sessionIndexReady;
458
+ await this.sessionIndex?.getStore().resolveTranscriptPath(sessionKey);
459
+ new ProjectService().attachSession(sessionKey, projectId);
460
+ }
461
+ const result = await agent.sessionConfig.patch(sessionKey, {
423
462
  model: typeof patch.model === "string" ? patch.model : void 0,
424
463
  thinkingLevel: typeof patch.thinkingLevel === "string" ? patch.thinkingLevel : void 0,
425
464
  reasoningLevel: typeof patch.reasoningLevel === "string" ? patch.reasoningLevel : void 0,
@@ -1 +1 @@
1
- {"version":3,"file":"embedded-backend.js","names":[],"sources":["../../../../src/tui/backends/embedded-backend.ts"],"sourcesContent":["import type { ExtensionRegistryImpl } from '../../extensions/loader.js';\nimport { isAbsolute, relative, resolve } from 'node:path';\nimport type { AgentService } from '../../agent/service.js';\nimport { listAgentEntries, normalizeAgentId } from '../../agent/agent-scope.js';\nimport { resolveAgentIdFromSessionKey } from '../../routing/agent-session-key.js';\nimport { parseModelRef } from '../../agent/models/selection.js';\nimport { createCreateShareTool, isShareToolAvailable } from '../../agent/tools/create-share-tool.js';\nimport { transcriptRowsToClientHistory } from '../../session/client-history.js';\nimport { prependEnvelopeTimestamp } from '../../channels/envelope-timestamp.js';\nimport { loadConfig, getWorkspacePath, saveConfig } from '../../config/index.js';\nimport { getAgentDefaultModelRef, type Config } from '../../config/schema.js';\nimport { setTuiDefaultAgentConfig } from '../../commands/agents.config.js';\nimport { MessageBus, MessageBusShutdownError } from '../../infra/bus/index.js';\nimport { evictEmbeddedSessionRunner } from '../../agent/embedded/session-runner.js';\nimport type { ExportFormat } from '../../session/types.js';\nimport { SessionIndex } from '../../session/index.js';\nimport { openXopcDatabase } from '../../storage/sqlite/index.js';\nimport { buildWorkflowChildTools } from '../../agent/workflow/workflow-child-tools.js';\nimport type { GatewayWorkflowHost } from '../../gateway/gateway-workflow-host.types.js';\nimport { WorkflowRunService } from '../../workflows/service/workflow-run-service.js';\nimport { WorkflowSessionBridge } from '../../workflows/service/workflow-session-bridge.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type {\n ChatSendOptions,\n HistoryMessage,\n TuiBackend,\n TuiCompactionResult,\n TuiEvent,\n TuiModelChoice,\n TuiShareRequest,\n TuiShareResult,\n TuiSessionStats,\n TuiSessionItem,\n TuiTranscriptTreeEntry,\n TuiAgentInfo,\n TuiWorkspaceFileSearchEntry,\n TuiWorkflowRunStartRequest,\n TuiWorkflowRunStartResult,\n} from '../tui-backend.js';\nimport type { SessionInfo } from '../tui-types.js';\nimport { sessionMetadataToTuiItem } from '../tui-session-format.js';\nimport { computeTuiSessionStats } from '../tui-session-stats.js';\nimport { buildTuiTranscriptTree, transcriptTreeEntryIdToRowNumber } from '../tui-transcript-tree.js';\nimport { ChatStreamMapper } from '../../gateway/chat-stream/mapper.js';\nimport { collectTuiStartupResources } from '../tui-startup-resources.js';\nimport { fuzzySearchWorkspaceFiles } from '../../gateway/workspace-file-search.js';\n\nconst log = createLogger('TUI:Embedded');\n\ninterface EmbeddedBackendOptions {\n config?: Config;\n extensionRegistry?: ExtensionRegistryImpl;\n implicitTrustedWorkspace?: string;\n isWorkspaceTrusted?: (workspaceDir: string) => boolean | null | undefined;\n}\n\nfunction isPathSameOrInside(parentDir: string, childDir: string): boolean {\n const rel = relative(resolve(parentDir), resolve(childDir));\n return rel === '' || (!!rel && !rel.startsWith('..') && !isAbsolute(rel));\n}\n\n/**\n * TUI backend that runs the agent in-process (no gateway required).\n *\n * Wraps `AgentService` directly and emits TuiEvents by observing the\n * `MessageBus` output stream.\n */\nexport class EmbeddedBackend implements TuiBackend {\n private bus: MessageBus;\n private agent: AgentService | null = null;\n private agentLoading: Promise<AgentService> | null = null;\n private config: Config | null = null;\n private workspace = '';\n private sessionIndex: SessionIndex | null = null;\n private sessionIndexReady: Promise<void> | null = null;\n private workflowRunService: WorkflowRunService | null = null;\n private running = false;\n private chatAbort: AbortController | null = null;\n\n onEvent?: (evt: TuiEvent) => void;\n onConnected?: () => void;\n onDisconnected?: (reason: string) => void;\n\n constructor(private readonly opts?: EmbeddedBackendOptions) {\n this.bus = new MessageBus();\n }\n\n get connectionLabel(): string {\n return 'local embedded';\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n\n const config = this.opts?.config ?? loadConfig();\n this.config = config;\n const workspace = getWorkspacePath(config);\n this.workspace = workspace;\n openXopcDatabase();\n this.sessionIndex = new SessionIndex({ config });\n this.sessionIndexReady = this.sessionIndex.initialize().catch((err: unknown) => {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, errorMessage }, `Embedded session index initialization failed: ${errorMessage}`);\n throw err;\n });\n\n this.onConnected?.();\n setTimeout(() => {\n void this.ensureAgent().catch((err) => {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage }, `Embedded agent failed: ${errorMessage}`);\n this.onDisconnected?.(errorMessage);\n });\n }, 750);\n\n // Process outbound messages in background\n this.processOutbound();\n }\n\n private async ensureAgent(): Promise<AgentService> {\n if (this.agent) {\n return this.agent;\n }\n if (this.agentLoading) {\n return this.agentLoading;\n }\n if (!this.config || !this.sessionIndex) {\n throw new Error('Embedded backend not started');\n }\n\n this.agentLoading = this.createAgent(this.config, this.sessionIndex);\n try {\n return await this.agentLoading;\n } finally {\n this.agentLoading = null;\n }\n }\n\n private async createAgent(config: Config, sessionIndex: SessionIndex): Promise<AgentService> {\n const { AgentService } = await import('../../agent/service.js');\n const workspace = this.workspace || getWorkspacePath(config);\n const modelId = getAgentDefaultModelRef(config);\n const agent = new AgentService(this.bus, {\n workspace,\n model: modelId,\n config,\n sessionStore: sessionIndex.getStore(),\n getWorkflowRunService: () => this.getWorkflowRunService(),\n extensionRegistry: this.opts?.extensionRegistry,\n isWorkspaceTrusted: (workspaceDir) => {\n const explicit = this.opts?.isWorkspaceTrusted?.(workspaceDir);\n if (explicit !== undefined && explicit !== null) {\n return explicit;\n }\n const implicit = this.opts?.implicitTrustedWorkspace;\n if (implicit && isPathSameOrInside(implicit, workspaceDir)) {\n return true;\n }\n return undefined;\n },\n });\n this.agent = agent;\n await agent.start();\n return agent;\n }\n\n stop(): void {\n this.running = false;\n this.chatAbort?.abort();\n this.chatAbort = null;\n this.bus.shutdown();\n void this.agent?.stop();\n this.agent = null;\n }\n\n getActiveSignal(): AbortSignal | undefined {\n const signal = this.chatAbort?.signal;\n return signal && !signal.aborted ? signal : undefined;\n }\n\n private getWorkflowRunService(): WorkflowRunService {\n if (this.workflowRunService) return this.workflowRunService;\n if (!this.config || !this.sessionIndex || !this.agent) {\n throw new Error('Embedded workflow runtime is not ready.');\n }\n\n const host: GatewayWorkflowHost = {\n currentConfig: this.config,\n currentWorkspacePath: this.workspace,\n messageBusInstance: this.bus,\n agentService: {\n getModelForSession: (sessionKey) => this.agent!.getModelForSession(sessionKey),\n },\n sessionIndexInstance: this.sessionIndex,\n emit: (event, payload) => {\n this.onEvent?.({\n event,\n data: payload,\n source: 'broadcast',\n });\n },\n };\n\n this.workflowRunService = new WorkflowRunService({\n service: host,\n sessionBridge: new WorkflowSessionBridge(host),\n buildChildTools: buildWorkflowChildTools,\n });\n return this.workflowRunService;\n }\n\n private activeConfig(): Config {\n return this.config ?? this.opts?.config ?? loadConfig();\n }\n\n async getStartupResources(sessionKey: string) {\n return collectTuiStartupResources(this.activeConfig(), sessionKey);\n }\n\n async startWorkflowRun(opts: TuiWorkflowRunStartRequest): Promise<TuiWorkflowRunStartResult> {\n await this.sessionIndexReady;\n const agentId = opts.agentId?.trim() || resolveAgentIdFromSessionKey(opts.sessionKey);\n const result = await this.getWorkflowRunService().startWorkflowRun({\n agentId,\n definitionId: opts.definitionId,\n parentSessionKey: opts.sessionKey,\n source: { kind: 'chat', sessionKey: opts.sessionKey },\n goal: opts.goal,\n input: opts.input,\n });\n if (result.ok === false) {\n throw new Error(result.message);\n }\n return {\n runId: result.runId,\n sessionKey: result.sessionKey,\n definitionId: opts.definitionId,\n };\n }\n\n async searchWorkspaceFiles(\n sessionKey: string,\n query: string,\n options?: { limit?: number },\n ): Promise<TuiWorkspaceFileSearchEntry[]> {\n if (!this.agent) return [];\n try {\n const workspaceRoot = await this.agent.getEffectiveWorkspacePathForSession(sessionKey);\n return await fuzzySearchWorkspaceFiles(workspaceRoot, query, options?.limit ?? 15);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded workspace file search failed: ${errorMessage}`);\n return [];\n }\n }\n\n async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> {\n const agent = await this.ensureAgent();\n\n const runId = crypto.randomUUID();\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n\n const mapper = new ChatStreamMapper({ runId, sessionKey: opts.sessionKey, channel: 'tui' });\n for (const event of mapper.start()) {\n this.onEvent?.({ event: event.type, data: event, source: 'embedded' });\n }\n\n // Run the stream in background so the TUI event loop stays responsive.\n void (async () => {\n try {\n // Prepend envelope timestamp so the model knows the current date/time,\n // matching the behavior of channel pipelines (Telegram, Weixin, etc.).\n // Skip for slash commands — parseSlashCommand requires lines starting with '/'.\n const messageForAgent = opts.message.trimStart().startsWith('/')\n ? opts.message\n : prependEnvelopeTimestamp(opts.message);\n\n const stream = agent.turnDispatcher.processDirectStreaming(\n messageForAgent,\n opts.sessionKey,\n opts.attachments,\n opts.thinking,\n { signal, runId },\n );\n\n for await (const event of stream) {\n if (signal.aborted) break;\n for (const mapped of mapper.map(event)) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n }\n\n if (!signal.aborted) {\n for (const mapped of mapper.end('success')) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n }\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n for (const mapped of mapper.error(errorMessage)) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n for (const mapped of mapper.end('error', errorMessage)) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n }\n })();\n\n return { runId };\n }\n\n async abortChat(_opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean }> {\n if (this.chatAbort) {\n this.chatAbort.abort();\n this.chatAbort = null;\n return { ok: true };\n }\n return { ok: false };\n }\n\n async steerChat(opts: { sessionKey: string; message: string }): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n const ok = await this.agent.turnDispatcher.steerWebchatSession(opts.sessionKey, opts.message);\n return { ok };\n }\n\n async loadHistory(opts: {\n sessionKey: string;\n limit?: number;\n }): Promise<{ messages: HistoryMessage[] }> {\n if (!this.agent) {\n return { messages: [] };\n }\n try {\n const rows = await this.agent.sessionStore.loadTranscriptRows(opts.sessionKey);\n return {\n messages: transcriptRowsToClientHistory(rows, { limit: opts.limit }),\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Embedded loadHistory failed: ${errorMessage}`);\n return { messages: [] };\n }\n }\n\n async loadTranscriptTree(sessionKey: string): Promise<TuiTranscriptTreeEntry[]> {\n if (!this.agent) return [];\n try {\n const rows = await this.agent.sessionStore.loadTranscriptRows(sessionKey);\n return buildTuiTranscriptTree(rows);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded loadTranscriptTree failed: ${errorMessage}`);\n return [];\n }\n }\n\n async getSessionStats(sessionKey: string): Promise<TuiSessionStats> {\n if (!this.agent) return computeTuiSessionStats([]);\n try {\n const rows = await this.agent.sessionStore.loadTranscriptRows(sessionKey);\n return computeTuiSessionStats(rows);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded getSessionStats failed: ${errorMessage}`);\n return computeTuiSessionStats([]);\n }\n }\n\n async listSessions(): Promise<TuiSessionItem[]> {\n if (!this.agent) return [];\n try {\n const result = await this.agent.sessionStore.list({\n limit: 200,\n sortBy: 'updatedAt',\n sortOrder: 'desc',\n });\n return result.items.map(sessionMetadataToTuiItem);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Embedded listSessions failed: ${errorMessage}`);\n return [];\n }\n }\n\n async listAgents(): Promise<TuiAgentInfo[]> {\n const config = this.activeConfig();\n const agents = new Map<string, TuiAgentInfo>();\n for (const entry of listAgentEntries(config)) {\n if (entry.enabled === false) continue;\n const id = normalizeAgentId(entry.id);\n agents.set(id, {\n id,\n enabled: true,\n });\n }\n return [...agents.values()].sort((a, b) => a.id.localeCompare(b.id));\n }\n\n async setTuiDefaultAgent(agentId: string): Promise<{ agentId: string }> {\n const result = setTuiDefaultAgentConfig(this.activeConfig(), agentId);\n if (result.ok === false) {\n throw new Error(result.message);\n }\n await saveConfig(result.config);\n this.config = result.config;\n return { agentId: result.agentId };\n }\n\n async renameSession(sessionKey: string, name: string): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n try {\n await this.agent.sessionStore.updateMetadata(sessionKey, { name: name.trim() });\n return { ok: true };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded renameSession failed: ${errorMessage}`);\n return { ok: false };\n }\n }\n\n async deleteSession(sessionKey: string): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n try {\n const ok = await this.agent.sessionStore.deleteSession(sessionKey);\n return { ok };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded deleteSession failed: ${errorMessage}`);\n return { ok: false };\n }\n }\n\n async getSessionInfo(sessionKey: string): Promise<SessionInfo> {\n if (!this.agent) {\n const config = this.activeConfig();\n const model = getAgentDefaultModelRef(config);\n return { model: model ?? undefined };\n }\n try {\n const cfg = await this.agent.sessionInspector.agentConfig(sessionKey);\n const parsed = parseModelRef(cfg.model);\n const usage = await this.agent.sessionInspector.contextUsage(sessionKey);\n return {\n model: parsed?.model ?? cfg.model,\n modelProvider: parsed?.provider,\n thinkingLevel: cfg.thinkingLevel,\n reasoningLevel: cfg.reasoningLevel,\n verboseLevel: cfg.verboseLevel,\n totalTokens: usage.estimatedTokens,\n contextWindow: usage.contextWindow,\n contextUsagePercent: usage.usagePercent,\n effectiveWorkspacePath: cfg.effectiveWorkspacePath,\n workingDirectoryLocked: cfg.workingDirectoryLocked,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey, errorMessage }, `getSessionInfo failed: ${errorMessage}`);\n const config = this.activeConfig();\n const model = getAgentDefaultModelRef(config);\n return { model: model ?? undefined };\n }\n }\n\n async listModels(): Promise<TuiModelChoice[]> {\n const { getAvailableModels } = await import('../../providers/index.js');\n const models = await getAvailableModels();\n return models.map((model) => ({\n id: model.id,\n name: model.name ?? model.id,\n provider: model.provider,\n contextWindow: model.contextWindow,\n }));\n }\n\n async resetSession(sessionKey: string): Promise<void> {\n if (!this.agent) return;\n await this.agent.resetSession(sessionKey);\n }\n\n async patchSession(\n sessionKey: string,\n patch: Record<string, unknown>,\n ): Promise<void> {\n const agent = await this.ensureAgent();\n const result = await agent.sessionConfig.patch(sessionKey, {\n model: typeof patch.model === 'string' ? patch.model : undefined,\n thinkingLevel: typeof patch.thinkingLevel === 'string' ? patch.thinkingLevel : undefined,\n reasoningLevel: typeof patch.reasoningLevel === 'string' ? patch.reasoningLevel : undefined,\n verboseLevel: typeof patch.verboseLevel === 'string' ? patch.verboseLevel : undefined,\n workingDirectory: typeof patch.workingDirectory === 'string' ? patch.workingDirectory : undefined,\n });\n if (!result.ok) {\n throw new Error(result.error);\n }\n }\n\n async compactSession(\n sessionKey: string,\n options?: { force?: boolean; instructions?: string },\n ): Promise<TuiCompactionResult> {\n if (!this.agent) return { compacted: false, summary: 'Agent not started' };\n try {\n const result = await this.agent.sessionInspector.compact(sessionKey, {\n force: options?.force ?? true,\n instructions: options?.instructions,\n });\n if (!result.compacted) return { compacted: false, summary: 'Nothing to compact' };\n return {\n compacted: true,\n summary: `Compacted (${result.tokensBefore ?? '?'} → ${result.tokensAfter ?? '?'} tokens)`,\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n transcriptSummary: result.summary,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return { compacted: false, summary: errorMessage };\n }\n }\n\n async exportSession(sessionKey: string, format: ExportFormat): Promise<string> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n return this.agent.sessionStore.exportSession(sessionKey, format);\n }\n\n async importSession(\n targetSessionKey: string,\n jsonContent: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n return this.agent.sessionStore.importSessionExport(targetSessionKey, jsonContent);\n }\n\n async createShare(\n _sessionKey: string,\n request: TuiShareRequest,\n _options?: { agentId?: string },\n ): Promise<TuiShareResult> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n const config = this.activeConfig();\n if (!isShareToolAvailable(config)) {\n throw new Error('Sharing is disabled in gateway config');\n }\n const workspace = getWorkspacePath(config);\n const tool = createCreateShareTool({\n workspace,\n getConfig: () => config,\n getAgentId: () => 'tui',\n });\n const result = await tool.execute(\n 'tui-share',\n {\n filePath: request.path,\n audience: request.audience,\n mode: request.mode,\n title: request.title,\n description: request.description,\n },\n );\n const details = (result.details ?? {}) as Record<string, unknown>;\n const error = typeof details.error === 'string' ? details.error : undefined;\n if (error) {\n throw new Error(error);\n }\n const shareUrl = typeof details.shareUrl === 'string' ? details.shareUrl : undefined;\n if (!shareUrl) {\n const text = result.content\n .map((part) => (part.type === 'text' ? part.text : ''))\n .join('\\n')\n .trim();\n throw new Error(text || 'Share failed');\n }\n return {\n kind: typeof details.kind === 'string' ? details.kind : 'share',\n shareUrl,\n title: typeof details.title === 'string' ? details.title : undefined,\n description: typeof details.description === 'string' ? details.description : undefined,\n thumbnailUrl: typeof details.thumbnailUrl === 'string' ? details.thumbnailUrl : undefined,\n reachability: typeof details.reachability === 'string' ? details.reachability : undefined,\n reachabilityHint:\n typeof details.reachabilityHint === 'string' ? details.reachabilityHint : undefined,\n expiresAt: typeof details.expiresAt === 'string' ? details.expiresAt : undefined,\n maxViews: typeof details.maxViews === 'number' ? details.maxViews : null,\n routingReason:\n typeof (details.routing as { reason?: unknown } | undefined)?.reason === 'string'\n ? ((details.routing as { reason: string }).reason)\n : undefined,\n routingHint:\n typeof (details.routing as { hint?: unknown } | undefined)?.hint === 'string'\n ? ((details.routing as { hint: string }).hint)\n : undefined,\n };\n }\n\n async btwQuery(sessionKey: string, question: string): Promise<{ text: string; error?: string }> {\n if (!this.agent) {\n return { text: '', error: 'Agent not started' };\n }\n return this.agent.sessionInspector.btwQuery(sessionKey, question);\n }\n\n async forkSession(\n sourceSessionKey: string,\n targetSessionKey: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n return this.agent.sessionStore.forkSession(sourceSessionKey, targetSessionKey);\n }\n\n async forkSessionAt(\n sourceSessionKey: string,\n targetSessionKey: string,\n entryId: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n const throughRow = transcriptTreeEntryIdToRowNumber(entryId);\n if (throughRow == null) {\n throw new Error(`Invalid transcript entry: ${entryId}`);\n }\n return this.agent.sessionStore.forkSessionRows(sourceSessionKey, targetSessionKey, { throughRow });\n }\n\n async setTranscriptLabel(\n sessionKey: string,\n entryId: string,\n label: string | undefined,\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptLabelEntry(sessionKey, { targetId: entryId, label });\n return { ok: true };\n }\n\n async appendCustomEntry(\n sessionKey: string,\n customType: string,\n data?: unknown,\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptCustomEntry(sessionKey, { customType, data });\n return { ok: true };\n }\n\n async appendCustomMessage(\n sessionKey: string,\n message: {\n customType: string;\n content?: string | unknown[];\n display?: boolean;\n details?: unknown;\n },\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptCustomMessageEntry(sessionKey, message);\n evictEmbeddedSessionRunner(sessionKey, 'tui_custom_message_appended');\n return { ok: true };\n }\n\n async appendBashExecution(\n sessionKey: string,\n entry: {\n command: string;\n output?: string;\n exitCode?: number | null;\n signal?: string | null;\n excludeFromContext?: boolean;\n truncated?: boolean;\n fullOutputPath?: string;\n },\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptBashExecutionEntry(sessionKey, entry);\n evictEmbeddedSessionRunner(sessionKey, 'tui_bash_execution_appended');\n return { ok: true };\n }\n\n private processOutbound(): void {\n void (async () => {\n while (this.running) {\n try {\n const msg = await this.bus.consumeOutbound();\n log.debug({ channel: msg.channel, chatId: msg.chat_id }, 'Outbound message');\n } catch (error) {\n if (error instanceof MessageBusShutdownError) break;\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Outbound processor failed: ${errorMessage}`);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n })();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"embedded-backend.js","names":[],"sources":["../../../../src/tui/backends/embedded-backend.ts"],"sourcesContent":["import type { ExtensionRegistryImpl } from '../../extensions/loader.js';\nimport { isAbsolute, relative, resolve } from 'node:path';\nimport type { AgentService } from '../../agent/service.js';\nimport { listAgentEntries, normalizeAgentId } from '../../agent/agent-scope.js';\nimport { resolveAgentIdFromSessionKey } from '../../routing/agent-session-key.js';\nimport { parseModelRef } from '../../agent/models/selection.js';\nimport { createCreateShareTool, isShareToolAvailable } from '../../agent/tools/create-share-tool.js';\nimport { transcriptRowsToClientHistory } from '../../session/client-history.js';\nimport { prependEnvelopeTimestamp } from '../../channels/envelope-timestamp.js';\nimport { loadConfig, getWorkspacePath, saveConfig } from '../../config/index.js';\nimport { getAgentDefaultModelRef, type Config } from '../../config/schema.js';\nimport { setTuiDefaultAgentConfig } from '../../commands/agents.config.js';\nimport { MessageBus, MessageBusShutdownError } from '../../infra/bus/index.js';\nimport { evictEmbeddedSessionRunner } from '../../agent/embedded/session-runner.js';\nimport type { ExportFormat } from '../../session/types.js';\nimport { SessionIndex } from '../../session/index.js';\nimport { getSessionMetadata, openXopcDatabase } from '../../storage/sqlite/index.js';\nimport { buildWorkflowChildTools } from '../../agent/workflow/workflow-child-tools.js';\nimport type { GatewayWorkflowHost } from '../../gateway/gateway-workflow-host.types.js';\nimport { WorkflowRunService } from '../../workflows/service/workflow-run-service.js';\nimport { WorkflowSessionBridge } from '../../workflows/service/workflow-session-bridge.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type {\n ChatSendOptions,\n HistoryMessage,\n TuiBackend,\n TuiCompactionResult,\n TuiEvent,\n TuiModelChoice,\n TuiShareRequest,\n TuiShareResult,\n TuiSessionStats,\n TuiSessionItem,\n TuiTranscriptTreeEntry,\n TuiAgentInfo,\n TuiWorkspaceFileSearchEntry,\n TuiWorkflowRunStartRequest,\n TuiWorkflowRunStartResult,\n TuiStartupProjectResult,\n} from '../tui-backend.js';\nimport type { SessionInfo } from '../tui-types.js';\nimport { sessionMetadataToTuiItem } from '../tui-session-format.js';\nimport { computeTuiSessionStats } from '../tui-session-stats.js';\nimport { buildTuiTranscriptTree, transcriptTreeEntryIdToRowNumber } from '../tui-transcript-tree.js';\nimport { ChatStreamMapper } from '../../gateway/chat-stream/mapper.js';\nimport { collectTuiStartupResources } from '../tui-startup-resources.js';\nimport { fuzzySearchWorkspaceFiles } from '../../gateway/workspace-file-search.js';\nimport { ProjectService } from '../../projects/project-service.js';\n\nconst log = createLogger('TUI:Embedded');\n\ninterface EmbeddedBackendOptions {\n config?: Config;\n extensionRegistry?: ExtensionRegistryImpl;\n implicitTrustedWorkspace?: string;\n isWorkspaceTrusted?: (workspaceDir: string) => boolean | null | undefined;\n}\n\nfunction isPathSameOrInside(parentDir: string, childDir: string): boolean {\n const rel = relative(resolve(parentDir), resolve(childDir));\n return rel === '' || (!!rel && !rel.startsWith('..') && !isAbsolute(rel));\n}\n\n/**\n * TUI backend that runs the agent in-process (no gateway required).\n *\n * Wraps `AgentService` directly and emits TuiEvents by observing the\n * `MessageBus` output stream.\n */\nexport class EmbeddedBackend implements TuiBackend {\n private bus: MessageBus;\n private agent: AgentService | null = null;\n private agentLoading: Promise<AgentService> | null = null;\n private config: Config | null = null;\n private workspace = '';\n private sessionIndex: SessionIndex | null = null;\n private sessionIndexReady: Promise<void> | null = null;\n private workflowRunService: WorkflowRunService | null = null;\n private running = false;\n private chatAbort: AbortController | null = null;\n\n onEvent?: (evt: TuiEvent) => void;\n onConnected?: () => void;\n onDisconnected?: (reason: string) => void;\n\n constructor(private readonly opts?: EmbeddedBackendOptions) {\n this.bus = new MessageBus();\n }\n\n get connectionLabel(): string {\n return 'local embedded';\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n\n const config = this.opts?.config ?? loadConfig();\n this.config = config;\n const workspace = getWorkspacePath(config);\n this.workspace = workspace;\n openXopcDatabase();\n this.sessionIndex = new SessionIndex({ config });\n this.sessionIndexReady = this.sessionIndex.initialize().catch((err: unknown) => {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, errorMessage }, `Embedded session index initialization failed: ${errorMessage}`);\n throw err;\n });\n\n this.onConnected?.();\n setTimeout(() => {\n void this.ensureAgent().catch((err) => {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage }, `Embedded agent failed: ${errorMessage}`);\n this.onDisconnected?.(errorMessage);\n });\n }, 750);\n\n // Process outbound messages in background\n this.processOutbound();\n }\n\n private async ensureAgent(): Promise<AgentService> {\n if (this.agent) {\n return this.agent;\n }\n if (this.agentLoading) {\n return this.agentLoading;\n }\n if (!this.config || !this.sessionIndex) {\n throw new Error('Embedded backend not started');\n }\n\n this.agentLoading = this.createAgent(this.config, this.sessionIndex);\n try {\n return await this.agentLoading;\n } finally {\n this.agentLoading = null;\n }\n }\n\n private async createAgent(config: Config, sessionIndex: SessionIndex): Promise<AgentService> {\n const { AgentService } = await import('../../agent/service.js');\n const workspace = this.workspace || getWorkspacePath(config);\n const modelId = getAgentDefaultModelRef(config);\n const agent = new AgentService(this.bus, {\n workspace,\n model: modelId,\n config,\n sessionStore: sessionIndex.getStore(),\n getWorkflowRunService: () => this.getWorkflowRunService(),\n extensionRegistry: this.opts?.extensionRegistry,\n isWorkspaceTrusted: (workspaceDir) => {\n const explicit = this.opts?.isWorkspaceTrusted?.(workspaceDir);\n if (explicit !== undefined && explicit !== null) {\n return explicit;\n }\n const implicit = this.opts?.implicitTrustedWorkspace;\n if (implicit && isPathSameOrInside(implicit, workspaceDir)) {\n return true;\n }\n return undefined;\n },\n });\n this.agent = agent;\n await agent.start();\n return agent;\n }\n\n stop(): void {\n this.running = false;\n this.chatAbort?.abort();\n this.chatAbort = null;\n this.bus.shutdown();\n void this.agent?.stop();\n this.agent = null;\n }\n\n getActiveSignal(): AbortSignal | undefined {\n const signal = this.chatAbort?.signal;\n return signal && !signal.aborted ? signal : undefined;\n }\n\n private getWorkflowRunService(): WorkflowRunService {\n if (this.workflowRunService) return this.workflowRunService;\n if (!this.config || !this.sessionIndex || !this.agent) {\n throw new Error('Embedded workflow runtime is not ready.');\n }\n\n const host: GatewayWorkflowHost = {\n currentConfig: this.config,\n currentWorkspacePath: this.workspace,\n messageBusInstance: this.bus,\n agentService: {\n getModelForSession: (sessionKey) => this.agent!.getModelForSession(sessionKey),\n },\n sessionIndexInstance: this.sessionIndex,\n emit: (event, payload) => {\n this.onEvent?.({\n event,\n data: payload,\n source: 'broadcast',\n });\n },\n };\n\n this.workflowRunService = new WorkflowRunService({\n service: host,\n sessionBridge: new WorkflowSessionBridge(host),\n buildChildTools: buildWorkflowChildTools,\n });\n return this.workflowRunService;\n }\n\n private activeConfig(): Config {\n return this.config ?? this.opts?.config ?? loadConfig();\n }\n\n async getStartupResources(sessionKey: string) {\n return collectTuiStartupResources(this.activeConfig(), sessionKey);\n }\n\n async startWorkflowRun(opts: TuiWorkflowRunStartRequest): Promise<TuiWorkflowRunStartResult> {\n await this.sessionIndexReady;\n const agentId = opts.agentId?.trim() || resolveAgentIdFromSessionKey(opts.sessionKey);\n const result = await this.getWorkflowRunService().startWorkflowRun({\n agentId,\n definitionId: opts.definitionId,\n parentSessionKey: opts.sessionKey,\n source: { kind: 'chat', sessionKey: opts.sessionKey },\n goal: opts.goal,\n input: opts.input,\n });\n if (result.ok === false) {\n throw new Error(result.message);\n }\n return {\n runId: result.runId,\n sessionKey: result.sessionKey,\n definitionId: opts.definitionId,\n };\n }\n\n async resolveStartupProject(opts: {\n workspacePath: string;\n sessionKey: string;\n agentId: string;\n autoCreate?: boolean;\n }): Promise<TuiStartupProjectResult> {\n await this.sessionIndexReady;\n if (!this.sessionIndex) return { project: null };\n const projects = new ProjectService();\n const match = projects.resolveOrCreateForWorkspacePath({\n workspacePath: opts.workspacePath,\n agentId: opts.agentId,\n autoCreate: opts.autoCreate !== false,\n });\n if (!match) return { project: null };\n if (!getSessionMetadata(opts.sessionKey)) {\n await this.sessionIndex.getStore().resolveTranscriptPath(opts.sessionKey, {\n metadata: {\n sourceChannel: 'tui',\n sourceChatId: `default:direct:${opts.sessionKey}`,\n sessionType: 'chat',\n projectId: match.project.id,\n routing: {\n agentId: opts.agentId,\n source: 'tui',\n accountId: 'default',\n peerKind: 'direct',\n peerId: opts.sessionKey,\n },\n },\n });\n }\n projects.attachSession(opts.sessionKey, match.project.id);\n return { project: match.project, created: match.created, reason: match.reason };\n }\n\n async searchWorkspaceFiles(\n sessionKey: string,\n query: string,\n options?: { limit?: number },\n ): Promise<TuiWorkspaceFileSearchEntry[]> {\n if (!this.agent) return [];\n try {\n const workspaceRoot = await this.agent.getEffectiveWorkspacePathForSession(sessionKey);\n return await fuzzySearchWorkspaceFiles(workspaceRoot, query, options?.limit ?? 15);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded workspace file search failed: ${errorMessage}`);\n return [];\n }\n }\n\n async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> {\n const agent = await this.ensureAgent();\n\n const runId = crypto.randomUUID();\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n\n const mapper = new ChatStreamMapper({ runId, sessionKey: opts.sessionKey, channel: 'tui' });\n for (const event of mapper.start()) {\n this.onEvent?.({ event: event.type, data: event, source: 'embedded' });\n }\n\n // Run the stream in background so the TUI event loop stays responsive.\n void (async () => {\n try {\n // Prepend envelope timestamp so the model knows the current date/time,\n // matching the behavior of channel pipelines (Telegram, Weixin, etc.).\n // Skip for slash commands — parseSlashCommand requires lines starting with '/'.\n const messageForAgent = opts.message.trimStart().startsWith('/')\n ? opts.message\n : prependEnvelopeTimestamp(opts.message);\n\n const stream = agent.turnDispatcher.processDirectStreaming(\n messageForAgent,\n opts.sessionKey,\n opts.attachments,\n opts.thinking,\n { signal, runId },\n );\n\n for await (const event of stream) {\n if (signal.aborted) break;\n for (const mapped of mapper.map(event)) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n }\n\n if (!signal.aborted) {\n for (const mapped of mapper.end('success')) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n }\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n for (const mapped of mapper.error(errorMessage)) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n for (const mapped of mapper.end('error', errorMessage)) {\n this.onEvent?.({ event: mapped.type, data: mapped, source: 'embedded' });\n }\n }\n })();\n\n return { runId };\n }\n\n async abortChat(_opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean }> {\n if (this.chatAbort) {\n this.chatAbort.abort();\n this.chatAbort = null;\n return { ok: true };\n }\n return { ok: false };\n }\n\n async steerChat(opts: { sessionKey: string; message: string }): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n const ok = await this.agent.turnDispatcher.steerWebchatSession(opts.sessionKey, opts.message);\n return { ok };\n }\n\n async loadHistory(opts: {\n sessionKey: string;\n limit?: number;\n }): Promise<{ messages: HistoryMessage[] }> {\n if (!this.agent) {\n return { messages: [] };\n }\n try {\n const rows = await this.agent.sessionStore.loadTranscriptRows(opts.sessionKey);\n return {\n messages: transcriptRowsToClientHistory(rows, { limit: opts.limit }),\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Embedded loadHistory failed: ${errorMessage}`);\n return { messages: [] };\n }\n }\n\n async loadTranscriptTree(sessionKey: string): Promise<TuiTranscriptTreeEntry[]> {\n if (!this.agent) return [];\n try {\n const rows = await this.agent.sessionStore.loadTranscriptRows(sessionKey);\n return buildTuiTranscriptTree(rows);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded loadTranscriptTree failed: ${errorMessage}`);\n return [];\n }\n }\n\n async getSessionStats(sessionKey: string): Promise<TuiSessionStats> {\n if (!this.agent) return computeTuiSessionStats([]);\n try {\n const rows = await this.agent.sessionStore.loadTranscriptRows(sessionKey);\n return computeTuiSessionStats(rows);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded getSessionStats failed: ${errorMessage}`);\n return computeTuiSessionStats([]);\n }\n }\n\n async listSessions(): Promise<TuiSessionItem[]> {\n if (!this.agent) return [];\n try {\n const result = await this.agent.sessionStore.list({\n limit: 200,\n sortBy: 'updatedAt',\n sortOrder: 'desc',\n });\n return result.items.map(sessionMetadataToTuiItem);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Embedded listSessions failed: ${errorMessage}`);\n return [];\n }\n }\n\n async listAgents(): Promise<TuiAgentInfo[]> {\n const config = this.activeConfig();\n const agents = new Map<string, TuiAgentInfo>();\n for (const entry of listAgentEntries(config)) {\n if (entry.enabled === false) continue;\n const id = normalizeAgentId(entry.id);\n agents.set(id, {\n id,\n enabled: true,\n });\n }\n return [...agents.values()].sort((a, b) => a.id.localeCompare(b.id));\n }\n\n async setTuiDefaultAgent(agentId: string): Promise<{ agentId: string }> {\n const result = setTuiDefaultAgentConfig(this.activeConfig(), agentId);\n if (result.ok === false) {\n throw new Error(result.message);\n }\n await saveConfig(result.config);\n this.config = result.config;\n return { agentId: result.agentId };\n }\n\n async renameSession(sessionKey: string, name: string): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n try {\n await this.agent.sessionStore.updateMetadata(sessionKey, { name: name.trim() });\n return { ok: true };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded renameSession failed: ${errorMessage}`);\n return { ok: false };\n }\n }\n\n async deleteSession(sessionKey: string): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n try {\n const ok = await this.agent.sessionStore.deleteSession(sessionKey);\n return { ok };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded deleteSession failed: ${errorMessage}`);\n return { ok: false };\n }\n }\n\n async getSessionInfo(sessionKey: string): Promise<SessionInfo> {\n if (!this.agent) {\n const config = this.activeConfig();\n const model = getAgentDefaultModelRef(config);\n return { model: model ?? undefined };\n }\n try {\n const cfg = await this.agent.sessionInspector.agentConfig(sessionKey);\n const parsed = parseModelRef(cfg.model);\n const usage = await this.agent.sessionInspector.contextUsage(sessionKey);\n return {\n model: parsed?.model ?? cfg.model,\n modelProvider: parsed?.provider,\n thinkingLevel: cfg.thinkingLevel,\n reasoningLevel: cfg.reasoningLevel,\n verboseLevel: cfg.verboseLevel,\n totalTokens: usage.estimatedTokens,\n contextWindow: usage.contextWindow,\n contextUsagePercent: usage.usagePercent,\n effectiveWorkspacePath: cfg.effectiveWorkspacePath,\n workingDirectoryLocked: cfg.workingDirectoryLocked,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey, errorMessage }, `getSessionInfo failed: ${errorMessage}`);\n const config = this.activeConfig();\n const model = getAgentDefaultModelRef(config);\n return { model: model ?? undefined };\n }\n }\n\n async listModels(): Promise<TuiModelChoice[]> {\n const { getAvailableModels } = await import('../../providers/index.js');\n const models = await getAvailableModels();\n return models.map((model) => ({\n id: model.id,\n name: model.name ?? model.id,\n provider: model.provider,\n contextWindow: model.contextWindow,\n }));\n }\n\n async resetSession(sessionKey: string): Promise<void> {\n if (!this.agent) return;\n await this.agent.resetSession(sessionKey);\n }\n\n async patchSession(\n sessionKey: string,\n patch: Record<string, unknown>,\n ): Promise<void> {\n const agent = await this.ensureAgent();\n const projectId = typeof patch.projectId === 'string' ? patch.projectId.trim() : '';\n if (projectId) {\n await this.sessionIndexReady;\n await this.sessionIndex?.getStore().resolveTranscriptPath(sessionKey);\n new ProjectService().attachSession(sessionKey, projectId);\n }\n const result = await agent.sessionConfig.patch(sessionKey, {\n model: typeof patch.model === 'string' ? patch.model : undefined,\n thinkingLevel: typeof patch.thinkingLevel === 'string' ? patch.thinkingLevel : undefined,\n reasoningLevel: typeof patch.reasoningLevel === 'string' ? patch.reasoningLevel : undefined,\n verboseLevel: typeof patch.verboseLevel === 'string' ? patch.verboseLevel : undefined,\n workingDirectory: typeof patch.workingDirectory === 'string' ? patch.workingDirectory : undefined,\n });\n if (!result.ok) {\n throw new Error(result.error);\n }\n }\n\n async compactSession(\n sessionKey: string,\n options?: { force?: boolean; instructions?: string },\n ): Promise<TuiCompactionResult> {\n if (!this.agent) return { compacted: false, summary: 'Agent not started' };\n try {\n const result = await this.agent.sessionInspector.compact(sessionKey, {\n force: options?.force ?? true,\n instructions: options?.instructions,\n });\n if (!result.compacted) return { compacted: false, summary: 'Nothing to compact' };\n return {\n compacted: true,\n summary: `Compacted (${result.tokensBefore ?? '?'} → ${result.tokensAfter ?? '?'} tokens)`,\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n transcriptSummary: result.summary,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return { compacted: false, summary: errorMessage };\n }\n }\n\n async exportSession(sessionKey: string, format: ExportFormat): Promise<string> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n return this.agent.sessionStore.exportSession(sessionKey, format);\n }\n\n async importSession(\n targetSessionKey: string,\n jsonContent: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n return this.agent.sessionStore.importSessionExport(targetSessionKey, jsonContent);\n }\n\n async createShare(\n _sessionKey: string,\n request: TuiShareRequest,\n _options?: { agentId?: string },\n ): Promise<TuiShareResult> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n const config = this.activeConfig();\n if (!isShareToolAvailable(config)) {\n throw new Error('Sharing is disabled in gateway config');\n }\n const workspace = getWorkspacePath(config);\n const tool = createCreateShareTool({\n workspace,\n getConfig: () => config,\n getAgentId: () => 'tui',\n });\n const result = await tool.execute(\n 'tui-share',\n {\n filePath: request.path,\n audience: request.audience,\n mode: request.mode,\n title: request.title,\n description: request.description,\n },\n );\n const details = (result.details ?? {}) as Record<string, unknown>;\n const error = typeof details.error === 'string' ? details.error : undefined;\n if (error) {\n throw new Error(error);\n }\n const shareUrl = typeof details.shareUrl === 'string' ? details.shareUrl : undefined;\n if (!shareUrl) {\n const text = result.content\n .map((part) => (part.type === 'text' ? part.text : ''))\n .join('\\n')\n .trim();\n throw new Error(text || 'Share failed');\n }\n return {\n kind: typeof details.kind === 'string' ? details.kind : 'share',\n shareUrl,\n title: typeof details.title === 'string' ? details.title : undefined,\n description: typeof details.description === 'string' ? details.description : undefined,\n thumbnailUrl: typeof details.thumbnailUrl === 'string' ? details.thumbnailUrl : undefined,\n reachability: typeof details.reachability === 'string' ? details.reachability : undefined,\n reachabilityHint:\n typeof details.reachabilityHint === 'string' ? details.reachabilityHint : undefined,\n expiresAt: typeof details.expiresAt === 'string' ? details.expiresAt : undefined,\n maxViews: typeof details.maxViews === 'number' ? details.maxViews : null,\n routingReason:\n typeof (details.routing as { reason?: unknown } | undefined)?.reason === 'string'\n ? ((details.routing as { reason: string }).reason)\n : undefined,\n routingHint:\n typeof (details.routing as { hint?: unknown } | undefined)?.hint === 'string'\n ? ((details.routing as { hint: string }).hint)\n : undefined,\n };\n }\n\n async btwQuery(sessionKey: string, question: string): Promise<{ text: string; error?: string }> {\n if (!this.agent) {\n return { text: '', error: 'Agent not started' };\n }\n return this.agent.sessionInspector.btwQuery(sessionKey, question);\n }\n\n async forkSession(\n sourceSessionKey: string,\n targetSessionKey: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n return this.agent.sessionStore.forkSession(sourceSessionKey, targetSessionKey);\n }\n\n async forkSessionAt(\n sourceSessionKey: string,\n targetSessionKey: string,\n entryId: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n const throughRow = transcriptTreeEntryIdToRowNumber(entryId);\n if (throughRow == null) {\n throw new Error(`Invalid transcript entry: ${entryId}`);\n }\n return this.agent.sessionStore.forkSessionRows(sourceSessionKey, targetSessionKey, { throughRow });\n }\n\n async setTranscriptLabel(\n sessionKey: string,\n entryId: string,\n label: string | undefined,\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptLabelEntry(sessionKey, { targetId: entryId, label });\n return { ok: true };\n }\n\n async appendCustomEntry(\n sessionKey: string,\n customType: string,\n data?: unknown,\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptCustomEntry(sessionKey, { customType, data });\n return { ok: true };\n }\n\n async appendCustomMessage(\n sessionKey: string,\n message: {\n customType: string;\n content?: string | unknown[];\n display?: boolean;\n details?: unknown;\n },\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptCustomMessageEntry(sessionKey, message);\n evictEmbeddedSessionRunner(sessionKey, 'tui_custom_message_appended');\n return { ok: true };\n }\n\n async appendBashExecution(\n sessionKey: string,\n entry: {\n command: string;\n output?: string;\n exitCode?: number | null;\n signal?: string | null;\n excludeFromContext?: boolean;\n truncated?: boolean;\n fullOutputPath?: string;\n },\n ): Promise<{ ok: boolean }> {\n if (!this.agent) {\n throw new Error('Agent not started');\n }\n await this.agent.sessionStore.appendTranscriptBashExecutionEntry(sessionKey, entry);\n evictEmbeddedSessionRunner(sessionKey, 'tui_bash_execution_appended');\n return { ok: true };\n }\n\n private processOutbound(): void {\n void (async () => {\n while (this.running) {\n try {\n const msg = await this.bus.consumeOutbound();\n log.debug({ channel: msg.channel, chatId: msg.chat_id }, 'Outbound message');\n } catch (error) {\n if (error instanceof MessageBusShutdownError) break;\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Outbound processor failed: ${errorMessage}`);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n })();\n }\n}\n"],"mappings":"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"}