@segmentstream/agent 0.27.0 → 0.29.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/dist/_chunks/{build-metadata-R_ssK2uR.mjs → build-metadata-DZs-ivRD.mjs} +1 -1
- package/dist/extension/config.mjs +7 -1
- package/dist/extension/extension.d.ts +2 -2
- package/dist/extension/extension.mjs +3 -3
- 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 +1 -23
- package/dist/extension/lib/group-routing/addressing.d.ts +18 -0
- package/dist/extension/lib/group-routing/addressing.mjs +108 -0
- package/dist/extension/lib/group-routing/burst.d.ts +0 -17
- package/dist/extension/lib/group-routing/burst.mjs +2 -18
- package/dist/extension/lib/group-routing/conversation-prompt.d.ts +5 -2
- package/dist/extension/lib/group-routing/conversation-prompt.mjs +26 -6
- package/dist/extension/lib/group-routing/conversation-router.d.ts +8 -16
- package/dist/extension/lib/group-routing/conversation-router.mjs +49 -93
- package/dist/extension/lib/group-routing/messages.d.ts +0 -2
- package/dist/extension/lib/group-routing/policy.d.ts +15 -11
- package/dist/extension/lib/group-routing/policy.mjs +37 -29
- package/dist/extension/lib/group-routing/router.d.ts +0 -2
- package/dist/extension/lib/group-routing/router.mjs +9 -31
- package/dist/extension/lib/group-routing/routing-context.mjs +0 -10
- package/dist/extension/lib/telegram/adapter.mjs +1 -1
- package/dist/extension/lib/telegram/health.mjs +1 -1
- package/dist/extension/lib/telegram/inbound-request.d.ts +1 -1
- package/dist/extension/lib/telegram/inbound.mjs +1 -62
- package/dist/extension/lib/telegram/lifecycle.mjs +1 -1
- package/dist/extension/lib/telegram/turn-errors.mjs +0 -19
- package/package.json +1 -1
- package/dist/extension/hooks/group-routing.d.ts +0 -2
- package/dist/extension/hooks/group-routing.mjs +0 -10
- package/dist/extension/lib/group-routing/cancellation.d.ts +0 -4
- package/dist/extension/lib/group-routing/cancellation.mjs +0 -22
- package/dist/extension/lib/group-routing/conversation.d.ts +0 -46
- package/dist/extension/lib/group-routing/conversation.mjs +0 -269
- package/dist/extension/lib/group-routing/dequeue.d.ts +0 -5
- package/dist/extension/lib/group-routing/dequeue.mjs +0 -145
|
@@ -4,8 +4,15 @@ 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 { ADDRESSEE_CRITERIA,
|
|
8
|
-
|
|
7
|
+
import { ADDRESSEE_CRITERIA, NEEDS_RESPONSE_INSTRUCTIONS } from "./conversation-prompt.mjs";
|
|
8
|
+
const ADDRESSED_TO_AGENT = {
|
|
9
|
+
addressee: .5,
|
|
10
|
+
needsResponse: .5
|
|
11
|
+
};
|
|
12
|
+
const RECENT_CHARACTERS = 24e3;
|
|
13
|
+
function addressedToAgent(answers) {
|
|
14
|
+
return answers !== void 0 && answers.addressee.choice === "agent" && (answers.addressee.probabilities?.agent ?? NaN) >= ADDRESSED_TO_AGENT.addressee && (answers.needs_response.probabilities?.true ?? NaN) >= ADDRESSED_TO_AGENT.needsResponse;
|
|
15
|
+
}
|
|
9
16
|
function conversationQuestions(instructions) {
|
|
10
17
|
return {
|
|
11
18
|
addressee: {
|
|
@@ -20,28 +27,38 @@ function conversationQuestions(instructions) {
|
|
|
20
27
|
true: "Asks for or needs an answer or action.",
|
|
21
28
|
false: "Acknowledgement, reaction, status, closing or remark."
|
|
22
29
|
}
|
|
23
|
-
},
|
|
24
|
-
ends_conversation: {
|
|
25
|
-
type: "boolean",
|
|
26
|
-
instructions: ENDS_CONVERSATION_INSTRUCTIONS,
|
|
27
|
-
criteria: {
|
|
28
|
-
true: "Ends or declines the agent's involvement.",
|
|
29
|
-
false: "Anything else."
|
|
30
|
-
}
|
|
31
30
|
}
|
|
32
31
|
};
|
|
33
32
|
}
|
|
34
|
-
function conversationModelState(question
|
|
33
|
+
function conversationModelState(question) {
|
|
35
34
|
const groups = question.groups;
|
|
36
35
|
const now = Date.parse(question.processingAt ?? question.message.at);
|
|
37
36
|
const own = (m) => "role" in m && m.role === "agent";
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
const classified = question.messagesToClassify ?? [question.message];
|
|
38
|
+
const classifiedIds = new Set(classified.map((m) => m.messageId));
|
|
39
|
+
const format = (m, media) => JSON.stringify({
|
|
40
|
+
at: m.at,
|
|
41
|
+
id: m.messageId ?? null,
|
|
42
|
+
author: own(m) ? "THE AGENT" : m.author,
|
|
43
|
+
author_id: m.authorId ?? null,
|
|
44
|
+
text: m.text.slice(0, 1e3),
|
|
45
|
+
media: m.media?.slice(0, media),
|
|
46
|
+
reply_to: m.replyTo ?? null,
|
|
47
|
+
tags: m.taggedUsers ?? [],
|
|
48
|
+
attachments: m.attachmentKinds ?? []
|
|
49
|
+
});
|
|
50
|
+
const candidates = question.recent.filter((m) => !classifiedIds.has(m.messageId) && Date.parse(m.at) <= now).slice(-groups.smart.context.messages);
|
|
51
|
+
const lines = [];
|
|
52
|
+
let characters = 0;
|
|
53
|
+
for (const m of [...candidates].reverse()) {
|
|
54
|
+
const line = format(m, 500);
|
|
55
|
+
if (lines.length > 0 && characters + line.length > RECENT_CHARACTERS) break;
|
|
56
|
+
lines.unshift(line);
|
|
57
|
+
characters += line.length;
|
|
58
|
+
}
|
|
59
|
+
const recent = candidates.slice(candidates.length - lines.length);
|
|
43
60
|
const roster = new Map();
|
|
44
|
-
for (const m of [...recent,
|
|
61
|
+
for (const m of [...recent, ...classified]) {
|
|
45
62
|
if (own(m)) continue;
|
|
46
63
|
const id = m.authorId ?? m.username ?? m.author;
|
|
47
64
|
roster.set(id, {
|
|
@@ -52,16 +69,6 @@ function conversationModelState(question, analysis) {
|
|
|
52
69
|
kind: m.kind ?? "human"
|
|
53
70
|
});
|
|
54
71
|
}
|
|
55
|
-
const format = (m) => JSON.stringify({
|
|
56
|
-
at: m.at,
|
|
57
|
-
author: own(m) ? "THE AGENT" : m.author,
|
|
58
|
-
author_id: m.authorId ?? null,
|
|
59
|
-
text: m.text.slice(0, 1e3),
|
|
60
|
-
media: m.media?.slice(0, 2e3),
|
|
61
|
-
reply_to: m.replyTo ?? null,
|
|
62
|
-
tags: m.taggedUsers ?? [],
|
|
63
|
-
attachments: m.attachmentKinds ?? []
|
|
64
|
-
});
|
|
65
72
|
return {
|
|
66
73
|
agent: JSON.stringify({
|
|
67
74
|
names: [...new Set([
|
|
@@ -77,47 +84,14 @@ function conversationModelState(question, analysis) {
|
|
|
77
84
|
purpose: (groups.smart.context.includeChatGuidance ? question.chat ?? [] : []).join("\n").slice(0, 500)
|
|
78
85
|
}),
|
|
79
86
|
participants: groups.smart.context.includeParticipants ? [...roster.values()].map((p) => JSON.stringify(p)) : [],
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
A: "explicit",
|
|
83
|
-
H: "human_target",
|
|
84
|
-
C: "continuation",
|
|
85
|
-
Ci: "inferred_continuation",
|
|
86
|
-
C2: "contested",
|
|
87
|
-
B: "ambient",
|
|
88
|
-
X: "human_exchange"
|
|
89
|
-
}[analysis.path],
|
|
90
|
-
conversation_with_sender: {
|
|
91
|
-
...analysis.conversation,
|
|
92
|
-
seconds_since_last_exchange: analysis.conversation.last_exchange_at ? Math.floor((now - Date.parse(analysis.conversation.last_exchange_at)) / 1e3) : null,
|
|
93
|
-
agent_last_message_to_sender: last ? {
|
|
94
|
-
text: last.text.slice(0, 600),
|
|
95
|
-
seconds_ago: Math.floor((now - Date.parse(last.at)) / 1e3)
|
|
96
|
-
} : null,
|
|
97
|
-
agent_open_question: pending ? {
|
|
98
|
-
text: pending.question.slice(0, 300),
|
|
99
|
-
seconds_ago: pending.at ? Math.floor((now - Date.parse(pending.at)) / 1e3) : null
|
|
100
|
-
} : null
|
|
101
|
-
},
|
|
102
|
-
human_exchange: analysis.human_exchange,
|
|
103
|
-
other_live_agent_conversations: analysis.other_live_agent_conversations
|
|
104
|
-
}),
|
|
105
|
-
recent_messages: recent.map(format),
|
|
106
|
-
messages_to_classify: (question.messagesToClassify ?? [question.message]).map(format)
|
|
87
|
+
recent_messages: lines,
|
|
88
|
+
messages_to_classify: classified.map((m) => format(m, 2e3))
|
|
107
89
|
};
|
|
108
90
|
}
|
|
109
91
|
function conversationDecisionRouter(decide) {
|
|
110
92
|
return async (question) => {
|
|
111
93
|
const policy = question.groups;
|
|
112
|
-
const
|
|
113
|
-
signals: question.signals,
|
|
114
|
-
recent: question.recent,
|
|
115
|
-
policy,
|
|
116
|
-
agentNames: question.agentIdentity?.aliases,
|
|
117
|
-
pendingQuestionAt: question.pendingQuestions?.at(-1)?.at,
|
|
118
|
-
asOf: question.processingAt
|
|
119
|
-
});
|
|
120
|
-
const state = conversationModelState(question, analysis);
|
|
94
|
+
const state = conversationModelState(question);
|
|
121
95
|
const questions = conversationQuestions(policy.smart.instructions);
|
|
122
96
|
const started = performance.now();
|
|
123
97
|
const signal = AbortSignal.timeout(Math.ceil(policy.smart.timeoutSeconds * 1e3));
|
|
@@ -127,56 +101,38 @@ function conversationDecisionRouter(decide) {
|
|
|
127
101
|
questions,
|
|
128
102
|
signal
|
|
129
103
|
});
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
104
|
+
const p = raw.needs_response.probabilities?.true ?? raw.needs_response.probability ?? NaN;
|
|
105
|
+
const answers = {
|
|
106
|
+
...raw,
|
|
107
|
+
needs_response: {
|
|
133
108
|
choice: p >= .5,
|
|
134
109
|
probabilities: {
|
|
135
110
|
true: p,
|
|
136
111
|
false: 1 - p
|
|
137
112
|
}
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
const answers = {
|
|
141
|
-
...raw,
|
|
142
|
-
needs_response: normalize(raw.needs_response),
|
|
143
|
-
ends_conversation: normalize(raw.ends_conversation)
|
|
113
|
+
}
|
|
144
114
|
};
|
|
145
|
-
const
|
|
146
|
-
if ([
|
|
147
|
-
p,
|
|
148
|
-
needs,
|
|
149
|
-
ends
|
|
150
|
-
].some((x) => !Number.isFinite(x) || x < 0 || x > 1)) throw new Error("Conversation classifier probabilities unavailable.");
|
|
151
|
-
const threshold = conversationThreshold(analysis.path, policy);
|
|
152
|
-
const reply = answers.addressee.choice === "agent" && p >= threshold && needs >= (analysis.path === "B" || analysis.path === "X" ? .7 : .5) && ends < .7;
|
|
115
|
+
const agent = answers.addressee.probabilities?.agent ?? NaN;
|
|
116
|
+
if ([agent, p].some((x) => !Number.isFinite(x) || x < 0 || x > 1)) throw new Error("Conversation classifier probabilities unavailable.");
|
|
153
117
|
const diagnostics = {
|
|
154
118
|
model: policy.smart.model,
|
|
155
119
|
latencyMs: Math.round(performance.now() - started),
|
|
156
120
|
probabilities: {
|
|
157
|
-
reply:
|
|
158
|
-
observe: 1 -
|
|
121
|
+
reply: agent,
|
|
122
|
+
observe: 1 - agent
|
|
159
123
|
},
|
|
160
124
|
addressing: answers
|
|
161
125
|
};
|
|
162
126
|
return {
|
|
163
|
-
route:
|
|
127
|
+
route: addressedToAgent(answers) ? "reply" : "observe",
|
|
164
128
|
diagnostics,
|
|
165
129
|
router: {
|
|
166
130
|
state,
|
|
167
131
|
questions,
|
|
168
132
|
answer: answers,
|
|
169
|
-
|
|
170
|
-
conversation: analysis.conversation,
|
|
171
|
-
human_exchange: analysis.human_exchange,
|
|
172
|
-
thresholds_applied: {
|
|
173
|
-
threshold,
|
|
174
|
-
needs_response: analysis.path === "B" || analysis.path === "X" ? .7 : .5,
|
|
175
|
-
ends_conversation: .7
|
|
176
|
-
},
|
|
177
|
-
conversation_ended: ends >= .7
|
|
133
|
+
thresholds_applied: ADDRESSED_TO_AGENT
|
|
178
134
|
}
|
|
179
135
|
};
|
|
180
136
|
};
|
|
181
137
|
}
|
|
182
|
-
export { conversationDecisionRouter, conversationModelState, conversationQuestions };
|
|
138
|
+
export { ADDRESSED_TO_AGENT, addressedToAgent, conversationDecisionRouter, conversationModelState, conversationQuestions };
|
|
@@ -12,8 +12,6 @@ export interface ObservedMessage {
|
|
|
12
12
|
/** How intake named the sender: a linked member's name and roles, then their handle. */
|
|
13
13
|
readonly signedAuthor?: string;
|
|
14
14
|
readonly invitation?: string;
|
|
15
|
-
/** Classifier rejection closes routing hints without cancelling the underlying task. */
|
|
16
|
-
readonly conversationEnded?: boolean;
|
|
17
15
|
readonly text: string;
|
|
18
16
|
readonly media?: string;
|
|
19
17
|
/** Stored native images, by reference only: never image bytes or a sender's download grant. */
|
|
@@ -29,13 +29,7 @@ export interface SmartPolicy {
|
|
|
29
29
|
readonly engagement: {
|
|
30
30
|
readonly policy: "legacy" | "engaged" | "conversation";
|
|
31
31
|
readonly agentAliases: ReadonlyArray<string>;
|
|
32
|
-
readonly idleMinutes: number;
|
|
33
|
-
readonly humanExchangeMinutes: number;
|
|
34
|
-
readonly humanQuestionMinutes: number;
|
|
35
|
-
readonly inferredMaxMinutes: number;
|
|
36
32
|
readonly burst: {
|
|
37
|
-
readonly quietSeconds: number;
|
|
38
|
-
readonly maxSeconds: number;
|
|
39
33
|
readonly gapSeconds: number;
|
|
40
34
|
};
|
|
41
35
|
readonly followUpMinutes: number;
|
|
@@ -44,11 +38,6 @@ export interface SmartPolicy {
|
|
|
44
38
|
readonly addresseeGate: boolean;
|
|
45
39
|
};
|
|
46
40
|
readonly thresholds: {
|
|
47
|
-
readonly continuation: number;
|
|
48
|
-
readonly inferredContinuation: number;
|
|
49
|
-
readonly contested: number;
|
|
50
|
-
readonly ambient: number;
|
|
51
|
-
readonly humanExchange: number;
|
|
52
41
|
readonly engaged: number;
|
|
53
42
|
readonly neutral: number;
|
|
54
43
|
readonly humanThread: number;
|
|
@@ -67,6 +56,21 @@ export interface GroupPolicy {
|
|
|
67
56
|
readonly sources: Readonly<Record<string, string>>;
|
|
68
57
|
}
|
|
69
58
|
export declare function smartPolicy(input?: SmartPolicyInput, base?: SmartPolicy): SmartPolicy;
|
|
59
|
+
/**
|
|
60
|
+
* Settings of former SDK mechanisms: the conversation state machine's timers and per-state
|
|
61
|
+
* thresholds, and the webhook's wait for the rest of a burst. They are still accepted so that
|
|
62
|
+
* existing configurations deploy, and are ignored: the classifier reads the chat's recent
|
|
63
|
+
* messages, and a member's next message joins their active turn through eve's steering.
|
|
64
|
+
*/
|
|
65
|
+
export declare const IGNORED_GROUP_SETTINGS: {
|
|
66
|
+
readonly engagement: readonly ["idleMinutes", "humanExchangeMinutes", "humanQuestionMinutes", "inferredMaxMinutes"];
|
|
67
|
+
readonly thresholds: readonly ["continuation", "inferredContinuation", "contested", "ambient", "humanExchange"];
|
|
68
|
+
readonly burst: readonly ["quietSeconds", "maxSeconds"];
|
|
69
|
+
};
|
|
70
|
+
/** Whether a settings path names an ignored setting, in the agent, a chat or the shadow policy. */
|
|
71
|
+
export declare function ignoredGroupSetting(path: ReadonlyArray<string>): boolean;
|
|
72
|
+
/** The dotted paths of ignored settings an authored `groups` value sets, for a deploy warning. */
|
|
73
|
+
export declare function ignoredGroupSettings(value: unknown, prefix?: ReadonlyArray<string>): string[];
|
|
70
74
|
export declare function groupPolicy(input?: GroupPolicyInput, aliases?: {
|
|
71
75
|
readonly mode?: string;
|
|
72
76
|
readonly model?: string;
|
|
@@ -48,10 +48,6 @@ function smartPolicy(input = {}, base) {
|
|
|
48
48
|
engagement: {
|
|
49
49
|
policy: selected,
|
|
50
50
|
agentAliases: [],
|
|
51
|
-
idleMinutes: 10,
|
|
52
|
-
humanExchangeMinutes: 5,
|
|
53
|
-
humanQuestionMinutes: 30,
|
|
54
|
-
inferredMaxMinutes: 30,
|
|
55
51
|
followUpMinutes: 30,
|
|
56
52
|
conversationMinutes: 10,
|
|
57
53
|
...base?.engagement,
|
|
@@ -63,38 +59,55 @@ function smartPolicy(input = {}, base) {
|
|
|
63
59
|
openQuestionMinutes: engaged ? 30 : "unlimited",
|
|
64
60
|
addresseeGate: engaged
|
|
65
61
|
} : {},
|
|
66
|
-
...input.engagement,
|
|
62
|
+
...withoutIgnored(input.engagement, IGNORED_GROUP_SETTINGS.engagement),
|
|
67
63
|
burst: {
|
|
68
|
-
quietSeconds: 6,
|
|
69
|
-
maxSeconds: 15,
|
|
70
64
|
gapSeconds: 30,
|
|
71
65
|
...base?.engagement.burst,
|
|
72
|
-
...input.engagement?.burst
|
|
66
|
+
...withoutIgnored(input.engagement?.burst, IGNORED_GROUP_SETTINGS.burst)
|
|
73
67
|
}
|
|
74
68
|
},
|
|
75
69
|
thresholds: {
|
|
76
70
|
engaged: .5,
|
|
77
71
|
neutral: conversation ? .9 : engaged ? .75 : .5,
|
|
78
72
|
humanThread: conversation ? .95 : engaged ? .9 : .5,
|
|
79
|
-
continuation: .5,
|
|
80
|
-
inferredContinuation: .7,
|
|
81
|
-
contested: .8,
|
|
82
|
-
ambient: .9,
|
|
83
|
-
humanExchange: .95,
|
|
84
73
|
...changed ? {} : base?.thresholds,
|
|
85
|
-
...input.thresholds,
|
|
86
|
-
...conversation ? {
|
|
87
|
-
continuation: input.thresholds?.continuation ?? input.thresholds?.engaged ?? (changed ? .5 : base?.thresholds.continuation) ?? .5,
|
|
88
|
-
ambient: input.thresholds?.ambient ?? input.thresholds?.neutral ?? (changed ? .9 : base?.thresholds.ambient) ?? .9,
|
|
89
|
-
humanExchange: input.thresholds?.humanExchange ?? input.thresholds?.humanThread ?? (changed ? .95 : base?.thresholds.humanExchange) ?? .95
|
|
90
|
-
} : {}
|
|
74
|
+
...withoutIgnored(input.thresholds, IGNORED_GROUP_SETTINGS.thresholds)
|
|
91
75
|
},
|
|
92
76
|
timeoutSeconds: input.timeoutSeconds ?? base?.timeoutSeconds ?? 15
|
|
93
77
|
};
|
|
94
|
-
if (policy.
|
|
95
|
-
if ((conversation ? policy.instructions.length + JSON.stringify(ADDRESSEE_CRITERIA).length + 260 + 185 : policy.instructions.length + policy.criteria.observe.length + policy.criteria.reply.length) > 8e3) throw new RangeError("Group instructions and criteria must fit 8000 characters after inheritance.");
|
|
78
|
+
if ((conversation ? policy.instructions.length + JSON.stringify(ADDRESSEE_CRITERIA).length + 260 : policy.instructions.length + policy.criteria.observe.length + policy.criteria.reply.length) > 8e3) throw new RangeError("Group instructions and criteria must fit 8000 characters after inheritance.");
|
|
96
79
|
return policy;
|
|
97
80
|
}
|
|
81
|
+
const IGNORED_GROUP_SETTINGS = {
|
|
82
|
+
engagement: [
|
|
83
|
+
"idleMinutes",
|
|
84
|
+
"humanExchangeMinutes",
|
|
85
|
+
"humanQuestionMinutes",
|
|
86
|
+
"inferredMaxMinutes"
|
|
87
|
+
],
|
|
88
|
+
thresholds: [
|
|
89
|
+
"continuation",
|
|
90
|
+
"inferredContinuation",
|
|
91
|
+
"contested",
|
|
92
|
+
"ambient",
|
|
93
|
+
"humanExchange"
|
|
94
|
+
],
|
|
95
|
+
burst: ["quietSeconds", "maxSeconds"]
|
|
96
|
+
};
|
|
97
|
+
function withoutIgnored(value, ignored) {
|
|
98
|
+
return Object.fromEntries(Object.entries(value ?? {}).filter(([key]) => !ignored.includes(key)));
|
|
99
|
+
}
|
|
100
|
+
const IGNORED_SUFFIXES = Object.entries(IGNORED_GROUP_SETTINGS).flatMap(([section, keys]) => keys.map((key) => `${section}.${key}`));
|
|
101
|
+
function ignoredGroupSetting(path) {
|
|
102
|
+
return IGNORED_SUFFIXES.includes(path.slice(-2).join("."));
|
|
103
|
+
}
|
|
104
|
+
function ignoredGroupSettings(value, prefix = []) {
|
|
105
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return ignoredGroupSetting(prefix) ? [prefix.join(".")] : [];
|
|
106
|
+
return Object.entries(value).flatMap(([key, child]) => ignoredGroupSettings(child, [...prefix, key]));
|
|
107
|
+
}
|
|
108
|
+
function authored(value, source) {
|
|
109
|
+
return Object.fromEntries(Object.entries(sources(value, source)).filter(([path]) => !ignoredGroupSetting(path.split("."))));
|
|
110
|
+
}
|
|
98
111
|
function sources(value, source, prefix = "") {
|
|
99
112
|
return Object.fromEntries(Object.entries(value).flatMap(([key, value]) => {
|
|
100
113
|
const path = prefix ? `${prefix}.${key}` : key;
|
|
@@ -122,7 +135,7 @@ function groupPolicy(input = {}, aliases) {
|
|
|
122
135
|
}, "platform"),
|
|
123
136
|
...aliases?.mode ? { mode: "channels.telegram.policy" } : {},
|
|
124
137
|
...aliases?.model ? { "smart.model": "models.decision" } : {},
|
|
125
|
-
...
|
|
138
|
+
...authored(input, "agent")
|
|
126
139
|
}
|
|
127
140
|
};
|
|
128
141
|
}
|
|
@@ -144,11 +157,6 @@ function overlaySmartPolicy(input, base) {
|
|
|
144
157
|
addresseeGate: inherited("engagement.addresseeGate") ? defaults.engagement.addresseeGate : base.smart.engagement.addresseeGate
|
|
145
158
|
},
|
|
146
159
|
thresholds: {
|
|
147
|
-
continuation: inherited("thresholds.continuation") ? defaults.thresholds.continuation : base.smart.thresholds.continuation,
|
|
148
|
-
inferredContinuation: inherited("thresholds.inferredContinuation") ? defaults.thresholds.inferredContinuation : base.smart.thresholds.inferredContinuation,
|
|
149
|
-
contested: inherited("thresholds.contested") ? defaults.thresholds.contested : base.smart.thresholds.contested,
|
|
150
|
-
ambient: inherited("thresholds.ambient") ? defaults.thresholds.ambient : base.smart.thresholds.ambient,
|
|
151
|
-
humanExchange: inherited("thresholds.humanExchange") ? defaults.thresholds.humanExchange : base.smart.thresholds.humanExchange,
|
|
152
160
|
engaged: inherited("thresholds.engaged") ? defaults.thresholds.engaged : base.smart.thresholds.engaged,
|
|
153
161
|
neutral: inherited("thresholds.neutral") ? defaults.thresholds.neutral : base.smart.thresholds.neutral,
|
|
154
162
|
humanThread: inherited("thresholds.humanThread") ? defaults.thresholds.humanThread : base.smart.thresholds.humanThread
|
|
@@ -169,11 +177,11 @@ function effectiveGroupPolicy(base, key) {
|
|
|
169
177
|
},
|
|
170
178
|
sources: {
|
|
171
179
|
...result.sources,
|
|
172
|
-
...
|
|
180
|
+
...authored(layer, "agent.chat")
|
|
173
181
|
}
|
|
174
182
|
};
|
|
175
183
|
const thresholds = result.smart.thresholds;
|
|
176
184
|
if (thresholds.engaged > thresholds.neutral || thresholds.neutral > thresholds.humanThread) throw new RangeError("Group thresholds must satisfy engaged <= neutral <= humanThread after inheritance.");
|
|
177
185
|
return result;
|
|
178
186
|
}
|
|
179
|
-
export { DEFAULT_CRITERIA, DEFAULT_GROUP_POLICY, ENGAGED_CRITERIA, ENGAGED_INSTRUCTIONS, LEGACY_INSTRUCTIONS, effectiveGroupPolicy, groupPolicy, overlaySmartPolicy, smartPolicy };
|
|
187
|
+
export { DEFAULT_CRITERIA, DEFAULT_GROUP_POLICY, ENGAGED_CRITERIA, ENGAGED_INSTRUCTIONS, IGNORED_GROUP_SETTINGS, LEGACY_INSTRUCTIONS, effectiveGroupPolicy, groupPolicy, ignoredGroupSetting, ignoredGroupSettings, overlaySmartPolicy, smartPolicy };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { type ConversationAnalysis } from "./conversation.js";
|
|
2
1
|
import { type AddressingAnswers } from "./conversation-router.js";
|
|
3
2
|
import { type GroupPolicy } from "./policy.js";
|
|
4
3
|
import type { ObservedMessage, PendingConfirmation, RecentMessage } from "./messages.js";
|
|
@@ -9,7 +8,6 @@ export type Route = "observe" | "reply";
|
|
|
9
8
|
/** What the decision model sees. */
|
|
10
9
|
export interface RouteQuestion {
|
|
11
10
|
readonly groups?: GroupPolicy;
|
|
12
|
-
readonly conversation?: ConversationAnalysis;
|
|
13
11
|
readonly burstId?: string;
|
|
14
12
|
readonly processingAt?: string;
|
|
15
13
|
readonly messagesToClassify?: ReadonlyArray<ObservedMessage>;
|
|
@@ -6,8 +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 {
|
|
10
|
-
import { conversationDecisionRouter } from "./conversation-router.mjs";
|
|
9
|
+
import { conversationAddressing } from "./addressing.mjs";
|
|
10
|
+
import { addressedToAgent, conversationDecisionRouter } from "./conversation-router.mjs";
|
|
11
11
|
import { recordRoutingMetric } from "./health.mjs";
|
|
12
12
|
import { humanAddressee, humanThread, questionInvitation, restoreEngagement } from "./engagement.mjs";
|
|
13
13
|
import { boundedRouterPayload, policyHash } from "./record.mjs";
|
|
@@ -300,25 +300,11 @@ async function routeMessage(input) {
|
|
|
300
300
|
groups,
|
|
301
301
|
signals
|
|
302
302
|
};
|
|
303
|
-
const
|
|
304
|
-
signals,
|
|
305
|
-
recent: question.recent,
|
|
306
|
-
policy: groups,
|
|
307
|
-
agentNames: [...question.agentIdentity?.aliases ?? [], ...question.agentIdentity?.username ? [question.agentIdentity.username] : []],
|
|
308
|
-
pendingQuestionAt: input.pendingQuestionAt,
|
|
309
|
-
asOf: question.processingAt
|
|
310
|
-
});
|
|
311
|
-
question = {
|
|
312
|
-
...question,
|
|
313
|
-
conversation: analysis
|
|
314
|
-
};
|
|
303
|
+
const evidence = conversationAddressing(signals, question.recent, groups, [...question.agentIdentity?.aliases ?? [], ...question.agentIdentity?.username ? [question.agentIdentity.username] : []]);
|
|
315
304
|
payload = {
|
|
316
305
|
...payload,
|
|
317
306
|
burst_id: question.burstId,
|
|
318
|
-
|
|
319
|
-
conversation: analysis.conversation,
|
|
320
|
-
human_exchange: analysis.human_exchange,
|
|
321
|
-
pending_question_voided: input.pendingQuestionAt !== void 0 && !analysis.pending_question_live,
|
|
307
|
+
addressing_evidence: evidence,
|
|
322
308
|
question: {
|
|
323
309
|
recent: question.recent,
|
|
324
310
|
pendingQuestions: question.pendingQuestions,
|
|
@@ -328,30 +314,22 @@ async function routeMessage(input) {
|
|
|
328
314
|
messagesToClassify: question.messagesToClassify
|
|
329
315
|
}
|
|
330
316
|
};
|
|
331
|
-
if (
|
|
317
|
+
if (evidence.agent_named || evidence.reply_to_agent && evidence.human_targets.length === 0 && evidence.soft_target === void 0) {
|
|
332
318
|
if (input.beforeDecide && !await input.beforeDecide(true)) return complete("observe", "admission_refused", "Admission refused before direct turn.");
|
|
333
319
|
return complete("reply", addressed && addressed !== "pending-question" ? addressed : "mention", "Agent explicitly addressed.");
|
|
334
320
|
}
|
|
335
|
-
if (
|
|
321
|
+
if (evidence.human_targets.length > 0) return complete("observe", "human_addressee", "Message replies to, tags or names another participant.");
|
|
336
322
|
if (signals.addressing.eligible === false) return complete("observe", "channel_ineligible", "Channel excludes ambient messages.");
|
|
337
323
|
if (groups.mode === "mention") return complete("observe", "mention_policy", "Mention policy observes unaddressed messages.");
|
|
338
324
|
if (input.beforeDecide && !await input.beforeDecide()) return complete("observe", "admission_refused", "Admission refused before inference.");
|
|
339
325
|
const result = await input.decide(question);
|
|
340
326
|
const diagnostics = typeof result === "string" ? void 0 : result.diagnostics;
|
|
341
|
-
const
|
|
342
|
-
const threshold = conversationThreshold(analysis.path, groups);
|
|
343
|
-
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;
|
|
327
|
+
const valid = addressedToAgent(diagnostics?.addressing);
|
|
344
328
|
payload = {
|
|
345
329
|
...payload,
|
|
346
|
-
...typeof result === "string" ? {} : result.router
|
|
347
|
-
thresholds_applied: {
|
|
348
|
-
threshold,
|
|
349
|
-
path: analysis.path,
|
|
350
|
-
needs_response: analysis.path === "B" || analysis.path === "X" ? .7 : .5,
|
|
351
|
-
ends_conversation: .7
|
|
352
|
-
}
|
|
330
|
+
...typeof result === "string" ? {} : result.router
|
|
353
331
|
};
|
|
354
|
-
return complete(valid ? "reply" : "observe", `decision_model_${valid ? "reply" : "observe"}`, "
|
|
332
|
+
return complete(valid ? "reply" : "observe", `decision_model_${valid ? "reply" : "observe"}`, "Addressee read from the chat's recent messages.", "decision-model", diagnostics);
|
|
355
333
|
} catch (error) {
|
|
356
334
|
console.error("[segmentstream] conversation routing failed", { error });
|
|
357
335
|
return complete("observe", error instanceof Error && error.name === "TimeoutError" ? "decision_model_timeout" : "decision_model_failed", "Conversation routing unavailable; observe fallback.");
|
|
@@ -34,15 +34,6 @@ 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));
|
|
46
37
|
const admitted = new Set(events.flatMap((event) => event.kind === "decision" && event.reason === "turn_admitted" && event.messageId !== null ? [event.messageId] : []));
|
|
47
38
|
const membership = new Map();
|
|
48
39
|
for (const event of [...events].reverse()) {
|
|
@@ -80,7 +71,6 @@ function archivedRoutingMessages(events, limit = 30) {
|
|
|
80
71
|
invitation: metadata.invitation ?? invitations.get(event.messageId ?? ""),
|
|
81
72
|
...event.kind === "inbound" && admitted.has(event.messageId ?? "") ? { admitted: true } : {},
|
|
82
73
|
...images.length === 0 ? {} : { images },
|
|
83
|
-
...ended.has(event.messageId) ? { conversationEnded: true } : {},
|
|
84
74
|
author: event.senderName ?? event.senderId ?? "unknown sender",
|
|
85
75
|
...typeof metadata.author === "string" && event.kind === "inbound" ? { signedAuthor: metadata.author } : {},
|
|
86
76
|
text,
|
|
@@ -23,7 +23,7 @@ import { archivedRoutingMessages } from "../group-routing/routing-context.mjs";
|
|
|
23
23
|
import { dispatchStructuredMessage, structuredMessage } from "./structured.mjs";
|
|
24
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-DZs-ivRD.mjs";
|
|
27
27
|
import { attachmentLifecycle } from "./lifecycle.mjs";
|
|
28
28
|
import { gatewayImageDescriber } from "./images.mjs";
|
|
29
29
|
import { gatewayAttachmentDescriber } from "./attachment-description.mjs";
|
|
@@ -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-DZs-ivRD.mjs";
|
|
9
9
|
import { GET } from "eve/channels";
|
|
10
10
|
const HEALTH_ROUTE = "/eve/v1/telegram/health";
|
|
11
11
|
function healthRoute(input) {
|
|
@@ -29,7 +29,7 @@ export declare class InboundRequest {
|
|
|
29
29
|
readonly agentDescription: () => Promise<string>;
|
|
30
30
|
readonly agentName: () => Promise<string>;
|
|
31
31
|
readonly traceId: `${string}-${string}-${string}-${string}-${string}`;
|
|
32
|
-
/** Conversation policy
|
|
32
|
+
/** Conversation policy steers the active caller's turn; Eve queues other callers. */
|
|
33
33
|
turnPolicy: "queue" | "steer";
|
|
34
34
|
edited: boolean;
|
|
35
35
|
triggeringMessageId: string | undefined;
|
|
@@ -9,10 +9,9 @@ import { admitMessage, localizedText } from "../admission.mjs";
|
|
|
9
9
|
import { d as telegramRuntimeSource, n as conversationLanguage, o as telegramPrincipal, u as runtimeEnvelope } from "../../../_chunks/principal-D99Lleae.mjs";
|
|
10
10
|
import { p as reportTelegramError } from "../../../_chunks/inbound-request-D1r0FkDI.mjs";
|
|
11
11
|
import { addressingReason, conversationKey, otherRecipient } from "../group-routing/signals.mjs";
|
|
12
|
-
import { conversationAddressing, conversationState } from "../group-routing/conversation.mjs";
|
|
13
12
|
import { questionInvitation } from "../group-routing/engagement.mjs";
|
|
14
13
|
import { routeMessage } from "../group-routing/router.mjs";
|
|
15
|
-
import {
|
|
14
|
+
import { routingBurst } from "../group-routing/burst.mjs";
|
|
16
15
|
import { WorkspaceActionAlreadyDispatched } from "./workspace-action.mjs";
|
|
17
16
|
import { structuredMessage } from "./structured.mjs";
|
|
18
17
|
import { formatAddress, messageAddress, scopedAddress } from "./sessions.mjs";
|
|
@@ -495,11 +494,6 @@ async function handleInboundMessage(input) {
|
|
|
495
494
|
model: dependencies.decisionModel
|
|
496
495
|
}), conversationKey(policySignals));
|
|
497
496
|
const conversationPolicy = group && groups.smart.engagement.policy === "conversation";
|
|
498
|
-
const agentAliases = [...groups.smart.engagement.agentAliases, ...[
|
|
499
|
-
bot?.username,
|
|
500
|
-
bot?.firstName,
|
|
501
|
-
bot?.displayName
|
|
502
|
-
].filter((n) => !!n)];
|
|
503
497
|
let conversationRecent = conversationPolicy ? await recentConversation() : [];
|
|
504
498
|
if (conversationPolicy && input.request && input.request.lifecycleKey === void 0 && input.request.structuredContext === void 0) {
|
|
505
499
|
address = topicAddress;
|
|
@@ -515,15 +509,6 @@ async function handleInboundMessage(input) {
|
|
|
515
509
|
});
|
|
516
510
|
const questionAge = Date.parse(sentAt(sent)) - Date.parse(asked?.meta.at ?? "");
|
|
517
511
|
if (answering && addressed === void 0 && !questionInvitation(asked?.meta.at, sentAt(sent), groups.smart.engagement.openQuestionMinutes)) answering = false;
|
|
518
|
-
if (conversationPolicy && answering) {
|
|
519
|
-
if (!conversationState({
|
|
520
|
-
signals: policySignals,
|
|
521
|
-
recent: conversationRecent,
|
|
522
|
-
policy: groups,
|
|
523
|
-
agentNames: agentAliases,
|
|
524
|
-
pendingQuestionAt: asked?.meta.at
|
|
525
|
-
}).pending_question_live) answering = false;
|
|
526
|
-
}
|
|
527
512
|
const recipientAnswer = !conversationPolicy && answering && (groups.smart.engagement.policy === "engaged" || questionAge >= 0 && questionAge <= groups.smart.engagement.followUpMinutes * 6e4) && audience.replyTo?.kind === "bot" && isRecipientAnswer(audience);
|
|
528
513
|
if (!conversationPolicy && answering && pending !== void 0 && input.request !== void 0) {
|
|
529
514
|
address = pending.address || (questionSession === void 0 ? topicAddress : memberQuestionAddress);
|
|
@@ -635,7 +620,6 @@ async function handleInboundMessage(input) {
|
|
|
635
620
|
};
|
|
636
621
|
return question;
|
|
637
622
|
};
|
|
638
|
-
let burstId;
|
|
639
623
|
if (conversationPolicy && input.request?.lifecycleKey === void 0 && input.request?.structuredContext === void 0) {
|
|
640
624
|
if (voice !== void 0) await transcribeVoice(true);
|
|
641
625
|
const routingSignals = {
|
|
@@ -645,26 +629,7 @@ async function handleInboundMessage(input) {
|
|
|
645
629
|
text
|
|
646
630
|
}
|
|
647
631
|
};
|
|
648
|
-
const evidence = conversationAddressing(routingSignals, conversationRecent, groups, agentAliases);
|
|
649
|
-
if (!evidence.agent_named && !evidence.reply_to_agent && evidence.human_targets.length === 0) {
|
|
650
|
-
await recordContent({
|
|
651
|
-
text,
|
|
652
|
-
decoded
|
|
653
|
-
});
|
|
654
|
-
const refreshed = await debounceBurst({
|
|
655
|
-
signals: routingSignals,
|
|
656
|
-
policy: groups,
|
|
657
|
-
read: freshConversation
|
|
658
|
-
});
|
|
659
|
-
conversationRecent = refreshed.recent;
|
|
660
|
-
question = void 0;
|
|
661
|
-
if (refreshed.burst.superseded) {
|
|
662
|
-
recordGateDecision("burst_superseded");
|
|
663
|
-
return null;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
632
|
const burst = routingBurst(routingSignals, conversationRecent, groups);
|
|
667
|
-
burstId = burst.id;
|
|
668
633
|
const q = await conversation();
|
|
669
634
|
question = {
|
|
670
635
|
...q,
|
|
@@ -1063,32 +1028,6 @@ async function handleInboundMessage(input) {
|
|
|
1063
1028
|
chatGuidance: guidance
|
|
1064
1029
|
}
|
|
1065
1030
|
};
|
|
1066
|
-
if (conversationPolicy && input.request?.lifecycleKey === void 0 && input.request?.structuredContext === void 0) principal = {
|
|
1067
|
-
...principal,
|
|
1068
|
-
attributes: {
|
|
1069
|
-
...principal.attributes,
|
|
1070
|
-
groupRouting: JSON.stringify({
|
|
1071
|
-
policy: {
|
|
1072
|
-
...groups,
|
|
1073
|
-
chats: {}
|
|
1074
|
-
},
|
|
1075
|
-
signals: {
|
|
1076
|
-
...policySignals,
|
|
1077
|
-
message: {
|
|
1078
|
-
...policySignals.message,
|
|
1079
|
-
text
|
|
1080
|
-
}
|
|
1081
|
-
},
|
|
1082
|
-
agent: question?.agent.slice(0, 600) ?? "",
|
|
1083
|
-
agentIdentity: question?.agentIdentity,
|
|
1084
|
-
chat: question?.chat,
|
|
1085
|
-
burst_id: burstId ?? routingBurst(policySignals, conversationRecent, groups).id,
|
|
1086
|
-
message: question?.message,
|
|
1087
|
-
pendingQuestions: question?.pendingQuestions,
|
|
1088
|
-
pendingQuestionAt: asked?.meta.at
|
|
1089
|
-
})
|
|
1090
|
-
}
|
|
1091
|
-
};
|
|
1092
1031
|
return {
|
|
1093
1032
|
auth: answering && pending !== void 0 ? {
|
|
1094
1033
|
...principal,
|
|
@@ -10,7 +10,7 @@ import { n as currentInboundRequest } from "../../../_chunks/inbound-request-D1r
|
|
|
10
10
|
import { member } from "../actor.mjs";
|
|
11
11
|
import { needsConfirmation } from "../confirmations.mjs";
|
|
12
12
|
import { OntologyActionNotPermitted, actionToolName, ontologyTools } from "../ontology-tools.mjs";
|
|
13
|
-
import { t as agentBuild } from "../../../_chunks/build-metadata-
|
|
13
|
+
import { t as agentBuild } from "../../../_chunks/build-metadata-DZs-ivRD.mjs";
|
|
14
14
|
var AttachmentLifecycleNotPermitted = class extends Error {
|
|
15
15
|
code = "attachment_lifecycle_not_permitted";
|
|
16
16
|
};
|