@ashx-j/lunr 0.2.21 → 0.2.23

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 (461) hide show
  1. package/CHANGELOG.md +4 -2
  2. package/README.md +23 -5
  3. package/dist/builtin-extensions/index.d.ts.map +1 -1
  4. package/dist/builtin-extensions/index.js +1 -0
  5. package/dist/builtin-extensions/index.js.map +1 -1
  6. package/dist/builtin-extensions/lunr-browser.d.ts +9 -0
  7. package/dist/builtin-extensions/lunr-browser.d.ts.map +1 -0
  8. package/dist/builtin-extensions/lunr-browser.js +169 -0
  9. package/dist/builtin-extensions/lunr-browser.js.map +1 -0
  10. package/dist/builtin-extensions/lunr-todos.d.ts.map +1 -1
  11. package/dist/builtin-extensions/lunr-todos.js +15 -0
  12. package/dist/builtin-extensions/lunr-todos.js.map +1 -1
  13. package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
  14. package/dist/builtin-extensions/pi-intercom/index.js +3 -0
  15. package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
  16. package/dist/builtin-extensions/pi-subagents/src/extension/control-notices.d.ts +1 -1
  17. package/dist/builtin-extensions/pi-subagents/src/extension/control-notices.d.ts.map +1 -1
  18. package/dist/builtin-extensions/pi-subagents/src/extension/control-notices.js +5 -1
  19. package/dist/builtin-extensions/pi-subagents/src/extension/control-notices.js.map +1 -1
  20. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts +2 -2
  21. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
  22. package/dist/builtin-extensions/pi-subagents/src/extension/index.js +18 -5
  23. package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
  24. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts +4 -3
  25. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts.map +1 -1
  26. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js +3 -0
  27. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
  28. package/dist/builtin-extensions/pi-subagents/src/intercom/communication.d.ts +17 -0
  29. package/dist/builtin-extensions/pi-subagents/src/intercom/communication.d.ts.map +1 -0
  30. package/dist/builtin-extensions/pi-subagents/src/intercom/communication.js +45 -0
  31. package/dist/builtin-extensions/pi-subagents/src/intercom/communication.js.map +1 -0
  32. package/dist/builtin-extensions/pi-subagents/src/intercom/intercom-bridge.d.ts.map +1 -1
  33. package/dist/builtin-extensions/pi-subagents/src/intercom/intercom-bridge.js +4 -11
  34. package/dist/builtin-extensions/pi-subagents/src/intercom/intercom-bridge.js.map +1 -1
  35. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.d.ts +4 -1
  36. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.d.ts.map +1 -1
  37. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js +160 -51
  38. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js.map +1 -1
  39. package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.d.ts.map +1 -1
  40. package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.js +5 -0
  41. package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.js.map +1 -1
  42. package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.d.ts +2 -0
  43. package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.d.ts.map +1 -1
  44. package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.js +3 -2
  45. package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.js.map +1 -1
  46. package/dist/builtin-extensions/pi-subagents/src/runs/background/auto-drain.d.ts +1 -0
  47. package/dist/builtin-extensions/pi-subagents/src/runs/background/auto-drain.d.ts.map +1 -1
  48. package/dist/builtin-extensions/pi-subagents/src/runs/background/auto-drain.js +2 -1
  49. package/dist/builtin-extensions/pi-subagents/src/runs/background/auto-drain.js.map +1 -1
  50. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.d.ts +3 -0
  51. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.d.ts.map +1 -1
  52. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.js +11 -0
  53. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.js.map +1 -1
  54. package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.d.ts.map +1 -1
  55. package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.js +41 -3
  56. package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.js.map +1 -1
  57. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  58. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +4 -0
  59. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
  60. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.d.ts.map +1 -1
  61. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js +3 -0
  62. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js.map +1 -1
  63. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-control.d.ts +1 -0
  64. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-control.d.ts.map +1 -1
  65. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-control.js +3 -0
  66. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-control.js.map +1 -1
  67. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
  68. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.js +1 -1
  69. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
  70. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +1 -0
  71. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
  72. package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
  73. package/dist/builtin-extensions/pi-subagents/src/tui/communication.d.ts +18 -0
  74. package/dist/builtin-extensions/pi-subagents/src/tui/communication.d.ts.map +1 -0
  75. package/dist/builtin-extensions/pi-subagents/src/tui/communication.js +48 -0
  76. package/dist/builtin-extensions/pi-subagents/src/tui/communication.js.map +1 -0
  77. package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
  78. package/dist/builtin-extensions/pi-web-access/index.js +11 -7
  79. package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
  80. package/dist/cli/args.d.ts.map +1 -1
  81. package/dist/cli/args.js +2 -1
  82. package/dist/cli/args.js.map +1 -1
  83. package/dist/cli/install-cli.d.ts.map +1 -1
  84. package/dist/cli/install-cli.js +15 -45
  85. package/dist/cli/install-cli.js.map +1 -1
  86. package/dist/core/agent-session-runtime.d.ts +9 -0
  87. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  88. package/dist/core/agent-session-runtime.js +126 -29
  89. package/dist/core/agent-session-runtime.js.map +1 -1
  90. package/dist/core/agent-session.d.ts +22 -0
  91. package/dist/core/agent-session.d.ts.map +1 -1
  92. package/dist/core/agent-session.js +250 -105
  93. package/dist/core/agent-session.js.map +1 -1
  94. package/dist/core/browser/network.d.ts +9 -0
  95. package/dist/core/browser/network.d.ts.map +1 -0
  96. package/dist/core/browser/network.js +146 -0
  97. package/dist/core/browser/network.js.map +1 -0
  98. package/dist/core/browser/runtime.d.ts +24 -0
  99. package/dist/core/browser/runtime.d.ts.map +1 -0
  100. package/dist/core/browser/runtime.js +289 -0
  101. package/dist/core/browser/runtime.js.map +1 -0
  102. package/dist/core/browser/schema.d.ts +16 -0
  103. package/dist/core/browser/schema.d.ts.map +1 -0
  104. package/dist/core/browser/schema.js +36 -0
  105. package/dist/core/browser/schema.js.map +1 -0
  106. package/dist/core/browser/settings.d.ts +8 -0
  107. package/dist/core/browser/settings.d.ts.map +1 -0
  108. package/dist/core/browser/settings.js +30 -0
  109. package/dist/core/browser/settings.js.map +1 -0
  110. package/dist/core/browser/setup.d.ts +2 -0
  111. package/dist/core/browser/setup.d.ts.map +1 -0
  112. package/dist/core/browser/setup.js +16 -0
  113. package/dist/core/browser/setup.js.map +1 -0
  114. package/dist/core/customize.d.ts.map +1 -1
  115. package/dist/core/customize.js +15 -14
  116. package/dist/core/customize.js.map +1 -1
  117. package/dist/core/export-html/index.d.ts.map +1 -1
  118. package/dist/core/export-html/index.js +1 -1
  119. package/dist/core/export-html/index.js.map +1 -1
  120. package/dist/core/extensions/runner.d.ts +1 -0
  121. package/dist/core/extensions/runner.d.ts.map +1 -1
  122. package/dist/core/extensions/runner.js +1 -1
  123. package/dist/core/extensions/runner.js.map +1 -1
  124. package/dist/core/extensions/types.d.ts +1 -1
  125. package/dist/core/extensions/types.d.ts.map +1 -1
  126. package/dist/core/extensions/types.js.map +1 -1
  127. package/dist/core/install-features.d.ts.map +1 -1
  128. package/dist/core/install-features.js +10 -8
  129. package/dist/core/install-features.js.map +1 -1
  130. package/dist/core/memory-cap.d.ts.map +1 -1
  131. package/dist/core/memory-cap.js +5 -3
  132. package/dist/core/memory-cap.js.map +1 -1
  133. package/dist/core/model-tiers.d.ts.map +1 -1
  134. package/dist/core/model-tiers.js +9 -6
  135. package/dist/core/model-tiers.js.map +1 -1
  136. package/dist/core/permissions.d.ts.map +1 -1
  137. package/dist/core/permissions.js +9 -0
  138. package/dist/core/permissions.js.map +1 -1
  139. package/dist/core/plan-mode.d.ts.map +1 -1
  140. package/dist/core/plan-mode.js +3 -0
  141. package/dist/core/plan-mode.js.map +1 -1
  142. package/dist/core/process-registry.d.ts.map +1 -1
  143. package/dist/core/process-registry.js +6 -4
  144. package/dist/core/process-registry.js.map +1 -1
  145. package/dist/core/runtime-scope.d.ts +11 -0
  146. package/dist/core/runtime-scope.d.ts.map +1 -0
  147. package/dist/core/runtime-scope.js +3 -0
  148. package/dist/core/runtime-scope.js.map +1 -0
  149. package/dist/core/sdk.d.ts.map +1 -1
  150. package/dist/core/sdk.js +13 -0
  151. package/dist/core/sdk.js.map +1 -1
  152. package/dist/core/session-handoff.d.ts +35 -0
  153. package/dist/core/session-handoff.d.ts.map +1 -0
  154. package/dist/core/session-handoff.js +214 -0
  155. package/dist/core/session-handoff.js.map +1 -0
  156. package/dist/core/session-manager.d.ts +20 -0
  157. package/dist/core/session-manager.d.ts.map +1 -1
  158. package/dist/core/session-manager.js +184 -22
  159. package/dist/core/session-manager.js.map +1 -1
  160. package/dist/core/session-ownership.d.ts +28 -0
  161. package/dist/core/session-ownership.d.ts.map +1 -0
  162. package/dist/core/session-ownership.js +138 -0
  163. package/dist/core/session-ownership.js.map +1 -0
  164. package/dist/core/session-retention.d.ts.map +1 -1
  165. package/dist/core/session-retention.js +11 -2
  166. package/dist/core/session-retention.js.map +1 -1
  167. package/dist/core/settings-manager.d.ts +11 -0
  168. package/dist/core/settings-manager.d.ts.map +1 -1
  169. package/dist/core/settings-manager.js +27 -0
  170. package/dist/core/settings-manager.js.map +1 -1
  171. package/dist/core/slash-commands.d.ts.map +1 -1
  172. package/dist/core/slash-commands.js +2 -0
  173. package/dist/core/slash-commands.js.map +1 -1
  174. package/dist/core/subagent-wait-interruption.d.ts +11 -0
  175. package/dist/core/subagent-wait-interruption.d.ts.map +1 -0
  176. package/dist/core/subagent-wait-interruption.js +19 -0
  177. package/dist/core/subagent-wait-interruption.js.map +1 -0
  178. package/dist/core/system-prompt.d.ts.map +1 -1
  179. package/dist/core/system-prompt.js +7 -2
  180. package/dist/core/system-prompt.js.map +1 -1
  181. package/dist/core/todo-settings.d.ts +5 -0
  182. package/dist/core/todo-settings.d.ts.map +1 -0
  183. package/dist/core/todo-settings.js +7 -0
  184. package/dist/core/todo-settings.js.map +1 -0
  185. package/dist/core/tools/bash.d.ts.map +1 -1
  186. package/dist/core/tools/bash.js +4 -4
  187. package/dist/core/tools/bash.js.map +1 -1
  188. package/dist/core/tools/edit.d.ts.map +1 -1
  189. package/dist/core/tools/edit.js +5 -3
  190. package/dist/core/tools/edit.js.map +1 -1
  191. package/dist/core/tools/find.d.ts.map +1 -1
  192. package/dist/core/tools/find.js +4 -4
  193. package/dist/core/tools/find.js.map +1 -1
  194. package/dist/core/tools/grep.d.ts.map +1 -1
  195. package/dist/core/tools/grep.js +4 -4
  196. package/dist/core/tools/grep.js.map +1 -1
  197. package/dist/core/tools/ls.d.ts.map +1 -1
  198. package/dist/core/tools/ls.js +4 -4
  199. package/dist/core/tools/ls.js.map +1 -1
  200. package/dist/core/tools/read.d.ts.map +1 -1
  201. package/dist/core/tools/read.js +4 -4
  202. package/dist/core/tools/read.js.map +1 -1
  203. package/dist/core/tools/render-utils.d.ts +14 -5
  204. package/dist/core/tools/render-utils.d.ts.map +1 -1
  205. package/dist/core/tools/render-utils.js +34 -7
  206. package/dist/core/tools/render-utils.js.map +1 -1
  207. package/dist/core/tools/write.d.ts.map +1 -1
  208. package/dist/core/tools/write.js +4 -4
  209. package/dist/core/tools/write.js.map +1 -1
  210. package/dist/core/usage-service.d.ts.map +1 -1
  211. package/dist/core/usage-service.js +5 -3
  212. package/dist/core/usage-service.js.map +1 -1
  213. package/dist/gateway/adapters/discord.d.ts +12 -1
  214. package/dist/gateway/adapters/discord.d.ts.map +1 -1
  215. package/dist/gateway/adapters/discord.js +112 -17
  216. package/dist/gateway/adapters/discord.js.map +1 -1
  217. package/dist/gateway/adapters/telegram.d.ts +4 -2
  218. package/dist/gateway/adapters/telegram.d.ts.map +1 -1
  219. package/dist/gateway/adapters/telegram.js +34 -11
  220. package/dist/gateway/adapters/telegram.js.map +1 -1
  221. package/dist/gateway/agent-bridge.d.ts +14 -3
  222. package/dist/gateway/agent-bridge.d.ts.map +1 -1
  223. package/dist/gateway/agent-bridge.js +252 -60
  224. package/dist/gateway/agent-bridge.js.map +1 -1
  225. package/dist/gateway/approval.d.ts +1 -0
  226. package/dist/gateway/approval.d.ts.map +1 -1
  227. package/dist/gateway/approval.js +60 -18
  228. package/dist/gateway/approval.js.map +1 -1
  229. package/dist/gateway/authz.d.ts +2 -0
  230. package/dist/gateway/authz.d.ts.map +1 -1
  231. package/dist/gateway/authz.js +13 -0
  232. package/dist/gateway/authz.js.map +1 -1
  233. package/dist/gateway/buttons.d.ts +3 -1
  234. package/dist/gateway/buttons.d.ts.map +1 -1
  235. package/dist/gateway/buttons.js +18 -3
  236. package/dist/gateway/buttons.js.map +1 -1
  237. package/dist/gateway/command.d.ts.map +1 -1
  238. package/dist/gateway/command.js +49 -4
  239. package/dist/gateway/command.js.map +1 -1
  240. package/dist/gateway/commands.d.ts.map +1 -1
  241. package/dist/gateway/commands.js +18 -3
  242. package/dist/gateway/commands.js.map +1 -1
  243. package/dist/gateway/config.d.ts +5 -0
  244. package/dist/gateway/config.d.ts.map +1 -1
  245. package/dist/gateway/config.js +5 -0
  246. package/dist/gateway/config.js.map +1 -1
  247. package/dist/gateway/conversations.d.ts +14 -0
  248. package/dist/gateway/conversations.d.ts.map +1 -0
  249. package/dist/gateway/conversations.js +30 -0
  250. package/dist/gateway/conversations.js.map +1 -0
  251. package/dist/gateway/download.d.ts +2 -0
  252. package/dist/gateway/download.d.ts.map +1 -0
  253. package/dist/gateway/download.js +36 -0
  254. package/dist/gateway/download.js.map +1 -0
  255. package/dist/gateway/index.d.ts.map +1 -1
  256. package/dist/gateway/index.js +85 -26
  257. package/dist/gateway/index.js.map +1 -1
  258. package/dist/gateway/mobile-commands.d.ts +24 -0
  259. package/dist/gateway/mobile-commands.d.ts.map +1 -0
  260. package/dist/gateway/mobile-commands.js +422 -0
  261. package/dist/gateway/mobile-commands.js.map +1 -0
  262. package/dist/gateway/presenter.d.ts +16 -0
  263. package/dist/gateway/presenter.d.ts.map +1 -0
  264. package/dist/gateway/presenter.js +210 -0
  265. package/dist/gateway/presenter.js.map +1 -0
  266. package/dist/gateway/router.d.ts +1 -0
  267. package/dist/gateway/router.d.ts.map +1 -1
  268. package/dist/gateway/router.js +32 -3
  269. package/dist/gateway/router.js.map +1 -1
  270. package/dist/gateway/service.d.ts +46 -0
  271. package/dist/gateway/service.d.ts.map +1 -0
  272. package/dist/gateway/service.js +395 -0
  273. package/dist/gateway/service.js.map +1 -0
  274. package/dist/gateway/setup.d.ts +4 -0
  275. package/dist/gateway/setup.d.ts.map +1 -0
  276. package/dist/gateway/setup.js +146 -0
  277. package/dist/gateway/setup.js.map +1 -0
  278. package/dist/gateway/types.d.ts +4 -2
  279. package/dist/gateway/types.d.ts.map +1 -1
  280. package/dist/gateway/types.js.map +1 -1
  281. package/dist/index.d.ts +2 -0
  282. package/dist/index.d.ts.map +1 -1
  283. package/dist/index.js +2 -0
  284. package/dist/index.js.map +1 -1
  285. package/dist/main.d.ts.map +1 -1
  286. package/dist/main.js +2 -1
  287. package/dist/main.js.map +1 -1
  288. package/dist/modes/interactive/components/assistant-message.d.ts +19 -2
  289. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  290. package/dist/modes/interactive/components/assistant-message.js +78 -17
  291. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  292. package/dist/modes/interactive/components/copyable-text.d.ts +27 -0
  293. package/dist/modes/interactive/components/copyable-text.d.ts.map +1 -0
  294. package/dist/modes/interactive/components/copyable-text.js +156 -0
  295. package/dist/modes/interactive/components/copyable-text.js.map +1 -0
  296. package/dist/modes/interactive/components/custom-entry.d.ts +1 -0
  297. package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
  298. package/dist/modes/interactive/components/custom-entry.js +8 -0
  299. package/dist/modes/interactive/components/custom-entry.js.map +1 -1
  300. package/dist/modes/interactive/components/custom-message.d.ts +1 -0
  301. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  302. package/dist/modes/interactive/components/custom-message.js +8 -0
  303. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  304. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  305. package/dist/modes/interactive/components/session-selector.js +16 -0
  306. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  307. package/dist/modes/interactive/components/settings-selector.d.ts +8 -1
  308. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  309. package/dist/modes/interactive/components/settings-selector.js +51 -0
  310. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  311. package/dist/modes/interactive/components/thinking-tail.d.ts +9 -2
  312. package/dist/modes/interactive/components/thinking-tail.d.ts.map +1 -1
  313. package/dist/modes/interactive/components/thinking-tail.js +43 -1
  314. package/dist/modes/interactive/components/thinking-tail.js.map +1 -1
  315. package/dist/modes/interactive/components/tool-execution.d.ts +4 -3
  316. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  317. package/dist/modes/interactive/components/tool-execution.js +34 -45
  318. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  319. package/dist/modes/interactive/interactive-mode.d.ts +9 -0
  320. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  321. package/dist/modes/interactive/interactive-mode.js +267 -44
  322. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  323. package/dist/modes/interactive/theme/theme.d.ts +1 -0
  324. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  325. package/dist/modes/interactive/theme/theme.js +23 -0
  326. package/dist/modes/interactive/theme/theme.js.map +1 -1
  327. package/dist/node-runtime/agent-session-services-GZVSOGYR.js +49 -0
  328. package/dist/node-runtime/{builtin-extensions-JCOPAAB5.js → builtin-extensions-QVCXNQT4.js} +6 -5
  329. package/dist/node-runtime/{chunk-LTYWTR3V.js → chunk-2MI5OPMN.js} +17 -2
  330. package/dist/node-runtime/{chunk-W3CVH5RX.js → chunk-2TKODGBZ.js} +11 -6
  331. package/dist/node-runtime/chunk-4EULYYR4.js +62 -0
  332. package/dist/node-runtime/{chunk-6N22HULO.js → chunk-5D7LC2ZQ.js} +7 -2
  333. package/dist/node-runtime/{chunk-KBKMCJDU.js → chunk-5ZQXRPMC.js} +19 -4
  334. package/dist/node-runtime/{chunk-BOHIDQT4.js → chunk-6VYZ5UFP.js} +1 -1
  335. package/dist/node-runtime/chunk-AZJHNQKQ.js +205 -0
  336. package/dist/node-runtime/{chunk-A3KMVBZV.js → chunk-BBUVIEZB.js} +1 -10
  337. package/dist/node-runtime/{chunk-IRPJXI6U.js → chunk-BX2RB477.js} +1 -1
  338. package/dist/node-runtime/chunk-CCCN2UCX.js +14 -0
  339. package/dist/node-runtime/{chunk-25I2BHAA.js → chunk-CSUZVNYE.js} +1 -1
  340. package/dist/node-runtime/chunk-DD5YWFSJ.js +708 -0
  341. package/dist/node-runtime/chunk-DQMGU675.js +3409 -0
  342. package/dist/node-runtime/{chunk-QZMODZQ2.js → chunk-E24EO34J.js} +2 -1
  343. package/dist/node-runtime/chunk-EKVFP6YF.js +355 -0
  344. package/dist/node-runtime/chunk-GJ2RDBRL.js +672 -0
  345. package/dist/node-runtime/{chunk-W3SILANP.js → chunk-GMHJCCEN.js} +8 -8
  346. package/dist/node-runtime/{chunk-DDWHZ633.js → chunk-GS42T6MX.js} +1 -1
  347. package/dist/node-runtime/chunk-GS66LV4M.js +2830 -0
  348. package/dist/node-runtime/chunk-H4K3JDRB.js +47 -0
  349. package/dist/node-runtime/{chunk-2SJB2AWP.js → chunk-HMBGROQK.js} +3 -3
  350. package/dist/node-runtime/{chunk-GB2MGFLI.js → chunk-HPUP7E4T.js} +216 -201
  351. package/dist/node-runtime/{chunk-YMEAYRBE.js → chunk-HTFRRDSF.js} +2 -7
  352. package/dist/node-runtime/{chunk-OPEK5RRY.js → chunk-IF24A3FH.js} +1 -1
  353. package/dist/node-runtime/chunk-IMLCWLPI.js +9 -0
  354. package/dist/node-runtime/chunk-KKUTQQV7.js +144 -0
  355. package/dist/node-runtime/chunk-LFZDVKSP.js +521 -0
  356. package/dist/node-runtime/{chunk-2BUAYZ7K.js → chunk-LLUIPGDR.js} +581 -197
  357. package/dist/node-runtime/{chunk-7NBZ5P7I.js → chunk-NAY6UUQZ.js} +1 -1
  358. package/dist/node-runtime/chunk-OAR4TYZE.js +146 -0
  359. package/dist/node-runtime/{chunk-STERBGUB.js → chunk-OFKSVRV7.js} +2 -2
  360. package/dist/node-runtime/{chunk-JZQ7F36T.js → chunk-PCEW76PM.js} +19 -3
  361. package/dist/node-runtime/{chunk-WLLDXJRT.js → chunk-QAZOULTU.js} +29 -1014
  362. package/dist/node-runtime/{chunk-6KDIOJL3.js → chunk-QH6YL7VK.js} +3 -3
  363. package/dist/node-runtime/chunk-R3K2F5GI.js +122 -0
  364. package/dist/node-runtime/{chunk-RNBHABBA.js → chunk-RDDAZOMU.js} +23 -0
  365. package/dist/node-runtime/{chunk-SY2TFMLO.js → chunk-RIR5EDDJ.js} +46 -151
  366. package/dist/node-runtime/chunk-RMQZU2YU.js +117 -0
  367. package/dist/node-runtime/{chunk-6BEIBLQ3.js → chunk-RWTKOC3B.js} +1 -1
  368. package/dist/node-runtime/{chunk-G6LRAP36.js → chunk-TQGPP4SC.js} +1 -1
  369. package/dist/node-runtime/{chunk-Y5KOEMBH.js → chunk-TVJUOJZJ.js} +9 -674
  370. package/dist/node-runtime/chunk-UAEOTO52.js +119 -0
  371. package/dist/node-runtime/{chunk-AY6CAJSL.js → chunk-UJOCPOMX.js} +6 -3
  372. package/dist/node-runtime/chunk-UP2XBVH5.js +40 -0
  373. package/dist/node-runtime/{chunk-WJVFOIB5.js → chunk-UQUCFAJ3.js} +1 -1
  374. package/dist/node-runtime/chunk-URD2SLIT.js +1019 -0
  375. package/dist/node-runtime/chunk-US544NN6.js +13 -0
  376. package/dist/node-runtime/chunk-UWPJL22I.js +341 -0
  377. package/dist/node-runtime/{chunk-4I23VTC4.js → chunk-VWCE2HSZ.js} +21 -124
  378. package/dist/node-runtime/{chunk-VZR3NCM7.js → chunk-W64JC4E7.js} +18 -14
  379. package/dist/node-runtime/{chunk-AWVD5IEY.js → chunk-X3URCYCU.js} +23 -12
  380. package/dist/node-runtime/{chunk-GKLEQ3I4.js → chunk-XLTL257W.js} +126 -27
  381. package/dist/node-runtime/{chunk-WKDK4C5P.js → chunk-YDO2F2IA.js} +38 -164
  382. package/dist/node-runtime/{chunk-PDNXVXMD.js → chunk-YHQ3NBB3.js} +58 -48
  383. package/dist/node-runtime/chunk-YPHAIRZT.js +10 -0
  384. package/dist/node-runtime/cli-runtime.js +2 -2
  385. package/dist/node-runtime/command-PDVTBRWA.js +45 -0
  386. package/dist/node-runtime/{commands-MV5KOZ64.js → commands-KAMO2T6P.js} +10 -10
  387. package/dist/node-runtime/commands-XDCFGIGW.js +36 -0
  388. package/dist/node-runtime/config-RORQUQFG.js +20 -0
  389. package/dist/node-runtime/{customize-LF5VST47.js → customize-VFJV3U6A.js} +2 -1
  390. package/dist/node-runtime/{direct-tool-executor-BK7CLEBX.js → direct-tool-executor-YAVOLDFY.js} +8 -8
  391. package/dist/node-runtime/{export-html-TEXVV5RP.js → export-html-KSOIMVPI.js} +4 -3
  392. package/dist/node-runtime/{extension-input-O4HSAEM4.js → extension-input-E7BJZ35H.js} +4 -4
  393. package/dist/node-runtime/{extension-selector-X5FCX43D.js → extension-selector-WU42XDTU.js} +4 -4
  394. package/dist/node-runtime/{extract-XLQSJN4V.js → extract-EQJE7FZZ.js} +2 -2
  395. package/dist/node-runtime/{gateway-BM6NLOH4.js → gateway-AYWM5DIO.js} +275 -994
  396. package/dist/node-runtime/{gemini-search-U7SUMPQA.js → gemini-search-35OCRFLN.js} +1 -1
  397. package/dist/node-runtime/{gemini-url-context-PXWCBQHK.js → gemini-url-context-UQY3PHHW.js} +2 -2
  398. package/dist/node-runtime/index.js +104 -65
  399. package/dist/node-runtime/{init-3C7Z24OI.js → init-X2LIUB2H.js} +3 -3
  400. package/dist/node-runtime/{install-cli-EXMJQESX.js → install-cli-3BO4MMQ6.js} +21 -82
  401. package/dist/node-runtime/interactive-mode-4MFRKAHG.js +72 -0
  402. package/dist/node-runtime/{interactive-view-ILS6TAN5.js → interactive-view-TUZHCQM3.js} +8 -8
  403. package/dist/node-runtime/lunr-browser-RYFIIVJN.js +174 -0
  404. package/dist/node-runtime/{lunr-cron-4VHP4E4B.js → lunr-cron-YWXEPZMQ.js} +19 -5
  405. package/dist/node-runtime/main.js +44 -31
  406. package/dist/node-runtime/{memory-cap-3S4XRLNP.js → memory-cap-EL3KL4JJ.js} +2 -1
  407. package/dist/node-runtime/mobile-commands-LVHDAUDP.js +32 -0
  408. package/dist/node-runtime/model-runtime-PLQKAG4B.js +16 -0
  409. package/dist/node-runtime/{model-tiers-NJLHUEGK.js → model-tiers-OU3U5CZG.js} +2 -1
  410. package/dist/node-runtime/{package-manager-cli-N37546V5.js → package-manager-cli-6GT3Z3CK.js} +22 -15
  411. package/dist/node-runtime/{pi-intercom-NV2LW6X7.js → pi-intercom-HBPK5HSJ.js} +4 -1
  412. package/dist/node-runtime/{pi-mcp-adapter-GUFZWW54.js → pi-mcp-adapter-E6WULBKF.js} +4 -4
  413. package/dist/node-runtime/{pi-subagents-6MUIFNDM.js → pi-subagents-CDWK2AKY.js} +236 -79
  414. package/dist/node-runtime/{pi-web-access-2CQGFLT2.js → pi-web-access-WTGMNZ32.js} +15 -15
  415. package/dist/node-runtime/process-registry-4FAKZQOL.js +31 -0
  416. package/dist/node-runtime/{proxy-modes-V6YVFH3C.js → proxy-modes-OSQCWRBI.js} +9 -9
  417. package/dist/node-runtime/rpc-entry.js +44 -31
  418. package/dist/node-runtime/{rpc-mode-W5ZTLTMZ.js → rpc-mode-O3XV4SID.js} +2 -2
  419. package/dist/node-runtime/runtime-4VA6JKNF.js +373 -0
  420. package/dist/node-runtime/{runtime-bridges-2VQTLPOZ.js → runtime-bridges-3XUQ46XW.js} +6 -5
  421. package/dist/node-runtime/service-ISSP6VHE.js +36 -0
  422. package/dist/node-runtime/session-handoff-HC7R5E24.js +33 -0
  423. package/dist/node-runtime/{session-manager-E7AREDTV.js → session-manager-CFN4UFM2.js} +2 -1
  424. package/dist/node-runtime/{session-selector-FJ7M2AIX.js → session-selector-GIM4DHQ2.js} +5 -4
  425. package/dist/node-runtime/{settings-manager-CUGAZWAB.js → settings-manager-6VEMXRDL.js} +7 -2
  426. package/dist/node-runtime/setup-5SR2J35A.js +155 -0
  427. package/dist/node-runtime/setup-JBKOLJ6F.js +18 -0
  428. package/dist/node-runtime/{subagent-executor-MH4A7CCF.js → subagent-executor-M5VWB3D3.js} +26 -18
  429. package/dist/node-runtime/theme-UOO5RVFN.js +68 -0
  430. package/dist/node-runtime/trust-manager-MMISNCYO.js +17 -0
  431. package/dist/node-runtime/{update-cli-XTZMZGOL.js → update-cli-UVKJWRRJ.js} +7 -2
  432. package/dist/node-runtime/usage-service-LFB2SWYV.js +37 -0
  433. package/dist/node-runtime/{video-extract-OYT5WKS5.js → video-extract-OOZHR7SU.js} +2 -2
  434. package/dist/node-runtime/{youtube-extract-TQYKGFGS.js → youtube-extract-FNPAUNHU.js} +6 -6
  435. package/docs/browser.md +86 -0
  436. package/docs/extensions.md +2 -0
  437. package/docs/features.md +96 -16
  438. package/docs/interactive-startup.md +2 -1
  439. package/docs/sessions.md +10 -0
  440. package/docs/settings.md +2 -0
  441. package/docs/tui.md +2 -0
  442. package/docs/usage.md +16 -0
  443. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  444. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  445. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  446. package/examples/extensions/gondolin/package-lock.json +2 -2
  447. package/examples/extensions/gondolin/package.json +1 -1
  448. package/examples/extensions/sandbox/package-lock.json +2 -2
  449. package/examples/extensions/sandbox/package.json +1 -1
  450. package/examples/extensions/with-deps/package-lock.json +2 -2
  451. package/examples/extensions/with-deps/package.json +1 -1
  452. package/package.json +7 -4
  453. package/scripts/install-browser.mjs +20 -0
  454. package/dist/node-runtime/agent-session-services-C743TYQE.js +0 -37
  455. package/dist/node-runtime/chunk-VGYZ3APQ.js +0 -59
  456. package/dist/node-runtime/chunk-XI77KVKA.js +0 -6009
  457. package/dist/node-runtime/command-P4NYCJ2M.js +0 -13
  458. package/dist/node-runtime/interactive-mode-EVVBA64D.js +0 -59
  459. package/dist/node-runtime/{chunk-UWQYRSYQ.js → chunk-YJMMG7C6.js} +25 -25
  460. package/dist/node-runtime/{mcp-panel-IIGMXQGP.js → mcp-panel-KTTFXJZ4.js} +3 -3
  461. package/dist/node-runtime/{src-BPQFJR76.js → src-IVPNZ2BX.js} +9 -9
