@poolzin/pool-bot 2026.2.11 → 2026.2.18

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 (535) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/agents/agent-scope.js +4 -0
  3. package/dist/agents/announce-idempotency.js +14 -0
  4. package/dist/agents/auth-profiles/usage.js +22 -0
  5. package/dist/agents/auth-profiles.js +1 -1
  6. package/dist/agents/auth-profiles.resolve-auth-profile-order.fixtures.js +23 -0
  7. package/dist/agents/bash-tools.exec-runtime.js +438 -0
  8. package/dist/agents/bash-tools.shared.js +6 -0
  9. package/dist/agents/cli-runner/reliability.js +61 -0
  10. package/dist/agents/cli-watchdog-defaults.js +11 -0
  11. package/dist/agents/command-poll-backoff.js +63 -0
  12. package/dist/agents/current-time.js +16 -0
  13. package/dist/agents/glob-pattern.js +42 -0
  14. package/dist/agents/memory-search.js +33 -0
  15. package/dist/agents/model-alias-lines.js +18 -0
  16. package/dist/agents/model-auth-label.js +61 -0
  17. package/dist/agents/model-fallback.js +59 -8
  18. package/dist/agents/models-config.e2e-harness.js +115 -0
  19. package/dist/agents/ollama-stream.js +11 -3
  20. package/dist/agents/openclaw-tools.js +135 -0
  21. package/dist/agents/pi-auth-json.js +118 -0
  22. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +147 -0
  23. package/dist/agents/pi-embedded-subscribe.e2e-harness.js +90 -0
  24. package/dist/agents/pi-embedded-subscribe.handlers.compaction.js +63 -0
  25. package/dist/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.js +30 -0
  26. package/dist/agents/pi-extensions/session-manager-runtime-registry.js +23 -0
  27. package/dist/agents/pi-tools.before-tool-call.js +145 -4
  28. package/dist/agents/pi-tools.js +29 -9
  29. package/dist/agents/pi-tools.policy.js +85 -92
  30. package/dist/agents/pi-tools.schema.js +54 -27
  31. package/dist/agents/queued-file-writer.js +22 -0
  32. package/dist/agents/sandbox/docker.js +133 -40
  33. package/dist/agents/sandbox/fs-bridge.js +146 -0
  34. package/dist/agents/sandbox/fs-paths.js +205 -0
  35. package/dist/agents/sandbox/hash.js +4 -0
  36. package/dist/agents/sandbox/validate-sandbox-security.js +157 -0
  37. package/dist/agents/sandbox-paths.js +3 -0
  38. package/dist/agents/sandbox-tool-policy.js +26 -0
  39. package/dist/agents/sanitize-for-prompt.js +18 -0
  40. package/dist/agents/session-dirs.js +20 -0
  41. package/dist/agents/session-write-lock.js +203 -39
  42. package/dist/agents/skills/filter.js +24 -0
  43. package/dist/agents/skills/tools-dir.js +9 -0
  44. package/dist/agents/skills-install-download.js +290 -0
  45. package/dist/agents/skills-install-output.js +30 -0
  46. package/dist/agents/skills-install.download-test-utils.js +36 -0
  47. package/dist/agents/skills.e2e-test-helpers.js +13 -0
  48. package/dist/agents/subagent-announce-queue.js +59 -15
  49. package/dist/agents/subagent-depth.js +137 -0
  50. package/dist/agents/subagent-registry.js +448 -96
  51. package/dist/agents/subagent-spawn.js +262 -0
  52. package/dist/agents/system-prompt.js +52 -10
  53. package/dist/agents/test-helpers/fast-tool-stubs.js +18 -0
  54. package/dist/agents/test-helpers/host-sandbox-fs-bridge.js +74 -0
  55. package/dist/agents/tool-display-common.js +782 -0
  56. package/dist/agents/tool-loop-detection.js +466 -0
  57. package/dist/agents/tool-policy.js +6 -0
  58. package/dist/agents/tools/image-tool.js +1 -1
  59. package/dist/agents/tools/sessions-access.js +178 -0
  60. package/dist/agents/tools/sessions-resolution.js +206 -0
  61. package/dist/agents/tools/subagents-tool.js +616 -0
  62. package/dist/agents/workspace-dir.js +18 -0
  63. package/dist/agents/workspace-dirs.js +14 -0
  64. package/dist/agents/workspace.js +70 -0
  65. package/dist/auto-reply/heartbeat-reply-payload.js +18 -0
  66. package/dist/auto-reply/reply/commands-export-session.js +163 -0
  67. package/dist/auto-reply/reply/commands-mesh.js +245 -0
  68. package/dist/auto-reply/reply/commands-setunset.js +28 -0
  69. package/dist/auto-reply/reply/commands-slash-parse.js +31 -0
  70. package/dist/auto-reply/reply/commands-system-prompt.js +117 -0
  71. package/dist/auto-reply/reply/directive-handling.levels.js +17 -0
  72. package/dist/auto-reply/reply/directive-handling.params.js +1 -0
  73. package/dist/auto-reply/reply/directive-parsing.js +36 -0
  74. package/dist/auto-reply/reply/dispatcher-registry.js +43 -0
  75. package/dist/auto-reply/reply/elevated-unavailable.js +20 -0
  76. package/dist/auto-reply/reply/post-compaction-audit.js +96 -0
  77. package/dist/auto-reply/reply/post-compaction-context.js +98 -0
  78. package/dist/auto-reply/reply/reply-delivery.js +92 -0
  79. package/dist/auto-reply/reply/session-reset-prompt.js +1 -0
  80. package/dist/auto-reply/reply/session-run-accounting.js +33 -0
  81. package/dist/auto-reply/reply.directive.directive-behavior.e2e-harness.js +115 -0
  82. package/dist/auto-reply/reply.directive.directive-behavior.e2e-mocks.js +12 -0
  83. package/dist/browser/bridge-auth-registry.js +26 -0
  84. package/dist/browser/client-actions-url.js +10 -0
  85. package/dist/browser/control-auth.js +73 -0
  86. package/dist/browser/csrf.js +64 -0
  87. package/dist/browser/http-auth.js +52 -0
  88. package/dist/browser/paths.js +37 -0
  89. package/dist/browser/proxy-files.js +32 -0
  90. package/dist/browser/pw-ai-state.js +7 -0
  91. package/dist/browser/resolved-config-refresh.js +42 -0
  92. package/dist/browser/routes/path-output.js +1 -0
  93. package/dist/browser/server-context.chrome-test-harness.js +20 -0
  94. package/dist/browser/server-middleware.js +31 -0
  95. package/dist/browser/test-port.js +16 -0
  96. package/dist/build-info.json +3 -3
  97. package/dist/canvas-host/file-resolver.js +43 -0
  98. package/dist/channels/account-summary.js +19 -0
  99. package/dist/channels/draft-stream-loop.js +77 -0
  100. package/dist/channels/plugins/account-helpers.js +26 -0
  101. package/dist/channels/telegram/allow-from.js +10 -0
  102. package/dist/cli/browser-cli-resize.js +22 -0
  103. package/dist/cli/browser-cli-shared.js +8 -0
  104. package/dist/cli/clawbot-cli.js +5 -0
  105. package/dist/cli/completion-cli.js +566 -0
  106. package/dist/cli/config-cli.js +63 -5
  107. package/dist/cli/daemon-cli/lifecycle-core.js +256 -0
  108. package/dist/cli/daemon-cli/register-service-commands.js +60 -0
  109. package/dist/cli/daemon-cli-compat.js +80 -0
  110. package/dist/cli/nodes-cli/pairing-render.js +26 -0
  111. package/dist/cli/program/action-reparse.js +17 -0
  112. package/dist/cli/program/command-registry.js +17 -0
  113. package/dist/cli/program/program-context.js +8 -0
  114. package/dist/cli/program/register.subclis.js +7 -0
  115. package/dist/cli/program/routes.js +233 -0
  116. package/dist/cli/qr-cli.js +132 -0
  117. package/dist/cli/requirements-test-fixtures.js +17 -0
  118. package/dist/cli/respawn-policy.js +4 -0
  119. package/dist/cli/shared/parse-port.js +18 -0
  120. package/dist/cli/skills-cli.format.js +241 -0
  121. package/dist/cli/update-cli/progress.js +121 -0
  122. package/dist/cli/update-cli/restart-helper.js +108 -0
  123. package/dist/cli/update-cli/shared.js +196 -0
  124. package/dist/cli/update-cli/status.js +97 -0
  125. package/dist/cli/update-cli/suppress-deprecations.js +17 -0
  126. package/dist/cli/update-cli/update-command.js +506 -0
  127. package/dist/cli/update-cli/wizard.js +130 -0
  128. package/dist/cli/update-cli.js +3 -9
  129. package/dist/cli/windows-argv.js +69 -0
  130. package/dist/commands/auth-choice-legacy.js +20 -0
  131. package/dist/commands/auth-choice.apply-helpers.js +8 -0
  132. package/dist/commands/channel-test-helpers.js +19 -0
  133. package/dist/commands/cleanup-plan.js +10 -0
  134. package/dist/commands/cleanup-utils.js +7 -0
  135. package/dist/commands/config-validation.js +15 -0
  136. package/dist/commands/doctor-completion.js +112 -0
  137. package/dist/commands/doctor-memory-search.js +119 -0
  138. package/dist/commands/doctor-session-locks.js +73 -0
  139. package/dist/commands/doctor.e2e-harness.js +364 -0
  140. package/dist/commands/gateway-presence.js +19 -0
  141. package/dist/commands/model-default.js +35 -0
  142. package/dist/commands/models/fallbacks-shared.js +102 -0
  143. package/dist/commands/models/shared.js +24 -0
  144. package/dist/commands/onboard-auth.config-gateways.js +64 -0
  145. package/dist/commands/onboard-auth.config-litellm.js +45 -0
  146. package/dist/commands/onboard-auth.config-shared.js +116 -0
  147. package/dist/commands/onboard-config.js +16 -0
  148. package/dist/commands/onboard-non-interactive.test-helpers.js +31 -0
  149. package/dist/commands/onboard-provider-auth-flags.js +136 -0
  150. package/dist/commands/openai-codex-oauth.js +40 -0
  151. package/dist/commands/test-runtime-config-helpers.js +21 -0
  152. package/dist/commands/test-wizard-helpers.js +68 -0
  153. package/dist/commands/vllm-setup.js +66 -0
  154. package/dist/compat/legacy-names.js +2 -0
  155. package/dist/config/backup-rotation.js +19 -0
  156. package/dist/config/env-preserve.js +122 -0
  157. package/dist/config/includes-scan.js +78 -0
  158. package/dist/config/plugins-allowlist.js +13 -0
  159. package/dist/config/schema.help.js +256 -0
  160. package/dist/config/schema.hints.js +189 -0
  161. package/dist/config/schema.irc.js +20 -0
  162. package/dist/config/schema.labels.js +317 -0
  163. package/dist/config/sessions/delivery-info.js +40 -0
  164. package/dist/config/types.irc.js +1 -0
  165. package/dist/config/zod-schema.agent-defaults.js +14 -0
  166. package/dist/config/zod-schema.agent-model.js +10 -0
  167. package/dist/config/zod-schema.agent-runtime.js +14 -0
  168. package/dist/config/zod-schema.allowdeny.js +35 -0
  169. package/dist/config/zod-schema.sensitive.js +4 -0
  170. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  171. package/dist/cron/isolated-agent/skills-snapshot.js +26 -0
  172. package/dist/cron/isolated-agent/subagent-followup.js +127 -0
  173. package/dist/cron/isolated-agent.mocks.js +12 -0
  174. package/dist/cron/isolated-agent.test-setup.js +22 -0
  175. package/dist/cron/legacy-delivery.js +43 -0
  176. package/dist/cron/webhook-url.js +22 -0
  177. package/dist/daemon/arg-split.js +40 -0
  178. package/dist/daemon/exec-file.js +23 -0
  179. package/dist/daemon/output.js +6 -0
  180. package/dist/daemon/runtime-format.js +31 -0
  181. package/dist/daemon/schtasks-exec.js +4 -0
  182. package/dist/daemon/service-audit.js +22 -0
  183. package/dist/discord/client.js +41 -0
  184. package/dist/discord/components-registry.js +57 -0
  185. package/dist/discord/components.js +816 -0
  186. package/dist/discord/guilds.js +12 -0
  187. package/dist/discord/monitor/gateway-plugin.js +48 -0
  188. package/dist/discord/monitor/presence.js +30 -0
  189. package/dist/discord/send.components.js +115 -0
  190. package/dist/discord/send.shared.js +4 -0
  191. package/dist/discord/ui.js +26 -0
  192. package/dist/discord/voice-message.js +254 -0
  193. package/dist/gateway/agent-event-assistant-text.js +5 -0
  194. package/dist/gateway/agent-prompt.js +33 -0
  195. package/dist/gateway/auth-rate-limit.js +136 -0
  196. package/dist/gateway/channel-health-monitor.js +114 -0
  197. package/dist/gateway/control-ui-contract.js +1 -0
  198. package/dist/gateway/control-ui-csp.js +15 -0
  199. package/dist/gateway/gateway-config-prompts.shared.js +25 -0
  200. package/dist/gateway/http-auth-helpers.js +18 -0
  201. package/dist/gateway/http-common.js +18 -0
  202. package/dist/gateway/http-endpoint-helpers.js +27 -0
  203. package/dist/gateway/node-invoke-sanitize.js +11 -0
  204. package/dist/gateway/node-invoke-system-run-approval.js +205 -0
  205. package/dist/gateway/probe-auth.js +21 -0
  206. package/dist/gateway/protocol/index.js +7 -2
  207. package/dist/gateway/protocol/schema/mesh.js +54 -0
  208. package/dist/gateway/protocol/schema/protocol-schemas.js +7 -0
  209. package/dist/gateway/protocol/schema.js +1 -0
  210. package/dist/gateway/server/ws-connection/auth-messages.js +54 -0
  211. package/dist/gateway/server-channels.js +11 -0
  212. package/dist/gateway/server-methods/attachment-normalize.js +16 -0
  213. package/dist/gateway/server-methods/base-hash.js +8 -0
  214. package/dist/gateway/server-methods/mesh.js +700 -0
  215. package/dist/gateway/server-methods/nodes.handlers.invoke-result.js +55 -0
  216. package/dist/gateway/server-methods/restart-request.js +13 -0
  217. package/dist/gateway/server-methods/validation.js +8 -0
  218. package/dist/gateway/server.agent.gateway-server-agent.mocks.js +35 -0
  219. package/dist/gateway/server.e2e-registry-helpers.js +1 -0
  220. package/dist/gateway/server.e2e-ws-harness.js +20 -0
  221. package/dist/gateway/test-helpers.js +2 -0
  222. package/dist/gateway/test-helpers.server.js +3 -1
  223. package/dist/gateway/test-http-response.js +12 -0
  224. package/dist/gateway/test-openai-responses-model.js +20 -0
  225. package/dist/gateway/test-temp-config.js +30 -0
  226. package/dist/gateway/test-with-server.js +32 -0
  227. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +46 -0
  228. package/dist/imessage/monitor/abort-handler.js +23 -0
  229. package/dist/imessage/monitor/inbound-processing.js +346 -0
  230. package/dist/imessage/monitor/parse-notification.js +64 -0
  231. package/dist/imessage/target-parsing-helpers.js +92 -0
  232. package/dist/infra/archive.js +244 -20
  233. package/dist/infra/detect-package-manager.js +26 -0
  234. package/dist/infra/exec-approvals-allowlist.js +257 -0
  235. package/dist/infra/exec-approvals-analysis.js +770 -0
  236. package/dist/infra/exec-approvals.js +13 -0
  237. package/dist/infra/file-lock.js +1 -0
  238. package/dist/infra/gemini-auth.js +39 -0
  239. package/dist/infra/heartbeat-active-hours.js +85 -0
  240. package/dist/infra/heartbeat-events-filter.js +50 -0
  241. package/dist/infra/heartbeat-runner.test-utils.js +39 -0
  242. package/dist/infra/http-body.js +265 -0
  243. package/dist/infra/install-package-dir.js +50 -0
  244. package/dist/infra/install-safe-path.js +49 -0
  245. package/dist/infra/json-files.js +49 -0
  246. package/dist/infra/jsonl-socket.js +52 -0
  247. package/dist/infra/map-size.js +14 -0
  248. package/dist/infra/net/hostname.js +7 -0
  249. package/dist/infra/npm-registry-spec.js +39 -0
  250. package/dist/infra/openclaw-root.js +109 -0
  251. package/dist/infra/outbound/delivery-queue.js +214 -0
  252. package/dist/infra/outbound/identity.js +23 -0
  253. package/dist/infra/outbound/message-action-params.js +307 -0
  254. package/dist/infra/outbound/tool-payload.js +21 -0
  255. package/dist/infra/package-json.js +23 -0
  256. package/dist/infra/pairing-files.js +19 -0
  257. package/dist/infra/pairing-token.js +9 -0
  258. package/dist/infra/path-prepend.js +51 -0
  259. package/dist/infra/path-safety.js +16 -0
  260. package/dist/infra/process-respawn.js +49 -0
  261. package/dist/infra/runtime-status.js +16 -0
  262. package/dist/infra/session-cost-usage.types.js +1 -0
  263. package/dist/infra/session-maintenance-warning.js +89 -0
  264. package/dist/infra/system-run-command.js +78 -0
  265. package/dist/infra/tmp-openclaw-dir.js +81 -0
  266. package/dist/infra/tmp-poolbot-dir.js +2 -0
  267. package/dist/infra/update-channels.js +19 -0
  268. package/dist/line/actions.js +45 -0
  269. package/dist/line/channel-access-token.js +9 -0
  270. package/dist/line/flex-templates/basic-cards.js +332 -0
  271. package/dist/line/flex-templates/common.js +18 -0
  272. package/dist/line/flex-templates/media-control-cards.js +453 -0
  273. package/dist/line/flex-templates/message.js +10 -0
  274. package/dist/line/flex-templates/schedule-cards.js +399 -0
  275. package/dist/line/flex-templates/types.js +1 -0
  276. package/dist/line/webhook-node.js +100 -0
  277. package/dist/line/webhook-utils.js +11 -0
  278. package/dist/logging/diagnostic-session-state.js +73 -0
  279. package/dist/logging/diagnostic.js +22 -0
  280. package/dist/logging/timestamps.js +14 -0
  281. package/dist/markdown/whatsapp.js +62 -0
  282. package/dist/media/base64.js +34 -0
  283. package/dist/media/local-roots.js +32 -0
  284. package/dist/media/outbound-attachment.js +10 -0
  285. package/dist/media/read-response-with-limit.js +41 -0
  286. package/dist/media/sniff-mime-from-base64.js +19 -0
  287. package/dist/media-understanding/audio-preflight.js +67 -0
  288. package/dist/media-understanding/fs.js +13 -0
  289. package/dist/media-understanding/output-extract.js +26 -0
  290. package/dist/media-understanding/providers/audio.test-helpers.js +34 -0
  291. package/dist/media-understanding/providers/google/inline-data.js +64 -0
  292. package/dist/media-understanding/providers/shared.js +7 -0
  293. package/dist/media-understanding/runner.entries.js +459 -0
  294. package/dist/memory/batch-error-utils.js +11 -0
  295. package/dist/memory/batch-http.js +27 -0
  296. package/dist/memory/batch-output.js +29 -0
  297. package/dist/memory/batch-runner.js +22 -0
  298. package/dist/memory/batch-upload.js +23 -0
  299. package/dist/memory/batch-utils.js +26 -0
  300. package/dist/memory/embeddings-debug.js +11 -0
  301. package/dist/memory/embeddings-remote-client.js +22 -0
  302. package/dist/memory/embeddings-remote-fetch.js +14 -0
  303. package/dist/memory/embeddings.js +36 -9
  304. package/dist/memory/hybrid.js +24 -5
  305. package/dist/memory/manager-embedding-ops.js +616 -0
  306. package/dist/memory/manager-sync-ops.js +953 -0
  307. package/dist/memory/manager.js +76 -28
  308. package/dist/memory/mmr.js +164 -0
  309. package/dist/memory/qmd-manager.js +1061 -0
  310. package/dist/memory/qmd-query-parser.js +107 -0
  311. package/dist/memory/qmd-scope.js +93 -0
  312. package/dist/memory/query-expansion.js +331 -0
  313. package/dist/memory/search-manager.js +0 -1
  314. package/dist/memory/sync-index.js +21 -0
  315. package/dist/memory/sync-progress.js +22 -0
  316. package/dist/memory/sync-stale.js +30 -0
  317. package/dist/memory/temporal-decay.js +119 -0
  318. package/dist/memory/test-embeddings-mock.js +16 -0
  319. package/dist/memory/test-manager-helpers.js +14 -0
  320. package/dist/memory/test-runtime-mocks.js +11 -0
  321. package/dist/node-host/invoke-browser.js +177 -0
  322. package/dist/node-host/invoke.js +685 -0
  323. package/dist/pairing/setup-code.js +285 -0
  324. package/dist/plugin-sdk/account-id.js +1 -0
  325. package/dist/plugin-sdk/agent-media-payload.js +13 -0
  326. package/dist/plugin-sdk/allow-from.js +47 -0
  327. package/dist/plugin-sdk/command-auth.js +23 -0
  328. package/dist/plugin-sdk/config-paths.js +9 -0
  329. package/dist/plugin-sdk/file-lock.js +116 -0
  330. package/dist/plugin-sdk/json-store.js +31 -0
  331. package/dist/plugin-sdk/onboarding.js +28 -0
  332. package/dist/plugin-sdk/provider-auth-result.js +29 -0
  333. package/dist/plugin-sdk/slack-message-actions.js +133 -0
  334. package/dist/plugin-sdk/status-helpers.js +35 -0
  335. package/dist/plugin-sdk/text-chunking.js +31 -0
  336. package/dist/plugin-sdk/tool-send.js +12 -0
  337. package/dist/plugin-sdk/webhook-path.js +27 -0
  338. package/dist/plugin-sdk/webhook-targets.js +34 -0
  339. package/dist/plugins/hooks.test-helpers.js +21 -0
  340. package/dist/plugins/uninstall.js +171 -0
  341. package/dist/process/kill-tree.js +98 -0
  342. package/dist/process/supervisor/adapters/child.js +143 -0
  343. package/dist/process/supervisor/adapters/env.js +13 -0
  344. package/dist/process/supervisor/adapters/pty.js +148 -0
  345. package/dist/process/supervisor/index.js +10 -0
  346. package/dist/process/supervisor/registry.js +117 -0
  347. package/dist/process/supervisor/supervisor.js +244 -0
  348. package/dist/process/supervisor/types.js +1 -0
  349. package/dist/providers/google-shared.test-helpers.js +75 -0
  350. package/dist/security/audit-channel.js +419 -0
  351. package/dist/security/audit-tool-policy.js +1 -0
  352. package/dist/security/scan-paths.js +12 -0
  353. package/dist/sessions/input-provenance.js +55 -0
  354. package/dist/sessions/session-key-utils.js +7 -0
  355. package/dist/shared/chat-content.js +31 -0
  356. package/dist/shared/chat-envelope.js +45 -0
  357. package/dist/shared/config-eval.js +117 -0
  358. package/dist/shared/device-auth.js +16 -0
  359. package/dist/shared/entry-metadata.js +9 -0
  360. package/dist/shared/entry-status.js +25 -0
  361. package/dist/shared/frontmatter.js +98 -0
  362. package/dist/shared/model-param-b.js +19 -0
  363. package/dist/shared/net/ipv4.js +17 -0
  364. package/dist/shared/node-match.js +53 -0
  365. package/dist/shared/pid-alive.js +12 -0
  366. package/dist/shared/process-scoped-map.js +10 -0
  367. package/dist/shared/requirements.js +128 -0
  368. package/dist/shared/subagents-format.js +84 -0
  369. package/dist/shared/usage-aggregates.js +28 -0
  370. package/dist/signal/monitor/mentions.js +45 -0
  371. package/dist/signal/rpc-context.js +19 -0
  372. package/dist/slack/blocks-fallback.js +76 -0
  373. package/dist/slack/blocks-input.js +40 -0
  374. package/dist/slack/draft-stream.js +106 -0
  375. package/dist/slack/message-actions.js +51 -0
  376. package/dist/slack/modal-metadata.js +32 -0
  377. package/dist/slack/monitor/events/interactions.js +462 -0
  378. package/dist/slack/monitor/room-context.js +17 -0
  379. package/dist/slack/stream-mode.js +41 -0
  380. package/dist/telegram/bot-native-command-menu.js +64 -0
  381. package/dist/telegram/bot.media.e2e-harness.js +81 -0
  382. package/dist/telegram/button-types.js +1 -0
  383. package/dist/telegram/group-access.js +65 -0
  384. package/dist/telegram/outbound-params.js +21 -0
  385. package/dist/telegram/poll-vote-cache.js +21 -0
  386. package/dist/terminal/health-style.js +36 -0
  387. package/dist/test-utils/chunk-test-helpers.js +21 -0
  388. package/dist/test-utils/env.js +72 -0
  389. package/dist/test-utils/exec-assertions.js +12 -0
  390. package/dist/test-utils/imessage-test-plugin.js +54 -0
  391. package/dist/test-utils/mock-http-response.js +17 -0
  392. package/dist/test-utils/vitest-mock-fn.js +1 -0
  393. package/dist/tts/tts-core.js +550 -0
  394. package/dist/utils/chunk-items.js +10 -0
  395. package/dist/utils/reaction-level.js +52 -0
  396. package/dist/utils/safe-json.js +22 -0
  397. package/dist/utils/with-timeout.js +14 -0
  398. package/dist/web/media.js +17 -5
  399. package/dist/whatsapp/resolve-outbound-target.js +42 -0
  400. package/dist/wizard/onboarding.completion.js +74 -0
  401. package/extensions/bluebubbles/package.json +1 -1
  402. package/extensions/bluebubbles/src/account-resolve.ts +29 -0
  403. package/extensions/bluebubbles/src/monitor-normalize.ts +796 -0
  404. package/extensions/bluebubbles/src/monitor-processing.ts +1007 -0
  405. package/extensions/bluebubbles/src/monitor-reply-cache.ts +185 -0
  406. package/extensions/bluebubbles/src/monitor-shared.ts +51 -0
  407. package/extensions/bluebubbles/src/multipart.ts +32 -0
  408. package/extensions/bluebubbles/src/send-helpers.ts +53 -0
  409. package/extensions/bluebubbles/src/test-harness.ts +50 -0
  410. package/extensions/bluebubbles/src/test-mocks.ts +11 -0
  411. package/extensions/copilot-proxy/package.json +1 -1
  412. package/extensions/device-pair/index.ts +554 -0
  413. package/extensions/diagnostics-otel/package.json +1 -1
  414. package/extensions/discord/package.json +1 -1
  415. package/extensions/discord/src/channel.js +366 -0
  416. package/extensions/discord/src/runtime.js +10 -0
  417. package/extensions/feishu/index.ts +63 -0
  418. package/extensions/feishu/src/accounts.ts +114 -0
  419. package/extensions/feishu/src/bitable.ts +739 -0
  420. package/extensions/feishu/src/bot.ts +965 -0
  421. package/extensions/feishu/src/channel.ts +351 -0
  422. package/extensions/feishu/src/client.ts +118 -0
  423. package/extensions/feishu/src/config-schema.ts +206 -0
  424. package/extensions/feishu/src/dedup.ts +33 -0
  425. package/extensions/feishu/src/directory.ts +177 -0
  426. package/extensions/feishu/src/doc-schema.ts +47 -0
  427. package/extensions/feishu/src/docx.ts +536 -0
  428. package/extensions/feishu/src/drive-schema.ts +46 -0
  429. package/extensions/feishu/src/drive.ts +227 -0
  430. package/extensions/feishu/src/dynamic-agent.ts +131 -0
  431. package/extensions/feishu/src/media.ts +449 -0
  432. package/extensions/feishu/src/mention.ts +126 -0
  433. package/extensions/feishu/src/monitor.ts +330 -0
  434. package/extensions/feishu/src/onboarding.ts +359 -0
  435. package/extensions/feishu/src/outbound.ts +55 -0
  436. package/extensions/feishu/src/perm-schema.ts +52 -0
  437. package/extensions/feishu/src/perm.ts +173 -0
  438. package/extensions/feishu/src/policy.ts +84 -0
  439. package/extensions/feishu/src/probe.ts +44 -0
  440. package/extensions/feishu/src/reactions.ts +160 -0
  441. package/extensions/feishu/src/reply-dispatcher.ts +239 -0
  442. package/extensions/feishu/src/runtime.ts +14 -0
  443. package/extensions/feishu/src/send-result.ts +29 -0
  444. package/extensions/feishu/src/send.ts +335 -0
  445. package/extensions/feishu/src/streaming-card.ts +223 -0
  446. package/extensions/feishu/src/targets.ts +78 -0
  447. package/extensions/feishu/src/tools-config.ts +21 -0
  448. package/extensions/feishu/src/types.ts +81 -0
  449. package/extensions/feishu/src/typing.ts +80 -0
  450. package/extensions/feishu/src/wiki-schema.ts +55 -0
  451. package/extensions/feishu/src/wiki.ts +232 -0
  452. package/extensions/google-antigravity-auth/package.json +1 -1
  453. package/extensions/google-gemini-cli-auth/package.json +1 -1
  454. package/extensions/googlechat/package.json +1 -1
  455. package/extensions/imessage/package.json +1 -1
  456. package/extensions/imessage/src/channel.js +253 -0
  457. package/extensions/imessage/src/runtime.js +10 -0
  458. package/extensions/irc/index.ts +17 -0
  459. package/extensions/irc/src/accounts.ts +268 -0
  460. package/extensions/irc/src/channel.ts +367 -0
  461. package/extensions/irc/src/client.ts +439 -0
  462. package/extensions/irc/src/config-schema.ts +97 -0
  463. package/extensions/irc/src/connect-options.ts +30 -0
  464. package/extensions/irc/src/control-chars.ts +22 -0
  465. package/extensions/irc/src/inbound.ts +334 -0
  466. package/extensions/irc/src/monitor.ts +147 -0
  467. package/extensions/irc/src/normalize.ts +117 -0
  468. package/extensions/irc/src/onboarding.ts +479 -0
  469. package/extensions/irc/src/policy.ts +157 -0
  470. package/extensions/irc/src/probe.ts +53 -0
  471. package/extensions/irc/src/protocol.ts +169 -0
  472. package/extensions/irc/src/runtime.ts +14 -0
  473. package/extensions/irc/src/send.ts +88 -0
  474. package/extensions/irc/src/types.ts +93 -0
  475. package/extensions/line/package.json +1 -1
  476. package/extensions/llm-task/package.json +1 -1
  477. package/extensions/lobster/package.json +1 -1
  478. package/extensions/matrix/CHANGELOG.md +5 -0
  479. package/extensions/matrix/package.json +1 -1
  480. package/extensions/matrix/src/matrix/client-bootstrap.ts +39 -0
  481. package/extensions/mattermost/package.json +1 -1
  482. package/extensions/mattermost/src/mattermost/monitor-onchar.ts +25 -0
  483. package/extensions/mattermost/src/mattermost/monitor-websocket.ts +221 -0
  484. package/extensions/mattermost/src/mattermost/reactions.ts +130 -0
  485. package/extensions/mattermost/src/mattermost/reconnect.ts +103 -0
  486. package/extensions/memory-core/package.json +1 -1
  487. package/extensions/memory-lancedb/package.json +1 -1
  488. package/extensions/minimax-portal-auth/index.ts +161 -0
  489. package/extensions/minimax-portal-auth/oauth.ts +247 -0
  490. package/extensions/msteams/CHANGELOG.md +5 -0
  491. package/extensions/msteams/package.json +1 -1
  492. package/extensions/msteams/src/file-lock.ts +1 -0
  493. package/extensions/msteams/src/graph.ts +92 -0
  494. package/extensions/msteams/src/mentions.ts +114 -0
  495. package/extensions/msteams/src/test-runtime.ts +16 -0
  496. package/extensions/nextcloud-talk/package.json +1 -1
  497. package/extensions/nostr/CHANGELOG.md +5 -0
  498. package/extensions/nostr/package.json +1 -1
  499. package/extensions/open-prose/package.json +1 -1
  500. package/extensions/openai-codex-auth/index.ts +177 -0
  501. package/extensions/phone-control/index.ts +421 -0
  502. package/extensions/shared/resolve-target-test-helpers.ts +66 -0
  503. package/extensions/signal/package.json +1 -1
  504. package/extensions/signal/src/channel.js +273 -0
  505. package/extensions/signal/src/runtime.js +10 -0
  506. package/extensions/slack/package.json +1 -1
  507. package/extensions/slack/src/channel.js +489 -0
  508. package/extensions/slack/src/runtime.js +10 -0
  509. package/extensions/talk-voice/index.ts +150 -0
  510. package/extensions/telegram/package.json +1 -1
  511. package/extensions/telegram/src/channel.js +424 -0
  512. package/extensions/telegram/src/runtime.js +10 -0
  513. package/extensions/thread-ownership/index.ts +133 -0
  514. package/extensions/tlon/package.json +1 -1
  515. package/extensions/tlon/src/account-fields.ts +25 -0
  516. package/extensions/tlon/src/urbit/base-url.ts +57 -0
  517. package/extensions/tlon/src/urbit/channel-client.ts +157 -0
  518. package/extensions/tlon/src/urbit/channel-ops.ts +164 -0
  519. package/extensions/tlon/src/urbit/context.ts +47 -0
  520. package/extensions/tlon/src/urbit/errors.ts +51 -0
  521. package/extensions/tlon/src/urbit/fetch.ts +39 -0
  522. package/extensions/twitch/CHANGELOG.md +5 -0
  523. package/extensions/twitch/package.json +1 -1
  524. package/extensions/twitch/src/test-fixtures.ts +30 -0
  525. package/extensions/voice-call/CHANGELOG.md +5 -0
  526. package/extensions/voice-call/package.json +1 -1
  527. package/extensions/voice-call/src/allowlist.ts +19 -0
  528. package/extensions/whatsapp/package.json +1 -1
  529. package/extensions/whatsapp/src/channel.js +429 -0
  530. package/extensions/whatsapp/src/runtime.js +10 -0
  531. package/extensions/zalo/CHANGELOG.md +5 -0
  532. package/extensions/zalo/package.json +1 -1
  533. package/extensions/zalouser/CHANGELOG.md +5 -0
  534. package/extensions/zalouser/package.json +1 -1
  535. package/package.json +1 -1
