@finalai/final-agent-provider 0.6.4 → 0.8.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 (106) hide show
  1. package/HOSTING.md +4 -4
  2. package/README.md +113 -2
  3. package/dist/modules/authoring-final-channel.mjs +2 -2
  4. package/dist/modules/authoring-final-channel.mjs.map +2 -2
  5. package/dist/modules/channel-factories.d.mts +5 -7
  6. package/dist/modules/channel-factories.mjs +4 -50
  7. package/dist/modules/channel-factories.mjs.map +2 -2
  8. package/dist/modules/channels/channel-utils.d.mts +132 -0
  9. package/dist/modules/channels/channel-utils.mjs +445 -0
  10. package/dist/modules/channels/channel-utils.mjs.map +7 -0
  11. package/dist/modules/channels/delivery.d.mts +5 -5
  12. package/dist/modules/channels/delivery.mjs.map +1 -1
  13. package/dist/modules/channels/direct-messaging.d.mts +2 -2
  14. package/dist/modules/channels/direct-messaging.mjs.map +2 -2
  15. package/dist/modules/channels/discord-dispatch.d.mts +17 -0
  16. package/dist/modules/channels/discord-dispatch.mjs +139 -0
  17. package/dist/modules/channels/discord-dispatch.mjs.map +7 -0
  18. package/dist/modules/channels/discord-gateway.d.mts +19 -0
  19. package/dist/modules/channels/discord-gateway.mjs +233 -0
  20. package/dist/modules/channels/discord-gateway.mjs.map +7 -0
  21. package/dist/modules/channels/discord.d.mts +9 -0
  22. package/dist/modules/channels/discord.mjs +904 -0
  23. package/dist/modules/channels/discord.mjs.map +7 -0
  24. package/dist/modules/channels/fake-platform.d.mts +2 -7
  25. package/dist/modules/channels/fake-platform.mjs +2 -38
  26. package/dist/modules/channels/fake-platform.mjs.map +2 -2
  27. package/dist/modules/channels/github.d.mts +5 -0
  28. package/dist/modules/channels/github.mjs +720 -0
  29. package/dist/modules/channels/github.mjs.map +7 -0
  30. package/dist/modules/channels/group-messaging.d.mts +2 -2
  31. package/dist/modules/channels/group-messaging.mjs.map +2 -2
  32. package/dist/modules/channels/index.d.mts +7 -3
  33. package/dist/modules/channels/index.mjs +7 -3
  34. package/dist/modules/channels/index.mjs.map +2 -2
  35. package/dist/modules/channels/linear.d.mts +4 -0
  36. package/dist/modules/channels/linear.mjs +621 -0
  37. package/dist/modules/channels/linear.mjs.map +7 -0
  38. package/dist/modules/channels/messaging-channel.d.mts +3 -3
  39. package/dist/modules/channels/messaging-channel.mjs +2 -2
  40. package/dist/modules/channels/messaging-channel.mjs.map +2 -2
  41. package/dist/modules/channels/phone.d.mts +8 -0
  42. package/dist/modules/channels/phone.mjs +382 -0
  43. package/dist/modules/channels/phone.mjs.map +7 -0
  44. package/dist/modules/channels/projection.mjs.map +1 -1
  45. package/dist/modules/channels/reply-receipts.d.mts +31 -0
  46. package/dist/modules/channels/reply-receipts.mjs +63 -0
  47. package/dist/modules/channels/reply-receipts.mjs.map +7 -0
  48. package/dist/modules/channels/routing.d.mts +1 -0
  49. package/dist/modules/channels/routing.mjs +5 -1
  50. package/dist/modules/channels/routing.mjs.map +2 -2
  51. package/dist/modules/channels/slack.d.mts +7 -0
  52. package/dist/modules/channels/slack.mjs +1134 -0
  53. package/dist/modules/channels/slack.mjs.map +7 -0
  54. package/dist/modules/channels/voice-recording.d.mts +7 -0
  55. package/dist/modules/channels/voice-recording.mjs +49 -0
  56. package/dist/modules/channels/voice-recording.mjs.map +7 -0
  57. package/dist/modules/channels/voice.d.mts +33 -0
  58. package/dist/modules/channels/voice.mjs +851 -0
  59. package/dist/modules/channels/voice.mjs.map +7 -0
  60. package/dist/modules/channels/webhook.d.mts +5 -0
  61. package/dist/modules/channels/webhook.mjs +76 -0
  62. package/dist/modules/channels/webhook.mjs.map +7 -0
  63. package/dist/modules/harnesses/acp-describe.mjs +1 -1
  64. package/dist/modules/harnesses/acp-describe.mjs.map +2 -2
  65. package/dist/modules/harnesses/fidelity.mjs +17 -17
  66. package/dist/modules/harnesses/fidelity.mjs.map +2 -2
  67. package/dist/modules/harnesses/remote-url.mjs.map +1 -1
  68. package/dist/modules/runtime/remote-host.mjs.map +1 -1
  69. package/dist/modules/sandboxes/build-scripts.mjs.map +1 -1
  70. package/dist/modules/sandboxes/bytes.d.mts +1 -0
  71. package/dist/modules/sandboxes/bytes.mjs +7 -1
  72. package/dist/modules/sandboxes/bytes.mjs.map +2 -2
  73. package/dist/modules/sandboxes/computer.mjs +5 -1
  74. package/dist/modules/sandboxes/computer.mjs.map +2 -2
  75. package/dist/modules/sandboxes/computers-api-transport.d.mts +1 -1
  76. package/dist/modules/sandboxes/computers-api-transport.mjs +8 -0
  77. package/dist/modules/sandboxes/computers-api-transport.mjs.map +2 -2
  78. package/dist/modules/sandboxes/device-api.mjs +8 -0
  79. package/dist/modules/sandboxes/device-api.mjs.map +2 -2
  80. package/dist/modules/sandboxes/device.mjs +6 -0
  81. package/dist/modules/sandboxes/device.mjs.map +2 -2
  82. package/dist/modules/sandboxes/paths.mjs.map +1 -1
  83. package/dist/modules/sandboxes/session.d.mts +1 -0
  84. package/dist/modules/sandboxes/session.mjs +106 -36
  85. package/dist/modules/sandboxes/session.mjs.map +3 -3
  86. package/dist/modules/sandboxes/testing/conformance.mjs +3 -0
  87. package/dist/modules/sandboxes/testing/conformance.mjs.map +2 -2
  88. package/dist/modules/sandboxes/testing/fake-machine.mjs +11 -0
  89. package/dist/modules/sandboxes/testing/fake-machine.mjs.map +2 -2
  90. package/dist/modules/sandboxes/transport.d.mts +1 -0
  91. package/dist/modules/sandboxes/transport.mjs.map +2 -2
  92. package/dist/modules/testing/harness.mjs +2 -2
  93. package/dist/modules/testing/harness.mjs.map +1 -1
  94. package/package.json +45 -19
  95. package/dist/modules/channels/address-context.d.mts +0 -7
  96. package/dist/modules/channels/address-context.mjs +0 -80
  97. package/dist/modules/channels/address-context.mjs.map +0 -7
  98. package/dist/modules/channels/agent-events.d.mts +0 -103
  99. package/dist/modules/channels/agent-events.mjs +0 -341
  100. package/dist/modules/channels/agent-events.mjs.map +0 -7
  101. package/dist/modules/channels/agent-schedules.d.mts +0 -25
  102. package/dist/modules/channels/agent-schedules.mjs +0 -96
  103. package/dist/modules/channels/agent-schedules.mjs.map +0 -7
  104. package/dist/modules/channels/service-input.d.mts +0 -22
  105. package/dist/modules/channels/service-input.mjs +0 -32
  106. package/dist/modules/channels/service-input.mjs.map +0 -7
