@xopcai/xopc 0.0.113 → 0.0.114

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 (327) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CBlklyNr.js → agent-avatar-dicebear-Fp-p7bP1.js} +1 -1
  4. package/dist/gateway/static/root/assets/agent-browser-settings-page-CWh7trjd.js +6 -0
  5. package/dist/gateway/static/root/assets/agent-defaults-tabbed-page-kC8OuLoj.js +1 -0
  6. package/dist/gateway/static/root/assets/agents-DJWG26Si.js +71 -0
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-28E_WfMe.js → app-management-settings-panel-v24i25Oj.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-Do1z0XgJ.js → appearance-settings-ycbdJRuZ.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-0V3DXZ7J.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-BAFlsov_.js → archive-plugin-CJzSADJo.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-V3qGPC3x.js → attachment-load-BTAB4zGS.js} +1 -1
  12. package/dist/gateway/static/root/assets/{binary-plugins-DT8NvA6W.js → binary-plugins-DXR1-mct.js} +1 -1
  13. package/dist/gateway/static/root/assets/{block-editor-CdLMEcHd.js → block-editor-CzQri9h2.js} +44 -44
  14. package/dist/gateway/static/root/assets/channels-settings-CIKOkYdO.js +1 -0
  15. package/dist/gateway/static/root/assets/{config-api-Bf5mjpu7.js → config-api-DRqjEvAx.js} +1 -1
  16. package/dist/gateway/static/root/assets/connectors-settings-ClrDS5jH.js +2 -0
  17. package/dist/gateway/static/root/assets/{cron-api--pAB_rps.js → cron-api-BXaZXv1o.js} +1 -1
  18. package/dist/gateway/static/root/assets/cron-page-Chi_4u4P.js +3 -0
  19. package/dist/gateway/static/root/assets/dreaming-settings-B0_rs2tj.js +1 -0
  20. package/dist/gateway/static/root/assets/{extension-debug-page-BGhE7msg.js → extension-debug-page-DSkqMYo7.js} +1 -1
  21. package/dist/gateway/static/root/assets/{extension-page-CvpI45WG.js → extension-page-h2dGDbMN.js} +1 -1
  22. package/dist/gateway/static/root/assets/{extension-settings-page-DAmR5FUe.js → extension-settings-page-hN99RhOi.js} +1 -1
  23. package/dist/gateway/static/root/assets/{fetch-NZmjSXLl.js → fetch-D9QqUtzZ.js} +1 -1
  24. package/dist/gateway/static/root/assets/{format-interval-label-qhth3BMV.js → format-interval-label-D6E687rG.js} +1 -1
  25. package/dist/gateway/static/root/assets/{gateway-config-api-CaXBt-9R.js → gateway-config-api-CLt_SKTC.js} +1 -1
  26. package/dist/gateway/static/root/assets/{gateway-settings-DphpbLBc.js → gateway-settings-1PDi4lI0.js} +1 -1
  27. package/dist/gateway/static/root/assets/{gateway-startup-retry-Be1omjh3.js → gateway-startup-retry-N7Z_sz7p.js} +1 -1
  28. package/dist/gateway/static/root/assets/goal-detail-page-D_wwXL-t.js +1 -0
  29. package/dist/gateway/static/root/assets/goals-page-DZ2toZ8y.js +2 -0
  30. package/dist/gateway/static/root/assets/{goals-settings-ceEBjnfc.js → goals-settings-LFlwtIKE.js} +1 -1
  31. package/dist/gateway/static/root/assets/{heartbeat-config-api-BzO6G4fD.js → heartbeat-config-api-DGPadTw-.js} +1 -1
  32. package/dist/gateway/static/root/assets/{heartbeat-settings-DCST9-du.js → heartbeat-settings-BHHAs8ZX.js} +1 -1
  33. package/dist/gateway/static/root/assets/index-DefIdW9q.js +122 -0
  34. package/dist/gateway/static/root/assets/index-DzmWBkq-.css +1 -0
  35. package/dist/gateway/static/root/assets/{interval-presets-DJmshV8n.js → interval-presets-CEklPQ_r.js} +1 -1
  36. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-9AUUlQ5Q.js → keyboard-shortcuts-settings-Ce_aQ8Iv.js} +1 -1
  37. package/dist/gateway/static/root/assets/logs-page-j_D2QTuQ.js +2 -0
  38. package/dist/gateway/static/root/assets/{markdown-split-CYYELqAr.js → markdown-split-BMz3xPXO.js} +1 -1
  39. package/dist/gateway/static/root/assets/{media-plugins-BEcMR2M8.js → media-plugins-BalOSoz9.js} +1 -1
  40. package/dist/gateway/static/root/assets/messages-BN_WiUg0.js +3 -0
  41. package/dist/gateway/static/root/assets/models-hub-panel-CMH_TmZz.js +2 -0
  42. package/dist/gateway/static/root/assets/note-detail-page-O18BrQPq.js +2 -0
  43. package/dist/gateway/static/root/assets/{note-image-lightbox-DNAoPcmm.js → note-image-lightbox-C5D1Q5mY.js} +1 -1
  44. package/dist/gateway/static/root/assets/notes-page-B_ZYCmY9.js +1 -0
  45. package/dist/gateway/static/root/assets/{preview-open-alternatives-BPLz0AIv.js → preview-open-alternatives-CYFR5U9l.js} +1 -1
  46. package/dist/gateway/static/root/assets/remote-access-hub-CwKgWixY.js +1 -0
  47. package/dist/gateway/static/root/assets/{save-bar-controls-CrWwWRVK.js → save-bar-controls-BQ79Z5IP.js} +1 -1
  48. package/dist/gateway/static/root/assets/{schedule-field-CF2sadvN.js → schedule-field-Bgk6Emue.js} +1 -1
  49. package/dist/gateway/static/root/assets/{schema-form-Di5cKjYE.js → schema-form-DzS36TMa.js} +1 -1
  50. package/dist/gateway/static/root/assets/sessions-page-C4fcD-7B.js +1 -0
  51. package/dist/gateway/static/root/assets/settings-advanced-gate-Ad2OJ86w.js +1 -0
  52. package/dist/gateway/static/root/assets/{settings-collapsible-section-Chh282jW.js → settings-collapsible-section-CdjFnSAy.js} +1 -1
  53. package/dist/gateway/static/root/assets/{settings-form-section-Dq-83cQd.js → settings-form-section-DZLy_exu.js} +1 -1
  54. package/dist/gateway/static/root/assets/settings-page-D2BMY5wo.js +1 -0
  55. package/dist/gateway/static/root/assets/setup-status-panel-aWwxl_E1.js +1 -0
  56. package/dist/gateway/static/root/assets/{share-preview-page-0FXYnSUA.js → share-preview-page-_qRDr4g_.js} +1 -1
  57. package/dist/gateway/static/root/assets/shares-settings-DUE83uIf.js +1 -0
  58. package/dist/gateway/static/root/assets/skill-reload-api-mWKJcrog.js +1 -0
  59. package/dist/gateway/static/root/assets/skills-page-0Sx1Q58n.js +2 -0
  60. package/dist/gateway/static/root/assets/{system-settings-panel-W1pTcojB.js → system-settings-panel-hqaBF5aG.js} +1 -1
  61. package/dist/gateway/static/root/assets/{use-agent-defaults-form-BVRvkK7m.js → use-agent-defaults-form-zbVIQLOA.js} +1 -1
  62. package/dist/gateway/static/root/assets/{use-channel-catalog-CgJibfMM.js → use-channel-catalog-CbXq97rT.js} +1 -1
  63. package/dist/gateway/static/root/assets/{use-settings-tab-guard-Bw_qkRQE.js → use-settings-tab-guard-DwS3drWK.js} +1 -1
  64. package/dist/gateway/static/root/assets/voice-api-key-field-D6HR0hR2.js +1 -0
  65. package/dist/gateway/static/root/assets/{voice-player-store-C7lziaAx.js → voice-player-store-DSuOd9zy.js} +1 -1
  66. package/dist/gateway/static/root/assets/workflow-page.utils-C0Uq3xyq.js +1 -0
  67. package/dist/gateway/static/root/assets/workflows-page-DW0fmWhQ.js +18 -0
  68. package/dist/gateway/static/root/index.html +5 -5
  69. package/dist/package.js +1 -1
  70. package/dist/src/agent/agent-manager.d.ts +8 -0
  71. package/dist/src/agent/agent-manager.js +57 -22
  72. package/dist/src/agent/agent-manager.js.map +1 -1
  73. package/dist/src/agent/bootstrap/project-agents-context.d.ts +2 -0
  74. package/dist/src/agent/bootstrap/project-agents-context.js +32 -0
  75. package/dist/src/agent/bootstrap/project-agents-context.js.map +1 -0
  76. package/dist/src/agent/goals/persistent-goal-service.js +2 -4
  77. package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
  78. package/dist/src/agent/inbound/inbound-loop.js +5 -1
  79. package/dist/src/agent/inbound/inbound-loop.js.map +1 -1
  80. package/dist/src/agent/inbound/turn-dispatcher.d.ts +2 -2
  81. package/dist/src/agent/inbound/turn-dispatcher.js +2 -2
  82. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  83. package/dist/src/agent/mcp/bundle-mcp-materialize.d.ts +27 -0
  84. package/dist/src/agent/mcp/bundle-mcp-materialize.js +49 -1
  85. package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
  86. package/dist/src/agent/mcp/bundle-mcp-runtime.js +78 -3
  87. package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
  88. package/dist/src/agent/mcp/bundle-mcp-types.d.ts +19 -0
  89. package/dist/src/agent/service/btw-query.js +11 -3
  90. package/dist/src/agent/service/btw-query.js.map +1 -1
  91. package/dist/src/agent/service/parse-outbound-session-key.js +13 -8
  92. package/dist/src/agent/service/parse-outbound-session-key.js.map +1 -1
  93. package/dist/src/agent/service/process-direct-one-shot.d.ts +2 -2
  94. package/dist/src/agent/service/process-direct-one-shot.js +1 -1
  95. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  96. package/dist/src/agent/service/process-direct-streaming.d.ts +2 -2
  97. package/dist/src/agent/service/process-direct-streaming.js +1 -1
  98. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  99. package/dist/src/agent/service.d.ts +1 -1
  100. package/dist/src/agent/service.js +9 -5
  101. package/dist/src/agent/service.js.map +1 -1
  102. package/dist/src/agent/service.types.d.ts +5 -0
  103. package/dist/src/agent/skills/filesystem-watcher.js +4 -3
  104. package/dist/src/agent/skills/filesystem-watcher.js.map +1 -1
  105. package/dist/src/agent/skills/index.js +2 -1
  106. package/dist/src/agent/skills/index.js.map +1 -1
  107. package/dist/src/agent/skills/skill-manage-ops.js +2 -1
  108. package/dist/src/agent/skills/skill-manage-ops.js.map +1 -1
  109. package/dist/src/agent/skills/types.d.ts +1 -1
  110. package/dist/src/agent/skills/watcher.js +2 -5
  111. package/dist/src/agent/skills/watcher.js.map +1 -1
  112. package/dist/src/agent/skills/workspace-skills-dir.d.ts +1 -0
  113. package/dist/src/agent/skills/workspace-skills-dir.js +11 -0
  114. package/dist/src/agent/skills/workspace-skills-dir.js.map +1 -0
  115. package/dist/src/agent/tool-manuals/browser.d.ts +1 -1
  116. package/dist/src/agent/tool-manuals/browser.js +62 -3
  117. package/dist/src/agent/tool-manuals/browser.js.map +1 -1
  118. package/dist/src/agent/tools/browser/tool/browser-use-tool.d.ts +4 -1
  119. package/dist/src/agent/tools/browser/tool/browser-use-tool.js +13 -4
  120. package/dist/src/agent/tools/browser/tool/browser-use-tool.js.map +1 -1
  121. package/dist/src/agent/tools/browser/tool/schemas.d.ts +0 -2
  122. package/dist/src/agent/tools/browser/tool/schemas.js +1 -2
  123. package/dist/src/agent/tools/browser/tool/schemas.js.map +1 -1
  124. package/dist/src/agent/tools/factory.js +1 -10
  125. package/dist/src/agent/tools/factory.js.map +1 -1
  126. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  127. package/dist/src/agent/tools/skill-manage-tool.js.map +1 -1
  128. package/dist/src/agent/workflow/subagent-runner.js +18 -1
  129. package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
  130. package/dist/src/browser/actions/extension-registry-bridge.js +91 -7
  131. package/dist/src/browser/actions/extension-registry-bridge.js.map +1 -1
  132. package/dist/src/browser/actions/registry.js +383 -7
  133. package/dist/src/browser/actions/registry.js.map +1 -1
  134. package/dist/src/browser/actions/types.d.ts +11 -3
  135. package/dist/src/browser/index.js +2 -2
  136. package/dist/src/browser/pipeline/output.d.ts +18 -16
  137. package/dist/src/browser/pipeline/output.js +28 -20
  138. package/dist/src/browser/pipeline/output.js.map +1 -1
  139. package/dist/src/browser/pipeline/runner.d.ts +7 -14
  140. package/dist/src/browser/pipeline/runner.js +349 -102
  141. package/dist/src/browser/pipeline/runner.js.map +1 -1
  142. package/dist/src/browser/pipeline/schema.d.ts +1 -0
  143. package/dist/src/browser/pipeline/schema.js +35 -58
  144. package/dist/src/browser/pipeline/schema.js.map +1 -1
  145. package/dist/src/browser/pipeline/source.d.ts +2 -0
  146. package/dist/src/browser/pipeline/source.js +10 -1
  147. package/dist/src/browser/pipeline/source.js.map +1 -1
  148. package/dist/src/browser/pipeline/template.d.ts +10 -2
  149. package/dist/src/browser/pipeline/template.js +50 -8
  150. package/dist/src/browser/pipeline/template.js.map +1 -1
  151. package/dist/src/chat-commands/builtins/config.js +3 -5
  152. package/dist/src/chat-commands/builtins/config.js.map +1 -1
  153. package/dist/src/chat-commands/context.d.ts +5 -0
  154. package/dist/src/chat-commands/context.js +18 -9
  155. package/dist/src/chat-commands/context.js.map +1 -1
  156. package/dist/src/chat-commands/index.d.ts +1 -1
  157. package/dist/src/chat-commands/index.js +2 -2
  158. package/dist/src/chat-commands/index.js.map +1 -1
  159. package/dist/src/chat-commands/processor.js +22 -21
  160. package/dist/src/chat-commands/processor.js.map +1 -1
  161. package/dist/src/chat-commands/session-key.d.ts +3 -15
  162. package/dist/src/chat-commands/session-key.js +3 -49
  163. package/dist/src/chat-commands/session-key.js.map +1 -1
  164. package/dist/src/chat-commands/types.d.ts +4 -0
  165. package/dist/src/cli/commands/browser-cli-helpers.js +11 -5
  166. package/dist/src/cli/commands/browser-cli-helpers.js.map +1 -1
  167. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  168. package/dist/src/cli/commands/doctor/checks/session-integrity.js +8 -8
  169. package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -1
  170. package/dist/src/config/rules.js +5 -0
  171. package/dist/src/config/rules.js.map +1 -1
  172. package/dist/src/connectors/builtin-catalog.js +180 -1
  173. package/dist/src/connectors/builtin-catalog.js.map +1 -1
  174. package/dist/src/connectors/health.js +18 -9
  175. package/dist/src/connectors/health.js.map +1 -1
  176. package/dist/src/connectors/types.d.ts +19 -0
  177. package/dist/src/connectors/usage.js +12 -4
  178. package/dist/src/connectors/usage.js.map +1 -1
  179. package/dist/src/extensions/types/tui.d.ts +2 -0
  180. package/dist/src/gateway/heartbeat/service.js +13 -1
  181. package/dist/src/gateway/heartbeat/service.js.map +1 -1
  182. package/dist/src/gateway/hono/app.js +13 -4
  183. package/dist/src/gateway/hono/app.js.map +1 -1
  184. package/dist/src/gateway/hono/oauth-async.js +1 -1
  185. package/dist/src/gateway/hono/routes/agent-stream.js +6 -6
  186. package/dist/src/gateway/hono/routes/agent-stream.js.map +1 -1
  187. package/dist/src/gateway/hono/routes/config-patch/gateway.d.ts +1 -1
  188. package/dist/src/gateway/hono/routes/config-patch/gateway.js +6 -0
  189. package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -1
  190. package/dist/src/gateway/hono/routes/goals.js +16 -3
  191. package/dist/src/gateway/hono/routes/goals.js.map +1 -1
  192. package/dist/src/gateway/hono/routes/logs.js +1 -0
  193. package/dist/src/gateway/hono/routes/logs.js.map +1 -1
  194. package/dist/src/gateway/hono/routes/mcp.js +27 -8
  195. package/dist/src/gateway/hono/routes/mcp.js.map +1 -1
  196. package/dist/src/gateway/hono/routes/notes.js +14 -2
  197. package/dist/src/gateway/hono/routes/notes.js.map +1 -1
  198. package/dist/src/gateway/hono/routes/sessions.js +62 -4
  199. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  200. package/dist/src/gateway/hono/routes/workspace.js +3 -66
  201. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  202. package/dist/src/gateway/hono/sse.d.ts +2 -2
  203. package/dist/src/gateway/hono/sse.js +7 -15
  204. package/dist/src/gateway/hono/sse.js.map +1 -1
  205. package/dist/src/gateway/resolve-webchat-session-key.d.ts +2 -6
  206. package/dist/src/gateway/resolve-webchat-session-key.js +5 -31
  207. package/dist/src/gateway/resolve-webchat-session-key.js.map +1 -1
  208. package/dist/src/gateway/service/agent-runner.d.ts +2 -3
  209. package/dist/src/gateway/service/agent-runner.js +9 -22
  210. package/dist/src/gateway/service/agent-runner.js.map +1 -1
  211. package/dist/src/gateway/service/run-gateway-agent.js +9 -7
  212. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  213. package/dist/src/gateway/service/sessions-api.d.ts +9 -0
  214. package/dist/src/gateway/service/sessions-api.js +11 -0
  215. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  216. package/dist/src/gateway/service.d.ts +1 -1
  217. package/dist/src/gateway/service.js +19 -5
  218. package/dist/src/gateway/service.js.map +1 -1
  219. package/dist/src/gateway/workspace-file-search.d.ts +9 -0
  220. package/dist/src/gateway/workspace-file-search.js +76 -0
  221. package/dist/src/gateway/workspace-file-search.js.map +1 -0
  222. package/dist/src/goals/goal-notifications.d.ts +2 -0
  223. package/dist/src/goals/goal-notifications.js +16 -11
  224. package/dist/src/goals/goal-notifications.js.map +1 -1
  225. package/dist/src/goals/goal-service.js +1 -6
  226. package/dist/src/goals/goal-service.js.map +1 -1
  227. package/dist/src/project-trust/trust-store.js +2 -0
  228. package/dist/src/project-trust/trust-store.js.map +1 -1
  229. package/dist/src/session/index.d.ts +1 -1
  230. package/dist/src/session/index.js +2 -2
  231. package/dist/src/session/init-session-turn.js +6 -9
  232. package/dist/src/session/init-session-turn.js.map +1 -1
  233. package/dist/src/session/manager.d.ts +5 -1
  234. package/dist/src/session/manager.js +5 -2
  235. package/dist/src/session/manager.js.map +1 -1
  236. package/dist/src/session/reset-type.d.ts +0 -2
  237. package/dist/src/session/reset-type.js +3 -9
  238. package/dist/src/session/reset-type.js.map +1 -1
  239. package/dist/src/session/resolve-session.js +9 -20
  240. package/dist/src/session/resolve-session.js.map +1 -1
  241. package/dist/src/session/store.d.ts +11 -3
  242. package/dist/src/session/store.js +29 -13
  243. package/dist/src/session/store.js.map +1 -1
  244. package/dist/src/session/types.d.ts +2 -4
  245. package/dist/src/session/types.js.map +1 -1
  246. package/dist/src/storage/sqlite/index.d.ts +2 -2
  247. package/dist/src/storage/sqlite/index.js +3 -3
  248. package/dist/src/storage/sqlite/migrations/010_session_id_identity.sql +30 -0
  249. package/dist/src/storage/sqlite/migrations/runner.d.ts +2 -2
  250. package/dist/src/storage/sqlite/migrations/runner.js +4 -4
  251. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  252. package/dist/src/storage/sqlite/migrations/runner.ts +2 -2
  253. package/dist/src/storage/sqlite/row-mappers.d.ts +4 -4
  254. package/dist/src/storage/sqlite/row-mappers.js +11 -14
  255. package/dist/src/storage/sqlite/row-mappers.js.map +1 -1
  256. package/dist/src/storage/sqlite/schema.js +3 -3
  257. package/dist/src/storage/sqlite/schema.js.map +1 -1
  258. package/dist/src/storage/sqlite/schema.sql +37 -10
  259. package/dist/src/storage/sqlite/session-metadata.d.ts +2 -7
  260. package/dist/src/storage/sqlite/session-metadata.js +18 -72
  261. package/dist/src/storage/sqlite/session-metadata.js.map +1 -1
  262. package/dist/src/storage/sqlite/session-repository.d.ts +6 -5
  263. package/dist/src/storage/sqlite/session-repository.js +34 -40
  264. package/dist/src/storage/sqlite/session-repository.js.map +1 -1
  265. package/dist/src/storage/sqlite/transcript-repository.d.ts +2 -2
  266. package/dist/src/storage/sqlite/transcript-repository.js +52 -52
  267. package/dist/src/storage/sqlite/transcript-repository.js.map +1 -1
  268. package/dist/src/tui/backends/embedded-backend.d.ts +11 -4
  269. package/dist/src/tui/backends/embedded-backend.js +27 -1
  270. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  271. package/dist/src/tui/backends/gateway-sse-backend.d.ts +4 -1
  272. package/dist/src/tui/backends/gateway-sse-backend.js +21 -2
  273. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  274. package/dist/src/tui/extension-host/autocomplete.js +13 -10
  275. package/dist/src/tui/extension-host/autocomplete.js.map +1 -1
  276. package/dist/src/tui/extension-host/runtime.d.ts +7 -0
  277. package/dist/src/tui/extension-host/runtime.js +6 -4
  278. package/dist/src/tui/extension-host/runtime.js.map +1 -1
  279. package/dist/src/tui/tui-backend.d.ts +9 -0
  280. package/dist/src/tui/tui-commands.js +85 -6
  281. package/dist/src/tui/tui-commands.js.map +1 -1
  282. package/dist/src/tui/tui-file-autocomplete.d.ts +5 -0
  283. package/dist/src/tui/tui-file-autocomplete.js +26 -0
  284. package/dist/src/tui/tui-file-autocomplete.js.map +1 -0
  285. package/dist/src/tui/tui-picker-overlay.js +1 -1
  286. package/dist/src/tui/tui.js +29 -11
  287. package/dist/src/tui/tui.js.map +1 -1
  288. package/dist/src/utils/logger/context.d.ts +2 -2
  289. package/dist/src/utils/logger/context.js +2 -1
  290. package/dist/src/utils/logger/context.js.map +1 -1
  291. package/dist/src/utils/logger/log-store.js +1 -0
  292. package/dist/src/utils/logger/log-store.js.map +1 -1
  293. package/dist/src/utils/logger/pino-record.js +3 -0
  294. package/dist/src/utils/logger/pino-record.js.map +1 -1
  295. package/dist/src/utils/logger/types.d.ts +5 -0
  296. package/dist/src/utils/logger/types.js.map +1 -1
  297. package/dist/src/workflows/service/workflow-session-bridge.js +13 -1
  298. package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
  299. package/package.json +1 -1
  300. package/dist/gateway/static/root/assets/agent-browser-settings-page-rGbKc2Hh.js +0 -6
  301. package/dist/gateway/static/root/assets/agent-defaults-tabbed-page-CtwVbjOA.js +0 -1
  302. package/dist/gateway/static/root/assets/agents-CCBmGcwc.js +0 -71
  303. package/dist/gateway/static/root/assets/apps-page-xwCsJS15.js +0 -1
  304. package/dist/gateway/static/root/assets/channels-settings-C044DcYk.js +0 -1
  305. package/dist/gateway/static/root/assets/connectors-settings-8VEWUOTj.js +0 -2
  306. package/dist/gateway/static/root/assets/cron-page-DqmNGNOy.js +0 -3
  307. package/dist/gateway/static/root/assets/dreaming-settings-6UZE1FSK.js +0 -1
  308. package/dist/gateway/static/root/assets/goal-detail-page-bv0VRDaf.js +0 -1
  309. package/dist/gateway/static/root/assets/goals-page-1Ywnt4Cb.js +0 -2
  310. package/dist/gateway/static/root/assets/index-Cxm6xsps.js +0 -122
  311. package/dist/gateway/static/root/assets/index-b_R4tKqt.css +0 -1
  312. package/dist/gateway/static/root/assets/logs-page-B_wrxhee.js +0 -2
  313. package/dist/gateway/static/root/assets/messages-CkT-lcgP.js +0 -3
  314. package/dist/gateway/static/root/assets/models-hub-panel-D0JDSbNu.js +0 -2
  315. package/dist/gateway/static/root/assets/note-detail-page-DpFV-EO6.js +0 -2
  316. package/dist/gateway/static/root/assets/notes-page-CdkjTch8.js +0 -1
  317. package/dist/gateway/static/root/assets/remote-access-hub-DSYVAi96.js +0 -1
  318. package/dist/gateway/static/root/assets/sessions-page-CenkcVjF.js +0 -1
  319. package/dist/gateway/static/root/assets/settings-advanced-gate-D9sva86V.js +0 -1
  320. package/dist/gateway/static/root/assets/settings-page-DL35U0x0.js +0 -1
  321. package/dist/gateway/static/root/assets/setup-status-panel-Ctwbp9Er.js +0 -1
  322. package/dist/gateway/static/root/assets/shares-settings-DIM3JNsr.js +0 -1
  323. package/dist/gateway/static/root/assets/skill-reload-api-DAlNaNzL.js +0 -1
  324. package/dist/gateway/static/root/assets/skills-page-CLrHVzAt.js +0 -2
  325. package/dist/gateway/static/root/assets/voice-api-key-field-vYAO71Gn.js +0 -1
  326. package/dist/gateway/static/root/assets/workflow-page.utils-sgHjOXOm.js +0 -1
  327. package/dist/gateway/static/root/assets/workflows-page-DYyOTmFx.js +0 -18
