@segmentstream/agent 0.27.0 → 0.28.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-CMmCRD8l.mjs} +1 -1
- package/dist/extension/config.mjs +7 -1
- package/dist/extension/extension.d.ts +1 -1
- package/dist/extension/extension.mjs +2 -2
- 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.mjs +1 -1
- 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/dequeue.mjs +1 -1
- package/dist/extension/lib/group-routing/messages.d.ts +0 -2
- package/dist/extension/lib/group-routing/policy.d.ts +13 -9
- package/dist/extension/lib/group-routing/policy.mjs +35 -25
- 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.mjs +1 -10
- package/dist/extension/lib/telegram/lifecycle.mjs +1 -1
- package/package.json +1 -1
- package/dist/extension/lib/group-routing/conversation.d.ts +0 -46
- package/dist/extension/lib/group-routing/conversation.mjs +0 -269
|
@@ -29,10 +29,6 @@ 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
33
|
readonly quietSeconds: number;
|
|
38
34
|
readonly maxSeconds: number;
|
|
@@ -44,11 +40,6 @@ export interface SmartPolicy {
|
|
|
44
40
|
readonly addresseeGate: boolean;
|
|
45
41
|
};
|
|
46
42
|
readonly thresholds: {
|
|
47
|
-
readonly continuation: number;
|
|
48
|
-
readonly inferredContinuation: number;
|
|
49
|
-
readonly contested: number;
|
|
50
|
-
readonly ambient: number;
|
|
51
|
-
readonly humanExchange: number;
|
|
52
43
|
readonly engaged: number;
|
|
53
44
|
readonly neutral: number;
|
|
54
45
|
readonly humanThread: number;
|
|
@@ -67,6 +58,19 @@ export interface GroupPolicy {
|
|
|
67
58
|
readonly sources: Readonly<Record<string, string>>;
|
|
68
59
|
}
|
|
69
60
|
export declare function smartPolicy(input?: SmartPolicyInput, base?: SmartPolicy): SmartPolicy;
|
|
61
|
+
/**
|
|
62
|
+
* Settings of the former conversation state machine (timers and per-state thresholds). They are
|
|
63
|
+
* still accepted so that existing configurations deploy, and are ignored: the classifier reads the
|
|
64
|
+
* chat's recent messages instead.
|
|
65
|
+
*/
|
|
66
|
+
export declare const IGNORED_GROUP_SETTINGS: {
|
|
67
|
+
readonly engagement: readonly ["idleMinutes", "humanExchangeMinutes", "humanQuestionMinutes", "inferredMaxMinutes"];
|
|
68
|
+
readonly thresholds: readonly ["continuation", "inferredContinuation", "contested", "ambient", "humanExchange"];
|
|
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,7 +59,7 @@ 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
64
|
quietSeconds: 6,
|
|
69
65
|
maxSeconds: 15,
|
|
@@ -76,25 +72,44 @@ function smartPolicy(input = {}, base) {
|
|
|
76
72
|
engaged: .5,
|
|
77
73
|
neutral: conversation ? .9 : engaged ? .75 : .5,
|
|
78
74
|
humanThread: conversation ? .95 : engaged ? .9 : .5,
|
|
79
|
-
continuation: .5,
|
|
80
|
-
inferredContinuation: .7,
|
|
81
|
-
contested: .8,
|
|
82
|
-
ambient: .9,
|
|
83
|
-
humanExchange: .95,
|
|
84
75
|
...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
|
-
} : {}
|
|
76
|
+
...withoutIgnored(input.thresholds, IGNORED_GROUP_SETTINGS.thresholds)
|
|
91
77
|
},
|
|
92
78
|
timeoutSeconds: input.timeoutSeconds ?? base?.timeoutSeconds ?? 15
|
|
93
79
|
};
|
|
94
80
|
if (policy.engagement.burst.quietSeconds > policy.engagement.burst.maxSeconds) throw new RangeError("Burst quietSeconds must not exceed maxSeconds after inheritance.");
|
|
95
|
-
if ((conversation ? policy.instructions.length + JSON.stringify(ADDRESSEE_CRITERIA).length + 260
|
|
81
|
+
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
82
|
return policy;
|
|
97
83
|
}
|
|
84
|
+
const IGNORED_GROUP_SETTINGS = {
|
|
85
|
+
engagement: [
|
|
86
|
+
"idleMinutes",
|
|
87
|
+
"humanExchangeMinutes",
|
|
88
|
+
"humanQuestionMinutes",
|
|
89
|
+
"inferredMaxMinutes"
|
|
90
|
+
],
|
|
91
|
+
thresholds: [
|
|
92
|
+
"continuation",
|
|
93
|
+
"inferredContinuation",
|
|
94
|
+
"contested",
|
|
95
|
+
"ambient",
|
|
96
|
+
"humanExchange"
|
|
97
|
+
]
|
|
98
|
+
};
|
|
99
|
+
function withoutIgnored(value, ignored) {
|
|
100
|
+
return Object.fromEntries(Object.entries(value ?? {}).filter(([key]) => !ignored.includes(key)));
|
|
101
|
+
}
|
|
102
|
+
const IGNORED_SUFFIXES = Object.entries(IGNORED_GROUP_SETTINGS).flatMap(([section, keys]) => keys.map((key) => `${section}.${key}`));
|
|
103
|
+
function ignoredGroupSetting(path) {
|
|
104
|
+
return IGNORED_SUFFIXES.includes(path.slice(-2).join("."));
|
|
105
|
+
}
|
|
106
|
+
function ignoredGroupSettings(value, prefix = []) {
|
|
107
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return ignoredGroupSetting(prefix) ? [prefix.join(".")] : [];
|
|
108
|
+
return Object.entries(value).flatMap(([key, child]) => ignoredGroupSettings(child, [...prefix, key]));
|
|
109
|
+
}
|
|
110
|
+
function authored(value, source) {
|
|
111
|
+
return Object.fromEntries(Object.entries(sources(value, source)).filter(([path]) => !ignoredGroupSetting(path.split("."))));
|
|
112
|
+
}
|
|
98
113
|
function sources(value, source, prefix = "") {
|
|
99
114
|
return Object.fromEntries(Object.entries(value).flatMap(([key, value]) => {
|
|
100
115
|
const path = prefix ? `${prefix}.${key}` : key;
|
|
@@ -122,7 +137,7 @@ function groupPolicy(input = {}, aliases) {
|
|
|
122
137
|
}, "platform"),
|
|
123
138
|
...aliases?.mode ? { mode: "channels.telegram.policy" } : {},
|
|
124
139
|
...aliases?.model ? { "smart.model": "models.decision" } : {},
|
|
125
|
-
...
|
|
140
|
+
...authored(input, "agent")
|
|
126
141
|
}
|
|
127
142
|
};
|
|
128
143
|
}
|
|
@@ -144,11 +159,6 @@ function overlaySmartPolicy(input, base) {
|
|
|
144
159
|
addresseeGate: inherited("engagement.addresseeGate") ? defaults.engagement.addresseeGate : base.smart.engagement.addresseeGate
|
|
145
160
|
},
|
|
146
161
|
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
162
|
engaged: inherited("thresholds.engaged") ? defaults.thresholds.engaged : base.smart.thresholds.engaged,
|
|
153
163
|
neutral: inherited("thresholds.neutral") ? defaults.thresholds.neutral : base.smart.thresholds.neutral,
|
|
154
164
|
humanThread: inherited("thresholds.humanThread") ? defaults.thresholds.humanThread : base.smart.thresholds.humanThread
|
|
@@ -169,11 +179,11 @@ function effectiveGroupPolicy(base, key) {
|
|
|
169
179
|
},
|
|
170
180
|
sources: {
|
|
171
181
|
...result.sources,
|
|
172
|
-
...
|
|
182
|
+
...authored(layer, "agent.chat")
|
|
173
183
|
}
|
|
174
184
|
};
|
|
175
185
|
const thresholds = result.smart.thresholds;
|
|
176
186
|
if (thresholds.engaged > thresholds.neutral || thresholds.neutral > thresholds.humanThread) throw new RangeError("Group thresholds must satisfy engaged <= neutral <= humanThread after inheritance.");
|
|
177
187
|
return result;
|
|
178
188
|
}
|
|
179
|
-
export { DEFAULT_CRITERIA, DEFAULT_GROUP_POLICY, ENGAGED_CRITERIA, ENGAGED_INSTRUCTIONS, LEGACY_INSTRUCTIONS, effectiveGroupPolicy, groupPolicy, overlaySmartPolicy, smartPolicy };
|
|
189
|
+
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-CMmCRD8l.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-CMmCRD8l.mjs";
|
|
9
9
|
import { GET } from "eve/channels";
|
|
10
10
|
const HEALTH_ROUTE = "/eve/v1/telegram/health";
|
|
11
11
|
function healthRoute(input) {
|
|
@@ -9,7 +9,7 @@ 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
|
|
12
|
+
import { conversationAddressing } from "../group-routing/addressing.mjs";
|
|
13
13
|
import { questionInvitation } from "../group-routing/engagement.mjs";
|
|
14
14
|
import { routeMessage } from "../group-routing/router.mjs";
|
|
15
15
|
import { debounceBurst, routingBurst } from "../group-routing/burst.mjs";
|
|
@@ -515,15 +515,6 @@ async function handleInboundMessage(input) {
|
|
|
515
515
|
});
|
|
516
516
|
const questionAge = Date.parse(sentAt(sent)) - Date.parse(asked?.meta.at ?? "");
|
|
517
517
|
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
518
|
const recipientAnswer = !conversationPolicy && answering && (groups.smart.engagement.policy === "engaged" || questionAge >= 0 && questionAge <= groups.smart.engagement.followUpMinutes * 6e4) && audience.replyTo?.kind === "bot" && isRecipientAnswer(audience);
|
|
528
519
|
if (!conversationPolicy && answering && pending !== void 0 && input.request !== void 0) {
|
|
529
520
|
address = pending.address || (questionSession === void 0 ? topicAddress : memberQuestionAddress);
|
|
@@ -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-CMmCRD8l.mjs";
|
|
14
14
|
var AttachmentLifecycleNotPermitted = class extends Error {
|
|
15
15
|
code = "attachment_lifecycle_not_permitted";
|
|
16
16
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@segmentstream/agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "Mount a SegmentStream workspace's agent on eve: the ontology as tools, acting as the member who writes, and Telegram through the workspace's connection.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "SegmentStream",
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import type { GroupPolicy } from "./policy.js";
|
|
2
|
-
import type { GroupMessageSignals } from "./signals.js";
|
|
3
|
-
import type { RecentMessage } from "./messages.js";
|
|
4
|
-
export type ConversationPath = "A" | "H" | "C" | "Ci" | "C2" | "B" | "X";
|
|
5
|
-
export interface ConversationState {
|
|
6
|
-
status: "active" | "closed" | "none";
|
|
7
|
-
opened_by: "explicit" | "agent_initiated" | "inferred" | null;
|
|
8
|
-
opened_at?: string;
|
|
9
|
-
last_exchange_at?: string;
|
|
10
|
-
last_p_at?: string;
|
|
11
|
-
contested: boolean;
|
|
12
|
-
closed_reason?: "turned_to_human" | "idle" | "ended" | "inferred_expired";
|
|
13
|
-
}
|
|
14
|
-
export interface ConversationAnalysis {
|
|
15
|
-
readonly path: ConversationPath;
|
|
16
|
-
readonly conversation: ConversationState;
|
|
17
|
-
readonly human_targets: ReadonlyArray<string>;
|
|
18
|
-
readonly human_exchange: {
|
|
19
|
-
readonly active: boolean;
|
|
20
|
-
readonly with: ReadonlyArray<string>;
|
|
21
|
-
readonly evidence: ReadonlyArray<string>;
|
|
22
|
-
};
|
|
23
|
-
readonly other_live_agent_conversations: ReadonlyArray<string>;
|
|
24
|
-
readonly pending_question_live: boolean;
|
|
25
|
-
}
|
|
26
|
-
/** Workspace names are data. Only leading/trailing vocatives count as an address. */
|
|
27
|
-
export declare function vocatives(text: string): ReadonlyArray<string>;
|
|
28
|
-
export interface AddressEvidence {
|
|
29
|
-
readonly agent_named: boolean;
|
|
30
|
-
readonly reply_to_agent: boolean;
|
|
31
|
-
readonly human_targets: ReadonlyArray<string>;
|
|
32
|
-
readonly soft_target?: string;
|
|
33
|
-
}
|
|
34
|
-
export declare function conversationAddressing(signals: GroupMessageSignals, recent: ReadonlyArray<RecentMessage>, policy: GroupPolicy, agentNames?: ReadonlyArray<string>): AddressEvidence;
|
|
35
|
-
export declare function recordedMessageSignals(m: RecentMessage, current: GroupMessageSignals): GroupMessageSignals;
|
|
36
|
-
/** Restore bounded state from received messages and confirmed delivery facts, including unpaired humans. */
|
|
37
|
-
export declare function conversationState(input: {
|
|
38
|
-
readonly signals: GroupMessageSignals;
|
|
39
|
-
readonly recent: ReadonlyArray<RecentMessage>;
|
|
40
|
-
readonly policy: GroupPolicy;
|
|
41
|
-
readonly agentNames?: ReadonlyArray<string>;
|
|
42
|
-
readonly pendingQuestionAt?: string;
|
|
43
|
-
/** Dequeued inputs use the current turn boundary without changing provider timestamps. */
|
|
44
|
-
readonly asOf?: string;
|
|
45
|
-
}): ConversationAnalysis;
|
|
46
|
-
export declare const conversationThreshold: (path: ConversationPath, policy: GroupPolicy) => number;
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
-
import { dirname as __eveDirname } from "node:path";
|
|
3
|
-
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
-
const __filename = __eveFileURLToPath(import.meta.url);
|
|
5
|
-
__eveDirname(__filename);
|
|
6
|
-
__eveCreateRequire(import.meta.url);
|
|
7
|
-
const own = (m) => "role" in m && m.role === "agent";
|
|
8
|
-
const norm = (s) => s.normalize("NFKC").toLowerCase().replace(/^@/, "");
|
|
9
|
-
const stamp = (s) => Date.parse(/[zZ]|[+-]\d\d:\d\d$/.test(s) ? s : s + "Z");
|
|
10
|
-
function vocatives(text) {
|
|
11
|
-
return [/^[\s«"“'([]*(@?[\p{L}\p{N}_-]{2,})(?=[\s,!:;?.»"”')\]]|$)/u.exec(text)?.[1], /,\s*([\p{L}\p{N}_-]{2,})\s*[?!.)»"”]*\s*$/u.exec(text)?.[1]].filter((s) => s !== void 0).map(norm);
|
|
12
|
-
}
|
|
13
|
-
function identities(participants, recent) {
|
|
14
|
-
const names = new Map();
|
|
15
|
-
for (const [id, p] of Object.entries(participants)) for (const a of [id, ...p.aliases]) names.set(norm(a), id);
|
|
16
|
-
for (const m of recent) {
|
|
17
|
-
if (own(m)) continue;
|
|
18
|
-
const id = names.get(norm(m.authorId ?? m.username ?? m.author)) ?? m.authorId ?? m.username ?? m.author;
|
|
19
|
-
for (const a of [
|
|
20
|
-
m.authorId,
|
|
21
|
-
m.username,
|
|
22
|
-
m.author,
|
|
23
|
-
m.author.split(/\s/u)[0]
|
|
24
|
-
]) if (a) names.set(norm(a), id);
|
|
25
|
-
}
|
|
26
|
-
return (name) => names.get(norm(name)) ?? norm(name);
|
|
27
|
-
}
|
|
28
|
-
function conversationAddressing(signals, recent, policy, agentNames = []) {
|
|
29
|
-
const canonical = identities(policy.participants, recent);
|
|
30
|
-
const sender = canonical(signals.sender.externalId);
|
|
31
|
-
const agents = new Set([...agentNames, ...policy.smart.engagement.agentAliases].map(norm));
|
|
32
|
-
const names = vocatives(signals.derived?.transcript ?? signals.message.text);
|
|
33
|
-
const mentions = [...signals.addressing.mentions, ...Array.from(signals.message.text.matchAll(/(?:^|[^\p{L}\p{N}_])@([A-Za-z0-9_]+)/gu), (m) => ({
|
|
34
|
-
username: m[1],
|
|
35
|
-
kind: "unknown"
|
|
36
|
-
}))];
|
|
37
|
-
const punctuated = /^[\s«"“'([]*(@?[\p{L}\p{N}_-]{2,})\s*(?:[,!:?]|$)/u.exec(signals.derived?.transcript ?? signals.message.text)?.[1];
|
|
38
|
-
const trailing = /,\s*([\p{L}\p{N}_-]{2,})\s*[?!.)»"”]*\s*$/u.exec(signals.derived?.transcript ?? signals.message.text)?.[1];
|
|
39
|
-
const named = signals.addressing.agentMentioned || signals.addressing.command || [punctuated, trailing].some((n) => n !== void 0 && agents.has(norm(n))) || mentions.some((m) => m.kind === "bot" || agents.has(norm(m.username ?? m.externalId ?? "")));
|
|
40
|
-
const humans = new Set();
|
|
41
|
-
const terminalMention = /@([A-Za-z0-9_]+)\s*[.!?]*\s*$/u.exec(signals.message.text)?.[1];
|
|
42
|
-
const replyToAgent = signals.addressing.replyTo?.author === "agent" || signals.addressing.threadWithAgent === true;
|
|
43
|
-
let soft;
|
|
44
|
-
for (const m of mentions) if (m.kind !== "bot" && !agents.has(norm(m.username ?? m.externalId ?? ""))) {
|
|
45
|
-
const target = canonical(m.externalId ?? m.username ?? "unknown");
|
|
46
|
-
if (replyToAgent && m.type !== "text_mention" && m.username !== void 0 && !names.some((name) => canonical(name) === target) && (terminalMention === void 0 || canonical(terminalMention) !== target)) soft = target;
|
|
47
|
-
else humans.add(target);
|
|
48
|
-
}
|
|
49
|
-
const known = new Set([...Object.keys(policy.participants), ...recent.filter((m) => !own(m)).map((m) => canonical(m.authorId ?? m.username ?? m.author))]);
|
|
50
|
-
for (const name of names) {
|
|
51
|
-
const id = canonical(name);
|
|
52
|
-
if (known.has(id) && id !== sender && !agents.has(name)) humans.add(id);
|
|
53
|
-
}
|
|
54
|
-
const reply = signals.addressing.replyTo;
|
|
55
|
-
if (reply && reply.author !== "agent" && !reply.self) {
|
|
56
|
-
const parent = recent.find((m) => m.messageId === reply.messageId);
|
|
57
|
-
const id = canonical(reply.authorExternalId ?? reply.authorUsername ?? parent?.authorId ?? parent?.username ?? reply.authorName ?? "unknown");
|
|
58
|
-
if (id !== sender) {
|
|
59
|
-
const upload = reply.hasAttachments || (parent?.attachmentKinds?.length ?? 0) > 0;
|
|
60
|
-
const parentTargetsSender = parent !== void 0 && ((parent.taggedUsers ?? []).some((m) => canonical(m.id ?? m.username ?? "") === sender) || vocatives(parent.text).some((n) => canonical(n) === sender));
|
|
61
|
-
if (upload && !parentTargetsSender && reply.author !== "other_bot") soft = id;
|
|
62
|
-
else humans.add(id);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return {
|
|
66
|
-
agent_named: named,
|
|
67
|
-
reply_to_agent: replyToAgent,
|
|
68
|
-
human_targets: [...humans],
|
|
69
|
-
...soft ? { soft_target: soft } : {}
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
function recordedMessageSignals(m, current) {
|
|
73
|
-
return {
|
|
74
|
-
...current,
|
|
75
|
-
derived: void 0,
|
|
76
|
-
sender: {
|
|
77
|
-
externalId: m.authorId ?? m.username ?? m.author,
|
|
78
|
-
username: m.username,
|
|
79
|
-
displayName: m.author,
|
|
80
|
-
kind: m.kind ?? "human"
|
|
81
|
-
},
|
|
82
|
-
message: {
|
|
83
|
-
...current.message,
|
|
84
|
-
id: m.messageId ?? "",
|
|
85
|
-
at: m.at,
|
|
86
|
-
text: m.text,
|
|
87
|
-
attachments: [],
|
|
88
|
-
hasCaption: m.hasCaption ?? false
|
|
89
|
-
},
|
|
90
|
-
addressing: {
|
|
91
|
-
agentMentioned: m.invitation === "mention" || (m.taggedUsers?.some((t) => t.kind === "bot") ?? false),
|
|
92
|
-
command: m.invitation === "command",
|
|
93
|
-
threadWithAgent: m.invitation === "thread-with-agent",
|
|
94
|
-
mentions: (m.taggedUsers ?? []).map((t) => ({
|
|
95
|
-
type: t.type,
|
|
96
|
-
externalId: t.id,
|
|
97
|
-
username: t.username,
|
|
98
|
-
kind: t.kind
|
|
99
|
-
})),
|
|
100
|
-
...m.replyTo ? { replyTo: {
|
|
101
|
-
messageId: m.replyTo.messageId,
|
|
102
|
-
author: m.replyTo.kind === "bot" ? "agent" : m.replyTo.kind,
|
|
103
|
-
authorExternalId: m.replyTo.id,
|
|
104
|
-
authorUsername: m.replyTo.username
|
|
105
|
-
} } : {}
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
function conversationState(input) {
|
|
110
|
-
const { signals, policy } = input;
|
|
111
|
-
const now = stamp(input.asOf ?? signals.message.at);
|
|
112
|
-
const settings = policy.smart.engagement;
|
|
113
|
-
const recent = input.recent.filter((m) => m.messageId !== signals.message.id && stamp(m.at) <= now).slice().sort((a, b) => stamp(a.at) - stamp(b.at));
|
|
114
|
-
const current = {
|
|
115
|
-
author: signals.sender.displayName ?? signals.sender.externalId,
|
|
116
|
-
authorId: signals.sender.externalId,
|
|
117
|
-
username: signals.sender.username,
|
|
118
|
-
at: signals.message.at,
|
|
119
|
-
text: signals.derived?.transcript ?? signals.message.text,
|
|
120
|
-
messageId: signals.message.id,
|
|
121
|
-
kind: signals.sender.kind
|
|
122
|
-
};
|
|
123
|
-
const canonical = identities(policy.participants, [...recent, current]);
|
|
124
|
-
const states = new Map();
|
|
125
|
-
const edges = [];
|
|
126
|
-
let lastAgent = -Infinity;
|
|
127
|
-
const humanSinceAgent = [];
|
|
128
|
-
const open = (person, by, at) => states.set(person, {
|
|
129
|
-
status: "active",
|
|
130
|
-
opened_by: by,
|
|
131
|
-
opened_at: at,
|
|
132
|
-
last_exchange_at: at,
|
|
133
|
-
last_p_at: by === "agent_initiated" ? void 0 : at,
|
|
134
|
-
contested: false
|
|
135
|
-
});
|
|
136
|
-
const expire = (state, t) => {
|
|
137
|
-
if (state?.status !== "active") return;
|
|
138
|
-
if (t - stamp(state.last_exchange_at) > settings.idleMinutes * 6e4) {
|
|
139
|
-
state.status = "closed";
|
|
140
|
-
state.closed_reason = "idle";
|
|
141
|
-
} else if (state.opened_by === "inferred" && t - stamp(state.opened_at) > settings.inferredMaxMinutes * 6e4) {
|
|
142
|
-
state.status = "closed";
|
|
143
|
-
state.closed_reason = "inferred_expired";
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
const human = (m, s, isCurrent) => {
|
|
147
|
-
const t = stamp(isCurrent ? input.asOf ?? m.at : m.at), person = canonical(s.sender.externalId);
|
|
148
|
-
const evidence = conversationAddressing(s, recent, policy, input.agentNames);
|
|
149
|
-
for (const target of evidence.human_targets) {
|
|
150
|
-
edges.push({
|
|
151
|
-
at: t,
|
|
152
|
-
from: person,
|
|
153
|
-
to: target
|
|
154
|
-
});
|
|
155
|
-
const targetState = states.get(target);
|
|
156
|
-
if (targetState?.status === "active") targetState.contested = true;
|
|
157
|
-
}
|
|
158
|
-
if (evidence.soft_target) edges.push({
|
|
159
|
-
at: t,
|
|
160
|
-
from: person,
|
|
161
|
-
to: evidence.soft_target
|
|
162
|
-
});
|
|
163
|
-
for (const [other, state] of states) {
|
|
164
|
-
expire(state, t);
|
|
165
|
-
if (other !== person && state.status === "active" && evidence.human_targets.length === 0 && !evidence.agent_named && t > stamp(state.last_p_at ?? state.opened_at)) state.contested = true;
|
|
166
|
-
}
|
|
167
|
-
let state = states.get(person);
|
|
168
|
-
expire(state, t);
|
|
169
|
-
const wasActive = state?.status === "active";
|
|
170
|
-
const explicit = evidence.agent_named || evidence.reply_to_agent && evidence.human_targets.length === 0 && evidence.soft_target === void 0;
|
|
171
|
-
let path;
|
|
172
|
-
if (explicit) {
|
|
173
|
-
path = "A";
|
|
174
|
-
open(person, "explicit", m.at);
|
|
175
|
-
state = states.get(person);
|
|
176
|
-
} else if (evidence.human_targets.length) {
|
|
177
|
-
path = "H";
|
|
178
|
-
if (state?.status === "active") {
|
|
179
|
-
state.status = "closed";
|
|
180
|
-
state.closed_reason = "turned_to_human";
|
|
181
|
-
}
|
|
182
|
-
} else if (state?.status === "active") path = state.contested || evidence.soft_target ? "C2" : state.opened_by === "inferred" ? "Ci" : "C";
|
|
183
|
-
else path = "B";
|
|
184
|
-
const humanEdges = edges.filter((e) => t - e.at >= 0 && t - e.at <= settings.humanExchangeMinutes * 6e4 && (e.from === person || e.to === person));
|
|
185
|
-
const questions = edges.filter((e) => e.to === person && t - e.at <= settings.humanQuestionMinutes * 6e4 && lastAgent < e.at);
|
|
186
|
-
const others = [...states].filter(([p, c]) => p !== person && c.status === "active" && t - stamp(c.last_p_at ?? c.opened_at) <= 12e4).map(([p]) => p);
|
|
187
|
-
const sequence = [...humanSinceAgent.filter((x) => t - x.at <= settings.humanExchangeMinutes * 6e4).map((x) => x.who), person];
|
|
188
|
-
const switches = sequence.slice(1).filter((p, i) => p !== sequence[i] && (p === person || sequence[i] === person)).length;
|
|
189
|
-
const exchange = humanEdges.length > 0 || questions.length > 0 || switches >= 2 || others.length > 0;
|
|
190
|
-
if (path === "B" && exchange) path = "X";
|
|
191
|
-
if (!isCurrent) {
|
|
192
|
-
if (m.conversationEnded && state) {
|
|
193
|
-
state.status = "closed";
|
|
194
|
-
state.closed_reason = "ended";
|
|
195
|
-
}
|
|
196
|
-
if (state?.status === "active" && [
|
|
197
|
-
"A",
|
|
198
|
-
"C",
|
|
199
|
-
"Ci",
|
|
200
|
-
"C2"
|
|
201
|
-
].includes(path)) state.last_p_at = m.at;
|
|
202
|
-
humanSinceAgent.push({
|
|
203
|
-
at: t,
|
|
204
|
-
who: person
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
return {
|
|
208
|
-
path,
|
|
209
|
-
conversation: { ...state ?? {
|
|
210
|
-
status: "none",
|
|
211
|
-
opened_by: null,
|
|
212
|
-
contested: false
|
|
213
|
-
} },
|
|
214
|
-
human_targets: evidence.human_targets,
|
|
215
|
-
human_exchange: {
|
|
216
|
-
active: state?.status !== "active" && exchange,
|
|
217
|
-
with: [...new Set([...humanEdges, ...questions].flatMap((e) => [e.from, e.to]).filter((p) => p !== person))],
|
|
218
|
-
evidence: [
|
|
219
|
-
...humanEdges.length ? ["recent_human_address"] : [],
|
|
220
|
-
...questions.length ? ["human_question"] : [],
|
|
221
|
-
...switches >= 2 ? ["human_alternation"] : [],
|
|
222
|
-
...others.length ? ["other_live_agent_conversation"] : []
|
|
223
|
-
]
|
|
224
|
-
},
|
|
225
|
-
other_live_agent_conversations: others,
|
|
226
|
-
pending_question_live: wasActive && state?.status === "active" && evidence.human_targets.length === 0 && input.pendingQuestionAt !== void 0 && t - stamp(input.pendingQuestionAt) >= 0 && (settings.openQuestionMinutes === "unlimited" || t - stamp(input.pendingQuestionAt) <= settings.openQuestionMinutes * 6e4)
|
|
227
|
-
};
|
|
228
|
-
};
|
|
229
|
-
for (const m of recent) {
|
|
230
|
-
if (!own(m)) {
|
|
231
|
-
if (m.kind === "bot" || m.kind === "other_bot") continue;
|
|
232
|
-
human(m, recordedMessageSignals(m, signals), false);
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
if (m.confirmation) continue;
|
|
236
|
-
lastAgent = stamp(m.at);
|
|
237
|
-
humanSinceAgent.length = 0;
|
|
238
|
-
const parent = recent.find((p) => !own(p) && p.messageId === m.replyTo?.messageId);
|
|
239
|
-
const targets = new Set([...m.addressedTo.map((p) => canonical(p.id ?? p.username ?? p.author)), ...parent ? [canonical(parent.authorId ?? parent.username ?? parent.author)] : []]);
|
|
240
|
-
const named = conversationAddressing(recordedMessageSignals({
|
|
241
|
-
...m,
|
|
242
|
-
role: void 0
|
|
243
|
-
}, signals), recent, policy, input.agentNames).human_targets;
|
|
244
|
-
for (const target of named) targets.add(target);
|
|
245
|
-
for (const target of targets) {
|
|
246
|
-
const state = states.get(target);
|
|
247
|
-
expire(state, stamp(m.at));
|
|
248
|
-
if (state?.status === "active") state.last_exchange_at = m.at;
|
|
249
|
-
else open(target, parent || m.inReplyTo ? "inferred" : "agent_initiated", m.at);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
return human(current, input.asOf === void 0 ? signals : {
|
|
253
|
-
...signals,
|
|
254
|
-
message: {
|
|
255
|
-
...signals.message,
|
|
256
|
-
at: input.asOf
|
|
257
|
-
}
|
|
258
|
-
}, true);
|
|
259
|
-
}
|
|
260
|
-
const conversationThreshold = (path, policy) => ({
|
|
261
|
-
A: 0,
|
|
262
|
-
H: 1,
|
|
263
|
-
C: policy.smart.thresholds.continuation,
|
|
264
|
-
Ci: policy.smart.thresholds.inferredContinuation,
|
|
265
|
-
C2: policy.smart.thresholds.contested,
|
|
266
|
-
B: policy.smart.thresholds.ambient,
|
|
267
|
-
X: policy.smart.thresholds.humanExchange
|
|
268
|
-
})[path];
|
|
269
|
-
export { conversationAddressing, conversationState, conversationThreshold, recordedMessageSignals, vocatives };
|