@xopcai/xopc 0.0.6 → 0.0.8

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 (291) hide show
  1. package/dist/extensions/telegram/src/plugin.js +1 -1
  2. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  3. package/dist/extensions/weixin/src/api/api.js +1 -1
  4. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  5. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  6. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  7. package/dist/extensions/weixin/src/plugin.js +1 -1
  8. package/dist/gateway/static/root/assets/{agents-B6s2BvpH.js → agents-BSNzJWbQ.js} +2 -2
  9. package/dist/gateway/static/root/assets/{agents-B6s2BvpH.js.map → agents-BSNzJWbQ.js.map} +1 -1
  10. package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js → apps-page-BKk9SB4D.js} +2 -2
  11. package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js.map → apps-page-BKk9SB4D.js.map} +1 -1
  12. package/dist/gateway/static/root/assets/attachment-load-DXcJLSWT.js +1 -0
  13. package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js → channels-settings-_J6cQN6G.js} +2 -2
  14. package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js.map → channels-settings-_J6cQN6G.js.map} +1 -1
  15. package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js → chat-agents-api-DPb_0O8M.js} +2 -2
  16. package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js.map → chat-agents-api-DPb_0O8M.js.map} +1 -1
  17. package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js → cron-page-BUJOuuKX.js} +2 -2
  18. package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js.map → cron-page-BUJOuuKX.js.map} +1 -1
  19. package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js → cron-utils-Cn0YVg8x.js} +2 -2
  20. package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js.map → cron-utils-Cn0YVg8x.js.map} +1 -1
  21. package/dist/gateway/static/root/assets/electron-env-D9bm1FIu.js +2 -0
  22. package/dist/gateway/static/root/assets/electron-env-D9bm1FIu.js.map +1 -0
  23. package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js → extension-debug-page-DTz4O5Ua.js} +2 -2
  24. package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js.map → extension-debug-page-DTz4O5Ua.js.map} +1 -1
  25. package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js → extension-iframe-host-Cs1Kde9o.js} +2 -2
  26. package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js.map → extension-iframe-host-Cs1Kde9o.js.map} +1 -1
  27. package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js → extension-page-G52iX0Bo.js} +2 -2
  28. package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js.map → extension-page-G52iX0Bo.js.map} +1 -1
  29. package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js → extension-provider-CO2jxBA9.js} +2 -2
  30. package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js.map → extension-provider-CO2jxBA9.js.map} +1 -1
  31. package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js → extension-settings-page-D9Ul8uSt.js} +2 -2
  32. package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js.map → extension-settings-page-D9Ul8uSt.js.map} +1 -1
  33. package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js → gateway-config-swr-Bc8SVD15.js} +2 -2
  34. package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js.map → gateway-config-swr-Bc8SVD15.js.map} +1 -1
  35. package/dist/gateway/static/root/assets/index-BXUJbteW.js +16 -0
  36. package/dist/gateway/static/root/assets/index-BXUJbteW.js.map +1 -0
  37. package/dist/gateway/static/root/assets/index-CQLMxWSA.css +2 -0
  38. package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js → logs-page-5V25JkQY.js} +2 -2
  39. package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js.map → logs-page-5V25JkQY.js.map} +1 -1
  40. package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js → model-selector-he3aQfme.js} +2 -2
  41. package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js.map → model-selector-he3aQfme.js.map} +1 -1
  42. package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js +2 -0
  43. package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js.map +1 -0
  44. package/dist/gateway/static/root/assets/page-header-store-DJHD9Ean.js +2 -0
  45. package/dist/gateway/static/root/assets/{page-header-store-HcRZK5CZ.js.map → page-header-store-DJHD9Ean.js.map} +1 -1
  46. package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js → session-api-n-4O5d9U.js} +2 -2
  47. package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js.map → session-api-n-4O5d9U.js.map} +1 -1
  48. package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js → session-working-directory-control-B6dHLvbr.js} +3 -3
  49. package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js.map → session-working-directory-control-B6dHLvbr.js.map} +1 -1
  50. package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js → sessions-page-rBUfTdm3.js} +2 -2
  51. package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js.map → sessions-page-rBUfTdm3.js.map} +1 -1
  52. package/dist/gateway/static/root/assets/settings-page-B3QrJm-E.js +2 -0
  53. package/dist/gateway/static/root/assets/settings-page-B3QrJm-E.js.map +1 -0
  54. package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js → skill-api-vxtE8kI6.js} +2 -2
  55. package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js.map → skill-api-vxtE8kI6.js.map} +1 -1
  56. package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js → skills-page-D36_O2Ub.js} +2 -2
  57. package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js.map → skills-page-D36_O2Ub.js.map} +1 -1
  58. package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js → theme-store-CmiSsYBd.js} +2 -2
  59. package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js.map → theme-store-CmiSsYBd.js.map} +1 -1
  60. package/dist/gateway/static/root/assets/url-CtSqjF9J.js +2 -0
  61. package/dist/gateway/static/root/assets/url-CtSqjF9J.js.map +1 -0
  62. package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js → useTranslation-DYORQ7x6.js} +2 -2
  63. package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js.map → useTranslation-DYORQ7x6.js.map} +1 -1
  64. package/dist/gateway/static/root/index.html +16 -16
  65. package/dist/package.js +1 -1
  66. package/dist/src/agent/agent-manager.js +4 -4
  67. package/dist/src/agent/context/workspace-seed.js +2 -2
  68. package/dist/src/agent/image/index.d.ts +0 -1
  69. package/dist/src/agent/image/index.js +1 -2
  70. package/dist/src/agent/ipc/bus.js +1 -1
  71. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  72. package/dist/src/agent/models/manager.js +1 -1
  73. package/dist/src/agent/prompt/service-prompt-builder.js +1 -1
  74. package/dist/src/agent/service.js +3 -3
  75. package/dist/src/agent/skills/hub-hash.js +1 -1
  76. package/dist/src/agent/skills/hub-pull.js +1 -1
  77. package/dist/src/agent/skills/index.js +1 -1
  78. package/dist/src/agent/skills/skill-manager.js +1 -1
  79. package/dist/src/agent/tools/browser/tools.js +2 -2
  80. package/dist/src/agent/tools/browser/tools.js.map +1 -1
  81. package/dist/src/agent/tools/factory.js +1 -1
  82. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  83. package/dist/src/agent/tools/image-tool.js +2 -2
  84. package/dist/src/agent/tools/image-tool.js.map +1 -1
  85. package/dist/src/agent/tools/index.d.ts +1 -1
  86. package/dist/src/agent/tools/index.js +2 -2
  87. package/dist/src/agent/tools/read.d.ts +0 -2
  88. package/dist/src/agent/tools/read.js +1 -3
  89. package/dist/src/agent/tools/read.js.map +1 -1
  90. package/dist/src/agent/tools/send-media.js +1 -1
  91. package/dist/src/auth/sync-provider-auth.js +1 -1
  92. package/dist/src/channels/plugin-types.d.ts +14 -0
  93. package/dist/src/cli/commands/agent.js +1 -1
  94. package/dist/src/cli/commands/doctor/checks/channel-config.d.ts +2 -0
  95. package/dist/src/cli/commands/doctor/checks/channel-config.js +113 -0
  96. package/dist/src/cli/commands/doctor/checks/channel-config.js.map +1 -0
  97. package/dist/src/cli/commands/doctor/checks/channel-plugins.d.ts +2 -0
  98. package/dist/src/cli/commands/doctor/checks/channel-plugins.js +47 -0
  99. package/dist/src/cli/commands/doctor/checks/channel-plugins.js.map +1 -0
  100. package/dist/src/cli/commands/doctor/checks/config-health.d.ts +2 -0
  101. package/dist/src/cli/commands/doctor/checks/config-health.js +82 -0
  102. package/dist/src/cli/commands/doctor/checks/config-health.js.map +1 -0
  103. package/dist/src/cli/commands/doctor/checks/cron-health.d.ts +2 -0
  104. package/dist/src/cli/commands/doctor/checks/cron-health.js +116 -0
  105. package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -0
  106. package/dist/src/cli/commands/doctor/checks/gateway-health.d.ts +2 -0
  107. package/dist/src/cli/commands/doctor/checks/gateway-health.js +64 -0
  108. package/dist/src/cli/commands/doctor/checks/gateway-health.js.map +1 -0
  109. package/dist/src/cli/commands/doctor/checks/gateway-service.d.ts +2 -0
  110. package/dist/src/cli/commands/doctor/checks/gateway-service.js +64 -0
  111. package/dist/src/cli/commands/doctor/checks/gateway-service.js.map +1 -0
  112. package/dist/src/cli/commands/doctor/checks/node-version.d.ts +2 -0
  113. package/dist/src/cli/commands/doctor/checks/node-version.js +33 -0
  114. package/dist/src/cli/commands/doctor/checks/node-version.js.map +1 -0
  115. package/dist/src/cli/commands/doctor/checks/provider-auth.d.ts +2 -0
  116. package/dist/src/cli/commands/doctor/checks/provider-auth.js +91 -0
  117. package/dist/src/cli/commands/doctor/checks/provider-auth.js.map +1 -0
  118. package/dist/src/cli/commands/doctor/checks/security-audit.d.ts +2 -0
  119. package/dist/src/cli/commands/doctor/checks/security-audit.js +85 -0
  120. package/dist/src/cli/commands/doctor/checks/security-audit.js.map +1 -0
  121. package/dist/src/cli/commands/doctor/checks/session-integrity.d.ts +2 -0
  122. package/dist/src/cli/commands/doctor/checks/session-integrity.js +118 -0
  123. package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -0
  124. package/dist/src/cli/commands/doctor/checks/state-integrity.d.ts +2 -0
  125. package/dist/src/cli/commands/doctor/checks/state-integrity.js +99 -0
  126. package/dist/src/cli/commands/doctor/checks/state-integrity.js.map +1 -0
  127. package/dist/src/cli/commands/doctor/checks/version-check.d.ts +2 -0
  128. package/dist/src/cli/commands/doctor/checks/version-check.js +71 -0
  129. package/dist/src/cli/commands/doctor/checks/version-check.js.map +1 -0
  130. package/dist/src/cli/commands/doctor/checks/workspace-status.d.ts +2 -0
  131. package/dist/src/cli/commands/doctor/checks/workspace-status.js +73 -0
  132. package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -0
  133. package/dist/src/cli/commands/doctor/flow.d.ts +9 -0
  134. package/dist/src/cli/commands/doctor/flow.js +51 -0
  135. package/dist/src/cli/commands/doctor/flow.js.map +1 -0
  136. package/dist/src/cli/commands/doctor/format.d.ts +6 -0
  137. package/dist/src/cli/commands/doctor/format.js +61 -0
  138. package/dist/src/cli/commands/doctor/format.js.map +1 -0
  139. package/dist/src/cli/commands/doctor/index.js +44 -0
  140. package/dist/src/cli/commands/doctor/index.js.map +1 -0
  141. package/dist/src/cli/commands/doctor/types.d.ts +20 -0
  142. package/dist/src/cli/commands/doctor/types.js +1 -0
  143. package/dist/src/cli/commands/init.js +2 -2
  144. package/dist/src/cli/index.d.ts +1 -1
  145. package/dist/src/cli/index.js +1 -1
  146. package/dist/src/cli/index.js.map +1 -1
  147. package/dist/src/cli/utils/init-workspace.js +1 -1
  148. package/dist/src/config/index.js +3 -3
  149. package/dist/src/config/loader.js +1 -1
  150. package/dist/src/config/models-json.d.ts +15 -15
  151. package/dist/src/config/models-json.js +1 -1
  152. package/dist/src/config/profile.js +1 -1
  153. package/dist/src/config/schema.d.ts +0 -105
  154. package/dist/src/config/schema.js +3 -40
  155. package/dist/src/config/schema.js.map +1 -1
  156. package/dist/src/cron/executor.js +2 -2
  157. package/dist/src/daemon/launchd.js +2 -2
  158. package/dist/src/daemon/launchd.js.map +1 -1
  159. package/dist/src/daemon/systemd.js +2 -2
  160. package/dist/src/daemon/systemd.js.map +1 -1
  161. package/dist/src/extensions/loader.js +1 -1
  162. package/dist/src/gateway/agents-admin.js +1 -1
  163. package/dist/src/gateway/hono/lib/static-ui.js +1 -1
  164. package/dist/src/gateway/hono/routes/doctor.d.ts +3 -0
  165. package/dist/src/gateway/hono/routes/doctor.js +35 -0
  166. package/dist/src/gateway/hono/routes/doctor.js.map +1 -0
  167. package/dist/src/gateway/hono/routes/index.js +2 -0
  168. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  169. package/dist/src/gateway/hono/routes/workspace.js +2 -2
  170. package/dist/src/gateway/hono/sse.js +2 -2
  171. package/dist/src/gateway/lock.js +1 -1
  172. package/dist/src/gateway/ports.js +98 -3
  173. package/dist/src/gateway/ports.js.map +1 -1
  174. package/dist/src/gateway/service.d.ts +0 -4
  175. package/dist/src/gateway/service.js +4 -23
  176. package/dist/src/gateway/service.js.map +1 -1
  177. package/dist/src/routing/bindings.js +1 -1
  178. package/dist/src/routing/index.d.ts +1 -1
  179. package/dist/src/routing/index.js +2 -2
  180. package/dist/src/routing/index.js.map +1 -1
  181. package/dist/src/routing/resolve-route.js +1 -1
  182. package/dist/src/routing/session-key.d.ts +0 -5
  183. package/dist/src/routing/session-key.js +1 -27
  184. package/dist/src/routing/session-key.js.map +1 -1
  185. package/dist/src/session/session-title.js +1 -1
  186. package/dist/src/session/store.js +2 -6
  187. package/dist/src/session/store.js.map +1 -1
  188. package/dist/src/session/types.d.ts +0 -10
  189. package/dist/src/session/types.js.map +1 -1
  190. package/package.json +1 -2
  191. package/dist/gateway/static/root/assets/attachment-load-6pRlDPZ8.js +0 -1
  192. package/dist/gateway/static/root/assets/index-DBZ5eXW5.js +0 -16
  193. package/dist/gateway/static/root/assets/index-DBZ5eXW5.js.map +0 -1
  194. package/dist/gateway/static/root/assets/index-KsVMH-Jo.css +0 -2
  195. package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js +0 -2
  196. package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js.map +0 -1
  197. package/dist/gateway/static/root/assets/page-header-store-HcRZK5CZ.js +0 -2
  198. package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js +0 -2
  199. package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js.map +0 -1
  200. package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js +0 -2
  201. package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js.map +0 -1
  202. package/dist/gateway/static/root/assets/url-QmwQTJ-j.js +0 -2
  203. package/dist/gateway/static/root/assets/url-QmwQTJ-j.js.map +0 -1
  204. package/dist/src/acp/commands.d.ts +0 -11
  205. package/dist/src/acp/commands.js +0 -17
  206. package/dist/src/acp/commands.js.map +0 -1
  207. package/dist/src/acp/control-plane/identity-reconcile.d.ts +0 -36
  208. package/dist/src/acp/control-plane/identity-reconcile.js +0 -124
  209. package/dist/src/acp/control-plane/identity-reconcile.js.map +0 -1
  210. package/dist/src/acp/control-plane/index.d.ts +0 -10
  211. package/dist/src/acp/control-plane/index.js +0 -6
  212. package/dist/src/acp/control-plane/manager.d.ts +0 -86
  213. package/dist/src/acp/control-plane/manager.js +0 -502
  214. package/dist/src/acp/control-plane/manager.js.map +0 -1
  215. package/dist/src/acp/control-plane/manager.types.d.ts +0 -125
  216. package/dist/src/acp/control-plane/manager.types.js +0 -14
  217. package/dist/src/acp/control-plane/manager.types.js.map +0 -1
  218. package/dist/src/acp/control-plane/manager.utils.d.ts +0 -29
  219. package/dist/src/acp/control-plane/manager.utils.js +0 -46
  220. package/dist/src/acp/control-plane/manager.utils.js.map +0 -1
  221. package/dist/src/acp/control-plane/runtime-cache-manager.d.ts +0 -49
  222. package/dist/src/acp/control-plane/runtime-cache-manager.js +0 -155
  223. package/dist/src/acp/control-plane/runtime-cache-manager.js.map +0 -1
  224. package/dist/src/acp/control-plane/runtime-cache.d.ts +0 -45
  225. package/dist/src/acp/control-plane/runtime-cache.js +0 -58
  226. package/dist/src/acp/control-plane/runtime-cache.js.map +0 -1
  227. package/dist/src/acp/control-plane/runtime-options.d.ts +0 -30
  228. package/dist/src/acp/control-plane/runtime-options.js +0 -92
  229. package/dist/src/acp/control-plane/runtime-options.js.map +0 -1
  230. package/dist/src/acp/control-plane/session-actor-queue.d.ts +0 -22
  231. package/dist/src/acp/control-plane/session-actor-queue.js +0 -70
  232. package/dist/src/acp/control-plane/session-actor-queue.js.map +0 -1
  233. package/dist/src/acp/control-plane/session-lifecycle-manager.d.ts +0 -59
  234. package/dist/src/acp/control-plane/session-lifecycle-manager.js +0 -209
  235. package/dist/src/acp/control-plane/session-lifecycle-manager.js.map +0 -1
  236. package/dist/src/acp/control-plane/session-store.d.ts +0 -39
  237. package/dist/src/acp/control-plane/session-store.js +0 -149
  238. package/dist/src/acp/control-plane/session-store.js.map +0 -1
  239. package/dist/src/acp/control-plane/turn-manager.d.ts +0 -40
  240. package/dist/src/acp/control-plane/turn-manager.js +0 -134
  241. package/dist/src/acp/control-plane/turn-manager.js.map +0 -1
  242. package/dist/src/acp/event-mapper.d.ts +0 -48
  243. package/dist/src/acp/event-mapper.js +0 -94
  244. package/dist/src/acp/event-mapper.js.map +0 -1
  245. package/dist/src/acp/index.d.ts +0 -10
  246. package/dist/src/acp/index.js +0 -5
  247. package/dist/src/acp/meta.d.ts +0 -15
  248. package/dist/src/acp/meta.js +0 -36
  249. package/dist/src/acp/meta.js.map +0 -1
  250. package/dist/src/acp/routing-integration.d.ts +0 -37
  251. package/dist/src/acp/routing-integration.js +0 -58
  252. package/dist/src/acp/routing-integration.js.map +0 -1
  253. package/dist/src/acp/runtime/backends/index.d.ts +0 -4
  254. package/dist/src/acp/runtime/backends/index.js +0 -2
  255. package/dist/src/acp/runtime/backends/local.d.ts +0 -136
  256. package/dist/src/acp/runtime/backends/local.js +0 -603
  257. package/dist/src/acp/runtime/backends/local.js.map +0 -1
  258. package/dist/src/acp/runtime/error-text.d.ts +0 -16
  259. package/dist/src/acp/runtime/error-text.js +0 -40
  260. package/dist/src/acp/runtime/error-text.js.map +0 -1
  261. package/dist/src/acp/runtime/errors.d.ts +0 -31
  262. package/dist/src/acp/runtime/errors.js +0 -47
  263. package/dist/src/acp/runtime/errors.js.map +0 -1
  264. package/dist/src/acp/runtime/index.d.ts +0 -7
  265. package/dist/src/acp/runtime/index.js +0 -4
  266. package/dist/src/acp/runtime/registry.d.ts +0 -35
  267. package/dist/src/acp/runtime/registry.js +0 -85
  268. package/dist/src/acp/runtime/registry.js.map +0 -1
  269. package/dist/src/acp/runtime/session-identity.d.ts +0 -35
  270. package/dist/src/acp/runtime/session-identity.js +0 -134
  271. package/dist/src/acp/runtime/session-identity.js.map +0 -1
  272. package/dist/src/acp/runtime/types.d.ts +0 -214
  273. package/dist/src/acp/secret-file.d.ts +0 -7
  274. package/dist/src/acp/secret-file.js +0 -19
  275. package/dist/src/acp/secret-file.js.map +0 -1
  276. package/dist/src/acp/server.d.ts +0 -48
  277. package/dist/src/acp/server.js +0 -300
  278. package/dist/src/acp/server.js.map +0 -1
  279. package/dist/src/acp/session.d.ts +0 -29
  280. package/dist/src/acp/session.js +0 -30
  281. package/dist/src/acp/session.js.map +0 -1
  282. package/dist/src/acp/types.d.ts +0 -39
  283. package/dist/src/acp/types.js +0 -13
  284. package/dist/src/acp/types.js.map +0 -1
  285. package/dist/src/agent/image/describe-images.d.ts +0 -18
  286. package/dist/src/agent/image/describe-images.js +0 -19
  287. package/dist/src/agent/image/describe-images.js.map +0 -1
  288. package/dist/src/cli/commands/acp.d.ts +0 -4
  289. package/dist/src/cli/commands/acp.js +0 -200
  290. package/dist/src/cli/commands/acp.js.map +0 -1
  291. /package/dist/src/{acp/runtime/types.js → cli/commands/doctor/index.d.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","names":["parseRoutingSessionKey"],"sources":["../../../src/session/store.ts"],"sourcesContent":["// Session store - manages session persistence, indexing, compaction, and sliding window\n\nimport { readFile, writeFile, mkdir, unlink, readdir, stat } from 'fs/promises';\nimport { basename, join } from 'path';\nimport { existsSync } from 'fs';\nimport { resolveSessionsDir, FILENAMES } from '../config/paths.js';\nimport { resolveDefaultAgentId } from '../agent/agent-scope.js';\nimport type { Config } from '../config/schema.js';\nimport { resolveSessionShardRelativePath } from './shard-path.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../routing/session-key.js';\nimport type { AgentMessage } from '@mariozechner/pi-agent-core';\nimport { createLogger } from '../utils/logger.js';\nimport type {\n SessionMetadata,\n SessionDetail,\n SessionIndex,\n SessionListQuery,\n PaginatedResult,\n GlobalSessionStats,\n ExportFormat,\n SessionExport,\n} from './types.js';\nimport { SessionStatus } from './types.js';\nimport type { Message } from './types.js';\nimport { SessionCompactor, type CompactionConfig, type CompactionResult } from '../agent/memory/compaction.js';\nimport { SlidingWindow, type WindowConfig } from '../agent/memory/window.js';\nimport { cleanTrailingErrors, hasProblematicMessages } from '../agent/memory/message-sanitizer.js';\nimport { invalidateSessionSearchIndexCache } from './search-index-cache.js';\n\nconst log = createLogger('SessionStore');\n\nconst INDEX_VERSION = '1.0';\nconst DEFAULT_LIMIT = 50;\n\n/**\n * Session files live under `resolveSessionsDir(config, agentId)` (ADR-003), sharded by\n * `resolveSessionShardRelativePath(sessionKey)` (users/… vs system/heartbeat; web UI uses\n * compact `users/{agent}/web/{peerId}` for gateway/webchat direct sessions).\n */\nexport interface SessionStoreOptions {\n /** Loaded app config (required for session path resolution). */\n config: Config;\n /** Agent id for the session store root (default: configured default agent). */\n agentId?: string;\n /** Override storage root (tests); skips `resolveSessionsDir` */\n sessionsDir?: string;\n}\n\nexport class SessionStore {\n private sessionsDir: string;\n private archiveDir: string;\n private indexFile: string;\n private indexCache: SessionIndex | null = null;\n private indexCacheTime: number = 0;\n private indexDirty = false;\n private window: SlidingWindow;\n private compactor: SessionCompactor;\n\n constructor(\n options: SessionStoreOptions,\n windowConfig?: Partial<WindowConfig>,\n compactionConfig?: Partial<CompactionConfig>\n ) {\n const agentId = options.agentId ?? resolveDefaultAgentId(options.config);\n this.sessionsDir = options.sessionsDir ?? resolveSessionsDir(options.config, agentId);\n this.archiveDir = join(this.sessionsDir, 'archive');\n this.indexFile = join(this.sessionsDir, FILENAMES.SESSIONS_INDEX);\n this.window = new SlidingWindow(windowConfig);\n this.compactor = new SessionCompactor(compactionConfig);\n }\n\n /** Root directory of session JSON files (sharded). Used by `session_search` indexing. */\n getSessionsRoot(): string {\n return this.sessionsDir;\n }\n\n // ========== Initialization ==========\n\n async initialize(): Promise<void> {\n await mkdir(this.sessionsDir, { recursive: true });\n await mkdir(this.archiveDir, { recursive: true });\n\n if (!existsSync(this.indexFile)) {\n await this.rebuildIndex();\n } else {\n await this.loadIndex();\n }\n\n log.debug('Session store initialized');\n }\n\n private sessionPathsForKey(key: string): { dir: string; jsonPath: string; metaPath: string } {\n const safeKey = this.sanitizeKey(key);\n const shard = resolveSessionShardRelativePath(key);\n const dir = join(this.sessionsDir, shard);\n return {\n dir,\n jsonPath: join(dir, `${safeKey}.json`),\n metaPath: join(dir, `${safeKey}.meta.json`),\n };\n }\n\n // ========== Index Management ==========\n\n /**\n * Get sessions by agent ID\n */\n async getByAgent(agentId: string): Promise<SessionMetadata[]> {\n const index = await this.loadIndex();\n return (index.sessions || []).filter(\n (s) => s.routing?.agentId?.toLowerCase() === agentId.toLowerCase()\n );\n }\n\n /**\n * Get sessions by account ID\n */\n async getByAccount(accountId: string): Promise<SessionMetadata[]> {\n const index = await this.loadIndex();\n return (index.sessions || []).filter(\n (s) => s.routing?.accountId?.toLowerCase() === accountId.toLowerCase()\n );\n }\n\n /**\n * Get sessions by peer\n */\n async getByPeer(peerKind: string, peerId: string): Promise<SessionMetadata[]> {\n const index = await this.loadIndex();\n return (index.sessions || []).filter(\n (s) =>\n s.routing?.peerKind?.toLowerCase() === peerKind.toLowerCase() &&\n s.routing?.peerId?.toLowerCase() === peerId.toLowerCase()\n );\n }\n\n /**\n * Get main session for a DM conversation\n */\n async getMainSession(channel: string, accountId: string): Promise<SessionMetadata | null> {\n const index = await this.loadIndex();\n return (\n (index.sessions || []).find(\n (s) =>\n s.routing?.source?.toLowerCase() === channel.toLowerCase() &&\n s.routing?.accountId?.toLowerCase() === accountId.toLowerCase() &&\n s.routing?.peerKind?.toLowerCase() === 'dm' &&\n s.routing?.peerId === 'main'\n ) ?? null\n );\n }\n\n private async loadIndex(): Promise<SessionIndex> {\n try {\n // Check if index file has been modified\n const stats = await stat(this.indexFile);\n const mtime = stats.mtime.getTime();\n\n // If cache is valid and file hasn't changed, use cache\n if (this.indexCache && mtime <= this.indexCacheTime) {\n // Ensure sessions array exists\n if (!this.indexCache.sessions) {\n this.indexCache.sessions = [];\n }\n return this.indexCache;\n }\n\n // File has changed or cache is empty, reload\n const data = await readFile(this.indexFile, 'utf-8');\n const parsed = JSON.parse(data) as SessionIndex;\n // Ensure sessions array exists\n if (!parsed.sessions) {\n parsed.sessions = [];\n }\n this.indexCache = parsed;\n this.indexCacheTime = mtime;\n return this.indexCache;\n } catch {\n // Index corrupted or missing, rebuild\n return this.rebuildIndex();\n }\n }\n\n /**\n * Force refresh the index cache from disk\n */\n async refreshIndex(): Promise<void> {\n this.indexCache = null;\n this.indexCacheTime = 0;\n await this.loadIndex();\n }\n\n private async saveIndex(): Promise<void> {\n if (!this.indexCache) return;\n\n this.indexCache.lastUpdated = new Date().toISOString();\n await writeFile(this.indexFile, JSON.stringify(this.indexCache, null, 2));\n this.indexDirty = false;\n \n // Update cache time after saving\n try {\n const stats = await stat(this.indexFile);\n this.indexCacheTime = stats.mtime.getTime();\n } catch {\n this.indexCacheTime = Date.now();\n }\n }\n\n private async rebuildIndex(): Promise<SessionIndex> {\n log.info('Rebuilding session index...');\n\n const sessions: SessionMetadata[] = [];\n\n // Scan sessions directory\n const files = await this.scanSessionFiles();\n\n for (const file of files) {\n if (file.endsWith('.json') && !file.endsWith('.meta.json')) {\n const stem = basename(file, '.json');\n const key = this.fileNameToKey(stem);\n try {\n const metadata = await this.scanSessionFile(key);\n if (metadata) {\n sessions.push(metadata);\n }\n } catch (err) {\n log.warn({ key, err }, 'Failed to scan session file');\n }\n }\n }\n\n this.indexCache = {\n version: INDEX_VERSION,\n lastUpdated: new Date().toISOString(),\n sessions,\n };\n\n await this.saveIndex();\n \n // Update cache time after saving\n try {\n const stats = await stat(this.indexFile);\n this.indexCacheTime = stats.mtime.getTime();\n } catch {\n this.indexCacheTime = Date.now();\n }\n \n log.info({ count: sessions.length }, 'Session index rebuilt');\n\n return this.indexCache;\n }\n\n private async scanSessionFiles(): Promise<string[]> {\n const out: string[] = [];\n const walk = async (rel: string): Promise<void> => {\n const abs = join(this.sessionsDir, rel);\n let entries;\n try {\n entries = await readdir(abs, { withFileTypes: true });\n } catch {\n return;\n }\n for (const ent of entries) {\n const childRel = rel ? join(rel, ent.name) : ent.name;\n if (ent.isDirectory()) {\n if (ent.name === 'archive') continue;\n await walk(childRel);\n } else if (\n ent.name.endsWith('.json') &&\n ent.name !== FILENAMES.SESSIONS_INDEX &&\n !ent.name.endsWith('.meta.json')\n ) {\n out.push(childRel);\n }\n }\n };\n await walk('');\n return out;\n }\n\n private async scanSessionFile(key: string): Promise<SessionMetadata | null> {\n const messages = await this.loadMessages(key);\n if (messages.length === 0) return null;\n\n const { jsonPath } = this.sessionPathsForKey(key);\n const stats = await stat(jsonPath);\n\n const { channel, chatId } = this.parseSessionKey(key);\n const routing = this.extractRoutingFromKey(key, channel);\n const isCronSession = channel === 'cron';\n const isHeartbeatSession = channel === 'heartbeat';\n\n return {\n key,\n status: SessionStatus.ACTIVE,\n tags: [],\n createdAt: stats.birthtime.toISOString(),\n updatedAt: stats.mtime.toISOString(),\n lastAccessedAt: stats.mtime.toISOString(),\n messageCount: messages.length,\n estimatedTokens: this.estimateTokens(messages),\n compactedCount: 0,\n sourceChannel: channel,\n sourceChatId: chatId,\n routing,\n ...(isCronSession\n ? {\n sessionType: 'cron',\n customData: { cronJobId: chatId },\n }\n : {}),\n ...(isHeartbeatSession\n ? {\n sessionType: 'heartbeat',\n customData: { heartbeatTarget: chatId },\n }\n : {}),\n stats: {\n messageCount: messages.length,\n tokenCount: this.estimateTokens(messages),\n },\n };\n }\n\n /**\n * Extract routing metadata from session key\n */\n private extractRoutingFromKey(key: string, channel: string): SessionMetadata['routing'] {\n const parts = key.split(':');\n if (parts.length < 5) {\n return undefined;\n }\n\n const [agentId, source, accountId, peerKind, peerId, ...rest] = parts;\n \n let threadId: string | undefined;\n let scopeId: string | undefined;\n \n // Parse optional thread and scope\n for (let i = 0; i < rest.length; i++) {\n if (rest[i] === 'thread' && rest[i + 1]) {\n threadId = rest[i + 1];\n i++;\n } else if (rest[i] === 'scope' && rest[i + 1]) {\n scopeId = rest[i + 1];\n i++;\n }\n }\n\n return {\n agentId: agentId?.toLowerCase() || 'main',\n source: source?.toLowerCase() || channel,\n accountId: accountId?.toLowerCase() || 'default',\n peerKind: peerKind?.toLowerCase() || 'dm',\n peerId: peerId?.toLowerCase() || 'unknown',\n threadId,\n scopeId,\n };\n }\n\n // ========== CRUD Operations ==========\n\n async list(query: SessionListQuery = {}): Promise<PaginatedResult<SessionMetadata>> {\n const index = await this.loadIndex();\n let sessions = [...(index.sessions || [])];\n\n // Apply filters\n if (query.status) {\n const statuses = Array.isArray(query.status) ? query.status : [query.status];\n sessions = sessions.filter((s) => statuses.includes(s.status));\n }\n\n if (query.channel) {\n const channels = query.channel\n .split(',')\n .map((c) => c.trim())\n .filter(Boolean);\n if (channels.length === 0) {\n sessions = [];\n } else if (channels.length === 1) {\n sessions = sessions.filter((s) => s.sourceChannel === channels[0]);\n } else {\n sessions = sessions.filter((s) => channels.includes(s.sourceChannel));\n }\n }\n\n if (query.tags && query.tags.length > 0) {\n sessions = sessions.filter((s) => query.tags!.some((tag) => s.tags.includes(tag)));\n }\n\n if (query.search) {\n const searchLower = query.search.toLowerCase();\n sessions = sessions.filter(\n (s) =>\n s.key.toLowerCase().includes(searchLower) ||\n s.name?.toLowerCase().includes(searchLower) ||\n s.tags.some((t) => t.toLowerCase().includes(searchLower))\n );\n }\n\n // Apply sorting\n const sortBy = query.sortBy || 'updatedAt';\n const sortOrder = query.sortOrder || 'desc';\n\n sessions.sort((a, b) => {\n const aVal = a[sortBy];\n const bVal = b[sortBy];\n const comparison = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;\n return sortOrder === 'asc' ? comparison : -comparison;\n });\n\n // Apply pagination\n const total = sessions.length;\n const limit = query.limit || DEFAULT_LIMIT;\n const offset = query.offset || 0;\n const items = sessions.slice(offset, offset + limit);\n\n return {\n items,\n total,\n limit,\n offset,\n hasMore: offset + limit < total,\n };\n }\n\n async get(key: string): Promise<SessionDetail | null> {\n const metadata = await this.getMetadata(key);\n if (!metadata) return null;\n\n const messages = await this.loadMessages(key);\n\n return {\n ...metadata,\n messages: this.convertMessages(messages),\n };\n }\n\n async getMetadata(key: string): Promise<SessionMetadata | null> {\n const index = await this.loadIndex();\n const metadata = index.sessions.find((s) => s.key === key);\n\n if (!metadata) {\n // Try to load from file directly (orphaned session)\n const scanned = await this.scanSessionFile(key);\n if (scanned) {\n index.sessions.push(scanned);\n this.indexDirty = true;\n return scanned;\n }\n return null;\n }\n\n return metadata;\n }\n\n async updateMetadata(key: string, updates: Partial<SessionMetadata>): Promise<void> {\n const index = await this.loadIndex();\n const idx = index.sessions.findIndex((s) => s.key === key);\n\n if (idx === -1) {\n throw new Error(`Session not found: ${key}`);\n }\n\n index.sessions[idx] = {\n ...index.sessions[idx],\n ...updates,\n updatedAt: new Date().toISOString(),\n };\n\n this.indexDirty = true;\n await this.saveIndex();\n\n log.debug({ key, updates }, 'Session metadata updated');\n }\n\n async delete(key: string): Promise<boolean> {\n const index = await this.loadIndex();\n const idx = index.sessions.findIndex((s) => s.key === key);\n\n const primary = this.sessionPathsForKey(key);\n\n for (const p of [primary.jsonPath]) {\n try {\n await unlink(p);\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n for (const p of [primary.metaPath]) {\n try {\n await unlink(p);\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n\n // Remove from index\n if (idx !== -1) {\n index.sessions.splice(idx, 1);\n this.indexDirty = true;\n await this.saveIndex();\n }\n\n log.info({ key }, 'Session deleted');\n return true;\n }\n\n async deleteMany(keys: string[]): Promise<{ success: string[]; failed: string[] }> {\n const success: string[] = [];\n const failed: string[] = [];\n\n for (const key of keys) {\n try {\n await this.delete(key);\n success.push(key);\n } catch {\n failed.push(key);\n }\n }\n\n return { success, failed };\n }\n\n // ========== Status Operations ==========\n\n async setStatus(key: string, status: SessionStatus): Promise<void> {\n await this.updateMetadata(key, { status });\n\n if (status === SessionStatus.ARCHIVED) {\n await this.moveToArchive(key);\n } else {\n await this.moveFromArchive(key);\n }\n }\n\n async archive(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ARCHIVED);\n }\n\n async unarchive(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ACTIVE);\n }\n\n async pin(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.PINNED);\n }\n\n async unpin(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ACTIVE);\n }\n\n // ========== Message Operations ==========\n\n async loadMessages(key: string, options?: { fromArchive?: boolean }): Promise<AgentMessage[]> {\n const primary = this.sessionPathsForKey(key);\n\n const readAndNormalize = async (path: string): Promise<AgentMessage[] | null> => {\n try {\n const data = await readFile(path, 'utf-8');\n const messages = JSON.parse(data) as AgentMessage[];\n if (hasProblematicMessages(messages)) {\n const cleaned = cleanTrailingErrors(messages);\n if (cleaned.length !== messages.length) {\n log.info(\n { key, original: messages.length, cleaned: cleaned.length },\n 'Cleaned problematic messages on load'\n );\n }\n return cleaned;\n }\n return messages;\n } catch {\n return null;\n }\n };\n\n const messages = await readAndNormalize(primary.jsonPath);\n\n if (messages !== null) {\n return messages;\n }\n\n if (options?.fromArchive) {\n const archivedFile = await this.findMostRecentArchive(key);\n if (!archivedFile) {\n return [];\n }\n const archived = await readAndNormalize(archivedFile);\n return archived ?? [];\n }\n return [];\n }\n\n /**\n * Find the most recent archived session file for a given key.\n * Archived files have format: {safeKey}.{timestamp}.json\n */\n private async findMostRecentArchive(sessionKey: string): Promise<string | null> {\n const safeKey = this.sanitizeKey(sessionKey);\n const shardDir = join(this.archiveDir, resolveSessionShardRelativePath(sessionKey));\n\n const scanDir = async (dir: string): Promise<string | null> => {\n try {\n const files = await readdir(dir);\n const matchingFiles = files\n .filter((f) => f.startsWith(`${safeKey}.`) && f.endsWith('.json') && !f.endsWith('.meta.json'))\n .sort()\n .reverse();\n if (matchingFiles.length === 0) return null;\n return join(dir, matchingFiles[0]);\n } catch {\n return null;\n }\n };\n\n const inShard = await scanDir(shardDir);\n if (inShard) return inShard;\n return await scanDir(this.archiveDir);\n }\n\n async saveMessages(key: string, messages: AgentMessage[]): Promise<void> {\n const { dir, jsonPath } = this.sessionPathsForKey(key);\n\n await mkdir(dir, { recursive: true });\n await writeFile(jsonPath, JSON.stringify(messages, null, 2));\n\n // Update or create metadata\n const index = await this.loadIndex();\n const existingIdx = index.sessions.findIndex((s) => s.key === key);\n const now = new Date().toISOString();\n\n const { channel, chatId } = this.parseSessionKey(key);\n const routing = this.extractRoutingFromKey(key, channel);\n const isCronSession = channel === 'cron';\n const isHeartbeatSession = channel === 'heartbeat';\n\n if (existingIdx !== -1) {\n const prev = index.sessions[existingIdx];\n index.sessions[existingIdx] = {\n ...prev,\n sourceChannel: channel,\n sourceChatId: chatId,\n messageCount: messages.length,\n estimatedTokens: this.estimateTokens(messages),\n updatedAt: now,\n lastAccessedAt: now,\n routing: routing || prev.routing,\n ...(isCronSession\n ? {\n sessionType: 'cron',\n customData: {\n ...prev.customData,\n cronJobId: chatId,\n },\n }\n : {}),\n ...(isHeartbeatSession\n ? {\n sessionType: 'heartbeat',\n customData: {\n ...prev.customData,\n heartbeatTarget: chatId,\n },\n }\n : {}),\n stats: {\n ...prev.stats,\n messageCount: messages.length,\n tokenCount: this.estimateTokens(messages),\n lastTurnAt: Date.now(),\n },\n };\n } else {\n index.sessions.push({\n key,\n status: SessionStatus.ACTIVE,\n tags: [],\n createdAt: now,\n updatedAt: now,\n lastAccessedAt: now,\n messageCount: messages.length,\n estimatedTokens: this.estimateTokens(messages),\n compactedCount: 0,\n sourceChannel: channel,\n sourceChatId: chatId,\n routing,\n ...(isCronSession\n ? {\n sessionType: 'cron',\n customData: { cronJobId: chatId },\n }\n : {}),\n ...(isHeartbeatSession\n ? {\n sessionType: 'heartbeat',\n customData: { heartbeatTarget: chatId },\n }\n : {}),\n stats: {\n messageCount: messages.length,\n tokenCount: this.estimateTokens(messages),\n lastTurnAt: Date.now(),\n },\n });\n }\n\n this.indexDirty = true;\n await this.saveIndex();\n\n invalidateSessionSearchIndexCache();\n }\n\n // ========== Sliding Window & Compaction ==========\n\n /**\n * Get window stats for messages\n */\n getWindowStats(messages: AgentMessage[]) {\n return this.window.getStats(messages);\n }\n\n /**\n * Check if session needs compaction\n */\n needsCompaction(key: string, messages: AgentMessage[], contextWindow: number) {\n return this.compactor.needsCompaction(messages, contextWindow);\n }\n\n /**\n * Prepare compaction (check if needed)\n */\n prepareCompaction(\n key: string,\n messages: AgentMessage[],\n contextWindow: number\n ): { needsCompaction: boolean; messages: AgentMessage[]; stats?: ReturnType<typeof this.compactor.needsCompaction> } {\n const result = this.compactor.needsCompaction(messages, contextWindow);\n return {\n needsCompaction: result.needed,\n messages,\n stats: result,\n };\n }\n\n /**\n * Apply compaction result to messages\n */\n async applyCompaction(\n key: string,\n messages: AgentMessage[],\n result: CompactionResult\n ): Promise<AgentMessage[]> {\n const compacted = this.compactor.applyCompaction(messages, result);\n \n // Persist the compacted messages to disk so subsequent loads see the reduced context\n await this.saveMessages(key, compacted);\n \n const metadata = await this.getMetadata(key);\n if (metadata) {\n await this.updateMetadata(key, {\n compactedCount: metadata.compactedCount + 1,\n });\n }\n \n log.info({\n key,\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n keptMessages: compacted.length,\n }, 'Session compacted');\n \n return compacted;\n }\n\n /**\n * Compact session with LLM summary\n */\n async compact(\n key: string,\n messages: AgentMessage[],\n contextWindow: number,\n instructions?: string\n ): Promise<CompactionResult> {\n const result = await this.compactor.compact(messages, instructions);\n \n if (result.compacted) {\n await this.applyCompaction(key, messages, result);\n }\n \n return result;\n }\n\n /**\n * Get compaction stats for a session\n */\n async getCompactionStats(key: string) {\n const metadata = await this.getMetadata(key);\n if (!metadata) return undefined;\n \n return {\n compactionCount: metadata.compactedCount,\n totalTokensBefore: 0,\n totalTokensAfter: 0,\n lastCompactionAt: undefined,\n };\n }\n\n // ========== MemoryStore API Aliases ==========\n\n /** Alias for delete */\n async deleteSession(key: string): Promise<boolean> {\n return this.delete(key);\n }\n\n /** Alias for loadMessages */\n async load(key: string, options?: { fromArchive?: boolean }): Promise<AgentMessage[]> {\n return this.loadMessages(key, options);\n }\n\n /** Alias for saveMessages */\n async save(key: string, messages: AgentMessage[]): Promise<void> {\n return this.saveMessages(key, messages);\n }\n\n /** Alias for estimateTokens */\n async estimateTokenUsage(key: string, messages: AgentMessage[]): Promise<number> {\n return this.estimateTokens(messages);\n }\n\n // ========== Search ==========\n\n async searchInSession(key: string, keyword: string): Promise<Message[]> {\n const messages = await this.loadMessages(key);\n const keywordLower = keyword.toLowerCase();\n\n return this.convertMessages(\n messages.filter((m) => {\n const content = this.extractTextContent(m.content);\n return content.toLowerCase().includes(keywordLower);\n })\n );\n }\n\n // ========== Export/Import ==========\n\n async exportSession(key: string, format: ExportFormat): Promise<string> {\n const detail = await this.get(key);\n if (!detail) {\n throw new Error(`Session not found: ${key}`);\n }\n\n if (format === 'json') {\n const exportData: SessionExport = {\n version: INDEX_VERSION,\n exportedAt: new Date().toISOString(),\n metadata: detail,\n messages: detail.messages,\n };\n return JSON.stringify(exportData, null, 2);\n } else {\n // Markdown format\n const lines = [\n `# ${detail.name || detail.key}`,\n '',\n `- **Channel:** ${detail.sourceChannel}`,\n `- **Created:** ${detail.createdAt}`,\n `- **Messages:** ${detail.messageCount}`,\n `- **Tags:** ${detail.tags.join(', ') || 'none'}`,\n '',\n '---',\n '',\n ];\n\n for (const msg of detail.messages) {\n const role = msg.role === 'assistant' ? 'Assistant' : msg.role === 'user' ? 'User' : msg.role;\n lines.push(`## ${role}`);\n lines.push('');\n const body =\n typeof msg.content === 'string'\n ? msg.content\n : JSON.stringify(msg.content, null, 2);\n lines.push(body);\n lines.push('');\n lines.push('---');\n lines.push('');\n }\n\n return lines.join('\\n');\n }\n }\n\n // ========== Statistics ==========\n\n async getStats(): Promise<GlobalSessionStats> {\n const index = await this.loadIndex();\n const sessions = index.sessions;\n\n const byChannel: Record<string, number> = {};\n for (const s of sessions) {\n byChannel[s.sourceChannel] = (byChannel[s.sourceChannel] || 0) + 1;\n }\n\n let oldestSession: string | undefined;\n let newestSession: string | undefined;\n\n if (sessions.length > 0) {\n const sorted = [...sessions].sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n oldestSession = sorted[0].createdAt;\n newestSession = sorted[sorted.length - 1].createdAt;\n }\n\n return {\n totalSessions: sessions.length,\n activeSessions: sessions.filter((s) => s.status === SessionStatus.ACTIVE || s.status === SessionStatus.IDLE).length,\n archivedSessions: sessions.filter((s) => s.status === SessionStatus.ARCHIVED).length,\n pinnedSessions: sessions.filter((s) => s.status === SessionStatus.PINNED).length,\n totalMessages: sessions.reduce((sum, s) => sum + s.messageCount, 0),\n totalTokens: sessions.reduce((sum, s) => sum + s.estimatedTokens, 0),\n oldestSession,\n newestSession,\n byChannel,\n };\n }\n\n // ========== Cleanup ==========\n\n async archiveOld(olderThanDays: number): Promise<number> {\n const cutoff = new Date();\n cutoff.setDate(cutoff.getDate() - olderThanDays);\n\n const index = await this.loadIndex();\n let archived = 0;\n\n for (const session of index.sessions) {\n if (session.status !== SessionStatus.ARCHIVED && session.status !== SessionStatus.PINNED) {\n const lastAccess = new Date(session.lastAccessedAt);\n if (lastAccess < cutoff) {\n await this.archive(session.key);\n archived++;\n }\n }\n }\n\n return archived;\n }\n\n // ========== Helper Methods ==========\n\n private sanitizeKey(key: string): string {\n return key.replace(/[^a-zA-Z0-9_-]/g, '_');\n }\n\n private fileNameToKey(fileName: string): string {\n // Reverse of sanitizeKey - restore all colons from underscores\n // telegram_dm_123456 -> telegram:dm:123456\n // telegram_g_-100123456_t_789 -> telegram:g:-100123456:t:789\n return fileName.replace(/_/g, ':');\n }\n\n private parseSessionKey(key: string): { channel: string; chatId: string } {\n const parts = key.split(':');\n // Session key format: {agentId}:{source}:{accountId}:{peerKind}:{peerId}\n if (parts.length >= 5) {\n const parsed = parseRoutingSessionKey(key);\n if (parsed?.source === 'cron') {\n return { channel: 'cron', chatId: parsed.peerId };\n }\n return { channel: parts[1], chatId: parts.slice(2).join(':') };\n }\n // ACP session key format: {agentId}:acp:{uuid}\n if (parts.length === 3 && parts[1] === 'acp') {\n return { channel: 'acp', chatId: parts[2] };\n }\n // Gateway heartbeat: `heartbeat:main` / `heartbeat:isolated:<ts>`\n if (parts.length >= 2 && parts[0] === 'heartbeat') {\n return { channel: 'heartbeat', chatId: parts.slice(1).join(':') };\n }\n return { channel: 'unknown', chatId: key };\n }\n\n estimateTokens(messages: AgentMessage[]): number {\n // Rough estimate: 1 token ≈ 4 characters\n let total = 0;\n for (const msg of messages) {\n const text = this.extractTextContent(msg.content);\n total += Math.ceil(text.length / 4);\n }\n return total;\n }\n\n private extractTextContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n const parts: string[] = [];\n for (const item of content) {\n if (typeof item !== 'object' || item === null || !('type' in item)) continue;\n const c = item as { type?: string; text?: string; name?: string };\n if (c.type === 'text' && typeof c.text === 'string') {\n parts.push(c.text);\n } else if (c.type === 'toolCall' || c.type === 'tool_use') {\n parts.push(c.name ? `[${c.name}]` : '');\n }\n }\n return parts.join('');\n }\n return '';\n }\n\n private convertMessages(messages: AgentMessage[]): Message[] {\n return messages.map((m: any) => {\n const c = m.content;\n const content: string | unknown[] =\n typeof c === 'string'\n ? c\n : Array.isArray(c)\n ? c\n : this.extractTextContent(c);\n\n const row: Message = {\n role: m.role as 'system' | 'user' | 'assistant' | 'tool' | 'toolResult',\n content,\n timestamp: m.timestamp ? new Date(m.timestamp).toISOString() : undefined,\n tool_call_id: m.tool_call_id || m.toolCallId,\n tool_calls: m.tool_calls,\n name: m.name,\n };\n if (Array.isArray(m.attachments) && m.attachments.length > 0) {\n row.attachments = m.attachments;\n }\n return row;\n });\n }\n\n private async moveToArchive(key: string): Promise<void> {\n const safeKey = this.sanitizeKey(key);\n const primary = this.sessionPathsForKey(key);\n const sourcePath = existsSync(primary.jsonPath) ? primary.jsonPath : null;\n if (!sourcePath) {\n return;\n }\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const archiveShard = join(this.archiveDir, resolveSessionShardRelativePath(key));\n await mkdir(archiveShard, { recursive: true });\n const targetPath = join(archiveShard, `${safeKey}.${timestamp}.json`);\n\n try {\n const data = await readFile(sourcePath, 'utf-8');\n await writeFile(targetPath, data);\n await unlink(sourcePath);\n\n const metaSource = primary.metaPath;\n const metaTarget = join(archiveShard, `${safeKey}.${timestamp}.meta.json`);\n try {\n const metaData = await readFile(metaSource, 'utf-8');\n await writeFile(metaTarget, metaData);\n await unlink(metaSource);\n } catch {\n // Meta file might not exist\n }\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n\n private async moveFromArchive(key: string): Promise<void> {\n const sourcePath = await this.findMostRecentArchive(key);\n if (!sourcePath) {\n return;\n }\n\n const primary = this.sessionPathsForKey(key);\n await mkdir(primary.dir, { recursive: true });\n const targetPath = primary.jsonPath;\n\n try {\n const data = await readFile(sourcePath, 'utf-8');\n await writeFile(targetPath, data);\n await unlink(sourcePath);\n\n const metaSource = sourcePath.replace('.json', '.meta.json');\n const metaTarget = primary.metaPath;\n try {\n const metaData = await readFile(metaSource, 'utf-8');\n await writeFile(metaTarget, metaData);\n await unlink(metaSource);\n } catch {\n // Meta file might not exist\n }\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"store.js","names":["parseRoutingSessionKey"],"sources":["../../../src/session/store.ts"],"sourcesContent":["// Session store - manages session persistence, indexing, compaction, and sliding window\n\nimport { readFile, writeFile, mkdir, unlink, readdir, stat } from 'fs/promises';\nimport { basename, join } from 'path';\nimport { existsSync } from 'fs';\nimport { resolveSessionsDir, FILENAMES } from '../config/paths.js';\nimport { resolveDefaultAgentId } from '../agent/agent-scope.js';\nimport type { Config } from '../config/schema.js';\nimport { resolveSessionShardRelativePath } from './shard-path.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../routing/session-key.js';\nimport type { AgentMessage } from '@mariozechner/pi-agent-core';\nimport { createLogger } from '../utils/logger.js';\nimport type {\n SessionMetadata,\n SessionDetail,\n SessionIndex,\n SessionListQuery,\n PaginatedResult,\n GlobalSessionStats,\n ExportFormat,\n SessionExport,\n} from './types.js';\nimport { SessionStatus } from './types.js';\nimport type { Message } from './types.js';\nimport { SessionCompactor, type CompactionConfig, type CompactionResult } from '../agent/memory/compaction.js';\nimport { SlidingWindow, type WindowConfig } from '../agent/memory/window.js';\nimport { cleanTrailingErrors, hasProblematicMessages } from '../agent/memory/message-sanitizer.js';\nimport { invalidateSessionSearchIndexCache } from './search-index-cache.js';\n\nconst log = createLogger('SessionStore');\n\nconst INDEX_VERSION = '1.0';\nconst DEFAULT_LIMIT = 50;\n\n/**\n * Session files live under `resolveSessionsDir(config, agentId)` (ADR-003), sharded by\n * `resolveSessionShardRelativePath(sessionKey)` (users/… vs system/heartbeat; web UI uses\n * compact `users/{agent}/web/{peerId}` for gateway/webchat direct sessions).\n */\nexport interface SessionStoreOptions {\n /** Loaded app config (required for session path resolution). */\n config: Config;\n /** Agent id for the session store root (default: configured default agent). */\n agentId?: string;\n /** Override storage root (tests); skips `resolveSessionsDir` */\n sessionsDir?: string;\n}\n\nexport class SessionStore {\n private sessionsDir: string;\n private archiveDir: string;\n private indexFile: string;\n private indexCache: SessionIndex | null = null;\n private indexCacheTime: number = 0;\n private indexDirty = false;\n private window: SlidingWindow;\n private compactor: SessionCompactor;\n\n constructor(\n options: SessionStoreOptions,\n windowConfig?: Partial<WindowConfig>,\n compactionConfig?: Partial<CompactionConfig>\n ) {\n const agentId = options.agentId ?? resolveDefaultAgentId(options.config);\n this.sessionsDir = options.sessionsDir ?? resolveSessionsDir(options.config, agentId);\n this.archiveDir = join(this.sessionsDir, 'archive');\n this.indexFile = join(this.sessionsDir, FILENAMES.SESSIONS_INDEX);\n this.window = new SlidingWindow(windowConfig);\n this.compactor = new SessionCompactor(compactionConfig);\n }\n\n /** Root directory of session JSON files (sharded). Used by `session_search` indexing. */\n getSessionsRoot(): string {\n return this.sessionsDir;\n }\n\n // ========== Initialization ==========\n\n async initialize(): Promise<void> {\n await mkdir(this.sessionsDir, { recursive: true });\n await mkdir(this.archiveDir, { recursive: true });\n\n if (!existsSync(this.indexFile)) {\n await this.rebuildIndex();\n } else {\n await this.loadIndex();\n }\n\n log.debug('Session store initialized');\n }\n\n private sessionPathsForKey(key: string): { dir: string; jsonPath: string; metaPath: string } {\n const safeKey = this.sanitizeKey(key);\n const shard = resolveSessionShardRelativePath(key);\n const dir = join(this.sessionsDir, shard);\n return {\n dir,\n jsonPath: join(dir, `${safeKey}.json`),\n metaPath: join(dir, `${safeKey}.meta.json`),\n };\n }\n\n // ========== Index Management ==========\n\n /**\n * Get sessions by agent ID\n */\n async getByAgent(agentId: string): Promise<SessionMetadata[]> {\n const index = await this.loadIndex();\n return (index.sessions || []).filter(\n (s) => s.routing?.agentId?.toLowerCase() === agentId.toLowerCase()\n );\n }\n\n /**\n * Get sessions by account ID\n */\n async getByAccount(accountId: string): Promise<SessionMetadata[]> {\n const index = await this.loadIndex();\n return (index.sessions || []).filter(\n (s) => s.routing?.accountId?.toLowerCase() === accountId.toLowerCase()\n );\n }\n\n /**\n * Get sessions by peer\n */\n async getByPeer(peerKind: string, peerId: string): Promise<SessionMetadata[]> {\n const index = await this.loadIndex();\n return (index.sessions || []).filter(\n (s) =>\n s.routing?.peerKind?.toLowerCase() === peerKind.toLowerCase() &&\n s.routing?.peerId?.toLowerCase() === peerId.toLowerCase()\n );\n }\n\n /**\n * Get main session for a DM conversation\n */\n async getMainSession(channel: string, accountId: string): Promise<SessionMetadata | null> {\n const index = await this.loadIndex();\n return (\n (index.sessions || []).find(\n (s) =>\n s.routing?.source?.toLowerCase() === channel.toLowerCase() &&\n s.routing?.accountId?.toLowerCase() === accountId.toLowerCase() &&\n s.routing?.peerKind?.toLowerCase() === 'dm' &&\n s.routing?.peerId === 'main'\n ) ?? null\n );\n }\n\n private async loadIndex(): Promise<SessionIndex> {\n try {\n // Check if index file has been modified\n const stats = await stat(this.indexFile);\n const mtime = stats.mtime.getTime();\n\n // If cache is valid and file hasn't changed, use cache\n if (this.indexCache && mtime <= this.indexCacheTime) {\n // Ensure sessions array exists\n if (!this.indexCache.sessions) {\n this.indexCache.sessions = [];\n }\n return this.indexCache;\n }\n\n // File has changed or cache is empty, reload\n const data = await readFile(this.indexFile, 'utf-8');\n const parsed = JSON.parse(data) as SessionIndex;\n // Ensure sessions array exists\n if (!parsed.sessions) {\n parsed.sessions = [];\n }\n this.indexCache = parsed;\n this.indexCacheTime = mtime;\n return this.indexCache;\n } catch {\n // Index corrupted or missing, rebuild\n return this.rebuildIndex();\n }\n }\n\n /**\n * Force refresh the index cache from disk\n */\n async refreshIndex(): Promise<void> {\n this.indexCache = null;\n this.indexCacheTime = 0;\n await this.loadIndex();\n }\n\n private async saveIndex(): Promise<void> {\n if (!this.indexCache) return;\n\n this.indexCache.lastUpdated = new Date().toISOString();\n await writeFile(this.indexFile, JSON.stringify(this.indexCache, null, 2));\n this.indexDirty = false;\n \n // Update cache time after saving\n try {\n const stats = await stat(this.indexFile);\n this.indexCacheTime = stats.mtime.getTime();\n } catch {\n this.indexCacheTime = Date.now();\n }\n }\n\n private async rebuildIndex(): Promise<SessionIndex> {\n log.info('Rebuilding session index...');\n\n const sessions: SessionMetadata[] = [];\n\n // Scan sessions directory\n const files = await this.scanSessionFiles();\n\n for (const file of files) {\n if (file.endsWith('.json') && !file.endsWith('.meta.json')) {\n const stem = basename(file, '.json');\n const key = this.fileNameToKey(stem);\n try {\n const metadata = await this.scanSessionFile(key);\n if (metadata) {\n sessions.push(metadata);\n }\n } catch (err) {\n log.warn({ key, err }, 'Failed to scan session file');\n }\n }\n }\n\n this.indexCache = {\n version: INDEX_VERSION,\n lastUpdated: new Date().toISOString(),\n sessions,\n };\n\n await this.saveIndex();\n \n // Update cache time after saving\n try {\n const stats = await stat(this.indexFile);\n this.indexCacheTime = stats.mtime.getTime();\n } catch {\n this.indexCacheTime = Date.now();\n }\n \n log.info({ count: sessions.length }, 'Session index rebuilt');\n\n return this.indexCache;\n }\n\n private async scanSessionFiles(): Promise<string[]> {\n const out: string[] = [];\n const walk = async (rel: string): Promise<void> => {\n const abs = join(this.sessionsDir, rel);\n let entries;\n try {\n entries = await readdir(abs, { withFileTypes: true });\n } catch {\n return;\n }\n for (const ent of entries) {\n const childRel = rel ? join(rel, ent.name) : ent.name;\n if (ent.isDirectory()) {\n if (ent.name === 'archive') continue;\n await walk(childRel);\n } else if (\n ent.name.endsWith('.json') &&\n ent.name !== FILENAMES.SESSIONS_INDEX &&\n !ent.name.endsWith('.meta.json')\n ) {\n out.push(childRel);\n }\n }\n };\n await walk('');\n return out;\n }\n\n private async scanSessionFile(key: string): Promise<SessionMetadata | null> {\n const messages = await this.loadMessages(key);\n if (messages.length === 0) return null;\n\n const { jsonPath } = this.sessionPathsForKey(key);\n const stats = await stat(jsonPath);\n\n const { channel, chatId } = this.parseSessionKey(key);\n const routing = this.extractRoutingFromKey(key, channel);\n const isCronSession = channel === 'cron';\n const isHeartbeatSession = channel === 'heartbeat';\n\n return {\n key,\n status: SessionStatus.ACTIVE,\n tags: [],\n createdAt: stats.birthtime.toISOString(),\n updatedAt: stats.mtime.toISOString(),\n lastAccessedAt: stats.mtime.toISOString(),\n messageCount: messages.length,\n estimatedTokens: this.estimateTokens(messages),\n compactedCount: 0,\n sourceChannel: channel,\n sourceChatId: chatId,\n routing,\n ...(isCronSession\n ? {\n sessionType: 'cron',\n customData: { cronJobId: chatId },\n }\n : {}),\n ...(isHeartbeatSession\n ? {\n sessionType: 'heartbeat',\n customData: { heartbeatTarget: chatId },\n }\n : {}),\n stats: {\n messageCount: messages.length,\n tokenCount: this.estimateTokens(messages),\n },\n };\n }\n\n /**\n * Extract routing metadata from session key\n */\n private extractRoutingFromKey(key: string, channel: string): SessionMetadata['routing'] {\n const parts = key.split(':');\n if (parts.length < 5) {\n return undefined;\n }\n\n const [agentId, source, accountId, peerKind, peerId, ...rest] = parts;\n \n let threadId: string | undefined;\n let scopeId: string | undefined;\n \n // Parse optional thread and scope\n for (let i = 0; i < rest.length; i++) {\n if (rest[i] === 'thread' && rest[i + 1]) {\n threadId = rest[i + 1];\n i++;\n } else if (rest[i] === 'scope' && rest[i + 1]) {\n scopeId = rest[i + 1];\n i++;\n }\n }\n\n return {\n agentId: agentId?.toLowerCase() || 'main',\n source: source?.toLowerCase() || channel,\n accountId: accountId?.toLowerCase() || 'default',\n peerKind: peerKind?.toLowerCase() || 'dm',\n peerId: peerId?.toLowerCase() || 'unknown',\n threadId,\n scopeId,\n };\n }\n\n // ========== CRUD Operations ==========\n\n async list(query: SessionListQuery = {}): Promise<PaginatedResult<SessionMetadata>> {\n const index = await this.loadIndex();\n let sessions = [...(index.sessions || [])];\n\n // Apply filters\n if (query.status) {\n const statuses = Array.isArray(query.status) ? query.status : [query.status];\n sessions = sessions.filter((s) => statuses.includes(s.status));\n }\n\n if (query.channel) {\n const channels = query.channel\n .split(',')\n .map((c) => c.trim())\n .filter(Boolean);\n if (channels.length === 0) {\n sessions = [];\n } else if (channels.length === 1) {\n sessions = sessions.filter((s) => s.sourceChannel === channels[0]);\n } else {\n sessions = sessions.filter((s) => channels.includes(s.sourceChannel));\n }\n }\n\n if (query.tags && query.tags.length > 0) {\n sessions = sessions.filter((s) => query.tags!.some((tag) => s.tags.includes(tag)));\n }\n\n if (query.search) {\n const searchLower = query.search.toLowerCase();\n sessions = sessions.filter(\n (s) =>\n s.key.toLowerCase().includes(searchLower) ||\n s.name?.toLowerCase().includes(searchLower) ||\n s.tags.some((t) => t.toLowerCase().includes(searchLower))\n );\n }\n\n // Apply sorting\n const sortBy = query.sortBy || 'updatedAt';\n const sortOrder = query.sortOrder || 'desc';\n\n sessions.sort((a, b) => {\n const aVal = a[sortBy];\n const bVal = b[sortBy];\n const comparison = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;\n return sortOrder === 'asc' ? comparison : -comparison;\n });\n\n // Apply pagination\n const total = sessions.length;\n const limit = query.limit || DEFAULT_LIMIT;\n const offset = query.offset || 0;\n const items = sessions.slice(offset, offset + limit);\n\n return {\n items,\n total,\n limit,\n offset,\n hasMore: offset + limit < total,\n };\n }\n\n async get(key: string): Promise<SessionDetail | null> {\n const metadata = await this.getMetadata(key);\n if (!metadata) return null;\n\n const messages = await this.loadMessages(key);\n\n return {\n ...metadata,\n messages: this.convertMessages(messages),\n };\n }\n\n async getMetadata(key: string): Promise<SessionMetadata | null> {\n const index = await this.loadIndex();\n const metadata = index.sessions.find((s) => s.key === key);\n\n if (!metadata) {\n // Try to load from file directly (orphaned session)\n const scanned = await this.scanSessionFile(key);\n if (scanned) {\n index.sessions.push(scanned);\n this.indexDirty = true;\n return scanned;\n }\n return null;\n }\n\n return metadata;\n }\n\n async updateMetadata(key: string, updates: Partial<SessionMetadata>): Promise<void> {\n const index = await this.loadIndex();\n const idx = index.sessions.findIndex((s) => s.key === key);\n\n if (idx === -1) {\n throw new Error(`Session not found: ${key}`);\n }\n\n index.sessions[idx] = {\n ...index.sessions[idx],\n ...updates,\n updatedAt: new Date().toISOString(),\n };\n\n this.indexDirty = true;\n await this.saveIndex();\n\n log.debug({ key, updates }, 'Session metadata updated');\n }\n\n async delete(key: string): Promise<boolean> {\n const index = await this.loadIndex();\n const idx = index.sessions.findIndex((s) => s.key === key);\n\n const primary = this.sessionPathsForKey(key);\n\n for (const p of [primary.jsonPath]) {\n try {\n await unlink(p);\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n for (const p of [primary.metaPath]) {\n try {\n await unlink(p);\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n\n // Remove from index\n if (idx !== -1) {\n index.sessions.splice(idx, 1);\n this.indexDirty = true;\n await this.saveIndex();\n }\n\n log.info({ key }, 'Session deleted');\n return true;\n }\n\n async deleteMany(keys: string[]): Promise<{ success: string[]; failed: string[] }> {\n const success: string[] = [];\n const failed: string[] = [];\n\n for (const key of keys) {\n try {\n await this.delete(key);\n success.push(key);\n } catch {\n failed.push(key);\n }\n }\n\n return { success, failed };\n }\n\n // ========== Status Operations ==========\n\n async setStatus(key: string, status: SessionStatus): Promise<void> {\n await this.updateMetadata(key, { status });\n\n if (status === SessionStatus.ARCHIVED) {\n await this.moveToArchive(key);\n } else {\n await this.moveFromArchive(key);\n }\n }\n\n async archive(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ARCHIVED);\n }\n\n async unarchive(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ACTIVE);\n }\n\n async pin(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.PINNED);\n }\n\n async unpin(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ACTIVE);\n }\n\n // ========== Message Operations ==========\n\n async loadMessages(key: string, options?: { fromArchive?: boolean }): Promise<AgentMessage[]> {\n const primary = this.sessionPathsForKey(key);\n\n const readAndNormalize = async (path: string): Promise<AgentMessage[] | null> => {\n try {\n const data = await readFile(path, 'utf-8');\n const messages = JSON.parse(data) as AgentMessage[];\n if (hasProblematicMessages(messages)) {\n const cleaned = cleanTrailingErrors(messages);\n if (cleaned.length !== messages.length) {\n log.info(\n { key, original: messages.length, cleaned: cleaned.length },\n 'Cleaned problematic messages on load'\n );\n }\n return cleaned;\n }\n return messages;\n } catch {\n return null;\n }\n };\n\n const messages = await readAndNormalize(primary.jsonPath);\n\n if (messages !== null) {\n return messages;\n }\n\n if (options?.fromArchive) {\n const archivedFile = await this.findMostRecentArchive(key);\n if (!archivedFile) {\n return [];\n }\n const archived = await readAndNormalize(archivedFile);\n return archived ?? [];\n }\n return [];\n }\n\n /**\n * Find the most recent archived session file for a given key.\n * Archived files have format: {safeKey}.{timestamp}.json\n */\n private async findMostRecentArchive(sessionKey: string): Promise<string | null> {\n const safeKey = this.sanitizeKey(sessionKey);\n const shardDir = join(this.archiveDir, resolveSessionShardRelativePath(sessionKey));\n\n const scanDir = async (dir: string): Promise<string | null> => {\n try {\n const files = await readdir(dir);\n const matchingFiles = files\n .filter((f) => f.startsWith(`${safeKey}.`) && f.endsWith('.json') && !f.endsWith('.meta.json'))\n .sort()\n .reverse();\n if (matchingFiles.length === 0) return null;\n return join(dir, matchingFiles[0]);\n } catch {\n return null;\n }\n };\n\n const inShard = await scanDir(shardDir);\n if (inShard) return inShard;\n return await scanDir(this.archiveDir);\n }\n\n async saveMessages(key: string, messages: AgentMessage[]): Promise<void> {\n const { dir, jsonPath } = this.sessionPathsForKey(key);\n\n await mkdir(dir, { recursive: true });\n await writeFile(jsonPath, JSON.stringify(messages, null, 2));\n\n // Update or create metadata\n const index = await this.loadIndex();\n const existingIdx = index.sessions.findIndex((s) => s.key === key);\n const now = new Date().toISOString();\n\n const { channel, chatId } = this.parseSessionKey(key);\n const routing = this.extractRoutingFromKey(key, channel);\n const isCronSession = channel === 'cron';\n const isHeartbeatSession = channel === 'heartbeat';\n\n if (existingIdx !== -1) {\n const prev = index.sessions[existingIdx];\n index.sessions[existingIdx] = {\n ...prev,\n sourceChannel: channel,\n sourceChatId: chatId,\n messageCount: messages.length,\n estimatedTokens: this.estimateTokens(messages),\n updatedAt: now,\n lastAccessedAt: now,\n routing: routing || prev.routing,\n ...(isCronSession\n ? {\n sessionType: 'cron',\n customData: {\n ...prev.customData,\n cronJobId: chatId,\n },\n }\n : {}),\n ...(isHeartbeatSession\n ? {\n sessionType: 'heartbeat',\n customData: {\n ...prev.customData,\n heartbeatTarget: chatId,\n },\n }\n : {}),\n stats: {\n ...prev.stats,\n messageCount: messages.length,\n tokenCount: this.estimateTokens(messages),\n lastTurnAt: Date.now(),\n },\n };\n } else {\n index.sessions.push({\n key,\n status: SessionStatus.ACTIVE,\n tags: [],\n createdAt: now,\n updatedAt: now,\n lastAccessedAt: now,\n messageCount: messages.length,\n estimatedTokens: this.estimateTokens(messages),\n compactedCount: 0,\n sourceChannel: channel,\n sourceChatId: chatId,\n routing,\n ...(isCronSession\n ? {\n sessionType: 'cron',\n customData: { cronJobId: chatId },\n }\n : {}),\n ...(isHeartbeatSession\n ? {\n sessionType: 'heartbeat',\n customData: { heartbeatTarget: chatId },\n }\n : {}),\n stats: {\n messageCount: messages.length,\n tokenCount: this.estimateTokens(messages),\n lastTurnAt: Date.now(),\n },\n });\n }\n\n this.indexDirty = true;\n await this.saveIndex();\n\n invalidateSessionSearchIndexCache();\n }\n\n // ========== Sliding Window & Compaction ==========\n\n /**\n * Get window stats for messages\n */\n getWindowStats(messages: AgentMessage[]) {\n return this.window.getStats(messages);\n }\n\n /**\n * Check if session needs compaction\n */\n needsCompaction(key: string, messages: AgentMessage[], contextWindow: number) {\n return this.compactor.needsCompaction(messages, contextWindow);\n }\n\n /**\n * Prepare compaction (check if needed)\n */\n prepareCompaction(\n key: string,\n messages: AgentMessage[],\n contextWindow: number\n ): { needsCompaction: boolean; messages: AgentMessage[]; stats?: ReturnType<typeof this.compactor.needsCompaction> } {\n const result = this.compactor.needsCompaction(messages, contextWindow);\n return {\n needsCompaction: result.needed,\n messages,\n stats: result,\n };\n }\n\n /**\n * Apply compaction result to messages\n */\n async applyCompaction(\n key: string,\n messages: AgentMessage[],\n result: CompactionResult\n ): Promise<AgentMessage[]> {\n const compacted = this.compactor.applyCompaction(messages, result);\n \n // Persist the compacted messages to disk so subsequent loads see the reduced context\n await this.saveMessages(key, compacted);\n \n const metadata = await this.getMetadata(key);\n if (metadata) {\n await this.updateMetadata(key, {\n compactedCount: metadata.compactedCount + 1,\n });\n }\n \n log.info({\n key,\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n keptMessages: compacted.length,\n }, 'Session compacted');\n \n return compacted;\n }\n\n /**\n * Compact session with LLM summary\n */\n async compact(\n key: string,\n messages: AgentMessage[],\n contextWindow: number,\n instructions?: string\n ): Promise<CompactionResult> {\n const result = await this.compactor.compact(messages, instructions);\n \n if (result.compacted) {\n await this.applyCompaction(key, messages, result);\n }\n \n return result;\n }\n\n /**\n * Get compaction stats for a session\n */\n async getCompactionStats(key: string) {\n const metadata = await this.getMetadata(key);\n if (!metadata) return undefined;\n \n return {\n compactionCount: metadata.compactedCount,\n totalTokensBefore: 0,\n totalTokensAfter: 0,\n lastCompactionAt: undefined,\n };\n }\n\n // ========== MemoryStore API Aliases ==========\n\n /** Alias for delete */\n async deleteSession(key: string): Promise<boolean> {\n return this.delete(key);\n }\n\n /** Alias for loadMessages */\n async load(key: string, options?: { fromArchive?: boolean }): Promise<AgentMessage[]> {\n return this.loadMessages(key, options);\n }\n\n /** Alias for saveMessages */\n async save(key: string, messages: AgentMessage[]): Promise<void> {\n return this.saveMessages(key, messages);\n }\n\n /** Alias for estimateTokens */\n async estimateTokenUsage(key: string, messages: AgentMessage[]): Promise<number> {\n return this.estimateTokens(messages);\n }\n\n // ========== Search ==========\n\n async searchInSession(key: string, keyword: string): Promise<Message[]> {\n const messages = await this.loadMessages(key);\n const keywordLower = keyword.toLowerCase();\n\n return this.convertMessages(\n messages.filter((m) => {\n const content = this.extractTextContent(m.content);\n return content.toLowerCase().includes(keywordLower);\n })\n );\n }\n\n // ========== Export/Import ==========\n\n async exportSession(key: string, format: ExportFormat): Promise<string> {\n const detail = await this.get(key);\n if (!detail) {\n throw new Error(`Session not found: ${key}`);\n }\n\n if (format === 'json') {\n const exportData: SessionExport = {\n version: INDEX_VERSION,\n exportedAt: new Date().toISOString(),\n metadata: detail,\n messages: detail.messages,\n };\n return JSON.stringify(exportData, null, 2);\n } else {\n // Markdown format\n const lines = [\n `# ${detail.name || detail.key}`,\n '',\n `- **Channel:** ${detail.sourceChannel}`,\n `- **Created:** ${detail.createdAt}`,\n `- **Messages:** ${detail.messageCount}`,\n `- **Tags:** ${detail.tags.join(', ') || 'none'}`,\n '',\n '---',\n '',\n ];\n\n for (const msg of detail.messages) {\n const role = msg.role === 'assistant' ? 'Assistant' : msg.role === 'user' ? 'User' : msg.role;\n lines.push(`## ${role}`);\n lines.push('');\n const body =\n typeof msg.content === 'string'\n ? msg.content\n : JSON.stringify(msg.content, null, 2);\n lines.push(body);\n lines.push('');\n lines.push('---');\n lines.push('');\n }\n\n return lines.join('\\n');\n }\n }\n\n // ========== Statistics ==========\n\n async getStats(): Promise<GlobalSessionStats> {\n const index = await this.loadIndex();\n const sessions = index.sessions;\n\n const byChannel: Record<string, number> = {};\n for (const s of sessions) {\n byChannel[s.sourceChannel] = (byChannel[s.sourceChannel] || 0) + 1;\n }\n\n let oldestSession: string | undefined;\n let newestSession: string | undefined;\n\n if (sessions.length > 0) {\n const sorted = [...sessions].sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n oldestSession = sorted[0].createdAt;\n newestSession = sorted[sorted.length - 1].createdAt;\n }\n\n return {\n totalSessions: sessions.length,\n activeSessions: sessions.filter((s) => s.status === SessionStatus.ACTIVE || s.status === SessionStatus.IDLE).length,\n archivedSessions: sessions.filter((s) => s.status === SessionStatus.ARCHIVED).length,\n pinnedSessions: sessions.filter((s) => s.status === SessionStatus.PINNED).length,\n totalMessages: sessions.reduce((sum, s) => sum + s.messageCount, 0),\n totalTokens: sessions.reduce((sum, s) => sum + s.estimatedTokens, 0),\n oldestSession,\n newestSession,\n byChannel,\n };\n }\n\n // ========== Cleanup ==========\n\n async archiveOld(olderThanDays: number): Promise<number> {\n const cutoff = new Date();\n cutoff.setDate(cutoff.getDate() - olderThanDays);\n\n const index = await this.loadIndex();\n let archived = 0;\n\n for (const session of index.sessions) {\n if (session.status !== SessionStatus.ARCHIVED && session.status !== SessionStatus.PINNED) {\n const lastAccess = new Date(session.lastAccessedAt);\n if (lastAccess < cutoff) {\n await this.archive(session.key);\n archived++;\n }\n }\n }\n\n return archived;\n }\n\n // ========== Helper Methods ==========\n\n private sanitizeKey(key: string): string {\n return key.replace(/[^a-zA-Z0-9_-]/g, '_');\n }\n\n private fileNameToKey(fileName: string): string {\n // Reverse of sanitizeKey - restore all colons from underscores\n // telegram_dm_123456 -> telegram:dm:123456\n // telegram_g_-100123456_t_789 -> telegram:g:-100123456:t:789\n return fileName.replace(/_/g, ':');\n }\n\n private parseSessionKey(key: string): { channel: string; chatId: string } {\n const parts = key.split(':');\n // Session key format: {agentId}:{source}:{accountId}:{peerKind}:{peerId}\n if (parts.length >= 5) {\n const parsed = parseRoutingSessionKey(key);\n if (parsed?.source === 'cron') {\n return { channel: 'cron', chatId: parsed.peerId };\n }\n return { channel: parts[1], chatId: parts.slice(2).join(':') };\n }\n // Gateway heartbeat: `heartbeat:main` / `heartbeat:isolated:<ts>`\n if (parts.length >= 2 && parts[0] === 'heartbeat') {\n return { channel: 'heartbeat', chatId: parts.slice(1).join(':') };\n }\n return { channel: 'unknown', chatId: key };\n }\n\n estimateTokens(messages: AgentMessage[]): number {\n // Rough estimate: 1 token ≈ 4 characters\n let total = 0;\n for (const msg of messages) {\n const text = this.extractTextContent(msg.content);\n total += Math.ceil(text.length / 4);\n }\n return total;\n }\n\n private extractTextContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n const parts: string[] = [];\n for (const item of content) {\n if (typeof item !== 'object' || item === null || !('type' in item)) continue;\n const c = item as { type?: string; text?: string; name?: string };\n if (c.type === 'text' && typeof c.text === 'string') {\n parts.push(c.text);\n } else if (c.type === 'toolCall' || c.type === 'tool_use') {\n parts.push(c.name ? `[${c.name}]` : '');\n }\n }\n return parts.join('');\n }\n return '';\n }\n\n private convertMessages(messages: AgentMessage[]): Message[] {\n return messages.map((m: any) => {\n const c = m.content;\n const content: string | unknown[] =\n typeof c === 'string'\n ? c\n : Array.isArray(c)\n ? c\n : this.extractTextContent(c);\n\n const row: Message = {\n role: m.role as 'system' | 'user' | 'assistant' | 'tool' | 'toolResult',\n content,\n timestamp: m.timestamp ? new Date(m.timestamp).toISOString() : undefined,\n tool_call_id: m.tool_call_id || m.toolCallId,\n tool_calls: m.tool_calls,\n name: m.name,\n };\n if (Array.isArray(m.attachments) && m.attachments.length > 0) {\n row.attachments = m.attachments;\n }\n return row;\n });\n }\n\n private async moveToArchive(key: string): Promise<void> {\n const safeKey = this.sanitizeKey(key);\n const primary = this.sessionPathsForKey(key);\n const sourcePath = existsSync(primary.jsonPath) ? primary.jsonPath : null;\n if (!sourcePath) {\n return;\n }\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const archiveShard = join(this.archiveDir, resolveSessionShardRelativePath(key));\n await mkdir(archiveShard, { recursive: true });\n const targetPath = join(archiveShard, `${safeKey}.${timestamp}.json`);\n\n try {\n const data = await readFile(sourcePath, 'utf-8');\n await writeFile(targetPath, data);\n await unlink(sourcePath);\n\n const metaSource = primary.metaPath;\n const metaTarget = join(archiveShard, `${safeKey}.${timestamp}.meta.json`);\n try {\n const metaData = await readFile(metaSource, 'utf-8');\n await writeFile(metaTarget, metaData);\n await unlink(metaSource);\n } catch {\n // Meta file might not exist\n }\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n\n private async moveFromArchive(key: string): Promise<void> {\n const sourcePath = await this.findMostRecentArchive(key);\n if (!sourcePath) {\n return;\n }\n\n const primary = this.sessionPathsForKey(key);\n await mkdir(primary.dir, { recursive: true });\n const targetPath = primary.jsonPath;\n\n try {\n const data = await readFile(sourcePath, 'utf-8');\n await writeFile(targetPath, data);\n await unlink(sourcePath);\n\n const metaSource = sourcePath.replace('.json', '.meta.json');\n const metaTarget = primary.metaPath;\n try {\n const metaData = await readFile(metaSource, 'utf-8');\n await writeFile(metaTarget, metaData);\n await unlink(metaSource);\n } catch {\n // Meta file might not exist\n }\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n\n}\n"],"mappings":"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"}
@@ -44,14 +44,6 @@ export interface SessionRoutingMeta {
44
44
  mainSessionKey?: string;
45
45
  lastRoutePolicy?: 'main' | 'session';
46
46
  }
47
- /** Session ACP metadata */
48
- export interface SessionAcpMeta {
49
- backend?: string;
50
- runtimeSessionName?: string;
51
- mode?: 'persistent' | 'oneshot';
52
- state?: 'idle' | 'running' | 'error';
53
- lastActivityAt?: number;
54
- }
55
47
  /** Session-level statistics (per session) */
56
48
  export interface SessionStats {
57
49
  messageCount: number;
@@ -88,8 +80,6 @@ export interface SessionMetadata {
88
80
  customData?: Record<string, unknown>;
89
81
  /** Routing metadata */
90
82
  routing?: SessionRoutingMeta;
91
- /** ACP metadata */
92
- acp?: SessionAcpMeta;
93
83
  /** Session statistics */
94
84
  stats?: SessionStats;
95
85
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../../../src/session/types.ts"],"sourcesContent":["// Session management types\n\nexport interface Message {\n role: 'system' | 'user' | 'assistant' | 'tool' | 'toolResult';\n /** Plain string or structured content blocks (tool calls, multimodal). */\n content: string | unknown[];\n timestamp?: string;\n tool_call_id?: string;\n tool_calls?: Array<{\n id: string;\n type: 'function';\n function: {\n name: string;\n arguments: string;\n };\n }>;\n tool_call_id$?: string;\n toolName?: string;\n isError?: boolean;\n name?: string;\n /** Webchat early-save / UI: inbound file metadata (no base64). Omitted when absent. */\n attachments?: Array<{\n type?: string;\n mimeType?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n }>;\n}\n\n/** Session status enum */\nexport enum SessionStatus {\n ACTIVE = 'active',\n IDLE = 'idle',\n ARCHIVED = 'archived',\n PINNED = 'pinned',\n}\n\n/** Session routing metadata */\nexport interface SessionRoutingMeta {\n agentId: string;\n source: string;\n accountId: string;\n peerKind: string;\n peerId: string;\n threadId?: string;\n scopeId?: string;\n mainSessionKey?: string;\n lastRoutePolicy?: 'main' | 'session';\n}\n\n/** Session ACP metadata */\nexport interface SessionAcpMeta {\n backend?: string;\n runtimeSessionName?: string;\n mode?: 'persistent' | 'oneshot';\n state?: 'idle' | 'running' | 'error';\n lastActivityAt?: number;\n}\n\n/** Session-level statistics (per session) */\nexport interface SessionStats {\n messageCount: number;\n tokenCount: number;\n turnCount?: number;\n lastTurnAt?: number;\n}\n\n/** Global session statistics (aggregate) */\nexport interface GlobalSessionStats {\n totalSessions: number;\n activeSessions: number;\n archivedSessions: number;\n pinnedSessions: number;\n totalMessages: number;\n totalTokens: number;\n oldestSession?: string;\n newestSession?: string;\n byChannel: Record<string, number>;\n}\n\n/** Session metadata (stored in index) */\nexport interface SessionMetadata {\n key: string;\n name?: string;\n status: SessionStatus;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n lastAccessedAt: string;\n messageCount: number;\n estimatedTokens: number;\n compactedCount: number;\n sourceChannel: string;\n sourceChatId: string;\n customData?: Record<string, unknown>;\n /** Routing metadata */\n routing?: SessionRoutingMeta;\n /** ACP metadata */\n acp?: SessionAcpMeta;\n /** Session statistics */\n stats?: SessionStats;\n /**\n * High-level origin for filtering/UI (e.g. `cron`, `heartbeat`, `webchat`).\n * Distinct from `sourceChannel` (routing namespace).\n */\n sessionType?: string;\n}\n\n/** Session detail (metadata + messages) */\nexport interface SessionDetail extends SessionMetadata {\n messages: Message[];\n}\n\n/** Session index file structure */\nexport interface SessionIndex {\n version: string;\n lastUpdated: string;\n sessions: SessionMetadata[];\n}\n\n/** Session list query parameters */\nexport interface SessionListQuery {\n status?: SessionStatus | SessionStatus[];\n /** Single `sourceChannel`, or comma-separated list (e.g. `telegram,weixin`). */\n channel?: string;\n tags?: string[];\n search?: string;\n sortBy?: 'updatedAt' | 'createdAt' | 'messageCount' | 'lastAccessedAt';\n sortOrder?: 'asc' | 'desc';\n limit?: number;\n offset?: number;\n}\n\n/** Paginated result */\nexport interface PaginatedResult<T> {\n items: T[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\n/** Export format */\nexport type ExportFormat = 'json' | 'markdown';\n\n/** Session export data */\nexport interface SessionExport {\n version: string;\n exportedAt: string;\n metadata: SessionMetadata;\n messages: Message[];\n}\n"],"mappings":";;AA+BA,IAAY,gBAAL,yBAAA,eAAA;AACL,eAAA,YAAA;AACA,eAAA,UAAA;AACA,eAAA,cAAA;AACA,eAAA,YAAA;;KACD"}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../../src/session/types.ts"],"sourcesContent":["// Session management types\n\nexport interface Message {\n role: 'system' | 'user' | 'assistant' | 'tool' | 'toolResult';\n /** Plain string or structured content blocks (tool calls, multimodal). */\n content: string | unknown[];\n timestamp?: string;\n tool_call_id?: string;\n tool_calls?: Array<{\n id: string;\n type: 'function';\n function: {\n name: string;\n arguments: string;\n };\n }>;\n tool_call_id$?: string;\n toolName?: string;\n isError?: boolean;\n name?: string;\n /** Webchat early-save / UI: inbound file metadata (no base64). Omitted when absent. */\n attachments?: Array<{\n type?: string;\n mimeType?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n }>;\n}\n\n/** Session status enum */\nexport enum SessionStatus {\n ACTIVE = 'active',\n IDLE = 'idle',\n ARCHIVED = 'archived',\n PINNED = 'pinned',\n}\n\n/** Session routing metadata */\nexport interface SessionRoutingMeta {\n agentId: string;\n source: string;\n accountId: string;\n peerKind: string;\n peerId: string;\n threadId?: string;\n scopeId?: string;\n mainSessionKey?: string;\n lastRoutePolicy?: 'main' | 'session';\n}\n\n/** Session-level statistics (per session) */\nexport interface SessionStats {\n messageCount: number;\n tokenCount: number;\n turnCount?: number;\n lastTurnAt?: number;\n}\n\n/** Global session statistics (aggregate) */\nexport interface GlobalSessionStats {\n totalSessions: number;\n activeSessions: number;\n archivedSessions: number;\n pinnedSessions: number;\n totalMessages: number;\n totalTokens: number;\n oldestSession?: string;\n newestSession?: string;\n byChannel: Record<string, number>;\n}\n\n/** Session metadata (stored in index) */\nexport interface SessionMetadata {\n key: string;\n name?: string;\n status: SessionStatus;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n lastAccessedAt: string;\n messageCount: number;\n estimatedTokens: number;\n compactedCount: number;\n sourceChannel: string;\n sourceChatId: string;\n customData?: Record<string, unknown>;\n /** Routing metadata */\n routing?: SessionRoutingMeta;\n /** Session statistics */\n stats?: SessionStats;\n /**\n * High-level origin for filtering/UI (e.g. `cron`, `heartbeat`, `webchat`).\n * Distinct from `sourceChannel` (routing namespace).\n */\n sessionType?: string;\n}\n\n/** Session detail (metadata + messages) */\nexport interface SessionDetail extends SessionMetadata {\n messages: Message[];\n}\n\n/** Session index file structure */\nexport interface SessionIndex {\n version: string;\n lastUpdated: string;\n sessions: SessionMetadata[];\n}\n\n/** Session list query parameters */\nexport interface SessionListQuery {\n status?: SessionStatus | SessionStatus[];\n /** Single `sourceChannel`, or comma-separated list (e.g. `telegram,weixin`). */\n channel?: string;\n tags?: string[];\n search?: string;\n sortBy?: 'updatedAt' | 'createdAt' | 'messageCount' | 'lastAccessedAt';\n sortOrder?: 'asc' | 'desc';\n limit?: number;\n offset?: number;\n}\n\n/** Paginated result */\nexport interface PaginatedResult<T> {\n items: T[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\n/** Export format */\nexport type ExportFormat = 'json' | 'markdown';\n\n/** Session export data */\nexport interface SessionExport {\n version: string;\n exportedAt: string;\n metadata: SessionMetadata;\n messages: Message[];\n}\n"],"mappings":";;AA+BA,IAAY,gBAAL,yBAAA,eAAA;AACL,eAAA,YAAA;AACA,eAAA,UAAA;AACA,eAAA,cAAA;AACA,eAAA,YAAA;;KACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xopcai/xopc",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Personal AI assistant: CLI, gateway (HTTP/WebSocket + React console), Telegram and WeChat (Weixin) channels — TypeScript, 20+ LLM providers via pi-ai, extensions and skills.",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -100,7 +100,6 @@
100
100
  }
101
101
  },
102
102
  "dependencies": {
103
- "@agentclientprotocol/sdk": "^0.18.2",
104
103
  "@grammyjs/runner": "^2.0.3",
105
104
  "@grammyjs/types": "^3.26.0",
106
105
  "@hono/node-server": "^1.19.13",
@@ -1 +0,0 @@
1
- import{t as e}from"./index-DBZ5eXW5.js";export{e as loadAttachment};