@segmentstream/agent 0.6.12 → 0.6.13
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/_chunks/{inbound-request-DgyI7oR9.mjs → inbound-request-CysnjMdw.mjs} +51 -59
- package/dist/extension/instructions.md +8 -1
- package/dist/extension/lib/chat-post-confirmation.mjs +1 -1
- package/dist/extension/lib/confirmation-description.d.ts +1 -0
- package/dist/extension/lib/confirmation-description.mjs +29 -13
- package/dist/extension/lib/confirmations.d.ts +3 -0
- package/dist/extension/lib/confirmations.mjs +22 -5
- package/dist/extension/lib/customer-text-safety.d.ts +1 -0
- package/dist/extension/lib/customer-text-safety.mjs +23 -0
- package/dist/extension/lib/ontology-tools.mjs +13 -6
- package/dist/extension/lib/telegram/adapter.mjs +5 -5
- package/dist/extension/lib/telegram/attachments-send.mjs +6 -6
- package/dist/extension/lib/telegram/chat-post-card.d.ts +4 -12
- package/dist/extension/lib/telegram/chat-post-card.mjs +15 -36
- package/dist/extension/lib/telegram/confirmation-text.d.ts +19 -8
- package/dist/extension/lib/telegram/confirmation-text.mjs +55 -65
- package/dist/extension/lib/telegram/confirmations.d.ts +1 -2
- package/dist/extension/lib/telegram/confirmations.mjs +36 -94
- package/dist/extension/lib/telegram/conversations.mjs +1 -1
- package/dist/extension/lib/telegram/customer-text.d.ts +3 -4
- package/dist/extension/lib/telegram/customer-text.mjs +2 -1
- package/dist/extension/lib/telegram/files.mjs +1 -1
- package/dist/extension/lib/telegram/images.mjs +1 -1
- package/dist/extension/lib/telegram/inbound-request.mjs +1 -1
- package/dist/extension/lib/telegram/inbound.mjs +5 -5
- package/dist/extension/lib/telegram/media-policy.mjs +1 -1
- package/dist/extension/lib/telegram/principal.mjs +1 -1
- package/dist/extension/lib/telegram/silent.mjs +2 -2
- package/dist/extension/lib/telegram/turn-errors.mjs +2 -2
- package/dist/extension/lib/telegram/voice.mjs +1 -1
- package/package.json +2 -2
|
@@ -4,6 +4,8 @@ import { createRequire as __eveCreateRequire } from "node:module";
|
|
|
4
4
|
const __filename = __eveFileURLToPath(import.meta.url);
|
|
5
5
|
__eveDirname(__filename);
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
|
+
import { isTechnicalText } from "../extension/lib/customer-text-safety.mjs";
|
|
8
|
+
import { isConfirmationText } from "../extension/lib/telegram/confirmation-text.mjs";
|
|
7
9
|
import { chatPostCardLinks, isExactChatPostCard } from "../extension/lib/telegram/chat-post-card.mjs";
|
|
8
10
|
import { redactDiagnostic } from "../extension/lib/diagnostics.mjs";
|
|
9
11
|
import { plainTelegramText, splitTelegramText, telegramMarkdown } from "../extension/lib/telegram/format.mjs";
|
|
@@ -24,43 +26,17 @@ function currentLocale() {
|
|
|
24
26
|
function currentThreadId() {
|
|
25
27
|
return deliveryLocale.getStore()?.threadId ?? currentInboundRequest()?.threadId;
|
|
26
28
|
}
|
|
27
|
-
function customerCopy(
|
|
28
|
-
return
|
|
29
|
-
failed: "
|
|
30
|
-
recovery: "
|
|
31
|
-
attachment: "
|
|
32
|
-
authorization: "
|
|
33
|
-
response: "
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
disconnected: "Подключение отменено."
|
|
37
|
-
} : {
|
|
38
|
-
failed: "I couldn't complete the request. Please try again.",
|
|
39
|
-
recovery: "I couldn't continue the conversation. Please send a new message.",
|
|
40
|
-
attachment: "I couldn't process the file. Please try again.",
|
|
41
|
-
authorization: "I couldn't connect. Please try again.",
|
|
42
|
-
response: "I couldn't display the response. Please try again.",
|
|
43
|
-
reference: "Error reference",
|
|
44
|
-
connected: "Access connected.",
|
|
45
|
-
disconnected: "Connection cancelled."
|
|
29
|
+
function customerCopy() {
|
|
30
|
+
return {
|
|
31
|
+
failed: "⚠️",
|
|
32
|
+
recovery: "⚠️",
|
|
33
|
+
attachment: "⚠️",
|
|
34
|
+
authorization: "⚠️",
|
|
35
|
+
response: "⚠️",
|
|
36
|
+
connected: "✅",
|
|
37
|
+
disconnected: "❌"
|
|
46
38
|
};
|
|
47
39
|
}
|
|
48
|
-
const TECHNICAL_PATTERNS = [
|
|
49
|
-
/[{}]|```/u,
|
|
50
|
-
/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/iu,
|
|
51
|
-
/\b[0-9a-hjkmnp-tv-z]{26}\b/iu,
|
|
52
|
-
/\b\w+__\w+/u,
|
|
53
|
-
/\b(?:Serialization|Type|Reference|Syntax|Tool|Provider|API|Workflow|InternalServer)Error\b/u,
|
|
54
|
-
/\bError:\s|(?:^|\n)\s*at\s+\S+|\b(?:stack trace|traceback|SQLSTATE|node_modules)\b/imu,
|
|
55
|
-
/\b(?:duplicate key|ECONN\w+|HTTP [45]\d\d)\b/iu,
|
|
56
|
-
/\[\s*(?:"[^"]*"|\d+|true|false|null)\s*[,\]]/u
|
|
57
|
-
];
|
|
58
|
-
function isTechnicalText(text) {
|
|
59
|
-
if (/https?:\/\/[^\s/@]+:[^\s/@]+@|[?&](?:token|access_token|api_key|secret|password)=/iu.test(text)) return true;
|
|
60
|
-
const visible = text.replace(/https?:\/\/[^\s)<>]+/giu, "link");
|
|
61
|
-
const prose = visible.replace(/(?<![\w@])@[a-z][a-z0-9_]{4,31}(?!\w)/giu, "recipient");
|
|
62
|
-
return TECHNICAL_PATTERNS.some((pattern) => pattern.test(visible)) || /\b[a-z][a-z0-9]*_[a-z][a-z0-9_]*\b(?!\.[a-z0-9])/iu.test(prose);
|
|
63
|
-
}
|
|
64
40
|
function redactCredentials(value) {
|
|
65
41
|
if (typeof value === "string") return value.replace(/\bBearer [A-Za-z0-9_.~+/-]+=*/giu, "Bearer [redacted]").replace(/(https?:\/\/)[^/@\s]+:[^/@\s]+@/gu, "$1[redacted]@").replace(/([?&](?:token|access_token|api_key|secret|password)=)[^&#\s]*/giu, "$1[redacted]").replace(/("(?:token|api_key|password|secret)"\s*:\s*)"(?:\\.|[^"\\])*"/giu, "$1\"[redacted]\"");
|
|
66
42
|
if (Array.isArray(value)) return value.map(redactCredentials);
|
|
@@ -78,16 +54,18 @@ function reportTelegramError(operation, error, metadata = {}, secrets = new Set(
|
|
|
78
54
|
});
|
|
79
55
|
return errorId;
|
|
80
56
|
}
|
|
81
|
-
function failureText(errorId, kind = "failed",
|
|
57
|
+
function failureText(errorId, kind = "failed", _locale = currentLocale()) {
|
|
82
58
|
if (kind === "cancelled") return `⏹ ${errorId}`;
|
|
83
|
-
|
|
84
|
-
return `${copy[kind]} ${copy.reference}: ${errorId}.`;
|
|
59
|
+
return `${customerCopy()[kind]} ${errorId}`;
|
|
85
60
|
}
|
|
86
61
|
function customerText(text, locale = currentLocale()) {
|
|
87
62
|
if (!isTechnicalText(text)) return text;
|
|
88
63
|
return failureText(reportTelegramError("technical_text_blocked", { text }), "response", locale);
|
|
89
64
|
}
|
|
90
65
|
function customerMessage(text, locale = currentLocale()) {
|
|
66
|
+
return guardedCustomerMessage(text, locale);
|
|
67
|
+
}
|
|
68
|
+
function guardedCustomerMessage(text, locale, blocked) {
|
|
91
69
|
let message;
|
|
92
70
|
try {
|
|
93
71
|
message = telegramMarkdown(text);
|
|
@@ -100,14 +78,20 @@ function customerMessage(text, locale = currentLocale()) {
|
|
|
100
78
|
}
|
|
101
79
|
const links = message.entities.flatMap((entity) => entity.url === void 0 ? [] : [entity.url]);
|
|
102
80
|
const visible = customerText(message.text, locale);
|
|
103
|
-
if (visible !== message.text)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
81
|
+
if (visible !== message.text) {
|
|
82
|
+
blocked?.();
|
|
83
|
+
return {
|
|
84
|
+
text: visible,
|
|
85
|
+
entities: []
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (links.some(isTechnicalText)) {
|
|
89
|
+
blocked?.();
|
|
90
|
+
return {
|
|
91
|
+
text: failureText(reportTelegramError("unsafe_markdown_link", new Error("Credential-bearing link refused.")), "response", locale),
|
|
92
|
+
entities: []
|
|
93
|
+
};
|
|
94
|
+
}
|
|
111
95
|
return message;
|
|
112
96
|
}
|
|
113
97
|
async function formattingRefused(response) {
|
|
@@ -141,17 +125,22 @@ function safeTelegramFetch(fetchImpl, scope = "telegram", previews = {}) {
|
|
|
141
125
|
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
142
126
|
if (!methods.has(url.split("/").at(-1) ?? "") || typeof init?.body !== "string") return fetchImpl(input, init);
|
|
143
127
|
const body = JSON.parse(init.body);
|
|
128
|
+
let textBlocked = false;
|
|
144
129
|
for (const field of ["text", "caption"]) {
|
|
145
130
|
if (typeof body[field] === "string" && isExactChatPostCard(body[field])) body.parse_mode = "MarkdownV2";
|
|
146
131
|
if (typeof body[field] === "string" && !isExactChatPostCard(body[field])) {
|
|
147
132
|
const entityField = field === "text" ? "entities" : "caption_entities";
|
|
148
133
|
const existing = body[entityField];
|
|
149
|
-
const message = Array.isArray(existing) ? {
|
|
134
|
+
const message = Array.isArray(existing) || isConfirmationText(body[field]) ? {
|
|
150
135
|
text: body[field],
|
|
151
|
-
entities: existing
|
|
152
|
-
} :
|
|
136
|
+
entities: existing ?? []
|
|
137
|
+
} : guardedCustomerMessage(body[field], currentLocale(), () => {
|
|
138
|
+
textBlocked = true;
|
|
139
|
+
});
|
|
153
140
|
const guarded = customerText(message.text);
|
|
154
|
-
|
|
141
|
+
const unsafeLink = message.entities.some((entity) => typeof entity.url === "string" && isTechnicalText(entity.url));
|
|
142
|
+
textBlocked ||= guarded !== message.text || unsafeLink;
|
|
143
|
+
body[field] = unsafeLink ? failureText(reportTelegramError("unsafe_entity_link", new Error("Credential-bearing link refused.")), "response") : guarded;
|
|
155
144
|
if (!url.endsWith("/answerCallbackQuery")) body[entityField] = body[field] === message.text ? message.entities : [];
|
|
156
145
|
delete body.parse_mode;
|
|
157
146
|
}
|
|
@@ -161,14 +150,17 @@ function safeTelegramFetch(fetchImpl, scope = "telegram", previews = {}) {
|
|
|
161
150
|
...typeof item.caption === "string" ? { caption: customerText(item.caption) } : {}
|
|
162
151
|
}));
|
|
163
152
|
const markup = body.reply_markup;
|
|
164
|
-
if (Array.isArray(markup?.inline_keyboard))
|
|
165
|
-
if (typeof
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
153
|
+
if (Array.isArray(markup?.inline_keyboard)) {
|
|
154
|
+
if ((textBlocked || typeof body.text === "string" && body.text.length > 4096) && markup.inline_keyboard.some((row) => row.some((button) => typeof button.callback_data === "string" && button.callback_data.startsWith("ss-confirm:")))) throw new Error("Approval buttons require the complete, safe confirmation text.");
|
|
155
|
+
markup.inline_keyboard = markup.inline_keyboard.map((row) => row.map((button) => {
|
|
156
|
+
if (typeof button.text !== "string" || !isTechnicalText(button.text)) return button;
|
|
157
|
+
reportTelegramError("technical_button_label_blocked", { text: button.text });
|
|
158
|
+
return {
|
|
159
|
+
...button,
|
|
160
|
+
text: "➡️"
|
|
161
|
+
};
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
172
164
|
const send = async () => {
|
|
173
165
|
const parts = url.endsWith("/sendMessage") && typeof body.text === "string" && !isExactChatPostCard(body.text) ? splitTelegramText({
|
|
174
166
|
text: body.text,
|
|
@@ -330,4 +322,4 @@ function withReplacedText(source, request) {
|
|
|
330
322
|
}
|
|
331
323
|
};
|
|
332
324
|
}
|
|
333
|
-
export { TelegramDeliveryError as a, customerCopy as c, failureText as d, formattingRefused as f, withDeliveryLocale as
|
|
325
|
+
export { TelegramDeliveryError as a, customerCopy as c, failureText as d, formattingRefused as f, withDeliveryLocale as h, withReplacedText as i, customerMessage as l, safeTelegramFetch as m, currentInboundRequest as n, currentLocale as o, reportTelegramError as p, runInboundRequest as r, currentThreadId as s, InboundRequest as t, customerText as u };
|
|
@@ -26,6 +26,13 @@ talking to you, and you act with exactly their permissions.
|
|
|
26
26
|
- Destructive or irreversible actions, and legacy custom actions without safety labels, require
|
|
27
27
|
the tool boundary's Confirm / Cancel buttons. Call the action to start that flow; never ask for
|
|
28
28
|
approval in prose or treat a text acknowledgement as approval. Wait for the button outcome.
|
|
29
|
+
When the tool schema offers `_confirmation`, supply {question, confirm, cancel, done,
|
|
30
|
+
cancelled, failed, expired?, edit?} in the chat's language. Describe the action and key
|
|
31
|
+
parameters in a plain single-line question (at most 300 characters); labels are at most
|
|
32
|
+
24 characters. No IDs, tool names, snake_case, URLs, credentials, or markup. The platform
|
|
33
|
+
adds trusted facts and symbols. Omit this argument on older tools without schema support.
|
|
34
|
+
Leave decision symbols to the platform and use distinct confirm/cancel labels. If trusted
|
|
35
|
+
target titles are unavailable or the facts are too long, resolve the targets or narrow the call.
|
|
29
36
|
Cancel ends that call; retry it only after a new instruction from the member.
|
|
30
37
|
- Every change goes through an action. Report results after success; never claim a record changed
|
|
31
38
|
unless an action reported it.
|
|
@@ -36,7 +43,7 @@ talking to you, and you act with exactly their permissions.
|
|
|
36
43
|
Read `chat_history` or `chat_search` only when permitted, then compose the exact draft and call
|
|
37
44
|
`chat_post_message`. The runtime supplies the operation key. Its Confirm / Edit / Cancel card
|
|
38
45
|
shows the full attributed text in the requesting chat; only the requester can act on it and it
|
|
39
|
-
expires after ten minutes.
|
|
46
|
+
expires after ten minutes. A text acknowledgement cannot approve a post. After Edit, ask what
|
|
40
47
|
to change and make a new call/card for the revision; the cancelled call cannot be reused.
|
|
41
48
|
An uncertain delivery must never be automatically resent, including through a fresh call/key.
|
|
42
49
|
For a reply in the current chat, answer normally. If chat posting is unavailable, say so.
|
|
@@ -48,7 +48,7 @@ function describeChatPost(input) {
|
|
|
48
48
|
].every((value) => typeof value === "string" && value.length > 0) || target.provider !== "telegram" || target.id !== values.chat && target.title !== values.chat) throw new Error("An unambiguous destination and a complete exact chat preview are required.");
|
|
49
49
|
const footer = escapeMarkdownV2(caller.attribution);
|
|
50
50
|
return {
|
|
51
|
-
action: "
|
|
51
|
+
action: "❓",
|
|
52
52
|
fields: [],
|
|
53
53
|
chatPost: {
|
|
54
54
|
chatId: target.id,
|
|
@@ -9,6 +9,7 @@ export interface ConfirmationDescription {
|
|
|
9
9
|
readonly value: string;
|
|
10
10
|
}[];
|
|
11
11
|
readonly chatPost?: ChatPostPreview;
|
|
12
|
+
readonly warning?: boolean;
|
|
12
13
|
}
|
|
13
14
|
export declare function object(value: unknown): Record<string, unknown>;
|
|
14
15
|
export declare function humanize(value: string): string;
|
|
@@ -4,6 +4,7 @@ import { createRequire as __eveCreateRequire } from "node:module";
|
|
|
4
4
|
const __filename = __eveFileURLToPath(import.meta.url);
|
|
5
5
|
__eveDirname(__filename);
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
|
+
import { isTechnicalText } from "./customer-text-safety.mjs";
|
|
7
8
|
function object(value) {
|
|
8
9
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
9
10
|
}
|
|
@@ -12,9 +13,20 @@ function humanize(value) {
|
|
|
12
13
|
}
|
|
13
14
|
function humanText(value, maximum = 180) {
|
|
14
15
|
if (typeof value !== "string") return;
|
|
15
|
-
const text = value.
|
|
16
|
-
if (text === "" ||
|
|
17
|
-
|
|
16
|
+
const text = value.trim();
|
|
17
|
+
if (text === "" || text.length > maximum || isTechnicalText(text) || [
|
|
18
|
+
/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}<>`*_~|\\\[\]{}]|^(?:#{1,6}|[-+])\s/u,
|
|
19
|
+
/[a-z][a-z0-9+.-]*:\/\/|www\.|\b[a-z][a-z0-9]*\.[a-z][a-z_0-9]*\b/iu,
|
|
20
|
+
/&(?:#\d+|#x[0-9a-f]+|[a-z]+);/iu,
|
|
21
|
+
/\bBearer\s+|\b(?:password|secret|token|credential|api[ _-]?key)\s*[:=]/iu,
|
|
22
|
+
/\b(?:sk|ssw|ghp)[_-][a-z0-9]+/iu,
|
|
23
|
+
/\b[0-9a-f]{8}-[0-9a-f-]{27,}\b|\b[0-9A-HJKMNP-TV-Z]{26}\b|\b[a-z0-9]{24,}\b/iu
|
|
24
|
+
].some((pattern) => pattern.test(text))) return;
|
|
25
|
+
return text;
|
|
26
|
+
}
|
|
27
|
+
function factText(value, maximum = 180) {
|
|
28
|
+
if (typeof value === "string" && value.trim().length > maximum) throw new Error("The complete confirmation facts cannot fit in one Telegram message.");
|
|
29
|
+
return humanText(value, maximum);
|
|
18
30
|
}
|
|
19
31
|
function fieldSchema(value, root, depth = 0) {
|
|
20
32
|
const schema = object(value);
|
|
@@ -33,46 +45,50 @@ async function describeConfirmation(input) {
|
|
|
33
45
|
const properties = Array.isArray(entity.properties) ? entity.properties.map(object) : [];
|
|
34
46
|
const fields = [];
|
|
35
47
|
const read = new Map();
|
|
36
|
-
for (const [key, value] of Object.entries(input.values)
|
|
48
|
+
for (const [key, value] of Object.entries(input.values)) {
|
|
37
49
|
const declared = object(input.schema.properties)[key];
|
|
38
|
-
if (declared === void 0 || /password|secret|token|credential|authorization/i.test(key)) continue;
|
|
50
|
+
if (declared === void 0 || /password|secret|token|credential|authorization|api.?key|private.?key|cookie/i.test(key)) continue;
|
|
39
51
|
const schema = fieldSchema(declared, input.schema);
|
|
40
52
|
const itemSchema = schema.type === "array" ? fieldSchema(schema.items, input.schema) : schema;
|
|
41
53
|
const property = properties.find((candidate) => candidate.name === key);
|
|
42
54
|
const reference = itemSchema["x-ontology-ref"];
|
|
43
55
|
const target = input.entities.find((candidate) => candidate.name === reference);
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
if (schema.writeOnly === true || schema.format === "password" || itemSchema.writeOnly === true || itemSchema.format === "password") continue;
|
|
57
|
+
const label = factText(schema.title) ?? factText(property?.label) ?? factText(humanize(key));
|
|
58
|
+
if (label === void 0) throw new Error("A confirmation field has no safe label.");
|
|
46
59
|
const options = Array.isArray(property?.options) ? property.options.map(object) : [];
|
|
47
60
|
const choices = [itemSchema.oneOf, itemSchema.anyOf].flatMap((items) => Array.isArray(items) ? items.map(object) : []);
|
|
48
61
|
const values = [];
|
|
49
|
-
for (const item of
|
|
62
|
+
for (const item of Array.isArray(value) ? value : [value]) {
|
|
50
63
|
if (typeof reference === "string") {
|
|
64
|
+
if (item === null || item === void 0) continue;
|
|
51
65
|
let title;
|
|
52
66
|
if (target !== void 0 && typeof item === "string" && typeof target.title === "string") {
|
|
53
67
|
const cacheKey = `${reference}:${item}`;
|
|
54
68
|
if (!read.has(cacheKey)) read.set(cacheKey, input.read(reference, item));
|
|
55
69
|
const value = (await read.get(cacheKey))?.[target.title];
|
|
56
|
-
title = value === item ? void 0 :
|
|
70
|
+
title = value === item ? void 0 : factText(value, 3584);
|
|
57
71
|
}
|
|
58
|
-
|
|
72
|
+
if (title === void 0) throw new Error("Every confirmation target requires an authorized, safe record title.");
|
|
73
|
+
values.push(title);
|
|
59
74
|
continue;
|
|
60
75
|
}
|
|
61
76
|
if (/^(id|.*_id)$/i.test(key) || itemSchema.format === "uuid" || typeof item === "object") continue;
|
|
62
77
|
const option = options.find((candidate) => candidate.value === item);
|
|
63
78
|
const choice = choices.find((candidate) => candidate.const === item);
|
|
64
79
|
const enumeration = Array.isArray(itemSchema.enum) || choices.length > 0 || options.length > 0;
|
|
65
|
-
const text =
|
|
80
|
+
const text = factText(option?.label) ?? factText(choice?.title) ?? (enumeration ? void 0 : typeof item === "string" ? factText(item, 3584) : typeof item === "number" || typeof item === "boolean" ? String(item) : void 0);
|
|
66
81
|
if (text !== void 0) values.push(text);
|
|
67
82
|
}
|
|
68
83
|
if (values.length > 0) fields.push({
|
|
69
84
|
label,
|
|
70
85
|
value: values.join(", ")
|
|
71
86
|
});
|
|
72
|
-
if (fields.length === 6) break;
|
|
73
87
|
}
|
|
88
|
+
const action = factText(input.title);
|
|
89
|
+
if (action === void 0) throw new Error("A confirmation requires a safe declared action title.");
|
|
74
90
|
return {
|
|
75
|
-
action
|
|
91
|
+
action,
|
|
76
92
|
...humanText(entity.label) === void 0 ? {} : { entity: humanText(entity.label) },
|
|
77
93
|
...humanText(input.description?.split("\n\n")[0]) === void 0 ? {} : { description: humanText(input.description?.split("\n\n")[0]) },
|
|
78
94
|
fields
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { type StateHandle } from "eve/context";
|
|
2
2
|
import type { ApprovalConfiguration } from "eve/tools/approval";
|
|
3
3
|
import type { ConfirmationDescription } from "./confirmation-description.js";
|
|
4
|
+
import { type ConfirmationCopy } from "./telegram/confirmation-text.js";
|
|
4
5
|
export declare const CONFIRMATION_TTL_MS: number;
|
|
5
6
|
export interface ActionAnnotations {
|
|
6
7
|
readonly readOnlyHint?: boolean;
|
|
8
|
+
readonly confirmation?: boolean;
|
|
7
9
|
readonly destructiveHint?: boolean;
|
|
8
10
|
readonly irreversibleHint?: boolean;
|
|
9
11
|
readonly idempotentHint?: boolean;
|
|
@@ -19,6 +21,7 @@ export interface Confirmation {
|
|
|
19
21
|
readonly expiresAt: number;
|
|
20
22
|
readonly status: "pending" | "approved" | "consumed" | "cancelled";
|
|
21
23
|
readonly description?: ConfirmationDescription;
|
|
24
|
+
readonly copy?: ConfirmationCopy;
|
|
22
25
|
readonly source?: {
|
|
23
26
|
readonly chatId: string;
|
|
24
27
|
readonly threadId: string;
|
|
@@ -6,6 +6,7 @@ __eveDirname(__filename);
|
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { memberIdOf } from "./telegram/principal.mjs";
|
|
8
8
|
import { chatPostDigest, isChatPost } from "./chat-post-confirmation.mjs";
|
|
9
|
+
import { confirmationCopy, renderConfirmation } from "./telegram/confirmation-text.mjs";
|
|
9
10
|
import { chatPostCard } from "./telegram/chat-post-card.mjs";
|
|
10
11
|
import { createHash } from "node:crypto";
|
|
11
12
|
import { defineState } from "eve/context";
|
|
@@ -49,15 +50,19 @@ function actionApproval(options) {
|
|
|
49
50
|
console.warn("The ontology action permission check failed.", error);
|
|
50
51
|
return denied("The action is unavailable or the member no longer has permission.");
|
|
51
52
|
}
|
|
53
|
+
const copy = labels.confirmation === false ? void 0 : confirmationCopy(ctx.toolInput?._confirmation);
|
|
54
|
+
const businessInput = Object.fromEntries(Object.entries(ctx.toolInput ?? {}).filter(([key]) => key !== "_confirmation"));
|
|
52
55
|
const posting = isChatPost(ctx.toolName);
|
|
53
56
|
let description;
|
|
54
57
|
if (posting) try {
|
|
55
|
-
description = await options.describe?.(memberId, ctx.toolName,
|
|
58
|
+
description = await options.describe?.(memberId, ctx.toolName, businessInput);
|
|
56
59
|
const preview = description?.chatPost;
|
|
57
60
|
const source = ctx.session.auth.current?.attributes;
|
|
58
|
-
if (preview === void 0 || typeof source?.chatId !== "string" || source.chatId === preview.externalId) return denied("A complete preview for a different chat is required. Reply normally in this chat.");
|
|
59
|
-
chatPostCard(
|
|
60
|
-
|
|
61
|
+
if (description === void 0 || preview === void 0 || typeof source?.chatId !== "string" || source.chatId === preview.externalId) return denied("A complete preview for a different chat is required. Reply normally in this chat.");
|
|
62
|
+
chatPostCard({
|
|
63
|
+
...description,
|
|
64
|
+
warning: labels.destructiveHint === true || labels.irreversibleHint === true
|
|
65
|
+
}, copy);
|
|
61
66
|
if (saved !== void 0 && (saved.description?.chatPost === void 0 || chatPostDigest(saved.description.chatPost) !== chatPostDigest(preview) || saved.source?.chatId !== source.chatId || saved.source.threadId !== String(source.messageThreadId ?? ""))) return denied("The destination or attribution changed. Make a new call and confirmation.");
|
|
62
67
|
} catch (error) {
|
|
63
68
|
console.warn("The exact chat confirmation could not be prepared.", error);
|
|
@@ -76,9 +81,20 @@ function actionApproval(options) {
|
|
|
76
81
|
if (saved !== void 0) return denied("The original call is waiting for its Confirm / Cancel buttons.");
|
|
77
82
|
if (!posting && !needsConfirmation(labels)) return "approved";
|
|
78
83
|
try {
|
|
79
|
-
if (!posting) description = await options.describe?.(memberId, ctx.toolName,
|
|
84
|
+
if (!posting) description = await options.describe?.(memberId, ctx.toolName, businessInput);
|
|
85
|
+
if (description === void 0) throw new Error("A trusted confirmation description is required.");
|
|
80
86
|
} catch (error) {
|
|
81
87
|
console.warn("The confirmation description could not be read.", error);
|
|
88
|
+
return denied("Trusted confirmation facts are incomplete, unsafe or too long. Resolve every target title and narrow the input before making a new call.");
|
|
89
|
+
}
|
|
90
|
+
description = description === void 0 ? void 0 : {
|
|
91
|
+
...description,
|
|
92
|
+
warning: labels.destructiveHint === true || labels.irreversibleHint === true
|
|
93
|
+
};
|
|
94
|
+
try {
|
|
95
|
+
if (!posting) renderConfirmation(description, copy);
|
|
96
|
+
} catch {
|
|
97
|
+
return denied("The complete confirmation facts are unsafe or too long. Narrow the action input.");
|
|
82
98
|
}
|
|
83
99
|
state.update((all) => ({
|
|
84
100
|
...all,
|
|
@@ -88,6 +104,7 @@ function actionApproval(options) {
|
|
|
88
104
|
inputHash: hash,
|
|
89
105
|
expiresAt: now() + CONFIRMATION_TTL_MS,
|
|
90
106
|
status: "pending",
|
|
107
|
+
...copy === void 0 ? {} : { copy },
|
|
91
108
|
...description === void 0 ? {} : { description },
|
|
92
109
|
...posting ? { source: {
|
|
93
110
|
chatId: String(ctx.session.auth.current?.attributes.chatId),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isTechnicalText(text: string): boolean;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
const __filename = __eveFileURLToPath(import.meta.url);
|
|
5
|
+
__eveDirname(__filename);
|
|
6
|
+
__eveCreateRequire(import.meta.url);
|
|
7
|
+
const TECHNICAL_PATTERNS = [
|
|
8
|
+
/[{}]|```/u,
|
|
9
|
+
/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/iu,
|
|
10
|
+
/\b[0-9a-hjkmnp-tv-z]{26}\b/iu,
|
|
11
|
+
/\b\w+__\w+/u,
|
|
12
|
+
/\b(?:Serialization|Type|Reference|Syntax|Tool|Provider|API|Workflow|InternalServer)Error\b/u,
|
|
13
|
+
/\bError:\s|(?:^|\n)\s*at\s+\S+|\b(?:stack trace|traceback|SQLSTATE|node_modules)\b/imu,
|
|
14
|
+
/\b(?:duplicate key|ECONN\w+|HTTP [45]\d\d)\b/iu,
|
|
15
|
+
/\[\s*(?:"[^"]*"|\d+|true|false|null)\s*[,\]]/u
|
|
16
|
+
];
|
|
17
|
+
function isTechnicalText(text) {
|
|
18
|
+
if (/https?:\/\/[^\s/@]+:[^\s/@]+@|[?&](?:token|access_token|api_key|secret|password)=/iu.test(text)) return true;
|
|
19
|
+
const visible = text.replace(/https?:\/\/[^\s)<>]+/giu, "link");
|
|
20
|
+
const prose = visible.replace(/(?<![\w@])@[a-z][a-z0-9_]{4,31}(?!\w)/giu, "recipient");
|
|
21
|
+
return TECHNICAL_PATTERNS.some((pattern) => pattern.test(visible)) || /\b[a-z][a-z0-9]*_[a-z][a-z0-9_]*\b(?!\.[a-z0-9])/iu.test(prose);
|
|
22
|
+
}
|
|
23
|
+
export { isTechnicalText };
|
|
@@ -59,7 +59,11 @@ function ontologyTools(options = {}) {
|
|
|
59
59
|
return Array.isArray(result.tools) && result.tools.map(object).some(supportsChatPost);
|
|
60
60
|
},
|
|
61
61
|
async labels(memberId, action) {
|
|
62
|
-
|
|
62
|
+
const { tool } = await toolFor(memberId, action);
|
|
63
|
+
return {
|
|
64
|
+
...object(tool.annotations),
|
|
65
|
+
confirmation: object(object(tool.inputSchema).properties)._confirmation !== void 0
|
|
66
|
+
};
|
|
63
67
|
},
|
|
64
68
|
async describe(memberId, action, input) {
|
|
65
69
|
const signal = AbortSignal.timeout(15e3);
|
|
@@ -76,11 +80,14 @@ function ontologyTools(options = {}) {
|
|
|
76
80
|
return object(result.structuredContent);
|
|
77
81
|
};
|
|
78
82
|
const [caller, destinations] = await Promise.all([read("me", {}), read("chat_list", { chat: input.chat })]);
|
|
79
|
-
return
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
return {
|
|
84
|
+
...describeChatPost({
|
|
85
|
+
values: input,
|
|
86
|
+
caller,
|
|
87
|
+
destinations
|
|
88
|
+
}),
|
|
89
|
+
action: typeof tool.title === "string" ? tool.title : "❓"
|
|
90
|
+
};
|
|
84
91
|
}
|
|
85
92
|
let entities = [];
|
|
86
93
|
try {
|
|
@@ -8,17 +8,17 @@ import { appOrigin, channelsEnabled, proxyEnvironment, runtimeIdentityEnvironmen
|
|
|
8
8
|
import { conversationLanguage, telegramPrincipal } from "./principal.mjs";
|
|
9
9
|
import { chooseModel } from "../../model.mjs";
|
|
10
10
|
import { MAXIMUM_DOWNLOAD_BYTES, connectionProxy } from "../connection-proxy.mjs";
|
|
11
|
+
import { confirmationLocale } from "./confirmation-text.mjs";
|
|
11
12
|
import { runtimeToken } from "../runtime-token.mjs";
|
|
12
13
|
import { agentAttachments, parseAttachmentUrl } from "../attachments.mjs";
|
|
13
14
|
import { agentIdentities } from "../identities.mjs";
|
|
14
15
|
import { conversations } from "../conversations.mjs";
|
|
15
16
|
import { observedMessages } from "./observed.mjs";
|
|
16
17
|
import { telegramSendScope } from "./send-queue.mjs";
|
|
17
|
-
import { d as failureText,
|
|
18
|
+
import { d as failureText, h as withDeliveryLocale, i as withReplacedText, m as safeTelegramFetch, n as currentInboundRequest, p as reportTelegramError, r as runInboundRequest, t as InboundRequest } from "../../../_chunks/inbound-request-CysnjMdw.mjs";
|
|
18
19
|
import { captureRouteDecider, captureTelegramFetch, conversationIdentities, recordGateDecision, telegramConversationCapture } from "./conversations.mjs";
|
|
19
20
|
import { runtimeTokenVerifier } from "../runtime-identity.mjs";
|
|
20
21
|
import { botIdentityCache } from "./bot.mjs";
|
|
21
|
-
import { confirmationLocale, confirmationRenderer } from "./confirmation-text.mjs";
|
|
22
22
|
import { scopedAddress, sessionPolicy } from "./sessions.mjs";
|
|
23
23
|
import { handleConfirmationCallback, telegramConfirmationEvents } from "./confirmations.mjs";
|
|
24
24
|
import { gatewayImageDescriber } from "./images.mjs";
|
|
@@ -237,16 +237,16 @@ function segmentstreamTelegram(options) {
|
|
|
237
237
|
scope: options.sessions().scope
|
|
238
238
|
});
|
|
239
239
|
} catch (error) {
|
|
240
|
-
|
|
240
|
+
reportTelegramError("confirmation_callback_failed", error);
|
|
241
241
|
await context.telegram.answerCallbackQuery({
|
|
242
242
|
callbackQueryId: query.id,
|
|
243
|
-
text:
|
|
243
|
+
text: "⚠️"
|
|
244
244
|
});
|
|
245
245
|
}
|
|
246
246
|
},
|
|
247
247
|
events: {
|
|
248
248
|
...telegramFailureEvents,
|
|
249
|
-
...telegramConfirmationEvents(
|
|
249
|
+
...telegramConfirmationEvents(archive),
|
|
250
250
|
"message.completed": replyDelivery(observed, () => telegramSendScope(environment, settings().connection))
|
|
251
251
|
}
|
|
252
252
|
});
|
|
@@ -7,13 +7,13 @@ __eveCreateRequire(import.meta.url);
|
|
|
7
7
|
import { appOrigin, channelsEnabled, proxyEnvironment } from "../environment.mjs";
|
|
8
8
|
import "./principal.mjs";
|
|
9
9
|
import { MAXIMUM_PROXY_BODY_BYTES, connectionProxy } from "../connection-proxy.mjs";
|
|
10
|
+
import { confirmationLocale } from "./confirmation-text.mjs";
|
|
10
11
|
import { redactDiagnostic } from "../diagnostics.mjs";
|
|
11
12
|
import { runtimeToken } from "../runtime-token.mjs";
|
|
12
13
|
import { ACT_AS_HEADER } from "../ontology-connection.mjs";
|
|
13
14
|
import { AttachmentError, CONVERSATION_HEADER, SESSION_HEADER } from "../attachments.mjs";
|
|
14
15
|
import { inTelegramSendQueue, telegramSendScope } from "./send-queue.mjs";
|
|
15
|
-
import { c as customerCopy, f as formattingRefused, l as customerMessage,
|
|
16
|
-
import { confirmationLocale } from "./confirmation-text.mjs";
|
|
16
|
+
import { c as customerCopy, f as formattingRefused, l as customerMessage, p as reportTelegramError } from "../../../_chunks/inbound-request-CysnjMdw.mjs";
|
|
17
17
|
import { TELEGRAM_PLACEHOLDER_TOKEN, TELEGRAM_PROXY_ORIGIN, telegramProxyFetch } from "./proxy-fetch.mjs";
|
|
18
18
|
import { attachmentOwner } from "../attachment-tools.mjs";
|
|
19
19
|
const MAXIMUM_PHOTO_BYTES = 10485760;
|
|
@@ -85,7 +85,7 @@ async function sendAttachmentsInOrder(options) {
|
|
|
85
85
|
const locale = confirmationLocale(principal);
|
|
86
86
|
const unavailable = (error, diagnostic) => ({
|
|
87
87
|
error,
|
|
88
|
-
detail: customerCopy(
|
|
88
|
+
detail: customerCopy().attachment,
|
|
89
89
|
reference: reportTelegramError("attachment_send_refused", {
|
|
90
90
|
error,
|
|
91
91
|
diagnostic
|
|
@@ -104,7 +104,7 @@ async function sendAttachmentsInOrder(options) {
|
|
|
104
104
|
const reference = reportTelegramError("attachment_owner_refused", error);
|
|
105
105
|
return {
|
|
106
106
|
error: "forbidden",
|
|
107
|
-
detail: customerCopy(
|
|
107
|
+
detail: customerCopy().attachment,
|
|
108
108
|
reference
|
|
109
109
|
};
|
|
110
110
|
}
|
|
@@ -187,7 +187,7 @@ async function sendAttachmentsInOrder(options) {
|
|
|
187
187
|
id: reference.id,
|
|
188
188
|
filename,
|
|
189
189
|
error: error instanceof AttachmentError ? error.code : "unavailable",
|
|
190
|
-
detail: customerCopy(
|
|
190
|
+
detail: customerCopy().attachment,
|
|
191
191
|
reference: errorId
|
|
192
192
|
};
|
|
193
193
|
}
|
|
@@ -271,7 +271,7 @@ async function sendAttachmentsInOrder(options) {
|
|
|
271
271
|
id: file.id,
|
|
272
272
|
filename: file.filename,
|
|
273
273
|
error: error instanceof AttachmentError ? error.code : "send_failed",
|
|
274
|
-
detail: customerCopy(
|
|
274
|
+
detail: customerCopy().attachment,
|
|
275
275
|
reference: errorId
|
|
276
276
|
};
|
|
277
277
|
});
|
|
@@ -1,18 +1,10 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type {
|
|
1
|
+
import { type ConfirmationCopy, type ConfirmationText } from "./confirmation-text.js";
|
|
2
|
+
import type { ConfirmationDescription } from "../confirmation-description.js";
|
|
3
3
|
/** Only the frozen deterministic card may bypass the generic technical-prose filter. */
|
|
4
4
|
export declare function withChatPostCard<T>(text: string, run: () => T, links?: readonly string[]): T;
|
|
5
5
|
export declare function isExactChatPostCard(text: string): boolean;
|
|
6
6
|
export declare function chatPostCardLinks(text: string): readonly string[] | undefined;
|
|
7
|
-
export declare function
|
|
8
|
-
edit: string;
|
|
9
|
-
changes: string;
|
|
10
|
-
uncertain: string;
|
|
11
|
-
sent: string;
|
|
12
|
-
history: string;
|
|
13
|
-
expired: string;
|
|
14
|
-
};
|
|
15
|
-
export declare function chatPostCard(preview: ChatPostPreview, locale: string): ConfirmationText;
|
|
7
|
+
export declare function chatPostCard(description: ConfirmationDescription, copy?: ConfirmationCopy): ConfirmationText;
|
|
16
8
|
/** Status/receipt strings are data outside the already rendered preview. */
|
|
17
9
|
export declare function chatPostCardStatus(text: string, status: string): string;
|
|
18
|
-
export declare function chatPostReceipt(output: unknown,
|
|
10
|
+
export declare function chatPostReceipt(output: unknown, completed?: string): string | undefined;
|