@poolzin/pool-bot 2026.2.0 → 2026.2.2

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 (258) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/README-header.png +0 -0
  3. package/dist/agents/bash-tools.exec.js +76 -25
  4. package/dist/agents/cli-runner/helpers.js +9 -11
  5. package/dist/agents/context.js +1 -1
  6. package/dist/agents/identity.js +47 -7
  7. package/dist/agents/memory-search.js +25 -8
  8. package/dist/agents/model-catalog.js +1 -1
  9. package/dist/agents/model-selection.js +21 -0
  10. package/dist/agents/pi-embedded-block-chunker.js +117 -42
  11. package/dist/agents/pi-embedded-helpers/errors.js +183 -78
  12. package/dist/agents/pi-embedded-helpers.js +1 -1
  13. package/dist/agents/pi-embedded-runner/compact.js +8 -10
  14. package/dist/agents/pi-embedded-runner/model.js +62 -3
  15. package/dist/agents/pi-embedded-runner/run/attempt.js +21 -11
  16. package/dist/agents/pi-embedded-runner/run.js +199 -46
  17. package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
  18. package/dist/agents/pi-embedded-subscribe.js +118 -29
  19. package/dist/agents/pi-tools.js +10 -5
  20. package/dist/agents/poolbot-tools.js +15 -10
  21. package/dist/agents/sandbox-paths.js +31 -0
  22. package/dist/agents/session-tool-result-guard.js +94 -15
  23. package/dist/agents/shell-utils.js +51 -0
  24. package/dist/agents/skills/bundled-context.js +23 -0
  25. package/dist/agents/skills/bundled-dir.js +41 -7
  26. package/dist/agents/skills-install.js +60 -23
  27. package/dist/agents/subagent-announce.js +79 -34
  28. package/dist/agents/tool-policy.conformance.js +14 -0
  29. package/dist/agents/tool-policy.js +24 -0
  30. package/dist/agents/tools/cron-tool.js +166 -19
  31. package/dist/agents/tools/discord-actions-presence.js +78 -0
  32. package/dist/agents/tools/image-tool.js +1 -1
  33. package/dist/agents/tools/message-tool.js +56 -2
  34. package/dist/agents/tools/sessions-history-tool.js +69 -1
  35. package/dist/agents/tools/web-search.js +211 -42
  36. package/dist/agents/usage.js +23 -1
  37. package/dist/agents/workspace-run.js +67 -0
  38. package/dist/agents/workspace-templates.js +44 -0
  39. package/dist/auto-reply/command-auth.js +121 -6
  40. package/dist/auto-reply/envelope.js +74 -82
  41. package/dist/auto-reply/reply/commands-compact.js +1 -0
  42. package/dist/auto-reply/reply/commands-context-report.js +1 -0
  43. package/dist/auto-reply/reply/commands-context.js +1 -0
  44. package/dist/auto-reply/reply/commands-models.js +107 -60
  45. package/dist/auto-reply/reply/commands-ptt.js +171 -0
  46. package/dist/auto-reply/reply/get-reply-run.js +2 -1
  47. package/dist/auto-reply/reply/inbound-context.js +5 -1
  48. package/dist/auto-reply/reply/mentions.js +1 -1
  49. package/dist/auto-reply/reply/model-selection.js +3 -3
  50. package/dist/auto-reply/thinking.js +88 -43
  51. package/dist/browser/bridge-server.js +13 -0
  52. package/dist/browser/cdp.helpers.js +38 -24
  53. package/dist/browser/client-fetch.js +50 -7
  54. package/dist/browser/config.js +1 -10
  55. package/dist/browser/extension-relay.js +101 -40
  56. package/dist/browser/pw-ai.js +1 -1
  57. package/dist/browser/pw-session.js +143 -8
  58. package/dist/browser/pw-tools-core.interactions.js +125 -27
  59. package/dist/browser/pw-tools-core.responses.js +1 -1
  60. package/dist/browser/pw-tools-core.state.js +1 -1
  61. package/dist/browser/routes/agent.act.js +86 -41
  62. package/dist/browser/routes/dispatcher.js +4 -4
  63. package/dist/browser/screenshot.js +1 -1
  64. package/dist/browser/server.js +13 -0
  65. package/dist/build-info.json +3 -3
  66. package/dist/canvas-host/a2ui/index.html +28 -28
  67. package/dist/channels/reply-prefix.js +8 -1
  68. package/dist/cli/cron-cli/register.cron-add.js +61 -40
  69. package/dist/cli/cron-cli/register.cron-edit.js +60 -34
  70. package/dist/cli/cron-cli/shared.js +56 -41
  71. package/dist/cli/dns-cli.js +26 -14
  72. package/dist/cli/gateway-cli/register.js +37 -19
  73. package/dist/cli/memory-cli.js +5 -5
  74. package/dist/cli/parse-bytes.js +37 -0
  75. package/dist/cli/update-cli.js +173 -52
  76. package/dist/commands/agent.js +1 -0
  77. package/dist/commands/auth-choice.apply.oauth.js +1 -1
  78. package/dist/commands/doctor-config-flow.js +61 -5
  79. package/dist/commands/doctor-state-migrations.js +1 -1
  80. package/dist/commands/health.js +1 -1
  81. package/dist/commands/model-allowlist.js +29 -0
  82. package/dist/commands/model-picker.js +2 -1
  83. package/dist/commands/models/list.registry.js +1 -1
  84. package/dist/commands/models/list.status-command.js +43 -23
  85. package/dist/commands/models/shared.js +15 -0
  86. package/dist/commands/onboard-custom.js +384 -0
  87. package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +35 -0
  88. package/dist/commands/onboard-non-interactive/local/auth-choice.js +6 -3
  89. package/dist/commands/onboard-skills.js +63 -38
  90. package/dist/commands/openai-model-default.js +41 -0
  91. package/dist/compat/legacy-names.js +2 -0
  92. package/dist/config/defaults.js +3 -2
  93. package/dist/config/paths.js +136 -35
  94. package/dist/config/plugin-auto-enable.js +21 -5
  95. package/dist/config/redact-snapshot.js +153 -0
  96. package/dist/config/schema.field-metadata.js +590 -0
  97. package/dist/config/schema.js +2 -2
  98. package/dist/config/sessions/store.js +291 -23
  99. package/dist/config/zod-schema.agent-defaults.js +3 -0
  100. package/dist/config/zod-schema.agent-runtime.js +13 -2
  101. package/dist/config/zod-schema.providers-core.js +142 -0
  102. package/dist/config/zod-schema.session.js +3 -0
  103. package/dist/control-ui/assets/{index-CIRDm-Lu.css → index-CSfXd2LO.css} +1 -1
  104. package/dist/control-ui/assets/{index-CmNMuoem.js → index-HRr1grwl.js} +446 -413
  105. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -0
  106. package/dist/control-ui/index.html +4 -4
  107. package/dist/cron/delivery.js +57 -0
  108. package/dist/cron/isolated-agent/delivery-target.js +18 -3
  109. package/dist/cron/isolated-agent/helpers.js +22 -5
  110. package/dist/cron/isolated-agent/run.js +172 -63
  111. package/dist/cron/isolated-agent/session.js +2 -0
  112. package/dist/cron/normalize.js +356 -28
  113. package/dist/cron/parse.js +10 -5
  114. package/dist/cron/run-log.js +35 -10
  115. package/dist/cron/schedule.js +41 -6
  116. package/dist/cron/service/jobs.js +208 -35
  117. package/dist/cron/service/ops.js +72 -16
  118. package/dist/cron/service/state.js +2 -0
  119. package/dist/cron/service/store.js +386 -14
  120. package/dist/cron/service/timer.js +390 -147
  121. package/dist/cron/session-reaper.js +86 -0
  122. package/dist/cron/store.js +23 -8
  123. package/dist/cron/validate-timestamp.js +43 -0
  124. package/dist/discord/monitor/agent-components.js +438 -0
  125. package/dist/discord/monitor/allow-list.js +28 -5
  126. package/dist/discord/monitor/gateway-registry.js +29 -0
  127. package/dist/discord/monitor/native-command.js +44 -23
  128. package/dist/discord/monitor/sender-identity.js +45 -0
  129. package/dist/discord/pluralkit.js +27 -0
  130. package/dist/discord/send.outbound.js +92 -5
  131. package/dist/discord/send.shared.js +60 -23
  132. package/dist/discord/targets.js +84 -1
  133. package/dist/entry.js +15 -9
  134. package/dist/extensionAPI.js +8 -0
  135. package/dist/gateway/control-ui.js +8 -1
  136. package/dist/gateway/hooks-mapping.js +3 -0
  137. package/dist/gateway/hooks.js +65 -0
  138. package/dist/gateway/net.js +96 -31
  139. package/dist/gateway/node-command-policy.js +50 -15
  140. package/dist/gateway/origin-check.js +56 -0
  141. package/dist/gateway/protocol/client-info.js +9 -0
  142. package/dist/gateway/protocol/index.js +9 -2
  143. package/dist/gateway/protocol/schema/agents-models-skills.js +71 -1
  144. package/dist/gateway/protocol/schema/cron.js +22 -10
  145. package/dist/gateway/protocol/schema/protocol-schemas.js +16 -2
  146. package/dist/gateway/protocol/schema/sessions.js +12 -0
  147. package/dist/gateway/server/hooks.js +1 -1
  148. package/dist/gateway/server-broadcast.js +26 -9
  149. package/dist/gateway/server-chat.js +112 -23
  150. package/dist/gateway/server-discovery-runtime.js +10 -2
  151. package/dist/gateway/server-http.js +109 -11
  152. package/dist/gateway/server-methods/agent-timestamp.js +60 -0
  153. package/dist/gateway/server-methods/agents.js +321 -2
  154. package/dist/gateway/server-methods/usage.js +559 -16
  155. package/dist/gateway/server-runtime-state.js +22 -8
  156. package/dist/gateway/server-startup-memory.js +16 -0
  157. package/dist/gateway/server.impl.js +5 -1
  158. package/dist/gateway/session-utils.fs.js +23 -25
  159. package/dist/gateway/session-utils.js +20 -10
  160. package/dist/gateway/sessions-patch.js +7 -22
  161. package/dist/gateway/test-helpers.mocks.js +11 -7
  162. package/dist/gateway/test-helpers.server.js +35 -2
  163. package/dist/imessage/constants.js +2 -0
  164. package/dist/imessage/monitor/deliver.js +4 -1
  165. package/dist/imessage/monitor/monitor-provider.js +51 -1
  166. package/dist/infra/bonjour-discovery.js +131 -70
  167. package/dist/infra/control-ui-assets.js +134 -12
  168. package/dist/infra/errors.js +12 -0
  169. package/dist/infra/exec-approvals.js +266 -57
  170. package/dist/infra/format-time/format-datetime.js +79 -0
  171. package/dist/infra/format-time/format-duration.js +81 -0
  172. package/dist/infra/format-time/format-relative.js +80 -0
  173. package/dist/infra/heartbeat-runner.js +140 -49
  174. package/dist/infra/home-dir.js +54 -0
  175. package/dist/infra/net/fetch-guard.js +122 -0
  176. package/dist/infra/net/ssrf.js +65 -29
  177. package/dist/infra/outbound/abort.js +14 -0
  178. package/dist/infra/outbound/message-action-runner.js +77 -13
  179. package/dist/infra/outbound/outbound-session.js +143 -37
  180. package/dist/infra/poolbot-root.js +43 -1
  181. package/dist/infra/session-cost-usage.js +631 -41
  182. package/dist/infra/state-migrations.js +317 -47
  183. package/dist/infra/update-global.js +35 -0
  184. package/dist/infra/update-runner.js +149 -43
  185. package/dist/infra/warning-filter.js +65 -0
  186. package/dist/infra/widearea-dns.js +30 -9
  187. package/dist/logging/redact-identifier.js +12 -0
  188. package/dist/media/fetch.js +81 -58
  189. package/dist/media/store.js +2 -0
  190. package/dist/media-understanding/apply.js +403 -3
  191. package/dist/media-understanding/attachments.js +38 -27
  192. package/dist/media-understanding/defaults.js +16 -0
  193. package/dist/media-understanding/providers/deepgram/audio.js +22 -14
  194. package/dist/media-understanding/providers/google/audio.js +24 -17
  195. package/dist/media-understanding/providers/google/video.js +24 -17
  196. package/dist/media-understanding/providers/image.js +3 -3
  197. package/dist/media-understanding/providers/index.js +4 -1
  198. package/dist/media-understanding/providers/openai/audio.js +22 -14
  199. package/dist/media-understanding/providers/shared.js +16 -11
  200. package/dist/media-understanding/providers/zai/index.js +6 -0
  201. package/dist/media-understanding/runner.js +158 -90
  202. package/dist/memory/batch-voyage.js +277 -0
  203. package/dist/memory/embeddings-voyage.js +75 -0
  204. package/dist/memory/embeddings.js +28 -16
  205. package/dist/memory/internal.js +101 -18
  206. package/dist/memory/manager.js +154 -48
  207. package/dist/memory/search-manager.js +173 -0
  208. package/dist/memory/session-files.js +9 -3
  209. package/dist/node-host/runner.js +34 -24
  210. package/dist/node-host/with-timeout.js +27 -0
  211. package/dist/plugins/commands.js +5 -1
  212. package/dist/plugins/config-state.js +86 -7
  213. package/dist/plugins/source-display.js +51 -0
  214. package/dist/process/exec.js +20 -2
  215. package/dist/routing/resolve-route.js +12 -0
  216. package/dist/routing/session-key.js +15 -0
  217. package/dist/runtime.js +2 -0
  218. package/dist/security/audit-extra.async.js +601 -0
  219. package/dist/security/audit-extra.js +2 -830
  220. package/dist/security/audit-extra.sync.js +505 -0
  221. package/dist/security/channel-metadata.js +34 -0
  222. package/dist/security/external-content.js +88 -6
  223. package/dist/security/skill-scanner.js +330 -0
  224. package/dist/sessions/session-key-utils.js +7 -0
  225. package/dist/signal/monitor/event-handler.js +80 -1
  226. package/dist/slack/monitor/media.js +85 -15
  227. package/dist/tailscale/detect.js +1 -2
  228. package/dist/telegram/bot/helpers.js +109 -28
  229. package/dist/telegram/bot-handlers.js +144 -3
  230. package/dist/telegram/bot-message-context.js +37 -10
  231. package/dist/telegram/bot-message-dispatch.js +54 -17
  232. package/dist/telegram/bot-native-commands.js +86 -29
  233. package/dist/telegram/bot.js +30 -29
  234. package/dist/telegram/model-buttons.js +163 -0
  235. package/dist/telegram/monitor.js +110 -85
  236. package/dist/telegram/send.js +129 -47
  237. package/dist/terminal/restore.js +45 -0
  238. package/dist/test-helpers/state-dir-env.js +16 -0
  239. package/dist/tts/tts.js +12 -6
  240. package/dist/tui/tui-session-actions.js +166 -54
  241. package/dist/utils/fetch-timeout.js +20 -0
  242. package/dist/utils/normalize-secret-input.js +19 -0
  243. package/dist/utils/transcript-tools.js +58 -0
  244. package/dist/utils.js +45 -14
  245. package/dist/version.js +42 -5
  246. package/dist/wizard/clack-prompter.js +9 -6
  247. package/extensions/googlechat/node_modules/.bin/poolbot +21 -0
  248. package/extensions/googlechat/package.json +2 -2
  249. package/extensions/line/node_modules/.bin/poolbot +21 -0
  250. package/extensions/line/package.json +1 -1
  251. package/extensions/matrix/node_modules/.bin/poolbot +21 -0
  252. package/extensions/matrix/package.json +1 -1
  253. package/extensions/memory-core/node_modules/.bin/poolbot +21 -0
  254. package/extensions/memory-core/package.json +4 -1
  255. package/extensions/twitch/node_modules/.bin/poolbot +21 -0
  256. package/extensions/twitch/package.json +1 -1
  257. package/package.json +183 -24
  258. package/dist/control-ui/assets/index-CmNMuoem.js.map +0 -1
