@poolzin/pool-bot 2026.2.0 → 2026.2.2
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 +118 -0
- package/README-header.png +0 -0
- package/dist/agents/bash-tools.exec.js +76 -25
- package/dist/agents/cli-runner/helpers.js +9 -11
- package/dist/agents/context.js +1 -1
- package/dist/agents/identity.js +47 -7
- package/dist/agents/memory-search.js +25 -8
- package/dist/agents/model-catalog.js +1 -1
- 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 +8 -10
- package/dist/agents/pi-embedded-runner/model.js +62 -3
- 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/image-tool.js +1 -1
- 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 +74 -82
- 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/mentions.js +1 -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/canvas-host/a2ui/index.html +28 -28
- 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/auth-choice.apply.oauth.js +1 -1
- 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.registry.js +1 -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/compat/legacy-names.js +2 -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/control-ui/assets/{index-CIRDm-Lu.css → index-CSfXd2LO.css} +1 -1
- package/dist/control-ui/assets/{index-CmNMuoem.js → index-HRr1grwl.js} +446 -413
- package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -0
- package/dist/control-ui/index.html +4 -4
- 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 +172 -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.mocks.js +11 -7
- 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/store.js +2 -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 +3 -3
- 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 +54 -17
- 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/dist/wizard/clack-prompter.js +9 -6
- package/extensions/googlechat/node_modules/.bin/poolbot +21 -0
- package/extensions/googlechat/package.json +2 -2
- package/extensions/line/node_modules/.bin/poolbot +21 -0
- package/extensions/line/package.json +1 -1
- package/extensions/matrix/node_modules/.bin/poolbot +21 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/memory-core/node_modules/.bin/poolbot +21 -0
- package/extensions/memory-core/package.json +4 -1
- package/extensions/twitch/node_modules/.bin/poolbot +21 -0
- package/extensions/twitch/package.json +1 -1
- package/package.json +183 -24
- package/dist/control-ui/assets/index-CmNMuoem.js.map +0 -1
|
@@ -1,22 +1,67 @@
|
|
|
1
1
|
import { formatLocationText } from "../../channels/location.js";
|
|
2
2
|
const TELEGRAM_GENERAL_TOPIC_ID = 1;
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the thread ID for Telegram forum topics.
|
|
5
|
+
* For non-forum groups, returns undefined even if messageThreadId is present
|
|
6
|
+
* (reply threads in regular groups should not create separate sessions).
|
|
7
|
+
* For forum groups, returns the topic ID (or General topic ID=1 if unspecified).
|
|
8
|
+
*/
|
|
3
9
|
export function resolveTelegramForumThreadId(params) {
|
|
4
|
-
|
|
10
|
+
// Non-forum groups: ignore message_thread_id (reply threads are not real topics)
|
|
11
|
+
if (!params.isForum) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
// Forum groups: use the topic ID, defaulting to General topic
|
|
15
|
+
if (params.messageThreadId == null) {
|
|
5
16
|
return TELEGRAM_GENERAL_TOPIC_ID;
|
|
6
17
|
}
|
|
7
|
-
return params.messageThreadId
|
|
18
|
+
return params.messageThreadId;
|
|
19
|
+
}
|
|
20
|
+
export function resolveTelegramThreadSpec(params) {
|
|
21
|
+
if (params.isGroup) {
|
|
22
|
+
const id = resolveTelegramForumThreadId({
|
|
23
|
+
isForum: params.isForum,
|
|
24
|
+
messageThreadId: params.messageThreadId,
|
|
25
|
+
});
|
|
26
|
+
return {
|
|
27
|
+
id,
|
|
28
|
+
scope: params.isForum ? "forum" : "none",
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (params.messageThreadId == null) {
|
|
32
|
+
return { scope: "dm" };
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
id: params.messageThreadId,
|
|
36
|
+
scope: "dm",
|
|
37
|
+
};
|
|
8
38
|
}
|
|
9
39
|
/**
|
|
10
40
|
* Build thread params for Telegram API calls (messages, media).
|
|
11
41
|
* General forum topic (id=1) must be treated like a regular supergroup send:
|
|
12
42
|
* Telegram rejects sendMessage/sendMedia with message_thread_id=1 ("thread not found").
|
|
43
|
+
*
|
|
44
|
+
* Accepts either a TelegramThreadSpec (preferred) or a raw messageThreadId number
|
|
45
|
+
* for backward compatibility with callers that haven't migrated yet.
|
|
13
46
|
*/
|
|
14
|
-
export function buildTelegramThreadParams(
|
|
15
|
-
if (
|
|
47
|
+
export function buildTelegramThreadParams(threadOrId) {
|
|
48
|
+
if (threadOrId == null) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
// Legacy call: raw number
|
|
52
|
+
if (typeof threadOrId === "number") {
|
|
53
|
+
const normalized = Math.trunc(threadOrId);
|
|
54
|
+
if (normalized === TELEGRAM_GENERAL_TOPIC_ID) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
return { message_thread_id: normalized };
|
|
58
|
+
}
|
|
59
|
+
// New call: TelegramThreadSpec
|
|
60
|
+
if (!threadOrId.id) {
|
|
16
61
|
return undefined;
|
|
17
62
|
}
|
|
18
|
-
const normalized = Math.trunc(
|
|
19
|
-
if (normalized === TELEGRAM_GENERAL_TOPIC_ID) {
|
|
63
|
+
const normalized = Math.trunc(threadOrId.id);
|
|
64
|
+
if (normalized === TELEGRAM_GENERAL_TOPIC_ID && threadOrId.scope === "forum") {
|
|
20
65
|
return undefined;
|
|
21
66
|
}
|
|
22
67
|
return { message_thread_id: normalized };
|
|
@@ -43,6 +88,19 @@ export function buildTelegramGroupPeerId(chatId, messageThreadId) {
|
|
|
43
88
|
export function buildTelegramGroupFrom(chatId, messageThreadId) {
|
|
44
89
|
return `telegram:group:${buildTelegramGroupPeerId(chatId, messageThreadId)}`;
|
|
45
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Build parentPeer for forum topic binding inheritance.
|
|
93
|
+
* When a message comes from a forum topic, the peer ID includes the topic suffix
|
|
94
|
+
* (e.g., `-1001234567890:topic:99`). To allow bindings configured for the base
|
|
95
|
+
* group ID to match, we provide the parent group as `parentPeer` so the routing
|
|
96
|
+
* layer can fall back to it when the exact peer doesn't match.
|
|
97
|
+
*/
|
|
98
|
+
export function buildTelegramParentPeer(params) {
|
|
99
|
+
if (!params.isGroup || params.resolvedThreadId == null) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
return { kind: "group", id: String(params.chatId) };
|
|
103
|
+
}
|
|
46
104
|
export function buildSenderName(msg) {
|
|
47
105
|
const name = [msg.from?.first_name, msg.from?.last_name].filter(Boolean).join(" ").trim() ||
|
|
48
106
|
msg.from?.username;
|
|
@@ -93,7 +151,7 @@ export function expandTextLinks(text, entities) {
|
|
|
93
151
|
return text;
|
|
94
152
|
const textLinks = entities
|
|
95
153
|
.filter((entity) => entity.type === "text_link" && Boolean(entity.url))
|
|
96
|
-
.
|
|
154
|
+
.toSorted((a, b) => b.offset - a.offset);
|
|
97
155
|
if (textLinks.length === 0)
|
|
98
156
|
return text;
|
|
99
157
|
let result = text;
|
|
@@ -115,33 +173,53 @@ export function resolveTelegramReplyId(raw) {
|
|
|
115
173
|
}
|
|
116
174
|
export function describeReplyTarget(msg) {
|
|
117
175
|
const reply = msg.reply_to_message;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
body = "<media:audio>";
|
|
129
|
-
else if (reply.document)
|
|
130
|
-
body = "<media:document>";
|
|
131
|
-
else {
|
|
132
|
-
const locationData = extractTelegramLocation(reply);
|
|
133
|
-
if (locationData)
|
|
134
|
-
body = formatLocationText(locationData);
|
|
176
|
+
const externalReply = msg
|
|
177
|
+
.external_reply;
|
|
178
|
+
const quoteText = msg.quote?.text ??
|
|
179
|
+
externalReply?.quote?.text;
|
|
180
|
+
let body = "";
|
|
181
|
+
let kind = "reply";
|
|
182
|
+
if (typeof quoteText === "string") {
|
|
183
|
+
body = quoteText.trim();
|
|
184
|
+
if (body) {
|
|
185
|
+
kind = "quote";
|
|
135
186
|
}
|
|
136
187
|
}
|
|
137
|
-
|
|
188
|
+
const replyLike = reply ?? externalReply;
|
|
189
|
+
if (!body && replyLike) {
|
|
190
|
+
const replyBody = (replyLike.text ?? replyLike.caption ?? "").trim();
|
|
191
|
+
body = replyBody;
|
|
192
|
+
if (!body) {
|
|
193
|
+
if (replyLike.photo) {
|
|
194
|
+
body = "<media:image>";
|
|
195
|
+
}
|
|
196
|
+
else if (replyLike.video) {
|
|
197
|
+
body = "<media:video>";
|
|
198
|
+
}
|
|
199
|
+
else if (replyLike.audio || replyLike.voice) {
|
|
200
|
+
body = "<media:audio>";
|
|
201
|
+
}
|
|
202
|
+
else if (replyLike.document) {
|
|
203
|
+
body = "<media:document>";
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
const locationData = extractTelegramLocation(replyLike);
|
|
207
|
+
if (locationData) {
|
|
208
|
+
body = formatLocationText(locationData);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (!body) {
|
|
138
214
|
return null;
|
|
139
|
-
|
|
140
|
-
const
|
|
215
|
+
}
|
|
216
|
+
const sender = replyLike ? buildSenderName(replyLike) : undefined;
|
|
217
|
+
const senderLabel = sender ?? "unknown sender";
|
|
141
218
|
return {
|
|
142
|
-
id:
|
|
219
|
+
id: replyLike?.message_id ? String(replyLike.message_id) : undefined,
|
|
143
220
|
sender: senderLabel,
|
|
144
221
|
body,
|
|
222
|
+
kind,
|
|
145
223
|
};
|
|
146
224
|
}
|
|
147
225
|
function normalizeForwardedUserLabel(user) {
|
|
@@ -191,6 +269,7 @@ function buildForwardedContextFromChat(params) {
|
|
|
191
269
|
return null;
|
|
192
270
|
const signature = params.signature?.trim() || undefined;
|
|
193
271
|
const from = signature ? `${display} (${signature})` : display;
|
|
272
|
+
const chatType = (params.chat.type?.trim() || undefined);
|
|
194
273
|
return {
|
|
195
274
|
from,
|
|
196
275
|
date: params.date,
|
|
@@ -199,6 +278,8 @@ function buildForwardedContextFromChat(params) {
|
|
|
199
278
|
fromUsername: username,
|
|
200
279
|
fromTitle: title,
|
|
201
280
|
fromSignature: signature,
|
|
281
|
+
fromChatType: chatType,
|
|
282
|
+
fromMessageId: params.messageId,
|
|
202
283
|
};
|
|
203
284
|
}
|
|
204
285
|
function resolveForwardOrigin(origin, signature) {
|
|
@@ -2,21 +2,27 @@
|
|
|
2
2
|
import { hasControlCommand } from "../auto-reply/command-detection.js";
|
|
3
3
|
import { createInboundDebouncer, resolveInboundDebounceMs, } from "../auto-reply/inbound-debounce.js";
|
|
4
4
|
import { buildCommandsPaginationKeyboard } from "../auto-reply/reply/commands-info.js";
|
|
5
|
+
import { buildModelsProviderData } from "../auto-reply/reply/commands-models.js";
|
|
6
|
+
import { resolveStoredModelOverride } from "../auto-reply/reply/model-selection.js";
|
|
5
7
|
import { buildCommandsMessagePaginated } from "../auto-reply/status.js";
|
|
6
8
|
import { listSkillCommandsForAgents } from "../auto-reply/skill-commands.js";
|
|
7
9
|
import { resolveDefaultAgentId } from "../agents/agent-scope.js";
|
|
8
10
|
import { loadConfig } from "../config/config.js";
|
|
11
|
+
import { loadSessionStore, resolveStorePath } from "../config/sessions.js";
|
|
12
|
+
import { resolveAgentRoute } from "../routing/resolve-route.js";
|
|
13
|
+
import { resolveThreadSessionKeys } from "../routing/session-key.js";
|
|
9
14
|
import { writeConfigFile } from "../config/io.js";
|
|
10
15
|
import { danger, logVerbose, warn } from "../globals.js";
|
|
11
16
|
import { resolveMedia } from "./bot/delivery.js";
|
|
12
17
|
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
|
13
|
-
import { resolveTelegramForumThreadId } from "./bot/helpers.js";
|
|
18
|
+
import { buildTelegramGroupPeerId, buildTelegramParentPeer, resolveTelegramForumThreadId, } from "./bot/helpers.js";
|
|
14
19
|
import { firstDefined, isSenderAllowed, normalizeAllowFromWithStore } from "./bot-access.js";
|
|
15
20
|
import { MEDIA_GROUP_TIMEOUT_MS } from "./bot-updates.js";
|
|
16
21
|
import { migrateTelegramGroupConfig } from "./group-migration.js";
|
|
17
22
|
import { resolveTelegramInlineButtonsScope } from "./inline-buttons.js";
|
|
18
23
|
import { readTelegramAllowFromStore } from "./pairing-store.js";
|
|
19
24
|
import { resolveChannelConfigWrites } from "../channels/plugins/config-writes.js";
|
|
25
|
+
import { buildModelsKeyboard, buildProviderKeyboard, calculateTotalPages, getModelsPageSize, parseModelCallbackData, } from "./model-buttons.js";
|
|
20
26
|
import { buildInlineKeyboard } from "./send.js";
|
|
21
27
|
export const registerTelegramHandlers = ({ cfg, accountId, bot, opts, runtime, mediaMaxBytes, telegramCfg, groupAllowFrom, resolveGroupPolicy, resolveTelegramGroupConfig, shouldSkipUpdate, processMessage, logger, }) => {
|
|
22
28
|
const TELEGRAM_TEXT_FRAGMENT_START_THRESHOLD_CHARS = 4000;
|
|
@@ -72,6 +78,57 @@ export const registerTelegramHandlers = ({ cfg, accountId, bot, opts, runtime, m
|
|
|
72
78
|
runtime.error?.(danger(`telegram debounce flush failed: ${String(err)}`));
|
|
73
79
|
},
|
|
74
80
|
});
|
|
81
|
+
const resolveTelegramSessionModel = (params) => {
|
|
82
|
+
const resolvedThreadId = params.resolvedThreadId ??
|
|
83
|
+
resolveTelegramForumThreadId({
|
|
84
|
+
isForum: params.isForum,
|
|
85
|
+
messageThreadId: params.messageThreadId,
|
|
86
|
+
});
|
|
87
|
+
const peerId = params.isGroup
|
|
88
|
+
? buildTelegramGroupPeerId(params.chatId, resolvedThreadId)
|
|
89
|
+
: String(params.chatId);
|
|
90
|
+
const parentPeer = buildTelegramParentPeer({
|
|
91
|
+
isGroup: params.isGroup,
|
|
92
|
+
resolvedThreadId,
|
|
93
|
+
chatId: params.chatId,
|
|
94
|
+
});
|
|
95
|
+
const route = resolveAgentRoute({
|
|
96
|
+
cfg,
|
|
97
|
+
channel: "telegram",
|
|
98
|
+
accountId,
|
|
99
|
+
peer: {
|
|
100
|
+
kind: params.isGroup ? "group" : "dm",
|
|
101
|
+
id: peerId,
|
|
102
|
+
},
|
|
103
|
+
parentPeer,
|
|
104
|
+
});
|
|
105
|
+
const baseSessionKey = route.sessionKey;
|
|
106
|
+
const dmThreadId = !params.isGroup ? params.messageThreadId : undefined;
|
|
107
|
+
const threadKeys = dmThreadId != null
|
|
108
|
+
? resolveThreadSessionKeys({ baseSessionKey, threadId: String(dmThreadId) })
|
|
109
|
+
: null;
|
|
110
|
+
const sessionKey = threadKeys?.sessionKey ?? baseSessionKey;
|
|
111
|
+
const storePath = resolveStorePath(cfg.session?.store, { agentId: route.agentId });
|
|
112
|
+
const store = loadSessionStore(storePath);
|
|
113
|
+
const entry = store[sessionKey];
|
|
114
|
+
const storedOverride = resolveStoredModelOverride({
|
|
115
|
+
sessionEntry: entry,
|
|
116
|
+
sessionStore: store,
|
|
117
|
+
sessionKey,
|
|
118
|
+
});
|
|
119
|
+
if (storedOverride) {
|
|
120
|
+
return storedOverride.provider
|
|
121
|
+
? `${storedOverride.provider}/${storedOverride.model}`
|
|
122
|
+
: storedOverride.model;
|
|
123
|
+
}
|
|
124
|
+
const provider = entry?.modelProvider?.trim();
|
|
125
|
+
const model = entry?.model?.trim();
|
|
126
|
+
if (provider && model) {
|
|
127
|
+
return `${provider}/${model}`;
|
|
128
|
+
}
|
|
129
|
+
const modelCfg = cfg.agents?.defaults?.model;
|
|
130
|
+
return typeof modelCfg === "string" ? modelCfg : modelCfg?.primary;
|
|
131
|
+
};
|
|
75
132
|
const processMediaGroup = async (entry) => {
|
|
76
133
|
try {
|
|
77
134
|
entry.messages.sort((a, b) => a.msg.message_id - b.msg.message_id);
|
|
@@ -205,7 +262,7 @@ export const registerTelegramHandlers = ({ cfg, accountId, bot, opts, runtime, m
|
|
|
205
262
|
}
|
|
206
263
|
}
|
|
207
264
|
const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy;
|
|
208
|
-
const groupPolicy = telegramCfg.groupPolicy
|
|
265
|
+
const groupPolicy = firstDefined(topicConfig?.groupPolicy, groupConfig?.groupPolicy, telegramCfg.groupPolicy, defaultGroupPolicy, "open");
|
|
209
266
|
if (groupPolicy === "disabled") {
|
|
210
267
|
logVerbose(`Blocked telegram group message (groupPolicy: disabled)`);
|
|
211
268
|
return;
|
|
@@ -293,6 +350,90 @@ export const registerTelegramHandlers = ({ cfg, accountId, bot, opts, runtime, m
|
|
|
293
350
|
}
|
|
294
351
|
return;
|
|
295
352
|
}
|
|
353
|
+
// Model selection callback handler (mdl_prov, mdl_list_*, mdl_sel_*, mdl_back)
|
|
354
|
+
const modelCallback = parseModelCallbackData(data);
|
|
355
|
+
if (modelCallback) {
|
|
356
|
+
const modelData = await buildModelsProviderData(cfg);
|
|
357
|
+
const { byProvider, providers } = modelData;
|
|
358
|
+
const editMessageWithButtons = async (text, buttons) => {
|
|
359
|
+
const keyboard = buildInlineKeyboard(buttons);
|
|
360
|
+
try {
|
|
361
|
+
await bot.api.editMessageText(callbackMessage.chat.id, callbackMessage.message_id, text, keyboard ? { reply_markup: keyboard } : undefined);
|
|
362
|
+
}
|
|
363
|
+
catch (editErr) {
|
|
364
|
+
const errStr = String(editErr);
|
|
365
|
+
if (!errStr.includes("message is not modified")) {
|
|
366
|
+
throw editErr;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
if (modelCallback.type === "providers" || modelCallback.type === "back") {
|
|
371
|
+
if (providers.length === 0) {
|
|
372
|
+
await editMessageWithButtons("No providers available.", []);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
const providerInfos = providers.map((p) => ({
|
|
376
|
+
id: p,
|
|
377
|
+
count: byProvider.get(p)?.size ?? 0,
|
|
378
|
+
}));
|
|
379
|
+
const buttons = buildProviderKeyboard(providerInfos);
|
|
380
|
+
await editMessageWithButtons("Select a provider:", buttons);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
if (modelCallback.type === "list") {
|
|
384
|
+
const { provider, page } = modelCallback;
|
|
385
|
+
const modelSet = byProvider.get(provider);
|
|
386
|
+
if (!modelSet || modelSet.size === 0) {
|
|
387
|
+
const providerInfos = providers.map((p) => ({
|
|
388
|
+
id: p,
|
|
389
|
+
count: byProvider.get(p)?.size ?? 0,
|
|
390
|
+
}));
|
|
391
|
+
const buttons = buildProviderKeyboard(providerInfos);
|
|
392
|
+
await editMessageWithButtons(`Unknown provider: ${provider}\n\nSelect a provider:`, buttons);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
const models = [...modelSet].toSorted();
|
|
396
|
+
const pageSize = getModelsPageSize();
|
|
397
|
+
const totalPages = calculateTotalPages(models.length, pageSize);
|
|
398
|
+
const safePage = Math.max(1, Math.min(page, totalPages));
|
|
399
|
+
const currentModel = resolveTelegramSessionModel({
|
|
400
|
+
chatId,
|
|
401
|
+
isGroup,
|
|
402
|
+
isForum,
|
|
403
|
+
messageThreadId,
|
|
404
|
+
resolvedThreadId,
|
|
405
|
+
});
|
|
406
|
+
const buttons = buildModelsKeyboard({
|
|
407
|
+
provider,
|
|
408
|
+
models,
|
|
409
|
+
currentModel,
|
|
410
|
+
currentPage: safePage,
|
|
411
|
+
totalPages,
|
|
412
|
+
pageSize,
|
|
413
|
+
});
|
|
414
|
+
const text = `Models (${provider}) — ${models.length} available`;
|
|
415
|
+
await editMessageWithButtons(text, buttons);
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
if (modelCallback.type === "select") {
|
|
419
|
+
const { provider, model } = modelCallback;
|
|
420
|
+
const syntheticMessage = {
|
|
421
|
+
...callbackMessage,
|
|
422
|
+
from: callback.from,
|
|
423
|
+
text: `/model ${provider}/${model}`,
|
|
424
|
+
caption: undefined,
|
|
425
|
+
caption_entities: undefined,
|
|
426
|
+
entities: undefined,
|
|
427
|
+
};
|
|
428
|
+
const getFile = typeof ctx.getFile === "function" ? ctx.getFile.bind(ctx) : async () => ({});
|
|
429
|
+
await processMessage({ message: syntheticMessage, me: ctx.me, getFile }, [], storeAllowFrom, {
|
|
430
|
+
forceWasMentioned: true,
|
|
431
|
+
messageIdOverride: callback.id,
|
|
432
|
+
});
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
296
437
|
const syntheticMessage = {
|
|
297
438
|
...callbackMessage,
|
|
298
439
|
from: callback.from,
|
|
@@ -403,7 +544,7 @@ export const registerTelegramHandlers = ({ cfg, accountId, bot, opts, runtime, m
|
|
|
403
544
|
// - "disabled": block all group messages entirely
|
|
404
545
|
// - "allowlist": only allow group messages from senders in groupAllowFrom/allowFrom
|
|
405
546
|
const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy;
|
|
406
|
-
const groupPolicy = telegramCfg.groupPolicy
|
|
547
|
+
const groupPolicy = firstDefined(topicConfig?.groupPolicy, groupConfig?.groupPolicy, telegramCfg.groupPolicy, defaultGroupPolicy, "open");
|
|
407
548
|
if (groupPolicy === "disabled") {
|
|
408
549
|
logVerbose(`Blocked telegram group message (groupPolicy: disabled)`);
|
|
409
550
|
return;
|
|
@@ -11,6 +11,7 @@ import { formatLocationText, toLocationContext } from "../channels/location.js";
|
|
|
11
11
|
import { recordInboundSession } from "../channels/session.js";
|
|
12
12
|
import { formatCliCommand } from "../cli/command-format.js";
|
|
13
13
|
import { readSessionUpdatedAt, resolveStorePath } from "../config/sessions.js";
|
|
14
|
+
import { loadConfig } from "../config/config.js";
|
|
14
15
|
import { logVerbose, shouldLogVerbose } from "../globals.js";
|
|
15
16
|
import { recordChannelActivity } from "../infra/channel-activity.js";
|
|
16
17
|
import { resolveAgentRoute } from "../routing/resolve-route.js";
|
|
@@ -20,7 +21,7 @@ import { resolveMentionGatingWithBypass } from "../channels/mention-gating.js";
|
|
|
20
21
|
import { resolveControlCommandGate } from "../channels/command-gating.js";
|
|
21
22
|
import { logInboundDrop } from "../channels/logging.js";
|
|
22
23
|
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
|
23
|
-
import { buildGroupLabel, buildSenderLabel, buildSenderName, buildTelegramGroupFrom, buildTelegramGroupPeerId, buildTypingThreadParams, expandTextLinks, normalizeForwardedContext, describeReplyTarget, extractTelegramLocation, hasBotMention,
|
|
24
|
+
import { buildGroupLabel, buildSenderLabel, buildSenderName, buildTelegramGroupFrom, buildTelegramGroupPeerId, buildTelegramParentPeer, buildTypingThreadParams, expandTextLinks, normalizeForwardedContext, describeReplyTarget, extractTelegramLocation, hasBotMention, resolveTelegramThreadSpec, } from "./bot/helpers.js";
|
|
24
25
|
import { firstDefined, isSenderAllowed, normalizeAllowFromWithStore, resolveSenderAllowMatch, } from "./bot-access.js";
|
|
25
26
|
import { upsertTelegramPairingRequest } from "./pairing-store.js";
|
|
26
27
|
async function resolveStickerVisionSupport(params) {
|
|
@@ -50,23 +51,28 @@ export const buildTelegramMessageContext = async ({ primaryCtx, allMedia, storeA
|
|
|
50
51
|
const isGroup = msg.chat.type === "group" || msg.chat.type === "supergroup";
|
|
51
52
|
const messageThreadId = msg.message_thread_id;
|
|
52
53
|
const isForum = msg.chat.is_forum === true;
|
|
53
|
-
const
|
|
54
|
+
const threadSpec = resolveTelegramThreadSpec({
|
|
55
|
+
isGroup,
|
|
54
56
|
isForum,
|
|
55
57
|
messageThreadId,
|
|
56
58
|
});
|
|
59
|
+
const resolvedThreadId = threadSpec.scope === "forum" ? threadSpec.id : undefined;
|
|
60
|
+
const replyThreadId = threadSpec.id;
|
|
57
61
|
const { groupConfig, topicConfig } = resolveTelegramGroupConfig(chatId, resolvedThreadId);
|
|
58
62
|
const peerId = isGroup ? buildTelegramGroupPeerId(chatId, resolvedThreadId) : String(chatId);
|
|
63
|
+
const parentPeer = buildTelegramParentPeer({ isGroup, resolvedThreadId, chatId });
|
|
59
64
|
const route = resolveAgentRoute({
|
|
60
|
-
cfg,
|
|
65
|
+
cfg: loadConfig(),
|
|
61
66
|
channel: "telegram",
|
|
62
67
|
accountId: account.accountId,
|
|
63
68
|
peer: {
|
|
64
69
|
kind: isGroup ? "group" : "dm",
|
|
65
70
|
id: peerId,
|
|
66
71
|
},
|
|
72
|
+
parentPeer,
|
|
67
73
|
});
|
|
68
74
|
const baseSessionKey = route.sessionKey;
|
|
69
|
-
const dmThreadId =
|
|
75
|
+
const dmThreadId = threadSpec.scope === "dm" ? threadSpec.id : undefined;
|
|
70
76
|
const threadKeys = dmThreadId != null
|
|
71
77
|
? resolveThreadSessionKeys({ baseSessionKey, threadId: String(dmThreadId) })
|
|
72
78
|
: null;
|
|
@@ -90,14 +96,14 @@ export const buildTelegramMessageContext = async ({ primaryCtx, allMedia, storeA
|
|
|
90
96
|
const sendTyping = async () => {
|
|
91
97
|
await withTelegramApiErrorLogging({
|
|
92
98
|
operation: "sendChatAction",
|
|
93
|
-
fn: () => bot.api.sendChatAction(chatId, "typing", buildTypingThreadParams(
|
|
99
|
+
fn: () => bot.api.sendChatAction(chatId, "typing", buildTypingThreadParams(replyThreadId)),
|
|
94
100
|
});
|
|
95
101
|
};
|
|
96
102
|
const sendRecordVoice = async () => {
|
|
97
103
|
try {
|
|
98
104
|
await withTelegramApiErrorLogging({
|
|
99
105
|
operation: "sendChatAction",
|
|
100
|
-
fn: () => bot.api.sendChatAction(chatId, "record_voice", buildTypingThreadParams(
|
|
106
|
+
fn: () => bot.api.sendChatAction(chatId, "record_voice", buildTypingThreadParams(replyThreadId)),
|
|
101
107
|
});
|
|
102
108
|
}
|
|
103
109
|
catch (err) {
|
|
@@ -109,7 +115,8 @@ export const buildTelegramMessageContext = async ({ primaryCtx, allMedia, storeA
|
|
|
109
115
|
if (dmPolicy === "disabled")
|
|
110
116
|
return null;
|
|
111
117
|
if (dmPolicy !== "open") {
|
|
112
|
-
const
|
|
118
|
+
const senderUserId = msg.from?.id != null ? String(msg.from.id) : null;
|
|
119
|
+
const candidate = senderUserId ?? String(chatId);
|
|
113
120
|
const senderUsername = msg.from?.username ?? "";
|
|
114
121
|
const allowMatch = resolveSenderAllowMatch({
|
|
115
122
|
allow: effectiveDmAllow,
|
|
@@ -131,7 +138,8 @@ export const buildTelegramMessageContext = async ({ primaryCtx, allMedia, storeA
|
|
|
131
138
|
});
|
|
132
139
|
if (created) {
|
|
133
140
|
logger.info({
|
|
134
|
-
chatId:
|
|
141
|
+
chatId: String(chatId),
|
|
142
|
+
senderUserId: senderUserId ?? undefined,
|
|
135
143
|
username: from?.username,
|
|
136
144
|
firstName: from?.first_name,
|
|
137
145
|
lastName: from?.last_name,
|
|
@@ -201,6 +209,8 @@ export const buildTelegramMessageContext = async ({ primaryCtx, allMedia, storeA
|
|
|
201
209
|
placeholder = "<media:image>";
|
|
202
210
|
else if (msg.video)
|
|
203
211
|
placeholder = "<media:video>";
|
|
212
|
+
else if (msg.video_note)
|
|
213
|
+
placeholder = "<media:video>";
|
|
204
214
|
else if (msg.audio || msg.voice)
|
|
205
215
|
placeholder = "<media:audio>";
|
|
206
216
|
else if (msg.document)
|
|
@@ -326,7 +336,9 @@ export const buildTelegramMessageContext = async ({ primaryCtx, allMedia, storeA
|
|
|
326
336
|
const replyTarget = describeReplyTarget(msg);
|
|
327
337
|
const forwardOrigin = normalizeForwardedContext(msg);
|
|
328
338
|
const replySuffix = replyTarget
|
|
329
|
-
?
|
|
339
|
+
? replyTarget.kind === "quote"
|
|
340
|
+
? `\n\n[Quoting ${replyTarget.sender}${replyTarget.id ? ` id:${replyTarget.id}` : ""}]\n"${replyTarget.body}"\n[/Quoting]`
|
|
341
|
+
: `\n\n[Replying to ${replyTarget.sender}${replyTarget.id ? ` id:${replyTarget.id}` : ""}]\n${replyTarget.body}\n[/Replying]`
|
|
330
342
|
: "";
|
|
331
343
|
const forwardPrefix = forwardOrigin
|
|
332
344
|
? `[Forwarded from ${forwardOrigin.from}${forwardOrigin.date ? ` at ${new Date(forwardOrigin.date * 1000).toISOString()}` : ""}]\n`
|
|
@@ -383,9 +395,17 @@ export const buildTelegramMessageContext = async ({ primaryCtx, allMedia, storeA
|
|
|
383
395
|
].filter((entry) => Boolean(entry));
|
|
384
396
|
const groupSystemPrompt = systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : undefined;
|
|
385
397
|
const commandBody = normalizeCommandBody(rawBody, { botUsername });
|
|
398
|
+
const inboundHistory = isGroup && historyKey && historyLimit > 0
|
|
399
|
+
? (groupHistories.get(historyKey) ?? []).map((entry) => ({
|
|
400
|
+
sender: entry.sender,
|
|
401
|
+
body: entry.body,
|
|
402
|
+
timestamp: entry.timestamp,
|
|
403
|
+
}))
|
|
404
|
+
: undefined;
|
|
386
405
|
const ctxPayload = finalizeInboundContext({
|
|
387
406
|
Body: combinedBody,
|
|
388
407
|
RawBody: rawBody,
|
|
408
|
+
BodyForAgent: bodyText,
|
|
389
409
|
CommandBody: commandBody,
|
|
390
410
|
From: isGroup ? buildTelegramGroupFrom(chatId, resolvedThreadId) : `telegram:${chatId}`,
|
|
391
411
|
To: `telegram:${chatId}`,
|
|
@@ -404,12 +424,15 @@ export const buildTelegramMessageContext = async ({ primaryCtx, allMedia, storeA
|
|
|
404
424
|
ReplyToId: replyTarget?.id,
|
|
405
425
|
ReplyToBody: replyTarget?.body,
|
|
406
426
|
ReplyToSender: replyTarget?.sender,
|
|
427
|
+
ReplyToIsQuote: replyTarget?.kind === "quote" ? true : undefined,
|
|
407
428
|
ForwardedFrom: forwardOrigin?.from,
|
|
408
429
|
ForwardedFromType: forwardOrigin?.fromType,
|
|
409
430
|
ForwardedFromId: forwardOrigin?.fromId,
|
|
410
431
|
ForwardedFromUsername: forwardOrigin?.fromUsername,
|
|
411
432
|
ForwardedFromTitle: forwardOrigin?.fromTitle,
|
|
412
433
|
ForwardedFromSignature: forwardOrigin?.fromSignature,
|
|
434
|
+
ForwardedFromChatType: forwardOrigin?.fromChatType,
|
|
435
|
+
ForwardedFromMessageId: forwardOrigin?.fromMessageId,
|
|
413
436
|
ForwardedDate: forwardOrigin?.date ? forwardOrigin.date * 1000 : undefined,
|
|
414
437
|
Timestamp: msg.date ? msg.date * 1000 : undefined,
|
|
415
438
|
WasMentioned: isGroup ? effectiveWasMentioned : undefined,
|
|
@@ -435,7 +458,8 @@ export const buildTelegramMessageContext = async ({ primaryCtx, allMedia, storeA
|
|
|
435
458
|
Sticker: allMedia[0]?.stickerMetadata,
|
|
436
459
|
...(locationData ? toLocationContext(locationData) : undefined),
|
|
437
460
|
CommandAuthorized: commandAuthorized,
|
|
438
|
-
|
|
461
|
+
InboundHistory: inboundHistory,
|
|
462
|
+
MessageThreadId: threadSpec.id,
|
|
439
463
|
IsForum: isForum,
|
|
440
464
|
// Originating channel for reply routing.
|
|
441
465
|
OriginatingChannel: "telegram",
|
|
@@ -451,6 +475,7 @@ export const buildTelegramMessageContext = async ({ primaryCtx, allMedia, storeA
|
|
|
451
475
|
channel: "telegram",
|
|
452
476
|
to: String(chatId),
|
|
453
477
|
accountId: route.accountId,
|
|
478
|
+
threadId: dmThreadId != null ? String(dmThreadId) : undefined,
|
|
454
479
|
}
|
|
455
480
|
: undefined,
|
|
456
481
|
onRecordError: (err) => {
|
|
@@ -477,6 +502,8 @@ export const buildTelegramMessageContext = async ({ primaryCtx, allMedia, storeA
|
|
|
477
502
|
chatId,
|
|
478
503
|
isGroup,
|
|
479
504
|
resolvedThreadId,
|
|
505
|
+
threadSpec,
|
|
506
|
+
replyThreadId,
|
|
480
507
|
isForum,
|
|
481
508
|
historyKey,
|
|
482
509
|
historyLimit,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
import { EmbeddedBlockChunker } from "../agents/pi-embedded-block-chunker.js";
|
|
1
|
+
import { resolveAgentDir } from "../agents/agent-scope.js";
|
|
3
2
|
import { findModelInCatalog, loadModelCatalog, modelSupportsVision, } from "../agents/model-catalog.js";
|
|
4
3
|
import { resolveDefaultModelForAgent } from "../agents/model-selection.js";
|
|
4
|
+
import { EmbeddedBlockChunker } from "../agents/pi-embedded-block-chunker.js";
|
|
5
5
|
import { resolveChunkMode } from "../auto-reply/chunk.js";
|
|
6
6
|
import { clearHistoryEntriesIfEnabled } from "../auto-reply/reply/history.js";
|
|
7
7
|
import { dispatchReplyWithBufferedBlockDispatcher } from "../auto-reply/reply/provider-dispatcher.js";
|
|
@@ -9,13 +9,13 @@ import { removeAckReactionAfterReply } from "../channels/ack-reactions.js";
|
|
|
9
9
|
import { logAckFailure, logTypingFailure } from "../channels/logging.js";
|
|
10
10
|
import { createReplyPrefixContext } from "../channels/reply-prefix.js";
|
|
11
11
|
import { createTypingCallbacks } from "../channels/typing.js";
|
|
12
|
-
import { danger, logVerbose } from "../globals.js";
|
|
13
12
|
import { resolveMarkdownTableMode } from "../config/markdown-tables.js";
|
|
13
|
+
import { danger, logVerbose } from "../globals.js";
|
|
14
14
|
import { deliverReplies } from "./bot/delivery.js";
|
|
15
15
|
import { resolveTelegramDraftStreamingChunking } from "./draft-chunking.js";
|
|
16
16
|
import { createTelegramDraftStream } from "./draft-stream.js";
|
|
17
17
|
import { cacheSticker, describeStickerImage } from "./sticker-cache.js";
|
|
18
|
-
|
|
18
|
+
const EMPTY_RESPONSE_FALLBACK = "No response generated. Please try again.";
|
|
19
19
|
async function resolveStickerVisionSupport(cfg, agentId) {
|
|
20
20
|
try {
|
|
21
21
|
const catalog = await loadModelCatalog({ config: cfg });
|
|
@@ -118,7 +118,7 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
|
|
|
118
118
|
// Handle uncached stickers: get a dedicated vision description before dispatch
|
|
119
119
|
// This ensures we cache a raw description rather than a conversational response
|
|
120
120
|
const sticker = ctxPayload.Sticker;
|
|
121
|
-
if (sticker?.fileUniqueId && ctxPayload.MediaPath) {
|
|
121
|
+
if (sticker?.fileId && sticker.fileUniqueId && ctxPayload.MediaPath) {
|
|
122
122
|
const agentDir = resolveAgentDir(cfg, route.agentId);
|
|
123
123
|
const stickerSupportsVision = await resolveStickerVisionSupport(cfg, route.agentId);
|
|
124
124
|
let description = sticker.cachedDescription ?? null;
|
|
@@ -150,19 +150,32 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
|
|
|
150
150
|
ctxPayload.MediaTypes = undefined;
|
|
151
151
|
}
|
|
152
152
|
// Cache the description for future encounters
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
153
|
+
if (sticker.fileId) {
|
|
154
|
+
cacheSticker({
|
|
155
|
+
fileId: sticker.fileId,
|
|
156
|
+
fileUniqueId: sticker.fileUniqueId,
|
|
157
|
+
emoji: sticker.emoji,
|
|
158
|
+
setName: sticker.setName,
|
|
159
|
+
description,
|
|
160
|
+
cachedAt: new Date().toISOString(),
|
|
161
|
+
receivedFrom: ctxPayload.From,
|
|
162
|
+
});
|
|
163
|
+
logVerbose(`telegram: cached sticker description for ${sticker.fileUniqueId}`);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
logVerbose(`telegram: skipped sticker cache (missing fileId)`);
|
|
167
|
+
}
|
|
163
168
|
}
|
|
164
169
|
}
|
|
165
|
-
|
|
170
|
+
// TODO: pass replyQuoteText to deliverReplies once delivery.ts supports it
|
|
171
|
+
const _replyQuoteText = ctxPayload.ReplyToIsQuote && ctxPayload.ReplyToBody
|
|
172
|
+
? ctxPayload.ReplyToBody.trim() || undefined
|
|
173
|
+
: undefined;
|
|
174
|
+
void _replyQuoteText;
|
|
175
|
+
const deliveryState = {
|
|
176
|
+
delivered: false,
|
|
177
|
+
};
|
|
178
|
+
const { queuedFinal, counts } = await dispatchReplyWithBufferedBlockDispatcher({
|
|
166
179
|
ctx: ctxPayload,
|
|
167
180
|
cfg,
|
|
168
181
|
dispatcherOptions: {
|
|
@@ -187,6 +200,7 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
|
|
|
187
200
|
onVoiceRecording: sendRecordVoice,
|
|
188
201
|
linkPreview: telegramCfg.linkPreview,
|
|
189
202
|
});
|
|
203
|
+
deliveryState.delivered = true;
|
|
190
204
|
},
|
|
191
205
|
onError: (err, info) => {
|
|
192
206
|
runtime.error?.(danger(`telegram ${info.kind} reply failed: ${String(err)}`));
|
|
@@ -219,7 +233,30 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
|
|
|
219
233
|
},
|
|
220
234
|
});
|
|
221
235
|
draftStream?.stop();
|
|
222
|
-
|
|
236
|
+
let sentFallback = false;
|
|
237
|
+
if (!deliveryState.delivered && (counts.tool + counts.block + counts.final) > 0) {
|
|
238
|
+
try {
|
|
239
|
+
await deliverReplies({
|
|
240
|
+
replies: [{ text: EMPTY_RESPONSE_FALLBACK }],
|
|
241
|
+
chatId: String(chatId),
|
|
242
|
+
token: opts.token,
|
|
243
|
+
runtime,
|
|
244
|
+
bot,
|
|
245
|
+
replyToMode,
|
|
246
|
+
textLimit,
|
|
247
|
+
messageThreadId: resolvedThreadId,
|
|
248
|
+
tableMode,
|
|
249
|
+
chunkMode,
|
|
250
|
+
linkPreview: telegramCfg.linkPreview,
|
|
251
|
+
});
|
|
252
|
+
sentFallback = true;
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// Fallback delivery failed; proceed without it
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const hasFinalResponse = queuedFinal || sentFallback;
|
|
259
|
+
if (!hasFinalResponse) {
|
|
223
260
|
if (isGroup && historyKey) {
|
|
224
261
|
clearHistoryEntriesIfEnabled({ historyMap: groupHistories, historyKey, limit: historyLimit });
|
|
225
262
|
}
|