@segmentstream/agent 0.26.1 → 0.27.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 (49) hide show
  1. package/README.md +41 -43
  2. package/dist/_chunks/{build-metadata-D_Gqlb5f.mjs → build-metadata-R_ssK2uR.mjs} +1 -1
  3. package/dist/extension/config.d.ts +11 -12
  4. package/dist/extension/config.mjs +13 -36
  5. package/dist/extension/extension.d.ts +1 -1
  6. package/dist/extension/extension.mjs +2 -2
  7. package/dist/extension/hooks/chat-context.d.ts +3 -0
  8. package/dist/extension/hooks/chat-context.mjs +10 -0
  9. package/dist/extension/hooks/sandbox.d.ts +2 -3
  10. package/dist/extension/instructions/conversations.d.ts +3 -2
  11. package/dist/extension/instructions/conversations.mjs +7 -11
  12. package/dist/extension/instructions.md +18 -0
  13. package/dist/extension/lib/build-metadata.mjs +1 -1
  14. package/dist/extension/lib/chat-context.d.ts +108 -0
  15. package/dist/extension/lib/chat-context.mjs +217 -0
  16. package/dist/extension/lib/conversations.d.ts +10 -6
  17. package/dist/extension/lib/conversations.mjs +28 -42
  18. package/dist/extension/lib/group-routing/burst.d.ts +1 -1
  19. package/dist/extension/lib/group-routing/conversation.d.ts +1 -1
  20. package/dist/extension/lib/group-routing/engagement.d.ts +1 -1
  21. package/dist/extension/lib/group-routing/{observed.d.ts → messages.d.ts} +9 -31
  22. package/dist/extension/lib/group-routing/messages.mjs +38 -0
  23. package/dist/extension/lib/group-routing/router.d.ts +3 -9
  24. package/dist/extension/lib/group-routing/router.mjs +2 -4
  25. package/dist/extension/lib/group-routing/routing-context.d.ts +6 -2
  26. package/dist/extension/lib/group-routing/routing-context.mjs +12 -1
  27. package/dist/extension/lib/telegram/adapter.d.ts +1 -3
  28. package/dist/extension/lib/telegram/adapter.mjs +9 -18
  29. package/dist/extension/lib/telegram/confirmations.d.ts +3 -3
  30. package/dist/extension/lib/telegram/confirmations.mjs +3 -25
  31. package/dist/extension/lib/telegram/conversations.d.ts +14 -3
  32. package/dist/extension/lib/telegram/conversations.mjs +16 -37
  33. package/dist/extension/lib/telegram/delivery-diagnostics.d.ts +4 -1
  34. package/dist/extension/lib/telegram/delivery-diagnostics.mjs +9 -3
  35. package/dist/extension/lib/telegram/health.mjs +1 -1
  36. package/dist/extension/lib/telegram/inbound.d.ts +14 -14
  37. package/dist/extension/lib/telegram/inbound.mjs +72 -68
  38. package/dist/extension/lib/telegram/lifecycle.mjs +1 -1
  39. package/dist/extension/lib/telegram/{observed.d.ts → message-metadata.d.ts} +2 -6
  40. package/dist/extension/lib/telegram/{observed.mjs → message-metadata.mjs} +1 -14
  41. package/dist/extension/lib/telegram/routing-context.d.ts +4 -3
  42. package/dist/extension/lib/telegram/routing-context.mjs +2 -2
  43. package/dist/extension/lib/telegram/sessions.d.ts +5 -52
  44. package/dist/extension/lib/telegram/sessions.mjs +1 -188
  45. package/dist/extension/lib/telegram/signals.mjs +1 -1
  46. package/dist/extension/lib/telegram/silent.d.ts +5 -6
  47. package/dist/extension/lib/telegram/silent.mjs +4 -31
  48. package/package.json +1 -1
  49. package/dist/extension/lib/group-routing/observed.mjs +0 -103
@@ -11,27 +11,26 @@ import { p as reportTelegramError } from "../../../_chunks/inbound-request-D1r0F
11
11
  import { addressingReason, conversationKey, otherRecipient } from "../group-routing/signals.mjs";
12
12
  import { conversationAddressing, conversationState } from "../group-routing/conversation.mjs";
13
13
  import { questionInvitation } from "../group-routing/engagement.mjs";
