@poolzin/pool-bot 2026.2.21 → 2026.2.22

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 (369) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/agents/api-key-rotation.js +47 -0
  3. package/dist/agents/apply-patch-update.js +19 -9
  4. package/dist/agents/apply-patch.js +72 -47
  5. package/dist/agents/bash-tools.exec.js +141 -559
  6. package/dist/agents/cli-backends.js +49 -6
  7. package/dist/agents/cli-runner/helpers.js +69 -152
  8. package/dist/agents/cli-runner.js +70 -19
  9. package/dist/agents/identity.js +20 -1
  10. package/dist/agents/image-sanitization.js +9 -0
  11. package/dist/agents/live-auth-keys.js +123 -26
  12. package/dist/agents/live-model-filter.js +13 -4
  13. package/dist/agents/model-catalog.js +40 -9
  14. package/dist/agents/model-forward-compat.js +60 -23
  15. package/dist/agents/model-selection.js +134 -41
  16. package/dist/agents/pi-auth-json.js +2 -2
  17. package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
  18. package/dist/agents/pi-embedded-helpers/errors.js +140 -15
  19. package/dist/agents/pi-embedded-helpers/images.js +22 -12
  20. package/dist/agents/pi-embedded-helpers.js +2 -2
  21. package/dist/agents/pi-embedded-runner/abort.js +10 -3
  22. package/dist/agents/pi-embedded-runner/compact.js +230 -32
  23. package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
  24. package/dist/agents/pi-embedded-runner/google.js +109 -19
  25. package/dist/agents/pi-embedded-runner/history.js +35 -17
  26. package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
  27. package/dist/agents/pi-embedded-runner/run/images.js +81 -55
  28. package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
  29. package/dist/agents/pi-embedded-runner/run.js +193 -25
  30. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
  31. package/dist/agents/pi-embedded-runner/runs.js +17 -8
  32. package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
  33. package/dist/agents/pi-embedded-runner.js +1 -1
  34. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
  35. package/dist/agents/pi-embedded-subscribe.js +37 -0
  36. package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
  37. package/dist/agents/pi-model-discovery.js +9 -2
  38. package/dist/agents/pi-tool-definition-adapter.js +60 -8
  39. package/dist/agents/pi-tools.before-tool-call.js +1 -1
  40. package/dist/agents/pi-tools.js +113 -94
  41. package/dist/agents/pi-tools.read.js +337 -38
  42. package/dist/agents/poolbot-tools.js +14 -5
  43. package/dist/agents/sandbox/docker.js +10 -5
  44. package/dist/agents/sandbox/registry.js +96 -46
  45. package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
  46. package/dist/agents/sandbox-paths.js +43 -10
  47. package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
  48. package/dist/agents/session-tool-result-guard.js +39 -39
  49. package/dist/agents/session-transcript-repair.js +36 -33
  50. package/dist/agents/session-write-lock.js +62 -44
  51. package/dist/agents/skills/frontmatter.js +49 -88
  52. package/dist/agents/skills/workspace.js +335 -28
  53. package/dist/agents/subagent-announce.js +508 -174
  54. package/dist/agents/subagent-registry.js +45 -4
  55. package/dist/agents/subagent-spawn.js +16 -33
  56. package/dist/agents/system-prompt-report.js +27 -10
  57. package/dist/agents/system-prompt.js +26 -32
  58. package/dist/agents/tool-call-id.js +69 -17
  59. package/dist/agents/tool-display-common.js +1 -1
  60. package/dist/agents/tool-images.js +64 -31
  61. package/dist/agents/tools/canvas-tool.js +17 -11
  62. package/dist/agents/tools/common.js +37 -19
  63. package/dist/agents/tools/cron-tool.js +40 -38
  64. package/dist/agents/tools/gateway.js +70 -2
  65. package/dist/agents/tools/message-tool.js +181 -40
  66. package/dist/agents/tools/nodes-tool.js +128 -36
  67. package/dist/agents/tools/nodes-utils.js +12 -38
  68. package/dist/agents/tools/session-status-tool.js +24 -71
  69. package/dist/agents/tools/sessions-helpers.js +38 -210
  70. package/dist/agents/tools/sessions-spawn-tool.js +28 -198
  71. package/dist/agents/tools/telegram-actions.js +58 -7
  72. package/dist/agents/tools/web-fetch-utils.js +112 -7
  73. package/dist/agents/tools/web-fetch.js +279 -175
  74. package/dist/agents/tools/web-shared.js +71 -8
  75. package/dist/agents/usage.js +25 -16
  76. package/dist/auto-reply/commands-registry.data.js +85 -11
  77. package/dist/auto-reply/dispatch.js +40 -21
  78. package/dist/auto-reply/reply/abort.js +102 -33
  79. package/dist/auto-reply/reply/commands-core.js +82 -33
  80. package/dist/auto-reply/reply/commands-export-session.js +1 -1
  81. package/dist/auto-reply/reply/commands-info.js +41 -12
  82. package/dist/auto-reply/reply/commands-subagents.js +352 -100
  83. package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
  84. package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
  85. package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
  86. package/dist/auto-reply/reply/inbound-meta.js +12 -1
  87. package/dist/auto-reply/reply/mentions.js +18 -11
  88. package/dist/auto-reply/reply/normalize-reply.js +17 -8
  89. package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
  90. package/dist/auto-reply/reply/session.js +102 -21
  91. package/dist/auto-reply/reply/streaming-directives.js +16 -5
  92. package/dist/auto-reply/status.js +73 -50
  93. package/dist/browser/extension-relay.js +3 -3
  94. package/dist/browser/http-auth.js +1 -1
  95. package/dist/browser/paths.js +2 -2
  96. package/dist/build-info.json +3 -3
  97. package/dist/channels/allowlist-match.js +20 -0
  98. package/dist/channels/allowlists/resolve-utils.js +65 -2
  99. package/dist/channels/chat-type.js +8 -4
  100. package/dist/channels/dock.js +127 -35
  101. package/dist/channels/draft-stream-loop.js +6 -2
  102. package/dist/channels/plugins/actions/telegram.js +42 -18
  103. package/dist/channels/plugins/allowlist-match.js +1 -1
  104. package/dist/channels/plugins/group-mentions.js +51 -41
  105. package/dist/channels/plugins/message-action-names.js +2 -0
  106. package/dist/channels/plugins/message-actions.js +24 -5
  107. package/dist/channels/plugins/normalize/discord.js +26 -4
  108. package/dist/channels/plugins/normalize/signal.js +35 -22
  109. package/dist/channels/plugins/onboarding/helpers.js +8 -26
  110. package/dist/channels/plugins/outbound/imessage.js +15 -14
  111. package/dist/channels/registry.js +20 -7
  112. package/dist/cli/acp-cli.js +7 -5
  113. package/dist/cli/browser-cli-extension.js +25 -12
  114. package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
  115. package/dist/cli/browser-cli-state.js +101 -145
  116. package/dist/cli/command-options.js +28 -0
  117. package/dist/cli/completion-cli.js +6 -6
  118. package/dist/cli/cron-cli/register.cron-add.js +25 -1
  119. package/dist/cli/cron-cli/register.cron-edit.js +44 -0
  120. package/dist/cli/cron-cli/shared.js +7 -1
  121. package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
  122. package/dist/cli/daemon-cli/lifecycle.js +23 -247
  123. package/dist/cli/daemon-cli/register-service-commands.js +25 -4
  124. package/dist/cli/daemon-cli.js +1 -0
  125. package/dist/cli/devices-cli.js +33 -20
  126. package/dist/cli/gateway-cli/register.js +37 -105
  127. package/dist/cli/gateway-cli/run.js +49 -11
  128. package/dist/cli/nodes-camera.js +59 -4
  129. package/dist/cli/nodes-cli/register.camera.js +27 -24
  130. package/dist/cli/nodes-cli/rpc.js +21 -38
  131. package/dist/cli/qr-cli.js +2 -2
  132. package/dist/cli/skills-cli.format.js +2 -2
  133. package/dist/cli/update-cli/progress.js +2 -2
  134. package/dist/cli/update-cli/restart-helper.js +28 -7
  135. package/dist/cli/update-cli/shared.js +7 -7
  136. package/dist/cli/update-cli/status.js +1 -1
  137. package/dist/cli/update-cli/update-command.js +14 -8
  138. package/dist/cli/update-cli/wizard.js +2 -2
  139. package/dist/cli/update-cli.js +21 -1027
  140. package/dist/commands/auth-choice.apply.anthropic.js +10 -2
  141. package/dist/commands/channels/add-mutators.js +3 -35
  142. package/dist/commands/channels/add.js +39 -51
  143. package/dist/commands/config-validation.js +1 -1
  144. package/dist/commands/configure.gateway-auth.js +52 -15
  145. package/dist/commands/configure.gateway.js +84 -40
  146. package/dist/commands/doctor-completion.js +3 -3
  147. package/dist/commands/doctor-config-flow.js +536 -16
  148. package/dist/commands/doctor-gateway-services.js +103 -79
  149. package/dist/commands/doctor-memory-search.js +9 -9
  150. package/dist/commands/doctor-platform-notes.js +57 -30
  151. package/dist/commands/doctor-prompter.js +26 -15
  152. package/dist/commands/doctor-session-locks.js +1 -1
  153. package/dist/commands/doctor.js +21 -9
  154. package/dist/commands/model-picker.js +120 -95
  155. package/dist/commands/models/set.js +2 -21
  156. package/dist/commands/models/shared.js +65 -37
  157. package/dist/commands/onboard-helpers.js +81 -39
  158. package/dist/commands/openai-codex-oauth.js +1 -1
  159. package/dist/commands/sessions.js +52 -53
  160. package/dist/commands/status.summary.js +52 -34
  161. package/dist/commands/test-wizard-helpers.js +2 -2
  162. package/dist/config/defaults.js +79 -42
  163. package/dist/config/group-policy.js +50 -18
  164. package/dist/config/includes.js +37 -10
  165. package/dist/config/schema.help.js +5 -4
  166. package/dist/config/schema.hints.js +2 -2
  167. package/dist/config/schema.labels.js +1 -0
  168. package/dist/config/sessions/group.js +12 -11
  169. package/dist/config/sessions/paths.js +137 -11
  170. package/dist/config/sessions/store.js +185 -65
  171. package/dist/config/sessions/types.js +15 -1
  172. package/dist/config/sessions.js +1 -0
  173. package/dist/config/telegram-custom-commands.js +3 -2
  174. package/dist/config/types.js +2 -0
  175. package/dist/config/zod-schema.agent-defaults.js +6 -27
  176. package/dist/config/zod-schema.agent-runtime.js +171 -79
  177. package/dist/config/zod-schema.providers-core.js +138 -65
  178. package/dist/config/zod-schema.session.js +49 -22
  179. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  180. package/dist/cron/isolated-agent/run.js +224 -57
  181. package/dist/cron/normalize.js +48 -45
  182. package/dist/cron/run-log.js +14 -0
  183. package/dist/cron/service/jobs.js +190 -28
  184. package/dist/cron/service/normalize.js +29 -11
  185. package/dist/cron/service/store.js +30 -44
  186. package/dist/cron/service/timer.js +182 -96
  187. package/dist/cron/service.js +3 -0
  188. package/dist/cron/stagger.js +37 -0
  189. package/dist/daemon/inspect.js +132 -92
  190. package/dist/daemon/runtime-paths.js +25 -4
  191. package/dist/daemon/service-audit.js +47 -16
  192. package/dist/discord/accounts.js +23 -20
  193. package/dist/discord/monitor/agent-components.js +1115 -219
  194. package/dist/discord/monitor/allow-list.js +114 -34
  195. package/dist/discord/monitor/listeners.js +204 -97
  196. package/dist/discord/monitor/message-handler.js +21 -10
  197. package/dist/discord/monitor/message-handler.preflight.js +195 -101
  198. package/dist/discord/monitor/message-handler.process.js +384 -123
  199. package/dist/discord/monitor/message-utils.js +86 -23
  200. package/dist/discord/monitor/native-command.js +77 -57
  201. package/dist/discord/monitor/provider.js +122 -117
  202. package/dist/discord/monitor/reply-context.js +20 -16
  203. package/dist/discord/monitor/reply-delivery.js +40 -8
  204. package/dist/discord/monitor/rest-fetch.js +22 -0
  205. package/dist/discord/monitor/threading.js +117 -24
  206. package/dist/discord/send.js +2 -1
  207. package/dist/discord/send.outbound.js +124 -11
  208. package/dist/discord/send.shared.js +112 -72
  209. package/dist/discord/voice-message.js +3 -3
  210. package/dist/gateway/auth.js +119 -44
  211. package/dist/gateway/call.js +76 -34
  212. package/dist/gateway/channel-health-monitor.js +57 -50
  213. package/dist/gateway/client.js +63 -29
  214. package/dist/gateway/control-ui-contract.js +1 -1
  215. package/dist/gateway/gateway-config-prompts.shared.js +2 -2
  216. package/dist/gateway/net.js +109 -1
  217. package/dist/gateway/protocol/index.js +5 -8
  218. package/dist/gateway/protocol/schema/agent.js +19 -1
  219. package/dist/gateway/protocol/schema/channels.js +21 -0
  220. package/dist/gateway/protocol/schema/cron.js +43 -30
  221. package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
  222. package/dist/gateway/protocol/schema/sessions.js +5 -1
  223. package/dist/gateway/protocol/schema.js +0 -1
  224. package/dist/gateway/server/presence-events.js +12 -0
  225. package/dist/gateway/server/ws-connection/message-handler.js +203 -212
  226. package/dist/gateway/server/ws-connection.js +58 -21
  227. package/dist/gateway/server-broadcast.js +18 -13
  228. package/dist/gateway/server-cron.js +177 -10
  229. package/dist/gateway/server-methods/agent-job.js +131 -38
  230. package/dist/gateway/server-methods/send.js +60 -14
  231. package/dist/gateway/server-methods/sessions.js +160 -96
  232. package/dist/gateway/server-methods/system.js +5 -7
  233. package/dist/gateway/server-methods-list.js +8 -0
  234. package/dist/gateway/server-methods.js +24 -8
  235. package/dist/gateway/server-node-events.js +278 -68
  236. package/dist/gateway/session-utils.fs.js +316 -75
  237. package/dist/gateway/session-utils.js +224 -70
  238. package/dist/gateway/sessions-patch.js +63 -20
  239. package/dist/gateway/test-temp-config.js +1 -1
  240. package/dist/gateway/tools-invoke-http.js +118 -70
  241. package/dist/gateway/ws-log.js +135 -107
  242. package/dist/hooks/frontmatter.js +36 -82
  243. package/dist/hooks/install.js +149 -139
  244. package/dist/hooks/internal-hooks.js +29 -4
  245. package/dist/hooks/plugin-hooks.js +2 -1
  246. package/dist/imessage/monitor/deliver.js +10 -4
  247. package/dist/imessage/monitor/monitor-provider.js +138 -375
  248. package/dist/imessage/monitor/runtime.js +4 -8
  249. package/dist/imessage/send.js +65 -19
  250. package/dist/infra/exec-approvals-allowlist.js +7 -0
  251. package/dist/infra/exec-approvals.js +35 -920
  252. package/dist/infra/exec-safe-bin-trust.js +64 -0
  253. package/dist/infra/heartbeat-runner.js +207 -134
  254. package/dist/infra/heartbeat-wake.js +183 -22
  255. package/dist/infra/install-source-utils.js +47 -0
  256. package/dist/infra/net/ssrf.js +170 -36
  257. package/dist/infra/outbound/deliver.js +224 -58
  258. package/dist/infra/outbound/message-action-spec.js +12 -5
  259. package/dist/infra/outbound/outbound-session.js +27 -25
  260. package/dist/infra/poolbot-root.js +32 -22
  261. package/dist/infra/ports.js +14 -11
  262. package/dist/infra/skills-remote.js +48 -37
  263. package/dist/infra/system-events.js +25 -11
  264. package/dist/infra/system-presence.js +26 -33
  265. package/dist/infra/tmp-poolbot-dir.js +81 -2
  266. package/dist/infra/wsl.js +37 -1
  267. package/dist/line/bot-message-context.js +163 -191
  268. package/dist/logging/subsystem.js +59 -22
  269. package/dist/markdown/ir.js +124 -50
  270. package/dist/media/store.js +1 -1
  271. package/dist/media-understanding/runner.entries.js +42 -25
  272. package/dist/media-understanding/runner.js +53 -488
  273. package/dist/memory/embeddings-gemini.js +53 -38
  274. package/dist/memory/manager-embedding-ops.js +48 -69
  275. package/dist/pairing/pairing-store.js +178 -119
  276. package/dist/plugin-sdk/index.js +34 -6
  277. package/dist/plugins/hooks.js +135 -14
  278. package/dist/plugins/install.js +190 -152
  279. package/dist/polls.js +11 -0
  280. package/dist/routing/resolve-route.js +190 -56
  281. package/dist/routing/session-key.js +38 -22
  282. package/dist/runtime.js +35 -9
  283. package/dist/security/audit-channel.js +1 -1
  284. package/dist/sessions/session-key-utils.js +29 -11
  285. package/dist/shared/frontmatter.js +5 -5
  286. package/dist/shared/node-list-types.js +1 -0
  287. package/dist/shared/string-normalization.js +15 -0
  288. package/dist/signal/monitor/event-handler.js +68 -36
  289. package/dist/signal/send.js +29 -37
  290. package/dist/slack/monitor/allow-list.js +10 -11
  291. package/dist/slack/monitor/commands.js +14 -3
  292. package/dist/slack/monitor/events/interactions.js +4 -4
  293. package/dist/slack/monitor/media.js +224 -16
  294. package/dist/slack/monitor/message-handler/dispatch.js +247 -13
  295. package/dist/slack/monitor/message-handler/prepare.js +128 -45
  296. package/dist/slack/monitor/slash.js +357 -144
  297. package/dist/slack/streaming.js +77 -0
  298. package/dist/telegram/accounts.js +40 -13
  299. package/dist/telegram/allowed-updates.js +3 -0
  300. package/dist/telegram/bot/delivery.js +129 -66
  301. package/dist/telegram/bot/helpers.js +136 -122
  302. package/dist/telegram/bot-handlers.js +600 -339
  303. package/dist/telegram/bot-message-context.js +115 -73
  304. package/dist/telegram/bot-message-dispatch.js +235 -104
  305. package/dist/telegram/bot-native-command-menu.js +3 -1
  306. package/dist/telegram/bot-native-commands.js +213 -193
  307. package/dist/telegram/bot.js +24 -132
  308. package/dist/telegram/draft-stream.js +84 -75
  309. package/dist/telegram/format.js +150 -6
  310. package/dist/telegram/send.js +415 -255
  311. package/dist/telegram/targets.js +21 -2
  312. package/dist/telegram/update-offset-store.js +19 -3
  313. package/dist/terminal/restore.js +5 -2
  314. package/dist/test-utils/fetch-mock.js +5 -0
  315. package/dist/version.js +18 -5
  316. package/dist/web/auto-reply/monitor/broadcast.js +7 -3
  317. package/dist/web/auto-reply/monitor/on-message.js +6 -3
  318. package/dist/web/inbound/media.js +34 -8
  319. package/dist/web/inbound/monitor.js +34 -17
  320. package/dist/web/inbound/send-api.js +18 -17
  321. package/dist/web/outbound.js +12 -5
  322. package/dist/wizard/clack-prompter.js +40 -7
  323. package/extensions/bluebubbles/package.json +1 -1
  324. package/extensions/copilot-proxy/package.json +1 -1
  325. package/extensions/diagnostics-otel/package.json +1 -1
  326. package/extensions/discord/package.json +1 -1
  327. package/extensions/feishu/package.json +1 -1
  328. package/extensions/google-antigravity-auth/package.json +1 -1
  329. package/extensions/google-gemini-cli-auth/package.json +1 -1
  330. package/extensions/googlechat/package.json +1 -1
  331. package/extensions/imessage/package.json +1 -1
  332. package/extensions/irc/package.json +1 -1
  333. package/extensions/line/package.json +1 -1
  334. package/extensions/llm-task/package.json +1 -1
  335. package/extensions/lobster/package.json +1 -1
  336. package/extensions/matrix/CHANGELOG.md +5 -0
  337. package/extensions/matrix/package.json +1 -1
  338. package/extensions/mattermost/package.json +1 -1
  339. package/extensions/memory-core/package.json +1 -1
  340. package/extensions/memory-lancedb/package.json +1 -1
  341. package/extensions/minimax-portal-auth/package.json +1 -1
  342. package/extensions/msteams/CHANGELOG.md +5 -0
  343. package/extensions/msteams/package.json +1 -1
  344. package/extensions/nextcloud-talk/package.json +1 -1
  345. package/extensions/nostr/CHANGELOG.md +5 -0
  346. package/extensions/nostr/package.json +1 -1
  347. package/extensions/open-prose/package.json +1 -1
  348. package/extensions/openai-codex-auth/package.json +1 -1
  349. package/extensions/signal/package.json +1 -1
  350. package/extensions/slack/package.json +1 -1
  351. package/extensions/telegram/package.json +1 -1
  352. package/extensions/tlon/package.json +1 -1
  353. package/extensions/twitch/CHANGELOG.md +5 -0
  354. package/extensions/twitch/package.json +1 -1
  355. package/extensions/voice-call/CHANGELOG.md +5 -0
  356. package/extensions/voice-call/package.json +1 -1
  357. package/extensions/whatsapp/package.json +1 -1
  358. package/extensions/zalo/CHANGELOG.md +5 -0
  359. package/extensions/zalo/package.json +1 -1
  360. package/extensions/zalouser/CHANGELOG.md +5 -0
  361. package/extensions/zalouser/package.json +1 -1
  362. package/package.json +1 -1
  363. package/skills/apple-reminders/SKILL.md +100 -49
  364. package/skills/coding-agent/SKILL.md +34 -28
  365. package/skills/github/SKILL.md +131 -16
  366. package/skills/imsg/SKILL.md +112 -15
  367. package/skills/openhue/SKILL.md +101 -19
  368. package/skills/tmux/SKILL.md +111 -79
  369. package/skills/weather/SKILL.md +88 -25
