@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,8 +1,9 @@
1
1
  import { normalizeAgentId, normalizeMainKey, parseAgentSessionKey, } from "../routing/session-key.js";
2
2
  import { asString, extractTextFromMessage, isCommandMessage } from "./tui-formatters.js";
3
3
  export function createSessionActions(context) {
4
- const { client, chatLog, tui, opts, state, agentNames, initialSessionInput, initialSessionAgentId, resolveSessionKey, updateHeader, updateFooter, updateAutocompleteProvider, setActivityStatus, } = context;
5
- let refreshSessionInfoPromise = null;
4
+ const { client, chatLog, tui, opts, state, agentNames, initialSessionInput, initialSessionAgentId, resolveSessionKey, updateHeader, updateFooter, updateAutocompleteProvider, setActivityStatus, clearLocalRunIds, } = context;
5
+ let refreshSessionInfoPromise = Promise.resolve();
6
+ let lastSessionDefaults = null;
6
7
  const applyAgentsResult = (result) => {
7
8
  state.agentDefaultId = normalizeAgentId(result.defaultId);
8
9
  state.sessionMainKey = normalizeMainKey(result.mainKey);
@@ -13,8 +14,9 @@ export function createSessionActions(context) {
13
14
  }));
14
15
  agentNames.clear();
15
16
  for (const agent of state.agents) {
16
- if (agent.name)
17
+ if (agent.name) {
17
18
  agentNames.set(agent.id, agent.name);
19
+ }
18
20
  }
