@segmentstream/agent 0.6.14 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +10 -10
  2. package/dist/_chunks/{customer-text-DjszxgTo.mjs → inbound-request-BwBvXk0A.mjs} +110 -109
  3. package/dist/extension/_manifest.json +2 -0
  4. package/dist/extension/channels/telegram.mjs +1 -0
  5. package/dist/extension/config.d.ts +8 -1
  6. package/dist/extension/config.mjs +14 -2
  7. package/dist/extension/connections/ontology.mjs +1 -1
  8. package/dist/extension/hooks/conversations.mjs +1 -1
  9. package/dist/extension/instructions/conversations.mjs +1 -1
  10. package/dist/extension/instructions.md +17 -0
  11. package/dist/extension/lib/confirmation-description.d.ts +5 -0
  12. package/dist/extension/lib/confirmation-description.mjs +7 -1
  13. package/dist/extension/lib/confirmation-outcome.d.ts +19 -0
  14. package/dist/extension/lib/confirmation-outcome.mjs +34 -0
  15. package/dist/extension/lib/confirmations.mjs +1 -1
  16. package/dist/extension/lib/conversation-runtime.mjs +1 -1
  17. package/dist/extension/lib/customer-text-safety.mjs +2 -1
  18. package/dist/extension/lib/deferred-confirmations.d.ts +39 -0
  19. package/dist/extension/lib/deferred-confirmations.mjs +149 -0
  20. package/dist/extension/lib/ontology-tools.d.ts +2 -0
  21. package/dist/extension/lib/ontology-tools.mjs +6 -1
  22. package/dist/extension/lib/telegram/adapter.d.ts +1 -0
  23. package/dist/extension/lib/telegram/adapter.mjs +72 -6
  24. package/dist/extension/lib/telegram/attachment-description.mjs +1 -1
  25. package/dist/extension/lib/telegram/attachments-send.mjs +3 -3
  26. package/dist/extension/lib/telegram/confirmation-expiry.d.ts +4 -0
  27. package/dist/extension/lib/telegram/confirmation-expiry.mjs +50 -0
  28. package/dist/extension/lib/telegram/confirmations.d.ts +2 -1
  29. package/dist/extension/lib/telegram/confirmations.mjs +13 -5
  30. package/dist/extension/lib/telegram/conversations.mjs +1 -1
  31. package/dist/extension/lib/telegram/customer-text.mjs +1 -1
  32. package/dist/extension/lib/telegram/deferred-confirmations.d.ts +26 -0
  33. package/dist/extension/lib/telegram/deferred-confirmations.mjs +282 -0
  34. package/dist/extension/lib/telegram/files.mjs +1 -1
  35. package/dist/extension/lib/telegram/images.mjs +1 -1
  36. package/dist/extension/lib/telegram/inbound-request.d.ts +2 -0
  37. package/dist/extension/lib/telegram/inbound-request.mjs +1 -1
  38. package/dist/extension/lib/telegram/inbound.mjs +5 -5
  39. package/dist/extension/lib/telegram/media-policy.mjs +1 -1
  40. package/dist/extension/lib/telegram/observed.d.ts +1 -0
  41. package/dist/extension/lib/telegram/routing-context.mjs +27 -2
  42. package/dist/extension/lib/telegram/silent.mjs +1 -1
  43. package/dist/extension/lib/telegram/turn-errors.mjs +1 -1
  44. package/dist/extension/lib/telegram/voice.mjs +1 -1
  45. package/dist/extension/schedules/confirmations.d.ts +6 -0
  46. package/dist/extension/schedules/confirmations.mjs +33 -0
  47. package/dist/extension/tools/ontology.d.ts +4 -0
  48. package/dist/extension/tools/ontology.mjs +30 -0
  49. package/dist/tools/index.d.ts +1 -0
  50. package/dist/tools/index.mjs +2 -1
  51. package/package.json +2 -2
  52. package/dist/_chunks/{conversation-runtime-BINKO_6b.mjs → conversation-runtime-ovs8IhXJ.mjs} +1 -1
