@xopcai/xopc 0.0.158 → 0.0.160

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 (218) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/dashscope/src/image-generation-provider.js +1 -1
  3. package/dist/extensions/fal/src/image-generation-provider.js +1 -1
  4. package/dist/extensions/google/src/image-generation-provider.js +1 -1
  5. package/dist/extensions/telegram/xopc.extension.json +1 -1
  6. package/dist/gateway/static/root/assets/{action-boundary-settings-panel-2p1f4-KA.js → action-boundary-settings-panel-CY3sizqC.js} +1 -1
  7. package/dist/gateway/static/root/assets/{activity-store-C-57rvkX.js → activity-store-3Cso_2qm.js} +1 -1
  8. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BIl6X2b_.js → agent-avatar-dicebear-B_27aaZQ.js} +1 -1
  9. package/dist/gateway/static/root/assets/agents-B1l3iii6.js +2 -0
  10. package/dist/gateway/static/root/assets/{api-BjcBIV2O.js → api-BbzB57CJ.js} +1 -1
  11. package/dist/gateway/static/root/assets/{app-management-settings-panel-CZxw161D.js → app-management-settings-panel-DfmyWcyh.js} +1 -1
  12. package/dist/gateway/static/root/assets/{appearance-settings-CRXNlDhH.js → appearance-settings-B2v0c-K-.js} +1 -1
  13. package/dist/gateway/static/root/assets/apps-page-DaNFLsp0.js +1 -0
  14. package/dist/gateway/static/root/assets/{archive-plugin-CpVshjXu.js → archive-plugin-DL5Lnx0C.js} +1 -1
  15. package/dist/gateway/static/root/assets/{attachment-load-M9WqqJnQ.js → attachment-load-InuP-ZjQ.js} +1 -1
  16. package/dist/gateway/static/root/assets/{automation-suggestion-card-CYMsAsYf.js → automation-suggestion-card-CahGuh7B.js} +1 -1
  17. package/dist/gateway/static/root/assets/automations-page-Cd0gMx6p.js +6 -0
  18. package/dist/gateway/static/root/assets/{binary-plugins-Cjn3zb4u.js → binary-plugins-CdOTGYVu.js} +1 -1
  19. package/dist/gateway/static/root/assets/{block-editor-BPKdquaV.js → block-editor-D6ShK1S-.js} +3 -3
  20. package/dist/gateway/static/root/assets/browser-settings-page-BmUYDL2Z.js +8 -0
  21. package/dist/gateway/static/root/assets/{capability-presets-api-Bzkcsyzn.js → capability-presets-api-BzoLm52Z.js} +1 -1
  22. package/dist/gateway/static/root/assets/{capability-presets-settings-panel-D4rH4Dax.js → capability-presets-settings-panel-CE7mrWtS.js} +2 -2
  23. package/dist/gateway/static/root/assets/{channel-recipient-api-CFqrSi5K.js → channel-recipient-api-DbgydMjl.js} +1 -1
  24. package/dist/gateway/static/root/assets/channels-settings-D1_gcAHK.js +1 -0
  25. package/dist/gateway/static/root/assets/channels-status-swr-Cqfqv59m.js +1 -0
  26. package/dist/gateway/static/root/assets/{connector-logo-ljPKdYKh.js → connector-logo-C3bJC9nt.js} +1 -1
  27. package/dist/gateway/static/root/assets/connectors-page-CaLBi6Na.js +2 -0
  28. package/dist/gateway/static/root/assets/desktop-pet-settings-BrHVvbBw.js +1 -0
  29. package/dist/gateway/static/root/assets/desktop-pet-sprite-CFd1jGLJ.js +1 -0
  30. package/dist/gateway/static/root/assets/desktop-pet-wacrnJJ1.js +1 -0
  31. package/dist/gateway/static/root/assets/{directory-picker-path-field-Dmy1035j.js → directory-picker-path-field-BPI969w-.js} +1 -1
  32. package/dist/gateway/static/root/assets/{dreaming-settings-tT4WkBkd.js → dreaming-settings-iw6QXD45.js} +3 -3
  33. package/dist/gateway/static/root/assets/{extension-debug-page-CL79q8AH.js → extension-debug-page-rCHyhEr6.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-page-hq80wkFI.js → extension-page-C4gQsAPt.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-settings-page-BV-9GDfT.js → extension-settings-page-DB_3eik7.js} +1 -1
  36. package/dist/gateway/static/root/assets/{gateway-config-api-CFqoN2NW.js → gateway-config-api-Cdt38iF2.js} +1 -1
  37. package/dist/gateway/static/root/assets/gateway-settings-DM4BgyGw.js +1 -0
  38. package/dist/gateway/static/root/assets/{gateway-startup-retry-cxQ94qMm.js → gateway-startup-retry-ChRFNjj_.js} +1 -1
  39. package/dist/gateway/static/root/assets/goal-create-dialog-BQamcEmF.js +1 -0
  40. package/dist/gateway/static/root/assets/goal-detail-page-DZUQ0Lf1.js +4 -0
  41. package/dist/gateway/static/root/assets/{goals-config-api--qJ4qlRq.js → goals-config-api-RvljeEm6.js} +1 -1
  42. package/dist/gateway/static/root/assets/goals-page-BtxVD27C.js +2 -0
  43. package/dist/gateway/static/root/assets/{goals-settings-h7x9h5lb.js → goals-settings-DMGV4S4L.js} +1 -1
  44. package/dist/gateway/static/root/assets/{heartbeat-settings-71IhhM3s.js → heartbeat-settings-B8r5EXc5.js} +1 -1
  45. package/dist/gateway/static/root/assets/index-CHKe7nwC.js +197 -0
  46. package/dist/gateway/static/root/assets/index-CXhfUIjh.css +1 -0
  47. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BZGmHvLu.js → keyboard-shortcuts-settings-1-LYibQ4.js} +1 -1
  48. package/dist/gateway/static/root/assets/local-app-workbench-page-CTTfyFe3.js +2 -0
  49. package/dist/gateway/static/root/assets/{local-apps-page-CiLqrMIh.js → local-apps-page-CC4vtUUI.js} +1 -1
  50. package/dist/gateway/static/root/assets/{locale-store-C_M71MK9.js → locale-store-CPbGB_6l.js} +1 -1
  51. package/dist/gateway/static/root/assets/logs-page-Bn_taxBV.js +7 -0
  52. package/dist/gateway/static/root/assets/{markdown-split-r1HPWfCU.js → markdown-split-CprEIf8A.js} +1 -1
  53. package/dist/gateway/static/root/assets/{media-plugins-D74kd3hn.js → media-plugins-BXt_Sj2O.js} +1 -1
  54. package/dist/gateway/static/root/assets/{messages-BuT0SR1R.js → messages-DDsiqdJS.js} +4 -4
  55. package/dist/gateway/static/root/assets/models-hub-panel-DJReXRP9.js +2 -0
  56. package/dist/gateway/static/root/assets/{notes-page-Dgu02514.js → notes-page-Wf5-eyyM.js} +1 -1
  57. package/dist/gateway/static/root/assets/notes-workbench-BbdjzYzs.js +4 -0
  58. package/dist/gateway/static/root/assets/{page-tabs-hinRdDy3.js → page-tabs-dtpK_4Fs.js} +1 -1
  59. package/dist/gateway/static/root/assets/{preview-open-alternatives-C7fw3oGy.js → preview-open-alternatives-DijtEVxG.js} +1 -1
  60. package/dist/gateway/static/root/assets/project-detail-page-BwG8SOzI.js +1 -0
  61. package/dist/gateway/static/root/assets/projects-page-nUlyUtxh.js +1 -0
  62. package/dist/gateway/static/root/assets/remote-access-hub-rf7bjije.js +1 -0
  63. package/dist/gateway/static/root/assets/{save-bar-controls-C3aU8-vO.js → save-bar-controls-qhDisdqY.js} +1 -1
  64. package/dist/gateway/static/root/assets/{schedule-field-DjegExFD.js → schedule-field-AhJ50NoT.js} +1 -1
  65. package/dist/gateway/static/root/assets/{schema-form-DLubnsql.js → schema-form-AuJg2CXU.js} +1 -1
  66. package/dist/gateway/static/root/assets/sessions-page-CSoHUe38.js +1 -0
  67. package/dist/gateway/static/root/assets/settings-advanced-gate-Db976IEa.js +1 -0
  68. package/dist/gateway/static/root/assets/{settings-collapsible-section-3syQHsNR.js → settings-collapsible-section-D5ganr_X.js} +1 -1
  69. package/dist/gateway/static/root/assets/{settings-form-section-D4DCZzKg.js → settings-form-section-Db3cKRsg.js} +1 -1
  70. package/dist/gateway/static/root/assets/{settings-loading-skeleton-c5K85ylS.js → settings-loading-skeleton-D05YKT2x.js} +1 -1
  71. package/dist/gateway/static/root/assets/{settings-page-DNiDlaRq.js → settings-page-DYfXmfTx.js} +1 -1
  72. package/dist/gateway/static/root/assets/{settings-page-layout-iTRY1Hy-.js → settings-page-layout-Cv_Cu4I5.js} +1 -1
  73. package/dist/gateway/static/root/assets/setup-status-panel-wBWOFpvw.js +1 -0
  74. package/dist/gateway/static/root/assets/{share-preview-page-B88Afrhe.js → share-preview-page-CYkZVuse.js} +1 -1
  75. package/dist/gateway/static/root/assets/shares-settings-BAMma1-P.js +1 -0
  76. package/dist/gateway/static/root/assets/skill-reload-api-TWxFFWxM.js +1 -0
  77. package/dist/gateway/static/root/assets/skills-page-Ca2RA29R.js +2 -0
  78. package/dist/gateway/static/root/assets/{system-settings-panel-DtpTq837.js → system-settings-panel-C1Ng5bXh.js} +1 -1
  79. package/dist/gateway/static/root/assets/{theme-store-C8ohIHhw.js → theme-store-LMHIDR9n.js} +1 -1
  80. package/dist/gateway/static/root/assets/{typed-models-lib-ndEnUP3b.js → typed-models-lib-Ch2Bdm_5.js} +1 -1
  81. package/dist/gateway/static/root/assets/{use-channel-catalog-BWzou5ar.js → use-channel-catalog-CZnaMOeL.js} +1 -1
  82. package/dist/gateway/static/root/assets/user-context-page-CTumZa0y.js +1 -0
  83. package/dist/gateway/static/root/assets/voice-api-key-field-DdlMxeqO.js +1 -0
  84. package/dist/gateway/static/root/assets/work-discovery-page-CcfZhdBi.js +1 -0
  85. package/dist/gateway/static/root/assets/work-item-detail-page--wxLw1p_.js +1 -0
  86. package/dist/gateway/static/root/assets/work-page-DyS0XBpV.js +1 -0
  87. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-ukmpkj66.js → workflow-run-setup-panel-BFk8TvTw.js} +2 -2
  88. package/dist/gateway/static/root/assets/workflows-page-CVVJd4gy.js +11 -0
  89. package/dist/gateway/static/root/index.html +7 -7
  90. package/dist/package.js +1 -1
  91. package/dist/src/agent/embedded/session-tool-result-guard.js +14 -0
  92. package/dist/src/agent/image/generation/openai-compatible-image-provider.js +1 -1
  93. package/dist/src/agent/service/process-direct-streaming.js +1 -1
  94. package/dist/src/agent/tools/image-generate-tool.js +10 -3
  95. package/dist/src/agent/tools/send-media.js +20 -70
  96. package/dist/src/agent/tools/tool-media.d.ts +9 -0
  97. package/dist/src/agent/tools/tool-media.js +58 -0
  98. package/dist/src/automations/service/action-executor.js +1 -1
  99. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  100. package/dist/src/config/agent-profile.js +1 -1
  101. package/dist/src/gateway/agent-run-relay.d.ts +2 -0
  102. package/dist/src/gateway/agent-run-relay.js +114 -14
  103. package/dist/src/gateway/agents-admin.js +1 -1
  104. package/dist/src/gateway/chat-stream/mapper.d.ts +1 -0
  105. package/dist/src/gateway/chat-stream/mapper.js +40 -19
  106. package/dist/src/gateway/chat-stream/thinking-delta-coalescer.d.ts +7 -0
  107. package/dist/src/gateway/chat-stream/thinking-delta-coalescer.js +103 -0
  108. package/dist/src/gateway/hono/routes/home.js +15 -2
  109. package/dist/src/gateway/hono/routes/work-discovery.js +451 -0
  110. package/dist/src/gateway/hono/sse.d.ts +1 -1
  111. package/dist/src/gateway/hono/sse.js +1 -1
  112. package/dist/src/gateway/service/run-gateway-agent.js +5 -1
  113. package/dist/src/gateway/service.d.ts +2 -0
  114. package/dist/src/gateway/service.js +27 -2
  115. package/dist/src/media/session-references.js +9 -6
  116. package/dist/src/mobile/notification-service.js +1 -0
  117. package/dist/src/pets/factory.js +23 -26
  118. package/dist/src/pets/manifest.d.ts +2 -1
  119. package/dist/src/pets/manifest.js +15 -8
  120. package/dist/src/pets/validator.js +8 -3
  121. package/dist/src/proactive/calendar-signals.d.ts +4 -0
  122. package/dist/src/proactive/calendar-signals.js +120 -0
  123. package/dist/src/proactive/focus-service.d.ts +62 -0
  124. package/dist/src/proactive/focus-service.js +173 -0
  125. package/dist/src/proactive/index.d.ts +7 -0
  126. package/dist/src/proactive/index.js +7 -0
  127. package/dist/src/proactive/insight-repository.d.ts +19 -0
  128. package/dist/src/proactive/insight-repository.js +106 -0
  129. package/dist/src/proactive/run-processor.d.ts +19 -0
  130. package/dist/src/proactive/run-processor.js +125 -0
  131. package/dist/src/proactive/trial-expiry-reconciler.d.ts +5 -0
  132. package/dist/src/proactive/trial-expiry-reconciler.js +30 -0
  133. package/dist/src/proactive/types.d.ts +64 -0
  134. package/dist/src/proactive/types.js +1 -0
  135. package/dist/src/proactive/watch-repository.d.ts +28 -0
  136. package/dist/src/proactive/watch-repository.js +129 -0
  137. package/dist/src/session/client-history.d.ts +1 -0
  138. package/dist/src/session/client-history.js +56 -12
  139. package/dist/src/session/types.d.ts +1 -0
  140. package/dist/src/storage/sqlite/knowledge-repository.d.ts +1 -0
  141. package/dist/src/storage/sqlite/knowledge-repository.js +4 -0
  142. package/dist/src/storage/sqlite/migrations/047_work_discovery_sources.sql +16 -0
  143. package/dist/src/storage/sqlite/migrations/048_work_understanding_investigations.sql +40 -0
  144. package/dist/src/storage/sqlite/migrations/049_work_understanding_threads.sql +52 -0
  145. package/dist/src/storage/sqlite/migrations/050_work_discovery_source_refreshes.sql +15 -0
  146. package/dist/src/storage/sqlite/migrations/051_focus_watches.sql +26 -0
  147. package/dist/src/storage/sqlite/migrations/052_proactive_insights.sql +25 -0
  148. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  149. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  150. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  151. package/dist/src/storage/sqlite/schema.js +1 -1
  152. package/dist/src/work-discovery/analyzer.d.ts +12 -1
  153. package/dist/src/work-discovery/analyzer.js +126 -7
  154. package/dist/src/work-discovery/candidate-discovery.d.ts +17 -0
  155. package/dist/src/work-discovery/candidate-discovery.js +279 -0
  156. package/dist/src/work-discovery/evaluation.d.ts +11 -0
  157. package/dist/src/work-discovery/evaluation.js +17 -0
  158. package/dist/src/work-discovery/governance.d.ts +25 -0
  159. package/dist/src/work-discovery/governance.js +96 -0
  160. package/dist/src/work-discovery/incremental-repository.d.ts +13 -0
  161. package/dist/src/work-discovery/incremental-repository.js +55 -0
  162. package/dist/src/work-discovery/incremental.d.ts +2 -0
  163. package/dist/src/work-discovery/incremental.js +7 -0
  164. package/dist/src/work-discovery/index.d.ts +10 -0
  165. package/dist/src/work-discovery/index.js +13 -3
  166. package/dist/src/work-discovery/investigation-repository.d.ts +16 -0
  167. package/dist/src/work-discovery/investigation-repository.js +122 -0
  168. package/dist/src/work-discovery/investigator.d.ts +55 -0
  169. package/dist/src/work-discovery/investigator.js +358 -0
  170. package/dist/src/work-discovery/probe.d.ts +22 -0
  171. package/dist/src/work-discovery/probe.js +88 -1
  172. package/dist/src/work-discovery/service.d.ts +110 -1
  173. package/dist/src/work-discovery/service.js +578 -8
  174. package/dist/src/work-discovery/source-repository.d.ts +13 -0
  175. package/dist/src/work-discovery/source-repository.js +72 -0
  176. package/dist/src/work-discovery/thread-repository.d.ts +34 -0
  177. package/dist/src/work-discovery/thread-repository.js +112 -0
  178. package/dist/src/work-discovery/thread-service.d.ts +8 -0
  179. package/dist/src/work-discovery/thread-service.js +104 -0
  180. package/dist/src/work-discovery/types.d.ts +145 -0
  181. package/package.json +1 -1
  182. package/skills/creative/hatch-pet/SKILL.md +2 -2
  183. package/dist/gateway/static/root/assets/agents-bnkKH9EI.js +0 -2
  184. package/dist/gateway/static/root/assets/apps-page-l4ripRNG.js +0 -1
  185. package/dist/gateway/static/root/assets/automations-page-Kny7tLiF.js +0 -6
  186. package/dist/gateway/static/root/assets/browser-settings-page-vr1tXAab.js +0 -8
  187. package/dist/gateway/static/root/assets/channels-settings-Tlhbi5eh.js +0 -1
  188. package/dist/gateway/static/root/assets/channels-status-swr-vooEF4lG.js +0 -1
  189. package/dist/gateway/static/root/assets/connectors-page-_XfvjXoY.js +0 -2
  190. package/dist/gateway/static/root/assets/desktop-pet-DKUlAgm7.js +0 -1
  191. package/dist/gateway/static/root/assets/desktop-pet-settings-aWWzjdza.js +0 -1
  192. package/dist/gateway/static/root/assets/desktop-pet-sprite-DqZ6fbIj.js +0 -1
  193. package/dist/gateway/static/root/assets/gateway-settings-DzR-kM91.js +0 -1
  194. package/dist/gateway/static/root/assets/goal-create-dialog-BLjHRtqe.js +0 -1
  195. package/dist/gateway/static/root/assets/goal-detail-page-BRxeujXh.js +0 -4
  196. package/dist/gateway/static/root/assets/goals-page-BaZ3aw4u.js +0 -2
  197. package/dist/gateway/static/root/assets/index-EXa_i6BT.js +0 -193
  198. package/dist/gateway/static/root/assets/index-gpwk8Nro.css +0 -1
  199. package/dist/gateway/static/root/assets/local-app-workbench-page-lUpd6lSo.js +0 -2
  200. package/dist/gateway/static/root/assets/logs-page-FPDTmcWe.js +0 -7
  201. package/dist/gateway/static/root/assets/models-hub-panel-BXrdePhv.js +0 -2
  202. package/dist/gateway/static/root/assets/notes-workbench-D-EAhXv1.js +0 -4
  203. package/dist/gateway/static/root/assets/project-detail-page-BjpB1wrp.js +0 -1
  204. package/dist/gateway/static/root/assets/projects-page-BxyjqOfC.js +0 -1
  205. package/dist/gateway/static/root/assets/remote-access-hub-BFv6Upke.js +0 -1
  206. package/dist/gateway/static/root/assets/sessions-page-BX4DH8J7.js +0 -1
  207. package/dist/gateway/static/root/assets/settings-advanced-gate-CJ9MbRP8.js +0 -1
  208. package/dist/gateway/static/root/assets/setup-status-panel-BH4xQKM9.js +0 -1
  209. package/dist/gateway/static/root/assets/shares-settings-B6kKkvOb.js +0 -1
  210. package/dist/gateway/static/root/assets/skill-reload-api-DtjS-nUK.js +0 -1
  211. package/dist/gateway/static/root/assets/skills-page-CQEoPv0u.js +0 -2
  212. package/dist/gateway/static/root/assets/sliding-segmented-uWmrxE3P.js +0 -1
  213. package/dist/gateway/static/root/assets/user-context-page-Dn2T3itn.js +0 -1
  214. package/dist/gateway/static/root/assets/voice-api-key-field-wtWc3SfN.js +0 -1
  215. package/dist/gateway/static/root/assets/work-discovery-page-Ckxhj66e.js +0 -1
  216. package/dist/gateway/static/root/assets/work-item-detail-page-_1QgGPNw.js +0 -1
  217. package/dist/gateway/static/root/assets/work-page-BPdf2iuv.js +0 -1
  218. package/dist/gateway/static/root/assets/workflows-page-DdKTx0Hs.js +0 -11
