@finalai/final-agent-provider 0.6.2 → 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 (94) 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/config-fields.d.mts +6 -1
  66. package/dist/modules/harnesses/config-fields.mjs +7 -0
  67. package/dist/modules/harnesses/config-fields.mjs.map +3 -3
  68. package/dist/modules/harnesses/fidelity.mjs +17 -17
  69. package/dist/modules/harnesses/fidelity.mjs.map +2 -2
  70. package/dist/modules/harnesses/remote-claude-code.d.mts +14 -2
  71. package/dist/modules/harnesses/remote-claude-code.mjs +5 -3
  72. package/dist/modules/harnesses/remote-claude-code.mjs.map +2 -2
  73. package/dist/modules/harnesses/remote-url.mjs.map +1 -1
  74. package/dist/modules/harnesses/serverless-nanocode-codex.d.mts +3 -1
  75. package/dist/modules/harnesses/serverless-nanocode-codex.mjs +6 -0
  76. package/dist/modules/harnesses/serverless-nanocode-codex.mjs.map +2 -2
  77. package/dist/modules/runtime/remote-host.mjs.map +1 -1
  78. package/dist/modules/sandboxes/build-scripts.mjs.map +1 -1
  79. package/dist/modules/sandboxes/paths.mjs.map +1 -1
  80. package/dist/modules/testing/harness.mjs +2 -2
  81. package/dist/modules/testing/harness.mjs.map +1 -1
  82. package/package.json +45 -19
  83. package/dist/modules/channels/address-context.d.mts +0 -7
  84. package/dist/modules/channels/address-context.mjs +0 -80
  85. package/dist/modules/channels/address-context.mjs.map +0 -7
  86. package/dist/modules/channels/agent-events.d.mts +0 -103
  87. package/dist/modules/channels/agent-events.mjs +0 -341
  88. package/dist/modules/channels/agent-events.mjs.map +0 -7
  89. package/dist/modules/channels/agent-schedules.d.mts +0 -25
  90. package/dist/modules/channels/agent-schedules.mjs +0 -96
  91. package/dist/modules/channels/agent-schedules.mjs.map +0 -7
  92. package/dist/modules/channels/service-input.d.mts +0 -22
  93. package/dist/modules/channels/service-input.mjs +0 -32
  94. package/dist/modules/channels/service-input.mjs.map +0 -7
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@finalai/final-agent-provider",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "private": false,
5
- "description": "Final's provider package: serverless, remote and local harnesses; the computer and device sandboxes; the messaging, schedule and event channels.",
5
+ "description": "Final provider helpers for harnesses, computer and device sandboxes, the Final room surface, and external provider channels.",
6
6
  "license": "Apache-2.0",
7
7
  "files": [
8
8
  "dist",
@@ -101,16 +101,6 @@
101
101
  "import": "./dist/modules/channels/group-messaging.mjs",
102
102
  "default": "./dist/modules/channels/group-messaging.mjs"
103
103
  },
104
- "./channels/agent-schedules": {
105
- "types": "./dist/modules/channels/agent-schedules.d.mts",
106
- "import": "./dist/modules/channels/agent-schedules.mjs",
107
- "default": "./dist/modules/channels/agent-schedules.mjs"
108
- },
109
- "./channels/agent-events": {
110
- "types": "./dist/modules/channels/agent-events.d.mts",
111
- "import": "./dist/modules/channels/agent-events.mjs",
112
- "default": "./dist/modules/channels/agent-events.mjs"
113
- },
114
104
  "./package.json": "./package.json",
115
105
  "./channel-factories": {
116
106
  "types": "./dist/modules/channel-factories.d.mts",
@@ -211,6 +201,41 @@
211
201
  "types": "./dist/modules/sandboxes/egress-bootstrap.d.mts",
212
202
  "import": "./dist/modules/sandboxes/egress-bootstrap.mjs",
213
203
  "default": "./dist/modules/sandboxes/egress-bootstrap.mjs"
204
+ },
205
+ "./channels/slack": {
206
+ "types": "./dist/modules/channels/slack.d.mts",
207
+ "import": "./dist/modules/channels/slack.mjs",
208
+ "default": "./dist/modules/channels/slack.mjs"
209
+ },
210
+ "./channels/discord": {
211
+ "types": "./dist/modules/channels/discord.d.mts",
212
+ "import": "./dist/modules/channels/discord.mjs",
213
+ "default": "./dist/modules/channels/discord.mjs"
214
+ },
215
+ "./channels/github": {
216
+ "types": "./dist/modules/channels/github.d.mts",
217
+ "import": "./dist/modules/channels/github.mjs",
218
+ "default": "./dist/modules/channels/github.mjs"
219
+ },
220
+ "./channels/linear": {
221
+ "types": "./dist/modules/channels/linear.d.mts",
222
+ "import": "./dist/modules/channels/linear.mjs",
223
+ "default": "./dist/modules/channels/linear.mjs"
224
+ },
225
+ "./channels/webhook": {
226
+ "types": "./dist/modules/channels/webhook.d.mts",
227
+ "import": "./dist/modules/channels/webhook.mjs",
228
+ "default": "./dist/modules/channels/webhook.mjs"
229
+ },
230
+ "./channels/voice": {
231
+ "types": "./dist/modules/channels/voice.d.mts",
232
+ "import": "./dist/modules/channels/voice.mjs",
233
+ "default": "./dist/modules/channels/voice.mjs"
234
+ },
235
+ "./channels/phone": {
236
+ "types": "./dist/modules/channels/phone.d.mts",
237
+ "import": "./dist/modules/channels/phone.mjs",
238
+ "default": "./dist/modules/channels/phone.mjs"
214
239
  }