@@ -2,46 +2,103 @@ import { createRequire as __lunrRequire } from "node:module"; const require = __
2
2
  import {
3
3
  AgentBridge,
4
4
  QUEUED,
5
- listSessions,
6
- splitMessage,
7
5
  startGatewayCron
8
- } from "./chunk-GB2MGFLI.js";
6
+ } from "./chunk-HPUP7E4T.js";
9
7
  import {
10
- gatewayConfigPath,
11
- isFeatureEnabled,
12
- loadGatewayConfig,
13
- platformConfigFor,
14
- resolvePlatformToken,
15
- saveGatewayConfig
16
- } from "./chunk-4I23VTC4.js";
17
- import "./chunk-CJECDQKP.js";
8
+ CHAT_COMMANDS,
9
+ botCommandSpecs,
10
+ runChatCommand,
11
+ sendCommandReply
12
+ } from "./chunk-LFZDVKSP.js";
13
+ import "./chunk-EKVFP6YF.js";
18
14
  import {
19
- computeContextBreakdown
20
- } from "./chunk-G6LRAP36.js";
15
+ acceptGatewayInput,
16
+ addAllowedUser,
17
+ bindConversation,
18
+ conversationBinding,
19
+ handleApprovalCallback,
20
+ handleCallback,
21
+ invalidateGatewayDialogs,
22
+ isAuthorized,
23
+ isGatewayOwner,
24
+ listSessions,
25
+ registerGatewayApprovalHandler,
26
+ requireGatewayOwner,
27
+ runWithApprovalContext,
28
+ splitMessage,
29
+ startButtonSweeper,
30
+ startGatewayPresenter,
31
+ stopButtonSweeper,
32
+ stopGatewayPresenter
33
+ } from "./chunk-DD5YWFSJ.js";
21
34
  import {
22
- findExactModelReferenceMatch
23
- } from "./chunk-STERBGUB.js";
24
- import "./chunk-CGBXPL3E.js";
35
+ createPairingStore
36
+ } from "./chunk-RMQZU2YU.js";
25
37
  import {
26
- SessionManager
27
- } from "./chunk-GKLEQ3I4.js";
28
- import "./chunk-FVDOXRUD.js";
38
+ claimGateway
39
+ } from "./chunk-UWPJL22I.js";
40
+ import "./chunk-CJECDQKP.js";
41
+ import "./chunk-TQGPP4SC.js";
29
42
  import "./chunk-AD6VXAO7.js";