@@ -1,26 +1,32 @@
1
1
  import { confirm, isCancel, select, spinner } from "@clack/prompts";
2
+ import { spawnSync } from "node:child_process";
2
3
  import fs from "node:fs/promises";
3
4
  import os from "node:os";
4
5
  import path from "node:path";
6
+ import { doctorCommand } from "../commands/doctor.js";
7
+ import { formatUpdateAvailableHint, formatUpdateOneLiner, resolveUpdateAvailability, } from "../commands/status.update.js";
5
8
  import { readConfigFileSnapshot, writeConfigFile } from "../config/config.js";
9
+ import { resolveStateDir } from "../config/paths.js";
10
+ import { formatDurationPrecise } from "../infra/format-time/format-duration.js";
6
11
  import { resolvePoolBotPackageRoot } from "../infra/poolbot-root.js";
7
- import { checkUpdateStatus, compareSemverStrings, fetchNpmTagVersion, resolveNpmChannelTag, } from "../infra/update-check.js";
12
+ import { trimLogTail } from "../infra/restart-sentinel.js";
8
13
  import { parseSemver } from "../infra/runtime-guard.js";
9
- import { runGatewayUpdate, } from "../infra/update-runner.js";
10
- import { detectGlobalInstallManagerByPresence, detectGlobalInstallManagerForRoot, globalInstallArgs, resolveGlobalPackageRoot, } from "../infra/update-global.js";
11
14
  import { channelToNpmTag, DEFAULT_GIT_CHANNEL, DEFAULT_PACKAGE_CHANNEL, formatUpdateChannelLabel, normalizeUpdateChannel, resolveEffectiveUpdateChannel, } from "../infra/update-channels.js";
