@xopcai/xopc 0.0.134 → 0.0.136

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 (215) 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/Combination-HAlzriaz.js +41 -0
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-mQ4lbZRf.js → agent-avatar-dicebear-CER17NHM.js} +1 -1
  5. package/dist/gateway/static/root/assets/agents-Bv5R-N8Q.js +2 -0
  6. package/dist/gateway/static/root/assets/{app-management-settings-panel-Bj2YJiQe.js → app-management-settings-panel-B3Ap_wpX.js} +1 -1
  7. package/dist/gateway/static/root/assets/{appearance-settings-B93olV5Z.js → appearance-settings-3kBO22Xd.js} +1 -1
  8. package/dist/gateway/static/root/assets/apps-page-BHYbGQcZ.js +1 -0
  9. package/dist/gateway/static/root/assets/{archive-plugin-D_yymKFJ.js → archive-plugin-D_vGeNB8.js} +1 -1
  10. package/dist/gateway/static/root/assets/{attachment-load-kfyH3HV-.js → attachment-load-hWV_8pHL.js} +1 -1
  11. package/dist/gateway/static/root/assets/{automation-suggestion-card-DyGBB2WH.js → automation-suggestion-card-DVKmVTIo.js} +1 -1
  12. package/dist/gateway/static/root/assets/automations-page-BCTXpOBp.js +4 -0
  13. package/dist/gateway/static/root/assets/{binary-plugins-BX1AXf98.js → binary-plugins-BuXIeSPc.js} +1 -1
  14. package/dist/gateway/static/root/assets/{block-editor-D-WEVNHU.js → block-editor-C1m0u-UU.js} +39 -39
  15. package/dist/gateway/static/root/assets/browser-settings-page-ChUF08V5.js +8 -0
  16. package/dist/gateway/static/root/assets/capability-presets-api-B7WWYaEh.js +1 -0
  17. package/dist/gateway/static/root/assets/{capability-presets-settings-panel-CHExb90z.js → capability-presets-settings-panel-D_Bh1PnA.js} +1 -1
  18. package/dist/gateway/static/root/assets/{channel-recipient-api-RANYrzfa.js → channel-recipient-api-sV06LCht.js} +1 -1
  19. package/dist/gateway/static/root/assets/channels-settings-BI2ChQc7.js +1 -0
  20. package/dist/gateway/static/root/assets/channels-status-swr-MY4Xh4fM.js +1 -0
  21. package/dist/gateway/static/root/assets/connectors-page-qgvEp09M.js +2 -0
  22. package/dist/gateway/static/root/assets/desktop-pet-BxMJlvb-.js +1 -0
  23. package/dist/gateway/static/root/assets/{desktop-pet-settings-C3JDiXSu.js → desktop-pet-settings-DCvpDImn.js} +1 -1
  24. package/dist/gateway/static/root/assets/{directory-picker-path-field-MT71cjgl.js → directory-picker-path-field-D1nxs9PA.js} +1 -1
  25. package/dist/gateway/static/root/assets/dist-Mys7eFJ5.js +1 -0
  26. package/dist/gateway/static/root/assets/{dreaming-settings-Cfx_Qg4R.js → dreaming-settings-EAWkpOgw.js} +3 -3
  27. package/dist/gateway/static/root/assets/{extension-debug-page-Wp23cLkf.js → extension-debug-page-LUQ8dE8z.js} +1 -1
  28. package/dist/gateway/static/root/assets/{extension-page-B5HRNtVY.js → extension-page-B_mnhoRd.js} +1 -1
  29. package/dist/gateway/static/root/assets/{extension-settings-page-zPfMpsYi.js → extension-settings-page-BWQsGmvW.js} +1 -1
  30. package/dist/gateway/static/root/assets/{fetch-6HXXdO77.js → fetch-BWXmBizE.js} +1 -1
  31. package/dist/gateway/static/root/assets/{gateway-config-api-CsS_3Ii6.js → gateway-config-api-M9bzPASf.js} +1 -1
  32. package/dist/gateway/static/root/assets/{gateway-settings-DOxh0hei.js → gateway-settings-CbgtpprN.js} +1 -1
  33. package/dist/gateway/static/root/assets/{gateway-startup-retry-BE1M9fH_.js → gateway-startup-retry-BALL91S7.js} +1 -1
  34. package/dist/gateway/static/root/assets/goal-create-dialog-CbP4t9aM.js +1 -0
  35. package/dist/gateway/static/root/assets/goal-detail-page-DlwI-MZZ.js +4 -0
  36. package/dist/gateway/static/root/assets/{goals-config-api--3Vzceq-.js → goals-config-api-BBhFVzBS.js} +1 -1
  37. package/dist/gateway/static/root/assets/goals-page-D26r7knp.js +2 -0
  38. package/dist/gateway/static/root/assets/{goals-settings-Dg_hQNVC.js → goals-settings-DdmVG_TR.js} +1 -1
  39. package/dist/gateway/static/root/assets/{heartbeat-settings-CecoDTZJ.js → heartbeat-settings-DZW2Xq-U.js} +1 -1
  40. package/dist/gateway/static/root/assets/index-CPwVzi9x.css +1 -0
  41. package/dist/gateway/static/root/assets/index-DZOm5_ex.js +262 -0
  42. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BWMLAFxf.js → keyboard-shortcuts-settings-1IWd88-T.js} +1 -1
  43. package/dist/gateway/static/root/assets/logs-page-DxoKJO7-.js +7 -0
  44. package/dist/gateway/static/root/assets/{markdown-split-5tvDwUMS.js → markdown-split-BtdBj32h.js} +1 -1
  45. package/dist/gateway/static/root/assets/{media-plugins-RSjPG4B0.js → media-plugins-CpoPj3s8.js} +1 -1
  46. package/dist/gateway/static/root/assets/messages-CQ_k8RHj.js +3 -0
  47. package/dist/gateway/static/root/assets/models-hub-panel-D1Rp__dV.js +2 -0
  48. package/dist/gateway/static/root/assets/notes-page-CgR1bO2W.js +1 -0
  49. package/dist/gateway/static/root/assets/notes-workbench-ozsnmDTV.js +2 -0
  50. package/dist/gateway/static/root/assets/{page-tabs-DYNWfN6S.js → page-tabs-CVLqCfQB.js} +1 -1
  51. package/dist/gateway/static/root/assets/{preview-open-alternatives-B50PdIc8.js → preview-open-alternatives-CKUXJk2t.js} +1 -1
  52. package/dist/gateway/static/root/assets/project-detail-page-B4dqFAQF.js +1 -0
  53. package/dist/gateway/static/root/assets/projects-page-DlRGXv4a.js +1 -0
  54. package/dist/gateway/static/root/assets/remote-access-hub-rZEYQMZV.js +1 -0
  55. package/dist/gateway/static/root/assets/{save-bar-controls-DggNb0SF.js → save-bar-controls-BtGni268.js} +1 -1
  56. package/dist/gateway/static/root/assets/{schedule-field-T4ti2C8S.js → schedule-field-BDGzKVWn.js} +1 -1
  57. package/dist/gateway/static/root/assets/{schema-form-D6wGEfEa.js → schema-form-DQJsKMCE.js} +1 -1
  58. package/dist/gateway/static/root/assets/sessions-page-DxGoCGU8.js +1 -0
  59. package/dist/gateway/static/root/assets/settings-advanced-gate-4e8ZQwxr.js +1 -0
  60. package/dist/gateway/static/root/assets/{settings-collapsible-section-Cgd_aLBV.js → settings-collapsible-section-c8XGd4-8.js} +1 -1
  61. package/dist/gateway/static/root/assets/{settings-form-section-DnDrLMWZ.js → settings-form-section-By0YiDWo.js} +1 -1
  62. package/dist/gateway/static/root/assets/{settings-loading-skeleton-A2OshMK8.js → settings-loading-skeleton-SYCPY-WM.js} +1 -1
  63. package/dist/gateway/static/root/assets/{settings-page-CXMK9Vb3.js → settings-page-D9NwvnBt.js} +1 -1
  64. package/dist/gateway/static/root/assets/{settings-page-layout-Da_LNPDS.js → settings-page-layout-CrICyGob.js} +1 -1
  65. package/dist/gateway/static/root/assets/setup-status-panel-IjK-hDxf.js +1 -0
  66. package/dist/gateway/static/root/assets/{share-preview-page-BGZV-PCJ.js → share-preview-page-Dfnk9M_m.js} +1 -1
  67. package/dist/gateway/static/root/assets/shares-settings-vQiRBra7.js +1 -0
  68. package/dist/gateway/static/root/assets/skill-reload-api-BIWv778A.js +1 -0
  69. package/dist/gateway/static/root/assets/skills-page-CruV215G.js +2 -0
  70. package/dist/gateway/static/root/assets/{system-settings-panel-D8uzXP0Y.js → system-settings-panel-BBVbSDFD.js} +1 -1
  71. package/dist/gateway/static/root/assets/{typed-models-lib-DQxv2BHa.js → typed-models-lib-CwFayhxu.js} +1 -1
  72. package/dist/gateway/static/root/assets/{use-channel-catalog-BCBfSoHx.js → use-channel-catalog-BKtHOM50.js} +1 -1
  73. package/dist/gateway/static/root/assets/{user-profile-settings-panel-DkZSHsIs.js → user-profile-settings-panel-_KDWzagh.js} +1 -1
  74. package/dist/gateway/static/root/assets/{utils-ivGpUrZi.js → utils-BiUjIHb4.js} +1 -1
  75. package/dist/gateway/static/root/assets/voice-api-key-field-agJqlm4o.js +1 -0
  76. package/dist/gateway/static/root/assets/work-item-detail-page-Bdw4ATCb.js +1 -0
  77. package/dist/gateway/static/root/assets/workflow-run-setup-panel-D-Dy6mTy.js +3 -0
  78. package/dist/gateway/static/root/assets/workflow-start-dialog-GammZ2GK.js +1 -0
  79. package/dist/gateway/static/root/assets/workflows-page-Cw5zrqti.js +18 -0
  80. package/dist/gateway/static/root/index.html +7 -8
  81. package/dist/package.js +1 -1
  82. package/dist/src/agent/image/inbound-image-handling.d.ts +1 -0
  83. package/dist/src/agent/image/inbound-image-handling.js +4 -1
  84. package/dist/src/agent/image/inbound-image-handling.js.map +1 -1
  85. package/dist/src/agent/image/tool-model-config.d.ts +18 -2
  86. package/dist/src/agent/image/tool-model-config.js +100 -14
  87. package/dist/src/agent/image/tool-model-config.js.map +1 -1
  88. package/dist/src/agent/image/vision-detection.js +4 -23
  89. package/dist/src/agent/image/vision-detection.js.map +1 -1
  90. package/dist/src/agent/inbound/attachment-pipeline.js +3 -0
  91. package/dist/src/agent/inbound/attachment-pipeline.js.map +1 -1
  92. package/dist/src/agent/messaging/command-handler.d.ts +5 -2
  93. package/dist/src/agent/messaging/command-handler.js +4 -3
  94. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  95. package/dist/src/agent/service/btw-query.d.ts +2 -0
  96. package/dist/src/agent/service/btw-query.js +4 -2
  97. package/dist/src/agent/service/btw-query.js.map +1 -1
  98. package/dist/src/agent/service/direct-turn-helpers.d.ts +2 -0
  99. package/dist/src/agent/service/direct-turn-helpers.js +1 -1
  100. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  101. package/dist/src/agent/service/process-direct-streaming.js +58 -1
  102. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  103. package/dist/src/agent/service.js +1 -1
  104. package/dist/src/agent/service.js.map +1 -1
  105. package/dist/src/agent/session/session-inspector.d.ts +2 -1
  106. package/dist/src/agent/session/session-inspector.js +5 -3
  107. package/dist/src/agent/session/session-inspector.js.map +1 -1
  108. package/dist/src/agent/tools/factory.js +2 -1
  109. package/dist/src/agent/tools/factory.js.map +1 -1
  110. package/dist/src/agent/tools/image-tool.d.ts +1 -0
  111. package/dist/src/agent/tools/image-tool.js +4 -1
  112. package/dist/src/agent/tools/image-tool.js.map +1 -1
  113. package/dist/src/chat-commands/builtins/review.js +94 -7
  114. package/dist/src/chat-commands/builtins/review.js.map +1 -1
  115. package/dist/src/chat-commands/context.d.ts +5 -3
  116. package/dist/src/chat-commands/context.js +5 -2
  117. package/dist/src/chat-commands/context.js.map +1 -1
  118. package/dist/src/chat-commands/index.d.ts +1 -1
  119. package/dist/src/chat-commands/index.js.map +1 -1
  120. package/dist/src/chat-commands/types.d.ts +12 -1
  121. package/dist/src/config/agent-profile.js +1 -1
  122. package/dist/src/gateway/agents-admin.d.ts +5 -0
  123. package/dist/src/gateway/agents-admin.js +6 -1
  124. package/dist/src/gateway/agents-admin.js.map +1 -1
  125. package/dist/src/gateway/hono/routes/models.js +2 -1
  126. package/dist/src/gateway/hono/routes/models.js.map +1 -1
  127. package/dist/src/gateway/hono/routes/projects.js +88 -0
  128. package/dist/src/gateway/hono/routes/projects.js.map +1 -1
  129. package/dist/src/gateway/hono/routes/sessions.js +2 -1
  130. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  131. package/dist/src/gateway/hono/routes/workflows.js +1 -0
  132. package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
  133. package/dist/src/gateway/image-capabilities.d.ts +7 -1
  134. package/dist/src/gateway/image-capabilities.js +16 -3
  135. package/dist/src/gateway/image-capabilities.js.map +1 -1
  136. package/dist/src/gateway/service/sessions-api.d.ts +1 -0
  137. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  138. package/dist/src/gateway/service.js +3 -3
  139. package/dist/src/projects/project-loop.js +2 -2
  140. package/dist/src/projects/project-loop.js.map +1 -1
  141. package/dist/src/review/review-types.js +2 -1
  142. package/dist/src/review/review-types.js.map +1 -1
  143. package/dist/src/session/client-history.d.ts +1 -0
  144. package/dist/src/session/client-history.js +56 -0
  145. package/dist/src/session/client-history.js.map +1 -1
  146. package/dist/src/session/index.js +1 -1
  147. package/dist/src/session/manager.d.ts +1 -0
  148. package/dist/src/session/manager.js.map +1 -1
  149. package/dist/src/session/store.d.ts +1 -0
  150. package/dist/src/session/store.js +30 -1
  151. package/dist/src/session/store.js.map +1 -1
  152. package/dist/src/tui/backends/embedded-backend.js +1 -1
  153. package/dist/src/tui/components/assistant-message.js +10 -3
  154. package/dist/src/tui/components/assistant-message.js.map +1 -1
  155. package/dist/src/tui/components/bash-execution.d.ts +4 -0
  156. package/dist/src/tui/components/bash-execution.js +15 -7
  157. package/dist/src/tui/components/bash-execution.js.map +1 -1
  158. package/dist/src/tui/tool-summary.js +9 -3
  159. package/dist/src/tui/tool-summary.js.map +1 -1
  160. package/dist/src/tui/tui-submit.d.ts +3 -0
  161. package/dist/src/tui/tui-submit.js +24 -1
  162. package/dist/src/tui/tui-submit.js.map +1 -1
  163. package/dist/src/tui/tui.js +29 -2
  164. package/dist/src/tui/tui.js.map +1 -1
  165. package/dist/src/work-items/index.d.ts +1 -0
  166. package/dist/src/work-items/index.js +2 -1
  167. package/dist/src/work-items/work-item-workflow-judge.d.ts +8 -0
  168. package/dist/src/work-items/work-item-workflow-judge.js +50 -0
  169. package/dist/src/work-items/work-item-workflow-judge.js.map +1 -0
  170. package/dist/src/workflows/domain/run.d.ts +17 -0
  171. package/dist/src/workflows/domain/run.js.map +1 -1
  172. package/dist/src/workflows/service/workflow-run-service.d.ts +3 -0
  173. package/dist/src/workflows/service/workflow-run-service.js +67 -0
  174. package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
  175. package/dist/src/workflows/service/workflow-run-service.types.d.ts +4 -1
  176. package/dist/src/workflows/service/workflow-session-bridge.d.ts +1 -0
  177. package/dist/src/workflows/service/workflow-session-bridge.js +45 -0
  178. package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
  179. package/package.json +1 -1
  180. package/dist/gateway/static/root/assets/Combination-BzaXOLd9.js +0 -41
  181. package/dist/gateway/static/root/assets/agents-BP0iRtiu.js +0 -2
  182. package/dist/gateway/static/root/assets/apps-page-12_2SDFq.js +0 -1
  183. package/dist/gateway/static/root/assets/automations-page-V-tw6y1o.js +0 -4
  184. package/dist/gateway/static/root/assets/browser-settings-page-aMPnxWXi.js +0 -8
  185. package/dist/gateway/static/root/assets/capability-presets-api-CsaY2Z8B.js +0 -1
  186. package/dist/gateway/static/root/assets/channels-settings-N8NujD--.js +0 -1
  187. package/dist/gateway/static/root/assets/channels-status-swr-BG_j4Ci-.js +0 -1
  188. package/dist/gateway/static/root/assets/connectors-page-zBJbFgnK.js +0 -2
  189. package/dist/gateway/static/root/assets/desktop-pet-DfA-hVlt.js +0 -1
  190. package/dist/gateway/static/root/assets/dist-CkobR7mk.js +0 -1
  191. package/dist/gateway/static/root/assets/dist-DokQNVMQ.js +0 -1
  192. package/dist/gateway/static/root/assets/goal-create-dialog-BOWSTGB-.js +0 -1
  193. package/dist/gateway/static/root/assets/goal-detail-page-CCLgkqj1.js +0 -4
  194. package/dist/gateway/static/root/assets/goals-page-CsfIp8Vl.js +0 -2
  195. package/dist/gateway/static/root/assets/index-BOme955B.js +0 -262
  196. package/dist/gateway/static/root/assets/index-DXUkWmeK.css +0 -1
  197. package/dist/gateway/static/root/assets/logs-page-CW7AMYhN.js +0 -7
  198. package/dist/gateway/static/root/assets/messages-DR9ODkb8.js +0 -3
  199. package/dist/gateway/static/root/assets/models-hub-panel-mKfeyGVq.js +0 -2
  200. package/dist/gateway/static/root/assets/notes-page-DYw5zPeJ.js +0 -1
  201. package/dist/gateway/static/root/assets/notes-workbench-SPdKpvSN.js +0 -2
  202. package/dist/gateway/static/root/assets/project-detail-page-D3yP4K3k.js +0 -1
  203. package/dist/gateway/static/root/assets/projects-page-B6EV45NR.js +0 -1
  204. package/dist/gateway/static/root/assets/remote-access-hub-9foX60FQ.js +0 -1
  205. package/dist/gateway/static/root/assets/sessions-page-BReBhxWd.js +0 -1
  206. package/dist/gateway/static/root/assets/settings-advanced-gate-DNDBN4nT.js +0 -1
  207. package/dist/gateway/static/root/assets/setup-status-panel-C8UtBMJ_.js +0 -1
  208. package/dist/gateway/static/root/assets/shares-settings-xXAMKlc7.js +0 -1
  209. package/dist/gateway/static/root/assets/skill-reload-api-CurjHfEM.js +0 -1
  210. package/dist/gateway/static/root/assets/skills-page-BRR7NQkL.js +0 -2
  211. package/dist/gateway/static/root/assets/voice-api-key-field-DCiECZN1.js +0 -1
  212. package/dist/gateway/static/root/assets/work-item-detail-page-DAel5CHH.js +0 -1
  213. package/dist/gateway/static/root/assets/workflow-run-setup-panel-2w0zj9eJ.js +0 -3
  214. package/dist/gateway/static/root/assets/workflow-start-dialog-B5xdDFV-.js +0 -1
  215. package/dist/gateway/static/root/assets/workflows-page-DRhiSwuU.js +0 -18
