@finalai/final-agent-provider 0.11.0 → 0.13.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 (71) hide show
  1. package/README.md +1 -14
  2. package/dist/modules/integrations.d.mts +0 -1
  3. package/dist/modules/integrations.mjs +0 -2
  4. package/dist/modules/integrations.mjs.map +2 -2
  5. package/dist/modules/sandboxes/computer.d.mts +2 -4
  6. package/dist/modules/sandboxes/computer.mjs +110 -480
  7. package/dist/modules/sandboxes/computer.mjs.map +2 -2
  8. package/dist/modules/sandboxes/computers-api.d.mts +6 -1
  9. package/dist/modules/sandboxes/computers-api.mjs.map +2 -2
  10. package/dist/modules/sandboxes/device-api.d.mts +8 -48
  11. package/dist/modules/sandboxes/device-api.mjs +2 -92
  12. package/dist/modules/sandboxes/device-api.mjs.map +2 -2
  13. package/dist/modules/sandboxes/device.d.mts +1 -1
  14. package/dist/modules/sandboxes/device.mjs.map +2 -2
  15. package/dist/modules/sandboxes/environment.d.mts +4 -0
  16. package/dist/modules/sandboxes/environment.mjs.map +2 -2
  17. package/dist/modules/sandboxes/local.d.mts +2 -1
  18. package/dist/modules/sandboxes/local.mjs +64 -8
  19. package/dist/modules/sandboxes/local.mjs.map +2 -2
  20. package/dist/modules/sandboxes/session.mjs +0 -1
  21. package/dist/modules/sandboxes/session.mjs.map +2 -2
  22. package/package.json +5 -57
  23. package/WORKSPACE-EGRESS.md +0 -184
  24. package/dist/modules/authoring-final-channel.d.mts +0 -3
  25. package/dist/modules/authoring-final-channel.mjs +0 -10
  26. package/dist/modules/authoring-final-channel.mjs.map +0 -7
  27. package/dist/modules/channel-factories.d.mts +0 -40
  28. package/dist/modules/channel-factories.mjs +0 -212
  29. package/dist/modules/channel-factories.mjs.map +0 -7
  30. package/dist/modules/channels/content.d.mts +0 -6
  31. package/dist/modules/channels/content.mjs +0 -31
  32. package/dist/modules/channels/content.mjs.map +0 -7
  33. package/dist/modules/channels/delivery.d.mts +0 -54
  34. package/dist/modules/channels/delivery.mjs +0 -243
  35. package/dist/modules/channels/delivery.mjs.map +0 -7
  36. package/dist/modules/channels/direct-messaging.d.mts +0 -5
  37. package/dist/modules/channels/direct-messaging.mjs +0 -8
  38. package/dist/modules/channels/direct-messaging.mjs.map +0 -7
  39. package/dist/modules/channels/fake-platform.d.mts +0 -36
  40. package/dist/modules/channels/fake-platform.mjs +0 -168
  41. package/dist/modules/channels/fake-platform.mjs.map +0 -7
  42. package/dist/modules/channels/group-messaging.d.mts +0 -5
  43. package/dist/modules/channels/group-messaging.mjs +0 -8
  44. package/dist/modules/channels/group-messaging.mjs.map +0 -7
  45. package/dist/modules/channels/inbound.d.mts +0 -52
  46. package/dist/modules/channels/inbound.mjs +0 -42
  47. package/dist/modules/channels/inbound.mjs.map +0 -7
  48. package/dist/modules/channels/index.d.mts +0 -9
  49. package/dist/modules/channels/index.mjs +0 -10
  50. package/dist/modules/channels/index.mjs.map +0 -7
  51. package/dist/modules/channels/messaging-channel.d.mts +0 -7
  52. package/dist/modules/channels/messaging-channel.mjs +0 -99
  53. package/dist/modules/channels/messaging-channel.mjs.map +0 -7
  54. package/dist/modules/channels/projection.d.mts +0 -36
  55. package/dist/modules/channels/projection.mjs +0 -233
  56. package/dist/modules/channels/projection.mjs.map +0 -7
  57. package/dist/modules/channels/prompt.d.mts +0 -72
  58. package/dist/modules/channels/prompt.mjs +0 -208
  59. package/dist/modules/channels/prompt.mjs.map +0 -7
  60. package/dist/modules/channels/routing.d.mts +0 -5
  61. package/dist/modules/channels/routing.mjs +0 -53
  62. package/dist/modules/channels/routing.mjs.map +0 -7
  63. package/dist/modules/sandboxes/access.d.mts +0 -34
  64. package/dist/modules/sandboxes/access.mjs +0 -15
  65. package/dist/modules/sandboxes/access.mjs.map +0 -7
  66. package/dist/modules/sandboxes/egress-bootstrap.d.mts +0 -3
  67. package/dist/modules/sandboxes/egress-bootstrap.mjs +0 -929
  68. package/dist/modules/sandboxes/egress-bootstrap.mjs.map +0 -7
  69. package/dist/modules/sandboxes/egress.d.mts +0 -94
  70. package/dist/modules/sandboxes/egress.mjs +0 -1561
  71. package/dist/modules/sandboxes/egress.mjs.map +0 -7
