@ashx-j/lunr 0.2.26 → 0.2.27

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 (639) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/LICENSE +22 -0
  3. package/dist/builtin-extensions/ashxj-thinking.d.ts +1 -1
  4. package/dist/builtin-extensions/ashxj-thinking.d.ts.map +1 -1
  5. package/dist/builtin-extensions/ashxj-thinking.js +1 -1
  6. package/dist/builtin-extensions/ashxj-thinking.js.map +1 -1
  7. package/dist/builtin-extensions/ashxj-tui.d.ts +11 -1
  8. package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
  9. package/dist/builtin-extensions/ashxj-tui.js +6 -10
  10. package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
  11. package/dist/builtin-extensions/lunr-cron.d.ts +4 -5
  12. package/dist/builtin-extensions/lunr-cron.d.ts.map +1 -1
  13. package/dist/builtin-extensions/lunr-cron.js +94 -108
  14. package/dist/builtin-extensions/lunr-cron.js.map +1 -1
  15. package/dist/builtin-extensions/lunr-local-providers/local-servers.d.ts.map +1 -1
  16. package/dist/builtin-extensions/lunr-local-providers/local-servers.js +3 -1
  17. package/dist/builtin-extensions/lunr-local-providers/local-servers.js.map +1 -1
  18. package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.d.ts.map +1 -1
  19. package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js +2 -2
  20. package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js.map +1 -1
  21. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.d.ts.map +1 -1
  22. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js +1 -1
  23. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js.map +1 -1
  24. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.d.ts.map +1 -1
  25. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js +1 -1
  26. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js.map +1 -1
  27. package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
  28. package/dist/builtin-extensions/pi-intercom/index.js +2 -2
  29. package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
  30. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts +1 -1
  31. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts.map +1 -1
  32. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js +1 -1
  33. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js.map +1 -1
  34. package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts.map +1 -1
  35. package/dist/builtin-extensions/pi-lsp-extension/src/index.js +1 -15
  36. package/dist/builtin-extensions/pi-lsp-extension/src/index.js.map +1 -1
  37. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts +6 -0
  38. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts.map +1 -1
  39. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js +187 -132
  40. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js.map +1 -1
  41. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts +8 -0
  42. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts.map +1 -1
  43. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js +142 -112
  44. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js.map +1 -1
  45. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts +12 -1
  46. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts.map +1 -1
  47. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js +14 -7
  48. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js.map +1 -1
  49. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts +11 -3
  50. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts.map +1 -1
  51. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js +67 -41
  52. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js.map +1 -1
  53. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts +4 -2
  54. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts.map +1 -1
  55. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js +3 -0
  56. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js.map +1 -1
  57. package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts +2 -1
  58. package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts.map +1 -1
  59. package/dist/builtin-extensions/pi-mcp-adapter/commands.js +6 -6
  60. package/dist/builtin-extensions/pi-mcp-adapter/commands.js.map +1 -1
  61. package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts +1 -1
  62. package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts.map +1 -1
  63. package/dist/builtin-extensions/pi-mcp-adapter/config.js +11 -5
  64. package/dist/builtin-extensions/pi-mcp-adapter/config.js.map +1 -1
  65. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts.map +1 -1
  66. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js +2 -2
  67. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js.map +1 -1
  68. package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.d.ts.map +1 -1
  69. package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.js +1 -1
  70. package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.js.map +1 -1
  71. package/dist/builtin-extensions/pi-mcp-adapter/index.d.ts.map +1 -1
  72. package/dist/builtin-extensions/pi-mcp-adapter/index.js +48 -40
  73. package/dist/builtin-extensions/pi-mcp-adapter/index.js.map +1 -1
  74. package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts.map +1 -1
  75. package/dist/builtin-extensions/pi-mcp-adapter/init.js +3 -1
  76. package/dist/builtin-extensions/pi-mcp-adapter/init.js.map +1 -1
  77. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts +12 -40
  78. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts.map +1 -1
  79. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js +166 -235
  80. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js.map +1 -1
  81. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts +5 -0
  82. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts.map +1 -1
  83. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js +43 -13
  84. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js.map +1 -1
  85. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts +14 -3
  86. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts.map +1 -1
  87. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js +73 -21
  88. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js.map +1 -1
  89. package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.d.ts.map +1 -1
  90. package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js +4 -4
  91. package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js.map +1 -1
  92. package/dist/builtin-extensions/pi-mcp-adapter/server-manager.d.ts.map +1 -1
  93. package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js +2 -2
  94. package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js.map +1 -1
  95. package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts +2 -0
  96. package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts.map +1 -1
  97. package/dist/builtin-extensions/pi-mcp-adapter/state.js.map +1 -1
  98. package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts +1 -1
  99. package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts.map +1 -1
  100. package/dist/builtin-extensions/pi-ollama-cloud/index.js +1 -1
  101. package/dist/builtin-extensions/pi-ollama-cloud/index.js.map +1 -1
  102. package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts +11 -5
  103. package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts.map +1 -1
  104. package/dist/builtin-extensions/pi-ollama-cloud/models.js +12 -32
  105. package/dist/builtin-extensions/pi-ollama-cloud/models.js.map +1 -1
  106. package/dist/builtin-extensions/pi-prompt-template-model/index.d.ts.map +1 -1
  107. package/dist/builtin-extensions/pi-prompt-template-model/index.js +5 -3
  108. package/dist/builtin-extensions/pi-prompt-template-model/index.js.map +1 -1
  109. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts +2 -1
  110. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts.map +1 -1
  111. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js +21 -12
  112. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js.map +1 -1
  113. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts.map +1 -1
  114. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js +7 -24
  115. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js.map +1 -1
  116. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
  117. package/dist/builtin-extensions/pi-subagents/src/extension/index.js +39 -77
  118. package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
  119. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +1 -1
  120. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts.map +1 -1
  121. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js +15 -31
  122. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js.map +1 -1
  123. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts.map +1 -1
  124. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js +7 -2
  125. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js.map +1 -1
  126. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
  127. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +10 -2
  128. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
  129. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
  130. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +8 -1
  131. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
  132. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
  133. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +106 -24
  134. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
  135. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  136. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +25 -5
  137. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
  138. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +1 -1
  139. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
  140. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +1 -1
  141. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
  142. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +7 -8
  143. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
  144. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js +60 -32
  145. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
  146. package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts +1 -1
  147. package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts.map +1 -1
  148. package/dist/builtin-extensions/pi-subagents/src/shared/utils.js +15 -5
  149. package/dist/builtin-extensions/pi-subagents/src/shared/utils.js.map +1 -1
  150. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
  151. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +4 -3
  152. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
  153. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +1 -1
  154. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  155. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +17 -8
  156. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  157. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts.map +1 -1
  158. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js +3 -3
  159. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js.map +1 -1
  160. package/dist/builtin-extensions/pi-tps.d.ts.map +1 -1
  161. package/dist/builtin-extensions/pi-tps.js +20 -17
  162. package/dist/builtin-extensions/pi-tps.js.map +1 -1
  163. package/dist/builtin-extensions/pi-web-access/activity.d.ts +1 -0
  164. package/dist/builtin-extensions/pi-web-access/activity.d.ts.map +1 -1
  165. package/dist/builtin-extensions/pi-web-access/activity.js +27 -5
  166. package/dist/builtin-extensions/pi-web-access/activity.js.map +1 -1
  167. package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts +3 -0
  168. package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts.map +1 -0
  169. package/dist/builtin-extensions/pi-web-access/bounded-body.js +43 -0
  170. package/dist/builtin-extensions/pi-web-access/bounded-body.js.map +1 -0
  171. package/dist/builtin-extensions/pi-web-access/content-page.d.ts +24 -0
  172. package/dist/builtin-extensions/pi-web-access/content-page.d.ts.map +1 -0
  173. package/dist/builtin-extensions/pi-web-access/content-page.js +57 -0
  174. package/dist/builtin-extensions/pi-web-access/content-page.js.map +1 -0
  175. package/dist/builtin-extensions/pi-web-access/extract.d.ts.map +1 -1
  176. package/dist/builtin-extensions/pi-web-access/extract.js +7 -2
  177. package/dist/builtin-extensions/pi-web-access/extract.js.map +1 -1
  178. package/dist/builtin-extensions/pi-web-access/github-extract.d.ts +0 -1
  179. package/dist/builtin-extensions/pi-web-access/github-extract.d.ts.map +1 -1
  180. package/dist/builtin-extensions/pi-web-access/github-extract.js +147 -36
  181. package/dist/builtin-extensions/pi-web-access/github-extract.js.map +1 -1
  182. package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
  183. package/dist/builtin-extensions/pi-web-access/index.js +48 -73
  184. package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
  185. package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts +12 -4
  186. package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts.map +1 -1
  187. package/dist/builtin-extensions/pi-web-access/session-cleanup.js +35 -12
  188. package/dist/builtin-extensions/pi-web-access/session-cleanup.js.map +1 -1
  189. package/dist/builtin-extensions/pi-web-access/storage.d.ts +9 -6
  190. package/dist/builtin-extensions/pi-web-access/storage.d.ts.map +1 -1
  191. package/dist/builtin-extensions/pi-web-access/storage.js +36 -24
  192. package/dist/builtin-extensions/pi-web-access/storage.js.map +1 -1
  193. package/dist/builtin-extensions/simple-pi-memory.d.ts +1 -1
  194. package/dist/builtin-extensions/simple-pi-memory.d.ts.map +1 -1
  195. package/dist/builtin-extensions/simple-pi-memory.js +1 -1
  196. package/dist/builtin-extensions/simple-pi-memory.js.map +1 -1
  197. package/dist/cli/install-cli.d.ts.map +1 -1
  198. package/dist/cli/install-cli.js +38 -30
  199. package/dist/cli/install-cli.js.map +1 -1
  200. package/dist/core/agent-session-runtime.d.ts +13 -0
  201. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  202. package/dist/core/agent-session-runtime.js +75 -11
  203. package/dist/core/agent-session-runtime.js.map +1 -1
  204. package/dist/core/agent-session-services.d.ts +1 -0
  205. package/dist/core/agent-session-services.d.ts.map +1 -1
  206. package/dist/core/agent-session-services.js +1 -0
  207. package/dist/core/agent-session-services.js.map +1 -1
  208. package/dist/core/agent-session.d.ts +40 -6
  209. package/dist/core/agent-session.d.ts.map +1 -1
  210. package/dist/core/agent-session.js +309 -76
  211. package/dist/core/agent-session.js.map +1 -1
  212. package/dist/core/browser/runtime.d.ts +2 -0
  213. package/dist/core/browser/runtime.d.ts.map +1 -1
  214. package/dist/core/browser/runtime.js +80 -45
  215. package/dist/core/browser/runtime.js.map +1 -1
  216. package/dist/core/browser/setup.d.ts.map +1 -1
  217. package/dist/core/browser/setup.js +12 -1
  218. package/dist/core/browser/setup.js.map +1 -1
  219. package/dist/core/computer-use/adapter.d.ts.map +1 -1
  220. package/dist/core/computer-use/adapter.js +7 -2
  221. package/dist/core/computer-use/adapter.js.map +1 -1
  222. package/dist/core/computer-use/image.d.ts.map +1 -1
  223. package/dist/core/computer-use/image.js +17 -5
  224. package/dist/core/computer-use/image.js.map +1 -1
  225. package/dist/core/computer-use/schemas.d.ts.map +1 -1
  226. package/dist/core/computer-use/schemas.js +23 -6
  227. package/dist/core/computer-use/schemas.js.map +1 -1
  228. package/dist/core/computer-use/workflow.d.ts.map +1 -1
  229. package/dist/core/computer-use/workflow.js +459 -119
  230. package/dist/core/computer-use/workflow.js.map +1 -1
  231. package/dist/core/cron/jobs.d.ts +26 -8
  232. package/dist/core/cron/jobs.d.ts.map +1 -1
  233. package/dist/core/cron/jobs.js +357 -232
  234. package/dist/core/cron/jobs.js.map +1 -1
  235. package/dist/core/cron/origin-context.d.ts +1 -0
  236. package/dist/core/cron/origin-context.d.ts.map +1 -1
  237. package/dist/core/cron/origin-context.js.map +1 -1
  238. package/dist/core/cron/scheduler.d.ts +22 -17
  239. package/dist/core/cron/scheduler.d.ts.map +1 -1
  240. package/dist/core/cron/scheduler.js +153 -73
  241. package/dist/core/cron/scheduler.js.map +1 -1
  242. package/dist/core/extensions/index.d.ts +1 -1
  243. package/dist/core/extensions/index.d.ts.map +1 -1
  244. package/dist/core/extensions/index.js.map +1 -1
  245. package/dist/core/extensions/runner.d.ts +1 -0
  246. package/dist/core/extensions/runner.d.ts.map +1 -1
  247. package/dist/core/extensions/runner.js +8 -0
  248. package/dist/core/extensions/runner.js.map +1 -1
  249. package/dist/core/extensions/types.d.ts +8 -0
  250. package/dist/core/extensions/types.d.ts.map +1 -1
  251. package/dist/core/extensions/types.js.map +1 -1
  252. package/dist/core/footer-data-provider.d.ts +4 -0
  253. package/dist/core/footer-data-provider.d.ts.map +1 -1
  254. package/dist/core/footer-data-provider.js +22 -1
  255. package/dist/core/footer-data-provider.js.map +1 -1
  256. package/dist/core/large-subagent-launch.d.ts +1 -1
  257. package/dist/core/large-subagent-launch.d.ts.map +1 -1
  258. package/dist/core/large-subagent-launch.js +7 -7
  259. package/dist/core/large-subagent-launch.js.map +1 -1
  260. package/dist/core/model-registry.d.ts +2 -0
  261. package/dist/core/model-registry.d.ts.map +1 -1
  262. package/dist/core/model-registry.js +5 -0
  263. package/dist/core/model-registry.js.map +1 -1
  264. package/dist/core/model-runtime.d.ts +4 -1
  265. package/dist/core/model-runtime.d.ts.map +1 -1
  266. package/dist/core/model-runtime.js +40 -4
  267. package/dist/core/model-runtime.js.map +1 -1
  268. package/dist/core/package-manager.d.ts.map +1 -1
  269. package/dist/core/package-manager.js.map +1 -1
  270. package/dist/core/permissions.d.ts +3 -3
  271. package/dist/core/permissions.d.ts.map +1 -1
  272. package/dist/core/permissions.js +11 -35
  273. package/dist/core/permissions.js.map +1 -1
  274. package/dist/core/plan-mode.d.ts +5 -13
  275. package/dist/core/plan-mode.d.ts.map +1 -1
  276. package/dist/core/plan-mode.js +126 -116
  277. package/dist/core/plan-mode.js.map +1 -1
  278. package/dist/core/project-trust.d.ts.map +1 -1
  279. package/dist/core/project-trust.js +1 -1
  280. package/dist/core/project-trust.js.map +1 -1
  281. package/dist/core/prompt-resource-bridge.d.ts +12 -0
  282. package/dist/core/prompt-resource-bridge.d.ts.map +1 -0
  283. package/dist/core/prompt-resource-bridge.js +9 -0
  284. package/dist/core/prompt-resource-bridge.js.map +1 -0
  285. package/dist/core/protected-targets.d.ts +6 -0
  286. package/dist/core/protected-targets.d.ts.map +1 -0
  287. package/dist/core/protected-targets.js +45 -0
  288. package/dist/core/protected-targets.js.map +1 -0
  289. package/dist/core/resource-loader.d.ts.map +1 -1
  290. package/dist/core/resource-loader.js +7 -0
  291. package/dist/core/resource-loader.js.map +1 -1
  292. package/dist/core/rollback.d.ts +19 -3
  293. package/dist/core/rollback.d.ts.map +1 -1
  294. package/dist/core/rollback.js +172 -56
  295. package/dist/core/rollback.js.map +1 -1
  296. package/dist/core/runtime-credentials.d.ts +3 -0
  297. package/dist/core/runtime-credentials.d.ts.map +1 -1
  298. package/dist/core/runtime-credentials.js +14 -1
  299. package/dist/core/runtime-credentials.js.map +1 -1
  300. package/dist/core/sdk.d.ts +3 -0
  301. package/dist/core/sdk.d.ts.map +1 -1
  302. package/dist/core/sdk.js +1 -0
  303. package/dist/core/sdk.js.map +1 -1
  304. package/dist/core/session-manager.d.ts +6 -0
  305. package/dist/core/session-manager.d.ts.map +1 -1
  306. package/dist/core/session-manager.js +95 -32
  307. package/dist/core/session-manager.js.map +1 -1
  308. package/dist/core/session-ownership.d.ts.map +1 -1
  309. package/dist/core/session-ownership.js +8 -3
  310. package/dist/core/session-ownership.js.map +1 -1
  311. package/dist/core/session-retention.d.ts.map +1 -1
  312. package/dist/core/session-retention.js +18 -7
  313. package/dist/core/session-retention.js.map +1 -1
  314. package/dist/core/session-startup-settings.d.ts +10 -0
  315. package/dist/core/session-startup-settings.d.ts.map +1 -0
  316. package/dist/core/session-startup-settings.js +70 -0
  317. package/dist/core/session-startup-settings.js.map +1 -0
  318. package/dist/core/subscriptions.d.ts +16 -7
  319. package/dist/core/subscriptions.d.ts.map +1 -1
  320. package/dist/core/subscriptions.js +96 -55
  321. package/dist/core/subscriptions.js.map +1 -1
  322. package/dist/core/trust-manager.d.ts +2 -1
  323. package/dist/core/trust-manager.d.ts.map +1 -1
  324. package/dist/core/trust-manager.js +6 -1
  325. package/dist/core/trust-manager.js.map +1 -1
  326. package/dist/core/update-check.d.ts.map +1 -1
  327. package/dist/core/update-check.js.map +1 -1
  328. package/dist/core/usage-adapters/kimi-coding.d.ts +2 -2
  329. package/dist/core/usage-adapters/kimi-coding.d.ts.map +1 -1
  330. package/dist/core/usage-adapters/kimi-coding.js +2 -2
  331. package/dist/core/usage-adapters/kimi-coding.js.map +1 -1
  332. package/dist/core/usage-adapters/openai-codex.d.ts +3 -2
  333. package/dist/core/usage-adapters/openai-codex.d.ts.map +1 -1
  334. package/dist/core/usage-adapters/openai-codex.js +3 -3
  335. package/dist/core/usage-adapters/openai-codex.js.map +1 -1
  336. package/dist/core/usage-adapters/xai.d.ts +2 -2
  337. package/dist/core/usage-adapters/xai.d.ts.map +1 -1
  338. package/dist/core/usage-adapters/xai.js +3 -3
  339. package/dist/core/usage-adapters/xai.js.map +1 -1
  340. package/dist/core/usage-adapters/zai.d.ts +2 -2
  341. package/dist/core/usage-adapters/zai.d.ts.map +1 -1
  342. package/dist/core/usage-adapters/zai.js +2 -2
  343. package/dist/core/usage-adapters/zai.js.map +1 -1
  344. package/dist/core/usage-service.d.ts +9 -2
  345. package/dist/core/usage-service.d.ts.map +1 -1
  346. package/dist/core/usage-service.js +95 -50
  347. package/dist/core/usage-service.js.map +1 -1
  348. package/dist/gateway/agent-bridge.d.ts +9 -1
  349. package/dist/gateway/agent-bridge.d.ts.map +1 -1
  350. package/dist/gateway/agent-bridge.js +91 -37
  351. package/dist/gateway/agent-bridge.js.map +1 -1
  352. package/dist/gateway/buttons.d.ts +8 -1
  353. package/dist/gateway/buttons.d.ts.map +1 -1
  354. package/dist/gateway/buttons.js +76 -18
  355. package/dist/gateway/buttons.js.map +1 -1
  356. package/dist/gateway/commands.d.ts +1 -0
  357. package/dist/gateway/commands.d.ts.map +1 -1
  358. package/dist/gateway/commands.js +10 -4
  359. package/dist/gateway/commands.js.map +1 -1
  360. package/dist/gateway/cron.d.ts +12 -11
  361. package/dist/gateway/cron.d.ts.map +1 -1
  362. package/dist/gateway/cron.js +65 -56
  363. package/dist/gateway/cron.js.map +1 -1
  364. package/dist/gateway/index.d.ts.map +1 -1
  365. package/dist/gateway/index.js +2 -1
  366. package/dist/gateway/index.js.map +1 -1
  367. package/dist/gateway/mobile-commands.d.ts +1 -0
  368. package/dist/gateway/mobile-commands.d.ts.map +1 -1
  369. package/dist/gateway/mobile-commands.js +56 -14
  370. package/dist/gateway/mobile-commands.js.map +1 -1
  371. package/dist/gateway/presenter.d.ts +12 -2
  372. package/dist/gateway/presenter.d.ts.map +1 -1
  373. package/dist/gateway/presenter.js +73 -38
  374. package/dist/gateway/presenter.js.map +1 -1
  375. package/dist/gateway/router.d.ts +2 -2
  376. package/dist/gateway/router.d.ts.map +1 -1
  377. package/dist/gateway/router.js +17 -2
  378. package/dist/gateway/router.js.map +1 -1
  379. package/dist/gateway/service.d.ts +2 -0
  380. package/dist/gateway/service.d.ts.map +1 -1
  381. package/dist/gateway/service.js +153 -0
  382. package/dist/gateway/service.js.map +1 -1
  383. package/dist/main.d.ts.map +1 -1
  384. package/dist/main.js +86 -33
  385. package/dist/main.js.map +1 -1
  386. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  387. package/dist/modes/interactive/components/assistant-message.js +6 -4
  388. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  389. package/dist/modes/interactive/components/copyable-text.d.ts.map +1 -1
  390. package/dist/modes/interactive/components/copyable-text.js +3 -2
  391. package/dist/modes/interactive/components/copyable-text.js.map +1 -1
  392. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  393. package/dist/modes/interactive/components/extension-editor.js +1 -1
  394. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  395. package/dist/modes/interactive/interactive-mode.d.ts +2 -2
  396. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  397. package/dist/modes/interactive/interactive-mode.js +96 -109
  398. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  399. package/dist/modes/interactive/smooth-streaming.d.ts.map +1 -1
  400. package/dist/modes/interactive/smooth-streaming.js.map +1 -1
  401. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  402. package/dist/modes/rpc/rpc-mode.js +2 -1
  403. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  404. package/dist/node-runtime/{adapter-VFG7WNPX.js → adapter-247RTYLS.js} +4 -5
  405. package/dist/node-runtime/agent-session-services-KDMSPQDY.js +53 -0
  406. package/dist/node-runtime/{brave-UWDA3I22.js → brave-2AFFAXNN.js} +1 -1
  407. package/dist/node-runtime/{builtin-extensions-I2HIFEMY.js → builtin-extensions-OX6KELNX.js} +8 -7
  408. package/dist/node-runtime/{chunk-QH6YL7VK.js → chunk-252ZVPPF.js} +3 -3
  409. package/dist/node-runtime/{chunk-PCEW76PM.js → chunk-2OIPYKLZ.js} +6 -6
  410. package/dist/node-runtime/{chunk-XBWPBZBN.js → chunk-2SWW7KMW.js} +1 -1
  411. package/dist/node-runtime/{chunk-UQUCFAJ3.js → chunk-2VIW3IMF.js} +40 -7
  412. package/dist/node-runtime/{chunk-UOEDI7RP.js → chunk-4KNKQGHM.js} +2 -2
  413. package/dist/node-runtime/{chunk-FQMM2EZ2.js → chunk-64XTQNLA.js} +29 -7
  414. package/dist/node-runtime/{chunk-SPVDBUOS.js → chunk-6O3IW3IA.js} +2 -2
  415. package/dist/node-runtime/{chunk-5ZQXRPMC.js → chunk-6ZZHGUSA.js} +2 -2
  416. package/dist/node-runtime/chunk-A2WO6WIN.js +1197 -0
  417. package/dist/node-runtime/{chunk-WIF24I7G.js → chunk-APVM5NEU.js} +149 -144
  418. package/dist/node-runtime/{chunk-RCR3XAQY.js → chunk-AYHH7YSD.js} +177 -144
  419. package/dist/node-runtime/{chunk-LWYIVUGV.js → chunk-B2IBAXNX.js} +48 -31
  420. package/dist/node-runtime/{chunk-M3LZLQW2.js → chunk-BDRGN3L2.js} +1 -0
  421. package/dist/node-runtime/{chunk-GJ2RDBRL.js → chunk-BYC6RKCN.js} +4 -2
  422. package/dist/node-runtime/{chunk-UVWCDQYF.js → chunk-C3P6Q3B2.js} +455 -278
  423. package/dist/node-runtime/{chunk-SN2KSMPQ.js → chunk-DQSCFU7R.js} +13 -7
  424. package/dist/node-runtime/{chunk-4SBQAI4T.js → chunk-EBLAZVSA.js} +2058 -73
  425. package/dist/node-runtime/{chunk-2KIPT4HJ.js → chunk-EPR2F3GU.js} +68 -20
  426. package/dist/node-runtime/{chunk-CSFI6YXL.js → chunk-ES6PBSIE.js} +1 -1
  427. package/dist/node-runtime/{chunk-IF24A3FH.js → chunk-EVM653DD.js} +4 -4
  428. package/dist/node-runtime/{chunk-HTFRRDSF.js → chunk-F2SMQ6ES.js} +3 -2
  429. package/dist/node-runtime/chunk-F2V2F4ZG.js +114 -0
  430. package/dist/node-runtime/{chunk-IKU6Y7DL.js → chunk-FE3WZI7F.js} +9 -6
  431. package/dist/node-runtime/{chunk-GYIXXQAO.js → chunk-FIRA54AC.js} +2 -5
  432. package/dist/node-runtime/{chunk-6VYZ5UFP.js → chunk-FLGEEH27.js} +1 -1
  433. package/dist/node-runtime/{chunk-5EFIU3OM.js → chunk-FTKEZL27.js} +1 -1
  434. package/dist/node-runtime/{chunk-RGQAEOM5.js → chunk-FYIFPFXE.js} +23 -6
  435. package/dist/node-runtime/{chunk-HMBGROQK.js → chunk-GLDXBBL6.js} +3 -3
  436. package/dist/node-runtime/{chunk-KWA4OC2J.js → chunk-H6YBCWKG.js} +1 -1
  437. package/dist/node-runtime/{chunk-2MI5OPMN.js → chunk-HNNHHJAB.js} +68 -50
  438. package/dist/node-runtime/{chunk-I74LN5JG.js → chunk-HUD2RFJW.js} +2 -2
  439. package/dist/node-runtime/{chunk-I54LGHWM.js → chunk-I2H5QMWW.js} +3 -3
  440. package/dist/node-runtime/{chunk-XHIRAZ26.js → chunk-IAZSK7KK.js} +1 -1
  441. package/dist/node-runtime/{chunk-X3PGBLQ7.js → chunk-IHHTWSXN.js} +9 -9
  442. package/dist/node-runtime/{chunk-DAPBPCBP.js → chunk-IPP5BIV5.js} +25 -3
  443. package/dist/node-runtime/{chunk-QFWG2RVL.js → chunk-IXG6F4NT.js} +11 -0
  444. package/dist/node-runtime/{chunk-RDDAZOMU.js → chunk-JL4HYQXK.js} +1 -1
  445. package/dist/node-runtime/{chunk-CDVHZOOK.js → chunk-JQYNHAKX.js} +1 -1
  446. package/dist/node-runtime/{chunk-L24TPBS4.js → chunk-JWEVT363.js} +37 -29
  447. package/dist/node-runtime/{chunk-2HMIRK7W.js → chunk-LHV3V67B.js} +5 -5
  448. package/dist/node-runtime/{chunk-UAEOTO52.js → chunk-MNQXDFPV.js} +6 -2
  449. package/dist/node-runtime/{chunk-T4DA4XE7.js → chunk-MQPKUHRN.js} +162 -82
  450. package/dist/node-runtime/{chunk-MPKSOJBT.js → chunk-NHATCSOI.js} +4 -7
  451. package/dist/node-runtime/{chunk-RMQZU2YU.js → chunk-NNNR3QIY.js} +1 -1
  452. package/dist/node-runtime/{chunk-RWTKOC3B.js → chunk-QBTJG3UM.js} +1 -1
  453. package/dist/node-runtime/{chunk-P7XBVXGT.js → chunk-QNVMKNNN.js} +18 -14
  454. package/dist/node-runtime/{chunk-GS42T6MX.js → chunk-QW3AAHWN.js} +1 -1
  455. package/dist/node-runtime/{chunk-DVQSHCVP.js → chunk-R4WSB2MQ.js} +5 -5
  456. package/dist/node-runtime/{chunk-B4HP6ZWM.js → chunk-RHCGYWO7.js} +1 -1
  457. package/dist/node-runtime/{chunk-2CFDTH5B.js → chunk-RLGY2QIL.js} +45 -59
  458. package/dist/node-runtime/{chunk-KKUTQQV7.js → chunk-RYRGL7P4.js} +1 -1
  459. package/dist/node-runtime/{chunk-5Q4A37RB.js → chunk-TCT6PCUR.js} +32 -11
  460. package/dist/node-runtime/{chunk-D5UCKPBQ.js → chunk-UBWW2PID.js} +1 -1
  461. package/dist/node-runtime/{chunk-E75WFZ33.js → chunk-UNUZICC4.js} +3 -3
  462. package/dist/node-runtime/{chunk-RAK2QA2B.js → chunk-UYRKUKFE.js} +4 -4
  463. package/dist/node-runtime/{chunk-LWJXTA6Z.js → chunk-VSUEAI7O.js} +1377 -45
  464. package/dist/node-runtime/{chunk-NAY6UUQZ.js → chunk-WDIVLMOH.js} +1 -1
  465. package/dist/node-runtime/{chunk-E24EO34J.js → chunk-WPXUR3HE.js} +1 -1
  466. package/dist/node-runtime/{chunk-SLVOAQOY.js → chunk-WXYNALHD.js} +113 -1
  467. package/dist/node-runtime/{chunk-KJ6DWS3K.js → chunk-XEOCUEUJ.js} +121 -60
  468. package/dist/node-runtime/chunk-YD3IZAZ7.js +47 -0
  469. package/dist/node-runtime/chunk-YSB6URMT.js +41 -0
  470. package/dist/node-runtime/{chunk-LJTIVM7I.js → chunk-ZDJUR45D.js} +427 -126
  471. package/dist/node-runtime/{chunk-YWOJIGFY.js → chunk-ZEVFBFNF.js} +95 -142
  472. package/dist/node-runtime/{chunk-5B6VKFEV.js → chunk-ZIQUS5QO.js} +1 -1
  473. package/dist/node-runtime/cli-runtime.js +4 -4
  474. package/dist/node-runtime/{command-FSBU2EVZ.js → command-3HMSY6W2.js} +5 -5
  475. package/dist/node-runtime/{commands-EMBQQVVT.js → commands-D6A6H4TS.js} +11 -18
  476. package/dist/node-runtime/{commands-NAAL7KNT.js → commands-OCSSEEWJ.js} +16 -16
  477. package/dist/node-runtime/{config-QDD5PUE2.js → config-CU7M7BTK.js} +1 -1
  478. package/dist/node-runtime/{config-D4QUPAHX.js → config-DS7XLMEM.js} +2 -2
  479. package/dist/node-runtime/{direct-tool-executor-4D7UXYU4.js → direct-tool-executor-2QJXVQD2.js} +6 -11
  480. package/dist/node-runtime/{exa-LL64QZR2.js → exa-6VBZNPGE.js} +1 -1
  481. package/dist/node-runtime/{export-html-VM4J2MNR.js → export-html-BRWF5AHI.js} +4 -4
  482. package/dist/node-runtime/{extension-input-E7BJZ35H.js → extension-input-VBBMX7YV.js} +5 -5
  483. package/dist/node-runtime/{extension-selector-WU42XDTU.js → extension-selector-T5AOG5XB.js} +5 -5
  484. package/dist/node-runtime/{extract-EQJE7FZZ.js → extract-BFX2N35A.js} +3 -3
  485. package/dist/node-runtime/{gateway-DQM333YV.js → gateway-6623C2U3.js} +51 -42
  486. package/dist/node-runtime/{gemini-search-35OCRFLN.js → gemini-search-C66QHDT6.js} +2 -2
  487. package/dist/node-runtime/{gemini-url-context-UQY3PHHW.js → gemini-url-context-T7DEK73R.js} +3 -3
  488. package/dist/node-runtime/{github-extract-F64RZOEA.js → github-extract-HOGE3D6F.js} +107 -38
  489. package/dist/node-runtime/{goal-NJEE5CTU.js → goal-AIHEDUYA.js} +5 -5
  490. package/dist/node-runtime/index.js +50 -48
  491. package/dist/node-runtime/{init-P5AUV77I.js → init-JBQJIPYI.js} +3 -6
  492. package/dist/node-runtime/{install-cli-NN4YAU44.js → install-cli-INGHIVBY.js} +30 -25
  493. package/dist/node-runtime/interactive-mode-FM5DNUSW.js +74 -0
  494. package/dist/node-runtime/{interactive-view-NGFAMHPW.js → interactive-view-K33PM4OS.js} +8 -8
  495. package/dist/node-runtime/{lsp-manager-3ASUSO4T.js → lsp-manager-S554V5HR.js} +240 -155
  496. package/dist/node-runtime/{lunr-browser-IR7TL3J7.js → lunr-browser-H4FDOR3C.js} +7 -7
  497. package/dist/node-runtime/{lunr-computer-use-Y23CCDIJ.js → lunr-computer-use-VDP2QNRQ.js} +9 -9
  498. package/dist/node-runtime/{lunr-cron-USHC4LXH.js → lunr-cron-LKVA5CZZ.js} +93 -80
  499. package/dist/node-runtime/main.js +42 -41
  500. package/dist/node-runtime/{mobile-commands-O5LCHL32.js → mobile-commands-XT3NQXLD.js} +12 -12
  501. package/dist/node-runtime/{model-runtime-OO5HFLWE.js → model-runtime-MZ2JMDV4.js} +4 -4
  502. package/dist/node-runtime/{openai-search-B3MSNCQA.js → openai-search-K72LJDYN.js} +1 -1
  503. package/dist/node-runtime/{package-manager-cli-Q7BL6KZO.js → package-manager-cli-CYM262SI.js} +20 -20
  504. package/dist/node-runtime/{parallel-AI2MNSCU.js → parallel-X724NCNU.js} +1 -1
  505. package/dist/node-runtime/{perplexity-QVJW3I35.js → perplexity-RDHQHVMK.js} +2 -2
  506. package/dist/node-runtime/{pi-intercom-QM53RLTS.js → pi-intercom-NHHEPBMI.js} +2 -2
  507. package/dist/node-runtime/{pi-mcp-adapter-3PHF4KZ4.js → pi-mcp-adapter-PP3GQJQ2.js} +98 -94
  508. package/dist/node-runtime/{pi-ollama-cloud-2F3Y2QNM.js → pi-ollama-cloud-4MNWIMFN.js} +11 -24
  509. package/dist/node-runtime/{pi-prompt-template-model-QWTCGLJU.js → pi-prompt-template-model-FTGELLS5.js} +32 -36
  510. package/dist/node-runtime/{pi-subagents-7IBSSGEW.js → pi-subagents-LT3CTO4E.js} +98 -106
  511. package/dist/node-runtime/{pi-web-access-WTGMNZ32.js → pi-web-access-Z3LP246C.js} +144 -108
  512. package/dist/node-runtime/{print-mode-XQ76CYMT.js → print-mode-2XHN5SUA.js} +3 -3
  513. package/dist/node-runtime/{process-registry-4FAKZQOL.js → process-registry-Q4QWIU33.js} +3 -3
  514. package/dist/node-runtime/{proxy-modes-5FAPSZEZ.js → proxy-modes-VEGC6QG5.js} +8 -15
  515. package/dist/node-runtime/rpc-entry.js +42 -41
  516. package/dist/node-runtime/{rpc-mode-O3XV4SID.js → rpc-mode-32DHJPEJ.js} +4 -4
  517. package/dist/node-runtime/{runtime-4VA6JKNF.js → runtime-EDUUGAQD.js} +56 -46
  518. package/dist/node-runtime/{runtime-EFPPD2UW.js → runtime-UGXM4NPQ.js} +2 -2
  519. package/dist/node-runtime/{runtime-bridges-7WVCLI76.js → runtime-bridges-DNKMNJCF.js} +7 -7
  520. package/dist/node-runtime/{service-MDQIPQ26.js → service-IHLGFLBT.js} +6 -4
  521. package/dist/node-runtime/{session-handoff-GWARVEWT.js → session-handoff-LNSPDTKY.js} +4 -4
  522. package/dist/node-runtime/{session-manager-JB5J6XBN.js → session-manager-YIFIAVGC.js} +3 -3
  523. package/dist/node-runtime/{session-selector-GIM4DHQ2.js → session-selector-I73U3EIO.js} +7 -7
  524. package/dist/node-runtime/{settings-manager-DJDM5AHM.js → settings-manager-P2TKGYTR.js} +5 -5
  525. package/dist/node-runtime/{setup-JBKOLJ6F.js → setup-H7CCELIL.js} +15 -1
  526. package/dist/node-runtime/{setup-PFBWRNUI.js → setup-OLQ4GXWS.js} +17 -17
  527. package/dist/node-runtime/{src-IVPNZ2BX.js → src-6RV4PXKJ.js} +64 -36
  528. package/dist/node-runtime/{subagent-executor-LPZ4BHWE.js → subagent-executor-Q5F7DBVM.js} +119 -58
  529. package/dist/node-runtime/{tavily-WEZFVFP7.js → tavily-KZBFYQB6.js} +1 -1
  530. package/dist/node-runtime/{theme-UOO5RVFN.js → theme-YLVZKKZD.js} +2 -2
  531. package/dist/node-runtime/{trust-manager-MMISNCYO.js → trust-manager-ZCU76D23.js} +2 -2
  532. package/dist/node-runtime/{update-cli-HNMOTNGX.js → update-cli-F3XXJANQ.js} +6 -6
  533. package/dist/node-runtime/{usage-service-LFB2SWYV.js → usage-service-CPAGBED5.js} +1 -1
  534. package/dist/node-runtime/{video-extract-OOZHR7SU.js → video-extract-UPKYIBW3.js} +3 -3
  535. package/dist/node-runtime/{workflow-OHPBEFO4.js → workflow-ICDI4N3J.js} +208 -78
  536. package/dist/node-runtime/{youtube-extract-FNPAUNHU.js → youtube-extract-77NT7FWH.js} +4 -4
  537. package/dist/utils/clipboard-image.d.ts.map +1 -1
  538. package/dist/utils/clipboard-image.js.map +1 -1
  539. package/dist/utils/exif-orientation.d.ts.map +1 -1
  540. package/dist/utils/exif-orientation.js +25 -10
  541. package/dist/utils/exif-orientation.js.map +1 -1
  542. package/dist/utils/image-resize-core.d.ts.map +1 -1
  543. package/dist/utils/image-resize-core.js +2 -0
  544. package/dist/utils/image-resize-core.js.map +1 -1
  545. package/dist/utils/paths.d.ts +4 -0
  546. package/dist/utils/paths.d.ts.map +1 -1
  547. package/dist/utils/paths.js +33 -2
  548. package/dist/utils/paths.js.map +1 -1
  549. package/dist/utils/sanitize-terminal-text.d.ts +7 -0
  550. package/dist/utils/sanitize-terminal-text.d.ts.map +1 -0
  551. package/dist/utils/sanitize-terminal-text.js +67 -0
  552. package/dist/utils/sanitize-terminal-text.js.map +1 -0
  553. package/docs/browser.md +1 -1
  554. package/docs/custom-provider.md +3 -3
  555. package/docs/extensions.md +8 -4
  556. package/docs/features.md +15 -7
  557. package/docs/keybindings.md +1 -1
  558. package/docs/models.md +5 -5
  559. package/docs/packages.md +1 -1
  560. package/docs/providers.md +2 -2
  561. package/docs/rpc.md +7 -7
  562. package/docs/security.md +10 -5
  563. package/docs/sessions.md +2 -2
  564. package/docs/skills.md +1 -1
  565. package/docs/terminal-setup.md +7 -7
  566. package/docs/themes.md +1 -1
  567. package/docs/usage.md +1 -1
  568. package/examples/extensions/auto-commit-on-exit.ts +1 -1
  569. package/examples/extensions/bash-spawn-hook.ts +1 -1
  570. package/examples/extensions/built-in-tool-renderer.ts +1 -1
  571. package/examples/extensions/claude-rules.ts +1 -1
  572. package/examples/extensions/commands.ts +1 -1
  573. package/examples/extensions/custom-compaction.ts +1 -1
  574. package/examples/extensions/custom-provider-anthropic/index.ts +3 -3
  575. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  576. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  577. package/examples/extensions/custom-provider-gitlab-duo/index.ts +1 -1
  578. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  579. package/examples/extensions/doom-overlay/index.ts +1 -1
  580. package/examples/extensions/git-merge-and-resolve.ts +2 -2
  581. package/examples/extensions/gondolin/index.ts +1 -1
  582. package/examples/extensions/gondolin/package-lock.json +2 -2
  583. package/examples/extensions/gondolin/package.json +1 -1
  584. package/examples/extensions/hidden-thinking-label.ts +1 -1
  585. package/examples/extensions/inline-bash.ts +2 -2
  586. package/examples/extensions/input-transform-streaming.ts +2 -2
  587. package/examples/extensions/input-transform.ts +2 -2
  588. package/examples/extensions/interactive-shell.ts +1 -1
  589. package/examples/extensions/kimi-deferred-tools.ts +1 -1
  590. package/examples/extensions/mac-system-theme.ts +1 -1
  591. package/examples/extensions/minimal-mode.ts +1 -1
  592. package/examples/extensions/modal-editor.ts +1 -1
  593. package/examples/extensions/model-status.ts +1 -1
  594. package/examples/extensions/notify.ts +3 -3
  595. package/examples/extensions/overlay-qa-tests.ts +1 -1
  596. package/examples/extensions/overlay-test.ts +1 -1
  597. package/examples/extensions/pirate.ts +1 -1
  598. package/examples/extensions/preset.ts +3 -3
  599. package/examples/extensions/project-trust.ts +4 -4
  600. package/examples/extensions/prompt-customizer.ts +1 -1
  601. package/examples/extensions/rainbow-editor.ts +1 -1
  602. package/examples/extensions/rpc-demo.ts +1 -1
  603. package/examples/extensions/sandbox/index.ts +7 -7
  604. package/examples/extensions/sandbox/package-lock.json +2 -2
  605. package/examples/extensions/sandbox/package.json +1 -1
  606. package/examples/extensions/shutdown-command.ts +5 -5
  607. package/examples/extensions/ssh.ts +2 -2
  608. package/examples/extensions/subagent/README.md +3 -3
  609. package/examples/extensions/subagent/index.ts +2 -2
  610. package/examples/extensions/titlebar-spinner.ts +1 -1
  611. package/examples/extensions/tool-override.ts +1 -1
  612. package/examples/extensions/tools.ts +1 -1
  613. package/examples/extensions/with-deps/package-lock.json +2 -2
  614. package/examples/extensions/with-deps/package.json +1 -1
  615. package/examples/extensions/working-indicator.ts +1 -1
  616. package/examples/extensions/working-message-test.ts +1 -1
  617. package/examples/sdk/01-minimal.ts +1 -1
  618. package/examples/sdk/03-custom-prompt.ts +1 -1
  619. package/examples/sdk/04-skills.ts +1 -1
  620. package/examples/sdk/06-extensions.ts +2 -2
  621. package/examples/sdk/08-prompt-templates.ts +1 -1
  622. package/install-lock/package-lock.json +24 -24
  623. package/install-lock/package.json +2 -2
  624. package/npm-shrinkwrap.json +60 -24
  625. package/package.json +9 -9
  626. package/dist/modes/interactive/components/exit-card.d.ts +0 -22
  627. package/dist/modes/interactive/components/exit-card.d.ts.map +0 -1
  628. package/dist/modes/interactive/components/exit-card.js +0 -69
  629. package/dist/modes/interactive/components/exit-card.js.map +0 -1
  630. package/dist/node-runtime/agent-session-services-5YXS2PLF.js +0 -51
  631. package/dist/node-runtime/chunk-2IDTZAG5.js +0 -1415
  632. package/dist/node-runtime/chunk-3MISGG4S.js +0 -769
  633. package/dist/node-runtime/chunk-4HXHDDCF.js +0 -16
  634. package/dist/node-runtime/chunk-5B5QN3ZQ.js +0 -356
  635. package/dist/node-runtime/chunk-7IQGBKBJ.js +0 -2033
  636. package/dist/node-runtime/chunk-AUXVGBO4.js +0 -19
  637. package/dist/node-runtime/chunk-PTFBPO3C.js +0 -50
  638. package/dist/node-runtime/interactive-mode-YOHZVMKX.js +0 -74
  639. package/dist/node-runtime/mcp-auth-flow-QVLFVLEO.js +0 -33
