@segmentstream/agent 0.8.24 → 0.9.1

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.
Files changed (64) hide show
  1. package/dist/_chunks/{build-metadata-BrXE_Dxu.mjs → build-metadata-DMD3azjO.mjs} +1 -1
  2. package/dist/_chunks/content-CvFsrmSD.mjs +102 -0
  3. package/dist/extension/channels/telegram.mjs +1 -0
  4. package/dist/extension/config.d.ts +16 -2
  5. package/dist/extension/config.mjs +92 -3
  6. package/dist/extension/extension.d.ts +7 -0
  7. package/dist/extension/extension.mjs +8 -1
  8. package/dist/extension/hooks/conversations.mjs +1 -1
  9. package/dist/extension/instructions/conversations.mjs +1 -1
  10. package/dist/extension/lib/build-metadata.mjs +1 -1
  11. package/dist/extension/lib/conversation-runtime.mjs +132 -1
  12. package/dist/extension/lib/deferred-confirmations.mjs +1 -1
  13. package/dist/extension/lib/group-routing/engagement.d.ts +18 -0
  14. package/dist/extension/lib/group-routing/engagement.mjs +65 -0
  15. package/dist/extension/lib/group-routing/health.d.ts +11 -0
  16. package/dist/extension/lib/group-routing/health.mjs +36 -0
  17. package/dist/extension/lib/group-routing/observed.d.ts +100 -0
  18. package/dist/extension/lib/group-routing/observed.mjs +103 -0
  19. package/dist/extension/lib/group-routing/policy.d.ts +63 -0
  20. package/dist/extension/lib/group-routing/policy.mjs +162 -0
  21. package/dist/extension/lib/group-routing/record.d.ts +3 -0
  22. package/dist/extension/lib/group-routing/record.mjs +35 -0
  23. package/dist/extension/lib/group-routing/replay.d.ts +18 -0
  24. package/dist/extension/lib/group-routing/replay.mjs +70 -0
  25. package/dist/extension/lib/group-routing/router.d.ts +109 -0
  26. package/dist/extension/lib/group-routing/router.mjs +383 -0
  27. package/dist/extension/lib/group-routing/routing-context.d.ts +4 -0
  28. package/dist/extension/lib/group-routing/routing-context.mjs +96 -0
  29. package/dist/extension/lib/group-routing/settings.d.ts +14 -0
  30. package/dist/extension/lib/group-routing/settings.mjs +58 -0
  31. package/dist/extension/lib/group-routing/signals.d.ts +110 -0
  32. package/dist/extension/lib/group-routing/signals.mjs +47 -0
  33. package/dist/extension/lib/identities.d.ts +3 -0
  34. package/dist/extension/lib/identities.mjs +13 -1
  35. package/dist/extension/lib/ontology-connection.mjs +1 -1
  36. package/dist/extension/lib/ontology-tools.mjs +1 -1
  37. package/dist/extension/lib/telegram/adapter.d.ts +2 -0
  38. package/dist/extension/lib/telegram/adapter.mjs +17 -5
  39. package/dist/extension/lib/telegram/attachments-send.mjs +1 -1
  40. package/dist/extension/lib/telegram/audience.d.ts +3 -26
  41. package/dist/extension/lib/telegram/audience.mjs +1 -5
  42. package/dist/extension/lib/telegram/confirmation-expiry.mjs +2 -2
  43. package/dist/extension/lib/telegram/confirmations.mjs +2 -2
  44. package/dist/extension/lib/telegram/conversations.d.ts +1 -1
  45. package/dist/extension/lib/telegram/conversations.mjs +17 -4
  46. package/dist/extension/lib/telegram/deferred-confirmations.mjs +2 -2
  47. package/dist/extension/lib/telegram/health.d.ts +1 -0
  48. package/dist/extension/lib/telegram/health.mjs +2 -1
  49. package/dist/extension/lib/telegram/inbound.d.ts +7 -0
  50. package/dist/extension/lib/telegram/inbound.mjs +47 -15
  51. package/dist/extension/lib/telegram/lifecycle.mjs +1 -1
  52. package/dist/extension/lib/telegram/observed.d.ts +5 -98
  53. package/dist/extension/lib/telegram/observed.mjs +11 -92
  54. package/dist/extension/lib/telegram/router.d.ts +4 -114
  55. package/dist/extension/lib/telegram/router.mjs +4 -250
  56. package/dist/extension/lib/telegram/routing-context.d.ts +3 -3
  57. package/dist/extension/lib/telegram/routing-context.mjs +7 -86
  58. package/dist/extension/lib/telegram/signals.d.ts +5 -0
  59. package/dist/extension/lib/telegram/signals.mjs +60 -0
  60. package/dist/extension/lib/telegram/silent.mjs +1 -1
  61. package/dist/extension/tools/attachments_promote.mjs +1 -1
  62. package/dist/extension/tools/attachments_send.mjs +1 -1
  63. package/package.json +2 -2
  64. package/dist/_chunks/conversation-runtime-ovs8IhXJ.mjs +0 -212
