@poolzin/pool-bot 2026.2.21 → 2026.2.23

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 (378) hide show
  1. package/CHANGELOG.md +25 -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/device-pair/index.ts +2 -2
  326. package/extensions/diagnostics-otel/package.json +1 -1
  327. package/extensions/discord/package.json +1 -1
  328. package/extensions/feishu/package.json +1 -1
  329. package/extensions/google-antigravity-auth/package.json +1 -1
  330. package/extensions/google-gemini-cli-auth/package.json +1 -1
  331. package/extensions/googlechat/package.json +1 -1
  332. package/extensions/imessage/package.json +1 -1
  333. package/extensions/irc/package.json +1 -1
  334. package/extensions/irc/src/accounts.ts +1 -1
  335. package/extensions/irc/src/onboarding.ts +4 -4
  336. package/extensions/line/package.json +1 -1
  337. package/extensions/llm-task/package.json +1 -1
  338. package/extensions/lobster/package.json +1 -1
  339. package/extensions/matrix/CHANGELOG.md +10 -0
  340. package/extensions/matrix/package.json +1 -1
  341. package/extensions/mattermost/package.json +1 -1
  342. package/extensions/memory-core/package.json +1 -1
  343. package/extensions/memory-lancedb/package.json +1 -1
  344. package/extensions/minimax-portal-auth/package.json +1 -1
  345. package/extensions/msteams/CHANGELOG.md +10 -0
  346. package/extensions/msteams/package.json +1 -1
  347. package/extensions/nextcloud-talk/package.json +1 -1
  348. package/extensions/nostr/CHANGELOG.md +10 -0
  349. package/extensions/nostr/package.json +1 -1
  350. package/extensions/open-prose/package.json +1 -1
  351. package/extensions/openai-codex-auth/package.json +1 -1
  352. package/extensions/signal/package.json +1 -1
  353. package/extensions/slack/package.json +1 -1
  354. package/extensions/telegram/package.json +1 -1
  355. package/extensions/tlon/package.json +1 -1
  356. package/extensions/twitch/CHANGELOG.md +10 -0
  357. package/extensions/twitch/package.json +1 -1
  358. package/extensions/voice-call/CHANGELOG.md +10 -0
  359. package/extensions/voice-call/package.json +1 -1
  360. package/extensions/whatsapp/package.json +1 -1
  361. package/extensions/zalo/CHANGELOG.md +10 -0
  362. package/extensions/zalo/package.json +1 -1
  363. package/extensions/zalouser/CHANGELOG.md +10 -0
  364. package/extensions/zalouser/package.json +1 -1
  365. package/package.json +1 -1
  366. package/skills/apple-reminders/SKILL.md +100 -49
  367. package/skills/coding-agent/SKILL.md +34 -28
  368. package/skills/github/SKILL.md +131 -16
  369. package/skills/imsg/SKILL.md +112 -15
  370. package/skills/openhue/SKILL.md +101 -19
  371. package/skills/tmux/SKILL.md +111 -79
  372. package/skills/weather/SKILL.md +88 -25
  373. package/dist/agents/openclaw-tools.js +0 -151
  374. package/dist/agents/tool-security.js +0 -96
  375. package/dist/gateway/url-validation.js +0 -94
  376. package/dist/infra/openclaw-root.js +0 -109
  377. package/dist/infra/tmp-openclaw-dir.js +0 -81
  378. package/dist/media/path-sanitization.js +0 -78
@@ -1,4 +1,7 @@
1
1
  import { resolveDefaultAgentId } from "../agents/agent-scope.js";
2
+ import { normalizeChatType } from "../channels/chat-type.js";
3
+ import { shouldLogVerbose } from "../globals.js";
4
+ import { logDebug } from "../logger.js";
2
5
  import { listBindings } from "./bindings.js";
3
6
  import { buildAgentMainSessionKey, buildAgentPeerSessionKey, DEFAULT_ACCOUNT_ID, DEFAULT_MAIN_KEY, normalizeAgentId, sanitizeAgentId, } from "./session-key.js";
4
7
  export { DEFAULT_ACCOUNT_ID, DEFAULT_AGENT_ID } from "./session-key.js";
@@ -6,7 +9,13 @@ function normalizeToken(value) {
6
9
  return (value ?? "").trim().toLowerCase();
7
10
  }