12
- import { trimLogTail } from "../infra/restart-sentinel.js";
15
+ import { checkUpdateStatus, compareSemverStrings, fetchNpmTagVersion, resolveNpmChannelTag, } from "../infra/update-check.js";
16
+ import { detectGlobalInstallManagerByPresence, detectGlobalInstallManagerForRoot, cleanupGlobalRenameDirs, globalInstallArgs, resolveGlobalPackageRoot, } from "../infra/update-global.js";
17
+ import { runGatewayUpdate, } from "../infra/update-runner.js";
18
+ import { syncPluginsForUpdateChannel, updateNpmInstalledPlugins } from "../plugins/update.js";
19
+ import { runCommandWithTimeout } from "../process/exec.js";
13
20
  import { defaultRuntime } from "../runtime.js";
14
21
  import { formatDocsLink } from "../terminal/links.js";
15
- import { formatCliCommand } from "./command-format.js";
16
- import { replaceCliName, resolveCliName } from "./cli-name.js";
17
22
  import { stylePromptHint, stylePromptMessage } from "../terminal/prompt-style.js";
18
- import { theme } from "../terminal/theme.js";
19
23
  import { renderTable } from "../terminal/table.js";
24
+ import { theme } from "../terminal/theme.js";
25
+ import { pathExists } from "../utils.js";
26
+ import { replaceCliName, resolveCliName } from "./cli-name.js";
27
+ import { formatCliCommand } from "./command-format.js";
28
+ import { runDaemonRestart } from "./daemon-cli.js";
20
29
  import { formatHelpExamples } from "./help-format.js";
