@poolzin/pool-bot 2026.2.23 → 2026.2.25

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 (235) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/acp/client.js +207 -18
  3. package/dist/acp/secret-file.js +22 -0
  4. package/dist/agents/agent-scope.js +10 -0
  5. package/dist/agents/bash-process-registry.test-helpers.js +29 -0
  6. package/dist/agents/bash-tools.exec-approval-request.js +20 -0
  7. package/dist/agents/bash-tools.exec-host-gateway.js +230 -0
  8. package/dist/agents/bash-tools.exec-host-node.js +235 -0
  9. package/dist/agents/bash-tools.exec-types.js +1 -0
  10. package/dist/agents/bash-tools.process.js +224 -218
  11. package/dist/agents/content-blocks.js +16 -0
  12. package/dist/agents/model-fallback.js +96 -101
  13. package/dist/agents/models-config.providers.js +299 -182
  14. package/dist/agents/pi-embedded-payloads.js +1 -0
  15. package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
  16. package/dist/agents/skills.test-helpers.js +13 -0
  17. package/dist/agents/stable-stringify.js +12 -0
  18. package/dist/agents/subagent-registry.mocks.shared.js +12 -0
  19. package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
  20. package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
  21. package/dist/agents/tool-policy-shared.js +108 -0
  22. package/dist/agents/tools/browser-tool.js +160 -54
  23. package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
  24. package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
  25. package/dist/agents/tools/image-tool.js +214 -99
  26. package/dist/agents/tools/sessions-history-tool.js +140 -108
  27. package/dist/agents/workspace.js +222 -46
  28. package/dist/auto-reply/commands-registry.js +15 -18
  29. package/dist/auto-reply/fallback-state.js +114 -0
  30. package/dist/auto-reply/model-runtime.js +68 -0
  31. package/dist/auto-reply/reply/agent-runner-execution.js +36 -4
  32. package/dist/auto-reply/reply/agent-runner.js +165 -39
  33. package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
  34. package/dist/browser/config.js +26 -0
  35. package/dist/browser/navigation-guard.js +31 -0
  36. package/dist/browser/routes/agent.act.js +431 -424
  37. package/dist/browser/routes/agent.shared.js +47 -3
  38. package/dist/browser/routes/agent.snapshot.js +122 -116
  39. package/dist/browser/routes/agent.storage.js +303 -297
  40. package/dist/browser/routes/tabs.js +154 -100
  41. package/dist/browser/server-lifecycle.js +37 -0
  42. package/dist/build-info.json +3 -3
  43. package/dist/channels/allow-from.js +25 -0
  44. package/dist/channels/plugins/account-action-gate.js +13 -0
  45. package/dist/channels/plugins/message-actions.js +10 -0
  46. package/dist/channels/telegram/api.js +18 -0
  47. package/dist/cli/argv.js +84 -21
  48. package/dist/cli/banner.js +2 -1
  49. package/dist/cli/exec-approvals-cli.js +92 -124
  50. package/dist/cli/memory-cli.js +158 -61
  51. package/dist/cli/nodes-cli/register.push.js +63 -0
  52. package/dist/cli/nodes-media-utils.js +21 -0
  53. package/dist/cli/plugins-cli.js +245 -61
  54. package/dist/cli/program/build-program.js +3 -1
  55. package/dist/cli/program/command-registry.js +223 -136
  56. package/dist/cli/program/help.js +43 -12
  57. package/dist/cli/route.js +1 -1
  58. package/dist/cli/test-runtime-capture.js +24 -0
  59. package/dist/commands/agent.js +163 -87
  60. package/dist/commands/channels.mock-harness.js +23 -0
  61. package/dist/commands/daemon-install-runtime-warning.js +11 -0
  62. package/dist/commands/onboard-helpers.js +4 -4
  63. package/dist/commands/sessions.test-helpers.js +61 -0
  64. package/dist/compat/legacy-names.js +2 -2
  65. package/dist/config/commands.js +3 -0
  66. package/dist/config/config.js +1 -1
  67. package/dist/config/env-substitution.js +62 -34
  68. package/dist/config/env-vars.js +9 -0
  69. package/dist/config/io.js +571 -171
  70. package/dist/config/merge-patch.js +50 -4
  71. package/dist/config/redact-snapshot.js +404 -76
  72. package/dist/config/schema.js +58 -570
  73. package/dist/config/validation.js +140 -85
  74. package/dist/config/zod-schema.hooks.js +40 -11
  75. package/dist/config/zod-schema.installs.js +20 -0
  76. package/dist/config/zod-schema.js +8 -7
  77. package/dist/control-ui/assets/{index-HRr1grwl.js → index-Dvkl4Xlx.js} +2 -1
  78. package/dist/control-ui/assets/{index-HRr1grwl.js.map → index-Dvkl4Xlx.js.map} +1 -1
  79. package/dist/control-ui/index.html +1 -1
  80. package/dist/daemon/cmd-argv.js +21 -0
  81. package/dist/daemon/cmd-set.js +58 -0
  82. package/dist/daemon/service-types.js +1 -0
  83. package/dist/discord/monitor/exec-approvals.js +357 -162
  84. package/dist/gateway/auth.js +38 -3
  85. package/dist/gateway/call.js +149 -68
  86. package/dist/gateway/canvas-capability.js +75 -0
  87. package/dist/gateway/control-plane-audit.js +28 -0
  88. package/dist/gateway/control-plane-rate-limit.js +53 -0
  89. package/dist/gateway/events.js +1 -0
  90. package/dist/gateway/hooks.js +109 -54
  91. package/dist/gateway/http-common.js +22 -0
  92. package/dist/gateway/method-scopes.js +169 -0
  93. package/dist/gateway/net.js +23 -0
  94. package/dist/gateway/openresponses-http.js +120 -110
  95. package/dist/gateway/probe-auth.js +2 -0
  96. package/dist/gateway/protocol/index.js +3 -2
  97. package/dist/gateway/protocol/schema/protocol-schemas.js +2 -0
  98. package/dist/gateway/protocol/schema/push.js +18 -0
  99. package/dist/gateway/protocol/schema.js +1 -0
  100. package/dist/gateway/server-http.js +236 -52
  101. package/dist/gateway/server-methods/agent.js +162 -24
  102. package/dist/gateway/server-methods/chat.js +461 -130
  103. package/dist/gateway/server-methods/config.js +193 -150
  104. package/dist/gateway/server-methods/nodes.helpers.js +12 -0
  105. package/dist/gateway/server-methods/nodes.js +251 -69
  106. package/dist/gateway/server-methods/push.js +53 -0
  107. package/dist/gateway/server-reload-handlers.js +2 -3
  108. package/dist/gateway/server-runtime-config.js +5 -0
  109. package/dist/gateway/server-runtime-state.js +2 -0
  110. package/dist/gateway/server-ws-runtime.js +1 -0
  111. package/dist/gateway/server.impl.js +296 -139
  112. package/dist/gateway/session-preview.test-helpers.js +11 -0
  113. package/dist/gateway/startup-auth.js +126 -0
  114. package/dist/gateway/test-helpers.agent-results.js +15 -0
  115. package/dist/gateway/test-helpers.mocks.js +37 -14
  116. package/dist/gateway/test-helpers.server.js +161 -77
  117. package/dist/hooks/bundled/session-memory/handler.js +165 -34
  118. package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
  119. package/dist/infra/archive-path.js +49 -0
  120. package/dist/infra/device-pairing.js +148 -167
  121. package/dist/infra/exec-approvals-allowlist.js +19 -70
  122. package/dist/infra/exec-approvals-analysis.js +44 -17
  123. package/dist/infra/exec-safe-bin-policy.js +269 -0
  124. package/dist/infra/fixed-window-rate-limit.js +33 -0
  125. package/dist/infra/git-root.js +61 -0
  126. package/dist/infra/heartbeat-active-hours.js +2 -2
  127. package/dist/infra/heartbeat-reason.js +40 -0
  128. package/dist/infra/heartbeat-runner.js +72 -32
  129. package/dist/infra/install-source-utils.js +91 -7
  130. package/dist/infra/node-pairing.js +50 -105
  131. package/dist/infra/npm-integrity.js +45 -0
  132. package/dist/infra/npm-pack-install.js +40 -0
  133. package/dist/infra/outbound/channel-adapters.js +20 -7
  134. package/dist/infra/outbound/message-action-runner.js +107 -327
  135. package/dist/infra/outbound/message.js +59 -36
  136. package/dist/infra/outbound/outbound-policy.js +52 -25
  137. package/dist/infra/outbound/outbound-send-service.js +58 -71
  138. package/dist/infra/pairing-files.js +10 -0
  139. package/dist/infra/plain-object.js +9 -0
  140. package/dist/infra/push-apns.js +365 -0
  141. package/dist/infra/restart-sentinel.js +16 -1
  142. package/dist/infra/restart.js +229 -26
  143. package/dist/infra/scp-host.js +54 -0
  144. package/dist/infra/update-startup.js +86 -9
  145. package/dist/media/inbound-path-policy.js +114 -0
  146. package/dist/media/input-files.js +16 -0
  147. package/dist/memory/test-manager.js +8 -0
  148. package/dist/plugin-sdk/temp-path.js +47 -0
  149. package/dist/plugins/discovery.js +217 -23
  150. package/dist/plugins/hook-runner-global.js +16 -0
  151. package/dist/plugins/loader.js +192 -26
  152. package/dist/plugins/logger.js +8 -0
  153. package/dist/plugins/manifest-registry.js +3 -0
  154. package/dist/plugins/path-safety.js +34 -0
  155. package/dist/plugins/registry.js +5 -2
  156. package/dist/plugins/runtime/index.js +271 -206
  157. package/dist/providers/github-copilot-models.js +4 -1
  158. package/dist/security/audit-channel.js +8 -19
  159. package/dist/security/audit-extra.async.js +354 -182
  160. package/dist/security/audit-extra.js +11 -1
  161. package/dist/security/audit-extra.sync.js +340 -33
  162. package/dist/security/audit-fs.js +31 -13
  163. package/dist/security/audit.js +145 -371
  164. package/dist/security/dm-policy-shared.js +24 -0
  165. package/dist/security/external-content.js +20 -8
  166. package/dist/security/fix.js +49 -85
  167. package/dist/security/scan-paths.js +20 -0
  168. package/dist/security/secret-equal.js +3 -7
  169. package/dist/security/windows-acl.js +30 -15
  170. package/dist/shared/node-list-parse.js +13 -0
  171. package/dist/shared/operator-scope-compat.js +37 -0
  172. package/dist/shared/text-chunking.js +29 -0
  173. package/dist/slack/blocks.test-helpers.js +31 -0
  174. package/dist/slack/monitor/mrkdwn.js +8 -0
  175. package/dist/telegram/bot-message-dispatch.js +366 -164
  176. package/dist/telegram/draft-stream.js +30 -7
  177. package/dist/telegram/reasoning-lane-coordinator.js +128 -0
  178. package/dist/terminal/prompt-select-styled.js +9 -0
  179. package/dist/test-utils/command-runner.js +6 -0
  180. package/dist/test-utils/internal-hook-event-payload.js +10 -0
  181. package/dist/test-utils/model-auth-mock.js +12 -0
  182. package/dist/test-utils/provider-usage-fetch.js +14 -0
  183. package/dist/test-utils/temp-home.js +33 -0
  184. package/dist/tui/components/chat-log.js +9 -0
  185. package/dist/tui/tui-command-handlers.js +36 -27
  186. package/dist/tui/tui-event-handlers.js +122 -32
  187. package/dist/tui/tui.js +181 -45
  188. package/dist/utils/mask-api-key.js +10 -0
  189. package/dist/utils/run-with-concurrency.js +39 -0
  190. package/dist/web/media.js +4 -0
  191. package/docs/tools/slash-commands.md +5 -1
  192. package/extensions/bluebubbles/package.json +1 -1
  193. package/extensions/copilot-proxy/package.json +1 -1
  194. package/extensions/diagnostics-otel/package.json +1 -1
  195. package/extensions/discord/package.json +1 -1
  196. package/extensions/feishu/package.json +1 -1
  197. package/extensions/feishu/src/external-keys.ts +19 -0
  198. package/extensions/google-antigravity-auth/package.json +1 -1
  199. package/extensions/google-gemini-cli-auth/package.json +1 -1
  200. package/extensions/googlechat/package.json +1 -1
  201. package/extensions/imessage/package.json +1 -1
  202. package/extensions/irc/package.json +1 -1
  203. package/extensions/line/package.json +1 -1
  204. package/extensions/llm-task/package.json +1 -1
  205. package/extensions/lobster/package.json +1 -1
  206. package/extensions/lobster/src/windows-spawn.ts +193 -0
  207. package/extensions/matrix/CHANGELOG.md +5 -0
  208. package/extensions/matrix/package.json +1 -1
  209. package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
  210. package/extensions/mattermost/package.json +1 -1
  211. package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
  212. package/extensions/memory-core/package.json +1 -1
  213. package/extensions/memory-lancedb/package.json +1 -1
  214. package/extensions/minimax-portal-auth/package.json +1 -1
  215. package/extensions/msteams/CHANGELOG.md +5 -0
  216. package/extensions/msteams/package.json +1 -1
  217. package/extensions/nextcloud-talk/package.json +1 -1
  218. package/extensions/nostr/CHANGELOG.md +5 -0
  219. package/extensions/nostr/package.json +1 -1
  220. package/extensions/open-prose/package.json +1 -1
  221. package/extensions/openai-codex-auth/package.json +1 -1
  222. package/extensions/signal/package.json +1 -1
  223. package/extensions/slack/package.json +1 -1
  224. package/extensions/telegram/package.json +1 -1
  225. package/extensions/tlon/package.json +1 -1
  226. package/extensions/twitch/CHANGELOG.md +5 -0
  227. package/extensions/twitch/package.json +1 -1
  228. package/extensions/voice-call/CHANGELOG.md +5 -0
  229. package/extensions/voice-call/package.json +1 -1
  230. package/extensions/whatsapp/package.json +1 -1
  231. package/extensions/zalo/CHANGELOG.md +5 -0
  232. package/extensions/zalo/package.json +1 -1
  233. package/extensions/zalouser/CHANGELOG.md +5 -0
  234. package/extensions/zalouser/package.json +1 -1
  235. package/package.json +1 -1