@@ -0,0 +1,445 @@
1
+ import {
2
+ placeRefSchema as importPlaceSchema,
3
+ principalRefSchema,
4
+ jsonObjectSchema,
5
+ jsonValueSchema
6
+ } from "@finalai/agent-provider";
7
+ import * as z from "zod";
8
+ const channelBaseUrl = (config) => config.baseUrl ?? "https://agent-session.final.ai";
9
+ const providerPlaceKey = (place) => JSON.stringify([place.tenant ?? null, place.kind, place.id]);
10
+ const parseJson = (text) => {
11
+ try {
12
+ return JSON.parse(text);
13
+ } catch {
14
+ return null;
15
+ }
16
+ };
17
+ const textOf = (content) => content.flatMap(
18
+ (block) => block.type === "text" && typeof block.text === "string" ? [block.text] : []
19
+ ).join("");
20
+ const replyText = (data) => data.messages != null ? data.messages.map((message) => textOf(message.content)).join("\n\n") : textOf(data.content ?? []);
21
+ const turnStatus = (data) => {
22
+ const reason = data.stopReason;
23
+ if (reason == null || reason === "end_turn") return null;
24
+ switch (reason) {
25
+ case "cancelled":
26
+ return "This turn was cancelled.";
27
+ case "error":
28
+ return "The agent could not finish this turn. Open Final to try again.";
29
+ case "timed_out":
30
+ return "The agent timed out before finishing this turn.";
31
+ case "refusal":
32
+ return "The agent declined this request.";
33
+ case "max_tokens":
34
+ return "The reply stopped at its output limit.";
35
+ case "max_turn_requests":
36
+ return "The agent stopped at its request limit.";
37
+ default:
38
+ return `The agent stopped (${reason.replace(/[\r\n]/g, " ").slice(0, 120)}). Open Final for details.`;
39
+ }
40
+ };
41
+ const completionText = (data, partial = "") => {
42
+ const reply = replyText(data);
43
+ const text = reply.trim().length === 0 ? partial : reply;
44
+ const status = turnStatus(data);
45
+ const combined = [status, text].filter((part) => part != null && part.trim().length > 0).join("\n\n");
46
+ return combined.length > 0 ? combined : "The turn finished without a text reply. Open Final for details.";
47
+ };
48
+ const hexBytes = (hex) => {
49
+ if (!/^(?:[0-9a-fA-F]{2})+$/.test(hex)) return null;
50
+ return Uint8Array.from(hex.match(/../g) ?? [], (pair) => Number.parseInt(pair, 16));
51
+ };
52
+ const verifyHmac = async (args) => {
53
+ const key = await crypto.subtle.importKey(
54
+ "raw",
55
+ new TextEncoder().encode(args.secret),
56
+ { name: "HMAC", hash: args.algorithm ?? "SHA-256" },
57
+ false,
58
+ ["verify"]
59
+ );
60
+ let signature;
61
+ if (args.encoding === "base64") {
62
+ try {
63
+ signature = Uint8Array.from(atob(args.signature), (char) => char.charCodeAt(0));
64
+ } catch {
65
+ return false;
66
+ }
67
+ } else signature = hexBytes(args.signature);
68
+ if (signature == null) return false;
69
+ return crypto.subtle.verify(
70
+ "HMAC",
71
+ key,
72
+ new Uint8Array(signature),
73
+ new TextEncoder().encode(args.body)
74
+ );
75
+ };
76
+ const freshTimestamp = (timestamp, now = Date.now()) => timestamp != null && /^\d+$/.test(timestamp) && Math.abs(now / 1e3 - Number(timestamp)) <= 300;
77
+ const refusal = (result) => ({
78
+ accepted: false,
79
+ refusal: result
80
+ });
81
+ const receipt = (result) => "refused" in result ? refusal(result) : { accepted: true };
82
+ const personFor = async (ctx, principal) => {
83
+ const result = await ctx.people.resolve(principal);
84
+ switch (result.kind) {
85
+ case "member":
86
+ return result.person;
87
+ case "unlinked":
88
+ return {
89
+ refused: "unlinked",
90
+ linkUrl: result.linkUrl,
91
+ message: "Link your account in Final to continue."
92
+ };
93
+ case "blocked":
94
+ return { refused: "no-access", message: "This account cannot access this workspace." };
95
+ }
96
+ };
97
+ const receivePersonMessage = async (args) => {
98
+ const { ctx, principal, place } = args;
99
+ const context = () => args.loadContext == null ? args.context : args.loadContext();
100
+ const routePlace = args.routePlace ?? place;
101
+ const accepted = async (result) => {
102
+ if (!("refused" in result)) await args.onAccepted?.(result);
103
+ return receipt(result);
104
+ };
105
+ const person = await personFor(ctx, principal);
106
+ if (typeof person !== "string" && person.refused !== "unlinked") return refusal(person);
107
+ const route = await ctx.places.resolve(routePlace);
108
+ if (typeof person !== "string" && args.resumeAfterLink === true && (route.kind === "unconnected" || route.policy.audience !== "anyone")) {
109
+ const pending = jsonValueSchema.parse({
110
+ principal,
111
+ place,
112
+ text: args.text,
113
+ externalKey: args.externalKey,
114
+ ...args.ref == null ? {} : { ref: args.ref }
115
+ });
116
+ if (JSON.stringify(pending).length <= 64 * 1024)
117
+ await ctx.store.set(
118
+ stateKey("pending-link", principal.tenant ?? null, principal.id),
119
+ pending,
120
+ {
121
+ ttlSeconds: 600
122
+ }
123
+ );
124
+ }
125
+ if (route.kind === "unconnected") {
126
+ if (typeof person !== "string") return refusal(person);
127
+ if (route.defaults == null)
128
+ return refusal({
129
+ refused: "unconnected",
130
+ connectUrl: route.connectUrl,
131
+ message: "Connect this place to a chat in Final."
132
+ });
133
+ let conversation;
134
+ try {
135
+ conversation = await ctx.places.connect(routePlace, {
136
+ by: person,
137
+ create: route.defaults.create
138
+ });
139
+ } catch {
140
+ ctx.log("Channel connection failed.");
141
+ return refusal({
142
+ refused: "unconnected",
143
+ connectUrl: route.connectUrl,
144
+ message: "Open Final to connect this place to a chat you can manage."
145
+ });
146
+ }
147
+ return accepted(
148
+ await ctx.conversation(conversation).post({
149
+ from: person,
150
+ content: [{ type: "text", text: args.text }],
151
+ context: await context(),
152
+ origin: { place, ref: args.ref },
153
+ idempotencyKey: args.externalKey,
154
+ waitForAcceptance: args.waitForAcceptance
155
+ })
156
+ );
157
+ }
158
+ const origin = { place, ref: args.ref };
159
+ if (typeof person !== "string") {
160
+ if (route.policy.audience !== "anyone") return refusal(person);
161
+ return accepted(
162
+ await ctx.conversation(route.conversation).input({
163
+ principal,
164
+ label: `From ${principal.displayName ?? principal.id} in ${place.title ?? place.kind}`,
165
+ content: [{ type: "text", text: args.text }],
166
+ context: await context(),
167
+ origin,
168
+ idempotencyKey: args.externalKey,
169
+ waitForAcceptance: args.waitForAcceptance
170
+ })
171
+ );
172
+ }
173
+ return accepted(
174
+ await ctx.conversation(route.conversation).post({
175
+ from: person,
176
+ content: [{ type: "text", text: args.text }],
177
+ context: await context(),
178
+ origin,
179
+ idempotencyKey: args.externalKey,
180
+ waitForAcceptance: args.waitForAcceptance
181
+ })
182
+ );
183
+ };
184
+ const externalKey = (place, id) => JSON.stringify([place.tenant ?? null, place.id, id]);
185
+ const refString = (ref, name) => typeof ref?.[name] === "string" ? ref[name] : null;
186
+ const responseJson = async (response) => {
187
+ const text = await response.text();
188
+ if (!response.ok)
189
+ throw new Error(`Channel request failed (${response.status}): ${text.slice(0, 500)}`, {
190
+ cause: { status: response.status }
191
+ });
192
+ return parseJson(text);
193
+ };
194
+ const deliveryRetention = { ttlSeconds: 90 * 24 * 60 * 60 };
195
+ const stateKey = (prefix, ...values) => `${prefix}:${JSON.stringify(values)}`;
196
+ const oauthStateSchema = z.object({
197
+ ticket: z.string(),
198
+ principal: z.object({
199
+ id: z.string(),
200
+ tenant: z.string().optional(),
201
+ displayName: z.string().optional()
202
+ }),
203
+ redirectUri: z.string(),
204
+ verifier: z.string().optional()
205
+ });
206
+ const startOAuth = async (args, ctx) => {
207
+ const state = crypto.randomUUID();
208
+ const verifier = args.pkce === true ? crypto.randomUUID() + crypto.randomUUID() : void 0;
209
+ let challenge = null;
210
+ if (verifier != null) {
211
+ const bytes = new Uint8Array(
212
+ await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier))
213
+ );
214
+ challenge = btoa(String.fromCharCode(...bytes)).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
215
+ }
216
+ await ctx.store.set(
217
+ stateKey("oauth", state),
218
+ {
219
+ ticket: args.ticket,
220
+ principal: { ...args.principal },
221
+ redirectUri: args.redirectUri,
222
+ ...verifier == null ? {} : { verifier }
223
+ },
224
+ { ttlSeconds: 600 }
225
+ );
226
+ const url = new URL(args.authorizeUrl);
227
+ url.search = new URLSearchParams({
228
+ client_id: args.clientId,
229
+ redirect_uri: args.redirectUri,
230
+ response_type: "code",
231
+ state,
232
+ scope: args.scope,
233
+ ...challenge == null ? {} : { code_challenge: challenge, code_challenge_method: "S256" },
234
+ ...args.extra
235
+ }).toString();
236
+ return url;
237
+ };
238
+ const takeOAuth = async (request, ctx) => {
239
+ const params = new URL(request.url).searchParams;
240
+ const state = params.get("state");
241
+ const code = params.get("code");
242
+ if (state == null || code == null || params.has("error")) return null;
243
+ const key = stateKey("oauth", state);
244
+ const saved = oauthStateSchema.safeParse(await ctx.store.get(key));
245
+ if (!saved.success) return null;
246
+ await ctx.store.set(key, null);
247
+ return { ...saved.data, code };
248
+ };
249
+ const confirmOAuth = async (args, ctx) => {
250
+ if (args.actual.id !== args.expected.id || args.actual.tenant !== args.expected.tenant)
251
+ return new Response("The provider account does not match this link request.", { status: 403 });
252
+ const result = await ctx.people.confirm({ ticket: args.ticket, principal: args.actual });
253
+ if ("refused" in result) return new Response(result.message, { status: 403 });
254
+ const key = stateKey("pending-link", args.actual.tenant ?? null, args.actual.id);
255
+ const pending = z.object({
256
+ principal: principalRefSchema,
257
+ place: importPlaceSchema,
258
+ text: z.string().max(65536),
259
+ externalKey: z.string(),
260
+ ref: jsonObjectSchema.optional()
261
+ }).safeParse(await ctx.store.get(key));
262
+ if (pending.success) {
263
+ const context = pending.data.place.tenant == null ? ctx : inTenant(ctx, pending.data.place.tenant);
264
+ try {
265
+ const replayed = await receivePersonMessage({ ...pending.data, ctx: context });
266
+ if ("accepted" in replayed && replayed.accepted) await ctx.store.set(key, null);
267
+ } catch {
268
+ return new Response("Account linked. Send your message again to continue.");
269
+ }
270
+ }
271
+ return new Response("Account linked. You can return to your conversation.");
272
+ };
273
+ const permissionOptionsSchema = z.object({
274
+ options: z.array(z.object({ optionId: z.string(), name: z.string(), kind: z.string() }))
275
+ });
276
+ const decisionSummary = (request) => {
277
+ const detail = z.object({
278
+ toolCall: z.object({ title: z.string().optional() }).optional(),
279
+ message: z.string().optional()
280
+ }).safeParse(request);
281
+ return detail.success ? (detail.data.toolCall?.title ?? detail.data.message ?? "An action needs your approval.").slice(0, 1500) : "An action needs your approval.";
282
+ };
283
+ const decisionAnswer = (choices, text) => {
284
+ const normalized = text.trim().toLowerCase().replace(/[.!?]$/g, "");
285
+ const exact = choices.filter((choice) => choice.label.toLowerCase() === normalized);
286
+ if (exact.length === 1) return exact[0];
287
+ const answer = ["yes", "approve"].includes(normalized) ? "approve" : ["no", "deny"].includes(normalized) ? "deny" : null;
288
+ const matching = choices.filter((choice) => choice.answer === answer);
289
+ return matching.length === 1 ? matching[0] : void 0;
290
+ };
291
+ const decisionChoices = (request) => {
292
+ const parsed = permissionOptionsSchema.safeParse(request);
293
+ if (!parsed.success) {
294
+ const elicitation = z.object({
295
+ requestedSchema: z.object({ properties: z.record(z.string(), z.unknown()).default({}) })
296
+ }).safeParse(request);
297
+ if (!elicitation.success || Object.keys(elicitation.data.requestedSchema.properties).length !== 0)
298
+ return [];
299
+ return [
300
+ { label: "Approve", answer: "approve", outcome: { action: "accept", content: {} } },
301
+ { label: "Deny", answer: "deny", outcome: { action: "decline" } }
302
+ ];
303
+ }
304
+ const options = parsed.data.options.filter(
305
+ (option) => option.kind === "allow_once" || option.kind === "reject_once"
306
+ );
307
+ if (!options.some((option) => option.kind === "allow_once")) return [];
308
+ const names = options.map((option) => option.name.trim().slice(0, 75));
309
+ if (names.some((name) => name.length === 0) || new Set(names.map((name) => name.toLowerCase())).size !== names.length)
310
+ return [];
311
+ return options.map((option) => ({
312
+ label: option.name.trim().slice(0, 75),
313
+ answer: option.kind === "allow_once" ? "approve" : "deny",
314
+ outcome: { outcome: "selected", optionId: option.optionId }
315
+ }));
316
+ };
317
+ const createQueue = () => {
318
+ const pending = [];
319
+ let wake = null;
320
+ let ended = false;
321
+ const push = (value) => {
322
+ if (ended) return;
323
+ pending.push(value);
324
+ wake?.();
325
+ wake = null;
326
+ };
327
+ const close = () => {
328
+ ended = true;
329
+ wake?.();
330
+ wake = null;
331
+ };
332
+ async function* stream() {
333
+ while (!ended || pending.length > 0) {
334
+ const value = pending.shift();
335
+ if (value !== void 0) {
336
+ yield value;
337
+ continue;
338
+ }
339
+ await new Promise((resolve) => {
340
+ wake = resolve;
341
+ });
342
+ }
343
+ }
344
+ return { push, close, stream: stream() };
345
+ };
346
+ const inTenant = (ctx, tenant) => ({
347
+ ...ctx,
348
+ credentials: {
349
+ ...ctx.credentials,
350
+ app: (options) => ctx.credentials.app({ tenant: options?.tenant ?? tenant })
351
+ }
352
+ });
353
+ const providerOriginSchema = z.object({
354
+ place: z.lazy(() => importPlaceSchema),
355
+ externalKey: z.string(),
356
+ turnId: z.string().optional()
357
+ });
358
+ const abortable = async function* (source, signal) {
359
+ const iterator = source[Symbol.asyncIterator]();
360
+ const stopped = Promise.withResolvers();
361
+ const abort = () => stopped.resolve(null);
362
+ signal.addEventListener("abort", abort, { once: true });
363
+ if (signal.aborted) abort();
364
+ try {
365
+ while (!signal.aborted) {
366
+ const next = await Promise.race([iterator.next(), stopped.promise]);
367
+ if (next == null || next.done) break;
368
+ yield next.value;
369
+ }
370
+ } finally {
371
+ signal.removeEventListener("abort", abort);
372
+ void iterator.return?.().catch(() => void 0);
373
+ }
374
+ };
375
+ const sharedReactionSchema = z.object({
376
+ principals: z.array(z.string()).max(256),
377
+ overflow: z.boolean(),
378
+ owned: z.boolean()
379
+ });
380
+ const addSharedReaction = async (args) => {
381
+ const principal = JSON.stringify([args.principal.tenant ?? null, args.principal.id]);
382
+ const saved = sharedReactionSchema.safeParse(await args.store.get(args.key));
383
+ if (saved.success) {
384
+ if (saved.data.overflow || saved.data.principals.includes(principal)) return;
385
+ await args.store.set(
386
+ args.key,
387
+ saved.data.principals.length >= 256 ? { ...saved.data, overflow: true } : { ...saved.data, principals: [...saved.data.principals, principal] },
388
+ deliveryRetention
389
+ );
390
+ return;
391
+ }
392
+ const owned = await args.add();
393
+ await args.store.set(
394
+ args.key,
395
+ { principals: [principal], overflow: false, owned },
396
+ deliveryRetention
397
+ );
398
+ };
399
+ const removeSharedReaction = async (args) => {
400
+ const saved = sharedReactionSchema.safeParse(await args.store.get(args.key));
401
+ const principal = JSON.stringify([args.principal.tenant ?? null, args.principal.id]);
402
+ if (!saved.success || saved.data.overflow || !saved.data.principals.includes(principal)) return;
403
+ const remaining = saved.data.principals.filter((value) => value !== principal);
404
+ if (remaining.length > 0) {
405
+ await args.store.set(args.key, { ...saved.data, principals: remaining }, deliveryRetention);
406
+ return;
407
+ }
408
+ if (saved.data.owned) await args.remove();
409
+ await args.store.set(args.key, null);
410
+ };
411
+ export {
412
+ abortable,
413
+ addSharedReaction,
414
+ channelBaseUrl,
415
+ completionText,
416
+ confirmOAuth,
417
+ createQueue,
418
+ decisionAnswer,
419
+ decisionChoices,
420
+ decisionSummary,
421
+ deliveryRetention,
422
+ externalKey,
423
+ freshTimestamp,
424
+ hexBytes,
425
+ inTenant,
426
+ oauthStateSchema,
427
+ parseJson,
428
+ personFor,
429
+ providerOriginSchema,
430
+ providerPlaceKey,
431
+ receipt,
432
+ receivePersonMessage,
433
+ refString,
434
+ refusal,
435
+ removeSharedReaction,
436
+ replyText,
437
+ responseJson,
438
+ startOAuth,
439
+ stateKey,
440
+ takeOAuth,
441
+ textOf,
442
+ turnStatus,
443
+ verifyHmac
444
+ };
445
+ //# sourceMappingURL=channel-utils.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/channels/channel-utils.ts"],
4
+ "sourcesContent": ["import {\n placeRefSchema as importPlaceSchema,\n principalRefSchema,\n jsonObjectSchema,\n jsonValueSchema,\n} from '@finalai/agent-provider';\nimport type {\n ChannelReceiveContextIface,\n ConversationHandle,\n ChannelReceiveResult,\n JsonObject,\n JsonValue,\n OriginIface,\n PersonHandle,\n PlaceRef,\n PrincipalRef,\n Refusal,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\n\nexport const channelBaseUrl = (config: { baseUrl?: string }): string =>\n config.baseUrl ?? 'https://agent-session.final.ai';\nexport const providerPlaceKey = (place: PlaceRef): string =>\n JSON.stringify([place.tenant ?? null, place.kind, place.id]);\nexport const parseJson = (text: string): unknown => {\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n};\nexport const textOf = (content: readonly JsonObject[]): string =>\n content\n .flatMap((block) =>\n block.type === 'text' && typeof block.text === 'string' ? [block.text] : [],\n )\n .join('');\nexport const replyText = (data: {\n messages?: { content: JsonObject[] }[];\n content?: JsonObject[];\n}): string =>\n data.messages != null\n ? data.messages.map((message) => textOf(message.content)).join('\\n\\n')\n : textOf(data.content ?? []);\nexport const turnStatus = (data: { stopReason?: string | null }): string | null => {\n const reason = data.stopReason;\n if (reason == null || reason === 'end_turn') return null;\n switch (reason) {\n case 'cancelled':\n return 'This turn was cancelled.';\n case 'error':\n return 'The agent could not finish this turn. Open Final to try again.';\n case 'timed_out':\n return 'The agent timed out before finishing this turn.';\n case 'refusal':\n return 'The agent declined this request.';\n case 'max_tokens':\n return 'The reply stopped at its output limit.';\n case 'max_turn_requests':\n return 'The agent stopped at its request limit.';\n default:\n return `The agent stopped (${reason.replace(/[\\r\\n]/g, ' ').slice(0, 120)}). Open Final for details.`;\n }\n};\nexport const completionText = (\n data: {\n messages?: { content: JsonObject[] }[];\n content?: JsonObject[];\n stopReason?: string | null;\n },\n partial = '',\n): string => {\n const reply = replyText(data);\n const text = reply.trim().length === 0 ? partial : reply;\n const status = turnStatus(data);\n const combined = [status, text]\n .filter((part) => part != null && part.trim().length > 0)\n .join('\\n\\n');\n return combined.length > 0\n ? combined\n : 'The turn finished without a text reply. Open Final for details.';\n};\nexport const hexBytes = (hex: string): Uint8Array | null => {\n if (!/^(?:[0-9a-fA-F]{2})+$/.test(hex)) return null;\n return Uint8Array.from(hex.match(/../g) ?? [], (pair) => Number.parseInt(pair, 16));\n};\nexport const verifyHmac = async (args: {\n secret: string;\n body: string;\n signature: string;\n algorithm?: 'SHA-256' | 'SHA-1';\n encoding?: 'hex' | 'base64';\n}): Promise<boolean> => {\n const key = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(args.secret),\n { name: 'HMAC', hash: args.algorithm ?? 'SHA-256' },\n false,\n ['verify'],\n );\n let signature: Uint8Array | null;\n if (args.encoding === 'base64') {\n try {\n signature = Uint8Array.from(atob(args.signature), (char) => char.charCodeAt(0));\n } catch {\n return false;\n }\n } else signature = hexBytes(args.signature);\n if (signature == null) return false;\n return crypto.subtle.verify(\n 'HMAC',\n key,\n new Uint8Array(signature),\n new TextEncoder().encode(args.body),\n );\n};\nexport const freshTimestamp = (timestamp: string | null, now = Date.now()): timestamp is string =>\n timestamp != null && /^\\d+$/.test(timestamp) && Math.abs(now / 1000 - Number(timestamp)) <= 300;\nexport const refusal = (result: Refusal): ChannelReceiveResult => ({\n accepted: false,\n refusal: result,\n});\nexport const receipt = (\n result: { messageId: string } | { inputId: string } | Refusal,\n): ChannelReceiveResult => ('refused' in result ? refusal(result) : { accepted: true });\nexport const personFor = async <Config>(\n ctx: ChannelReceiveContextIface<Config>,\n principal: PrincipalRef,\n): Promise<PersonHandle | Refusal> => {\n const result = await ctx.people.resolve(principal);\n switch (result.kind) {\n case 'member':\n return result.person;\n case 'unlinked':\n return {\n refused: 'unlinked',\n linkUrl: result.linkUrl,\n message: 'Link your account in Final to continue.',\n };\n case 'blocked':\n return { refused: 'no-access', message: 'This account cannot access this workspace.' };\n }\n};\nexport interface ReceivePersonMessageIface<Config> {\n ctx: ChannelReceiveContextIface<Config>;\n principal: PrincipalRef;\n place: PlaceRef;\n routePlace?: PlaceRef;\n text: string;\n externalKey: string;\n ref?: JsonObject;\n context?: string[];\n loadContext?: () => Promise<string[]>;\n waitForAcceptance?: boolean;\n onAccepted?: (result: { turnId?: string }) => Promise<void>;\n resumeAfterLink?: boolean;\n}\nexport const receivePersonMessage = async <Config>(\n args: ReceivePersonMessageIface<Config>,\n): Promise<ChannelReceiveResult> => {\n const { ctx, principal, place } = args;\n const context = () => (args.loadContext == null ? args.context : args.loadContext());\n const routePlace = args.routePlace ?? place;\n const accepted = async (\n result: { messageId: string; turnId?: string } | { inputId: string; turnId?: string } | Refusal,\n ): Promise<ChannelReceiveResult> => {\n if (!('refused' in result)) await args.onAccepted?.(result);\n return receipt(result);\n };\n const person = await personFor(ctx, principal);\n if (typeof person !== 'string' && person.refused !== 'unlinked') return refusal(person);\n const route = await ctx.places.resolve(routePlace);\n if (\n typeof person !== 'string' &&\n args.resumeAfterLink === true &&\n (route.kind === 'unconnected' || route.policy.audience !== 'anyone')\n ) {\n const pending = jsonValueSchema.parse({\n principal,\n place,\n text: args.text,\n externalKey: args.externalKey,\n ...(args.ref == null ? {} : { ref: args.ref }),\n });\n if (JSON.stringify(pending).length <= 64 * 1024)\n await ctx.store.set(\n stateKey('pending-link', principal.tenant ?? null, principal.id),\n pending,\n {\n ttlSeconds: 600,\n },\n );\n }\n if (route.kind === 'unconnected') {\n if (typeof person !== 'string') return refusal(person);\n if (route.defaults == null)\n return refusal({\n refused: 'unconnected',\n connectUrl: route.connectUrl,\n message: 'Connect this place to a chat in Final.',\n });\n let conversation: ConversationHandle;\n try {\n conversation = await ctx.places.connect(routePlace, {\n by: person,\n create: route.defaults.create,\n });\n } catch {\n ctx.log('Channel connection failed.');\n return refusal({\n refused: 'unconnected',\n connectUrl: route.connectUrl,\n message: 'Open Final to connect this place to a chat you can manage.',\n });\n }\n return accepted(\n await ctx.conversation(conversation).post({\n from: person,\n content: [{ type: 'text', text: args.text }],\n context: await context(),\n origin: { place, ref: args.ref },\n idempotencyKey: args.externalKey,\n waitForAcceptance: args.waitForAcceptance,\n }),\n );\n }\n const origin: OriginIface = { place, ref: args.ref };\n if (typeof person !== 'string') {\n if (route.policy.audience !== 'anyone') return refusal(person);\n return accepted(\n await ctx.conversation(route.conversation).input({\n principal,\n label: `From ${principal.displayName ?? principal.id} in ${place.title ?? place.kind}`,\n content: [{ type: 'text', text: args.text }],\n context: await context(),\n origin,\n idempotencyKey: args.externalKey,\n waitForAcceptance: args.waitForAcceptance,\n }),\n );\n }\n return accepted(\n await ctx.conversation(route.conversation).post({\n from: person,\n content: [{ type: 'text', text: args.text }],\n context: await context(),\n origin,\n idempotencyKey: args.externalKey,\n waitForAcceptance: args.waitForAcceptance,\n }),\n );\n};\nexport const externalKey = (place: PlaceRef, id: string): string =>\n JSON.stringify([place.tenant ?? null, place.id, id]);\nexport const refString = (ref: JsonObject | undefined, name: string): string | null =>\n typeof ref?.[name] === 'string' ? ref[name] : null;\nexport const responseJson = async (response: Response): Promise<unknown> => {\n const text = await response.text();\n if (!response.ok)\n throw new Error(`Channel request failed (${response.status}): ${text.slice(0, 500)}`, {\n cause: { status: response.status },\n });\n return parseJson(text);\n};\nexport const deliveryRetention = { ttlSeconds: 90 * 24 * 60 * 60 };\nexport const stateKey = (prefix: string, ...values: (string | null)[]): string =>\n `${prefix}:${JSON.stringify(values)}`;\nexport const oauthStateSchema = z.object({\n ticket: z.string(),\n principal: z.object({\n id: z.string(),\n tenant: z.string().optional(),\n displayName: z.string().optional(),\n }),\n redirectUri: z.string(),\n verifier: z.string().optional(),\n});\nexport const startOAuth = async <Config>(\n args: {\n ticket: string;\n principal: PrincipalRef;\n authorizeUrl: string;\n clientId: string;\n redirectUri: string;\n scope: string;\n extra?: Record<string, string>;\n pkce?: boolean;\n },\n ctx: ChannelReceiveContextIface<Config>,\n): Promise<URL> => {\n const state = crypto.randomUUID();\n const verifier = args.pkce === true ? crypto.randomUUID() + crypto.randomUUID() : undefined;\n let challenge: string | null = null;\n if (verifier != null) {\n const bytes = new Uint8Array(\n await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier)),\n );\n challenge = btoa(String.fromCharCode(...bytes))\n .replace(/=/g, '')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_');\n }\n await ctx.store.set(\n stateKey('oauth', state),\n {\n ticket: args.ticket,\n principal: { ...args.principal },\n redirectUri: args.redirectUri,\n ...(verifier == null ? {} : { verifier }),\n },\n { ttlSeconds: 600 },\n );\n const url = new URL(args.authorizeUrl);\n url.search = new URLSearchParams({\n client_id: args.clientId,\n redirect_uri: args.redirectUri,\n response_type: 'code',\n state,\n scope: args.scope,\n ...(challenge == null ? {} : { code_challenge: challenge, code_challenge_method: 'S256' }),\n ...args.extra,\n }).toString();\n return url;\n};\nexport const takeOAuth = async <Config>(\n request: Request,\n ctx: ChannelReceiveContextIface<Config>,\n) => {\n const params = new URL(request.url).searchParams;\n const state = params.get('state');\n const code = params.get('code');\n if (state == null || code == null || params.has('error')) return null;\n const key = stateKey('oauth', state);\n const saved = oauthStateSchema.safeParse(await ctx.store.get(key));\n if (!saved.success) return null;\n await ctx.store.set(key, null);\n return { ...saved.data, code };\n};\nexport const confirmOAuth = async <Config>(\n args: { ticket: string; expected: PrincipalRef; actual: PrincipalRef },\n ctx: ChannelReceiveContextIface<Config>,\n): Promise<Response> => {\n if (args.actual.id !== args.expected.id || args.actual.tenant !== args.expected.tenant)\n return new Response('The provider account does not match this link request.', { status: 403 });\n const result = await ctx.people.confirm({ ticket: args.ticket, principal: args.actual });\n if ('refused' in result) return new Response(result.message, { status: 403 });\n const key = stateKey('pending-link', args.actual.tenant ?? null, args.actual.id);\n const pending = z\n .object({\n principal: principalRefSchema,\n place: importPlaceSchema,\n text: z.string().max(65536),\n externalKey: z.string(),\n ref: jsonObjectSchema.optional(),\n })\n .safeParse(await ctx.store.get(key));\n if (pending.success) {\n const context =\n pending.data.place.tenant == null ? ctx : inTenant(ctx, pending.data.place.tenant);\n try {\n const replayed = await receivePersonMessage({ ...pending.data, ctx: context });\n if ('accepted' in replayed && replayed.accepted) await ctx.store.set(key, null);\n } catch {\n return new Response('Account linked. Send your message again to continue.');\n }\n }\n return new Response('Account linked. You can return to your conversation.');\n};\nconst permissionOptionsSchema = z.object({\n options: z.array(z.object({ optionId: z.string(), name: z.string(), kind: z.string() })),\n});\nexport interface DecisionChoiceIface {\n label: string;\n outcome: JsonValue;\n answer: 'approve' | 'deny';\n}\nexport const decisionSummary = (request: JsonObject): string => {\n const detail = z\n .object({\n toolCall: z.object({ title: z.string().optional() }).optional(),\n message: z.string().optional(),\n })\n .safeParse(request);\n return detail.success\n ? (\n detail.data.toolCall?.title ??\n detail.data.message ??\n 'An action needs your approval.'\n ).slice(0, 1500)\n : 'An action needs your approval.';\n};\nexport const decisionAnswer = (\n choices: DecisionChoiceIface[],\n text: string,\n): DecisionChoiceIface | undefined => {\n const normalized = text\n .trim()\n .toLowerCase()\n .replace(/[.!?]$/g, '');\n const exact = choices.filter((choice) => choice.label.toLowerCase() === normalized);\n if (exact.length === 1) return exact[0];\n const answer = ['yes', 'approve'].includes(normalized)\n ? 'approve'\n : ['no', 'deny'].includes(normalized)\n ? 'deny'\n : null;\n const matching = choices.filter((choice) => choice.answer === answer);\n return matching.length === 1 ? matching[0] : undefined;\n};\nexport const decisionChoices = (request: JsonObject): DecisionChoiceIface[] => {\n const parsed = permissionOptionsSchema.safeParse(request);\n if (!parsed.success) {\n const elicitation = z\n .object({\n requestedSchema: z.object({ properties: z.record(z.string(), z.unknown()).default({}) }),\n })\n .safeParse(request);\n if (\n !elicitation.success ||\n Object.keys(elicitation.data.requestedSchema.properties).length !== 0\n )\n return [];\n return [\n { label: 'Approve', answer: 'approve', outcome: { action: 'accept', content: {} } },\n { label: 'Deny', answer: 'deny', outcome: { action: 'decline' } },\n ];\n }\n const options = parsed.data.options.filter(\n (option) => option.kind === 'allow_once' || option.kind === 'reject_once',\n );\n if (!options.some((option) => option.kind === 'allow_once')) return [];\n const names = options.map((option) => option.name.trim().slice(0, 75));\n if (\n names.some((name) => name.length === 0) ||\n new Set(names.map((name) => name.toLowerCase())).size !== names.length\n )\n return [];\n return options.map((option) => ({\n label: option.name.trim().slice(0, 75),\n answer: option.kind === 'allow_once' ? 'approve' : 'deny',\n outcome: { outcome: 'selected', optionId: option.optionId },\n }));\n};\n\nexport const createQueue = <T>() => {\n const pending: T[] = [];\n let wake: (() => void) | null = null;\n let ended = false;\n const push = (value: T): void => {\n if (ended) return;\n pending.push(value);\n wake?.();\n wake = null;\n };\n const close = (): void => {\n ended = true;\n wake?.();\n wake = null;\n };\n async function* stream(): AsyncGenerator<T> {\n while (!ended || pending.length > 0) {\n const value = pending.shift();\n if (value !== undefined) {\n yield value;\n continue;\n }\n await new Promise<void>((resolve) => {\n wake = resolve;\n });\n }\n }\n return { push, close, stream: stream() };\n};\n\nexport const inTenant = <Config>(\n ctx: ChannelReceiveContextIface<Config>,\n tenant: string,\n): ChannelReceiveContextIface<Config> => ({\n ...ctx,\n credentials: {\n ...ctx.credentials,\n app: (options) => ctx.credentials.app({ tenant: options?.tenant ?? tenant }),\n },\n});\nexport const providerOriginSchema = z.object({\n place: z.lazy(() => importPlaceSchema),\n externalKey: z.string(),\n turnId: z.string().optional(),\n});\n\nexport const abortable = async function* <T>(\n source: AsyncIterable<T>,\n signal: AbortSignal,\n): AsyncGenerator<T> {\n const iterator = source[Symbol.asyncIterator]();\n const stopped = Promise.withResolvers<null>();\n const abort = () => stopped.resolve(null);\n signal.addEventListener('abort', abort, { once: true });\n if (signal.aborted) abort();\n try {\n while (!signal.aborted) {\n const next = await Promise.race([iterator.next(), stopped.promise]);\n if (next == null || next.done) break;\n yield next.value;\n }\n } finally {\n signal.removeEventListener('abort', abort);\n void iterator.return?.().catch(() => undefined);\n }\n};\n\nconst sharedReactionSchema = z.object({\n principals: z.array(z.string()).max(256),\n overflow: z.boolean(),\n owned: z.boolean(),\n});\ninterface SharedReactionArgsIface {\n store: ChannelReceiveContextIface['store'];\n key: string;\n principal: PrincipalRef;\n}\nexport const addSharedReaction = async (\n args: SharedReactionArgsIface & { add(): Promise<boolean> },\n): Promise<void> => {\n const principal = JSON.stringify([args.principal.tenant ?? null, args.principal.id]);\n const saved = sharedReactionSchema.safeParse(await args.store.get(args.key));\n if (saved.success) {\n if (saved.data.overflow || saved.data.principals.includes(principal)) return;\n await args.store.set(\n args.key,\n saved.data.principals.length >= 256\n ? { ...saved.data, overflow: true }\n : { ...saved.data, principals: [...saved.data.principals, principal] },\n deliveryRetention,\n );\n return;\n }\n const owned = await args.add();\n await args.store.set(\n args.key,\n { principals: [principal], overflow: false, owned },\n deliveryRetention,\n );\n};\nexport const removeSharedReaction = async (\n args: SharedReactionArgsIface & { remove(): Promise<void> },\n): Promise<void> => {\n const saved = sharedReactionSchema.safeParse(await args.store.get(args.key));\n const principal = JSON.stringify([args.principal.tenant ?? null, args.principal.id]);\n if (!saved.success || saved.data.overflow || !saved.data.principals.includes(principal)) return;\n const remaining = saved.data.principals.filter((value) => value !== principal);\n if (remaining.length > 0) {\n await args.store.set(args.key, { ...saved.data, principals: remaining }, deliveryRetention);\n return;\n }\n if (saved.data.owned) await args.remove();\n await args.store.set(args.key, null);\n};\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,4 +1,4 @@
1
- import { Delivery, TurnPolicy, type ChannelPlatformIface, type ConversationIface, type ConversationMessageIface, type ParticipantIface, type RouteArgsIface, type RouteDecisionIface } from '@finalai/agent-provider';
1
+ import { Delivery, TurnPolicy, type RoomPlatformIface, type ConversationIface, type ConversationMessageIface, type ParticipantIface, type RouteArgsIface, type RouteDecisionIface } from '@finalai/agent-provider';
2
2
  export declare const CONTEXT_WINDOW_MESSAGES = 50;
3
3
  export interface DeliveryOutcomeIface {
4
4
  participantId: string;
@@ -8,7 +8,7 @@ export interface DeliveryOutcomeIface {
8
8
  error: string | null;
9
9
  }
10
10
  export interface DeliverMessageArgsIface {
11
- platform: ChannelPlatformIface;
11
+ platform: RoomPlatformIface;
12
12
  route: (args: RouteArgsIface) => RouteDecisionIface[] | Promise<RouteDecisionIface[]>;
13
13
  conversation: ConversationIface;
14
14
  message: ConversationMessageIface;
@@ -24,14 +24,14 @@ export declare const audienceFor: (author: ParticipantIface | null, participants
24
24
  export declare const deliveryIdempotencyKey: (messageId: string, participantId: string) => string;
25
25
  export declare const deliverMessage: (args: DeliverMessageArgsIface) => Promise<DeliveryOutcomeIface[]>;
26
26
  export interface ObserveNoticeArgsIface {
27
- platform: ChannelPlatformIface;
27
+ platform: RoomPlatformIface;
28
28
  recipients: ParticipantIface[];
29
29
  text: string;
30
30
  idempotencyKey: string;
31
31
  }
32
32
  export declare const observeNotice: (args: ObserveNoticeArgsIface) => Promise<string[]>;
33
33
  export interface ReactionNoticeArgsIface {
34
- platform: ChannelPlatformIface;
34
+ platform: RoomPlatformIface;
35
35
  participants: ParticipantIface[];
36
36
  message: ConversationMessageIface;
37
37
  isLatestOfAuthor: boolean;
@@ -45,7 +45,7 @@ export declare enum MembershipChange {
45
45
  Left = "left"
46
46
  }
47
47
  export interface MembershipNoticeArgsIface {
48
- platform: ChannelPlatformIface;
48
+ platform: RoomPlatformIface;
49
49
  participants: ParticipantIface[];
50
50
  subject: ParticipantIface;
51
51
  change: MembershipChange;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/channels/delivery.ts"],
4
- "sourcesContent": ["import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';\nimport {\n Delivery,\n ParticipantKind,\n RoutingMode,\n TurnPolicy,\n type ChannelPlatformIface,\n type ConversationIface,\n type ConversationMessageIface,\n type ParticipantIface,\n type RouteArgsIface,\n type RouteDecisionIface,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport {\n authorPrefixedContent,\n buildPromptContent,\n catchUpFor,\n excerpt,\n finalAuthorOf,\n finalContextOf,\n messageRefOf,\n messageSummary,\n renderReplyContext,\n systemNoticeBlock,\n type ReplyContextArgsIface,\n} from \"./prompt.mjs\";\nimport { MAX_AGENT_HOP_DEPTH } from \"./routing.mjs\";\n\n// How a channel hands a conversation message to its agents: route, then build\n// each recipient's ACP content and call prompt or observe. Prompt construction,\n// not orchestration.\n\nconst thrownMessageSchema = z.object({ message: z.string() });\n\nexport const CONTEXT_WINDOW_MESSAGES = 50;\n\nexport interface DeliveryOutcomeIface {\n participantId: string;\n delivery: Delivery;\n // What actually happened: an observe can fall back to a silent prompt, or be\n // skipped for an agent with no session yet (its first prompt catches up).\n sent: 'prompt' | 'observe' | 'silent-prompt' | 'skipped' | 'failed';\n messageId: string | null;\n error: string | null;\n}\n\nexport interface DeliverMessageArgsIface {\n platform: ChannelPlatformIface;\n // A channel's pure route(), or a bundle channel's run in its isolate.\n route: (args: RouteArgsIface) => RouteDecisionIface[] | Promise<RouteDecisionIface[]>;\n conversation: ConversationIface;\n message: ConversationMessageIface;\n participants?: ParticipantIface[];\n // The delivering channel's turnPolicy.\n turnPolicy: TurnPolicy;\n}\n\n// A channel's policy applies unless the conversation is set to\n// queue, the per-conversation override.\nexport const effectiveTurnPolicy = (args: {\n channel: TurnPolicy;\n conversation: TurnPolicy;\n}): TurnPolicy => (args.conversation === TurnPolicy.Queue ? TurnPolicy.Queue : args.channel);\n\n// Every person in the room: who may answer what a turn no person prompted asks.\nexport const humanAudience = (participants: ParticipantIface[]): string[] =>\n participants\n .filter((participant) => !participant.left && participant.kind === ParticipantKind.User)\n .map((participant) => participant.id);\n\n// A person answers their own prompt; an agent's prompt is answered by the room's people.\nexport const audienceFor = (\n author: ParticipantIface | null,\n participants: ParticipantIface[],\n): string[] => (author?.kind === ParticipantKind.User ? [author.id] : humanAudience(participants));\n\n// Reply, thread and quote context. A referenced message outside the\n// recent window is read by id, however old.\nconst replyContextFor = async (args: {\n platform: ChannelPlatformIface;\n message: ConversationMessageIface;\n earlier: ConversationMessageIface[];\n participants: ParticipantIface[];\n}): Promise<ReplyContextArgsIface> => {\n const byId = new Map(args.earlier.map((message) => [message.messageId, message]));\n const referenced = [\n args.message.replyToMessageId,\n args.message.threadRootId,\n ...args.message.quotedMessageIds,\n ].flatMap((id) => (id == null || byId.has(id) ? [] : [id]));\n if (referenced.length > 0) {\n const found = await args.platform.conversations.getMessages({\n conversationId: args.message.conversationId,\n messageIds: [...new Set(referenced)],\n });\n for (const message of found) byId.set(message.messageId, message);\n }\n const replyTo =\n args.message.replyToMessageId == null\n ? null\n : (byId.get(args.message.replyToMessageId) ?? null);\n const root =\n args.message.threadRootId == null ? null : (byId.get(args.message.threadRootId) ?? null);\n const rootAuthor =\n root == null\n ? null\n : args.participants.find((participant) => participant.id === root.authorParticipantId);\n return {\n replyTo:\n replyTo == null || replyTo.messageId === root?.messageId\n ? null\n : messageRefOf(replyTo, args.participants),\n thread:\n args.message.threadRootId == null\n ? null\n : {\n rootMessageId: args.message.threadRootId,\n rootExcerpt: root == null ? '' : excerpt(messageSummary(root.content)),\n rootAuthor: rootAuthor == null ? null : finalAuthorOf(rootAuthor),\n },\n quotes: args.message.quotedMessageIds.flatMap((id) => {\n const quoted = byId.get(id);\n return quoted == null ? [] : [messageRefOf(quoted, args.participants)];\n }),\n };\n};\n\nexport const deliveryIdempotencyKey = (messageId: string, participantId: string): string =>\n `${messageId}:${participantId}`;\n\nexport const deliverMessage = async (\n args: DeliverMessageArgsIface,\n): Promise<DeliveryOutcomeIface[]> => {\n const { platform, conversation, message } = args;\n const participants =\n args.participants ?? (await platform.conversations.participants(conversation.conversationId));\n const decisions = await args.route({ conversation, message, participants });\n const author =\n participants.find((participant) => participant.id === message.authorParticipantId) ?? null;\n for (const participant of participants) {\n if (participant.kind !== ParticipantKind.Agent) continue;\n const decision = decisions.find((candidate) => candidate.participantId === participant.id);\n platform.log('channel route decision', {\n observed_at: new Date().toISOString(),\n organization_id: conversation.organizationId,\n conversation_id: conversation.conversationId,\n conversation_kind: conversation.kind,\n message_id: message.messageId,\n author_kind: author?.kind ?? '',\n participant_id: participant.id,\n routing_mode: participant.routingMode ?? '',\n delivery: decision?.delivery ?? 'skipped',\n reason: participant.left\n ? 'left conversation'\n : participant.id === message.authorParticipantId\n ? 'author'\n : decision == null\n ? 'not selected by route'\n : 'selected by route',\n hop_depth: message.hopDepth,\n });\n }\n if (decisions.length === 0) return [];\n const earlier = await platform.conversations.recentMessages({\n conversationId: conversation.conversationId,\n limit: CONTEXT_WINDOW_MESSAGES,\n beforeSeq: message.seq,\n });\n const replyContext = await replyContextFor({ platform, message, earlier, participants });\n const turnPolicy = effectiveTurnPolicy({\n channel: args.turnPolicy,\n conversation: conversation.turnPolicy,\n });\n const audience = audienceFor(author, participants);\n const authorParticipant: ParticipantIface = author ?? {\n id: message.authorParticipantId,\n kind: ParticipantKind.User,\n displayName: 'Someone',\n userId: null,\n definitionId: null,\n routingMode: null,\n sessionId: null,\n supportsObserve: null,\n rendersAuthorMeta: null,\n left: true,\n };\n\n const deliverOne = async (decision: RouteDecisionIface): Promise<DeliveryOutcomeIface> => {\n const recipient = participants.find((participant) => participant.id === decision.participantId);\n const base = { participantId: decision.participantId, delivery: decision.delivery };\n if (recipient == null) {\n return { ...base, sent: 'skipped', messageId: null, error: null };\n }\n const idempotencyKey = deliveryIdempotencyKey(message.messageId, recipient.id);\n const prompt = async (content: ContentBlock[], replyExpected: boolean): Promise<string> => {\n const result = await platform.sessions.prompt({\n participantId: recipient.id,\n content,\n turnPolicy,\n replyExpected,\n idempotencyKey,\n authorParticipantId: message.authorParticipantId,\n audienceParticipantIds: audience,\n sourceMessageId: message.messageId,\n });\n return result.messageId;\n };\n try {\n if (decision.delivery === Delivery.Prompt) {\n const content = buildPromptContent({\n message,\n author: authorParticipant,\n recipient,\n replyContext,\n catchUp: catchUpFor({ recipient, earlier, participants }),\n });\n return { ...base, sent: 'prompt', messageId: await prompt(content, true), error: null };\n }\n if (recipient.sessionId == null) {\n return { ...base, sent: 'skipped', messageId: null, error: null };\n }\n const contextBlock = renderReplyContext(replyContext);\n const content = [\n ...(contextBlock == null ? [] : [contextBlock]),\n ...authorPrefixedContent({\n content: message.content,\n author: finalAuthorOf(authorParticipant),\n rendersAuthorMeta: recipient.rendersAuthorMeta ?? false,\n context: finalContextOf(replyContext),\n }),\n ];\n if (recipient.supportsObserve === false) {\n if (message.hopDepth >= MAX_AGENT_HOP_DEPTH) {\n return { ...base, sent: 'skipped', messageId: null, error: null };\n }\n return {\n ...base,\n sent: 'silent-prompt',\n messageId: await prompt(content, false),\n error: null,\n };\n }\n await platform.sessions.observe({ participantId: recipient.id, content, idempotencyKey });\n return { ...base, sent: 'observe', messageId: null, error: null };\n } catch (error) {\n // JSON-RPC failures arrive as plain `{ code, message }` objects.\n const reason =\n error instanceof Error\n ? error.message\n : (thrownMessageSchema.safeParse(error).data?.message ?? String(error));\n platform.log('channel delivery failed', {\n conversationId: conversation.conversationId,\n messageId: message.messageId,\n participantId: recipient.id,\n delivery: decision.delivery,\n error: reason,\n });\n return { ...base, sent: 'failed', messageId: null, error: reason };\n }\n };\n\n return Promise.all(decisions.map(deliverOne));\n};\n\nexport interface ObserveNoticeArgsIface {\n platform: ChannelPlatformIface;\n recipients: ParticipantIface[];\n text: string;\n idempotencyKey: string;\n}\n\n// System blocks with no model call. Agents without a session skip them (their\n// first prompt catches up); agents without observe support skip them too, since\n// a silent prompt per reaction is not worth a model call.\nexport const observeNotice = async (args: ObserveNoticeArgsIface): Promise<string[]> => {\n const delivered: string[] = [];\n for (const recipient of args.recipients) {\n if (recipient.sessionId == null || recipient.supportsObserve === false || recipient.left)\n continue;\n try {\n await args.platform.sessions.observe({\n participantId: recipient.id,\n content: [systemNoticeBlock({ text: args.text })],\n idempotencyKey: `${args.idempotencyKey}:${recipient.id}`,\n });\n delivered.push(recipient.id);\n } catch (error) {\n args.platform.log('channel notice failed', {\n participantId: recipient.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n return delivered;\n};\n\nexport interface ReactionNoticeArgsIface {\n platform: ChannelPlatformIface;\n participants: ParticipantIface[];\n message: ConversationMessageIface;\n // Whether the reacted message is the agent's latest one.\n isLatestOfAuthor: boolean;\n reactorParticipantId: string;\n emoji: string;\n eventId?: string;\n}\n\n// Only the agent whose message was reacted to hears about it.\nexport const reactionNotice = async (args: ReactionNoticeArgsIface): Promise<string[]> => {\n const author = args.participants.find(\n (participant) => participant.id === args.message.authorParticipantId,\n );\n const reactor = args.participants.find(\n (participant) => participant.id === args.reactorParticipantId,\n );\n if (\n author == null ||\n author.kind !== ParticipantKind.Agent ||\n reactor == null ||\n reactor.id === author.id\n ) {\n return [];\n }\n const target = args.isLatestOfAuthor\n ? 'your last message'\n : `your message \"${excerpt(messageSummary(args.message.content), 120)}\"`;\n const reactionId = `reaction:${args.message.messageId}:${reactor.id}:${args.emoji}`;\n return observeNotice({\n platform: args.platform,\n recipients: [author],\n text: `${reactor.displayName} reacted ${args.emoji} to ${target}`,\n idempotencyKey: args.eventId == null ? reactionId : `${reactionId}:${args.eventId}`,\n });\n};\n\nexport enum MembershipChange {\n Joined = 'joined',\n Left = 'left',\n}\n\nexport interface MembershipNoticeArgsIface {\n platform: ChannelPlatformIface;\n participants: ParticipantIface[];\n subject: ParticipantIface;\n change: MembershipChange;\n idempotencyKey: string;\n}\n\n// Membership changes reach agents that follow the whole conversation.\nexport const membershipNotice = async (args: MembershipNoticeArgsIface): Promise<string[]> => {\n const recipients = args.participants.filter(\n (participant) =>\n participant.kind === ParticipantKind.Agent &&\n participant.id !== args.subject.id &&\n (participant.routingMode === RoutingMode.All ||\n participant.routingMode === RoutingMode.Default),\n );\n const verb = args.change === MembershipChange.Joined ? 'joined' : 'left';\n return observeNotice({\n platform: args.platform,\n recipients,\n text: `${args.subject.displayName} ${verb} the conversation`,\n idempotencyKey: args.idempotencyKey,\n });\n};\n"],
4
+ "sourcesContent": ["import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';\nimport {\n Delivery,\n ParticipantKind,\n RoutingMode,\n TurnPolicy,\n type RoomPlatformIface,\n type ConversationIface,\n type ConversationMessageIface,\n type ParticipantIface,\n type RouteArgsIface,\n type RouteDecisionIface,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport {\n authorPrefixedContent,\n buildPromptContent,\n catchUpFor,\n excerpt,\n finalAuthorOf,\n finalContextOf,\n messageRefOf,\n messageSummary,\n renderReplyContext,\n systemNoticeBlock,\n type ReplyContextArgsIface,\n} from \"./prompt.mjs\";\nimport { MAX_AGENT_HOP_DEPTH } from \"./routing.mjs\";\n\n// How a channel hands a conversation message to its agents: route, then build\n// each recipient's ACP content and call prompt or observe. Prompt construction,\n// not orchestration.\n\nconst thrownMessageSchema = z.object({ message: z.string() });\n\nexport const CONTEXT_WINDOW_MESSAGES = 50;\n\nexport interface DeliveryOutcomeIface {\n participantId: string;\n delivery: Delivery;\n // What actually happened: an observe can fall back to a silent prompt, or be\n // skipped for an agent with no session yet (its first prompt catches up).\n sent: 'prompt' | 'observe' | 'silent-prompt' | 'skipped' | 'failed';\n messageId: string | null;\n error: string | null;\n}\n\nexport interface DeliverMessageArgsIface {\n platform: RoomPlatformIface;\n // A channel's pure route(), or a bundle channel's run in its isolate.\n route: (args: RouteArgsIface) => RouteDecisionIface[] | Promise<RouteDecisionIface[]>;\n conversation: ConversationIface;\n message: ConversationMessageIface;\n participants?: ParticipantIface[];\n // The delivering channel's turnPolicy.\n turnPolicy: TurnPolicy;\n}\n\n// A channel's policy applies unless the conversation is set to\n// queue, the per-conversation override.\nexport const effectiveTurnPolicy = (args: {\n channel: TurnPolicy;\n conversation: TurnPolicy;\n}): TurnPolicy => (args.conversation === TurnPolicy.Queue ? TurnPolicy.Queue : args.channel);\n\n// Every person in the room: who may answer what a turn no person prompted asks.\nexport const humanAudience = (participants: ParticipantIface[]): string[] =>\n participants\n .filter((participant) => !participant.left && participant.kind === ParticipantKind.User)\n .map((participant) => participant.id);\n\n// A person answers their own prompt; an agent's prompt is answered by the room's people.\nexport const audienceFor = (\n author: ParticipantIface | null,\n participants: ParticipantIface[],\n): string[] => (author?.kind === ParticipantKind.User ? [author.id] : humanAudience(participants));\n\n// Reply, thread and quote context. A referenced message outside the\n// recent window is read by id, however old.\nconst replyContextFor = async (args: {\n platform: RoomPlatformIface;\n message: ConversationMessageIface;\n earlier: ConversationMessageIface[];\n participants: ParticipantIface[];\n}): Promise<ReplyContextArgsIface> => {\n const byId = new Map(args.earlier.map((message) => [message.messageId, message]));\n const referenced = [\n args.message.replyToMessageId,\n args.message.threadRootId,\n ...args.message.quotedMessageIds,\n ].flatMap((id) => (id == null || byId.has(id) ? [] : [id]));\n if (referenced.length > 0) {\n const found = await args.platform.conversations.getMessages({\n conversationId: args.message.conversationId,\n messageIds: [...new Set(referenced)],\n });\n for (const message of found) byId.set(message.messageId, message);\n }\n const replyTo =\n args.message.replyToMessageId == null\n ? null\n : (byId.get(args.message.replyToMessageId) ?? null);\n const root =\n args.message.threadRootId == null ? null : (byId.get(args.message.threadRootId) ?? null);\n const rootAuthor =\n root == null\n ? null\n : args.participants.find((participant) => participant.id === root.authorParticipantId);\n return {\n replyTo:\n replyTo == null || replyTo.messageId === root?.messageId\n ? null\n : messageRefOf(replyTo, args.participants),\n thread:\n args.message.threadRootId == null\n ? null\n : {\n rootMessageId: args.message.threadRootId,\n rootExcerpt: root == null ? '' : excerpt(messageSummary(root.content)),\n rootAuthor: rootAuthor == null ? null : finalAuthorOf(rootAuthor),\n },\n quotes: args.message.quotedMessageIds.flatMap((id) => {\n const quoted = byId.get(id);\n return quoted == null ? [] : [messageRefOf(quoted, args.participants)];\n }),\n };\n};\n\nexport const deliveryIdempotencyKey = (messageId: string, participantId: string): string =>\n `${messageId}:${participantId}`;\n\nexport const deliverMessage = async (\n args: DeliverMessageArgsIface,\n): Promise<DeliveryOutcomeIface[]> => {\n const { platform, conversation, message } = args;\n const participants =\n args.participants ?? (await platform.conversations.participants(conversation.conversationId));\n const decisions = await args.route({ conversation, message, participants });\n const author =\n participants.find((participant) => participant.id === message.authorParticipantId) ?? null;\n for (const participant of participants) {\n if (participant.kind !== ParticipantKind.Agent) continue;\n const decision = decisions.find((candidate) => candidate.participantId === participant.id);\n platform.log('channel route decision', {\n observed_at: new Date().toISOString(),\n organization_id: conversation.organizationId,\n conversation_id: conversation.conversationId,\n conversation_kind: conversation.kind,\n message_id: message.messageId,\n author_kind: author?.kind ?? '',\n participant_id: participant.id,\n routing_mode: participant.routingMode ?? '',\n delivery: decision?.delivery ?? 'skipped',\n reason: participant.left\n ? 'left conversation'\n : participant.id === message.authorParticipantId\n ? 'author'\n : decision == null\n ? 'not selected by route'\n : 'selected by route',\n hop_depth: message.hopDepth,\n });\n }\n if (decisions.length === 0) return [];\n const earlier = await platform.conversations.recentMessages({\n conversationId: conversation.conversationId,\n limit: CONTEXT_WINDOW_MESSAGES,\n beforeSeq: message.seq,\n });\n const replyContext = await replyContextFor({ platform, message, earlier, participants });\n const turnPolicy = effectiveTurnPolicy({\n channel: args.turnPolicy,\n conversation: conversation.turnPolicy,\n });\n const audience = audienceFor(author, participants);\n const authorParticipant: ParticipantIface = author ?? {\n id: message.authorParticipantId,\n kind: ParticipantKind.User,\n displayName: 'Someone',\n userId: null,\n definitionId: null,\n routingMode: null,\n sessionId: null,\n supportsObserve: null,\n rendersAuthorMeta: null,\n left: true,\n };\n\n const deliverOne = async (decision: RouteDecisionIface): Promise<DeliveryOutcomeIface> => {\n const recipient = participants.find((participant) => participant.id === decision.participantId);\n const base = { participantId: decision.participantId, delivery: decision.delivery };\n if (recipient == null) {\n return { ...base, sent: 'skipped', messageId: null, error: null };\n }\n const idempotencyKey = deliveryIdempotencyKey(message.messageId, recipient.id);\n const prompt = async (content: ContentBlock[], replyExpected: boolean): Promise<string> => {\n const result = await platform.sessions.prompt({\n participantId: recipient.id,\n content,\n turnPolicy,\n replyExpected,\n idempotencyKey,\n authorParticipantId: message.authorParticipantId,\n audienceParticipantIds: audience,\n sourceMessageId: message.messageId,\n });\n return result.messageId;\n };\n try {\n if (decision.delivery === Delivery.Prompt) {\n const content = buildPromptContent({\n message,\n author: authorParticipant,\n recipient,\n replyContext,\n catchUp: catchUpFor({ recipient, earlier, participants }),\n });\n return { ...base, sent: 'prompt', messageId: await prompt(content, true), error: null };\n }\n if (recipient.sessionId == null) {\n return { ...base, sent: 'skipped', messageId: null, error: null };\n }\n const contextBlock = renderReplyContext(replyContext);\n const content = [\n ...(contextBlock == null ? [] : [contextBlock]),\n ...authorPrefixedContent({\n content: message.content,\n author: finalAuthorOf(authorParticipant),\n rendersAuthorMeta: recipient.rendersAuthorMeta ?? false,\n context: finalContextOf(replyContext),\n }),\n ];\n if (recipient.supportsObserve === false) {\n if (message.hopDepth >= MAX_AGENT_HOP_DEPTH) {\n return { ...base, sent: 'skipped', messageId: null, error: null };\n }\n return {\n ...base,\n sent: 'silent-prompt',\n messageId: await prompt(content, false),\n error: null,\n };\n }\n await platform.sessions.observe({ participantId: recipient.id, content, idempotencyKey });\n return { ...base, sent: 'observe', messageId: null, error: null };\n } catch (error) {\n // JSON-RPC failures arrive as plain `{ code, message }` objects.\n const reason =\n error instanceof Error\n ? error.message\n : (thrownMessageSchema.safeParse(error).data?.message ?? String(error));\n platform.log('channel delivery failed', {\n conversationId: conversation.conversationId,\n messageId: message.messageId,\n participantId: recipient.id,\n delivery: decision.delivery,\n error: reason,\n });\n return { ...base, sent: 'failed', messageId: null, error: reason };\n }\n };\n\n return Promise.all(decisions.map(deliverOne));\n};\n\nexport interface ObserveNoticeArgsIface {\n platform: RoomPlatformIface;\n recipients: ParticipantIface[];\n text: string;\n idempotencyKey: string;\n}\n\n// System blocks with no model call. Agents without a session skip them (their\n// first prompt catches up); agents without observe support skip them too, since\n// a silent prompt per reaction is not worth a model call.\nexport const observeNotice = async (args: ObserveNoticeArgsIface): Promise<string[]> => {\n const delivered: string[] = [];\n for (const recipient of args.recipients) {\n if (recipient.sessionId == null || recipient.supportsObserve === false || recipient.left)\n continue;\n try {\n await args.platform.sessions.observe({\n participantId: recipient.id,\n content: [systemNoticeBlock({ text: args.text })],\n idempotencyKey: `${args.idempotencyKey}:${recipient.id}`,\n });\n delivered.push(recipient.id);\n } catch (error) {\n args.platform.log('channel notice failed', {\n participantId: recipient.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n return delivered;\n};\n\nexport interface ReactionNoticeArgsIface {\n platform: RoomPlatformIface;\n participants: ParticipantIface[];\n message: ConversationMessageIface;\n // Whether the reacted message is the agent's latest one.\n isLatestOfAuthor: boolean;\n reactorParticipantId: string;\n emoji: string;\n eventId?: string;\n}\n\n// Only the agent whose message was reacted to hears about it.\nexport const reactionNotice = async (args: ReactionNoticeArgsIface): Promise<string[]> => {\n const author = args.participants.find(\n (participant) => participant.id === args.message.authorParticipantId,\n );\n const reactor = args.participants.find(\n (participant) => participant.id === args.reactorParticipantId,\n );\n if (\n author == null ||\n author.kind !== ParticipantKind.Agent ||\n reactor == null ||\n reactor.id === author.id\n ) {\n return [];\n }\n const target = args.isLatestOfAuthor\n ? 'your last message'\n : `your message \"${excerpt(messageSummary(args.message.content), 120)}\"`;\n const reactionId = `reaction:${args.message.messageId}:${reactor.id}:${args.emoji}`;\n return observeNotice({\n platform: args.platform,\n recipients: [author],\n text: `${reactor.displayName} reacted ${args.emoji} to ${target}`,\n idempotencyKey: args.eventId == null ? reactionId : `${reactionId}:${args.eventId}`,\n });\n};\n\nexport enum MembershipChange {\n Joined = 'joined',\n Left = 'left',\n}\n\nexport interface MembershipNoticeArgsIface {\n platform: RoomPlatformIface;\n participants: ParticipantIface[];\n subject: ParticipantIface;\n change: MembershipChange;\n idempotencyKey: string;\n}\n\n// Membership changes reach agents that follow the whole conversation.\nexport const membershipNotice = async (args: MembershipNoticeArgsIface): Promise<string[]> => {\n const recipients = args.participants.filter(\n (participant) =>\n participant.kind === ParticipantKind.Agent &&\n participant.id !== args.subject.id &&\n (participant.routingMode === RoutingMode.All ||\n participant.routingMode === RoutingMode.Default),\n );\n const verb = args.change === MembershipChange.Joined ? 'joined' : 'left';\n return observeNotice({\n platform: args.platform,\n recipients,\n text: `${args.subject.displayName} ${verb} the conversation`,\n idempotencyKey: args.idempotencyKey,\n });\n};\n"],
5
5
  "mappings": "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",
6
6
  "names": ["content", "MembershipChange"]
7
7
  }
@@ -1,5 +1,5 @@
1
- import type { ChannelDefinitionIface, JsonValue } from '@finalai/agent-provider';
1
+ import type { RoomSurfaceDefinitionIface, JsonValue } from '@finalai/agent-provider';
2
2
  import { type MessagingChannelConfigIface } from "./messaging-channel.mjs";
3
3
  export type DirectMessagingChannelConfigIface = MessagingChannelConfigIface;
4
4
  export declare const DIRECT_MESSAGING_SPECIFIER = "@finalai/final-agent-provider/channels/direct-messaging";
5
- export declare const createDirectMessagingChannel: (config?: DirectMessagingChannelConfigIface) => ChannelDefinitionIface<JsonValue, JsonValue>;
5
+ export declare const createDirectMessagingChannel: (config?: DirectMessagingChannelConfigIface) => RoomSurfaceDefinitionIface<JsonValue, JsonValue>;