@poolzin/pool-bot 2026.3.25 → 2026.3.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/dist/agents/model-fallback.js +5 -4
  2. package/dist/agents/tools/common.js +16 -201
  3. package/dist/auto-reply/auto-reply/reply/agent-runner-execution.js +502 -0
  4. package/dist/auto-reply/auto-reply/reply/agent-runner-helpers.js +65 -0
  5. package/dist/auto-reply/auto-reply/reply/agent-runner-memory.js +160 -0
  6. package/dist/auto-reply/auto-reply/reply/agent-runner-payloads.js +85 -0
  7. package/dist/auto-reply/auto-reply/reply/agent-runner-utils.js +101 -0
  8. package/dist/auto-reply/auto-reply/reply/bash-command.js +338 -0
  9. package/dist/auto-reply/auto-reply/reply/block-streaming.js +91 -0
  10. package/dist/auto-reply/auto-reply/reply/commands-approve.js +88 -0
  11. package/dist/auto-reply/auto-reply/reply/commands-bash.js +26 -0
  12. package/dist/auto-reply/auto-reply/reply/commands-compact.js +107 -0
  13. package/dist/auto-reply/auto-reply/reply/commands-config.js +241 -0
  14. package/dist/auto-reply/auto-reply/reply/commands-context-report.js +295 -0
  15. package/dist/auto-reply/auto-reply/reply/commands-context.js +30 -0
  16. package/dist/auto-reply/auto-reply/reply/commands-core.js +151 -0
  17. package/dist/auto-reply/auto-reply/reply/commands-export-session.js +163 -0
  18. package/dist/auto-reply/auto-reply/reply/commands-info.js +184 -0
  19. package/dist/auto-reply/auto-reply/reply/commands-models.js +299 -0
  20. package/dist/auto-reply/auto-reply/reply/commands-plugin.js +35 -0
  21. package/dist/auto-reply/auto-reply/reply/commands-ptt.js +171 -0
  22. package/dist/auto-reply/auto-reply/reply/commands-setunset-standard.js +13 -0
  23. package/dist/auto-reply/auto-reply/reply/commands-setunset.js +73 -0
  24. package/dist/auto-reply/auto-reply/reply/commands-slash-parse.js +31 -0
  25. package/dist/auto-reply/auto-reply/reply/commands-status.js +178 -0
  26. package/dist/auto-reply/auto-reply/reply/commands-subagents.js +73 -0
  27. package/dist/auto-reply/auto-reply/reply/commands-system-prompt.js +117 -0
  28. package/dist/auto-reply/auto-reply/reply/commands-tts.js +231 -0
  29. package/dist/auto-reply/auto-reply/reply/directive-handling.impl.js +380 -0
  30. package/dist/auto-reply/auto-reply/reply/followup-runner.js +227 -0
  31. package/dist/auto-reply/auto-reply/reply/get-reply-directives-apply.js +201 -0
  32. package/dist/auto-reply/auto-reply/reply/get-reply-directives-utils.js +54 -0
  33. package/dist/auto-reply/auto-reply/reply/get-reply-directives.js +332 -0
  34. package/dist/auto-reply/auto-reply/reply/get-reply-inline-actions.js +258 -0
  35. package/dist/auto-reply/auto-reply/reply/get-reply-run.js +297 -0
  36. package/dist/auto-reply/auto-reply/reply/groups.js +102 -0
  37. package/dist/auto-reply/auto-reply/reply/mentions.js +129 -0
  38. package/dist/auto-reply/auto-reply/reply/reply-delivery.js +92 -0
  39. package/dist/auto-reply/auto-reply/reply/reply-directives.js +30 -0
  40. package/dist/auto-reply/auto-reply/reply/reply-dispatcher.js +152 -0
  41. package/dist/auto-reply/auto-reply/reply/reply-elevated.js +166 -0
  42. package/dist/auto-reply/auto-reply/reply/reply-inline.js +28 -0
  43. package/dist/auto-reply/auto-reply/reply/reply-payloads.js +114 -0
  44. package/dist/auto-reply/auto-reply/reply/reply-reference.js +36 -0
  45. package/dist/auto-reply/auto-reply/reply/reply-tags.js +13 -0
  46. package/dist/auto-reply/auto-reply/reply/reply-threading.js +41 -0
  47. package/dist/auto-reply/auto-reply/reply/session-updates.js +233 -0
  48. package/dist/auto-reply/auto-reply/reply/stage-sandbox-media.js +146 -0
  49. package/dist/build-info.json +3 -3
  50. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  51. package/dist/canvas-host/a2ui/a2ui.bundle.js +2 -17772
  52. package/dist/canvas-host/a2ui/index.html +1 -307
  53. package/dist/channels/channels/directory-config.js +185 -0
  54. package/dist/channels/channels/discord/handle-action.guild-admin.js +332 -0
  55. package/dist/channels/channels/discord/handle-action.js +165 -0
  56. package/dist/channels/channels/discord.js +413 -0
  57. package/dist/channels/channels/dock.js +436 -0
  58. package/dist/channels/channels/index.js +51 -0
  59. package/dist/channels/channels/plugins/outbound/discord.js +101 -0
  60. package/dist/channels/channels/whatsapp.js +17 -0
  61. package/dist/channels/plugins/types.js +1 -1
  62. package/dist/channels/run-state-machine.js +7 -0
  63. package/dist/commands/models/auth.js +47 -1
  64. package/dist/commands-subagents/action-agents.js +44 -0
  65. package/dist/commands-subagents/action-focus.js +64 -0
  66. package/dist/commands-subagents/action-help.js +4 -0
  67. package/dist/commands-subagents/action-info.js +45 -0
  68. package/dist/commands-subagents/action-kill.js +60 -0
  69. package/dist/commands-subagents/action-list.js +44 -0
  70. package/dist/commands-subagents/action-log.js +29 -0
  71. package/dist/commands-subagents/action-send.js +119 -0
  72. package/dist/commands-subagents/action-spawn.js +52 -0
  73. package/dist/commands-subagents/action-unfocus.js +30 -0
  74. package/dist/commands-subagents/shared.js +303 -0
  75. package/dist/config/config.js +1 -8
  76. package/dist/config/types.secrets.js +61 -0
  77. package/dist/control-ui/assets/{index-D7shnQwQ.js → index-umCsvrWy.js} +884 -741
  78. package/dist/control-ui/assets/index-umCsvrWy.js.map +1 -0
  79. package/dist/control-ui/assets/pt-BR-DedEVAvY.js +2 -0
  80. package/dist/control-ui/assets/pt-BR-DedEVAvY.js.map +1 -0
  81. package/dist/control-ui/assets/zh-CN-CDzeklK-.js +2 -0
  82. package/dist/control-ui/assets/zh-CN-CDzeklK-.js.map +1 -0
  83. package/dist/control-ui/assets/zh-TW-BJCRYNWH.js +2 -0
  84. package/dist/control-ui/assets/zh-TW-BJCRYNWH.js.map +1 -0
  85. package/dist/control-ui/index.html +1 -1
  86. package/dist/gateway/method-scopes.js +9 -1
  87. package/dist/gateway/node-pending-work.js +142 -0
  88. package/dist/gateway/protocol/index.js +5 -1
  89. package/dist/gateway/protocol/schema/nodes.js +18 -0
  90. package/dist/gateway/server-methods/nodes-pending.js +96 -0
  91. package/dist/gateway/server-methods-list.js +4 -0
  92. package/dist/gateway/server-methods.js +2 -0
  93. package/dist/imessage/channel.js +253 -0
  94. package/dist/imessage/monitor/echo-cache.js +70 -0
  95. package/dist/imessage/monitor/loop-rate-limiter.js +51 -0
  96. package/dist/imessage/monitor/reflection-guard.js +50 -0
  97. package/dist/imessage/monitor/sanitize-outbound.js +25 -0
  98. package/dist/imessage/monitor/self-chat-cache.js +75 -0
  99. package/dist/imessage/runtime.js +3 -0
  100. package/dist/infra/exec-approval-reply.js +7 -0
  101. package/dist/infra/tmp-openclaw-dir.js +84 -0
  102. package/dist/pairing/pairing-challenge.js +15 -0
  103. package/dist/plugin-sdk/account-id.d.ts +1 -0
  104. package/dist/plugin-sdk/agent-media-payload.d.ts +12 -0
  105. package/dist/plugin-sdk/allow-from.d.ts +27 -0
  106. package/dist/plugin-sdk/command-auth.d.ts +25 -0
  107. package/dist/plugin-sdk/command-auth.js +3 -1
  108. package/dist/plugin-sdk/config-paths.d.ts +6 -0
  109. package/dist/plugin-sdk/file-lock.d.ts +16 -0
  110. package/dist/plugin-sdk/index.d.ts +428 -0
  111. package/dist/plugin-sdk/index.js +237 -103
  112. package/dist/plugin-sdk/json-store.d.ts +5 -0
  113. package/dist/plugin-sdk/keyed-async-queue.d.ts +12 -0
  114. package/dist/plugin-sdk/onboarding.d.ts +11 -0
  115. package/dist/plugin-sdk/provider-auth-result.d.ts +14 -0
  116. package/dist/plugin-sdk/slack-message-actions.d.ts +11 -0
  117. package/dist/plugin-sdk/status-helpers.d.ts +25 -0
  118. package/dist/plugin-sdk/temp-path.d.ts +12 -0
  119. package/dist/plugin-sdk/text-chunking.d.ts +1 -0
  120. package/dist/plugin-sdk/tool-send.d.ts +4 -0
  121. package/dist/plugin-sdk/webhook-path.d.ts +6 -0
  122. package/dist/plugin-sdk/webhook-targets.d.ts +23 -0
  123. package/dist/plugin-sdk/windows-spawn.d.ts +39 -0
  124. package/dist/plugin-sdk-internal/accounts.js +6 -0
  125. package/dist/plugin-sdk-internal/discord.js +23 -0
  126. package/dist/plugin-sdk-internal/imessage.js +13 -0
  127. package/dist/plugin-sdk-internal/setup.js +9 -0
  128. package/dist/plugin-sdk-internal/signal.js +13 -0
  129. package/dist/plugin-sdk-internal/slack.js +22 -0
  130. package/dist/plugin-sdk-internal/telegram.js +32 -0
  131. package/dist/plugin-sdk-internal/whatsapp.js +29 -0
  132. package/dist/routing/session-key.js +4 -185
  133. package/dist/shared/pid-alive.js +2 -61
  134. package/dist/shared/process-scoped-map.js +5 -7
  135. package/dist/signal/channel.js +264 -0
  136. package/dist/signal/monitor/access-policy.js +60 -0
  137. package/dist/signal/runtime.js +3 -0
  138. package/dist/slack/account-inspect.js +135 -0
  139. package/dist/slack/blocks-input.js +7 -38
  140. package/dist/slack/channel.js +394 -0
  141. package/dist/slack/interactive-replies.js +28 -0
  142. package/dist/slack/monitor/channel-type.js +31 -0
  143. package/dist/slack/monitor/dm-auth.js +49 -0
  144. package/dist/slack/monitor/events/interactions.modal.js +137 -0
  145. package/dist/slack/monitor/events/message-subtype-handlers.js +68 -0
  146. package/dist/slack/monitor/events/system-event-context.js +29 -0
  147. package/dist/slack/monitor/events/system-event-test-harness.js +41 -0
  148. package/dist/slack/monitor/external-arg-menu-store.js +46 -0
  149. package/dist/slack/monitor/message-handler/prepare-content.js +69 -0
  150. package/dist/slack/monitor/message-handler/prepare-thread-context.js +91 -0
  151. package/dist/slack/monitor/message-handler/prepare.test-helpers.js +55 -0
  152. package/dist/slack/monitor/reconnect-policy.js +78 -0
  153. package/dist/slack/monitor/slash-commands.runtime.js +1 -0
  154. package/dist/slack/monitor/slash-dispatch.runtime.js +9 -0
  155. package/dist/slack/monitor/slash-skill-commands.runtime.js +1 -0
  156. package/dist/slack/resolve-allowlist-common.js +36 -0
  157. package/dist/slack/runtime.js +3 -0
  158. package/dist/slack/sent-thread-cache.js +61 -0
  159. package/dist/slack/truncate.js +10 -0
  160. package/dist/telegram/account-inspect.js +175 -0
  161. package/dist/telegram/allow-from.js +10 -0
  162. package/dist/telegram/api-fetch.js +18 -0
  163. package/dist/telegram/approval-buttons.js +30 -0
  164. package/dist/telegram/audit-membership-runtime.js +61 -0
  165. package/dist/telegram/bot/delivery.replies.js +508 -0
  166. package/dist/telegram/bot/delivery.resolve-media.js +227 -0
  167. package/dist/telegram/bot/delivery.send.js +132 -0
  168. package/dist/telegram/bot/reply-threading.js +46 -0
  169. package/dist/telegram/bot-message-context.body.js +186 -0
  170. package/dist/telegram/bot-message-context.session.js +207 -0
  171. package/dist/telegram/bot-message-context.types.js +1 -0
  172. package/dist/telegram/bot-native-commands.test-helpers.js +117 -0
  173. package/dist/telegram/bot.media.e2e-harness.js +81 -0
  174. package/dist/telegram/bot.media.test-utils.js +81 -0
  175. package/dist/telegram/channel-actions.js +225 -0
  176. package/dist/telegram/channel.js +515 -0
  177. package/dist/telegram/conversation-route.js +107 -0
  178. package/dist/telegram/delivery.js +2 -0
  179. package/dist/telegram/delivery.replies.js +508 -0
  180. package/dist/telegram/dm-access.js +86 -0
  181. package/dist/telegram/draft-stream.test-helpers.js +62 -0
  182. package/dist/telegram/exec-approvals-handler.js +281 -0
  183. package/dist/telegram/exec-approvals.js +62 -0
  184. package/dist/telegram/forum-service-message.js +22 -0
  185. package/dist/telegram/group-config-helpers.js +10 -0
  186. package/dist/telegram/lane-delivery-state.js +19 -0
  187. package/dist/telegram/lane-delivery-text-deliverer.js +357 -0
  188. package/dist/telegram/lane-delivery.js +2 -0
  189. package/dist/telegram/normalize.js +37 -0
  190. package/dist/telegram/onboarding.js +192 -0
  191. package/dist/telegram/outbound-adapter.js +100 -0
  192. package/dist/telegram/polling-session.js +275 -0
  193. package/dist/telegram/runtime.js +3 -0
  194. package/dist/telegram/sendchataction-401-backoff.js +71 -0
  195. package/dist/telegram/sequential-key.js +46 -0
  196. package/dist/telegram/status-issues.js +105 -0
  197. package/dist/telegram/target-writeback.js +165 -0
  198. package/dist/telegram/thread-bindings.js +560 -0
  199. package/dist/utils.js +10 -276
  200. package/dist/wizard/prompts.js +5 -5
  201. package/extensions/feishu/src/policy.ts +1 -1
  202. package/extensions/firecrawl/index.test.ts +82 -0
  203. package/extensions/firecrawl/index.ts +20 -0
  204. package/extensions/firecrawl/openclaw.plugin.json +8 -0
  205. package/extensions/firecrawl/package.json +12 -0
  206. package/extensions/firecrawl/src/config.ts +159 -0
  207. package/extensions/firecrawl/src/firecrawl-client.ts +446 -0
  208. package/extensions/firecrawl/src/firecrawl-scrape-tool.ts +89 -0
  209. package/extensions/firecrawl/src/firecrawl-search-provider.ts +63 -0
  210. package/extensions/firecrawl/src/firecrawl-search-tool.ts +76 -0
  211. package/package.json +1 -1
  212. package/dist/.buildstamp +0 -1
  213. package/dist/acp/bindings-store.js +0 -209
  214. package/dist/acp/control-plane/runtime-cache.js +0 -54
  215. package/dist/acp/control-plane/runtime-options.js +0 -215
  216. package/dist/acp/control-plane/session-actor-queue.js +0 -36
  217. package/dist/acp/index.js +0 -2
  218. package/dist/acp/runtime/errors.js +0 -47
  219. package/dist/acp/runtime/registry.js +0 -86
  220. package/dist/acp/secret-file.js +0 -22
  221. package/dist/agents/auth-profiles.resolve-auth-profile-order.fixtures.js +0 -23
  222. package/dist/agents/bash-process-registry.test-helpers.js +0 -29
  223. package/dist/agents/bash-tools.exec-approval-request.js +0 -20
  224. package/dist/agents/bash-tools.exec-host-gateway.js +0 -240
  225. package/dist/agents/bash-tools.exec-host-node.js +0 -235
  226. package/dist/agents/checkpoint-manager.js +0 -290
  227. package/dist/agents/claude-cli-runner.js +0 -3
  228. package/dist/agents/error-classifier.js +0 -251
  229. package/dist/agents/live-model-filter.js +0 -84
  230. package/dist/agents/nvidia-models.js +0 -228
  231. package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +0 -34
  232. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +0 -156
  233. package/dist/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.js +0 -30
  234. package/dist/agents/provider/config-loader.js +0 -76
  235. package/dist/agents/provider/index.js +0 -15
  236. package/dist/agents/provider/models-dev.js +0 -129
  237. package/dist/agents/provider/session-binding.js +0 -376
  238. package/dist/agents/queued-file-writer.js +0 -22
  239. package/dist/agents/skills/bundled-context.js +0 -23
  240. package/dist/agents/skills/security.js +0 -211
  241. package/dist/agents/skills/tools-dir.js +0 -9
  242. package/dist/agents/skills-install-download.js +0 -290
  243. package/dist/agents/skills-install-output.js +0 -30
  244. package/dist/agents/skills-install.download-test-utils.js +0 -36
  245. package/dist/agents/skills.test-helpers.js +0 -13
  246. package/dist/agents/subagent-announce-reliability.js +0 -160
  247. package/dist/agents/subagent-registry.mocks.shared.js +0 -12
  248. package/dist/agents/test-helpers/assistant-message-fixtures.js +0 -29
  249. package/dist/agents/test-helpers/fast-coding-tools.js +0 -1
  250. package/dist/agents/test-helpers/fast-core-tools.js +0 -8
  251. package/dist/agents/test-helpers/fast-tool-stubs.js +0 -18
  252. package/dist/agents/test-helpers/host-sandbox-fs-bridge.js +0 -74
  253. package/dist/agents/test-helpers/pi-tools-sandbox-context.js +0 -27
  254. package/dist/agents/tool-display-common.js +0 -915
  255. package/dist/agents/tool-policy-shared.js +0 -108
  256. package/dist/agents/tool-policy.conformance.js +0 -14
  257. package/dist/agents/tool-result-truncation.js +0 -299
  258. package/dist/agents/tools/cron-tool.test-helpers.js +0 -12
  259. package/dist/agents/tools/discord-actions-moderation-shared.js +0 -27
  260. package/dist/agents/tools/discord-actions-presence.js +0 -78
  261. package/dist/control-ui/assets/index-D7shnQwQ.js.map +0 -1
  262. package/dist/discord/discord-improvements.js +0 -167
  263. package/dist/discord/index.js +0 -2
  264. package/dist/hooks/bundled/boot-md/HOOK.md +0 -19
  265. package/dist/hooks/bundled/command-logger/HOOK.md +0 -122
  266. package/dist/hooks/bundled/session-memory/HOOK.md +0 -86
  267. package/dist/hooks/bundled/soul-evil/HOOK.md +0 -71
  268. package/dist/whatsapp/normalize.js +0 -66
  269. package/dist/whatsapp/resolve-outbound-target.js +0 -42
  270. /package/dist/{acp/runtime/types.js → auto-reply/auto-reply/reply/commands-types.js} +0 -0
  271. /package/dist/{agents/pi-embedded-payloads.js → slack/account-surface-fields.js} +0 -0