package/dist/tui/tui.js CHANGED
@@ -1,4 +1,4 @@
1
- import { CombinedAutocompleteProvider, Container, Loader, ProcessTerminal, Text, TUI, truncateToWidth, } from "@mariozechner/pi-tui";
1
+ import { CombinedAutocompleteProvider, Container, Loader, ProcessTerminal, Text, TUI, } from "@mariozechner/pi-tui";
2
2
  import { resolveDefaultAgentId } from "../agents/agent-scope.js";
3
3
  import { loadConfig } from "../config/config.js";
4
4
  import { buildAgentMainSessionKey, normalizeAgentId, normalizeMainKey, parseAgentSessionKey, } from "../routing/session-key.js";
@@ -11,9 +11,9 @@ import { createCommandHandlers } from "./tui-command-handlers.js";
11
11
  import { createEventHandlers } from "./tui-event-handlers.js";
12
12
  import { formatTokens } from "./tui-formatters.js";
13
13
  import { createLocalShellRunner } from "./tui-local-shell.js";
14
- import { buildWaitingStatusMessage, defaultWaitingPhrases } from "./tui-waiting.js";
15
14
  import { createOverlayHandlers } from "./tui-overlays.js";
16
15
  import { createSessionActions } from "./tui-session-actions.js";
16
+ import { buildWaitingStatusMessage, defaultWaitingPhrases } from "./tui-waiting.js";
17
17
  export { resolveFinalAssistantText } from "./tui-formatters.js";
