@poolzin/pool-bot 2026.2.21 → 2026.2.22

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 (369) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/agents/api-key-rotation.js +47 -0
  3. package/dist/agents/apply-patch-update.js +19 -9
  4. package/dist/agents/apply-patch.js +72 -47
  5. package/dist/agents/bash-tools.exec.js +141 -559
  6. package/dist/agents/cli-backends.js +49 -6
  7. package/dist/agents/cli-runner/helpers.js +69 -152
  8. package/dist/agents/cli-runner.js +70 -19
  9. package/dist/agents/identity.js +20 -1
  10. package/dist/agents/image-sanitization.js +9 -0
  11. package/dist/agents/live-auth-keys.js +123 -26
  12. package/dist/agents/live-model-filter.js +13 -4
  13. package/dist/agents/model-catalog.js +40 -9
  14. package/dist/agents/model-forward-compat.js +60 -23
  15. package/dist/agents/model-selection.js +134 -41
  16. package/dist/agents/pi-auth-json.js +2 -2
  17. package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
  18. package/dist/agents/pi-embedded-helpers/errors.js +140 -15
  19. package/dist/agents/pi-embedded-helpers/images.js +22 -12
  20. package/dist/agents/pi-embedded-helpers.js +2 -2
  21. package/dist/agents/pi-embedded-runner/abort.js +10 -3
  22. package/dist/agents/pi-embedded-runner/compact.js +230 -32
  23. package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
  24. package/dist/agents/pi-embedded-runner/google.js +109 -19
  25. package/dist/agents/pi-embedded-runner/history.js +35 -17
  26. package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
  27. package/dist/agents/pi-embedded-runner/run/images.js +81 -55
  28. package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
  29. package/dist/agents/pi-embedded-runner/run.js +193 -25
  30. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
  31. package/dist/agents/pi-embedded-runner/runs.js +17 -8
  32. package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
  33. package/dist/agents/pi-embedded-runner.js +1 -1
  34. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
  35. package/dist/agents/pi-embedded-subscribe.js +37 -0
  36. package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
  37. package/dist/agents/pi-model-discovery.js +9 -2
  38. package/dist/agents/pi-tool-definition-adapter.js +60 -8
  39. package/dist/agents/pi-tools.before-tool-call.js +1 -1
  40. package/dist/agents/pi-tools.js +113 -94
  41. package/dist/agents/pi-tools.read.js +337 -38
  42. package/dist/agents/poolbot-tools.js +14 -5
  43. package/dist/agents/sandbox/docker.js +10 -5
  44. package/dist/agents/sandbox/registry.js +96 -46
  45. package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
  46. package/dist/agents/sandbox-paths.js +43 -10
  47. package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
  48. package/dist/agents/session-tool-result-guard.js +39 -39
  49. package/dist/agents/session-transcript-repair.js +36 -33
  50. package/dist/agents/session-write-lock.js +62 -44
  51. package/dist/agents/skills/frontmatter.js +49 -88
  52. package/dist/agents/skills/workspace.js +335 -28
  53. package/dist/agents/subagent-announce.js +508 -174
  54. package/dist/agents/subagent-registry.js +45 -4
  55. package/dist/agents/subagent-spawn.js +16 -33
  56. package/dist/agents/system-prompt-report.js +27 -10
  57. package/dist/agents/system-prompt.js +26 -32
  58. package/dist/agents/tool-call-id.js +69 -17
  59. package/dist/agents/tool-display-common.js +1 -1
  60. package/dist/agents/tool-images.js +64 -31
  61. package/dist/agents/tools/canvas-tool.js +17 -11
  62. package/dist/agents/tools/common.js +37 -19
  63. package/dist/agents/tools/cron-tool.js +40 -38
  64. package/dist/agents/tools/gateway.js +70 -2
  65. package/dist/agents/tools/message-tool.js +181 -40
  66. package/dist/agents/tools/nodes-tool.js +128 -36
  67. package/dist/agents/tools/nodes-utils.js +12 -38
  68. package/dist/agents/tools/session-status-tool.js +24 -71
  69. package/dist/agents/tools/sessions-helpers.js +38 -210
  70. package/dist/agents/tools/sessions-spawn-tool.js +28 -198
  71. package/dist/agents/tools/telegram-actions.js +58 -7
  72. package/dist/agents/tools/web-fetch-utils.js +112 -7
  73. package/dist/agents/tools/web-fetch.js +279 -175
  74. package/dist/agents/tools/web-shared.js +71 -8
  75. package/dist/agents/usage.js +25 -16
  76. package/dist/auto-reply/commands-registry.data.js +85 -11
  77. package/dist/auto-reply/dispatch.js +40 -21
  78. package/dist/auto-reply/reply/abort.js +102 -33
  79. package/dist/auto-reply/reply/commands-core.js +82 -33
  80. package/dist/auto-reply/reply/commands-export-session.js +1 -1
  81. package/dist/auto-reply/reply/commands-info.js +41 -12
  82. package/dist/auto-reply/reply/commands-subagents.js +352 -100
  83. package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
  84. package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
  85. package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
  86. package/dist/auto-reply/reply/inbound-meta.js +12 -1
  87. package/dist/auto-reply/reply/mentions.js +18 -11
  88. package/dist/auto-reply/reply/normalize-reply.js +17 -8
  89. package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
  90. package/dist/auto-reply/reply/session.js +102 -21
  91. package/dist/auto-reply/reply/streaming-directives.js +16 -5
  92. package/dist/auto-reply/status.js +73 -50
  93. package/dist/browser/extension-relay.js +3 -3
  94. package/dist/browser/http-auth.js +1 -1
  95. package/dist/browser/paths.js +2 -2
  96. package/dist/build-info.json +3 -3
  97. package/dist/channels/allowlist-match.js +20 -0
  98. package/dist/channels/allowlists/resolve-utils.js +65 -2
  99. package/dist/channels/chat-type.js +8 -4
  100. package/dist/channels/dock.js +127 -35
  101. package/dist/channels/draft-stream-loop.js +6 -2
  102. package/dist/channels/plugins/actions/telegram.js +42 -18
  103. package/dist/channels/plugins/allowlist-match.js +1 -1
  104. package/dist/channels/plugins/group-mentions.js +51 -41
  105. package/dist/channels/plugins/message-action-names.js +2 -0
  106. package/dist/channels/plugins/message-actions.js +24 -5
  107. package/dist/channels/plugins/normalize/discord.js +26 -4
  108. package/dist/channels/plugins/normalize/signal.js +35 -22
  109. package/dist/channels/plugins/onboarding/helpers.js +8 -26
  110. package/dist/channels/plugins/outbound/imessage.js +15 -14
  111. package/dist/channels/registry.js +20 -7
  112. package/dist/cli/acp-cli.js +7 -5
  113. package/dist/cli/browser-cli-extension.js +25 -12
  114. package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
  115. package/dist/cli/browser-cli-state.js +101 -145
  116. package/dist/cli/command-options.js +28 -0
  117. package/dist/cli/completion-cli.js +6 -6
  118. package/dist/cli/cron-cli/register.cron-add.js +25 -1
  119. package/dist/cli/cron-cli/register.cron-edit.js +44 -0
  120. package/dist/cli/cron-cli/shared.js +7 -1
  121. package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
  122. package/dist/cli/daemon-cli/lifecycle.js +23 -247
  123. package/dist/cli/daemon-cli/register-service-commands.js +25 -4
  124. package/dist/cli/daemon-cli.js +1 -0
  125. package/dist/cli/devices-cli.js +33 -20
  126. package/dist/cli/gateway-cli/register.js +37 -105
  127. package/dist/cli/gateway-cli/run.js +49 -11
  128. package/dist/cli/nodes-camera.js +59 -4
  129. package/dist/cli/nodes-cli/register.camera.js +27 -24
  130. package/dist/cli/nodes-cli/rpc.js +21 -38
  131. package/dist/cli/qr-cli.js +2 -2
  132. package/dist/cli/skills-cli.format.js +2 -2
  133. package/dist/cli/update-cli/progress.js +2 -2
  134. package/dist/cli/update-cli/restart-helper.js +28 -7
  135. package/dist/cli/update-cli/shared.js +7 -7
  136. package/dist/cli/update-cli/status.js +1 -1
  137. package/dist/cli/update-cli/update-command.js +14 -8
  138. package/dist/cli/update-cli/wizard.js +2 -2
  139. package/dist/cli/update-cli.js +21 -1027
  140. package/dist/commands/auth-choice.apply.anthropic.js +10 -2
  141. package/dist/commands/channels/add-mutators.js +3 -35
  142. package/dist/commands/channels/add.js +39 -51
  143. package/dist/commands/config-validation.js +1 -1
  144. package/dist/commands/configure.gateway-auth.js +52 -15
  145. package/dist/commands/configure.gateway.js +84 -40
  146. package/dist/commands/doctor-completion.js +3 -3
  147. package/dist/commands/doctor-config-flow.js +536 -16
  148. package/dist/commands/doctor-gateway-services.js +103 -79
  149. package/dist/commands/doctor-memory-search.js +9 -9
  150. package/dist/commands/doctor-platform-notes.js +57 -30
  151. package/dist/commands/doctor-prompter.js +26 -15
  152. package/dist/commands/doctor-session-locks.js +1 -1
  153. package/dist/commands/doctor.js +21 -9
  154. package/dist/commands/model-picker.js +120 -95
  155. package/dist/commands/models/set.js +2 -21
  156. package/dist/commands/models/shared.js +65 -37
  157. package/dist/commands/onboard-helpers.js +81 -39
  158. package/dist/commands/openai-codex-oauth.js +1 -1
  159. package/dist/commands/sessions.js +52 -53
  160. package/dist/commands/status.summary.js +52 -34
  161. package/dist/commands/test-wizard-helpers.js +2 -2
  162. package/dist/config/defaults.js +79 -42
  163. package/dist/config/group-policy.js +50 -18
  164. package/dist/config/includes.js +37 -10
  165. package/dist/config/schema.help.js +5 -4
  166. package/dist/config/schema.hints.js +2 -2
  167. package/dist/config/schema.labels.js +1 -0
  168. package/dist/config/sessions/group.js +12 -11
  169. package/dist/config/sessions/paths.js +137 -11
  170. package/dist/config/sessions/store.js +185 -65
  171. package/dist/config/sessions/types.js +15 -1
  172. package/dist/config/sessions.js +1 -0
  173. package/dist/config/telegram-custom-commands.js +3 -2
  174. package/dist/config/types.js +2 -0
  175. package/dist/config/zod-schema.agent-defaults.js +6 -27
  176. package/dist/config/zod-schema.agent-runtime.js +171 -79
  177. package/dist/config/zod-schema.providers-core.js +138 -65
  178. package/dist/config/zod-schema.session.js +49 -22
  179. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  180. package/dist/cron/isolated-agent/run.js +224 -57
  181. package/dist/cron/normalize.js +48 -45
  182. package/dist/cron/run-log.js +14 -0
  183. package/dist/cron/service/jobs.js +190 -28
  184. package/dist/cron/service/normalize.js +29 -11
  185. package/dist/cron/service/store.js +30 -44
  186. package/dist/cron/service/timer.js +182 -96
  187. package/dist/cron/service.js +3 -0
  188. package/dist/cron/stagger.js +37 -0
  189. package/dist/daemon/inspect.js +132 -92
  190. package/dist/daemon/runtime-paths.js +25 -4
  191. package/dist/daemon/service-audit.js +47 -16
  192. package/dist/discord/accounts.js +23 -20
  193. package/dist/discord/monitor/agent-components.js +1115 -219
  194. package/dist/discord/monitor/allow-list.js +114 -34
  195. package/dist/discord/monitor/listeners.js +204 -97
  196. package/dist/discord/monitor/message-handler.js +21 -10
  197. package/dist/discord/monitor/message-handler.preflight.js +195 -101
  198. package/dist/discord/monitor/message-handler.process.js +384 -123
  199. package/dist/discord/monitor/message-utils.js +86 -23
  200. package/dist/discord/monitor/native-command.js +77 -57
  201. package/dist/discord/monitor/provider.js +122 -117
  202. package/dist/discord/monitor/reply-context.js +20 -16
  203. package/dist/discord/monitor/reply-delivery.js +40 -8
  204. package/dist/discord/monitor/rest-fetch.js +22 -0
  205. package/dist/discord/monitor/threading.js +117 -24
  206. package/dist/discord/send.js +2 -1
  207. package/dist/discord/send.outbound.js +124 -11
  208. package/dist/discord/send.shared.js +112 -72
  209. package/dist/discord/voice-message.js +3 -3
  210. package/dist/gateway/auth.js +119 -44
  211. package/dist/gateway/call.js +76 -34
  212. package/dist/gateway/channel-health-monitor.js +57 -50
  213. package/dist/gateway/client.js +63 -29
  214. package/dist/gateway/control-ui-contract.js +1 -1
  215. package/dist/gateway/gateway-config-prompts.shared.js +2 -2
  216. package/dist/gateway/net.js +109 -1
  217. package/dist/gateway/protocol/index.js +5 -8
  218. package/dist/gateway/protocol/schema/agent.js +19 -1
  219. package/dist/gateway/protocol/schema/channels.js +21 -0
  220. package/dist/gateway/protocol/schema/cron.js +43 -30
  221. package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
  222. package/dist/gateway/protocol/schema/sessions.js +5 -1
  223. package/dist/gateway/protocol/schema.js +0 -1
  224. package/dist/gateway/server/presence-events.js +12 -0
  225. package/dist/gateway/server/ws-connection/message-handler.js +203 -212
  226. package/dist/gateway/server/ws-connection.js +58 -21
  227. package/dist/gateway/server-broadcast.js +18 -13
  228. package/dist/gateway/server-cron.js +177 -10
  229. package/dist/gateway/server-methods/agent-job.js +131 -38
  230. package/dist/gateway/server-methods/send.js +60 -14
  231. package/dist/gateway/server-methods/sessions.js +160 -96
  232. package/dist/gateway/server-methods/system.js +5 -7
  233. package/dist/gateway/server-methods-list.js +8 -0
  234. package/dist/gateway/server-methods.js +24 -8
  235. package/dist/gateway/server-node-events.js +278 -68
  236. package/dist/gateway/session-utils.fs.js +316 -75
  237. package/dist/gateway/session-utils.js +224 -70
  238. package/dist/gateway/sessions-patch.js +63 -20
  239. package/dist/gateway/test-temp-config.js +1 -1
  240. package/dist/gateway/tools-invoke-http.js +118 -70
  241. package/dist/gateway/ws-log.js +135 -107
  242. package/dist/hooks/frontmatter.js +36 -82
  243. package/dist/hooks/install.js +149 -139
  244. package/dist/hooks/internal-hooks.js +29 -4
  245. package/dist/hooks/plugin-hooks.js +2 -1
  246. package/dist/imessage/monitor/deliver.js +10 -4
  247. package/dist/imessage/monitor/monitor-provider.js +138 -375
  248. package/dist/imessage/monitor/runtime.js +4 -8
  249. package/dist/imessage/send.js +65 -19
  250. package/dist/infra/exec-approvals-allowlist.js +7 -0
  251. package/dist/infra/exec-approvals.js +35 -920
  252. package/dist/infra/exec-safe-bin-trust.js +64 -0
  253. package/dist/infra/heartbeat-runner.js +207 -134
  254. package/dist/infra/heartbeat-wake.js +183 -22
  255. package/dist/infra/install-source-utils.js +47 -0
  256. package/dist/infra/net/ssrf.js +170 -36
  257. package/dist/infra/outbound/deliver.js +224 -58
  258. package/dist/infra/outbound/message-action-spec.js +12 -5
  259. package/dist/infra/outbound/outbound-session.js +27 -25
  260. package/dist/infra/poolbot-root.js +32 -22
  261. package/dist/infra/ports.js +14 -11
  262. package/dist/infra/skills-remote.js +48 -37
  263. package/dist/infra/system-events.js +25 -11
  264. package/dist/infra/system-presence.js +26 -33
  265. package/dist/infra/tmp-poolbot-dir.js +81 -2
  266. package/dist/infra/wsl.js +37 -1
  267. package/dist/line/bot-message-context.js +163 -191
  268. package/dist/logging/subsystem.js +59 -22
  269. package/dist/markdown/ir.js +124 -50
  270. package/dist/media/store.js +1 -1
  271. package/dist/media-understanding/runner.entries.js +42 -25
  272. package/dist/media-understanding/runner.js +53 -488
  273. package/dist/memory/embeddings-gemini.js +53 -38
  274. package/dist/memory/manager-embedding-ops.js +48 -69
  275. package/dist/pairing/pairing-store.js +178 -119
  276. package/dist/plugin-sdk/index.js +34 -6
  277. package/dist/plugins/hooks.js +135 -14
  278. package/dist/plugins/install.js +190 -152
  279. package/dist/polls.js +11 -0
  280. package/dist/routing/resolve-route.js +190 -56
  281. package/dist/routing/session-key.js +38 -22
  282. package/dist/runtime.js +35 -9
  283. package/dist/security/audit-channel.js +1 -1
  284. package/dist/sessions/session-key-utils.js +29 -11
  285. package/dist/shared/frontmatter.js +5 -5
  286. package/dist/shared/node-list-types.js +1 -0
  287. package/dist/shared/string-normalization.js +15 -0
  288. package/dist/signal/monitor/event-handler.js +68 -36
  289. package/dist/signal/send.js +29 -37
  290. package/dist/slack/monitor/allow-list.js +10 -11
  291. package/dist/slack/monitor/commands.js +14 -3
  292. package/dist/slack/monitor/events/interactions.js +4 -4
  293. package/dist/slack/monitor/media.js +224 -16
  294. package/dist/slack/monitor/message-handler/dispatch.js +247 -13
  295. package/dist/slack/monitor/message-handler/prepare.js +128 -45
  296. package/dist/slack/monitor/slash.js +357 -144
  297. package/dist/slack/streaming.js +77 -0
  298. package/dist/telegram/accounts.js +40 -13
  299. package/dist/telegram/allowed-updates.js +3 -0
  300. package/dist/telegram/bot/delivery.js +129 -66
  301. package/dist/telegram/bot/helpers.js +136 -122
  302. package/dist/telegram/bot-handlers.js +600 -339
  303. package/dist/telegram/bot-message-context.js +115 -73
  304. package/dist/telegram/bot-message-dispatch.js +235 -104
  305. package/dist/telegram/bot-native-command-menu.js +3 -1
  306. package/dist/telegram/bot-native-commands.js +213 -193
  307. package/dist/telegram/bot.js +24 -132
  308. package/dist/telegram/draft-stream.js +84 -75
  309. package/dist/telegram/format.js +150 -6
  310. package/dist/telegram/send.js +415 -255
  311. package/dist/telegram/targets.js +21 -2
  312. package/dist/telegram/update-offset-store.js +19 -3
  313. package/dist/terminal/restore.js +5 -2
  314. package/dist/test-utils/fetch-mock.js +5 -0
  315. package/dist/version.js +18 -5
  316. package/dist/web/auto-reply/monitor/broadcast.js +7 -3
  317. package/dist/web/auto-reply/monitor/on-message.js +6 -3
  318. package/dist/web/inbound/media.js +34 -8
  319. package/dist/web/inbound/monitor.js +34 -17
  320. package/dist/web/inbound/send-api.js +18 -17
  321. package/dist/web/outbound.js +12 -5
  322. package/dist/wizard/clack-prompter.js +40 -7
  323. package/extensions/bluebubbles/package.json +1 -1
  324. package/extensions/copilot-proxy/package.json +1 -1
  325. package/extensions/diagnostics-otel/package.json +1 -1
  326. package/extensions/discord/package.json +1 -1
  327. package/extensions/feishu/package.json +1 -1
  328. package/extensions/google-antigravity-auth/package.json +1 -1
  329. package/extensions/google-gemini-cli-auth/package.json +1 -1
  330. package/extensions/googlechat/package.json +1 -1
  331. package/extensions/imessage/package.json +1 -1
  332. package/extensions/irc/package.json +1 -1
  333. package/extensions/line/package.json +1 -1
  334. package/extensions/llm-task/package.json +1 -1
  335. package/extensions/lobster/package.json +1 -1
  336. package/extensions/matrix/CHANGELOG.md +5 -0
  337. package/extensions/matrix/package.json +1 -1
  338. package/extensions/mattermost/package.json +1 -1
  339. package/extensions/memory-core/package.json +1 -1
  340. package/extensions/memory-lancedb/package.json +1 -1
  341. package/extensions/minimax-portal-auth/package.json +1 -1
  342. package/extensions/msteams/CHANGELOG.md +5 -0
  343. package/extensions/msteams/package.json +1 -1
  344. package/extensions/nextcloud-talk/package.json +1 -1
  345. package/extensions/nostr/CHANGELOG.md +5 -0
  346. package/extensions/nostr/package.json +1 -1
  347. package/extensions/open-prose/package.json +1 -1
  348. package/extensions/openai-codex-auth/package.json +1 -1
  349. package/extensions/signal/package.json +1 -1
  350. package/extensions/slack/package.json +1 -1
  351. package/extensions/telegram/package.json +1 -1
  352. package/extensions/tlon/package.json +1 -1
  353. package/extensions/twitch/CHANGELOG.md +5 -0
  354. package/extensions/twitch/package.json +1 -1
  355. package/extensions/voice-call/CHANGELOG.md +5 -0
  356. package/extensions/voice-call/package.json +1 -1
  357. package/extensions/whatsapp/package.json +1 -1
  358. package/extensions/zalo/CHANGELOG.md +5 -0
  359. package/extensions/zalo/package.json +1 -1
  360. package/extensions/zalouser/CHANGELOG.md +5 -0
  361. package/extensions/zalouser/package.json +1 -1
  362. package/package.json +1 -1
  363. package/skills/apple-reminders/SKILL.md +100 -49
  364. package/skills/coding-agent/SKILL.md +34 -28
  365. package/skills/github/SKILL.md +131 -16
  366. package/skills/imsg/SKILL.md +112 -15
  367. package/skills/openhue/SKILL.md +101 -19
  368. package/skills/tmux/SKILL.md +111 -79
  369. package/skills/weather/SKILL.md +88 -25