14
- import { conversationContext, routeMessage } from "../group-routing/router.mjs";
15
- import { observedContext } from "../group-routing/observed.mjs";
14
+ import { routeMessage } from "../group-routing/router.mjs";
16
15
  import { debounceBurst, routingBurst } from "../group-routing/burst.mjs";
17
16
  import { WorkspaceActionAlreadyDispatched } from "./workspace-action.mjs";
18
17
  import { structuredMessage } from "./structured.mjs";
19
- import { evidenceSince, formatAddress, messageAddress, scopedAddress } from "./sessions.mjs";
18
+ import { formatAddress, messageAddress, scopedAddress } from "./sessions.mjs";
20
19
  import { AttachmentLifecycleNotPermitted } from "./lifecycle.mjs";
21
20
  import { MAXIMUM_NATIVE_IMAGE_BYTES } from "./images.mjs";
22
21
  import { MAXIMUM_ROUTING_ATTACHMENT_BYTES, describeRoutingAttachment, routingAttachmentDeadline } from "./attachment-description.mjs";
23
22
  import { attachmentUrl } from "../attachments.mjs";
24
23
  import { contentShape, messageContent } from "./content.mjs";
25
24
  import { messageAudience } from "./audience.mjs";
26
- import { attachmentKind, messageMetadata } from "./observed.mjs";
27
- import { telegramSignals } from "./signals.mjs";
25
+ import { attachmentKind, messageMetadata } from "./message-metadata.mjs";
26
+ import { recordGateDecision, recordInboundContent, recordRouteDecision } from "./conversations.mjs";
28
27
  import { answerMarker } from "../questions.mjs";
29
28
  import { questionAddress, questionFromHistory } from "./confirmations.mjs";
30
- import { routingContext } from "./routing-context.mjs";
31
- import { recordGateDecision, recordInboundContent, recordRouteDecision } from "./conversations.mjs";
32
29
  import { chatContext } from "./chats.mjs";
30
+ import { telegramSignals } from "./signals.mjs";
33
31
  import { attachmentsContext, inboundAttachments, stageFiles } from "./files.mjs";
34
32
  import { heldApprovals, releaseHeldApproval } from "./held-approvals.mjs";
33
+ import { routingContext } from "./routing-context.mjs";
35
34
  import { voiceOf, voiceText } from "./voice.mjs";
36
35
  const MAXIMUM_QUOTE_LENGTH = 500;
37
36
  function buttonTo(text, url) {
@@ -68,6 +67,10 @@ function observedMessage(message, text, member, media, bot) {
68
67
  ...media ? { media } : {}
69
68
  };
70
69
  }
70
+ function observedImages(recent, current) {
71
+ const answered = recent.findLastIndex((message) => message.admitted === true);
72
+ return recent.slice(answered + 1).filter((message) => !("role" in message) && message.messageId !== current).flatMap((message) => message.images ?? []);
73
+ }
71
74
  function isRecord(value) {
72
75
  return typeof value === "object" && value !== null && !Array.isArray(value);
73
76
  }