@@ -0,0 +1,282 @@
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 { telegramPrincipal } from "./principal.mjs";
8
+ import { humanText } from "../confirmation-description.mjs";
9
+ import { renderConfirmation, withConfirmationText } from "./confirmation-text.mjs";
10
+ import { confirmationOutcome } from "../confirmation-outcome.mjs";
11
+ import { chatPostCard, chatPostCardStatus, chatPostReceipt, withChatPostCard } from "./chat-post-card.mjs";
12
+ import { CONFIRMATION_TTL_MS, inputHash } from "../confirmations.mjs";
13
+ import { deferredConfirmations, deferredKey, runDeferredAction } from "../deferred-confirmations.mjs";
14
+ import { conversationEvent } from "../conversations.mjs";
15
+ import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-BwBvXk0A.mjs";
16
+ import { formatAddress } from "./sessions.mjs";
17
+ import { agentMessage, observedChatKey, withConfirmationPrompt } from "./observed.mjs";
18
+ import { telegramTextLinks } from "@segmentstream-cloud/ontology/telegram";
19
+ const PREFIX = "ss-confirm:d:";
20
+ const isDeferredCallback = (data) => data?.startsWith(PREFIX) === true;
21
+ const deferredCallback = (record, choice) => `${PREFIX}${record.key}:${choice}`;
22
+ const copyOf = (record) => record.data.confirmation.description?.chatPost ? chatPostCard(record.data.confirmation.description, record.data.confirmation.copy) : renderConfirmation(record.data.confirmation.description, record.data.confirmation.copy);
23
+ function promptOf(record, state) {
24
+ const name = humanText(record.data.requesterName);
25
+ return {
26
+ mode: "deferred",
27
+ requestId: record.id,
28
+ sessionId: record.data.sessionId,
29
+ address: record.data.address,
30
+ action: humanText(record.data.confirmation.description?.action) ?? "❓",
31
+ requestedBy: name ?? null,
32
+ at: new Date(record.expiresAt - CONFIRMATION_TTL_MS).toISOString(),
33
+ state,
34
+ ...name === void 0 ? {} : { requester: {
35
+ id: record.data.requester,
36
+ author: name
37
+ } }
38
+ };
39
+ }
40
+ function remember(record, text, prompt, observed) {
41
+ if (record.data.messageId === void 0) return;
42
+ observed?.remember(observedChatKey({
43
+ chatId: record.data.chatId,
44
+ ...record.data.threadId ? { messageThreadId: Number(record.data.threadId) } : {}
45
+ }), {
46
+ ...agentMessage({
47
+ text,
48
+ at: new Date().toISOString(),
49
+ messageId: record.data.messageId,
50
+ ...prompt.requester === void 0 ? {} : { inReplyTo: prompt.requester }
51
+ }),
52
+ confirmation: prompt
53
+ });
54
+ }
55
+ async function archiveDeferred(record, phase, archive) {
56
+ await archive.append(conversationEvent({
57
+ eventId: `confirmation:${record.id}:${phase}`,
58
+ provider: "telegram",
59
+ chatId: record.data.chatId,
60
+ threadId: record.data.threadId || null,
61
+ chatType: record.data.chatId.startsWith("-") ? "group" : "private",
62
+ participantIds: [record.memberId],
63
+ kind: "tool_result",
64
+ sessionId: record.data.sessionId,
65
+ toolCallId: record.id,
66
+ operationId: record.id,
67
+ toolName: record.data.action,
68
+ reason: phase,
69
+ status: phase === "done" ? "completed" : phase === "failed" ? "failed" : phase === "cancelled" || phase === "expired" ? "cancelled" : "requested"
70
+ }));
71
+ }
72
+ async function postDeferredCard(id, channel, ctx, options, archive, observed) {
73
+ const store = deferredConfirmations(options);
74
+ const record = await store.command({
75
+ operation: "read",
76
+ key: deferredKey(id)
77
+ });
78
+ if (!record || record.data.sessionId !== ctx.session.id || record.status !== "pending" || record.data.messageId) return;
79
+ const copy = copyOf(record);
80
+ const address = channel.continuation?.token ?? formatAddress({
81
+ chatId: record.data.chatId,
82
+ messageThreadId: record.data.threadId,
83
+ conversationId: channel.telegram.conversationId ?? ""
84
+ });
85
+ const prompt = {
86
+ ...promptOf(record, "pending"),
87
+ address
88
+ };
89
+ await archiveDeferred(record, "requested", archive);
90
+ const post = () => channel.telegram.post({
91
+ text: copy.text,
92
+ reply_markup: { inline_keyboard: [[
93
+ {
94
+ text: copy.confirm,
95
+ callback_data: deferredCallback(record, "y")
96
+ },
97
+ ...record.data.confirmation.description?.chatPost ? [{
98
+ text: copy.edit,
99
+ callback_data: deferredCallback(record, "e")
100
+ }] : [],
101
+ {
102
+ text: copy.cancel,
103
+ callback_data: deferredCallback(record, "n")
104
+ }
105
+ ]] }
106
+ });
107
+ const sent = await withConfirmationPrompt(prompt, () => record.data.confirmation.description?.chatPost ? withChatPostCard(copy.text, post, copy.previewLinks) : withConfirmationText(copy.text, post));
108
+ await store.command({
109
+ operation: "attach",
110
+ key: record.key,
111
+ messageId: sent.id,
112
+ address
113
+ });
114
+ remember({
115
+ ...record,
116
+ data: {
117
+ ...record.data,
118
+ messageId: sent.id,
119
+ address
120
+ }
121
+ }, copy.text, prompt, observed);
122
+ }
123
+ async function editCard(record, context, status, state, observed) {
124
+ if (!record.data.messageId) return;
125
+ const copy = copyOf(record);
126
+ const exact = record.data.confirmation.description?.chatPost !== void 0;
127
+ const text = exact ? chatPostCardStatus(copy.text, status) : `${copy.text}\n\n${status}`;
128
+ const edit = () => context.telegram.editMessageText({
129
+ messageId: record.data.messageId,
130
+ text,
131
+ replyMarkup: { inline_keyboard: [] }
132
+ });
133
+ const prompt = promptOf(record, state);
134
+ await withConfirmationPrompt(prompt, () => exact ? withChatPostCard(text, edit, [...copy.previewLinks ?? [], ...telegramTextLinks(status)]) : withConfirmationText(text, edit));
135
+ remember(record, text, prompt, observed);
136
+ }
137
+ async function handleDeferredCallback(input) {
138
+ const { query, context, options, archive } = input;
139
+ const notice = async (text) => {
140
+ try {
141
+ await context.telegram.answerCallbackQuery({
142
+ callbackQueryId: query.id,
143
+ text,
144
+ showAlert: false
145
+ });
146
+ } catch (error) {
147
+ reportTelegramError("deferred_acknowledgment_failed", error);
148
+ }
149
+ };
150
+ const parts = /^ss-confirm:d:([0-9a-f]{20}):([yne])$/.exec(query.data ?? "");
151
+ if (!parts || !query.message || query.from.isBot) {
152
+ await notice("⚠️");
153
+ return;
154
+ }
155
+ const store = deferredConfirmations(options);
156
+ const record = await store.command({
157
+ operation: "read",
158
+ key: parts[1]
159
+ });
160
+ const lookup = await input.identities.lookup({
161
+ provider: "telegram",
162
+ externalId: query.from.id
163
+ });
164
+ if (!record || lookup.status !== "paired" || lookup.member.id !== record.memberId || record.data.requester !== query.from.id || record.data.chatId !== query.message.chat.id || record.data.threadId !== String(query.message.messageThreadId ?? "") || record.data.messageId !== query.message.messageId) {
165
+ await notice("🔒");
166
+ return;
167
+ }
168
+ if (record.inputHash !== inputHash(record.data.input) || parts[2] === "e" && record.data.confirmation.description?.chatPost === void 0) {
169
+ await notice("⚠️");
170
+ return;
171
+ }
172
+ const copy = copyOf(record);
173
+ const decision = parts[2] === "y" ? "approve" : "cancel";
174
+ const claimed = await store.command({
175
+ operation: "decide",
176
+ key: record.key,
177
+ memberId: record.memberId,
178
+ inputHash: inputHash(record.data.input),
179
+ decision
180
+ });
181
+ if (!claimed || claimed.status === "pending") {
182
+ await notice("⌛");
183
+ return;
184
+ }
185
+ await notice(claimed.status === "expired" ? "⌛" : void 0);
186
+ let status = claimed.status;
187
+ let cardStatus = status === "expired" ? copy.expired : copy.cancelled;
188
+ let execution;
189
+ try {
190
+ await archiveDeferred(claimed, claimed.status, archive);
191
+ } catch (error) {
192
+ const reference = reportTelegramError("deferred_decision_archive_failed", error);
193
+ if (status === "approved") {
194
+ status = "failed";
195
+ execution = "not_started";
196
+ cardStatus = `${copy.failed} ${reference}`;
197
+ await store.command({
198
+ operation: "finish",
199
+ key: claimed.key,
200
+ status
201
+ });
202
+ }
203
+ }
204
+ if (status === "approved") {
205
+ try {
206
+ await editCard(claimed, context, copy.executing, "approved", input.observed);
207
+ } catch (error) {
208
+ reportTelegramError("deferred_card_update_failed", error);
209
+ }
210
+ try {
211
+ const result = await runDeferredAction(claimed, options);
212
+ status = "done";
213
+ cardStatus = claimed.data.confirmation.description?.chatPost ? chatPostReceipt(result.structuredContent, copy.completed) ?? "⚠️ ❓" : copy.completed;
214
+ } catch (error) {
215
+ status = "failed";
216
+ const reference = reportTelegramError("deferred_action_failed", error);
217
+ cardStatus = `${copy.failed} ${reference}`;
218
+ }
219
+ await store.command({
220
+ operation: "finish",
221
+ key: claimed.key,
222
+ status
223
+ });
224
+ }
225
+ if (status === "done" || status === "failed") try {
226
+ await archiveDeferred(claimed, status, archive);
227
+ } catch (error) {
228
+ reportTelegramError("deferred_outcome_archive_failed", error);
229
+ }
230
+ try {
231
+ await editCard(claimed, context, cardStatus, status === "done" ? "completed" : status, input.observed);
232
+ } catch (error) {
233
+ reportTelegramError("deferred_card_update_failed", error);
234
+ }
235
+ const principal = telegramPrincipal({
236
+ member: lookup.member,
237
+ message: {
238
+ ...query.message,
239
+ from: query.from,
240
+ text: "",
241
+ caption: "",
242
+ raw: {},
243
+ attachments: []
244
+ }
245
+ });
246
+ await currentInboundRequest()?.confirmationEvent?.(claimed, confirmationOutcome(claimed, status, {
247
+ ...execution === void 0 ? {} : { execution },
248
+ ...parts[2] === "e" && status === "cancelled" ? { editRequested: true } : {}
249
+ }), principal);
250
+ }
251
+ async function expireDeferredRecord(record, context, options, archive, notify, observed) {
252
+ const claimed = await deferredConfirmations(options).command({
253
+ operation: "decide",
254
+ key: record.key,
255
+ memberId: record.memberId,
256
+ inputHash: record.inputHash,
257
+ decision: "expire"
258
+ });
259
+ if (!claimed) return;
260
+ try {
261
+ await archiveDeferred(claimed, "expired", archive);
262
+ } catch (error) {
263
+ reportTelegramError("deferred_outcome_archive_failed", error);
264
+ }
265
+ try {
266
+ await editCard(claimed, context, copyOf(claimed).expired, "expired", observed);
267
+ } catch (error) {
268
+ reportTelegramError("deferred_card_update_failed", error);
269
+ }
270
+ await notify(claimed, confirmationOutcome(claimed, "expired"), null);
271
+ }
272
+ async function expireDeferredCards(context, options, archive, observed) {
273
+ const records = await deferredConfirmations(options).command({
274
+ operation: "expired",
275
+ chatId: context.telegram.chatId
276
+ });
277
+ for (const record of records) {
278
+ if (record.data.threadId !== String(context.telegram.messageThreadId ?? "")) continue;
279
+ await expireDeferredRecord(record, context, options, archive, async (saved, result, auth) => currentInboundRequest()?.confirmationEvent?.(saved, result, auth), observed);
280
+ }
281
+ }
282
+ export { archiveDeferred, deferredCallback, expireDeferredCards, expireDeferredRecord, handleDeferredCallback, isDeferredCallback, postDeferredCard };
@@ -5,7 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { conversationLanguage } from "./principal.mjs";
8
- import { l as reportTelegramError, s as failureText } from "../../../_chunks/customer-text-DjszxgTo.mjs";
8
+ import { d as failureText, p as reportTelegramError } from "../../../_chunks/inbound-request-BwBvXk0A.mjs";
9
9
  import { describableImage, describeReceivedImage } from "./images.mjs";