30
43
  import {
31
- NO_HANDLER_REASON,
32
- registerApprovalHandler
33
- } from "./chunk-6N22HULO.js";
44
+ isFeatureEnabled
45
+ } from "./chunk-VWCE2HSZ.js";
46
+ import "./chunk-OFKSVRV7.js";
47
+ import "./chunk-GJ2RDBRL.js";
48
+ import "./chunk-CGBXPL3E.js";
49
+ import "./chunk-BBUVIEZB.js";
50
+ import "./chunk-5D7LC2ZQ.js";
34
51
  import "./chunk-M3LZLQW2.js";
52
+ import "./chunk-KKUTQQV7.js";
53
+ import "./chunk-CSFI6YXL.js";
54
+ import "./chunk-AZJHNQKQ.js";
55
+ import "./chunk-XLTL257W.js";
56
+ import "./chunk-FVDOXRUD.js";
57
+ import "./chunk-IMLCWLPI.js";
58
+ import "./chunk-UAEOTO52.js";
35
59
  import "./chunk-RQ2Q5IYR.js";
36
- import "./chunk-QZMODZQ2.js";
37
60
  import {
38
- getAgentDir
39
- } from "./chunk-FQMM2EZ2.js";
61
+ gatewayConfigPath,
62
+ loadGatewayConfig,
63
+ platformConfigFor,
64
+ resolvePlatformToken
65
+ } from "./chunk-R3K2F5GI.js";
66
+ import "./chunk-E24EO34J.js";
67
+ import "./chunk-RDDAZOMU.js";
68
+ import "./chunk-FQMM2EZ2.js";
40
69
  import "./chunk-Q4Q6D4IY.js";
41
70
  import "./chunk-BRJX2PRC.js";
42
71
 
43
72
  // packages/coding-agent/dist/gateway/adapters/discord.js