@@ -268,7 +271,19 @@ async function handleInboundMessage(input) {
268
271
  const turn = structured === void 0 && input.request?.structuredContext === void 0 && addressed !== void 0 && hasContent && (addressed !== "reply-to-bot" || elsewhere === void 0);
269
272
  const lookup = await resolve(turn);
270
273
  const group = isGroupChat(sent);
271
- const chat = conversationKey(signals);
274
+ const freshConversation = async () => await dependencies.recentConversation?.(sent.chat.id, sent.messageThreadId === void 0 ? void 0 : String(sent.messageThreadId)) ?? [];
275
+ let archived;
276
+ const recentConversation = () => archived ??= freshConversation();
277
+ const languageOf = async (current) => {
278
+ const own = conversationLanguage([current]);
279
+ if (own !== void 0) return own;
280
+ try {
281
+ return conversationLanguage((await recentConversation()).map((message) => message.text).reverse());
282
+ } catch (error) {
283
+ reportTelegramError("conversation_language_unavailable", error, { chatId: sent.chat.id });
284
+ return;
285
+ }
286
+ };
272
287
  const report = group ? dependencies.chats.seen({
273
288
  chatId: sent.chat.id,
274
289
  title: sent.chat.title ?? "",
@@ -349,9 +364,9 @@ async function handleInboundMessage(input) {
349
364
  async function observeRaw() {
350
365
  await recordInboundContent({
351
366
  text,
352
- decoded
367
+ decoded,
368
+ author: authorOf(sent.from, void 0)
353
369
  });
354
- if (group) dependencies.observed.add(chat, observedMessage(sent, text || "[attachment]", void 0, void 0, bot));
355
370
  }
356
371
  if (lookup.status !== "existing" && lookup.status !== "created") {
357
372
  const configuredGroupPairing = lookup.status === "pairing_required" && localizedText(dependencies.admission().replies.groupPairingRequired, sent.from.languageCode) !== void 0;
@@ -360,6 +375,10 @@ async function handleInboundMessage(input) {
360
375
  return null;
361
376
  }
362
377
  let member = lookup.member;
378
+ const recordContent = (content) => recordInboundContent({
379
+ ...content,
380
+ author: authorOf(sent.from, member)
381
+ });
363
382
  if (input.request?.structuredContext !== void 0 && input.request.structuredContext.memberId !== member.id) {
364
383
  recordGateDecision("structured_context_actor_changed");
365
384
  return null;
@@ -393,7 +412,7 @@ async function handleInboundMessage(input) {
393
412
  const principal = telegramPrincipal({
394
413
  message: sent,
395
414
  member,
396
- conversationLanguage: conversationLanguage([sent.text || sent.caption, ...dependencies.observed.recent(chat).map((message) => message.text).reverse()])
415
+ conversationLanguage: await languageOf(sent.text || sent.caption)
397
416
  });
398
417
  input.request.lifecycle = dependencies.lifecycle(principal, member.id, sent);
399
418
  await context.telegram.startTyping();
@@ -481,8 +500,7 @@ async function handleInboundMessage(input) {
481
500
  bot?.firstName,
482
501
  bot?.displayName
483
502
  ].filter((n) => !!n)];
484
- const freshConversation = async () => dependencies.recentConversation ? dependencies.recentConversation(sent.chat.id, sent.messageThreadId === void 0 ? void 0 : String(sent.messageThreadId)) : dependencies.observed.recent(chat);
485
- let conversationRecent = conversationPolicy ? await freshConversation() : dependencies.observed.recent(chat);
503
+ let conversationRecent = conversationPolicy ? await recentConversation() : [];
486
504
  if (conversationPolicy && input.request && input.request.lifecycleKey === void 0 && input.request.structuredContext === void 0) {
487
505
  address = topicAddress;
488
506
  input.request.address = topicAddress;
@@ -512,11 +530,10 @@ async function handleInboundMessage(input) {
512
530
  input.request.address = address;
513
531
  }
514
532
  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))) {
515
- await recordInboundContent({
516
- text,
533
+ await recordContent({
534
+ text: text || (structured === void 0 ? text : `[${structured.kind}]`),
517
535
  decoded
518
536
  });
519
- dependencies.observed.add(chat, observedMessage(sent, text || (structured === void 0 ? "[attachment]" : `[${structured.kind}]`), member, void 0, bot));
520
537
  recordRouteDecision("observe", addressed, groups.mode, {
521
538
  reason: elsewhere === void 0 ? "mention_policy" : "other_recipient",
522
539
  note: elsewhere ?? "Mention policy observes unaddressed messages."
@@ -531,7 +548,12 @@ async function handleInboundMessage(input) {
531
548
  await observeRaw();
532
549
  return null;
533
550
  }
534
- 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;
551
+ if (group && !conversationPolicy) conversationRecent = (await recentConversation()).filter((message) => message.messageId !== sent.messageId).map(({ signedAuthor, ...message }) => signedAuthor === void 0 ? message : {
552
+ ...message,
553
+ author: signedAuthor
554
+ });
555
+ const carried = observedImages(conversationRecent, sent.messageId);
556
+ const mediaPlan = voice !== void 0 || sent.attachments.length > 0 || carried.length > 0 ? await dependencies.mediaPlan?.(member, owner?.id) : void 0;
535
557
  if (voice !== void 0 && !group) await transcribeVoice(false);
536
558
  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.]");
537
559
  if (structured !== void 0) {
@@ -575,7 +597,7 @@ async function handleInboundMessage(input) {
575
597
  address,
576
598
  operations,
577
599
  read: dependencies.history ?? dependencies.readHistory,
578
- remembered: conversationPolicy ? conversationRecent : dependencies.observed.recent(chat),
600
+ remembered: conversationRecent,
579
601
  at: sentAt(sent),
580
602
  questionHistory,
581
603
  questionSessionId: owner?.id
@@ -625,7 +647,7 @@ async function handleInboundMessage(input) {
625
647
  };
626
648
  const evidence = conversationAddressing(routingSignals, conversationRecent, groups, agentAliases);
627
649
  if (!evidence.agent_named && !evidence.reply_to_agent && evidence.human_targets.length === 0) {
628
- await recordInboundContent({
650
+ await recordContent({
629
651
  text,
630
652
  decoded
631
653
  });
@@ -718,7 +740,7 @@ async function handleInboundMessage(input) {
718
740
  ...notes
719
741
  ].join("\n");
720
742
  } else if (voice !== void 0) media = [...attachmentMetadata, ...notes].join("\n");
721
- await recordInboundContent({
743
+ await recordContent({
722
744
  text,
723
745
  decoded,
724
746
  ...processedMedia === void 0 ? {} : { media: processedMedia }
@@ -743,11 +765,10 @@ async function handleInboundMessage(input) {
743
765
  let structuredDispatched = false;
744
766
  if (structured !== void 0 && dependencies.structured !== void 0) {
745
767
  if (route === "observe" || admissionRefused) {
746
- await recordInboundContent({
768
+ await recordContent({
747
769
  text,
748
770
  decoded
749
771
  });
750
- if (group) dependencies.observed.add(chat, observedMessage(sent, text, member, media, bot));
751
772
  return null;
752
773
  }
753
774
  if (!await admitTurn(true)) return null;
@@ -813,14 +834,12 @@ async function handleInboundMessage(input) {
813
834
  note: "The configured workspace action admitted this attachment task."
814
835
  });
815
836
  if (admissionRefused) {
816
- await recordInboundContent({
837
+ await recordContent({
817
838
  text,
818
839
  decoded
819
840
  });
820
- dependencies.observed.add(chat, observedMessage(sent, text || "[attachment]", member, void 0, bot));
821
841
  return null;
822
842
  }
823
- if (group && route === "observe") dependencies.observed.add(chat, observedMessage(sent, text, member, media, bot));
824
843
  if (sent.attachments.length > 0) await report;
825
844
  let files = input.request?.edited && !input.request.attachmentsChanged && (input.request.previousContent?.decoded.length ?? 0) > 0 || sent.attachments.length === 0 || dependencies.attachments === void 0 || input.request === void 0 ? new Map() : await stageFiles({
826
845
  message: sent,
@@ -937,24 +956,24 @@ async function handleInboundMessage(input) {
937
956
  if (input.request !== void 0 && (voice !== void 0 || processedMedia !== void 0)) input.request.text = text;
938
957
  const retainedMedia = processedMedia ?? ([...files.values()].flatMap((file) => file.status === "stored" && file.description !== void 0 ? [file.description] : []).join("\n") || void 0);
939
958
  if (decoded.length === 0) decoded.push(...[...files.values()].flatMap((file) => file.status === "stored" && file.decoded !== void 0 ? [file.decoded] : []));
940
- await recordInboundContent({
959
+ await recordContent({
941
960
  text,
942
961
  decoded,
943
- ...retainedMedia === void 0 ? {} : { media: retainedMedia }
962
+ ...retainedMedia === void 0 ? {} : { media: retainedMedia },
963
+ files: [...files.values()].flatMap((file) => file.status === "stored" ? [{
964
+ id: file.attachment.id,
965
+ filename: file.attachment.filename,
966
+ contentType: file.attachment.contentType,
967
+ size: file.attachment.size,
968
+ expiresAt: file.attachment.expiresAt,
969
+ ...file.imageInput === "native" ? { image: true } : {}
970
+ }] : [])
944
971
  });
945
- if (route === "observe") {
946
- const images = [...files.values()].flatMap((file) => file.status === "stored" && file.imageInput === "native" ? [file.attachment] : []);
947
- dependencies.observed.add(chat, {
948
- ...observedMessage(sent, text === "" ? "[attachment]" : text, member, media, bot),
949
- images
950
- });
951
- return null;
952
- }
972
+ if (route === "observe") return null;
953
973
  if (!structuredDispatched && input.request?.claimTurn !== void 0 && !await input.request.claimTurn()) {
954
974
  recordGateDecision("message_already_handled");
955
975
  return null;
956
976
  }
957
- let receivingHistory;
958
977
  if (operations !== void 0 && dependencies.history !== void 0) {
959
978
  let recoverySessionId;
960
979
  try {
@@ -962,7 +981,6 @@ async function handleInboundMessage(input) {
962
981
  if (session !== void 0) {
963
982
  recoverySessionId = session.id;
964
983
  const previous = session.id === owner?.id ? questionHistory : await dependencies.history(session.id);
965
- receivingHistory = previous;
966
984
  const recovery = heldApprovals(previous).length === 0 ? void 0 : await releaseHeldApproval({
967
985
  events: await dependencies.history(session.id),
968
986
  now: Date.now(),
@@ -996,24 +1014,6 @@ async function handleInboundMessage(input) {
996
1014
  notes.push(`<pending_question>\nTask: ${pending.task}\nQuestion: ${pending.question}\nUse this member's answer to continue the pending task with their current permissions.
997
1015
  </pending_question>`);
998
1016
  }
999
- if (operations !== void 0) {
1000
- const newest = dependencies.observed.list(chat).at(-1);
1001
- if (await dependencies.sessionPolicy.apply({
1002
- address,
1003
- operations,
1004
- ...newest === void 0 ? {} : { activeAt: Date.parse(newest.at) || 0 }
1005
- }) === "cleared") {
1006
- dependencies.observed.clear(chat);
1007
- question = void 0;
1008
- if (answering && input.request !== void 0) {
1009
- answering = false;
1010
- input.request.text = text;
1011
- const questionNote = notes.findIndex((note) => note.startsWith("<pending_question>"));
1012
- if (questionNote !== -1) notes.splice(questionNote, 1);
1013
- }
1014
- }
1015
- dependencies.sessionPolicy.delivered(address);
1016
- }
1017
1017
  await context.telegram.startTyping();
1018
1018
  await report;
1019
1019
  if (input.request !== void 0 && files.size > 0) input.request.files = files;
@@ -1021,7 +1021,7 @@ async function handleInboundMessage(input) {
1021
1021
  const currentImages = [...files.values()].flatMap((file) => file.status === "stored" && file.imageInput === "native" ? [file.attachment] : []);
1022
1022
  let remaining = Math.max(0, MAXIMUM_NATIVE_IMAGE_BYTES - currentImages.reduce((sum, file) => sum + file.size, 0));
1023
1023
  let count = currentImages.length;
1024
- input.request.extraFiles = dependencies.observed.list(chat).flatMap((message) => (message.images ?? []).flatMap((image) => {
1024
+ input.request.extraFiles = carried.flatMap((image) => {
1025
1025
  if (Date.parse(image.expiresAt) <= Date.now() || image.size > remaining || count >= 5) {
1026
1026
  notes.push(`[Earlier image ${JSON.stringify(image.filename)} omitted: expired or native context limit.]`);
1027
1027
  return [];
@@ -1038,16 +1038,15 @@ async function handleInboundMessage(input) {
1038
1038
  mediaType: image.contentType,
1039
1039
  filename: image.filename
1040
1040
  }];
1041
- }));
1041
+ });
1042
1042
  }
1043
- const language = conversationLanguage([text, ...dependencies.observed.recent(chat).map((message) => message.text).reverse()]);
1044
- const sharedContext = group ? conversationContext(await conversation(), { since: receivingHistory === void 0 ? void 0 : evidenceSince(receivingHistory) }) : void 0;
1043
+ const language = await languageOf(text);
1044
+ const confirmationReply = (group ? await conversation() : void 0)?.recent.some((entry) => "role" in entry && entry.confirmation !== void 0 && entry.messageId === audience.replyTo?.messageId) === true;
1045
1045
  const beginTurnDispatch = structuredDispatched ? input.request?.beginStructuredContinuation : input.request?.beginDispatch;
1046
1046
  if (beginTurnDispatch !== void 0 && !await beginTurnDispatch()) {
1047
1047
  recordGateDecision("message_already_handled");
1048
1048
  return null;
1049
1049
  }
1050
- dependencies.observed.remember(chat, observedMessage(sent, text, member, retainedMedia, bot));
1051
1050
  let principal = telegramPrincipal({
1052
1051
  message: {
1053
1052
  ...sent,
@@ -1056,6 +1055,14 @@ async function handleInboundMessage(input) {
1056
1055
  member,
1057
1056
  conversationLanguage: language
1058
1057
  });
1058
+ const [guidance] = group ? chatContext(dependencies.chats.chat(sent.chat.id)) : [];
1059
+ if (guidance !== void 0) principal = {
1060
+ ...principal,
1061
+ attributes: {
1062
+ ...principal.attributes,
1063
+ chatGuidance: guidance
1064
+ }
1065
+ };
1059
1066
  if (conversationPolicy && input.request?.lifecycleKey === void 0 && input.request?.structuredContext === void 0) principal = {
1060
1067
  ...principal,
1061
1068
  attributes: {
@@ -1109,27 +1116,24 @@ async function handleInboundMessage(input) {
1109
1116
  },
1110
1117
  context: [
1111
1118
  ...input.request?.lifecycleContext === void 0 ? [] : [input.request.lifecycleContext],
1112
- ...observedContext(dependencies.observed.take(chat).slice(-groups.smart.context.messages), sharedContext),
1113
- ...group ? chatContext(dependencies.chats.chat(sent.chat.id)) : [],
1114
1119
  speakerBlock({
1115
1120
  message: sent,
1116
1121
  member,
1117
1122
  bot,
1118
- omitQuote: group && question?.recent.some((entry) => "role" in entry && entry.confirmation !== void 0 && entry.messageId === audience.replyTo?.messageId)
1123
+ omitQuote: confirmationReply
1119
1124
  }),
1120
1125
  "<message_audience>\n" + JSON.stringify({
1121
1126
  bot_addressed: addressed !== void 0 || answering,
1122
1127
  bot_tagged: audience.botTagged,
1123
- reply_target: sharedContext === void 0 ? audience.replyTo : JSON.parse(sharedContext.reply_target),
1124
- tagged_users: audience.mentions
1125
- }) + "\nUse these user-ID and rich-mention signals for addressing.\n</message_audience>",
1126
- "Reply in the member's conversation language. After an action, use at most one short human sentence saying what changed and where, with a link to the record in the app. Confirmations already show their status. Never quote tool output, tool names, record ids, JSON, stack traces or internal errors; explain failures briefly with an error reference when available.",
1128
+ reply_target: confirmationReply && audience.replyTo !== null ? Object.fromEntries(Object.entries(audience.replyTo).filter(([key]) => key !== "text")) : audience.replyTo,
1129
+ tagged_users: audience.mentions,
1130
+ ...conversationPolicy ? { engagement_policy: "conversation" } : {}
1131
+ }) + "\n</message_audience>",
1127
1132
  ...attachmentsContext({
1128
1133
  files,
1129
1134
  group
1130
1135
  }),
1131
1136
  ...processedMedia === void 0 ? [] : [processedMedia],
1132
- ...conversationPolicy ? ["If this member declines your involvement or closes the conversation, output exactly [SILENT]. Do not acknowledge the rejection or perform actions."] : [],
1133
1137
  ...notes
1134
1138
  ]
1135
1139
  };
@@ -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-D_Gqlb5f.mjs";
13
+ import { t as agentBuild } from "../../../_chunks/build-metadata-R_ssK2uR.mjs";
14
14
  var AttachmentLifecycleNotPermitted = class extends Error {
15
15
  code = "attachment_lifecycle_not_permitted";
16
16
  };
@@ -1,14 +1,10 @@
1
- import { type ObservedMessages } from "../group-routing/observed.js";
2
1
  import type { TelegramMessage } from "eve/channels/telegram";
3
2
  import type { BotIdentity } from "./router.js";
4
- import type { ObservedMessage } from "../group-routing/observed.js";
5
- export * from "../group-routing/observed.js";
3
+ import type { ObservedMessage } from "../group-routing/messages.js";
6
4
  /** Eve represents non-image media as documents; retain the actual Telegram kind in context. */
7
5
  export declare function attachmentKind(message: TelegramMessage, file: {
8
6
  readonly fileId: string;
9
7
  readonly kind: string;
10
8
  }): string;
11
- /** Bounded provider structure, shared by the warm ring and archive reconstruction. No file grants. */
9
+ /** Bounded provider structure, archived with each message for routing. No file grants. */
12
10
  export declare function messageMetadata(message: TelegramMessage, bot?: BotIdentity): Pick<ObservedMessage, "invitation" | "kind" | "username" | "replyTo" | "taggedUsers" | "attachmentKinds" | "hasCaption" | "revision">;
13
- /** Compatibility boundary for callers that used Telegram's former unqualified ring keys. */
14
- export declare function observedMessages(capacity?: number): ObservedMessages;
@@ -4,7 +4,6 @@ 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 { OBSERVED_MESSAGES, agentMessage, currentConfirmationPrompt, observedChatKey, observedContext, observedMessages as observedMessages$1, withConfirmationPrompt } from "../group-routing/observed.mjs";
8
7
  import { messageAudience } from "./audience.mjs";
9
8
  const PROVIDER_MEDIA_KINDS = [
10
9
  "voice",
@@ -49,16 +48,4 @@ function messageMetadata(message, bot) {
49
48
  }
50
49
  };
51
50
  }
52
- function observedMessages(capacity) {
53
- const ring = observedMessages$1(capacity);
54
- const key = (value) => /^[a-z][a-z0-9_-]*:/u.test(value) ? value : `telegram:${value.replace(/:/u, "/")}`;
55
- return {
56
- add: (chat, message) => ring.add(key(chat), message),
57
- remember: (chat, message) => ring.remember(key(chat), message),
58
- list: (chat) => ring.list(key(chat)),
59
- recent: (chat) => ring.recent(key(chat)),
60
- take: (chat) => ring.take(key(chat)),
61
- clear: (chat) => ring.clear(key(chat))
62
- };
63
- }
64
- export { OBSERVED_MESSAGES, agentMessage, attachmentKind, currentConfirmationPrompt, messageMetadata, observedChatKey, observedContext, observedMessages, withConfirmationPrompt };
51
+ export { attachmentKind, messageMetadata };
@@ -1,10 +1,11 @@
1
- import { type RecentMessage, type PendingConfirmation } from "./observed.js";
1
+ import { type RecentMessage, type PendingConfirmation } from "../group-routing/messages.js";
2
2
  import type { HistoryReader, RecordedEvent, SessionOperations } from "./sessions.js";
3
3
  import { type PendingQuestion } from "../questions.js";
4
4
  export { archivedRoutingMessages } from "../group-routing/routing-context.js";
5
5
  /**
6
- * Recover the agent's questions from the existing session without starting a turn. The local
7
- * ring supplements this with recipient identities and chatter not written to Eve's stream.
6
+ * Recover the agent's questions from the existing session without starting a turn. The archived
7
+ * messages (`remembered`) supplement this with recipient identities and chatter not written to
8
+ * Eve's stream.
8
9
  */
9
10
  export declare function routingContext(input: {
10
11
  readonly address: string;
@@ -5,11 +5,11 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import "../group-routing/router.mjs";
8
- import { agentMessage } from "../group-routing/observed.mjs";
8
+ import { agentMessage } from "../group-routing/messages.mjs";
9
9
  import { archivedRoutingMessages } from "../group-routing/routing-context.mjs";
10
- import { isSilentReply } from "./silent.mjs";
11
10
  import { questionKey } from "../questions.mjs";
12
11
  import { questionsFromHistory, routingConfirmationState, routingConfirmations } from "./confirmations.mjs";
12
+ import { isSilentReply } from "./silent.mjs";
13
13
  const DELIVERY_CLOCK_TOLERANCE_MS = 6e4;
14
14
  async function routingContext(input) {
15
15
  const histories = new Map();
@@ -3,11 +3,9 @@ import type { ChannelSource } from "eve/channels";
3
3
  import type { TelegramChannelState } from "eve/channels/telegram";
4
4
  import type { SessionSettings } from "../../config.js";
5
5
  /**
6
- * Session policy for the Telegram channel (`agent-design.md`, section 4, and docs/agent.md,
7
- * "Sessions"): what a group session spans, the daily compaction and the idle reset. The policy
8
- * runs in the webhook route, before eve delivers a message, with the operations the route offers
9
- * on a continuation address (`resolveSession`, `compact`, `clear`); the history it needs is read
10
- * from the session's own durable stream, so nothing is stored beside eve.
6
+ * Telegram session addressing (`agent-design.md`, section 4, and docs/agent.md, "Sessions"): what
7
+ * a group session spans. Compaction and idle expiry are eve's own: automatic compaction near the
8
+ * context window and `limits.sessionTimeoutMs` in the workspace's `agent.ts`.
11
9
  */
12
10
  /** eve's Telegram continuation address: `chat:thread:conversation`, the last two possibly empty. */
13
11
  export interface TelegramAddress {
@@ -28,18 +26,7 @@ export declare function messageAddress(message: TelegramMessage): string;
28
26
  * eve's own address. A private chat has no anchor either way.
29
27
  */
30
28
  export declare function scopedAddress(address: string, scope: SessionSettings["scope"]): string;
31
- /** The most recent `HH:MM` in `timezone` at or before `now`. */
32
- export declare function compactBoundary(now: Date, compactAt: string, timezone: string): Date;
33
- /** What the policy knows about one session: from its stream, then kept per process. */
34
- export interface SessionHistory {
35
- /** When the session last did anything. */
36
- readonly lastAt: number;
37
- /** When its history last started afresh: creation, compaction or clear. */
38
- readonly resetAt: number;
39
- /** Turns completed since `resetAt`; a compaction is worth nothing before the first. */
40
- readonly turnsSinceReset: number;
41
- }
42
- /** One recorded event used by session policy, routing context and interaction projections. */
29
+ /** One recorded event used by routing context and interaction projections. */
43
30
  export interface RecordedEvent {
44
31
  readonly type: string;
45
32
  readonly scope?: {
@@ -53,18 +40,8 @@ export interface RecordedEvent {
53
40
  }
54
41
  /** Session events, oldest first; readers choose a tail or full stream for their consumers. */
55
42
  export type HistoryReader = (sessionId: string) => Promise<ReadonlyArray<RecordedEvent>>;
56
- /** How many events of the tail the reader fetches: enough for a day of a busy group. */
43
+ /** How many events of the tail the adapter's tail reader fetches. */
57
44
  export declare const HISTORY_TAIL = 400;
58
- /** Folds a tail into a history; with no reset marker in it, `resetAt` is its earliest event. */
59
- export declare function historyOf(events: ReadonlyArray<RecordedEvent>): SessionHistory;
60
- /**
61
- * The earliest message time a group turn's conversation evidence still needs: each member
62
- * message the session took in carried the chat's evidence up to then, and its history keeps it
63
- * until a compaction or clear. Undefined when the session has none since its last reset, so the
64
- * turn carries the whole window. Schedule sends and button answers carry no evidence and are
65
- * skipped; a workspace callback turn is indistinguishable and may hide earlier unseen chatter.
66
- */
67
- export declare function evidenceSince(events: ReadonlyArray<RecordedEvent>): number | undefined;
68
45
  /** The route's operations on a continuation address, as eve gives them to the handler. */
69
46
  export interface SessionOperations {
70
47
  readonly resolveSession: (address: string) => Promise<{
@@ -75,29 +52,5 @@ export interface SessionOperations {
75
52
  } | undefined>;
76
53
  readonly from: (address: string) => {
77
54
  readonly send?: ChannelSource<TelegramChannelState>["send"];
78
- compact: ChannelSource<TelegramChannelState>["compact"];
79
- clear: ChannelSource<TelegramChannelState>["clear"];
80
55
  };
81
56
  }
82
- export type SessionAction = "none" | "cleared" | "compacted";
83
- export interface SessionPolicy {
84
- /**
85
- * Applies the policy to the session `address` continues, before a message is delivered to it:
86
- * clears a history idle for longer than `idleResetHours`, else compacts it once after each
87
- * day's `compactAt`. A session that does not exist yet needs neither.
88
- */
89
- apply(input: {
90
- readonly address: string;
91
- readonly operations: SessionOperations;
92
- /** The newest activity the stream does not show, such as an observed group message. */
93
- readonly activeAt?: number;
94
- readonly now?: Date;
95
- }): Promise<SessionAction>;
96
- /** Records that a message was just delivered to the session. */
97
- delivered(address: string, now?: Date): void;
98
- }
99
- export declare function sessionPolicy(input: {
100
- readonly settings: () => SessionSettings;
101
- readonly read: HistoryReader;
102
- readonly log?: (message: string, error: unknown) => void;
103
- }): SessionPolicy;