@@ -1,12 +1,12 @@
1
1
  import { DEFAULT_CONTEXT_TOKENS } from "../agents/defaults.js";
2
2
  import { parseModelRef } from "../agents/model-selection.js";
3
- import { resolveTalkApiKey } from "./talk.js";
4
3
  import { DEFAULT_AGENT_MAX_CONCURRENT, DEFAULT_SUBAGENT_MAX_CONCURRENT } from "./agent-limits.js";
4
+ import { resolveTalkApiKey } from "./talk.js";
5
5
  let defaultWarnState = { warned: false };
6
6
  const DEFAULT_MODEL_ALIASES = {
7
7
  // Anthropic (pi-ai catalog uses "latest" ids without date suffix)
8
8
  opus: "anthropic/claude-opus-4-6",
9
- sonnet: "anthropic/claude-sonnet-4-5",
9
+ sonnet: "anthropic/claude-sonnet-4-6",
10
10
  // OpenAI
11
11
  gpt: "openai/gpt-5.2",
12
12
  "gpt-mini": "openai/gpt-5-mini",
@@ -39,39 +39,49 @@ function resolveAnthropicDefaultAuthMode(cfg) {
39
39
  const order = cfg.auth?.order?.anthropic ?? [];
40
40
  for (const profileId of order) {
41
41
  const entry = profiles[profileId];
42
- if (!entry || entry.provider !== "anthropic")
42
+ if (!entry || entry.provider !== "anthropic") {
43
43
  continue;
44
- if (entry.mode === "api_key")
44
+ }
45
+ if (entry.mode === "api_key") {
45
46
  return "api_key";
46
- if (entry.mode === "oauth" || entry.mode === "token")
47
+ }
48
+ if (entry.mode === "oauth" || entry.mode === "token") {
47
49
  return "oauth";
50
+ }
48
51
  }
49
52
  const hasApiKey = anthropicProfiles.some(([, profile]) => profile?.mode === "api_key");
50
53
  const hasOauth = anthropicProfiles.some(([, profile]) => profile?.mode === "oauth" || profile?.mode === "token");
51
- if (hasApiKey && !hasOauth)
54
+ if (hasApiKey && !hasOauth) {
52
55
  return "api_key";
53
- if (hasOauth && !hasApiKey)
56
+ }
57
+ if (hasOauth && !hasApiKey) {
54
58
  return "oauth";
55
- if (process.env.ANTHROPIC_OAUTH_TOKEN?.trim())
59
+ }
60
+ if (process.env.ANTHROPIC_OAUTH_TOKEN?.trim()) {
56
61
  return "oauth";
57
- if (process.env.ANTHROPIC_API_KEY?.trim())
62
+ }
63
+ if (process.env.ANTHROPIC_API_KEY?.trim()) {
58
64
  return "api_key";
65
+ }
59
66
  return null;
60
67
  }
61
68
  function resolvePrimaryModelRef(raw) {
62
- if (!raw || typeof raw !== "string")
69
+ if (!raw || typeof raw !== "string") {
63
70
  return null;
71
+ }
64
72
  const trimmed = raw.trim();
65
- if (!trimmed)
73
+ if (!trimmed) {
66
74
  return null;
75
+ }
67
76
  const aliasKey = trimmed.toLowerCase();
68
77
  return DEFAULT_MODEL_ALIASES[aliasKey] ?? trimmed;
69
78
  }
70
79
  export function applyMessageDefaults(cfg) {
71
80
  const messages = cfg.messages;
72
81
  const hasAckScope = messages?.ackReactionScope !== undefined;
73
- if (hasAckScope)
82
+ if (hasAckScope) {
74
83
  return cfg;
84
+ }
75
85
  const nextMessages = messages ? { ...messages } : {};
76
86
  nextMessages.ackReactionScope = "group-mentions";
77
87
  return {
@@ -81,8 +91,9 @@ export function applyMessageDefaults(cfg) {
81
91
  }
82
92
  export function applySessionDefaults(cfg, options = {}) {
83
93
  const session = cfg.session;
84
- if (!session || session.mainKey === undefined)
94
+ if (!session || session.mainKey === undefined) {
85
95
  return cfg;
96
+ }
86
97
  const trimmed = session.mainKey.trim();
87
98
  const warn = options.warn ?? console.warn;
88
99
  const warnState = options.warnState ?? defaultWarnState;
@@ -98,11 +109,13 @@ export function applySessionDefaults(cfg, options = {}) {
98
109
  }
99
110
  export function applyTalkApiKey(config) {
100
111
  const resolved = resolveTalkApiKey();
101
- if (!resolved)
112
+ if (!resolved) {
102
113
  return config;
114
+ }
103
115
  const existing = config.talk?.apiKey?.trim();
104
- if (existing)
116
+ if (existing) {
105
117
  return config;
118
+ }
106
119
  return {
107
120
  ...config,
108
121
  talk: {
@@ -119,38 +132,45 @@ export function applyModelDefaults(cfg) {
119
132
  const nextProviders = { ...providerConfig };
120
133
  for (const [providerId, provider] of Object.entries(providerConfig)) {
121
134
  const models = provider.models;
122
- if (!Array.isArray(models) || models.length === 0)
135
+ if (!Array.isArray(models) || models.length === 0) {
123
136
  continue;
137
+ }
124
138
  let providerMutated = false;
125
139
  const nextModels = models.map((model) => {
126
140
  const raw = model;
127
141
  let modelMutated = false;
128
142
  const reasoning = typeof raw.reasoning === "boolean" ? raw.reasoning : false;
129
- if (raw.reasoning !== reasoning)
143
+ if (raw.reasoning !== reasoning) {
130
144
  modelMutated = true;
145
+ }
131
146
  const input = raw.input ?? [...DEFAULT_MODEL_INPUT];
132
- if (raw.input === undefined)
147
+ if (raw.input === undefined) {
133
148
  modelMutated = true;
149
+ }
134
150
  const cost = resolveModelCost(raw.cost);
135
151
  const costMutated = !raw.cost ||
136
152
  raw.cost.input !== cost.input ||
137
153
  raw.cost.output !== cost.output ||
138
154
  raw.cost.cacheRead !== cost.cacheRead ||
139
155
  raw.cost.cacheWrite !== cost.cacheWrite;
140
- if (costMutated)
156
+ if (costMutated) {
141
157
  modelMutated = true;
158
+ }
142
159
  const contextWindow = isPositiveNumber(raw.contextWindow)
143
160
  ? raw.contextWindow
144
161
  : DEFAULT_CONTEXT_TOKENS;
145
- if (raw.contextWindow !== contextWindow)
162
+ if (raw.contextWindow !== contextWindow) {
146
163
  modelMutated = true;
164
+ }
147
165
  const defaultMaxTokens = Math.min(DEFAULT_MODEL_MAX_TOKENS, contextWindow);
148
166
  const rawMaxTokens = isPositiveNumber(raw.maxTokens) ? raw.maxTokens : defaultMaxTokens;
149
167
  const maxTokens = Math.min(rawMaxTokens, contextWindow);
150
- if (raw.maxTokens !== maxTokens)
168
+ if (raw.maxTokens !== maxTokens) {
151
169
  modelMutated = true;
152
- if (!modelMutated)
170
+ }
171
+ if (!modelMutated) {
153
172
  return model;
173
+ }
154
174
  providerMutated = true;
155
175
  return {
156
176
  ...raw,
@@ -161,8 +181,9 @@ export function applyModelDefaults(cfg) {
161
181
  maxTokens,
162
182
  };
163
183
  });
164
- if (!providerMutated)
184
+ if (!providerMutated) {
165
185
  continue;
186
+ }
166
187
  nextProviders[providerId] = { ...provider, models: nextModels };
167
188
  mutated = true;
168
189
  }
@@ -177,25 +198,30 @@ export function applyModelDefaults(cfg) {
177
198
  }
178
199
  }
179
200
  const existingAgent = nextCfg.agents?.defaults;
180
- if (!existingAgent)
201
+ if (!existingAgent) {
181
202
  return mutated ? nextCfg : cfg;
203
+ }
182
204
  const existingModels = existingAgent.models ?? {};
183
- if (Object.keys(existingModels).length === 0)
205
+ if (Object.keys(existingModels).length === 0) {
184
206
  return mutated ? nextCfg : cfg;
207
+ }
185
208
  const nextModels = {
186
209
  ...existingModels,
187
210
  };
188
211
  for (const [alias, target] of Object.entries(DEFAULT_MODEL_ALIASES)) {
189
212
  const entry = nextModels[target];
190
- if (!entry)
213
+ if (!entry) {
191
214
  continue;
192
- if (entry.alias !== undefined)
215
+ }
216
+ if (entry.alias !== undefined) {
193
217
  continue;
218
+ }
194
219
  nextModels[target] = { ...entry, alias };
195
220
  mutated = true;
196
221
  }
197
- if (!mutated)
222
+ if (!mutated) {
198
223
  return cfg;
224
+ }
199
225
  return {
200
226
  ...nextCfg,
201
227
  agents: {
@@ -210,8 +236,9 @@ export function applyAgentDefaults(cfg) {
210
236
  const hasMax = typeof defaults?.maxConcurrent === "number" && Number.isFinite(defaults.maxConcurrent);
211
237
  const hasSubMax = typeof defaults?.subagents?.maxConcurrent === "number" &&
212
238
  Number.isFinite(defaults.subagents.maxConcurrent);
213
- if (hasMax && hasSubMax)
239
+ if (hasMax && hasSubMax) {
214
240
  return cfg;
241
+ }
215
242
  let mutated = false;
216
243
  const nextDefaults = defaults ? { ...defaults } : {};
217
244
  if (!hasMax) {
@@ -223,8 +250,9 @@ export function applyAgentDefaults(cfg) {
223
250
  nextSubagents.maxConcurrent = DEFAULT_SUBAGENT_MAX_CONCURRENT;
224
251
  mutated = true;
225
252
  }
226
- if (!mutated)
253
+ if (!mutated) {
227
254
  return cfg;
255
+ }
228
256
  return {
229
257
  ...cfg,
230
258
  agents: {
@@ -238,10 +266,12 @@ export function applyAgentDefaults(cfg) {
238
266
  }
239
267
  export function applyLoggingDefaults(cfg) {
240
268
  const logging = cfg.logging;
241
- if (!logging)
269
+ if (!logging) {
242
270
  return cfg;
243
- if (logging.redactSensitive)
271
+ }
272
+ if (logging.redactSensitive) {
244
273
  return cfg;
274
+ }
245
275
  return {
246
276
  ...cfg,
247
277
  logging: {
@@ -252,11 +282,13 @@ export function applyLoggingDefaults(cfg) {
252
282
  }
253
283
  export function applyContextPruningDefaults(cfg) {
254
284
  const defaults = cfg.agents?.defaults;
255
- if (!defaults)
285
+ if (!defaults) {
256
286
  return cfg;
287
+ }
257
288
  const authMode = resolveAnthropicDefaultAuthMode(cfg);
258
- if (!authMode)
289
+ if (!authMode) {
259
290
  return cfg;
291
+ }
260
292
  let mutated = false;
261
293
  const nextDefaults = { ...defaults };
262
294
  const contextPruning = defaults.contextPruning ?? {};
@@ -281,15 +313,17 @@ export function applyContextPruningDefaults(cfg) {
281
313
  let modelsMutated = false;
282
314
  for (const [key, entry] of Object.entries(nextModels)) {
283
315
  const parsed = parseModelRef(key, "anthropic");
284
- if (!parsed || parsed.provider !== "anthropic")
316
+ if (!parsed || parsed.provider !== "anthropic") {
285
317
  continue;
318
+ }
286
319
  const current = entry ?? {};
287
320
  const params = current.params ?? {};
288
- if (typeof params.cacheControlTtl === "string")
321
+ if (typeof params.cacheRetention === "string") {
289
322
  continue;
323
+ }
290
324
  nextModels[key] = {
291
325
  ...current,
292
- params: { ...params, cacheControlTtl: "1h" },
326
+ params: { ...params, cacheRetention: "short" },
293
327
  };
294
328
  modelsMutated = true;
295
329
  }
@@ -301,10 +335,10 @@ export function applyContextPruningDefaults(cfg) {
301
335
  const entry = nextModels[key];
302
336
  const current = entry ?? {};
303
337
  const params = current.params ?? {};
304
- if (typeof params.cacheControlTtl !== "string") {
338
+ if (typeof params.cacheRetention !== "string") {
305
339
  nextModels[key] = {
306
340
  ...current,
307
- params: { ...params, cacheControlTtl: "1h" },
341
+ params: { ...params, cacheRetention: "short" },
308
342
  };
309
343
  modelsMutated = true;
310
344
  }
@@ -315,8 +349,9 @@ export function applyContextPruningDefaults(cfg) {
315
349
  mutated = true;
316
350
  }
317
351
  }
318
- if (!mutated)
352
+ if (!mutated) {
319
353
  return cfg;
354
+ }
320
355
  return {
321
356
  ...cfg,
322
357
  agents: {
@@ -327,11 +362,13 @@ export function applyContextPruningDefaults(cfg) {
327
362
  }
328
363
  export function applyCompactionDefaults(cfg) {
329
364
  const defaults = cfg.agents?.defaults;
330
- if (!defaults)
365
+ if (!defaults) {
331
366
  return cfg;
367
+ }
332
368
  const compaction = defaults?.compaction;
333
- if (compaction?.mode)
369
+ if (compaction?.mode) {
334
370
  return cfg;
371
+ }
335
372
  return {
336
373
  ...cfg,
337
374
  agents: {
@@ -1,26 +1,49 @@
1
1
  import { normalizeAccountId } from "../routing/session-key.js";
2
+ function resolveChannelGroupConfig(groups, groupId, caseInsensitive = false) {
3
+ if (!groups) {
4
+ return undefined;
5
+ }
6
+ const direct = groups[groupId];
7
+ if (direct) {
8
+ return direct;
9
+ }
10
+ if (!caseInsensitive) {
11
+ return undefined;
12
+ }
13
+ const target = groupId.toLowerCase();
14
+ const matchedKey = Object.keys(groups).find((key) => key !== "*" && key.toLowerCase() === target);
15
+ if (!matchedKey) {
16
+ return undefined;
17
+ }
18
+ return groups[matchedKey];
19
+ }
2
20
  function normalizeSenderKey(value) {
3
21
  const trimmed = value.trim();
4
- if (!trimmed)
22
+ if (!trimmed) {
5
23
  return "";
24
+ }
6
25
  const withoutAt = trimmed.startsWith("@") ? trimmed.slice(1) : trimmed;
7
26
  return withoutAt.toLowerCase();
8
27
  }
9
28
  export function resolveToolsBySender(params) {
10
29
  const toolsBySender = params.toolsBySender;
11
- if (!toolsBySender)
30
+ if (!toolsBySender) {
12
31
  return undefined;
32
+ }
13
33
  const entries = Object.entries(toolsBySender);
14
- if (entries.length === 0)
34
+ if (entries.length === 0) {
15
35
  return undefined;
36
+ }
16
37
  const normalized = new Map();
17
38
  let wildcard;
18
39
  for (const [rawKey, policy] of entries) {
19
- if (!policy)
40
+ if (!policy) {
20
41
  continue;
42
+ }
21
43
  const key = normalizeSenderKey(rawKey);
22
- if (!key)
44
+ if (!key) {
23
45
  continue;
46
+ }
24
47
  if (key === "*") {
25
48
  wildcard = policy;
26
49
  continue;
@@ -32,8 +55,9 @@ export function resolveToolsBySender(params) {
32
55
  const candidates = [];
33
56
  const pushCandidate = (value) => {
34
57
  const trimmed = value?.trim();
35
- if (!trimmed)
58
+ if (!trimmed) {
36
59
  return;
60
+ }
37
61
  candidates.push(trimmed);
38
62
  };
39
63
  pushCandidate(params.senderId);
@@ -42,19 +66,22 @@ export function resolveToolsBySender(params) {
42
66
  pushCandidate(params.senderName);
43
67
  for (const candidate of candidates) {
44
68
  const key = normalizeSenderKey(candidate);
45
- if (!key)
69
+ if (!key) {
46
70
  continue;
71
+ }
47
72
  const match = normalized.get(key);
48
- if (match)
73
+ if (match) {
49
74
  return match;
75
+ }
50
76
  }
51
77
  return wildcard;
52
78
  }
53
79
  function resolveChannelGroups(cfg, channel, accountId) {
54
80
  const normalizedAccountId = normalizeAccountId(accountId);
55
81
  const channelConfig = cfg.channels?.[channel];
56
- if (!channelConfig)
82
+ if (!channelConfig) {
57
83
  return undefined;
84
+ }
58
85
  const accountGroups = channelConfig.accounts?.[normalizedAccountId]?.groups ??
59
86
  channelConfig.accounts?.[Object.keys(channelConfig.accounts ?? {}).find((key) => key.toLowerCase() === normalizedAccountId.toLowerCase()) ?? ""]?.groups;
60
87
  return accountGroups ?? channelConfig.groups;
@@ -64,12 +91,12 @@ export function resolveChannelGroupPolicy(params) {
64
91
  const groups = resolveChannelGroups(cfg, channel, params.accountId);
65
92
  const allowlistEnabled = Boolean(groups && Object.keys(groups).length > 0);
66
93
  const normalizedId = params.groupId?.trim();
67
- const groupConfig = normalizedId && groups ? groups[normalizedId] : undefined;
94
+ const groupConfig = normalizedId
95
+ ? resolveChannelGroupConfig(groups, normalizedId, params.groupIdCaseInsensitive)
96
+ : undefined;
68
97
  const defaultConfig = groups?.["*"];
69
98
  const allowAll = allowlistEnabled && Boolean(groups && Object.hasOwn(groups, "*"));
70
- const allowed = !allowlistEnabled ||
71
- allowAll ||
72
- (normalizedId ? Boolean(groups && Object.hasOwn(groups, normalizedId)) : false);
99
+ const allowed = !allowlistEnabled || allowAll || Boolean(groupConfig);
73
100
  return {
74
101
  allowlistEnabled,
75
102
  allowed,
@@ -88,8 +115,9 @@ export function resolveChannelGroupRequireMention(params) {
88
115
  if (overrideOrder === "before-config" && typeof requireMentionOverride === "boolean") {
89
116
  return requireMentionOverride;
90
117
  }
91
- if (typeof configMention === "boolean")
118
+ if (typeof configMention === "boolean") {
92
119
  return configMention;
120
+ }
93
121
  if (overrideOrder !== "before-config" && typeof requireMentionOverride === "boolean") {
94
122
  return requireMentionOverride;
95
123
  }
@@ -104,10 +132,12 @@ export function resolveChannelGroupToolsPolicy(params) {
104
132
  senderUsername: params.senderUsername,
105
133
  senderE164: params.senderE164,
106
134
  });
107
- if (groupSenderPolicy)
135
+ if (groupSenderPolicy) {
108
136
  return groupSenderPolicy;
109
- if (groupConfig?.tools)
137
+ }
138
+ if (groupConfig?.tools) {
110
139
  return groupConfig.tools;
140
+ }
111
141
  const defaultSenderPolicy = resolveToolsBySender({
112
142
  toolsBySender: defaultConfig?.toolsBySender,
113
143
  senderId: params.senderId,
@@ -115,9 +145,11 @@ export function resolveChannelGroupToolsPolicy(params) {
115
145
  senderUsername: params.senderUsername,
116
146
  senderE164: params.senderE164,
117
147
  });
118
- if (defaultSenderPolicy)
148
+ if (defaultSenderPolicy) {
119
149
  return defaultSenderPolicy;
120
- if (defaultConfig?.tools)
150
+ }
151
+ if (defaultConfig?.tools) {
121
152
  return defaultConfig.tools;
153
+ }
122
154
  return undefined;
123
155
  }
@@ -12,6 +12,8 @@
12
12
  import fs from "node:fs";
13
13
  import path from "node:path";
14
14
  import JSON5 from "json5";
15
+ import { isPathInside } from "../security/scan-paths.js";
16
+ import { isPlainObject } from "../utils.js";
15
17
  export const INCLUDE_KEY = "$include";
16
18
  export const MAX_INCLUDE_DEPTH = 10;
17
19
  // ============================================================================
@@ -38,12 +40,6 @@ export class CircularIncludeError extends ConfigIncludeError {
38
40
  // ============================================================================
39
41
  // Utilities
40
42
  // ============================================================================
41
- function isPlainObject(value) {
42
- return (typeof value === "object" &&
43
- value !== null &&
44
- !Array.isArray(value) &&
45
- Object.prototype.toString.call(value) === "[object Object]");
46
- }
47
43
  /** Deep merge: arrays concatenate, objects merge recursively, primitives: source wins */
48
44
  export function deepMerge(target, source) {
49
45
  if (Array.isArray(target) && Array.isArray(source)) {
@@ -66,10 +62,14 @@ class IncludeProcessor {
66
62
  resolver;
67
63
  visited = new Set();
68
64
  depth = 0;
69
- constructor(basePath, resolver) {
65
+ rootDir;
66
+ rootRealDir;
67
+ constructor(basePath, resolver, rootDir) {
70
68
  this.basePath = basePath;
71
69
  this.resolver = resolver;
72
70
  this.visited.add(path.normalize(basePath));
71
+ this.rootDir = path.normalize(rootDir ?? path.dirname(basePath));
72
+ this.rootRealDir = path.normalize(safeRealpath(this.rootDir));
73
73
  }
74
74
  process(obj) {
75
75
  if (Array.isArray(obj)) {
@@ -130,10 +130,29 @@ class IncludeProcessor {
130
130
  return this.processNested(resolvedPath, parsed);
131
131
  }
132
132
  resolvePath(includePath) {
133
+ const configDir = path.dirname(this.basePath);
133
134
  const resolved = path.isAbsolute(includePath)
134
135
  ? includePath
135
- : path.resolve(path.dirname(this.basePath), includePath);
136
- return path.normalize(resolved);
136
+ : path.resolve(configDir, includePath);
137
+ const normalized = path.normalize(resolved);
138
+ // SECURITY: Reject paths outside top-level config directory (CWE-22: Path Traversal)
139
+ if (!isPathInside(this.rootDir, normalized)) {
140
+ throw new ConfigIncludeError(`Include path escapes config directory: ${includePath} (root: ${this.rootDir})`, includePath);
141
+ }
142
+ // SECURITY: Resolve symlinks and re-validate to prevent symlink bypass
143
+ try {
144
+ const real = fs.realpathSync(normalized);
145
+ if (!isPathInside(this.rootRealDir, real)) {
146
+ throw new ConfigIncludeError(`Include path resolves outside config directory (symlink): ${includePath} (root: ${this.rootDir})`, includePath);
147
+ }
148
+ }
149
+ catch (err) {
150
+ if (err instanceof ConfigIncludeError) {
151
+ throw err;
152
+ }
153
+ // File doesn't exist yet - normalized path check above is sufficient
154
+ }
155
+ return normalized;
137
156
  }
138
157
  checkCircular(resolvedPath) {
139
158
  if (this.visited.has(resolvedPath)) {
@@ -162,12 +181,20 @@ class IncludeProcessor {
162
181
  }
163
182
  }
164
183
  processNested(resolvedPath, parsed) {
165
- const nested = new IncludeProcessor(resolvedPath, this.resolver);
184
+ const nested = new IncludeProcessor(resolvedPath, this.resolver, this.rootDir);
166
185
  nested.visited = new Set([...this.visited, resolvedPath]);
167
186
  nested.depth = this.depth + 1;
168
187
  return nested.process(parsed);
169
188
  }
170
189
  }
190
+ function safeRealpath(target) {
191
+ try {
192
+ return fs.realpathSync(target);
193
+ }
194
+ catch {
195
+ return target;
196
+ }
197
+ }
171
198
  // ============================================================================
172
199
  // Public API
173
200
  // ============================================================================
@@ -1,6 +1,6 @@
1
1
  import { IRC_FIELD_HELP } from "./schema.irc.js";
2
2
  export const FIELD_HELP = {
3
- "meta.lastTouchedVersion": "Auto-set when OpenClaw writes the config.",
3
+ "meta.lastTouchedVersion": "Auto-set when Pool Bot writes the config.",
4
4
  "meta.lastTouchedAt": "ISO timestamp of the last config write (auto-set).",
5
5
  "update.channel": 'Update channel for git + npm installs ("stable", "beta", or "dev").',
6
6
  "update.checkOnStart": "Check for npm updates when the gateway starts (default: true).",
@@ -16,7 +16,7 @@ export const FIELD_HELP = {
16
16
  "discovery.mdns.mode": 'mDNS broadcast mode ("minimal" default, "full" includes cliPath/sshPort, "off" disables mDNS).',
17
17
  "gateway.auth.token": "Required by default for gateway access (unless using Tailscale Serve identity); required for non-loopback binds.",
18
18
  "gateway.auth.password": "Required for Tailscale funnel.",
19
- "gateway.controlUi.basePath": "Optional URL prefix where the Control UI is served (e.g. /openclaw).",
19
+ "gateway.controlUi.basePath": "Optional URL prefix where the Control UI is served (e.g. /poolbot).",
20
20
  "gateway.controlUi.root": "Optional filesystem root for Control UI assets (defaults to dist/control-ui).",
21
21
  "gateway.controlUi.allowedOrigins": "Allowed browser origins for Control UI/WebChat websocket connections (full origins only, e.g. https://control.example.com).",
22
22
  "gateway.controlUi.allowInsecureAuth": "Allow Control UI auth over insecure HTTP (token-only; not recommended).",
@@ -129,7 +129,7 @@ export const FIELD_HELP = {
129
129
  "agents.defaults.memorySearch.query.hybrid.candidateMultiplier": "Multiplier for candidate pool size (default: 4).",
130
130
  "agents.defaults.memorySearch.cache.enabled": "Cache chunk embeddings in SQLite to speed up reindexing and frequent updates (default: true).",
131
131
  memory: "Memory backend configuration (global).",
132
- "memory.backend": 'Memory backend ("builtin" for OpenClaw embeddings, "qmd" for QMD sidecar).',
132
+ "memory.backend": 'Memory backend ("builtin" for Pool Bot embeddings, "qmd" for QMD sidecar).',
133
133
  "memory.citations": 'Default citation behavior ("auto", "on", or "off").',
134
134
  "memory.qmd.command": "Path to the qmd binary (default: resolves from PATH).",
135
135
  "memory.qmd.includeDefaultMemory": "Whether to automatically index MEMORY.md + memory/**/*.md (default: true).",
@@ -167,7 +167,7 @@ export const FIELD_HELP = {
167
167
  "plugins.entries": "Per-plugin settings keyed by plugin id (enable/disable + config payloads).",
168
168
  "plugins.entries.*.enabled": "Overrides plugin enable/disable for this entry (restart required).",
169
169
  "plugins.entries.*.config": "Plugin-defined config payload (schema is provided by the plugin).",
170
- "plugins.installs": "CLI-managed install metadata (used by `openclaw plugins update` to locate install sources).",
170
+ "plugins.installs": "CLI-managed install metadata (used by `poolbot plugins update` to locate install sources).",
171
171
  "plugins.installs.*.source": 'Install source ("npm", "archive", or "path").',
172
172
  "plugins.installs.*.spec": "Original npm spec used for install (if source is npm).",
173
173
  "plugins.installs.*.sourcePath": "Original archive/path used for install (if any).",
@@ -179,6 +179,7 @@ export const FIELD_HELP = {
179
179
  "agents.defaults.model.fallbacks": "Ordered fallback models (provider/model). Used when the primary model fails.",
180
180
  "agents.defaults.imageModel.primary": "Optional image model (provider/model) used when the primary model lacks image input.",
181
181
  "agents.defaults.imageModel.fallbacks": "Ordered fallback image models (provider/model).",
182
+ "agents.defaults.imageMaxDimensionPx": "Max image side length in pixels when sanitizing transcript/tool-result image payloads (default: 1200).",
182
183
  "agents.defaults.cliBackends": "Optional CLI backends for text-only fallback (claude-cli, etc.).",
183
184
  "agents.defaults.humanDelay.mode": 'Delay style for block replies ("off", "natural", "custom").',
184
185
  "agents.defaults.humanDelay.minMs": "Minimum delay in ms for custom humanDelay (default: 800).",
@@ -62,11 +62,11 @@ const FIELD_PLACEHOLDERS = {
62
62
  "gateway.remote.url": "ws://host:18789",
63
63
  "gateway.remote.tlsFingerprint": "sha256:ab12cd34…",
64
64
  "gateway.remote.sshTarget": "user@host",
65
- "gateway.controlUi.basePath": "/openclaw",
65
+ "gateway.controlUi.basePath": "/poolbot",
66
66
  "gateway.controlUi.root": "dist/control-ui",
67
67
  "gateway.controlUi.allowedOrigins": "https://control.example.com",
68
68
  "channels.mattermost.baseUrl": "https://chat.example.com",
69
- "agents.list[].identity.avatar": "avatars/openclaw.png",
69
+ "agents.list[].identity.avatar": "avatars/poolbot.png",
70
70
  };
71
71
  /**
72
72
  * Non-sensitive field names that happen to match sensitive patterns.
@@ -201,6 +201,7 @@ export const FIELD_LABELS = {
201
201
  "agents.defaults.model.fallbacks": "Model Fallbacks",
202
202
  "agents.defaults.imageModel.primary": "Image Model",
203
203
  "agents.defaults.imageModel.fallbacks": "Image Model Fallbacks",
204
+ "agents.defaults.imageMaxDimensionPx": "Image Max Dimension (px)",
204
205
  "agents.defaults.humanDelay.mode": "Human Delay Mode",
205
206
  "agents.defaults.humanDelay.minMs": "Human Delay Min (ms)",
206
207
  "agents.defaults.humanDelay.maxMs": "Human Delay Max (ms)",
@@ -1,19 +1,17 @@
1
+ import { normalizeHyphenSlug } from "../../shared/string-normalization.js";
1
2
  import { listDeliverableMessageChannels } from "../../utils/message-channel.js";
2
3
  const getGroupSurfaces = () => new Set([...listDeliverableMessageChannels(), "webchat"]);
3
4
  function normalizeGroupLabel(raw) {
4
- const trimmed = raw?.trim().toLowerCase() ?? "";
5
- if (!trimmed)
6
- return "";
7
- const dashed = trimmed.replace(/\s+/g, "-");
8
- const cleaned = dashed.replace(/[^a-z0-9#@._+-]+/g, "-");
9
- return cleaned.replace(/-{2,}/g, "-").replace(/^[-.]+|[-.]+$/g, "");
5
+ return normalizeHyphenSlug(raw);
10
6
  }
11
7
  function shortenGroupId(value) {
12
8
  const trimmed = value?.trim() ?? "";
13
- if (!trimmed)
9
+ if (!trimmed) {
14
10
  return "";
15
- if (trimmed.length <= 14)
11
+ }
12
+ if (trimmed.length <= 14) {
16
13
  return trimmed;
14
+ }
17
15
  return `${trimmed.slice(0, 6)}...${trimmed.slice(-4)}`;
18
16
  }
19
17
  export function buildGroupDisplayName(params) {
@@ -48,8 +46,9 @@ export function resolveGroupSessionKey(ctx) {
48
46
  from.includes(":group:") ||
49
47
  from.includes(":channel:") ||
50
48
  isWhatsAppGroupId;
51
- if (!looksLikeGroup)
49
+ if (!looksLikeGroup) {
52
50
  return null;
51
+ }
53
52
  const providerHint = ctx.Provider?.trim().toLowerCase();
54
53
  const parts = from.split(":").filter(Boolean);
55
54
  const head = parts[0]?.trim().toLowerCase() ?? "";
@@ -57,8 +56,9 @@ export function resolveGroupSessionKey(ctx) {
57
56
  const provider = headIsSurface
58
57
  ? head
59
58
  : (providerHint ?? (isWhatsAppGroupId ? "whatsapp" : undefined));
60
- if (!provider)
59
+ if (!provider) {
61
60
  return null;
61
+ }
62
62
  const second = parts[1]?.trim().toLowerCase();
63
63
  const secondIsKind = second === "group" || second === "channel";
64
64
  const kind = secondIsKind
@@ -72,8 +72,9 @@ export function resolveGroupSessionKey(ctx) {
72
72
  : parts.slice(1).join(":")
73
73
  : from;
74
74
  const finalId = id.trim().toLowerCase();
75
- if (!finalId)
75
+ if (!finalId) {
76
76
  return null;
77
+ }
77
78
  return {
78
79
  key: `${provider}:${kind}:${finalId}`,
79
80
  channel: provider,