@@ -0,0 +1,103 @@
1
+ //#region src/gateway/chat-stream/thinking-delta-coalescer.ts
2
+ const DEFAULT_WINDOW_MS = 24;
3
+ const DEFAULT_MAX_CHARS = 256;
4
+ function isThinkingDelta(event) {
5
+ return event.type === "thinking_delta";
6
+ }
7
+ function belongsToSameThinkingStream(left, right) {
8
+ return left.runId === right.runId && left.sessionKey === right.sessionKey && left.payload.messageId === right.payload.messageId;
9
+ }
10
+ function mergeThinkingDelta(left, right) {
11
+ return {
12
+ ...left,
13
+ payload: {
14
+ ...left.payload,
15
+ delta: left.payload.delta + right.payload.delta
16
+ }
17
+ };
18
+ }
19
+ async function nextBeforeDeadline(next, deadlineAt) {
20
+ let timer;
21
+ try {
22
+ return await Promise.race([next.then((result) => ({
23
+ kind: "next",
24
+ result
25
+ })), new Promise((resolve) => {
26
+ timer = setTimeout(() => resolve({ kind: "timeout" }), Math.max(0, deadlineAt - Date.now()));
27
+ })]);
28
+ } finally {
29
+ if (timer !== void 0) clearTimeout(timer);
30
+ }
31
+ }
32
+ async function* coalesceThinkingDeltas(source, options = {}) {
33
+ const windowMs = options.windowMs ?? DEFAULT_WINDOW_MS;
34
+ const maxChars = options.maxChars ?? DEFAULT_MAX_CHARS;
35
+ if (!Number.isFinite(windowMs) || windowMs <= 0) throw new Error("thinking delta coalescer windowMs must be greater than zero");
36
+ if (!Number.isInteger(maxChars) || maxChars <= 0) throw new Error("thinking delta coalescer maxChars must be a positive integer");
37
+ const iterator = source[Symbol.asyncIterator]();
38
+ let next = iterator.next();
39
+ let pending;
40
+ let deadlineAt = 0;
41
+ try {
42
+ while (true) {
43
+ if (!pending) {
44
+ const result = await next;
45
+ if (result.done) break;
46
+ next = iterator.next();
47
+ if (!isThinkingDelta(result.value)) {
48
+ yield result.value;
49
+ continue;
50
+ }
51
+ pending = result.value;
52
+ deadlineAt = Date.now() + windowMs;
53
+ if (pending.payload.delta.length >= maxChars) {
54
+ yield pending;
55
+ pending = void 0;
56
+ }
57
+ continue;
58
+ }
59
+ const outcome = await nextBeforeDeadline(next, deadlineAt);
60
+ if (outcome.kind === "timeout") {
61
+ yield pending;
62
+ pending = void 0;
63
+ continue;
64
+ }
65
+ const result = outcome.result;
66
+ if (result.done) {
67
+ yield pending;
68
+ pending = void 0;
69
+ break;
70
+ }
71
+ next = iterator.next();
72
+ const event = result.value;
73
+ if (!isThinkingDelta(event) || !belongsToSameThinkingStream(pending, event)) {
74
+ yield pending;
75
+ pending = void 0;
76
+ if (isThinkingDelta(event)) {
77
+ pending = event;
78
+ deadlineAt = Date.now() + windowMs;
79
+ if (pending.payload.delta.length >= maxChars) {
80
+ yield pending;
81
+ pending = void 0;
82
+ }
83
+ } else yield event;
84
+ continue;
85
+ }
86
+ pending = mergeThinkingDelta(pending, event);
87
+ if (pending.payload.delta.length >= maxChars) {
88
+ yield pending;
89
+ pending = void 0;
90
+ }
91
+ }
92
+ } catch (error) {
93
+ if (pending) {
94
+ yield pending;
95
+ pending = void 0;
96
+ }
97
+ throw error;
98
+ } finally {
99
+ await iterator.return?.();
100
+ }
101
+ }
102
+ //#endregion
103
+ export { coalesceThinkingDeltas };
@@ -3,6 +3,10 @@ import "../../../storage/sqlite/index.js";
3
3
  import { GoalService } from "../../../goals/goal-service.js";
