@songsid/agend 2.1.12 → 2.2.0-alpha.1

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 (358) hide show
  1. package/README.md +2 -2
  2. package/README.zh-TW.md +2 -2
  3. package/dist/auth/one-time-code.d.ts +19 -0
  4. package/dist/auth/one-time-code.js +49 -0
  5. package/dist/auth/one-time-code.js.map +1 -0
  6. package/dist/auth-api.d.ts +52 -0
  7. package/dist/auth-api.js +346 -0
  8. package/dist/auth-api.js.map +1 -0
  9. package/dist/backend/claude-code.d.ts +4 -0
  10. package/dist/backend/claude-code.js +97 -0
  11. package/dist/backend/claude-code.js.map +1 -1
  12. package/dist/backend/codex.js +59 -30
  13. package/dist/backend/codex.js.map +1 -1
  14. package/dist/backend/kiro-agent.d.ts +66 -0
  15. package/dist/backend/kiro-agent.js +302 -0
  16. package/dist/backend/kiro-agent.js.map +1 -0
  17. package/dist/backend/kiro-identity.d.ts +80 -0
  18. package/dist/backend/kiro-identity.js +537 -0
  19. package/dist/backend/kiro-identity.js.map +1 -0
  20. package/dist/backend/kiro-v2-store-worker.d.ts +1 -0
  21. package/dist/backend/kiro-v2-store-worker.js +87 -0
  22. package/dist/backend/kiro-v2-store-worker.js.map +1 -0
  23. package/dist/backend/kiro-v2-store.d.ts +54 -0
  24. package/dist/backend/kiro-v2-store.js +131 -0
  25. package/dist/backend/kiro-v2-store.js.map +1 -0
  26. package/dist/backend/kiro-v3-identity.d.ts +5 -0
  27. package/dist/backend/kiro-v3-identity.js +22 -0
  28. package/dist/backend/kiro-v3-identity.js.map +1 -1
  29. package/dist/backend/kiro.d.ts +83 -7
  30. package/dist/backend/kiro.js +418 -91
  31. package/dist/backend/kiro.js.map +1 -1
  32. package/dist/backend/types.d.ts +45 -3
  33. package/dist/backend/types.js.map +1 -1
  34. package/dist/channel/adapters/discord.d.ts +1 -0
  35. package/dist/channel/adapters/discord.js +29 -10
  36. package/dist/channel/adapters/discord.js.map +1 -1
  37. package/dist/channel/adapters/telegram.d.ts +1 -0
  38. package/dist/channel/adapters/telegram.js +13 -1
  39. package/dist/channel/adapters/telegram.js.map +1 -1
  40. package/dist/channel/types.d.ts +9 -1
  41. package/dist/classic-channel-manager.d.ts +18 -10
  42. package/dist/classic-channel-manager.js +45 -18
  43. package/dist/classic-channel-manager.js.map +1 -1
  44. package/dist/cli.js +225 -127
  45. package/dist/cli.js.map +1 -1
  46. package/dist/command-table.d.ts +5 -3
  47. package/dist/command-table.js +29 -27
  48. package/dist/command-table.js.map +1 -1
  49. package/dist/config-validator.js +46 -0
  50. package/dist/config-validator.js.map +1 -1
  51. package/dist/config.js +12 -0
  52. package/dist/config.js.map +1 -1
  53. package/dist/connection-secrets.d.ts +6 -0
  54. package/dist/connection-secrets.js +4 -0
  55. package/dist/connection-secrets.js.map +1 -1
  56. package/dist/daemon.d.ts +89 -4
  57. package/dist/daemon.js +661 -77
  58. package/dist/daemon.js.map +1 -1
  59. package/dist/darwin-memory.d.ts +6 -1
  60. package/dist/darwin-memory.js +17 -6
  61. package/dist/darwin-memory.js.map +1 -1
  62. package/dist/delivery-consumed-watch.d.ts +85 -0
  63. package/dist/delivery-consumed-watch.js +244 -0
  64. package/dist/delivery-consumed-watch.js.map +1 -0
  65. package/dist/delivery-outbox.d.ts +61 -0
  66. package/dist/delivery-outbox.js +120 -2
  67. package/dist/delivery-outbox.js.map +1 -1
  68. package/dist/delivery-reconciliation.d.ts +14 -5
  69. package/dist/delivery-reconciliation.js +27 -4
  70. package/dist/delivery-reconciliation.js.map +1 -1
  71. package/dist/delivery-reconciliation.test.js +35 -1
  72. package/dist/delivery-reconciliation.test.js.map +1 -1
  73. package/dist/event-loop-watch.d.ts +28 -0
  74. package/dist/event-loop-watch.js +75 -3
  75. package/dist/event-loop-watch.js.map +1 -1
  76. package/dist/fleet-context.d.ts +22 -2
  77. package/dist/fleet-level-config.d.ts +2 -2
  78. package/dist/fleet-level-config.js +6 -1
  79. package/dist/fleet-level-config.js.map +1 -1
  80. package/dist/fleet-manager.d.ts +293 -12
  81. package/dist/fleet-manager.js +2364 -449
  82. package/dist/fleet-manager.js.map +1 -1
  83. package/dist/host-memory.d.ts +4 -0
  84. package/dist/host-memory.js.map +1 -1
  85. package/dist/instance-config-impact.js +5 -0
  86. package/dist/instance-config-impact.js.map +1 -1
  87. package/dist/instance-lifecycle.d.ts +10 -5
  88. package/dist/instance-lifecycle.js +162 -53
  89. package/dist/instance-lifecycle.js.map +1 -1
  90. package/dist/locale.js +150 -16
  91. package/dist/locale.js.map +1 -1
  92. package/dist/login-controller.d.ts +2 -0
  93. package/dist/login-controller.js +17 -5
  94. package/dist/login-controller.js.map +1 -1
  95. package/dist/memory-pressure.d.ts +7 -8
  96. package/dist/memory-pressure.js +76 -15
  97. package/dist/memory-pressure.js.map +1 -1
  98. package/dist/monotonic-budget.d.ts +2 -0
  99. package/dist/monotonic-budget.js +28 -0
  100. package/dist/monotonic-budget.js.map +1 -0
  101. package/dist/needs-you-hub.d.ts +114 -0
  102. package/dist/needs-you-hub.js +370 -0
  103. package/dist/needs-you-hub.js.map +1 -0
  104. package/dist/needs-you-live.d.ts +116 -0
  105. package/dist/needs-you-live.js +228 -0
  106. package/dist/needs-you-live.js.map +1 -0
  107. package/dist/needs-you.d.ts +132 -0
  108. package/dist/needs-you.js +164 -0
  109. package/dist/needs-you.js.map +1 -0
  110. package/dist/node-version-guard.d.ts +30 -0
  111. package/dist/node-version-guard.js +50 -0
  112. package/dist/node-version-guard.js.map +1 -0
  113. package/dist/operator-control-paths.d.ts +4 -0
  114. package/dist/operator-control-paths.js +24 -0
  115. package/dist/operator-control-paths.js.map +1 -0
  116. package/dist/outbound-handlers.d.ts +2 -0
  117. package/dist/outbound-handlers.js +9 -2
  118. package/dist/outbound-handlers.js.map +1 -1
  119. package/dist/outbound-schemas.d.ts +5 -5
  120. package/dist/profile-control.js +4 -20
  121. package/dist/profile-control.js.map +1 -1
  122. package/dist/public-web-gateway.d.ts +18 -0
  123. package/dist/public-web-gateway.js +141 -0
  124. package/dist/public-web-gateway.js.map +1 -0
  125. package/dist/public-web-link.d.ts +53 -0
  126. package/dist/public-web-link.js +175 -0
  127. package/dist/public-web-link.js.map +1 -0
  128. package/dist/quickstart-api.d.ts +8 -1
  129. package/dist/quickstart-api.js +86 -48
  130. package/dist/quickstart-api.js.map +1 -1
  131. package/dist/quickstart.d.ts +1 -0
  132. package/dist/quickstart.js +6 -2
  133. package/dist/quickstart.js.map +1 -1
  134. package/dist/scheduler/db.d.ts +7 -1
  135. package/dist/scheduler/db.js +38 -0
  136. package/dist/scheduler/db.js.map +1 -1
  137. package/dist/scheduler/index.d.ts +1 -1
  138. package/dist/scheduler/retry-label.d.ts +11 -0
  139. package/dist/scheduler/retry-label.js +24 -0
  140. package/dist/scheduler/retry-label.js.map +1 -0
  141. package/dist/scheduler/scheduler.d.ts +55 -2
  142. package/dist/scheduler/scheduler.js +220 -8
  143. package/dist/scheduler/scheduler.js.map +1 -1
  144. package/dist/scheduler/types.d.ts +22 -0
  145. package/dist/scheduler/types.js.map +1 -1
  146. package/dist/secret-store.d.ts +5 -0
  147. package/dist/secret-store.js +20 -0
  148. package/dist/secret-store.js.map +1 -1
  149. package/dist/service-activation.d.ts +110 -0
  150. package/dist/service-activation.js +302 -0
  151. package/dist/service-activation.js.map +1 -0
  152. package/dist/service-installer.d.ts +7 -0
  153. package/dist/service-installer.js +7 -4
  154. package/dist/service-installer.js.map +1 -1
  155. package/dist/service-restart-selection.d.ts +6 -5
  156. package/dist/service-restart-selection.js +6 -5
  157. package/dist/service-restart-selection.js.map +1 -1
  158. package/dist/settings-api.d.ts +10 -2
  159. package/dist/settings-api.js +175 -72
  160. package/dist/settings-api.js.map +1 -1
  161. package/dist/settings-baseline.d.ts +20 -0
  162. package/dist/settings-baseline.js +104 -0
  163. package/dist/settings-baseline.js.map +1 -0
  164. package/dist/settings-change.d.ts +21 -0
  165. package/dist/settings-change.js +133 -0
  166. package/dist/settings-change.js.map +1 -0
  167. package/dist/settings-confirmation.d.ts +105 -0
  168. package/dist/settings-confirmation.js +232 -0
  169. package/dist/settings-confirmation.js.map +1 -0
  170. package/dist/settings-control.d.ts +41 -0
  171. package/dist/settings-control.js +294 -0
  172. package/dist/settings-control.js.map +1 -0
  173. package/dist/settings-effect.d.ts +24 -0
  174. package/dist/settings-effect.js +169 -0
  175. package/dist/settings-effect.js.map +1 -0
  176. package/dist/settings-http-confirmation.d.ts +29 -0
  177. package/dist/settings-http-confirmation.js +123 -0
  178. package/dist/settings-http-confirmation.js.map +1 -0
  179. package/dist/settings-instance-patch.d.ts +4 -0
  180. package/dist/settings-instance-patch.js +26 -0
  181. package/dist/settings-instance-patch.js.map +1 -0
  182. package/dist/settings-request-capability.d.ts +16 -0
  183. package/dist/settings-request-capability.js +32 -0
  184. package/dist/settings-request-capability.js.map +1 -0
  185. package/dist/settings-transaction.d.ts +65 -0
  186. package/dist/settings-transaction.js +223 -0
  187. package/dist/settings-transaction.js.map +1 -0
  188. package/dist/setup-auth.d.ts +10 -18
  189. package/dist/setup-auth.js +14 -38
  190. package/dist/setup-auth.js.map +1 -1
  191. package/dist/setup-form.d.ts +1 -1
  192. package/dist/setup-form.js +75 -18
  193. package/dist/setup-form.js.map +1 -1
  194. package/dist/setup-guide.d.ts +13 -0
  195. package/dist/setup-guide.js +25 -0
  196. package/dist/setup-guide.js.map +1 -0
  197. package/dist/setup-host.d.ts +16 -0
  198. package/dist/setup-host.js +197 -32
  199. package/dist/setup-host.js.map +1 -1
  200. package/dist/setup-wizard.js +3 -0
  201. package/dist/setup-wizard.js.map +1 -1
  202. package/dist/slash-authz.d.ts +17 -3
  203. package/dist/slash-authz.js +5 -0
  204. package/dist/slash-authz.js.map +1 -1
  205. package/dist/spawn-gate.js +3 -3
  206. package/dist/spawn-gate.js.map +1 -1
  207. package/dist/statusline-watcher.d.ts +5 -0
  208. package/dist/statusline-watcher.js +2 -0
  209. package/dist/statusline-watcher.js.map +1 -1
  210. package/dist/steer-capability.d.ts +6 -0
  211. package/dist/steer-capability.js +8 -0
  212. package/dist/steer-capability.js.map +1 -1
  213. package/dist/sync-work-attribution.d.ts +14 -0
  214. package/dist/sync-work-attribution.js +43 -8
  215. package/dist/sync-work-attribution.js.map +1 -1
  216. package/dist/tips.js +10 -5
  217. package/dist/tips.js.map +1 -1
  218. package/dist/tmux-control.d.ts +35 -5
  219. package/dist/tmux-control.js +291 -57
  220. package/dist/tmux-control.js.map +1 -1
  221. package/dist/tmux-manager.d.ts +20 -6
  222. package/dist/tmux-manager.js +90 -47
  223. package/dist/tmux-manager.js.map +1 -1
  224. package/dist/tmux-read.d.ts +59 -0
  225. package/dist/tmux-read.js +213 -0
  226. package/dist/tmux-read.js.map +1 -0
  227. package/dist/topic-commands.d.ts +21 -10
  228. package/dist/topic-commands.js +191 -58
  229. package/dist/topic-commands.js.map +1 -1
  230. package/dist/transcript-delta-reader.js +2 -1
  231. package/dist/transcript-delta-reader.js.map +1 -1
  232. package/dist/tunnel/cloudflared-install.d.ts +2 -0
  233. package/dist/tunnel/cloudflared-install.js +1 -1
  234. package/dist/tunnel/cloudflared-install.js.map +1 -1
  235. package/dist/tunnel/purpose-lane.d.ts +14 -0
  236. package/dist/tunnel/purpose-lane.js +43 -0
  237. package/dist/tunnel/purpose-lane.js.map +1 -0
  238. package/dist/turn-reply-guard.d.ts +2 -0
  239. package/dist/turn-reply-guard.js +10 -1
  240. package/dist/turn-reply-guard.js.map +1 -1
  241. package/dist/types.d.ts +33 -0
  242. package/dist/ui/app.html +18 -0
  243. package/dist/ui/chat-render.js +478 -0
  244. package/dist/ui/chat-store.js +289 -0
  245. package/dist/ui/chat-strings.js +69 -0
  246. package/dist/ui/chat-thread.js +209 -0
  247. package/dist/ui/chat-tour.js +116 -0
  248. package/dist/ui/fleet-org.js +142 -0
  249. package/dist/ui/panel-chat.js +418 -0
  250. package/dist/ui/panel-fleet.js +413 -0
  251. package/dist/ui/panel-needs.js +202 -0
  252. package/dist/ui/panel-settings.js +750 -0
  253. package/dist/ui/preview.js +266 -0
  254. package/dist/ui/settings-apply.js +239 -0
  255. package/dist/ui/settings-confirm.js +186 -0
  256. package/dist/ui/settings-dialogs.js +546 -0
  257. package/dist/ui/settings-model.js +277 -0
  258. package/dist/ui/settings-strings.js +652 -0
  259. package/dist/ui/settings-wizard.js +140 -0
  260. package/dist/ui/shared/agend-auth.js +93 -0
  261. package/dist/ui/shared/app-ctx.js +80 -0
  262. package/dist/ui/shared/app-html.js +9 -0
  263. package/dist/ui/shared/app-i18n.js +95 -0
  264. package/dist/ui/shared/app-nav.js +73 -0
  265. package/dist/ui/shared/app-needs.js +114 -0
  266. package/dist/ui/shared/app-route.js +73 -0
  267. package/dist/ui/shared/app-session.js +66 -0
  268. package/dist/ui/shared/app-shell.js +344 -0
  269. package/dist/ui/shared/app-store.js +51 -0
  270. package/dist/ui/shared/app-stream.js +201 -0
  271. package/dist/ui/shared/app.css +676 -0
  272. package/dist/ui/shared/app.js +88 -0
  273. package/dist/ui/shared/fonts/OFL.txt +92 -0
  274. package/dist/ui/shared/fonts/README.md +21 -0
  275. package/dist/ui/shared/fonts/inter.woff2 +0 -0
  276. package/dist/ui/shared/htm.module.js +1 -0
  277. package/dist/ui/shared/panel-view.js +655 -0
  278. package/dist/ui/shared/preact-hooks.module.js +2 -0
  279. package/dist/ui/shared/preact.module.js +2 -0
  280. package/dist/ui/shared/signin.js +130 -0
  281. package/dist/ui/shared/theme.js +29 -0
  282. package/dist/ui/shared/tokens.css +153 -0
  283. package/dist/ui/shared/ui-confirm.js +43 -0
  284. package/dist/ui/shared/ui-dialog.js +45 -0
  285. package/dist/ui/shared/ui-icons.js +61 -0
  286. package/dist/ui/shared/ui-menu.js +39 -0
  287. package/dist/ui/shared/ui-states.js +20 -0
  288. package/dist/ui/shared/ui-toast.js +18 -0
  289. package/dist/ui/shared/vendor/htm.LICENSE +202 -0
  290. package/dist/ui/shared/vendor/preact.LICENSE +21 -0
  291. package/dist/ui/shared/vendor/vendor.json +39 -0
  292. package/dist/ui/shared/view-strings.js +309 -0
  293. package/dist/ui/signin.html +51 -0
  294. package/dist/update-check.d.ts +6 -2
  295. package/dist/update-check.js +12 -3
  296. package/dist/update-check.js.map +1 -1
  297. package/dist/update-install.d.ts +71 -0
  298. package/dist/update-install.js +162 -0
  299. package/dist/update-install.js.map +1 -0
  300. package/dist/upgrade-notices.d.ts +17 -0
  301. package/dist/upgrade-notices.js +93 -0
  302. package/dist/upgrade-notices.js.map +1 -0
  303. package/dist/usage/statusline-usage.d.ts +2 -0
  304. package/dist/usage/statusline-usage.js +14 -10
  305. package/dist/usage/statusline-usage.js.map +1 -1
  306. package/dist/view-api.d.ts +6 -1
  307. package/dist/view-api.js +62 -51
  308. package/dist/view-api.js.map +1 -1
  309. package/dist/web-api.d.ts +71 -14
  310. package/dist/web-api.js +492 -77
  311. package/dist/web-api.js.map +1 -1
  312. package/dist/web-auth.d.ts +71 -27
  313. package/dist/web-auth.js +169 -69
  314. package/dist/web-auth.js.map +1 -1
  315. package/dist/web-body.d.ts +3 -0
  316. package/dist/web-body.js +55 -0
  317. package/dist/web-body.js.map +1 -0
  318. package/dist/web-channel-echo.d.ts +9 -0
  319. package/dist/web-channel-echo.js +55 -0
  320. package/dist/web-channel-echo.js.map +1 -0
  321. package/dist/web-chat-history.d.ts +125 -0
  322. package/dist/web-chat-history.js +184 -0
  323. package/dist/web-chat-history.js.map +1 -0
  324. package/dist/web-continuation.d.ts +7 -0
  325. package/dist/web-continuation.js +22 -0
  326. package/dist/web-continuation.js.map +1 -0
  327. package/dist/web-host-guard.d.ts +34 -0
  328. package/dist/web-host-guard.js +57 -1
  329. package/dist/web-host-guard.js.map +1 -1
  330. package/dist/web-login.d.ts +72 -0
  331. package/dist/web-login.js +132 -0
  332. package/dist/web-login.js.map +1 -0
  333. package/dist/web-org.d.ts +55 -0
  334. package/dist/web-org.js +67 -0
  335. package/dist/web-org.js.map +1 -0
  336. package/dist/web-preview.d.ts +77 -0
  337. package/dist/web-preview.js +240 -0
  338. package/dist/web-preview.js.map +1 -0
  339. package/dist/web-request-context.d.ts +10 -0
  340. package/dist/web-request-context.js +13 -0
  341. package/dist/web-request-context.js.map +1 -0
  342. package/dist/web-session.d.ts +167 -0
  343. package/dist/web-session.js +389 -0
  344. package/dist/web-session.js.map +1 -0
  345. package/dist/web-shell-routes.d.ts +59 -0
  346. package/dist/web-shell-routes.js +96 -0
  347. package/dist/web-shell-routes.js.map +1 -0
  348. package/dist/web-upload.d.ts +183 -0
  349. package/dist/web-upload.js +468 -0
  350. package/dist/web-upload.js.map +1 -0
  351. package/dist/website-tips-renderer.d.ts +2 -2
  352. package/dist/website-tips-renderer.js +3 -3
  353. package/dist/website-tips-renderer.js.map +1 -1
  354. package/package.json +8 -5
  355. package/scripts/preinstall-guard.cjs +36 -0
  356. package/dist/ui/dashboard.html +0 -774
  357. package/dist/ui/settings.html +0 -2165
  358. package/dist/ui/view.html +0 -1139
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux-read.js","sourceRoot":"","sources":["../src/tmux-read.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAqB,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,GAAG,IAAI,CAAC;AAC/C,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AACzC,MAAM,CAAC,MAAM,uBAAuB,GAAG,KAAK,CAAC;AAC7C,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAexC,MAAM,OAAO,aAAc,SAAQ,KAAK;IAEjB;IADZ,IAAI,CAAmB;IAChC,YAAqB,IAA+D,EAAE,OAAe,EAAE,KAAe;QACpH,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QADP,SAAI,GAAJ,IAAI,CAA2D;QAElF,MAAM,IAAI,GAAI,KAAmC,EAAE,IAAI,CAAC;QACxD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAC7E,CAAC;CACF;AAED,wEAAwE;AACxE,SAAS,iBAAiB,CAAC,KAAc;IACvC,MAAM,MAAM,GAAG,KAAsE,CAAC;IACtF,MAAM,IAAI,GAAG,MAAM,EAAE,IAAI,KAAK,mCAAmC,CAAC,CAAC,CAAC,OAAO;QACzE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS;YAC5B,CAAC,CAAC,OAAO,MAAM,EAAE,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC;IACtG,OAAO,IAAI,aAAa,CAAC,IAAI,EAAE,2BAA2B,EAAE,KAAK,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAoB;IAC/C,MAAM,MAAM,GAAG,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;IACtF,IAAI,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,aAAa,CAAC,SAAS,EAAE,0BAA0B,CAAC,CAAC;IACpG,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,SAAS,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,+BAA+B,CAAC,CAAC;QAC7F,KAAK,MAAM,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;gBACrD,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,kCAAkC,EAAE,GAAG,EAAE,aAAa;aACjF,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAChB,KAAK,KAAK,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;QAC1E,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,IAAI,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;YAClD,IAAI,KAAK,CAAC,MAAM;gBAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;gBAChC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC;oBAAE,MAAM,IAAI,aAAa,CAAC,SAAS,EAAE,2BAA2B,CAAC,CAAC;gBAC1G,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACvC,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,IAAI,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,aAAa,CAAC,SAAS,EAAE,4BAA4B,CAAC,CAAC;IACrG,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AAC7C,CAAC;AAmBD,SAAS,aAAa,CAAC,KAAoB;IACzC,IAAI,CAAC;QAAC,KAAK,EAAE,IAAI,EAAE,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,6CAA6C,CAAC,CAAC;AAChF,CAAC;AAED,4FAA4F;AAC5F,MAAM,OAAO,YAAY;IAOH;IAA+B;IAN3C,IAAI,GAAc,EAAE,CAAC;IACrB,MAAM,GAAmB,IAAI,CAAC;IAC9B,QAAQ,GAAG,IAAI,GAAG,EAAW,CAAC;IAC9B,OAAO,GAAG,KAAK,CAAC;IAChB,QAAQ,GAAG,CAAC,CAAC;IAErB,YAAoB,MAAqB,EAAU,OAA6B;QAA5D,WAAM,GAAN,MAAM,CAAe;QAAU,YAAO,GAAP,OAAO,CAAsB;IAAG,CAAC;IAEpF,KAAK,KAAW,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC;IACvC,IAAI,KAAW,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAE7B,IAAI;QACF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,KAAK,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9C,IAAI,GAAG;gBAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,aAAa,CAAC,SAAS,EAAE,0BAA0B,CAAC,CAAC,CAAC;QACtF,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,QAAQ;YAAE,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC5D,CAAC;IAED,IAAI,CAAC,IAAc,EAAE,SAAiB;QACpC,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,SAAS,EAAE,0BAA0B,CAAC,CAAC,CAAC;QAClG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC;YAAE,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,SAAS,EAAE,4BAA4B,CAAC,CAAC,CAAC;QACrI,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,IAAI,qBAAqB,EAAE,CAAC;YAC7E,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,GAAG,GAAG;gBACV,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK;aAC1F,CAAC;YACb,GAAG,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC1B,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK;oBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBAC7D,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACzB,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,aAAa,CAAC,SAAS,EAAE,4BAA4B,CAAC,CAAC,CAAC;YAC/E,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACvC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACpB,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,OAAO,CAAC,GAAY;QAC1B,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC,QAAQ,IAAI,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC1G,CAAC;IAEO,MAAM,CAAC,GAAY,EAAE,KAAe,EAAE,MAAM,GAAG,EAAE;QACvD,IAAI,GAAG,CAAC,IAAI;YAAE,OAAO;QACrB,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAChB,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;QACnD,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG;YAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QAC5C,IAAI,KAAK;YAAE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;;YAAM,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAEO,IAAI;QACV,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,aAAa,CAAC,SAAS,EAAE,4BAA4B,CAAC,CAAC,CAAC;YAC7E,OAAO;QACT,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,sBAAsB;YAAE,OAAO;QAClF,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAClB,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC;QAClB,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAEO,KAAK,CAAC,GAAG,CAAC,GAAY;QAC5B,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;gBACnD,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,uBAAuB,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC;gBACrF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBAC7D,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;oBAAE,MAAM,IAAI,aAAa,CAAC,SAAS,EAAE,4BAA4B,CAAC,CAAC;gBACzF,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;gBACpC,OAAO;YACT,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,GAAG,CAAC,IAAI;oBAAE,OAAO;gBACrB,IAAI,CAAC,CAAC,KAAK,YAAY,aAAa,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;oBACpE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;oBACxB,OAAO;gBACT,CAAC;YACH,CAAC;QACH,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,aAAa,CAAC,SAAS,EAAE,4BAA4B,CAAC,CAAC,CAAC;YAC7E,OAAO;QACT,CAAC;QACD,wEAAwE;QACxE,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,sBAAsB,EAAE,CAAC;YACjD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,MAAM,OAAO,GAAG,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC;YAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACtE,IAAI,aAAa,GAAG,KAAK,CAAC;QAC1B,IAAI,aAAsB,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;YACvE,MAAM,KAAK,GAAG,eAAe,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE;gBACvE,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,mBAAmB;aACnG,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;gBACnB,mEAAmE;gBACnE,kEAAkE;gBAClE,aAAa,GAAG,KAAK,CAAC;gBACtB,IAAI,aAAa,IAAI,KAAK,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,SAAS;oBAAE,OAAO,EAAE,CAAC;gBACtE,IAAI,GAAG,CAAC,IAAI;oBAAE,OAAO;gBACrB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;oBAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,aAAa,CAAC,SAAS,EAAE,4BAA4B,CAAC,CAAC,CAAC;;oBAChG,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;YACtF,CAAC,CAAC,CAAC,CAAC;YACJ,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC;YAClB,aAAa,GAAG,IAAI,CAAC;YACrB,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC7B,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9B,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS;gBAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACxE,qEAAqE;YACrE,yEAAyE;YACzE,IAAI,CAAC,KAAK,IAAI,CAAC,aAAa,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC;gBAAE,OAAO,EAAE,CAAC;YACpE,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,aAAa,CAAC,KAAK,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,aAAa,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,SAAS;gBAAE,OAAO,EAAE,CAAC;YAC9D,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;CACF"}
@@ -7,6 +7,8 @@ export { parseContextPercent, parseTokenContextRatio } from "./context-percent.j
7
7
  export type { TokenContextRatio } from "./context-percent.js";
