@finalai/final-agent-provider 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/modules/channel-factories.d.mts +3 -1
- package/dist/modules/channel-factories.mjs +32 -18
- package/dist/modules/channel-factories.mjs.map +2 -2
- package/dist/modules/channels/address-context.mjs +16 -53
- package/dist/modules/channels/address-context.mjs.map +3 -3
- package/dist/modules/channels/agent-events.d.mts +3 -29
- package/dist/modules/channels/agent-events.mjs +67 -146
- package/dist/modules/channels/agent-events.mjs.map +3 -3
- package/dist/modules/channels/agent-schedules.d.mts +1 -0
- package/dist/modules/channels/agent-schedules.mjs +18 -19
- package/dist/modules/channels/agent-schedules.mjs.map +2 -2
- package/dist/modules/channels/delivery.d.mts +1 -1
- package/dist/modules/channels/delivery.mjs +5 -23
- package/dist/modules/channels/delivery.mjs.map +2 -2
- package/dist/modules/channels/fake-platform.d.mts +6 -1
- package/dist/modules/channels/fake-platform.mjs +31 -12
- package/dist/modules/channels/fake-platform.mjs.map +2 -2
- package/dist/modules/channels/index.d.mts +1 -0
- package/dist/modules/channels/index.mjs +1 -0
- package/dist/modules/channels/index.mjs.map +2 -2
- package/dist/modules/channels/prompt.d.mts +19 -2
- package/dist/modules/channels/prompt.mjs +44 -17
- package/dist/modules/channels/prompt.mjs.map +2 -2
- package/dist/modules/channels/routing.mjs +1 -15
- package/dist/modules/channels/routing.mjs.map +2 -2
- package/dist/modules/channels/service-input.d.mts +22 -0
- package/dist/modules/channels/service-input.mjs +32 -0
- package/dist/modules/channels/service-input.mjs.map +7 -0
- package/dist/modules/sandboxes/computer.d.mts +2 -0
- package/dist/modules/sandboxes/computer.mjs +52 -7
- package/dist/modules/sandboxes/computer.mjs.map +3 -3
- package/dist/modules/sandboxes/device.d.mts +2 -0
- package/dist/modules/sandboxes/device.mjs +26 -3
- package/dist/modules/sandboxes/device.mjs.map +2 -2
- package/dist/modules/sandboxes/testing/fake-backends.d.mts +3 -0
- package/dist/modules/sandboxes/testing/fake-backends.mjs +35 -3
- package/dist/modules/sandboxes/testing/fake-backends.mjs.map +2 -2
- package/package.json +5 -5
|
@@ -2,15 +2,13 @@ import {
|
|
|
2
2
|
Delivery,
|
|
3
3
|
ParticipantKind,
|
|
4
4
|
RoutingMode,
|
|
5
|
-
SystemParticipantKind,
|
|
6
5
|
TurnPolicy
|
|
7
6
|
} from "@finalai/agent-provider";
|
|
8
7
|
import * as z from "zod";
|
|
9
8
|
import {
|
|
10
9
|
authorPrefixedContent,
|
|
11
|
-
buildCatchUpBlock,
|
|
12
10
|
buildPromptContent,
|
|
13
|
-
|
|
11
|
+
catchUpFor,
|
|
14
12
|
excerpt,
|
|
15
13
|
finalAuthorOf,
|
|
16
14
|
finalContextOf,
|
|
@@ -23,10 +21,8 @@ import { MAX_AGENT_HOP_DEPTH } from "./routing.mjs";
|
|
|
23
21
|
const thrownMessageSchema = z.object({ message: z.string() });
|
|
24
22
|
const CONTEXT_WINDOW_MESSAGES = 50;
|
|
25
23
|
const effectiveTurnPolicy = (args) => args.conversation === TurnPolicy.Queue ? TurnPolicy.Queue : args.channel;
|
|
26
|
-
const humanAudience = (participants) => participants.filter(
|
|
27
|
-
|
|
28
|
-
).map((participant) => participant.id);
|
|
29
|
-
const audienceFor = (author, participants) => author?.kind === ParticipantKind.User || author?.kind === ParticipantKind.System && author.systemKind === SystemParticipantKind.ApiKey ? [author.id] : humanAudience(participants);
|
|
24
|
+
const humanAudience = (participants) => participants.filter((participant) => !participant.left && participant.kind === ParticipantKind.User).map((participant) => participant.id);
|
|
25
|
+
const audienceFor = (author, participants) => author?.kind === ParticipantKind.User ? [author.id] : humanAudience(participants);
|
|
30
26
|
const replyContextFor = async (args) => {
|
|
31
27
|
const byId = new Map(args.earlier.map((message) => [message.messageId, message]));
|
|
32
28
|
const referenced = [
|
|
@@ -57,18 +53,6 @@ const replyContextFor = async (args) => {
|
|
|
57
53
|
})
|
|
58
54
|
};
|
|
59
55
|
};
|
|
60
|
-
const catchUpFor = (args) => {
|
|
61
|
-
const since = args.recipient.sessionId == null ? CatchUpSince.Start : args.recipient.routingMode === RoutingMode.Mention ? CatchUpSince.LastContact : null;
|
|
62
|
-
if (since == null) {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
return buildCatchUpBlock({
|
|
66
|
-
messages: args.earlier,
|
|
67
|
-
participants: args.participants,
|
|
68
|
-
agentParticipantId: args.recipient.id,
|
|
69
|
-
since
|
|
70
|
-
});
|
|
71
|
-
};
|
|
72
56
|
const deliveryIdempotencyKey = (messageId, participantId) => `${messageId}:${participantId}`;
|
|
73
57
|
const deliverMessage = async (args) => {
|
|
74
58
|
const { platform, conversation, message } = args;
|
|
@@ -110,7 +94,6 @@ const deliverMessage = async (args) => {
|
|
|
110
94
|
displayName: "Someone",
|
|
111
95
|
userId: null,
|
|
112
96
|
definitionId: null,
|
|
113
|
-
systemKind: null,
|
|
114
97
|
routingMode: null,
|
|
115
98
|
sessionId: null,
|
|
116
99
|
supportsObserve: null,
|
|
@@ -196,7 +179,7 @@ const observeNotice = async (args) => {
|
|
|
196
179
|
try {
|
|
197
180
|
await args.platform.sessions.observe({
|
|
198
181
|
participantId: recipient.id,
|
|
199
|
-
content: [systemNoticeBlock({ text: args.text
|
|
182
|
+
content: [systemNoticeBlock({ text: args.text })],
|
|
200
183
|
idempotencyKey: `${args.idempotencyKey}:${recipient.id}`
|
|
201
184
|
});
|
|
202
185
|
delivered.push(recipient.id);
|
|
@@ -225,7 +208,6 @@ const reactionNotice = async (args) => {
|
|
|
225
208
|
platform: args.platform,
|
|
226
209
|
recipients: [author],
|
|
227
210
|
text: `${reactor.displayName} reacted ${args.emoji} to ${target}`,
|
|
228
|
-
sourceParticipantId: reactor.id,
|
|
229
211
|
idempotencyKey: args.eventId == null ? reactionId : `${reactionId}:${args.eventId}`
|
|
230
212
|
});
|
|
231
213
|
};
|
|
@@ -243,7 +225,6 @@ const membershipNotice = async (args) => {
|
|
|
243
225
|
platform: args.platform,
|
|
244
226
|
recipients,
|
|
245
227
|
text: `${args.subject.displayName} ${verb} the conversation`,
|
|
246
|
-
sourceParticipantId: args.subject.id,
|
|
247
228
|
idempotencyKey: args.idempotencyKey
|
|
248
229
|
});
|
|
249
230
|
};
|
|
@@ -254,6 +235,7 @@ export {
|
|
|
254
235
|
deliverMessage,
|
|
255
236
|
deliveryIdempotencyKey,
|
|
256
237
|
effectiveTurnPolicy,
|
|
238
|
+
humanAudience,
|
|
257
239
|
membershipNotice,
|
|
258
240
|
observeNotice,
|
|
259
241
|
reactionNotice
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/channels/delivery.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';\nimport {\n Delivery,\n ParticipantKind,\n RoutingMode,\n SystemParticipantKind,\n TurnPolicy,\n type ChannelPlatformIface,\n type ConversationIface,\n type ConversationMessageIface,\n type ParticipantIface,\n type RouteArgsIface,\n type RouteDecisionIface,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport {\n authorPrefixedContent,\n buildCatchUpBlock,\n buildPromptContent,\n CatchUpSince,\n excerpt,\n finalAuthorOf,\n finalContextOf,\n messageRefOf,\n messageSummary,\n renderReplyContext,\n systemNoticeBlock,\n type ReplyContextArgsIface,\n} from \"./prompt.mjs\";\nimport { MAX_AGENT_HOP_DEPTH } from \"./routing.mjs\";\n\n// How a channel hands a conversation message to its agents: route, then build\n// each recipient's ACP content and call prompt or observe. Prompt construction,\n// not orchestration.\n\nconst thrownMessageSchema = z.object({ message: z.string() });\n\nexport const CONTEXT_WINDOW_MESSAGES = 50;\n\nexport interface DeliveryOutcomeIface {\n participantId: string;\n delivery: Delivery;\n // What actually happened: an observe can fall back to a silent prompt, or be\n // skipped for an agent with no session yet (its first prompt catches up).\n sent: 'prompt' | 'observe' | 'silent-prompt' | 'skipped' | 'failed';\n messageId: string | null;\n error: string | null;\n}\n\nexport interface DeliverMessageArgsIface {\n platform: ChannelPlatformIface;\n // A channel's pure route(), or a bundle channel's run in its isolate.\n route: (args: RouteArgsIface) => RouteDecisionIface[] | Promise<RouteDecisionIface[]>;\n conversation: ConversationIface;\n message: ConversationMessageIface;\n participants?: ParticipantIface[];\n // The delivering channel's turnPolicy.\n turnPolicy: TurnPolicy;\n}\n\n// A channel's policy applies unless the conversation is set to\n// queue, the per-conversation override.\nexport const effectiveTurnPolicy = (args: {\n channel: TurnPolicy;\n conversation: TurnPolicy;\n}): TurnPolicy => (args.conversation === TurnPolicy.Queue ? TurnPolicy.Queue : args.channel);\n\nconst humanAudience = (participants: ParticipantIface[]): string[] =>\n participants\n .filter(\n (participant) =>\n !participant.left &&\n (participant.kind === ParticipantKind.User ||\n participant.systemKind === SystemParticipantKind.ApiKey),\n )\n .map((participant) => participant.id);\n\n// A user or API-key author answers its own prompt; other sources use the room\u2019s answering actors.\nexport const audienceFor = (\n author: ParticipantIface | null,\n participants: ParticipantIface[],\n): string[] =>\n author?.kind === ParticipantKind.User ||\n (author?.kind === ParticipantKind.System && author.systemKind === SystemParticipantKind.ApiKey)\n ? [author.id]\n : humanAudience(participants);\n\n// Reply, thread and quote context. A referenced message outside the\n// recent window is read by id, however old.\nconst replyContextFor = async (args: {\n platform: ChannelPlatformIface;\n message: ConversationMessageIface;\n earlier: ConversationMessageIface[];\n participants: ParticipantIface[];\n}): Promise<ReplyContextArgsIface> => {\n const byId = new Map(args.earlier.map((message) => [message.messageId, message]));\n const referenced = [\n args.message.replyToMessageId,\n args.message.threadRootId,\n ...args.message.quotedMessageIds,\n ].flatMap((id) => (id == null || byId.has(id) ? [] : [id]));\n if (referenced.length > 0) {\n const found = await args.platform.conversations.getMessages({\n conversationId: args.message.conversationId,\n messageIds: [...new Set(referenced)],\n });\n for (const message of found) byId.set(message.messageId, message);\n }\n const replyTo =\n args.message.replyToMessageId == null\n ? null\n : (byId.get(args.message.replyToMessageId) ?? null);\n const root =\n args.message.threadRootId == null ? null : (byId.get(args.message.threadRootId) ?? null);\n const rootAuthor =\n root == null\n ? null\n : args.participants.find((participant) => participant.id === root.authorParticipantId);\n return {\n replyTo:\n replyTo == null || replyTo.messageId === root?.messageId\n ? null\n : messageRefOf(replyTo, args.participants),\n thread:\n args.message.threadRootId == null\n ? null\n : {\n rootMessageId: args.message.threadRootId,\n rootExcerpt: root == null ? '' : excerpt(messageSummary(root.content)),\n rootAuthor: rootAuthor == null ? null : finalAuthorOf(rootAuthor),\n },\n quotes: args.message.quotedMessageIds.flatMap((id) => {\n const quoted = byId.get(id);\n return quoted == null ? [] : [messageRefOf(quoted, args.participants)];\n }),\n };\n};\n\nconst catchUpFor = (args: {\n recipient: ParticipantIface;\n earlier: ConversationMessageIface[];\n participants: ParticipantIface[];\n}): ContentBlock | null => {\n // A fresh session has seen nothing; a Mention agent sees only what it is\n // prompted with; everyone else has been observing.\n const since =\n args.recipient.sessionId == null\n ? CatchUpSince.Start\n : args.recipient.routingMode === RoutingMode.Mention\n ? CatchUpSince.LastContact\n : null;\n if (since == null) {\n return null;\n }\n return buildCatchUpBlock({\n messages: args.earlier,\n participants: args.participants,\n agentParticipantId: args.recipient.id,\n since,\n });\n};\n\nexport const deliveryIdempotencyKey = (messageId: string, participantId: string): string =>\n `${messageId}:${participantId}`;\n\nexport const deliverMessage = async (\n args: DeliverMessageArgsIface,\n): Promise<DeliveryOutcomeIface[]> => {\n const { platform, conversation, message } = args;\n const participants =\n args.participants ?? (await platform.conversations.participants(conversation.conversationId));\n const decisions = await args.route({ conversation, message, participants });\n const author =\n participants.find((participant) => participant.id === message.authorParticipantId) ?? null;\n for (const participant of participants) {\n if (participant.kind !== ParticipantKind.Agent) continue;\n const decision = decisions.find((candidate) => candidate.participantId === participant.id);\n platform.log('channel route decision', {\n observed_at: new Date().toISOString(),\n organization_id: conversation.organizationId,\n conversation_id: conversation.conversationId,\n conversation_kind: conversation.kind,\n message_id: message.messageId,\n author_kind: author?.kind ?? '',\n participant_id: participant.id,\n routing_mode: participant.routingMode ?? '',\n delivery: decision?.delivery ?? 'skipped',\n reason: participant.left\n ? 'left conversation'\n : participant.id === message.authorParticipantId\n ? 'author'\n : decision == null\n ? 'not selected by route'\n : 'selected by route',\n hop_depth: message.hopDepth,\n });\n }\n if (decisions.length === 0) return [];\n const earlier = await platform.conversations.recentMessages({\n conversationId: conversation.conversationId,\n limit: CONTEXT_WINDOW_MESSAGES,\n beforeSeq: message.seq,\n });\n const replyContext = await replyContextFor({ platform, message, earlier, participants });\n const turnPolicy = effectiveTurnPolicy({\n channel: args.turnPolicy,\n conversation: conversation.turnPolicy,\n });\n const audience = audienceFor(author, participants);\n const authorParticipant: ParticipantIface = author ?? {\n id: message.authorParticipantId,\n kind: ParticipantKind.User,\n displayName: 'Someone',\n userId: null,\n definitionId: null,\n systemKind: null,\n routingMode: null,\n sessionId: null,\n supportsObserve: null,\n rendersAuthorMeta: null,\n left: true,\n };\n\n const deliverOne = async (decision: RouteDecisionIface): Promise<DeliveryOutcomeIface> => {\n const recipient = participants.find((participant) => participant.id === decision.participantId);\n const base = { participantId: decision.participantId, delivery: decision.delivery };\n if (recipient == null) {\n return { ...base, sent: 'skipped', messageId: null, error: null };\n }\n const idempotencyKey = deliveryIdempotencyKey(message.messageId, recipient.id);\n const prompt = async (content: ContentBlock[], replyExpected: boolean): Promise<string> => {\n const result = await platform.sessions.prompt({\n participantId: recipient.id,\n content,\n turnPolicy,\n replyExpected,\n idempotencyKey,\n authorParticipantId: message.authorParticipantId,\n audienceParticipantIds: audience,\n sourceMessageId: message.messageId,\n });\n return result.messageId;\n };\n try {\n if (decision.delivery === Delivery.Prompt) {\n const content = buildPromptContent({\n message,\n author: authorParticipant,\n recipient,\n replyContext,\n catchUp: catchUpFor({ recipient, earlier, participants }),\n });\n return { ...base, sent: 'prompt', messageId: await prompt(content, true), error: null };\n }\n if (recipient.sessionId == null) {\n return { ...base, sent: 'skipped', messageId: null, error: null };\n }\n const contextBlock = renderReplyContext(replyContext);\n const content = [\n ...(contextBlock == null ? [] : [contextBlock]),\n ...authorPrefixedContent({\n content: message.content,\n author: finalAuthorOf(authorParticipant),\n rendersAuthorMeta: recipient.rendersAuthorMeta ?? false,\n context: finalContextOf(replyContext),\n }),\n ];\n if (recipient.supportsObserve === false) {\n if (message.hopDepth >= MAX_AGENT_HOP_DEPTH) {\n return { ...base, sent: 'skipped', messageId: null, error: null };\n }\n return {\n ...base,\n sent: 'silent-prompt',\n messageId: await prompt(content, false),\n error: null,\n };\n }\n await platform.sessions.observe({ participantId: recipient.id, content, idempotencyKey });\n return { ...base, sent: 'observe', messageId: null, error: null };\n } catch (error) {\n // JSON-RPC failures arrive as plain `{ code, message }` objects.\n const reason =\n error instanceof Error\n ? error.message\n : (thrownMessageSchema.safeParse(error).data?.message ?? String(error));\n platform.log('channel delivery failed', {\n conversationId: conversation.conversationId,\n messageId: message.messageId,\n participantId: recipient.id,\n delivery: decision.delivery,\n error: reason,\n });\n return { ...base, sent: 'failed', messageId: null, error: reason };\n }\n };\n\n return Promise.all(decisions.map(deliverOne));\n};\n\nexport interface ObserveNoticeArgsIface {\n platform: ChannelPlatformIface;\n recipients: ParticipantIface[];\n text: string;\n sourceParticipantId: string;\n idempotencyKey: string;\n}\n\n// System blocks with no model call. Agents without a session skip them (their\n// first prompt catches up); agents without observe support skip them too, since\n// a silent prompt per reaction is not worth a model call.\nexport const observeNotice = async (args: ObserveNoticeArgsIface): Promise<string[]> => {\n const delivered: string[] = [];\n for (const recipient of args.recipients) {\n if (recipient.sessionId == null || recipient.supportsObserve === false || recipient.left)\n continue;\n try {\n await args.platform.sessions.observe({\n participantId: recipient.id,\n content: [systemNoticeBlock({ text: args.text, participantId: args.sourceParticipantId })],\n idempotencyKey: `${args.idempotencyKey}:${recipient.id}`,\n });\n delivered.push(recipient.id);\n } catch (error) {\n args.platform.log('channel notice failed', {\n participantId: recipient.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n return delivered;\n};\n\nexport interface ReactionNoticeArgsIface {\n platform: ChannelPlatformIface;\n participants: ParticipantIface[];\n message: ConversationMessageIface;\n // Whether the reacted message is the agent's latest one.\n isLatestOfAuthor: boolean;\n reactorParticipantId: string;\n emoji: string;\n eventId?: string;\n}\n\n// Only the agent whose message was reacted to hears about it.\nexport const reactionNotice = async (args: ReactionNoticeArgsIface): Promise<string[]> => {\n const author = args.participants.find(\n (participant) => participant.id === args.message.authorParticipantId,\n );\n const reactor = args.participants.find(\n (participant) => participant.id === args.reactorParticipantId,\n );\n if (\n author == null ||\n author.kind !== ParticipantKind.Agent ||\n reactor == null ||\n reactor.id === author.id\n ) {\n return [];\n }\n const target = args.isLatestOfAuthor\n ? 'your last message'\n : `your message \"${excerpt(messageSummary(args.message.content), 120)}\"`;\n const reactionId = `reaction:${args.message.messageId}:${reactor.id}:${args.emoji}`;\n return observeNotice({\n platform: args.platform,\n recipients: [author],\n text: `${reactor.displayName} reacted ${args.emoji} to ${target}`,\n sourceParticipantId: reactor.id,\n idempotencyKey: args.eventId == null ? reactionId : `${reactionId}:${args.eventId}`,\n });\n};\n\nexport enum MembershipChange {\n Joined = 'joined',\n Left = 'left',\n}\n\nexport interface MembershipNoticeArgsIface {\n platform: ChannelPlatformIface;\n participants: ParticipantIface[];\n subject: ParticipantIface;\n change: MembershipChange;\n idempotencyKey: string;\n}\n\n// Membership changes reach agents that follow the whole conversation.\nexport const membershipNotice = async (args: MembershipNoticeArgsIface): Promise<string[]> => {\n const recipients = args.participants.filter(\n (participant) =>\n participant.kind === ParticipantKind.Agent &&\n participant.id !== args.subject.id &&\n (participant.routingMode === RoutingMode.All ||\n participant.routingMode === RoutingMode.Default),\n );\n const verb = args.change === MembershipChange.Joined ? 'joined' : 'left';\n return observeNotice({\n platform: args.platform,\n recipients,\n text: `${args.subject.displayName} ${verb} the conversation`,\n sourceParticipantId: args.subject.id,\n idempotencyKey: args.idempotencyKey,\n });\n};\n"],
|
|
5
|
-
"mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,
|
|
4
|
+
"sourcesContent": ["import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';\nimport {\n Delivery,\n ParticipantKind,\n RoutingMode,\n TurnPolicy,\n type ChannelPlatformIface,\n type ConversationIface,\n type ConversationMessageIface,\n type ParticipantIface,\n type RouteArgsIface,\n type RouteDecisionIface,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport {\n authorPrefixedContent,\n buildPromptContent,\n catchUpFor,\n excerpt,\n finalAuthorOf,\n finalContextOf,\n messageRefOf,\n messageSummary,\n renderReplyContext,\n systemNoticeBlock,\n type ReplyContextArgsIface,\n} from \"./prompt.mjs\";\nimport { MAX_AGENT_HOP_DEPTH } from \"./routing.mjs\";\n\n// How a channel hands a conversation message to its agents: route, then build\n// each recipient's ACP content and call prompt or observe. Prompt construction,\n// not orchestration.\n\nconst thrownMessageSchema = z.object({ message: z.string() });\n\nexport const CONTEXT_WINDOW_MESSAGES = 50;\n\nexport interface DeliveryOutcomeIface {\n participantId: string;\n delivery: Delivery;\n // What actually happened: an observe can fall back to a silent prompt, or be\n // skipped for an agent with no session yet (its first prompt catches up).\n sent: 'prompt' | 'observe' | 'silent-prompt' | 'skipped' | 'failed';\n messageId: string | null;\n error: string | null;\n}\n\nexport interface DeliverMessageArgsIface {\n platform: ChannelPlatformIface;\n // A channel's pure route(), or a bundle channel's run in its isolate.\n route: (args: RouteArgsIface) => RouteDecisionIface[] | Promise<RouteDecisionIface[]>;\n conversation: ConversationIface;\n message: ConversationMessageIface;\n participants?: ParticipantIface[];\n // The delivering channel's turnPolicy.\n turnPolicy: TurnPolicy;\n}\n\n// A channel's policy applies unless the conversation is set to\n// queue, the per-conversation override.\nexport const effectiveTurnPolicy = (args: {\n channel: TurnPolicy;\n conversation: TurnPolicy;\n}): TurnPolicy => (args.conversation === TurnPolicy.Queue ? TurnPolicy.Queue : args.channel);\n\n// Every person in the room: who may answer what a turn no person prompted asks.\nexport const humanAudience = (participants: ParticipantIface[]): string[] =>\n participants\n .filter((participant) => !participant.left && participant.kind === ParticipantKind.User)\n .map((participant) => participant.id);\n\n// A person answers their own prompt; an agent's prompt is answered by the room's people.\nexport const audienceFor = (\n author: ParticipantIface | null,\n participants: ParticipantIface[],\n): string[] => (author?.kind === ParticipantKind.User ? [author.id] : humanAudience(participants));\n\n// Reply, thread and quote context. A referenced message outside the\n// recent window is read by id, however old.\nconst replyContextFor = async (args: {\n platform: ChannelPlatformIface;\n message: ConversationMessageIface;\n earlier: ConversationMessageIface[];\n participants: ParticipantIface[];\n}): Promise<ReplyContextArgsIface> => {\n const byId = new Map(args.earlier.map((message) => [message.messageId, message]));\n const referenced = [\n args.message.replyToMessageId,\n args.message.threadRootId,\n ...args.message.quotedMessageIds,\n ].flatMap((id) => (id == null || byId.has(id) ? [] : [id]));\n if (referenced.length > 0) {\n const found = await args.platform.conversations.getMessages({\n conversationId: args.message.conversationId,\n messageIds: [...new Set(referenced)],\n });\n for (const message of found) byId.set(message.messageId, message);\n }\n const replyTo =\n args.message.replyToMessageId == null\n ? null\n : (byId.get(args.message.replyToMessageId) ?? null);\n const root =\n args.message.threadRootId == null ? null : (byId.get(args.message.threadRootId) ?? null);\n const rootAuthor =\n root == null\n ? null\n : args.participants.find((participant) => participant.id === root.authorParticipantId);\n return {\n replyTo:\n replyTo == null || replyTo.messageId === root?.messageId\n ? null\n : messageRefOf(replyTo, args.participants),\n thread:\n args.message.threadRootId == null\n ? null\n : {\n rootMessageId: args.message.threadRootId,\n rootExcerpt: root == null ? '' : excerpt(messageSummary(root.content)),\n rootAuthor: rootAuthor == null ? null : finalAuthorOf(rootAuthor),\n },\n quotes: args.message.quotedMessageIds.flatMap((id) => {\n const quoted = byId.get(id);\n return quoted == null ? [] : [messageRefOf(quoted, args.participants)];\n }),\n };\n};\n\nexport const deliveryIdempotencyKey = (messageId: string, participantId: string): string =>\n `${messageId}:${participantId}`;\n\nexport const deliverMessage = async (\n args: DeliverMessageArgsIface,\n): Promise<DeliveryOutcomeIface[]> => {\n const { platform, conversation, message } = args;\n const participants =\n args.participants ?? (await platform.conversations.participants(conversation.conversationId));\n const decisions = await args.route({ conversation, message, participants });\n const author =\n participants.find((participant) => participant.id === message.authorParticipantId) ?? null;\n for (const participant of participants) {\n if (participant.kind !== ParticipantKind.Agent) continue;\n const decision = decisions.find((candidate) => candidate.participantId === participant.id);\n platform.log('channel route decision', {\n observed_at: new Date().toISOString(),\n organization_id: conversation.organizationId,\n conversation_id: conversation.conversationId,\n conversation_kind: conversation.kind,\n message_id: message.messageId,\n author_kind: author?.kind ?? '',\n participant_id: participant.id,\n routing_mode: participant.routingMode ?? '',\n delivery: decision?.delivery ?? 'skipped',\n reason: participant.left\n ? 'left conversation'\n : participant.id === message.authorParticipantId\n ? 'author'\n : decision == null\n ? 'not selected by route'\n : 'selected by route',\n hop_depth: message.hopDepth,\n });\n }\n if (decisions.length === 0) return [];\n const earlier = await platform.conversations.recentMessages({\n conversationId: conversation.conversationId,\n limit: CONTEXT_WINDOW_MESSAGES,\n beforeSeq: message.seq,\n });\n const replyContext = await replyContextFor({ platform, message, earlier, participants });\n const turnPolicy = effectiveTurnPolicy({\n channel: args.turnPolicy,\n conversation: conversation.turnPolicy,\n });\n const audience = audienceFor(author, participants);\n const authorParticipant: ParticipantIface = author ?? {\n id: message.authorParticipantId,\n kind: ParticipantKind.User,\n displayName: 'Someone',\n userId: null,\n definitionId: null,\n routingMode: null,\n sessionId: null,\n supportsObserve: null,\n rendersAuthorMeta: null,\n left: true,\n };\n\n const deliverOne = async (decision: RouteDecisionIface): Promise<DeliveryOutcomeIface> => {\n const recipient = participants.find((participant) => participant.id === decision.participantId);\n const base = { participantId: decision.participantId, delivery: decision.delivery };\n if (recipient == null) {\n return { ...base, sent: 'skipped', messageId: null, error: null };\n }\n const idempotencyKey = deliveryIdempotencyKey(message.messageId, recipient.id);\n const prompt = async (content: ContentBlock[], replyExpected: boolean): Promise<string> => {\n const result = await platform.sessions.prompt({\n participantId: recipient.id,\n content,\n turnPolicy,\n replyExpected,\n idempotencyKey,\n authorParticipantId: message.authorParticipantId,\n audienceParticipantIds: audience,\n sourceMessageId: message.messageId,\n });\n return result.messageId;\n };\n try {\n if (decision.delivery === Delivery.Prompt) {\n const content = buildPromptContent({\n message,\n author: authorParticipant,\n recipient,\n replyContext,\n catchUp: catchUpFor({ recipient, earlier, participants }),\n });\n return { ...base, sent: 'prompt', messageId: await prompt(content, true), error: null };\n }\n if (recipient.sessionId == null) {\n return { ...base, sent: 'skipped', messageId: null, error: null };\n }\n const contextBlock = renderReplyContext(replyContext);\n const content = [\n ...(contextBlock == null ? [] : [contextBlock]),\n ...authorPrefixedContent({\n content: message.content,\n author: finalAuthorOf(authorParticipant),\n rendersAuthorMeta: recipient.rendersAuthorMeta ?? false,\n context: finalContextOf(replyContext),\n }),\n ];\n if (recipient.supportsObserve === false) {\n if (message.hopDepth >= MAX_AGENT_HOP_DEPTH) {\n return { ...base, sent: 'skipped', messageId: null, error: null };\n }\n return {\n ...base,\n sent: 'silent-prompt',\n messageId: await prompt(content, false),\n error: null,\n };\n }\n await platform.sessions.observe({ participantId: recipient.id, content, idempotencyKey });\n return { ...base, sent: 'observe', messageId: null, error: null };\n } catch (error) {\n // JSON-RPC failures arrive as plain `{ code, message }` objects.\n const reason =\n error instanceof Error\n ? error.message\n : (thrownMessageSchema.safeParse(error).data?.message ?? String(error));\n platform.log('channel delivery failed', {\n conversationId: conversation.conversationId,\n messageId: message.messageId,\n participantId: recipient.id,\n delivery: decision.delivery,\n error: reason,\n });\n return { ...base, sent: 'failed', messageId: null, error: reason };\n }\n };\n\n return Promise.all(decisions.map(deliverOne));\n};\n\nexport interface ObserveNoticeArgsIface {\n platform: ChannelPlatformIface;\n recipients: ParticipantIface[];\n text: string;\n idempotencyKey: string;\n}\n\n// System blocks with no model call. Agents without a session skip them (their\n// first prompt catches up); agents without observe support skip them too, since\n// a silent prompt per reaction is not worth a model call.\nexport const observeNotice = async (args: ObserveNoticeArgsIface): Promise<string[]> => {\n const delivered: string[] = [];\n for (const recipient of args.recipients) {\n if (recipient.sessionId == null || recipient.supportsObserve === false || recipient.left)\n continue;\n try {\n await args.platform.sessions.observe({\n participantId: recipient.id,\n content: [systemNoticeBlock({ text: args.text })],\n idempotencyKey: `${args.idempotencyKey}:${recipient.id}`,\n });\n delivered.push(recipient.id);\n } catch (error) {\n args.platform.log('channel notice failed', {\n participantId: recipient.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n return delivered;\n};\n\nexport interface ReactionNoticeArgsIface {\n platform: ChannelPlatformIface;\n participants: ParticipantIface[];\n message: ConversationMessageIface;\n // Whether the reacted message is the agent's latest one.\n isLatestOfAuthor: boolean;\n reactorParticipantId: string;\n emoji: string;\n eventId?: string;\n}\n\n// Only the agent whose message was reacted to hears about it.\nexport const reactionNotice = async (args: ReactionNoticeArgsIface): Promise<string[]> => {\n const author = args.participants.find(\n (participant) => participant.id === args.message.authorParticipantId,\n );\n const reactor = args.participants.find(\n (participant) => participant.id === args.reactorParticipantId,\n );\n if (\n author == null ||\n author.kind !== ParticipantKind.Agent ||\n reactor == null ||\n reactor.id === author.id\n ) {\n return [];\n }\n const target = args.isLatestOfAuthor\n ? 'your last message'\n : `your message \"${excerpt(messageSummary(args.message.content), 120)}\"`;\n const reactionId = `reaction:${args.message.messageId}:${reactor.id}:${args.emoji}`;\n return observeNotice({\n platform: args.platform,\n recipients: [author],\n text: `${reactor.displayName} reacted ${args.emoji} to ${target}`,\n idempotencyKey: args.eventId == null ? reactionId : `${reactionId}:${args.eventId}`,\n });\n};\n\nexport enum MembershipChange {\n Joined = 'joined',\n Left = 'left',\n}\n\nexport interface MembershipNoticeArgsIface {\n platform: ChannelPlatformIface;\n participants: ParticipantIface[];\n subject: ParticipantIface;\n change: MembershipChange;\n idempotencyKey: string;\n}\n\n// Membership changes reach agents that follow the whole conversation.\nexport const membershipNotice = async (args: MembershipNoticeArgsIface): Promise<string[]> => {\n const recipients = args.participants.filter(\n (participant) =>\n participant.kind === ParticipantKind.Agent &&\n participant.id !== args.subject.id &&\n (participant.routingMode === RoutingMode.All ||\n participant.routingMode === RoutingMode.Default),\n );\n const verb = args.change === MembershipChange.Joined ? 'joined' : 'left';\n return observeNotice({\n platform: args.platform,\n recipients,\n text: `${args.subject.displayName} ${verb} the conversation`,\n idempotencyKey: args.idempotencyKey,\n });\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["content", "MembershipChange"]
|
|
7
7
|
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import { ConversationKind, ParticipantKind, TurnPolicy, type ChannelPlatformIface, type ConversationIface, type ConversationMessageIface, type JsonObject, type ObserveArgsIface, type ParticipantIface, type PromptArgsIface } from '@finalai/agent-provider';
|
|
1
|
+
import { ConversationKind, ParticipantKind, TurnPolicy, type ChannelPlatformIface, type ChannelSendInputIface, type ChannelSessionTargetIface, type ConversationIface, type ConversationMessageIface, type JsonObject, type ObserveArgsIface, type ParticipantIface, type PromptArgsIface } from '@finalai/agent-provider';
|
|
2
|
+
export interface FakeInputIface {
|
|
3
|
+
target: ChannelSessionTargetIface;
|
|
4
|
+
input: ChannelSendInputIface;
|
|
5
|
+
}
|
|
2
6
|
export interface FakeMessageIface extends ConversationMessageIface {
|
|
3
7
|
final: boolean;
|
|
4
8
|
updates: number;
|
|
@@ -21,6 +25,7 @@ export interface FakePlatformIface {
|
|
|
21
25
|
cards: FakeCardIface[];
|
|
22
26
|
prompts: PromptArgsIface[];
|
|
23
27
|
observes: ObserveArgsIface[];
|
|
28
|
+
inputs: FakeInputIface[];
|
|
24
29
|
logs: string[];
|
|
25
30
|
getMessagesCalls: string[][];
|
|
26
31
|
addConversation(args: {
|
|
@@ -15,6 +15,7 @@ const createFakePlatform = () => {
|
|
|
15
15
|
const cards = [];
|
|
16
16
|
const prompts = [];
|
|
17
17
|
const observes = [];
|
|
18
|
+
const inputs = [];
|
|
18
19
|
const logs = [];
|
|
19
20
|
const getMessagesCalls = [];
|
|
20
21
|
let nextId = 1;
|
|
@@ -36,7 +37,6 @@ const createFakePlatform = () => {
|
|
|
36
37
|
displayName: partial.id,
|
|
37
38
|
userId: null,
|
|
38
39
|
definitionId: null,
|
|
39
|
-
systemKind: null,
|
|
40
40
|
routingMode: null,
|
|
41
41
|
sessionId: partial.kind === ParticipantKind.Agent ? `session-${partial.id}` : null,
|
|
42
42
|
supportsObserve: partial.kind === ParticipantKind.Agent ? true : null,
|
|
@@ -70,7 +70,36 @@ const createFakePlatform = () => {
|
|
|
70
70
|
messages.push(message);
|
|
71
71
|
return message;
|
|
72
72
|
};
|
|
73
|
+
const unsupported = async () => {
|
|
74
|
+
throw new Error("not supported by the fake platform");
|
|
75
|
+
};
|
|
73
76
|
const platform = {
|
|
77
|
+
channelSessions: {
|
|
78
|
+
send: async (target, input) => {
|
|
79
|
+
const prior = inputs.findIndex(
|
|
80
|
+
(entry) => entry.input.idempotencyKey === input.idempotencyKey
|
|
81
|
+
);
|
|
82
|
+
if (prior < 0) inputs.push({ target, input });
|
|
83
|
+
const agent = target.participantId == null ? null : (participants.get(target.conversationId ?? "") ?? []).find(
|
|
84
|
+
(participant) => participant.id === target.participantId
|
|
85
|
+
);
|
|
86
|
+
return {
|
|
87
|
+
inputId: `input-${prior < 0 ? inputs.length : prior + 1}`,
|
|
88
|
+
sessionId: agent?.sessionId ?? target.sessionId ?? `session-${target.address ?? "room"}`
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
inspect: unsupported,
|
|
92
|
+
resolve: unsupported,
|
|
93
|
+
respond: unsupported,
|
|
94
|
+
cancel: unsupported,
|
|
95
|
+
close: unsupported,
|
|
96
|
+
observe: unsupported,
|
|
97
|
+
setConfigOption: unsupported,
|
|
98
|
+
park: unsupported,
|
|
99
|
+
getState: unsupported,
|
|
100
|
+
setState: unsupported,
|
|
101
|
+
handoff: unsupported
|
|
102
|
+
},
|
|
74
103
|
conversations: {
|
|
75
104
|
dispatchMessage: async (input) => {
|
|
76
105
|
const room = conversations.get(input.conversationId);
|
|
@@ -91,17 +120,6 @@ const createFakePlatform = () => {
|
|
|
91
120
|
},
|
|
92
121
|
boundTo: async () => null,
|
|
93
122
|
participants: async (conversationId) => participants.get(conversationId) ?? [],
|
|
94
|
-
systemParticipant: async (args) => {
|
|
95
|
-
const existing = (participants.get(args.conversationId) ?? []).find(
|
|
96
|
-
(participant) => participant.kind === ParticipantKind.System && participant.id === args.sourceRef
|
|
97
|
-
);
|
|
98
|
-
return existing ?? addParticipant(args.conversationId, {
|
|
99
|
-
id: args.sourceRef,
|
|
100
|
-
kind: ParticipantKind.System,
|
|
101
|
-
systemKind: args.systemKind,
|
|
102
|
-
displayName: args.displayName
|
|
103
|
-
});
|
|
104
|
-
},
|
|
105
123
|
postMessage: async (args) => {
|
|
106
124
|
if (args.clientMessageId != null) {
|
|
107
125
|
const existing = messages.find(
|
|
@@ -173,6 +191,7 @@ const createFakePlatform = () => {
|
|
|
173
191
|
cards,
|
|
174
192
|
prompts,
|
|
175
193
|
observes,
|
|
194
|
+
inputs,
|
|
176
195
|
logs,
|
|
177
196
|
getMessagesCalls,
|
|
178
197
|
addConversation,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/channels/fake-platform.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';\nimport {\n ConversationKind,\n MessageKind,\n ParticipantKind,\n TurnPolicy,\n type CardArgsIface,\n type ChannelPlatformIface,\n type ConversationIface,\n type ConversationMessageIface,\n type JsonObject,\n type ObserveArgsIface,\n type ParticipantIface,\n type PromptArgsIface,\n} from '@finalai/agent-provider';\nimport { routeToOwner } from \"./agent-events.mjs\";\nimport { deliverMessage } from \"./delivery.mjs\";\nimport { messageSummary } from \"./prompt.mjs\";\nimport { routeMessage } from \"./routing.mjs\";\n\n// An in-memory ChannelPlatformIface for channel tests (not exported from the\n// package index). Records every prompt, observe and visible-message write.\n\nexport interface FakeMessageIface extends ConversationMessageIface {\n final: boolean;\n updates: number;\n source: { turnId: string; fromSeq: number; toSeq: number } | null;\n}\n\nexport interface FakeCardIface {\n messageId: string;\n pendingRequestId: string | null;\n state: JsonObject;\n}\n\nexport interface FakePlatformIface {\n platform: ChannelPlatformIface;\n conversations: Map<string, ConversationIface>;\n participants: Map<string, ParticipantIface[]>;\n messages: FakeMessageIface[];\n cards: FakeCardIface[];\n prompts: PromptArgsIface[];\n observes: ObserveArgsIface[];\n logs: string[];\n // The message ids of each getMessages call.\n getMessagesCalls: string[][];\n addConversation(args: {\n conversationId: string;\n kind: ConversationKind;\n turnPolicy?: TurnPolicy;\n }): ConversationIface;\n addParticipant(\n conversationId: string,\n participant: Partial<ParticipantIface> & { id: string; kind: ParticipantKind },\n ): ParticipantIface;\n}\n\nexport const createFakePlatform = (): FakePlatformIface => {\n const conversations = new Map<string, ConversationIface>();\n const participants = new Map<string, ParticipantIface[]>();\n const messages: FakeMessageIface[] = [];\n const cards: FakeCardIface[] = [];\n const prompts: PromptArgsIface[] = [];\n const observes: ObserveArgsIface[] = [];\n const logs: string[] = [];\n const getMessagesCalls: string[][] = [];\n let nextId = 1;\n\n const addConversation: FakePlatformIface['addConversation'] = (args) => {\n const conversation: ConversationIface = {\n conversationId: args.conversationId,\n organizationId: 'org',\n kind: args.kind,\n title: null,\n isHome: false,\n turnPolicy: args.turnPolicy ?? TurnPolicy.Steer,\n };\n conversations.set(args.conversationId, conversation);\n participants.set(args.conversationId, []);\n return conversation;\n };\n\n const addParticipant: FakePlatformIface['addParticipant'] = (conversationId, partial) => {\n const participant: ParticipantIface = {\n displayName: partial.id,\n userId: null,\n definitionId: null,\n systemKind: null,\n routingMode: null,\n sessionId: partial.kind === ParticipantKind.Agent ? `session-${partial.id}` : null,\n supportsObserve: partial.kind === ParticipantKind.Agent ? true : null,\n rendersAuthorMeta: partial.kind === ParticipantKind.Agent ? true : null,\n left: false,\n ...partial,\n };\n participants.get(conversationId)?.push(participant);\n return participant;\n };\n\n const nextSeq = (conversationId: string): number =>\n messages.filter((message) => message.conversationId === conversationId).length + 1;\n\n const insert = (args: {\n conversationId: string;\n authorParticipantId: string;\n kind: MessageKind;\n content: ContentBlock[];\n replyToMessageId?: string | null;\n threadRootId?: string | null;\n quotedMessageIds?: string[];\n mentionedParticipantIds?: string[];\n hopDepth?: number;\n source?: FakeMessageIface['source'];\n }): FakeMessageIface => {\n const message: FakeMessageIface = {\n messageId: `m${nextId++}`,\n seq: nextSeq(args.conversationId),\n conversationId: args.conversationId,\n authorParticipantId: args.authorParticipantId,\n kind: args.kind,\n body: messageSummary(args.content),\n content: args.content,\n replyToMessageId: args.replyToMessageId ?? null,\n threadRootId: args.threadRootId ?? null,\n quotedMessageIds: args.quotedMessageIds ?? [],\n mentionedParticipantIds: args.mentionedParticipantIds ?? [],\n hopDepth: args.hopDepth ?? 0,\n createdAt: new Date(0).toISOString(),\n final: args.source == null,\n updates: 0,\n source: args.source ?? null,\n };\n messages.push(message);\n return message;\n };\n\n const platform: ChannelPlatformIface = {\n conversations: {\n dispatchMessage: async (input) => {\n const room = conversations.get(input.conversationId);\n const message = messages.find((entry) => entry.messageId === input.messageId);\n if (room == null || message == null) throw new Error('dispatch source missing');\n await deliverMessage({\n platform,\n conversation: room,\n message,\n turnPolicy: room.turnPolicy,\n route: input.participantId == null ? routeMessage : routeToOwner(input.participantId),\n });\n },\n get: async (conversationId) => conversations.get(conversationId) ?? null,\n findOrCreateDirect: async (args) => {\n const id = `direct:${args.userId}:${args.definitionId}`;\n return (\n conversations.get(id) ??\n addConversation({ conversationId: id, kind: ConversationKind.Direct })\n );\n },\n boundTo: async () => null,\n participants: async (conversationId) => participants.get(conversationId) ?? [],\n systemParticipant: async (args) => {\n const existing = (participants.get(args.conversationId) ?? []).find(\n (participant) =>\n participant.kind === ParticipantKind.System && participant.id === args.sourceRef,\n );\n return (\n existing ??\n addParticipant(args.conversationId, {\n id: args.sourceRef,\n kind: ParticipantKind.System,\n systemKind: args.systemKind,\n displayName: args.displayName,\n })\n );\n },\n postMessage: async (args) => {\n if (args.clientMessageId != null) {\n const existing = messages.find(\n (message) =>\n message.conversationId === args.conversationId &&\n message.messageId === args.clientMessageId,\n );\n if (existing != null) return existing;\n }\n return insert({ ...args, kind: args.kind ?? MessageKind.Message });\n },\n updateMessage: async (args) => {\n const message = messages.find((candidate) => candidate.messageId === args.messageId);\n if (message == null) throw new Error(`no message ${args.messageId}`);\n message.content = args.content;\n message.body = messageSummary(args.content);\n message.final = args.final;\n message.updates += 1;\n },\n postCard: async (args: CardArgsIface) => {\n const message = insert({\n conversationId: args.conversationId,\n authorParticipantId: args.authorParticipantId,\n kind: MessageKind.Card,\n content: [],\n });\n cards.push({\n messageId: message.messageId,\n pendingRequestId: args.pendingRequestId,\n state: args.state,\n });\n return message;\n },\n updateCard: async (args) => {\n const card = cards.find(\n (candidate) =>\n candidate.messageId === args.messageId || candidate.pendingRequestId === args.messageId,\n );\n if (card == null) throw new Error(`no card ${args.messageId}`);\n card.state = { ...card.state, ...args.state };\n },\n recentMessages: async (args) =>\n messages\n .filter(\n (message) =>\n message.conversationId === args.conversationId &&\n (args.beforeSeq == null || message.seq < args.beforeSeq) &&\n (args.afterSeq == null || message.seq > args.afterSeq),\n )\n .slice(-args.limit),\n getMessages: async (args) => {\n getMessagesCalls.push(args.messageIds);\n return messages.filter(\n (message) =>\n message.conversationId === args.conversationId &&\n args.messageIds.includes(message.messageId),\n );\n },\n },\n sessions: {\n prompt: async (args) => {\n prompts.push(args);\n return { messageId: `acp-${prompts.length}` };\n },\n observe: async (args) => {\n observes.push(args);\n },\n cancel: async () => {},\n },\n log: (message) => {\n logs.push(message);\n },\n };\n\n return {\n platform,\n conversations,\n participants,\n messages,\n cards,\n prompts,\n observes,\n logs,\n getMessagesCalls,\n addConversation,\n addParticipant,\n };\n};\n"],
|
|
5
|
-
"mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,
|
|
4
|
+
"sourcesContent": ["import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';\nimport {\n ConversationKind,\n MessageKind,\n ParticipantKind,\n TurnPolicy,\n type CardArgsIface,\n type ChannelPlatformIface,\n type ChannelSendInputIface,\n type ChannelSessionTargetIface,\n type ConversationIface,\n type ConversationMessageIface,\n type JsonObject,\n type ObserveArgsIface,\n type ParticipantIface,\n type PromptArgsIface,\n} from '@finalai/agent-provider';\nimport { routeToOwner } from \"./agent-events.mjs\";\nimport { deliverMessage } from \"./delivery.mjs\";\nimport { messageSummary } from \"./prompt.mjs\";\nimport { routeMessage } from \"./routing.mjs\";\n\n// An in-memory ChannelPlatformIface for channel tests (not exported from the\n// package index). Records every prompt, observe, channel input and\n// visible-message write.\n\nexport interface FakeInputIface {\n target: ChannelSessionTargetIface;\n input: ChannelSendInputIface;\n}\n\nexport interface FakeMessageIface extends ConversationMessageIface {\n final: boolean;\n updates: number;\n source: { turnId: string; fromSeq: number; toSeq: number } | null;\n}\n\nexport interface FakeCardIface {\n messageId: string;\n pendingRequestId: string | null;\n state: JsonObject;\n}\n\nexport interface FakePlatformIface {\n platform: ChannelPlatformIface;\n conversations: Map<string, ConversationIface>;\n participants: Map<string, ParticipantIface[]>;\n messages: FakeMessageIface[];\n cards: FakeCardIface[];\n prompts: PromptArgsIface[];\n observes: ObserveArgsIface[];\n // Channel inputs sent (service input to a room's agent included), in order.\n inputs: FakeInputIface[];\n logs: string[];\n // The message ids of each getMessages call.\n getMessagesCalls: string[][];\n addConversation(args: {\n conversationId: string;\n kind: ConversationKind;\n turnPolicy?: TurnPolicy;\n }): ConversationIface;\n addParticipant(\n conversationId: string,\n participant: Partial<ParticipantIface> & { id: string; kind: ParticipantKind },\n ): ParticipantIface;\n}\n\nexport const createFakePlatform = (): FakePlatformIface => {\n const conversations = new Map<string, ConversationIface>();\n const participants = new Map<string, ParticipantIface[]>();\n const messages: FakeMessageIface[] = [];\n const cards: FakeCardIface[] = [];\n const prompts: PromptArgsIface[] = [];\n const observes: ObserveArgsIface[] = [];\n const inputs: FakeInputIface[] = [];\n const logs: string[] = [];\n const getMessagesCalls: string[][] = [];\n let nextId = 1;\n\n const addConversation: FakePlatformIface['addConversation'] = (args) => {\n const conversation: ConversationIface = {\n conversationId: args.conversationId,\n organizationId: 'org',\n kind: args.kind,\n title: null,\n isHome: false,\n turnPolicy: args.turnPolicy ?? TurnPolicy.Steer,\n };\n conversations.set(args.conversationId, conversation);\n participants.set(args.conversationId, []);\n return conversation;\n };\n\n const addParticipant: FakePlatformIface['addParticipant'] = (conversationId, partial) => {\n const participant: ParticipantIface = {\n displayName: partial.id,\n userId: null,\n definitionId: null,\n routingMode: null,\n sessionId: partial.kind === ParticipantKind.Agent ? `session-${partial.id}` : null,\n supportsObserve: partial.kind === ParticipantKind.Agent ? true : null,\n rendersAuthorMeta: partial.kind === ParticipantKind.Agent ? true : null,\n left: false,\n ...partial,\n };\n participants.get(conversationId)?.push(participant);\n return participant;\n };\n\n const nextSeq = (conversationId: string): number =>\n messages.filter((message) => message.conversationId === conversationId).length + 1;\n\n const insert = (args: {\n conversationId: string;\n authorParticipantId: string;\n kind: MessageKind;\n content: ContentBlock[];\n replyToMessageId?: string | null;\n threadRootId?: string | null;\n quotedMessageIds?: string[];\n mentionedParticipantIds?: string[];\n hopDepth?: number;\n source?: FakeMessageIface['source'];\n }): FakeMessageIface => {\n const message: FakeMessageIface = {\n messageId: `m${nextId++}`,\n seq: nextSeq(args.conversationId),\n conversationId: args.conversationId,\n authorParticipantId: args.authorParticipantId,\n kind: args.kind,\n body: messageSummary(args.content),\n content: args.content,\n replyToMessageId: args.replyToMessageId ?? null,\n threadRootId: args.threadRootId ?? null,\n quotedMessageIds: args.quotedMessageIds ?? [],\n mentionedParticipantIds: args.mentionedParticipantIds ?? [],\n hopDepth: args.hopDepth ?? 0,\n createdAt: new Date(0).toISOString(),\n final: args.source == null,\n updates: 0,\n source: args.source ?? null,\n };\n messages.push(message);\n return message;\n };\n\n const unsupported = async (): Promise<never> => {\n throw new Error('not supported by the fake platform');\n };\n const platform: ChannelPlatformIface = {\n channelSessions: {\n send: async (target, input) => {\n const prior = inputs.findIndex(\n (entry) => entry.input.idempotencyKey === input.idempotencyKey,\n );\n if (prior < 0) inputs.push({ target, input });\n const agent =\n target.participantId == null\n ? null\n : (participants.get(target.conversationId ?? '') ?? []).find(\n (participant) => participant.id === target.participantId,\n );\n return {\n inputId: `input-${prior < 0 ? inputs.length : prior + 1}`,\n sessionId: agent?.sessionId ?? target.sessionId ?? `session-${target.address ?? 'room'}`,\n };\n },\n inspect: unsupported,\n resolve: unsupported,\n respond: unsupported,\n cancel: unsupported,\n close: unsupported,\n observe: unsupported,\n setConfigOption: unsupported,\n park: unsupported,\n getState: unsupported,\n setState: unsupported,\n handoff: unsupported,\n },\n conversations: {\n dispatchMessage: async (input) => {\n const room = conversations.get(input.conversationId);\n const message = messages.find((entry) => entry.messageId === input.messageId);\n if (room == null || message == null) throw new Error('dispatch source missing');\n await deliverMessage({\n platform,\n conversation: room,\n message,\n turnPolicy: room.turnPolicy,\n route: input.participantId == null ? routeMessage : routeToOwner(input.participantId),\n });\n },\n get: async (conversationId) => conversations.get(conversationId) ?? null,\n findOrCreateDirect: async (args) => {\n const id = `direct:${args.userId}:${args.definitionId}`;\n return (\n conversations.get(id) ??\n addConversation({ conversationId: id, kind: ConversationKind.Direct })\n );\n },\n boundTo: async () => null,\n participants: async (conversationId) => participants.get(conversationId) ?? [],\n postMessage: async (args) => {\n if (args.clientMessageId != null) {\n const existing = messages.find(\n (message) =>\n message.conversationId === args.conversationId &&\n message.messageId === args.clientMessageId,\n );\n if (existing != null) return existing;\n }\n return insert({ ...args, kind: args.kind ?? MessageKind.Message });\n },\n updateMessage: async (args) => {\n const message = messages.find((candidate) => candidate.messageId === args.messageId);\n if (message == null) throw new Error(`no message ${args.messageId}`);\n message.content = args.content;\n message.body = messageSummary(args.content);\n message.final = args.final;\n message.updates += 1;\n },\n postCard: async (args: CardArgsIface) => {\n const message = insert({\n conversationId: args.conversationId,\n authorParticipantId: args.authorParticipantId,\n kind: MessageKind.Card,\n content: [],\n });\n cards.push({\n messageId: message.messageId,\n pendingRequestId: args.pendingRequestId,\n state: args.state,\n });\n return message;\n },\n updateCard: async (args) => {\n const card = cards.find(\n (candidate) =>\n candidate.messageId === args.messageId || candidate.pendingRequestId === args.messageId,\n );\n if (card == null) throw new Error(`no card ${args.messageId}`);\n card.state = { ...card.state, ...args.state };\n },\n recentMessages: async (args) =>\n messages\n .filter(\n (message) =>\n message.conversationId === args.conversationId &&\n (args.beforeSeq == null || message.seq < args.beforeSeq) &&\n (args.afterSeq == null || message.seq > args.afterSeq),\n )\n .slice(-args.limit),\n getMessages: async (args) => {\n getMessagesCalls.push(args.messageIds);\n return messages.filter(\n (message) =>\n message.conversationId === args.conversationId &&\n args.messageIds.includes(message.messageId),\n );\n },\n },\n sessions: {\n prompt: async (args) => {\n prompts.push(args);\n return { messageId: `acp-${prompts.length}` };\n },\n observe: async (args) => {\n observes.push(args);\n },\n cancel: async () => {},\n },\n log: (message) => {\n logs.push(message);\n },\n };\n\n return {\n platform,\n conversations,\n participants,\n messages,\n cards,\n prompts,\n observes,\n inputs,\n logs,\n getMessagesCalls,\n addConversation,\n addParticipant,\n };\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/channels/index.ts"],
|
|
4
|
-
"sourcesContent": ["export * from \"./agent-events.mjs\";\nexport * from \"./agent-schedules.mjs\";\nexport * 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 \"./pending.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;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
|
|
4
|
+
"sourcesContent": ["export * from \"./agent-events.mjs\";\nexport * from \"./agent-schedules.mjs\";\nexport * 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 \"./pending.mjs\";\nexport * from \"./projection.mjs\";\nexport * from \"./prompt.mjs\";\nexport * from \"./routing.mjs\";\nexport * from \"./service-input.mjs\";\n"],
|
|
5
|
+
"mappings": "AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';
|
|
2
2
|
import { type FinalAuthorIface, type FinalContextIface, type FinalMessageRefIface } from '@finalai/acp-session';
|
|
3
|
-
import { type ConversationMessageIface, type ParticipantIface } from '@finalai/agent-provider/channel';
|
|
3
|
+
import { type ChannelAuthIface, type ConversationMessageIface, type ParticipantIface } from '@finalai/agent-provider/channel';
|
|
4
4
|
export declare const EXCERPT_CHARS = 280;
|
|
5
5
|
export declare const CATCH_UP_MAX_MESSAGES = 20;
|
|
6
6
|
export declare const CATCH_UP_MAX_CHARS = 4000;
|
|
@@ -9,6 +9,10 @@ export declare const contentText: (content: ContentBlock[]) => string;
|
|
|
9
9
|
export declare const messageSummary: (content: ContentBlock[]) => string;
|
|
10
10
|
export declare const excerpt: (text: string, maxChars?: number) => string;
|
|
11
11
|
export declare const finalAuthorOf: (participant: ParticipantIface) => FinalAuthorIface;
|
|
12
|
+
export declare const principalAuthorOf: (args: {
|
|
13
|
+
auth: ChannelAuthIface;
|
|
14
|
+
agent?: boolean;
|
|
15
|
+
}) => FinalAuthorIface;
|
|
12
16
|
export interface AuthorPrefixedContentArgsIface {
|
|
13
17
|
content: ContentBlock[];
|
|
14
18
|
author: FinalAuthorIface;
|
|
@@ -43,8 +47,12 @@ export interface CatchUpArgsIface {
|
|
|
43
47
|
export declare const buildCatchUpBlock: (args: CatchUpArgsIface) => ContentBlock | null;
|
|
44
48
|
export declare const systemNoticeBlock: (args: {
|
|
45
49
|
text: string;
|
|
46
|
-
participantId: string;
|
|
47
50
|
}) => ContentBlock;
|
|
51
|
+
export declare const catchUpFor: (args: {
|
|
52
|
+
recipient: ParticipantIface;
|
|
53
|
+
earlier: ConversationMessageIface[];
|
|
54
|
+
participants: ParticipantIface[];
|
|
55
|
+
}) => ContentBlock | null;
|
|
48
56
|
export interface PromptContentArgsIface {
|
|
49
57
|
message: ConversationMessageIface;
|
|
50
58
|
author: ParticipantIface;
|
|
@@ -53,3 +61,12 @@ export interface PromptContentArgsIface {
|
|
|
53
61
|
catchUp: ContentBlock | null;
|
|
54
62
|
}
|
|
55
63
|
export declare const buildPromptContent: (args: PromptContentArgsIface) => ContentBlock[];
|
|
64
|
+
export interface ServiceInputPromptArgsIface {
|
|
65
|
+
content: ContentBlock[];
|
|
66
|
+
context?: string[];
|
|
67
|
+
author: FinalAuthorIface;
|
|
68
|
+
recipient: ParticipantIface;
|
|
69
|
+
earlier: ConversationMessageIface[];
|
|
70
|
+
participants: ParticipantIface[];
|
|
71
|
+
}
|
|
72
|
+
export declare const buildServiceInputPrompt: (args: ServiceInputPromptArgsIface) => ContentBlock[];
|
|
@@ -6,8 +6,11 @@ import {
|
|
|
6
6
|
withFinalMeta
|
|
7
7
|
} from "@finalai/acp-session";
|
|
8
8
|
import {
|
|
9
|
+
ChannelPrincipalType,
|
|
9
10
|
MessageKind,
|
|
10
|
-
ParticipantKind
|
|
11
|
+
ParticipantKind,
|
|
12
|
+
principalDisplayName,
|
|
13
|
+
RoutingMode
|
|
11
14
|
} from "@finalai/agent-provider/channel";
|
|
12
15
|
const EXCERPT_CHARS = 280;
|
|
13
16
|
const CATCH_UP_MAX_MESSAGES = 20;
|
|
@@ -26,21 +29,20 @@ const excerpt = (text, maxChars = EXCERPT_CHARS) => {
|
|
|
26
29
|
const normalized = text.replace(/\s+/gu, " ").trim();
|
|
27
30
|
return normalized.length <= maxChars ? normalized : `${normalized.slice(0, maxChars - 1)}\u2026`;
|
|
28
31
|
};
|
|
29
|
-
const authorKindOf = (kind) => {
|
|
30
|
-
switch (kind) {
|
|
31
|
-
case ParticipantKind.Agent:
|
|
32
|
-
return FinalAuthorKind.Agent;
|
|
33
|
-
case ParticipantKind.System:
|
|
34
|
-
return FinalAuthorKind.System;
|
|
35
|
-
case ParticipantKind.User:
|
|
36
|
-
return FinalAuthorKind.User;
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
32
|
const finalAuthorOf = (participant) => ({
|
|
40
33
|
participantId: participant.id,
|
|
41
|
-
kind:
|
|
34
|
+
kind: participant.kind === ParticipantKind.Agent ? FinalAuthorKind.Agent : FinalAuthorKind.User,
|
|
42
35
|
displayName: participant.displayName
|
|
43
36
|
});
|
|
37
|
+
const principalAuthorOf = (args) => ({
|
|
38
|
+
kind: args.agent === true ? FinalAuthorKind.Agent : args.auth.principalType === ChannelPrincipalType.User ? FinalAuthorKind.User : FinalAuthorKind.Service,
|
|
39
|
+
displayName: principalDisplayName(args.auth),
|
|
40
|
+
principal: {
|
|
41
|
+
principalId: args.auth.principalId,
|
|
42
|
+
principalType: args.auth.principalType,
|
|
43
|
+
authenticator: args.auth.authenticator
|
|
44
|
+
}
|
|
45
|
+
});
|
|
44
46
|
const withContentRole = (block, context) => withFinalMeta(block, { role: FinalContentRole.Context, context });
|
|
45
47
|
const authorPrefixedContent = (args) => {
|
|
46
48
|
const blocks = args.rendersAuthorMeta ? args.content : promptTextWithAuthorPrefix(args.content, args.author.displayName);
|
|
@@ -140,12 +142,18 @@ const buildCatchUpBlock = (args) => {
|
|
|
140
142
|
return withContentRole(textBlock([header, ...omittedLine, ...lines].join("\n")));
|
|
141
143
|
};
|
|
142
144
|
const systemNoticeBlock = (args) => withFinalMeta(textBlock(args.text), {
|
|
143
|
-
author: {
|
|
144
|
-
participantId: args.participantId,
|
|
145
|
-
kind: FinalAuthorKind.System,
|
|
146
|
-
displayName: "Final"
|
|
147
|
-
}
|
|
145
|
+
author: { kind: FinalAuthorKind.Service, displayName: "Final" }
|
|
148
146
|
});
|
|
147
|
+
const catchUpFor = (args) => {
|
|
148
|
+
const since = args.recipient.sessionId == null ? "start" /* Start */ : args.recipient.routingMode === RoutingMode.Mention ? "last-contact" /* LastContact */ : null;
|
|
149
|
+
if (since == null) return null;
|
|
150
|
+
return buildCatchUpBlock({
|
|
151
|
+
messages: args.earlier,
|
|
152
|
+
participants: args.participants,
|
|
153
|
+
agentParticipantId: args.recipient.id,
|
|
154
|
+
since
|
|
155
|
+
});
|
|
156
|
+
};
|
|
149
157
|
const buildPromptContent = (args) => {
|
|
150
158
|
const contextBlock = renderReplyContext(args.replyContext);
|
|
151
159
|
const body = authorPrefixedContent({
|
|
@@ -160,6 +168,22 @@ const buildPromptContent = (args) => {
|
|
|
160
168
|
...body
|
|
161
169
|
];
|
|
162
170
|
};
|
|
171
|
+
const buildServiceInputPrompt = (args) => {
|
|
172
|
+
const catchUp = catchUpFor({
|
|
173
|
+
recipient: args.recipient,
|
|
174
|
+
earlier: args.earlier,
|
|
175
|
+
participants: args.participants
|
|
176
|
+
});
|
|
177
|
+
return [
|
|
178
|
+
...catchUp == null ? [] : [catchUp],
|
|
179
|
+
...(args.context ?? []).map((text) => withContentRole(textBlock(text))),
|
|
180
|
+
...authorPrefixedContent({
|
|
181
|
+
content: args.content,
|
|
182
|
+
author: args.author,
|
|
183
|
+
rendersAuthorMeta: args.recipient.rendersAuthorMeta ?? false
|
|
184
|
+
})
|
|
185
|
+
];
|
|
186
|
+
};
|
|
163
187
|
export {
|
|
164
188
|
CATCH_UP_MAX_CHARS,
|
|
165
189
|
CATCH_UP_MAX_MESSAGES,
|
|
@@ -168,12 +192,15 @@ export {
|
|
|
168
192
|
authorPrefixedContent,
|
|
169
193
|
buildCatchUpBlock,
|
|
170
194
|
buildPromptContent,
|
|
195
|
+
buildServiceInputPrompt,
|
|
196
|
+
catchUpFor,
|
|
171
197
|
contentText,
|
|
172
198
|
excerpt,
|
|
173
199
|
finalAuthorOf,
|
|
174
200
|
finalContextOf,
|
|
175
201
|
messageRefOf,
|
|
176
202
|
messageSummary,
|
|
203
|
+
principalAuthorOf,
|
|
177
204
|
renderReplyContext,
|
|
178
205
|
systemNoticeBlock,
|
|
179
206
|
textBlock
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/channels/prompt.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';\nimport {\n FinalAuthorKind,\n FinalContentRole,\n promptTextWithAuthorPrefix,\n textOf,\n withFinalMeta,\n type FinalAuthorIface,\n type FinalContextIface,\n type FinalMessageRefIface,\n} from '@finalai/acp-session';\nimport {\n MessageKind,\n ParticipantKind,\n type ConversationMessageIface,\n type ParticipantIface,\n} from '@finalai/agent-provider/channel';\n\nexport const EXCERPT_CHARS = 280;\nexport const CATCH_UP_MAX_MESSAGES = 20;\nexport const CATCH_UP_MAX_CHARS = 4_000;\n\nexport const textBlock = (text: string): ContentBlock => ({ type: 'text', text });\n\nexport const contentText = (content: ContentBlock[]): string =>\n textOf(content, { separator: '\\n' }).trim();\n\n// A one-line description of a message for previews, excerpts and transcripts.\nexport const messageSummary = (content: ContentBlock[]): string => {\n const text = contentText(content);\n if (text.length > 0) {\n return text;\n }\n const attachments = content.filter((block) => block.type !== 'text').length;\n return attachments === 0 ? '' : `[${attachments} attachment${attachments === 1 ? '' : 's'}]`;\n};\n\nexport const excerpt = (text: string, maxChars: number = EXCERPT_CHARS): string => {\n const normalized = text.replace(/\\s+/gu, ' ').trim();\n return normalized.length <= maxChars ? normalized : `${normalized.slice(0, maxChars - 1)}\u2026`;\n};\n\nconst authorKindOf = (kind: ParticipantKind): FinalAuthorKind => {\n switch (kind) {\n case ParticipantKind.Agent:\n return FinalAuthorKind.Agent;\n case ParticipantKind.System:\n return FinalAuthorKind.System;\n case ParticipantKind.User:\n return FinalAuthorKind.User;\n }\n};\n\nexport const finalAuthorOf = (participant: ParticipantIface): FinalAuthorIface => ({\n participantId: participant.id,\n kind: authorKindOf(participant.kind),\n displayName: participant.displayName,\n});\n\nconst withContentRole = (block: ContentBlock, context?: FinalContextIface): ContentBlock =>\n withFinalMeta(block, { role: FinalContentRole.Context, context });\n\n// Attribution: `_meta[\"final.ai\"].author` on every block, plus a\n// `<name>: ` prefix for harnesses that do not render author meta.\nexport interface AuthorPrefixedContentArgsIface {\n content: ContentBlock[];\n author: FinalAuthorIface;\n rendersAuthorMeta: boolean;\n context?: FinalContextIface;\n}\n\nexport const authorPrefixedContent = (args: AuthorPrefixedContentArgsIface): ContentBlock[] => {\n const blocks: ContentBlock[] = args.rendersAuthorMeta\n ? args.content\n : promptTextWithAuthorPrefix(args.content, args.author.displayName);\n return blocks.map((block, index) =>\n withFinalMeta(block, { author: args.author, context: index === 0 ? args.context : undefined }),\n );\n};\n\nexport interface ReplyContextArgsIface {\n replyTo?: FinalMessageRefIface | null;\n thread?: {\n rootMessageId: string;\n rootExcerpt: string;\n rootAuthor?: FinalAuthorIface | null;\n } | null;\n quotes?: FinalMessageRefIface[];\n}\n\nexport const finalContextOf = (args: ReplyContextArgsIface): FinalContextIface | undefined => {\n const context: FinalContextIface = {};\n if (args.replyTo != null) context.replyTo = args.replyTo;\n if (args.thread != null) {\n context.thread = {\n rootMessageId: args.thread.rootMessageId,\n rootExcerpt: args.thread.rootExcerpt,\n };\n }\n if (args.quotes != null && args.quotes.length > 0) context.quotes = args.quotes;\n return Object.keys(context).length === 0 ? undefined : context;\n};\n\n// The rendered reply/thread/quote block, for harnesses that ignore\n// `_meta`. Null when the message has no context.\nexport const renderReplyContext = (args: ReplyContextArgsIface): ContentBlock | null => {\n const context = finalContextOf(args);\n if (context == null) {\n return null;\n }\n const lines: string[] = [];\n if (args.thread != null) {\n const by =\n args.thread.rootAuthor == null ? '' : ` started by ${args.thread.rootAuthor.displayName}`;\n lines.push(`In a thread${by}: \"${args.thread.rootExcerpt}\"`);\n }\n if (args.replyTo != null) {\n lines.push(\n `Replying to ${args.replyTo.author?.displayName ?? 'a message'}: \"${args.replyTo.excerpt}\"`,\n );\n }\n for (const quote of args.quotes ?? []) {\n lines.push(`Quoting ${quote.author?.displayName ?? 'a message'}: \"${quote.excerpt}\"`);\n }\n return withContentRole(textBlock(lines.join('\\n')), context);\n};\n\nexport const messageRefOf = (\n message: ConversationMessageIface,\n participants: ParticipantIface[],\n): FinalMessageRefIface => {\n const author = participants.find((participant) => participant.id === message.authorParticipantId);\n return {\n messageId: message.messageId,\n author: author == null ? null : finalAuthorOf(author),\n excerpt: excerpt(messageSummary(message.content)),\n };\n};\n\nconst transcriptLine = (\n message: ConversationMessageIface,\n participants: ParticipantIface[],\n): string => {\n const author = participants.find((participant) => participant.id === message.authorParticipantId);\n return `${author?.displayName ?? 'Someone'}: ${messageSummary(message.content)}`;\n};\n\nconst visibleForTranscript = (message: ConversationMessageIface): boolean =>\n message.kind !== MessageKind.Card && messageSummary(message.content).length > 0;\n\ninterface BoundedLinesArgsIface {\n messages: ConversationMessageIface[];\n participants: ParticipantIface[];\n maxMessages: number;\n maxChars: number;\n}\n\ninterface BoundedLinesIface {\n lines: string[];\n omitted: number;\n}\n\n// Lines from newest backwards until a bound is hit, returned oldest first.\nconst boundedLines = (args: BoundedLinesArgsIface): BoundedLinesIface => {\n const lines: string[] = [];\n let chars = 0;\n const ordered = [...args.messages].sort((left, right) => right.seq - left.seq);\n for (const message of ordered) {\n const line = transcriptLine(message, args.participants);\n if (lines.length >= args.maxMessages || chars + line.length > args.maxChars) {\n break;\n }\n lines.push(line);\n chars += line.length + 1;\n }\n return { lines: lines.reverse(), omitted: ordered.length - lines.length };\n};\n\nexport enum CatchUpSince {\n // A session that has never seen this conversation (new or reset).\n Start = 'start',\n // An agent that does not observe: since it last spoke or was mentioned.\n LastContact = 'last-contact',\n}\n\nexport interface CatchUpArgsIface {\n // Messages before the one being delivered, any order.\n messages: ConversationMessageIface[];\n participants: ParticipantIface[];\n agentParticipantId: string;\n since: CatchUpSince;\n maxMessages?: number;\n maxChars?: number;\n}\n\n// \"Earlier in this conversation, not previously shown: \u2026\", marked\n// as context. Null when there is nothing the agent has not seen.\nexport const buildCatchUpBlock = (args: CatchUpArgsIface): ContentBlock | null => {\n let unseen = args.messages.filter(\n (message) =>\n visibleForTranscript(message) && message.authorParticipantId !== args.agentParticipantId,\n );\n if (args.since === CatchUpSince.LastContact) {\n const lastContactSeq = args.messages.reduce(\n (latest, message) =>\n message.authorParticipantId === args.agentParticipantId ||\n message.mentionedParticipantIds.includes(args.agentParticipantId)\n ? Math.max(latest, message.seq)\n : latest,\n -1,\n );\n unseen = unseen.filter((message) => message.seq > lastContactSeq);\n }\n if (unseen.length === 0) {\n return null;\n }\n const { lines, omitted } = boundedLines({\n messages: unseen,\n participants: args.participants,\n maxMessages: args.maxMessages ?? CATCH_UP_MAX_MESSAGES,\n maxChars: args.maxChars ?? CATCH_UP_MAX_CHARS,\n });\n if (lines.length === 0) {\n return null;\n }\n const header = 'Earlier in this conversation, not previously shown:';\n const omittedLine =\n omitted > 0 ? [`(${omitted} earlier message${omitted === 1 ? '' : 's'} omitted)`] : [];\n return withContentRole(textBlock([header, ...omittedLine, ...lines].join('\\n')));\n};\n\n// A system-authored block delivered by observe: reactions, membership changes.\nexport const systemNoticeBlock = (args: { text: string; participantId: string }): ContentBlock =>\n withFinalMeta(textBlock(args.text), {\n author: {\n participantId: args.participantId,\n kind: FinalAuthorKind.System,\n displayName: 'Final',\n },\n });\n\nexport interface PromptContentArgsIface {\n message: ConversationMessageIface;\n author: ParticipantIface;\n recipient: ParticipantIface;\n replyContext: ReplyContextArgsIface;\n catchUp: ContentBlock | null;\n}\n\n// The ACP prompt one agent receives for a conversation message.\nexport const buildPromptContent = (args: PromptContentArgsIface): ContentBlock[] => {\n const contextBlock = renderReplyContext(args.replyContext);\n const body = authorPrefixedContent({\n content:\n args.message.content.length > 0 ? args.message.content : [textBlock(args.message.body)],\n author: finalAuthorOf(args.author),\n rendersAuthorMeta: args.recipient.rendersAuthorMeta ?? false,\n context: finalContextOf(args.replyContext),\n });\n return [\n ...(args.catchUp == null ? [] : [args.catchUp]),\n ...(contextBlock == null ? [] : [contextBlock]),\n ...body,\n ];\n};\n"],
|
|
5
|
-
"mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP;AAAA,EACE;AAAA,EACA;AAAA,
|
|
4
|
+
"sourcesContent": ["import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';\nimport {\n FinalAuthorKind,\n FinalContentRole,\n promptTextWithAuthorPrefix,\n textOf,\n withFinalMeta,\n type FinalAuthorIface,\n type FinalContextIface,\n type FinalMessageRefIface,\n} from '@finalai/acp-session';\nimport {\n ChannelPrincipalType,\n MessageKind,\n ParticipantKind,\n principalDisplayName,\n RoutingMode,\n type ChannelAuthIface,\n type ConversationMessageIface,\n type ParticipantIface,\n} from '@finalai/agent-provider/channel';\n\nexport const EXCERPT_CHARS = 280;\nexport const CATCH_UP_MAX_MESSAGES = 20;\nexport const CATCH_UP_MAX_CHARS = 4_000;\n\nexport const textBlock = (text: string): ContentBlock => ({ type: 'text', text });\n\nexport const contentText = (content: ContentBlock[]): string =>\n textOf(content, { separator: '\\n' }).trim();\n\n// A one-line description of a message for previews, excerpts and transcripts.\nexport const messageSummary = (content: ContentBlock[]): string => {\n const text = contentText(content);\n if (text.length > 0) {\n return text;\n }\n const attachments = content.filter((block) => block.type !== 'text').length;\n return attachments === 0 ? '' : `[${attachments} attachment${attachments === 1 ? '' : 's'}]`;\n};\n\nexport const excerpt = (text: string, maxChars: number = EXCERPT_CHARS): string => {\n const normalized = text.replace(/\\s+/gu, ' ').trim();\n return normalized.length <= maxChars ? normalized : `${normalized.slice(0, maxChars - 1)}\u2026`;\n};\n\nexport const finalAuthorOf = (participant: ParticipantIface): FinalAuthorIface => ({\n participantId: participant.id,\n kind: participant.kind === ParticipantKind.Agent ? FinalAuthorKind.Agent : FinalAuthorKind.User,\n displayName: participant.displayName,\n});\n\n// The author an agent sees for channel input from no member: the principal the\n// channel authenticated, named as the channel named it. `agent` marks input an\n// agent wrote elsewhere (another chat), so the recipient knows it is not a person.\nexport const principalAuthorOf = (args: {\n auth: ChannelAuthIface;\n agent?: boolean;\n}): FinalAuthorIface => ({\n kind:\n args.agent === true\n ? FinalAuthorKind.Agent\n : args.auth.principalType === ChannelPrincipalType.User\n ? FinalAuthorKind.User\n : FinalAuthorKind.Service,\n displayName: principalDisplayName(args.auth),\n principal: {\n principalId: args.auth.principalId,\n principalType: args.auth.principalType,\n authenticator: args.auth.authenticator,\n },\n});\n\nconst withContentRole = (block: ContentBlock, context?: FinalContextIface): ContentBlock =>\n withFinalMeta(block, { role: FinalContentRole.Context, context });\n\n// Attribution: `_meta[\"final.ai\"].author` on every block, plus a\n// `<name>: ` prefix for harnesses that do not render author meta.\nexport interface AuthorPrefixedContentArgsIface {\n content: ContentBlock[];\n author: FinalAuthorIface;\n rendersAuthorMeta: boolean;\n context?: FinalContextIface;\n}\n\nexport const authorPrefixedContent = (args: AuthorPrefixedContentArgsIface): ContentBlock[] => {\n const blocks: ContentBlock[] = args.rendersAuthorMeta\n ? args.content\n : promptTextWithAuthorPrefix(args.content, args.author.displayName);\n return blocks.map((block, index) =>\n withFinalMeta(block, { author: args.author, context: index === 0 ? args.context : undefined }),\n );\n};\n\nexport interface ReplyContextArgsIface {\n replyTo?: FinalMessageRefIface | null;\n thread?: {\n rootMessageId: string;\n rootExcerpt: string;\n rootAuthor?: FinalAuthorIface | null;\n } | null;\n quotes?: FinalMessageRefIface[];\n}\n\nexport const finalContextOf = (args: ReplyContextArgsIface): FinalContextIface | undefined => {\n const context: FinalContextIface = {};\n if (args.replyTo != null) context.replyTo = args.replyTo;\n if (args.thread != null) {\n context.thread = {\n rootMessageId: args.thread.rootMessageId,\n rootExcerpt: args.thread.rootExcerpt,\n };\n }\n if (args.quotes != null && args.quotes.length > 0) context.quotes = args.quotes;\n return Object.keys(context).length === 0 ? undefined : context;\n};\n\n// The rendered reply/thread/quote block, for harnesses that ignore\n// `_meta`. Null when the message has no context.\nexport const renderReplyContext = (args: ReplyContextArgsIface): ContentBlock | null => {\n const context = finalContextOf(args);\n if (context == null) {\n return null;\n }\n const lines: string[] = [];\n if (args.thread != null) {\n const by =\n args.thread.rootAuthor == null ? '' : ` started by ${args.thread.rootAuthor.displayName}`;\n lines.push(`In a thread${by}: \"${args.thread.rootExcerpt}\"`);\n }\n if (args.replyTo != null) {\n lines.push(\n `Replying to ${args.replyTo.author?.displayName ?? 'a message'}: \"${args.replyTo.excerpt}\"`,\n );\n }\n for (const quote of args.quotes ?? []) {\n lines.push(`Quoting ${quote.author?.displayName ?? 'a message'}: \"${quote.excerpt}\"`);\n }\n return withContentRole(textBlock(lines.join('\\n')), context);\n};\n\nexport const messageRefOf = (\n message: ConversationMessageIface,\n participants: ParticipantIface[],\n): FinalMessageRefIface => {\n const author = participants.find((participant) => participant.id === message.authorParticipantId);\n return {\n messageId: message.messageId,\n author: author == null ? null : finalAuthorOf(author),\n excerpt: excerpt(messageSummary(message.content)),\n };\n};\n\nconst transcriptLine = (\n message: ConversationMessageIface,\n participants: ParticipantIface[],\n): string => {\n const author = participants.find((participant) => participant.id === message.authorParticipantId);\n return `${author?.displayName ?? 'Someone'}: ${messageSummary(message.content)}`;\n};\n\nconst visibleForTranscript = (message: ConversationMessageIface): boolean =>\n message.kind !== MessageKind.Card && messageSummary(message.content).length > 0;\n\ninterface BoundedLinesArgsIface {\n messages: ConversationMessageIface[];\n participants: ParticipantIface[];\n maxMessages: number;\n maxChars: number;\n}\n\ninterface BoundedLinesIface {\n lines: string[];\n omitted: number;\n}\n\n// Lines from newest backwards until a bound is hit, returned oldest first.\nconst boundedLines = (args: BoundedLinesArgsIface): BoundedLinesIface => {\n const lines: string[] = [];\n let chars = 0;\n const ordered = [...args.messages].sort((left, right) => right.seq - left.seq);\n for (const message of ordered) {\n const line = transcriptLine(message, args.participants);\n if (lines.length >= args.maxMessages || chars + line.length > args.maxChars) {\n break;\n }\n lines.push(line);\n chars += line.length + 1;\n }\n return { lines: lines.reverse(), omitted: ordered.length - lines.length };\n};\n\nexport enum CatchUpSince {\n // A session that has never seen this conversation (new or reset).\n Start = 'start',\n // An agent that does not observe: since it last spoke or was mentioned.\n LastContact = 'last-contact',\n}\n\nexport interface CatchUpArgsIface {\n // Messages before the one being delivered, any order.\n messages: ConversationMessageIface[];\n participants: ParticipantIface[];\n agentParticipantId: string;\n since: CatchUpSince;\n maxMessages?: number;\n maxChars?: number;\n}\n\n// \"Earlier in this conversation, not previously shown: \u2026\", marked\n// as context. Null when there is nothing the agent has not seen.\nexport const buildCatchUpBlock = (args: CatchUpArgsIface): ContentBlock | null => {\n let unseen = args.messages.filter(\n (message) =>\n visibleForTranscript(message) && message.authorParticipantId !== args.agentParticipantId,\n );\n if (args.since === CatchUpSince.LastContact) {\n const lastContactSeq = args.messages.reduce(\n (latest, message) =>\n message.authorParticipantId === args.agentParticipantId ||\n message.mentionedParticipantIds.includes(args.agentParticipantId)\n ? Math.max(latest, message.seq)\n : latest,\n -1,\n );\n unseen = unseen.filter((message) => message.seq > lastContactSeq);\n }\n if (unseen.length === 0) {\n return null;\n }\n const { lines, omitted } = boundedLines({\n messages: unseen,\n participants: args.participants,\n maxMessages: args.maxMessages ?? CATCH_UP_MAX_MESSAGES,\n maxChars: args.maxChars ?? CATCH_UP_MAX_CHARS,\n });\n if (lines.length === 0) {\n return null;\n }\n const header = 'Earlier in this conversation, not previously shown:';\n const omittedLine =\n omitted > 0 ? [`(${omitted} earlier message${omitted === 1 ? '' : 's'} omitted)`] : [];\n return withContentRole(textBlock([header, ...omittedLine, ...lines].join('\\n')));\n};\n\n// A notice from Final itself delivered by observe: reactions, membership changes.\nexport const systemNoticeBlock = (args: { text: string }): ContentBlock =>\n withFinalMeta(textBlock(args.text), {\n author: { kind: FinalAuthorKind.Service, displayName: 'Final' },\n });\n\n// What a recipient has not seen of its conversation before a prompt: a fresh\n// session has seen nothing; a Mention agent sees only what it is prompted\n// with; everyone else has been observing.\nexport const catchUpFor = (args: {\n recipient: ParticipantIface;\n earlier: ConversationMessageIface[];\n participants: ParticipantIface[];\n}): ContentBlock | null => {\n const since =\n args.recipient.sessionId == null\n ? CatchUpSince.Start\n : args.recipient.routingMode === RoutingMode.Mention\n ? CatchUpSince.LastContact\n : null;\n if (since == null) return null;\n return buildCatchUpBlock({\n messages: args.earlier,\n participants: args.participants,\n agentParticipantId: args.recipient.id,\n since,\n });\n};\n\nexport interface PromptContentArgsIface {\n message: ConversationMessageIface;\n author: ParticipantIface;\n recipient: ParticipantIface;\n replyContext: ReplyContextArgsIface;\n catchUp: ContentBlock | null;\n}\n\n// The ACP prompt one agent receives for a conversation message.\nexport const buildPromptContent = (args: PromptContentArgsIface): ContentBlock[] => {\n const contextBlock = renderReplyContext(args.replyContext);\n const body = authorPrefixedContent({\n content:\n args.message.content.length > 0 ? args.message.content : [textBlock(args.message.body)],\n author: finalAuthorOf(args.author),\n rendersAuthorMeta: args.recipient.rendersAuthorMeta ?? false,\n context: finalContextOf(args.replyContext),\n });\n return [\n ...(args.catchUp == null ? [] : [args.catchUp]),\n ...(contextBlock == null ? [] : [contextBlock]),\n ...body,\n ];\n};\n\nexport interface ServiceInputPromptArgsIface {\n content: ContentBlock[];\n // Channel-supplied context lines, marked as context before the input.\n context?: string[];\n author: FinalAuthorIface;\n recipient: ParticipantIface;\n // The room's latest messages when the input is delivered, any order.\n earlier: ConversationMessageIface[];\n participants: ParticipantIface[];\n}\n\n// The ACP prompt a room's agent receives for channel input: what it has not seen\n// of the room up to its latest message, then the input attributed to its principal.\nexport const buildServiceInputPrompt = (args: ServiceInputPromptArgsIface): ContentBlock[] => {\n const catchUp = catchUpFor({\n recipient: args.recipient,\n earlier: args.earlier,\n participants: args.participants,\n });\n return [\n ...(catchUp == null ? [] : [catchUp]),\n ...(args.context ?? []).map((text) => withContentRole(textBlock(text))),\n ...authorPrefixedContent({\n content: args.content,\n author: args.author,\n rendersAuthorMeta: args.recipient.rendersAuthorMeta ?? false,\n }),\n ];\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["CatchUpSince"]
|
|
7
7
|
}
|