@poolzin/pool-bot 2026.1.38 → 2026.2.0

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 (336) hide show
  1. package/assets/chrome-extension/README.md +3 -3
  2. package/assets/chrome-extension/background.js +5 -5
  3. package/assets/chrome-extension/manifest.json +3 -3
  4. package/assets/chrome-extension/options.html +4 -4
  5. package/assets/chrome-extension/options.js +1 -1
  6. package/dist/acp/client.js +3 -3
  7. package/dist/acp/types.js +1 -1
  8. package/dist/agents/agent-paths.js +3 -3
  9. package/dist/agents/auth-profiles/paths.js +3 -3
  10. package/dist/agents/cli-runner/helpers.js +1 -1
  11. package/dist/agents/cli-runner.js +2 -2
  12. package/dist/agents/cloudflare-ai-gateway.js +31 -0
  13. package/dist/agents/compaction.js +16 -2
  14. package/dist/agents/context-window-guard.js +13 -10
  15. package/dist/agents/context.js +4 -4
  16. package/dist/agents/docs-path.js +1 -1
  17. package/dist/agents/minimax-vlm.js +1 -1
  18. package/dist/agents/model-auth.js +12 -1
  19. package/dist/agents/model-catalog.js +4 -4
  20. package/dist/agents/model-selection.js +10 -4
  21. package/dist/agents/models-config.js +3 -3
  22. package/dist/agents/models-config.providers.js +147 -39
  23. package/dist/agents/pi-embedded-helpers/openai.js +1 -1
  24. package/dist/agents/pi-embedded-runner/compact.js +8 -8
  25. package/dist/agents/pi-embedded-runner/model.js +2 -2
  26. package/dist/agents/pi-embedded-runner/run/attempt.js +6 -6
  27. package/dist/agents/pi-embedded-runner/run.js +4 -4
  28. package/dist/agents/pi-embedded-runner/tool-result-truncation.js +275 -0
  29. package/dist/agents/pi-embedded-runner/utils.js +1 -1
  30. package/dist/agents/pi-model-discovery.js +10 -0
  31. package/dist/agents/pi-tool-definition-adapter.js +50 -9
  32. package/dist/agents/pi-tools.before-tool-call.js +67 -0
  33. package/dist/agents/pi-tools.js +10 -5
  34. package/dist/agents/pi-tools.read.js +2 -2
  35. package/dist/agents/session-file-repair.js +83 -0
  36. package/dist/agents/session-transcript-repair.js +68 -0
  37. package/dist/agents/skills/frontmatter.js +1 -1
  38. package/dist/agents/skills/workspace.js +2 -2
  39. package/dist/agents/system-prompt.js +28 -4
  40. package/dist/agents/together-models.js +127 -0
  41. package/dist/agents/tool-images.js +1 -1
  42. package/dist/agents/tool-policy.js +1 -1
  43. package/dist/agents/tools/browser-tool.js +3 -3
  44. package/dist/agents/tools/image-tool.js +2 -2
  45. package/dist/agents/tools/memory-tool.js +94 -7
  46. package/dist/agents/tools/web-search.js +1 -1
  47. package/dist/agents/workspace.js +1 -5
  48. package/dist/auto-reply/commands-registry.data.js +1 -1
  49. package/dist/auto-reply/reply/commands-context-report.js +2 -2
  50. package/dist/auto-reply/reply/commands-session.js +2 -2
  51. package/dist/auto-reply/reply/get-reply-run.js +14 -4
  52. package/dist/auto-reply/reply/groups.js +1 -1
  53. package/dist/auto-reply/reply/inbound-context.js +4 -0
  54. package/dist/auto-reply/reply/inbound-meta.js +130 -0
  55. package/dist/auto-reply/reply/untrusted-context.js +15 -0
  56. package/dist/auto-reply/status.js +1 -1
  57. package/dist/browser/client-fetch.js +1 -1
  58. package/dist/browser/config.js +1 -1
  59. package/dist/browser/extension-relay.js +3 -3
  60. package/dist/browser/server-context.js +2 -2
  61. package/dist/build-info.json +3 -3
  62. package/dist/canvas-host/a2ui.js +3 -3
  63. package/dist/channels/plugins/agent-tools/whatsapp-login.js +1 -17
  64. package/dist/channels/plugins/catalog.js +2 -2
  65. package/dist/channels/plugins/onboarding/imessage.js +1 -1
  66. package/dist/channels/plugins/onboarding/signal.js +1 -1
  67. package/dist/channels/plugins/onboarding/slack.js +4 -4
  68. package/dist/channels/plugins/onboarding/whatsapp.js +3 -3
  69. package/dist/channels/plugins/pairing-message.js +1 -1
  70. package/dist/cli/browser-cli-extension.js +2 -2
  71. package/dist/cli/docs-cli.js +1 -1
  72. package/dist/cli/gateway-cli/dev.js +1 -1
  73. package/dist/cli/memory-cli.js +25 -15
  74. package/dist/cli/nodes-cli/register.canvas.js +1 -1
  75. package/dist/cli/plugins-cli.js +1 -1
  76. package/dist/cli/run-main.js +2 -2
  77. package/dist/cli/security-cli.js +1 -1
  78. package/dist/cli/tagline.js +1 -1
  79. package/dist/cli/update-cli.js +4 -4
  80. package/dist/cli/webhooks-cli.js +5 -5
  81. package/dist/commands/agents.commands.add.js +1 -1
  82. package/dist/commands/auth-choice.apply.api-providers.js +305 -17
  83. package/dist/commands/auth-choice.apply.js +4 -1
  84. package/dist/commands/auth-choice.apply.plugin-provider.js +2 -2
  85. package/dist/commands/auth-choice.apply.xai.js +63 -0
  86. package/dist/commands/auth-choice.preferred-provider.js +7 -1
  87. package/dist/commands/configure.wizard.js +1 -1
  88. package/dist/commands/dashboard.js +1 -1
  89. package/dist/commands/docs.js +1 -1
  90. package/dist/commands/doctor-gateway-services.js +3 -3
  91. package/dist/commands/doctor-state-integrity.js +2 -14
  92. package/dist/commands/doctor-update.js +3 -3
  93. package/dist/commands/doctor.js +1 -1
  94. package/dist/commands/models/list.probe.js +2 -2
  95. package/dist/commands/models/list.registry.js +4 -4
  96. package/dist/commands/models/list.status-command.js +2 -2
  97. package/dist/commands/onboard-auth.config-core.js +366 -28
  98. package/dist/commands/onboard-auth.credentials.js +71 -9
  99. package/dist/commands/onboard-auth.js +3 -3
  100. package/dist/commands/onboard-auth.models.js +26 -24
  101. package/dist/commands/onboard-non-interactive/local/auth-choice.js +140 -6
  102. package/dist/commands/status-all/report-lines.js +1 -1
  103. package/dist/commands/status.command.js +1 -1
  104. package/dist/commands/uninstall.js +3 -3
  105. package/dist/compat/legacy-names.js +1 -1
  106. package/dist/config/io.js +3 -3
  107. package/dist/config/schema.js +1 -1
  108. package/dist/config/types.js +0 -1
  109. package/dist/config/types.memory.js +1 -0
  110. package/dist/config/version.js +4 -4
  111. package/dist/config/zod-schema.js +0 -6
  112. package/dist/daemon/constants.js +7 -7
  113. package/dist/daemon/inspect.js +6 -6
  114. package/dist/daemon/systemd-unit.js +1 -1
  115. package/dist/discord/monitor/message-handler.process.js +6 -4
  116. package/dist/gateway/client.js +0 -14
  117. package/dist/gateway/live-image-probe.js +1 -66
  118. package/dist/gateway/openai-http.js +2 -2
  119. package/dist/gateway/openresponses-http.js +4 -4
  120. package/dist/gateway/server-discovery.js +2 -2
  121. package/dist/gateway/server-http.js +1 -1
  122. package/dist/gateway/server.impl.js +2 -6
  123. package/dist/hooks/frontmatter.js +1 -1
  124. package/dist/hooks/hooks-status.js +1 -1
  125. package/dist/hooks/install.js +2 -2
  126. package/dist/hooks/loader.js +1 -1
  127. package/dist/hooks/workspace.js +3 -3
  128. package/dist/index.js +2 -2
  129. package/dist/infra/bonjour.js +3 -3
  130. package/dist/infra/path-env.js +3 -3
  131. package/dist/infra/provider-usage.fetch.minimax.js +1 -1
  132. package/dist/infra/restart.js +1 -1
  133. package/dist/infra/tailscale.js +1 -1
  134. package/dist/macos/relay.js +2 -2
  135. package/dist/media/input-files.js +1 -1
  136. package/dist/media/mime.js +4 -0
  137. package/dist/media/png-encode.js +74 -0
  138. package/dist/media-understanding/providers/image.js +2 -2
  139. package/dist/memory/backend-config.js +207 -0
  140. package/dist/memory/embeddings.js +1 -1
  141. package/dist/memory/index.js +0 -5
  142. package/dist/memory/manager.js +3 -25
  143. package/dist/memory/types.js +1 -0
  144. package/dist/node-host/runner.js +2 -2
  145. package/dist/pairing/pairing-messages.js +1 -1
  146. package/dist/plugins/discovery.js +1 -1
  147. package/dist/plugins/install.js +2 -2
  148. package/dist/plugins/update.js +1 -1
  149. package/dist/security/audit.js +2 -2
  150. package/dist/shared/text/reasoning-tags.js +52 -7
  151. package/dist/slack/monitor/message-handler/prepare.js +10 -4
  152. package/dist/slack/monitor/slash.js +10 -4
  153. package/dist/tailscale/detect.js +146 -0
  154. package/dist/telegram/bot-message-context.js +1 -1
  155. package/dist/test-helpers/workspace.js +11 -0
  156. package/dist/test-utils/channel-plugins.js +82 -0
  157. package/dist/test-utils/ports.js +73 -0
  158. package/dist/utils/shell-argv.js +61 -0
  159. package/dist/utils.js +10 -0
  160. package/dist/web/qr-image.js +1 -61
  161. package/dist/wizard/onboarding.finalize.js +7 -7
  162. package/dist/wizard/onboarding.js +3 -3
  163. package/docs/RELEASE_WORKFOTS_COMPARISON.md +3 -3
  164. package/docs/_config.yml +2 -2
  165. package/docs/_layouts/default.html +9 -9
  166. package/docs/concepts/typebox.md +1 -1
  167. package/docs/docs.json +1 -1
  168. package/docs/northflank.mdx +7 -7
  169. package/docs/railway.mdx +3 -3
  170. package/docs/render.mdx +5 -5
  171. package/docs/start/lore.md +2 -2
  172. package/extensions/bluebubbles/index.ts +2 -2
  173. package/extensions/bluebubbles/package.json +1 -1
  174. package/extensions/bluebubbles/src/accounts.ts +8 -8
  175. package/extensions/bluebubbles/src/actions.test.ts +22 -22
  176. package/extensions/bluebubbles/src/actions.ts +5 -5
  177. package/extensions/bluebubbles/src/attachments.ts +2 -2
  178. package/extensions/bluebubbles/src/channel.ts +16 -16
  179. package/extensions/bluebubbles/src/chat.ts +2 -2
  180. package/extensions/bluebubbles/src/media-send.ts +2 -2
  181. package/extensions/bluebubbles/src/monitor.test.ts +46 -46
  182. package/extensions/bluebubbles/src/monitor.ts +5 -5
  183. package/extensions/bluebubbles/src/onboarding.ts +7 -7
  184. package/extensions/bluebubbles/src/reactions.ts +2 -2
  185. package/extensions/bluebubbles/src/send.ts +2 -2
  186. package/extensions/copilot-proxy/README.md +1 -1
  187. package/extensions/copilot-proxy/package.json +1 -1
  188. package/extensions/diagnostics-otel/index.ts +2 -2
  189. package/extensions/diagnostics-otel/package.json +1 -1
  190. package/extensions/diagnostics-otel/src/service.ts +3 -3
  191. package/extensions/discord/index.ts +2 -2
  192. package/extensions/discord/package.json +1 -1
  193. package/extensions/google-antigravity-auth/README.md +1 -1
  194. package/extensions/google-antigravity-auth/index.ts +1 -1
  195. package/extensions/google-antigravity-auth/package.json +1 -1
  196. package/extensions/google-gemini-cli-auth/README.md +1 -1
  197. package/extensions/google-gemini-cli-auth/oauth.ts +1 -1
  198. package/extensions/google-gemini-cli-auth/package.json +1 -1
  199. package/extensions/googlechat/index.ts +3 -3
  200. package/extensions/googlechat/package.json +1 -1
  201. package/extensions/googlechat/src/accounts.ts +8 -8
  202. package/extensions/googlechat/src/actions.ts +6 -6
  203. package/extensions/googlechat/src/channel.ts +21 -21
  204. package/extensions/googlechat/src/monitor.ts +8 -8
  205. package/extensions/googlechat/src/onboarding.ts +10 -10
  206. package/extensions/imessage/index.ts +2 -2
  207. package/extensions/imessage/package.json +1 -1
  208. package/extensions/line/index.ts +2 -2
  209. package/extensions/line/package.json +1 -1
  210. package/extensions/line/src/card-command.ts +2 -2
  211. package/extensions/line/src/channel.logout.test.ts +4 -4
  212. package/extensions/line/src/channel.sendPayload.test.ts +8 -8
  213. package/extensions/line/src/channel.ts +3 -3
  214. package/extensions/llm-task/README.md +3 -3
  215. package/extensions/llm-task/index.ts +2 -2
  216. package/extensions/llm-task/package.json +1 -1
  217. package/extensions/llm-task/src/llm-task-tool.ts +4 -4
  218. package/extensions/lobster/README.md +6 -6
  219. package/extensions/lobster/index.ts +2 -2
  220. package/extensions/lobster/src/lobster-tool.test.ts +4 -4
  221. package/extensions/lobster/src/lobster-tool.ts +2 -2
  222. package/extensions/matrix/index.ts +2 -2
  223. package/extensions/matrix/package.json +1 -1
  224. package/extensions/matrix/src/matrix/client/config.ts +1 -1
  225. package/extensions/matrix/src/matrix/monitor/handler.ts +1 -1
  226. package/extensions/matrix/src/onboarding.ts +1 -1
  227. package/extensions/mattermost/index.ts +2 -2
  228. package/extensions/mattermost/package.json +1 -1
  229. package/extensions/mattermost/src/mattermost/accounts.ts +8 -8
  230. package/extensions/mattermost/src/mattermost/monitor-helpers.ts +5 -5
  231. package/extensions/mattermost/src/mattermost/monitor.ts +2 -2
  232. package/extensions/mattermost/src/onboarding-helpers.ts +3 -3
  233. package/extensions/mattermost/src/onboarding.ts +2 -2
  234. package/extensions/memory-core/index.ts +2 -2
  235. package/extensions/memory-core/package.json +1 -4
  236. package/extensions/memory-lancedb/index.ts +3 -3
  237. package/extensions/memory-lancedb/package.json +1 -1
  238. package/extensions/msteams/index.ts +2 -2
  239. package/extensions/msteams/package.json +1 -1
  240. package/extensions/msteams/src/channel.directory.test.ts +2 -2
  241. package/extensions/msteams/src/channel.ts +2 -2
  242. package/extensions/msteams/src/graph-upload.ts +4 -4
  243. package/extensions/msteams/src/monitor-handler.ts +2 -2
  244. package/extensions/msteams/src/monitor.ts +2 -2
  245. package/extensions/msteams/src/onboarding.ts +9 -9
  246. package/extensions/msteams/src/reply-dispatcher.ts +2 -2
  247. package/extensions/msteams/src/send-context.ts +2 -2
  248. package/extensions/msteams/src/send.ts +4 -4
  249. package/extensions/nextcloud-talk/index.ts +2 -2
  250. package/extensions/nextcloud-talk/package.json +1 -1
  251. package/extensions/nextcloud-talk/src/channel.ts +7 -7
  252. package/extensions/nextcloud-talk/src/inbound.ts +7 -7
  253. package/extensions/nextcloud-talk/src/onboarding.ts +1 -1
  254. package/extensions/nostr/README.md +2 -2
  255. package/extensions/nostr/index.ts +5 -5
  256. package/extensions/nostr/package.json +1 -1
  257. package/extensions/nostr/src/types.ts +4 -4
  258. package/extensions/open-prose/index.ts +2 -2
  259. package/extensions/qwen-portal-auth/README.md +1 -1
  260. package/extensions/signal/index.ts +2 -2
  261. package/extensions/signal/package.json +1 -1
  262. package/extensions/slack/index.ts +2 -2
  263. package/extensions/slack/package.json +1 -1
  264. package/extensions/telegram/index.ts +2 -2
  265. package/extensions/telegram/package.json +1 -1
  266. package/extensions/telegram/src/channel.ts +2 -2
  267. package/extensions/tlon/README.md +2 -2
  268. package/extensions/tlon/index.ts +2 -2
  269. package/extensions/tlon/package.json +1 -1
  270. package/extensions/tlon/src/channel.ts +13 -13
  271. package/extensions/tlon/src/monitor/index.ts +3 -3
  272. package/extensions/tlon/src/onboarding.ts +3 -3
  273. package/extensions/tlon/src/types.ts +3 -3
  274. package/extensions/twitch/README.md +1 -1
  275. package/extensions/twitch/index.ts +2 -2
  276. package/extensions/twitch/package.json +1 -1
  277. package/extensions/twitch/src/config.ts +3 -3
  278. package/extensions/twitch/src/monitor.ts +3 -3
  279. package/extensions/twitch/src/onboarding.ts +9 -9
  280. package/extensions/twitch/src/outbound.test.ts +2 -2
  281. package/extensions/twitch/src/plugin.test.ts +2 -2
  282. package/extensions/twitch/src/plugin.ts +8 -8
  283. package/extensions/twitch/src/send.test.ts +2 -2
  284. package/extensions/twitch/src/send.ts +4 -4
  285. package/extensions/twitch/src/token.test.ts +8 -8
  286. package/extensions/twitch/src/token.ts +3 -3
  287. package/extensions/twitch/src/twitch-client.ts +3 -3
  288. package/extensions/twitch/src/types.ts +3 -3
  289. package/extensions/twitch/src/utils/markdown.ts +1 -1
  290. package/extensions/voice-call/README.md +3 -3
  291. package/extensions/voice-call/package.json +1 -1
  292. package/extensions/voice-call/src/core-bridge.ts +2 -2
  293. package/extensions/voice-call/src/response-generator.ts +1 -1
  294. package/extensions/whatsapp/index.ts +2 -2
  295. package/extensions/whatsapp/package.json +1 -1
  296. package/extensions/zalo/README.md +1 -1
  297. package/extensions/zalo/index.ts +2 -2
  298. package/extensions/zalo/package.json +1 -1
  299. package/extensions/zalo/src/accounts.ts +8 -8
  300. package/extensions/zalo/src/actions.ts +4 -4
  301. package/extensions/zalo/src/channel.directory.test.ts +2 -2
  302. package/extensions/zalo/src/channel.ts +18 -18
  303. package/extensions/zalo/src/monitor.ts +9 -9
  304. package/extensions/zalo/src/monitor.webhook.test.ts +2 -2
  305. package/extensions/zalo/src/onboarding.ts +24 -24
  306. package/extensions/zalo/src/send.ts +2 -2
  307. package/extensions/zalouser/README.md +2 -2
  308. package/extensions/zalouser/index.ts +2 -2
  309. package/extensions/zalouser/package.json +1 -1
  310. package/extensions/zalouser/src/accounts.ts +9 -9
  311. package/extensions/zalouser/src/channel.ts +24 -24
  312. package/extensions/zalouser/src/monitor.ts +4 -4
  313. package/extensions/zalouser/src/onboarding.ts +28 -28
  314. package/package.json +13 -250
  315. package/skills/nano-banana-pro/scripts/generate_image.py +1 -1
  316. package/skills/tmux/scripts/find-sessions.sh +1 -1
  317. package/CHANGELOG.md +0 -200
  318. package/README-header.png +0 -0
  319. package/git-hooks/pre-commit +0 -4
  320. package/scripts/format-staged.js +0 -148
  321. package/scripts/postinstall.js +0 -300
  322. package/scripts/setup-git-hooks.js +0 -96
  323. package/skills/webgpu-threejs-tsl/REFERENCE.md +0 -283
  324. package/skills/webgpu-threejs-tsl/SKILL.md +0 -91
  325. package/skills/webgpu-threejs-tsl/docs/compute-shaders.md +0 -404
  326. package/skills/webgpu-threejs-tsl/docs/core-concepts.md +0 -453
  327. package/skills/webgpu-threejs-tsl/docs/materials.md +0 -353
  328. package/skills/webgpu-threejs-tsl/docs/post-processing.md +0 -434
  329. package/skills/webgpu-threejs-tsl/docs/wgsl-integration.md +0 -324
  330. package/skills/webgpu-threejs-tsl/examples/basic-setup.js +0 -87
  331. package/skills/webgpu-threejs-tsl/examples/custom-material.js +0 -170
  332. package/skills/webgpu-threejs-tsl/examples/earth-shader.js +0 -292
  333. package/skills/webgpu-threejs-tsl/examples/particle-system.js +0 -259
  334. package/skills/webgpu-threejs-tsl/examples/post-processing.js +0 -199
  335. package/skills/webgpu-threejs-tsl/templates/compute-shader.js +0 -305
  336. package/skills/webgpu-threejs-tsl/templates/webgpu-project.js +0 -276