44
- import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, Client, Events, GatewayIntentBits } from "discord.js";
73
+ import { ActionRowBuilder, ApplicationCommandOptionType, ButtonBuilder, ButtonStyle, ChannelType, Client, Events, GatewayIntentBits } from "discord.js";
74
+
75
+ // packages/coding-agent/dist/gateway/download.js
76
+ async function downloadAttachment(url, limit = 8 * 1024 * 1024) {
77
+ let response = await fetch(url, { signal: AbortSignal.timeout(3e4) });
78
+ if (!response.ok || !response.body)
79
+ throw new Error(`Attachment request failed with HTTP ${response.status}.`);
80
+ if (Number(response.headers.get("content-length")) > limit)
81
+ throw await response.body.cancel(), new Error("Attachment exceeds the 8 MB limit.");
82
+ let reader = response.body.getReader(), parts = [], size = 0;
83
+ try {
84
+ for (; ; ) {
85
+ let part = await reader.read();
86
+ if (part.done)
87
+ break;
88
+ if (size += part.value.byteLength, size > limit)
89
+ throw await reader.cancel(), new Error("Attachment exceeds the 8 MB limit.");
90
+ parts.push(part.value);
91
+ }
92
+ } finally {
93
+ reader.releaseLock();
94
+ }
95
+ let bytes = new Uint8Array(size), offset = 0;
96
+ for (let part of parts)
97
+ bytes.set(part, offset), offset += part.byteLength;
98
+ return bytes;
99
+ }
100
+
101
+ // packages/coding-agent/dist/gateway/adapters/discord.js
45
102
  var DEFAULT_DEBOUNCE_MS = 600, TYPING_INTERVAL_MS = 8e3, TYPING_SAFETY_MS = 12e4, REPLY_TO_TEXT_MAX = 500, THREAD_NAME_MAX = 40, SEEN_CAP = 500, SENT_CHANNEL_CAP = 500, CALLBACK_INTERACTION_CAP = 500, CHANNEL_CACHE_CAP = 500, PARTICIPATING_THREAD_CAP = 500, ERR_INVALID_FORM_BODY = 50035, ERR_UNKNOWN_MESSAGE = 10008, THREAD_TYPES = [
46
103
  ChannelType.PublicThread,
47
104
  ChannelType.PrivateThread,
@@ -50,12 +107,6 @@ var DEFAULT_DEBOUNCE_MS = 600, TYPING_INTERVAL_MS = 8e3, TYPING_SAFETY_MS = 12e4
50
107
  function errMessage(err) {
51
108
  return err instanceof Error ? err.message : String(err);
52
109
  }
53
- async function defaultDownloadAttachment(url) {
54
- let res = await fetch(url);
55
- if (!res.ok)
56
- throw new Error(`attachment download HTTP ${res.status}`);
57
- return new Uint8Array(await res.arrayBuffer());
58
- }
59
110
  function discordErrorCode(err) {
60
111
  let code = err?.code;
61
112
  return typeof code == "number" ? code : void 0;
@@ -64,14 +115,14 @@ function isImageContentType(contentType) {
64
115
  return typeof contentType == "string" && contentType.split(";")[0]?.trim().toLowerCase().startsWith("image/");
65
116
  }
66
117
  function collectDiscordImageAttachments(message) {
67
- return (message.attachments ?? []).filter((attachment) => isImageContentType(attachment.contentType));
118
+ return Array.from(message.attachments?.values() ?? []).filter((attachment) => isImageContentType(attachment.contentType));
68
119
  }
69
120
  function messageToEvent(message, botUser, options = {}) {
70
121
  let author = message.author;
71
122
  if (!author || author.id === botUser.id || author.bot || message.system)
72
123
  return null;
73
124
  let content = (message.content ?? "").trim(), imageAttachments = collectDiscordImageAttachments(message);
74
- if (content === "" && imageAttachments.length === 0)
125
+ if (content === "" && imageAttachments.length === 0 && !Array.from(message.attachments?.values() ?? []).length)
75
126
  return null;
76
127
  let channel = message.channel, ignored = options.ignoredChannels ?? [];
77
128
  if (ignored.includes(channel.id) || channel.parentId != null && ignored.includes(channel.parentId))
@@ -181,6 +232,7 @@ var DiscordAdapter = class {
181
232
  client;
182
233
  botUserId;
183
234
  handler;
235
+ commandSuggestions;
184
236
  callbackHandler;
185
237
  pending = /* @__PURE__ */ new Map();
186
238
  typing = /* @__PURE__ */ new Map();
@@ -199,7 +251,10 @@ var DiscordAdapter = class {
199
251
  throw new Error("DiscordAdapter: no bot token (set discord.token or LUNR_DISCORD_BOT_TOKEN)");
200
252
  this.clientFactory = defaultClientFactory;
201
253
  }
202
- this.downloadAttachment = options.downloadAttachment ?? defaultDownloadAttachment, this.maxMediaBytes = options.maxMediaBytes ?? 10 * 1024 * 1024;
254
+ this.downloadAttachment = options.downloadAttachment ?? downloadAttachment, this.maxMediaBytes = options.maxMediaBytes ?? 10 * 1024 * 1024;
255
+ }
256
+ setCommandSuggestions(handler) {
257
+ this.commandSuggestions = handler;
203
258
  }
204
259
  onMessage(handler) {
205
260
  this.handler = handler;
@@ -216,7 +271,8 @@ var DiscordAdapter = class {
216
271
  console.error(`[gateway] discord message handling error: ${errMessage(err)}`);
217
272
  });
218
273
  }), client.on(Events.InteractionCreate, (interaction) => {
219
- this.handleInteraction(interaction).catch((err) => {
274
+ let event = interaction;
275
+ (event.isButton() ? this.handleInteraction(interaction) : this.handleNativeCommand(event)).catch((err) => {
220
276
  console.error(`[gateway] discord interaction handling error: ${errMessage(err)}`);
221
277
  });
222
278
  });
@@ -245,11 +301,17 @@ var DiscordAdapter = class {
245
301
  ignoredChannels: this.cfg.ignoredChannels,
246
302
  participatingThreads: this.participatingThreads
247
303
  });
248
- event && (await this.resolveReplyToText(message, event), await this.maybeAutoThread(message, event), await this.downloadAttachments(message, event), this.dispatchEvent(event));
304
+ if (event) {
305
+ if (!isAuthorized(event.source, { ...loadGatewayConfig(), discord: this.cfg }, createPairingStore())) {
306
+ this.dispatchEvent(event);
307
+ return;
308
+ }
309
+ await this.resolveReplyToText(message, event), await this.maybeAutoThread(message, event), await this.downloadAttachments(message, event), this.dispatchEvent(event);
310
+ }
249
311
  }
250
- /** lunr: download + base64-encode inbound image attachments for one Discord message. */
312
+ /** Download and encode attachments after authorization. */
251
313
  async downloadAttachments(message, event) {
252
- let imageAttachments = collectDiscordImageAttachments(message);
314
+ let imageAttachments = Array.from(message.attachments?.values() ?? []);
253
315
  if (imageAttachments.length === 0)
254
316
  return;
255
317
  let attachments = [];
@@ -261,13 +323,72 @@ var DiscordAdapter = class {
261
323
  continue;
262
324
  attachments.push({
263
325
  data: Buffer.from(bytes).toString("base64"),
264
- mimeType: attachment.contentType ?? "image/png",
326
+ mimeType: attachment.contentType ?? "application/octet-stream",
265
327
  ...attachment.name ? { filename: attachment.name } : {}
266
328
  });
267
329
  } catch {
268
330
  }
269
331
  attachments.length > 0 && (event.attachments = attachments);
270
332
  }
333
+ async registerCommands(commands) {
334
+ this.client?.application && await this.client.application.commands.set(commands.slice(0, 95).map((command) => ({
335
+ name: command.name,
336
+ description: command.description.slice(0, 100),
337
+ options: [
338
+ {
339
+ name: "args",
340
+ description: "Command arguments or search",
341
+ type: ApplicationCommandOptionType.String,
342
+ required: !1,
343
+ autocomplete: ["mode", "thinking", "project", "sessions"].includes(command.name)
344
+ }
345
+ ]
346
+ })));
347
+ }
348
+ async handleNativeCommand(interaction) {
349
+ if (!interaction.isChatInputCommand?.() && !interaction.isAutocomplete?.() || !interaction.isChatInputCommand() && !interaction.isAutocomplete())
350
+ return !1;
351
+ let thread = interaction.channel?.isThread() ? interaction.channel : void 0, source = {
352
+ platform: "discord",
353
+ chatId: thread?.parentId ?? interaction.channelId ?? interaction.user.id,
354
+ threadId: thread?.id,
355
+ chatType: thread ? "thread" : interaction.inGuild() ? "channel" : "dm",
356
+ userId: interaction.user.id,
357
+ userName: interaction.user.username
358
+ }, cfg = loadGatewayConfig(), authorized = isAuthorized(source, cfg, createPairingStore());
359
+ if (interaction.isAutocomplete()) {
360
+ if (!authorized || !isGatewayOwner(source, cfg))
361
+ return await interaction.respond([]), !0;
362
+ let query = String(interaction.options.getFocused()).toLowerCase(), values = [];
363
+ if (interaction.commandName === "mode" && (values = ["manual", "yolo", "plan", "auto"]), this.commandSuggestions && ["thinking", "model"].includes(interaction.commandName) && (values = [...await this.commandSuggestions(source, interaction.commandName)]), interaction.commandName === "project" && (values = cfg.projectRoots ?? []), interaction.commandName === "sessions") {
364
+ let { SessionManager } = await import("./session-manager-CFN4UFM2.js");
365
+ values = (await Promise.race([
366
+ SessionManager.listAll(),
367
+ new Promise((resolve) => setTimeout(() => resolve([]), 1500))
368
+ ])).map((s) => s.name || s.id);
369
+ }
370
+ return await interaction.respond(values.filter((s) => s.toLowerCase().includes(query)).slice(0, 25).map((value) => ({ name: value.slice(0, 100), value: value.slice(0, 100) }))), !0;
371
+ }
372
+ return await interaction.reply({
373
+ content: authorized ? "Command received." : "Pair your account before using this command. Send the bot a private message for a pairing code.",
374
+ ephemeral: !0
375
+ }), authorized && this.handler?.({
376
+ text: `/${interaction.commandName}${interaction.options.getString("args") ? ` ${interaction.options.getString("args")}` : ""}`,
377
+ source,
378
+ messageId: "",
379
+ metadata: { mentionedBot: !0, nativeCommand: !0 }
380
+ }), !0;
381
+ }
382
+ async sendFile(chatId, file, opts) {
383
+ let channel = await this.fetchChannel(opts?.threadId ?? chatId);
384
+ if (!channel?.send)
385
+ return { success: !1, error: "Channel is unavailable." };
386
+ try {
387
+ return { success: !0, messageId: (await channel.send({ content: "", files: [file] })).id };
388
+ } catch (error) {
389
+ return { success: !1, error: errMessage(error) };
390
+ }
391
+ }
271
392
  async handleInteraction(interaction) {
272
393
  if (!this.callbackHandler || !interaction.isButton())
273
394
  return;
@@ -466,8 +587,9 @@ ${event.text}`,
466
587
  };
467
588
 
468
589
  // packages/coding-agent/dist/gateway/adapters/telegram.js
469
- var API_BASE = "https://api.telegram.org";
470
- var MARKDOWN_PARSE_ERROR_RE = /can't parse|parse entities/i, NOT_MODIFIED_RE = /message is not modified/i, TelegramApiError = class extends Error {
590
+ import { readFileSync } from "node:fs";
591
+ import { basename } from "node:path";
592
+ var API_BASE = "https://api.telegram.org", DEFAULT_DEBOUNCE_MS2 = 300, TYPING_INTERVAL_MS2 = 4e3, TYPING_SAFETY_MS2 = 12e4, REPLY_TO_TEXT_MAX2 = 500, MARKDOWN_PARSE_ERROR_RE = /can't parse|parse entities/i, NOT_MODIFIED_RE = /message is not modified/i, TelegramApiError = class extends Error {
471
593
  status;
472
594
  retryAfter;
473
595
  constructor(status, description, retryAfter) {
@@ -505,10 +627,7 @@ async function defaultCallApi(token, method, body, signal) {
505
627
  return data.result;
506
628
  }
507
629
  async function defaultDownloadFile(token, filePath) {
508
- let res = await fetch(`${API_BASE}/file/bot${token}/${filePath}`);
509
- if (!res.ok)
510
- throw new TelegramApiError(res.status, `file download HTTP ${res.status}`);
511
- return new Uint8Array(await res.arrayBuffer());
630
+ return downloadAttachment(`${API_BASE}/file/bot${token}/${filePath}`);
512
631
  }
513
632
  function errMessage2(err) {
514
633
  return err instanceof Error ? err.message : String(err);
@@ -550,9 +669,6 @@ function extractMention(text, entities, botInfo) {
550
669
  out += text.slice(pos, start), pos = Math.max(pos, end);
551
670
  return out += text.slice(pos), { text: out.trim(), mentioned };
552
671
  }
553
- function isImageMimeType(mimeType) {
554
- return typeof mimeType == "string" && mimeType.split(";")[0]?.trim().toLowerCase().startsWith("image/");
555
- }
556
672
  function collectTelegramMedia(message) {
557
673
  let descriptors = [];
558
674
  if (message.photo && message.photo.length > 0) {
@@ -563,9 +679,9 @@ function collectTelegramMedia(message) {
563
679
  ...largest.file_size !== void 0 ? { fileSize: largest.file_size } : {}
564
680
  });
565
681
  }
566
- return message.document && isImageMimeType(message.document.mime_type) && descriptors.push({
682
+ return message.document && descriptors.push({
567
683
  fileId: message.document.file_id,
568
- mimeType: message.document.mime_type ?? "image/jpeg",
684
+ mimeType: message.document.mime_type ?? "application/octet-stream",
569
685
  ...message.document.file_name ? { filename: message.document.file_name } : {},
570
686
  ...message.document.file_size !== void 0 ? { fileSize: message.document.file_size } : {}
571
687
  }), descriptors;
@@ -587,7 +703,7 @@ function updateToEvent(update, botInfo) {
587
703
  return {
588
704
  text,
589
705
  messageId: String(message.message_id),
590
- replyToText: replyText !== void 0 ? replyText.slice(0, 500) : void 0,
706
+ replyToText: replyText !== void 0 ? replyText.slice(0, REPLY_TO_TEXT_MAX2) : void 0,
591
707
  source: {
592
708
  platform: "telegram",
593
709
  chatId: String(message.chat.id),
@@ -630,8 +746,9 @@ var TelegramAdapter = class {
630
746
  abortController;
631
747
  pending = /* @__PURE__ */ new Map();
632
748
  typing = /* @__PURE__ */ new Map();
749
+ cfg;
633
750
  constructor(cfg, options = {}) {
634
- if (this.scheduler = options.scheduler ?? defaultScheduler2, this.debounceMs = options.debounceMs ?? 300, this.now = options.now ?? Date.now, options.callApi)
751
+ if (this.cfg = cfg, this.scheduler = options.scheduler ?? defaultScheduler2, this.debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS2, this.now = options.now ?? Date.now, options.callApi)
635
752
  this.callApi = options.callApi;
636
753
  else {
637
754
  if (!cfg.token)
@@ -708,7 +825,7 @@ var TelegramAdapter = class {
708
825
  let event = updateToEvent(update, this.botInfo);
709
826
  if (!event)
710
827
  return;
711
- if (update.message) {
828
+ if (update.message && isAuthorized(event.source, { ...loadGatewayConfig(), telegram: this.cfg }, createPairingStore())) {
712
829
  let attachments = await this.downloadMedia(update.message);
713
830
  attachments.length > 0 && (event.attachments = attachments);
714
831
  }
@@ -793,6 +910,20 @@ ${event.text}`,
793
910
  });
794
911
  }
795
912
  /** lunr: register the bot's slash-command menu (Telegram client autocomplete). Best-effort — caller catches. */