@@ -1,4 +1,5 @@
1
1
  import type { Config } from '../config/schema.js';
2
+ import type { SessionMetadata } from '../session/index.js';
2
3
  export type GoalNotificationEvent = 'done' | 'blocked' | 'needs_input' | 'queue_failed' | 'queue_retry' | 'queue_succeeded' | 'queue_skipped';
3
4
  export interface GoalNotificationTarget {
4
5
  channel: string;
@@ -12,6 +13,7 @@ export interface GoalNotificationSendInput extends GoalNotificationTarget {
12
13
  }
13
14
  export interface GoalNotificationServiceOptions {
14
15
  getConfig: () => Config;
16
+ getSessionMetadata?: (sessionKey: string) => Promise<SessionMetadata | null>;
15
17
  send: (input: GoalNotificationSendInput) => Promise<void>;
16
18
  }
17
19
  export declare class GoalNotificationService {
@@ -1,9 +1,7 @@
1
1
  import { createLogger } from "../utils/logger/index.js";
2
2
  import { init_logger } from "../utils/logger.js";
3
- import { init_session_key, parseSessionKey } from "../routing/session-key.js";
4
3
  import { GoalService } from "./goal-service.js";
5
4
  //#region src/goals/goal-notifications.ts
6
- init_session_key();
7
5
  init_logger();
8
6
  const log = createLogger("GoalNotifications");
9
7
  function eventForQueueStatus(status) {
@@ -88,7 +86,7 @@ var GoalNotificationService = class {
88
86
  const policy = this.opts.getConfig().goals?.notifications;
89
87
  if (!policy?.enabled) return;
90
88
  if (!policy.events.includes(event)) return;
91
- const targets = this.resolveTargets(event, goal);
89
+ const targets = await this.resolveTargets(event, goal);
92
90
  if (targets.length === 0) return;
93
91
  const text = formatGoalNotification(event, goal, { queueItem: opts?.queueItem });
94
92
  for (const target of targets) {
@@ -104,18 +102,25 @@ var GoalNotificationService = class {
104
102
  });
105
103
  }
106
104
  }
107
- resolveTargets(event, goal) {
105
+ async resolveTargets(event, goal) {
108
106
  const policy = this.opts.getConfig().goals?.notifications;
109
107
  if (!policy?.enabled) return [];
110
108
  const byKey = /* @__PURE__ */ new Map();
111
- if (policy.includeLinkedSessions !== false && goal.activeSessionKey) {
112
- const parsed = parseSessionKey(goal.activeSessionKey);
113
- if (parsed && policy.channels.includes(parsed.source)) {
109
+ if (policy.includeLinkedSessions !== false && goal.activeSessionKey && this.opts.getSessionMetadata) {
110
+ const routing = (await this.opts.getSessionMetadata(goal.activeSessionKey).catch((err) => {
111
+ log.warn({
112
+ err,
113
+ sessionKey: goal.activeSessionKey,
114
+ goalId: goal.id
115
+ }, "Failed to load linked session metadata for goal notification");
116
+ return null;
117
+ }))?.routing;
118
+ if (routing && policy.channels.includes(routing.source)) {
114
119
  const target = {
115
- channel: parsed.source,
116
- chatId: parsed.peerId,
117
- accountId: parsed.accountId,
118
- ...parsed.threadId ? { threadId: parsed.threadId } : {}
120
+ channel: routing.source,
121
+ chatId: routing.peerId,
122
+ accountId: routing.accountId,
123
+ ...routing.threadId ? { threadId: routing.threadId } : {}
119
124
  };
120
125
  byKey.set(targetKey(target), target);
121
126
  }
@@ -1 +1 @@
1
- {"version":3,"file":"goal-notifications.js","names":[],"sources":["../../../src/goals/goal-notifications.ts"],"sourcesContent":["import type { Config } from '../config/schema.js';\nimport { parseSessionKey } from '../routing/session-key.js';\nimport { createLogger } from '../utils/logger.js';\n\nimport { GoalService } from './goal-service.js';\nimport type { GoalWithDetails } from './types.js';\nimport type { GoalQueueItemSnapshot } from './goal-queue-types.js';\n\nconst log = createLogger('GoalNotifications');\n\nexport type GoalNotificationEvent =\n | 'done'\n | 'blocked'\n | 'needs_input'\n | 'queue_failed'\n | 'queue_retry'\n | 'queue_succeeded'\n | 'queue_skipped';\n\nexport interface GoalNotificationTarget {\n channel: string;\n chatId: string;\n accountId?: string;\n threadId?: string | number;\n silent?: boolean;\n}\n\nexport interface GoalNotificationSendInput extends GoalNotificationTarget {\n text: string;\n}\n\nexport interface GoalNotificationServiceOptions {\n getConfig: () => Config;\n send: (input: GoalNotificationSendInput) => Promise<void>;\n}\n\nfunction eventForQueueStatus(status: GoalQueueItemSnapshot['status']): GoalNotificationEvent | null {\n if (status === 'failed') return 'queue_failed';\n if (status === 'retry_waiting') return 'queue_retry';\n if (status === 'succeeded') return 'queue_succeeded';\n if (status === 'skipped') return 'queue_skipped';\n return null;\n}\n\nfunction eventForGoalStatus(status: GoalWithDetails['status']): GoalNotificationEvent | null {\n if (status === 'done' || status === 'blocked' || status === 'needs_input') return status;\n return null;\n}\n\nfunction targetKey(target: GoalNotificationTarget): string {\n return [\n target.channel,\n target.chatId,\n target.accountId ?? '',\n target.threadId ?? '',\n ].join('\\u0000');\n}\n\nfunction formatGoalNotification(event: GoalNotificationEvent, goal: GoalWithDetails, opts?: {\n queueItem?: GoalQueueItemSnapshot;\n}): string {\n const labels: Record<GoalNotificationEvent, string> = {\n done: 'Goal completed',\n blocked: 'Goal blocked',\n needs_input: 'Goal needs input',\n queue_failed: 'Goal run failed',\n queue_retry: 'Goal run retry scheduled',\n queue_succeeded: 'Goal run finished',\n queue_skipped: 'Goal run skipped',\n };\n const lines = [`${labels[event]}: ${goal.title}`];\n if (goal.blockedReason) lines.push(`Reason: ${goal.blockedReason}`);\n if (goal.nextAction) lines.push(`Next: ${goal.nextAction}`);\n if (goal.latestRun?.reason && goal.latestRun.reason !== goal.blockedReason) {\n lines.push(`Run: ${goal.latestRun.reason}`);\n }\n if (opts?.queueItem?.lastError) lines.push(`Queue: ${opts.queueItem.lastError}`);\n const progress = goal.checklist.length\n ? goal.checklist.filter((it) => it.status === 'completed' || it.status === 'impossible').length\n : 0;\n if (goal.checklist.length) lines.push(`Checklist: ${progress}/${goal.checklist.length}`);\n lines.push(`Turns: ${goal.turnsUsed}/${goal.maxTurns}`);\n return lines.join('\\n');\n}\n\nexport class GoalNotificationService {\n private readonly goals = new GoalService();\n private readonly sentKeys = new Set<string>();\n\n constructor(private readonly opts: GoalNotificationServiceOptions) {}\n\n handleGatewayEvent(type: string, payload: unknown): void {\n if (type === 'goal.queue.updated') {\n void this.handleQueueUpdated(payload).catch((err) => {\n log.warn({ err }, 'Goal queue notification failed');\n });\n return;\n }\n if (type === 'goal.status.updated') {\n void this.handleStatusUpdated(payload).catch((err) => {\n log.warn({ err }, 'Goal status notification failed');\n });\n }\n }\n\n private async handleQueueUpdated(payload: unknown): Promise<void> {\n const item = this.parseQueueItem(payload);\n if (!item) return;\n const event = eventForQueueStatus(item.status);\n if (!event) return;\n const goal = this.goals.get(item.goalId);\n if (!goal) return;\n await this.notify(event, goal, { queueItem: item, eventKey: `${item.id}:${item.status}:${item.attempts}` });\n }\n\n private async handleStatusUpdated(payload: unknown): Promise<void> {\n if (!payload || typeof payload !== 'object') return;\n const rec = payload as { goal?: unknown; status?: unknown; goalId?: unknown; previousStatus?: unknown };\n const goal =\n this.isGoal(rec.goal)\n ? rec.goal\n : typeof rec.goalId === 'string'\n ? this.goals.get(rec.goalId)\n : null;\n if (!goal) return;\n const status = typeof rec.status === 'string' ? rec.status : goal.status;\n const event = eventForGoalStatus(status as GoalWithDetails['status']);\n if (!event) return;\n if (rec.previousStatus === status) return;\n await this.notify(event, goal, { eventKey: `${goal.id}:${event}:${goal.updatedAt}` });\n }\n\n private async notify(\n event: GoalNotificationEvent,\n goal: GoalWithDetails,\n opts?: { queueItem?: GoalQueueItemSnapshot; eventKey?: string },\n ): Promise<void> {\n const policy = this.opts.getConfig().goals?.notifications;\n if (!policy?.enabled) return;\n if (!policy.events.includes(event)) return;\n\n const targets = this.resolveTargets(event, goal);\n if (targets.length === 0) return;\n\n const text = formatGoalNotification(event, goal, { queueItem: opts?.queueItem });\n for (const target of targets) {\n const dedupeKey = `${opts?.eventKey ?? `${goal.id}:${event}`}:${targetKey(target)}`;\n if (this.sentKeys.has(dedupeKey)) continue;\n this.sentKeys.add(dedupeKey);\n await this.opts.send({ ...target, text }).catch((err) => {\n this.sentKeys.delete(dedupeKey);\n throw err;\n });\n }\n }\n\n private resolveTargets(event: GoalNotificationEvent, goal: GoalWithDetails): GoalNotificationTarget[] {\n const policy = this.opts.getConfig().goals?.notifications;\n if (!policy?.enabled) return [];\n const byKey = new Map<string, GoalNotificationTarget>();\n\n if (policy.includeLinkedSessions !== false && goal.activeSessionKey) {\n const parsed = parseSessionKey(goal.activeSessionKey);\n const eligible = parsed && policy.channels.includes(parsed.source);\n if (eligible) {\n const target: GoalNotificationTarget = {\n channel: parsed.source,\n chatId: parsed.peerId,\n accountId: parsed.accountId,\n ...(parsed.threadId ? { threadId: parsed.threadId } : {}),\n };\n byKey.set(targetKey(target), target);\n }\n }\n\n for (const target of policy.targets ?? []) {\n const events = target.events;\n if (events && !events.includes(event)) continue;\n const normalized: GoalNotificationTarget = {\n channel: target.channel,\n chatId: target.chatId,\n ...(target.accountId ? { accountId: target.accountId } : {}),\n ...(target.threadId != null ? { threadId: target.threadId } : {}),\n ...(target.silent != null ? { silent: target.silent } : {}),\n };\n byKey.set(targetKey(normalized), normalized);\n }\n\n return [...byKey.values()];\n }\n\n private parseQueueItem(payload: unknown): GoalQueueItemSnapshot | null {\n if (!payload || typeof payload !== 'object') return null;\n const item = (payload as { item?: unknown }).item;\n if (!item || typeof item !== 'object') return null;\n const rec = item as Partial<GoalQueueItemSnapshot>;\n if (typeof rec.id !== 'string' || typeof rec.goalId !== 'string' || typeof rec.status !== 'string') return null;\n return rec as GoalQueueItemSnapshot;\n }\n\n private isGoal(value: unknown): value is GoalWithDetails {\n if (!value || typeof value !== 'object') return false;\n const rec = value as Partial<GoalWithDetails>;\n return typeof rec.id === 'string' && typeof rec.title === 'string' && Array.isArray(rec.checklist);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"goal-notifications.js","names":[],"sources":["../../../src/goals/goal-notifications.ts"],"sourcesContent":["import type { Config } from '../config/schema.js';\nimport { createLogger } from '../utils/logger.js';\nimport type { SessionMetadata } from '../session/index.js';\n\nimport { GoalService } from './goal-service.js';\nimport type { GoalWithDetails } from './types.js';\nimport type { GoalQueueItemSnapshot } from './goal-queue-types.js';\n\nconst log = createLogger('GoalNotifications');\n\nexport type GoalNotificationEvent =\n | 'done'\n | 'blocked'\n | 'needs_input'\n | 'queue_failed'\n | 'queue_retry'\n | 'queue_succeeded'\n | 'queue_skipped';\n\nexport interface GoalNotificationTarget {\n channel: string;\n chatId: string;\n accountId?: string;\n threadId?: string | number;\n silent?: boolean;\n}\n\nexport interface GoalNotificationSendInput extends GoalNotificationTarget {\n text: string;\n}\n\nexport interface GoalNotificationServiceOptions {\n getConfig: () => Config;\n getSessionMetadata?: (sessionKey: string) => Promise<SessionMetadata | null>;\n send: (input: GoalNotificationSendInput) => Promise<void>;\n}\n\nfunction eventForQueueStatus(status: GoalQueueItemSnapshot['status']): GoalNotificationEvent | null {\n if (status === 'failed') return 'queue_failed';\n if (status === 'retry_waiting') return 'queue_retry';\n if (status === 'succeeded') return 'queue_succeeded';\n if (status === 'skipped') return 'queue_skipped';\n return null;\n}\n\nfunction eventForGoalStatus(status: GoalWithDetails['status']): GoalNotificationEvent | null {\n if (status === 'done' || status === 'blocked' || status === 'needs_input') return status;\n return null;\n}\n\nfunction targetKey(target: GoalNotificationTarget): string {\n return [\n target.channel,\n target.chatId,\n target.accountId ?? '',\n target.threadId ?? '',\n ].join('\\u0000');\n}\n\nfunction formatGoalNotification(event: GoalNotificationEvent, goal: GoalWithDetails, opts?: {\n queueItem?: GoalQueueItemSnapshot;\n}): string {\n const labels: Record<GoalNotificationEvent, string> = {\n done: 'Goal completed',\n blocked: 'Goal blocked',\n needs_input: 'Goal needs input',\n queue_failed: 'Goal run failed',\n queue_retry: 'Goal run retry scheduled',\n queue_succeeded: 'Goal run finished',\n queue_skipped: 'Goal run skipped',\n };\n const lines = [`${labels[event]}: ${goal.title}`];\n if (goal.blockedReason) lines.push(`Reason: ${goal.blockedReason}`);\n if (goal.nextAction) lines.push(`Next: ${goal.nextAction}`);\n if (goal.latestRun?.reason && goal.latestRun.reason !== goal.blockedReason) {\n lines.push(`Run: ${goal.latestRun.reason}`);\n }\n if (opts?.queueItem?.lastError) lines.push(`Queue: ${opts.queueItem.lastError}`);\n const progress = goal.checklist.length\n ? goal.checklist.filter((it) => it.status === 'completed' || it.status === 'impossible').length\n : 0;\n if (goal.checklist.length) lines.push(`Checklist: ${progress}/${goal.checklist.length}`);\n lines.push(`Turns: ${goal.turnsUsed}/${goal.maxTurns}`);\n return lines.join('\\n');\n}\n\nexport class GoalNotificationService {\n private readonly goals = new GoalService();\n private readonly sentKeys = new Set<string>();\n\n constructor(private readonly opts: GoalNotificationServiceOptions) {}\n\n handleGatewayEvent(type: string, payload: unknown): void {\n if (type === 'goal.queue.updated') {\n void this.handleQueueUpdated(payload).catch((err) => {\n log.warn({ err }, 'Goal queue notification failed');\n });\n return;\n }\n if (type === 'goal.status.updated') {\n void this.handleStatusUpdated(payload).catch((err) => {\n log.warn({ err }, 'Goal status notification failed');\n });\n }\n }\n\n private async handleQueueUpdated(payload: unknown): Promise<void> {\n const item = this.parseQueueItem(payload);\n if (!item) return;\n const event = eventForQueueStatus(item.status);\n if (!event) return;\n const goal = this.goals.get(item.goalId);\n if (!goal) return;\n await this.notify(event, goal, { queueItem: item, eventKey: `${item.id}:${item.status}:${item.attempts}` });\n }\n\n private async handleStatusUpdated(payload: unknown): Promise<void> {\n if (!payload || typeof payload !== 'object') return;\n const rec = payload as { goal?: unknown; status?: unknown; goalId?: unknown; previousStatus?: unknown };\n const goal =\n this.isGoal(rec.goal)\n ? rec.goal\n : typeof rec.goalId === 'string'\n ? this.goals.get(rec.goalId)\n : null;\n if (!goal) return;\n const status = typeof rec.status === 'string' ? rec.status : goal.status;\n const event = eventForGoalStatus(status as GoalWithDetails['status']);\n if (!event) return;\n if (rec.previousStatus === status) return;\n await this.notify(event, goal, { eventKey: `${goal.id}:${event}:${goal.updatedAt}` });\n }\n\n private async notify(\n event: GoalNotificationEvent,\n goal: GoalWithDetails,\n opts?: { queueItem?: GoalQueueItemSnapshot; eventKey?: string },\n ): Promise<void> {\n const policy = this.opts.getConfig().goals?.notifications;\n if (!policy?.enabled) return;\n if (!policy.events.includes(event)) return;\n\n const targets = await this.resolveTargets(event, goal);\n if (targets.length === 0) return;\n\n const text = formatGoalNotification(event, goal, { queueItem: opts?.queueItem });\n for (const target of targets) {\n const dedupeKey = `${opts?.eventKey ?? `${goal.id}:${event}`}:${targetKey(target)}`;\n if (this.sentKeys.has(dedupeKey)) continue;\n this.sentKeys.add(dedupeKey);\n await this.opts.send({ ...target, text }).catch((err) => {\n this.sentKeys.delete(dedupeKey);\n throw err;\n });\n }\n }\n\n private async resolveTargets(event: GoalNotificationEvent, goal: GoalWithDetails): Promise<GoalNotificationTarget[]> {\n const policy = this.opts.getConfig().goals?.notifications;\n if (!policy?.enabled) return [];\n const byKey = new Map<string, GoalNotificationTarget>();\n\n if (policy.includeLinkedSessions !== false && goal.activeSessionKey && this.opts.getSessionMetadata) {\n const metadata = await this.opts.getSessionMetadata(goal.activeSessionKey).catch((err) => {\n log.warn({ err, sessionKey: goal.activeSessionKey, goalId: goal.id }, 'Failed to load linked session metadata for goal notification');\n return null;\n });\n const routing = metadata?.routing;\n const eligible = routing && policy.channels.includes(routing.source);\n if (eligible) {\n const target: GoalNotificationTarget = {\n channel: routing.source,\n chatId: routing.peerId,\n accountId: routing.accountId,\n ...(routing.threadId ? { threadId: routing.threadId } : {}),\n };\n byKey.set(targetKey(target), target);\n }\n }\n\n for (const target of policy.targets ?? []) {\n const events = target.events;\n if (events && !events.includes(event)) continue;\n const normalized: GoalNotificationTarget = {\n channel: target.channel,\n chatId: target.chatId,\n ...(target.accountId ? { accountId: target.accountId } : {}),\n ...(target.threadId != null ? { threadId: target.threadId } : {}),\n ...(target.silent != null ? { silent: target.silent } : {}),\n };\n byKey.set(targetKey(normalized), normalized);\n }\n\n return [...byKey.values()];\n }\n\n private parseQueueItem(payload: unknown): GoalQueueItemSnapshot | null {\n if (!payload || typeof payload !== 'object') return null;\n const item = (payload as { item?: unknown }).item;\n if (!item || typeof item !== 'object') return null;\n const rec = item as Partial<GoalQueueItemSnapshot>;\n if (typeof rec.id !== 'string' || typeof rec.goalId !== 'string' || typeof rec.status !== 'string') return null;\n return rec as GoalQueueItemSnapshot;\n }\n\n private isGoal(value: unknown): value is GoalWithDetails {\n if (!value || typeof value !== 'object') return false;\n const rec = value as Partial<GoalWithDetails>;\n return typeof rec.id === 'string' && typeof rec.title === 'string' && Array.isArray(rec.checklist);\n }\n}\n"],"mappings":";;;;aACkD;AAOlD,MAAM,MAAM,aAAa,oBAAoB;AA6B7C,SAAS,oBAAoB,QAAuE;AAClG,KAAI,WAAW,SAAU,QAAO;AAChC,KAAI,WAAW,gBAAiB,QAAO;AACvC,KAAI,WAAW,YAAa,QAAO;AACnC,KAAI,WAAW,UAAW,QAAO;AACjC,QAAO;;AAGT,SAAS,mBAAmB,QAAiE;AAC3F,KAAI,WAAW,UAAU,WAAW,aAAa,WAAW,cAAe,QAAO;AAClF,QAAO;;AAGT,SAAS,UAAU,QAAwC;AACzD,QAAO;EACL,OAAO;EACP,OAAO;EACP,OAAO,aAAa;EACpB,OAAO,YAAY;EACpB,CAAC,KAAK,KAAS;;AAGlB,SAAS,uBAAuB,OAA8B,MAAuB,MAE1E;CAUT,MAAM,QAAQ,CAAC,GAAG;EARhB,MAAM;EACN,SAAS;EACT,aAAa;EACb,cAAc;EACd,aAAa;EACb,iBAAiB;EACjB,eAAe;EAEO,CAAC,OAAO,IAAI,KAAK,QAAQ;AACjD,KAAI,KAAK,cAAe,OAAM,KAAK,WAAW,KAAK,gBAAgB;AACnE,KAAI,KAAK,WAAY,OAAM,KAAK,SAAS,KAAK,aAAa;AAC3D,KAAI,KAAK,WAAW,UAAU,KAAK,UAAU,WAAW,KAAK,cAC3D,OAAM,KAAK,QAAQ,KAAK,UAAU,SAAS;AAE7C,KAAI,MAAM,WAAW,UAAW,OAAM,KAAK,UAAU,KAAK,UAAU,YAAY;CAChF,MAAM,WAAW,KAAK,UAAU,SAC5B,KAAK,UAAU,QAAQ,OAAO,GAAG,WAAW,eAAe,GAAG,WAAW,aAAa,CAAC,SACvF;AACJ,KAAI,KAAK,UAAU,OAAQ,OAAM,KAAK,cAAc,SAAS,GAAG,KAAK,UAAU,SAAS;AACxF,OAAM,KAAK,UAAU,KAAK,UAAU,GAAG,KAAK,WAAW;AACvD,QAAO,MAAM,KAAK,KAAK;;AAGzB,IAAa,0BAAb,MAAqC;CACnC,QAAyB,IAAI,aAAa;CAC1C,2BAA4B,IAAI,KAAa;CAE7C,YAAY,MAAuD;AAAtC,OAAA,OAAA;;CAE7B,mBAAmB,MAAc,SAAwB;AACvD,MAAI,SAAS,sBAAsB;AAC5B,QAAK,mBAAmB,QAAQ,CAAC,OAAO,QAAQ;AACnD,QAAI,KAAK,EAAE,KAAK,EAAE,iCAAiC;KACnD;AACF;;AAEF,MAAI,SAAS,sBACN,MAAK,oBAAoB,QAAQ,CAAC,OAAO,QAAQ;AACpD,OAAI,KAAK,EAAE,KAAK,EAAE,kCAAkC;IACpD;;CAIN,MAAc,mBAAmB,SAAiC;EAChE,MAAM,OAAO,KAAK,eAAe,QAAQ;AACzC,MAAI,CAAC,KAAM;EACX,MAAM,QAAQ,oBAAoB,KAAK,OAAO;AAC9C,MAAI,CAAC,MAAO;EACZ,MAAM,OAAO,KAAK,MAAM,IAAI,KAAK,OAAO;AACxC,MAAI,CAAC,KAAM;AACX,QAAM,KAAK,OAAO,OAAO,MAAM;GAAE,WAAW;GAAM,UAAU,GAAG,KAAK,GAAG,GAAG,KAAK,OAAO,GAAG,KAAK;GAAY,CAAC;;CAG7G,MAAc,oBAAoB,SAAiC;AACjE,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU;EAC7C,MAAM,MAAM;EACZ,MAAM,OACJ,KAAK,OAAO,IAAI,KAAK,GACjB,IAAI,OACJ,OAAO,IAAI,WAAW,WACpB,KAAK,MAAM,IAAI,IAAI,OAAO,GAC1B;AACR,MAAI,CAAC,KAAM;EACX,MAAM,SAAS,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS,KAAK;EAClE,MAAM,QAAQ,mBAAmB,OAAoC;AACrE,MAAI,CAAC,MAAO;AACZ,MAAI,IAAI,mBAAmB,OAAQ;AACnC,QAAM,KAAK,OAAO,OAAO,MAAM,EAAE,UAAU,GAAG,KAAK,GAAG,GAAG,MAAM,GAAG,KAAK,aAAa,CAAC;;CAGvF,MAAc,OACZ,OACA,MACA,MACe;EACf,MAAM,SAAS,KAAK,KAAK,WAAW,CAAC,OAAO;AAC5C,MAAI,CAAC,QAAQ,QAAS;AACtB,MAAI,CAAC,OAAO,OAAO,SAAS,MAAM,CAAE;EAEpC,MAAM,UAAU,MAAM,KAAK,eAAe,OAAO,KAAK;AACtD,MAAI,QAAQ,WAAW,EAAG;EAE1B,MAAM,OAAO,uBAAuB,OAAO,MAAM,EAAE,WAAW,MAAM,WAAW,CAAC;AAChF,OAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,YAAY,GAAG,MAAM,YAAY,GAAG,KAAK,GAAG,GAAG,QAAQ,GAAG,UAAU,OAAO;AACjF,OAAI,KAAK,SAAS,IAAI,UAAU,CAAE;AAClC,QAAK,SAAS,IAAI,UAAU;AAC5B,SAAM,KAAK,KAAK,KAAK;IAAE,GAAG;IAAQ;IAAM,CAAC,CAAC,OAAO,QAAQ;AACvD,SAAK,SAAS,OAAO,UAAU;AAC/B,UAAM;KACN;;;CAIN,MAAc,eAAe,OAA8B,MAA0D;EACnH,MAAM,SAAS,KAAK,KAAK,WAAW,CAAC,OAAO;AAC5C,MAAI,CAAC,QAAQ,QAAS,QAAO,EAAE;EAC/B,MAAM,wBAAQ,IAAI,KAAqC;AAEvD,MAAI,OAAO,0BAA0B,SAAS,KAAK,oBAAoB,KAAK,KAAK,oBAAoB;GAKnG,MAAM,WAAU,MAJO,KAAK,KAAK,mBAAmB,KAAK,iBAAiB,CAAC,OAAO,QAAQ;AACxF,QAAI,KAAK;KAAE;KAAK,YAAY,KAAK;KAAkB,QAAQ,KAAK;KAAI,EAAE,+DAA+D;AACrI,WAAO;KACP,GACwB;AAE1B,OADiB,WAAW,OAAO,SAAS,SAAS,QAAQ,OAAO,EACtD;IACZ,MAAM,SAAiC;KACrC,SAAS,QAAQ;KACjB,QAAQ,QAAQ;KAChB,WAAW,QAAQ;KACnB,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,UAAU,GAAG,EAAE;KAC3D;AACD,UAAM,IAAI,UAAU,OAAO,EAAE,OAAO;;;AAIxC,OAAK,MAAM,UAAU,OAAO,WAAW,EAAE,EAAE;GACzC,MAAM,SAAS,OAAO;AACtB,OAAI,UAAU,CAAC,OAAO,SAAS,MAAM,CAAE;GACvC,MAAM,aAAqC;IACzC,SAAS,OAAO;IAChB,QAAQ,OAAO;IACf,GAAI,OAAO,YAAY,EAAE,WAAW,OAAO,WAAW,GAAG,EAAE;IAC3D,GAAI,OAAO,YAAY,OAAO,EAAE,UAAU,OAAO,UAAU,GAAG,EAAE;IAChE,GAAI,OAAO,UAAU,OAAO,EAAE,QAAQ,OAAO,QAAQ,GAAG,EAAE;IAC3D;AACD,SAAM,IAAI,UAAU,WAAW,EAAE,WAAW;;AAG9C,SAAO,CAAC,GAAG,MAAM,QAAQ,CAAC;;CAG5B,eAAuB,SAAgD;AACrE,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;EACpD,MAAM,OAAQ,QAA+B;AAC7C,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;EAC9C,MAAM,MAAM;AACZ,MAAI,OAAO,IAAI,OAAO,YAAY,OAAO,IAAI,WAAW,YAAY,OAAO,IAAI,WAAW,SAAU,QAAO;AAC3G,SAAO;;CAGT,OAAe,OAA0C;AACvD,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;EAChD,MAAM,MAAM;AACZ,SAAO,OAAO,IAAI,OAAO,YAAY,OAAO,IAAI,UAAU,YAAY,MAAM,QAAQ,IAAI,UAAU"}
@@ -1,11 +1,6 @@
1
- import { init_session_key, parseSessionKey } from "../routing/session-key.js";
2
1
  import { defaultMaxTurns } from "../agent/goals/state.js";
3
2
  import { GoalStore } from "./goal-store.js";
4
3
  //#region src/goals/goal-service.ts
5
- init_session_key();
6
- function resolveAgentId(sessionKey, fallback = "main") {
7
- return parseSessionKey(sessionKey)?.agentId ?? fallback;
8
- }
9
4
  function normalizeTitle(title) {
10
5
  const t = title.trim();
11
6
  if (!t) throw new Error("Goal title is required");
@@ -28,7 +23,7 @@ var GoalService = class {
28
23
  return this.store.create({
29
24
  title: normalizeTitle(input.title),
30
25
  description: input.description,
31
- agentId: input.agentId ?? (sessionKey ? resolveAgentId(sessionKey) : "main"),
26
+ agentId: input.agentId ?? "main",
32
27
  sessionKey,
33
28
  priority: input.priority,
34
29
  deadlineAt: input.deadlineAt,
@@ -1 +1 @@
1
- {"version":3,"file":"goal-service.js","names":[],"sources":["../../../src/goals/goal-service.ts"],"sourcesContent":["import type { Config } from '../config/schema.js';\nimport { defaultMaxTurns } from '../agent/goals/state.js';\nimport { parseSessionKey } from '../routing/session-key.js';\nimport { GoalStore } from './goal-store.js';\nimport type {\n CreateGoalInput,\n Goal,\n GoalChecklistAddedBy,\n GoalChecklistStatus,\n GoalJudgeDecision,\n GoalListQuery,\n GoalSource,\n GoalStatus,\n GoalUiLocale,\n GoalWithDetails,\n} from './types.js';\n\nfunction resolveAgentId(sessionKey: string, fallback = 'main'): string {\n return parseSessionKey(sessionKey)?.agentId ?? fallback;\n}\n\nfunction normalizeTitle(title: string): string {\n const t = title.trim();\n if (!t) throw new Error('Goal title is required');\n return t;\n}\n\nfunction toTerminalStatus(verdict: GoalJudgeDecision['verdict']): GoalStatus {\n if (verdict === 'done') return 'done';\n if (verdict === 'blocked') return 'blocked';\n if (verdict === 'needs_input') return 'needs_input';\n return 'active';\n}\n\nexport class GoalService {\n private readonly store: GoalStore;\n\n constructor(store = new GoalStore()) {\n this.store = store;\n }\n\n create(input: Omit<CreateGoalInput, 'maxTurns' | 'agentId'> & {\n maxTurns?: number;\n agentId?: string;\n config?: Config;\n }): Goal {\n const sessionKey = input.sessionKey;\n const cfg = input.config?.goals;\n return this.store.create({\n title: normalizeTitle(input.title),\n description: input.description,\n agentId: input.agentId ?? (sessionKey ? resolveAgentId(sessionKey) : 'main'),\n sessionKey,\n priority: input.priority,\n deadlineAt: input.deadlineAt,\n judgeModelRef: input.judgeModelRef ?? cfg?.judgeModelRef,\n maxTurns: input.maxTurns ?? defaultMaxTurns(cfg),\n uiLocale: input.uiLocale,\n source: input.source,\n });\n }\n\n get(goalId: string): GoalWithDetails | null {\n const goal = this.store.get(goalId);\n if (!goal) return null;\n return {\n ...goal,\n checklist: this.store.listChecklist(goalId),\n latestRun: this.store.listRuns(goalId, 1)[0],\n };\n }\n\n getActiveForSession(sessionKey: string): GoalWithDetails | null {\n const goal = this.store.getActiveForSession(sessionKey);\n return goal ? this.get(goal.id) : null;\n }\n\n list(query: GoalListQuery = {}): GoalWithDetails[] {\n return this.store.list(query).map((goal) => ({\n ...goal,\n checklist: this.store.listChecklist(goal.id),\n latestRun: this.store.listRuns(goal.id, 1)[0],\n }));\n }\n\n update(goalId: string, patch: Partial<Pick<\n Goal,\n | 'title'\n | 'description'\n | 'priority'\n | 'deadlineAt'\n | 'maxTurns'\n | 'judgeModelRef'\n | 'nextAction'\n | 'blockedReason'\n | 'activeSessionKey'\n | 'uiLocale'\n >>): GoalWithDetails | null {\n const normalized: typeof patch = { ...patch };\n if (typeof normalized.title === 'string') {\n normalized.title = normalizeTitle(normalized.title);\n }\n if (typeof normalized.description === 'string') {\n normalized.description = normalized.description.trim() || undefined;\n }\n if (typeof normalized.judgeModelRef === 'string') {\n normalized.judgeModelRef = normalized.judgeModelRef.trim() || undefined;\n }\n if (typeof normalized.nextAction === 'string') {\n normalized.nextAction = normalized.nextAction.trim() || undefined;\n }\n if (typeof normalized.blockedReason === 'string') {\n normalized.blockedReason = normalized.blockedReason.trim() || undefined;\n }\n if (typeof normalized.maxTurns === 'number') {\n normalized.maxTurns = Math.max(1, Math.min(500, Math.floor(normalized.maxTurns)));\n }\n const goal = this.store.update(goalId, normalized);\n return goal ? this.get(goal.id) : null;\n }\n\n setStatus(goalId: string, status: GoalStatus, opts?: { reason?: string }): GoalWithDetails | null {\n const now = Date.now();\n const goal = this.store.update(goalId, {\n status,\n completedAt: status === 'done' ? now : undefined,\n archivedAt: status === 'archived' ? now : undefined,\n blockedReason: status === 'blocked' || status === 'needs_input' ? opts?.reason : undefined,\n });\n return goal ? this.get(goal.id) : null;\n }\n\n pause(goalId: string, reason = 'user-paused'): GoalWithDetails | null {\n const goal = this.store.update(goalId, {\n status: 'paused',\n blockedReason: reason,\n completedAt: undefined,\n archivedAt: undefined,\n });\n return goal ? this.get(goal.id) : null;\n }\n\n resume(goalId: string): GoalWithDetails | null {\n const goal = this.store.update(goalId, {\n status: 'active',\n blockedReason: undefined,\n completedAt: undefined,\n archivedAt: undefined,\n turnsUsed: 0,\n });\n return goal ? this.get(goal.id) : null;\n }\n\n reopen(goalId: string): GoalWithDetails | null {\n const goal = this.store.update(goalId, {\n status: 'active',\n blockedReason: undefined,\n completedAt: undefined,\n archivedAt: undefined,\n turnsUsed: 0,\n });\n return goal ? this.get(goal.id) : null;\n }\n\n complete(goalId: string): GoalWithDetails | null {\n return this.setStatus(goalId, 'done');\n }\n\n archive(goalId: string): GoalWithDetails | null {\n return this.setStatus(goalId, 'archived');\n }\n\n unarchive(goalId: string): GoalWithDetails | null {\n const goal = this.store.update(goalId, {\n status: 'paused',\n archivedAt: undefined,\n completedAt: undefined,\n blockedReason: undefined,\n });\n return goal ? this.get(goal.id) : null;\n }\n\n attachSession(goalId: string, sessionKey: string): GoalWithDetails | null {\n const goal = this.store.update(goalId, { activeSessionKey: sessionKey });\n return goal ? this.get(goal.id) : null;\n }\n\n updateChecklist(goalId: string, op: {\n type: 'add';\n text: string;\n addedBy?: GoalChecklistAddedBy;\n } | {\n type: 'mark';\n itemId: string;\n status: GoalChecklistStatus;\n evidenceSummary?: string;\n } | {\n type: 'remove';\n itemId: string;\n } | {\n type: 'reset';\n }): GoalWithDetails | null {\n if (op.type === 'add') {\n const text = op.text.trim();\n if (!text) throw new Error('Checklist item text is required');\n this.store.addChecklistItem({ goalId, text, addedBy: op.addedBy ?? 'user' });\n } else if (op.type === 'mark') {\n this.store.updateChecklistItem(op.itemId, {\n status: op.status,\n evidenceSummary: op.evidenceSummary,\n });\n } else if (op.type === 'remove') {\n this.store.removeChecklistItem(op.itemId);\n } else {\n this.store.clearChecklist(goalId);\n }\n return this.get(goalId);\n }\n\n recordJudgeDecision(input: {\n goalId: string;\n sessionKey: string;\n source: GoalSource;\n decision: GoalJudgeDecision;\n }): GoalWithDetails | null {\n const goal = this.store.get(input.goalId);\n if (!goal) return null;\n const nextStatus = toTerminalStatus(input.decision.verdict);\n const turnsUsed = goal.turnsUsed + 1;\n const status = turnsUsed >= goal.maxTurns && nextStatus === 'active' ? 'paused' : nextStatus;\n const reason =\n status === 'paused' && turnsUsed >= goal.maxTurns\n ? `turn budget exhausted (${turnsUsed}/${goal.maxTurns})`\n : input.decision.reason;\n\n const run = this.store.appendRun({\n goalId: input.goalId,\n sessionKey: input.sessionKey,\n source: input.source,\n status: 'succeeded',\n finishedAt: Date.now(),\n verdict: input.decision.verdict,\n reason: input.decision.reason,\n nextAction: input.decision.nextAction,\n assistantPreview: input.decision.assistantPreview,\n checklistProgress: input.decision.checklistProgress,\n confidence: input.decision.confidence,\n missingEvidence: input.decision.missingEvidence,\n userQuestion: input.decision.userQuestion,\n completedChecklistItemIds: input.decision.completedChecklistItemIds,\n });\n\n this.store.update(input.goalId, {\n status,\n turnsUsed,\n currentRunId: run.id,\n nextAction: input.decision.nextAction,\n blockedReason: status === 'blocked' || status === 'needs_input' || status === 'paused' ? reason : undefined,\n completedAt: status === 'done' ? Date.now() : undefined,\n });\n\n return this.get(input.goalId);\n }\n\n syncPostTurnState(input: {\n goalId: string;\n sessionKey: string;\n source: GoalSource;\n status: GoalStatus;\n turnsUsed: number;\n maxTurns: number;\n reason?: string;\n nextAction?: string;\n assistantPreview?: string;\n verdict: 'continue' | 'done' | 'blocked' | 'needs_input' | 'decompose';\n confidence?: number;\n missingEvidence?: string[];\n userQuestion?: string;\n completedChecklistItemIds?: string[];\n checklist?: Array<{\n text: string;\n status: 'pending' | 'completed' | 'impossible';\n addedBy: 'user' | 'judge';\n addedAt?: number;\n completedAt?: number;\n evidenceSummary?: string;\n }>;\n }): GoalWithDetails | null {\n const goal = this.store.get(input.goalId);\n if (!goal) return null;\n if (input.checklist) {\n this.store.replaceChecklist(input.goalId, input.checklist);\n }\n const total = input.checklist?.length ?? this.store.listChecklist(input.goalId).length;\n const done = (input.checklist ?? this.store.listChecklist(input.goalId)).filter(\n (it) => it.status === 'completed' || it.status === 'impossible',\n ).length;\n const run = this.store.appendRun({\n goalId: input.goalId,\n sessionKey: input.sessionKey,\n source: input.source,\n status: 'succeeded',\n finishedAt: Date.now(),\n verdict: input.verdict,\n reason: input.reason,\n nextAction: input.nextAction,\n assistantPreview: input.assistantPreview,\n checklistProgress: total > 0 ? { done, total } : undefined,\n confidence: input.confidence,\n missingEvidence: input.missingEvidence,\n userQuestion: input.userQuestion,\n completedChecklistItemIds: input.completedChecklistItemIds,\n });\n for (const item of input.checklist ?? []) {\n if ((item.status === 'completed' || item.status === 'impossible') && item.evidenceSummary?.trim()) {\n this.store.addEvidence({\n goalId: input.goalId,\n runId: run.id,\n kind: 'message',\n title: `Checklist evidence: ${item.text.slice(0, 80)}`,\n summary: item.evidenceSummary.trim(),\n });\n }\n }\n for (const missing of input.missingEvidence ?? []) {\n if (!missing.trim()) continue;\n this.store.addEvidence({\n goalId: input.goalId,\n runId: run.id,\n kind: 'message',\n title: `Missing evidence: ${missing.slice(0, 80)}`,\n summary: missing,\n });\n }\n if (input.userQuestion?.trim()) {\n this.store.addEvidence({\n goalId: input.goalId,\n runId: run.id,\n kind: 'message',\n title: 'User input requested',\n summary: input.userQuestion.trim(),\n });\n }\n this.store.update(input.goalId, {\n status: input.status,\n turnsUsed: input.turnsUsed,\n maxTurns: input.maxTurns,\n currentRunId: run.id,\n nextAction: input.nextAction,\n blockedReason:\n input.status === 'blocked' || input.status === 'needs_input' || input.status === 'paused'\n ? input.reason\n : undefined,\n completedAt: input.status === 'done' ? Date.now() : undefined,\n });\n return this.get(input.goalId);\n }\n\n listRuns(goalId: string, limit?: number) {\n return this.store.listRuns(goalId, limit);\n }\n\n listRunsForSession(sessionKey: string, limit?: number) {\n return this.store.listRunsForSession(sessionKey, limit);\n }\n\n listEvents(goalId: string, limit?: number) {\n return this.store.listEvents(goalId, limit);\n }\n\n addEvidence(input: Parameters<GoalStore['addEvidence']>[0]) {\n return this.store.addEvidence(input);\n }\n\n listEvidence(goalId: string, limit?: number) {\n return this.store.listEvidence(goalId, limit);\n }\n}\n\nexport function normalizeGoalUiLocale(raw: unknown): GoalUiLocale | undefined {\n return raw === 'en' || raw === 'zh' ? raw : undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"goal-service.js","names":[],"sources":["../../../src/goals/goal-service.ts"],"sourcesContent":["import type { Config } from '../config/schema.js';\nimport { defaultMaxTurns } from '../agent/goals/state.js';\nimport { GoalStore } from './goal-store.js';\nimport type {\n CreateGoalInput,\n Goal,\n GoalChecklistAddedBy,\n GoalChecklistStatus,\n GoalJudgeDecision,\n GoalListQuery,\n GoalSource,\n GoalStatus,\n GoalUiLocale,\n GoalWithDetails,\n} from './types.js';\n\nfunction normalizeTitle(title: string): string {\n const t = title.trim();\n if (!t) throw new Error('Goal title is required');\n return t;\n}\n\nfunction toTerminalStatus(verdict: GoalJudgeDecision['verdict']): GoalStatus {\n if (verdict === 'done') return 'done';\n if (verdict === 'blocked') return 'blocked';\n if (verdict === 'needs_input') return 'needs_input';\n return 'active';\n}\n\nexport class GoalService {\n private readonly store: GoalStore;\n\n constructor(store = new GoalStore()) {\n this.store = store;\n }\n\n create(input: Omit<CreateGoalInput, 'maxTurns' | 'agentId'> & {\n maxTurns?: number;\n agentId?: string;\n config?: Config;\n }): Goal {\n const sessionKey = input.sessionKey;\n const cfg = input.config?.goals;\n return this.store.create({\n title: normalizeTitle(input.title),\n description: input.description,\n agentId: input.agentId ?? 'main',\n sessionKey,\n priority: input.priority,\n deadlineAt: input.deadlineAt,\n judgeModelRef: input.judgeModelRef ?? cfg?.judgeModelRef,\n maxTurns: input.maxTurns ?? defaultMaxTurns(cfg),\n uiLocale: input.uiLocale,\n source: input.source,\n });\n }\n\n get(goalId: string): GoalWithDetails | null {\n const goal = this.store.get(goalId);\n if (!goal) return null;\n return {\n ...goal,\n checklist: this.store.listChecklist(goalId),\n latestRun: this.store.listRuns(goalId, 1)[0],\n };\n }\n\n getActiveForSession(sessionKey: string): GoalWithDetails | null {\n const goal = this.store.getActiveForSession(sessionKey);\n return goal ? this.get(goal.id) : null;\n }\n\n list(query: GoalListQuery = {}): GoalWithDetails[] {\n return this.store.list(query).map((goal) => ({\n ...goal,\n checklist: this.store.listChecklist(goal.id),\n latestRun: this.store.listRuns(goal.id, 1)[0],\n }));\n }\n\n update(goalId: string, patch: Partial<Pick<\n Goal,\n | 'title'\n | 'description'\n | 'priority'\n | 'deadlineAt'\n | 'maxTurns'\n | 'judgeModelRef'\n | 'nextAction'\n | 'blockedReason'\n | 'activeSessionKey'\n | 'uiLocale'\n >>): GoalWithDetails | null {\n const normalized: typeof patch = { ...patch };\n if (typeof normalized.title === 'string') {\n normalized.title = normalizeTitle(normalized.title);\n }\n if (typeof normalized.description === 'string') {\n normalized.description = normalized.description.trim() || undefined;\n }\n if (typeof normalized.judgeModelRef === 'string') {\n normalized.judgeModelRef = normalized.judgeModelRef.trim() || undefined;\n }\n if (typeof normalized.nextAction === 'string') {\n normalized.nextAction = normalized.nextAction.trim() || undefined;\n }\n if (typeof normalized.blockedReason === 'string') {\n normalized.blockedReason = normalized.blockedReason.trim() || undefined;\n }\n if (typeof normalized.maxTurns === 'number') {\n normalized.maxTurns = Math.max(1, Math.min(500, Math.floor(normalized.maxTurns)));\n }\n const goal = this.store.update(goalId, normalized);\n return goal ? this.get(goal.id) : null;\n }\n\n setStatus(goalId: string, status: GoalStatus, opts?: { reason?: string }): GoalWithDetails | null {\n const now = Date.now();\n const goal = this.store.update(goalId, {\n status,\n completedAt: status === 'done' ? now : undefined,\n archivedAt: status === 'archived' ? now : undefined,\n blockedReason: status === 'blocked' || status === 'needs_input' ? opts?.reason : undefined,\n });\n return goal ? this.get(goal.id) : null;\n }\n\n pause(goalId: string, reason = 'user-paused'): GoalWithDetails | null {\n const goal = this.store.update(goalId, {\n status: 'paused',\n blockedReason: reason,\n completedAt: undefined,\n archivedAt: undefined,\n });\n return goal ? this.get(goal.id) : null;\n }\n\n resume(goalId: string): GoalWithDetails | null {\n const goal = this.store.update(goalId, {\n status: 'active',\n blockedReason: undefined,\n completedAt: undefined,\n archivedAt: undefined,\n turnsUsed: 0,\n });\n return goal ? this.get(goal.id) : null;\n }\n\n reopen(goalId: string): GoalWithDetails | null {\n const goal = this.store.update(goalId, {\n status: 'active',\n blockedReason: undefined,\n completedAt: undefined,\n archivedAt: undefined,\n turnsUsed: 0,\n });\n return goal ? this.get(goal.id) : null;\n }\n\n complete(goalId: string): GoalWithDetails | null {\n return this.setStatus(goalId, 'done');\n }\n\n archive(goalId: string): GoalWithDetails | null {\n return this.setStatus(goalId, 'archived');\n }\n\n unarchive(goalId: string): GoalWithDetails | null {\n const goal = this.store.update(goalId, {\n status: 'paused',\n archivedAt: undefined,\n completedAt: undefined,\n blockedReason: undefined,\n });\n return goal ? this.get(goal.id) : null;\n }\n\n attachSession(goalId: string, sessionKey: string): GoalWithDetails | null {\n const goal = this.store.update(goalId, { activeSessionKey: sessionKey });\n return goal ? this.get(goal.id) : null;\n }\n\n updateChecklist(goalId: string, op: {\n type: 'add';\n text: string;\n addedBy?: GoalChecklistAddedBy;\n } | {\n type: 'mark';\n itemId: string;\n status: GoalChecklistStatus;\n evidenceSummary?: string;\n } | {\n type: 'remove';\n itemId: string;\n } | {\n type: 'reset';\n }): GoalWithDetails | null {\n if (op.type === 'add') {\n const text = op.text.trim();\n if (!text) throw new Error('Checklist item text is required');\n this.store.addChecklistItem({ goalId, text, addedBy: op.addedBy ?? 'user' });\n } else if (op.type === 'mark') {\n this.store.updateChecklistItem(op.itemId, {\n status: op.status,\n evidenceSummary: op.evidenceSummary,\n });\n } else if (op.type === 'remove') {\n this.store.removeChecklistItem(op.itemId);\n } else {\n this.store.clearChecklist(goalId);\n }\n return this.get(goalId);\n }\n\n recordJudgeDecision(input: {\n goalId: string;\n sessionKey: string;\n source: GoalSource;\n decision: GoalJudgeDecision;\n }): GoalWithDetails | null {\n const goal = this.store.get(input.goalId);\n if (!goal) return null;\n const nextStatus = toTerminalStatus(input.decision.verdict);\n const turnsUsed = goal.turnsUsed + 1;\n const status = turnsUsed >= goal.maxTurns && nextStatus === 'active' ? 'paused' : nextStatus;\n const reason =\n status === 'paused' && turnsUsed >= goal.maxTurns\n ? `turn budget exhausted (${turnsUsed}/${goal.maxTurns})`\n : input.decision.reason;\n\n const run = this.store.appendRun({\n goalId: input.goalId,\n sessionKey: input.sessionKey,\n source: input.source,\n status: 'succeeded',\n finishedAt: Date.now(),\n verdict: input.decision.verdict,\n reason: input.decision.reason,\n nextAction: input.decision.nextAction,\n assistantPreview: input.decision.assistantPreview,\n checklistProgress: input.decision.checklistProgress,\n confidence: input.decision.confidence,\n missingEvidence: input.decision.missingEvidence,\n userQuestion: input.decision.userQuestion,\n completedChecklistItemIds: input.decision.completedChecklistItemIds,\n });\n\n this.store.update(input.goalId, {\n status,\n turnsUsed,\n currentRunId: run.id,\n nextAction: input.decision.nextAction,\n blockedReason: status === 'blocked' || status === 'needs_input' || status === 'paused' ? reason : undefined,\n completedAt: status === 'done' ? Date.now() : undefined,\n });\n\n return this.get(input.goalId);\n }\n\n syncPostTurnState(input: {\n goalId: string;\n sessionKey: string;\n source: GoalSource;\n status: GoalStatus;\n turnsUsed: number;\n maxTurns: number;\n reason?: string;\n nextAction?: string;\n assistantPreview?: string;\n verdict: 'continue' | 'done' | 'blocked' | 'needs_input' | 'decompose';\n confidence?: number;\n missingEvidence?: string[];\n userQuestion?: string;\n completedChecklistItemIds?: string[];\n checklist?: Array<{\n text: string;\n status: 'pending' | 'completed' | 'impossible';\n addedBy: 'user' | 'judge';\n addedAt?: number;\n completedAt?: number;\n evidenceSummary?: string;\n }>;\n }): GoalWithDetails | null {\n const goal = this.store.get(input.goalId);\n if (!goal) return null;\n if (input.checklist) {\n this.store.replaceChecklist(input.goalId, input.checklist);\n }\n const total = input.checklist?.length ?? this.store.listChecklist(input.goalId).length;\n const done = (input.checklist ?? this.store.listChecklist(input.goalId)).filter(\n (it) => it.status === 'completed' || it.status === 'impossible',\n ).length;\n const run = this.store.appendRun({\n goalId: input.goalId,\n sessionKey: input.sessionKey,\n source: input.source,\n status: 'succeeded',\n finishedAt: Date.now(),\n verdict: input.verdict,\n reason: input.reason,\n nextAction: input.nextAction,\n assistantPreview: input.assistantPreview,\n checklistProgress: total > 0 ? { done, total } : undefined,\n confidence: input.confidence,\n missingEvidence: input.missingEvidence,\n userQuestion: input.userQuestion,\n completedChecklistItemIds: input.completedChecklistItemIds,\n });\n for (const item of input.checklist ?? []) {\n if ((item.status === 'completed' || item.status === 'impossible') && item.evidenceSummary?.trim()) {\n this.store.addEvidence({\n goalId: input.goalId,\n runId: run.id,\n kind: 'message',\n title: `Checklist evidence: ${item.text.slice(0, 80)}`,\n summary: item.evidenceSummary.trim(),\n });\n }\n }\n for (const missing of input.missingEvidence ?? []) {\n if (!missing.trim()) continue;\n this.store.addEvidence({\n goalId: input.goalId,\n runId: run.id,\n kind: 'message',\n title: `Missing evidence: ${missing.slice(0, 80)}`,\n summary: missing,\n });\n }\n if (input.userQuestion?.trim()) {\n this.store.addEvidence({\n goalId: input.goalId,\n runId: run.id,\n kind: 'message',\n title: 'User input requested',\n summary: input.userQuestion.trim(),\n });\n }\n this.store.update(input.goalId, {\n status: input.status,\n turnsUsed: input.turnsUsed,\n maxTurns: input.maxTurns,\n currentRunId: run.id,\n nextAction: input.nextAction,\n blockedReason:\n input.status === 'blocked' || input.status === 'needs_input' || input.status === 'paused'\n ? input.reason\n : undefined,\n completedAt: input.status === 'done' ? Date.now() : undefined,\n });\n return this.get(input.goalId);\n }\n\n listRuns(goalId: string, limit?: number) {\n return this.store.listRuns(goalId, limit);\n }\n\n listRunsForSession(sessionKey: string, limit?: number) {\n return this.store.listRunsForSession(sessionKey, limit);\n }\n\n listEvents(goalId: string, limit?: number) {\n return this.store.listEvents(goalId, limit);\n }\n\n addEvidence(input: Parameters<GoalStore['addEvidence']>[0]) {\n return this.store.addEvidence(input);\n }\n\n listEvidence(goalId: string, limit?: number) {\n return this.store.listEvidence(goalId, limit);\n }\n}\n\nexport function normalizeGoalUiLocale(raw: unknown): GoalUiLocale | undefined {\n return raw === 'en' || raw === 'zh' ? raw : undefined;\n}\n"],"mappings":"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"}
@@ -16,6 +16,7 @@ const TRUST_REQUIRING_XOPC_PROJECT_RESOURCES = [
16
16
  "APPEND_SYSTEM.md",
17
17
  "workspace.json"
18
18
  ];
19
+ const TRUST_REQUIRING_PROJECT_ROOT_RESOURCES = ["AGENTS.md"];
19
20
  function canonicalizePath(path) {
20
21
  const resolved = resolve(path);
21
22
  try {
@@ -147,6 +148,7 @@ function getProjectTrustOptions(cwd, options = {}) {
147
148
  function hasTrustRequiringProjectResources(cwd) {
148
149
  const userAgentsSkillsDir = join(canonicalizePath(process.env.HOME || homedir()), ".agents", "skills");
149
150
  let currentDir = canonicalizePath(cwd);
151
+ if (TRUST_REQUIRING_PROJECT_ROOT_RESOURCES.some((entry) => existsSync(join(currentDir, entry)))) return true;
150
152
  const xopcProjectDir = join(currentDir, ".xopc");
151
153
  if (TRUST_REQUIRING_XOPC_PROJECT_RESOURCES.some((entry) => existsSync(join(xopcProjectDir, entry)))) return true;
152
154
  while (true) {
@@ -1 +1 @@
1
- {"version":3,"file":"trust-store.js","names":["resolvePath"],"sources":["../../../src/project-trust/trust-store.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join, resolve as resolvePath } from 'node:path';\n\nimport lockfile from 'proper-lockfile';\n\nimport { resolveStateDir } from '../config/paths.js';\n\nexport type ProjectTrustDecision = boolean | null;\n\nexport interface ProjectTrustStoreEntry {\n path: string;\n decision: boolean;\n}\n\nexport interface ProjectTrustUpdate {\n path: string;\n decision: ProjectTrustDecision;\n}\n\nexport interface ProjectTrustOption {\n label: string;\n trusted: boolean;\n updates: ProjectTrustUpdate[];\n savedPath?: string;\n}\n\ntype TrustFile = Record<string, boolean | null | undefined>;\n\nconst TRUST_REQUIRING_XOPC_PROJECT_RESOURCES = [\n 'settings.json',\n 'extensions',\n 'skills',\n 'prompts',\n 'themes',\n 'SYSTEM.md',\n 'APPEND_SYSTEM.md',\n 'workspace.json',\n] as const;\n\nfunction canonicalizePath(path: string): string {\n const resolved = resolvePath(path);\n try {\n return realpathSync.native(resolved);\n } catch {\n return resolved;\n }\n}\n\nfunction normalizeCwd(cwd: string): string {\n return canonicalizePath(cwd);\n}\n\nfunction readTrustFile(path: string): TrustFile {\n if (!existsSync(path)) return {};\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(path, 'utf8'));\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to read project trust store ${path}: ${errorMessage}`);\n }\n\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new Error(`Invalid project trust store ${path}: expected an object`);\n }\n\n const data: TrustFile = {};\n for (const [key, value] of Object.entries(parsed)) {\n if (value !== true && value !== false && value !== null) {\n throw new Error(`Invalid project trust store ${path}: value for ${JSON.stringify(key)} must be true, false, or null`);\n }\n data[normalizeCwd(key)] = value;\n }\n return data;\n}\n\nfunction writeTrustFile(path: string, data: TrustFile): void {\n const sorted: TrustFile = {};\n for (const key of Object.keys(data).sort()) {\n const value = data[key];\n if (value === true || value === false || value === null) {\n sorted[key] = value;\n }\n }\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, `${JSON.stringify(sorted, null, 2)}\\n`, 'utf8');\n}\n\nfunction acquireTrustLockSync(path: string): () => void {\n const trustDir = dirname(path);\n mkdirSync(trustDir, { recursive: true });\n const maxAttempts = 10;\n const delayMs = 20;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return lockfile.lockSync(trustDir, { realpath: false, lockfilePath: `${path}.lock` });\n } catch (err) {\n const code =\n typeof err === 'object' && err !== null && 'code' in err\n ? String((err as { code?: unknown }).code)\n : undefined;\n if (code !== 'ELOCKED' || attempt === maxAttempts) {\n throw err;\n }\n const startedAt = Date.now();\n while (Date.now() - startedAt < delayMs) {\n // Synchronous callers need a tiny bounded wait while another TUI updates trust.json.\n }\n }\n }\n\n throw new Error('Failed to acquire project trust store lock');\n}\n\nfunction withTrustFileLock<T>(path: string, fn: () => T): T {\n const release = acquireTrustLockSync(path);\n try {\n return fn();\n } finally {\n release();\n }\n}\n\nfunction findNearestTrustEntry(data: TrustFile, cwd: string): ProjectTrustStoreEntry | null {\n let currentDir = normalizeCwd(cwd);\n while (true) {\n const value = data[currentDir];\n if (value === true || value === false) {\n return { path: currentDir, decision: value };\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) return null;\n currentDir = parentDir;\n }\n}\n\nexport function resolveProjectTrustStorePath(): string {\n return join(resolveStateDir(), 'trust.json');\n}\n\nexport function getProjectTrustParentPath(cwd: string): string | undefined {\n const trustPath = normalizeCwd(cwd);\n const parentDir = dirname(trustPath);\n return parentDir === trustPath ? undefined : parentDir;\n}\n\nexport function getProjectTrustOptions(\n cwd: string,\n options: { includeSessionOnly?: boolean } = {},\n): ProjectTrustOption[] {\n const trustPath = normalizeCwd(cwd);\n const trustOptions: ProjectTrustOption[] = [\n { label: 'Trust', trusted: true, updates: [{ path: trustPath, decision: true }], savedPath: trustPath },\n ];\n const parentPath = getProjectTrustParentPath(cwd);\n if (parentPath) {\n trustOptions.push({\n label: `Trust parent folder (${parentPath})`,\n trusted: true,\n updates: [\n { path: parentPath, decision: true },\n { path: trustPath, decision: null },\n ],\n savedPath: parentPath,\n });\n }\n if (options.includeSessionOnly) {\n trustOptions.push({ label: 'Trust (this session only)', trusted: true, updates: [] });\n }\n trustOptions.push({\n label: 'Do not trust',\n trusted: false,\n updates: [{ path: trustPath, decision: false }],\n savedPath: trustPath,\n });\n if (options.includeSessionOnly) {\n trustOptions.push({ label: 'Do not trust (this session only)', trusted: false, updates: [] });\n }\n return trustOptions;\n}\n\nexport function hasTrustRequiringProjectResources(cwd: string): boolean {\n const homeDir = canonicalizePath(process.env.HOME || homedir());\n const userAgentsSkillsDir = join(homeDir, '.agents', 'skills');\n let currentDir = canonicalizePath(cwd);\n\n const xopcProjectDir = join(currentDir, '.xopc');\n if (TRUST_REQUIRING_XOPC_PROJECT_RESOURCES.some((entry) => existsSync(join(xopcProjectDir, entry)))) {\n return true;\n }\n\n while (true) {\n const agentsSkillsDir = join(currentDir, '.agents', 'skills');\n if (agentsSkillsDir !== userAgentsSkillsDir && existsSync(agentsSkillsDir)) {\n return true;\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) return false;\n currentDir = parentDir;\n }\n}\n\nexport class ProjectTrustStore {\n constructor(private readonly trustPath: string = resolveProjectTrustStorePath()) {}\n\n getPath(): string {\n return this.trustPath;\n }\n\n get(cwd: string): ProjectTrustDecision {\n return this.getEntry(cwd)?.decision ?? null;\n }\n\n getEntry(cwd: string): ProjectTrustStoreEntry | null {\n return withTrustFileLock(this.trustPath, () => findNearestTrustEntry(readTrustFile(this.trustPath), cwd));\n }\n\n set(cwd: string, decision: ProjectTrustDecision): void {\n this.setMany([{ path: cwd, decision }]);\n }\n\n setMany(decisions: ProjectTrustUpdate[]): void {\n withTrustFileLock(this.trustPath, () => {\n const data = readTrustFile(this.trustPath);\n for (const { path, decision } of decisions) {\n const key = normalizeCwd(path);\n if (decision === null) {\n delete data[key];\n } else {\n data[key] = decision;\n }\n }\n writeTrustFile(this.trustPath, data);\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"trust-store.js","names":["resolvePath"],"sources":["../../../src/project-trust/trust-store.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join, resolve as resolvePath } from 'node:path';\n\nimport lockfile from 'proper-lockfile';\n\nimport { resolveStateDir } from '../config/paths.js';\n\nexport type ProjectTrustDecision = boolean | null;\n\nexport interface ProjectTrustStoreEntry {\n path: string;\n decision: boolean;\n}\n\nexport interface ProjectTrustUpdate {\n path: string;\n decision: ProjectTrustDecision;\n}\n\nexport interface ProjectTrustOption {\n label: string;\n trusted: boolean;\n updates: ProjectTrustUpdate[];\n savedPath?: string;\n}\n\ntype TrustFile = Record<string, boolean | null | undefined>;\n\nconst TRUST_REQUIRING_XOPC_PROJECT_RESOURCES = [\n 'settings.json',\n 'extensions',\n 'skills',\n 'prompts',\n 'themes',\n 'SYSTEM.md',\n 'APPEND_SYSTEM.md',\n 'workspace.json',\n] as const;\n\nconst TRUST_REQUIRING_PROJECT_ROOT_RESOURCES = [\n 'AGENTS.md',\n] as const;\n\nfunction canonicalizePath(path: string): string {\n const resolved = resolvePath(path);\n try {\n return realpathSync.native(resolved);\n } catch {\n return resolved;\n }\n}\n\nfunction normalizeCwd(cwd: string): string {\n return canonicalizePath(cwd);\n}\n\nfunction readTrustFile(path: string): TrustFile {\n if (!existsSync(path)) return {};\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(path, 'utf8'));\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to read project trust store ${path}: ${errorMessage}`);\n }\n\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new Error(`Invalid project trust store ${path}: expected an object`);\n }\n\n const data: TrustFile = {};\n for (const [key, value] of Object.entries(parsed)) {\n if (value !== true && value !== false && value !== null) {\n throw new Error(`Invalid project trust store ${path}: value for ${JSON.stringify(key)} must be true, false, or null`);\n }\n data[normalizeCwd(key)] = value;\n }\n return data;\n}\n\nfunction writeTrustFile(path: string, data: TrustFile): void {\n const sorted: TrustFile = {};\n for (const key of Object.keys(data).sort()) {\n const value = data[key];\n if (value === true || value === false || value === null) {\n sorted[key] = value;\n }\n }\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, `${JSON.stringify(sorted, null, 2)}\\n`, 'utf8');\n}\n\nfunction acquireTrustLockSync(path: string): () => void {\n const trustDir = dirname(path);\n mkdirSync(trustDir, { recursive: true });\n const maxAttempts = 10;\n const delayMs = 20;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return lockfile.lockSync(trustDir, { realpath: false, lockfilePath: `${path}.lock` });\n } catch (err) {\n const code =\n typeof err === 'object' && err !== null && 'code' in err\n ? String((err as { code?: unknown }).code)\n : undefined;\n if (code !== 'ELOCKED' || attempt === maxAttempts) {\n throw err;\n }\n const startedAt = Date.now();\n while (Date.now() - startedAt < delayMs) {\n // Synchronous callers need a tiny bounded wait while another TUI updates trust.json.\n }\n }\n }\n\n throw new Error('Failed to acquire project trust store lock');\n}\n\nfunction withTrustFileLock<T>(path: string, fn: () => T): T {\n const release = acquireTrustLockSync(path);\n try {\n return fn();\n } finally {\n release();\n }\n}\n\nfunction findNearestTrustEntry(data: TrustFile, cwd: string): ProjectTrustStoreEntry | null {\n let currentDir = normalizeCwd(cwd);\n while (true) {\n const value = data[currentDir];\n if (value === true || value === false) {\n return { path: currentDir, decision: value };\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) return null;\n currentDir = parentDir;\n }\n}\n\nexport function resolveProjectTrustStorePath(): string {\n return join(resolveStateDir(), 'trust.json');\n}\n\nexport function getProjectTrustParentPath(cwd: string): string | undefined {\n const trustPath = normalizeCwd(cwd);\n const parentDir = dirname(trustPath);\n return parentDir === trustPath ? undefined : parentDir;\n}\n\nexport function getProjectTrustOptions(\n cwd: string,\n options: { includeSessionOnly?: boolean } = {},\n): ProjectTrustOption[] {\n const trustPath = normalizeCwd(cwd);\n const trustOptions: ProjectTrustOption[] = [\n { label: 'Trust', trusted: true, updates: [{ path: trustPath, decision: true }], savedPath: trustPath },\n ];\n const parentPath = getProjectTrustParentPath(cwd);\n if (parentPath) {\n trustOptions.push({\n label: `Trust parent folder (${parentPath})`,\n trusted: true,\n updates: [\n { path: parentPath, decision: true },\n { path: trustPath, decision: null },\n ],\n savedPath: parentPath,\n });\n }\n if (options.includeSessionOnly) {\n trustOptions.push({ label: 'Trust (this session only)', trusted: true, updates: [] });\n }\n trustOptions.push({\n label: 'Do not trust',\n trusted: false,\n updates: [{ path: trustPath, decision: false }],\n savedPath: trustPath,\n });\n if (options.includeSessionOnly) {\n trustOptions.push({ label: 'Do not trust (this session only)', trusted: false, updates: [] });\n }\n return trustOptions;\n}\n\nexport function hasTrustRequiringProjectResources(cwd: string): boolean {\n const homeDir = canonicalizePath(process.env.HOME || homedir());\n const userAgentsSkillsDir = join(homeDir, '.agents', 'skills');\n let currentDir = canonicalizePath(cwd);\n\n if (TRUST_REQUIRING_PROJECT_ROOT_RESOURCES.some((entry) => existsSync(join(currentDir, entry)))) {\n return true;\n }\n\n const xopcProjectDir = join(currentDir, '.xopc');\n if (TRUST_REQUIRING_XOPC_PROJECT_RESOURCES.some((entry) => existsSync(join(xopcProjectDir, entry)))) {\n return true;\n }\n\n while (true) {\n const agentsSkillsDir = join(currentDir, '.agents', 'skills');\n if (agentsSkillsDir !== userAgentsSkillsDir && existsSync(agentsSkillsDir)) {\n return true;\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) return false;\n currentDir = parentDir;\n }\n}\n\nexport class ProjectTrustStore {\n constructor(private readonly trustPath: string = resolveProjectTrustStorePath()) {}\n\n getPath(): string {\n return this.trustPath;\n }\n\n get(cwd: string): ProjectTrustDecision {\n return this.getEntry(cwd)?.decision ?? null;\n }\n\n getEntry(cwd: string): ProjectTrustStoreEntry | null {\n return withTrustFileLock(this.trustPath, () => findNearestTrustEntry(readTrustFile(this.trustPath), cwd));\n }\n\n set(cwd: string, decision: ProjectTrustDecision): void {\n this.setMany([{ path: cwd, decision }]);\n }\n\n setMany(decisions: ProjectTrustUpdate[]): void {\n withTrustFileLock(this.trustPath, () => {\n const data = readTrustFile(this.trustPath);\n for (const { path, decision } of decisions) {\n const key = normalizeCwd(path);\n if (decision === null) {\n delete data[key];\n } else {\n data[key] = decision;\n }\n }\n writeTrustFile(this.trustPath, data);\n });\n }\n}\n"],"mappings":"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"}
@@ -10,7 +10,7 @@ export { SessionStatus, type SessionMetadata, type SessionDetail, type SessionLi
10
10
  export { normalizeCompactionCheckpointId } from './compaction-checkpoints.js';
11
11
  export { shouldSkipWebchatInboundByAbortCutoff } from './abort-cutoff.js';
12
12
  export { DEFAULT_RESET_AT_HOUR, DEFAULT_RESET_MODE, DEFAULT_IDLE_MINUTES, evaluateSessionFreshness, resolveDailyResetAtMs, resolveSessionResetPolicy, type SessionFreshness, type SessionResetConfig, type SessionResetMode, type SessionResetPolicy, type SessionResetType, } from './reset-policy.js';
13
- export { isThreadSessionKey, resolveChannelResetConfig, resolveSessionResetType, } from './reset-type.js';
13
+ export { resolveChannelResetConfig, resolveSessionResetType, } from './reset-type.js';
14
14
  export { resolveSessionLifecycleTimestamps, type SessionLifecycleEntry } from './lifecycle-timestamps.js';
15
15
  export { resolveSession, resolveSessionKeyForRequest, type SessionKeyResolution, type SessionResolution, } from './resolve-session.js';
16
16
  export { initSessionTurn, type InitSessionTurnOptions, type InitSessionTurnResult, type SessionResetFn, } from './init-session-turn.js';
@@ -11,11 +11,11 @@ import { resolveEffectiveReasoningLevel, resolveEffectiveThinkingLevel } from ".
11
11
  import { effectiveWorkspacePathForSession, normalizeWorkingDirectoryInput } from "./session-workspace.js";
12
12
  import { shouldSkipWebchatInboundByAbortCutoff } from "./abort-cutoff.js";
13
13
  import { DEFAULT_IDLE_MINUTES, DEFAULT_RESET_AT_HOUR, DEFAULT_RESET_MODE, evaluateSessionFreshness, resolveDailyResetAtMs, resolveSessionResetPolicy } from "./reset-policy.js";
14
- import { isThreadSessionKey, resolveChannelResetConfig, resolveSessionResetType } from "./reset-type.js";
14
+ import { resolveChannelResetConfig, resolveSessionResetType } from "./reset-type.js";
15
15
  import { resolveSessionLifecycleTimestamps } from "./lifecycle-timestamps.js";
16
16
  import { resolveSession, resolveSessionKeyForRequest } from "./resolve-session.js";
17
17
  import { DEFAULT_RESET_TRIGGERS, RESET_OVERLAP_COMMANDS, bareResetAckMessage, matchResetTriggers, resolveResetTriggers, shouldSkipResetOverlapCommand, stripLeadingEnvelopeTimestamp } from "./reset-triggers.js";
18
18
  import { initSessionTurn } from "./init-session-turn.js";
19
19
  import { flattenMessageContent, messagesToClientHistory } from "./client-history.js";
20
20
  import { CURRENT_SESSION_TRANSCRIPT_VERSION, XOPC_SESSION_TRANSCRIPT_TYPE } from "./transcript-format.js";
21
- export { CURRENT_SESSION_TRANSCRIPT_VERSION, DEFAULT_IDLE_MINUTES, DEFAULT_RESET_AT_HOUR, DEFAULT_RESET_MODE, DEFAULT_RESET_TRIGGERS, RESET_OVERLAP_COMMANDS, SessionConfigStore, SessionIndex, SessionStatus, SessionStore, XOPC_SESSION_TRANSCRIPT_TYPE, applySessionPatchToMetadata, bareResetAckMessage, buildSessionContextForLlm, effectiveWorkspacePathForSession, emitSessionTranscriptUpdate, evaluateSessionFreshness, fallbackTitleFromMessages, flattenMessageContent, generateSessionTitleFromMessages, getSessionTitleSource, initSessionTurn, isThreadSessionKey, isTranscriptContextEntry, isWebchatSessionKey, matchResetTriggers, maybeRefineSessionTitleWithLlm, maybeSetProvisionalSessionTitle, mergeLlmMessagesPreservingContextRows, messagesToClientHistory, normalizeCompactionCheckpointId, normalizeWorkingDirectoryInput, onSessionTranscriptUpdate, provisionalTitleFromUserText, resolveChannelResetConfig, resolveDailyResetAtMs, resolveEffectiveReasoningLevel, resolveEffectiveThinkingLevel, resolveReasoningLevel, resolveResetTriggers, resolveSession, resolveSessionKeyForRequest, resolveSessionLifecycleTimestamps, resolveSessionResetPolicy, resolveSessionResetType, resolveThinkingLevel, resolveVerboseLevel, sanitizeSessionTitle, shouldAutoTitleSessionKey, shouldRefineSessionTitleWithLlm, shouldSkipResetOverlapCommand, shouldSkipWebchatInboundByAbortCutoff, stripLeadingEnvelopeTimestamp, transcriptRowsFromJsonArray };
21
+ export { CURRENT_SESSION_TRANSCRIPT_VERSION, DEFAULT_IDLE_MINUTES, DEFAULT_RESET_AT_HOUR, DEFAULT_RESET_MODE, DEFAULT_RESET_TRIGGERS, RESET_OVERLAP_COMMANDS, SessionConfigStore, SessionIndex, SessionStatus, SessionStore, XOPC_SESSION_TRANSCRIPT_TYPE, applySessionPatchToMetadata, bareResetAckMessage, buildSessionContextForLlm, effectiveWorkspacePathForSession, emitSessionTranscriptUpdate, evaluateSessionFreshness, fallbackTitleFromMessages, flattenMessageContent, generateSessionTitleFromMessages, getSessionTitleSource, initSessionTurn, isTranscriptContextEntry, isWebchatSessionKey, matchResetTriggers, maybeRefineSessionTitleWithLlm, maybeSetProvisionalSessionTitle, mergeLlmMessagesPreservingContextRows, messagesToClientHistory, normalizeCompactionCheckpointId, normalizeWorkingDirectoryInput, onSessionTranscriptUpdate, provisionalTitleFromUserText, resolveChannelResetConfig, resolveDailyResetAtMs, resolveEffectiveReasoningLevel, resolveEffectiveThinkingLevel, resolveReasoningLevel, resolveResetTriggers, resolveSession, resolveSessionKeyForRequest, resolveSessionLifecycleTimestamps, resolveSessionResetPolicy, resolveSessionResetType, resolveThinkingLevel, resolveVerboseLevel, sanitizeSessionTitle, shouldAutoTitleSessionKey, shouldRefineSessionTitleWithLlm, shouldSkipResetOverlapCommand, shouldSkipWebchatInboundByAbortCutoff, stripLeadingEnvelopeTimestamp, transcriptRowsFromJsonArray };
@@ -1,7 +1,6 @@
1
1
  import { createLogger } from "../utils/logger/index.js";
2
2
  import { init_logger } from "../utils/logger.js";
3
3
  import { SessionConfigSchema, init_schema } from "../config/schema.js";
4
- import { init_session_key, parseSessionKey } from "../routing/session-key.js";
5
4
  import { requireXopcDatabase } from "../storage/sqlite/connection.js";
6
5
  import "../storage/sqlite/index.js";
7
6
  import { evaluateSessionFreshness, resolveSessionResetPolicy } from "./reset-policy.js";
@@ -12,7 +11,6 @@ import { bareResetAckMessage, matchResetTriggers, resolveResetTriggers } from ".
12
11
  import { randomUUID } from "node:crypto";
13
12
  //#region src/session/init-session-turn.ts
14
13
  init_schema();
15
- init_session_key();
16
14
  init_logger();
17
15
  const log = createLogger("InitSessionTurn");
18
16
  async function initSessionTurn(opts) {
@@ -26,19 +24,18 @@ async function initSessionTurn(opts) {
26
24
  sessionKey: opts.sessionKey
27
25
  });
28
26
  const key = sessionKey?.trim() ?? opts.sessionKey.trim();
29
- const parsed = key ? parseSessionKey(key) : null;
30
- const peerKind = parsed?.peerKind;
27
+ const routing = sessionMetadata?.routing;
28
+ const peerKind = routing?.peerKind;
31
29
  const resetType = resolveSessionResetType({
32
- sessionKey: key,
33
30
  isGroup: peerKind === "group" || peerKind === "channel",
34
- isThread: Boolean(parsed?.threadId)
31
+ isThread: Boolean(routing?.threadId)
35
32
  });
36
33
  const resetPolicy = resolveSessionResetPolicy({
37
34
  sessionCfg,
38
35
  resetType,
39
36
  resetOverride: resolveChannelResetConfig({
40
37
  sessionCfg,
41
- channel: parsed?.source ?? sessionMetadata?.sourceChannel
38
+ channel: routing?.source ?? sessionMetadata?.sourceChannel
42
39
  })
43
40
  });
44
41
  const lifecycle = resolveSessionLifecycleTimestamps({ entry: sessionMetadata ? {
@@ -56,10 +53,10 @@ async function initSessionTurn(opts) {
56
53
  const skipImplicit = opts.skipImplicitExpiry ?? false;
57
54
  const staleRollover = Boolean(sessionMetadata && !skipImplicit && !freshness.fresh);
58
55
  const needsRollover = triggerMatch.resetTriggered || staleRollover;
59
- let sessionId = sessionMetadata?.transcriptId;
56
+ let sessionId = sessionMetadata?.sessionId;
60
57
  let previousSessionId;
61
58
  let isNewSession = false;
62
- if (needsRollover && sessionMetadata?.transcriptId) {
59
+ if (needsRollover && sessionMetadata?.sessionId) {
63
60
  const outcome = await opts.resetSession(key);
64
61
  if (outcome) {
65
62
  previousSessionId = outcome.previousSessionId;
@@ -1 +1 @@
1
- {"version":3,"file":"init-session-turn.js","names":[],"sources":["../../../src/session/init-session-turn.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { SessionConfigSchema, type Config } from '../config/schema.js';\nimport { parseSessionKey } from '../routing/session-key.js';\nimport { createLogger } from '../utils/logger.js';\nimport { requireXopcDatabase } from '../storage/sqlite/index.js';\nimport { resolveSessionLifecycleTimestamps } from './lifecycle-timestamps.js';\nimport {\n evaluateSessionFreshness,\n resolveSessionResetPolicy,\n} from './reset-policy.js';\nimport {\n bareResetAckMessage,\n matchResetTriggers,\n resolveResetTriggers,\n} from './reset-triggers.js';\nimport { resolveChannelResetConfig, resolveSessionResetType } from './reset-type.js';\nimport { resolveSessionKeyForRequest } from './resolve-session.js';\n\nconst log = createLogger('InitSessionTurn');\n\nexport type SessionResetFn = (\n sessionKey: string,\n) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n\nexport type InitSessionTurnResult = {\n sessionKey: string;\n sessionId?: string;\n previousSessionId?: string;\n isNewSession: boolean;\n resetTriggered: boolean;\n staleRollover: boolean;\n bodyStripped: string;\n bareReset: boolean;\n ackMessage?: string;\n};\n\nexport type InitSessionTurnOptions = {\n cfg: Config;\n sessionKey: string;\n body?: string;\n resetSession: SessionResetFn;\n skipImplicitExpiry?: boolean;\n};\n\nexport async function initSessionTurn(\n opts: InitSessionTurnOptions,\n): Promise<InitSessionTurnResult> {\n requireXopcDatabase();\n\n const sessionCfg = opts.cfg.session ?? SessionConfigSchema.parse({});\n const triggers = resolveResetTriggers(sessionCfg.resetTriggers);\n const rawBody = opts.body ?? '';\n const triggerMatch = matchResetTriggers(rawBody, triggers);\n\n const { sessionKey, sessionMetadata } = await resolveSessionKeyForRequest({\n cfg: opts.cfg,\n sessionKey: opts.sessionKey,\n });\n const key = sessionKey?.trim() ?? opts.sessionKey.trim();\n\n const parsed = key ? parseSessionKey(key) : null;\n const peerKind = parsed?.peerKind;\n const resetType = resolveSessionResetType({\n sessionKey: key,\n isGroup: peerKind === 'group' || peerKind === 'channel',\n isThread: Boolean(parsed?.threadId),\n });\n const channelReset = resolveChannelResetConfig({\n sessionCfg,\n channel: parsed?.source ?? sessionMetadata?.sourceChannel,\n });\n const resetPolicy = resolveSessionResetPolicy({\n sessionCfg,\n resetType,\n resetOverride: channelReset,\n });\n const lifecycle = resolveSessionLifecycleTimestamps({\n entry: sessionMetadata\n ? {\n updatedAt: Date.parse(sessionMetadata.updatedAt),\n sessionStartedAt: sessionMetadata.sessionStartedAt\n ? Date.parse(sessionMetadata.sessionStartedAt)\n : undefined,\n lastInteractionAt: sessionMetadata.lastInteractionAt\n ? Date.parse(sessionMetadata.lastInteractionAt)\n : undefined,\n }\n : undefined,\n });\n const now = Date.now();\n const freshness = sessionMetadata\n ? evaluateSessionFreshness({\n updatedAt: Date.parse(sessionMetadata.updatedAt),\n ...lifecycle,\n now,\n policy: resetPolicy,\n })\n : { fresh: false };\n\n const skipImplicit = opts.skipImplicitExpiry ?? false;\n const staleRollover = Boolean(sessionMetadata && !skipImplicit && !freshness.fresh);\n const needsRollover = triggerMatch.resetTriggered || staleRollover;\n\n let sessionId = sessionMetadata?.transcriptId;\n let previousSessionId: string | undefined;\n let isNewSession = false;\n\n if (needsRollover && sessionMetadata?.transcriptId) {\n const outcome = await opts.resetSession(key);\n if (outcome) {\n previousSessionId = outcome.previousSessionId;\n sessionId = outcome.sessionId;\n isNewSession = true;\n log.info(\n {\n sessionKey: key,\n sessionId: outcome.sessionId,\n previousSessionId: outcome.previousSessionId,\n resetTriggered: triggerMatch.resetTriggered,\n staleRollover,\n resetType,\n },\n triggerMatch.resetTriggered\n ? 'Session reset via reset trigger'\n : 'Session rolled over (stale freshness)',\n );\n } else {\n log.warn({ sessionKey: key }, 'Session rollover requested but resetSession returned null');\n sessionId = randomUUID();\n isNewSession = true;\n }\n } else if (!sessionMetadata) {\n isNewSession = true;\n sessionId = randomUUID();\n }\n\n const bareReset = triggerMatch.resetTriggered && triggerMatch.bareReset;\n const ackMessage = bareReset ? bareResetAckMessage(triggerMatch.matchedTrigger) : undefined;\n\n return {\n sessionKey: key,\n sessionId,\n previousSessionId,\n isNewSession,\n resetTriggered: triggerMatch.resetTriggered,\n staleRollover,\n bodyStripped: triggerMatch.resetTriggered ? triggerMatch.bodyStripped : rawBody,\n bareReset,\n ackMessage,\n };\n}\n"],"mappings":";;;;;;;;;;;;;aAEuE;kBACX;aACV;AAelD,MAAM,MAAM,aAAa,kBAAkB;AA0B3C,eAAsB,gBACpB,MACgC;AAChC,sBAAqB;CAErB,MAAM,aAAa,KAAK,IAAI,WAAW,oBAAoB,MAAM,EAAE,CAAC;CACpE,MAAM,WAAW,qBAAqB,WAAW,cAAc;CAC/D,MAAM,UAAU,KAAK,QAAQ;CAC7B,MAAM,eAAe,mBAAmB,SAAS,SAAS;CAE1D,MAAM,EAAE,YAAY,oBAAoB,MAAM,4BAA4B;EACxE,KAAK,KAAK;EACV,YAAY,KAAK;EAClB,CAAC;CACF,MAAM,MAAM,YAAY,MAAM,IAAI,KAAK,WAAW,MAAM;CAExD,MAAM,SAAS,MAAM,gBAAgB,IAAI,GAAG;CAC5C,MAAM,WAAW,QAAQ;CACzB,MAAM,YAAY,wBAAwB;EACxC,YAAY;EACZ,SAAS,aAAa,WAAW,aAAa;EAC9C,UAAU,QAAQ,QAAQ,SAAS;EACpC,CAAC;CAKF,MAAM,cAAc,0BAA0B;EAC5C;EACA;EACA,eAPmB,0BAA0B;GAC7C;GACA,SAAS,QAAQ,UAAU,iBAAiB;GAC7C,CAI4B;EAC5B,CAAC;CACF,MAAM,YAAY,kCAAkC,EAClD,OAAO,kBACH;EACE,WAAW,KAAK,MAAM,gBAAgB,UAAU;EAChD,kBAAkB,gBAAgB,mBAC9B,KAAK,MAAM,gBAAgB,iBAAiB,GAC5C,KAAA;EACJ,mBAAmB,gBAAgB,oBAC/B,KAAK,MAAM,gBAAgB,kBAAkB,GAC7C,KAAA;EACL,GACD,KAAA,GACL,CAAC;CACF,MAAM,MAAM,KAAK,KAAK;CACtB,MAAM,YAAY,kBACd,yBAAyB;EACvB,WAAW,KAAK,MAAM,gBAAgB,UAAU;EAChD,GAAG;EACH;EACA,QAAQ;EACT,CAAC,GACF,EAAE,OAAO,OAAO;CAEpB,MAAM,eAAe,KAAK,sBAAsB;CAChD,MAAM,gBAAgB,QAAQ,mBAAmB,CAAC,gBAAgB,CAAC,UAAU,MAAM;CACnF,MAAM,gBAAgB,aAAa,kBAAkB;CAErD,IAAI,YAAY,iBAAiB;CACjC,IAAI;CACJ,IAAI,eAAe;AAEnB,KAAI,iBAAiB,iBAAiB,cAAc;EAClD,MAAM,UAAU,MAAM,KAAK,aAAa,IAAI;AAC5C,MAAI,SAAS;AACX,uBAAoB,QAAQ;AAC5B,eAAY,QAAQ;AACpB,kBAAe;AACf,OAAI,KACF;IACE,YAAY;IACZ,WAAW,QAAQ;IACnB,mBAAmB,QAAQ;IAC3B,gBAAgB,aAAa;IAC7B;IACA;IACD,EACD,aAAa,iBACT,oCACA,wCACL;SACI;AACL,OAAI,KAAK,EAAE,YAAY,KAAK,EAAE,4DAA4D;AAC1F,eAAY,YAAY;AACxB,kBAAe;;YAER,CAAC,iBAAiB;AAC3B,iBAAe;AACf,cAAY,YAAY;;CAG1B,MAAM,YAAY,aAAa,kBAAkB,aAAa;CAC9D,MAAM,aAAa,YAAY,oBAAoB,aAAa,eAAe,GAAG,KAAA;AAElF,QAAO;EACL,YAAY;EACZ;EACA;EACA;EACA,gBAAgB,aAAa;EAC7B;EACA,cAAc,aAAa,iBAAiB,aAAa,eAAe;EACxE;EACA;EACD"}
1
+ {"version":3,"file":"init-session-turn.js","names":[],"sources":["../../../src/session/init-session-turn.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { SessionConfigSchema, type Config } from '../config/schema.js';\nimport { createLogger } from '../utils/logger.js';\nimport { requireXopcDatabase } from '../storage/sqlite/index.js';\nimport { resolveSessionLifecycleTimestamps } from './lifecycle-timestamps.js';\nimport {\n evaluateSessionFreshness,\n resolveSessionResetPolicy,\n} from './reset-policy.js';\nimport {\n bareResetAckMessage,\n matchResetTriggers,\n resolveResetTriggers,\n} from './reset-triggers.js';\nimport { resolveChannelResetConfig, resolveSessionResetType } from './reset-type.js';\nimport { resolveSessionKeyForRequest } from './resolve-session.js';\n\nconst log = createLogger('InitSessionTurn');\n\nexport type SessionResetFn = (\n sessionKey: string,\n) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n\nexport type InitSessionTurnResult = {\n sessionKey: string;\n sessionId?: string;\n previousSessionId?: string;\n isNewSession: boolean;\n resetTriggered: boolean;\n staleRollover: boolean;\n bodyStripped: string;\n bareReset: boolean;\n ackMessage?: string;\n};\n\nexport type InitSessionTurnOptions = {\n cfg: Config;\n sessionKey: string;\n body?: string;\n resetSession: SessionResetFn;\n skipImplicitExpiry?: boolean;\n};\n\nexport async function initSessionTurn(\n opts: InitSessionTurnOptions,\n): Promise<InitSessionTurnResult> {\n requireXopcDatabase();\n\n const sessionCfg = opts.cfg.session ?? SessionConfigSchema.parse({});\n const triggers = resolveResetTriggers(sessionCfg.resetTriggers);\n const rawBody = opts.body ?? '';\n const triggerMatch = matchResetTriggers(rawBody, triggers);\n\n const { sessionKey, sessionMetadata } = await resolveSessionKeyForRequest({\n cfg: opts.cfg,\n sessionKey: opts.sessionKey,\n });\n const key = sessionKey?.trim() ?? opts.sessionKey.trim();\n\n const routing = sessionMetadata?.routing;\n const peerKind = routing?.peerKind;\n const resetType = resolveSessionResetType({\n isGroup: peerKind === 'group' || peerKind === 'channel',\n isThread: Boolean(routing?.threadId),\n });\n const channelReset = resolveChannelResetConfig({\n sessionCfg,\n channel: routing?.source ?? sessionMetadata?.sourceChannel,\n });\n const resetPolicy = resolveSessionResetPolicy({\n sessionCfg,\n resetType,\n resetOverride: channelReset,\n });\n const lifecycle = resolveSessionLifecycleTimestamps({\n entry: sessionMetadata\n ? {\n updatedAt: Date.parse(sessionMetadata.updatedAt),\n sessionStartedAt: sessionMetadata.sessionStartedAt\n ? Date.parse(sessionMetadata.sessionStartedAt)\n : undefined,\n lastInteractionAt: sessionMetadata.lastInteractionAt\n ? Date.parse(sessionMetadata.lastInteractionAt)\n : undefined,\n }\n : undefined,\n });\n const now = Date.now();\n const freshness = sessionMetadata\n ? evaluateSessionFreshness({\n updatedAt: Date.parse(sessionMetadata.updatedAt),\n ...lifecycle,\n now,\n policy: resetPolicy,\n })\n : { fresh: false };\n\n const skipImplicit = opts.skipImplicitExpiry ?? false;\n const staleRollover = Boolean(sessionMetadata && !skipImplicit && !freshness.fresh);\n const needsRollover = triggerMatch.resetTriggered || staleRollover;\n\n let sessionId = sessionMetadata?.sessionId;\n let previousSessionId: string | undefined;\n let isNewSession = false;\n\n if (needsRollover && sessionMetadata?.sessionId) {\n const outcome = await opts.resetSession(key);\n if (outcome) {\n previousSessionId = outcome.previousSessionId;\n sessionId = outcome.sessionId;\n isNewSession = true;\n log.info(\n {\n sessionKey: key,\n sessionId: outcome.sessionId,\n previousSessionId: outcome.previousSessionId,\n resetTriggered: triggerMatch.resetTriggered,\n staleRollover,\n resetType,\n },\n triggerMatch.resetTriggered\n ? 'Session reset via reset trigger'\n : 'Session rolled over (stale freshness)',\n );\n } else {\n log.warn({ sessionKey: key }, 'Session rollover requested but resetSession returned null');\n sessionId = randomUUID();\n isNewSession = true;\n }\n } else if (!sessionMetadata) {\n isNewSession = true;\n sessionId = randomUUID();\n }\n\n const bareReset = triggerMatch.resetTriggered && triggerMatch.bareReset;\n const ackMessage = bareReset ? bareResetAckMessage(triggerMatch.matchedTrigger) : undefined;\n\n return {\n sessionKey: key,\n sessionId,\n previousSessionId,\n isNewSession,\n resetTriggered: triggerMatch.resetTriggered,\n staleRollover,\n bodyStripped: triggerMatch.resetTriggered ? triggerMatch.bodyStripped : rawBody,\n bareReset,\n ackMessage,\n };\n}\n"],"mappings":";;;;;;;;;;;;aAEuE;aACrB;AAelD,MAAM,MAAM,aAAa,kBAAkB;AA0B3C,eAAsB,gBACpB,MACgC;AAChC,sBAAqB;CAErB,MAAM,aAAa,KAAK,IAAI,WAAW,oBAAoB,MAAM,EAAE,CAAC;CACpE,MAAM,WAAW,qBAAqB,WAAW,cAAc;CAC/D,MAAM,UAAU,KAAK,QAAQ;CAC7B,MAAM,eAAe,mBAAmB,SAAS,SAAS;CAE1D,MAAM,EAAE,YAAY,oBAAoB,MAAM,4BAA4B;EACxE,KAAK,KAAK;EACV,YAAY,KAAK;EAClB,CAAC;CACF,MAAM,MAAM,YAAY,MAAM,IAAI,KAAK,WAAW,MAAM;CAExD,MAAM,UAAU,iBAAiB;CACjC,MAAM,WAAW,SAAS;CAC1B,MAAM,YAAY,wBAAwB;EACxC,SAAS,aAAa,WAAW,aAAa;EAC9C,UAAU,QAAQ,SAAS,SAAS;EACrC,CAAC;CAKF,MAAM,cAAc,0BAA0B;EAC5C;EACA;EACA,eAPmB,0BAA0B;GAC7C;GACA,SAAS,SAAS,UAAU,iBAAiB;GAC9C,CAI4B;EAC5B,CAAC;CACF,MAAM,YAAY,kCAAkC,EAClD,OAAO,kBACH;EACE,WAAW,KAAK,MAAM,gBAAgB,UAAU;EAChD,kBAAkB,gBAAgB,mBAC9B,KAAK,MAAM,gBAAgB,iBAAiB,GAC5C,KAAA;EACJ,mBAAmB,gBAAgB,oBAC/B,KAAK,MAAM,gBAAgB,kBAAkB,GAC7C,KAAA;EACL,GACD,KAAA,GACL,CAAC;CACF,MAAM,MAAM,KAAK,KAAK;CACtB,MAAM,YAAY,kBACd,yBAAyB;EACvB,WAAW,KAAK,MAAM,gBAAgB,UAAU;EAChD,GAAG;EACH;EACA,QAAQ;EACT,CAAC,GACF,EAAE,OAAO,OAAO;CAEpB,MAAM,eAAe,KAAK,sBAAsB;CAChD,MAAM,gBAAgB,QAAQ,mBAAmB,CAAC,gBAAgB,CAAC,UAAU,MAAM;CACnF,MAAM,gBAAgB,aAAa,kBAAkB;CAErD,IAAI,YAAY,iBAAiB;CACjC,IAAI;CACJ,IAAI,eAAe;AAEnB,KAAI,iBAAiB,iBAAiB,WAAW;EAC/C,MAAM,UAAU,MAAM,KAAK,aAAa,IAAI;AAC5C,MAAI,SAAS;AACX,uBAAoB,QAAQ;AAC5B,eAAY,QAAQ;AACpB,kBAAe;AACf,OAAI,KACF;IACE,YAAY;IACZ,WAAW,QAAQ;IACnB,mBAAmB,QAAQ;IAC3B,gBAAgB,aAAa;IAC7B;IACA;IACD,EACD,aAAa,iBACT,oCACA,wCACL;SACI;AACL,OAAI,KAAK,EAAE,YAAY,KAAK,EAAE,4DAA4D;AAC1F,eAAY,YAAY;AACxB,kBAAe;;YAER,CAAC,iBAAiB;AAC3B,iBAAe;AACf,cAAY,YAAY;;CAG1B,MAAM,YAAY,aAAa,kBAAkB,aAAa;CAC9D,MAAM,aAAa,YAAY,oBAAoB,aAAa,eAAe,GAAG,KAAA;AAElF,QAAO;EACL,YAAY;EACZ;EACA;EACA;EACA,gBAAgB,aAAa;EAC7B;EACA,cAAc,aAAa,iBAAiB,aAAa,eAAe;EACxE;EACA;EACD"}
@@ -1,6 +1,7 @@
1
1
  import EventEmitter from 'events';
2
2
  import { SessionStore } from './store.js';
3
3
  import type { SessionMetadata, SessionDetail, SessionListQuery, PaginatedResult, GlobalSessionStats, ExportFormat, SessionStatus } from './types.js';
4
+ import type { SessionMetadataSeed } from '../storage/sqlite/index.js';
4
5
  import type { Message } from './types.js';
5
6
  import type { CompactionConfig, CompactionResult } from '../agent/memory/compaction.js';
6
7
  import type { XopcSessionTranscriptV1 } from './transcript-format.js';
@@ -52,6 +53,7 @@ export declare class SessionIndex extends EventEmitter {
52
53
  error: string;
53
54
  }>;
54
55
  getSessionMetadata(key: string): Promise<SessionMetadata | null>;
56
+ resolveSessionKeyBySessionId(sessionId: string): Promise<string | null>;
55
57
  deleteSession(key: string): Promise<boolean>;
56
58
  deleteSessions(keys: string[]): Promise<{
57
59
  success: string[];
@@ -123,7 +125,9 @@ export declare class SessionIndex extends EventEmitter {
123
125
  * Runtime turns must use PiTranscriptManager.appendMessage; this entry point
124
126
  * is reserved for compaction, tests, and admin tools.
125
127
  */
126
- saveMessages(key: string, messages: any[]): Promise<void>;
128
+ saveMessages(key: string, messages: any[], options?: {
129
+ metadata?: SessionMetadataSeed;
130
+ }): Promise<void>;
127
131
  /**
128
132
  * Append `kind: 'context'` transcript row (persisted, excluded from {@link loadMessages} / LLM).
129
133
  */
@@ -58,6 +58,9 @@ var SessionIndex = class extends EventEmitter$1 {
58
58
  async getSessionMetadata(key) {
59
59
  return this.store.getMetadata(key);
60
60
  }
61
+ async resolveSessionKeyBySessionId(sessionId) {
62
+ return this.store.resolveKeyBySessionId(sessionId);
63
+ }
61
64
  async deleteSession(key) {
62
65
  const result = await this.store.delete(key);
63
66
  if (result) this.emit("sessionDeleted", { key });
@@ -218,8 +221,8 @@ var SessionIndex = class extends EventEmitter$1 {
218
221
  * Runtime turns must use PiTranscriptManager.appendMessage; this entry point
219
222
  * is reserved for compaction, tests, and admin tools.
220
223
  */
221
- async saveMessages(key, messages) {
222
- return this.store.saveMessages(key, messages);
224
+ async saveMessages(key, messages, options) {
225
+ return this.store.saveMessages(key, messages, options);
223
226
  }
224
227
  /**
225
228
  * Append `kind: 'context'` transcript row (persisted, excluded from {@link loadMessages} / LLM).
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","names":["EventEmitter"],"sources":["../../../src/session/manager.ts"],"sourcesContent":["// Session manager - high-level session management service\n\nimport EventEmitter from 'events';\nimport { createLogger } from '../utils/logger.js';\nimport { SessionStore } from './store.js';\nimport type {\n SessionMetadata,\n SessionDetail,\n SessionListQuery,\n PaginatedResult,\n GlobalSessionStats,\n ExportFormat,\n SessionStatus,\n} from './types.js';\nimport type { Message } from './types.js';\nimport type { CompactionConfig, CompactionResult } from '../agent/memory/compaction.js';\nimport type { XopcSessionTranscriptV1 } from './transcript-format.js';\nimport type { XopcTranscriptContextEntry } from './session-context-for-llm.js';\nimport { applySessionPatchToMetadata, type SessionPatchBody } from './patch-metadata.js';\nimport type { WindowConfig } from '../agent/memory/window.js';\nimport type { Config } from '../config/schema.js';\n\nconst log = createLogger('SessionIndex');\n\nexport interface SessionIndexConfig {\n config: Config;\n windowConfig?: Partial<WindowConfig>;\n compactionConfig?: Partial<CompactionConfig>;\n}\n\nexport class SessionIndex extends EventEmitter {\n private store: SessionStore;\n\n constructor(config: SessionIndexConfig) {\n super();\n this.store = new SessionStore(\n {\n config: config.config,\n },\n config.windowConfig,\n config.compactionConfig\n );\n }\n\n async initialize(): Promise<void> {\n await this.store.initialize();\n this.emit('ready');\n }\n\n /** Low-level store (e.g. cron resolving weixin delivery from session index). */\n getStore(): SessionStore {\n return this.store;\n }\n\n // ========== CRUD Operations ==========\n\n async listSessions(query?: SessionListQuery): Promise<PaginatedResult<SessionMetadata>> {\n return this.store.list(query);\n }\n\n async listSubagents(query: SessionListQuery = {}): Promise<PaginatedResult<SessionMetadata>> {\n return this.store.list({\n ...query,\n sessionTypes: ['workflow-subagent'],\n includeHidden: true,\n });\n }\n\n async getSession(\n key: string,\n options?: { includeTranscriptSummary?: boolean; includeTranscriptRows?: boolean },\n ): Promise<SessionDetail | null> {\n const session = await this.store.get(key, options);\n if (session) {\n this.emit('sessionAccessed', { key });\n }\n return session;\n }\n\n async getSessionMessagePage(\n key: string,\n options?: {\n offset?: number;\n limit?: number;\n before?: string;\n includeTranscriptSummary?: boolean;\n includeTranscriptRows?: boolean;\n },\n ): Promise<{\n session: SessionDetail;\n pagination: {\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n before?: string;\n nextBeforeCursor?: string;\n };\n } | null> {\n const result = await this.store.getMessagePage(key, options);\n if (result) {\n this.emit('sessionAccessed', { key });\n }\n return result;\n }\n\n /**\n * OpenClaw-style `sessions.patch`: partial metadata (name, tags, customData shallow merge).\n */\n async patchSession(\n key: string,\n patch: SessionPatchBody,\n ): Promise<{ ok: true } | { ok: false; error: string }> {\n const meta = await this.store.getMetadata(key);\n if (!meta) {\n return { ok: false, error: 'Session not found' };\n }\n const updates = applySessionPatchToMetadata(meta, patch);\n if (Object.keys(updates).length === 0) {\n return { ok: true };\n }\n await this.store.updateMetadata(key, updates);\n this.emit('sessionUpdated', { key });\n return { ok: true };\n }\n\n async getSessionMetadata(key: string): Promise<SessionMetadata | null> {\n return this.store.getMetadata(key);\n }\n\n async deleteSession(key: string): Promise<boolean> {\n const result = await this.store.delete(key);\n if (result) {\n this.emit('sessionDeleted', { key });\n }\n return result;\n }\n\n async deleteSessions(keys: string[]): Promise<{ success: string[]; failed: string[] }> {\n const result = await this.store.deleteMany(keys);\n for (const key of result.success) {\n this.emit('sessionDeleted', { key });\n }\n return result;\n }\n\n // ========== Metadata Updates ==========\n\n async renameSession(key: string, name: string): Promise<void> {\n const existing = await this.store.getMetadata(key);\n await this.store.updateMetadata(key, {\n name,\n customData: { ...(existing?.customData ?? {}), titleSource: 'user' },\n });\n this.emit('sessionUpdated', { key, name });\n }\n\n /** Partial metadata update (caller merges nested fields like `customData` when needed). */\n async updateSessionMetadata(key: string, updates: Partial<SessionMetadata>): Promise<void> {\n await this.store.updateMetadata(key, updates);\n this.emit('sessionUpdated', { key });\n }\n\n async tagSession(key: string, tags: string[]): Promise<void> {\n const existing = await this.store.getMetadata(key);\n if (!existing) {\n throw new Error(`Session not found: ${key}`);\n }\n\n // Merge tags, remove duplicates\n const mergedTags = [...new Set([...existing.tags, ...tags])];\n await this.store.updateMetadata(key, { tags: mergedTags });\n this.emit('sessionUpdated', { key, tags: mergedTags });\n }\n\n async untagSession(key: string, tags: string[]): Promise<void> {\n const existing = await this.store.getMetadata(key);\n if (!existing) {\n throw new Error(`Session not found: ${key}`);\n }\n\n const filteredTags = existing.tags.filter((t) => !tags.includes(t));\n await this.store.updateMetadata(key, { tags: filteredTags });\n this.emit('sessionUpdated', { key, tags: filteredTags });\n }\n\n async setSessionTags(key: string, tags: string[]): Promise<void> {\n await this.store.updateMetadata(key, { tags: [...new Set(tags)] });\n this.emit('sessionUpdated', { key, tags });\n }\n\n // ========== Status Management ==========\n\n async archiveSession(key: string): Promise<void> {\n await this.store.archive(key);\n this.emit('sessionArchived', { key });\n }\n\n async unarchiveSession(key: string): Promise<void> {\n await this.store.unarchive(key);\n this.emit('sessionRestored', { key });\n }\n\n async pinSession(key: string): Promise<void> {\n await this.store.pin(key);\n this.emit('sessionPinned', { key });\n }\n\n async unpinSession(key: string): Promise<void> {\n await this.store.unpin(key);\n this.emit('sessionUnpinned', { key });\n }\n\n async setSessionStatus(key: string, status: SessionStatus): Promise<void> {\n await this.store.setStatus(key, status);\n this.emit('sessionStatusChanged', { key, status });\n }\n\n // ========== Search ==========\n\n async searchSessions(query: string): Promise<SessionMetadata[]> {\n const result = await this.store.list({ search: query, sessionTypes: ['chat'], limit: 100 });\n return result.items;\n }\n\n async searchInSession(key: string, keyword: string): Promise<Message[]> {\n return this.store.searchInSession(key, keyword);\n }\n\n // ========== Export/Import ==========\n\n async exportSession(key: string, format: ExportFormat): Promise<string> {\n return this.store.exportSession(key, format);\n }\n\n async importSessionExport(\n targetKey: string,\n jsonContent: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n const result = await this.store.importSessionExport(targetKey, jsonContent);\n const metadata = await this.store.getMetadata(result.sessionKey);\n if (metadata) {\n this.emit('sessionCreated', metadata);\n }\n return result;\n }\n\n async forkSession(\n sourceKey: string,\n targetKey: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n const result = await this.store.forkSession(sourceKey, targetKey);\n const metadata = await this.store.getMetadata(result.sessionKey);\n if (metadata) {\n this.emit('sessionCreated', metadata);\n }\n return result;\n }\n\n async forkSessionRows(\n sourceKey: string,\n targetKey: string,\n options: { throughRow?: number } = {},\n ): Promise<{ sessionKey: string; rowCount: number }> {\n const result = await this.store.forkSessionRows(sourceKey, targetKey, options);\n const metadata = await this.store.getMetadata(result.sessionKey);\n if (metadata) {\n this.emit('sessionCreated', metadata);\n }\n return result;\n }\n\n // ========== Statistics ==========\n\n async getStats(): Promise<GlobalSessionStats> {\n return this.store.getStats();\n }\n\n // ========== Maintenance ==========\n\n async archiveOldSessions(olderThanDays: number): Promise<number> {\n const count = await this.store.archiveOld(olderThanDays);\n log.info({ count, olderThanDays }, 'Archived old sessions');\n return count;\n }\n\n // ========== Event Helpers ==========\n\n onSessionCreated(callback: (metadata: SessionMetadata) => void): void {\n this.on('sessionCreated', callback);\n }\n\n onSessionUpdated(callback: (data: { key: string; name?: string; tags?: string[] }) => void): void {\n this.on('sessionUpdated', callback);\n }\n\n onSessionDeleted(callback: (data: { key: string }) => void): void {\n this.on('sessionDeleted', callback);\n }\n\n onSessionArchived(callback: (data: { key: string }) => void): void {\n this.on('sessionArchived', callback);\n }\n\n onSessionRestored(callback: (data: { key: string }) => void): void {\n this.on('sessionRestored', callback);\n }\n\n onSessionPinned(callback: (data: { key: string }) => void): void {\n this.on('sessionPinned', callback);\n }\n\n onSessionUnpinned(callback: (data: { key: string }) => void): void {\n this.on('sessionUnpinned', callback);\n }\n\n onSessionStatusChanged(callback: (data: { key: string; status: SessionStatus }) => void): void {\n this.on('sessionStatusChanged', callback);\n }\n\n onSessionAccessed(callback: (data: { key: string }) => void): void {\n this.on('sessionAccessed', callback);\n }\n\n // ========== Store delegation (messages, compaction) ==========\n\n /** Load messages for a session key */\n async loadMessages(key: string) {\n return this.store.loadMessages(key);\n }\n\n /** Wrapped transcript document (stable id, compaction history); null if missing or not a valid envelope. */\n async loadTranscriptDocument(key: string): Promise<XopcSessionTranscriptV1 | null> {\n return this.store.loadTranscriptDocument(key);\n }\n\n /**\n * Runtime turns must use PiTranscriptManager.appendMessage; this entry point\n * is reserved for compaction, tests, and admin tools.\n */\n async saveMessages(key: string, messages: any[]) {\n return this.store.saveMessages(key, messages);\n }\n\n /**\n * Append `kind: 'context'` transcript row (persisted, excluded from {@link loadMessages} / LLM).\n */\n async appendTranscriptContextEntry(\n key: string,\n entry: Omit<XopcTranscriptContextEntry, 'kind'> & Partial<Pick<XopcTranscriptContextEntry, 'kind'>>,\n ): Promise<void> {\n await this.store.appendTranscriptContextEntry(key, entry);\n this.emit('sessionUpdated', { key });\n }\n\n async appendTranscriptLabelEntry(\n key: string,\n entry: { targetId: string; label?: string },\n ): Promise<void> {\n await this.store.appendTranscriptLabelEntry(key, entry);\n this.emit('sessionUpdated', { key });\n }\n\n async appendTranscriptCustomEntry(\n key: string,\n entry: { customType: string; data?: unknown },\n ): Promise<void> {\n await this.store.appendTranscriptCustomEntry(key, entry);\n this.emit('sessionUpdated', { key });\n }\n\n async appendTranscriptCustomMessageEntry(\n key: string,\n entry: {\n customType: string;\n content?: string | unknown[];\n display?: boolean;\n details?: unknown;\n },\n ): Promise<void> {\n await this.store.appendTranscriptCustomMessageEntry(key, entry);\n this.emit('sessionUpdated', { key });\n }\n\n async appendTranscriptBashExecutionEntry(\n key: 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<void> {\n await this.store.appendTranscriptBashExecutionEntry(key, entry);\n this.emit('sessionUpdated', { key });\n }\n\n /** Delete session data */\n async delete(key: string): Promise<void> {\n await this.store.delete(key);\n }\n\n /** Archive transcript and start a new session id for the same key. */\n async resetSession(\n key: string,\n ): Promise<{ sessionId: string; previousSessionId: string } | null> {\n const result = await this.store.reset(key);\n if (result) {\n this.emit('sessionUpdated', { key });\n }\n return result;\n }\n\n /** Token/window stats for a message list */\n getWindowStats(messages: any[]) {\n return this.store.getWindowStats(messages);\n }\n\n /** Prepare compaction run */\n prepareCompaction(key: string, messages: any[], contextWindow: number) {\n return this.store.prepareCompaction(key, messages, contextWindow);\n }\n\n /** Compact session messages */\n compact(\n key: string,\n messages: any[],\n contextWindow: number,\n instructions?: string,\n force?: boolean,\n ): Promise<CompactionResult> {\n return this.store.compact(key, messages, contextWindow, instructions, force);\n }\n\n /** Compaction stats for a session */\n async getCompactionStats(key: string) {\n return this.store.getCompactionStats(key);\n }\n\n /** List pre-compaction transcript snapshots (newest first). */\n listCompactionCheckpoints(key: string) {\n return this.store.listCompactionCheckpoints(key);\n }\n\n getCompactionCheckpointDetail(key: string, checkpointId: string) {\n return this.store.getCompactionCheckpointDetail(key, checkpointId);\n }\n\n restoreCompactionCheckpoint(key: string, checkpointId: string) {\n return this.store.restoreCompactionCheckpoint(key, checkpointId);\n }\n\n /** Estimate token usage for messages */\n async estimateTokenUsage(key: string, messages: any[]): Promise<number> {\n return this.store.estimateTokens(messages);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"manager.js","names":["EventEmitter"],"sources":["../../../src/session/manager.ts"],"sourcesContent":["// Session manager - high-level session management service\n\nimport EventEmitter from 'events';\nimport { createLogger } from '../utils/logger.js';\nimport { SessionStore } from './store.js';\nimport type {\n SessionMetadata,\n SessionDetail,\n SessionListQuery,\n PaginatedResult,\n GlobalSessionStats,\n ExportFormat,\n SessionStatus,\n} from './types.js';\nimport type { SessionMetadataSeed } from '../storage/sqlite/index.js';\nimport type { Message } from './types.js';\nimport type { CompactionConfig, CompactionResult } from '../agent/memory/compaction.js';\nimport type { XopcSessionTranscriptV1 } from './transcript-format.js';\nimport type { XopcTranscriptContextEntry } from './session-context-for-llm.js';\nimport { applySessionPatchToMetadata, type SessionPatchBody } from './patch-metadata.js';\nimport type { WindowConfig } from '../agent/memory/window.js';\nimport type { Config } from '../config/schema.js';\n\nconst log = createLogger('SessionIndex');\n\nexport interface SessionIndexConfig {\n config: Config;\n windowConfig?: Partial<WindowConfig>;\n compactionConfig?: Partial<CompactionConfig>;\n}\n\nexport class SessionIndex extends EventEmitter {\n private store: SessionStore;\n\n constructor(config: SessionIndexConfig) {\n super();\n this.store = new SessionStore(\n {\n config: config.config,\n },\n config.windowConfig,\n config.compactionConfig\n );\n }\n\n async initialize(): Promise<void> {\n await this.store.initialize();\n this.emit('ready');\n }\n\n /** Low-level store (e.g. cron resolving weixin delivery from session index). */\n getStore(): SessionStore {\n return this.store;\n }\n\n // ========== CRUD Operations ==========\n\n async listSessions(query?: SessionListQuery): Promise<PaginatedResult<SessionMetadata>> {\n return this.store.list(query);\n }\n\n async listSubagents(query: SessionListQuery = {}): Promise<PaginatedResult<SessionMetadata>> {\n return this.store.list({\n ...query,\n sessionTypes: ['workflow-subagent'],\n includeHidden: true,\n });\n }\n\n async getSession(\n key: string,\n options?: { includeTranscriptSummary?: boolean; includeTranscriptRows?: boolean },\n ): Promise<SessionDetail | null> {\n const session = await this.store.get(key, options);\n if (session) {\n this.emit('sessionAccessed', { key });\n }\n return session;\n }\n\n async getSessionMessagePage(\n key: string,\n options?: {\n offset?: number;\n limit?: number;\n before?: string;\n includeTranscriptSummary?: boolean;\n includeTranscriptRows?: boolean;\n },\n ): Promise<{\n session: SessionDetail;\n pagination: {\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n before?: string;\n nextBeforeCursor?: string;\n };\n } | null> {\n const result = await this.store.getMessagePage(key, options);\n if (result) {\n this.emit('sessionAccessed', { key });\n }\n return result;\n }\n\n /**\n * OpenClaw-style `sessions.patch`: partial metadata (name, tags, customData shallow merge).\n */\n async patchSession(\n key: string,\n patch: SessionPatchBody,\n ): Promise<{ ok: true } | { ok: false; error: string }> {\n const meta = await this.store.getMetadata(key);\n if (!meta) {\n return { ok: false, error: 'Session not found' };\n }\n const updates = applySessionPatchToMetadata(meta, patch);\n if (Object.keys(updates).length === 0) {\n return { ok: true };\n }\n await this.store.updateMetadata(key, updates);\n this.emit('sessionUpdated', { key });\n return { ok: true };\n }\n\n async getSessionMetadata(key: string): Promise<SessionMetadata | null> {\n return this.store.getMetadata(key);\n }\n\n async resolveSessionKeyBySessionId(sessionId: string): Promise<string | null> {\n return this.store.resolveKeyBySessionId(sessionId);\n }\n\n async deleteSession(key: string): Promise<boolean> {\n const result = await this.store.delete(key);\n if (result) {\n this.emit('sessionDeleted', { key });\n }\n return result;\n }\n\n async deleteSessions(keys: string[]): Promise<{ success: string[]; failed: string[] }> {\n const result = await this.store.deleteMany(keys);\n for (const key of result.success) {\n this.emit('sessionDeleted', { key });\n }\n return result;\n }\n\n // ========== Metadata Updates ==========\n\n async renameSession(key: string, name: string): Promise<void> {\n const existing = await this.store.getMetadata(key);\n await this.store.updateMetadata(key, {\n name,\n customData: { ...(existing?.customData ?? {}), titleSource: 'user' },\n });\n this.emit('sessionUpdated', { key, name });\n }\n\n /** Partial metadata update (caller merges nested fields like `customData` when needed). */\n async updateSessionMetadata(key: string, updates: Partial<SessionMetadata>): Promise<void> {\n await this.store.updateMetadata(key, updates);\n this.emit('sessionUpdated', { key });\n }\n\n async tagSession(key: string, tags: string[]): Promise<void> {\n const existing = await this.store.getMetadata(key);\n if (!existing) {\n throw new Error(`Session not found: ${key}`);\n }\n\n // Merge tags, remove duplicates\n const mergedTags = [...new Set([...existing.tags, ...tags])];\n await this.store.updateMetadata(key, { tags: mergedTags });\n this.emit('sessionUpdated', { key, tags: mergedTags });\n }\n\n async untagSession(key: string, tags: string[]): Promise<void> {\n const existing = await this.store.getMetadata(key);\n if (!existing) {\n throw new Error(`Session not found: ${key}`);\n }\n\n const filteredTags = existing.tags.filter((t) => !tags.includes(t));\n await this.store.updateMetadata(key, { tags: filteredTags });\n this.emit('sessionUpdated', { key, tags: filteredTags });\n }\n\n async setSessionTags(key: string, tags: string[]): Promise<void> {\n await this.store.updateMetadata(key, { tags: [...new Set(tags)] });\n this.emit('sessionUpdated', { key, tags });\n }\n\n // ========== Status Management ==========\n\n async archiveSession(key: string): Promise<void> {\n await this.store.archive(key);\n this.emit('sessionArchived', { key });\n }\n\n async unarchiveSession(key: string): Promise<void> {\n await this.store.unarchive(key);\n this.emit('sessionRestored', { key });\n }\n\n async pinSession(key: string): Promise<void> {\n await this.store.pin(key);\n this.emit('sessionPinned', { key });\n }\n\n async unpinSession(key: string): Promise<void> {\n await this.store.unpin(key);\n this.emit('sessionUnpinned', { key });\n }\n\n async setSessionStatus(key: string, status: SessionStatus): Promise<void> {\n await this.store.setStatus(key, status);\n this.emit('sessionStatusChanged', { key, status });\n }\n\n // ========== Search ==========\n\n async searchSessions(query: string): Promise<SessionMetadata[]> {\n const result = await this.store.list({ search: query, sessionTypes: ['chat'], limit: 100 });\n return result.items;\n }\n\n async searchInSession(key: string, keyword: string): Promise<Message[]> {\n return this.store.searchInSession(key, keyword);\n }\n\n // ========== Export/Import ==========\n\n async exportSession(key: string, format: ExportFormat): Promise<string> {\n return this.store.exportSession(key, format);\n }\n\n async importSessionExport(\n targetKey: string,\n jsonContent: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n const result = await this.store.importSessionExport(targetKey, jsonContent);\n const metadata = await this.store.getMetadata(result.sessionKey);\n if (metadata) {\n this.emit('sessionCreated', metadata);\n }\n return result;\n }\n\n async forkSession(\n sourceKey: string,\n targetKey: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n const result = await this.store.forkSession(sourceKey, targetKey);\n const metadata = await this.store.getMetadata(result.sessionKey);\n if (metadata) {\n this.emit('sessionCreated', metadata);\n }\n return result;\n }\n\n async forkSessionRows(\n sourceKey: string,\n targetKey: string,\n options: { throughRow?: number } = {},\n ): Promise<{ sessionKey: string; rowCount: number }> {\n const result = await this.store.forkSessionRows(sourceKey, targetKey, options);\n const metadata = await this.store.getMetadata(result.sessionKey);\n if (metadata) {\n this.emit('sessionCreated', metadata);\n }\n return result;\n }\n\n // ========== Statistics ==========\n\n async getStats(): Promise<GlobalSessionStats> {\n return this.store.getStats();\n }\n\n // ========== Maintenance ==========\n\n async archiveOldSessions(olderThanDays: number): Promise<number> {\n const count = await this.store.archiveOld(olderThanDays);\n log.info({ count, olderThanDays }, 'Archived old sessions');\n return count;\n }\n\n // ========== Event Helpers ==========\n\n onSessionCreated(callback: (metadata: SessionMetadata) => void): void {\n this.on('sessionCreated', callback);\n }\n\n onSessionUpdated(callback: (data: { key: string; name?: string; tags?: string[] }) => void): void {\n this.on('sessionUpdated', callback);\n }\n\n onSessionDeleted(callback: (data: { key: string }) => void): void {\n this.on('sessionDeleted', callback);\n }\n\n onSessionArchived(callback: (data: { key: string }) => void): void {\n this.on('sessionArchived', callback);\n }\n\n onSessionRestored(callback: (data: { key: string }) => void): void {\n this.on('sessionRestored', callback);\n }\n\n onSessionPinned(callback: (data: { key: string }) => void): void {\n this.on('sessionPinned', callback);\n }\n\n onSessionUnpinned(callback: (data: { key: string }) => void): void {\n this.on('sessionUnpinned', callback);\n }\n\n onSessionStatusChanged(callback: (data: { key: string; status: SessionStatus }) => void): void {\n this.on('sessionStatusChanged', callback);\n }\n\n onSessionAccessed(callback: (data: { key: string }) => void): void {\n this.on('sessionAccessed', callback);\n }\n\n // ========== Store delegation (messages, compaction) ==========\n\n /** Load messages for a session key */\n async loadMessages(key: string) {\n return this.store.loadMessages(key);\n }\n\n /** Wrapped transcript document (stable id, compaction history); null if missing or not a valid envelope. */\n async loadTranscriptDocument(key: string): Promise<XopcSessionTranscriptV1 | null> {\n return this.store.loadTranscriptDocument(key);\n }\n\n /**\n * Runtime turns must use PiTranscriptManager.appendMessage; this entry point\n * is reserved for compaction, tests, and admin tools.\n */\n async saveMessages(\n key: string,\n messages: any[],\n options?: { metadata?: SessionMetadataSeed },\n ) {\n return this.store.saveMessages(key, messages, options);\n }\n\n /**\n * Append `kind: 'context'` transcript row (persisted, excluded from {@link loadMessages} / LLM).\n */\n async appendTranscriptContextEntry(\n key: string,\n entry: Omit<XopcTranscriptContextEntry, 'kind'> & Partial<Pick<XopcTranscriptContextEntry, 'kind'>>,\n ): Promise<void> {\n await this.store.appendTranscriptContextEntry(key, entry);\n this.emit('sessionUpdated', { key });\n }\n\n async appendTranscriptLabelEntry(\n key: string,\n entry: { targetId: string; label?: string },\n ): Promise<void> {\n await this.store.appendTranscriptLabelEntry(key, entry);\n this.emit('sessionUpdated', { key });\n }\n\n async appendTranscriptCustomEntry(\n key: string,\n entry: { customType: string; data?: unknown },\n ): Promise<void> {\n await this.store.appendTranscriptCustomEntry(key, entry);\n this.emit('sessionUpdated', { key });\n }\n\n async appendTranscriptCustomMessageEntry(\n key: string,\n entry: {\n customType: string;\n content?: string | unknown[];\n display?: boolean;\n details?: unknown;\n },\n ): Promise<void> {\n await this.store.appendTranscriptCustomMessageEntry(key, entry);\n this.emit('sessionUpdated', { key });\n }\n\n async appendTranscriptBashExecutionEntry(\n key: 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<void> {\n await this.store.appendTranscriptBashExecutionEntry(key, entry);\n this.emit('sessionUpdated', { key });\n }\n\n /** Delete session data */\n async delete(key: string): Promise<void> {\n await this.store.delete(key);\n }\n\n /** Archive transcript and start a new session id for the same key. */\n async resetSession(\n key: string,\n ): Promise<{ sessionId: string; previousSessionId: string } | null> {\n const result = await this.store.reset(key);\n if (result) {\n this.emit('sessionUpdated', { key });\n }\n return result;\n }\n\n /** Token/window stats for a message list */\n getWindowStats(messages: any[]) {\n return this.store.getWindowStats(messages);\n }\n\n /** Prepare compaction run */\n prepareCompaction(key: string, messages: any[], contextWindow: number) {\n return this.store.prepareCompaction(key, messages, contextWindow);\n }\n\n /** Compact session messages */\n compact(\n key: string,\n messages: any[],\n contextWindow: number,\n instructions?: string,\n force?: boolean,\n ): Promise<CompactionResult> {\n return this.store.compact(key, messages, contextWindow, instructions, force);\n }\n\n /** Compaction stats for a session */\n async getCompactionStats(key: string) {\n return this.store.getCompactionStats(key);\n }\n\n /** List pre-compaction transcript snapshots (newest first). */\n listCompactionCheckpoints(key: string) {\n return this.store.listCompactionCheckpoints(key);\n }\n\n getCompactionCheckpointDetail(key: string, checkpointId: string) {\n return this.store.getCompactionCheckpointDetail(key, checkpointId);\n }\n\n restoreCompactionCheckpoint(key: string, checkpointId: string) {\n return this.store.restoreCompactionCheckpoint(key, checkpointId);\n }\n\n /** Estimate token usage for messages */\n async estimateTokenUsage(key: string, messages: any[]): Promise<number> {\n return this.store.estimateTokens(messages);\n }\n}\n"],"mappings":"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"}
@@ -1,8 +1,6 @@
1
1
  import type { Config } from '../config/schema.js';
2
2
  import type { SessionResetConfig, SessionResetType } from './reset-policy.js';
3
- export declare function isThreadSessionKey(sessionKey?: string | null): boolean;
4
3
  export declare function resolveSessionResetType(params: {
5
- sessionKey?: string | null;
6
4
  isGroup?: boolean;
7
5
  isThread?: boolean;
8
6
  }): SessionResetType;