@@ -0,0 +1,207 @@
1
+ import path from "node:path";
2
+ import { resolveAgentWorkspaceDir } from "../agents/agent-scope.js";
3
+ import { parseDurationMs } from "../cli/parse-duration.js";
4
+ import { resolveUserPath } from "../utils.js";
5
+ import { splitShellArgs } from "../utils/shell-argv.js";
6
+ const DEFAULT_BACKEND = "builtin";
7
+ const DEFAULT_CITATIONS = "auto";
8
+ const DEFAULT_QMD_INTERVAL = "5m";
9
+ const DEFAULT_QMD_DEBOUNCE_MS = 15_000;
10
+ const DEFAULT_QMD_TIMEOUT_MS = 4_000;
11
+ const DEFAULT_QMD_EMBED_INTERVAL = "60m";
12
+ const DEFAULT_QMD_COMMAND_TIMEOUT_MS = 30_000;
13
+ const DEFAULT_QMD_UPDATE_TIMEOUT_MS = 120_000;
14
+ const DEFAULT_QMD_EMBED_TIMEOUT_MS = 120_000;
15
+ const DEFAULT_QMD_LIMITS = {
16
+ maxResults: 6,
17
+ maxSnippetChars: 700,
18
+ maxInjectedChars: 4_000,
19
+ timeoutMs: DEFAULT_QMD_TIMEOUT_MS,
20
+ };
21
+ const DEFAULT_QMD_SCOPE = {
22
+ default: "deny",
23
+ rules: [
24
+ {
25
+ action: "allow",
26
+ match: { chatType: "direct" },
27
+ },
28
+ ],
29
+ };
30
+ function sanitizeName(input) {
31
+ const lower = input.toLowerCase().replace(/[^a-z0-9-]+/g, "-");
32
+ const trimmed = lower.replace(/^-+|-+$/g, "");
33
+ return trimmed || "collection";
34
+ }
35
+ function ensureUniqueName(base, existing) {
36
+ let name = sanitizeName(base);
37
+ if (!existing.has(name)) {
38
+ existing.add(name);
39
+ return name;
40
+ }
41
+ let suffix = 2;
42
+ while (existing.has(`${name}-${suffix}`)) {
43
+ suffix += 1;
44
+ }
45
+ const unique = `${name}-${suffix}`;
46
+ existing.add(unique);
47
+ return unique;
48
+ }
49
+ function resolvePath(raw, workspaceDir) {
50
+ const trimmed = raw.trim();
51
+ if (!trimmed) {
52
+ throw new Error("path required");
53
+ }
54
+ if (trimmed.startsWith("~") || path.isAbsolute(trimmed)) {
55
+ return path.normalize(resolveUserPath(trimmed));
56
+ }
57
+ return path.normalize(path.resolve(workspaceDir, trimmed));
58
+ }
59
+ function resolveIntervalMs(raw) {
60
+ const value = raw?.trim();
61
+ if (!value) {
62
+ return parseDurationMs(DEFAULT_QMD_INTERVAL, { defaultUnit: "m" });
63
+ }
64
+ try {
65
+ return parseDurationMs(value, { defaultUnit: "m" });
66
+ }
67
+ catch {
68
+ return parseDurationMs(DEFAULT_QMD_INTERVAL, { defaultUnit: "m" });
69
+ }
70
+ }
71
+ function resolveEmbedIntervalMs(raw) {
72
+ const value = raw?.trim();
73
+ if (!value) {
74
+ return parseDurationMs(DEFAULT_QMD_EMBED_INTERVAL, { defaultUnit: "m" });
75
+ }
76
+ try {
77
+ return parseDurationMs(value, { defaultUnit: "m" });
78
+ }
79
+ catch {
80
+ return parseDurationMs(DEFAULT_QMD_EMBED_INTERVAL, { defaultUnit: "m" });
81
+ }
82
+ }
83
+ function resolveDebounceMs(raw) {
84
+ if (typeof raw === "number" && Number.isFinite(raw) && raw >= 0) {
85
+ return Math.floor(raw);
86
+ }
87
+ return DEFAULT_QMD_DEBOUNCE_MS;
88
+ }
89
+ function resolveTimeoutMs(raw, fallback) {
90
+ if (typeof raw === "number" && Number.isFinite(raw) && raw > 0) {
91
+ return Math.floor(raw);
92
+ }
93
+ return fallback;
94
+ }
95
+ function resolveLimits(raw) {
96
+ const parsed = { ...DEFAULT_QMD_LIMITS };
97
+ if (raw?.maxResults && raw.maxResults > 0) {
98
+ parsed.maxResults = Math.floor(raw.maxResults);
99
+ }
100
+ if (raw?.maxSnippetChars && raw.maxSnippetChars > 0) {
101
+ parsed.maxSnippetChars = Math.floor(raw.maxSnippetChars);
102
+ }
103
+ if (raw?.maxInjectedChars && raw.maxInjectedChars > 0) {
104
+ parsed.maxInjectedChars = Math.floor(raw.maxInjectedChars);
105
+ }
106
+ if (raw?.timeoutMs && raw.timeoutMs > 0) {
107
+ parsed.timeoutMs = Math.floor(raw.timeoutMs);
108
+ }
109
+ return parsed;
110
+ }
111
+ function resolveSessionConfig(cfg, workspaceDir) {
112
+ const enabled = Boolean(cfg?.enabled);
113
+ const exportDirRaw = cfg?.exportDir?.trim();
114
+ const exportDir = exportDirRaw ? resolvePath(exportDirRaw, workspaceDir) : undefined;
115
+ const retentionDays = cfg?.retentionDays && cfg.retentionDays > 0 ? Math.floor(cfg.retentionDays) : undefined;
116
+ return {
117
+ enabled,
118
+ exportDir,
119
+ retentionDays,
120
+ };
121
+ }
122
+ function resolveCustomPaths(rawPaths, workspaceDir, existing) {
123
+ if (!rawPaths?.length) {
124
+ return [];
125
+ }
126
+ const collections = [];
127
+ rawPaths.forEach((entry, index) => {
128
+ const trimmedPath = entry?.path?.trim();
129
+ if (!trimmedPath) {
130
+ return;
131
+ }
132
+ let resolved;
133
+ try {
134
+ resolved = resolvePath(trimmedPath, workspaceDir);
135
+ }
136
+ catch {
137
+ return;
138
+ }
139
+ const pattern = entry.pattern?.trim() || "**/*.md";
140
+ const baseName = entry.name?.trim() || `custom-${index + 1}`;
141
+ const name = ensureUniqueName(baseName, existing);
142
+ collections.push({
143
+ name,
144
+ path: resolved,
145
+ pattern,
146
+ kind: "custom",
147
+ });
148
+ });
149
+ return collections;
150
+ }
151
+ function resolveDefaultCollections(include, workspaceDir, existing) {
152
+ if (!include) {
153
+ return [];
154
+ }
155
+ const entries = [
156
+ { path: workspaceDir, pattern: "MEMORY.md", base: "memory-root" },
157
+ { path: workspaceDir, pattern: "memory.md", base: "memory-alt" },
158
+ { path: path.join(workspaceDir, "memory"), pattern: "**/*.md", base: "memory-dir" },
159
+ ];
160
+ return entries.map((entry) => ({
161
+ name: ensureUniqueName(entry.base, existing),
162
+ path: entry.path,
163
+ pattern: entry.pattern,
164
+ kind: "memory",
165
+ }));
166
+ }
167
+ export function resolveMemoryBackendConfig(params) {
168
+ const backend = params.cfg.memory?.backend ?? DEFAULT_BACKEND;
169
+ const citations = params.cfg.memory?.citations ?? DEFAULT_CITATIONS;
170
+ if (backend !== "qmd") {
171
+ return { backend: "builtin", citations };
172
+ }
173
+ const workspaceDir = resolveAgentWorkspaceDir(params.cfg, params.agentId);
174
+ const qmdCfg = params.cfg.memory?.qmd;
175
+ const includeDefaultMemory = qmdCfg?.includeDefaultMemory !== false;
176
+ const nameSet = new Set();
177
+ const collections = [
178
+ ...resolveDefaultCollections(includeDefaultMemory, workspaceDir, nameSet),
179
+ ...resolveCustomPaths(qmdCfg?.paths, workspaceDir, nameSet),
180
+ ];
181
+ const rawCommand = qmdCfg?.command?.trim() || "qmd";
182
+ const parsedCommand = splitShellArgs(rawCommand);
183
+ const command = parsedCommand?.[0] || rawCommand.split(/\s+/)[0] || "qmd";
184
+ const resolved = {
185
+ command,
186
+ collections,
187
+ includeDefaultMemory,
188
+ sessions: resolveSessionConfig(qmdCfg?.sessions, workspaceDir),
189
+ update: {
190
+ intervalMs: resolveIntervalMs(qmdCfg?.update?.interval),
191
+ debounceMs: resolveDebounceMs(qmdCfg?.update?.debounceMs),
192
+ onBoot: qmdCfg?.update?.onBoot !== false,
193
+ waitForBootSync: qmdCfg?.update?.waitForBootSync === true,
194
+ embedIntervalMs: resolveEmbedIntervalMs(qmdCfg?.update?.embedInterval),
195
+ commandTimeoutMs: resolveTimeoutMs(qmdCfg?.update?.commandTimeoutMs, DEFAULT_QMD_COMMAND_TIMEOUT_MS),
196
+ updateTimeoutMs: resolveTimeoutMs(qmdCfg?.update?.updateTimeoutMs, DEFAULT_QMD_UPDATE_TIMEOUT_MS),
197
+ embedTimeoutMs: resolveTimeoutMs(qmdCfg?.update?.embedTimeoutMs, DEFAULT_QMD_EMBED_TIMEOUT_MS),
198
+ },
199
+ limits: resolveLimits(qmdCfg?.limits),
200
+ scope: qmdCfg?.scope ?? DEFAULT_QMD_SCOPE,
201
+ };
202
+ return {
203
+ backend: "qmd",
204
+ citations,
205
+ qmd: resolved,
206
+ };
207
+ }
@@ -160,7 +160,7 @@ function formatLocalSetupError(err) {
160
160
  "To enable local embeddings:",
161
161
  "1) Use Node 22 LTS (recommended for installs/updates)",
162
162
  missing
163
- ? "2) Reinstall Moltbot (this should install node-llama-cpp): npm i -g poolbot@latest"
163
+ ? "2) Reinstall Poolbot (this should install node-llama-cpp): npm i -g poolbot@latest"
164
164
  : null,
165
165
  "3) If you use pnpm: pnpm approve-builds (select node-llama-cpp), then pnpm rebuild node-llama-cpp",
166
166
  'Or set agents.defaults.memorySearch.provider = "openai" (remote).',
@@ -1,6 +1 @@
1
- /**
2
- * Memory Search - Public Exports
3
- * Legacy system only (PME removed 2026-02-03)
4
- */
5
- // Legacy memory search system
6
1
  export { getMemorySearchManager } from "./search-manager.js";
@@ -157,7 +157,6 @@ export class MemoryIndexManager {
157
157
  this.sessionWarm.add(key);
158
158
  }
159
159
  async search(query, opts) {
160
- const searchStart = Date.now();
161
160
  void this.warmSession(opts?.sessionKey);
162
161
  if (this.settings.sync.onSearch && (this.dirty || this.sessionsDirty)) {
163
162
  void this.sync({ reason: "search" }).catch((err) => {
@@ -180,17 +179,7 @@ export class MemoryIndexManager {
180
179
  ? await this.searchVector(queryVec, candidates).catch(() => [])
181
180
  : [];
182
181
  if (!hybrid.enabled) {
183
- const results = vectorResults.filter((entry) => entry.score >= minScore).slice(0, maxResults);
184
- const searchDuration = Date.now() - searchStart;
185
- log.debug("memory search performance", {
186
- query: cleaned.substring(0, 50),
187
- durationMs: searchDuration,
188
- resultCount: results.length,
189
- provider: this.provider.id,
190
- model: this.provider.model,
191
- hybrid: false,
192
- });
193
- return results;
182
+ return vectorResults.filter((entry) => entry.score >= minScore).slice(0, maxResults);
194
183
  }
195
184
  const merged = this.mergeHybridResults({
196
185
  vector: vectorResults,
@@ -198,19 +187,7 @@ export class MemoryIndexManager {
198
187
  vectorWeight: hybrid.vectorWeight,
199
188
  textWeight: hybrid.textWeight,
200
189
  });
201
- const results = merged.filter((entry) => entry.score >= minScore).slice(0, maxResults);
202
- const searchDuration = Date.now() - searchStart;
203
- log.debug("memory search performance", {
204
- query: cleaned.substring(0, 50),
205
- durationMs: searchDuration,
206
- resultCount: results.length,
207
- provider: this.provider.id,
208
- model: this.provider.model,
209
- hybrid: true,
210
- keywordResults: keywordResults.length,
211
- vectorResults: vectorResults.length,
212
- });
213
- return results;
190
+ return merged.filter((entry) => entry.score >= minScore).slice(0, maxResults);
214
191
  }
215
192
  async searchVector(queryVec, limit) {
216
193
  const results = await searchVector({
@@ -333,6 +310,7 @@ export class MemoryIndexManager {
333
310
  return sources.map((source) => ({ source, ...bySource.get(source) }));
334
311
  })();
335
312
  return {
313
+ backend: "builtin",
336
314
  files: files?.c ?? 0,
337
315
  chunks: chunks?.c ?? 0,
338
316
  dirty: this.dirty,
@@ -0,0 +1 @@
1
+ export {};
@@ -13,7 +13,7 @@ import { createBrowserControlContext, startBrowserControlServiceFromConfig, } fr
13
13
  import { createBrowserRouteDispatcher } from "../browser/routes/dispatcher.js";
14
14
  import { detectMime } from "../media/mime.js";
15
15
  import { resolveAgentConfig } from "../agents/agent-scope.js";
16
- import { ensureMoltbotCliOnPath } from "../infra/path-env.js";
16
+ import { ensurePoolbotCliOnPath } from "../infra/path-env.js";
17
17
  import { VERSION } from "../version.js";
18
18
  import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
19
19
  import { ensureNodeHostConfig, saveNodeHostConfig } from "./config.js";
@@ -296,7 +296,7 @@ function resolveEnvPath(env) {
296
296
  return raw.split(path.delimiter).filter(Boolean);
297
297
  }
298
298
  function ensureNodePathEnv() {
299
- ensureMoltbotCliOnPath({ pathEnv: process.env.PATH ?? "" });
299
+ ensurePoolbotCliOnPath({ pathEnv: process.env.PATH ?? "" });
300
300
  const current = process.env.PATH ?? "";
301
301
  if (current.trim())
302
302
  return current;
@@ -2,7 +2,7 @@ import { formatCliCommand } from "../cli/command-format.js";
2
2
  export function buildPairingReply(params) {
3
3
  const { channel, idLine, code } = params;
4
4
  return [
5
- "Moltbot: access not configured.",
5
+ "Poolbot: access not configured.",
6
6
  "",
7
7
  idLine,
8
8
  "",
@@ -58,7 +58,7 @@ function addCandidate(params) {
58
58
  packageVersion: manifest?.version?.trim() || undefined,
59
59
  packageDescription: manifest?.description?.trim() || undefined,
60
60
  packageDir: params.packageDir,
61
- packageMoltbot: getPackageManifestMetadata(manifest ?? undefined),
61
+ packagePoolbot: getPackageManifestMetadata(manifest ?? undefined),
62
62
  });
63
63
  }
64
64
  function discoverInDirectory(params) {
@@ -21,7 +21,7 @@ function safeDirName(input) {
21
21
  function safeFileName(input) {
22
22
  return safeDirName(input);
23
23
  }
24
- async function ensureMoltbotExtensions(manifest) {
24
+ async function ensurePoolbotExtensions(manifest) {
25
25
  const extensions = manifest.poolbot?.extensions ?? manifest[LEGACY_MANIFEST_KEY]?.extensions;
26
26
  if (!Array.isArray(extensions)) {
27
27
  throw new Error("package.json missing poolbot.extensions");
@@ -56,7 +56,7 @@ async function installPluginFromPackageDir(params) {
56
56
  }
57
57
  let extensions;
58
58
  try {
59
- extensions = await ensureMoltbotExtensions(manifest);
59
+ extensions = await ensurePoolbotExtensions(manifest);
60
60
  }
61
61
  catch (err) {
62
62
  return { ok: false, error: String(err) };
@@ -26,7 +26,7 @@ function resolveBundledPluginSources(params) {
26
26
  const pluginId = manifest.manifest.id;
27
27
  if (bundled.has(pluginId))
28
28
  continue;
29
- const npmSpec = candidate.packageMoltbot?.install?.npmSpec?.trim() ||
29
+ const npmSpec = candidate.packagePoolbot?.install?.npmSpec?.trim() ||
30
30
  candidate.packageName?.trim() ||
31
31
  undefined;
32
32
  bundled.set(pluginId, {
@@ -65,7 +65,7 @@ async function collectFilesystemFindings(params) {
65
65
  checkId: "fs.state_dir.perms_world_writable",
66
66
  severity: "critical",
67
67
  title: "State dir is world-writable",
68
- detail: `${formatPermissionDetail(params.stateDir, stateDirPerms)}; other users can write into your Moltbot state.`,
68
+ detail: `${formatPermissionDetail(params.stateDir, stateDirPerms)}; other users can write into your Poolbot state.`,
69
69
  remediation: formatPermissionRemediation({
70
70
  targetPath: params.stateDir,
71
71
  perms: stateDirPerms,
@@ -80,7 +80,7 @@ async function collectFilesystemFindings(params) {
80
80
  checkId: "fs.state_dir.perms_group_writable",
81
81
  severity: "warn",
82
82
  title: "State dir is group-writable",
83
- detail: `${formatPermissionDetail(params.stateDir, stateDirPerms)}; group users can write into your Moltbot state.`,
83
+ detail: `${formatPermissionDetail(params.stateDir, stateDirPerms)}; group users can write into your Poolbot state.`,
84
84
  remediation: formatPermissionRemediation({
85
85
  targetPath: params.stateDir,
86
86
  perms: stateDirPerms,
@@ -1,28 +1,70 @@
1
1
  const QUICK_TAG_RE = /<\s*\/?\s*(?:think(?:ing)?|thought|antthinking|final)\b/i;
2
- const FINAL_TAG_RE = /<\s*\/?\s*final\b[^>]*>/gi;
3
- const THINKING_TAG_RE = /<\s*(\/?)\s*(?:think(?:ing)?|thought|antthinking)\b[^>]*>/gi;
2
+ const FINAL_TAG_RE = /<\s*\/?\s*final\b[^<>]*>/gi;
3
+ const THINKING_TAG_RE = /<\s*(\/?)\s*(?:think(?:ing)?|thought|antthinking)\b[^<>]*>/gi;
4
+ function findCodeRegions(text) {
5
+ const regions = [];
6
+ const fencedRe = /(^|\n)(```|~~~)[^\n]*\n[\s\S]*?(?:\n\2(?:\n|$)|$)/g;
7
+ for (const match of text.matchAll(fencedRe)) {
8
+ const start = (match.index ?? 0) + match[1].length;
9
+ regions.push({ start, end: start + match[0].length - match[1].length });
10
+ }
11
+ const inlineRe = /`+[^`]+`+/g;
12
+ for (const match of text.matchAll(inlineRe)) {
13
+ const start = match.index ?? 0;
14
+ const end = start + match[0].length;
15
+ const insideFenced = regions.some((r) => start >= r.start && end <= r.end);
16
+ if (!insideFenced) {
17
+ regions.push({ start, end });
18
+ }
19
+ }
20
+ regions.sort((a, b) => a.start - b.start);
21
+ return regions;
22
+ }
23
+ function isInsideCode(pos, regions) {
24
+ return regions.some((r) => pos >= r.start && pos < r.end);
25
+ }
4
26
  function applyTrim(value, mode) {
5
- if (mode === "none")
27
+ if (mode === "none") {
6
28
  return value;
7
- if (mode === "start")
29
+ }
30
+ if (mode === "start") {
8
31
  return value.trimStart();
32
+ }
9
33
  return value.trim();
10
34
  }
11
35
  export function stripReasoningTagsFromText(text, options) {
12
- if (!text)
36
+ if (!text) {
13
37
  return text;
14
- if (!QUICK_TAG_RE.test(text))
38
+ }
39
+ if (!QUICK_TAG_RE.test(text)) {
15
40
  return text;
41
+ }
16
42
  const mode = options?.mode ?? "strict";
17
43
  const trimMode = options?.trim ?? "both";
18
44
  let cleaned = text;
19
45
  if (FINAL_TAG_RE.test(cleaned)) {
20
46
  FINAL_TAG_RE.lastIndex = 0;
21
- cleaned = cleaned.replace(FINAL_TAG_RE, "");
47
+ const finalMatches = [];
48
+ const preCodeRegions = findCodeRegions(cleaned);
49
+ for (const match of cleaned.matchAll(FINAL_TAG_RE)) {
50
+ const start = match.index ?? 0;
51
+ finalMatches.push({
52
+ start,
53
+ length: match[0].length,
54
+ inCode: isInsideCode(start, preCodeRegions),
55
+ });
56
+ }
57
+ for (let i = finalMatches.length - 1; i >= 0; i--) {
58
+ const m = finalMatches[i];
59
+ if (!m.inCode) {
60
+ cleaned = cleaned.slice(0, m.start) + cleaned.slice(m.start + m.length);
61
+ }
62
+ }
22
63
  }
23
64
  else {
24
65
  FINAL_TAG_RE.lastIndex = 0;
25
66
  }
67
+ const codeRegions = findCodeRegions(cleaned);
26
68
  THINKING_TAG_RE.lastIndex = 0;
27
69
  let result = "";
28
70
  let lastIndex = 0;
@@ -30,6 +72,9 @@ export function stripReasoningTagsFromText(text, options) {
30
72
  for (const match of cleaned.matchAll(THINKING_TAG_RE)) {
31
73
  const idx = match.index ?? 0;
32
74
  const isClose = match[1] === "/";
75
+ if (isInsideCode(idx, codeRegions)) {
76
+ continue;
77
+ }
33
78
  if (!inThinking) {
34
79
  result += cleaned.slice(lastIndex, idx);
35
80
  if (!isClose) {
@@ -350,10 +350,16 @@ export async function prepareSlackMessage(params) {
350
350
  });
351
351
  }
352
352
  const slackTo = isDirectMessage ? `user:${message.user}` : `channel:${message.channel}`;
353
- // SECURITY: Do NOT include channel topic/purpose in system prompt
354
- // Channel metadata is user-controlled and can cause prompt injection
355
- // Only include channelConfig.systemPrompt (admin-controlled)
356
- const groupSystemPrompt = channelConfig?.systemPrompt?.trim() || undefined;
353
+ const channelDescription = [channelInfo?.topic, channelInfo?.purpose]
354
+ .map((entry) => entry?.trim())
355
+ .filter((entry) => Boolean(entry))
356
+ .filter((entry, index, list) => list.indexOf(entry) === index)
357
+ .join("\n");
358
+ const systemPromptParts = [
359
+ channelDescription ? `Channel description: ${channelDescription}` : null,
360
+ channelConfig?.systemPrompt?.trim() || null,
361
+ ].filter((entry) => Boolean(entry));
362
+ const groupSystemPrompt = systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : undefined;
357
363
  let threadStarterBody;
358
364
  let threadLabel;
359
365
  let threadStarterMedia = null;
@@ -288,10 +288,16 @@ export function registerSlackMonitorSlashCommands(params) {
288
288
  id: isDirectMessage ? command.user_id : command.channel_id,
289
289
  },
290
290
  });
291
- // SECURITY: Do NOT include channel topic/purpose in system prompt
292
- // Channel metadata is user-controlled and can cause prompt injection
293
- // Only include channelConfig.systemPrompt (admin-controlled)
294
- const groupSystemPrompt = channelConfig?.systemPrompt?.trim() || undefined;
291
+ const channelDescription = [channelInfo?.topic, channelInfo?.purpose]
292
+ .map((entry) => entry?.trim())
293
+ .filter((entry) => Boolean(entry))
294
+ .filter((entry, index, list) => list.indexOf(entry) === index)
295
+ .join("\n");
296
+ const systemPromptParts = [
297
+ channelDescription ? `Channel description: ${channelDescription}` : null,
298
+ channelConfig?.systemPrompt?.trim() || null,
299
+ ].filter((entry) => Boolean(entry));
300
+ const groupSystemPrompt = systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : undefined;
295
301
  const ctxPayload = finalizeInboundContext({
296
302
  Body: prompt,
297
303
  RawBody: prompt,
@@ -0,0 +1,146 @@
1
+ import { execSync } from "node:child_process";
2
+ import fs from "node:fs/promises";
3
+ import os from "node:os";
4
+ /**
5
+ * Check if Tailscale is installed by looking for the binary.
6
+ */
7
+ async function isTailscaleInstalled() {
8
+ try {
9
+ const platform = os.platform();
10
+ const possiblePaths = [];
11
+ if (platform === "linux") {
12
+ possiblePaths.push("/usr/bin/tailscale", "/usr/local/bin/tailscale");
13
+ }
14
+ else if (platform === "darwin") {
15
+ possiblePaths.push("/Applications/Tailscale.app/Contents/MacOS/Tailscale", "/usr/local/bin/tailscale");
16
+ }
17
+ else if (platform === "win32") {
18
+ possiblePaths.push("C:\\Program Files\\Tailscale\\tailscale.exe", "C:\\ProgramData\\chocolatey\\lib\\tailscale\\tools\\tailscale.exe");
19
+ }
20
+ for (const binPath of possiblePaths) {
21
+ try {
22
+ await fs.access(binPath);
23
+ return true;
24
+ }
25
+ catch {
26
+ continue;
27
+ }
28
+ }
29
+ return false;
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ }
35
+ /**
36
+ * Run tailscale status command and parse output.
37
+ */
38
+ async function getTailscaleStatus() {
39
+ try {
40
+ const platform = os.platform();
41
+ const binPath = platform === "win32"
42
+ ? "tailscale.exe"
43
+ : platform === "darwin"
44
+ ? "/Applications/Tailscale.app/Contents/MacOS/Tailscale"
45
+ : "tailscale";
46
+ const timeout = 5000;
47
+ const result = execSync(`"${binPath}" status --json`, {
48
+ encoding: "utf-8",
49
+ stdio: ["ignore", "pipe", "pipe"],
50
+ timeout,
51
+ });
52
+ const status = JSON.parse(result.trim());
53
+ // Extract useful info from Tailscale status JSON
54
+ const self = status.Self || {};
55
+ const backendState = status.BackendState || "";
56
+ return {
57
+ installed: true,
58
+ running: backendState === "Running",
59
+ ip: self.TailscaleIPs?.[0],
60
+ dnsName: self.DNSName,
61
+ hostname: self.HostName,
62
+ tailnet: status.MagicsockName || status.TailnetName,
63
+ online: self.Online ?? false,
64
+ canServe: true, // If running, can serve
65
+ canFunnel: self.Capabilities?.map.includes("funnel") ?? false,
66
+ };
67
+ }
68
+ catch (err) {
69
+ // If command fails, return minimal info
70
+ return {
71
+ installed: await isTailscaleInstalled(),
72
+ running: false,
73
+ canServe: false,
74
+ canFunnel: false,
75
+ online: false,
76
+ };
77
+ }
78
+ }
79
+ /**
80
+ * Auto-detect Tailscale configuration and recommend settings.
81
+ *
82
+ * This is a SAFE detection function - it only reads status and never
83
+ * modifies configuration. Recommendations are purely advisory.
84
+ *
85
+ * @returns Tailscale auto-detection result
86
+ */
87
+ export async function detectTailscaleConfig() {
88
+ const info = {
89
+ installed: await isTailscaleInstalled(),
90
+ running: false,
91
+ canServe: false,
92
+ canFunnel: false,
93
+ online: false,
94
+ };
95
+ if (!info.installed) {
96
+ return { detected: false };
97
+ }
98
+ // Get detailed status if installed
99
+ const status = await getTailscaleStatus();
100
+ Object.assign(info, status);
101
+ // Generate recommendations based on detected state
102
+ const recommended = info.online
103
+ ? {
104
+ mode: info.canFunnel ? "funnel" : "serve",
105
+ bind: "tailnet",
106
+ port: 18789,
107
+ reason: info.canFunnel
108
+ ? "Tailscale is online and supports funnel (public access)"
109
+ : "Tailscale is online - use serve for private tailnet access",
110
+ }
111
+ : info.running
112
+ ? {
113
+ mode: "off",
114
+ bind: "loopback",
115
+ port: 18789,
116
+ reason: "Tailscale is installed but offline - use loopback bind",
117
+ }
118
+ : undefined;
119
+ return {
120
+ detected: true,
121
+ info,
122
+ recommended,
123
+ };
124
+ }
125
+ /**
126
+ * Get a human-readable summary of Tailscale detection.
127
+ */
128
+ export function formatTailscaleDetection(result) {
129
+ if (!result.detected || !result.info) {
130
+ return "Tailscale: Not installed";
131
+ }
132
+ const { info, recommended } = result;
133
+ const parts = [];
134
+ parts.push(`Tailscale: ${info.installed ? "Installed" : "Not installed"}`);
135
+ parts.push(`Status: ${info.running ? (info.online ? "Online" : "Offline") : "Not running"}`);
136
+ if (info.online && info.ip) {
137
+ parts.push(`IP: ${info.ip}`);
138
+ }
139
+ if (info.dnsName) {
140
+ parts.push(`DNS: ${info.dnsName}`);
141
+ }
142
+ if (recommended) {
143
+ parts.push(`\nRecommendation: ${recommended.mode} (${recommended.reason})`);
144
+ }
145
+ return parts.join(" | ");
146
+ }
@@ -141,7 +141,7 @@ export const buildTelegramMessageContext = async ({ primaryCtx, allMedia, storeA
141
141
  await withTelegramApiErrorLogging({
142
142
  operation: "sendMessage",
143
143
  fn: () => bot.api.sendMessage(chatId, [
144
- "Moltbot: access not configured.",
144
+ "Poolbot: access not configured.",
145
145
  "",
146
146
  `Your Telegram user id: ${telegramUserId}`,
147
147
  "",
@@ -0,0 +1,11 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ export async function makeTempWorkspace(prefix = "poolbot-workspace-") {
5
+ return fs.mkdtemp(path.join(os.tmpdir(), prefix));
6
+ }
7
+ export async function writeWorkspaceFile(params) {
8
+ const filePath = path.join(params.dir, params.name);
9
+ await fs.writeFile(filePath, params.content, "utf-8");
10
+ return filePath;
11
+ }