@poolzin/pool-bot 2026.1.39 → 2026.2.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 (511) hide show
  1. package/assets/chrome-extension/README.md +3 -3
  2. package/assets/chrome-extension/background.js +5 -5
  3. package/assets/chrome-extension/manifest.json +3 -3
  4. package/assets/chrome-extension/options.html +4 -4
  5. package/assets/chrome-extension/options.js +1 -1
  6. package/dist/acp/client.js +3 -3
  7. package/dist/acp/types.js +1 -1
  8. package/dist/agents/agent-paths.js +3 -3
  9. package/dist/agents/auth-profiles/paths.js +3 -3
  10. package/dist/agents/bash-tools.exec.js +76 -25
  11. package/dist/agents/cli-runner/helpers.js +10 -12
  12. package/dist/agents/cli-runner.js +2 -2
  13. package/dist/agents/cloudflare-ai-gateway.js +31 -0
  14. package/dist/agents/compaction.js +16 -2
  15. package/dist/agents/context-window-guard.js +13 -10
  16. package/dist/agents/context.js +4 -4
  17. package/dist/agents/docs-path.js +1 -1
  18. package/dist/agents/identity.js +47 -7
  19. package/dist/agents/memory-search.js +25 -8
  20. package/dist/agents/minimax-vlm.js +1 -1
  21. package/dist/agents/model-auth.js +12 -1
  22. package/dist/agents/model-catalog.js +4 -4
  23. package/dist/agents/model-selection.js +31 -4
  24. package/dist/agents/models-config.js +3 -3
  25. package/dist/agents/models-config.providers.js +147 -39
  26. package/dist/agents/pi-embedded-block-chunker.js +117 -42
  27. package/dist/agents/pi-embedded-helpers/errors.js +183 -78
  28. package/dist/agents/pi-embedded-helpers/openai.js +1 -1
  29. package/dist/agents/pi-embedded-helpers.js +1 -1
  30. package/dist/agents/pi-embedded-runner/compact.js +9 -8
  31. package/dist/agents/pi-embedded-runner/model.js +63 -4
  32. package/dist/agents/pi-embedded-runner/run/attempt.js +27 -17
  33. package/dist/agents/pi-embedded-runner/run.js +203 -50
  34. package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
  35. package/dist/agents/pi-embedded-runner/tool-result-truncation.js +275 -0
  36. package/dist/agents/pi-embedded-runner/utils.js +1 -1
  37. package/dist/agents/pi-embedded-subscribe.js +118 -29
  38. package/dist/agents/pi-model-discovery.js +10 -0
  39. package/dist/agents/pi-tool-definition-adapter.js +50 -9
  40. package/dist/agents/pi-tools.before-tool-call.js +67 -0
  41. package/dist/agents/pi-tools.js +20 -10
  42. package/dist/agents/pi-tools.read.js +2 -2
  43. package/dist/agents/poolbot-tools.js +15 -10
  44. package/dist/agents/sandbox-paths.js +31 -0
  45. package/dist/agents/session-file-repair.js +83 -0
  46. package/dist/agents/session-tool-result-guard.js +94 -15
  47. package/dist/agents/session-transcript-repair.js +68 -0
  48. package/dist/agents/shell-utils.js +51 -0
  49. package/dist/agents/skills/bundled-context.js +23 -0
  50. package/dist/agents/skills/bundled-dir.js +41 -7
  51. package/dist/agents/skills/frontmatter.js +1 -1
  52. package/dist/agents/skills/workspace.js +2 -2
  53. package/dist/agents/skills-install.js +60 -23
  54. package/dist/agents/subagent-announce.js +79 -34
  55. package/dist/agents/system-prompt.js +28 -4
  56. package/dist/agents/together-models.js +127 -0
  57. package/dist/agents/tool-images.js +1 -1
  58. package/dist/agents/tool-policy.conformance.js +14 -0
  59. package/dist/agents/tool-policy.js +25 -1
  60. package/dist/agents/tools/browser-tool.js +3 -3
  61. package/dist/agents/tools/cron-tool.js +166 -19
  62. package/dist/agents/tools/discord-actions-presence.js +78 -0
  63. package/dist/agents/tools/image-tool.js +2 -2
  64. package/dist/agents/tools/memory-tool.js +93 -5
  65. package/dist/agents/tools/message-tool.js +56 -2
  66. package/dist/agents/tools/sessions-history-tool.js +69 -1
  67. package/dist/agents/tools/web-search.js +211 -42
  68. package/dist/agents/usage.js +23 -1
  69. package/dist/agents/workspace-run.js +67 -0
  70. package/dist/agents/workspace-templates.js +44 -0
  71. package/dist/auto-reply/command-auth.js +121 -6
  72. package/dist/auto-reply/commands-registry.data.js +1 -1
  73. package/dist/auto-reply/envelope.js +50 -72
  74. package/dist/auto-reply/reply/commands-compact.js +1 -0
  75. package/dist/auto-reply/reply/commands-context-report.js +3 -2
  76. package/dist/auto-reply/reply/commands-context.js +1 -0
  77. package/dist/auto-reply/reply/commands-models.js +107 -60
  78. package/dist/auto-reply/reply/commands-ptt.js +171 -0
  79. package/dist/auto-reply/reply/commands-session.js +2 -2
  80. package/dist/auto-reply/reply/get-reply-run.js +16 -5
  81. package/dist/auto-reply/reply/groups.js +1 -1
  82. package/dist/auto-reply/reply/inbound-context.js +9 -1
  83. package/dist/auto-reply/reply/inbound-meta.js +130 -0
  84. package/dist/auto-reply/reply/model-selection.js +3 -3
  85. package/dist/auto-reply/reply/untrusted-context.js +15 -0
  86. package/dist/auto-reply/status.js +1 -1
  87. package/dist/auto-reply/thinking.js +88 -43
  88. package/dist/browser/bridge-server.js +13 -0
  89. package/dist/browser/cdp.helpers.js +38 -24
  90. package/dist/browser/client-fetch.js +51 -8
  91. package/dist/browser/config.js +2 -11
  92. package/dist/browser/extension-relay.js +104 -43
  93. package/dist/browser/pw-ai.js +1 -1
  94. package/dist/browser/pw-session.js +143 -8
  95. package/dist/browser/pw-tools-core.interactions.js +125 -27
  96. package/dist/browser/pw-tools-core.responses.js +1 -1
  97. package/dist/browser/pw-tools-core.state.js +1 -1
  98. package/dist/browser/routes/agent.act.js +86 -41
  99. package/dist/browser/routes/dispatcher.js +4 -4
  100. package/dist/browser/screenshot.js +1 -1
  101. package/dist/browser/server-context.js +2 -2
  102. package/dist/browser/server.js +13 -0
  103. package/dist/build-info.json +3 -3
  104. package/dist/canvas-host/a2ui.js +3 -3
  105. package/dist/channels/plugins/catalog.js +2 -2
  106. package/dist/channels/plugins/onboarding/imessage.js +1 -1
  107. package/dist/channels/plugins/onboarding/signal.js +1 -1
  108. package/dist/channels/plugins/onboarding/slack.js +4 -4
  109. package/dist/channels/plugins/onboarding/whatsapp.js +3 -3
  110. package/dist/channels/plugins/pairing-message.js +1 -1
  111. package/dist/channels/reply-prefix.js +8 -1
  112. package/dist/cli/browser-cli-extension.js +2 -2
  113. package/dist/cli/cron-cli/register.cron-add.js +61 -40
  114. package/dist/cli/cron-cli/register.cron-edit.js +60 -34
  115. package/dist/cli/cron-cli/shared.js +56 -41
  116. package/dist/cli/dns-cli.js +26 -14
  117. package/dist/cli/docs-cli.js +1 -1
  118. package/dist/cli/gateway-cli/dev.js +1 -1
  119. package/dist/cli/gateway-cli/register.js +37 -19
  120. package/dist/cli/memory-cli.js +30 -20
  121. package/dist/cli/nodes-cli/register.canvas.js +1 -1
  122. package/dist/cli/parse-bytes.js +37 -0
  123. package/dist/cli/plugins-cli.js +1 -1
  124. package/dist/cli/run-main.js +2 -2
  125. package/dist/cli/security-cli.js +1 -1
  126. package/dist/cli/tagline.js +1 -1
  127. package/dist/cli/update-cli.js +173 -52
  128. package/dist/cli/webhooks-cli.js +5 -5
  129. package/dist/commands/agent.js +1 -0
  130. package/dist/commands/agents.commands.add.js +1 -1
  131. package/dist/commands/auth-choice.apply.api-providers.js +305 -17
  132. package/dist/commands/auth-choice.apply.js +4 -1
  133. package/dist/commands/auth-choice.apply.plugin-provider.js +2 -2
  134. package/dist/commands/auth-choice.apply.xai.js +63 -0
  135. package/dist/commands/auth-choice.preferred-provider.js +7 -1
  136. package/dist/commands/configure.wizard.js +1 -1
  137. package/dist/commands/dashboard.js +1 -1
  138. package/dist/commands/docs.js +1 -1
  139. package/dist/commands/doctor-config-flow.js +61 -5
  140. package/dist/commands/doctor-gateway-services.js +3 -3
  141. package/dist/commands/doctor-state-migrations.js +1 -1
  142. package/dist/commands/doctor-update.js +3 -3
  143. package/dist/commands/doctor.js +1 -1
  144. package/dist/commands/health.js +1 -1
  145. package/dist/commands/model-allowlist.js +29 -0
  146. package/dist/commands/model-picker.js +2 -1
  147. package/dist/commands/models/list.probe.js +2 -2
  148. package/dist/commands/models/list.registry.js +4 -4
  149. package/dist/commands/models/list.status-command.js +44 -24
  150. package/dist/commands/models/shared.js +15 -0
  151. package/dist/commands/onboard-auth.config-core.js +366 -28
  152. package/dist/commands/onboard-auth.credentials.js +71 -9
  153. package/dist/commands/onboard-auth.js +3 -3
  154. package/dist/commands/onboard-auth.models.js +26 -24
  155. package/dist/commands/onboard-custom.js +384 -0
  156. package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +35 -0
  157. package/dist/commands/onboard-non-interactive/local/auth-choice.js +146 -9
  158. package/dist/commands/onboard-skills.js +63 -38
  159. package/dist/commands/openai-model-default.js +41 -0
  160. package/dist/commands/status-all/report-lines.js +1 -1
  161. package/dist/commands/status.command.js +1 -1
  162. package/dist/commands/uninstall.js +3 -3
  163. package/dist/compat/legacy-names.js +1 -1
  164. package/dist/config/defaults.js +3 -2
  165. package/dist/config/io.js +3 -3
  166. package/dist/config/paths.js +136 -35
  167. package/dist/config/plugin-auto-enable.js +21 -5
  168. package/dist/config/redact-snapshot.js +153 -0
  169. package/dist/config/schema.field-metadata.js +590 -0
  170. package/dist/config/schema.js +3 -3
  171. package/dist/config/sessions/store.js +291 -23
  172. package/dist/config/types.memory.js +1 -0
  173. package/dist/config/version.js +4 -4
  174. package/dist/config/zod-schema.agent-defaults.js +3 -0
  175. package/dist/config/zod-schema.agent-runtime.js +13 -2
  176. package/dist/config/zod-schema.providers-core.js +142 -0
  177. package/dist/config/zod-schema.session.js +3 -0
  178. package/dist/cron/delivery.js +57 -0
  179. package/dist/cron/isolated-agent/delivery-target.js +18 -3
  180. package/dist/cron/isolated-agent/helpers.js +22 -5
  181. package/dist/cron/isolated-agent/run.js +171 -63
  182. package/dist/cron/isolated-agent/session.js +2 -0
  183. package/dist/cron/normalize.js +356 -28
  184. package/dist/cron/parse.js +10 -5
  185. package/dist/cron/run-log.js +35 -10
  186. package/dist/cron/schedule.js +41 -6
  187. package/dist/cron/service/jobs.js +208 -35
  188. package/dist/cron/service/ops.js +72 -16
  189. package/dist/cron/service/state.js +2 -0
  190. package/dist/cron/service/store.js +386 -14
  191. package/dist/cron/service/timer.js +390 -147
  192. package/dist/cron/session-reaper.js +86 -0
  193. package/dist/cron/store.js +23 -8
  194. package/dist/cron/validate-timestamp.js +43 -0
  195. package/dist/daemon/constants.js +7 -7
  196. package/dist/daemon/inspect.js +6 -6
  197. package/dist/daemon/systemd-unit.js +1 -1
  198. package/dist/discord/monitor/agent-components.js +438 -0
  199. package/dist/discord/monitor/allow-list.js +28 -5
  200. package/dist/discord/monitor/gateway-registry.js +29 -0
  201. package/dist/discord/monitor/native-command.js +44 -23
  202. package/dist/discord/monitor/sender-identity.js +45 -0
  203. package/dist/discord/pluralkit.js +27 -0
  204. package/dist/discord/send.outbound.js +92 -5
  205. package/dist/discord/send.shared.js +60 -23
  206. package/dist/discord/targets.js +84 -1
  207. package/dist/entry.js +15 -9
  208. package/dist/extensionAPI.js +8 -0
  209. package/dist/gateway/control-ui.js +8 -1
  210. package/dist/gateway/hooks-mapping.js +3 -0
  211. package/dist/gateway/hooks.js +65 -0
  212. package/dist/gateway/live-image-probe.js +1 -66
  213. package/dist/gateway/net.js +96 -31
  214. package/dist/gateway/node-command-policy.js +50 -15
  215. package/dist/gateway/openai-http.js +2 -2
  216. package/dist/gateway/openresponses-http.js +4 -4
  217. package/dist/gateway/origin-check.js +56 -0
  218. package/dist/gateway/protocol/client-info.js +9 -0
  219. package/dist/gateway/protocol/index.js +9 -2
  220. package/dist/gateway/protocol/schema/agents-models-skills.js +71 -1
  221. package/dist/gateway/protocol/schema/cron.js +22 -10
  222. package/dist/gateway/protocol/schema/protocol-schemas.js +16 -2
  223. package/dist/gateway/protocol/schema/sessions.js +12 -0
  224. package/dist/gateway/server/hooks.js +1 -1
  225. package/dist/gateway/server-broadcast.js +26 -9
  226. package/dist/gateway/server-chat.js +112 -23
  227. package/dist/gateway/server-discovery-runtime.js +10 -2
  228. package/dist/gateway/server-discovery.js +2 -2
  229. package/dist/gateway/server-http.js +110 -12
  230. package/dist/gateway/server-methods/agent-timestamp.js +60 -0
  231. package/dist/gateway/server-methods/agents.js +321 -2
  232. package/dist/gateway/server-methods/usage.js +559 -16
  233. package/dist/gateway/server-runtime-state.js +22 -8
  234. package/dist/gateway/server-startup-memory.js +16 -0
  235. package/dist/gateway/server.impl.js +7 -3
  236. package/dist/gateway/session-utils.fs.js +23 -25
  237. package/dist/gateway/session-utils.js +20 -10
  238. package/dist/gateway/sessions-patch.js +7 -22
  239. package/dist/gateway/test-helpers.server.js +35 -2
  240. package/dist/hooks/frontmatter.js +1 -1
  241. package/dist/hooks/hooks-status.js +1 -1
  242. package/dist/hooks/install.js +2 -2
  243. package/dist/hooks/loader.js +1 -1
  244. package/dist/hooks/workspace.js +3 -3
  245. package/dist/imessage/constants.js +2 -0
  246. package/dist/imessage/monitor/deliver.js +4 -1
  247. package/dist/imessage/monitor/monitor-provider.js +51 -1
  248. package/dist/index.js +2 -2
  249. package/dist/infra/bonjour-discovery.js +131 -70
  250. package/dist/infra/bonjour.js +3 -3
  251. package/dist/infra/control-ui-assets.js +134 -12
  252. package/dist/infra/errors.js +12 -0
  253. package/dist/infra/exec-approvals.js +266 -57
  254. package/dist/infra/format-time/format-datetime.js +79 -0
  255. package/dist/infra/format-time/format-duration.js +81 -0
  256. package/dist/infra/format-time/format-relative.js +80 -0
  257. package/dist/infra/heartbeat-runner.js +140 -49
  258. package/dist/infra/home-dir.js +54 -0
  259. package/dist/infra/net/fetch-guard.js +122 -0
  260. package/dist/infra/net/ssrf.js +65 -29
  261. package/dist/infra/outbound/abort.js +14 -0
  262. package/dist/infra/outbound/message-action-runner.js +77 -13
  263. package/dist/infra/outbound/outbound-session.js +143 -37
  264. package/dist/infra/path-env.js +3 -3
  265. package/dist/infra/poolbot-root.js +43 -1
  266. package/dist/infra/provider-usage.fetch.minimax.js +1 -1
  267. package/dist/infra/restart.js +1 -1
  268. package/dist/infra/session-cost-usage.js +631 -41
  269. package/dist/infra/state-migrations.js +317 -47
  270. package/dist/infra/tailscale.js +1 -1
  271. package/dist/infra/update-global.js +35 -0
  272. package/dist/infra/update-runner.js +149 -43
  273. package/dist/infra/warning-filter.js +65 -0
  274. package/dist/infra/widearea-dns.js +30 -9
  275. package/dist/logging/redact-identifier.js +12 -0
  276. package/dist/macos/relay.js +2 -2
  277. package/dist/media/fetch.js +81 -58
  278. package/dist/media/input-files.js +1 -1
  279. package/dist/media/mime.js +4 -0
  280. package/dist/media/png-encode.js +74 -0
  281. package/dist/media-understanding/apply.js +403 -3
  282. package/dist/media-understanding/attachments.js +38 -27
  283. package/dist/media-understanding/defaults.js +16 -0
  284. package/dist/media-understanding/providers/deepgram/audio.js +22 -14
  285. package/dist/media-understanding/providers/google/audio.js +24 -17
  286. package/dist/media-understanding/providers/google/video.js +24 -17
  287. package/dist/media-understanding/providers/image.js +4 -4
  288. package/dist/media-understanding/providers/index.js +4 -1
  289. package/dist/media-understanding/providers/openai/audio.js +22 -14
  290. package/dist/media-understanding/providers/shared.js +16 -11
  291. package/dist/media-understanding/providers/zai/index.js +6 -0
  292. package/dist/media-understanding/runner.js +158 -90
  293. package/dist/memory/backend-config.js +207 -0
  294. package/dist/memory/batch-voyage.js +277 -0
  295. package/dist/memory/embeddings-voyage.js +75 -0
  296. package/dist/memory/embeddings.js +29 -17
  297. package/dist/memory/internal.js +101 -18
  298. package/dist/memory/manager.js +155 -48
  299. package/dist/memory/search-manager.js +173 -0
  300. package/dist/memory/session-files.js +9 -3
  301. package/dist/memory/types.js +1 -0
  302. package/dist/node-host/runner.js +36 -26
  303. package/dist/node-host/with-timeout.js +27 -0
  304. package/dist/pairing/pairing-messages.js +1 -1
  305. package/dist/plugins/commands.js +5 -1
  306. package/dist/plugins/config-state.js +86 -7
  307. package/dist/plugins/discovery.js +1 -1
  308. package/dist/plugins/install.js +2 -2
  309. package/dist/plugins/source-display.js +51 -0
  310. package/dist/plugins/update.js +1 -1
  311. package/dist/process/exec.js +20 -2
  312. package/dist/routing/resolve-route.js +12 -0
  313. package/dist/routing/session-key.js +15 -0
  314. package/dist/runtime.js +2 -0
  315. package/dist/security/audit-extra.async.js +601 -0
  316. package/dist/security/audit-extra.js +2 -830
  317. package/dist/security/audit-extra.sync.js +505 -0
  318. package/dist/security/audit.js +2 -2
  319. package/dist/security/channel-metadata.js +34 -0
  320. package/dist/security/external-content.js +88 -6
  321. package/dist/security/skill-scanner.js +330 -0
  322. package/dist/sessions/session-key-utils.js +7 -0
  323. package/dist/shared/text/reasoning-tags.js +52 -7
  324. package/dist/signal/monitor/event-handler.js +80 -1
  325. package/dist/slack/monitor/media.js +85 -15
  326. package/dist/tailscale/detect.js +145 -0
  327. package/dist/telegram/bot/helpers.js +109 -28
  328. package/dist/telegram/bot-handlers.js +144 -3
  329. package/dist/telegram/bot-message-context.js +38 -11
  330. package/dist/telegram/bot-message-dispatch.js +48 -15
  331. package/dist/telegram/bot-native-commands.js +86 -29
  332. package/dist/telegram/bot.js +30 -29
  333. package/dist/telegram/model-buttons.js +163 -0
  334. package/dist/telegram/monitor.js +110 -85
  335. package/dist/telegram/send.js +129 -47
  336. package/dist/terminal/restore.js +45 -0
  337. package/dist/test-helpers/state-dir-env.js +16 -0
  338. package/dist/test-helpers/workspace.js +11 -0
  339. package/dist/test-utils/channel-plugins.js +82 -0
  340. package/dist/test-utils/ports.js +73 -0
  341. package/dist/tts/tts.js +12 -6
  342. package/dist/tui/tui-session-actions.js +166 -54
  343. package/dist/utils/fetch-timeout.js +20 -0
  344. package/dist/utils/normalize-secret-input.js +19 -0
  345. package/dist/utils/shell-argv.js +61 -0
  346. package/dist/utils/transcript-tools.js +58 -0
  347. package/dist/utils.js +55 -14
  348. package/dist/version.js +42 -5
  349. package/dist/web/qr-image.js +1 -61
  350. package/dist/wizard/onboarding.finalize.js +7 -7
  351. package/dist/wizard/onboarding.js +3 -3
  352. package/docs/RELEASE_WORKFOTS_COMPARISON.md +3 -3
  353. package/docs/_config.yml +2 -2
  354. package/docs/_layouts/default.html +9 -9
  355. package/docs/concepts/typebox.md +1 -1
  356. package/docs/docs.json +1 -1
  357. package/docs/northflank.mdx +7 -7
  358. package/docs/railway.mdx +3 -3
  359. package/docs/render.mdx +5 -5
  360. package/docs/start/lore.md +2 -2
  361. package/extensions/bluebubbles/index.ts +2 -2
  362. package/extensions/bluebubbles/package.json +1 -1
  363. package/extensions/bluebubbles/src/accounts.ts +8 -8
  364. package/extensions/bluebubbles/src/actions.test.ts +22 -22
  365. package/extensions/bluebubbles/src/actions.ts +5 -5
  366. package/extensions/bluebubbles/src/attachments.ts +2 -2
  367. package/extensions/bluebubbles/src/channel.ts +16 -16
  368. package/extensions/bluebubbles/src/chat.ts +2 -2
  369. package/extensions/bluebubbles/src/media-send.ts +2 -2
  370. package/extensions/bluebubbles/src/monitor.test.ts +46 -46
  371. package/extensions/bluebubbles/src/monitor.ts +5 -5
  372. package/extensions/bluebubbles/src/onboarding.ts +7 -7
  373. package/extensions/bluebubbles/src/reactions.ts +2 -2
  374. package/extensions/bluebubbles/src/send.ts +2 -2
  375. package/extensions/copilot-proxy/README.md +1 -1
  376. package/extensions/copilot-proxy/package.json +1 -1
  377. package/extensions/diagnostics-otel/index.ts +2 -2
  378. package/extensions/diagnostics-otel/package.json +1 -1
  379. package/extensions/diagnostics-otel/src/service.ts +3 -3
  380. package/extensions/discord/index.ts +2 -2
  381. package/extensions/discord/package.json +1 -1
  382. package/extensions/google-antigravity-auth/README.md +1 -1
  383. package/extensions/google-antigravity-auth/index.ts +1 -1
  384. package/extensions/google-antigravity-auth/package.json +1 -1
  385. package/extensions/google-gemini-cli-auth/README.md +1 -1
  386. package/extensions/google-gemini-cli-auth/oauth.ts +1 -1
  387. package/extensions/google-gemini-cli-auth/package.json +1 -1
  388. package/extensions/googlechat/index.ts +3 -3
  389. package/extensions/googlechat/package.json +1 -1
  390. package/extensions/googlechat/src/accounts.ts +8 -8
  391. package/extensions/googlechat/src/actions.ts +6 -6
  392. package/extensions/googlechat/src/channel.ts +21 -21
  393. package/extensions/googlechat/src/monitor.ts +8 -8
  394. package/extensions/googlechat/src/onboarding.ts +10 -10
  395. package/extensions/imessage/index.ts +2 -2
  396. package/extensions/imessage/package.json +1 -1
  397. package/extensions/line/index.ts +2 -2
  398. package/extensions/line/package.json +1 -1
  399. package/extensions/line/src/card-command.ts +2 -2
  400. package/extensions/line/src/channel.logout.test.ts +4 -4
  401. package/extensions/line/src/channel.sendPayload.test.ts +8 -8
  402. package/extensions/line/src/channel.ts +3 -3
  403. package/extensions/llm-task/README.md +3 -3
  404. package/extensions/llm-task/index.ts +2 -2
  405. package/extensions/llm-task/package.json +1 -1
  406. package/extensions/llm-task/src/llm-task-tool.ts +4 -4
  407. package/extensions/lobster/README.md +6 -6
  408. package/extensions/lobster/index.ts +2 -2
  409. package/extensions/lobster/src/lobster-tool.test.ts +4 -4
  410. package/extensions/lobster/src/lobster-tool.ts +2 -2
  411. package/extensions/matrix/index.ts +2 -2
  412. package/extensions/matrix/package.json +1 -1
  413. package/extensions/matrix/src/matrix/client/config.ts +1 -1
  414. package/extensions/matrix/src/matrix/monitor/handler.ts +1 -1
  415. package/extensions/matrix/src/onboarding.ts +1 -1
  416. package/extensions/mattermost/index.ts +2 -2
  417. package/extensions/mattermost/package.json +1 -1
  418. package/extensions/mattermost/src/mattermost/accounts.ts +8 -8
  419. package/extensions/mattermost/src/mattermost/monitor-helpers.ts +5 -5
  420. package/extensions/mattermost/src/mattermost/monitor.ts +2 -2
  421. package/extensions/mattermost/src/onboarding-helpers.ts +3 -3
  422. package/extensions/mattermost/src/onboarding.ts +2 -2
  423. package/extensions/memory-core/index.ts +2 -2
  424. package/extensions/memory-core/package.json +1 -1
  425. package/extensions/memory-lancedb/index.ts +3 -3
  426. package/extensions/memory-lancedb/package.json +1 -1
  427. package/extensions/msteams/index.ts +2 -2
  428. package/extensions/msteams/package.json +1 -1
  429. package/extensions/msteams/src/channel.directory.test.ts +2 -2
  430. package/extensions/msteams/src/channel.ts +2 -2
  431. package/extensions/msteams/src/graph-upload.ts +4 -4
  432. package/extensions/msteams/src/monitor-handler.ts +2 -2
  433. package/extensions/msteams/src/monitor.ts +2 -2
  434. package/extensions/msteams/src/onboarding.ts +9 -9
  435. package/extensions/msteams/src/reply-dispatcher.ts +2 -2
  436. package/extensions/msteams/src/send-context.ts +2 -2
  437. package/extensions/msteams/src/send.ts +4 -4
  438. package/extensions/nextcloud-talk/index.ts +2 -2
  439. package/extensions/nextcloud-talk/package.json +1 -1
  440. package/extensions/nextcloud-talk/src/channel.ts +7 -7
  441. package/extensions/nextcloud-talk/src/inbound.ts +7 -7
  442. package/extensions/nextcloud-talk/src/onboarding.ts +1 -1
  443. package/extensions/nostr/README.md +2 -2
  444. package/extensions/nostr/index.ts +5 -5
  445. package/extensions/nostr/package.json +1 -1
  446. package/extensions/nostr/src/types.ts +4 -4
  447. package/extensions/open-prose/index.ts +2 -2
  448. package/extensions/qwen-portal-auth/README.md +1 -1
  449. package/extensions/signal/index.ts +2 -2
  450. package/extensions/signal/package.json +1 -1
  451. package/extensions/slack/index.ts +2 -2
  452. package/extensions/slack/package.json +1 -1
  453. package/extensions/telegram/index.ts +2 -2
  454. package/extensions/telegram/package.json +1 -1
  455. package/extensions/telegram/src/channel.ts +2 -2
  456. package/extensions/tlon/README.md +2 -2
  457. package/extensions/tlon/index.ts +2 -2
  458. package/extensions/tlon/package.json +1 -1
  459. package/extensions/tlon/src/channel.ts +13 -13
  460. package/extensions/tlon/src/monitor/index.ts +3 -3
  461. package/extensions/tlon/src/onboarding.ts +3 -3
  462. package/extensions/tlon/src/types.ts +3 -3
  463. package/extensions/twitch/README.md +1 -1
  464. package/extensions/twitch/index.ts +2 -2
  465. package/extensions/twitch/package.json +1 -1
  466. package/extensions/twitch/src/config.ts +3 -3
  467. package/extensions/twitch/src/monitor.ts +3 -3
  468. package/extensions/twitch/src/onboarding.ts +9 -9
  469. package/extensions/twitch/src/outbound.test.ts +2 -2
  470. package/extensions/twitch/src/plugin.test.ts +2 -2
  471. package/extensions/twitch/src/plugin.ts +8 -8
  472. package/extensions/twitch/src/send.test.ts +2 -2
  473. package/extensions/twitch/src/send.ts +4 -4
  474. package/extensions/twitch/src/token.test.ts +8 -8
  475. package/extensions/twitch/src/token.ts +3 -3
  476. package/extensions/twitch/src/twitch-client.ts +3 -3
  477. package/extensions/twitch/src/types.ts +3 -3
  478. package/extensions/twitch/src/utils/markdown.ts +1 -1
  479. package/extensions/voice-call/README.md +3 -3
  480. package/extensions/voice-call/package.json +1 -1
  481. package/extensions/voice-call/src/core-bridge.ts +2 -2
  482. package/extensions/voice-call/src/response-generator.ts +1 -1
  483. package/extensions/whatsapp/index.ts +2 -2
  484. package/extensions/whatsapp/package.json +1 -1
  485. package/extensions/zalo/README.md +1 -1
  486. package/extensions/zalo/index.ts +2 -2
  487. package/extensions/zalo/package.json +1 -1
  488. package/extensions/zalo/src/accounts.ts +8 -8
  489. package/extensions/zalo/src/actions.ts +4 -4
  490. package/extensions/zalo/src/channel.directory.test.ts +2 -2
  491. package/extensions/zalo/src/channel.ts +18 -18
  492. package/extensions/zalo/src/monitor.ts +9 -9
  493. package/extensions/zalo/src/monitor.webhook.test.ts +2 -2
  494. package/extensions/zalo/src/onboarding.ts +24 -24
  495. package/extensions/zalo/src/send.ts +2 -2
  496. package/extensions/zalouser/README.md +2 -2
  497. package/extensions/zalouser/index.ts +2 -2
  498. package/extensions/zalouser/package.json +1 -1
  499. package/extensions/zalouser/src/accounts.ts +9 -9
  500. package/extensions/zalouser/src/channel.ts +24 -24
  501. package/extensions/zalouser/src/monitor.ts +4 -4
  502. package/extensions/zalouser/src/onboarding.ts +28 -28
  503. package/package.json +13 -251
  504. package/skills/nano-banana-pro/scripts/generate_image.py +1 -1
  505. package/skills/tmux/scripts/find-sessions.sh +1 -1
  506. package/CHANGELOG.md +0 -102
  507. package/README-header.png +0 -0
  508. package/git-hooks/pre-commit +0 -4
  509. package/scripts/format-staged.js +0 -148
  510. package/scripts/postinstall.js +0 -300
  511. package/scripts/setup-git-hooks.js +0 -96
