@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
|
@@ -11,7 +11,6 @@ talking to you, and you act with exactly their permissions.
|
|
|
11
11
|
exact MCP names and input schemas. Pass returned ontology hashes as `expected_hash`
|
|
12
12
|
on subsequent describe/discover requests so a redeployment cannot mix definitions.
|
|
13
13
|
These tools are directly available; do not search a connection namespace to find them.
|
|
14
|
-
With held confirmations the same operations are reached through `segmentstream__ontology`.
|
|
15
14
|
Call `me` when you need to check whom you act for. For an unfamiliar task, call `describe`
|
|
16
15
|
without a ref for a compact entity index, or `discover` to search names and descriptions.
|
|
17
16
|
Inspect relevant `entity:`, `action:`, or `measure:` refs with `describe` for their complete
|
|
@@ -42,14 +41,14 @@ talking to you, and you act with exactly their permissions.
|
|
|
42
41
|
the member explicitly cancels or replaces the earlier task. If work is interrupted or blocked,
|
|
43
42
|
say what remains unfinished; never silently replace it with the latest question.
|
|
44
43
|
- Destructive or irreversible actions, and legacy custom actions without safety labels, require
|
|
45
|
-
the tool boundary's
|
|
44
|
+
the tool boundary's workspace-authored confirmation buttons. Call the action to start that flow; never ask for
|
|
46
45
|
approval in prose or treat a text acknowledgement as approval. Wait for the button outcome.
|
|
47
46
|
When the tool schema offers `_confirmation`, supply {question, confirm, cancel, done,
|
|
48
47
|
cancelled, failed, expired?, edit?} in the chat's language. Describe the action and key
|
|
49
48
|
parameters in a plain single-line question (at most 300 characters); labels are at most
|
|
50
49
|
24 characters. No IDs, tool names, snake_case, URLs, credentials, or markup. The platform
|
|
51
|
-
adds trusted facts
|
|
52
|
-
|
|
50
|
+
adds trusted facts. Omit this argument on older tools without schema support.
|
|
51
|
+
Use distinct confirm/cancel labels; all visible wording comes from the workspace. If trusted
|
|
53
52
|
target titles are unavailable or the facts are too long, resolve the targets or narrow the call.
|
|
54
53
|
Cancel ends that call; retry it only after a new instruction from the member.
|
|
55
54
|
- Every change goes through an action. Report results after success; never claim a record changed
|
|
@@ -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 { t as agentBuild } from "../../_chunks/build-metadata-
|
|
7
|
+
import { t as agentBuild } from "../../_chunks/build-metadata-BLeBanNg.mjs";
|
|
8
8
|
export { agentBuild };
|
|
@@ -47,8 +47,9 @@ function describeChatPost(input) {
|
|
|
47
47
|
caller.attribution
|
|
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
|
+
const separator = typeof caller.attribution_separator === "string" ? caller.attribution_separator : "\n\n";
|
|
50
51
|
return {
|
|
51
|
-
action: "
|
|
52
|
+
action: "",
|
|
52
53
|
fields: [],
|
|
53
54
|
chatPost: {
|
|
54
55
|
chatId: target.id,
|
|
@@ -56,8 +57,9 @@ function describeChatPost(input) {
|
|
|
56
57
|
externalId: target.external_id,
|
|
57
58
|
title: target.title,
|
|
58
59
|
attribution: caller.attribution,
|
|
60
|
+
...separator === "\n\n" ? {} : { attributionSeparator: separator },
|
|
59
61
|
text: values.text,
|
|
60
|
-
finalText: `${converter.fromMarkdown(values.text)}
|
|
62
|
+
finalText: `${converter.fromMarkdown(values.text)}${escapeMarkdownV2(separator)}_${footer}_`,
|
|
61
63
|
replyTo: typeof values.reply_to === "string" ? values.reply_to : null
|
|
62
64
|
}
|
|
63
65
|
};
|
|
@@ -22,6 +22,6 @@ export declare function confirmationOutcome(record: DeferredConfirmation, status
|
|
|
22
22
|
request?: string | undefined;
|
|
23
23
|
} | undefined;
|
|
24
24
|
confirmation: string;
|
|
25
|
-
status: "
|
|
25
|
+
status: "expired" | "done" | "cancelled" | "failed" | "approved";
|
|
26
26
|
summary: string;
|
|
27
27
|
};
|
|
@@ -2,6 +2,7 @@ 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
4
|
import { type ConfirmationCopy } from "./telegram/confirmation-text.js";
|
|
5
|
+
import { type TelegramTexts } from "./telegram/texts.js";
|
|
5
6
|
export declare const CONFIRMATION_TTL_MS: number;
|
|
6
7
|
export interface ActionAnnotations {
|
|
7
8
|
readonly readOnlyHint?: boolean;
|
|
@@ -33,6 +34,7 @@ export declare const confirmations: StateHandle<Record<string, Confirmation>>;
|
|
|
33
34
|
export declare function actionApproval(options: {
|
|
34
35
|
readonly labels: (memberId: string, action: string, input: Readonly<Record<string, unknown>>) => Promise<ActionAnnotations>;
|
|
35
36
|
readonly describe?: (memberId: string, action: string, input: Readonly<Record<string, unknown>>) => Promise<ConfirmationDescription>;
|
|
37
|
+
readonly texts?: () => TelegramTexts;
|
|
36
38
|
readonly state?: StateHandle<Record<string, Confirmation>>;
|
|
37
39
|
readonly now?: () => number;
|
|
38
40
|
}): ApprovalConfiguration;
|
|
@@ -4,8 +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 { withTelegramTexts } from "./telegram/texts.mjs";
|
|
7
8
|
import { memberIdOf } from "./telegram/principal.mjs";
|
|
8
|
-
import { confirmationCopy, renderConfirmation } from "./telegram/confirmation-text.mjs";
|
|
9
|
+
import { confirmationCopy, confirmationLocale, renderConfirmation } from "./telegram/confirmation-text.mjs";
|
|
9
10
|
import { chatPostCard } from "./telegram/chat-post-card.mjs";
|
|
10
11
|
import { chatPostDigest, isChatPost } from "./chat-post-confirmation.mjs";
|
|
11
12
|
import { createHash } from "node:crypto";
|
|
@@ -36,6 +37,7 @@ function actionApproval(options) {
|
|
|
36
37
|
async request(ctx) {
|
|
37
38
|
const memberId = memberIdOf(ctx.session.auth.current);
|
|
38
39
|
if (memberId === void 0) return denied("A paired member is required.");
|
|
40
|
+
const present = (run) => options.texts === void 0 ? run() : withTelegramTexts(options.texts(), run, () => confirmationLocale(ctx.session.auth.current));
|
|
39
41
|
const hash = inputHash(ctx.toolInput ?? {});
|
|
40
42
|
const saved = state.get()[ctx.callId];
|
|
41
43
|
if (saved !== void 0) {
|
|
@@ -59,10 +61,11 @@ function actionApproval(options) {
|
|
|
59
61
|
const preview = description?.chatPost;
|
|
60
62
|
const source = ctx.session.auth.current?.attributes;
|
|
61
63
|
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
|
-
|
|
64
|
+
const previewDescription = {
|
|
63
65
|
...description,
|
|
64
66
|
warning: labels.destructiveHint === true || labels.irreversibleHint === true
|
|
65
|
-
}
|
|
67
|
+
};
|
|
68
|
+
present(() => chatPostCard(previewDescription, copy));
|
|
66
69
|
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.");
|
|
67
70
|
} catch (error) {
|
|
68
71
|
console.warn("The exact chat confirmation could not be prepared.", error);
|
|
@@ -92,7 +95,7 @@ function actionApproval(options) {
|
|
|
92
95
|
warning: labels.destructiveHint === true || labels.irreversibleHint === true
|
|
93
96
|
};
|
|
94
97
|
try {
|
|
95
|
-
if (!posting) renderConfirmation(description, copy);
|
|
98
|
+
if (!posting) present(() => renderConfirmation(description, copy));
|
|
96
99
|
} catch {
|
|
97
100
|
return denied("The complete confirmation facts are unsafe or too long. Narrow the action input.");
|
|
98
101
|
}
|
|
@@ -2,6 +2,7 @@ import type { ToolContext } from "eve/tools";
|
|
|
2
2
|
import { type Confirmation } from "./confirmations.js";
|
|
3
3
|
import { type Environment } from "./environment.js";
|
|
4
4
|
import { type RuntimeToken } from "./runtime-token.js";
|
|
5
|
+
import type { TelegramTexts } from "./telegram/texts.js";
|
|
5
6
|
export interface DeferredConfirmation {
|
|
6
7
|
readonly id: string;
|
|
7
8
|
readonly key: string;
|
|
@@ -18,12 +19,14 @@ export interface DeferredConfirmation {
|
|
|
18
19
|
readonly chatId: string;
|
|
19
20
|
readonly threadId: string;
|
|
20
21
|
readonly requester: string;
|
|
22
|
+
readonly locale?: string;
|
|
21
23
|
readonly requesterName?: string;
|
|
22
24
|
readonly messageId?: string;
|
|
23
25
|
readonly address?: string;
|
|
24
26
|
};
|
|
25
27
|
}
|
|
26
28
|
export type DeferredOptions = {
|
|
29
|
+
readonly texts?: () => TelegramTexts;
|
|
27
30
|
readonly environment?: Environment;
|
|
28
31
|
readonly token?: RuntimeToken;
|
|
29
32
|
readonly fetch?: typeof fetch;
|
|
@@ -60,6 +60,7 @@ async function executeDeferredTool(memberId, action, input, ctx, options = {}) {
|
|
|
60
60
|
let saved = {};
|
|
61
61
|
const result = await actionApproval({
|
|
62
62
|
...tools,
|
|
63
|
+
...options.texts === void 0 ? {} : { texts: options.texts },
|
|
63
64
|
state: {
|
|
64
65
|
get: () => saved,
|
|
65
66
|
update: (update) => {
|
|
@@ -101,6 +102,7 @@ async function executeDeferredTool(memberId, action, input, ctx, options = {}) {
|
|
|
101
102
|
chatId: attributes.chatId,
|
|
102
103
|
threadId: String(attributes.messageThreadId ?? ""),
|
|
103
104
|
requester: principal.subject,
|
|
105
|
+
locale: String(attributes.conversationLanguage ?? attributes.locale ?? "en"),
|
|
104
106
|
...humanText(attributes.memberName) === void 0 ? {} : { requesterName: humanText(attributes.memberName) }
|
|
105
107
|
}
|
|
106
108
|
}
|
|
@@ -154,7 +154,7 @@ function ontologyTools(options = {}) {
|
|
|
154
154
|
caller,
|
|
155
155
|
destinations
|
|
156
156
|
}),
|
|
157
|
-
action: typeof tool.title === "string" ? tool.title : "
|
|
157
|
+
action: typeof tool.title === "string" ? tool.title : typeof tool.description === "string" ? tool.description.split("\n\n")[0] : ""
|
|
158
158
|
};
|
|
159
159
|
}
|
|
160
160
|
let entities = [];
|
|
@@ -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 { telegramText, withTelegramTexts } from "./texts.mjs";
|
|
7
8
|
import { manifestOf } from "../../config.mjs";
|
|
8
9
|
import { appOrigin, channelsEnabled, proxyEnvironment, runtimeIdentityEnvironment, webhookSecret } from "../environment.mjs";
|
|
9
10
|
import { conversationLanguage, telegramPrincipal } from "./principal.mjs";
|
|
@@ -13,10 +14,10 @@ import { decisionRouter } from "../group-routing/router.mjs";
|
|
|
13
14
|
import { runtimeToken } from "../runtime-token.mjs";
|
|
14
15
|
import { conversations } from "../conversations.mjs";
|
|
15
16
|
import { archivedRoutingMessages } from "../group-routing/routing-context.mjs";
|
|
16
|
-
import { t as agentBuild } from "../../../_chunks/build-metadata-
|
|
17
|
+
import { t as agentBuild } from "../../../_chunks/build-metadata-BLeBanNg.mjs";
|
|
17
18
|
import { confirmationLocale } from "./confirmation-text.mjs";
|
|
18
19
|
import { telegramSendScope } from "./send-queue.mjs";
|
|
19
|
-
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-
|
|
20
|
+
import { d as failureText, h as withDeliveryLocale, i as withReplacedText, m as safeTelegramFetch, n as currentInboundRequest, o as currentLocale, p as reportTelegramError, r as runInboundRequest, t as InboundRequest } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
20
21
|
import { attachmentLifecycle } from "./lifecycle.mjs";
|
|
21
22
|
import { scopedAddress, sessionPolicy } from "./sessions.mjs";
|
|
22
23
|
import { observedMessages } from "./observed.mjs";
|
|
@@ -120,7 +121,31 @@ function segmentstreamTelegram(options) {
|
|
|
120
121
|
identities: pairing,
|
|
121
122
|
observed,
|
|
122
123
|
chats,
|
|
123
|
-
verify: telegramWebhookVerifier(() => webhookSecret(environment))
|
|
124
|
+
verify: telegramWebhookVerifier(() => webhookSecret(environment)),
|
|
125
|
+
answerCallback: async (callbackQueryId, locale) => {
|
|
126
|
+
const text = telegramText("callback", "unsupported", locale);
|
|
127
|
+
const reply = await proxy().request({
|
|
128
|
+
api: "bot",
|
|
129
|
+
method: "POST",
|
|
130
|
+
path: "/answerCallbackQuery",
|
|
131
|
+
headers: [["content-type", "application/json"]],
|
|
132
|
+
body: new TextEncoder().encode(JSON.stringify({
|
|
133
|
+
callback_query_id: callbackQueryId,
|
|
134
|
+
...text === void 0 ? {} : { text }
|
|
135
|
+
}))
|
|
136
|
+
});
|
|
137
|
+
const body = new TextDecoder().decode(reply.body);
|
|
138
|
+
let answer;
|
|
139
|
+
try {
|
|
140
|
+
answer = JSON.parse(body);
|
|
141
|
+
} catch (cause) {
|
|
142
|
+
throw new Error(`Telegram callback acknowledgement was invalid (HTTP ${reply.status}): ${body}`, { cause });
|
|
143
|
+
}
|
|
144
|
+
if (reply.status < 200 || reply.status >= 300 || answer === null || typeof answer !== "object" || !("ok" in answer) || answer.ok !== true) throw new Error("Telegram callback acknowledgement was refused.", { cause: {
|
|
145
|
+
status: reply.status,
|
|
146
|
+
body
|
|
147
|
+
} });
|
|
148
|
+
}
|
|
124
149
|
});
|
|
125
150
|
let store = options.attachments;
|
|
126
151
|
function attachments() {
|
|
@@ -279,6 +304,8 @@ function segmentstreamTelegram(options) {
|
|
|
279
304
|
}
|
|
280
305
|
},
|
|
281
306
|
onCallbackQuery: async (context, query) => {
|
|
307
|
+
const inbound = currentInboundRequest();
|
|
308
|
+
if (inbound !== void 0) inbound.locale = query.from.languageCode;
|
|
282
309
|
try {
|
|
283
310
|
if (isDeferredCallback(query.data)) {
|
|
284
311
|
await handleDeferredCallback({
|
|
@@ -306,7 +333,7 @@ function segmentstreamTelegram(options) {
|
|
|
306
333
|
reportTelegramError("confirmation_callback_failed", error);
|
|
307
334
|
await context.telegram.answerCallbackQuery({
|
|
308
335
|
callbackQueryId: query.id,
|
|
309
|
-
text: "
|
|
336
|
+
...telegramText("callback", "error") === void 0 ? {} : { text: telegramText("callback", "error") }
|
|
310
337
|
});
|
|
311
338
|
}
|
|
312
339
|
},
|
|
@@ -404,7 +431,7 @@ function segmentstreamTelegram(options) {
|
|
|
404
431
|
const locale = confirmationLocale(principal ?? null, context.state.customerLocale ?? "en");
|
|
405
432
|
context.state.customerLocale = locale;
|
|
406
433
|
await withDeliveryLocale(locale, async () => {
|
|
407
|
-
await handler(event, context);
|
|
434
|
+
await withTelegramTexts(options.admission?.().texts ?? {}, () => handler(event, context), currentLocale);
|
|
408
435
|
}, context.state.messageThreadId === null ? void 0 : String(context.state.messageThreadId), typeof principal?.attributes.replyToMessageId === "string" ? principal.attributes.replyToMessageId : void 0, typeof principal?.attributes.chatId === "string" ? principal.attributes.chatId : void 0);
|
|
409
436
|
}]))
|
|
410
437
|
} },
|
|
@@ -482,7 +509,7 @@ function gatedRoute(input) {
|
|
|
482
509
|
};
|
|
483
510
|
inbound.waitUntil = waitUntil;
|
|
484
511
|
inbound.backgroundUntil = (task) => args.waitUntil(task);
|
|
485
|
-
return runInboundRequest(inbound, () => input.capture(request, async (routedRequest = request) => {
|
|
512
|
+
return withTelegramTexts(options.admission?.().texts ?? {}, () => runInboundRequest(inbound, () => input.capture(request, async (routedRequest = request) => {
|
|
486
513
|
const response = await route.handler(routedRequest, {
|
|
487
514
|
...args,
|
|
488
515
|
waitUntil,
|
|
@@ -501,7 +528,7 @@ function gatedRoute(input) {
|
|
|
501
528
|
headers: { "retry-after": "1" }
|
|
502
529
|
});
|
|
503
530
|
return response;
|
|
504
|
-
}));
|
|
531
|
+
})), currentLocale);
|
|
505
532
|
}
|
|
506
533
|
};
|
|
507
534
|
}
|
|
@@ -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 { describableImage } from "./images.mjs";
|
|
9
9
|
import { MAXIMUM_ROUTING_PDF_CHARACTERS, extractRoutingPdf } from "./pdf-text.mjs";
|
|
10
10
|
import { gateway, generateText } from "ai";
|
|
@@ -10,7 +10,7 @@ import { runtimeToken } from "../runtime-token.mjs";
|
|
|
10
10
|
import { redactDiagnostic } from "../diagnostics.mjs";
|
|
11
11
|
import { confirmationLocale } from "./confirmation-text.mjs";
|
|
12
12
|
import { inTelegramSendQueue, telegramSendScope } from "./send-queue.mjs";
|
|
13
|
-
import {
|
|
13
|
+
import { d as failureText, f as formattingRefused, l as customerMessage, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
14
14
|
import { MAXIMUM_PROXY_BODY_BYTES, connectionProxy } from "../connection-proxy.mjs";
|
|
15
15
|
import { ACT_AS_HEADER } from "../ontology-connection.mjs";
|
|
16
16
|
import { AttachmentError, CONVERSATION_HEADER, SESSION_HEADER } from "../attachments.mjs";
|
|
@@ -83,14 +83,17 @@ async function sendAttachmentsInOrder(options) {
|
|
|
83
83
|
const environment = options.environment ?? process.env;
|
|
84
84
|
const principal = options.principal;
|
|
85
85
|
const locale = confirmationLocale(principal);
|
|
86
|
-
const unavailable = (error, diagnostic) =>
|
|
87
|
-
|
|
88
|
-
detail: customerCopy().attachment,
|
|
89
|
-
reference: reportTelegramError("attachment_send_refused", {
|
|
86
|
+
const unavailable = (error, diagnostic) => {
|
|
87
|
+
const reference = reportTelegramError("attachment_send_refused", {
|
|
90
88
|
error,
|
|
91
89
|
diagnostic
|
|
92
|
-
})
|
|
93
|
-
|
|
90
|
+
});
|
|
91
|
+
return {
|
|
92
|
+
error,
|
|
93
|
+
detail: failureText(reference, "attachment", locale),
|
|
94
|
+
reference
|
|
95
|
+
};
|
|
96
|
+
};
|
|
94
97
|
if (!channelsEnabled(environment)) return unavailable("channels_disabled", "Attachments can be sent only in production.");
|
|
95
98
|
if (options.connection === void 0) return unavailable("unsupported", "The Telegram channel is not configured.");
|
|
96
99
|
if (principal?.authenticator !== "segmentstream-telegram" || typeof principal.attributes.chatId !== "string" || !/^-?\d{1,20}$/.test(principal.attributes.chatId)) return unavailable("no_telegram_chat", "This turn has no Telegram chat.");
|
|
@@ -104,7 +107,7 @@ async function sendAttachmentsInOrder(options) {
|
|
|
104
107
|
const reference = reportTelegramError("attachment_owner_refused", error);
|
|
105
108
|
return {
|
|
106
109
|
error: "forbidden",
|
|
107
|
-
detail:
|
|
110
|
+
detail: failureText(reference, "attachment", locale),
|
|
108
111
|
reference
|
|
109
112
|
};
|
|
110
113
|
}
|
|
@@ -187,7 +190,7 @@ async function sendAttachmentsInOrder(options) {
|
|
|
187
190
|
id: reference.id,
|
|
188
191
|
filename,
|
|
189
192
|
error: error instanceof AttachmentError ? error.code : "unavailable",
|
|
190
|
-
detail:
|
|
193
|
+
detail: failureText(errorId, "attachment", locale),
|
|
191
194
|
reference: errorId
|
|
192
195
|
};
|
|
193
196
|
}
|
|
@@ -271,7 +274,7 @@ async function sendAttachmentsInOrder(options) {
|
|
|
271
274
|
id: file.id,
|
|
272
275
|
filename: file.filename,
|
|
273
276
|
error: error instanceof AttachmentError ? error.code : "send_failed",
|
|
274
|
-
detail:
|
|
277
|
+
detail: failureText(errorId, "attachment", locale),
|
|
275
278
|
reference: errorId
|
|
276
279
|
};
|
|
277
280
|
});
|
|
@@ -7,4 +7,4 @@ export declare function chatPostCardLinks(text: string): readonly string[] | und
|
|
|
7
7
|
export declare function chatPostCard(description: ConfirmationDescription, copy?: ConfirmationCopy): ConfirmationText;
|
|
8
8
|
/** Status/receipt strings are data outside the already rendered preview. */
|
|
9
9
|
export declare function chatPostCardStatus(text: string, status: string): string;
|
|
10
|
-
export declare function chatPostReceipt(output: unknown
|
|
10
|
+
export declare function chatPostReceipt(output: unknown): string | undefined;
|
|
@@ -4,11 +4,12 @@ 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 { renderConfirmation } from "./confirmation-text.mjs";
|
|
8
|
-
import { telegramMarkdownLinks, telegramTextLinks } from "@segmentstream-cloud/ontology/telegram";
|
|
9
9
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
10
|
+
import { telegramMarkdownLinks, telegramTextLinks } from "@segmentstream-cloud/ontology/telegram";
|
|
10
11
|
import { escapeMarkdownV2 } from "@chat-adapter/telegram";
|
|
11
|
-
const
|
|
12
|
+
const RECEIPT_DATA_SPACE = 512;
|
|
12
13
|
const exactText = new AsyncLocalStorage();
|
|
13
14
|
function withChatPostCard(text, run, links = []) {
|
|
14
15
|
if (text.length > 4096) throw new Error("The complete chat confirmation cannot fit in one Telegram message.");
|
|
@@ -30,31 +31,33 @@ function chatPostCard(description, copy) {
|
|
|
30
31
|
const base = renderConfirmation(description, copy);
|
|
31
32
|
const text = [
|
|
32
33
|
base.text,
|
|
33
|
-
|
|
34
|
-
...preview.replyTo === null ? [] : [
|
|
34
|
+
`${preview.title} (${preview.provider})`,
|
|
35
|
+
...preview.replyTo === null ? [] : [preview.replyTo]
|
|
35
36
|
].map(escapeMarkdownV2).join("\n") + `\n\n${preview.finalText}`;
|
|
36
|
-
|
|
37
|
+
const receiptSpace = RECEIPT_DATA_SPACE + Math.max(...["sent", "uncertain"].map((status) => escapeMarkdownV2(telegramText("chatPost", status) ?? "").length)) + escapeMarkdownV2(telegramText("chatPost", "historyNotRecorded") ?? "").length;
|
|
38
|
+
if (text.length > 4096 - receiptSpace) throw new Error("The full message and receipt cannot fit in one Telegram confirmation. Make a shorter draft.");
|
|
37
39
|
return {
|
|
38
40
|
...base,
|
|
39
41
|
text,
|
|
40
42
|
previewLinks: [
|
|
41
43
|
...telegramTextLinks(`${preview.title}\n${preview.replyTo ?? ""}`),
|
|
42
44
|
...telegramMarkdownLinks(preview.text),
|
|
45
|
+
...telegramTextLinks(preview.attributionSeparator ?? "\n\n"),
|
|
43
46
|
...telegramTextLinks(preview.attribution)
|
|
44
47
|
]
|
|
45
48
|
};
|
|
46
49
|
}
|
|
47
50
|
function chatPostCardStatus(text, status) {
|
|
48
|
-
return `${text}\n\n${escapeMarkdownV2(status)}`;
|
|
51
|
+
return status === "" ? text : `${text}\n\n${escapeMarkdownV2(status)}`;
|
|
49
52
|
}
|
|
50
|
-
function chatPostReceipt(output
|
|
53
|
+
function chatPostReceipt(output) {
|
|
51
54
|
const value = typeof output === "object" && output !== null ? output : {};
|
|
52
55
|
if (value.status !== "sent" && value.status !== "uncertain" || typeof value.history_recorded !== "boolean" || typeof value.duplicate !== "boolean" || value.message_id !== null && typeof value.message_id !== "string" || value.url !== null && typeof value.url !== "string") return;
|
|
53
56
|
const url = typeof value.url === "string" && value.url.length <= 256 && /^https:\/\/t\.me\/[A-Za-z0-9_/-]+$/u.test(value.url) ? value.url : null;
|
|
54
57
|
return [
|
|
55
|
-
value.status === "sent" ?
|
|
58
|
+
telegramText("chatPost", value.status === "sent" ? "sent" : "uncertain"),
|
|
56
59
|
...url === null ? [] : [url],
|
|
57
|
-
...value.history_recorded ? [] : ["
|
|
58
|
-
].join("\n");
|
|
60
|
+
...value.history_recorded ? [] : [telegramText("chatPost", "historyNotRecorded")]
|
|
61
|
+
].filter((line) => line !== void 0).join("\n");
|
|
59
62
|
}
|
|
60
63
|
export { chatPostCard, chatPostCardLinks, chatPostCardStatus, chatPostReceipt, isExactChatPostCard, withChatPostCard };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TelegramSettings } from "../../config.js";
|
|
2
2
|
import { type DeferredConfirmation, type DeferredOptions } from "../deferred-confirmations.js";
|
|
3
|
+
import { type TelegramTexts } from "./texts.js";
|
|
3
4
|
/** A bounded sweep; concurrent webhooks/schedules compete for the same atomic expiry claim. */
|
|
4
|
-
export declare function expireScheduledConfirmations(connection: string, notify: (record: DeferredConfirmation, result: Readonly<Record<string, unknown>>) => Promise<unknown>, options?: DeferredOptions, settings?: TelegramSettings): Promise<void>;
|
|
5
|
+
export declare function expireScheduledConfirmations(connection: string, notify: (record: DeferredConfirmation, result: Readonly<Record<string, unknown>>) => Promise<unknown>, options?: DeferredOptions, settings?: TelegramSettings, texts?: TelegramTexts): Promise<void>;
|
|
@@ -4,18 +4,19 @@ 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 { withTelegramTexts } from "./texts.mjs";
|
|
7
8
|
import { proxyEnvironment } from "../environment.mjs";
|
|
8
9
|
import { runtimeToken } from "../runtime-token.mjs";
|
|
9
10
|
import { conversations } from "../conversations.mjs";
|
|
10
11
|
import { telegramSendScope } from "./send-queue.mjs";
|
|
11
|
-
import { m as safeTelegramFetch, p as reportTelegramError } from "../../../_chunks/inbound-request-
|
|
12
|
+
import { m as safeTelegramFetch, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
|
|
12
13
|
import { deferredConfirmations } from "../deferred-confirmations.mjs";
|
|
13
14
|
import { expireDeferredRecord } from "./deferred-confirmations.mjs";
|
|
14
15
|
import { connectionProxy } from "../connection-proxy.mjs";
|
|
15
16
|
import { TELEGRAM_PLACEHOLDER_TOKEN, TELEGRAM_PROXY_ORIGIN, telegramProxyFetch } from "./proxy-fetch.mjs";
|
|
16
17
|
import { telegramPreviewEnvironment } from "@segmentstream-cloud/ontology/telegram";
|
|
17
18
|
import { editTelegramMessageText } from "eve/channels/telegram";
|
|
18
|
-
async function expireScheduledConfirmations(connection, notify, options = {}, settings) {
|
|
19
|
+
async function expireScheduledConfirmations(connection, notify, options = {}, settings, texts = {}) {
|
|
19
20
|
const environment = options.environment ?? process.env;
|
|
20
21
|
const token = options.token ?? runtimeToken;
|
|
21
22
|
const archive = conversations({
|
|
@@ -36,13 +37,13 @@ async function expireScheduledConfirmations(connection, notify, options = {}, se
|
|
|
36
37
|
});
|
|
37
38
|
const records = await deferredConfirmations(options).command({ operation: "expired" });
|
|
38
39
|
for (const record of records) try {
|
|
39
|
-
await expireDeferredRecord(record, { telegram: { editMessageText: (input) => editTelegramMessageText({
|
|
40
|
+
await withTelegramTexts(texts, () => expireDeferredRecord(record, { telegram: { editMessageText: (input) => editTelegramMessageText({
|
|
40
41
|
apiBaseUrl: TELEGRAM_PROXY_ORIGIN,
|
|
41
42
|
credentials: { botToken: TELEGRAM_PLACEHOLDER_TOKEN },
|
|
42
43
|
fetch: transport,
|
|
43
44
|
chatId: record.data.chatId,
|
|
44
45
|
...input
|
|
45
|
-
}) } }, options, archive, notify);
|
|
46
|
+
}) } }, options, archive, notify), () => record.data.locale ?? "en");
|
|
46
47
|
} catch (error) {
|
|
47
48
|
reportTelegramError("deferred_expiry_failed", error);
|
|
48
49
|
}
|
|
@@ -30,5 +30,5 @@ export interface ConfirmationText {
|
|
|
30
30
|
export declare function confirmationLocale(principal: SessionAuthContext | null, fallback?: string): string;
|
|
31
31
|
/** Reject the entire presentation object; never partially trust malformed model copy. */
|
|
32
32
|
export declare function confirmationCopy(value?: unknown): ConfirmationCopy | undefined;
|
|
33
|
-
/**
|
|
33
|
+
/** Button positions retain callback meaning; workspace copy supplies every label and status. */
|
|
34
34
|
export declare function renderConfirmation(description?: ConfirmationDescription, copy?: ConfirmationCopy): ConfirmationText;
|
|
@@ -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 { telegramText } from "./texts.mjs";
|
|
7
8
|
import { isTechnicalText } from "../customer-text-safety.mjs";
|
|
8
9
|
import { humanText, object } from "../confirmation-description.mjs";
|
|
9
10
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -48,7 +49,7 @@ function confirmationCopy(value) {
|
|
|
48
49
|
return typeof text === "string" && text === text.trim() && (key === "question" || !/[✅❌⌛⚠❓✏]/u.test(text)) && humanText(text, key === "question" ? 300 : 24) === text;
|
|
49
50
|
};
|
|
50
51
|
if (required.some((key) => !valid(key)) || optional.some((key) => copy[key] !== void 0 && !valid(key)) || copy.confirm === copy.cancel || Object.keys(copy).some((key) => ![...required, ...optional].includes(key))) {
|
|
51
|
-
console.warn("Invalid confirmation presentation; using
|
|
52
|
+
console.warn("Invalid confirmation presentation; using workspace presentation.");
|
|
52
53
|
return;
|
|
53
54
|
}
|
|
54
55
|
return { ...copy };
|
|
@@ -57,27 +58,32 @@ function renderConfirmation(description, copy) {
|
|
|
57
58
|
copy = confirmationCopy(copy);
|
|
58
59
|
const action = humanText(description?.action);
|
|
59
60
|
if (description === void 0 || action === void 0 || action === "❓") throw new Error("A confirmation requires a safe declared action title.");
|
|
60
|
-
const facts = [
|
|
61
|
+
const facts = [action, ...(description?.fields ?? []).map(({ label, value }) => {
|
|
61
62
|
if (value.length > 3584) throw new Error("The complete confirmation facts cannot fit in one Telegram message.");
|
|
62
63
|
const safeLabel = humanText(label);
|
|
63
64
|
const safeValue = humanText(value, 3584);
|
|
64
65
|
if (safeLabel === void 0 || safeValue === void 0) throw new Error("The complete confirmation facts contain an unsafe field.");
|
|
65
66
|
return `${safeLabel}: ${safeValue}`;
|
|
66
67
|
})].filter(Boolean).join("\n");
|
|
67
|
-
const
|
|
68
|
+
const configured = (key) => {
|
|
69
|
+
const value = telegramText("confirmation", key);
|
|
70
|
+
if (value === void 0) throw new Error(`texts.confirmation.${key} is required for Telegram confirmations.`);
|
|
71
|
+
return value;
|
|
72
|
+
};
|
|
73
|
+
const text = [copy?.question ?? configured("question"), facts].filter(Boolean).join("\n\n");
|
|
68
74
|
if (text.length > 3584) throw new Error("The complete confirmation facts cannot fit in one Telegram message.");
|
|
69
75
|
if (isTechnicalText(text)) throw new Error("The complete confirmation cannot pass the delivery safety check.");
|
|
70
76
|
return {
|
|
71
77
|
text,
|
|
72
|
-
confirm: copy
|
|
73
|
-
cancel: copy
|
|
74
|
-
executing: "
|
|
75
|
-
cancelled: copy
|
|
76
|
-
completed: copy
|
|
77
|
-
failed: copy
|
|
78
|
-
expired: copy?.expired
|
|
79
|
-
interrupted: copy?.interrupted
|
|
80
|
-
edit: copy?.edit
|
|
78
|
+
confirm: copy?.confirm ?? configured("confirm"),
|
|
79
|
+
cancel: copy?.cancel ?? configured("cancel"),
|
|
80
|
+
executing: configured("executing"),
|
|
81
|
+
cancelled: copy?.cancelled ?? configured("cancelled"),
|
|
82
|
+
completed: copy?.done ?? configured("done"),
|
|
83
|
+
failed: copy?.failed ?? configured("failed"),
|
|
84
|
+
expired: copy?.expired ?? configured("expired"),
|
|
85
|
+
interrupted: copy?.interrupted ?? configured("interrupted"),
|
|
86
|
+
edit: copy?.edit ?? configured("edit")
|
|
81
87
|
};
|
|
82
88
|
}
|
|
83
89
|
export { confirmationCopy, confirmationLocale, isConfirmationText, renderConfirmation, withConfirmationText };
|