@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
@@ -5,10 +5,10 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { appOrigin } from "./environment.mjs";
8
- import { identityProviderOf, memberIdOf } from "./telegram/principal.mjs";
9
- import { runtimeToken } from "./runtime-token.mjs";
8
+ import { c as compatibleRuntimeEnvelope, i as memberIdOf, r as identityProviderOf, s as RUNTIME_SOURCE_PROTOCOL, u as runtimeEnvelope } from "../../_chunks/principal-MEMlG3EG.mjs";
10
9
  import { redactDiagnostic } from "./diagnostics.mjs";
11
10
  import { describeConfirmation, object } from "./confirmation-description.mjs";
11
+ import { runtimeToken } from "./runtime-token.mjs";
12
12
  import { describeChatPost, isChatPost, supportsChatPost } from "./chat-post-confirmation.mjs";
13
13
  import { randomUUID } from "node:crypto";
14
14
  var OntologyToolRejected = class extends Error {
@@ -18,10 +18,23 @@ var OntologyActionNotPermitted = class extends Error {
18
18
  code = "ontology_action_not_permitted";
19
19
  };
20
20
  function ontologyTools(options = {}) {
21
+ const runtimeSources = new Map();
21
22
  async function rpc(memberId, method, params, signal) {
23
+ if (method === "tools/call") {
24
+ const envelope = options.envelope ?? runtimeEnvelope({ principal: memberIdOf(options.principal) === memberId ? options.principal : null });
25
+ if (envelope.source !== void 0 && !runtimeSources.has(memberId)) await rpc(memberId, "tools/list", {}, signal);
26
+ params = {
27
+ ...params,
28
+ arguments: {
29
+ ...object(params.arguments),
30
+ _segmentstream: compatibleRuntimeEnvelope(envelope, runtimeSources.get(memberId) === true)
31
+ }
32
+ };
33
+ }
22
34
  const provider = memberIdOf(options.principal) === memberId ? identityProviderOf(options.principal) : void 0;
23
35
  const credential = await (options.token ?? runtimeToken)();
24
36
  const secrets = new Set([credential]);
37
+ if (method === "tools/call") await options.beforeCall?.(String(params.name));
25
38
  try {
26
39
  const response = await (options.fetch ?? fetch)(`${appOrigin(options.environment ?? process.env)}/mcp`, {
27
40
  method: "POST",
@@ -46,6 +59,7 @@ function ontologyTools(options = {}) {
46
59
  const body = object(await response.json());
47
60
  if (body.error !== void 0) throw new Error("Ontology metadata unavailable.", { cause: body.error });
48
61
  const result = object(body.result);
62
+ if (method === "tools/list" && Array.isArray(result.tools)) runtimeSources.set(memberId, result.tools.map(object).some((tool) => object(tool._meta)[RUNTIME_SOURCE_PROTOCOL] === 1));
49
63
  if (result.isError === true) {
50
64
  const message = (Array.isArray(result.content) ? result.content : []).map(object).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join("\n");
51
65
  if ([
@@ -119,12 +133,23 @@ function ontologyTools(options = {}) {
119
133
  const result = await rpc(memberId, "tools/list", {});
120
134
  return Array.isArray(result.tools) ? result.tools.map(object) : [];
121
135
  },
136
+ async supportsRuntimeSource(memberId, signal) {
137
+ if (!runtimeSources.has(memberId)) await rpc(memberId, "tools/list", {}, signal);
138
+ return runtimeSources.get(memberId) === true;
139
+ },
140
+ async capabilities(memberId) {
141
+ const result = await rpc(memberId, "tools/list", {});
142
+ return {
143
+ chatPosting: Array.isArray(result.tools) && result.tools.map(object).some(supportsChatPost),
144
+ runtimeSource: runtimeSources.get(memberId) === true
145
+ };
146
+ },
122
147
  async supportsChatPosting(memberId) {
123
148
  const result = await rpc(memberId, "tools/list", {});
124
149
  return Array.isArray(result.tools) && result.tools.map(object).some(supportsChatPost);
125
150
  },
126
- async labels(memberId, action, input = {}) {
127
- const { tool } = await resolve(memberId, action, input);
151
+ async labels(memberId, action, input = {}, signal) {
152
+ const { tool } = await resolve(memberId, action, input, signal);
128
153
  return {
129
154
  ...object(tool.annotations),
130
155
  confirmation: object(object(tool.inputSchema).properties)._confirmation !== void 0
@@ -0,0 +1,28 @@
1
+ import type { RuntimeSource } from "@segmentstream-cloud/ontology";
2
+ import type { SessionAuthContext } from "eve/context";
3
+ import type { TelegramMessage } from "eve/channels/telegram";
4
+ import type { StagedFile } from "./telegram/inbound-request.js";
5
+ export type RuntimeEnvelope = {
6
+ readonly version: 1;
7
+ readonly session_id: string;
8
+ readonly call_id: string;
9
+ readonly operation_id?: string;
10
+ readonly source?: RuntimeSource;
11
+ readonly expected?: {
12
+ readonly chat_id: string;
13
+ readonly digest: string;
14
+ };
15
+ };
16
+ /** The source comes from the verified Telegram message and admitted storage results. */
17
+ export declare function telegramRuntimeSource(message: Pick<TelegramMessage, "chat" | "messageId" | "messageThreadId" | "replyToMessage" | "from" | "raw" | "attachments">, files?: ReadonlyMap<string, StagedFile>, callbackQueryId?: string): RuntimeSource;
18
+ /** Session auth is SDK-owned; no tool argument is read while rebuilding provenance. */
19
+ export declare function principalRuntimeSource(principal?: SessionAuthContext | null): RuntimeSource | undefined;
20
+ export declare function runtimeEnvelope(input?: {
21
+ readonly principal?: SessionAuthContext | null;
22
+ readonly sessionId?: string;
23
+ readonly callId?: string;
24
+ readonly source?: RuntimeSource;
25
+ }): RuntimeEnvelope;
26
+ /** Older strict ontology schemas accept only the original chat identity fields. */
27
+ export declare function compatibleRuntimeEnvelope(envelope: RuntimeEnvelope, extended: boolean): RuntimeEnvelope;
28
+ export declare const RUNTIME_SOURCE_PROTOCOL = "segmentstream/runtime-source";
@@ -0,0 +1,8 @@
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 { c as compatibleRuntimeEnvelope, d as telegramRuntimeSource, l as principalRuntimeSource, s as RUNTIME_SOURCE_PROTOCOL, u as runtimeEnvelope } from "../../_chunks/principal-MEMlG3EG.mjs";
8
+ export { RUNTIME_SOURCE_PROTOCOL, compatibleRuntimeEnvelope, principalRuntimeSource, runtimeEnvelope, telegramRuntimeSource };
@@ -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 { memberIdOf } from "./telegram/principal.mjs";
7
+ import { i as memberIdOf } from "../../_chunks/principal-MEMlG3EG.mjs";
8
8
  import { redactDiagnostic } from "./diagnostics.mjs";
9
9
  import { object } from "./confirmation-description.mjs";
10
10
  import { ontologyTools } from "./ontology-tools.mjs";
@@ -7,19 +7,21 @@ __eveCreateRequire(import.meta.url);
7
7
  import { telegramText, withTelegramTexts } from "./texts.mjs";
8
8
  import { manifestOf } from "../../config.mjs";
9
9
  import { appOrigin, channelsEnabled, proxyEnvironment, runtimeIdentityEnvironment, webhookSecret } from "../environment.mjs";
10
- import { conversationLanguage, telegramPrincipal } from "./principal.mjs";
10
+ import { n as conversationLanguage, o as telegramPrincipal } from "../../../_chunks/principal-MEMlG3EG.mjs";
11
+ import { confirmationLocale } from "./confirmation-text.mjs";
12
+ import { telegramSendScope } from "./send-queue.mjs";
13
+ import { d as failureText, h as withDeliveryLocale, i as withReplacedText, m as safeTelegramFetch, n as currentInboundRequest, o as currentLocale, p as reportTelegramError, r as runInboundRequest, t as InboundRequest } from "../../../_chunks/inbound-request-RIRNBxUC.mjs";
11
14
  import { chooseModel } from "../../model.mjs";
12
15
  import { groupRoutingHealth } from "../group-routing/health.mjs";
13
16
  import { decisionRouter } from "../group-routing/router.mjs";
14
17
  import { runtimeToken } from "../runtime-token.mjs";
15
18
  import { conversations } from "../conversations.mjs";
16
19
  import { archivedRoutingMessages } from "../group-routing/routing-context.mjs";
17
- import { t as agentBuild } from "../../../_chunks/build-metadata-BLeBanNg.mjs";
18
- import { confirmationLocale } from "./confirmation-text.mjs";
19
- import { telegramSendScope } from "./send-queue.mjs";
20
- import { d as failureText, h as withDeliveryLocale, i as withReplacedText, m as safeTelegramFetch, n as currentInboundRequest, o as currentLocale, p as reportTelegramError, r as runInboundRequest, t as InboundRequest } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
21
- import { attachmentLifecycle } from "./lifecycle.mjs";
20
+ import { dispatchStructuredMessage, structuredMessage } from "./structured.mjs";
22
21
  import { scopedAddress, sessionPolicy } from "./sessions.mjs";
22
+ import { handleWorkspaceCallback, isWorkspaceCallback } from "./callbacks.mjs";
23
+ import { t as agentBuild } from "../../../_chunks/build-metadata-CJlaXNhc.mjs";
24
+ import { attachmentLifecycle } from "./lifecycle.mjs";
23
25
  import { observedMessages } from "./observed.mjs";
24
26
  import { expireDeferredCards, handleDeferredCallback, isDeferredCallback } from "./deferred-confirmations.mjs";
25
27
  import { gatewayImageDescriber } from "./images.mjs";
@@ -121,6 +123,10 @@ function segmentstreamTelegram(options) {
121
123
  identities: pairing,
122
124
  observed,
123
125
  chats,
126
+ structured: (raw) => {
127
+ const inbound = settings().inbound;
128
+ return inbound === void 0 ? void 0 : structuredMessage(raw, inbound.kinds);
129
+ },
124
130
  verify: telegramWebhookVerifier(() => webhookSecret(environment)),
125
131
  answerCallback: async (callbackQueryId, locale) => {
126
132
  const text = telegramText("callback", "unsupported", locale);
@@ -203,6 +209,20 @@ function segmentstreamTelegram(options) {
203
209
  includeDecisions: true
204
210
  })).events, 100),
205
211
  signalSettings: settings().groups,
212
+ ...settings().inbound === void 0 ? {} : { structured: {
213
+ configuration: settings().inbound,
214
+ run: (principal, memberId, message, event, beforeDispatch) => dispatchStructuredMessage({
215
+ configuration: settings().inbound,
216
+ principal,
217
+ memberId,
218
+ message,
219
+ event,
220
+ beforeDispatch,
221
+ environment,
222
+ token,
223
+ ...options.fetch ? { fetch: options.fetch } : {}
224
+ })
225
+ } },
206
226
  recoverHeldApprovals: options.confirmationMode?.() === "deferred",
207
227
  ...settings().attachmentLifecycle === void 0 ? {} : { lifecycle: (principal, memberId, message) => attachmentLifecycle({
208
228
  configuration: settings().attachmentLifecycle,
@@ -296,7 +316,7 @@ function segmentstreamTelegram(options) {
296
316
  reportTelegramError("intake_dispatch_release_failed", cleanupError);
297
317
  }
298
318
  const errorId = reportTelegramError("inbound_message_failed", error, { chatId: message.chat.id });
299
- if (inbound?.retryReference !== void 0 || inbound?.lifecycleKey !== void 0) inbound.retryReference = errorId;
319
+ if (inbound?.retryReference !== void 0 || inbound?.lifecycleKey !== void 0 || inbound?.structuredContext !== void 0) inbound.retryReference = errorId;
300
320
  else await context.telegram.post(failureText(errorId));
301
321
  return null;
302
322
  } finally {
@@ -307,6 +327,20 @@ function segmentstreamTelegram(options) {
307
327
  const inbound = currentInboundRequest();
308
328
  if (inbound !== void 0) inbound.locale = query.from.languageCode;
309
329
  try {
330
+ if (isWorkspaceCallback(query.data)) {
331
+ await handleWorkspaceCallback({
332
+ context,
333
+ query,
334
+ configuration: settings().callbacks,
335
+ admission: options.admission?.() ?? manifestOf({}),
336
+ scope: options.sessions().scope,
337
+ identities: pairing(),
338
+ environment,
339
+ token,
340
+ ...options.fetch ? { fetch: options.fetch } : {}
341
+ });
342
+ return;
343
+ }
310
344
  if (isDeferredCallback(query.data)) {
311
345
  await handleDeferredCallback({
312
346
  context,
@@ -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 { describableImage } from "./images.mjs";
9
9
  import { MAXIMUM_ROUTING_PDF_CHARACTERS, extractRoutingPdf } from "./pdf-text.mjs";
10
10
  import { gateway, generateText } from "ai";
@@ -5,12 +5,12 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { appOrigin, channelsEnabled, proxyEnvironment } from "../environment.mjs";
8
- import "./principal.mjs";
9
- import { runtimeToken } from "../runtime-token.mjs";
8
+ import "../../../_chunks/principal-MEMlG3EG.mjs";
10
9
  import { redactDiagnostic } from "../diagnostics.mjs";
11
10
  import { confirmationLocale } from "./confirmation-text.mjs";
12
11
  import { inTelegramSendQueue, telegramSendScope } from "./send-queue.mjs";
13
- import { d as failureText, f as formattingRefused, l as customerMessage, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
12
+ import { d as failureText, f as formattingRefused, l as customerMessage, p as reportTelegramError } from "../../../_chunks/inbound-request-RIRNBxUC.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";
@@ -0,0 +1,18 @@
1
+ import type { TelegramCallbackQuery, TelegramContext } from "eve/channels/telegram";
2
+ import type { AdmissionSettings, SessionSettings, TelegramSettings } from "../../config.js";
3
+ import type { AgentIdentities } from "../identities.js";
4
+ import type { Environment } from "../environment.js";
5
+ import type { RuntimeToken } from "../runtime-token.js";
6
+ export declare const isWorkspaceCallback: (data?: string) => boolean;
7
+ /** A single dispatch and acknowledgement. Token policy and business idempotency belong to the workspace. */
8
+ export declare function handleWorkspaceCallback(input: {
9
+ readonly context: TelegramContext;
10
+ readonly query: TelegramCallbackQuery;
11
+ readonly configuration?: TelegramSettings["callbacks"];
12
+ readonly scope: SessionSettings["scope"];
13
+ readonly admission: AdmissionSettings;
14
+ readonly identities: AgentIdentities;
15
+ readonly environment?: Environment;
16
+ readonly token?: RuntimeToken;
17
+ readonly fetch?: typeof fetch;
18
+ }): Promise<void>;
@@ -0,0 +1,155 @@
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 { admitMessage } from "../admission.mjs";
8
+ import { telegramText } from "./texts.mjs";
9
+ import { d as telegramRuntimeSource, i as memberIdOf, o as telegramPrincipal, u as runtimeEnvelope } from "../../../_chunks/principal-MEMlG3EG.mjs";
10
+ import { object } from "../confirmation-description.mjs";
11
+ import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-RIRNBxUC.mjs";
12
+ import { workspaceAction } from "./workspace-action.mjs";
13
+ import { formatAddress, parseAddress, scopedAddress } from "./sessions.mjs";
14
+ const isWorkspaceCallback = (data) => data?.startsWith("ws:") === true;
15
+ async function handleWorkspaceCallback(input) {
16
+ const { query, context } = input;
17
+ const locale = query.from.languageCode;
18
+ let acknowledgement;
19
+ let showAlert = false;
20
+ let result;
21
+ let principal;
22
+ const controller = new AbortController();
23
+ let timer;
24
+ try {
25
+ const token = query.data?.slice(3) ?? "";
26
+ if (!query.message || query.from.isBot || !token || new TextEncoder().encode(token).byteLength > 60) acknowledgement = telegramText("callback", "unknown", locale);
27
+ else if (input.configuration === void 0) acknowledgement = telegramText("callback", "unsupported", locale);
28
+ else {
29
+ const message = {
30
+ ...query.message,
31
+ from: query.from,
32
+ attachments: [],
33
+ text: "",
34
+ caption: "",
35
+ raw: {}
36
+ };
37
+ const dispatch = async () => {
38
+ const lookup = await input.identities.lookup({
39
+ provider: "telegram",
40
+ externalId: query.from.id
41
+ });
42
+ if (lookup.status !== "paired") {
43
+ acknowledgement = telegramText("callback", "forbidden", locale);
44
+ return;
45
+ }
46
+ controller.signal.throwIfAborted();
47
+ principal = telegramPrincipal({
48
+ message,
49
+ member: lookup.member,
50
+ callbackQueryId: query.id
51
+ });
52
+ return workspaceAction({
53
+ ...input,
54
+ configuration: input.configuration,
55
+ principal,
56
+ memberId: lookup.member.id,
57
+ input: { token },
58
+ signal: controller.signal,
59
+ envelope: runtimeEnvelope({
60
+ source: telegramRuntimeSource(message, void 0, query.id),
61
+ sessionId: `telegram:callback:${query.id}`,
62
+ callId: query.id
63
+ })
64
+ });
65
+ };
66
+ const deadline = new Promise((resolve) => {
67
+ timer = setTimeout(() => {
68
+ controller.abort();
69
+ reportTelegramError("workspace_callback_timeout", new Error("Telegram callback dispatch exceeded 10 seconds."), { callbackQueryId: query.id });
70
+ resolve(void 0);
71
+ }, 1e4);
72
+ });
73
+ result = await Promise.race([dispatch(), deadline]);
74
+ if (result !== void 0) {
75
+ acknowledgement = typeof result.acknowledgement === "string" && result.acknowledgement.length <= 200 ? result.acknowledgement : void 0;
76
+ showAlert = result.show_alert === true;
77
+ }
78
+ }
79
+ } catch (error) {
80
+ reportTelegramError("workspace_callback_failed", error, { callbackQueryId: query.id });
81
+ acknowledgement = controller.signal.aborted ? void 0 : telegramText("callback", "error", locale);
82
+ } finally {
83
+ if (timer !== void 0) clearTimeout(timer);
84
+ try {
85
+ await context.telegram.answerCallbackQuery({
86
+ callbackQueryId: query.id,
87
+ ...acknowledgement === void 0 ? {} : { text: acknowledgement },
88
+ showAlert
89
+ });
90
+ } catch (error) {
91
+ reportTelegramError("workspace_callback_ack_failed", error, { callbackQueryId: query.id });
92
+ }
93
+ }
94
+ if (result === void 0 || query.message === void 0 || principal === void 0) return;
95
+ const edit = object(result.edit);
96
+ try {
97
+ if (typeof edit.text === "string" || edit.reply_markup !== void 0) {
98
+ const payload = {
99
+ chat_id: query.message.chat.id,
100
+ message_id: Number(query.message.messageId),
101
+ ...typeof edit.text === "string" && edit.text.length <= 4096 ? { text: edit.text } : {},
102
+ ...[
103
+ "MarkdownV2",
104
+ "Markdown",
105
+ "HTML"
106
+ ].includes(String(edit.parse_mode)) ? { parse_mode: edit.parse_mode } : {},
107
+ ...Array.isArray(edit.entities) && edit.entities.length <= 100 ? { entities: edit.entities } : {},
108
+ ...edit.reply_markup === void 0 ? {} : { reply_markup: edit.reply_markup }
109
+ };
110
+ const encoded = JSON.stringify(payload);
111
+ if (encoded.length > 32768) throw new Error("The callback edit exceeds the Telegram hook limit.");
112
+ await context.telegram.request(typeof edit.text === "string" ? "editMessageText" : "editMessageReplyMarkup", JSON.parse(encoded));
113
+ }
114
+ } catch (error) {
115
+ reportTelegramError("workspace_callback_edit_failed", error, { callbackQueryId: query.id });
116
+ }
117
+ if (typeof result.context === "string" && result.context.trim() && result.context.length <= 16384) try {
118
+ const admitted = await admitMessage({
119
+ identity: {
120
+ provider: "telegram",
121
+ externalId: query.from.id,
122
+ chat: query.message.chat.type === "private" ? "private" : "group",
123
+ turn: true
124
+ },
125
+ identities: input.identities,
126
+ settings: input.admission,
127
+ languageCode: locale,
128
+ sendNotice: (text) => context.telegram.post(text),
129
+ noticeFailed: (error) => reportTelegramError("workspace_callback_notice_failed", error)
130
+ });
131
+ if (admitted.status !== "existing" && admitted.status !== "created" || admitted.member.id !== memberIdOf(principal)) return;
132
+ const sessions = currentInboundRequest()?.sessions;
133
+ if (sessions === void 0) throw new Error("The callback has no session transport.");
134
+ const address = scopedAddress(formatAddress({
135
+ chatId: query.message.chat.type === "private" ? query.from.id : query.message.chat.id,
136
+ messageThreadId: String(query.message.messageThreadId ?? ""),
137
+ conversationId: query.message.chat.type === "private" ? "" : query.message.messageId
138
+ }), input.scope);
139
+ const session = sessions.from(address);
140
+ if (session.send === void 0) throw new Error("The callback session cannot enqueue turns.");
141
+ await session.send(result.context, {
142
+ auth: principal,
143
+ turnPolicy: "queue",
144
+ state: {
145
+ chatId: query.message.chat.id,
146
+ chatType: query.message.chat.type,
147
+ messageThreadId: query.message.messageThreadId ?? null,
148
+ conversationId: parseAddress(address).conversationId || null
149
+ }
150
+ });
151
+ } catch (error) {
152
+ reportTelegramError("workspace_callback_turn_failed", error, { callbackQueryId: query.id });
153
+ }
154
+ }
155
+ export { handleWorkspaceCallback, isWorkspaceCallback };
@@ -6,10 +6,10 @@ __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { withTelegramTexts } from "./texts.mjs";
8
8
  import { proxyEnvironment } from "../environment.mjs";
9
+ import { telegramSendScope } from "./send-queue.mjs";
10
+ import { m as safeTelegramFetch, p as reportTelegramError } from "../../../_chunks/inbound-request-RIRNBxUC.mjs";
9
11
  import { runtimeToken } from "../runtime-token.mjs";
10
12
  import { conversations } from "../conversations.mjs";
11
- import { telegramSendScope } from "./send-queue.mjs";
12
- import { m as safeTelegramFetch, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
13
13
  import { deferredConfirmations } from "../deferred-confirmations.mjs";
14
14
  import { expireDeferredRecord } from "./deferred-confirmations.mjs";
15
15
  import { connectionProxy } from "../connection-proxy.mjs";
@@ -5,13 +5,13 @@ 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, telegramPrincipal } from "./principal.mjs";
9
- import { agentMessage, observedChatKey, withConfirmationPrompt } from "../group-routing/observed.mjs";
10
- import { conversationEvent } from "../conversations.mjs";
8
+ import { i as memberIdOf, o as telegramPrincipal } from "../../../_chunks/principal-MEMlG3EG.mjs";
11
9
  import { humanText } from "../confirmation-description.mjs";
12
10
  import { renderConfirmation, withConfirmationText } from "./confirmation-text.mjs";
13
11
  import { chatPostCard, chatPostCardStatus, chatPostReceipt, withChatPostCard } from "./chat-post-card.mjs";
14
- import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
12
+ import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-RIRNBxUC.mjs";
13
+ import { agentMessage, observedChatKey, withConfirmationPrompt } from "../group-routing/observed.mjs";
14
+ import { conversationEvent } from "../conversations.mjs";
15
15
  import { chatPostOperationId, isChatPost } from "../chat-post-confirmation.mjs";
16
16
  import { confirmations } from "../confirmations.mjs";
17
17
  import { formatAddress, scopedAddress } from "./sessions.mjs";
@@ -1,3 +1,4 @@
1
+ import type { StructuredMessage } from "./structured.js";
1
2
  import type { AgentIdentities } from "../identities.js";
2
3
  import { type Conversations } from "../conversations.js";
3
4
  import type { AgentChats } from "./chats.js";
@@ -9,6 +10,7 @@ export declare const REVISION_CLAIM_TTL_MS: number;
9
10
  /** Runs after webhook verification, before eve drops bot/channel/service updates. */
10
11
  export declare function captureTelegramWebhook(input: {
11
12
  readonly answerCallback?: (id: string, locale?: string) => Promise<unknown>;
13
+ readonly structured?: (raw: Readonly<Record<string, unknown>>) => StructuredMessage | undefined;
12
14
  readonly request: Request;
13
15
  readonly archive: Conversations;
14
16
  readonly identities: AgentIdentities;
@@ -33,6 +35,7 @@ export declare function conversationIdentities(identities: AgentIdentities): Age
33
35
  /** Verification and failure acknowledgement stay outside eve's swallowed onMessage failures. */
34
36
  export declare function telegramConversationCapture(input: {
35
37
  readonly answerCallback?: (id: string, locale?: string) => Promise<unknown>;
38
+ readonly structured?: (raw: Readonly<Record<string, unknown>>) => StructuredMessage | undefined;
36
39
  readonly archive: Conversations;
37
40
  readonly identities: () => AgentIdentities;
38
41
  readonly observed: ObservedMessages;
@@ -4,10 +4,10 @@ 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-RIRNBxUC.mjs";
7
8
  import { currentConfirmationPrompt, observedChatKey } from "../group-routing/observed.mjs";
8
9
  import { conversationEvent } from "../conversations.mjs";
9
10
  import { archivedRoutingMessages } from "../group-routing/routing-context.mjs";
10
- import { n as currentInboundRequest, p as reportTelegramError, s as currentThreadId } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
11
11
  import { contentShape, messageContent } from "./content.mjs";
12
12
  import { messageMetadata } from "./observed.mjs";
13
13
  import { reportMembership } from "./membership.mjs";
@@ -108,6 +108,7 @@ async function captureTelegramWebhook(input) {
108
108
  if (parsed?.kind !== "message") return input.next();
109
109
  const message = parsed.message;
110
110
  const content = messageContent(message.raw);
111
+ const structured = input.structured?.(message.raw);
111
112
  const time = edited === void 0 ? message.raw.date : message.raw.edit_date ?? message.raw.date;
112
113
  const sentAt = typeof time === "number" ? time * 1e3 : Date.now();
113
114
  const senderChat = record(message.raw.sender_chat);
@@ -158,8 +159,10 @@ async function captureTelegramWebhook(input) {
158
159
  const revisionId = edited === void 0 ? void 0 : `telegram:${message.chat.id}:${message.messageId}:routing:${createHash("sha256").update(JSON.stringify({
159
160
  at: event.at,
160
161
  content,
162
+ ...structured === void 0 ? {} : { structured },
161
163
  ...event.attachments.length === 0 ? {} : { attachments: attachmentIdentity(event.attachments) }
162
164
  })).digest("hex")}`;
165
+ const dispatchKey = `telegram:${message.chat.id}:${message.messageId}` + (structured === void 0 ? "" : `:structured:${revisionId ?? "initial"}`);
163
166
  const revisionEvents = [];
164
167
  let revisionClaim;
165
168
  const finishRevision = async (status) => {
@@ -197,12 +200,31 @@ async function captureTelegramWebhook(input) {
197
200
  ...lookup?.status === "paired" ? { participantId: lookup.member.id } : {}
198
201
  })).events;
199
202
  const routing = messageHistory.filter((earlier) => earlier.kind === "decision" && earlier.senderId === event.senderId);
200
- const previousClaim = routing.find((earlier) => earlier.reason === "turn_claimed");
201
- const admitted = routing.some((earlier) => earlier.reason === "turn_admitted");
202
- const released = previousClaim !== void 0 && routing.some((earlier) => earlier.eventId === `${previousClaim.eventId}:released`);
203
- const dispatched = routing.some((earlier) => earlier.reason === "turn_dispatch_started");
203
+ const dispatchHistory = structured === void 0 ? routing : routing.filter((earlier) => earlier.eventId.startsWith(`${dispatchKey}:`));
204
+ const previousClaim = dispatchHistory.find((earlier) => earlier.reason === "turn_claimed");
205
+ const admitted = dispatchHistory.some((earlier) => earlier.reason === "turn_admitted");
206
+ const released = previousClaim !== void 0 && dispatchHistory.some((earlier) => earlier.eventId === `${previousClaim.eventId}:released`);
207
+ const dispatched = dispatchHistory.some((earlier) => earlier.reason === "turn_dispatch_started");
204
208
  const reserved = previousClaim !== void 0 && !released && Date.now() - Date.parse(previousClaim.at) < 6e5;
205
- const alreadyHandled = admitted || dispatched || reserved;
209
+ const result = dispatchHistory.find((earlier) => earlier.reason === "structured_action_completed" && earlier.status === "completed");
210
+ let structuredContext;
211
+ if (structured !== void 0 && result?.argsSummary && lookup === void 0 && message.from !== void 0 && !message.from.isBot) {
212
+ lookup = await input.identities.lookup({
213
+ provider: "telegram",
214
+ externalId: message.from.id
215
+ });
216
+ capture.lookup = lookup;
217
+ }
218
+ if (structured !== void 0 && lookup?.status === "paired" && result?.argsSummary) try {
219
+ const value = record(JSON.parse(result.argsSummary));
220
+ if (typeof value.memberId === "string" && value.memberId === lookup.member.id && typeof value.context === "string" && value.context.trim() && value.context.length <= 16384) structuredContext = {
221
+ text: value.context,
222
+ memberId: value.memberId
223
+ };
224
+ } catch {}
225
+ const continuationDispatched = dispatchHistory.some((earlier) => earlier.reason === "structured_context_dispatch_started");
226
+ const continuationPending = dispatched && structuredContext !== void 0 && !admitted && !continuationDispatched;
227
+ const alreadyHandled = (admitted || dispatched || reserved) && !continuationPending;
206
228
  const rememberRevision = (events) => {
207
229
  if (revisionId !== void 0) revisionEvents.push(...events.filter((item) => item.eventId === revisionId || item.eventId.startsWith(`${revisionId}:`)));
208
230
  };
@@ -282,8 +304,9 @@ async function captureTelegramWebhook(input) {
282
304
  }
283
305
  let accepted = false;
284
306
  if (inbound !== void 0) {
285
- inbound.edited = edited !== void 0;
286
- if (revisionId !== void 0) inbound.claimRevision = async () => {
307
+ inbound.structuredContext = continuationPending ? structuredContext : void 0;
308
+ inbound.edited = edited !== void 0 && !continuationPending;
309
+ if (revisionId !== void 0 && !continuationPending) inbound.claimRevision = async () => {
287
310
  if (revisionEvents.some((item) => item.eventId === `${revisionId}:completed`)) return false;
288
311
  const claims = revisionEvents.filter((item) => item.reason === "routing_claimed");
289
312
  const latest = claims[0];
@@ -305,9 +328,9 @@ async function captureTelegramWebhook(input) {
305
328
  return inserted !== false;
306
329
  };
307
330
  const nextClaim = previousClaim === void 0 ? void 0 : createHash("sha256").update(previousClaim.eventId).digest("hex");
308
- const claimId = previousClaim === void 0 ? `telegram:${message.chat.id}:${message.messageId}:turn` : `telegram:${message.chat.id}:${message.messageId}:turn:${nextClaim}`;
309
- let claimed = false;
310
- let dispatchStarted = false;
331
+ const claimId = continuationPending && previousClaim !== void 0 ? previousClaim.eventId : previousClaim === void 0 ? `${dispatchKey}:turn` : `${dispatchKey}:turn:${nextClaim}`;
332
+ let claimed = continuationPending;
333
+ let dispatchStarted = continuationPending;
311
334
  const claimEvent = (reason, status, suffix = "") => ({
312
335
  ...capture.event,
313
336
  eventId: `${claimId}${suffix}`,
@@ -327,10 +350,32 @@ async function captureTelegramWebhook(input) {
327
350
  inbound.beginDispatch = async () => {
328
351
  dispatchStarted = await input.archive.append({
329
352
  ...claimEvent("turn_dispatch_started", "requested"),
330
- eventId: `telegram:${message.chat.id}:${message.messageId}:dispatch`
353
+ eventId: `${dispatchKey}:dispatch`
331
354
  }) === true;
332
355
  return dispatchStarted;
333
356
  };
357
+ inbound.recordStructuredContext = async (context, memberId) => {
358
+ if (await input.archive.append({
359
+ ...claimEvent("structured_action_completed", "completed"),
360
+ eventId: `${dispatchKey}:result`,
361
+ argsSummary: JSON.stringify({
362
+ context,
363
+ memberId
364
+ })
365
+ }) !== true) throw new Error("The archive did not acknowledge the structured continuation.");
366
+ inbound.structuredContext = {
367
+ text: context,
368
+ memberId
369
+ };
370
+ };
371
+ inbound.beginStructuredContinuation = async () => {
372
+ const inserted = await input.archive.append({
373
+ ...claimEvent("structured_context_dispatch_started", "requested"),
374
+ eventId: `${dispatchKey}:context-dispatch`
375
+ });
376
+ dispatchStarted ||= inserted === true;
377
+ return inserted === true;
378
+ };
334
379
  inbound.finishClaim = async (delivered) => {
335
380
  if ((accepted || dispatchStarted) && !delivered) return;
336
381
  accepted ||= delivered;
@@ -5,5 +5,5 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { isTechnicalText } from "../customer-text-safety.mjs";
8
- import { a as TelegramDeliveryError, c as customerCopy, d as failureText, f as formattingRefused, h as withDeliveryLocale, l as customerMessage, m as safeTelegramFetch, o as currentLocale, p as reportTelegramError, s as currentThreadId, u as customerText } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
8
+ import { a as TelegramDeliveryError, c as customerCopy, d as failureText, f as formattingRefused, h as withDeliveryLocale, l as customerMessage, m as safeTelegramFetch, o as currentLocale, p as reportTelegramError, s as currentThreadId, u as customerText } from "../../../_chunks/inbound-request-RIRNBxUC.mjs";
9
9
  export { TelegramDeliveryError, currentLocale, currentThreadId, customerCopy, customerMessage, customerText, failureText, formattingRefused, isTechnicalText, reportTelegramError, safeTelegramFetch, withDeliveryLocale };
@@ -5,18 +5,18 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { telegramText, withTelegramLocale } from "./texts.mjs";
8
- import { telegramPrincipal } from "./principal.mjs";
9
- import { agentMessage, observedChatKey, withConfirmationPrompt } from "../group-routing/observed.mjs";
10
- import { conversationEvent } from "../conversations.mjs";
8
+ import { o as telegramPrincipal } from "../../../_chunks/principal-MEMlG3EG.mjs";
11
9
  import { humanText } from "../confirmation-description.mjs";
12
10
  import { renderConfirmation, withConfirmationText } from "./confirmation-text.mjs";
13
11
  import { chatPostCard, chatPostCardStatus, chatPostReceipt, withChatPostCard } from "./chat-post-card.mjs";
14
- import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
12
+ import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-RIRNBxUC.mjs";
13
+ import { agentMessage, observedChatKey, withConfirmationPrompt } from "../group-routing/observed.mjs";
14
+ import { conversationEvent } from "../conversations.mjs";
15
15
  import { CONFIRMATION_TTL_MS, inputHash } from "../confirmations.mjs";
16
16
  import { OntologyToolRejected } from "../ontology-tools.mjs";
17
+ import { formatAddress } from "./sessions.mjs";
17
18
  import { confirmationOutcome } from "../confirmation-outcome.mjs";
18
19
  import { deferredConfirmations, deferredKey, runDeferredAction } from "../deferred-confirmations.mjs";
19
- import { formatAddress } from "./sessions.mjs";
20
20
  import { telegramTextLinks } from "@segmentstream-cloud/ontology/telegram";
21
21
  const PREFIX = "ss-confirm:d:";
22
22
  const isDeferredCallback = (data) => data?.startsWith(PREFIX) === true;
@@ -4,8 +4,8 @@ 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 { conversationLanguage } from "./principal.mjs";
8
- import { d as failureText, p as reportTelegramError } from "../../../_chunks/inbound-request-BXhiIph-.mjs";
7
+ import { n as conversationLanguage } from "../../../_chunks/principal-MEMlG3EG.mjs";
8
+ import { d as failureText, p as reportTelegramError } from "../../../_chunks/inbound-request-RIRNBxUC.mjs";
9
9
  import { describableImage, describeReceivedImage } from "./images.mjs";
10
10
  import { MAXIMUM_ATTACHMENT_BYTES, attachmentUrl } from "../attachments.mjs";
11
11
  function telegramConversation(chatId) {