913
+ async sendFile(chatId, file, opts) {
914
+ try {
915
+ let data = new FormData();
916
+ data.set("chat_id", chatId), opts?.threadId && data.set("message_thread_id", opts.threadId), data.set("document", new Blob([new Uint8Array(readFileSync(file))]), basename(file));
917
+ let response = await fetch(`${API_BASE}/bot${this.cfg.token}/sendDocument`, {
918
+ method: "POST",
919
+ body: data,
920
+ signal: AbortSignal.timeout(3e4)
921
+ });
922
+ return response.ok ? { success: !0 } : { success: !1, error: `Telegram file delivery failed: HTTP ${response.status}` };
923
+ } catch {
924
+ return { success: !1, error: "Telegram file delivery failed. Check your connection." };
925
+ }
926
+ }
796
927
  async registerCommands(commands) {
797
928
  let valid = commands.filter((c) => /^[a-z0-9_]{1,32}$/.test(c.name) && c.description.trim().length > 0).map((c) => ({ command: c.name, description: c.description.trim().slice(0, 256) }));
798
929
  valid.length !== 0 && await this.callApi("setMyCommands", { commands: valid });
@@ -864,14 +995,14 @@ ${event.text}`,
864
995
  let interval = this.scheduler.setInterval(() => {
865
996
  let entry = this.typing.get(key);
866
997
  if (entry) {
867
- if (this.now() - entry.lastRefresh > 12e4) {
998
+ if (this.now() - entry.lastRefresh > TYPING_SAFETY_MS2) {
868
999
  this.stopTyping(key);
869
1000
  return;
870
1001
  }
871
1002
  this.callApi("sendChatAction", body).catch(() => {
872
1003
  });
873
1004
  }
874
- }, 4e3);
1005
+ }, TYPING_INTERVAL_MS2);
875
1006
  this.typing.set(key, { interval, lastRefresh: this.now() });
876
1007
  }
877
1008
  stopTyping(key) {
@@ -883,907 +1014,8 @@ function typingKey2(chatId, threadId) {
883
1014
  return `${chatId}:${threadId ?? ""}`;
884
1015
  }
885
1016
 
886
- // packages/coding-agent/dist/gateway/approval.js
887
- import { AsyncLocalStorage } from "node:async_hooks";
888
- import { randomBytes } from "node:crypto";
889
- var CALLBACK_PREFIX = "ap:", ID_LENGTH = 8, CODE_ALPHABET = "ABCDEFGHJKMNPQRSTUVWXYZ23456789", DEFAULT_TIMEOUT_MS = 300 * 1e3, approvalContext = new AsyncLocalStorage(), pending = /* @__PURE__ */ new Map();
890
- function generateId() {
891
- let id = "", bytes = randomBytes(ID_LENGTH);
892
- for (let i = 0; i < ID_LENGTH; i++)
893
- id += CODE_ALPHABET[bytes[i] % CODE_ALPHABET.length];
894
- return id;
895
- }
896
- function makeCallbackData(id, action) {
897
- return `${CALLBACK_PREFIX}${id}:${action}`;
898
- }
899
- function parseCallbackData(data) {
900
- if (!data.startsWith(CALLBACK_PREFIX))
901
- return null;
902
- let rest = data.slice(CALLBACK_PREFIX.length), colon = rest.indexOf(":");
903
- if (colon === -1)
904
- return null;
905
- let id = rest.slice(0, colon), action = rest.slice(colon + 1);
906
- return action === "once" || action === "session" || action === "reject" || action === "cancel" ? { id, action } : null;
907
- }
908
- function runWithApprovalContext(ctx, fn) {
909
- return approvalContext.run(ctx, fn);
910
- }
911
- async function createApprovalPrompt(adapter, source, req, timeoutMs = DEFAULT_TIMEOUT_MS) {
912
- let id = generateId(), title = req.kind === "large-subagent-launch" ? `Approve large subagent launch?
913
- ${req.detail}` : `Approve ${req.toolName}?
914
- ${req.detail}`, rows = [
915
- [
916
- { label: "\u2713 Approve once", data: makeCallbackData(id, "once") },
917
- { label: "\u2713 Approve session", data: makeCallbackData(id, "session") }
918
- ],
919
- [{ label: "\u2717 Reject", data: makeCallbackData(id, "reject") }]
920
- ];
921
- return new Promise((resolve, reject) => {
922
- let messageId, timeout = setTimeout(() => {
923
- pending.delete(id), reject(new Error("Approval request timed out")), messageId && adapter.editMessage(source.chatId, messageId, "\u23F1 Approval expired.", []).catch(() => {
924
- });
925
- }, timeoutMs);
926
- (async () => {
927
- try {
928
- let result = await adapter.sendButtons(source.chatId, title, rows, { threadId: source.threadId });
929
- if (!result.success || !result.messageId) {
930
- clearTimeout(timeout), reject(new Error(result.error ?? "Failed to send approval prompt"));
931
- return;
932
- }
933
- messageId = result.messageId, pending.set(id, {
934
- resolve,
935
- reject,
936
- timeout,
937
- messageId,
938
- adapter,
939
- chatId: source.chatId,
940
- threadId: source.threadId,
941
- userId: source.userId
942
- });
943
- } catch (err) {
944
- clearTimeout(timeout), reject(err);
945
- }
946
- })();
947
- });
948
- }
949
- async function createGatewayApprovalRequest(req) {
950
- let ctx = approvalContext.getStore();
951
- if (!ctx)
952
- throw new Error(NO_HANDLER_REASON);
953
- return createApprovalPrompt(ctx.adapter, ctx.source, req, ctx.timeoutMs);
954
- }
955
- function registerGatewayApprovalHandler() {
956
- registerApprovalHandler(createGatewayApprovalRequest);
957
- }
958
- async function handleApprovalCallback(event, adapter) {
959
- let parsed = parseCallbackData(event.data);
960
- if (!parsed)
961
- return !1;
962
- let entry = pending.get(parsed.id);
963
- if (!entry)
964
- return await adapter?.answerCallback(event.id, "Approval expired.").catch(() => {
965
- }), !0;
966
- if (event.userId !== entry.userId)
967
- return await entry.adapter.answerCallback(event.id, "\u26D4 Not your approval.").catch(() => {
968
- }), !0;
969
- clearTimeout(entry.timeout), pending.delete(parsed.id);
970
- let response = parsed.action === "reject" || parsed.action === "cancel" ? "reject" : parsed.action;
971
- entry.resolve(response);
972
- let label = parsed.action === "reject" || parsed.action === "cancel" ? "Rejected." : `Approved (${parsed.action}).`;
973
- return await entry.adapter.editMessage(entry.chatId, entry.messageId ?? "", label, []).catch(() => {
974
- }), await entry.adapter.answerCallback(event.id).catch(() => {
975
- }), !0;
976
- }
977
-
978
- // packages/coding-agent/dist/gateway/authz.js
979
- function envFlagEnabled(name) {
980
- let raw = process.env[name];
981
- return raw === void 0 ? !1 : ["1", "true", "yes", "on"].includes(raw.toLowerCase());
982
- }
983
- function globalAllowedUsers() {
984
- let raw = process.env.LUNR_GATEWAY_ALLOWED_USERS;
985
- return raw ? raw.split(",").map((u) => u.trim()).filter((u) => u.length > 0) : [];
986
- }
987
- function isAuthorized(source, cfg, pairing) {
988
- if (envFlagEnabled("LUNR_GATEWAY_ALLOW_ALL_USERS"))
989
- return !0;
990
- let platformCfg = platformConfigFor(cfg, source.platform);
991
- return !!(platformCfg?.allowedChats.includes(source.chatId) || source.roleAuthorized === !0 || source.userId && pairing.isPaired(source.platform, source.userId) || source.userId && (platformCfg?.allowedUsers.includes(source.userId) || globalAllowedUsers().includes(source.userId)));
992
- }
993
- function addAllowedUser(platform, userId) {
994
- let cfg = loadGatewayConfig(), platformCfg = platformConfigFor(cfg, platform);
995
- if (!platformCfg)
996
- throw new Error(`Unknown platform "${platform}"`);
997
- platformCfg.allowedUsers.includes(userId) || (platformCfg.allowedUsers.push(userId), saveGatewayConfig(cfg));
998
- }
999
-
1000
- // packages/coding-agent/dist/gateway/buttons.js
1001
- import { randomBytes as randomBytes2 } from "node:crypto";
1002
- var PICKER_TTL_MS = 900 * 1e3, SWEEP_INTERVAL_MS = 60 * 1e3, DEFAULT_PER_PAGE = 8, BUTTONS_PER_ROW = 2, CODE_ALPHABET2 = "ABCDEFGHJKMNPQRSTUVWXYZ23456789", CODE_LENGTH = 8, CALLBACK_PREFIX2 = "cp:", registry = /* @__PURE__ */ new Map(), sweeper;
1003
- function generatePickerId() {
1004
- let id = "", bytes = randomBytes2(CODE_LENGTH);
1005
- for (let i = 0; i < CODE_LENGTH; i++)
1006
- id += CODE_ALPHABET2[bytes[i] % CODE_ALPHABET2.length];
1007
- return id;
1008
- }
1009
- function makeCallbackData2(id, action, payload) {
1010
- return action === "noop" ? `${CALLBACK_PREFIX2}${id}:` : action === "cancel" ? `${CALLBACK_PREFIX2}${id}:x` : action === "page" ? `${CALLBACK_PREFIX2}${id}:p${payload ?? "0"}` : `${CALLBACK_PREFIX2}${id}:${payload ?? ""}`;
1011
- }
1012
- function parseCallbackData2(data) {
1013
- if (!data.startsWith(CALLBACK_PREFIX2))
1014
- return null;
1015
- let rest = data.slice(CALLBACK_PREFIX2.length), colon = rest.indexOf(":");
1016
- if (colon === -1)
1017
- return null;
1018
- let id = rest.slice(0, colon), tail = rest.slice(colon + 1);
1019
- return tail === "" ? { id, action: "noop" } : tail === "x" ? { id, action: "cancel" } : tail.startsWith("p") ? { id, action: "page", payload: tail.slice(1) } : { id, action: "select", payload: tail };
1020
- }
1021
- function isSameChat(a, b) {
1022
- return a.platform === b.platform && a.chatId === b.chatId && a.threadId === b.threadId;
1023
- }
1024
- function sourceForAuth(picker, cb) {
1025
- return {
1026
- platform: picker.source.platform,
1027
- chatId: cb.chatId,
1028
- chatType: picker.source.chatType,
1029
- userId: cb.userId,
1030
- userName: cb.userName,
1031
- threadId: cb.threadId,
1032
- // Do NOT inherit the original invoker's role authorization.
1033
- roleAuthorized: void 0
1034
- };
1035
- }
1036
- function renderPage(picker) {
1037
- let { id, title, breadcrumbs, items, page, perPage } = picker, totalPages = Math.max(1, Math.ceil(items.length / perPage)), clampedPage = Math.max(0, Math.min(page, totalPages - 1)), start = clampedPage * perPage, pageItems = items.slice(start, start + perPage), rows = [];
1038
- for (let i = 0; i < pageItems.length; i += BUTTONS_PER_ROW) {
1039
- let row = [];
1040
- for (let j = 0; j < BUTTONS_PER_ROW && i + j < pageItems.length; j++) {
1041
- let index = start + i + j;
1042
- row.push({ label: pageItems[i + j].label, data: makeCallbackData2(id, "select", String(index)) });
1043
- }
1044
- rows.push(row);
1045
- }
1046
- if (totalPages > 1) {
1047
- let nav = [];
1048
- clampedPage > 0 && nav.push({ label: "\u25C0 Prev", data: makeCallbackData2(id, "page", String(clampedPage - 1)) }), nav.push({ label: `${clampedPage + 1}/${totalPages}`, data: makeCallbackData2(id, "noop") }), clampedPage < totalPages - 1 && nav.push({ label: "Next \u25B6", data: makeCallbackData2(id, "page", String(clampedPage + 1)) }), rows.push(nav);
1049
- }
1050
- rows.push([{ label: "\u2717 Cancel", data: makeCallbackData2(id, "cancel") }]);
1051
- let header = breadcrumbs ? `${title} \xB7 ${breadcrumbs}` : title;
1052
- return { text: items.length === 0 ? `${header}
1053
-
1054
- No options available.` : `${header} (${clampedPage + 1}/${totalPages}):`, buttons: rows };
1055
- }
1056
- async function createPicker(adapter, source, spec, opts) {
1057
- let id = generatePickerId(), pending2 = {
1058
- ...spec,
1059
- id,
1060
- chatId: source.chatId,
1061
- source,
1062
- adapter,
1063
- messageId: "",
1064
- page: 0,
1065
- perPage: spec.perPage ?? DEFAULT_PER_PAGE,
1066
- createdAt: Date.now()
1067
- }, { text, buttons } = renderPage(pending2), result = await adapter.sendButtons(source.chatId, text, buttons, opts);
1068
- return result.success && result.messageId && (pending2.messageId = result.messageId, registry.set(id, pending2)), result;
1069
- }
1070
- async function answerExpired(adapter, cb) {
1071
- await adapter.answerCallback(cb.id, "Picker expired \u2014 run the command again.").catch(() => {
1072
- });
1073
- }
1074
- async function answerUnauthorized(adapter, cb) {
1075
- await adapter.answerCallback(cb.id, "\u26D4 Not authorized.").catch(() => {
1076
- });
1077
- }
1078
- async function answerNotYourPicker(adapter, cb) {
1079
- await adapter.answerCallback(cb.id, "\u26D4 Not your picker.").catch(() => {
1080
- });
1081
- }
1082
- async function handleCallback(cb, deps) {
1083
- let { adapter, cfg, pairing } = deps, parsed = parseCallbackData2(cb.data);
1084
- if (!parsed)
1085
- return;
1086
- let picker = registry.get(parsed.id);
1087
- if (!picker) {
1088
- await answerExpired(adapter, cb);
1089
- return;
1090
- }
1091
- if (Date.now() > picker.createdAt + PICKER_TTL_MS) {
1092
- registry.delete(parsed.id);
1093
- try {
1094
- await picker.adapter.editMessage(picker.chatId, picker.messageId, "\u23F1 Expired \u2014 run the command again.", []);
1095
- } catch {
1096
- }
1097
- await answerExpired(adapter, cb);
1098
- return;
1099
- }
1100
- if (!isSameChat(picker.source, sourceForAuth(picker, cb))) {
1101
- await answerExpired(adapter, cb);
1102
- return;
1103
- }
1104
- if (cb.userId !== picker.invokerId) {
1105
- await answerNotYourPicker(adapter, cb);
1106
- return;
1107
- }
1108
- if (!isAuthorized(sourceForAuth(picker, cb), cfg, pairing)) {
1109
- await answerUnauthorized(adapter, cb);
1110
- return;
1111
- }
1112
- if (parsed.action === "cancel") {
1113
- registry.delete(parsed.id);
1114
- try {
1115
- await picker.adapter.editMessage(picker.chatId, picker.messageId, "Cancelled.", []);
1116
- } catch {
1117
- }
1118
- await adapter.answerCallback(cb.id).catch(() => {
1119
- });
1120
- return;
1121
- }
1122
- if (parsed.action === "noop") {
1123
- await adapter.answerCallback(cb.id).catch(() => {
1124
- });
1125
- return;
1126
- }
1127
- if (parsed.action === "page") {
1128
- let page = Number.parseInt(parsed.payload ?? "0", 10), totalPages = Math.max(1, Math.ceil(picker.items.length / picker.perPage));
1129
- picker.page = Math.max(0, Math.min(page, totalPages - 1));
1130
- let { text, buttons } = renderPage(picker);
1131
- try {
1132
- await picker.adapter.editMessage(picker.chatId, picker.messageId, text, buttons);
1133
- } catch {
1134
- }
1135
- await adapter.answerCallback(cb.id).catch(() => {
1136
- });
1137
- return;
1138
- }
1139
- let index = Number.parseInt(parsed.payload ?? "", 10), item = picker.items[index];
1140
- if (!item) {
1141
- await adapter.answerCallback(cb.id, "Invalid selection.").catch(() => {
1142
- });
1143
- return;
1144
- }
1145
- try {
1146
- let result = await picker.resolve(item);
1147
- if (!result.done) {
1148
- picker.items = result.items, picker.title = result.title, picker.breadcrumbs = result.breadcrumbs, picker.page = 0;
1149
- let { text, buttons } = renderPage(picker);
1150
- try {
1151
- await picker.adapter.editMessage(picker.chatId, picker.messageId, text, buttons);
1152
- } catch {
1153
- }
1154
- await adapter.answerCallback(cb.id).catch(() => {
1155
- });
1156
- return;
1157
- }
1158
- registry.delete(parsed.id);
1159
- try {
1160
- await picker.adapter.editMessage(picker.chatId, picker.messageId, result.text, []);
1161
- } catch {
1162
- }
1163
- await adapter.answerCallback(cb.id).catch(() => {
1164
- });
1165
- } catch (err) {
1166
- registry.delete(parsed.id);
1167
- let message = err instanceof Error ? err.message : String(err);
1168
- try {
1169
- await picker.adapter.editMessage(picker.chatId, picker.messageId, `\u26A0 ${message}`, []);
1170
- } catch {
1171
- }
1172
- await adapter.answerCallback(cb.id).catch(() => {
1173
- });
1174
- }
1175
- }
1176
- function startButtonSweeper() {
1177
- sweeper || (sweeper = setInterval(() => {
1178
- let cutoff = Date.now() - PICKER_TTL_MS;
1179
- for (let [id, entry] of registry)
1180
- if (!(entry.createdAt > cutoff)) {
1181
- registry.delete(id);
1182
- try {
1183
- entry.adapter.editMessage(entry.chatId, entry.messageId, "\u23F1 Expired \u2014 run the command again.", []);
1184
- } catch {
1185
- }
1186
- }
1187
- }, SWEEP_INTERVAL_MS), sweeper.unref?.());
1188
- }
1189
- function stopButtonSweeper() {
1190
- sweeper && (clearInterval(sweeper), sweeper = void 0);
1191
- }
1192
-
1193
- // packages/coding-agent/dist/gateway/commands.js
1194
- var MODEL_CACHE_TTL_MS = 300 * 1e3, SESSIONS_CACHE_TTL_MS = 300 * 1e3, MAX_MODEL_LIST_LINES = 15, PROVIDER_PER_PAGE = 10;
1195
- var MAX_MODELS_PER_PROVIDER = 50, MODEL_ID_MAX = 38, MODEL_CACHE_CAP = 100, SESSIONS_CACHE_CAP = 100, modelCache = /* @__PURE__ */ new Map(), sessionsCache = /* @__PURE__ */ new Map();
1196
- function evictLRU(map, cap) {
1197
- for (; map.size > cap; ) {
1198
- let first = map.keys().next().value;
1199
- first !== void 0 && map.delete(first);
1200
- }
1201
- }
1202
- function getCachedModels(key) {
1203
- let cached = modelCache.get(key);
1204
- if (cached) {
1205
- if (Date.now() > cached.expires) {
1206
- modelCache.delete(key);
1207
- return;
1208
- }
1209
- return modelCache.delete(key), modelCache.set(key, cached), cached.models;
1210
- }
1211
- }
1212
- function setCachedModels(key, models) {
1213
- modelCache.set(key, { models, expires: Date.now() + MODEL_CACHE_TTL_MS }), evictLRU(modelCache, MODEL_CACHE_CAP);
1214
- }
1215
- function getCachedSessions(key) {
1216
- let cached = sessionsCache.get(key);
1217
- if (cached) {
1218
- if (Date.now() > cached.expires) {
1219
- sessionsCache.delete(key);
1220
- return;
1221
- }
1222
- return sessionsCache.delete(key), sessionsCache.set(key, cached), cached.sessions;
1223
- }
1224
- }
1225
- function setCachedSessions(key, sessions) {
1226
- sessionsCache.set(key, { sessions, expires: Date.now() + SESSIONS_CACHE_TTL_MS }), evictLRU(sessionsCache, SESSIONS_CACHE_CAP);
1227
- }
1228
- function oneLine(text) {
1229
- return text.replace(/\s+/g, " ").trim().slice(0, 200);
1230
- }
1231
- function prefixLines(text, prefix) {
1232
- return text.split(`
1233
- `).map((line, index) => (index === 0 ? prefix : " ") + line).join(`
1234
- `);
1235
- }
1236
- function truncate(text, max) {
1237
- return text.length <= max ? text : `${text.slice(0, max - 1)}\u2026`;
1238
- }
1239
- function formatRelativeTime(date) {
1240
- let seconds = Math.max(0, Math.floor((Date.now() - date.getTime()) / 1e3));
1241
- if (seconds < 60)
1242
- return `${seconds}s ago`;
1243
- let minutes = Math.floor(seconds / 60);
1244
- if (minutes < 60)
1245
- return `${minutes}m ago`;
1246
- let hours = Math.floor(minutes / 60);
1247
- return hours < 24 ? `${hours}h ago` : `${Math.floor(hours / 24)}d ago`;
1248
- }
1249
- function formatModelList(models, maxLines = MAX_MODEL_LIST_LINES) {
1250
- if (models.length === 0)
1251
- return ["No models available."];
1252
- let byProvider = /* @__PURE__ */ new Map();
1253
- for (let model of models) {
1254
- let list = byProvider.get(model.provider) ?? [];
1255
- list.push(model), byProvider.set(model.provider, list);
1256
- }
1257
- let providers = [...byProvider.keys()].sort(), lines = [], index = 1;
1258
- for (let provider of providers) {
1259
- let list = (byProvider.get(provider) ?? []).sort((a, b) => a.id.localeCompare(b.id));
1260
- for (let model of list)
1261
- lines.push(`${index}) ${provider}/${model.id}`), index++;
1262
- }
1263
- return lines.length <= maxLines ? lines : [
1264
- `${models.length} models across ${providers.length} providers.`,
1265
- "Use /model <provider/id> or /model <query>.",
1266
- ...providers.map((p) => `- ${p}`)
1267
- ];
1268
- }
1269
- function groupModelsByProvider(models) {
1270
- let byProvider = /* @__PURE__ */ new Map();
1271
- for (let model of models) {
1272
- let list = byProvider.get(model.provider) ?? [];
1273
- list.push(model), byProvider.set(model.provider, list);
1274
- }
1275
- for (let list of byProvider.values())
1276
- list.sort((a, b) => a.id.localeCompare(b.id));
1277
- return byProvider;
1278
- }
1279
- function truncateModelId(id) {
1280
- let lastSlash = id.lastIndexOf("/"), name = lastSlash >= 0 ? id.slice(lastSlash + 1) : id;
1281
- return truncate(name, MODEL_ID_MAX);
1282
- }
1283
- async function refreshAndCacheModels(session, key) {
1284
- let runtime = session.modelRuntime;
1285
- await runtime.refresh();
1286
- let models = [...await runtime.getAvailable()].sort((a, b) => a.provider.localeCompare(b.provider) || a.id.localeCompare(b.id));
1287
- return setCachedModels(key, models), models;
1288
- }
1289
- async function setModelAndReply(ctx, model) {
1290
- let session = ctx.session;
1291
- try {
1292
- await session.setModel(model);
1293
- } catch (error) {
1294
- if ((error instanceof Error ? error.message : String(error)).includes("No API key")) {
1295
- await ctx.reply(`No API key for ${model.provider}.`);
1296
- return;
1297
- }
1298
- throw error;
1299
- }
1300
- await ctx.reply(`Model \u2192 ${model.provider}/${model.id} (thinking level re-clamped to ${session.thinkingLevel})`);
1301
- }
1302
- var newCommand = {
1303
- name: "new",
1304
- aliases: ["reset"],
1305
- description: "start a fresh session for this chat",
1306
- // lunr: busy-session /new must bypass the guard (same as /stop).
1307
- bypassBusy: !0,
1308
- needsSession: !1,
1309
- async handler(ctx) {
1310
- await ctx.bridge.reset(ctx.key), await ctx.reply("Session reset \u2014 next message starts a fresh session.");
1311
- }
1312
- }, undoCommand = {
1313
- name: "undo",
1314
- description: "rewind the session tree before the last user turn",
1315
- bypassBusy: !1,
1316
- needsSession: !0,
1317
- async handler(ctx) {
1318
- let { userText } = await ctx.bridge.undo(ctx.key), text = userText ? ` "${truncate(userText, 60)}"` : "";
1319
- await ctx.reply(`Undone \u2014 rewound before${text}. /redo to restore.`);
1320
- }
1321
- }, redoCommand = {
1322
- name: "redo",
1323
- description: "restore the last undone turn",
1324
- bypassBusy: !1,
1325
- needsSession: !0,
1326
- async handler(ctx) {
1327
- await ctx.bridge.redo(ctx.key), await ctx.reply("Redone.");
1328
- }
1329
- }, stopCommand = {
1330
- name: "stop",
1331
- description: "abort the running turn",
1332
- bypassBusy: !0,
1333
- needsSession: !1,
1334
- async handler(ctx) {
1335
- await ctx.bridge.abort(ctx.key), await ctx.reply("Stopped.");
1336
- }
1337
- }, statusCommand = {
1338
- name: "status",
1339
- description: "session status",
1340
- bypassBusy: !0,
1341
- needsSession: !1,
1342
- async handler(ctx) {
1343
- let status = ctx.bridge.getStatus(ctx.key), age = status.createdAt ? `${Math.max(0, Math.round((Date.now() - Date.parse(status.createdAt)) / 1e3))}s` : "no session yet";
1344
- await ctx.reply(`${ctx.event.source.platform} \xB7 session age ${age} \xB7 ${status.busy ? "busy" : "idle"} \xB7 queue ${status.queueDepth}`);
1345
- }
1346
- }, whoamiCommand = {
1347
- name: "whoami",
1348
- description: "your ids and session key",
1349
- bypassBusy: !0,
1350
- needsSession: !1,
1351
- async handler(ctx) {
1352
- let { userId, chatId } = ctx.event.source;
1353
- await ctx.reply(`userId: ${userId}
1354
- chatId: ${chatId}
1355
- key: ${ctx.key}`);
1356
- }
1357
- }, helpCommand = {
1358
- name: "help",
1359
- description: "this message",
1360
- bypassBusy: !0,
1361
- needsSession: !1,
1362
- async handler(ctx) {
1363
- await ctx.reply(formatHelpText());
1364
- }
1365
- }, modelCommand = {
1366
- name: "model",
1367
- description: "list or switch models",
1368
- bypassBusy: !1,
1369
- needsSession: !0,
1370
- async handler(ctx) {
1371
- let session = ctx.session, arg = ctx.args.trim();
1372
- if (!arg) {
1373
- let models2 = getCachedModels(ctx.key) ?? await refreshAndCacheModels(session, ctx.key), byProvider = groupModelsByProvider(models2), current = session.model, currentRef = current ? `${current.provider}/${current.id}` : "", providerItems = [...byProvider.keys()].sort().map((provider) => ({
1374
- label: `${current?.provider === provider ? "\u2713 " : ""}${provider} (${byProvider.get(provider)?.length ?? 0})`,
1375
- value: provider
1376
- })), pickerOpts = {
1377
- replyTo: ctx.event.messageId,
1378
- threadId: ctx.event.source.threadId
1379
- };
1380
- if (!(await createPicker(ctx.adapter, ctx.event.source, {
1381
- kind: "model",
1382
- sessionKey: ctx.key,
1383
- invokerId: ctx.event.source.userId,
1384
- items: providerItems,
1385
- perPage: PROVIDER_PER_PAGE,
1386
- title: "Pick a model provider",
1387
- async resolve(item) {
1388
- if (item.value === "__back__")
1389
- return { done: !1, items: providerItems, title: "Pick a model provider" };
1390
- if (!item.value.includes("/")) {
1391
- let provider = item.value, list = byProvider.get(provider) ?? [], capped = list.slice(0, MAX_MODELS_PER_PROVIDER), note = list.length > MAX_MODELS_PER_PROVIDER ? ` \u2014 ${list.length - MAX_MODELS_PER_PROVIDER} more available` : "", modelItems = [];
1392
- for (let m of capped) {
1393
- let ref = `${provider}/${m.id}`;
1394
- modelItems.push({
1395
- label: `${currentRef === ref ? "\u2713 " : ""}${truncateModelId(m.id)}`,
1396
- value: ref
1397
- });
1398
- }
1399
- return modelItems.unshift({ label: "\u25C0 Back", value: "__back__" }), {
1400
- done: !1,
1401
- items: modelItems,
1402
- title: `${provider} (${list.length})${note}`,
1403
- breadcrumbs: provider
1404
- };
1405
- }
1406
- let exact2 = findExactModelReferenceMatch(item.value, models2);
1407
- if (!exact2)
1408
- return { done: !0, text: "That model is no longer available." };
1409
- try {
1410
- await session.setModel(exact2);
1411
- } catch (error) {
1412
- if ((error instanceof Error ? error.message : String(error)).includes("No API key"))
1413
- return { done: !0, text: `No API key for ${exact2.provider}.` };
1414
- throw error;
1415
- }
1416
- return {
1417
- done: !0,
1418
- text: `\u263E Model \u2192 ${exact2.provider}/${exact2.id} (thinking level re-clamped to ${session.thinkingLevel})`
1419
- };
1420
- }
1421
- }, pickerOpts)).success) {
1422
- let currentText = `${current?.provider ?? "none"}/${current?.id ?? "none"}`, lines = formatModelList(models2);
1423
- await ctx.reply([`Current: ${currentText}`, ...lines, "Use /model <n> or /model <provider/id>"].join(`
1424
- `));
1425
- }
1426
- return;
1427
- }
1428
- let numeric = Number.parseInt(arg, 10);
1429
- if (!Number.isNaN(numeric) && /^\d+$/.test(arg)) {
1430
- let cached = getCachedModels(ctx.key);
1431
- if (!cached || numeric < 1 || numeric > cached.length) {
1432
- await ctx.reply("Invalid selection \u2014 run /model to see the current list.");
1433
- return;
1434
- }
1435
- await setModelAndReply(ctx, cached[numeric - 1]);
1436
- return;
1437
- }
1438
- let models = getCachedModels(ctx.key) ?? await refreshAndCacheModels(session, ctx.key), exact = findExactModelReferenceMatch(arg, models);
1439
- if (exact) {
1440
- await setModelAndReply(ctx, exact);
1441
- return;
1442
- }
1443
- let query = arg.toLowerCase(), matches = models.filter((m) => m.id.toLowerCase().includes(query) || `${m.provider}/${m.id}`.toLowerCase().includes(query) || (m.name?.toLowerCase().includes(query) ?? !1));
1444
- if (matches.length === 0) {
1445
- await ctx.reply(`No model matches "${arg}".`);
1446
- return;
1447
- }
1448
- if (matches.length > 1) {
1449
- await ctx.reply(formatModelList(matches).join(`
1450
- `));
1451
- return;
1452
- }
1453
- await setModelAndReply(ctx, matches[0]);
1454
- }
1455
- }, sessionsCommand = {
1456
- name: "sessions",
1457
- description: "list or switch sessions",
1458
- bypassBusy: !1,
1459
- needsSession: !0,
1460
- async handler(ctx) {
1461
- let session = ctx.session, arg = ctx.args.trim(), currentFile = session.sessionManager?.getSessionFile();
1462
- if (!arg) {
1463
- let all = await SessionManager.list(process.cwd());
1464
- all.sort((a, b) => b.modified.getTime() - a.modified.getTime());
1465
- let sessions = all.slice(0, 10);
1466
- setCachedSessions(ctx.key, sessions);
1467
- let items = sessions.map((s) => ({
1468
- label: `${s.path === currentFile ? "\u263E " : ""}${s.name ? truncate(s.name, 30) : truncate(s.firstMessage, 30) || "(empty)"}`,
1469
- value: s.path
1470
- }));
1471
- if (!(await createPicker(ctx.adapter, ctx.event.source, {
1472
- kind: "sessions",
1473
- sessionKey: ctx.key,
1474
- invokerId: ctx.event.source.userId,
1475
- items,
1476
- perPage: 8,
1477
- title: "Pick a session",
1478
- async resolve(item) {
1479
- await ctx.bridge.switchSession(ctx.key, item.value);
1480
- let switched2 = await ctx.bridge.getSession(ctx.key), name2 = switched2?.sessionManager?.getSessionName() ?? "unnamed", count2 = switched2?.sessionManager?.getEntries().length ?? 0;
1481
- return { done: !0, text: `\u263E Switched to "${name2}" (${count2} msgs). History continues here.` };
1482
- }
1483
- }, { replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId })).success) {
1484
- let lines = sessions.map((s, i) => {
1485
- let marker = s.path === currentFile ? " \u263E" : "", label = s.name ? truncate(s.name, 40) : truncate(s.firstMessage, 40) || "(empty)";
1486
- return `${i + 1}) ${label}${marker} \xB7 ${formatRelativeTime(s.modified)} \xB7 ${s.messageCount} msgs`;
1487
- });
1488
- await ctx.reply([`Sessions for ${process.cwd()}`, ...lines, "Use /sessions <n> or /sessions <id-prefix>"].join(`
1489
- `));
1490
- }
1491
- return;
1492
- }
1493
- let path, numeric = Number.parseInt(arg, 10), cached = getCachedSessions(ctx.key);
1494
- if (!Number.isNaN(numeric) && /^\d+$/.test(arg)) {
1495
- if (!cached || numeric < 1 || numeric > cached.length) {
1496
- await ctx.reply("Invalid selection \u2014 run /sessions to see the current list.");
1497
- return;
1498
- }
1499
- path = cached[numeric - 1].path;
1500
- } else {
1501
- let matches = (cached ?? await SessionManager.list(process.cwd())).filter((s) => s.id.startsWith(arg) || s.path.startsWith(arg));
1502
- if (matches.length === 0) {
1503
- await ctx.reply(`No session matches "${arg}".`);
1504
- return;
1505
- }
1506
- if (matches.length > 1) {
1507
- await ctx.reply(`Multiple sessions match "${arg}" \u2014 use a longer prefix.`);
1508
- return;
1509
- }
1510
- path = matches[0].path;
1511
- }
1512
- if (!path) {
1513
- await ctx.reply("No session selected.");
1514
- return;
1515
- }
1516
- await ctx.bridge.switchSession(ctx.key, path);
1517
- let switched = await ctx.bridge.getSession(ctx.key), name = switched?.sessionManager?.getSessionName() ?? "unnamed", count = switched?.sessionManager?.getEntries().length ?? 0;
1518
- await ctx.reply(`Switched to "${name}" (${count} msgs). History continues here.`);
1519
- }
1520
- }, titleCommand = {
1521
- name: "title",
1522
- aliases: ["name"],
1523
- description: "name the current session",
1524
- bypassBusy: !0,
1525
- needsSession: !0,
1526
- async handler(ctx) {
1527
- let session = ctx.session, name = ctx.args.trim();
1528
- if (!name) {
1529
- let current = session.sessionManager?.getSessionName();
1530
- await ctx.reply(current ? `Session title: ${current}` : "(unnamed)");
1531
- return;
1532
- }
1533
- session.setSessionName(name);
1534
- let readBack = session.sessionManager?.getSessionName() ?? name;
1535
- await ctx.reply(`Session titled "${readBack}".`);
1536
- }
1537
- }, contextCommand = {
1538
- name: "context",
1539
- description: "estimated context window breakdown",
1540
- bypassBusy: !0,
1541
- needsSession: !0,
1542
- async handler(ctx) {
1543
- let session = ctx.session, model = session.model;
1544
- if (!model) {
1545
- await ctx.reply("No model selected.");
1546
- return;
1547
- }
1548
- let contextWindow = model.contextWindow ?? 0;
1549
- if (contextWindow <= 0) {
1550
- await ctx.reply("Context window unknown for this model.");
1551
- return;
1552
- }
1553
- let tools = session.getActiveToolNames().map((name) => session.getToolDefinition(name)).filter((d) => d !== void 0), breakdown = computeContextBreakdown({
1554
- systemPrompt: session.systemPrompt,
1555
- tools,
1556
- messages: session.messages,
1557
- contextWindow
1558
- }), usage = session.getContextUsage(), tokens = usage?.tokens ?? breakdown.total, percent = usage?.percent ?? (contextWindow > 0 ? tokens / contextWindow * 100 : 0), messageTokens = breakdown.user + breakdown.assistantText + breakdown.thinking + breakdown.toolCalls + breakdown.toolResults + breakdown.summaries, stats = session.getSessionStats();
1559
- await ctx.reply([
1560
- `Context: ${tokens.toLocaleString("en-US")} / ${contextWindow.toLocaleString("en-US")} tokens (${Math.round(percent)}%)`,
1561
- ` system ${breakdown.systemPrompt.toLocaleString("en-US")} \xB7 tools ${breakdown.toolDefinitions.toLocaleString("en-US")} \xB7 messages ${messageTokens.toLocaleString("en-US")}`,
1562
- `Session: ${stats.totalMessages} messages \xB7 ${stats.userMessages} turns \xB7 model ${model.provider}/${model.id}`
1563
- ].join(`
1564
- `));
1565
- }
1566
- }, compactCommand = {
1567
- name: "compact",
1568
- description: "summarize and compress session context",
1569
- bypassBusy: !0,
1570
- needsSession: !0,
1571
- async handler(ctx) {
1572
- let session = ctx.session, instructions = ctx.args.trim() || void 0;
1573
- try {
1574
- let result = await session.compact(instructions), before = result.tokensBefore.toLocaleString("en-US"), after = result.estimatedTokensAfter !== void 0 ? `~${result.estimatedTokensAfter.toLocaleString("en-US")}` : "~?";
1575
- await ctx.reply(`Compacted: ${before} \u2192 ${after} tokens.`);
1576
- } catch (error) {
1577
- let message = error instanceof Error ? error.message : String(error);
1578
- if (message.includes("Nothing to compact") || message.includes("Already compacted") || message.includes("No model selected")) {
1579
- await ctx.reply(message);
1580
- return;
1581
- }
1582
- throw error;
1583
- }
1584
- }
1585
- }, thinkingCommand = {
1586
- name: "thinking",
1587
- aliases: ["effort", "reasoning"],
1588
- description: "get or set the thinking level",
1589
- bypassBusy: !1,
1590
- needsSession: !0,
1591
- async handler(ctx) {
1592
- let session = ctx.session, levels = session.getAvailableThinkingLevels();
1593
- if (!session.supportsThinking()) {
1594
- await ctx.reply(`Model ${session.model?.provider ?? "none"}/${session.model?.id ?? "none"} doesn't support thinking.`);
1595
- return;
1596
- }
1597
- let arg = ctx.args.trim().toLowerCase();
1598
- if (!arg) {
1599
- let current = session.thinkingLevel, items = levels.map((level) => ({
1600
- label: `${level === current ? "\u2713 " : ""}${level}`,
1601
- value: level
1602
- }));
1603
- (await createPicker(ctx.adapter, ctx.event.source, {
1604
- kind: "thinking",
1605
- sessionKey: ctx.key,
1606
- invokerId: ctx.event.source.userId,
1607
- items,
1608
- perPage: levels.length,
1609
- title: "Pick a thinking level",
1610
- async resolve(item) {
1611
- let level = item.value;
1612
- return session.setThinkingLevel(level), { done: !0, text: `\u263E Thinking \u2192 ${level}` };
1613
- }
1614
- }, { replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId })).success || await ctx.reply(`Level: ${current} \u2014 available: ${levels.join(", ")}`);
1615
- return;
1616
- }
1617
- if (!levels.includes(arg)) {
1618
- await ctx.reply(`Invalid level. Available: ${levels.join(", ")}`);
1619
- return;
1620
- }
1621
- session.setThinkingLevel(arg), await ctx.reply(`Thinking \u2192 ${arg}`);
1622
- }
1623
- }, CHAT_COMMANDS = [
1624
- newCommand,
1625
- undoCommand,
1626
- redoCommand,
1627
- stopCommand,
1628
- statusCommand,
1629
- whoamiCommand,
1630
- helpCommand,
1631
- modelCommand,
1632
- sessionsCommand,
1633
- titleCommand,
1634
- contextCommand,
1635
- compactCommand,
1636
- thinkingCommand
1637
- ];
1638
- function botCommandSpecs() {
1639
- return CHAT_COMMANDS.map((c) => ({ name: c.name, description: c.description }));
1640
- }
1641
- function formatHelpText() {
1642
- let lines = ["lunR gateway commands:"];
1643
- for (let cmd of CHAT_COMMANDS) {
1644
- let names = [cmd.name, ...cmd.aliases ?? []].map((a) => `/${a}`).join(" | ");
1645
- lines.push(`${names} \u2014 ${cmd.description}`);
1646
- }
1647
- return lines.push(""), lines.push("Tap to pick: /model, /thinking, /sessions (run without args to see buttons)."), lines.join(`
1648
- `);
1649
- }
1650
- async function runChatCommand(cmd, ctx) {
1651
- if (!cmd.bypassBusy && ctx.bridge.getStatus(ctx.key).busy)
1652
- return await ctx.reply("\u23F3 busy \u2014 /stop first or wait"), !0;
1653
- if (cmd.needsSession) {
1654
- let session = await ctx.bridge.getSession(ctx.key);
1655
- if (!session)
1656
- return await ctx.reply("No session yet \u2014 send a message first."), !0;
1657
- ctx.session = session;
1658
- }
1659
- try {
1660
- return await cmd.handler(ctx) ?? !0;
1661
- } catch (err) {
1662
- let message = err instanceof Error ? err.message : String(err);
1663
- return await ctx.adapter.send(ctx.event.source.chatId, `\u26A0 ${oneLine(message)}`, {
1664
- replyTo: ctx.event.messageId,
1665
- threadId: ctx.event.source.threadId
1666
- }), !0;
1667
- }
1668
- }
1669
- async function sendCommandReply(adapter, event, text) {
1670
- await adapter.send(event.source.chatId, prefixLines(text, "\u263E "), {
1671
- replyTo: event.messageId,
1672
- threadId: event.source.threadId
1673
- });
1674
- }
1675
-
1676
- // packages/coding-agent/dist/gateway/pairing.js
1677
- import { randomInt } from "node:crypto";
1678
- import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
1679
- import { join } from "node:path";
1680
- var CODE_ALPHABET3 = "ABCDEFGHJKMNPQRSTUVWXYZ23456789", CODE_LENGTH2 = 8, DEFAULT_TTL_MS = 3600 * 1e3, DEFAULT_RATE_LIMIT_MS = 600 * 1e3, DEFAULT_MAX_PENDING = 3, DEFAULT_MAX_FAILS = 5, DEFAULT_LOCKOUT_MS = 3600 * 1e3;
1681
- function normalizeCode(code) {
1682
- return code.replace(/-/g, "").trim().toUpperCase();
1683
- }
1684
- function generateCode() {
1685
- let code = "";
1686
- for (let i = 0; i < CODE_LENGTH2; i++)
1687
- code += CODE_ALPHABET3[randomInt(0, CODE_ALPHABET3.length)];
1688
- return code;
1689
- }
1690
- function createPairingStore(options = {}) {
1691
- let dir = options.dir ?? join(getAgentDir(), "pairing"), ttlMs = options.ttlMs ?? DEFAULT_TTL_MS, rateLimitMs = options.rateLimitMs ?? DEFAULT_RATE_LIMIT_MS, maxPending = options.maxPending ?? DEFAULT_MAX_PENDING, maxFails = options.maxFails ?? DEFAULT_MAX_FAILS, lockoutMs = options.lockoutMs ?? DEFAULT_LOCKOUT_MS, now = options.now ?? (() => Date.now());
1692
- function storeFile() {
1693
- return join(dir, "store.json");
1694
- }
1695
- function read() {
1696
- let path = storeFile();
1697
- if (!existsSync(path))
1698
- return { pending: [], approved: [] };
1699
- try {
1700
- let parsed = JSON.parse(readFileSync(path, "utf-8"));
1701
- if (parsed == null || typeof parsed != "object" || Array.isArray(parsed))
1702
- return { pending: [], approved: [] };
1703
- let p = parsed, pending2 = Array.isArray(p.pending) ? p.pending : [], approved = Array.isArray(p.approved) ? p.approved : [];
1704
- return {
1705
- pending: pending2.filter((c) => typeof c?.code == "string" && typeof c?.platform == "string" && typeof c?.userId == "string"),
1706
- approved: approved.filter((u) => typeof u?.platform == "string" && typeof u?.userId == "string"),
1707
- failedAttempts: typeof p.failedAttempts == "object" && p.failedAttempts !== null && !Array.isArray(p.failedAttempts) ? p.failedAttempts : void 0,
1708
- lockedOutUntil: typeof p.lockedOutUntil == "object" && p.lockedOutUntil !== null && !Array.isArray(p.lockedOutUntil) ? p.lockedOutUntil : void 0
1709
- };
1710
- } catch {
1711
- return { pending: [], approved: [] };
1712
- }
1713
- }
1714
- function write(data) {
1715
- existsSync(dir) || mkdirSync(dir, { recursive: !0 });
1716
- let path = storeFile(), tmp = `${path}.tmp-${process.pid}`;
1717
- writeFileSync(tmp, `${JSON.stringify(data, null, 2)}
1718
- `, "utf-8"), renameSync(tmp, path);
1719
- try {
1720
- chmodSync(path, 384);
1721
- } catch {
1722
- }
1723
- }
1724
- function isLockedOut(platform) {
1725
- let until = read().lockedOutUntil?.[platform] ?? 0;
1726
- return now() < until;
1727
- }
1728
- function recordFailedAttempt(platform) {
1729
- let data = read(), failed = data.failedAttempts ?? {};
1730
- if (failed[platform] = (failed[platform] ?? 0) + 1, failed[platform] >= maxFails) {
1731
- let locked = data.lockedOutUntil ?? {};
1732
- locked[platform] = now() + lockoutMs, failed[platform] = 0, data.lockedOutUntil = locked;
1733
- }
1734
- data.failedAttempts = failed, write(data);
1735
- }
1736
- function livePending(data) {
1737
- let cutoff = now() - ttlMs;
1738
- return data.pending.filter((c) => c.issuedAt >= cutoff);
1739
- }
1740
- return {
1741
- issueCode(platform, userId) {
1742
- if (isLockedOut(platform))
1743
- return null;
1744
- let data = read(), lastIssued = data.pending.filter((c) => c.platform === platform && c.userId === userId).reduce((max, c) => Math.max(max, c.issuedAt), 0);
1745
- if (lastIssued > 0 && now() - lastIssued < rateLimitMs)
1746
- return null;
1747
- let live = livePending(data);
1748
- if (live.filter((c) => c.platform === platform).length >= maxPending)
1749
- return null;
1750
- let entry = {
1751
- code: generateCode(),
1752
- platform,
1753
- userId,
1754
- issuedAt: now()
1755
- };
1756
- return data.pending = [...live, entry], write(data), entry.code;
1757
- },
1758
- approve(platform, code) {
1759
- if (isLockedOut(platform))
1760
- return null;
1761
- let data = read(), live = livePending(data), normalized = normalizeCode(code), match = live.find((c) => c.platform === platform && c.code === normalized);
1762
- return match ? (data.pending = live.filter((c) => c !== match), data.approved = [
1763
- ...data.approved.filter((u) => !(u.platform === platform && u.userId === match.userId)),
1764
- { platform, userId: match.userId, approvedAt: now() }
1765
- ], write(data), match.userId) : (recordFailedAttempt(platform), null);
1766
- },
1767
- isPaired(platform, userId) {
1768
- return read().approved.some((u) => u.platform === platform && u.userId === userId);
1769
- },
1770
- listPending() {
1771
- let data = read();
1772
- return livePending(data).map((c) => ({
1773
- code: c.code,
1774
- platform: c.platform,
1775
- userId: c.userId,
1776
- expiresAt: c.issuedAt + ttlMs
1777
- }));
1778
- },
1779
- listApproved() {
1780
- return read().approved.map((u) => ({ platform: u.platform, userId: u.userId, approvedAt: u.approvedAt }));
1781
- }
1782
- };
1783
- }
1784
-
1785
1017
  // packages/coding-agent/dist/gateway/router.js
