@segmentstream/agent 0.18.2 → 0.19.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 (92) hide show
  1. package/README.md +4 -4
  2. package/dist/_chunks/{build-metadata-CaihnyGE.mjs → build-metadata-CkW-D9NI.mjs} +1 -1
  3. package/dist/_chunks/{inbound-request-8xhK7EaW.mjs → inbound-request-Dx1B6Y5r.mjs} +1 -1
  4. package/dist/_chunks/{principal-qyR3jKAr.mjs → principal-BnJuWJtk.mjs} +1 -1
  5. package/dist/extension/_manifest.json +3 -15
  6. package/dist/extension/connections/ontology.d.ts +1 -1
  7. package/dist/extension/connections/ontology.mjs +2 -2
  8. package/dist/extension/hooks/authorization.d.ts +1 -1
  9. package/dist/extension/hooks/authorization.mjs +3 -3
  10. package/dist/extension/hooks/ontology-files.d.ts +1 -1
  11. package/dist/extension/hooks/ontology-files.mjs +3 -1
  12. package/dist/extension/hooks/sandbox.d.ts +2 -2
  13. package/dist/extension/hooks/sandbox.mjs +2 -2
  14. package/dist/extension/instructions/conversations.d.ts +1 -1
  15. package/dist/extension/instructions/conversations.mjs +3 -3
  16. package/dist/extension/instructions/sandbox.d.ts +1 -1
  17. package/dist/extension/instructions/sandbox.mjs +2 -2
  18. package/dist/extension/lib/attachment-tools.mjs +1 -1
  19. package/dist/extension/lib/build-metadata.mjs +1 -1
  20. package/dist/extension/lib/confirmations.mjs +3 -3
  21. package/dist/extension/lib/conversation-runtime.d.ts +2 -2
  22. package/dist/extension/lib/conversation-runtime.mjs +40 -55
  23. package/dist/extension/lib/conversations.mjs +1 -1
  24. package/dist/extension/lib/group-routing/conversation-router.d.ts +1 -1
  25. package/dist/extension/lib/group-routing/conversation-router.mjs +2 -2
  26. package/dist/extension/lib/group-routing/dequeue.mjs +2 -2
  27. package/dist/extension/lib/group-routing/record.mjs +1 -1
  28. package/dist/extension/lib/group-routing/replay.mjs +1 -1
  29. package/dist/extension/lib/group-routing/router.d.ts +1 -1
  30. package/dist/extension/lib/group-routing/router.mjs +3 -3
  31. package/dist/extension/lib/ontology-connection.mjs +2 -2
  32. package/dist/extension/lib/ontology-file-call.mjs +2 -2
  33. package/dist/extension/lib/ontology-tools.mjs +1 -1
  34. package/dist/extension/lib/runtime-envelope.mjs +1 -1
  35. package/dist/extension/lib/sandbox.d.ts +3 -2
  36. package/dist/extension/lib/sandbox.mjs +1 -1
  37. package/dist/extension/lib/stored-files.mjs +1 -1
  38. package/dist/extension/lib/telegram/adapter.mjs +43 -28
  39. package/dist/extension/lib/telegram/archive-message.d.ts +4 -0
  40. package/dist/extension/lib/telegram/archive-message.mjs +85 -0
  41. package/dist/extension/lib/telegram/attachment-description.mjs +1 -1
  42. package/dist/extension/lib/telegram/attachments-send.mjs +2 -2
  43. package/dist/extension/lib/telegram/callbacks.mjs +2 -2
  44. package/dist/extension/lib/telegram/confirmation-expiry.mjs +20 -9
  45. package/dist/extension/lib/telegram/confirmations.d.ts +2 -2
  46. package/dist/extension/lib/telegram/confirmations.mjs +206 -181
  47. package/dist/extension/lib/telegram/conversations.mjs +5 -6
  48. package/dist/extension/lib/telegram/customer-text.mjs +1 -1
  49. package/dist/extension/lib/telegram/deferred-confirmations.mjs +2 -2
  50. package/dist/extension/lib/telegram/delivery-diagnostics.mjs +1 -1
  51. package/dist/extension/lib/telegram/durable-typing.d.ts +11 -0
  52. package/dist/extension/lib/telegram/durable-typing.mjs +10 -10
  53. package/dist/extension/lib/telegram/files.mjs +2 -2
  54. package/dist/extension/lib/telegram/health.mjs +1 -1
  55. package/dist/extension/lib/telegram/held-approvals.d.ts +3 -3
  56. package/dist/extension/lib/telegram/held-approvals.mjs +23 -19
  57. package/dist/extension/lib/telegram/images.d.ts +1 -1
  58. package/dist/extension/lib/telegram/images.mjs +1 -1
  59. package/dist/extension/lib/telegram/inbound-request.d.ts +2 -2
  60. package/dist/extension/lib/telegram/inbound-request.mjs +1 -1
  61. package/dist/extension/lib/telegram/inbound.mjs +5 -6
  62. package/dist/extension/lib/telegram/lifecycle.d.ts +2 -2
  63. package/dist/extension/lib/telegram/lifecycle.mjs +23 -27
  64. package/dist/extension/lib/telegram/media-policy.mjs +1 -1
  65. package/dist/extension/lib/telegram/principal.mjs +1 -1
  66. package/dist/extension/lib/telegram/routing-context.mjs +4 -4
  67. package/dist/extension/lib/telegram/session-recovery.d.ts +1 -1
  68. package/dist/extension/lib/telegram/session-recovery.mjs +2 -1
  69. package/dist/extension/lib/telegram/sessions.d.ts +7 -4
  70. package/dist/extension/lib/telegram/sessions.mjs +6 -3
  71. package/dist/extension/lib/telegram/silent.d.ts +1 -1
  72. package/dist/extension/lib/telegram/silent.mjs +11 -8
  73. package/dist/extension/lib/telegram/structured.mjs +1 -1
  74. package/dist/extension/lib/telegram/turn-errors.d.ts +1 -1
  75. package/dist/extension/lib/telegram/turn-errors.mjs +47 -31
  76. package/dist/extension/lib/telegram/voice.mjs +1 -1
  77. package/dist/extension/lib/workflow-serializable.mjs +1 -1
  78. package/dist/extension/model.mjs +7 -4
  79. package/dist/extension/tools/attachments_discard.mjs +1 -1
  80. package/dist/extension/tools/attachments_list.mjs +1 -1
  81. package/dist/extension/tools/attachments_promote.mjs +2 -2
  82. package/dist/extension/tools/attachments_read.mjs +1 -1
  83. package/dist/extension/tools/attachments_send.mjs +1 -1
  84. package/dist/extension/tools/attachments_table.mjs +1 -1
  85. package/dist/extension/tools/ontology_call_file.d.ts +1 -1
  86. package/dist/extension/tools/ontology_call_file.mjs +1 -1
  87. package/dist/extension/tools/questions_ask.mjs +2 -2
  88. package/dist/extension/tools/sandbox.d.ts +1 -8
  89. package/dist/extension/tools/sandbox.mjs +1 -1
  90. package/package.json +5 -5
  91. /package/dist/_chunks/{content-TRaZitQZ.mjs → content-cDtVnr8Y.mjs} +0 -0
  92. /package/dist/_chunks/{workflow-serializable-_g4JRau5.mjs → workflow-serializable-DU7WeCIW.mjs} +0 -0
