@finalai/final-agent-provider 0.6.3 → 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.
Files changed (85) hide show
  1. package/HOSTING.md +4 -4
  2. package/README.md +94 -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/paths.mjs.map +1 -1
  71. package/dist/modules/testing/harness.mjs +2 -2
  72. package/dist/modules/testing/harness.mjs.map +1 -1
  73. package/package.json +45 -19
  74. package/dist/modules/channels/address-context.d.mts +0 -7
  75. package/dist/modules/channels/address-context.mjs +0 -80
  76. package/dist/modules/channels/address-context.mjs.map +0 -7
  77. package/dist/modules/channels/agent-events.d.mts +0 -103
  78. package/dist/modules/channels/agent-events.mjs +0 -341
  79. package/dist/modules/channels/agent-events.mjs.map +0 -7
  80. package/dist/modules/channels/agent-schedules.d.mts +0 -25
  81. package/dist/modules/channels/agent-schedules.mjs +0 -96
  82. package/dist/modules/channels/agent-schedules.mjs.map +0 -7
  83. package/dist/modules/channels/service-input.d.mts +0 -22
  84. package/dist/modules/channels/service-input.mjs +0 -32
  85. package/dist/modules/channels/service-input.mjs.map +0 -7
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/channels/phone.ts"],
4
+ "sourcesContent": ["import {\n defineChannel,\n packageRef,\n type ChannelReceiveContextIface,\n type ChannelSocketIface,\n type JsonObject,\n type JsonValue,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport {\n channelBaseUrl,\n abortable,\n createQueue,\n parseJson,\n responseJson,\n stateKey,\n verifyHmac,\n} from \"./channel-utils.mjs\";\nimport { runVoiceConnection } from \"./voice.mjs\";\nconst configSchema = z.object({\n baseUrl: z.string().url().optional(),\n accountSid: z.string(),\n fromNumber: z.string(),\n voice: z.string().optional(),\n audience: z.union([z.literal('members'), z.literal('anyone')]).default('members'),\n});\ntype Config = z.infer<typeof configSchema>;\nconst activeLinkDeliveries = new Set<string>();\nconst xml = (value: string) =>\n value.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\nconst twiml = (body: string) =>\n new Response(`<Response>${body}</Response>`, { headers: { 'content-type': 'application/xml' } });\nconst callSchema = z.object({ CallSid: z.string(), From: z.string(), To: z.string() });\nconst signedRequest = async (\n request: Pick<Request, 'url' | 'headers'>,\n token: string,\n params: URLSearchParams,\n): Promise<boolean> => {\n const signature = request.headers.get('x-twilio-signature');\n if (signature == null) return false;\n const body =\n request.url +\n [...new Set(params.keys())]\n .sort()\n .flatMap((key) => [...new Set(params.getAll(key))].sort().map((value) => key + value))\n .join('');\n return verifyHmac({ secret: token, body, signature, algorithm: 'SHA-1', encoding: 'base64' });\n};\nconst hash = async (text: string) =>\n [...new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text)))]\n .map((value) => value.toString(16).padStart(2, '0'))\n .join('');\nconst toBase64 = (bytes: Uint8Array) => {\n let binary = '';\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary);\n};\nconst sendSms = async (\n ctx: ChannelReceiveContextIface<Config>,\n config: Config,\n to: string,\n body: string,\n): Promise<void> => {\n const token = await ctx.credentials.secret('twilio-auth-token');\n if (token == null) throw new Error('Twilio credential is unavailable');\n await responseJson(\n await ctx.credentials.fetch(\n `https://api.twilio.com/2010-04-01/Accounts/${config.accountSid}/Messages.json`,\n {\n method: 'POST',\n headers: { authorization: `Basic ${btoa(`${config.accountSid}:${token}`)}` },\n body: new URLSearchParams({ To: to, From: config.fromNumber, Body: body }),\n },\n ),\n );\n};\nconst streamResponse = async (\n ctx: ChannelReceiveContextIface<Config>,\n config: Config,\n call: z.infer<typeof callSchema>,\n announced: boolean,\n): Promise<Response> => {\n const nonce = crypto.randomUUID();\n await ctx.store.set(\n stateKey('twilio-call', nonce),\n { id: call.CallSid, from: call.From, to: call.To },\n { ttlSeconds: 600 },\n );\n const url = `${channelBaseUrl(config).replace(/^http/, 'ws')}/b/${ctx.binding.id}/stream`;\n return twiml(\n `${announced ? '' : '<Say>This call is recorded and saved in Final. You can hang up to end the recording.</Say>'}<Connect><Stream url=\"${xml(url)}\"><Parameter name=\"ticket\" value=\"${nonce}\" /></Stream></Connect>`,\n );\n};\nexport const phoneChannel = (options: JsonObject) => {\n const config = configSchema.parse(options);\n return defineChannel<Config, JsonValue, JsonValue>({\n name: 'phone',\n config,\n installed: packageRef('@finalai/final-agent-provider/channels/phone'),\n credentials: { app: null, secrets: ['twilio-auth-token'] },\n identity: {\n kind: 'challenge',\n deliver: async (args, ctx) =>\n sendSms(\n ctx,\n config,\n args.principal.id,\n `Your Final verification code is ${args.code}. It expires in 10 minutes.`,\n ),\n },\n places: {\n kinds: ['call', 'number'],\n defaults: (place) =>\n place.kind === 'call'\n ? {\n create: config.audience === 'anyone' ? 'group' : 'direct-with-agent',\n replies: 'origin',\n audience: config.audience,\n }\n : null,\n },\n receive: async () => ({ ignored: true, reason: 'phone-uses-stream' }),\n routes: [\n {\n method: 'POST',\n path: '/webhook',\n handler: async (ctx) => {\n const params = new URLSearchParams(await ctx.request.text());\n const token = await ctx.credentials.secret('twilio-auth-token');\n if (token == null || !(await signedRequest(ctx.request, token, params)))\n return new Response(null, { status: 401 });\n const call = callSchema.safeParse(Object.fromEntries(params));\n if (!call.success) return new Response('Invalid call', { status: 400 });\n const person = await ctx.people.resolve({ id: call.data.From });\n if (person.kind === 'blocked')\n return twiml('<Say>This account cannot use this line.</Say><Hangup/>');\n if (person.kind === 'unlinked') {\n if (config.audience === 'anyone') return streamResponse(ctx, config, call.data, false);\n if (!/^\\+[1-9]\\d{1,14}$/.test(call.data.From))\n return twiml(\n '<Say>Please call with caller ID enabled from a number that can receive SMS.</Say><Hangup/>',\n );\n if (activeLinkDeliveries.has(ctx.binding.id))\n return twiml(\n '<Say>Please wait a moment before requesting another link.</Say><Hangup/>',\n );\n activeLinkDeliveries.add(ctx.binding.id);\n try {\n const receipt = stateKey('twilio-link-call', call.data.CallSid);\n if ((await ctx.store.get(receipt)) == null) {\n const cooldown = stateKey('twilio-link-cooldown', call.data.From);\n if ((await ctx.store.get(cooldown)) != null)\n return twiml(\n '<Say>Please wait a minute before requesting another link.</Say><Hangup/>',\n );\n const window = Math.floor(Date.now() / 60_000);\n const budgetKey = stateKey('twilio-link-budget', ctx.binding.id);\n const budget = z\n .object({ window: z.number(), count: z.number() })\n .safeParse(await ctx.store.get(budgetKey));\n const count =\n budget.success && budget.data.window === window ? budget.data.count : 0;\n if (count >= 5)\n return twiml(\n '<Say>This line has reached its SMS limit. Please wait a minute and call again.</Say><Hangup/>',\n );\n await ctx.store.set(budgetKey, { window, count: count + 1 }, { ttlSeconds: 120 });\n await ctx.store.set(cooldown, true, { ttlSeconds: 60 });\n try {\n await sendSms(\n ctx,\n config,\n call.data.From,\n `Link this phone number to Final: ${person.linkUrl} Sign in, verify the SMS code, then call again.`,\n );\n } catch {\n return twiml(\n '<Say>The account link could not be sent. Please wait a minute and call again.</Say><Hangup/>',\n );\n }\n await ctx.store.set(receipt, true, { ttlSeconds: 600 });\n }\n return twiml(\n '<Say>A link was sent to your phone. Open it, sign in to Final and verify this number, then call again.</Say><Hangup/>',\n );\n } finally {\n activeLinkDeliveries.delete(ctx.binding.id);\n }\n }\n // A signed caller ID is routing data. Fresh SMS control proof is required before a member call.\n const key = stateKey('twilio-proof', call.data.CallSid);\n const saved = await ctx.store.get(key);\n if (saved == null) {\n const cooldown = stateKey('twilio-sms-cooldown', call.data.From);\n if ((await ctx.store.get(cooldown)) != null)\n return twiml(\n '<Say>Please wait a minute before requesting another verification code.</Say><Hangup/>',\n );\n await ctx.store.set(cooldown, true, { ttlSeconds: 60 });\n const code = String(crypto.getRandomValues(new Uint32Array(1))[0] % 1_000_000).padStart(\n 6,\n '0',\n );\n await ctx.store.set(\n key,\n { from: call.data.From, to: call.data.To, hash: await hash(code), attempts: 0 },\n { ttlSeconds: 300 },\n );\n try {\n await sendSms(\n ctx,\n config,\n call.data.From,\n `Enter ${code} on your Final call to verify it. This code is for this call only.`,\n );\n } catch {\n await ctx.store.set(key, null);\n return twiml(\n '<Say>The verification code could not be sent. Please wait a minute and call again.</Say><Hangup/>',\n );\n }\n }\n const action = `${channelBaseUrl(config)}/b/${ctx.binding.id}/verify`;\n return twiml(\n `<Say>This call will be recorded and saved to your Final chat after verification.</Say><Gather numDigits=\"6\" timeout=\"30\" action=\"${xml(action)}\" method=\"POST\"><Say>Enter the six digit code sent to your phone to continue.</Say></Gather><Hangup/>`,\n );\n },\n },\n {\n method: 'POST',\n path: '/verify',\n handler: async (ctx) => {\n const params = new URLSearchParams(await ctx.request.text());\n const token = await ctx.credentials.secret('twilio-auth-token');\n if (token == null || !(await signedRequest(ctx.request, token, params)))\n return new Response(null, { status: 401 });\n const call = callSchema\n .extend({ Digits: z.string().regex(/^\\d{6}$/) })\n .safeParse(Object.fromEntries(params));\n if (!call.success) return twiml('<Say>Verification failed.</Say><Hangup/>');\n const key = stateKey('twilio-proof', call.data.CallSid);\n const proof = z\n .object({ from: z.string(), to: z.string(), hash: z.string(), attempts: z.number() })\n .safeParse(await ctx.store.get(key));\n if (\n !proof.success ||\n proof.data.from !== call.data.From ||\n proof.data.to !== call.data.To ||\n proof.data.attempts >= 5\n )\n return twiml('<Say>This code has expired. Please call again.</Say><Hangup/>');\n if (proof.data.hash !== (await hash(call.data.Digits))) {\n await ctx.store.set(\n key,\n { ...proof.data, attempts: proof.data.attempts + 1 },\n { ttlSeconds: 300 },\n );\n return twiml('<Say>Verification failed. Please call again.</Say><Hangup/>');\n }\n const person = await ctx.people.resolve({ id: call.data.From });\n if (person.kind !== 'member')\n return twiml(\n '<Say>This number is no longer linked to an active account.</Say><Hangup/>',\n );\n await ctx.store.set(key, { ...proof.data, attempts: 5 }, { ttlSeconds: 300 });\n return streamResponse(ctx, config, call.data, true);\n },\n },\n {\n method: 'WS',\n path: '/stream',\n handler: async (ctx) => {\n const token = await ctx.credentials.secret('twilio-auth-token');\n if (token == null) return new Response(null, { status: 401 });\n const url = new URL(ctx.request.url);\n const socketUrl = new URL(url);\n socketUrl.protocol =\n url.protocol === 'http:' ? 'ws:' : url.protocol === 'https:' ? 'wss:' : url.protocol;\n const candidates = [\n ...new Set([\n url.href,\n socketUrl.href,\n url.href.replace(/\\/?$/, '/'),\n socketUrl.href.replace(/\\/?$/, '/'),\n ]),\n ];\n let verified = false;\n for (const candidate of candidates)\n if (\n await signedRequest(\n { url: candidate, headers: ctx.request.headers },\n token,\n new URLSearchParams(),\n )\n ) {\n verified = true;\n break;\n }\n if (!verified) return new Response(null, { status: 401 });\n ctx.socket.accept();\n const iterator = ctx.socket.messages[Symbol.asyncIterator]();\n let started: {\n callSid: string;\n streamSid: string;\n customParameters: { ticket: string };\n } | null = null;\n while (started == null) {\n const next = await iterator.next();\n if (next.done) return;\n const event = z\n .object({\n event: z.string(),\n start: z\n .object({\n callSid: z.string(),\n streamSid: z.string(),\n customParameters: z.object({ ticket: z.string() }),\n })\n .optional(),\n })\n .safeParse(typeof next.value === 'string' ? parseJson(next.value) : null);\n if (event.success && event.data.event === 'start' && event.data.start != null)\n started = event.data.start;\n }\n const savedKey = stateKey('twilio-call', started.customParameters.ticket);\n const saved = z\n .object({ id: z.string(), from: z.string(), to: z.string() })\n .safeParse(await ctx.store.get(savedKey));\n if (!saved.success || saved.data.id !== started.callSid) {\n ctx.socket.close(1008, 'Unknown call');\n return;\n }\n await ctx.store.set(savedKey, null);\n const messages = createQueue<string | Uint8Array>();\n const streamSid = started.streamSid;\n const finished = new AbortController();\n const signal = AbortSignal.any([ctx.socket.signal, finished.signal]);\n const incoming = { [Symbol.asyncIterator]: () => iterator };\n let sentAudio = false;\n const markName = crypto.randomUUID();\n const playbackDrained = Promise.withResolvers<void>();\n const closing: { request: { code?: number; reason?: string } | null } = { request: null };\n\n const input = (async () => {\n try {\n for await (const value of abortable(incoming, signal)) {\n const event = z\n .object({\n event: z.string(),\n media: z.object({ payload: z.string() }).optional(),\n mark: z.object({ name: z.string() }).optional(),\n })\n .safeParse(typeof value === 'string' ? parseJson(value) : null);\n if (!event.success) continue;\n if (event.data.event === 'mark' && event.data.mark?.name === markName)\n playbackDrained.resolve();\n if (event.data.event === 'media' && event.data.media != null)\n messages.push(\n Uint8Array.from(atob(event.data.media.payload), (char) => char.charCodeAt(0)),\n );\n if (event.data.event === 'stop') {\n messages.push(JSON.stringify({ type: 'stop' }));\n break;\n }\n }\n } finally {\n playbackDrained.resolve();\n messages.close();\n }\n })().catch(() => {\n ctx.socket.close(1011, 'Phone input failed');\n finished.abort();\n });\n const socket: ChannelSocketIface = {\n signal,\n messages: messages.stream,\n accept: () => undefined,\n close: (code, reason) => {\n if (!sentAudio) {\n ctx.socket.close(code, reason);\n return;\n }\n if (closing.request != null) return;\n closing.request = { code, reason };\n ctx.socket.send(\n JSON.stringify({ event: 'mark', streamSid, mark: { name: markName } }),\n );\n },\n send: (data) => {\n if (data instanceof Uint8Array) {\n sentAudio = true;\n ctx.socket.send(\n JSON.stringify({ event: 'media', streamSid, media: { payload: toBase64(data) } }),\n );\n } else {\n const control = z.object({ type: z.string() }).safeParse(parseJson(data));\n if (control.success && control.data.type === 'speech.start')\n ctx.socket.send(JSON.stringify({ event: 'clear', streamSid }));\n }\n },\n };\n try {\n await runVoiceConnection({\n ctx,\n socket,\n principal: { id: saved.data.from },\n place: {\n kind: 'call',\n id: saved.data.id,\n parent: { kind: 'number', id: saved.data.to },\n },\n encoding: 'mulaw',\n sampleRate: 8000,\n voice: config.voice,\n recording: true,\n });\n const requested = closing.request;\n if (requested != null && !signal.aborted) {\n const timer = setTimeout(() => playbackDrained.resolve(), 10_000);\n const abortDrain = () => playbackDrained.resolve();\n signal.addEventListener('abort', abortDrain, { once: true });\n if (signal.aborted) abortDrain();\n try {\n await playbackDrained.promise;\n } finally {\n clearTimeout(timer);\n signal.removeEventListener('abort', abortDrain);\n }\n ctx.socket.close(requested.code, requested.reason);\n }\n } finally {\n finished.abort();\n messages.close();\n await input;\n }\n },\n },\n ],\n });\n};\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/channels/projection.ts"],
4
- "sourcesContent": ["import type { ContentBlock, SessionUpdate } from '@agentclientprotocol/sdk/experimental/v2';\nimport { textOfBlock } from '@finalai/acp-session';\nimport {\n jsonObjectSchema,\n MessageKind,\n turnEndOf,\n TurnPolicy,\n type JsonValue,\n type ProjectionContextIface,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport { contentBlockOf, jsonOfContentBlock } from \"./content.mjs\";\n\n// Each ACP message ID owns a visible conversation message. The message is\n// created on its first visible chunk (streaming),\n// updated at most every `updateIntervalMs`, and finalized at the idle\n// state_update that ends the turn. The fold lives in the turn state the\n// platform keeps (ctx.state). An empty turn projects nothing.\n\nexport const DEFAULT_PROJECTION_UPDATE_INTERVAL_MS = 300;\n\nconst projectedMessageSchema = z.object({\n messageId: z.string().nullable().default(null),\n // The reply's conversation seq, so the finished reply is read back by\n // position however many messages arrived meanwhile.\n messageSeq: z.number().nullable().default(null),\n fromSeq: z.number().nullable().default(null),\n lastSeq: z.number().nullable().default(null),\n lastFlushAt: z.number().default(0),\n dirty: z.boolean().default(false),\n});\n\nconst segmentSchema = projectedMessageSchema.extend({\n acpMessageId: z.string(),\n blocks: z.array(jsonObjectSchema),\n});\n\n// Root message fields are retained for turns created by the old projector.\n// Those turns finish in their existing combined row; new turns use segment rows.\nexport const projectionStateSchema = projectedMessageSchema.extend({\n segments: z.array(segmentSchema),\n});\n\nexport type ProjectionStateIface = z.infer<typeof projectionStateSchema>;\n\nexport interface ProjectorConfigIface {\n updateIntervalMs?: number;\n now?: () => number;\n // The channel's turnPolicy, for routing finished replies (default steer).\n turnPolicy?: TurnPolicy;\n}\n\nconst agentChunkSchema = z.object({\n sessionUpdate: z.literal('agent_message_chunk'),\n messageId: z.string(),\n content: jsonObjectSchema,\n});\n\nconst agentMessageSchema = z.object({\n sessionUpdate: z.literal('agent_message'),\n messageId: z.string(),\n content: z.array(jsonObjectSchema).nullish(),\n});\n\nconst updateJson = (update: SessionUpdate): JsonValue => {\n const parsed = jsonObjectSchema.safeParse(JSON.parse(JSON.stringify(update)));\n return parsed.success ? parsed.data : null;\n};\n\nexport const emptyProjectionState = (): ProjectionStateIface => ({\n messageId: null,\n messageSeq: null,\n fromSeq: null,\n lastSeq: null,\n lastFlushAt: 0,\n dirty: false,\n segments: [],\n});\n\nexport const projectionStateOf = (state: JsonValue | null): ProjectionStateIface => {\n const parsed = projectionStateSchema.safeParse(state);\n return parsed.success ? parsed.data : emptyProjectionState();\n};\n\n// Plain text (no `_meta`) of a block, the only kind consecutive chunks merge into.\nconst plainText = (block: ContentBlock | undefined): string | null =>\n block == null || block._meta != null ? null : textOfBlock(block);\n\nconst appendBlock = (blocks: ContentBlock[], block: ContentBlock, separator: string): void => {\n const lastText = plainText(blocks.at(-1));\n const nextText = plainText(block);\n if (lastText != null && nextText != null) {\n blocks[blocks.length - 1] = { type: 'text', text: `${lastText}${separator}${nextText}` };\n } else {\n blocks.push(block);\n }\n};\n\n// The visible content of the turn: segments in order of first appearance,\n// separated by a blank line between text runs.\nexport const visibleContent = (state: Pick<ProjectionStateIface, 'segments'>): ContentBlock[] => {\n const content: ContentBlock[] = [];\n for (const segment of state.segments) {\n const blocks: ContentBlock[] = [];\n for (const block of segment.blocks) {\n const parsed = contentBlockOf(block);\n if (parsed != null) appendBlock(blocks, parsed, '');\n }\n for (const block of blocks) appendBlock(content, block, '\\n\\n');\n }\n return content.flatMap((block): ContentBlock[] => {\n const text = textOfBlock(block);\n if (text == null) return [block];\n const trimmed = text.trim();\n return trimmed.length === 0 ? [] : [{ ...block, type: 'text', text: trimmed }];\n });\n};\n\nconst segmentFor = (\n state: ProjectionStateIface,\n acpMessageId: string,\n): ProjectionStateIface['segments'][number] => {\n const existing = state.segments.find((segment) => segment.acpMessageId === acpMessageId);\n if (existing != null) {\n return existing;\n }\n const segment = segmentSchema.parse({ acpMessageId, blocks: [] });\n state.segments.push(segment);\n return segment;\n};\n\n// Folds one ACP update into the state and returns the affected segment, or null\n// for updates that are not visible output.\nexport const accumulateUpdate = (\n state: ProjectionStateIface,\n update: SessionUpdate,\n): ProjectionStateIface['segments'][number] | null => {\n const json = updateJson(update);\n const chunk = agentChunkSchema.safeParse(json);\n if (chunk.success) {\n const block = contentBlockOf(chunk.data.content);\n if (block == null) return null;\n const segment = segmentFor(state, chunk.data.messageId);\n segment.blocks.push(jsonOfContentBlock(block));\n return segment;\n }\n const message = agentMessageSchema.safeParse(json);\n if (message.success) {\n // ACP v2 upsert: a concrete array replaces the message's content; omitted leaves it.\n if (message.data.content === undefined) return null;\n const blocks = (message.data.content ?? []).flatMap((block) => {\n const parsed = contentBlockOf(block);\n return parsed == null ? [] : [jsonOfContentBlock(parsed)];\n });\n const segment = segmentFor(state, message.data.messageId);\n segment.blocks = blocks;\n return segment;\n }\n return null;\n};\n\ninterface ConversationProjectionContextIface extends ProjectionContextIface {\n conversation: NonNullable<ProjectionContextIface['conversation']>;\n agentParticipant: NonNullable<ProjectionContextIface['agentParticipant']>;\n}\n\nexport interface ProjectorIface {\n // ChannelDefinitionIface['project'] for the messaging channels: folds visible\n // output into separate messages and, at the turn-ending idle state_update,\n // finalizes and routes them in order of first appearance.\n project(update: SessionUpdate, ctx: ProjectionContextIface): Promise<void>;\n}\n\nexport const createProjector = (config: ProjectorConfigIface = {}): ProjectorIface => {\n const now = config.now ?? Date.now;\n const interval = config.updateIntervalMs ?? DEFAULT_PROJECTION_UPDATE_INTERVAL_MS;\n\n const fold = async (\n update: SessionUpdate,\n ctx: ConversationProjectionContextIface,\n ): Promise<void> => {\n const state = projectionStateOf(ctx.state.get());\n const segment = accumulateUpdate(state, update);\n if (state.messageId == null && (segment == null || segment.messageId == null)) {\n for (const previous of state.segments) {\n if (previous === segment || !previous.dirty || previous.messageId == null) continue;\n await ctx.platform.conversations.updateMessage({\n messageId: previous.messageId,\n content: visibleContent({ segments: [previous] }),\n final: false,\n });\n previous.lastFlushAt = now();\n previous.dirty = false;\n }\n }\n if (segment == null) {\n ctx.state.set(state);\n return;\n }\n const target = state.messageId == null ? segment : state;\n target.lastSeq = ctx.seq;\n const content = visibleContent({ segments: target === state ? state.segments : [segment] });\n if (content.length === 0 && target.messageId == null) {\n ctx.state.set(state);\n return;\n }\n if (target.messageId == null) {\n const message = await ctx.platform.conversations.postMessage({\n conversationId: ctx.conversation.conversationId,\n authorParticipantId: ctx.agentParticipant.id,\n kind: MessageKind.Message,\n content,\n source: { turnId: ctx.turn.id, fromSeq: ctx.seq, toSeq: ctx.seq },\n });\n target.messageId = message.messageId;\n target.messageSeq = message.seq;\n target.fromSeq = ctx.seq;\n target.lastFlushAt = now();\n target.dirty = false;\n } else if (now() - target.lastFlushAt >= interval) {\n await ctx.platform.conversations.updateMessage({\n messageId: target.messageId,\n content,\n final: false,\n });\n target.lastFlushAt = now();\n target.dirty = false;\n } else {\n target.dirty = true;\n }\n ctx.state.set(state);\n };\n\n const finish = async (ctx: ConversationProjectionContextIface): Promise<void> => {\n const state = projectionStateOf(ctx.state.get());\n if (state.messageId != null) {\n await finishMessage(ctx, state, visibleContent(state));\n return;\n }\n for (const segment of state.segments) {\n await finishMessage(ctx, segment, visibleContent({ segments: [segment] }));\n }\n };\n\n const finishMessage = async (\n ctx: ConversationProjectionContextIface,\n state: z.infer<typeof projectedMessageSchema>,\n content: ContentBlock[],\n ): Promise<void> => {\n if (state.messageId == null) {\n return;\n }\n await ctx.platform.conversations.updateMessage({\n messageId: state.messageId,\n content,\n final: true,\n });\n // The finished reply is routed like any message: other agents may observe\n // it or, when mentioned within the hop limit, be prompted.\n const reply =\n state.messageSeq == null\n ? null\n : (\n await ctx.platform.conversations.recentMessages({\n conversationId: ctx.conversation.conversationId,\n beforeSeq: state.messageSeq + 1,\n limit: 1,\n })\n ).find((message) => message.messageId === state.messageId);\n if (reply == null) {\n ctx.platform.log('finished reply not found for routing', {\n messageId: state.messageId,\n seq: state.messageSeq,\n });\n }\n if (reply != null) {\n if (ctx.platform.conversations.dispatchMessage == null)\n throw new Error('channel dispatch is unavailable');\n await ctx.platform.conversations.dispatchMessage({\n conversationId: reply.conversationId,\n messageId: reply.messageId,\n });\n }\n };\n\n const project: ProjectorIface['project'] = async (update, ctx) => {\n const { conversation, agentParticipant } = ctx;\n if (conversation == null || agentParticipant == null)\n throw new Error('Final rendering requires a Final conversation');\n const context = { ...ctx, conversation, agentParticipant };\n return turnEndOf(update) == null ? fold(update, context) : finish(context);\n };\n\n return { project };\n};\n"],
4
+ "sourcesContent": ["import type { ContentBlock, SessionUpdate } from '@agentclientprotocol/sdk/experimental/v2';\nimport { textOfBlock } from '@finalai/acp-session';\nimport {\n jsonObjectSchema,\n MessageKind,\n turnEndOf,\n TurnPolicy,\n type JsonValue,\n type ProjectionContextIface,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport { contentBlockOf, jsonOfContentBlock } from \"./content.mjs\";\n\n// Each ACP message ID owns a visible conversation message. The message is\n// created on its first visible chunk (streaming),\n// updated at most every `updateIntervalMs`, and finalized at the idle\n// state_update that ends the turn. The fold lives in the turn state the\n// platform keeps (ctx.state). An empty turn projects nothing.\n\nexport const DEFAULT_PROJECTION_UPDATE_INTERVAL_MS = 300;\n\nconst projectedMessageSchema = z.object({\n messageId: z.string().nullable().default(null),\n // The reply's conversation seq, so the finished reply is read back by\n // position however many messages arrived meanwhile.\n messageSeq: z.number().nullable().default(null),\n fromSeq: z.number().nullable().default(null),\n lastSeq: z.number().nullable().default(null),\n lastFlushAt: z.number().default(0),\n dirty: z.boolean().default(false),\n});\n\nconst segmentSchema = projectedMessageSchema.extend({\n acpMessageId: z.string(),\n blocks: z.array(jsonObjectSchema),\n});\n\n// Root message fields are retained for turns created by the old projector.\n// Those turns finish in their existing combined row; new turns use segment rows.\nexport const projectionStateSchema = projectedMessageSchema.extend({\n segments: z.array(segmentSchema),\n});\n\nexport type ProjectionStateIface = z.infer<typeof projectionStateSchema>;\n\nexport interface ProjectorConfigIface {\n updateIntervalMs?: number;\n now?: () => number;\n // The channel's turnPolicy, for routing finished replies (default steer).\n turnPolicy?: TurnPolicy;\n}\n\nconst agentChunkSchema = z.object({\n sessionUpdate: z.literal('agent_message_chunk'),\n messageId: z.string(),\n content: jsonObjectSchema,\n});\n\nconst agentMessageSchema = z.object({\n sessionUpdate: z.literal('agent_message'),\n messageId: z.string(),\n content: z.array(jsonObjectSchema).nullish(),\n});\n\nconst updateJson = (update: SessionUpdate): JsonValue => {\n const parsed = jsonObjectSchema.safeParse(JSON.parse(JSON.stringify(update)));\n return parsed.success ? parsed.data : null;\n};\n\nexport const emptyProjectionState = (): ProjectionStateIface => ({\n messageId: null,\n messageSeq: null,\n fromSeq: null,\n lastSeq: null,\n lastFlushAt: 0,\n dirty: false,\n segments: [],\n});\n\nexport const projectionStateOf = (state: JsonValue | null): ProjectionStateIface => {\n const parsed = projectionStateSchema.safeParse(state);\n return parsed.success ? parsed.data : emptyProjectionState();\n};\n\n// Plain text (no `_meta`) of a block, the only kind consecutive chunks merge into.\nconst plainText = (block: ContentBlock | undefined): string | null =>\n block == null || block._meta != null ? null : textOfBlock(block);\n\nconst appendBlock = (blocks: ContentBlock[], block: ContentBlock, separator: string): void => {\n const lastText = plainText(blocks.at(-1));\n const nextText = plainText(block);\n if (lastText != null && nextText != null) {\n blocks[blocks.length - 1] = { type: 'text', text: `${lastText}${separator}${nextText}` };\n } else {\n blocks.push(block);\n }\n};\n\n// The visible content of the turn: segments in order of first appearance,\n// separated by a blank line between text runs.\nexport const visibleContent = (state: Pick<ProjectionStateIface, 'segments'>): ContentBlock[] => {\n const content: ContentBlock[] = [];\n for (const segment of state.segments) {\n const blocks: ContentBlock[] = [];\n for (const block of segment.blocks) {\n const parsed = contentBlockOf(block);\n if (parsed != null) appendBlock(blocks, parsed, '');\n }\n for (const block of blocks) appendBlock(content, block, '\\n\\n');\n }\n return content.flatMap((block): ContentBlock[] => {\n const text = textOfBlock(block);\n if (text == null) return [block];\n const trimmed = text.trim();\n return trimmed.length === 0 ? [] : [{ ...block, type: 'text', text: trimmed }];\n });\n};\n\nconst segmentFor = (\n state: ProjectionStateIface,\n acpMessageId: string,\n): ProjectionStateIface['segments'][number] => {\n const existing = state.segments.find((segment) => segment.acpMessageId === acpMessageId);\n if (existing != null) {\n return existing;\n }\n const segment = segmentSchema.parse({ acpMessageId, blocks: [] });\n state.segments.push(segment);\n return segment;\n};\n\n// Folds one ACP update into the state and returns the affected segment, or null\n// for updates that are not visible output.\nexport const accumulateUpdate = (\n state: ProjectionStateIface,\n update: SessionUpdate,\n): ProjectionStateIface['segments'][number] | null => {\n const json = updateJson(update);\n const chunk = agentChunkSchema.safeParse(json);\n if (chunk.success) {\n const block = contentBlockOf(chunk.data.content);\n if (block == null) return null;\n const segment = segmentFor(state, chunk.data.messageId);\n segment.blocks.push(jsonOfContentBlock(block));\n return segment;\n }\n const message = agentMessageSchema.safeParse(json);\n if (message.success) {\n // ACP v2 upsert: a concrete array replaces the message's content; omitted leaves it.\n if (message.data.content === undefined) return null;\n const blocks = (message.data.content ?? []).flatMap((block) => {\n const parsed = contentBlockOf(block);\n return parsed == null ? [] : [jsonOfContentBlock(parsed)];\n });\n const segment = segmentFor(state, message.data.messageId);\n segment.blocks = blocks;\n return segment;\n }\n return null;\n};\n\ninterface ConversationProjectionContextIface extends ProjectionContextIface {\n conversation: NonNullable<ProjectionContextIface['conversation']>;\n agentParticipant: NonNullable<ProjectionContextIface['agentParticipant']>;\n}\n\nexport interface ProjectorIface {\n // RoomSurfaceDefinitionIface['project'] for the messaging channels: folds visible\n // output into separate messages and, at the turn-ending idle state_update,\n // finalizes and routes them in order of first appearance.\n project(update: SessionUpdate, ctx: ProjectionContextIface): Promise<void>;\n}\n\nexport const createProjector = (config: ProjectorConfigIface = {}): ProjectorIface => {\n const now = config.now ?? Date.now;\n const interval = config.updateIntervalMs ?? DEFAULT_PROJECTION_UPDATE_INTERVAL_MS;\n\n const fold = async (\n update: SessionUpdate,\n ctx: ConversationProjectionContextIface,\n ): Promise<void> => {\n const state = projectionStateOf(ctx.state.get());\n const segment = accumulateUpdate(state, update);\n if (state.messageId == null && (segment == null || segment.messageId == null)) {\n for (const previous of state.segments) {\n if (previous === segment || !previous.dirty || previous.messageId == null) continue;\n await ctx.platform.conversations.updateMessage({\n messageId: previous.messageId,\n content: visibleContent({ segments: [previous] }),\n final: false,\n });\n previous.lastFlushAt = now();\n previous.dirty = false;\n }\n }\n if (segment == null) {\n ctx.state.set(state);\n return;\n }\n const target = state.messageId == null ? segment : state;\n target.lastSeq = ctx.seq;\n const content = visibleContent({ segments: target === state ? state.segments : [segment] });\n if (content.length === 0 && target.messageId == null) {\n ctx.state.set(state);\n return;\n }\n if (target.messageId == null) {\n const message = await ctx.platform.conversations.postMessage({\n conversationId: ctx.conversation.conversationId,\n authorParticipantId: ctx.agentParticipant.id,\n kind: MessageKind.Message,\n content,\n source: { turnId: ctx.turn.id, fromSeq: ctx.seq, toSeq: ctx.seq },\n });\n target.messageId = message.messageId;\n target.messageSeq = message.seq;\n target.fromSeq = ctx.seq;\n target.lastFlushAt = now();\n target.dirty = false;\n } else if (now() - target.lastFlushAt >= interval) {\n await ctx.platform.conversations.updateMessage({\n messageId: target.messageId,\n content,\n final: false,\n });\n target.lastFlushAt = now();\n target.dirty = false;\n } else {\n target.dirty = true;\n }\n ctx.state.set(state);\n };\n\n const finish = async (ctx: ConversationProjectionContextIface): Promise<void> => {\n const state = projectionStateOf(ctx.state.get());\n if (state.messageId != null) {\n await finishMessage(ctx, state, visibleContent(state));\n return;\n }\n for (const segment of state.segments) {\n await finishMessage(ctx, segment, visibleContent({ segments: [segment] }));\n }\n };\n\n const finishMessage = async (\n ctx: ConversationProjectionContextIface,\n state: z.infer<typeof projectedMessageSchema>,\n content: ContentBlock[],\n ): Promise<void> => {\n if (state.messageId == null) {\n return;\n }\n await ctx.platform.conversations.updateMessage({\n messageId: state.messageId,\n content,\n final: true,\n });\n // The finished reply is routed like any message: other agents may observe\n // it or, when mentioned within the hop limit, be prompted.\n const reply =\n state.messageSeq == null\n ? null\n : (\n await ctx.platform.conversations.recentMessages({\n conversationId: ctx.conversation.conversationId,\n beforeSeq: state.messageSeq + 1,\n limit: 1,\n })\n ).find((message) => message.messageId === state.messageId);\n if (reply == null) {\n ctx.platform.log('finished reply not found for routing', {\n messageId: state.messageId,\n seq: state.messageSeq,\n });\n }\n if (reply != null) {\n if (ctx.platform.conversations.dispatchMessage == null)\n throw new Error('channel dispatch is unavailable');\n await ctx.platform.conversations.dispatchMessage({\n conversationId: reply.conversationId,\n messageId: reply.messageId,\n });\n }\n };\n\n const project: ProjectorIface['project'] = async (update, ctx) => {\n const { conversation, agentParticipant } = ctx;\n if (conversation == null || agentParticipant == null)\n throw new Error('Final rendering requires a Final conversation');\n const context = { ...ctx, conversation, agentParticipant };\n return turnEndOf(update) == null ? fold(update, context) : finish(context);\n };\n\n return { project };\n};\n"],
5
5
  "mappings": "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",