@@ -1 +1 @@
1
- {"version":3,"file":"presenter.d.ts","sourceRoot":"","sources":["../../src/gateway/presenter.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAIhG,OAAO,EAAE,KAAK,aAAa,EAAwC,MAAM,aAAa,CAAC;AAKvF,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAe,MAAM,YAAY,CAAC;AAoC7E,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,GAAG,IAAI,CAIrF;AAUD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;CAAE,CAM5G;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAGzF;AAED,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,IAAI,CA2BnF;AAED,wBAAgB,oBAAoB,IAAI,IAAI,CAO3C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAsB,iBAAiB,CACtC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,QAAQ,GAAG,QAAmB,EACpC,KAAK,SAAoB,GACvB,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,gBAAgB,CACrC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,EAC9B,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,aAAa,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9G,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAElD;AAsED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAU1D;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAM5E;AAqCD,wBAAgB,aAAa,CAC5B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,CAAC,EAAE,wBAAwB,GAC7B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuB7B;AAED,wBAAgB,YAAY,CAC3B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,wBAAwB,GAC7B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAa7B;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,CA4B/D","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { noOpUIContext } from \"../core/extensions/runner.ts\";\nimport type { ExtensionUIContext, ExtensionUIDialogOptions } from \"../core/extensions/types.ts\";\nimport { runWithApprovalContext } from \"./approval.ts\";\nimport { isAuthorized } from \"./authz.ts\";\nimport { createPicker } from \"./buttons.ts\";\nimport { type GatewayConfig, gatewayConfigPath, loadGatewayConfig } from \"./config.ts\";\nimport { conversationBinding } from \"./conversations.ts\";\nimport { createPairingStore } from \"./pairing.ts\";\nimport { atomicJson } from \"./service.ts\";\nimport { splitMessage } from \"./text.ts\";\nimport type { MessageEvent, PlatformAdapter, SendOptions } from \"./types.ts\";\n\nconst epochs = new Map<string, number>();\nconst inputs = new Map<string, { userId: string; resolve: (value: string | undefined) => void }>();\nconst dialogs = new Map<string, Set<() => void>>();\nlet adapters = new Map<string, PlatformAdapter>();\nconst deliveryConfigs = new Map<string, GatewayConfig>();\nconst roleGrants = new Map<string, { userId: string; chatId: string; threadId?: string }>();\nlet timer: ReturnType<typeof setInterval> | undefined;\nconst activeDeliveries = new Map<string, Promise<void>>();\ninterface Outbound {\n\tid: string;\n\tkey: string;\n\tepoch: number;\n\ttext: string;\n\tsource?: MessageEvent[\"source\"];\n\treplyTo?: string;\n\tkind?: \"result\" | \"notice\";\n\tattempts?: number;\n\tnextAttempt?: number;\n\tfailed?: boolean;\n\tbatchId?: string;\n}\nconst MAX_ATTEMPTS = 5;\nconst path = () => join(getAgentDir(), \"gateway-outbox.json\");\nlet outbox: Outbound[] = [];\n\nfunction sourceWithCurrentRole(key: string, source: MessageEvent[\"source\"]): MessageEvent[\"source\"] {\n\tconst grant = roleGrants.get(key);\n\treturn {\n\t\t...source,\n\t\troleAuthorized:\n\t\t\tgrant?.userId === source.userId && grant.chatId === source.chatId && grant.threadId === source.threadId,\n\t};\n}\n\nexport function rememberGatewayRole(key: string, source: MessageEvent[\"source\"]): void {\n\tif (source.roleAuthorized === true)\n\t\troleGrants.set(key, { userId: source.userId, chatId: source.chatId, threadId: source.threadId });\n\telse roleGrants.delete(key);\n}\n\nfunction canDeliver(key: string): boolean {\n\tconst binding = conversationBinding(key);\n\tif (!binding) return false;\n\tconst cfg = loadGatewayConfig();\n\tif (binding.owner && binding.owner !== binding.source.userId) return false;\n\treturn isAuthorized(sourceWithCurrentRole(key, binding.source), cfg, createPairingStore());\n}\n\nexport function gatewayDestination(key: string): { adapter: PlatformAdapter; source: MessageEvent[\"source\"] } {\n\tconst binding = conversationBinding(key);\n\tconst adapter = binding && adapters.get(binding.source.platform);\n\tif (!binding || !adapter || !canDeliver(key))\n\t\tthrow new Error(\"This chat is no longer authorized or its platform is disconnected.\");\n\treturn { adapter, source: binding.source };\n}\n\nexport function withGatewayPresentation<T>(key: string, run: () => Promise<T>): Promise<T> {\n\tconst { adapter, source } = gatewayDestination(key);\n\treturn runWithApprovalContext({ adapter, source, key }, run);\n}\n\nexport function startGatewayPresenter(connected: Map<string, PlatformAdapter>): void {\n\tadapters = connected;\n\tif (existsSync(path())) {\n\t\ttry {\n\t\t\tconst raw: unknown = JSON.parse(readFileSync(path(), \"utf8\"));\n\t\t\tif (\n\t\t\t\t!Array.isArray(raw) ||\n\t\t\t\t!raw.every(\n\t\t\t\t\t(item) =>\n\t\t\t\t\t\titem && typeof item.id === \"string\" && typeof item.key === \"string\" && typeof item.text === \"string\",\n\t\t\t\t)\n\t\t\t)\n\t\t\t\tthrow new Error(\"Invalid outbox\");\n\t\t\toutbox = raw as Outbound[];\n\t\t} catch {\n\t\t\tthrow new Error(\"Gateway outbox is invalid. Restore it before starting.\");\n\t\t}\n\t} else outbox = [];\n\tfor (const item of outbox) {\n\t\tif (item.kind === \"notice\") epochs.set(item.key, Math.max(epochs.get(item.key) ?? 0, item.epoch ?? 0));\n\t}\n\tif (timer) clearInterval(timer);\n\ttimer = setInterval(() => {\n\t\tvoid flushOutbox().catch((error) => console.error(\"[gateway] outbox flush failed:\", error));\n\t}, 3000);\n\ttimer.unref();\n\tvoid flushOutbox().catch((error) => console.error(\"[gateway] outbox flush failed:\", error));\n}\n\nexport function stopGatewayPresenter(): void {\n\tif (timer) clearInterval(timer);\n\ttimer = undefined;\n\tfor (const key of dialogs.keys()) invalidateGatewayDialogs(key);\n\tadapters = new Map();\n\tdeliveryConfigs.clear();\n\troleGrants.clear();\n}\n\nexport function gatewayEpoch(key: string): number {\n\treturn epochs.get(key) ?? 0;\n}\n\nexport async function sendGatewayNotice(\n\tkey: string,\n\ttext: string,\n\tkind: \"notice\" | \"result\" = \"notice\",\n\tepoch = gatewayEpoch(key),\n): Promise<void> {\n\tconst clean = text.replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, \"\").trim();\n\tif (!clean || !canDeliver(key)) return;\n\tconst { adapter, source } = gatewayDestination(key);\n\tawait queueGatewayText(key, source, adapter, clean, { kind, epoch });\n}\n\nexport async function queueGatewayText(\n\tkey: string,\n\tsource: MessageEvent[\"source\"],\n\tadapter: PlatformAdapter,\n\ttext: string,\n\toptions: { kind: \"result\" | \"notice\"; replyTo?: string; cfg?: GatewayConfig; chunks?: string[]; epoch?: number },\n): Promise<void> {\n\tif (!text || (options.kind === \"notice\" && options.epoch !== undefined && options.epoch !== gatewayEpoch(key)))\n\t\treturn;\n\tadapters.set(source.platform, adapter);\n\tif (options.cfg) deliveryConfigs.set(key, options.cfg);\n\tconst chunks = options.chunks ?? splitMessage(text, adapter.maxMessageLength);\n\tconst batchId = randomUUID();\n\tfor (const [index, chunk] of chunks.entries())\n\t\toutbox.push({\n\t\t\tid: randomUUID(),\n\t\t\tbatchId,\n\t\t\tkey,\n\t\t\tsource: { ...source, roleAuthorized: undefined },\n\t\t\tepoch: options.epoch ?? gatewayEpoch(key),\n\t\t\ttext: chunk,\n\t\t\tkind: options.kind,\n\t\t\treplyTo: index === 0 ? options.replyTo : undefined,\n\t\t});\n\tatomicJson(path(), outbox);\n\tif (activeDeliveries.has(key)) await activeDeliveries.get(key);\n\tawait flushKey(key);\n}\n\nexport function flushGatewayOutbox(): Promise<void> {\n\treturn flushOutbox();\n}\n\nfunction flushOutbox(): Promise<void> {\n\treturn Promise.all([...new Set(outbox.map((item) => item.key))].map(flushKey)).then(() => {});\n}\n\nfunction flushKey(key: string): Promise<void> {\n\tconst current = activeDeliveries.get(key);\n\tif (current) return current;\n\tconst running = drainKey(key).finally(() => {\n\t\tactiveDeliveries.delete(key);\n\t});\n\tactiveDeliveries.set(key, running);\n\treturn running;\n}\n\nasync function drainKey(key: string): Promise<void> {\n\tconst failedBatches = new Set(outbox.filter((item) => item.failed && item.batchId).map((item) => item.batchId));\n\tfor (const item of [...outbox].filter((item) => item.key === key)) {\n\t\tif (item.failed || (item.batchId && failedBatches.has(item.batchId))) continue;\n\t\tif (item.kind === \"notice\" && item.epoch !== (epochs.get(item.key) ?? 0)) {\n\t\t\toutbox = outbox.filter((v) => v.id !== item.id);\n\t\t\tatomicJson(path(), outbox);\n\t\t\tcontinue;\n\t\t}\n\t\tconst binding = conversationBinding(item.key);\n\t\tconst source = item.source ?? binding?.source;\n\t\tif (!source) break;\n\t\tconst cfg = existsSync(gatewayConfigPath())\n\t\t\t? loadGatewayConfig()\n\t\t\t: (deliveryConfigs.get(item.key) ?? loadGatewayConfig());\n\t\tif (\n\t\t\t(binding &&\n\t\t\t\t(binding.source.platform !== source.platform ||\n\t\t\t\t\tbinding.source.chatId !== source.chatId ||\n\t\t\t\t\tbinding.source.userId !== source.userId ||\n\t\t\t\t\tbinding.source.threadId !== source.threadId)) ||\n\t\t\t!isAuthorized(sourceWithCurrentRole(key, source), cfg, createPairingStore()) ||\n\t\t\t(binding?.owner && binding.owner !== source.userId)\n\t\t) {\n\t\t\toutbox = outbox.filter((v) => v.id !== item.id);\n\t\t\tatomicJson(path(), outbox);\n\t\t\tcontinue;\n\t\t}\n\t\tconst adapter = adapters.get(source.platform);\n\t\tif (!adapter || (item.nextAttempt ?? 0) > Date.now()) break;\n\t\tconst opts: SendOptions = { threadId: source.threadId, replyTo: item.replyTo };\n\t\tconst result = await adapter\n\t\t\t.send(source.chatId, item.text, opts)\n\t\t\t.catch((error: unknown) => ({ success: false as const, error: String(error), retryable: true }));\n\t\tif (result.success) {\n\t\t\toutbox = outbox.filter((v) => v.id !== item.id);\n\t\t\tatomicJson(path(), outbox);\n\t\t} else {\n\t\t\titem.attempts = (item.attempts ?? 0) + 1;\n\t\t\titem.failed = result.retryable === false || item.attempts >= MAX_ATTEMPTS;\n\t\t\titem.nextAttempt = Date.now() + Math.min(60_000, 3000 * 2 ** (item.attempts - 1));\n\t\t\tif (item.failed && item.batchId) {\n\t\t\t\tfailedBatches.add(item.batchId);\n\t\t\t\tfor (const pending of outbox) if (pending.batchId === item.batchId) pending.failed = true;\n\t\t\t}\n\t\t\tatomicJson(path(), outbox);\n\t\t\tconsole.error(\n\t\t\t\t`[gateway] delivery ${item.failed ? \"failed permanently\" : \"retry scheduled\"} for ${source.platform}: ${result.error ?? \"unknown error\"}`,\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nexport function invalidateGatewayDialogs(key: string): void {\n\tepochs.set(key, (epochs.get(key) ?? 0) + 1);\n\tconst remaining = outbox.filter((item) => item.key !== key || item.kind !== \"notice\");\n\tif (remaining.length !== outbox.length) {\n\t\toutbox = remaining;\n\t\tatomicJson(path(), outbox);\n\t}\n\tfor (const cancel of dialogs.get(key) ?? []) cancel();\n\tdialogs.delete(key);\n\tinputs.delete(key);\n}\n\nexport function acceptGatewayInput(key: string, event: MessageEvent): boolean {\n\tconst input = inputs.get(key);\n\tif (!input || input.userId !== event.source.userId || !canDeliver(key)) return false;\n\tif (event.text.startsWith(\"/\") && event.text !== \"/cancel\") return false;\n\tinput.resolve(event.text === \"/cancel\" ? undefined : event.text);\n\treturn true;\n}\n\nfunction dialog(\n\tkey: string,\n\topts: ExtensionUIDialogOptions | undefined,\n\tshow: (done: (value: string | undefined) => void, valid: () => boolean) => Promise<void>,\n): Promise<string | undefined> {\n\tconst epoch = epochs.get(key) ?? 0;\n\treturn new Promise((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst finish = (value: string | undefined, error?: unknown) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timeout);\n\t\t\topts?.signal?.removeEventListener(\"abort\", cancel);\n\t\t\tinputs.delete(key);\n\t\t\tdialogs.get(key)?.delete(cancel);\n\t\t\tif (error !== undefined) reject(error);\n\t\t\telse resolve(value);\n\t\t};\n\t\tconst done = (value: string | undefined) => finish(value);\n\t\tconst cancel = () => done(undefined);\n\t\tconst timeout = setTimeout(cancel, opts?.timeout ?? 300_000);\n\t\tconst set = dialogs.get(key) ?? new Set();\n\t\tset.add(cancel);\n\t\tdialogs.set(key, set);\n\t\topts?.signal?.addEventListener(\"abort\", cancel, { once: true });\n\t\tif (opts?.signal?.aborted) {\n\t\t\tcancel();\n\t\t\treturn;\n\t\t}\n\t\tvoid show(done, () => !settled && (epochs.get(key) ?? 0) === epoch && canDeliver(key)).catch((error) => {\n\t\t\tfinish(undefined, error);\n\t\t});\n\t});\n}\n\nexport function gatewaySelect(\n\tkey: string,\n\ttitle: string,\n\toptions: string[],\n\topts?: ExtensionUIDialogOptions,\n): Promise<string | undefined> {\n\treturn dialog(key, opts, async (done, valid) => {\n\t\tconst { adapter, source } = gatewayDestination(key);\n\t\tconst result = await createPicker(\n\t\t\tadapter,\n\t\t\tsource,\n\t\t\t{\n\t\t\t\tkind: \"dialog\",\n\t\t\t\tsessionKey: key,\n\t\t\t\tinvokerId: source.userId,\n\t\t\t\ttitle,\n\t\t\t\titems: options.map((value) => ({ label: value, value })),\n\t\t\t\tvalidate: valid,\n\t\t\t\tonCancel: () => done(undefined),\n\t\t\t\tresolve: async (item) => {\n\t\t\t\t\tdone(item.value);\n\t\t\t\t\treturn { done: true, text: `${title}\\n${item.value}` };\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ threadId: source.threadId },\n\t\t);\n\t\tif (!result.success) throw new Error(result.error ?? \"Could not send picker.\");\n\t});\n}\n\nexport function gatewayInput(\n\tkey: string,\n\ttitle: string,\n\tplaceholder?: string,\n\topts?: ExtensionUIDialogOptions,\n): Promise<string | undefined> {\n\tif (inputs.has(key)) return Promise.reject(new Error(\"Answer or cancel the existing question first.\"));\n\tconst epoch = gatewayEpoch(key);\n\treturn dialog(key, opts, async (done) => {\n\t\tconst { source } = gatewayDestination(key);\n\t\tinputs.set(key, { userId: source.userId, resolve: done });\n\t\tawait sendGatewayNotice(\n\t\t\tkey,\n\t\t\t`${title}${placeholder ? `\\n${placeholder}` : \"\"}\\nReply with text, or /cancel.`,\n\t\t\t\"notice\",\n\t\t\tepoch,\n\t\t);\n\t});\n}\n\nexport function createGatewayUI(key: string): ExtensionUIContext {\n\tconst epoch = gatewayEpoch(key);\n\treturn {\n\t\t...noOpUIContext,\n\t\tselect: (title, options, opts) =>\n\t\t\tgatewayEpoch(key) === epoch ? gatewaySelect(key, title, options, opts) : Promise.resolve(undefined),\n\t\tconfirm: async (title, message, opts) =>\n\t\t\tgatewayEpoch(key) === epoch &&\n\t\t\t(await gatewaySelect(key, `${title}\\n${message}`, [\"Approve\", \"Decline\"], opts)) === \"Approve\",\n\t\tinput: (title, placeholder, opts) =>\n\t\t\tgatewayEpoch(key) === epoch ? gatewayInput(key, title, placeholder, opts) : Promise.resolve(undefined),\n\t\teditor: (title, prefill) =>\n\t\t\tgatewayEpoch(key) === epoch ? gatewayInput(key, title, prefill) : Promise.resolve(undefined),\n\t\tnotify: (message) => {\n\t\t\tvoid sendGatewayNotice(key, message, \"notice\", epoch).catch(() => {});\n\t\t},\n\t\tcustom: async () => {\n\t\t\tthrow new Error(\n\t\t\t\t\"This command requires the terminal interface. Use the gateway's text commands or button pickers.\",\n\t\t\t);\n\t\t},\n\t\tpasteToEditor: (text) => {\n\t\t\tvoid sendGatewayNotice(key, text, \"notice\", epoch).catch(() => {});\n\t\t},\n\t\tsetEditorText: (text) => {\n\t\t\tvoid sendGatewayNotice(key, text, \"notice\", epoch).catch(() => {});\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"presenter.d.ts","sourceRoot":"","sources":["../../src/gateway/presenter.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { noOpUIContext } from \"../core/extensions/runner.ts\";\nimport type { ExtensionUIContext, ExtensionUIDialogOptions } from \"../core/extensions/types.ts\";\nimport { runWithApprovalContext } from \"./approval.ts\";\nimport { isAuthorized } from \"./authz.ts\";\nimport { createPicker, gatewayPickerGeneration, invalidateGatewayPickers } from \"./buttons.ts\";\nimport { type GatewayConfig, gatewayConfigPath, loadGatewayConfig } from \"./config.ts\";\nimport { conversationBinding } from \"./conversations.ts\";\nimport { createPairingStore, type PairingStore } from \"./pairing.ts\";\nimport { atomicJson } from \"./service.ts\";\nimport { splitMessage } from \"./text.ts\";\nimport type { MessageEvent, PlatformAdapter, SendOptions } from \"./types.ts\";\n\nconst epochs = new Map<string, number>();\nconst sessionIdentities = new Map<string, object>();\n\nfunction sessionIdentity(key: string): object {\n\tlet identity = sessionIdentities.get(key);\n\tif (!identity) {\n\t\tidentity = {};\n\t\tsessionIdentities.set(key, identity);\n\t}\n\treturn identity;\n}\n\n/** Retire session UI independently of cancelling its current dialogs. Roll back a failed replacement. */\nexport function invalidateGatewaySession(key: string): () => void {\n\tconst previous = sessionIdentity(key);\n\tconst next = {};\n\tsessionIdentities.set(key, next);\n\tinvalidateGatewayDialogs(key);\n\treturn () => {\n\t\tif (sessionIdentities.get(key) === next) {\n\t\t\tinvalidateGatewayDialogs(key);\n\t\t\tsessionIdentities.set(key, previous);\n\t\t}\n\t};\n}\nconst inputs = new Map<string, { userId: string; resolve: (value: string | undefined) => void }>();\nconst dialogs = new Map<string, Set<() => void>>();\nlet adapters = new Map<string, PlatformAdapter>();\nconst deliveryConfigs = new Map<string, GatewayConfig>();\nconst roleGrants = new Map<string, { userId: string; chatId: string; threadId?: string }>();\nlet timer: ReturnType<typeof setInterval> | undefined;\nconst activeDeliveries = new Map<string, Promise<void>>();\ninterface Outbound {\n\tid: string;\n\tkey: string;\n\tepoch: number;\n\ttext: string;\n\tsource?: MessageEvent[\"source\"];\n\treplyTo?: string;\n\tkind?: \"result\" | \"notice\";\n\tattempts?: number;\n\tnextAttempt?: number;\n\tfailed?: boolean;\n\tbatchId?: string;\n}\nconst MAX_ATTEMPTS = 5;\nconst path = () => join(getAgentDir(), \"gateway-outbox.json\");\nlet outbox: Outbound[] = [];\n\nfunction sourceWithCurrentRole(key: string, source: MessageEvent[\"source\"]): MessageEvent[\"source\"] {\n\tconst grant = roleGrants.get(key);\n\treturn {\n\t\t...source,\n\t\troleAuthorized:\n\t\t\tgrant?.userId === source.userId && grant.chatId === source.chatId && grant.threadId === source.threadId,\n\t};\n}\n\nexport function rememberGatewayRole(key: string, source: MessageEvent[\"source\"]): void {\n\tif (source.roleAuthorized === true)\n\t\troleGrants.set(key, { userId: source.userId, chatId: source.chatId, threadId: source.threadId });\n\telse roleGrants.delete(key);\n}\n\n/** Fresh delivery authorization shared by previews, final edits and durable retries. */\nexport function canDeliverGatewayText(\n\tkey: string,\n\tsource: MessageEvent[\"source\"],\n\tfallbackConfig?: GatewayConfig,\n\tpairing: PairingStore = createPairingStore(),\n): boolean {\n\tconst binding = conversationBinding(key);\n\tconst cfg = existsSync(gatewayConfigPath()) ? loadGatewayConfig() : (fallbackConfig ?? loadGatewayConfig());\n\treturn (\n\t\t(!binding ||\n\t\t\t(binding.source.platform === source.platform &&\n\t\t\t\tbinding.source.chatId === source.chatId &&\n\t\t\t\tbinding.source.userId === source.userId &&\n\t\t\t\tbinding.source.threadId === source.threadId &&\n\t\t\t\t(!binding.owner || binding.owner === source.userId))) &&\n\t\tisAuthorized(sourceWithCurrentRole(key, source), cfg, pairing)\n\t);\n}\n\nfunction canDeliver(key: string): boolean {\n\tconst binding = conversationBinding(key);\n\treturn !!binding && canDeliverGatewayText(key, binding.source);\n}\n\nexport function gatewayDestination(key: string): { adapter: PlatformAdapter; source: MessageEvent[\"source\"] } {\n\tconst binding = conversationBinding(key);\n\tconst adapter = binding && adapters.get(binding.source.platform);\n\tif (!binding || !adapter || !canDeliver(key))\n\t\tthrow new Error(\"This chat is no longer authorized or its platform is disconnected.\");\n\treturn { adapter, source: binding.source };\n}\n\nexport function withGatewayPresentation<T>(key: string, run: () => Promise<T>): Promise<T> {\n\tconst { adapter, source } = gatewayDestination(key);\n\treturn runWithApprovalContext({ adapter, source, key }, run);\n}\n\nexport function startGatewayPresenter(connected: Map<string, PlatformAdapter>): void {\n\tadapters = connected;\n\tif (existsSync(path())) {\n\t\ttry {\n\t\t\tconst raw: unknown = JSON.parse(readFileSync(path(), \"utf8\"));\n\t\t\tif (\n\t\t\t\t!Array.isArray(raw) ||\n\t\t\t\t!raw.every(\n\t\t\t\t\t(item) =>\n\t\t\t\t\t\titem && typeof item.id === \"string\" && typeof item.key === \"string\" && typeof item.text === \"string\",\n\t\t\t\t)\n\t\t\t)\n\t\t\t\tthrow new Error(\"Invalid outbox\");\n\t\t\toutbox = raw as Outbound[];\n\t\t} catch {\n\t\t\tthrow new Error(\"Gateway outbox is invalid. Restore it before starting.\");\n\t\t}\n\t} else outbox = [];\n\tfor (const item of outbox) {\n\t\tif (item.kind === \"notice\") epochs.set(item.key, Math.max(epochs.get(item.key) ?? 0, item.epoch ?? 0));\n\t}\n\tif (timer) clearInterval(timer);\n\ttimer = setInterval(() => {\n\t\tvoid flushOutbox().catch((error) => console.error(\"[gateway] outbox flush failed:\", error));\n\t}, 3000);\n\ttimer.unref();\n\tvoid flushOutbox().catch((error) => console.error(\"[gateway] outbox flush failed:\", error));\n}\n\nexport function stopGatewayPresenter(): void {\n\tif (timer) clearInterval(timer);\n\ttimer = undefined;\n\tfor (const key of new Set([...sessionIdentities.keys(), ...dialogs.keys()])) invalidateGatewaySession(key);\n\tadapters = new Map();\n\tdeliveryConfigs.clear();\n\troleGrants.clear();\n}\n\nexport function gatewayEpoch(key: string): number {\n\treturn epochs.get(key) ?? 0;\n}\n\nexport async function sendGatewayNotice(\n\tkey: string,\n\ttext: string,\n\tkind: \"notice\" | \"result\" = \"notice\",\n\tepoch = gatewayEpoch(key),\n): Promise<void> {\n\tconst clean = text.replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, \"\").trim();\n\tif (!clean || !canDeliver(key)) return;\n\tconst { adapter, source } = gatewayDestination(key);\n\tawait queueGatewayText(key, source, adapter, clean, { kind, epoch });\n}\n\nexport async function queueGatewayText(\n\tkey: string,\n\tsource: MessageEvent[\"source\"],\n\tadapter: PlatformAdapter,\n\ttext: string,\n\toptions: { kind: \"result\" | \"notice\"; replyTo?: string; cfg?: GatewayConfig; chunks?: string[]; epoch?: number },\n): Promise<void> {\n\tif (!text || (options.kind === \"notice\" && options.epoch !== undefined && options.epoch !== gatewayEpoch(key)))\n\t\treturn;\n\tadapters.set(source.platform, adapter);\n\tif (options.cfg) deliveryConfigs.set(key, options.cfg);\n\tconst chunks = options.chunks ?? splitMessage(text, adapter.maxMessageLength);\n\tconst batchId = randomUUID();\n\tfor (const [index, chunk] of chunks.entries())\n\t\toutbox.push({\n\t\t\tid: randomUUID(),\n\t\t\tbatchId,\n\t\t\tkey,\n\t\t\tsource: { ...source, roleAuthorized: undefined },\n\t\t\tepoch: options.epoch ?? gatewayEpoch(key),\n\t\t\ttext: chunk,\n\t\t\tkind: options.kind,\n\t\t\treplyTo: index === 0 ? options.replyTo : undefined,\n\t\t});\n\tatomicJson(path(), outbox);\n\tif (activeDeliveries.has(key)) await activeDeliveries.get(key);\n\tawait flushKey(key);\n}\n\nexport function flushGatewayOutbox(): Promise<void> {\n\treturn flushOutbox();\n}\n\nfunction flushOutbox(): Promise<void> {\n\treturn Promise.all([...new Set(outbox.map((item) => item.key))].map(flushKey)).then(() => {});\n}\n\nfunction flushKey(key: string): Promise<void> {\n\tconst current = activeDeliveries.get(key);\n\tif (current) return current;\n\tconst running = drainKey(key).finally(() => {\n\t\tactiveDeliveries.delete(key);\n\t});\n\tactiveDeliveries.set(key, running);\n\treturn running;\n}\n\nasync function drainKey(key: string): Promise<void> {\n\tconst failedBatches = new Set(outbox.filter((item) => item.failed && item.batchId).map((item) => item.batchId));\n\tfor (const item of [...outbox].filter((item) => item.key === key)) {\n\t\tif (item.failed || (item.batchId && failedBatches.has(item.batchId))) continue;\n\t\tif (item.kind === \"notice\" && item.epoch !== (epochs.get(item.key) ?? 0)) {\n\t\t\toutbox = outbox.filter((v) => v.id !== item.id);\n\t\t\tatomicJson(path(), outbox);\n\t\t\tcontinue;\n\t\t}\n\t\tconst binding = conversationBinding(item.key);\n\t\tconst source = item.source ?? binding?.source;\n\t\tif (!source) break;\n\t\tif (!canDeliverGatewayText(key, source, deliveryConfigs.get(key))) {\n\t\t\toutbox = outbox.filter((v) => v.id !== item.id);\n\t\t\tatomicJson(path(), outbox);\n\t\t\tcontinue;\n\t\t}\n\t\tconst adapter = adapters.get(source.platform);\n\t\tif (!adapter || (item.nextAttempt ?? 0) > Date.now()) break;\n\t\tconst opts: SendOptions = { threadId: source.threadId, replyTo: item.replyTo };\n\t\tconst result = await adapter\n\t\t\t.send(source.chatId, item.text, opts)\n\t\t\t.catch((error: unknown) => ({ success: false as const, error: String(error), retryable: true }));\n\t\tif (result.success) {\n\t\t\toutbox = outbox.filter((v) => v.id !== item.id);\n\t\t\tatomicJson(path(), outbox);\n\t\t} else {\n\t\t\titem.attempts = (item.attempts ?? 0) + 1;\n\t\t\titem.failed = result.retryable === false || item.attempts >= MAX_ATTEMPTS;\n\t\t\titem.nextAttempt = Date.now() + Math.min(60_000, 3000 * 2 ** (item.attempts - 1));\n\t\t\tif (item.failed && item.batchId) {\n\t\t\t\tfailedBatches.add(item.batchId);\n\t\t\t\tfor (const pending of outbox) if (pending.batchId === item.batchId) pending.failed = true;\n\t\t\t}\n\t\t\tatomicJson(path(), outbox);\n\t\t\tconsole.error(\n\t\t\t\t`[gateway] delivery ${item.failed ? \"failed permanently\" : \"retry scheduled\"} for ${source.platform}: ${result.error ?? \"unknown error\"}`,\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nexport function invalidateGatewayDialogs(key: string): void {\n\tinvalidateGatewayPickers(key);\n\tepochs.set(key, (epochs.get(key) ?? 0) + 1);\n\tconst remaining = outbox.filter((item) => item.key !== key || item.kind !== \"notice\");\n\tif (remaining.length !== outbox.length) {\n\t\toutbox = remaining;\n\t\tatomicJson(path(), outbox);\n\t}\n\tfor (const cancel of dialogs.get(key) ?? []) cancel();\n\tdialogs.delete(key);\n\tinputs.delete(key);\n}\n\nexport function acceptGatewayInput(key: string, event: MessageEvent): boolean {\n\tconst input = inputs.get(key);\n\tif (!input || input.userId !== event.source.userId || !canDeliver(key)) return false;\n\tif (event.text.startsWith(\"/\") && event.text !== \"/cancel\") return false;\n\tinput.resolve(event.text === \"/cancel\" ? undefined : event.text);\n\treturn true;\n}\n\ninterface GatewayDialogRequest {\n\tgeneration: number;\n\tsource: MessageEvent[\"source\"];\n}\n\nfunction dialog(\n\tkey: string,\n\topts: ExtensionUIDialogOptions | undefined,\n\tshow: (done: (value: string | undefined) => void, valid: () => boolean) => Promise<void>,\n\trequest?: GatewayDialogRequest,\n): Promise<string | undefined> {\n\tif (request && request.generation !== gatewayPickerGeneration(key)) return Promise.resolve(undefined);\n\tconst epoch = epochs.get(key) ?? 0;\n\tconst source = request?.source ?? conversationBinding(key)?.source;\n\treturn new Promise((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst finish = (value: string | undefined, error?: unknown) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timeout);\n\t\t\topts?.signal?.removeEventListener(\"abort\", cancel);\n\t\t\tif (inputs.get(key)?.resolve === done) inputs.delete(key);\n\t\t\tdialogs.get(key)?.delete(cancel);\n\t\t\tif (error !== undefined) reject(error);\n\t\t\telse resolve(value);\n\t\t};\n\t\tconst done = (value: string | undefined) => finish(value);\n\t\tconst cancel = () => done(undefined);\n\t\tconst timeout = setTimeout(cancel, opts?.timeout ?? 300_000);\n\t\tconst set = dialogs.get(key) ?? new Set();\n\t\tset.add(cancel);\n\t\tdialogs.set(key, set);\n\t\topts?.signal?.addEventListener(\"abort\", cancel, { once: true });\n\t\tif (opts?.signal?.aborted) {\n\t\t\tcancel();\n\t\t\treturn;\n\t\t}\n\t\tvoid show(\n\t\t\tdone,\n\t\t\t() => !settled && (epochs.get(key) ?? 0) === epoch && !!source && canDeliverGatewayText(key, source),\n\t\t).catch((error) => {\n\t\t\tfinish(undefined, error);\n\t\t});\n\t});\n}\n\nexport function gatewaySelect(\n\tkey: string,\n\ttitle: string,\n\toptions: string[],\n\topts?: ExtensionUIDialogOptions,\n\trequest?: GatewayDialogRequest,\n): Promise<string | undefined> {\n\treturn dialog(\n\t\tkey,\n\t\topts,\n\t\tasync (done, valid) => {\n\t\t\tconst { adapter, source: destination } = gatewayDestination(key);\n\t\t\tconst source = request?.source ?? destination;\n\t\t\tconst result = await createPicker(\n\t\t\t\tadapter,\n\t\t\t\tsource,\n\t\t\t\t{\n\t\t\t\t\tkind: \"dialog\",\n\t\t\t\t\tgeneration: request?.generation,\n\t\t\t\t\tsessionKey: key,\n\t\t\t\t\tinvokerId: source.userId,\n\t\t\t\t\ttitle,\n\t\t\t\t\titems: options.map((value) => ({ label: value, value })),\n\t\t\t\t\tvalidate: valid,\n\t\t\t\t\tonCancel: () => done(undefined),\n\t\t\t\t\tresolve: async (item) => {\n\t\t\t\t\t\tdone(item.value);\n\t\t\t\t\t\treturn { done: true, text: `${title}\\n${item.value}` };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ threadId: source.threadId },\n\t\t\t);\n\t\t\tif (!result.success) throw new Error(result.error ?? \"Could not send picker.\");\n\t\t},\n\t\trequest,\n\t);\n}\n\nexport function gatewayInput(\n\tkey: string,\n\ttitle: string,\n\tplaceholder?: string,\n\topts?: ExtensionUIDialogOptions,\n\trequest?: GatewayDialogRequest,\n): Promise<string | undefined> {\n\tif (inputs.has(key)) return Promise.reject(new Error(\"Answer or cancel the existing question first.\"));\n\tconst epoch = gatewayEpoch(key);\n\treturn dialog(\n\t\tkey,\n\t\topts,\n\t\tasync (done, valid) => {\n\t\t\tif (!valid()) {\n\t\t\t\tdone(undefined);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst source = request?.source ?? gatewayDestination(key).source;\n\t\t\tinputs.set(key, { userId: source.userId, resolve: done });\n\t\t\tawait sendGatewayNotice(\n\t\t\t\tkey,\n\t\t\t\t`${title}${placeholder ? `\\n${placeholder}` : \"\"}\\nReply with text, or /cancel.`,\n\t\t\t\t\"notice\",\n\t\t\t\tepoch,\n\t\t\t);\n\t\t},\n\t\trequest,\n\t);\n}\n\nexport function createGatewayUI(key: string): ExtensionUIContext {\n\tconst identity = sessionIdentity(key);\n\tconst source = conversationBinding(key)?.source;\n\tconst valid = () => sessionIdentity(key) === identity && !!source && canDeliverGatewayText(key, source);\n\treturn {\n\t\t...noOpUIContext,\n\t\tselect: (title, options, opts) =>\n\t\t\tvalid() ? gatewaySelect(key, title, options, opts) : Promise.resolve(undefined),\n\t\tconfirm: async (title, message, opts) =>\n\t\t\tvalid() && (await gatewaySelect(key, `${title}\\n${message}`, [\"Approve\", \"Decline\"], opts)) === \"Approve\",\n\t\tinput: (title, placeholder, opts) =>\n\t\t\tvalid() ? gatewayInput(key, title, placeholder, opts) : Promise.resolve(undefined),\n\t\teditor: (title, prefill) => (valid() ? gatewayInput(key, title, prefill) : Promise.resolve(undefined)),\n\t\tnotify: (message) => {\n\t\t\tif (valid()) void sendGatewayNotice(key, message).catch(() => {});\n\t\t},\n\t\tcustom: async () => {\n\t\t\tthrow new Error(\n\t\t\t\t\"This command requires the terminal interface. Use the gateway's text commands or button pickers.\",\n\t\t\t);\n\t\t},\n\t\tpasteToEditor: (text) => {\n\t\t\tif (valid()) void sendGatewayNotice(key, text).catch(() => {});\n\t\t},\n\t\tsetEditorText: (text) => {\n\t\t\tif (valid()) void sendGatewayNotice(key, text).catch(() => {});\n\t\t},\n\t};\n}\n"]}
@@ -5,13 +5,35 @@ import { getAgentDir } from "../config.js";
5
5
  import { noOpUIContext } from "../core/extensions/runner.js";