@@ -5,12 +5,12 @@ 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 { i as memberIdOf, o as telegramPrincipal } from "../../../_chunks/principal-qyR3jKAr.mjs";
8
+ import { i as memberIdOf, o as telegramPrincipal } from "../../../_chunks/principal-BnJuWJtk.mjs";
9
9
  import { humanText } from "../confirmation-description.mjs";
10
10
  import { renderConfirmation, withConfirmationText } from "./confirmation-text.mjs";
11
11
  import { chatPostCard, chatPostCardStatus, chatPostReceipt, withChatPostCard } from "./chat-post-card.mjs";
12
12
  import { conversationEvent } from "../conversations.mjs";
13
- import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-8xhK7EaW.mjs";
13
+ import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-Dx1B6Y5r.mjs";
14
14
  import { agentMessage, observedChatKey, withConfirmationPrompt } from "../group-routing/observed.mjs";
15
15
  import { chatPostOperationId, isChatPost } from "../chat-post-confirmation.mjs";
16
16
  import { confirmations } from "../confirmations.mjs";
@@ -19,7 +19,7 @@ import { postDeferredCard } from "./deferred-confirmations.mjs";
19
19
  import { answerMarker, isPendingQuestion, pendingQuestions, questionKey } from "../questions.mjs";
20
20
  import { createHash } from "node:crypto";
21
21
  import { telegramTextLinks } from "@segmentstream-cloud/ontology/telegram";
22
- import { registerTelegramFreeformPrompt } from "eve/channels/telegram";
22
+ import { call } from "eve/events";
23
23
  const PREFIX = "ss-confirm:";
