@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,30 +1,44 @@
1
1
  import fs from "node:fs/promises";
2
- import os from "node:os";
3
2
  import path from "node:path";
4
- import { LEGACY_MANIFEST_KEY } from "../compat/legacy-names.js";
5
- import { runCommandWithTimeout } from "../process/exec.js";
6
- import { CONFIG_DIR, resolveUserPath } from "../utils.js";
3
+ import { MANIFEST_KEY } from "../compat/legacy-names.js";
7
4
  import { extractArchive, fileExists, readJsonFile, resolveArchiveKind, resolvePackedRootDir, } from "../infra/archive.js";
5
+ import { installPackageDir } from "../infra/install-package-dir.js";
6
+ import { resolveSafeInstallDir, unscopedPackageName } from "../infra/install-safe-path.js";
7
+ import { packNpmSpecToArchive, resolveArchiveSourcePath, withTempDir, } from "../infra/install-source-utils.js";
8
+ import { validateRegistryNpmSpec } from "../infra/npm-registry-spec.js";
9
+ import { CONFIG_DIR, resolveUserPath } from "../utils.js";
8
10
  import { parseFrontmatter } from "./frontmatter.js";
9
11
  const defaultLogger = {};
10
- function unscopedPackageName(name) {
11
- const trimmed = name.trim();
12
- if (!trimmed)
13
- return trimmed;
14
- return trimmed.includes("/") ? (trimmed.split("/").pop() ?? trimmed) : trimmed;
15
- }
16
- function safeDirName(input) {
17
- const trimmed = input.trim();
18
- if (!trimmed)
19
- return trimmed;
20
- return trimmed.replaceAll("/", "__");
12
+ function validateHookId(hookId) {
13
+ if (!hookId) {
14
+ return "invalid hook name: missing";
15
+ }
16
+ if (hookId === "." || hookId === "..") {
17
+ return "invalid hook name: reserved path segment";
18
+ }
19
+ if (hookId.includes("/") || hookId.includes("\\")) {
20
+ return "invalid hook name: path separators not allowed";
21
+ }
22
+ return null;
21
23
  }
22
24
  export function resolveHookInstallDir(hookId, hooksDir) {
23
25
  const hooksBase = hooksDir ? resolveUserPath(hooksDir) : path.join(CONFIG_DIR, "hooks");
24
- return path.join(hooksBase, safeDirName(hookId));
26
+ const hookIdError = validateHookId(hookId);
27
+ if (hookIdError) {
28
+ throw new Error(hookIdError);
29
+ }
30
+ const targetDirResult = resolveSafeInstallDir({
31
+ baseDir: hooksBase,
32
+ id: hookId,
33
+ invalidNameMessage: "invalid hook name: path traversal detected",
34
+ });
35
+ if (!targetDirResult.ok) {
36
+ throw new Error(targetDirResult.error);
37
+ }
38
+ return targetDirResult.path;
25
39
  }
26
40
  async function ensurePoolbotHooks(manifest) {
27
- const hooks = manifest.poolbot?.hooks ?? manifest[LEGACY_MANIFEST_KEY]?.hooks;
41
+ const hooks = manifest[MANIFEST_KEY]?.hooks;
28
42
  if (!Array.isArray(hooks)) {
29
43
  throw new Error("package.json missing poolbot.hooks");
30
44
  }
@@ -34,6 +48,32 @@ async function ensurePoolbotHooks(manifest) {
34
48
  }
35
49
  return list;
36
50
  }
51
+ function resolveHookInstallModeOptions(params) {
52
+ return {
53
+ logger: params.logger ?? defaultLogger,
54
+ mode: params.mode ?? "install",
55
+ dryRun: params.dryRun ?? false,
56
+ };
57
+ }
58
+ function resolveTimedHookInstallModeOptions(params) {
59
+ return {
60
+ ...resolveHookInstallModeOptions(params),
61
+ timeoutMs: params.timeoutMs ?? 120_000,
62
+ };
63
+ }
64
+ async function resolveInstallTargetDir(id, hooksDir) {
65
+ const baseHooksDir = hooksDir ? resolveUserPath(hooksDir) : path.join(CONFIG_DIR, "hooks");
66
+ await fs.mkdir(baseHooksDir, { recursive: true });
67
+ const targetDirResult = resolveSafeInstallDir({
68
+ baseDir: baseHooksDir,
69
+ id,
70
+ invalidNameMessage: "invalid hook name: path traversal detected",
71
+ });
72
+ if (!targetDirResult.ok) {
73
+ return { ok: false, error: targetDirResult.error };
74
+ }
75
+ return { ok: true, targetDir: targetDirResult.path };
76
+ }
37
77
  async function resolveHookNameFromDir(hookDir) {
38
78
  const hookMdPath = path.join(hookDir, "HOOK.md");
39
79
  if (!(await fileExists(hookMdPath))) {
@@ -55,10 +95,7 @@ async function validateHookDir(hookDir) {
55
95
  }
56
96
  }
57
97
  async function installHookPackageFromDir(params) {
58
- const logger = params.logger ?? defaultLogger;
59
- const timeoutMs = params.timeoutMs ?? 120_000;
60
- const mode = params.mode ?? "install";
61
- const dryRun = params.dryRun ?? false;
98
+ const { logger, timeoutMs, mode, dryRun } = resolveTimedHookInstallModeOptions(params);
62
99
  const manifestPath = path.join(params.packageDir, "package.json");
63
100
  if (!(await fileExists(manifestPath))) {
64
101
  return { ok: false, error: "package.json missing" };
@@ -79,17 +116,21 @@ async function installHookPackageFromDir(params) {
79
116
  }
80
117
  const pkgName = typeof manifest.name === "string" ? manifest.name : "";
81
118
  const hookPackId = pkgName ? unscopedPackageName(pkgName) : path.basename(params.packageDir);
119
+ const hookIdError = validateHookId(hookPackId);
120
+ if (hookIdError) {
121
+ return { ok: false, error: hookIdError };
122
+ }
82
123
  if (params.expectedHookPackId && params.expectedHookPackId !== hookPackId) {
83
124
  return {
84
125
  ok: false,
85
126
  error: `hook pack id mismatch: expected ${params.expectedHookPackId}, got ${hookPackId}`,
86
127
  };
87
128
  }
88
- const hooksDir = params.hooksDir
89
- ? resolveUserPath(params.hooksDir)
90
- : path.join(CONFIG_DIR, "hooks");
91
- await fs.mkdir(hooksDir, { recursive: true });
92
- const targetDir = resolveHookInstallDir(hookPackId, hooksDir);
129
+ const targetDirResult = await resolveInstallTargetDir(hookPackId, params.hooksDir);
130
+ if (!targetDirResult.ok) {
131
+ return { ok: false, error: targetDirResult.error };
132
+ }
133
+ const targetDir = targetDirResult.targetDir;
93
134
  if (mode === "install" && (await fileExists(targetDir))) {
94
135
  return { ok: false, error: `hook pack already exists: ${targetDir} (delete it first)` };
95
136
  }
@@ -109,43 +150,20 @@ async function installHookPackageFromDir(params) {
109
150
  version: typeof manifest.version === "string" ? manifest.version : undefined,
110
151
  };
111
152
  }
112
- logger.info?.(`Installing to ${targetDir}…`);
113
- let backupDir = null;
114
- if (mode === "update" && (await fileExists(targetDir))) {
115
- backupDir = `${targetDir}.backup-${Date.now()}`;
116
- await fs.rename(targetDir, backupDir);
117
- }
118
- try {
119
- await fs.cp(params.packageDir, targetDir, { recursive: true });
120
- }
121
- catch (err) {
122
- if (backupDir) {
123
- await fs.rm(targetDir, { recursive: true, force: true }).catch(() => undefined);
124
- await fs.rename(backupDir, targetDir).catch(() => undefined);
125
- }
126
- return { ok: false, error: `failed to copy hook pack: ${String(err)}` };
127
- }
128
153
  const deps = manifest.dependencies ?? {};
129
154
  const hasDeps = Object.keys(deps).length > 0;
130
- if (hasDeps) {
131
- logger.info?.("Installing hook pack dependencies…");
132
- const npmRes = await runCommandWithTimeout(["npm", "install", "--omit=dev", "--silent"], {
133
- timeoutMs: Math.max(timeoutMs, 300_000),
134
- cwd: targetDir,
135
- });
136
- if (npmRes.code !== 0) {
137
- if (backupDir) {
138
- await fs.rm(targetDir, { recursive: true, force: true }).catch(() => undefined);
139
- await fs.rename(backupDir, targetDir).catch(() => undefined);
140
- }
141
- return {
142
- ok: false,
143
- error: `npm install failed: ${npmRes.stderr.trim() || npmRes.stdout.trim()}`,
144
- };
145
- }
146
- }
147
- if (backupDir) {
148
- await fs.rm(backupDir, { recursive: true, force: true }).catch(() => undefined);
155
+ const installRes = await installPackageDir({
156
+ sourceDir: params.packageDir,
157
+ targetDir,
158
+ mode,
159
+ timeoutMs,
160
+ logger,
161
+ copyErrorPrefix: "failed to copy hook pack",
162
+ hasDeps,
163
+ depsLogMessage: "Installing hook pack dependencies…",
164
+ });
165
+ if (!installRes.ok) {
166
+ return installRes;
149
167
  }
150
168
  return {
151
169
  ok: true,
@@ -156,22 +174,24 @@ async function installHookPackageFromDir(params) {
156
174
  };
157
175
  }
158
176
  async function installHookFromDir(params) {
159
- const logger = params.logger ?? defaultLogger;
160
- const mode = params.mode ?? "install";
161
- const dryRun = params.dryRun ?? false;
177
+ const { logger, mode, dryRun } = resolveHookInstallModeOptions(params);
162
178
  await validateHookDir(params.hookDir);
163
179
  const hookName = await resolveHookNameFromDir(params.hookDir);
180
+ const hookIdError = validateHookId(hookName);
181
+ if (hookIdError) {
182
+ return { ok: false, error: hookIdError };
183
+ }
164
184
  if (params.expectedHookPackId && params.expectedHookPackId !== hookName) {
165
185
  return {
166
186
  ok: false,
167
187
  error: `hook id mismatch: expected ${params.expectedHookPackId}, got ${hookName}`,
168
188
  };
169
189
  }
170
- const hooksDir = params.hooksDir
171
- ? resolveUserPath(params.hooksDir)
172
- : path.join(CONFIG_DIR, "hooks");
173
- await fs.mkdir(hooksDir, { recursive: true });
174
- const targetDir = resolveHookInstallDir(hookName, hooksDir);
190
+ const targetDirResult = await resolveInstallTargetDir(hookName, params.hooksDir);
191
+ if (!targetDirResult.ok) {
192
+ return { ok: false, error: targetDirResult.error };
193
+ }
194
+ const targetDir = targetDirResult.targetDir;
175
195
  if (mode === "install" && (await fileExists(targetDir))) {
176
196
  return { ok: false, error: `hook already exists: ${targetDir} (delete it first)` };
177
197
  }
@@ -202,87 +222,77 @@ async function installHookFromDir(params) {
202
222
  export async function installHooksFromArchive(params) {
203
223
  const logger = params.logger ?? defaultLogger;
204
224
  const timeoutMs = params.timeoutMs ?? 120_000;
205
- const archivePath = resolveUserPath(params.archivePath);
206
- if (!(await fileExists(archivePath))) {
207
- return { ok: false, error: `archive not found: ${archivePath}` };
208
- }
209
- if (!resolveArchiveKind(archivePath)) {
210
- return { ok: false, error: `unsupported archive: ${archivePath}` };
211
- }
212
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "poolbot-hook-"));
213
- const extractDir = path.join(tmpDir, "extract");
214
- await fs.mkdir(extractDir, { recursive: true });
215
- logger.info?.(`Extracting ${archivePath}…`);
216
- try {
217
- await extractArchive({ archivePath, destDir: extractDir, timeoutMs, logger });
218
- }
219
- catch (err) {
220
- return { ok: false, error: `failed to extract archive: ${String(err)}` };
221
- }
222
- let rootDir = "";
223
- try {
224
- rootDir = await resolvePackedRootDir(extractDir);
225
- }
226
- catch (err) {
227
- return { ok: false, error: String(err) };
228
- }
229
- const manifestPath = path.join(rootDir, "package.json");
230
- if (await fileExists(manifestPath)) {
231
- return await installHookPackageFromDir({
232
- packageDir: rootDir,
225
+ const archivePathResult = await resolveArchiveSourcePath(params.archivePath);
226
+ if (!archivePathResult.ok) {
227
+ return archivePathResult;
228
+ }
229
+ const archivePath = archivePathResult.path;
230
+ return await withTempDir("poolbot-hook-", async (tmpDir) => {
231
+ const extractDir = path.join(tmpDir, "extract");
232
+ await fs.mkdir(extractDir, { recursive: true });
233
+ logger.info?.(`Extracting ${archivePath}…`);
234
+ try {
235
+ await extractArchive({ archivePath, destDir: extractDir, timeoutMs, logger });
236
+ }
237
+ catch (err) {
238
+ return { ok: false, error: `failed to extract archive: ${String(err)}` };
239
+ }
240
+ let rootDir = "";
241
+ try {
242
+ rootDir = await resolvePackedRootDir(extractDir);
243
+ }
244
+ catch (err) {
245
+ return { ok: false, error: String(err) };
246
+ }
247
+ const manifestPath = path.join(rootDir, "package.json");
248
+ if (await fileExists(manifestPath)) {
249
+ return await installHookPackageFromDir({
250
+ packageDir: rootDir,
251
+ hooksDir: params.hooksDir,
252
+ timeoutMs,
253
+ logger,
254
+ mode: params.mode,
255
+ dryRun: params.dryRun,
256
+ expectedHookPackId: params.expectedHookPackId,
257
+ });
258
+ }
259
+ return await installHookFromDir({
260
+ hookDir: rootDir,
233
261
  hooksDir: params.hooksDir,
234
- timeoutMs,
235
262
  logger,
236
263
  mode: params.mode,
237
264
  dryRun: params.dryRun,
238
265
  expectedHookPackId: params.expectedHookPackId,
239
266
  });
240
- }
241
- return await installHookFromDir({
242
- hookDir: rootDir,
243
- hooksDir: params.hooksDir,
244
- logger,
245
- mode: params.mode,
246
- dryRun: params.dryRun,
247
- expectedHookPackId: params.expectedHookPackId,
248
267
  });
249
268
  }
250
269
  export async function installHooksFromNpmSpec(params) {
251
- const logger = params.logger ?? defaultLogger;
252
- const timeoutMs = params.timeoutMs ?? 120_000;
253
- const mode = params.mode ?? "install";
254
- const dryRun = params.dryRun ?? false;
270
+ const { logger, timeoutMs, mode, dryRun } = resolveTimedHookInstallModeOptions(params);
255
271
  const expectedHookPackId = params.expectedHookPackId;
256
272
  const spec = params.spec.trim();
257
- if (!spec)
258
- return { ok: false, error: "missing npm spec" };
259
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "poolbot-hook-pack-"));
260
- logger.info?.(`Downloading ${spec}…`);
261
- const res = await runCommandWithTimeout(["npm", "pack", spec], {
262
- timeoutMs: Math.max(timeoutMs, 300_000),
263
- cwd: tmpDir,
264
- env: { COREPACK_ENABLE_DOWNLOAD_PROMPT: "0" },
265
- });
266
- if (res.code !== 0) {
267
- return { ok: false, error: `npm pack failed: ${res.stderr.trim() || res.stdout.trim()}` };
268
- }
269
- const packed = (res.stdout || "")
270
- .split("\n")
271
- .map((l) => l.trim())
272
- .filter(Boolean)
273
- .pop();
274
- if (!packed) {
275
- return { ok: false, error: "npm pack produced no archive" };
276
- }
277
- const archivePath = path.join(tmpDir, packed);
278
- return await installHooksFromArchive({
279
- archivePath,
280
- hooksDir: params.hooksDir,
281
- timeoutMs,
282
- logger,
283
- mode,
284
- dryRun,
285
- expectedHookPackId,
273
+ const specError = validateRegistryNpmSpec(spec);
274
+ if (specError) {
275
+ return { ok: false, error: specError };
276
+ }
277
+ return await withTempDir("poolbot-hook-pack-", async (tmpDir) => {
278
+ logger.info?.(`Downloading ${spec}…`);
279
+ const packedResult = await packNpmSpecToArchive({
280
+ spec,
281
+ timeoutMs,
282
+ cwd: tmpDir,
283
+ });
284
+ if (!packedResult.ok) {
285
+ return packedResult;
286
+ }
287
+ return await installHooksFromArchive({
288
+ archivePath: packedResult.archivePath,
289
+ hooksDir: params.hooksDir,
290
+ timeoutMs,
291
+ logger,
292
+ mode,
293
+ dryRun,
294
+ expectedHookPackId,
295
+ });
286
296
  });
287
297
  }
288
298
  export async function installHooksFromPath(params) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Hook system for poolbot agent events
2
+ * Hook system for Pool Bot agent events
3
3
  *
4
4
  * Provides an extensible event-driven hook system for agent events
5
5
  * like command processing, session lifecycle, etc.
@@ -110,12 +110,37 @@ export function createInternalHookEvent(type, action, sessionKey, context = {})
110
110
  };
111
111
  }
112
112
  export function isAgentBootstrapEvent(event) {
113
- if (event.type !== "agent" || event.action !== "bootstrap")
113
+ if (event.type !== "agent" || event.action !== "bootstrap") {
114
114
  return false;
115
+ }
115
116
  const context = event.context;
116
- if (!context || typeof context !== "object")
117
+ if (!context || typeof context !== "object") {
117
118
  return false;
118
- if (typeof context.workspaceDir !== "string")
119
+ }
120
+ if (typeof context.workspaceDir !== "string") {
119
121
  return false;
122
+ }
120
123
  return Array.isArray(context.bootstrapFiles);
121
124
  }
125
+ export function isMessageReceivedEvent(event) {
126
+ if (event.type !== "message" || event.action !== "received") {
127
+ return false;
128
+ }
129
+ const context = event.context;
130
+ if (!context || typeof context !== "object") {
131
+ return false;
132
+ }
133
+ return typeof context.from === "string" && typeof context.channelId === "string";
134
+ }
135
+ export function isMessageSentEvent(event) {
136
+ if (event.type !== "message" || event.action !== "sent") {
137
+ return false;
138
+ }
139
+ const context = event.context;
140
+ if (!context || typeof context !== "object") {
141
+ return false;
142
+ }
143
+ return (typeof context.to === "string" &&
144
+ typeof context.channelId === "string" &&
145
+ typeof context.success === "boolean");
146
+ }
@@ -3,8 +3,9 @@ import { pathToFileURL } from "node:url";
3
3
  import { shouldIncludeHook } from "./config.js";
4
4
  import { loadHookEntriesFromDir } from "./workspace.js";
5
5
  function resolveHookDir(api, dir) {
6
- if (path.isAbsolute(dir))
6
+ if (path.isAbsolute(dir)) {
7
7
  return dir;
8
+ }
8
9
  return path.resolve(path.dirname(api.source), dir);
9
10
  }
10
11
  function normalizePluginHookEntry(api, entry) {
@@ -5,6 +5,7 @@ import { convertMarkdownTables } from "../../markdown/tables.js";
5
5
  import { sendMessageIMessage } from "../send.js";
6
6
  export async function deliverReplies(params) {
7
7
  const { replies, target, client, runtime, maxBytes, textLimit, accountId, sentMessageCache } = params;
8
+ const scope = `${accountId ?? ""}:${target}`;
8
9
  const cfg = loadConfig();
9
10
  const tableMode = resolveMarkdownTableMode({
10
11
  cfg,
@@ -16,16 +17,19 @@ export async function deliverReplies(params) {
16
17
  const mediaList = payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []);
17
18
  const rawText = payload.text ?? "";
18
19
  const text = convertMarkdownTables(rawText, tableMode);
19
- if (!text && mediaList.length === 0)
20
+ if (!text && mediaList.length === 0) {
20
21
  continue;
22
+ }
21
23
  if (mediaList.length === 0) {
24
+ sentMessageCache?.remember(scope, text);
22
25
  for (const chunk of chunkTextWithMode(text, textLimit, chunkMode)) {
23
26
  await sendMessageIMessage(target, chunk, {
24
27
  maxBytes,
25
28
  client,
26
29
  accountId,
30
+ replyToId: payload.replyToId,
27
31
  });
28
- sentMessageCache?.remember(target, chunk);
32
+ sentMessageCache?.remember(scope, chunk);
29
33
  }
30
34
  }
31
35
  else {
@@ -38,9 +42,11 @@ export async function deliverReplies(params) {
38
42
  maxBytes,
39
43
  client,
40
44
  accountId,
45
+ replyToId: payload.replyToId,
41
46
  });
42
- if (caption)
43
- sentMessageCache?.remember(target, caption);
47
+ if (caption) {
48
+ sentMessageCache?.remember(scope, caption);
49
+ }
44
50
  }
45
51
  }
46
52
  runtime.log?.(`imessage: delivered reply to ${target}`);