6
6
  import { runWithApprovalContext } from "./approval.js";
7
7
  import { isAuthorized } from "./authz.js";
8
- import { createPicker } from "./buttons.js";
8
+ import { createPicker, gatewayPickerGeneration, invalidateGatewayPickers } from "./buttons.js";
9
9
  import { gatewayConfigPath, loadGatewayConfig } from "./config.js";
10
10
  import { conversationBinding } from "./conversations.js";
11
11
  import { createPairingStore } from "./pairing.js";
12
12
  import { atomicJson } from "./service.js";
13
13
  import { splitMessage } from "./text.js";
14
14
  const epochs = new Map();
15
+ const sessionIdentities = new Map();
16
+ function sessionIdentity(key) {
17
+ let identity = sessionIdentities.get(key);
18
+ if (!identity) {
19
+ identity = {};
20
+ sessionIdentities.set(key, identity);
21
+ }
22
+ return identity;
23
+ }
24
+ /** Retire session UI independently of cancelling its current dialogs. Roll back a failed replacement. */
25
+ export function invalidateGatewaySession(key) {
26
+ const previous = sessionIdentity(key);
27
+ const next = {};
28
+ sessionIdentities.set(key, next);
29
+ invalidateGatewayDialogs(key);
30
+ return () => {
31
+ if (sessionIdentities.get(key) === next) {
32
+ invalidateGatewayDialogs(key);
33
+ sessionIdentities.set(key, previous);
34
+ }
35
+ };
36
+ }
15
37
  const inputs = new Map();
