@ashx-j/lunr 0.2.23 → 0.2.25

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 (272) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/README.md +10 -10
  3. package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
  4. package/dist/builtin-extensions/ashxj-tui.js +2 -2
  5. package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
  6. package/dist/builtin-extensions/lunr-plan-tools.d.ts +7 -7
  7. package/dist/builtin-extensions/lunr-plan-tools.d.ts.map +1 -1
  8. package/dist/builtin-extensions/lunr-plan-tools.js +12 -12
  9. package/dist/builtin-extensions/lunr-plan-tools.js.map +1 -1
  10. package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
  11. package/dist/builtin-extensions/pi-intercom/index.js +2 -0
  12. package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
  13. package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.d.ts.map +1 -1
  14. package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.js +1 -0
  15. package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.js.map +1 -1
  16. package/dist/builtin-extensions/pi-mcp-adapter/index.d.ts.map +1 -1
  17. package/dist/builtin-extensions/pi-mcp-adapter/index.js +1 -1
  18. package/dist/builtin-extensions/pi-mcp-adapter/index.js.map +1 -1
  19. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts.map +1 -1
  20. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +1 -1
  21. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js.map +1 -1
  22. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts +4 -4
  23. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts.map +1 -1
  24. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js +6 -4
  25. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
  26. package/dist/builtin-extensions/pi-subagents/src/intercom/intercom-bridge.d.ts +2 -0
  27. package/dist/builtin-extensions/pi-subagents/src/intercom/intercom-bridge.d.ts.map +1 -1
  28. package/dist/builtin-extensions/pi-subagents/src/intercom/intercom-bridge.js +6 -3
  29. package/dist/builtin-extensions/pi-subagents/src/intercom/intercom-bridge.js.map +1 -1
  30. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.d.ts.map +1 -1
  31. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js +7 -2
  32. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js.map +1 -1
  33. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts +2 -0
  34. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts.map +1 -1
  35. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js +4 -0
  36. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js.map +1 -1
  37. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
  38. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +1 -1
  39. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
  40. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts +1 -0
  41. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
  42. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +5 -1
  43. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
  44. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
  45. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +3 -2
  46. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
  47. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  48. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +19 -0
  49. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
  50. package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.d.ts +1 -1
  51. package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.d.ts.map +1 -1
  52. package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.js +1 -1
  53. package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.js.map +1 -1
  54. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.d.ts +2 -0
  55. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.d.ts.map +1 -1
  56. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js +17 -15
  57. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js.map +1 -1
  58. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
  59. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.js +3 -1
  60. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
  61. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts.map +1 -1
  62. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js +2 -2
  63. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js.map +1 -1
  64. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +1 -0
  65. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
  66. package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
  67. package/dist/core/agent-session.d.ts +1 -1
  68. package/dist/core/agent-session.d.ts.map +1 -1
  69. package/dist/core/agent-session.js +5 -4
  70. package/dist/core/agent-session.js.map +1 -1
  71. package/dist/core/browser/schema.d.ts +1 -1
  72. package/dist/core/browser/schema.d.ts.map +1 -1
  73. package/dist/core/browser/schema.js +1 -1
  74. package/dist/core/browser/schema.js.map +1 -1
  75. package/dist/core/catalog-cache-scope.d.ts.map +1 -1
  76. package/dist/core/catalog-cache-scope.js +3 -1
  77. package/dist/core/catalog-cache-scope.js.map +1 -1
  78. package/dist/core/model-runtime.d.ts.map +1 -1
  79. package/dist/core/model-runtime.js +3 -2
  80. package/dist/core/model-runtime.js.map +1 -1
  81. package/dist/core/permissions.d.ts +16 -23
  82. package/dist/core/permissions.d.ts.map +1 -1
  83. package/dist/core/permissions.js +66 -141
  84. package/dist/core/permissions.js.map +1 -1
  85. package/dist/core/plan-mode.d.ts +10 -12
  86. package/dist/core/plan-mode.d.ts.map +1 -1
  87. package/dist/core/plan-mode.js +16 -18
  88. package/dist/core/plan-mode.js.map +1 -1
  89. package/dist/core/runtime-bridges.d.ts.map +1 -1
  90. package/dist/core/runtime-bridges.js +2 -0
  91. package/dist/core/runtime-bridges.js.map +1 -1
  92. package/dist/core/sdk.d.ts.map +1 -1
  93. package/dist/core/sdk.js +13 -6
  94. package/dist/core/sdk.js.map +1 -1
  95. package/dist/core/session-manager.d.ts.map +1 -1
  96. package/dist/core/session-manager.js +9 -2
  97. package/dist/core/session-manager.js.map +1 -1
  98. package/dist/core/settings-manager.d.ts +9 -1
  99. package/dist/core/settings-manager.d.ts.map +1 -1
  100. package/dist/core/settings-manager.js +40 -1
  101. package/dist/core/settings-manager.js.map +1 -1
  102. package/dist/core/slash-commands.d.ts.map +1 -1
  103. package/dist/core/slash-commands.js +4 -4
  104. package/dist/core/slash-commands.js.map +1 -1
  105. package/dist/core/subagent-permission-inherit.d.ts +6 -10
  106. package/dist/core/subagent-permission-inherit.d.ts.map +1 -1
  107. package/dist/core/subagent-permission-inherit.js +14 -14
  108. package/dist/core/subagent-permission-inherit.js.map +1 -1
  109. package/dist/core/system-prompt.d.ts +2 -0
  110. package/dist/core/system-prompt.d.ts.map +1 -1
  111. package/dist/core/system-prompt.js +9 -4
  112. package/dist/core/system-prompt.js.map +1 -1
  113. package/dist/gateway/adapters/discord.d.ts.map +1 -1
  114. package/dist/gateway/adapters/discord.js +3 -8
  115. package/dist/gateway/adapters/discord.js.map +1 -1
  116. package/dist/gateway/adapters/telegram.d.ts.map +1 -1
  117. package/dist/gateway/adapters/telegram.js +7 -0
  118. package/dist/gateway/adapters/telegram.js.map +1 -1
  119. package/dist/gateway/agent-bridge.d.ts +4 -1
  120. package/dist/gateway/agent-bridge.d.ts.map +1 -1
  121. package/dist/gateway/agent-bridge.js +57 -17
  122. package/dist/gateway/agent-bridge.js.map +1 -1
  123. package/dist/gateway/approval.d.ts +2 -6
  124. package/dist/gateway/approval.d.ts.map +1 -1
  125. package/dist/gateway/approval.js +3 -7
  126. package/dist/gateway/approval.js.map +1 -1
  127. package/dist/gateway/buttons.d.ts.map +1 -1
  128. package/dist/gateway/buttons.js +29 -19
  129. package/dist/gateway/buttons.js.map +1 -1
  130. package/dist/gateway/commands.d.ts +2 -0
  131. package/dist/gateway/commands.d.ts.map +1 -1
  132. package/dist/gateway/commands.js +39 -18
  133. package/dist/gateway/commands.js.map +1 -1
  134. package/dist/gateway/config.d.ts +1 -0
  135. package/dist/gateway/config.d.ts.map +1 -1
  136. package/dist/gateway/config.js +2 -0
  137. package/dist/gateway/config.js.map +1 -1
  138. package/dist/gateway/cron.d.ts +1 -1
  139. package/dist/gateway/cron.d.ts.map +1 -1
  140. package/dist/gateway/cron.js +2 -2
  141. package/dist/gateway/cron.js.map +1 -1
  142. package/dist/gateway/index.d.ts.map +1 -1
  143. package/dist/gateway/index.js +26 -26
  144. package/dist/gateway/index.js.map +1 -1
  145. package/dist/gateway/mobile-commands.d.ts.map +1 -1
  146. package/dist/gateway/mobile-commands.js +15 -14
  147. package/dist/gateway/mobile-commands.js.map +1 -1
  148. package/dist/gateway/presenter.d.ts +12 -1
  149. package/dist/gateway/presenter.d.ts.map +1 -1
  150. package/dist/gateway/presenter.js +146 -48
  151. package/dist/gateway/presenter.js.map +1 -1
  152. package/dist/gateway/router.d.ts +5 -5
  153. package/dist/gateway/router.d.ts.map +1 -1
  154. package/dist/gateway/router.js +96 -42
  155. package/dist/gateway/router.js.map +1 -1
  156. package/dist/gateway/service.d.ts.map +1 -1
  157. package/dist/gateway/service.js +13 -4
  158. package/dist/gateway/service.js.map +1 -1
  159. package/dist/gateway/setup.d.ts +5 -0
  160. package/dist/gateway/setup.d.ts.map +1 -1
  161. package/dist/gateway/setup.js +98 -42
  162. package/dist/gateway/setup.js.map +1 -1
  163. package/dist/gateway/stream.d.ts +6 -5
  164. package/dist/gateway/stream.d.ts.map +1 -1
  165. package/dist/gateway/stream.js +24 -11
  166. package/dist/gateway/stream.js.map +1 -1
  167. package/dist/main.d.ts.map +1 -1
  168. package/dist/main.js +1 -2
  169. package/dist/main.js.map +1 -1
  170. package/dist/modes/interactive/components/settings-selector.d.ts +4 -0
  171. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  172. package/dist/modes/interactive/components/settings-selector.js +23 -3
  173. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  174. package/dist/modes/interactive/components/thinking-tail.d.ts.map +1 -1
  175. package/dist/modes/interactive/components/thinking-tail.js +2 -2
  176. package/dist/modes/interactive/components/thinking-tail.js.map +1 -1
  177. package/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  178. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  179. package/dist/modes/interactive/components/tool-execution.js +12 -2
  180. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  181. package/dist/modes/interactive/interactive-mode.d.ts +1 -2
  182. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  183. package/dist/modes/interactive/interactive-mode.js +76 -90
  184. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  185. package/dist/node-runtime/{agent-session-services-GZVSOGYR.js → agent-session-services-YXXFCP5J.js} +12 -12
  186. package/dist/node-runtime/{builtin-extensions-QVCXNQT4.js → builtin-extensions-ZLAX7TUH.js} +3 -3
  187. package/dist/node-runtime/{chunk-W64JC4E7.js → chunk-2HHLJWGD.js} +3 -87
  188. package/dist/node-runtime/{chunk-VWCE2HSZ.js → chunk-2HMIRK7W.js} +4 -4
  189. package/dist/node-runtime/{chunk-XLTL257W.js → chunk-2KIPT4HJ.js} +5 -1
  190. package/dist/node-runtime/{chunk-RIR5EDDJ.js → chunk-3XPLBUEM.js} +17 -12
  191. package/dist/node-runtime/{chunk-QAZOULTU.js → chunk-6XL67VVY.js} +40 -6
  192. package/dist/node-runtime/chunk-7S3T6YEA.js +97 -0
  193. package/dist/node-runtime/{chunk-R3K2F5GI.js → chunk-B4HP6ZWM.js} +2 -0
  194. package/dist/node-runtime/{chunk-TQGPP4SC.js → chunk-CDVHZOOK.js} +1 -1
  195. package/dist/node-runtime/{chunk-YJMMG7C6.js → chunk-DSIKTUPW.js} +1 -1
  196. package/dist/node-runtime/{chunk-LFZDVKSP.js → chunk-E4D3IGP2.js} +41 -25
  197. package/dist/node-runtime/{chunk-OFKSVRV7.js → chunk-GYIXXQAO.js} +1 -1
  198. package/dist/node-runtime/{chunk-BX2RB477.js → chunk-HFVZUPFJ.js} +1 -1
  199. package/dist/node-runtime/{chunk-AZJHNQKQ.js → chunk-I54LGHWM.js} +1 -1
  200. package/dist/node-runtime/{chunk-YHQ3NBB3.js → chunk-IJIFHRTB.js} +20 -18
  201. package/dist/node-runtime/{chunk-GS66LV4M.js → chunk-KJ6DWS3K.js} +4 -4
  202. package/dist/node-runtime/{chunk-HPUP7E4T.js → chunk-KWIYUUZK.js} +68 -42
  203. package/dist/node-runtime/{chunk-TVJUOJZJ.js → chunk-LQP3RB67.js} +184 -1527
  204. package/dist/node-runtime/{chunk-VSELYKWU.js → chunk-LWYIVUGV.js} +2 -1
  205. package/dist/node-runtime/{chunk-JHYKXIMM.js → chunk-MPKSOJBT.js} +1 -1
  206. package/dist/node-runtime/{chunk-GMHJCCEN.js → chunk-NRKGMJYU.js} +4 -1
  207. package/dist/node-runtime/{chunk-CSUZVNYE.js → chunk-OVX6X377.js} +10 -6
  208. package/dist/node-runtime/{chunk-DD5YWFSJ.js → chunk-QTU6AKNJ.js} +130 -65
  209. package/dist/node-runtime/{chunk-LLUIPGDR.js → chunk-RC7DMV45.js} +110 -77
  210. package/dist/node-runtime/{chunk-UWPJL22I.js → chunk-SLVOAQOY.js} +15 -4
  211. package/dist/node-runtime/{chunk-EKVFP6YF.js → chunk-TORR5XBV.js} +17 -16
  212. package/dist/node-runtime/{chunk-UP2XBVH5.js → chunk-UOEDI7RP.js} +1 -1
  213. package/dist/node-runtime/{chunk-DQMGU675.js → chunk-VQFSUJJG.js} +20 -17
  214. package/dist/node-runtime/{chunk-X3URCYCU.js → chunk-WF7PGWRZ.js} +14 -14
  215. package/dist/node-runtime/chunk-YCUZEGJG.js +1361 -0
  216. package/dist/node-runtime/{chunk-5D7LC2ZQ.js → chunk-ZXSGTGJ6.js} +54 -85
  217. package/dist/node-runtime/cli-runtime.js +1 -1
  218. package/dist/node-runtime/{command-PDVTBRWA.js → command-6WDOYKS6.js} +5 -5
  219. package/dist/node-runtime/{commands-KAMO2T6P.js → commands-NZLSOHPE.js} +2 -2
  220. package/dist/node-runtime/{commands-XDCFGIGW.js → commands-QC6VUZJQ.js} +9 -9
  221. package/dist/node-runtime/{config-RORQUQFG.js → config-D4QUPAHX.js} +1 -1
  222. package/dist/node-runtime/{direct-tool-executor-YAVOLDFY.js → direct-tool-executor-77GW4F5P.js} +3 -3
  223. package/dist/node-runtime/{export-html-KSOIMVPI.js → export-html-VM4J2MNR.js} +1 -1
  224. package/dist/node-runtime/{gateway-AYWM5DIO.js → gateway-INAYBF4Q.js} +141 -85
  225. package/dist/node-runtime/index.js +23 -21
  226. package/dist/node-runtime/{init-X2LIUB2H.js → init-HS6NQXSK.js} +2 -2
  227. package/dist/node-runtime/{install-cli-3BO4MMQ6.js → install-cli-KL4UDDAD.js} +3 -3
  228. package/dist/node-runtime/{interactive-mode-4MFRKAHG.js → interactive-mode-Z5M7WYYZ.js} +14 -14
  229. package/dist/node-runtime/{interactive-view-TUZHCQM3.js → interactive-view-NGFAMHPW.js} +2 -2
  230. package/dist/node-runtime/{lunr-browser-RYFIIVJN.js → lunr-browser-IR7TL3J7.js} +4 -4
  231. package/dist/node-runtime/{lunr-cron-YWXEPZMQ.js → lunr-cron-LWIZJM2N.js} +8 -8
  232. package/dist/node-runtime/main.js +19 -18
  233. package/dist/node-runtime/{mobile-commands-LVHDAUDP.js → mobile-commands-J7OJEKFO.js} +6 -6
  234. package/dist/node-runtime/{model-runtime-PLQKAG4B.js → model-runtime-OO5HFLWE.js} +1 -1
  235. package/dist/node-runtime/{package-manager-cli-6GT3Z3CK.js → package-manager-cli-VYZM5WHN.js} +10 -8
  236. package/dist/node-runtime/{pi-intercom-HBPK5HSJ.js → pi-intercom-QM53RLTS.js} +2 -0
  237. package/dist/node-runtime/{pi-mcp-adapter-E6WULBKF.js → pi-mcp-adapter-5UKBZP44.js} +7 -6
  238. package/dist/node-runtime/{pi-subagents-CDWK2AKY.js → pi-subagents-DNY7EAIG.js} +23 -13
  239. package/dist/node-runtime/{proxy-modes-OSQCWRBI.js → proxy-modes-LGEUYD7R.js} +3 -3
  240. package/dist/node-runtime/rpc-entry.js +19 -18
  241. package/dist/node-runtime/{runtime-bridges-3XUQ46XW.js → runtime-bridges-VRDDR73P.js} +8 -1
  242. package/dist/node-runtime/{service-ISSP6VHE.js → service-MDQIPQ26.js} +1 -1
  243. package/dist/node-runtime/{session-handoff-HC7R5E24.js → session-handoff-GWARVEWT.js} +2 -2
  244. package/dist/node-runtime/{session-manager-CFN4UFM2.js → session-manager-JB5J6XBN.js} +1 -1
  245. package/dist/node-runtime/{settings-manager-6VEMXRDL.js → settings-manager-J4SPQPDJ.js} +10 -7
  246. package/dist/node-runtime/setup-CQ2NLVK5.js +214 -0
  247. package/dist/node-runtime/{subagent-executor-M5VWB3D3.js → subagent-executor-KZZQGAKV.js} +40 -7
  248. package/dist/node-runtime/{update-cli-UVKJWRRJ.js → update-cli-GTFJC7XK.js} +4 -5
  249. package/dist/startup/interactive-view.d.ts.map +1 -1
  250. package/dist/startup/interactive-view.js +5 -1
  251. package/dist/startup/interactive-view.js.map +1 -1
  252. package/docs/browser.md +1 -1
  253. package/docs/features.md +19 -18
  254. package/docs/index.md +1 -1
  255. package/docs/keybindings.md +1 -1
  256. package/docs/providers.md +6 -2
  257. package/docs/quickstart.md +2 -2
  258. package/docs/settings.md +1 -1
  259. package/docs/usage.md +4 -4
  260. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  261. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  262. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  263. package/examples/extensions/gondolin/package-lock.json +2 -2
  264. package/examples/extensions/gondolin/package.json +1 -1
  265. package/examples/extensions/plan-mode/README.md +1 -1
  266. package/examples/extensions/sandbox/package-lock.json +2 -2
  267. package/examples/extensions/sandbox/package.json +1 -1
  268. package/examples/extensions/with-deps/package-lock.json +2 -2
  269. package/examples/extensions/with-deps/package.json +1 -1
  270. package/package.json +4 -4
  271. package/dist/node-runtime/chunk-LISUTZS3.js +0 -19
  272. package/dist/node-runtime/setup-5SR2J35A.js +0 -155