6
6
  "names": ["segment"]
7
7
  }
@@ -0,0 +1,31 @@
1
+ import type { ChannelReceiveContextIface, JsonValue, PlaceRef } from '@finalai/agent-provider';
2
+ import * as z from 'zod';
3
+ interface MirrorReceiptKeyArgsIface {
4
+ provider: string;
5
+ place: PlaceRef;
6
+ externalKey: string;
7
+ messageId?: string;
8
+ }
9
+ export declare const mirrorReceiptKey: (args: MirrorReceiptKeyArgsIface) => string | null;
10
+ export declare const deletedMirrorReceiptSchema: z.ZodObject<{
11
+ deleted: z.ZodLiteral<true>;
12
+ }, z.core.$strip>;
13
+ export type MirrorReactionResult = 'missing' | 'unavailable' | 'retry';
14
+ interface MirrorReactionArgsIface {
15
+ ctx: Pick<ChannelReceiveContextIface, 'store' | 'log'>;
16
+ key: string | null;
17
+ work(): Promise<void>;
18
+ classify(cause: unknown): Promise<MirrorReactionResult>;
19
+ }
20
+ export declare const settleMirrorReaction: (args: MirrorReactionArgsIface) => Promise<void>;
21
+ interface ReplyReceiptArgsIface {
22
+ bindingId: string;
23
+ store: ChannelReceiveContextIface['store'];
24
+ destination: string;
25
+ messageIds: readonly string[];
26
+ originKey: string;
27
+ send(key: string): Promise<void>;
28
+ removeDuplicate?(receipt: JsonValue, canonical: JsonValue): Promise<void>;
29
+ }
30
+ export declare const shareReplyReceipt: (args: ReplyReceiptArgsIface) => Promise<void>;
31
+ export {};
@@ -0,0 +1,63 @@
1
+ import * as z from "zod";
2
+ import { deliveryRetention, providerPlaceKey, stateKey } from "./channel-utils.mjs";
3
+ const mirrorReceiptKey = (args) => {
4
+ if (!args.externalKey.startsWith("final:")) return null;
5
+ const id = args.externalKey.slice(6);
6
+ if (id.length === 0 || id.length > 256 || args.messageId != null && args.messageId !== id)
7
+ return null;
8
+ return stateKey(`${args.provider}-mirror`, providerPlaceKey(args.place), id);
9
+ };
10
+ const deletedMirrorReceiptSchema = z.object({ deleted: z.literal(true) });
11
+ const settleMirrorReaction = async (args) => {
12
+ try {
13
+ await args.work();
14
+ } catch (cause) {
15
+ if (args.key == null) throw cause;
16
+ const result = await args.classify(cause);
17
+ if (result === "retry") throw cause;
18
+ if (result === "missing")
19
+ await args.ctx.store.set(args.key, { deleted: true }, deliveryRetention);
20
+ else args.ctx.log("Mirrored reaction unavailable.");
21
+ }
22
+ };
23
+ const activeReplies = /* @__PURE__ */ new Map();
24
+ const shareReplyReceipt = async (args) => {
25
+ const lockKey = stateKey(args.bindingId, args.destination);
26
+ const preceding = activeReplies.get(lockKey);
27
+ const settled = Promise.withResolvers();
28
+ activeReplies.set(lockKey, settled.promise);
29
+ await preceding;
30
+ try {
31
+ let canonical = args.originKey;
32
+ for (const messageId of args.messageIds) {
33
+ const saved = z.string().safeParse(await args.store.get(stateKey("reply-message", args.destination, messageId)));
34
+ if (saved.success && await args.store.get(saved.data) != null) {
35
+ canonical = saved.data;
36
+ break;
37
+ }
38
+ }
39
+ const duplicate = canonical === args.originKey ? null : await args.store.get(args.originKey);
40
+ await args.send(canonical);
41
+ const receipt = await args.store.get(canonical);
42
+ if (receipt == null) return;
43
+ if (duplicate != null) await args.removeDuplicate?.(duplicate, receipt);
44
+ if (canonical !== args.originKey)
45
+ await args.store.set(args.originKey, receipt, deliveryRetention);
46
+ for (const messageId of args.messageIds)
47
+ await args.store.set(
48
+ stateKey("reply-message", args.destination, messageId),
49
+ canonical,
50
+ deliveryRetention
51
+ );
52
+ } finally {
53
+ settled.resolve();
54
+ if (activeReplies.get(lockKey) === settled.promise) activeReplies.delete(lockKey);
55
+ }
56
+ };
57
+ export {
58
+ deletedMirrorReceiptSchema,
59
+ mirrorReceiptKey,
60
+ settleMirrorReaction,
61
+ shareReplyReceipt
62
+ };
63
+ //# sourceMappingURL=reply-receipts.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/channels/reply-receipts.ts"],
4
+ "sourcesContent": ["import type { ChannelReceiveContextIface, JsonValue, PlaceRef } from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport { deliveryRetention, providerPlaceKey, stateKey } from \"./channel-utils.mjs\";\n\ninterface MirrorReceiptKeyArgsIface {\n provider: string;\n place: PlaceRef;\n externalKey: string;\n messageId?: string;\n}\nexport const mirrorReceiptKey = (args: MirrorReceiptKeyArgsIface): string | null => {\n if (!args.externalKey.startsWith('final:')) return null;\n const id = args.externalKey.slice(6);\n if (id.length === 0 || id.length > 256 || (args.messageId != null && args.messageId !== id))\n return null;\n return stateKey(`${args.provider}-mirror`, providerPlaceKey(args.place), id);\n};\nexport const deletedMirrorReceiptSchema = z.object({ deleted: z.literal(true) });\nexport type MirrorReactionResult = 'missing' | 'unavailable' | 'retry';\ninterface MirrorReactionArgsIface {\n ctx: Pick<ChannelReceiveContextIface, 'store' | 'log'>;\n key: string | null;\n work(): Promise<void>;\n classify(cause: unknown): Promise<MirrorReactionResult>;\n}\nexport const settleMirrorReaction = async (args: MirrorReactionArgsIface): Promise<void> => {\n try {\n await args.work();\n } catch (cause) {\n if (args.key == null) throw cause;\n const result = await args.classify(cause);\n if (result === 'retry') throw cause;\n if (result === 'missing')\n await args.ctx.store.set(args.key, { deleted: true }, deliveryRetention);\n else args.ctx.log('Mirrored reaction unavailable.');\n }\n};\n\nconst activeReplies = new Map<string, Promise<void>>();\ninterface ReplyReceiptArgsIface {\n bindingId: string;\n store: ChannelReceiveContextIface['store'];\n destination: string;\n messageIds: readonly string[];\n originKey: string;\n send(key: string): Promise<void>;\n removeDuplicate?(receipt: JsonValue, canonical: JsonValue): Promise<void>;\n}\n// Several accepted prompts can share one reply. Keep each origin's mutation receipt.\nexport const shareReplyReceipt = async (args: ReplyReceiptArgsIface): Promise<void> => {\n const lockKey = stateKey(args.bindingId, args.destination);\n const preceding = activeReplies.get(lockKey);\n const settled = Promise.withResolvers<void>();\n activeReplies.set(lockKey, settled.promise);\n await preceding;\n try {\n let canonical = args.originKey;\n for (const messageId of args.messageIds) {\n const saved = z\n .string()\n .safeParse(await args.store.get(stateKey('reply-message', args.destination, messageId)));\n if (saved.success && (await args.store.get(saved.data)) != null) {\n canonical = saved.data;\n break;\n }\n }\n const duplicate = canonical === args.originKey ? null : await args.store.get(args.originKey);\n await args.send(canonical);\n const receipt = await args.store.get(canonical);\n if (receipt == null) return;\n if (duplicate != null) await args.removeDuplicate?.(duplicate, receipt);\n if (canonical !== args.originKey)\n await args.store.set(args.originKey, receipt, deliveryRetention);\n for (const messageId of args.messageIds)\n await args.store.set(\n stateKey('reply-message', args.destination, messageId),\n canonical,\n deliveryRetention,\n );\n } finally {\n settled.resolve();\n if (activeReplies.get(lockKey) === settled.promise) activeReplies.delete(lockKey);\n }\n};\n"],
5
+ "mappings": "AACA,YAAY,OAAO;AACnB,SAAS,mBAAmB,kBAAkB,gBAAgB;AAQvD,MAAM,mBAAmB,CAAC,SAAmD;AAClF,MAAI,CAAC,KAAK,YAAY,WAAW,QAAQ,EAAG,QAAO;AACnD,QAAM,KAAK,KAAK,YAAY,MAAM,CAAC;AACnC,MAAI,GAAG,WAAW,KAAK,GAAG,SAAS,OAAQ,KAAK,aAAa,QAAQ,KAAK,cAAc;AACtF,WAAO;AACT,SAAO,SAAS,GAAG,KAAK,QAAQ,WAAW,iBAAiB,KAAK,KAAK,GAAG,EAAE;AAC7E;AACO,MAAM,6BAA6B,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,IAAI,EAAE,CAAC;AAQxE,MAAM,uBAAuB,OAAO,SAAiD;AAC1F,MAAI;AACF,UAAM,KAAK,KAAK;AAAA,EAClB,SAAS,OAAO;AACd,QAAI,KAAK,OAAO,KAAM,OAAM;AAC5B,UAAM,SAAS,MAAM,KAAK,SAAS,KAAK;AACxC,QAAI,WAAW,QAAS,OAAM;AAC9B,QAAI,WAAW;AACb,YAAM,KAAK,IAAI,MAAM,IAAI,KAAK,KAAK,EAAE,SAAS,KAAK,GAAG,iBAAiB;AAAA,QACpE,MAAK,IAAI,IAAI,gCAAgC;AAAA,EACpD;AACF;AAEA,MAAM,gBAAgB,oBAAI,IAA2B;AAW9C,MAAM,oBAAoB,OAAO,SAA+C;AACrF,QAAM,UAAU,SAAS,KAAK,WAAW,KAAK,WAAW;AACzD,QAAM,YAAY,cAAc,IAAI,OAAO;AAC3C,QAAM,UAAU,QAAQ,cAAoB;AAC5C,gBAAc,IAAI,SAAS,QAAQ,OAAO;AAC1C,QAAM;AACN,MAAI;AACF,QAAI,YAAY,KAAK;AACrB,eAAW,aAAa,KAAK,YAAY;AACvC,YAAM,QAAQ,EACX,OAAO,EACP,UAAU,MAAM,KAAK,MAAM,IAAI,SAAS,iBAAiB,KAAK,aAAa,SAAS,CAAC,CAAC;AACzF,UAAI,MAAM,WAAY,MAAM,KAAK,MAAM,IAAI,MAAM,IAAI,KAAM,MAAM;AAC/D,oBAAY,MAAM;AAClB;AAAA,MACF;AAAA,IACF;AACA,UAAM,YAAY,cAAc,KAAK,YAAY,OAAO,MAAM,KAAK,MAAM,IAAI,KAAK,SAAS;AAC3F,UAAM,KAAK,KAAK,SAAS;AACzB,UAAM,UAAU,MAAM,KAAK,MAAM,IAAI,SAAS;AAC9C,QAAI,WAAW,KAAM;AACrB,QAAI,aAAa,KAAM,OAAM,KAAK,kBAAkB,WAAW,OAAO;AACtE,QAAI,cAAc,KAAK;AACrB,YAAM,KAAK,MAAM,IAAI,KAAK,WAAW,SAAS,iBAAiB;AACjE,eAAW,aAAa,KAAK;AAC3B,YAAM,KAAK,MAAM;AAAA,QACf,SAAS,iBAAiB,KAAK,aAAa,SAAS;AAAA,QACrD;AAAA,QACA;AAAA,MACF;AAAA,EACJ,UAAE;AACA,YAAQ,QAAQ;AAChB,QAAI,cAAc,IAAI,OAAO,MAAM,QAAQ,QAAS,eAAc,OAAO,OAAO;AAAA,EAClF;AACF;",
6
+ "names": []
7
+ }
@@ -2,3 +2,4 @@ import { type ParticipantIface, type RouteArgsIface, type RouteDecisionIface } f
2
2
  export declare const MAX_AGENT_HOP_DEPTH = 2;
