@poolzin/pool-bot 2026.1.39 → 2026.2.1

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 (511) hide show
  1. package/assets/chrome-extension/README.md +3 -3
  2. package/assets/chrome-extension/background.js +5 -5
  3. package/assets/chrome-extension/manifest.json +3 -3
  4. package/assets/chrome-extension/options.html +4 -4
  5. package/assets/chrome-extension/options.js +1 -1
  6. package/dist/acp/client.js +3 -3
  7. package/dist/acp/types.js +1 -1
  8. package/dist/agents/agent-paths.js +3 -3
  9. package/dist/agents/auth-profiles/paths.js +3 -3
  10. package/dist/agents/bash-tools.exec.js +76 -25
  11. package/dist/agents/cli-runner/helpers.js +10 -12
  12. package/dist/agents/cli-runner.js +2 -2
  13. package/dist/agents/cloudflare-ai-gateway.js +31 -0
  14. package/dist/agents/compaction.js +16 -2
  15. package/dist/agents/context-window-guard.js +13 -10
  16. package/dist/agents/context.js +4 -4
  17. package/dist/agents/docs-path.js +1 -1
  18. package/dist/agents/identity.js +47 -7
  19. package/dist/agents/memory-search.js +25 -8
  20. package/dist/agents/minimax-vlm.js +1 -1
  21. package/dist/agents/model-auth.js +12 -1
  22. package/dist/agents/model-catalog.js +4 -4
  23. package/dist/agents/model-selection.js +31 -4
  24. package/dist/agents/models-config.js +3 -3
  25. package/dist/agents/models-config.providers.js +147 -39
  26. package/dist/agents/pi-embedded-block-chunker.js +117 -42
  27. package/dist/agents/pi-embedded-helpers/errors.js +183 -78
  28. package/dist/agents/pi-embedded-helpers/openai.js +1 -1
  29. package/dist/agents/pi-embedded-helpers.js +1 -1
  30. package/dist/agents/pi-embedded-runner/compact.js +9 -8
  31. package/dist/agents/pi-embedded-runner/model.js +63 -4
  32. package/dist/agents/pi-embedded-runner/run/attempt.js +27 -17
  33. package/dist/agents/pi-embedded-runner/run.js +203 -50
  34. package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
  35. package/dist/agents/pi-embedded-runner/tool-result-truncation.js +275 -0
  36. package/dist/agents/pi-embedded-runner/utils.js +1 -1
  37. package/dist/agents/pi-embedded-subscribe.js +118 -29
  38. package/dist/agents/pi-model-discovery.js +10 -0
  39. package/dist/agents/pi-tool-definition-adapter.js +50 -9
  40. package/dist/agents/pi-tools.before-tool-call.js +67 -0
  41. package/dist/agents/pi-tools.js +20 -10
  42. package/dist/agents/pi-tools.read.js +2 -2
  43. package/dist/agents/poolbot-tools.js +15 -10
  44. package/dist/agents/sandbox-paths.js +31 -0
  45. package/dist/agents/session-file-repair.js +83 -0
  46. package/dist/agents/session-tool-result-guard.js +94 -15
  47. package/dist/agents/session-transcript-repair.js +68 -0
  48. package/dist/agents/shell-utils.js +51 -0
  49. package/dist/agents/skills/bundled-context.js +23 -0
  50. package/dist/agents/skills/bundled-dir.js +41 -7
  51. package/dist/agents/skills/frontmatter.js +1 -1
  52. package/dist/agents/skills/workspace.js +2 -2
  53. package/dist/agents/skills-install.js +60 -23
  54. package/dist/agents/subagent-announce.js +79 -34
  55. package/dist/agents/system-prompt.js +28 -4
  56. package/dist/agents/together-models.js +127 -0
  57. package/dist/agents/tool-images.js +1 -1
  58. package/dist/agents/tool-policy.conformance.js +14 -0
  59. package/dist/agents/tool-policy.js +25 -1
  60. package/dist/agents/tools/browser-tool.js +3 -3
  61. package/dist/agents/tools/cron-tool.js +166 -19
  62. package/dist/agents/tools/discord-actions-presence.js +78 -0
  63. package/dist/agents/tools/image-tool.js +2 -2
  64. package/dist/agents/tools/memory-tool.js +93 -5
  65. package/dist/agents/tools/message-tool.js +56 -2
  66. package/dist/agents/tools/sessions-history-tool.js +69 -1
  67. package/dist/agents/tools/web-search.js +211 -42
  68. package/dist/agents/usage.js +23 -1
  69. package/dist/agents/workspace-run.js +67 -0
  70. package/dist/agents/workspace-templates.js +44 -0
  71. package/dist/auto-reply/command-auth.js +121 -6
  72. package/dist/auto-reply/commands-registry.data.js +1 -1
  73. package/dist/auto-reply/envelope.js +50 -72
  74. package/dist/auto-reply/reply/commands-compact.js +1 -0
  75. package/dist/auto-reply/reply/commands-context-report.js +3 -2
  76. package/dist/auto-reply/reply/commands-context.js +1 -0
  77. package/dist/auto-reply/reply/commands-models.js +107 -60
  78. package/dist/auto-reply/reply/commands-ptt.js +171 -0
  79. package/dist/auto-reply/reply/commands-session.js +2 -2
  80. package/dist/auto-reply/reply/get-reply-run.js +16 -5
  81. package/dist/auto-reply/reply/groups.js +1 -1
  82. package/dist/auto-reply/reply/inbound-context.js +9 -1
  83. package/dist/auto-reply/reply/inbound-meta.js +130 -0
  84. package/dist/auto-reply/reply/model-selection.js +3 -3
  85. package/dist/auto-reply/reply/untrusted-context.js +15 -0
  86. package/dist/auto-reply/status.js +1 -1
  87. package/dist/auto-reply/thinking.js +88 -43
  88. package/dist/browser/bridge-server.js +13 -0
  89. package/dist/browser/cdp.helpers.js +38 -24
  90. package/dist/browser/client-fetch.js +51 -8
  91. package/dist/browser/config.js +2 -11
  92. package/dist/browser/extension-relay.js +104 -43
  93. package/dist/browser/pw-ai.js +1 -1
  94. package/dist/browser/pw-session.js +143 -8
  95. package/dist/browser/pw-tools-core.interactions.js +125 -27
  96. package/dist/browser/pw-tools-core.responses.js +1 -1
  97. package/dist/browser/pw-tools-core.state.js +1 -1
  98. package/dist/browser/routes/agent.act.js +86 -41
  99. package/dist/browser/routes/dispatcher.js +4 -4
  100. package/dist/browser/screenshot.js +1 -1
  101. package/dist/browser/server-context.js +2 -2
  102. package/dist/browser/server.js +13 -0
  103. package/dist/build-info.json +3 -3
  104. package/dist/canvas-host/a2ui.js +3 -3
  105. package/dist/channels/plugins/catalog.js +2 -2
  106. package/dist/channels/plugins/onboarding/imessage.js +1 -1
  107. package/dist/channels/plugins/onboarding/signal.js +1 -1
  108. package/dist/channels/plugins/onboarding/slack.js +4 -4
  109. package/dist/channels/plugins/onboarding/whatsapp.js +3 -3
  110. package/dist/channels/plugins/pairing-message.js +1 -1
  111. package/dist/channels/reply-prefix.js +8 -1
  112. package/dist/cli/browser-cli-extension.js +2 -2
  113. package/dist/cli/cron-cli/register.cron-add.js +61 -40
  114. package/dist/cli/cron-cli/register.cron-edit.js +60 -34
  115. package/dist/cli/cron-cli/shared.js +56 -41
  116. package/dist/cli/dns-cli.js +26 -14
  117. package/dist/cli/docs-cli.js +1 -1
  118. package/dist/cli/gateway-cli/dev.js +1 -1
  119. package/dist/cli/gateway-cli/register.js +37 -19
  120. package/dist/cli/memory-cli.js +30 -20
  121. package/dist/cli/nodes-cli/register.canvas.js +1 -1
  122. package/dist/cli/parse-bytes.js +37 -0
  123. package/dist/cli/plugins-cli.js +1 -1
  124. package/dist/cli/run-main.js +2 -2
  125. package/dist/cli/security-cli.js +1 -1
  126. package/dist/cli/tagline.js +1 -1
  127. package/dist/cli/update-cli.js +173 -52
  128. package/dist/cli/webhooks-cli.js +5 -5
  129. package/dist/commands/agent.js +1 -0
  130. package/dist/commands/agents.commands.add.js +1 -1
  131. package/dist/commands/auth-choice.apply.api-providers.js +305 -17
  132. package/dist/commands/auth-choice.apply.js +4 -1
  133. package/dist/commands/auth-choice.apply.plugin-provider.js +2 -2
  134. package/dist/commands/auth-choice.apply.xai.js +63 -0
  135. package/dist/commands/auth-choice.preferred-provider.js +7 -1
  136. package/dist/commands/configure.wizard.js +1 -1
  137. package/dist/commands/dashboard.js +1 -1
  138. package/dist/commands/docs.js +1 -1
  139. package/dist/commands/doctor-config-flow.js +61 -5
  140. package/dist/commands/doctor-gateway-services.js +3 -3
  141. package/dist/commands/doctor-state-migrations.js +1 -1
  142. package/dist/commands/doctor-update.js +3 -3
  143. package/dist/commands/doctor.js +1 -1
  144. package/dist/commands/health.js +1 -1
  145. package/dist/commands/model-allowlist.js +29 -0
  146. package/dist/commands/model-picker.js +2 -1
  147. package/dist/commands/models/list.probe.js +2 -2
  148. package/dist/commands/models/list.registry.js +4 -4
  149. package/dist/commands/models/list.status-command.js +44 -24
  150. package/dist/commands/models/shared.js +15 -0
  151. package/dist/commands/onboard-auth.config-core.js +366 -28
  152. package/dist/commands/onboard-auth.credentials.js +71 -9
  153. package/dist/commands/onboard-auth.js +3 -3
  154. package/dist/commands/onboard-auth.models.js +26 -24
  155. package/dist/commands/onboard-custom.js +384 -0
  156. package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +35 -0
  157. package/dist/commands/onboard-non-interactive/local/auth-choice.js +146 -9
  158. package/dist/commands/onboard-skills.js +63 -38
  159. package/dist/commands/openai-model-default.js +41 -0
  160. package/dist/commands/status-all/report-lines.js +1 -1
  161. package/dist/commands/status.command.js +1 -1
  162. package/dist/commands/uninstall.js +3 -3
  163. package/dist/compat/legacy-names.js +1 -1
  164. package/dist/config/defaults.js +3 -2
  165. package/dist/config/io.js +3 -3
  166. package/dist/config/paths.js +136 -35
  167. package/dist/config/plugin-auto-enable.js +21 -5
  168. package/dist/config/redact-snapshot.js +153 -0
  169. package/dist/config/schema.field-metadata.js +590 -0
  170. package/dist/config/schema.js +3 -3
  171. package/dist/config/sessions/store.js +291 -23
  172. package/dist/config/types.memory.js +1 -0
  173. package/dist/config/version.js +4 -4
  174. package/dist/config/zod-schema.agent-defaults.js +3 -0
  175. package/dist/config/zod-schema.agent-runtime.js +13 -2
  176. package/dist/config/zod-schema.providers-core.js +142 -0
  177. package/dist/config/zod-schema.session.js +3 -0
  178. package/dist/cron/delivery.js +57 -0
  179. package/dist/cron/isolated-agent/delivery-target.js +18 -3
  180. package/dist/cron/isolated-agent/helpers.js +22 -5
  181. package/dist/cron/isolated-agent/run.js +171 -63
  182. package/dist/cron/isolated-agent/session.js +2 -0
  183. package/dist/cron/normalize.js +356 -28
  184. package/dist/cron/parse.js +10 -5
  185. package/dist/cron/run-log.js +35 -10
  186. package/dist/cron/schedule.js +41 -6
  187. package/dist/cron/service/jobs.js +208 -35
  188. package/dist/cron/service/ops.js +72 -16
  189. package/dist/cron/service/state.js +2 -0
  190. package/dist/cron/service/store.js +386 -14
  191. package/dist/cron/service/timer.js +390 -147
  192. package/dist/cron/session-reaper.js +86 -0
  193. package/dist/cron/store.js +23 -8
  194. package/dist/cron/validate-timestamp.js +43 -0
  195. package/dist/daemon/constants.js +7 -7
  196. package/dist/daemon/inspect.js +6 -6
  197. package/dist/daemon/systemd-unit.js +1 -1
  198. package/dist/discord/monitor/agent-components.js +438 -0
  199. package/dist/discord/monitor/allow-list.js +28 -5
  200. package/dist/discord/monitor/gateway-registry.js +29 -0
  201. package/dist/discord/monitor/native-command.js +44 -23
  202. package/dist/discord/monitor/sender-identity.js +45 -0
  203. package/dist/discord/pluralkit.js +27 -0
  204. package/dist/discord/send.outbound.js +92 -5
  205. package/dist/discord/send.shared.js +60 -23
  206. package/dist/discord/targets.js +84 -1
  207. package/dist/entry.js +15 -9
  208. package/dist/extensionAPI.js +8 -0
  209. package/dist/gateway/control-ui.js +8 -1
  210. package/dist/gateway/hooks-mapping.js +3 -0
  211. package/dist/gateway/hooks.js +65 -0
  212. package/dist/gateway/live-image-probe.js +1 -66
  213. package/dist/gateway/net.js +96 -31
  214. package/dist/gateway/node-command-policy.js +50 -15
  215. package/dist/gateway/openai-http.js +2 -2
  216. package/dist/gateway/openresponses-http.js +4 -4
  217. package/dist/gateway/origin-check.js +56 -0
  218. package/dist/gateway/protocol/client-info.js +9 -0
  219. package/dist/gateway/protocol/index.js +9 -2
  220. package/dist/gateway/protocol/schema/agents-models-skills.js +71 -1
  221. package/dist/gateway/protocol/schema/cron.js +22 -10
  222. package/dist/gateway/protocol/schema/protocol-schemas.js +16 -2
  223. package/dist/gateway/protocol/schema/sessions.js +12 -0
  224. package/dist/gateway/server/hooks.js +1 -1
  225. package/dist/gateway/server-broadcast.js +26 -9
  226. package/dist/gateway/server-chat.js +112 -23
  227. package/dist/gateway/server-discovery-runtime.js +10 -2
  228. package/dist/gateway/server-discovery.js +2 -2
  229. package/dist/gateway/server-http.js +110 -12
  230. package/dist/gateway/server-methods/agent-timestamp.js +60 -0
  231. package/dist/gateway/server-methods/agents.js +321 -2
  232. package/dist/gateway/server-methods/usage.js +559 -16
  233. package/dist/gateway/server-runtime-state.js +22 -8
  234. package/dist/gateway/server-startup-memory.js +16 -0
  235. package/dist/gateway/server.impl.js +7 -3
  236. package/dist/gateway/session-utils.fs.js +23 -25
  237. package/dist/gateway/session-utils.js +20 -10
  238. package/dist/gateway/sessions-patch.js +7 -22
  239. package/dist/gateway/test-helpers.server.js +35 -2
  240. package/dist/hooks/frontmatter.js +1 -1
  241. package/dist/hooks/hooks-status.js +1 -1
  242. package/dist/hooks/install.js +2 -2
  243. package/dist/hooks/loader.js +1 -1
  244. package/dist/hooks/workspace.js +3 -3
  245. package/dist/imessage/constants.js +2 -0
  246. package/dist/imessage/monitor/deliver.js +4 -1
  247. package/dist/imessage/monitor/monitor-provider.js +51 -1
  248. package/dist/index.js +2 -2
  249. package/dist/infra/bonjour-discovery.js +131 -70
  250. package/dist/infra/bonjour.js +3 -3
  251. package/dist/infra/control-ui-assets.js +134 -12
  252. package/dist/infra/errors.js +12 -0
  253. package/dist/infra/exec-approvals.js +266 -57
  254. package/dist/infra/format-time/format-datetime.js +79 -0
  255. package/dist/infra/format-time/format-duration.js +81 -0
  256. package/dist/infra/format-time/format-relative.js +80 -0
  257. package/dist/infra/heartbeat-runner.js +140 -49
  258. package/dist/infra/home-dir.js +54 -0
  259. package/dist/infra/net/fetch-guard.js +122 -0
  260. package/dist/infra/net/ssrf.js +65 -29
  261. package/dist/infra/outbound/abort.js +14 -0
  262. package/dist/infra/outbound/message-action-runner.js +77 -13
  263. package/dist/infra/outbound/outbound-session.js +143 -37
  264. package/dist/infra/path-env.js +3 -3
  265. package/dist/infra/poolbot-root.js +43 -1
  266. package/dist/infra/provider-usage.fetch.minimax.js +1 -1
  267. package/dist/infra/restart.js +1 -1
  268. package/dist/infra/session-cost-usage.js +631 -41
  269. package/dist/infra/state-migrations.js +317 -47
  270. package/dist/infra/tailscale.js +1 -1
  271. package/dist/infra/update-global.js +35 -0
  272. package/dist/infra/update-runner.js +149 -43
  273. package/dist/infra/warning-filter.js +65 -0
  274. package/dist/infra/widearea-dns.js +30 -9
  275. package/dist/logging/redact-identifier.js +12 -0
  276. package/dist/macos/relay.js +2 -2
  277. package/dist/media/fetch.js +81 -58
  278. package/dist/media/input-files.js +1 -1
  279. package/dist/media/mime.js +4 -0
  280. package/dist/media/png-encode.js +74 -0
  281. package/dist/media-understanding/apply.js +403 -3
  282. package/dist/media-understanding/attachments.js +38 -27
  283. package/dist/media-understanding/defaults.js +16 -0
  284. package/dist/media-understanding/providers/deepgram/audio.js +22 -14
  285. package/dist/media-understanding/providers/google/audio.js +24 -17
  286. package/dist/media-understanding/providers/google/video.js +24 -17
  287. package/dist/media-understanding/providers/image.js +4 -4
  288. package/dist/media-understanding/providers/index.js +4 -1
  289. package/dist/media-understanding/providers/openai/audio.js +22 -14
  290. package/dist/media-understanding/providers/shared.js +16 -11
  291. package/dist/media-understanding/providers/zai/index.js +6 -0
  292. package/dist/media-understanding/runner.js +158 -90
  293. package/dist/memory/backend-config.js +207 -0
  294. package/dist/memory/batch-voyage.js +277 -0
  295. package/dist/memory/embeddings-voyage.js +75 -0
  296. package/dist/memory/embeddings.js +29 -17
  297. package/dist/memory/internal.js +101 -18
  298. package/dist/memory/manager.js +155 -48
  299. package/dist/memory/search-manager.js +173 -0
  300. package/dist/memory/session-files.js +9 -3
  301. package/dist/memory/types.js +1 -0
  302. package/dist/node-host/runner.js +36 -26
  303. package/dist/node-host/with-timeout.js +27 -0
  304. package/dist/pairing/pairing-messages.js +1 -1
  305. package/dist/plugins/commands.js +5 -1
  306. package/dist/plugins/config-state.js +86 -7
  307. package/dist/plugins/discovery.js +1 -1
  308. package/dist/plugins/install.js +2 -2
  309. package/dist/plugins/source-display.js +51 -0
  310. package/dist/plugins/update.js +1 -1
  311. package/dist/process/exec.js +20 -2
  312. package/dist/routing/resolve-route.js +12 -0
  313. package/dist/routing/session-key.js +15 -0
  314. package/dist/runtime.js +2 -0
  315. package/dist/security/audit-extra.async.js +601 -0
  316. package/dist/security/audit-extra.js +2 -830
  317. package/dist/security/audit-extra.sync.js +505 -0
  318. package/dist/security/audit.js +2 -2
  319. package/dist/security/channel-metadata.js +34 -0
  320. package/dist/security/external-content.js +88 -6
  321. package/dist/security/skill-scanner.js +330 -0
  322. package/dist/sessions/session-key-utils.js +7 -0
  323. package/dist/shared/text/reasoning-tags.js +52 -7
  324. package/dist/signal/monitor/event-handler.js +80 -1
  325. package/dist/slack/monitor/media.js +85 -15
  326. package/dist/tailscale/detect.js +145 -0
  327. package/dist/telegram/bot/helpers.js +109 -28
  328. package/dist/telegram/bot-handlers.js +144 -3
  329. package/dist/telegram/bot-message-context.js +38 -11
  330. package/dist/telegram/bot-message-dispatch.js +48 -15
  331. package/dist/telegram/bot-native-commands.js +86 -29
  332. package/dist/telegram/bot.js +30 -29
  333. package/dist/telegram/model-buttons.js +163 -0
  334. package/dist/telegram/monitor.js +110 -85
  335. package/dist/telegram/send.js +129 -47
  336. package/dist/terminal/restore.js +45 -0
  337. package/dist/test-helpers/state-dir-env.js +16 -0
  338. package/dist/test-helpers/workspace.js +11 -0
  339. package/dist/test-utils/channel-plugins.js +82 -0
  340. package/dist/test-utils/ports.js +73 -0
  341. package/dist/tts/tts.js +12 -6
  342. package/dist/tui/tui-session-actions.js +166 -54
  343. package/dist/utils/fetch-timeout.js +20 -0
  344. package/dist/utils/normalize-secret-input.js +19 -0
  345. package/dist/utils/shell-argv.js +61 -0
  346. package/dist/utils/transcript-tools.js +58 -0
  347. package/dist/utils.js +55 -14
  348. package/dist/version.js +42 -5
  349. package/dist/web/qr-image.js +1 -61
  350. package/dist/wizard/onboarding.finalize.js +7 -7
  351. package/dist/wizard/onboarding.js +3 -3
  352. package/docs/RELEASE_WORKFOTS_COMPARISON.md +3 -3
  353. package/docs/_config.yml +2 -2
  354. package/docs/_layouts/default.html +9 -9
  355. package/docs/concepts/typebox.md +1 -1
  356. package/docs/docs.json +1 -1
  357. package/docs/northflank.mdx +7 -7
  358. package/docs/railway.mdx +3 -3
  359. package/docs/render.mdx +5 -5
  360. package/docs/start/lore.md +2 -2
  361. package/extensions/bluebubbles/index.ts +2 -2
  362. package/extensions/bluebubbles/package.json +1 -1
  363. package/extensions/bluebubbles/src/accounts.ts +8 -8
  364. package/extensions/bluebubbles/src/actions.test.ts +22 -22
  365. package/extensions/bluebubbles/src/actions.ts +5 -5
  366. package/extensions/bluebubbles/src/attachments.ts +2 -2
  367. package/extensions/bluebubbles/src/channel.ts +16 -16
  368. package/extensions/bluebubbles/src/chat.ts +2 -2
  369. package/extensions/bluebubbles/src/media-send.ts +2 -2
  370. package/extensions/bluebubbles/src/monitor.test.ts +46 -46
  371. package/extensions/bluebubbles/src/monitor.ts +5 -5
  372. package/extensions/bluebubbles/src/onboarding.ts +7 -7
  373. package/extensions/bluebubbles/src/reactions.ts +2 -2
  374. package/extensions/bluebubbles/src/send.ts +2 -2
  375. package/extensions/copilot-proxy/README.md +1 -1
  376. package/extensions/copilot-proxy/package.json +1 -1
  377. package/extensions/diagnostics-otel/index.ts +2 -2
  378. package/extensions/diagnostics-otel/package.json +1 -1
  379. package/extensions/diagnostics-otel/src/service.ts +3 -3
  380. package/extensions/discord/index.ts +2 -2
  381. package/extensions/discord/package.json +1 -1
  382. package/extensions/google-antigravity-auth/README.md +1 -1
  383. package/extensions/google-antigravity-auth/index.ts +1 -1
  384. package/extensions/google-antigravity-auth/package.json +1 -1
  385. package/extensions/google-gemini-cli-auth/README.md +1 -1
  386. package/extensions/google-gemini-cli-auth/oauth.ts +1 -1
  387. package/extensions/google-gemini-cli-auth/package.json +1 -1
  388. package/extensions/googlechat/index.ts +3 -3
  389. package/extensions/googlechat/package.json +1 -1
  390. package/extensions/googlechat/src/accounts.ts +8 -8
  391. package/extensions/googlechat/src/actions.ts +6 -6
  392. package/extensions/googlechat/src/channel.ts +21 -21
  393. package/extensions/googlechat/src/monitor.ts +8 -8
  394. package/extensions/googlechat/src/onboarding.ts +10 -10
  395. package/extensions/imessage/index.ts +2 -2
  396. package/extensions/imessage/package.json +1 -1
  397. package/extensions/line/index.ts +2 -2
  398. package/extensions/line/package.json +1 -1
  399. package/extensions/line/src/card-command.ts +2 -2
  400. package/extensions/line/src/channel.logout.test.ts +4 -4
  401. package/extensions/line/src/channel.sendPayload.test.ts +8 -8
  402. package/extensions/line/src/channel.ts +3 -3
  403. package/extensions/llm-task/README.md +3 -3
  404. package/extensions/llm-task/index.ts +2 -2
  405. package/extensions/llm-task/package.json +1 -1
  406. package/extensions/llm-task/src/llm-task-tool.ts +4 -4
  407. package/extensions/lobster/README.md +6 -6
  408. package/extensions/lobster/index.ts +2 -2
  409. package/extensions/lobster/src/lobster-tool.test.ts +4 -4
  410. package/extensions/lobster/src/lobster-tool.ts +2 -2
  411. package/extensions/matrix/index.ts +2 -2
  412. package/extensions/matrix/package.json +1 -1
  413. package/extensions/matrix/src/matrix/client/config.ts +1 -1
  414. package/extensions/matrix/src/matrix/monitor/handler.ts +1 -1
  415. package/extensions/matrix/src/onboarding.ts +1 -1
  416. package/extensions/mattermost/index.ts +2 -2
  417. package/extensions/mattermost/package.json +1 -1
  418. package/extensions/mattermost/src/mattermost/accounts.ts +8 -8
  419. package/extensions/mattermost/src/mattermost/monitor-helpers.ts +5 -5
  420. package/extensions/mattermost/src/mattermost/monitor.ts +2 -2
  421. package/extensions/mattermost/src/onboarding-helpers.ts +3 -3
  422. package/extensions/mattermost/src/onboarding.ts +2 -2
  423. package/extensions/memory-core/index.ts +2 -2
  424. package/extensions/memory-core/package.json +1 -1
  425. package/extensions/memory-lancedb/index.ts +3 -3
  426. package/extensions/memory-lancedb/package.json +1 -1
  427. package/extensions/msteams/index.ts +2 -2
  428. package/extensions/msteams/package.json +1 -1
  429. package/extensions/msteams/src/channel.directory.test.ts +2 -2
  430. package/extensions/msteams/src/channel.ts +2 -2
  431. package/extensions/msteams/src/graph-upload.ts +4 -4
  432. package/extensions/msteams/src/monitor-handler.ts +2 -2
  433. package/extensions/msteams/src/monitor.ts +2 -2
  434. package/extensions/msteams/src/onboarding.ts +9 -9
  435. package/extensions/msteams/src/reply-dispatcher.ts +2 -2
  436. package/extensions/msteams/src/send-context.ts +2 -2
  437. package/extensions/msteams/src/send.ts +4 -4
  438. package/extensions/nextcloud-talk/index.ts +2 -2
  439. package/extensions/nextcloud-talk/package.json +1 -1
  440. package/extensions/nextcloud-talk/src/channel.ts +7 -7
  441. package/extensions/nextcloud-talk/src/inbound.ts +7 -7
  442. package/extensions/nextcloud-talk/src/onboarding.ts +1 -1
  443. package/extensions/nostr/README.md +2 -2
  444. package/extensions/nostr/index.ts +5 -5
  445. package/extensions/nostr/package.json +1 -1
  446. package/extensions/nostr/src/types.ts +4 -4
  447. package/extensions/open-prose/index.ts +2 -2
  448. package/extensions/qwen-portal-auth/README.md +1 -1
  449. package/extensions/signal/index.ts +2 -2
  450. package/extensions/signal/package.json +1 -1
  451. package/extensions/slack/index.ts +2 -2
  452. package/extensions/slack/package.json +1 -1
  453. package/extensions/telegram/index.ts +2 -2
  454. package/extensions/telegram/package.json +1 -1
  455. package/extensions/telegram/src/channel.ts +2 -2
  456. package/extensions/tlon/README.md +2 -2
  457. package/extensions/tlon/index.ts +2 -2
  458. package/extensions/tlon/package.json +1 -1
  459. package/extensions/tlon/src/channel.ts +13 -13
  460. package/extensions/tlon/src/monitor/index.ts +3 -3
  461. package/extensions/tlon/src/onboarding.ts +3 -3
  462. package/extensions/tlon/src/types.ts +3 -3
  463. package/extensions/twitch/README.md +1 -1
  464. package/extensions/twitch/index.ts +2 -2
  465. package/extensions/twitch/package.json +1 -1
  466. package/extensions/twitch/src/config.ts +3 -3
  467. package/extensions/twitch/src/monitor.ts +3 -3
  468. package/extensions/twitch/src/onboarding.ts +9 -9
  469. package/extensions/twitch/src/outbound.test.ts +2 -2
  470. package/extensions/twitch/src/plugin.test.ts +2 -2
  471. package/extensions/twitch/src/plugin.ts +8 -8
  472. package/extensions/twitch/src/send.test.ts +2 -2
  473. package/extensions/twitch/src/send.ts +4 -4
  474. package/extensions/twitch/src/token.test.ts +8 -8
  475. package/extensions/twitch/src/token.ts +3 -3
  476. package/extensions/twitch/src/twitch-client.ts +3 -3
  477. package/extensions/twitch/src/types.ts +3 -3
  478. package/extensions/twitch/src/utils/markdown.ts +1 -1
  479. package/extensions/voice-call/README.md +3 -3
  480. package/extensions/voice-call/package.json +1 -1
  481. package/extensions/voice-call/src/core-bridge.ts +2 -2
  482. package/extensions/voice-call/src/response-generator.ts +1 -1
  483. package/extensions/whatsapp/index.ts +2 -2
  484. package/extensions/whatsapp/package.json +1 -1
  485. package/extensions/zalo/README.md +1 -1
  486. package/extensions/zalo/index.ts +2 -2
  487. package/extensions/zalo/package.json +1 -1
  488. package/extensions/zalo/src/accounts.ts +8 -8
  489. package/extensions/zalo/src/actions.ts +4 -4
  490. package/extensions/zalo/src/channel.directory.test.ts +2 -2
  491. package/extensions/zalo/src/channel.ts +18 -18
  492. package/extensions/zalo/src/monitor.ts +9 -9
  493. package/extensions/zalo/src/monitor.webhook.test.ts +2 -2
  494. package/extensions/zalo/src/onboarding.ts +24 -24
  495. package/extensions/zalo/src/send.ts +2 -2
  496. package/extensions/zalouser/README.md +2 -2
  497. package/extensions/zalouser/index.ts +2 -2
  498. package/extensions/zalouser/package.json +1 -1
  499. package/extensions/zalouser/src/accounts.ts +9 -9
  500. package/extensions/zalouser/src/channel.ts +24 -24
  501. package/extensions/zalouser/src/monitor.ts +4 -4
  502. package/extensions/zalouser/src/onboarding.ts +28 -28
  503. package/package.json +13 -251
  504. package/skills/nano-banana-pro/scripts/generate_image.py +1 -1
  505. package/skills/tmux/scripts/find-sessions.sh +1 -1
  506. package/CHANGELOG.md +0 -102
  507. package/README-header.png +0 -0
  508. package/git-hooks/pre-commit +0 -4
  509. package/scripts/format-staged.js +0 -148
  510. package/scripts/postinstall.js +0 -300
  511. package/scripts/setup-git-hooks.js +0 -96