@@ -9,9 +9,11 @@ import { computeContextBreakdown } from "../core/context-breakdown.js";
9
9
  import { findExactModelReferenceMatch } from "../core/model-resolver.js";
10
10
  import { runtimeScope } from "../core/runtime-scope.js";
11
11
  import { SessionManager } from "../core/session-manager.js";
12
+ import { isGatewayOwner } from "./authz.js";
12
13
  import { createPicker } from "./buttons.js";
14
+ import { defaultGatewayConfig } from "./config.js";
13
15
  import { conversationBinding } from "./conversations.js";
14
- import { FORWARDED_COMMANDS, MOBILE_COMMANDS } from "./mobile-commands.js";
16
+ import { FORWARDED_COMMANDS, MOBILE_COMMANDS, resolveWithinRoots } from "./mobile-commands.js";
15
17
  const MODEL_CACHE_TTL_MS = 5 * 60 * 1000;
16
18
  const SESSIONS_CACHE_TTL_MS = 5 * 60 * 1000;
17
19
  const MAX_MODEL_LIST_LINES = 15;
@@ -165,7 +167,21 @@ const newCommand = {
165
167
  needsSession: false,
166
168
  async handler(ctx) {
167
169
  await ctx.bridge.reset(ctx.key);
168
- await ctx.reply("Session reset — next message starts a fresh session.");
170
+ const cfg = ctx.cfg ?? defaultGatewayConfig();
171
+ const owner = isGatewayOwner(ctx.event.source, cfg);
172
+ let ready = Boolean(conversationBinding(ctx.key)?.cwd);
173
+ if (!ready && owner && cfg.defaultProject) {
174
+ try {
175
+ resolveWithinRoots(cfg.defaultProject, cfg.projectRoots ?? []);
176
+ ready = true;
177
+ }
178
+ catch { }
179
+ }
180
+ await ctx.reply(ready
181
+ ? "Session reset. Send a message to start fresh in the approved project."
182
+ : owner
183
+ ? "Session reset. Use /project to choose an approved project before sending a message."
184
+ : "Session reset. Ask the computer's gateway owner to configure this chat's project access locally.");
169
185
  },
170
186
  };
171
187
  const undoCommand = {
@@ -304,7 +320,7 @@ const modelCommand = {
304
320
  }
305
321
  return {
306
322
  done: true,
307
- text: `☾ Model → ${exact.provider}/${exact.id} (thinking level re-clamped to ${session.thinkingLevel})`,
323
+ text: `Model → ${exact.provider}/${exact.id} (thinking level re-clamped to ${session.thinkingLevel})`,
308
324
  };
309
325
  },
310
326
  }, pickerOpts);
