@segmentstream/agent 0.13.0 → 0.15.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.
Files changed (61) hide show
  1. package/README.md +7 -1
  2. package/dist/_chunks/{build-metadata-BLeBanNg.mjs → build-metadata-CJlaXNhc.mjs} +1 -1
  3. package/dist/_chunks/{inbound-request-BXhiIph-.mjs → inbound-request-RIRNBxUC.mjs} +3 -0
  4. package/dist/_chunks/principal-MEMlG3EG.mjs +126 -0
  5. package/dist/extension/config.d.ts +11 -0
  6. package/dist/extension/config.mjs +60 -1
  7. package/dist/extension/hooks/authorization.mjs +2 -2
  8. package/dist/extension/lib/attachment-tools.mjs +1 -1
  9. package/dist/extension/lib/attachments.d.ts +2 -0
  10. package/dist/extension/lib/attachments.mjs +1 -0
  11. package/dist/extension/lib/build-metadata.mjs +1 -1
  12. package/dist/extension/lib/confirmations.mjs +1 -1
  13. package/dist/extension/lib/conversation-runtime.mjs +1 -1
  14. package/dist/extension/lib/deferred-confirmations.d.ts +2 -0
  15. package/dist/extension/lib/deferred-confirmations.mjs +21 -10
  16. package/dist/extension/lib/ontology-connection.d.ts +1 -0
  17. package/dist/extension/lib/ontology-connection.mjs +18 -12
  18. package/dist/extension/lib/ontology-gateway.mjs +9 -2
  19. package/dist/extension/lib/ontology-metadata-read.mjs +1 -1
  20. package/dist/extension/lib/ontology-tools.d.ts +11 -2
  21. package/dist/extension/lib/ontology-tools.mjs +29 -4
  22. package/dist/extension/lib/runtime-envelope.d.ts +28 -0
  23. package/dist/extension/lib/runtime-envelope.mjs +8 -0
  24. package/dist/extension/lib/stored-files.mjs +1 -1
  25. package/dist/extension/lib/telegram/adapter.mjs +41 -7
  26. package/dist/extension/lib/telegram/attachment-description.mjs +1 -1
  27. package/dist/extension/lib/telegram/attachments-send.mjs +3 -3
  28. package/dist/extension/lib/telegram/callbacks.d.ts +18 -0
  29. package/dist/extension/lib/telegram/callbacks.mjs +155 -0
  30. package/dist/extension/lib/telegram/confirmation-expiry.mjs +2 -2
  31. package/dist/extension/lib/telegram/confirmations.mjs +4 -4
  32. package/dist/extension/lib/telegram/conversations.d.ts +3 -0
  33. package/dist/extension/lib/telegram/conversations.mjs +57 -12
  34. package/dist/extension/lib/telegram/customer-text.mjs +1 -1
  35. package/dist/extension/lib/telegram/deferred-confirmations.mjs +5 -5
  36. package/dist/extension/lib/telegram/files.mjs +2 -2
  37. package/dist/extension/lib/telegram/health.mjs +1 -1
  38. package/dist/extension/lib/telegram/images.mjs +1 -1
  39. package/dist/extension/lib/telegram/inbound-request.d.ts +7 -0
  40. package/dist/extension/lib/telegram/inbound-request.mjs +1 -1
  41. package/dist/extension/lib/telegram/inbound.d.ts +5 -0
  42. package/dist/extension/lib/telegram/inbound.mjs +110 -16
  43. package/dist/extension/lib/telegram/lifecycle.mjs +15 -3
  44. package/dist/extension/lib/telegram/media-policy.mjs +1 -1
  45. package/dist/extension/lib/telegram/principal.d.ts +1 -0
  46. package/dist/extension/lib/telegram/principal.mjs +1 -49
  47. package/dist/extension/lib/telegram/sessions.d.ts +3 -1
  48. package/dist/extension/lib/telegram/silent.mjs +3 -3
  49. package/dist/extension/lib/telegram/structured-settings.d.ts +6 -0
  50. package/dist/extension/lib/telegram/structured-settings.mjs +12 -0
  51. package/dist/extension/lib/telegram/structured.d.ts +28 -0
  52. package/dist/extension/lib/telegram/structured.mjs +114 -0
  53. package/dist/extension/lib/telegram/turn-errors.mjs +2 -2
  54. package/dist/extension/lib/telegram/voice.mjs +1 -1
  55. package/dist/extension/lib/telegram/workspace-action.d.ts +24 -0
  56. package/dist/extension/lib/telegram/workspace-action.mjs +29 -0
  57. package/dist/extension/model.mjs +1 -1
  58. package/dist/extension/tools/attachments_promote.mjs +6 -0
  59. package/dist/extension/tools/ontology.mjs +1 -1
  60. package/dist/extension/tools/questions_ask.mjs +1 -1
  61. package/package.json +2 -2
@@ -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-BLeBanNg.mjs";
7
+ import { t as agentBuild } from "../../../_chunks/build-metadata-CJlaXNhc.mjs";
8
8
  import { runtimeRequestFailure } from "./runtime-request.mjs";
