@poolzin/pool-bot 2026.2.0 → 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/dist/agents/bash-tools.exec.js +76 -25
- package/dist/agents/cli-runner/helpers.js +9 -11
- package/dist/agents/identity.js +47 -7
- package/dist/agents/memory-search.js +25 -8
- package/dist/agents/model-selection.js +21 -0
- 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.js +1 -1
- package/dist/agents/pi-embedded-runner/compact.js +1 -0
- package/dist/agents/pi-embedded-runner/model.js +61 -2
- package/dist/agents/pi-embedded-runner/run/attempt.js +21 -11
- package/dist/agents/pi-embedded-runner/run.js +199 -46
- package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
- package/dist/agents/pi-embedded-subscribe.js +118 -29
- package/dist/agents/pi-tools.js +10 -5
- package/dist/agents/poolbot-tools.js +15 -10
- package/dist/agents/sandbox-paths.js +31 -0
- package/dist/agents/session-tool-result-guard.js +94 -15
- 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-install.js +60 -23
- package/dist/agents/subagent-announce.js +79 -34
- package/dist/agents/tool-policy.conformance.js +14 -0
- package/dist/agents/tool-policy.js +24 -0
- package/dist/agents/tools/cron-tool.js +166 -19
- package/dist/agents/tools/discord-actions-presence.js +78 -0
- 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/envelope.js +50 -72
- package/dist/auto-reply/reply/commands-compact.js +1 -0
- package/dist/auto-reply/reply/commands-context-report.js +1 -0
- 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/get-reply-run.js +2 -1
- package/dist/auto-reply/reply/inbound-context.js +5 -1
- package/dist/auto-reply/reply/model-selection.js +3 -3
- 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 +50 -7
- package/dist/browser/config.js +1 -10
- package/dist/browser/extension-relay.js +101 -40
- 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.js +13 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/reply-prefix.js +8 -1
- 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/gateway-cli/register.js +37 -19
- package/dist/cli/memory-cli.js +5 -5
- package/dist/cli/parse-bytes.js +37 -0
- package/dist/cli/update-cli.js +173 -52
- package/dist/commands/agent.js +1 -0
- package/dist/commands/doctor-config-flow.js +61 -5
- package/dist/commands/doctor-state-migrations.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.status-command.js +43 -23
- package/dist/commands/models/shared.js +15 -0
- 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 +6 -3
- package/dist/commands/onboard-skills.js +63 -38
- package/dist/commands/openai-model-default.js +41 -0
- package/dist/config/defaults.js +3 -2
- 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 +2 -2
- package/dist/config/sessions/store.js +291 -23
- 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/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/net.js +96 -31
- package/dist/gateway/node-command-policy.js +50 -15
- 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-http.js +109 -11
- 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 +5 -1
- 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/imessage/constants.js +2 -0
- package/dist/imessage/monitor/deliver.js +4 -1
- package/dist/imessage/monitor/monitor-provider.js +51 -1
- package/dist/infra/bonjour-discovery.js +131 -70
- 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/poolbot-root.js +43 -1
- package/dist/infra/session-cost-usage.js +631 -41
- package/dist/infra/state-migrations.js +317 -47
- 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/media/fetch.js +81 -58
- 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 +2 -2
- 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/batch-voyage.js +277 -0
- package/dist/memory/embeddings-voyage.js +75 -0
- package/dist/memory/embeddings.js +28 -16
- package/dist/memory/internal.js +101 -18
- package/dist/memory/manager.js +154 -48
- package/dist/memory/search-manager.js +173 -0
- package/dist/memory/session-files.js +9 -3
- package/dist/node-host/runner.js +34 -24
- package/dist/node-host/with-timeout.js +27 -0
- package/dist/plugins/commands.js +5 -1
- package/dist/plugins/config-state.js +86 -7
- package/dist/plugins/source-display.js +51 -0
- 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/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/signal/monitor/event-handler.js +80 -1
- package/dist/slack/monitor/media.js +85 -15
- package/dist/tailscale/detect.js +1 -2
- package/dist/telegram/bot/helpers.js +109 -28
- package/dist/telegram/bot-handlers.js +144 -3
- package/dist/telegram/bot-message-context.js +37 -10
- 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/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/transcript-tools.js +58 -0
- package/dist/utils.js +45 -14
- package/dist/version.js +42 -5
- package/package.json +1 -1
package/dist/infra/net/ssrf.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { lookup as dnsLookup } from "node:dns/promises";
|
|
2
1
|
import { lookup as dnsLookupCb } from "node:dns";
|
|
2
|
+
import { lookup as dnsLookup } from "node:dns/promises";
|
|
3
3
|
import { Agent } from "undici";
|
|
4
4
|
export class SsrFBlockedError extends Error {
|
|
5
5
|
constructor(message) {
|
|
@@ -16,13 +16,21 @@ function normalizeHostname(hostname) {
|
|
|
16
16
|
}
|
|
17
17
|
return normalized;
|
|
18
18
|
}
|
|
19
|
+
function normalizeHostnameSet(values) {
|
|
20
|
+
if (!values || values.length === 0) {
|
|
21
|
+
return new Set();
|
|
22
|
+
}
|
|
23
|
+
return new Set(values.map((value) => normalizeHostname(value)).filter(Boolean));
|
|
24
|
+
}
|
|
19
25
|
function parseIpv4(address) {
|
|
20
26
|
const parts = address.split(".");
|
|
21
|
-
if (parts.length !== 4)
|
|
27
|
+
if (parts.length !== 4) {
|
|
22
28
|
return null;
|
|
29
|
+
}
|
|
23
30
|
const numbers = parts.map((part) => Number.parseInt(part, 10));
|
|
24
|
-
if (numbers.some((value) => Number.isNaN(value) || value < 0 || value > 255))
|
|
31
|
+
if (numbers.some((value) => Number.isNaN(value) || value < 0 || value > 255)) {
|
|
25
32
|
return null;
|
|
33
|
+
}
|
|
26
34
|
return numbers;
|
|
27
35
|
}
|
|
28
36
|
function parseIpv4FromMappedIpv6(mapped) {
|
|
@@ -32,12 +40,14 @@ function parseIpv4FromMappedIpv6(mapped) {
|
|
|
32
40
|
const parts = mapped.split(":").filter(Boolean);
|
|
33
41
|
if (parts.length === 1) {
|
|
34
42
|
const value = Number.parseInt(parts[0], 16);
|
|
35
|
-
if (Number.isNaN(value) || value < 0 || value > 0xffff_ffff)
|
|
43
|
+
if (Number.isNaN(value) || value < 0 || value > 0xffff_ffff) {
|
|
36
44
|
return null;
|
|
45
|
+
}
|
|
37
46
|
return [(value >>> 24) & 0xff, (value >>> 16) & 0xff, (value >>> 8) & 0xff, value & 0xff];
|
|
38
47
|
}
|
|
39
|
-
if (parts.length !== 2)
|
|
48
|
+
if (parts.length !== 2) {
|
|
40
49
|
return null;
|
|
50
|
+
}
|
|
41
51
|
const high = Number.parseInt(parts[0], 16);
|
|
42
52
|
const low = Number.parseInt(parts[1], 16);
|
|
43
53
|
if (Number.isNaN(high) ||
|
|
@@ -53,20 +63,27 @@ function parseIpv4FromMappedIpv6(mapped) {
|
|
|
53
63
|
}
|
|
54
64
|
function isPrivateIpv4(parts) {
|
|
55
65
|
const [octet1, octet2] = parts;
|
|
56
|
-
if (octet1 === 0)
|
|
66
|
+
if (octet1 === 0) {
|
|
57
67
|
return true;
|
|
58
|
-
|
|
68
|
+
}
|
|
69
|
+
if (octet1 === 10) {
|
|
59
70
|
return true;
|
|
60
|
-
|
|
71
|
+
}
|
|
72
|
+
if (octet1 === 127) {
|
|
61
73
|
return true;
|
|
62
|
-
|
|
74
|
+
}
|
|
75
|
+
if (octet1 === 169 && octet2 === 254) {
|
|
63
76
|
return true;
|
|
64
|
-
|
|
77
|
+
}
|
|
78
|
+
if (octet1 === 172 && octet2 >= 16 && octet2 <= 31) {
|
|
65
79
|
return true;
|
|
66
|
-
|
|
80
|
+
}
|
|
81
|
+
if (octet1 === 192 && octet2 === 168) {
|
|
67
82
|
return true;
|
|
68
|
-
|
|
83
|
+
}
|
|
84
|
+
if (octet1 === 100 && octet2 >= 64 && octet2 <= 127) {
|
|
69
85
|
return true;
|
|
86
|
+
}
|
|
70
87
|
return false;
|
|
71
88
|
}
|
|
72
89
|
export function isPrivateIpAddress(address) {
|
|
@@ -74,30 +91,36 @@ export function isPrivateIpAddress(address) {
|
|
|
74
91
|
if (normalized.startsWith("[") && normalized.endsWith("]")) {
|
|
75
92
|
normalized = normalized.slice(1, -1);
|
|
76
93
|
}
|
|
77
|
-
if (!normalized)
|
|
94
|
+
if (!normalized) {
|
|
78
95
|
return false;
|
|
96
|
+
}
|
|
79
97
|
if (normalized.startsWith("::ffff:")) {
|
|
80
98
|
const mapped = normalized.slice("::ffff:".length);
|
|
81
99
|
const ipv4 = parseIpv4FromMappedIpv6(mapped);
|
|
82
|
-
if (ipv4)
|
|
100
|
+
if (ipv4) {
|
|
83
101
|
return isPrivateIpv4(ipv4);
|
|
102
|
+
}
|
|
84
103
|
}
|
|
85
104
|
if (normalized.includes(":")) {
|
|
86
|
-
if (normalized === "::" || normalized === "::1")
|
|
105
|
+
if (normalized === "::" || normalized === "::1") {
|
|
87
106
|
return true;
|
|
107
|
+
}
|
|
88
108
|
return PRIVATE_IPV6_PREFIXES.some((prefix) => normalized.startsWith(prefix));
|
|
89
109
|
}
|
|
90
110
|
const ipv4 = parseIpv4(normalized);
|
|
91
|
-
if (!ipv4)
|
|
111
|
+
if (!ipv4) {
|
|
92
112
|
return false;
|
|
113
|
+
}
|
|
93
114
|
return isPrivateIpv4(ipv4);
|
|
94
115
|
}
|
|
95
116
|
export function isBlockedHostname(hostname) {
|
|
96
117
|
const normalized = normalizeHostname(hostname);
|
|
97
|
-
if (!normalized)
|
|
118
|
+
if (!normalized) {
|
|
98
119
|
return false;
|
|
99
|
-
|
|
120
|
+
}
|
|
121
|
+
if (BLOCKED_HOSTNAMES.has(normalized)) {
|
|
100
122
|
return true;
|
|
123
|
+
}
|
|
101
124
|
return (normalized.endsWith(".localhost") ||
|
|
102
125
|
normalized.endsWith(".local") ||
|
|
103
126
|
normalized.endsWith(".internal"));
|
|
@@ -114,8 +137,9 @@ export function createPinnedLookup(params) {
|
|
|
114
137
|
let index = 0;
|
|
115
138
|
return ((host, options, callback) => {
|
|
116
139
|
const cb = typeof options === "function" ? options : callback;
|
|
117
|
-
if (!cb)
|
|
140
|
+
if (!cb) {
|
|
118
141
|
return;
|
|
142
|
+
}
|
|
119
143
|
const normalized = normalizeHostname(host);
|
|
120
144
|
if (!normalized || normalized !== normalizedHost) {
|
|
121
145
|
if (typeof options === "function" || options === undefined) {
|
|
@@ -140,24 +164,32 @@ export function createPinnedLookup(params) {
|
|
|
140
164
|
cb(null, chosen.address, chosen.family);
|
|
141
165
|
});
|
|
142
166
|
}
|
|
143
|
-
export async function
|
|
167
|
+
export async function resolvePinnedHostnameWithPolicy(hostname, params = {}) {
|
|
144
168
|
const normalized = normalizeHostname(hostname);
|
|
145
169
|
if (!normalized) {
|
|
146
170
|
throw new Error("Invalid hostname");
|
|
147
171
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (
|
|
152
|
-
|
|
172
|
+
const allowPrivateNetwork = Boolean(params.policy?.allowPrivateNetwork);
|
|
173
|
+
const allowedHostnames = normalizeHostnameSet(params.policy?.allowedHostnames);
|
|
174
|
+
const isExplicitAllowed = allowedHostnames.has(normalized);
|
|
175
|
+
if (!allowPrivateNetwork && !isExplicitAllowed) {
|
|
176
|
+
if (isBlockedHostname(normalized)) {
|
|
177
|
+
throw new SsrFBlockedError(`Blocked hostname: ${hostname}`);
|
|
178
|
+
}
|
|
179
|
+
if (isPrivateIpAddress(normalized)) {
|
|
180
|
+
throw new SsrFBlockedError("Blocked: private/internal IP address");
|
|
181
|
+
}
|
|
153
182
|
}
|
|
183
|
+
const lookupFn = params.lookupFn ?? dnsLookup;
|
|
154
184
|
const results = await lookupFn(normalized, { all: true });
|
|
155
185
|
if (results.length === 0) {
|
|
156
186
|
throw new Error(`Unable to resolve hostname: ${hostname}`);
|
|
157
187
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
188
|
+
if (!allowPrivateNetwork && !isExplicitAllowed) {
|
|
189
|
+
for (const entry of results) {
|
|
190
|
+
if (isPrivateIpAddress(entry.address)) {
|
|
191
|
+
throw new SsrFBlockedError("Blocked: resolves to private/internal IP address");
|
|
192
|
+
}
|
|
161
193
|
}
|
|
162
194
|
}
|
|
163
195
|
const addresses = Array.from(new Set(results.map((entry) => entry.address)));
|
|
@@ -170,6 +202,9 @@ export async function resolvePinnedHostname(hostname, lookupFn = dnsLookup) {
|
|
|
170
202
|
lookup: createPinnedLookup({ hostname: normalized, addresses }),
|
|
171
203
|
};
|
|
172
204
|
}
|
|
205
|
+
export async function resolvePinnedHostname(hostname, lookupFn = dnsLookup) {
|
|
206
|
+
return await resolvePinnedHostnameWithPolicy(hostname, { lookupFn });
|
|
207
|
+
}
|
|
173
208
|
export function createPinnedDispatcher(pinned) {
|
|
174
209
|
return new Agent({
|
|
175
210
|
connect: {
|
|
@@ -178,8 +213,9 @@ export function createPinnedDispatcher(pinned) {
|
|
|
178
213
|
});
|
|
179
214
|
}
|
|
180
215
|
export async function closeDispatcher(dispatcher) {
|
|
181
|
-
if (!dispatcher)
|
|
216
|
+
if (!dispatcher) {
|
|
182
217
|
return;
|
|
218
|
+
}
|
|
183
219
|
const candidate = dispatcher;
|
|
184
220
|
try {
|
|
185
221
|
if (typeof candidate.close === "function") {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility for checking AbortSignal state and throwing a standard AbortError.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Throws an AbortError if the given signal has been aborted.
|
|
6
|
+
* Use at async checkpoints to support cancellation.
|
|
7
|
+
*/
|
|
8
|
+
export function throwIfAborted(abortSignal) {
|
|
9
|
+
if (abortSignal?.aborted) {
|
|
10
|
+
const err = new Error("Operation aborted");
|
|
11
|
+
err.name = "AbortError";
|
|
12
|
+
throw err;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -15,6 +15,9 @@ import { resolveChannelTarget } from "./target-resolver.js";
|
|
|
15
15
|
import { loadWebMedia } from "../../web/media.js";
|
|
16
16
|
import { extensionForMime } from "../../media/mime.js";
|
|
17
17
|
import { parseSlackTarget } from "../../slack/targets.js";
|
|
18
|
+
import { parseTelegramTarget } from "../../telegram/targets.js";
|
|
19
|
+
import { assertMediaNotDataUrl, resolveSandboxedMediaSource } from "../../agents/sandbox-paths.js";
|
|
20
|
+
import { throwIfAborted } from "./abort.js";
|
|
18
21
|
export function getToolResult(result) {
|
|
19
22
|
return "toolResult" in result ? result.toolResult : undefined;
|
|
20
23
|
}
|
|
@@ -99,13 +102,23 @@ function resolveSlackAutoThreadId(params) {
|
|
|
99
102
|
return undefined;
|
|
100
103
|
return context.currentThreadTs;
|
|
101
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Auto-inject Telegram forum topic thread ID when the message tool targets
|
|
107
|
+
* the same chat the session originated from.
|
|
108
|
+
*/
|
|
109
|
+
function resolveTelegramAutoThreadId(params) {
|
|
110
|
+
const context = params.toolContext;
|
|
111
|
+
if (!context?.currentThreadTs || !context.currentChannelId)
|
|
112
|
+
return undefined;
|
|
113
|
+
const parsedTo = parseTelegramTarget(params.to);
|
|
114
|
+
const parsedChannel = parseTelegramTarget(context.currentChannelId);
|
|
115
|
+
if (parsedTo.chatId.toLowerCase() !== parsedChannel.chatId.toLowerCase())
|
|
116
|
+
return undefined;
|
|
117
|
+
return context.currentThreadTs;
|
|
118
|
+
}
|
|
102
119
|
function resolveAttachmentMaxBytes(params) {
|
|
103
|
-
const fallback = params.cfg.agents?.defaults?.mediaMaxMb;
|
|
104
|
-
if (params.channel !== "bluebubbles") {
|
|
105
|
-
return typeof fallback === "number" ? fallback * 1024 * 1024 : undefined;
|
|
106
|
-
}
|
|
107
120
|
const accountId = typeof params.accountId === "string" ? params.accountId.trim() : "";
|
|
108
|
-
const channelCfg = params.cfg.channels?.
|
|
121
|
+
const channelCfg = params.cfg.channels?.[params.channel];
|
|
109
122
|
const channelObj = channelCfg && typeof channelCfg === "object"
|
|
110
123
|
? channelCfg
|
|
111
124
|
: undefined;
|
|
@@ -163,6 +176,41 @@ function normalizeBase64Payload(params) {
|
|
|
163
176
|
contentType: params.contentType ?? mime,
|
|
164
177
|
};
|
|
165
178
|
}
|
|
179
|
+
async function normalizeSandboxMediaParams(params) {
|
|
180
|
+
const sandboxRoot = params.sandboxRoot?.trim();
|
|
181
|
+
const mediaKeys = ["media", "path", "filePath"];
|
|
182
|
+
for (const key of mediaKeys) {
|
|
183
|
+
const raw = readStringParam(params.args, key, { trim: false });
|
|
184
|
+
if (!raw)
|
|
185
|
+
continue;
|
|
186
|
+
assertMediaNotDataUrl(raw);
|
|
187
|
+
if (!sandboxRoot)
|
|
188
|
+
continue;
|
|
189
|
+
const normalized = await resolveSandboxedMediaSource({ media: raw, sandboxRoot });
|
|
190
|
+
if (normalized !== raw) {
|
|
191
|
+
params.args[key] = normalized;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function normalizeSandboxMediaList(params) {
|
|
196
|
+
const sandboxRoot = params.sandboxRoot?.trim();
|
|
197
|
+
const normalized = [];
|
|
198
|
+
const seen = new Set();
|
|
199
|
+
for (const value of params.values) {
|
|
200
|
+
const raw = value?.trim();
|
|
201
|
+
if (!raw)
|
|
202
|
+
continue;
|
|
203
|
+
assertMediaNotDataUrl(raw);
|
|
204
|
+
const resolved = sandboxRoot
|
|
205
|
+
? await resolveSandboxedMediaSource({ media: raw, sandboxRoot })
|
|
206
|
+
: raw;
|
|
207
|
+
if (seen.has(resolved))
|
|
208
|
+
continue;
|
|
209
|
+
seen.add(resolved);
|
|
210
|
+
normalized.push(resolved);
|
|
211
|
+
}
|
|
212
|
+
return normalized;
|
|
213
|
+
}
|
|
166
214
|
async function hydrateSetGroupIconParams(params) {
|
|
167
215
|
if (params.action !== "setGroupIcon")
|
|
168
216
|
return;
|
|
@@ -417,13 +465,6 @@ async function handleBroadcastAction(input, params) {
|
|
|
417
465
|
dryRun: Boolean(input.dryRun),
|
|
418
466
|
};
|
|
419
467
|
}
|
|
420
|
-
function throwIfAborted(abortSignal) {
|
|
421
|
-
if (abortSignal?.aborted) {
|
|
422
|
-
const err = new Error("Message send aborted");
|
|
423
|
-
err.name = "AbortError";
|
|
424
|
-
throw err;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
468
|
async function handleSendAction(ctx) {
|
|
428
469
|
const { cfg, params, channel, accountId, dryRun, gateway, input, agentId, resolvedTarget, abortSignal, } = ctx;
|
|
429
470
|
throwIfAborted(abortSignal);
|
|
@@ -438,6 +479,10 @@ async function handleSendAction(ctx) {
|
|
|
438
479
|
required: !mediaHint && !hasCard,
|
|
439
480
|
allowEmpty: true,
|
|
440
481
|
}) ?? "";
|
|
482
|
+
// Unescape literal \\n sequences that models sometimes emit in tool arguments.
|
|
483
|
+
if (message.includes("\\n")) {
|
|
484
|
+
message = message.replaceAll("\\n", "\n");
|
|
485
|
+
}
|
|
441
486
|
const parsed = parseReplyDirectives(message);
|
|
442
487
|
const mergedMediaUrls = [];
|
|
443
488
|
const seenMedia = new Set();
|
|
@@ -454,6 +499,13 @@ async function handleSendAction(ctx) {
|
|
|
454
499
|
for (const url of parsed.mediaUrls ?? [])
|
|
455
500
|
pushMedia(url);
|
|
456
501
|
pushMedia(parsed.mediaUrl);
|
|
502
|
+
// Sandbox-validate and deduplicate media URLs.
|
|
503
|
+
const normalizedMediaUrls = await normalizeSandboxMediaList({
|
|
504
|
+
values: mergedMediaUrls,
|
|
505
|
+
sandboxRoot: input.sandboxRoot,
|
|
506
|
+
});
|
|
507
|
+
mergedMediaUrls.length = 0;
|
|
508
|
+
mergedMediaUrls.push(...normalizedMediaUrls);
|
|
457
509
|
message = parsed.text;
|
|
458
510
|
params.message = message;
|
|
459
511
|
if (!params.replyTo && parsed.replyToId)
|
|
@@ -482,6 +534,14 @@ async function handleSendAction(ctx) {
|
|
|
482
534
|
const slackAutoThreadId = channel === "slack" && !replyToId && !threadId
|
|
483
535
|
? resolveSlackAutoThreadId({ to, toolContext: input.toolContext })
|
|
484
536
|
: undefined;
|
|
537
|
+
// Telegram forum topic auto-threading
|
|
538
|
+
const telegramAutoThreadId = channel === "telegram" && !threadId
|
|
539
|
+
? resolveTelegramAutoThreadId({ to, toolContext: input.toolContext })
|
|
540
|
+
: undefined;
|
|
541
|
+
const resolvedThreadId = threadId ?? slackAutoThreadId ?? telegramAutoThreadId;
|
|
542
|
+
if (resolvedThreadId && !params.threadId) {
|
|
543
|
+
params.threadId = resolvedThreadId;
|
|
544
|
+
}
|
|
485
545
|
const outboundRoute = agentId && !dryRun
|
|
486
546
|
? await resolveOutboundSessionRoute({
|
|
487
547
|
cfg,
|
|
@@ -491,7 +551,7 @@ async function handleSendAction(ctx) {
|
|
|
491
551
|
target: to,
|
|
492
552
|
resolvedTarget,
|
|
493
553
|
replyToId,
|
|
494
|
-
threadId:
|
|
554
|
+
threadId: resolvedThreadId,
|
|
495
555
|
})
|
|
496
556
|
: null;
|
|
497
557
|
if (outboundRoute && agentId && !dryRun) {
|
|
@@ -696,6 +756,10 @@ export async function runMessageAction(input) {
|
|
|
696
756
|
params.accountId = accountId;
|
|
697
757
|
}
|
|
698
758
|
const dryRun = Boolean(input.dryRun ?? readBooleanParam(params, "dryRun"));
|
|
759
|
+
await normalizeSandboxMediaParams({
|
|
760
|
+
args: params,
|
|
761
|
+
sandboxRoot: input.sandboxRoot,
|
|
762
|
+
});
|
|
699
763
|
await hydrateSendAttachmentParams({
|
|
700
764
|
cfg,
|
|
701
765
|
channel,
|