@poolzin/pool-bot 2026.2.21 → 2026.2.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/agents/api-key-rotation.js +47 -0
- package/dist/agents/apply-patch-update.js +19 -9
- package/dist/agents/apply-patch.js +72 -47
- package/dist/agents/bash-tools.exec.js +141 -559
- package/dist/agents/cli-backends.js +49 -6
- package/dist/agents/cli-runner/helpers.js +69 -152
- package/dist/agents/cli-runner.js +70 -19
- package/dist/agents/identity.js +20 -1
- package/dist/agents/image-sanitization.js +9 -0
- package/dist/agents/live-auth-keys.js +123 -26
- package/dist/agents/live-model-filter.js +13 -4
- package/dist/agents/model-catalog.js +40 -9
- package/dist/agents/model-forward-compat.js +60 -23
- package/dist/agents/model-selection.js +134 -41
- package/dist/agents/pi-auth-json.js +2 -2
- package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
- package/dist/agents/pi-embedded-helpers/errors.js +140 -15
- package/dist/agents/pi-embedded-helpers/images.js +22 -12
- package/dist/agents/pi-embedded-helpers.js +2 -2
- package/dist/agents/pi-embedded-runner/abort.js +10 -3
- package/dist/agents/pi-embedded-runner/compact.js +230 -32
- package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
- package/dist/agents/pi-embedded-runner/google.js +109 -19
- package/dist/agents/pi-embedded-runner/history.js +35 -17
- package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
- package/dist/agents/pi-embedded-runner/run/images.js +81 -55
- package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
- package/dist/agents/pi-embedded-runner/run.js +193 -25
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
- package/dist/agents/pi-embedded-runner/runs.js +17 -8
- package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
- package/dist/agents/pi-embedded-runner.js +1 -1
- package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
- package/dist/agents/pi-embedded-subscribe.js +37 -0
- package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
- package/dist/agents/pi-model-discovery.js +9 -2
- package/dist/agents/pi-tool-definition-adapter.js +60 -8
- package/dist/agents/pi-tools.before-tool-call.js +1 -1
- package/dist/agents/pi-tools.js +113 -94
- package/dist/agents/pi-tools.read.js +337 -38
- package/dist/agents/poolbot-tools.js +14 -5
- package/dist/agents/sandbox/docker.js +10 -5
- package/dist/agents/sandbox/registry.js +96 -46
- package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
- package/dist/agents/sandbox-paths.js +43 -10
- package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
- package/dist/agents/session-tool-result-guard.js +39 -39
- package/dist/agents/session-transcript-repair.js +36 -33
- package/dist/agents/session-write-lock.js +62 -44
- package/dist/agents/skills/frontmatter.js +49 -88
- package/dist/agents/skills/workspace.js +335 -28
- package/dist/agents/subagent-announce.js +508 -174
- package/dist/agents/subagent-registry.js +45 -4
- package/dist/agents/subagent-spawn.js +16 -33
- package/dist/agents/system-prompt-report.js +27 -10
- package/dist/agents/system-prompt.js +26 -32
- package/dist/agents/tool-call-id.js +69 -17
- package/dist/agents/tool-display-common.js +1 -1
- package/dist/agents/tool-images.js +64 -31
- package/dist/agents/tools/canvas-tool.js +17 -11
- package/dist/agents/tools/common.js +37 -19
- package/dist/agents/tools/cron-tool.js +40 -38
- package/dist/agents/tools/gateway.js +70 -2
- package/dist/agents/tools/message-tool.js +181 -40
- package/dist/agents/tools/nodes-tool.js +128 -36
- package/dist/agents/tools/nodes-utils.js +12 -38
- package/dist/agents/tools/session-status-tool.js +24 -71
- package/dist/agents/tools/sessions-helpers.js +38 -210
- package/dist/agents/tools/sessions-spawn-tool.js +28 -198
- package/dist/agents/tools/telegram-actions.js +58 -7
- package/dist/agents/tools/web-fetch-utils.js +112 -7
- package/dist/agents/tools/web-fetch.js +279 -175
- package/dist/agents/tools/web-shared.js +71 -8
- package/dist/agents/usage.js +25 -16
- package/dist/auto-reply/commands-registry.data.js +85 -11
- package/dist/auto-reply/dispatch.js +40 -21
- package/dist/auto-reply/reply/abort.js +102 -33
- package/dist/auto-reply/reply/commands-core.js +82 -33
- package/dist/auto-reply/reply/commands-export-session.js +1 -1
- package/dist/auto-reply/reply/commands-info.js +41 -12
- package/dist/auto-reply/reply/commands-subagents.js +352 -100
- package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
- package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
- package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
- package/dist/auto-reply/reply/inbound-meta.js +12 -1
- package/dist/auto-reply/reply/mentions.js +18 -11
- package/dist/auto-reply/reply/normalize-reply.js +17 -8
- package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
- package/dist/auto-reply/reply/session.js +102 -21
- package/dist/auto-reply/reply/streaming-directives.js +16 -5
- package/dist/auto-reply/status.js +73 -50
- package/dist/browser/extension-relay.js +3 -3
- package/dist/browser/http-auth.js +1 -1
- package/dist/browser/paths.js +2 -2
- package/dist/build-info.json +3 -3
- package/dist/channels/allowlist-match.js +20 -0
- package/dist/channels/allowlists/resolve-utils.js +65 -2
- package/dist/channels/chat-type.js +8 -4
- package/dist/channels/dock.js +127 -35
- package/dist/channels/draft-stream-loop.js +6 -2
- package/dist/channels/plugins/actions/telegram.js +42 -18
- package/dist/channels/plugins/allowlist-match.js +1 -1
- package/dist/channels/plugins/group-mentions.js +51 -41
- package/dist/channels/plugins/message-action-names.js +2 -0
- package/dist/channels/plugins/message-actions.js +24 -5
- package/dist/channels/plugins/normalize/discord.js +26 -4
- package/dist/channels/plugins/normalize/signal.js +35 -22
- package/dist/channels/plugins/onboarding/helpers.js +8 -26
- package/dist/channels/plugins/outbound/imessage.js +15 -14
- package/dist/channels/registry.js +20 -7
- package/dist/cli/acp-cli.js +7 -5
- package/dist/cli/browser-cli-extension.js +25 -12
- package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
- package/dist/cli/browser-cli-state.js +101 -145
- package/dist/cli/command-options.js +28 -0
- package/dist/cli/completion-cli.js +6 -6
- package/dist/cli/cron-cli/register.cron-add.js +25 -1
- package/dist/cli/cron-cli/register.cron-edit.js +44 -0
- package/dist/cli/cron-cli/shared.js +7 -1
- package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
- package/dist/cli/daemon-cli/lifecycle.js +23 -247
- package/dist/cli/daemon-cli/register-service-commands.js +25 -4
- package/dist/cli/daemon-cli.js +1 -0
- package/dist/cli/devices-cli.js +33 -20
- package/dist/cli/gateway-cli/register.js +37 -105
- package/dist/cli/gateway-cli/run.js +49 -11
- package/dist/cli/nodes-camera.js +59 -4
- package/dist/cli/nodes-cli/register.camera.js +27 -24
- package/dist/cli/nodes-cli/rpc.js +21 -38
- package/dist/cli/qr-cli.js +2 -2
- package/dist/cli/skills-cli.format.js +2 -2
- package/dist/cli/update-cli/progress.js +2 -2
- package/dist/cli/update-cli/restart-helper.js +28 -7
- package/dist/cli/update-cli/shared.js +7 -7
- package/dist/cli/update-cli/status.js +1 -1
- package/dist/cli/update-cli/update-command.js +14 -8
- package/dist/cli/update-cli/wizard.js +2 -2
- package/dist/cli/update-cli.js +21 -1027
- package/dist/commands/auth-choice.apply.anthropic.js +10 -2
- package/dist/commands/channels/add-mutators.js +3 -35
- package/dist/commands/channels/add.js +39 -51
- package/dist/commands/config-validation.js +1 -1
- package/dist/commands/configure.gateway-auth.js +52 -15
- package/dist/commands/configure.gateway.js +84 -40
- package/dist/commands/doctor-completion.js +3 -3
- package/dist/commands/doctor-config-flow.js +536 -16
- package/dist/commands/doctor-gateway-services.js +103 -79
- package/dist/commands/doctor-memory-search.js +9 -9
- package/dist/commands/doctor-platform-notes.js +57 -30
- package/dist/commands/doctor-prompter.js +26 -15
- package/dist/commands/doctor-session-locks.js +1 -1
- package/dist/commands/doctor.js +21 -9
- package/dist/commands/model-picker.js +120 -95
- package/dist/commands/models/set.js +2 -21
- package/dist/commands/models/shared.js +65 -37
- package/dist/commands/onboard-helpers.js +81 -39
- package/dist/commands/openai-codex-oauth.js +1 -1
- package/dist/commands/sessions.js +52 -53
- package/dist/commands/status.summary.js +52 -34
- package/dist/commands/test-wizard-helpers.js +2 -2
- package/dist/config/defaults.js +79 -42
- package/dist/config/group-policy.js +50 -18
- package/dist/config/includes.js +37 -10
- package/dist/config/schema.help.js +5 -4
- package/dist/config/schema.hints.js +2 -2
- package/dist/config/schema.labels.js +1 -0
- package/dist/config/sessions/group.js +12 -11
- package/dist/config/sessions/paths.js +137 -11
- package/dist/config/sessions/store.js +185 -65
- package/dist/config/sessions/types.js +15 -1
- package/dist/config/sessions.js +1 -0
- package/dist/config/telegram-custom-commands.js +3 -2
- package/dist/config/types.js +2 -0
- package/dist/config/zod-schema.agent-defaults.js +6 -27
- package/dist/config/zod-schema.agent-runtime.js +171 -79
- package/dist/config/zod-schema.providers-core.js +138 -65
- package/dist/config/zod-schema.session.js +49 -22
- package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
- package/dist/cron/isolated-agent/run.js +224 -57
- package/dist/cron/normalize.js +48 -45
- package/dist/cron/run-log.js +14 -0
- package/dist/cron/service/jobs.js +190 -28
- package/dist/cron/service/normalize.js +29 -11
- package/dist/cron/service/store.js +30 -44
- package/dist/cron/service/timer.js +182 -96
- package/dist/cron/service.js +3 -0
- package/dist/cron/stagger.js +37 -0
- package/dist/daemon/inspect.js +132 -92
- package/dist/daemon/runtime-paths.js +25 -4
- package/dist/daemon/service-audit.js +47 -16
- package/dist/discord/accounts.js +23 -20
- package/dist/discord/monitor/agent-components.js +1115 -219
- package/dist/discord/monitor/allow-list.js +114 -34
- package/dist/discord/monitor/listeners.js +204 -97
- package/dist/discord/monitor/message-handler.js +21 -10
- package/dist/discord/monitor/message-handler.preflight.js +195 -101
- package/dist/discord/monitor/message-handler.process.js +384 -123
- package/dist/discord/monitor/message-utils.js +86 -23
- package/dist/discord/monitor/native-command.js +77 -57
- package/dist/discord/monitor/provider.js +122 -117
- package/dist/discord/monitor/reply-context.js +20 -16
- package/dist/discord/monitor/reply-delivery.js +40 -8
- package/dist/discord/monitor/rest-fetch.js +22 -0
- package/dist/discord/monitor/threading.js +117 -24
- package/dist/discord/send.js +2 -1
- package/dist/discord/send.outbound.js +124 -11
- package/dist/discord/send.shared.js +112 -72
- package/dist/discord/voice-message.js +3 -3
- package/dist/gateway/auth.js +119 -44
- package/dist/gateway/call.js +76 -34
- package/dist/gateway/channel-health-monitor.js +57 -50
- package/dist/gateway/client.js +63 -29
- package/dist/gateway/control-ui-contract.js +1 -1
- package/dist/gateway/gateway-config-prompts.shared.js +2 -2
- package/dist/gateway/net.js +109 -1
- package/dist/gateway/protocol/index.js +5 -8
- package/dist/gateway/protocol/schema/agent.js +19 -1
- package/dist/gateway/protocol/schema/channels.js +21 -0
- package/dist/gateway/protocol/schema/cron.js +43 -30
- package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
- package/dist/gateway/protocol/schema/sessions.js +5 -1
- package/dist/gateway/protocol/schema.js +0 -1
- package/dist/gateway/server/presence-events.js +12 -0
- package/dist/gateway/server/ws-connection/message-handler.js +203 -212
- package/dist/gateway/server/ws-connection.js +58 -21
- package/dist/gateway/server-broadcast.js +18 -13
- package/dist/gateway/server-cron.js +177 -10
- package/dist/gateway/server-methods/agent-job.js +131 -38
- package/dist/gateway/server-methods/send.js +60 -14
- package/dist/gateway/server-methods/sessions.js +160 -96
- package/dist/gateway/server-methods/system.js +5 -7
- package/dist/gateway/server-methods-list.js +8 -0
- package/dist/gateway/server-methods.js +24 -8
- package/dist/gateway/server-node-events.js +278 -68
- package/dist/gateway/session-utils.fs.js +316 -75
- package/dist/gateway/session-utils.js +224 -70
- package/dist/gateway/sessions-patch.js +63 -20
- package/dist/gateway/test-temp-config.js +1 -1
- package/dist/gateway/tools-invoke-http.js +118 -70
- package/dist/gateway/ws-log.js +135 -107
- package/dist/hooks/frontmatter.js +36 -82
- package/dist/hooks/install.js +149 -139
- package/dist/hooks/internal-hooks.js +29 -4
- package/dist/hooks/plugin-hooks.js +2 -1
- package/dist/imessage/monitor/deliver.js +10 -4
- package/dist/imessage/monitor/monitor-provider.js +138 -375
- package/dist/imessage/monitor/runtime.js +4 -8
- package/dist/imessage/send.js +65 -19
- package/dist/infra/exec-approvals-allowlist.js +7 -0
- package/dist/infra/exec-approvals.js +35 -920
- package/dist/infra/exec-safe-bin-trust.js +64 -0
- package/dist/infra/heartbeat-runner.js +207 -134
- package/dist/infra/heartbeat-wake.js +183 -22
- package/dist/infra/install-source-utils.js +47 -0
- package/dist/infra/net/ssrf.js +170 -36
- package/dist/infra/outbound/deliver.js +224 -58
- package/dist/infra/outbound/message-action-spec.js +12 -5
- package/dist/infra/outbound/outbound-session.js +27 -25
- package/dist/infra/poolbot-root.js +32 -22
- package/dist/infra/ports.js +14 -11
- package/dist/infra/skills-remote.js +48 -37
- package/dist/infra/system-events.js +25 -11
- package/dist/infra/system-presence.js +26 -33
- package/dist/infra/tmp-poolbot-dir.js +81 -2
- package/dist/infra/wsl.js +37 -1
- package/dist/line/bot-message-context.js +163 -191
- package/dist/logging/subsystem.js +59 -22
- package/dist/markdown/ir.js +124 -50
- package/dist/media/store.js +1 -1
- package/dist/media-understanding/runner.entries.js +42 -25
- package/dist/media-understanding/runner.js +53 -488
- package/dist/memory/embeddings-gemini.js +53 -38
- package/dist/memory/manager-embedding-ops.js +48 -69
- package/dist/pairing/pairing-store.js +178 -119
- package/dist/plugin-sdk/index.js +34 -6
- package/dist/plugins/hooks.js +135 -14
- package/dist/plugins/install.js +190 -152
- package/dist/polls.js +11 -0
- package/dist/routing/resolve-route.js +190 -56
- package/dist/routing/session-key.js +38 -22
- package/dist/runtime.js +35 -9
- package/dist/security/audit-channel.js +1 -1
- package/dist/sessions/session-key-utils.js +29 -11
- package/dist/shared/frontmatter.js +5 -5
- package/dist/shared/node-list-types.js +1 -0
- package/dist/shared/string-normalization.js +15 -0
- package/dist/signal/monitor/event-handler.js +68 -36
- package/dist/signal/send.js +29 -37
- package/dist/slack/monitor/allow-list.js +10 -11
- package/dist/slack/monitor/commands.js +14 -3
- package/dist/slack/monitor/events/interactions.js +4 -4
- package/dist/slack/monitor/media.js +224 -16
- package/dist/slack/monitor/message-handler/dispatch.js +247 -13
- package/dist/slack/monitor/message-handler/prepare.js +128 -45
- package/dist/slack/monitor/slash.js +357 -144
- package/dist/slack/streaming.js +77 -0
- package/dist/telegram/accounts.js +40 -13
- package/dist/telegram/allowed-updates.js +3 -0
- package/dist/telegram/bot/delivery.js +129 -66
- package/dist/telegram/bot/helpers.js +136 -122
- package/dist/telegram/bot-handlers.js +600 -339
- package/dist/telegram/bot-message-context.js +115 -73
- package/dist/telegram/bot-message-dispatch.js +235 -104
- package/dist/telegram/bot-native-command-menu.js +3 -1
- package/dist/telegram/bot-native-commands.js +213 -193
- package/dist/telegram/bot.js +24 -132
- package/dist/telegram/draft-stream.js +84 -75
- package/dist/telegram/format.js +150 -6
- package/dist/telegram/send.js +415 -255
- package/dist/telegram/targets.js +21 -2
- package/dist/telegram/update-offset-store.js +19 -3
- package/dist/terminal/restore.js +5 -2
- package/dist/test-utils/fetch-mock.js +5 -0
- package/dist/version.js +18 -5
- package/dist/web/auto-reply/monitor/broadcast.js +7 -3
- package/dist/web/auto-reply/monitor/on-message.js +6 -3
- package/dist/web/inbound/media.js +34 -8
- package/dist/web/inbound/monitor.js +34 -17
- package/dist/web/inbound/send-api.js +18 -17
- package/dist/web/outbound.js +12 -5
- package/dist/wizard/clack-prompter.js +40 -7
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +5 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +5 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +5 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +1 -1
- package/skills/apple-reminders/SKILL.md +100 -49
- package/skills/coding-agent/SKILL.md +34 -28
- package/skills/github/SKILL.md +131 -16
- package/skills/imsg/SKILL.md +112 -15
- package/skills/openhue/SKILL.md +101 -19
- package/skills/tmux/SKILL.md +111 -79
- package/skills/weather/SKILL.md +88 -25
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { assertSandboxPath } from "../../sandbox-paths.js";
|
|
5
|
-
import { sanitizeImageBlocks } from "../../tool-images.js";
|
|
6
|
-
import { extractTextFromMessage } from "../../../tui/tui-formatters.js";
|
|
7
|
-
import { loadWebMedia } from "../../../web/media.js";
|
|
8
3
|
import { resolveUserPath } from "../../../utils.js";
|
|
4
|
+
import { loadWebMedia } from "../../../web/media.js";
|
|
5
|
+
import { sanitizeImageBlocks } from "../../tool-images.js";
|
|
9
6
|
import { log } from "../logger.js";
|
|
10
7
|
/**
|
|
11
8
|
* Common image file extensions for detection.
|
|
@@ -29,8 +26,8 @@ function isImageExtension(filePath) {
|
|
|
29
26
|
const ext = path.extname(filePath).toLowerCase();
|
|
30
27
|
return IMAGE_EXTENSIONS.has(ext);
|
|
31
28
|
}
|
|
32
|
-
async function sanitizeImagesWithLog(images, label) {
|
|
33
|
-
const { images: sanitized, dropped } = await sanitizeImageBlocks(images, label);
|
|
29
|
+
async function sanitizeImagesWithLog(images, label, imageSanitization) {
|
|
30
|
+
const { images: sanitized, dropped } = await sanitizeImageBlocks(images, label, imageSanitization);
|
|
34
31
|
if (dropped > 0) {
|
|
35
32
|
log.warn(`Native image: dropped ${dropped} image(s) after sanitization (${label}).`);
|
|
36
33
|
}
|
|
@@ -55,12 +52,15 @@ export function detectImageReferences(prompt) {
|
|
|
55
52
|
// Helper to add a path ref
|
|
56
53
|
const addPathRef = (raw) => {
|
|
57
54
|
const trimmed = raw.trim();
|
|
58
|
-
if (!trimmed || seen.has(trimmed.toLowerCase()))
|
|
55
|
+
if (!trimmed || seen.has(trimmed.toLowerCase())) {
|
|
59
56
|
return;
|
|
60
|
-
|
|
57
|
+
}
|
|
58
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
|
|
61
59
|
return;
|
|
62
|
-
|
|
60
|
+
}
|
|
61
|
+
if (!isImageExtension(trimmed)) {
|
|
63
62
|
return;
|
|
63
|
+
}
|
|
64
64
|
seen.add(trimmed.toLowerCase());
|
|
65
65
|
const resolved = trimmed.startsWith("~") ? resolveUserPath(trimmed) : trimmed;
|
|
66
66
|
refs.push({ raw: trimmed, type: "path", resolved });
|
|
@@ -89,16 +89,18 @@ export function detectImageReferences(prompt) {
|
|
|
89
89
|
const messageImagePattern = /\[Image:\s*source:\s*([^\]]+\.(?:png|jpe?g|gif|webp|bmp|tiff?|heic|heif))\]/gi;
|
|
90
90
|
while ((match = messageImagePattern.exec(prompt)) !== null) {
|
|
91
91
|
const raw = match[1]?.trim();
|
|
92
|
-
if (raw)
|
|
92
|
+
if (raw) {
|
|
93
93
|
addPathRef(raw);
|
|
94
|
+
}
|
|
94
95
|
}
|
|
95
96
|
// Remote HTTP(S) URLs are intentionally ignored. Native image injection is local-only.
|
|
96
97
|
// Pattern for file:// URLs - treat as paths since loadWebMedia handles them
|
|
97
98
|
const fileUrlPattern = /file:\/\/[^\s<>"'`\]]+\.(?:png|jpe?g|gif|webp|bmp|tiff?|heic|heif)/gi;
|
|
98
99
|
while ((match = fileUrlPattern.exec(prompt)) !== null) {
|
|
99
100
|
const raw = match[0];
|
|
100
|
-
if (seen.has(raw.toLowerCase()))
|
|
101
|
+
if (seen.has(raw.toLowerCase())) {
|
|
101
102
|
continue;
|
|
103
|
+
}
|
|
102
104
|
seen.add(raw.toLowerCase());
|
|
103
105
|
// Use fileURLToPath for proper handling (e.g., file://localhost/path)
|
|
104
106
|
try {
|
|
@@ -118,8 +120,9 @@ export function detectImageReferences(prompt) {
|
|
|
118
120
|
const pathPattern = /(?:^|\s|["'`(])((\.\.?\/|[~/])[^\s"'`()[\]]*\.(?:png|jpe?g|gif|webp|bmp|tiff?|heic|heif))/gi;
|
|
119
121
|
while ((match = pathPattern.exec(prompt)) !== null) {
|
|
120
122
|
// Use capture group 1 (the path without delimiter prefix); skip if undefined
|
|
121
|
-
if (match[1])
|
|
123
|
+
if (match[1]) {
|
|
122
124
|
addPathRef(match[1]);
|
|
125
|
+
}
|
|
123
126
|
}
|
|
124
127
|
return refs;
|
|
125
128
|
}
|
|
@@ -139,43 +142,33 @@ export async function loadImageFromRef(ref, workspaceDir, options) {
|
|
|
139
142
|
log.debug(`Native image: rejecting remote URL (local-only): ${ref.resolved}`);
|
|
140
143
|
return null;
|
|
141
144
|
}
|
|
142
|
-
//
|
|
143
|
-
// - When sandbox is enabled, resolve relative to sandboxRoot for security
|
|
144
|
-
// - Otherwise, resolve relative to workspaceDir
|
|
145
|
-
// Note: ref.resolved may already be absolute (e.g., after ~ expansion in detectImageReferences),
|
|
146
|
-
// in which case we skip relative resolution.
|
|
147
|
-
if (ref.type === "path" && !path.isAbsolute(targetPath)) {
|
|
148
|
-
const resolveRoot = options?.sandboxRoot ?? workspaceDir;
|
|
149
|
-
targetPath = path.resolve(resolveRoot, targetPath);
|
|
150
|
-
}
|
|
151
|
-
// Enforce sandbox restrictions if sandboxRoot is set
|
|
152
|
-
if (ref.type === "path" && options?.sandboxRoot) {
|
|
153
|
-
try {
|
|
154
|
-
const validated = await assertSandboxPath({
|
|
155
|
-
filePath: targetPath,
|
|
156
|
-
cwd: options.sandboxRoot,
|
|
157
|
-
root: options.sandboxRoot,
|
|
158
|
-
});
|
|
159
|
-
targetPath = validated.resolved;
|
|
160
|
-
}
|
|
161
|
-
catch (err) {
|
|
162
|
-
// Log the actual error for debugging (sandbox violation or other path error)
|
|
163
|
-
log.debug(`Native image: sandbox validation failed for ${ref.resolved}: ${err instanceof Error ? err.message : String(err)}`);
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
// Check file exists for local paths
|
|
145
|
+
// Resolve paths relative to sandbox or workspace as needed
|
|
168
146
|
if (ref.type === "path") {
|
|
169
|
-
|
|
170
|
-
|
|
147
|
+
if (options?.sandbox) {
|
|
148
|
+
try {
|
|
149
|
+
const resolved = options.sandbox.bridge.resolvePath({
|
|
150
|
+
filePath: targetPath,
|
|
151
|
+
cwd: options.sandbox.root,
|
|
152
|
+
});
|
|
153
|
+
targetPath = resolved.hostPath;
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
log.debug(`Native image: sandbox validation failed for ${ref.resolved}: ${err instanceof Error ? err.message : String(err)}`);
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
171
159
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return null;
|
|
160
|
+
else if (!path.isAbsolute(targetPath)) {
|
|
161
|
+
targetPath = path.resolve(workspaceDir, targetPath);
|
|
175
162
|
}
|
|
176
163
|
}
|
|
177
164
|
// loadWebMedia handles local file paths (including file:// URLs)
|
|
178
|
-
const media =
|
|
165
|
+
const media = options?.sandbox
|
|
166
|
+
? await loadWebMedia(targetPath, {
|
|
167
|
+
maxBytes: options.maxBytes,
|
|
168
|
+
sandboxValidated: true,
|
|
169
|
+
readFile: (filePath) => options.sandbox.bridge.readFile({ filePath, cwd: options.sandbox.root }),
|
|
170
|
+
})
|
|
171
|
+
: await loadWebMedia(targetPath, options?.maxBytes);
|
|
179
172
|
if (media.kind !== "image") {
|
|
180
173
|
log.debug(`Native image: not an image file: ${targetPath} (got ${media.kind})`);
|
|
181
174
|
return null;
|
|
@@ -201,6 +194,29 @@ export async function loadImageFromRef(ref, workspaceDir, options) {
|
|
|
201
194
|
export function modelSupportsImages(model) {
|
|
202
195
|
return model.input?.includes("image") ?? false;
|
|
203
196
|
}
|
|
197
|
+
function extractTextFromMessage(message) {
|
|
198
|
+
if (!message || typeof message !== "object") {
|
|
199
|
+
return "";
|
|
200
|
+
}
|
|
201
|
+
const content = message.content;
|
|
202
|
+
if (typeof content === "string") {
|
|
203
|
+
return content;
|
|
204
|
+
}
|
|
205
|
+
if (!Array.isArray(content)) {
|
|
206
|
+
return "";
|
|
207
|
+
}
|
|
208
|
+
const textParts = [];
|
|
209
|
+
for (const part of content) {
|
|
210
|
+
if (!part || typeof part !== "object") {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
const record = part;
|
|
214
|
+
if (record.type === "text" && typeof record.text === "string") {
|
|
215
|
+
textParts.push(record.text);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return textParts.join("\n").trim();
|
|
219
|
+
}
|
|
204
220
|
/**
|
|
205
221
|
* Extracts image references from conversation history messages.
|
|
206
222
|
* Scans user messages for image paths/URLs that can be loaded.
|
|
@@ -216,32 +232,39 @@ function detectImagesFromHistory(messages) {
|
|
|
216
232
|
const allRefs = [];
|
|
217
233
|
const seen = new Set();
|
|
218
234
|
const messageHasImageContent = (msg) => {
|
|
219
|
-
if (!msg || typeof msg !== "object")
|
|
235
|
+
if (!msg || typeof msg !== "object") {
|
|
220
236
|
return false;
|
|
237
|
+
}
|
|
221
238
|
const content = msg.content;
|
|
222
|
-
if (!Array.isArray(content))
|
|
239
|
+
if (!Array.isArray(content)) {
|
|
223
240
|
return false;
|
|
241
|
+
}
|
|
224
242
|
return content.some((part) => part != null && typeof part === "object" && part.type === "image");
|
|
225
243
|
};
|
|
226
244
|
for (let i = 0; i < messages.length; i++) {
|
|
227
245
|
const msg = messages[i];
|
|
228
|
-
if (!msg || typeof msg !== "object")
|
|
246
|
+
if (!msg || typeof msg !== "object") {
|
|
229
247
|
continue;
|
|
248
|
+
}
|
|
230
249
|
const message = msg;
|
|
231
250
|
// Only scan user messages for image references
|
|
232
|
-
if (message.role !== "user")
|
|
251
|
+
if (message.role !== "user") {
|
|
233
252
|
continue;
|
|
253
|
+
}
|
|
234
254
|
// Skip if message already has image content (prevents reloading each turn)
|
|
235
|
-
if (messageHasImageContent(msg))
|
|
255
|
+
if (messageHasImageContent(msg)) {
|
|
236
256
|
continue;
|
|
257
|
+
}
|
|
237
258
|
const text = extractTextFromMessage(msg);
|
|
238
|
-
if (!text)
|
|
259
|
+
if (!text) {
|
|
239
260
|
continue;
|
|
261
|
+
}
|
|
240
262
|
const refs = detectImageReferences(text);
|
|
241
263
|
for (const ref of refs) {
|
|
242
264
|
const key = ref.resolved.toLowerCase();
|
|
243
|
-
if (seen.has(key))
|
|
265
|
+
if (seen.has(key)) {
|
|
244
266
|
continue;
|
|
267
|
+
}
|
|
245
268
|
seen.add(key);
|
|
246
269
|
allRefs.push({ ...ref, messageIndex: i });
|
|
247
270
|
}
|
|
@@ -302,7 +325,7 @@ export async function detectAndLoadPromptImages(params) {
|
|
|
302
325
|
for (const ref of allRefs) {
|
|
303
326
|
const image = await loadImageFromRef(ref, params.workspaceDir, {
|
|
304
327
|
maxBytes: params.maxBytes,
|
|
305
|
-
|
|
328
|
+
sandbox: params.sandbox,
|
|
306
329
|
});
|
|
307
330
|
if (image) {
|
|
308
331
|
if (ref.messageIndex !== undefined) {
|
|
@@ -326,10 +349,13 @@ export async function detectAndLoadPromptImages(params) {
|
|
|
326
349
|
skippedCount++;
|
|
327
350
|
}
|
|
328
351
|
}
|
|
329
|
-
const
|
|
352
|
+
const imageSanitization = {
|
|
353
|
+
maxDimensionPx: params.maxDimensionPx,
|
|
354
|
+
};
|
|
355
|
+
const sanitizedPromptImages = await sanitizeImagesWithLog(promptImages, "prompt:images", imageSanitization);
|
|
330
356
|
const sanitizedHistoryImagesByIndex = new Map();
|
|
331
357
|
for (const [index, images] of historyImagesByIndex) {
|
|
332
|
-
const sanitized = await sanitizeImagesWithLog(images, `history:images:${index}
|
|
358
|
+
const sanitized = await sanitizeImagesWithLog(images, `history:images:${index}`, imageSanitization);
|
|
333
359
|
if (sanitized.length > 0) {
|
|
334
360
|
sanitizedHistoryImagesByIndex.set(index, sanitized);
|
|
335
361
|
}
|
|
@@ -1,8 +1,35 @@
|
|
|
1
1
|
import { parseReplyDirectives } from "../../../auto-reply/reply/reply-directives.js";
|
|
2
2
|
import { isSilentReplyText, SILENT_REPLY_TOKEN } from "../../../auto-reply/tokens.js";
|
|
3
3
|
import { formatToolAggregate } from "../../../auto-reply/tool-meta.js";
|
|
4
|
-
import { formatAssistantErrorText, formatRawAssistantErrorForUi, getApiErrorPayloadFingerprint, isRawApiErrorPayload, normalizeTextForComparison, } from "../../pi-embedded-helpers.js";
|
|
4
|
+
import { BILLING_ERROR_USER_MESSAGE, formatAssistantErrorText, formatRawAssistantErrorForUi, getApiErrorPayloadFingerprint, isRawApiErrorPayload, normalizeTextForComparison, } from "../../pi-embedded-helpers.js";
|
|
5
5
|
import { extractAssistantText, extractAssistantThinking, formatReasoningMessage, } from "../../pi-embedded-utils.js";
|
|
6
|
+
import { isLikelyMutatingToolName } from "../../tool-mutation.js";
|
|
7
|
+
const RECOVERABLE_TOOL_ERROR_KEYWORDS = [
|
|
8
|
+
"required",
|
|
9
|
+
"missing",
|
|
10
|
+
"invalid",
|
|
11
|
+
"must be",
|
|
12
|
+
"must have",
|
|
13
|
+
"needs",
|
|
14
|
+
"requires",
|
|
15
|
+
];
|
|
16
|
+
function isRecoverableToolError(error) {
|
|
17
|
+
const errorLower = (error ?? "").toLowerCase();
|
|
18
|
+
return RECOVERABLE_TOOL_ERROR_KEYWORDS.some((keyword) => errorLower.includes(keyword));
|
|
19
|
+
}
|
|
20
|
+
function shouldShowToolErrorWarning(params) {
|
|
21
|
+
if (params.suppressToolErrorWarnings) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const isMutatingToolError = params.lastToolError.mutatingAction ?? isLikelyMutatingToolName(params.lastToolError.toolName);
|
|
25
|
+
if (isMutatingToolError) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
if (params.suppressToolErrors) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return !params.hasUserFacingReply && !isRecoverableToolError(params.lastToolError.error);
|
|
32
|
+
}
|
|
6
33
|
export function buildEmbeddedRunPayloads(params) {
|
|
7
34
|
const replyItems = [];
|
|
8
35
|
const useMarkdown = params.toolResultFormat === "markdown";
|
|
@@ -11,6 +38,7 @@ export function buildEmbeddedRunPayloads(params) {
|
|
|
11
38
|
? formatAssistantErrorText(params.lastAssistant, {
|
|
12
39
|
cfg: params.config,
|
|
13
40
|
sessionKey: params.sessionKey,
|
|
41
|
+
provider: params.provider,
|
|
14
42
|
})
|
|
15
43
|
: undefined;
|
|
16
44
|
const rawErrorMessage = lastAssistantErrored
|
|
@@ -29,9 +57,11 @@ export function buildEmbeddedRunPayloads(params) {
|
|
|
29
57
|
? normalizeTextForComparison(rawErrorMessage)
|
|
30
58
|
: null;
|
|
31
59
|
const normalizedErrorText = errorText ? normalizeTextForComparison(errorText) : null;
|
|
60
|
+
const normalizedGenericBillingErrorText = normalizeTextForComparison(BILLING_ERROR_USER_MESSAGE);
|
|
32
61
|
const genericErrorText = "The AI service returned an error. Please try again.";
|
|
33
|
-
if (errorText)
|
|
62
|
+
if (errorText) {
|
|
34
63
|
replyItems.push({ text: errorText, isError: true });
|
|
64
|
+
}
|
|
35
65
|
const inlineToolResults = params.inlineToolResultsAllowed && params.verboseLevel !== "off" && params.toolMetas.length > 0;
|
|
36
66
|
if (inlineToolResults) {
|
|
37
67
|
for (const { toolName, meta } of params.toolMetas) {
|
|
@@ -54,40 +84,55 @@ export function buildEmbeddedRunPayloads(params) {
|
|
|
54
84
|
const reasoningText = params.lastAssistant && params.reasoningLevel === "on"
|
|
55
85
|
? formatReasoningMessage(extractAssistantThinking(params.lastAssistant))
|
|
56
86
|
: "";
|
|
57
|
-
if (reasoningText)
|
|
87
|
+
if (reasoningText) {
|
|
58
88
|
replyItems.push({ text: reasoningText });
|
|
89
|
+
}
|
|
59
90
|
const fallbackAnswerText = params.lastAssistant ? extractAssistantText(params.lastAssistant) : "";
|
|
60
91
|
const shouldSuppressRawErrorText = (text) => {
|
|
61
|
-
if (!lastAssistantErrored)
|
|
92
|
+
if (!lastAssistantErrored) {
|
|
62
93
|
return false;
|
|
94
|
+
}
|
|
63
95
|
const trimmed = text.trim();
|
|
64
|
-
if (!trimmed)
|
|
96
|
+
if (!trimmed) {
|
|
65
97
|
return false;
|
|
98
|
+
}
|
|
66
99
|
if (errorText) {
|
|
67
100
|
const normalized = normalizeTextForComparison(trimmed);
|
|
68
|
-
if (normalized && normalizedErrorText && normalized === normalizedErrorText)
|
|
101
|
+
if (normalized && normalizedErrorText && normalized === normalizedErrorText) {
|
|
69
102
|
return true;
|
|
70
|
-
|
|
103
|
+
}
|
|
104
|
+
if (trimmed === genericErrorText) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
if (normalized &&
|
|
108
|
+
normalizedGenericBillingErrorText &&
|
|
109
|
+
normalized === normalizedGenericBillingErrorText) {
|
|
71
110
|
return true;
|
|
111
|
+
}
|
|
72
112
|
}
|
|
73
|
-
if (rawErrorMessage && trimmed === rawErrorMessage)
|
|
113
|
+
if (rawErrorMessage && trimmed === rawErrorMessage) {
|
|
74
114
|
return true;
|
|
75
|
-
|
|
115
|
+
}
|
|
116
|
+
if (formattedRawErrorMessage && trimmed === formattedRawErrorMessage) {
|
|
76
117
|
return true;
|
|
118
|
+
}
|
|
77
119
|
if (normalizedRawErrorText) {
|
|
78
120
|
const normalized = normalizeTextForComparison(trimmed);
|
|
79
|
-
if (normalized && normalized === normalizedRawErrorText)
|
|
121
|
+
if (normalized && normalized === normalizedRawErrorText) {
|
|
80
122
|
return true;
|
|
123
|
+
}
|
|
81
124
|
}
|
|
82
125
|
if (normalizedFormattedRawErrorMessage) {
|
|
83
126
|
const normalized = normalizeTextForComparison(trimmed);
|
|
84
|
-
if (normalized && normalized === normalizedFormattedRawErrorMessage)
|
|
127
|
+
if (normalized && normalized === normalizedFormattedRawErrorMessage) {
|
|
85
128
|
return true;
|
|
129
|
+
}
|
|
86
130
|
}
|
|
87
131
|
if (rawErrorFingerprint) {
|
|
88
132
|
const fingerprint = getApiErrorPayloadFingerprint(trimmed);
|
|
89
|
-
if (fingerprint && fingerprint === rawErrorFingerprint)
|
|
133
|
+
if (fingerprint && fingerprint === rawErrorFingerprint) {
|
|
90
134
|
return true;
|
|
135
|
+
}
|
|
91
136
|
}
|
|
92
137
|
return isRawApiErrorPayload(trimmed);
|
|
93
138
|
};
|
|
@@ -96,6 +141,7 @@ export function buildEmbeddedRunPayloads(params) {
|
|
|
96
141
|
: fallbackAnswerText
|
|
97
142
|
? [fallbackAnswerText]
|
|
98
143
|
: []).filter((text) => !shouldSuppressRawErrorText(text));
|
|
144
|
+
let hasUserFacingAssistantReply = false;
|
|
99
145
|
for (const text of answerTexts) {
|
|
100
146
|
const { text: cleanedText, mediaUrls, audioAsVoice, replyToId, replyToTag, replyToCurrent, } = parseReplyDirectives(text);
|
|
101
147
|
if (!cleanedText && (!mediaUrls || mediaUrls.length === 0) && !audioAsVoice) {
|
|
@@ -109,35 +155,37 @@ export function buildEmbeddedRunPayloads(params) {
|
|
|
109
155
|
replyToTag,
|
|
110
156
|
replyToCurrent,
|
|
111
157
|
});
|
|
158
|
+
hasUserFacingAssistantReply = true;
|
|
112
159
|
}
|
|
113
160
|
if (params.lastToolError) {
|
|
114
|
-
const
|
|
115
|
-
params.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
const isRecoverableError = errorLower.includes("required") ||
|
|
124
|
-
errorLower.includes("missing") ||
|
|
125
|
-
errorLower.includes("invalid") ||
|
|
126
|
-
errorLower.includes("must be") ||
|
|
127
|
-
errorLower.includes("must have") ||
|
|
128
|
-
errorLower.includes("needs") ||
|
|
129
|
-
errorLower.includes("requires");
|
|
130
|
-
// Show tool errors only when:
|
|
131
|
-
// 1. There's no user-facing reply AND the error is not recoverable
|
|
132
|
-
// Recoverable errors (validation, missing params) are already in the model's context
|
|
133
|
-
// and shouldn't be surfaced to users since the model should retry.
|
|
134
|
-
if (!hasUserFacingReply && !isRecoverableError) {
|
|
161
|
+
const shouldShowToolError = shouldShowToolErrorWarning({
|
|
162
|
+
lastToolError: params.lastToolError,
|
|
163
|
+
hasUserFacingReply: hasUserFacingAssistantReply,
|
|
164
|
+
suppressToolErrors: Boolean(params.config?.messages?.suppressToolErrors),
|
|
165
|
+
suppressToolErrorWarnings: params.suppressToolErrorWarnings,
|
|
166
|
+
});
|
|
167
|
+
// Always surface mutating tool failures so we do not silently confirm actions that did not happen.
|
|
168
|
+
// Otherwise, keep the previous behavior and only surface non-recoverable failures when no reply exists.
|
|
169
|
+
if (shouldShowToolError) {
|
|
135
170
|
const toolSummary = formatToolAggregate(params.lastToolError.toolName, params.lastToolError.meta ? [params.lastToolError.meta] : undefined, { markdown: useMarkdown });
|
|
136
171
|
const errorSuffix = params.lastToolError.error ? `: ${params.lastToolError.error}` : "";
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
172
|
+
const warningText = `⚠️ ${toolSummary} failed${errorSuffix}`;
|
|
173
|
+
const normalizedWarning = normalizeTextForComparison(warningText);
|
|
174
|
+
const duplicateWarning = normalizedWarning
|
|
175
|
+
? replyItems.some((item) => {
|
|
176
|
+
if (!item.text) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
const normalizedExisting = normalizeTextForComparison(item.text);
|
|
180
|
+
return normalizedExisting.length > 0 && normalizedExisting === normalizedWarning;
|
|
181
|
+
})
|
|
182
|
+
: false;
|
|
183
|
+
if (!duplicateWarning) {
|
|
184
|
+
replyItems.push({
|
|
185
|
+
text: warningText,
|
|
186
|
+
isError: true,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
141
189
|
}
|
|
142
190
|
}
|
|
143
191
|
const hasAudioAsVoiceTag = replyItems.some((item) => item.audioAsVoice);
|
|
@@ -153,10 +201,12 @@ export function buildEmbeddedRunPayloads(params) {
|
|
|
153
201
|
audioAsVoice: item.audioAsVoice || Boolean(hasAudioAsVoiceTag && item.media?.length),
|
|
154
202
|
}))
|
|
155
203
|
.filter((p) => {
|
|
156
|
-
if (!p.text && !p.mediaUrl && (!p.mediaUrls || p.mediaUrls.length === 0))
|
|
204
|
+
if (!p.text && !p.mediaUrl && (!p.mediaUrls || p.mediaUrls.length === 0)) {
|
|
157
205
|
return false;
|
|
158
|
-
|
|
206
|
+
}
|
|
207
|
+
if (p.text && isSilentReplyText(p.text, SILENT_REPLY_TOKEN)) {
|
|
159
208
|
return false;
|
|
209
|
+
}
|
|
160
210
|
return true;
|
|
161
211
|
});
|
|
162
212
|
}
|