@poolzin/pool-bot 2026.2.25 → 2026.2.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (506) hide show
  1. package/dist/acp/event-mapper.js +87 -22
  2. package/dist/acp/meta.js +12 -6
  3. package/dist/agents/agent-paths.js +8 -9
  4. package/dist/agents/agent-scope.js +7 -5
  5. package/dist/agents/auth-profiles/oauth.js +148 -64
  6. package/dist/agents/auth-profiles/session-override.js +13 -7
  7. package/dist/agents/bash-tools.exec-host-gateway.js +14 -4
  8. package/dist/agents/bash-tools.exec-runtime.js +2 -25
  9. package/dist/agents/bedrock-discovery.js +3 -1
  10. package/dist/agents/byteplus-models.js +97 -0
  11. package/dist/agents/chutes-oauth.js +1 -0
  12. package/dist/agents/cli-runner/helpers.js +4 -0
  13. package/dist/agents/compaction.js +41 -14
  14. package/dist/agents/doubao-models.js +121 -0
  15. package/dist/agents/failover-error.js +2 -0
  16. package/dist/agents/huggingface-models.js +5 -3
  17. package/dist/agents/live-model-filter.js +5 -0
  18. package/dist/agents/minimax-vlm.js +10 -8
  19. package/dist/agents/model-auth.js +6 -0
  20. package/dist/agents/model-catalog.js +3 -1
  21. package/dist/agents/model-selection.js +7 -1
  22. package/dist/agents/models-config.providers.js +93 -11
  23. package/dist/agents/ollama-stream.js +117 -4
  24. package/dist/agents/opencode-zen-models.js +22 -11
  25. package/dist/agents/pi-embedded-helpers/errors.js +55 -33
  26. package/dist/agents/pi-embedded-helpers/messaging-dedupe.js +10 -5
  27. package/dist/agents/pi-embedded-helpers/thinking.js +10 -5
  28. package/dist/agents/pi-embedded-helpers.js +1 -1
  29. package/dist/agents/pi-embedded-runner/compact.js +29 -7
  30. package/dist/agents/pi-embedded-runner/extensions.js +28 -26
  31. package/dist/agents/pi-embedded-runner/google.js +20 -8
  32. package/dist/agents/pi-embedded-runner/run/attempt.js +95 -36
  33. package/dist/agents/pi-embedded-runner/run.js +71 -12
  34. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +11 -2
  35. package/dist/agents/pi-embedded-runner/session-manager-cache.js +11 -7
  36. package/dist/agents/pi-embedded-runner/system-prompt.js +2 -0
  37. package/dist/agents/pi-embedded-runner/thinking.js +42 -0
  38. package/dist/agents/pi-embedded-runner/tool-name-allowlist.js +19 -0
  39. package/dist/agents/pi-embedded-runner/utils.js +7 -10
  40. package/dist/agents/pi-embedded-subscribe.handlers.lifecycle.js +45 -56
  41. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +2 -2
  42. package/dist/agents/pi-embedded-subscribe.js +9 -4
  43. package/dist/agents/pi-embedded-subscribe.tools.js +68 -14
  44. package/dist/agents/pi-embedded-utils.js +3 -0
  45. package/dist/agents/pi-extensions/compaction-safeguard-runtime.js +4 -20
  46. package/dist/agents/pi-extensions/compaction-safeguard.js +75 -33
  47. package/dist/agents/pi-settings.js +40 -0
  48. package/dist/agents/pi-tools.policy.js +2 -1
  49. package/dist/agents/provider/config-loader.js +1 -1
  50. package/dist/agents/sandbox/browser.js +170 -33
  51. package/dist/agents/sandbox/config-hash.js +14 -27
  52. package/dist/agents/sandbox/config.js +21 -2
  53. package/dist/agents/sandbox/constants.js +2 -0
  54. package/dist/agents/sandbox/docker.js +16 -2
  55. package/dist/agents/sandbox/novnc-auth.js +62 -0
  56. package/dist/agents/sandbox/sanitize-env-vars.js +1 -1
  57. package/dist/agents/sandbox/shared.js +10 -6
  58. package/dist/agents/sandbox-paths.js +24 -11
  59. package/dist/agents/schema/clean-for-gemini.js +132 -85
  60. package/dist/agents/session-slug.js +10 -5
  61. package/dist/agents/session-tool-result-guard-wrapper.js +1 -0
  62. package/dist/agents/session-tool-result-guard.js +3 -1
  63. package/dist/agents/session-transcript-repair.js +40 -6
  64. package/dist/agents/skills/bundled-dir.js +19 -5
  65. package/dist/agents/skills/env-overrides.js +124 -43
  66. package/dist/agents/skills/frontmatter.js +6 -6
  67. package/dist/agents/skills/plugin-skills.js +14 -7
  68. package/dist/agents/skills/workspace.js +1 -0
  69. package/dist/agents/subagent-announce.js +251 -49
  70. package/dist/agents/subagent-lifecycle-events.js +19 -0
  71. package/dist/agents/subagent-registry-cleanup.js +31 -0
  72. package/dist/agents/subagent-registry-completion.js +68 -0
  73. package/dist/agents/subagent-registry-queries.js +117 -0
  74. package/dist/agents/subagent-registry-state.js +46 -0
  75. package/dist/agents/subagent-registry.js +252 -221
  76. package/dist/agents/subagent-registry.store.js +1 -0
  77. package/dist/agents/subagent-registry.types.js +1 -0
  78. package/dist/agents/subagent-spawn.js +195 -7
  79. package/dist/agents/system-prompt.js +22 -6
  80. package/dist/agents/test-helpers/fast-coding-tools.js +1 -18
  81. package/dist/agents/test-helpers/fast-core-tools.js +1 -17
  82. package/dist/agents/timeout.js +18 -6
  83. package/dist/agents/tool-call-id.js +1 -1
  84. package/dist/agents/tool-display-common.js +162 -29
  85. package/dist/agents/tool-images.js +82 -9
  86. package/dist/agents/tool-policy.js +51 -26
  87. package/dist/agents/tools/browser-tool.js +2 -2
  88. package/dist/agents/tools/canvas-tool.js +27 -1
  89. package/dist/agents/tools/common.js +45 -0
  90. package/dist/agents/tools/discord-actions-guild.js +4 -1
  91. package/dist/agents/tools/gateway-tool.js +3 -1
  92. package/dist/agents/tools/nodes-utils.js +1 -10
  93. package/dist/agents/tools/sessions-send-helpers.js +12 -6
  94. package/dist/agents/tools/sessions-spawn-tool.js +8 -2
  95. package/dist/agents/tools/subagents-tool.js +2 -1
  96. package/dist/agents/tools/whatsapp-actions.js +10 -2
  97. package/dist/agents/tools/whatsapp-target-auth.js +18 -0
  98. package/dist/agents/transcript-policy.js +22 -8
  99. package/dist/agents/venice-models.js +11 -3
  100. package/dist/auto-reply/commands-registry.data.js +51 -0
  101. package/dist/auto-reply/commands-registry.js +4 -3
  102. package/dist/auto-reply/group-activation.js +10 -5
  103. package/dist/auto-reply/inbound-debounce.js +10 -5
  104. package/dist/auto-reply/reply/abort.js +1 -1
  105. package/dist/auto-reply/reply/agent-runner-execution.js +4 -1
  106. package/dist/auto-reply/reply/bash-command.js +41 -39
  107. package/dist/auto-reply/reply/command-gates.js +25 -0
  108. package/dist/auto-reply/reply/commands-allowlist.js +111 -72
  109. package/dist/auto-reply/reply/commands-bash.js +6 -5
  110. package/dist/auto-reply/reply/commands-config.js +30 -28
  111. package/dist/auto-reply/reply/commands-core.js +2 -1
  112. package/dist/auto-reply/reply/commands-info.js +1 -0
  113. package/dist/auto-reply/reply/commands-models.js +65 -14
  114. package/dist/auto-reply/reply/commands-session.js +237 -82
  115. package/dist/auto-reply/reply/commands-setunset.js +45 -0
  116. package/dist/auto-reply/reply/commands-subagents/action-agents.js +44 -0
  117. package/dist/auto-reply/reply/commands-subagents/action-focus.js +64 -0
  118. package/dist/auto-reply/reply/commands-subagents/action-help.js +4 -0
  119. package/dist/auto-reply/reply/commands-subagents/action-info.js +45 -0
  120. package/dist/auto-reply/reply/commands-subagents/action-kill.js +60 -0
  121. package/dist/auto-reply/reply/commands-subagents/action-list.js +44 -0
  122. package/dist/auto-reply/reply/commands-subagents/action-log.js +29 -0
  123. package/dist/auto-reply/reply/commands-subagents/action-send.js +119 -0
  124. package/dist/auto-reply/reply/commands-subagents/action-spawn.js +52 -0
  125. package/dist/auto-reply/reply/commands-subagents/action-unfocus.js +30 -0
  126. package/dist/auto-reply/reply/commands-subagents/shared.js +303 -0
  127. package/dist/auto-reply/reply/commands-subagents.js +51 -587
  128. package/dist/auto-reply/reply/commands-tts.js +10 -5
  129. package/dist/auto-reply/reply/config-value.js +10 -5
  130. package/dist/auto-reply/reply/directive-handling.model-picker.js +12 -6
  131. package/dist/auto-reply/reply/directive-handling.persist.js +9 -21
  132. package/dist/auto-reply/reply/directive-handling.shared.js +24 -4
  133. package/dist/auto-reply/reply/followup-runner.js +1 -0
  134. package/dist/auto-reply/reply/get-reply-directives-utils.js +23 -14
  135. package/dist/auto-reply/reply/get-reply-directives.js +17 -28
  136. package/dist/auto-reply/reply/get-reply-inline-actions.js +1 -0
  137. package/dist/auto-reply/reply/get-reply.js +71 -12
  138. package/dist/auto-reply/reply/model-selection.js +80 -39
  139. package/dist/auto-reply/reply/queue/enqueue.js +10 -5
  140. package/dist/auto-reply/reply/queue/state.js +13 -12
  141. package/dist/auto-reply/reply/reply-payloads.js +67 -36
  142. package/dist/auto-reply/reply/reply-reference.js +9 -8
  143. package/dist/auto-reply/reply/route-reply.js +15 -8
  144. package/dist/auto-reply/reply/session-reset-prompt.js +1 -1
  145. package/dist/auto-reply/reply/session.js +22 -6
  146. package/dist/auto-reply/reply/strip-inbound-meta.js +147 -0
  147. package/dist/auto-reply/reply/subagents-utils.js +56 -30
  148. package/dist/auto-reply/reply/typing.js +46 -21
  149. package/dist/auto-reply/send-policy.js +14 -7
  150. package/dist/auto-reply/status.js +140 -16
  151. package/dist/auto-reply/templating.js +10 -5
  152. package/dist/auto-reply/thinking.js +7 -16
  153. package/dist/auto-reply/tokens.js +21 -5
  154. package/dist/browser/bridge-server.js +36 -20
  155. package/dist/browser/cdp.helpers.js +7 -14
  156. package/dist/browser/cdp.js +35 -15
  157. package/dist/browser/chrome.profile-decoration.js +7 -4
  158. package/dist/browser/config.js +4 -0
  159. package/dist/browser/extension-relay-auth.js +55 -0
  160. package/dist/browser/extension-relay.js +74 -29
  161. package/dist/browser/navigation-guard.js +9 -1
  162. package/dist/browser/paths.js +77 -0
  163. package/dist/browser/profiles.js +13 -8
  164. package/dist/browser/pw-ai-module.js +10 -5
  165. package/dist/browser/pw-session.js +76 -39
  166. package/dist/browser/pw-tools-core.interactions.js +14 -7
  167. package/dist/browser/pw-tools-core.state.js +12 -6
  168. package/dist/browser/routes/agent.act.js +2 -2
  169. package/dist/browser/server-context.js +7 -0
  170. package/dist/build-info.json +3 -3
  171. package/dist/channels/allow-from.js +2 -1
  172. package/dist/channels/allowlists/resolve-utils.js +43 -19
  173. package/dist/channels/channel-config.js +14 -7
  174. package/dist/channels/draft-stream-loop.js +7 -0
  175. package/dist/channels/model-overrides.js +82 -0
  176. package/dist/channels/plugins/normalize/imessage.js +14 -7
  177. package/dist/channels/plugins/normalize/slack.js +10 -5
  178. package/dist/channels/plugins/normalize/telegram.js +14 -7
  179. package/dist/channels/plugins/outbound/discord.js +80 -8
  180. package/dist/channels/plugins/outbound/signal.js +11 -11
  181. package/dist/channels/plugins/setup-helpers.js +10 -5
  182. package/dist/channels/sender-label.js +14 -7
  183. package/dist/channels/session.js +4 -2
  184. package/dist/channels/status-reactions.js +297 -0
  185. package/dist/cli/banner.js +1 -1
  186. package/dist/cli/browser-cli-actions-input/register.files-downloads.js +65 -56
  187. package/dist/cli/cli-name.js +11 -11
  188. package/dist/cli/cli-utils.js +13 -3
  189. package/dist/cli/command-format.js +1 -1
  190. package/dist/cli/config-cli.js +1 -1
  191. package/dist/cli/daemon-cli/lifecycle-core.js +31 -19
  192. package/dist/cli/daemon-cli/lifecycle.js +64 -2
  193. package/dist/cli/daemon-cli/restart-health.js +126 -0
  194. package/dist/cli/daemon-cli/status.gather.js +9 -13
  195. package/dist/cli/daemon-cli/status.print.js +2 -10
  196. package/dist/cli/deps.js +27 -22
  197. package/dist/cli/gateway-cli/run-loop.js +23 -5
  198. package/dist/cli/node-cli/register.js +14 -5
  199. package/dist/cli/nodes-media-utils.js +7 -2
  200. package/dist/cli/outbound-send-deps.js +2 -9
  201. package/dist/cli/outbound-send-mapping.js +11 -0
  202. package/dist/cli/pairing-cli.js +40 -14
  203. package/dist/cli/plugins-cli.js +34 -41
  204. package/dist/cli/ports.js +11 -10
  205. package/dist/cli/program/command-registry.js +2 -11
  206. package/dist/cli/program/command-tree.js +16 -0
  207. package/dist/cli/program/preaction.js +13 -9
  208. package/dist/cli/program/register.configure.js +3 -18
  209. package/dist/cli/program/register.maintenance.js +2 -2
  210. package/dist/cli/program/register.onboard.js +2 -0
  211. package/dist/cli/program/register.status-health-sessions.js +16 -17
  212. package/dist/cli/program/register.subclis.js +93 -52
  213. package/dist/cli/route.js +11 -7
  214. package/dist/cli/system-cli.js +36 -46
  215. package/dist/cli/update-cli/shared.js +22 -9
  216. package/dist/cli/update-cli/update-command.js +89 -14
  217. package/dist/cli/update-cli/wizard.js +6 -12
  218. package/dist/commands/agent/run-context.js +18 -5
  219. package/dist/commands/agent/session-store.js +17 -4
  220. package/dist/commands/agent.js +22 -2
  221. package/dist/commands/agents.bindings.js +14 -7
  222. package/dist/commands/agents.commands.add.js +13 -9
  223. package/dist/commands/agents.commands.identity.js +12 -6
  224. package/dist/commands/agents.commands.list.js +11 -6
  225. package/dist/commands/agents.config.js +8 -10
  226. package/dist/commands/agents.providers.js +12 -6
  227. package/dist/commands/auth-choice-options.js +103 -75
  228. package/dist/commands/auth-choice.apply.byteplus.js +55 -0
  229. package/dist/commands/auth-choice.apply.js +4 -0
  230. package/dist/commands/auth-choice.apply.minimax.js +61 -13
  231. package/dist/commands/auth-choice.apply.openai.js +3 -1
  232. package/dist/commands/auth-choice.apply.volcengine.js +55 -0
  233. package/dist/commands/auth-choice.preferred-provider.js +2 -0
  234. package/dist/commands/channels/remove.js +13 -6
  235. package/dist/commands/channels/shared.js +4 -14
  236. package/dist/commands/configure.commands.js +14 -0
  237. package/dist/commands/configure.gateway.js +2 -4
  238. package/dist/commands/configure.js +1 -1
  239. package/dist/commands/configure.shared.js +11 -0
  240. package/dist/commands/daemon-install-helpers.js +2 -2
  241. package/dist/commands/dashboard.js +12 -10
  242. package/dist/commands/docs.js +14 -8
  243. package/dist/commands/doctor-config-flow.js +11 -9
  244. package/dist/commands/doctor-legacy-config.js +281 -0
  245. package/dist/commands/doctor-state-integrity.js +99 -23
  246. package/dist/commands/doctor-update.js +12 -9
  247. package/dist/commands/models/list.list-command.js +7 -5
  248. package/dist/commands/models/set-image.js +2 -21
  249. package/dist/commands/node-daemon-install-helpers.js +10 -8
  250. package/dist/commands/onboard-auth.config-minimax.js +54 -80
  251. package/dist/commands/onboard-auth.config-opencode.js +2 -18
  252. package/dist/commands/onboard-auth.credentials.js +90 -13
  253. package/dist/commands/onboard-auth.js +1 -1
  254. package/dist/commands/onboard-auth.models.js +6 -5
  255. package/dist/commands/onboard-hooks.js +1 -1
  256. package/dist/commands/onboard-non-interactive/api-keys.js +14 -7
  257. package/dist/commands/onboard-non-interactive/local/auth-choice.js +64 -49
  258. package/dist/commands/onboard-provider-auth-flags.js +14 -0
  259. package/dist/commands/onboard-remote.js +14 -7
  260. package/dist/commands/onboard.js +11 -13
  261. package/dist/commands/sandbox-display.js +6 -5
  262. package/dist/commands/status-all/diagnosis.js +14 -10
  263. package/dist/commands/status-all/format.js +1 -0
  264. package/dist/commands/status.gateway-probe.js +1 -16
  265. package/dist/commands/systemd-linger.js +12 -6
  266. package/dist/config/agent-limits.js +2 -0
  267. package/dist/config/commands.js +30 -16
  268. package/dist/config/config-paths.js +9 -11
  269. package/dist/config/defaults.js +22 -2
  270. package/dist/config/discord-preview-streaming.js +104 -0
  271. package/dist/config/env-vars.js +37 -8
  272. package/dist/config/includes.js +4 -0
  273. package/dist/config/io.js +97 -12
  274. package/dist/config/legacy.migrations.part-1.js +189 -78
  275. package/dist/config/legacy.shared.js +3 -1
  276. package/dist/config/merge-patch.js +4 -0
  277. package/dist/config/prototype-keys.js +4 -0
  278. package/dist/config/schema.help.js +44 -7
  279. package/dist/config/schema.labels.js +38 -6
  280. package/dist/config/sessions/delivery-info.js +10 -3
  281. package/dist/config/sessions/main-session.js +10 -5
  282. package/dist/config/sessions/session-file.js +33 -0
  283. package/dist/config/sessions/session-key.js +10 -5
  284. package/dist/config/sessions/store.js +1 -1
  285. package/dist/config/sessions.js +1 -0
  286. package/dist/config/zod-schema.agent-runtime.js +11 -0
  287. package/dist/config/zod-schema.js +148 -13
  288. package/dist/config/zod-schema.providers-core.js +78 -4
  289. package/dist/config/zod-schema.providers.js +6 -1
  290. package/dist/config/zod-schema.session.js +41 -2
  291. package/dist/cron/run-log.js +3 -0
  292. package/dist/cron/schedule.js +21 -10
  293. package/dist/cron/service/ops.js +35 -21
  294. package/dist/cron/service/timer.js +116 -16
  295. package/dist/cron/stagger.js +3 -1
  296. package/dist/discord/api.js +12 -6
  297. package/dist/discord/draft-chunking.js +22 -0
  298. package/dist/discord/draft-stream.js +124 -0
  299. package/dist/discord/monitor/agent-components.js +1 -1
  300. package/dist/discord/monitor/commands.js +5 -0
  301. package/dist/discord/monitor/gateway-plugin.js +2 -1
  302. package/dist/discord/monitor/listeners.js +37 -27
  303. package/dist/discord/monitor/message-handler.js +4 -1
  304. package/dist/discord/monitor/message-handler.preflight.js +65 -8
  305. package/dist/discord/monitor/message-handler.process.js +246 -217
  306. package/dist/discord/monitor/message-utils.js +143 -6
  307. package/dist/discord/monitor/model-picker-preferences.js +143 -0
  308. package/dist/discord/monitor/model-picker.js +651 -0
  309. package/dist/discord/monitor/native-command.js +573 -16
  310. package/dist/discord/monitor/provider.allowlist.js +223 -0
  311. package/dist/discord/monitor/provider.js +275 -347
  312. package/dist/discord/monitor/provider.lifecycle.js +100 -0
  313. package/dist/discord/monitor/reply-delivery.js +123 -16
  314. package/dist/discord/monitor/thread-bindings.discord-api.js +215 -0
  315. package/dist/discord/monitor/thread-bindings.js +4 -0
  316. package/dist/discord/monitor/thread-bindings.lifecycle.js +177 -0
  317. package/dist/discord/monitor/thread-bindings.manager.js +423 -0
  318. package/dist/discord/monitor/thread-bindings.messages.js +55 -0
  319. package/dist/discord/monitor/thread-bindings.state.js +358 -0
  320. package/dist/discord/monitor/thread-bindings.types.js +6 -0
  321. package/dist/discord/resolve-users.js +33 -21
  322. package/dist/discord/send.channels.js +15 -0
  323. package/dist/discord/send.js +3 -2
  324. package/dist/discord/send.outbound.js +82 -26
  325. package/dist/discord/send.permissions.js +83 -30
  326. package/dist/discord/send.reactions.js +8 -4
  327. package/dist/discord/token.js +10 -5
  328. package/dist/discord/voice/command.js +263 -0
  329. package/dist/discord/voice/manager.js +531 -0
  330. package/dist/gateway/auth.js +34 -10
  331. package/dist/gateway/call.js +4 -16
  332. package/dist/gateway/client.js +28 -4
  333. package/dist/gateway/config-reload.js +3 -4
  334. package/dist/gateway/control-ui.js +219 -96
  335. package/dist/gateway/hooks-mapping.js +88 -38
  336. package/dist/gateway/http-auth-helpers.js +3 -2
  337. package/dist/gateway/http-endpoint-helpers.js +1 -0
  338. package/dist/gateway/net.js +54 -12
  339. package/dist/gateway/node-invoke-system-run-approval.js +14 -35
  340. package/dist/gateway/node-registry.js +10 -5
  341. package/dist/gateway/openai-http.js +1 -0
  342. package/dist/gateway/openresponses-http.js +1 -0
  343. package/dist/gateway/origin-check.js +1 -18
  344. package/dist/gateway/protocol/index.js +4 -3
  345. package/dist/gateway/protocol/schema/cron.js +1 -0
  346. package/dist/gateway/protocol/schema/devices.js +1 -0
  347. package/dist/gateway/protocol/schema/protocol-schemas.js +2 -1
  348. package/dist/gateway/protocol/schema/sessions.js +6 -0
  349. package/dist/gateway/role-policy.js +17 -0
  350. package/dist/gateway/server/ws-connection/connect-policy.js +37 -0
  351. package/dist/gateway/server/ws-connection/message-handler.js +175 -148
  352. package/dist/gateway/server-chat.js +83 -25
  353. package/dist/gateway/server-constants.js +10 -9
  354. package/dist/gateway/server-cron.js +1 -0
  355. package/dist/gateway/server-http.js +16 -7
  356. package/dist/gateway/server-maintenance.js +20 -5
  357. package/dist/gateway/server-methods/chat.js +10 -6
  358. package/dist/gateway/server-methods/config.js +12 -14
  359. package/dist/gateway/server-methods/devices.js +17 -3
  360. package/dist/gateway/server-methods/models.js +11 -1
  361. package/dist/gateway/server-methods/sessions.js +64 -8
  362. package/dist/gateway/server-methods/usage.js +162 -75
  363. package/dist/gateway/server-node-events.js +29 -0
  364. package/dist/gateway/server-runtime-config.js +34 -13
  365. package/dist/gateway/server-startup-memory.js +17 -11
  366. package/dist/gateway/session-utils.fs.js +32 -34
  367. package/dist/gateway/sessions-resolve.js +17 -5
  368. package/dist/gateway/test-helpers.openai-mock.js +14 -7
  369. package/dist/gateway/tools-invoke-http.js +21 -10
  370. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +3 -1
  371. package/dist/hooks/bundled/command-logger/handler.js +7 -2
  372. package/dist/hooks/bundled/session-memory/handler.js +6 -5
  373. package/dist/hooks/frontmatter.js +6 -6
  374. package/dist/hooks/gmail-watcher.js +11 -6
  375. package/dist/hooks/internal-hooks.js +11 -1
  376. package/dist/hooks/llm-slug-generator.js +4 -1
  377. package/dist/hooks/workspace.js +47 -17
  378. package/dist/imessage/accounts.js +9 -20
  379. package/dist/imessage/monitor/inbound-processing.js +2 -1
  380. package/dist/infra/archive.js +174 -73
  381. package/dist/infra/control-ui-assets.js +14 -6
  382. package/dist/infra/device-pairing.js +108 -29
  383. package/dist/infra/env.js +10 -5
  384. package/dist/infra/exec-approvals-allowlist.js +122 -0
  385. package/dist/infra/exec-approvals-analysis.js +34 -3
  386. package/dist/infra/exec-approvals.js +5 -17
  387. package/dist/infra/exec-safe-bin-policy.js +53 -45
  388. package/dist/infra/fs-safe.js +71 -39
  389. package/dist/infra/gateway-lock.js +6 -2
  390. package/dist/infra/heartbeat-wake.js +6 -12
  391. package/dist/infra/host-env-security-policy.json +19 -0
  392. package/dist/infra/host-env-security.js +66 -0
  393. package/dist/infra/net/ssrf.js +131 -38
  394. package/dist/infra/outbound/bound-delivery-router.js +88 -0
  395. package/dist/infra/outbound/channel-selection.js +12 -6
  396. package/dist/infra/outbound/envelope.js +1 -1
  397. package/dist/infra/outbound/format.js +12 -6
  398. package/dist/infra/outbound/payloads.js +14 -7
  399. package/dist/infra/outbound/session-binding-service.js +123 -0
  400. package/dist/infra/path-guards.js +25 -0
  401. package/dist/infra/provider-usage.fetch.codex.js +7 -15
  402. package/dist/infra/provider-usage.fetch.gemini.js +14 -11
  403. package/dist/infra/provider-usage.fetch.shared.js +30 -1
  404. package/dist/infra/provider-usage.fetch.zai.js +10 -9
  405. package/dist/infra/retry-policy.js +4 -2
  406. package/dist/infra/retry.js +9 -5
  407. package/dist/infra/session-cost-usage.js +107 -59
  408. package/dist/infra/session-maintenance-warning.js +3 -1
  409. package/dist/infra/shell-env.js +98 -34
  410. package/dist/infra/ssh-config.js +12 -6
  411. package/dist/infra/system-run-command.js +49 -4
  412. package/dist/infra/update-channels.js +10 -5
  413. package/dist/line/accounts.js +5 -7
  414. package/dist/line/bot-access.js +8 -20
  415. package/dist/line/bot-handlers.js +3 -1
  416. package/dist/link-understanding/detect.js +15 -7
  417. package/dist/media/constants.js +15 -6
  418. package/dist/media/image-ops.js +7 -0
  419. package/dist/media/local-roots.js +3 -2
  420. package/dist/media-understanding/apply.js +4 -1
  421. package/dist/media-understanding/concurrency.js +8 -20
  422. package/dist/memory/backend-config.js +45 -6
  423. package/dist/memory/embeddings.js +10 -4
  424. package/dist/memory/fs-utils.js +23 -0
  425. package/dist/memory/manager-search.js +12 -6
  426. package/dist/memory/manager-sync-ops.js +12 -2
  427. package/dist/memory/qmd-manager.js +466 -53
  428. package/dist/memory/query-expansion.js +167 -3
  429. package/dist/memory/status-format.js +10 -5
  430. package/dist/memory/sync-memory-files.js +1 -1
  431. package/dist/node-host/invoke-system-run.js +281 -0
  432. package/dist/node-host/invoke.js +55 -337
  433. package/dist/pairing/pairing-store.js +22 -0
  434. package/dist/plugin-sdk/allow-from.js +1 -1
  435. package/dist/plugin-sdk/command-auth.js +3 -1
  436. package/dist/plugin-sdk/index.js +6 -3
  437. package/dist/plugin-sdk/webhook-targets.js +32 -0
  438. package/dist/plugins/bundled-dir.js +9 -6
  439. package/dist/plugins/hooks.js +50 -0
  440. package/dist/plugins/install.js +28 -16
  441. package/dist/plugins/runtime.js +3 -17
  442. package/dist/plugins/update.js +78 -12
  443. package/dist/process/spawn-utils.js +14 -7
  444. package/dist/providers/github-copilot-token.js +11 -6
  445. package/dist/providers/qwen-portal-oauth.js +14 -6
  446. package/dist/routing/account-id.js +30 -0
  447. package/dist/routing/resolve-route.js +3 -7
  448. package/dist/routing/session-key.js +2 -16
  449. package/dist/security/audit-channel.js +93 -2
  450. package/dist/security/audit-extra.async.js +159 -5
  451. package/dist/security/audit-extra.js +1 -1
  452. package/dist/security/audit-extra.sync.js +85 -6
  453. package/dist/security/audit.js +40 -4
  454. package/dist/security/dm-policy-shared.js +44 -0
  455. package/dist/security/external-content.js +26 -6
  456. package/dist/shared/entry-status.js +6 -0
  457. package/dist/shared/frontmatter.js +5 -5
  458. package/dist/shared/node-match.js +11 -4
  459. package/dist/shared/operator-scope-compat.js +8 -3
  460. package/dist/signal/accounts.js +7 -20
  461. package/dist/signal/monitor/event-handler.js +3 -1
  462. package/dist/slack/accounts.js +6 -19
  463. package/dist/slack/actions.js +11 -3
  464. package/dist/slack/monitor/auth.js +1 -1
  465. package/dist/slack/monitor/message-handler/dispatch.js +50 -29
  466. package/dist/slack/monitor/replies.js +15 -7
  467. package/dist/slack/monitor/slash.js +22 -13
  468. package/dist/slack/resolve-channels.js +10 -5
  469. package/dist/slack/send.js +102 -12
  470. package/dist/slack/stream-mode.js +10 -0
  471. package/dist/slack/streaming.js +4 -2
  472. package/dist/telegram/accounts.js +19 -14
  473. package/dist/telegram/bot/helpers.js +3 -5
  474. package/dist/telegram/bot-access.js +35 -36
  475. package/dist/telegram/bot-handlers.js +120 -148
  476. package/dist/telegram/bot-message-context.js +68 -9
  477. package/dist/telegram/bot-message-dispatch.js +155 -90
  478. package/dist/telegram/bot-native-commands.js +16 -0
  479. package/dist/telegram/draft-stream.js +14 -1
  480. package/dist/telegram/inline-buttons.js +5 -15
  481. package/dist/telegram/monitor.js +11 -7
  482. package/dist/telegram/network-config.js +19 -7
  483. package/dist/telegram/send.js +3 -2
  484. package/dist/telegram/sent-message-cache.js +5 -6
  485. package/dist/telegram/status-reaction-variants.js +208 -0
  486. package/dist/telegram/sticker-cache.js +11 -9
  487. package/dist/terminal/theme.js +12 -12
  488. package/dist/tts/tts.js +80 -567
  489. package/dist/tui/components/chat-log.js +41 -8
  490. package/dist/tui/theme/theme.js +10 -12
  491. package/dist/tui/tui-local-shell.js +16 -6
  492. package/dist/tui/tui.js +58 -6
  493. package/dist/utils/account-id.js +2 -4
  494. package/dist/utils/boolean.js +10 -5
  495. package/dist/utils/directive-tags.js +11 -0
  496. package/dist/utils/queue-helpers.js +67 -12
  497. package/dist/web/auto-reply/deliver-reply.js +8 -4
  498. package/dist/web/auto-reply/mentions.js +10 -5
  499. package/dist/web/auto-reply/monitor/group-members.js +14 -7
  500. package/dist/web/auto-reply/monitor/process-message.js +45 -24
  501. package/dist/web/inbound/access-control.js +5 -2
  502. package/dist/web/login-qr.js +12 -6
  503. package/dist/web/media.js +123 -16
  504. package/extensions/bluebubbles/src/monitor-processing.ts +580 -139
  505. package/extensions/bluebubbles/src/monitor.ts +208 -1950
  506. package/package.json +1 -1