@@ -355,13 +371,16 @@ const sessionsCommand = {
355
371
  const session = ctx.session;
356
372
  const arg = ctx.args.trim();
357
373
  const currentFile = session.sessionManager?.getSessionFile();
374
+ const cwd = session.sessionManager?.getCwd() ?? conversationBinding(ctx.key)?.cwd;
375
+ if (!cwd)
376
+ throw new Error("Select a project before listing sessions.");
358
377
  if (!arg) {
359
- const all = await SessionManager.list(process.cwd());
378
+ const all = await SessionManager.list(cwd);
360
379
  all.sort((a, b) => b.modified.getTime() - a.modified.getTime());
361
380
  const sessions = all.slice(0, 10);
362
381
  setCachedSessions(ctx.key, sessions);
363
382
  const items = sessions.map((s) => ({
364
- label: `${s.path === currentFile ? "☾ " : ""}${s.name ? truncate(s.name, 30) : truncate(s.firstMessage, 30) || "(empty)"}`,
383
+ label: `${s.path === currentFile ? "(current) " : ""}${s.name ? truncate(s.name, 30) : truncate(s.firstMessage, 30) || "(empty)"}`,
365
384
  value: s.path,
366
385
  }));
367
386
  const result = await createPicker(ctx.adapter, ctx.event.source, {
@@ -376,16 +395,16 @@ const sessionsCommand = {
376
395
  const switched = await ctx.bridge.getSession(ctx.key);
377
396
  const name = switched?.sessionManager?.getSessionName() ?? "unnamed";
378
397
  const count = switched?.sessionManager?.getEntries().length ?? 0;
379
- return { done: true, text: `☾ Switched to "${name}" (${count} msgs). History continues here.` };
398
+ return { done: true, text: `Switched to "${name}" (${count} msgs). History continues here.` };
380
399
  },
381
400
  }, { replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId });
382
401
  if (!result.success) {
383
402
  const lines = sessions.map((s, i) => {
384
- const marker = s.path === currentFile ? " ☾" : "";
403
+ const marker = s.path === currentFile ? " (current)" : "";
385
404
  const label = s.name ? truncate(s.name, 40) : truncate(s.firstMessage, 40) || "(empty)";
386
405
  return `${i + 1}) ${label}${marker} · ${formatRelativeTime(s.modified)} · ${s.messageCount} msgs`;
387
406
  });
388
- await ctx.reply([`Sessions for ${process.cwd()}`, ...lines, "Use /sessions <n> or /sessions <id-prefix>"].join("\n"));
407
+ await ctx.reply([`Sessions for ${cwd}`, ...lines, "Use /sessions <n> or /sessions <id-prefix>"].join("\n"));
389
408
  }
390
409
  return;
391
410
  }
@@ -400,7 +419,7 @@ const sessionsCommand = {
400
419
  path = cached[numeric - 1].path;
401
420
  }
402
421
  else {
403
- const pool = cached ?? (await SessionManager.list(process.cwd()));
422
+ const pool = cached ?? (await SessionManager.list(cwd));
404
423
  const matches = pool.filter((s) => s.id.startsWith(arg) || s.path.startsWith(arg));
405
424
  if (matches.length === 0) {
406
425
  await ctx.reply(`No session matches "${arg}".`);
@@ -544,7 +563,7 @@ const thinkingCommand = {
544
563
  async resolve(item) {
545
564
  const level = item.value;
546
565
  session.setThinkingLevel(level);
547
- return { done: true, text: `☾ Thinking → ${level}` };
566
+ return { done: true, text: `Thinking → ${level}` };
548
567
  },
549
568
  }, { replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId });
550
569
  if (!result.success) {
@@ -578,14 +597,16 @@ export const CHAT_COMMANDS = [
578
597
  /** Platform menu specs (e.g. Telegram setMyCommands): canonical names only, aliases skipped. */
579
598
  export function botCommandSpecs() {
580
599
  return [
581
- ...new Map([...CHAT_COMMANDS, ...MOBILE_COMMANDS, ...FORWARDED_COMMANDS].map((c) => [
582
- c.name,
583
- { name: c.name, description: c.description },
584
- ])).values(),
600
+ ...new Map([
601
+ { name: "start", description: "Check gateway readiness" },
602
+ ...CHAT_COMMANDS,
603
+ ...MOBILE_COMMANDS,
604
+ ...FORWARDED_COMMANDS,
605
+ ].map((c) => [c.name, { name: c.name, description: c.description }])).values(),
585
606
  ];
586
607
  }
587
608
  export function formatHelpText() {
588
- const lines = ["lunR gateway commands:"];
609
+ const lines = ["lunR gateway commands:", "/start — check gateway readiness"];
589
610
  for (const cmd of CHAT_COMMANDS) {
590
611
  const names = [cmd.name, ...(cmd.aliases ?? [])].map((a) => `/${a}`).join(" | ");
591
612
  lines.push(`${names} — ${cmd.description}`);
@@ -609,9 +630,9 @@ export async function runChatCommand(cmd, ctx) {
609
630
  return true;
610
631
  }
611
632
  if (cmd.needsSession) {
612
- const session = await ctx.bridge.getSession(ctx.key);
633
+ const session = await ctx.bridge.getSession(ctx.key, cmd.name === "model");
613
634
  if (!session) {
614
- await ctx.reply("No session yet — send a message first.");
635
+ await ctx.reply("No session yet. Select an approved project with /project first.");
615
636
  return true;
616
637
  }
617
638
  ctx.session = session;
@@ -634,7 +655,7 @@ export async function runChatCommand(cmd, ctx) {
634
655
  }
635
656
  /** Reply helper used by the router to build the ChatCommandContext. */
636
657
  export async function sendCommandReply(adapter, event, text) {
637
- await adapter.send(event.source.chatId, prefixLines(text, "☾ "), {
658
+ await adapter.send(event.source.chatId, prefixLines(text, ""), {
638
659
  replyTo: event.messageId,
639
660
  threadId: event.source.threadId,
640
661
  });
@@ -1 +1 @@
1
- {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/gateway/commands.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway chat command registry.\n *\n * Every command is a small headless handler over AgentSession. Router.ts keeps\n * platform gating/authorization and delegates to runChatCommand for busy/session\n * guards, error wrapping, and /help generation.\n */\n\nimport type { ThinkingLevel } from \"@ashx-j/lunr-agent\";\nimport type { Model } from \"@ashx-j/lunr-ai/compat\";\nimport { computeContextBreakdown } from \"../core/context-breakdown.ts\";\nimport type { ToolDefinition } from \"../core/extensions/types.ts\";\nimport { findExactModelReferenceMatch } from \"../core/model-resolver.ts\";\nimport type { ModelRuntime } from \"../core/model-runtime.ts\";\nimport { runtimeScope } from \"../core/runtime-scope.ts\";\nimport type { SessionInfo } from \"../core/session-manager.ts\";\nimport { SessionManager } from \"../core/session-manager.ts\";\nimport type { BridgeSession } from \"./agent-bridge.ts\";\nimport { createPicker, type PickerItem } from \"./buttons.ts\";\nimport { conversationBinding } from \"./conversations.ts\";\nimport { FORWARDED_COMMANDS, MOBILE_COMMANDS } from \"./mobile-commands.ts\";\nimport type { BridgeLike } from \"./router.ts\";\nimport type { MessageEvent, PlatformAdapter } from \"./types.ts\";\n\nconst MODEL_CACHE_TTL_MS = 5 * 60 * 1000;\nconst SESSIONS_CACHE_TTL_MS = 5 * 60 * 1000;\nconst MAX_MODEL_LIST_LINES = 15;\nconst PROVIDER_PER_PAGE = 10;\nconst _MODEL_PER_PAGE = 8;\nconst MAX_MODELS_PER_PROVIDER = 50;\nconst MODEL_ID_MAX = 38;\nconst MODEL_CACHE_CAP = 100;\nconst SESSIONS_CACHE_CAP = 100;\n\ninterface ModelCacheEntry {\n\tmodels: Model<any>[];\n\texpires: number;\n}\n\ninterface SessionsCacheEntry {\n\tsessions: SessionInfo[];\n\texpires: number;\n}\n\nconst modelCache = new Map<string, ModelCacheEntry>();\nconst sessionsCache = new Map<string, SessionsCacheEntry>();\n\nfunction evictLRU<K, V>(map: Map<K, V>, cap: number): void {\n\twhile (map.size > cap) {\n\t\tconst first = map.keys().next().value;\n\t\tif (first !== undefined) map.delete(first);\n\t}\n}\n\nfunction getCachedModels(key: string): Model<any>[] | undefined {\n\tconst cached = modelCache.get(key);\n\tif (!cached) return undefined;\n\tif (Date.now() > cached.expires) {\n\t\tmodelCache.delete(key);\n\t\treturn undefined;\n\t}\n\t// LRU refresh.\n\tmodelCache.delete(key);\n\tmodelCache.set(key, cached);\n\treturn cached.models;\n}\n\nfunction setCachedModels(key: string, models: Model<any>[]): void {\n\tmodelCache.set(key, { models, expires: Date.now() + MODEL_CACHE_TTL_MS });\n\tevictLRU(modelCache, MODEL_CACHE_CAP);\n}\n\nfunction getCachedSessions(key: string): SessionInfo[] | undefined {\n\tconst cached = sessionsCache.get(key);\n\tif (!cached) return undefined;\n\tif (Date.now() > cached.expires) {\n\t\tsessionsCache.delete(key);\n\t\treturn undefined;\n\t}\n\tsessionsCache.delete(key);\n\tsessionsCache.set(key, cached);\n\treturn cached.sessions;\n}\n\nfunction setCachedSessions(key: string, sessions: SessionInfo[]): void {\n\tsessionsCache.set(key, { sessions, expires: Date.now() + SESSIONS_CACHE_TTL_MS });\n\tevictLRU(sessionsCache, SESSIONS_CACHE_CAP);\n}\n\nexport interface ChatCommandContext {\n\tevent: MessageEvent;\n\tkey: string;\n\tadapter: PlatformAdapter;\n\tbridge: BridgeLike;\n\tsession?: BridgeSession;\n\treply(text: string): Promise<void>;\n\targs: string;\n}\n\nexport interface ChatCommand {\n\tname: string;\n\taliases?: string[];\n\tdescription: string;\n\tbypassBusy: boolean;\n\tneedsSession: boolean;\n\t// biome-ignore lint/suspicious/noConfusingVoidType: handlers may return nothing; runChatCommand treats undefined as consumed.\n\thandler(ctx: ChatCommandContext): Promise<boolean | void>;\n}\n\nfunction oneLine(text: string): string {\n\treturn text.replace(/\\s+/g, \" \").trim().slice(0, 200);\n}\n\nfunction prefixLines(text: string, prefix: string): string {\n\treturn text\n\t\t.split(\"\\n\")\n\t\t.map((line, index) => (index === 0 ? prefix : \" \") + line)\n\t\t.join(\"\\n\");\n}\n\nfunction truncate(text: string, max: number): string {\n\tif (text.length <= max) return text;\n\treturn `${text.slice(0, max - 1)}…`;\n}\n\nfunction formatRelativeTime(date: Date): string {\n\tconst seconds = Math.max(0, Math.floor((Date.now() - date.getTime()) / 1000));\n\tif (seconds < 60) return `${seconds}s ago`;\n\tconst minutes = Math.floor(seconds / 60);\n\tif (minutes < 60) return `${minutes}m ago`;\n\tconst hours = Math.floor(minutes / 60);\n\tif (hours < 24) return `${hours}h ago`;\n\tconst days = Math.floor(hours / 24);\n\treturn `${days}d ago`;\n}\n\nfunction formatModelList(models: Model<any>[], maxLines = MAX_MODEL_LIST_LINES): string[] {\n\tif (models.length === 0) return [\"No models available.\"];\n\tconst byProvider = new Map<string, Model<any>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\tconst providers = [...byProvider.keys()].sort();\n\tconst lines: string[] = [];\n\tlet index = 1;\n\tfor (const provider of providers) {\n\t\tconst list = (byProvider.get(provider) ?? []).sort((a, b) => a.id.localeCompare(b.id));\n\t\tfor (const model of list) {\n\t\t\tlines.push(`${index}) ${provider}/${model.id}`);\n\t\t\tindex++;\n\t\t}\n\t}\n\tif (lines.length <= maxLines) return lines;\n\treturn [\n\t\t`${models.length} models across ${providers.length} providers.`,\n\t\t\"Use /model <provider/id> or /model <query>.\",\n\t\t...providers.map((p) => `- ${p}`),\n\t];\n}\n\nfunction groupModelsByProvider(models: Model<any>[]): Map<string, Model<any>[]> {\n\tconst byProvider = new Map<string, Model<any>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\tfor (const list of byProvider.values()) {\n\t\tlist.sort((a, b) => a.id.localeCompare(b.id));\n\t}\n\treturn byProvider;\n}\n\nfunction truncateModelId(id: string): string {\n\tconst lastSlash = id.lastIndexOf(\"/\");\n\tconst name = lastSlash >= 0 ? id.slice(lastSlash + 1) : id;\n\treturn truncate(name, MODEL_ID_MAX);\n}\n\nasync function refreshAndCacheModels(session: BridgeSession, key: string): Promise<Model<any>[]> {\n\tconst runtime: ModelRuntime = session.modelRuntime;\n\tawait runtime.refresh();\n\tconst models = [...(await runtime.getAvailable())].sort(\n\t\t(a, b) => a.provider.localeCompare(b.provider) || a.id.localeCompare(b.id),\n\t);\n\tsetCachedModels(key, models);\n\treturn models;\n}\n\nasync function setModelAndReply(ctx: ChatCommandContext, model: Model<any>): Promise<void> {\n\tconst session = ctx.session!;\n\ttry {\n\t\tawait session.setModel(model);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message.includes(\"No API key\")) {\n\t\t\tawait ctx.reply(`No API key for ${model.provider}.`);\n\t\t\treturn;\n\t\t}\n\t\tthrow error;\n\t}\n\tawait ctx.reply(`Model → ${model.provider}/${model.id} (thinking level re-clamped to ${session.thinkingLevel})`);\n}\n\nconst newCommand: ChatCommand = {\n\tname: \"new\",\n\taliases: [\"reset\"],\n\tdescription: \"start a fresh session for this chat\",\n\t// lunr: busy-session /new must bypass the guard (same as /stop).\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.reset(ctx.key);\n\t\tawait ctx.reply(\"Session reset — next message starts a fresh session.\");\n\t},\n};\n\nconst undoCommand: ChatCommand = {\n\tname: \"undo\",\n\tdescription: \"rewind the session tree before the last user turn\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst { userText } = await ctx.bridge.undo(ctx.key);\n\t\tconst text = userText ? ` \"${truncate(userText, 60)}\"` : \"\";\n\t\tawait ctx.reply(`Undone — rewound before${text}. /redo to restore.`);\n\t},\n};\n\nconst redoCommand: ChatCommand = {\n\tname: \"redo\",\n\tdescription: \"restore the last undone turn\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.redo(ctx.key);\n\t\tawait ctx.reply(\"Redone.\");\n\t},\n};\n\nconst stopCommand: ChatCommand = {\n\tname: \"stop\",\n\tdescription: \"abort the running turn\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.abort(ctx.key);\n\t\tawait ctx.reply(\"Stopped.\");\n\t},\n};\n\nconst statusCommand: ChatCommand = {\n\tname: \"status\",\n\tdescription: \"session status\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tconst status = ctx.bridge.getStatus(ctx.key);\n\t\tconst age = status.createdAt\n\t\t\t? `${Math.max(0, Math.round((Date.now() - Date.parse(status.createdAt)) / 1000))}s`\n\t\t\t: \"no session yet\";\n\t\tawait ctx.reply(\n\t\t\t`${ctx.event.source.platform} · session age ${age} · ${status.busy ? \"busy\" : \"idle\"} · queue ${status.queueDepth}\\nProject: ${conversationBinding(ctx.key)?.cwd ?? \"not selected\"}`,\n\t\t);\n\t},\n};\n\nconst whoamiCommand: ChatCommand = {\n\tname: \"whoami\",\n\tdescription: \"your ids and session key\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tconst { userId, chatId } = ctx.event.source;\n\t\tawait ctx.reply(`userId: ${userId}\\nchatId: ${chatId}\\nkey: ${ctx.key}`);\n\t},\n};\n\nconst helpCommand: ChatCommand = {\n\tname: \"help\",\n\tdescription: \"this message\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.reply(formatHelpText());\n\t},\n};\n\nconst modelCommand: ChatCommand = {\n\tname: \"model\",\n\tdescription: \"list or switch models\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst arg = ctx.args.trim();\n\t\tif (!arg) {\n\t\t\tconst cached = getCachedModels(ctx.key);\n\t\t\tconst models = cached ?? (await refreshAndCacheModels(session, ctx.key));\n\t\t\tconst byProvider = groupModelsByProvider(models);\n\t\t\tconst current = session.model;\n\t\t\tconst currentRef = current ? `${current.provider}/${current.id}` : \"\";\n\t\t\tconst providers = [...byProvider.keys()].sort();\n\t\t\tconst providerItems: PickerItem[] = providers.map((provider) => ({\n\t\t\t\tlabel: `${current?.provider === provider ? \"✓ \" : \"\"}${provider} (${byProvider.get(provider)?.length ?? 0})`,\n\t\t\t\tvalue: provider,\n\t\t\t}));\n\n\t\t\tconst pickerOpts = {\n\t\t\t\treplyTo: ctx.event.messageId,\n\t\t\t\tthreadId: ctx.event.source.threadId,\n\t\t\t};\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"model\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems: providerItems,\n\t\t\t\t\tperPage: PROVIDER_PER_PAGE,\n\t\t\t\t\ttitle: \"Pick a model provider\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tif (item.value === \"__back__\") {\n\t\t\t\t\t\t\treturn { done: false, items: providerItems, title: \"Pick a model provider\" };\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!item.value.includes(\"/\")) {\n\t\t\t\t\t\t\tconst provider = item.value;\n\t\t\t\t\t\t\tconst list = byProvider.get(provider) ?? [];\n\t\t\t\t\t\t\tconst capped = list.slice(0, MAX_MODELS_PER_PROVIDER);\n\t\t\t\t\t\t\tconst note =\n\t\t\t\t\t\t\t\tlist.length > MAX_MODELS_PER_PROVIDER\n\t\t\t\t\t\t\t\t\t? ` — ${list.length - MAX_MODELS_PER_PROVIDER} more available`\n\t\t\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\t\tconst modelItems: PickerItem[] = [];\n\t\t\t\t\t\t\tfor (const m of capped) {\n\t\t\t\t\t\t\t\tconst ref = `${provider}/${m.id}`;\n\t\t\t\t\t\t\t\tmodelItems.push({\n\t\t\t\t\t\t\t\t\tlabel: `${currentRef === ref ? \"✓ \" : \"\"}${truncateModelId(m.id)}`,\n\t\t\t\t\t\t\t\t\tvalue: ref,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmodelItems.unshift({ label: \"◀ Back\", value: \"__back__\" });\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tdone: false,\n\t\t\t\t\t\t\t\titems: modelItems,\n\t\t\t\t\t\t\t\ttitle: `${provider} (${list.length})${note}`,\n\t\t\t\t\t\t\t\tbreadcrumbs: provider,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst exact = findExactModelReferenceMatch(item.value, models);\n\t\t\t\t\t\tif (!exact) {\n\t\t\t\t\t\t\treturn { done: true, text: \"That model is no longer available.\" };\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait session.setModel(exact);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t\t\tif (message.includes(\"No API key\")) {\n\t\t\t\t\t\t\t\treturn { done: true, text: `No API key for ${exact.provider}.` };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tdone: true,\n\t\t\t\t\t\t\ttext: `☾ Model → ${exact.provider}/${exact.id} (thinking level re-clamped to ${session.thinkingLevel})`,\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpickerOpts,\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tconst currentText = `${current?.provider ?? \"none\"}/${current?.id ?? \"none\"}`;\n\t\t\t\tconst lines = formatModelList(models);\n\t\t\t\tawait ctx.reply([`Current: ${currentText}`, ...lines, \"Use /model <n> or /model <provider/id>\"].join(\"\\n\"));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst numeric = Number.parseInt(arg, 10);\n\t\tif (!Number.isNaN(numeric) && /^\\d+$/.test(arg)) {\n\t\t\tconst cached = getCachedModels(ctx.key);\n\t\t\tif (!cached || numeric < 1 || numeric > cached.length) {\n\t\t\t\tawait ctx.reply(\"Invalid selection — run /model to see the current list.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait setModelAndReply(ctx, cached[numeric - 1]);\n\t\t\treturn;\n\t\t}\n\n\t\tconst models = getCachedModels(ctx.key) ?? (await refreshAndCacheModels(session, ctx.key));\n\t\tconst exact = findExactModelReferenceMatch(arg, models);\n\t\tif (exact) {\n\t\t\tawait setModelAndReply(ctx, exact);\n\t\t\treturn;\n\t\t}\n\n\t\tconst query = arg.toLowerCase();\n\t\tconst matches = models.filter(\n\t\t\t(m) =>\n\t\t\t\tm.id.toLowerCase().includes(query) ||\n\t\t\t\t`${m.provider}/${m.id}`.toLowerCase().includes(query) ||\n\t\t\t\t(m.name?.toLowerCase().includes(query) ?? false),\n\t\t);\n\t\tif (matches.length === 0) {\n\t\t\tawait ctx.reply(`No model matches \"${arg}\".`);\n\t\t\treturn;\n\t\t}\n\t\tif (matches.length > 1) {\n\t\t\tawait ctx.reply(formatModelList(matches).join(\"\\n\"));\n\t\t\treturn;\n\t\t}\n\t\tawait setModelAndReply(ctx, matches[0]);\n\t},\n};\n\nconst sessionsCommand: ChatCommand = {\n\tname: \"sessions\",\n\tdescription: \"list or switch sessions\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst arg = ctx.args.trim();\n\t\tconst currentFile = session.sessionManager?.getSessionFile();\n\n\t\tif (!arg) {\n\t\t\tconst all = await SessionManager.list(process.cwd());\n\t\t\tall.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\t\tconst sessions = all.slice(0, 10);\n\t\t\tsetCachedSessions(ctx.key, sessions);\n\t\t\tconst items: PickerItem[] = sessions.map((s) => ({\n\t\t\t\tlabel: `${s.path === currentFile ? \"☾ \" : \"\"}${s.name ? truncate(s.name, 30) : truncate(s.firstMessage, 30) || \"(empty)\"}`,\n\t\t\t\tvalue: s.path,\n\t\t\t}));\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"sessions\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems,\n\t\t\t\t\tperPage: 8,\n\t\t\t\t\ttitle: \"Pick a session\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tawait ctx.bridge.switchSession(ctx.key, item.value);\n\t\t\t\t\t\tconst switched = await ctx.bridge.getSession(ctx.key);\n\t\t\t\t\t\tconst name = switched?.sessionManager?.getSessionName() ?? \"unnamed\";\n\t\t\t\t\t\tconst count = switched?.sessionManager?.getEntries().length ?? 0;\n\t\t\t\t\t\treturn { done: true, text: `☾ Switched to \"${name}\" (${count} msgs). History continues here.` };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId },\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tconst lines = sessions.map((s, i) => {\n\t\t\t\t\tconst marker = s.path === currentFile ? \" ☾\" : \"\";\n\t\t\t\t\tconst label = s.name ? truncate(s.name, 40) : truncate(s.firstMessage, 40) || \"(empty)\";\n\t\t\t\t\treturn `${i + 1}) ${label}${marker} · ${formatRelativeTime(s.modified)} · ${s.messageCount} msgs`;\n\t\t\t\t});\n\t\t\t\tawait ctx.reply(\n\t\t\t\t\t[`Sessions for ${process.cwd()}`, ...lines, \"Use /sessions <n> or /sessions <id-prefix>\"].join(\"\\n\"),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tlet path: string | undefined;\n\t\tconst numeric = Number.parseInt(arg, 10);\n\t\tconst cached = getCachedSessions(ctx.key);\n\t\tif (!Number.isNaN(numeric) && /^\\d+$/.test(arg)) {\n\t\t\tif (!cached || numeric < 1 || numeric > cached.length) {\n\t\t\t\tawait ctx.reply(\"Invalid selection — run /sessions to see the current list.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpath = cached[numeric - 1].path;\n\t\t} else {\n\t\t\tconst pool = cached ?? (await SessionManager.list(process.cwd()));\n\t\t\tconst matches = pool.filter((s) => s.id.startsWith(arg) || s.path.startsWith(arg));\n\t\t\tif (matches.length === 0) {\n\t\t\t\tawait ctx.reply(`No session matches \"${arg}\".`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (matches.length > 1) {\n\t\t\t\tawait ctx.reply(`Multiple sessions match \"${arg}\" — use a longer prefix.`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpath = matches[0].path;\n\t\t}\n\n\t\tif (!path) {\n\t\t\tawait ctx.reply(\"No session selected.\");\n\t\t\treturn;\n\t\t}\n\n\t\tawait ctx.bridge.switchSession(ctx.key, path);\n\t\tconst switched = await ctx.bridge.getSession(ctx.key);\n\t\tconst name = switched?.sessionManager?.getSessionName() ?? \"unnamed\";\n\t\tconst count = switched?.sessionManager?.getEntries().length ?? 0;\n\t\tawait ctx.reply(`Switched to \"${name}\" (${count} msgs). History continues here.`);\n\t},\n};\n\nconst titleCommand: ChatCommand = {\n\tname: \"title\",\n\taliases: [\"name\"],\n\tdescription: \"name the current session\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst name = ctx.args.trim();\n\t\tif (!name) {\n\t\t\tconst current = session.sessionManager?.getSessionName();\n\t\t\tawait ctx.reply(current ? `Session title: ${current}` : \"(unnamed)\");\n\t\t\treturn;\n\t\t}\n\t\tsession.setSessionName(name);\n\t\tconst readBack = session.sessionManager?.getSessionName() ?? name;\n\t\tawait ctx.reply(`Session titled \"${readBack}\".`);\n\t},\n};\n\nconst contextCommand: ChatCommand = {\n\tname: \"context\",\n\tdescription: \"estimated context window breakdown\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst model = session.model;\n\t\tif (!model) {\n\t\t\tawait ctx.reply(\"No model selected.\");\n\t\t\treturn;\n\t\t}\n\t\tconst contextWindow = model.contextWindow ?? 0;\n\t\tif (contextWindow <= 0) {\n\t\t\tawait ctx.reply(\"Context window unknown for this model.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst tools: ToolDefinition[] = session\n\t\t\t.getActiveToolNames()\n\t\t\t.map((name) => session.getToolDefinition(name))\n\t\t\t.filter((d): d is ToolDefinition => d !== undefined);\n\n\t\tconst breakdown = computeContextBreakdown({\n\t\t\tsystemPrompt: session.systemPrompt,\n\t\t\ttools,\n\t\t\tmessages: session.messages,\n\t\t\tcontextWindow,\n\t\t});\n\n\t\tconst usage = session.getContextUsage();\n\t\tconst tokens = usage?.tokens ?? breakdown.total;\n\t\tconst percent = usage?.percent ?? (contextWindow > 0 ? (tokens / contextWindow) * 100 : 0);\n\n\t\tconst messageTokens =\n\t\t\tbreakdown.user +\n\t\t\tbreakdown.assistantText +\n\t\t\tbreakdown.thinking +\n\t\t\tbreakdown.toolCalls +\n\t\t\tbreakdown.toolResults +\n\t\t\tbreakdown.summaries;\n\n\t\tconst stats = session.getSessionStats();\n\t\tawait ctx.reply(\n\t\t\t[\n\t\t\t\t`Context: ${tokens.toLocaleString(\"en-US\")} / ${contextWindow.toLocaleString(\"en-US\")} tokens (${Math.round(percent)}%)`,\n\t\t\t\t` system ${breakdown.systemPrompt.toLocaleString(\"en-US\")} · tools ${breakdown.toolDefinitions.toLocaleString(\"en-US\")} · messages ${messageTokens.toLocaleString(\"en-US\")}`,\n\t\t\t\t`Session: ${stats.totalMessages} messages · ${stats.userMessages} turns · model ${model.provider}/${model.id}`,\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t},\n};\n\nconst compactCommand: ChatCommand = {\n\tname: \"compact\",\n\tdescription: \"summarize and compress session context\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst instructions = ctx.args.trim() || undefined;\n\t\ttry {\n\t\t\tconst result = await session.compact(instructions);\n\t\t\tconst before = result.tokensBefore.toLocaleString(\"en-US\");\n\t\t\tconst after =\n\t\t\t\tresult.estimatedTokensAfter !== undefined\n\t\t\t\t\t? `~${result.estimatedTokensAfter.toLocaleString(\"en-US\")}`\n\t\t\t\t\t: \"~?\";\n\t\t\tawait ctx.reply(`Compacted: ${before} → ${after} tokens.`);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tif (\n\t\t\t\tmessage.includes(\"Nothing to compact\") ||\n\t\t\t\tmessage.includes(\"Already compacted\") ||\n\t\t\t\tmessage.includes(\"No model selected\")\n\t\t\t) {\n\t\t\t\tawait ctx.reply(message);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t},\n};\n\nconst thinkingCommand: ChatCommand = {\n\tname: \"thinking\",\n\taliases: [\"effort\", \"reasoning\"],\n\tdescription: \"get or set the thinking level\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst levels = session.getAvailableThinkingLevels();\n\t\tif (!session.supportsThinking()) {\n\t\t\tawait ctx.reply(\n\t\t\t\t`Model ${session.model?.provider ?? \"none\"}/${session.model?.id ?? \"none\"} doesn't support thinking.`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst arg = ctx.args.trim().toLowerCase() as ThinkingLevel;\n\t\tif (!arg) {\n\t\t\tconst current = session.thinkingLevel;\n\t\t\tconst items: PickerItem[] = levels.map((level) => ({\n\t\t\t\tlabel: `${level === current ? \"✓ \" : \"\"}${level}`,\n\t\t\t\tvalue: level,\n\t\t\t}));\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"thinking\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems,\n\t\t\t\t\tperPage: levels.length,\n\t\t\t\t\ttitle: \"Pick a thinking level\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tconst level = item.value as ThinkingLevel;\n\t\t\t\t\t\tsession.setThinkingLevel(level);\n\t\t\t\t\t\treturn { done: true, text: `☾ Thinking → ${level}` };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId },\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tawait ctx.reply(`Level: ${current} — available: ${levels.join(\", \")}`);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!levels.includes(arg)) {\n\t\t\tawait ctx.reply(`Invalid level. Available: ${levels.join(\", \")}`);\n\t\t\treturn;\n\t\t}\n\t\tsession.setThinkingLevel(arg);\n\t\tawait ctx.reply(`Thinking → ${arg}`);\n\t},\n};\n\nexport const CHAT_COMMANDS: ChatCommand[] = [\n\tnewCommand,\n\tundoCommand,\n\tredoCommand,\n\tstopCommand,\n\tstatusCommand,\n\twhoamiCommand,\n\thelpCommand,\n\tmodelCommand,\n\tsessionsCommand,\n\ttitleCommand,\n\tcontextCommand,\n\tcompactCommand,\n\tthinkingCommand,\n];\n\n/** Platform menu specs (e.g. Telegram setMyCommands): canonical names only, aliases skipped. */\nexport function botCommandSpecs(): { name: string; description: string }[] {\n\treturn [\n\t\t...new Map(\n\t\t\t[...CHAT_COMMANDS, ...MOBILE_COMMANDS, ...FORWARDED_COMMANDS].map((c) => [\n\t\t\t\tc.name,\n\t\t\t\t{ name: c.name, description: c.description },\n\t\t\t]),\n\t\t).values(),\n\t];\n}\n\nexport function formatHelpText(): string {\n\tconst lines = [\"lunR gateway commands:\"];\n\tfor (const cmd of CHAT_COMMANDS) {\n\t\tconst names = [cmd.name, ...(cmd.aliases ?? [])].map((a) => `/${a}`).join(\" | \");\n\t\tlines.push(`${names} — ${cmd.description}`);\n\t}\n\tfor (const cmd of botCommandSpecs()) {\n\t\tif (!CHAT_COMMANDS.some((c) => c.name === cmd.name)) lines.push(`/${cmd.name} · ${cmd.description}`);\n\t}\n\tlines.push(\"\");\n\tlines.push(\"Tap to pick: /model, /thinking, /sessions (run without args to see buttons).\");\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Run a chat command with busy/session guards and error → \"⚠ one-line\".\n * Returns true when the event was consumed by the command, false when the\n * router should continue when a command mutates the event and wants a normal turn.\n */\nexport async function runChatCommand(cmd: ChatCommand, ctx: ChatCommandContext): Promise<boolean> {\n\tif (!cmd.bypassBusy && ctx.bridge.getStatus(ctx.key).busy) {\n\t\tawait ctx.reply(\"⏳ busy — /stop first or wait\");\n\t\treturn true;\n\t}\n\tif (cmd.needsSession) {\n\t\tconst session = await ctx.bridge.getSession(ctx.key);\n\t\tif (!session) {\n\t\t\tawait ctx.reply(\"No session yet — send a message first.\");\n\t\t\treturn true;\n\t\t}\n\t\tctx.session = session;\n\t}\n\ttry {\n\t\tconst settingsManager = ctx.session?.settingsManager;\n\t\tconst consumed = settingsManager\n\t\t\t? await runtimeScope.run(\n\t\t\t\t\t{ settingsManager, modelRuntime: ctx.session?.modelRuntime, thinking: () => ctx.session!.thinkingLevel },\n\t\t\t\t\t() => cmd.handler(ctx),\n\t\t\t\t)\n\t\t\t: await cmd.handler(ctx);\n\t\treturn consumed ?? true;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tawait ctx.adapter.send(ctx.event.source.chatId, `⚠ ${oneLine(message)}`, {\n\t\t\treplyTo: ctx.event.messageId,\n\t\t\tthreadId: ctx.event.source.threadId,\n\t\t});\n\t\treturn true;\n\t}\n}\n\n/** Reply helper used by the router to build the ChatCommandContext. */\nexport async function sendCommandReply(adapter: PlatformAdapter, event: MessageEvent, text: string): Promise<void> {\n\tawait adapter.send(event.source.chatId, prefixLines(text, \"☾ \"), {\n\t\treplyTo: event.messageId,\n\t\tthreadId: event.source.threadId,\n\t});\n}\n"]}
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/gateway/commands.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway chat command registry.\n *\n * Every command is a small headless handler over AgentSession. Router.ts keeps\n * platform gating/authorization and delegates to runChatCommand for busy/session\n * guards, error wrapping, and /help generation.\n */\n\nimport type { ThinkingLevel } from \"@ashx-j/lunr-agent\";\nimport type { Model } from \"@ashx-j/lunr-ai/compat\";\nimport { computeContextBreakdown } from \"../core/context-breakdown.ts\";\nimport type { ToolDefinition } from \"../core/extensions/types.ts\";\nimport { findExactModelReferenceMatch } from \"../core/model-resolver.ts\";\nimport type { ModelRuntime } from \"../core/model-runtime.ts\";\nimport { runtimeScope } from \"../core/runtime-scope.ts\";\nimport type { SessionInfo } from \"../core/session-manager.ts\";\nimport { SessionManager } from \"../core/session-manager.ts\";\nimport type { BridgeSession } from \"./agent-bridge.ts\";\nimport { isGatewayOwner } from \"./authz.ts\";\nimport { createPicker, type PickerItem } from \"./buttons.ts\";\nimport { defaultGatewayConfig, type GatewayConfig } from \"./config.ts\";\nimport { conversationBinding } from \"./conversations.ts\";\nimport { FORWARDED_COMMANDS, MOBILE_COMMANDS, resolveWithinRoots } from \"./mobile-commands.ts\";\nimport type { BridgeLike } from \"./router.ts\";\nimport type { MessageEvent, PlatformAdapter } from \"./types.ts\";\n\nconst MODEL_CACHE_TTL_MS = 5 * 60 * 1000;\nconst SESSIONS_CACHE_TTL_MS = 5 * 60 * 1000;\nconst MAX_MODEL_LIST_LINES = 15;\nconst PROVIDER_PER_PAGE = 10;\nconst _MODEL_PER_PAGE = 8;\nconst MAX_MODELS_PER_PROVIDER = 50;\nconst MODEL_ID_MAX = 38;\nconst MODEL_CACHE_CAP = 100;\nconst SESSIONS_CACHE_CAP = 100;\n\ninterface ModelCacheEntry {\n\tmodels: Model<any>[];\n\texpires: number;\n}\n\ninterface SessionsCacheEntry {\n\tsessions: SessionInfo[];\n\texpires: number;\n}\n\nconst modelCache = new Map<string, ModelCacheEntry>();\nconst sessionsCache = new Map<string, SessionsCacheEntry>();\n\nfunction evictLRU<K, V>(map: Map<K, V>, cap: number): void {\n\twhile (map.size > cap) {\n\t\tconst first = map.keys().next().value;\n\t\tif (first !== undefined) map.delete(first);\n\t}\n}\n\nfunction getCachedModels(key: string): Model<any>[] | undefined {\n\tconst cached = modelCache.get(key);\n\tif (!cached) return undefined;\n\tif (Date.now() > cached.expires) {\n\t\tmodelCache.delete(key);\n\t\treturn undefined;\n\t}\n\t// LRU refresh.\n\tmodelCache.delete(key);\n\tmodelCache.set(key, cached);\n\treturn cached.models;\n}\n\nfunction setCachedModels(key: string, models: Model<any>[]): void {\n\tmodelCache.set(key, { models, expires: Date.now() + MODEL_CACHE_TTL_MS });\n\tevictLRU(modelCache, MODEL_CACHE_CAP);\n}\n\nfunction getCachedSessions(key: string): SessionInfo[] | undefined {\n\tconst cached = sessionsCache.get(key);\n\tif (!cached) return undefined;\n\tif (Date.now() > cached.expires) {\n\t\tsessionsCache.delete(key);\n\t\treturn undefined;\n\t}\n\tsessionsCache.delete(key);\n\tsessionsCache.set(key, cached);\n\treturn cached.sessions;\n}\n\nfunction setCachedSessions(key: string, sessions: SessionInfo[]): void {\n\tsessionsCache.set(key, { sessions, expires: Date.now() + SESSIONS_CACHE_TTL_MS });\n\tevictLRU(sessionsCache, SESSIONS_CACHE_CAP);\n}\n\nexport interface ChatCommandContext {\n\tevent: MessageEvent;\n\tkey: string;\n\tadapter: PlatformAdapter;\n\tbridge: BridgeLike;\n\tcfg?: GatewayConfig;\n\tsession?: BridgeSession;\n\treply(text: string): Promise<void>;\n\targs: string;\n}\n\nexport interface ChatCommand {\n\tname: string;\n\taliases?: string[];\n\tdescription: string;\n\tbypassBusy: boolean;\n\tneedsSession: boolean;\n\t// biome-ignore lint/suspicious/noConfusingVoidType: handlers may return nothing; runChatCommand treats undefined as consumed.\n\thandler(ctx: ChatCommandContext): Promise<boolean | void>;\n}\n\nfunction oneLine(text: string): string {\n\treturn text.replace(/\\s+/g, \" \").trim().slice(0, 200);\n}\n\nfunction prefixLines(text: string, prefix: string): string {\n\treturn text\n\t\t.split(\"\\n\")\n\t\t.map((line, index) => (index === 0 ? prefix : \" \") + line)\n\t\t.join(\"\\n\");\n}\n\nfunction truncate(text: string, max: number): string {\n\tif (text.length <= max) return text;\n\treturn `${text.slice(0, max - 1)}…`;\n}\n\nfunction formatRelativeTime(date: Date): string {\n\tconst seconds = Math.max(0, Math.floor((Date.now() - date.getTime()) / 1000));\n\tif (seconds < 60) return `${seconds}s ago`;\n\tconst minutes = Math.floor(seconds / 60);\n\tif (minutes < 60) return `${minutes}m ago`;\n\tconst hours = Math.floor(minutes / 60);\n\tif (hours < 24) return `${hours}h ago`;\n\tconst days = Math.floor(hours / 24);\n\treturn `${days}d ago`;\n}\n\nfunction formatModelList(models: Model<any>[], maxLines = MAX_MODEL_LIST_LINES): string[] {\n\tif (models.length === 0) return [\"No models available.\"];\n\tconst byProvider = new Map<string, Model<any>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\tconst providers = [...byProvider.keys()].sort();\n\tconst lines: string[] = [];\n\tlet index = 1;\n\tfor (const provider of providers) {\n\t\tconst list = (byProvider.get(provider) ?? []).sort((a, b) => a.id.localeCompare(b.id));\n\t\tfor (const model of list) {\n\t\t\tlines.push(`${index}) ${provider}/${model.id}`);\n\t\t\tindex++;\n\t\t}\n\t}\n\tif (lines.length <= maxLines) return lines;\n\treturn [\n\t\t`${models.length} models across ${providers.length} providers.`,\n\t\t\"Use /model <provider/id> or /model <query>.\",\n\t\t...providers.map((p) => `- ${p}`),\n\t];\n}\n\nfunction groupModelsByProvider(models: Model<any>[]): Map<string, Model<any>[]> {\n\tconst byProvider = new Map<string, Model<any>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\tfor (const list of byProvider.values()) {\n\t\tlist.sort((a, b) => a.id.localeCompare(b.id));\n\t}\n\treturn byProvider;\n}\n\nfunction truncateModelId(id: string): string {\n\tconst lastSlash = id.lastIndexOf(\"/\");\n\tconst name = lastSlash >= 0 ? id.slice(lastSlash + 1) : id;\n\treturn truncate(name, MODEL_ID_MAX);\n}\n\nasync function refreshAndCacheModels(session: BridgeSession, key: string): Promise<Model<any>[]> {\n\tconst runtime: ModelRuntime = session.modelRuntime;\n\tawait runtime.refresh();\n\tconst models = [...(await runtime.getAvailable())].sort(\n\t\t(a, b) => a.provider.localeCompare(b.provider) || a.id.localeCompare(b.id),\n\t);\n\tsetCachedModels(key, models);\n\treturn models;\n}\n\nasync function setModelAndReply(ctx: ChatCommandContext, model: Model<any>): Promise<void> {\n\tconst session = ctx.session!;\n\ttry {\n\t\tawait session.setModel(model);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message.includes(\"No API key\")) {\n\t\t\tawait ctx.reply(`No API key for ${model.provider}.`);\n\t\t\treturn;\n\t\t}\n\t\tthrow error;\n\t}\n\tawait ctx.reply(`Model → ${model.provider}/${model.id} (thinking level re-clamped to ${session.thinkingLevel})`);\n}\n\nconst newCommand: ChatCommand = {\n\tname: \"new\",\n\taliases: [\"reset\"],\n\tdescription: \"start a fresh session for this chat\",\n\t// lunr: busy-session /new must bypass the guard (same as /stop).\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.reset(ctx.key);\n\t\tconst cfg = ctx.cfg ?? defaultGatewayConfig();\n\t\tconst owner = isGatewayOwner(ctx.event.source, cfg);\n\t\tlet ready = Boolean(conversationBinding(ctx.key)?.cwd);\n\t\tif (!ready && owner && cfg.defaultProject) {\n\t\t\ttry {\n\t\t\t\tresolveWithinRoots(cfg.defaultProject, cfg.projectRoots ?? []);\n\t\t\t\tready = true;\n\t\t\t} catch {}\n\t\t}\n\t\tawait ctx.reply(\n\t\t\tready\n\t\t\t\t? \"Session reset. Send a message to start fresh in the approved project.\"\n\t\t\t\t: owner\n\t\t\t\t\t? \"Session reset. Use /project to choose an approved project before sending a message.\"\n\t\t\t\t\t: \"Session reset. Ask the computer's gateway owner to configure this chat's project access locally.\",\n\t\t);\n\t},\n};\n\nconst undoCommand: ChatCommand = {\n\tname: \"undo\",\n\tdescription: \"rewind the session tree before the last user turn\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst { userText } = await ctx.bridge.undo(ctx.key);\n\t\tconst text = userText ? ` \"${truncate(userText, 60)}\"` : \"\";\n\t\tawait ctx.reply(`Undone — rewound before${text}. /redo to restore.`);\n\t},\n};\n\nconst redoCommand: ChatCommand = {\n\tname: \"redo\",\n\tdescription: \"restore the last undone turn\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.redo(ctx.key);\n\t\tawait ctx.reply(\"Redone.\");\n\t},\n};\n\nconst stopCommand: ChatCommand = {\n\tname: \"stop\",\n\tdescription: \"abort the running turn\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.abort(ctx.key);\n\t\tawait ctx.reply(\"Stopped.\");\n\t},\n};\n\nconst statusCommand: ChatCommand = {\n\tname: \"status\",\n\tdescription: \"session status\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tconst status = ctx.bridge.getStatus(ctx.key);\n\t\tconst age = status.createdAt\n\t\t\t? `${Math.max(0, Math.round((Date.now() - Date.parse(status.createdAt)) / 1000))}s`\n\t\t\t: \"no session yet\";\n\t\tawait ctx.reply(\n\t\t\t`${ctx.event.source.platform} · session age ${age} · ${status.busy ? \"busy\" : \"idle\"} · queue ${status.queueDepth}\\nProject: ${conversationBinding(ctx.key)?.cwd ?? \"not selected\"}`,\n\t\t);\n\t},\n};\n\nconst whoamiCommand: ChatCommand = {\n\tname: \"whoami\",\n\tdescription: \"your ids and session key\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tconst { userId, chatId } = ctx.event.source;\n\t\tawait ctx.reply(`userId: ${userId}\\nchatId: ${chatId}\\nkey: ${ctx.key}`);\n\t},\n};\n\nconst helpCommand: ChatCommand = {\n\tname: \"help\",\n\tdescription: \"this message\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.reply(formatHelpText());\n\t},\n};\n\nconst modelCommand: ChatCommand = {\n\tname: \"model\",\n\tdescription: \"list or switch models\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst arg = ctx.args.trim();\n\t\tif (!arg) {\n\t\t\tconst cached = getCachedModels(ctx.key);\n\t\t\tconst models = cached ?? (await refreshAndCacheModels(session, ctx.key));\n\t\t\tconst byProvider = groupModelsByProvider(models);\n\t\t\tconst current = session.model;\n\t\t\tconst currentRef = current ? `${current.provider}/${current.id}` : \"\";\n\t\t\tconst providers = [...byProvider.keys()].sort();\n\t\t\tconst providerItems: PickerItem[] = providers.map((provider) => ({\n\t\t\t\tlabel: `${current?.provider === provider ? \"✓ \" : \"\"}${provider} (${byProvider.get(provider)?.length ?? 0})`,\n\t\t\t\tvalue: provider,\n\t\t\t}));\n\n\t\t\tconst pickerOpts = {\n\t\t\t\treplyTo: ctx.event.messageId,\n\t\t\t\tthreadId: ctx.event.source.threadId,\n\t\t\t};\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"model\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems: providerItems,\n\t\t\t\t\tperPage: PROVIDER_PER_PAGE,\n\t\t\t\t\ttitle: \"Pick a model provider\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tif (item.value === \"__back__\") {\n\t\t\t\t\t\t\treturn { done: false, items: providerItems, title: \"Pick a model provider\" };\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!item.value.includes(\"/\")) {\n\t\t\t\t\t\t\tconst provider = item.value;\n\t\t\t\t\t\t\tconst list = byProvider.get(provider) ?? [];\n\t\t\t\t\t\t\tconst capped = list.slice(0, MAX_MODELS_PER_PROVIDER);\n\t\t\t\t\t\t\tconst note =\n\t\t\t\t\t\t\t\tlist.length > MAX_MODELS_PER_PROVIDER\n\t\t\t\t\t\t\t\t\t? ` — ${list.length - MAX_MODELS_PER_PROVIDER} more available`\n\t\t\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\t\tconst modelItems: PickerItem[] = [];\n\t\t\t\t\t\t\tfor (const m of capped) {\n\t\t\t\t\t\t\t\tconst ref = `${provider}/${m.id}`;\n\t\t\t\t\t\t\t\tmodelItems.push({\n\t\t\t\t\t\t\t\t\tlabel: `${currentRef === ref ? \"✓ \" : \"\"}${truncateModelId(m.id)}`,\n\t\t\t\t\t\t\t\t\tvalue: ref,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmodelItems.unshift({ label: \"◀ Back\", value: \"__back__\" });\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tdone: false,\n\t\t\t\t\t\t\t\titems: modelItems,\n\t\t\t\t\t\t\t\ttitle: `${provider} (${list.length})${note}`,\n\t\t\t\t\t\t\t\tbreadcrumbs: provider,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst exact = findExactModelReferenceMatch(item.value, models);\n\t\t\t\t\t\tif (!exact) {\n\t\t\t\t\t\t\treturn { done: true, text: \"That model is no longer available.\" };\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait session.setModel(exact);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t\t\tif (message.includes(\"No API key\")) {\n\t\t\t\t\t\t\t\treturn { done: true, text: `No API key for ${exact.provider}.` };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tdone: true,\n\t\t\t\t\t\t\ttext: `Model → ${exact.provider}/${exact.id} (thinking level re-clamped to ${session.thinkingLevel})`,\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpickerOpts,\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tconst currentText = `${current?.provider ?? \"none\"}/${current?.id ?? \"none\"}`;\n\t\t\t\tconst lines = formatModelList(models);\n\t\t\t\tawait ctx.reply([`Current: ${currentText}`, ...lines, \"Use /model <n> or /model <provider/id>\"].join(\"\\n\"));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst numeric = Number.parseInt(arg, 10);\n\t\tif (!Number.isNaN(numeric) && /^\\d+$/.test(arg)) {\n\t\t\tconst cached = getCachedModels(ctx.key);\n\t\t\tif (!cached || numeric < 1 || numeric > cached.length) {\n\t\t\t\tawait ctx.reply(\"Invalid selection — run /model to see the current list.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait setModelAndReply(ctx, cached[numeric - 1]);\n\t\t\treturn;\n\t\t}\n\n\t\tconst models = getCachedModels(ctx.key) ?? (await refreshAndCacheModels(session, ctx.key));\n\t\tconst exact = findExactModelReferenceMatch(arg, models);\n\t\tif (exact) {\n\t\t\tawait setModelAndReply(ctx, exact);\n\t\t\treturn;\n\t\t}\n\n\t\tconst query = arg.toLowerCase();\n\t\tconst matches = models.filter(\n\t\t\t(m) =>\n\t\t\t\tm.id.toLowerCase().includes(query) ||\n\t\t\t\t`${m.provider}/${m.id}`.toLowerCase().includes(query) ||\n\t\t\t\t(m.name?.toLowerCase().includes(query) ?? false),\n\t\t);\n\t\tif (matches.length === 0) {\n\t\t\tawait ctx.reply(`No model matches \"${arg}\".`);\n\t\t\treturn;\n\t\t}\n\t\tif (matches.length > 1) {\n\t\t\tawait ctx.reply(formatModelList(matches).join(\"\\n\"));\n\t\t\treturn;\n\t\t}\n\t\tawait setModelAndReply(ctx, matches[0]);\n\t},\n};\n\nconst sessionsCommand: ChatCommand = {\n\tname: \"sessions\",\n\tdescription: \"list or switch sessions\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst arg = ctx.args.trim();\n\t\tconst currentFile = session.sessionManager?.getSessionFile();\n\t\tconst cwd = session.sessionManager?.getCwd() ?? conversationBinding(ctx.key)?.cwd;\n\t\tif (!cwd) throw new Error(\"Select a project before listing sessions.\");\n\n\t\tif (!arg) {\n\t\t\tconst all = await SessionManager.list(cwd);\n\t\t\tall.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\t\tconst sessions = all.slice(0, 10);\n\t\t\tsetCachedSessions(ctx.key, sessions);\n\t\t\tconst items: PickerItem[] = sessions.map((s) => ({\n\t\t\t\tlabel: `${s.path === currentFile ? \"(current) \" : \"\"}${s.name ? truncate(s.name, 30) : truncate(s.firstMessage, 30) || \"(empty)\"}`,\n\t\t\t\tvalue: s.path,\n\t\t\t}));\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"sessions\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems,\n\t\t\t\t\tperPage: 8,\n\t\t\t\t\ttitle: \"Pick a session\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tawait ctx.bridge.switchSession(ctx.key, item.value);\n\t\t\t\t\t\tconst switched = await ctx.bridge.getSession(ctx.key);\n\t\t\t\t\t\tconst name = switched?.sessionManager?.getSessionName() ?? \"unnamed\";\n\t\t\t\t\t\tconst count = switched?.sessionManager?.getEntries().length ?? 0;\n\t\t\t\t\t\treturn { done: true, text: `Switched to \"${name}\" (${count} msgs). History continues here.` };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId },\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tconst lines = sessions.map((s, i) => {\n\t\t\t\t\tconst marker = s.path === currentFile ? \" (current)\" : \"\";\n\t\t\t\t\tconst label = s.name ? truncate(s.name, 40) : truncate(s.firstMessage, 40) || \"(empty)\";\n\t\t\t\t\treturn `${i + 1}) ${label}${marker} · ${formatRelativeTime(s.modified)} · ${s.messageCount} msgs`;\n\t\t\t\t});\n\t\t\t\tawait ctx.reply([`Sessions for ${cwd}`, ...lines, \"Use /sessions <n> or /sessions <id-prefix>\"].join(\"\\n\"));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tlet path: string | undefined;\n\t\tconst numeric = Number.parseInt(arg, 10);\n\t\tconst cached = getCachedSessions(ctx.key);\n\t\tif (!Number.isNaN(numeric) && /^\\d+$/.test(arg)) {\n\t\t\tif (!cached || numeric < 1 || numeric > cached.length) {\n\t\t\t\tawait ctx.reply(\"Invalid selection — run /sessions to see the current list.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpath = cached[numeric - 1].path;\n\t\t} else {\n\t\t\tconst pool = cached ?? (await SessionManager.list(cwd));\n\t\t\tconst matches = pool.filter((s) => s.id.startsWith(arg) || s.path.startsWith(arg));\n\t\t\tif (matches.length === 0) {\n\t\t\t\tawait ctx.reply(`No session matches \"${arg}\".`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (matches.length > 1) {\n\t\t\t\tawait ctx.reply(`Multiple sessions match \"${arg}\" — use a longer prefix.`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpath = matches[0].path;\n\t\t}\n\n\t\tif (!path) {\n\t\t\tawait ctx.reply(\"No session selected.\");\n\t\t\treturn;\n\t\t}\n\n\t\tawait ctx.bridge.switchSession(ctx.key, path);\n\t\tconst switched = await ctx.bridge.getSession(ctx.key);\n\t\tconst name = switched?.sessionManager?.getSessionName() ?? \"unnamed\";\n\t\tconst count = switched?.sessionManager?.getEntries().length ?? 0;\n\t\tawait ctx.reply(`Switched to \"${name}\" (${count} msgs). History continues here.`);\n\t},\n};\n\nconst titleCommand: ChatCommand = {\n\tname: \"title\",\n\taliases: [\"name\"],\n\tdescription: \"name the current session\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst name = ctx.args.trim();\n\t\tif (!name) {\n\t\t\tconst current = session.sessionManager?.getSessionName();\n\t\t\tawait ctx.reply(current ? `Session title: ${current}` : \"(unnamed)\");\n\t\t\treturn;\n\t\t}\n\t\tsession.setSessionName(name);\n\t\tconst readBack = session.sessionManager?.getSessionName() ?? name;\n\t\tawait ctx.reply(`Session titled \"${readBack}\".`);\n\t},\n};\n\nconst contextCommand: ChatCommand = {\n\tname: \"context\",\n\tdescription: \"estimated context window breakdown\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst model = session.model;\n\t\tif (!model) {\n\t\t\tawait ctx.reply(\"No model selected.\");\n\t\t\treturn;\n\t\t}\n\t\tconst contextWindow = model.contextWindow ?? 0;\n\t\tif (contextWindow <= 0) {\n\t\t\tawait ctx.reply(\"Context window unknown for this model.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst tools: ToolDefinition[] = session\n\t\t\t.getActiveToolNames()\n\t\t\t.map((name) => session.getToolDefinition(name))\n\t\t\t.filter((d): d is ToolDefinition => d !== undefined);\n\n\t\tconst breakdown = computeContextBreakdown({\n\t\t\tsystemPrompt: session.systemPrompt,\n\t\t\ttools,\n\t\t\tmessages: session.messages,\n\t\t\tcontextWindow,\n\t\t});\n\n\t\tconst usage = session.getContextUsage();\n\t\tconst tokens = usage?.tokens ?? breakdown.total;\n\t\tconst percent = usage?.percent ?? (contextWindow > 0 ? (tokens / contextWindow) * 100 : 0);\n\n\t\tconst messageTokens =\n\t\t\tbreakdown.user +\n\t\t\tbreakdown.assistantText +\n\t\t\tbreakdown.thinking +\n\t\t\tbreakdown.toolCalls +\n\t\t\tbreakdown.toolResults +\n\t\t\tbreakdown.summaries;\n\n\t\tconst stats = session.getSessionStats();\n\t\tawait ctx.reply(\n\t\t\t[\n\t\t\t\t`Context: ${tokens.toLocaleString(\"en-US\")} / ${contextWindow.toLocaleString(\"en-US\")} tokens (${Math.round(percent)}%)`,\n\t\t\t\t` system ${breakdown.systemPrompt.toLocaleString(\"en-US\")} · tools ${breakdown.toolDefinitions.toLocaleString(\"en-US\")} · messages ${messageTokens.toLocaleString(\"en-US\")}`,\n\t\t\t\t`Session: ${stats.totalMessages} messages · ${stats.userMessages} turns · model ${model.provider}/${model.id}`,\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t},\n};\n\nconst compactCommand: ChatCommand = {\n\tname: \"compact\",\n\tdescription: \"summarize and compress session context\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst instructions = ctx.args.trim() || undefined;\n\t\ttry {\n\t\t\tconst result = await session.compact(instructions);\n\t\t\tconst before = result.tokensBefore.toLocaleString(\"en-US\");\n\t\t\tconst after =\n\t\t\t\tresult.estimatedTokensAfter !== undefined\n\t\t\t\t\t? `~${result.estimatedTokensAfter.toLocaleString(\"en-US\")}`\n\t\t\t\t\t: \"~?\";\n\t\t\tawait ctx.reply(`Compacted: ${before} → ${after} tokens.`);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tif (\n\t\t\t\tmessage.includes(\"Nothing to compact\") ||\n\t\t\t\tmessage.includes(\"Already compacted\") ||\n\t\t\t\tmessage.includes(\"No model selected\")\n\t\t\t) {\n\t\t\t\tawait ctx.reply(message);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t},\n};\n\nconst thinkingCommand: ChatCommand = {\n\tname: \"thinking\",\n\taliases: [\"effort\", \"reasoning\"],\n\tdescription: \"get or set the thinking level\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst levels = session.getAvailableThinkingLevels();\n\t\tif (!session.supportsThinking()) {\n\t\t\tawait ctx.reply(\n\t\t\t\t`Model ${session.model?.provider ?? \"none\"}/${session.model?.id ?? \"none\"} doesn't support thinking.`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst arg = ctx.args.trim().toLowerCase() as ThinkingLevel;\n\t\tif (!arg) {\n\t\t\tconst current = session.thinkingLevel;\n\t\t\tconst items: PickerItem[] = levels.map((level) => ({\n\t\t\t\tlabel: `${level === current ? \"✓ \" : \"\"}${level}`,\n\t\t\t\tvalue: level,\n\t\t\t}));\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"thinking\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems,\n\t\t\t\t\tperPage: levels.length,\n\t\t\t\t\ttitle: \"Pick a thinking level\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tconst level = item.value as ThinkingLevel;\n\t\t\t\t\t\tsession.setThinkingLevel(level);\n\t\t\t\t\t\treturn { done: true, text: `Thinking → ${level}` };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId },\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tawait ctx.reply(`Level: ${current} — available: ${levels.join(\", \")}`);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!levels.includes(arg)) {\n\t\t\tawait ctx.reply(`Invalid level. Available: ${levels.join(\", \")}`);\n\t\t\treturn;\n\t\t}\n\t\tsession.setThinkingLevel(arg);\n\t\tawait ctx.reply(`Thinking → ${arg}`);\n\t},\n};\n\nexport const CHAT_COMMANDS: ChatCommand[] = [\n\tnewCommand,\n\tundoCommand,\n\tredoCommand,\n\tstopCommand,\n\tstatusCommand,\n\twhoamiCommand,\n\thelpCommand,\n\tmodelCommand,\n\tsessionsCommand,\n\ttitleCommand,\n\tcontextCommand,\n\tcompactCommand,\n\tthinkingCommand,\n];\n\n/** Platform menu specs (e.g. Telegram setMyCommands): canonical names only, aliases skipped. */\nexport function botCommandSpecs(): { name: string; description: string }[] {\n\treturn [\n\t\t...new Map(\n\t\t\t[\n\t\t\t\t{ name: \"start\", description: \"Check gateway readiness\" },\n\t\t\t\t...CHAT_COMMANDS,\n\t\t\t\t...MOBILE_COMMANDS,\n\t\t\t\t...FORWARDED_COMMANDS,\n\t\t\t].map((c) => [c.name, { name: c.name, description: c.description }]),\n\t\t).values(),\n\t];\n}\n\nexport function formatHelpText(): string {\n\tconst lines = [\"lunR gateway commands:\", \"/start — check gateway readiness\"];\n\tfor (const cmd of CHAT_COMMANDS) {\n\t\tconst names = [cmd.name, ...(cmd.aliases ?? [])].map((a) => `/${a}`).join(\" | \");\n\t\tlines.push(`${names} — ${cmd.description}`);\n\t}\n\tfor (const cmd of botCommandSpecs()) {\n\t\tif (!CHAT_COMMANDS.some((c) => c.name === cmd.name)) lines.push(`/${cmd.name} · ${cmd.description}`);\n\t}\n\tlines.push(\"\");\n\tlines.push(\"Tap to pick: /model, /thinking, /sessions (run without args to see buttons).\");\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Run a chat command with busy/session guards and error → \"⚠ one-line\".\n * Returns true when the event was consumed by the command, false when the\n * router should continue when a command mutates the event and wants a normal turn.\n */\nexport async function runChatCommand(cmd: ChatCommand, ctx: ChatCommandContext): Promise<boolean> {\n\tif (!cmd.bypassBusy && ctx.bridge.getStatus(ctx.key).busy) {\n\t\tawait ctx.reply(\"⏳ busy — /stop first or wait\");\n\t\treturn true;\n\t}\n\tif (cmd.needsSession) {\n\t\tconst session = await ctx.bridge.getSession(ctx.key, cmd.name === \"model\");\n\t\tif (!session) {\n\t\t\tawait ctx.reply(\"No session yet. Select an approved project with /project first.\");\n\t\t\treturn true;\n\t\t}\n\t\tctx.session = session;\n\t}\n\ttry {\n\t\tconst settingsManager = ctx.session?.settingsManager;\n\t\tconst consumed = settingsManager\n\t\t\t? await runtimeScope.run(\n\t\t\t\t\t{ settingsManager, modelRuntime: ctx.session?.modelRuntime, thinking: () => ctx.session!.thinkingLevel },\n\t\t\t\t\t() => cmd.handler(ctx),\n\t\t\t\t)\n\t\t\t: await cmd.handler(ctx);\n\t\treturn consumed ?? true;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tawait ctx.adapter.send(ctx.event.source.chatId, `⚠ ${oneLine(message)}`, {\n\t\t\treplyTo: ctx.event.messageId,\n\t\t\tthreadId: ctx.event.source.threadId,\n\t\t});\n\t\treturn true;\n\t}\n}\n\n/** Reply helper used by the router to build the ChatCommandContext. */\nexport async function sendCommandReply(adapter: PlatformAdapter, event: MessageEvent, text: string): Promise<void> {\n\tawait adapter.send(event.source.chatId, prefixLines(text, \"\"), {\n\t\treplyTo: event.messageId,\n\t\tthreadId: event.source.threadId,\n\t});\n}\n"]}
@@ -34,6 +34,7 @@ export interface GatewayConfig {
34
34
  discord: string[];
35
35
  };
36
36
  projectRoots?: string[];
37
+ defaultProject?: string;
37
38
  telegram: PlatformConfig;
38
39
  discord: DiscordConfig;
39
40
  /** Group/channel chats get one session per user when true. */
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/gateway/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAc,SAAQ,cAAc;IACpD,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC7B,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACnD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,aAAa,CAAC;IACvB,8DAA8D;IAC9D,oBAAoB,EAAE,OAAO,CAAC;IAC9B,mDAAmD;IACnD,sBAAsB,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1C,SAAS,EAAE,eAAe,CAAC;CAC3B;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAYD,wBAAgB,oBAAoB,IAAI,aAAa,CAUpD;AA+DD,kGAAkG;AAClG,wBAAgB,iBAAiB,IAAI,aAAa,CASjD;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAY7D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAGtG;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAIlG","sourcesContent":["/**\n * lunR: gateway config (<agentDir>/gateway.json).\n *\n * Follows the pi-ollama-cloud load/save pattern: JSON file under the agent\n * dir (getAgentDir(), honoring PI_CODING_AGENT_DIR), defaults when missing,\n * unknown keys dropped, malformed file tolerated. Writes are atomic\n * (tmp + rename) and the file is created chmod 0600 best-effort — it may\n * hold bot tokens.\n *\n * Token resolution order: LUNR_<PLATFORM>_BOT_TOKEN env →\n * <PLATFORM>_BOT_TOKEN env → file token.\n */\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../config.ts\";\n\nexport interface PlatformConfig {\n\tenabled: boolean;\n\ttoken?: string;\n\tallowedUsers: string[];\n\tallowedChats: string[];\n\trequireMention: boolean;\n\tfreeResponseChats: string[];\n\thomeChannel?: string;\n}\n\nexport interface DiscordConfig extends PlatformConfig {\n\tignoredChannels: string[];\n\tautoThread: boolean;\n}\n\nexport interface StreamingConfig {\n\tenabled: boolean;\n\teditIntervalMs: number;\n\tbufferThreshold: number;\n}\n\nexport interface GatewayConfig {\n\towners?: { telegram: string[]; discord: string[] };\n\tprojectRoots?: string[];\n\ttelegram: PlatformConfig;\n\tdiscord: DiscordConfig;\n\t/** Group/channel chats get one session per user when true. */\n\tgroupSessionsPerUser: boolean;\n\t/** What to do with DMs from unauthorized users. */\n\tunauthorizedDmBehavior: \"pair\" | \"ignore\";\n\tstreaming: StreamingConfig;\n}\n\nexport function gatewayConfigPath(): string {\n\treturn join(getAgentDir(), \"gateway.json\");\n}\n\nfunction defaultPlatformConfig(requireMention: boolean): PlatformConfig {\n\treturn {\n\t\tenabled: false,\n\t\tallowedUsers: [],\n\t\tallowedChats: [],\n\t\trequireMention,\n\t\tfreeResponseChats: [],\n\t};\n}\n\nexport function defaultGatewayConfig(): GatewayConfig {\n\treturn {\n\t\towners: { telegram: [], discord: [] },\n\t\tprojectRoots: [],\n\t\ttelegram: defaultPlatformConfig(false),\n\t\tdiscord: { ...defaultPlatformConfig(true), ignoredChannels: [], autoThread: true },\n\t\tgroupSessionsPerUser: true,\n\t\tunauthorizedDmBehavior: \"pair\",\n\t\tstreaming: { enabled: true, editIntervalMs: 800, bufferThreshold: 24 },\n\t};\n}\n\nfunction asStringArray(value: unknown): string[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\treturn value.every((v) => typeof v === \"string\") ? (value as string[]) : undefined;\n}\n\nfunction asBoolean(value: unknown): boolean | undefined {\n\treturn typeof value === \"boolean\" ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n\treturn typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction sanitizePlatform(raw: unknown, base: PlatformConfig): PlatformConfig {\n\tif (raw == null || typeof raw !== \"object\" || Array.isArray(raw)) return base;\n\tconst r = raw as Record<string, unknown>;\n\treturn {\n\t\tenabled: asBoolean(r.enabled) ?? base.enabled,\n\t\ttoken: typeof r.token === \"string\" ? r.token : base.token,\n\t\tallowedUsers: asStringArray(r.allowedUsers) ?? base.allowedUsers,\n\t\tallowedChats: asStringArray(r.allowedChats) ?? base.allowedChats,\n\t\trequireMention: asBoolean(r.requireMention) ?? base.requireMention,\n\t\tfreeResponseChats: asStringArray(r.freeResponseChats) ?? base.freeResponseChats,\n\t\thomeChannel: typeof r.homeChannel === \"string\" ? r.homeChannel : base.homeChannel,\n\t};\n}\n\nfunction sanitizeDiscord(raw: unknown, base: DiscordConfig): DiscordConfig {\n\tconst platform = sanitizePlatform(raw, base);\n\tconst r = raw != null && typeof raw === \"object\" && !Array.isArray(raw) ? (raw as Record<string, unknown>) : {};\n\treturn {\n\t\t...platform,\n\t\tignoredChannels: asStringArray(r.ignoredChannels) ?? base.ignoredChannels,\n\t\tautoThread: asBoolean(r.autoThread) ?? base.autoThread,\n\t};\n}\n\nfunction sanitizeConfig(raw: unknown): GatewayConfig {\n\tconst base = defaultGatewayConfig();\n\tif (raw == null || typeof raw !== \"object\" || Array.isArray(raw)) return base;\n\tconst r = raw as Record<string, unknown>;\n\tconst streamingRaw =\n\t\tr.streaming != null && typeof r.streaming === \"object\" && !Array.isArray(r.streaming)\n\t\t\t? (r.streaming as Record<string, unknown>)\n\t\t\t: {};\n\tconst owners = r.owners && typeof r.owners === \"object\" ? (r.owners as Record<string, unknown>) : {};\n\treturn {\n\t\towners: { telegram: asStringArray(owners.telegram) ?? [], discord: asStringArray(owners.discord) ?? [] },\n\t\tprojectRoots: asStringArray(r.projectRoots) ?? [],\n\t\ttelegram: sanitizePlatform(r.telegram, base.telegram),\n\t\tdiscord: sanitizeDiscord(r.discord, base.discord),\n\t\tgroupSessionsPerUser: asBoolean(r.groupSessionsPerUser) ?? base.groupSessionsPerUser,\n\t\tunauthorizedDmBehavior: r.unauthorizedDmBehavior === \"ignore\" ? \"ignore\" : \"pair\",\n\t\tstreaming: {\n\t\t\tenabled: asBoolean(streamingRaw.enabled) ?? base.streaming.enabled,\n\t\t\teditIntervalMs: asNumber(streamingRaw.editIntervalMs) ?? base.streaming.editIntervalMs,\n\t\t\tbufferThreshold: asNumber(streamingRaw.bufferThreshold) ?? base.streaming.bufferThreshold,\n\t\t},\n\t};\n}\n\n/** Load <agentDir>/gateway.json; defaults (both platforms disabled) when missing or malformed. */\nexport function loadGatewayConfig(): GatewayConfig {\n\tconst path = gatewayConfigPath();\n\tif (!existsSync(path)) return defaultGatewayConfig();\n\ttry {\n\t\treturn sanitizeConfig(JSON.parse(readFileSync(path, \"utf-8\")));\n\t} catch (err) {\n\t\tconsole.error(`[gateway] Failed to load config from ${path}: ${err}`);\n\t\treturn defaultGatewayConfig();\n\t}\n}\n\n/** Write the whole config atomically (tmp + rename); chmod 0600 best-effort. */\nexport function saveGatewayConfig(config: GatewayConfig): void {\n\tconst path = gatewayConfigPath();\n\tconst dir = getAgentDir();\n\tif (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n\tconst tmp = `${path}.tmp-${process.pid}`;\n\twriteFileSync(tmp, `${JSON.stringify(config, null, 2)}\\n`, \"utf-8\");\n\trenameSync(tmp, path);\n\ttry {\n\t\tchmodSync(path, 0o600);\n\t} catch {\n\t\t// best-effort (Windows ACLs don't map cleanly)\n\t}\n}\n\n/**\n * Resolve a platform's bot token: LUNR_<PLATFORM>_BOT_TOKEN env →\n * <PLATFORM>_BOT_TOKEN env → config file token.\n */\nexport function resolvePlatformToken(platform: string, platformCfg: PlatformConfig): string | undefined {\n\tconst upper = platform.toUpperCase();\n\treturn process.env[`LUNR_${upper}_BOT_TOKEN`] ?? process.env[`${upper}_BOT_TOKEN`] ?? platformCfg.token;\n}\n\n/** Per-platform config lookup; undefined for platforms Phase 2a doesn't model. */\nexport function platformConfigFor(cfg: GatewayConfig, platform: string): PlatformConfig | undefined {\n\tif (platform === \"telegram\") return cfg.telegram;\n\tif (platform === \"discord\") return cfg.discord;\n\treturn undefined;\n}\n"]}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/gateway/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAc,SAAQ,cAAc;IACpD,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC7B,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACnD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,aAAa,CAAC;IACvB,8DAA8D;IAC9D,oBAAoB,EAAE,OAAO,CAAC;IAC9B,mDAAmD;IACnD,sBAAsB,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1C,SAAS,EAAE,eAAe,CAAC;CAC3B;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAYD,wBAAgB,oBAAoB,IAAI,aAAa,CAWpD;AAgED,kGAAkG;AAClG,wBAAgB,iBAAiB,IAAI,aAAa,CASjD;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAY7D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAGtG;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAIlG","sourcesContent":["/**\n * lunR: gateway config (<agentDir>/gateway.json).\n *\n * Follows the pi-ollama-cloud load/save pattern: JSON file under the agent\n * dir (getAgentDir(), honoring PI_CODING_AGENT_DIR), defaults when missing,\n * unknown keys dropped, malformed file tolerated. Writes are atomic\n * (tmp + rename) and the file is created chmod 0600 best-effort — it may\n * hold bot tokens.\n *\n * Token resolution order: LUNR_<PLATFORM>_BOT_TOKEN env →\n * <PLATFORM>_BOT_TOKEN env → file token.\n */\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../config.ts\";\n\nexport interface PlatformConfig {\n\tenabled: boolean;\n\ttoken?: string;\n\tallowedUsers: string[];\n\tallowedChats: string[];\n\trequireMention: boolean;\n\tfreeResponseChats: string[];\n\thomeChannel?: string;\n}\n\nexport interface DiscordConfig extends PlatformConfig {\n\tignoredChannels: string[];\n\tautoThread: boolean;\n}\n\nexport interface StreamingConfig {\n\tenabled: boolean;\n\teditIntervalMs: number;\n\tbufferThreshold: number;\n}\n\nexport interface GatewayConfig {\n\towners?: { telegram: string[]; discord: string[] };\n\tprojectRoots?: string[];\n\tdefaultProject?: string;\n\ttelegram: PlatformConfig;\n\tdiscord: DiscordConfig;\n\t/** Group/channel chats get one session per user when true. */\n\tgroupSessionsPerUser: boolean;\n\t/** What to do with DMs from unauthorized users. */\n\tunauthorizedDmBehavior: \"pair\" | \"ignore\";\n\tstreaming: StreamingConfig;\n}\n\nexport function gatewayConfigPath(): string {\n\treturn join(getAgentDir(), \"gateway.json\");\n}\n\nfunction defaultPlatformConfig(requireMention: boolean): PlatformConfig {\n\treturn {\n\t\tenabled: false,\n\t\tallowedUsers: [],\n\t\tallowedChats: [],\n\t\trequireMention,\n\t\tfreeResponseChats: [],\n\t};\n}\n\nexport function defaultGatewayConfig(): GatewayConfig {\n\treturn {\n\t\towners: { telegram: [], discord: [] },\n\t\tprojectRoots: [],\n\t\tdefaultProject: undefined,\n\t\ttelegram: defaultPlatformConfig(false),\n\t\tdiscord: { ...defaultPlatformConfig(true), ignoredChannels: [], autoThread: true },\n\t\tgroupSessionsPerUser: true,\n\t\tunauthorizedDmBehavior: \"pair\",\n\t\tstreaming: { enabled: true, editIntervalMs: 800, bufferThreshold: 24 },\n\t};\n}\n\nfunction asStringArray(value: unknown): string[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\treturn value.every((v) => typeof v === \"string\") ? (value as string[]) : undefined;\n}\n\nfunction asBoolean(value: unknown): boolean | undefined {\n\treturn typeof value === \"boolean\" ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n\treturn typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction sanitizePlatform(raw: unknown, base: PlatformConfig): PlatformConfig {\n\tif (raw == null || typeof raw !== \"object\" || Array.isArray(raw)) return base;\n\tconst r = raw as Record<string, unknown>;\n\treturn {\n\t\tenabled: asBoolean(r.enabled) ?? base.enabled,\n\t\ttoken: typeof r.token === \"string\" ? r.token : base.token,\n\t\tallowedUsers: asStringArray(r.allowedUsers) ?? base.allowedUsers,\n\t\tallowedChats: asStringArray(r.allowedChats) ?? base.allowedChats,\n\t\trequireMention: asBoolean(r.requireMention) ?? base.requireMention,\n\t\tfreeResponseChats: asStringArray(r.freeResponseChats) ?? base.freeResponseChats,\n\t\thomeChannel: typeof r.homeChannel === \"string\" ? r.homeChannel : base.homeChannel,\n\t};\n}\n\nfunction sanitizeDiscord(raw: unknown, base: DiscordConfig): DiscordConfig {\n\tconst platform = sanitizePlatform(raw, base);\n\tconst r = raw != null && typeof raw === \"object\" && !Array.isArray(raw) ? (raw as Record<string, unknown>) : {};\n\treturn {\n\t\t...platform,\n\t\tignoredChannels: asStringArray(r.ignoredChannels) ?? base.ignoredChannels,\n\t\tautoThread: asBoolean(r.autoThread) ?? base.autoThread,\n\t};\n}\n\nfunction sanitizeConfig(raw: unknown): GatewayConfig {\n\tconst base = defaultGatewayConfig();\n\tif (raw == null || typeof raw !== \"object\" || Array.isArray(raw)) return base;\n\tconst r = raw as Record<string, unknown>;\n\tconst streamingRaw =\n\t\tr.streaming != null && typeof r.streaming === \"object\" && !Array.isArray(r.streaming)\n\t\t\t? (r.streaming as Record<string, unknown>)\n\t\t\t: {};\n\tconst owners = r.owners && typeof r.owners === \"object\" ? (r.owners as Record<string, unknown>) : {};\n\treturn {\n\t\towners: { telegram: asStringArray(owners.telegram) ?? [], discord: asStringArray(owners.discord) ?? [] },\n\t\tprojectRoots: asStringArray(r.projectRoots) ?? [],\n\t\tdefaultProject: typeof r.defaultProject === \"string\" ? r.defaultProject : undefined,\n\t\ttelegram: sanitizePlatform(r.telegram, base.telegram),\n\t\tdiscord: sanitizeDiscord(r.discord, base.discord),\n\t\tgroupSessionsPerUser: asBoolean(r.groupSessionsPerUser) ?? base.groupSessionsPerUser,\n\t\tunauthorizedDmBehavior: r.unauthorizedDmBehavior === \"ignore\" ? \"ignore\" : \"pair\",\n\t\tstreaming: {\n\t\t\tenabled: asBoolean(streamingRaw.enabled) ?? base.streaming.enabled,\n\t\t\teditIntervalMs: asNumber(streamingRaw.editIntervalMs) ?? base.streaming.editIntervalMs,\n\t\t\tbufferThreshold: asNumber(streamingRaw.bufferThreshold) ?? base.streaming.bufferThreshold,\n\t\t},\n\t};\n}\n\n/** Load <agentDir>/gateway.json; defaults (both platforms disabled) when missing or malformed. */\nexport function loadGatewayConfig(): GatewayConfig {\n\tconst path = gatewayConfigPath();\n\tif (!existsSync(path)) return defaultGatewayConfig();\n\ttry {\n\t\treturn sanitizeConfig(JSON.parse(readFileSync(path, \"utf-8\")));\n\t} catch (err) {\n\t\tconsole.error(`[gateway] Failed to load config from ${path}: ${err}`);\n\t\treturn defaultGatewayConfig();\n\t}\n}\n\n/** Write the whole config atomically (tmp + rename); chmod 0600 best-effort. */\nexport function saveGatewayConfig(config: GatewayConfig): void {\n\tconst path = gatewayConfigPath();\n\tconst dir = getAgentDir();\n\tif (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n\tconst tmp = `${path}.tmp-${process.pid}`;\n\twriteFileSync(tmp, `${JSON.stringify(config, null, 2)}\\n`, \"utf-8\");\n\trenameSync(tmp, path);\n\ttry {\n\t\tchmodSync(path, 0o600);\n\t} catch {\n\t\t// best-effort (Windows ACLs don't map cleanly)\n\t}\n}\n\n/**\n * Resolve a platform's bot token: LUNR_<PLATFORM>_BOT_TOKEN env →\n * <PLATFORM>_BOT_TOKEN env → config file token.\n */\nexport function resolvePlatformToken(platform: string, platformCfg: PlatformConfig): string | undefined {\n\tconst upper = platform.toUpperCase();\n\treturn process.env[`LUNR_${upper}_BOT_TOKEN`] ?? process.env[`${upper}_BOT_TOKEN`] ?? platformCfg.token;\n}\n\n/** Per-platform config lookup; undefined for platforms Phase 2a doesn't model. */\nexport function platformConfigFor(cfg: GatewayConfig, platform: string): PlatformConfig | undefined {\n\tif (platform === \"telegram\") return cfg.telegram;\n\tif (platform === \"discord\") return cfg.discord;\n\treturn undefined;\n}\n"]}
@@ -29,6 +29,7 @@ export function defaultGatewayConfig() {
29
29
  return {
30
30
  owners: { telegram: [], discord: [] },
31
31
  projectRoots: [],
32
+ defaultProject: undefined,
32
33
  telegram: defaultPlatformConfig(false),
33
34
  discord: { ...defaultPlatformConfig(true), ignoredChannels: [], autoThread: true },
34
35
  groupSessionsPerUser: true,
@@ -82,6 +83,7 @@ function sanitizeConfig(raw) {
82
83
  return {
83
84
  owners: { telegram: asStringArray(owners.telegram) ?? [], discord: asStringArray(owners.discord) ?? [] },
84
85
  projectRoots: asStringArray(r.projectRoots) ?? [],
86
+ defaultProject: typeof r.defaultProject === "string" ? r.defaultProject : undefined,
85
87
  telegram: sanitizePlatform(r.telegram, base.telegram),
86
88
  discord: sanitizeDiscord(r.discord, base.discord),
87
89
  groupSessionsPerUser: asBoolean(r.groupSessionsPerUser) ?? base.groupSessionsPerUser,
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/gateway/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACpG,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAmC3C,MAAM,UAAU,iBAAiB,GAAW;IAC3C,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,cAAc,CAAC,CAAC;AAAA,CAC3C;AAED,SAAS,qBAAqB,CAAC,cAAuB,EAAkB;IACvE,OAAO;QACN,OAAO,EAAE,KAAK;QACd,YAAY,EAAE,EAAE;QAChB,YAAY,EAAE,EAAE;QAChB,cAAc;QACd,iBAAiB,EAAE,EAAE;KACrB,CAAC;AAAA,CACF;AAED,MAAM,UAAU,oBAAoB,GAAkB;IACrD,OAAO;QACN,MAAM,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACrC,YAAY,EAAE,EAAE;QAChB,QAAQ,EAAE,qBAAqB,CAAC,KAAK,CAAC;QACtC,OAAO,EAAE,EAAE,GAAG,qBAAqB,CAAC,IAAI,CAAC,EAAE,eAAe,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;QAClF,oBAAoB,EAAE,IAAI;QAC1B,sBAAsB,EAAE,MAAM;QAC9B,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,EAAE,EAAE,EAAE;KACtE,CAAC;AAAA,CACF;AAED,SAAS,aAAa,CAAC,KAAc,EAAwB;IAC5D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5C,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAE,KAAkB,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CACnF;AAED,SAAS,SAAS,CAAC,KAAc,EAAuB;IACvD,OAAO,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CACtD;AAED,SAAS,QAAQ,CAAC,KAAc,EAAsB;IACrD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CAC/E;AAED,SAAS,gBAAgB,CAAC,GAAY,EAAE,IAAoB,EAAkB;IAC7E,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9E,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,OAAO;QACN,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO;QAC7C,KAAK,EAAE,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK;QACzD,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,YAAY;QAChE,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,YAAY;QAChE,cAAc,EAAE,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,cAAc;QAClE,iBAAiB,EAAE,aAAa,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,IAAI,CAAC,iBAAiB;QAC/E,WAAW,EAAE,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW;KACjF,CAAC;AAAA,CACF;AAED,SAAS,eAAe,CAAC,GAAY,EAAE,IAAmB,EAAiB;IAC1E,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC7C,MAAM,CAAC,GAAG,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,GAA+B,CAAC,CAAC,CAAC,EAAE,CAAC;IAChH,OAAO;QACN,GAAG,QAAQ;QACX,eAAe,EAAE,aAAa,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC,eAAe;QACzE,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,UAAU;KACtD,CAAC;AAAA,CACF;AAED,SAAS,cAAc,CAAC,GAAY,EAAiB;IACpD,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAC;IACpC,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9E,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,YAAY,GACjB,CAAC,CAAC,SAAS,IAAI,IAAI,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QACpF,CAAC,CAAE,CAAC,CAAC,SAAqC;QAC1C,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAE,CAAC,CAAC,MAAkC,CAAC,CAAC,CAAC,EAAE,CAAC;IACrG,OAAO;QACN,MAAM,EAAE,EAAE,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE;QACxG,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE;QACjD,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC;QACrD,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC;QACjD,oBAAoB,EAAE,SAAS,CAAC,CAAC,CAAC,oBAAoB,CAAC,IAAI,IAAI,CAAC,oBAAoB;QACpF,sBAAsB,EAAE,CAAC,CAAC,sBAAsB,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM;QACjF,SAAS,EAAE;YACV,OAAO,EAAE,SAAS,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO;YAClE,cAAc,EAAE,QAAQ,CAAC,YAAY,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,cAAc;YACtF,eAAe,EAAE,QAAQ,CAAC,YAAY,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,eAAe;SACzF;KACD,CAAC;AAAA,CACF;AAED,kGAAkG;AAClG,MAAM,UAAU,iBAAiB,GAAkB;IAClD,MAAM,IAAI,GAAG,iBAAiB,EAAE,CAAC;IACjC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,oBAAoB,EAAE,CAAC;IACrD,IAAI,CAAC;QACJ,OAAO,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,wCAAwC,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC;QACtE,OAAO,oBAAoB,EAAE,CAAC;IAC/B,CAAC;AAAA,CACD;AAED,gFAAgF;AAChF,MAAM,UAAU,iBAAiB,CAAC,MAAqB,EAAQ;IAC9D,MAAM,IAAI,GAAG,iBAAiB,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,MAAM,GAAG,GAAG,GAAG,IAAI,QAAQ,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,aAAa,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACpE,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACtB,IAAI,CAAC;QACJ,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACR,+CAA+C;IAChD,CAAC;AAAA,CACD;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAgB,EAAE,WAA2B,EAAsB;IACvG,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACrC,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,YAAY,CAAC,IAAI,WAAW,CAAC,KAAK,CAAC;AAAA,CACxG;AAED,kFAAkF;AAClF,MAAM,UAAU,iBAAiB,CAAC,GAAkB,EAAE,QAAgB,EAA8B;IACnG,IAAI,QAAQ,KAAK,UAAU;QAAE,OAAO,GAAG,CAAC,QAAQ,CAAC;IACjD,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC,OAAO,CAAC;IAC/C,OAAO,SAAS,CAAC;AAAA,CACjB","sourcesContent":["/**\n * lunR: gateway config (<agentDir>/gateway.json).\n *\n * Follows the pi-ollama-cloud load/save pattern: JSON file under the agent\n * dir (getAgentDir(), honoring PI_CODING_AGENT_DIR), defaults when missing,\n * unknown keys dropped, malformed file tolerated. Writes are atomic\n * (tmp + rename) and the file is created chmod 0600 best-effort — it may\n * hold bot tokens.\n *\n * Token resolution order: LUNR_<PLATFORM>_BOT_TOKEN env →\n * <PLATFORM>_BOT_TOKEN env → file token.\n */\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../config.ts\";\n\nexport interface PlatformConfig {\n\tenabled: boolean;\n\ttoken?: string;\n\tallowedUsers: string[];\n\tallowedChats: string[];\n\trequireMention: boolean;\n\tfreeResponseChats: string[];\n\thomeChannel?: string;\n}\n\nexport interface DiscordConfig extends PlatformConfig {\n\tignoredChannels: string[];\n\tautoThread: boolean;\n}\n\nexport interface StreamingConfig {\n\tenabled: boolean;\n\teditIntervalMs: number;\n\tbufferThreshold: number;\n}\n\nexport interface GatewayConfig {\n\towners?: { telegram: string[]; discord: string[] };\n\tprojectRoots?: string[];\n\ttelegram: PlatformConfig;\n\tdiscord: DiscordConfig;\n\t/** Group/channel chats get one session per user when true. */\n\tgroupSessionsPerUser: boolean;\n\t/** What to do with DMs from unauthorized users. */\n\tunauthorizedDmBehavior: \"pair\" | \"ignore\";\n\tstreaming: StreamingConfig;\n}\n\nexport function gatewayConfigPath(): string {\n\treturn join(getAgentDir(), \"gateway.json\");\n}\n\nfunction defaultPlatformConfig(requireMention: boolean): PlatformConfig {\n\treturn {\n\t\tenabled: false,\n\t\tallowedUsers: [],\n\t\tallowedChats: [],\n\t\trequireMention,\n\t\tfreeResponseChats: [],\n\t};\n}\n\nexport function defaultGatewayConfig(): GatewayConfig {\n\treturn {\n\t\towners: { telegram: [], discord: [] },\n\t\tprojectRoots: [],\n\t\ttelegram: defaultPlatformConfig(false),\n\t\tdiscord: { ...defaultPlatformConfig(true), ignoredChannels: [], autoThread: true },\n\t\tgroupSessionsPerUser: true,\n\t\tunauthorizedDmBehavior: \"pair\",\n\t\tstreaming: { enabled: true, editIntervalMs: 800, bufferThreshold: 24 },\n\t};\n}\n\nfunction asStringArray(value: unknown): string[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\treturn value.every((v) => typeof v === \"string\") ? (value as string[]) : undefined;\n}\n\nfunction asBoolean(value: unknown): boolean | undefined {\n\treturn typeof value === \"boolean\" ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n\treturn typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction sanitizePlatform(raw: unknown, base: PlatformConfig): PlatformConfig {\n\tif (raw == null || typeof raw !== \"object\" || Array.isArray(raw)) return base;\n\tconst r = raw as Record<string, unknown>;\n\treturn {\n\t\tenabled: asBoolean(r.enabled) ?? base.enabled,\n\t\ttoken: typeof r.token === \"string\" ? r.token : base.token,\n\t\tallowedUsers: asStringArray(r.allowedUsers) ?? base.allowedUsers,\n\t\tallowedChats: asStringArray(r.allowedChats) ?? base.allowedChats,\n\t\trequireMention: asBoolean(r.requireMention) ?? base.requireMention,\n\t\tfreeResponseChats: asStringArray(r.freeResponseChats) ?? base.freeResponseChats,\n\t\thomeChannel: typeof r.homeChannel === \"string\" ? r.homeChannel : base.homeChannel,\n\t};\n}\n\nfunction sanitizeDiscord(raw: unknown, base: DiscordConfig): DiscordConfig {\n\tconst platform = sanitizePlatform(raw, base);\n\tconst r = raw != null && typeof raw === \"object\" && !Array.isArray(raw) ? (raw as Record<string, unknown>) : {};\n\treturn {\n\t\t...platform,\n\t\tignoredChannels: asStringArray(r.ignoredChannels) ?? base.ignoredChannels,\n\t\tautoThread: asBoolean(r.autoThread) ?? base.autoThread,\n\t};\n}\n\nfunction sanitizeConfig(raw: unknown): GatewayConfig {\n\tconst base = defaultGatewayConfig();\n\tif (raw == null || typeof raw !== \"object\" || Array.isArray(raw)) return base;\n\tconst r = raw as Record<string, unknown>;\n\tconst streamingRaw =\n\t\tr.streaming != null && typeof r.streaming === \"object\" && !Array.isArray(r.streaming)\n\t\t\t? (r.streaming as Record<string, unknown>)\n\t\t\t: {};\n\tconst owners = r.owners && typeof r.owners === \"object\" ? (r.owners as Record<string, unknown>) : {};\n\treturn {\n\t\towners: { telegram: asStringArray(owners.telegram) ?? [], discord: asStringArray(owners.discord) ?? [] },\n\t\tprojectRoots: asStringArray(r.projectRoots) ?? [],\n\t\ttelegram: sanitizePlatform(r.telegram, base.telegram),\n\t\tdiscord: sanitizeDiscord(r.discord, base.discord),\n\t\tgroupSessionsPerUser: asBoolean(r.groupSessionsPerUser) ?? base.groupSessionsPerUser,\n\t\tunauthorizedDmBehavior: r.unauthorizedDmBehavior === \"ignore\" ? \"ignore\" : \"pair\",\n\t\tstreaming: {\n\t\t\tenabled: asBoolean(streamingRaw.enabled) ?? base.streaming.enabled,\n\t\t\teditIntervalMs: asNumber(streamingRaw.editIntervalMs) ?? base.streaming.editIntervalMs,\n\t\t\tbufferThreshold: asNumber(streamingRaw.bufferThreshold) ?? base.streaming.bufferThreshold,\n\t\t},\n\t};\n}\n\n/** Load <agentDir>/gateway.json; defaults (both platforms disabled) when missing or malformed. */\nexport function loadGatewayConfig(): GatewayConfig {\n\tconst path = gatewayConfigPath();\n\tif (!existsSync(path)) return defaultGatewayConfig();\n\ttry {\n\t\treturn sanitizeConfig(JSON.parse(readFileSync(path, \"utf-8\")));\n\t} catch (err) {\n\t\tconsole.error(`[gateway] Failed to load config from ${path}: ${err}`);\n\t\treturn defaultGatewayConfig();\n\t}\n}\n\n/** Write the whole config atomically (tmp + rename); chmod 0600 best-effort. */\nexport function saveGatewayConfig(config: GatewayConfig): void {\n\tconst path = gatewayConfigPath();\n\tconst dir = getAgentDir();\n\tif (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n\tconst tmp = `${path}.tmp-${process.pid}`;\n\twriteFileSync(tmp, `${JSON.stringify(config, null, 2)}\\n`, \"utf-8\");\n\trenameSync(tmp, path);\n\ttry {\n\t\tchmodSync(path, 0o600);\n\t} catch {\n\t\t// best-effort (Windows ACLs don't map cleanly)\n\t}\n}\n\n/**\n * Resolve a platform's bot token: LUNR_<PLATFORM>_BOT_TOKEN env →\n * <PLATFORM>_BOT_TOKEN env → config file token.\n */\nexport function resolvePlatformToken(platform: string, platformCfg: PlatformConfig): string | undefined {\n\tconst upper = platform.toUpperCase();\n\treturn process.env[`LUNR_${upper}_BOT_TOKEN`] ?? process.env[`${upper}_BOT_TOKEN`] ?? platformCfg.token;\n}\n\n/** Per-platform config lookup; undefined for platforms Phase 2a doesn't model. */\nexport function platformConfigFor(cfg: GatewayConfig, platform: string): PlatformConfig | undefined {\n\tif (platform === \"telegram\") return cfg.telegram;\n\tif (platform === \"discord\") return cfg.discord;\n\treturn undefined;\n}\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/gateway/config.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway config (<agentDir>/gateway.json).\n *\n * Follows the pi-ollama-cloud load/save pattern: JSON file under the agent\n * dir (getAgentDir(), honoring PI_CODING_AGENT_DIR), defaults when missing,\n * unknown keys dropped, malformed file tolerated. Writes are atomic\n * (tmp + rename) and the file is created chmod 0600 best-effort — it may\n * hold bot tokens.\n *\n * Token resolution order: LUNR_<PLATFORM>_BOT_TOKEN env →\n * <PLATFORM>_BOT_TOKEN env → file token.\n */\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../config.ts\";\n\nexport interface PlatformConfig {\n\tenabled: boolean;\n\ttoken?: string;\n\tallowedUsers: string[];\n\tallowedChats: string[];\n\trequireMention: boolean;\n\tfreeResponseChats: string[];\n\thomeChannel?: string;\n}\n\nexport interface DiscordConfig extends PlatformConfig {\n\tignoredChannels: string[];\n\tautoThread: boolean;\n}\n\nexport interface StreamingConfig {\n\tenabled: boolean;\n\teditIntervalMs: number;\n\tbufferThreshold: number;\n}\n\nexport interface GatewayConfig {\n\towners?: { telegram: string[]; discord: string[] };\n\tprojectRoots?: string[];\n\tdefaultProject?: string;\n\ttelegram: PlatformConfig;\n\tdiscord: DiscordConfig;\n\t/** Group/channel chats get one session per user when true. */\n\tgroupSessionsPerUser: boolean;\n\t/** What to do with DMs from unauthorized users. */\n\tunauthorizedDmBehavior: \"pair\" | \"ignore\";\n\tstreaming: StreamingConfig;\n}\n\nexport function gatewayConfigPath(): string {\n\treturn join(getAgentDir(), \"gateway.json\");\n}\n\nfunction defaultPlatformConfig(requireMention: boolean): PlatformConfig {\n\treturn {\n\t\tenabled: false,\n\t\tallowedUsers: [],\n\t\tallowedChats: [],\n\t\trequireMention,\n\t\tfreeResponseChats: [],\n\t};\n}\n\nexport function defaultGatewayConfig(): GatewayConfig {\n\treturn {\n\t\towners: { telegram: [], discord: [] },\n\t\tprojectRoots: [],\n\t\tdefaultProject: undefined,\n\t\ttelegram: defaultPlatformConfig(false),\n\t\tdiscord: { ...defaultPlatformConfig(true), ignoredChannels: [], autoThread: true },\n\t\tgroupSessionsPerUser: true,\n\t\tunauthorizedDmBehavior: \"pair\",\n\t\tstreaming: { enabled: true, editIntervalMs: 800, bufferThreshold: 24 },\n\t};\n}\n\nfunction asStringArray(value: unknown): string[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\treturn value.every((v) => typeof v === \"string\") ? (value as string[]) : undefined;\n}\n\nfunction asBoolean(value: unknown): boolean | undefined {\n\treturn typeof value === \"boolean\" ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n\treturn typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction sanitizePlatform(raw: unknown, base: PlatformConfig): PlatformConfig {\n\tif (raw == null || typeof raw !== \"object\" || Array.isArray(raw)) return base;\n\tconst r = raw as Record<string, unknown>;\n\treturn {\n\t\tenabled: asBoolean(r.enabled) ?? base.enabled,\n\t\ttoken: typeof r.token === \"string\" ? r.token : base.token,\n\t\tallowedUsers: asStringArray(r.allowedUsers) ?? base.allowedUsers,\n\t\tallowedChats: asStringArray(r.allowedChats) ?? base.allowedChats,\n\t\trequireMention: asBoolean(r.requireMention) ?? base.requireMention,\n\t\tfreeResponseChats: asStringArray(r.freeResponseChats) ?? base.freeResponseChats,\n\t\thomeChannel: typeof r.homeChannel === \"string\" ? r.homeChannel : base.homeChannel,\n\t};\n}\n\nfunction sanitizeDiscord(raw: unknown, base: DiscordConfig): DiscordConfig {\n\tconst platform = sanitizePlatform(raw, base);\n\tconst r = raw != null && typeof raw === \"object\" && !Array.isArray(raw) ? (raw as Record<string, unknown>) : {};\n\treturn {\n\t\t...platform,\n\t\tignoredChannels: asStringArray(r.ignoredChannels) ?? base.ignoredChannels,\n\t\tautoThread: asBoolean(r.autoThread) ?? base.autoThread,\n\t};\n}\n\nfunction sanitizeConfig(raw: unknown): GatewayConfig {\n\tconst base = defaultGatewayConfig();\n\tif (raw == null || typeof raw !== \"object\" || Array.isArray(raw)) return base;\n\tconst r = raw as Record<string, unknown>;\n\tconst streamingRaw =\n\t\tr.streaming != null && typeof r.streaming === \"object\" && !Array.isArray(r.streaming)\n\t\t\t? (r.streaming as Record<string, unknown>)\n\t\t\t: {};\n\tconst owners = r.owners && typeof r.owners === \"object\" ? (r.owners as Record<string, unknown>) : {};\n\treturn {\n\t\towners: { telegram: asStringArray(owners.telegram) ?? [], discord: asStringArray(owners.discord) ?? [] },\n\t\tprojectRoots: asStringArray(r.projectRoots) ?? [],\n\t\tdefaultProject: typeof r.defaultProject === \"string\" ? r.defaultProject : undefined,\n\t\ttelegram: sanitizePlatform(r.telegram, base.telegram),\n\t\tdiscord: sanitizeDiscord(r.discord, base.discord),\n\t\tgroupSessionsPerUser: asBoolean(r.groupSessionsPerUser) ?? base.groupSessionsPerUser,\n\t\tunauthorizedDmBehavior: r.unauthorizedDmBehavior === \"ignore\" ? \"ignore\" : \"pair\",\n\t\tstreaming: {\n\t\t\tenabled: asBoolean(streamingRaw.enabled) ?? base.streaming.enabled,\n\t\t\teditIntervalMs: asNumber(streamingRaw.editIntervalMs) ?? base.streaming.editIntervalMs,\n\t\t\tbufferThreshold: asNumber(streamingRaw.bufferThreshold) ?? base.streaming.bufferThreshold,\n\t\t},\n\t};\n}\n\n/** Load <agentDir>/gateway.json; defaults (both platforms disabled) when missing or malformed. */\nexport function loadGatewayConfig(): GatewayConfig {\n\tconst path = gatewayConfigPath();\n\tif (!existsSync(path)) return defaultGatewayConfig();\n\ttry {\n\t\treturn sanitizeConfig(JSON.parse(readFileSync(path, \"utf-8\")));\n\t} catch (err) {\n\t\tconsole.error(`[gateway] Failed to load config from ${path}: ${err}`);\n\t\treturn defaultGatewayConfig();\n\t}\n}\n\n/** Write the whole config atomically (tmp + rename); chmod 0600 best-effort. */\nexport function saveGatewayConfig(config: GatewayConfig): void {\n\tconst path = gatewayConfigPath();\n\tconst dir = getAgentDir();\n\tif (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n\tconst tmp = `${path}.tmp-${process.pid}`;\n\twriteFileSync(tmp, `${JSON.stringify(config, null, 2)}\\n`, \"utf-8\");\n\trenameSync(tmp, path);\n\ttry {\n\t\tchmodSync(path, 0o600);\n\t} catch {\n\t\t// best-effort (Windows ACLs don't map cleanly)\n\t}\n}\n\n/**\n * Resolve a platform's bot token: LUNR_<PLATFORM>_BOT_TOKEN env →\n * <PLATFORM>_BOT_TOKEN env → config file token.\n */\nexport function resolvePlatformToken(platform: string, platformCfg: PlatformConfig): string | undefined {\n\tconst upper = platform.toUpperCase();\n\treturn process.env[`LUNR_${upper}_BOT_TOKEN`] ?? process.env[`${upper}_BOT_TOKEN`] ?? platformCfg.token;\n}\n\n/** Per-platform config lookup; undefined for platforms Phase 2a doesn't model. */\nexport function platformConfigFor(cfg: GatewayConfig, platform: string): PlatformConfig | undefined {\n\tif (platform === \"telegram\") return cfg.telegram;\n\tif (platform === \"discord\") return cfg.discord;\n\treturn undefined;\n}\n"]}
@@ -16,7 +16,7 @@
16
16
  * "origin" (the chat that created the job, homeChannel fallback), a bare
17
17
  * platform name (that platform's homeChannel), or
18
18
  * "<platform>:<chatId>[:<threadId>]" (explicit). Content is wrapped as
19
- * "☾ Cron: <name>" and split to the adapter's maxMessageLength.
19
+ * "Cron: <name>" and split to the adapter's maxMessageLength.
20
20
  *
21
21
  * The bridge is re-installed after every cron session creation because loading
22
22
  * the builtin extensions (lunr-cron) re-registers its local-notify bridge per