@@ -1 +1 @@
1
- {"version":3,"file":"sessions-api.js","names":[],"sources":["../../../../src/gateway/service/sessions-api.ts"],"sourcesContent":["/**\n * GatewaySessionsApi — session CRUD, search, compaction, tag/pin/archive,\n * stats, and chat-id grouping for the gateway REST surface.\n *\n * Twenty-four methods, previously sitting on `GatewayService` as a mix of\n * one-line `sessionIndex.*` delegations and small composite operations (e.g.\n * `restoreCheckpoint` also evicts the in-memory agent, `runCompaction` also\n * appends a transcript context entry). Centralising them here lets routes\n * depend on the narrow `GatewaySessionsApi` surface instead of the full\n * `GatewayService`, and keeps the gateway composition root focused on\n * lifecycle + wiring.\n */\n\nimport type { AgentService } from '../../agent/service.js';\nimport type { CompactionResult } from '../../agent/memory/compaction.js';\nimport { retireSessionMcpRuntimeForSessionKey } from '../../agent/mcp/bundle-mcp-tools.js';\nimport { SessionIndex } from '../../session/index.js';\nimport type { ExportFormat, SessionListQuery } from '../../session/types.js';\nimport { transcriptRowsToClientHistory } from '../../session/client-history.js';\nimport { buildSessionTimeline } from '../../session/transcript-outline.js';\nimport type { SessionPatchBody } from '../../session/patch-metadata.js';\nimport { collectMediaUrisFromMessages, deleteMediaUris } from '../../media/session-references.js';\nimport { getDistinctSessionChatIds } from './session-chat-ids.js';\nimport { performSessionReset, type SessionResetResult } from '../session-reset-service.js';\n\nfunction clampWindowSpan(value: number | undefined, fallback: number): number {\n const parsed = Math.trunc(value ?? fallback);\n if (!Number.isFinite(parsed)) return fallback;\n return Math.min(200, Math.max(0, parsed));\n}\n\nexport interface GatewaySessionsApiOptions {\n sessionIndex: SessionIndex;\n /** Resolves the live agent service (created lazily; throws if gateway is starting). */\n getAgentService: () => AgentService;\n /** Read-only view of in-flight webchat runs (per session key → run id). */\n getActiveWebchatRunId: (sessionKey: string) => string | undefined;\n}\n\nexport class GatewaySessionsApi {\n private readonly opts: GatewaySessionsApiOptions;\n\n constructor(opts: GatewaySessionsApiOptions) {\n this.opts = opts;\n }\n\n // ── List / get ─────────────────────────────────────────────────────────\n\n listSessions(query?: SessionListQuery) {\n return this.opts.sessionIndex.listSessions(query);\n }\n\n /** Subagent sessions have keys starting with `subagent:`. */\n listSubagents(query?: SessionListQuery) {\n return this.opts.sessionIndex.listSubagents(query);\n }\n\n getSession(\n key: string,\n options?: { includeTranscriptSummary?: boolean; includeTranscriptRows?: boolean },\n ) {\n return this.opts.sessionIndex.getSession(key, options);\n }\n\n async resolveSession(input: {\n key?: string;\n sessionKey?: string;\n sessionId?: string;\n }): Promise<\n | {\n sessionKey: string;\n sessionId: string;\n session: Awaited<ReturnType<GatewaySessionsApi['getSession']>>;\n }\n | null\n > {\n const explicitKey = input.sessionKey?.trim() || input.key?.trim();\n const resolvedKey =\n explicitKey ||\n (input.sessionId?.trim()\n ? await this.opts.sessionIndex.resolveSessionKeyBySessionId(input.sessionId.trim())\n : null);\n if (!resolvedKey) {\n return null;\n }\n const session = await this.getSession(resolvedKey);\n if (!session?.sessionId) {\n return null;\n }\n return { sessionKey: resolvedKey, sessionId: session.sessionId, session };\n }\n\n /** Read-only: in-flight webchat agent run for this session key, if any. */\n getActiveRun(sessionKey: string): { active: boolean; runId?: string } {\n const key = sessionKey.trim();\n if (!key) return { active: false };\n const runId = this.opts.getActiveWebchatRunId(key)?.trim();\n if (!runId) return { active: false };\n return { active: true, runId };\n }\n\n getMessagePage(\n key: string,\n options?: {\n offset?: number;\n limit?: number;\n before?: string;\n includeTranscriptSummary?: boolean;\n includeTranscriptRows?: boolean;\n },\n ) {\n return this.opts.sessionIndex.getSessionMessagePage(key, options);\n }\n\n async getTimeline(key: string) {\n const metadata = await this.opts.sessionIndex.getSessionMetadata(key);\n if (!metadata) return null;\n const rows = await this.opts.sessionIndex.getStore().loadTranscriptRows(key);\n return buildSessionTimeline(rows);\n }\n\n async getTranscriptWindow(\n key: string,\n options: { rowNumber: number; before?: number; after?: number },\n ) {\n const metadata = await this.opts.sessionIndex.getSessionMetadata(key);\n if (!metadata) return null;\n const rows = await this.opts.sessionIndex.getStore().loadTranscriptRows(key);\n const totalRows = rows.length;\n if (totalRows === 0) {\n return {\n messages: [],\n startRowNumber: 0,\n endRowNumber: 0,\n totalRows,\n };\n }\n\n const targetRowNumber = Math.min(totalRows, Math.max(1, Math.trunc(options.rowNumber)));\n const before = clampWindowSpan(options.before, 80);\n const after = clampWindowSpan(options.after, 120);\n const startRowNumber = Math.max(1, targetRowNumber - before);\n const endRowNumber = Math.min(totalRows, targetRowNumber + after);\n\n return {\n messages: transcriptRowsToClientHistory(rows, { startRowNumber, endRowNumber }),\n startRowNumber,\n endRowNumber,\n totalRows,\n };\n }\n\n // ── Metadata patches ──────────────────────────────────────────────────\n\n patch(key: string, body: SessionPatchBody): Promise<{ ok: true } | { ok: false; error: string }> {\n return this.opts.sessionIndex.patchSession(key, body);\n }\n\n async getAgentConfig(sessionKey: string) {\n return this.opts.getAgentService().sessionInspector.agentConfig(sessionKey);\n }\n\n /** Resolved markdown workspace for a session (after hydration / mkdir). */\n getEffectiveWorkspacePath(sessionKey: string): Promise<string> {\n return this.opts.getAgentService().getEffectiveWorkspacePathForSession(sessionKey);\n }\n\n patchAgentConfig(\n sessionKey: string,\n body: {\n thinkingLevel?: string;\n model?: string | null;\n reasoningLevel?: string;\n verboseLevel?: string;\n workingDirectory?: string;\n },\n ) {\n return this.opts.getAgentService().sessionConfig.patch(sessionKey, body);\n }\n\n // ── Compaction checkpoints ────────────────────────────────────────────\n\n listCompactionCheckpoints(key: string) {\n return this.opts.sessionIndex.listCompactionCheckpoints(key);\n }\n\n getCompactionCheckpoint(key: string, checkpointId: string) {\n return this.opts.sessionIndex.getCompactionCheckpointDetail(key, checkpointId);\n }\n\n async restoreCompactionCheckpoint(key: string, checkpointId: string): Promise<void> {\n await this.opts.sessionIndex.restoreCompactionCheckpoint(key, checkpointId);\n this.opts.getAgentService().evictSessionAgent(key);\n }\n\n async runCompaction(\n key: string,\n options?: { instructions?: string; force?: boolean },\n ): Promise<CompactionResult> {\n const result = await this.opts.getAgentService().sessionInspector.compact(key, options);\n if (result.compacted) {\n void this.opts.sessionIndex\n .appendTranscriptContextEntry(key, {\n text: 'Session transcript compacted',\n data: {\n firstKeptIndex: result.firstKeptIndex,\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n summaryPreview: result.summary.slice(0, 500),\n },\n })\n .catch(() => {});\n }\n return result;\n }\n\n // ── Lifecycle (delete / rename / tag / pin / archive) ─────────────────\n\n async delete(key: string): Promise<{ deleted: boolean }> {\n const messages = await this.opts.sessionIndex.loadMessages(key).catch(() => []);\n const result = await this.opts.sessionIndex.deleteSession(key);\n if (result) {\n await deleteMediaUris(collectMediaUrisFromMessages(messages));\n this.opts.getAgentService().evictSessionAgent(key);\n await retireSessionMcpRuntimeForSessionKey({ sessionKey: key, reason: 'session-delete' });\n }\n return { deleted: result };\n }\n\n /** Reset transcript in place (archive + new session id); preserves session key and overrides. */\n reset(key: string): Promise<SessionResetResult> {\n return performSessionReset(key, {\n sessionIndex: this.opts.sessionIndex,\n getAgentService: this.opts.getAgentService,\n });\n }\n\n deleteMany(keys: string[]): Promise<{ success: string[]; failed: string[] }> {\n return this.opts.sessionIndex.deleteSessions(keys);\n }\n\n async rename(key: string, name: string): Promise<{ renamed: boolean }> {\n await this.opts.sessionIndex.renameSession(key, name);\n return { renamed: true };\n }\n\n async tag(key: string, tags: string[]): Promise<{ tagged: boolean }> {\n await this.opts.sessionIndex.tagSession(key, tags);\n return { tagged: true };\n }\n\n async untag(key: string, tags: string[]): Promise<{ untagged: boolean }> {\n await this.opts.sessionIndex.untagSession(key, tags);\n return { untagged: true };\n }\n\n async archive(key: string): Promise<{ archived: boolean }> {\n await this.opts.sessionIndex.archiveSession(key);\n return { archived: true };\n }\n\n async unarchive(key: string): Promise<{ unarchived: boolean }> {\n await this.opts.sessionIndex.unarchiveSession(key);\n return { unarchived: true };\n }\n\n async pin(key: string): Promise<{ pinned: boolean }> {\n await this.opts.sessionIndex.pinSession(key);\n return { pinned: true };\n }\n\n async unpin(key: string): Promise<{ unpinned: boolean }> {\n await this.opts.sessionIndex.unpinSession(key);\n return { unpinned: true };\n }\n\n // ── Search + export + stats ───────────────────────────────────────────\n\n search(query: string) {\n return this.opts.sessionIndex.searchSessions(query);\n }\n\n searchIn(key: string, keyword: string) {\n return this.opts.sessionIndex.searchInSession(key, keyword);\n }\n\n async export(key: string, format: ExportFormat): Promise<{ content: string }> {\n const content = await this.opts.sessionIndex.exportSession(key, format);\n return { content };\n }\n\n importExport(\n targetKey: string,\n jsonContent: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n return this.opts.sessionIndex.importSessionExport(targetKey, jsonContent);\n }\n\n fork(\n key: string,\n targetKey: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n return this.opts.sessionIndex.forkSession(key, targetKey);\n }\n\n forkRows(\n key: string,\n targetKey: string,\n options: { throughRow?: number } = {},\n ): Promise<{ sessionKey: string; rowCount: number }> {\n return this.opts.sessionIndex.forkSessionRows(key, targetKey, options);\n }\n\n btwQuery(sessionKey: string, question: string): Promise<{ text: string; error?: string }> {\n return this.opts.getAgentService().sessionInspector.btwQuery(sessionKey, question);\n }\n\n stats() {\n return this.opts.sessionIndex.getStats();\n }\n\n /**\n * Distinct chat-id pairs from sessions, grouped by channel. Used by automation\n * configuration UI to seed the \"send to existing chat\" picker.\n */\n chatIds(channel?: string): Promise<\n Array<{\n channel: string;\n chatId: string;\n lastActive: string;\n accountId?: string;\n peerKind?: string;\n peerId?: string;\n }>\n > {\n return getDistinctSessionChatIds(this.opts.sessionIndex, channel);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sessions-api.js","names":[],"sources":["../../../../src/gateway/service/sessions-api.ts"],"sourcesContent":["/**\n * GatewaySessionsApi — session CRUD, search, compaction, tag/pin/archive,\n * stats, and chat-id grouping for the gateway REST surface.\n *\n * Twenty-four methods, previously sitting on `GatewayService` as a mix of\n * one-line `sessionIndex.*` delegations and small composite operations (e.g.\n * `restoreCheckpoint` also evicts the in-memory agent, `runCompaction` also\n * appends a transcript context entry). Centralising them here lets routes\n * depend on the narrow `GatewaySessionsApi` surface instead of the full\n * `GatewayService`, and keeps the gateway composition root focused on\n * lifecycle + wiring.\n */\n\nimport type { AgentService } from '../../agent/service.js';\nimport type { CompactionResult } from '../../agent/memory/compaction.js';\nimport { retireSessionMcpRuntimeForSessionKey } from '../../agent/mcp/bundle-mcp-tools.js';\nimport { SessionIndex } from '../../session/index.js';\nimport type { ExportFormat, SessionListQuery } from '../../session/types.js';\nimport { transcriptRowsToClientHistory } from '../../session/client-history.js';\nimport { buildSessionTimeline } from '../../session/transcript-outline.js';\nimport type { SessionPatchBody } from '../../session/patch-metadata.js';\nimport { collectMediaUrisFromMessages, deleteMediaUris } from '../../media/session-references.js';\nimport { getDistinctSessionChatIds } from './session-chat-ids.js';\nimport { performSessionReset, type SessionResetResult } from '../session-reset-service.js';\n\nfunction clampWindowSpan(value: number | undefined, fallback: number): number {\n const parsed = Math.trunc(value ?? fallback);\n if (!Number.isFinite(parsed)) return fallback;\n return Math.min(200, Math.max(0, parsed));\n}\n\nexport interface GatewaySessionsApiOptions {\n sessionIndex: SessionIndex;\n /** Resolves the live agent service (created lazily; throws if gateway is starting). */\n getAgentService: () => AgentService;\n /** Read-only view of in-flight webchat runs (per session key → run id). */\n getActiveWebchatRunId: (sessionKey: string) => string | undefined;\n}\n\nexport class GatewaySessionsApi {\n private readonly opts: GatewaySessionsApiOptions;\n\n constructor(opts: GatewaySessionsApiOptions) {\n this.opts = opts;\n }\n\n // ── List / get ─────────────────────────────────────────────────────────\n\n listSessions(query?: SessionListQuery) {\n return this.opts.sessionIndex.listSessions(query);\n }\n\n /** Subagent sessions have keys starting with `subagent:`. */\n listSubagents(query?: SessionListQuery) {\n return this.opts.sessionIndex.listSubagents(query);\n }\n\n getSession(\n key: string,\n options?: { includeTranscriptSummary?: boolean; includeTranscriptRows?: boolean },\n ) {\n return this.opts.sessionIndex.getSession(key, options);\n }\n\n async resolveSession(input: {\n key?: string;\n sessionKey?: string;\n sessionId?: string;\n }): Promise<\n | {\n sessionKey: string;\n sessionId: string;\n session: Awaited<ReturnType<GatewaySessionsApi['getSession']>>;\n }\n | null\n > {\n const explicitKey = input.sessionKey?.trim() || input.key?.trim();\n const resolvedKey =\n explicitKey ||\n (input.sessionId?.trim()\n ? await this.opts.sessionIndex.resolveSessionKeyBySessionId(input.sessionId.trim())\n : null);\n if (!resolvedKey) {\n return null;\n }\n const session = await this.getSession(resolvedKey);\n if (!session?.sessionId) {\n return null;\n }\n return { sessionKey: resolvedKey, sessionId: session.sessionId, session };\n }\n\n /** Read-only: in-flight webchat agent run for this session key, if any. */\n getActiveRun(sessionKey: string): { active: boolean; runId?: string } {\n const key = sessionKey.trim();\n if (!key) return { active: false };\n const runId = this.opts.getActiveWebchatRunId(key)?.trim();\n if (!runId) return { active: false };\n return { active: true, runId };\n }\n\n getMessagePage(\n key: string,\n options?: {\n offset?: number;\n limit?: number;\n before?: string;\n includeTranscriptSummary?: boolean;\n includeTranscriptRows?: boolean;\n includeContextRows?: boolean;\n },\n ) {\n return this.opts.sessionIndex.getSessionMessagePage(key, options);\n }\n\n async getTimeline(key: string) {\n const metadata = await this.opts.sessionIndex.getSessionMetadata(key);\n if (!metadata) return null;\n const rows = await this.opts.sessionIndex.getStore().loadTranscriptRows(key);\n return buildSessionTimeline(rows);\n }\n\n async getTranscriptWindow(\n key: string,\n options: { rowNumber: number; before?: number; after?: number },\n ) {\n const metadata = await this.opts.sessionIndex.getSessionMetadata(key);\n if (!metadata) return null;\n const rows = await this.opts.sessionIndex.getStore().loadTranscriptRows(key);\n const totalRows = rows.length;\n if (totalRows === 0) {\n return {\n messages: [],\n startRowNumber: 0,\n endRowNumber: 0,\n totalRows,\n };\n }\n\n const targetRowNumber = Math.min(totalRows, Math.max(1, Math.trunc(options.rowNumber)));\n const before = clampWindowSpan(options.before, 80);\n const after = clampWindowSpan(options.after, 120);\n const startRowNumber = Math.max(1, targetRowNumber - before);\n const endRowNumber = Math.min(totalRows, targetRowNumber + after);\n\n return {\n messages: transcriptRowsToClientHistory(rows, { startRowNumber, endRowNumber }),\n startRowNumber,\n endRowNumber,\n totalRows,\n };\n }\n\n // ── Metadata patches ──────────────────────────────────────────────────\n\n patch(key: string, body: SessionPatchBody): Promise<{ ok: true } | { ok: false; error: string }> {\n return this.opts.sessionIndex.patchSession(key, body);\n }\n\n async getAgentConfig(sessionKey: string) {\n return this.opts.getAgentService().sessionInspector.agentConfig(sessionKey);\n }\n\n /** Resolved markdown workspace for a session (after hydration / mkdir). */\n getEffectiveWorkspacePath(sessionKey: string): Promise<string> {\n return this.opts.getAgentService().getEffectiveWorkspacePathForSession(sessionKey);\n }\n\n patchAgentConfig(\n sessionKey: string,\n body: {\n thinkingLevel?: string;\n model?: string | null;\n reasoningLevel?: string;\n verboseLevel?: string;\n workingDirectory?: string;\n },\n ) {\n return this.opts.getAgentService().sessionConfig.patch(sessionKey, body);\n }\n\n // ── Compaction checkpoints ────────────────────────────────────────────\n\n listCompactionCheckpoints(key: string) {\n return this.opts.sessionIndex.listCompactionCheckpoints(key);\n }\n\n getCompactionCheckpoint(key: string, checkpointId: string) {\n return this.opts.sessionIndex.getCompactionCheckpointDetail(key, checkpointId);\n }\n\n async restoreCompactionCheckpoint(key: string, checkpointId: string): Promise<void> {\n await this.opts.sessionIndex.restoreCompactionCheckpoint(key, checkpointId);\n this.opts.getAgentService().evictSessionAgent(key);\n }\n\n async runCompaction(\n key: string,\n options?: { instructions?: string; force?: boolean },\n ): Promise<CompactionResult> {\n const result = await this.opts.getAgentService().sessionInspector.compact(key, options);\n if (result.compacted) {\n void this.opts.sessionIndex\n .appendTranscriptContextEntry(key, {\n text: 'Session transcript compacted',\n data: {\n firstKeptIndex: result.firstKeptIndex,\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n summaryPreview: result.summary.slice(0, 500),\n },\n })\n .catch(() => {});\n }\n return result;\n }\n\n // ── Lifecycle (delete / rename / tag / pin / archive) ─────────────────\n\n async delete(key: string): Promise<{ deleted: boolean }> {\n const messages = await this.opts.sessionIndex.loadMessages(key).catch(() => []);\n const result = await this.opts.sessionIndex.deleteSession(key);\n if (result) {\n await deleteMediaUris(collectMediaUrisFromMessages(messages));\n this.opts.getAgentService().evictSessionAgent(key);\n await retireSessionMcpRuntimeForSessionKey({ sessionKey: key, reason: 'session-delete' });\n }\n return { deleted: result };\n }\n\n /** Reset transcript in place (archive + new session id); preserves session key and overrides. */\n reset(key: string): Promise<SessionResetResult> {\n return performSessionReset(key, {\n sessionIndex: this.opts.sessionIndex,\n getAgentService: this.opts.getAgentService,\n });\n }\n\n deleteMany(keys: string[]): Promise<{ success: string[]; failed: string[] }> {\n return this.opts.sessionIndex.deleteSessions(keys);\n }\n\n async rename(key: string, name: string): Promise<{ renamed: boolean }> {\n await this.opts.sessionIndex.renameSession(key, name);\n return { renamed: true };\n }\n\n async tag(key: string, tags: string[]): Promise<{ tagged: boolean }> {\n await this.opts.sessionIndex.tagSession(key, tags);\n return { tagged: true };\n }\n\n async untag(key: string, tags: string[]): Promise<{ untagged: boolean }> {\n await this.opts.sessionIndex.untagSession(key, tags);\n return { untagged: true };\n }\n\n async archive(key: string): Promise<{ archived: boolean }> {\n await this.opts.sessionIndex.archiveSession(key);\n return { archived: true };\n }\n\n async unarchive(key: string): Promise<{ unarchived: boolean }> {\n await this.opts.sessionIndex.unarchiveSession(key);\n return { unarchived: true };\n }\n\n async pin(key: string): Promise<{ pinned: boolean }> {\n await this.opts.sessionIndex.pinSession(key);\n return { pinned: true };\n }\n\n async unpin(key: string): Promise<{ unpinned: boolean }> {\n await this.opts.sessionIndex.unpinSession(key);\n return { unpinned: true };\n }\n\n // ── Search + export + stats ───────────────────────────────────────────\n\n search(query: string) {\n return this.opts.sessionIndex.searchSessions(query);\n }\n\n searchIn(key: string, keyword: string) {\n return this.opts.sessionIndex.searchInSession(key, keyword);\n }\n\n async export(key: string, format: ExportFormat): Promise<{ content: string }> {\n const content = await this.opts.sessionIndex.exportSession(key, format);\n return { content };\n }\n\n importExport(\n targetKey: string,\n jsonContent: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n return this.opts.sessionIndex.importSessionExport(targetKey, jsonContent);\n }\n\n fork(\n key: string,\n targetKey: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n return this.opts.sessionIndex.forkSession(key, targetKey);\n }\n\n forkRows(\n key: string,\n targetKey: string,\n options: { throughRow?: number } = {},\n ): Promise<{ sessionKey: string; rowCount: number }> {\n return this.opts.sessionIndex.forkSessionRows(key, targetKey, options);\n }\n\n btwQuery(sessionKey: string, question: string): Promise<{ text: string; error?: string }> {\n return this.opts.getAgentService().sessionInspector.btwQuery(sessionKey, question);\n }\n\n stats() {\n return this.opts.sessionIndex.getStats();\n }\n\n /**\n * Distinct chat-id pairs from sessions, grouped by channel. Used by automation\n * configuration UI to seed the \"send to existing chat\" picker.\n */\n chatIds(channel?: string): Promise<\n Array<{\n channel: string;\n chatId: string;\n lastActive: string;\n accountId?: string;\n peerKind?: string;\n peerId?: string;\n }>\n > {\n return getDistinctSessionChatIds(this.opts.sessionIndex, channel);\n }\n}\n"],"mappings":"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"}
@@ -51,6 +51,9 @@ import { NotesService } from "../notes/service.js";
51
51
  import "../notes/index.js";
52
52
  import { buildWorkflowChildTools } from "../agent/workflow/workflow-child-tools.js";
53
53
  import { WorkflowRunService } from "../workflows/service/workflow-run-service.js";
54
+ import { WorkItemService } from "../work-items/work-item-service.js";
55
+ import { buildWorkItemAgentContext } from "../work-items/agent-context.js";
56
+ import "../work-items/index.js";
54
57
  import { WorkflowSessionBridge } from "../workflows/service/workflow-session-bridge.js";
55
58
  import { HeartbeatService, heartbeatRunnerConfigFromConfig } from "./heartbeat/service.js";
56
59
  import "./heartbeat/index.js";
@@ -66,9 +69,6 @@ import { assertGatewayRuntimeConfig } from "./runtime-config.js";
66
69
  import { auditGatewayConfig } from "./security/audit.js";
67
70
  import { runBootstrapMigrationsSync } from "../migrations/runner.js";
68
71
  import { MobileNotificationService } from "../mobile/notification-service.js";
69
- import { WorkItemService } from "../work-items/work-item-service.js";
70
- import { buildWorkItemAgentContext } from "../work-items/agent-context.js";
71
- import "../work-items/index.js";
72
72
  import { scheduleGatewayUpdateCheck } from "../infra/update-startup.js";
73
73
  import { resolveChannelConnectDeferSet } from "./resolve-channel-connect-defer.js";
74
74
  import { restartGatewayProcessWithFreshPid } from "./respawn.js";
@@ -54,7 +54,7 @@ function buildProjectLoopOverview(input) {
54
54
  title: run.definitionId,
55
55
  detail: compactText(run.errorMessage || run.status),
56
56
  status: run.status,
57
- href: `/workflows/runs/${encodeURIComponent(run.runId)}`,
57
+ href: `/workflows?run=${encodeURIComponent(run.runId)}`,
58
58
  updatedAt: run.createdAt
59
59
  }))