@@ -0,0 +1,651 @@
1
+ import { Button, Container, Row, Separator, StringSelectMenu, TextDisplay, } from "@buape/carbon";
2
+ import { ButtonStyle } from "discord-api-types/v10";
3
+ import { normalizeProviderId } from "../../agents/model-selection.js";
4
+ import { buildModelsProviderData, } from "../../auto-reply/reply/commands-models.js";
5
+ export const DISCORD_MODEL_PICKER_CUSTOM_ID_KEY = "mdlpk";
6
+ export const DISCORD_CUSTOM_ID_MAX_CHARS = 100;
7
+ // Discord component limits.
8
+ export const DISCORD_COMPONENT_MAX_ROWS = 5;
9
+ export const DISCORD_COMPONENT_MAX_BUTTONS_PER_ROW = 5;
10
+ export const DISCORD_COMPONENT_MAX_SELECT_OPTIONS = 25;
11
+ // Reserve one row for navigation/utility buttons when rendering providers.
12
+ export const DISCORD_MODEL_PICKER_PROVIDER_PAGE_SIZE = DISCORD_COMPONENT_MAX_BUTTONS_PER_ROW * (DISCORD_COMPONENT_MAX_ROWS - 1);
13
+ // When providers fit in one page, we can use all button rows and hide nav controls.
14
+ export const DISCORD_MODEL_PICKER_PROVIDER_SINGLE_PAGE_MAX = DISCORD_COMPONENT_MAX_BUTTONS_PER_ROW * DISCORD_COMPONENT_MAX_ROWS;
15
+ export const DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE = DISCORD_COMPONENT_MAX_SELECT_OPTIONS;
16
+ const DISCORD_PROVIDER_BUTTON_LABEL_MAX_CHARS = 18;
17
+ const COMMAND_CONTEXTS = ["model", "models"];
18
+ const PICKER_ACTIONS = [
19
+ "open",
20
+ "provider",
21
+ "model",
22
+ "submit",
23
+ "quick",
24
+ "back",
25
+ "reset",
26
+ "cancel",
27
+ "recents",
28
+ ];
29
+ const PICKER_VIEWS = ["providers", "models", "recents"];
30
+ function encodeCustomIdValue(value) {
31
+ return encodeURIComponent(value);
32
+ }
33
+ function decodeCustomIdValue(value) {
34
+ try {
35
+ return decodeURIComponent(value);
36
+ }
37
+ catch {
38
+ return value;
39
+ }
40
+ }
41
+ function isValidCommandContext(value) {
42
+ return COMMAND_CONTEXTS.includes(value);
43
+ }
44
+ function isValidPickerAction(value) {
45
+ return PICKER_ACTIONS.includes(value);
46
+ }
47
+ function isValidPickerView(value) {
48
+ return PICKER_VIEWS.includes(value);
49
+ }
50
+ function normalizePage(value) {
51
+ const numeric = typeof value === "number" ? value : Number.NaN;
52
+ if (!Number.isFinite(numeric)) {
53
+ return 1;
54
+ }
55
+ return Math.max(1, Math.floor(numeric));
56
+ }
57
+ function parseRawPage(value) {
58
+ if (typeof value === "number") {
59
+ return normalizePage(value);
60
+ }
61
+ if (typeof value === "string" && value.trim()) {
62
+ const parsed = Number.parseInt(value, 10);
63
+ if (Number.isFinite(parsed)) {
64
+ return normalizePage(parsed);
65
+ }
66
+ }
67
+ return 1;
68
+ }
69
+ function parseRawPositiveInt(value) {
70
+ if (typeof value !== "string" && typeof value !== "number") {
71
+ return undefined;
72
+ }
73
+ const parsed = Number.parseInt(String(value), 10);
74
+ if (!Number.isFinite(parsed) || parsed < 1) {
75
+ return undefined;
76
+ }
77
+ return Math.floor(parsed);
78
+ }
79
+ function coerceString(value) {
80
+ return typeof value === "string" || typeof value === "number" ? String(value) : "";
81
+ }
82
+ function clampPageSize(rawPageSize, max, fallback) {
83
+ if (!Number.isFinite(rawPageSize)) {
84
+ return fallback;
85
+ }
86
+ return Math.min(max, Math.max(1, Math.floor(rawPageSize ?? fallback)));
87
+ }
88
+ function paginateItems(params) {
89
+ const totalItems = params.items.length;
90
+ const totalPages = Math.max(1, Math.ceil(totalItems / params.pageSize));
91
+ const page = Math.max(1, Math.min(params.page, totalPages));
92
+ const startIndex = (page - 1) * params.pageSize;
93
+ const endIndexExclusive = Math.min(totalItems, startIndex + params.pageSize);
94
+ return {
95
+ items: params.items.slice(startIndex, endIndexExclusive),
96
+ page,
97
+ pageSize: params.pageSize,
98
+ totalPages,
99
+ totalItems,
100
+ hasPrev: page > 1,
101
+ hasNext: page < totalPages,
102
+ };
103
+ }
104
+ function parseCurrentModelRef(raw) {
105
+ const trimmed = raw?.trim();
106
+ if (!trimmed) {
107
+ return null;
108
+ }
109
+ const slashIndex = trimmed.indexOf("/");
110
+ if (slashIndex <= 0 || slashIndex >= trimmed.length - 1) {
111
+ return null;
112
+ }
113
+ const provider = normalizeProviderId(trimmed.slice(0, slashIndex));
114
+ const model = trimmed.slice(slashIndex + 1);
115
+ if (!provider || !model) {
116
+ return null;
117
+ }
118
+ return { provider, model };
119
+ }
120
+ function formatCurrentModelLine(currentModel) {
121
+ const parsed = parseCurrentModelRef(currentModel);
122
+ if (!parsed) {
123
+ return "Current model: default";
124
+ }
125
+ return `Current model: ${parsed.provider}/${parsed.model}`;
126
+ }
127
+ function formatProviderButtonLabel(provider) {
128
+ if (provider.length <= DISCORD_PROVIDER_BUTTON_LABEL_MAX_CHARS) {
129
+ return provider;
130
+ }
131
+ return `${provider.slice(0, DISCORD_PROVIDER_BUTTON_LABEL_MAX_CHARS - 1)}…`;
132
+ }
133
+ function chunkProvidersForRows(items) {
134
+ if (items.length === 0) {
135
+ return [];
136
+ }
137
+ const rowCount = Math.max(1, Math.ceil(items.length / DISCORD_COMPONENT_MAX_BUTTONS_PER_ROW));
138
+ const minPerRow = Math.floor(items.length / rowCount);
139
+ const rowsWithExtraItem = items.length % rowCount;
140
+ const counts = Array.from({ length: rowCount }, (_, index) => index < rowCount - rowsWithExtraItem ? minPerRow : minPerRow + 1);
141
+ const rows = [];
142
+ let cursor = 0;
143
+ for (const count of counts) {
144
+ rows.push(items.slice(cursor, cursor + count));
145
+ cursor += count;
146
+ }
147
+ return rows;
148
+ }
149
+ function createModelPickerButton(params) {
150
+ class DiscordModelPickerButton extends Button {
151
+ label = params.label;
152
+ customId = params.customId;
153
+ style = params.style ?? ButtonStyle.Secondary;
154
+ disabled = params.disabled ?? false;
155
+ }
156
+ return new DiscordModelPickerButton();
157
+ }
158
+ function createModelSelect(params) {
159
+ class DiscordModelPickerSelect extends StringSelectMenu {
160
+ customId = params.customId;
161
+ options = params.options;
162
+ minValues = 1;
163
+ maxValues = 1;
164
+ placeholder = params.placeholder;
165
+ disabled = params.disabled ?? false;
166
+ }
167
+ return new DiscordModelPickerSelect();
168
+ }
169
+ function buildRenderedShell(params) {
170
+ if (params.layout === "classic") {
171
+ const lines = [params.title, ...params.detailLines, "", params.footer].filter(Boolean);
172
+ return {
173
+ layout: "classic",
174
+ content: lines.join("\n"),
175
+ components: params.rows,
176
+ };
177
+ }
178
+ const containerComponents = [
179
+ new TextDisplay(`## ${params.title}`),
180
+ ];
181
+ if (params.detailLines.length > 0) {
182
+ containerComponents.push(new TextDisplay(params.detailLines.join("\n")));
183
+ }
184
+ containerComponents.push(new Separator({ divider: true, spacing: "small" }));
185
+ if (params.preRowText) {
186
+ containerComponents.push(new TextDisplay(params.preRowText));
187
+ }
188
+ containerComponents.push(...params.rows);
189
+ if (params.trailingRows && params.trailingRows.length > 0) {
190
+ containerComponents.push(new Separator({ divider: true, spacing: "small" }));
191
+ containerComponents.push(...params.trailingRows);
192
+ }
193
+ if (params.footer) {
194
+ containerComponents.push(new Separator({ divider: false, spacing: "small" }));
195
+ containerComponents.push(new TextDisplay(`-# ${params.footer}`));
196
+ }
197
+ const container = new Container(containerComponents);
198
+ return {
199
+ layout: "v2",
200
+ components: [container],
201
+ };
202
+ }
203
+ function buildProviderRows(params) {
204
+ const rows = chunkProvidersForRows(params.page.items).map((providers) => new Row(providers.map((provider) => {
205
+ const style = provider.id === params.currentProvider ? ButtonStyle.Primary : ButtonStyle.Secondary;
206
+ return createModelPickerButton({
207
+ label: formatProviderButtonLabel(provider.id),
208
+ style,
209
+ customId: buildDiscordModelPickerCustomId({
210
+ command: params.command,
211
+ action: "provider",
212
+ view: "models",
213
+ provider: provider.id,
214
+ page: params.page.page,
215
+ userId: params.userId,
216
+ }),
217
+ });
218
+ })));
219
+ return rows;
220
+ }
221
+ function buildModelRows(params) {
222
+ const parsedCurrentModel = parseCurrentModelRef(params.currentModel);
223
+ const parsedPendingModel = parseCurrentModelRef(params.pendingModel);
224
+ const rows = [];
225
+ const hasQuickModels = (params.quickModels ?? []).length > 0;
226
+ const providerPage = getDiscordModelPickerProviderPage({
227
+ data: params.data,
228
+ page: params.providerPage,
229
+ });
230
+ const providerOptions = providerPage.items.map((provider) => ({
231
+ label: provider.id,
232
+ value: provider.id,
233
+ default: provider.id === params.modelPage.provider,
234
+ }));
235
+ rows.push(new Row([
236
+ createModelSelect({
237
+ customId: buildDiscordModelPickerCustomId({
238
+ command: params.command,
239
+ action: "provider",
240
+ view: "models",
241
+ provider: params.modelPage.provider,
242
+ page: providerPage.page,
243
+ providerPage: providerPage.page,
244
+ userId: params.userId,
245
+ }),
246
+ options: providerOptions,
247
+ placeholder: "Select provider",
248
+ }),
249
+ ]));
250
+ const selectedModelRef = parsedPendingModel ?? parsedCurrentModel;
251
+ const modelOptions = params.modelPage.items.map((model) => ({
252
+ label: model,
253
+ value: model,
254
+ default: selectedModelRef
255
+ ? selectedModelRef.provider === params.modelPage.provider && selectedModelRef.model === model
256
+ : false,
257
+ }));
258
+ rows.push(new Row([
259
+ createModelSelect({
260
+ customId: buildDiscordModelPickerCustomId({
261
+ command: params.command,
262
+ action: "model",
263
+ view: "models",
264
+ provider: params.modelPage.provider,
265
+ page: params.modelPage.page,
266
+ providerPage: providerPage.page,
267
+ userId: params.userId,
268
+ }),
269
+ options: modelOptions,
270
+ placeholder: `Select ${params.modelPage.provider} model`,
271
+ }),
272
+ ]));
273
+ const resolvedDefault = params.data.resolvedDefault;
274
+ const shouldDisableReset = Boolean(parsedCurrentModel) &&
275
+ parsedCurrentModel?.provider === resolvedDefault.provider &&
276
+ parsedCurrentModel?.model === resolvedDefault.model;
277
+ const hasPendingSelection = Boolean(parsedPendingModel) &&
278
+ parsedPendingModel?.provider === params.modelPage.provider &&
279
+ typeof params.pendingModelIndex === "number" &&
280
+ params.pendingModelIndex > 0;
281
+ const buttonRowItems = [
282
+ createModelPickerButton({
283
+ label: "Cancel",
284
+ style: ButtonStyle.Secondary,
285
+ customId: buildDiscordModelPickerCustomId({
286
+ command: params.command,
287
+ action: "cancel",
288
+ view: "models",
289
+ provider: params.modelPage.provider,
290
+ page: params.modelPage.page,
291
+ providerPage: providerPage.page,
292
+ userId: params.userId,
293
+ }),
294
+ }),
295
+ createModelPickerButton({
296
+ label: "Reset to default",
297
+ style: ButtonStyle.Secondary,
298
+ disabled: shouldDisableReset,
299
+ customId: buildDiscordModelPickerCustomId({
300
+ command: params.command,
301
+ action: "reset",
302
+ view: "models",
303
+ provider: params.modelPage.provider,
304
+ page: params.modelPage.page,
305
+ providerPage: providerPage.page,
306
+ userId: params.userId,
307
+ }),
308
+ }),
309
+ ];
310
+ if (hasQuickModels) {
311
+ buttonRowItems.push(createModelPickerButton({
312
+ label: "Recents",
313
+ style: ButtonStyle.Secondary,
314
+ customId: buildDiscordModelPickerCustomId({
315
+ command: params.command,
316
+ action: "recents",
317
+ view: "recents",
318
+ provider: params.modelPage.provider,
319
+ page: params.modelPage.page,
320
+ providerPage: providerPage.page,
321
+ userId: params.userId,
322
+ }),
323
+ }));
324
+ }
325
+ buttonRowItems.push(createModelPickerButton({
326
+ label: "Submit",
327
+ style: ButtonStyle.Primary,
328
+ disabled: !hasPendingSelection,
329
+ customId: buildDiscordModelPickerCustomId({
330
+ command: params.command,
331
+ action: "submit",
332
+ view: "models",
333
+ provider: params.modelPage.provider,
334
+ page: params.modelPage.page,
335
+ providerPage: providerPage.page,
336
+ modelIndex: params.pendingModelIndex,
337
+ userId: params.userId,
338
+ }),
339
+ }));
340
+ return { rows, buttonRow: new Row(buttonRowItems) };
341
+ }
342
+ /**
343
+ * Source-of-truth data for Discord picker views. This intentionally reuses the
344
+ * same provider/model resolver used by text and Telegram model commands.
345
+ */
346
+ export async function loadDiscordModelPickerData(cfg) {
347
+ return buildModelsProviderData(cfg);
348
+ }
349
+ export function buildDiscordModelPickerCustomId(params) {
350
+ const userId = params.userId.trim();
351
+ if (!userId) {
352
+ throw new Error("Discord model picker custom_id requires userId");
353
+ }
354
+ const page = normalizePage(params.page);
355
+ const providerPage = typeof params.providerPage === "number" && Number.isFinite(params.providerPage)
356
+ ? Math.max(1, Math.floor(params.providerPage))
357
+ : undefined;
358
+ const normalizedProvider = params.provider ? normalizeProviderId(params.provider) : undefined;
359
+ const modelIndex = typeof params.modelIndex === "number" && Number.isFinite(params.modelIndex)
360
+ ? Math.max(1, Math.floor(params.modelIndex))
361
+ : undefined;
362
+ const recentSlot = typeof params.recentSlot === "number" && Number.isFinite(params.recentSlot)
363
+ ? Math.max(1, Math.floor(params.recentSlot))
364
+ : undefined;
365
+ const parts = [
366
+ `${DISCORD_MODEL_PICKER_CUSTOM_ID_KEY}:cmd=${encodeCustomIdValue(params.command)}`,
367
+ `act=${encodeCustomIdValue(params.action)}`,
368
+ `view=${encodeCustomIdValue(params.view)}`,
369
+ `u=${encodeCustomIdValue(userId)}`,
370
+ `pg=${String(page)}`,
371
+ ];
372
+ if (normalizedProvider) {
373
+ parts.push(`p=${encodeCustomIdValue(normalizedProvider)}`);
374
+ }
375
+ if (providerPage) {
376
+ parts.push(`pp=${String(providerPage)}`);
377
+ }
378
+ if (modelIndex) {
379
+ parts.push(`mi=${String(modelIndex)}`);
380
+ }
381
+ if (recentSlot) {
382
+ parts.push(`rs=${String(recentSlot)}`);
383
+ }
384
+ const customId = parts.join(";");
385
+ if (customId.length > DISCORD_CUSTOM_ID_MAX_CHARS) {
386
+ throw new Error(`Discord model picker custom_id exceeds ${DISCORD_CUSTOM_ID_MAX_CHARS} chars (${customId.length})`);
387
+ }
388
+ return customId;
389
+ }
390
+ export function parseDiscordModelPickerCustomId(customId) {
391
+ const trimmed = customId.trim();
392
+ if (!trimmed.startsWith(`${DISCORD_MODEL_PICKER_CUSTOM_ID_KEY}:`)) {
393
+ return null;
394
+ }
395
+ const rawParts = trimmed.split(";");
396
+ const data = {};
397
+ for (const part of rawParts) {
398
+ const equalsIndex = part.indexOf("=");
399
+ if (equalsIndex <= 0) {
400
+ continue;
401
+ }
402
+ const rawKey = part.slice(0, equalsIndex);
403
+ const rawValue = part.slice(equalsIndex + 1);
404
+ const key = rawKey.includes(":") ? rawKey.split(":").slice(1).join(":") : rawKey;
405
+ if (!key) {
406
+ continue;
407
+ }
408
+ data[key] = rawValue;
409
+ }
410
+ return parseDiscordModelPickerData(data);
411
+ }
412
+ export function parseDiscordModelPickerData(data) {
413
+ if (!data || typeof data !== "object") {
414
+ return null;
415
+ }
416
+ const command = decodeCustomIdValue(coerceString(data.cmd));
417
+ const action = decodeCustomIdValue(coerceString(data.act));
418
+ const view = decodeCustomIdValue(coerceString(data.view));
419
+ const userId = decodeCustomIdValue(coerceString(data.u));
420
+ const providerRaw = decodeCustomIdValue(coerceString(data.p));
421
+ const page = parseRawPage(data.pg);
422
+ const providerPage = parseRawPositiveInt(data.pp);
423
+ const modelIndex = parseRawPositiveInt(data.mi);
424
+ const recentSlot = parseRawPositiveInt(data.rs);
425
+ if (!isValidCommandContext(command) || !isValidPickerAction(action) || !isValidPickerView(view)) {
426
+ return null;
427
+ }
428
+ const trimmedUserId = userId.trim();
429
+ if (!trimmedUserId) {
430
+ return null;
431
+ }
432
+ const provider = providerRaw ? normalizeProviderId(providerRaw) : undefined;
433
+ return {
434
+ command,
435
+ action,
436
+ view,
437
+ userId: trimmedUserId,
438
+ provider,
439
+ page,
440
+ ...(typeof providerPage === "number" ? { providerPage } : {}),
441
+ ...(typeof modelIndex === "number" ? { modelIndex } : {}),
442
+ ...(typeof recentSlot === "number" ? { recentSlot } : {}),
443
+ };
444
+ }
445
+ export function buildDiscordModelPickerProviderItems(data) {
446
+ return data.providers.map((provider) => ({
447
+ id: provider,
448
+ count: data.byProvider.get(provider)?.size ?? 0,
449
+ }));
450
+ }
451
+ export function getDiscordModelPickerProviderPage(params) {
452
+ const items = buildDiscordModelPickerProviderItems(params.data);
453
+ const canFitSinglePage = items.length <= DISCORD_MODEL_PICKER_PROVIDER_SINGLE_PAGE_MAX;
454
+ const maxPageSize = canFitSinglePage
455
+ ? DISCORD_MODEL_PICKER_PROVIDER_SINGLE_PAGE_MAX
456
+ : DISCORD_MODEL_PICKER_PROVIDER_PAGE_SIZE;
457
+ const pageSize = clampPageSize(params.pageSize, maxPageSize, maxPageSize);
458
+ return paginateItems({
459
+ items,
460
+ page: normalizePage(params.page),
461
+ pageSize,
462
+ });
463
+ }
464
+ export function getDiscordModelPickerModelPage(params) {
465
+ const provider = normalizeProviderId(params.provider);
466
+ const modelSet = params.data.byProvider.get(provider);
467
+ if (!modelSet) {
468
+ return null;
469
+ }
470
+ const pageSize = clampPageSize(params.pageSize, DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE, DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE);
471
+ const models = [...modelSet].toSorted();
472
+ const page = paginateItems({
473
+ items: models,
474
+ page: normalizePage(params.page),
475
+ pageSize,
476
+ });
477
+ return {
478
+ ...page,
479
+ provider,
480
+ };
481
+ }
482
+ export function renderDiscordModelPickerProvidersView(params) {
483
+ const page = getDiscordModelPickerProviderPage({ data: params.data, page: params.page });
484
+ const parsedCurrent = parseCurrentModelRef(params.currentModel);
485
+ const rows = buildProviderRows({
486
+ command: params.command,
487
+ userId: params.userId,
488
+ page,
489
+ currentProvider: parsedCurrent?.provider,
490
+ });
491
+ const detailLines = [
492
+ formatCurrentModelLine(params.currentModel),
493
+ `Select a provider (${page.totalItems} available).`,
494
+ ];
495
+ return buildRenderedShell({
496
+ layout: params.layout ?? "v2",
497
+ title: "Model Picker",
498
+ detailLines,
499
+ rows,
500
+ footer: `All ${page.totalItems} providers shown`,
501
+ });
502
+ }
503
+ export function renderDiscordModelPickerModelsView(params) {
504
+ const providerPage = normalizePage(params.providerPage);
505
+ const modelPage = getDiscordModelPickerModelPage({
506
+ data: params.data,
507
+ provider: params.provider,
508
+ page: params.page,
509
+ });
510
+ if (!modelPage) {
511
+ const rows = [
512
+ new Row([
513
+ createModelPickerButton({
514
+ label: "Back",
515
+ customId: buildDiscordModelPickerCustomId({
516
+ command: params.command,
517
+ action: "back",
518
+ view: "providers",
519
+ page: providerPage,
520
+ userId: params.userId,
521
+ }),
522
+ }),
523
+ ]),
524
+ ];
525
+ return buildRenderedShell({
526
+ layout: params.layout ?? "v2",
527
+ title: "Model Picker",
528
+ detailLines: [
529
+ formatCurrentModelLine(params.currentModel),
530
+ `Provider not found: ${normalizeProviderId(params.provider)}`,
531
+ ],
532
+ rows,
533
+ footer: "Choose a different provider.",
534
+ });
535
+ }
536
+ const { rows, buttonRow } = buildModelRows({
537
+ command: params.command,
538
+ userId: params.userId,
539
+ data: params.data,
540
+ providerPage,
541
+ modelPage,
542
+ currentModel: params.currentModel,
543
+ pendingModel: params.pendingModel,
544
+ pendingModelIndex: params.pendingModelIndex,
545
+ quickModels: params.quickModels,
546
+ });
547
+ const defaultModel = `${params.data.resolvedDefault.provider}/${params.data.resolvedDefault.model}`;
548
+ const pendingLine = params.pendingModel
549
+ ? `Selected: ${params.pendingModel} (press Submit)`
550
+ : "Select a model, then press Submit.";
551
+ return buildRenderedShell({
552
+ layout: params.layout ?? "v2",
553
+ title: "Model Picker",
554
+ detailLines: [formatCurrentModelLine(params.currentModel), `Default: ${defaultModel}`],
555
+ preRowText: pendingLine,
556
+ rows,
557
+ trailingRows: [buttonRow],
558
+ });
559
+ }
560
+ function formatRecentsButtonLabel(modelRef, suffix) {
561
+ const maxLen = 80;
562
+ const label = suffix ? `${modelRef} ${suffix}` : modelRef;
563
+ if (label.length <= maxLen) {
564
+ return label;
565
+ }
566
+ const trimmed = suffix
567
+ ? `${modelRef.slice(0, maxLen - suffix.length - 2)}… ${suffix}`
568
+ : `${modelRef.slice(0, maxLen - 1)}…`;
569
+ return trimmed;
570
+ }
571
+ export function renderDiscordModelPickerRecentsView(params) {
572
+ const defaultModelRef = `${params.data.resolvedDefault.provider}/${params.data.resolvedDefault.model}`;
573
+ const rows = [];
574
+ // Dedupe: filter recents that match the default model.
575
+ const dedupedQuickModels = params.quickModels.filter((modelRef) => modelRef !== defaultModelRef);
576
+ // Default model button — slot 1.
577
+ rows.push(new Row([
578
+ createModelPickerButton({
579
+ label: formatRecentsButtonLabel(defaultModelRef, "(default)"),
580
+ style: ButtonStyle.Secondary,
581
+ customId: buildDiscordModelPickerCustomId({
582
+ command: params.command,
583
+ action: "submit",
584
+ view: "recents",
585
+ recentSlot: 1,
586
+ provider: params.provider,
587
+ page: params.page,
588
+ providerPage: params.providerPage,
589
+ userId: params.userId,
590
+ }),
591
+ }),
592
+ ]));
593
+ // Recent model buttons — slot 2+.
594
+ for (let i = 0; i < dedupedQuickModels.length; i++) {
595
+ const modelRef = dedupedQuickModels[i];
596
+ rows.push(new Row([
597
+ createModelPickerButton({
598
+ label: formatRecentsButtonLabel(modelRef),
599
+ style: ButtonStyle.Secondary,
600
+ customId: buildDiscordModelPickerCustomId({
601
+ command: params.command,
602
+ action: "submit",
603
+ view: "recents",
604
+ recentSlot: i + 2,
605
+ provider: params.provider,
606
+ page: params.page,
607
+ providerPage: params.providerPage,
608
+ userId: params.userId,
609
+ }),
610
+ }),
611
+ ]));
612
+ }
613
+ // Back button after a divider (via trailingRows).
614
+ const backRow = new Row([
615
+ createModelPickerButton({
616
+ label: "Back",
617
+ style: ButtonStyle.Secondary,
618
+ customId: buildDiscordModelPickerCustomId({
619
+ command: params.command,
620
+ action: "back",
621
+ view: "models",
622
+ provider: params.provider,
623
+ page: params.page,
624
+ providerPage: params.providerPage,
625
+ userId: params.userId,
626
+ }),
627
+ }),
628
+ ]);
629
+ return buildRenderedShell({
630
+ layout: params.layout ?? "v2",
631
+ title: "Recents",
632
+ detailLines: [
633
+ "Models you've previously selected appear here.",
634
+ formatCurrentModelLine(params.currentModel),
635
+ ],
636
+ preRowText: "Tap a model to switch.",
637
+ rows,
638
+ trailingRows: [backRow],
639
+ });
640
+ }
641
+ export function toDiscordModelPickerMessagePayload(view) {
642
+ if (view.layout === "classic") {
643
+ return {
644
+ content: view.content,
645
+ components: view.components,
646
+ };
647
+ }
648
+ return {
649
+ components: view.components,
650
+ };
651
+ }