@segmentstream/agent 0.26.0 → 0.27.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 +41 -43
- package/dist/_chunks/{build-metadata-jbNa5dbV.mjs → build-metadata-R_ssK2uR.mjs} +1 -1
- package/dist/extension/config.d.ts +11 -12
- package/dist/extension/config.mjs +13 -36
- package/dist/extension/extension.d.ts +1 -1
- package/dist/extension/extension.mjs +2 -2
- package/dist/extension/hooks/chat-context.d.ts +3 -0
- package/dist/extension/hooks/chat-context.mjs +10 -0
- package/dist/extension/hooks/sandbox.d.ts +2 -3
- package/dist/extension/instructions/conversations.d.ts +3 -2
- package/dist/extension/instructions/conversations.mjs +7 -11
- package/dist/extension/instructions.md +18 -0
- package/dist/extension/lib/build-metadata.mjs +1 -1
- package/dist/extension/lib/chat-context.d.ts +108 -0
- package/dist/extension/lib/chat-context.mjs +217 -0
- package/dist/extension/lib/conversations.d.ts +10 -6
- package/dist/extension/lib/conversations.mjs +28 -42
- package/dist/extension/lib/group-routing/burst.d.ts +1 -1
- package/dist/extension/lib/group-routing/conversation.d.ts +1 -1
- package/dist/extension/lib/group-routing/engagement.d.ts +1 -1
- package/dist/extension/lib/group-routing/{observed.d.ts → messages.d.ts} +9 -31
- package/dist/extension/lib/group-routing/messages.mjs +38 -0
- package/dist/extension/lib/group-routing/router.d.ts +3 -9
- package/dist/extension/lib/group-routing/router.mjs +2 -4
- package/dist/extension/lib/group-routing/routing-context.d.ts +6 -2
- package/dist/extension/lib/group-routing/routing-context.mjs +12 -1
- package/dist/extension/lib/telegram/adapter.d.ts +1 -3
- package/dist/extension/lib/telegram/adapter.mjs +9 -18
- package/dist/extension/lib/telegram/confirmations.d.ts +3 -3
- package/dist/extension/lib/telegram/confirmations.mjs +3 -25
- package/dist/extension/lib/telegram/conversations.d.ts +14 -3
- package/dist/extension/lib/telegram/conversations.mjs +16 -37
- package/dist/extension/lib/telegram/delivery-diagnostics.d.ts +4 -1
- package/dist/extension/lib/telegram/delivery-diagnostics.mjs +9 -3
- package/dist/extension/lib/telegram/health.mjs +1 -1
- package/dist/extension/lib/telegram/inbound.d.ts +14 -14
- package/dist/extension/lib/telegram/inbound.mjs +72 -68
- package/dist/extension/lib/telegram/lifecycle.mjs +1 -1
- package/dist/extension/lib/telegram/{observed.d.ts → message-metadata.d.ts} +2 -6
- package/dist/extension/lib/telegram/{observed.mjs → message-metadata.mjs} +1 -14
- package/dist/extension/lib/telegram/routing-context.d.ts +4 -3
- package/dist/extension/lib/telegram/routing-context.mjs +2 -2
- package/dist/extension/lib/telegram/sessions.d.ts +5 -52
- package/dist/extension/lib/telegram/sessions.mjs +1 -188
- package/dist/extension/lib/telegram/signals.mjs +1 -1
- package/dist/extension/lib/telegram/silent.d.ts +5 -6
- package/dist/extension/lib/telegram/silent.mjs +4 -31
- package/package.json +2 -2
- package/dist/extension/lib/group-routing/observed.mjs +0 -103
|
@@ -13,7 +13,7 @@ import { n as conversationLanguage, o as telegramPrincipal } from "../../../_chu
|
|
|
13
13
|
import { object } from "../confirmation-description.mjs";
|
|
14
14
|
import { confirmationLocale } from "./confirmation-text.mjs";
|
|
15
15
|
import { telegramSendScope } from "./send-queue.mjs";
|
|
16
|
-
import { conversations } from "../conversations.mjs";
|
|
16
|
+
import { conversations, topicContext } from "../conversations.mjs";
|
|
17
17
|
import { withDeliveryDiagnostics } from "./delivery-diagnostics.mjs";
|
|
18
18
|
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-D1r0FkDI.mjs";
|
|
19
19
|
import { chooseModel } from "../../model.mjs";
|
|
@@ -21,22 +21,21 @@ import { groupRoutingHealth } from "../group-routing/health.mjs";
|
|
|
21
21
|
import { decisionRouter } from "../group-routing/router.mjs";
|
|
22
22
|
import { archivedRoutingMessages } from "../group-routing/routing-context.mjs";
|
|
23
23
|
import { dispatchStructuredMessage, structuredMessage } from "./structured.mjs";
|
|
24
|
-
import { scopedAddress
|
|
24
|
+
import { scopedAddress } from "./sessions.mjs";
|
|
25
25
|
import { handleWorkspaceCallback, isWorkspaceCallback } from "./callbacks.mjs";
|
|
26
|
-
import { t as agentBuild } from "../../../_chunks/build-metadata-
|
|
26
|
+
import { t as agentBuild } from "../../../_chunks/build-metadata-R_ssK2uR.mjs";
|
|
27
27
|
import { attachmentLifecycle } from "./lifecycle.mjs";
|
|
28
28
|
import { gatewayImageDescriber } from "./images.mjs";
|
|
29
29
|
import { gatewayAttachmentDescriber } from "./attachment-description.mjs";
|
|
30
30
|
import { MAXIMUM_DOWNLOAD_BYTES, connectionProxy } from "../connection-proxy.mjs";
|
|
31
31
|
import { agentAttachments, parseAttachmentUrl } from "../attachments.mjs";
|
|
32
32
|
import { agentIdentities } from "../identities.mjs";
|
|
33
|
-
import { observedMessages } from "./observed.mjs";
|
|
34
|
-
import { interimTexts, replyDelivery } from "./silent.mjs";
|
|
35
|
-
import { handleConfirmationCallback, telegramConfirmationEvents } from "./confirmations.mjs";
|
|
36
33
|
import { captureRouteDecider, captureTelegramFetch, conversationIdentities, recordGateDecision, telegramConversationCapture } from "./conversations.mjs";
|
|
37
34
|
import { botIdentityCache } from "./bot.mjs";
|
|
35
|
+
import { handleConfirmationCallback, telegramConfirmationEvents } from "./confirmations.mjs";
|
|
38
36
|
import { gatewayMediaCapabilities, planMedia } from "./media-policy.mjs";
|
|
39
37
|
import { agentChats } from "./chats.mjs";
|
|
38
|
+
import { interimTexts, replyDelivery } from "./silent.mjs";
|
|
40
39
|
import { downloadTelegramFile, gatewayTranscriber } from "./voice.mjs";
|
|
41
40
|
import { handleInboundMessage } from "./inbound.mjs";
|
|
42
41
|
import { recoverableSessions } from "./session-recovery.mjs";
|
|
@@ -78,7 +77,6 @@ const eveInteractions = async (sessionId) => {
|
|
|
78
77
|
function segmentstreamTelegram(options) {
|
|
79
78
|
const environment = options.environment ?? process.env;
|
|
80
79
|
const token = options.token ?? runtimeToken;
|
|
81
|
-
const observed = options.observed ?? observedMessages(60);
|
|
82
80
|
const readHistory = options.history ?? eveHistory;
|
|
83
81
|
const readInteractions = options.history ?? eveInteractions;
|
|
84
82
|
const capabilities = options.modelCapabilities ?? gatewayMediaCapabilities(options.fetch);
|
|
@@ -87,10 +85,6 @@ function segmentstreamTelegram(options) {
|
|
|
87
85
|
token,
|
|
88
86
|
fetch: options.fetch
|
|
89
87
|
});
|
|
90
|
-
const policy = sessionPolicy({
|
|
91
|
-
settings: options.sessions,
|
|
92
|
-
read: readHistory
|
|
93
|
-
});
|
|
94
88
|
const bot = botIdentityCache();
|
|
95
89
|
const proxies = new Map();
|
|
96
90
|
const interimDelivered = interimTexts();
|
|
@@ -127,7 +121,6 @@ function segmentstreamTelegram(options) {
|
|
|
127
121
|
const capture = telegramConversationCapture({
|
|
128
122
|
archive,
|
|
129
123
|
identities: pairing,
|
|
130
|
-
observed,
|
|
131
124
|
chats,
|
|
132
125
|
structured: (raw) => {
|
|
133
126
|
const inbound = settings().inbound;
|
|
@@ -172,7 +165,7 @@ function segmentstreamTelegram(options) {
|
|
|
172
165
|
archive,
|
|
173
166
|
identities: pairing
|
|
174
167
|
});
|
|
175
|
-
const approvalEvents = telegramConfirmationEvents(archive,
|
|
168
|
+
const approvalEvents = telegramConfirmationEvents(archive, () => options.sessions().scope);
|
|
176
169
|
const channel = telegramChannel({
|
|
177
170
|
turnPolicy: "queue",
|
|
178
171
|
api: {
|
|
@@ -203,10 +196,10 @@ function segmentstreamTelegram(options) {
|
|
|
203
196
|
policy: settings().policy,
|
|
204
197
|
decisionModel: options.models().decision,
|
|
205
198
|
groups: options.groups?.(),
|
|
206
|
-
recentConversation: async (chatId, threadId) => archivedRoutingMessages((await archive
|
|
199
|
+
recentConversation: async (chatId, threadId) => archivedRoutingMessages((await topicContext(archive, {
|
|
207
200
|
provider: "telegram",
|
|
208
201
|
chatId,
|
|
209
|
-
|
|
202
|
+
threadId: threadId ?? null,
|
|
210
203
|
limit: 200,
|
|
211
204
|
includeDecisions: true
|
|
212
205
|
})).events, 100),
|
|
@@ -236,7 +229,6 @@ function segmentstreamTelegram(options) {
|
|
|
236
229
|
}) },
|
|
237
230
|
sessions: options.sessions,
|
|
238
231
|
admission: options.admission ?? (() => manifestOf({})),
|
|
239
|
-
sessionPolicy: policy,
|
|
240
232
|
sttModel: options.models().stt,
|
|
241
233
|
attachmentsModel: options.models().imageDescription ?? options.models().attachments ?? "google/gemini-2.5-flash-lite",
|
|
242
234
|
describeAttachment: options.describeAttachment ?? gatewayAttachmentDescriber,
|
|
@@ -285,7 +277,6 @@ function segmentstreamTelegram(options) {
|
|
|
285
277
|
...options
|
|
286
278
|
}),
|
|
287
279
|
attachments: attachments(),
|
|
288
|
-
observed,
|
|
289
280
|
readHistory,
|
|
290
281
|
history: readInteractions,
|
|
291
282
|
chats,
|
|
@@ -425,7 +416,7 @@ function segmentstreamTelegram(options) {
|
|
|
425
416
|
await channel.telegram.post(failureText(reference));
|
|
426
417
|
return;
|
|
427
418
|
}
|
|
428
|
-
await replyDelivery(
|
|
419
|
+
await replyDelivery(() => telegramSendScope(environment, settings().connection), {
|
|
429
420
|
enabled: () => settings().interimMessages === true,
|
|
430
421
|
delivered: interimDelivered
|
|
431
422
|
})(event, context);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { ChannelEvents } from "eve/channels";
|
|
2
2
|
import type { TelegramCallbackQuery, TelegramChannelEvents, TelegramContext, TelegramEventContext } from "eve/channels/telegram";
|
|
3
|
-
import { type
|
|
3
|
+
import { type ConfirmationPrompt, type PendingConfirmation, type RecentMessage } from "../group-routing/messages.js";
|
|
4
4
|
import { type Conversations } from "../conversations.js";
|
|
5
5
|
import type { AgentIdentities } from "../identities.js";
|
|
6
6
|
import { type PendingQuestion } from "../questions.js";
|
|
7
7
|
import { type HistoryReader, type RecordedEvent } from "./sessions.js";
|
|
8
8
|
export declare function confirmationCallback(requestId: string, memberId: string, decision: "approve" | "cancel" | "edit"): string;
|
|
9
9
|
/** Terminal facts override stale presentation, including when a Telegram edit failed. */
|
|
10
|
-
export declare function routingConfirmationState(events: readonly RecordedEvent[], prompt:
|
|
10
|
+
export declare function routingConfirmationState(events: readonly RecordedEvent[], prompt: ConfirmationPrompt, at: string): NonNullable<PendingConfirmation["state"]>;
|
|
11
11
|
/** Read-only projection of the same durable approval lifecycle used by the callback gate. */
|
|
12
12
|
export declare function routingConfirmations(events: readonly RecordedEvent[], recent: readonly RecentMessage[], at: string): ReadonlyArray<PendingConfirmation>;
|
|
13
13
|
export declare function questionFromHistory(events: readonly RecordedEvent[], owner?: Pick<PendingQuestion, "memberId" | "chatId" | "messageThreadId">): PendingQuestion | undefined;
|
|
@@ -23,5 +23,5 @@ export declare function handleConfirmationCallback(input: {
|
|
|
23
23
|
readonly scope: "chat" | "thread";
|
|
24
24
|
readonly now?: number;
|
|
25
25
|
}): Promise<void>;
|
|
26
|
-
export declare function telegramConfirmationEvents(archive?: Conversations,
|
|
26
|
+
export declare function telegramConfirmationEvents(archive?: Conversations, scope?: () => "chat" | "thread"): TelegramChannelEvents & Pick<ChannelEvents<TelegramEventContext>, "context.settled">;
|
|
27
27
|
export declare const confirmationEvents: TelegramChannelEvents & Pick<ChannelEvents<TelegramEventContext>, "context.settled">;
|
|
@@ -11,7 +11,7 @@ import { renderConfirmation, withConfirmationText } from "./confirmation-text.mj
|
|
|
11
11
|
import { chatPostCard, chatPostCardStatus, chatPostReceipt, withChatPostCard } from "./chat-post-card.mjs";
|
|
12
12
|
import { conversationEvent } from "../conversations.mjs";
|
|
13
13
|
import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-D1r0FkDI.mjs";
|
|
14
|
-
import {
|
|
14
|
+
import { withConfirmationPrompt } from "../group-routing/messages.mjs";
|
|
15
15
|
import { chatPostOperationId, isChatPost } from "../chat-post-confirmation.mjs";
|
|
16
16
|
import { confirmations } from "../confirmations.mjs";
|
|
17
17
|
import { formatAddress, scopedAddress } from "./sessions.mjs";
|
|
@@ -201,23 +201,7 @@ function messagePrompt(message) {
|
|
|
201
201
|
at: new Date().toISOString()
|
|
202
202
|
};
|
|
203
203
|
}
|
|
204
|
-
function telegramConfirmationEvents(archive,
|
|
205
|
-
const remember = (channel, ctx, messageId, text, confirmation) => {
|
|
206
|
-
const principal = ctx.session.auth.current;
|
|
207
|
-
const name = humanText(principal?.attributes.memberName);
|
|
208
|
-
observed?.remember(observedChatKey(channel.telegram), {
|
|
209
|
-
...agentMessage({
|
|
210
|
-
text,
|
|
211
|
-
at: new Date().toISOString(),
|
|
212
|
-
messageId,
|
|
213
|
-
...principal?.subject === void 0 || name === void 0 ? {} : { inReplyTo: {
|
|
214
|
-
id: principal.subject,
|
|
215
|
-
author: name
|
|
216
|
-
} }
|
|
217
|
-
}),
|
|
218
|
-
...confirmation === void 0 ? {} : { confirmation }
|
|
219
|
-
});
|
|
220
|
-
};
|
|
204
|
+
function telegramConfirmationEvents(archive, scope) {
|
|
221
205
|
const record = async (callId, phase, text, status, ctx) => {
|
|
222
206
|
const saved = confirmations.get()[callId];
|
|
223
207
|
if (archive === void 0 || saved?.description?.chatPost === void 0 || saved.source === void 0) return;
|
|
@@ -275,7 +259,6 @@ function telegramConfirmationEvents(archive, observed, scope) {
|
|
|
275
259
|
selective: true
|
|
276
260
|
}
|
|
277
261
|
}));
|
|
278
|
-
remember(channel, ctx, sent.id, text);
|
|
279
262
|
channel.state.pendingFreeformReplies = {
|
|
280
263
|
...channel.state.pendingFreeformReplies,
|
|
281
264
|
[sent.id]: requestId
|
|
@@ -341,7 +324,6 @@ function telegramConfirmationEvents(archive, observed, scope) {
|
|
|
341
324
|
} }
|
|
342
325
|
};
|
|
343
326
|
const sent = await withConfirmationPrompt(prompt, () => exactPost ? withChatPostCard(text, send, copy.previewLinks) : withConfirmationText(text, send));
|
|
344
|
-
remember(channel, ctx, sent.id, text, prompt);
|
|
345
327
|
if (aliasesPendingTopic(scope?.(), channel.continuation?.token)) channel.continuation?.alias(formatAddress({
|
|
346
328
|
chatId: channel.telegram.chatId,
|
|
347
329
|
messageThreadId: String(channel.telegram.messageThreadId ?? ""),
|
|
@@ -405,7 +387,6 @@ function telegramConfirmationEvents(archive, observed, scope) {
|
|
|
405
387
|
};
|
|
406
388
|
message.prompt = prompt;
|
|
407
389
|
await withConfirmationPrompt(prompt, () => message.exactPost ? withChatPostCard(text, edit, [...message.previewLinks ?? [], ...telegramTextLinks(status)]) : withConfirmationText(text, edit));
|
|
408
|
-
remember(channel, ctx, message.messageId, text, prompt);
|
|
409
390
|
await record(callId, reason ?? (resolution.outcome === "accepted" ? "approved" : resolution.outcome === "declined" ? "cancelled" : "ignored"), text, resolution.outcome === "accepted" ? "requested" : "cancelled", ctx);
|
|
410
391
|
if (resolution.outcome !== "accepted") {
|
|
411
392
|
delete state.confirmationMessages?.[callId];
|
|
@@ -451,14 +432,13 @@ function telegramConfirmationEvents(archive, observed, scope) {
|
|
|
451
432
|
}));
|
|
452
433
|
channel.continuation?.alias(questionAddress(saved));
|
|
453
434
|
}
|
|
454
|
-
|
|
435
|
+
await channel.telegram.post({
|
|
455
436
|
text: question.question,
|
|
456
437
|
reply_markup: {
|
|
457
438
|
force_reply: true,
|
|
458
439
|
selective: true
|
|
459
440
|
}
|
|
460
441
|
});
|
|
461
|
-
remember(channel, ctx, sent.id, question.question);
|
|
462
442
|
}
|
|
463
443
|
}
|
|
464
444
|
const message = state.confirmationMessages?.[data.callId];
|
|
@@ -479,7 +459,6 @@ function telegramConfirmationEvents(archive, observed, scope) {
|
|
|
479
459
|
replyMarkup: { inline_keyboard: [] },
|
|
480
460
|
text
|
|
481
461
|
}), [...message.previewLinks ?? [], ...telegramTextLinks(status)]));
|
|
482
|
-
remember(channel, ctx, message.messageId, text, prompt);
|
|
483
462
|
await record(data.callId, "receipt", text, failed ? "failed" : object(action?.kind === "tool" ? data.output : void 0).status === "sent" ? "completed" : "unknown", ctx);
|
|
484
463
|
} else {
|
|
485
464
|
const copy = message.copy ?? renderConfirmation();
|
|
@@ -495,7 +474,6 @@ function telegramConfirmationEvents(archive, observed, scope) {
|
|
|
495
474
|
replyMarkup: { inline_keyboard: [] },
|
|
496
475
|
text
|
|
497
476
|
})));
|
|
498
|
-
remember(channel, ctx, message.messageId, text, prompt);
|
|
499
477
|
}
|
|
500
478
|
delete state.confirmationMessages?.[data.callId];
|
|
501
479
|
},
|
|
@@ -2,7 +2,6 @@ import type { StructuredMessage } from "./structured.js";
|
|
|
2
2
|
import type { AgentIdentities } from "../identities.js";
|
|
3
3
|
import { type Conversations } from "../conversations.js";
|
|
4
4
|
import type { AgentChats } from "./chats.js";
|
|
5
|
-
import type { ObservedMessages } from "./observed.js";
|
|
6
5
|
import type { DecodedAttachment } from "./attachment-description.js";
|
|
7
6
|
import type { AddressingReason, RouteDecider, RouteDecision } from "./router.js";
|
|
8
7
|
/** A crashed routing attempt can be reclaimed; the per-message turn claim still fences dispatch. */
|
|
@@ -14,7 +13,6 @@ export declare function captureTelegramWebhook(input: {
|
|
|
14
13
|
readonly request: Request;
|
|
15
14
|
readonly archive: Conversations;
|
|
16
15
|
readonly identities: AgentIdentities;
|
|
17
|
-
readonly observed: ObservedMessages;
|
|
18
16
|
readonly chats?: AgentChats;
|
|
19
17
|
readonly next: (request?: Request) => Promise<Response>;
|
|
20
18
|
}): Promise<Response>;
|
|
@@ -22,11 +20,25 @@ export declare function captureTelegramWebhook(input: {
|
|
|
22
20
|
export declare function recordGateDecision(reason: string, decision?: "reply" | "silent"): void;
|
|
23
21
|
export declare function recordRouteDecision(route: "reply" | "observe", addressed: boolean | AddressingReason | undefined, policy: string, audit?: Pick<RouteDecision, "reason" | "note" | "diagnostics" | "router" | "shadow">): void;
|
|
24
22
|
export declare function captureRouteDecider(decide: RouteDecider): RouteDecider;
|
|
23
|
+
/** A stored attachment of an archived message: what a later turn needs to read or show it. */
|
|
24
|
+
export interface ArchivedFile {
|
|
25
|
+
readonly id: string;
|
|
26
|
+
readonly filename: string;
|
|
27
|
+
readonly contentType: string;
|
|
28
|
+
readonly size: number;
|
|
29
|
+
readonly expiresAt: string;
|
|
30
|
+
/** Whether it can reach a model as a native image. */
|
|
31
|
+
readonly image?: true;
|
|
32
|
+
}
|
|
25
33
|
/** Update the existing inbound event after STT/description, under the same text retention policy. */
|
|
26
34
|
export declare function recordInboundContent(content: {
|
|
27
35
|
readonly text: string;
|
|
28
36
|
readonly media?: string;
|
|
29
37
|
readonly decoded?: ReadonlyArray<DecodedAttachment>;
|
|
38
|
+
/** How the chat context names a linked member: their name and roles, then their handle. */
|
|
39
|
+
readonly author?: string;
|
|
40
|
+
/** The message's stored attachments, by reference: never bytes or download grants. */
|
|
41
|
+
readonly files?: ReadonlyArray<ArchivedFile>;
|
|
30
42
|
}): Promise<void>;
|
|
31
43
|
/**
|
|
32
44
|
* Records eve's unrecoverable session failure from the channel. The channel receives it even when
|
|
@@ -54,7 +66,6 @@ export declare function telegramConversationCapture(input: {
|
|
|
54
66
|
readonly structured?: (raw: Readonly<Record<string, unknown>>) => StructuredMessage | undefined;
|
|
55
67
|
readonly archive: Conversations;
|
|
56
68
|
readonly identities: () => AgentIdentities;
|
|
57
|
-
readonly observed: ObservedMessages;
|
|
58
69
|
readonly chats: AgentChats;
|
|
59
70
|
readonly verify: (request: Request) => boolean;
|
|
60
71
|
}): (request: Request, next: (request?: Request) => Promise<Response>) => Promise<Response>;
|
|
@@ -6,12 +6,12 @@ __eveDirname(__filename);
|
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { n as contentText, t as CONTENT_CAPS } from "../../../_chunks/content-cDtVnr8Y.mjs";
|
|
8
8
|
import { conversationEvent, sessionFailureEventId } from "../conversations.mjs";
|
|
9
|
+
import { currentDeliveryScope } from "./delivery-diagnostics.mjs";
|
|
9
10
|
import { n as currentInboundRequest, p as reportTelegramError, s as currentThreadId } from "../../../_chunks/inbound-request-D1r0FkDI.mjs";
|
|
10
|
-
import { currentConfirmationPrompt
|
|
11
|
-
import { archivedRoutingMessages } from "../group-routing/routing-context.mjs";
|
|
11
|
+
import { currentConfirmationPrompt } from "../group-routing/messages.mjs";
|
|
12
12
|
import { parseArchivedMessage } from "./archive-message.mjs";
|
|
13
13
|
import { contentShape, messageContent } from "./content.mjs";
|
|
14
|
-
import { messageMetadata } from "./
|
|
14
|
+
import { messageMetadata } from "./message-metadata.mjs";
|
|
15
15
|
import { reportMembership } from "./membership.mjs";
|
|
16
16
|
import { createHash, randomUUID } from "node:crypto";
|
|
17
17
|
const captures = new WeakMap();
|
|
@@ -177,20 +177,8 @@ async function captureTelegramWebhook(input) {
|
|
|
177
177
|
};
|
|
178
178
|
const inbound = currentInboundRequest();
|
|
179
179
|
if (inbound !== void 0) captures.set(inbound, capture);
|
|
180
|
-
const chat = observedChatKey({
|
|
181
|
-
chatId: message.chat.id,
|
|
182
|
-
...message.messageThreadId === void 0 ? {} : { messageThreadId: message.messageThreadId }
|
|
183
|
-
});
|
|
184
180
|
const automated = message.from === void 0 || message.from.isBot || message.chat.type === "channel";
|
|
185
181
|
if (automated || edited !== void 0 || message.raw.left_chat_member || message.raw.migrate_to_chat_id || message.raw.migrate_from_chat_id) await capture.persist();
|
|
186
|
-
const history = automated ? void 0 : await input.archive.context({
|
|
187
|
-
provider: "telegram",
|
|
188
|
-
chatId: message.chat.id,
|
|
189
|
-
...event.threadId === null ? {} : { threadId: event.threadId },
|
|
190
|
-
limit: 100,
|
|
191
|
-
includeDecisions: true,
|
|
192
|
-
...lookup?.status === "paired" ? { participantId: lookup.member.id } : {}
|
|
193
|
-
});
|
|
194
182
|
const messageHistory = automated ? [] : (await input.archive.context({
|
|
195
183
|
provider: "telegram",
|
|
196
184
|
chatId: message.chat.id,
|
|
@@ -265,35 +253,16 @@ async function captureTelegramWebhook(input) {
|
|
|
265
253
|
await recordInboundContent({
|
|
266
254
|
text: [content.text, ...transcripts].filter(Boolean).join("\n\n"),
|
|
267
255
|
decoded,
|
|
268
|
-
...media === void 0 ? {} : { media }
|
|
256
|
+
...media === void 0 ? {} : { media },
|
|
257
|
+
...Array.isArray(previousRouting.files) ? { files: previousRouting.files } : {}
|
|
269
258
|
});
|
|
270
259
|
}
|
|
271
260
|
}
|
|
272
|
-
if (!automated) for (const earlier of input.observed.recent(chat).length === 0 ? archivedRoutingMessages((history?.events ?? []).filter((item) => item.eventId !== event.eventId), 60) : []) if ("role" in earlier || history?.events.some((decision) => decision.kind === "decision" && decision.messageId === earlier.messageId && decision.reason === "turn_admitted")) input.observed.remember(chat, earlier);
|
|
273
|
-
else input.observed.add(chat, earlier);
|
|
274
261
|
if (automated || edited !== void 0) {
|
|
275
262
|
if (message.chat.type === "group" || message.chat.type === "supergroup") await input.chats?.seen({
|
|
276
263
|
chatId: message.chat.id,
|
|
277
264
|
title: message.chat.title ?? ""
|
|
278
265
|
});
|
|
279
|
-
const remembered = input.observed.recent(chat).findLast((entry) => entry.messageId === message.messageId);
|
|
280
|
-
const retained = alreadyHandled && edited === void 0 ? remembered : void 0;
|
|
281
|
-
const retainedContent = archivedRoutingMessages([{
|
|
282
|
-
...capture.event,
|
|
283
|
-
sequence: "1",
|
|
284
|
-
storedAt: event.at
|
|
285
|
-
}])[0];
|
|
286
|
-
input.observed.add(chat, retained ?? (retainedContent === void 0 ? void 0 : {
|
|
287
|
-
...retainedContent,
|
|
288
|
-
...unchangedAttachments && remembered?.images !== void 0 ? { images: remembered.images } : {}
|
|
289
|
-
}) ?? {
|
|
290
|
-
...messageMetadata(message),
|
|
291
|
-
author: event.senderName ?? event.senderId ?? "automated post",
|
|
292
|
-
text: event.text ?? "[attachment or service message]",
|
|
293
|
-
at: event.at,
|
|
294
|
-
messageId: message.messageId,
|
|
295
|
-
...event.senderId === null ? {} : { authorId: event.senderId }
|
|
296
|
-
});
|
|
297
266
|
capture.decision = "silent";
|
|
298
267
|
capture.reason = automated ? "automated_or_channel_message" : alreadyHandled ? "message_already_handled" : "message_edit";
|
|
299
268
|
if (alreadyHandled) capture.note = "This message already started a turn; edits and retries do not repeat its task.";
|
|
@@ -495,7 +464,9 @@ async function recordInboundContent(content) {
|
|
|
495
464
|
await capture.persist();
|
|
496
465
|
const metadata = record(JSON.parse(capture.event.argsSummary ?? "{}"));
|
|
497
466
|
const membership = capture.lookup === void 0 ? record(metadata.routing).membership : capture.lookup.status === "paired" || capture.lookup.status === "existing" || capture.lookup.status === "created" ? "linked" : "unlinked";
|
|
498
|
-
|
|
467
|
+
const author = content.author ?? record(metadata.routing).author;
|
|
468
|
+
const files = content.files ?? record(metadata.routing).files;
|
|
469
|
+
if (content.text === capture.event.text && content.media === record(metadata.routing).media && membership === record(metadata.routing).membership && author === record(metadata.routing).author && JSON.stringify(files) === JSON.stringify(record(metadata.routing).files) && JSON.stringify(content.decoded) === JSON.stringify(record(metadata.routing).decoded)) return;
|
|
499
470
|
const contentHash = createHash("sha256").update(JSON.stringify({
|
|
500
471
|
...content,
|
|
501
472
|
membership
|
|
@@ -509,6 +480,8 @@ async function recordInboundContent(content) {
|
|
|
509
480
|
routing: {
|
|
510
481
|
...record(metadata.routing),
|
|
511
482
|
membership,
|
|
483
|
+
...typeof author === "string" ? { author: author.slice(0, 500) } : {},
|
|
484
|
+
...Array.isArray(files) && files.length > 0 ? { files: files.slice(0, 10) } : {},
|
|
512
485
|
media: content.media?.slice(0, 8e3),
|
|
513
486
|
decoded: content.decoded
|
|
514
487
|
}
|
|
@@ -582,6 +555,8 @@ function captureTelegramFetch(fetchImpl, archive, identities) {
|
|
|
582
555
|
const chatId = id(body.chat_id);
|
|
583
556
|
if (chatId === null) return fetchImpl(input, init);
|
|
584
557
|
const { capture, chatType, participantIds } = await outboundAudience(chatId, identities);
|
|
558
|
+
const delivery = currentDeliveryScope();
|
|
559
|
+
const own = delivery?.chatId === chatId && delivery.sessionId !== null;
|
|
585
560
|
const event = conversationEvent({
|
|
586
561
|
eventId: `telegram:${edit ? "edit" : "send"}:${randomUUID()}`,
|
|
587
562
|
provider: "telegram",
|
|
@@ -620,6 +595,10 @@ function captureTelegramFetch(fetchImpl, archive, identities) {
|
|
|
620
595
|
participantIds,
|
|
621
596
|
threadId: id(body.message_thread_id) ?? currentThreadId() ?? null,
|
|
622
597
|
replyToMessageId: id(body.reply_to_message_id) ?? id(record(body.reply_parameters).message_id),
|
|
598
|
+
...own ? {
|
|
599
|
+
sessionId: delivery.sessionId,
|
|
600
|
+
turnId: delivery.turnId
|
|
601
|
+
} : {},
|
|
623
602
|
status: "requested"
|
|
624
603
|
});
|
|
625
604
|
await archive.append(event);
|
|
@@ -5,8 +5,11 @@ interface Diagnostic {
|
|
|
5
5
|
readonly error: unknown;
|
|
6
6
|
readonly metadata: Record<string, unknown>;
|
|
7
7
|
}
|
|
8
|
+
type DeliveryScope = Pick<ConversationEvent, "chatId" | "chatType" | "threadId" | "sessionId" | "turnId">;
|
|
8
9
|
/** Receives the same credential-redacted payload as operator logs, never raw provider errors. */
|
|
9
10
|
export declare function retainDeliveryDiagnostic(diagnostic: Diagnostic): void;
|
|
11
|
+
/** The session and turn a channel callback delivers for, so its sends are archived as its own. */
|
|
12
|
+
export declare function currentDeliveryScope(): DeliveryScope | undefined;
|
|
10
13
|
/** Await archive writes inside the channel callback, including failed or restored callbacks. */
|
|
11
|
-
export declare function withDeliveryDiagnostics<T>(archive: Conversations, scope:
|
|
14
|
+
export declare function withDeliveryDiagnostics<T>(archive: Conversations, scope: DeliveryScope, run: () => T | Promise<T>): Promise<T>;
|
|
12
15
|
export {};
|
|
@@ -10,11 +10,17 @@ import { conversationEvent } from "../conversations.mjs";
|
|
|
10
10
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
11
11
|
const pending = new AsyncLocalStorage();
|
|
12
12
|
function retainDeliveryDiagnostic(diagnostic) {
|
|
13
|
-
pending.getStore()?.push(diagnostic);
|
|
13
|
+
pending.getStore()?.diagnostics.push(diagnostic);
|
|
14
|
+
}
|
|
15
|
+
function currentDeliveryScope() {
|
|
16
|
+
return pending.getStore()?.scope;
|
|
14
17
|
}
|
|
15
18
|
async function withDeliveryDiagnostics(archive, scope, run) {
|
|
16
19
|
const diagnostics = [];
|
|
17
|
-
return pending.run(
|
|
20
|
+
return pending.run({
|
|
21
|
+
diagnostics,
|
|
22
|
+
scope
|
|
23
|
+
}, async () => {
|
|
18
24
|
try {
|
|
19
25
|
return await run();
|
|
20
26
|
} finally {
|
|
@@ -43,4 +49,4 @@ async function withDeliveryDiagnostics(archive, scope, run) {
|
|
|
43
49
|
}
|
|
44
50
|
});
|
|
45
51
|
}
|
|
46
|
-
export { retainDeliveryDiagnostic, withDeliveryDiagnostics };
|
|
52
|
+
export { currentDeliveryScope, retainDeliveryDiagnostic, withDeliveryDiagnostics };
|
|
@@ -5,7 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
|
|
|
5
5
|
__eveDirname(__filename);
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { runtimeRequestFailure } from "./runtime-request.mjs";
|
|
8
|
-
import { t as agentBuild } from "../../../_chunks/build-metadata-
|
|
8
|
+
import { t as agentBuild } from "../../../_chunks/build-metadata-R_ssK2uR.mjs";
|
|
9
9
|
import { GET } from "eve/channels";
|
|
10
10
|
const HEALTH_ROUTE = "/eve/v1/telegram/health";
|
|
11
11
|
function healthRoute(input) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type StructuredInbound, type StructuredMessage, type StructuredResult } from "./structured.js";
|
|
2
|
-
import type { RecentMessage } from "../group-routing/
|
|
2
|
+
import type { RecentMessage } from "../group-routing/messages.js";
|
|
3
3
|
import { type GroupPolicy } from "../group-routing/policy.js";
|
|
4
4
|
import type { TelegramSettings } from "../../config.js";
|
|
5
5
|
import { attachmentLifecycle } from "./lifecycle.js";
|
|
@@ -12,9 +12,8 @@ import { type AgentChats } from "./chats.js";
|
|
|
12
12
|
import type { InboundRequest } from "./inbound-request.js";
|
|
13
13
|
import type { ImageDescriber } from "./images.js";
|
|
14
14
|
import type { MediaPlan } from "./media-policy.js";
|
|
15
|
-
import { type ObservedMessages } from "./observed.js";
|
|
16
15
|
import { type RouteDecider } from "./router.js";
|
|
17
|
-
import { type HistoryReader
|
|
16
|
+
import { type HistoryReader } from "./sessions.js";
|
|
18
17
|
import { type Transcriber } from "./voice.js";
|
|
19
18
|
import { type AttachmentDescriber } from "./attachment-description.js";
|
|
20
19
|
import type { PdfTextExtractor } from "./pdf-text.js";
|
|
@@ -30,16 +29,18 @@ import type { PdfTextExtractor } from "./pdf-text.js";
|
|
|
30
29
|
* 2. Voice becomes text, so the router and the agent read what was said.
|
|
31
30
|
* 3. The router answers or observes. Group files follow the same addressing rules as text;
|
|
32
31
|
* captionless uploads use smart routing and recent agent requests. Files are stored after the
|
|
33
|
-
* registry knows the sender. Observing starts no session or typing indication;
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
32
|
+
* registry knows the sender. Observing starts no session or typing indication; the
|
|
33
|
+
* conversation archive keeps the message and its stored files' references for routing, the
|
|
34
|
+
* next turn's chat context and the native images that turn carries.
|
|
35
|
+
* 4. An answered message continues the session its address names under the configured scope; eve
|
|
36
|
+
* compacts and expires sessions itself.
|
|
37
37
|
* 5. An answered message reads the stored attachments in place of the Telegram files.
|
|
38
|
-
* 6. An answered message dispatches as the paired member, with
|
|
39
|
-
*
|
|
40
|
-
* an `<attachments>` block naming its stored files
|
|
41
|
-
*
|
|
42
|
-
* its
|
|
38
|
+
* 6. An answered message dispatches as the paired member, with only its own facts as context: a
|
|
39
|
+
* `<speaker>` block naming the member, their roles and the message they replied to, its
|
|
40
|
+
* `<message_audience>` and an `<attachments>` block naming its stored files. The chat around
|
|
41
|
+
* it, observed messages and the registry's `<chat>` guidance, reaches the session once through
|
|
42
|
+
* its chat context (`lib/chat-context.ts`). An answered group message waits for its chat's
|
|
43
|
+
* report first, so that a chat new to the process already carries its guidance.
|
|
43
44
|
*/
|
|
44
45
|
export interface InboundDependencies {
|
|
45
46
|
readonly structured?: {
|
|
@@ -49,12 +50,12 @@ export interface InboundDependencies {
|
|
|
49
50
|
readonly policy: TelegramPolicy;
|
|
50
51
|
readonly decisionModel?: string;
|
|
51
52
|
readonly groups?: GroupPolicy;
|
|
53
|
+
/** The chat's or forum topic's recent messages from the conversation archive, oldest first. */
|
|
52
54
|
readonly recentConversation?: (chatId: string, threadId?: string) => Promise<ReadonlyArray<RecentMessage>>;
|
|
53
55
|
readonly signalSettings?: TelegramSettings["groups"];
|
|
54
56
|
readonly lifecycle?: (principal: import("eve/context").SessionAuthContext, memberId: string, message: TelegramMessage) => ReturnType<typeof attachmentLifecycle>;
|
|
55
57
|
readonly admission: () => AdmissionSettings;
|
|
56
58
|
readonly sessions: () => SessionSettings;
|
|
57
|
-
readonly sessionPolicy: SessionPolicy;
|
|
58
59
|
readonly sttModel: string | false;
|
|
59
60
|
readonly attachmentsModel: string | false;
|
|
60
61
|
readonly describeAttachment: AttachmentDescriber;
|
|
@@ -73,7 +74,6 @@ export interface InboundDependencies {
|
|
|
73
74
|
}) => Promise<Uint8Array>;
|
|
74
75
|
/** The attachment store; without it, files reach the turn as eve's Telegram file parts. */
|
|
75
76
|
readonly attachments?: AgentAttachments;
|
|
76
|
-
readonly observed: ObservedMessages;
|
|
77
77
|
/** Recent session events for smart routing. */
|
|
78
78
|
readonly readHistory: HistoryReader;
|
|
79
79
|
readonly chats: AgentChats;
|