@@ -1,15 +1,16 @@
1
+ import JSON5 from "json5";
1
2
  import fs from "node:fs";
2
- import os from "node:os";
3
3
  import path from "node:path";
4
- import JSON5 from "json5";
4
+ import { expandHomePrefix } from "../infra/home-dir.js";
5
5
  import { CONFIG_DIR } from "../utils.js";
6
6
  export const DEFAULT_CRON_DIR = path.join(CONFIG_DIR, "cron");
7
7
  export const DEFAULT_CRON_STORE_PATH = path.join(DEFAULT_CRON_DIR, "jobs.json");
8
8
  export function resolveCronStorePath(storePath) {
9
9
  if (storePath?.trim()) {
10
10
  const raw = storePath.trim();
11
- if (raw.startsWith("~"))
12
- return path.resolve(raw.replace("~", os.homedir()));
11
+ if (raw.startsWith("~")) {
12
+ return path.resolve(expandHomePrefix(raw));
13
+ }
13
14
  return path.resolve(raw);
14
15
  }
15
16
  return DEFAULT_CRON_STORE_PATH;
@@ -17,15 +18,29 @@ export function resolveCronStorePath(storePath) {
17
18
  export async function loadCronStore(storePath) {
18
19
  try {
19
20
  const raw = await fs.promises.readFile(storePath, "utf-8");
20
- const parsed = JSON5.parse(raw);
21
- const jobs = Array.isArray(parsed?.jobs) ? parsed?.jobs : [];
21
+ let parsed;
22
+ try {
23
+ parsed = JSON5.parse(raw);
24
+ }
25
+ catch (err) {
26
+ throw new Error(`Failed to parse cron store at ${storePath}: ${String(err)}`, {
27
+ cause: err,
28
+ });
29
+ }
30
+ const parsedRecord = parsed && typeof parsed === "object" && !Array.isArray(parsed)
31
+ ? parsed
32
+ : {};
33
+ const jobs = Array.isArray(parsedRecord.jobs) ? parsedRecord.jobs : [];
22
34
  return {
23
35
  version: 1,
24
36
  jobs: jobs.filter(Boolean),
25
37
  };
26
38
  }
27
- catch {
28
- return { version: 1, jobs: [] };
39
+ catch (err) {
40
+ if (err?.code === "ENOENT") {
41
+ return { version: 1, jobs: [] };
42
+ }
43
+ throw err;
29
44
  }
30
45
  }
31
46
  export async function saveCronStore(storePath, store) {
@@ -0,0 +1,43 @@
1
+ import { parseAbsoluteTimeMs } from "./parse.js";
2
+ const ONE_MINUTE_MS = 60 * 1000;
3
+ const TEN_YEARS_MS = 10 * 365.25 * 24 * 60 * 60 * 1000;
4
+ /**
5
+ * Validates at timestamps in cron schedules.
6
+ * Rejects timestamps that are:
7
+ * - More than 1 minute in the past
8
+ * - More than 10 years in the future
9
+ */
10
+ export function validateScheduleTimestamp(schedule, nowMs = Date.now()) {
11
+ if (schedule.kind !== "at") {
12
+ return { ok: true };
13
+ }
14
+ const atRaw = typeof schedule.at === "string" ? schedule.at.trim() : "";
15
+ const atMs = atRaw ? parseAbsoluteTimeMs(atRaw) : null;
16
+ if (atMs === null || !Number.isFinite(atMs)) {
17
+ return {
18
+ ok: false,
19
+ message: `Invalid schedule.at: expected ISO-8601 timestamp (got ${String(schedule.at)})`,
20
+ };
21
+ }
22
+ const diffMs = atMs - nowMs;
23
+ // Check if timestamp is in the past (allow 1 minute grace period)
24
+ if (diffMs < -ONE_MINUTE_MS) {
25
+ const nowDate = new Date(nowMs).toISOString();
26
+ const atDate = new Date(atMs).toISOString();
27
+ const minutesAgo = Math.floor(-diffMs / ONE_MINUTE_MS);
28
+ return {
29
+ ok: false,
30
+ message: `schedule.at is in the past: ${atDate} (${minutesAgo} minutes ago). Current time: ${nowDate}`,
31
+ };
32
+ }
33
+ // Check if timestamp is too far in the future
34
+ if (diffMs > TEN_YEARS_MS) {
35
+ const atDate = new Date(atMs).toISOString();
36
+ const yearsAhead = Math.floor(diffMs / (365.25 * 24 * 60 * 60 * 1000));
37
+ return {
38
+ ok: false,
39
+ message: `schedule.at is too far in the future: ${atDate} (${yearsAhead} years ahead). Maximum allowed: 10 years`,
40
+ };
41
+ }
42
+ return { ok: true };
43
+ }
@@ -1,12 +1,12 @@
1
1
  // Default service labels (for backward compatibility and when no profile specified)
2
2
  export const GATEWAY_LAUNCH_AGENT_LABEL = "com.poolbot.gateway";
3
3
  export const GATEWAY_SYSTEMD_SERVICE_NAME = "poolbot-gateway";
4
- export const GATEWAY_WINDOWS_TASK_NAME = "Moltbot Gateway";
4
+ export const GATEWAY_WINDOWS_TASK_NAME = "Poolbot Gateway";
5
5
  export const GATEWAY_SERVICE_MARKER = "poolbot";
6
6
  export const GATEWAY_SERVICE_KIND = "gateway";
7
7
  export const NODE_LAUNCH_AGENT_LABEL = "com.poolbot.node";
8
8
  export const NODE_SYSTEMD_SERVICE_NAME = "poolbot-node";
9
- export const NODE_WINDOWS_TASK_NAME = "Moltbot Node";
9
+ export const NODE_WINDOWS_TASK_NAME = "Poolbot Node";
10
10
  export const NODE_SERVICE_MARKER = "poolbot";
11
11
  export const NODE_SERVICE_KIND = "node";
12
12
  export const NODE_WINDOWS_TASK_SCRIPT_NAME = "node.cmd";
@@ -40,7 +40,7 @@ export function resolveGatewayWindowsTaskName(profile) {
40
40
  const normalized = normalizeGatewayProfile(profile);
41
41
  if (!normalized)
42
42
  return GATEWAY_WINDOWS_TASK_NAME;
43
- return `Moltbot Gateway (${normalized})`;
43
+ return `Poolbot Gateway (${normalized})`;
44
44
  }
45
45
  export function formatGatewayServiceDescription(params) {
46
46
  const profile = normalizeGatewayProfile(params?.profile);
@@ -51,8 +51,8 @@ export function formatGatewayServiceDescription(params) {
51
51
  if (version)
52
52
  parts.push(`v${version}`);
53
53
  if (parts.length === 0)
54
- return "Moltbot Gateway";
55
- return `Moltbot Gateway (${parts.join(", ")})`;
54
+ return "Poolbot Gateway";
55
+ return `Poolbot Gateway (${parts.join(", ")})`;
56
56
  }
57
57
  export function resolveNodeLaunchAgentLabel() {
58
58
  return NODE_LAUNCH_AGENT_LABEL;
@@ -66,6 +66,6 @@ export function resolveNodeWindowsTaskName() {
66
66
  export function formatNodeServiceDescription(params) {
67
67
  const version = params?.version?.trim();
68
68
  if (!version)
69
- return "Moltbot Node Host";
70
- return `Moltbot Node Host (v${version})`;
69
+ return "Poolbot Node Host";
70
+ return `Poolbot Node Host (v${version})`;
71
71
  }
@@ -44,7 +44,7 @@ function hasGatewayServiceMarker(content) {
44
44
  lower.includes("poolbot_service_kind") &&
45
45
  lower.includes(GATEWAY_SERVICE_KIND.toLowerCase()));
46
46
  }
47
- function isMoltbotGatewayLaunchdService(label, contents) {
47
+ function isPoolbotGatewayLaunchdService(label, contents) {
48
48
  if (hasGatewayServiceMarker(contents))
49
49
  return true;
50
50
  const lowerContents = contents.toLowerCase();
@@ -52,14 +52,14 @@ function isMoltbotGatewayLaunchdService(label, contents) {
52
52
  return false;
53
53
  return label.startsWith("com.poolbot.");
54
54
  }
55
- function isMoltbotGatewaySystemdService(name, contents) {
55
+ function isPoolbotGatewaySystemdService(name, contents) {
56
56
  if (hasGatewayServiceMarker(contents))
57
57
  return true;
58
58
  if (!name.startsWith("poolbot-gateway"))
59
59
  return false;
60
60
  return contents.toLowerCase().includes("gateway");
61
61
  }
62
- function isMoltbotGatewayTaskName(name) {
62
+ function isPoolbotGatewayTaskName(name) {
63
63
  const normalized = name.trim().toLowerCase();
64
64
  if (!normalized)
65
65
  return false;
@@ -107,7 +107,7 @@ async function scanLaunchdDir(params) {
107
107
  const label = tryExtractPlistLabel(contents) ?? labelFromName;
108
108
  if (isIgnoredLaunchdLabel(label))
109
109
  continue;
110
- if (isMoltbotGatewayLaunchdService(label, contents))
110
+ if (isPoolbotGatewayLaunchdService(label, contents))
111
111
  continue;
112
112
  results.push({
113
113
  platform: "darwin",
@@ -143,7 +143,7 @@ async function scanSystemdDir(params) {
143
143
  }
144
144
  if (!containsMarker(contents))
145
145
  continue;
146
- if (isMoltbotGatewaySystemdService(name, contents))
146
+ if (isPoolbotGatewaySystemdService(name, contents))
147
147
  continue;
148
148
  results.push({
149
149
  platform: "linux",
@@ -291,7 +291,7 @@ export async function findExtraGatewayServices(env, opts = {}) {
291
291
  const name = task.name.trim();
292
292
  if (!name)
293
293
  continue;
294
- if (isMoltbotGatewayTaskName(name))
294
+ if (isPoolbotGatewayTaskName(name))
295
295
  continue;
296
296
  if (LEGACY_GATEWAY_WINDOWS_TASK_NAMES.includes(name))
297
297
  continue;
@@ -13,7 +13,7 @@ function renderEnvLines(env) {
13
13
  }
14
14
  export function buildSystemdUnit({ description, programArguments, workingDirectory, environment, }) {
15
15
  const execStart = programArguments.map(systemdEscapeArg).join(" ");
16
- const descriptionLine = `Description=${description?.trim() || "Moltbot Gateway"}`;
16
+ const descriptionLine = `Description=${description?.trim() || "Poolbot Gateway"}`;
17
17
  const workingDirLine = workingDirectory
18
18
  ? `WorkingDirectory=${systemdEscapeArg(workingDirectory)}`
19
19
  : null;
@@ -0,0 +1,438 @@
1
+ import { Button, StringSelectMenu, } from "@buape/carbon";
2
+ import { ButtonStyle, ChannelType } from "discord-api-types/v10";
3
+ import { logVerbose } from "../../globals.js";
4
+ import { enqueueSystemEvent } from "../../infra/system-events.js";
5
+ import { logDebug, logError } from "../../logger.js";
6
+ import { buildPairingReply } from "../../pairing/pairing-messages.js";
7
+ import { readChannelAllowFromStore, upsertChannelPairingRequest, } from "../../pairing/pairing-store.js";
8
+ import { resolveAgentRoute } from "../../routing/resolve-route.js";
9
+ import { normalizeDiscordAllowList, normalizeDiscordSlug, resolveDiscordAllowListMatch, resolveDiscordChannelConfigWithFallback, resolveDiscordGuildEntry, resolveDiscordUserAllowed, } from "./allow-list.js";
10
+ import { formatDiscordUserTag } from "./format.js";
11
+ const AGENT_BUTTON_KEY = "agent";
12
+ const AGENT_SELECT_KEY = "agentsel";
13
+ /**
14
+ * Build agent button custom ID: agent:componentId=<id>
15
+ * The channelId is NOT embedded in customId - we use interaction.rawData.channel_id instead
16
+ * to prevent channel spoofing attacks.
17
+ *
18
+ * Carbon's customIdParser parses "key:arg1=value1;arg2=value2" into { arg1: value1, arg2: value2 }
19
+ */
20
+ export function buildAgentButtonCustomId(componentId) {
21
+ return `${AGENT_BUTTON_KEY}:componentId=${encodeURIComponent(componentId)}`;
22
+ }
23
+ /**
24
+ * Build agent select menu custom ID: agentsel:componentId=<id>
25
+ */
26
+ export function buildAgentSelectCustomId(componentId) {
27
+ return `${AGENT_SELECT_KEY}:componentId=${encodeURIComponent(componentId)}`;
28
+ }
29
+ /**
30
+ * Parse agent component data from Carbon's parsed ComponentData
31
+ * Carbon parses "key:componentId=xxx" into { componentId: "xxx" }
32
+ */
33
+ function parseAgentComponentData(data) {
34
+ if (!data || typeof data !== "object") {
35
+ return null;
36
+ }
37
+ const componentId = typeof data.componentId === "string"
38
+ ? decodeURIComponent(data.componentId)
39
+ : typeof data.componentId === "number"
40
+ ? String(data.componentId)
41
+ : null;
42
+ if (!componentId) {
43
+ return null;
44
+ }
45
+ return { componentId };
46
+ }
47
+ function formatUsername(user) {
48
+ if (user.discriminator && user.discriminator !== "0") {
49
+ return `${user.username}#${user.discriminator}`;
50
+ }
51
+ return user.username;
52
+ }
53
+ /**
54
+ * Check if a channel type is a thread type
55
+ */
56
+ function isThreadChannelType(channelType) {
57
+ return (channelType === ChannelType.PublicThread ||
58
+ channelType === ChannelType.PrivateThread ||
59
+ channelType === ChannelType.AnnouncementThread);
60
+ }
61
+ async function ensureDmComponentAuthorized(params) {
62
+ const { ctx, interaction, user, componentLabel } = params;
63
+ const dmPolicy = ctx.dmPolicy ?? "pairing";
64
+ if (dmPolicy === "disabled") {
65
+ logVerbose(`agent ${componentLabel}: blocked (DM policy disabled)`);
66
+ try {
67
+ await interaction.reply({
68
+ content: "DM interactions are disabled.",
69
+ ephemeral: true,
70
+ });
71
+ }
72
+ catch {
73
+ // Interaction may have expired
74
+ }
75
+ return false;
76
+ }
77
+ if (dmPolicy === "open") {
78
+ return true;
79
+ }
80
+ const storeAllowFrom = await readChannelAllowFromStore("discord").catch(() => []);
81
+ const effectiveAllowFrom = [...(ctx.allowFrom ?? []), ...storeAllowFrom];
82
+ const allowList = normalizeDiscordAllowList(effectiveAllowFrom, ["discord:", "user:", "pk:"]);
83
+ const allowMatch = allowList
84
+ ? resolveDiscordAllowListMatch({
85
+ allowList,
86
+ candidate: {
87
+ id: user.id,
88
+ name: user.username,
89
+ tag: formatDiscordUserTag(user),
90
+ },
91
+ })
92
+ : { allowed: false };
93
+ if (allowMatch.allowed) {
94
+ return true;
95
+ }
96
+ if (dmPolicy === "pairing") {
97
+ const { code, created } = await upsertChannelPairingRequest({
98
+ channel: "discord",
99
+ id: user.id,
100
+ meta: {
101
+ tag: formatDiscordUserTag(user),
102
+ name: user.username,
103
+ },
104
+ });
105
+ try {
106
+ await interaction.reply({
107
+ content: created
108
+ ? buildPairingReply({
109
+ channel: "discord",
110
+ idLine: `Your Discord user id: ${user.id}`,
111
+ code,
112
+ })
113
+ : "Pairing already requested. Ask the bot owner to approve your code.",
114
+ ephemeral: true,
115
+ });
116
+ }
117
+ catch {
118
+ // Interaction may have expired
119
+ }
120
+ return false;
121
+ }
122
+ logVerbose(`agent ${componentLabel}: blocked DM user ${user.id} (not in allowFrom)`);
123
+ try {
124
+ await interaction.reply({
125
+ content: `You are not authorized to use this ${componentLabel}.`,
126
+ ephemeral: true,
127
+ });
128
+ }
129
+ catch {
130
+ // Interaction may have expired
131
+ }
132
+ return false;
133
+ }
134
+ export class AgentComponentButton extends Button {
135
+ label = AGENT_BUTTON_KEY;
136
+ customId = `${AGENT_BUTTON_KEY}:seed=1`;
137
+ style = ButtonStyle.Primary;
138
+ ctx;
139
+ constructor(ctx) {
140
+ super();
141
+ this.ctx = ctx;
142
+ }
143
+ async run(interaction, data) {
144
+ // Parse componentId from Carbon's parsed ComponentData
145
+ const parsed = parseAgentComponentData(data);
146
+ if (!parsed) {
147
+ logError("agent button: failed to parse component data");
148
+ try {
149
+ await interaction.reply({
150
+ content: "This button is no longer valid.",
151
+ ephemeral: true,
152
+ });
153
+ }
154
+ catch {
155
+ // Interaction may have expired
156
+ }
157
+ return;
158
+ }
159
+ const { componentId } = parsed;
160
+ // P1 FIX: Use interaction's actual channel_id instead of trusting customId
161
+ // This prevents channel ID spoofing attacks where an attacker crafts a button
162
+ // with a different channelId to inject events into other sessions
163
+ const channelId = interaction.rawData.channel_id;
164
+ if (!channelId) {
165
+ logError("agent button: missing channel_id in interaction");
166
+ return;
167
+ }
168
+ const user = interaction.user;
169
+ if (!user) {
170
+ logError("agent button: missing user in interaction");
171
+ return;
172
+ }
173
+ const username = formatUsername(user);
174
+ const userId = user.id;
175
+ // P1 FIX: Use rawData.guild_id as source of truth - interaction.guild can be null
176
+ // when guild is not cached even though guild_id is present in rawData
177
+ const rawGuildId = interaction.rawData.guild_id;
178
+ const isDirectMessage = !rawGuildId;
179
+ if (isDirectMessage) {
180
+ const authorized = await ensureDmComponentAuthorized({
181
+ ctx: this.ctx,
182
+ interaction,
183
+ user,
184
+ componentLabel: "button",
185
+ });
186
+ if (!authorized) {
187
+ return;
188
+ }
189
+ }
190
+ // P2 FIX: Check user allowlist before processing component interaction
191
+ // This prevents unauthorized users from injecting system events
192
+ const guild = interaction.guild;
193
+ const guildInfo = resolveDiscordGuildEntry({
194
+ guild: guild ?? undefined,
195
+ guildEntries: this.ctx.guildEntries,
196
+ });
197
+ // Resolve channel info for thread detection and allowlist inheritance
198
+ const channel = interaction.channel;
199
+ const channelName = channel && "name" in channel ? channel.name : undefined;
200
+ const channelSlug = channelName ? normalizeDiscordSlug(channelName) : "";
201
+ const channelType = channel && "type" in channel ? channel.type : undefined;
202
+ const isThread = isThreadChannelType(channelType);
203
+ // Resolve thread parent for allowlist inheritance
204
+ // Note: We can get parentId from channel but cannot fetch parent name without a client.
205
+ // The parentId alone enables ID-based parent config matching. Name-based matching
206
+ // requires the channel cache to have parent info available.
207
+ let parentId;
208
+ let parentName;
209
+ let parentSlug = "";
210
+ if (isThread && channel && "parentId" in channel) {
211
+ parentId = channel.parentId ?? undefined;
212
+ // Try to get parent name from channel's parent if available
213
+ if ("parent" in channel) {
214
+ const parent = channel.parent;
215
+ if (parent?.name) {
216
+ parentName = parent.name;
217
+ parentSlug = normalizeDiscordSlug(parentName);
218
+ }
219
+ }
220
+ }
221
+ // Only check guild allowlists if this is a guild interaction
222
+ if (rawGuildId) {
223
+ const channelConfig = resolveDiscordChannelConfigWithFallback({
224
+ guildInfo,
225
+ channelId,
226
+ channelName,
227
+ channelSlug,
228
+ parentId,
229
+ parentName,
230
+ parentSlug,
231
+ scope: isThread ? "thread" : "channel",
232
+ });
233
+ const channelUsers = channelConfig?.users ?? guildInfo?.users;
234
+ if (Array.isArray(channelUsers) && channelUsers.length > 0) {
235
+ const userOk = resolveDiscordUserAllowed({
236
+ allowList: channelUsers,
237
+ userId,
238
+ userName: user.username,
239
+ userTag: user.discriminator ? `${user.username}#${user.discriminator}` : undefined,
240
+ });
241
+ if (!userOk) {
242
+ logVerbose(`agent button: blocked user ${userId} (not in allowlist)`);
243
+ try {
244
+ await interaction.reply({
245
+ content: "You are not authorized to use this button.",
246
+ ephemeral: true,
247
+ });
248
+ }
249
+ catch {
250
+ // Interaction may have expired
251
+ }
252
+ return;
253
+ }
254
+ }
255
+ }
256
+ // Resolve route with full context (guildId, proper peer kind)
257
+ const route = resolveAgentRoute({
258
+ cfg: this.ctx.cfg,
259
+ channel: "discord",
260
+ accountId: this.ctx.accountId,
261
+ guildId: rawGuildId,
262
+ peer: {
263
+ kind: isDirectMessage ? "dm" : "channel",
264
+ id: isDirectMessage ? userId : channelId,
265
+ },
266
+ });
267
+ const eventText = `[Discord component: ${componentId} clicked by ${username} (${userId})]`;
268
+ logDebug(`agent button: enqueuing event for channel ${channelId}: ${eventText}`);
269
+ enqueueSystemEvent(eventText, {
270
+ sessionKey: route.sessionKey,
271
+ contextKey: `discord:agent-button:${channelId}:${componentId}:${userId}`,
272
+ });
273
+ // Acknowledge the interaction
274
+ try {
275
+ await interaction.reply({
276
+ content: "✓",
277
+ ephemeral: true,
278
+ });
279
+ }
280
+ catch (err) {
281
+ logError(`agent button: failed to acknowledge interaction: ${String(err)}`);
282
+ }
283
+ }
284
+ }
285
+ export class AgentSelectMenu extends StringSelectMenu {
286
+ customId = `${AGENT_SELECT_KEY}:seed=1`;
287
+ options = [];
288
+ ctx;
289
+ constructor(ctx) {
290
+ super();
291
+ this.ctx = ctx;
292
+ }
293
+ async run(interaction, data) {
294
+ // Parse componentId from Carbon's parsed ComponentData
295
+ const parsed = parseAgentComponentData(data);
296
+ if (!parsed) {
297
+ logError("agent select: failed to parse component data");
298
+ try {
299
+ await interaction.reply({
300
+ content: "This select menu is no longer valid.",
301
+ ephemeral: true,
302
+ });
303
+ }
304
+ catch {
305
+ // Interaction may have expired
306
+ }
307
+ return;
308
+ }
309
+ const { componentId } = parsed;
310
+ // Use interaction's actual channel_id (trusted source from Discord)
311
+ // This prevents channel spoofing attacks
312
+ const channelId = interaction.rawData.channel_id;
313
+ if (!channelId) {
314
+ logError("agent select: missing channel_id in interaction");
315
+ return;
316
+ }
317
+ const user = interaction.user;
318
+ if (!user) {
319
+ logError("agent select: missing user in interaction");
320
+ return;
321
+ }
322
+ const username = formatUsername(user);
323
+ const userId = user.id;
324
+ // P1 FIX: Use rawData.guild_id as source of truth - interaction.guild can be null
325
+ // when guild is not cached even though guild_id is present in rawData
326
+ const rawGuildId = interaction.rawData.guild_id;
327
+ const isDirectMessage = !rawGuildId;
328
+ if (isDirectMessage) {
329
+ const authorized = await ensureDmComponentAuthorized({
330
+ ctx: this.ctx,
331
+ interaction,
332
+ user,
333
+ componentLabel: "select menu",
334
+ });
335
+ if (!authorized) {
336
+ return;
337
+ }
338
+ }
339
+ // Check user allowlist before processing component interaction
340
+ const guild = interaction.guild;
341
+ const guildInfo = resolveDiscordGuildEntry({
342
+ guild: guild ?? undefined,
343
+ guildEntries: this.ctx.guildEntries,
344
+ });
345
+ // Resolve channel info for thread detection and allowlist inheritance
346
+ const channel = interaction.channel;
347
+ const channelName = channel && "name" in channel ? channel.name : undefined;
348
+ const channelSlug = channelName ? normalizeDiscordSlug(channelName) : "";
349
+ const channelType = channel && "type" in channel ? channel.type : undefined;
350
+ const isThread = isThreadChannelType(channelType);
351
+ // Resolve thread parent for allowlist inheritance
352
+ let parentId;
353
+ let parentName;
354
+ let parentSlug = "";
355
+ if (isThread && channel && "parentId" in channel) {
356
+ parentId = channel.parentId ?? undefined;
357
+ // Try to get parent name from channel's parent if available
358
+ if ("parent" in channel) {
359
+ const parent = channel.parent;
360
+ if (parent?.name) {
361
+ parentName = parent.name;
362
+ parentSlug = normalizeDiscordSlug(parentName);
363
+ }
364
+ }
365
+ }
366
+ // Only check guild allowlists if this is a guild interaction
367
+ if (rawGuildId) {
368
+ const channelConfig = resolveDiscordChannelConfigWithFallback({
369
+ guildInfo,
370
+ channelId,
371
+ channelName,
372
+ channelSlug,
373
+ parentId,
374
+ parentName,
375
+ parentSlug,
376
+ scope: isThread ? "thread" : "channel",
377
+ });
378
+ const channelUsers = channelConfig?.users ?? guildInfo?.users;
379
+ if (Array.isArray(channelUsers) && channelUsers.length > 0) {
380
+ const userOk = resolveDiscordUserAllowed({
381
+ allowList: channelUsers,
382
+ userId,
383
+ userName: user.username,
384
+ userTag: user.discriminator ? `${user.username}#${user.discriminator}` : undefined,
385
+ });
386
+ if (!userOk) {
387
+ logVerbose(`agent select: blocked user ${userId} (not in allowlist)`);
388
+ try {
389
+ await interaction.reply({
390
+ content: "You are not authorized to use this select menu.",
391
+ ephemeral: true,
392
+ });
393
+ }
394
+ catch {
395
+ // Interaction may have expired
396
+ }
397
+ return;
398
+ }
399
+ }
400
+ }
401
+ // Extract selected values
402
+ const values = interaction.values ?? [];
403
+ const valuesText = values.length > 0 ? ` (selected: ${values.join(", ")})` : "";
404
+ // Resolve route with full context (guildId, proper peer kind)
405
+ const route = resolveAgentRoute({
406
+ cfg: this.ctx.cfg,
407
+ channel: "discord",
408
+ accountId: this.ctx.accountId,
409
+ guildId: rawGuildId,
410
+ peer: {
411
+ kind: isDirectMessage ? "dm" : "channel",
412
+ id: isDirectMessage ? userId : channelId,
413
+ },
414
+ });
415
+ const eventText = `[Discord select menu: ${componentId} interacted by ${username} (${userId})${valuesText}]`;
416
+ logDebug(`agent select: enqueuing event for channel ${channelId}: ${eventText}`);
417
+ enqueueSystemEvent(eventText, {
418
+ sessionKey: route.sessionKey,
419
+ contextKey: `discord:agent-select:${channelId}:${componentId}:${userId}`,
420
+ });
421
+ // Acknowledge the interaction
422
+ try {
423
+ await interaction.reply({
424
+ content: "✓",
425
+ ephemeral: true,
426
+ });
427
+ }
428
+ catch (err) {
429
+ logError(`agent select: failed to acknowledge interaction: ${String(err)}`);
430
+ }
431
+ }
432
+ }
433
+ export function createAgentComponentButton(ctx) {
434
+ return new AgentComponentButton(ctx);
435
+ }
436
+ export function createAgentSelectMenu(ctx) {
437
+ return new AgentSelectMenu(ctx);
438
+ }