8
11
  function normalizeId(value) {
9
- return (value ?? "").trim();
12
+ if (typeof value === "string") {
13
+ return value.trim();
14
+ }
15
+ if (typeof value === "number" || typeof value === "bigint") {
16
+ return String(value).trim();
17
+ }
18
+ return "";
10
19
  }
11
20
  function normalizeAccountId(value) {
12
21
  const trimmed = (value ?? "").trim();
@@ -14,10 +23,12 @@ function normalizeAccountId(value) {
14
23
  }
15
24
  function matchesAccountId(match, actual) {
16
25
  const trimmed = (match ?? "").trim();
17
- if (!trimmed)
26
+ if (!trimmed) {
18
27
  return actual === DEFAULT_ACCOUNT_ID;
19
- if (trimmed === "*")
28
+ }
29
+ if (trimmed === "*") {
20
30
  return true;
31
+ }
21
32
  return trimmed === actual;
22
33
  }
23
34
  export function buildAgentSessionKey(params) {
@@ -28,7 +39,7 @@ export function buildAgentSessionKey(params) {
28
39
  mainKey: DEFAULT_MAIN_KEY,
29
40
  channel,
30
41
  accountId: params.accountId,
31
- peerKind: peer?.kind ?? "dm",
42
+ peerKind: peer?.kind ?? "direct",
32
43
  peerId: peer ? normalizeId(peer.id) || "unknown" : null,
33
44
  dmScope: params.dmScope,
34
45
  identityLinks: params.identityLinks,
@@ -40,44 +51,116 @@ function listAgents(cfg) {
40
51
  }
41
52
  function pickFirstExistingAgentId(cfg, agentId) {
42
53
  const trimmed = (agentId ?? "").trim();
43
- if (!trimmed)
54
+ if (!trimmed) {
44
55
  return sanitizeAgentId(resolveDefaultAgentId(cfg));
56
+ }
45
57
  const normalized = normalizeAgentId(trimmed);
46
58
  const agents = listAgents(cfg);
47
- if (agents.length === 0)
59
+ if (agents.length === 0) {
48
60
  return sanitizeAgentId(trimmed);
61
+ }
49
62
  const match = agents.find((agent) => normalizeAgentId(agent.id) === normalized);
50
- if (match?.id?.trim())
63
+ if (match?.id?.trim()) {
51
64
  return sanitizeAgentId(match.id.trim());
65
+ }
52
66
  return sanitizeAgentId(resolveDefaultAgentId(cfg));
53
67
  }
54
68
  function matchesChannel(match, channel) {
55
69
  const key = normalizeToken(match?.channel);
56
- if (!key)
70
+ if (!key) {
57
71
  return false;
72
+ }
58
73
  return key === channel;
59
74
  }
60
- function matchesPeer(match, peer) {
61
- const m = match?.peer;
62
- if (!m)
75
+ const evaluatedBindingsCacheByCfg = new WeakMap();
76
+ const MAX_EVALUATED_BINDINGS_CACHE_KEYS = 2000;
77
+ function getEvaluatedBindingsForChannelAccount(cfg, channel, accountId) {
78
+ const bindingsRef = cfg.bindings;
79
+ const existing = evaluatedBindingsCacheByCfg.get(cfg);
80
+ const cache = existing && existing.bindingsRef === bindingsRef
81
+ ? existing
82
+ : { bindingsRef, byChannelAccount: new Map() };
83
+ if (cache !== existing) {
84
+ evaluatedBindingsCacheByCfg.set(cfg, cache);
85
+ }
86
+ const cacheKey = `${channel}\t${accountId}`;
87
+ const hit = cache.byChannelAccount.get(cacheKey);
88
+ if (hit) {
89
+ return hit;
90
+ }
91
+ const evaluated = listBindings(cfg).flatMap((binding) => {
92
+ if (!binding || typeof binding !== "object") {
93
+ return [];
94
+ }
95
+ if (!matchesChannel(binding.match, channel)) {
96
+ return [];
97
+ }
98
+ if (!matchesAccountId(binding.match?.accountId, accountId)) {
99
+ return [];
100
+ }
101
+ return [{ binding, match: normalizeBindingMatch(binding.match) }];
102
+ });
103
+ cache.byChannelAccount.set(cacheKey, evaluated);
104
+ if (cache.byChannelAccount.size > MAX_EVALUATED_BINDINGS_CACHE_KEYS) {
105
+ cache.byChannelAccount.clear();
106
+ cache.byChannelAccount.set(cacheKey, evaluated);
107
+ }
108
+ return evaluated;
109
+ }
110
+ function normalizePeerConstraint(peer) {
111
+ if (!peer) {
112
+ return { state: "none" };
113
+ }
114
+ const kind = normalizeChatType(peer.kind);
115
+ const id = normalizeId(peer.id);
116
+ if (!kind || !id) {
117
+ return { state: "invalid" };
118
+ }
119
+ return { state: "valid", kind, id };
120
+ }
121
+ function normalizeBindingMatch(match) {
122
+ const rawRoles = match?.roles;
123
+ return {
124
+ accountPattern: (match?.accountId ?? "").trim(),
125
+ peer: normalizePeerConstraint(match?.peer),
126
+ guildId: normalizeId(match?.guildId) || null,
127
+ teamId: normalizeId(match?.teamId) || null,
128
+ roles: Array.isArray(rawRoles) && rawRoles.length > 0 ? rawRoles : null,
129
+ };
130
+ }
131
+ function hasGuildConstraint(match) {
132
+ return Boolean(match.guildId);
133
+ }
134
+ function hasTeamConstraint(match) {
135
+ return Boolean(match.teamId);
136
+ }
137
+ function hasRolesConstraint(match) {
138
+ return Boolean(match.roles);
139
+ }
140
+ function matchesBindingScope(match, scope) {
141
+ if (match.peer.state === "invalid") {
63
142
  return false;
64
- const kind = normalizeToken(m.kind);
65
- const id = normalizeId(m.id);
66
- if (!kind || !id)
143
+ }
144
+ if (match.peer.state === "valid") {
145
+ if (!scope.peer || scope.peer.kind !== match.peer.kind || scope.peer.id !== match.peer.id) {
146
+ return false;
147
+ }
148
+ }
149
+ if (match.guildId && match.guildId !== scope.guildId) {
67
150
  return false;
68
- return kind === peer.kind && id === peer.id;
69
- }
70
- function matchesGuild(match, guildId) {
71
- const id = normalizeId(match?.guildId);
72
- if (!id)
151
+ }
152
+ if (match.teamId && match.teamId !== scope.teamId) {
73
153
  return false;
74
- return id === guildId;
75
- }
76
- function matchesTeam(match, teamId) {
77
- const id = normalizeId(match?.teamId);
78
- if (!id)
154
+ }
155
+ if (match.roles) {
156
+ for (const role of match.roles) {
157
+ if (scope.memberRoleIds.has(role)) {
158
+ return true;
159
+ }
160
+ }
79
161
  return false;
80
- return id === teamId;
162
+ }
163
+ return true;
81
164
  }
82
165
  export function resolveAgentRoute(input) {
83
166
  const channel = normalizeToken(input.channel);
@@ -85,13 +168,9 @@ export function resolveAgentRoute(input) {
85
168
  const peer = input.peer ? { kind: input.peer.kind, id: normalizeId(input.peer.id) } : null;
86
169
  const guildId = normalizeId(input.guildId);
87
170
  const teamId = normalizeId(input.teamId);
88
- const bindings = listBindings(input.cfg).filter((binding) => {
89
- if (!binding || typeof binding !== "object")
90
- return false;
91
- if (!matchesChannel(binding.match, channel))
92
- return false;
93
- return matchesAccountId(binding.match?.accountId, accountId);
94
- });
171
+ const memberRoleIds = input.memberRoleIds ?? [];
172
+ const memberRoleIdSet = new Set(memberRoleIds);
173
+ const bindings = getEvaluatedBindingsForChannelAccount(input.cfg, channel, accountId);
95
174
  const dmScope = input.cfg.session?.dmScope ?? "main";
96
175
  const identityLinks = input.cfg.session?.identityLinks;
97
176
  const choose = (agentId, matchedBy) => {
@@ -117,36 +196,91 @@ export function resolveAgentRoute(input) {
117
196
  matchedBy,
118
197
  };
119
198
  };
120
- if (peer) {
121
- const peerMatch = bindings.find((b) => matchesPeer(b.match, peer));
122
- if (peerMatch)
123
- return choose(peerMatch.agentId, "binding.peer");
199
+ const shouldLogDebug = shouldLogVerbose();
200
+ const formatPeer = (value) => value?.kind && value?.id ? `${value.kind}:${value.id}` : "none";
201
+ const formatNormalizedPeer = (value) => {
202
+ if (value.state === "none") {
203
+ return "none";
204
+ }
205
+ if (value.state === "invalid") {
206
+ return "invalid";
207
+ }
208
+ return `${value.kind}:${value.id}`;
209
+ };
210
+ if (shouldLogDebug) {
211
+ logDebug(`[routing] resolveAgentRoute: channel=${channel} accountId=${accountId} peer=${formatPeer(peer)} guildId=${guildId || "none"} teamId=${teamId || "none"} bindings=${bindings.length}`);
212
+ for (const entry of bindings) {
213
+ logDebug(`[routing] binding: agentId=${entry.binding.agentId} accountPattern=${entry.match.accountPattern || "default"} peer=${formatNormalizedPeer(entry.match.peer)} guildId=${entry.match.guildId ?? "none"} teamId=${entry.match.teamId ?? "none"} roles=${entry.match.roles?.length ?? 0}`);
214
+ }
124
215
  }
125
216
  // Thread parent inheritance: if peer (thread) didn't match, check parent peer binding
126
217
  const parentPeer = input.parentPeer
127
218
  ? { kind: input.parentPeer.kind, id: normalizeId(input.parentPeer.id) }
128
219
  : null;
129
- if (parentPeer && parentPeer.id) {
130
- const parentPeerMatch = bindings.find((b) => matchesPeer(b.match, parentPeer));
131
- if (parentPeerMatch) {
132
- return choose(parentPeerMatch.agentId, "binding.peer.parent");
220
+ const baseScope = {
221
+ guildId,
222
+ teamId,
223
+ memberRoleIds: memberRoleIdSet,
224
+ };
225
+ const tiers = [
226
+ {
227
+ matchedBy: "binding.peer",
228
+ enabled: Boolean(peer),
229
+ scopePeer: peer,
230
+ predicate: (candidate) => candidate.match.peer.state === "valid",
231
+ },
232
+ {
233
+ matchedBy: "binding.peer.parent",
234
+ enabled: Boolean(parentPeer && parentPeer.id),
235
+ scopePeer: parentPeer && parentPeer.id ? parentPeer : null,
236
+ predicate: (candidate) => candidate.match.peer.state === "valid",
237
+ },
238
+ {
239
+ matchedBy: "binding.guild+roles",
240
+ enabled: Boolean(guildId && memberRoleIds.length > 0),
241
+ scopePeer: peer,
242
+ predicate: (candidate) => hasGuildConstraint(candidate.match) && hasRolesConstraint(candidate.match),
243
+ },
244
+ {
245
+ matchedBy: "binding.guild",
246
+ enabled: Boolean(guildId),
247
+ scopePeer: peer,
248
+ predicate: (candidate) => hasGuildConstraint(candidate.match) && !hasRolesConstraint(candidate.match),
249
+ },
250
+ {
251
+ matchedBy: "binding.team",
252
+ enabled: Boolean(teamId),
253
+ scopePeer: peer,
254
+ predicate: (candidate) => hasTeamConstraint(candidate.match),
255
+ },
256
+ {
257
+ matchedBy: "binding.account",
258
+ enabled: true,
259
+ scopePeer: peer,
260
+ predicate: (candidate) => candidate.match.accountPattern !== "*",
261
+ },
262
+ {
263
+ matchedBy: "binding.channel",
264
+ enabled: true,
265
+ scopePeer: peer,
266
+ predicate: (candidate) => candidate.match.accountPattern === "*",
267
+ },
268
+ ];
269
+ for (const tier of tiers) {
270
+ if (!tier.enabled) {
271
+ continue;
272
+ }
273
+ const matched = bindings.find((candidate) => tier.predicate(candidate) &&
274
+ matchesBindingScope(candidate.match, {
275
+ ...baseScope,
276
+ peer: tier.scopePeer,
277
+ }));
278
+ if (matched) {
279
+ if (shouldLogDebug) {
280
+ logDebug(`[routing] match: matchedBy=${tier.matchedBy} agentId=${matched.binding.agentId}`);
281
+ }
282
+ return choose(matched.binding.agentId, tier.matchedBy);
133
283
  }
134
284
  }
135
- if (guildId) {
136
- const guildMatch = bindings.find((b) => matchesGuild(b.match, guildId));
137
- if (guildMatch)
138
- return choose(guildMatch.agentId, "binding.guild");
139
- }
140
- if (teamId) {
141
- const teamMatch = bindings.find((b) => matchesTeam(b.match, teamId));
142
- if (teamMatch)
143
- return choose(teamMatch.agentId, "binding.team");
144
- }
145
- const accountMatch = bindings.find((b) => b.match?.accountId?.trim() !== "*" && !b.match?.peer && !b.match?.guildId && !b.match?.teamId);
146
- if (accountMatch)
147
- return choose(accountMatch.agentId, "binding.account");
148
- const anyAccountMatch = bindings.find((b) => b.match?.accountId?.trim() === "*" && !b.match?.peer && !b.match?.guildId && !b.match?.teamId);
149
- if (anyAccountMatch)
150
- return choose(anyAccountMatch.agentId, "binding.channel");
151
285
  return choose(resolveDefaultAgentId(input.cfg), "default");
152
286
  }
@@ -1,5 +1,5 @@
1
1
  import { parseAgentSessionKey } from "../sessions/session-key-utils.js";
2
- export { isAcpSessionKey, isSubagentSessionKey, parseAgentSessionKey, } from "../sessions/session-key-utils.js";
2
+ export { getSubagentDepth, isCronSessionKey, isAcpSessionKey, isSubagentSessionKey, parseAgentSessionKey, } from "../sessions/session-key-utils.js";
3
3
  export const DEFAULT_AGENT_ID = "main";
4
4
  export const DEFAULT_MAIN_KEY = "main";
5
5
  export const DEFAULT_ACCOUNT_ID = "default";
@@ -17,8 +17,9 @@ export function normalizeMainKey(value) {
17
17
  }
18
18
  export function toAgentRequestSessionKey(storeKey) {
19
19
  const raw = (storeKey ?? "").trim();
20
- if (!raw)
20
+ if (!raw) {
21
21
  return undefined;
22
+ }
22
23
  return parseAgentSessionKey(raw)?.rest ?? raw;
23
24
  }
24
25
  export function toAgentStoreSessionKey(params) {
@@ -27,8 +28,9 @@ export function toAgentStoreSessionKey(params) {
27
28
  return buildAgentMainSessionKey({ agentId: params.agentId, mainKey: params.mainKey });
28
29
  }
29
30
  const lowered = raw.toLowerCase();
30
- if (lowered.startsWith("agent:"))
31
+ if (lowered.startsWith("agent:")) {
31
32
  return lowered;
33
+ }
32
34
  if (lowered.startsWith("subagent:")) {
33
35
  return `agent:${normalizeAgentId(params.agentId)}:${lowered}`;
34
36
  }
@@ -50,11 +52,13 @@ export function classifySessionKeyShape(sessionKey) {
50
52
  }
51
53
  export function normalizeAgentId(value) {
52
54
  const trimmed = (value ?? "").trim();
53
- if (!trimmed)
55
+ if (!trimmed) {
54
56
  return DEFAULT_AGENT_ID;
57
+ }
55
58
  // Keep it path-safe + shell-friendly.
56
- if (VALID_ID_RE.test(trimmed))
59
+ if (VALID_ID_RE.test(trimmed)) {
57
60
  return trimmed.toLowerCase();
61
+ }
58
62
  // Best-effort fallback: collapse invalid characters to "-"
59
63
  return (trimmed
60
64
  .toLowerCase()
@@ -65,10 +69,12 @@ export function normalizeAgentId(value) {
65
69
  }
66
70
  export function sanitizeAgentId(value) {
67
71
  const trimmed = (value ?? "").trim();
68
- if (!trimmed)
72
+ if (!trimmed) {
69
73
  return DEFAULT_AGENT_ID;
70
- if (VALID_ID_RE.test(trimmed))
74
+ }
75
+ if (VALID_ID_RE.test(trimmed)) {
71
76
  return trimmed.toLowerCase();
77
+ }
72
78
  return (trimmed
73
79
  .toLowerCase()
74
80
  .replace(INVALID_CHARS_RE, "-")
@@ -78,10 +84,12 @@ export function sanitizeAgentId(value) {
78
84
  }
79
85
  export function normalizeAccountId(value) {
80
86
  const trimmed = (value ?? "").trim();
81
- if (!trimmed)
87
+ if (!trimmed) {
82
88
  return DEFAULT_ACCOUNT_ID;
83
- if (VALID_ID_RE.test(trimmed))
89
+ }
90
+ if (VALID_ID_RE.test(trimmed)) {
84
91
  return trimmed.toLowerCase();
92
+ }
85
93
  return (trimmed
86
94
  .toLowerCase()
87
95
  .replace(INVALID_CHARS_RE, "-")
@@ -95,8 +103,8 @@ export function buildAgentMainSessionKey(params) {
95
103
  return `agent:${agentId}:${mainKey}`;
96
104
  }
97
105
  export function buildAgentPeerSessionKey(params) {
98
- const peerKind = params.peerKind ?? "dm";
99
- if (peerKind === "dm") {
106
+ const peerKind = params.peerKind ?? "direct";
107
+ if (peerKind === "direct") {
100
108
  const dmScope = params.dmScope ?? "main";
101
109
  let peerId = (params.peerId ?? "").trim();
102
110
  const linkedPeerId = dmScope === "main"
@@ -106,20 +114,21 @@ export function buildAgentPeerSessionKey(params) {
106
114
  channel: params.channel,
107
115
  peerId,
108
116
  });
109
- if (linkedPeerId)
117
+ if (linkedPeerId) {
110
118
  peerId = linkedPeerId;
119
+ }
111
120
  peerId = peerId.toLowerCase();
112
121
  if (dmScope === "per-account-channel-peer" && peerId) {
113
122
  const channel = (params.channel ?? "").trim().toLowerCase() || "unknown";
114
123
  const accountId = normalizeAccountId(params.accountId);
115
- return `agent:${normalizeAgentId(params.agentId)}:${channel}:${accountId}:dm:${peerId}`;
124
+ return `agent:${normalizeAgentId(params.agentId)}:${channel}:${accountId}:direct:${peerId}`;
116
125
  }
117
126
  if (dmScope === "per-channel-peer" && peerId) {
118
127
  const channel = (params.channel ?? "").trim().toLowerCase() || "unknown";
119
- return `agent:${normalizeAgentId(params.agentId)}:${channel}:dm:${peerId}`;
128
+ return `agent:${normalizeAgentId(params.agentId)}:${channel}:direct:${peerId}`;
120
129
  }
121
130
  if (dmScope === "per-peer" && peerId) {
122
- return `agent:${normalizeAgentId(params.agentId)}:dm:${peerId}`;
131
+ return `agent:${normalizeAgentId(params.agentId)}:direct:${peerId}`;
123
132
  }
124
133
  return buildAgentMainSessionKey({
125
134
  agentId: params.agentId,
@@ -132,29 +141,36 @@ export function buildAgentPeerSessionKey(params) {
132
141
  }
133
142
  function resolveLinkedPeerId(params) {
134
143
  const identityLinks = params.identityLinks;
135
- if (!identityLinks)
144
+ if (!identityLinks) {
136
145
  return null;
146
+ }
137
147
  const peerId = params.peerId.trim();
138
- if (!peerId)
148
+ if (!peerId) {
139
149
  return null;
150
+ }
140
151
  const candidates = new Set();
141
152
  const rawCandidate = normalizeToken(peerId);
142
- if (rawCandidate)
153
+ if (rawCandidate) {
143
154
  candidates.add(rawCandidate);
155
+ }
144
156
  const channel = normalizeToken(params.channel);
145
157
  if (channel) {
146
158
  const scopedCandidate = normalizeToken(`${channel}:${peerId}`);
147
- if (scopedCandidate)
159
+ if (scopedCandidate) {
148
160
  candidates.add(scopedCandidate);
161
+ }
149
162
  }
150
- if (candidates.size === 0)
163
+ if (candidates.size === 0) {
151
164
  return null;
165
+ }
152
166
  for (const [canonical, ids] of Object.entries(identityLinks)) {
153
167
  const canonicalName = canonical.trim();
154
- if (!canonicalName)
168
+ if (!canonicalName) {
155
169
  continue;
156
- if (!Array.isArray(ids))
170
+ }
171
+ if (!Array.isArray(ids)) {
157
172
  continue;
173
+ }
158
174
  for (const id of ids) {
159
175
  const normalized = normalizeToken(id);
160
176
  if (normalized && candidates.has(normalized)) {
package/dist/runtime.js CHANGED
@@ -1,17 +1,43 @@
1
1
  import { clearActiveProgressLine } from "./terminal/progress-line.js";
2
2
  import { restoreTerminalState } from "./terminal/restore.js";
3
+ function shouldEmitRuntimeLog(env = process.env) {
4
+ if (env.VITEST !== "true") {
5
+ return true;
6
+ }
7
+ if (env.POOLBOT_TEST_RUNTIME_LOG === "1") {
8
+ return true;
9
+ }
10
+ const maybeMockedLog = console.log;
11
+ return typeof maybeMockedLog.mock === "object";
12
+ }
13
+ function createRuntimeIo() {
14
+ return {
15
+ log: (...args) => {
16
+ if (!shouldEmitRuntimeLog()) {
17
+ return;
18
+ }
19
+ clearActiveProgressLine();
20
+ console.log(...args);
21
+ },
22
+ error: (...args) => {
23
+ clearActiveProgressLine();
24
+ console.error(...args);
25
+ },
26
+ };
27
+ }
3
28
  export const defaultRuntime = {
4
- log: (...args) => {
5
- clearActiveProgressLine();
6
- console.log(...args);
7
- },
8
- error: (...args) => {
9
- clearActiveProgressLine();
10
- console.error(...args);
11
- },
29
+ ...createRuntimeIo(),
12
30
  exit: (code) => {
13
- restoreTerminalState("runtime exit");
31
+ restoreTerminalState("runtime exit", { resumeStdinIfPaused: false });
14
32
  process.exit(code);
15
33
  throw new Error("unreachable"); // satisfies tests when mocked
16
34
  },
17
35
  };
36
+ export function createNonExitingRuntime() {
37
+ return {
38
+ ...createRuntimeIo(),
39
+ exit: (code) => {
40
+ throw new Error(`exit ${code}`);
41
+ },
42
+ };
43
+ }
@@ -90,7 +90,7 @@ export async function collectChannelSecurityFindings(params) {
90
90
  title: `${input.label} DMs share the main session`,
91
91
  detail: "Multiple DM senders currently share the main session, which can leak context across users.",
92
92
  remediation: "Run: " +
93
- formatCliCommand('openclaw config set session.dmScope "per-channel-peer"') +
93
+ formatCliCommand('poolbot config set session.dmScope "per-channel-peer"') +
94
94
  ' (or "per-account-channel-peer" for multi-account channels) to isolate DM sessions per sender.',
95
95
  });
96
96
  }
@@ -1,16 +1,20 @@
1
1
  export function parseAgentSessionKey(sessionKey) {
2
2
  const raw = (sessionKey ?? "").trim();
3
- if (!raw)
3
+ if (!raw) {
4
4
  return null;
5
+ }
5
6
  const parts = raw.split(":").filter(Boolean);
6
- if (parts.length < 3)
7
+ if (parts.length < 3) {
7
8
  return null;
8
- if (parts[0] !== "agent")
9
+ }
10
+ if (parts[0] !== "agent") {
9
11
  return null;
12
+ }
10
13
  const agentId = parts[1]?.trim();
11
14
  const rest = parts.slice(2).join(":");
12
- if (!agentId || !rest)
15
+ if (!agentId || !rest) {
13
16
  return null;
17
+ }
14
18
  return { agentId, rest };
15
19
  }
16
20
  export function isCronRunSessionKey(sessionKey) {
@@ -20,12 +24,21 @@ export function isCronRunSessionKey(sessionKey) {
20
24
  }
21
25
  return /^cron:[^:]+:run:[^:]+$/.test(parsed.rest);
22
26
  }
27
+ export function isCronSessionKey(sessionKey) {
28
+ const parsed = parseAgentSessionKey(sessionKey);
29
+ if (!parsed) {
30
+ return false;
31
+ }
32
+ return parsed.rest.toLowerCase().startsWith("cron:");
33
+ }
23
34
  export function isSubagentSessionKey(sessionKey) {
24
35
  const raw = (sessionKey ?? "").trim();
25
- if (!raw)
36
+ if (!raw) {
26
37
  return false;
27
- if (raw.toLowerCase().startsWith("subagent:"))
38
+ }
39
+ if (raw.toLowerCase().startsWith("subagent:")) {
28
40
  return true;
41
+ }
29
42
  const parsed = parseAgentSessionKey(raw);
30
43
  return Boolean((parsed?.rest ?? "").toLowerCase().startsWith("subagent:"));
31
44
  }
@@ -38,28 +51,33 @@ export function getSubagentDepth(sessionKey) {
38
51
  }
39
52
  export function isAcpSessionKey(sessionKey) {
40
53
  const raw = (sessionKey ?? "").trim();
41
- if (!raw)
54
+ if (!raw) {
42
55
  return false;
56
+ }
43
57
  const normalized = raw.toLowerCase();
44
- if (normalized.startsWith("acp:"))
58
+ if (normalized.startsWith("acp:")) {
45
59
  return true;
60
+ }
46
61
  const parsed = parseAgentSessionKey(raw);
47
62
  return Boolean((parsed?.rest ?? "").toLowerCase().startsWith("acp:"));
48
63
  }
49
64
  const THREAD_SESSION_MARKERS = [":thread:", ":topic:"];
50
65
  export function resolveThreadParentSessionKey(sessionKey) {
51
66
  const raw = (sessionKey ?? "").trim();
52
- if (!raw)
67
+ if (!raw) {
53
68
  return null;
69
+ }
54
70
  const normalized = raw.toLowerCase();
55
71
  let idx = -1;
56
72
  for (const marker of THREAD_SESSION_MARKERS) {
57
73
  const candidate = normalized.lastIndexOf(marker);
58
- if (candidate > idx)
74
+ if (candidate > idx) {
59
75
  idx = candidate;
76
+ }
60
77
  }
61
- if (idx <= 0)
78
+ if (idx <= 0) {
62
79
  return null;
80
+ }
63
81
  const parent = raw.slice(0, idx).trim();
64
82
  return parent ? parent : null;
65
83
  }
@@ -24,7 +24,7 @@ export function parseFrontmatterBool(value, fallback) {
24
24
  const parsed = parseBooleanValue(value);
25
25
  return parsed === undefined ? fallback : parsed;
26
26
  }
27
- export function resolveOpenClawManifestBlock(params) {
27
+ export function resolvePoolbotManifestBlock(params) {
28
28
  const raw = getFrontmatterString(params.frontmatter, params.key ?? "metadata");
29
29
  if (!raw) {
30
30
  return undefined;
@@ -47,7 +47,7 @@ export function resolveOpenClawManifestBlock(params) {
47
47
  return undefined;
48
48
  }
49
49
  }
50
- export function resolveOpenClawManifestRequires(metadataObj) {
50
+ export function resolvePoolbotManifestRequires(metadataObj) {
51
51
  const requiresRaw = typeof metadataObj.requires === "object" && metadataObj.requires !== null
52
52
  ? metadataObj.requires
53
53
  : undefined;
@@ -61,16 +61,16 @@ export function resolveOpenClawManifestRequires(metadataObj) {
61
61
  config: normalizeStringList(requiresRaw.config),
62
62
  };
63
63
  }
64
- export function resolveOpenClawManifestInstall(metadataObj, parseInstallSpec) {
64
+ export function resolvePoolbotManifestInstall(metadataObj, parseInstallSpec) {
65
65
  const installRaw = Array.isArray(metadataObj.install) ? metadataObj.install : [];
66
66
  return installRaw
67
67
  .map((entry) => parseInstallSpec(entry))
68
68
  .filter((entry) => Boolean(entry));
69
69
  }
70
- export function resolveOpenClawManifestOs(metadataObj) {
70
+ export function resolvePoolbotManifestOs(metadataObj) {
71
71
  return normalizeStringList(metadataObj.os);
72
72
  }
73
- export function parseOpenClawManifestInstallBase(input, allowedKinds) {
73
+ export function parsePoolbotManifestInstallBase(input, allowedKinds) {
74
74
  if (!input || typeof input !== "object") {
75
75
  return undefined;
76
76
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ export function normalizeStringEntries(list) {
2
+ return (list ?? []).map((entry) => String(entry).trim()).filter(Boolean);
3
+ }
4
+ export function normalizeStringEntriesLower(list) {
5
+ return normalizeStringEntries(list).map((entry) => entry.toLowerCase());
6
+ }
7
+ export function normalizeHyphenSlug(raw) {
8
+ const trimmed = raw?.trim().toLowerCase() ?? "";
9
+ if (!trimmed) {
10
+ return "";
11
+ }
12
+ const dashed = trimmed.replace(/\s+/g, "-");
13
+ const cleaned = dashed.replace(/[^a-z0-9#@._+-]+/g, "-");
14
+ return cleaned.replace(/-{2,}/g, "-").replace(/^[-.]+|[-.]+$/g, "");
15
+ }