@geminixiang/mama 0.2.0-beta.1 → 0.2.0-beta.3
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/README.md +133 -78
- package/dist/adapter.d.ts +22 -10
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/discord/bot.d.ts +10 -7
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +228 -69
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +92 -34
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/shared.d.ts +23 -0
- package/dist/adapters/shared.d.ts.map +1 -0
- package/dist/adapters/shared.js +57 -0
- package/dist/adapters/shared.js.map +1 -0
- package/dist/adapters/slack/bot.d.ts +19 -11
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +356 -96
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/branch-manager.d.ts +21 -0
- package/dist/adapters/slack/branch-manager.d.ts.map +1 -0
- package/dist/adapters/slack/branch-manager.js +96 -0
- package/dist/adapters/slack/branch-manager.js.map +1 -0
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +100 -67
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/slack/session.d.ts +3 -0
- package/dist/adapters/slack/session.d.ts.map +1 -0
- package/dist/adapters/slack/session.js +16 -0
- package/dist/adapters/slack/session.js.map +1 -0
- package/dist/adapters/telegram/bot.d.ts +4 -2
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +141 -74
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +49 -109
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/adapters/telegram/html.d.ts +3 -0
- package/dist/adapters/telegram/html.d.ts.map +1 -0
- package/dist/adapters/telegram/html.js +98 -0
- package/dist/adapters/telegram/html.js.map +1 -0
- package/dist/agent.d.ts +4 -11
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +116 -196
- package/dist/agent.js.map +1 -1
- package/dist/bindings.d.ts +1 -20
- package/dist/bindings.d.ts.map +1 -1
- package/dist/bindings.js +1 -21
- package/dist/bindings.js.map +1 -1
- package/dist/config.d.ts +9 -27
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +89 -63
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +13 -3
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +102 -18
- package/dist/context.js.map +1 -1
- package/dist/events.d.ts +18 -6
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +86 -35
- package/dist/events.js.map +1 -1
- package/dist/execution-resolver.d.ts.map +1 -1
- package/dist/execution-resolver.js +1 -3
- package/dist/execution-resolver.js.map +1 -1
- package/dist/instrument.d.ts.map +1 -1
- package/dist/instrument.js +5 -11
- package/dist/instrument.js.map +1 -1
- package/dist/{login.d.ts → login/index.d.ts} +2 -2
- package/dist/login/index.d.ts.map +1 -0
- package/dist/{login.js → login/index.js} +2 -2
- package/dist/login/index.js.map +1 -0
- package/dist/{link-server.d.ts → login/portal.d.ts} +6 -4
- package/dist/login/portal.d.ts.map +1 -0
- package/dist/login/portal.js +1453 -0
- package/dist/login/portal.js.map +1 -0
- package/dist/{link-token.d.ts → login/session.d.ts} +1 -1
- package/dist/login/session.d.ts.map +1 -0
- package/dist/{link-token.js → login/session.js} +1 -1
- package/dist/login/session.js.map +1 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +175 -119
- package/dist/main.js.map +1 -1
- package/dist/provisioner.d.ts +17 -43
- package/dist/provisioner.d.ts.map +1 -1
- package/dist/provisioner.js +84 -50
- package/dist/provisioner.js.map +1 -1
- package/dist/sandbox/host.d.ts +0 -2
- package/dist/sandbox/host.d.ts.map +1 -1
- package/dist/sandbox/host.js +1 -5
- package/dist/sandbox/host.js.map +1 -1
- package/dist/sentry.d.ts.map +1 -1
- package/dist/sentry.js +2 -0
- package/dist/sentry.js.map +1 -1
- package/dist/session-policy.d.ts +13 -0
- package/dist/session-policy.d.ts.map +1 -0
- package/dist/session-policy.js +23 -0
- package/dist/session-policy.js.map +1 -0
- package/dist/session-store.d.ts +27 -1
- package/dist/session-store.d.ts.map +1 -1
- package/dist/session-store.js +162 -9
- package/dist/session-store.js.map +1 -1
- package/dist/session-view/command.d.ts +5 -0
- package/dist/session-view/command.d.ts.map +1 -0
- package/dist/session-view/command.js +11 -0
- package/dist/session-view/command.js.map +1 -0
- package/dist/session-view/portal.d.ts +9 -0
- package/dist/session-view/portal.d.ts.map +1 -0
- package/dist/session-view/portal.js +766 -0
- package/dist/session-view/portal.js.map +1 -0
- package/dist/session-view/service.d.ts +34 -0
- package/dist/session-view/service.d.ts.map +1 -0
- package/dist/session-view/service.js +380 -0
- package/dist/session-view/service.js.map +1 -0
- package/dist/session-view/store.d.ts +16 -0
- package/dist/session-view/store.d.ts.map +1 -0
- package/dist/session-view/store.js +38 -0
- package/dist/session-view/store.js.map +1 -0
- package/dist/store.d.ts +3 -6
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +15 -35
- package/dist/store.js.map +1 -1
- package/dist/tools/event.d.ts +3 -0
- package/dist/tools/event.d.ts.map +1 -1
- package/dist/tools/event.js +27 -8
- package/dist/tools/event.js.map +1 -1
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/ui-copy.d.ts +1 -0
- package/dist/ui-copy.d.ts.map +1 -1
- package/dist/ui-copy.js +3 -0
- package/dist/ui-copy.js.map +1 -1
- package/dist/vault-routing.d.ts +1 -2
- package/dist/vault-routing.d.ts.map +1 -1
- package/dist/vault-routing.js +1 -7
- package/dist/vault-routing.js.map +1 -1
- package/package.json +1 -1
- package/dist/link-server.d.ts.map +0 -1
- package/dist/link-server.js +0 -839
- package/dist/link-server.js.map +0 -1
- package/dist/link-token.d.ts.map +0 -1
- package/dist/link-token.js.map +0 -1
- package/dist/login.d.ts.map +0 -1
- package/dist/login.js.map +0 -1
- package/dist/vault.test.d.ts +0 -2
- package/dist/vault.test.d.ts.map +0 -1
- package/dist/vault.test.js +0 -67
- package/dist/vault.test.js.map +0 -1
|
@@ -1,108 +1,30 @@
|
|
|
1
1
|
import * as log from "../../log.js";
|
|
2
|
+
import { formatToolArgs, splitText } from "../shared.js";
|
|
3
|
+
import { sanitizeTelegramHtml } from "./html.js";
|
|
2
4
|
export const TELEGRAM_FORMATTING_GUIDE = `## Telegram Formatting (HTML mode)
|
|
3
5
|
Bold: <b>text</b>, Italic: <i>text</i>, Code: <code>code</code>, Pre: <pre>code</pre>
|
|
4
6
|
Links: <a href="url">text</a>
|
|
5
7
|
Do NOT use Markdown asterisks or backtick syntax.
|
|
6
8
|
Do NOT use <table> tags — they are unsupported. Use <pre> with ASCII art for tables instead.`;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const cells = [];
|
|
11
|
-
for (const cellMatch of rowMatch[1].matchAll(/<t[dh][^>]*>([\s\S]*?)<\/t[dh]>/gi)) {
|
|
12
|
-
cells.push(cellMatch[1].replace(/<[^>]+>/g, "").trim());
|
|
13
|
-
}
|
|
14
|
-
if (cells.length > 0)
|
|
15
|
-
rows.push(cells);
|
|
16
|
-
}
|
|
17
|
-
if (rows.length === 0)
|
|
18
|
-
return "";
|
|
19
|
-
const numCols = Math.max(...rows.map((r) => r.length));
|
|
20
|
-
const colWidths = Array(numCols).fill(0);
|
|
21
|
-
for (const row of rows) {
|
|
22
|
-
for (let i = 0; i < row.length; i++) {
|
|
23
|
-
colWidths[i] = Math.max(colWidths[i], (row[i] ?? "").length);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
const sep = "+" + colWidths.map((w) => "-".repeat(w + 2)).join("+") + "+";
|
|
27
|
-
const lines = [sep];
|
|
28
|
-
for (let i = 0; i < rows.length; i++) {
|
|
29
|
-
const cells = colWidths.map((w, j) => ` ${(rows[i][j] ?? "").padEnd(w)} `);
|
|
30
|
-
lines.push("|" + cells.join("|") + "|");
|
|
31
|
-
if (i === 0)
|
|
32
|
-
lines.push(sep);
|
|
33
|
-
}
|
|
34
|
-
lines.push(sep);
|
|
35
|
-
return lines.join("\n");
|
|
36
|
-
}
|
|
37
|
-
const ALLOWED_TELEGRAM_TAGS = new Set([
|
|
38
|
-
"b",
|
|
39
|
-
"strong",
|
|
40
|
-
"i",
|
|
41
|
-
"em",
|
|
42
|
-
"u",
|
|
43
|
-
"ins",
|
|
44
|
-
"s",
|
|
45
|
-
"strike",
|
|
46
|
-
"del",
|
|
47
|
-
"code",
|
|
48
|
-
"pre",
|
|
49
|
-
"a",
|
|
50
|
-
]);
|
|
51
|
-
function escapeHtml(text) {
|
|
52
|
-
return text
|
|
53
|
-
.replace(/&(?!(?:[a-z]+|#\d+|#x[0-9a-f]+);)/gi, "&")
|
|
54
|
-
.replace(/</g, "<")
|
|
55
|
-
.replace(/>/g, ">");
|
|
56
|
-
}
|
|
57
|
-
function escapeAttr(value) {
|
|
58
|
-
return escapeHtml(value).replace(/"/g, """);
|
|
59
|
-
}
|
|
60
|
-
function normalizeAllowedTelegramTag(tag) {
|
|
61
|
-
const closing = tag.match(/^<\s*\/\s*([a-z0-9-]+)\s*>$/i);
|
|
62
|
-
if (closing) {
|
|
63
|
-
const name = closing[1].toLowerCase();
|
|
64
|
-
return ALLOWED_TELEGRAM_TAGS.has(name) ? `</${name}>` : null;
|
|
65
|
-
}
|
|
66
|
-
const simple = tag.match(/^<\s*([a-z0-9-]+)\s*>$/i);
|
|
67
|
-
if (simple) {
|
|
68
|
-
const name = simple[1].toLowerCase();
|
|
69
|
-
return ALLOWED_TELEGRAM_TAGS.has(name) && name !== "a" ? `<${name}>` : null;
|
|
70
|
-
}
|
|
71
|
-
const anchor = tag.match(/^<\s*a\s+href\s*=\s*(?:"([^"]*)"|'([^']*)')\s*>$/i);
|
|
72
|
-
if (anchor) {
|
|
73
|
-
const href = anchor[1] ?? anchor[2] ?? "";
|
|
74
|
-
return `<a href="${escapeAttr(href)}">`;
|
|
75
|
-
}
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
function sanitizeTelegramHtml(text) {
|
|
79
|
-
const withTablesNormalized = text.replace(/<table[\s\S]*?<\/table>/gi, (tableHtml) => {
|
|
80
|
-
const ascii = htmlTableToText(tableHtml);
|
|
81
|
-
return ascii ? `<pre>${escapeHtml(ascii)}</pre>` : "";
|
|
82
|
-
});
|
|
83
|
-
const placeholders = [];
|
|
84
|
-
const tokenized = withTablesNormalized.replace(/<\/?[a-z][^>]*>/gi, (tag) => {
|
|
85
|
-
const normalized = normalizeAllowedTelegramTag(tag);
|
|
86
|
-
if (!normalized) {
|
|
87
|
-
return tag;
|
|
88
|
-
}
|
|
89
|
-
const token = `__TG_HTML_${placeholders.length}__`;
|
|
90
|
-
placeholders.push(normalized);
|
|
91
|
-
return token;
|
|
92
|
-
});
|
|
93
|
-
const escaped = escapeHtml(tokenized);
|
|
94
|
-
return escaped.replace(/__TG_HTML_(\d+)__/g, (_match, index) => placeholders[Number(index)] ?? "");
|
|
95
|
-
}
|
|
9
|
+
// Telegram message length limit is 4096 chars; 3800 leaves headroom for HTML escapes.
|
|
10
|
+
const MAX_LENGTH = 3800;
|
|
11
|
+
const formatTelegramContinuation = (partNum) => `(continued ${partNum})`;
|
|
96
12
|
async function notifyError(bot, chatId, label, err) {
|
|
97
13
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
98
14
|
log.logWarning(`Telegram ${label} error`, errMsg);
|
|
99
15
|
try {
|
|
100
|
-
await bot.postPlainMessage(chatId,
|
|
16
|
+
await bot.postPlainMessage(chatId, `⚠️ 發送失敗:${errMsg}`);
|
|
101
17
|
}
|
|
102
18
|
catch {
|
|
103
19
|
// ignore secondary failure
|
|
104
20
|
}
|
|
105
21
|
}
|
|
22
|
+
function formatToolResult(result) {
|
|
23
|
+
const argsFormatted = formatToolArgs(result.args);
|
|
24
|
+
const duration = (result.durationMs / 1000).toFixed(1);
|
|
25
|
+
const title = `${result.isError ? "Error" : "Done"} ${result.toolName}${result.label ? `: ${result.label}` : ""} (${duration}s)`;
|
|
26
|
+
return [title, argsFormatted, result.result].filter(Boolean).join("\n\n");
|
|
27
|
+
}
|
|
106
28
|
export function createTelegramAdapters(event, bot, _isEvent) {
|
|
107
29
|
let messageId = null;
|
|
108
30
|
let accumulatedText = "";
|
|
@@ -114,11 +36,13 @@ export function createTelegramAdapters(event, bot, _isEvent) {
|
|
|
114
36
|
typingInterval = null;
|
|
115
37
|
}
|
|
116
38
|
}
|
|
117
|
-
const
|
|
39
|
+
const conversationId = event.conversationId;
|
|
40
|
+
const chatId = parseInt(conversationId);
|
|
118
41
|
const replyToId = event.thread_ts ? parseInt(event.thread_ts) : null;
|
|
119
42
|
const message = {
|
|
120
43
|
id: event.ts,
|
|
121
|
-
sessionKey: event.sessionKey ?? `${
|
|
44
|
+
sessionKey: event.sessionKey ?? `${conversationId}:${event.thread_ts ?? event.ts}`,
|
|
45
|
+
conversationKind: event.conversationKind,
|
|
122
46
|
userId: event.user,
|
|
123
47
|
userName: event.userName,
|
|
124
48
|
text: event.text,
|
|
@@ -131,18 +55,12 @@ export function createTelegramAdapters(event, bot, _isEvent) {
|
|
|
131
55
|
channels: [],
|
|
132
56
|
users: [],
|
|
133
57
|
};
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const truncationNote = "\n\n<i>(message truncated, ask me to elaborate on specific parts)</i>";
|
|
137
|
-
function truncate(text, limit, note) {
|
|
138
|
-
if (text.length > limit) {
|
|
139
|
-
return text.substring(0, limit - note.length) + note;
|
|
140
|
-
}
|
|
141
|
-
return text;
|
|
58
|
+
async function sendContinuation(text) {
|
|
59
|
+
await bot.postMessageRaw(chatId, text);
|
|
142
60
|
}
|
|
143
61
|
async function sendOrUpdate(displayText) {
|
|
144
62
|
if (messageId !== null) {
|
|
145
|
-
await bot.updateMessage(
|
|
63
|
+
await bot.updateMessage(conversationId, String(messageId), displayText);
|
|
146
64
|
}
|
|
147
65
|
else if (replyToId !== null) {
|
|
148
66
|
messageId = await bot.postReply(chatId, replyToId, displayText);
|
|
@@ -157,10 +75,13 @@ export function createTelegramAdapters(event, bot, _isEvent) {
|
|
|
157
75
|
try {
|
|
158
76
|
const sanitized = sanitizeTelegramHtml(text);
|
|
159
77
|
accumulatedText = accumulatedText ? `${accumulatedText}\n${sanitized}` : sanitized;
|
|
160
|
-
const
|
|
161
|
-
await sendOrUpdate(
|
|
78
|
+
const [firstPart, ...extraParts] = splitText(accumulatedText, MAX_LENGTH, formatTelegramContinuation);
|
|
79
|
+
await sendOrUpdate(firstPart);
|
|
80
|
+
for (const part of extraParts) {
|
|
81
|
+
await sendContinuation(part);
|
|
82
|
+
}
|
|
162
83
|
if (messageId !== null) {
|
|
163
|
-
bot.logBotResponse(
|
|
84
|
+
bot.logBotResponse(conversationId, text, String(messageId));
|
|
164
85
|
}
|
|
165
86
|
}
|
|
166
87
|
catch (err) {
|
|
@@ -172,8 +93,12 @@ export function createTelegramAdapters(event, bot, _isEvent) {
|
|
|
172
93
|
replaceResponse: async (text) => {
|
|
173
94
|
updatePromise = updatePromise.then(async () => {
|
|
174
95
|
try {
|
|
175
|
-
accumulatedText =
|
|
176
|
-
|
|
96
|
+
accumulatedText = sanitizeTelegramHtml(text);
|
|
97
|
+
const [firstPart, ...extraParts] = splitText(accumulatedText, MAX_LENGTH, formatTelegramContinuation);
|
|
98
|
+
await sendOrUpdate(firstPart);
|
|
99
|
+
for (const part of extraParts) {
|
|
100
|
+
await sendContinuation(part);
|
|
101
|
+
}
|
|
177
102
|
}
|
|
178
103
|
catch (err) {
|
|
179
104
|
await notifyError(bot, chatId, "replaceResponse", err);
|
|
@@ -181,8 +106,23 @@ export function createTelegramAdapters(event, bot, _isEvent) {
|
|
|
181
106
|
});
|
|
182
107
|
await updatePromise;
|
|
183
108
|
},
|
|
184
|
-
|
|
185
|
-
|
|
109
|
+
respondDiagnostic: async (text, options) => {
|
|
110
|
+
updatePromise = updatePromise.then(async () => {
|
|
111
|
+
try {
|
|
112
|
+
const prefix = options?.style === "error" ? "Error: " : "";
|
|
113
|
+
for (const part of splitText(sanitizeTelegramHtml(`${prefix}${text}`), MAX_LENGTH, formatTelegramContinuation)) {
|
|
114
|
+
await sendContinuation(part);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
await notifyError(bot, chatId, "respondDiagnostic", err);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
await updatePromise;
|
|
122
|
+
},
|
|
123
|
+
respondToolResult: async (result) => {
|
|
124
|
+
await responseCtx.respondDiagnostic(formatToolResult(result));
|
|
125
|
+
},
|
|
186
126
|
setTyping: async (isTyping) => {
|
|
187
127
|
if (isTyping && typingInterval === null) {
|
|
188
128
|
// Send immediately and repeat every 4s (Telegram clears indicator after ~5s)
|
|
@@ -200,7 +140,7 @@ export function createTelegramAdapters(event, bot, _isEvent) {
|
|
|
200
140
|
stopTyping();
|
|
201
141
|
},
|
|
202
142
|
uploadFile: async (filePath, title) => {
|
|
203
|
-
await bot.uploadFile(
|
|
143
|
+
await bot.uploadFile(conversationId, filePath, title);
|
|
204
144
|
},
|
|
205
145
|
deleteResponse: async () => {
|
|
206
146
|
updatePromise = updatePromise.then(async () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/telegram/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChatMessage, ChatResponseContext, PlatformInfo } from \"../../adapter.js\";\nimport * as log from \"../../log.js\";\nimport type { TelegramBot, TelegramEvent } from \"./bot.js\";\n\nexport const TELEGRAM_FORMATTING_GUIDE = `## Telegram Formatting (HTML mode)\nBold: <b>text</b>, Italic: <i>text</i>, Code: <code>code</code>, Pre: <pre>code</pre>\nLinks: <a href=\"url\">text</a>\nDo NOT use Markdown asterisks or backtick syntax.\nDo NOT use <table> tags — they are unsupported. Use <pre> with ASCII art for tables instead.`;\n\nfunction htmlTableToText(tableHtml: string): string {\n const rows: string[][] = [];\n for (const rowMatch of tableHtml.matchAll(/<tr[^>]*>([\\s\\S]*?)<\\/tr>/gi)) {\n const cells: string[] = [];\n for (const cellMatch of rowMatch[1].matchAll(/<t[dh][^>]*>([\\s\\S]*?)<\\/t[dh]>/gi)) {\n cells.push(cellMatch[1].replace(/<[^>]+>/g, \"\").trim());\n }\n if (cells.length > 0) rows.push(cells);\n }\n\n if (rows.length === 0) return \"\";\n\n const numCols = Math.max(...rows.map((r) => r.length));\n const colWidths: number[] = Array(numCols).fill(0);\n for (const row of rows) {\n for (let i = 0; i < row.length; i++) {\n colWidths[i] = Math.max(colWidths[i], (row[i] ?? \"\").length);\n }\n }\n\n const sep = \"+\" + colWidths.map((w) => \"-\".repeat(w + 2)).join(\"+\") + \"+\";\n const lines: string[] = [sep];\n for (let i = 0; i < rows.length; i++) {\n const cells = colWidths.map((w, j) => ` ${(rows[i][j] ?? \"\").padEnd(w)} `);\n lines.push(\"|\" + cells.join(\"|\") + \"|\");\n if (i === 0) lines.push(sep);\n }\n lines.push(sep);\n return lines.join(\"\\n\");\n}\n\nconst ALLOWED_TELEGRAM_TAGS = new Set([\n \"b\",\n \"strong\",\n \"i\",\n \"em\",\n \"u\",\n \"ins\",\n \"s\",\n \"strike\",\n \"del\",\n \"code\",\n \"pre\",\n \"a\",\n]);\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&(?!(?:[a-z]+|#\\d+|#x[0-9a-f]+);)/gi, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\nfunction escapeAttr(value: string): string {\n return escapeHtml(value).replace(/\"/g, \""\");\n}\n\nfunction normalizeAllowedTelegramTag(tag: string): string | null {\n const closing = tag.match(/^<\\s*\\/\\s*([a-z0-9-]+)\\s*>$/i);\n if (closing) {\n const name = closing[1].toLowerCase();\n return ALLOWED_TELEGRAM_TAGS.has(name) ? `</${name}>` : null;\n }\n\n const simple = tag.match(/^<\\s*([a-z0-9-]+)\\s*>$/i);\n if (simple) {\n const name = simple[1].toLowerCase();\n return ALLOWED_TELEGRAM_TAGS.has(name) && name !== \"a\" ? `<${name}>` : null;\n }\n\n const anchor = tag.match(/^<\\s*a\\s+href\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')\\s*>$/i);\n if (anchor) {\n const href = anchor[1] ?? anchor[2] ?? \"\";\n return `<a href=\"${escapeAttr(href)}\">`;\n }\n\n return null;\n}\n\nfunction sanitizeTelegramHtml(text: string): string {\n const withTablesNormalized = text.replace(/<table[\\s\\S]*?<\\/table>/gi, (tableHtml) => {\n const ascii = htmlTableToText(tableHtml);\n return ascii ? `<pre>${escapeHtml(ascii)}</pre>` : \"\";\n });\n\n const placeholders: string[] = [];\n const tokenized = withTablesNormalized.replace(/<\\/?[a-z][^>]*>/gi, (tag) => {\n const normalized = normalizeAllowedTelegramTag(tag);\n if (!normalized) {\n return tag;\n }\n const token = `__TG_HTML_${placeholders.length}__`;\n placeholders.push(normalized);\n return token;\n });\n\n const escaped = escapeHtml(tokenized);\n return escaped.replace(\n /__TG_HTML_(\\d+)__/g,\n (_match, index) => placeholders[Number(index)] ?? \"\",\n );\n}\n\nasync function notifyError(\n bot: TelegramBot,\n chatId: number,\n label: string,\n err: unknown,\n): Promise<void> {\n const errMsg = err instanceof Error ? err.message : String(err);\n log.logWarning(`Telegram ${label} error`, errMsg);\n try {\n await bot.postPlainMessage(chatId, `Delivery failed: ${errMsg}`);\n } catch {\n // ignore secondary failure\n }\n}\n\nexport function createTelegramAdapters(\n event: TelegramEvent,\n bot: TelegramBot,\n _isEvent?: boolean,\n): {\n message: ChatMessage;\n responseCtx: ChatResponseContext;\n platform: PlatformInfo;\n} {\n let messageId: number | null = null;\n let accumulatedText = \"\";\n let updatePromise = Promise.resolve();\n let typingInterval: ReturnType<typeof setInterval> | null = null;\n\n function stopTyping() {\n if (typingInterval !== null) {\n clearInterval(typingInterval);\n typingInterval = null;\n }\n }\n\n const chatId = parseInt(event.conversationId);\n const replyToId = event.thread_ts ? parseInt(event.thread_ts) : null;\n\n const message: ChatMessage = {\n id: event.ts,\n sessionKey: event.sessionKey ?? `${event.conversationId}:${event.thread_ts ?? event.ts}`,\n userId: event.user,\n userName: event.userName,\n text: event.text,\n attachments: event.attachments,\n threadTs: event.thread_ts,\n };\n\n const platform: PlatformInfo = {\n name: \"telegram\",\n formattingGuide: TELEGRAM_FORMATTING_GUIDE,\n channels: [],\n users: [],\n };\n\n // Telegram message length limit is 4096 chars; use 3800 for safety\n const MAX_LENGTH = 3800;\n const truncationNote = \"\\n\\n<i>(message truncated, ask me to elaborate on specific parts)</i>\";\n\n function truncate(text: string, limit: number, note: string): string {\n if (text.length > limit) {\n return text.substring(0, limit - note.length) + note;\n }\n return text;\n }\n\n async function sendOrUpdate(displayText: string): Promise<void> {\n if (messageId !== null) {\n await bot.updateMessage(event.conversationId, String(messageId), displayText);\n } else if (replyToId !== null) {\n messageId = await bot.postReply(chatId, replyToId, displayText);\n } else {\n messageId = await bot.postMessageRaw(chatId, displayText);\n }\n }\n\n const responseCtx: ChatResponseContext = {\n respond: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n const sanitized = sanitizeTelegramHtml(text);\n accumulatedText = accumulatedText ? `${accumulatedText}\\n${sanitized}` : sanitized;\n const displayText = truncate(accumulatedText, MAX_LENGTH, truncationNote);\n await sendOrUpdate(displayText);\n if (messageId !== null) {\n bot.logBotResponse(event.conversationId, text, String(messageId));\n }\n } catch (err) {\n await notifyError(bot, chatId, \"respond\", err);\n }\n });\n await updatePromise;\n },\n\n replaceResponse: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n accumulatedText = truncate(sanitizeTelegramHtml(text), MAX_LENGTH, truncationNote);\n await sendOrUpdate(accumulatedText);\n } catch (err) {\n await notifyError(bot, chatId, \"replaceResponse\", err);\n }\n });\n await updatePromise;\n },\n\n // Telegram has no threads — discard thread-only messages (e.g. usage summary)\n respondInThread: async (_text: string) => {},\n\n setTyping: async (isTyping: boolean) => {\n if (isTyping && typingInterval === null) {\n // Send immediately and repeat every 4s (Telegram clears indicator after ~5s)\n bot.sendTyping(chatId).catch(() => {});\n typingInterval = setInterval(() => {\n bot.sendTyping(chatId).catch(() => {});\n }, 4000);\n } else if (!isTyping) {\n stopTyping();\n }\n },\n\n setWorking: async (working: boolean) => {\n if (!working) stopTyping();\n },\n\n uploadFile: async (filePath: string, title?: string) => {\n await bot.uploadFile(event.conversationId, filePath, title);\n },\n\n deleteResponse: async () => {\n updatePromise = updatePromise.then(async () => {\n if (messageId !== null) {\n try {\n await bot.deleteMessageRaw(chatId, messageId);\n } catch {\n // Ignore errors\n }\n messageId = null;\n }\n });\n await updatePromise;\n },\n };\n\n return { message, responseCtx, platform };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/telegram/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ChatMessage,\n ChatResponseContext,\n ChatToolResult,\n PlatformInfo,\n} from \"../../adapter.js\";\nimport * as log from \"../../log.js\";\nimport { formatToolArgs, splitText } from \"../shared.js\";\nimport { sanitizeTelegramHtml } from \"./html.js\";\nimport type { TelegramBot, TelegramEvent } from \"./bot.js\";\n\nexport const TELEGRAM_FORMATTING_GUIDE = `## Telegram Formatting (HTML mode)\nBold: <b>text</b>, Italic: <i>text</i>, Code: <code>code</code>, Pre: <pre>code</pre>\nLinks: <a href=\"url\">text</a>\nDo NOT use Markdown asterisks or backtick syntax.\nDo NOT use <table> tags — they are unsupported. Use <pre> with ASCII art for tables instead.`;\n\n// Telegram message length limit is 4096 chars; 3800 leaves headroom for HTML escapes.\nconst MAX_LENGTH = 3800;\n\nconst formatTelegramContinuation = (partNum: number): string => `(continued ${partNum})`;\n\nasync function notifyError(\n bot: TelegramBot,\n chatId: number,\n label: string,\n err: unknown,\n): Promise<void> {\n const errMsg = err instanceof Error ? err.message : String(err);\n log.logWarning(`Telegram ${label} error`, errMsg);\n try {\n await bot.postPlainMessage(chatId, `⚠️ 發送失敗:${errMsg}`);\n } catch {\n // ignore secondary failure\n }\n}\n\nfunction formatToolResult(result: ChatToolResult): string {\n const argsFormatted = formatToolArgs(result.args);\n const duration = (result.durationMs / 1000).toFixed(1);\n const title = `${result.isError ? \"Error\" : \"Done\"} ${result.toolName}${result.label ? `: ${result.label}` : \"\"} (${duration}s)`;\n return [title, argsFormatted, result.result].filter(Boolean).join(\"\\n\\n\");\n}\n\nexport function createTelegramAdapters(\n event: TelegramEvent,\n bot: TelegramBot,\n _isEvent?: boolean,\n): {\n message: ChatMessage;\n responseCtx: ChatResponseContext;\n platform: PlatformInfo;\n} {\n let messageId: number | null = null;\n let accumulatedText = \"\";\n let updatePromise = Promise.resolve();\n let typingInterval: ReturnType<typeof setInterval> | null = null;\n\n function stopTyping() {\n if (typingInterval !== null) {\n clearInterval(typingInterval);\n typingInterval = null;\n }\n }\n\n const conversationId = event.conversationId;\n const chatId = parseInt(conversationId);\n const replyToId = event.thread_ts ? parseInt(event.thread_ts) : null;\n\n const message: ChatMessage = {\n id: event.ts,\n sessionKey: event.sessionKey ?? `${conversationId}:${event.thread_ts ?? event.ts}`,\n conversationKind: event.conversationKind,\n userId: event.user,\n userName: event.userName,\n text: event.text,\n attachments: event.attachments,\n threadTs: event.thread_ts,\n };\n\n const platform: PlatformInfo = {\n name: \"telegram\",\n formattingGuide: TELEGRAM_FORMATTING_GUIDE,\n channels: [],\n users: [],\n };\n\n async function sendContinuation(text: string): Promise<void> {\n await bot.postMessageRaw(chatId, text);\n }\n\n async function sendOrUpdate(displayText: string): Promise<void> {\n if (messageId !== null) {\n await bot.updateMessage(conversationId, String(messageId), displayText);\n } else if (replyToId !== null) {\n messageId = await bot.postReply(chatId, replyToId, displayText);\n } else {\n messageId = await bot.postMessageRaw(chatId, displayText);\n }\n }\n\n const responseCtx: ChatResponseContext = {\n respond: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n const sanitized = sanitizeTelegramHtml(text);\n accumulatedText = accumulatedText ? `${accumulatedText}\\n${sanitized}` : sanitized;\n const [firstPart, ...extraParts] = splitText(\n accumulatedText,\n MAX_LENGTH,\n formatTelegramContinuation,\n );\n await sendOrUpdate(firstPart);\n for (const part of extraParts) {\n await sendContinuation(part);\n }\n if (messageId !== null) {\n bot.logBotResponse(conversationId, text, String(messageId));\n }\n } catch (err) {\n await notifyError(bot, chatId, \"respond\", err);\n }\n });\n await updatePromise;\n },\n\n replaceResponse: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n accumulatedText = sanitizeTelegramHtml(text);\n const [firstPart, ...extraParts] = splitText(\n accumulatedText,\n MAX_LENGTH,\n formatTelegramContinuation,\n );\n await sendOrUpdate(firstPart);\n for (const part of extraParts) {\n await sendContinuation(part);\n }\n } catch (err) {\n await notifyError(bot, chatId, \"replaceResponse\", err);\n }\n });\n await updatePromise;\n },\n\n respondDiagnostic: async (text: string, options?: { style?: \"muted\" | \"error\" }) => {\n updatePromise = updatePromise.then(async () => {\n try {\n const prefix = options?.style === \"error\" ? \"Error: \" : \"\";\n for (const part of splitText(\n sanitizeTelegramHtml(`${prefix}${text}`),\n MAX_LENGTH,\n formatTelegramContinuation,\n )) {\n await sendContinuation(part);\n }\n } catch (err) {\n await notifyError(bot, chatId, \"respondDiagnostic\", err);\n }\n });\n await updatePromise;\n },\n\n respondToolResult: async (result: ChatToolResult) => {\n await responseCtx.respondDiagnostic(formatToolResult(result));\n },\n\n setTyping: async (isTyping: boolean) => {\n if (isTyping && typingInterval === null) {\n // Send immediately and repeat every 4s (Telegram clears indicator after ~5s)\n bot.sendTyping(chatId).catch(() => {});\n typingInterval = setInterval(() => {\n bot.sendTyping(chatId).catch(() => {});\n }, 4000);\n } else if (!isTyping) {\n stopTyping();\n }\n },\n\n setWorking: async (working: boolean) => {\n if (!working) stopTyping();\n },\n\n uploadFile: async (filePath: string, title?: string) => {\n await bot.uploadFile(conversationId, filePath, title);\n },\n\n deleteResponse: async () => {\n updatePromise = updatePromise.then(async () => {\n if (messageId !== null) {\n try {\n await bot.deleteMessageRaw(chatId, messageId);\n } catch {\n // Ignore errors\n }\n messageId = null;\n }\n });\n await updatePromise;\n },\n };\n\n return { message, responseCtx, platform };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../src/adapters/telegram/html.ts"],"names":[],"mappings":"AA+CA,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKvD;AAkCD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgBzD","sourcesContent":["function htmlTableToText(tableHtml: string): string {\n const rows: string[][] = [];\n for (const rowMatch of tableHtml.matchAll(/<tr[^>]*>([\\s\\S]*?)<\\/tr>/gi)) {\n const cells: string[] = [];\n for (const cellMatch of rowMatch[1].matchAll(/<t[dh][^>]*>([\\s\\S]*?)<\\/t[dh]>/gi)) {\n cells.push(cellMatch[1].replace(/<[^>]+>/g, \"\").trim());\n }\n if (cells.length > 0) rows.push(cells);\n }\n\n if (rows.length === 0) return \"\";\n\n const numCols = Math.max(...rows.map((r) => r.length));\n const colWidths: number[] = Array(numCols).fill(0);\n for (const row of rows) {\n for (let i = 0; i < row.length; i++) {\n colWidths[i] = Math.max(colWidths[i], (row[i] ?? \"\").length);\n }\n }\n\n const sep = \"+\" + colWidths.map((w) => \"-\".repeat(w + 2)).join(\"+\") + \"+\";\n const lines: string[] = [sep];\n for (let i = 0; i < rows.length; i++) {\n const cells = colWidths.map((w, j) => ` ${(rows[i][j] ?? \"\").padEnd(w)} `);\n lines.push(\"|\" + cells.join(\"|\") + \"|\");\n if (i === 0) lines.push(sep);\n }\n lines.push(sep);\n return lines.join(\"\\n\");\n}\n\nconst SIMPLE_TAG_ALIASES: Record<string, string> = {\n b: \"b\",\n strong: \"b\",\n i: \"i\",\n em: \"i\",\n u: \"u\",\n ins: \"u\",\n s: \"s\",\n strike: \"s\",\n del: \"s\",\n code: \"code\",\n pre: \"pre\",\n blockquote: \"blockquote\",\n \"tg-spoiler\": \"tg-spoiler\",\n};\n\nexport function escapeTelegramHtml(text: string): string {\n return text\n .replace(/&(?!(?:amp|lt|gt|quot|apos|#39|#\\d+|#x[\\da-f]+);)/gi, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\nfunction escapeTelegramAttribute(text: string): string {\n return escapeTelegramHtml(text).replace(/\"/g, \""\");\n}\n\nfunction sanitizeTelegramTag(tag: string): string {\n const trimmed = tag.trim();\n\n if (/^<br\\s*\\/?>$/i.test(trimmed)) return \"\\n\";\n if (/^<(p|div)\\s*>$/i.test(trimmed)) return \"\";\n if (/^<\\/(p|div)\\s*>$/i.test(trimmed)) return \"\\n\";\n\n const match = trimmed.match(/^<\\s*(\\/?)\\s*([a-z0-9-]+)([^>]*)>$/i);\n if (!match) return escapeTelegramHtml(tag);\n\n const [, closing, rawName, rawAttrs] = match;\n const name = rawName.toLowerCase();\n const aliasedName = SIMPLE_TAG_ALIASES[name];\n\n if (aliasedName) {\n return `<${closing}${aliasedName}>`;\n }\n\n if (name === \"a\") {\n if (closing) return \"</a>\";\n const hrefMatch = rawAttrs.match(/\\bhref\\s*=\\s*([\"'])(.*?)\\1/i);\n if (!hrefMatch) return escapeTelegramHtml(tag);\n return `<a href=\"${escapeTelegramAttribute(hrefMatch[2])}\">`;\n }\n\n return escapeTelegramHtml(tag);\n}\n\nexport function sanitizeTelegramHtml(text: string): string {\n const withAsciiTables = text.replace(/<table[\\s\\S]*?<\\/table>/gi, (tableHtml) => {\n const ascii = htmlTableToText(tableHtml);\n return ascii ? `<pre>${ascii}</pre>` : \"\";\n });\n\n return withAsciiTables\n .split(/(<[^>]+>)/g)\n .filter(Boolean)\n .map((segment) => {\n if (segment.startsWith(\"<\") && segment.endsWith(\">\")) {\n return sanitizeTelegramTag(segment);\n }\n return escapeTelegramHtml(segment);\n })\n .join(\"\");\n}\n"]}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
function htmlTableToText(tableHtml) {
|
|
2
|
+
const rows = [];
|
|
3
|
+
for (const rowMatch of tableHtml.matchAll(/<tr[^>]*>([\s\S]*?)<\/tr>/gi)) {
|
|
4
|
+
const cells = [];
|
|
5
|
+
for (const cellMatch of rowMatch[1].matchAll(/<t[dh][^>]*>([\s\S]*?)<\/t[dh]>/gi)) {
|
|
6
|
+
cells.push(cellMatch[1].replace(/<[^>]+>/g, "").trim());
|
|
7
|
+
}
|
|
8
|
+
if (cells.length > 0)
|
|
9
|
+
rows.push(cells);
|
|
10
|
+
}
|
|
11
|
+
if (rows.length === 0)
|
|
12
|
+
return "";
|
|
13
|
+
const numCols = Math.max(...rows.map((r) => r.length));
|
|
14
|
+
const colWidths = Array(numCols).fill(0);
|
|
15
|
+
for (const row of rows) {
|
|
16
|
+
for (let i = 0; i < row.length; i++) {
|
|
17
|
+
colWidths[i] = Math.max(colWidths[i], (row[i] ?? "").length);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const sep = "+" + colWidths.map((w) => "-".repeat(w + 2)).join("+") + "+";
|
|
21
|
+
const lines = [sep];
|
|
22
|
+
for (let i = 0; i < rows.length; i++) {
|
|
23
|
+
const cells = colWidths.map((w, j) => ` ${(rows[i][j] ?? "").padEnd(w)} `);
|
|
24
|
+
lines.push("|" + cells.join("|") + "|");
|
|
25
|
+
if (i === 0)
|
|
26
|
+
lines.push(sep);
|
|
27
|
+
}
|
|
28
|
+
lines.push(sep);
|
|
29
|
+
return lines.join("\n");
|
|
30
|
+
}
|
|
31
|
+
const SIMPLE_TAG_ALIASES = {
|
|
32
|
+
b: "b",
|
|
33
|
+
strong: "b",
|
|
34
|
+
i: "i",
|
|
35
|
+
em: "i",
|
|
36
|
+
u: "u",
|
|
37
|
+
ins: "u",
|
|
38
|
+
s: "s",
|
|
39
|
+
strike: "s",
|
|
40
|
+
del: "s",
|
|
41
|
+
code: "code",
|
|
42
|
+
pre: "pre",
|
|
43
|
+
blockquote: "blockquote",
|
|
44
|
+
"tg-spoiler": "tg-spoiler",
|
|
45
|
+
};
|
|
46
|
+
export function escapeTelegramHtml(text) {
|
|
47
|
+
return text
|
|
48
|
+
.replace(/&(?!(?:amp|lt|gt|quot|apos|#39|#\d+|#x[\da-f]+);)/gi, "&")
|
|
49
|
+
.replace(/</g, "<")
|
|
50
|
+
.replace(/>/g, ">");
|
|
51
|
+
}
|
|
52
|
+
function escapeTelegramAttribute(text) {
|
|
53
|
+
return escapeTelegramHtml(text).replace(/"/g, """);
|
|
54
|
+
}
|
|
55
|
+
function sanitizeTelegramTag(tag) {
|
|
56
|
+
const trimmed = tag.trim();
|
|
57
|
+
if (/^<br\s*\/?>$/i.test(trimmed))
|
|
58
|
+
return "\n";
|
|
59
|
+
if (/^<(p|div)\s*>$/i.test(trimmed))
|
|
60
|
+
return "";
|
|
61
|
+
if (/^<\/(p|div)\s*>$/i.test(trimmed))
|
|
62
|
+
return "\n";
|
|
63
|
+
const match = trimmed.match(/^<\s*(\/?)\s*([a-z0-9-]+)([^>]*)>$/i);
|
|
64
|
+
if (!match)
|
|
65
|
+
return escapeTelegramHtml(tag);
|
|
66
|
+
const [, closing, rawName, rawAttrs] = match;
|
|
67
|
+
const name = rawName.toLowerCase();
|
|
68
|
+
const aliasedName = SIMPLE_TAG_ALIASES[name];
|
|
69
|
+
if (aliasedName) {
|
|
70
|
+
return `<${closing}${aliasedName}>`;
|
|
71
|
+
}
|
|
72
|
+
if (name === "a") {
|
|
73
|
+
if (closing)
|
|
74
|
+
return "</a>";
|
|
75
|
+
const hrefMatch = rawAttrs.match(/\bhref\s*=\s*(["'])(.*?)\1/i);
|
|
76
|
+
if (!hrefMatch)
|
|
77
|
+
return escapeTelegramHtml(tag);
|
|
78
|
+
return `<a href="${escapeTelegramAttribute(hrefMatch[2])}">`;
|
|
79
|
+
}
|
|
80
|
+
return escapeTelegramHtml(tag);
|
|
81
|
+
}
|
|
82
|
+
export function sanitizeTelegramHtml(text) {
|
|
83
|
+
const withAsciiTables = text.replace(/<table[\s\S]*?<\/table>/gi, (tableHtml) => {
|
|
84
|
+
const ascii = htmlTableToText(tableHtml);
|
|
85
|
+
return ascii ? `<pre>${ascii}</pre>` : "";
|
|
86
|
+
});
|
|
87
|
+
return withAsciiTables
|
|
88
|
+
.split(/(<[^>]+>)/g)
|
|
89
|
+
.filter(Boolean)
|
|
90
|
+
.map((segment) => {
|
|
91
|
+
if (segment.startsWith("<") && segment.endsWith(">")) {
|
|
92
|
+
return sanitizeTelegramTag(segment);
|
|
93
|
+
}
|
|
94
|
+
return escapeTelegramHtml(segment);
|
|
95
|
+
})
|
|
96
|
+
.join("");
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.js","sourceRoot":"","sources":["../../../src/adapters/telegram/html.ts"],"names":[],"mappings":"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","sourcesContent":["function htmlTableToText(tableHtml: string): string {\n const rows: string[][] = [];\n for (const rowMatch of tableHtml.matchAll(/<tr[^>]*>([\\s\\S]*?)<\\/tr>/gi)) {\n const cells: string[] = [];\n for (const cellMatch of rowMatch[1].matchAll(/<t[dh][^>]*>([\\s\\S]*?)<\\/t[dh]>/gi)) {\n cells.push(cellMatch[1].replace(/<[^>]+>/g, \"\").trim());\n }\n if (cells.length > 0) rows.push(cells);\n }\n\n if (rows.length === 0) return \"\";\n\n const numCols = Math.max(...rows.map((r) => r.length));\n const colWidths: number[] = Array(numCols).fill(0);\n for (const row of rows) {\n for (let i = 0; i < row.length; i++) {\n colWidths[i] = Math.max(colWidths[i], (row[i] ?? \"\").length);\n }\n }\n\n const sep = \"+\" + colWidths.map((w) => \"-\".repeat(w + 2)).join(\"+\") + \"+\";\n const lines: string[] = [sep];\n for (let i = 0; i < rows.length; i++) {\n const cells = colWidths.map((w, j) => ` ${(rows[i][j] ?? \"\").padEnd(w)} `);\n lines.push(\"|\" + cells.join(\"|\") + \"|\");\n if (i === 0) lines.push(sep);\n }\n lines.push(sep);\n return lines.join(\"\\n\");\n}\n\nconst SIMPLE_TAG_ALIASES: Record<string, string> = {\n b: \"b\",\n strong: \"b\",\n i: \"i\",\n em: \"i\",\n u: \"u\",\n ins: \"u\",\n s: \"s\",\n strike: \"s\",\n del: \"s\",\n code: \"code\",\n pre: \"pre\",\n blockquote: \"blockquote\",\n \"tg-spoiler\": \"tg-spoiler\",\n};\n\nexport function escapeTelegramHtml(text: string): string {\n return text\n .replace(/&(?!(?:amp|lt|gt|quot|apos|#39|#\\d+|#x[\\da-f]+);)/gi, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\nfunction escapeTelegramAttribute(text: string): string {\n return escapeTelegramHtml(text).replace(/\"/g, \""\");\n}\n\nfunction sanitizeTelegramTag(tag: string): string {\n const trimmed = tag.trim();\n\n if (/^<br\\s*\\/?>$/i.test(trimmed)) return \"\\n\";\n if (/^<(p|div)\\s*>$/i.test(trimmed)) return \"\";\n if (/^<\\/(p|div)\\s*>$/i.test(trimmed)) return \"\\n\";\n\n const match = trimmed.match(/^<\\s*(\\/?)\\s*([a-z0-9-]+)([^>]*)>$/i);\n if (!match) return escapeTelegramHtml(tag);\n\n const [, closing, rawName, rawAttrs] = match;\n const name = rawName.toLowerCase();\n const aliasedName = SIMPLE_TAG_ALIASES[name];\n\n if (aliasedName) {\n return `<${closing}${aliasedName}>`;\n }\n\n if (name === \"a\") {\n if (closing) return \"</a>\";\n const hrefMatch = rawAttrs.match(/\\bhref\\s*=\\s*([\"'])(.*?)\\1/i);\n if (!hrefMatch) return escapeTelegramHtml(tag);\n return `<a href=\"${escapeTelegramAttribute(hrefMatch[2])}\">`;\n }\n\n return escapeTelegramHtml(tag);\n}\n\nexport function sanitizeTelegramHtml(text: string): string {\n const withAsciiTables = text.replace(/<table[\\s\\S]*?<\\/table>/gi, (tableHtml) => {\n const ascii = htmlTableToText(tableHtml);\n return ascii ? `<pre>${ascii}</pre>` : \"\";\n });\n\n return withAsciiTables\n .split(/(<[^>]+>)/g)\n .filter(Boolean)\n .map((segment) => {\n if (segment.startsWith(\"<\") && segment.endsWith(\">\")) {\n return sanitizeTelegramTag(segment);\n }\n return escapeTelegramHtml(segment);\n })\n .join(\"\");\n}\n"]}
|
package/dist/agent.d.ts
CHANGED
|
@@ -3,14 +3,7 @@ import type { UserBindingStore } from "./bindings.js";
|
|
|
3
3
|
import type { DockerContainerManager } from "./provisioner.js";
|
|
4
4
|
import { type SandboxConfig } from "./sandbox.js";
|
|
5
5
|
import type { VaultManager } from "./vault.js";
|
|
6
|
-
|
|
7
|
-
userName: string;
|
|
8
|
-
text: string;
|
|
9
|
-
attachments: {
|
|
10
|
-
localPath: string;
|
|
11
|
-
}[];
|
|
12
|
-
timestamp: number;
|
|
13
|
-
}
|
|
6
|
+
import { type ResolvedSessionScope } from "./session-store.js";
|
|
14
7
|
export interface AgentRunner {
|
|
15
8
|
run(message: ChatMessage, responseCtx: ChatResponseContext, platform: PlatformInfo): Promise<{
|
|
16
9
|
stopReason: string;
|
|
@@ -24,11 +17,11 @@ export interface AgentRunner {
|
|
|
24
17
|
} | undefined;
|
|
25
18
|
}
|
|
26
19
|
/**
|
|
27
|
-
* Create a new AgentRunner for a
|
|
20
|
+
* Create a new AgentRunner for a channel.
|
|
28
21
|
* Sets up the session and subscribes to events once.
|
|
29
22
|
*
|
|
30
|
-
* Runner caching is handled by the caller (
|
|
23
|
+
* Runner caching is handled by the caller (channelStates in main.ts).
|
|
31
24
|
* This is a stateless factory function.
|
|
32
25
|
*/
|
|
33
|
-
export declare function createRunner(sandboxConfig: SandboxConfig, sessionKey: string, conversationId: string, conversationDir: string, workspaceDir: string, vaultManager?: VaultManager, bindingStore?: UserBindingStore, provisioner?: DockerContainerManager
|
|
26
|
+
export declare function createRunner(sandboxConfig: SandboxConfig, sessionKey: string, conversationId: string, conversationDir: string, workspaceDir: string, sessionScope: ResolvedSessionScope, vaultManager?: VaultManager, bindingStore?: UserBindingStore, provisioner?: DockerContainerManager): Promise<AgentRunner>;
|
|
34
27
|
//# sourceMappingURL=agent.d.ts.map
|