3
3
  export declare const effectiveDefaultAgent: (participants: ParticipantIface[]) => ParticipantIface | null;
4
4
  export declare const routeMessage: (args: RouteArgsIface) => RouteDecisionIface[];
5
+ export declare const routeToParticipant: (participantId: string) => (args: RouteArgsIface) => RouteDecisionIface[];
@@ -41,9 +41,13 @@ const routeMessage = (args) => {
41
41
  return [{ participantId: agent.id, delivery: Delivery.Observe }];
42
42
  });
43
43
  };
44
+ const routeToParticipant = (participantId) => (args) => args.participants.some(
45
+ (participant) => participant.id === participantId && participant.kind === ParticipantKind.Agent && !participant.left
46
+ ) ? [{ participantId, delivery: Delivery.Prompt }] : [];
44
47
  export {
45
48
  MAX_AGENT_HOP_DEPTH,
46
49
  effectiveDefaultAgent,
47
- routeMessage
50
+ routeMessage,
51
+ routeToParticipant
48
52
  };
49
53
  //# sourceMappingURL=routing.mjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/channels/routing.ts"],
4
- "sourcesContent": ["import {\n ConversationKind,\n Delivery,\n ParticipantKind,\n RoutingMode,\n type ParticipantIface,\n type RouteArgsIface,\n type RouteDecisionIface,\n} from '@finalai/agent-provider/channel';\n\n// Agent-authored messages may prompt other agents only while the chain of\n// agent hops that produced them is shorter than this: a human\n// message (0) prompts A, A's reply (1) may prompt B, B's reply (2) prompts no one.\nexport const MAX_AGENT_HOP_DEPTH = 2;\n\nconst activeAgents = (participants: ParticipantIface[]): ParticipantIface[] =>\n participants.filter(\n (participant) => participant.kind === ParticipantKind.Agent && !participant.left,\n );\n\n// The agent a no-mention message goes to: the Default agent, else (no Default)\n// the earliest-joined All agent, which then acts as the Default. Participants\n// arrive in join order.\nexport const effectiveDefaultAgent = (\n participants: ParticipantIface[],\n): ParticipantIface | null => {\n const agents = activeAgents(participants);\n return (\n agents.find((agent) => agent.routingMode === RoutingMode.Default) ??\n agents.find((agent) => agent.routingMode === RoutingMode.All) ??\n null\n );\n};\n\n// Pure: who gets a message and how. Users never receive decisions.\nexport const routeMessage = (args: RouteArgsIface): RouteDecisionIface[] => {\n const { conversation, message, participants } = args;\n const agents = activeAgents(participants).filter(\n (agent) =>\n agent.id !== message.authorParticipantId &&\n (message.agentAuthorDefinitionId == null ||\n agent.definitionId !== message.agentAuthorDefinitionId),\n );\n if (agents.length === 0) {\n return [];\n }\n const mentioned = new Set(message.mentionedParticipantIds);\n const mayPrompt = message.hopDepth < MAX_AGENT_HOP_DEPTH;\n const promptDelivery = mayPrompt ? Delivery.Prompt : Delivery.Observe;\n\n if (conversation.kind === ConversationKind.Direct) {\n return agents.map((agent) => ({ participantId: agent.id, delivery: promptDelivery }));\n }\n\n const anyAgentMentioned = agents.some((agent) => mentioned.has(agent.id));\n const defaultAgent = effectiveDefaultAgent(participants);\n return agents.flatMap((agent): RouteDecisionIface[] => {\n if (mentioned.has(agent.id)) {\n return [{ participantId: agent.id, delivery: promptDelivery }];\n }\n if (agent.routingMode === RoutingMode.Mention || agent.routingMode == null) {\n return [];\n }\n if (!anyAgentMentioned && agent.id === defaultAgent?.id) {\n return [{ participantId: agent.id, delivery: promptDelivery }];\n }\n return [{ participantId: agent.id, delivery: Delivery.Observe }];\n });\n};\n"],
5
- "mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAKA,MAAM,sBAAsB;AAEnC,MAAM,eAAe,CAAC,iBACpB,aAAa;AAAA,EACX,CAAC,gBAAgB,YAAY,SAAS,gBAAgB,SAAS,CAAC,YAAY;AAC9E;AAKK,MAAM,wBAAwB,CACnC,iBAC4B;AAC5B,QAAM,SAAS,aAAa,YAAY;AACxC,SACE,OAAO,KAAK,CAAC,UAAU,MAAM,gBAAgB,YAAY,OAAO,KAChE,OAAO,KAAK,CAAC,UAAU,MAAM,gBAAgB,YAAY,GAAG,KAC5D;AAEJ;AAGO,MAAM,eAAe,CAAC,SAA+C;AAC1E,QAAM,EAAE,cAAc,SAAS,aAAa,IAAI;AAChD,QAAM,SAAS,aAAa,YAAY,EAAE;AAAA,IACxC,CAAC,UACC,MAAM,OAAO,QAAQ,wBACpB,QAAQ,2BAA2B,QAClC,MAAM,iBAAiB,QAAQ;AAAA,EACrC;AACA,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO,CAAC;AAAA,EACV;AACA,QAAM,YAAY,IAAI,IAAI,QAAQ,uBAAuB;AACzD,QAAM,YAAY,QAAQ,WAAW;AACrC,QAAM,iBAAiB,YAAY,SAAS,SAAS,SAAS;AAE9D,MAAI,aAAa,SAAS,iBAAiB,QAAQ;AACjD,WAAO,OAAO,IAAI,CAAC,WAAW,EAAE,eAAe,MAAM,IAAI,UAAU,eAAe,EAAE;AAAA,EACtF;AAEA,QAAM,oBAAoB,OAAO,KAAK,CAAC,UAAU,UAAU,IAAI,MAAM,EAAE,CAAC;AACxE,QAAM,eAAe,sBAAsB,YAAY;AACvD,SAAO,OAAO,QAAQ,CAAC,UAAgC;AACrD,QAAI,UAAU,IAAI,MAAM,EAAE,GAAG;AAC3B,aAAO,CAAC,EAAE,eAAe,MAAM,IAAI,UAAU,eAAe,CAAC;AAAA,IAC/D;AACA,QAAI,MAAM,gBAAgB,YAAY,WAAW,MAAM,eAAe,MAAM;AAC1E,aAAO,CAAC;AAAA,IACV;AACA,QAAI,CAAC,qBAAqB,MAAM,OAAO,cAAc,IAAI;AACvD,aAAO,CAAC,EAAE,eAAe,MAAM,IAAI,UAAU,eAAe,CAAC;AAAA,IAC/D;AACA,WAAO,CAAC,EAAE,eAAe,MAAM,IAAI,UAAU,SAAS,QAAQ,CAAC;AAAA,EACjE,CAAC;AACH;",
4
+ "sourcesContent": ["import {\n ConversationKind,\n Delivery,\n ParticipantKind,\n RoutingMode,\n type ParticipantIface,\n type RouteArgsIface,\n type RouteDecisionIface,\n} from '@finalai/agent-provider/channel';\n\n// Agent-authored messages may prompt other agents only while the chain of\n// agent hops that produced them is shorter than this: a human\n// message (0) prompts A, A's reply (1) may prompt B, B's reply (2) prompts no one.\nexport const MAX_AGENT_HOP_DEPTH = 2;\n\nconst activeAgents = (participants: ParticipantIface[]): ParticipantIface[] =>\n participants.filter(\n (participant) => participant.kind === ParticipantKind.Agent && !participant.left,\n );\n\n// The agent a no-mention message goes to: the Default agent, else (no Default)\n// the earliest-joined All agent, which then acts as the Default. Participants\n// arrive in join order.\nexport const effectiveDefaultAgent = (\n participants: ParticipantIface[],\n): ParticipantIface | null => {\n const agents = activeAgents(participants);\n return (\n agents.find((agent) => agent.routingMode === RoutingMode.Default) ??\n agents.find((agent) => agent.routingMode === RoutingMode.All) ??\n null\n );\n};\n\n// Pure: who gets a message and how. Users never receive decisions.\nexport const routeMessage = (args: RouteArgsIface): RouteDecisionIface[] => {\n const { conversation, message, participants } = args;\n const agents = activeAgents(participants).filter(\n (agent) =>\n agent.id !== message.authorParticipantId &&\n (message.agentAuthorDefinitionId == null ||\n agent.definitionId !== message.agentAuthorDefinitionId),\n );\n if (agents.length === 0) {\n return [];\n }\n const mentioned = new Set(message.mentionedParticipantIds);\n const mayPrompt = message.hopDepth < MAX_AGENT_HOP_DEPTH;\n const promptDelivery = mayPrompt ? Delivery.Prompt : Delivery.Observe;\n\n if (conversation.kind === ConversationKind.Direct) {\n return agents.map((agent) => ({ participantId: agent.id, delivery: promptDelivery }));\n }\n\n const anyAgentMentioned = agents.some((agent) => mentioned.has(agent.id));\n const defaultAgent = effectiveDefaultAgent(participants);\n return agents.flatMap((agent): RouteDecisionIface[] => {\n if (mentioned.has(agent.id)) {\n return [{ participantId: agent.id, delivery: promptDelivery }];\n }\n if (agent.routingMode === RoutingMode.Mention || agent.routingMode == null) {\n return [];\n }\n if (!anyAgentMentioned && agent.id === defaultAgent?.id) {\n return [{ participantId: agent.id, delivery: promptDelivery }];\n }\n return [{ participantId: agent.id, delivery: Delivery.Observe }];\n });\n};\n\nexport const routeToParticipant =\n (participantId: string) =>\n (args: RouteArgsIface): RouteDecisionIface[] =>\n args.participants.some(\n (participant) =>\n participant.id === participantId &&\n participant.kind === ParticipantKind.Agent &&\n !participant.left,\n )\n ? [{ participantId, delivery: Delivery.Prompt }]\n : [];\n"],
5
+ "mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAKA,MAAM,sBAAsB;AAEnC,MAAM,eAAe,CAAC,iBACpB,aAAa;AAAA,EACX,CAAC,gBAAgB,YAAY,SAAS,gBAAgB,SAAS,CAAC,YAAY;AAC9E;AAKK,MAAM,wBAAwB,CACnC,iBAC4B;AAC5B,QAAM,SAAS,aAAa,YAAY;AACxC,SACE,OAAO,KAAK,CAAC,UAAU,MAAM,gBAAgB,YAAY,OAAO,KAChE,OAAO,KAAK,CAAC,UAAU,MAAM,gBAAgB,YAAY,GAAG,KAC5D;AAEJ;AAGO,MAAM,eAAe,CAAC,SAA+C;AAC1E,QAAM,EAAE,cAAc,SAAS,aAAa,IAAI;AAChD,QAAM,SAAS,aAAa,YAAY,EAAE;AAAA,IACxC,CAAC,UACC,MAAM,OAAO,QAAQ,wBACpB,QAAQ,2BAA2B,QAClC,MAAM,iBAAiB,QAAQ;AAAA,EACrC;AACA,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO,CAAC;AAAA,EACV;AACA,QAAM,YAAY,IAAI,IAAI,QAAQ,uBAAuB;AACzD,QAAM,YAAY,QAAQ,WAAW;AACrC,QAAM,iBAAiB,YAAY,SAAS,SAAS,SAAS;AAE9D,MAAI,aAAa,SAAS,iBAAiB,QAAQ;AACjD,WAAO,OAAO,IAAI,CAAC,WAAW,EAAE,eAAe,MAAM,IAAI,UAAU,eAAe,EAAE;AAAA,EACtF;AAEA,QAAM,oBAAoB,OAAO,KAAK,CAAC,UAAU,UAAU,IAAI,MAAM,EAAE,CAAC;AACxE,QAAM,eAAe,sBAAsB,YAAY;AACvD,SAAO,OAAO,QAAQ,CAAC,UAAgC;AACrD,QAAI,UAAU,IAAI,MAAM,EAAE,GAAG;AAC3B,aAAO,CAAC,EAAE,eAAe,MAAM,IAAI,UAAU,eAAe,CAAC;AAAA,IAC/D;AACA,QAAI,MAAM,gBAAgB,YAAY,WAAW,MAAM,eAAe,MAAM;AAC1E,aAAO,CAAC;AAAA,IACV;AACA,QAAI,CAAC,qBAAqB,MAAM,OAAO,cAAc,IAAI;AACvD,aAAO,CAAC,EAAE,eAAe,MAAM,IAAI,UAAU,eAAe,CAAC;AAAA,IAC/D;AACA,WAAO,CAAC,EAAE,eAAe,MAAM,IAAI,UAAU,SAAS,QAAQ,CAAC;AAAA,EACjE,CAAC;AACH;AAEO,MAAM,qBACX,CAAC,kBACD,CAAC,SACC,KAAK,aAAa;AAAA,EAChB,CAAC,gBACC,YAAY,OAAO,iBACnB,YAAY,SAAS,gBAAgB,SACrC,CAAC,YAAY;AACjB,IACI,CAAC,EAAE,eAAe,UAAU,SAAS,OAAO,CAAC,IAC7C,CAAC;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,7 @@
1
+ import { type JsonObject, type JsonValue } from '@finalai/agent-provider';
2
+ export declare const slackChannel: (options?: JsonObject) => import("@finalai/agent-provider").ChannelDefinitionIface<{
3
+ history: "none" | "linked-members";
4
+ baseUrl?: string | undefined;
5
+ }, JsonValue, {
6
+ text: string;
7
+ }>;