24
24
  function shortHash(value) {
25
25
  return createHash("sha256").update(value).digest("hex").slice(0, 20);
@@ -33,26 +33,29 @@ function object(value) {
33
33
  }
34
34
  function openConfirmations(events) {
35
35
  const open = new Map();
36
+ const calls = new Map();
36
37
  for (const event of events) {
37
38
  const data = object(event.data);
38
- if (event.type === "context.cleared" || event.type === "session.started") open.clear();
39
- if (event.type === "input.requested" && Array.isArray(data.requests)) for (const item of data.requests) {
40
- const request = object(item);
41
- const action = object(request.action);
42
- if (request.kind === "tool-approval" && typeof request.requestId === "string" && typeof action.callId === "string") open.set(request.requestId, {
43
- requestId: request.requestId,
44
- callId: action.callId,
39
+ if (event.type === "session.started" || event.type === "context.settled" && data.kind === "clear" && data.outcome === "completed") {
40
+ open.clear();
41
+ calls.clear();
42
+ }
43
+ if (event.type === "call.requested" && typeof data.callId === "string") calls.set(data.callId, data);
44
+ const request = object(data.request);
45
+ const callId = object(data.subject).callId;
46
+ if (event.type === "interaction.opened" && request.kind === "approval" && typeof data.interactionId === "string" && typeof callId === "string" && data.origin === void 0) {
47
+ const action = calls.get(callId);
48
+ if (action) open.set(data.interactionId, {
49
+ requestId: data.interactionId,
50
+ callId,
45
51
  at: Date.parse(event.meta.at),
46
- toolName: String(action.toolName ?? ""),
52
+ toolName: String(object(action.capability).name ?? ""),
47
53
  input: object(action.input)
48
54
  });
49
55
  }
50
- if (event.type === "input.resolved" && Array.isArray(data.resolutions)) for (const item of data.resolutions) {
51
- const id = object(item).requestId;
52
- if (typeof id === "string") open.delete(id);
53
- }
54
- if (event.type === "action.result") {
55
- for (const [key, prompt] of open) if (prompt.callId === object(data.result).callId) open.delete(key);
56
+ if (event.type === "interaction.settled" && typeof data.interactionId === "string") open.delete(data.interactionId);
57
+ if (event.type === "call.settled") {
58
+ for (const [key, prompt] of open) if (prompt.callId === data.callId) open.delete(key);
56
59
  }
57
60
  }
58
61
  return [...open.values()];
@@ -63,17 +66,17 @@ function routingConfirmationState(events, prompt, at) {
63
66
  let callId;
64
67
  for (const event of prompt.mode === "deferred" ? [] : events) {
65
68
  const data = object(event.data);
66
- if ((event.type === "context.cleared" || event.type === "session.started") && Date.parse(event.meta.at) >= Date.parse(prompt.at)) state = "expired";
67
- if (event.type === "input.requested" && Array.isArray(data.requests)) {
68
- for (const request of data.requests) if (object(request).requestId === prompt.requestId) {
69
- callId = String(object(object(request).action).callId);
70
- state = prompt.state ?? "pending";
71
- }
72
- }
73
- if (event.type === "input.resolved" && Array.isArray(data.resolutions)) {
74
- for (const resolution of data.resolutions) if (object(resolution).requestId === prompt.requestId) state = object(resolution).outcome === "approved" ? "approved" : object(resolution).outcome === "ignored" ? "interrupted" : "cancelled";
69
+ if ((event.type === "session.started" || event.type === "context.settled" && data.kind === "clear" && data.outcome === "completed") && Date.parse(event.meta.at) >= Date.parse(prompt.at)) state = "expired";
70
+ if (event.type === "interaction.opened" && data.interactionId === prompt.requestId) {
71
+ callId = String(object(data.subject).callId);
72
+ state = prompt.state ?? "pending";
75
73
  }
76
- if (event.type === "action.result" && callId !== void 0 && object(data.result).callId === callId) state = data.status === "completed" ? "completed" : "failed";
74
+ if (event.type === "interaction.settled" && data.interactionId === prompt.requestId) state = data.outcome === "accepted" ? "approved" : [
75
+ "withdrawn",
76
+ "interrupted",
77
+ "abandoned"
78
+ ].includes(String(data.outcome)) ? "interrupted" : data.outcome === "expired" ? "expired" : "cancelled";
79
+ if (event.type === "call.settled" && callId !== void 0 && data.callId === callId) state = data.outcome === "completed" ? "completed" : "failed";
77
80
  }
78
81
  if (state === "pending" && Date.parse(at) - Date.parse(prompt.at) >= 6e5) return "expired";
79
82
  return state;
@@ -101,16 +104,17 @@ function questionFromHistory(events, owner) {
101
104
  }
102
105
  function questionsFromHistory(events) {
103
106
  const pending = new Map();
107
+ const calls = new Map();
104
108
  for (const event of events) {
105
109
  const data = object(event.data);
106
- if (event.type === "context.cleared") pending.clear();
107
- if (event.type === "action.result") {
108
- const result = object(data.result);
109
- const candidate = object(result.output).pendingQuestion;
110
- if (typeof result.toolName === "string" && result.toolName.endsWith("__questions_ask") && isPendingQuestion(candidate)) pending.set(questionKey(candidate), candidate);
110
+ if (event.type === "context.settled" && data.kind === "clear" && data.outcome === "completed") pending.clear();
111
+ if (event.type === "call.requested" && typeof data.callId === "string") calls.set(data.callId, String(object(data.capability).name ?? ""));
112
+ if (event.type === "call.settled" && data.outcome === "completed") {
113
+ const candidate = object(data.output).pendingQuestion;
114
+ if (calls.get(String(data.callId))?.endsWith("__questions_ask") && isPendingQuestion(candidate)) pending.set(questionKey(candidate), candidate);
111
115
  }
112
- if (event.type === "message.received" && typeof data.message === "string") {
113
- const message = data.message.replace(/^\[source-message:\d+;trigger:\d+\]\r?\n/u, "");
116
+ if (event.type === "delivery.consumed" && Array.isArray(data.parts)) {
117
+ const message = data.parts.filter((part) => object(part).kind === "text").map((part) => object(part).text).join("\n").replace(/^\[source-message:\d+;trigger:\d+\]\r?\n/u, "");
114
118
  for (const [key, question] of pending) if (message.startsWith(answerMarker(question.id))) pending.delete(key);
115
119
  }
116
120
  }
@@ -243,169 +247,189 @@ function telegramConfirmationEvents(archive, observed, deferredOptions) {
243
247
  }
244
248
  };
245
249
  return {
246
- "context.cleared"(_data, channel) {
250
+ "context.settled"(event, ctx) {
251
+ if (event.data.kind !== "clear" || event.data.outcome !== "completed") return;
252
+ const channel = ctx.channel;
247
253
  confirmations.update(() => ({}));
248
254
  pendingQuestions.update(() => ({}));
249
255
  const state = channel.state;
250
256
  state.confirmationMessages = {};
251
257
  state.pendingQuestions = {};
252
258
  },
253
- async "input.requested"(data, channel, ctx) {
254
- for (const request of data.requests) {
255
- if (request.kind !== "tool-approval") {
256
- const text = [request.prompt, ...(request.options ?? []).map((option, index) => `${index + 1}. ${option.label}`)].join("\n");
257
- if (text.length > 4096) throw new Error("A typed Telegram question must fit in one message.");
258
- const sent = await withConfirmationText(text, () => channel.telegram.post({
259
- text,
260
- reply_markup: {
261
- force_reply: true,
262
- selective: true
263
- }
264
- }));
265
- remember(channel, ctx, sent.id, text);
266
- registerTelegramFreeformPrompt(channel.state, {
267
- messageId: sent.id,
268
- requestId: request.requestId
269
- });
270
- channel.state.hitlPrompts = {
271
- ...channel.state.hitlPrompts,
272
- [request.requestId]: {
273
- messageId: sent.id,
274
- text,
275
- options: (request.options ?? []).map(({ id, label }) => ({
276
- id,
277
- label
278
- }))
279
- }
280
- };
281
- continue;
282
- }
283
- const memberId = confirmations.get()[request.action.callId]?.memberId ?? memberIdOf(ctx.session.auth.current);
284
- if (memberId === void 0) throw new Error("Confirmation requires a member.");
285
- const saved = confirmations.get()[request.action.callId];
286
- const description = saved?.description;
287
- const exactPost = isChatPost(request.action.toolName);
288
- if (description === void 0) throw new Error("Approval buttons require trusted confirmation facts.");
289
- if (exactPost && description?.chatPost === void 0) throw new Error("An exact chat post cannot show a generic confirmation.");
290
- const copy = description?.chatPost === void 0 ? renderConfirmation(description, saved?.copy) : chatPostCard(description, saved?.copy);
291
- const text = copy.text;
292
- await record(request.action.callId, "preview", text, "requested", ctx);
293
- const send = () => channel.telegram.post({
259
+ async "interaction.opened"(event, ctx) {
260
+ const channel = ctx.channel;
261
+ const data = event.data;
262
+ const request = data.request;
263
+ if (request.kind === "sign-in") return;
264
+ const requestId = data.interactionId;
265
+ if (request.kind !== "approval") {
266
+ const text = [request.prompt, ...(request.options ?? []).map((option, index) => `${index + 1}. ${option.label}`)].join("\n");
267
+ if (text.length > 4096) throw new Error("A typed Telegram question must fit in one message.");
268
+ const sent = await withConfirmationText(text, () => channel.telegram.post({
294
269
  text,
295
- reply_markup: { inline_keyboard: [[
296
- {
297
- text: copy.confirm,
298
- callback_data: confirmationCallback(request.requestId, memberId, "approve")
299
- },
300
- ...exactPost ? [{
301
- text: copy.edit,
302
- callback_data: confirmationCallback(request.requestId, memberId, "edit")
303
- }] : [],
304
- {
305
- text: copy.cancel,
306
- callback_data: confirmationCallback(request.requestId, memberId, "cancel")
307
- }
308
- ]] }
309
- });
310
- const prompt = {
311
- requestId: request.requestId,
312
- state: "pending",
313
- sessionId: ctx.session.id,
314
- address: channel.continuation?.token ?? formatAddress({
315
- chatId: channel.telegram.chatId,
316
- messageThreadId: String(channel.telegram.messageThreadId ?? ""),
317
- conversationId: channel.telegram.conversationId ?? ""
318
- }),
319
- action: humanText(description?.action) ?? "the requested action",
320
- requestedBy: humanText(ctx.session.auth.current?.attributes.memberName) ?? null,
321
- at: new Date().toISOString(),
322
- ...ctx.session.auth.current?.subject === void 0 ? {} : { requester: {
323
- id: ctx.session.auth.current.subject,
324
- author: humanText(ctx.session.auth.current.attributes.memberName) ?? "the requester"
325
- } }
326
- };
327
- const sent = await withConfirmationPrompt(prompt, () => exactPost ? withChatPostCard(text, send, copy.previewLinks) : withConfirmationText(text, send));
328
- remember(channel, ctx, sent.id, text, prompt);
329
- channel.continuation?.alias(formatAddress({
330
- chatId: channel.telegram.chatId,
331
- messageThreadId: String(channel.telegram.messageThreadId ?? ""),
332
- conversationId: ""
270
+ reply_markup: {
271
+ force_reply: true,
272
+ selective: true
273
+ }
333
274
  }));
334
- const state = channel.state;
335
- state.confirmationMessages = {
336
- ...state.confirmationMessages,
337
- [request.action.callId]: {
338
- requestId: request.requestId,
275
+ remember(channel, ctx, sent.id, text);
276
+ channel.state.pendingFreeformReplies = {
277
+ ...channel.state.pendingFreeformReplies,
278
+ [sent.id]: requestId
279
+ };
280
+ channel.state.hitlPrompts = {
281
+ ...channel.state.hitlPrompts,
282
+ [requestId]: {
339
283
  messageId: sent.id,
340
284
  text,
341
- copy,
342
- exactPost,
343
- prompt,
344
- previewLinks: copy.previewLinks
285
+ options: (request.options ?? []).map(({ id, label }) => ({
286
+ id,
287
+ label
288
+ }))
345
289
  }
346
290
  };
291
+ return;
347
292
  }
348
- },
349
- async "input.resolved"(data, channel, ctx) {
350
- const state = channel.state;
351
- for (const resolution of data.resolutions) {
352
- const entry = Object.entries(state.confirmationMessages ?? {}).find(([, message]) => message.requestId === resolution.requestId);
353
- if (entry === void 0) {
354
- const prompt = channel.state.hitlPrompts?.[resolution.requestId];
355
- if (prompt !== void 0) {
356
- const response = resolution.response;
357
- const label = response?.text ?? prompt.options.find((option) => option.id === response?.optionId)?.label;
358
- await channel.telegram.editMessageText({
359
- messageId: prompt.messageId,
360
- replyMarkup: { inline_keyboard: [] },
361
- text: label === void 0 ? prompt.text : `${prompt.text}\n\n${label}`
362
- });
363
- delete channel.state.hitlPrompts?.[resolution.requestId];
293
+ const callId = "callId" in data.subject ? data.subject.callId : void 0;
294
+ const action = callId === void 0 ? void 0 : call(ctx.view, callId);
295
+ if (callId === void 0 || action === void 0 || data.origin !== void 0) throw new Error("A local tool call is required for confirmation.");
296
+ const memberId = confirmations.get()[callId]?.memberId ?? memberIdOf(ctx.session.auth.current);
297
+ if (memberId === void 0) throw new Error("Confirmation requires a member.");
298
+ const saved = confirmations.get()[callId];
299
+ const description = saved?.description;
300
+ const exactPost = isChatPost(action.name);
301
+ if (description === void 0) throw new Error("Approval buttons require trusted confirmation facts.");
302
+ if (exactPost && description?.chatPost === void 0) throw new Error("An exact chat post cannot show a generic confirmation.");
303
+ const copy = description?.chatPost === void 0 ? renderConfirmation(description, saved?.copy) : chatPostCard(description, saved?.copy);
304
+ const text = copy.text;
305
+ await record(callId, "preview", text, "requested", ctx);
306
+ const send = () => channel.telegram.post({
307
+ text,
308
+ reply_markup: { inline_keyboard: [[
309
+ {
310
+ text: copy.confirm,
311
+ callback_data: confirmationCallback(requestId, memberId, "approve")
312
+ },
313
+ ...exactPost ? [{
314
+ text: copy.edit,
315
+ callback_data: confirmationCallback(requestId, memberId, "edit")
316
+ }] : [],
317
+ {
318
+ text: copy.cancel,
319
+ callback_data: confirmationCallback(requestId, memberId, "cancel")
364
320
  }
365
- continue;
321
+ ]] }
322
+ });
323
+ const prompt = {
324
+ requestId,
325
+ state: "pending",
326
+ sessionId: ctx.session.id,
327
+ address: channel.continuation?.token ?? formatAddress({
328
+ chatId: channel.telegram.chatId,
329
+ messageThreadId: String(channel.telegram.messageThreadId ?? ""),
330
+ conversationId: channel.telegram.conversationId ?? ""
331
+ }),
332
+ action: humanText(description?.action) ?? "the requested action",
333
+ requestedBy: humanText(ctx.session.auth.current?.attributes.memberName) ?? null,
334
+ at: new Date().toISOString(),
335
+ ...ctx.session.auth.current?.subject === void 0 ? {} : { requester: {
336
+ id: ctx.session.auth.current.subject,
337
+ author: humanText(ctx.session.auth.current.attributes.memberName) ?? "the requester"
338
+ } }
339
+ };
340
+ const sent = await withConfirmationPrompt(prompt, () => exactPost ? withChatPostCard(text, send, copy.previewLinks) : withConfirmationText(text, send));
341
+ remember(channel, ctx, sent.id, text, prompt);
342
+ channel.continuation?.alias(formatAddress({
343
+ chatId: channel.telegram.chatId,
344
+ messageThreadId: String(channel.telegram.messageThreadId ?? ""),
345
+ conversationId: ""
346
+ }));
347
+ const state = channel.state;
348
+ state.confirmationMessages = {
349
+ ...state.confirmationMessages,
350
+ [callId]: {
351
+ requestId,
352
+ messageId: sent.id,
353
+ text,
354
+ copy,
355
+ exactPost,
356
+ prompt,
357
+ previewLinks: copy.previewLinks
366
358
  }
367
- const [callId, message] = entry;
368
- const copy = message.copy ?? renderConfirmation();
369
- const reason = confirmations.get()[callId]?.cancellationReason;
370
- const status = resolution.outcome === "approved" ? copy.executing : reason === "expired" ? copy.expired : resolution.outcome === "ignored" ? copy.interrupted : copy.cancelled;
371
- const text = message.exactPost ? chatPostCardStatus(message.text, status) : `${message.text}\n\n${status}`;
372
- const edit = () => channel.telegram.editMessageText({
373
- messageId: message.messageId,
374
- replyMarkup: { inline_keyboard: [] },
375
- text
376
- });
377
- const prompt = {
378
- ...messagePrompt(message),
379
- state: resolution.outcome === "approved" ? "approved" : reason === "expired" ? "expired" : resolution.outcome === "ignored" ? "interrupted" : "cancelled"
380
- };
381
- message.prompt = prompt;
382
- await withConfirmationPrompt(prompt, () => message.exactPost ? withChatPostCard(text, edit, [...message.previewLinks ?? [], ...telegramTextLinks(status)]) : withConfirmationText(text, edit));
383
- remember(channel, ctx, message.messageId, text, prompt);
384
- await record(callId, reason ?? resolution.outcome, text, resolution.outcome === "approved" ? "requested" : "cancelled", ctx);
385
- if (resolution.outcome !== "approved") {
386
- delete state.confirmationMessages?.[callId];
387
- confirmations.update((all) => ({
388
- ...all,
389
- ...all[callId] === void 0 ? {} : { [callId]: {
390
- ...all[callId],
391
- status: "cancelled"
392
- } }
393
- }));
359
+ };
360
+ },
361
+ async "interaction.settled"(event, ctx) {
362
+ const channel = ctx.channel;
363
+ const state = channel.state;
364
+ const resolution = event.data;
365
+ const entry = Object.entries(state.confirmationMessages ?? {}).find(([, message]) => message.requestId === resolution.interactionId);
366
+ if (entry === void 0) {
367
+ const prompt = channel.state.hitlPrompts?.[resolution.interactionId];
368
+ if (prompt !== void 0) {
369
+ const response = resolution.response;
370
+ const label = response?.text ?? prompt.options.find((option) => option.id === response?.optionId)?.label;
371
+ await channel.telegram.editMessageText({
372
+ messageId: prompt.messageId,
373
+ replyMarkup: { inline_keyboard: [] },
374
+ text: label === void 0 ? prompt.text : `${prompt.text}\n\n${label}`
375
+ });
376
+ delete channel.state.hitlPrompts?.[resolution.interactionId];
377
+ delete channel.state.pendingFreeformReplies?.[prompt.messageId];
394
378
  }
379
+ return;
380
+ }
381
+ const [callId, message] = entry;
382
+ const copy = message.copy ?? renderConfirmation();
383
+ const reason = confirmations.get()[callId]?.cancellationReason;
384
+ const status = resolution.outcome === "accepted" ? copy.executing : reason === "expired" ? copy.expired : [
385
+ "withdrawn",
386
+ "interrupted",
387
+ "abandoned"
388
+ ].includes(resolution.outcome) ? copy.interrupted : copy.cancelled;
389
+ const text = message.exactPost ? chatPostCardStatus(message.text, status) : `${message.text}\n\n${status}`;
390
+ const edit = () => channel.telegram.editMessageText({
391
+ messageId: message.messageId,
392
+ replyMarkup: { inline_keyboard: [] },
393
+ text
394
+ });
395
+ const prompt = {
396
+ ...messagePrompt(message),
397
+ state: resolution.outcome === "accepted" ? "approved" : reason === "expired" ? "expired" : [
398
+ "withdrawn",
399
+ "interrupted",
400
+ "abandoned"
401
+ ].includes(resolution.outcome) ? "interrupted" : "cancelled"
402
+ };
403
+ message.prompt = prompt;
404
+ await withConfirmationPrompt(prompt, () => message.exactPost ? withChatPostCard(text, edit, [...message.previewLinks ?? [], ...telegramTextLinks(status)]) : withConfirmationText(text, edit));
405
+ remember(channel, ctx, message.messageId, text, prompt);
406
+ await record(callId, reason ?? (resolution.outcome === "accepted" ? "approved" : resolution.outcome === "declined" ? "cancelled" : "ignored"), text, resolution.outcome === "accepted" ? "requested" : "cancelled", ctx);
407
+ if (resolution.outcome !== "accepted") {
408
+ delete state.confirmationMessages?.[callId];
409
+ confirmations.update((all) => ({
410
+ ...all,
411
+ ...all[callId] === void 0 ? {} : { [callId]: {
412
+ ...all[callId],
413
+ status: "cancelled"
414
+ } }
415
+ }));
395
416
  }
396
417
  },
397
- async "action.result"(data, channel, ctx) {
418
+ async "call.settled"(event, ctx) {
419
+ const channel = ctx.channel;
398
420
  const state = channel.state;
399
- if (data.result.kind === "tool-result" && deferredOptions !== void 0 && archive !== void 0) {
400
- const output = object(data.result.output);
421
+ const data = event.data;
422
+ const action = call(ctx.view, data.callId);
423
+ if (action?.kind === "tool" && deferredOptions !== void 0 && archive !== void 0) {
424
+ const output = object(data.output);
401
425
  if (output.status === "awaiting_confirmation" && typeof output.confirmation === "string") {
402
426
  await postDeferredCard(output.confirmation, channel, ctx, deferredOptions, archive, observed);
403
427
  return;
404
428
  }
405
429
  }
406
- if (data.result.kind === "tool-result") {
407
- const question = object(data.result.output).pendingQuestion;
408
- if (data.result.toolName.endsWith("__questions_ask") && isPendingQuestion(question)) {
430
+ if (action?.kind === "tool") {
431
+ const question = object(data.output).pendingQuestion;
432
+ if (action.name.endsWith("__questions_ask") && isPendingQuestion(question)) {
409
433
  const address = channel.continuation?.token ?? formatAddress({
410
434
  chatId: channel.telegram.chatId,
411
435
  messageThreadId: String(channel.telegram.messageThreadId ?? ""),
@@ -439,13 +463,13 @@ function telegramConfirmationEvents(archive, observed, deferredOptions) {
439
463
  remember(channel, ctx, sent.id, question.question);
440
464
  }
441
465
  }
442
- const message = state.confirmationMessages?.[data.result.callId];
466
+ const message = state.confirmationMessages?.[data.callId];
443
467
  if (message === void 0) return;
444
- const failed = data.status !== "completed" || data.result.kind === "tool-result" && (data.result.isError === true || object(data.result.output).isError === true || object(object(data.result.output).validation).ok === false);
468
+ const failed = data.outcome !== "completed" || action?.kind === "tool" && (object(data.output).isError === true || object(object(data.output).validation).ok === false);
445
469
  if (message.exactPost) {
446
470
  const copy = message.copy ?? renderConfirmation();
447
- const receipt = !failed && data.result.kind === "tool-result" ? chatPostReceipt(data.result.output) : void 0;
448
- const errorId = failed ? reportTelegramError("confirmed_chat_post_failed", data, { callId: data.result.callId }) : void 0;
471
+ const receipt = !failed && action?.kind === "tool" ? chatPostReceipt(data.output) : void 0;
472
+ const errorId = failed ? reportTelegramError("confirmed_chat_post_failed", data, { callId: data.callId }) : void 0;
449
473
  const status = receipt ?? (failed ? `${copy.failed} ${errorId}` : telegramText("chatPost", "uncertain") ?? "");
450
474
  const text = chatPostCardStatus(message.text, status);
451
475
  const prompt = {
@@ -458,10 +482,10 @@ function telegramConfirmationEvents(archive, observed, deferredOptions) {
458
482
  text
459
483
  }), [...message.previewLinks ?? [], ...telegramTextLinks(status)]));
460
484
  remember(channel, ctx, message.messageId, text, prompt);
461
- await record(data.result.callId, "receipt", text, failed ? "failed" : object(data.result.kind === "tool-result" ? data.result.output : void 0).status === "sent" ? "completed" : "unknown", ctx);
485
+ await record(data.callId, "receipt", text, failed ? "failed" : object(action?.kind === "tool" ? data.output : void 0).status === "sent" ? "completed" : "unknown", ctx);
462
486
  } else {
463
487
  const copy = message.copy ?? renderConfirmation();
464
- const errorId = failed ? reportTelegramError("confirmed_action_failed", data, { callId: data.result.callId }) : void 0;
488
+ const errorId = failed ? reportTelegramError("confirmed_action_failed", data, { callId: data.callId }) : void 0;
465
489
  const status = failed ? `${copy.failed} ${errorId}` : copy.completed;
466
490
  const text = `${message.text}\n\n${status}`;
467
491
  const prompt = {
@@ -475,9 +499,10 @@ function telegramConfirmationEvents(archive, observed, deferredOptions) {
475
499
  })));
476
500
  remember(channel, ctx, message.messageId, text, prompt);
477
501
  }
478
- delete state.confirmationMessages?.[data.result.callId];
502
+ delete state.confirmationMessages?.[data.callId];
479
503
  },
480
- "turn.started"(_data, channel, ctx) {
504
+ "turn.started"(_event, ctx) {
505
+ const channel = ctx.channel;
481
506
  const memberId = memberIdOf(ctx.session.auth.current);
482
507
  const answeredId = ctx.session.auth.current?.attributes.pendingQuestionId;
483
508
  if (memberId !== void 0 && typeof answeredId === "string") {
@@ -4,16 +4,16 @@ 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 contentText, t as CONTENT_CAPS } from "../../../_chunks/content-TRaZitQZ.mjs";
7
+ import { n as contentText, t as CONTENT_CAPS } from "../../../_chunks/content-cDtVnr8Y.mjs";
8
8
  import { conversationEvent, sessionFailureEventId } from "../conversations.mjs";
9
- import { n as currentInboundRequest, p as reportTelegramError, s as currentThreadId } from "../../../_chunks/inbound-request-8xhK7EaW.mjs";
9
+ import { n as currentInboundRequest, p as reportTelegramError, s as currentThreadId } from "../../../_chunks/inbound-request-Dx1B6Y5r.mjs";
10
10
  import { currentConfirmationPrompt, observedChatKey } from "../group-routing/observed.mjs";
11
11
  import { archivedRoutingMessages } from "../group-routing/routing-context.mjs";
12
12
  import { contentShape, messageContent } from "./content.mjs";
13
13
  import { messageMetadata } from "./observed.mjs";
14
+ import { parseArchivedMessage } from "./archive-message.mjs";
14
15
  import { reportMembership } from "./membership.mjs";
15
16
  import { createHash, randomUUID } from "node:crypto";
16
- import { parseTelegramUpdate } from "eve/channels/telegram";
17
17
  const captures = new WeakMap();
18
18
  const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
19
19
  const id = (value) => typeof value === "number" || typeof value === "string" ? String(value) : null;
@@ -105,9 +105,8 @@ async function captureTelegramWebhook(input) {
105
105
  }
106
106
  const edited = update.edited_message ?? update.edited_channel_post;
107
107
  const messagePayload = edited ?? update.channel_post ?? update.message;
108
- const parsed = parseTelegramUpdate({ message: messagePayload });
109
- if (parsed?.kind !== "message") return input.next();
110
- const message = parsed.message;
108
+ const message = parseArchivedMessage(messagePayload);
109
+ if (message === void 0) return input.next();
111
110
  const content = messageContent(message.raw);
112
111
  const structured = input.structured?.(message.raw);
113
112
  const time = edited === void 0 ? message.raw.date : message.raw.edit_date ?? message.raw.date;
@@ -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-8xhK7EaW.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-Dx1B6Y5r.mjs";
9
9
  export { TelegramDeliveryError, currentLocale, currentThreadId, customerCopy, customerMessage, customerText, failureText, formattingRefused, isTechnicalText, reportTelegramError, safeTelegramFetch, withDeliveryLocale };
@@ -5,12 +5,12 @@ 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 { o as telegramPrincipal } from "../../../_chunks/principal-qyR3jKAr.mjs";
8
+ import { o as telegramPrincipal } from "../../../_chunks/principal-BnJuWJtk.mjs";
9
9
  import { humanText } from "../confirmation-description.mjs";
10
10
  import { renderConfirmation, withConfirmationText } from "./confirmation-text.mjs";
11
11
  import { chatPostCard, chatPostCardStatus, chatPostReceipt, withChatPostCard } from "./chat-post-card.mjs";
12
12
  import { conversationEvent } from "../conversations.mjs";
13
- import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-8xhK7EaW.mjs";
13
+ import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-Dx1B6Y5r.mjs";
14
14
  import { agentMessage, observedChatKey, withConfirmationPrompt } from "../group-routing/observed.mjs";
15
15
  import { CONFIRMATION_TTL_MS, inputHash } from "../confirmations.mjs";
16
16
  import { OntologyToolRejected } from "../ontology-tools.mjs";
@@ -5,7 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { redactTelegramDiagnostic } from "./diagnostic-redaction.mjs";
8
- import { n as contentText, t as CONTENT_CAPS } from "../../../_chunks/content-TRaZitQZ.mjs";
8
+ import { n as contentText, t as CONTENT_CAPS } from "../../../_chunks/content-cDtVnr8Y.mjs";
9
9
  import { conversationEvent } from "../conversations.mjs";
10
10
  import { AsyncLocalStorage } from "node:async_hooks";
11
11
  const pending = new AsyncLocalStorage();
@@ -14,6 +14,17 @@ export interface TypingCursor {
14
14
  }
15
15
  interface TypingEvent {
16
16
  readonly type: string;
17
+ readonly scope?: {
18
+ readonly turnId?: string;
19
+ };
20
+ readonly meta?: {
21
+ readonly position?: {
22
+ readonly line: number;
23
+ readonly index: number;
24
+ };
25
+ readonly endOfLine?: boolean;
26
+ readonly next?: number;
27
+ };
17
28
  readonly data?: unknown;
18
29
  }
19
30
  interface TypingBlockDependencies {
@@ -8,7 +8,7 @@ import { channelsEnabled, proxyEnvironment } from "../environment.mjs";
8
8
  import { redactTelegramDiagnostic } from "./diagnostic-redaction.mjs";
9
9
  import { runtimeToken } from "../runtime-token.mjs";
10
10
  import { conversationEvent, conversations } from "../conversations.mjs";
11
- import { p as reportTelegramError } from "../../../_chunks/inbound-request-8xhK7EaW.mjs";
11
+ import { p as reportTelegramError } from "../../../_chunks/inbound-request-Dx1B6Y5r.mjs";
12
12
  import { connectionProxy } from "../connection-proxy.mjs";
13
13
  import { sessions } from "eve/server";
14
14
  const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
@@ -46,16 +46,16 @@ async function runTypingBlock(source, cursor, dependencies) {
46
46
  if (next === null) return result(false, "history_read_timeout");
47
47
  if (next.done) break;
48
48
  const event = next.value;
49
- index += 1;
49
+ if (event.meta?.position === void 0) index += 1;
50
+ else if (event.meta.endOfLine !== false) index = event.meta.next ?? event.meta.position.line + 1;
50
51
  const data = object(event.data) ? event.data : {};
51
- if (event.type === "message.received" && typeof data.message === "string" && typeof source.marker === "string" && source.marker !== "" && data.message.includes(source.marker) && typeof data.turnId === "string") turnId = data.turnId;
52
- if (["session.failed", "session.completed"].includes(event.type)) return result(true, event.type);
53
- if (turnId !== null && data.turnId === turnId && ([
54
- "turn.completed",
55
- "turn.failed",
56
- "turn.cancelled",
57
- "input.requested"
58
- ].includes(event.type) || event.type === "turn.waiting" && data.on === "input")) return result(true, event.type);
52
+ if (event.type === "delivery.consumed" && Array.isArray(data.parts) && typeof source.marker === "string" && source.marker !== "" && data.parts.some((part) => object(part) && part.kind === "text" && typeof part.text === "string" && part.text.includes(source.marker)) && typeof data.turnId === "string") turnId = data.turnId;
53
+ if (event.type === "session.ended") return result(true, event.type);
54
+ if (turnId !== null && (event.scope?.turnId ?? data.turnId) === turnId && [
55
+ "turn.settled",
56
+ "turn.paused",
57
+ "interaction.opened"
58
+ ].includes(event.type)) return result(true, event.type);
59
59
  }
60
60
  } finally {
61
61
  clearTimeout(timer);
@@ -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 { n as conversationLanguage } from "../../../_chunks/principal-qyR3jKAr.mjs";
8
- import { d as failureText, p as reportTelegramError } from "../../../_chunks/inbound-request-8xhK7EaW.mjs";
7
+ import { n as conversationLanguage } from "../../../_chunks/principal-BnJuWJtk.mjs";
8
+ import { d as failureText, p as reportTelegramError } from "../../../_chunks/inbound-request-Dx1B6Y5r.mjs";
9
9
  import { describableImage, describeReceivedImage } from "./images.mjs";
10
10
  import { MAXIMUM_ATTACHMENT_BYTES, attachmentUrl } from "../attachments.mjs";
11
11
  function telegramConversation(chatId) {