9
9
  import { GET } from "eve/channels";
10
10
  const HEALTH_ROUTE = "/eve/v1/telegram/health";
@@ -4,7 +4,7 @@ import { createRequire as __eveCreateRequire } from "node:module";
4
4
  const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
- import { p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
7
+ import { p as reportTelegramError } from "../../../_chunks/inbound-request-RIRNBxUC.mjs";
8
8
  import { gateway, generateText } from "ai";
9
9
  const MAXIMUM_IMAGE_DESCRIPTION_BYTES = 5242880;
10
10
  const MAXIMUM_NATIVE_IMAGE_BYTES = 3145728;
@@ -60,6 +60,13 @@ export declare class InboundRequest {
60
60
  /** Finalize the reservation only after the durable inbox acknowledges delivery. */
61
61
  beginDispatch: (() => Promise<boolean>) | undefined;
62
62
  finishClaim: ((accepted: boolean) => Promise<void>) | undefined;
63
+ /** A known successful hook's continuation, bound to its admitted member. */
64
+ structuredContext: {
65
+ readonly text: string;
66
+ readonly memberId: string;
67
+ } | undefined;
68
+ recordStructuredContext: ((text: string, memberId: string) => Promise<void>) | undefined;
69
+ beginStructuredContinuation: (() => Promise<boolean>) | undefined;
63
70
  locale: string | undefined;
64
71
  threadId: string | undefined;
65
72
  /** The text the turn should carry instead of the parsed message, once a transcript exists. */
@@ -4,5 +4,5 @@ import { createRequire as __eveCreateRequire } from "node:module";
4
4
  const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
- import { i as withReplacedText, n as currentInboundRequest, r as runInboundRequest, t as InboundRequest } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
7
+ import { i as withReplacedText, n as currentInboundRequest, r as runInboundRequest, t as InboundRequest } from "../../../_chunks/inbound-request-RIRNBxUC.mjs";
8
8
  export { InboundRequest, currentInboundRequest, runInboundRequest, withReplacedText };
@@ -1,3 +1,4 @@
1
+ import { type StructuredInbound, type StructuredMessage, type StructuredResult } from "./structured.js";
1
2
  import type { RecentMessage } from "../group-routing/observed.js";
2
3
  import { type GroupPolicy } from "../group-routing/policy.js";
3
4
  import type { TelegramSettings } from "../../config.js";
@@ -41,6 +42,10 @@ import type { PdfTextExtractor } from "./pdf-text.js";
41
42
  * its `<chat>` block.
42
43
  */
43
44
  export interface InboundDependencies {
45
+ readonly structured?: {
46
+ readonly configuration: StructuredInbound;
47
+ readonly run: (principal: import("eve/context").SessionAuthContext, memberId: string, message: TelegramMessage, event: StructuredMessage, beforeDispatch?: () => Promise<void>) => Promise<StructuredResult>;
48
+ };
44
49
  readonly policy: TelegramPolicy;
45
50
  readonly decisionModel?: string;
46
51
  readonly groups?: GroupPolicy;
@@ -6,16 +6,18 @@ __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { effectiveGroupPolicy, groupPolicy } from "../group-routing/policy.mjs";
8
8
  import { admitMessage, localizedText } from "../admission.mjs";
9
- import { conversationLanguage, telegramPrincipal } from "./principal.mjs";
9
+ import { d as telegramRuntimeSource, n as conversationLanguage, o as telegramPrincipal, u as runtimeEnvelope } from "../../../_chunks/principal-MEMlG3EG.mjs";
10
+ import { p as reportTelegramError } from "../../../_chunks/inbound-request-RIRNBxUC.mjs";
10
11
  import { addressingReason, conversationKey, otherRecipient } from "../group-routing/signals.mjs";
11
12
  import { conversationAddressing, conversationState } from "../group-routing/conversation.mjs";
12
13
  import { questionInvitation } from "../group-routing/engagement.mjs";
13
14
  import { conversationContext, routeMessage } from "../group-routing/router.mjs";
14
15
  import { observedContext } from "../group-routing/observed.mjs";
15
16
  import { debounceBurst, routingBurst } from "../group-routing/burst.mjs";
16
- import { p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
17
- import { AttachmentLifecycleNotPermitted, startTypingHeartbeat, typingUntilComplete } from "./lifecycle.mjs";
17
+ import { WorkspaceActionAlreadyDispatched } from "./workspace-action.mjs";
18
+ import { structuredMessage } from "./structured.mjs";
18
19
  import { formatAddress, messageAddress, scopedAddress } from "./sessions.mjs";
20
+ import { AttachmentLifecycleNotPermitted, startTypingHeartbeat, typingUntilComplete } from "./lifecycle.mjs";
19
21
  import { contentShape, messageContent } from "./content.mjs";
20
22
  import { messageAudience } from "./audience.mjs";
21
23
  import { attachmentKind, messageMetadata } from "./observed.mjs";
@@ -225,13 +227,14 @@ async function handleInboundMessage(input) {
225
227
  from: message.from,
226
228
  attachments: inboundAttachments(message)
227
229
  };
230
+ const structured = dependencies.structured === void 0 ? void 0 : structuredMessage(sent.raw, dependencies.structured.configuration.kinds);
228
231
  const bot = await dependencies.bot(context.telegram);
229
232
  const audience = messageAudience(sent, bot);
230
233
  const signals = telegramSignals(sent, bot, dependencies.signalSettings);
231
234
  const addressed = addressingReason(signals);
232
235
  const elsewhere = otherRecipient(audience);
233
236
  if (input.request?.edited) {
234
- if (addressed === void 0 && !input.request.attachmentsChanged && (input.request.previousText ?? "").trim().replace(/\s+/gu, " ") === audience.originalText.trim().replace(/\s+/gu, " ")) {
237
+ if (structured === void 0 && addressed === void 0 && !input.request.attachmentsChanged && (input.request.previousText ?? "").trim().replace(/\s+/gu, " ") === audience.originalText.trim().replace(/\s+/gu, " ")) {
235
238
  recordGateDecision("message_edit");
236
239
  return null;
237
240
  }
@@ -262,7 +265,7 @@ async function handleInboundMessage(input) {
262
265
  reportTelegramError("identity_notice_failed", error, { provider: "telegram" });
263
266
  }
264
267
  });
265
- const turn = addressed !== void 0 && hasContent && (addressed !== "reply-to-bot" || elsewhere === void 0);
268
+ const turn = structured === void 0 && input.request?.structuredContext === void 0 && addressed !== void 0 && hasContent && (addressed !== "reply-to-bot" || elsewhere === void 0);
266
269
  const lookup = await resolve(turn);
267
270
  const group = isGroupChat(sent);
268
271
  const chat = conversationKey(signals);
@@ -334,7 +337,7 @@ async function handleInboundMessage(input) {
334
337
  }
335
338
  };
336
339
  async function reject(refused, reply) {
337
- const replied = hasContent && reply && (refused.status !== "limited" || refused.notify) ? await answerUnpaired({
340
+ const replied = (hasContent || structured !== void 0) && reply && (refused.status !== "limited" || refused.notify) ? await answerUnpaired({
338
341
  context,
339
342
  message: sent,
340
343
  lookup: refused,
@@ -357,13 +360,28 @@ async function handleInboundMessage(input) {
357
360
  return null;
358
361
  }
359
362
  let member = lookup.member;
360
- let turnAdmitted = turn || !lookup.provisioned || member.roles.length !== 1 || member.roles[0] !== "member";
363
+ if (input.request?.structuredContext !== void 0 && input.request.structuredContext.memberId !== member.id) {
364
+ recordGateDecision("structured_context_actor_changed");
365
+ return null;
366
+ }
367
+ let turnAdmitted = turn || input.request?.structuredContext !== void 0 || !lookup.provisioned || member.roles.length !== 1 || member.roles[0] !== "member";
361
368
  let admissionRefused = false;
369
+ let structuredClaimed = input.request?.structuredContext !== void 0;
362
370
  async function admitTurn(reply) {
371
+ if (admissionRefused) return false;
372
+ if (structured !== void 0 && !structuredClaimed) {
373
+ if (input.request?.claimTurn !== void 0 && !await input.request.claimTurn()) {
374
+ admissionRefused = true;
375
+ recordGateDecision("message_already_handled");
376
+ return false;
377
+ }
378
+ structuredClaimed = true;
379
+ }
363
380
  if (turnAdmitted) return true;
364
381
  const admitted = await resolve(true);
365
382
  if (admitted.status !== "existing" && admitted.status !== "created") {
366
383
  admissionRefused = true;
384
+ if (structuredClaimed) await input.request?.finishClaim?.(false);
367
385
  await reject(admitted, reply);
368
386
  return false;
369
387
  }
@@ -474,7 +492,7 @@ async function handleInboundMessage(input) {
474
492
  ].filter((n) => !!n)];
475
493
  const freshConversation = async () => dependencies.recentConversation ? dependencies.recentConversation(sent.chat.id, sent.messageThreadId === void 0 ? void 0 : String(sent.messageThreadId)) : dependencies.observed.recent(chat);
476
494
  let conversationRecent = conversationPolicy ? await freshConversation() : dependencies.observed.recent(chat);
477
- if (conversationPolicy && input.request && input.request.lifecycleKey === void 0) {
495
+ if (conversationPolicy && input.request && input.request.lifecycleKey === void 0 && input.request.structuredContext === void 0) {
478
496
  address = topicAddress;
479
497
  input.request.address = topicAddress;
480
498
  input.request.turnPolicy = "steer";
@@ -503,12 +521,12 @@ async function handleInboundMessage(input) {
503
521
  address = pending.address || (questionSession === void 0 ? topicAddress : memberQuestionAddress);
504
522
  input.request.address = address;
505
523
  }
506
- if (input.request?.lifecycleKey === void 0 && group && (addressed === void 0 && !answering && groups.mode === "mention" || !conversationPolicy && elsewhere !== void 0 && (addressed === void 0 || addressed === "reply-to-bot" && !recipientAnswer))) {
524
+ if (input.request?.lifecycleKey === void 0 && input.request?.structuredContext === void 0 && group && (addressed === void 0 && !answering && groups.mode === "mention" || !conversationPolicy && elsewhere !== void 0 && (addressed === void 0 || addressed === "reply-to-bot" && !recipientAnswer))) {
507
525
  await recordInboundContent({
508
526
  text,
509
527
  decoded
510
528
  });
511
- dependencies.observed.add(chat, observedMessage(sent, text || "[attachment]", member, void 0, bot));
529
+ dependencies.observed.add(chat, observedMessage(sent, text || (structured === void 0 ? "[attachment]" : `[${structured.kind}]`), member, void 0, bot));
512
530
  recordRouteDecision("observe", addressed, groups.mode, {
513
531
  reason: elsewhere === void 0 ? "mention_policy" : "other_recipient",
514
532
  note: elsewhere ?? "Mention policy observes unaddressed messages."
@@ -526,6 +544,9 @@ async function handleInboundMessage(input) {
526
544
  const mediaPlan = voice !== void 0 || sent.attachments.length > 0 || dependencies.observed.list(chat).some((message) => (message.images?.length ?? 0) > 0) ? await dependencies.mediaPlan?.(member, owner?.id) : void 0;
527
545
  if (voice !== void 0 && !group) await transcribeVoice(false);
528
546
  if (voice !== void 0 && mediaPlan?.audio) notes.push("[The chat model supports audio, but this host cannot deliver native audio. The configured speech-to-text fallback was used if enabled.]");
547
+ if (structured !== void 0) {
548
+ if (text.trim() === "") text = `[${structured.kind}]`;
549
+ }
529
550
  if (text.trim() === "" && sent.attachments.length === 0) {
530
551
  console.info("[segmentstream] Telegram message has no readable content", {
531
552
  chatId: sent.chat.id,
@@ -603,7 +624,7 @@ async function handleInboundMessage(input) {
603
624
  return question;
604
625
  };
605
626
  let burstId;
606
- if (conversationPolicy && input.request?.lifecycleKey === void 0) {
627
+ if (conversationPolicy && input.request?.lifecycleKey === void 0 && input.request?.structuredContext === void 0) {
607
628
  if (voice !== void 0) await transcribeVoice(true);
608
629
  const routingSignals = {
609
630
  ...policySignals,
@@ -644,7 +665,7 @@ async function handleInboundMessage(input) {
644
665
  messagesToClassify: burst.messages
645
666
  };
646
667
  }
647
- const route = input.request?.lifecycleKey !== void 0 ? "reply" : await routeMessage({
668
+ const route = input.request?.lifecycleKey !== void 0 || input.request?.structuredContext !== void 0 ? "reply" : await routeMessage({
648
669
  chatId: sent.chat.id,
649
670
  memberId: member.id,
650
671
  policy: groups.mode,
@@ -728,6 +749,74 @@ async function handleInboundMessage(input) {
728
749
  return conversation();
729
750
  }
730
751
  });
752
+ let structuredDispatched = false;
753
+ if (structured !== void 0 && dependencies.structured !== void 0) {
754
+ if (route === "observe" || admissionRefused) {
755
+ await recordInboundContent({
756
+ text,
757
+ decoded
758
+ });
759
+ if (group) dependencies.observed.add(chat, observedMessage(sent, text, member, media, bot));
760
+ return null;
761
+ }
762
+ if (!await admitTurn(true)) return null;
763
+ const retained = input.request?.structuredContext;
764
+ let result;
765
+ if (retained !== void 0) {
766
+ structuredDispatched = true;
767
+ result = { context: retained.text };
768
+ } else {
769
+ const principal = telegramPrincipal({
770
+ message: sent,
771
+ member
772
+ });
773
+ try {
774
+ result = await dependencies.structured.run(principal, member.id, sent, structured, async () => {
775
+ if (input.request?.beginDispatch !== void 0 && !await input.request.beginDispatch()) throw new WorkspaceActionAlreadyDispatched("structured_action_already_dispatched");
776
+ structuredDispatched = true;
777
+ });
778
+ } catch (error) {
779
+ try {
780
+ await input.request?.finishClaim?.(false);
781
+ } catch (cleanupError) {
782
+ reportTelegramError("workspace_structured_claim_release_failed", new AggregateError([error, cleanupError], "Structured action and claim cleanup failed"));
783
+ }
784
+ if (error instanceof WorkspaceActionAlreadyDispatched) {
785
+ recordGateDecision("message_already_handled");
786
+ return null;
787
+ }
788
+ throw error;
789
+ }
790
+ if (result.complete !== true && result.context?.trim()) {
791
+ if (input.request !== void 0) input.request.structuredContext = {
792
+ text: result.context,
793
+ memberId: member.id
794
+ };
795
+ try {
796
+ if (input.request?.recordStructuredContext === void 0) throw new Error("A structured continuation requires durable conversation capture.");
797
+ await input.request.recordStructuredContext(result.context, member.id);
798
+ } catch (error) {
799
+ reportTelegramError("workspace_structured_context_recording_failed", error);
800
+ }
801
+ } else try {
802
+ await input.request?.finishClaim?.(true);
803
+ } catch (error) {
804
+ reportTelegramError("workspace_structured_admission_recording_failed", error);
805
+ }
806
+ if (result.acknowledgement?.trim()) try {
807
+ await context.telegram.post(result.acknowledgement);
808
+ } catch (error) {
809
+ reportTelegramError("workspace_structured_ack_failed", error, {
810
+ chatId: sent.chat.id,
811
+ messageId: sent.messageId
812
+ });
813
+ }
814
+ }
815
+ recordGateDecision("workspace_structured_inbound", result.context ? "reply" : "silent");
816
+ if (result.complete === true || !result.context?.trim()) return null;
817
+ text = result.context;
818
+ if (input.request !== void 0) input.request.text = text;
819
+ }
731
820
  if (input.request?.lifecycleKey !== void 0) recordRouteDecision("reply", addressed, groups.mode, {
732
821
  reason: "workspace_attachment_lifecycle",
733
822
  note: "The configured workspace action admitted this attachment task."
@@ -757,6 +846,7 @@ async function handleInboundMessage(input) {
757
846
  } : mediaPlan },
758
847
  ...dependencies.describeImage === void 0 ? {} : { describeImage: dependencies.describeImage }
759
848
  });
849
+ if (input.request !== void 0) input.request.files = files;
760
850
  if (input.request?.lifecycle !== void 0) {
761
851
  const lifecycle = input.request.lifecycle;
762
852
  const key = input.request.lifecycleKey;
@@ -787,7 +877,10 @@ async function handleInboundMessage(input) {
787
877
  outcome = { result: (await dependencies.attachments.promote({
788
878
  memberId: member.id,
789
879
  conversation: `telegram:${sent.chat.id}`
790
- }, file.attachment.id, stored.retain)).result };
880
+ }, file.attachment.id, {
881
+ ...stored.retain,
882
+ runtime: runtimeEnvelope({ source: telegramRuntimeSource(sent, files) })
883
+ })).result };
791
884
  } catch (error) {
792
885
  outcome = { error: reportTelegramError("attachment_retention_failed", error, { attachmentId: file.attachment.id }) };
793
886
  }
@@ -866,7 +959,7 @@ async function handleInboundMessage(input) {
866
959
  });
867
960
  return null;
868
961
  }
869
- if (input.request?.claimTurn !== void 0 && !await input.request.claimTurn()) {
962
+ if (!structuredDispatched && input.request?.claimTurn !== void 0 && !await input.request.claimTurn()) {
870
963
  recordGateDecision("message_already_handled");
871
964
  return null;
872
965
  }
@@ -957,7 +1050,8 @@ async function handleInboundMessage(input) {
957
1050
  }
958
1051
  const language = conversationLanguage([text, ...dependencies.observed.recent(chat).map((message) => message.text).reverse()]);
959
1052
  const sharedContext = group ? conversationContext(await conversation()) : void 0;
960
- if (input.request?.beginDispatch !== void 0 && !await input.request.beginDispatch()) {
1053
+ const beginTurnDispatch = structuredDispatched ? input.request?.beginStructuredContinuation : input.request?.beginDispatch;
1054
+ if (beginTurnDispatch !== void 0 && !await beginTurnDispatch()) {
961
1055
  recordGateDecision("message_already_handled");
962
1056
  return null;
963
1057
  }
@@ -970,7 +1064,7 @@ async function handleInboundMessage(input) {
970
1064
  member,
971
1065
  conversationLanguage: language
972
1066
  });
973
- if (conversationPolicy && input.request?.lifecycleKey === void 0) principal = {
1067
+ if (conversationPolicy && input.request?.lifecycleKey === void 0 && input.request?.structuredContext === void 0) principal = {
974
1068
  ...principal,
975
1069
  attributes: {
976
1070
  ...principal.attributes,
@@ -4,16 +4,21 @@ 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-BLeBanNg.mjs";
7
+ import { d as telegramRuntimeSource, l as principalRuntimeSource, u as runtimeEnvelope } from "../../../_chunks/principal-MEMlG3EG.mjs";
8
8
  import { object } from "../confirmation-description.mjs";
9
- import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
9
+ import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-RIRNBxUC.mjs";
10
10
  import { needsConfirmation } from "../confirmations.mjs";
11
11
  import { OntologyActionNotPermitted, actionToolName, ontologyTools } from "../ontology-tools.mjs";
12
+ import { t as agentBuild } from "../../../_chunks/build-metadata-CJlaXNhc.mjs";
12
13
  var AttachmentLifecycleNotPermitted = class extends Error {
13
14
  code = "attachment_lifecycle_not_permitted";
14
15
  };
15
16
  function attachmentLifecycle(options) {
16
- const tools = ontologyTools(options);
17
+ const source = () => options.message.raw === void 0 ? principalRuntimeSource(options.principal) : telegramRuntimeSource({
18
+ ...options.message,
19
+ attachments: options.message.attachments ?? [],
20
+ raw: options.message.raw
21
+ }, currentInboundRequest()?.files);
17
22
  const action = actionToolName(options.configuration.entity, options.configuration.action);
18
23
  return async (phase, data = {}) => {
19
24
  console.info("[segmentstream] telegram.phase", {
@@ -40,6 +45,13 @@ function attachmentLifecycle(options) {
40
45
  ...options.configuration.contextVersion === 2 && typeof options.message.raw?.date === "number" && Number.isSafeInteger(options.message.raw.date) && options.message.raw.date > 0 ? { source_at: options.message.raw.date * 1e3 } : {}
41
46
  }
42
47
  };
48
+ const tools = ontologyTools({
49
+ ...options,
50
+ envelope: runtimeEnvelope({
51
+ principal: options.principal,
52
+ source: source()
53
+ })
54
+ });
43
55
  const labels = await tools.labels(options.memberId, action, input).catch((error) => {
44
56
  throw error instanceof OntologyActionNotPermitted ? new AttachmentLifecycleNotPermitted("The sender may not run the lifecycle action.", { cause: error }) : error;
45
57
  });
@@ -5,7 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { DEFAULT_IMAGE_DESCRIPTION_MODEL } from "../../config.mjs";
8
- import { p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
8
+ import { p as reportTelegramError } from "../../../_chunks/inbound-request-RIRNBxUC.mjs";
9
9
  function planMedia(capabilities, descriptionModel = null) {
10
10
  if (typeof descriptionModel === "string") return {
11
11
  images: "describe",
@@ -14,6 +14,7 @@ export declare function telegramPrincipal(input: {
14
14
  };
15
15
  readonly member: PairedMember;
16
16
  readonly conversationLanguage?: string;
17
+ readonly callbackQueryId?: string;
17
18
  }): SessionAuthContext;
18
19
  /** Short answers and attachment-only turns retain the recent chat language when known. */
19
20
  export declare function conversationLanguage(texts: readonly string[]): "ru" | "en" | undefined;
@@ -4,53 +4,5 @@ import { createRequire as __eveCreateRequire } from "node:module";
4
4
  const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
- const TELEGRAM_AUTHENTICATOR = "segmentstream-telegram";
8
- function isGroup(message) {
9
- return message.chat.type === "group" || message.chat.type === "supergroup";
10
- }
11
- function telegramPrincipal(input) {
12
- const { message, member } = input;
13
- const sender = message.from.id;
14
- const attributes = {
15
- memberId: member.id,
16
- identityProvider: "telegram",
17
- roles: member.roles,
18
- chatId: message.chat.id,
19
- chatType: message.chat.type
20
- };
21
- if (member.name !== null) attributes.memberName = member.name;
22
- if (message.messageThreadId !== void 0) attributes.messageThreadId = String(message.messageThreadId);
23
- if (message.from.username !== void 0) attributes.username = message.from.username;
24
- const locale = member.locale ?? message.from.languageCode;
25
- if (locale !== void 0) attributes.locale = locale;
26
- const language = input.conversationLanguage ?? conversationLanguage([message.text || message.caption]);
27
- if (language !== void 0) attributes.conversationLanguage = language;
28
- return {
29
- authenticator: TELEGRAM_AUTHENTICATOR,
30
- issuer: "telegram",
31
- principalId: isGroup(message) ? `telegram:${message.chat.id}:${sender}` : `telegram:${sender}`,
32
- principalType: "user",
33
- subject: sender,
34
- attributes
35
- };
36
- }
37
- function conversationLanguage(texts) {
38
- for (const text of texts) {
39
- if (/[\u0430-\u044f\u0451]/iu.test(text)) return "ru";
40
- if ((text.match(/\b[a-z]+\b/gi)?.length ?? 0) >= 3) return "en";
41
- }
42
- }
43
- function memberIdOf(principal) {
44
- const memberId = principal?.attributes.memberId;
45
- return typeof memberId === "string" && memberId !== "" ? memberId : void 0;
46
- }
47
- function rolesOf(principal) {
48
- const roles = principal?.attributes.roles;
49
- if (typeof roles === "string") return [roles];
50
- return roles ?? [];
51
- }
52
- function identityProviderOf(principal) {
53
- const provider = principal?.attributes.identityProvider;
54
- return typeof provider === "string" && provider !== "" ? provider : void 0;
55
- }
7
+ import { a as rolesOf, i as memberIdOf, n as conversationLanguage, o as telegramPrincipal, r as identityProviderOf, t as TELEGRAM_AUTHENTICATOR } from "../../../_chunks/principal-MEMlG3EG.mjs";
56
8
  export { TELEGRAM_AUTHENTICATOR, conversationLanguage, identityProviderOf, memberIdOf, rolesOf, telegramPrincipal };
@@ -1,5 +1,6 @@
1
1
  import type { TelegramMessage } from "eve/channels/telegram";
2
- import type { CancelTurnResult } from "eve/channels";
2
+ import type { CancelTurnResult, ChannelSource } from "eve/channels";
3
+ import type { TelegramChannelState } from "eve/channels/telegram";
3
4
  import type { SessionSettings } from "../../config.js";
4
5
  /**
5
6
  * Session policy for the Telegram channel (`agent-design.md`, section 4, and docs/agent.md,
@@ -62,6 +63,7 @@ export interface SessionOperations {
62
63
  }) => Promise<CancelTurnResult>;
63
64
  } | undefined>;
64
65
  readonly from: (address: string) => {
66
+ readonly send?: ChannelSource<TelegramChannelState>["send"];
65
67
  compact(): Promise<unknown>;
66
68
  clear(): Promise<unknown>;
67
69
  };
@@ -4,12 +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 "./principal.mjs";
8
- import { agentMessage, observedChatKey } from "../group-routing/observed.mjs";
7
+ import "../../../_chunks/principal-MEMlG3EG.mjs";
9
8
  import { confirmationLocale } from "./confirmation-text.mjs";
10
9
  import { splitTelegramText } from "./format.mjs";
11
10
  import { inTelegramSendQueue } from "./send-queue.mjs";
12
- import { a as TelegramDeliveryError, l as customerMessage, o as currentLocale, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
11
+ import { a as TelegramDeliveryError, l as customerMessage, o as currentLocale, p as reportTelegramError } from "../../../_chunks/inbound-request-RIRNBxUC.mjs";
12
+ import { agentMessage, observedChatKey } from "../group-routing/observed.mjs";
13
13
  const SILENT_MARKERS = new Set(["[silent]", "no_reply"]);
14
14
  function isSilentReply(text) {
15
15
  return SILENT_MARKERS.has(text.trim().toLowerCase());
@@ -0,0 +1,6 @@
1
+ import type { WorkspaceActionHook } from "./workspace-action.js";
2
+ export declare const STRUCTURED_KINDS: readonly ["location", "venue", "contact"];
3
+ export type StructuredKind = (typeof STRUCTURED_KINDS)[number];
4
+ export interface StructuredInbound extends WorkspaceActionHook {
5
+ readonly kinds: ReadonlyArray<StructuredKind>;
6
+ }
@@ -0,0 +1,12 @@
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 STRUCTURED_KINDS = [
8
+ "location",
9
+ "venue",
10
+ "contact"
11
+ ];
12
+ export { STRUCTURED_KINDS };
@@ -0,0 +1,28 @@
1
+ import type { TelegramMessage } from "eve/channels/telegram";
2
+ import type { SessionAuthContext } from "eve/context";
3
+ import type { Environment } from "../environment.js";
4
+ import type { RuntimeToken } from "../runtime-token.js";
5
+ import type { StructuredInbound, StructuredKind } from "./structured-settings.js";
6
+ export { STRUCTURED_KINDS, type StructuredInbound, type StructuredKind, } from "./structured-settings.js";
7
+ export interface StructuredMessage {
8
+ readonly kind: StructuredKind;
9
+ readonly data: Readonly<Record<string, unknown>>;
10
+ }
11
+ export interface StructuredResult {
12
+ readonly acknowledgement?: string;
13
+ readonly context?: string;
14
+ readonly complete?: boolean;
15
+ }
16
+ /** Just declared provider fields, bounded to 16 KiB; unknown kinds and malformed payloads stay undispatched. */
17
+ export declare function structuredMessage(raw: Readonly<Record<string, unknown>>, kinds: ReadonlyArray<StructuredKind>): StructuredMessage | undefined;
18
+ export declare function dispatchStructuredMessage(input: {
19
+ readonly configuration: StructuredInbound;
20
+ readonly event: StructuredMessage;
21
+ readonly principal: SessionAuthContext;
22
+ readonly memberId: string;
23
+ readonly message: TelegramMessage;
24
+ readonly environment?: Environment;
25
+ readonly token?: RuntimeToken;
26
+ readonly fetch?: typeof fetch;
27
+ readonly beforeDispatch?: () => Promise<void>;
28
+ }): Promise<StructuredResult>;
@@ -0,0 +1,114 @@
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
+ import { STRUCTURED_KINDS } from "./structured-settings.mjs";
8
+ import { d as telegramRuntimeSource, u as runtimeEnvelope } from "../../../_chunks/principal-MEMlG3EG.mjs";
9
+ import { workspaceAction } from "./workspace-action.mjs";
10
+ const record = (value) => {
11
+ if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("Invalid structured Telegram object.");
12
+ return value;
13
+ };
14
+ function text(value, maximum) {
15
+ if (typeof value !== "string" || value.length > maximum) throw new Error("Invalid structured Telegram text field.");
16
+ return value;
17
+ }
18
+ function numeric(value, minimum, maximum, integer = false) {
19
+ if (typeof value !== "number" || !Number.isFinite(value) || value < minimum || value > maximum || integer && !Number.isSafeInteger(value)) throw new Error("Invalid structured Telegram number field.");
20
+ return value;
21
+ }
22
+ function location(value) {
23
+ const data = record(value);
24
+ const result = {
25
+ latitude: numeric(data.latitude, -90, 90),
26
+ longitude: numeric(data.longitude, -180, 180)
27
+ };
28
+ for (const [key, minimum, maximum, integer] of [
29
+ [
30
+ "horizontal_accuracy",
31
+ 0,
32
+ 1500,
33
+ false
34
+ ],
35
+ [
36
+ "live_period",
37
+ 0,
38
+ 2 ** 31 - 1,
39
+ true
40
+ ],
41
+ [
42
+ "heading",
43
+ 1,
44
+ 360,
45
+ true
46
+ ],
47
+ [
48
+ "proximity_alert_radius",
49
+ 0,
50
+ 1e5,
51
+ true
52
+ ]
53
+ ]) if (data[key] !== void 0) result[key] = numeric(data[key], minimum, maximum, integer);
54
+ return result;
55
+ }
56
+ function structuredMessage(raw, kinds) {
57
+ for (const kind of [
58
+ "venue",
59
+ "contact",
60
+ "location"
61
+ ]) {
62
+ if (raw[kind] === void 0) continue;
63
+ if (!kinds.includes(kind)) return;
64
+ try {
65
+ const input = record(raw[kind]);
66
+ const data = kind === "location" ? location(input) : kind === "venue" ? {
67
+ location: location(input.location),
68
+ title: text(input.title, 1024),
69
+ address: text(input.address, 1024)
70
+ } : {
71
+ phone_number: text(input.phone_number, 256),
72
+ first_name: text(input.first_name, 256)
73
+ };
74
+ if (kind === "venue") {
75
+ for (const key of [
76
+ "foursquare_id",
77
+ "foursquare_type",
78
+ "google_place_id",
79
+ "google_place_type"
80
+ ]) if (input[key] !== void 0) data[key] = text(input[key], 1024);
81
+ }
82
+ if (kind === "contact") {
83
+ for (const [key, maximum] of [["last_name", 256], ["vcard", 16384]]) if (input[key] !== void 0) data[key] = text(input[key], maximum);
84
+ if (input.user_id !== void 0) data.user_id = numeric(input.user_id, 0, Number.MAX_SAFE_INTEGER, true);
85
+ }
86
+ if (new TextEncoder().encode(JSON.stringify(data)).byteLength <= 16384) return {
87
+ kind,
88
+ data
89
+ };
90
+ } catch {
91
+ return;
92
+ }
93
+ }
94
+ }
95
+ async function dispatchStructuredMessage(input) {
96
+ const source = telegramRuntimeSource(input.message);
97
+ const result = await workspaceAction({
98
+ ...input,
99
+ input: {
100
+ ...input.event,
101
+ source
102
+ },
103
+ envelope: runtimeEnvelope({
104
+ principal: input.principal,
105
+ source
106
+ })
107
+ });
108
+ return {
109
+ ...typeof result.acknowledgement === "string" && result.acknowledgement.trim() !== "" && result.acknowledgement.length <= 4096 ? { acknowledgement: result.acknowledgement } : {},
110
+ ...typeof result.context === "string" && result.context.length <= 16384 ? { context: result.context } : {},
111
+ ...typeof result.complete === "boolean" ? { complete: result.complete } : {}
112
+ };
113
+ }
114
+ export { STRUCTURED_KINDS, dispatchStructuredMessage, structuredMessage };
@@ -5,9 +5,9 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { telegramText } from "./texts.mjs";
8
- import { memberIdOf } from "./principal.mjs";
8
+ import { i as memberIdOf } from "../../../_chunks/principal-MEMlG3EG.mjs";
9
9
  import { confirmationLocale } from "./confirmation-text.mjs";
10
- import { c as customerCopy, d as failureText, o as currentLocale, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
10
+ import { c as customerCopy, d as failureText, o as currentLocale, p as reportTelegramError } from "../../../_chunks/inbound-request-RIRNBxUC.mjs";
11
11
  const telegramFailureEvents = {
12
12
  async "authorization.required"(event, channel, context) {
13
13
  if (event.candidateId !== void 0) return;
@@ -4,7 +4,7 @@ import { createRequire as __eveCreateRequire } from "node:module";
4
4
  const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
- import { p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
7
+ import { p as reportTelegramError } from "../../../_chunks/inbound-request-RIRNBxUC.mjs";
8
8
  import "../connection-proxy.mjs";
9
9
  import { gateway, transcribe } from "ai";
10
10
  const LABELS = {