@@ -1,22 +1,187 @@
1
1
  import { createEditTool, createReadTool, createWriteTool } from "@mariozechner/pi-coding-agent";
2
2
  import { detectMime } from "../media/mime.js";
3
+ import { sniffMimeFromBase64 } from "../media/sniff-mime-from-base64.js";
3
4
  import { assertSandboxPath } from "./sandbox-paths.js";
4
5
  import { sanitizeToolResultImages } from "./tool-images.js";
5
- async function sniffMimeFromBase64(base64) {
6
- const trimmed = base64.trim();
7
- if (!trimmed)
6
+ const DEFAULT_READ_PAGE_MAX_BYTES = 50 * 1024;
7
+ const MAX_ADAPTIVE_READ_MAX_BYTES = 512 * 1024;
8
+ const ADAPTIVE_READ_CONTEXT_SHARE = 0.2;
9
+ const CHARS_PER_TOKEN_ESTIMATE = 4;
10
+ const MAX_ADAPTIVE_READ_PAGES = 8;
11
+ const READ_CONTINUATION_NOTICE_RE = /\n\n\[(?:Showing lines [^\]]*?Use offset=\d+ to continue\.|\d+ more lines in file\. Use offset=\d+ to continue\.)\]\s*$/;
12
+ function clamp(value, min, max) {
13
+ return Math.max(min, Math.min(max, value));
14
+ }
15
+ function resolveAdaptiveReadMaxBytes(options) {
16
+ const contextWindowTokens = options?.modelContextWindowTokens;
17
+ if (typeof contextWindowTokens !== "number" ||
18
+ !Number.isFinite(contextWindowTokens) ||
19
+ contextWindowTokens <= 0) {
20
+ return DEFAULT_READ_PAGE_MAX_BYTES;
21
+ }
22
+ const fromContext = Math.floor(contextWindowTokens * CHARS_PER_TOKEN_ESTIMATE * ADAPTIVE_READ_CONTEXT_SHARE);
23
+ return clamp(fromContext, DEFAULT_READ_PAGE_MAX_BYTES, MAX_ADAPTIVE_READ_MAX_BYTES);
24
+ }
25
+ function formatBytes(bytes) {
26
+ if (bytes >= 1024 * 1024) {
27
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
28
+ }
29
+ if (bytes >= 1024) {
30
+ return `${Math.round(bytes / 1024)}KB`;
31
+ }
32
+ return `${bytes}B`;
33
+ }
34
+ function getToolResultText(result) {
35
+ const content = Array.isArray(result.content) ? result.content : [];
36
+ const textBlocks = content
37
+ .map((block) => {
38
+ if (block &&
39
+ typeof block === "object" &&
40
+ block.type === "text" &&
41
+ typeof block.text === "string") {
42
+ return block.text;
43
+ }
8
44
  return undefined;
9
- const take = Math.min(256, trimmed.length);
10
- const sliceLen = take - (take % 4);
11
- if (sliceLen < 8)
45
+ })
46
+ .filter((value) => typeof value === "string");
47
+ if (textBlocks.length === 0) {
12
48
  return undefined;
13
- try {
14
- const head = Buffer.from(trimmed.slice(0, sliceLen), "base64");
15
- return await detectMime({ buffer: head });
16
49
  }
17
- catch {
18
- return undefined;
50
+ return textBlocks.join("\n");
51
+ }
52
+ function withToolResultText(result, text) {
53
+ const content = Array.isArray(result.content) ? result.content : [];
54
+ let replaced = false;
55
+ const nextContent = content.map((block) => {
56
+ if (!replaced &&
57
+ block &&
58
+ typeof block === "object" &&
59
+ block.type === "text") {
60
+ replaced = true;
61
+ return {
62
+ ...block,
63
+ text,
64
+ };
65
+ }
66
+ return block;
67
+ });
68
+ if (replaced) {
69
+ return {
70
+ ...result,
71
+ content: nextContent,
72
+ };
73
+ }
74
+ const textBlock = { type: "text", text };
75
+ return {
76
+ ...result,
77
+ content: [textBlock],
78
+ };
79
+ }
80
+ function extractReadTruncationDetails(result) {
81
+ const details = result.details;
82
+ if (!details || typeof details !== "object") {
83
+ return null;
84
+ }
85
+ const truncation = details.truncation;
86
+ if (!truncation || typeof truncation !== "object") {
87
+ return null;
88
+ }
89
+ const record = truncation;
90
+ if (record.truncated !== true) {
91
+ return null;
92
+ }
93
+ const outputLinesRaw = record.outputLines;
94
+ const outputLines = typeof outputLinesRaw === "number" && Number.isFinite(outputLinesRaw)
95
+ ? Math.max(0, Math.floor(outputLinesRaw))
96
+ : 0;
97
+ return {
98
+ truncated: true,
99
+ outputLines,
100
+ firstLineExceedsLimit: record.firstLineExceedsLimit === true,
101
+ };
102
+ }
103
+ function stripReadContinuationNotice(text) {
104
+ return text.replace(READ_CONTINUATION_NOTICE_RE, "");
105
+ }
106
+ function stripReadTruncationContentDetails(result) {
107
+ const details = result.details;
108
+ if (!details || typeof details !== "object") {
109
+ return result;
110
+ }
111
+ const detailsRecord = details;
112
+ const truncationRaw = detailsRecord.truncation;
113
+ if (!truncationRaw || typeof truncationRaw !== "object") {
114
+ return result;
115
+ }
116
+ const truncation = truncationRaw;
117
+ if (!Object.prototype.hasOwnProperty.call(truncation, "content")) {
118
+ return result;
119
+ }
120
+ const { content: _content, ...restTruncation } = truncation;
121
+ return {
122
+ ...result,
123
+ details: {
124
+ ...detailsRecord,
125
+ truncation: restTruncation,
126
+ },
127
+ };
128
+ }
129
+ async function executeReadWithAdaptivePaging(params) {
130
+ const userLimit = params.args.limit;
131
+ const hasExplicitLimit = typeof userLimit === "number" && Number.isFinite(userLimit) && userLimit > 0;
132
+ if (hasExplicitLimit) {
133
+ return await params.base.execute(params.toolCallId, params.args, params.signal);
134
+ }
135
+ const offsetRaw = params.args.offset;
136
+ let nextOffset = typeof offsetRaw === "number" && Number.isFinite(offsetRaw) && offsetRaw > 0
137
+ ? Math.floor(offsetRaw)
138
+ : 1;
139
+ let firstResult = null;
140
+ let aggregatedText = "";
141
+ let aggregatedBytes = 0;
142
+ let capped = false;
143
+ let continuationOffset;
144
+ for (let page = 0; page < MAX_ADAPTIVE_READ_PAGES; page += 1) {
145
+ const pageArgs = { ...params.args, offset: nextOffset };
146
+ const pageResult = await params.base.execute(params.toolCallId, pageArgs, params.signal);
147
+ firstResult ??= pageResult;
148
+ const rawText = getToolResultText(pageResult);
149
+ if (typeof rawText !== "string") {
150
+ return pageResult;
151
+ }
152
+ const truncation = extractReadTruncationDetails(pageResult);
153
+ const canContinue = Boolean(truncation?.truncated) &&
154
+ !truncation?.firstLineExceedsLimit &&
155
+ (truncation?.outputLines ?? 0) > 0 &&
156
+ page < MAX_ADAPTIVE_READ_PAGES - 1;
157
+ const pageText = canContinue ? stripReadContinuationNotice(rawText) : rawText;
158
+ const delimiter = aggregatedText ? "\n\n" : "";
159
+ const nextBytes = Buffer.byteLength(`${delimiter}${pageText}`, "utf-8");
160
+ if (aggregatedText && aggregatedBytes + nextBytes > params.maxBytes) {
161
+ capped = true;
162
+ continuationOffset = nextOffset;
163
+ break;
164
+ }
165
+ aggregatedText += `${delimiter}${pageText}`;
166
+ aggregatedBytes += nextBytes;
167
+ if (!canContinue || !truncation) {
168
+ return withToolResultText(pageResult, aggregatedText);
169
+ }
170
+ nextOffset += truncation.outputLines;
171
+ continuationOffset = nextOffset;
172
+ if (aggregatedBytes >= params.maxBytes) {
173
+ capped = true;
174
+ break;
175
+ }
176
+ }
177
+ if (!firstResult) {
178
+ return await params.base.execute(params.toolCallId, params.args, params.signal);
179
+ }
180
+ let finalText = aggregatedText;
181
+ if (capped && continuationOffset) {
182
+ finalText += `\n\n[Read output capped at ${formatBytes(params.maxBytes)} for this call. Use offset=${continuationOffset} to continue.]`;
19
183
  }
184
+ return withToolResultText(firstResult, finalText);
20
185
  }
21
186
  function rewriteReadImageHeader(text, mimeType) {
22
187
  // pi-coding-agent uses: "Read image file [image/png]"
@@ -32,19 +197,22 @@ async function normalizeReadImageResult(result, filePath) {
32
197
  b.type === "image" &&
33
198
  typeof b.data === "string" &&
34
199
  typeof b.mimeType === "string");
35
- if (!image)
200
+ if (!image) {
36
201
  return result;
202
+ }
37
203
  if (!image.data.trim()) {
38
204
  throw new Error(`read: image payload is empty (${filePath})`);
39
205
  }
40
206
  const sniffed = await sniffMimeFromBase64(image.data);
41
- if (!sniffed)
207
+ if (!sniffed) {
42
208
  return result;
209
+ }
43
210
  if (!sniffed.startsWith("image/")) {
44
211
  throw new Error(`read: file looks like ${sniffed} but was treated as ${image.mimeType} (${filePath})`);
45
212
  }
46
- if (sniffed === image.mimeType)
213
+ if (sniffed === image.mimeType) {
47
214
  return result;
215
+ }
48
216
  const nextContent = content.map((block) => {
49
217
  if (block && typeof block === "object" && block.type === "image") {
50
218
  const b = block;
@@ -64,9 +232,16 @@ async function normalizeReadImageResult(result, filePath) {
64
232
  });
65
233
  return { ...result, content: nextContent };
66
234
  }
235
+ const RETRY_GUIDANCE_SUFFIX = " Supply correct parameters before retrying.";
236
+ function parameterValidationError(message) {
237
+ return new Error(`${message}.${RETRY_GUIDANCE_SUFFIX}`);
238
+ }
67
239
  export const CLAUDE_PARAM_GROUPS = {
68
240
  read: [{ keys: ["path", "file_path"], label: "path (path or file_path)" }],
69
- write: [{ keys: ["path", "file_path"], label: "path (path or file_path)" }],
241
+ write: [
242
+ { keys: ["path", "file_path"], label: "path (path or file_path)" },
243
+ { keys: ["content"], label: "content" },
244
+ ],
70
245
  edit: [
71
246
  { keys: ["path", "file_path"], label: "path (path or file_path)" },
72
247
  {
@@ -79,12 +254,61 @@ export const CLAUDE_PARAM_GROUPS = {
79
254
  },
80
255
  ],
81
256
  };
257
+ function extractStructuredText(value, depth = 0) {
258
+ if (depth > 6) {
259
+ return undefined;
260
+ }
261
+ if (typeof value === "string") {
262
+ return value;
263
+ }
264
+ if (Array.isArray(value)) {
265
+ const parts = value
266
+ .map((entry) => extractStructuredText(entry, depth + 1))
267
+ .filter((entry) => typeof entry === "string");
268
+ return parts.length > 0 ? parts.join("") : undefined;
269
+ }
270
+ if (!value || typeof value !== "object") {
271
+ return undefined;
272
+ }
273
+ const record = value;
274
+ if (typeof record.text === "string") {
275
+ return record.text;
276
+ }
277
+ if (typeof record.content === "string") {
278
+ return record.content;
279
+ }
280
+ if (Array.isArray(record.content)) {
281
+ return extractStructuredText(record.content, depth + 1);
282
+ }
283
+ if (Array.isArray(record.parts)) {
284
+ return extractStructuredText(record.parts, depth + 1);
285
+ }
286
+ if (typeof record.value === "string" && record.value.length > 0) {
287
+ const type = typeof record.type === "string" ? record.type.toLowerCase() : "";
288
+ const kind = typeof record.kind === "string" ? record.kind.toLowerCase() : "";
289
+ if (type.includes("text") || kind === "text") {
290
+ return record.value;
291
+ }
292
+ }
293
+ return undefined;
294
+ }
295
+ function normalizeTextLikeParam(record, key) {
296
+ const value = record[key];
297
+ if (typeof value === "string") {
298
+ return;
299
+ }
300
+ const extracted = extractStructuredText(value);
301
+ if (typeof extracted === "string") {
302
+ record[key] = extracted;
303
+ }
304
+ }
82
305
  // Normalize tool parameters from Claude Code conventions to pi-coding-agent conventions.
83
306
  // Claude Code uses file_path/old_string/new_string while pi-coding-agent uses path/oldText/newText.
84
307
  // This prevents models trained on Claude Code from getting stuck in tool-call loops.
85
308
  export function normalizeToolParams(params) {
86
- if (!params || typeof params !== "object")
309
+ if (!params || typeof params !== "object") {
87
310
  return undefined;
311
+ }
88
312
  const record = params;
89
313
  const normalized = { ...record };
90
314
  // file_path → path (read, write, edit)
@@ -102,6 +326,11 @@ export function normalizeToolParams(params) {
102
326
  normalized.newText = normalized.new_string;
103
327
  delete normalized.new_string;
104
328
  }
329
+ // Some providers/models emit text payloads as structured blocks instead of raw strings.
330
+ // Normalize these for write/edit so content matching and writes stay deterministic.
331
+ normalizeTextLikeParam(normalized, "content");
332
+ normalizeTextLikeParam(normalized, "oldText");
333
+ normalizeTextLikeParam(normalized, "newText");
105
334
  return normalized;
106
335
  }
107
336
  export function patchToolSchemaForClaudeCompatibility(tool) {
@@ -122,8 +351,9 @@ export function patchToolSchemaForClaudeCompatibility(tool) {
122
351
  { original: "newText", alias: "new_string" },
123
352
  ];
124
353
  for (const { original, alias } of aliasPairs) {
125
- if (!(original in properties))
354
+ if (!(original in properties)) {
126
355
  continue;
356
+ }
127
357
  if (!(alias in properties)) {
128
358
  properties[alias] = properties[original];
129
359
  changed = true;
@@ -134,37 +364,47 @@ export function patchToolSchemaForClaudeCompatibility(tool) {
134
364
  changed = true;
135
365
  }
136
366
  }
137
- if (!changed)
367
+ if (!changed) {
138
368
  return tool;
369
+ }
139
370
  return {
140
371
  ...tool,
141
372
  parameters: {
142
373
  ...schema,
143
374
  properties,
144
- ...(required.length > 0 ? { required } : {}),
375
+ required,
145
376
  },
146
377
  };
147
378
  }
148
379
  export function assertRequiredParams(record, groups, toolName) {
149
380
  if (!record || typeof record !== "object") {
150
- throw new Error(`Missing parameters for ${toolName}`);
381
+ throw parameterValidationError(`Missing parameters for ${toolName}`);
151
382
  }
383
+ const missingLabels = [];
152
384
  for (const group of groups) {
153
385
  const satisfied = group.keys.some((key) => {
154
- if (!(key in record))
386
+ if (!(key in record)) {
155
387
  return false;
388
+ }
156
389
  const value = record[key];
157
- if (typeof value !== "string")
390
+ if (typeof value !== "string") {
158
391
  return false;
159
- if (group.allowEmpty)
392
+ }
393
+ if (group.allowEmpty) {
160
394
  return true;
395
+ }
161
396
  return value.trim().length > 0;
162
397
  });
163
398
  if (!satisfied) {
164
399
  const label = group.label ?? group.keys.join(" or ");
165
- throw new Error(`Missing required parameter: ${label}`);
400
+ missingLabels.push(label);
166
401
  }
167
402
  }
403
+ if (missingLabels.length > 0) {
404
+ const joined = missingLabels.join(", ");
405
+ const noun = missingLabels.length === 1 ? "parameter" : "parameters";
406
+ throw parameterValidationError(`Missing required ${noun}: ${joined}`);
407
+ }
168
408
  }
169
409
  // Generic wrapper to normalize parameters for any tool
170
410
  export function wrapToolParamNormalization(tool, requiredParamGroups) {
@@ -182,7 +422,7 @@ export function wrapToolParamNormalization(tool, requiredParamGroups) {
182
422
  },
183
423
  };
184
424
  }
185
- function wrapSandboxPathGuard(tool, root) {
425
+ export function wrapToolWorkspaceRootGuard(tool, root) {
186
426
  return {
187
427
  ...tool,
188
428
  execute: async (toolCallId, args, signal, onUpdate) => {
@@ -197,19 +437,28 @@ function wrapSandboxPathGuard(tool, root) {
197
437
  },
198
438
  };
199
439
  }
200
- export function createSandboxedReadTool(root) {
201
- const base = createReadTool(root);
202
- return wrapSandboxPathGuard(createPoolbotReadTool(base), root);
440
+ export function createSandboxedReadTool(params) {
441
+ const base = createReadTool(params.root, {
442
+ operations: createSandboxReadOperations(params),
443
+ });
444
+ return createPoolbotReadTool(base, {
445
+ modelContextWindowTokens: params.modelContextWindowTokens,
446
+ imageSanitization: params.imageSanitization,
447
+ });
203
448
  }
204
- export function createSandboxedWriteTool(root) {
205
- const base = createWriteTool(root);
206
- return wrapSandboxPathGuard(wrapToolParamNormalization(base, CLAUDE_PARAM_GROUPS.write), root);
449
+ export function createSandboxedWriteTool(params) {
450
+ const base = createWriteTool(params.root, {
451
+ operations: createSandboxWriteOperations(params),
452
+ });
453
+ return wrapToolParamNormalization(base, CLAUDE_PARAM_GROUPS.write);
207
454
  }
208
- export function createSandboxedEditTool(root) {
209
- const base = createEditTool(root);
210
- return wrapSandboxPathGuard(wrapToolParamNormalization(base, CLAUDE_PARAM_GROUPS.edit), root);
455
+ export function createSandboxedEditTool(params) {
456
+ const base = createEditTool(params.root, {
457
+ operations: createSandboxEditOperations(params),
458
+ });
459
+ return wrapToolParamNormalization(base, CLAUDE_PARAM_GROUPS.edit);
211
460
  }
212
- export function createPoolbotReadTool(base) {
461
+ export function createPoolbotReadTool(base, options) {
213
462
  const patched = patchToolSchemaForClaudeCompatibility(base);
214
463
  return {
215
464
  ...patched,
@@ -218,10 +467,60 @@ export function createPoolbotReadTool(base) {
218
467
  const record = normalized ??
219
468
  (params && typeof params === "object" ? params : undefined);
220
469
  assertRequiredParams(record, CLAUDE_PARAM_GROUPS.read, base.name);
221
- const result = (await base.execute(toolCallId, normalized ?? params, signal));
470
+ const result = await executeReadWithAdaptivePaging({
471
+ base,
472
+ toolCallId,
473
+ args: (normalized ?? params ?? {}),
474
+ signal,
475
+ maxBytes: resolveAdaptiveReadMaxBytes(options),
476
+ });
222
477
  const filePath = typeof record?.path === "string" ? String(record.path) : "<unknown>";
223
- const normalizedResult = await normalizeReadImageResult(result, filePath);
224
- return sanitizeToolResultImages(normalizedResult, `read:${filePath}`);
478
+ const strippedDetailsResult = stripReadTruncationContentDetails(result);
479
+ const normalizedResult = await normalizeReadImageResult(strippedDetailsResult, filePath);
480
+ return sanitizeToolResultImages(normalizedResult, `read:${filePath}`, options?.imageSanitization);
225
481
  },
226
482
  };
227
483
  }
484
+ function createSandboxReadOperations(params) {
485
+ return {
486
+ readFile: (absolutePath) => params.bridge.readFile({ filePath: absolutePath, cwd: params.root }),
487
+ access: async (absolutePath) => {
488
+ const stat = await params.bridge.stat({ filePath: absolutePath, cwd: params.root });
489
+ if (!stat) {
490
+ throw createFsAccessError("ENOENT", absolutePath);
491
+ }
492
+ },
493
+ detectImageMimeType: async (absolutePath) => {
494
+ const buffer = await params.bridge.readFile({ filePath: absolutePath, cwd: params.root });
495
+ const mime = await detectMime({ buffer, filePath: absolutePath });
496
+ return mime && mime.startsWith("image/") ? mime : undefined;
497
+ },
498
+ };
499
+ }
500
+ function createSandboxWriteOperations(params) {
501
+ return {
502
+ mkdir: async (dir) => {
503
+ await params.bridge.mkdirp({ filePath: dir, cwd: params.root });
504
+ },
505
+ writeFile: async (absolutePath, content) => {
506
+ await params.bridge.writeFile({ filePath: absolutePath, cwd: params.root, data: content });
507
+ },
508
+ };
509
+ }
510
+ function createSandboxEditOperations(params) {
511
+ return {
512
+ readFile: (absolutePath) => params.bridge.readFile({ filePath: absolutePath, cwd: params.root }),
513
+ writeFile: (absolutePath, content) => params.bridge.writeFile({ filePath: absolutePath, cwd: params.root, data: content }),
514
+ access: async (absolutePath) => {
515
+ const stat = await params.bridge.stat({ filePath: absolutePath, cwd: params.root });
516
+ if (!stat) {
517
+ throw createFsAccessError("ENOENT", absolutePath);
518
+ }
519
+ },
520
+ };
521
+ }
522
+ function createFsAccessError(code, filePath) {
523
+ const error = new Error(`Sandbox FS error (${code}): ${filePath}`);
524
+ error.code = code;
525
+ return error;
526
+ }
@@ -4,8 +4,8 @@ import { createAgentsListTool } from "./tools/agents-list-tool.js";
4
4
  import { createBrowserTool } from "./tools/browser-tool.js";
5
5
  import { createCanvasTool } from "./tools/canvas-tool.js";
6
6
  import { createCronTool } from "./tools/cron-tool.js";
7
- import { createGatewayTool } from "./tools/gateway-tool.js";
8
7
  import { createDeepResearchTool } from "./tools/deep-research-tool.js";
8
+ import { createGatewayTool } from "./tools/gateway-tool.js";
9
9
  import { createImageGenerateTool } from "./tools/image-generate-tool.js";
10
10
  import { createImageTool } from "./tools/image-tool.js";
11
11
  import { createMessageTool } from "./tools/message-tool.js";
@@ -15,14 +15,20 @@ import { createSessionsHistoryTool } from "./tools/sessions-history-tool.js";
15
15
  import { createSessionsListTool } from "./tools/sessions-list-tool.js";
16
16
  import { createSessionsSendTool } from "./tools/sessions-send-tool.js";
17
17
  import { createSessionsSpawnTool } from "./tools/sessions-spawn-tool.js";
18
- import { createWebFetchTool, createWebSearchTool } from "./tools/web-tools.js";
18
+ import { createSubagentsTool } from "./tools/subagents-tool.js";
19
19
  import { createTtsTool } from "./tools/tts-tool.js";
20
+ import { createWebFetchTool, createWebSearchTool } from "./tools/web-tools.js";
21
+ import { resolveWorkspaceRoot } from "./workspace-dir.js";
20
22
  export function createPoolBotTools(options) {
23
+ const workspaceDir = resolveWorkspaceRoot(options?.workspaceDir);
21
24
  const imageTool = options?.agentDir?.trim()
22
25
  ? createImageTool({
23
26
  config: options?.config,
24
27
  agentDir: options.agentDir,
25
- sandboxRoot: options?.sandboxRoot,
28
+ workspaceDir,
29
+ sandbox: options?.sandboxRoot && options?.sandboxFsBridge
30
+ ? { root: options.sandboxRoot, bridge: options.sandboxFsBridge }
31
+ : undefined,
26
32
  modelHasVision: options?.modelHasVision,
27
33
  })
28
34
  : null;
@@ -58,7 +64,7 @@ export function createPoolBotTools(options) {
58
64
  sandboxBridgeUrl: options?.sandboxBrowserBridgeUrl,
59
65
  allowHostControl: options?.allowHostBrowserControl,
60
66
  }),
61
- createCanvasTool(),
67
+ createCanvasTool({ config: options?.config }),
62
68
  createNodesTool({
63
69
  agentSessionKey: options?.agentSessionKey,
64
70
  config: options?.config,
@@ -104,6 +110,9 @@ export function createPoolBotTools(options) {
104
110
  sandboxed: options?.sandboxed,
105
111
  requesterAgentIdOverride: options?.requesterAgentIdOverride,
106
112
  }),
113
+ createSubagentsTool({
114
+ agentSessionKey: options?.agentSessionKey,
115
+ }),
107
116
  createSessionStatusTool({
108
117
  agentSessionKey: options?.agentSessionKey,
109
118
  config: options?.config,
@@ -124,7 +133,7 @@ export function createPoolBotTools(options) {
124
133
  const pluginTools = resolvePluginTools({
125
134
  context: {
126
135
  config: options?.config,
127
- workspaceDir: options?.workspaceDir,
136
+ workspaceDir,
128
137
  agentDir: options?.agentDir,
129
138
  agentId: resolveSessionAgentId({
130
139
  sessionKey: options?.agentSessionKey,
@@ -1,4 +1,5 @@
1
1
  import { spawn } from "node:child_process";
2
+ import { sanitizeEnvVars } from "./sanitize-env-vars.js";
2
3
  function createAbortError() {
3
4
  const err = new Error("Aborted");
4
5
  err.name = "AbortError";
@@ -209,11 +210,15 @@ export function buildSandboxCreateArgs(params) {
209
210
  if (params.cfg.user) {
210
211
  args.push("--user", params.cfg.user);
211
212
  }
212
- for (const [key, value] of Object.entries(params.cfg.env ?? {})) {
213
- if (!key.trim()) {
214
- continue;
215
- }
216
- args.push("--env", key + "=" + value);
213
+ const envSanitization = sanitizeEnvVars(params.cfg.env ?? {});
214
+ if (envSanitization.blocked.length > 0) {
215
+ console.warn("[Security] Blocked sensitive environment variables:", envSanitization.blocked.join(", "));
216
+ }
217
+ if (envSanitization.warnings.length > 0) {
218
+ console.warn("[Security] Suspicious environment variables:", envSanitization.warnings);
219
+ }
220
+ for (const [key, value] of Object.entries(envSanitization.allowed)) {
221
+ args.push("--env", `${key}=${value}`);
217
222
  }
218
223
  for (const cap of params.cfg.capDrop) {
219
224
  args.push("--cap-drop", cap);