16
38
  const dialogs = new Map();
17
39
  let adapters = new Map();
@@ -35,14 +57,21 @@ export function rememberGatewayRole(key, source) {
35
57
  else
36
58
  roleGrants.delete(key);
37
59
  }
60
+ /** Fresh delivery authorization shared by previews, final edits and durable retries. */
61
+ export function canDeliverGatewayText(key, source, fallbackConfig, pairing = createPairingStore()) {
62
+ const binding = conversationBinding(key);
63
+ const cfg = existsSync(gatewayConfigPath()) ? loadGatewayConfig() : (fallbackConfig ?? loadGatewayConfig());
64
+ return ((!binding ||
65
+ (binding.source.platform === source.platform &&
66
+ binding.source.chatId === source.chatId &&
67
+ binding.source.userId === source.userId &&
68
+ binding.source.threadId === source.threadId &&
69
+ (!binding.owner || binding.owner === source.userId))) &&
70
+ isAuthorized(sourceWithCurrentRole(key, source), cfg, pairing));
71
+ }
38
72
  function canDeliver(key) {
39
73
  const binding = conversationBinding(key);
40
- if (!binding)
41
- return false;
42
- const cfg = loadGatewayConfig();
43
- if (binding.owner && binding.owner !== binding.source.userId)
44
- return false;
45
- return isAuthorized(sourceWithCurrentRole(key, binding.source), cfg, createPairingStore());
74
+ return !!binding && canDeliverGatewayText(key, binding.source);
46
75
  }