18
18
  export function createEditorSubmitHandler(params) {
19
19
  return (text) => {
@@ -21,8 +21,9 @@ export function createEditorSubmitHandler(params) {
21
21
  const value = raw.trim();
22
22
  params.editor.setText("");
23
23
  // Keep previous behavior: ignore empty/whitespace-only submissions.
24
- if (!value)
24
+ if (!value) {
25
25
  return;
26
+ }
26
27
  // Bash mode: only if the very first character is '!' and it's not just '!'.
27
28
  // IMPORTANT: use the raw (untrimmed) text so leading spaces do NOT trigger.
28
29
  // Per requirement: a lone '!' should be treated as a normal message.
@@ -40,6 +41,102 @@ export function createEditorSubmitHandler(params) {
40
41
  void params.sendMessage(value);
41
42
  };
42
43
  }
44
+ export function shouldEnableWindowsGitBashPasteFallback(params) {
45
+ const platform = params?.platform ?? process.platform;
46
+ if (platform !== "win32") {
47
+ return false;
48
+ }
49
+ const env = params?.env ?? process.env;
50
+ const msystem = (env.MSYSTEM ?? "").toUpperCase();
51
+ const shell = env.SHELL ?? "";
52
+ const termProgram = (env.TERM_PROGRAM ?? "").toLowerCase();
53
+ if (msystem.startsWith("MINGW") || msystem.startsWith("MSYS")) {
54
+ return true;
55
+ }
56
+ if (shell.toLowerCase().includes("bash")) {
57
+ return true;
58
+ }
59
+ return termProgram.includes("mintty");
60
+ }
61
+ export function createSubmitBurstCoalescer(params) {
62
+ const windowMs = Math.max(1, params.burstWindowMs ?? 50);
63
+ const now = params.now ?? (() => Date.now());
64
+ const setTimer = params.setTimer ?? setTimeout;
65
+ const clearTimer = params.clearTimer ?? clearTimeout;
66
+ let pending = null;
67
+ let pendingAt = 0;
68
+ let flushTimer = null;
69
+ const clearFlushTimer = () => {
70
+ if (!flushTimer) {
71
+ return;
72
+ }
73
+ clearTimer(flushTimer);
74
+ flushTimer = null;
75
+ };
76
+ const flushPending = () => {
77
+ if (pending === null) {
78
+ return;
79
+ }
80
+ const value = pending;
81
+ pending = null;
82
+ pendingAt = 0;
83
+ clearFlushTimer();
84
+ params.submit(value);
85
+ };
86
+ const scheduleFlush = () => {
87
+ clearFlushTimer();
88
+ flushTimer = setTimer(() => {
89
+ flushPending();
90
+ }, windowMs);
91
+ };
92
+ return (value) => {
93
+ if (!params.enabled) {
94
+ params.submit(value);
95
+ return;
96
+ }
97
+ if (value.includes("\n")) {
98
+ flushPending();
99
+ params.submit(value);
100
+ return;
101
+ }
102
+ const ts = now();
103
+ if (pending === null) {
104
+ pending = value;
105
+ pendingAt = ts;
106
+ scheduleFlush();
107
+ return;
108
+ }
109
+ if (ts - pendingAt <= windowMs) {
110
+ pending = `${pending}\n${value}`;
111
+ pendingAt = ts;
112
+ scheduleFlush();
113
+ return;
114
+ }
115
+ flushPending();
116
+ pending = value;
117
+ pendingAt = ts;
118
+ scheduleFlush();
119
+ };
120
+ }
121
+ export function resolveTuiSessionKey(params) {
122
+ const trimmed = (params.raw ?? "").trim();
123
+ if (!trimmed) {
124
+ if (params.sessionScope === "global") {
125
+ return "global";
126
+ }
127
+ return buildAgentMainSessionKey({
128
+ agentId: params.currentAgentId,
129
+ mainKey: params.sessionMainKey,
130
+ });
131
+ }
132
+ if (trimmed === "global" || trimmed === "unknown") {
133
+ return trimmed;
134
+ }
135
+ if (trimmed.startsWith("agent:")) {
136
+ return trimmed;
137
+ }
138
+ return `agent:${params.currentAgentId}:${trimmed}`;
139
+ }
43
140
  export async function runTui(opts) {
44
141
  const config = loadConfig();
45
142
  const initialSessionInput = (opts.session ?? "").trim();
@@ -58,6 +155,7 @@ export async function runTui(opts) {
58
155
  let wasDisconnected = false;
59
156
  let toolsExpanded = false;
60
157
  let showThinking = false;
158
+ const localRunIds = new Set();
61
159
  const deliverDefault = opts.deliver ?? false;
62
160
  const autoMessage = opts.message?.trim();
63
161
  let autoMessageSent = false;
@@ -185,6 +283,25 @@ export async function runTui(opts) {
185
283
  lastCtrlCAt = value;
186
284
  },
187
285
  };
286
+ const noteLocalRunId = (runId) => {
287
+ if (!runId) {
288
+ return;
289
+ }
290
+ localRunIds.add(runId);
291
+ if (localRunIds.size > 200) {
292
+ const [first] = localRunIds;
293
+ if (first) {
294
+ localRunIds.delete(first);
295
+ }
296
+ }
297
+ };
298
+ const forgetLocalRunId = (runId) => {
299
+ localRunIds.delete(runId);
300
+ };
301
+ const isLocalRunId = (runId) => localRunIds.has(runId);
302
+ const clearLocalRunIds = () => {
303
+ localRunIds.clear();
304
+ };
188
305
  const client = new GatewayChatClient({
189
306
  url: opts.url,
190
307
  token: opts.token,
@@ -212,8 +329,9 @@ export async function runTui(opts) {
212
329
  tui.addChild(root);
213
330
  tui.setFocus(editor);
214
331
  const formatSessionKey = (key) => {
215
- if (key === "global" || key === "unknown")
332
+ if (key === "global" || key === "unknown") {
216
333
  return key;
334
+ }
217
335
  const parsed = parseAgentSessionKey(key);
218
336
  return parsed?.rest ?? key;
219
337
  };
@@ -222,43 +340,35 @@ export async function runTui(opts) {
222
340
  return name ? `${id} (${name})` : id;
223
341
  };
224
342
  const resolveSessionKey = (raw) => {
225
- const trimmed = (raw ?? "").trim();
226
- if (sessionScope === "global")
227
- return "global";
228
- if (!trimmed) {
229
- return buildAgentMainSessionKey({
230
- agentId: currentAgentId,
231
- mainKey: sessionMainKey,
232
- });
233
- }
234
- if (trimmed === "global" || trimmed === "unknown")
235
- return trimmed;
236
- if (trimmed.startsWith("agent:"))
237
- return trimmed;
238
- return `agent:${currentAgentId}:${trimmed}`;
343
+ return resolveTuiSessionKey({
344
+ raw,
345
+ sessionScope,
346
+ currentAgentId,
347
+ sessionMainKey,
348
+ });
239
349
  };
240
350
  currentSessionKey = resolveSessionKey(initialSessionInput);
241
- const getContentWidth = () => Math.max(20, (tui.terminal.columns ?? 80) - 2); // paddingX=1 each side
242
351
  const updateHeader = () => {
243
352
  const sessionLabel = formatSessionKey(currentSessionKey);
244
353
  const agentLabel = formatAgentLabel(currentAgentId);
245
- const raw = `poolbot tui - ${client.connection.url} - agent ${agentLabel} - session ${sessionLabel}`;
246
- header.setText(theme.header(truncateToWidth(raw, getContentWidth())));
354
+ header.setText(theme.header(`poolbot tui - ${client.connection.url} - agent ${agentLabel} - session ${sessionLabel}`));
247
355
  };
248
356
  const busyStates = new Set(["sending", "waiting", "streaming", "running"]);
249
357
  let statusText = null;
250
358
  let statusLoader = null;
251
359
  const formatElapsed = (startMs) => {
252
360
  const totalSeconds = Math.max(0, Math.floor((Date.now() - startMs) / 1000));
253
- if (totalSeconds < 60)
361
+ if (totalSeconds < 60) {
254
362
  return `${totalSeconds}s`;
363
+ }
255
364
  const minutes = Math.floor(totalSeconds / 60);
256
365
  const seconds = totalSeconds % 60;
257
366
  return `${minutes}m ${seconds}s`;
258
367
  };
259
368
  const ensureStatusText = () => {
260
- if (statusText)
369
+ if (statusText) {
261
370
  return;
371
+ }
262
372
  statusContainer.clear();
263
373
  statusLoader?.stop();
264
374
  statusLoader = null;
@@ -266,8 +376,9 @@ export async function runTui(opts) {
266
376
  statusContainer.addChild(statusText);
267
377
  };
268
378
  const ensureStatusLoader = () => {
269
- if (statusLoader)
379
+ if (statusLoader) {
270
380
  return;
381
+ }
271
382
  statusContainer.clear();
272
383
  statusText = null;
273
384
  statusLoader = new Loader(tui, (spinner) => theme.accent(spinner), (text) => theme.bold(theme.accentSoft(text)), "");
@@ -277,44 +388,45 @@ export async function runTui(opts) {
277
388
  let waitingTimer = null;
278
389
  let waitingPhrase = null;
279
390
  const updateBusyStatusMessage = () => {
280
- if (!statusLoader || !statusStartedAt)
391
+ if (!statusLoader || !statusStartedAt) {
281
392
  return;
393
+ }
282
394
  const elapsed = formatElapsed(statusStartedAt);
283
- // Truncate status messages to terminal width to prevent TUI crash
284
- // (Loader adds spinner + space = 2 visible chars before the message)
285
- const maxMsgWidth = Math.max(10, getContentWidth() - 4);
286
395
  if (activityStatus === "waiting") {
287
396
  waitingTick++;
288
- const msg = buildWaitingStatusMessage({
397
+ statusLoader.setMessage(buildWaitingStatusMessage({
289
398
  theme,
290
399
  tick: waitingTick,
291
400
  elapsed,
292
401
  connectionStatus,
293
402
  phrases: waitingPhrase ? [waitingPhrase] : undefined,
294
- });
295
- statusLoader.setMessage(truncateToWidth(msg, maxMsgWidth));
403
+ }));
296
404
  return;
297
405
  }
298
- statusLoader.setMessage(truncateToWidth(`${activityStatus} • ${elapsed} | ${connectionStatus}`, maxMsgWidth));
406
+ statusLoader.setMessage(`${activityStatus} • ${elapsed} | ${connectionStatus}`);
299
407
  };
300
408
  const startStatusTimer = () => {
301
- if (statusTimer)
409
+ if (statusTimer) {
302
410
  return;
411
+ }
303
412
  statusTimer = setInterval(() => {
304
- if (!busyStates.has(activityStatus))
413
+ if (!busyStates.has(activityStatus)) {
305
414
  return;
415
+ }
306
416
  updateBusyStatusMessage();
307
417
  }, 1000);
308
418
  };
309
419
  const stopStatusTimer = () => {
310
- if (!statusTimer)
420
+ if (!statusTimer) {
311
421
  return;
422
+ }
312
423
  clearInterval(statusTimer);
313
424
  statusTimer = null;
314
425
  };
315
426
  const startWaitingTimer = () => {
316
- if (waitingTimer)
427
+ if (waitingTimer) {
317
428
  return;
429
+ }
318
430
  // Pick a phrase once per waiting session.
319
431
  if (!waitingPhrase) {
320
432
  const idx = Math.floor(Math.random() * defaultWaitingPhrases.length);
@@ -322,14 +434,16 @@ export async function runTui(opts) {
322
434
  }
323
435
  waitingTick = 0;
324
436
  waitingTimer = setInterval(() => {
325
- if (activityStatus !== "waiting")
437
+ if (activityStatus !== "waiting") {
326
438
  return;
439
+ }
327
440
  updateBusyStatusMessage();
328
441
  }, 120);
329
442
  };
330
443
  const stopWaitingTimer = () => {
331
- if (!waitingTimer)
444
+ if (!waitingTimer) {
332
445
  return;
446
+ }
333
447
  clearInterval(waitingTimer);
334
448
  waitingTimer = null;
335
449
  waitingPhrase = null;
@@ -366,8 +480,9 @@ export async function runTui(opts) {
366
480
  const setConnectionStatus = (text, ttlMs) => {
367
481
  connectionStatus = text;
368
482
  renderStatus();
369
- if (statusTimeout)
483
+ if (statusTimeout) {
370
484
  clearTimeout(statusTimeout);
485
+ }
371
486
  if (ttlMs && ttlMs > 0) {
372
487
  statusTimeout = setTimeout(() => {
373
488
  connectionStatus = isConnected ? "connected" : "disconnected";
@@ -404,12 +519,13 @@ export async function runTui(opts) {
404
519
  reasoningLabel,
405
520
  tokens,
406
521
  ].filter(Boolean);
407
- footer.setText(theme.dim(truncateToWidth(footerParts.join(" | "), getContentWidth())));
522
+ footer.setText(theme.dim(footerParts.join(" | ")));
408
523
  };
409
524
  const { openOverlay, closeOverlay } = createOverlayHandlers(tui, editor);
410
525
  const initialSessionAgentId = (() => {
411
- if (!initialSessionInput)
526
+ if (!initialSessionInput) {
412
527
  return null;
528
+ }
413
529
  const parsed = parseAgentSessionKey(initialSessionInput);
414
530
  return parsed ? normalizeAgentId(parsed.agentId) : null;
415
531
  })();
@@ -427,14 +543,19 @@ export async function runTui(opts) {
427
543
  updateFooter,
428
544
  updateAutocompleteProvider,
429
545
  setActivityStatus,
546
+ clearLocalRunIds,
430
547
  });
431
- const { refreshAgents, refreshSessionInfo, loadHistory, setSession, abortActive } = sessionActions;
548
+ const { refreshAgents, refreshSessionInfo, applySessionInfoFromPatch, loadHistory, setSession, abortActive, } = sessionActions;
432
549
  const { handleChatEvent, handleAgentEvent } = createEventHandlers({
433
550
  chatLog,
434
551
  tui,
435
552
  state,
436
553
  setActivityStatus,
437
554
  refreshSessionInfo,
555
+ loadHistory,
556
+ isLocalRunId,
557
+ forgetLocalRunId,
558
+ clearLocalRunIds,
438
559
  });
439
560
  const { handleCommand, sendMessage, openModelSelector, openAgentSelector, openSessionSelector } = createCommandHandlers({
440
561
  client,
@@ -446,12 +567,15 @@ export async function runTui(opts) {
446
567
  openOverlay,
447
568
  closeOverlay,
448
569
  refreshSessionInfo,
570
+ applySessionInfoFromPatch,
449
571
  loadHistory,
450
572
  setSession,
451
573
  refreshAgents,
452
574
  abortActive,
453
575
  setActivityStatus,
454
576
  formatSessionKey,
577
+ noteLocalRunId,
578
+ forgetLocalRunId,
455
579
  });
456
580
  const { runLocalShellLine } = createLocalShellRunner({
457
581
  chatLog,
@@ -460,12 +584,16 @@ export async function runTui(opts) {
460
584
  closeOverlay,
461
585
  });
462
586
  updateAutocompleteProvider();
463
- editor.onSubmit = createEditorSubmitHandler({
587
+ const submitHandler = createEditorSubmitHandler({
464
588
  editor,
465
589
  handleCommand,
466
590
  sendMessage,
467
591
  handleBangLine: runLocalShellLine,
468
592
  });
593
+ editor.onSubmit = createSubmitBurstCoalescer({
594
+ submit: submitHandler,
595
+ enabled: shouldEnableWindowsGitBashPasteFallback(),
596
+ });
469
597
  editor.onEscape = () => {
470
598
  void abortActive();
471
599
  };
@@ -511,10 +639,12 @@ export async function runTui(opts) {
511
639
  void loadHistory();
512
640
  };
513
641
  client.onEvent = (evt) => {
514
- if (evt.event === "chat")
642
+ if (evt.event === "chat") {
515
643
  handleChatEvent(evt.payload);
516
- if (evt.event === "agent")
644
+ }
645
+ if (evt.event === "agent") {
517
646
  handleAgentEvent(evt.payload);
647
+ }
518
648
  };
519
649
  client.onConnected = () => {
520
650
  isConnected = true;
@@ -554,4 +684,10 @@ export async function runTui(opts) {
554
684
  updateFooter();
555
685
  tui.start();
556
686
  client.start();
687
+ await new Promise((resolve) => {
688
+ const finish = () => resolve();
689
+ process.once("exit", finish);
690
+ process.once("SIGINT", finish);
691
+ process.once("SIGTERM", finish);
692
+ });
557
693
  }
@@ -0,0 +1,10 @@
1
+ export const maskApiKey = (value) => {
2
+ const trimmed = value.trim();
3
+ if (!trimmed) {
4
+ return "missing";
5
+ }
6
+ if (trimmed.length <= 16) {
7
+ return trimmed;
8
+ }
9
+ return `${trimmed.slice(0, 8)}...${trimmed.slice(-8)}`;
10
+ };
@@ -0,0 +1,39 @@
1
+ export async function runTasksWithConcurrency(params) {
2
+ const { tasks, limit, onTaskError } = params;
3
+ const errorMode = params.errorMode ?? "continue";
4
+ if (tasks.length === 0) {
5
+ return { results: [], firstError: undefined, hasError: false };
6
+ }
7
+ const resolvedLimit = Math.max(1, Math.min(limit, tasks.length));
8
+ const results = Array.from({ length: tasks.length });
9
+ let next = 0;
10
+ let firstError = undefined;
11
+ let hasError = false;
12
+ const workers = Array.from({ length: resolvedLimit }, async () => {
13
+ while (true) {
14
+ if (errorMode === "stop" && hasError) {
15
+ return;
16
+ }
17
+ const index = next;
18
+ next += 1;
19
+ if (index >= tasks.length) {
20
+ return;
21
+ }
22
+ try {
23
+ results[index] = await tasks[index]();
24
+ }
25
+ catch (error) {
26
+ if (!hasError) {
27
+ firstError = error;
28
+ hasError = true;
29
+ }
30
+ onTaskError?.(error, index);
31
+ if (errorMode === "stop") {
32
+ return;
33
+ }
34
+ }
35
+ }
36
+ });
37
+ await Promise.allSettled(workers);
38
+ return { results, firstError, hasError };
39
+ }
package/dist/web/media.js CHANGED
@@ -5,8 +5,12 @@ import { logVerbose, shouldLogVerbose } from "../globals.js";
5
5
  import { maxBytesForKind, mediaKindFromMime } from "../media/constants.js";
6
6
  import { resolveUserPath } from "../utils.js";
7
7
  import { fetchRemoteMedia } from "../media/fetch.js";
8
+ import { getDefaultMediaLocalRoots } from "../media/local-roots.js";
8
9
  import { convertHeicToJpeg, hasAlphaChannel, optimizeImageToPng, resizeToJpeg, } from "../media/image-ops.js";
9
10
  import { detectMime, extensionForMime } from "../media/mime.js";
11
+ export function getDefaultLocalRoots() {
12
+ return getDefaultMediaLocalRoots();
13
+ }
10
14
  const HEIC_MIME_RE = /^image\/hei[cf]$/i;
11
15
  const HEIC_EXT_RE = /\.(heic|heif)$/i;
12
16
  const MB = 1024 * 1024;
@@ -3,6 +3,7 @@ summary: "Slash commands: text vs native, config, and supported commands"
3
3
  read_when:
4
4
  - Using or configuring chat commands
5
5
  - Debugging command routing or permissions
6
+ title: "Slash Commands"
6
7
  ---
7
8
  # Slash commands
8
9
 
@@ -65,8 +66,11 @@ Text + native (when enabled):
65
66
  - `/allowlist` (list/add/remove allowlist entries)
66
67
  - `/approve <id> allow-once|allow-always|deny` (resolve exec approval prompts)
67
68
  - `/context [list|detail|json]` (explain “context”; `detail` shows per-file + per-tool + per-skill + system prompt size)
69
+ - `/export-session [path]` (export the current session; alias: `/export`)
68
70
  - `/whoami` (show your sender id; alias: `/id`)
69
- - `/subagents list|stop|log|info|send` (inspect, stop, log, or message sub-agent runs for the current session)
71
+ - `/subagents list|kill|log|info|send|steer|spawn` (inspect, kill, log, or message sub-agent runs for the current session)
72
+ - `/kill <id|#|all>` (kill a sub-agent run)
73
+ - `/steer <id|#> <message>` (redirect a sub-agent; alias: `/tell`)
70
74
  - `/config show|get|set|unset` (persist config to disk, owner-only; requires `commands.config: true`)
71
75
  - `/debug show|set|unset|reset` (runtime overrides, owner-only; requires `commands.debug: true`)
72
76
  - `/usage off|tokens|full|cost` (per-response usage footer or local cost summary)
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolbot/bluebubbles",
3
- "version": "2026.2.23",
3
+ "version": "2026.2.24",
4
4
  "type": "module",
5
5
  "description": "Poolbot BlueBubbles channel plugin",
6
6
  "poolbot": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolbot/copilot-proxy",
3
- "version": "2026.2.23",
3
+ "version": "2026.2.24",
4
4
  "type": "module",
5
5
  "description": "Poolbot Copilot Proxy provider plugin",
6
6
  "poolbot": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolbot/diagnostics-otel",
3
- "version": "2026.2.23",
3
+ "version": "2026.2.24",
4
4
  "type": "module",
5
5
  "description": "Poolbot diagnostics OpenTelemetry exporter",
6
6
  "poolbot": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolbot/discord",
3
- "version": "2026.2.23",
3
+ "version": "2026.2.24",
4
4
  "type": "module",
5
5
  "description": "Poolbot Discord channel plugin",
6
6
  "poolbot": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolzin/feishu",
3
- "version": "2026.2.23",
3
+ "version": "2026.2.24",
4
4
  "description": "Pool Bot Feishu/Lark channel plugin (community maintained by @m1heng)",
5
5
  "type": "module",
6
6
  "dependencies": {
@@ -0,0 +1,19 @@
1
+ const CONTROL_CHARS_RE = /[\u0000-\u001f\u007f]/;
2
+ const MAX_EXTERNAL_KEY_LENGTH = 512;
3
+
4
+ export function normalizeFeishuExternalKey(value: unknown): string | undefined {
5
+ if (typeof value !== "string") {
6
+ return undefined;
7
+ }
8
+ const normalized = value.trim();
9
+ if (!normalized || normalized.length > MAX_EXTERNAL_KEY_LENGTH) {
10
+ return undefined;
11
+ }
12
+ if (CONTROL_CHARS_RE.test(normalized)) {
13
+ return undefined;
14
+ }
15
+ if (normalized.includes("/") || normalized.includes("\\") || normalized.includes("..")) {
16
+ return undefined;
17
+ }
18
+ return normalized;
19
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolbot/google-antigravity-auth",
3
- "version": "2026.2.23",
3
+ "version": "2026.2.24",
4
4
  "type": "module",
5
5
  "description": "Poolbot Google Antigravity OAuth provider plugin",
6
6
  "poolbot": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolbot/google-gemini-cli-auth",
3
- "version": "2026.2.23",
3
+ "version": "2026.2.24",
4
4
  "type": "module",
5
5
  "description": "Poolbot Gemini CLI OAuth provider plugin",
6
6
  "poolbot": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolbot/googlechat",
3
- "version": "2026.2.23",
3
+ "version": "2026.2.24",
4
4
  "type": "module",
5
5
  "description": "Poolbot Google Chat channel plugin",
6
6
  "poolbot": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolbot/imessage",
3
- "version": "2026.2.23",
3
+ "version": "2026.2.24",
4
4
  "type": "module",
5
5
  "description": "Poolbot iMessage channel plugin",
6
6
  "poolbot": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolzin/irc",
3
- "version": "2026.2.23",
3
+ "version": "2026.2.24",
4
4
  "description": "Pool Bot IRC channel plugin",
5
5
  "type": "module",
6
6
  "devDependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolbot/line",
3
- "version": "2026.2.23",
3
+ "version": "2026.2.24",
4
4
  "type": "module",
5
5
  "description": "Poolbot LINE channel plugin",
6
6
  "poolbot": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolbot/llm-task",
3
- "version": "2026.2.23",
3
+ "version": "2026.2.24",
4
4
  "type": "module",
5
5
  "description": "Poolbot JSON-only LLM task plugin",
6
6
  "poolbot": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolbot/lobster",
3
- "version": "2026.2.23",
3
+ "version": "2026.2.24",
4
4
  "type": "module",
5
5
  "description": "Lobster workflow tool plugin (typed pipelines + resumable approvals)",
6
6
  "poolbot": {