@finalai/final-agent-provider 0.8.0 → 0.10.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.
- package/HOSTING.md +3 -3
- package/README.md +7 -91
- package/dist/modules/channel-factories.d.mts +5 -6
- package/dist/modules/channel-factories.mjs +7 -11
- package/dist/modules/channel-factories.mjs.map +3 -3
- package/dist/modules/channels/inbound.mjs +2 -2
- package/dist/modules/channels/inbound.mjs.map +2 -2
- package/dist/modules/channels/index.d.mts +0 -7
- package/dist/modules/channels/index.mjs +0 -7
- package/dist/modules/channels/index.mjs.map +2 -2
- package/dist/modules/channels/prompt.d.mts +2 -2
- package/dist/modules/channels/prompt.mjs +3 -3
- package/dist/modules/channels/prompt.mjs.map +2 -2
- package/dist/modules/channels/routing.d.mts +1 -1
- package/dist/modules/channels/routing.mjs +1 -1
- package/dist/modules/channels/routing.mjs.map +1 -1
- package/package.json +4 -40
- package/dist/modules/channels/channel-utils.d.mts +0 -132
- package/dist/modules/channels/channel-utils.mjs +0 -445
- package/dist/modules/channels/channel-utils.mjs.map +0 -7
- package/dist/modules/channels/discord-dispatch.d.mts +0 -17
- package/dist/modules/channels/discord-dispatch.mjs +0 -139
- package/dist/modules/channels/discord-dispatch.mjs.map +0 -7
- package/dist/modules/channels/discord-gateway.d.mts +0 -19
- package/dist/modules/channels/discord-gateway.mjs +0 -233
- package/dist/modules/channels/discord-gateway.mjs.map +0 -7
- package/dist/modules/channels/discord.d.mts +0 -9
- package/dist/modules/channels/discord.mjs +0 -904
- package/dist/modules/channels/discord.mjs.map +0 -7
- package/dist/modules/channels/github.d.mts +0 -5
- package/dist/modules/channels/github.mjs +0 -720
- package/dist/modules/channels/github.mjs.map +0 -7
- package/dist/modules/channels/linear.d.mts +0 -4
- package/dist/modules/channels/linear.mjs +0 -621
- package/dist/modules/channels/linear.mjs.map +0 -7
- package/dist/modules/channels/phone.d.mts +0 -8
- package/dist/modules/channels/phone.mjs +0 -382
- package/dist/modules/channels/phone.mjs.map +0 -7
- package/dist/modules/channels/reply-receipts.d.mts +0 -31
- package/dist/modules/channels/reply-receipts.mjs +0 -63
- package/dist/modules/channels/reply-receipts.mjs.map +0 -7
- package/dist/modules/channels/slack.d.mts +0 -7
- package/dist/modules/channels/slack.mjs +0 -1134
- package/dist/modules/channels/slack.mjs.map +0 -7
- package/dist/modules/channels/voice-recording.d.mts +0 -7
- package/dist/modules/channels/voice-recording.mjs +0 -49
- package/dist/modules/channels/voice-recording.mjs.map +0 -7
- package/dist/modules/channels/voice.d.mts +0 -33
- package/dist/modules/channels/voice.mjs +0 -851
- package/dist/modules/channels/voice.mjs.map +0 -7
- package/dist/modules/channels/webhook.d.mts +0 -5
- package/dist/modules/channels/webhook.mjs +0 -76
- package/dist/modules/channels/webhook.mjs.map +0 -7
package/HOSTING.md
CHANGED
|
@@ -269,6 +269,6 @@ Optional readers preserve source compatibility with existing hosts. Definitions
|
|
|
269
269
|
requesting bindings fail clearly if hosting does not provide them. Installing these
|
|
270
270
|
packages alone does not change Final's deployed service wiring or provision secrets.
|
|
271
271
|
|
|
272
|
-
New compiles emit manifest version 4. Hosts
|
|
273
|
-
|
|
274
|
-
publishing newly compiled agents.
|
|
272
|
+
New compiles emit manifest version 4. Hosts read versions 2 through 4; older
|
|
273
|
+
channel entries default to no credentials and an empty surface. Upgrade hosting
|
|
274
|
+
before publishing newly compiled agents.
|
package/README.md
CHANGED
|
@@ -61,94 +61,10 @@ a networking extension. Use ordinary resume for execution. The bundled adapters
|
|
|
61
61
|
cache deferred setup failures per handle; reconnect to retry rather than replaying
|
|
62
62
|
partially completed user setup commands on each MCP poll.
|
|
63
63
|
|
|
64
|
-
##
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Factories also exist for Discord, GitHub, Linear, authenticated webhooks, voice,
|
|
74
|
-
and Twilio phone calls. Slack uses a separate OpenID identity flow; Linear uses
|
|
75
|
-
`actor=user` for identity proof and `actor=app` for application installation.
|
|
76
|
-
Provider principals are never matched by email. Slack Enterprise Grid principals
|
|
77
|
-
use the enterprise user/enterprise namespace when available.
|
|
78
|
-
|
|
79
|
-
Slack installs are scoped to a workspace, including workspaces in Enterprise Grid. Organization-wide installs with no workspace claim are rejected; install the app in each workspace that should connect. Discord guilds share the installed application bot token. Slack group direct messages are not subscribed; use a direct message or mention the app in a workspace channel.
|
|
80
|
-
|
|
81
|
-
Slack and Discord default to `history: 'none'`. Opt into `linked-members` to send
|
|
82
|
-
only currently linked members' thread history to the agent. Shared places default
|
|
83
|
-
to member-only input and origin-only replies. Configure `replies: all` explicitly
|
|
84
|
-
to mirror the chat. Provider signature verification is required on `/webhook`;
|
|
85
|
-
Discord also maintains a Gateway listener without the privileged message-content
|
|
86
|
-
intent. Requests must still fit the provider application's installed scopes.
|
|
87
|
-
|
|
88
|
-
`webhookChannel({secret})` verifies `X-Final-Signature: sha256=<HMAC hex>` over the
|
|
89
|
-
raw body. Its envelope is `{place,label,content,idempotencyKey,context?}`. Connect
|
|
90
|
-
its place before delivering events. Retries reuse the event's idempotency key.
|
|
91
|
-
|
|
92
|
-
`voiceChannel()` verifies a Final OAuth identity with PKCE, then serves `/stream`.
|
|
93
|
-
A JSON `start` frame selects PCM16 at 16 kHz and the authenticated principal;
|
|
94
|
-
binary frames carry microphone samples. `cancel` interrupts a turn and `stop`
|
|
95
|
-
finishes input while output drains. Only `speech.end` with `final: true` marks
|
|
96
|
-
the drained terminal response; per-turn ends use `final: false`. Server frames report transcripts, speech
|
|
97
|
-
boundaries, stage timestamps and errors, interleaved with binary PCM output.
|
|
98
|
-
An `error` with `recoverable: true` is advisory; other error frames are terminal.
|
|
99
|
-
A `notice` ends microphone capture while its spoken explanation drains. Stop
|
|
100
|
-
sending audio, display the notice and continue playback until the terminal
|
|
101
|
-
`speech.end`, including when the server ends the call without a client `stop`.
|
|
102
|
-
Only final utterances are posted. Once-only permission choices and empty-schema
|
|
103
|
-
confirmations can be answered by voice; other forms remain in Final. When recording is enabled, the channel attaches recordings without starting another turn.
|
|
104
|
-
Clients opt in to recording in the start frame after displaying the recording notice.
|
|
105
|
-
Saved recordings are PCM16 WAV attachments, split below 10 MiB including base64 serialization.
|
|
106
|
-
|
|
107
|
-
`phoneChannel({accountSid,fromNumber})` adapts the same core to signed Twilio call
|
|
108
|
-
webhooks and 8 kHz mu-law streams. It uses the declared `twilio-auth-token` secret
|
|
109
|
-
and an SMS challenge for identity. An unlinked member caller receives the account-link
|
|
110
|
-
URL by SMS, signs in and verifies their number, then calls again. Each linked call also requires a fresh SMS code
|
|
111
|
-
entered on the call; caller ID alone cannot act as a member. Configure `audience:'anyone'` explicitly for
|
|
112
|
-
anonymous support calls; those calls use captioned input and group defaults.
|
|
113
|
-
|
|
114
|
-
Provider delivery receipts expire after 90 days; later provider edits may no longer find their original delivery.
|
|
115
|
-
Provider mutation support varies: Slack, Discord and GitHub update bot-owned
|
|
116
|
-
replies. Linear activities are emitted as separate entries and are not edited by
|
|
117
|
-
this adapter. GitHub has no inbound reaction
|
|
118
|
-
webhook. Final room customization remains `finalChannel` from `/channels/final`.
|
|
119
|
-
|
|
120
|
-
Slack direct-message replies stay in a thread under the original request, so each
|
|
121
|
-
reply, edit and reaction remains associated with its source message. Approvals
|
|
122
|
-
show the requested action and the provider's one-time option names. Ambiguous
|
|
123
|
-
spoken answers and decisions that require a form must be answered in Final;
|
|
124
|
-
channels do not offer permanent approval choices.
|
|
125
|
-
|
|
126
|
-
Slack Grid workspace removals revoke that workspace's routes and identities. An
|
|
127
|
-
enterprise identity remains linked while another workspace has successfully
|
|
128
|
-
proven an active grant. Tracking is bounded to 256 workspaces per enterprise;
|
|
129
|
-
overflow conservatively requires enterprise identities to link again after the
|
|
130
|
-
next workspace removal. Phone verification requests use a per-number cooldown.
|
|
131
|
-
|
|
132
|
-
Configure Slack's `/final` slash command and interactivity callback in your app
|
|
133
|
-
settings. Register Discord's `/final` application command with `connect`,
|
|
134
|
-
`disconnect` and `unlink` subcommands. The SDK handles dispatched commands and
|
|
135
|
-
keeps your application's command registry under your control.
|
|
136
|
-
|
|
137
|
-
`replies: all` mirrors new messages. Slack, Discord, and GitHub can edit or remove
|
|
138
|
-
existing copies and synchronize supported reactions while their delivery receipts
|
|
139
|
-
are retained. Missing or deleted copies are not recreated by later mutations.
|
|
140
|
-
Apps can modify only messages they own. The Linear adapter does not propagate
|
|
141
|
-
Final edits, removals, or reactions to its activity entries. Provider permissions
|
|
142
|
-
and receipt retention limit later synchronization.
|
|
143
|
-
|
|
144
|
-
Hosts that terminate TLS upstream must give channel routes the public request URL,
|
|
145
|
-
including its scheme; Twilio verifies the complete URL. Voice closes a connection
|
|
146
|
-
if pending microphone audio exceeds 2 MiB instead of retaining an unbounded queue.
|
|
147
|
-
Discord guild removal ends that guild's connections while preserving identity
|
|
148
|
-
proofs usable in other guilds or direct messages.
|
|
149
|
-
|
|
150
|
-
Discord guild history is limited to content the bot can read without privileged
|
|
151
|
-
message-content access, such as mentions; unavailable content is omitted. GitHub
|
|
152
|
-
installation suspension pauses incoming delivery without deleting connections;
|
|
153
|
-
unsuspending resumes it. Voice synthesizes text replies through the configured
|
|
154
|
-
speech service; audio attachments remain available in Final.
|
|
64
|
+
## Authored channels
|
|
65
|
+
|
|
66
|
+
External integrations are ordinary authored modules built with `defineChannel`
|
|
67
|
+
from `@finalai/agent-provider`. The SDK provides scoped platform capabilities and
|
|
68
|
+
opaque credential operations; application implementations live with their source.
|
|
69
|
+
Final chat customization uses `finalChannel` from `/channels/final` and is compiled
|
|
70
|
+
into the authored bundle in the same way as other customization code.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import type { SessionUpdate } from '@agentclientprotocol/sdk/experimental/v2';
|
|
2
|
-
import { TurnPolicy, type
|
|
3
|
-
export
|
|
4
|
-
export { ChannelPrincipalType, type ChannelAuthIface } from '@finalai/agent-provider';
|
|
2
|
+
import { TurnPolicy, type RoomAuthIface, type RoomSurfaceDefinitionIface, type RoomInputResponseIface, type ChannelMessageCompletedDataIface, type ChannelMessageDeltaDataIface, type RoomReceiveContextIface, type ChannelTurnEventDataIface, type ChannelTurnStateIface, type ConversationMessageIface, type DecisionCreatedDataIface, type DecisionResolvedDataIface, type JsonValue } from '@finalai/agent-provider';
|
|
3
|
+
export { RoomPrincipalType, type RoomAuthIface } from '@finalai/agent-provider';
|
|
5
4
|
export interface ChannelAdmissionIface {
|
|
6
|
-
auth:
|
|
5
|
+
auth: RoomAuthIface | null;
|
|
7
6
|
context?: string[];
|
|
8
7
|
title?: string;
|
|
9
8
|
}
|
|
10
9
|
export interface FinalMessageContextIface extends RoomReceiveContextIface {
|
|
11
|
-
defaultAuth:
|
|
10
|
+
defaultAuth: RoomAuthIface | null;
|
|
12
11
|
}
|
|
13
12
|
export interface FinalChannelContextIface {
|
|
14
13
|
final: {
|
|
@@ -34,7 +33,7 @@ export interface FinalChannelOptionsIface {
|
|
|
34
33
|
updateIntervalMs?: number;
|
|
35
34
|
route?: RoomSurfaceDefinitionIface<JsonValue, JsonValue>['route'];
|
|
36
35
|
onMessage?: (ctx: FinalMessageContextIface, message: ConversationMessageIface) => ChannelAdmissionIface | null | Promise<ChannelAdmissionIface | null>;
|
|
37
|
-
onInputResponse?: (ctx: FinalMessageContextIface, response:
|
|
36
|
+
onInputResponse?: (ctx: FinalMessageContextIface, response: RoomInputResponseIface) => ChannelAdmissionIface | null | Promise<ChannelAdmissionIface | null>;
|
|
38
37
|
events?: FinalChannelEventHandlersIface;
|
|
39
38
|
renderer?: (update: SessionUpdate, channel: FinalChannelContextIface, ctx: FinalSessionContextIface, next: () => Promise<void>) => void | Promise<void>;
|
|
40
39
|
}
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
RoomPrincipalType,
|
|
3
3
|
SemanticChannelEventKind,
|
|
4
4
|
TurnPolicy,
|
|
5
|
-
|
|
5
|
+
roomAuthSchema,
|
|
6
6
|
channelMessageCompletedSchema,
|
|
7
7
|
decisionCreatedDataSchema,
|
|
8
8
|
decisionResolvedDataSchema,
|
|
9
9
|
defineRoomSurface,
|
|
10
|
-
jsonObjectSchema
|
|
11
|
-
packageRef
|
|
10
|
+
jsonObjectSchema
|
|
12
11
|
} from "@finalai/agent-provider";
|
|
13
12
|
import * as z from "zod";
|
|
14
13
|
import { contentBlocksOf } from "./channels/content.mjs";
|
|
15
14
|
import { conversationEventInboundSchema, messageInboundSchema } from "./channels/inbound.mjs";
|
|
16
15
|
import { createMessagingChannel } from "./channels/messaging-channel.mjs";
|
|
17
|
-
|
|
18
|
-
import { ChannelPrincipalType as ChannelPrincipalType2 } from "@finalai/agent-provider";
|
|
16
|
+
import { RoomPrincipalType as RoomPrincipalType2 } from "@finalai/agent-provider";
|
|
19
17
|
const admissionSchema = z.object({
|
|
20
|
-
auth:
|
|
18
|
+
auth: roomAuthSchema.nullable(),
|
|
21
19
|
context: z.array(z.string()).optional(),
|
|
22
20
|
title: z.string().optional()
|
|
23
21
|
});
|
|
@@ -29,7 +27,7 @@ const defaultAuthOf = (author) => {
|
|
|
29
27
|
if (author == null) return null;
|
|
30
28
|
return {
|
|
31
29
|
principalId: author.userId ?? author.definitionId ?? author.id,
|
|
32
|
-
principalType: author.userId == null ?
|
|
30
|
+
principalType: author.userId == null ? RoomPrincipalType.Service : RoomPrincipalType.User,
|
|
33
31
|
authenticator: "final",
|
|
34
32
|
displayName: author.displayName,
|
|
35
33
|
attributes: { participant_id: author.id }
|
|
@@ -163,7 +161,6 @@ const finalChannel = (inputOptions = {}) => {
|
|
|
163
161
|
defineRoomSurface({
|
|
164
162
|
...base,
|
|
165
163
|
config: jsonObjectSchema.parse(config),
|
|
166
|
-
installed: options.onMessage == null && options.onInputResponse == null && options.events == null && options.renderer == null && options.route == null ? packageRef(FINAL_CHANNEL_SPECIFIER) : void 0,
|
|
167
164
|
configure: (next) => finalChannel({ ...options, ...optionsSchema.parse({ ...config, ...next }) }),
|
|
168
165
|
receive,
|
|
169
166
|
respond: async (input, ctx) => {
|
|
@@ -209,8 +206,7 @@ const finalChannel = (inputOptions = {}) => {
|
|
|
209
206
|
);
|
|
210
207
|
};
|
|
211
208
|
export {
|
|
212
|
-
|
|
213
|
-
FINAL_CHANNEL_SPECIFIER,
|
|
209
|
+
RoomPrincipalType2 as RoomPrincipalType,
|
|
214
210
|
finalChannel
|
|
215
211
|
};
|
|
216
212
|
//# sourceMappingURL=channel-factories.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/channel-factories.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ContentBlock, SessionUpdate } from '@agentclientprotocol/sdk/experimental/v2';\nimport {\n ChannelPrincipalType,\n SemanticChannelEventKind,\n TurnPolicy,\n channelAuthSchema,\n channelMessageCompletedSchema,\n decisionCreatedDataSchema,\n decisionResolvedDataSchema,\n defineRoomSurface,\n jsonObjectSchema,\n packageRef,\n type ChannelAuthIface,\n type RoomSurfaceDefinitionIface,\n type RoomEventHandler,\n type ChannelInputResponseIface,\n type ChannelMessageCompletedDataIface,\n type ChannelMessageDeltaDataIface,\n type RoomReceiveContextIface,\n type ChannelTurnEventDataIface,\n type ChannelTurnStateIface,\n type ConversationMessageIface,\n type DecisionCreatedDataIface,\n type DecisionResolvedDataIface,\n type JsonValue,\n type ProjectionContextIface,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport { contentBlocksOf } from \"./channels/content.mjs\";\nimport { conversationEventInboundSchema, messageInboundSchema } from \"./channels/inbound.mjs\";\nimport { createMessagingChannel } from \"./channels/messaging-channel.mjs\";\n\nexport const FINAL_CHANNEL_SPECIFIER = '@finalai/final-agent-provider/channels/final';\n\nexport { ChannelPrincipalType, type ChannelAuthIface } from '@finalai/agent-provider';\n\nexport interface ChannelAdmissionIface {\n auth: ChannelAuthIface | null;\n context?: string[];\n title?: string;\n}\n\nexport interface FinalMessageContextIface extends RoomReceiveContextIface {\n defaultAuth: ChannelAuthIface | null;\n}\n\nexport interface FinalChannelContextIface {\n final: {\n post(message: ChannelMessageCompletedDataIface['message']): Promise<ConversationMessageIface>;\n };\n}\nexport interface FinalSessionContextIface {\n sessionId: string | null;\n turnId: string;\n state: ChannelTurnStateIface<JsonValue>;\n}\nexport type FinalChannelEventHandler<Data> = (\n data: Data,\n channel: FinalChannelContextIface,\n ctx: FinalSessionContextIface,\n) => void | Promise<void>;\nexport interface FinalChannelEventHandlersIface {\n 'message.delta'?: FinalChannelEventHandler<ChannelMessageDeltaDataIface>;\n 'message.completed'?: FinalChannelEventHandler<ChannelMessageCompletedDataIface>;\n 'turn.started'?: FinalChannelEventHandler<ChannelTurnEventDataIface>;\n 'turn.completed'?: FinalChannelEventHandler<ChannelTurnEventDataIface>;\n 'decision.created'?: FinalChannelEventHandler<DecisionCreatedDataIface>;\n 'decision.resolved'?: FinalChannelEventHandler<DecisionResolvedDataIface>;\n}\n\nexport interface FinalChannelOptionsIface {\n turnPolicy?: TurnPolicy | `${TurnPolicy}`;\n updateIntervalMs?: number;\n route?: RoomSurfaceDefinitionIface<JsonValue, JsonValue>['route'];\n onMessage?: (\n ctx: FinalMessageContextIface,\n message: ConversationMessageIface,\n ) => ChannelAdmissionIface | null | Promise<ChannelAdmissionIface | null>;\n onInputResponse?: (\n ctx: FinalMessageContextIface,\n response: ChannelInputResponseIface,\n ) => ChannelAdmissionIface | null | Promise<ChannelAdmissionIface | null>;\n events?: FinalChannelEventHandlersIface;\n renderer?: (\n update: SessionUpdate,\n channel: FinalChannelContextIface,\n ctx: FinalSessionContextIface,\n next: () => Promise<void>,\n ) => void | Promise<void>;\n}\n\nconst admissionSchema = z.object({\n auth: channelAuthSchema.nullable(),\n context: z.array(z.string()).optional(),\n title: z.string().optional(),\n});\n\nconst optionsSchema = z.object({\n turnPolicy: z.nativeEnum(TurnPolicy).default(TurnPolicy.Steer),\n updateIntervalMs: z.number().finite().nonnegative().optional(),\n});\n\n// A room member as a principal: a person by user id, an agent by definition id.\nconst defaultAuthOf = (\n author: import('@finalai/agent-provider').ParticipantIface | undefined,\n): ChannelAuthIface | null => {\n if (author == null) return null;\n return {\n principalId: author.userId ?? author.definitionId ?? author.id,\n principalType: author.userId == null ? ChannelPrincipalType.Service : ChannelPrincipalType.User,\n authenticator: 'final',\n displayName: author.displayName,\n attributes: { participant_id: author.id },\n };\n};\n\nexport const finalChannel = (\n inputOptions: FinalChannelOptionsIface = {},\n): RoomSurfaceDefinitionIface<JsonValue, JsonValue> => {\n const options = {\n ...inputOptions,\n events: inputOptions.events == null ? undefined : { ...inputOptions.events },\n };\n const callbacks = {\n onMessage: options.onMessage,\n onInputResponse: options.onInputResponse,\n renderer: options.renderer,\n route: options.route,\n ...options.events,\n };\n for (const [name, callback] of Object.entries(callbacks))\n if (callback != null && !z.function().safeParse(callback).success)\n throw new Error(`Final channel ${name} must be a function`);\n const supportedEvents = new Set([\n 'message.delta',\n 'message.completed',\n 'turn.started',\n 'turn.completed',\n 'decision.created',\n 'decision.resolved',\n ]);\n for (const kind of Object.keys(options.events ?? {}))\n if (!supportedEvents.has(kind)) throw new Error(`unsupported Final channel event: ${kind}`);\n const config = optionsSchema.parse(options);\n const base = createMessagingChannel({ ...config, route: options.route });\n const receive: typeof base.receive = async (input, ctx) => {\n if (conversationEventInboundSchema.safeParse(input).success) return base.receive(input, ctx);\n const parsed = messageInboundSchema.parse(input);\n const participants =\n ctx.participants ?? (await ctx.platform.conversations.participants(parsed.conversationId));\n const author = participants.find(\n (participant) => participant.id === parsed.authorParticipantId,\n );\n const message =\n ctx.message ??\n (await ctx.platform.conversations.postMessage({\n ...parsed,\n content: contentBlocksOf(parsed.content),\n }));\n if (ctx.definitionId == null) throw new Error('Final channel has no agent definition');\n const messageContext: FinalMessageContextIface = {\n ...ctx,\n defaultAuth: defaultAuthOf(author),\n };\n const accepted =\n options.onMessage == null\n ? { auth: messageContext.defaultAuth }\n : await options.onMessage(messageContext, message);\n const admission = accepted == null ? null : admissionSchema.parse(accepted);\n if (admission == null) return { conversationId: parsed.conversationId };\n const context = admission.context ?? [];\n const injected: ContentBlock[] = context.map((text) => ({ type: 'text', text }));\n return base.receive(\n {\n ...parsed,\n content: [...context.map((text) => ({ type: 'text', text })), ...parsed.content],\n },\n {\n ...ctx,\n platform: {\n ...ctx.platform,\n sessions: {\n ...ctx.platform.sessions,\n prompt: (args) =>\n ctx.platform.sessions.prompt({\n ...args,\n auth: admission.auth,\n title: admission.title,\n }),\n },\n },\n message: { ...message, content: [...injected, ...message.content] },\n },\n );\n };\n const channelContext = (ctx: ProjectionContextIface): FinalChannelContextIface => {\n const { conversation, agentParticipant } = ctx;\n if (conversation == null || agentParticipant == null || agentParticipant.definitionId == null)\n throw new Error('Final platform context requires a Final conversation');\n return {\n final: {\n post: async (message) => {\n if (ctx.platform.conversations.dispatchMessage == null)\n throw new Error('channel dispatch is unavailable');\n const posted = await ctx.platform.conversations.postMessage({\n conversationId: conversation.conversationId,\n authorParticipantId: agentParticipant.id,\n content: contentBlocksOf(message.content),\n clientMessageId: `channel:${ctx.turn.id}:${message.id}`,\n originTurnId: ctx.turn.id,\n });\n await ctx.platform.conversations.dispatchMessage({\n conversationId: posted.conversationId,\n messageId: posted.messageId,\n });\n return posted;\n },\n },\n };\n };\n const sessionContext = (ctx: ProjectionContextIface): FinalSessionContextIface => ({\n sessionId: ctx.sessionId ?? null,\n turnId: ctx.turn.id,\n state: ctx.state,\n });\n const wrap = <Data>(\n schema: z.ZodType<Data>,\n handler: FinalChannelEventHandler<Data> | undefined,\n ): RoomEventHandler | undefined =>\n handler == null\n ? undefined\n : async (event, ctx) =>\n handler(schema.parse(event.data), channelContext(ctx), sessionContext(ctx));\n const authoredEvents = {\n [SemanticChannelEventKind.MessageCompleted]: wrap(\n channelMessageCompletedSchema,\n options.events?.['message.completed'],\n ),\n [SemanticChannelEventKind.MessageDelta]: wrap(\n z.object({ messageId: z.string(), delta: jsonObjectSchema }),\n options.events?.['message.delta'],\n ),\n [SemanticChannelEventKind.TurnStarted]: wrap(\n z.object({ turnId: z.string() }),\n options.events?.['turn.started'],\n ),\n [SemanticChannelEventKind.TurnCompleted]: wrap(\n z.object({ turnId: z.string(), stopReason: z.string().nullable() }),\n options.events?.['turn.completed'],\n ),\n 'decision.created': wrap(decisionCreatedDataSchema, options.events?.['decision.created']),\n 'decision.resolved': wrap(decisionResolvedDataSchema, options.events?.['decision.resolved']),\n };\n const events = { ...base.events };\n for (const [kind, handler] of Object.entries(authoredEvents))\n if (handler != null) events[kind] = handler;\n return Object.freeze(\n defineRoomSurface({\n ...base,\n config: jsonObjectSchema.parse(config),\n installed:\n options.onMessage == null &&\n options.onInputResponse == null &&\n options.events == null &&\n options.renderer == null &&\n options.route == null\n ? packageRef(FINAL_CHANNEL_SPECIFIER)\n : undefined,\n configure: (next) =>\n finalChannel({ ...options, ...optionsSchema.parse({ ...config, ...next }) }),\n receive,\n respond: async (input, ctx) => {\n const conversation = z.string().min(1).parse(ctx.conversationId);\n const participants = await ctx.platform.conversations.participants(conversation);\n const author = participants.find(\n (participant) => participant.id === input.actorParticipantId,\n );\n const defaultAuth = defaultAuthOf(author);\n let admission: ChannelAdmissionIface = { auth: defaultAuth };\n if (options.onInputResponse != null) {\n const accepted = await options.onInputResponse({ ...ctx, defaultAuth }, input);\n if (accepted == null) return { resolved: false };\n admission = admissionSchema.parse(accepted);\n }\n if (ctx.platform.sessions.respond == null)\n throw new Error('response submission is unavailable');\n return ctx.platform.sessions.respond({ ...input, auth: admission.auth });\n },\n cancel: async (ctx) => {\n if (ctx.agentParticipantId == null) return { accepted: false };\n const conversation = z.string().min(1).parse(ctx.conversationId);\n const participants = await ctx.platform.conversations.participants(conversation);\n const agent = participants.find((participant) => participant.id === ctx.agentParticipantId);\n if (agent?.sessionId == null) return { accepted: false };\n await ctx.platform.sessions.cancel({\n participantId: agent.id,\n reason: ctx.cancelReason,\n throughMessageSeq: ctx.cancelThroughMessageSeq,\n });\n return { accepted: true };\n },\n project: async (update, ctx) => {\n const next = () =>\n options.events?.['message.completed'] == null\n ? base.project(update, ctx)\n : Promise.resolve();\n if (options.renderer != null) {\n await options.renderer(update, channelContext(ctx), sessionContext(ctx), next);\n return;\n }\n await next();\n },\n events: Object.freeze(events),\n }),\n );\n};\n"],
|
|
5
|
-
"mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,
|
|
6
|
-
"names": ["
|
|
4
|
+
"sourcesContent": ["import type { ContentBlock, SessionUpdate } from '@agentclientprotocol/sdk/experimental/v2';\nimport {\n RoomPrincipalType,\n SemanticChannelEventKind,\n TurnPolicy,\n roomAuthSchema,\n channelMessageCompletedSchema,\n decisionCreatedDataSchema,\n decisionResolvedDataSchema,\n defineRoomSurface,\n jsonObjectSchema,\n type RoomAuthIface,\n type RoomSurfaceDefinitionIface,\n type RoomEventHandler,\n type RoomInputResponseIface,\n type ChannelMessageCompletedDataIface,\n type ChannelMessageDeltaDataIface,\n type RoomReceiveContextIface,\n type ChannelTurnEventDataIface,\n type ChannelTurnStateIface,\n type ConversationMessageIface,\n type DecisionCreatedDataIface,\n type DecisionResolvedDataIface,\n type JsonValue,\n type ProjectionContextIface,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport { contentBlocksOf } from \"./channels/content.mjs\";\nimport { conversationEventInboundSchema, messageInboundSchema } from \"./channels/inbound.mjs\";\nimport { createMessagingChannel } from \"./channels/messaging-channel.mjs\";\n\nexport { RoomPrincipalType, type RoomAuthIface } from '@finalai/agent-provider';\n\nexport interface ChannelAdmissionIface {\n auth: RoomAuthIface | null;\n context?: string[];\n title?: string;\n}\n\nexport interface FinalMessageContextIface extends RoomReceiveContextIface {\n defaultAuth: RoomAuthIface | null;\n}\n\nexport interface FinalChannelContextIface {\n final: {\n post(message: ChannelMessageCompletedDataIface['message']): Promise<ConversationMessageIface>;\n };\n}\nexport interface FinalSessionContextIface {\n sessionId: string | null;\n turnId: string;\n state: ChannelTurnStateIface<JsonValue>;\n}\nexport type FinalChannelEventHandler<Data> = (\n data: Data,\n channel: FinalChannelContextIface,\n ctx: FinalSessionContextIface,\n) => void | Promise<void>;\nexport interface FinalChannelEventHandlersIface {\n 'message.delta'?: FinalChannelEventHandler<ChannelMessageDeltaDataIface>;\n 'message.completed'?: FinalChannelEventHandler<ChannelMessageCompletedDataIface>;\n 'turn.started'?: FinalChannelEventHandler<ChannelTurnEventDataIface>;\n 'turn.completed'?: FinalChannelEventHandler<ChannelTurnEventDataIface>;\n 'decision.created'?: FinalChannelEventHandler<DecisionCreatedDataIface>;\n 'decision.resolved'?: FinalChannelEventHandler<DecisionResolvedDataIface>;\n}\n\nexport interface FinalChannelOptionsIface {\n turnPolicy?: TurnPolicy | `${TurnPolicy}`;\n updateIntervalMs?: number;\n route?: RoomSurfaceDefinitionIface<JsonValue, JsonValue>['route'];\n onMessage?: (\n ctx: FinalMessageContextIface,\n message: ConversationMessageIface,\n ) => ChannelAdmissionIface | null | Promise<ChannelAdmissionIface | null>;\n onInputResponse?: (\n ctx: FinalMessageContextIface,\n response: RoomInputResponseIface,\n ) => ChannelAdmissionIface | null | Promise<ChannelAdmissionIface | null>;\n events?: FinalChannelEventHandlersIface;\n renderer?: (\n update: SessionUpdate,\n channel: FinalChannelContextIface,\n ctx: FinalSessionContextIface,\n next: () => Promise<void>,\n ) => void | Promise<void>;\n}\n\nconst admissionSchema = z.object({\n auth: roomAuthSchema.nullable(),\n context: z.array(z.string()).optional(),\n title: z.string().optional(),\n});\n\nconst optionsSchema = z.object({\n turnPolicy: z.nativeEnum(TurnPolicy).default(TurnPolicy.Steer),\n updateIntervalMs: z.number().finite().nonnegative().optional(),\n});\n\n// A room member as a principal: a person by user id, an agent by definition id.\nconst defaultAuthOf = (\n author: import('@finalai/agent-provider').ParticipantIface | undefined,\n): RoomAuthIface | null => {\n if (author == null) return null;\n return {\n principalId: author.userId ?? author.definitionId ?? author.id,\n principalType: author.userId == null ? RoomPrincipalType.Service : RoomPrincipalType.User,\n authenticator: 'final',\n displayName: author.displayName,\n attributes: { participant_id: author.id },\n };\n};\n\nexport const finalChannel = (\n inputOptions: FinalChannelOptionsIface = {},\n): RoomSurfaceDefinitionIface<JsonValue, JsonValue> => {\n const options = {\n ...inputOptions,\n events: inputOptions.events == null ? undefined : { ...inputOptions.events },\n };\n const callbacks = {\n onMessage: options.onMessage,\n onInputResponse: options.onInputResponse,\n renderer: options.renderer,\n route: options.route,\n ...options.events,\n };\n for (const [name, callback] of Object.entries(callbacks))\n if (callback != null && !z.function().safeParse(callback).success)\n throw new Error(`Final channel ${name} must be a function`);\n const supportedEvents = new Set([\n 'message.delta',\n 'message.completed',\n 'turn.started',\n 'turn.completed',\n 'decision.created',\n 'decision.resolved',\n ]);\n for (const kind of Object.keys(options.events ?? {}))\n if (!supportedEvents.has(kind)) throw new Error(`unsupported Final channel event: ${kind}`);\n const config = optionsSchema.parse(options);\n const base = createMessagingChannel({ ...config, route: options.route });\n const receive: typeof base.receive = async (input, ctx) => {\n if (conversationEventInboundSchema.safeParse(input).success) return base.receive(input, ctx);\n const parsed = messageInboundSchema.parse(input);\n const participants =\n ctx.participants ?? (await ctx.platform.conversations.participants(parsed.conversationId));\n const author = participants.find(\n (participant) => participant.id === parsed.authorParticipantId,\n );\n const message =\n ctx.message ??\n (await ctx.platform.conversations.postMessage({\n ...parsed,\n content: contentBlocksOf(parsed.content),\n }));\n if (ctx.definitionId == null) throw new Error('Final channel has no agent definition');\n const messageContext: FinalMessageContextIface = {\n ...ctx,\n defaultAuth: defaultAuthOf(author),\n };\n const accepted =\n options.onMessage == null\n ? { auth: messageContext.defaultAuth }\n : await options.onMessage(messageContext, message);\n const admission = accepted == null ? null : admissionSchema.parse(accepted);\n if (admission == null) return { conversationId: parsed.conversationId };\n const context = admission.context ?? [];\n const injected: ContentBlock[] = context.map((text) => ({ type: 'text', text }));\n return base.receive(\n {\n ...parsed,\n content: [...context.map((text) => ({ type: 'text', text })), ...parsed.content],\n },\n {\n ...ctx,\n platform: {\n ...ctx.platform,\n sessions: {\n ...ctx.platform.sessions,\n prompt: (args) =>\n ctx.platform.sessions.prompt({\n ...args,\n auth: admission.auth,\n title: admission.title,\n }),\n },\n },\n message: { ...message, content: [...injected, ...message.content] },\n },\n );\n };\n const channelContext = (ctx: ProjectionContextIface): FinalChannelContextIface => {\n const { conversation, agentParticipant } = ctx;\n if (conversation == null || agentParticipant == null || agentParticipant.definitionId == null)\n throw new Error('Final platform context requires a Final conversation');\n return {\n final: {\n post: async (message) => {\n if (ctx.platform.conversations.dispatchMessage == null)\n throw new Error('channel dispatch is unavailable');\n const posted = await ctx.platform.conversations.postMessage({\n conversationId: conversation.conversationId,\n authorParticipantId: agentParticipant.id,\n content: contentBlocksOf(message.content),\n clientMessageId: `channel:${ctx.turn.id}:${message.id}`,\n originTurnId: ctx.turn.id,\n });\n await ctx.platform.conversations.dispatchMessage({\n conversationId: posted.conversationId,\n messageId: posted.messageId,\n });\n return posted;\n },\n },\n };\n };\n const sessionContext = (ctx: ProjectionContextIface): FinalSessionContextIface => ({\n sessionId: ctx.sessionId ?? null,\n turnId: ctx.turn.id,\n state: ctx.state,\n });\n const wrap = <Data>(\n schema: z.ZodType<Data>,\n handler: FinalChannelEventHandler<Data> | undefined,\n ): RoomEventHandler | undefined =>\n handler == null\n ? undefined\n : async (event, ctx) =>\n handler(schema.parse(event.data), channelContext(ctx), sessionContext(ctx));\n const authoredEvents = {\n [SemanticChannelEventKind.MessageCompleted]: wrap(\n channelMessageCompletedSchema,\n options.events?.['message.completed'],\n ),\n [SemanticChannelEventKind.MessageDelta]: wrap(\n z.object({ messageId: z.string(), delta: jsonObjectSchema }),\n options.events?.['message.delta'],\n ),\n [SemanticChannelEventKind.TurnStarted]: wrap(\n z.object({ turnId: z.string() }),\n options.events?.['turn.started'],\n ),\n [SemanticChannelEventKind.TurnCompleted]: wrap(\n z.object({ turnId: z.string(), stopReason: z.string().nullable() }),\n options.events?.['turn.completed'],\n ),\n 'decision.created': wrap(decisionCreatedDataSchema, options.events?.['decision.created']),\n 'decision.resolved': wrap(decisionResolvedDataSchema, options.events?.['decision.resolved']),\n };\n const events = { ...base.events };\n for (const [kind, handler] of Object.entries(authoredEvents))\n if (handler != null) events[kind] = handler;\n return Object.freeze(\n defineRoomSurface({\n ...base,\n config: jsonObjectSchema.parse(config),\n configure: (next) =>\n finalChannel({ ...options, ...optionsSchema.parse({ ...config, ...next }) }),\n receive,\n respond: async (input, ctx) => {\n const conversation = z.string().min(1).parse(ctx.conversationId);\n const participants = await ctx.platform.conversations.participants(conversation);\n const author = participants.find(\n (participant) => participant.id === input.actorParticipantId,\n );\n const defaultAuth = defaultAuthOf(author);\n let admission: ChannelAdmissionIface = { auth: defaultAuth };\n if (options.onInputResponse != null) {\n const accepted = await options.onInputResponse({ ...ctx, defaultAuth }, input);\n if (accepted == null) return { resolved: false };\n admission = admissionSchema.parse(accepted);\n }\n if (ctx.platform.sessions.respond == null)\n throw new Error('response submission is unavailable');\n return ctx.platform.sessions.respond({ ...input, auth: admission.auth });\n },\n cancel: async (ctx) => {\n if (ctx.agentParticipantId == null) return { accepted: false };\n const conversation = z.string().min(1).parse(ctx.conversationId);\n const participants = await ctx.platform.conversations.participants(conversation);\n const agent = participants.find((participant) => participant.id === ctx.agentParticipantId);\n if (agent?.sessionId == null) return { accepted: false };\n await ctx.platform.sessions.cancel({\n participantId: agent.id,\n reason: ctx.cancelReason,\n throughMessageSeq: ctx.cancelThroughMessageSeq,\n });\n return { accepted: true };\n },\n project: async (update, ctx) => {\n const next = () =>\n options.events?.['message.completed'] == null\n ? base.project(update, ctx)\n : Promise.resolve();\n if (options.renderer != null) {\n await options.renderer(update, channelContext(ctx), sessionContext(ctx), next);\n return;\n }\n await next();\n },\n events: Object.freeze(events),\n }),\n );\n};\n"],
|
|
5
|
+
"mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAeK;AACP,YAAY,OAAO;AACnB,SAAS,uBAAuB;AAChC,SAAS,gCAAgC,4BAA4B;AACrE,SAAS,8BAA8B;AAEvC,SAAS,qBAAAA,0BAA6C;AAyDtD,MAAM,kBAAkB,EAAE,OAAO;AAAA,EAC/B,MAAM,eAAe,SAAS;AAAA,EAC9B,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACtC,OAAO,EAAE,OAAO,EAAE,SAAS;AAC7B,CAAC;AAED,MAAM,gBAAgB,EAAE,OAAO;AAAA,EAC7B,YAAY,EAAE,WAAW,UAAU,EAAE,QAAQ,WAAW,KAAK;AAAA,EAC7D,kBAAkB,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS;AAC/D,CAAC;AAGD,MAAM,gBAAgB,CACpB,WACyB;AACzB,MAAI,UAAU,KAAM,QAAO;AAC3B,SAAO;AAAA,IACL,aAAa,OAAO,UAAU,OAAO,gBAAgB,OAAO;AAAA,IAC5D,eAAe,OAAO,UAAU,OAAO,kBAAkB,UAAU,kBAAkB;AAAA,IACrF,eAAe;AAAA,IACf,aAAa,OAAO;AAAA,IACpB,YAAY,EAAE,gBAAgB,OAAO,GAAG;AAAA,EAC1C;AACF;AAEO,MAAM,eAAe,CAC1B,eAAyC,CAAC,MACW;AACrD,QAAM,UAAU;AAAA,IACd,GAAG;AAAA,IACH,QAAQ,aAAa,UAAU,OAAO,SAAY,EAAE,GAAG,aAAa,OAAO;AAAA,EAC7E;AACA,QAAM,YAAY;AAAA,IAChB,WAAW,QAAQ;AAAA,IACnB,iBAAiB,QAAQ;AAAA,IACzB,UAAU,QAAQ;AAAA,IAClB,OAAO,QAAQ;AAAA,IACf,GAAG,QAAQ;AAAA,EACb;AACA,aAAW,CAAC,MAAM,QAAQ,KAAK,OAAO,QAAQ,SAAS;AACrD,QAAI,YAAY,QAAQ,CAAC,EAAE,SAAS,EAAE,UAAU,QAAQ,EAAE;AACxD,YAAM,IAAI,MAAM,iBAAiB,IAAI,qBAAqB;AAC9D,QAAM,kBAAkB,oBAAI,IAAI;AAAA,IAC9B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,aAAW,QAAQ,OAAO,KAAK,QAAQ,UAAU,CAAC,CAAC;AACjD,QAAI,CAAC,gBAAgB,IAAI,IAAI,EAAG,OAAM,IAAI,MAAM,oCAAoC,IAAI,EAAE;AAC5F,QAAM,SAAS,cAAc,MAAM,OAAO;AAC1C,QAAM,OAAO,uBAAuB,EAAE,GAAG,QAAQ,OAAO,QAAQ,MAAM,CAAC;AACvE,QAAM,UAA+B,OAAO,OAAO,QAAQ;AACzD,QAAI,+BAA+B,UAAU,KAAK,EAAE,QAAS,QAAO,KAAK,QAAQ,OAAO,GAAG;AAC3F,UAAM,SAAS,qBAAqB,MAAM,KAAK;AAC/C,UAAM,eACJ,IAAI,gBAAiB,MAAM,IAAI,SAAS,cAAc,aAAa,OAAO,cAAc;AAC1F,UAAM,SAAS,aAAa;AAAA,MAC1B,CAAC,gBAAgB,YAAY,OAAO,OAAO;AAAA,IAC7C;AACA,UAAM,UACJ,IAAI,WACH,MAAM,IAAI,SAAS,cAAc,YAAY;AAAA,MAC5C,GAAG;AAAA,MACH,SAAS,gBAAgB,OAAO,OAAO;AAAA,IACzC,CAAC;AACH,QAAI,IAAI,gBAAgB,KAAM,OAAM,IAAI,MAAM,uCAAuC;AACrF,UAAM,iBAA2C;AAAA,MAC/C,GAAG;AAAA,MACH,aAAa,cAAc,MAAM;AAAA,IACnC;AACA,UAAM,WACJ,QAAQ,aAAa,OACjB,EAAE,MAAM,eAAe,YAAY,IACnC,MAAM,QAAQ,UAAU,gBAAgB,OAAO;AACrD,UAAM,YAAY,YAAY,OAAO,OAAO,gBAAgB,MAAM,QAAQ;AAC1E,QAAI,aAAa,KAAM,QAAO,EAAE,gBAAgB,OAAO,eAAe;AACtE,UAAM,UAAU,UAAU,WAAW,CAAC;AACtC,UAAM,WAA2B,QAAQ,IAAI,CAAC,UAAU,EAAE,MAAM,QAAQ,KAAK,EAAE;AAC/E,WAAO,KAAK;AAAA,MACV;AAAA,QACE,GAAG;AAAA,QACH,SAAS,CAAC,GAAG,QAAQ,IAAI,CAAC,UAAU,EAAE,MAAM,QAAQ,KAAK,EAAE,GAAG,GAAG,OAAO,OAAO;AAAA,MACjF;AAAA,MACA;AAAA,QACE,GAAG;AAAA,QACH,UAAU;AAAA,UACR,GAAG,IAAI;AAAA,UACP,UAAU;AAAA,YACR,GAAG,IAAI,SAAS;AAAA,YAChB,QAAQ,CAAC,SACP,IAAI,SAAS,SAAS,OAAO;AAAA,cAC3B,GAAG;AAAA,cACH,MAAM,UAAU;AAAA,cAChB,OAAO,UAAU;AAAA,YACnB,CAAC;AAAA,UACL;AAAA,QACF;AAAA,QACA,SAAS,EAAE,GAAG,SAAS,SAAS,CAAC,GAAG,UAAU,GAAG,QAAQ,OAAO,EAAE;AAAA,MACpE;AAAA,IACF;AAAA,EACF;AACA,QAAM,iBAAiB,CAAC,QAA0D;AAChF,UAAM,EAAE,cAAc,iBAAiB,IAAI;AAC3C,QAAI,gBAAgB,QAAQ,oBAAoB,QAAQ,iBAAiB,gBAAgB;AACvF,YAAM,IAAI,MAAM,sDAAsD;AACxE,WAAO;AAAA,MACL,OAAO;AAAA,QACL,MAAM,OAAO,YAAY;AACvB,cAAI,IAAI,SAAS,cAAc,mBAAmB;AAChD,kBAAM,IAAI,MAAM,iCAAiC;AACnD,gBAAM,SAAS,MAAM,IAAI,SAAS,cAAc,YAAY;AAAA,YAC1D,gBAAgB,aAAa;AAAA,YAC7B,qBAAqB,iBAAiB;AAAA,YACtC,SAAS,gBAAgB,QAAQ,OAAO;AAAA,YACxC,iBAAiB,WAAW,IAAI,KAAK,EAAE,IAAI,QAAQ,EAAE;AAAA,YACrD,cAAc,IAAI,KAAK;AAAA,UACzB,CAAC;AACD,gBAAM,IAAI,SAAS,cAAc,gBAAgB;AAAA,YAC/C,gBAAgB,OAAO;AAAA,YACvB,WAAW,OAAO;AAAA,UACpB,CAAC;AACD,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,QAAM,iBAAiB,CAAC,SAA2D;AAAA,IACjF,WAAW,IAAI,aAAa;AAAA,IAC5B,QAAQ,IAAI,KAAK;AAAA,IACjB,OAAO,IAAI;AAAA,EACb;AACA,QAAM,OAAO,CACX,QACA,YAEA,WAAW,OACP,SACA,OAAO,OAAO,QACZ,QAAQ,OAAO,MAAM,MAAM,IAAI,GAAG,eAAe,GAAG,GAAG,eAAe,GAAG,CAAC;AAClF,QAAM,iBAAiB;AAAA,IACrB,CAAC,yBAAyB,gBAAgB,GAAG;AAAA,MAC3C;AAAA,MACA,QAAQ,SAAS,mBAAmB;AAAA,IACtC;AAAA,IACA,CAAC,yBAAyB,YAAY,GAAG;AAAA,MACvC,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,GAAG,OAAO,iBAAiB,CAAC;AAAA,MAC3D,QAAQ,SAAS,eAAe;AAAA,IAClC;AAAA,IACA,CAAC,yBAAyB,WAAW,GAAG;AAAA,MACtC,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAAA,MAC/B,QAAQ,SAAS,cAAc;AAAA,IACjC;AAAA,IACA,CAAC,yBAAyB,aAAa,GAAG;AAAA,MACxC,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,GAAG,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAAA,MAClE,QAAQ,SAAS,gBAAgB;AAAA,IACnC;AAAA,IACA,oBAAoB,KAAK,2BAA2B,QAAQ,SAAS,kBAAkB,CAAC;AAAA,IACxF,qBAAqB,KAAK,4BAA4B,QAAQ,SAAS,mBAAmB,CAAC;AAAA,EAC7F;AACA,QAAM,SAAS,EAAE,GAAG,KAAK,OAAO;AAChC,aAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,cAAc;AACzD,QAAI,WAAW,KAAM,QAAO,IAAI,IAAI;AACtC,SAAO,OAAO;AAAA,IACZ,kBAAkB;AAAA,MAChB,GAAG;AAAA,MACH,QAAQ,iBAAiB,MAAM,MAAM;AAAA,MACrC,WAAW,CAAC,SACV,aAAa,EAAE,GAAG,SAAS,GAAG,cAAc,MAAM,EAAE,GAAG,QAAQ,GAAG,KAAK,CAAC,EAAE,CAAC;AAAA,MAC7E;AAAA,MACA,SAAS,OAAO,OAAO,QAAQ;AAC7B,cAAM,eAAe,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,IAAI,cAAc;AAC/D,cAAM,eAAe,MAAM,IAAI,SAAS,cAAc,aAAa,YAAY;AAC/E,cAAM,SAAS,aAAa;AAAA,UAC1B,CAAC,gBAAgB,YAAY,OAAO,MAAM;AAAA,QAC5C;AACA,cAAM,cAAc,cAAc,MAAM;AACxC,YAAI,YAAmC,EAAE,MAAM,YAAY;AAC3D,YAAI,QAAQ,mBAAmB,MAAM;AACnC,gBAAM,WAAW,MAAM,QAAQ,gBAAgB,EAAE,GAAG,KAAK,YAAY,GAAG,KAAK;AAC7E,cAAI,YAAY,KAAM,QAAO,EAAE,UAAU,MAAM;AAC/C,sBAAY,gBAAgB,MAAM,QAAQ;AAAA,QAC5C;AACA,YAAI,IAAI,SAAS,SAAS,WAAW;AACnC,gBAAM,IAAI,MAAM,oCAAoC;AACtD,eAAO,IAAI,SAAS,SAAS,QAAQ,EAAE,GAAG,OAAO,MAAM,UAAU,KAAK,CAAC;AAAA,MACzE;AAAA,MACA,QAAQ,OAAO,QAAQ;AACrB,YAAI,IAAI,sBAAsB,KAAM,QAAO,EAAE,UAAU,MAAM;AAC7D,cAAM,eAAe,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,IAAI,cAAc;AAC/D,cAAM,eAAe,MAAM,IAAI,SAAS,cAAc,aAAa,YAAY;AAC/E,cAAM,QAAQ,aAAa,KAAK,CAAC,gBAAgB,YAAY,OAAO,IAAI,kBAAkB;AAC1F,YAAI,OAAO,aAAa,KAAM,QAAO,EAAE,UAAU,MAAM;AACvD,cAAM,IAAI,SAAS,SAAS,OAAO;AAAA,UACjC,eAAe,MAAM;AAAA,UACrB,QAAQ,IAAI;AAAA,UACZ,mBAAmB,IAAI;AAAA,QACzB,CAAC;AACD,eAAO,EAAE,UAAU,KAAK;AAAA,MAC1B;AAAA,MACA,SAAS,OAAO,QAAQ,QAAQ;AAC9B,cAAM,OAAO,MACX,QAAQ,SAAS,mBAAmB,KAAK,OACrC,KAAK,QAAQ,QAAQ,GAAG,IACxB,QAAQ,QAAQ;AACtB,YAAI,QAAQ,YAAY,MAAM;AAC5B,gBAAM,QAAQ,SAAS,QAAQ,eAAe,GAAG,GAAG,eAAe,GAAG,GAAG,IAAI;AAC7E;AAAA,QACF;AACA,cAAM,KAAK;AAAA,MACb;AAAA,MACA,QAAQ,OAAO,OAAO,MAAM;AAAA,IAC9B,CAAC;AAAA,EACH;AACF;",
|
|
6
|
+
"names": ["RoomPrincipalType"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { roomMessageInputSchema } from "@finalai/agent-provider";
|
|
2
2
|
import * as z from "zod";
|
|
3
3
|
import { MembershipChange } from "./delivery.mjs";
|
|
4
|
-
const messageInboundSchema =
|
|
4
|
+
const messageInboundSchema = roomMessageInputSchema;
|
|
5
5
|
var ConversationEventKind = /* @__PURE__ */ ((ConversationEventKind2) => {
|
|
6
6
|
ConversationEventKind2["Reaction"] = "reaction";
|
|
7
7
|
ConversationEventKind2["Membership"] = "membership";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/channels/inbound.ts"],
|
|
4
|
-
"sourcesContent": ["import {
|
|
5
|
-
"mappings": "AAAA,SAAS,
|
|
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
6
|
"names": ["ConversationEventKind"]
|
|
7
7
|
}
|
|
@@ -7,10 +7,3 @@ export * from "./messaging-channel.mjs";
|
|
|
7
7
|
export * from "./projection.mjs";
|
|
8
8
|
export * from "./prompt.mjs";
|
|
9
9
|
export * from "./routing.mjs";
|
|
10
|
-
export * from "./slack.mjs";
|
|
11
|
-
export * from "./discord.mjs";
|
|
12
|
-
export * from "./github.mjs";
|
|
13
|
-
export * from "./linear.mjs";
|
|
14
|
-
export * from "./webhook.mjs";
|
|
15
|
-
export * from "./voice.mjs";
|
|
16
|
-
export * from "./phone.mjs";
|
|
@@ -7,11 +7,4 @@ export * from "./messaging-channel.mjs";
|
|
|
7
7
|
export * from "./projection.mjs";
|
|
8
8
|
export * from "./prompt.mjs";
|
|
9
9
|
export * from "./routing.mjs";
|
|
10
|
-
export * from "./slack.mjs";
|
|
11
|
-
export * from "./discord.mjs";
|
|
12
|
-
export * from "./github.mjs";
|
|
13
|
-
export * from "./linear.mjs";
|
|
14
|
-
export * from "./webhook.mjs";
|
|
15
|
-
export * from "./voice.mjs";
|
|
16
|
-
export * from "./phone.mjs";
|
|
17
10
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
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\";\
|
|
5
|
-
"mappings": "AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;
|
|
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
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
|
|
3
|
+
import { type RoomAuthIface, type ConversationMessageIface, type ParticipantIface } from '@finalai/agent-provider/room-surface';
|
|
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;
|
|
@@ -10,7 +10,7 @@ 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
12
|
export declare const principalAuthorOf: (args: {
|
|
13
|
-
auth:
|
|
13
|
+
auth: RoomAuthIface;
|
|
14
14
|
agent?: boolean;
|
|
15
15
|
}) => FinalAuthorIface;
|
|
16
16
|
export interface AuthorPrefixedContentArgsIface {
|
|
@@ -6,12 +6,12 @@ import {
|
|
|
6
6
|
withFinalMeta
|
|
7
7
|
} from "@finalai/acp-session";
|
|
8
8
|
import {
|
|
9
|
-
|
|
9
|
+
RoomPrincipalType,
|
|
10
10
|
MessageKind,
|
|
11
11
|
ParticipantKind,
|
|
12
12
|
principalDisplayName,
|
|
13
13
|
RoutingMode
|
|
14
|
-
} from "@finalai/agent-provider/
|
|
14
|
+
} from "@finalai/agent-provider/room-surface";
|
|
15
15
|
const EXCERPT_CHARS = 280;
|
|
16
16
|
const CATCH_UP_MAX_MESSAGES = 20;
|
|
17
17
|
const CATCH_UP_MAX_CHARS = 4e3;
|
|
@@ -35,7 +35,7 @@ const finalAuthorOf = (participant) => ({
|
|
|
35
35
|
displayName: participant.displayName
|
|
36
36
|
});
|
|
37
37
|
const principalAuthorOf = (args) => ({
|
|
38
|
-
kind: args.agent === true ? FinalAuthorKind.Agent : args.auth.principalType ===
|
|
38
|
+
kind: args.agent === true ? FinalAuthorKind.Agent : args.auth.principalType === RoomPrincipalType.User ? FinalAuthorKind.User : FinalAuthorKind.Service,
|
|
39
39
|
displayName: principalDisplayName(args.auth),
|
|
40
40
|
principal: {
|
|
41
41
|
principalId: args.auth.principalId,
|
|
@@ -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 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": "AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAEA,MAAM,gBAAgB;AACtB,MAAM,wBAAwB;AAC9B,MAAM,qBAAqB;AAE3B,MAAM,YAAY,CAAC,UAAgC,EAAE,MAAM,QAAQ,KAAK;AAExE,MAAM,cAAc,CAAC,YAC1B,OAAO,SAAS,EAAE,WAAW,KAAK,CAAC,EAAE,KAAK;AAGrC,MAAM,iBAAiB,CAAC,YAAoC;AACjE,QAAM,OAAO,YAAY,OAAO;AAChC,MAAI,KAAK,SAAS,GAAG;AACnB,WAAO;AAAA,EACT;AACA,QAAM,cAAc,QAAQ,OAAO,CAAC,UAAU,MAAM,SAAS,MAAM,EAAE;AACrE,SAAO,gBAAgB,IAAI,KAAK,IAAI,WAAW,cAAc,gBAAgB,IAAI,KAAK,GAAG;AAC3F;AAEO,MAAM,UAAU,CAAC,MAAc,WAAmB,kBAA0B;AACjF,QAAM,aAAa,KAAK,QAAQ,SAAS,GAAG,EAAE,KAAK;AACnD,SAAO,WAAW,UAAU,WAAW,aAAa,GAAG,WAAW,MAAM,GAAG,WAAW,CAAC,CAAC;AAC1F;AAEO,MAAM,gBAAgB,CAAC,iBAAqD;AAAA,EACjF,eAAe,YAAY;AAAA,EAC3B,MAAM,YAAY,SAAS,gBAAgB,QAAQ,gBAAgB,QAAQ,gBAAgB;AAAA,EAC3F,aAAa,YAAY;AAC3B;AAKO,MAAM,oBAAoB,CAAC,UAGT;AAAA,EACvB,MACE,KAAK,UAAU,OACX,gBAAgB,QAChB,KAAK,KAAK,kBAAkB,
|
|
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 RoomPrincipalType,\n MessageKind,\n ParticipantKind,\n principalDisplayName,\n RoutingMode,\n type RoomAuthIface,\n type ConversationMessageIface,\n type ParticipantIface,\n} from '@finalai/agent-provider/room-surface';\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: RoomAuthIface;\n agent?: boolean;\n}): FinalAuthorIface => ({\n kind:\n args.agent === true\n ? FinalAuthorKind.Agent\n : args.auth.principalType === RoomPrincipalType.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
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ParticipantIface, type RouteArgsIface, type RouteDecisionIface } from '@finalai/agent-provider/
|
|
1
|
+
import { type ParticipantIface, type RouteArgsIface, type RouteDecisionIface } from '@finalai/agent-provider/room-surface';
|
|
2
2
|
export declare const MAX_AGENT_HOP_DEPTH = 2;
|
|
3
3
|
export declare const effectiveDefaultAgent: (participants: ParticipantIface[]) => ParticipantIface | null;
|
|
4
4
|
export declare const routeMessage: (args: RouteArgsIface) => RouteDecisionIface[];
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
Delivery,
|
|
4
4
|
ParticipantKind,
|
|
5
5
|
RoutingMode
|
|
6
|
-
} from "@finalai/agent-provider/
|
|
6
|
+
} from "@finalai/agent-provider/room-surface";
|
|
7
7
|
const MAX_AGENT_HOP_DEPTH = 2;
|
|
8
8
|
const activeAgents = (participants) => participants.filter(
|
|
9
9
|
(participant) => participant.kind === ParticipantKind.Agent && !participant.left
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/channels/routing.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n ConversationKind,\n Delivery,\n ParticipantKind,\n RoutingMode,\n type ParticipantIface,\n type RouteArgsIface,\n type RouteDecisionIface,\n} from '@finalai/agent-provider/
|
|
4
|
+
"sourcesContent": ["import {\n ConversationKind,\n Delivery,\n ParticipantKind,\n RoutingMode,\n type ParticipantIface,\n type RouteArgsIface,\n type RouteDecisionIface,\n} from '@finalai/agent-provider/room-surface';\n\n// Agent-authored messages may prompt other agents only while the chain of\n// agent hops that produced them is shorter than this: a human\n// message (0) prompts A, A's reply (1) may prompt B, B's reply (2) prompts no one.\nexport const MAX_AGENT_HOP_DEPTH = 2;\n\nconst activeAgents = (participants: ParticipantIface[]): ParticipantIface[] =>\n participants.filter(\n (participant) => participant.kind === ParticipantKind.Agent && !participant.left,\n );\n\n// The agent a no-mention message goes to: the Default agent, else (no Default)\n// the earliest-joined All agent, which then acts as the Default. Participants\n// arrive in join order.\nexport const effectiveDefaultAgent = (\n participants: ParticipantIface[],\n): ParticipantIface | null => {\n const agents = activeAgents(participants);\n return (\n agents.find((agent) => agent.routingMode === RoutingMode.Default) ??\n agents.find((agent) => agent.routingMode === RoutingMode.All) ??\n null\n );\n};\n\n// Pure: who gets a message and how. Users never receive decisions.\nexport const routeMessage = (args: RouteArgsIface): RouteDecisionIface[] => {\n const { conversation, message, participants } = args;\n const agents = activeAgents(participants).filter(\n (agent) =>\n agent.id !== message.authorParticipantId &&\n (message.agentAuthorDefinitionId == null ||\n agent.definitionId !== message.agentAuthorDefinitionId),\n );\n if (agents.length === 0) {\n return [];\n }\n const mentioned = new Set(message.mentionedParticipantIds);\n const mayPrompt = message.hopDepth < MAX_AGENT_HOP_DEPTH;\n const promptDelivery = mayPrompt ? Delivery.Prompt : Delivery.Observe;\n\n if (conversation.kind === ConversationKind.Direct) {\n return agents.map((agent) => ({ participantId: agent.id, delivery: promptDelivery }));\n }\n\n const anyAgentMentioned = agents.some((agent) => mentioned.has(agent.id));\n const defaultAgent = effectiveDefaultAgent(participants);\n return agents.flatMap((agent): RouteDecisionIface[] => {\n if (mentioned.has(agent.id)) {\n return [{ participantId: agent.id, delivery: promptDelivery }];\n }\n if (agent.routingMode === RoutingMode.Mention || agent.routingMode == null) {\n return [];\n }\n if (!anyAgentMentioned && agent.id === defaultAgent?.id) {\n return [{ participantId: agent.id, delivery: promptDelivery }];\n }\n return [{ participantId: agent.id, delivery: Delivery.Observe }];\n });\n};\n\nexport const routeToParticipant =\n (participantId: string) =>\n (args: RouteArgsIface): RouteDecisionIface[] =>\n args.participants.some(\n (participant) =>\n participant.id === participantId &&\n participant.kind === ParticipantKind.Agent &&\n !participant.left,\n )\n ? [{ participantId, delivery: Delivery.Prompt }]\n : [];\n"],
|
|
5
5
|
"mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAKA,MAAM,sBAAsB;AAEnC,MAAM,eAAe,CAAC,iBACpB,aAAa;AAAA,EACX,CAAC,gBAAgB,YAAY,SAAS,gBAAgB,SAAS,CAAC,YAAY;AAC9E;AAKK,MAAM,wBAAwB,CACnC,iBAC4B;AAC5B,QAAM,SAAS,aAAa,YAAY;AACxC,SACE,OAAO,KAAK,CAAC,UAAU,MAAM,gBAAgB,YAAY,OAAO,KAChE,OAAO,KAAK,CAAC,UAAU,MAAM,gBAAgB,YAAY,GAAG,KAC5D;AAEJ;AAGO,MAAM,eAAe,CAAC,SAA+C;AAC1E,QAAM,EAAE,cAAc,SAAS,aAAa,IAAI;AAChD,QAAM,SAAS,aAAa,YAAY,EAAE;AAAA,IACxC,CAAC,UACC,MAAM,OAAO,QAAQ,wBACpB,QAAQ,2BAA2B,QAClC,MAAM,iBAAiB,QAAQ;AAAA,EACrC;AACA,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO,CAAC;AAAA,EACV;AACA,QAAM,YAAY,IAAI,IAAI,QAAQ,uBAAuB;AACzD,QAAM,YAAY,QAAQ,WAAW;AACrC,QAAM,iBAAiB,YAAY,SAAS,SAAS,SAAS;AAE9D,MAAI,aAAa,SAAS,iBAAiB,QAAQ;AACjD,WAAO,OAAO,IAAI,CAAC,WAAW,EAAE,eAAe,MAAM,IAAI,UAAU,eAAe,EAAE;AAAA,EACtF;AAEA,QAAM,oBAAoB,OAAO,KAAK,CAAC,UAAU,UAAU,IAAI,MAAM,EAAE,CAAC;AACxE,QAAM,eAAe,sBAAsB,YAAY;AACvD,SAAO,OAAO,QAAQ,CAAC,UAAgC;AACrD,QAAI,UAAU,IAAI,MAAM,EAAE,GAAG;AAC3B,aAAO,CAAC,EAAE,eAAe,MAAM,IAAI,UAAU,eAAe,CAAC;AAAA,IAC/D;AACA,QAAI,MAAM,gBAAgB,YAAY,WAAW,MAAM,eAAe,MAAM;AAC1E,aAAO,CAAC;AAAA,IACV;AACA,QAAI,CAAC,qBAAqB,MAAM,OAAO,cAAc,IAAI;AACvD,aAAO,CAAC,EAAE,eAAe,MAAM,IAAI,UAAU,eAAe,CAAC;AAAA,IAC/D;AACA,WAAO,CAAC,EAAE,eAAe,MAAM,IAAI,UAAU,SAAS,QAAQ,CAAC;AAAA,EACjE,CAAC;AACH;AAEO,MAAM,qBACX,CAAC,kBACD,CAAC,SACC,KAAK,aAAa;AAAA,EAChB,CAAC,gBACC,YAAY,OAAO,iBACnB,YAAY,SAAS,gBAAgB,SACrC,CAAC,YAAY;AACjB,IACI,CAAC,EAAE,eAAe,UAAU,SAAS,OAAO,CAAC,IAC7C,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finalai/final-agent-provider",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Final provider helpers for harnesses, computer and device sandboxes, the Final room surface, and external provider channels.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -201,41 +201,6 @@
|
|
|
201
201
|
"types": "./dist/modules/sandboxes/egress-bootstrap.d.mts",
|
|
202
202
|
"import": "./dist/modules/sandboxes/egress-bootstrap.mjs",
|
|
203
203
|
"default": "./dist/modules/sandboxes/egress-bootstrap.mjs"
|
|
204
|
-
},
|
|
205
|
-
"./channels/slack": {
|
|
206
|
-
"types": "./dist/modules/channels/slack.d.mts",
|
|
207
|
-
"import": "./dist/modules/channels/slack.mjs",
|
|
208
|
-
"default": "./dist/modules/channels/slack.mjs"
|
|
209
|
-
},
|
|
210
|
-
"./channels/discord": {
|
|
211
|
-
"types": "./dist/modules/channels/discord.d.mts",
|
|
212
|
-
"import": "./dist/modules/channels/discord.mjs",
|
|
213
|
-
"default": "./dist/modules/channels/discord.mjs"
|
|
214
|
-
},
|
|
215
|
-
"./channels/github": {
|
|
216
|
-
"types": "./dist/modules/channels/github.d.mts",
|
|
217
|
-
"import": "./dist/modules/channels/github.mjs",
|
|
218
|
-
"default": "./dist/modules/channels/github.mjs"
|
|
219
|
-
},
|
|
220
|
-
"./channels/linear": {
|
|
221
|
-
"types": "./dist/modules/channels/linear.d.mts",
|
|
222
|
-
"import": "./dist/modules/channels/linear.mjs",
|
|
223
|
-
"default": "./dist/modules/channels/linear.mjs"
|
|
224
|
-
},
|
|
225
|
-
"./channels/webhook": {
|
|
226
|
-
"types": "./dist/modules/channels/webhook.d.mts",
|
|
227
|
-
"import": "./dist/modules/channels/webhook.mjs",
|
|
228
|
-
"default": "./dist/modules/channels/webhook.mjs"
|
|
229
|
-
},
|
|
230
|
-
"./channels/voice": {
|
|
231
|
-
"types": "./dist/modules/channels/voice.d.mts",
|
|
232
|
-
"import": "./dist/modules/channels/voice.mjs",
|
|
233
|
-
"default": "./dist/modules/channels/voice.mjs"
|
|
234
|
-
},
|
|
235
|
-
"./channels/phone": {
|
|
236
|
-
"types": "./dist/modules/channels/phone.d.mts",
|
|
237
|
-
"import": "./dist/modules/channels/phone.mjs",
|
|
238
|
-
"default": "./dist/modules/channels/phone.mjs"
|
|
239
204
|
}
|
|
240
205
|
},
|
|
241
206
|
"publishConfig": {
|
|
@@ -245,14 +210,13 @@
|
|
|
245
210
|
"dependencies": {
|
|
246
211
|
"@agentclientprotocol/sdk": "1.8.0",
|
|
247
212
|
"@finalai/acp-session": "0.1.11",
|
|
248
|
-
"@finalai/agent-protocol": "0.
|
|
249
|
-
"@finalai/agent-provider": "0.
|
|
213
|
+
"@finalai/agent-protocol": "0.8.0",
|
|
214
|
+
"@finalai/agent-provider": "0.9.0",
|
|
250
215
|
"@finalai/computers": "0.3.1",
|
|
251
216
|
"@finalai/websockets": "0.1.7",
|
|
252
217
|
"ws": "^8.21.3",
|
|
253
218
|
"zod": "4.6.6",
|
|
254
|
-
"@finalai/agent-definition": "0.
|
|
255
|
-
"gemoji": "^8.1.0"
|
|
219
|
+
"@finalai/agent-definition": "0.8.0"
|
|
256
220
|
},
|
|
257
221
|
"engines": {
|
|
258
222
|
"node": ">=26"
|