21
- import { formatUpdateAvailableHint, formatUpdateOneLiner, resolveUpdateAvailability, } from "../commands/status.update.js";
22
- import { syncPluginsForUpdateChannel, updateNpmInstalledPlugins } from "../plugins/update.js";
23
- import { runCommandWithTimeout } from "../process/exec.js";
24
30
  const STEP_LABELS = {
25
31
  "clean check": "Working directory is clean",
26
32
  "upstream check": "Upstream branch exists",
@@ -33,7 +39,10 @@ const STEP_LABELS = {
33
39
  "preflight cleanup": "Cleaning preflight worktree",
34
40
  "deps install": "Installing dependencies",
35
41
  build: "Building",
36
- "ui:build": "Building UI",
42
+ "ui:build": "Building UI assets",
43
+ "ui:build (post-doctor repair)": "Restoring missing UI assets",
44
+ "ui assets verify": "Validating UI assets",
45
+ "poolbot doctor entry": "Checking doctor entrypoint",
37
46
  "poolbot doctor": "Running doctor checks",
38
47
  "git rev-parse HEAD (after)": "Verifying update",
39
48
  "global update": "Updating via package manager",
@@ -63,18 +72,20 @@ const UPDATE_QUIPS = [
63
72
  ];
64
73
  const MAX_LOG_CHARS = 8000;
65
74
  const DEFAULT_PACKAGE_NAME = "poolbot";
66
- const CORE_PACKAGE_NAMES = new Set([DEFAULT_PACKAGE_NAME, "poolbot"]);
75
+ const CORE_PACKAGE_NAMES = new Set([DEFAULT_PACKAGE_NAME]);
67
76
  const CLI_NAME = resolveCliName();
68
77
  const CLAWDBOT_REPO_URL = "https://github.com/poolbot/poolbot.git";
69
- const DEFAULT_GIT_DIR = path.join(os.homedir(), "poolbot");
70
78
  function normalizeTag(value) {
71
- if (!value)
79
+ if (!value) {
72
80
  return null;
81
+ }
73
82
  const trimmed = value.trim();
74
- if (!trimmed)
83
+ if (!trimmed) {
75
84
  return null;
76
- if (trimmed.startsWith("poolbot@"))
85
+ }
86
+ if (trimmed.startsWith("poolbot@")) {
77
87
  return trimmed.slice("poolbot@".length);
88
+ }
78
89
  if (trimmed.startsWith(`${DEFAULT_PACKAGE_NAME}@`)) {
79
90
  return trimmed.slice(`${DEFAULT_PACKAGE_NAME}@`.length);
80
91
  }
@@ -85,8 +96,9 @@ function pickUpdateQuip() {
85
96
  }
86
97
  function normalizeVersionTag(tag) {
87
98
  const trimmed = tag.trim();
88
- if (!trimmed)
99
+ if (!trimmed) {
89
100
  return null;
101
+ }
90
102
  const cleaned = trimmed.startsWith("v") ? trimmed.slice(1) : trimmed;
91
103
  return parseSemver(cleaned) ? cleaned : null;
92
104
  }
@@ -102,8 +114,9 @@ async function readPackageVersion(root) {
102
114
  }
103
115
  async function resolveTargetVersion(tag, timeoutMs) {
104
116
  const direct = normalizeVersionTag(tag);
105
- if (direct)
117
+ if (direct) {
106
118
  return direct;
119
+ }
107
120
  const res = await fetchNpmTagVersion({ tag, timeoutMs });
108
121
  return res.version ?? null;
109
122
  }
@@ -131,13 +144,100 @@ async function isCorePackage(root) {
131
144
  const name = await readPackageName(root);
132
145
  return Boolean(name && CORE_PACKAGE_NAMES.has(name));
133
146
  }
134
- async function pathExists(targetPath) {
147
+ async function tryWriteCompletionCache(root, jsonMode) {
135
148
  try {
136
- await fs.stat(targetPath);
137
- return true;
149
+ const binPath = path.join(root, "poolbot.mjs");
150
+ if (!(await pathExists(binPath))) {
151
+ return;
152
+ }
153
+ const result = spawnSync(resolveNodeRunner(), [binPath, "completion", "--write-state"], {
154
+ cwd: root,
155
+ env: process.env,
156
+ encoding: "utf-8",
157
+ });
158
+ if (result.error) {
159
+ if (!jsonMode) {
160
+ defaultRuntime.log(theme.warn(`Completion cache update failed: ${String(result.error)}`));
161
+ }
162
+ return;
163
+ }
164
+ if (result.status !== 0 && !jsonMode) {
165
+ const stderr = (result.stderr ?? "").toString().trim();
166
+ const detail = stderr ? ` (${stderr})` : "";
167
+ defaultRuntime.log(theme.warn(`Completion cache update failed${detail}.`));
168
+ }
138
169
  }
139
170
  catch {
140
- return false;
171
+ // completion-cli module may not exist yet; silently ignore
172
+ }
173
+ }
174
+ /** Check if shell completion is installed and prompt user to install if not. */
175
+ async function tryInstallShellCompletion(opts) {
176
+ if (opts.jsonMode || !process.stdin.isTTY) {
177
+ return;
178
+ }
179
+ try {
180
+ const { checkShellCompletionStatus, ensureCompletionCacheExists } =
181
+ // @ts-expect-error module may not exist yet
182
+ await import("../commands/doctor-completion.js");
183
+ const status = await checkShellCompletionStatus(CLI_NAME);
184
+ // Profile uses slow dynamic pattern - upgrade to cached version
185
+ if (status.usesSlowPattern) {
186
+ defaultRuntime.log(theme.muted("Upgrading shell completion to cached version..."));
187
+ // Ensure cache exists first
188
+ const cacheGenerated = await ensureCompletionCacheExists(CLI_NAME);
189
+ if (cacheGenerated) {
190
+ try {
191
+ const { installCompletion } =
192
+ // @ts-expect-error module may not exist yet
193
+ await import("./completion-cli.js");
194
+ await installCompletion(status.shell, true, CLI_NAME);
195
+ }
196
+ catch {
197
+ // completion-cli module may not exist yet
198
+ }
199
+ }
200
+ return;
201
+ }
202
+ // Profile has completion but no cache - auto-fix silently
203
+ if (status.profileInstalled && !status.cacheExists) {
204
+ defaultRuntime.log(theme.muted("Regenerating shell completion cache..."));
205
+ await ensureCompletionCacheExists(CLI_NAME);
206
+ return;
207
+ }
208
+ // No completion at all - prompt to install
209
+ if (!status.profileInstalled) {
210
+ defaultRuntime.log("");
211
+ defaultRuntime.log(theme.heading("Shell completion"));
212
+ const shouldInstall = await confirm({
213
+ message: stylePromptMessage(`Enable ${status.shell} shell completion for ${CLI_NAME}?`),
214
+ initialValue: true,
215
+ });
216
+ if (isCancel(shouldInstall) || !shouldInstall) {
217
+ if (!opts.skipPrompt) {
218
+ defaultRuntime.log(theme.muted(`Skipped. Run \`${replaceCliName(formatCliCommand("poolbot completion --install"), CLI_NAME)}\` later to enable.`));
219
+ }
220
+ return;
221
+ }
222
+ // Generate cache first (required for fast shell startup)
223
+ const cacheGenerated = await ensureCompletionCacheExists(CLI_NAME);
224
+ if (!cacheGenerated) {
225
+ defaultRuntime.log(theme.warn("Failed to generate completion cache."));
226
+ return;
227
+ }
228
+ try {
229
+ const { installCompletion } =
230
+ // @ts-expect-error module may not exist yet
231
+ await import("./completion-cli.js");
232
+ await installCompletion(status.shell, opts.skipPrompt, CLI_NAME);
233
+ }
234
+ catch {
235
+ // completion-cli module may not exist yet
236
+ }
237
+ }
238
+ }
239
+ catch {
240
+ // doctor-completion module may not exist yet; silently ignore
141
241
  }
142
242
  }
143
243
  async function isEmptyDir(targetPath) {
@@ -151,14 +251,19 @@ async function isEmptyDir(targetPath) {
151
251
  }
152
252
  function resolveGitInstallDir() {
153
253
  const override = process.env.CLAWDBOT_GIT_DIR?.trim();
154
- if (override)
254
+ if (override) {
155
255
  return path.resolve(override);
156
- return DEFAULT_GIT_DIR;
256
+ }
257
+ return resolveDefaultGitDir();
258
+ }
259
+ function resolveDefaultGitDir() {
260
+ return resolveStateDir(process.env, os.homedir);
157
261
  }
158
262
  function resolveNodeRunner() {
159
263
  const base = path.basename(process.execPath).toLowerCase();
160
- if (base === "node" || base === "node.exe")
264
+ if (base === "node" || base === "node.exe") {
161
265
  return process.execPath;
266
+ }
162
267
  return "node";
163
268
  }
164
269
  async function runUpdateStep(params) {
@@ -230,8 +335,9 @@ async function resolveGlobalManager(params) {
230
335
  };
231
336
  if (params.installKind === "package") {
232
337
  const detected = await detectGlobalInstallManagerForRoot(runCommand, params.root, params.timeoutMs);
233
- if (detected)
338
+ if (detected) {
234
339
  return detected;
340
+ }
235
341
  }
236
342
  const byPresence = await detectGlobalInstallManagerByPresence(runCommand, params.timeoutMs);
237
343
  return byPresence ?? "npm";
@@ -317,7 +423,7 @@ export async function updateStatusCommand(opts) {
317
423
  Value: updateAvailability.available ? theme.warn(`available · ${updateLine}`) : updateLine,
318
424
  },
319
425
  ];
320
- defaultRuntime.log(theme.heading("Poolbot update status"));
426
+ defaultRuntime.log(theme.heading("PoolBot update status"));
321
427
  defaultRuntime.log("");
322
428
  defaultRuntime.log(renderTable({
323
429
  width: tableWidth,
@@ -350,10 +456,11 @@ function createUpdateProgress(enabled) {
350
456
  currentSpinner.start(theme.accent(getStepLabel(step)));
351
457
  },
352
458
  onStepComplete: (step) => {
353
- if (!currentSpinner)
459
+ if (!currentSpinner) {
354
460
  return;
461
+ }
355
462
  const label = getStepLabel(step);
356
- const duration = theme.muted(`(${formatDuration(step.durationMs)})`);
463
+ const duration = theme.muted(`(${formatDurationPrecise(step.durationMs)})`);
357
464
  const icon = step.exitCode === 0 ? theme.success("\u2713") : theme.error("\u2717");
358
465
  currentSpinner.stop(`${icon} ${label} ${duration}`);
359
466
  currentSpinner = null;
@@ -377,17 +484,13 @@ function createUpdateProgress(enabled) {
377
484
  },
378
485
  };
379
486
  }
380
- function formatDuration(ms) {
381
- if (ms < 1000)
382
- return `${ms}ms`;
383
- const seconds = (ms / 1000).toFixed(1);
384
- return `${seconds}s`;
385
- }
386
487
  function formatStepStatus(exitCode) {
387
- if (exitCode === 0)
488
+ if (exitCode === 0) {
388
489
  return theme.success("\u2713");
389
- if (exitCode === null)
490
+ }
491
+ if (exitCode === null) {
390
492
  return theme.warn("?");
493
+ }
391
494
  return theme.error("\u2717");
392
495
  }
393
496
  const selectStyled = (params) => select({
@@ -422,7 +525,7 @@ function printResult(result, opts) {
422
525
  defaultRuntime.log(theme.heading("Steps:"));
423
526
  for (const step of result.steps) {
424
527
  const status = formatStepStatus(step.exitCode);
425
- const duration = theme.muted(`(${formatDuration(step.durationMs)})`);
528
+ const duration = theme.muted(`(${formatDurationPrecise(step.durationMs)})`);
426
529
  defaultRuntime.log(` ${status} ${step.name} ${duration}`);
427
530
  if (step.exitCode !== 0 && step.stderrTail) {
428
531
  const lines = step.stderrTail.split("\n").slice(0, 5);
@@ -435,7 +538,7 @@ function printResult(result, opts) {
435
538
  }
436
539
  }
437
540
  defaultRuntime.log("");
438
- defaultRuntime.log(`Total time: ${theme.muted(formatDuration(result.durationMs))}`);
541
+ defaultRuntime.log(`Total time: ${theme.muted(formatDurationPrecise(result.durationMs))}`);
439
542
  }
440
543
  export async function updateCommand(opts) {
441
544
  process.noDeprecation = true;
@@ -512,7 +615,7 @@ export async function updateCommand(opts) {
512
615
  message: stylePromptMessage(message),
513
616
  initialValue: false,
514
617
  });
515
- if (isCancel(ok) || ok === false) {
618
+ if (isCancel(ok) || !ok) {
516
619
  if (!opts.json) {
517
620
  defaultRuntime.log(theme.muted("Update cancelled."));
518
621
  }
@@ -540,7 +643,7 @@ export async function updateCommand(opts) {
540
643
  }
541
644
  const showProgress = !opts.json && process.stdout.isTTY;
542
645
  if (!opts.json) {
543
- defaultRuntime.log(theme.heading("Updating Poolbot..."));
646
+ defaultRuntime.log(theme.heading("Updating PoolBot..."));
544
647
  defaultRuntime.log("");
545
648
  }
546
649
  const { progress, stop } = createUpdateProgress(showProgress);
@@ -560,6 +663,12 @@ export async function updateCommand(opts) {
560
663
  const packageName = (pkgRoot ? await readPackageName(pkgRoot) : await readPackageName(root)) ??
561
664
  DEFAULT_PACKAGE_NAME;
562
665
  const beforeVersion = pkgRoot ? await readPackageVersion(pkgRoot) : null;
666
+ if (pkgRoot) {
667
+ await cleanupGlobalRenameDirs({
668
+ globalRoot: path.dirname(pkgRoot),
669
+ packageName,
670
+ });
671
+ }
563
672
  const updateStep = await runUpdateStep({
564
673
  name: "global update",
565
674
  argv: globalInstallArgs(manager, `${packageName}@${tag}`),
@@ -666,7 +775,7 @@ export async function updateCommand(opts) {
666
775
  defaultRuntime.log(theme.warn("Skipped: working directory has uncommitted changes. Commit or stash them first."));
667
776
  }
668
777
  if (result.reason === "not-git-install") {
669
- defaultRuntime.log(theme.warn(`Skipped: this Poolbot install isn't a git checkout, and the package manager couldn't be detected. Update via your package manager, then run \`${replaceCliName(formatCliCommand("poolbot doctor"), CLI_NAME)}\` and \`${replaceCliName(formatCliCommand("poolbot gateway restart"), CLI_NAME)}\`.`));
778
+ defaultRuntime.log(theme.warn(`Skipped: this PoolBot install isn't a git checkout, and the package manager couldn't be detected. Update via your package manager, then run \`${replaceCliName(formatCliCommand("poolbot doctor"), CLI_NAME)}\` and \`${replaceCliName(formatCliCommand("poolbot gateway restart"), CLI_NAME)}\`.`));
670
779
  defaultRuntime.log(theme.muted(`Examples: \`${replaceCliName("npm i -g poolbot@latest", CLI_NAME)}\` or \`${replaceCliName("pnpm add -g poolbot@latest", CLI_NAME)}\``));
671
780
  }
672
781
  defaultRuntime.exit(0);
@@ -702,8 +811,9 @@ export async function updateCommand(opts) {
702
811
  }
703
812
  if (!opts.json) {
704
813
  const summarizeList = (list) => {
705
- if (list.length <= 6)
814
+ if (list.length <= 6) {
706
815
  return list.join(", ");
816
+ }
707
817
  return `${list.slice(0, 6).join(", ")} +${list.length - 6} more`;
708
818
  };
709
819
  if (syncResult.summary.switchedToBundled.length > 0) {
@@ -727,15 +837,18 @@ export async function updateCommand(opts) {
727
837
  }
728
838
  else {
729
839
  const parts = [`${updated} updated`, `${unchanged} unchanged`];
730
- if (failed > 0)
840
+ if (failed > 0) {
731
841
  parts.push(`${failed} failed`);
732
- if (skipped > 0)
842
+ }
843
+ if (skipped > 0) {
733
844
  parts.push(`${skipped} skipped`);
845
+ }
734
846
  defaultRuntime.log(theme.muted(`npm plugins: ${parts.join(", ")}.`));
735
847
  }
736
848
  for (const outcome of npmResult.outcomes) {
737
- if (outcome.status !== "error")
849
+ if (outcome.status !== "error") {
738
850
  continue;
851
+ }
739
852
  defaultRuntime.log(theme.error(outcome.message));
740
853
  }
741
854
  }
@@ -743,6 +856,12 @@ export async function updateCommand(opts) {
743
856
  else if (!opts.json) {
744
857
  defaultRuntime.log(theme.warn("Skipping plugin updates: config is invalid."));
745
858
  }
859
+ await tryWriteCompletionCache(root, Boolean(opts.json));
860
+ // Offer to install shell completion if not already installed
861
+ await tryInstallShellCompletion({
862
+ jsonMode: Boolean(opts.json),
863
+ skipPrompt: Boolean(opts.yes),
864
+ });
746
865
  // Restart service if requested
747
866
  if (shouldRestart) {
748
867
  if (!opts.json) {
@@ -750,16 +869,16 @@ export async function updateCommand(opts) {
750
869
  defaultRuntime.log(theme.heading("Restarting service..."));
751
870
  }
752
871
  try {
753
- const { runDaemonRestart } = await import("./daemon-cli.js");
754
872
  const restarted = await runDaemonRestart();
755
873
  if (!opts.json && restarted) {
756
874
  defaultRuntime.log(theme.success("Daemon restarted successfully."));
757
875
  defaultRuntime.log("");
758
876
  process.env.CLAWDBOT_UPDATE_IN_PROGRESS = "1";
759
877
  try {
760
- const { doctorCommand } = await import("../commands/doctor.js");
761
878
  const interactiveDoctor = Boolean(process.stdin.isTTY) && !opts.json && opts.yes !== true;
762
- await doctorCommand(defaultRuntime, { nonInteractive: !interactiveDoctor });
879
+ await doctorCommand(defaultRuntime, {
880
+ nonInteractive: !interactiveDoctor,
881
+ });
763
882
  }
764
883
  catch (err) {
765
884
  defaultRuntime.log(theme.warn(`Doctor failed: ${String(err)}`));
@@ -880,7 +999,7 @@ export async function updateWizardCommand(opts = {}) {
880
999
  message: stylePromptMessage(`Create a git checkout at ${gitDir}? (override via CLAWDBOT_GIT_DIR)`),
881
1000
  initialValue: true,
882
1001
  });
883
- if (isCancel(ok) || ok === false) {
1002
+ if (isCancel(ok) || !ok) {
884
1003
  defaultRuntime.log(theme.muted("Update cancelled."));
885
1004
  defaultRuntime.exit(0);
886
1005
  return;
@@ -911,7 +1030,7 @@ export async function updateWizardCommand(opts = {}) {
911
1030
  export function registerUpdateCli(program) {
912
1031
  const update = program
913
1032
  .command("update")
914
- .description("Update Poolbot to the latest version")
1033
+ .description("Update PoolBot to the latest version")
915
1034
  .option("--json", "Output result as JSON", false)
916
1035
  .option("--no-restart", "Skip restarting the gateway service after a successful update")
917
1036
  .option("--channel <stable|beta|dev>", "Persist update channel (git + npm)")
@@ -981,7 +1100,9 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/update", "docs.molt.bot/cli/updat
981
1100
  .addHelpText("after", `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/update", "docs.molt.bot/cli/update")}\n`)
982
1101
  .action(async (opts) => {
983
1102
  try {
984
- await updateWizardCommand({ timeout: opts.timeout });
1103
+ await updateWizardCommand({
1104
+ timeout: opts.timeout,
1105
+ });
985
1106
  }
986
1107
  catch (err) {
987
1108
  defaultRuntime.error(String(err));
@@ -342,6 +342,7 @@ export async function agentCommand(opts, runtime = defaultRuntime, deps = create
342
342
  currentThreadTs: runContext.currentThreadTs,
343
343
  replyToMode: runContext.replyToMode,
344
344
  hasRepliedRef: runContext.hasRepliedRef,
345
+ senderIsOwner: true,
345
346
  sessionFile,
346
347
  workspaceDir,
347
348
  config: cfg,
@@ -53,7 +53,7 @@ export async function applyAuthChoiceOAuth(params) {
53
53
  onProgress: (msg) => spin.update(msg),
54
54
  });
55
55
  spin.stop("Chutes OAuth complete");
56
- const email = creds.email?.trim() || "default";
56
+ const email = typeof creds.email === "string" && creds.email.trim() ? creds.email.trim() : "default";
57
57
  const profileId = `chutes:${email}`;
58
58
  await writeOAuthCredentials("chutes", creds, params.agentDir);
59
59
  nextConfig = applyAuthProfileConfig(nextConfig, {
@@ -1,11 +1,12 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { formatCliCommand } from "../cli/command-format.js";
1
4
  import { PoolBotSchema, CONFIG_PATH, migrateLegacyConfig, readConfigFileSnapshot, } from "../config/config.js";
2
5
  import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js";
3
- import { formatCliCommand } from "../cli/command-format.js";
4
6
  import { note } from "../terminal/note.js";
7
+ import { isRecord, resolveHomeDir } from "../utils.js";
5
8
  import { normalizeLegacyConfigValues } from "./doctor-legacy-config.js";
6
- function isRecord(value) {
7
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
8
- }
9
+ import { autoMigrateLegacyStateDir } from "./doctor-state-migrations.js";
9
10
  function normalizeIssuePath(path) {
10
11
  return path.filter((part) => typeof part !== "symbol");
11
12
  }
@@ -96,9 +97,64 @@ function noteOpencodeProviderOverrides(cfg) {
96
97
  lines.push("- Remove these entries to restore per-model API routing + costs (then re-run onboarding if needed).");
97
98
  note(lines.join("\n"), "OpenCode Zen");
98
99
  }
100
+ async function maybeMigrateLegacyConfig() {
101
+ const changes = [];
102
+ const home = resolveHomeDir();
103
+ if (!home) {
104
+ return changes;
105
+ }
106
+ const targetDir = path.join(home, ".poolbot");
107
+ const targetPath = path.join(targetDir, "poolbot.json");
108
+ try {
109
+ await fs.access(targetPath);
110
+ return changes;
111
+ }
112
+ catch {
113
+ // missing config
114
+ }
115
+ const legacyCandidates = [
116
+ path.join(home, ".clawdbot", "clawdbot.json"),
117
+ path.join(home, ".moltbot", "moltbot.json"),
118
+ path.join(home, ".moldbot", "moldbot.json"),
119
+ ];
120
+ let legacyPath = null;
121
+ for (const candidate of legacyCandidates) {
122
+ try {
123
+ await fs.access(candidate);
124
+ legacyPath = candidate;
125
+ break;
126
+ }
127
+ catch {
128
+ // continue
129
+ }
130
+ }
131
+ if (!legacyPath) {
132
+ return changes;
133
+ }
134
+ await fs.mkdir(targetDir, { recursive: true });
135
+ try {
136
+ await fs.copyFile(legacyPath, targetPath, fs.constants.COPYFILE_EXCL);
137
+ changes.push(`Migrated legacy config: ${legacyPath} -> ${targetPath}`);
138
+ }
139
+ catch {
140
+ // If it already exists, skip silently.
141
+ }
142
+ return changes;
143
+ }
99
144
  export async function loadAndMaybeMigrateDoctorConfig(params) {
100
145
  const shouldRepair = params.options.repair === true || params.options.yes === true;
101
- const snapshot = await readConfigFileSnapshot();
146
+ const stateDirResult = await autoMigrateLegacyStateDir({ env: process.env });
147
+ if (stateDirResult.changes.length > 0) {
148
+ note(stateDirResult.changes.map((entry) => `- ${entry}`).join("\n"), "Doctor changes");
149
+ }
150
+ if (stateDirResult.warnings.length > 0) {
151
+ note(stateDirResult.warnings.map((entry) => `- ${entry}`).join("\n"), "Doctor warnings");
152
+ }
153
+ const legacyConfigChanges = await maybeMigrateLegacyConfig();
154
+ if (legacyConfigChanges.length > 0) {
155
+ note(legacyConfigChanges.map((entry) => `- ${entry}`).join("\n"), "Doctor changes");
156
+ }
157
+ let snapshot = await readConfigFileSnapshot();
102
158
  const baseCfg = snapshot.config ?? {};
103
159
  let cfg = baseCfg;
104
160
  let candidate = structuredClone(baseCfg);
@@ -1 +1 @@
1
- export { autoMigrateLegacyAgentDir, autoMigrateLegacyState, detectLegacyStateMigrations, migrateLegacyAgentDir, resetAutoMigrateLegacyAgentDirForTest, resetAutoMigrateLegacyStateForTest, runLegacyStateMigrations, } from "../infra/state-migrations.js";
1
+ export { autoMigrateLegacyAgentDir, autoMigrateLegacyState, autoMigrateLegacyStateDir, detectLegacyStateMigrations, migrateLegacyAgentDir, resetAutoMigrateLegacyAgentDirForTest, resetAutoMigrateLegacyStateForTest, runLegacyStateMigrations, } from "../infra/state-migrations.js";
@@ -73,7 +73,7 @@ const buildSessionSummary = (storePath) => {
73
73
  const sessions = Object.entries(store)
74
74
  .filter(([key]) => key !== "global" && key !== "unknown")
75
75
  .map(([key, entry]) => ({ key, updatedAt: entry?.updatedAt ?? 0 }))
76
- .sort((a, b) => b.updatedAt - a.updatedAt);
76
+ .toSorted((a, b) => b.updatedAt - a.updatedAt);
77
77
  const recent = sessions.slice(0, 5).map((s) => ({
78
78
  key: s.key,
79
79
  updatedAt: s.updatedAt || null,
@@ -0,0 +1,29 @@
1
+ import { DEFAULT_PROVIDER } from "../agents/defaults.js";
2
+ import { resolveAllowlistModelKey } from "../agents/model-selection.js";
3
+ export function ensureModelAllowlistEntry(params) {
4
+ const rawModelRef = params.modelRef.trim();
5
+ if (!rawModelRef) {
6
+ return params.cfg;
7
+ }
8
+ const models = { ...params.cfg.agents?.defaults?.models };
9
+ const keySet = new Set([rawModelRef]);
10
+ const canonicalKey = resolveAllowlistModelKey(rawModelRef, params.defaultProvider ?? DEFAULT_PROVIDER);
11
+ if (canonicalKey) {
12
+ keySet.add(canonicalKey);
13
+ }
14
+ for (const key of keySet) {
15
+ models[key] = {
16
+ ...models[key],
17
+ };
18
+ }
19
+ return {
20
+ ...params.cfg,
21
+ agents: {
22
+ ...params.cfg.agents,
23
+ defaults: {
24
+ ...params.cfg.agents?.defaults,
25
+ models,
26
+ },
27
+ },
28
+ };
29
+ }
@@ -4,6 +4,7 @@ import { getCustomProviderApiKey, resolveEnvApiKey } from "../agents/model-auth.
4
4
  import { loadModelCatalog } from "../agents/model-catalog.js";
5
5
  import { buildAllowedModelSet, buildModelAliasIndex, modelKey, normalizeProviderId, resolveConfiguredModelRef, } from "../agents/model-selection.js";
6
6
  import { formatTokenK } from "./models/shared.js";
7
+ import { OPENAI_CODEX_DEFAULT_MODEL } from "./openai-codex-model-default.js";
7
8
  const KEEP_VALUE = "__keep__";
8
9
  const MANUAL_VALUE = "__manual__";
9
10
  const PROVIDER_FILTER_THRESHOLD = 30;
@@ -238,7 +239,7 @@ export async function promptModelAllowlist(params) {
238
239
  message: params.message ??
239
240
  "Allowlist models (comma-separated provider/model; blank to keep current)",
240
241
  initialValue: existingKeys.join(", "),
241
- placeholder: "openai-codex/gpt-5.2, anthropic/claude-opus-4-5",
242
+ placeholder: `${OPENAI_CODEX_DEFAULT_MODEL}, anthropic/claude-opus-4-6`,
242
243
  });
243
244
  const parsed = String(raw ?? "")
244
245
  .split(",")
@@ -1,4 +1,4 @@
1
- import { discoverAuthStorage, discoverModels } from "@mariozechner/pi-coding-agent";
1
+ import { discoverAuthStorage, discoverModels } from "../../agents/pi-model-discovery.js";
2
2
  import { resolvePoolbotAgentDir } from "../../agents/agent-paths.js";
3
3
  import { listProfilesForProvider } from "../../agents/auth-profiles.js";
4
4
  import { getCustomProviderApiKey, resolveAwsSdkEnvVarName, resolveEnvApiKey, } from "../../agents/model-auth.js";