4
4
  import "../../../goals/index.js";
5
5
  import { listGatewayAgents } from "../../agents-admin.js";
6
+ import { listFocusCalendarSignals } from "../../../proactive/calendar-signals.js";
7
+ import { FocusService } from "../../../proactive/focus-service.js";
8
+ import { listProactiveInsights } from "../../../proactive/insight-repository.js";
9
+ import "../../../proactive/index.js";
6
10
  import { WorkItemService } from "../../../work-items/work-item-service.js";
7
11
  import "../../../work-items/index.js";
8
12
  import { getTunnelService } from "../../../tunnel/tunnel-service.js";
@@ -155,6 +159,7 @@ function buildHomeBriefing(input) {
155
159
  function registerHomeRoutes(authenticated, deps) {
156
160
  const { service } = deps;
157
161
  const goals = new GoalService();
162
+ const focuses = new FocusService(service.automationServiceInstance);
158
163
  authenticated.get("/api/home", async (c) => {
159
164
  const notes = service.notesServiceInstance;
160
165
  const sessions = service.sessions;
@@ -165,7 +170,8 @@ function registerHomeRoutes(authenticated, deps) {
165
170
  const health = service.getHealth();
166
171
  const workItems = new WorkItemService();
167
172
  const nowMs = Date.now();
168
- const [recentlyOpened, inbox, pendingTasks, recentSessions, workflowRuns, automations, automationRuns, projects, allWorkItems, activeGoals, connectorApprovals] = await Promise.all([
173
+ const focusViews = focuses.list();
174
+ const [recentlyOpened, inbox, pendingTasks, recentSessions, workflowRuns, automations, automationRuns, projects, allWorkItems, activeGoals, connectorApprovals, proactiveInsights, calendarSignals] = await Promise.all([
169
175
  notes.listNotes({
170
176
  sortBy: "lastOpenedAt",
171
177
  sortOrder: "desc",
@@ -204,7 +210,12 @@ function registerHomeRoutes(authenticated, deps) {
204
210
  principalId: "local-owner",
205
211
  status: "pending",
206
212
  limit: 100
207
- }))
213
+ })),
214
+ Promise.resolve(listProactiveInsights({
215
+ status: ["unread"],
216
+ limit: 10
217
+ })),
218
+ Promise.resolve(listFocusCalendarSignals(focusViews, nowMs))
208
219
  ]);
209
220
  const projectsById = new Map(projects.items.map((project) => [project.id, project]));
210
221
  const activeWorkItems = allWorkItems.items.filter((item) => item.status !== "done" && item.status !== "cancelled").sort((left, right) => {
@@ -338,6 +349,8 @@ function registerHomeRoutes(authenticated, deps) {
338
349
  },
339
350
  briefing,
340
351
  decisions,
352
+ proactiveInsights,
353
+ calendarSignals,
341
354
  work: {
342
355
  attentionCount: activeWorkItems.filter((item) => workItemAttentionRank(item, nowMs) < 3).length,
343
356
  overdueCount: activeWorkItems.filter((item) => item.dueAt != null && item.dueAt < nowMs).length,
@@ -1,6 +1,10 @@
1
1
  import { getAgentDefaultModelRef, init_schema } from "../../../config/schema.js";
2
2
  import { init_providers, resolveModel } from "../../../providers/index.js";
3
3
  import { isLocalModelBaseUrl } from "../../../providers/model-call.js";
4
+ import { listFocusCalendarSignals } from "../../../proactive/calendar-signals.js";
5
+ import { FocusService } from "../../../proactive/focus-service.js";
6
+ import { claimProactiveInsightApproval, getProactiveInsight, setProactiveInsightStatus } from "../../../proactive/insight-repository.js";
7
+ import "../../../proactive/index.js";
4
8
  import { previewWorkDiscoveryRoot } from "../../../work-discovery/probe.js";
5
9
  import { WorkDiscoveryService } from "../../../work-discovery/service.js";
6
10
  //#region src/gateway/hono/routes/work-discovery.ts
@@ -26,6 +30,7 @@ function stringField(body, field) {
26
30
  }
27
31
  function registerWorkDiscoveryRoutes(authenticated, deps) {
28
32
  const service = workDiscoveryService(deps);
33
+ const focuses = new FocusService(deps.service.automationServiceInstance);
29
34
  const limited = deps.strictRateLimitMiddleware;
30
35
  authenticated.get("/api/onboarding/work-discovery", (c) => c.json({
31
36
  enabled: service.isEnabled(),
@@ -77,6 +82,178 @@ function registerWorkDiscoveryRoutes(authenticated, deps) {
77
82
  }, 400);
78
83
  }
79
84
  });
85
+ authenticated.post("/api/work-discovery/candidates", limited, async (c) => {
86
+ if (!service.isEnabled()) return c.json({
87
+ ok: false,
88
+ error: "Work discovery is disabled"
89
+ }, 404);
90
+ try {
91
+ const candidates = await service.discoverCandidates(c.req.raw.signal);
92
+ return c.json({
93
+ ok: true,
94
+ candidates
95
+ });
96
+ } catch (error) {
97
+ return c.json({
98
+ ok: false,
99
+ error: error instanceof Error ? error.message : String(error)
100
+ }, 500);
101
+ }
102
+ });
103
+ authenticated.get("/api/work-discovery/sources/directories", (c) => c.json({
104
+ ok: true,
105
+ sources: service.listDirectorySources()
106
+ }));
107
+ authenticated.post("/api/work-discovery/sources/directories", limited, async (c) => {
108
+ const rootPath = stringField(await c.req.json().catch(() => null), "rootPath");
109
+ if (!rootPath) return c.json({
110
+ ok: false,
111
+ error: "Missing rootPath"
112
+ }, 400);
113
+ try {
114
+ const source = await service.grantDirectorySource(rootPath);
115
+ return c.json({
116
+ ok: true,
117
+ source
118
+ }, 201);
119
+ } catch (error) {
120
+ return c.json({
121
+ ok: false,
122
+ error: error instanceof Error ? error.message : String(error)
123
+ }, 400);
124
+ }
125
+ });
126
+ authenticated.delete("/api/work-discovery/sources/directories/:sourceId", limited, (c) => {
127
+ const source = service.revokeDirectorySource(c.req.param("sourceId"));
128
+ return source ? c.json({
129
+ ok: true,
130
+ source
131
+ }) : c.json({
132
+ ok: false,
133
+ error: "Source not found"
134
+ }, 404);
135
+ });
136
+ authenticated.post("/api/work-discovery/sources/directories/:sourceId/runs", limited, async (c) => {
137
+ const idempotencyKey = stringField(await c.req.json().catch(() => null), "idempotencyKey");
138
+ if (!idempotencyKey) return c.json({
139
+ ok: false,
140
+ error: "Missing idempotencyKey"
141
+ }, 400);
142
+ try {
143
+ const run = await service.rescanDirectorySource({
144
+ id: c.req.param("sourceId"),
145
+ idempotencyKey
146
+ });
147
+ return c.json({
148
+ ok: true,
149
+ run
150
+ }, 202);
151
+ } catch (error) {
152
+ return c.json({
153
+ ok: false,
154
+ error: error instanceof Error ? error.message : String(error)
155
+ }, 400);
156
+ }
157
+ });
158
+ authenticated.post("/api/work-discovery/sources/directories/check", limited, async (c) => {
159
+ return c.json({
160
+ ok: true,
161
+ checks: await service.checkDirectorySources()
162
+ });
163
+ });
164
+ authenticated.post("/api/work-discovery/sources/directories/:sourceId/refresh-if-changed", limited, async (c) => {
165
+ const idempotencyKey = stringField(await c.req.json().catch(() => null), "idempotencyKey");
166
+ if (!idempotencyKey) return c.json({
167
+ ok: false,
168
+ error: "Missing idempotencyKey"
169
+ }, 400);
170
+ try {
171
+ const result = await service.refreshDirectorySourceIfChanged({
172
+ id: c.req.param("sourceId"),
173
+ idempotencyKey
174
+ });
175
+ return c.json({
176
+ ok: true,
177
+ ...result
178
+ }, result.changed ? 202 : 200);
179
+ } catch (error) {
180
+ return c.json({
181
+ ok: false,
182
+ error: error instanceof Error ? error.message : String(error)
183
+ }, 400);
184
+ }
185
+ });
186
+ authenticated.post("/api/work-discovery/personal-context/import", limited, async (c) => {
187
+ if (!service.isEnabled()) return c.json({
188
+ ok: false,
189
+ error: "Work discovery is disabled"
190
+ }, 404);
191
+ const body = await c.req.json().catch(() => null);
192
+ const items = body && typeof body === "object" && Array.isArray(body.items) ? body.items.slice(0, 150) : [];
193
+ const runId = stringField(body, "runId");
194
+ if (!items.length) return c.json({
195
+ ok: false,
196
+ error: "No personal context was provided"
197
+ }, 400);
198
+ try {
199
+ const result = await service.importPersonalContext(items, c.req.raw.signal, runId || void 0);
200
+ return c.json({
201
+ ok: true,
202
+ ...result
203
+ }, 201);
204
+ } catch (error) {
205
+ return c.json({
206
+ ok: false,
207
+ error: error instanceof Error ? error.message : String(error)
208
+ }, 400);
209
+ }
210
+ });
211
+ authenticated.post("/api/work-discovery/personal-context/profile", limited, async (c) => {
212
+ const body = await c.req.json().catch(() => null);
213
+ const decisions = (body && typeof body === "object" && Array.isArray(body.decisions) ? body.decisions : []).flatMap((value) => {
214
+ if (!value || typeof value !== "object") return [];
215
+ const item = value;
216
+ const memoryRecordId = typeof item.memoryRecordId === "string" ? item.memoryRecordId.trim() : "";
217
+ const status = item.status === "accepted" || item.status === "edited" || item.status === "rejected" ? item.status : void 0;
218
+ if (!memoryRecordId || !status) return [];
219
+ return [{
220
+ memoryRecordId,
221
+ status,
222
+ ...typeof item.statement === "string" ? { statement: item.statement } : {}
223
+ }];
224
+ }).slice(0, 10);
225
+ if (!decisions.length) return c.json({
226
+ ok: false,
227
+ error: "At least one valid decision is required"
228
+ }, 400);
229
+ return c.json({
230
+ ok: true,
231
+ decisions: service.updatePersonalContextProfile({ decisions })
232
+ });
233
+ });
234
+ authenticated.post("/api/work-discovery/quick-runs", limited, async (c) => {
235
+ if (!service.isEnabled()) return c.json({
236
+ ok: false,
237
+ error: "Work discovery is disabled"
238
+ }, 404);
239
+ const idempotencyKey = stringField(await c.req.json().catch(() => null), "idempotencyKey");
240
+ if (!idempotencyKey) return c.json({
241
+ ok: false,
242
+ error: "Missing idempotencyKey"
243
+ }, 400);
244
+ try {
245
+ const run = await service.startQuickRun({ idempotencyKey });
246
+ return c.json({
247
+ ok: true,
248
+ run
249
+ }, 202);
250
+ } catch (error) {
251
+ return c.json({
252
+ ok: false,
253
+ error: error instanceof Error ? error.message : String(error)
254
+ }, 400);
255
+ }
256
+ });
80
257
  authenticated.post("/api/work-discovery/runs", limited, async (c) => {
81
258
  if (!service.isEnabled()) return c.json({
82
259
  ok: false,
@@ -117,6 +294,250 @@ function registerWorkDiscoveryRoutes(authenticated, deps) {
117
294
  error: "Run not found"
118
295
  }, 404);
119
296
  });
297
+ authenticated.get("/api/work-discovery/runs/:runId/investigation", (c) => {
298
+ const result = service.getInvestigation(c.req.param("runId"));
299
+ return result ? c.json({
300
+ ok: true,
301
+ ...result
302
+ }) : c.json({
303
+ ok: false,
304
+ error: "Investigation not found"
305
+ }, 404);
306
+ });
307
+ authenticated.get("/api/work-understanding/threads", (c) => {
308
+ const projectId = c.req.query("projectId")?.trim();
309
+ const limit = Number(c.req.query("limit"));
310
+ return c.json({
311
+ ok: true,
312
+ threads: service.listWorkThreads({
313
+ ...projectId ? { projectId } : {},
314
+ ...Number.isFinite(limit) ? { limit } : {}
315
+ })
316
+ });
317
+ });
318
+ authenticated.get("/api/work-understanding/metrics", (c) => c.json({
319
+ ok: true,
320
+ metrics: service.getUnderstandingMetrics()
321
+ }));
322
+ authenticated.get("/api/work-understanding/sources/:sourceId/lineage", (c) => {
323
+ const lineage = service.getSourceLineage(c.req.param("sourceId"));
324
+ return lineage ? c.json({
325
+ ok: true,
326
+ lineage
327
+ }) : c.json({
328
+ ok: false,
329
+ error: "Source not found"
330
+ }, 404);
331
+ });
332
+ authenticated.delete("/api/work-understanding/sources/:sourceId/derived-data", limited, (c) => {
333
+ const deleted = service.deleteSourceDerivedData(c.req.param("sourceId"));
334
+ return deleted ? c.json({
335
+ ok: true,
336
+ deleted
337
+ }) : c.json({
338
+ ok: false,
339
+ error: "Source not found"
340
+ }, 404);
341
+ });
342
+ authenticated.get("/api/work-understanding/threads/:threadId", (c) => {
343
+ const thread = service.getWorkThread(c.req.param("threadId"));
344
+ return thread ? c.json({
345
+ ok: true,
346
+ thread
347
+ }) : c.json({
348
+ ok: false,
349
+ error: "Work thread not found"
350
+ }, 404);
351
+ });
352
+ authenticated.patch("/api/work-understanding/threads/:threadId", limited, async (c) => {
353
+ const body = await c.req.json().catch(() => null);
354
+ const decision = stringField(body, "decision");
355
+ if (![
356
+ "confirmed",
357
+ "corrected",
358
+ "rejected",
359
+ "paused",
360
+ "completed"
361
+ ].includes(decision)) return c.json({
362
+ ok: false,
363
+ error: "Invalid work thread decision"
364
+ }, 400);
365
+ const thread = service.updateWorkThread({
366
+ id: c.req.param("threadId"),
367
+ decision,
368
+ ...stringField(body, "correctedTitle") ? { correctedTitle: stringField(body, "correctedTitle") } : {},
369
+ ...stringField(body, "correctedSummary") ? { correctedSummary: stringField(body, "correctedSummary") } : {}
370
+ });
371
+ return thread ? c.json({
372
+ ok: true,
373
+ thread
374
+ }) : c.json({
375
+ ok: false,
376
+ error: "Work thread not found"
377
+ }, 404);
378
+ });
379
+ authenticated.get("/api/focuses", (c) => {
380
+ return c.json({
381
+ ok: true,
382
+ focuses: focuses.list({ includeUnreviewed: c.req.query("includeUnreviewed") === "true" })
383
+ });
384
+ });
385
+ authenticated.post("/api/focuses/:focusId/confirm", limited, (c) => {
386
+ const focus = focuses.confirm(c.req.param("focusId"));
387
+ return focus ? c.json({
388
+ ok: true,
389
+ focus
390
+ }) : c.json({
391
+ ok: false,
392
+ error: "Focus not found"
393
+ }, 404);
394
+ });
395
+ authenticated.post("/api/focuses/:focusId/watches/trial", limited, async (c) => {
396
+ const requestedKind = stringField(await c.req.json().catch(() => null), "kind");
397
+ const kind = [
398
+ "progress",
399
+ "staleness",
400
+ "deadline",
401
+ "intelligence"
402
+ ].includes(requestedKind) ? requestedKind : void 0;
403
+ try {
404
+ const result = await focuses.activateTrial({
405
+ threadId: c.req.param("focusId"),
406
+ ...kind ? { kind } : {}
407
+ });
408
+ return c.json({
409
+ ok: true,
410
+ ...result
411
+ }, 201);
412
+ } catch (error) {
413
+ return c.json({
414
+ ok: false,
415
+ error: error instanceof Error ? error.message : String(error)
416
+ }, 400);
417
+ }
418
+ });
419
+ authenticated.post("/api/focuses/:focusId/watches/:watchId/pause", limited, async (c) => {
420
+ const watch = focuses.list({ includeUnreviewed: true }).find((item) => item.id === c.req.param("focusId"))?.watches.find((item) => item.id === c.req.param("watchId"));
421
+ if (!watch) return c.json({
422
+ ok: false,
423
+ error: "Watch not found"
424
+ }, 404);
425
+ const paused = await focuses.pauseWatch(watch.id);
426
+ return c.json({
427
+ ok: true,
428
+ watch: paused
429
+ });
430
+ });
431
+ authenticated.patch("/api/proactive/insights/:insightId", limited, async (c) => {
432
+ const requested = stringField(await c.req.json().catch(() => null), "status");
433
+ const status = requested === "read" || requested === "dismissed" ? requested : void 0;
434
+ if (!status) return c.json({
435
+ ok: false,
436
+ error: "Status must be read or dismissed"
437
+ }, 400);
438
+ const insight = setProactiveInsightStatus(c.req.param("insightId"), status, Date.now(), "unread");
439
+ if (!insight) return c.json({
440
+ ok: false,
441
+ error: "Insight was already handled or not found"
442
+ }, 409);
443
+ if (status === "dismissed") await focuses.recordInsightFeedback(insight.watchId, false);
444
+ return c.json({
445
+ ok: true,
446
+ insight
447
+ });
448
+ });
449
+ authenticated.post("/api/proactive/insights/:insightId/approve", limited, async (c) => {
450
+ const insight = getProactiveInsight(c.req.param("insightId"));
451
+ if (!insight) return c.json({
452
+ ok: false,
453
+ error: "Insight not found"
454
+ }, 404);
455
+ if (insight.status !== "unread") return c.json({
456
+ ok: false,
457
+ error: `Insight is ${insight.status}`
458
+ }, 409);
459
+ const focus = focuses.list({ includeUnreviewed: true }).find((item) => item.watches.some((watch) => watch.id === insight.watchId));
460
+ if (!focus) return c.json({
461
+ ok: false,
462
+ error: "Focus not found"
463
+ }, 404);
464
+ if (!claimProactiveInsightApproval(insight.id)) return c.json({
465
+ ok: false,
466
+ error: "Insight was already handled"
467
+ }, 409);
468
+ let automationId;
469
+ try {
470
+ const automation = await deps.service.automationServiceInstance.create({
471
+ name: `Investigate: ${insight.title}`.slice(0, 200),
472
+ description: `User-approved read-only proposal from proactive insight ${insight.id}.`,
473
+ ...focus.projectIds[0] ? { projectId: focus.projectIds[0] } : {},
474
+ trigger: { kind: "manual" },
475
+ action: {
476
+ kind: "agent",
477
+ instruction: [
478
+ "The user approved this evidence-backed proposal for read-only investigation and preparation.",
479
+ `Focus: ${focus.title}`,
480
+ `Observed change: ${insight.summary}`,
481
+ `Why it matters: ${insight.whyItMatters}`,
482
+ `Approved next step: ${insight.nextAction}`,
483
+ `Evidence: ${JSON.stringify(insight.evidence)}`,
484
+ "Investigate or prepare the requested material. Return a concise result with evidence. Do not modify files or external systems."
485
+ ].join("\n"),
486
+ timeoutSeconds: 300
487
+ },
488
+ safety: { mode: "suggest_only" },
489
+ afterRun: { kind: "none" },
490
+ reliability: {
491
+ timeoutSeconds: 300,
492
+ disableAfterConsecutiveFailures: 1
493
+ }
494
+ });
495
+ automationId = automation.id;
496
+ const run = await deps.service.automationServiceInstance.runNow(automation.id);
497
+ await focuses.recordInsightFeedback(insight.watchId, true);
498
+ return c.json({
499
+ ok: true,
500
+ automationId: automation.id,
501
+ runId: run.id
502
+ }, 202);
503
+ } catch (error) {
504
+ if (automationId) await deps.service.automationServiceInstance.remove(automationId);
505
+ setProactiveInsightStatus(insight.id, "unread", Date.now(), "approved");
506
+ return c.json({
507
+ ok: false,
508
+ error: error instanceof Error ? error.message : String(error)
509
+ }, 500);
510
+ }
511
+ });
512
+ authenticated.post("/api/focuses/:focusId/calendar/:signalId/prepare", limited, async (c) => {
513
+ const focus = focuses.list().find((item) => item.id === c.req.param("focusId"));
514
+ if (!focus) return c.json({
515
+ ok: false,
516
+ error: "Focus not found"
517
+ }, 404);
518
+ const signal = listFocusCalendarSignals([focus]).find((item) => item.id === c.req.param("signalId"));
519
+ if (!signal) return c.json({
520
+ ok: false,
521
+ error: "Calendar event not found"
522
+ }, 404);
523
+ const eventContext = `${signal.title} at ${new Date(signal.startsAt).toISOString()}`;
524
+ try {
525
+ const result = await focuses.activateTrial({
526
+ threadId: focus.id,
527
+ kind: "deadline",
528
+ eventContext
529
+ });
530
+ return c.json({
531
+ ok: true,
532
+ ...result
533
+ });
534
+ } catch (error) {
535
+ return c.json({
536
+ ok: false,
537
+ error: error instanceof Error ? error.message : String(error)
538
+ }, 500);
539
+ }
540
+ });
120
541
  authenticated.post("/api/work-discovery/runs/:runId/cancel", (c) => {
121
542
  const run = service.cancelRun(c.req.param("runId"));
122
543
  return run ? c.json({
@@ -169,6 +590,36 @@ function registerWorkDiscoveryRoutes(authenticated, deps) {
169
590
  }, 400);
170
591
  }
171
592
  });
593
+ authenticated.post("/api/work-discovery/runs/:runId/profile", limited, async (c) => {
594
+ const body = await c.req.json().catch(() => null);
595
+ const decisions = (body && typeof body === "object" && Array.isArray(body.decisions) ? body.decisions : []).flatMap((value) => {
596
+ if (!value || typeof value !== "object") return [];
597
+ const item = value;
598
+ const id = typeof item.id === "string" ? item.id.trim() : "";
599
+ const status = item.status === "accepted" || item.status === "edited" || item.status === "rejected" ? item.status : void 0;
600
+ if (!id || !status) return [];
601
+ return [{
602
+ id,
603
+ status,
604
+ ...typeof item.statement === "string" ? { statement: item.statement } : {}
605
+ }];
606
+ }).slice(0, 10);
607
+ if (!decisions.length) return c.json({
608
+ ok: false,
609
+ error: "At least one valid decision is required"
610
+ }, 400);
611
+ const run = service.updateProfileCandidates({
612
+ runId: c.req.param("runId"),
613
+ decisions
614
+ });
615
+ return run ? c.json({
616
+ ok: true,
617
+ run
618
+ }) : c.json({
619
+ ok: false,
620
+ error: "Profile candidates not found"
621
+ }, 404);
622
+ });
172
623
  authenticated.post("/api/work-discovery/runs/:runId/suggestions/:suggestionId/select", (c) => {
173
624
  return service.selectSuggestion(c.req.param("runId"), c.req.param("suggestionId")) ? c.json({ ok: true }) : c.json({
174
625
  ok: false,
@@ -23,7 +23,7 @@ export declare function createAgentSSEHandler(config: SSEHandlerConfig): (c: Con
23
23
  * POST /api/agent/resume — Re-attach to an in-progress agent run via SSE.
24
24
  *
25
25
  * Request body: { runId, sessionKey }
26
- * The relay replays all buffered events from the beginning and then live-tails
26
+ * The relay replays retained buffered events from the beginning and then live-tails
27
27
  * until the run completes.
28
28
  *
29
29
  * SSE events are identical to those from POST /api/agent.
@@ -168,7 +168,7 @@ function createAgentSSEHandler(config) {
168
168
  * POST /api/agent/resume — Re-attach to an in-progress agent run via SSE.
169
169
  *
170
170
  * Request body: { runId, sessionKey }
171
- * The relay replays all buffered events from the beginning and then live-tails
171
+ * The relay replays retained buffered events from the beginning and then live-tails
172
172
  * until the run completes.
173
173
  *
174
174
  * SSE events are identical to those from POST /api/agent.
@@ -7,6 +7,7 @@ import { shouldSkipWebchatInboundByAbortCutoff } from "../../session/abort-cutof
7
7
  import "../chat-limits.js";
8
8
  import { resolveWebchatSessionKey } from "../resolve-webchat-session-key.js";
9
9
  import { ChatStreamMapper } from "../chat-stream/mapper.js";
10
+ import { coalesceThinkingDeltas } from "../chat-stream/thinking-delta-coalescer.js";
10
11
  import crypto from "crypto";
11
12
  //#region src/gateway/service/run-gateway-agent.ts
12
13
  init_logger();
@@ -90,7 +91,10 @@ async function* runGatewayAgent(deps, message, channel, chatId, attachments, thi
90
91
  signal: mergedSignal,
91
92
  runId
92
93
  });
93
- for await (const event of eventStream) yield* emitAndYield(mapper.map(event));
94
+ const mappedEvents = (async function* () {
95
+ for await (const event of eventStream) yield* mapper.map(event);
96
+ })();
97
+ for await (const event of coalesceThinkingDeltas(mappedEvents)) yield* emitAndYield([event]);
94
98
  const endStatus = mergedSignal.aborted ? "cancelled" : "success";
95
99
  const endSummary = mergedSignal.aborted ? "Interrupted" : "Message processed successfully";
96
100
  yield* emitAndYield(mapper.end(endStatus, endSummary));
@@ -61,6 +61,7 @@ export declare class GatewayService {
61
61
  private connectedKnowledgeCoordinator;
62
62
  private stopAutomationProductEventBridge;
63
63
  private stopSessionTranscriptAutomationEvents;
64
+ private stopFocusTrialExpiryReconciler;
64
65
  /**
65
66
  * Webchat agent invocation surface (`runAgent`, `abortAgentRun`, `steer*`,
66
67
  * `submitClarifyResponse`, clarify-bridge dispatch). Owns the
@@ -267,6 +268,7 @@ export declare class GatewayService {
267
268
  processDirect(content: string, sessionKey?: string): Promise<string>;
268
269
  subscribe(sessionId: string, listener: (event: ServiceEvent) => Promise<void> | void): () => void;
269
270
  emit(type: string, payload: unknown): void;
271
+ private handleAutomationRunCompleted;
270
272
  private startAutomationProductEventBridge;
271
273
  /** Replay events since `lastEventId` for SSE reconnection. */
272
274
  getEventsSince(sessionId: string, lastEventId: string): ServiceEvent[];