@@ -0,0 +1,110 @@
1
+ /** Channels report facts; unavailable signals must never be guessed. */
2
+ export interface AgentIdentity {
3
+ readonly id: string;
4
+ readonly username: string | undefined;
5
+ readonly displayName?: string;
6
+ readonly firstName?: string;
7
+ }
8
+ export interface GroupMessageSignals {
9
+ readonly provider: string;
10
+ readonly conversation: {
11
+ readonly externalId: string;
12
+ readonly kind: "direct" | "group" | "channel";
13
+ readonly scope?: string;
14
+ readonly title?: string;
15
+ };
16
+ readonly message: {
17
+ readonly id: string;
18
+ readonly at: string;
19
+ readonly text: string;
20
+ readonly edited?: {
21
+ readonly originalAt: string;
22
+ readonly editedAt: string;
23
+ };
24
+ readonly attachments: ReadonlyArray<{
25
+ readonly kind: string;
26
+ readonly filename?: string;
27
+ readonly mediaType?: string;
28
+ readonly size?: number;
29
+ }>;
30
+ readonly hasCaption: boolean;
31
+ };
32
+ readonly sender: {
33
+ readonly externalId: string;
34
+ readonly username?: string;
35
+ readonly displayName?: string;
36
+ readonly kind: "human" | "bot" | "other_bot" | "unknown";
37
+ readonly member?: {
38
+ readonly id: string;
39
+ readonly name: string | null;
40
+ readonly roles: ReadonlyArray<string>;
41
+ };
42
+ };
43
+ readonly addressing: {
44
+ readonly agentMentioned: boolean;
45
+ readonly command: boolean;
46
+ readonly replyTo?: {
47
+ readonly messageId: string;
48
+ readonly author: "agent" | "human" | "other_bot" | "unknown";
49
+ readonly authorExternalId?: string;
50
+ readonly authorUsername?: string;
51
+ readonly authorName?: string;
52
+ readonly hasAttachments?: boolean;
53
+ readonly self?: boolean;
54
+ };
55
+ readonly mentions: ReadonlyArray<{
56
+ readonly externalId?: string;
57
+ readonly username?: string;
58
+ readonly kind: "human" | "bot" | "other_bot" | "unknown";
59
+ }>;
60
+ readonly threadWithAgent: boolean | "unavailable";
61
+ /** False when a channel does not admit ambient channel messages. */
62
+ readonly eligible?: boolean;
63
+ };
64
+ readonly derived?: {
65
+ readonly transcript?: string;
66
+ readonly attachmentDescription?: string;
67
+ };
68
+ }
69
+ export interface AgentMessageSignals {
70
+ readonly provider: string;
71
+ readonly conversation: GroupMessageSignals["conversation"];
72
+ readonly message: GroupMessageSignals["message"];
73
+ readonly replyToMessageId?: string;
74
+ }
75
+ export interface GroupChannelAdapter<Message, Delivery, Principal> {
76
+ readonly provider: string;
77
+ signals(message: Message, bot: AgentIdentity | undefined): GroupMessageSignals;
78
+ outbound(delivery: Delivery): AgentMessageSignals;
79
+ address(signals: GroupMessageSignals, scope: "chat" | "thread"): string;
80
+ principal(signals: GroupMessageSignals): Principal;
81
+ }
82
+ export declare function conversationKey(signals: Pick<GroupMessageSignals, "provider" | "conversation">): string;
83
+ export interface MessageAudience {
84
+ readonly originalText: string;
85
+ readonly botTagged: boolean;
86
+ readonly mentions: ReadonlyArray<{
87
+ readonly kind: "bot" | "human" | "other_bot" | "unknown";
88
+ readonly id?: string;
89
+ readonly username?: string;
90
+ readonly name?: string;
91
+ readonly type: "mention" | "text_mention";
92
+ readonly text: string;
93
+ }>;
94
+ readonly replyTo: null | {
95
+ readonly kind: "bot" | "human" | "other_bot" | "unknown";
96
+ readonly id?: string;
97
+ readonly username?: string;
98
+ readonly name?: string;
99
+ readonly messageId: string;
100
+ readonly text: string;
101
+ readonly hasAttachments?: boolean;
102
+ readonly filename?: string;
103
+ readonly self?: boolean;
104
+ };
105
+ }
106
+ export declare function signalAudience(signals: GroupMessageSignals): MessageAudience;
107
+ export type AddressingReason = "private" | "mention" | "reply-to-bot" | "command" | "pending-question" | "thread-with-agent";
108
+ export declare function addressingReason(signals: GroupMessageSignals): AddressingReason | undefined;
109
+ /** Explicit recipients win; quoting an upload or one's own message remains inference evidence. */
110
+ export declare function otherRecipient(audience: MessageAudience | undefined): string | undefined;
@@ -0,0 +1,47 @@
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
+ function conversationKey(signals) {
8
+ const scope = signals.conversation.scope === void 0 ? "" : `/${signals.conversation.scope}`;
9
+ return `${signals.provider}:${signals.conversation.externalId}${scope}`;
10
+ }
11
+ function signalAudience(signals) {
12
+ const reply = signals.addressing.replyTo;
13
+ return {
14
+ originalText: signals.message.text,
15
+ botTagged: signals.addressing.agentMentioned,
16
+ mentions: signals.addressing.mentions.map((m) => ({
17
+ kind: m.kind,
18
+ id: m.externalId,
19
+ username: m.username,
20
+ type: "mention",
21
+ text: m.username === void 0 ? "" : `@${m.username}`
22
+ })),
23
+ replyTo: reply === void 0 ? null : {
24
+ kind: reply.author === "agent" ? "bot" : reply.author,
25
+ id: reply.authorExternalId,
26
+ username: reply.authorUsername,
27
+ name: reply.authorName,
28
+ messageId: reply.messageId,
29
+ text: "",
30
+ hasAttachments: reply.hasAttachments,
31
+ self: reply.self
32
+ }
33
+ };
34
+ }
35
+ function addressingReason(signals) {
36
+ if (signals.conversation.kind === "direct") return "private";
37
+ if (signals.addressing.command) return "command";
38
+ if (signals.addressing.agentMentioned) return "mention";
39
+ if (signals.addressing.replyTo?.author === "agent") return "reply-to-bot";
40
+ return signals.addressing.threadWithAgent === true ? "thread-with-agent" : void 0;
41
+ }
42
+ function otherRecipient(audience) {
43
+ if (audience === void 0 || audience.botTagged) return;
44
+ if (audience.mentions.some((item) => item.kind !== "bot")) return "Message tags another recipient; this agent was not addressed.";
45
+ if (audience.replyTo?.kind === "other_bot" || audience.replyTo?.kind === "human" && !audience.replyTo.hasAttachments && !audience.replyTo.self) return "Reply targets another person or bot; this agent was not addressed.";
46
+ }
47
+ export { addressingReason, conversationKey, otherRecipient, signalAudience };
@@ -1,3 +1,4 @@
1
+ import { type ChatOverrideInput } from "@segmentstream-cloud/ontology/groups";
1
2
  import type { AdmissionLimitsInput } from "../config.js";
