@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
@@ -3,26 +3,30 @@ import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { CURRENT_SESSION_VERSION, SessionManager } from "@mariozechner/pi-coding-agent";
5
5
  import { resolveSessionAgentId } from "../../agents/agent-scope.js";
6
+ import { normalizeChatType } from "../../channels/chat-type.js";
6
7
  import { DEFAULT_RESET_TRIGGERS, deriveSessionMetaPatch, evaluateSessionFreshness, loadSessionStore, resolveChannelResetConfig, resolveThreadFlag, resolveSessionResetPolicy, resolveSessionResetType, resolveGroupSessionKey, resolveSessionFilePath, resolveSessionKey, resolveSessionTranscriptPath, resolveStorePath, updateSessionStore, } from "../../config/sessions.js";
8
+ import { archiveSessionTranscripts } from "../../gateway/session-utils.fs.js";
9
+ import { deliverSessionMaintenanceWarning } from "../../infra/session-maintenance-warning.js";
10
+ import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js";
7
11
  import { normalizeMainKey } from "../../routing/session-key.js";
12
+ import { normalizeSessionDeliveryFields } from "../../utils/delivery-context.js";
8
13
  import { resolveCommandAuthorization } from "../command-auth.js";
9
- import { normalizeChatType } from "../../channels/chat-type.js";
10
- import { stripMentions, stripStructuralPrefixes } from "./mentions.js";
11
- import { formatInboundBodyWithSenderMeta } from "./inbound-sender-meta.js";
12
14
  import { normalizeInboundTextNewlines } from "./inbound-text.js";