@@ -1,6 +1,16 @@
1
1
  import { logDebug, logError } from "../logger.js";
2
+ import { isPlainObject } from "../utils.js";
3
+ import { runBeforeToolCallHook } from "./pi-tools.before-tool-call.js";
2
4
  import { normalizeToolName } from "./tool-policy.js";
3
5
  import { jsonResult } from "./tools/common.js";
6
+ function isAbortSignal(value) {
7
+ return typeof value === "object" && value !== null && "aborted" in value;
8
+ }
9
+ function isLegacyToolExecuteArgs(args) {
10
+ const third = args[2];
11
+ const fourth = args[3];
12
+ return isAbortSignal(third) || typeof fourth === "function";
13
+ }
4
14
  function describeToolExecutionError(err) {
5
15
  if (err instanceof Error) {
6
16
  const message = err.message?.trim() ? err.message : String(err);
@@ -8,6 +18,24 @@ function describeToolExecutionError(err) {
8
18
  }
9
19
  return { message: String(err) };
10
20
  }
21
+ function splitToolExecuteArgs(args) {
22
+ if (isLegacyToolExecuteArgs(args)) {
23
+ const [toolCallId, params, signal, onUpdate] = args;
24
+ return {
25
+ toolCallId,
26
+ params,
27
+ onUpdate,
28
+ signal,
29
+ };
30
+ }
31
+ const [toolCallId, params, onUpdate, _ctx, signal] = args;
32
+ return {
33
+ toolCallId,
34
+ params,
35
+ onUpdate,
36
+ signal,
37
+ };
38
+ }
11
39
  export function toToolDefinitions(tools) {
12
40
  return tools.map((tool) => {
13
41
  const name = tool.name || "tool";
@@ -16,22 +44,22 @@ export function toToolDefinitions(tools) {
16
44
  name,
17
45
  label: tool.label ?? name,
18
46
  description: tool.description ?? "",
19
- // biome-ignore lint/suspicious/noExplicitAny: TypeBox schema from pi-agent-core uses a different module instance.
20
47
  parameters: tool.parameters,
21
- execute: async (toolCallId, params, onUpdate, _ctx, signal) => {
22
- // KNOWN: pi-coding-agent `ToolDefinition.execute` has a different signature/order
23
- // than pi-agent-core `AgentTool.execute`. This adapter keeps our existing tools intact.
48
+ execute: async (...args) => {
49
+ const { toolCallId, params, onUpdate, signal } = splitToolExecuteArgs(args);
24
50
  try {
25
51
  return await tool.execute(toolCallId, params, signal, onUpdate);
26
52
  }
27
53
  catch (err) {
28
- if (signal?.aborted)
54
+ if (signal?.aborted) {
29
55
  throw err;
56
+ }
30
57
  const name = err && typeof err === "object" && "name" in err
31
58
  ? String(err.name)
32
59
  : "";
33
- if (name === "AbortError")
60
+ if (name === "AbortError") {
34
61
  throw err;
62
+ }
35
63
  const described = describeToolExecutionError(err);
36
64
  if (described.stack && described.stack !== described.message) {
37
65
  logDebug(`tools: ${normalizedName} failed stack:\n${described.stack}`);
@@ -49,18 +77,31 @@ export function toToolDefinitions(tools) {
49
77
  }
50
78
  // Convert client tools (OpenResponses hosted tools) to ToolDefinition format
51
79
  // These tools are intercepted to return a "pending" result instead of executing
52
- export function toClientToolDefinitions(tools, onClientToolCall) {
80
+ export function toClientToolDefinitions(tools, onClientToolCall, hookContext) {
53
81
  return tools.map((tool) => {
54
82
  const func = tool.function;
55
83
  return {
56
84
  name: func.name,
57
85
  label: func.name,
58
86
  description: func.description ?? "",
87
+ // oxlint-disable-next-line typescript/no-explicit-any
59
88
  parameters: func.parameters,
60
- execute: async (toolCallId, params, _onUpdate, _ctx, _signal) => {
89
+ execute: async (...args) => {
90
+ const { toolCallId, params } = splitToolExecuteArgs(args);
91
+ const outcome = await runBeforeToolCallHook({
92
+ toolName: func.name,
93
+ params,
94
+ toolCallId,
95
+ ctx: hookContext,
96
+ });
97
+ if (outcome.blocked) {
98
+ throw new Error(outcome.reason);
99
+ }
100
+ const adjustedParams = outcome.params;
101
+ const paramsRecord = isPlainObject(adjustedParams) ? adjustedParams : {};
61
102
  // Notify handler that a client tool was called
62
103
  if (onClientToolCall) {
63
- onClientToolCall(func.name, params);
104
+ onClientToolCall(func.name, paramsRecord);
64
105
  }
65
106
  // Return a pending result - the client will execute this tool
66
107
  return jsonResult({
@@ -0,0 +1,67 @@
1
+ import { createSubsystemLogger } from "../logging/subsystem.js";
2
+ import { getGlobalHookRunner } from "../plugins/hook-runner-global.js";
3
+ import { isPlainObject } from "../utils.js";
4
+ import { normalizeToolName } from "./tool-policy.js";
5
+ const log = createSubsystemLogger("agents/tools");
6
+ export async function runBeforeToolCallHook(args) {
7
+ const hookRunner = getGlobalHookRunner();
8
+ if (!hookRunner?.hasHooks("before_tool_call")) {
9
+ return { blocked: false, params: args.params };
10
+ }
11
+ const toolName = normalizeToolName(args.toolName || "tool");
12
+ const params = args.params;
13
+ try {
14
+ const normalizedParams = isPlainObject(params) ? params : {};
15
+ const hookResult = await hookRunner.runBeforeToolCall({
16
+ toolName,
17
+ params: normalizedParams,
18
+ }, {
19
+ toolName,
20
+ agentId: args.ctx?.agentId,
21
+ sessionKey: args.ctx?.sessionKey,
22
+ });
23
+ if (hookResult?.block) {
24
+ return {
25
+ blocked: true,
26
+ reason: hookResult.blockReason || "Tool call blocked by plugin hook",
27
+ };
28
+ }
29
+ if (hookResult?.params && isPlainObject(hookResult.params)) {
30
+ if (isPlainObject(params)) {
31
+ return { blocked: false, params: { ...params, ...hookResult.params } };
32
+ }
33
+ return { blocked: false, params: hookResult.params };
34
+ }
35
+ }
36
+ catch (err) {
37
+ const toolCallId = args.toolCallId ? ` toolCallId=${args.toolCallId}` : "";
38
+ log.warn(`before_tool_call hook failed: tool=${toolName}${toolCallId} error=${String(err)}`);
39
+ }
40
+ return { blocked: false, params };
41
+ }
42
+ export function wrapToolWithBeforeToolCallHook(tool, ctx) {
43
+ const execute = tool.execute;
44
+ if (!execute) {
45
+ return tool;
46
+ }
47
+ const toolName = tool.name || "tool";
48
+ return {
49
+ ...tool,
50
+ execute: async (toolCallId, params, signal, onUpdate) => {
51
+ const outcome = await runBeforeToolCallHook({
52
+ toolName,
53
+ params,
54
+ toolCallId,
55
+ ctx,
56
+ });
57
+ if (outcome.blocked) {
58
+ throw new Error(outcome.reason);
59
+ }
60
+ return await execute(toolCallId, outcome.params, signal, onUpdate);
61
+ },
62
+ };
63
+ }
64
+ export const __testing = {
65
+ runBeforeToolCallHook,
66
+ isPlainObject,
67
+ };
@@ -6,10 +6,11 @@ import { createExecTool, createProcessTool, } from "./bash-tools.js";
6
6
  import { listChannelAgentTools } from "./channel-tools.js";
7
7
  import { createPoolBotTools } from "./poolbot-tools.js";
8
8
  import { wrapToolWithAbortSignal } from "./pi-tools.abort.js";
9
+ import { wrapToolWithBeforeToolCallHook } from "./pi-tools.before-tool-call.js";
9
10
  import { filterToolsByPolicy, isToolAllowedByPolicies, resolveEffectiveToolPolicy, resolveGroupToolPolicy, resolveSubagentToolPolicy, } from "./pi-tools.policy.js";
10
- import { assertRequiredParams, CLAUDE_PARAM_GROUPS, createMoltbotReadTool, createSandboxedEditTool, createSandboxedReadTool, createSandboxedWriteTool, normalizeToolParams, patchToolSchemaForClaudeCompatibility, wrapToolParamNormalization, } from "./pi-tools.read.js";
11
+ import { assertRequiredParams, CLAUDE_PARAM_GROUPS, createPoolbotReadTool, createSandboxedEditTool, createSandboxedReadTool, createSandboxedWriteTool, normalizeToolParams, patchToolSchemaForClaudeCompatibility, wrapToolParamNormalization, } from "./pi-tools.read.js";
11
12
  import { cleanToolSchemaForGemini, normalizeToolParameters } from "./pi-tools.schema.js";
12
- import { buildPluginToolGroups, collectExplicitAllowlist, expandPolicyWithPluginGroups, normalizeToolName, resolveToolProfilePolicy, stripPluginOnlyAllowlist, } from "./tool-policy.js";
13
+ import { buildPluginToolGroups, collectExplicitAllowlist, expandPolicyWithPluginGroups, normalizeToolName, resolveToolProfilePolicy, stripPluginOnlyAllowlist, applyOwnerOnlyToolPolicy, } from "./tool-policy.js";
13
14
  import { getPluginToolMeta } from "../plugins/tools.js";
14
15
  import { logWarn } from "../logger.js";
15
16
  function isOpenAIProvider(provider) {
@@ -59,7 +60,7 @@ export const __testing = {
59
60
  wrapToolParamNormalization,
60
61
  assertRequiredParams,
61
62
  };
62
- export function createMoltbotCodingTools(options) {
63
+ export function createPoolbotCodingTools(options) {
63
64
  const execToolName = "exec";
64
65
  const sandbox = options?.sandbox?.enabled ? options.sandbox : undefined;
65
66
  const { agentId, globalPolicy, globalProviderPolicy, agentPolicy, agentProviderPolicy, profile, providerProfile, profileAlsoAllow, providerProfileAlsoAllow, } = resolveEffectiveToolPolicy({
@@ -124,7 +125,7 @@ export function createMoltbotCodingTools(options) {
124
125
  return [createSandboxedReadTool(sandboxRoot)];
125
126
  }
126
127
  const freshReadTool = createReadTool(workspaceRoot);
127
- return [createMoltbotReadTool(freshReadTool)];
128
+ return [createPoolbotReadTool(freshReadTool)];
128
129
  }
129
130
  if (tool.name === "bash" || tool.name === execToolName)
130
131
  return [];
@@ -226,15 +227,20 @@ export function createMoltbotCodingTools(options) {
226
227
  replyToMode: options?.replyToMode,
227
228
  hasRepliedRef: options?.hasRepliedRef,
228
229
  modelHasVision: options?.modelHasVision,
230
+ requireExplicitMessageTarget: options?.requireExplicitMessageTarget,
231
+ disableMessageTool: options?.disableMessageTool,
229
232
  requesterAgentIdOverride: agentId,
230
233
  }),
231
234
  ];
232
- const coreToolNames = new Set(tools
235
+ // Security: treat unknown/undefined as unauthorized (opt-in, not opt-out)
236
+ const senderIsOwner = options?.senderIsOwner === true;
237
+ const toolsByAuthorization = applyOwnerOnlyToolPolicy(tools, senderIsOwner);
238
+ const coreToolNames = new Set(toolsByAuthorization
233
239
  .filter((tool) => !getPluginToolMeta(tool))
234
240
  .map((tool) => normalizeToolName(tool.name))
235
241
  .filter(Boolean));
236
242
  const pluginGroups = buildPluginToolGroups({
237
- tools,
243
+ tools: toolsByAuthorization,
238
244
  toolMeta: (tool) => getPluginToolMeta(tool),
239
245
  });
240
246
  const resolvePolicy = (policy, label) => {
@@ -258,8 +264,8 @@ export function createMoltbotCodingTools(options) {
258
264
  const sandboxPolicyExpanded = expandPolicyWithPluginGroups(sandbox?.tools, pluginGroups);
259
265
  const subagentPolicyExpanded = expandPolicyWithPluginGroups(subagentPolicy, pluginGroups);
260
266
  const toolsFiltered = profilePolicyExpanded
261
- ? filterToolsByPolicy(tools, profilePolicyExpanded)
262
- : tools;
267
+ ? filterToolsByPolicy(toolsByAuthorization, profilePolicyExpanded)
268
+ : toolsByAuthorization;
263
269
  const providerProfileFiltered = providerProfileExpanded
264
270
  ? filterToolsByPolicy(toolsFiltered, providerProfileExpanded)
265
271
  : toolsFiltered;
@@ -287,9 +293,13 @@ export function createMoltbotCodingTools(options) {
287
293
  // Always normalize tool JSON Schemas before handing them to pi-agent/pi-ai.
288
294
  // Without this, some providers (notably OpenAI) will reject root-level union schemas.
289
295
  const normalized = subagentFiltered.map(normalizeToolParameters);
296
+ const withHooks = normalized.map((tool) => wrapToolWithBeforeToolCallHook(tool, {
297
+ agentId,
298
+ sessionKey: options?.sessionKey,
299
+ }));
290
300
  const withAbort = options?.abortSignal
291
- ? normalized.map((tool) => wrapToolWithAbortSignal(tool, options.abortSignal))
292
- : normalized;
301
+ ? withHooks.map((tool) => wrapToolWithAbortSignal(tool, options.abortSignal))
302
+ : withHooks;
293
303
  // NOTE: Keep canonical (lowercase) tool names here.
294
304
  // pi-ai's Anthropic OAuth transport remaps tool names to Claude Code-style names
295
305
  // on the wire and maps them back for tool dispatch.
@@ -199,7 +199,7 @@ function wrapSandboxPathGuard(tool, root) {
199
199
  }
200
200
  export function createSandboxedReadTool(root) {
201
201
  const base = createReadTool(root);
202
- return wrapSandboxPathGuard(createMoltbotReadTool(base), root);
202
+ return wrapSandboxPathGuard(createPoolbotReadTool(base), root);
203
203
  }
204
204
  export function createSandboxedWriteTool(root) {
205
205
  const base = createWriteTool(root);
@@ -209,7 +209,7 @@ export function createSandboxedEditTool(root) {
209
209
  const base = createEditTool(root);
210
210
  return wrapSandboxPathGuard(wrapToolParamNormalization(base, CLAUDE_PARAM_GROUPS.edit), root);
211
211
  }
212
- export function createMoltbotReadTool(base) {
212
+ export function createPoolbotReadTool(base) {
213
213
  const patched = patchToolSchemaForClaudeCompatibility(base);
214
214
  return {
215
215
  ...patched,
@@ -32,6 +32,20 @@ export function createPoolBotTools(options) {
32
32
  config: options?.config,
33
33
  sandboxed: options?.sandboxed,
34
34
  });
35
+ const messageTool = options?.disableMessageTool
36
+ ? null
37
+ : createMessageTool({
38
+ agentAccountId: options?.agentAccountId,
39
+ agentSessionKey: options?.agentSessionKey,
40
+ config: options?.config,
41
+ currentChannelId: options?.currentChannelId,
42
+ currentChannelProvider: options?.agentChannel,
43
+ currentThreadTs: options?.currentThreadTs,
44
+ replyToMode: options?.replyToMode,
45
+ hasRepliedRef: options?.hasRepliedRef,
46
+ sandboxRoot: options?.sandboxRoot,
47
+ requireExplicitTarget: options?.requireExplicitMessageTarget,
48
+ });
35
49
  const tools = [
36
50
  createBrowserTool({
37
51
  sandboxBridgeUrl: options?.sandboxBrowserBridgeUrl,
@@ -45,16 +59,7 @@ export function createPoolBotTools(options) {
45
59
  createCronTool({
46
60
  agentSessionKey: options?.agentSessionKey,
47
61
  }),
48
- createMessageTool({
49
- agentAccountId: options?.agentAccountId,
50
- agentSessionKey: options?.agentSessionKey,
51
- config: options?.config,
52
- currentChannelId: options?.currentChannelId,
53
- currentChannelProvider: options?.agentChannel,
54
- currentThreadTs: options?.currentThreadTs,
55
- replyToMode: options?.replyToMode,
56
- hasRepliedRef: options?.hasRepliedRef,
57
- }),
62
+ ...(messageTool ? [messageTool] : []),
58
63
  createTtsTool({
59
64
  agentChannel: options?.agentChannel,
60
65
  config: options?.config,
@@ -1,6 +1,9 @@
1
1
  import fs from "node:fs/promises";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const HTTP_URL_RE = /^https?:\/\//i;
6
+ const DATA_URL_RE = /^data:/i;
4
7
  const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
5
8
  function normalizeUnicodeSpaces(str) {
6
9
  return str.replace(UNICODE_SPACES, " ");
@@ -38,6 +41,34 @@ export async function assertSandboxPath(params) {
38
41
  await assertNoSymlink(resolved.relative, path.resolve(params.root));
39
42
  return resolved;
40
43
  }
44
+ export function assertMediaNotDataUrl(media) {
45
+ const raw = media.trim();
46
+ if (DATA_URL_RE.test(raw)) {
47
+ throw new Error("data: URLs are not supported for media. Use buffer instead.");
48
+ }
49
+ }
50
+ export async function resolveSandboxedMediaSource(params) {
51
+ const raw = params.media.trim();
52
+ if (!raw)
53
+ return raw;
54
+ if (HTTP_URL_RE.test(raw))
55
+ return raw;
56
+ let candidate = raw;
57
+ if (/^file:\/\//i.test(candidate)) {
58
+ try {
59
+ candidate = fileURLToPath(candidate);
60
+ }
61
+ catch {
62
+ throw new Error(`Invalid file:// URL for sandboxed media: ${raw}`);
63
+ }
64
+ }
65
+ const resolved = await assertSandboxPath({
66
+ filePath: candidate,
67
+ cwd: params.sandboxRoot,
68
+ root: params.sandboxRoot,
69
+ });
70
+ return resolved.resolved;
71
+ }
41
72
  async function assertNoSymlink(relative, root) {
42
73
  if (!relative)
43
74
  return;
@@ -0,0 +1,83 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ function isSessionHeader(entry) {
4
+ if (!entry || typeof entry !== "object") {
5
+ return false;
6
+ }
7
+ const record = entry;
8
+ return record.type === "session" && typeof record.id === "string" && record.id.length > 0;
9
+ }
10
+ export async function repairSessionFileIfNeeded(params) {
11
+ const sessionFile = params.sessionFile.trim();
12
+ if (!sessionFile) {
13
+ return { repaired: false, droppedLines: 0, reason: "missing session file" };
14
+ }
15
+ let content;
16
+ try {
17
+ content = await fs.readFile(sessionFile, "utf-8");
18
+ }
19
+ catch (err) {
20
+ const code = err?.code;
21
+ if (code === "ENOENT") {
22
+ return { repaired: false, droppedLines: 0, reason: "missing session file" };
23
+ }
24
+ const reason = `failed to read session file: ${err instanceof Error ? err.message : "unknown error"}`;
25
+ params.warn?.(`session file repair skipped: ${reason} (${path.basename(sessionFile)})`);
26
+ return { repaired: false, droppedLines: 0, reason };
27
+ }
28
+ const lines = content.split(/\r?\n/);
29
+ const entries = [];
30
+ let droppedLines = 0;
31
+ for (const line of lines) {
32
+ if (!line.trim()) {
33
+ continue;
34
+ }
35
+ try {
36
+ const entry = JSON.parse(line);
37
+ entries.push(entry);
38
+ }
39
+ catch {
40
+ droppedLines += 1;
41
+ }
42
+ }
43
+ if (entries.length === 0) {
44
+ return { repaired: false, droppedLines, reason: "empty session file" };
45
+ }
46
+ if (!isSessionHeader(entries[0])) {
47
+ params.warn?.(`session file repair skipped: invalid session header (${path.basename(sessionFile)})`);
48
+ return { repaired: false, droppedLines, reason: "invalid session header" };
49
+ }
50
+ if (droppedLines === 0) {
51
+ return { repaired: false, droppedLines: 0 };
52
+ }
53
+ const cleaned = `${entries.map((entry) => JSON.stringify(entry)).join("\n")}\n`;
54
+ const backupPath = `${sessionFile}.bak-${process.pid}-${Date.now()}`;
55
+ const tmpPath = `${sessionFile}.repair-${process.pid}-${Date.now()}.tmp`;
56
+ try {
57
+ const stat = await fs.stat(sessionFile).catch(() => null);
58
+ await fs.writeFile(backupPath, content, "utf-8");
59
+ if (stat) {
60
+ await fs.chmod(backupPath, stat.mode);
61
+ }
62
+ await fs.writeFile(tmpPath, cleaned, "utf-8");
63
+ if (stat) {
64
+ await fs.chmod(tmpPath, stat.mode);
65
+ }
66
+ await fs.rename(tmpPath, sessionFile);
67
+ }
68
+ catch (err) {
69
+ try {
70
+ await fs.unlink(tmpPath);
71
+ }
72
+ catch (cleanupErr) {
73
+ params.warn?.(`session file repair cleanup failed: ${cleanupErr instanceof Error ? cleanupErr.message : "unknown error"} (${path.basename(tmpPath)})`);
74
+ }
75
+ return {
76
+ repaired: false,
77
+ droppedLines,
78
+ reason: `repair failed: ${err instanceof Error ? err.message : "unknown error"}`,
79
+ };
80
+ }
81
+ params.warn?.(`session file repaired: dropped ${droppedLines} malformed line(s) (${path.basename(sessionFile)})`);
82
+ return { repaired: true, droppedLines, backupPath };
83
+ }
@@ -1,16 +1,76 @@
1
- import { makeMissingToolResult } from "./session-transcript-repair.js";
2
1
  import { emitSessionTranscriptUpdate } from "../sessions/transcript-events.js";
2
+ import { HARD_MAX_TOOL_RESULT_CHARS } from "./pi-embedded-runner/tool-result-truncation.js";
3
+ import { makeMissingToolResult, sanitizeToolCallInputs } from "./session-transcript-repair.js";
4
+ const GUARD_TRUNCATION_SUFFIX = "\n\n⚠️ [Content truncated during persistence — original exceeded size limit. " +
5
+ "Use offset/limit parameters or request specific sections for large content.]";
6
+ /**
7
+ * Truncate oversized text content blocks in a tool result message.
8
+ * Returns the original message if under the limit, or a new message with
9
+ * truncated text blocks otherwise.
10
+ */
11
+ function capToolResultSize(msg) {
12
+ const role = msg.role;
13
+ if (role !== "toolResult") {
14
+ return msg;
15
+ }
16
+ const content = msg.content;
17
+ if (!Array.isArray(content)) {
18
+ return msg;
19
+ }
20
+ // Calculate total text size
21
+ let totalTextChars = 0;
22
+ for (const block of content) {
23
+ if (block && typeof block === "object" && block.type === "text") {
24
+ const text = block.text;
25
+ if (typeof text === "string") {
26
+ totalTextChars += text.length;
27
+ }
28
+ }
29
+ }
30
+ if (totalTextChars <= HARD_MAX_TOOL_RESULT_CHARS) {
31
+ return msg;
32
+ }
33
+ // Truncate proportionally
34
+ const newContent = content.map((block) => {
35
+ if (!block || typeof block !== "object" || block.type !== "text") {
36
+ return block;
37
+ }
38
+ const textBlock = block;
39
+ if (typeof textBlock.text !== "string") {
40
+ return block;
41
+ }
42
+ const blockShare = textBlock.text.length / totalTextChars;
43
+ const blockBudget = Math.max(2_000, Math.floor(HARD_MAX_TOOL_RESULT_CHARS * blockShare) - GUARD_TRUNCATION_SUFFIX.length);
44
+ if (textBlock.text.length <= blockBudget) {
45
+ return block;
46
+ }
47
+ // Try to cut at a newline boundary
48
+ let cutPoint = blockBudget;
49
+ const lastNewline = textBlock.text.lastIndexOf("\n", blockBudget);
50
+ if (lastNewline > blockBudget * 0.8) {
51
+ cutPoint = lastNewline;
52
+ }
53
+ return {
54
+ ...textBlock,
55
+ text: textBlock.text.slice(0, cutPoint) + GUARD_TRUNCATION_SUFFIX,
56
+ };
57
+ });
58
+ return { ...msg, content: newContent };
59
+ }
3
60
  function extractAssistantToolCalls(msg) {
4
61
  const content = msg.content;
5
- if (!Array.isArray(content))
62
+ if (!Array.isArray(content)) {
6
63
  return [];
64
+ }
7
65
  const toolCalls = [];
8
66
  for (const block of content) {
9
- if (!block || typeof block !== "object")
67
+ if (!block || typeof block !== "object") {
10
68
  continue;
69
+ }
11
70
  const rec = block;
12
- if (typeof rec.id !== "string" || !rec.id)
71
+ if (typeof rec.id !== "string" || !rec.id) {
13
72
  continue;
73
+ }
14
74
  if (rec.type === "toolCall" || rec.type === "toolUse" || rec.type === "functionCall") {
15
75
  toolCalls.push({
16
76
  id: rec.id,
@@ -22,11 +82,13 @@ function extractAssistantToolCalls(msg) {
22
82
  }
23
83
  function extractToolResultId(msg) {
24
84
  const toolCallId = msg.toolCallId;
25
- if (typeof toolCallId === "string" && toolCallId)
85
+ if (typeof toolCallId === "string" && toolCallId) {
26
86
  return toolCallId;
87
+ }
27
88
  const toolUseId = msg.toolUseId;
28
- if (typeof toolUseId === "string" && toolUseId)
89
+ if (typeof toolUseId === "string" && toolUseId) {
29
90
  return toolUseId;
91
+ }
30
92
  return null;
31
93
  }
32
94
  export function installSessionToolResultGuard(sessionManager, opts) {
@@ -38,8 +100,9 @@ export function installSessionToolResultGuard(sessionManager, opts) {
38
100
  };
39
101
  const allowSyntheticToolResults = opts?.allowSyntheticToolResults ?? true;
40
102
  const flushPendingToolResults = () => {
41
- if (pending.size === 0)
103
+ if (pending.size === 0) {
42
104
  return;
105
+ }
43
106
  if (allowSyntheticToolResults) {
44
107
  for (const [id, name] of pending.entries()) {
45
108
  const synthetic = makeMissingToolResult({ toolCallId: id, toolName: name });
@@ -53,24 +116,40 @@ export function installSessionToolResultGuard(sessionManager, opts) {
53
116
  pending.clear();
54
117
  };
55
118
  const guardedAppend = (message) => {
119
+ let nextMessage = message;
56
120
  const role = message.role;
57
- if (role === "toolResult") {
58
- const id = extractToolResultId(message);
121
+ if (role === "assistant") {
122
+ const sanitized = sanitizeToolCallInputs([message]);
123
+ if (sanitized.length === 0) {
124
+ if (allowSyntheticToolResults && pending.size > 0) {
125
+ flushPendingToolResults();
126
+ }
127
+ return undefined;
128
+ }
129
+ nextMessage = sanitized[0];
130
+ }
131
+ const nextRole = nextMessage.role;
132
+ if (nextRole === "toolResult") {
133
+ const id = extractToolResultId(nextMessage);
59
134
  const toolName = id ? pending.get(id) : undefined;
60
- if (id)
135
+ if (id) {
61
136
  pending.delete(id);
62
- return originalAppend(persistToolResult(message, {
137
+ }
138
+ // Apply hard size cap before persistence to prevent oversized tool results
139
+ // from consuming the entire context window on subsequent LLM calls.
140
+ const capped = capToolResultSize(nextMessage);
141
+ return originalAppend(persistToolResult(capped, {
63
142
  toolCallId: id ?? undefined,
64
143
  toolName,
65
144
  isSynthetic: false,
66
145
  }));
67
146
  }
68
- const toolCalls = role === "assistant"
69
- ? extractAssistantToolCalls(message)
147
+ const toolCalls = nextRole === "assistant"
148
+ ? extractAssistantToolCalls(nextMessage)
70
149
  : [];
71
150
  if (allowSyntheticToolResults) {
72
151
  // If previous tool calls are still pending, flush before non-tool results.
73
- if (pending.size > 0 && (toolCalls.length === 0 || role !== "assistant")) {
152
+ if (pending.size > 0 && (toolCalls.length === 0 || nextRole !== "assistant")) {
74
153
  flushPendingToolResults();
75
154
  }
76
155
  // If new tool calls arrive while older ones are pending, flush the old ones first.
@@ -78,7 +157,7 @@ export function installSessionToolResultGuard(sessionManager, opts) {
78
157
  flushPendingToolResults();
79
158
  }
80
159
  }
81
- const result = originalAppend(message);
160
+ const result = originalAppend(nextMessage);
82
161
  const sessionFile = sessionManager.getSessionFile?.();
83
162
  if (sessionFile) {
84
163
  emitSessionTranscriptUpdate(sessionFile);