2
3
  import type { RuntimeToken } from "./runtime-token.js";
3
4
  /**
@@ -68,6 +69,8 @@ export interface ChatAccount {
68
69
  }
69
70
  /** A group chat in the ontology's `agent_chat` registry, with what members said it is for. */
70
71
  export interface AgentChat {
72
+ readonly routing?: ChatOverrideInput;
73
+ readonly scope?: string;
71
74
  readonly id: string;
72
75
  readonly title: string;
73
76
  readonly purpose: string | null;
@@ -5,6 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { redactDiagnostic } from "./diagnostics.mjs";
8
+ import { groupPolicyIssues } from "@segmentstream-cloud/ontology/groups";
8
9
  var AgentIdentityError = class extends Error {
9
10
  name = "AgentIdentityError";
10
11
  code;
@@ -93,11 +94,22 @@ function agentChat(value) {
93
94
  code: "invalid_response",
94
95
  message: "The ontology service answered the chat report with an invalid body."
95
96
  });
97
+ const override = {
98
+ ...isRecord(value.routing) ? value.routing : {},
99
+ ...isRecord(value.participants) ? { participants: value.participants } : {}
100
+ };
101
+ const valid = groupPolicyIssues(override, true).length === 0;
102
+ if (!valid) console.error("[segmentstream] invalid chat routing override", {
103
+ chatId: value.id,
104
+ issues: groupPolicyIssues(override, true)
105
+ });
96
106
  return {
97
107
  id: value.id,
98
108
  title: value.title,
99
109
  purpose: value.purpose,
100
- whenToUse: value.whenToUse
110
+ whenToUse: value.whenToUse,
111
+ ...typeof value.scope === "string" ? { scope: value.scope } : {},
112
+ ...valid && Object.keys(override).length > 0 ? { routing: override } : {}
101
113
  };
102
114
  }
