@segmentstream/agent 0.12.2 → 0.13.0
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 +29 -22
- package/dist/_chunks/{build-metadata-CLprTpsn.mjs → build-metadata-BLeBanNg.mjs} +1 -1
- package/dist/_chunks/{inbound-request-BrWspFw5.mjs → inbound-request-BXhiIph-.mjs} +21 -19
- package/dist/extension/_manifest.json +0 -1
- package/dist/extension/config.d.ts +18 -5
- package/dist/extension/config.mjs +138 -18
- package/dist/extension/extension.d.ts +7 -1
- package/dist/extension/hooks/authorization.d.ts +3 -0
- package/dist/extension/hooks/authorization.mjs +16 -0
- package/dist/extension/instructions.md +3 -4
- package/dist/extension/lib/build-metadata.mjs +1 -1
- package/dist/extension/lib/chat-post-confirmation.d.ts +1 -0
- package/dist/extension/lib/chat-post-confirmation.mjs +4 -2
- package/dist/extension/lib/confirmation-outcome.d.ts +1 -1
- package/dist/extension/lib/confirmations.d.ts +2 -0
- package/dist/extension/lib/confirmations.mjs +7 -4
- package/dist/extension/lib/deferred-confirmations.d.ts +3 -0
- package/dist/extension/lib/deferred-confirmations.mjs +2 -0
- package/dist/extension/lib/ontology-tools.mjs +1 -1
- package/dist/extension/lib/telegram/adapter.mjs +34 -7
- package/dist/extension/lib/telegram/attachment-description.mjs +1 -1
- package/dist/extension/lib/telegram/attachments-send.mjs +13 -10
- package/dist/extension/lib/telegram/chat-post-card.d.ts +1 -1
- package/dist/extension/lib/telegram/chat-post-card.mjs +13 -10
- package/dist/extension/lib/telegram/confirmation-expiry.d.ts +2 -1
- package/dist/extension/lib/telegram/confirmation-expiry.mjs +5 -4
- package/dist/extension/lib/telegram/confirmation-text.d.ts +1 -1
- package/dist/extension/lib/telegram/confirmation-text.mjs +18 -12
- package/dist/extension/lib/telegram/confirmations.mjs +24 -19
- package/dist/extension/lib/telegram/conversations.d.ts +2 -0
- package/dist/extension/lib/telegram/conversations.mjs +14 -1
- package/dist/extension/lib/telegram/customer-text.d.ts +9 -8
- package/dist/extension/lib/telegram/customer-text.mjs +1 -1
- package/dist/extension/lib/telegram/deferred-confirmations.mjs +10 -9
- package/dist/extension/lib/telegram/files.mjs +1 -1
- package/dist/extension/lib/telegram/health.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 +18 -17
- package/dist/extension/lib/telegram/lifecycle.mjs +2 -2
- package/dist/extension/lib/telegram/media-policy.mjs +1 -1
- package/dist/extension/lib/telegram/silent.mjs +1 -1
- package/dist/extension/lib/telegram/texts.d.ts +16 -0
- package/dist/extension/lib/telegram/texts.mjs +70 -0
- package/dist/extension/lib/telegram/turn-errors.d.ts +1 -1
- package/dist/extension/lib/telegram/turn-errors.mjs +37 -3
- package/dist/extension/lib/telegram/voice.mjs +1 -1
- package/dist/extension/schedules/confirmations.mjs +1 -1
- package/dist/extension/tools/attachments_promote.mjs +2 -0
- package/dist/extension/tools/ontology.mjs +1 -1
- package/package.json +2 -2
- package/dist/extension/connections/ontology.d.ts +0 -7
- package/dist/extension/connections/ontology.mjs +0 -11
|
@@ -4,21 +4,22 @@ 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 { telegramText } from "./texts.mjs";
|
|
7
8
|
import { memberIdOf, telegramPrincipal } from "./principal.mjs";
|
|
8
9
|
import { agentMessage, observedChatKey, withConfirmationPrompt } from "../group-routing/observed.mjs";
|
|
9
10
|
import { conversationEvent } from "../conversations.mjs";
|
|
10
11
|
import { humanText } from "../confirmation-description.mjs";
|
|
11
12
|
import { renderConfirmation, withConfirmationText } from "./confirmation-text.mjs";
|
|
12
13
|
import { chatPostCard, chatPostCardStatus, chatPostReceipt, withChatPostCard } from "./chat-post-card.mjs";
|
|
13
|
-
import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-
|
|
14
|
+
import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
14
15
|
import { chatPostOperationId, isChatPost } from "../chat-post-confirmation.mjs";
|
|
15
16
|
import { confirmations } from "../confirmations.mjs";
|
|
16
17
|
import { formatAddress, scopedAddress } from "./sessions.mjs";
|
|
17
18
|
import { postDeferredCard } from "./deferred-confirmations.mjs";
|
|
18
19
|
import { answerMarker, isPendingQuestion, pendingQuestions, questionKey } from "../questions.mjs";
|
|
19
|
-
import { telegramTextLinks } from "@segmentstream-cloud/ontology/telegram";
|
|
20
20
|
import { createHash } from "node:crypto";
|
|
21
|
-
import {
|
|
21
|
+
import { telegramTextLinks } from "@segmentstream-cloud/ontology/telegram";
|
|
22
|
+
import { registerTelegramFreeformPrompt } from "eve/channels/telegram";
|
|
22
23
|
const PREFIX = "ss-confirm:";
|
|
23
24
|
function shortHash(value) {
|
|
24
25
|
return createHash("sha256").update(value).digest("hex").slice(0, 20);
|
|
@@ -130,7 +131,7 @@ async function handleConfirmationCallback(input) {
|
|
|
130
131
|
showAlert: false
|
|
131
132
|
});
|
|
132
133
|
if (!query.data?.startsWith(PREFIX) || query.message === void 0 || query.from.isBot) {
|
|
133
|
-
await notice("
|
|
134
|
+
await notice(telegramText("callback", "unknown"));
|
|
134
135
|
return;
|
|
135
136
|
}
|
|
136
137
|
const [requestHash, memberHash, choice] = query.data.slice(11).split(":");
|
|
@@ -139,7 +140,7 @@ async function handleConfirmationCallback(input) {
|
|
|
139
140
|
externalId: query.from.id
|
|
140
141
|
});
|
|
141
142
|
if (lookup.status !== "paired" || shortHash(lookup.member.id) !== memberHash) {
|
|
142
|
-
await notice("
|
|
143
|
+
await notice(telegramText("callback", "forbidden"));
|
|
143
144
|
return;
|
|
144
145
|
}
|
|
145
146
|
const address = scopedAddress(formatAddress({
|
|
@@ -154,7 +155,7 @@ async function handleConfirmationCallback(input) {
|
|
|
154
155
|
"n",
|
|
155
156
|
"e"
|
|
156
157
|
].includes(choice ?? "") || choice === "e" && !isChatPost(prompt.toolName)) {
|
|
157
|
-
await notice("
|
|
158
|
+
await notice(telegramText("callback", "already"));
|
|
158
159
|
return;
|
|
159
160
|
}
|
|
160
161
|
const expired = !Number.isFinite(prompt.at) || prompt.at + 6e5 <= (input.now ?? Date.now());
|
|
@@ -182,7 +183,7 @@ async function handleConfirmationCallback(input) {
|
|
|
182
183
|
...expired ? { confirmationReason: "expired" } : edit ? { confirmationReason: "edit" } : {}
|
|
183
184
|
}
|
|
184
185
|
}, edit ? "The chat post was cancelled for editing. Ask the member what to change using questions_ask, then make a NEW chat_post_message call and confirmation. Never reuse the cancelled call." : void 0);
|
|
185
|
-
await notice(expired ? "
|
|
186
|
+
await notice(expired ? telegramText("callback", "expired") : void 0);
|
|
186
187
|
}
|
|
187
188
|
function messagePrompt(message) {
|
|
188
189
|
return message.prompt ?? {
|
|
@@ -251,21 +252,25 @@ function telegramConfirmationEvents(archive, observed, deferredOptions) {
|
|
|
251
252
|
async "input.requested"(data, channel, ctx) {
|
|
252
253
|
for (const request of data.requests) {
|
|
253
254
|
if (request.kind !== "tool-approval") {
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
255
|
+
const text = [request.prompt, ...(request.options ?? []).map((option, index) => `${index + 1}. ${option.label}`)].join("\n");
|
|
256
|
+
if (text.length > 4096) throw new Error("A typed Telegram question must fit in one message.");
|
|
257
|
+
const sent = await withConfirmationText(text, () => channel.telegram.post({
|
|
258
|
+
text,
|
|
259
|
+
reply_markup: {
|
|
260
|
+
force_reply: true,
|
|
261
|
+
selective: true
|
|
262
|
+
}
|
|
263
|
+
}));
|
|
264
|
+
remember(channel, ctx, sent.id, text);
|
|
265
|
+
registerTelegramFreeformPrompt(channel.state, {
|
|
261
266
|
messageId: sent.id,
|
|
262
267
|
requestId: request.requestId
|
|
263
268
|
});
|
|
264
|
-
|
|
269
|
+
channel.state.hitlPrompts = {
|
|
265
270
|
...channel.state.hitlPrompts,
|
|
266
271
|
[request.requestId]: {
|
|
267
272
|
messageId: sent.id,
|
|
268
|
-
text
|
|
273
|
+
text,
|
|
269
274
|
options: (request.options ?? []).map(({ id, label }) => ({
|
|
270
275
|
id,
|
|
271
276
|
label
|
|
@@ -352,7 +357,7 @@ function telegramConfirmationEvents(archive, observed, deferredOptions) {
|
|
|
352
357
|
await channel.telegram.editMessageText({
|
|
353
358
|
messageId: prompt.messageId,
|
|
354
359
|
replyMarkup: { inline_keyboard: [] },
|
|
355
|
-
text: `${prompt.text}\n\n${label
|
|
360
|
+
text: label === void 0 ? prompt.text : `${prompt.text}\n\n${label}`
|
|
356
361
|
});
|
|
357
362
|
delete channel.state.hitlPrompts?.[resolution.requestId];
|
|
358
363
|
}
|
|
@@ -438,9 +443,9 @@ function telegramConfirmationEvents(archive, observed, deferredOptions) {
|
|
|
438
443
|
const failed = data.status !== "completed" || data.result.kind === "tool-result" && (data.result.isError === true || object(data.result.output).isError === true || object(object(data.result.output).validation).ok === false);
|
|
439
444
|
if (message.exactPost) {
|
|
440
445
|
const copy = message.copy ?? renderConfirmation();
|
|
441
|
-
const receipt = !failed && data.result.kind === "tool-result" ? chatPostReceipt(data.result.output
|
|
446
|
+
const receipt = !failed && data.result.kind === "tool-result" ? chatPostReceipt(data.result.output) : void 0;
|
|
442
447
|
const errorId = failed ? reportTelegramError("confirmed_chat_post_failed", data, { callId: data.result.callId }) : void 0;
|
|
443
|
-
const status = receipt ?? (failed ? `${copy.failed} ${errorId}
|
|
448
|
+
const status = receipt ?? (failed ? `${copy.failed} ${errorId}` : telegramText("chatPost", "uncertain") ?? "");
|
|
444
449
|
const text = chatPostCardStatus(message.text, status);
|
|
445
450
|
const prompt = {
|
|
446
451
|
...messagePrompt(message),
|
|
@@ -8,6 +8,7 @@ import type { AddressingReason, RouteDecider, RouteDecision } from "./router.js"
|
|
|
8
8
|
export declare const REVISION_CLAIM_TTL_MS: number;
|
|
9
9
|
/** Runs after webhook verification, before eve drops bot/channel/service updates. */
|
|
10
10
|
export declare function captureTelegramWebhook(input: {
|
|
11
|
+
readonly answerCallback?: (id: string, locale?: string) => Promise<unknown>;
|
|
11
12
|
readonly request: Request;
|
|
12
13
|
readonly archive: Conversations;
|
|
13
14
|
readonly identities: AgentIdentities;
|
|
@@ -31,6 +32,7 @@ export declare function captureTelegramFetch(fetchImpl: typeof fetch, archive: C
|
|
|
31
32
|
export declare function conversationIdentities(identities: AgentIdentities): AgentIdentities;
|
|
32
33
|
/** Verification and failure acknowledgement stay outside eve's swallowed onMessage failures. */
|
|
33
34
|
export declare function telegramConversationCapture(input: {
|
|
35
|
+
readonly answerCallback?: (id: string, locale?: string) => Promise<unknown>;
|
|
34
36
|
readonly archive: Conversations;
|
|
35
37
|
readonly identities: () => AgentIdentities;
|
|
36
38
|
readonly observed: ObservedMessages;
|
|
@@ -7,7 +7,7 @@ __eveCreateRequire(import.meta.url);
|
|
|
7
7
|
import { currentConfirmationPrompt, observedChatKey } from "../group-routing/observed.mjs";
|
|
8
8
|
import { conversationEvent } from "../conversations.mjs";
|
|
9
9
|
import { archivedRoutingMessages } from "../group-routing/routing-context.mjs";
|
|
10
|
-
import { n as currentInboundRequest, p as reportTelegramError, s as currentThreadId } from "../../../_chunks/inbound-request-
|
|
10
|
+
import { n as currentInboundRequest, p as reportTelegramError, s as currentThreadId } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
11
11
|
import { contentShape, messageContent } from "./content.mjs";
|
|
12
12
|
import { messageMetadata } from "./observed.mjs";
|
|
13
13
|
import { reportMembership } from "./membership.mjs";
|
|
@@ -79,6 +79,19 @@ async function captureTelegramWebhook(input) {
|
|
|
79
79
|
return input.next();
|
|
80
80
|
}
|
|
81
81
|
const update = record(raw);
|
|
82
|
+
const callback = record(update.callback_query);
|
|
83
|
+
const data = callback.data;
|
|
84
|
+
if (typeof data === "string" && (data.startsWith("eve_auth:") || data.startsWith("eve:"))) {
|
|
85
|
+
if (typeof callback.id === "string" && input.answerCallback !== void 0) {
|
|
86
|
+
const locale = record(callback.from).language_code;
|
|
87
|
+
try {
|
|
88
|
+
await input.answerCallback(callback.id, typeof locale === "string" ? locale : void 0);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
reportTelegramError("unsupported_callback_ack_failed", error, { callbackQueryId: callback.id });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return new Response("ok");
|
|
94
|
+
}
|
|
82
95
|
if (input.chats !== void 0 && (update.my_chat_member !== void 0 || update.chat_member !== void 0)) {
|
|
83
96
|
const task = reportMembership({
|
|
84
97
|
update,
|
|
@@ -4,14 +4,15 @@ export { isTechnicalText } from "../customer-text-safety.js";
|
|
|
4
4
|
export declare function withDeliveryLocale<T>(locale: string, run: () => T, threadId?: string, replyTo?: string, chatId?: string): T;
|
|
5
5
|
export declare function currentLocale(): string;
|
|
6
6
|
export declare function currentThreadId(): string | undefined;
|
|
7
|
-
export declare function customerCopy(): {
|
|
8
|
-
failed: string;
|
|
9
|
-
recovery: string;
|
|
10
|
-
attachment: string;
|
|
11
|
-
authorization: string;
|
|
12
|
-
response: string;
|
|
13
|
-
connected: string;
|
|
14
|
-
disconnected: string;
|
|
7
|
+
export declare function customerCopy(locale?: string): {
|
|
8
|
+
failed: string | undefined;
|
|
9
|
+
recovery: string | undefined;
|
|
10
|
+
attachment: string | undefined;
|
|
11
|
+
authorization: string | undefined;
|
|
12
|
+
response: string | undefined;
|
|
13
|
+
connected: string | undefined;
|
|
14
|
+
disconnected: string | undefined;
|
|
15
|
+
cancelled: string | undefined;
|
|
15
16
|
};
|
|
16
17
|
/** Original failures stay in operator diagnostics, joined to the public notice by a short reference. */
|
|
17
18
|
export declare function reportTelegramError(operation: string, error: unknown, metadata?: Record<string, unknown>, secrets?: ReadonlySet<string>): string;
|
|
@@ -5,5 +5,5 @@ const __filename = __eveFileURLToPath(import.meta.url);
|
|
|
5
5
|
__eveDirname(__filename);
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { isTechnicalText } from "../customer-text-safety.mjs";
|
|
8
|
-
import { a as TelegramDeliveryError, c as customerCopy, d as failureText, f as formattingRefused, h as withDeliveryLocale, l as customerMessage, m as safeTelegramFetch, o as currentLocale, p as reportTelegramError, s as currentThreadId, u as customerText } from "../../../_chunks/inbound-request-
|
|
8
|
+
import { a as TelegramDeliveryError, c as customerCopy, d as failureText, f as formattingRefused, h as withDeliveryLocale, l as customerMessage, m as safeTelegramFetch, o as currentLocale, p as reportTelegramError, s as currentThreadId, u as customerText } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
9
9
|
export { TelegramDeliveryError, currentLocale, currentThreadId, customerCopy, customerMessage, customerText, failureText, formattingRefused, isTechnicalText, reportTelegramError, safeTelegramFetch, withDeliveryLocale };
|
|
@@ -4,13 +4,14 @@ 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 { telegramText, withTelegramLocale } from "./texts.mjs";
|
|
7
8
|
import { telegramPrincipal } from "./principal.mjs";
|
|
8
9
|
import { agentMessage, observedChatKey, withConfirmationPrompt } from "../group-routing/observed.mjs";
|
|
9
10
|
import { conversationEvent } from "../conversations.mjs";
|
|
10
11
|
import { humanText } from "../confirmation-description.mjs";
|
|
11
12
|
import { renderConfirmation, withConfirmationText } from "./confirmation-text.mjs";
|
|
12
13
|
import { chatPostCard, chatPostCardStatus, chatPostReceipt, withChatPostCard } from "./chat-post-card.mjs";
|
|
13
|
-
import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-
|
|
14
|
+
import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
14
15
|
import { CONFIRMATION_TTL_MS, inputHash } from "../confirmations.mjs";
|
|
15
16
|
import { OntologyToolRejected } from "../ontology-tools.mjs";
|
|
16
17
|
import { confirmationOutcome } from "../confirmation-outcome.mjs";
|
|
@@ -20,7 +21,7 @@ import { telegramTextLinks } from "@segmentstream-cloud/ontology/telegram";
|
|
|
20
21
|
const PREFIX = "ss-confirm:d:";
|
|
21
22
|
const isDeferredCallback = (data) => data?.startsWith(PREFIX) === true;
|
|
22
23
|
const deferredCallback = (record, choice) => `${PREFIX}${record.key}:${choice}`;
|
|
23
|
-
const copyOf = (record) => record.data.confirmation.description?.chatPost ? chatPostCard(record.data.confirmation.description, record.data.confirmation.copy) : renderConfirmation(record.data.confirmation.description, record.data.confirmation.copy);
|
|
24
|
+
const copyOf = (record) => withTelegramLocale(record.data.locale, () => record.data.confirmation.description?.chatPost ? chatPostCard(record.data.confirmation.description, record.data.confirmation.copy) : renderConfirmation(record.data.confirmation.description, record.data.confirmation.copy));
|
|
24
25
|
function promptOf(record, state) {
|
|
25
26
|
const name = humanText(record.data.requesterName);
|
|
26
27
|
return {
|
|
@@ -28,7 +29,7 @@ function promptOf(record, state) {
|
|
|
28
29
|
requestId: record.id,
|
|
29
30
|
sessionId: record.data.sessionId,
|
|
30
31
|
address: record.data.address,
|
|
31
|
-
action: humanText(record.data.confirmation.description?.action) ?? "
|
|
32
|
+
action: humanText(record.data.confirmation.description?.action) ?? "",
|
|
32
33
|
requestedBy: name ?? null,
|
|
33
34
|
at: new Date(record.expiresAt - CONFIRMATION_TTL_MS).toISOString(),
|
|
34
35
|
state,
|
|
@@ -151,7 +152,7 @@ async function handleDeferredCallback(input) {
|
|
|
151
152
|
};
|
|
152
153
|
const parts = /^ss-confirm:d:([0-9a-f]{20}):([yne])$/.exec(query.data ?? "");
|
|
153
154
|
if (!parts || !query.message || query.from.isBot) {
|
|
154
|
-
await notice("
|
|
155
|
+
await notice(telegramText("callback", "unknown"));
|
|
155
156
|
return;
|
|
156
157
|
}
|
|
157
158
|
const store = deferredConfirmations(options);
|
|
@@ -164,11 +165,11 @@ async function handleDeferredCallback(input) {
|
|
|
164
165
|
externalId: query.from.id
|
|
165
166
|
});
|
|
166
167
|
if (!record || lookup.status !== "paired" || lookup.member.id !== record.memberId || record.data.requester !== query.from.id || record.data.chatId !== query.message.chat.id || record.data.threadId !== String(query.message.messageThreadId ?? "") || record.data.messageId !== query.message.messageId) {
|
|
167
|
-
await notice("
|
|
168
|
+
await notice(telegramText("callback", "forbidden"));
|
|
168
169
|
return;
|
|
169
170
|
}
|
|
170
171
|
if (record.inputHash !== inputHash(record.data.input) || parts[2] === "e" && record.data.confirmation.description?.chatPost === void 0) {
|
|
171
|
-
await notice("
|
|
172
|
+
await notice(telegramText("callback", "unknown"));
|
|
172
173
|
return;
|
|
173
174
|
}
|
|
174
175
|
const copy = copyOf(record);
|
|
@@ -181,10 +182,10 @@ async function handleDeferredCallback(input) {
|
|
|
181
182
|
decision
|
|
182
183
|
});
|
|
183
184
|
if (!claimed || claimed.status === "pending") {
|
|
184
|
-
await notice("
|
|
185
|
+
await notice(telegramText("callback", "already"));
|
|
185
186
|
return;
|
|
186
187
|
}
|
|
187
|
-
await notice(claimed.status === "expired" ? "
|
|
188
|
+
await notice(claimed.status === "expired" ? telegramText("callback", "expired") : void 0);
|
|
188
189
|
let status = claimed.status;
|
|
189
190
|
let cardStatus = status === "expired" ? copy.expired : copy.cancelled;
|
|
190
191
|
let execution;
|
|
@@ -213,7 +214,7 @@ async function handleDeferredCallback(input) {
|
|
|
213
214
|
try {
|
|
214
215
|
const result = await runDeferredAction(claimed, options);
|
|
215
216
|
status = "done";
|
|
216
|
-
cardStatus = claimed.data.confirmation.description?.chatPost ? chatPostReceipt(result.structuredContent,
|
|
217
|
+
cardStatus = claimed.data.confirmation.description?.chatPost ? withTelegramLocale(claimed.data.locale, () => chatPostReceipt(result.structuredContent) ?? telegramText("chatPost", "uncertain") ?? "") : copy.completed;
|
|
217
218
|
} catch (error) {
|
|
218
219
|
status = "failed";
|
|
219
220
|
const reference = reportTelegramError("deferred_action_failed", error);
|
|
@@ -5,7 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
|
|
|
5
5
|
__eveDirname(__filename);
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { conversationLanguage } from "./principal.mjs";
|
|
8
|
-
import { d as failureText, p as reportTelegramError } from "../../../_chunks/inbound-request-
|
|
8
|
+
import { d as failureText, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
9
9
|
import { describableImage, describeReceivedImage } from "./images.mjs";
|
|
10
10
|
import { MAXIMUM_ATTACHMENT_BYTES, attachmentUrl } from "../attachments.mjs";
|
|
11
11
|
function telegramConversation(chatId) {
|
|
@@ -4,7 +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 { t as agentBuild } from "../../../_chunks/build-metadata-
|
|
7
|
+
import { t as agentBuild } from "../../../_chunks/build-metadata-BLeBanNg.mjs";
|
|
8
8
|
import { runtimeRequestFailure } from "./runtime-request.mjs";
|
|
9
9
|
import { GET } from "eve/channels";
|
|
10
10
|
const HEALTH_ROUTE = "/eve/v1/telegram/health";
|
|
@@ -4,7 +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 { p as reportTelegramError } from "../../../_chunks/inbound-request-
|
|
7
|
+
import { p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
8
8
|
import { gateway, generateText } from "ai";
|
|
9
9
|
const MAXIMUM_IMAGE_DESCRIPTION_BYTES = 5242880;
|
|
10
10
|
const MAXIMUM_NATIVE_IMAGE_BYTES = 3145728;
|
|
@@ -4,5 +4,5 @@ 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 { i as withReplacedText, n as currentInboundRequest, r as runInboundRequest, t as InboundRequest } from "../../../_chunks/inbound-request-
|
|
7
|
+
import { i as withReplacedText, n as currentInboundRequest, r as runInboundRequest, t as InboundRequest } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
8
8
|
export { InboundRequest, currentInboundRequest, runInboundRequest, withReplacedText };
|
|
@@ -5,6 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
|
|
|
5
5
|
__eveDirname(__filename);
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { effectiveGroupPolicy, groupPolicy } from "../group-routing/policy.mjs";
|
|
8
|
+
import { admitMessage, localizedText } from "../admission.mjs";
|
|
8
9
|
import { conversationLanguage, telegramPrincipal } from "./principal.mjs";
|
|
9
10
|
import { addressingReason, conversationKey, otherRecipient } from "../group-routing/signals.mjs";
|
|
10
11
|
import { conversationAddressing, conversationState } from "../group-routing/conversation.mjs";
|
|
@@ -12,7 +13,7 @@ import { questionInvitation } from "../group-routing/engagement.mjs";
|
|
|
12
13
|
import { conversationContext, routeMessage } from "../group-routing/router.mjs";
|
|
13
14
|
import { observedContext } from "../group-routing/observed.mjs";
|
|
14
15
|
import { debounceBurst, routingBurst } from "../group-routing/burst.mjs";
|
|
15
|
-
import { p as reportTelegramError } from "../../../_chunks/inbound-request-
|
|
16
|
+
import { p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
16
17
|
import { AttachmentLifecycleNotPermitted, startTypingHeartbeat, typingUntilComplete } from "./lifecycle.mjs";
|
|
17
18
|
import { formatAddress, messageAddress, scopedAddress } from "./sessions.mjs";
|
|
18
19
|
import { contentShape, messageContent } from "./content.mjs";
|
|
@@ -27,13 +28,9 @@ import { questionAddress, questionFromHistory } from "./confirmations.mjs";
|
|
|
27
28
|
import { routingContext } from "./routing-context.mjs";
|
|
28
29
|
import { recordGateDecision, recordInboundContent, recordRouteDecision } from "./conversations.mjs";
|
|
29
30
|
import { chatContext } from "./chats.mjs";
|
|
30
|
-
import { admitMessage, localizedText } from "../admission.mjs";
|
|
31
31
|
import { attachmentsContext, inboundAttachments, stageFiles } from "./files.mjs";
|
|
32
32
|
import { heldApprovals, releaseHeldApproval } from "./held-approvals.mjs";
|
|
33
33
|
import { voiceOf, voiceText } from "./voice.mjs";
|
|
34
|
-
const UNKNOWN_PRIVATE = "🔗";
|
|
35
|
-
const UNKNOWN_IN_GROUP = "🔒";
|
|
36
|
-
const REFUSED = "🔒";
|
|
37
34
|
const MAXIMUM_QUOTE_LENGTH = 500;
|
|
38
35
|
function buttonTo(text, url) {
|
|
39
36
|
return { inline_keyboard: [[{
|
|
@@ -96,12 +93,6 @@ function speakerBlock(input) {
|
|
|
96
93
|
lines.push("</speaker>");
|
|
97
94
|
return lines.join("\n");
|
|
98
95
|
}
|
|
99
|
-
function unknownInGroup(bot) {
|
|
100
|
-
return bot?.username === void 0 ? { text: UNKNOWN_IN_GROUP } : {
|
|
101
|
-
text: UNKNOWN_IN_GROUP,
|
|
102
|
-
reply_markup: buttonTo("✉️", `https://t.me/${bot.username}`)
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
96
|
async function answerUnpaired(input) {
|
|
106
97
|
const { context, message, lookup, dependencies } = input;
|
|
107
98
|
if (lookup.status === "limited") {
|
|
@@ -131,12 +122,19 @@ async function answerUnpaired(input) {
|
|
|
131
122
|
}
|
|
132
123
|
if (lookup.status === "refused") {
|
|
133
124
|
const text = localizedText(dependencies.admission().replies.refused, message.from.languageCode);
|
|
134
|
-
await context.telegram.post(text
|
|
135
|
-
return
|
|
125
|
+
if (text !== void 0) await context.telegram.post(text);
|
|
126
|
+
return text !== void 0;
|
|
136
127
|
}
|
|
128
|
+
const replies = dependencies.admission().replies;
|
|
129
|
+
const locale = message.from.languageCode;
|
|
137
130
|
if (message.chat.type !== "private") {
|
|
138
|
-
|
|
139
|
-
|
|
131
|
+
const text = localizedText(replies.groupPairingRequired, locale);
|
|
132
|
+
const button = localizedText(replies.groupPairingButton, locale);
|
|
133
|
+
if (text !== void 0) await context.telegram.post({
|
|
134
|
+
text,
|
|
135
|
+
...button === void 0 || input.bot?.username === void 0 ? {} : { reply_markup: buttonTo(button, `https://t.me/${input.bot.username}`) }
|
|
136
|
+
});
|
|
137
|
+
return text !== void 0;
|
|
140
138
|
}
|
|
141
139
|
const sender = display(message.from);
|
|
142
140
|
const pairing = await dependencies.identities.createPairing({
|
|
@@ -144,9 +142,12 @@ async function answerUnpaired(input) {
|
|
|
144
142
|
externalId: message.from.id,
|
|
145
143
|
...sender === void 0 ? {} : { display: sender }
|
|
146
144
|
});
|
|
145
|
+
const text = localizedText(replies.pairingRequired, locale);
|
|
146
|
+
const label = localizedText(replies.pairingButton, locale);
|
|
147
|
+
if (text === void 0 || label === void 0) throw new Error("replies.pairingRequired and replies.pairingButton are required for Telegram.");
|
|
147
148
|
await context.telegram.post({
|
|
148
|
-
text
|
|
149
|
-
reply_markup: buttonTo(
|
|
149
|
+
text,
|
|
150
|
+
reply_markup: buttonTo(label, dependencies.connectUrl(pairing.code))
|
|
150
151
|
});
|
|
151
152
|
return true;
|
|
152
153
|
}
|
|
@@ -4,9 +4,9 @@ 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 { t as agentBuild } from "../../../_chunks/build-metadata-
|
|
7
|
+
import { t as agentBuild } from "../../../_chunks/build-metadata-BLeBanNg.mjs";
|
|
8
8
|
import { object } from "../confirmation-description.mjs";
|
|
9
|
-
import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-
|
|
9
|
+
import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
10
10
|
import { needsConfirmation } from "../confirmations.mjs";
|
|
11
11
|
import { OntologyActionNotPermitted, actionToolName, ontologyTools } from "../ontology-tools.mjs";
|
|
12
12
|
var AttachmentLifecycleNotPermitted = class extends Error {
|
|
@@ -5,7 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
|
|
|
5
5
|
__eveDirname(__filename);
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { DEFAULT_IMAGE_DESCRIPTION_MODEL } from "../../config.mjs";
|
|
8
|
-
import { p as reportTelegramError } from "../../../_chunks/inbound-request-
|
|
8
|
+
import { p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
9
9
|
function planMedia(capabilities, descriptionModel = null) {
|
|
10
10
|
if (typeof descriptionModel === "string") return {
|
|
11
11
|
images: "describe",
|
|
@@ -9,7 +9,7 @@ import { agentMessage, observedChatKey } from "../group-routing/observed.mjs";
|
|
|
9
9
|
import { confirmationLocale } from "./confirmation-text.mjs";
|
|
10
10
|
import { splitTelegramText } from "./format.mjs";
|
|
11
11
|
import { inTelegramSendQueue } from "./send-queue.mjs";
|
|
12
|
-
import { a as TelegramDeliveryError, l as customerMessage, o as currentLocale, p as reportTelegramError } from "../../../_chunks/inbound-request-
|
|
12
|
+
import { a as TelegramDeliveryError, l as customerMessage, o as currentLocale, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
13
13
|
const SILENT_MARKERS = new Set(["[silent]", "no_reply"]);
|
|
14
14
|
function isSilentReply(text) {
|
|
15
15
|
return SILENT_MARKERS.has(text.trim().toLowerCase());
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { LocalizedText } from "../../config.js";
|
|
2
|
+
export declare const TELEGRAM_TEXT_KEYS: {
|
|
3
|
+
readonly callback: readonly ["error", "unknown", "forbidden", "expired", "already", "unsupported"];
|
|
4
|
+
readonly confirmation: readonly ["question", "confirm", "cancel", "edit", "executing", "done", "cancelled", "failed", "expired", "interrupted"];
|
|
5
|
+
readonly chatPost: readonly ["sent", "uncertain", "historyNotRecorded"];
|
|
6
|
+
readonly notice: readonly ["failed", "recovery", "attachment", "authorization", "response", "connected", "disconnected", "cancelled"];
|
|
7
|
+
readonly authorization: readonly ["prompt", "url", "button"];
|
|
8
|
+
};
|
|
9
|
+
export type TelegramTexts = {
|
|
10
|
+
readonly [Group in keyof typeof TELEGRAM_TEXT_KEYS]?: Partial<Record<(typeof TELEGRAM_TEXT_KEYS)[Group][number], LocalizedText>>;
|
|
11
|
+
};
|
|
12
|
+
/** Bind workspace copy to one webhook or durable event, never to another workspace's request. */
|
|
13
|
+
export declare function withTelegramTexts<T>(texts: TelegramTexts, run: () => T, locale?: () => string): T;
|
|
14
|
+
export declare function telegramText<Group extends keyof TelegramTexts>(group: Group, key: (typeof TELEGRAM_TEXT_KEYS)[Group][number], locale?: string): string | undefined;
|
|
15
|
+
/** A persisted card keeps its original locale even while another member's webhook reconciles it. */
|
|
16
|
+
export declare function withTelegramLocale<T>(locale: string | undefined, run: () => T): T;
|
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
import { localizedText } from "../admission.mjs";
|
|
8
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
9
|
+
const TELEGRAM_TEXT_KEYS = {
|
|
10
|
+
callback: [
|
|
11
|
+
"error",
|
|
12
|
+
"unknown",
|
|
13
|
+
"forbidden",
|
|
14
|
+
"expired",
|
|
15
|
+
"already",
|
|
16
|
+
"unsupported"
|
|
17
|
+
],
|
|
18
|
+
confirmation: [
|
|
19
|
+
"question",
|
|
20
|
+
"confirm",
|
|
21
|
+
"cancel",
|
|
22
|
+
"edit",
|
|
23
|
+
"executing",
|
|
24
|
+
"done",
|
|
25
|
+
"cancelled",
|
|
26
|
+
"failed",
|
|
27
|
+
"expired",
|
|
28
|
+
"interrupted"
|
|
29
|
+
],
|
|
30
|
+
chatPost: [
|
|
31
|
+
"sent",
|
|
32
|
+
"uncertain",
|
|
33
|
+
"historyNotRecorded"
|
|
34
|
+
],
|
|
35
|
+
notice: [
|
|
36
|
+
"failed",
|
|
37
|
+
"recovery",
|
|
38
|
+
"attachment",
|
|
39
|
+
"authorization",
|
|
40
|
+
"response",
|
|
41
|
+
"connected",
|
|
42
|
+
"disconnected",
|
|
43
|
+
"cancelled"
|
|
44
|
+
],
|
|
45
|
+
authorization: [
|
|
46
|
+
"prompt",
|
|
47
|
+
"url",
|
|
48
|
+
"button"
|
|
49
|
+
]
|
|
50
|
+
};
|
|
51
|
+
const storage = new AsyncLocalStorage();
|
|
52
|
+
function withTelegramTexts(texts, run, locale = () => "en") {
|
|
53
|
+
return storage.run({
|
|
54
|
+
texts,
|
|
55
|
+
locale
|
|
56
|
+
}, run);
|
|
57
|
+
}
|
|
58
|
+
function telegramText(group, key, locale) {
|
|
59
|
+
const configured = storage.getStore();
|
|
60
|
+
const section = configured?.texts[group];
|
|
61
|
+
return localizedText(section?.[key], locale ?? configured?.locale());
|
|
62
|
+
}
|
|
63
|
+
function withTelegramLocale(locale, run) {
|
|
64
|
+
const configured = storage.getStore();
|
|
65
|
+
return configured === void 0 || locale === void 0 ? run() : storage.run({
|
|
66
|
+
...configured,
|
|
67
|
+
locale: () => locale
|
|
68
|
+
}, run);
|
|
69
|
+
}
|
|
70
|
+
export { TELEGRAM_TEXT_KEYS, telegramText, withTelegramLocale, withTelegramTexts };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { TelegramChannelEvents } from "eve/channels/telegram";
|
|
2
|
-
export declare const telegramFailureEvents: Pick<TelegramChannelEvents, "turn.failed" | "turn.cancelled" | "session.failed" | "authorization.completed">;
|
|
2
|
+
export declare const telegramFailureEvents: Pick<TelegramChannelEvents, "turn.failed" | "turn.cancelled" | "session.failed" | "authorization.completed" | "authorization.required">;
|
|
@@ -4,11 +4,41 @@ 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 { telegramText } from "./texts.mjs";
|
|
7
8
|
import { memberIdOf } from "./principal.mjs";
|
|
8
9
|
import { confirmationLocale } from "./confirmation-text.mjs";
|
|
9
|
-
import { c as customerCopy, d as failureText, o as currentLocale, p as reportTelegramError } from "../../../_chunks/inbound-request-
|
|
10
|
+
import { c as customerCopy, d as failureText, o as currentLocale, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
10
11
|
const telegramFailureEvents = {
|
|
12
|
+
async "authorization.required"(event, channel, context) {
|
|
13
|
+
if (event.candidateId !== void 0) return;
|
|
14
|
+
const locale = confirmationLocale(context.session.auth.current, currentLocale());
|
|
15
|
+
const prompt = telegramText("authorization", "prompt", locale);
|
|
16
|
+
const url = telegramText("authorization", "url", locale);
|
|
17
|
+
const button = telegramText("authorization", "button", locale);
|
|
18
|
+
if (prompt !== void 0) {
|
|
19
|
+
const sent = await channel.telegram.post({
|
|
20
|
+
text: prompt,
|
|
21
|
+
...url === void 0 || button === void 0 ? {} : { reply_markup: { inline_keyboard: [[{
|
|
22
|
+
text: button,
|
|
23
|
+
url
|
|
24
|
+
}]] } }
|
|
25
|
+
});
|
|
26
|
+
if (sent.id) channel.state.pendingAuthMessageIds = {
|
|
27
|
+
...channel.state.pendingAuthMessageIds,
|
|
28
|
+
[event.name]: sent.id
|
|
29
|
+
};
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const state = channel.state;
|
|
33
|
+
state.authorizationCancellationTurnId = event.turnId;
|
|
34
|
+
const errorId = reportTelegramError("authorization_prompt_unconfigured", new Error("texts.authorization.prompt is not configured."), { sessionId: context.session.id });
|
|
35
|
+
await channel.telegram.post(failureText(errorId, "authorization", locale));
|
|
36
|
+
},
|
|
11
37
|
async "turn.cancelled"(event, channel, context) {
|
|
38
|
+
const state = channel.state;
|
|
39
|
+
const authorizationTurn = state.authorizationCancellationTurnId;
|
|
40
|
+
delete state.authorizationCancellationTurnId;
|
|
41
|
+
if (typeof event.turnId === "string" && authorizationTurn === event.turnId) return;
|
|
12
42
|
const errorId = reportTelegramError("turn_cancelled", event, {
|
|
13
43
|
chatId: channel.state.chatId,
|
|
14
44
|
sessionId: context.session.id,
|
|
@@ -37,17 +67,21 @@ const telegramFailureEvents = {
|
|
|
37
67
|
const messageId = channel.state.pendingAuthMessageIds?.[event.name];
|
|
38
68
|
if (event.candidateId !== void 0 || messageId === void 0) return;
|
|
39
69
|
const locale = confirmationLocale(context.session.auth.current);
|
|
40
|
-
const copy = customerCopy();
|
|
70
|
+
const copy = customerCopy(locale);
|
|
41
71
|
let text = event.outcome === "authorized" ? copy.connected : copy.disconnected;
|
|
42
72
|
if (event.reason !== void 0) {
|
|
43
73
|
const errorId = reportTelegramError("authorization_not_completed", event, { chatId: channel.state.chatId });
|
|
44
74
|
text = failureText(errorId, "authorization", locale);
|
|
45
75
|
}
|
|
46
|
-
await channel.telegram.editMessageText({
|
|
76
|
+
if (text !== void 0) await channel.telegram.editMessageText({
|
|
47
77
|
messageId,
|
|
48
78
|
text,
|
|
49
79
|
replyMarkup: { inline_keyboard: [] }
|
|
50
80
|
});
|
|
81
|
+
else await channel.telegram.editMessageReplyMarkup({
|
|
82
|
+
messageId,
|
|
83
|
+
replyMarkup: { inline_keyboard: [] }
|
|
84
|
+
});
|
|
51
85
|
delete channel.state.pendingAuthMessageIds?.[event.name];
|
|
52
86
|
}
|
|
53
87
|
};
|
|
@@ -4,7 +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 { p as reportTelegramError } from "../../../_chunks/inbound-request-
|
|
7
|
+
import { p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
8
8
|
import "../connection-proxy.mjs";
|
|
9
9
|
import { gateway, transcribe } from "ai";
|
|
10
10
|
const LABELS = {
|
|
@@ -5,12 +5,14 @@ const __filename = __eveFileURLToPath(import.meta.url);
|
|
|
5
5
|
__eveDirname(__filename);
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { n as workflowToolResult } from "../../_chunks/workflow-serializable-DVQBWK8p.mjs";
|
|
8
|
+
import segmentstream from "../extension.mjs";
|
|
8
9
|
import { actionApproval } from "../lib/confirmations.mjs";
|
|
9
10
|
import { ontologyTools, promotionTarget } from "../lib/ontology-tools.mjs";
|
|
10
11
|
import { attachmentForModel, attachmentOwner, optionalObject, optionalString, stringArgument, toolAttachments } from "../lib/attachment-tools.mjs";
|
|
11
12
|
import { defineTool } from "eve/tools";
|
|
12
13
|
var attachments_promote_default = defineTool({
|
|
13
14
|
approval: actionApproval({
|
|
15
|
+
texts: () => segmentstream.config.texts ?? {},
|
|
14
16
|
labels: (memberId, _tool, input) => {
|
|
15
17
|
const target = promotionTarget(input);
|
|
16
18
|
return ontologyTools().labels(memberId, target.action, target.input);
|
|
@@ -123,7 +123,7 @@ var ontology_default = defineDynamic({ events: { "turn.started": async (_event,
|
|
|
123
123
|
required: ["name"],
|
|
124
124
|
additionalProperties: false
|
|
125
125
|
},
|
|
126
|
-
execute: (input, ctx) => executeDeferredTool(memberId, input.name, input.arguments ?? {}, ctx)
|
|
126
|
+
execute: (input, ctx) => executeDeferredTool(memberId, input.name, input.arguments ?? {}, ctx, { texts: () => segmentstream.config.texts ?? {} })
|
|
127
127
|
})
|
|
128
128
|
};
|
|
129
129
|
} } });
|