@poolzin/pool-bot 2026.2.25 → 2026.2.26

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 (506) hide show
  1. package/dist/acp/event-mapper.js +87 -22
  2. package/dist/acp/meta.js +12 -6
  3. package/dist/agents/agent-paths.js +8 -9
  4. package/dist/agents/agent-scope.js +7 -5
  5. package/dist/agents/auth-profiles/oauth.js +148 -64
  6. package/dist/agents/auth-profiles/session-override.js +13 -7
  7. package/dist/agents/bash-tools.exec-host-gateway.js +14 -4
  8. package/dist/agents/bash-tools.exec-runtime.js +2 -25
  9. package/dist/agents/bedrock-discovery.js +3 -1
  10. package/dist/agents/byteplus-models.js +97 -0
  11. package/dist/agents/chutes-oauth.js +1 -0
  12. package/dist/agents/cli-runner/helpers.js +4 -0
  13. package/dist/agents/compaction.js +41 -14
  14. package/dist/agents/doubao-models.js +121 -0
  15. package/dist/agents/failover-error.js +2 -0
  16. package/dist/agents/huggingface-models.js +5 -3
  17. package/dist/agents/live-model-filter.js +5 -0
  18. package/dist/agents/minimax-vlm.js +10 -8
  19. package/dist/agents/model-auth.js +6 -0
  20. package/dist/agents/model-catalog.js +3 -1
  21. package/dist/agents/model-selection.js +7 -1
  22. package/dist/agents/models-config.providers.js +93 -11
  23. package/dist/agents/ollama-stream.js +117 -4
  24. package/dist/agents/opencode-zen-models.js +22 -11
  25. package/dist/agents/pi-embedded-helpers/errors.js +55 -33
  26. package/dist/agents/pi-embedded-helpers/messaging-dedupe.js +10 -5
  27. package/dist/agents/pi-embedded-helpers/thinking.js +10 -5
  28. package/dist/agents/pi-embedded-helpers.js +1 -1
  29. package/dist/agents/pi-embedded-runner/compact.js +29 -7
  30. package/dist/agents/pi-embedded-runner/extensions.js +28 -26
  31. package/dist/agents/pi-embedded-runner/google.js +20 -8
  32. package/dist/agents/pi-embedded-runner/run/attempt.js +95 -36
  33. package/dist/agents/pi-embedded-runner/run.js +71 -12
  34. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +11 -2
  35. package/dist/agents/pi-embedded-runner/session-manager-cache.js +11 -7
  36. package/dist/agents/pi-embedded-runner/system-prompt.js +2 -0
  37. package/dist/agents/pi-embedded-runner/thinking.js +42 -0
  38. package/dist/agents/pi-embedded-runner/tool-name-allowlist.js +19 -0
  39. package/dist/agents/pi-embedded-runner/utils.js +7 -10
  40. package/dist/agents/pi-embedded-subscribe.handlers.lifecycle.js +45 -56
  41. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +2 -2
  42. package/dist/agents/pi-embedded-subscribe.js +9 -4
  43. package/dist/agents/pi-embedded-subscribe.tools.js +68 -14
  44. package/dist/agents/pi-embedded-utils.js +3 -0
  45. package/dist/agents/pi-extensions/compaction-safeguard-runtime.js +4 -20
  46. package/dist/agents/pi-extensions/compaction-safeguard.js +75 -33
  47. package/dist/agents/pi-settings.js +40 -0
  48. package/dist/agents/pi-tools.policy.js +2 -1
  49. package/dist/agents/provider/config-loader.js +1 -1
  50. package/dist/agents/sandbox/browser.js +170 -33
  51. package/dist/agents/sandbox/config-hash.js +14 -27
  52. package/dist/agents/sandbox/config.js +21 -2
  53. package/dist/agents/sandbox/constants.js +2 -0
  54. package/dist/agents/sandbox/docker.js +16 -2
  55. package/dist/agents/sandbox/novnc-auth.js +62 -0
  56. package/dist/agents/sandbox/sanitize-env-vars.js +1 -1
  57. package/dist/agents/sandbox/shared.js +10 -6
  58. package/dist/agents/sandbox-paths.js +24 -11
  59. package/dist/agents/schema/clean-for-gemini.js +132 -85
  60. package/dist/agents/session-slug.js +10 -5
  61. package/dist/agents/session-tool-result-guard-wrapper.js +1 -0
  62. package/dist/agents/session-tool-result-guard.js +3 -1
  63. package/dist/agents/session-transcript-repair.js +40 -6
  64. package/dist/agents/skills/bundled-dir.js +19 -5
  65. package/dist/agents/skills/env-overrides.js +124 -43
  66. package/dist/agents/skills/frontmatter.js +6 -6
  67. package/dist/agents/skills/plugin-skills.js +14 -7
  68. package/dist/agents/skills/workspace.js +1 -0
  69. package/dist/agents/subagent-announce.js +251 -49
  70. package/dist/agents/subagent-lifecycle-events.js +19 -0
  71. package/dist/agents/subagent-registry-cleanup.js +31 -0
  72. package/dist/agents/subagent-registry-completion.js +68 -0
  73. package/dist/agents/subagent-registry-queries.js +117 -0
  74. package/dist/agents/subagent-registry-state.js +46 -0
  75. package/dist/agents/subagent-registry.js +252 -221
  76. package/dist/agents/subagent-registry.store.js +1 -0
  77. package/dist/agents/subagent-registry.types.js +1 -0
  78. package/dist/agents/subagent-spawn.js +195 -7
  79. package/dist/agents/system-prompt.js +22 -6
  80. package/dist/agents/test-helpers/fast-coding-tools.js +1 -18
  81. package/dist/agents/test-helpers/fast-core-tools.js +1 -17
  82. package/dist/agents/timeout.js +18 -6
  83. package/dist/agents/tool-call-id.js +1 -1
  84. package/dist/agents/tool-display-common.js +162 -29
  85. package/dist/agents/tool-images.js +82 -9
  86. package/dist/agents/tool-policy.js +51 -26
  87. package/dist/agents/tools/browser-tool.js +2 -2
  88. package/dist/agents/tools/canvas-tool.js +27 -1
  89. package/dist/agents/tools/common.js +45 -0
  90. package/dist/agents/tools/discord-actions-guild.js +4 -1
  91. package/dist/agents/tools/gateway-tool.js +3 -1
  92. package/dist/agents/tools/nodes-utils.js +1 -10
  93. package/dist/agents/tools/sessions-send-helpers.js +12 -6
  94. package/dist/agents/tools/sessions-spawn-tool.js +8 -2
  95. package/dist/agents/tools/subagents-tool.js +2 -1
  96. package/dist/agents/tools/whatsapp-actions.js +10 -2
  97. package/dist/agents/tools/whatsapp-target-auth.js +18 -0
  98. package/dist/agents/transcript-policy.js +22 -8
  99. package/dist/agents/venice-models.js +11 -3
  100. package/dist/auto-reply/commands-registry.data.js +51 -0
  101. package/dist/auto-reply/commands-registry.js +4 -3
  102. package/dist/auto-reply/group-activation.js +10 -5
  103. package/dist/auto-reply/inbound-debounce.js +10 -5
  104. package/dist/auto-reply/reply/abort.js +1 -1
  105. package/dist/auto-reply/reply/agent-runner-execution.js +4 -1
  106. package/dist/auto-reply/reply/bash-command.js +41 -39
  107. package/dist/auto-reply/reply/command-gates.js +25 -0
  108. package/dist/auto-reply/reply/commands-allowlist.js +111 -72
  109. package/dist/auto-reply/reply/commands-bash.js +6 -5
  110. package/dist/auto-reply/reply/commands-config.js +30 -28
  111. package/dist/auto-reply/reply/commands-core.js +2 -1
  112. package/dist/auto-reply/reply/commands-info.js +1 -0
  113. package/dist/auto-reply/reply/commands-models.js +65 -14
  114. package/dist/auto-reply/reply/commands-session.js +237 -82
  115. package/dist/auto-reply/reply/commands-setunset.js +45 -0
  116. package/dist/auto-reply/reply/commands-subagents/action-agents.js +44 -0
  117. package/dist/auto-reply/reply/commands-subagents/action-focus.js +64 -0
  118. package/dist/auto-reply/reply/commands-subagents/action-help.js +4 -0
  119. package/dist/auto-reply/reply/commands-subagents/action-info.js +45 -0
  120. package/dist/auto-reply/reply/commands-subagents/action-kill.js +60 -0
  121. package/dist/auto-reply/reply/commands-subagents/action-list.js +44 -0
  122. package/dist/auto-reply/reply/commands-subagents/action-log.js +29 -0
  123. package/dist/auto-reply/reply/commands-subagents/action-send.js +119 -0
  124. package/dist/auto-reply/reply/commands-subagents/action-spawn.js +52 -0
  125. package/dist/auto-reply/reply/commands-subagents/action-unfocus.js +30 -0
  126. package/dist/auto-reply/reply/commands-subagents/shared.js +303 -0
  127. package/dist/auto-reply/reply/commands-subagents.js +51 -587
  128. package/dist/auto-reply/reply/commands-tts.js +10 -5
  129. package/dist/auto-reply/reply/config-value.js +10 -5
  130. package/dist/auto-reply/reply/directive-handling.model-picker.js +12 -6
  131. package/dist/auto-reply/reply/directive-handling.persist.js +9 -21
  132. package/dist/auto-reply/reply/directive-handling.shared.js +24 -4
  133. package/dist/auto-reply/reply/followup-runner.js +1 -0
  134. package/dist/auto-reply/reply/get-reply-directives-utils.js +23 -14
  135. package/dist/auto-reply/reply/get-reply-directives.js +17 -28
  136. package/dist/auto-reply/reply/get-reply-inline-actions.js +1 -0
  137. package/dist/auto-reply/reply/get-reply.js +71 -12
  138. package/dist/auto-reply/reply/model-selection.js +80 -39
  139. package/dist/auto-reply/reply/queue/enqueue.js +10 -5
  140. package/dist/auto-reply/reply/queue/state.js +13 -12
  141. package/dist/auto-reply/reply/reply-payloads.js +67 -36
  142. package/dist/auto-reply/reply/reply-reference.js +9 -8
  143. package/dist/auto-reply/reply/route-reply.js +15 -8
  144. package/dist/auto-reply/reply/session-reset-prompt.js +1 -1
  145. package/dist/auto-reply/reply/session.js +22 -6
  146. package/dist/auto-reply/reply/strip-inbound-meta.js +147 -0
  147. package/dist/auto-reply/reply/subagents-utils.js +56 -30
  148. package/dist/auto-reply/reply/typing.js +46 -21
  149. package/dist/auto-reply/send-policy.js +14 -7
  150. package/dist/auto-reply/status.js +140 -16
  151. package/dist/auto-reply/templating.js +10 -5
  152. package/dist/auto-reply/thinking.js +7 -16
  153. package/dist/auto-reply/tokens.js +21 -5
  154. package/dist/browser/bridge-server.js +36 -20
  155. package/dist/browser/cdp.helpers.js +7 -14
  156. package/dist/browser/cdp.js +35 -15
  157. package/dist/browser/chrome.profile-decoration.js +7 -4
  158. package/dist/browser/config.js +4 -0
  159. package/dist/browser/extension-relay-auth.js +55 -0
  160. package/dist/browser/extension-relay.js +74 -29
  161. package/dist/browser/navigation-guard.js +9 -1
  162. package/dist/browser/paths.js +77 -0
  163. package/dist/browser/profiles.js +13 -8
  164. package/dist/browser/pw-ai-module.js +10 -5
  165. package/dist/browser/pw-session.js +76 -39
  166. package/dist/browser/pw-tools-core.interactions.js +14 -7
  167. package/dist/browser/pw-tools-core.state.js +12 -6
  168. package/dist/browser/routes/agent.act.js +2 -2
  169. package/dist/browser/server-context.js +7 -0
  170. package/dist/build-info.json +3 -3
  171. package/dist/channels/allow-from.js +2 -1
  172. package/dist/channels/allowlists/resolve-utils.js +43 -19
  173. package/dist/channels/channel-config.js +14 -7
  174. package/dist/channels/draft-stream-loop.js +7 -0
  175. package/dist/channels/model-overrides.js +82 -0
  176. package/dist/channels/plugins/normalize/imessage.js +14 -7
  177. package/dist/channels/plugins/normalize/slack.js +10 -5
  178. package/dist/channels/plugins/normalize/telegram.js +14 -7
  179. package/dist/channels/plugins/outbound/discord.js +80 -8
  180. package/dist/channels/plugins/outbound/signal.js +11 -11
  181. package/dist/channels/plugins/setup-helpers.js +10 -5
  182. package/dist/channels/sender-label.js +14 -7
  183. package/dist/channels/session.js +4 -2
  184. package/dist/channels/status-reactions.js +297 -0
  185. package/dist/cli/banner.js +1 -1
  186. package/dist/cli/browser-cli-actions-input/register.files-downloads.js +65 -56
  187. package/dist/cli/cli-name.js +11 -11
  188. package/dist/cli/cli-utils.js +13 -3
  189. package/dist/cli/command-format.js +1 -1
  190. package/dist/cli/config-cli.js +1 -1
  191. package/dist/cli/daemon-cli/lifecycle-core.js +31 -19
  192. package/dist/cli/daemon-cli/lifecycle.js +64 -2
  193. package/dist/cli/daemon-cli/restart-health.js +126 -0
  194. package/dist/cli/daemon-cli/status.gather.js +9 -13
  195. package/dist/cli/daemon-cli/status.print.js +2 -10
  196. package/dist/cli/deps.js +27 -22
  197. package/dist/cli/gateway-cli/run-loop.js +23 -5
  198. package/dist/cli/node-cli/register.js +14 -5
  199. package/dist/cli/nodes-media-utils.js +7 -2
  200. package/dist/cli/outbound-send-deps.js +2 -9
  201. package/dist/cli/outbound-send-mapping.js +11 -0
  202. package/dist/cli/pairing-cli.js +40 -14
  203. package/dist/cli/plugins-cli.js +34 -41
  204. package/dist/cli/ports.js +11 -10
  205. package/dist/cli/program/command-registry.js +2 -11
  206. package/dist/cli/program/command-tree.js +16 -0
  207. package/dist/cli/program/preaction.js +13 -9
  208. package/dist/cli/program/register.configure.js +3 -18
  209. package/dist/cli/program/register.maintenance.js +2 -2
  210. package/dist/cli/program/register.onboard.js +2 -0
  211. package/dist/cli/program/register.status-health-sessions.js +16 -17
  212. package/dist/cli/program/register.subclis.js +93 -52
  213. package/dist/cli/route.js +11 -7
  214. package/dist/cli/system-cli.js +36 -46
  215. package/dist/cli/update-cli/shared.js +22 -9
  216. package/dist/cli/update-cli/update-command.js +89 -14
  217. package/dist/cli/update-cli/wizard.js +6 -12
  218. package/dist/commands/agent/run-context.js +18 -5
  219. package/dist/commands/agent/session-store.js +17 -4
  220. package/dist/commands/agent.js +22 -2
  221. package/dist/commands/agents.bindings.js +14 -7
  222. package/dist/commands/agents.commands.add.js +13 -9
  223. package/dist/commands/agents.commands.identity.js +12 -6
  224. package/dist/commands/agents.commands.list.js +11 -6
  225. package/dist/commands/agents.config.js +8 -10
  226. package/dist/commands/agents.providers.js +12 -6
  227. package/dist/commands/auth-choice-options.js +103 -75
  228. package/dist/commands/auth-choice.apply.byteplus.js +55 -0
  229. package/dist/commands/auth-choice.apply.js +4 -0
  230. package/dist/commands/auth-choice.apply.minimax.js +61 -13
  231. package/dist/commands/auth-choice.apply.openai.js +3 -1
  232. package/dist/commands/auth-choice.apply.volcengine.js +55 -0
  233. package/dist/commands/auth-choice.preferred-provider.js +2 -0
  234. package/dist/commands/channels/remove.js +13 -6
  235. package/dist/commands/channels/shared.js +4 -14
  236. package/dist/commands/configure.commands.js +14 -0
  237. package/dist/commands/configure.gateway.js +2 -4
  238. package/dist/commands/configure.js +1 -1
  239. package/dist/commands/configure.shared.js +11 -0
  240. package/dist/commands/daemon-install-helpers.js +2 -2
  241. package/dist/commands/dashboard.js +12 -10
  242. package/dist/commands/docs.js +14 -8
  243. package/dist/commands/doctor-config-flow.js +11 -9
  244. package/dist/commands/doctor-legacy-config.js +281 -0
  245. package/dist/commands/doctor-state-integrity.js +99 -23
  246. package/dist/commands/doctor-update.js +12 -9
  247. package/dist/commands/models/list.list-command.js +7 -5
  248. package/dist/commands/models/set-image.js +2 -21
  249. package/dist/commands/node-daemon-install-helpers.js +10 -8
  250. package/dist/commands/onboard-auth.config-minimax.js +54 -80
  251. package/dist/commands/onboard-auth.config-opencode.js +2 -18
  252. package/dist/commands/onboard-auth.credentials.js +90 -13
  253. package/dist/commands/onboard-auth.js +1 -1
  254. package/dist/commands/onboard-auth.models.js +6 -5
  255. package/dist/commands/onboard-hooks.js +1 -1
  256. package/dist/commands/onboard-non-interactive/api-keys.js +14 -7
  257. package/dist/commands/onboard-non-interactive/local/auth-choice.js +64 -49
  258. package/dist/commands/onboard-provider-auth-flags.js +14 -0
  259. package/dist/commands/onboard-remote.js +14 -7
  260. package/dist/commands/onboard.js +11 -13
  261. package/dist/commands/sandbox-display.js +6 -5
  262. package/dist/commands/status-all/diagnosis.js +14 -10
  263. package/dist/commands/status-all/format.js +1 -0
  264. package/dist/commands/status.gateway-probe.js +1 -16
  265. package/dist/commands/systemd-linger.js +12 -6
  266. package/dist/config/agent-limits.js +2 -0
  267. package/dist/config/commands.js +30 -16
  268. package/dist/config/config-paths.js +9 -11
  269. package/dist/config/defaults.js +22 -2
  270. package/dist/config/discord-preview-streaming.js +104 -0
  271. package/dist/config/env-vars.js +37 -8
  272. package/dist/config/includes.js +4 -0
  273. package/dist/config/io.js +97 -12
  274. package/dist/config/legacy.migrations.part-1.js +189 -78
  275. package/dist/config/legacy.shared.js +3 -1
  276. package/dist/config/merge-patch.js +4 -0
  277. package/dist/config/prototype-keys.js +4 -0
  278. package/dist/config/schema.help.js +44 -7
  279. package/dist/config/schema.labels.js +38 -6
  280. package/dist/config/sessions/delivery-info.js +10 -3
  281. package/dist/config/sessions/main-session.js +10 -5
  282. package/dist/config/sessions/session-file.js +33 -0
  283. package/dist/config/sessions/session-key.js +10 -5
  284. package/dist/config/sessions/store.js +1 -1
  285. package/dist/config/sessions.js +1 -0
  286. package/dist/config/zod-schema.agent-runtime.js +11 -0
  287. package/dist/config/zod-schema.js +148 -13
  288. package/dist/config/zod-schema.providers-core.js +78 -4
  289. package/dist/config/zod-schema.providers.js +6 -1
  290. package/dist/config/zod-schema.session.js +41 -2
  291. package/dist/cron/run-log.js +3 -0
  292. package/dist/cron/schedule.js +21 -10
  293. package/dist/cron/service/ops.js +35 -21
  294. package/dist/cron/service/timer.js +116 -16
  295. package/dist/cron/stagger.js +3 -1
  296. package/dist/discord/api.js +12 -6
  297. package/dist/discord/draft-chunking.js +22 -0
  298. package/dist/discord/draft-stream.js +124 -0
  299. package/dist/discord/monitor/agent-components.js +1 -1
  300. package/dist/discord/monitor/commands.js +5 -0
  301. package/dist/discord/monitor/gateway-plugin.js +2 -1
  302. package/dist/discord/monitor/listeners.js +37 -27
  303. package/dist/discord/monitor/message-handler.js +4 -1
  304. package/dist/discord/monitor/message-handler.preflight.js +65 -8
  305. package/dist/discord/monitor/message-handler.process.js +246 -217
  306. package/dist/discord/monitor/message-utils.js +143 -6
  307. package/dist/discord/monitor/model-picker-preferences.js +143 -0
  308. package/dist/discord/monitor/model-picker.js +651 -0
  309. package/dist/discord/monitor/native-command.js +573 -16
  310. package/dist/discord/monitor/provider.allowlist.js +223 -0
  311. package/dist/discord/monitor/provider.js +275 -347
  312. package/dist/discord/monitor/provider.lifecycle.js +100 -0
  313. package/dist/discord/monitor/reply-delivery.js +123 -16
  314. package/dist/discord/monitor/thread-bindings.discord-api.js +215 -0
  315. package/dist/discord/monitor/thread-bindings.js +4 -0
  316. package/dist/discord/monitor/thread-bindings.lifecycle.js +177 -0
  317. package/dist/discord/monitor/thread-bindings.manager.js +423 -0
  318. package/dist/discord/monitor/thread-bindings.messages.js +55 -0
  319. package/dist/discord/monitor/thread-bindings.state.js +358 -0
  320. package/dist/discord/monitor/thread-bindings.types.js +6 -0
  321. package/dist/discord/resolve-users.js +33 -21
  322. package/dist/discord/send.channels.js +15 -0
  323. package/dist/discord/send.js +3 -2
  324. package/dist/discord/send.outbound.js +82 -26
  325. package/dist/discord/send.permissions.js +83 -30
  326. package/dist/discord/send.reactions.js +8 -4
  327. package/dist/discord/token.js +10 -5
  328. package/dist/discord/voice/command.js +263 -0
  329. package/dist/discord/voice/manager.js +531 -0
  330. package/dist/gateway/auth.js +34 -10
  331. package/dist/gateway/call.js +4 -16
  332. package/dist/gateway/client.js +28 -4
  333. package/dist/gateway/config-reload.js +3 -4
  334. package/dist/gateway/control-ui.js +219 -96
  335. package/dist/gateway/hooks-mapping.js +88 -38
  336. package/dist/gateway/http-auth-helpers.js +3 -2
  337. package/dist/gateway/http-endpoint-helpers.js +1 -0
  338. package/dist/gateway/net.js +54 -12
  339. package/dist/gateway/node-invoke-system-run-approval.js +14 -35
  340. package/dist/gateway/node-registry.js +10 -5
  341. package/dist/gateway/openai-http.js +1 -0
  342. package/dist/gateway/openresponses-http.js +1 -0
  343. package/dist/gateway/origin-check.js +1 -18
  344. package/dist/gateway/protocol/index.js +4 -3
  345. package/dist/gateway/protocol/schema/cron.js +1 -0
  346. package/dist/gateway/protocol/schema/devices.js +1 -0
  347. package/dist/gateway/protocol/schema/protocol-schemas.js +2 -1
  348. package/dist/gateway/protocol/schema/sessions.js +6 -0
  349. package/dist/gateway/role-policy.js +17 -0
  350. package/dist/gateway/server/ws-connection/connect-policy.js +37 -0
  351. package/dist/gateway/server/ws-connection/message-handler.js +175 -148
  352. package/dist/gateway/server-chat.js +83 -25
  353. package/dist/gateway/server-constants.js +10 -9
  354. package/dist/gateway/server-cron.js +1 -0
  355. package/dist/gateway/server-http.js +16 -7
  356. package/dist/gateway/server-maintenance.js +20 -5
  357. package/dist/gateway/server-methods/chat.js +10 -6
  358. package/dist/gateway/server-methods/config.js +12 -14
  359. package/dist/gateway/server-methods/devices.js +17 -3
  360. package/dist/gateway/server-methods/models.js +11 -1
  361. package/dist/gateway/server-methods/sessions.js +64 -8
  362. package/dist/gateway/server-methods/usage.js +162 -75
  363. package/dist/gateway/server-node-events.js +29 -0
  364. package/dist/gateway/server-runtime-config.js +34 -13
  365. package/dist/gateway/server-startup-memory.js +17 -11
  366. package/dist/gateway/session-utils.fs.js +32 -34
  367. package/dist/gateway/sessions-resolve.js +17 -5
  368. package/dist/gateway/test-helpers.openai-mock.js +14 -7
  369. package/dist/gateway/tools-invoke-http.js +21 -10
  370. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +3 -1
  371. package/dist/hooks/bundled/command-logger/handler.js +7 -2
  372. package/dist/hooks/bundled/session-memory/handler.js +6 -5
  373. package/dist/hooks/frontmatter.js +6 -6
  374. package/dist/hooks/gmail-watcher.js +11 -6
  375. package/dist/hooks/internal-hooks.js +11 -1
  376. package/dist/hooks/llm-slug-generator.js +4 -1
  377. package/dist/hooks/workspace.js +47 -17
  378. package/dist/imessage/accounts.js +9 -20
  379. package/dist/imessage/monitor/inbound-processing.js +2 -1
  380. package/dist/infra/archive.js +174 -73
  381. package/dist/infra/control-ui-assets.js +14 -6
  382. package/dist/infra/device-pairing.js +108 -29
  383. package/dist/infra/env.js +10 -5
  384. package/dist/infra/exec-approvals-allowlist.js +122 -0
  385. package/dist/infra/exec-approvals-analysis.js +34 -3
  386. package/dist/infra/exec-approvals.js +5 -17
  387. package/dist/infra/exec-safe-bin-policy.js +53 -45
  388. package/dist/infra/fs-safe.js +71 -39
  389. package/dist/infra/gateway-lock.js +6 -2
  390. package/dist/infra/heartbeat-wake.js +6 -12
  391. package/dist/infra/host-env-security-policy.json +19 -0
  392. package/dist/infra/host-env-security.js +66 -0
  393. package/dist/infra/net/ssrf.js +131 -38
  394. package/dist/infra/outbound/bound-delivery-router.js +88 -0
  395. package/dist/infra/outbound/channel-selection.js +12 -6
  396. package/dist/infra/outbound/envelope.js +1 -1
  397. package/dist/infra/outbound/format.js +12 -6
  398. package/dist/infra/outbound/payloads.js +14 -7
  399. package/dist/infra/outbound/session-binding-service.js +123 -0
  400. package/dist/infra/path-guards.js +25 -0
  401. package/dist/infra/provider-usage.fetch.codex.js +7 -15
  402. package/dist/infra/provider-usage.fetch.gemini.js +14 -11
  403. package/dist/infra/provider-usage.fetch.shared.js +30 -1
  404. package/dist/infra/provider-usage.fetch.zai.js +10 -9
  405. package/dist/infra/retry-policy.js +4 -2
  406. package/dist/infra/retry.js +9 -5
  407. package/dist/infra/session-cost-usage.js +107 -59
  408. package/dist/infra/session-maintenance-warning.js +3 -1
  409. package/dist/infra/shell-env.js +98 -34
  410. package/dist/infra/ssh-config.js +12 -6
  411. package/dist/infra/system-run-command.js +49 -4
  412. package/dist/infra/update-channels.js +10 -5
  413. package/dist/line/accounts.js +5 -7
  414. package/dist/line/bot-access.js +8 -20
  415. package/dist/line/bot-handlers.js +3 -1
  416. package/dist/link-understanding/detect.js +15 -7
  417. package/dist/media/constants.js +15 -6
  418. package/dist/media/image-ops.js +7 -0
  419. package/dist/media/local-roots.js +3 -2
  420. package/dist/media-understanding/apply.js +4 -1
  421. package/dist/media-understanding/concurrency.js +8 -20
  422. package/dist/memory/backend-config.js +45 -6
  423. package/dist/memory/embeddings.js +10 -4
  424. package/dist/memory/fs-utils.js +23 -0
  425. package/dist/memory/manager-search.js +12 -6
  426. package/dist/memory/manager-sync-ops.js +12 -2
  427. package/dist/memory/qmd-manager.js +466 -53
  428. package/dist/memory/query-expansion.js +167 -3
  429. package/dist/memory/status-format.js +10 -5
  430. package/dist/memory/sync-memory-files.js +1 -1
  431. package/dist/node-host/invoke-system-run.js +281 -0
  432. package/dist/node-host/invoke.js +55 -337
  433. package/dist/pairing/pairing-store.js +22 -0
  434. package/dist/plugin-sdk/allow-from.js +1 -1
  435. package/dist/plugin-sdk/command-auth.js +3 -1
  436. package/dist/plugin-sdk/index.js +6 -3
  437. package/dist/plugin-sdk/webhook-targets.js +32 -0
  438. package/dist/plugins/bundled-dir.js +9 -6
  439. package/dist/plugins/hooks.js +50 -0
  440. package/dist/plugins/install.js +28 -16
  441. package/dist/plugins/runtime.js +3 -17
  442. package/dist/plugins/update.js +78 -12
  443. package/dist/process/spawn-utils.js +14 -7
  444. package/dist/providers/github-copilot-token.js +11 -6
  445. package/dist/providers/qwen-portal-oauth.js +14 -6
  446. package/dist/routing/account-id.js +30 -0
  447. package/dist/routing/resolve-route.js +3 -7
  448. package/dist/routing/session-key.js +2 -16
  449. package/dist/security/audit-channel.js +93 -2
  450. package/dist/security/audit-extra.async.js +159 -5
  451. package/dist/security/audit-extra.js +1 -1
  452. package/dist/security/audit-extra.sync.js +85 -6
  453. package/dist/security/audit.js +40 -4
  454. package/dist/security/dm-policy-shared.js +44 -0
  455. package/dist/security/external-content.js +26 -6
  456. package/dist/shared/entry-status.js +6 -0
  457. package/dist/shared/frontmatter.js +5 -5
  458. package/dist/shared/node-match.js +11 -4
  459. package/dist/shared/operator-scope-compat.js +8 -3
  460. package/dist/signal/accounts.js +7 -20
  461. package/dist/signal/monitor/event-handler.js +3 -1
  462. package/dist/slack/accounts.js +6 -19
  463. package/dist/slack/actions.js +11 -3
  464. package/dist/slack/monitor/auth.js +1 -1
  465. package/dist/slack/monitor/message-handler/dispatch.js +50 -29
  466. package/dist/slack/monitor/replies.js +15 -7
  467. package/dist/slack/monitor/slash.js +22 -13
  468. package/dist/slack/resolve-channels.js +10 -5
  469. package/dist/slack/send.js +102 -12
  470. package/dist/slack/stream-mode.js +10 -0
  471. package/dist/slack/streaming.js +4 -2
  472. package/dist/telegram/accounts.js +19 -14
  473. package/dist/telegram/bot/helpers.js +3 -5
  474. package/dist/telegram/bot-access.js +35 -36
  475. package/dist/telegram/bot-handlers.js +120 -148
  476. package/dist/telegram/bot-message-context.js +68 -9
  477. package/dist/telegram/bot-message-dispatch.js +155 -90
  478. package/dist/telegram/bot-native-commands.js +16 -0
  479. package/dist/telegram/draft-stream.js +14 -1
  480. package/dist/telegram/inline-buttons.js +5 -15
  481. package/dist/telegram/monitor.js +11 -7
  482. package/dist/telegram/network-config.js +19 -7
  483. package/dist/telegram/send.js +3 -2
  484. package/dist/telegram/sent-message-cache.js +5 -6
  485. package/dist/telegram/status-reaction-variants.js +208 -0
  486. package/dist/telegram/sticker-cache.js +11 -9
  487. package/dist/terminal/theme.js +12 -12
  488. package/dist/tts/tts.js +80 -567
  489. package/dist/tui/components/chat-log.js +41 -8
  490. package/dist/tui/theme/theme.js +10 -12
  491. package/dist/tui/tui-local-shell.js +16 -6
  492. package/dist/tui/tui.js +58 -6
  493. package/dist/utils/account-id.js +2 -4
  494. package/dist/utils/boolean.js +10 -5
  495. package/dist/utils/directive-tags.js +11 -0
  496. package/dist/utils/queue-helpers.js +67 -12
  497. package/dist/web/auto-reply/deliver-reply.js +8 -4
  498. package/dist/web/auto-reply/mentions.js +10 -5
  499. package/dist/web/auto-reply/monitor/group-members.js +14 -7
  500. package/dist/web/auto-reply/monitor/process-message.js +45 -24
  501. package/dist/web/inbound/access-control.js +5 -2
  502. package/dist/web/login-qr.js +12 -6
  503. package/dist/web/media.js +123 -16
  504. package/extensions/bluebubbles/src/monitor-processing.ts +580 -139
  505. package/extensions/bluebubbles/src/monitor.ts +208 -1950
  506. package/package.json +1 -1