13
- import { normalizeSessionDeliveryFields } from "../../utils/delivery-context.js";
15
+ import { stripMentions, stripStructuralPrefixes } from "./mentions.js";
14
16
  function forkSessionFromParent(params) {
15
- const parentSessionFile = resolveSessionFilePath(params.parentEntry.sessionId, params.parentEntry);
16
- if (!parentSessionFile || !fs.existsSync(parentSessionFile))
17
+ const parentSessionFile = resolveSessionFilePath(params.parentEntry.sessionId, params.parentEntry, { agentId: params.agentId, sessionsDir: params.sessionsDir });
18
+ if (!parentSessionFile || !fs.existsSync(parentSessionFile)) {
17
19
  return null;
20
+ }
18
21
  try {
19
22
  const manager = SessionManager.open(parentSessionFile);
20
23
  const leafId = manager.getLeafId();
21
24
  if (leafId) {
22
25
  const sessionFile = manager.createBranchedSession(leafId) ?? manager.getSessionFile();
23
26
  const sessionId = manager.getSessionId();
24
- if (sessionFile && sessionId)
27
+ if (sessionFile && sessionId) {
25
28
  return { sessionId, sessionFile };
29
+ }
26
30
  }
27
31
  const sessionId = crypto.randomUUID();
28
32
  const timestamp = new Date().toISOString();
@@ -63,7 +67,13 @@ export async function initSessionState(params) {
63
67
  : DEFAULT_RESET_TRIGGERS;
64
68
  const sessionScope = sessionCfg?.scope ?? "per-sender";
65
69
  const storePath = resolveStorePath(sessionCfg?.store, { agentId });
66
- const sessionStore = loadSessionStore(storePath);
70
+ // CRITICAL: Skip cache to ensure fresh data when resolving session identity.
71
+ // Stale cache (especially with multiple gateway processes or on Windows where
72
+ // mtime granularity may miss rapid writes) can cause incorrect sessionId
73
+ // generation, leading to orphaned transcript files. See #17971.
74
+ const sessionStore = loadSessionStore(storePath, {
75
+ skipCache: true,
76
+ });
67
77
  let sessionKey;
68
78
  let sessionEntry;
69
79
  let sessionId;
@@ -106,10 +116,12 @@ export async function initSessionState(params) {
106
116
  const trimmedBodyLower = trimmedBody.toLowerCase();
107
117
  const strippedForResetLower = strippedForReset.toLowerCase();
108
118
  for (const trigger of resetTriggers) {
109
- if (!trigger)
119
+ if (!trigger) {
110
120
  continue;
111
- if (!resetAuthorized)
121
+ }
122
+ if (!resetAuthorized) {
112
123
  break;
124
+ }
113
125
  const triggerLower = trigger.toLowerCase();
114
126
  if (trimmedBodyLower === triggerLower || strippedForResetLower === triggerLower) {
115
127
  isNewSession = true;
@@ -169,13 +181,25 @@ export async function initSessionState(params) {
169
181
  isNewSession = true;
170
182
  systemSent = false;
171
183
  abortedLastRun = false;
184
+ // When a reset trigger (/new, /reset) starts a new session, carry over
185
+ // user-set behavior overrides (verbose, thinking, reasoning, ttsAuto)
186
+ // so the user doesn't have to re-enable them every time.
187
+ if (resetTriggered && entry) {
188
+ persistedThinking = entry.thinkingLevel;
189
+ persistedVerbose = entry.verboseLevel;
190
+ persistedReasoning = entry.reasoningLevel;
191
+ persistedTtsAuto = entry.ttsAuto;
192
+ }
172
193
  }
173
194
  const baseEntry = !isNewSession && freshEntry ? entry : undefined;
174
195
  // Track the originating channel/to for announce routing (subagent announce-back).
175
196
  const lastChannelRaw = ctx.OriginatingChannel || baseEntry?.lastChannel;
176
197
  const lastToRaw = ctx.OriginatingTo || ctx.To || baseEntry?.lastTo;
177
198
  const lastAccountIdRaw = ctx.AccountId || baseEntry?.lastAccountId;
178
- const lastThreadIdRaw = ctx.MessageThreadId || baseEntry?.lastThreadId;
199
+ // Only fall back to persisted threadId for thread sessions. Non-thread
200
+ // sessions (e.g. DM without topics) must not inherit a stale threadId from a
201
+ // previous interaction that happened inside a topic/thread.
202
+ const lastThreadIdRaw = ctx.MessageThreadId || (isThread ? baseEntry?.lastThreadId : undefined);
179
203
  const deliveryFields = normalizeSessionDeliveryFields({
180
204
  deliveryContext: {
181
205
  channel: lastChannelRaw,
@@ -242,13 +266,18 @@ export async function initSessionState(params) {
242
266
  parentSessionKey &&
243
267
  parentSessionKey !== sessionKey &&
244
268
  sessionStore[parentSessionKey]) {
269
+ console.warn(`[session-init] forking from parent session: parentKey=${parentSessionKey} → sessionKey=${sessionKey} ` +
270
+ `parentTokens=${sessionStore[parentSessionKey].totalTokens ?? "?"}`);
245
271
  const forked = forkSessionFromParent({
246
272
  parentEntry: sessionStore[parentSessionKey],
273
+ agentId,
274
+ sessionsDir: path.dirname(storePath),
247
275
  });
248
276
  if (forked) {
249
277
  sessionId = forked.sessionId;
250
278
  sessionEntry.sessionId = forked.sessionId;
251
279
  sessionEntry.sessionFile = forked.sessionFile;
280
+ console.warn(`[session-init] forked session created: file=${forked.sessionFile}`);
252
281
  }
253
282
  }
254
283
  if (!sessionEntry.sessionFile) {
@@ -258,30 +287,82 @@ export async function initSessionState(params) {
258
287
  sessionEntry.compactionCount = 0;
259
288
  sessionEntry.memoryFlushCompactionCount = undefined;
260
289
  sessionEntry.memoryFlushAt = undefined;
290
+ // Clear stale token metrics from previous session so /status doesn't
291
+ // display the old session's context usage after /new or /reset.
292
+ sessionEntry.totalTokens = undefined;
293
+ sessionEntry.inputTokens = undefined;
294
+ sessionEntry.outputTokens = undefined;
295
+ sessionEntry.contextTokens = undefined;
261
296
  }
262
297
  // Preserve per-session overrides while resetting compaction state on /new.
263
298
  sessionStore[sessionKey] = { ...sessionStore[sessionKey], ...sessionEntry };
264
299
  await updateSessionStore(storePath, (store) => {
265
300
  // Preserve per-session overrides while resetting compaction state on /new.
266
301
  store[sessionKey] = { ...store[sessionKey], ...sessionEntry };
302
+ }, {
303
+ activeSessionKey: sessionKey,
304
+ onWarn: (warning) => deliverSessionMaintenanceWarning({
305
+ cfg,
306
+ sessionKey,
307
+ entry: sessionEntry,
308
+ warning,
309
+ }),
267
310
  });
311
+ // Archive old transcript so it doesn't accumulate on disk (#14869).
312
+ if (previousSessionEntry?.sessionId) {
313
+ archiveSessionTranscripts({
314
+ sessionId: previousSessionEntry.sessionId,
315
+ storePath,
316
+ sessionFile: previousSessionEntry.sessionFile,
317
+ agentId,
318
+ reason: "reset",
319
+ });
320
+ }
268
321
  const sessionCtx = {
269
322
  ...ctx,
270
323
  // Keep BodyStripped aligned with Body (best default for agent prompts).
271
324
  // RawBody is reserved for command/directive parsing and may omit context.
272
- BodyStripped: formatInboundBodyWithSenderMeta({
273
- ctx,
274
- body: normalizeInboundTextNewlines(bodyStripped ??
275
- ctx.BodyForAgent ??
276
- ctx.Body ??
277
- ctx.CommandBody ??
278
- ctx.RawBody ??
279
- ctx.BodyForCommands ??
280
- ""),
281
- }),
325
+ BodyStripped: normalizeInboundTextNewlines(bodyStripped ??
326
+ ctx.BodyForAgent ??
327
+ ctx.Body ??
328
+ ctx.CommandBody ??
329
+ ctx.RawBody ??
330
+ ctx.BodyForCommands ??
331
+ ""),
282
332
  SessionId: sessionId,
283
333
  IsNewSession: isNewSession ? "true" : "false",
284
334
  };
335
+ // Run session plugin hooks (fire-and-forget)
336
+ const hookRunner = getGlobalHookRunner();
337
+ if (hookRunner && isNewSession) {
338
+ const effectiveSessionId = sessionId ?? "";
339
+ // If replacing an existing session, fire session_end for the old one
340
+ if (previousSessionEntry?.sessionId && previousSessionEntry.sessionId !== effectiveSessionId) {
341
+ if (hookRunner.hasHooks("session_end")) {
342
+ void hookRunner
343
+ .runSessionEnd({
344
+ sessionId: previousSessionEntry.sessionId,
345
+ messageCount: 0,
346
+ }, {
347
+ sessionId: previousSessionEntry.sessionId,
348
+ agentId: resolveSessionAgentId({ sessionKey, config: cfg }),
349
+ })
350
+ .catch(() => { });
351
+ }
352
+ }
353
+ // Fire session_start for the new session
354
+ if (hookRunner.hasHooks("session_start")) {
355
+ void hookRunner
356
+ .runSessionStart({
357
+ sessionId: effectiveSessionId,
358
+ resumedFrom: previousSessionEntry?.sessionId,
359
+ }, {
360
+ sessionId: effectiveSessionId,
361
+ agentId: resolveSessionAgentId({ sessionKey, config: cfg }),
362
+ })
363
+ .catch(() => { });
364
+ }
365
+ }
285
366
  return {
286
367
  sessionCtx,
287
368
  sessionEntry,
@@ -3,11 +3,13 @@ import { parseInlineDirectives } from "../../utils/directive-tags.js";
3
3
  import { isSilentReplyText, SILENT_REPLY_TOKEN } from "../tokens.js";
4
4
  const splitTrailingDirective = (text) => {
5
5
  const openIndex = text.lastIndexOf("[[");
6
- if (openIndex < 0)
6
+ if (openIndex < 0) {
7
7
  return { text, tail: "" };
8
+ }
8
9
  const closeIndex = text.indexOf("]]", openIndex + 2);
9
- if (closeIndex >= 0)
10
+ if (closeIndex >= 0) {
10
11
  return { text, tail: "" };
12
+ }
11
13
  return {
12
14
  text: text.slice(0, openIndex),
13
15
  tail: text.slice(openIndex),
@@ -47,9 +49,11 @@ const hasRenderableContent = (parsed) => Boolean(parsed.text) ||
47
49
  export function createStreamingDirectiveAccumulator() {
48
50
  let pendingTail = "";
49
51
  let pendingReply = { sawCurrent: false, hasTag: false };
52
+ let activeReply = { sawCurrent: false, hasTag: false };
50
53
  const reset = () => {
51
54
  pendingTail = "";
52
55
  pendingReply = { sawCurrent: false, hasTag: false };
56
+ activeReply = { sawCurrent: false, hasTag: false };
53
57
  };
54
58
  const consume = (raw, options = {}) => {
55
59
  let combined = `${pendingTail}${raw ?? ""}`;
@@ -63,9 +67,9 @@ export function createStreamingDirectiveAccumulator() {
63
67
  return null;
64
68
  }
65
69
  const parsed = parseChunk(combined, { silentToken: options.silentToken });
66
- const hasTag = pendingReply.hasTag || parsed.replyToTag;
67
- const sawCurrent = pendingReply.sawCurrent || parsed.replyToCurrent;
68
- const explicitId = parsed.replyToExplicitId ?? pendingReply.explicitId;
70
+ const hasTag = activeReply.hasTag || pendingReply.hasTag || parsed.replyToTag;
71
+ const sawCurrent = activeReply.sawCurrent || pendingReply.sawCurrent || parsed.replyToCurrent;
72
+ const explicitId = parsed.replyToExplicitId ?? pendingReply.explicitId ?? activeReply.explicitId;
69
73
  const combinedResult = {
70
74
  ...parsed,
71
75
  replyToId: explicitId,
@@ -82,6 +86,13 @@ export function createStreamingDirectiveAccumulator() {
82
86
  }
83
87
  return null;
84
88
  }
89
+ // Keep reply context sticky for the full assistant message so split/newline chunks
90
+ // stay on the same native reply target until reset() is called for the next message.
91
+ activeReply = {
92
+ explicitId,
93
+ sawCurrent,
94
+ hasTag,
95
+ };
85
96
  pendingReply = { sawCurrent: false, hasTag: false };
86
97
  return combinedResult;
87
98
  };
@@ -5,13 +5,15 @@ import { resolveModelAuthMode } from "../agents/model-auth.js";
5
5
  import { resolveConfiguredModelRef } from "../agents/model-selection.js";
6
6
  import { resolveSandboxRuntimeStatus } from "../agents/sandbox.js";
7
7
  import { derivePromptTokens, normalizeUsage } from "../agents/usage.js";
8
- import { resolveMainSessionKey, resolveSessionFilePath, } from "../config/sessions.js";
9
- import { getTtsMaxLength, getTtsProvider, isSummarizationEnabled, resolveTtsAutoMode, resolveTtsConfig, resolveTtsPrefsPath, } from "../tts/tts.js";
8
+ import { resolveMainSessionKey, resolveSessionFilePath, resolveSessionFilePathOptions, } from "../config/sessions.js";
9
+ import { formatTimeAgo } from "../infra/format-time/format-relative.js";
10
10
  import { resolveCommitHash } from "../infra/git-commit.js";
11
+ import { listPluginCommands } from "../plugins/commands.js";
12
+ import { resolveAgentIdFromSessionKey } from "../routing/session-key.js";
13
+ import { getTtsMaxLength, getTtsProvider, isSummarizationEnabled, resolveTtsAutoMode, resolveTtsConfig, resolveTtsPrefsPath, } from "../tts/tts.js";
11
14
  import { estimateUsageCost, formatTokenCount as formatTokenCountShared, formatUsd, resolveModelCostConfig, } from "../utils/usage-format.js";
12
15
  import { VERSION } from "../version.js";
13
16
  import { listChatCommands, listChatCommandsForConfig, } from "./commands-registry.js";
14
- import { listPluginCommands } from "../plugins/commands.js";
15
17
  export const formatTokenCount = formatTokenCountShared;
16
18
  function resolveRuntimeLabel(args) {
17
19
  const sessionKey = args.sessionKey?.trim();
@@ -21,19 +23,23 @@ function resolveRuntimeLabel(args) {
21
23
  sessionKey,
22
24
  });
23
25
  const sandboxMode = runtimeStatus.mode ?? "off";
24
- if (sandboxMode === "off")
26
+ if (sandboxMode === "off") {
25
27
  return "direct";
28
+ }
26
29
  const runtime = runtimeStatus.sandboxed ? "docker" : sessionKey ? "direct" : "unknown";
27
30
  return `${runtime}/${sandboxMode}`;
28
31
  }
29
32
  const sandboxMode = args.agent?.sandbox?.mode ?? "off";
30
- if (sandboxMode === "off")
33
+ if (sandboxMode === "off") {
31
34
  return "direct";
35
+ }
32
36
  const sandboxed = (() => {
33
- if (!sessionKey)
37
+ if (!sessionKey) {
34
38
  return false;
35
- if (sandboxMode === "all")
39
+ }
40
+ if (sandboxMode === "all") {
36
41
  return true;
42
+ }
37
43
  if (args.config) {
38
44
  return resolveSandboxRuntimeStatus({
39
45
  cfg: args.config,
@@ -61,48 +67,47 @@ const formatTokens = (total, contextTokens) => {
61
67
  return `${totalLabel}/${ctxLabel}${pct !== null ? ` (${pct}%)` : ""}`;
62
68
  };
63
69
  export const formatContextUsageShort = (total, contextTokens) => `Context ${formatTokens(total, contextTokens ?? null)}`;
64
- const formatAge = (ms) => {
65
- if (!ms || ms < 0)
66
- return "unknown";
67
- const minutes = Math.round(ms / 60_000);
68
- if (minutes < 1)
69
- return "just now";
70
- if (minutes < 60)
71
- return `${minutes}m ago`;
72
- const hours = Math.round(minutes / 60);
73
- if (hours < 48)
74
- return `${hours}h ago`;
75
- const days = Math.round(hours / 24);
76
- return `${days}d ago`;
77
- };
78
70
  const formatQueueDetails = (queue) => {
79
- if (!queue)
71
+ if (!queue) {
80
72
  return "";
73
+ }
81
74
  const depth = typeof queue.depth === "number" ? `depth ${queue.depth}` : null;
82
75
  if (!queue.showDetails) {
83
76
  return depth ? ` (${depth})` : "";
84
77
  }
85
78
  const detailParts = [];
86
- if (depth)
79
+ if (depth) {
87
80
  detailParts.push(depth);
81
+ }
88
82
  if (typeof queue.debounceMs === "number") {
89
83
  const ms = Math.max(0, Math.round(queue.debounceMs));
90
84
  const label = ms >= 1000 ? `${ms % 1000 === 0 ? ms / 1000 : (ms / 1000).toFixed(1)}s` : `${ms}ms`;
91
85
  detailParts.push(`debounce ${label}`);
92
86
  }
93
- if (typeof queue.cap === "number")
87
+ if (typeof queue.cap === "number") {
94
88
  detailParts.push(`cap ${queue.cap}`);
95
- if (queue.dropPolicy)
89
+ }
90
+ if (queue.dropPolicy) {
96
91
  detailParts.push(`drop ${queue.dropPolicy}`);
92
+ }
97
93
  return detailParts.length ? ` (${detailParts.join(" · ")})` : "";
98
94
  };
99
- const readUsageFromSessionLog = (sessionId, sessionEntry) => {
95
+ const readUsageFromSessionLog = (sessionId, sessionEntry, agentId, sessionKey, storePath) => {
100
96
  // Transcripts are stored at the session file path (fallback: ~/.poolbot/sessions/<SessionId>.jsonl)
101
- if (!sessionId)
97
+ if (!sessionId) {
102
98
  return undefined;
103
- const logPath = resolveSessionFilePath(sessionId, sessionEntry);
104
- if (!fs.existsSync(logPath))
99
+ }
100
+ let logPath;
101
+ try {
102
+ const resolvedAgentId = agentId ?? (sessionKey ? resolveAgentIdFromSessionKey(sessionKey) : undefined);
103
+ logPath = resolveSessionFilePath(sessionId, sessionEntry, resolveSessionFilePathOptions({ agentId: resolvedAgentId, storePath }));
104
+ }
105
+ catch {
106
+ return undefined;
107
+ }
108
+ if (!fs.existsSync(logPath)) {
105
109
  return undefined;
110
+ }
106
111
  try {
107
112
  const lines = fs.readFileSync(logPath, "utf-8").split(/\n+/);
108
113
  let input = 0;
@@ -111,28 +116,32 @@ const readUsageFromSessionLog = (sessionId, sessionEntry) => {
111
116
  let model;
112
117
  let lastUsage;
113
118
  for (const line of lines) {
114
- if (!line.trim())
119
+ if (!line.trim()) {
115
120
  continue;
121
+ }
116
122
  try {
117
123
  const parsed = JSON.parse(line);
118
124
  const usageRaw = parsed.message?.usage ?? parsed.usage;
119
125
  const usage = normalizeUsage(usageRaw);
120
- if (usage)
126
+ if (usage) {
121
127
  lastUsage = usage;
128
+ }
122
129
  model = parsed.message?.model ?? parsed.model ?? model;
123
130
  }
124
131
  catch {
125
132
  // ignore bad lines
126
133
  }
127
134
  }
128
- if (!lastUsage)
135
+ if (!lastUsage) {
129
136
  return undefined;
137
+ }
130
138
  input = lastUsage.input ?? 0;
131
139
  output = lastUsage.output ?? 0;
132
140
  promptTokens = derivePromptTokens(lastUsage) ?? lastUsage.total ?? input + output;
133
141
  const total = lastUsage.total ?? promptTokens + output;
134
- if (promptTokens === 0 && total === 0)
142
+ if (promptTokens === 0 && total === 0) {
135
143
  return undefined;
144
+ }
136
145
  return { input, output, promptTokens, total, model };
137
146
  }
138
147
  catch {
@@ -140,15 +149,17 @@ const readUsageFromSessionLog = (sessionId, sessionEntry) => {
140
149
  }
141
150
  };
142
151
  const formatUsagePair = (input, output) => {
143
- if (input == null && output == null)
152
+ if (input == null && output == null) {
144
153
  return null;
154
+ }
145
155
  const inputLabel = typeof input === "number" ? formatTokenCount(input) : "?";
146
156
  const outputLabel = typeof output === "number" ? formatTokenCount(output) : "?";
147
157
  return `🧮 Tokens: ${inputLabel} in / ${outputLabel} out`;
148
158
  };
149
159
  const formatMediaUnderstandingLine = (decisions) => {
150
- if (!decisions || decisions.length === 0)
160
+ if (!decisions || decisions.length === 0) {
151
161
  return null;
162
+ }
152
163
  const parts = decisions
153
164
  .map((decision) => {
154
165
  const count = decision.attachments.length;
@@ -179,15 +190,18 @@ const formatMediaUnderstandingLine = (decisions) => {
179
190
  return null;
180
191
  })
181
192
  .filter((part) => part != null);
182
- if (parts.length === 0)
193
+ if (parts.length === 0) {
183
194
  return null;
184
- if (parts.every((part) => part.endsWith(" none")))
195
+ }
196
+ if (parts.every((part) => part.endsWith(" none"))) {
185
197
  return null;
198
+ }
186
199
  return `📎 Media: ${parts.join(" · ")}`;
187
200
  };
188
201
  const formatVoiceModeLine = (config, sessionEntry) => {
189
- if (!config)
202
+ if (!config) {
190
203
  return null;
204
+ }
191
205
  const ttsConfig = resolveTtsConfig(config);
192
206
  const prefsPath = resolveTtsPrefsPath(ttsConfig);
193
207
  const autoMode = resolveTtsAutoMode({
@@ -195,8 +209,9 @@ const formatVoiceModeLine = (config, sessionEntry) => {
195
209
  prefsPath,
196
210
  sessionAuto: sessionEntry?.ttsAuto,
197
211
  });
198
- if (autoMode === "off")
212
+ if (autoMode === "off") {
199
213
  return null;
214
+ }
200
215
  const provider = getTtsProvider(ttsConfig, prefsPath);
201
216
  const maxLength = getTtsMaxLength(prefsPath);
202
217
  const summarize = isSummarizationEnabled(prefsPath) ? "on" : "off";
@@ -226,21 +241,24 @@ export function buildStatusMessage(args) {
226
241
  // Prefer prompt-size tokens from the session transcript when it looks larger
227
242
  // (cached prompt tokens are often missing from agent meta/store).
228
243
  if (args.includeTranscriptUsage) {
229
- const logUsage = readUsageFromSessionLog(entry?.sessionId, entry);
244
+ const logUsage = readUsageFromSessionLog(entry?.sessionId, entry, args.agentId, args.sessionKey, args.sessionStorePath);
230
245
  if (logUsage) {
231
246
  const candidate = logUsage.promptTokens || logUsage.total;
232
247
  if (!totalTokens || totalTokens === 0 || candidate > totalTokens) {
233
248
  totalTokens = candidate;
234
249
  }
235
- if (!model)
250
+ if (!model) {
236
251
  model = logUsage.model ?? model;
252
+ }
237
253
  if (!contextTokens && logUsage.model) {
238
254
  contextTokens = lookupContextTokens(logUsage.model) ?? contextTokens;
239
255
  }
240
- if (!inputTokens || inputTokens === 0)
256
+ if (!inputTokens || inputTokens === 0) {
241
257
  inputTokens = logUsage.input;
242
- if (!outputTokens || outputTokens === 0)
258
+ }
259
+ if (!outputTokens || outputTokens === 0) {
243
260
  outputTokens = logUsage.output;
261
+ }
244
262
  }
245
263
  }
246
264
  const thinkLevel = args.resolvedThink ?? args.agent?.thinkingDefault ?? "off";
@@ -254,7 +272,7 @@ export function buildStatusMessage(args) {
254
272
  const updatedAt = entry?.updatedAt;
255
273
  const sessionLine = [
256
274
  `Session: ${args.sessionKey ?? "unknown"}`,
257
- typeof updatedAt === "number" ? `updated ${formatAge(now - updatedAt)}` : "no activity",
275
+ typeof updatedAt === "number" ? `updated ${formatTimeAgo(now - updatedAt)}` : "no activity",
258
276
  ]
259
277
  .filter(Boolean)
260
278
  .join(" • ");
@@ -317,7 +335,7 @@ export function buildStatusMessage(args) {
317
335
  const authLabel = authLabelValue ? ` · 🔑 ${authLabelValue}` : "";
318
336
  const modelLine = `🧠 Model: ${modelLabel}${authLabel}`;
319
337
  const commit = resolveCommitHash();
320
- const versionLine = `🦞 Poolbot ${VERSION}${commit ? ` (${commit})` : ""}`;
338
+ const versionLine = `🦞 Pool Bot ${VERSION}${commit ? ` (${commit})` : ""}`;
321
339
  const usagePair = formatUsagePair(inputTokens, outputTokens);
322
340
  const costLine = costLabel ? `💵 Cost: ${costLabel}` : null;
323
341
  const usageCostLine = usagePair && costLine ? `${usagePair} · ${costLine}` : (usagePair ?? costLine);
@@ -377,10 +395,12 @@ export function buildHelpMessage(cfg) {
377
395
  lines.push(" /new | /reset | /compact [instructions] | /stop");
378
396
  lines.push("");
379
397
  const optionParts = ["/think <level>", "/model <id>", "/verbose on|off"];
380
- if (cfg?.commands?.config === true)
398
+ if (cfg?.commands?.config === true) {
381
399
  optionParts.push("/config");
382
- if (cfg?.commands?.debug === true)
400
+ }
401
+ if (cfg?.commands?.debug === true) {
383
402
  optionParts.push("/debug");
403
+ }
384
404
  lines.push("Options");
385
405
  lines.push(` ${optionParts.join(" | ")}`);
386
406
  lines.push("");
@@ -405,8 +425,9 @@ function formatCommandEntry(command) {
405
425
  .filter((alias) => alias.toLowerCase() !== primary.toLowerCase())
406
426
  .filter((alias) => {
407
427
  const key = alias.toLowerCase();
408
- if (seen.has(key))
428
+ if (seen.has(key)) {
409
429
  return false;
430
+ }
410
431
  seen.add(key);
411
432
  return true;
412
433
  });
@@ -419,8 +440,9 @@ function buildCommandItems(commands, pluginCommands) {
419
440
  const items = [];
420
441
  for (const category of CATEGORY_ORDER) {
421
442
  const categoryCommands = grouped.get(category) ?? [];
422
- if (categoryCommands.length === 0)
443
+ if (categoryCommands.length === 0) {
423
444
  continue;
445
+ }
424
446
  const label = CATEGORY_LABELS[category];
425
447
  for (const command of categoryCommands) {
426
448
  items.push({ label, text: formatCommandEntry(command) });
@@ -440,8 +462,9 @@ function formatCommandList(items) {
440
462
  let currentLabel = null;
441
463
  for (const item of items) {
442
464
  if (item.label !== currentLabel) {
443
- if (lines.length > 0)
465
+ if (lines.length > 0) {
444
466
  lines.push("");
467
+ }
445
468
  lines.push(item.label);
446
469
  currentLabel = item.label;
447
470
  }
@@ -144,9 +144,9 @@ export async function ensureChromeExtensionRelayServer(opts) {
144
144
  case "Browser.getVersion":
145
145
  return {
146
146
  protocolVersion: "1.3",
147
- product: "Chrome/Poolbot-Extension-Relay",
147
+ product: "Chrome/PoolBot-Extension-Relay",
148
148
  revision: "0",
149
- userAgent: "Poolbot-Extension-Relay",
149
+ userAgent: "PoolBot-Extension-Relay",
150
150
  jsVersion: "V8",
151
151
  };
152
152
  case "Browser.setDownloadBehavior":
@@ -240,7 +240,7 @@ export async function ensureChromeExtensionRelayServer(opts) {
240
240
  if ((path === "/json/version" || path === "/json/version/") &&
241
241
  (req.method === "GET" || req.method === "PUT")) {
242
242
  const payload = {
243
- Browser: "Poolbot/extension-relay",
243
+ Browser: "PoolBot/extension-relay",
244
244
  "Protocol-Version": "1.3",
245
245
  };
246
246
  // Only advertise the WS URL if a real extension is connected.
@@ -39,7 +39,7 @@ export function isAuthorizedBrowserRequest(req, auth) {
39
39
  }
40
40
  }
41
41
  if (auth.password) {
42
- const passwordHeader = firstHeaderValue(req.headers["x-openclaw-password"]).trim();
42
+ const passwordHeader = firstHeaderValue(req.headers["x-poolbot-password"]).trim();
43
43
  if (passwordHeader && safeEqualSecret(passwordHeader, auth.password)) {
44
44
  return true;
45
45
  }
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
- import { resolvePreferredOpenClawTmpDir } from "../infra/tmp-poolbot-dir.js";
3
- export const DEFAULT_BROWSER_TMP_DIR = resolvePreferredOpenClawTmpDir();
2
+ import { resolvePreferredPoolbotTmpDir } from "../infra/tmp-poolbot-dir.js";
3
+ export const DEFAULT_BROWSER_TMP_DIR = resolvePreferredPoolbotTmpDir();
4
4
  export const DEFAULT_TRACE_DIR = DEFAULT_BROWSER_TMP_DIR;
5
5
  export const DEFAULT_DOWNLOAD_DIR = path.join(DEFAULT_BROWSER_TMP_DIR, "downloads");
6
6
  export const DEFAULT_UPLOAD_DIR = path.join(DEFAULT_BROWSER_TMP_DIR, "uploads");
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2026.2.21",
3
- "commit": "c1b8db5b38e189213061393716632d810d42bafd",
4
- "builtAt": "2026-02-18T06:05:30.087Z"
2
+ "version": "2026.2.22",
3
+ "commit": "2f28abca3332ffa8582bc9169e87e707374b2c56",
4
+ "builtAt": "2026-02-18T13:03:00.077Z"
5
5
  }
@@ -1,3 +1,23 @@
1
1
  export function formatAllowlistMatchMeta(match) {
2
2
  return `matchKey=${match?.matchKey ?? "none"} matchSource=${match?.matchSource ?? "none"}`;
3
3
  }
4
+ export function resolveAllowlistMatchSimple(params) {
5
+ const allowFrom = params.allowFrom
6
+ .map((entry) => String(entry).trim().toLowerCase())
7
+ .filter(Boolean);
8
+ if (allowFrom.length === 0) {
9
+ return { allowed: false };
10
+ }
11
+ if (allowFrom.includes("*")) {
12
+ return { allowed: true, matchKey: "*", matchSource: "wildcard" };
13
+ }
14
+ const senderId = params.senderId.toLowerCase();
15
+ if (allowFrom.includes(senderId)) {
16
+ return { allowed: true, matchKey: senderId, matchSource: "id" };
17
+ }
18
+ const senderName = params.senderName?.toLowerCase();
19
+ if (senderName && allowFrom.includes(senderName)) {
20
+ return { allowed: true, matchKey: senderName, matchSource: "name" };
21
+ }
22
+ return { allowed: false };
23
+ }