60
60
  ].slice(0, 8);
@@ -83,7 +83,7 @@ function buildProjectLoopOverview(input) {
83
83
  detail: run.status,
84
84
  timestamp: run.createdAt,
85
85
  status: run.status,
86
- href: `/workflows/runs/${encodeURIComponent(run.runId)}`
86
+ href: `/workflows?run=${encodeURIComponent(run.runId)}`
87
87
  })),
88
88
  ...(input.memoryRecords ?? []).slice(0, 5).map((record) => ({
89
89
  id: `memory:${record.id}`,
@@ -1 +1 @@
1
- {"version":3,"file":"project-loop.js","names":[],"sources":["../../../src/projects/project-loop.ts"],"sourcesContent":["import type { GoalWithDetails } from '../goals/index.js';\nimport type { MemoryRecord } from '../agent/memory/types.js';\nimport type { ProjectWithDetails } from './types.js';\n\nexport type ProjectWorkflowRunBrief = {\n runId: string;\n definitionId: string;\n status: string;\n createdAt: number;\n errorMessage?: string;\n};\n\nexport type ProjectAttentionItem = {\n id: string;\n kind: 'blocked_goal' | 'stale_goal' | 'failed_workflow';\n title: string;\n detail?: string;\n status?: string;\n href?: string;\n updatedAt?: number;\n};\n\nexport type ProjectTimelineItem = {\n id: string;\n kind: 'session' | 'goal' | 'workflow' | 'memory';\n title: string;\n detail?: string;\n timestamp: number;\n status?: string;\n href?: string;\n};\n\nexport type ProjectLoopOverview = {\n activeGoals: GoalWithDetails[];\n blockedGoals: GoalWithDetails[];\n staleGoals: GoalWithDetails[];\n nextActions: Array<{\n goalId: string;\n title: string;\n nextAction?: string;\n status: string;\n updatedAt?: number;\n }>;\n attentionItems: ProjectAttentionItem[];\n timeline: ProjectTimelineItem[];\n digest: {\n status: 'healthy' | 'attention' | 'idle' | 'empty';\n summary: string;\n nextAction?: string;\n };\n failedWorkflowRuns: ProjectWorkflowRunBrief[];\n recommendedAction?: string;\n};\n\nconst ACTIVE_GOAL_STATUSES = new Set(['active', 'paused', 'blocked', 'needs_input']);\nconst STALE_GOAL_STATUSES = new Set(['active', 'paused']);\nconst DEFAULT_STALE_AFTER_MS = 7 * 24 * 60 * 60 * 1000;\n\nfunction goalUpdatedAtDesc(left: GoalWithDetails, right: GoalWithDetails): number {\n return right.updatedAt - left.updatedAt;\n}\n\nfunction compactText(value: string | undefined, max = 180): string | undefined {\n const trimmed = value?.trim().replace(/\\s+/g, ' ');\n if (!trimmed) return undefined;\n return trimmed.length <= max ? trimmed : `${trimmed.slice(0, max - 3).trimEnd()}...`;\n}\n\nexport function buildProjectLoopOverview(input: {\n project: ProjectWithDetails;\n goals: GoalWithDetails[];\n recentWorkflowRuns: ProjectWorkflowRunBrief[];\n failedWorkflowRuns?: ProjectWorkflowRunBrief[];\n memoryRecords?: MemoryRecord[];\n nowMs?: number;\n staleAfterMs?: number;\n}): ProjectLoopOverview {\n const nowMs = input.nowMs ?? Date.now();\n const staleCutoff = nowMs - (input.staleAfterMs ?? DEFAULT_STALE_AFTER_MS);\n const projectGoals = [...input.goals].sort(goalUpdatedAtDesc);\n const activeGoals = projectGoals.filter((goal) => ACTIVE_GOAL_STATUSES.has(goal.status)).slice(0, 6);\n const blockedGoals = projectGoals.filter((goal) => goal.status === 'blocked' || goal.status === 'needs_input').slice(0, 4);\n const staleGoals = projectGoals\n .filter((goal) => STALE_GOAL_STATUSES.has(goal.status) && goal.updatedAt < staleCutoff)\n .slice(0, 5);\n const nextActions = activeGoals\n .filter((goal) => goal.nextAction?.trim())\n .slice(0, 5)\n .map((goal) => ({\n goalId: goal.id,\n title: goal.title,\n nextAction: goal.nextAction,\n status: goal.status,\n updatedAt: goal.updatedAt,\n }));\n const failedWorkflowRuns = (input.failedWorkflowRuns ?? input.recentWorkflowRuns.filter((run) => (\n run.status === 'failed' || run.status === 'timeout' || run.status === 'cancelled'\n ))).slice(0, 5);\n\n const attentionItems: ProjectAttentionItem[] = [\n ...blockedGoals.map((goal) => ({\n id: `goal:${goal.id}:blocked`,\n kind: 'blocked_goal' as const,\n title: goal.title,\n detail: compactText(goal.blockedReason || goal.nextAction || goal.description),\n status: goal.status,\n href: `/goals/${encodeURIComponent(goal.id)}`,\n updatedAt: goal.updatedAt,\n })),\n ...staleGoals.map((goal) => ({\n id: `goal:${goal.id}:stale`,\n kind: 'stale_goal' as const,\n title: goal.title,\n detail: compactText(goal.nextAction || goal.description || 'Goal has not changed recently.'),\n status: goal.status,\n href: `/goals/${encodeURIComponent(goal.id)}`,\n updatedAt: goal.updatedAt,\n })),\n ...failedWorkflowRuns.map((run) => ({\n id: `workflow:${run.runId}:failed`,\n kind: 'failed_workflow' as const,\n title: run.definitionId,\n detail: compactText(run.errorMessage || run.status),\n status: run.status,\n href: `/workflows/runs/${encodeURIComponent(run.runId)}`,\n updatedAt: run.createdAt,\n })),\n ].slice(0, 8);\n\n const timeline: ProjectTimelineItem[] = [\n ...input.project.recentSessions.map((session) => ({\n id: `session:${session.key}`,\n kind: 'session' as const,\n title: session.name ?? session.key,\n detail: session.agentId,\n timestamp: Date.parse(session.updatedAt),\n href: `/chat/${encodeURIComponent(session.key)}`,\n })),\n ...projectGoals.slice(0, 8).map((goal) => ({\n id: `goal:${goal.id}`,\n kind: 'goal' as const,\n title: goal.title,\n detail: compactText(goal.nextAction || goal.blockedReason || goal.description),\n timestamp: goal.updatedAt,\n status: goal.status,\n href: `/goals/${encodeURIComponent(goal.id)}`,\n })),\n ...input.recentWorkflowRuns.map((run) => ({\n id: `workflow:${run.runId}`,\n kind: 'workflow' as const,\n title: run.definitionId,\n detail: run.status,\n timestamp: run.createdAt,\n status: run.status,\n href: `/workflows/runs/${encodeURIComponent(run.runId)}`,\n })),\n ...(input.memoryRecords ?? []).slice(0, 5).map((record) => ({\n id: `memory:${record.id}`,\n kind: 'memory' as const,\n title: record.kind,\n detail: compactText(record.content),\n timestamp: Date.parse(record.updatedAt),\n status: record.status,\n })),\n ]\n .filter((item) => Number.isFinite(item.timestamp))\n .sort((left, right) => right.timestamp - left.timestamp)\n .slice(0, 12);\n\n const recommendedAction = nextActions[0]?.nextAction\n ?? blockedGoals[0]?.blockedReason\n ?? staleGoals[0]?.nextAction\n ?? activeGoals[0]?.title\n ?? (input.project.brief ? 'Open a new chat to continue from the project brief.' : undefined);\n\n const digestStatus =\n attentionItems.length > 0 ? 'attention'\n : activeGoals.length > 0 || input.recentWorkflowRuns.some((run) => run.status === 'running' || run.status === 'queued') ? 'healthy'\n : timeline.length > 0 ? 'idle'\n : 'empty';\n const digestSummary =\n digestStatus === 'attention'\n ? `${attentionItems.length} item(s) need attention: ${blockedGoals.length} blocker(s), ${staleGoals.length} stale goal(s), ${failedWorkflowRuns.length} failed workflow run(s).`\n : digestStatus === 'healthy'\n ? `${activeGoals.length} active goal(s), ${nextActions.length} next action(s), and ${input.recentWorkflowRuns.length} recent workflow run(s).`\n : digestStatus === 'idle'\n ? 'Recent activity exists, but no active goal is driving the project.'\n : 'No project activity has been recorded yet.';\n\n return {\n activeGoals,\n blockedGoals,\n staleGoals,\n nextActions,\n attentionItems,\n timeline,\n digest: {\n status: digestStatus,\n summary: digestSummary,\n nextAction: recommendedAction,\n },\n failedWorkflowRuns,\n recommendedAction,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"project-loop.js","names":[],"sources":["../../../src/projects/project-loop.ts"],"sourcesContent":["import type { GoalWithDetails } from '../goals/index.js';\nimport type { MemoryRecord } from '../agent/memory/types.js';\nimport type { ProjectWithDetails } from './types.js';\n\nexport type ProjectWorkflowRunBrief = {\n runId: string;\n definitionId: string;\n status: string;\n createdAt: number;\n errorMessage?: string;\n};\n\nexport type ProjectAttentionItem = {\n id: string;\n kind: 'blocked_goal' | 'stale_goal' | 'failed_workflow';\n title: string;\n detail?: string;\n status?: string;\n href?: string;\n updatedAt?: number;\n};\n\nexport type ProjectTimelineItem = {\n id: string;\n kind: 'session' | 'goal' | 'workflow' | 'memory';\n title: string;\n detail?: string;\n timestamp: number;\n status?: string;\n href?: string;\n};\n\nexport type ProjectLoopOverview = {\n activeGoals: GoalWithDetails[];\n blockedGoals: GoalWithDetails[];\n staleGoals: GoalWithDetails[];\n nextActions: Array<{\n goalId: string;\n title: string;\n nextAction?: string;\n status: string;\n updatedAt?: number;\n }>;\n attentionItems: ProjectAttentionItem[];\n timeline: ProjectTimelineItem[];\n digest: {\n status: 'healthy' | 'attention' | 'idle' | 'empty';\n summary: string;\n nextAction?: string;\n };\n failedWorkflowRuns: ProjectWorkflowRunBrief[];\n recommendedAction?: string;\n};\n\nconst ACTIVE_GOAL_STATUSES = new Set(['active', 'paused', 'blocked', 'needs_input']);\nconst STALE_GOAL_STATUSES = new Set(['active', 'paused']);\nconst DEFAULT_STALE_AFTER_MS = 7 * 24 * 60 * 60 * 1000;\n\nfunction goalUpdatedAtDesc(left: GoalWithDetails, right: GoalWithDetails): number {\n return right.updatedAt - left.updatedAt;\n}\n\nfunction compactText(value: string | undefined, max = 180): string | undefined {\n const trimmed = value?.trim().replace(/\\s+/g, ' ');\n if (!trimmed) return undefined;\n return trimmed.length <= max ? trimmed : `${trimmed.slice(0, max - 3).trimEnd()}...`;\n}\n\nexport function buildProjectLoopOverview(input: {\n project: ProjectWithDetails;\n goals: GoalWithDetails[];\n recentWorkflowRuns: ProjectWorkflowRunBrief[];\n failedWorkflowRuns?: ProjectWorkflowRunBrief[];\n memoryRecords?: MemoryRecord[];\n nowMs?: number;\n staleAfterMs?: number;\n}): ProjectLoopOverview {\n const nowMs = input.nowMs ?? Date.now();\n const staleCutoff = nowMs - (input.staleAfterMs ?? DEFAULT_STALE_AFTER_MS);\n const projectGoals = [...input.goals].sort(goalUpdatedAtDesc);\n const activeGoals = projectGoals.filter((goal) => ACTIVE_GOAL_STATUSES.has(goal.status)).slice(0, 6);\n const blockedGoals = projectGoals.filter((goal) => goal.status === 'blocked' || goal.status === 'needs_input').slice(0, 4);\n const staleGoals = projectGoals\n .filter((goal) => STALE_GOAL_STATUSES.has(goal.status) && goal.updatedAt < staleCutoff)\n .slice(0, 5);\n const nextActions = activeGoals\n .filter((goal) => goal.nextAction?.trim())\n .slice(0, 5)\n .map((goal) => ({\n goalId: goal.id,\n title: goal.title,\n nextAction: goal.nextAction,\n status: goal.status,\n updatedAt: goal.updatedAt,\n }));\n const failedWorkflowRuns = (input.failedWorkflowRuns ?? input.recentWorkflowRuns.filter((run) => (\n run.status === 'failed' || run.status === 'timeout' || run.status === 'cancelled'\n ))).slice(0, 5);\n\n const attentionItems: ProjectAttentionItem[] = [\n ...blockedGoals.map((goal) => ({\n id: `goal:${goal.id}:blocked`,\n kind: 'blocked_goal' as const,\n title: goal.title,\n detail: compactText(goal.blockedReason || goal.nextAction || goal.description),\n status: goal.status,\n href: `/goals/${encodeURIComponent(goal.id)}`,\n updatedAt: goal.updatedAt,\n })),\n ...staleGoals.map((goal) => ({\n id: `goal:${goal.id}:stale`,\n kind: 'stale_goal' as const,\n title: goal.title,\n detail: compactText(goal.nextAction || goal.description || 'Goal has not changed recently.'),\n status: goal.status,\n href: `/goals/${encodeURIComponent(goal.id)}`,\n updatedAt: goal.updatedAt,\n })),\n ...failedWorkflowRuns.map((run) => ({\n id: `workflow:${run.runId}:failed`,\n kind: 'failed_workflow' as const,\n title: run.definitionId,\n detail: compactText(run.errorMessage || run.status),\n status: run.status,\n href: `/workflows?run=${encodeURIComponent(run.runId)}`,\n updatedAt: run.createdAt,\n })),\n ].slice(0, 8);\n\n const timeline: ProjectTimelineItem[] = [\n ...input.project.recentSessions.map((session) => ({\n id: `session:${session.key}`,\n kind: 'session' as const,\n title: session.name ?? session.key,\n detail: session.agentId,\n timestamp: Date.parse(session.updatedAt),\n href: `/chat/${encodeURIComponent(session.key)}`,\n })),\n ...projectGoals.slice(0, 8).map((goal) => ({\n id: `goal:${goal.id}`,\n kind: 'goal' as const,\n title: goal.title,\n detail: compactText(goal.nextAction || goal.blockedReason || goal.description),\n timestamp: goal.updatedAt,\n status: goal.status,\n href: `/goals/${encodeURIComponent(goal.id)}`,\n })),\n ...input.recentWorkflowRuns.map((run) => ({\n id: `workflow:${run.runId}`,\n kind: 'workflow' as const,\n title: run.definitionId,\n detail: run.status,\n timestamp: run.createdAt,\n status: run.status,\n href: `/workflows?run=${encodeURIComponent(run.runId)}`,\n })),\n ...(input.memoryRecords ?? []).slice(0, 5).map((record) => ({\n id: `memory:${record.id}`,\n kind: 'memory' as const,\n title: record.kind,\n detail: compactText(record.content),\n timestamp: Date.parse(record.updatedAt),\n status: record.status,\n })),\n ]\n .filter((item) => Number.isFinite(item.timestamp))\n .sort((left, right) => right.timestamp - left.timestamp)\n .slice(0, 12);\n\n const recommendedAction = nextActions[0]?.nextAction\n ?? blockedGoals[0]?.blockedReason\n ?? staleGoals[0]?.nextAction\n ?? activeGoals[0]?.title\n ?? (input.project.brief ? 'Open a new chat to continue from the project brief.' : undefined);\n\n const digestStatus =\n attentionItems.length > 0 ? 'attention'\n : activeGoals.length > 0 || input.recentWorkflowRuns.some((run) => run.status === 'running' || run.status === 'queued') ? 'healthy'\n : timeline.length > 0 ? 'idle'\n : 'empty';\n const digestSummary =\n digestStatus === 'attention'\n ? `${attentionItems.length} item(s) need attention: ${blockedGoals.length} blocker(s), ${staleGoals.length} stale goal(s), ${failedWorkflowRuns.length} failed workflow run(s).`\n : digestStatus === 'healthy'\n ? `${activeGoals.length} active goal(s), ${nextActions.length} next action(s), and ${input.recentWorkflowRuns.length} recent workflow run(s).`\n : digestStatus === 'idle'\n ? 'Recent activity exists, but no active goal is driving the project.'\n : 'No project activity has been recorded yet.';\n\n return {\n activeGoals,\n blockedGoals,\n staleGoals,\n nextActions,\n attentionItems,\n timeline,\n digest: {\n status: digestStatus,\n summary: digestSummary,\n nextAction: recommendedAction,\n },\n failedWorkflowRuns,\n recommendedAction,\n };\n}\n"],"mappings":"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"}
@@ -7,7 +7,8 @@ function formatLocation(finding) {
7
7
  }
8
8
  function formatReviewMarkdown(review) {
9
9
  const lines = ["Code review", ""];
10
- if (review.findings.length === 0) lines.push("No findings.");
10
+ const modelReviewIncomplete = review.source === "local" && review.overallCorrectness === "unknown";
11
+ if (review.findings.length === 0) lines.push(modelReviewIncomplete ? "No model findings were produced." : "No findings.");
11
12
  else {
12
13
  lines.push("Findings:");
13
14
  for (const finding of review.findings) {
@@ -1 +1 @@
1
- {"version":3,"file":"review-types.js","names":[],"sources":["../../../src/review/review-types.ts"],"sourcesContent":["export type ReviewPriority = 0 | 1 | 2 | 3;\n\nexport interface ReviewFinding {\n title: string;\n body: string;\n priority: ReviewPriority;\n confidenceScore?: number;\n filePath?: string;\n lineStart?: number;\n lineEnd?: number;\n}\n\nexport interface ReviewOutput {\n type: 'review';\n target: string;\n summary: string;\n findings: ReviewFinding[];\n overallCorrectness: 'patch is correct' | 'patch is incorrect' | 'unknown';\n overallExplanation: string;\n overallConfidenceScore?: number;\n generatedAt: number;\n source: 'model' | 'local';\n}\n\nfunction formatLocation(finding: ReviewFinding): string {\n if (!finding.filePath) return '';\n if (!finding.lineStart) return finding.filePath;\n const end = finding.lineEnd && finding.lineEnd !== finding.lineStart ? `-${finding.lineEnd}` : '';\n return `${finding.filePath}:${finding.lineStart}${end}`;\n}\n\nexport function formatReviewMarkdown(review: ReviewOutput): string {\n const lines: string[] = ['Code review', ''];\n if (review.findings.length === 0) {\n lines.push('No findings.');\n } else {\n lines.push('Findings:');\n for (const finding of review.findings) {\n const location = formatLocation(finding);\n const suffix = location ? ` - ${location}` : '';\n lines.push(`- [P${finding.priority}] ${finding.title}${suffix}`);\n if (finding.body.trim()) {\n lines.push(` ${finding.body.trim().replace(/\\n+/g, '\\n ')}`);\n }\n }\n }\n lines.push('');\n lines.push(`Overall correctness: ${review.overallCorrectness}`);\n if (review.overallExplanation.trim()) {\n lines.push(review.overallExplanation.trim());\n }\n if (typeof review.overallConfidenceScore === 'number') {\n lines.push(`Confidence: ${review.overallConfidenceScore.toFixed(2)}`);\n }\n return lines.join('\\n');\n}\n"],"mappings":";AAwBA,SAAS,eAAe,SAAgC;AACtD,KAAI,CAAC,QAAQ,SAAU,QAAO;AAC9B,KAAI,CAAC,QAAQ,UAAW,QAAO,QAAQ;CACvC,MAAM,MAAM,QAAQ,WAAW,QAAQ,YAAY,QAAQ,YAAY,IAAI,QAAQ,YAAY;AAC/F,QAAO,GAAG,QAAQ,SAAS,GAAG,QAAQ,YAAY;;AAGpD,SAAgB,qBAAqB,QAA8B;CACjE,MAAM,QAAkB,CAAC,eAAe,GAAG;AAC3C,KAAI,OAAO,SAAS,WAAW,EAC7B,OAAM,KAAK,eAAe;MACrB;AACL,QAAM,KAAK,YAAY;AACvB,OAAK,MAAM,WAAW,OAAO,UAAU;GACrC,MAAM,WAAW,eAAe,QAAQ;GACxC,MAAM,SAAS,WAAW,MAAM,aAAa;AAC7C,SAAM,KAAK,OAAO,QAAQ,SAAS,IAAI,QAAQ,QAAQ,SAAS;AAChE,OAAI,QAAQ,KAAK,MAAM,CACrB,OAAM,KAAK,KAAK,QAAQ,KAAK,MAAM,CAAC,QAAQ,QAAQ,OAAO,GAAG;;;AAIpE,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,wBAAwB,OAAO,qBAAqB;AAC/D,KAAI,OAAO,mBAAmB,MAAM,CAClC,OAAM,KAAK,OAAO,mBAAmB,MAAM,CAAC;AAE9C,KAAI,OAAO,OAAO,2BAA2B,SAC3C,OAAM,KAAK,eAAe,OAAO,uBAAuB,QAAQ,EAAE,GAAG;AAEvE,QAAO,MAAM,KAAK,KAAK"}
1
+ {"version":3,"file":"review-types.js","names":[],"sources":["../../../src/review/review-types.ts"],"sourcesContent":["export type ReviewPriority = 0 | 1 | 2 | 3;\n\nexport interface ReviewFinding {\n title: string;\n body: string;\n priority: ReviewPriority;\n confidenceScore?: number;\n filePath?: string;\n lineStart?: number;\n lineEnd?: number;\n}\n\nexport interface ReviewOutput {\n type: 'review';\n target: string;\n summary: string;\n findings: ReviewFinding[];\n overallCorrectness: 'patch is correct' | 'patch is incorrect' | 'unknown';\n overallExplanation: string;\n overallConfidenceScore?: number;\n generatedAt: number;\n source: 'model' | 'local';\n}\n\nfunction formatLocation(finding: ReviewFinding): string {\n if (!finding.filePath) return '';\n if (!finding.lineStart) return finding.filePath;\n const end = finding.lineEnd && finding.lineEnd !== finding.lineStart ? `-${finding.lineEnd}` : '';\n return `${finding.filePath}:${finding.lineStart}${end}`;\n}\n\nexport function formatReviewMarkdown(review: ReviewOutput): string {\n const lines: string[] = ['Code review', ''];\n const modelReviewIncomplete = review.source === 'local' && review.overallCorrectness === 'unknown';\n if (review.findings.length === 0) {\n lines.push(modelReviewIncomplete ? 'No model findings were produced.' : 'No findings.');\n } else {\n lines.push('Findings:');\n for (const finding of review.findings) {\n const location = formatLocation(finding);\n const suffix = location ? ` - ${location}` : '';\n lines.push(`- [P${finding.priority}] ${finding.title}${suffix}`);\n if (finding.body.trim()) {\n lines.push(` ${finding.body.trim().replace(/\\n+/g, '\\n ')}`);\n }\n }\n }\n lines.push('');\n lines.push(`Overall correctness: ${review.overallCorrectness}`);\n if (review.overallExplanation.trim()) {\n lines.push(review.overallExplanation.trim());\n }\n if (typeof review.overallConfidenceScore === 'number') {\n lines.push(`Confidence: ${review.overallConfidenceScore.toFixed(2)}`);\n }\n return lines.join('\\n');\n}\n"],"mappings":";AAwBA,SAAS,eAAe,SAAgC;AACtD,KAAI,CAAC,QAAQ,SAAU,QAAO;AAC9B,KAAI,CAAC,QAAQ,UAAW,QAAO,QAAQ;CACvC,MAAM,MAAM,QAAQ,WAAW,QAAQ,YAAY,QAAQ,YAAY,IAAI,QAAQ,YAAY;AAC/F,QAAO,GAAG,QAAQ,SAAS,GAAG,QAAQ,YAAY;;AAGpD,SAAgB,qBAAqB,QAA8B;CACjE,MAAM,QAAkB,CAAC,eAAe,GAAG;CAC3C,MAAM,wBAAwB,OAAO,WAAW,WAAW,OAAO,uBAAuB;AACzF,KAAI,OAAO,SAAS,WAAW,EAC7B,OAAM,KAAK,wBAAwB,qCAAqC,eAAe;MAClF;AACL,QAAM,KAAK,YAAY;AACvB,OAAK,MAAM,WAAW,OAAO,UAAU;GACrC,MAAM,WAAW,eAAe,QAAQ;GACxC,MAAM,SAAS,WAAW,MAAM,aAAa;AAC7C,SAAM,KAAK,OAAO,QAAQ,SAAS,IAAI,QAAQ,QAAQ,SAAS;AAChE,OAAI,QAAQ,KAAK,MAAM,CACrB,OAAM,KAAK,KAAK,QAAQ,KAAK,MAAM,CAAC,QAAQ,QAAQ,OAAO,GAAG;;;AAIpE,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,wBAAwB,OAAO,qBAAqB;AAC/D,KAAI,OAAO,mBAAmB,MAAM,CAClC,OAAM,KAAK,OAAO,mBAAmB,MAAM,CAAC;AAE9C,KAAI,OAAO,OAAO,2BAA2B,SAC3C,OAAM,KAAK,eAAe,OAAO,uBAAuB,QAAQ,EAAE,GAAG;AAEvE,QAAO,MAAM,KAAK,KAAK"}
@@ -31,6 +31,7 @@ export interface ClientHistoryMessage {
31
31
  fromId?: string;
32
32
  };
33
33
  toolCalls?: Array<{
34
+ id?: string;
34
35
  name: string;
35
36
  args?: unknown;
36
37
  result?: string;
@@ -44,6 +44,7 @@ function toolCallsWithResults(tool_calls, results) {
44
44
  for (const tc of tool_calls) {
45
45
  const res = results.get(tc.id);
46
46
  out.push({
47
+ id: tc.id,
47
48
  name: tc.function.name,
48
49
  args: safeJsonParseArguments(tc.function.arguments),
49
50
  result: res?.text,
@@ -138,9 +139,35 @@ function contextRowToClientHistory(row) {
138
139
  timestamp: typeof createdAt === "string" ? parseTimestamp(createdAt) : void 0
139
140
  };
140
141
  }
142
+ function asRecord(value) {
143
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
144
+ }
145
+ function reviewTraceRowToClientHistory(row) {
146
+ if (row.kind !== "context") return null;
147
+ const data = asRecord(row.data);
148
+ if (data?.type !== "review_trace" || data.scope !== "review") return null;
149
+ const event = data.event === "tool_end" ? "tool_end" : data.event === "tool_start" ? "tool_start" : null;
150
+ if (!event) return null;
151
+ const toolCallId = optionalString(data.toolCallId);
152
+ const toolName = optionalString(data.toolName);
153
+ if (!toolCallId || !toolName) return null;
154
+ const createdAt = row.createdAt;
155
+ return {
156
+ event,
157
+ toolCallId,
158
+ toolName,
159
+ input: data.input,
160
+ resultPreview: optionalString(data.resultPreview),
161
+ isError: data.isError === true || data.status === "error",
162
+ timestamp: typeof createdAt === "string" ? parseTimestamp(createdAt) : void 0
163
+ };
164
+ }
141
165
  function optionalString(value) {
142
166
  return typeof value === "string" ? value : void 0;
143
167
  }
168
+ function rawAssistantContent(content) {
169
+ return Array.isArray(content) ? content : void 0;
170
+ }
144
171
  function bashRowToClientHistory(row) {
145
172
  const r = row;
146
173
  if (r.role !== "bashExecution") return null;
@@ -232,6 +259,7 @@ function transcriptRowsToClientHistory(rows, opts) {
232
259
  const slice = hasRowWindow ? rows.slice(startIndex, endIndex) : opts?.limit !== void 0 && rows.length > opts.limit ? rows.slice(-opts.limit) : rows;
233
260
  const results = collectHistoryToolResults(slice.map(asHistoryMessageRow).filter((row) => row !== null));
234
261
  const out = [];
262
+ const reviewTraceToolById = /* @__PURE__ */ new Map();
235
263
  for (const [offset, row] of slice.entries()) {
236
264
  const rowNumber = startIndex + offset + 1;
237
265
  const id = `row-${rowNumber}`;
@@ -258,6 +286,32 @@ function transcriptRowsToClientHistory(rows, opts) {
258
286
  });
259
287
  continue;
260
288
  }
289
+ const reviewTraceRow = reviewTraceRowToClientHistory(row);
290
+ if (reviewTraceRow) {
291
+ const existing = reviewTraceToolById.get(reviewTraceRow.toolCallId);
292
+ if (reviewTraceRow.event === "tool_end" && existing) {
293
+ existing.result = reviewTraceRow.resultPreview ?? "";
294
+ existing.isError = reviewTraceRow.isError;
295
+ continue;
296
+ }
297
+ const toolCall = {
298
+ id: reviewTraceRow.toolCallId,
299
+ name: reviewTraceRow.toolName,
300
+ args: reviewTraceRow.input,
301
+ ...reviewTraceRow.event === "tool_end" ? { result: reviewTraceRow.resultPreview ?? "" } : {},
302
+ ...reviewTraceRow.isError ? { isError: true } : {}
303
+ };
304
+ reviewTraceToolById.set(reviewTraceRow.toolCallId, toolCall);
305
+ out.push({
306
+ id,
307
+ role: "assistant",
308
+ content: "",
309
+ timestamp: reviewTraceRow.timestamp,
310
+ toolCalls: [toolCall],
311
+ ...displayIndex !== void 0 ? { displayIndex } : {}
312
+ });
313
+ continue;
314
+ }
261
315
  const contextRow = contextRowToClientHistory(row);
262
316
  if (contextRow) {
263
317
  out.push({
@@ -317,11 +371,13 @@ function transcriptRowsToClientHistory(rows, opts) {
317
371
  });
318
372
  continue;
319
373
  }
374
+ const rawContent = rawAssistantContent(messageRow.content);
320
375
  out.push({
321
376
  id,
322
377
  role: "assistant",
323
378
  kind: "message",
324
379
  content: flattenMessageContent(messageRow.content ?? ""),
380
+ ...rawContent ? { rawContent } : {},
325
381
  ...displayIndex !== void 0 ? { displayIndex } : {},
326
382
  timestamp: parseTimestampValue(messageRow.timestamp),
327
383
  toolCalls: toolCallsWithResults(messageRow.tool_calls ?? messageRow.toolCalls, results)
@@ -1 +1 @@
1
- {"version":3,"file":"client-history.js","names":[],"sources":["../../../src/session/client-history.ts"],"sourcesContent":["import type { Message } from './types.js';\nimport type { TranscriptStoredRow } from './session-context-for-llm.js';\nimport { buildTranscriptOutline } from './transcript-outline.js';\n\n/** Transcript row for TUI and HTTP clients (flattened from persisted session messages). */\nexport interface ClientHistoryMessage {\n id?: string;\n role: 'user' | 'assistant' | 'system';\n content: string;\n displayIndex?: number;\n rawContent?: string | unknown[];\n timestamp?: number;\n kind?: 'message' | 'compaction' | 'context' | 'bash' | 'custom' | 'branch';\n tokensBefore?: number;\n tokensAfter?: number;\n bash?: {\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 custom?: {\n customType: string;\n details?: unknown;\n state?: boolean;\n display?: boolean;\n };\n branch?: {\n summary: string;\n fromId?: string;\n };\n toolCalls?: Array<{ name: string; args?: unknown; result?: string; isError?: boolean }>;\n}\n\nexport function flattenMessageContent(content: string | unknown[]): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block !== 'object' || block === null) continue;\n const b = block as Record<string, unknown>;\n if (b.type === 'text' && typeof b.text === 'string') {\n parts.push(b.text);\n }\n }\n return parts.join('');\n}\n\nfunction parseTimestamp(ts: string | undefined): number | undefined {\n if (!ts) return undefined;\n const ms = Date.parse(ts);\n return Number.isFinite(ms) ? ms : undefined;\n}\n\nfunction safeJsonParseArguments(raw: string): unknown {\n const t = raw.trim();\n if (!t) return undefined;\n try {\n return JSON.parse(t) as unknown;\n } catch {\n return raw;\n }\n}\n\nfunction collectToolResults(messages: Message[]): Map<string, { text: string; isError?: boolean }> {\n const map = new Map<string, { text: string; isError?: boolean }>();\n for (const m of messages) {\n if (m.role !== 'tool' && m.role !== 'toolResult') continue;\n const id = m.tool_call_id;\n if (!id || typeof id !== 'string') continue;\n map.set(id, {\n text: flattenMessageContent(m.content),\n isError: m.isError,\n });\n }\n return map;\n}\n\nfunction toolCallsWithResults(\n tool_calls: Message['tool_calls'],\n results: Map<string, { text: string; isError?: boolean }>,\n): ClientHistoryMessage['toolCalls'] | undefined {\n if (!tool_calls?.length) return undefined;\n const out: NonNullable<ClientHistoryMessage['toolCalls']> = [];\n for (const tc of tool_calls) {\n const res = results.get(tc.id);\n out.push({\n name: tc.function.name,\n args: safeJsonParseArguments(tc.function.arguments),\n result: res?.text,\n isError: res?.isError,\n });\n }\n return out.length ? out : undefined;\n}\n\n/**\n * Maps gateway/session API messages into a linear chat history for clients.\n * Tool / toolResult rows are merged into assistant `toolCalls` when ids match; otherwise skipped.\n */\nexport function messagesToClientHistory(\n messages: Message[],\n opts?: { limit?: number },\n): ClientHistoryMessage[] {\n const slice =\n opts?.limit !== undefined && messages.length > opts.limit\n ? messages.slice(-opts.limit)\n : messages;\n\n const results = collectToolResults(slice);\n const out: ClientHistoryMessage[] = [];\n\n for (const m of slice) {\n if (m.role === 'tool' || m.role === 'toolResult') {\n continue;\n }\n\n if (m.role === 'user' || m.role === 'system') {\n const text = flattenMessageContent(m.content);\n out.push({\n role: m.role,\n content: text,\n timestamp: parseTimestamp(m.timestamp),\n });\n continue;\n }\n\n if (m.role === 'assistant') {\n const text = flattenMessageContent(m.content);\n const toolCalls = toolCallsWithResults(m.tool_calls, results);\n out.push({\n role: 'assistant',\n content: text,\n timestamp: parseTimestamp(m.timestamp),\n toolCalls,\n });\n }\n }\n\n return out;\n}\n\ntype HistoryMessageRow = {\n role: 'system' | 'user' | 'assistant' | 'tool' | 'toolResult';\n content?: string | unknown[];\n timestamp?: string | number;\n tool_call_id?: string;\n toolCallId?: string;\n tool_calls?: Message['tool_calls'];\n toolCalls?: Message['tool_calls'];\n isError?: boolean;\n};\n\nfunction asHistoryMessageRow(row: TranscriptStoredRow): HistoryMessageRow | null {\n const role = (row as { role?: unknown }).role;\n if (\n typeof role !== 'string' ||\n !['system', 'user', 'assistant', 'tool', 'toolResult'].includes(role)\n ) {\n return null;\n }\n return row as unknown as HistoryMessageRow;\n}\n\nfunction parseTimestampValue(ts: string | number | undefined): number | undefined {\n if (typeof ts === 'number') return Number.isFinite(ts) ? ts : undefined;\n return parseTimestamp(ts);\n}\n\nfunction collectHistoryToolResults(messages: HistoryMessageRow[]): Map<string, { text: string; isError?: boolean }> {\n const map = new Map<string, { text: string; isError?: boolean }>();\n for (const m of messages) {\n if (m.role !== 'tool' && m.role !== 'toolResult') continue;\n const id = m.tool_call_id ?? m.toolCallId;\n if (!id || typeof id !== 'string') continue;\n map.set(id, {\n text: flattenMessageContent(m.content ?? ''),\n isError: m.isError,\n });\n }\n return map;\n}\n\nfunction isCompactionRow(row: TranscriptStoredRow): row is TranscriptStoredRow & {\n type: 'compaction';\n summary?: string;\n at?: string;\n tokensBefore?: number;\n tokensAfter?: number;\n} {\n return (row as { type?: unknown }).type === 'compaction';\n}\n\nfunction compactionSummaryRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'compactionSummary') return null;\n const summary = optionalString(r.summary) ?? '';\n return {\n role: 'system',\n kind: 'compaction',\n content: summary,\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n tokensBefore: typeof r.tokensBefore === 'number' ? r.tokensBefore : undefined,\n };\n}\n\nfunction contextRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n if ((row as { kind?: unknown }).kind !== 'context') return null;\n const text = (row as { text?: unknown }).text;\n if (typeof text !== 'string' || !text.trim()) return null;\n const createdAt = (row as { createdAt?: unknown }).createdAt;\n return {\n role: 'system',\n kind: 'context',\n content: text,\n timestamp: typeof createdAt === 'string' ? parseTimestamp(createdAt) : undefined,\n };\n}\n\nfunction optionalString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction bashRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'bashExecution') return null;\n\n const command = optionalString(r.command) ?? optionalString(r.content)?.trim();\n if (!command) return null;\n const output = Array.isArray(r.output) || typeof r.output === 'string'\n ? flattenMessageContent(r.output)\n : optionalString(r.output);\n const exitCode = typeof r.exitCode === 'number' ? r.exitCode : r.exitCode === null ? null : undefined;\n const signal = typeof r.signal === 'string' ? r.signal : r.signal === null ? null : undefined;\n const excludeFromContext =\n typeof r.excludeFromContext === 'boolean'\n ? r.excludeFromContext\n : typeof r.excludedFromContext === 'boolean'\n ? r.excludedFromContext\n : undefined;\n\n return {\n role: 'system',\n kind: 'bash',\n content: output || command,\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n bash: {\n command,\n output,\n exitCode,\n signal,\n excludeFromContext,\n truncated: typeof r.truncated === 'boolean' ? r.truncated : undefined,\n fullOutputPath: optionalString(r.fullOutputPath),\n },\n };\n}\n\nfunction customRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'custom' && r.type !== 'custom_message') return null;\n\n const customType = optionalString(r.customType)?.trim();\n if (!customType) return null;\n const content = Array.isArray(r.content) || typeof r.content === 'string'\n ? flattenMessageContent(r.content)\n : '';\n const timestamp = parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n );\n\n return {\n role: 'system',\n kind: 'custom',\n content,\n rawContent: Array.isArray(r.content) || typeof r.content === 'string' ? r.content : undefined,\n timestamp,\n custom: {\n customType,\n details: r.details,\n display: r.display === false ? false : true,\n },\n };\n}\n\nfunction customStateRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.type !== 'custom') return null;\n\n const customType = optionalString(r.customType)?.trim();\n if (!customType) return null;\n return {\n role: 'system',\n kind: 'custom',\n content: '',\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n custom: {\n customType,\n details: r.data,\n state: true,\n },\n };\n}\n\nfunction branchSummaryRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'branchSummary') return null;\n const summary = optionalString(r.summary)?.trim();\n if (!summary) return null;\n const fromId = optionalString(r.fromId);\n\n return {\n role: 'system',\n kind: 'branch',\n content: summary,\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n branch: {\n summary,\n fromId,\n },\n };\n}\n\n/**\n * Maps persisted transcript rows into linear chat history, preserving non-LLM\n * audit rows that the TUI can render as dedicated components.\n */\nexport function transcriptRowsToClientHistory(\n rows: TranscriptStoredRow[],\n opts?: { limit?: number; startRowNumber?: number; endRowNumber?: number },\n): ClientHistoryMessage[] {\n const displayIndexByRowNumber = new Map(\n buildTranscriptOutline(rows)\n .filter((entry) => entry.displayIndex !== undefined)\n .map((entry) => [entry.rowNumber, entry.displayIndex!] as const),\n );\n const hasRowWindow = opts?.startRowNumber !== undefined || opts?.endRowNumber !== undefined;\n const startIndex = hasRowWindow\n ? Math.max(0, Math.trunc(opts?.startRowNumber ?? 1) - 1)\n : opts?.limit !== undefined && rows.length > opts.limit\n ? rows.length - opts.limit\n : 0;\n const endIndex = hasRowWindow\n ? Math.max(\n startIndex,\n Math.min(rows.length, Math.trunc(opts?.endRowNumber ?? rows.length)),\n )\n : rows.length;\n const slice = hasRowWindow\n ? rows.slice(startIndex, endIndex)\n : opts?.limit !== undefined && rows.length > opts.limit\n ? rows.slice(-opts.limit)\n : rows;\n const messages = slice\n .map(asHistoryMessageRow)\n .filter((row): row is HistoryMessageRow => row !== null);\n const results = collectHistoryToolResults(messages);\n const out: ClientHistoryMessage[] = [];\n\n for (const [offset, row] of slice.entries()) {\n const rowNumber = startIndex + offset + 1;\n const id = `row-${rowNumber}`;\n const displayIndex = displayIndexByRowNumber.get(rowNumber);\n if (isCompactionRow(row)) {\n out.push({\n id,\n role: 'system',\n kind: 'compaction',\n content: typeof row.summary === 'string' ? row.summary : '',\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n timestamp: parseTimestamp(row.at),\n tokensBefore: row.tokensBefore,\n tokensAfter: row.tokensAfter,\n });\n continue;\n }\n\n const compactionSummaryRow = compactionSummaryRowToClientHistory(row);\n if (compactionSummaryRow) {\n out.push({\n id,\n ...compactionSummaryRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const contextRow = contextRowToClientHistory(row);\n if (contextRow) {\n out.push({\n id,\n ...contextRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const bashRow = bashRowToClientHistory(row);\n if (bashRow) {\n out.push({\n id,\n ...bashRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const customRow = customRowToClientHistory(row);\n if (customRow) {\n out.push({\n id,\n ...customRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const customStateRow = customStateRowToClientHistory(row);\n if (customStateRow) {\n out.push({\n id,\n ...customStateRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const branchSummaryRow = branchSummaryRowToClientHistory(row);\n if (branchSummaryRow) {\n out.push({\n id,\n ...branchSummaryRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const messageRow = asHistoryMessageRow(row);\n if (!messageRow) continue;\n if (messageRow.role === 'tool' || messageRow.role === 'toolResult') {\n continue;\n }\n\n if (messageRow.role === 'user' || messageRow.role === 'system') {\n out.push({\n id,\n role: messageRow.role,\n kind: 'message',\n content: flattenMessageContent(messageRow.content ?? ''),\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n timestamp: parseTimestampValue(messageRow.timestamp),\n });\n continue;\n }\n\n out.push({\n id,\n role: 'assistant',\n kind: 'message',\n content: flattenMessageContent(messageRow.content ?? ''),\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n timestamp: parseTimestampValue(messageRow.timestamp),\n toolCalls: toolCallsWithResults(messageRow.tool_calls ?? messageRow.toolCalls, results),\n });\n }\n\n return out;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client-history.js","names":[],"sources":["../../../src/session/client-history.ts"],"sourcesContent":["import type { Message } from './types.js';\nimport type { TranscriptStoredRow } from './session-context-for-llm.js';\nimport { buildTranscriptOutline } from './transcript-outline.js';\n\n/** Transcript row for TUI and HTTP clients (flattened from persisted session messages). */\nexport interface ClientHistoryMessage {\n id?: string;\n role: 'user' | 'assistant' | 'system';\n content: string;\n displayIndex?: number;\n rawContent?: string | unknown[];\n timestamp?: number;\n kind?: 'message' | 'compaction' | 'context' | 'bash' | 'custom' | 'branch';\n tokensBefore?: number;\n tokensAfter?: number;\n bash?: {\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 custom?: {\n customType: string;\n details?: unknown;\n state?: boolean;\n display?: boolean;\n };\n branch?: {\n summary: string;\n fromId?: string;\n };\n toolCalls?: Array<{ id?: string; name: string; args?: unknown; result?: string; isError?: boolean }>;\n}\n\nexport function flattenMessageContent(content: string | unknown[]): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block !== 'object' || block === null) continue;\n const b = block as Record<string, unknown>;\n if (b.type === 'text' && typeof b.text === 'string') {\n parts.push(b.text);\n }\n }\n return parts.join('');\n}\n\nfunction parseTimestamp(ts: string | undefined): number | undefined {\n if (!ts) return undefined;\n const ms = Date.parse(ts);\n return Number.isFinite(ms) ? ms : undefined;\n}\n\nfunction safeJsonParseArguments(raw: string): unknown {\n const t = raw.trim();\n if (!t) return undefined;\n try {\n return JSON.parse(t) as unknown;\n } catch {\n return raw;\n }\n}\n\nfunction collectToolResults(messages: Message[]): Map<string, { text: string; isError?: boolean }> {\n const map = new Map<string, { text: string; isError?: boolean }>();\n for (const m of messages) {\n if (m.role !== 'tool' && m.role !== 'toolResult') continue;\n const id = m.tool_call_id;\n if (!id || typeof id !== 'string') continue;\n map.set(id, {\n text: flattenMessageContent(m.content),\n isError: m.isError,\n });\n }\n return map;\n}\n\nfunction toolCallsWithResults(\n tool_calls: Message['tool_calls'],\n results: Map<string, { text: string; isError?: boolean }>,\n): ClientHistoryMessage['toolCalls'] | undefined {\n if (!tool_calls?.length) return undefined;\n const out: NonNullable<ClientHistoryMessage['toolCalls']> = [];\n for (const tc of tool_calls) {\n const res = results.get(tc.id);\n out.push({\n id: tc.id,\n name: tc.function.name,\n args: safeJsonParseArguments(tc.function.arguments),\n result: res?.text,\n isError: res?.isError,\n });\n }\n return out.length ? out : undefined;\n}\n\n/**\n * Maps gateway/session API messages into a linear chat history for clients.\n * Tool / toolResult rows are merged into assistant `toolCalls` when ids match; otherwise skipped.\n */\nexport function messagesToClientHistory(\n messages: Message[],\n opts?: { limit?: number },\n): ClientHistoryMessage[] {\n const slice =\n opts?.limit !== undefined && messages.length > opts.limit\n ? messages.slice(-opts.limit)\n : messages;\n\n const results = collectToolResults(slice);\n const out: ClientHistoryMessage[] = [];\n\n for (const m of slice) {\n if (m.role === 'tool' || m.role === 'toolResult') {\n continue;\n }\n\n if (m.role === 'user' || m.role === 'system') {\n const text = flattenMessageContent(m.content);\n out.push({\n role: m.role,\n content: text,\n timestamp: parseTimestamp(m.timestamp),\n });\n continue;\n }\n\n if (m.role === 'assistant') {\n const text = flattenMessageContent(m.content);\n const toolCalls = toolCallsWithResults(m.tool_calls, results);\n out.push({\n role: 'assistant',\n content: text,\n timestamp: parseTimestamp(m.timestamp),\n toolCalls,\n });\n }\n }\n\n return out;\n}\n\ntype HistoryMessageRow = {\n role: 'system' | 'user' | 'assistant' | 'tool' | 'toolResult';\n content?: string | unknown[];\n timestamp?: string | number;\n tool_call_id?: string;\n toolCallId?: string;\n tool_calls?: Message['tool_calls'];\n toolCalls?: Message['tool_calls'];\n isError?: boolean;\n};\n\nfunction asHistoryMessageRow(row: TranscriptStoredRow): HistoryMessageRow | null {\n const role = (row as { role?: unknown }).role;\n if (\n typeof role !== 'string' ||\n !['system', 'user', 'assistant', 'tool', 'toolResult'].includes(role)\n ) {\n return null;\n }\n return row as unknown as HistoryMessageRow;\n}\n\nfunction parseTimestampValue(ts: string | number | undefined): number | undefined {\n if (typeof ts === 'number') return Number.isFinite(ts) ? ts : undefined;\n return parseTimestamp(ts);\n}\n\nfunction collectHistoryToolResults(messages: HistoryMessageRow[]): Map<string, { text: string; isError?: boolean }> {\n const map = new Map<string, { text: string; isError?: boolean }>();\n for (const m of messages) {\n if (m.role !== 'tool' && m.role !== 'toolResult') continue;\n const id = m.tool_call_id ?? m.toolCallId;\n if (!id || typeof id !== 'string') continue;\n map.set(id, {\n text: flattenMessageContent(m.content ?? ''),\n isError: m.isError,\n });\n }\n return map;\n}\n\nfunction isCompactionRow(row: TranscriptStoredRow): row is TranscriptStoredRow & {\n type: 'compaction';\n summary?: string;\n at?: string;\n tokensBefore?: number;\n tokensAfter?: number;\n} {\n return (row as { type?: unknown }).type === 'compaction';\n}\n\nfunction compactionSummaryRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'compactionSummary') return null;\n const summary = optionalString(r.summary) ?? '';\n return {\n role: 'system',\n kind: 'compaction',\n content: summary,\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n tokensBefore: typeof r.tokensBefore === 'number' ? r.tokensBefore : undefined,\n };\n}\n\nfunction contextRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n if ((row as { kind?: unknown }).kind !== 'context') return null;\n const text = (row as { text?: unknown }).text;\n if (typeof text !== 'string' || !text.trim()) return null;\n const createdAt = (row as { createdAt?: unknown }).createdAt;\n return {\n role: 'system',\n kind: 'context',\n content: text,\n timestamp: typeof createdAt === 'string' ? parseTimestamp(createdAt) : undefined,\n };\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? value as Record<string, unknown>\n : null;\n}\n\nfunction reviewTraceRowToClientHistory(row: TranscriptStoredRow): {\n event: 'tool_start' | 'tool_end';\n toolCallId: string;\n toolName: string;\n input?: unknown;\n resultPreview?: string;\n isError?: boolean;\n timestamp?: number;\n} | null {\n if ((row as { kind?: unknown }).kind !== 'context') return null;\n const data = asRecord((row as { data?: unknown }).data);\n if (data?.type !== 'review_trace' || data.scope !== 'review') return null;\n const event = data.event === 'tool_end'\n ? 'tool_end'\n : data.event === 'tool_start'\n ? 'tool_start'\n : null;\n if (!event) return null;\n const toolCallId = optionalString(data.toolCallId);\n const toolName = optionalString(data.toolName);\n if (!toolCallId || !toolName) return null;\n const createdAt = (row as { createdAt?: unknown }).createdAt;\n return {\n event,\n toolCallId,\n toolName,\n input: data.input,\n resultPreview: optionalString(data.resultPreview),\n isError: data.isError === true || data.status === 'error',\n timestamp: typeof createdAt === 'string' ? parseTimestamp(createdAt) : undefined,\n };\n}\n\nfunction optionalString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction rawAssistantContent(content: unknown): unknown[] | undefined {\n return Array.isArray(content) ? content : undefined;\n}\n\nfunction bashRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'bashExecution') return null;\n\n const command = optionalString(r.command) ?? optionalString(r.content)?.trim();\n if (!command) return null;\n const output = Array.isArray(r.output) || typeof r.output === 'string'\n ? flattenMessageContent(r.output)\n : optionalString(r.output);\n const exitCode = typeof r.exitCode === 'number' ? r.exitCode : r.exitCode === null ? null : undefined;\n const signal = typeof r.signal === 'string' ? r.signal : r.signal === null ? null : undefined;\n const excludeFromContext =\n typeof r.excludeFromContext === 'boolean'\n ? r.excludeFromContext\n : typeof r.excludedFromContext === 'boolean'\n ? r.excludedFromContext\n : undefined;\n\n return {\n role: 'system',\n kind: 'bash',\n content: output || command,\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n bash: {\n command,\n output,\n exitCode,\n signal,\n excludeFromContext,\n truncated: typeof r.truncated === 'boolean' ? r.truncated : undefined,\n fullOutputPath: optionalString(r.fullOutputPath),\n },\n };\n}\n\nfunction customRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'custom' && r.type !== 'custom_message') return null;\n\n const customType = optionalString(r.customType)?.trim();\n if (!customType) return null;\n const content = Array.isArray(r.content) || typeof r.content === 'string'\n ? flattenMessageContent(r.content)\n : '';\n const timestamp = parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n );\n\n return {\n role: 'system',\n kind: 'custom',\n content,\n rawContent: Array.isArray(r.content) || typeof r.content === 'string' ? r.content : undefined,\n timestamp,\n custom: {\n customType,\n details: r.details,\n display: r.display === false ? false : true,\n },\n };\n}\n\nfunction customStateRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.type !== 'custom') return null;\n\n const customType = optionalString(r.customType)?.trim();\n if (!customType) return null;\n return {\n role: 'system',\n kind: 'custom',\n content: '',\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n custom: {\n customType,\n details: r.data,\n state: true,\n },\n };\n}\n\nfunction branchSummaryRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'branchSummary') return null;\n const summary = optionalString(r.summary)?.trim();\n if (!summary) return null;\n const fromId = optionalString(r.fromId);\n\n return {\n role: 'system',\n kind: 'branch',\n content: summary,\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n branch: {\n summary,\n fromId,\n },\n };\n}\n\n/**\n * Maps persisted transcript rows into linear chat history, preserving non-LLM\n * audit rows that the TUI can render as dedicated components.\n */\nexport function transcriptRowsToClientHistory(\n rows: TranscriptStoredRow[],\n opts?: { limit?: number; startRowNumber?: number; endRowNumber?: number },\n): ClientHistoryMessage[] {\n const displayIndexByRowNumber = new Map(\n buildTranscriptOutline(rows)\n .filter((entry) => entry.displayIndex !== undefined)\n .map((entry) => [entry.rowNumber, entry.displayIndex!] as const),\n );\n const hasRowWindow = opts?.startRowNumber !== undefined || opts?.endRowNumber !== undefined;\n const startIndex = hasRowWindow\n ? Math.max(0, Math.trunc(opts?.startRowNumber ?? 1) - 1)\n : opts?.limit !== undefined && rows.length > opts.limit\n ? rows.length - opts.limit\n : 0;\n const endIndex = hasRowWindow\n ? Math.max(\n startIndex,\n Math.min(rows.length, Math.trunc(opts?.endRowNumber ?? rows.length)),\n )\n : rows.length;\n const slice = hasRowWindow\n ? rows.slice(startIndex, endIndex)\n : opts?.limit !== undefined && rows.length > opts.limit\n ? rows.slice(-opts.limit)\n : rows;\n const messages = slice\n .map(asHistoryMessageRow)\n .filter((row): row is HistoryMessageRow => row !== null);\n const results = collectHistoryToolResults(messages);\n const out: ClientHistoryMessage[] = [];\n const reviewTraceToolById = new Map<string, NonNullable<ClientHistoryMessage['toolCalls']>[number]>();\n\n for (const [offset, row] of slice.entries()) {\n const rowNumber = startIndex + offset + 1;\n const id = `row-${rowNumber}`;\n const displayIndex = displayIndexByRowNumber.get(rowNumber);\n if (isCompactionRow(row)) {\n out.push({\n id,\n role: 'system',\n kind: 'compaction',\n content: typeof row.summary === 'string' ? row.summary : '',\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n timestamp: parseTimestamp(row.at),\n tokensBefore: row.tokensBefore,\n tokensAfter: row.tokensAfter,\n });\n continue;\n }\n\n const compactionSummaryRow = compactionSummaryRowToClientHistory(row);\n if (compactionSummaryRow) {\n out.push({\n id,\n ...compactionSummaryRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const reviewTraceRow = reviewTraceRowToClientHistory(row);\n if (reviewTraceRow) {\n const existing = reviewTraceToolById.get(reviewTraceRow.toolCallId);\n if (reviewTraceRow.event === 'tool_end' && existing) {\n existing.result = reviewTraceRow.resultPreview ?? '';\n existing.isError = reviewTraceRow.isError;\n continue;\n }\n const toolCall: NonNullable<ClientHistoryMessage['toolCalls']>[number] = {\n id: reviewTraceRow.toolCallId,\n name: reviewTraceRow.toolName,\n args: reviewTraceRow.input,\n ...(reviewTraceRow.event === 'tool_end' ? { result: reviewTraceRow.resultPreview ?? '' } : {}),\n ...(reviewTraceRow.isError ? { isError: true } : {}),\n };\n reviewTraceToolById.set(reviewTraceRow.toolCallId, toolCall);\n out.push({\n id,\n role: 'assistant',\n content: '',\n timestamp: reviewTraceRow.timestamp,\n toolCalls: [toolCall],\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const contextRow = contextRowToClientHistory(row);\n if (contextRow) {\n out.push({\n id,\n ...contextRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const bashRow = bashRowToClientHistory(row);\n if (bashRow) {\n out.push({\n id,\n ...bashRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const customRow = customRowToClientHistory(row);\n if (customRow) {\n out.push({\n id,\n ...customRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const customStateRow = customStateRowToClientHistory(row);\n if (customStateRow) {\n out.push({\n id,\n ...customStateRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const branchSummaryRow = branchSummaryRowToClientHistory(row);\n if (branchSummaryRow) {\n out.push({\n id,\n ...branchSummaryRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const messageRow = asHistoryMessageRow(row);\n if (!messageRow) continue;\n if (messageRow.role === 'tool' || messageRow.role === 'toolResult') {\n continue;\n }\n\n if (messageRow.role === 'user' || messageRow.role === 'system') {\n out.push({\n id,\n role: messageRow.role,\n kind: 'message',\n content: flattenMessageContent(messageRow.content ?? ''),\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n timestamp: parseTimestampValue(messageRow.timestamp),\n });\n continue;\n }\n\n const rawContent = rawAssistantContent(messageRow.content);\n out.push({\n id,\n role: 'assistant',\n kind: 'message',\n content: flattenMessageContent(messageRow.content ?? ''),\n ...(rawContent ? { rawContent } : {}),\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n timestamp: parseTimestampValue(messageRow.timestamp),\n toolCalls: toolCallsWithResults(messageRow.tool_calls ?? messageRow.toolCalls, results),\n });\n }\n\n return out;\n}\n"],"mappings":"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"}
@@ -4,6 +4,7 @@ import { effectiveWorkspacePathForSession, normalizeWorkingDirectoryInput, proje
4
4
  import { emitSessionTranscriptUpdate, onSessionTranscriptUpdate } from "./transcript-events.js";
5
5
  import { fallbackTitleFromMessages, generateSessionTitleFromMessages, getSessionTitleSource, isWebchatSessionKey, maybeRefineSessionTitleWithLlm, maybeSetProvisionalSessionTitle, provisionalTitleFromUserText, sanitizeSessionTitle, shouldAutoTitleSessionKey, shouldRefineSessionTitleWithLlm } from "./session-title.js";
6
6
  import { normalizeCompactionCheckpointId } from "./compaction-checkpoints.js";
7
+ import { flattenMessageContent, messagesToClientHistory } from "./client-history.js";
7
8
  import { SessionStore } from "./store.js";
8
9
  import { applySessionPatchToMetadata } from "./patch-metadata.js";
9
10
  import { SessionIndex } from "./manager.js";
@@ -16,6 +17,5 @@ import { resolveSessionLifecycleTimestamps } from "./lifecycle-timestamps.js";
16
17
  import { resolveSession, resolveSessionKeyForRequest } from "./resolve-session.js";
17
18
  import { DEFAULT_RESET_TRIGGERS, RESET_OVERLAP_COMMANDS, bareResetAckMessage, matchResetTriggers, resolveResetTriggers, shouldSkipResetOverlapCommand, stripLeadingEnvelopeTimestamp } from "./reset-triggers.js";
18
19
  import { initSessionTurn } from "./init-session-turn.js";
19
- import { flattenMessageContent, messagesToClientHistory } from "./client-history.js";
20
20
  import { CURRENT_SESSION_TRANSCRIPT_VERSION, XOPC_SESSION_TRANSCRIPT_TYPE } from "./transcript-format.js";
21
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, projectWorkspacePath, provisionalTitleFromUserText, resolveChannelResetConfig, resolveDailyResetAtMs, resolveEffectiveReasoningLevel, resolveEffectiveThinkingLevel, resolveReasoningLevel, resolveResetTriggers, resolveSession, resolveSessionKeyForRequest, resolveSessionLifecycleTimestamps, resolveSessionResetPolicy, resolveSessionResetType, resolveThinkingLevel, resolveVerboseLevel, sanitizeSessionTitle, shouldAutoTitleSessionKey, shouldRefineSessionTitleWithLlm, shouldSkipResetOverlapCommand, shouldSkipWebchatInboundByAbortCutoff, stripLeadingEnvelopeTimestamp, transcriptRowsFromJsonArray };
@@ -32,6 +32,7 @@ export declare class SessionIndex extends EventEmitter {
32
32
  before?: string;
33
33
  includeTranscriptSummary?: boolean;
34
34
  includeTranscriptRows?: boolean;
35
+ includeContextRows?: boolean;
35
36
  }): Promise<{
36
37
  session: SessionDetail;
37
38
  pagination: {
@@ -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 { 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
+ {"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 includeContextRows?: 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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"}
@@ -46,6 +46,7 @@ export declare class SessionStore {
46
46
  before?: string;
47
47
  includeTranscriptSummary?: boolean;
48
48
  includeTranscriptRows?: boolean;
49
+ includeContextRows?: boolean;
49
50
  }): Promise<{
50
51
  session: SessionDetail;
51
52
  pagination: {