47
76
  export function gatewayDestination(key) {
48
77
  const binding = conversationBinding(key);
@@ -87,8 +116,8 @@ export function stopGatewayPresenter() {
87
116
  if (timer)
88
117
  clearInterval(timer);
89
118
  timer = undefined;
90
- for (const key of dialogs.keys())
91
- invalidateGatewayDialogs(key);
119
+ for (const key of new Set([...sessionIdentities.keys(), ...dialogs.keys()]))
120
+ invalidateGatewaySession(key);
92
121
  adapters = new Map();
93
122
  deliveryConfigs.clear();
94
123
  roleGrants.clear();
@@ -157,16 +186,7 @@ async function drainKey(key) {
157
186
  const source = item.source ?? binding?.source;
158
187
  if (!source)
159
188
  break;
160
- const cfg = existsSync(gatewayConfigPath())
161
- ? loadGatewayConfig()
162
- : (deliveryConfigs.get(item.key) ?? loadGatewayConfig());
163
- if ((binding &&
164
- (binding.source.platform !== source.platform ||
165
- binding.source.chatId !== source.chatId ||
166
- binding.source.userId !== source.userId ||
167
- binding.source.threadId !== source.threadId)) ||
168
- !isAuthorized(sourceWithCurrentRole(key, source), cfg, createPairingStore()) ||
169
- (binding?.owner && binding.owner !== source.userId)) {
189
+ if (!canDeliverGatewayText(key, source, deliveryConfigs.get(key))) {
170
190
  outbox = outbox.filter((v) => v.id !== item.id);
171
191
  atomicJson(path(), outbox);
172
192
  continue;
@@ -199,6 +219,7 @@ async function drainKey(key) {
199
219
  }
200
220
  }
201
221
  export function invalidateGatewayDialogs(key) {
222
+ invalidateGatewayPickers(key);
202
223
  epochs.set(key, (epochs.get(key) ?? 0) + 1);
203
224
  const remaining = outbox.filter((item) => item.key !== key || item.kind !== "notice");
204
225
  if (remaining.length !== outbox.length) {
@@ -219,8 +240,11 @@ export function acceptGatewayInput(key, event) {
219
240
  input.resolve(event.text === "/cancel" ? undefined : event.text);
220
241
  return true;
221
242
  }
222
- function dialog(key, opts, show) {
243
+ function dialog(key, opts, show, request) {
244
+ if (request && request.generation !== gatewayPickerGeneration(key))
245
+ return Promise.resolve(undefined);
223
246
  const epoch = epochs.get(key) ?? 0;
247
+ const source = request?.source ?? conversationBinding(key)?.source;
224
248
  return new Promise((resolve, reject) => {
225
249
  let settled = false;
226
250
  const finish = (value, error) => {
@@ -229,7 +253,8 @@ function dialog(key, opts, show) {
229
253
  settled = true;
230
254
  clearTimeout(timeout);
231
255
  opts?.signal?.removeEventListener("abort", cancel);
232
- inputs.delete(key);
256
+ if (inputs.get(key)?.resolve === done)
257
+ inputs.delete(key);
233
258
  dialogs.get(key)?.delete(cancel);
234
259
  if (error !== undefined)
235
260
  reject(error);
@@ -247,16 +272,18 @@ function dialog(key, opts, show) {
247
272
  cancel();
248
273
  return;
249
274
  }
250
- void show(done, () => !settled && (epochs.get(key) ?? 0) === epoch && canDeliver(key)).catch((error) => {
275
+ void show(done, () => !settled && (epochs.get(key) ?? 0) === epoch && !!source && canDeliverGatewayText(key, source)).catch((error) => {
251
276
  finish(undefined, error);
252
277
  });
253
278
  });
254
279
  }
255
- export function gatewaySelect(key, title, options, opts) {
280
+ export function gatewaySelect(key, title, options, opts, request) {
256
281
  return dialog(key, opts, async (done, valid) => {
257
- const { adapter, source } = gatewayDestination(key);
282
+ const { adapter, source: destination } = gatewayDestination(key);
283
+ const source = request?.source ?? destination;
258
284
  const result = await createPicker(adapter, source, {
259
285
  kind: "dialog",
286
+ generation: request?.generation,
260
287
  sessionKey: key,
261
288
  invokerId: source.userId,
262
289
  title,
@@ -270,38 +297,46 @@ export function gatewaySelect(key, title, options, opts) {
270
297
  }, { threadId: source.threadId });
271
298
  if (!result.success)
272
299
  throw new Error(result.error ?? "Could not send picker.");
273
- });
300
+ }, request);
274
301
  }
275
- export function gatewayInput(key, title, placeholder, opts) {
302
+ export function gatewayInput(key, title, placeholder, opts, request) {
276
303
  if (inputs.has(key))
277
304
  return Promise.reject(new Error("Answer or cancel the existing question first."));
278
305
  const epoch = gatewayEpoch(key);
279
- return dialog(key, opts, async (done) => {
280
- const { source } = gatewayDestination(key);
306
+ return dialog(key, opts, async (done, valid) => {
307
+ if (!valid()) {
308
+ done(undefined);
309
+ return;
310
+ }
311
+ const source = request?.source ?? gatewayDestination(key).source;
281
312
  inputs.set(key, { userId: source.userId, resolve: done });
282
313
  await sendGatewayNotice(key, `${title}${placeholder ? `\n${placeholder}` : ""}\nReply with text, or /cancel.`, "notice", epoch);
283
- });
314
+ }, request);
284
315
  }
285
316
  export function createGatewayUI(key) {
286
- const epoch = gatewayEpoch(key);
317
+ const identity = sessionIdentity(key);
318
+ const source = conversationBinding(key)?.source;
319
+ const valid = () => sessionIdentity(key) === identity && !!source && canDeliverGatewayText(key, source);
287
320
  return {
288
321
  ...noOpUIContext,
289
- select: (title, options, opts) => gatewayEpoch(key) === epoch ? gatewaySelect(key, title, options, opts) : Promise.resolve(undefined),
290
- confirm: async (title, message, opts) => gatewayEpoch(key) === epoch &&
291
- (await gatewaySelect(key, `${title}\n${message}`, ["Approve", "Decline"], opts)) === "Approve",
292
- input: (title, placeholder, opts) => gatewayEpoch(key) === epoch ? gatewayInput(key, title, placeholder, opts) : Promise.resolve(undefined),
293
- editor: (title, prefill) => gatewayEpoch(key) === epoch ? gatewayInput(key, title, prefill) : Promise.resolve(undefined),
322
+ select: (title, options, opts) => valid() ? gatewaySelect(key, title, options, opts) : Promise.resolve(undefined),
323
+ confirm: async (title, message, opts) => valid() && (await gatewaySelect(key, `${title}\n${message}`, ["Approve", "Decline"], opts)) === "Approve",
324
+ input: (title, placeholder, opts) => valid() ? gatewayInput(key, title, placeholder, opts) : Promise.resolve(undefined),
325
+ editor: (title, prefill) => (valid() ? gatewayInput(key, title, prefill) : Promise.resolve(undefined)),
294
326
  notify: (message) => {
295
- void sendGatewayNotice(key, message, "notice", epoch).catch(() => { });
327
+ if (valid())
328
+ void sendGatewayNotice(key, message).catch(() => { });
296
329
  },
297
330
  custom: async () => {
298
331
  throw new Error("This command requires the terminal interface. Use the gateway's text commands or button pickers.");
299
332
  },
300
333
  pasteToEditor: (text) => {
301
- void sendGatewayNotice(key, text, "notice", epoch).catch(() => { });
334
+ if (valid())
335
+ void sendGatewayNotice(key, text).catch(() => { });
302
336
  },
303
337
  setEditorText: (text) => {
304
- void sendGatewayNotice(key, text, "notice", epoch).catch(() => { });
338
+ if (valid())
339
+ void sendGatewayNotice(key, text).catch(() => { });
305
340
  },
306
341
  };
307
342
  }
@@ -1 +1 @@
1
- {"version":3,"file":"presenter.js","sourceRoot":"","sources":["../../src/gateway/presenter.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { noOpUIContext } from \"../core/extensions/runner.ts\";\nimport type { ExtensionUIContext, ExtensionUIDialogOptions } from \"../core/extensions/types.ts\";\nimport { runWithApprovalContext } from \"./approval.ts\";\nimport { isAuthorized } from \"./authz.ts\";\nimport { createPicker } from \"./buttons.ts\";\nimport { type GatewayConfig, gatewayConfigPath, loadGatewayConfig } from \"./config.ts\";\nimport { conversationBinding } from \"./conversations.ts\";\nimport { createPairingStore } from \"./pairing.ts\";\nimport { atomicJson } from \"./service.ts\";\nimport { splitMessage } from \"./text.ts\";\nimport type { MessageEvent, PlatformAdapter, SendOptions } from \"./types.ts\";\n\nconst epochs = new Map<string, number>();\nconst inputs = new Map<string, { userId: string; resolve: (value: string | undefined) => void }>();\nconst dialogs = new Map<string, Set<() => void>>();\nlet adapters = new Map<string, PlatformAdapter>();\nconst deliveryConfigs = new Map<string, GatewayConfig>();\nconst roleGrants = new Map<string, { userId: string; chatId: string; threadId?: string }>();\nlet timer: ReturnType<typeof setInterval> | undefined;\nconst activeDeliveries = new Map<string, Promise<void>>();\ninterface Outbound {\n\tid: string;\n\tkey: string;\n\tepoch: number;\n\ttext: string;\n\tsource?: MessageEvent[\"source\"];\n\treplyTo?: string;\n\tkind?: \"result\" | \"notice\";\n\tattempts?: number;\n\tnextAttempt?: number;\n\tfailed?: boolean;\n\tbatchId?: string;\n}\nconst MAX_ATTEMPTS = 5;\nconst path = () => join(getAgentDir(), \"gateway-outbox.json\");\nlet outbox: Outbound[] = [];\n\nfunction sourceWithCurrentRole(key: string, source: MessageEvent[\"source\"]): MessageEvent[\"source\"] {\n\tconst grant = roleGrants.get(key);\n\treturn {\n\t\t...source,\n\t\troleAuthorized:\n\t\t\tgrant?.userId === source.userId && grant.chatId === source.chatId && grant.threadId === source.threadId,\n\t};\n}\n\nexport function rememberGatewayRole(key: string, source: MessageEvent[\"source\"]): void {\n\tif (source.roleAuthorized === true)\n\t\troleGrants.set(key, { userId: source.userId, chatId: source.chatId, threadId: source.threadId });\n\telse roleGrants.delete(key);\n}\n\nfunction canDeliver(key: string): boolean {\n\tconst binding = conversationBinding(key);\n\tif (!binding) return false;\n\tconst cfg = loadGatewayConfig();\n\tif (binding.owner && binding.owner !== binding.source.userId) return false;\n\treturn isAuthorized(sourceWithCurrentRole(key, binding.source), cfg, createPairingStore());\n}\n\nexport function gatewayDestination(key: string): { adapter: PlatformAdapter; source: MessageEvent[\"source\"] } {\n\tconst binding = conversationBinding(key);\n\tconst adapter = binding && adapters.get(binding.source.platform);\n\tif (!binding || !adapter || !canDeliver(key))\n\t\tthrow new Error(\"This chat is no longer authorized or its platform is disconnected.\");\n\treturn { adapter, source: binding.source };\n}\n\nexport function withGatewayPresentation<T>(key: string, run: () => Promise<T>): Promise<T> {\n\tconst { adapter, source } = gatewayDestination(key);\n\treturn runWithApprovalContext({ adapter, source, key }, run);\n}\n\nexport function startGatewayPresenter(connected: Map<string, PlatformAdapter>): void {\n\tadapters = connected;\n\tif (existsSync(path())) {\n\t\ttry {\n\t\t\tconst raw: unknown = JSON.parse(readFileSync(path(), \"utf8\"));\n\t\t\tif (\n\t\t\t\t!Array.isArray(raw) ||\n\t\t\t\t!raw.every(\n\t\t\t\t\t(item) =>\n\t\t\t\t\t\titem && typeof item.id === \"string\" && typeof item.key === \"string\" && typeof item.text === \"string\",\n\t\t\t\t)\n\t\t\t)\n\t\t\t\tthrow new Error(\"Invalid outbox\");\n\t\t\toutbox = raw as Outbound[];\n\t\t} catch {\n\t\t\tthrow new Error(\"Gateway outbox is invalid. Restore it before starting.\");\n\t\t}\n\t} else outbox = [];\n\tfor (const item of outbox) {\n\t\tif (item.kind === \"notice\") epochs.set(item.key, Math.max(epochs.get(item.key) ?? 0, item.epoch ?? 0));\n\t}\n\tif (timer) clearInterval(timer);\n\ttimer = setInterval(() => {\n\t\tvoid flushOutbox().catch((error) => console.error(\"[gateway] outbox flush failed:\", error));\n\t}, 3000);\n\ttimer.unref();\n\tvoid flushOutbox().catch((error) => console.error(\"[gateway] outbox flush failed:\", error));\n}\n\nexport function stopGatewayPresenter(): void {\n\tif (timer) clearInterval(timer);\n\ttimer = undefined;\n\tfor (const key of dialogs.keys()) invalidateGatewayDialogs(key);\n\tadapters = new Map();\n\tdeliveryConfigs.clear();\n\troleGrants.clear();\n}\n\nexport function gatewayEpoch(key: string): number {\n\treturn epochs.get(key) ?? 0;\n}\n\nexport async function sendGatewayNotice(\n\tkey: string,\n\ttext: string,\n\tkind: \"notice\" | \"result\" = \"notice\",\n\tepoch = gatewayEpoch(key),\n): Promise<void> {\n\tconst clean = text.replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, \"\").trim();\n\tif (!clean || !canDeliver(key)) return;\n\tconst { adapter, source } = gatewayDestination(key);\n\tawait queueGatewayText(key, source, adapter, clean, { kind, epoch });\n}\n\nexport async function queueGatewayText(\n\tkey: string,\n\tsource: MessageEvent[\"source\"],\n\tadapter: PlatformAdapter,\n\ttext: string,\n\toptions: { kind: \"result\" | \"notice\"; replyTo?: string; cfg?: GatewayConfig; chunks?: string[]; epoch?: number },\n): Promise<void> {\n\tif (!text || (options.kind === \"notice\" && options.epoch !== undefined && options.epoch !== gatewayEpoch(key)))\n\t\treturn;\n\tadapters.set(source.platform, adapter);\n\tif (options.cfg) deliveryConfigs.set(key, options.cfg);\n\tconst chunks = options.chunks ?? splitMessage(text, adapter.maxMessageLength);\n\tconst batchId = randomUUID();\n\tfor (const [index, chunk] of chunks.entries())\n\t\toutbox.push({\n\t\t\tid: randomUUID(),\n\t\t\tbatchId,\n\t\t\tkey,\n\t\t\tsource: { ...source, roleAuthorized: undefined },\n\t\t\tepoch: options.epoch ?? gatewayEpoch(key),\n\t\t\ttext: chunk,\n\t\t\tkind: options.kind,\n\t\t\treplyTo: index === 0 ? options.replyTo : undefined,\n\t\t});\n\tatomicJson(path(), outbox);\n\tif (activeDeliveries.has(key)) await activeDeliveries.get(key);\n\tawait flushKey(key);\n}\n\nexport function flushGatewayOutbox(): Promise<void> {\n\treturn flushOutbox();\n}\n\nfunction flushOutbox(): Promise<void> {\n\treturn Promise.all([...new Set(outbox.map((item) => item.key))].map(flushKey)).then(() => {});\n}\n\nfunction flushKey(key: string): Promise<void> {\n\tconst current = activeDeliveries.get(key);\n\tif (current) return current;\n\tconst running = drainKey(key).finally(() => {\n\t\tactiveDeliveries.delete(key);\n\t});\n\tactiveDeliveries.set(key, running);\n\treturn running;\n}\n\nasync function drainKey(key: string): Promise<void> {\n\tconst failedBatches = new Set(outbox.filter((item) => item.failed && item.batchId).map((item) => item.batchId));\n\tfor (const item of [...outbox].filter((item) => item.key === key)) {\n\t\tif (item.failed || (item.batchId && failedBatches.has(item.batchId))) continue;\n\t\tif (item.kind === \"notice\" && item.epoch !== (epochs.get(item.key) ?? 0)) {\n\t\t\toutbox = outbox.filter((v) => v.id !== item.id);\n\t\t\tatomicJson(path(), outbox);\n\t\t\tcontinue;\n\t\t}\n\t\tconst binding = conversationBinding(item.key);\n\t\tconst source = item.source ?? binding?.source;\n\t\tif (!source) break;\n\t\tconst cfg = existsSync(gatewayConfigPath())\n\t\t\t? loadGatewayConfig()\n\t\t\t: (deliveryConfigs.get(item.key) ?? loadGatewayConfig());\n\t\tif (\n\t\t\t(binding &&\n\t\t\t\t(binding.source.platform !== source.platform ||\n\t\t\t\t\tbinding.source.chatId !== source.chatId ||\n\t\t\t\t\tbinding.source.userId !== source.userId ||\n\t\t\t\t\tbinding.source.threadId !== source.threadId)) ||\n\t\t\t!isAuthorized(sourceWithCurrentRole(key, source), cfg, createPairingStore()) ||\n\t\t\t(binding?.owner && binding.owner !== source.userId)\n\t\t) {\n\t\t\toutbox = outbox.filter((v) => v.id !== item.id);\n\t\t\tatomicJson(path(), outbox);\n\t\t\tcontinue;\n\t\t}\n\t\tconst adapter = adapters.get(source.platform);\n\t\tif (!adapter || (item.nextAttempt ?? 0) > Date.now()) break;\n\t\tconst opts: SendOptions = { threadId: source.threadId, replyTo: item.replyTo };\n\t\tconst result = await adapter\n\t\t\t.send(source.chatId, item.text, opts)\n\t\t\t.catch((error: unknown) => ({ success: false as const, error: String(error), retryable: true }));\n\t\tif (result.success) {\n\t\t\toutbox = outbox.filter((v) => v.id !== item.id);\n\t\t\tatomicJson(path(), outbox);\n\t\t} else {\n\t\t\titem.attempts = (item.attempts ?? 0) + 1;\n\t\t\titem.failed = result.retryable === false || item.attempts >= MAX_ATTEMPTS;\n\t\t\titem.nextAttempt = Date.now() + Math.min(60_000, 3000 * 2 ** (item.attempts - 1));\n\t\t\tif (item.failed && item.batchId) {\n\t\t\t\tfailedBatches.add(item.batchId);\n\t\t\t\tfor (const pending of outbox) if (pending.batchId === item.batchId) pending.failed = true;\n\t\t\t}\n\t\t\tatomicJson(path(), outbox);\n\t\t\tconsole.error(\n\t\t\t\t`[gateway] delivery ${item.failed ? \"failed permanently\" : \"retry scheduled\"} for ${source.platform}: ${result.error ?? \"unknown error\"}`,\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nexport function invalidateGatewayDialogs(key: string): void {\n\tepochs.set(key, (epochs.get(key) ?? 0) + 1);\n\tconst remaining = outbox.filter((item) => item.key !== key || item.kind !== \"notice\");\n\tif (remaining.length !== outbox.length) {\n\t\toutbox = remaining;\n\t\tatomicJson(path(), outbox);\n\t}\n\tfor (const cancel of dialogs.get(key) ?? []) cancel();\n\tdialogs.delete(key);\n\tinputs.delete(key);\n}\n\nexport function acceptGatewayInput(key: string, event: MessageEvent): boolean {\n\tconst input = inputs.get(key);\n\tif (!input || input.userId !== event.source.userId || !canDeliver(key)) return false;\n\tif (event.text.startsWith(\"/\") && event.text !== \"/cancel\") return false;\n\tinput.resolve(event.text === \"/cancel\" ? undefined : event.text);\n\treturn true;\n}\n\nfunction dialog(\n\tkey: string,\n\topts: ExtensionUIDialogOptions | undefined,\n\tshow: (done: (value: string | undefined) => void, valid: () => boolean) => Promise<void>,\n): Promise<string | undefined> {\n\tconst epoch = epochs.get(key) ?? 0;\n\treturn new Promise((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst finish = (value: string | undefined, error?: unknown) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timeout);\n\t\t\topts?.signal?.removeEventListener(\"abort\", cancel);\n\t\t\tinputs.delete(key);\n\t\t\tdialogs.get(key)?.delete(cancel);\n\t\t\tif (error !== undefined) reject(error);\n\t\t\telse resolve(value);\n\t\t};\n\t\tconst done = (value: string | undefined) => finish(value);\n\t\tconst cancel = () => done(undefined);\n\t\tconst timeout = setTimeout(cancel, opts?.timeout ?? 300_000);\n\t\tconst set = dialogs.get(key) ?? new Set();\n\t\tset.add(cancel);\n\t\tdialogs.set(key, set);\n\t\topts?.signal?.addEventListener(\"abort\", cancel, { once: true });\n\t\tif (opts?.signal?.aborted) {\n\t\t\tcancel();\n\t\t\treturn;\n\t\t}\n\t\tvoid show(done, () => !settled && (epochs.get(key) ?? 0) === epoch && canDeliver(key)).catch((error) => {\n\t\t\tfinish(undefined, error);\n\t\t});\n\t});\n}\n\nexport function gatewaySelect(\n\tkey: string,\n\ttitle: string,\n\toptions: string[],\n\topts?: ExtensionUIDialogOptions,\n): Promise<string | undefined> {\n\treturn dialog(key, opts, async (done, valid) => {\n\t\tconst { adapter, source } = gatewayDestination(key);\n\t\tconst result = await createPicker(\n\t\t\tadapter,\n\t\t\tsource,\n\t\t\t{\n\t\t\t\tkind: \"dialog\",\n\t\t\t\tsessionKey: key,\n\t\t\t\tinvokerId: source.userId,\n\t\t\t\ttitle,\n\t\t\t\titems: options.map((value) => ({ label: value, value })),\n\t\t\t\tvalidate: valid,\n\t\t\t\tonCancel: () => done(undefined),\n\t\t\t\tresolve: async (item) => {\n\t\t\t\t\tdone(item.value);\n\t\t\t\t\treturn { done: true, text: `${title}\\n${item.value}` };\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ threadId: source.threadId },\n\t\t);\n\t\tif (!result.success) throw new Error(result.error ?? \"Could not send picker.\");\n\t});\n}\n\nexport function gatewayInput(\n\tkey: string,\n\ttitle: string,\n\tplaceholder?: string,\n\topts?: ExtensionUIDialogOptions,\n): Promise<string | undefined> {\n\tif (inputs.has(key)) return Promise.reject(new Error(\"Answer or cancel the existing question first.\"));\n\tconst epoch = gatewayEpoch(key);\n\treturn dialog(key, opts, async (done) => {\n\t\tconst { source } = gatewayDestination(key);\n\t\tinputs.set(key, { userId: source.userId, resolve: done });\n\t\tawait sendGatewayNotice(\n\t\t\tkey,\n\t\t\t`${title}${placeholder ? `\\n${placeholder}` : \"\"}\\nReply with text, or /cancel.`,\n\t\t\t\"notice\",\n\t\t\tepoch,\n\t\t);\n\t});\n}\n\nexport function createGatewayUI(key: string): ExtensionUIContext {\n\tconst epoch = gatewayEpoch(key);\n\treturn {\n\t\t...noOpUIContext,\n\t\tselect: (title, options, opts) =>\n\t\t\tgatewayEpoch(key) === epoch ? gatewaySelect(key, title, options, opts) : Promise.resolve(undefined),\n\t\tconfirm: async (title, message, opts) =>\n\t\t\tgatewayEpoch(key) === epoch &&\n\t\t\t(await gatewaySelect(key, `${title}\\n${message}`, [\"Approve\", \"Decline\"], opts)) === \"Approve\",\n\t\tinput: (title, placeholder, opts) =>\n\t\t\tgatewayEpoch(key) === epoch ? gatewayInput(key, title, placeholder, opts) : Promise.resolve(undefined),\n\t\teditor: (title, prefill) =>\n\t\t\tgatewayEpoch(key) === epoch ? gatewayInput(key, title, prefill) : Promise.resolve(undefined),\n\t\tnotify: (message) => {\n\t\t\tvoid sendGatewayNotice(key, message, \"notice\", epoch).catch(() => {});\n\t\t},\n\t\tcustom: async () => {\n\t\t\tthrow new Error(\n\t\t\t\t\"This command requires the terminal interface. Use the gateway's text commands or button pickers.\",\n\t\t\t);\n\t\t},\n\t\tpasteToEditor: (text) => {\n\t\t\tvoid sendGatewayNotice(key, text, \"notice\", epoch).catch(() => {});\n\t\t},\n\t\tsetEditorText: (text) => {\n\t\t\tvoid sendGatewayNotice(key, text, \"notice\", epoch).catch(() => {});\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"presenter.js","sourceRoot":"","sources":["../../src/gateway/presenter.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { noOpUIContext } from \"../core/extensions/runner.ts\";\nimport type { ExtensionUIContext, ExtensionUIDialogOptions } from \"../core/extensions/types.ts\";\nimport { runWithApprovalContext } from \"./approval.ts\";\nimport { isAuthorized } from \"./authz.ts\";\nimport { createPicker, gatewayPickerGeneration, invalidateGatewayPickers } from \"./buttons.ts\";\nimport { type GatewayConfig, gatewayConfigPath, loadGatewayConfig } from \"./config.ts\";\nimport { conversationBinding } from \"./conversations.ts\";\nimport { createPairingStore, type PairingStore } from \"./pairing.ts\";\nimport { atomicJson } from \"./service.ts\";\nimport { splitMessage } from \"./text.ts\";\nimport type { MessageEvent, PlatformAdapter, SendOptions } from \"./types.ts\";\n\nconst epochs = new Map<string, number>();\nconst sessionIdentities = new Map<string, object>();\n\nfunction sessionIdentity(key: string): object {\n\tlet identity = sessionIdentities.get(key);\n\tif (!identity) {\n\t\tidentity = {};\n\t\tsessionIdentities.set(key, identity);\n\t}\n\treturn identity;\n}\n\n/** Retire session UI independently of cancelling its current dialogs. Roll back a failed replacement. */\nexport function invalidateGatewaySession(key: string): () => void {\n\tconst previous = sessionIdentity(key);\n\tconst next = {};\n\tsessionIdentities.set(key, next);\n\tinvalidateGatewayDialogs(key);\n\treturn () => {\n\t\tif (sessionIdentities.get(key) === next) {\n\t\t\tinvalidateGatewayDialogs(key);\n\t\t\tsessionIdentities.set(key, previous);\n\t\t}\n\t};\n}\nconst inputs = new Map<string, { userId: string; resolve: (value: string | undefined) => void }>();\nconst dialogs = new Map<string, Set<() => void>>();\nlet adapters = new Map<string, PlatformAdapter>();\nconst deliveryConfigs = new Map<string, GatewayConfig>();\nconst roleGrants = new Map<string, { userId: string; chatId: string; threadId?: string }>();\nlet timer: ReturnType<typeof setInterval> | undefined;\nconst activeDeliveries = new Map<string, Promise<void>>();\ninterface Outbound {\n\tid: string;\n\tkey: string;\n\tepoch: number;\n\ttext: string;\n\tsource?: MessageEvent[\"source\"];\n\treplyTo?: string;\n\tkind?: \"result\" | \"notice\";\n\tattempts?: number;\n\tnextAttempt?: number;\n\tfailed?: boolean;\n\tbatchId?: string;\n}\nconst MAX_ATTEMPTS = 5;\nconst path = () => join(getAgentDir(), \"gateway-outbox.json\");\nlet outbox: Outbound[] = [];\n\nfunction sourceWithCurrentRole(key: string, source: MessageEvent[\"source\"]): MessageEvent[\"source\"] {\n\tconst grant = roleGrants.get(key);\n\treturn {\n\t\t...source,\n\t\troleAuthorized:\n\t\t\tgrant?.userId === source.userId && grant.chatId === source.chatId && grant.threadId === source.threadId,\n\t};\n}\n\nexport function rememberGatewayRole(key: string, source: MessageEvent[\"source\"]): void {\n\tif (source.roleAuthorized === true)\n\t\troleGrants.set(key, { userId: source.userId, chatId: source.chatId, threadId: source.threadId });\n\telse roleGrants.delete(key);\n}\n\n/** Fresh delivery authorization shared by previews, final edits and durable retries. */\nexport function canDeliverGatewayText(\n\tkey: string,\n\tsource: MessageEvent[\"source\"],\n\tfallbackConfig?: GatewayConfig,\n\tpairing: PairingStore = createPairingStore(),\n): boolean {\n\tconst binding = conversationBinding(key);\n\tconst cfg = existsSync(gatewayConfigPath()) ? loadGatewayConfig() : (fallbackConfig ?? loadGatewayConfig());\n\treturn (\n\t\t(!binding ||\n\t\t\t(binding.source.platform === source.platform &&\n\t\t\t\tbinding.source.chatId === source.chatId &&\n\t\t\t\tbinding.source.userId === source.userId &&\n\t\t\t\tbinding.source.threadId === source.threadId &&\n\t\t\t\t(!binding.owner || binding.owner === source.userId))) &&\n\t\tisAuthorized(sourceWithCurrentRole(key, source), cfg, pairing)\n\t);\n}\n\nfunction canDeliver(key: string): boolean {\n\tconst binding = conversationBinding(key);\n\treturn !!binding && canDeliverGatewayText(key, binding.source);\n}\n\nexport function gatewayDestination(key: string): { adapter: PlatformAdapter; source: MessageEvent[\"source\"] } {\n\tconst binding = conversationBinding(key);\n\tconst adapter = binding && adapters.get(binding.source.platform);\n\tif (!binding || !adapter || !canDeliver(key))\n\t\tthrow new Error(\"This chat is no longer authorized or its platform is disconnected.\");\n\treturn { adapter, source: binding.source };\n}\n\nexport function withGatewayPresentation<T>(key: string, run: () => Promise<T>): Promise<T> {\n\tconst { adapter, source } = gatewayDestination(key);\n\treturn runWithApprovalContext({ adapter, source, key }, run);\n}\n\nexport function startGatewayPresenter(connected: Map<string, PlatformAdapter>): void {\n\tadapters = connected;\n\tif (existsSync(path())) {\n\t\ttry {\n\t\t\tconst raw: unknown = JSON.parse(readFileSync(path(), \"utf8\"));\n\t\t\tif (\n\t\t\t\t!Array.isArray(raw) ||\n\t\t\t\t!raw.every(\n\t\t\t\t\t(item) =>\n\t\t\t\t\t\titem && typeof item.id === \"string\" && typeof item.key === \"string\" && typeof item.text === \"string\",\n\t\t\t\t)\n\t\t\t)\n\t\t\t\tthrow new Error(\"Invalid outbox\");\n\t\t\toutbox = raw as Outbound[];\n\t\t} catch {\n\t\t\tthrow new Error(\"Gateway outbox is invalid. Restore it before starting.\");\n\t\t}\n\t} else outbox = [];\n\tfor (const item of outbox) {\n\t\tif (item.kind === \"notice\") epochs.set(item.key, Math.max(epochs.get(item.key) ?? 0, item.epoch ?? 0));\n\t}\n\tif (timer) clearInterval(timer);\n\ttimer = setInterval(() => {\n\t\tvoid flushOutbox().catch((error) => console.error(\"[gateway] outbox flush failed:\", error));\n\t}, 3000);\n\ttimer.unref();\n\tvoid flushOutbox().catch((error) => console.error(\"[gateway] outbox flush failed:\", error));\n}\n\nexport function stopGatewayPresenter(): void {\n\tif (timer) clearInterval(timer);\n\ttimer = undefined;\n\tfor (const key of new Set([...sessionIdentities.keys(), ...dialogs.keys()])) invalidateGatewaySession(key);\n\tadapters = new Map();\n\tdeliveryConfigs.clear();\n\troleGrants.clear();\n}\n\nexport function gatewayEpoch(key: string): number {\n\treturn epochs.get(key) ?? 0;\n}\n\nexport async function sendGatewayNotice(\n\tkey: string,\n\ttext: string,\n\tkind: \"notice\" | \"result\" = \"notice\",\n\tepoch = gatewayEpoch(key),\n): Promise<void> {\n\tconst clean = text.replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, \"\").trim();\n\tif (!clean || !canDeliver(key)) return;\n\tconst { adapter, source } = gatewayDestination(key);\n\tawait queueGatewayText(key, source, adapter, clean, { kind, epoch });\n}\n\nexport async function queueGatewayText(\n\tkey: string,\n\tsource: MessageEvent[\"source\"],\n\tadapter: PlatformAdapter,\n\ttext: string,\n\toptions: { kind: \"result\" | \"notice\"; replyTo?: string; cfg?: GatewayConfig; chunks?: string[]; epoch?: number },\n): Promise<void> {\n\tif (!text || (options.kind === \"notice\" && options.epoch !== undefined && options.epoch !== gatewayEpoch(key)))\n\t\treturn;\n\tadapters.set(source.platform, adapter);\n\tif (options.cfg) deliveryConfigs.set(key, options.cfg);\n\tconst chunks = options.chunks ?? splitMessage(text, adapter.maxMessageLength);\n\tconst batchId = randomUUID();\n\tfor (const [index, chunk] of chunks.entries())\n\t\toutbox.push({\n\t\t\tid: randomUUID(),\n\t\t\tbatchId,\n\t\t\tkey,\n\t\t\tsource: { ...source, roleAuthorized: undefined },\n\t\t\tepoch: options.epoch ?? gatewayEpoch(key),\n\t\t\ttext: chunk,\n\t\t\tkind: options.kind,\n\t\t\treplyTo: index === 0 ? options.replyTo : undefined,\n\t\t});\n\tatomicJson(path(), outbox);\n\tif (activeDeliveries.has(key)) await activeDeliveries.get(key);\n\tawait flushKey(key);\n}\n\nexport function flushGatewayOutbox(): Promise<void> {\n\treturn flushOutbox();\n}\n\nfunction flushOutbox(): Promise<void> {\n\treturn Promise.all([...new Set(outbox.map((item) => item.key))].map(flushKey)).then(() => {});\n}\n\nfunction flushKey(key: string): Promise<void> {\n\tconst current = activeDeliveries.get(key);\n\tif (current) return current;\n\tconst running = drainKey(key).finally(() => {\n\t\tactiveDeliveries.delete(key);\n\t});\n\tactiveDeliveries.set(key, running);\n\treturn running;\n}\n\nasync function drainKey(key: string): Promise<void> {\n\tconst failedBatches = new Set(outbox.filter((item) => item.failed && item.batchId).map((item) => item.batchId));\n\tfor (const item of [...outbox].filter((item) => item.key === key)) {\n\t\tif (item.failed || (item.batchId && failedBatches.has(item.batchId))) continue;\n\t\tif (item.kind === \"notice\" && item.epoch !== (epochs.get(item.key) ?? 0)) {\n\t\t\toutbox = outbox.filter((v) => v.id !== item.id);\n\t\t\tatomicJson(path(), outbox);\n\t\t\tcontinue;\n\t\t}\n\t\tconst binding = conversationBinding(item.key);\n\t\tconst source = item.source ?? binding?.source;\n\t\tif (!source) break;\n\t\tif (!canDeliverGatewayText(key, source, deliveryConfigs.get(key))) {\n\t\t\toutbox = outbox.filter((v) => v.id !== item.id);\n\t\t\tatomicJson(path(), outbox);\n\t\t\tcontinue;\n\t\t}\n\t\tconst adapter = adapters.get(source.platform);\n\t\tif (!adapter || (item.nextAttempt ?? 0) > Date.now()) break;\n\t\tconst opts: SendOptions = { threadId: source.threadId, replyTo: item.replyTo };\n\t\tconst result = await adapter\n\t\t\t.send(source.chatId, item.text, opts)\n\t\t\t.catch((error: unknown) => ({ success: false as const, error: String(error), retryable: true }));\n\t\tif (result.success) {\n\t\t\toutbox = outbox.filter((v) => v.id !== item.id);\n\t\t\tatomicJson(path(), outbox);\n\t\t} else {\n\t\t\titem.attempts = (item.attempts ?? 0) + 1;\n\t\t\titem.failed = result.retryable === false || item.attempts >= MAX_ATTEMPTS;\n\t\t\titem.nextAttempt = Date.now() + Math.min(60_000, 3000 * 2 ** (item.attempts - 1));\n\t\t\tif (item.failed && item.batchId) {\n\t\t\t\tfailedBatches.add(item.batchId);\n\t\t\t\tfor (const pending of outbox) if (pending.batchId === item.batchId) pending.failed = true;\n\t\t\t}\n\t\t\tatomicJson(path(), outbox);\n\t\t\tconsole.error(\n\t\t\t\t`[gateway] delivery ${item.failed ? \"failed permanently\" : \"retry scheduled\"} for ${source.platform}: ${result.error ?? \"unknown error\"}`,\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nexport function invalidateGatewayDialogs(key: string): void {\n\tinvalidateGatewayPickers(key);\n\tepochs.set(key, (epochs.get(key) ?? 0) + 1);\n\tconst remaining = outbox.filter((item) => item.key !== key || item.kind !== \"notice\");\n\tif (remaining.length !== outbox.length) {\n\t\toutbox = remaining;\n\t\tatomicJson(path(), outbox);\n\t}\n\tfor (const cancel of dialogs.get(key) ?? []) cancel();\n\tdialogs.delete(key);\n\tinputs.delete(key);\n}\n\nexport function acceptGatewayInput(key: string, event: MessageEvent): boolean {\n\tconst input = inputs.get(key);\n\tif (!input || input.userId !== event.source.userId || !canDeliver(key)) return false;\n\tif (event.text.startsWith(\"/\") && event.text !== \"/cancel\") return false;\n\tinput.resolve(event.text === \"/cancel\" ? undefined : event.text);\n\treturn true;\n}\n\ninterface GatewayDialogRequest {\n\tgeneration: number;\n\tsource: MessageEvent[\"source\"];\n}\n\nfunction dialog(\n\tkey: string,\n\topts: ExtensionUIDialogOptions | undefined,\n\tshow: (done: (value: string | undefined) => void, valid: () => boolean) => Promise<void>,\n\trequest?: GatewayDialogRequest,\n): Promise<string | undefined> {\n\tif (request && request.generation !== gatewayPickerGeneration(key)) return Promise.resolve(undefined);\n\tconst epoch = epochs.get(key) ?? 0;\n\tconst source = request?.source ?? conversationBinding(key)?.source;\n\treturn new Promise((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst finish = (value: string | undefined, error?: unknown) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timeout);\n\t\t\topts?.signal?.removeEventListener(\"abort\", cancel);\n\t\t\tif (inputs.get(key)?.resolve === done) inputs.delete(key);\n\t\t\tdialogs.get(key)?.delete(cancel);\n\t\t\tif (error !== undefined) reject(error);\n\t\t\telse resolve(value);\n\t\t};\n\t\tconst done = (value: string | undefined) => finish(value);\n\t\tconst cancel = () => done(undefined);\n\t\tconst timeout = setTimeout(cancel, opts?.timeout ?? 300_000);\n\t\tconst set = dialogs.get(key) ?? new Set();\n\t\tset.add(cancel);\n\t\tdialogs.set(key, set);\n\t\topts?.signal?.addEventListener(\"abort\", cancel, { once: true });\n\t\tif (opts?.signal?.aborted) {\n\t\t\tcancel();\n\t\t\treturn;\n\t\t}\n\t\tvoid show(\n\t\t\tdone,\n\t\t\t() => !settled && (epochs.get(key) ?? 0) === epoch && !!source && canDeliverGatewayText(key, source),\n\t\t).catch((error) => {\n\t\t\tfinish(undefined, error);\n\t\t});\n\t});\n}\n\nexport function gatewaySelect(\n\tkey: string,\n\ttitle: string,\n\toptions: string[],\n\topts?: ExtensionUIDialogOptions,\n\trequest?: GatewayDialogRequest,\n): Promise<string | undefined> {\n\treturn dialog(\n\t\tkey,\n\t\topts,\n\t\tasync (done, valid) => {\n\t\t\tconst { adapter, source: destination } = gatewayDestination(key);\n\t\t\tconst source = request?.source ?? destination;\n\t\t\tconst result = await createPicker(\n\t\t\t\tadapter,\n\t\t\t\tsource,\n\t\t\t\t{\n\t\t\t\t\tkind: \"dialog\",\n\t\t\t\t\tgeneration: request?.generation,\n\t\t\t\t\tsessionKey: key,\n\t\t\t\t\tinvokerId: source.userId,\n\t\t\t\t\ttitle,\n\t\t\t\t\titems: options.map((value) => ({ label: value, value })),\n\t\t\t\t\tvalidate: valid,\n\t\t\t\t\tonCancel: () => done(undefined),\n\t\t\t\t\tresolve: async (item) => {\n\t\t\t\t\t\tdone(item.value);\n\t\t\t\t\t\treturn { done: true, text: `${title}\\n${item.value}` };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ threadId: source.threadId },\n\t\t\t);\n\t\t\tif (!result.success) throw new Error(result.error ?? \"Could not send picker.\");\n\t\t},\n\t\trequest,\n\t);\n}\n\nexport function gatewayInput(\n\tkey: string,\n\ttitle: string,\n\tplaceholder?: string,\n\topts?: ExtensionUIDialogOptions,\n\trequest?: GatewayDialogRequest,\n): Promise<string | undefined> {\n\tif (inputs.has(key)) return Promise.reject(new Error(\"Answer or cancel the existing question first.\"));\n\tconst epoch = gatewayEpoch(key);\n\treturn dialog(\n\t\tkey,\n\t\topts,\n\t\tasync (done, valid) => {\n\t\t\tif (!valid()) {\n\t\t\t\tdone(undefined);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst source = request?.source ?? gatewayDestination(key).source;\n\t\t\tinputs.set(key, { userId: source.userId, resolve: done });\n\t\t\tawait sendGatewayNotice(\n\t\t\t\tkey,\n\t\t\t\t`${title}${placeholder ? `\\n${placeholder}` : \"\"}\\nReply with text, or /cancel.`,\n\t\t\t\t\"notice\",\n\t\t\t\tepoch,\n\t\t\t);\n\t\t},\n\t\trequest,\n\t);\n}\n\nexport function createGatewayUI(key: string): ExtensionUIContext {\n\tconst identity = sessionIdentity(key);\n\tconst source = conversationBinding(key)?.source;\n\tconst valid = () => sessionIdentity(key) === identity && !!source && canDeliverGatewayText(key, source);\n\treturn {\n\t\t...noOpUIContext,\n\t\tselect: (title, options, opts) =>\n\t\t\tvalid() ? gatewaySelect(key, title, options, opts) : Promise.resolve(undefined),\n\t\tconfirm: async (title, message, opts) =>\n\t\t\tvalid() && (await gatewaySelect(key, `${title}\\n${message}`, [\"Approve\", \"Decline\"], opts)) === \"Approve\",\n\t\tinput: (title, placeholder, opts) =>\n\t\t\tvalid() ? gatewayInput(key, title, placeholder, opts) : Promise.resolve(undefined),\n\t\teditor: (title, prefill) => (valid() ? gatewayInput(key, title, prefill) : Promise.resolve(undefined)),\n\t\tnotify: (message) => {\n\t\t\tif (valid()) void sendGatewayNotice(key, message).catch(() => {});\n\t\t},\n\t\tcustom: async () => {\n\t\t\tthrow new Error(\n\t\t\t\t\"This command requires the terminal interface. Use the gateway's text commands or button pickers.\",\n\t\t\t);\n\t\t},\n\t\tpasteToEditor: (text) => {\n\t\t\tif (valid()) void sendGatewayNotice(key, text).catch(() => {});\n\t\t},\n\t\tsetEditorText: (text) => {\n\t\t\tif (valid()) void sendGatewayNotice(key, text).catch(() => {});\n\t\t},\n\t};\n}\n"]}
@@ -15,7 +15,7 @@
15
15
  * acknowledges chunks in order and retries by destination.
16
16
  * Errors surface as a compact "⚠ <one-line>" — never a stack trace.
17
17
  */
18
- import { type BridgeSession, type BridgeSessionStatus, type TurnCallbacks } from "./agent-bridge.ts";
18
+ import { type BridgeSession, type BridgeSessionStatus, type ResetOptions, type TurnCallbacks } from "./agent-bridge.ts";
19
19
  import { type GatewayConfig } from "./config.ts";
20
20
  import type { PairingStore } from "./pairing.ts";
21
21
  import type { MessageEvent, PlatformAdapter } from "./types.ts";
@@ -23,7 +23,7 @@ import type { MessageEvent, PlatformAdapter } from "./types.ts";
23
23
  export interface BridgeLike {
24
24
  runTurn(key: string, event: MessageEvent, callbacks: TurnCallbacks): Promise<string>;
25
25
  abort(key: string): Promise<void> | void;
26
- reset(key: string): void | Promise<void>;
26
+ reset(key: string, options?: ResetOptions): void | Promise<void>;
27
27
  getStatus(key: string): BridgeSessionStatus;
28
28
  getSession(key: string, create?: boolean): Promise<BridgeSession | null>;
29
29
  peekSession?(key: string): BridgeSession | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/gateway/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,mBAAmB,EAAU,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAI7G,OAAO,EAAE,KAAK,aAAa,EAA2D,MAAM,aAAa,CAAC;AAG1G,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAYjD,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEhE,yEAAyE;AACzE,MAAM,WAAW,UAAU;IAC1B,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrF,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACzC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAAC;IAC5C,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IACzE,WAAW,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IACrD,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACvC,GAAG,EAAE,aAAa,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,UAAU,CAAC;IACnB,8HAA8H;IAC9H,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,MAAM;IACtB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAcD,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAiSrD","sourcesContent":["/**\n * lunR: gateway router.\n *\n * Every inbound MessageEvent flows through handleEvent:\n * 1. group gating (allowedChats / freeResponseChats / requireMention,\n * with adapter-supplied metadata.mentionedBot)\n * 2. authorization (authz.ts); denied DMs get a pairing code when the\n * behavior is \"pair\" and the user isn't rate-limited\n * 3. command registry (/new, /undo, /redo, /model, /sessions, /title,\n * /context, /compact, /thinking, /stop, /status, /help, /whoami)\n * 4. normal path: bridge.runTurn with a StreamConsumer when streaming is\n * enabled and the adapter supports edit; the final text is\n * silence-filtered and folded into a successful streaming preview edit.\n * Failed final edits and ordinary answers enter the durable outbox, which\n * acknowledges chunks in order and retries by destination.\n * Errors surface as a compact \"⚠ <one-line>\" — never a stack trace.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { type BridgeSession, type BridgeSessionStatus, QUEUED, type TurnCallbacks } from \"./agent-bridge.ts\";\nimport { registerGatewayApprovalHandler, runWithApprovalContext } from \"./approval.ts\";\nimport { isAuthorized, requireAuthorized } from \"./authz.ts\";\nimport { CHAT_COMMANDS, runChatCommand, sendCommandReply } from \"./commands.ts\";\nimport { type GatewayConfig, gatewayConfigPath, loadGatewayConfig, platformConfigFor } from \"./config.ts\";\nimport { bindConversation, conversationBinding } from \"./conversations.ts\";\nimport { resolveWithinRoots } from \"./mobile-commands.ts\";\nimport type { PairingStore } from \"./pairing.ts\";\nimport {\n\tacceptGatewayInput,\n\tgatewayEpoch,\n\tinvalidateGatewayDialogs,\n\tqueueGatewayText,\n\trememberGatewayRole,\n} from \"./presenter.ts\";\nimport { buildSessionKey } from \"./session-keys.ts\";\nimport { getSession as getStoredSession } from \"./store.ts\";\nimport { applySilenceFilter, StreamConsumer } from \"./stream.ts\";\nimport { splitMessage } from \"./text.ts\";\nimport type { MessageEvent, PlatformAdapter } from \"./types.ts\";\n\n/** Structural bridge shape (AgentBridge satisfies it; tests fake it). */\nexport interface BridgeLike {\n\trunTurn(key: string, event: MessageEvent, callbacks: TurnCallbacks): Promise<string>;\n\tabort(key: string): Promise<void> | void;\n\treset(key: string): void | Promise<void>;\n\tgetStatus(key: string): BridgeSessionStatus;\n\tgetSession(key: string, create?: boolean): Promise<BridgeSession | null>;\n\tpeekSession?(key: string): BridgeSession | undefined;\n\tswitchSession(key: string, sessionFile: string): Promise<void>;\n\tundo(key: string): Promise<{ userText: string }>;\n\tredo(key: string): Promise<void>;\n}\n\nexport interface RouterDeps {\n\tadapters: Map<string, PlatformAdapter>;\n\tcfg: GatewayConfig;\n\tpairing: PairingStore;\n\tbridge: BridgeLike;\n\t/** Reload gateway.json on every inbound event so CLI config edits (e.g. pair approve) are picked up by the running daemon. */\n\treloadConfig?: boolean;\n\tremoteControls?: boolean;\n}\n\nexport interface Router {\n\thandleEvent(event: MessageEvent): Promise<void>;\n}\n\nfunction oneLine(text: string): string {\n\treturn text.replace(/\\s+/g, \" \").trim().slice(0, 200);\n}\n\nfunction formatPairingCode(code: string): string {\n\treturn code.length === 8 ? `${code.slice(0, 4)}-${code.slice(4)}` : code;\n}\n\nfunction mentionedBot(event: MessageEvent): boolean {\n\treturn event.metadata?.mentionedBot === true;\n}\n\nexport function createRouter(deps: RouterDeps): Router {\n\tconst { adapters, cfg: initialCfg, pairing, bridge, reloadConfig } = deps;\n\n\t// Headless gateway sessions still prompt for large subagent launches in yolo mode.\n\tregisterGatewayApprovalHandler();\n\n\t/** Reload gateway.json on every inbound event so CLI edits (e.g. pair approve) take effect immediately. */\n\tfunction freshCfg(): GatewayConfig {\n\t\tif (!reloadConfig) return initialCfg;\n\t\ttry {\n\t\t\tif (existsSync(gatewayConfigPath())) {\n\t\t\t\treturn loadGatewayConfig();\n\t\t\t}\n\t\t} catch {\n\t\t\t// fall through to initial config\n\t\t}\n\t\treturn initialCfg;\n\t}\n\n\tasync function sendError(\n\t\tadapter: PlatformAdapter,\n\t\tevent: MessageEvent,\n\t\tkey: string,\n\t\tcfg: GatewayConfig,\n\t\tmessage: string,\n\t\tepoch: number,\n\t): Promise<void> {\n\t\tawait queueGatewayText(key, event.source, adapter, `⚠ ${oneLine(message)}`, {\n\t\t\tkind: \"notice\",\n\t\t\treplyTo: event.messageId,\n\t\t\tcfg,\n\t\t\tepoch,\n\t\t});\n\t}\n\n\t/** Step 1: group-chat early gating. Returns true when the event must be dropped. */\n\tfunction isGroupGated(event: MessageEvent, cfg: GatewayConfig): boolean {\n\t\tconst { source } = event;\n\t\tif (source.chatType === \"dm\") return false;\n\t\tconst platformCfg = platformConfigFor(cfg, source.platform);\n\t\tif (!platformCfg) return true;\n\t\tconst freeResponse = platformCfg.freeResponseChats.includes(source.chatId);\n\t\tif (\n\t\t\tplatformCfg.allowedChats.length > 0 &&\n\t\t\t!platformCfg.allowedChats.includes(source.chatId) &&\n\t\t\t!freeResponse &&\n\t\t\t!mentionedBot(event)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tif (platformCfg.requireMention && !freeResponse && !mentionedBot(event)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Step 2: authorization; handles the denied-DM pairing flow. Returns true when denied. */\n\tasync function isDenied(adapter: PlatformAdapter, event: MessageEvent, cfg: GatewayConfig): Promise<boolean> {\n\t\tif (isAuthorized(event.source, cfg, pairing)) return false;\n\t\tconst { source } = event;\n\t\tif (source.chatType !== \"dm\") return true; // denied group: silent\n\t\tif (cfg.unauthorizedDmBehavior !== \"pair\") return true;\n\t\tconst code = pairing.issueCode(source.platform, source.userId);\n\t\tif (code === null) return true; // rate-limited or pending list full: stay silent\n\t\tawait adapter.send(\n\t\t\tsource.chatId,\n\t\t\t`Your lunR pairing code: ${formatPairingCode(code)}. On the computer running lunr, approve access with: lunr gateway pair approve ${source.platform} ${formatPairingCode(code)}. Approval grants full gateway access, including local projects and saved sessions.`,\n\t\t\t{ replyTo: event.messageId, threadId: source.threadId },\n\t\t);\n\t\treturn true;\n\t}\n\n\tfunction workspaceReadiness(event: MessageEvent, key: string, cfg: GatewayConfig): string | undefined {\n\t\tif (getStoredSession(key) || conversationBinding(key)?.cwd) return undefined;\n\t\tconst root = cfg.defaultProject;\n\t\tif (!root) return \"No default project selected. Use /project to choose an approved folder.\";\n\t\ttry {\n\t\t\tconst cwd = resolveWithinRoots(root, cfg.projectRoots ?? []);\n\t\t\tbindConversation(key, event.source, { cwd, owner: event.source.userId });\n\t\t\treturn undefined;\n\t\t} catch {\n\t\t\treturn \"Default project is no longer an approved folder. Use /project to choose an approved folder.\";\n\t\t}\n\t}\n\n\t/** Step 3: command registry. Returns true when the event was consumed. */\n\tasync function handleSlash(adapter: PlatformAdapter, event: MessageEvent, key: string): Promise<boolean> {\n\t\tconst text = event.text.trim();\n\t\tif (!text.startsWith(\"/\")) return false;\n\t\tconst firstToken = text.split(/\\s+/, 1)[0].toLowerCase();\n\t\tconst commandWord = firstToken.split(\"@\")[0].slice(1);\n\t\tconst args = text.slice(firstToken.length).trim();\n\t\tif (deps.remoteControls) {\n\t\t\tconst { handleMobileCommand, cancelMobileTransfer } = await import(\"./mobile-commands.ts\");\n\t\t\tif ([\"stop\", \"stopall\", \"new\", \"cancel\"].includes(commandWord)) cancelMobileTransfer(key);\n\t\t\tif (commandWord === \"cancel\") {\n\t\t\t\tinvalidateGatewayDialogs(key);\n\t\t\t\tawait adapter.send(event.source.chatId, \"Cancelled the pending selection or transfer.\", {\n\t\t\t\t\tthreadId: event.source.threadId,\n\t\t\t\t});\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tconst consumed = await runWithApprovalContext({ key, adapter, source: event.source }, () =>\n\t\t\t\thandleMobileCommand({ key, event, adapter, bridge, cfg: freshCfg() }, commandWord, args),\n\t\t\t);\n\t\t\tif (consumed) return true;\n\t\t\tif (!event.text.startsWith(\"/\")) return false;\n\t\t}\n\t\tif (commandWord === \"start\") {\n\t\t\tconst issue = deps.remoteControls ? workspaceReadiness(event, key, freshCfg()) : undefined;\n\t\t\tif (issue) {\n\t\t\t\tawait sendCommandReply(adapter, event, issue);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst session = await bridge.getSession(key, true);\n\t\t\t\tconst models = await session?.modelRuntime.getAvailable();\n\t\t\t\tif (!models?.length)\n\t\t\t\t\tawait sendCommandReply(\n\t\t\t\t\t\tadapter,\n\t\t\t\t\t\tevent,\n\t\t\t\t\t\t\"No authenticated model available. Use /login on your computer, then try /start again.\",\n\t\t\t\t\t);\n\t\t\t\telse if (\n\t\t\t\t\t!session?.model ||\n\t\t\t\t\t!models.some((model) => model.id === session.model?.id && model.provider === session.model.provider)\n\t\t\t\t)\n\t\t\t\t\tawait sendCommandReply(adapter, event, \"No available model selected. Use /model here to choose one.\");\n\t\t\t\telse\n\t\t\t\t\tawait sendCommandReply(\n\t\t\t\t\t\tadapter,\n\t\t\t\t\t\tevent,\n\t\t\t\t\t\t\"Ready. Send a message to begin, or use /model to choose a model.\",\n\t\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\tawait sendCommandReply(\n\t\t\t\t\tadapter,\n\t\t\t\t\tevent,\n\t\t\t\t\t\"Session setup failed. Run lunr gateway doctor on your computer, then try /start again.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tif (deps.remoteControls && commandWord === \"model\") {\n\t\t\tconst issue = workspaceReadiness(event, key, freshCfg());\n\t\t\tif (issue) {\n\t\t\t\tawait sendCommandReply(adapter, event, issue);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tconst cmd = CHAT_COMMANDS.find((c) => c.name === commandWord || c.aliases?.includes(commandWord));\n\t\tif (!cmd) {\n\t\t\treturn event.source.chatType !== \"dm\";\n\t\t}\n\t\tconst ctx = {\n\t\t\tevent,\n\t\t\tkey,\n\t\t\tadapter,\n\t\t\tbridge,\n\t\t\tcfg: freshCfg(),\n\t\t\targs,\n\t\t\treply: (message: string) => sendCommandReply(adapter, event, message),\n\t\t};\n\t\treturn runWithApprovalContext({ key, adapter, source: event.source }, () => runChatCommand(cmd, ctx));\n\t}\n\n\t/** A successful preview is finalized by edit; failed edits fall back to a durable full reply. */\n\tasync function deliver(\n\t\tadapter: PlatformAdapter,\n\t\tevent: MessageEvent,\n\t\ttext: string,\n\t\tkey: string,\n\t\tcfg: GatewayConfig,\n\t\topts: { reply: boolean; editMessageId?: string; previewTruncated?: boolean },\n\t): Promise<void> {\n\t\tconst filtered = applySilenceFilter(text);\n\t\tif (filtered === null) return;\n\t\tif (opts.editMessageId) {\n\t\t\tconst chunks = splitMessage(filtered, adapter.maxMessageLength);\n\t\t\tconst edit = await adapter\n\t\t\t\t.editMessage(event.source.chatId, opts.editMessageId, opts.previewTruncated ? chunks[0] : filtered)\n\t\t\t\t.catch(() => ({ success: false }));\n\t\t\tif (edit.success) {\n\t\t\t\tif (!opts.previewTruncated) return;\n\t\t\t\tawait queueGatewayText(key, event.source, adapter, chunks.slice(1).join(\"\"), {\n\t\t\t\t\tkind: \"result\",\n\t\t\t\t\tcfg,\n\t\t\t\t\tchunks: chunks.slice(1),\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tawait queueGatewayText(key, event.source, adapter, filtered, {\n\t\t\tkind: \"result\",\n\t\t\treplyTo: opts.reply ? event.messageId : undefined,\n\t\t\tcfg,\n\t\t});\n\t}\n\n\tasync function runTurn(\n\t\tadapter: PlatformAdapter,\n\t\tevent: MessageEvent,\n\t\tkey: string,\n\t\tcfg: GatewayConfig,\n\t): Promise<void> {\n\t\tconst epoch = gatewayEpoch(key);\n\t\tconst streaming = cfg.streaming.enabled && typeof adapter.editMessage === \"function\";\n\t\tlet consumer: StreamConsumer | undefined;\n\t\tif (streaming) {\n\t\t\tconsumer = new StreamConsumer({\n\t\t\t\tsendInitial: async (text) => {\n\t\t\t\t\tconst result = await adapter.send(event.source.chatId, text, {\n\t\t\t\t\t\treplyTo: event.messageId,\n\t\t\t\t\t\tthreadId: event.source.threadId,\n\t\t\t\t\t});\n\t\t\t\t\treturn result.success ? (result.messageId ?? null) : null;\n\t\t\t\t},\n\t\t\t\tedit: async (messageId, text) => {\n\t\t\t\t\tconst result = await adapter.editMessage(event.source.chatId, messageId, text);\n\t\t\t\t\tif (!result.success) throw new Error(result.error ?? \"Preview edit failed\");\n\t\t\t\t},\n\t\t\t\tintervalMs: cfg.streaming.editIntervalMs,\n\t\t\t\tthreshold: cfg.streaming.bufferThreshold,\n\t\t\t\tmaxPreview: adapter.maxMessageLength,\n\t\t\t});\n\t\t}\n\n\t\tconst callbacks: TurnCallbacks = {\n\t\t\tonDelta: consumer ? (delta) => consumer.push(delta) : undefined,\n\t\t\tonFollowUpResult: (text) => {\n\t\t\t\tvoid deliver(adapter, event, text, key, cfg, { reply: false }).catch((error) =>\n\t\t\t\t\tconsole.error(\"[gateway] follow-up delivery failed:\", error),\n\t\t\t\t);\n\t\t\t},\n\t\t\tonError: (message) => {\n\t\t\t\tvoid sendError(adapter, event, key, cfg, message, epoch).catch((error) =>\n\t\t\t\t\tconsole.error(\"[gateway] error delivery failed:\", error),\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\n\t\tconst result = await runWithApprovalContext({ key, adapter, source: event.source }, () =>\n\t\t\tbridge.runTurn(key, event, callbacks),\n\t\t);\n\t\tif (result === QUEUED) return; // queued behind a running turn: no reply\n\t\tif (consumer) await consumer.finalize();\n\t\tawait deliver(adapter, event, result, key, cfg, {\n\t\t\treply: !consumer,\n\t\t\teditMessageId: consumer?.sentMessageId ?? undefined,\n\t\t\tpreviewTruncated: consumer?.truncated ?? false,\n\t\t});\n\t}\n\n\treturn {\n\t\tasync handleEvent(event: MessageEvent): Promise<void> {\n\t\t\tconst adapter = adapters.get(event.source.platform);\n\t\t\tif (!adapter) return;\n\t\t\tconst cfg = freshCfg();\n\t\t\tconst key = buildSessionKey(event.source, { groupSessionsPerUser: cfg.groupSessionsPerUser });\n\t\t\tconst epoch = gatewayEpoch(key);\n\t\t\ttry {\n\t\t\t\tif (isGroupGated(event, cfg)) return;\n\t\t\t\trememberGatewayRole(key, event.source);\n\t\t\t\tif (await isDenied(adapter, event, cfg)) return;\n\t\t\t\tif (deps.remoteControls) {\n\t\t\t\t\tconst binding = conversationBinding(key);\n\t\t\t\t\tif (binding?.owner) {\n\t\t\t\t\t\trequireAuthorized(event.source, cfg);\n\t\t\t\t\t\tif (binding.owner !== event.source.userId)\n\t\t\t\t\t\t\tthrow new Error(\"This session belongs to a different user.\");\n\t\t\t\t\t}\n\t\t\t\t\tbindConversation(key, event.source);\n\t\t\t\t\tif (acceptGatewayInput(key, event)) return;\n\t\t\t\t}\n\t\t\t\tif (await handleSlash(adapter, event, key)) return;\n\t\t\t\tconst issue = deps.remoteControls ? workspaceReadiness(event, key, cfg) : undefined;\n\t\t\t\tif (issue) {\n\t\t\t\t\tawait sendCommandReply(adapter, event, issue);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait adapter.sendTyping(event.source.chatId, event.source.threadId).catch(() => {});\n\t\t\t\tawait runTurn(adapter, event, key, cfg);\n\t\t\t} catch (err) {\n\t\t\t\tawait sendError(adapter, event, key, cfg, err instanceof Error ? err.message : String(err), epoch).catch(\n\t\t\t\t\t() => {},\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/gateway/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,mBAAmB,EAExB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EAAE,KAAK,aAAa,EAA2D,MAAM,aAAa,CAAC;AAG1G,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAajD,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEhE,yEAAyE;AACzE,MAAM,WAAW,UAAU;IAC1B,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrF,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACzC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAAC;IAC5C,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IACzE,WAAW,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IACrD,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACvC,GAAG,EAAE,aAAa,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,UAAU,CAAC;IACnB,8HAA8H;IAC9H,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,MAAM;IACtB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAcD,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CA2SrD","sourcesContent":["/**\n * lunR: gateway router.\n *\n * Every inbound MessageEvent flows through handleEvent:\n * 1. group gating (allowedChats / freeResponseChats / requireMention,\n * with adapter-supplied metadata.mentionedBot)\n * 2. authorization (authz.ts); denied DMs get a pairing code when the\n * behavior is \"pair\" and the user isn't rate-limited\n * 3. command registry (/new, /undo, /redo, /model, /sessions, /title,\n * /context, /compact, /thinking, /stop, /status, /help, /whoami)\n * 4. normal path: bridge.runTurn with a StreamConsumer when streaming is\n * enabled and the adapter supports edit; the final text is\n * silence-filtered and folded into a successful streaming preview edit.\n * Failed final edits and ordinary answers enter the durable outbox, which\n * acknowledges chunks in order and retries by destination.\n * Errors surface as a compact \"⚠ <one-line>\" — never a stack trace.\n */\n\nimport { existsSync } from \"node:fs\";\nimport {\n\ttype BridgeSession,\n\ttype BridgeSessionStatus,\n\tQUEUED,\n\ttype ResetOptions,\n\ttype TurnCallbacks,\n} from \"./agent-bridge.ts\";\nimport { registerGatewayApprovalHandler, runWithApprovalContext } from \"./approval.ts\";\nimport { isAuthorized, requireAuthorized } from \"./authz.ts\";\nimport { CHAT_COMMANDS, runChatCommand, sendCommandReply } from \"./commands.ts\";\nimport { type GatewayConfig, gatewayConfigPath, loadGatewayConfig, platformConfigFor } from \"./config.ts\";\nimport { bindConversation, conversationBinding } from \"./conversations.ts\";\nimport { resolveWithinRoots } from \"./mobile-commands.ts\";\nimport type { PairingStore } from \"./pairing.ts\";\nimport {\n\tacceptGatewayInput,\n\tcanDeliverGatewayText,\n\tgatewayEpoch,\n\tinvalidateGatewayDialogs,\n\tqueueGatewayText,\n\trememberGatewayRole,\n} from \"./presenter.ts\";\nimport { buildSessionKey } from \"./session-keys.ts\";\nimport { getSession as getStoredSession } from \"./store.ts\";\nimport { applySilenceFilter, StreamConsumer } from \"./stream.ts\";\nimport { splitMessage } from \"./text.ts\";\nimport type { MessageEvent, PlatformAdapter } from \"./types.ts\";\n\n/** Structural bridge shape (AgentBridge satisfies it; tests fake it). */\nexport interface BridgeLike {\n\trunTurn(key: string, event: MessageEvent, callbacks: TurnCallbacks): Promise<string>;\n\tabort(key: string): Promise<void> | void;\n\treset(key: string, options?: ResetOptions): void | Promise<void>;\n\tgetStatus(key: string): BridgeSessionStatus;\n\tgetSession(key: string, create?: boolean): Promise<BridgeSession | null>;\n\tpeekSession?(key: string): BridgeSession | undefined;\n\tswitchSession(key: string, sessionFile: string): Promise<void>;\n\tundo(key: string): Promise<{ userText: string }>;\n\tredo(key: string): Promise<void>;\n}\n\nexport interface RouterDeps {\n\tadapters: Map<string, PlatformAdapter>;\n\tcfg: GatewayConfig;\n\tpairing: PairingStore;\n\tbridge: BridgeLike;\n\t/** Reload gateway.json on every inbound event so CLI config edits (e.g. pair approve) are picked up by the running daemon. */\n\treloadConfig?: boolean;\n\tremoteControls?: boolean;\n}\n\nexport interface Router {\n\thandleEvent(event: MessageEvent): Promise<void>;\n}\n\nfunction oneLine(text: string): string {\n\treturn text.replace(/\\s+/g, \" \").trim().slice(0, 200);\n}\n\nfunction formatPairingCode(code: string): string {\n\treturn code.length === 8 ? `${code.slice(0, 4)}-${code.slice(4)}` : code;\n}\n\nfunction mentionedBot(event: MessageEvent): boolean {\n\treturn event.metadata?.mentionedBot === true;\n}\n\nexport function createRouter(deps: RouterDeps): Router {\n\tconst { adapters, cfg: initialCfg, pairing, bridge, reloadConfig } = deps;\n\n\t// Headless gateway sessions still prompt for large subagent launches in yolo mode.\n\tregisterGatewayApprovalHandler();\n\n\t/** Reload gateway.json on every inbound event so CLI edits (e.g. pair approve) take effect immediately. */\n\tfunction freshCfg(): GatewayConfig {\n\t\tif (!reloadConfig) return initialCfg;\n\t\ttry {\n\t\t\tif (existsSync(gatewayConfigPath())) {\n\t\t\t\treturn loadGatewayConfig();\n\t\t\t}\n\t\t} catch {\n\t\t\t// fall through to initial config\n\t\t}\n\t\treturn initialCfg;\n\t}\n\n\tasync function sendError(\n\t\tadapter: PlatformAdapter,\n\t\tevent: MessageEvent,\n\t\tkey: string,\n\t\tcfg: GatewayConfig,\n\t\tmessage: string,\n\t\tepoch: number,\n\t): Promise<void> {\n\t\tawait queueGatewayText(key, event.source, adapter, `⚠ ${oneLine(message)}`, {\n\t\t\tkind: \"notice\",\n\t\t\treplyTo: event.messageId,\n\t\t\tcfg,\n\t\t\tepoch,\n\t\t});\n\t}\n\n\t/** Step 1: group-chat early gating. Returns true when the event must be dropped. */\n\tfunction isGroupGated(event: MessageEvent, cfg: GatewayConfig): boolean {\n\t\tconst { source } = event;\n\t\tif (source.chatType === \"dm\") return false;\n\t\tconst platformCfg = platformConfigFor(cfg, source.platform);\n\t\tif (!platformCfg) return true;\n\t\tconst freeResponse = platformCfg.freeResponseChats.includes(source.chatId);\n\t\tif (\n\t\t\tplatformCfg.allowedChats.length > 0 &&\n\t\t\t!platformCfg.allowedChats.includes(source.chatId) &&\n\t\t\t!freeResponse &&\n\t\t\t!mentionedBot(event)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tif (platformCfg.requireMention && !freeResponse && !mentionedBot(event)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Step 2: authorization; handles the denied-DM pairing flow. Returns true when denied. */\n\tasync function isDenied(adapter: PlatformAdapter, event: MessageEvent, cfg: GatewayConfig): Promise<boolean> {\n\t\tif (isAuthorized(event.source, cfg, pairing)) return false;\n\t\tconst { source } = event;\n\t\tif (source.chatType !== \"dm\") return true; // denied group: silent\n\t\tif (cfg.unauthorizedDmBehavior !== \"pair\") return true;\n\t\tconst code = pairing.issueCode(source.platform, source.userId);\n\t\tif (code === null) return true; // rate-limited or pending list full: stay silent\n\t\tawait adapter.send(\n\t\t\tsource.chatId,\n\t\t\t`Your lunR pairing code: ${formatPairingCode(code)}. On the computer running lunr, approve access with: lunr gateway pair approve ${source.platform} ${formatPairingCode(code)}. Approval grants full gateway access, including local projects and saved sessions.`,\n\t\t\t{ replyTo: event.messageId, threadId: source.threadId },\n\t\t);\n\t\treturn true;\n\t}\n\n\tfunction workspaceReadiness(event: MessageEvent, key: string, cfg: GatewayConfig): string | undefined {\n\t\tif (getStoredSession(key) || conversationBinding(key)?.cwd) return undefined;\n\t\tconst root = cfg.defaultProject;\n\t\tif (!root) return \"No default project selected. Use /project to choose an approved folder.\";\n\t\ttry {\n\t\t\tconst cwd = resolveWithinRoots(root, cfg.projectRoots ?? []);\n\t\t\tbindConversation(key, event.source, { cwd, owner: event.source.userId });\n\t\t\treturn undefined;\n\t\t} catch {\n\t\t\treturn \"Default project is no longer an approved folder. Use /project to choose an approved folder.\";\n\t\t}\n\t}\n\n\t/** Step 3: command registry. Returns true when the event was consumed. */\n\tasync function handleSlash(adapter: PlatformAdapter, event: MessageEvent, key: string): Promise<boolean> {\n\t\tconst text = event.text.trim();\n\t\tif (!text.startsWith(\"/\")) return false;\n\t\tconst firstToken = text.split(/\\s+/, 1)[0].toLowerCase();\n\t\tconst commandWord = firstToken.split(\"@\")[0].slice(1);\n\t\tconst args = text.slice(firstToken.length).trim();\n\t\tif (deps.remoteControls) {\n\t\t\tconst { handleMobileCommand, cancelMobileTransfer } = await import(\"./mobile-commands.ts\");\n\t\t\tif ([\"stop\", \"stopall\", \"new\", \"cancel\"].includes(commandWord)) cancelMobileTransfer(key);\n\t\t\tif (commandWord === \"cancel\") {\n\t\t\t\tinvalidateGatewayDialogs(key);\n\t\t\t\tawait adapter.send(event.source.chatId, \"Cancelled the pending selection or transfer.\", {\n\t\t\t\t\tthreadId: event.source.threadId,\n\t\t\t\t});\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tconst consumed = await runWithApprovalContext({ key, adapter, source: event.source }, () =>\n\t\t\t\thandleMobileCommand({ key, event, adapter, bridge, cfg: freshCfg() }, commandWord, args),\n\t\t\t);\n\t\t\tif (consumed) return true;\n\t\t\tif (!event.text.startsWith(\"/\")) return false;\n\t\t}\n\t\tif (commandWord === \"start\") {\n\t\t\tconst issue = deps.remoteControls ? workspaceReadiness(event, key, freshCfg()) : undefined;\n\t\t\tif (issue) {\n\t\t\t\tawait sendCommandReply(adapter, event, issue);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst session = await bridge.getSession(key, true);\n\t\t\t\tconst models = await session?.modelRuntime.getAvailable();\n\t\t\t\tif (!models?.length)\n\t\t\t\t\tawait sendCommandReply(\n\t\t\t\t\t\tadapter,\n\t\t\t\t\t\tevent,\n\t\t\t\t\t\t\"No authenticated model available. Use /login on your computer, then try /start again.\",\n\t\t\t\t\t);\n\t\t\t\telse if (\n\t\t\t\t\t!session?.model ||\n\t\t\t\t\t!models.some((model) => model.id === session.model?.id && model.provider === session.model.provider)\n\t\t\t\t)\n\t\t\t\t\tawait sendCommandReply(adapter, event, \"No available model selected. Use /model here to choose one.\");\n\t\t\t\telse\n\t\t\t\t\tawait sendCommandReply(\n\t\t\t\t\t\tadapter,\n\t\t\t\t\t\tevent,\n\t\t\t\t\t\t\"Ready. Send a message to begin, or use /model to choose a model.\",\n\t\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\tawait sendCommandReply(\n\t\t\t\t\tadapter,\n\t\t\t\t\tevent,\n\t\t\t\t\t\"Session setup failed. Run lunr gateway doctor on your computer, then try /start again.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tif (deps.remoteControls && commandWord === \"model\") {\n\t\t\tconst issue = workspaceReadiness(event, key, freshCfg());\n\t\t\tif (issue) {\n\t\t\t\tawait sendCommandReply(adapter, event, issue);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tconst cmd = CHAT_COMMANDS.find((c) => c.name === commandWord || c.aliases?.includes(commandWord));\n\t\tif (!cmd) {\n\t\t\treturn event.source.chatType !== \"dm\";\n\t\t}\n\t\tconst ctx = {\n\t\t\tevent,\n\t\t\tkey,\n\t\t\tadapter,\n\t\t\tbridge,\n\t\t\tcfg: freshCfg(),\n\t\t\targs,\n\t\t\treply: (message: string) => sendCommandReply(adapter, event, message),\n\t\t};\n\t\treturn runWithApprovalContext({ key, adapter, source: event.source }, () => runChatCommand(cmd, ctx));\n\t}\n\n\t/** A successful preview is finalized by edit; failed edits fall back to a durable full reply. */\n\tasync function deliver(\n\t\tadapter: PlatformAdapter,\n\t\tevent: MessageEvent,\n\t\ttext: string,\n\t\tkey: string,\n\t\tcfg: GatewayConfig,\n\t\topts: { reply: boolean; editMessageId?: string; previewTruncated?: boolean; valid?: () => boolean },\n\t): Promise<void> {\n\t\tif (opts.valid && !opts.valid()) return;\n\t\tif (!canDeliverGatewayText(key, event.source, cfg, pairing)) return;\n\t\tconst filtered = applySilenceFilter(text);\n\t\tif (filtered === null) return;\n\t\tif (opts.editMessageId) {\n\t\t\tconst chunks = splitMessage(filtered, adapter.maxMessageLength);\n\t\t\tconst edit = await adapter\n\t\t\t\t.editMessage(event.source.chatId, opts.editMessageId, opts.previewTruncated ? chunks[0] : filtered)\n\t\t\t\t.catch(() => ({ success: false }));\n\t\t\tif (edit.success) {\n\t\t\t\tif (!opts.previewTruncated) return;\n\t\t\t\tawait queueGatewayText(key, event.source, adapter, chunks.slice(1).join(\"\"), {\n\t\t\t\t\tkind: \"result\",\n\t\t\t\t\tcfg,\n\t\t\t\t\tchunks: chunks.slice(1),\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tawait queueGatewayText(key, event.source, adapter, filtered, {\n\t\t\tkind: \"result\",\n\t\t\treplyTo: opts.reply ? event.messageId : undefined,\n\t\t\tcfg,\n\t\t});\n\t}\n\n\tasync function runTurn(\n\t\tadapter: PlatformAdapter,\n\t\tevent: MessageEvent,\n\t\tkey: string,\n\t\tcfg: GatewayConfig,\n\t): Promise<void> {\n\t\tconst epoch = gatewayEpoch(key);\n\t\tconst streaming = cfg.streaming.enabled && typeof adapter.editMessage === \"function\";\n\t\tlet denied = false;\n\t\tconst valid = () => {\n\t\t\tif (!denied) denied = !canDeliverGatewayText(key, event.source, cfg, pairing);\n\t\t\treturn !denied;\n\t\t};\n\t\tlet consumer: StreamConsumer | undefined;\n\t\tif (streaming) {\n\t\t\tconsumer = new StreamConsumer({\n\t\t\t\tsendInitial: async (text) => {\n\t\t\t\t\tif (!valid() || gatewayEpoch(key) !== epoch) return null;\n\t\t\t\t\tconst result = await adapter.send(event.source.chatId, text, {\n\t\t\t\t\t\treplyTo: event.messageId,\n\t\t\t\t\t\tthreadId: event.source.threadId,\n\t\t\t\t\t});\n\t\t\t\t\treturn result.success ? (result.messageId ?? null) : null;\n\t\t\t\t},\n\t\t\t\tedit: async (messageId, text) => {\n\t\t\t\t\tif (!valid() || gatewayEpoch(key) !== epoch) return;\n\t\t\t\t\tconst result = await adapter.editMessage(event.source.chatId, messageId, text);\n\t\t\t\t\tif (!result.success) throw new Error(result.error ?? \"Preview edit failed\");\n\t\t\t\t},\n\t\t\t\tintervalMs: cfg.streaming.editIntervalMs,\n\t\t\t\tthreshold: cfg.streaming.bufferThreshold,\n\t\t\t\tmaxPreview: adapter.maxMessageLength,\n\t\t\t});\n\t\t}\n\n\t\tconst callbacks: TurnCallbacks = {\n\t\t\tonDelta: consumer ? (delta) => consumer.push(delta) : undefined,\n\t\t\tonFollowUpResult: (text) => {\n\t\t\t\tvoid deliver(adapter, event, text, key, cfg, { reply: false }).catch((error) =>\n\t\t\t\t\tconsole.error(\"[gateway] follow-up delivery failed:\", error),\n\t\t\t\t);\n\t\t\t},\n\t\t\tonError: (message) => {\n\t\t\t\tvoid sendError(adapter, event, key, cfg, message, epoch).catch((error) =>\n\t\t\t\t\tconsole.error(\"[gateway] error delivery failed:\", error),\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\n\t\tconst result = await runWithApprovalContext({ key, adapter, source: event.source }, () =>\n\t\t\tbridge.runTurn(key, event, callbacks),\n\t\t);\n\t\tif (result === QUEUED) return; // queued behind a running turn: no reply\n\t\tif (consumer) await consumer.finalize();\n\t\tawait deliver(adapter, event, result, key, cfg, {\n\t\t\treply: !consumer,\n\t\t\teditMessageId: consumer?.sentMessageId ?? undefined,\n\t\t\tpreviewTruncated: consumer?.truncated ?? false,\n\t\t\tvalid,\n\t\t});\n\t}\n\n\treturn {\n\t\tasync handleEvent(event: MessageEvent): Promise<void> {\n\t\t\tconst adapter = adapters.get(event.source.platform);\n\t\t\tif (!adapter) return;\n\t\t\tconst cfg = freshCfg();\n\t\t\tconst key = buildSessionKey(event.source, { groupSessionsPerUser: cfg.groupSessionsPerUser });\n\t\t\tconst epoch = gatewayEpoch(key);\n\t\t\ttry {\n\t\t\t\tif (isGroupGated(event, cfg)) return;\n\t\t\t\trememberGatewayRole(key, event.source);\n\t\t\t\tif (await isDenied(adapter, event, cfg)) return;\n\t\t\t\tif (deps.remoteControls) {\n\t\t\t\t\tconst binding = conversationBinding(key);\n\t\t\t\t\tif (binding?.owner) {\n\t\t\t\t\t\trequireAuthorized(event.source, cfg);\n\t\t\t\t\t\tif (binding.owner !== event.source.userId)\n\t\t\t\t\t\t\tthrow new Error(\"This session belongs to a different user.\");\n\t\t\t\t\t}\n\t\t\t\t\tbindConversation(key, event.source);\n\t\t\t\t\tif (acceptGatewayInput(key, event)) return;\n\t\t\t\t}\n\t\t\t\tif (await handleSlash(adapter, event, key)) return;\n\t\t\t\tconst issue = deps.remoteControls ? workspaceReadiness(event, key, cfg) : undefined;\n\t\t\t\tif (issue) {\n\t\t\t\t\tawait sendCommandReply(adapter, event, issue);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait adapter.sendTyping(event.source.chatId, event.source.threadId).catch(() => {});\n\t\t\t\tawait runTurn(adapter, event, key, cfg);\n\t\t\t} catch (err) {\n\t\t\t\tawait sendError(adapter, event, key, cfg, err instanceof Error ? err.message : String(err), epoch).catch(\n\t\t\t\t\t() => {},\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
@@ -16,14 +16,14 @@
16
16
  * Errors surface as a compact "⚠ <one-line>" — never a stack trace.
17
17
  */
18
18
  import { existsSync } from "node:fs";
19
- import { QUEUED } from "./agent-bridge.js";
19
+ import { QUEUED, } from "./agent-bridge.js";
20
20
  import { registerGatewayApprovalHandler, runWithApprovalContext } from "./approval.js";
21
21
  import { isAuthorized, requireAuthorized } from "./authz.js";
22
22
  import { CHAT_COMMANDS, runChatCommand, sendCommandReply } from "./commands.js";
23
23
  import { gatewayConfigPath, loadGatewayConfig, platformConfigFor } from "./config.js";
24
24
  import { bindConversation, conversationBinding } from "./conversations.js";
25
25
  import { resolveWithinRoots } from "./mobile-commands.js";
26
- import { acceptGatewayInput, gatewayEpoch, invalidateGatewayDialogs, queueGatewayText, rememberGatewayRole, } from "./presenter.js";
26
+ import { acceptGatewayInput, canDeliverGatewayText, gatewayEpoch, invalidateGatewayDialogs, queueGatewayText, rememberGatewayRole, } from "./presenter.js";
27
27
  import { buildSessionKey } from "./session-keys.js";
28
28
  import { getSession as getStoredSession } from "./store.js";
29
29
  import { applySilenceFilter, StreamConsumer } from "./stream.js";
@@ -184,6 +184,10 @@ export function createRouter(deps) {
184
184
  }
185
185
  /** A successful preview is finalized by edit; failed edits fall back to a durable full reply. */
186
186
  async function deliver(adapter, event, text, key, cfg, opts) {
187
+ if (opts.valid && !opts.valid())
188
+ return;
189
+ if (!canDeliverGatewayText(key, event.source, cfg, pairing))
190
+ return;
187
191
  const filtered = applySilenceFilter(text);
188
192
  if (filtered === null)
189
193
  return;
@@ -212,10 +216,18 @@ export function createRouter(deps) {
212
216
  async function runTurn(adapter, event, key, cfg) {
213
217
  const epoch = gatewayEpoch(key);
214
218
  const streaming = cfg.streaming.enabled && typeof adapter.editMessage === "function";
219
+ let denied = false;
220
+ const valid = () => {
221
+ if (!denied)
222
+ denied = !canDeliverGatewayText(key, event.source, cfg, pairing);
223
+ return !denied;
224
+ };
215
225
  let consumer;
216
226
  if (streaming) {
217
227
  consumer = new StreamConsumer({
218
228
  sendInitial: async (text) => {
229
+ if (!valid() || gatewayEpoch(key) !== epoch)
230
+ return null;
219
231
  const result = await adapter.send(event.source.chatId, text, {
220
232
  replyTo: event.messageId,
221
233
  threadId: event.source.threadId,
@@ -223,6 +235,8 @@ export function createRouter(deps) {
223
235
  return result.success ? (result.messageId ?? null) : null;
224
236
  },
225
237
  edit: async (messageId, text) => {
238
+ if (!valid() || gatewayEpoch(key) !== epoch)
239
+ return;
226
240
  const result = await adapter.editMessage(event.source.chatId, messageId, text);
227
241
  if (!result.success)
228
242
  throw new Error(result.error ?? "Preview edit failed");
@@ -250,6 +264,7 @@ export function createRouter(deps) {
250
264
  reply: !consumer,
251
265
  editMessageId: consumer?.sentMessageId ?? undefined,
252
266
  previewTruncated: consumer?.truncated ?? false,
267
+ valid,
253
268
  });
254
269
  }
255
270
  return {