8
8
  type ExecutionFleetContext = FleetContext & {
9
9
  getInstanceExecutionState?(instanceName: string): "idle" | "working" | "stuck" | null;
10
+ /** The running launch's own steer capability (CliBackend.supportsSteer, #1405); undefined when it has none to give. */
11
+ instanceLaunchSupportsSteer?(instanceName: string): boolean | undefined;
10
12
  };
11
13
  /** Sanitize a directory name into a valid instance name. Keeps Unicode letters (incl. CJK). */
12
14
  export declare function sanitizeInstanceName(name: string): string;
@@ -174,6 +176,18 @@ export declare class TopicCommands {
174
176
  private ctx;
175
177
  constructor(ctx: ExecutionFleetContext);
176
178
  /** Get the adapter that should reply to a given inbound message */
179
+ /**
180
+ * #754: who may run a typed Telegram command is decided by the command table — the same rule a Discord slash
181
+ * command goes through (`decideCommand`), here with its Telegram column — before any handler runs. `msg.adapterId`
182
+ * is the topic's owning adapter (ownedCopy), so the admin it asks about is that bot's. A command the table does not
183
+ * know (`/raw`) or does not handle here (a passthrough cell) is left to the handlers below. `command` is what the
184
+ * dispatcher recognized (GENERAL_FORMS / INSTANCE_FORMS), so the table judges exactly what would run.
185
+ *
186
+ * Synchronous on purpose (#1399 review): the decision, the handler's own checks and the command's effect run in one
187
+ * stretch, so the instance cannot be rebound to another bot between the check and the act. The refusal, if any.
188
+ */
189
+ private tableRefusal;
190
+ private sendRefusal;
177
191
  private getReplyAdapter;
178
192
  /**
179
193
  * #1346: keep only the owning adapter's copy. When several bots share a
@@ -189,17 +203,13 @@ export declare class TopicCommands {
189
203
  /** Parse and dispatch commands from the General topic */
190
204
  handleGeneralCommand(msg: InboundMessage, instanceName?: string): Promise<boolean>;
191
205
  /**
192
- * Build the dashboard URL text (View / Settings / Web UI). The Settings/Web UI
193
- * URLs carry the web token; when `htmlSpoiler` is set they're wrapped in a
194
- * Telegram HTML spoiler (`<tg-spoiler>`) so the token isn't shown in the clear
195
- * in a shared topic (the caller must send with format: "html"). /view is
196
- * public, so it's never spoilered. DC uses the plain form (ephemeral reply).
206
+ * Pure, secret-free dashboard/menu copy. Issuance belongs to the private
207
+ * delivery action; rendering or previewing this text cannot retire a code.
197
208
  */
198
- getDashboardText(htmlSpoiler?: boolean): string;
209
+ getDashboardText(_htmlSpoiler?: boolean, showRemoteHint?: boolean): string;
199
210
  /**
200
- * /dashboard (TG): admin-only. Replies directly in the topic; the token-
201
- * bearing URLs are wrapped in a Telegram HTML spoiler so they aren't shown in
202
- * the clear (the adapter supports plain/HTML, not MarkdownV2's `||…||`).
211
+ * /dashboard (TG): admin-only menu in General; codes are sent privately.
212
+ * `/dashboard revoke` also closes the public link and signs browsers out.
203
213
  */
204
214
  private handleDashboardCommand;
205
215
  /** Handle /ctx or /compact in any instance topic — returns true if handled */
@@ -249,7 +259,8 @@ export declare class TopicCommands {
249
259
  * Backends whose TUI accepts a busy-pane paste as steering input,
250
260
  * live-verified: claude-code and codex buffer-then-submit at the turn
251
261
  * boundary, grok accepts it in its input line. kiro's legacy TUI swallows
252
- * the paste outright, and opencode/antigravity are unverified — for those
262
+ * the paste outright; its TUI front-ends steer, so kiro answers per launch
263
+ * (#1405). opencode/antigravity are unverified — for those
253
264
  * the user gets an honest "not supported" instead of a silent queue
254
265
  * fallback that looks like a steer but behaves like a normal message.
255
266
  */
@@ -1,4 +1,6 @@
1
1
  import { measureSyncWork } from "./sync-work-attribution.js";
2
+ import { LOOPBACK_HOST_NAMES } from "./web-host-guard.js";
3
+ import { WEB_REMOTE_DOCS_URL } from "./upgrade-notices.js";
2
4
  import { readFileSync, existsSync } from "node:fs";
3
5
  import { exec, execFileSync } from "node:child_process";
4
6
  import { promisify } from "node:util";
@@ -11,11 +13,11 @@ import { formatCents } from "./cost-guard.js";
11
13
  import { truncateDisplay, MODEL_DISPLAY_WIDTH_MAX } from "./ls-rows.js";
12
14
  import { getTmuxSocketName, getTmuxSessionName } from "./paths.js";
13
15
  import { t, getLocale } from "./locale.js";
14
- import { telegramMenu } from "./command-table.js";
16
+ import { commandSpec, decideCommand, telegramMenu } from "./command-table.js";
15
17
  import { runVisibilityCommand } from "./cross-instance-notice.js";
16
18
  import { clampContextPercent, parseContextPercent, parseTokenContextRatio, } from "./context-percent.js";
17
19
  import { isGeneralInstance } from "./general-instance.js";
18
- import { backendSupportsSteer } from "./steer-capability.js";
20
+ import { instanceSupportsSteer } from "./steer-capability.js";
19
21
  import { SYSINFO_BACKEND_IDS } from "./backend/types.js";
20
22
  import { recordInternalRequest, withOrigin } from "./fleet-control-audit.js";
21
23
  import { UPDATE_COMMAND } from "./update-check.js";
@@ -455,12 +457,89 @@ export function resolveInstanceContext(dataDir, instanceName, backend, opts) {
455
457
  refreshPaneContext(instanceName, backend);
456
458
  return hit ? { context: hit.context, tokenRatio: hit.tokenRatio } : { context: null, tokenRatio: null };
457
459
  }
460
+ const PROFILE_RE = /^\/profile(?:@\w+)?(?:\s+([\s\S]*))?$/;
461
+ const RESTART_RE = /^\/restart(?:@[A-Za-z0-9_]*)?(?:\s+(.*))?$/i;
462
+ const LEGACY_INSTALL_RE = /^\/install[-_]cli(?:@\S+)?(?:\s+([\s\S]*))?$/;
463
+ const VISIBILITY_RE = /^\/visibility(?:@\S+)?(?:\s+([\s\S]*))?$/i;
464
+ /** `/name` or `/name@bot`. */
465
+ const bare = (name) => (text) => text === `/${name}` || text.startsWith(`/${name}@`);
466
+ /** `/name`, `/name@bot`, or `/name <args>`. */
467
+ const withArgs = (name) => (text) => bare(name)(text) || text.startsWith(`/${name} `);
468
+ /**
469
+ * The typed forms each dispatcher runs, in its order. The one recognizer for both the command-table gate and the
470
+ * dispatch (#1399 review): a form no handler runs (`/STATUS`, `/status report`, `/pause one two`) is text for the
471
+ * agent, and the table never answers it either.
472
+ */
473
+ const GENERAL_FORMS = [
474
+ ["profile", (text, source) => source === "telegram" && PROFILE_RE.test(text)],
475
+ ["status", bare("status")],
476
+ ["restart", text => RESTART_RE.test(text)],
477
+ ["sysinfo", text => bare("sysinfo")(text) || text === "/sys-info" || text === "/sys_info"],
478
+ ["doctor", bare("doctor")],
479
+ ["usage", bare("usage")],
480
+ ["tips", withArgs("tips")],
481
+ ["login", withArgs("login")],
482
+ ["install-cli", text => LEGACY_INSTALL_RE.test(text)],
483
+ ["update", bare("update")],
484
+ ["dashboard", withArgs("dashboard")],
485
+ ["visibility", text => VISIBILITY_RE.test(text)],
486
+ ];
487
+ const INSTANCE_FORMS = [
488
+ ["tips", withArgs("tips")],
489
+ ["pause", text => parsePauseWakeCommand(text)?.action === "pause"],
490
+ ["wake", text => parsePauseWakeCommand(text)?.action === "wake"],
491
+ ["collab", bare("collab")],
492
+ ["effort", withArgs("effort")],
493
+ ["model", withArgs("model")],
494
+ ["compact", text => parseCompactCommand(text) !== null],
495
+ ["steer", withArgs("steer")],
496
+ ["btw", withArgs("btw")],
497
+ ["clear", bare("clear")],
498
+ ["cancel", bare("cancel")],
499
+ ["save", withArgs("save")],
500
+ ["raw", text => text === "/raw" || text.startsWith("/raw ")],
501
+ ["ctx", bare("ctx")],
502
+ ];
503
+ const typedCommand = (forms, text, source) => forms.find(([, matches]) => matches(text, source))?.[0];
504
+ /** A typed name that is another spelling of a command-table entry. */
505
+ const TELEGRAM_COMMAND_ALIASES = { "install-cli": "login" };
458
506
  export class TopicCommands {
459
507
  ctx;
460
508
  constructor(ctx) {
461
509
  this.ctx = ctx;
462
510
  }
463
511
  /** Get the adapter that should reply to a given inbound message */
512
+ /**
513
+ * #754: who may run a typed Telegram command is decided by the command table — the same rule a Discord slash
514
+ * command goes through (`decideCommand`), here with its Telegram column — before any handler runs. `msg.adapterId`
515
+ * is the topic's owning adapter (ownedCopy), so the admin it asks about is that bot's. A command the table does not
516
+ * know (`/raw`) or does not handle here (a passthrough cell) is left to the handlers below. `command` is what the
517
+ * dispatcher recognized (GENERAL_FORMS / INSTANCE_FORMS), so the table judges exactly what would run.
518
+ *
519
+ * Synchronous on purpose (#1399 review): the decision, the handler's own checks and the command's effect run in one
520
+ * stretch, so the instance cannot be rebound to another bot between the check and the act. The refusal, if any.
521
+ */
522
+ tableRefusal(msg, command, scope) {
523
+ const spec = command ? commandSpec(TELEGRAM_COMMAND_ALIASES[command] ?? command) : undefined;
524
+ if (!spec)
525
+ return null;
526
+ const fleetAdmin = () => this.ctx.isFleetAdmin(msg.userId, msg.adapterId) ? "ok"
527
+ : this.ctx.hasFleetAdmins && !this.ctx.hasFleetAdmins(msg.adapterId) ? "disabled" : "denied";
528
+ const decision = decideCommand(spec, scope, {
529
+ fleetAdmin,
530
+ // In a fleet topic the channel's own admin IS the owning bot's fleet admin.
531
+ channelAdmin: () => fleetAdmin() === "ok",
532
+ classicAdmin: () => false,
533
+ }, "telegram");
534
+ if (decision.allow || "passthrough" in decision)
535
+ return null;
536
+ return decision.reply;
537
+ }
538
+ async sendRefusal(msg, [key, ...args]) {
539
+ const adapter = this.getReplyAdapter(msg);
540
+ if (adapter)
541
+ await adapter.sendText(msg.chatId, t(key, ...args), { threadId: msg.threadId }).catch(() => { });
542
+ }
464
543
  getReplyAdapter(msg) {
465
544
  if (msg.adapterId && this.ctx.adapters) {
466
545
  return this.ctx.adapters.get(msg.adapterId) ?? this.ctx.adapter;
@@ -495,47 +574,50 @@ export class TopicCommands {
495
574
  const text = msg.text?.trim();
496
575
  if (!text)
497
576
  return false;
498
- const profile = msg.source === "telegram" ? text.match(/^\/profile(?:@\w+)?(?:\s+([\s\S]*))?$/) : null;
499
- if (profile) {
500
- await this.ctx.runProfileCommand?.(msg, profile[1]?.trim());
577
+ const command = typedCommand(GENERAL_FORMS, text, msg.source);
578
+ const refusal = this.tableRefusal(msg, command, "general");
579
+ if (refusal) {
580
+ await this.sendRefusal(msg, refusal);
501
581
  return true;
502
582
  }
503
- if (text === "/status" || text === "/status@" || text.startsWith("/status@")) {
583
+ if (command === "profile") {
584
+ await this.ctx.runProfileCommand?.(msg, text.match(PROFILE_RE)[1]?.trim());
585
+ return true;
586
+ }
587
+ if (command === "status") {
504
588
  await this.handleStatusCommand(msg);
505
589
  return true;
506
590
  }
507
- const restart = text.match(/^\/restart(?:@[A-Za-z0-9_]*)?(?:\s+(.*))?$/i);
508
- if (restart) {
509
- const mode = restart[1]?.trim().toLowerCase();
591
+ if (command === "restart") {
592
+ const mode = text.match(RESTART_RE)[1]?.trim().toLowerCase();
510
593
  await this.handleRestartCommand(msg, mode);
511
594
  return true;
512
595
  }
513
- if (text === "/sysinfo" || text === "/sysinfo@" || text.startsWith("/sysinfo@")
514
- || text === "/sys-info" || text === "/sys_info") {
596
+ if (command === "sysinfo") {
515
597
  await this.handleSysInfoCommand(msg);
516
598
  return true;
517
599
  }
518
- if (text === "/doctor" || text.startsWith("/doctor@")) {
600
+ if (command === "doctor") {
519
601
  await this.handleDoctorCommand(msg);
520
602
  return true;
521
603
  }
522
- if (text === "/usage" || text.startsWith("/usage@")) {
604
+ if (command === "usage") {
523
605
  await this.handleUsageCommand(msg);
524
606
  return true;
525
607
  }
526
- if (text === "/tips" || text.startsWith("/tips ") || text.startsWith("/tips@")) {
608
+ if (command === "tips") {
527
609
  await this.handleTipsCommand(msg);
528
610
  return true;
529
611
  }
530
- if (text === "/login" || text.startsWith("/login ") || text.startsWith("/login@")) {
612
+ if (command === "login") {
531
613
  await this.handleLoginCommand(msg);
532
614
  return true;
533
615
  }
534
616
  // `/install-cli` became part of `/login` (#1131). Typed, it still works for
535
617
  // one release (2.1.10) — with a line saying where it went — and is in no
536
618
  // command menu. Both spellings: Telegram command names cannot contain "-".
537
- const legacyInstall = text.match(/^\/install[-_]cli(?:@\S+)?(?:\s+([\s\S]*))?$/);
538
- if (legacyInstall) {
619
+ if (command === "install-cli") {
620
+ const legacyInstall = text.match(LEGACY_INSTALL_RE);
539
621
  const adapter = this.getReplyAdapter(msg);
540
622
  if (adapter)
541
623
  await adapter.sendText(msg.chatId, t("login.install_cli_moved"), { threadId: msg.threadId }).catch(() => { });
@@ -544,50 +626,48 @@ export class TopicCommands {
544
626
  await this.handleLoginCommand({ ...msg, text: mapped });
545
627
  return true;
546
628
  }
547
- if (text === "/update" || text.startsWith("/update@")) {
629
+ if (command === "update") {
548
630
  await this.handleUpdateCommand(msg);
549
631
  return true;
550
632
  }
551
- if (text === "/dashboard" || text.startsWith("/dashboard@")) {
633
+ if (command === "dashboard") {
552
634
  await this.handleDashboardCommand(msg);
553
635
  return true;
554
636
  }
555
- const visibility = text.match(/^\/visibility(?:@\S+)?(?:\s+([\s\S]*))?$/i);
556
- if (visibility) {
557
- await this.handleVisibilityCommand(msg, visibility[1] ?? "");
637
+ if (command === "visibility") {
638
+ await this.handleVisibilityCommand(msg, text.match(VISIBILITY_RE)[1] ?? "");
558
639
  return true;
559
640
  }
560
641
  return false;
561
642
  }
562
643
  /**
563
- * Build the dashboard URL text (View / Settings / Web UI). The Settings/Web UI
564
- * URLs carry the web token; when `htmlSpoiler` is set they're wrapped in a
565
- * Telegram HTML spoiler (`<tg-spoiler>`) so the token isn't shown in the clear
566
- * in a shared topic (the caller must send with format: "html"). /view is
567
- * public, so it's never spoilered. DC uses the plain form (ephemeral reply).
644
+ * Pure, secret-free dashboard/menu copy. Issuance belongs to the private
645
+ * delivery action; rendering or previewing this text cannot retire a code.
568
646
  */
569
- getDashboardText(htmlSpoiler = false) {
647
+ getDashboardText(_htmlSpoiler = false, showRemoteHint = true) {
570
648
  const port = this.ctx.fleetConfig?.health_port ?? 19280;
571
649
  const host = this.ctx.fleetConfig?.hostname || "localhost";
572
650
  const access = this.ctx.getDashboardAccess?.();
573
651
  if (!access?.ready || !access.token)
574
652
  return t("dashboard.starting");
575
- const token = access.token;
576
653
  const base = `http://${host}:${port}`;
577
- const hide = (u) => htmlSpoiler ? `<tg-spoiler>${u}</tg-spoiler>` : u;
578
654
  return [
579
655
  t("dashboard.title"),
580
656
  "",
581
- `• View: ${base}/view`,
582
- `• View (edit): ${hide(`${base}/view?token=${token}`)}`,
583
- `• Settings: ${hide(`${base}/settings?token=${token}`)}`,
584
- `• Web UI: ${hide(`${base}/ui?token=${token}`)}`,
657
+ t("dashboard.signin", base),
658
+ "",
659
+ `• View: ${base}/view`,
660
+ `• Dashboard: ${base}/ui`,
661
+ `• Settings: ${base}/settings`,
662
+ "",
663
+ t("dashboard.code_help"),
664
+ // #1366: a loopback address does not open on a phone — say where the way in is documented.
665
+ ...(showRemoteHint && (LOOPBACK_HOST_NAMES.includes(host.toLowerCase()) || host === "::1") ? ["", t("dashboard.remote_hint", WEB_REMOTE_DOCS_URL)] : []),
585
666
  ].join("\n");
586
667
  }
587
668
  /**
588
- * /dashboard (TG): admin-only. Replies directly in the topic; the token-
589
- * bearing URLs are wrapped in a Telegram HTML spoiler so they aren't shown in
590
- * the clear (the adapter supports plain/HTML, not MarkdownV2's `||…||`).
669
+ * /dashboard (TG): admin-only menu in General; codes are sent privately.
670
+ * `/dashboard revoke` also closes the public link and signs browsers out.
591
671
  */
592
672
  async handleDashboardCommand(msg) {
593
673
  const adapter = this.getReplyAdapter(msg);
@@ -604,7 +684,17 @@ export class TopicCommands {
604
684
  await adapter.sendText(chatId, t("not_authorized"), { threadId });
605
685
  return;
606
686
  }
607
- await adapter.sendText(chatId, this.getDashboardText(true), { threadId, format: "html" });
687
+ const arg = (msg.text ?? "").trim().replace(/^\/dashboard(?:@\S+)?/i, "").trim().toLowerCase();
688
+ if (arg === "revoke") {
689
+ const result = this.ctx.revokeWebSessions?.() ?? { count: 0, durable: true };
690
+ // Not durable: they are signed out now, but a restart may bring them back — say so, never "done".
691
+ await adapter.sendText(chatId, result.durable ? t("dashboard.revoked", result.count) : t("dashboard.revoked_not_durable", result.count), { threadId });
692
+ return;
693
+ }
694
+ if (msg.source === "telegram")
695
+ await this.ctx.dashboardMenu?.(msg);
696
+ else
697
+ await adapter.sendText(chatId, this.getDashboardText(), { threadId });
608
698
  }
609
699
  /** Handle /ctx or /compact in any instance topic — returns true if handled */
610
700
  async handleInstanceCommand(msg, instanceName) {
@@ -615,15 +705,25 @@ export class TopicCommands {
615
705
  const text = msg.text?.trim();
616
706
  if (!text)
617
707
  return false;
708
+ const inGeneral = !!this.ctx.fleetConfig?.instances[instanceName]?.general_topic;
709
+ // A General command typed in an instance topic is recognized too, in its General forms only: the table points it
710
+ // to General (#1148) instead of handing it to the agent. No branch below runs it.
711
+ const command = typedCommand(INSTANCE_FORMS, text, msg.source)
712
+ ?? (inGeneral ? undefined : typedCommand(GENERAL_FORMS, text, msg.source));
713
+ const refusal = this.tableRefusal(msg, command, inGeneral ? "general" : "fleet");
714
+ if (refusal) {
715
+ await this.sendRefusal(msg, refusal);
716
+ return true;
717
+ }
618
718
  // Tips are informational and should appear where requested, including a
619
719
  // worker topic. This also keeps Telegram text commands aligned with
620
720
  // Discord's channel-local slash-command behavior.
621
- if (text === "/tips" || text.startsWith("/tips ") || text.startsWith("/tips@")) {
721
+ if (command === "tips") {
622
722
  await this.handleTipsCommand(msg);
623
723
  return true;
624
724
  }
625
- const pauseWake = parsePauseWakeCommand(text);
626
- if (pauseWake) {
725
+ if (command === "pause" || command === "wake") {
726
+ const pauseWake = parsePauseWakeCommand(text);
627
727
  const adapter = this.getReplyAdapter(msg);
628
728
  if (!adapter)
629
729
  return false;
@@ -631,7 +731,7 @@ export class TopicCommands {
631
731
  await adapter.sendText(msg.chatId, t("permission.denied"), { threadId: msg.threadId });
632
732
  return true;
633
733
  }
634
- const isGeneral = !!this.ctx.fleetConfig?.instances[instanceName]?.general_topic;
734
+ const isGeneral = inGeneral;
635
735
  if (isGeneral && !pauseWake.instance) {
636
736
  await adapter.sendText(msg.chatId, t(`${pauseWake.action}.usage`), { threadId: msg.threadId });
637
737
  return true;
@@ -641,6 +741,11 @@ export class TopicCommands {
641
741
  await adapter.sendText(msg.chatId, t("instance.not_found", target), { threadId: msg.threadId });
642
742
  return true;
643
743
  }
744
+ // #754 audit: a General speaks for its own bot's instances only — an admin of this bot is not one of another's.
745
+ if (isGeneral && this.ctx.getInstanceAdapterId && this.ctx.getInstanceAdapterId(target) !== msg.adapterId) {
746
+ await adapter.sendText(msg.chatId, t("instance.other_bot", target), { threadId: msg.threadId });
747
+ return true;
748
+ }
644
749
  if (pauseWake.action === "pause" && isGeneralInstance(this.ctx.fleetConfig, target)) {
645
750
  await adapter.sendText(msg.chatId, t("general.pause_forbidden"), { threadId: msg.threadId });
646
751
  return true;
@@ -648,17 +753,22 @@ export class TopicCommands {
648
753
  await adapter.sendText(msg.chatId, await this.runPauseWake(target, pauseWake.action), { threadId: msg.threadId });
649
754
  return true;
650
755
  }
651
- if (text === "/collab" || text.startsWith("/collab@")) {
756
+ if (command === "collab") {
652
757
  const adapter = this.getReplyAdapter(msg);
653
758
  if (!adapter)
654
759
  return false;
760
+ // Channel-admin, as the Discord slash command (#754 audit): it changes how the instance is reached.
761
+ if (!this.ctx.isFleetAdmin(msg.userId, msg.adapterId)) {
762
+ await adapter.sendText(msg.chatId, t("permission.denied"), { threadId: msg.threadId });
763
+ return true;
764
+ }
655
765
  const isCollab = this.ctx.toggleFleetCollab(instanceName);
656
766
  await adapter.sendText(msg.chatId, isCollab
657
767
  ? t("collab.on")
658
768
  : t("collab.off"), { threadId: msg.threadId });
659
769
  return true;
660
770
  }
661
- if (text === "/effort" || text.startsWith("/effort ") || text.startsWith("/effort@")) {
771
+ if (command === "effort") {
662
772
  const adapter = this.getReplyAdapter(msg);
663
773
  if (!adapter)
664
774
  return false;
@@ -674,13 +784,13 @@ export class TopicCommands {
674
784
  }
675
785
  else if (this.ctx.promptEffortMenu) {
676
786
  // No arg → inline keyboard menu (TG), same shape as /model.
677
- const fallback = await this.ctx.promptEffortMenu(instanceName, msg.userId, msg.threadId ?? msg.chatId, adapter, msg.chatId, msg.threadId);
787
+ const fallback = await this.ctx.promptEffortMenu(instanceName, msg.userId, msg.threadId ?? msg.chatId, adapter, msg.chatId, msg.threadId, msg.adapterId);
678
788
  if (fallback)
679
789
  await adapter.sendText(msg.chatId, fallback, { threadId: msg.threadId });
680
790
  }
681
791
  return true;
682
792
  }
683
- if (text === "/model" || text.startsWith("/model ") || text.startsWith("/model@")) {
793
+ if (command === "model") {
684
794
  const adapter = this.getReplyAdapter(msg);
685
795
  if (!adapter)
686
796
  return false;
@@ -695,7 +805,7 @@ export class TopicCommands {
695
805
  }
696
806
  else if (this.ctx.promptModelMenu) {
697
807
  // No arg → inline keyboard menu (TG)
698
- const fallback = await this.ctx.promptModelMenu(instanceName, msg.userId, msg.threadId ?? msg.chatId, adapter, msg.chatId, msg.threadId);
808
+ const fallback = await this.ctx.promptModelMenu(instanceName, msg.userId, msg.threadId ?? msg.chatId, adapter, msg.chatId, msg.threadId, msg.adapterId);
699
809
  if (fallback)
700
810
  await adapter.sendText(msg.chatId, fallback, { threadId: msg.threadId });
701
811
  }
@@ -704,16 +814,21 @@ export class TopicCommands {
704
814
  }
705
815
  return true;
706
816
  }
707
- const compact = parseCompactCommand(text);
708
- if (compact) {
817
+ if (command === "compact") {
818
+ const compact = parseCompactCommand(text);
709
819
  const adapter = this.getReplyAdapter(msg);
710
820
  if (!adapter)
711
821
  return false;
822
+ // Channel-admin, as the Discord slash command (#754 audit): it rewrites the instance's context.
823
+ if (!this.ctx.isFleetAdmin(msg.userId, msg.adapterId)) {
824
+ await adapter.sendText(msg.chatId, t("permission.denied"), { threadId: msg.threadId });
825
+ return true;
826
+ }
712
827
  const result = await this.sendCompact(instanceName, compact.instructions);
713
828
  await adapter.sendText(msg.chatId, result, { threadId: msg.threadId });
714
829
  return true;
715
830
  }
716
- if (text === "/steer" || text.startsWith("/steer ") || text.startsWith("/steer@")) {
831
+ if (command === "steer") {
717
832
  const adapter = this.getReplyAdapter(msg);
718
833
  if (!adapter)
719
834
  return false;
@@ -730,7 +845,7 @@ export class TopicCommands {
730
845
  await adapter.sendText(msg.chatId, result, { threadId: msg.threadId });
731
846
  return true;
732
847
  }
733
- if (text === "/btw" || text.startsWith("/btw ") || text.startsWith("/btw@")) {
848
+ if (command === "btw") {
734
849
  const adapter = this.getReplyAdapter(msg);
735
850
  if (!adapter)
736
851
  return false;
@@ -746,7 +861,7 @@ export class TopicCommands {
746
861
  await adapter.sendText(msg.chatId, result, { threadId: msg.threadId });
747
862
  return true;
748
863
  }
749
- if (text === "/clear" || text.startsWith("/clear@")) {
864
+ if (command === "clear") {
750
865
  const adapter = this.getReplyAdapter(msg);
751
866
  if (!adapter)
752
867
  return false;
@@ -763,7 +878,7 @@ export class TopicCommands {
763
878
  await adapter.sendText(msg.chatId, fallback, { threadId: msg.threadId });
764
879
  return true;
765
880
  }
766
- if (text === "/cancel" || text.startsWith("/cancel@")) {
881
+ if (command === "cancel") {
767
882
  const adapter = this.getReplyAdapter(msg);
768
883
  if (!adapter)
769
884
  return false;
@@ -771,10 +886,15 @@ export class TopicCommands {
771
886
  await adapter.sendText(msg.chatId, ok ? t("cancel.sent", instanceName) : t("cancel.not_running", instanceName), { threadId: msg.threadId });
772
887
  return true;
773
888
  }
774
- if (text === "/save" || text.startsWith("/save ") || text.startsWith("/save@")) {
889
+ if (command === "save") {
775
890
  const adapter = this.getReplyAdapter(msg);
776
891
  if (!adapter)
777
892
  return false;
893
+ // Channel-admin, as the Discord slash command (#754 audit): it writes a file in the instance's directory.
894
+ if (!this.ctx.isFleetAdmin(msg.userId, msg.adapterId)) {
895
+ await adapter.sendText(msg.chatId, t("permission.denied"), { threadId: msg.threadId });
896
+ return true;
897
+ }
778
898
  const filename = parseSaveFilename(text);
779
899
  if (!filename) {
780
900
  await adapter.sendText(msg.chatId, t("save.usage"), { threadId: msg.threadId });
@@ -788,7 +908,17 @@ export class TopicCommands {
788
908
  await adapter.sendText(msg.chatId, result, { threadId: msg.threadId });
789
909
  return true;
790
910
  }
791
- if (text !== "/ctx" && !text.startsWith("/ctx@"))
911
+ // `/raw <text>` is pasted into the CLI as typed, with no [user:] envelope (daemon.ts): it is CLI input, so only
912
+ // the owning bot's fleet admin may send it (#754 audit). An admin's falls through to delivery unchanged.
913
+ if (command === "raw") {
914
+ if (this.ctx.isFleetAdmin(msg.userId, msg.adapterId))
915
+ return false;
916
+ const adapter = this.getReplyAdapter(msg);
917
+ if (adapter)
918
+ await adapter.sendText(msg.chatId, t("permission.denied"), { threadId: msg.threadId });
919
+ return true;
920
+ }
921
+ if (command !== "ctx")
792
922
  return false;
793
923
  const adapter = this.getReplyAdapter(msg);
794
924
  if (!adapter)
@@ -946,14 +1076,17 @@ export class TopicCommands {
946
1076
  * Backends whose TUI accepts a busy-pane paste as steering input,
947
1077
  * live-verified: claude-code and codex buffer-then-submit at the turn
948
1078
  * boundary, grok accepts it in its input line. kiro's legacy TUI swallows
949
- * the paste outright, and opencode/antigravity are unverified — for those
1079
+ * the paste outright; its TUI front-ends steer, so kiro answers per launch
1080
+ * (#1405). opencode/antigravity are unverified — for those
950
1081
  * the user gets an honest "not supported" instead of a silent queue
951
1082
  * fallback that looks like a steer but behaves like a normal message.
952
1083
  */
953
1084
  sendSteer(instanceName, content, msg) {
954
1085
  const backend = this.effectiveBackend(instanceName);
955
- if (!backendSupportsSteer(backend)) {
956
- return t("steer.unsupported", backend);
1086
+ const launchSupportsSteer = this.ctx.instanceLaunchSupportsSteer?.(instanceName);
1087
+ if (!instanceSupportsSteer(backend, launchSupportsSteer)) {
1088
+ // A backend that answers per launch (kiro, #1405) can steer, just not as this instance runs now.
1089
+ return launchSupportsSteer === false ? t("steer.unsupported_launch", backend) : t("steer.unsupported", backend);
957
1090
  }
958
1091
  const ipc = this.ctx.instanceIpcClients.get(instanceName);
959
1092
  if (!ipc?.connected)