@segmentstream/agent 0.9.0 → 0.10.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 +8 -0
- package/dist/_chunks/{build-metadata-DuHeGf1m.mjs → build-metadata-CYEMFYjL.mjs} +1 -1
- package/dist/_chunks/{inbound-request-B8Eyjz1O.mjs → inbound-request-BrWspFw5.mjs} +2 -1
- package/dist/extension/extension.d.ts +2 -0
- package/dist/extension/extension.mjs +3 -1
- package/dist/extension/hooks/group-routing.d.ts +2 -0
- package/dist/extension/hooks/group-routing.mjs +10 -0
- package/dist/extension/lib/build-metadata.mjs +1 -1
- package/dist/extension/lib/conversation-runtime.d.ts +1 -1
- package/dist/extension/lib/conversation-runtime.mjs +23 -1
- package/dist/extension/lib/group-routing/burst.d.ts +28 -0
- package/dist/extension/lib/group-routing/burst.mjs +64 -0
- package/dist/extension/lib/group-routing/conversation-prompt.d.ts +10 -0
- package/dist/extension/lib/group-routing/conversation-prompt.mjs +16 -0
- package/dist/extension/lib/group-routing/conversation-router.d.ts +58 -0
- package/dist/extension/lib/group-routing/conversation-router.mjs +182 -0
- package/dist/extension/lib/group-routing/conversation.d.ts +46 -0
- package/dist/extension/lib/group-routing/conversation.mjs +262 -0
- package/dist/extension/lib/group-routing/dequeue.d.ts +5 -0
- package/dist/extension/lib/group-routing/dequeue.mjs +141 -0
- package/dist/extension/lib/group-routing/observed.d.ts +2 -0
- package/dist/extension/lib/group-routing/policy.d.ts +19 -1
- package/dist/extension/lib/group-routing/policy.mjs +49 -11
- package/dist/extension/lib/group-routing/replay.d.ts +2 -2
- package/dist/extension/lib/group-routing/replay.mjs +51 -11
- package/dist/extension/lib/group-routing/router.d.ts +8 -1
- package/dist/extension/lib/group-routing/router.mjs +65 -0
- package/dist/extension/lib/group-routing/routing-context.mjs +10 -0
- package/dist/extension/lib/telegram/adapter.mjs +10 -2
- package/dist/extension/lib/telegram/attachment-description.mjs +1 -1
- package/dist/extension/lib/telegram/attachments-send.mjs +1 -1
- package/dist/extension/lib/telegram/confirmation-expiry.mjs +1 -1
- package/dist/extension/lib/telegram/confirmations.mjs +1 -1
- package/dist/extension/lib/telegram/conversations.mjs +1 -1
- package/dist/extension/lib/telegram/customer-text.mjs +1 -1
- package/dist/extension/lib/telegram/deferred-confirmations.mjs +1 -1
- 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.d.ts +2 -0
- package/dist/extension/lib/telegram/inbound-request.mjs +1 -1
- package/dist/extension/lib/telegram/inbound.d.ts +2 -0
- package/dist/extension/lib/telegram/inbound.mjs +122 -11
- 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/turn-errors.mjs +1 -1
- package/dist/extension/lib/telegram/voice.mjs +1 -1
- package/package.json +2 -2
|
@@ -4,19 +4,34 @@ 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 { routeMessage } from "./router.mjs";
|
|
7
|
+
import { decisionRouter, routeMessage } from "./router.mjs";
|
|
8
8
|
async function replayRouterPayload(input) {
|
|
9
9
|
const policy = input.policy ?? input.payload.policy;
|
|
10
10
|
const signals = input.payload.signals;
|
|
11
11
|
const state = input.payload.state;
|
|
12
12
|
const questions = input.payload.questions;
|
|
13
13
|
const answer = input.payload.answer;
|
|
14
|
-
if (signals
|
|
14
|
+
if (!signals || !policy || input.payload.truncated === true) return {
|
|
15
15
|
status: "unavailable",
|
|
16
16
|
reason: "stored_router_input_unavailable"
|
|
17
17
|
};
|
|
18
|
-
const
|
|
18
|
+
const conversation = policy.smart.engagement.policy === "conversation";
|
|
19
|
+
const captured = input.payload.question;
|
|
20
|
+
const storedAddressing = answer && "addressee" in answer ? answer : void 0;
|
|
21
|
+
if (conversation && !input.decide && !storedAddressing) return {
|
|
22
|
+
status: "unavailable",
|
|
23
|
+
reason: "stored_conversation_classifier_unavailable"
|
|
24
|
+
};
|
|
25
|
+
let description = String(state?.agent ?? "");
|
|
26
|
+
if (conversation) try {
|
|
27
|
+
const a = JSON.parse(description);
|
|
28
|
+
if (typeof a.role === "string") description = a.role;
|
|
29
|
+
} catch {}
|
|
19
30
|
const decisions = [];
|
|
31
|
+
const fresh = input.decide ? decisionRouter({
|
|
32
|
+
decide: input.decide,
|
|
33
|
+
model: policy.smart.model
|
|
34
|
+
}) : void 0;
|
|
20
35
|
return {
|
|
21
36
|
status: "replayed",
|
|
22
37
|
route: await routeMessage({
|
|
@@ -29,25 +44,50 @@ async function replayRouterPayload(input) {
|
|
|
29
44
|
pendingQuestionAt: typeof input.payload.pending_question_at === "string" ? input.payload.pending_question_at : void 0,
|
|
30
45
|
recipientAnswer: input.payload.recipient_answer === true,
|
|
31
46
|
question: async () => ({
|
|
47
|
+
...captured,
|
|
32
48
|
signals,
|
|
33
49
|
groups: policy,
|
|
34
|
-
agent:
|
|
35
|
-
recent,
|
|
50
|
+
agent: description,
|
|
51
|
+
recent: captured?.recent ?? [],
|
|
36
52
|
message: {
|
|
37
53
|
author: signals.sender.displayName ?? signals.sender.externalId,
|
|
38
54
|
authorId: signals.sender.externalId,
|
|
55
|
+
messageId: signals.message.id,
|
|
39
56
|
text: signals.message.text,
|
|
40
57
|
at: signals.message.at
|
|
41
|
-
}
|
|
58
|
+
},
|
|
59
|
+
burstId: typeof input.payload.burst_id === "string" ? input.payload.burst_id : void 0
|
|
42
60
|
}),
|
|
43
|
-
decide: async () => {
|
|
44
|
-
if (
|
|
45
|
-
|
|
61
|
+
decide: async (q) => {
|
|
62
|
+
if (conversation) {
|
|
63
|
+
if (fresh) return fresh(q);
|
|
64
|
+
const p = storedAddressing?.addressee.probabilities?.agent;
|
|
65
|
+
if (p === void 0) throw new Error("Stored addressing probability unavailable.");
|
|
66
|
+
return {
|
|
67
|
+
route: "reply",
|
|
68
|
+
diagnostics: {
|
|
69
|
+
model: policy.smart.model,
|
|
70
|
+
latencyMs: 0,
|
|
71
|
+
probabilities: {
|
|
72
|
+
reply: p,
|
|
73
|
+
observe: 1 - p
|
|
74
|
+
},
|
|
75
|
+
addressing: storedAddressing
|
|
76
|
+
},
|
|
77
|
+
router: {
|
|
78
|
+
state,
|
|
79
|
+
questions,
|
|
80
|
+
answer
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (!state || !questions || !answer || !("choice" in answer)) throw new Error("Stored classifier contract unavailable.");
|
|
85
|
+
const result = input.decide ? (await input.decide({
|
|
46
86
|
model: policy.smart.model,
|
|
47
87
|
state,
|
|
48
88
|
questions,
|
|
49
89
|
abortSignal: AbortSignal.timeout(Math.ceil(policy.smart.timeoutSeconds * 1e3))
|
|
50
|
-
})).answers.route;
|
|
90
|
+
})).answers.route : answer;
|
|
51
91
|
return {
|
|
52
92
|
route: result.choice,
|
|
53
93
|
diagnostics: {
|
|
@@ -62,7 +102,7 @@ async function replayRouterPayload(input) {
|
|
|
62
102
|
}
|
|
63
103
|
};
|
|
64
104
|
},
|
|
65
|
-
record: (
|
|
105
|
+
record: (d) => decisions.push(d)
|
|
66
106
|
}),
|
|
67
107
|
decisions
|
|
68
108
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type ConversationAnalysis } from "./conversation.js";
|
|
2
|
+
import { type AddressingAnswers } from "./conversation-router.js";
|
|
1
3
|
import { type GroupPolicy } from "./policy.js";
|
|
2
4
|
import type { ObservedMessage, PendingConfirmation, RecentMessage } from "./observed.js";
|
|
3
5
|
import { type MessageAudience, type AgentIdentity as BotIdentity, type AddressingReason, type GroupMessageSignals } from "./signals.js";
|
|
@@ -7,6 +9,10 @@ export type Route = "observe" | "reply";
|
|
|
7
9
|
/** What the decision model sees. */
|
|
8
10
|
export interface RouteQuestion {
|
|
9
11
|
readonly groups?: GroupPolicy;
|
|
12
|
+
readonly conversation?: ConversationAnalysis;
|
|
13
|
+
readonly burstId?: string;
|
|
14
|
+
readonly processingAt?: string;
|
|
15
|
+
readonly messagesToClassify?: ReadonlyArray<ObservedMessage>;
|
|
10
16
|
readonly signals?: GroupMessageSignals;
|
|
11
17
|
readonly agentInstructions?: string;
|
|
12
18
|
/** The agent's own description, so the model knows what it can be asked for. */
|
|
@@ -39,6 +45,7 @@ export interface RouteDecision {
|
|
|
39
45
|
export interface RouteDiagnostics {
|
|
40
46
|
readonly model: string;
|
|
41
47
|
readonly latencyMs: number;
|
|
48
|
+
readonly addressing?: AddressingAnswers;
|
|
42
49
|
readonly probabilities?: Readonly<Record<Route, number>>;
|
|
43
50
|
}
|
|
44
51
|
/** Decides whether a group message not addressed to the bot is still for it. */
|
|
@@ -104,6 +111,6 @@ export declare function routeMessage(input: {
|
|
|
104
111
|
readonly record?: (decision: RouteDecision) => void;
|
|
105
112
|
/** Builds the question only when a decision is needed: it reads the agent's description. */
|
|
106
113
|
readonly question: () => Promise<RouteQuestion>;
|
|
107
|
-
readonly beforeDecide?: () => Promise<boolean>;
|
|
114
|
+
readonly beforeDecide?: (direct?: boolean) => Promise<boolean>;
|
|
108
115
|
readonly decide: RouteDecider;
|
|
109
116
|
}): Promise<Route>;
|
|
@@ -6,6 +6,8 @@ __eveDirname(__filename);
|
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { DEFAULT_GROUP_POLICY, groupPolicy, overlaySmartPolicy } from "./policy.mjs";
|
|
8
8
|
import { addressingReason, otherRecipient, signalAudience } from "./signals.mjs";
|
|
9
|
+
import { conversationState, conversationThreshold } from "./conversation.mjs";
|
|
10
|
+
import { conversationDecisionRouter } from "./conversation-router.mjs";
|
|
9
11
|
import { recordRoutingMetric } from "./health.mjs";
|
|
10
12
|
import { humanAddressee, humanThread, questionInvitation, restoreEngagement } from "./engagement.mjs";
|
|
11
13
|
import { boundedRouterPayload, policyHash } from "./record.mjs";
|
|
@@ -172,6 +174,7 @@ function conversationContext(question) {
|
|
|
172
174
|
}
|
|
173
175
|
function decisionRouter(input) {
|
|
174
176
|
return async (question) => {
|
|
177
|
+
if (question.groups?.smart.engagement.policy === "conversation") return conversationDecisionRouter(input.decide)(question);
|
|
175
178
|
const policy = question.groups?.smart;
|
|
176
179
|
const started = performance.now();
|
|
177
180
|
const abortSignal = AbortSignal.timeout(Math.ceil((policy?.timeoutSeconds ?? 15) * 1e3));
|
|
@@ -292,6 +295,68 @@ async function routeMessage(input) {
|
|
|
292
295
|
});
|
|
293
296
|
return route;
|
|
294
297
|
};
|
|
298
|
+
if (groups.smart.engagement.policy === "conversation" && signals !== void 0 && signals.conversation.kind !== "direct") try {
|
|
299
|
+
question = {
|
|
300
|
+
...await input.question(),
|
|
301
|
+
groups,
|
|
302
|
+
signals
|
|
303
|
+
};
|
|
304
|
+
const analysis = conversationState({
|
|
305
|
+
signals,
|
|
306
|
+
recent: question.recent,
|
|
307
|
+
policy: groups,
|
|
308
|
+
agentNames: [...question.agentIdentity?.aliases ?? [], ...question.agentIdentity?.username ? [question.agentIdentity.username] : []],
|
|
309
|
+
pendingQuestionAt: input.pendingQuestionAt,
|
|
310
|
+
asOf: question.processingAt
|
|
311
|
+
});
|
|
312
|
+
question = {
|
|
313
|
+
...question,
|
|
314
|
+
conversation: analysis
|
|
315
|
+
};
|
|
316
|
+
payload = {
|
|
317
|
+
...payload,
|
|
318
|
+
burst_id: question.burstId,
|
|
319
|
+
path: analysis.path,
|
|
320
|
+
conversation: analysis.conversation,
|
|
321
|
+
human_exchange: analysis.human_exchange,
|
|
322
|
+
pending_question_voided: input.pendingQuestionAt !== void 0 && !analysis.pending_question_live,
|
|
323
|
+
question: {
|
|
324
|
+
recent: question.recent,
|
|
325
|
+
pendingQuestions: question.pendingQuestions,
|
|
326
|
+
agentIdentity: question.agentIdentity,
|
|
327
|
+
chat: question.chat,
|
|
328
|
+
processingAt: question.processingAt,
|
|
329
|
+
messagesToClassify: question.messagesToClassify
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
if (analysis.path === "A") {
|
|
333
|
+
if (input.beforeDecide && !await input.beforeDecide(true)) return complete("observe", "admission_refused", "Admission refused before direct turn.");
|
|
334
|
+
return complete("reply", addressed && addressed !== "pending-question" ? addressed : "mention", "Agent explicitly addressed.");
|
|
335
|
+
}
|
|
336
|
+
if (analysis.path === "H") return complete("observe", "human_addressee", "Message addresses another participant; conversation and question invitation closed.");
|
|
337
|
+
if (signals.addressing.eligible === false) return complete("observe", "channel_ineligible", "Channel excludes ambient messages.");
|
|
338
|
+
if (groups.mode === "mention") return complete("observe", "mention_policy", "Mention policy observes unaddressed messages.");
|
|
339
|
+
if (input.beforeDecide && !await input.beforeDecide()) return complete("observe", "admission_refused", "Admission refused before inference.");
|
|
340
|
+
const result = await input.decide(question);
|
|
341
|
+
const diagnostics = typeof result === "string" ? void 0 : result.diagnostics;
|
|
342
|
+
const probabilities = diagnostics?.addressing;
|
|
343
|
+
const threshold = conversationThreshold(analysis.path, groups);
|
|
344
|
+
const valid = probabilities !== void 0 && probabilities.addressee.choice === "agent" && (probabilities.addressee.probabilities?.agent ?? NaN) >= threshold && (probabilities.needs_response.probabilities?.true ?? NaN) >= (analysis.path === "B" || analysis.path === "X" ? .7 : .5) && (probabilities.ends_conversation.probabilities?.true ?? NaN) < .7;
|
|
345
|
+
payload = {
|
|
346
|
+
...payload,
|
|
347
|
+
...typeof result === "string" ? {} : result.router,
|
|
348
|
+
thresholds_applied: {
|
|
349
|
+
threshold,
|
|
350
|
+
path: analysis.path,
|
|
351
|
+
needs_response: analysis.path === "B" || analysis.path === "X" ? .7 : .5,
|
|
352
|
+
ends_conversation: .7
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
return complete(valid ? "reply" : "observe", `decision_model_${valid ? "reply" : "observe"}`, "Conversation addressing probabilities evaluated.", "decision-model", diagnostics);
|
|
356
|
+
} catch (error) {
|
|
357
|
+
console.error("[segmentstream] conversation routing failed", { error });
|
|
358
|
+
return complete("observe", error instanceof Error && error.name === "TimeoutError" ? "decision_model_timeout" : "decision_model_failed", "Conversation routing unavailable; observe fallback.");
|
|
359
|
+
}
|
|
295
360
|
if (addressed !== void 0 && addressed !== "pending-question" && addressed !== "reply-to-bot") return complete("reply", addressed, `Bot addressed directly: ${addressed}.`);
|
|
296
361
|
if (input.recipientAnswer && addressed === "reply-to-bot") return complete("reply", "pending-question", "Recipient supplied as the pending question's answer.");
|
|
297
362
|
const elsewhere = otherRecipient(audience);
|
|
@@ -34,6 +34,15 @@ function archivedRoutingMessages(events, limit = 30) {
|
|
|
34
34
|
"thread-with-agent",
|
|
35
35
|
"pending-question"
|
|
36
36
|
].includes(event.reason ?? "")).map((event) => [event.messageId, event.reason]));
|
|
37
|
+
const ended = new Set(events.filter((event) => {
|
|
38
|
+
if (event.kind !== "decision" || event.messageId === null) return false;
|
|
39
|
+
try {
|
|
40
|
+
const data = JSON.parse(event.argsSummary ?? "{}");
|
|
41
|
+
return data.router?.conversation_ended === true || data.routing?.conversationEnded === true;
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}).map((event) => event.messageId));
|
|
37
46
|
const membership = new Map();
|
|
38
47
|
for (const event of [...events].reverse()) {
|
|
39
48
|
if (event.kind !== "decision" || event.senderId === null) continue;
|
|
@@ -56,6 +65,7 @@ function archivedRoutingMessages(events, limit = 30) {
|
|
|
56
65
|
const text = (event.text || derivedText || (metadata.media ? "" : "[attachment or service message]")).slice(0, 2e3);
|
|
57
66
|
const common = {
|
|
58
67
|
invitation: metadata.invitation ?? invitations.get(event.messageId ?? ""),
|
|
68
|
+
...ended.has(event.messageId) ? { conversationEnded: true } : {},
|
|
59
69
|
author: event.senderName ?? event.senderId ?? "unknown sender",
|
|
60
70
|
text,
|
|
61
71
|
...metadata.media === void 0 ? {} : { media: metadata.media },
|
|
@@ -13,10 +13,11 @@ import { decisionRouter } from "../group-routing/router.mjs";
|
|
|
13
13
|
import { runtimeToken } from "../runtime-token.mjs";
|
|
14
14
|
import { groupSettings } from "../group-routing/settings.mjs";
|
|
15
15
|
import { conversations } from "../conversations.mjs";
|
|
16
|
-
import {
|
|
16
|
+
import { archivedRoutingMessages } from "../group-routing/routing-context.mjs";
|
|
17
|
+
import { t as agentBuild } from "../../../_chunks/build-metadata-CYEMFYjL.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, p as reportTelegramError, r as runInboundRequest, t as InboundRequest } from "../../../_chunks/inbound-request-BrWspFw5.mjs";
|
|
20
21
|
import { attachmentLifecycle } from "./lifecycle.mjs";
|
|
21
22
|
import { scopedAddress, sessionPolicy } from "./sessions.mjs";
|
|
22
23
|
import { observedMessages } from "./observed.mjs";
|
|
@@ -175,6 +176,13 @@ function segmentstreamTelegram(options) {
|
|
|
175
176
|
policy: settings().policy,
|
|
176
177
|
decisionModel: options.models().decision,
|
|
177
178
|
groups: options.groups?.(),
|
|
179
|
+
recentConversation: async (chatId, threadId) => archivedRoutingMessages((await archive.context({
|
|
180
|
+
provider: "telegram",
|
|
181
|
+
chatId,
|
|
182
|
+
...threadId === void 0 ? {} : { threadId },
|
|
183
|
+
limit: 200,
|
|
184
|
+
includeDecisions: true
|
|
185
|
+
})).events, 100),
|
|
178
186
|
cloudGroups: options.groups === void 0 ? void 0 : readGroups,
|
|
179
187
|
signalSettings: settings().groups,
|
|
180
188
|
recoverHeldApprovals: options.confirmationMode?.() === "deferred",
|
|
@@ -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-BrWspFw5.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 { c as customerCopy, f as formattingRefused, l as customerMessage, p as reportTelegramError } from "../../../_chunks/inbound-request-
|
|
13
|
+
import { c as customerCopy, f as formattingRefused, l as customerMessage, p as reportTelegramError } from "../../../_chunks/inbound-request-BrWspFw5.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";
|
|
@@ -8,7 +8,7 @@ import { proxyEnvironment } from "../environment.mjs";
|
|
|
8
8
|
import { runtimeToken } from "../runtime-token.mjs";
|
|
9
9
|
import { conversations } from "../conversations.mjs";
|
|
10
10
|
import { telegramSendScope } from "./send-queue.mjs";
|
|
11
|
-
import { m as safeTelegramFetch, p as reportTelegramError } from "../../../_chunks/inbound-request-
|
|
11
|
+
import { m as safeTelegramFetch, p as reportTelegramError } from "../../../_chunks/inbound-request-BrWspFw5.mjs";
|
|
12
12
|
import { deferredConfirmations } from "../deferred-confirmations.mjs";
|
|
13
13
|
import { expireDeferredRecord } from "./deferred-confirmations.mjs";
|
|
14
14
|
import { connectionProxy } from "../connection-proxy.mjs";
|
|
@@ -10,7 +10,7 @@ import { conversationEvent } from "../conversations.mjs";
|
|
|
10
10
|
import { humanText } from "../confirmation-description.mjs";
|
|
11
11
|
import { renderConfirmation, withConfirmationText } from "./confirmation-text.mjs";
|
|
12
12
|
import { chatPostCard, chatPostCardStatus, chatPostReceipt, withChatPostCard } from "./chat-post-card.mjs";
|
|
13
|
-
import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-
|
|
13
|
+
import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-BrWspFw5.mjs";
|
|
14
14
|
import { chatPostOperationId, isChatPost } from "../chat-post-confirmation.mjs";
|
|
15
15
|
import { confirmations } from "../confirmations.mjs";
|
|
16
16
|
import { formatAddress, scopedAddress } from "./sessions.mjs";
|
|
@@ -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-BrWspFw5.mjs";
|
|
11
11
|
import { contentShape, messageContent } from "./content.mjs";
|
|
12
12
|
import { messageMetadata } from "./observed.mjs";
|
|
13
13
|
import { reportMembership } from "./membership.mjs";
|
|
@@ -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-BrWspFw5.mjs";
|
|
9
9
|
export { TelegramDeliveryError, currentLocale, currentThreadId, customerCopy, customerMessage, customerText, failureText, formattingRefused, isTechnicalText, reportTelegramError, safeTelegramFetch, withDeliveryLocale };
|
|
@@ -10,7 +10,7 @@ import { conversationEvent } from "../conversations.mjs";
|
|
|
10
10
|
import { humanText } from "../confirmation-description.mjs";
|
|
11
11
|
import { renderConfirmation, withConfirmationText } from "./confirmation-text.mjs";
|
|
12
12
|
import { chatPostCard, chatPostCardStatus, chatPostReceipt, withChatPostCard } from "./chat-post-card.mjs";
|
|
13
|
-
import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-
|
|
13
|
+
import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-BrWspFw5.mjs";
|
|
14
14
|
import { CONFIRMATION_TTL_MS, inputHash } from "../confirmations.mjs";
|
|
15
15
|
import { OntologyToolRejected } from "../ontology-tools.mjs";
|
|
16
16
|
import { confirmationOutcome } from "../confirmation-outcome.mjs";
|
|
@@ -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-BrWspFw5.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-CYEMFYjL.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-BrWspFw5.mjs";
|
|
8
8
|
import { gateway, generateText } from "ai";
|
|
9
9
|
const MAXIMUM_IMAGE_DESCRIPTION_BYTES = 5242880;
|
|
10
10
|
const MAXIMUM_NATIVE_IMAGE_BYTES = 3145728;
|
|
@@ -30,6 +30,8 @@ export declare class InboundRequest {
|
|
|
30
30
|
readonly agentDescription: () => Promise<string>;
|
|
31
31
|
readonly agentName: () => Promise<string>;
|
|
32
32
|
readonly traceId: `${string}-${string}-${string}-${string}-${string}`;
|
|
33
|
+
/** Conversation policy coalesces only the active caller; Eve queues other callers. */
|
|
34
|
+
turnPolicy: "queue" | "steer";
|
|
33
35
|
edited: boolean;
|
|
34
36
|
triggeringMessageId: string | undefined;
|
|
35
37
|
chatId: string | undefined;
|
|
@@ -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-BrWspFw5.mjs";
|
|
8
8
|
export { InboundRequest, currentInboundRequest, runInboundRequest, withReplacedText };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RecentMessage } from "../group-routing/observed.js";
|
|
1
2
|
import type { CloudGroupSettings } from "../group-routing/settings.js";
|
|
2
3
|
import { type GroupPolicy } from "../group-routing/policy.js";
|
|
3
4
|
import type { TelegramSettings } from "../../config.js";
|
|
@@ -44,6 +45,7 @@ export interface InboundDependencies {
|
|
|
44
45
|
readonly policy: TelegramPolicy;
|
|
45
46
|
readonly decisionModel?: string;
|
|
46
47
|
readonly groups?: GroupPolicy;
|
|
48
|
+
readonly recentConversation?: (chatId: string, threadId?: string) => Promise<ReadonlyArray<RecentMessage>>;
|
|
47
49
|
readonly cloudGroups?: () => Promise<CloudGroupSettings | undefined>;
|
|
48
50
|
readonly signalSettings?: TelegramSettings["groups"];
|
|
49
51
|
/** Recover old held sessions only when the workspace has chosen non-blocking confirmations. */
|
|
@@ -7,10 +7,12 @@ __eveCreateRequire(import.meta.url);
|
|
|
7
7
|
import { effectiveGroupPolicy, groupPolicy } from "../group-routing/policy.mjs";
|
|
8
8
|
import { conversationLanguage, telegramPrincipal } from "./principal.mjs";
|
|
9
9
|
import { addressingReason, conversationKey, otherRecipient } from "../group-routing/signals.mjs";
|
|
10
|
+
import { conversationAddressing, conversationState } from "../group-routing/conversation.mjs";
|
|
10
11
|
import { questionInvitation } from "../group-routing/engagement.mjs";
|
|
11
12
|
import { conversationContext, routeMessage } from "../group-routing/router.mjs";
|
|
12
13
|
import { observedContext } from "../group-routing/observed.mjs";
|
|
13
|
-
import {
|
|
14
|
+
import { debounceBurst, routingBurst } from "../group-routing/burst.mjs";
|
|
15
|
+
import { p as reportTelegramError } from "../../../_chunks/inbound-request-BrWspFw5.mjs";
|
|
14
16
|
import { startTypingHeartbeat, typingUntilComplete } from "./lifecycle.mjs";
|
|
15
17
|
import { formatAddress, messageAddress, scopedAddress } from "./sessions.mjs";
|
|
16
18
|
import { contentShape, messageContent } from "./content.mjs";
|
|
@@ -453,6 +455,19 @@ async function handleInboundMessage(input) {
|
|
|
453
455
|
mode: dependencies.policy,
|
|
454
456
|
model: dependencies.decisionModel
|
|
455
457
|
}), conversationKey(policySignals), registryChat?.scope !== void 0 && registryChat.scope !== policySignals.conversation.scope ? void 0 : registryChat?.routing, cloudGroups?.settings);
|
|
458
|
+
const conversationPolicy = group && groups.smart.engagement.policy === "conversation";
|
|
459
|
+
const agentAliases = [...groups.smart.engagement.agentAliases, ...[
|
|
460
|
+
bot?.username,
|
|
461
|
+
bot?.firstName,
|
|
462
|
+
bot?.displayName
|
|
463
|
+
].filter((n) => !!n)];
|
|
464
|
+
const freshConversation = async () => dependencies.recentConversation ? dependencies.recentConversation(sent.chat.id, sent.messageThreadId === void 0 ? void 0 : String(sent.messageThreadId)) : dependencies.observed.recent(chat);
|
|
465
|
+
let conversationRecent = conversationPolicy ? await freshConversation() : dependencies.observed.recent(chat);
|
|
466
|
+
if (conversationPolicy && input.request) {
|
|
467
|
+
address = topicAddress;
|
|
468
|
+
input.request.address = topicAddress;
|
|
469
|
+
input.request.turnPolicy = "steer";
|
|
470
|
+
}
|
|
456
471
|
const pending = questionFromHistory(questionHistory, questionOwner);
|
|
457
472
|
let answering = pending !== void 0 && (input.request?.lifecycleKey === void 0 || pending.taskId === input.request.lifecycleKey) && pending.memberId === member.id && pending.chatId === sent.chat.id && pending.messageThreadId === String(sent.messageThreadId ?? "");
|
|
458
473
|
const asked = questionHistory.findLast((event) => {
|
|
@@ -463,12 +478,21 @@ async function handleInboundMessage(input) {
|
|
|
463
478
|
});
|
|
464
479
|
const questionAge = Date.parse(sentAt(sent)) - Date.parse(asked?.meta.at ?? "");
|
|
465
480
|
if (answering && addressed === void 0 && !questionInvitation(asked?.meta.at, sentAt(sent), groups.smart.engagement.openQuestionMinutes)) answering = false;
|
|
466
|
-
|
|
467
|
-
|
|
481
|
+
if (conversationPolicy && answering) {
|
|
482
|
+
if (!conversationState({
|
|
483
|
+
signals: policySignals,
|
|
484
|
+
recent: conversationRecent,
|
|
485
|
+
policy: groups,
|
|
486
|
+
agentNames: agentAliases,
|
|
487
|
+
pendingQuestionAt: asked?.meta.at
|
|
488
|
+
}).pending_question_live) answering = false;
|
|
489
|
+
}
|
|
490
|
+
const recipientAnswer = !conversationPolicy && answering && (groups.smart.engagement.policy === "engaged" || questionAge >= 0 && questionAge <= groups.smart.engagement.followUpMinutes * 6e4) && audience.replyTo?.kind === "bot" && isRecipientAnswer(audience);
|
|
491
|
+
if (!conversationPolicy && answering && pending !== void 0 && input.request !== void 0) {
|
|
468
492
|
address = pending.address || (questionSession === void 0 ? topicAddress : memberQuestionAddress);
|
|
469
493
|
input.request.address = address;
|
|
470
494
|
}
|
|
471
|
-
if (input.request?.lifecycleKey === void 0 && group && (addressed === void 0 && !answering && groups.mode === "mention" || elsewhere !== void 0 && (addressed === void 0 || addressed === "reply-to-bot" && !recipientAnswer))) {
|
|
495
|
+
if (input.request?.lifecycleKey === void 0 && group && (addressed === void 0 && !answering && groups.mode === "mention" || !conversationPolicy && elsewhere !== void 0 && (addressed === void 0 || addressed === "reply-to-bot" && !recipientAnswer))) {
|
|
472
496
|
await recordInboundContent({
|
|
473
497
|
text,
|
|
474
498
|
decoded
|
|
@@ -480,7 +504,7 @@ async function handleInboundMessage(input) {
|
|
|
480
504
|
});
|
|
481
505
|
return null;
|
|
482
506
|
}
|
|
483
|
-
if (hasContent && answering && !await admitTurn(true)) {
|
|
507
|
+
if (!conversationPolicy && hasContent && answering && !await admitTurn(true)) {
|
|
484
508
|
await observeRaw();
|
|
485
509
|
return null;
|
|
486
510
|
}
|
|
@@ -529,7 +553,7 @@ async function handleInboundMessage(input) {
|
|
|
529
553
|
address,
|
|
530
554
|
operations,
|
|
531
555
|
read: dependencies.history ?? dependencies.readHistory,
|
|
532
|
-
remembered: dependencies.observed.recent(chat),
|
|
556
|
+
remembered: conversationPolicy ? conversationRecent : dependencies.observed.recent(chat),
|
|
533
557
|
at: sentAt(sent),
|
|
534
558
|
questionHistory,
|
|
535
559
|
questionSessionId: owner?.id
|
|
@@ -539,7 +563,8 @@ async function handleInboundMessage(input) {
|
|
|
539
563
|
name,
|
|
540
564
|
bot?.displayName,
|
|
541
565
|
bot?.firstName,
|
|
542
|
-
bot?.username
|
|
566
|
+
bot?.username,
|
|
567
|
+
...groups.smart.engagement.agentAliases
|
|
543
568
|
].filter((alias) => alias !== void 0 && alias !== ""))].slice(0, 8).map((alias) => alias.slice(0, 128));
|
|
544
569
|
question = {
|
|
545
570
|
groups,
|
|
@@ -558,7 +583,7 @@ async function handleInboundMessage(input) {
|
|
|
558
583
|
aliases
|
|
559
584
|
},
|
|
560
585
|
chat: group ? chatContext(dependencies.chats.chat(sent.chat.id)) : [],
|
|
561
|
-
recent: context.recent,
|
|
586
|
+
recent: conversationPolicy ? conversationRecent : context.recent,
|
|
562
587
|
pendingConfirmations: context.pendingConfirmations,
|
|
563
588
|
pendingQuestions: context.pendingQuestions.filter((question) => question.chatId === sent.chat.id && question.messageThreadId === String(sent.messageThreadId ?? "")),
|
|
564
589
|
message: observedMessage(sent, text, member, media, bot),
|
|
@@ -567,6 +592,48 @@ async function handleInboundMessage(input) {
|
|
|
567
592
|
};
|
|
568
593
|
return question;
|
|
569
594
|
};
|
|
595
|
+
let burstId;
|
|
596
|
+
if (conversationPolicy && input.request?.lifecycleKey === void 0) {
|
|
597
|
+
if (voice !== void 0) await transcribeVoice(true);
|
|
598
|
+
const routingSignals = {
|
|
599
|
+
...policySignals,
|
|
600
|
+
message: {
|
|
601
|
+
...policySignals.message,
|
|
602
|
+
text
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
const evidence = conversationAddressing(routingSignals, conversationRecent, groups, agentAliases);
|
|
606
|
+
if (!evidence.agent_named && !evidence.reply_to_agent && evidence.human_targets.length === 0) {
|
|
607
|
+
await recordInboundContent({
|
|
608
|
+
text,
|
|
609
|
+
decoded
|
|
610
|
+
});
|
|
611
|
+
const refreshed = await debounceBurst({
|
|
612
|
+
signals: routingSignals,
|
|
613
|
+
policy: groups,
|
|
614
|
+
read: freshConversation
|
|
615
|
+
});
|
|
616
|
+
conversationRecent = refreshed.recent;
|
|
617
|
+
question = void 0;
|
|
618
|
+
if (refreshed.burst.superseded) {
|
|
619
|
+
recordGateDecision("burst_superseded");
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
const burst = routingBurst(routingSignals, conversationRecent, groups);
|
|
624
|
+
burstId = burst.id;
|
|
625
|
+
const q = await conversation();
|
|
626
|
+
question = {
|
|
627
|
+
...q,
|
|
628
|
+
signals: routingSignals,
|
|
629
|
+
message: {
|
|
630
|
+
...q.message,
|
|
631
|
+
text
|
|
632
|
+
},
|
|
633
|
+
burstId: burst.id,
|
|
634
|
+
messagesToClassify: burst.messages
|
|
635
|
+
};
|
|
636
|
+
}
|
|
570
637
|
const route = input.request?.lifecycleKey !== void 0 ? "reply" : await routeMessage({
|
|
571
638
|
chatId: sent.chat.id,
|
|
572
639
|
memberId: member.id,
|
|
@@ -574,6 +641,10 @@ async function handleInboundMessage(input) {
|
|
|
574
641
|
groups,
|
|
575
642
|
signals: {
|
|
576
643
|
...policySignals,
|
|
644
|
+
message: {
|
|
645
|
+
...policySignals.message,
|
|
646
|
+
text
|
|
647
|
+
},
|
|
577
648
|
sender: {
|
|
578
649
|
...policySignals.sender,
|
|
579
650
|
member
|
|
@@ -586,7 +657,7 @@ async function handleInboundMessage(input) {
|
|
|
586
657
|
record: (decision) => {
|
|
587
658
|
if (decision.reason !== "admission_refused") recordRouteDecision(decision.route, addressed, groups.mode, decision);
|
|
588
659
|
},
|
|
589
|
-
beforeDecide: () => admitTurn(false),
|
|
660
|
+
beforeDecide: (direct) => admitTurn(direct ?? false),
|
|
590
661
|
decide: dependencies.decide,
|
|
591
662
|
question: async () => {
|
|
592
663
|
classified = true;
|
|
@@ -631,6 +702,19 @@ async function handleInboundMessage(input) {
|
|
|
631
702
|
decoded,
|
|
632
703
|
...processedMedia === void 0 ? {} : { media: processedMedia }
|
|
633
704
|
});
|
|
705
|
+
if (conversationPolicy) {
|
|
706
|
+
const current = await conversation();
|
|
707
|
+
const message = {
|
|
708
|
+
...current.message,
|
|
709
|
+
text,
|
|
710
|
+
media
|
|
711
|
+
};
|
|
712
|
+
question = {
|
|
713
|
+
...current,
|
|
714
|
+
message,
|
|
715
|
+
messagesToClassify: current.messagesToClassify?.map((m) => m.messageId === message.messageId ? message : m)
|
|
716
|
+
};
|
|
717
|
+
}
|
|
634
718
|
return conversation();
|
|
635
719
|
}
|
|
636
720
|
});
|
|
@@ -810,7 +894,7 @@ async function handleInboundMessage(input) {
|
|
|
810
894
|
}
|
|
811
895
|
if (voice !== void 0 && input.request !== void 0) input.request.text = text;
|
|
812
896
|
else if (voice !== void 0) notes.push(text);
|
|
813
|
-
if (answering && pending !== void 0 && input.request !== void 0) {
|
|
897
|
+
if (!conversationPolicy && answering && pending !== void 0 && input.request !== void 0) {
|
|
814
898
|
input.request.replyTo = pending.replyToMessageId ?? input.request.replyTo;
|
|
815
899
|
input.request.lifecycleKey = pending.taskId ?? input.request.lifecycleKey;
|
|
816
900
|
input.request.text = `${answerMarker(pending.id)}\n${text}`;
|
|
@@ -868,7 +952,7 @@ async function handleInboundMessage(input) {
|
|
|
868
952
|
return null;
|
|
869
953
|
}
|
|
870
954
|
dependencies.observed.remember(chat, observedMessage(sent, text, member, retainedMedia, bot));
|
|
871
|
-
|
|
955
|
+
let principal = telegramPrincipal({
|
|
872
956
|
message: {
|
|
873
957
|
...sent,
|
|
874
958
|
text
|
|
@@ -876,6 +960,32 @@ async function handleInboundMessage(input) {
|
|
|
876
960
|
member,
|
|
877
961
|
conversationLanguage: language
|
|
878
962
|
});
|
|
963
|
+
if (conversationPolicy && input.request?.lifecycleKey === void 0) principal = {
|
|
964
|
+
...principal,
|
|
965
|
+
attributes: {
|
|
966
|
+
...principal.attributes,
|
|
967
|
+
groupRouting: JSON.stringify({
|
|
968
|
+
policy: {
|
|
969
|
+
...groups,
|
|
970
|
+
chats: {}
|
|
971
|
+
},
|
|
972
|
+
signals: {
|
|
973
|
+
...policySignals,
|
|
974
|
+
message: {
|
|
975
|
+
...policySignals.message,
|
|
976
|
+
text
|
|
977
|
+
}
|
|
978
|
+
},
|
|
979
|
+
agent: question?.agent.slice(0, 600) ?? "",
|
|
980
|
+
agentIdentity: question?.agentIdentity,
|
|
981
|
+
chat: question?.chat,
|
|
982
|
+
burst_id: burstId ?? routingBurst(policySignals, conversationRecent, groups).id,
|
|
983
|
+
message: question?.message,
|
|
984
|
+
pendingQuestions: question?.pendingQuestions,
|
|
985
|
+
pendingQuestionAt: asked?.meta.at
|
|
986
|
+
})
|
|
987
|
+
}
|
|
988
|
+
};
|
|
879
989
|
return {
|
|
880
990
|
auth: answering && pending !== void 0 ? {
|
|
881
991
|
...principal,
|
|
@@ -923,6 +1033,7 @@ async function handleInboundMessage(input) {
|
|
|
923
1033
|
group
|
|
924
1034
|
}),
|
|
925
1035
|
...processedMedia === void 0 ? [] : [processedMedia],
|
|
1036
|
+
...conversationPolicy ? ["If this member declines your involvement or closes the conversation, output exactly [SILENT]. Do not acknowledge the rejection or perform actions."] : [],
|
|
926
1037
|
...notes
|
|
927
1038
|
]
|
|
928
1039
|
};
|