@@ -1,52 +0,0 @@
1
- import * as z from 'zod';
2
- import { MembershipChange } from "./delivery.mjs";
3
- export declare const messageInboundSchema: z.ZodObject<{
4
- conversationId: z.ZodString;
5
- authorParticipantId: z.ZodString;
6
- kind: z.ZodDefault<z.ZodEnum<typeof import("@finalai/agent-provider").MessageKind>>;
7
- content: z.ZodArray<z.ZodType<import("@finalai/agent-provider").JsonObject, unknown, z.core.$ZodTypeInternals<import("@finalai/agent-provider").JsonObject, unknown>>>;
8
- clientMessageId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
9
- replyToMessageId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
10
- threadRootId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
11
- quotedMessageIds: z.ZodDefault<z.ZodArray<z.ZodString>>;
12
- mentionedParticipantIds: z.ZodDefault<z.ZodArray<z.ZodString>>;
13
- }, z.core.$strip>;
14
- export type MessageInboundIface = z.infer<typeof messageInboundSchema>;
15
- export declare enum ConversationEventKind {
16
- Reaction = "reaction",
17
- Membership = "membership"
18
- }
19
- export declare const reactionInboundSchema: z.ZodObject<{
20
- event: z.ZodLiteral<ConversationEventKind.Reaction>;
21
- conversationId: z.ZodString;
22
- messageId: z.ZodString;
23
- messageSeq: z.ZodNumber;
24
- latestOfAuthor: z.ZodBoolean;
25
- reactorParticipantId: z.ZodString;
26
- emoji: z.ZodString;
27
- eventId: z.ZodOptional<z.ZodString>;
28
- }, z.core.$strip>;
29
- export declare const membershipInboundSchema: z.ZodObject<{
30
- event: z.ZodLiteral<ConversationEventKind.Membership>;
31
- conversationId: z.ZodString;
32
- participantId: z.ZodString;
33
- change: z.ZodEnum<typeof MembershipChange>;
34
- eventId: z.ZodString;
35
- }, z.core.$strip>;
36
- export declare const conversationEventInboundSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
37
- event: z.ZodLiteral<ConversationEventKind.Reaction>;
38
- conversationId: z.ZodString;
39
- messageId: z.ZodString;
40
- messageSeq: z.ZodNumber;
41
- latestOfAuthor: z.ZodBoolean;
42
- reactorParticipantId: z.ZodString;
43
- emoji: z.ZodString;
44
- eventId: z.ZodOptional<z.ZodString>;
45
- }, z.core.$strip>, z.ZodObject<{
46
- event: z.ZodLiteral<ConversationEventKind.Membership>;
47
- conversationId: z.ZodString;
48
- participantId: z.ZodString;
49
- change: z.ZodEnum<typeof MembershipChange>;
50
- eventId: z.ZodString;
51
- }, z.core.$strip>], "event">;
52
- export type ConversationEventInboundIface = z.infer<typeof conversationEventInboundSchema>;
@@ -1,42 +0,0 @@
1
- import { roomMessageInputSchema } from "@finalai/agent-provider";
2
- import * as z from "zod";
3
- import { MembershipChange } from "./delivery.mjs";
4
- const messageInboundSchema = roomMessageInputSchema;
5
- var ConversationEventKind = /* @__PURE__ */ ((ConversationEventKind2) => {
6
- ConversationEventKind2["Reaction"] = "reaction";
7
- ConversationEventKind2["Membership"] = "membership";
8
- return ConversationEventKind2;
9
- })(ConversationEventKind || {});
10
- const reactionInboundSchema = z.object({
11
- event: z.literal("reaction" /* Reaction */),
12
- conversationId: z.string().min(1),
13
- messageId: z.string().min(1),
14
- // The reacted message's position, so it is read back however old it is.
15
- messageSeq: z.number().int().positive(),
16
- // Whether it is its author's newest message ("your last message").
17
- latestOfAuthor: z.boolean(),
18
- reactorParticipantId: z.string().min(1),
19
- emoji: z.string().min(1),
20
- // A new persisted message revision distinguishes removing and re-adding a reaction.
21
- eventId: z.string().min(1).optional()
22
- });
23
- const membershipInboundSchema = z.object({
24
- event: z.literal("membership" /* Membership */),
25
- conversationId: z.string().min(1),
26
- participantId: z.string().min(1),
27
- change: z.nativeEnum(MembershipChange),
28
- // Distinguishes repeated joins and leaves of one participant.
29
- eventId: z.string().min(1)
30
- });
31
- const conversationEventInboundSchema = z.discriminatedUnion("event", [
32
- reactionInboundSchema,
33
- membershipInboundSchema
34
- ]);
35
- export {
36
- ConversationEventKind,
37
- conversationEventInboundSchema,
38
- membershipInboundSchema,
39
- messageInboundSchema,
40
- reactionInboundSchema
41
- };
42
- //# sourceMappingURL=inbound.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../src/channels/inbound.ts"],
4
- "sourcesContent": ["import { roomMessageInputSchema } from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport { MembershipChange } from \"./delivery.mjs\";\n\n// What the messaging channels receive: a message for an existing conversation,\n// authored by one of its participants (the HTTP API, an isolate callback).\nexport const messageInboundSchema = roomMessageInputSchema;\n\nexport type MessageInboundIface = z.infer<typeof messageInboundSchema>;\n\nexport enum ConversationEventKind {\n Reaction = 'reaction',\n Membership = 'membership',\n}\n\n// Conversation events that reach agents as observe-only system blocks.\nexport const reactionInboundSchema = z.object({\n event: z.literal(ConversationEventKind.Reaction),\n conversationId: z.string().min(1),\n messageId: z.string().min(1),\n // The reacted message's position, so it is read back however old it is.\n messageSeq: z.number().int().positive(),\n // Whether it is its author's newest message (\"your last message\").\n latestOfAuthor: z.boolean(),\n reactorParticipantId: z.string().min(1),\n emoji: z.string().min(1),\n // A new persisted message revision distinguishes removing and re-adding a reaction.\n eventId: z.string().min(1).optional(),\n});\n\nexport const membershipInboundSchema = z.object({\n event: z.literal(ConversationEventKind.Membership),\n conversationId: z.string().min(1),\n participantId: z.string().min(1),\n change: z.nativeEnum(MembershipChange),\n // Distinguishes repeated joins and leaves of one participant.\n eventId: z.string().min(1),\n});\n\nexport const conversationEventInboundSchema = z.discriminatedUnion('event', [\n reactionInboundSchema,\n membershipInboundSchema,\n]);\n\nexport type ConversationEventInboundIface = z.infer<typeof conversationEventInboundSchema>;\n"],
5
- "mappings": "AAAA,SAAS,8BAA8B;AACvC,YAAY,OAAO;AACnB,SAAS,wBAAwB;AAI1B,MAAM,uBAAuB;AAI7B,IAAK,wBAAL,kBAAKA,2BAAL;AACL,EAAAA,uBAAA,cAAW;AACX,EAAAA,uBAAA,gBAAa;AAFH,SAAAA;AAAA,GAAA;AAML,MAAM,wBAAwB,EAAE,OAAO;AAAA,EAC5C,OAAO,EAAE,QAAQ,yBAA8B;AAAA,EAC/C,gBAAgB,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAChC,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,EAE3B,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA;AAAA,EAEtC,gBAAgB,EAAE,QAAQ;AAAA,EAC1B,sBAAsB,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,EAEvB,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AACtC,CAAC;AAEM,MAAM,0BAA0B,EAAE,OAAO;AAAA,EAC9C,OAAO,EAAE,QAAQ,6BAAgC;AAAA,EACjD,gBAAgB,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAChC,eAAe,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/B,QAAQ,EAAE,WAAW,gBAAgB;AAAA;AAAA,EAErC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC;AAC3B,CAAC;AAEM,MAAM,iCAAiC,EAAE,mBAAmB,SAAS;AAAA,EAC1E;AAAA,EACA;AACF,CAAC;",
6
- "names": ["ConversationEventKind"]
7
- }
@@ -1,9 +0,0 @@
1
- export * from "./content.mjs";
2
- export * from "./delivery.mjs";
3
- export * from "./direct-messaging.mjs";
4
- export * from "./group-messaging.mjs";
5
- export * from "./inbound.mjs";
6
- export * from "./messaging-channel.mjs";
7
- export * from "./projection.mjs";
8
- export * from "./prompt.mjs";
9
- export * from "./routing.mjs";
@@ -1,10 +0,0 @@
1
- export * from "./content.mjs";
2
- export * from "./delivery.mjs";
3
- export * from "./direct-messaging.mjs";
4
- export * from "./group-messaging.mjs";
5
- export * from "./inbound.mjs";
6
- export * from "./messaging-channel.mjs";
7
- export * from "./projection.mjs";
8
- export * from "./prompt.mjs";
9
- export * from "./routing.mjs";
10
- //# sourceMappingURL=index.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../src/channels/index.ts"],
4
- "sourcesContent": ["export * from \"./content.mjs\";\nexport * from \"./delivery.mjs\";\nexport * from \"./direct-messaging.mjs\";\nexport * from \"./group-messaging.mjs\";\nexport * from \"./inbound.mjs\";\nexport * from \"./messaging-channel.mjs\";\nexport * from \"./projection.mjs\";\nexport * from \"./prompt.mjs\";\nexport * from \"./routing.mjs\";\n"],
5
- "mappings": "AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- import { TurnPolicy, type RoomSurfaceDefinitionIface, type RoomReceiveContextIface, type ConversationRefIface, type JsonValue, type RouteArgsIface, type RouteDecisionIface } from '@finalai/agent-provider';
2
- import { type ProjectorConfigIface } from "./projection.mjs";
3
- export interface MessagingChannelConfigIface extends ProjectorConfigIface {
4
- route?: (args: RouteArgsIface) => RouteDecisionIface[] | Promise<RouteDecisionIface[]>;
5
- }
6
- export declare const createConversationReceive: (turnPolicy: TurnPolicy, route?: NonNullable<MessagingChannelConfigIface["route"]>) => (input: JsonValue, ctx: RoomReceiveContextIface) => Promise<ConversationRefIface>;
7
- export declare const createMessagingChannel: (config?: MessagingChannelConfigIface) => RoomSurfaceDefinitionIface<JsonValue, JsonValue>;
@@ -1,99 +0,0 @@
1
- import {
2
- defineRoomSurface,
3
- TurnPolicy
4
- } from "@finalai/agent-provider";
5
- import { contentBlocksOf } from "./content.mjs";
6
- import { deliverMessage, membershipNotice, reactionNotice } from "./delivery.mjs";
7
- import {
8
- conversationEventInboundSchema,
9
- ConversationEventKind,
10
- messageInboundSchema
11
- } from "./inbound.mjs";
12
- import { createProjector } from "./projection.mjs";
13
- import { routeMessage } from "./routing.mjs";
14
- const receiveConversationEvent = async (event, ctx) => {
15
- const participants = await ctx.platform.conversations.participants(event.conversationId);
16
- if (event.event === ConversationEventKind.Membership) {
17
- const subject = participants.find((participant) => participant.id === event.participantId);
18
- if (subject != null) {
19
- await membershipNotice({
20
- platform: ctx.platform,
21
- participants: participants.filter(
22
- (participant) => !participant.left && (participant.kind !== "agent" || ctx.agentParticipantId == null || participant.id === ctx.agentParticipantId)
23
- ),
24
- subject,
25
- change: event.change,
26
- idempotencyKey: `membership:${event.participantId}:${event.eventId}`
27
- });
28
- }
29
- return { conversationId: event.conversationId };
30
- }
31
- const [message] = (await ctx.platform.conversations.recentMessages({
32
- conversationId: event.conversationId,
33
- beforeSeq: event.messageSeq + 1,
34
- limit: 1
35
- })).filter((candidate) => candidate.messageId === event.messageId);
36
- if (message != null && (ctx.agentParticipantId == null || message.authorParticipantId === ctx.agentParticipantId)) {
37
- await reactionNotice({
38
- platform: ctx.platform,
39
- participants,
40
- message,
41
- isLatestOfAuthor: event.latestOfAuthor,
42
- reactorParticipantId: event.reactorParticipantId,
43
- emoji: event.emoji,
44
- eventId: event.eventId
45
- });
46
- }
47
- return { conversationId: event.conversationId };
48
- };
49
- const createConversationReceive = (turnPolicy, route = routeMessage) => async (input, ctx) => {
50
- const event = conversationEventInboundSchema.safeParse(input);
51
- if (event.success) {
52
- return receiveConversationEvent(event.data, ctx);
53
- }
54
- const inbound = messageInboundSchema.parse(input);
55
- const conversation = ctx.conversation ?? await ctx.platform.conversations.get(inbound.conversationId);
56
- if (conversation == null) {
57
- throw new Error(`conversation ${inbound.conversationId} not found`);
58
- }
59
- const message = ctx.message ?? await ctx.platform.conversations.postMessage({
60
- conversationId: inbound.conversationId,
61
- authorParticipantId: inbound.authorParticipantId,
62
- kind: inbound.kind,
63
- content: contentBlocksOf(inbound.content),
64
- clientMessageId: inbound.clientMessageId ?? null,
65
- replyToMessageId: inbound.replyToMessageId ?? null,
66
- threadRootId: inbound.threadRootId ?? null,
67
- quotedMessageIds: inbound.quotedMessageIds,
68
- mentionedParticipantIds: inbound.mentionedParticipantIds
69
- });
70
- await deliverMessage({
71
- platform: ctx.platform,
72
- route: async (args) => {
73
- const requested = ctx.requestedDelivery != null && ctx.agentParticipantId != null && route === routeMessage ? [{ participantId: ctx.agentParticipantId, delivery: ctx.requestedDelivery }] : await route({ ...args, agentParticipantId: ctx.agentParticipantId });
74
- return requested.filter(
75
- (decision) => ctx.agentParticipantId == null || decision.participantId === ctx.agentParticipantId
76
- );
77
- },
78
- conversation,
79
- message,
80
- participants: ctx.participants == null ? void 0 : [...ctx.participants],
81
- turnPolicy
82
- });
83
- return { conversationId: conversation.conversationId };
84
- };
85
- const createMessagingChannel = (config = {}) => {
86
- const turnPolicy = config.turnPolicy ?? TurnPolicy.Steer;
87
- const projector = createProjector({ ...config, turnPolicy });
88
- return defineRoomSurface({
89
- receive: createConversationReceive(turnPolicy, config.route),
90
- route: config.route ?? routeMessage,
91
- turnPolicy,
92
- project: projector.project
93
- });
94
- };
95
- export {
96
- createConversationReceive,
97
- createMessagingChannel
98
- };
99
- //# sourceMappingURL=messaging-channel.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../src/channels/messaging-channel.ts"],
4
- "sourcesContent": ["import {\n defineRoomSurface,\n TurnPolicy,\n type RoomSurfaceDefinitionIface,\n type RoomReceiveContextIface,\n type ConversationRefIface,\n type JsonValue,\n type RouteArgsIface,\n type RouteDecisionIface,\n} from '@finalai/agent-provider';\nimport { contentBlocksOf } from \"./content.mjs\";\nimport { deliverMessage, membershipNotice, reactionNotice } from \"./delivery.mjs\";\nimport {\n conversationEventInboundSchema,\n ConversationEventKind,\n messageInboundSchema,\n type ConversationEventInboundIface,\n} from \"./inbound.mjs\";\nimport { createProjector, type ProjectorConfigIface } from \"./projection.mjs\";\nimport { routeMessage } from \"./routing.mjs\";\n\n// `turnPolicy` (ProjectorConfigIface): steer unless configured; a conversation\n// set to queue overrides it.\nexport interface MessagingChannelConfigIface extends ProjectorConfigIface {\n route?: (args: RouteArgsIface) => RouteDecisionIface[] | Promise<RouteDecisionIface[]>;\n}\n\nconst receiveConversationEvent = async (\n event: ConversationEventInboundIface,\n ctx: RoomReceiveContextIface,\n): Promise<ConversationRefIface> => {\n const participants = await ctx.platform.conversations.participants(event.conversationId);\n if (event.event === ConversationEventKind.Membership) {\n const subject = participants.find((participant) => participant.id === event.participantId);\n if (subject != null) {\n await membershipNotice({\n platform: ctx.platform,\n participants: participants.filter(\n (participant) =>\n !participant.left &&\n (participant.kind !== 'agent' ||\n ctx.agentParticipantId == null ||\n participant.id === ctx.agentParticipantId),\n ),\n subject,\n change: event.change,\n idempotencyKey: `membership:${event.participantId}:${event.eventId}`,\n });\n }\n return { conversationId: event.conversationId };\n }\n const [message] = (\n await ctx.platform.conversations.recentMessages({\n conversationId: event.conversationId,\n beforeSeq: event.messageSeq + 1,\n limit: 1,\n })\n ).filter((candidate) => candidate.messageId === event.messageId);\n if (\n message != null &&\n (ctx.agentParticipantId == null || message.authorParticipantId === ctx.agentParticipantId)\n ) {\n await reactionNotice({\n platform: ctx.platform,\n participants,\n message,\n isLatestOfAuthor: event.latestOfAuthor,\n reactorParticipantId: event.reactorParticipantId,\n emoji: event.emoji,\n eventId: event.eventId,\n });\n }\n return { conversationId: event.conversationId };\n};\n\n// Persists the message before routing and delivering it to sessions,\n// using the channel's turnPolicy.\nexport const createConversationReceive =\n (\n turnPolicy: TurnPolicy,\n route: NonNullable<MessagingChannelConfigIface['route']> = routeMessage,\n ) =>\n async (input: JsonValue, ctx: RoomReceiveContextIface): Promise<ConversationRefIface> => {\n const event = conversationEventInboundSchema.safeParse(input);\n if (event.success) {\n return receiveConversationEvent(event.data, ctx);\n }\n const inbound = messageInboundSchema.parse(input);\n const conversation =\n ctx.conversation ?? (await ctx.platform.conversations.get(inbound.conversationId));\n if (conversation == null) {\n throw new Error(`conversation ${inbound.conversationId} not found`);\n }\n const message =\n ctx.message ??\n (await ctx.platform.conversations.postMessage({\n conversationId: inbound.conversationId,\n authorParticipantId: inbound.authorParticipantId,\n kind: inbound.kind,\n content: contentBlocksOf(inbound.content),\n clientMessageId: inbound.clientMessageId ?? null,\n replyToMessageId: inbound.replyToMessageId ?? null,\n threadRootId: inbound.threadRootId ?? null,\n quotedMessageIds: inbound.quotedMessageIds,\n mentionedParticipantIds: inbound.mentionedParticipantIds,\n }));\n await deliverMessage({\n platform: ctx.platform,\n route: async (args) => {\n const requested =\n ctx.requestedDelivery != null && ctx.agentParticipantId != null && route === routeMessage\n ? [{ participantId: ctx.agentParticipantId, delivery: ctx.requestedDelivery }]\n : await route({ ...args, agentParticipantId: ctx.agentParticipantId });\n return requested.filter(\n (decision) =>\n ctx.agentParticipantId == null || decision.participantId === ctx.agentParticipantId,\n );\n },\n conversation,\n message,\n participants: ctx.participants == null ? undefined : [...ctx.participants],\n turnPolicy,\n });\n return { conversationId: conversation.conversationId };\n };\n\n// The shared body of direct-messaging and group-messaging: one routing table\n// and one projection; the conversation kind selects the rules.\nexport const createMessagingChannel = (\n config: MessagingChannelConfigIface = {},\n): RoomSurfaceDefinitionIface<JsonValue, JsonValue> => {\n const turnPolicy = config.turnPolicy ?? TurnPolicy.Steer;\n const projector = createProjector({ ...config, turnPolicy });\n return defineRoomSurface<JsonValue, JsonValue>({\n receive: createConversationReceive(turnPolicy, config.route),\n route: config.route ?? routeMessage,\n turnPolicy,\n project: projector.project,\n });\n};\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,36 +0,0 @@
1
- import type { ContentBlock, SessionUpdate } from '@agentclientprotocol/sdk/experimental/v2';
2
- import { TurnPolicy, type JsonValue, type ProjectionContextIface } from '@finalai/agent-provider';
3
- import * as z from 'zod';
4
- export declare const DEFAULT_PROJECTION_UPDATE_INTERVAL_MS = 300;
5
- export declare const projectionStateSchema: z.ZodObject<{
6
- messageId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
7
- messageSeq: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
8
- fromSeq: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
9
- lastSeq: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
10
- lastFlushAt: z.ZodDefault<z.ZodNumber>;
11
- dirty: z.ZodDefault<z.ZodBoolean>;
12
- segments: z.ZodArray<z.ZodObject<{
13
- messageId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
14
- messageSeq: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
15
- fromSeq: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
16
- lastSeq: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
17
- lastFlushAt: z.ZodDefault<z.ZodNumber>;
18
- dirty: z.ZodDefault<z.ZodBoolean>;
19
- acpMessageId: z.ZodString;
20
- blocks: z.ZodArray<z.ZodType<import("@finalai/agent-provider").JsonObject, unknown, z.core.$ZodTypeInternals<import("@finalai/agent-provider").JsonObject, unknown>>>;
21
- }, z.core.$strip>>;
22
- }, z.core.$strip>;
23
- export type ProjectionStateIface = z.infer<typeof projectionStateSchema>;
24
- export interface ProjectorConfigIface {
25
- updateIntervalMs?: number;
26
- now?: () => number;
27
- turnPolicy?: TurnPolicy;
28
- }
29
- export declare const emptyProjectionState: () => ProjectionStateIface;
30
- export declare const projectionStateOf: (state: JsonValue | null) => ProjectionStateIface;
31
- export declare const visibleContent: (state: Pick<ProjectionStateIface, "segments">) => ContentBlock[];
32
- export declare const accumulateUpdate: (state: ProjectionStateIface, update: SessionUpdate) => ProjectionStateIface["segments"][number] | null;
33
- export interface ProjectorIface {
34
- project(update: SessionUpdate, ctx: ProjectionContextIface): Promise<void>;
35
- }
36
- export declare const createProjector: (config?: ProjectorConfigIface) => ProjectorIface;
@@ -1,233 +0,0 @@
1
- import { textOfBlock } from "@finalai/acp-session";
2
- import {
3
- jsonObjectSchema,
4
- MessageKind,
5
- turnEndOf,
6
- TurnPolicy
7
- } from "@finalai/agent-provider";
8
- import * as z from "zod";
9
- import { contentBlockOf, jsonOfContentBlock } from "./content.mjs";
10
- const DEFAULT_PROJECTION_UPDATE_INTERVAL_MS = 300;
11
- const projectedMessageSchema = z.object({
12
- messageId: z.string().nullable().default(null),
13
- // The reply's conversation seq, so the finished reply is read back by
14
- // position however many messages arrived meanwhile.
15
- messageSeq: z.number().nullable().default(null),
16
- fromSeq: z.number().nullable().default(null),
17
- lastSeq: z.number().nullable().default(null),
18
- lastFlushAt: z.number().default(0),
19
- dirty: z.boolean().default(false)
20
- });
21
- const segmentSchema = projectedMessageSchema.extend({
22
- acpMessageId: z.string(),
23
- blocks: z.array(jsonObjectSchema)
24
- });
25
- const projectionStateSchema = projectedMessageSchema.extend({
26
- segments: z.array(segmentSchema)
27
- });
28
- const agentChunkSchema = z.object({
29
- sessionUpdate: z.literal("agent_message_chunk"),
30
- messageId: z.string(),
31
- content: jsonObjectSchema
32
- });
33
- const agentMessageSchema = z.object({
34
- sessionUpdate: z.literal("agent_message"),
35
- messageId: z.string(),
36
- content: z.array(jsonObjectSchema).nullish()
37
- });
38
- const updateJson = (update) => {
39
- const parsed = jsonObjectSchema.safeParse(JSON.parse(JSON.stringify(update)));
40
- return parsed.success ? parsed.data : null;
41
- };
42
- const emptyProjectionState = () => ({
43
- messageId: null,
44
- messageSeq: null,
45
- fromSeq: null,
46
- lastSeq: null,
47
- lastFlushAt: 0,
48
- dirty: false,
49
- segments: []
50
- });
51
- const projectionStateOf = (state) => {
52
- const parsed = projectionStateSchema.safeParse(state);
53
- return parsed.success ? parsed.data : emptyProjectionState();
54
- };
55
- const plainText = (block) => block == null || block._meta != null ? null : textOfBlock(block);
56
- const appendBlock = (blocks, block, separator) => {
57
- const lastText = plainText(blocks.at(-1));
58
- const nextText = plainText(block);
59
- if (lastText != null && nextText != null) {
60
- blocks[blocks.length - 1] = { type: "text", text: `${lastText}${separator}${nextText}` };
61
- } else {
62
- blocks.push(block);
63
- }
64
- };
65
- const visibleContent = (state) => {
66
- const content = [];
67
- for (const segment of state.segments) {
68
- const blocks = [];
69
- for (const block of segment.blocks) {
70
- const parsed = contentBlockOf(block);
71
- if (parsed != null) appendBlock(blocks, parsed, "");
72
- }
73
- for (const block of blocks) appendBlock(content, block, "\n\n");
74
- }
75
- return content.flatMap((block) => {
76
- const text = textOfBlock(block);
77
- if (text == null) return [block];
78
- const trimmed = text.trim();
79
- return trimmed.length === 0 ? [] : [{ ...block, type: "text", text: trimmed }];
80
- });
81
- };
82
- const segmentFor = (state, acpMessageId) => {
83
- const existing = state.segments.find((segment2) => segment2.acpMessageId === acpMessageId);
84
- if (existing != null) {
85
- return existing;
86
- }
87
- const segment = segmentSchema.parse({ acpMessageId, blocks: [] });
88
- state.segments.push(segment);
89
- return segment;
90
- };
91
- const accumulateUpdate = (state, update) => {
92
- const json = updateJson(update);
93
- const chunk = agentChunkSchema.safeParse(json);
94
- if (chunk.success) {
95
- const block = contentBlockOf(chunk.data.content);
96
- if (block == null) return null;
97
- const segment = segmentFor(state, chunk.data.messageId);
98
- const last = segment.blocks.at(-1);
99
- const lastBlock = last == null ? null : contentBlockOf(last);
100
- const lastText = lastBlock == null ? null : plainText(lastBlock);
101
- const nextText = plainText(block);
102
- if (lastText != null && nextText != null)
103
- segment.blocks[segment.blocks.length - 1] = { type: "text", text: `${lastText}${nextText}` };
104
- else segment.blocks.push(jsonOfContentBlock(block));
105
- return segment;
106
- }
107
- const message = agentMessageSchema.safeParse(json);
108
- if (message.success) {
109
- if (message.data.content === void 0) return null;
110
- const blocks = (message.data.content ?? []).flatMap((block) => {
111
- const parsed = contentBlockOf(block);
112
- return parsed == null ? [] : [jsonOfContentBlock(parsed)];
113
- });
114
- const segment = segmentFor(state, message.data.messageId);
115
- segment.blocks = blocks;
116
- return segment;
117
- }
118
- return null;
119
- };
120
- const createProjector = (config = {}) => {
121
- const now = config.now ?? Date.now;
122
- const interval = config.updateIntervalMs ?? DEFAULT_PROJECTION_UPDATE_INTERVAL_MS;
123
- const fold = async (update, ctx) => {
124
- const state = projectionStateOf(ctx.state.get());
125
- const segment = accumulateUpdate(state, update);
126
- if (state.messageId == null && (segment == null || segment.messageId == null)) {
127
- for (const previous of state.segments) {
128
- if (previous === segment || !previous.dirty || previous.messageId == null) continue;
129
- await ctx.platform.conversations.updateMessage({
130
- messageId: previous.messageId,
131
- content: visibleContent({ segments: [previous] }),
132
- final: false
133
- });
134
- previous.lastFlushAt = now();
135
- previous.dirty = false;
136
- }
137
- }
138
- if (segment == null) {
139
- ctx.state.set(state);
140
- return;
141
- }
142
- const target = state.messageId == null ? segment : state;
143
- target.lastSeq = ctx.seq;
144
- const content = () => visibleContent({ segments: target === state ? state.segments : [segment] });
145
- if (target.messageId == null) {
146
- const visible = content();
147
- if (visible.length === 0) {
148
- ctx.state.set(state);
149
- return;
150
- }
151
- const message = await ctx.platform.conversations.postMessage({
152
- conversationId: ctx.conversation.conversationId,
153
- authorParticipantId: ctx.agentParticipant.id,
154
- kind: MessageKind.Message,
155
- content: visible,
156
- source: { turnId: ctx.turn.id, fromSeq: ctx.seq, toSeq: ctx.seq }
157
- });
158
- target.messageId = message.messageId;
159
- target.messageSeq = message.seq;
160
- target.fromSeq = ctx.seq;
161
- target.lastFlushAt = now();
162
- target.dirty = false;
163
- } else if (now() - target.lastFlushAt >= interval) {
164
- await ctx.platform.conversations.updateMessage({
165
- messageId: target.messageId,
166
- content: content(),
167
- final: false
168
- });
169
- target.lastFlushAt = now();
170
- target.dirty = false;
171
- } else {
172
- target.dirty = true;
173
- }
174
- ctx.state.set(state);
175
- };
176
- const finish = async (ctx) => {
177
- const state = projectionStateOf(ctx.state.get());
178
- if (state.messageId != null) {
179
- await finishMessage(ctx, state, visibleContent(state));
180
- return;
181
- }
182
- for (const segment of state.segments) {
183
- await finishMessage(ctx, segment, visibleContent({ segments: [segment] }));
184
- }
185
- };
186
- const finishMessage = async (ctx, state, content) => {
187
- if (state.messageId == null) {
188
- return;
189
- }
190
- await ctx.platform.conversations.updateMessage({
191
- messageId: state.messageId,
192
- content,
193
- final: true
194
- });
195
- const reply = state.messageSeq == null ? null : (await ctx.platform.conversations.recentMessages({
196
- conversationId: ctx.conversation.conversationId,
197
- beforeSeq: state.messageSeq + 1,
198
- limit: 1
199
- })).find((message) => message.messageId === state.messageId);
200
- if (reply == null) {
201
- ctx.platform.log("finished reply not found for routing", {
202
- messageId: state.messageId,
203
- seq: state.messageSeq
204
- });
205
- }
206
- if (reply != null) {
207
- if (ctx.platform.conversations.dispatchMessage == null)
208
- throw new Error("channel dispatch is unavailable");
209
- await ctx.platform.conversations.dispatchMessage({
210
- conversationId: reply.conversationId,
211
- messageId: reply.messageId
212
- });
213
- }
214
- };
215
- const project = async (update, ctx) => {
216
- const { conversation, agentParticipant } = ctx;
217
- if (conversation == null || agentParticipant == null)
218
- throw new Error("Final rendering requires a Final conversation");
219
- const context = { ...ctx, conversation, agentParticipant };
220
- return turnEndOf(update) == null ? fold(update, context) : finish(context);
221
- };
222
- return { project };
223
- };
224
- export {
225
- DEFAULT_PROJECTION_UPDATE_INTERVAL_MS,
226
- accumulateUpdate,
227
- createProjector,
228
- emptyProjectionState,
229
- projectionStateOf,
230
- projectionStateSchema,
231
- visibleContent
232
- };
233
- //# sourceMappingURL=projection.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 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 // Plain text joins the run before it as it arrives (visibleContent joins\n // them the same way), so the state and each fold stay the same size\n // however long the reply grows.\n const last = segment.blocks.at(-1);\n const lastBlock = last == null ? null : contentBlockOf(last);\n const lastText = lastBlock == null ? null : plainText(lastBlock);\n const nextText = plainText(block);\n if (lastText != null && nextText != null)\n segment.blocks[segment.blocks.length - 1] = { type: 'text', text: `${lastText}${nextText}` };\n else 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 // Built only when it is written: between flushes a chunk only folds.\n const content = () =>\n visibleContent({ segments: target === state ? state.segments : [segment] });\n if (target.messageId == null) {\n const visible = content();\n if (visible.length === 0) {\n ctx.state.set(state);\n return;\n }\n const message = await ctx.platform.conversations.postMessage({\n conversationId: ctx.conversation.conversationId,\n authorParticipantId: ctx.agentParticipant.id,\n kind: MessageKind.Message,\n content: visible,\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: 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
- "mappings": "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",
6
- "names": ["segment"]
7
- }