@@ -6,6 +6,7 @@ import readline from "node:readline";
6
6
  import { resolveAgentWorkspaceDir } from "../agents/agent-scope.js";
7
7
  import { resolveStateDir } from "../config/paths.js";
8
8
  import { createSubsystemLogger } from "../logging/subsystem.js";
9
+ import { isFileMissingError, statRegularFile } from "./fs-utils.js";
9
10
  import { deriveQmdScopeChannel, deriveQmdScopeChatType, isQmdScopeAllowed } from "./qmd-scope.js";
10
11
  import { listSessionFilesForAgent, buildSessionEntry, } from "./session-files.js";
11
12
  import { requireNodeSqlite } from "./sqlite.js";
@@ -15,6 +16,23 @@ const SNIPPET_HEADER_RE = /@@\s*-([0-9]+),([0-9]+)/;
15
16
  const SEARCH_PENDING_UPDATE_WAIT_MS = 500;
16
17
  const MAX_QMD_OUTPUT_CHARS = 200_000;
17
18
  const NUL_MARKER_RE = /(?:\^@|\\0|\\x00|\\u0000|null\s*byte|nul\s*byte)/i;
19
+ const QMD_EMBED_BACKOFF_BASE_MS = 60_000;
20
+ const QMD_EMBED_BACKOFF_MAX_MS = 60 * 60 * 1000;
21
+ let qmdEmbedQueueTail = Promise.resolve();
22
+ async function runWithQmdEmbedLock(task) {
23
+ const previous = qmdEmbedQueueTail;
24
+ let release;
25
+ qmdEmbedQueueTail = new Promise((resolve) => {
26
+ release = resolve;
27
+ });
28
+ await previous.catch(() => undefined);
29
+ try {
30
+ return await task();
31
+ }
32
+ finally {
33
+ release?.();
34
+ }
35
+ }
18
36
  export class QmdMemoryManager {
19
37
  static async create(params) {
20
38
  const resolved = params.resolved.qmd;
@@ -50,6 +68,8 @@ export class QmdMemoryManager {
50
68
  db = null;
51
69
  lastUpdateAt = null;
52
70
  lastEmbedAt = null;
71
+ embedBackoffUntil = null;
72
+ embedFailureCount = 0;
53
73
  attemptedNullByteCollectionRepair = false;
54
74
  constructor(params) {
55
75
  this.cfg = params.cfg;
@@ -179,6 +199,7 @@ export class QmdMemoryManager {
179
199
  catch {
180
200
  // ignore; older qmd versions might not support list --json.
181
201
  }
202
+ await this.migrateLegacyUnscopedCollections(existing);
182
203
  for (const collection of this.qmd.collections) {
183
204
  const listed = existing.get(collection.name);
184
205
  if (listed && !this.shouldRebindCollection(collection, listed)) {
@@ -208,6 +229,52 @@ export class QmdMemoryManager {
208
229
  }
209
230
  }
210
231
  }
232
+ async migrateLegacyUnscopedCollections(existing) {
233
+ for (const collection of this.qmd.collections) {
234
+ if (existing.has(collection.name)) {
235
+ continue;
236
+ }
237
+ const legacyName = this.deriveLegacyCollectionName(collection.name);
238
+ if (!legacyName) {
239
+ continue;
240
+ }
241
+ const listedLegacy = existing.get(legacyName);
242
+ if (!listedLegacy) {
243
+ continue;
244
+ }
245
+ if (!this.canMigrateLegacyCollection(collection, listedLegacy)) {
246
+ log.debug(`qmd legacy collection migration skipped for ${legacyName} (path/pattern mismatch)`);
247
+ continue;
248
+ }
249
+ try {
250
+ await this.removeCollection(legacyName);
251
+ existing.delete(legacyName);
252
+ }
253
+ catch (err) {
254
+ const message = err instanceof Error ? err.message : String(err);
255
+ if (!this.isCollectionMissingError(message)) {
256
+ log.warn(`qmd collection remove failed for ${legacyName}: ${message}`);
257
+ }
258
+ }
259
+ }
260
+ }
261
+ deriveLegacyCollectionName(scopedName) {
262
+ const agentSuffix = `-${this.sanitizeCollectionNameSegment(this.agentId)}`;
263
+ if (!scopedName.endsWith(agentSuffix)) {
264
+ return null;
265
+ }
266
+ const legacyName = scopedName.slice(0, -agentSuffix.length).trim();
267
+ return legacyName || null;
268
+ }
269
+ canMigrateLegacyCollection(collection, listedLegacy) {
270
+ if (listedLegacy.path && !this.pathsMatch(listedLegacy.path, collection.path)) {
271
+ return false;
272
+ }
273
+ if (typeof listedLegacy.pattern === "string" && listedLegacy.pattern !== collection.pattern) {
274
+ return false;
275
+ }
276
+ return true;
277
+ }
211
278
  async ensureCollectionPath(collection) {
212
279
  if (!this.isDirectoryGlobPattern(collection.pattern)) {
213
280
  return;
@@ -238,8 +305,8 @@ export class QmdMemoryManager {
238
305
  shouldRebindCollection(collection, listed) {
239
306
  if (!listed.path) {
240
307
  // Older qmd versions may only return names from `collection list --json`.
241
- // Force sessions collections to rebind so per-agent session export paths stay isolated.
242
- return collection.kind === "sessions";
308
+ // Rebind managed collections so stale path bindings cannot survive upgrades.
309
+ return true;
243
310
  }
244
311
  if (!this.pathsMatch(listed.path, collection.path)) {
245
312
  return true;
@@ -312,11 +379,40 @@ export class QmdMemoryManager {
312
379
  log.warn("qmd query skipped: no managed collections configured");
313
380
  return [];
314
381
  }
315
- const qmdSearchCommand = this.qmd.searchMode ?? "query";
382
+ const qmdSearchCommand = this.qmd.searchMode;
383
+ const mcporterEnabled = this.qmd.mcporter.enabled;
316
384
  let parsed;
317
385
  try {
318
- if (qmdSearchCommand === "query" && collectionNames.length > 1) {
319
- parsed = await this.runQueryAcrossCollections(trimmed, limit, collectionNames);
386
+ if (mcporterEnabled) {
387
+ const tool = qmdSearchCommand === "search"
388
+ ? "search"
389
+ : qmdSearchCommand === "vsearch"
390
+ ? "vector_search"
391
+ : "deep_search";
392
+ const minScore = opts?.minScore ?? 0;
393
+ if (collectionNames.length > 1) {
394
+ parsed = await this.runMcporterAcrossCollections({
395
+ tool,
396
+ query: trimmed,
397
+ limit,
398
+ minScore,
399
+ collectionNames,
400
+ });
401
+ }
402
+ else {
403
+ parsed = await this.runQmdSearchViaMcporter({
404
+ mcporter: this.qmd.mcporter,
405
+ tool,
406
+ query: trimmed,
407
+ limit,
408
+ minScore,
409
+ collection: collectionNames[0],
410
+ timeoutMs: this.qmd.limits.timeoutMs,
411
+ });
412
+ }
413
+ }
414
+ else if (collectionNames.length > 1) {
415
+ parsed = await this.runQueryAcrossCollections(trimmed, limit, collectionNames, qmdSearchCommand);
320
416
  }
321
417
  else {
322
418
  const args = this.buildSearchArgs(qmdSearchCommand, trimmed, limit);
@@ -328,11 +424,13 @@ export class QmdMemoryManager {
328
424
  }
329
425
  }
330
426
  catch (err) {
331
- if (qmdSearchCommand !== "query" && this.isUnsupportedQmdOptionError(err)) {
427
+ if (!mcporterEnabled &&
428
+ qmdSearchCommand !== "query" &&
429
+ this.isUnsupportedQmdOptionError(err)) {
332
430
  log.warn(`qmd ${qmdSearchCommand} does not support configured flags; retrying search with qmd query`);
333
431
  try {
334
432
  if (collectionNames.length > 1) {
335
- parsed = await this.runQueryAcrossCollections(trimmed, limit, collectionNames);
433
+ parsed = await this.runQueryAcrossCollections(trimmed, limit, collectionNames, "query");
336
434
  }
337
435
  else {
338
436
  const fallbackArgs = this.buildSearchArgs("query", trimmed, limit);
@@ -349,13 +447,17 @@ export class QmdMemoryManager {
349
447
  }
350
448
  }
351
449
  else {
352
- log.warn(`qmd ${qmdSearchCommand} failed: ${String(err)}`);
450
+ const label = mcporterEnabled ? "mcporter/qmd" : `qmd ${qmdSearchCommand}`;
451
+ log.warn(`${label} failed: ${String(err)}`);
353
452
  throw err instanceof Error ? err : new Error(String(err));
354
453
  }
355
454
  }
356
455
  const results = [];
357
456
  for (const entry of parsed) {
358
- const doc = await this.resolveDocLocation(entry.docid);
457
+ const doc = await this.resolveDocLocation(entry.docid, {
458
+ preferredCollection: entry.collection,
459
+ preferredFile: entry.file,
460
+ });
359
461
  if (!doc) {
360
462
  continue;
361
463
  }
@@ -375,7 +477,7 @@ export class QmdMemoryManager {
375
477
  source: doc.source,
376
478
  });
377
479
  }
378
- return this.clampResultsByInjectedChars(results.slice(0, limit));
480
+ return this.clampResultsByInjectedChars(this.diversifyResultsBySource(results, limit));
379
481
  }
380
482
  async sync(params) {
381
483
  if (params?.progress) {
@@ -395,19 +497,25 @@ export class QmdMemoryManager {
395
497
  if (!absPath.endsWith(".md")) {
396
498
  throw new Error("path required");
397
499
  }
398
- const stat = await fs.lstat(absPath);
399
- if (stat.isSymbolicLink() || !stat.isFile()) {
400
- throw new Error("path required");
500
+ const statResult = await statRegularFile(absPath);
501
+ if (statResult.missing) {
502
+ return { text: "", path: relPath };
401
503
  }
402
504
  if (params.from !== undefined || params.lines !== undefined) {
403
- const text = await this.readPartialText(absPath, params.from, params.lines);
404
- return { text, path: relPath };
505
+ const partial = await this.readPartialText(absPath, params.from, params.lines);
506
+ if (partial.missing) {
507
+ return { text: "", path: relPath };
508
+ }
509
+ return { text: partial.text, path: relPath };
510
+ }
511
+ const full = await this.readFullText(absPath);
512
+ if (full.missing) {
513
+ return { text: "", path: relPath };
405
514
  }
406
- const content = await fs.readFile(absPath, "utf-8");
407
515
  if (!params.from && !params.lines) {
408
- return { text: content, path: relPath };
516
+ return { text: full.text, path: relPath };
409
517
  }
410
- const lines = content.split("\n");
518
+ const lines = full.text.split("\n");
411
519
  const start = Math.max(1, params.from ?? 1);
412
520
  const count = Math.max(1, params.lines ?? lines.length);
413
521
  const slice = lines.slice(start - 1, start - 1 + count);
@@ -491,26 +599,18 @@ export class QmdMemoryManager {
491
599
  if (this.sessionExporter) {
492
600
  await this.exportSessions();
493
601
  }
494
- try {
495
- await this.runQmd(["update"], { timeoutMs: this.qmd.update.updateTimeoutMs });
496
- }
497
- catch (err) {
498
- if (!(await this.tryRepairNullByteCollections(err, reason))) {
499
- throw err;
500
- }
501
- await this.runQmd(["update"], { timeoutMs: this.qmd.update.updateTimeoutMs });
502
- }
503
- const embedIntervalMs = this.qmd.update.embedIntervalMs;
504
- const shouldEmbed = Boolean(force) ||
505
- this.lastEmbedAt === null ||
506
- (embedIntervalMs > 0 && Date.now() - this.lastEmbedAt > embedIntervalMs);
507
- if (shouldEmbed) {
602
+ await this.runQmdUpdateWithRetry(reason);
603
+ if (this.shouldRunEmbed(force)) {
508
604
  try {
509
- await this.runQmd(["embed"], { timeoutMs: this.qmd.update.embedTimeoutMs });
605
+ await runWithQmdEmbedLock(async () => {
606
+ await this.runQmd(["embed"], { timeoutMs: this.qmd.update.embedTimeoutMs });
607
+ });
510
608
  this.lastEmbedAt = Date.now();
609
+ this.embedBackoffUntil = null;
610
+ this.embedFailureCount = 0;
511
611
  }
512
612
  catch (err) {
513
- log.warn(`qmd embed failed (${reason}): ${String(err)}`);
613
+ this.noteEmbedFailure(reason, err);
514
614
  }
515
615
  }
516
616
  this.lastUpdateAt = Date.now();
@@ -521,6 +621,62 @@ export class QmdMemoryManager {
521
621
  });
522
622
  await this.pendingUpdate;
523
623
  }
624
+ async runQmdUpdateWithRetry(reason) {
625
+ const isBootRun = reason === "boot" || reason.startsWith("boot:");
626
+ const maxAttempts = isBootRun ? 3 : 1;
627
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
628
+ try {
629
+ await this.runQmdUpdateOnce(reason);
630
+ return;
631
+ }
632
+ catch (err) {
633
+ if (attempt >= maxAttempts || !this.isRetryableUpdateError(err)) {
634
+ throw err;
635
+ }
636
+ const delayMs = 500 * 2 ** (attempt - 1);
637
+ log.warn(`qmd update retry ${attempt}/${maxAttempts - 1} after failure (${reason}): ${String(err)}`);
638
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
639
+ }
640
+ }
641
+ }
642
+ async runQmdUpdateOnce(reason) {
643
+ try {
644
+ await this.runQmd(["update"], { timeoutMs: this.qmd.update.updateTimeoutMs });
645
+ }
646
+ catch (err) {
647
+ if (!(await this.tryRepairNullByteCollections(err, reason))) {
648
+ throw err;
649
+ }
650
+ await this.runQmd(["update"], { timeoutMs: this.qmd.update.updateTimeoutMs });
651
+ }
652
+ }
653
+ isRetryableUpdateError(err) {
654
+ if (this.isSqliteBusyError(err)) {
655
+ return true;
656
+ }
657
+ const message = err instanceof Error ? err.message : String(err);
658
+ const normalized = message.toLowerCase();
659
+ return normalized.includes("timed out");
660
+ }
661
+ shouldRunEmbed(force) {
662
+ if (this.qmd.searchMode === "search") {
663
+ return false;
664
+ }
665
+ const now = Date.now();
666
+ if (this.embedBackoffUntil !== null && now < this.embedBackoffUntil) {
667
+ return false;
668
+ }
669
+ const embedIntervalMs = this.qmd.update.embedIntervalMs;
670
+ return (Boolean(force) ||
671
+ this.lastEmbedAt === null ||
672
+ (embedIntervalMs > 0 && now - this.lastEmbedAt > embedIntervalMs));
673
+ }
674
+ noteEmbedFailure(reason, err) {
675
+ this.embedFailureCount += 1;
676
+ const delayMs = Math.min(QMD_EMBED_BACKOFF_MAX_MS, QMD_EMBED_BACKOFF_BASE_MS * 2 ** Math.max(0, this.embedFailureCount - 1));
677
+ this.embedBackoffUntil = Date.now() + delayMs;
678
+ log.warn(`qmd embed failed (${reason}): ${String(err)}; backing off for ${Math.ceil(delayMs / 1000)}s`);
679
+ }
524
680
  enqueueForcedUpdate(reason) {
525
681
  this.queuedForcedRuns += 1;
526
682
  if (!this.queuedForcedUpdate) {
@@ -650,10 +806,144 @@ export class QmdMemoryManager {
650
806
  });
651
807
  });
652
808
  }
809
+ async ensureMcporterDaemonStarted(mcporter) {
810
+ if (!mcporter.enabled) {
811
+ return;
812
+ }
813
+ if (!mcporter.startDaemon) {
814
+ const g = globalThis;
815
+ if (!g.__poolbotMcporterColdStartWarned) {
816
+ g.__poolbotMcporterColdStartWarned = true;
817
+ log.warn("mcporter qmd bridge enabled but startDaemon=false; each query may cold-start QMD MCP. Consider setting memory.qmd.mcporter.startDaemon=true to keep it warm.");
818
+ }
819
+ return;
820
+ }
821
+ const g = globalThis;
822
+ if (!g.__poolbotMcporterDaemonStart) {
823
+ g.__poolbotMcporterDaemonStart = (async () => {
824
+ try {
825
+ await this.runMcporter(["daemon", "start"], { timeoutMs: 10_000 });
826
+ }
827
+ catch (err) {
828
+ log.warn(`mcporter daemon start failed: ${String(err)}`);
829
+ // Allow future searches to retry daemon start on transient failures.
830
+ delete g.__poolbotMcporterDaemonStart;
831
+ }
832
+ })();
833
+ }
834
+ await g.__poolbotMcporterDaemonStart;
835
+ }
836
+ async runMcporter(args, opts) {
837
+ return await new Promise((resolve, reject) => {
838
+ const child = spawn("mcporter", args, {
839
+ // Keep mcporter and direct qmd commands on the same agent-scoped XDG state.
840
+ env: this.env,
841
+ cwd: this.workspaceDir,
842
+ });
843
+ let stdout = "";
844
+ let stderr = "";
845
+ let stdoutTruncated = false;
846
+ let stderrTruncated = false;
847
+ const timer = opts?.timeoutMs
848
+ ? setTimeout(() => {
849
+ child.kill("SIGKILL");
850
+ reject(new Error(`mcporter ${args.join(" ")} timed out after ${opts.timeoutMs}ms`));
851
+ }, opts.timeoutMs)
852
+ : null;
853
+ child.stdout.on("data", (data) => {
854
+ const next = appendOutputWithCap(stdout, data.toString("utf8"), this.maxQmdOutputChars);
855
+ stdout = next.text;
856
+ stdoutTruncated = stdoutTruncated || next.truncated;
857
+ });
858
+ child.stderr.on("data", (data) => {
859
+ const next = appendOutputWithCap(stderr, data.toString("utf8"), this.maxQmdOutputChars);
860
+ stderr = next.text;
861
+ stderrTruncated = stderrTruncated || next.truncated;
862
+ });
863
+ child.on("error", (err) => {
864
+ if (timer) {
865
+ clearTimeout(timer);
866
+ }
867
+ reject(err);
868
+ });
869
+ child.on("close", (code) => {
870
+ if (timer) {
871
+ clearTimeout(timer);
872
+ }
873
+ if (stdoutTruncated || stderrTruncated) {
874
+ reject(new Error(`mcporter ${args.join(" ")} produced too much output (limit ${this.maxQmdOutputChars} chars)`));
875
+ return;
876
+ }
877
+ if (code === 0) {
878
+ resolve({ stdout, stderr });
879
+ }
880
+ else {
881
+ reject(new Error(`mcporter ${args.join(" ")} failed (code ${code}): ${stderr || stdout}`));
882
+ }
883
+ });
884
+ });
885
+ }
886
+ async runQmdSearchViaMcporter(params) {
887
+ await this.ensureMcporterDaemonStarted(params.mcporter);
888
+ const selector = `${params.mcporter.serverName}.${params.tool}`;
889
+ const callArgs = {
890
+ query: params.query,
891
+ limit: params.limit,
892
+ minScore: params.minScore,
893
+ };
894
+ if (params.collection) {
895
+ callArgs.collection = params.collection;
896
+ }
897
+ const result = await this.runMcporter([
898
+ "call",
899
+ selector,
900
+ "--args",
901
+ JSON.stringify(callArgs),
902
+ "--output",
903
+ "json",
904
+ "--timeout",
905
+ String(Math.max(0, params.timeoutMs)),
906
+ ], { timeoutMs: Math.max(params.timeoutMs + 2_000, 5_000) });
907
+ const parsedUnknown = JSON.parse(result.stdout);
908
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
909
+ const structured = isRecord(parsedUnknown) && isRecord(parsedUnknown.structuredContent)
910
+ ? parsedUnknown.structuredContent
911
+ : parsedUnknown;
912
+ const results = isRecord(structured) && Array.isArray(structured.results)
913
+ ? structured.results
914
+ : Array.isArray(structured)
915
+ ? structured
916
+ : [];
917
+ const out = [];
918
+ for (const item of results) {
919
+ if (!isRecord(item)) {
920
+ continue;
921
+ }
922
+ const docidRaw = item.docid;
923
+ const docid = typeof docidRaw === "string" ? docidRaw.replace(/^#/, "").trim() : "";
924
+ if (!docid) {
925
+ continue;
926
+ }
927
+ const scoreRaw = item.score;
928
+ const score = typeof scoreRaw === "number" ? scoreRaw : Number(scoreRaw);
929
+ const snippet = typeof item.snippet === "string" ? item.snippet : "";
930
+ out.push({ docid, score: Number.isFinite(score) ? score : 0, snippet });
931
+ }
932
+ return out;
933
+ }
653
934
  async readPartialText(absPath, from, lines) {
654
935
  const start = Math.max(1, from ?? 1);
655
936
  const count = Math.max(1, lines ?? Number.POSITIVE_INFINITY);
656
- const handle = await fs.open(absPath);
937
+ let handle;
938
+ try {
939
+ handle = await fs.open(absPath);
940
+ }
941
+ catch (err) {
942
+ if (isFileMissingError(err)) {
943
+ return { missing: true };
944
+ }
945
+ throw err;
946
+ }
657
947
  const stream = handle.createReadStream({ encoding: "utf-8" });
658
948
  const rl = readline.createInterface({
659
949
  input: stream,
@@ -677,7 +967,19 @@ export class QmdMemoryManager {
677
967
  rl.close();
678
968
  await handle.close();
679
969
  }
680
- return selected.slice(0, count).join("\n");
970
+ return { missing: false, text: selected.slice(0, count).join("\n") };
971
+ }
972
+ async readFullText(absPath) {
973
+ try {
974
+ const text = await fs.readFile(absPath, "utf-8");
975
+ return { missing: false, text };
976
+ }
977
+ catch (err) {
978
+ if (isFileMissingError(err)) {
979
+ return { missing: true };
980
+ }
981
+ throw err;
982
+ }
681
983
  }
682
984
  ensureDb() {
683
985
  if (this.db) {
@@ -762,7 +1064,7 @@ export class QmdMemoryManager {
762
1064
  const trimmed = lower.replace(/^-+|-+$/g, "");
763
1065
  return trimmed || "agent";
764
1066
  }
765
- async resolveDocLocation(docid) {
1067
+ async resolveDocLocation(docid, hints) {
766
1068
  if (!docid) {
767
1069
  return null;
768
1070
  }
@@ -770,21 +1072,21 @@ export class QmdMemoryManager {
770
1072
  if (!normalized) {
771
1073
  return null;
772
1074
  }
773
- const cached = this.docPathCache.get(normalized);
1075
+ const cacheKey = `${hints?.preferredCollection ?? "*"}:${normalized}`;
1076
+ const cached = this.docPathCache.get(cacheKey);
774
1077
  if (cached) {
775
1078
  return cached;
776
1079
  }
777
1080
  const db = this.ensureDb();
778
- let row;
1081
+ let rows = [];
779
1082
  try {
780
- const exact = db
781
- .prepare("SELECT collection, path FROM documents WHERE hash = ? AND active = 1 LIMIT 1")
782
- .get(normalized);
783
- row = exact;
784
- if (!row) {
785
- row = db
786
- .prepare("SELECT collection, path FROM documents WHERE hash LIKE ? AND active = 1 LIMIT 1")
787
- .get(`${normalized}%`);
1083
+ rows = db
1084
+ .prepare("SELECT collection, path FROM documents WHERE hash = ? AND active = 1")
1085
+ .all(normalized);
1086
+ if (rows.length === 0) {
1087
+ rows = db
1088
+ .prepare("SELECT collection, path FROM documents WHERE hash LIKE ? AND active = 1")
1089
+ .all(`${normalized}%`);
788
1090
  }
789
1091
  }
790
1092
  catch (err) {
@@ -794,16 +1096,49 @@ export class QmdMemoryManager {
794
1096
  }
795
1097
  throw err;
796
1098
  }
797
- if (!row) {
1099
+ if (rows.length === 0) {
798
1100
  return null;
799
1101
  }
800
- const location = this.toDocLocation(row.collection, row.path);
1102
+ const location = this.pickDocLocation(rows, hints);
801
1103
  if (!location) {
802
1104
  return null;
803
1105
  }
804
- this.docPathCache.set(normalized, location);
1106
+ this.docPathCache.set(cacheKey, location);
805
1107
  return location;
806
1108
  }
1109
+ pickDocLocation(rows, hints) {
1110
+ if (hints?.preferredCollection) {
1111
+ for (const row of rows) {
1112
+ if (row.collection !== hints.preferredCollection) {
1113
+ continue;
1114
+ }
1115
+ const location = this.toDocLocation(row.collection, row.path);
1116
+ if (location) {
1117
+ return location;
1118
+ }
1119
+ }
1120
+ }
1121
+ if (hints?.preferredFile) {
1122
+ const preferred = path.normalize(hints.preferredFile);
1123
+ for (const row of rows) {
1124
+ const rowPath = path.normalize(row.path);
1125
+ if (rowPath !== preferred && !rowPath.endsWith(path.sep + preferred)) {
1126
+ continue;
1127
+ }
1128
+ const location = this.toDocLocation(row.collection, row.path);
1129
+ if (location) {
1130
+ return location;
1131
+ }
1132
+ }
1133
+ }
1134
+ for (const row of rows) {
1135
+ const location = this.toDocLocation(row.collection, row.path);
1136
+ if (location) {
1137
+ return location;
1138
+ }
1139
+ }
1140
+ return null;
1141
+ }
807
1142
  extractSnippetLines(snippet) {
808
1143
  const match = SNIPPET_HEADER_RE.exec(snippet);
809
1144
  if (match) {
@@ -962,6 +1297,48 @@ export class QmdMemoryManager {
962
1297
  }
963
1298
  return clamped;
964
1299
  }
1300
+ diversifyResultsBySource(results, limit) {
1301
+ const target = Math.max(0, limit);
1302
+ if (target <= 0) {
1303
+ return [];
1304
+ }
1305
+ if (results.length <= 1) {
1306
+ return results.slice(0, target);
1307
+ }
1308
+ const bySource = new Map();
1309
+ for (const entry of results) {
1310
+ const list = bySource.get(entry.source) ?? [];
1311
+ list.push(entry);
1312
+ bySource.set(entry.source, list);
1313
+ }
1314
+ const hasSessions = bySource.has("sessions");
1315
+ const hasMemory = bySource.has("memory");
1316
+ if (!hasSessions || !hasMemory) {
1317
+ return results.slice(0, target);
1318
+ }
1319
+ const sourceOrder = Array.from(bySource.entries())
1320
+ .toSorted((a, b) => (b[1][0]?.score ?? 0) - (a[1][0]?.score ?? 0))
1321
+ .map(([source]) => source);
1322
+ const diversified = [];
1323
+ while (diversified.length < target) {
1324
+ let emitted = false;
1325
+ for (const source of sourceOrder) {
1326
+ const next = bySource.get(source)?.shift();
1327
+ if (!next) {
1328
+ continue;
1329
+ }
1330
+ diversified.push(next);
1331
+ emitted = true;
1332
+ if (diversified.length >= target) {
1333
+ break;
1334
+ }
1335
+ }
1336
+ if (!emitted) {
1337
+ break;
1338
+ }
1339
+ }
1340
+ return diversified;
1341
+ }
965
1342
  shouldSkipUpdate(force) {
966
1343
  if (force) {
967
1344
  return false;
@@ -1003,14 +1380,50 @@ export class QmdMemoryManager {
1003
1380
  new Promise((resolve) => setTimeout(resolve, SEARCH_PENDING_UPDATE_WAIT_MS)),
1004
1381
  ]);
1005
1382
  }
1006
- async runQueryAcrossCollections(query, limit, collectionNames) {
1007
- log.debug(`qmd query multi-collection workaround active (${collectionNames.length} collections)`);
1383
+ async runQueryAcrossCollections(query, limit, collectionNames, command) {
1384
+ log.debug(`qmd ${command} multi-collection workaround active (${collectionNames.length} collections)`);
1008
1385
  const bestByDocId = new Map();
1009
1386
  for (const collectionName of collectionNames) {
1010
- const args = this.buildSearchArgs("query", query, limit);
1387
+ const args = this.buildSearchArgs(command, query, limit);
1011
1388
  args.push("-c", collectionName);
1012
1389
  const result = await this.runQmd(args, { timeoutMs: this.qmd.limits.timeoutMs });
1013
1390
  const parsed = parseQmdQueryJson(result.stdout, result.stderr);
1391
+ for (const entry of parsed) {
1392
+ const normalizedDocId = typeof entry.docid === "string" && entry.docid.trim().length > 0
1393
+ ? entry.docid
1394
+ : undefined;
1395
+ if (!normalizedDocId) {
1396
+ continue;
1397
+ }
1398
+ const withCollection = {
1399
+ ...entry,
1400
+ docid: normalizedDocId,
1401
+ collection: entry.collection ?? collectionName,
1402
+ };
1403
+ const prev = bestByDocId.get(normalizedDocId);
1404
+ const prevScore = typeof prev?.score === "number" ? prev.score : Number.NEGATIVE_INFINITY;
1405
+ const nextScore = typeof withCollection.score === "number"
1406
+ ? withCollection.score
1407
+ : Number.NEGATIVE_INFINITY;
1408
+ if (!prev || nextScore > prevScore) {
1409
+ bestByDocId.set(normalizedDocId, withCollection);
1410
+ }
1411
+ }
1412
+ }
1413
+ return [...bestByDocId.values()].toSorted((a, b) => (b.score ?? 0) - (a.score ?? 0));
1414
+ }
1415
+ async runMcporterAcrossCollections(params) {
1416
+ const bestByDocId = new Map();
1417
+ for (const collectionName of params.collectionNames) {
1418
+ const parsed = await this.runQmdSearchViaMcporter({
1419
+ mcporter: this.qmd.mcporter,
1420
+ tool: params.tool,
1421
+ query: params.query,
1422
+ limit: params.limit,
1423
+ minScore: params.minScore,
1424
+ collection: collectionName,
1425
+ timeoutMs: this.qmd.limits.timeoutMs,
1426
+ });
1014
1427
  for (const entry of parsed) {
1015
1428
  if (typeof entry.docid !== "string" || !entry.docid.trim()) {
1016
1429
  continue;