1786
- import { existsSync as existsSync2 } from "node:fs";
1018
+ import { existsSync } from "node:fs";
1787
1019
 
1788
1020
  // packages/coding-agent/dist/gateway/session-keys.js
1789
1021
  var SESSION_KEY_PREFIX = "agent:main";
@@ -1864,7 +1096,7 @@ var StreamConsumer = class {
1864
1096
  };
1865
1097
 
1866
1098
  // packages/coding-agent/dist/gateway/router.js
1867
- function oneLine2(text) {
1099
+ function oneLine(text) {
1868
1100
  return text.replace(/\s+/g, " ").trim().slice(0, 200);
1869
1101
  }
1870
1102
  function formatPairingCode(code) {
@@ -1880,14 +1112,14 @@ function createRouter(deps) {
1880
1112
  if (!reloadConfig)
1881
1113
  return initialCfg;
1882
1114
  try {
1883
- if (existsSync2(gatewayConfigPath()))
1115
+ if (existsSync(gatewayConfigPath()))
1884
1116
  return loadGatewayConfig();
1885
1117
  } catch {
1886
1118
  }
1887
1119
  return initialCfg;
1888
1120
  }
1889
1121
  async function sendError(adapter, event, message) {
1890
- await adapter.send(event.source.chatId, `\u26A0 ${oneLine2(message)}`, {
1122
+ await adapter.send(event.source.chatId, `\u26A0 ${oneLine(message)}`, {
1891
1123
  replyTo: event.messageId,
1892
1124
  threadId: event.source.threadId
1893
1125
  });
@@ -1915,15 +1147,30 @@ function createRouter(deps) {
1915
1147
  let text = event.text.trim();
1916
1148
  if (!text.startsWith("/"))
1917
1149
  return !1;
1918
- let firstToken = text.split(/\s+/, 1)[0].toLowerCase(), commandWord = firstToken.split("@")[0].slice(1), args = text.slice(firstToken.length).trim(), cmd = CHAT_COMMANDS.find((c) => c.name === commandWord || c.aliases?.includes(commandWord));
1919
- return cmd ? runChatCommand(cmd, {
1150
+ let firstToken = text.split(/\s+/, 1)[0].toLowerCase(), commandWord = firstToken.split("@")[0].slice(1), args = text.slice(firstToken.length).trim();
1151
+ if (deps.remoteControls) {
1152
+ let { handleMobileCommand, cancelMobileTransfer } = await import("./mobile-commands-LVHDAUDP.js");
1153
+ if (["stop", "stopall", "new", "cancel"].includes(commandWord) && cancelMobileTransfer(key), commandWord === "cancel")
1154
+ return invalidateGatewayDialogs(key), await adapter.send(event.source.chatId, "Cancelled the pending selection or transfer.", {
1155
+ threadId: event.source.threadId
1156
+ }), !0;
1157
+ if (await runWithApprovalContext({ key, adapter, source: event.source }, () => handleMobileCommand({ key, event, adapter, bridge, cfg: freshCfg() }, commandWord, args)))
1158
+ return !0;
1159
+ if (!event.text.startsWith("/"))
1160
+ return !1;
1161
+ }
1162
+ let cmd = CHAT_COMMANDS.find((c) => c.name === commandWord || c.aliases?.includes(commandWord));
1163
+ if (!cmd)
1164
+ return event.source.chatType !== "dm";
1165
+ let ctx = {
1920
1166
  event,
1921
1167
  key,
1922
1168
  adapter,
1923
1169
  bridge,
1924
1170
  args,
1925
1171
  reply: (message) => sendCommandReply(adapter, event, message)
1926
- }) : event.source.chatType !== "dm";
1172
+ };
1173
+ return runWithApprovalContext({ key, adapter, source: event.source }, () => runChatCommand(cmd, ctx));
1927
1174
  }
1928
1175
  async function deliver(adapter, event, text, opts) {
1929
1176
  let filtered = applySilenceFilter(text);
@@ -1990,6 +1237,13 @@ function createRouter(deps) {
1990
1237
  if (isGroupGated(event, cfg) || await isDenied(adapter, event, cfg))
1991
1238
  return;
1992
1239
  let key = buildSessionKey(event.source, { groupSessionsPerUser: cfg.groupSessionsPerUser });
1240
+ if (deps.remoteControls) {
1241
+ let binding = conversationBinding(key);
1242
+ if (binding?.owner && (requireGatewayOwner(event.source, cfg), binding.owner !== event.source.userId))
1243
+ throw new Error("This session belongs to a different owner.");
1244
+ if (bindConversation(key, event.source), acceptGatewayInput(key, event))
1245
+ return;
1246
+ }
1993
1247
  if (await handleSlash(adapter, event, key))
1994
1248
  return;
1995
1249
  await adapter.sendTyping(event.source.chatId, event.source.threadId).catch(() => {
@@ -2040,11 +1294,11 @@ async function runPairApprove(platform, code) {
2040
1294
  return console.log(`Approved ${platform} user ${userId} (added to ${platform}.allowedUsers in gateway.json).`), 0;
2041
1295
  }
2042
1296
  function runPairList() {
2043
- let pairing = createPairingStore(), pending2 = pairing.listPending(), approved = pairing.listApproved();
2044
- if (console.log("Pending pairing codes:"), pending2.length === 0)
1297
+ let pairing = createPairingStore(), pending = pairing.listPending(), approved = pairing.listApproved();
1298
+ if (console.log("Pending pairing codes:"), pending.length === 0)
2045
1299
  console.log(" (none)");
2046
1300
  else
2047
- for (let p of pending2)
1301
+ for (let p of pending)
2048
1302
  console.log(` ${p.platform} ${p.code} user=${p.userId} expires ${new Date(p.expiresAt).toISOString()}`);
2049
1303
  if (console.log("Paired users:"), approved.length === 0)
2050
1304
  console.log(" (none)");
@@ -2088,40 +1342,67 @@ async function runDaemon() {
2088
1342
  }
2089
1343
  if (adapters.size === 0)
2090
1344
  return printSetupInstructions(skipped), 1;
2091
- let pairing = createPairingStore(), bridge = new AgentBridge(), router = createRouter({ adapters, cfg, pairing, bridge, reloadConfig: !0 }), connected = [];
2092
- startButtonSweeper();
2093
- for (let [platform, adapter] of adapters)
2094
- try {
2095
- if (!await adapter.connect()) {
2096
- console.error(`[gateway] ${platform}: connect() reported failure; skipping`);
2097
- continue;
2098
- }
2099
- adapter.onMessage((event) => {
2100
- router.handleEvent(event);
2101
- }), adapter.onCallback(async (event) => {
2102
- await handleApprovalCallback(event, adapter) || handleCallback(event, { adapters, cfg, pairing, bridge, adapter });
1345
+ let pairing = createPairingStore(), bridge = new AgentBridge();
1346
+ for (let adapter of adapters.values())
1347
+ adapter.setCommandSuggestions?.(async (source, command) => {
1348
+ let session = bridge.peekSession(buildSessionKey(source, loadGatewayConfig()));
1349
+ return session ? command === "thinking" ? session.getAvailableThinkingLevels() : command === "model" ? (await session.modelRuntime.getAvailable()).map((model) => `${model.provider}/${model.id}`) : [] : [];
1350
+ });
1351
+ let router = createRouter({ adapters, cfg, pairing, bridge, reloadConfig: !0, remoteControls: !0 }), finish, stopped = new Promise((resolve) => {
1352
+ finish = resolve;
1353
+ }), stopping = !1, cron, platformStatus = {}, retries = /* @__PURE__ */ new Set(), shutdown = () => {
1354
+ if (!stopping) {
1355
+ stopping = !0, owner.update(platformStatus, "stopping"), cron?.stop();
1356
+ for (let timer of retries)
1357
+ clearTimeout(timer);
1358
+ stopButtonSweeper(), (async () => {
1359
+ await bridge.shutdown(), await Promise.all([...adapters.values()].map((adapter) => adapter.disconnect().catch(() => {
1360
+ }))), stopGatewayPresenter(), owner.release(), process.removeListener("SIGINT", shutdown), process.removeListener("SIGTERM", shutdown), finish();
1361
+ })().catch((error) => {
1362
+ console.error("Gateway shutdown failed:", error instanceof Error ? error.message : String(error)), stopping = !1, owner.update(platformStatus, "ready"), startButtonSweeper(), cron = startGatewayCron({ adapters, cfg });
1363
+ for (let [platform, adapter] of adapters)
1364
+ platformStatus[platform] !== "connected" && connect(platform, adapter);
2103
1365
  });
1366
+ }
1367
+ }, owner = claimGateway(shutdown);
1368
+ process.on("SIGINT", shutdown), process.on("SIGTERM", shutdown), startGatewayPresenter(adapters);
1369
+ let connected = [];
1370
+ startButtonSweeper();
1371
+ let connect = async (platform, adapter) => {
1372
+ if (!stopping) {
2104
1373
  try {
2105
- await adapter.registerCommands?.(botCommandSpecs());
2106
- } catch (err) {
2107
- console.error(`[gateway] ${platform}: command menu registration failed: ${err instanceof Error ? err.message : String(err)}`);
1374
+ if (platformStatus[platform] = "connecting", owner.update(platformStatus, "starting"), !await adapter.connect())
1375
+ throw new Error("Connection failed");
1376
+ adapter.onMessage((event) => {
1377
+ router.handleEvent(event);
1378
+ }), adapter.onCallback(async (event) => {
1379
+ await handleApprovalCallback(event, adapter) || handleCallback(event, { adapters, cfg: loadGatewayConfig(), pairing, bridge, adapter });
1380
+ });
1381
+ try {
1382
+ await adapter.registerCommands?.(botCommandSpecs());
1383
+ } catch (err) {
1384
+ console.error(`[gateway] ${platform}: command menu registration failed: ${err instanceof Error ? err.message : String(err)}`);
1385
+ }
1386
+ if (stopping) {
1387
+ await adapter.disconnect();
1388
+ return;
1389
+ }
1390
+ connected.push(adapter), platformStatus[platform] = "connected", console.log(`lunR gateway: ${platform} connected`);
1391
+ } catch {
1392
+ if (platformStatus[platform] = "disconnected, retrying", await adapter.disconnect().catch(() => {
1393
+ }), !stopping) {
1394
+ let retry = setTimeout(() => {
1395
+ retries.delete(retry), connect(platform, adapter);
1396
+ }, 5e3);
1397
+ retries.add(retry);
1398
+ }
2108
1399
  }
2109
- connected.push(adapter), console.log(`lunR gateway: ${platform} connected`);
2110
- } catch (err) {
2111
- console.error(`[gateway] ${platform}: connect failed: ${err instanceof Error ? err.message : String(err)}`);
1400
+ stopping || owner.update(platformStatus);
2112
1401
  }
2113
- if (connected.length === 0)
2114
- return console.error("lunR gateway: every configured platform failed to connect."), stopButtonSweeper(), 1;
2115
- let cron = startGatewayCron({ adapters, cfg });
2116
- console.log(`lunR gateway: cron scheduler started (${cron.intervalMs / 1e3}s interval)`);
2117
- let shutdown = () => {
2118
- cron.stop(), stopButtonSweeper(), Promise.all(connected.map((adapter) => adapter.disconnect().catch(() => {
2119
- }))).finally(() => {
2120
- process.exit(0);
2121
- });
2122
1402
  };
2123
- return process.on("SIGINT", shutdown), process.on("SIGTERM", shutdown), await new Promise(() => {
2124
- }), 0;
1403
+ for (let [platform, adapter] of adapters)
1404
+ connect(platform, adapter);
1405
+ return cron = startGatewayCron({ adapters, cfg }), console.log(`lunR gateway: cron scheduler started (${cron.intervalMs / 1e3}s interval)`), owner.update(platformStatus), await stopped, 0;
2125
1406
  }
2126
1407
  async function runGateway(args) {
2127
1408
  let [sub, ...rest] = args;