@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.
- package/assets/chrome-extension/README.md +3 -3
- package/assets/chrome-extension/background.js +5 -5
- package/assets/chrome-extension/manifest.json +3 -3
- package/assets/chrome-extension/options.html +4 -4
- package/assets/chrome-extension/options.js +1 -1
- package/dist/acp/client.js +3 -3
- package/dist/acp/types.js +1 -1
- package/dist/agents/agent-paths.js +3 -3
- package/dist/agents/auth-profiles/paths.js +3 -3
- package/dist/agents/bash-tools.exec.js +76 -25
- package/dist/agents/cli-runner/helpers.js +10 -12
- package/dist/agents/cli-runner.js +2 -2
- package/dist/agents/cloudflare-ai-gateway.js +31 -0
- package/dist/agents/compaction.js +16 -2
- package/dist/agents/context-window-guard.js +13 -10
- package/dist/agents/context.js +4 -4
- package/dist/agents/docs-path.js +1 -1
- package/dist/agents/identity.js +47 -7
- package/dist/agents/memory-search.js +25 -8
- package/dist/agents/minimax-vlm.js +1 -1
- package/dist/agents/model-auth.js +12 -1
- package/dist/agents/model-catalog.js +4 -4
- package/dist/agents/model-selection.js +31 -4
- package/dist/agents/models-config.js +3 -3
- package/dist/agents/models-config.providers.js +147 -39
- package/dist/agents/pi-embedded-block-chunker.js +117 -42
- package/dist/agents/pi-embedded-helpers/errors.js +183 -78
- package/dist/agents/pi-embedded-helpers/openai.js +1 -1
- package/dist/agents/pi-embedded-helpers.js +1 -1
- package/dist/agents/pi-embedded-runner/compact.js +9 -8
- package/dist/agents/pi-embedded-runner/model.js +63 -4
- package/dist/agents/pi-embedded-runner/run/attempt.js +27 -17
- package/dist/agents/pi-embedded-runner/run.js +203 -50
- package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
- package/dist/agents/pi-embedded-runner/tool-result-truncation.js +275 -0
- package/dist/agents/pi-embedded-runner/utils.js +1 -1
- package/dist/agents/pi-embedded-subscribe.js +118 -29
- package/dist/agents/pi-model-discovery.js +10 -0
- package/dist/agents/pi-tool-definition-adapter.js +50 -9
- package/dist/agents/pi-tools.before-tool-call.js +67 -0
- package/dist/agents/pi-tools.js +20 -10
- package/dist/agents/pi-tools.read.js +2 -2
- package/dist/agents/poolbot-tools.js +15 -10
- package/dist/agents/sandbox-paths.js +31 -0
- package/dist/agents/session-file-repair.js +83 -0
- package/dist/agents/session-tool-result-guard.js +94 -15
- package/dist/agents/session-transcript-repair.js +68 -0
- package/dist/agents/shell-utils.js +51 -0
- package/dist/agents/skills/bundled-context.js +23 -0
- package/dist/agents/skills/bundled-dir.js +41 -7
- package/dist/agents/skills/frontmatter.js +1 -1
- package/dist/agents/skills/workspace.js +2 -2
- package/dist/agents/skills-install.js +60 -23
- package/dist/agents/subagent-announce.js +79 -34
- package/dist/agents/system-prompt.js +28 -4
- package/dist/agents/together-models.js +127 -0
- package/dist/agents/tool-images.js +1 -1
- package/dist/agents/tool-policy.conformance.js +14 -0
- package/dist/agents/tool-policy.js +25 -1
- package/dist/agents/tools/browser-tool.js +3 -3
- package/dist/agents/tools/cron-tool.js +166 -19
- package/dist/agents/tools/discord-actions-presence.js +78 -0
- package/dist/agents/tools/image-tool.js +2 -2
- package/dist/agents/tools/memory-tool.js +93 -5
- package/dist/agents/tools/message-tool.js +56 -2
- package/dist/agents/tools/sessions-history-tool.js +69 -1
- package/dist/agents/tools/web-search.js +211 -42
- package/dist/agents/usage.js +23 -1
- package/dist/agents/workspace-run.js +67 -0
- package/dist/agents/workspace-templates.js +44 -0
- package/dist/auto-reply/command-auth.js +121 -6
- package/dist/auto-reply/commands-registry.data.js +1 -1
- package/dist/auto-reply/envelope.js +50 -72
- package/dist/auto-reply/reply/commands-compact.js +1 -0
- package/dist/auto-reply/reply/commands-context-report.js +3 -2
- package/dist/auto-reply/reply/commands-context.js +1 -0
- package/dist/auto-reply/reply/commands-models.js +107 -60
- package/dist/auto-reply/reply/commands-ptt.js +171 -0
- package/dist/auto-reply/reply/commands-session.js +2 -2
- package/dist/auto-reply/reply/get-reply-run.js +16 -5
- package/dist/auto-reply/reply/groups.js +1 -1
- package/dist/auto-reply/reply/inbound-context.js +9 -1
- package/dist/auto-reply/reply/inbound-meta.js +130 -0
- package/dist/auto-reply/reply/model-selection.js +3 -3
- package/dist/auto-reply/reply/untrusted-context.js +15 -0
- package/dist/auto-reply/status.js +1 -1
- package/dist/auto-reply/thinking.js +88 -43
- package/dist/browser/bridge-server.js +13 -0
- package/dist/browser/cdp.helpers.js +38 -24
- package/dist/browser/client-fetch.js +51 -8
- package/dist/browser/config.js +2 -11
- package/dist/browser/extension-relay.js +104 -43
- package/dist/browser/pw-ai.js +1 -1
- package/dist/browser/pw-session.js +143 -8
- package/dist/browser/pw-tools-core.interactions.js +125 -27
- package/dist/browser/pw-tools-core.responses.js +1 -1
- package/dist/browser/pw-tools-core.state.js +1 -1
- package/dist/browser/routes/agent.act.js +86 -41
- package/dist/browser/routes/dispatcher.js +4 -4
- package/dist/browser/screenshot.js +1 -1
- package/dist/browser/server-context.js +2 -2
- package/dist/browser/server.js +13 -0
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui.js +3 -3
- package/dist/channels/plugins/catalog.js +2 -2
- package/dist/channels/plugins/onboarding/imessage.js +1 -1
- package/dist/channels/plugins/onboarding/signal.js +1 -1
- package/dist/channels/plugins/onboarding/slack.js +4 -4
- package/dist/channels/plugins/onboarding/whatsapp.js +3 -3
- package/dist/channels/plugins/pairing-message.js +1 -1
- package/dist/channels/reply-prefix.js +8 -1
- package/dist/cli/browser-cli-extension.js +2 -2
- package/dist/cli/cron-cli/register.cron-add.js +61 -40
- package/dist/cli/cron-cli/register.cron-edit.js +60 -34
- package/dist/cli/cron-cli/shared.js +56 -41
- package/dist/cli/dns-cli.js +26 -14
- package/dist/cli/docs-cli.js +1 -1
- package/dist/cli/gateway-cli/dev.js +1 -1
- package/dist/cli/gateway-cli/register.js +37 -19
- package/dist/cli/memory-cli.js +30 -20
- package/dist/cli/nodes-cli/register.canvas.js +1 -1
- package/dist/cli/parse-bytes.js +37 -0
- package/dist/cli/plugins-cli.js +1 -1
- package/dist/cli/run-main.js +2 -2
- package/dist/cli/security-cli.js +1 -1
- package/dist/cli/tagline.js +1 -1
- package/dist/cli/update-cli.js +173 -52
- package/dist/cli/webhooks-cli.js +5 -5
- package/dist/commands/agent.js +1 -0
- package/dist/commands/agents.commands.add.js +1 -1
- package/dist/commands/auth-choice.apply.api-providers.js +305 -17
- package/dist/commands/auth-choice.apply.js +4 -1
- package/dist/commands/auth-choice.apply.plugin-provider.js +2 -2
- package/dist/commands/auth-choice.apply.xai.js +63 -0
- package/dist/commands/auth-choice.preferred-provider.js +7 -1
- package/dist/commands/configure.wizard.js +1 -1
- package/dist/commands/dashboard.js +1 -1
- package/dist/commands/docs.js +1 -1
- package/dist/commands/doctor-config-flow.js +61 -5
- package/dist/commands/doctor-gateway-services.js +3 -3
- package/dist/commands/doctor-state-migrations.js +1 -1
- package/dist/commands/doctor-update.js +3 -3
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/health.js +1 -1
- package/dist/commands/model-allowlist.js +29 -0
- package/dist/commands/model-picker.js +2 -1
- package/dist/commands/models/list.probe.js +2 -2
- package/dist/commands/models/list.registry.js +4 -4
- package/dist/commands/models/list.status-command.js +44 -24
- package/dist/commands/models/shared.js +15 -0
- package/dist/commands/onboard-auth.config-core.js +366 -28
- package/dist/commands/onboard-auth.credentials.js +71 -9
- package/dist/commands/onboard-auth.js +3 -3
- package/dist/commands/onboard-auth.models.js +26 -24
- package/dist/commands/onboard-custom.js +384 -0
- package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +35 -0
- package/dist/commands/onboard-non-interactive/local/auth-choice.js +146 -9
- package/dist/commands/onboard-skills.js +63 -38
- package/dist/commands/openai-model-default.js +41 -0
- package/dist/commands/status-all/report-lines.js +1 -1
- package/dist/commands/status.command.js +1 -1
- package/dist/commands/uninstall.js +3 -3
- package/dist/compat/legacy-names.js +1 -1
- package/dist/config/defaults.js +3 -2
- package/dist/config/io.js +3 -3
- package/dist/config/paths.js +136 -35
- package/dist/config/plugin-auto-enable.js +21 -5
- package/dist/config/redact-snapshot.js +153 -0
- package/dist/config/schema.field-metadata.js +590 -0
- package/dist/config/schema.js +3 -3
- package/dist/config/sessions/store.js +291 -23
- package/dist/config/types.memory.js +1 -0
- package/dist/config/version.js +4 -4
- package/dist/config/zod-schema.agent-defaults.js +3 -0
- package/dist/config/zod-schema.agent-runtime.js +13 -2
- package/dist/config/zod-schema.providers-core.js +142 -0
- package/dist/config/zod-schema.session.js +3 -0
- package/dist/cron/delivery.js +57 -0
- package/dist/cron/isolated-agent/delivery-target.js +18 -3
- package/dist/cron/isolated-agent/helpers.js +22 -5
- package/dist/cron/isolated-agent/run.js +171 -63
- package/dist/cron/isolated-agent/session.js +2 -0
- package/dist/cron/normalize.js +356 -28
- package/dist/cron/parse.js +10 -5
- package/dist/cron/run-log.js +35 -10
- package/dist/cron/schedule.js +41 -6
- package/dist/cron/service/jobs.js +208 -35
- package/dist/cron/service/ops.js +72 -16
- package/dist/cron/service/state.js +2 -0
- package/dist/cron/service/store.js +386 -14
- package/dist/cron/service/timer.js +390 -147
- package/dist/cron/session-reaper.js +86 -0
- package/dist/cron/store.js +23 -8
- package/dist/cron/validate-timestamp.js +43 -0
- package/dist/daemon/constants.js +7 -7
- package/dist/daemon/inspect.js +6 -6
- package/dist/daemon/systemd-unit.js +1 -1
- package/dist/discord/monitor/agent-components.js +438 -0
- package/dist/discord/monitor/allow-list.js +28 -5
- package/dist/discord/monitor/gateway-registry.js +29 -0
- package/dist/discord/monitor/native-command.js +44 -23
- package/dist/discord/monitor/sender-identity.js +45 -0
- package/dist/discord/pluralkit.js +27 -0
- package/dist/discord/send.outbound.js +92 -5
- package/dist/discord/send.shared.js +60 -23
- package/dist/discord/targets.js +84 -1
- package/dist/entry.js +15 -9
- package/dist/extensionAPI.js +8 -0
- package/dist/gateway/control-ui.js +8 -1
- package/dist/gateway/hooks-mapping.js +3 -0
- package/dist/gateway/hooks.js +65 -0
- package/dist/gateway/live-image-probe.js +1 -66
- package/dist/gateway/net.js +96 -31
- package/dist/gateway/node-command-policy.js +50 -15
- package/dist/gateway/openai-http.js +2 -2
- package/dist/gateway/openresponses-http.js +4 -4
- package/dist/gateway/origin-check.js +56 -0
- package/dist/gateway/protocol/client-info.js +9 -0
- package/dist/gateway/protocol/index.js +9 -2
- package/dist/gateway/protocol/schema/agents-models-skills.js +71 -1
- package/dist/gateway/protocol/schema/cron.js +22 -10
- package/dist/gateway/protocol/schema/protocol-schemas.js +16 -2
- package/dist/gateway/protocol/schema/sessions.js +12 -0
- package/dist/gateway/server/hooks.js +1 -1
- package/dist/gateway/server-broadcast.js +26 -9
- package/dist/gateway/server-chat.js +112 -23
- package/dist/gateway/server-discovery-runtime.js +10 -2
- package/dist/gateway/server-discovery.js +2 -2
- package/dist/gateway/server-http.js +110 -12
- package/dist/gateway/server-methods/agent-timestamp.js +60 -0
- package/dist/gateway/server-methods/agents.js +321 -2
- package/dist/gateway/server-methods/usage.js +559 -16
- package/dist/gateway/server-runtime-state.js +22 -8
- package/dist/gateway/server-startup-memory.js +16 -0
- package/dist/gateway/server.impl.js +7 -3
- package/dist/gateway/session-utils.fs.js +23 -25
- package/dist/gateway/session-utils.js +20 -10
- package/dist/gateway/sessions-patch.js +7 -22
- package/dist/gateway/test-helpers.server.js +35 -2
- package/dist/hooks/frontmatter.js +1 -1
- package/dist/hooks/hooks-status.js +1 -1
- package/dist/hooks/install.js +2 -2
- package/dist/hooks/loader.js +1 -1
- package/dist/hooks/workspace.js +3 -3
- package/dist/imessage/constants.js +2 -0
- package/dist/imessage/monitor/deliver.js +4 -1
- package/dist/imessage/monitor/monitor-provider.js +51 -1
- package/dist/index.js +2 -2
- package/dist/infra/bonjour-discovery.js +131 -70
- package/dist/infra/bonjour.js +3 -3
- package/dist/infra/control-ui-assets.js +134 -12
- package/dist/infra/errors.js +12 -0
- package/dist/infra/exec-approvals.js +266 -57
- package/dist/infra/format-time/format-datetime.js +79 -0
- package/dist/infra/format-time/format-duration.js +81 -0
- package/dist/infra/format-time/format-relative.js +80 -0
- package/dist/infra/heartbeat-runner.js +140 -49
- package/dist/infra/home-dir.js +54 -0
- package/dist/infra/net/fetch-guard.js +122 -0
- package/dist/infra/net/ssrf.js +65 -29
- package/dist/infra/outbound/abort.js +14 -0
- package/dist/infra/outbound/message-action-runner.js +77 -13
- package/dist/infra/outbound/outbound-session.js +143 -37
- package/dist/infra/path-env.js +3 -3
- package/dist/infra/poolbot-root.js +43 -1
- package/dist/infra/provider-usage.fetch.minimax.js +1 -1
- package/dist/infra/restart.js +1 -1
- package/dist/infra/session-cost-usage.js +631 -41
- package/dist/infra/state-migrations.js +317 -47
- package/dist/infra/tailscale.js +1 -1
- package/dist/infra/update-global.js +35 -0
- package/dist/infra/update-runner.js +149 -43
- package/dist/infra/warning-filter.js +65 -0
- package/dist/infra/widearea-dns.js +30 -9
- package/dist/logging/redact-identifier.js +12 -0
- package/dist/macos/relay.js +2 -2
- package/dist/media/fetch.js +81 -58
- package/dist/media/input-files.js +1 -1
- package/dist/media/mime.js +4 -0
- package/dist/media/png-encode.js +74 -0
- package/dist/media-understanding/apply.js +403 -3
- package/dist/media-understanding/attachments.js +38 -27
- package/dist/media-understanding/defaults.js +16 -0
- package/dist/media-understanding/providers/deepgram/audio.js +22 -14
- package/dist/media-understanding/providers/google/audio.js +24 -17
- package/dist/media-understanding/providers/google/video.js +24 -17
- package/dist/media-understanding/providers/image.js +4 -4
- package/dist/media-understanding/providers/index.js +4 -1
- package/dist/media-understanding/providers/openai/audio.js +22 -14
- package/dist/media-understanding/providers/shared.js +16 -11
- package/dist/media-understanding/providers/zai/index.js +6 -0
- package/dist/media-understanding/runner.js +158 -90
- package/dist/memory/backend-config.js +207 -0
- package/dist/memory/batch-voyage.js +277 -0
- package/dist/memory/embeddings-voyage.js +75 -0
- package/dist/memory/embeddings.js +29 -17
- package/dist/memory/internal.js +101 -18
- package/dist/memory/manager.js +155 -48
- package/dist/memory/search-manager.js +173 -0
- package/dist/memory/session-files.js +9 -3
- package/dist/memory/types.js +1 -0
- package/dist/node-host/runner.js +36 -26
- package/dist/node-host/with-timeout.js +27 -0
- package/dist/pairing/pairing-messages.js +1 -1
- package/dist/plugins/commands.js +5 -1
- package/dist/plugins/config-state.js +86 -7
- package/dist/plugins/discovery.js +1 -1
- package/dist/plugins/install.js +2 -2
- package/dist/plugins/source-display.js +51 -0
- package/dist/plugins/update.js +1 -1
- package/dist/process/exec.js +20 -2
- package/dist/routing/resolve-route.js +12 -0
- package/dist/routing/session-key.js +15 -0
- package/dist/runtime.js +2 -0
- package/dist/security/audit-extra.async.js +601 -0
- package/dist/security/audit-extra.js +2 -830
- package/dist/security/audit-extra.sync.js +505 -0
- package/dist/security/audit.js +2 -2
- package/dist/security/channel-metadata.js +34 -0
- package/dist/security/external-content.js +88 -6
- package/dist/security/skill-scanner.js +330 -0
- package/dist/sessions/session-key-utils.js +7 -0
- package/dist/shared/text/reasoning-tags.js +52 -7
- package/dist/signal/monitor/event-handler.js +80 -1
- package/dist/slack/monitor/media.js +85 -15
- package/dist/tailscale/detect.js +145 -0
- package/dist/telegram/bot/helpers.js +109 -28
- package/dist/telegram/bot-handlers.js +144 -3
- package/dist/telegram/bot-message-context.js +38 -11
- package/dist/telegram/bot-message-dispatch.js +48 -15
- package/dist/telegram/bot-native-commands.js +86 -29
- package/dist/telegram/bot.js +30 -29
- package/dist/telegram/model-buttons.js +163 -0
- package/dist/telegram/monitor.js +110 -85
- package/dist/telegram/send.js +129 -47
- package/dist/terminal/restore.js +45 -0
- package/dist/test-helpers/state-dir-env.js +16 -0
- package/dist/test-helpers/workspace.js +11 -0
- package/dist/test-utils/channel-plugins.js +82 -0
- package/dist/test-utils/ports.js +73 -0
- package/dist/tts/tts.js +12 -6
- package/dist/tui/tui-session-actions.js +166 -54
- package/dist/utils/fetch-timeout.js +20 -0
- package/dist/utils/normalize-secret-input.js +19 -0
- package/dist/utils/shell-argv.js +61 -0
- package/dist/utils/transcript-tools.js +58 -0
- package/dist/utils.js +55 -14
- package/dist/version.js +42 -5
- package/dist/web/qr-image.js +1 -61
- package/dist/wizard/onboarding.finalize.js +7 -7
- package/dist/wizard/onboarding.js +3 -3
- package/docs/RELEASE_WORKFOTS_COMPARISON.md +3 -3
- package/docs/_config.yml +2 -2
- package/docs/_layouts/default.html +9 -9
- package/docs/concepts/typebox.md +1 -1
- package/docs/docs.json +1 -1
- package/docs/northflank.mdx +7 -7
- package/docs/railway.mdx +3 -3
- package/docs/render.mdx +5 -5
- package/docs/start/lore.md +2 -2
- package/extensions/bluebubbles/index.ts +2 -2
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/bluebubbles/src/accounts.ts +8 -8
- package/extensions/bluebubbles/src/actions.test.ts +22 -22
- package/extensions/bluebubbles/src/actions.ts +5 -5
- package/extensions/bluebubbles/src/attachments.ts +2 -2
- package/extensions/bluebubbles/src/channel.ts +16 -16
- package/extensions/bluebubbles/src/chat.ts +2 -2
- package/extensions/bluebubbles/src/media-send.ts +2 -2
- package/extensions/bluebubbles/src/monitor.test.ts +46 -46
- package/extensions/bluebubbles/src/monitor.ts +5 -5
- package/extensions/bluebubbles/src/onboarding.ts +7 -7
- package/extensions/bluebubbles/src/reactions.ts +2 -2
- package/extensions/bluebubbles/src/send.ts +2 -2
- package/extensions/copilot-proxy/README.md +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/index.ts +2 -2
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/diagnostics-otel/src/service.ts +3 -3
- package/extensions/discord/index.ts +2 -2
- package/extensions/discord/package.json +1 -1
- package/extensions/google-antigravity-auth/README.md +1 -1
- package/extensions/google-antigravity-auth/index.ts +1 -1
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/README.md +1 -1
- package/extensions/google-gemini-cli-auth/oauth.ts +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/index.ts +3 -3
- package/extensions/googlechat/package.json +1 -1
- package/extensions/googlechat/src/accounts.ts +8 -8
- package/extensions/googlechat/src/actions.ts +6 -6
- package/extensions/googlechat/src/channel.ts +21 -21
- package/extensions/googlechat/src/monitor.ts +8 -8
- package/extensions/googlechat/src/onboarding.ts +10 -10
- package/extensions/imessage/index.ts +2 -2
- package/extensions/imessage/package.json +1 -1
- package/extensions/line/index.ts +2 -2
- package/extensions/line/package.json +1 -1
- package/extensions/line/src/card-command.ts +2 -2
- package/extensions/line/src/channel.logout.test.ts +4 -4
- package/extensions/line/src/channel.sendPayload.test.ts +8 -8
- package/extensions/line/src/channel.ts +3 -3
- package/extensions/llm-task/README.md +3 -3
- package/extensions/llm-task/index.ts +2 -2
- package/extensions/llm-task/package.json +1 -1
- package/extensions/llm-task/src/llm-task-tool.ts +4 -4
- package/extensions/lobster/README.md +6 -6
- package/extensions/lobster/index.ts +2 -2
- package/extensions/lobster/src/lobster-tool.test.ts +4 -4
- package/extensions/lobster/src/lobster-tool.ts +2 -2
- package/extensions/matrix/index.ts +2 -2
- package/extensions/matrix/package.json +1 -1
- package/extensions/matrix/src/matrix/client/config.ts +1 -1
- package/extensions/matrix/src/matrix/monitor/handler.ts +1 -1
- package/extensions/matrix/src/onboarding.ts +1 -1
- package/extensions/mattermost/index.ts +2 -2
- package/extensions/mattermost/package.json +1 -1
- package/extensions/mattermost/src/mattermost/accounts.ts +8 -8
- package/extensions/mattermost/src/mattermost/monitor-helpers.ts +5 -5
- package/extensions/mattermost/src/mattermost/monitor.ts +2 -2
- package/extensions/mattermost/src/onboarding-helpers.ts +3 -3
- package/extensions/mattermost/src/onboarding.ts +2 -2
- package/extensions/memory-core/index.ts +2 -2
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/index.ts +3 -3
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/msteams/index.ts +2 -2
- package/extensions/msteams/package.json +1 -1
- package/extensions/msteams/src/channel.directory.test.ts +2 -2
- package/extensions/msteams/src/channel.ts +2 -2
- package/extensions/msteams/src/graph-upload.ts +4 -4
- package/extensions/msteams/src/monitor-handler.ts +2 -2
- package/extensions/msteams/src/monitor.ts +2 -2
- package/extensions/msteams/src/onboarding.ts +9 -9
- package/extensions/msteams/src/reply-dispatcher.ts +2 -2
- package/extensions/msteams/src/send-context.ts +2 -2
- package/extensions/msteams/src/send.ts +4 -4
- package/extensions/nextcloud-talk/index.ts +2 -2
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nextcloud-talk/src/channel.ts +7 -7
- package/extensions/nextcloud-talk/src/inbound.ts +7 -7
- package/extensions/nextcloud-talk/src/onboarding.ts +1 -1
- package/extensions/nostr/README.md +2 -2
- package/extensions/nostr/index.ts +5 -5
- package/extensions/nostr/package.json +1 -1
- package/extensions/nostr/src/types.ts +4 -4
- package/extensions/open-prose/index.ts +2 -2
- package/extensions/qwen-portal-auth/README.md +1 -1
- package/extensions/signal/index.ts +2 -2
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/index.ts +2 -2
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/index.ts +2 -2
- package/extensions/telegram/package.json +1 -1
- package/extensions/telegram/src/channel.ts +2 -2
- package/extensions/tlon/README.md +2 -2
- package/extensions/tlon/index.ts +2 -2
- package/extensions/tlon/package.json +1 -1
- package/extensions/tlon/src/channel.ts +13 -13
- package/extensions/tlon/src/monitor/index.ts +3 -3
- package/extensions/tlon/src/onboarding.ts +3 -3
- package/extensions/tlon/src/types.ts +3 -3
- package/extensions/twitch/README.md +1 -1
- package/extensions/twitch/index.ts +2 -2
- package/extensions/twitch/package.json +1 -1
- package/extensions/twitch/src/config.ts +3 -3
- package/extensions/twitch/src/monitor.ts +3 -3
- package/extensions/twitch/src/onboarding.ts +9 -9
- package/extensions/twitch/src/outbound.test.ts +2 -2
- package/extensions/twitch/src/plugin.test.ts +2 -2
- package/extensions/twitch/src/plugin.ts +8 -8
- package/extensions/twitch/src/send.test.ts +2 -2
- package/extensions/twitch/src/send.ts +4 -4
- package/extensions/twitch/src/token.test.ts +8 -8
- package/extensions/twitch/src/token.ts +3 -3
- package/extensions/twitch/src/twitch-client.ts +3 -3
- package/extensions/twitch/src/types.ts +3 -3
- package/extensions/twitch/src/utils/markdown.ts +1 -1
- package/extensions/voice-call/README.md +3 -3
- package/extensions/voice-call/package.json +1 -1
- package/extensions/voice-call/src/core-bridge.ts +2 -2
- package/extensions/voice-call/src/response-generator.ts +1 -1
- package/extensions/whatsapp/index.ts +2 -2
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/README.md +1 -1
- package/extensions/zalo/index.ts +2 -2
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalo/src/accounts.ts +8 -8
- package/extensions/zalo/src/actions.ts +4 -4
- package/extensions/zalo/src/channel.directory.test.ts +2 -2
- package/extensions/zalo/src/channel.ts +18 -18
- package/extensions/zalo/src/monitor.ts +9 -9
- package/extensions/zalo/src/monitor.webhook.test.ts +2 -2
- package/extensions/zalo/src/onboarding.ts +24 -24
- package/extensions/zalo/src/send.ts +2 -2
- package/extensions/zalouser/README.md +2 -2
- package/extensions/zalouser/index.ts +2 -2
- package/extensions/zalouser/package.json +1 -1
- package/extensions/zalouser/src/accounts.ts +9 -9
- package/extensions/zalouser/src/channel.ts +24 -24
- package/extensions/zalouser/src/monitor.ts +4 -4
- package/extensions/zalouser/src/onboarding.ts +28 -28
- package/package.json +13 -251
- package/skills/nano-banana-pro/scripts/generate_image.py +1 -1
- package/skills/tmux/scripts/find-sessions.sh +1 -1
- package/CHANGELOG.md +0 -102
- package/README-header.png +0 -0
- package/git-hooks/pre-commit +0 -4
- package/scripts/format-staged.js +0 -148
- package/scripts/postinstall.js +0 -300
- 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 (
|
|
22
|
-
|
|
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 (
|
|
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,
|
|
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
|
+
};
|
package/dist/agents/pi-tools.js
CHANGED
|
@@ -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,
|
|
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
|
|
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 [
|
|
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
|
-
|
|
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(
|
|
262
|
-
:
|
|
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
|
-
?
|
|
292
|
-
:
|
|
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(
|
|
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
|
|
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
|
-
|
|
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 === "
|
|
58
|
-
const
|
|
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
|
-
|
|
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 =
|
|
69
|
-
? extractAssistantToolCalls(
|
|
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 ||
|
|
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(
|
|
160
|
+
const result = originalAppend(nextMessage);
|
|
82
161
|
const sessionFile = sessionManager.getSessionFile?.();
|
|
83
162
|
if (sessionFile) {
|
|
84
163
|
emitSessionTranscriptUpdate(sessionFile);
|