10
10
  import { MAXIMUM_ATTACHMENT_BYTES, attachmentUrl } from "../attachments.mjs";
11
11
  function telegramConversation(chatId) {
@@ -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 { l as reportTelegramError } from "../../../_chunks/customer-text-DjszxgTo.mjs";
7
+ import { p as reportTelegramError } from "../../../_chunks/inbound-request-BwBvXk0A.mjs";
8
8
  import { gateway, generateText } from "ai";
9
9
  const MAXIMUM_IMAGE_DESCRIPTION_BYTES = 5242880;
10
10
  const MAXIMUM_NATIVE_IMAGE_BYTES = 3145728;
@@ -1,3 +1,4 @@
1
+ import type { DeferredConfirmation } from "../deferred-confirmations.js";
1
2
  import type { UserContent } from "ai";
2
3
  import type { ChannelSource } from "eve/channels";
3
4
  import type { SessionAuthContext } from "eve/context";
@@ -61,6 +62,7 @@ export declare class InboundRequest {
61
62
  waitUntil: ((task: Promise<unknown>) => void) | undefined;
62
63
  /** Continues the exact pending question's session, including under thread scope. */
63
64
  address: string | undefined;
65
+ confirmationEvent: ((record: DeferredConfirmation, result: Readonly<Record<string, unknown>>, auth: SessionAuthContext | null) => Promise<unknown>) | undefined;
64
66
  respond: ((address: string, requestId: string, decision: "approve" | "cancel", principal: SessionAuthContext, text?: string) => Promise<unknown>) | undefined;
65
67
  constructor(
66
68
  /** The agent's description from the compiled manifest, for the routing decision. */
@@ -4,5 +4,5 @@ import { createRequire as __eveCreateRequire } from "node:module";
4
4
  const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
- import { f as InboundRequest, h as withReplacedText, m as runInboundRequest, p as currentInboundRequest } from "../../../_chunks/customer-text-DjszxgTo.mjs";
7
+ import { i as withReplacedText, n as currentInboundRequest, r as runInboundRequest, t as InboundRequest } from "../../../_chunks/inbound-request-BwBvXk0A.mjs";
8
8
  export { InboundRequest, currentInboundRequest, runInboundRequest, withReplacedText };
@@ -5,16 +5,16 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { conversationLanguage, telegramPrincipal } from "./principal.mjs";
8
- import { l as reportTelegramError } from "../../../_chunks/customer-text-DjszxgTo.mjs";
9
- import { MAXIMUM_NATIVE_IMAGE_BYTES } from "./images.mjs";
10
- import { MAXIMUM_ROUTING_ATTACHMENT_BYTES, describeRoutingAttachment, routingAttachmentDeadline } from "./attachment-description.mjs";
11
- import { attachmentUrl } from "../attachments.mjs";
8
+ import { p as reportTelegramError } from "../../../_chunks/inbound-request-BwBvXk0A.mjs";
9
+ import { formatAddress, messageAddress, scopedAddress } from "./sessions.mjs";
12
10
  import { contentShape, messageContent } from "./content.mjs";
13
11
  import { messageAudience } from "./audience.mjs";
14
12
  import { attachmentKind, messageMetadata, observedChatKey, observedContext } from "./observed.mjs";
13
+ import { MAXIMUM_NATIVE_IMAGE_BYTES } from "./images.mjs";
14
+ import { MAXIMUM_ROUTING_ATTACHMENT_BYTES, describeRoutingAttachment, routingAttachmentDeadline } from "./attachment-description.mjs";
15
+ import { attachmentUrl } from "../attachments.mjs";
15
16
  import { addressingReason, conversationContext, routeMessage } from "./router.mjs";
16
17
  import { answerMarker } from "../questions.mjs";
17
- import { formatAddress, messageAddress, scopedAddress } from "./sessions.mjs";
18
18
  import { questionAddress, questionFromHistory } from "./confirmations.mjs";
19
19
  import { routingContext } from "./routing-context.mjs";
20
20
  import { recordGateDecision, recordInboundContent, recordRouteDecision } from "./conversations.mjs";
@@ -5,7 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { DEFAULT_IMAGE_DESCRIPTION_MODEL } from "../../config.mjs";
8
- import { l as reportTelegramError } from "../../../_chunks/customer-text-DjszxgTo.mjs";
8
+ import { p as reportTelegramError } from "../../../_chunks/inbound-request-BwBvXk0A.mjs";
9
9
  function planMedia(capabilities, descriptionModel = null) {
10
10
  if (typeof descriptionModel === "string") return {
11
11
  images: "describe",
@@ -69,6 +69,7 @@ export interface PendingConfirmation {
69
69
  }
70
70
  /** Internal correlation stays in the archive and never enters classifier state. */
71
71
  export interface ConfirmationPrompt extends PendingConfirmation {
72
+ readonly mode?: "deferred";
72
73
  readonly requestId: string;
73
74
  readonly sessionId?: string;
74
75
  readonly address?: string;
@@ -19,6 +19,18 @@ function archivedMetadata(event) {
19
19
  }
20
20
  function archivedRoutingMessages(events) {
21
21
  const messages = collapseConversationEvents(events).filter((event) => event.kind === "inbound" || event.kind === "outbound" && (event.status === "sent" || event.status === "completed"));
22
+ const deferredStates = new Map();
23
+ for (const event of [...events].reverse()) {
24
+ if (event.kind !== "tool_result" || event.operationId == null) continue;
25
+ const state = event.reason === "done" ? "completed" : event.reason;
26
+ if ([
27
+ "approved",
28
+ "cancelled",
29
+ "expired",
30
+ "completed",
31
+ "failed"
32
+ ].includes(state ?? "")) deferredStates.set(event.operationId, state);
33
+ }
22
34
  const membership = new Map();
23
35
  for (const event of [...events].reverse()) {
24
36
  if (event.kind !== "decision" || event.senderId === null) continue;
@@ -28,6 +40,8 @@ function archivedRoutingMessages(events) {
28
40
  }
29
41
  return messages.slice(0, 30).reverse().map((event) => {
30
42
  const metadata = archivedMetadata(event);
43
+ const prompt = metadata.confirmation;
44
+ const decision = prompt?.mode === "deferred" ? deferredStates.get(prompt.requestId) : void 0;
31
45
  const parent = messages.find((candidate) => candidate.messageId !== null && candidate.messageId === event.replyToMessageId);
32
46
  const replyTo = metadata.replyTo ?? (event.replyToMessageId === null ? void 0 : {
33
47
  messageId: event.replyToMessageId,
@@ -60,7 +74,10 @@ function archivedRoutingMessages(events) {
60
74
  };
61
75
  return event.kind === "outbound" ? {
62
76
  ...common,
63
- ...metadata.confirmation === void 0 ? {} : { confirmation: metadata.confirmation },
77
+ ...prompt === void 0 ? {} : { confirmation: {
78
+ ...prompt,
79
+ ...decision === void 0 ? {} : { state: decision }
80
+ } },
64
81
  ...agentMessage({
65
82
  text,
66
83
  at: event.at,
@@ -103,7 +120,15 @@ async function routingContext(input) {
103
120
  return {
104
121
  recent: reconciled,
105
122
  pendingQuestions: [...pending.values()].slice(-30),
106
- pendingConfirmations: [...histories.values()].flatMap((history) => routingConfirmations(history, reconciled, input.at)).slice(-5)
123
+ pendingConfirmations: [...[...histories.values()].flatMap((history) => routingConfirmations(history, reconciled, input.at)), ...reconciled.flatMap((message) => {
124
+ const prompt = "role" in message ? message.confirmation : void 0;
125
+ return prompt?.mode === "deferred" && prompt.state === "pending" ? [{
126
+ state: prompt.state,
127
+ action: prompt.action,
128
+ requestedBy: prompt.requestedBy,
129
+ at: prompt.at
130
+ }] : [];
131
+ })].slice(-5)
107
132
  };
108
133
  };
109
134
  if (input.operations === void 0) return context(input.remembered);
@@ -5,10 +5,10 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import "./principal.mjs";
8
- import { a as customerMessage, l as reportTelegramError, n as currentLocale, t as TelegramDeliveryError } from "../../../_chunks/customer-text-DjszxgTo.mjs";
9
8
  import { confirmationLocale } from "./confirmation-text.mjs";
10
9
  import { splitTelegramText } from "./format.mjs";
11
10
  import { inTelegramSendQueue } from "./send-queue.mjs";
11
+ import { a as TelegramDeliveryError, l as customerMessage, o as currentLocale, p as reportTelegramError } from "../../../_chunks/inbound-request-BwBvXk0A.mjs";
12
12
  import { agentMessage, observedChatKey } from "./observed.mjs";
13
13
  const SILENT_MARKERS = new Set(["[silent]", "no_reply"]);
14
14
  function isSilentReply(text) {
@@ -5,8 +5,8 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { memberIdOf } from "./principal.mjs";
8
- import { i as customerCopy, l as reportTelegramError, n as currentLocale, s as failureText } from "../../../_chunks/customer-text-DjszxgTo.mjs";
9
8
  import { confirmationLocale } from "./confirmation-text.mjs";
9
+ import { c as customerCopy, d as failureText, o as currentLocale, p as reportTelegramError } from "../../../_chunks/inbound-request-BwBvXk0A.mjs";
10
10
  const telegramFailureEvents = {
11
11
  async "turn.cancelled"(event, channel, context) {
12
12
  const errorId = reportTelegramError("turn_cancelled", event, {
@@ -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 { l as reportTelegramError } from "../../../_chunks/customer-text-DjszxgTo.mjs";
7
+ import { p as reportTelegramError } from "../../../_chunks/inbound-request-BwBvXk0A.mjs";
8
8
  import "../connection-proxy.mjs";
9
9
  import { gateway, transcribe } from "ai";
10
10
  const LABELS = {
@@ -0,0 +1,6 @@
1
+ /** No model is invoked until an actual expiry outcome is delivered to its original chat. */
2
+ declare const _default: {
3
+ cron: string;
4
+ run({ to }: import("eve/schedules").ScheduleHandlerArgs): Promise<void>;
5
+ };
6
+ export default _default;
@@ -0,0 +1,33 @@
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 { channelsEnabled } from "../lib/environment.mjs";
8
+ import segmentstream from "../extension.mjs";
9
+ import telegram_default from "../channels/telegram.mjs";
10
+ import { expireScheduledConfirmations } from "../lib/telegram/confirmation-expiry.mjs";
11
+ import { defineSchedule } from "eve/schedules";
12
+ var confirmations_default = defineSchedule({
13
+ cron: "* * * * *",
14
+ async run({ to }) {
15
+ const settings = segmentstream.config.channels.telegram;
16
+ if (!channelsEnabled(process.env) || !settings || segmentstream.config.confirmations.mode !== "deferred") return;
17
+ await expireScheduledConfirmations(settings.connection, async (record, result) => {
18
+ await to(telegram_default, {
19
+ chatId: record.data.chatId,
20
+ messageThreadId: record.data.threadId ? Number(record.data.threadId) : void 0,
21
+ confirmationAddress: record.data.address,
22
+ confirmationSessionId: record.data.sessionId
23
+ }).send(JSON.stringify({
24
+ type: "confirmation_result",
25
+ ...result
26
+ }), {
27
+ auth: null,
28
+ turnPolicy: "queue"
29
+ });
30
+ }, {}, settings);
31
+ }
32
+ });
33
+ export { confirmations_default as default };
@@ -0,0 +1,4 @@
1
+ declare const _default: import("eve").DynamicSentinel<{
2
+ [k: string]: import("eve/tools").ToolDefinition<Record<string, unknown>, Record<string, unknown>>;
3
+ } | null>;
4
+ export default _default;
@@ -0,0 +1,30 @@
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 { memberIdOf } from "../lib/telegram/principal.mjs";
8
+ import segmentstream from "../extension.mjs";
9
+ import { ontologyTools } from "../lib/ontology-tools.mjs";
10
+ import { executeDeferredTool } from "../lib/deferred-confirmations.mjs";
11
+ import { defineDynamic, defineTool } from "eve/tools";
12
+ var ontology_default = defineDynamic({ events: { "turn.started": async (_event, context) => {
13
+ const memberId = memberIdOf(context.session.auth.current);
14
+ if (!segmentstream.config.ontology.connection || segmentstream.config.confirmations.mode !== "deferred" || memberId === void 0) return null;
15
+ return Object.fromEntries((await ontologyTools().list(memberId)).map((tool) => {
16
+ const action = String(tool.name);
17
+ const schema = tool.inputSchema;
18
+ const properties = Object.fromEntries(Object.entries(schema.properties ?? {}).filter(([key]) => key !== "_segmentstream"));
19
+ return [`ontology__${action}`, defineTool({
20
+ deferred: true,
21
+ description: String(tool.description ?? tool.title ?? action),
22
+ inputSchema: {
23
+ ...schema,
24
+ properties
25
+ },
26
+ execute: (input, ctx) => executeDeferredTool(memberId, action, input, ctx)
27
+ })];
28
+ }));
29
+ } } });
30
+ export { ontology_default as default };
@@ -6,4 +6,5 @@ export { default as attachments_promote } from "../extension/tools/attachments_p
6
6
  export { default as attachments_read } from "../extension/tools/attachments_read.js";
7
7
  export { default as attachments_send } from "../extension/tools/attachments_send.js";
8
8
  export { default as attachments_table } from "../extension/tools/attachments_table.js";
9
+ export { default as ontology } from "../extension/tools/ontology.js";
9
10
  export { default as questions_ask } from "../extension/tools/questions_ask.js";
@@ -10,5 +10,6 @@ import attachments_promote_default from "../extension/tools/attachments_promote.
10
10
  import attachments_read_default from "../extension/tools/attachments_read.mjs";
11
11
  import attachments_send_default from "../extension/tools/attachments_send.mjs";
12
12
  import attachments_table_default from "../extension/tools/attachments_table.mjs";
13
+ import ontology_default from "../extension/tools/ontology.mjs";
13
14
  import questions_ask_default from "../extension/tools/questions_ask.mjs";
14
- export { attachments_discard_default as attachments_discard, attachments_list_default as attachments_list, attachments_promote_default as attachments_promote, attachments_read_default as attachments_read, attachments_send_default as attachments_send, attachments_table_default as attachments_table, questions_ask_default as questions_ask };
15
+ export { attachments_discard_default as attachments_discard, attachments_list_default as attachments_list, attachments_promote_default as attachments_promote, attachments_read_default as attachments_read, attachments_send_default as attachments_send, attachments_table_default as attachments_table, ontology_default as ontology, questions_ask_default as questions_ask };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@segmentstream/agent",
3
- "version": "0.6.14",
3
+ "version": "0.7.1",
4
4
  "description": "Mount a SegmentStream workspace's agent on eve: the ontology as tools, acting as the member who writes, and Telegram through the workspace's connection.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "SegmentStream",
@@ -62,7 +62,7 @@
62
62
  },
63
63
  "dependencies": {
64
64
  "@chat-adapter/telegram": "4.41.0",
65
- "@segmentstream-cloud/ontology": "0.31.5",
65
+ "@segmentstream-cloud/ontology": "0.32.1",
66
66
  "@vercel/oidc": "^3.8.4",
67
67
  "ai": "^7.0.128",
68
68
  "csv-parse": "^7.0.3",
@@ -6,8 +6,8 @@ __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { memberIdOf } from "../extension/lib/telegram/principal.mjs";
8
8
  import { conversationEvent, conversations, summarizeArguments } from "../extension/lib/conversations.mjs";
9
- import { completionKind } from "../extension/lib/telegram/silent.mjs";
10
9
  import { parseAddress } from "../extension/lib/telegram/sessions.mjs";
10
+ import { completionKind } from "../extension/lib/telegram/silent.mjs";
11
11
  const CONTENT_CAPS = {
12
12
  args: 16384,
13
13
  result: 32768,