@@ -1,290 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { Readable } from "node:stream";
4
- import { pipeline } from "node:stream/promises";
5
- import { extractArchive as extractArchiveSafe } from "../infra/archive.js";
6
- import { fetchWithSsrFGuard } from "../infra/net/fetch-guard.js";
7
- import { isWithinDir, resolveSafeBaseDir } from "../infra/path-safety.js";
8
- import { runCommandWithTimeout } from "../process/exec.js";
9
- import { ensureDir, resolveUserPath } from "../utils.js";
10
- import { formatInstallFailureMessage } from "./skills-install-output.js";
11
- import { hasBinary } from "./skills.js";
12
- import { resolveSkillToolsRootDir } from "./skills/tools-dir.js";
13
- function isNodeReadableStream(value) {
14
- return Boolean(value && typeof value.pipe === "function");
15
- }
16
- function isWindowsDrivePath(p) {
17
- return /^[a-zA-Z]:[\\/]/.test(p);
18
- }
19
- function resolveDownloadTargetDir(entry, spec) {
20
- const safeRoot = resolveSkillToolsRootDir(entry);
21
- const raw = spec.targetDir?.trim();
22
- if (!raw) {
23
- return safeRoot;
24
- }
25
- // Treat non-absolute paths as relative to the per-skill tools root.
26
- const resolved = raw.startsWith("~") || path.isAbsolute(raw) || isWindowsDrivePath(raw)
27
- ? resolveUserPath(raw)
28
- : path.resolve(safeRoot, raw);
29
- if (!isWithinDir(safeRoot, resolved)) {
30
- throw new Error(`Refusing to install outside the skill tools directory. targetDir="${raw}" resolves to "${resolved}". Allowed root: "${safeRoot}".`);
31
- }
32
- return resolved;
33
- }
34
- function resolveArchiveType(spec, filename) {
35
- const explicit = spec.archive?.trim().toLowerCase();
36
- if (explicit) {
37
- return explicit;
38
- }
39
- const lower = filename.toLowerCase();
40
- if (lower.endsWith(".tar.gz") || lower.endsWith(".tgz")) {
41
- return "tar.gz";
42
- }
43
- if (lower.endsWith(".tar.bz2") || lower.endsWith(".tbz2")) {
44
- return "tar.bz2";
45
- }
46
- if (lower.endsWith(".zip")) {
47
- return "zip";
48
- }
49
- return undefined;
50
- }
51
- function normalizeArchiveEntryPath(raw) {
52
- return raw.replaceAll("\\", "/");
53
- }
54
- function validateArchiveEntryPath(entryPath) {
55
- if (!entryPath || entryPath === "." || entryPath === "./") {
56
- return;
57
- }
58
- if (isWindowsDrivePath(entryPath)) {
59
- throw new Error(`archive entry uses a drive path: ${entryPath}`);
60
- }
61
- const normalized = path.posix.normalize(normalizeArchiveEntryPath(entryPath));
62
- if (normalized === ".." || normalized.startsWith("../")) {
63
- throw new Error(`archive entry escapes targetDir: ${entryPath}`);
64
- }
65
- if (path.posix.isAbsolute(normalized) || normalized.startsWith("//")) {
66
- throw new Error(`archive entry is absolute: ${entryPath}`);
67
- }
68
- }
69
- function stripArchivePath(entryPath, stripComponents) {
70
- const raw = normalizeArchiveEntryPath(entryPath);
71
- if (!raw || raw === "." || raw === "./") {
72
- return null;
73
- }
74
- // Important: tar's --strip-components semantics operate on raw path segments,
75
- // before any normalization that would collapse "..". We mimic that so we
76
- // can detect strip-induced escapes like "a/../b" with stripComponents=1.
77
- const parts = raw.split("/").filter((part) => part.length > 0 && part !== ".");
78
- const strip = Math.max(0, Math.floor(stripComponents));
79
- const stripped = strip === 0 ? parts.join("/") : parts.slice(strip).join("/");
80
- const result = path.posix.normalize(stripped);
81
- if (!result || result === "." || result === "./") {
82
- return null;
83
- }
84
- return result;
85
- }
86
- function validateExtractedPathWithinRoot(params) {
87
- const safeBase = resolveSafeBaseDir(params.rootDir);
88
- const outPath = path.resolve(params.rootDir, params.relPath);
89
- if (!outPath.startsWith(safeBase)) {
90
- throw new Error(`archive entry escapes targetDir: ${params.originalPath}`);
91
- }
92
- }
93
- async function downloadFile(url, destPath, timeoutMs) {
94
- const { response, release } = await fetchWithSsrFGuard({
95
- url,
96
- timeoutMs: Math.max(1_000, timeoutMs),
97
- });
98
- try {
99
- if (!response.ok || !response.body) {
100
- throw new Error(`Download failed (${response.status} ${response.statusText})`);
101
- }
102
- await ensureDir(path.dirname(destPath));
103
- const file = fs.createWriteStream(destPath);
104
- const body = response.body;
105
- const readable = isNodeReadableStream(body)
106
- ? body
107
- : Readable.fromWeb(body);
108
- await pipeline(readable, file);
109
- const stat = await fs.promises.stat(destPath);
110
- return { bytes: stat.size };
111
- }
112
- finally {
113
- await release();
114
- }
115
- }
116
- async function extractArchive(params) {
117
- const { archivePath, archiveType, targetDir, stripComponents, timeoutMs } = params;
118
- const strip = typeof stripComponents === "number" && Number.isFinite(stripComponents)
119
- ? Math.max(0, Math.floor(stripComponents))
120
- : 0;
121
- try {
122
- if (archiveType === "zip") {
123
- await extractArchiveSafe({
124
- archivePath,
125
- destDir: targetDir,
126
- timeoutMs,
127
- kind: "zip",
128
- stripComponents: strip,
129
- });
130
- return { stdout: "", stderr: "", code: 0 };
131
- }
132
- if (archiveType === "tar.gz") {
133
- await extractArchiveSafe({
134
- archivePath,
135
- destDir: targetDir,
136
- timeoutMs,
137
- kind: "tar",
138
- stripComponents: strip,
139
- tarGzip: true,
140
- });
141
- return { stdout: "", stderr: "", code: 0 };
142
- }
143
- if (archiveType === "tar.bz2") {
144
- if (!hasBinary("tar")) {
145
- return { stdout: "", stderr: "tar not found on PATH", code: null };
146
- }
147
- // Preflight list to prevent zip-slip style traversal before extraction.
148
- const listResult = await runCommandWithTimeout(["tar", "tf", archivePath], { timeoutMs });
149
- if (listResult.code !== 0) {
150
- return {
151
- stdout: listResult.stdout,
152
- stderr: listResult.stderr || "tar list failed",
153
- code: listResult.code,
154
- };
155
- }
156
- const entries = listResult.stdout
157
- .split("\n")
158
- .map((line) => line.trim())
159
- .filter(Boolean);
160
- const verboseResult = await runCommandWithTimeout(["tar", "tvf", archivePath], { timeoutMs });
161
- if (verboseResult.code !== 0) {
162
- return {
163
- stdout: verboseResult.stdout,
164
- stderr: verboseResult.stderr || "tar verbose list failed",
165
- code: verboseResult.code,
166
- };
167
- }
168
- for (const line of verboseResult.stdout.split("\n")) {
169
- const trimmed = line.trim();
170
- if (!trimmed) {
171
- continue;
172
- }
173
- const typeChar = trimmed[0];
174
- if (typeChar === "l" || typeChar === "h" || trimmed.includes(" -> ")) {
175
- return {
176
- stdout: verboseResult.stdout,
177
- stderr: "tar archive contains link entries; refusing to extract for safety",
178
- code: 1,
179
- };
180
- }
181
- }
182
- for (const entry of entries) {
183
- validateArchiveEntryPath(entry);
184
- const relPath = stripArchivePath(entry, strip);
185
- if (!relPath) {
186
- continue;
187
- }
188
- validateArchiveEntryPath(relPath);
189
- validateExtractedPathWithinRoot({ rootDir: targetDir, relPath, originalPath: entry });
190
- }
191
- const argv = ["tar", "xf", archivePath, "-C", targetDir];
192
- if (strip > 0) {
193
- argv.push("--strip-components", String(strip));
194
- }
195
- return await runCommandWithTimeout(argv, { timeoutMs });
196
- }
197
- return { stdout: "", stderr: `unsupported archive type: ${archiveType}`, code: null };
198
- }
199
- catch (err) {
200
- const message = err instanceof Error ? err.message : String(err);
201
- return { stdout: "", stderr: message, code: 1 };
202
- }
203
- }
204
- export async function installDownloadSpec(params) {
205
- const { entry, spec, timeoutMs } = params;
206
- const url = spec.url?.trim();
207
- if (!url) {
208
- return {
209
- ok: false,
210
- message: "missing download url",
211
- stdout: "",
212
- stderr: "",
213
- code: null,
214
- };
215
- }
216
- let filename = "";
217
- try {
218
- const parsed = new URL(url);
219
- filename = path.basename(parsed.pathname);
220
- }
221
- catch {
222
- filename = path.basename(url);
223
- }
224
- if (!filename) {
225
- filename = "download";
226
- }
227
- let targetDir = "";
228
- try {
229
- targetDir = resolveDownloadTargetDir(entry, spec);
230
- await ensureDir(targetDir);
231
- const stat = await fs.promises.lstat(targetDir);
232
- if (stat.isSymbolicLink()) {
233
- throw new Error(`targetDir is a symlink: ${targetDir}`);
234
- }
235
- if (!stat.isDirectory()) {
236
- throw new Error(`targetDir is not a directory: ${targetDir}`);
237
- }
238
- }
239
- catch (err) {
240
- const message = err instanceof Error ? err.message : String(err);
241
- return { ok: false, message, stdout: "", stderr: message, code: null };
242
- }
243
- const archivePath = path.join(targetDir, filename);
244
- let downloaded = 0;
245
- try {
246
- const result = await downloadFile(url, archivePath, timeoutMs);
247
- downloaded = result.bytes;
248
- }
249
- catch (err) {
250
- const message = err instanceof Error ? err.message : String(err);
251
- return { ok: false, message, stdout: "", stderr: message, code: null };
252
- }
253
- const archiveType = resolveArchiveType(spec, filename);
254
- const shouldExtract = spec.extract ?? Boolean(archiveType);
255
- if (!shouldExtract) {
256
- return {
257
- ok: true,
258
- message: `Downloaded to ${archivePath}`,
259
- stdout: `downloaded=${downloaded}`,
260
- stderr: "",
261
- code: 0,
262
- };
263
- }
264
- if (!archiveType) {
265
- return {
266
- ok: false,
267
- message: "extract requested but archive type could not be detected",
268
- stdout: "",
269
- stderr: "",
270
- code: null,
271
- };
272
- }
273
- const extractResult = await extractArchive({
274
- archivePath,
275
- archiveType,
276
- targetDir,
277
- stripComponents: spec.stripComponents,
278
- timeoutMs,
279
- });
280
- const success = extractResult.code === 0;
281
- return {
282
- ok: success,
283
- message: success
284
- ? `Downloaded and extracted to ${targetDir}`
285
- : formatInstallFailureMessage(extractResult),
286
- stdout: extractResult.stdout.trim(),
287
- stderr: extractResult.stderr.trim(),
288
- code: extractResult.code,
289
- };
290
- }
@@ -1,30 +0,0 @@
1
- function summarizeInstallOutput(text) {
2
- const raw = text.trim();
3
- if (!raw) {
4
- return undefined;
5
- }
6
- const lines = raw
7
- .split("\n")
8
- .map((line) => line.trim())
9
- .filter(Boolean);
10
- if (lines.length === 0) {
11
- return undefined;
12
- }
13
- const preferred = lines.find((line) => /^error\b/i.test(line)) ??
14
- lines.find((line) => /\b(err!|error:|failed)\b/i.test(line)) ??
15
- lines.at(-1);
16
- if (!preferred) {
17
- return undefined;
18
- }
19
- const normalized = preferred.replace(/\s+/g, " ").trim();
20
- const maxLen = 200;
21
- return normalized.length > maxLen ? `${normalized.slice(0, maxLen - 1)}…` : normalized;
22
- }
23
- export function formatInstallFailureMessage(result) {
24
- const code = typeof result.code === "number" ? `exit ${result.code}` : "unknown exit";
25
- const summary = summarizeInstallOutput(result.stderr) ?? summarizeInstallOutput(result.stdout);
26
- if (!summary) {
27
- return `Install failed (${code})`;
28
- }
29
- return `Install failed (${code}): ${summary}`;
30
- }
@@ -1,36 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- export function setTempStateDir(workspaceDir) {
4
- const stateDir = path.join(workspaceDir, "state");
5
- process.env.POOLBOT_STATE_DIR = stateDir;
6
- return stateDir;
7
- }
8
- export async function writeDownloadSkill(params) {
9
- const skillDir = path.join(params.workspaceDir, "skills", params.name);
10
- await fs.mkdir(skillDir, { recursive: true });
11
- const meta = {
12
- openclaw: {
13
- install: [
14
- {
15
- id: params.installId,
16
- kind: "download",
17
- url: params.url,
18
- archive: params.archive,
19
- extract: true,
20
- stripComponents: params.stripComponents,
21
- targetDir: params.targetDir,
22
- },
23
- ],
24
- },
25
- };
26
- await fs.writeFile(path.join(skillDir, "SKILL.md"), `---
27
- name: ${params.name}
28
- description: test skill
29
- metadata: ${JSON.stringify(meta)}
30
- ---
31
-
32
- # ${params.name}
33
- `, "utf-8");
34
- await fs.writeFile(path.join(skillDir, "runner.js"), "export {};\n", "utf-8");
35
- return skillDir;
36
- }
@@ -1,13 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- export async function writeSkill(params) {
4
- const { dir, name, description, body } = params;
5
- await fs.mkdir(dir, { recursive: true });
6
- await fs.writeFile(path.join(dir, "SKILL.md"), `---
7
- name: ${name}
8
- description: ${description}
9
- ---
10
-
11
- ${body ?? `# ${name}\n`}
12
- `, "utf-8");
13
- }
@@ -1,160 +0,0 @@
1
- /**
2
- * Subagent Announce Chain Delivery Reliability
3
- *
4
- * Implements:
5
- * - Retry budget with exponential backoff
6
- * - Dedupe of announce completions
7
- * - Delivery params validation
8
- * - Session model override persistence
9
- *
10
- * OpenClaw #32384, #30951
11
- */
12
- import { createSubsystemLogger } from "../logging/subsystem.js";
13
- const log = createSubsystemLogger("subagent-announce");
14
- /**
15
- * Default retry budget: 3 attempts with exponential backoff
16
- */
17
- export const DEFAULT_ANNOUNCE_RETRY_BUDGET = {
18
- maxRetries: 3,
19
- baseDelayMs: 1000,
20
- maxDelayMs: 10_000,
21
- backoffMultiplier: 2,
22
- };
23
- /**
24
- * In-memory store for announce delivery states
25
- */
26
- const announceStates = new Map();
27
- /**
28
- * Cleanup old announce states every 30 minutes
29
- */
30
- setInterval(() => {
31
- const now = Date.now();
32
- const maxAge = 30 * 60 * 1000; // 30 minutes
33
- for (const [id, state] of announceStates.entries()) {
34
- if (now - state.lastAttemptAt > maxAge) {
35
- announceStates.delete(id);
36
- }
37
- }
38
- }, 30 * 60 * 1000).unref();
39
- /**
40
- * Initialize or get announce delivery state
41
- */
42
- export function getOrCreateAnnounceState(announceId) {
43
- const existing = announceStates.get(announceId);
44
- if (existing) {
45
- return existing;
46
- }
47
- const newState = {
48
- announceId,
49
- attempts: 0,
50
- lastAttemptAt: 0,
51
- delivered: false,
52
- retriesExhausted: false,
53
- };
54
- announceStates.set(announceId, newState);
55
- return newState;
56
- }
57
- /**
58
- * Record a delivery attempt and check if retry is allowed
59
- */
60
- export function recordDeliveryAttempt(params) {
61
- const { announceId, success, error, budget = DEFAULT_ANNOUNCE_RETRY_BUDGET } = params;
62
- const state = getOrCreateAnnounceState(announceId);
63
- state.attempts += 1;
64
- state.lastAttemptAt = Date.now();
65
- if (success) {
66
- state.delivered = true;
67
- state.lastError = undefined;
68
- return { canRetry: false, delayMs: 0, attemptsRemaining: 0 };
69
- }
70
- state.lastError = error;
71
- const attemptsRemaining = Math.max(0, budget.maxRetries - state.attempts);
72
- state.retriesExhausted = attemptsRemaining === 0;
73
- if (!state.retriesExhausted) {
74
- // Calculate exponential backoff delay
75
- const exponentialDelay = budget.baseDelayMs * Math.pow(budget.backoffMultiplier, state.attempts - 1);
76
- const delayMs = Math.min(exponentialDelay, budget.maxDelayMs);
77
- log.warn(`Announce delivery attempt ${state.attempts}/${budget.maxRetries} failed, retrying in ${delayMs}ms`, { announceId, error });
78
- return { canRetry: true, delayMs, attemptsRemaining };
79
- }
80
- log.error(`Announce delivery failed after ${state.attempts} attempts (retries exhausted)`, {
81
- announceId,
82
- error,
83
- });
84
- return { canRetry: false, delayMs: 0, attemptsRemaining: 0 };
85
- }
86
- /**
87
- * Check if an announce completion is duplicate (dedupe)
88
- * OpenClaw #32384 - prevents duplicate announce deliveries
89
- */
90
- const completedAnnounces = new Map();
91
- export function isAnnounceDuplicate(announceId) {
92
- return completedAnnounces.has(announceId);
93
- }
94
- export function markAnnounceComplete(announceId) {
95
- completedAnnounces.set(announceId, Date.now());
96
- }
97
- /**
98
- * Cleanup completed announces every hour
99
- */
100
- setInterval(() => {
101
- const now = Date.now();
102
- const maxAge = 60 * 60 * 1000; // 1 hour
103
- for (const [id, timestamp] of completedAnnounces.entries()) {
104
- if (now - timestamp > maxAge) {
105
- completedAnnounces.delete(id);
106
- }
107
- }
108
- }, 60 * 60 * 1000).unref();
109
- /**
110
- * Validate announce delivery parameters
111
- */
112
- export function validateAnnounceDeliveryParams(params) {
113
- const { sessionKey, prompt, origin } = params;
114
- if (!sessionKey || !sessionKey.trim()) {
115
- return { valid: false, reason: "Missing or empty sessionKey" };
116
- }
117
- if (!prompt || !prompt.trim()) {
118
- return { valid: false, reason: "Missing or empty prompt" };
119
- }
120
- // Origin is optional but if provided should be an object
121
- if (origin !== undefined && origin !== null && typeof origin !== "object") {
122
- return { valid: false, reason: "Origin must be an object if provided" };
123
- }
124
- return { valid: true };
125
- }
126
- const modelOverrides = new Map();
127
- /**
128
- * Set a session model override
129
- */
130
- export function setSessionModelOverride(params) {
131
- const { sessionKey, model, ttlMs } = params;
132
- const override = {
133
- sessionKey,
134
- model,
135
- setAt: Date.now(),
136
- expiresAt: ttlMs ? Date.now() + ttlMs : undefined,
137
- };
138
- modelOverrides.set(sessionKey, override);
139
- return override;
140
- }
141
- /**
142
- * Get session model override (if not expired)
143
- */
144
- export function getSessionModelOverride(sessionKey) {
145
- const override = modelOverrides.get(sessionKey);
146
- if (!override) {
147
- return undefined;
148
- }
149
- if (override.expiresAt && Date.now() > override.expiresAt) {
150
- modelOverrides.delete(sessionKey);
151
- return undefined;
152
- }
153
- return override;
154
- }
155
- /**
156
- * Clear session model override
157
- */
158
- export function clearSessionModelOverride(sessionKey) {
159
- return modelOverrides.delete(sessionKey);
160
- }
@@ -1,12 +0,0 @@
1
- import { vi } from "vitest";
2
- const noop = () => { };
3
- vi.mock("../gateway/call.js", () => ({
4
- callGateway: vi.fn(async () => ({
5
- status: "ok",
6
- startedAt: 111,
7
- endedAt: 222,
8
- })),
9
- }));
10
- vi.mock("../infra/agent-events.js", () => ({
11
- onAgentEvent: vi.fn(() => noop),
12
- }));
@@ -1,29 +0,0 @@
1
- const ZERO_USAGE = {
2
- input: 0,
3
- output: 0,
4
- cacheRead: 0,
5
- cacheWrite: 0,
6
- totalTokens: 0,
7
- cost: {
8
- input: 0,
9
- output: 0,
10
- cacheRead: 0,
11
- cacheWrite: 0,
12
- total: 0,
13
- },
14
- };
15
- export function makeAssistantMessageFixture(overrides = {}) {
16
- const errorText = typeof overrides.errorMessage === "string" ? overrides.errorMessage : "error";
17
- return {
18
- role: "assistant",
19
- api: "openai-responses",
20
- provider: "openai",
21
- model: "test-model",
22
- usage: ZERO_USAGE,
23
- timestamp: 0,
24
- stopReason: "error",
25
- errorMessage: errorText,
26
- content: [{ type: "text", text: errorText }],
27
- ...overrides,
28
- };
29
- }
@@ -1 +0,0 @@
1
- import "./fast-tool-stubs.js";
@@ -1,8 +0,0 @@
1
- import { vi } from "vitest";
2
- import { stubTool } from "./fast-tool-stubs.js";
3
- vi.mock("../tools/browser-tool.js", () => ({
4
- createBrowserTool: () => stubTool("browser"),
5
- }));
6
- vi.mock("../tools/canvas-tool.js", () => ({
7
- createCanvasTool: () => stubTool("canvas"),
8
- }));
@@ -1,18 +0,0 @@
1
- import { vi } from "vitest";
2
- export const stubTool = (name) => ({
3
- name,
4
- description: `${name} stub`,
5
- parameters: { type: "object", properties: {} },
6
- execute: vi.fn(),
7
- });
8
- vi.mock("../tools/image-tool.js", () => ({
9
- createImageTool: () => stubTool("image"),
10
- }));
11
- vi.mock("../tools/web-tools.js", () => ({
12
- createWebSearchTool: () => null,
13
- createWebFetchTool: () => null,
14
- }));
15
- vi.mock("../../plugins/tools.js", () => ({
16
- resolvePluginTools: () => [],
17
- getPluginToolMeta: () => undefined,
18
- }));
@@ -1,74 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import { resolveSandboxPath } from "../sandbox-paths.js";
4
- export function createSandboxFsBridgeFromResolver(resolvePath) {
5
- return {
6
- resolvePath: ({ filePath, cwd }) => resolvePath(filePath, cwd),
7
- readFile: async ({ filePath, cwd }) => {
8
- const target = resolvePath(filePath, cwd);
9
- return fs.readFile(target.hostPath);
10
- },
11
- writeFile: async ({ filePath, cwd, data, mkdir = true }) => {
12
- const target = resolvePath(filePath, cwd);
13
- if (mkdir) {
14
- await fs.mkdir(path.dirname(target.hostPath), { recursive: true });
15
- }
16
- const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
17
- await fs.writeFile(target.hostPath, buffer);
18
- },
19
- mkdirp: async ({ filePath, cwd }) => {
20
- const target = resolvePath(filePath, cwd);
21
- await fs.mkdir(target.hostPath, { recursive: true });
22
- },
23
- remove: async ({ filePath, cwd, recursive, force }) => {
24
- const target = resolvePath(filePath, cwd);
25
- await fs.rm(target.hostPath, {
26
- recursive: recursive ?? false,
27
- force: force ?? false,
28
- });
29
- },
30
- rename: async ({ from, to, cwd }) => {
31
- const source = resolvePath(from, cwd);
32
- const target = resolvePath(to, cwd);
33
- await fs.mkdir(path.dirname(target.hostPath), { recursive: true });
34
- await fs.rename(source.hostPath, target.hostPath);
35
- },
36
- stat: async ({ filePath, cwd }) => {
37
- try {
38
- const target = resolvePath(filePath, cwd);
39
- const stats = await fs.stat(target.hostPath);
40
- return {
41
- type: stats.isDirectory() ? "directory" : stats.isFile() ? "file" : "other",
42
- size: stats.size,
43
- mtimeMs: stats.mtimeMs,
44
- };
45
- }
46
- catch (error) {
47
- if (error.code === "ENOENT") {
48
- return null;
49
- }
50
- throw error;
51
- }
52
- },
53
- };
54
- }
55
- export function createHostSandboxFsBridge(rootDir) {
56
- const root = path.resolve(rootDir);
57
- const resolvePath = (filePath, cwd) => {
58
- const resolved = resolveSandboxPath({
59
- filePath,
60
- cwd: cwd ?? root,
61
- root,
62
- });
63
- const relativePath = resolved.relative
64
- ? resolved.relative.split(path.sep).filter(Boolean).join(path.posix.sep)
65
- : "";
66
- const containerPath = relativePath ? path.posix.join("/workspace", relativePath) : "/workspace";
67
- return {
68
- hostPath: resolved.resolved,
69
- relativePath,
70
- containerPath,
71
- };
72
- };
73
- return createSandboxFsBridgeFromResolver(resolvePath);
74
- }