215
240
  },
216
241
  "publishConfig": {
@@ -219,14 +244,15 @@
219
244
  },
220
245
  "dependencies": {
221
246
  "@agentclientprotocol/sdk": "1.8.0",
222
- "@finalai/acp-session": "0.1.10",
223
- "@finalai/agent-protocol": "0.6.1",
224
- "@finalai/agent-provider": "0.5.1",
225
- "@finalai/computers": "0.3.0",
226
- "@finalai/websockets": "0.1.6",
247
+ "@finalai/acp-session": "0.1.11",
248
+ "@finalai/agent-protocol": "0.7.0",
249
+ "@finalai/agent-provider": "0.6.0",
250
+ "@finalai/computers": "0.3.1",
251
+ "@finalai/websockets": "0.1.7",
227
252
  "ws": "^8.21.3",
228
- "zod": "4.6.5",
229
- "@finalai/agent-definition": "0.5.1"
253
+ "zod": "4.6.6",
254
+ "@finalai/agent-definition": "0.6.0",
255
+ "gemoji": "^8.1.0"
230
256
  },
231
257
  "engines": {
232
258
  "node": ">=26"
@@ -1,7 +0,0 @@
1
- import { TurnPolicy, type ChannelAddressContextIface, type ChannelPlatformIface } from '@finalai/agent-provider';
2
- export interface FinalAddressContextArgsIface {
3
- platform: ChannelPlatformIface;
4
- definitionId: string;
5
- turnPolicy: TurnPolicy;
6
- }
7
- export declare const finalAddressContext: (args: FinalAddressContextArgsIface) => ChannelAddressContextIface;
@@ -1,80 +0,0 @@
1
- import {
2
- channelAccessors,
3
- ParticipantKind,
4
- TurnPolicy
5
- } from "@finalai/agent-provider";
6
- const finalAddressContext = (args) => {
7
- const generic = channelAccessors(args.platform);
8
- const operations = () => {
9
- if (args.platform.channelSessions == null)
10
- throw new Error("channel session operations are unavailable");
11
- return args.platform.channelSessions;
12
- };
13
- const agentIn = async (address) => {
14
- const participants = await args.platform.conversations.participants(address);
15
- return participants.find(
16
- (participant) => !participant.left && participant.kind === ParticipantKind.Agent && participant.definitionId === args.definitionId
17
- ) ?? null;
18
- };
19
- const handle = (address, pinnedId) => {
20
- const resolve = async () => {
21
- if (pinnedId != null) return generic.attachSession(pinnedId).resolveSession();
22
- const room = await address();
23
- return room == null ? null : (await agentIn(room))?.sessionId ?? null;
24
- };
25
- const existing = async () => {
26
- const sessionId = await resolve();
27
- if (sessionId == null) throw new Error("Final channel address has no session");
28
- return generic.attachSession(sessionId);
29
- };
30
- return {
31
- inspect: async () => {
32
- const sessionId = await resolve();
33
- return sessionId == null ? null : generic.attachSession(sessionId).inspect();
34
- },
35
- resolveSession: resolve,
36
- // Input to the room's agent from the sender's principal, never a message:
37
- // accepted at once, delivered by the platform, the reply captioned in the room.
38
- send: async (input) => {
39
- const room = await address();
40
- if (room == null) throw new Error("Final channel session has no room");
41
- const agent = await agentIn(room);
42
- if (agent == null) throw new Error("Final channel address is unavailable");
43
- if (pinnedId != null && agent.sessionId !== pinnedId)
44
- throw new Error("pinned Final session no longer owns this room");
45
- return operations().send(
46
- { conversationId: room, participantId: agent.id },
47
- {
48
- ...input,
49
- turnPolicy: input.turnPolicy ?? args.turnPolicy,
50
- source: input.source
51
- }
52
- );
53
- },
54
- respond: async (input) => (await existing()).respond(input),
55
- cancel: async () => {
56
- const sessionId = await resolve();
57
- return sessionId == null ? { accepted: false } : generic.attachSession(sessionId).cancel();
58
- },
59
- close: async () => {
60
- const sessionId = await resolve();
61
- if (sessionId != null) await generic.attachSession(sessionId).close();
62
- },
63
- observe: async (input) => (await existing()).observe(input),
64
- setConfigOption: async (input) => (await existing()).setConfigOption(input),
65
- park: async () => (await existing()).park(),
66
- getState: async () => (await existing()).getState(),
67
- setState: async (state) => (await existing()).setState(state)
68
- };
69
- };
70
- return {
71
- from: (address) => handle(async () => address),
72
- resolveSession: async (address) => (await agentIn(address))?.sessionId ?? null,
73
- attachSession: (id) => handle(async () => (await generic.attachSession(id).inspect())?.conversationId ?? null, id),
74
- to: generic.to
75
- };
76
- };
77
- export {
78
- finalAddressContext
79
- };
80
- //# sourceMappingURL=address-context.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../src/channels/address-context.ts"],
4
- "sourcesContent": ["import {\n channelAccessors,\n ParticipantKind,\n TurnPolicy,\n type ChannelAddressContextIface,\n type ChannelPlatformIface,\n type ChannelSessionHandleIface,\n} from '@finalai/agent-provider';\n\nexport interface FinalAddressContextArgsIface {\n platform: ChannelPlatformIface;\n definitionId: string;\n turnPolicy: TurnPolicy;\n}\n\n// Final addresses are room IDs. The existing participant/session association\n// owns the room's session; a pinned handle never follows a replacement.\nexport const finalAddressContext = (\n args: FinalAddressContextArgsIface,\n): ChannelAddressContextIface => {\n const generic = channelAccessors(args.platform);\n const operations = () => {\n if (args.platform.channelSessions == null)\n throw new Error('channel session operations are unavailable');\n return args.platform.channelSessions;\n };\n const agentIn = async (address: string) => {\n const participants = await args.platform.conversations.participants(address);\n return (\n participants.find(\n (participant) =>\n !participant.left &&\n participant.kind === ParticipantKind.Agent &&\n participant.definitionId === args.definitionId,\n ) ?? null\n );\n };\n const handle = (\n address: () => Promise<string | null>,\n pinnedId?: string,\n ): ChannelSessionHandleIface => {\n const resolve = async () => {\n if (pinnedId != null) return generic.attachSession(pinnedId).resolveSession();\n const room = await address();\n return room == null ? null : ((await agentIn(room))?.sessionId ?? null);\n };\n const existing = async () => {\n const sessionId = await resolve();\n if (sessionId == null) throw new Error('Final channel address has no session');\n return generic.attachSession(sessionId);\n };\n return {\n inspect: async () => {\n const sessionId = await resolve();\n return sessionId == null ? null : generic.attachSession(sessionId).inspect();\n },\n resolveSession: resolve,\n // Input to the room's agent from the sender's principal, never a message:\n // accepted at once, delivered by the platform, the reply captioned in the room.\n send: async (input) => {\n const room = await address();\n if (room == null) throw new Error('Final channel session has no room');\n const agent = await agentIn(room);\n if (agent == null) throw new Error('Final channel address is unavailable');\n if (pinnedId != null && agent.sessionId !== pinnedId)\n throw new Error('pinned Final session no longer owns this room');\n return operations().send(\n { conversationId: room, participantId: agent.id },\n {\n ...input,\n turnPolicy: input.turnPolicy ?? args.turnPolicy,\n source: input.source,\n },\n );\n },\n respond: async (input) => (await existing()).respond(input),\n cancel: async () => {\n const sessionId = await resolve();\n return sessionId == null ? { accepted: false } : generic.attachSession(sessionId).cancel();\n },\n close: async () => {\n const sessionId = await resolve();\n if (sessionId != null) await generic.attachSession(sessionId).close();\n },\n observe: async (input) => (await existing()).observe(input),\n setConfigOption: async (input) => (await existing()).setConfigOption(input),\n park: async () => (await existing()).park(),\n getState: async () => (await existing()).getState(),\n setState: async (state) => (await existing()).setState(state),\n };\n };\n return {\n from: (address) => handle(async () => address),\n resolveSession: async (address) => (await agentIn(address))?.sessionId ?? null,\n attachSession: (id) =>\n handle(async () => (await generic.attachSession(id).inspect())?.conversationId ?? null, id),\n to: generic.to,\n };\n};\n"],
5
- "mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAUA,MAAM,sBAAsB,CACjC,SAC+B;AAC/B,QAAM,UAAU,iBAAiB,KAAK,QAAQ;AAC9C,QAAM,aAAa,MAAM;AACvB,QAAI,KAAK,SAAS,mBAAmB;AACnC,YAAM,IAAI,MAAM,4CAA4C;AAC9D,WAAO,KAAK,SAAS;AAAA,EACvB;AACA,QAAM,UAAU,OAAO,YAAoB;AACzC,UAAM,eAAe,MAAM,KAAK,SAAS,cAAc,aAAa,OAAO;AAC3E,WACE,aAAa;AAAA,MACX,CAAC,gBACC,CAAC,YAAY,QACb,YAAY,SAAS,gBAAgB,SACrC,YAAY,iBAAiB,KAAK;AAAA,IACtC,KAAK;AAAA,EAET;AACA,QAAM,SAAS,CACb,SACA,aAC8B;AAC9B,UAAM,UAAU,YAAY;AAC1B,UAAI,YAAY,KAAM,QAAO,QAAQ,cAAc,QAAQ,EAAE,eAAe;AAC5E,YAAM,OAAO,MAAM,QAAQ;AAC3B,aAAO,QAAQ,OAAO,QAAS,MAAM,QAAQ,IAAI,IAAI,aAAa;AAAA,IACpE;AACA,UAAM,WAAW,YAAY;AAC3B,YAAM,YAAY,MAAM,QAAQ;AAChC,UAAI,aAAa,KAAM,OAAM,IAAI,MAAM,sCAAsC;AAC7E,aAAO,QAAQ,cAAc,SAAS;AAAA,IACxC;AACA,WAAO;AAAA,MACL,SAAS,YAAY;AACnB,cAAM,YAAY,MAAM,QAAQ;AAChC,eAAO,aAAa,OAAO,OAAO,QAAQ,cAAc,SAAS,EAAE,QAAQ;AAAA,MAC7E;AAAA,MACA,gBAAgB;AAAA;AAAA;AAAA,MAGhB,MAAM,OAAO,UAAU;AACrB,cAAM,OAAO,MAAM,QAAQ;AAC3B,YAAI,QAAQ,KAAM,OAAM,IAAI,MAAM,mCAAmC;AACrE,cAAM,QAAQ,MAAM,QAAQ,IAAI;AAChC,YAAI,SAAS,KAAM,OAAM,IAAI,MAAM,sCAAsC;AACzE,YAAI,YAAY,QAAQ,MAAM,cAAc;AAC1C,gBAAM,IAAI,MAAM,+CAA+C;AACjE,eAAO,WAAW,EAAE;AAAA,UAClB,EAAE,gBAAgB,MAAM,eAAe,MAAM,GAAG;AAAA,UAChD;AAAA,YACE,GAAG;AAAA,YACH,YAAY,MAAM,cAAc,KAAK;AAAA,YACrC,QAAQ,MAAM;AAAA,UAChB;AAAA,QACF;AAAA,MACF;AAAA,MACA,SAAS,OAAO,WAAW,MAAM,SAAS,GAAG,QAAQ,KAAK;AAAA,MAC1D,QAAQ,YAAY;AAClB,cAAM,YAAY,MAAM,QAAQ;AAChC,eAAO,aAAa,OAAO,EAAE,UAAU,MAAM,IAAI,QAAQ,cAAc,SAAS,EAAE,OAAO;AAAA,MAC3F;AAAA,MACA,OAAO,YAAY;AACjB,cAAM,YAAY,MAAM,QAAQ;AAChC,YAAI,aAAa,KAAM,OAAM,QAAQ,cAAc,SAAS,EAAE,MAAM;AAAA,MACtE;AAAA,MACA,SAAS,OAAO,WAAW,MAAM,SAAS,GAAG,QAAQ,KAAK;AAAA,MAC1D,iBAAiB,OAAO,WAAW,MAAM,SAAS,GAAG,gBAAgB,KAAK;AAAA,MAC1E,MAAM,aAAa,MAAM,SAAS,GAAG,KAAK;AAAA,MAC1C,UAAU,aAAa,MAAM,SAAS,GAAG,SAAS;AAAA,MAClD,UAAU,OAAO,WAAW,MAAM,SAAS,GAAG,SAAS,KAAK;AAAA,IAC9D;AAAA,EACF;AACA,SAAO;AAAA,IACL,MAAM,CAAC,YAAY,OAAO,YAAY,OAAO;AAAA,IAC7C,gBAAgB,OAAO,aAAa,MAAM,QAAQ,OAAO,IAAI,aAAa;AAAA,IAC1E,eAAe,CAAC,OACd,OAAO,aAAa,MAAM,QAAQ,cAAc,EAAE,EAAE,QAAQ,IAAI,kBAAkB,MAAM,EAAE;AAAA,IAC5F,IAAI,QAAQ;AAAA,EACd;AACF;",
6
- "names": []
7
- }
@@ -1,103 +0,0 @@
1
- import { SandboxEventKind, TurnPolicy, type ChannelDefinitionIface, type ChannelReceiveContextIface, type ConversationRefIface, type JsonValue, type RouteArgsIface, type RouteDecisionIface } from '@finalai/agent-provider';
2
- import * as z from 'zod';
3
- export declare const AGENT_EVENTS_SPECIFIER = "@finalai/final-agent-provider/channels/agent-events";
4
- export declare const AGENT_EVENTS_TURN_POLICY = TurnPolicy.Steer;
5
- export declare const WEBHOOK_SECRET_NAME = "webhook";
6
- export declare const WEBHOOK_BODY_MAX_CHARS = 8000;
7
- export declare enum AgentEventKind {
8
- Webhook = "webhook",
9
- LinkedConversation = "linked-conversation",
10
- Sandbox = "sandbox"
11
- }
12
- export { SandboxEventKind } from '@finalai/agent-provider';
13
- export declare const SYSTEM_EVENT_TEXT_MAX_CHARS = 4000;
14
- export declare enum LinkedConversationEvent {
15
- Replied = "replied",
16
- WaitingForPerson = "waiting_for_person",
17
- Failed = "failed"
18
- }
19
- export declare const LINKED_CONVERSATION_TEXT_MAX_CHARS = 4000;
20
- declare const linkedConversationEventSchema: z.ZodObject<{
21
- kind: z.ZodLiteral<AgentEventKind.LinkedConversation>;
22
- conversationId: z.ZodString;
23
- participantId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
24
- linkId: z.ZodString;
25
- linkedConversationId: z.ZodString;
26
- linkedTitle: z.ZodOptional<z.ZodNullable<z.ZodString>>;
27
- event: z.ZodEnum<typeof LinkedConversationEvent>;
28
- text: z.ZodOptional<z.ZodNullable<z.ZodString>>;
29
- eventId: z.ZodString;
30
- originTurnId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
31
- }, z.core.$strip>;
32
- declare const sandboxEventSchema: z.ZodObject<{
33
- kind: z.ZodLiteral<AgentEventKind.Sandbox>;
34
- conversationId: z.ZodString;
35
- participantId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
36
- bindingId: z.ZodString;
37
- sandboxName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
38
- event: z.ZodEnum<typeof SandboxEventKind>;
39
- processId: z.ZodString;
40
- command: z.ZodString;
41
- exitCode: z.ZodNullable<z.ZodNumber>;
42
- stdout: z.ZodOptional<z.ZodNullable<z.ZodString>>;
43
- stderr: z.ZodOptional<z.ZodNullable<z.ZodString>>;
44
- eventId: z.ZodString;
45
- }, z.core.$strip>;
46
- export declare const agentEventInboundSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
47
- kind: z.ZodLiteral<AgentEventKind.Webhook>;
48
- body: z.ZodString;
49
- contentType: z.ZodOptional<z.ZodNullable<z.ZodString>>;
50
- event: z.ZodOptional<z.ZodNullable<z.ZodString>>;
51
- deliveryId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
52
- }, z.core.$strip>, z.ZodObject<{
53
- kind: z.ZodLiteral<AgentEventKind.LinkedConversation>;
54
- conversationId: z.ZodString;
55
- participantId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
56
- linkId: z.ZodString;
57
- linkedConversationId: z.ZodString;
58
- linkedTitle: z.ZodOptional<z.ZodNullable<z.ZodString>>;
59
- event: z.ZodEnum<typeof LinkedConversationEvent>;
60
- text: z.ZodOptional<z.ZodNullable<z.ZodString>>;
61
- eventId: z.ZodString;
62
- originTurnId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
63
- }, z.core.$strip>, z.ZodObject<{
64
- kind: z.ZodLiteral<AgentEventKind.Sandbox>;
65
- conversationId: z.ZodString;
66
- participantId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
67
- bindingId: z.ZodString;
68
- sandboxName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
69
- event: z.ZodEnum<typeof SandboxEventKind>;
70
- processId: z.ZodString;
71
- command: z.ZodString;
72
- exitCode: z.ZodNullable<z.ZodNumber>;
73
- stdout: z.ZodOptional<z.ZodNullable<z.ZodString>>;
74
- stderr: z.ZodOptional<z.ZodNullable<z.ZodString>>;
75
- eventId: z.ZodString;
76
- }, z.core.$strip>], "kind">;
77
- export type SandboxEventInboundIface = z.infer<typeof sandboxEventSchema>;
78
- export type AgentEventInboundIface = z.infer<typeof agentEventInboundSchema>;
79
- export declare enum ReceiveTargetKind {
80
- BoundConversation = "bound-conversation",
81
- OwnerDirect = "owner-direct"
82
- }
83
- export declare const targetedInboundSchema: z.ZodObject<{
84
- target: z.ZodObject<{
85
- kind: z.ZodEnum<typeof ReceiveTargetKind>;
86
- }, z.core.$strip>;
87
- content: z.ZodArray<z.ZodType<import("@finalai/agent-provider").JsonObject, unknown, z.core.$ZodTypeInternals<import("@finalai/agent-provider").JsonObject, unknown>>>;
88
- deliveryId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
89
- }, z.core.$strip>;
90
- export type TargetedInboundIface = z.infer<typeof targetedInboundSchema>;
91
- export type ChannelReceiveIface = (input: JsonValue, ctx: ChannelReceiveContextIface) => Promise<ConversationRefIface>;
92
- export declare const createTargetedInputReceive: (turnPolicy?: TurnPolicy) => ChannelReceiveIface;
93
- export declare const renderLinkedConversationEvent: (event: Pick<z.infer<typeof linkedConversationEventSchema>, "linkedTitle" | "event" | "text">) => string;
94
- export declare const renderSandboxEvent: (event: Pick<SandboxEventInboundIface, "processId" | "command" | "exitCode" | "stdout" | "stderr">) => string;
95
- export declare const routeToOwner: (participantId: string) => (args: RouteArgsIface) => RouteDecisionIface[];
96
- export declare const receiveAgentEvent: ChannelReceiveIface;
97
- export declare const hmacSha256Hex: (secret: string, body: string) => Promise<string>;
98
- export declare const verifyWebhookSignature: (args: {
99
- secret: string;
100
- body: string;
101
- headers: Headers;
102
- }) => Promise<boolean>;
103
- export declare const createAgentEventsChannel: () => ChannelDefinitionIface<JsonValue, JsonValue>;
@@ -1,341 +0,0 @@
1
- import {
2
- defineChannel,
3
- Delivery,
4
- jsonObjectSchema,
5
- jsonValueSchema,
6
- ParticipantKind,
7
- SandboxEventKind,
8
- TurnPolicy
9
- } from "@finalai/agent-provider";
10
- import * as z from "zod";
11
- import { contentBlocksOf } from "./content.mjs";
12
- import { textBlock } from "./prompt.mjs";
13
- import { routeMessage } from "./routing.mjs";
14
- import { sendServiceInput } from "./service-input.mjs";
15
- const AGENT_EVENTS_SPECIFIER = "@finalai/final-agent-provider/channels/agent-events";
16
- const AGENT_EVENTS_TURN_POLICY = TurnPolicy.Steer;
17
- const WEBHOOK_SECRET_NAME = "webhook";
18
- const WEBHOOK_BODY_MAX_CHARS = 8e3;
19
- var AgentEventKind = /* @__PURE__ */ ((AgentEventKind2) => {
20
- AgentEventKind2["Webhook"] = "webhook";
21
- AgentEventKind2["LinkedConversation"] = "linked-conversation";
22
- AgentEventKind2["Sandbox"] = "sandbox";
23
- return AgentEventKind2;
24
- })(AgentEventKind || {});
25
- import { SandboxEventKind as SandboxEventKind2 } from "@finalai/agent-provider";
26
- const SYSTEM_EVENT_TEXT_MAX_CHARS = 4e3;
27
- var LinkedConversationEvent = /* @__PURE__ */ ((LinkedConversationEvent2) => {
28
- LinkedConversationEvent2["Replied"] = "replied";
29
- LinkedConversationEvent2["WaitingForPerson"] = "waiting_for_person";
30
- LinkedConversationEvent2["Failed"] = "failed";
31
- return LinkedConversationEvent2;
32
- })(LinkedConversationEvent || {});
33
- const LINKED_CONVERSATION_TEXT_MAX_CHARS = 4e3;
34
- const webhookEventSchema = z.object({
35
- kind: z.literal("webhook" /* Webhook */),
36
- body: z.string(),
37
- contentType: z.string().nullish(),
38
- // A provider event name when the sender gives one (`x-github-event`).
39
- event: z.string().nullish(),
40
- deliveryId: z.string().nullish()
41
- });
42
- const linkedConversationEventSchema = z.object({
43
- kind: z.literal("linked-conversation" /* LinkedConversation */),
44
- // The conversation that linked the other: where its agent is prompted.
45
- conversationId: z.string().min(1),
46
- // The agent that handed the work off; the Default agent when absent.
47
- participantId: z.string().min(1).nullish(),
48
- linkId: z.string().min(1),
49
- linkedConversationId: z.string().min(1),
50
- linkedTitle: z.string().nullish(),
51
- event: z.nativeEnum(LinkedConversationEvent),
52
- // The reply (Replied) or what the agent asks for (WaitingForPerson).
53
- text: z.string().nullish(),
54
- // Makes a redelivered event post once (the turn or decision).
55
- eventId: z.string().min(1),
56
- originTurnId: z.string().nullish()
57
- });
58
- const sandboxEventSchema = z.object({
59
- kind: z.literal("sandbox" /* Sandbox */),
60
- conversationId: z.string().min(1),
61
- // The agent participant whose session started the process: the only one told.
62
- participantId: z.string().min(1).nullish(),
63
- bindingId: z.string().min(1),
64
- sandboxName: z.string().nullish(),
65
- event: z.nativeEnum(SandboxEventKind),
66
- processId: z.string().min(1),
67
- command: z.string(),
68
- // Null when the sandbox could not report one (the process was lost).
69
- exitCode: z.number().int().nullable(),
70
- // The end of the process's output.
71
- stdout: z.string().nullish(),
72
- stderr: z.string().nullish(),
73
- // Makes a redelivered event post once (per binding).
74
- eventId: z.string().min(1)
75
- });
76
- const agentEventInboundSchema = z.discriminatedUnion("kind", [
77
- webhookEventSchema,
78
- linkedConversationEventSchema,
79
- sandboxEventSchema
80
- ]);
81
- var ReceiveTargetKind = /* @__PURE__ */ ((ReceiveTargetKind2) => {
82
- ReceiveTargetKind2["BoundConversation"] = "bound-conversation";
83
- ReceiveTargetKind2["OwnerDirect"] = "owner-direct";
84
- return ReceiveTargetKind2;
85
- })(ReceiveTargetKind || {});
86
- const targetedInboundSchema = z.object({
87
- target: z.object({ kind: z.nativeEnum(ReceiveTargetKind) }),
88
- content: z.array(jsonObjectSchema).min(1),
89
- // Makes a redelivered input count once.
90
- deliveryId: z.string().min(1).nullish()
91
- });
92
- const configSchema = z.looseObject({
93
- displayName: z.string().min(1).nullish(),
94
- // Owner direct conversation when the binding is attached to none; null for
95
- // organization-owned agents, whose webhooks need a bound conversation.
96
- ownerUserId: z.string().min(1).nullish()
97
- });
98
- const renderBody = (body, contentType) => {
99
- let rendered = body;
100
- if (contentType?.includes("json") === true) {
101
- try {
102
- rendered = JSON.stringify(jsonValueSchema.parse(JSON.parse(body)), null, 2);
103
- } catch {
104
- rendered = body;
105
- }
106
- }
107
- return rendered.length <= WEBHOOK_BODY_MAX_CHARS ? rendered : `${rendered.slice(0, WEBHOOK_BODY_MAX_CHARS)}
108
- \u2026 (truncated)`;
109
- };
110
- const boundConversation = async (ctx) => ctx.bindingId == null ? null : ctx.platform.conversations.boundTo(ctx.bindingId);
111
- const ownerDirectConversation = async (ctx) => {
112
- const config = configSchema.parse(ctx.config);
113
- if (config.ownerUserId == null || ctx.definitionId == null) {
114
- throw new Error(
115
- `channel binding ${ctx.bindingId ?? "(none)"} is attached to no conversation and its agent has no owner to post to`
116
- );
117
- }
118
- return ctx.platform.conversations.findOrCreateDirect({
119
- organizationId: ctx.organizationId,
120
- userId: config.ownerUserId,
121
- definitionId: ctx.definitionId
122
- });
123
- };
124
- const webhookConversation = async (ctx) => await boundConversation(ctx) ?? ownerDirectConversation(ctx);
125
- const targetConversation = async (target, ctx) => {
126
- if (target === "owner-direct" /* OwnerDirect */) return ownerDirectConversation(ctx);
127
- const bound = await boundConversation(ctx);
128
- if (bound == null) {
129
- throw new Error(`channel binding ${ctx.bindingId ?? "(none)"} is attached to no conversation`);
130
- }
131
- return bound;
132
- };
133
- const bindingAgent = async (conversation, ctx) => {
134
- const agent = (await ctx.platform.conversations.participants(conversation.conversationId)).find(
135
- (participant) => participant.kind === ParticipantKind.Agent && !participant.left && participant.definitionId === ctx.definitionId
136
- );
137
- if (agent == null) {
138
- throw new Error(
139
- `channel binding ${ctx.bindingId ?? "(none)"} has no agent in conversation ${conversation.conversationId}`
140
- );
141
- }
142
- return agent.id;
143
- };
144
- const sendBindingInput = async (args) => {
145
- const binding = args.ctx.bindingId ?? "none";
146
- const name = configSchema.parse(args.ctx.config).displayName ?? "Webhook";
147
- await sendServiceInput(args.ctx.platform, {
148
- room: args.conversation.conversationId,
149
- participantId: await bindingAgent(args.conversation, args.ctx),
150
- content: args.content,
151
- principal: { principalId: `binding:${binding}`, authenticator: "final" },
152
- displayName: name,
153
- idempotencyKey: `webhook:${binding}:${args.deliveryKey ?? crypto.randomUUID()}`,
154
- label: `From ${name}`,
155
- turnPolicy: args.turnPolicy
156
- });
157
- return { conversationId: args.conversation.conversationId };
158
- };
159
- const createTargetedInputReceive = (turnPolicy = TurnPolicy.Steer) => async (input, ctx) => {
160
- const targeted = targetedInboundSchema.parse(input);
161
- return sendBindingInput({
162
- ctx,
163
- conversation: await targetConversation(targeted.target.kind, ctx),
164
- content: contentBlocksOf(targeted.content),
165
- deliveryKey: targeted.deliveryId ?? null,
166
- turnPolicy
167
- });
168
- };
169
- const receiveTargetedInput = createTargetedInputReceive(AGENT_EVENTS_TURN_POLICY);
170
- const clip = (text) => text.length <= LINKED_CONVERSATION_TEXT_MAX_CHARS ? text : `${text.slice(0, LINKED_CONVERSATION_TEXT_MAX_CHARS)}
171
- \u2026 (truncated)`;
172
- const renderLinkedConversationEvent = (event) => {
173
- const title = event.linkedTitle?.trim() ?? "";
174
- const name = title.length === 0 ? "A linked conversation" : `"${title}"`;
175
- const text = event.text?.trim() ?? "";
176
- switch (event.event) {
177
- case "replied" /* Replied */:
178
- return text.length === 0 ? `${name} replied.` : `${name} replied:
179
-
180
- ${clip(text)}`;
181
- case "waiting_for_person" /* WaitingForPerson */:
182
- return text.length === 0 ? `${name} is waiting for a person.` : `${name} is waiting for a person: ${clip(text)}`;
183
- case "failed" /* Failed */:
184
- return text.length === 0 ? `${name} stopped with an error.` : `${name} stopped with an error.
185
-
186
- ${clip(text)}`;
187
- }
188
- };
189
- const receiveLinkedConversationEvent = async (event, ctx) => {
190
- const title = event.linkedTitle?.trim() ?? "";
191
- await sendServiceInput(ctx.platform, {
192
- room: event.conversationId,
193
- participantId: event.participantId ?? void 0,
194
- content: [textBlock(renderLinkedConversationEvent(event))],
195
- principal: { principalId: `link:${event.linkId}`, authenticator: "final" },
196
- displayName: title.length === 0 ? "Linked conversation" : title,
197
- originTurnId: event.originTurnId ?? void 0,
198
- idempotencyKey: `link:${event.linkId}:${event.eventId}`,
199
- label: title.length === 0 ? "From a linked conversation" : `From \u201C${title}\u201D`
200
- });
201
- return { conversationId: event.conversationId };
202
- };
203
- const clipEnd = (text) => text.length <= SYSTEM_EVENT_TEXT_MAX_CHARS ? text : `\u2026 (truncated)
204
- ${text.slice(text.length - SYSTEM_EVENT_TEXT_MAX_CHARS)}`;
205
- const outputSection = (label, output) => {
206
- const text = output?.trimEnd() ?? "";
207
- return text.length === 0 ? [] : [`${label}:
208
- \`\`\`
209
- ${clipEnd(text)}
210
- \`\`\``];
211
- };
212
- const renderSandboxEvent = (event) => {
213
- const outcome = event.exitCode == null ? "stopped without an exit code" : `exited with code ${event.exitCode}`;
214
- return [
215
- `Background process ${event.processId} (\`${event.command}\`) ${outcome}.`,
216
- ...outputSection("stdout", event.stdout),
217
- ...outputSection("stderr", event.stderr)
218
- ].join("\n\n");
219
- };
220
- const routeToOwner = (participantId) => (args) => args.participants.some(
221
- (participant) => participant.id === participantId && participant.kind === ParticipantKind.Agent && !participant.left
222
- ) ? [{ participantId, delivery: Delivery.Prompt }] : [];
223
- const nonEmptyOr = (value, fallback) => {
224
- const trimmed = value?.trim() ?? "";
225
- return trimmed.length === 0 ? fallback : trimmed;
226
- };
227
- const receiveSandboxEvent = async (event, ctx) => {
228
- const name = nonEmptyOr(event.sandboxName, "Sandbox");
229
- const args = {
230
- room: event.conversationId,
231
- participantId: event.participantId ?? void 0,
232
- content: [textBlock(renderSandboxEvent(event))],
233
- principal: { principalId: `sandbox:${event.bindingId}`, authenticator: "final" },
234
- displayName: name,
235
- idempotencyKey: `sandbox:${event.bindingId}:${event.eventId}`,
236
- label: `After a process in ${name} finished`
237
- };
238
- await sendServiceInput(ctx.platform, args);
239
- return { conversationId: event.conversationId };
240
- };
241
- const receiveAgentEvent = async (input, ctx) => {
242
- if (targetedInboundSchema.safeParse(input).success) return receiveTargetedInput(input, ctx);
243
- const event = agentEventInboundSchema.parse(input);
244
- if (event.kind === "webhook" /* Webhook */) {
245
- const name = configSchema.parse(ctx.config).displayName ?? "Webhook";
246
- const heading = event.event == null ? `${name} event` : `${name} event: ${event.event}`;
247
- return sendBindingInput({
248
- ctx,
249
- conversation: await webhookConversation(ctx),
250
- content: [textBlock(`${heading}
251
-
252
- ${renderBody(event.body, event.contentType ?? null)}`)],
253
- deliveryKey: event.deliveryId ?? null
254
- });
255
- }
256
- if (event.kind === "linked-conversation" /* LinkedConversation */) {
257
- return receiveLinkedConversationEvent(event, ctx);
258
- }
259
- return receiveSandboxEvent(event, ctx);
260
- };
261
- const hex = (bytes) => [...new Uint8Array(bytes)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
262
- const constantTimeEqual = (left, right) => {
263
- if (left.length !== right.length) return false;
264
- let difference = 0;
265
- for (let index = 0; index < left.length; index += 1) {
266
- difference |= left.charCodeAt(index) ^ right.charCodeAt(index);
267
- }
268
- return difference === 0;
269
- };
270
- const hmacSha256Hex = async (secret, body) => {
271
- const encoder = new TextEncoder();
272
- const key = await crypto.subtle.importKey(
273
- "raw",
274
- encoder.encode(secret),
275
- { name: "HMAC", hash: "SHA-256" },
276
- false,
277
- ["sign"]
278
- );
279
- return hex(await crypto.subtle.sign("HMAC", key, encoder.encode(body)));
280
- };
281
- const verifyWebhookSignature = async (args) => {
282
- const header = args.headers.get("x-final-signature") ?? args.headers.get("x-hub-signature-256");
283
- if (header == null) return false;
284
- const provided = header.startsWith("sha256=") ? header.slice("sha256=".length) : header;
285
- return constantTimeEqual(provided.toLowerCase(), await hmacSha256Hex(args.secret, args.body));
286
- };
287
- const webhookRoute = {
288
- method: "POST",
289
- path: "/",
290
- handler: async ({ request, secrets, receive }) => {
291
- const secret = await secrets.get(WEBHOOK_SECRET_NAME);
292
- if (secret == null) {
293
- return Response.json({ error: "webhook secret is not configured" }, { status: 503 });
294
- }
295
- const body = await request.text();
296
- if (!await verifyWebhookSignature({ secret, body, headers: request.headers })) {
297
- return Response.json({ error: "invalid signature" }, { status: 401 });
298
- }
299
- const ref = await receive({
300
- kind: "webhook" /* Webhook */,
301
- body,
302
- contentType: request.headers.get("content-type"),
303
- event: request.headers.get("x-github-event") ?? request.headers.get("x-final-event"),
304
- deliveryId: request.headers.get("x-github-delivery") ?? request.headers.get("x-final-delivery")
305
- });
306
- if (ref == null || !("conversationId" in ref))
307
- throw new Error("agent event channel returned no conversation");
308
- return Response.json({ ok: true, conversationId: ref.conversationId }, { status: 202 });
309
- }
310
- };
311
- const createAgentEventsChannel = () => defineChannel({
312
- routes: [webhookRoute],
313
- receive: receiveAgentEvent,
314
- route: routeMessage,
315
- turnPolicy: AGENT_EVENTS_TURN_POLICY,
316
- project: async () => {
317
- }
318
- });
319
- export {
320
- AGENT_EVENTS_SPECIFIER,
321
- AGENT_EVENTS_TURN_POLICY,
322
- AgentEventKind,
323
- LINKED_CONVERSATION_TEXT_MAX_CHARS,
324
- LinkedConversationEvent,
325
- ReceiveTargetKind,
326
- SYSTEM_EVENT_TEXT_MAX_CHARS,
327
- SandboxEventKind2 as SandboxEventKind,
328
- WEBHOOK_BODY_MAX_CHARS,
329
- WEBHOOK_SECRET_NAME,
330
- agentEventInboundSchema,
331
- createAgentEventsChannel,
332
- createTargetedInputReceive,
333
- hmacSha256Hex,
334
- receiveAgentEvent,
335
- renderLinkedConversationEvent,
336
- renderSandboxEvent,
337
- routeToOwner,
338
- targetedInboundSchema,
339
- verifyWebhookSignature
340
- };
341
- //# sourceMappingURL=agent-events.mjs.map