19
21
  if (!state.initialSessionApplied) {
20
22
  if (initialSessionAgentId) {
@@ -50,62 +52,161 @@ export function createSessionActions(context) {
50
52
  };
51
53
  const updateAgentFromSessionKey = (key) => {
52
54
  const parsed = parseAgentSessionKey(key);
53
- if (!parsed)
55
+ if (!parsed) {
54
56
  return;
57
+ }
55
58
  const next = normalizeAgentId(parsed.agentId);
56
59
  if (next !== state.currentAgentId) {
57
60
  state.currentAgentId = next;
58
61
  }
59
62
  };
60
- const refreshSessionInfo = async () => {
61
- if (refreshSessionInfoPromise)
62
- return refreshSessionInfoPromise;
63
- refreshSessionInfoPromise = (async () => {
64
- try {
65
- const listAgentId = state.currentSessionKey === "global" || state.currentSessionKey === "unknown"
66
- ? undefined
67
- : state.currentAgentId;
68
- const result = await client.listSessions({
69
- includeGlobal: false,
70
- includeUnknown: false,
71
- agentId: listAgentId,
72
- });
73
- const entry = result.sessions.find((row) => {
74
- // Exact match
75
- if (row.key === state.currentSessionKey)
76
- return true;
77
- // Also match canonical keys like "agent:default:main" against "main"
78
- const parsed = parseAgentSessionKey(row.key);
79
- return parsed?.rest === state.currentSessionKey;
80
- });
81
- state.sessionInfo = {
82
- thinkingLevel: entry?.thinkingLevel,
83
- verboseLevel: entry?.verboseLevel,
84
- reasoningLevel: entry?.reasoningLevel,
85
- model: entry?.model ?? result.defaults?.model ?? undefined,
86
- modelProvider: entry?.modelProvider ?? result.defaults?.modelProvider ?? undefined,
87
- contextTokens: entry?.contextTokens ?? result.defaults?.contextTokens,
88
- inputTokens: entry?.inputTokens ?? null,
89
- outputTokens: entry?.outputTokens ?? null,
90
- totalTokens: entry?.totalTokens ?? null,
91
- responseUsage: entry?.responseUsage,
92
- updatedAt: entry?.updatedAt ?? null,
93
- displayName: entry?.displayName,
94
- };
95
- }
96
- catch (err) {
97
- chatLog.addSystem(`sessions list failed: ${String(err)}`);
98
- }
99
- updateAutocompleteProvider();
100
- updateFooter();
101
- tui.requestRender();
102
- })();
63
+ const resolveModelSelection = (entry) => {
64
+ if (entry?.modelProvider || entry?.model) {
65
+ return {
66
+ modelProvider: entry.modelProvider ?? state.sessionInfo.modelProvider,
67
+ model: entry.model ?? state.sessionInfo.model,
68
+ };
69
+ }
70
+ const overrideModel = entry?.modelOverride?.trim();
71
+ if (overrideModel) {
72
+ const overrideProvider = entry?.providerOverride?.trim() || state.sessionInfo.modelProvider;
73
+ return { modelProvider: overrideProvider, model: overrideModel };
74
+ }
75
+ return {
76
+ modelProvider: state.sessionInfo.modelProvider,
77
+ model: state.sessionInfo.model,
78
+ };
79
+ };
80
+ const applySessionInfo = (params) => {
81
+ const entry = params.entry ?? undefined;
82
+ const defaults = params.defaults ?? lastSessionDefaults ?? undefined;
83
+ const previousDefaults = lastSessionDefaults;
84
+ const defaultsChanged = params.defaults
85
+ ? previousDefaults?.model !== params.defaults.model ||
86
+ previousDefaults?.modelProvider !== params.defaults.modelProvider ||
87
+ previousDefaults?.contextTokens !== params.defaults.contextTokens
88
+ : false;
89
+ if (params.defaults) {
90
+ lastSessionDefaults = params.defaults;
91
+ }
92
+ const entryUpdatedAt = entry?.updatedAt ?? null;
93
+ const currentUpdatedAt = state.sessionInfo.updatedAt ?? null;
94
+ const modelChanged = (entry?.modelProvider !== undefined &&
95
+ entry.modelProvider !== state.sessionInfo.modelProvider) ||
96
+ (entry?.model !== undefined && entry.model !== state.sessionInfo.model);
97
+ if (!params.force &&
98
+ entryUpdatedAt !== null &&
99
+ currentUpdatedAt !== null &&
100
+ entryUpdatedAt < currentUpdatedAt &&
101
+ !defaultsChanged &&
102
+ !modelChanged) {
103
+ return;
104
+ }
105
+ const next = { ...state.sessionInfo };
106
+ if (entry?.thinkingLevel !== undefined) {
107
+ next.thinkingLevel = entry.thinkingLevel;
108
+ }
109
+ if (entry?.verboseLevel !== undefined) {
110
+ next.verboseLevel = entry.verboseLevel;
111
+ }
112
+ if (entry?.reasoningLevel !== undefined) {
113
+ next.reasoningLevel = entry.reasoningLevel;
114
+ }
115
+ if (entry?.responseUsage !== undefined) {
116
+ next.responseUsage = entry.responseUsage;
117
+ }
118
+ if (entry?.inputTokens !== undefined) {
119
+ next.inputTokens = entry.inputTokens;
120
+ }
121
+ if (entry?.outputTokens !== undefined) {
122
+ next.outputTokens = entry.outputTokens;
123
+ }
124
+ if (entry?.totalTokens !== undefined) {
125
+ next.totalTokens = entry.totalTokens;
126
+ }
127
+ if (entry?.contextTokens !== undefined || defaults?.contextTokens !== undefined) {
128
+ next.contextTokens =
129
+ entry?.contextTokens ?? defaults?.contextTokens ?? state.sessionInfo.contextTokens;
130
+ }
131
+ if (entry?.displayName !== undefined) {
132
+ next.displayName = entry.displayName;
133
+ }
134
+ if (entry?.updatedAt !== undefined) {
135
+ next.updatedAt = entry.updatedAt;
136
+ }
137
+ const selection = resolveModelSelection(entry);
138
+ if (selection.modelProvider !== undefined) {
139
+ next.modelProvider = selection.modelProvider;
140
+ }
141
+ if (selection.model !== undefined) {
142
+ next.model = selection.model;
143
+ }
144
+ state.sessionInfo = next;
145
+ updateAutocompleteProvider();
146
+ updateFooter();
147
+ tui.requestRender();
148
+ };
149
+ const runRefreshSessionInfo = async () => {
103
150
  try {
104
- await refreshSessionInfoPromise;
151
+ const resolveListAgentId = () => {
152
+ if (state.currentSessionKey === "global" || state.currentSessionKey === "unknown") {
153
+ return undefined;
154
+ }
155
+ const parsed = parseAgentSessionKey(state.currentSessionKey);
156
+ return parsed?.agentId ? normalizeAgentId(parsed.agentId) : state.currentAgentId;
157
+ };
158
+ const listAgentId = resolveListAgentId();
159
+ const result = await client.listSessions({
160
+ includeGlobal: false,
161
+ includeUnknown: false,
162
+ agentId: listAgentId,
163
+ });
164
+ const normalizeMatchKey = (key) => parseAgentSessionKey(key)?.rest ?? key;
165
+ const currentMatchKey = normalizeMatchKey(state.currentSessionKey);
166
+ const entry = result.sessions.find((row) => {
167
+ // Exact match
168
+ if (row.key === state.currentSessionKey) {
169
+ return true;
170
+ }
171
+ // Also match canonical keys like "agent:default:main" against "main"
172
+ return normalizeMatchKey(row.key) === currentMatchKey;
173
+ });
174
+ if (entry?.key && entry.key !== state.currentSessionKey) {
175
+ updateAgentFromSessionKey(entry.key);
176
+ state.currentSessionKey = entry.key;
177
+ updateHeader();
178
+ }
179
+ applySessionInfo({
180
+ entry,
181
+ defaults: result.defaults,
182
+ });
183
+ }
184
+ catch (err) {
185
+ chatLog.addSystem(`sessions list failed: ${String(err)}`);
186
+ }
187
+ };
188
+ const refreshSessionInfo = async () => {
189
+ refreshSessionInfoPromise = refreshSessionInfoPromise.then(runRefreshSessionInfo, runRefreshSessionInfo);
190
+ await refreshSessionInfoPromise;
191
+ };
192
+ const applySessionInfoFromPatch = (result) => {
193
+ if (!result?.entry) {
194
+ return;
105
195
  }
106
- finally {
107
- refreshSessionInfoPromise = null;
196
+ if (result.key && result.key !== state.currentSessionKey) {
197
+ updateAgentFromSessionKey(result.key);
198
+ state.currentSessionKey = result.key;
199
+ updateHeader();
108
200
  }
201
+ const resolved = result.resolved;
202
+ const entry = resolved && (resolved.modelProvider || resolved.model)
203
+ ? {
204
+ ...result.entry,
205
+ modelProvider: resolved.modelProvider ?? result.entry.modelProvider,
206
+ model: resolved.model ?? result.entry.model,
207
+ }
208
+ : result.entry;
209
+ applySessionInfo({ entry, force: true });
109
210
  };
110
211
  const loadHistory = async () => {
111
212
  try {
@@ -116,33 +217,42 @@ export function createSessionActions(context) {
116
217
  const record = history;
117
218
  state.currentSessionId = typeof record.sessionId === "string" ? record.sessionId : null;
118
219
  state.sessionInfo.thinkingLevel = record.thinkingLevel ?? state.sessionInfo.thinkingLevel;
220
+ state.sessionInfo.verboseLevel = record.verboseLevel ?? state.sessionInfo.verboseLevel;
221
+ const showTools = (state.sessionInfo.verboseLevel ?? "off") !== "off";
119
222
  chatLog.clearAll();
120
223
  chatLog.addSystem(`session ${state.currentSessionKey}`);
121
224
  for (const entry of record.messages ?? []) {
122
- if (!entry || typeof entry !== "object")
225
+ if (!entry || typeof entry !== "object") {
123
226
  continue;
227
+ }
124
228
  const message = entry;
125
229
  if (isCommandMessage(message)) {
126
230
  const text = extractTextFromMessage(message);
127
- if (text)
231
+ if (text) {
128
232
  chatLog.addSystem(text);
233
+ }
129
234
  continue;
130
235
  }
131
236
  if (message.role === "user") {
132
237
  const text = extractTextFromMessage(message);
133
- if (text)
238
+ if (text) {
134
239
  chatLog.addUser(text);
240
+ }
135
241
  continue;
136
242
  }
137
243
  if (message.role === "assistant") {
138
244
  const text = extractTextFromMessage(message, {
139
245
  includeThinking: state.showThinking,
140
246
  });
141
- if (text)
247
+ if (text) {
142
248
  chatLog.finalizeAssistant(text);
249
+ }
143
250
  continue;
144
251
  }
145
252
  if (message.role === "toolResult") {
253
+ if (!showTools) {
254
+ continue;
255
+ }
146
256
  const toolCallId = asString(message.toolCallId, "");
147
257
  const toolName = asString(message.toolName, "tool");
148
258
  const component = chatLog.startTool(toolCallId, toolName, {});
@@ -171,6 +281,7 @@ export function createSessionActions(context) {
171
281
  state.activeChatRunId = null;
172
282
  state.currentSessionId = null;
173
283
  state.historyLoaded = false;
284
+ clearLocalRunIds?.();
174
285
  updateHeader();
175
286
  updateFooter();
176
287
  await loadHistory();
@@ -198,6 +309,7 @@ export function createSessionActions(context) {
198
309
  applyAgentsResult,
199
310
  refreshAgents,
200
311
  refreshSessionInfo,
312
+ applySessionInfoFromPatch,
201
313
  loadHistory,
202
314
  setSession,
203
315
  abortActive,
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Fetch wrapper that adds timeout support via AbortController.
3
+ *
4
+ * @param url - The URL to fetch
5
+ * @param init - RequestInit options (headers, method, body, etc.)
6
+ * @param timeoutMs - Timeout in milliseconds
7
+ * @param fetchFn - The fetch implementation to use (defaults to global fetch)
8
+ * @returns The fetch Response
9
+ * @throws AbortError if the request times out
10
+ */
11
+ export async function fetchWithTimeout(url, init, timeoutMs, fetchFn = fetch) {
12
+ const controller = new AbortController();
13
+ const timer = setTimeout(() => controller.abort(), Math.max(1, timeoutMs));
14
+ try {
15
+ return await fetchFn(url, { ...init, signal: controller.signal });
16
+ }
17
+ finally {
18
+ clearTimeout(timer);
19
+ }
20
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Secret normalization for copy/pasted credentials.
3
+ *
4
+ * Common footgun: line breaks (especially `\r`) embedded in API keys/tokens.
5
+ * We strip line breaks anywhere, then trim whitespace at the ends.
6
+ *
7
+ * Intentionally does NOT remove ordinary spaces inside the string to avoid
8
+ * silently altering "Bearer <token>" style values.
9
+ */
10
+ export function normalizeSecretInput(value) {
11
+ if (typeof value !== "string") {
12
+ return "";
13
+ }
14
+ return value.replace(/[\r\n\u2028\u2029]+/g, "").trim();
15
+ }
16
+ export function normalizeOptionalSecretInput(value) {
17
+ const normalized = normalizeSecretInput(value);
18
+ return normalized ? normalized : undefined;
19
+ }
@@ -0,0 +1,58 @@
1
+ const TOOL_CALL_TYPES = new Set(["tool_use", "toolcall", "tool_call"]);
2
+ const TOOL_RESULT_TYPES = new Set(["tool_result", "tool_result_error"]);
3
+ const normalizeType = (value) => {
4
+ if (typeof value !== "string") {
5
+ return "";
6
+ }
7
+ return value.trim().toLowerCase();
8
+ };
9
+ export const extractToolCallNames = (message) => {
10
+ const names = new Set();
11
+ const toolNameRaw = message.toolName ?? message.tool_name;
12
+ if (typeof toolNameRaw === "string" && toolNameRaw.trim()) {
13
+ names.add(toolNameRaw.trim());
14
+ }
15
+ const content = message.content;
16
+ if (!Array.isArray(content)) {
17
+ return Array.from(names);
18
+ }
19
+ for (const entry of content) {
20
+ if (!entry || typeof entry !== "object") {
21
+ continue;
22
+ }
23
+ const block = entry;
24
+ const type = normalizeType(block.type);
25
+ if (!TOOL_CALL_TYPES.has(type)) {
26
+ continue;
27
+ }
28
+ const name = block.name;
29
+ if (typeof name === "string" && name.trim()) {
30
+ names.add(name.trim());
31
+ }
32
+ }
33
+ return Array.from(names);
34
+ };
35
+ export const hasToolCall = (message) => extractToolCallNames(message).length > 0;
36
+ export const countToolResults = (message) => {
37
+ const content = message.content;
38
+ if (!Array.isArray(content)) {
39
+ return { total: 0, errors: 0 };
40
+ }
41
+ let total = 0;
42
+ let errors = 0;
43
+ for (const entry of content) {
44
+ if (!entry || typeof entry !== "object") {
45
+ continue;
46
+ }
47
+ const block = entry;
48
+ const type = normalizeType(block.type);
49
+ if (!TOOL_RESULT_TYPES.has(type)) {
50
+ continue;
51
+ }
52
+ total += 1;
53
+ if (block.is_error === true) {
54
+ errors += 1;
55
+ }
56
+ }
57
+ return { total, errors };
58
+ };
package/dist/utils.js CHANGED
@@ -3,15 +3,54 @@ import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { resolveOAuthDir } from "./config/paths.js";
5
5
  import { logVerbose, shouldLogVerbose } from "./globals.js";
6
+ import { expandHomePrefix, resolveEffectiveHomeDir, resolveRequiredHomeDir, } from "./infra/home-dir.js";
6
7
  export async function ensureDir(dir) {
7
8
  await fs.promises.mkdir(dir, { recursive: true });
8
9
  }
10
+ /**
11
+ * Check if a file or directory exists at the given path.
12
+ */
13
+ export async function pathExists(targetPath) {
14
+ try {
15
+ await fs.promises.access(targetPath);
16
+ return true;
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
9
22
  export function clampNumber(value, min, max) {
10
23
  return Math.max(min, Math.min(max, value));
11
24
  }
12
25
  export function clampInt(value, min, max) {
13
26
  return clampNumber(Math.floor(value), min, max);
14
27
  }
28
+ /** Alias for clampNumber (shorter, more common name) */
29
+ export const clamp = clampNumber;
30
+ /**
31
+ * Escapes special regex characters in a string so it can be used in a RegExp constructor.
32
+ */
33
+ export function escapeRegExp(value) {
34
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
35
+ }
36
+ /**
37
+ * Safely parse JSON, returning null on error instead of throwing.
38
+ */
39
+ export function safeParseJson(raw) {
40
+ try {
41
+ return JSON.parse(raw);
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ /**
48
+ * Type guard for Record<string, unknown> (less strict than isPlainObject).
49
+ * Accepts any non-null object that isn't an array.
50
+ */
51
+ export function isRecord(value) {
52
+ return typeof value === "object" && value !== null && !Array.isArray(value);
53
+ }
15
54
  export function assertWebChannel(input) {
16
55
  if (input !== "web") {
17
56
  throw new Error("Web channel must be 'web'");
@@ -181,7 +220,11 @@ export function resolveUserPath(input) {
181
220
  if (!trimmed)
182
221
  return trimmed;
183
222
  if (trimmed.startsWith("~")) {
184
- const expanded = trimmed.replace(/^~(?=$|[\\/])/, os.homedir());
223
+ const expanded = expandHomePrefix(trimmed, {
224
+ home: resolveRequiredHomeDir(process.env, os.homedir),
225
+ env: process.env,
226
+ homedir: os.homedir,
227
+ });
185
228
  return path.resolve(expanded);
186
229
  }
187
230
  return path.resolve(trimmed);
@@ -193,19 +236,7 @@ export function resolveConfigDir(env = process.env, homedir = os.homedir) {
193
236
  return path.join(homedir(), ".poolbot");
194
237
  }
195
238
  export function resolveHomeDir() {
196
- const envHome = process.env.HOME?.trim();
197
- if (envHome)
198
- return envHome;
199
- const envProfile = process.env.USERPROFILE?.trim();
200
- if (envProfile)
201
- return envProfile;
202
- try {
203
- const home = os.homedir();
204
- return home?.trim() ? home : undefined;
205
- }
206
- catch {
207
- return undefined;
208
- }
239
+ return resolveEffectiveHomeDir(process.env, os.homedir);
209
240
  }
210
241
  export function shortenHomePath(input) {
211
242
  if (!input)
package/dist/version.js CHANGED
@@ -1,18 +1,55 @@
1
1
  import { createRequire } from "node:module";
2
- function readVersionFromPackageJson() {
2
+ const CORE_PACKAGE_NAME = "poolbot";
3
+ const PACKAGE_JSON_CANDIDATES = [
4
+ "../package.json",
5
+ "../../package.json",
6
+ "../../../package.json",
7
+ "./package.json",
8
+ ];
9
+ const BUILD_INFO_CANDIDATES = [
10
+ "../build-info.json",
11
+ "../../build-info.json",
12
+ "./build-info.json",
13
+ ];
14
+ function readVersionFromJsonCandidates(moduleUrl, candidates, opts = {}) {
3
15
  try {
4
- const require = createRequire(import.meta.url);
5
- const pkg = require("../package.json");
6
- return pkg.version ?? null;
16
+ const require = createRequire(moduleUrl);
17
+ for (const candidate of candidates) {
18
+ try {
19
+ const parsed = require(candidate);
20
+ const version = parsed.version?.trim();
21
+ if (!version)
22
+ continue;
23
+ if (opts.requirePackageName && parsed.name !== CORE_PACKAGE_NAME)
24
+ continue;
25
+ return version;
26
+ }
27
+ catch {
28
+ // ignore missing or unreadable candidate
29
+ }
30
+ }
31
+ return null;
7
32
  }
8
33
  catch {
9
34
  return null;
10
35
  }
11
36
  }
37
+ export function readVersionFromPackageJsonForModuleUrl(moduleUrl) {
38
+ return readVersionFromJsonCandidates(moduleUrl, PACKAGE_JSON_CANDIDATES, {
39
+ requirePackageName: true,
40
+ });
41
+ }
42
+ export function readVersionFromBuildInfoForModuleUrl(moduleUrl) {
43
+ return readVersionFromJsonCandidates(moduleUrl, BUILD_INFO_CANDIDATES);
44
+ }
45
+ export function resolveVersionFromModuleUrl(moduleUrl) {
46
+ return (readVersionFromPackageJsonForModuleUrl(moduleUrl) ||
47
+ readVersionFromBuildInfoForModuleUrl(moduleUrl));
48
+ }
12
49
  // Single source of truth for the current poolbot version.
13
50
  // - Embedded/bundled builds: injected define or env var.
14
51
  // - Dev/npm builds: package.json.
15
52
  export const VERSION = (typeof __CLAWDBOT_VERSION__ === "string" && __CLAWDBOT_VERSION__) ||
16
53
  process.env.CLAWDBOT_BUNDLED_VERSION ||
17
- readVersionFromPackageJson() ||
54
+ resolveVersionFromModuleUrl(import.meta.url) ||
18
55
  "0.0.0";
@@ -38,12 +38,15 @@ export function createClackPrompter() {
38
38
  }),
39
39
  initialValues: params.initialValues,
40
40
  })),
41
- text: async (params) => guardCancel(await text({
42
- message: stylePromptMessage(params.message),
43
- initialValue: params.initialValue,
44
- placeholder: params.placeholder,
45
- validate: params.validate,
46
- })),
41
+ text: async (params) => {
42
+ const validate = params.validate;
43
+ return guardCancel(await text({
44
+ message: stylePromptMessage(params.message),
45
+ initialValue: params.initialValue,
46
+ placeholder: params.placeholder,
47
+ validate: validate ? (value) => validate(value ?? "") : undefined,
48
+ }));
49
+ },
47
50
  confirm: async (params) => guardCancel(await confirm({
48
51
  message: stylePromptMessage(params.message),
49
52
  initialValue: params.initialValue,
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/Users/pool/Documents/GitHub/pool-bot/dist/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules:/Users/pool/Documents/GitHub/node_modules:/Users/pool/Documents/node_modules:/Users/pool/node_modules:/Users/node_modules:/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/pool/Documents/GitHub/pool-bot/dist/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules:/Users/pool/Documents/GitHub/node_modules:/Users/pool/Documents/node_modules:/Users/pool/node_modules:/Users/node_modules:/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../@poolzin/pool-bot/dist/entry.js" "$@"
19
+ else
20
+ exec node "$basedir/../@poolzin/pool-bot/dist/entry.js" "$@"
21
+ fi
@@ -31,9 +31,9 @@
31
31
  "google-auth-library": "^10.5.0"
32
32
  },
33
33
  "devDependencies": {
34
- "poolbot": "workspace:*"
34
+ "@poolzin/pool-bot": "workspace:*"
35
35
  },
36
36
  "peerDependencies": {
37
- "poolbot": ">=2026.1.26"
37
+ "@poolzin/pool-bot": ">=2026.1.26"
38
38
  }
39
39
  }
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/Users/pool/Documents/GitHub/pool-bot/dist/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules:/Users/pool/Documents/GitHub/node_modules:/Users/pool/Documents/node_modules:/Users/pool/node_modules:/Users/node_modules:/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/pool/Documents/GitHub/pool-bot/dist/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules:/Users/pool/Documents/GitHub/node_modules:/Users/pool/Documents/node_modules:/Users/pool/node_modules:/Users/node_modules:/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../@poolzin/pool-bot/dist/entry.js" "$@"
19
+ else
20
+ exec node "$basedir/../@poolzin/pool-bot/dist/entry.js" "$@"
21
+ fi
@@ -24,6 +24,6 @@
24
24
  }
25
25
  },
26
26
  "devDependencies": {
27
- "poolbot": "workspace:*"
27
+ "@poolzin/pool-bot": "workspace:*"
28
28
  }
29
29
  }
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/Users/pool/Documents/GitHub/pool-bot/dist/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules:/Users/pool/Documents/GitHub/node_modules:/Users/pool/Documents/node_modules:/Users/pool/node_modules:/Users/node_modules:/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/pool/Documents/GitHub/pool-bot/dist/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules:/Users/pool/Documents/GitHub/node_modules:/Users/pool/Documents/node_modules:/Users/pool/node_modules:/Users/node_modules:/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../@poolzin/pool-bot/dist/entry.js" "$@"
19
+ else
20
+ exec node "$basedir/../@poolzin/pool-bot/dist/entry.js" "$@"
21
+ fi
@@ -31,6 +31,6 @@
31
31
  "zod": "^4.3.6"
32
32
  },
33
33
  "devDependencies": {
34
- "poolbot": "workspace:*"
34
+ "@poolzin/pool-bot": "workspace:*"
35
35
  }
36
36
  }