@@ -0,0 +1,462 @@
1
+ import { enqueueSystemEvent } from "../../../infra/system-events.js";
2
+ import { parseSlackModalPrivateMetadata } from "../../modal-metadata.js";
3
+ // Prefix for OpenClaw-generated action IDs to scope our handler
4
+ const POOLBOT_ACTION_PREFIX = "openclaw:";
5
+ function readOptionValues(options) {
6
+ if (!Array.isArray(options)) {
7
+ return undefined;
8
+ }
9
+ const values = options
10
+ .map((option) => (option && typeof option === "object" ? option.value : null))
11
+ .filter((value) => typeof value === "string" && value.trim().length > 0);
12
+ return values.length > 0 ? values : undefined;
13
+ }
14
+ function readOptionLabels(options) {
15
+ if (!Array.isArray(options)) {
16
+ return undefined;
17
+ }
18
+ const labels = options
19
+ .map((option) => option && typeof option === "object" ? (option.text?.text ?? null) : null)
20
+ .filter((label) => typeof label === "string" && label.trim().length > 0);
21
+ return labels.length > 0 ? labels : undefined;
22
+ }
23
+ function uniqueNonEmptyStrings(values) {
24
+ const unique = [];
25
+ const seen = new Set();
26
+ for (const entry of values) {
27
+ if (typeof entry !== "string") {
28
+ continue;
29
+ }
30
+ const trimmed = entry.trim();
31
+ if (!trimmed || seen.has(trimmed)) {
32
+ continue;
33
+ }
34
+ seen.add(trimmed);
35
+ unique.push(trimmed);
36
+ }
37
+ return unique;
38
+ }
39
+ function escapeSlackMrkdwn(value) {
40
+ return value
41
+ .replaceAll("\\", "\\\\")
42
+ .replaceAll("&", "&")
43
+ .replaceAll("<", "&lt;")
44
+ .replaceAll(">", "&gt;")
45
+ .replace(/([*_`~])/g, "\\$1");
46
+ }
47
+ function collectRichTextFragments(value, out) {
48
+ if (!value || typeof value !== "object") {
49
+ return;
50
+ }
51
+ const typed = value;
52
+ if (typeof typed.text === "string" && typed.text.trim().length > 0) {
53
+ out.push(typed.text.trim());
54
+ }
55
+ if (Array.isArray(typed.elements)) {
56
+ for (const child of typed.elements) {
57
+ collectRichTextFragments(child, out);
58
+ }
59
+ }
60
+ }
61
+ function summarizeRichTextPreview(value) {
62
+ const fragments = [];
63
+ collectRichTextFragments(value, fragments);
64
+ if (fragments.length === 0) {
65
+ return undefined;
66
+ }
67
+ const joined = fragments.join(" ").replace(/\s+/g, " ").trim();
68
+ if (!joined) {
69
+ return undefined;
70
+ }
71
+ const max = 120;
72
+ return joined.length <= max ? joined : `${joined.slice(0, max - 1)}…`;
73
+ }
74
+ function readInteractionAction(raw) {
75
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
76
+ return undefined;
77
+ }
78
+ return raw;
79
+ }
80
+ function summarizeAction(action) {
81
+ const typed = action;
82
+ const actionType = typed.type;
83
+ const selectedUsers = uniqueNonEmptyStrings([
84
+ ...(typed.selected_user ? [typed.selected_user] : []),
85
+ ...(Array.isArray(typed.selected_users) ? typed.selected_users : []),
86
+ ]);
87
+ const selectedChannels = uniqueNonEmptyStrings([
88
+ ...(typed.selected_channel ? [typed.selected_channel] : []),
89
+ ...(Array.isArray(typed.selected_channels) ? typed.selected_channels : []),
90
+ ]);
91
+ const selectedConversations = uniqueNonEmptyStrings([
92
+ ...(typed.selected_conversation ? [typed.selected_conversation] : []),
93
+ ...(Array.isArray(typed.selected_conversations) ? typed.selected_conversations : []),
94
+ ]);
95
+ const selectedValues = uniqueNonEmptyStrings([
96
+ ...(typed.selected_option?.value ? [typed.selected_option.value] : []),
97
+ ...(readOptionValues(typed.selected_options) ?? []),
98
+ ...selectedUsers,
99
+ ...selectedChannels,
100
+ ...selectedConversations,
101
+ ]);
102
+ const selectedLabels = uniqueNonEmptyStrings([
103
+ ...(typed.selected_option?.text?.text ? [typed.selected_option.text.text] : []),
104
+ ...(readOptionLabels(typed.selected_options) ?? []),
105
+ ]);
106
+ const inputValue = typeof typed.value === "string" ? typed.value : undefined;
107
+ const inputNumber = actionType === "number_input" && inputValue != null ? Number.parseFloat(inputValue) : undefined;
108
+ const parsedNumber = Number.isFinite(inputNumber) ? inputNumber : undefined;
109
+ const inputEmail = actionType === "email_text_input" && inputValue?.includes("@") ? inputValue : undefined;
110
+ let inputUrl;
111
+ if (actionType === "url_text_input" && inputValue) {
112
+ try {
113
+ // Normalize to a canonical URL string so downstream handlers do not need to reparse.
114
+ inputUrl = new URL(inputValue).toString();
115
+ }
116
+ catch {
117
+ inputUrl = undefined;
118
+ }
119
+ }
120
+ const richTextValue = actionType === "rich_text_input" ? typed.rich_text_value : undefined;
121
+ const richTextPreview = summarizeRichTextPreview(richTextValue);
122
+ const inputKind = actionType === "number_input"
123
+ ? "number"
124
+ : actionType === "email_text_input"
125
+ ? "email"
126
+ : actionType === "url_text_input"
127
+ ? "url"
128
+ : actionType === "rich_text_input"
129
+ ? "rich_text"
130
+ : inputValue != null
131
+ ? "text"
132
+ : undefined;
133
+ return {
134
+ actionType,
135
+ inputKind,
136
+ value: typed.value,
137
+ selectedValues: selectedValues.length > 0 ? selectedValues : undefined,
138
+ selectedUsers: selectedUsers.length > 0 ? selectedUsers : undefined,
139
+ selectedChannels: selectedChannels.length > 0 ? selectedChannels : undefined,
140
+ selectedConversations: selectedConversations.length > 0 ? selectedConversations : undefined,
141
+ selectedLabels: selectedLabels.length > 0 ? selectedLabels : undefined,
142
+ selectedDate: typed.selected_date,
143
+ selectedTime: typed.selected_time,
144
+ selectedDateTime: typeof typed.selected_date_time === "number" ? typed.selected_date_time : undefined,
145
+ inputValue,
146
+ inputNumber: parsedNumber,
147
+ inputEmail,
148
+ inputUrl,
149
+ richTextValue,
150
+ richTextPreview,
151
+ workflowTriggerUrl: typed.workflow?.trigger_url,
152
+ workflowId: typed.workflow?.workflow_id,
153
+ };
154
+ }
155
+ function isBulkActionsBlock(block) {
156
+ return (block.type === "actions" &&
157
+ Array.isArray(block.elements) &&
158
+ block.elements.length > 0 &&
159
+ block.elements.every((el) => typeof el.action_id === "string" && el.action_id.includes("_all_")));
160
+ }
161
+ function formatInteractionSelectionLabel(params) {
162
+ if (params.summary.actionType === "button" && params.buttonText?.trim()) {
163
+ return params.buttonText.trim();
164
+ }
165
+ if (params.summary.selectedLabels?.length) {
166
+ if (params.summary.selectedLabels.length <= 3) {
167
+ return params.summary.selectedLabels.join(", ");
168
+ }
169
+ return `${params.summary.selectedLabels.slice(0, 3).join(", ")} +${params.summary.selectedLabels.length - 3}`;
170
+ }
171
+ if (params.summary.selectedValues?.length) {
172
+ if (params.summary.selectedValues.length <= 3) {
173
+ return params.summary.selectedValues.join(", ");
174
+ }
175
+ return `${params.summary.selectedValues.slice(0, 3).join(", ")} +${params.summary.selectedValues.length - 3}`;
176
+ }
177
+ if (params.summary.selectedDate) {
178
+ return params.summary.selectedDate;
179
+ }
180
+ if (params.summary.selectedTime) {
181
+ return params.summary.selectedTime;
182
+ }
183
+ if (typeof params.summary.selectedDateTime === "number") {
184
+ return new Date(params.summary.selectedDateTime * 1000).toISOString();
185
+ }
186
+ if (params.summary.richTextPreview) {
187
+ return params.summary.richTextPreview;
188
+ }
189
+ if (params.summary.value?.trim()) {
190
+ return params.summary.value.trim();
191
+ }
192
+ return params.actionId;
193
+ }
194
+ function formatInteractionConfirmationText(params) {
195
+ const actor = params.userId?.trim() ? ` by <@${params.userId.trim()}>` : "";
196
+ return `:white_check_mark: *${escapeSlackMrkdwn(params.selectedLabel)}* selected${actor}`;
197
+ }
198
+ function summarizeViewState(values) {
199
+ if (!values || typeof values !== "object") {
200
+ return [];
201
+ }
202
+ const entries = [];
203
+ for (const [blockId, blockValue] of Object.entries(values)) {
204
+ if (!blockValue || typeof blockValue !== "object") {
205
+ continue;
206
+ }
207
+ for (const [actionId, rawAction] of Object.entries(blockValue)) {
208
+ if (!rawAction || typeof rawAction !== "object") {
209
+ continue;
210
+ }
211
+ const actionSummary = summarizeAction(rawAction);
212
+ entries.push({
213
+ blockId,
214
+ actionId,
215
+ ...actionSummary,
216
+ });
217
+ }
218
+ }
219
+ return entries;
220
+ }
221
+ function resolveModalSessionRouting(params) {
222
+ const metadata = parseSlackModalPrivateMetadata(params.privateMetadata);
223
+ if (metadata.sessionKey) {
224
+ return { sessionKey: metadata.sessionKey };
225
+ }
226
+ if (metadata.channelId) {
227
+ return {
228
+ sessionKey: params.ctx.resolveSlackSystemEventSessionKey({
229
+ channelId: metadata.channelId,
230
+ channelType: metadata.channelType,
231
+ }),
232
+ channelId: metadata.channelId,
233
+ channelType: metadata.channelType,
234
+ };
235
+ }
236
+ return {
237
+ sessionKey: params.ctx.resolveSlackSystemEventSessionKey({}),
238
+ };
239
+ }
240
+ function summarizeSlackViewLifecycleContext(view) {
241
+ const rootViewId = view.root_view_id;
242
+ const previousViewId = view.previous_view_id;
243
+ const externalId = view.external_id;
244
+ const viewHash = view.hash;
245
+ return {
246
+ rootViewId,
247
+ previousViewId,
248
+ externalId,
249
+ viewHash,
250
+ isStackedView: Boolean(previousViewId),
251
+ };
252
+ }
253
+ export function registerSlackInteractionEvents(params) {
254
+ const { ctx } = params;
255
+ if (typeof ctx.app.action !== "function") {
256
+ return;
257
+ }
258
+ // Handle Block Kit button clicks from OpenClaw-generated messages
259
+ // Only matches action_ids that start with our prefix to avoid interfering
260
+ // with other Slack integrations or future features
261
+ ctx.app.action(new RegExp(`^${POOLBOT_ACTION_PREFIX}`), async (args) => {
262
+ const { ack, body, action, respond } = args;
263
+ const typedBody = body;
264
+ // Acknowledge the action immediately to prevent the warning icon
265
+ await ack();
266
+ // Extract action details using proper Bolt types
267
+ const typedAction = readInteractionAction(action);
268
+ if (!typedAction) {
269
+ ctx.runtime.log?.(`slack:interaction malformed action payload channel=${typedBody.channel?.id ?? typedBody.container?.channel_id ?? "unknown"} user=${typedBody.user?.id ?? "unknown"}`);
270
+ return;
271
+ }
272
+ const typedActionWithText = typedAction;
273
+ const actionId = typeof typedActionWithText.action_id === "string"
274
+ ? typedActionWithText.action_id
275
+ : "unknown";
276
+ const blockId = typedActionWithText.block_id;
277
+ const userId = typedBody.user?.id ?? "unknown";
278
+ const channelId = typedBody.channel?.id ?? typedBody.container?.channel_id;
279
+ const messageTs = typedBody.message?.ts ?? typedBody.container?.message_ts;
280
+ const threadTs = typedBody.container?.thread_ts;
281
+ const actionSummary = summarizeAction(typedAction);
282
+ const eventPayload = {
283
+ interactionType: "block_action",
284
+ actionId,
285
+ blockId,
286
+ ...actionSummary,
287
+ userId,
288
+ teamId: typedBody.team?.id,
289
+ triggerId: typedBody.trigger_id,
290
+ responseUrl: typedBody.response_url,
291
+ channelId,
292
+ messageTs,
293
+ threadTs,
294
+ };
295
+ // Log the interaction for debugging
296
+ ctx.runtime.log?.(`slack:interaction action=${actionId} type=${actionSummary.actionType ?? "unknown"} user=${userId} channel=${channelId}`);
297
+ // Send a system event to notify the agent about the button click
298
+ // Pass undefined (not "unknown") to allow proper main session fallback
299
+ const sessionKey = ctx.resolveSlackSystemEventSessionKey({
300
+ channelId: channelId,
301
+ channelType: undefined,
302
+ });
303
+ // Build context key - only include defined values to avoid "unknown" noise
304
+ const contextParts = ["slack:interaction", channelId, messageTs, actionId].filter(Boolean);
305
+ const contextKey = contextParts.join(":");
306
+ enqueueSystemEvent(`Slack interaction: ${JSON.stringify(eventPayload)}`, {
307
+ sessionKey,
308
+ contextKey,
309
+ });
310
+ const originalBlocks = typedBody.message?.blocks;
311
+ if (!Array.isArray(originalBlocks) || !channelId || !messageTs) {
312
+ return;
313
+ }
314
+ if (!blockId) {
315
+ return;
316
+ }
317
+ const selectedLabel = formatInteractionSelectionLabel({
318
+ actionId,
319
+ summary: actionSummary,
320
+ buttonText: typedActionWithText.text?.text,
321
+ });
322
+ let updatedBlocks = originalBlocks.map((block) => {
323
+ const typedBlock = block;
324
+ if (typedBlock.type === "actions" && typedBlock.block_id === blockId) {
325
+ return {
326
+ type: "context",
327
+ elements: [
328
+ {
329
+ type: "mrkdwn",
330
+ text: formatInteractionConfirmationText({ selectedLabel, userId }),
331
+ },
332
+ ],
333
+ };
334
+ }
335
+ return block;
336
+ });
337
+ const hasRemainingIndividualActionRows = updatedBlocks.some((block) => {
338
+ const typedBlock = block;
339
+ return typedBlock.type === "actions" && !isBulkActionsBlock(typedBlock);
340
+ });
341
+ if (!hasRemainingIndividualActionRows) {
342
+ updatedBlocks = updatedBlocks.filter((block, index) => {
343
+ const typedBlock = block;
344
+ if (isBulkActionsBlock(typedBlock)) {
345
+ return false;
346
+ }
347
+ if (typedBlock.type !== "divider") {
348
+ return true;
349
+ }
350
+ const next = updatedBlocks[index + 1];
351
+ return !next || !isBulkActionsBlock(next);
352
+ });
353
+ }
354
+ try {
355
+ await ctx.app.client.chat.update({
356
+ channel: channelId,
357
+ ts: messageTs,
358
+ text: typedBody.message?.text ?? "",
359
+ blocks: updatedBlocks,
360
+ });
361
+ }
362
+ catch {
363
+ // If update fails, fallback to ephemeral confirmation for immediate UX feedback.
364
+ if (!respond) {
365
+ return;
366
+ }
367
+ try {
368
+ await respond({
369
+ text: `Button "${actionId}" clicked!`,
370
+ response_type: "ephemeral",
371
+ });
372
+ }
373
+ catch {
374
+ // Action was acknowledged and system event enqueued even when response updates fail.
375
+ }
376
+ }
377
+ });
378
+ if (typeof ctx.app.view !== "function") {
379
+ return;
380
+ }
381
+ // Handle OpenClaw modal submissions with callback_ids scoped by our prefix.
382
+ ctx.app.view(new RegExp(`^${POOLBOT_ACTION_PREFIX}`), async ({ ack, body }) => {
383
+ await ack();
384
+ const typedBody = body;
385
+ const callbackId = typedBody.view?.callback_id ?? "unknown";
386
+ const userId = typedBody.user?.id ?? "unknown";
387
+ const viewId = typedBody.view?.id;
388
+ const inputs = summarizeViewState(typedBody.view?.state?.values);
389
+ const sessionRouting = resolveModalSessionRouting({
390
+ ctx,
391
+ privateMetadata: typedBody.view?.private_metadata,
392
+ });
393
+ const eventPayload = {
394
+ interactionType: "view_submission",
395
+ actionId: `view:${callbackId}`,
396
+ callbackId,
397
+ viewId,
398
+ userId,
399
+ teamId: typedBody.team?.id,
400
+ ...summarizeSlackViewLifecycleContext({
401
+ root_view_id: typedBody.view?.root_view_id,
402
+ previous_view_id: typedBody.view?.previous_view_id,
403
+ external_id: typedBody.view?.external_id,
404
+ hash: typedBody.view?.hash,
405
+ }),
406
+ privateMetadata: typedBody.view?.private_metadata,
407
+ routedChannelId: sessionRouting.channelId,
408
+ routedChannelType: sessionRouting.channelType,
409
+ inputs,
410
+ };
411
+ ctx.runtime.log?.(`slack:interaction view_submission callback=${callbackId} user=${userId} inputs=${inputs.length}`);
412
+ enqueueSystemEvent(`Slack interaction: ${JSON.stringify(eventPayload)}`, {
413
+ sessionKey: sessionRouting.sessionKey,
414
+ contextKey: ["slack:interaction:view", callbackId, viewId, userId]
415
+ .filter(Boolean)
416
+ .join(":"),
417
+ });
418
+ });
419
+ const viewClosed = ctx.app.viewClosed;
420
+ if (typeof viewClosed !== "function") {
421
+ return;
422
+ }
423
+ // Handle modal close events so agent workflows can react to cancelled forms.
424
+ viewClosed(new RegExp(`^${POOLBOT_ACTION_PREFIX}`), async ({ ack, body }) => {
425
+ await ack();
426
+ const typedBody = body;
427
+ const callbackId = typedBody.view?.callback_id ?? "unknown";
428
+ const userId = typedBody.user?.id ?? "unknown";
429
+ const viewId = typedBody.view?.id;
430
+ const inputs = summarizeViewState(typedBody.view?.state?.values);
431
+ const sessionRouting = resolveModalSessionRouting({
432
+ ctx,
433
+ privateMetadata: typedBody.view?.private_metadata,
434
+ });
435
+ const eventPayload = {
436
+ interactionType: "view_closed",
437
+ actionId: `view:${callbackId}`,
438
+ callbackId,
439
+ viewId,
440
+ userId,
441
+ teamId: typedBody.team?.id,
442
+ ...summarizeSlackViewLifecycleContext({
443
+ root_view_id: typedBody.view?.root_view_id,
444
+ previous_view_id: typedBody.view?.previous_view_id,
445
+ external_id: typedBody.view?.external_id,
446
+ hash: typedBody.view?.hash,
447
+ }),
448
+ isCleared: typedBody.is_cleared === true,
449
+ privateMetadata: typedBody.view?.private_metadata,
450
+ routedChannelId: sessionRouting.channelId,
451
+ routedChannelType: sessionRouting.channelType,
452
+ inputs,
453
+ };
454
+ ctx.runtime.log?.(`slack:interaction view_closed callback=${callbackId} user=${userId} cleared=${typedBody.is_cleared === true}`);
455
+ enqueueSystemEvent(`Slack interaction: ${JSON.stringify(eventPayload)}`, {
456
+ sessionKey: sessionRouting.sessionKey,
457
+ contextKey: ["slack:interaction:view-closed", callbackId, viewId, userId]
458
+ .filter(Boolean)
459
+ .join(":"),
460
+ });
461
+ });
462
+ }
@@ -0,0 +1,17 @@
1
+ import { buildUntrustedChannelMetadata } from "../../security/channel-metadata.js";
2
+ export function resolveSlackRoomContextHints(params) {
3
+ if (!params.isRoomish) {
4
+ return {};
5
+ }
6
+ const untrustedChannelMetadata = buildUntrustedChannelMetadata({
7
+ source: "slack",
8
+ label: "Slack channel description",
9
+ entries: [params.channelInfo?.topic, params.channelInfo?.purpose],
10
+ });
11
+ const systemPromptParts = [params.channelConfig?.systemPrompt?.trim() || null].filter((entry) => Boolean(entry));
12
+ const groupSystemPrompt = systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : undefined;
13
+ return {
14
+ untrustedChannelMetadata,
15
+ groupSystemPrompt,
16
+ };
17
+ }
@@ -0,0 +1,41 @@
1
+ const DEFAULT_STREAM_MODE = "replace";
2
+ export function resolveSlackStreamMode(raw) {
3
+ if (typeof raw !== "string") {
4
+ return DEFAULT_STREAM_MODE;
5
+ }
6
+ const normalized = raw.trim().toLowerCase();
7
+ if (normalized === "replace" || normalized === "status_final" || normalized === "append") {
8
+ return normalized;
9
+ }
10
+ return DEFAULT_STREAM_MODE;
11
+ }
12
+ export function applyAppendOnlyStreamUpdate(params) {
13
+ const incoming = params.incoming.trimEnd();
14
+ if (!incoming) {
15
+ return { rendered: params.rendered, source: params.source, changed: false };
16
+ }
17
+ if (!params.rendered) {
18
+ return { rendered: incoming, source: incoming, changed: true };
19
+ }
20
+ if (incoming === params.source) {
21
+ return { rendered: params.rendered, source: params.source, changed: false };
22
+ }
23
+ // Typical model partials are cumulative prefixes.
24
+ if (incoming.startsWith(params.source) || incoming.startsWith(params.rendered)) {
25
+ return { rendered: incoming, source: incoming, changed: incoming !== params.rendered };
26
+ }
27
+ // Ignore regressive shorter variants of the same stream.
28
+ if (params.source.startsWith(incoming)) {
29
+ return { rendered: params.rendered, source: params.source, changed: false };
30
+ }
31
+ const separator = params.rendered.endsWith("\n") ? "" : "\n";
32
+ return {
33
+ rendered: `${params.rendered}${separator}${incoming}`,
34
+ source: incoming,
35
+ changed: true,
36
+ };
37
+ }
38
+ export function buildStatusFinalPreviewText(updateCount) {
39
+ const dots = ".".repeat((Math.max(1, updateCount) % 3) + 1);
40
+ return `Status: thinking${dots}`;
41
+ }
@@ -0,0 +1,64 @@
1
+ import { normalizeTelegramCommandName, TELEGRAM_COMMAND_NAME_PATTERN, } from "../config/telegram-custom-commands.js";
2
+ import { withTelegramApiErrorLogging } from "./api-logging.js";
3
+ export const TELEGRAM_MAX_COMMANDS = 100;
4
+ export function buildPluginTelegramMenuCommands(params) {
5
+ const { specs, existingCommands } = params;
6
+ const commands = [];
7
+ const issues = [];
8
+ const pluginCommandNames = new Set();
9
+ for (const spec of specs) {
10
+ const normalized = normalizeTelegramCommandName(spec.name);
11
+ if (!normalized || !TELEGRAM_COMMAND_NAME_PATTERN.test(normalized)) {
12
+ issues.push(`Plugin command "/${spec.name}" is invalid for Telegram (use a-z, 0-9, underscore; max 32 chars).`);
13
+ continue;
14
+ }
15
+ const description = spec.description.trim();
16
+ if (!description) {
17
+ issues.push(`Plugin command "/${normalized}" is missing a description.`);
18
+ continue;
19
+ }
20
+ if (existingCommands.has(normalized)) {
21
+ if (pluginCommandNames.has(normalized)) {
22
+ issues.push(`Plugin command "/${normalized}" is duplicated.`);
23
+ }
24
+ else {
25
+ issues.push(`Plugin command "/${normalized}" conflicts with an existing Telegram command.`);
26
+ }
27
+ continue;
28
+ }
29
+ pluginCommandNames.add(normalized);
30
+ existingCommands.add(normalized);
31
+ commands.push({ command: normalized, description });
32
+ }
33
+ return { commands, issues };
34
+ }
35
+ export function buildCappedTelegramMenuCommands(params) {
36
+ const { allCommands } = params;
37
+ const maxCommands = params.maxCommands ?? TELEGRAM_MAX_COMMANDS;
38
+ const totalCommands = allCommands.length;
39
+ const overflowCount = Math.max(0, totalCommands - maxCommands);
40
+ const commandsToRegister = allCommands.slice(0, maxCommands);
41
+ return { commandsToRegister, totalCommands, maxCommands, overflowCount };
42
+ }
43
+ export function syncTelegramMenuCommands(params) {
44
+ const { bot, runtime, commandsToRegister } = params;
45
+ const sync = async () => {
46
+ // Keep delete -> set ordering to avoid stale deletions racing after fresh registrations.
47
+ if (typeof bot.api.deleteMyCommands === "function") {
48
+ await withTelegramApiErrorLogging({
49
+ operation: "deleteMyCommands",
50
+ runtime,
51
+ fn: () => bot.api.deleteMyCommands(),
52
+ }).catch(() => { });
53
+ }
54
+ if (commandsToRegister.length === 0) {
55
+ return;
56
+ }
57
+ await withTelegramApiErrorLogging({
58
+ operation: "setMyCommands",
59
+ runtime,
60
+ fn: () => bot.api.setMyCommands(commandsToRegister),
61
+ });
62
+ };
63
+ void sync().catch(() => { });
64
+ }
@@ -0,0 +1,81 @@
1
+ import { beforeEach, vi } from "vitest";
2
+ import { resetInboundDedupe } from "../auto-reply/reply/inbound-dedupe.js";
3
+ export const useSpy = vi.fn();
4
+ export const middlewareUseSpy = vi.fn();
5
+ export const onSpy = vi.fn();
6
+ export const stopSpy = vi.fn();
7
+ export const sendChatActionSpy = vi.fn();
8
+ const apiStub = {
9
+ config: { use: useSpy },
10
+ sendChatAction: sendChatActionSpy,
11
+ setMyCommands: vi.fn(async () => undefined),
12
+ };
13
+ beforeEach(() => {
14
+ resetInboundDedupe();
15
+ });
16
+ vi.mock("grammy", () => ({
17
+ Bot: class {
18
+ token;
19
+ api = apiStub;
20
+ use = middlewareUseSpy;
21
+ on = onSpy;
22
+ command = vi.fn();
23
+ stop = stopSpy;
24
+ catch = vi.fn();
25
+ constructor(token) {
26
+ this.token = token;
27
+ }
28
+ },
29
+ InputFile: class {
30
+ },
31
+ webhookCallback: vi.fn(),
32
+ }));
33
+ vi.mock("@grammyjs/runner", () => ({
34
+ sequentialize: () => vi.fn(),
35
+ }));
36
+ const throttlerSpy = vi.fn(() => "throttler");
37
+ vi.mock("@grammyjs/transformer-throttler", () => ({
38
+ apiThrottler: () => throttlerSpy(),
39
+ }));
40
+ vi.mock("../media/store.js", async (importOriginal) => {
41
+ const actual = await importOriginal();
42
+ return {
43
+ ...actual,
44
+ saveMediaBuffer: vi.fn(async (buffer, contentType) => ({
45
+ id: "media",
46
+ path: "/tmp/telegram-media",
47
+ size: buffer.byteLength,
48
+ contentType: contentType ?? "application/octet-stream",
49
+ })),
50
+ };
51
+ });
52
+ vi.mock("../config/config.js", async (importOriginal) => {
53
+ const actual = await importOriginal();
54
+ return {
55
+ ...actual,
56
+ loadConfig: () => ({
57
+ channels: { telegram: { dmPolicy: "open", allowFrom: ["*"] } },
58
+ }),
59
+ };
60
+ });
61
+ vi.mock("../config/sessions.js", async (importOriginal) => {
62
+ const actual = await importOriginal();
63
+ return {
64
+ ...actual,
65
+ updateLastRoute: vi.fn(async () => undefined),
66
+ };
67
+ });
68
+ vi.mock("../pairing/pairing-store.js", () => ({
69
+ readChannelAllowFromStore: vi.fn(async () => []),
70
+ upsertChannelPairingRequest: vi.fn(async () => ({
71
+ code: "PAIRCODE",
72
+ created: true,
73
+ })),
74
+ }));
75
+ vi.mock("../auto-reply/reply.js", () => {
76
+ const replySpy = vi.fn(async (_ctx, opts) => {
77
+ await opts?.onReplyStart?.();
78
+ return undefined;
79
+ });
80
+ return { getReplyFromConfig: replySpy, __replySpy: replySpy };
81
+ });
@@ -0,0 +1 @@
1
+ export {};