@segmentstream/agent 0.6.14 → 0.7.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.
- package/dist/_chunks/{customer-text-DjszxgTo.mjs → inbound-request-BwBvXk0A.mjs} +110 -109
- package/dist/extension/_manifest.json +2 -0
- package/dist/extension/channels/telegram.mjs +1 -0
- package/dist/extension/config.d.ts +7 -0
- package/dist/extension/config.mjs +13 -1
- package/dist/extension/connections/ontology.mjs +1 -1
- package/dist/extension/hooks/conversations.mjs +1 -1
- package/dist/extension/instructions/conversations.mjs +1 -1
- package/dist/extension/instructions.md +17 -0
- package/dist/extension/lib/confirmation-description.d.ts +5 -0
- package/dist/extension/lib/confirmation-description.mjs +7 -1
- package/dist/extension/lib/confirmation-outcome.d.ts +19 -0
- package/dist/extension/lib/confirmation-outcome.mjs +34 -0
- package/dist/extension/lib/confirmations.mjs +1 -1
- package/dist/extension/lib/conversation-runtime.mjs +1 -1
- package/dist/extension/lib/customer-text-safety.mjs +2 -1
- package/dist/extension/lib/deferred-confirmations.d.ts +39 -0
- package/dist/extension/lib/deferred-confirmations.mjs +149 -0
- package/dist/extension/lib/ontology-tools.d.ts +2 -0
- package/dist/extension/lib/ontology-tools.mjs +6 -1
- package/dist/extension/lib/telegram/adapter.d.ts +1 -0
- package/dist/extension/lib/telegram/adapter.mjs +72 -6
- package/dist/extension/lib/telegram/attachment-description.mjs +1 -1
- package/dist/extension/lib/telegram/attachments-send.mjs +3 -3
- package/dist/extension/lib/telegram/confirmation-expiry.d.ts +4 -0
- package/dist/extension/lib/telegram/confirmation-expiry.mjs +50 -0
- package/dist/extension/lib/telegram/confirmations.d.ts +2 -1
- package/dist/extension/lib/telegram/confirmations.mjs +13 -5
- package/dist/extension/lib/telegram/conversations.mjs +1 -1
- package/dist/extension/lib/telegram/customer-text.mjs +1 -1
- package/dist/extension/lib/telegram/deferred-confirmations.d.ts +26 -0
- package/dist/extension/lib/telegram/deferred-confirmations.mjs +282 -0
- package/dist/extension/lib/telegram/files.mjs +1 -1
- package/dist/extension/lib/telegram/images.mjs +1 -1
- package/dist/extension/lib/telegram/inbound-request.d.ts +2 -0
- package/dist/extension/lib/telegram/inbound-request.mjs +1 -1
- package/dist/extension/lib/telegram/inbound.mjs +5 -5
- package/dist/extension/lib/telegram/media-policy.mjs +1 -1
- package/dist/extension/lib/telegram/observed.d.ts +1 -0
- package/dist/extension/lib/telegram/routing-context.mjs +27 -2
- package/dist/extension/lib/telegram/silent.mjs +1 -1
- package/dist/extension/lib/telegram/turn-errors.mjs +1 -1
- package/dist/extension/lib/telegram/voice.mjs +1 -1
- package/dist/extension/schedules/confirmations.d.ts +6 -0
- package/dist/extension/schedules/confirmations.mjs +33 -0
- package/dist/extension/tools/ontology.d.ts +4 -0
- package/dist/extension/tools/ontology.mjs +30 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.mjs +2 -1
- package/package.json +2 -2
- package/dist/_chunks/{conversation-runtime-BINKO_6b.mjs → conversation-runtime-ovs8IhXJ.mjs} +1 -1
|
@@ -0,0 +1,149 @@
|
|
|
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 { appOrigin } from "./environment.mjs";
|
|
8
|
+
import { memberIdOf } from "./telegram/principal.mjs";
|
|
9
|
+
import { humanText, object } from "./confirmation-description.mjs";
|
|
10
|
+
import { chatPostDigest, chatPostOperationId, isChatPost } from "./chat-post-confirmation.mjs";
|
|
11
|
+
import { actionApproval, inputHash } from "./confirmations.mjs";
|
|
12
|
+
import { redactDiagnostic } from "./diagnostics.mjs";
|
|
13
|
+
import { runtimeToken } from "./runtime-token.mjs";
|
|
14
|
+
import { ontologyTools } from "./ontology-tools.mjs";
|
|
15
|
+
import { createHash } from "node:crypto";
|
|
16
|
+
const deferredKey = (id) => createHash("sha256").update(id).digest("hex").slice(0, 20);
|
|
17
|
+
function deferredConfirmations(options = {}) {
|
|
18
|
+
return { async command(command) {
|
|
19
|
+
const credential = await (options.token ?? runtimeToken)();
|
|
20
|
+
try {
|
|
21
|
+
const response = await (options.fetch ?? fetch)(`${appOrigin(options.environment ?? process.env)}/api/ontology/agent/confirmations`, {
|
|
22
|
+
method: "POST",
|
|
23
|
+
redirect: "error",
|
|
24
|
+
signal: AbortSignal.timeout(15e3),
|
|
25
|
+
headers: {
|
|
26
|
+
authorization: `Bearer ${credential}`,
|
|
27
|
+
"content-type": "application/json"
|
|
28
|
+
},
|
|
29
|
+
body: JSON.stringify(command)
|
|
30
|
+
});
|
|
31
|
+
if (!response.ok) throw new Error(`Confirmation ledger unavailable (HTTP ${response.status}).`, { cause: await response.text() });
|
|
32
|
+
return (await response.json()).result;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
throw new Error("Confirmation ledger request failed.", { cause: redactDiagnostic(error, new Set([credential])) });
|
|
35
|
+
}
|
|
36
|
+
} };
|
|
37
|
+
}
|
|
38
|
+
async function executeDeferredTool(memberId, action, input, ctx, options = {}) {
|
|
39
|
+
if (memberIdOf(ctx.session.auth.current) !== memberId) throw new Error("The ontology tool belongs to another member.");
|
|
40
|
+
if (Object.hasOwn(input, "_segmentstream")) throw new Error("Runtime call metadata is application-owned.");
|
|
41
|
+
const principal = ctx.session.auth.current;
|
|
42
|
+
const attributes = principal?.attributes;
|
|
43
|
+
const id = chatPostOperationId(ctx.session.id, ctx.callId);
|
|
44
|
+
const key = deferredKey(id);
|
|
45
|
+
const hash = inputHash(input);
|
|
46
|
+
const store = deferredConfirmations(options);
|
|
47
|
+
const existing = await store.command({
|
|
48
|
+
operation: "read",
|
|
49
|
+
key
|
|
50
|
+
});
|
|
51
|
+
const boundResult = (record) => {
|
|
52
|
+
if (record.id !== id || record.key !== key || record.memberId !== memberId || record.inputHash !== hash || inputHash(record.data.input) !== hash || record.data.action !== action || record.data.sessionId !== ctx.session.id || record.data.callId !== ctx.callId || principal?.authenticator !== "segmentstream-telegram" || !principal.subject || typeof attributes?.chatId !== "string" || record.data.requester !== principal.subject || record.data.chatId !== attributes?.chatId || record.data.threadId !== String(attributes?.messageThreadId ?? "")) throw new Error("The confirmation call was replayed with another identity, action or input.");
|
|
53
|
+
return {
|
|
54
|
+
status: record.status === "pending" ? "awaiting_confirmation" : record.status,
|
|
55
|
+
confirmation: record.id
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
if (existing !== null) return boundResult(existing);
|
|
59
|
+
const tools = ontologyTools(options);
|
|
60
|
+
let saved = {};
|
|
61
|
+
const result = await actionApproval({
|
|
62
|
+
...tools,
|
|
63
|
+
state: {
|
|
64
|
+
get: () => saved,
|
|
65
|
+
update: (update) => {
|
|
66
|
+
saved = update(saved);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}).request({
|
|
70
|
+
...ctx,
|
|
71
|
+
callId: ctx.callId,
|
|
72
|
+
toolName: action,
|
|
73
|
+
toolInput: input,
|
|
74
|
+
approvedTools: new Set()
|
|
75
|
+
});
|
|
76
|
+
if (typeof result === "object" && result.type === "denied") throw new Error(result.reason);
|
|
77
|
+
const confirmation = saved[ctx.callId];
|
|
78
|
+
if (confirmation === void 0) {
|
|
79
|
+
const response = await tools.rpc(memberId, "tools/call", {
|
|
80
|
+
name: action,
|
|
81
|
+
arguments: input
|
|
82
|
+
});
|
|
83
|
+
return object(response.structuredContent ?? response);
|
|
84
|
+
}
|
|
85
|
+
if (principal?.authenticator !== "segmentstream-telegram" || !principal.subject || typeof attributes?.chatId !== "string") throw new Error("Deferred confirmation requires an authenticated Telegram requester.");
|
|
86
|
+
const record = await store.command({
|
|
87
|
+
operation: "create",
|
|
88
|
+
record: {
|
|
89
|
+
id,
|
|
90
|
+
key,
|
|
91
|
+
memberId,
|
|
92
|
+
inputHash: hash,
|
|
93
|
+
expiresAt: confirmation.expiresAt,
|
|
94
|
+
status: "pending",
|
|
95
|
+
data: {
|
|
96
|
+
action,
|
|
97
|
+
input,
|
|
98
|
+
confirmation,
|
|
99
|
+
sessionId: ctx.session.id,
|
|
100
|
+
callId: ctx.callId,
|
|
101
|
+
chatId: attributes.chatId,
|
|
102
|
+
threadId: String(attributes.messageThreadId ?? ""),
|
|
103
|
+
requester: principal.subject,
|
|
104
|
+
...humanText(attributes.memberName) === void 0 ? {} : { requesterName: humanText(attributes.memberName) }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
if (record === null) throw new Error("The confirmation call was replayed with different input.");
|
|
109
|
+
return boundResult(record);
|
|
110
|
+
}
|
|
111
|
+
async function runDeferredAction(record, options) {
|
|
112
|
+
if (record.inputHash !== inputHash(record.data.input)) throw new Error("The confirmed input changed.");
|
|
113
|
+
const tools = ontologyTools(options);
|
|
114
|
+
await tools.labels(record.memberId, record.data.action);
|
|
115
|
+
const preview = record.data.confirmation.description?.chatPost;
|
|
116
|
+
if (isChatPost(record.data.action)) {
|
|
117
|
+
const input = Object.fromEntries(Object.entries(record.data.input).filter(([key]) => key !== "_confirmation"));
|
|
118
|
+
const current = (await tools.describe(record.memberId, record.data.action, input)).chatPost;
|
|
119
|
+
if (!preview || !current || chatPostDigest(preview) !== chatPostDigest(current)) throw new Error("The confirmed chat destination or attribution changed.");
|
|
120
|
+
}
|
|
121
|
+
const envelope = {
|
|
122
|
+
version: 1,
|
|
123
|
+
session_id: record.data.sessionId,
|
|
124
|
+
call_id: record.data.callId,
|
|
125
|
+
operation_id: record.id,
|
|
126
|
+
...preview ? {
|
|
127
|
+
source: {
|
|
128
|
+
provider: "telegram",
|
|
129
|
+
chat_id: record.data.chatId,
|
|
130
|
+
thread_id: record.data.threadId
|
|
131
|
+
},
|
|
132
|
+
expected: {
|
|
133
|
+
chat_id: preview.chatId,
|
|
134
|
+
digest: chatPostDigest(preview)
|
|
135
|
+
}
|
|
136
|
+
} : {}
|
|
137
|
+
};
|
|
138
|
+
const result = await tools.rpc(record.memberId, "tools/call", {
|
|
139
|
+
name: record.data.action.split("__").at(-1),
|
|
140
|
+
arguments: {
|
|
141
|
+
...record.data.input,
|
|
142
|
+
_segmentstream: envelope
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
const output = result.structuredContent;
|
|
146
|
+
if (result.isError === true || (output?.validation)?.ok === false || preview !== void 0 && output?.status !== "sent") throw new Error("The confirmed action did not complete; its outcome may be uncertain.", { cause: result });
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
export { deferredConfirmations, deferredKey, executeDeferredTool, runDeferredAction };
|
|
@@ -7,6 +7,8 @@ export declare function ontologyTools(options?: {
|
|
|
7
7
|
readonly token?: RuntimeToken;
|
|
8
8
|
readonly fetch?: typeof fetch;
|
|
9
9
|
}): {
|
|
10
|
+
rpc: (memberId: string, method: string, params: Record<string, unknown>, signal?: AbortSignal) => Promise<Record<string, unknown>>;
|
|
11
|
+
list(memberId: string): Promise<Record<string, unknown>[]>;
|
|
10
12
|
supportsChatPosting(memberId: string): Promise<boolean>;
|
|
11
13
|
labels(memberId: string, action: string): Promise<ActionAnnotations>;
|
|
12
14
|
describe(memberId: string, action: string, input: Readonly<Record<string, unknown>>): Promise<import("./confirmation-description.js").ConfirmationDescription>;
|
|
@@ -5,9 +5,9 @@ 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 { redactDiagnostic } from "./diagnostics.mjs";
|
|
9
8
|
import { describeConfirmation, object } from "./confirmation-description.mjs";
|
|
10
9
|
import { describeChatPost, isChatPost, supportsChatPost } from "./chat-post-confirmation.mjs";
|
|
10
|
+
import { redactDiagnostic } from "./diagnostics.mjs";
|
|
11
11
|
import { runtimeToken } from "./runtime-token.mjs";
|
|
12
12
|
function ontologyTools(options = {}) {
|
|
13
13
|
async function rpc(memberId, method, params, signal) {
|
|
@@ -54,6 +54,11 @@ function ontologyTools(options = {}) {
|
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
return {
|
|
57
|
+
rpc,
|
|
58
|
+
async list(memberId) {
|
|
59
|
+
const result = await rpc(memberId, "tools/list", {});
|
|
60
|
+
return Array.isArray(result.tools) ? result.tools.map(object) : [];
|
|
61
|
+
},
|
|
57
62
|
async supportsChatPosting(memberId) {
|
|
58
63
|
const result = await rpc(memberId, "tools/list", {});
|
|
59
64
|
return Array.isArray(result.tools) && result.tools.map(object).some(supportsChatPost);
|
|
@@ -30,6 +30,7 @@ export interface TelegramAdapterOptions {
|
|
|
30
30
|
};
|
|
31
31
|
/** The mounted `sessions`. */
|
|
32
32
|
readonly sessions: () => SessionSettings;
|
|
33
|
+
readonly confirmationMode?: () => "deferred" | "held";
|
|
33
34
|
readonly decide: DecideFunction;
|
|
34
35
|
readonly environment?: Environment;
|
|
35
36
|
readonly token?: RuntimeToken;
|
|
@@ -8,20 +8,21 @@ import "../../config.mjs";
|
|
|
8
8
|
import { appOrigin, channelsEnabled, proxyEnvironment, runtimeIdentityEnvironment, webhookSecret } from "../environment.mjs";
|
|
9
9
|
import { conversationLanguage, telegramPrincipal } from "./principal.mjs";
|
|
10
10
|
import { chooseModel } from "../../model.mjs";
|
|
11
|
-
import { d as withDeliveryLocale, f as InboundRequest, h as withReplacedText, l as reportTelegramError, m as runInboundRequest, p as currentInboundRequest, s as failureText, u as safeTelegramFetch } from "../../../_chunks/customer-text-DjszxgTo.mjs";
|
|
12
11
|
import { confirmationLocale } from "./confirmation-text.mjs";
|
|
12
|
+
import { runtimeToken } from "../runtime-token.mjs";
|
|
13
|
+
import { conversations } from "../conversations.mjs";
|
|
13
14
|
import { telegramSendScope } from "./send-queue.mjs";
|
|
15
|
+
import { d as failureText, h as withDeliveryLocale, i as withReplacedText, m as safeTelegramFetch, n as currentInboundRequest, p as reportTelegramError, r as runInboundRequest, t as InboundRequest } from "../../../_chunks/inbound-request-BwBvXk0A.mjs";
|
|
16
|
+
import { scopedAddress, sessionPolicy } from "./sessions.mjs";
|
|
17
|
+
import { observedMessages } from "./observed.mjs";
|
|
18
|
+
import { expireDeferredCards, handleDeferredCallback, isDeferredCallback } from "./deferred-confirmations.mjs";
|
|
14
19
|
import { gatewayImageDescriber } from "./images.mjs";
|
|
15
20
|
import { gatewayAttachmentDescriber } from "./attachment-description.mjs";
|
|
16
21
|
import { MAXIMUM_DOWNLOAD_BYTES, connectionProxy } from "../connection-proxy.mjs";
|
|
17
|
-
import { runtimeToken } from "../runtime-token.mjs";
|
|
18
22
|
import { agentAttachments, parseAttachmentUrl } from "../attachments.mjs";
|
|
19
23
|
import { agentIdentities } from "../identities.mjs";
|
|
20
|
-
import { conversations } from "../conversations.mjs";
|
|
21
|
-
import { observedMessages } from "./observed.mjs";
|
|
22
24
|
import { decisionRouter } from "./router.mjs";
|
|
23
25
|
import { replyDelivery } from "./silent.mjs";
|
|
24
|
-
import { scopedAddress, sessionPolicy } from "./sessions.mjs";
|
|
25
26
|
import { handleConfirmationCallback, telegramConfirmationEvents } from "./confirmations.mjs";
|
|
26
27
|
import { captureRouteDecider, captureTelegramFetch, conversationIdentities, recordGateDecision, telegramConversationCapture } from "./conversations.mjs";
|
|
27
28
|
import { runtimeTokenVerifier } from "../runtime-identity.mjs";
|
|
@@ -142,6 +143,15 @@ function segmentstreamTelegram(options) {
|
|
|
142
143
|
},
|
|
143
144
|
uploadPolicy: { maxBytes: MAXIMUM_DOWNLOAD_BYTES },
|
|
144
145
|
onMessage: async (context, message) => {
|
|
146
|
+
if (options.confirmationMode?.() === "deferred") try {
|
|
147
|
+
await expireDeferredCards(context, {
|
|
148
|
+
environment,
|
|
149
|
+
token,
|
|
150
|
+
...options.fetch ? { fetch: options.fetch } : {}
|
|
151
|
+
}, archive, observed);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
reportTelegramError("deferred_expiry_failed", error);
|
|
154
|
+
}
|
|
145
155
|
const inbound = currentInboundRequest();
|
|
146
156
|
if (inbound !== void 0) {
|
|
147
157
|
inbound.locale = conversationLanguage([message.text || message.caption]) ?? message.from?.languageCode ?? "en";
|
|
@@ -236,6 +246,21 @@ function segmentstreamTelegram(options) {
|
|
|
236
246
|
},
|
|
237
247
|
onCallbackQuery: async (context, query) => {
|
|
238
248
|
try {
|
|
249
|
+
if (isDeferredCallback(query.data)) {
|
|
250
|
+
await handleDeferredCallback({
|
|
251
|
+
context,
|
|
252
|
+
query,
|
|
253
|
+
identities: pairing(),
|
|
254
|
+
archive,
|
|
255
|
+
options: {
|
|
256
|
+
environment,
|
|
257
|
+
token,
|
|
258
|
+
...options.fetch ? { fetch: options.fetch } : {}
|
|
259
|
+
},
|
|
260
|
+
observed
|
|
261
|
+
});
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
239
264
|
await handleConfirmationCallback({
|
|
240
265
|
context,
|
|
241
266
|
query,
|
|
@@ -253,7 +278,11 @@ function segmentstreamTelegram(options) {
|
|
|
253
278
|
},
|
|
254
279
|
events: {
|
|
255
280
|
...telegramFailureEvents,
|
|
256
|
-
...telegramConfirmationEvents(archive, observed
|
|
281
|
+
...telegramConfirmationEvents(archive, observed, {
|
|
282
|
+
environment,
|
|
283
|
+
token,
|
|
284
|
+
...options.fetch ? { fetch: options.fetch } : {}
|
|
285
|
+
}),
|
|
257
286
|
"message.completed": replyDelivery(observed, () => telegramSendScope(environment, settings().connection))
|
|
258
287
|
}
|
|
259
288
|
});
|
|
@@ -276,6 +305,25 @@ function segmentstreamTelegram(options) {
|
|
|
276
305
|
}
|
|
277
306
|
return {
|
|
278
307
|
...channel,
|
|
308
|
+
async receive(input, operations) {
|
|
309
|
+
const target = input.target;
|
|
310
|
+
if (target.confirmationSessionId !== void 0) {
|
|
311
|
+
const address = target.confirmationAddress;
|
|
312
|
+
if (address === void 0 || (await operations.resolveSession(address))?.id !== target.confirmationSessionId) throw new Error("The confirmation session was replaced.");
|
|
313
|
+
return operations.from(address).send(input.message, {
|
|
314
|
+
auth: null,
|
|
315
|
+
turnPolicy: "queue",
|
|
316
|
+
state: {
|
|
317
|
+
chatId: String(target.chatId),
|
|
318
|
+
chatType: String(target.chatId).startsWith("-") ? "supergroup" : "private",
|
|
319
|
+
conversationId: null,
|
|
320
|
+
messageThreadId: target.messageThreadId ?? null
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
if (channel.receive === void 0) throw new Error("The Telegram channel cannot receive events.");
|
|
325
|
+
return channel.receive(input, operations);
|
|
326
|
+
},
|
|
279
327
|
...adapter === void 0 ? {} : { adapter: {
|
|
280
328
|
...adapter,
|
|
281
329
|
fetchFile,
|
|
@@ -321,6 +369,24 @@ function gatedRoute(input) {
|
|
|
321
369
|
resolveSession: args.resolveSession,
|
|
322
370
|
from: args.from
|
|
323
371
|
};
|
|
372
|
+
inbound.confirmationEvent = async (record, result, auth) => {
|
|
373
|
+
const address = record.data.address;
|
|
374
|
+
if (address === void 0) throw new Error("Confirmation has no owning session address.");
|
|
375
|
+
if ((await args.resolveSession(address))?.id !== record.data.sessionId) throw new Error("The confirmation session was replaced.");
|
|
376
|
+
return args.from(address).send(JSON.stringify({
|
|
377
|
+
type: "confirmation_result",
|
|
378
|
+
...result
|
|
379
|
+
}), {
|
|
380
|
+
auth,
|
|
381
|
+
turnPolicy: "queue",
|
|
382
|
+
state: {
|
|
383
|
+
chatId: record.data.chatId,
|
|
384
|
+
chatType: record.data.chatId.startsWith("-") ? "supergroup" : "private",
|
|
385
|
+
messageThreadId: record.data.threadId ? Number(record.data.threadId) : null,
|
|
386
|
+
conversationId: null
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
};
|
|
324
390
|
inbound.respond = (address, requestId, decision, auth, text) => args.from(scoped(address)).respond([{
|
|
325
391
|
requestId,
|
|
326
392
|
optionId: decision,
|
|
@@ -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 {
|
|
7
|
+
import { p as reportTelegramError } from "../../../_chunks/inbound-request-BwBvXk0A.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";
|
|
@@ -6,12 +6,12 @@ __eveDirname(__filename);
|
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { appOrigin, channelsEnabled, proxyEnvironment } from "../environment.mjs";
|
|
8
8
|
import "./principal.mjs";
|
|
9
|
-
import { redactDiagnostic } from "../diagnostics.mjs";
|
|
10
|
-
import { a as customerMessage, c as formattingRefused, i as customerCopy, l as reportTelegramError } from "../../../_chunks/customer-text-DjszxgTo.mjs";
|
|
11
9
|
import { confirmationLocale } from "./confirmation-text.mjs";
|
|
10
|
+
import { redactDiagnostic } from "../diagnostics.mjs";
|
|
11
|
+
import { runtimeToken } from "../runtime-token.mjs";
|
|
12
12
|
import { inTelegramSendQueue, telegramSendScope } from "./send-queue.mjs";
|
|
13
|
+
import { c as customerCopy, f as formattingRefused, l as customerMessage, p as reportTelegramError } from "../../../_chunks/inbound-request-BwBvXk0A.mjs";
|
|
13
14
|
import { MAXIMUM_PROXY_BODY_BYTES, connectionProxy } from "../connection-proxy.mjs";
|
|
14
|
-
import { runtimeToken } from "../runtime-token.mjs";
|
|
15
15
|
import { ACT_AS_HEADER } from "../ontology-connection.mjs";
|
|
16
16
|
import { AttachmentError, CONVERSATION_HEADER, SESSION_HEADER } from "../attachments.mjs";
|
|
17
17
|
import { TELEGRAM_PLACEHOLDER_TOKEN, TELEGRAM_PROXY_ORIGIN, telegramProxyFetch } from "./proxy-fetch.mjs";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { TelegramSettings } from "../../config.js";
|
|
2
|
+
import { type DeferredConfirmation, type DeferredOptions } from "../deferred-confirmations.js";
|
|
3
|
+
/** A bounded sweep; concurrent webhooks/schedules compete for the same atomic expiry claim. */
|
|
4
|
+
export declare function expireScheduledConfirmations(connection: string, notify: (record: DeferredConfirmation, result: Readonly<Record<string, unknown>>) => Promise<unknown>, options?: DeferredOptions, settings?: TelegramSettings): Promise<void>;
|
|
@@ -0,0 +1,50 @@
|
|
|
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 { proxyEnvironment } from "../environment.mjs";
|
|
8
|
+
import { runtimeToken } from "../runtime-token.mjs";
|
|
9
|
+
import { deferredConfirmations } from "../deferred-confirmations.mjs";
|
|
10
|
+
import { conversations } from "../conversations.mjs";
|
|
11
|
+
import { telegramSendScope } from "./send-queue.mjs";
|
|
12
|
+
import { m as safeTelegramFetch, p as reportTelegramError } from "../../../_chunks/inbound-request-BwBvXk0A.mjs";
|
|
13
|
+
import { expireDeferredRecord } from "./deferred-confirmations.mjs";
|
|
14
|
+
import { connectionProxy } from "../connection-proxy.mjs";
|
|
15
|
+
import { TELEGRAM_PLACEHOLDER_TOKEN, TELEGRAM_PROXY_ORIGIN, telegramProxyFetch } from "./proxy-fetch.mjs";
|
|
16
|
+
import { telegramPreviewEnvironment } from "@segmentstream-cloud/ontology/telegram";
|
|
17
|
+
import { editTelegramMessageText } from "eve/channels/telegram";
|
|
18
|
+
async function expireScheduledConfirmations(connection, notify, options = {}, settings) {
|
|
19
|
+
const environment = options.environment ?? process.env;
|
|
20
|
+
const token = options.token ?? runtimeToken;
|
|
21
|
+
const archive = conversations({
|
|
22
|
+
environment,
|
|
23
|
+
token,
|
|
24
|
+
fetch: options.fetch
|
|
25
|
+
});
|
|
26
|
+
const scope = telegramSendScope(environment, connection);
|
|
27
|
+
const proxy = connectionProxy({
|
|
28
|
+
environment: proxyEnvironment(environment),
|
|
29
|
+
connection,
|
|
30
|
+
token,
|
|
31
|
+
fetch: options.fetch
|
|
32
|
+
});
|
|
33
|
+
const transport = safeTelegramFetch(telegramProxyFetch(proxy, scope), scope, {
|
|
34
|
+
settings,
|
|
35
|
+
ownOrigins: telegramPreviewEnvironment(environment).appOrigins
|
|
36
|
+
});
|
|
37
|
+
const records = await deferredConfirmations(options).command({ operation: "expired" });
|
|
38
|
+
for (const record of records) try {
|
|
39
|
+
await expireDeferredRecord(record, { telegram: { editMessageText: (input) => editTelegramMessageText({
|
|
40
|
+
apiBaseUrl: TELEGRAM_PROXY_ORIGIN,
|
|
41
|
+
credentials: { botToken: TELEGRAM_PLACEHOLDER_TOKEN },
|
|
42
|
+
fetch: transport,
|
|
43
|
+
chatId: record.data.chatId,
|
|
44
|
+
...input
|
|
45
|
+
}) } }, options, archive, notify);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
reportTelegramError("deferred_expiry_failed", error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export { expireScheduledConfirmations };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DeferredOptions } from "../deferred-confirmations.js";
|
|
1
2
|
import type { ChannelEvents } from "eve/channels";
|
|
2
3
|
import type { TelegramCallbackQuery, TelegramChannelEvents, TelegramContext, TelegramEventContext } from "eve/channels/telegram";
|
|
3
4
|
import { type ObservedMessages, type PendingConfirmation, type RecentMessage } from "./observed.js";
|
|
@@ -23,5 +24,5 @@ export declare function handleConfirmationCallback(input: {
|
|
|
23
24
|
readonly scope: "chat" | "thread";
|
|
24
25
|
readonly now?: number;
|
|
25
26
|
}): Promise<void>;
|
|
26
|
-
export declare function telegramConfirmationEvents(archive?: Conversations, observed?: ObservedMessages): TelegramChannelEvents & Pick<ChannelEvents<TelegramEventContext>, "context.cleared">;
|
|
27
|
+
export declare function telegramConfirmationEvents(archive?: Conversations, observed?: ObservedMessages, deferredOptions?: DeferredOptions): TelegramChannelEvents & Pick<ChannelEvents<TelegramEventContext>, "context.cleared">;
|
|
27
28
|
export declare const confirmationEvents: TelegramChannelEvents & Pick<ChannelEvents<TelegramEventContext>, "context.cleared">;
|
|
@@ -5,16 +5,17 @@ const __filename = __eveFileURLToPath(import.meta.url);
|
|
|
5
5
|
__eveDirname(__filename);
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { memberIdOf, telegramPrincipal } from "./principal.mjs";
|
|
8
|
-
import { l as reportTelegramError, p as currentInboundRequest } from "../../../_chunks/customer-text-DjszxgTo.mjs";
|
|
9
8
|
import { humanText } from "../confirmation-description.mjs";
|
|
10
9
|
import { renderConfirmation, withConfirmationText } from "./confirmation-text.mjs";
|
|
11
|
-
import { chatPostCard, chatPostCardStatus, chatPostReceipt, withChatPostCard } from "./chat-post-card.mjs";
|
|
12
10
|
import { chatPostOperationId, isChatPost } from "../chat-post-confirmation.mjs";
|
|
11
|
+
import { chatPostCard, chatPostCardStatus, chatPostReceipt, withChatPostCard } from "./chat-post-card.mjs";
|
|
13
12
|
import { confirmations } from "../confirmations.mjs";
|
|
14
13
|
import { conversationEvent } from "../conversations.mjs";
|
|
14
|
+
import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-BwBvXk0A.mjs";
|
|
15
|
+
import { formatAddress, scopedAddress } from "./sessions.mjs";
|
|
15
16
|
import { agentMessage, observedChatKey, withConfirmationPrompt } from "./observed.mjs";
|
|
17
|
+
import { postDeferredCard } from "./deferred-confirmations.mjs";
|
|
16
18
|
import { answerMarker, isPendingQuestion, pendingQuestions, questionKey } from "../questions.mjs";
|
|
17
|
-
import { formatAddress, scopedAddress } from "./sessions.mjs";
|
|
18
19
|
import { telegramTextLinks } from "@segmentstream-cloud/ontology/telegram";
|
|
19
20
|
import { createHash } from "node:crypto";
|
|
20
21
|
import { registerTelegramFreeformPrompt, renderTelegramInputRequest } from "eve/channels/telegram";
|
|
@@ -59,7 +60,7 @@ function routingConfirmationState(events, prompt, at) {
|
|
|
59
60
|
if (prompt.state !== void 0 && prompt.state !== "pending" && prompt.state !== "approved") return prompt.state;
|
|
60
61
|
let state = prompt.state ?? "pending";
|
|
61
62
|
let callId;
|
|
62
|
-
for (const event of events) {
|
|
63
|
+
for (const event of prompt.mode === "deferred" ? [] : events) {
|
|
63
64
|
const data = object(event.data);
|
|
64
65
|
if ((event.type === "context.cleared" || event.type === "session.started") && Date.parse(event.meta.at) >= Date.parse(prompt.at)) state = "expired";
|
|
65
66
|
if (event.type === "input.requested" && Array.isArray(data.requests)) {
|
|
@@ -191,7 +192,7 @@ function messagePrompt(message) {
|
|
|
191
192
|
at: new Date().toISOString()
|
|
192
193
|
};
|
|
193
194
|
}
|
|
194
|
-
function telegramConfirmationEvents(archive, observed) {
|
|
195
|
+
function telegramConfirmationEvents(archive, observed, deferredOptions) {
|
|
195
196
|
const remember = (channel, ctx, messageId, text, confirmation) => {
|
|
196
197
|
const principal = ctx.session.auth.current;
|
|
197
198
|
const name = humanText(principal?.attributes.memberName);
|
|
@@ -389,6 +390,13 @@ function telegramConfirmationEvents(archive, observed) {
|
|
|
389
390
|
},
|
|
390
391
|
async "action.result"(data, channel, ctx) {
|
|
391
392
|
const state = channel.state;
|
|
393
|
+
if (data.result.kind === "tool-result" && deferredOptions !== void 0 && archive !== void 0) {
|
|
394
|
+
const output = object(data.result.output);
|
|
395
|
+
if (output.status === "awaiting_confirmation" && typeof output.confirmation === "string") {
|
|
396
|
+
await postDeferredCard(output.confirmation, channel, ctx, deferredOptions, archive, observed);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
392
400
|
if (data.result.kind === "tool-result") {
|
|
393
401
|
const question = object(data.result.output).pendingQuestion;
|
|
394
402
|
if (data.result.toolName.endsWith("__questions_ask") && isPendingQuestion(question)) {
|
|
@@ -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 { p as currentInboundRequest, r as currentThreadId } from "../../../_chunks/customer-text-DjszxgTo.mjs";
|
|
8
7
|
import { conversationEvent } from "../conversations.mjs";
|
|
8
|
+
import { n as currentInboundRequest, s as currentThreadId } from "../../../_chunks/inbound-request-BwBvXk0A.mjs";
|
|
9
9
|
import { contentShape, messageContent } from "./content.mjs";
|
|
10
10
|
import { currentConfirmationPrompt, messageMetadata, observedChatKey } from "./observed.mjs";
|
|
11
11
|
import { archivedRoutingMessages } from "./routing-context.mjs";
|
|
@@ -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
|
|
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-BwBvXk0A.mjs";
|
|
9
9
|
export { TelegramDeliveryError, currentLocale, currentThreadId, customerCopy, customerMessage, customerText, failureText, formattingRefused, isTechnicalText, reportTelegramError, safeTelegramFetch, withDeliveryLocale };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { TelegramCallbackQuery, TelegramContext, TelegramHandle, TelegramEventContext } from "eve/channels/telegram";
|
|
2
|
+
import type { SessionContext } from "eve/context";
|
|
3
|
+
import { type DeferredConfirmation, type DeferredOptions } from "../deferred-confirmations.js";
|
|
4
|
+
import { type Conversations } from "../conversations.js";
|
|
5
|
+
import type { AgentIdentities } from "../identities.js";
|
|
6
|
+
import { type ObservedMessages } from "./observed.js";
|
|
7
|
+
export declare const isDeferredCallback: (data?: string) => boolean;
|
|
8
|
+
export declare const deferredCallback: (record: DeferredConfirmation, choice: "y" | "n" | "e") => string;
|
|
9
|
+
export declare function archiveDeferred(record: DeferredConfirmation, phase: string, archive: Conversations): Promise<void>;
|
|
10
|
+
/** Called at the durable tool-result boundary. Pending cards never become eve input requests. */
|
|
11
|
+
export declare function postDeferredCard(id: string, channel: TelegramEventContext, ctx: SessionContext, options: DeferredOptions, archive: Conversations, observed?: ObservedMessages): Promise<void>;
|
|
12
|
+
/** Every callback re-resolves the live identity. The database arbitrates concurrent presses. */
|
|
13
|
+
export declare function handleDeferredCallback(input: {
|
|
14
|
+
readonly context: TelegramContext;
|
|
15
|
+
readonly query: TelegramCallbackQuery;
|
|
16
|
+
readonly identities: AgentIdentities;
|
|
17
|
+
readonly archive: Conversations;
|
|
18
|
+
readonly options: DeferredOptions;
|
|
19
|
+
readonly observed?: ObservedMessages;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
/** A database-clock expiry claim is shared by webhooks and the minute schedule. */
|
|
22
|
+
export declare function expireDeferredRecord(record: DeferredConfirmation, context: {
|
|
23
|
+
readonly telegram: Pick<TelegramHandle, "editMessageText">;
|
|
24
|
+
}, options: DeferredOptions, archive: Conversations, notify: NonNullable<import("./inbound-request.js").InboundRequest["confirmationEvent"]>, observed?: ObservedMessages): Promise<void>;
|
|
25
|
+
/** Webhooks also reconcile expiry if the host's minute scheduler is delayed. */
|
|
26
|
+
export declare function expireDeferredCards(context: TelegramContext, options: DeferredOptions, archive: Conversations, observed?: ObservedMessages): Promise<void>;
|