103
115
  async function refusal(response, call) {
@@ -6,9 +6,9 @@ __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { appOrigin } from "./environment.mjs";
8
8
  import { identityProviderOf, memberIdOf } from "./telegram/principal.mjs";
9
+ import { runtimeToken, tokenExpiry } from "./runtime-token.mjs";
9
10
  import { CHAT_POST_ENVELOPE, chatPostDigest, chatPostOperationId, isChatPost } from "./chat-post-confirmation.mjs";
10
11
  import { actionApproval, confirmations } from "./confirmations.mjs";
11
- import { runtimeToken, tokenExpiry } from "./runtime-token.mjs";
12
12
  import { ontologyTools } from "./ontology-tools.mjs";
13
13
  import { defineMcpClientConnection } from "eve/connections";
14
14
  const ACT_AS_HEADER = "X-SegmentStream-Act-As";
@@ -6,10 +6,10 @@ __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { appOrigin } from "./environment.mjs";
8
8
  import { identityProviderOf, memberIdOf } from "./telegram/principal.mjs";
9
+ import { runtimeToken } from "./runtime-token.mjs";
9
10
  import { redactDiagnostic } from "./diagnostics.mjs";
10
11
  import { describeConfirmation, object } from "./confirmation-description.mjs";
11
12
  import { describeChatPost, isChatPost, supportsChatPost } from "./chat-post-confirmation.mjs";
12
- import { runtimeToken } from "./runtime-token.mjs";
13
13
  import { randomUUID } from "node:crypto";
14
14
  var OntologyToolRejected = class extends Error {
15
15
  code = "ontology_tool_rejected";
@@ -1,3 +1,4 @@
1
+ import type { GroupPolicy } from "../group-routing/policy.js";
1
2
  import { type AttachmentDescriber } from "./attachment-description.js";
2
3
  import { type TelegramChannel } from "eve/channels/telegram";
3
4
  import type { ChatModelChoice, AdmissionSettings, SessionSettings, TelegramSettings } from "../../config.js";
@@ -21,6 +22,7 @@ import { type Transcriber } from "./voice.js";
21
22
  export interface TelegramAdapterOptions {
22
23
  /** The mounted `channels.telegram`, or `null` when the workspace has no Telegram channel. */
23
24
  readonly settings: () => TelegramSettings | null;
25
+ readonly groups?: () => GroupPolicy;
24
26
  readonly models: () => {
25
27
  readonly stt: string | false;
26
28
  readonly decision: string;
@@ -8,13 +8,16 @@ import { manifestOf } from "../../config.mjs";
8
8
  import { appOrigin, channelsEnabled, proxyEnvironment, runtimeIdentityEnvironment, webhookSecret } from "../environment.mjs";
9
9
  import { conversationLanguage, telegramPrincipal } from "./principal.mjs";
10
10
  import { chooseModel } from "../../model.mjs";
11
- import { t as agentBuild } from "../../../_chunks/build-metadata-BrXE_Dxu.mjs";
11
+ import { groupRoutingHealth } from "../group-routing/health.mjs";
12
+ import { decisionRouter } from "../group-routing/router.mjs";
13
+ import { runtimeToken } from "../runtime-token.mjs";
14
+ import { groupSettings } from "../group-routing/settings.mjs";
15
+ import { conversations } from "../conversations.mjs";
16
+ import { t as agentBuild } from "../../../_chunks/build-metadata-DMD3azjO.mjs";
12
17
  import { confirmationLocale } from "./confirmation-text.mjs";
13
18
  import { telegramSendScope } from "./send-queue.mjs";
14
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-B8Eyjz1O.mjs";
15
- import { runtimeToken } from "../runtime-token.mjs";
16
20
  import { attachmentLifecycle } from "./lifecycle.mjs";
17
- import { conversations } from "../conversations.mjs";
18
21
  import { scopedAddress, sessionPolicy } from "./sessions.mjs";
19
22
  import { observedMessages } from "./observed.mjs";
20
23
  import { expireDeferredCards, handleDeferredCallback, isDeferredCallback } from "./deferred-confirmations.mjs";
@@ -23,7 +26,6 @@ import { gatewayAttachmentDescriber } from "./attachment-description.mjs";
23
26
  import { MAXIMUM_DOWNLOAD_BYTES, connectionProxy } from "../connection-proxy.mjs";
24
27
  import { agentAttachments, parseAttachmentUrl } from "../attachments.mjs";
25
28
  import { agentIdentities } from "../identities.mjs";
26
- import { decisionRouter } from "./router.mjs";
27
29
  import { replyDelivery } from "./silent.mjs";
28
30
  import { handleConfirmationCallback, telegramConfirmationEvents } from "./confirmations.mjs";
29
31
  import { captureRouteDecider, captureTelegramFetch, conversationIdentities, recordGateDecision, telegramConversationCapture } from "./conversations.mjs";
@@ -68,7 +70,12 @@ const eveInteractions = async (sessionId) => {
68
70
  function segmentstreamTelegram(options) {
69
71
  const environment = options.environment ?? process.env;
70
72
  const token = options.token ?? runtimeToken;
71
- const observed = options.observed ?? observedMessages();
73
+ const readGroups = groupSettings({
74
+ environment,
75
+ token,
76
+ fetch: options.fetch
77
+ });
78
+ const observed = options.observed ?? observedMessages(60);
72
79
  const readHistory = options.history ?? eveHistory;
73
80
  const readInteractions = options.history ?? eveInteractions;
74
81
  const capabilities = options.modelCapabilities ?? gatewayMediaCapabilities(options.fetch);
@@ -166,6 +173,10 @@ function segmentstreamTelegram(options) {
166
173
  request: currentInboundRequest(),
167
174
  dependencies: {
168
175
  policy: settings().policy,
176
+ decisionModel: options.models().decision,
177
+ groups: options.groups?.(),
178
+ cloudGroups: options.groups === void 0 ? void 0 : readGroups,
179
+ signalSettings: settings().groups,
169
180
  recoverHeldApprovals: options.confirmationMode?.() === "deferred",
170
181
  ...settings().attachmentLifecycle === void 0 ? {} : { lifecycle: (principal, memberId, message) => attachmentLifecycle({
171
182
  configuration: settings().attachmentLifecycle,
@@ -403,6 +414,7 @@ function segmentstreamTelegram(options) {
403
414
  capture
404
415
  })), healthRoute({
405
416
  chats,
417
+ ...options.groups === void 0 ? {} : { routing: groupRoutingHealth },
406
418
  enabled: () => channelsEnabled(environment),
407
419
  configured: () => options.settings() !== null,
408
420
  verify: () => verify ??= runtimeTokenVerifier(runtimeIdentityEnvironment(environment)),
@@ -6,11 +6,11 @@ __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { appOrigin, channelsEnabled, proxyEnvironment } from "../environment.mjs";
8
8
  import "./principal.mjs";
9
+ import { runtimeToken } from "../runtime-token.mjs";
9
10
  import { redactDiagnostic } from "../diagnostics.mjs";
10
11
  import { confirmationLocale } from "./confirmation-text.mjs";
11
12
  import { inTelegramSendQueue, telegramSendScope } from "./send-queue.mjs";
12
13
  import { c as customerCopy, f as formattingRefused, l as customerMessage, p as reportTelegramError } from "../../../_chunks/inbound-request-B8Eyjz1O.mjs";
13
- import { runtimeToken } from "../runtime-token.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";
@@ -1,30 +1,7 @@
1
1
  import type { TelegramMessage } from "eve/channels/telegram";
2
2
  import type { BotIdentity } from "./router.js";
3
- type TargetKind = "bot" | "human" | "other_bot" | "unknown";
4
- interface Recipient {
5
- readonly kind: TargetKind;
6
- readonly id?: string;
7
- readonly username?: string;
8
- readonly name?: string;
9
- }
10
- export interface MessageAudience {
11
- readonly originalText: string;
12
- readonly botTagged: boolean;
13
- readonly mentions: ReadonlyArray<Recipient & {
14
- readonly type: "mention" | "text_mention";
15
- readonly text: string;
16
- }>;
17
- readonly replyTo: (Recipient & {
18
- readonly messageId: string;
19
- readonly text: string;
20
- /** A reply can quote evidence instead of addressing its uploader. */
21
- readonly hasAttachments?: boolean;
22
- readonly filename?: string;
23
- readonly self?: boolean;
24
- }) | null;
25
- }
3
+ export type { MessageAudience } from "../group-routing/signals.js";
4
+ import type { MessageAudience } from "../group-routing/signals.js";
26
5
  /** Current-message entities only; mentions inside a quoted/code range are not addressees. */
27
6
  export declare function messageAudience(message: TelegramMessage, bot: BotIdentity | undefined): MessageAudience;
28
- /** Explicit recipients win; quoting an upload or one's own message goes to smart inference. */
29
- export declare function otherRecipient(audience: MessageAudience | undefined): string | undefined;
30
- export {};
7
+ export { otherRecipient } from "../group-routing/signals.js";
@@ -4,6 +4,7 @@ import { createRequire as __eveCreateRequire } from "node:module";
4
4
  const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
+ import { otherRecipient } from "../group-routing/signals.mjs";
7
8
  import { messageContent, telegramRecord } from "./content.mjs";
8
9
  function recipient(user, bot) {
9
10
  const id = typeof user.id === "string" || typeof user.id === "number" ? String(user.id) : void 0;
@@ -85,9 +86,4 @@ function messageAudience(message, bot) {
85
86
  replyTo
86
87
  };
87
88
  }
88
- function otherRecipient(audience) {
89
- if (audience === void 0 || audience.botTagged) return;
90
- if (audience.mentions.some((item) => item.kind !== "bot")) return "Message tags another recipient; this bot was not addressed.";
91
- if (audience.replyTo?.kind === "other_bot" || audience.replyTo?.kind === "human" && !audience.replyTo.hasAttachments && !audience.replyTo.self) return "Reply targets another person or bot; this bot was not addressed.";
92
- }
93
89
  export { messageAudience, otherRecipient };
@@ -5,11 +5,11 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { proxyEnvironment } from "../environment.mjs";
8
+ import { runtimeToken } from "../runtime-token.mjs";
9
+ import { conversations } from "../conversations.mjs";
8
10
  import { telegramSendScope } from "./send-queue.mjs";
9
11
  import { m as safeTelegramFetch, p as reportTelegramError } from "../../../_chunks/inbound-request-B8Eyjz1O.mjs";
10
- import { runtimeToken } from "../runtime-token.mjs";
11
12
  import { deferredConfirmations } from "../deferred-confirmations.mjs";
12
- import { conversations } from "../conversations.mjs";
13
13
  import { expireDeferredRecord } from "./deferred-confirmations.mjs";
14
14
  import { connectionProxy } from "../connection-proxy.mjs";
15
15
  import { TELEGRAM_PLACEHOLDER_TOKEN, TELEGRAM_PROXY_ORIGIN, telegramProxyFetch } from "./proxy-fetch.mjs";
@@ -5,15 +5,15 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { memberIdOf, telegramPrincipal } from "./principal.mjs";
8
+ import { agentMessage, observedChatKey, withConfirmationPrompt } from "../group-routing/observed.mjs";
9
+ import { conversationEvent } from "../conversations.mjs";
8
10
  import { humanText } from "../confirmation-description.mjs";
9
11
  import { renderConfirmation, withConfirmationText } from "./confirmation-text.mjs";
10
12
  import { chatPostCard, chatPostCardStatus, chatPostReceipt, withChatPostCard } from "./chat-post-card.mjs";
11
13
  import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-B8Eyjz1O.mjs";
12
14
  import { chatPostOperationId, isChatPost } from "../chat-post-confirmation.mjs";
13
15
  import { confirmations } from "../confirmations.mjs";
14
- import { conversationEvent } from "../conversations.mjs";
15
16
  import { formatAddress, scopedAddress } from "./sessions.mjs";
16
- import { agentMessage, observedChatKey, withConfirmationPrompt } from "./observed.mjs";
17
17
  import { postDeferredCard } from "./deferred-confirmations.mjs";
18
18
  import { answerMarker, isPendingQuestion, pendingQuestions, questionKey } from "../questions.mjs";
19
19
  import { telegramTextLinks } from "@segmentstream-cloud/ontology/telegram";
@@ -17,7 +17,7 @@ export declare function captureTelegramWebhook(input: {
17
17
  }): Promise<Response>;
18
18
  /** Additive inbound hook: the router keeps ownership of the reply policy. */
19
19
  export declare function recordGateDecision(reason: string, decision?: "reply" | "silent"): void;
20
- export declare function recordRouteDecision(route: "reply" | "observe", addressed: boolean | AddressingReason | undefined, policy: string, audit?: Pick<RouteDecision, "reason" | "note" | "diagnostics">): void;
20
+ export declare function recordRouteDecision(route: "reply" | "observe", addressed: boolean | AddressingReason | undefined, policy: string, audit?: Pick<RouteDecision, "reason" | "note" | "diagnostics" | "router" | "shadow">): void;
21
21
  export declare function captureRouteDecider(decide: RouteDecider): RouteDecider;
22
22
  /** Update the existing inbound event after STT/description, under the same text retention policy. */
23
23
  export declare function recordInboundContent(content: {
@@ -4,11 +4,12 @@ import { createRequire as __eveCreateRequire } from "node:module";
4
4
  const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
- import { n as currentInboundRequest, p as reportTelegramError, s as currentThreadId } from "../../../_chunks/inbound-request-B8Eyjz1O.mjs";
7
+ import { currentConfirmationPrompt, observedChatKey } from "../group-routing/observed.mjs";
8
8
  import { conversationEvent } from "../conversations.mjs";
9
+ import { archivedRoutingMessages } from "../group-routing/routing-context.mjs";
10
+ import { n as currentInboundRequest, p as reportTelegramError, s as currentThreadId } from "../../../_chunks/inbound-request-B8Eyjz1O.mjs";
9
11
  import { contentShape, messageContent } from "./content.mjs";
10
- import { currentConfirmationPrompt, messageMetadata, observedChatKey } from "./observed.mjs";
11
- import { archivedRoutingMessages } from "./routing-context.mjs";
12
+ import { messageMetadata } from "./observed.mjs";
12
13
  import { reportMembership } from "./membership.mjs";
13
14
  import { createHash, randomUUID } from "node:crypto";
14
15
  import { parseTelegramUpdate } from "eve/channels/telegram";
@@ -233,7 +234,7 @@ async function captureTelegramWebhook(input) {
233
234
  });
234
235
  }
235
236
  }
236
- if (!automated) for (const earlier of input.observed.recent(chat).length === 0 ? archivedRoutingMessages((history?.events ?? []).filter((item) => item.eventId !== event.eventId)) : []) if ("role" in earlier || history?.events.some((decision) => decision.kind === "decision" && decision.messageId === earlier.messageId && decision.reason === "turn_admitted")) input.observed.remember(chat, earlier);
237
+ if (!automated) for (const earlier of input.observed.recent(chat).length === 0 ? archivedRoutingMessages((history?.events ?? []).filter((item) => item.eventId !== event.eventId), 60) : []) if ("role" in earlier || history?.events.some((decision) => decision.kind === "decision" && decision.messageId === earlier.messageId && decision.reason === "turn_admitted")) input.observed.remember(chat, earlier);
237
238
  else input.observed.add(chat, earlier);
238
239
  if (automated || edited !== void 0) {
239
240
  if (message.chat.type === "group" || message.chat.type === "supergroup") await input.chats?.seen({
@@ -382,6 +383,8 @@ async function captureTelegramWebhook(input) {
382
383
  kind: "decision",
383
384
  argsSummary: JSON.stringify({
384
385
  ...capture.diagnostics === void 0 ? {} : { decision: capture.diagnostics },
386
+ ...capture.router === void 0 ? {} : { router: capture.router },
387
+ ...capture.shadow === void 0 ? {} : { shadow: capture.shadow },
385
388
  routing: { membership: capture.lookup === void 0 ? "unknown" : capture.lookup.status === "paired" || capture.lookup.status === "existing" || capture.lookup.status === "created" ? "linked" : "unlinked" }
386
389
  }),
387
390
  text: capture.note ?? (edited !== void 0 ? "Message edit recorded as context; no agent turn started." : null),
@@ -409,7 +412,10 @@ function recordRouteDecision(route, addressed, policy, audit) {
409
412
  capture.decision = route === "reply" ? "reply" : "silent";
410
413
  capture.reason ??= audit?.reason ?? (typeof addressed === "string" ? addressed : addressed ? "addressed_message" : policy === "mention" ? "mention_policy" : `decision_model_${route}`);
411
414
  capture.note = audit?.note.slice(0, 500);
415
+ capture.routeReason = audit?.reason;
412
416
  capture.diagnostics = audit?.diagnostics;
417
+ capture.router = audit?.router;
418
+ capture.shadow = audit?.shadow;
413
419
  }
414
420
  }
415
421
  function captureRouteDecider(decide) {
@@ -493,6 +499,13 @@ function captureTelegramFetch(fetchImpl, archive, identities) {
493
499
  ...typeof body.parse_mode === "string" ? { parse_mode: body.parse_mode } : {}
494
500
  },
495
501
  routing: {
502
+ ...capture?.routeReason !== void 0 && [
503
+ "mention",
504
+ "command",
505
+ "reply-to-bot",
506
+ "thread-with-agent",
507
+ "pending-question"
508
+ ].includes(capture.routeReason) ? { invitation: capture.routeReason } : {},
496
509
  ...currentConfirmationPrompt() === void 0 ? {} : { confirmation: currentConfirmationPrompt() },
497
510
  ...capture?.event.chatId === chatId && capture.event.senderId !== null ? { inReplyTo: {
498
511
  id: capture.event.senderId,
@@ -5,6 +5,8 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { telegramPrincipal } from "./principal.mjs";
8
+ import { agentMessage, observedChatKey, withConfirmationPrompt } from "../group-routing/observed.mjs";
9
+ import { conversationEvent } from "../conversations.mjs";
8
10
  import { humanText } from "../confirmation-description.mjs";
9
11
  import { renderConfirmation, withConfirmationText } from "./confirmation-text.mjs";
10
12
  import { chatPostCard, chatPostCardStatus, chatPostReceipt, withChatPostCard } from "./chat-post-card.mjs";
@@ -13,9 +15,7 @@ import { CONFIRMATION_TTL_MS, inputHash } from "../confirmations.mjs";
13
15
  import { OntologyToolRejected } from "../ontology-tools.mjs";
14
16
  import { confirmationOutcome } from "../confirmation-outcome.mjs";
15
17
  import { deferredConfirmations, deferredKey, runDeferredAction } from "../deferred-confirmations.mjs";
16
- import { conversationEvent } from "../conversations.mjs";
17
18
  import { formatAddress } from "./sessions.mjs";
18
- import { agentMessage, observedChatKey, withConfirmationPrompt } from "./observed.mjs";
19
19
  import { telegramTextLinks } from "@segmentstream-cloud/ontology/telegram";
20
20
  const PREFIX = "ss-confirm:d:";
21
21
  const isDeferredCallback = (data) => data?.startsWith(PREFIX) === true;
@@ -6,6 +6,7 @@ export declare const HEALTH_ROUTE = "/eve/v1/telegram/health";
6
6
  /** Runtime-only, per-instance health. It never exposes credentials or private provider bodies. */
7
7
  export declare function healthRoute(input: {
8
8
  readonly chats: AgentChats;
9
+ readonly routing?: () => unknown;
9
10
  readonly enabled: () => boolean;
10
11
  readonly configured: () => boolean;
11
12
  readonly verify: () => RuntimeTokenVerifier;
@@ -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-BrXE_Dxu.mjs";
7
+ import { t as agentBuild } from "../../../_chunks/build-metadata-DMD3azjO.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";
@@ -16,6 +16,7 @@ function healthRoute(input) {
16
16
  return Response.json({
17
17
  status: registry.degraded ? "degraded" : "ok",
18
18
  registry,
19
+ ...input.routing === void 0 ? {} : { routing: input.routing() },
19
20
  runtime: agentBuild(),
20
21
  ...input.configuration === void 0 ? {} : { configuration: input.configuration() }
21
22
  }, {
@@ -1,3 +1,6 @@
1
+ import type { CloudGroupSettings } from "../group-routing/settings.js";
2
+ import { type GroupPolicy } from "../group-routing/policy.js";
3
+ import type { TelegramSettings } from "../../config.js";
1
4
  import { attachmentLifecycle } from "./lifecycle.js";
2
5
  import type { TelegramContext, TelegramInboundResult, TelegramMessage } from "eve/channels/telegram";
3
6
  import type { AdmissionSettings, SessionSettings, TelegramPolicy } from "../../config.js";
@@ -39,6 +42,10 @@ import type { PdfTextExtractor } from "./pdf-text.js";
39
42
  */
40
43
  export interface InboundDependencies {
41
44
  readonly policy: TelegramPolicy;
45
+ readonly decisionModel?: string;
46
+ readonly groups?: GroupPolicy;
47
+ readonly cloudGroups?: () => Promise<CloudGroupSettings | undefined>;
48
+ readonly signalSettings?: TelegramSettings["groups"];
42
49
  /** Recover old held sessions only when the workspace has chosen non-blocking confirmations. */
43
50
  readonly recoverHeldApprovals?: boolean;
44
51
  readonly lifecycle?: (principal: import("eve/context").SessionAuthContext, memberId: string, message: TelegramMessage) => ReturnType<typeof attachmentLifecycle>;