@ashx-j/lunr 0.2.24 → 0.2.26

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 (254) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/dist/builtin-extensions/index.d.ts.map +1 -1
  3. package/dist/builtin-extensions/index.js +1 -0
  4. package/dist/builtin-extensions/index.js.map +1 -1
  5. package/dist/builtin-extensions/lunr-computer-use.d.ts +3 -0
  6. package/dist/builtin-extensions/lunr-computer-use.d.ts.map +1 -0
  7. package/dist/builtin-extensions/lunr-computer-use.js +179 -0
  8. package/dist/builtin-extensions/lunr-computer-use.js.map +1 -0
  9. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts.map +1 -1
  10. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +1 -1
  11. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js.map +1 -1
  12. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts +4 -4
  13. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts.map +1 -1
  14. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js +8 -5
  15. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
  16. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts +1 -0
  17. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
  18. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +11 -2
  19. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
  20. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
  21. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +3 -3
  22. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
  23. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
  24. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +15 -1
  25. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
  26. package/dist/builtin-extensions/pi-subagents/src/runs/shared/chain-outputs.d.ts +1 -0
  27. package/dist/builtin-extensions/pi-subagents/src/runs/shared/chain-outputs.d.ts.map +1 -1
  28. package/dist/builtin-extensions/pi-subagents/src/runs/shared/chain-outputs.js +5 -0
  29. package/dist/builtin-extensions/pi-subagents/src/runs/shared/chain-outputs.js.map +1 -1
  30. package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.d.ts +1 -1
  31. package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.d.ts.map +1 -1
  32. package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.js +2 -0
  33. package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.js.map +1 -1
  34. package/dist/builtin-extensions/pi-subagents/src/tui/communication.d.ts.map +1 -1
  35. package/dist/builtin-extensions/pi-subagents/src/tui/communication.js +1 -1
  36. package/dist/builtin-extensions/pi-subagents/src/tui/communication.js.map +1 -1
  37. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +1 -0
  38. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  39. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +5 -3
  40. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  41. package/dist/core/computer-use/adapter.d.ts +28 -0
  42. package/dist/core/computer-use/adapter.d.ts.map +1 -0
  43. package/dist/core/computer-use/adapter.js +133 -0
  44. package/dist/core/computer-use/adapter.js.map +1 -0
  45. package/dist/core/computer-use/image.d.ts +27 -0
  46. package/dist/core/computer-use/image.d.ts.map +1 -0
  47. package/dist/core/computer-use/image.js +72 -0
  48. package/dist/core/computer-use/image.js.map +1 -0
  49. package/dist/core/computer-use/lease.d.ts +18 -0
  50. package/dist/core/computer-use/lease.d.ts.map +1 -0
  51. package/dist/core/computer-use/lease.js +129 -0
  52. package/dist/core/computer-use/lease.js.map +1 -0
  53. package/dist/core/computer-use/macos-runtime.d.ts +14 -0
  54. package/dist/core/computer-use/macos-runtime.d.ts.map +1 -0
  55. package/dist/core/computer-use/macos-runtime.js +148 -0
  56. package/dist/core/computer-use/macos-runtime.js.map +1 -0
  57. package/dist/core/computer-use/policy.d.ts +19 -0
  58. package/dist/core/computer-use/policy.d.ts.map +1 -0
  59. package/dist/core/computer-use/policy.js +67 -0
  60. package/dist/core/computer-use/policy.js.map +1 -0
  61. package/dist/core/computer-use/release.generated.d.ts +31 -0
  62. package/dist/core/computer-use/release.generated.d.ts.map +1 -0
  63. package/dist/core/computer-use/release.generated.js +36 -0
  64. package/dist/core/computer-use/release.generated.js.map +1 -0
  65. package/dist/core/computer-use/runtime.d.ts +13 -0
  66. package/dist/core/computer-use/runtime.d.ts.map +1 -0
  67. package/dist/core/computer-use/runtime.js +158 -0
  68. package/dist/core/computer-use/runtime.js.map +1 -0
  69. package/dist/core/computer-use/schemas.d.ts +100 -0
  70. package/dist/core/computer-use/schemas.d.ts.map +1 -0
  71. package/dist/core/computer-use/schemas.js +79 -0
  72. package/dist/core/computer-use/schemas.js.map +1 -0
  73. package/dist/core/computer-use/windows-desktop.d.ts +3 -0
  74. package/dist/core/computer-use/windows-desktop.d.ts.map +1 -0
  75. package/dist/core/computer-use/windows-desktop.js +48 -0
  76. package/dist/core/computer-use/windows-desktop.js.map +1 -0
  77. package/dist/core/computer-use/workflow.d.ts +38 -0
  78. package/dist/core/computer-use/workflow.d.ts.map +1 -0
  79. package/dist/core/computer-use/workflow.js +560 -0
  80. package/dist/core/computer-use/workflow.js.map +1 -0
  81. package/dist/core/extensions/loader.d.ts.map +1 -1
  82. package/dist/core/extensions/loader.js +4 -0
  83. package/dist/core/extensions/loader.js.map +1 -1
  84. package/dist/core/permissions.d.ts +1 -1
  85. package/dist/core/permissions.d.ts.map +1 -1
  86. package/dist/core/permissions.js +14 -3
  87. package/dist/core/permissions.js.map +1 -1
  88. package/dist/core/plan-mode.d.ts.map +1 -1
  89. package/dist/core/plan-mode.js +9 -0
  90. package/dist/core/plan-mode.js.map +1 -1
  91. package/dist/core/settings-manager.d.ts +6 -0
  92. package/dist/core/settings-manager.d.ts.map +1 -1
  93. package/dist/core/settings-manager.js +19 -0
  94. package/dist/core/settings-manager.js.map +1 -1
  95. package/dist/core/subagent-event-transport.d.ts +3 -0
  96. package/dist/core/subagent-event-transport.d.ts.map +1 -0
  97. package/dist/core/subagent-event-transport.js +61 -0
  98. package/dist/core/subagent-event-transport.js.map +1 -0
  99. package/dist/gateway/adapters/discord.d.ts.map +1 -1
  100. package/dist/gateway/adapters/discord.js +4 -9
  101. package/dist/gateway/adapters/discord.js.map +1 -1
  102. package/dist/gateway/adapters/telegram.d.ts.map +1 -1
  103. package/dist/gateway/adapters/telegram.js +7 -0
  104. package/dist/gateway/adapters/telegram.js.map +1 -1
  105. package/dist/gateway/agent-bridge.d.ts +4 -1
  106. package/dist/gateway/agent-bridge.d.ts.map +1 -1
  107. package/dist/gateway/agent-bridge.js +57 -17
  108. package/dist/gateway/agent-bridge.js.map +1 -1
  109. package/dist/gateway/approval.d.ts.map +1 -1
  110. package/dist/gateway/approval.js +3 -3
  111. package/dist/gateway/approval.js.map +1 -1
  112. package/dist/gateway/authz.d.ts +4 -4
  113. package/dist/gateway/authz.d.ts.map +1 -1
  114. package/dist/gateway/authz.js +8 -12
  115. package/dist/gateway/authz.js.map +1 -1
  116. package/dist/gateway/buttons.d.ts.map +1 -1
  117. package/dist/gateway/buttons.js +29 -19
  118. package/dist/gateway/buttons.js.map +1 -1
  119. package/dist/gateway/commands.d.ts +2 -0
  120. package/dist/gateway/commands.d.ts.map +1 -1
  121. package/dist/gateway/commands.js +35 -18
  122. package/dist/gateway/commands.js.map +1 -1
  123. package/dist/gateway/config.d.ts +1 -0
  124. package/dist/gateway/config.d.ts.map +1 -1
  125. package/dist/gateway/config.js +2 -0
  126. package/dist/gateway/config.js.map +1 -1
  127. package/dist/gateway/cron.d.ts +1 -1
  128. package/dist/gateway/cron.d.ts.map +1 -1
  129. package/dist/gateway/cron.js +22 -8
  130. package/dist/gateway/cron.js.map +1 -1
  131. package/dist/gateway/index.d.ts.map +1 -1
  132. package/dist/gateway/index.js +26 -26
  133. package/dist/gateway/index.js.map +1 -1
  134. package/dist/gateway/mobile-commands.d.ts.map +1 -1
  135. package/dist/gateway/mobile-commands.js +11 -11
  136. package/dist/gateway/mobile-commands.js.map +1 -1
  137. package/dist/gateway/presenter.d.ts +12 -1
  138. package/dist/gateway/presenter.d.ts.map +1 -1
  139. package/dist/gateway/presenter.js +148 -50
  140. package/dist/gateway/presenter.js.map +1 -1
  141. package/dist/gateway/router.d.ts +5 -5
  142. package/dist/gateway/router.d.ts.map +1 -1
  143. package/dist/gateway/router.js +95 -43
  144. package/dist/gateway/router.js.map +1 -1
  145. package/dist/gateway/service.d.ts.map +1 -1
  146. package/dist/gateway/service.js +13 -4
  147. package/dist/gateway/service.js.map +1 -1
  148. package/dist/gateway/setup.d.ts +5 -0
  149. package/dist/gateway/setup.d.ts.map +1 -1
  150. package/dist/gateway/setup.js +101 -43
  151. package/dist/gateway/setup.js.map +1 -1
  152. package/dist/gateway/stream.d.ts +6 -5
  153. package/dist/gateway/stream.d.ts.map +1 -1
  154. package/dist/gateway/stream.js +24 -11
  155. package/dist/gateway/stream.js.map +1 -1
  156. package/dist/modes/interactive/components/settings-selector.d.ts +4 -0
  157. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  158. package/dist/modes/interactive/components/settings-selector.js +20 -0
  159. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  160. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  161. package/dist/modes/interactive/interactive-mode.js +8 -0
  162. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  163. package/dist/modes/print-mode.d.ts.map +1 -1
  164. package/dist/modes/print-mode.js +9 -1
  165. package/dist/modes/print-mode.js.map +1 -1
  166. package/dist/node-runtime/adapter-VFG7WNPX.js +17 -0
  167. package/dist/node-runtime/{agent-session-services-IEF7LQWD.js → agent-session-services-5YXS2PLF.js} +13 -10
  168. package/dist/node-runtime/{builtin-extensions-EBAWTH4M.js → builtin-extensions-I2HIFEMY.js} +3 -2
  169. package/dist/node-runtime/{chunk-K5AGHH57.js → chunk-2CFDTH5B.js} +7 -3
  170. package/dist/node-runtime/{chunk-VWCE2HSZ.js → chunk-2HMIRK7W.js} +4 -4
  171. package/dist/node-runtime/chunk-2IDTZAG5.js +1415 -0
  172. package/dist/node-runtime/{chunk-IGV54Q5V.js → chunk-3MISGG4S.js} +137 -76
  173. package/dist/node-runtime/chunk-4SBQAI4T.js +2185 -0
  174. package/dist/node-runtime/{chunk-WHLRAGUQ.js → chunk-5B5QN3ZQ.js} +16 -16
  175. package/dist/node-runtime/chunk-5B6VKFEV.js +180 -0
  176. package/dist/node-runtime/{chunk-AD6VXAO7.js → chunk-5Q4A37RB.js} +15 -104
  177. package/dist/node-runtime/{chunk-4TLO4PO2.js → chunk-7IQGBKBJ.js} +7 -1401
  178. package/dist/node-runtime/{chunk-R3K2F5GI.js → chunk-B4HP6ZWM.js} +2 -0
  179. package/dist/node-runtime/{chunk-OCVIXVTZ.js → chunk-D5UCKPBQ.js} +12 -1
  180. package/dist/node-runtime/chunk-DVQSHCVP.js +97 -0
  181. package/dist/node-runtime/{chunk-HFVZUPFJ.js → chunk-E75WFZ33.js} +2 -2
  182. package/dist/node-runtime/chunk-FY4A63RK.js +73 -0
  183. package/dist/node-runtime/{chunk-RORY2HMJ.js → chunk-I74LN5JG.js} +16 -1
  184. package/dist/node-runtime/chunk-IKU6Y7DL.js +287 -0
  185. package/dist/node-runtime/{chunk-QM43FEAE.js → chunk-L24TPBS4.js} +6 -5
  186. package/dist/node-runtime/{chunk-DE2JB6J5.js → chunk-LJTIVM7I.js} +7 -7
  187. package/dist/node-runtime/{chunk-DSIKTUPW.js → chunk-LWJXTA6Z.js} +274 -2433
  188. package/dist/node-runtime/{chunk-YVI6L6LU.js → chunk-P7XBVXGT.js} +36 -21
  189. package/dist/node-runtime/{chunk-OVX6X377.js → chunk-PTFBPO3C.js} +1 -1
  190. package/dist/node-runtime/{chunk-F4FLTQKV.js → chunk-RAK2QA2B.js} +186 -1529
  191. package/dist/node-runtime/{chunk-L3LX4SNR.js → chunk-RCR3XAQY.js} +36 -8
  192. package/dist/node-runtime/chunk-RGQAEOM5.js +81 -0
  193. package/dist/node-runtime/{chunk-UWPJL22I.js → chunk-SLVOAQOY.js} +15 -4
  194. package/dist/node-runtime/chunk-SN2KSMPQ.js +1364 -0
  195. package/dist/node-runtime/{chunk-FU6OODRZ.js → chunk-SPVDBUOS.js} +1 -1
  196. package/dist/node-runtime/{chunk-5Q3EHOHI.js → chunk-T4DA4XE7.js} +19 -17
  197. package/dist/node-runtime/chunk-U5F4IC7R.js +53 -0
  198. package/dist/node-runtime/{chunk-UP2XBVH5.js → chunk-UOEDI7RP.js} +1 -1
  199. package/dist/node-runtime/{chunk-Y73WPE6S.js → chunk-UVWCDQYF.js} +79 -44
  200. package/dist/node-runtime/{chunk-ZXSGTGJ6.js → chunk-WIF24I7G.js} +21 -5
  201. package/dist/node-runtime/{chunk-OF7PWMG4.js → chunk-X3PGBLQ7.js} +3 -87
  202. package/dist/node-runtime/chunk-XHIRAZ26.js +108 -0
  203. package/dist/node-runtime/{chunk-YDO2F2IA.js → chunk-YWOJIGFY.js} +1 -1
  204. package/dist/node-runtime/{command-7BG7VVTS.js → command-FSBU2EVZ.js} +5 -5
  205. package/dist/node-runtime/{commands-NZLSOHPE.js → commands-EMBQQVVT.js} +4 -2
  206. package/dist/node-runtime/{commands-QFDKNQTL.js → commands-NAAL7KNT.js} +7 -6
  207. package/dist/node-runtime/{config-RORQUQFG.js → config-D4QUPAHX.js} +1 -1
  208. package/dist/node-runtime/{direct-tool-executor-77GW4F5P.js → direct-tool-executor-4D7UXYU4.js} +7 -4
  209. package/dist/node-runtime/{gateway-TWR3N65G.js → gateway-DQM333YV.js} +140 -85
  210. package/dist/node-runtime/index.js +28 -21
  211. package/dist/node-runtime/{init-HS6NQXSK.js → init-P5AUV77I.js} +4 -2
  212. package/dist/node-runtime/{install-cli-VAP6KBFN.js → install-cli-NN4YAU44.js} +3 -3
  213. package/dist/node-runtime/{interactive-mode-6J5VZ2TT.js → interactive-mode-YOHZVMKX.js} +14 -11
  214. package/dist/node-runtime/{lunr-browser-J5ZZYVGM.js → lunr-browser-IR7TL3J7.js} +3 -3
  215. package/dist/node-runtime/lunr-computer-use-Y23CCDIJ.js +158 -0
  216. package/dist/node-runtime/{lunr-cron-CXOL37AL.js → lunr-cron-USHC4LXH.js} +9 -7
  217. package/dist/node-runtime/main.js +19 -15
  218. package/dist/node-runtime/{mcp-auth-flow-QIXHW6QN.js → mcp-auth-flow-QVLFVLEO.js} +2 -1
  219. package/dist/node-runtime/{mobile-commands-VTBM5DSZ.js → mobile-commands-O5LCHL32.js} +6 -5
  220. package/dist/node-runtime/{package-manager-cli-ZLW5DZXF.js → package-manager-cli-Q7BL6KZO.js} +12 -8
  221. package/dist/node-runtime/{pi-mcp-adapter-5UKBZP44.js → pi-mcp-adapter-3PHF4KZ4.js} +5 -5
  222. package/dist/node-runtime/{pi-subagents-XPCLLKIM.js → pi-subagents-7IBSSGEW.js} +27 -21
  223. package/dist/node-runtime/{print-mode-XSFM26EL.js → print-mode-XQ76CYMT.js} +2 -1
  224. package/dist/node-runtime/{proxy-modes-LGEUYD7R.js → proxy-modes-5FAPSZEZ.js} +7 -4
  225. package/dist/node-runtime/rpc-entry.js +19 -15
  226. package/dist/node-runtime/runtime-EFPPD2UW.js +21 -0
  227. package/dist/node-runtime/{runtime-bridges-QYXUX2TG.js → runtime-bridges-7WVCLI76.js} +6 -5
  228. package/dist/node-runtime/{service-ISSP6VHE.js → service-MDQIPQ26.js} +1 -1
  229. package/dist/node-runtime/{settings-manager-WCQDUVQB.js → settings-manager-DJDM5AHM.js} +5 -4
  230. package/dist/node-runtime/setup-PFBWRNUI.js +215 -0
  231. package/dist/node-runtime/{subagent-executor-OTNISDQU.js → subagent-executor-LPZ4BHWE.js} +32 -14
  232. package/dist/node-runtime/{update-cli-VBR2777K.js → update-cli-HNMOTNGX.js} +5 -4
  233. package/dist/node-runtime/workflow-OHPBEFO4.js +637 -0
  234. package/dist/utils/image-resize-core.d.ts +2 -0
  235. package/dist/utils/image-resize-core.d.ts.map +1 -1
  236. package/dist/utils/image-resize-core.js +10 -0
  237. package/dist/utils/image-resize-core.js.map +1 -1
  238. package/docs/computer-use.md +362 -0
  239. package/docs/features.md +9 -7
  240. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  241. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  242. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  243. package/examples/extensions/gondolin/package-lock.json +2 -2
  244. package/examples/extensions/gondolin/package.json +1 -1
  245. package/examples/extensions/sandbox/package-lock.json +2 -2
  246. package/examples/extensions/sandbox/package.json +1 -1
  247. package/examples/extensions/with-deps/package-lock.json +2 -2
  248. package/examples/extensions/with-deps/package.json +1 -1
  249. package/install-lock/package-lock.json +3841 -0
  250. package/install-lock/package.json +18 -0
  251. package/native/computer-use/LICENSE.md +21 -0
  252. package/npm-shrinkwrap.json +3822 -0
  253. package/package.json +15 -9
  254. package/dist/node-runtime/setup-YRBKH2LD.js +0 -155
@@ -4,9 +4,9 @@
4
4
  * StreamConsumer accumulates assistant text deltas and mirrors them into a
5
5
  * single chat message: the first flush sends a new message, later flushes
6
6
  * edit it. Flushes happen at most every intervalMs and only once at least
7
- * `threshold` new chars have accumulated. finalize() forces a last flush and
8
- * returns the full text — the caller splits THAT via text.ts for final
9
- * delivery; the streaming preview itself is truncated to maxPreview-3 + "…".
7
+ * `threshold` new chars have accumulated. finalize() waits for pending previews;
8
+ * the router owns final delivery. Previews omit silence-marker lines and truncate
9
+ * at maxPreview-3 + "…".
10
10
  *
11
11
  * applySilenceFilter() recognizes the [SILENT] / NO_REPLY markers (whole
12
12
  * response, or first/last line) and returns null when nothing deliverable
@@ -33,6 +33,7 @@ export declare class StreamConsumer {
33
33
  private readonly options;
34
34
  private buffer;
35
35
  private flushedLen;
36
+ private lastPreview;
36
37
  private messageId;
37
38
  private lastFlushAt;
38
39
  private chain;
@@ -41,13 +42,13 @@ export declare class StreamConsumer {
41
42
  constructor(options: StreamConsumerOptions);
42
43
  /** Accumulate a delta; schedule a flush when both gates (chars, interval) pass. */
43
44
  push(delta: string): void;
44
- /** Force a final flush; resolves with the FULL text (caller splits for delivery). */
45
+ /** Wait for pending previews; final delivery belongs to the router. */
45
46
  finalize(): Promise<string>;
46
47
  /** The platform message id of the preview, when one was successfully sent. */
47
48
  get sentMessageId(): string | null;
48
49
  /** True when the preview had to truncate the full text (buffer > maxPreview). */
49
50
  get truncated(): boolean;
50
- /** The streaming preview: full text truncated to maxPreview-3 + "…". */
51
+ /** Preview visible text without a trailing silence-marker fragment. */
51
52
  private preview;
52
53
  private schedule;
53
54
  private flush;
@@ -1 +1 @@
1
- {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/gateway/stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,qBAAqB;IACrC,4FAA4F;IAC5F,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACtD,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,4FAA4F;IAC5F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB;AAQD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS9D;AAED,qBAAa,cAAc;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IAEnC,YAAY,OAAO,EAAE,qBAAqB,EAIzC;IAED,mFAAmF;IACnF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAQxB;IAED,qFAAqF;IAC/E,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,CAIhC;IAED,8EAA8E;IAC9E,IAAI,aAAa,IAAI,MAAM,GAAG,IAAI,CAEjC;IAED,iFAAiF;IACjF,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,0EAAwE;IACxE,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,QAAQ;YAIF,KAAK;CAcnB","sourcesContent":["/**\n * lunR: gateway streaming — throttled progressive replies.\n *\n * StreamConsumer accumulates assistant text deltas and mirrors them into a\n * single chat message: the first flush sends a new message, later flushes\n * edit it. Flushes happen at most every intervalMs and only once at least\n * `threshold` new chars have accumulated. finalize() forces a last flush and\n * returns the full text — the caller splits THAT via text.ts for final\n * delivery; the streaming preview itself is truncated to maxPreview-3 + \"…\".\n *\n * applySilenceFilter() recognizes the [SILENT] / NO_REPLY markers (whole\n * response, or first/last line) and returns null when nothing deliverable\n * remains.\n */\n\nexport interface StreamConsumerOptions {\n\t/** Send the initial message; resolves with the platform message id (null = send failed). */\n\tsendInitial: (text: string) => Promise<string | null>;\n\tedit: (messageId: string, text: string) => Promise<void>;\n\tintervalMs: number;\n\tthreshold: number;\n\t/** Platform max message length; the streaming preview is truncated to fit. Default 4096. */\n\tmaxPreview?: number;\n\t/** Clock injection for tests. */\n\tnow?: () => number;\n}\n\nconst SILENCE_MARKERS = new Set([\"[SILENT]\", \"NO_REPLY\"]);\n\nfunction isMarkerLine(line: string): boolean {\n\treturn SILENCE_MARKERS.has(line.trim());\n}\n\n/**\n * Null when the text is just a silence marker (whole, first-line, or\n * last-line); otherwise the text with a leading/trailing marker line\n * stripped (null when nothing else remains).\n */\nexport function applySilenceFilter(text: string): string | null {\n\tconst trimmed = text.trim();\n\tif (trimmed.length === 0) return null;\n\tif (isMarkerLine(trimmed)) return null;\n\tconst lines = trimmed.split(\"\\n\");\n\tif (lines.length > 0 && isMarkerLine(lines[0])) lines.shift();\n\tif (lines.length > 0 && isMarkerLine(lines[lines.length - 1])) lines.pop();\n\tconst result = lines.join(\"\\n\").trim();\n\treturn result.length > 0 ? result : null;\n}\n\nexport class StreamConsumer {\n\tprivate readonly options: StreamConsumerOptions;\n\tprivate buffer = \"\";\n\tprivate flushedLen = 0;\n\tprivate messageId: string | null = null;\n\tprivate lastFlushAt = 0;\n\tprivate chain: Promise<void> = Promise.resolve();\n\tprivate readonly maxPreview: number;\n\tprivate readonly now: () => number;\n\n\tconstructor(options: StreamConsumerOptions) {\n\t\tthis.options = options;\n\t\tthis.maxPreview = options.maxPreview ?? 4096;\n\t\tthis.now = options.now ?? (() => Date.now());\n\t}\n\n\t/** Accumulate a delta; schedule a flush when both gates (chars, interval) pass. */\n\tpush(delta: string): void {\n\t\tthis.buffer += delta;\n\t\tif (\n\t\t\tthis.buffer.length - this.flushedLen >= this.options.threshold &&\n\t\t\tthis.now() - this.lastFlushAt >= this.options.intervalMs\n\t\t) {\n\t\t\tthis.schedule();\n\t\t}\n\t}\n\n\t/** Force a final flush; resolves with the FULL text (caller splits for delivery). */\n\tasync finalize(): Promise<string> {\n\t\tthis.schedule(true);\n\t\tawait this.chain;\n\t\treturn this.buffer;\n\t}\n\n\t/** The platform message id of the preview, when one was successfully sent. */\n\tget sentMessageId(): string | null {\n\t\treturn this.messageId;\n\t}\n\n\t/** True when the preview had to truncate the full text (buffer > maxPreview). */\n\tget truncated(): boolean {\n\t\treturn this.buffer.length > this.maxPreview;\n\t}\n\n\t/** The streaming preview: full text truncated to maxPreview-3 + \"…\". */\n\tprivate preview(): string {\n\t\tif (this.buffer.length <= this.maxPreview) return this.buffer;\n\t\tlet cut = this.maxPreview - 3;\n\t\t// Don't truncate between surrogate halves.\n\t\tconst code = this.buffer.charCodeAt(cut - 1);\n\t\tif (code >= 0xd800 && code <= 0xdbff) cut -= 1;\n\t\treturn `${this.buffer.slice(0, cut)}…`;\n\t}\n\n\tprivate schedule(force = false): void {\n\t\tthis.chain = this.chain.then(() => this.flush(force)).catch(() => {});\n\t}\n\n\tprivate async flush(force: boolean): Promise<void> {\n\t\tconst hasNew = this.buffer.length > this.flushedLen;\n\t\tif (!hasNew) return;\n\t\tif (!force && this.messageId !== null && this.now() - this.lastFlushAt < this.options.intervalMs) return;\n\t\tconst text = this.preview();\n\t\tif (this.messageId === null) {\n\t\t\tthis.messageId = await this.options.sendInitial(text);\n\t\t\tif (this.messageId === null) return; // send failed; retry on the next flush\n\t\t} else {\n\t\t\tawait this.options.edit(this.messageId, text);\n\t\t}\n\t\tthis.flushedLen = this.buffer.length;\n\t\tthis.lastFlushAt = this.now();\n\t}\n}\n"]}
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/gateway/stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,qBAAqB;IACrC,4FAA4F;IAC5F,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACtD,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,4FAA4F;IAC5F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB;AAQD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS9D;AAED,qBAAa,cAAc;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IAEnC,YAAY,OAAO,EAAE,qBAAqB,EAIzC;IAED,mFAAmF;IACnF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAQxB;IAED,uEAAuE;IACjE,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,CAGhC;IAED,8EAA8E;IAC9E,IAAI,aAAa,IAAI,MAAM,GAAG,IAAI,CAEjC;IAED,iFAAiF;IACjF,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,uEAAuE;IACvE,OAAO,CAAC,OAAO;IAgBf,OAAO,CAAC,QAAQ;YAIF,KAAK;CAiBnB","sourcesContent":["/**\n * lunR: gateway streaming — throttled progressive replies.\n *\n * StreamConsumer accumulates assistant text deltas and mirrors them into a\n * single chat message: the first flush sends a new message, later flushes\n * edit it. Flushes happen at most every intervalMs and only once at least\n * `threshold` new chars have accumulated. finalize() waits for pending previews;\n * the router owns final delivery. Previews omit silence-marker lines and truncate\n * at maxPreview-3 + \"…\".\n *\n * applySilenceFilter() recognizes the [SILENT] / NO_REPLY markers (whole\n * response, or first/last line) and returns null when nothing deliverable\n * remains.\n */\n\nexport interface StreamConsumerOptions {\n\t/** Send the initial message; resolves with the platform message id (null = send failed). */\n\tsendInitial: (text: string) => Promise<string | null>;\n\tedit: (messageId: string, text: string) => Promise<void>;\n\tintervalMs: number;\n\tthreshold: number;\n\t/** Platform max message length; the streaming preview is truncated to fit. Default 4096. */\n\tmaxPreview?: number;\n\t/** Clock injection for tests. */\n\tnow?: () => number;\n}\n\nconst SILENCE_MARKERS = new Set([\"[SILENT]\", \"NO_REPLY\"]);\n\nfunction isMarkerLine(line: string): boolean {\n\treturn SILENCE_MARKERS.has(line.trim());\n}\n\n/**\n * Null when the text is just a silence marker (whole, first-line, or\n * last-line); otherwise the text with a leading/trailing marker line\n * stripped (null when nothing else remains).\n */\nexport function applySilenceFilter(text: string): string | null {\n\tconst trimmed = text.trim();\n\tif (trimmed.length === 0) return null;\n\tif (isMarkerLine(trimmed)) return null;\n\tconst lines = trimmed.split(\"\\n\");\n\tif (lines.length > 0 && isMarkerLine(lines[0])) lines.shift();\n\tif (lines.length > 0 && isMarkerLine(lines[lines.length - 1])) lines.pop();\n\tconst result = lines.join(\"\\n\").trim();\n\treturn result.length > 0 ? result : null;\n}\n\nexport class StreamConsumer {\n\tprivate readonly options: StreamConsumerOptions;\n\tprivate buffer = \"\";\n\tprivate flushedLen = 0;\n\tprivate lastPreview = \"\";\n\tprivate messageId: string | null = null;\n\tprivate lastFlushAt = 0;\n\tprivate chain: Promise<void> = Promise.resolve();\n\tprivate readonly maxPreview: number;\n\tprivate readonly now: () => number;\n\n\tconstructor(options: StreamConsumerOptions) {\n\t\tthis.options = options;\n\t\tthis.maxPreview = options.maxPreview ?? 4096;\n\t\tthis.now = options.now ?? (() => Date.now());\n\t}\n\n\t/** Accumulate a delta; schedule a flush when both gates (chars, interval) pass. */\n\tpush(delta: string): void {\n\t\tthis.buffer += delta;\n\t\tif (\n\t\t\tthis.buffer.length - this.flushedLen >= this.options.threshold &&\n\t\t\tthis.now() - this.lastFlushAt >= this.options.intervalMs\n\t\t) {\n\t\t\tthis.schedule();\n\t\t}\n\t}\n\n\t/** Wait for pending previews; final delivery belongs to the router. */\n\tasync finalize(): Promise<string> {\n\t\tawait this.chain;\n\t\treturn this.buffer;\n\t}\n\n\t/** The platform message id of the preview, when one was successfully sent. */\n\tget sentMessageId(): string | null {\n\t\treturn this.messageId;\n\t}\n\n\t/** True when the preview had to truncate the full text (buffer > maxPreview). */\n\tget truncated(): boolean {\n\t\treturn this.buffer.length > this.maxPreview;\n\t}\n\n\t/** Preview visible text without a trailing silence-marker fragment. */\n\tprivate preview(): string {\n\t\tconst lines = this.buffer.split(\"\\n\");\n\t\tconst last = lines.at(-1)?.trim() ?? \"\";\n\t\tif (SILENCE_MARKERS.has(last) || (last && [...SILENCE_MARKERS].some((marker) => marker.startsWith(last))))\n\t\t\tlines.pop();\n\t\tif (lines.length && isMarkerLine(lines[0])) lines.shift();\n\t\tconst visible = lines.join(\"\\n\").trim();\n\t\tif (!visible) return \"\";\n\t\tif (visible.length <= this.maxPreview) return visible;\n\t\tlet cut = this.maxPreview - 3;\n\t\t// Don't truncate between surrogate halves.\n\t\tconst code = visible.charCodeAt(cut - 1);\n\t\tif (code >= 0xd800 && code <= 0xdbff) cut -= 1;\n\t\treturn `${visible.slice(0, cut)}…`;\n\t}\n\n\tprivate schedule(force = false): void {\n\t\tthis.chain = this.chain.then(() => this.flush(force)).catch(() => {});\n\t}\n\n\tprivate async flush(force: boolean): Promise<void> {\n\t\tconst hasNew = this.buffer.length > this.flushedLen;\n\t\tif (!hasNew) return;\n\t\tif (!force && this.messageId !== null && this.now() - this.lastFlushAt < this.options.intervalMs) return;\n\t\tconst text = this.preview();\n\t\tif (!text || text === this.lastPreview) return;\n\t\tconst length = this.buffer.length;\n\t\tif (this.messageId === null) {\n\t\t\tthis.messageId = await this.options.sendInitial(text);\n\t\t\tif (this.messageId === null) return; // send failed; retry on the next flush\n\t\t} else {\n\t\t\tawait this.options.edit(this.messageId, text);\n\t\t}\n\t\tthis.flushedLen = length;\n\t\tthis.lastPreview = text;\n\t\tthis.lastFlushAt = this.now();\n\t}\n}\n"]}
@@ -4,9 +4,9 @@
4
4
  * StreamConsumer accumulates assistant text deltas and mirrors them into a
5
5
  * single chat message: the first flush sends a new message, later flushes
6
6
  * edit it. Flushes happen at most every intervalMs and only once at least
7
- * `threshold` new chars have accumulated. finalize() forces a last flush and
8
- * returns the full text — the caller splits THAT via text.ts for final
9
- * delivery; the streaming preview itself is truncated to maxPreview-3 + "…".
7
+ * `threshold` new chars have accumulated. finalize() waits for pending previews;
8
+ * the router owns final delivery. Previews omit silence-marker lines and truncate
9
+ * at maxPreview-3 + "…".
10
10
  *
11
11
  * applySilenceFilter() recognizes the [SILENT] / NO_REPLY markers (whole
12
12
  * response, or first/last line) and returns null when nothing deliverable
@@ -39,6 +39,7 @@ export class StreamConsumer {
39
39
  options;
40
40
  buffer = "";
41
41
  flushedLen = 0;
42
+ lastPreview = "";
42
43
  messageId = null;
43
44
  lastFlushAt = 0;
44
45
  chain = Promise.resolve();
@@ -57,9 +58,8 @@ export class StreamConsumer {
57
58
  this.schedule();
58
59
  }
59
60
  }
60
- /** Force a final flush; resolves with the FULL text (caller splits for delivery). */
61
+ /** Wait for pending previews; final delivery belongs to the router. */
61
62
  async finalize() {
62
- this.schedule(true);
63
63
  await this.chain;
64
64
  return this.buffer;
65
65
  }
@@ -71,16 +71,25 @@ export class StreamConsumer {
71
71
  get truncated() {
72
72
  return this.buffer.length > this.maxPreview;
73
73
  }
74
- /** The streaming preview: full text truncated to maxPreview-3 + "…". */
74
+ /** Preview visible text without a trailing silence-marker fragment. */
75
75
  preview() {
76
- if (this.buffer.length <= this.maxPreview)
77
- return this.buffer;
76
+ const lines = this.buffer.split("\n");
77
+ const last = lines.at(-1)?.trim() ?? "";
78
+ if (SILENCE_MARKERS.has(last) || (last && [...SILENCE_MARKERS].some((marker) => marker.startsWith(last))))
79
+ lines.pop();
80
+ if (lines.length && isMarkerLine(lines[0]))
81
+ lines.shift();
82
+ const visible = lines.join("\n").trim();
83
+ if (!visible)
84
+ return "";
85
+ if (visible.length <= this.maxPreview)
86
+ return visible;
78
87
  let cut = this.maxPreview - 3;
79
88
  // Don't truncate between surrogate halves.
80
- const code = this.buffer.charCodeAt(cut - 1);
89
+ const code = visible.charCodeAt(cut - 1);
81
90
  if (code >= 0xd800 && code <= 0xdbff)
82
91
  cut -= 1;
83
- return `${this.buffer.slice(0, cut)}…`;
92
+ return `${visible.slice(0, cut)}…`;
84
93
  }
85
94
  schedule(force = false) {
86
95
  this.chain = this.chain.then(() => this.flush(force)).catch(() => { });
@@ -92,6 +101,9 @@ export class StreamConsumer {
92
101
  if (!force && this.messageId !== null && this.now() - this.lastFlushAt < this.options.intervalMs)
93
102
  return;
94
103
  const text = this.preview();
104
+ if (!text || text === this.lastPreview)
105
+ return;
106
+ const length = this.buffer.length;
95
107
  if (this.messageId === null) {
96
108
  this.messageId = await this.options.sendInitial(text);
97
109
  if (this.messageId === null)
@@ -100,7 +112,8 @@ export class StreamConsumer {
100
112
  else {
101
113
  await this.options.edit(this.messageId, text);
102
114
  }
103
- this.flushedLen = this.buffer.length;
115
+ this.flushedLen = length;
116
+ this.lastPreview = text;
104
117
  this.lastFlushAt = this.now();
105
118
  }
106
119
  }
@@ -1 +1 @@
1
- {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/gateway/stream.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway streaming — throttled progressive replies.\n *\n * StreamConsumer accumulates assistant text deltas and mirrors them into a\n * single chat message: the first flush sends a new message, later flushes\n * edit it. Flushes happen at most every intervalMs and only once at least\n * `threshold` new chars have accumulated. finalize() forces a last flush and\n * returns the full text — the caller splits THAT via text.ts for final\n * delivery; the streaming preview itself is truncated to maxPreview-3 + \"…\".\n *\n * applySilenceFilter() recognizes the [SILENT] / NO_REPLY markers (whole\n * response, or first/last line) and returns null when nothing deliverable\n * remains.\n */\n\nexport interface StreamConsumerOptions {\n\t/** Send the initial message; resolves with the platform message id (null = send failed). */\n\tsendInitial: (text: string) => Promise<string | null>;\n\tedit: (messageId: string, text: string) => Promise<void>;\n\tintervalMs: number;\n\tthreshold: number;\n\t/** Platform max message length; the streaming preview is truncated to fit. Default 4096. */\n\tmaxPreview?: number;\n\t/** Clock injection for tests. */\n\tnow?: () => number;\n}\n\nconst SILENCE_MARKERS = new Set([\"[SILENT]\", \"NO_REPLY\"]);\n\nfunction isMarkerLine(line: string): boolean {\n\treturn SILENCE_MARKERS.has(line.trim());\n}\n\n/**\n * Null when the text is just a silence marker (whole, first-line, or\n * last-line); otherwise the text with a leading/trailing marker line\n * stripped (null when nothing else remains).\n */\nexport function applySilenceFilter(text: string): string | null {\n\tconst trimmed = text.trim();\n\tif (trimmed.length === 0) return null;\n\tif (isMarkerLine(trimmed)) return null;\n\tconst lines = trimmed.split(\"\\n\");\n\tif (lines.length > 0 && isMarkerLine(lines[0])) lines.shift();\n\tif (lines.length > 0 && isMarkerLine(lines[lines.length - 1])) lines.pop();\n\tconst result = lines.join(\"\\n\").trim();\n\treturn result.length > 0 ? result : null;\n}\n\nexport class StreamConsumer {\n\tprivate readonly options: StreamConsumerOptions;\n\tprivate buffer = \"\";\n\tprivate flushedLen = 0;\n\tprivate messageId: string | null = null;\n\tprivate lastFlushAt = 0;\n\tprivate chain: Promise<void> = Promise.resolve();\n\tprivate readonly maxPreview: number;\n\tprivate readonly now: () => number;\n\n\tconstructor(options: StreamConsumerOptions) {\n\t\tthis.options = options;\n\t\tthis.maxPreview = options.maxPreview ?? 4096;\n\t\tthis.now = options.now ?? (() => Date.now());\n\t}\n\n\t/** Accumulate a delta; schedule a flush when both gates (chars, interval) pass. */\n\tpush(delta: string): void {\n\t\tthis.buffer += delta;\n\t\tif (\n\t\t\tthis.buffer.length - this.flushedLen >= this.options.threshold &&\n\t\t\tthis.now() - this.lastFlushAt >= this.options.intervalMs\n\t\t) {\n\t\t\tthis.schedule();\n\t\t}\n\t}\n\n\t/** Force a final flush; resolves with the FULL text (caller splits for delivery). */\n\tasync finalize(): Promise<string> {\n\t\tthis.schedule(true);\n\t\tawait this.chain;\n\t\treturn this.buffer;\n\t}\n\n\t/** The platform message id of the preview, when one was successfully sent. */\n\tget sentMessageId(): string | null {\n\t\treturn this.messageId;\n\t}\n\n\t/** True when the preview had to truncate the full text (buffer > maxPreview). */\n\tget truncated(): boolean {\n\t\treturn this.buffer.length > this.maxPreview;\n\t}\n\n\t/** The streaming preview: full text truncated to maxPreview-3 + \"…\". */\n\tprivate preview(): string {\n\t\tif (this.buffer.length <= this.maxPreview) return this.buffer;\n\t\tlet cut = this.maxPreview - 3;\n\t\t// Don't truncate between surrogate halves.\n\t\tconst code = this.buffer.charCodeAt(cut - 1);\n\t\tif (code >= 0xd800 && code <= 0xdbff) cut -= 1;\n\t\treturn `${this.buffer.slice(0, cut)}…`;\n\t}\n\n\tprivate schedule(force = false): void {\n\t\tthis.chain = this.chain.then(() => this.flush(force)).catch(() => {});\n\t}\n\n\tprivate async flush(force: boolean): Promise<void> {\n\t\tconst hasNew = this.buffer.length > this.flushedLen;\n\t\tif (!hasNew) return;\n\t\tif (!force && this.messageId !== null && this.now() - this.lastFlushAt < this.options.intervalMs) return;\n\t\tconst text = this.preview();\n\t\tif (this.messageId === null) {\n\t\t\tthis.messageId = await this.options.sendInitial(text);\n\t\t\tif (this.messageId === null) return; // send failed; retry on the next flush\n\t\t} else {\n\t\t\tawait this.options.edit(this.messageId, text);\n\t\t}\n\t\tthis.flushedLen = this.buffer.length;\n\t\tthis.lastFlushAt = this.now();\n\t}\n}\n"]}
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/gateway/stream.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway streaming — throttled progressive replies.\n *\n * StreamConsumer accumulates assistant text deltas and mirrors them into a\n * single chat message: the first flush sends a new message, later flushes\n * edit it. Flushes happen at most every intervalMs and only once at least\n * `threshold` new chars have accumulated. finalize() waits for pending previews;\n * the router owns final delivery. Previews omit silence-marker lines and truncate\n * at maxPreview-3 + \"…\".\n *\n * applySilenceFilter() recognizes the [SILENT] / NO_REPLY markers (whole\n * response, or first/last line) and returns null when nothing deliverable\n * remains.\n */\n\nexport interface StreamConsumerOptions {\n\t/** Send the initial message; resolves with the platform message id (null = send failed). */\n\tsendInitial: (text: string) => Promise<string | null>;\n\tedit: (messageId: string, text: string) => Promise<void>;\n\tintervalMs: number;\n\tthreshold: number;\n\t/** Platform max message length; the streaming preview is truncated to fit. Default 4096. */\n\tmaxPreview?: number;\n\t/** Clock injection for tests. */\n\tnow?: () => number;\n}\n\nconst SILENCE_MARKERS = new Set([\"[SILENT]\", \"NO_REPLY\"]);\n\nfunction isMarkerLine(line: string): boolean {\n\treturn SILENCE_MARKERS.has(line.trim());\n}\n\n/**\n * Null when the text is just a silence marker (whole, first-line, or\n * last-line); otherwise the text with a leading/trailing marker line\n * stripped (null when nothing else remains).\n */\nexport function applySilenceFilter(text: string): string | null {\n\tconst trimmed = text.trim();\n\tif (trimmed.length === 0) return null;\n\tif (isMarkerLine(trimmed)) return null;\n\tconst lines = trimmed.split(\"\\n\");\n\tif (lines.length > 0 && isMarkerLine(lines[0])) lines.shift();\n\tif (lines.length > 0 && isMarkerLine(lines[lines.length - 1])) lines.pop();\n\tconst result = lines.join(\"\\n\").trim();\n\treturn result.length > 0 ? result : null;\n}\n\nexport class StreamConsumer {\n\tprivate readonly options: StreamConsumerOptions;\n\tprivate buffer = \"\";\n\tprivate flushedLen = 0;\n\tprivate lastPreview = \"\";\n\tprivate messageId: string | null = null;\n\tprivate lastFlushAt = 0;\n\tprivate chain: Promise<void> = Promise.resolve();\n\tprivate readonly maxPreview: number;\n\tprivate readonly now: () => number;\n\n\tconstructor(options: StreamConsumerOptions) {\n\t\tthis.options = options;\n\t\tthis.maxPreview = options.maxPreview ?? 4096;\n\t\tthis.now = options.now ?? (() => Date.now());\n\t}\n\n\t/** Accumulate a delta; schedule a flush when both gates (chars, interval) pass. */\n\tpush(delta: string): void {\n\t\tthis.buffer += delta;\n\t\tif (\n\t\t\tthis.buffer.length - this.flushedLen >= this.options.threshold &&\n\t\t\tthis.now() - this.lastFlushAt >= this.options.intervalMs\n\t\t) {\n\t\t\tthis.schedule();\n\t\t}\n\t}\n\n\t/** Wait for pending previews; final delivery belongs to the router. */\n\tasync finalize(): Promise<string> {\n\t\tawait this.chain;\n\t\treturn this.buffer;\n\t}\n\n\t/** The platform message id of the preview, when one was successfully sent. */\n\tget sentMessageId(): string | null {\n\t\treturn this.messageId;\n\t}\n\n\t/** True when the preview had to truncate the full text (buffer > maxPreview). */\n\tget truncated(): boolean {\n\t\treturn this.buffer.length > this.maxPreview;\n\t}\n\n\t/** Preview visible text without a trailing silence-marker fragment. */\n\tprivate preview(): string {\n\t\tconst lines = this.buffer.split(\"\\n\");\n\t\tconst last = lines.at(-1)?.trim() ?? \"\";\n\t\tif (SILENCE_MARKERS.has(last) || (last && [...SILENCE_MARKERS].some((marker) => marker.startsWith(last))))\n\t\t\tlines.pop();\n\t\tif (lines.length && isMarkerLine(lines[0])) lines.shift();\n\t\tconst visible = lines.join(\"\\n\").trim();\n\t\tif (!visible) return \"\";\n\t\tif (visible.length <= this.maxPreview) return visible;\n\t\tlet cut = this.maxPreview - 3;\n\t\t// Don't truncate between surrogate halves.\n\t\tconst code = visible.charCodeAt(cut - 1);\n\t\tif (code >= 0xd800 && code <= 0xdbff) cut -= 1;\n\t\treturn `${visible.slice(0, cut)}…`;\n\t}\n\n\tprivate schedule(force = false): void {\n\t\tthis.chain = this.chain.then(() => this.flush(force)).catch(() => {});\n\t}\n\n\tprivate async flush(force: boolean): Promise<void> {\n\t\tconst hasNew = this.buffer.length > this.flushedLen;\n\t\tif (!hasNew) return;\n\t\tif (!force && this.messageId !== null && this.now() - this.lastFlushAt < this.options.intervalMs) return;\n\t\tconst text = this.preview();\n\t\tif (!text || text === this.lastPreview) return;\n\t\tconst length = this.buffer.length;\n\t\tif (this.messageId === null) {\n\t\t\tthis.messageId = await this.options.sendInitial(text);\n\t\t\tif (this.messageId === null) return; // send failed; retry on the next flush\n\t\t} else {\n\t\t\tawait this.options.edit(this.messageId, text);\n\t\t}\n\t\tthis.flushedLen = length;\n\t\tthis.lastPreview = text;\n\t\tthis.lastFlushAt = this.now();\n\t}\n}\n"]}
@@ -46,6 +46,8 @@ export interface SettingsConfig {
46
46
  globalInstructionsPath: string;
47
47
  modelInstructionsPath: string;
48
48
  confirmLargeSubagentLaunches: boolean;
49
+ computerUse?: boolean;
50
+ computerForeground?: boolean;
49
51
  subagentCommunicationEnabled: boolean;
50
52
  automaticSubagentDelegation: boolean;
51
53
  browserEnabled: boolean;
@@ -115,6 +117,8 @@ export interface SettingsCallbacks {
115
117
  onSubagentCommunicationChange: (enabled: boolean) => void;
116
118
  onAutomaticSubagentDelegationChange: (enabled: boolean) => void;
117
119
  getTierThinkingLevels: (tier: ModelTierName) => ThinkingLevel[];
120
+ onComputerUseChange?: (enabled: boolean) => void;
121
+ onComputerForegroundChange?: (enabled: boolean) => void;
118
122
  onBrowserEnabledChange: (enabled: boolean) => void;
119
123
  onMemoryEnabledChange: (enabled: boolean) => void;
120
124
  onMemoryCharCapChange: (cap: number) => void;