@finalai/final-agent-provider 0.6.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HOSTING.md +4 -4
- package/README.md +94 -2
- package/dist/modules/authoring-final-channel.mjs +2 -2
- package/dist/modules/authoring-final-channel.mjs.map +2 -2
- package/dist/modules/channel-factories.d.mts +5 -7
- package/dist/modules/channel-factories.mjs +4 -50
- package/dist/modules/channel-factories.mjs.map +2 -2
- package/dist/modules/channels/channel-utils.d.mts +132 -0
- package/dist/modules/channels/channel-utils.mjs +445 -0
- package/dist/modules/channels/channel-utils.mjs.map +7 -0
- package/dist/modules/channels/delivery.d.mts +5 -5
- package/dist/modules/channels/delivery.mjs.map +1 -1
- package/dist/modules/channels/direct-messaging.d.mts +2 -2
- package/dist/modules/channels/direct-messaging.mjs.map +2 -2
- package/dist/modules/channels/discord-dispatch.d.mts +17 -0
- package/dist/modules/channels/discord-dispatch.mjs +139 -0
- package/dist/modules/channels/discord-dispatch.mjs.map +7 -0
- package/dist/modules/channels/discord-gateway.d.mts +19 -0
- package/dist/modules/channels/discord-gateway.mjs +233 -0
- package/dist/modules/channels/discord-gateway.mjs.map +7 -0
- package/dist/modules/channels/discord.d.mts +9 -0
- package/dist/modules/channels/discord.mjs +904 -0
- package/dist/modules/channels/discord.mjs.map +7 -0
- package/dist/modules/channels/fake-platform.d.mts +2 -7
- package/dist/modules/channels/fake-platform.mjs +2 -38
- package/dist/modules/channels/fake-platform.mjs.map +2 -2
- package/dist/modules/channels/github.d.mts +5 -0
- package/dist/modules/channels/github.mjs +720 -0
- package/dist/modules/channels/github.mjs.map +7 -0
- package/dist/modules/channels/group-messaging.d.mts +2 -2
- package/dist/modules/channels/group-messaging.mjs.map +2 -2
- package/dist/modules/channels/index.d.mts +7 -3
- package/dist/modules/channels/index.mjs +7 -3
- package/dist/modules/channels/index.mjs.map +2 -2
- package/dist/modules/channels/linear.d.mts +4 -0
- package/dist/modules/channels/linear.mjs +621 -0
- package/dist/modules/channels/linear.mjs.map +7 -0
- package/dist/modules/channels/messaging-channel.d.mts +3 -3
- package/dist/modules/channels/messaging-channel.mjs +2 -2
- package/dist/modules/channels/messaging-channel.mjs.map +2 -2
- package/dist/modules/channels/phone.d.mts +8 -0
- package/dist/modules/channels/phone.mjs +382 -0
- package/dist/modules/channels/phone.mjs.map +7 -0
- package/dist/modules/channels/projection.mjs.map +1 -1
- package/dist/modules/channels/reply-receipts.d.mts +31 -0
- package/dist/modules/channels/reply-receipts.mjs +63 -0
- package/dist/modules/channels/reply-receipts.mjs.map +7 -0
- package/dist/modules/channels/routing.d.mts +1 -0
- package/dist/modules/channels/routing.mjs +5 -1
- package/dist/modules/channels/routing.mjs.map +2 -2
- package/dist/modules/channels/slack.d.mts +7 -0
- package/dist/modules/channels/slack.mjs +1134 -0
- package/dist/modules/channels/slack.mjs.map +7 -0
- package/dist/modules/channels/voice-recording.d.mts +7 -0
- package/dist/modules/channels/voice-recording.mjs +49 -0
- package/dist/modules/channels/voice-recording.mjs.map +7 -0
- package/dist/modules/channels/voice.d.mts +33 -0
- package/dist/modules/channels/voice.mjs +851 -0
- package/dist/modules/channels/voice.mjs.map +7 -0
- package/dist/modules/channels/webhook.d.mts +5 -0
- package/dist/modules/channels/webhook.mjs +76 -0
- package/dist/modules/channels/webhook.mjs.map +7 -0
- package/dist/modules/harnesses/acp-describe.mjs +1 -1
- package/dist/modules/harnesses/acp-describe.mjs.map +2 -2
- package/dist/modules/harnesses/fidelity.mjs +17 -17
- package/dist/modules/harnesses/fidelity.mjs.map +2 -2
- package/dist/modules/harnesses/remote-url.mjs.map +1 -1
- package/dist/modules/runtime/remote-host.mjs.map +1 -1
- package/dist/modules/sandboxes/build-scripts.mjs.map +1 -1
- package/dist/modules/sandboxes/paths.mjs.map +1 -1
- package/dist/modules/testing/harness.mjs +2 -2
- package/dist/modules/testing/harness.mjs.map +1 -1
- package/package.json +45 -19
- package/dist/modules/channels/address-context.d.mts +0 -7
- package/dist/modules/channels/address-context.mjs +0 -80
- package/dist/modules/channels/address-context.mjs.map +0 -7
- package/dist/modules/channels/agent-events.d.mts +0 -103
- package/dist/modules/channels/agent-events.mjs +0 -341
- package/dist/modules/channels/agent-events.mjs.map +0 -7
- package/dist/modules/channels/agent-schedules.d.mts +0 -25
- package/dist/modules/channels/agent-schedules.mjs +0 -96
- package/dist/modules/channels/agent-schedules.mjs.map +0 -7
- package/dist/modules/channels/service-input.d.mts +0 -22
- package/dist/modules/channels/service-input.mjs +0 -32
- package/dist/modules/channels/service-input.mjs.map +0 -7
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
defineChannel,
|
|
3
|
-
Delivery,
|
|
4
|
-
jsonObjectSchema,
|
|
5
|
-
jsonValueSchema,
|
|
6
|
-
ParticipantKind,
|
|
7
|
-
SandboxEventKind,
|
|
8
|
-
TurnPolicy
|
|
9
|
-
} from "@finalai/agent-provider";
|
|
10
|
-
import * as z from "zod";
|
|
11
|
-
import { contentBlocksOf } from "./content.mjs";
|
|
12
|
-
import { textBlock } from "./prompt.mjs";
|
|
13
|
-
import { routeMessage } from "./routing.mjs";
|
|
14
|
-
import { sendServiceInput } from "./service-input.mjs";
|
|
15
|
-
const AGENT_EVENTS_SPECIFIER = "@finalai/final-agent-provider/channels/agent-events";
|
|
16
|
-
const AGENT_EVENTS_TURN_POLICY = TurnPolicy.Steer;
|
|
17
|
-
const WEBHOOK_SECRET_NAME = "webhook";
|
|
18
|
-
const WEBHOOK_BODY_MAX_CHARS = 8e3;
|
|
19
|
-
var AgentEventKind = /* @__PURE__ */ ((AgentEventKind2) => {
|
|
20
|
-
AgentEventKind2["Webhook"] = "webhook";
|
|
21
|
-
AgentEventKind2["LinkedConversation"] = "linked-conversation";
|
|
22
|
-
AgentEventKind2["Sandbox"] = "sandbox";
|
|
23
|
-
return AgentEventKind2;
|
|
24
|
-
})(AgentEventKind || {});
|
|
25
|
-
import { SandboxEventKind as SandboxEventKind2 } from "@finalai/agent-provider";
|
|
26
|
-
const SYSTEM_EVENT_TEXT_MAX_CHARS = 4e3;
|
|
27
|
-
var LinkedConversationEvent = /* @__PURE__ */ ((LinkedConversationEvent2) => {
|
|
28
|
-
LinkedConversationEvent2["Replied"] = "replied";
|
|
29
|
-
LinkedConversationEvent2["WaitingForPerson"] = "waiting_for_person";
|
|
30
|
-
LinkedConversationEvent2["Failed"] = "failed";
|
|
31
|
-
return LinkedConversationEvent2;
|
|
32
|
-
})(LinkedConversationEvent || {});
|
|
33
|
-
const LINKED_CONVERSATION_TEXT_MAX_CHARS = 4e3;
|
|
34
|
-
const webhookEventSchema = z.object({
|
|
35
|
-
kind: z.literal("webhook" /* Webhook */),
|
|
36
|
-
body: z.string(),
|
|
37
|
-
contentType: z.string().nullish(),
|
|
38
|
-
// A provider event name when the sender gives one (`x-github-event`).
|
|
39
|
-
event: z.string().nullish(),
|
|
40
|
-
deliveryId: z.string().nullish()
|
|
41
|
-
});
|
|
42
|
-
const linkedConversationEventSchema = z.object({
|
|
43
|
-
kind: z.literal("linked-conversation" /* LinkedConversation */),
|
|
44
|
-
// The conversation that linked the other: where its agent is prompted.
|
|
45
|
-
conversationId: z.string().min(1),
|
|
46
|
-
// The agent that handed the work off; the Default agent when absent.
|
|
47
|
-
participantId: z.string().min(1).nullish(),
|
|
48
|
-
linkId: z.string().min(1),
|
|
49
|
-
linkedConversationId: z.string().min(1),
|
|
50
|
-
linkedTitle: z.string().nullish(),
|
|
51
|
-
event: z.nativeEnum(LinkedConversationEvent),
|
|
52
|
-
// The reply (Replied) or what the agent asks for (WaitingForPerson).
|
|
53
|
-
text: z.string().nullish(),
|
|
54
|
-
// Makes a redelivered event post once (the turn or decision).
|
|
55
|
-
eventId: z.string().min(1),
|
|
56
|
-
originTurnId: z.string().nullish()
|
|
57
|
-
});
|
|
58
|
-
const sandboxEventSchema = z.object({
|
|
59
|
-
kind: z.literal("sandbox" /* Sandbox */),
|
|
60
|
-
conversationId: z.string().min(1),
|
|
61
|
-
// The agent participant whose session started the process: the only one told.
|
|
62
|
-
participantId: z.string().min(1).nullish(),
|
|
63
|
-
bindingId: z.string().min(1),
|
|
64
|
-
sandboxName: z.string().nullish(),
|
|
65
|
-
event: z.nativeEnum(SandboxEventKind),
|
|
66
|
-
processId: z.string().min(1),
|
|
67
|
-
command: z.string(),
|
|
68
|
-
// Null when the sandbox could not report one (the process was lost).
|
|
69
|
-
exitCode: z.number().int().nullable(),
|
|
70
|
-
// The end of the process's output.
|
|
71
|
-
stdout: z.string().nullish(),
|
|
72
|
-
stderr: z.string().nullish(),
|
|
73
|
-
// Makes a redelivered event post once (per binding).
|
|
74
|
-
eventId: z.string().min(1)
|
|
75
|
-
});
|
|
76
|
-
const agentEventInboundSchema = z.discriminatedUnion("kind", [
|
|
77
|
-
webhookEventSchema,
|
|
78
|
-
linkedConversationEventSchema,
|
|
79
|
-
sandboxEventSchema
|
|
80
|
-
]);
|
|
81
|
-
var ReceiveTargetKind = /* @__PURE__ */ ((ReceiveTargetKind2) => {
|
|
82
|
-
ReceiveTargetKind2["BoundConversation"] = "bound-conversation";
|
|
83
|
-
ReceiveTargetKind2["OwnerDirect"] = "owner-direct";
|
|
84
|
-
return ReceiveTargetKind2;
|
|
85
|
-
})(ReceiveTargetKind || {});
|
|
86
|
-
const targetedInboundSchema = z.object({
|
|
87
|
-
target: z.object({ kind: z.nativeEnum(ReceiveTargetKind) }),
|
|
88
|
-
content: z.array(jsonObjectSchema).min(1),
|
|
89
|
-
// Makes a redelivered input count once.
|
|
90
|
-
deliveryId: z.string().min(1).nullish()
|
|
91
|
-
});
|
|
92
|
-
const configSchema = z.looseObject({
|
|
93
|
-
displayName: z.string().min(1).nullish(),
|
|
94
|
-
// Owner direct conversation when the binding is attached to none; null for
|
|
95
|
-
// organization-owned agents, whose webhooks need a bound conversation.
|
|
96
|
-
ownerUserId: z.string().min(1).nullish()
|
|
97
|
-
});
|
|
98
|
-
const renderBody = (body, contentType) => {
|
|
99
|
-
let rendered = body;
|
|
100
|
-
if (contentType?.includes("json") === true) {
|
|
101
|
-
try {
|
|
102
|
-
rendered = JSON.stringify(jsonValueSchema.parse(JSON.parse(body)), null, 2);
|
|
103
|
-
} catch {
|
|
104
|
-
rendered = body;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return rendered.length <= WEBHOOK_BODY_MAX_CHARS ? rendered : `${rendered.slice(0, WEBHOOK_BODY_MAX_CHARS)}
|
|
108
|
-
\u2026 (truncated)`;
|
|
109
|
-
};
|
|
110
|
-
const boundConversation = async (ctx) => ctx.bindingId == null ? null : ctx.platform.conversations.boundTo(ctx.bindingId);
|
|
111
|
-
const ownerDirectConversation = async (ctx) => {
|
|
112
|
-
const config = configSchema.parse(ctx.config);
|
|
113
|
-
if (config.ownerUserId == null || ctx.definitionId == null) {
|
|
114
|
-
throw new Error(
|
|
115
|
-
`channel binding ${ctx.bindingId ?? "(none)"} is attached to no conversation and its agent has no owner to post to`
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
return ctx.platform.conversations.findOrCreateDirect({
|
|
119
|
-
organizationId: ctx.organizationId,
|
|
120
|
-
userId: config.ownerUserId,
|
|
121
|
-
definitionId: ctx.definitionId
|
|
122
|
-
});
|
|
123
|
-
};
|
|
124
|
-
const webhookConversation = async (ctx) => await boundConversation(ctx) ?? ownerDirectConversation(ctx);
|
|
125
|
-
const targetConversation = async (target, ctx) => {
|
|
126
|
-
if (target === "owner-direct" /* OwnerDirect */) return ownerDirectConversation(ctx);
|
|
127
|
-
const bound = await boundConversation(ctx);
|
|
128
|
-
if (bound == null) {
|
|
129
|
-
throw new Error(`channel binding ${ctx.bindingId ?? "(none)"} is attached to no conversation`);
|
|
130
|
-
}
|
|
131
|
-
return bound;
|
|
132
|
-
};
|
|
133
|
-
const bindingAgent = async (conversation, ctx) => {
|
|
134
|
-
const agent = (await ctx.platform.conversations.participants(conversation.conversationId)).find(
|
|
135
|
-
(participant) => participant.kind === ParticipantKind.Agent && !participant.left && participant.definitionId === ctx.definitionId
|
|
136
|
-
);
|
|
137
|
-
if (agent == null) {
|
|
138
|
-
throw new Error(
|
|
139
|
-
`channel binding ${ctx.bindingId ?? "(none)"} has no agent in conversation ${conversation.conversationId}`
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
return agent.id;
|
|
143
|
-
};
|
|
144
|
-
const sendBindingInput = async (args) => {
|
|
145
|
-
const binding = args.ctx.bindingId ?? "none";
|
|
146
|
-
const name = configSchema.parse(args.ctx.config).displayName ?? "Webhook";
|
|
147
|
-
await sendServiceInput(args.ctx.platform, {
|
|
148
|
-
room: args.conversation.conversationId,
|
|
149
|
-
participantId: await bindingAgent(args.conversation, args.ctx),
|
|
150
|
-
content: args.content,
|
|
151
|
-
principal: { principalId: `binding:${binding}`, authenticator: "final" },
|
|
152
|
-
displayName: name,
|
|
153
|
-
idempotencyKey: `webhook:${binding}:${args.deliveryKey ?? crypto.randomUUID()}`,
|
|
154
|
-
label: `From ${name}`,
|
|
155
|
-
turnPolicy: args.turnPolicy
|
|
156
|
-
});
|
|
157
|
-
return { conversationId: args.conversation.conversationId };
|
|
158
|
-
};
|
|
159
|
-
const createTargetedInputReceive = (turnPolicy = TurnPolicy.Steer) => async (input, ctx) => {
|
|
160
|
-
const targeted = targetedInboundSchema.parse(input);
|
|
161
|
-
return sendBindingInput({
|
|
162
|
-
ctx,
|
|
163
|
-
conversation: await targetConversation(targeted.target.kind, ctx),
|
|
164
|
-
content: contentBlocksOf(targeted.content),
|
|
165
|
-
deliveryKey: targeted.deliveryId ?? null,
|
|
166
|
-
turnPolicy
|
|
167
|
-
});
|
|
168
|
-
};
|
|
169
|
-
const receiveTargetedInput = createTargetedInputReceive(AGENT_EVENTS_TURN_POLICY);
|
|
170
|
-
const clip = (text) => text.length <= LINKED_CONVERSATION_TEXT_MAX_CHARS ? text : `${text.slice(0, LINKED_CONVERSATION_TEXT_MAX_CHARS)}
|
|
171
|
-
\u2026 (truncated)`;
|
|
172
|
-
const renderLinkedConversationEvent = (event) => {
|
|
173
|
-
const title = event.linkedTitle?.trim() ?? "";
|
|
174
|
-
const name = title.length === 0 ? "A linked conversation" : `"${title}"`;
|
|
175
|
-
const text = event.text?.trim() ?? "";
|
|
176
|
-
switch (event.event) {
|
|
177
|
-
case "replied" /* Replied */:
|
|
178
|
-
return text.length === 0 ? `${name} replied.` : `${name} replied:
|
|
179
|
-
|
|
180
|
-
${clip(text)}`;
|
|
181
|
-
case "waiting_for_person" /* WaitingForPerson */:
|
|
182
|
-
return text.length === 0 ? `${name} is waiting for a person.` : `${name} is waiting for a person: ${clip(text)}`;
|
|
183
|
-
case "failed" /* Failed */:
|
|
184
|
-
return text.length === 0 ? `${name} stopped with an error.` : `${name} stopped with an error.
|
|
185
|
-
|
|
186
|
-
${clip(text)}`;
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
const receiveLinkedConversationEvent = async (event, ctx) => {
|
|
190
|
-
const title = event.linkedTitle?.trim() ?? "";
|
|
191
|
-
await sendServiceInput(ctx.platform, {
|
|
192
|
-
room: event.conversationId,
|
|
193
|
-
participantId: event.participantId ?? void 0,
|
|
194
|
-
content: [textBlock(renderLinkedConversationEvent(event))],
|
|
195
|
-
principal: { principalId: `link:${event.linkId}`, authenticator: "final" },
|
|
196
|
-
displayName: title.length === 0 ? "Linked conversation" : title,
|
|
197
|
-
originTurnId: event.originTurnId ?? void 0,
|
|
198
|
-
idempotencyKey: `link:${event.linkId}:${event.eventId}`,
|
|
199
|
-
label: title.length === 0 ? "From a linked conversation" : `From \u201C${title}\u201D`
|
|
200
|
-
});
|
|
201
|
-
return { conversationId: event.conversationId };
|
|
202
|
-
};
|
|
203
|
-
const clipEnd = (text) => text.length <= SYSTEM_EVENT_TEXT_MAX_CHARS ? text : `\u2026 (truncated)
|
|
204
|
-
${text.slice(text.length - SYSTEM_EVENT_TEXT_MAX_CHARS)}`;
|
|
205
|
-
const outputSection = (label, output) => {
|
|
206
|
-
const text = output?.trimEnd() ?? "";
|
|
207
|
-
return text.length === 0 ? [] : [`${label}:
|
|
208
|
-
\`\`\`
|
|
209
|
-
${clipEnd(text)}
|
|
210
|
-
\`\`\``];
|
|
211
|
-
};
|
|
212
|
-
const renderSandboxEvent = (event) => {
|
|
213
|
-
const outcome = event.exitCode == null ? "stopped without an exit code" : `exited with code ${event.exitCode}`;
|
|
214
|
-
return [
|
|
215
|
-
`Background process ${event.processId} (\`${event.command}\`) ${outcome}.`,
|
|
216
|
-
...outputSection("stdout", event.stdout),
|
|
217
|
-
...outputSection("stderr", event.stderr)
|
|
218
|
-
].join("\n\n");
|
|
219
|
-
};
|
|
220
|
-
const routeToOwner = (participantId) => (args) => args.participants.some(
|
|
221
|
-
(participant) => participant.id === participantId && participant.kind === ParticipantKind.Agent && !participant.left
|
|
222
|
-
) ? [{ participantId, delivery: Delivery.Prompt }] : [];
|
|
223
|
-
const nonEmptyOr = (value, fallback) => {
|
|
224
|
-
const trimmed = value?.trim() ?? "";
|
|
225
|
-
return trimmed.length === 0 ? fallback : trimmed;
|
|
226
|
-
};
|
|
227
|
-
const receiveSandboxEvent = async (event, ctx) => {
|
|
228
|
-
const name = nonEmptyOr(event.sandboxName, "Sandbox");
|
|
229
|
-
const args = {
|
|
230
|
-
room: event.conversationId,
|
|
231
|
-
participantId: event.participantId ?? void 0,
|
|
232
|
-
content: [textBlock(renderSandboxEvent(event))],
|
|
233
|
-
principal: { principalId: `sandbox:${event.bindingId}`, authenticator: "final" },
|
|
234
|
-
displayName: name,
|
|
235
|
-
idempotencyKey: `sandbox:${event.bindingId}:${event.eventId}`,
|
|
236
|
-
label: `After a process in ${name} finished`
|
|
237
|
-
};
|
|
238
|
-
await sendServiceInput(ctx.platform, args);
|
|
239
|
-
return { conversationId: event.conversationId };
|
|
240
|
-
};
|
|
241
|
-
const receiveAgentEvent = async (input, ctx) => {
|
|
242
|
-
if (targetedInboundSchema.safeParse(input).success) return receiveTargetedInput(input, ctx);
|
|
243
|
-
const event = agentEventInboundSchema.parse(input);
|
|
244
|
-
if (event.kind === "webhook" /* Webhook */) {
|
|
245
|
-
const name = configSchema.parse(ctx.config).displayName ?? "Webhook";
|
|
246
|
-
const heading = event.event == null ? `${name} event` : `${name} event: ${event.event}`;
|
|
247
|
-
return sendBindingInput({
|
|
248
|
-
ctx,
|
|
249
|
-
conversation: await webhookConversation(ctx),
|
|
250
|
-
content: [textBlock(`${heading}
|
|
251
|
-
|
|
252
|
-
${renderBody(event.body, event.contentType ?? null)}`)],
|
|
253
|
-
deliveryKey: event.deliveryId ?? null
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
if (event.kind === "linked-conversation" /* LinkedConversation */) {
|
|
257
|
-
return receiveLinkedConversationEvent(event, ctx);
|
|
258
|
-
}
|
|
259
|
-
return receiveSandboxEvent(event, ctx);
|
|
260
|
-
};
|
|
261
|
-
const hex = (bytes) => [...new Uint8Array(bytes)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
262
|
-
const constantTimeEqual = (left, right) => {
|
|
263
|
-
if (left.length !== right.length) return false;
|
|
264
|
-
let difference = 0;
|
|
265
|
-
for (let index = 0; index < left.length; index += 1) {
|
|
266
|
-
difference |= left.charCodeAt(index) ^ right.charCodeAt(index);
|
|
267
|
-
}
|
|
268
|
-
return difference === 0;
|
|
269
|
-
};
|
|
270
|
-
const hmacSha256Hex = async (secret, body) => {
|
|
271
|
-
const encoder = new TextEncoder();
|
|
272
|
-
const key = await crypto.subtle.importKey(
|
|
273
|
-
"raw",
|
|
274
|
-
encoder.encode(secret),
|
|
275
|
-
{ name: "HMAC", hash: "SHA-256" },
|
|
276
|
-
false,
|
|
277
|
-
["sign"]
|
|
278
|
-
);
|
|
279
|
-
return hex(await crypto.subtle.sign("HMAC", key, encoder.encode(body)));
|
|
280
|
-
};
|
|
281
|
-
const verifyWebhookSignature = async (args) => {
|
|
282
|
-
const header = args.headers.get("x-final-signature") ?? args.headers.get("x-hub-signature-256");
|
|
283
|
-
if (header == null) return false;
|
|
284
|
-
const provided = header.startsWith("sha256=") ? header.slice("sha256=".length) : header;
|
|
285
|
-
return constantTimeEqual(provided.toLowerCase(), await hmacSha256Hex(args.secret, args.body));
|
|
286
|
-
};
|
|
287
|
-
const webhookRoute = {
|
|
288
|
-
method: "POST",
|
|
289
|
-
path: "/",
|
|
290
|
-
handler: async ({ request, secrets, receive }) => {
|
|
291
|
-
const secret = await secrets.get(WEBHOOK_SECRET_NAME);
|
|
292
|
-
if (secret == null) {
|
|
293
|
-
return Response.json({ error: "webhook secret is not configured" }, { status: 503 });
|
|
294
|
-
}
|
|
295
|
-
const body = await request.text();
|
|
296
|
-
if (!await verifyWebhookSignature({ secret, body, headers: request.headers })) {
|
|
297
|
-
return Response.json({ error: "invalid signature" }, { status: 401 });
|
|
298
|
-
}
|
|
299
|
-
const ref = await receive({
|
|
300
|
-
kind: "webhook" /* Webhook */,
|
|
301
|
-
body,
|
|
302
|
-
contentType: request.headers.get("content-type"),
|
|
303
|
-
event: request.headers.get("x-github-event") ?? request.headers.get("x-final-event"),
|
|
304
|
-
deliveryId: request.headers.get("x-github-delivery") ?? request.headers.get("x-final-delivery")
|
|
305
|
-
});
|
|
306
|
-
if (ref == null || !("conversationId" in ref))
|
|
307
|
-
throw new Error("agent event channel returned no conversation");
|
|
308
|
-
return Response.json({ ok: true, conversationId: ref.conversationId }, { status: 202 });
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
const createAgentEventsChannel = () => defineChannel({
|
|
312
|
-
routes: [webhookRoute],
|
|
313
|
-
receive: receiveAgentEvent,
|
|
314
|
-
route: routeMessage,
|
|
315
|
-
turnPolicy: AGENT_EVENTS_TURN_POLICY,
|
|
316
|
-
project: async () => {
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
export {
|
|
320
|
-
AGENT_EVENTS_SPECIFIER,
|
|
321
|
-
AGENT_EVENTS_TURN_POLICY,
|
|
322
|
-
AgentEventKind,
|
|
323
|
-
LINKED_CONVERSATION_TEXT_MAX_CHARS,
|
|
324
|
-
LinkedConversationEvent,
|
|
325
|
-
ReceiveTargetKind,
|
|
326
|
-
SYSTEM_EVENT_TEXT_MAX_CHARS,
|
|
327
|
-
SandboxEventKind2 as SandboxEventKind,
|
|
328
|
-
WEBHOOK_BODY_MAX_CHARS,
|
|
329
|
-
WEBHOOK_SECRET_NAME,
|
|
330
|
-
agentEventInboundSchema,
|
|
331
|
-
createAgentEventsChannel,
|
|
332
|
-
createTargetedInputReceive,
|
|
333
|
-
hmacSha256Hex,
|
|
334
|
-
receiveAgentEvent,
|
|
335
|
-
renderLinkedConversationEvent,
|
|
336
|
-
renderSandboxEvent,
|
|
337
|
-
routeToOwner,
|
|
338
|
-
targetedInboundSchema,
|
|
339
|
-
verifyWebhookSignature
|
|
340
|
-
};
|
|
341
|
-
//# sourceMappingURL=agent-events.mjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../../src/channels/agent-events.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';\nimport {\n defineChannel,\n Delivery,\n jsonObjectSchema,\n jsonValueSchema,\n ParticipantKind,\n SandboxEventKind,\n TurnPolicy,\n type ChannelDefinitionIface,\n type ChannelReceiveContextIface,\n type ConversationIface,\n type ConversationRefIface,\n type JsonValue,\n type RouteArgsIface,\n type RouteDecisionIface,\n type RouteDefinitionIface,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport { contentBlocksOf } from \"./content.mjs\";\nimport { textBlock } from \"./prompt.mjs\";\nimport { routeMessage } from \"./routing.mjs\";\nimport { sendServiceInput, type ServiceInputArgsIface } from \"./service-input.mjs\";\n\nexport const AGENT_EVENTS_SPECIFIER = '@finalai/final-agent-provider/channels/agent-events';\nexport const AGENT_EVENTS_TURN_POLICY = TurnPolicy.Steer;\n\n// The binding secret the webhook route verifies with (the binding's\n// `webhook_secret_ref`, resolved by the session service's secrets seam).\nexport const WEBHOOK_SECRET_NAME = 'webhook';\nexport const WEBHOOK_BODY_MAX_CHARS = 8_000;\n\nexport enum AgentEventKind {\n Webhook = 'webhook',\n LinkedConversation = 'linked-conversation',\n // A sandbox's result that arrives after the turn that started the work.\n Sandbox = 'sandbox',\n}\n\nexport { SandboxEventKind } from '@finalai/agent-provider';\n\nexport const SYSTEM_EVENT_TEXT_MAX_CHARS = 4_000;\n\n// What happened in a linked conversation (one a conversation handed work to).\nexport enum LinkedConversationEvent {\n // Its agent ended a turn with a reply.\n Replied = 'replied',\n // Its agent asked for a permission or an answer.\n WaitingForPerson = 'waiting_for_person',\n // Its agent's turn ended without a reply because it failed.\n Failed = 'failed',\n}\n\nexport const LINKED_CONVERSATION_TEXT_MAX_CHARS = 4_000;\n\nconst webhookEventSchema = z.object({\n kind: z.literal(AgentEventKind.Webhook),\n body: z.string(),\n contentType: z.string().nullish(),\n // A provider event name when the sender gives one (`x-github-event`).\n event: z.string().nullish(),\n deliveryId: z.string().nullish(),\n});\n\n// Input to the conversation that linked another, from the link, so the agent\n// there hears about work it handed off.\nconst linkedConversationEventSchema = z.object({\n kind: z.literal(AgentEventKind.LinkedConversation),\n // The conversation that linked the other: where its agent is prompted.\n conversationId: z.string().min(1),\n // The agent that handed the work off; the Default agent when absent.\n participantId: z.string().min(1).nullish(),\n linkId: z.string().min(1),\n linkedConversationId: z.string().min(1),\n linkedTitle: z.string().nullish(),\n event: z.nativeEnum(LinkedConversationEvent),\n // The reply (Replied) or what the agent asks for (WaitingForPerson).\n text: z.string().nullish(),\n // Makes a redelivered event post once (the turn or decision).\n eventId: z.string().min(1),\n originTurnId: z.string().nullish(),\n});\n\n// Input from the sandbox binding to the agent whose session started the process.\nconst sandboxEventSchema = z.object({\n kind: z.literal(AgentEventKind.Sandbox),\n conversationId: z.string().min(1),\n // The agent participant whose session started the process: the only one told.\n participantId: z.string().min(1).nullish(),\n bindingId: z.string().min(1),\n sandboxName: z.string().nullish(),\n event: z.nativeEnum(SandboxEventKind),\n processId: z.string().min(1),\n command: z.string(),\n // Null when the sandbox could not report one (the process was lost).\n exitCode: z.number().int().nullable(),\n // The end of the process's output.\n stdout: z.string().nullish(),\n stderr: z.string().nullish(),\n // Makes a redelivered event post once (per binding).\n eventId: z.string().min(1),\n});\n\nexport const agentEventInboundSchema = z.discriminatedUnion('kind', [\n webhookEventSchema,\n linkedConversationEventSchema,\n sandboxEventSchema,\n]);\n\nexport type SandboxEventInboundIface = z.infer<typeof sandboxEventSchema>;\n\nexport type AgentEventInboundIface = z.infer<typeof agentEventInboundSchema>;\n\nexport enum ReceiveTargetKind {\n // The conversation the channel binding is attached to.\n BoundConversation = 'bound-conversation',\n // The agent owner's direct conversation with the agent.\n OwnerDirect = 'owner-direct',\n}\n\n// What a channel route hands to the default `receive`: content for the\n// binding's agent in a target conversation, as input from the binding.\nexport const targetedInboundSchema = z.object({\n target: z.object({ kind: z.nativeEnum(ReceiveTargetKind) }),\n content: z.array(jsonObjectSchema).min(1),\n // Makes a redelivered input count once.\n deliveryId: z.string().min(1).nullish(),\n});\n\nexport type TargetedInboundIface = z.infer<typeof targetedInboundSchema>;\n\nconst configSchema = z.looseObject({\n displayName: z.string().min(1).nullish(),\n // Owner direct conversation when the binding is attached to none; null for\n // organization-owned agents, whose webhooks need a bound conversation.\n ownerUserId: z.string().min(1).nullish(),\n});\n\nconst renderBody = (body: string, contentType: string | null): string => {\n let rendered = body;\n if (contentType?.includes('json') === true) {\n try {\n rendered = JSON.stringify(jsonValueSchema.parse(JSON.parse(body)), null, 2);\n } catch {\n rendered = body;\n }\n }\n return rendered.length <= WEBHOOK_BODY_MAX_CHARS\n ? rendered\n : `${rendered.slice(0, WEBHOOK_BODY_MAX_CHARS)}\\n\u2026 (truncated)`;\n};\n\nconst boundConversation = async (\n ctx: ChannelReceiveContextIface,\n): Promise<ConversationIface | null> =>\n ctx.bindingId == null ? null : ctx.platform.conversations.boundTo(ctx.bindingId);\n\nconst ownerDirectConversation = async (\n ctx: ChannelReceiveContextIface,\n): Promise<ConversationIface> => {\n const config = configSchema.parse(ctx.config);\n if (config.ownerUserId == null || ctx.definitionId == null) {\n throw new Error(\n `channel binding ${ctx.bindingId ?? '(none)'} is attached to no conversation and its agent has no owner to post to`,\n );\n }\n return ctx.platform.conversations.findOrCreateDirect({\n organizationId: ctx.organizationId,\n userId: config.ownerUserId,\n definitionId: ctx.definitionId,\n });\n};\n\nconst webhookConversation = async (ctx: ChannelReceiveContextIface): Promise<ConversationIface> =>\n (await boundConversation(ctx)) ?? ownerDirectConversation(ctx);\n\nconst targetConversation = async (\n target: ReceiveTargetKind,\n ctx: ChannelReceiveContextIface,\n): Promise<ConversationIface> => {\n if (target === ReceiveTargetKind.OwnerDirect) return ownerDirectConversation(ctx);\n const bound = await boundConversation(ctx);\n if (bound == null) {\n throw new Error(`channel binding ${ctx.bindingId ?? '(none)'} is attached to no conversation`);\n }\n return bound;\n};\n\n// The binding's own agent in the conversation its input goes to.\nconst bindingAgent = async (\n conversation: ConversationIface,\n ctx: ChannelReceiveContextIface,\n): Promise<string> => {\n const agent = (await ctx.platform.conversations.participants(conversation.conversationId)).find(\n (participant) =>\n participant.kind === ParticipantKind.Agent &&\n !participant.left &&\n participant.definitionId === ctx.definitionId,\n );\n if (agent == null) {\n throw new Error(\n `channel binding ${ctx.bindingId ?? '(none)'} has no agent in conversation ${conversation.conversationId}`,\n );\n }\n return agent.id;\n};\n\n// Input from the binding (a webhook or an authored route) to its agent.\nconst sendBindingInput = async (args: {\n ctx: ChannelReceiveContextIface;\n conversation: ConversationIface;\n content: ContentBlock[];\n deliveryKey: string | null;\n turnPolicy?: TurnPolicy;\n}): Promise<ConversationRefIface> => {\n const binding = args.ctx.bindingId ?? 'none';\n const name = configSchema.parse(args.ctx.config).displayName ?? 'Webhook';\n await sendServiceInput(args.ctx.platform, {\n room: args.conversation.conversationId,\n participantId: await bindingAgent(args.conversation, args.ctx),\n content: args.content,\n principal: { principalId: `binding:${binding}`, authenticator: 'final' },\n displayName: name,\n idempotencyKey: `webhook:${binding}:${args.deliveryKey ?? crypto.randomUUID()}`,\n label: `From ${name}`,\n turnPolicy: args.turnPolicy,\n });\n return { conversationId: args.conversation.conversationId };\n};\n\nexport type ChannelReceiveIface = (\n input: JsonValue,\n ctx: ChannelReceiveContextIface,\n) => Promise<ConversationRefIface>;\n\n// The receive of a custom channel that authors none: targeted content\n// (`{target, content}`), input to the binding's agent there under the\n// channel's turnPolicy.\nexport const createTargetedInputReceive =\n (turnPolicy: TurnPolicy = TurnPolicy.Steer): ChannelReceiveIface =>\n async (input, ctx) => {\n const targeted = targetedInboundSchema.parse(input);\n return sendBindingInput({\n ctx,\n conversation: await targetConversation(targeted.target.kind, ctx),\n content: contentBlocksOf(targeted.content),\n deliveryKey: targeted.deliveryId ?? null,\n turnPolicy,\n });\n };\n\nconst receiveTargetedInput = createTargetedInputReceive(AGENT_EVENTS_TURN_POLICY);\n\nconst clip = (text: string): string =>\n text.length <= LINKED_CONVERSATION_TEXT_MAX_CHARS\n ? text\n : `${text.slice(0, LINKED_CONVERSATION_TEXT_MAX_CHARS)}\\n\u2026 (truncated)`;\n\nexport const renderLinkedConversationEvent = (\n event: Pick<z.infer<typeof linkedConversationEventSchema>, 'linkedTitle' | 'event' | 'text'>,\n): string => {\n const title = event.linkedTitle?.trim() ?? '';\n const name = title.length === 0 ? 'A linked conversation' : `\"${title}\"`;\n const text = event.text?.trim() ?? '';\n switch (event.event) {\n case LinkedConversationEvent.Replied:\n return text.length === 0 ? `${name} replied.` : `${name} replied:\\n\\n${clip(text)}`;\n case LinkedConversationEvent.WaitingForPerson:\n return text.length === 0\n ? `${name} is waiting for a person.`\n : `${name} is waiting for a person: ${clip(text)}`;\n case LinkedConversationEvent.Failed:\n return text.length === 0\n ? `${name} stopped with an error.`\n : `${name} stopped with an error.\\n\\n${clip(text)}`;\n }\n};\n\n// Input from the link (the result of work handed off) to the agent that handed it off.\nconst receiveLinkedConversationEvent = async (\n event: z.infer<typeof linkedConversationEventSchema>,\n ctx: ChannelReceiveContextIface,\n): Promise<ConversationRefIface> => {\n const title = event.linkedTitle?.trim() ?? '';\n await sendServiceInput(ctx.platform, {\n room: event.conversationId,\n participantId: event.participantId ?? undefined,\n content: [textBlock(renderLinkedConversationEvent(event))],\n principal: { principalId: `link:${event.linkId}`, authenticator: 'final' },\n displayName: title.length === 0 ? 'Linked conversation' : title,\n originTurnId: event.originTurnId ?? undefined,\n idempotencyKey: `link:${event.linkId}:${event.eventId}`,\n label: title.length === 0 ? 'From a linked conversation' : `From \u201C${title}\u201D`,\n });\n return { conversationId: event.conversationId };\n};\n\nconst clipEnd = (text: string): string =>\n text.length <= SYSTEM_EVENT_TEXT_MAX_CHARS\n ? text\n : `\u2026 (truncated)\\n${text.slice(text.length - SYSTEM_EVENT_TEXT_MAX_CHARS)}`;\n\nconst outputSection = (label: string, output: string | null | undefined): string[] => {\n const text = output?.trimEnd() ?? '';\n return text.length === 0 ? [] : [`${label}:\\n\\`\\`\\`\\n${clipEnd(text)}\\n\\`\\`\\``];\n};\n\nexport const renderSandboxEvent = (\n event: Pick<SandboxEventInboundIface, 'processId' | 'command' | 'exitCode' | 'stdout' | 'stderr'>,\n): string => {\n const outcome =\n event.exitCode == null ? 'stopped without an exit code' : `exited with code ${event.exitCode}`;\n return [\n `Background process ${event.processId} (\\`${event.command}\\`) ${outcome}.`,\n ...outputSection('stdout', event.stdout),\n ...outputSection('stderr', event.stderr),\n ].join('\\n\\n');\n};\n\n// A result belongs to the session that produced it: that agent is prompted and\n// nobody observes it, so one agent's process output never reaches another\n// agent's history (a group's Default or All agents included).\nexport const routeToOwner =\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\nconst nonEmptyOr = (value: string | null | undefined, fallback: string): string => {\n const trimmed = value?.trim() ?? '';\n return trimmed.length === 0 ? fallback : trimmed;\n};\n\nconst receiveSandboxEvent = async (\n event: SandboxEventInboundIface,\n ctx: ChannelReceiveContextIface,\n): Promise<ConversationRefIface> => {\n const name = nonEmptyOr(event.sandboxName, 'Sandbox');\n const args: ServiceInputArgsIface = {\n room: event.conversationId,\n participantId: event.participantId ?? undefined,\n content: [textBlock(renderSandboxEvent(event))],\n principal: { principalId: `sandbox:${event.bindingId}`, authenticator: 'final' },\n displayName: name,\n idempotencyKey: `sandbox:${event.bindingId}:${event.eventId}`,\n label: `After a process in ${name} finished`,\n };\n await sendServiceInput(ctx.platform, args);\n return { conversationId: event.conversationId };\n};\n\n// agent-events' receive: a webhook, a linked conversation's event, a sandbox\n// result, or targeted content, each input to one agent. Outside systems\n// (Final's Goals among them) post conversation events through the public API instead.\nexport const receiveAgentEvent: ChannelReceiveIface = async (input, ctx) => {\n if (targetedInboundSchema.safeParse(input).success) return receiveTargetedInput(input, ctx);\n const event = agentEventInboundSchema.parse(input);\n if (event.kind === AgentEventKind.Webhook) {\n const name = configSchema.parse(ctx.config).displayName ?? 'Webhook';\n const heading = event.event == null ? `${name} event` : `${name} event: ${event.event}`;\n return sendBindingInput({\n ctx,\n conversation: await webhookConversation(ctx),\n content: [textBlock(`${heading}\\n\\n${renderBody(event.body, event.contentType ?? null)}`)],\n deliveryKey: event.deliveryId ?? null,\n });\n }\n if (event.kind === AgentEventKind.LinkedConversation) {\n return receiveLinkedConversationEvent(event, ctx);\n }\n return receiveSandboxEvent(event, ctx);\n};\n\nconst hex = (bytes: ArrayBuffer): string =>\n [...new Uint8Array(bytes)].map((byte) => byte.toString(16).padStart(2, '0')).join('');\n\nconst constantTimeEqual = (left: string, right: string): boolean => {\n if (left.length !== right.length) return false;\n let difference = 0;\n for (let index = 0; index < left.length; index += 1) {\n difference |= left.charCodeAt(index) ^ right.charCodeAt(index);\n }\n return difference === 0;\n};\n\nexport const hmacSha256Hex = async (secret: string, body: string): Promise<string> => {\n const encoder = new TextEncoder();\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n return hex(await crypto.subtle.sign('HMAC', key, encoder.encode(body)));\n};\n\n// `sha256=<hex>` in `x-final-signature` or GitHub's `x-hub-signature-256`.\nexport const verifyWebhookSignature = async (args: {\n secret: string;\n body: string;\n headers: Headers;\n}): Promise<boolean> => {\n const header = args.headers.get('x-final-signature') ?? args.headers.get('x-hub-signature-256');\n if (header == null) return false;\n const provided = header.startsWith('sha256=') ? header.slice('sha256='.length) : header;\n return constantTimeEqual(provided.toLowerCase(), await hmacSha256Hex(args.secret, args.body));\n};\n\nconst webhookRoute: RouteDefinitionIface = {\n method: 'POST',\n path: '/',\n handler: async ({ request, secrets, receive }) => {\n const secret = await secrets.get(WEBHOOK_SECRET_NAME);\n if (secret == null) {\n return Response.json({ error: 'webhook secret is not configured' }, { status: 503 });\n }\n const body = await request.text();\n if (!(await verifyWebhookSignature({ secret, body, headers: request.headers }))) {\n return Response.json({ error: 'invalid signature' }, { status: 401 });\n }\n const ref = await receive({\n kind: AgentEventKind.Webhook,\n body,\n contentType: request.headers.get('content-type'),\n event: request.headers.get('x-github-event') ?? request.headers.get('x-final-event'),\n deliveryId:\n request.headers.get('x-github-delivery') ?? request.headers.get('x-final-delivery'),\n });\n if (ref == null || !('conversationId' in ref))\n throw new Error('agent event channel returned no conversation');\n return Response.json({ ok: true, conversationId: ref.conversationId }, { status: 202 });\n },\n};\n\n// Its inputs go to agents in conversations whose own channel (direct or\n// group messaging) projects the replies, so this channel projects nothing.\nexport const createAgentEventsChannel = (): ChannelDefinitionIface<JsonValue, JsonValue> =>\n defineChannel<JsonValue, JsonValue>({\n routes: [webhookRoute],\n receive: receiveAgentEvent,\n route: routeMessage,\n turnPolicy: AGENT_EVENTS_TURN_POLICY,\n project: async () => {},\n });\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["AgentEventKind", "SandboxEventKind", "LinkedConversationEvent", "ReceiveTargetKind"]
|
|
7
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { ScheduleDelivery, ScheduleTarget, TurnPolicy, type ChannelDefinitionIface, type ChannelReceiveContextIface, type ConversationRefIface, type JsonValue } from '@finalai/agent-provider';
|
|
2
|
-
import * as z from 'zod';
|
|
3
|
-
export declare const AGENT_SCHEDULES_SPECIFIER = "@finalai/final-agent-provider/channels/agent-schedules";
|
|
4
|
-
export declare const AGENT_SCHEDULES_TURN_POLICY = TurnPolicy.Steer;
|
|
5
|
-
export declare const IF_NEEDED_INSTRUCTION = "This is a scheduled check with delivery=if_needed. Reply only if something needs attention; when there is nothing worth reporting, end your turn without writing a message.";
|
|
6
|
-
export declare const scheduleFiringInboundSchema: z.ZodObject<{
|
|
7
|
-
organizationId: z.ZodString;
|
|
8
|
-
scheduleId: z.ZodString;
|
|
9
|
-
scheduleKey: z.ZodString;
|
|
10
|
-
definitionId: z.ZodString;
|
|
11
|
-
ownerUserId: z.ZodNullable<z.ZodString>;
|
|
12
|
-
target: z.ZodEnum<typeof ScheduleTarget>;
|
|
13
|
-
conversationId: z.ZodNullable<z.ZodString>;
|
|
14
|
-
prompt: z.ZodString;
|
|
15
|
-
scheduledFor: z.ZodString;
|
|
16
|
-
displayName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
17
|
-
label: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
18
|
-
clientMessageId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
19
|
-
delivery: z.ZodOptional<z.ZodNullable<z.ZodEnum<typeof ScheduleDelivery>>>;
|
|
20
|
-
}, z.core.$strip>;
|
|
21
|
-
export type ScheduleFiringInboundIface = z.infer<typeof scheduleFiringInboundSchema>;
|
|
22
|
-
export declare const scheduleSourceRef: (scheduleId: string) => string;
|
|
23
|
-
export declare const scheduleFiringClientMessageId: (scheduleId: string, scheduledFor: string) => string;
|
|
24
|
-
export declare const receiveScheduleFiring: (input: JsonValue, ctx: ChannelReceiveContextIface) => Promise<ConversationRefIface>;
|
|
25
|
-
export declare const createAgentSchedulesChannel: () => ChannelDefinitionIface<JsonValue, JsonValue>;
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { FinalContentRole, withFinalMeta } from "@finalai/acp-session";
|
|
2
|
-
import {
|
|
3
|
-
defineChannel,
|
|
4
|
-
ParticipantKind,
|
|
5
|
-
ScheduleDelivery,
|
|
6
|
-
ScheduleTarget,
|
|
7
|
-
TurnPolicy
|
|
8
|
-
} from "@finalai/agent-provider";
|
|
9
|
-
import * as z from "zod";
|
|
10
|
-
import { textBlock } from "./prompt.mjs";
|
|
11
|
-
import { routeMessage } from "./routing.mjs";
|
|
12
|
-
import { sendServiceInput } from "./service-input.mjs";
|
|
13
|
-
const AGENT_SCHEDULES_SPECIFIER = "@finalai/final-agent-provider/channels/agent-schedules";
|
|
14
|
-
const AGENT_SCHEDULES_TURN_POLICY = TurnPolicy.Steer;
|
|
15
|
-
const IF_NEEDED_INSTRUCTION = "This is a scheduled check with delivery=if_needed. Reply only if something needs attention; when there is nothing worth reporting, end your turn without writing a message.";
|
|
16
|
-
const scheduleFiringInboundSchema = z.object({
|
|
17
|
-
organizationId: z.string().min(1),
|
|
18
|
-
scheduleId: z.string().min(1),
|
|
19
|
-
scheduleKey: z.string().min(1),
|
|
20
|
-
definitionId: z.string().min(1),
|
|
21
|
-
// Required for owner-direct schedules.
|
|
22
|
-
ownerUserId: z.string().min(1).nullable(),
|
|
23
|
-
target: z.nativeEnum(ScheduleTarget),
|
|
24
|
-
conversationId: z.string().min(1).nullable(),
|
|
25
|
-
prompt: z.string().min(1),
|
|
26
|
-
scheduledFor: z.string().min(1),
|
|
27
|
-
displayName: z.string().min(1).nullish(),
|
|
28
|
-
// The caption of the agent's reply ("After the 9:00 schedule"); derived when absent.
|
|
29
|
-
label: z.string().min(1).nullish(),
|
|
30
|
-
// Defaults to scheduleFiringClientMessageId(scheduleId, scheduledFor).
|
|
31
|
-
clientMessageId: z.string().min(1).nullish(),
|
|
32
|
-
// Absent means always.
|
|
33
|
-
delivery: z.nativeEnum(ScheduleDelivery).nullish()
|
|
34
|
-
});
|
|
35
|
-
const scheduleSourceRef = (scheduleId) => `schedule:${scheduleId}`;
|
|
36
|
-
const scheduleFiringClientMessageId = (scheduleId, scheduledFor) => `schedule:${scheduleId}:${scheduledFor}`;
|
|
37
|
-
const conversationForFiring = async (firing, ctx) => {
|
|
38
|
-
if (firing.target === ScheduleTarget.OwnerDirect) {
|
|
39
|
-
if (firing.ownerUserId == null) {
|
|
40
|
-
throw new Error(`schedule ${firing.scheduleId} has no owner`);
|
|
41
|
-
}
|
|
42
|
-
return ctx.platform.conversations.findOrCreateDirect({
|
|
43
|
-
organizationId: firing.organizationId,
|
|
44
|
-
userId: firing.ownerUserId,
|
|
45
|
-
definitionId: firing.definitionId
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
const bound = firing.conversationId == null ? ctx.bindingId == null ? null : await ctx.platform.conversations.boundTo(ctx.bindingId) : await ctx.platform.conversations.get(firing.conversationId);
|
|
49
|
-
if (bound == null) {
|
|
50
|
-
throw new Error(`schedule ${firing.scheduleId} has no bound conversation`);
|
|
51
|
-
}
|
|
52
|
-
return bound;
|
|
53
|
-
};
|
|
54
|
-
const firingContent = (firing) => firing.delivery === ScheduleDelivery.IfNeeded ? [
|
|
55
|
-
textBlock(firing.prompt),
|
|
56
|
-
withFinalMeta(textBlock(IF_NEEDED_INSTRUCTION), { role: FinalContentRole.Instruction })
|
|
57
|
-
] : [textBlock(firing.prompt)];
|
|
58
|
-
const receiveScheduleFiring = async (input, ctx) => {
|
|
59
|
-
const firing = scheduleFiringInboundSchema.parse(input);
|
|
60
|
-
const conversation = await conversationForFiring(firing, ctx);
|
|
61
|
-
const agent = (await ctx.platform.conversations.participants(conversation.conversationId)).find(
|
|
62
|
-
(participant) => participant.kind === ParticipantKind.Agent && !participant.left && participant.definitionId === firing.definitionId
|
|
63
|
-
);
|
|
64
|
-
if (agent == null) {
|
|
65
|
-
throw new Error(`schedule ${firing.scheduleId} has no agent in its conversation`);
|
|
66
|
-
}
|
|
67
|
-
await sendServiceInput(ctx.platform, {
|
|
68
|
-
room: conversation.conversationId,
|
|
69
|
-
participantId: agent.id,
|
|
70
|
-
content: firingContent(firing),
|
|
71
|
-
principal: { principalId: scheduleSourceRef(firing.scheduleId), authenticator: "final" },
|
|
72
|
-
displayName: firing.displayName ?? firing.scheduleKey,
|
|
73
|
-
idempotencyKey: firing.clientMessageId ?? scheduleFiringClientMessageId(firing.scheduleId, firing.scheduledFor),
|
|
74
|
-
label: firing.label ?? `After the ${firing.displayName ?? firing.scheduleKey} schedule`,
|
|
75
|
-
turnPolicy: AGENT_SCHEDULES_TURN_POLICY
|
|
76
|
-
});
|
|
77
|
-
return { conversationId: conversation.conversationId };
|
|
78
|
-
};
|
|
79
|
-
const createAgentSchedulesChannel = () => defineChannel({
|
|
80
|
-
receive: receiveScheduleFiring,
|
|
81
|
-
route: routeMessage,
|
|
82
|
-
turnPolicy: AGENT_SCHEDULES_TURN_POLICY,
|
|
83
|
-
project: async () => {
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
export {
|
|
87
|
-
AGENT_SCHEDULES_SPECIFIER,
|
|
88
|
-
AGENT_SCHEDULES_TURN_POLICY,
|
|
89
|
-
IF_NEEDED_INSTRUCTION,
|
|
90
|
-
createAgentSchedulesChannel,
|
|
91
|
-
receiveScheduleFiring,
|
|
92
|
-
scheduleFiringClientMessageId,
|
|
93
|
-
scheduleFiringInboundSchema,
|
|
94
|
-
scheduleSourceRef
|
|
95
|
-
};
|
|
96
|
-
//# sourceMappingURL=agent-schedules.mjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../../src/channels/agent-schedules.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';\nimport { FinalContentRole, withFinalMeta } from '@finalai/acp-session';\nimport {\n defineChannel,\n ParticipantKind,\n ScheduleDelivery,\n ScheduleTarget,\n TurnPolicy,\n type ChannelDefinitionIface,\n type ChannelReceiveContextIface,\n type ConversationIface,\n type ConversationRefIface,\n type JsonValue,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport { textBlock } from \"./prompt.mjs\";\nimport { routeMessage } from \"./routing.mjs\";\nimport { sendServiceInput } from \"./service-input.mjs\";\n\nexport const AGENT_SCHEDULES_SPECIFIER = '@finalai/final-agent-provider/channels/agent-schedules';\nexport const AGENT_SCHEDULES_TURN_POLICY = TurnPolicy.Steer;\n\n// The instruction an if_needed firing carries (an empty turn projects nothing).\nexport const IF_NEEDED_INSTRUCTION =\n 'This is a scheduled check with delivery=if_needed. Reply only if something needs attention; when there is nothing worth reporting, end your turn without writing a message.';\n\n// One firing of an agent schedule. The session service's\n// ScheduleFire reconcile builds this; `clientMessageId` makes a retried firing\n// one input to the schedule's agent.\nexport const scheduleFiringInboundSchema = z.object({\n organizationId: z.string().min(1),\n scheduleId: z.string().min(1),\n scheduleKey: z.string().min(1),\n definitionId: z.string().min(1),\n // Required for owner-direct schedules.\n ownerUserId: z.string().min(1).nullable(),\n target: z.nativeEnum(ScheduleTarget),\n conversationId: z.string().min(1).nullable(),\n prompt: z.string().min(1),\n scheduledFor: z.string().min(1),\n displayName: z.string().min(1).nullish(),\n // The caption of the agent's reply (\"After the 9:00 schedule\"); derived when absent.\n label: z.string().min(1).nullish(),\n // Defaults to scheduleFiringClientMessageId(scheduleId, scheduledFor).\n clientMessageId: z.string().min(1).nullish(),\n // Absent means always.\n delivery: z.nativeEnum(ScheduleDelivery).nullish(),\n});\n\nexport type ScheduleFiringInboundIface = z.infer<typeof scheduleFiringInboundSchema>;\n\nexport const scheduleSourceRef = (scheduleId: string): string => `schedule:${scheduleId}`;\n\nexport const scheduleFiringClientMessageId = (scheduleId: string, scheduledFor: string): string =>\n `schedule:${scheduleId}:${scheduledFor}`;\n\nconst conversationForFiring = async (\n firing: ScheduleFiringInboundIface,\n ctx: ChannelReceiveContextIface,\n): Promise<ConversationIface> => {\n if (firing.target === ScheduleTarget.OwnerDirect) {\n if (firing.ownerUserId == null) {\n throw new Error(`schedule ${firing.scheduleId} has no owner`);\n }\n return ctx.platform.conversations.findOrCreateDirect({\n organizationId: firing.organizationId,\n userId: firing.ownerUserId,\n definitionId: firing.definitionId,\n });\n }\n const bound =\n firing.conversationId == null\n ? ctx.bindingId == null\n ? null\n : await ctx.platform.conversations.boundTo(ctx.bindingId)\n : await ctx.platform.conversations.get(firing.conversationId);\n if (bound == null) {\n throw new Error(`schedule ${firing.scheduleId} has no bound conversation`);\n }\n return bound;\n};\n\nconst firingContent = (firing: ScheduleFiringInboundIface): ContentBlock[] =>\n firing.delivery === ScheduleDelivery.IfNeeded\n ? [\n textBlock(firing.prompt),\n withFinalMeta(textBlock(IF_NEEDED_INSTRUCTION), { role: FinalContentRole.Instruction }),\n ]\n : [textBlock(firing.prompt)];\n\n// A firing is input to the schedule's own agent from the schedule as a service\n// principal: no message is posted, and the agent's reply carries the caption.\nexport const receiveScheduleFiring = async (\n input: JsonValue,\n ctx: ChannelReceiveContextIface,\n): Promise<ConversationRefIface> => {\n const firing = scheduleFiringInboundSchema.parse(input);\n const conversation = await conversationForFiring(firing, ctx);\n const agent = (await ctx.platform.conversations.participants(conversation.conversationId)).find(\n (participant) =>\n participant.kind === ParticipantKind.Agent &&\n !participant.left &&\n participant.definitionId === firing.definitionId,\n );\n if (agent == null) {\n throw new Error(`schedule ${firing.scheduleId} has no agent in its conversation`);\n }\n await sendServiceInput(ctx.platform, {\n room: conversation.conversationId,\n participantId: agent.id,\n content: firingContent(firing),\n principal: { principalId: scheduleSourceRef(firing.scheduleId), authenticator: 'final' },\n displayName: firing.displayName ?? firing.scheduleKey,\n idempotencyKey:\n firing.clientMessageId ??\n scheduleFiringClientMessageId(firing.scheduleId, firing.scheduledFor),\n label: firing.label ?? `After the ${firing.displayName ?? firing.scheduleKey} schedule`,\n turnPolicy: AGENT_SCHEDULES_TURN_POLICY,\n });\n return { conversationId: conversation.conversationId };\n};\n\n// Firings land in conversations whose own channel (direct or group messaging)\n// projects the replies, so this channel projects nothing.\nexport const createAgentSchedulesChannel = (): ChannelDefinitionIface<JsonValue, JsonValue> =>\n defineChannel<JsonValue, JsonValue>({\n receive: receiveScheduleFiring,\n route: routeMessage,\n turnPolicy: AGENT_SCHEDULES_TURN_POLICY,\n project: async () => {},\n });\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';
|
|
2
|
-
import { ChannelPrincipalType, type ChannelAuthIface, type ChannelInputReceiptIface, type ChannelPlatformIface, type JsonObject, type TurnPolicy } from '@finalai/agent-provider';
|
|
3
|
-
export interface ServiceInputArgsIface {
|
|
4
|
-
room: string;
|
|
5
|
-
participantId?: string;
|
|
6
|
-
content: ContentBlock[];
|
|
7
|
-
principal: {
|
|
8
|
-
principalId: string;
|
|
9
|
-
principalType?: ChannelPrincipalType | `${ChannelPrincipalType}`;
|
|
10
|
-
authenticator: string;
|
|
11
|
-
attributes?: JsonObject;
|
|
12
|
-
};
|
|
13
|
-
displayName: string;
|
|
14
|
-
actingUserId?: string;
|
|
15
|
-
originTurnId?: string;
|
|
16
|
-
idempotencyKey: string;
|
|
17
|
-
label: string;
|
|
18
|
-
context?: string[];
|
|
19
|
-
turnPolicy?: TurnPolicy | `${TurnPolicy}`;
|
|
20
|
-
}
|
|
21
|
-
export declare const serviceAuthOf: (args: ServiceInputArgsIface) => ChannelAuthIface;
|
|
22
|
-
export declare const sendServiceInput: (platform: Pick<ChannelPlatformIface, "channelSessions">, args: ServiceInputArgsIface) => Promise<ChannelInputReceiptIface>;
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ChannelPrincipalType
|
|
3
|
-
} from "@finalai/agent-provider";
|
|
4
|
-
const serviceAuthOf = (args) => ({
|
|
5
|
-
principalId: args.principal.principalId,
|
|
6
|
-
principalType: args.principal.principalType ?? ChannelPrincipalType.Service,
|
|
7
|
-
authenticator: args.principal.authenticator,
|
|
8
|
-
displayName: args.displayName,
|
|
9
|
-
attributes: args.principal.attributes ?? {}
|
|
10
|
-
});
|
|
11
|
-
const sendServiceInput = async (platform, args) => {
|
|
12
|
-
if (platform.channelSessions == null)
|
|
13
|
-
throw new Error("channel session operations are unavailable");
|
|
14
|
-
return platform.channelSessions.send(
|
|
15
|
-
{ conversationId: args.room, participantId: args.participantId },
|
|
16
|
-
{
|
|
17
|
-
content: args.content,
|
|
18
|
-
idempotencyKey: args.idempotencyKey,
|
|
19
|
-
auth: serviceAuthOf(args),
|
|
20
|
-
actingUserId: args.actingUserId,
|
|
21
|
-
originTurnId: args.originTurnId,
|
|
22
|
-
source: { label: args.label },
|
|
23
|
-
context: args.context,
|
|
24
|
-
turnPolicy: args.turnPolicy
|
|
25
|
-
}
|
|
26
|
-
);
|
|
27
|
-
};
|
|
28
|
-
export {
|
|
29
|
-
sendServiceInput,
|
|
30
|
-
serviceAuthOf
|
|
31
|
-
};
|
|
32
|
-
//# sourceMappingURL=service-input.mjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../../src/channels/service-input.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';\nimport {\n ChannelPrincipalType,\n type ChannelAuthIface,\n type ChannelInputReceiptIface,\n type ChannelPlatformIface,\n type JsonObject,\n type TurnPolicy,\n} from '@finalai/agent-provider';\n\n// Input to a Final room's agent from no member of the room: a schedule\n// firing, a webhook, a linked conversation, a sandbox result, a handoff from\n// another channel. It is channel input from a principal, never a message: the\n// platform accepts it at once and prompts the agent; the agent's reply in the\n// room carries `label` as its caption.\n\nexport interface ServiceInputArgsIface {\n // The room (conversation id).\n room: string;\n // One agent of the room; the room's Default agent when absent.\n participantId?: string;\n content: ContentBlock[];\n // Who sent it. A service principal unless a channel authenticated a person.\n principal: {\n principalId: string;\n principalType?: ChannelPrincipalType | `${ChannelPrincipalType}`;\n authenticator: string;\n attributes?: JsonObject;\n };\n // How the agent and the room name the principal.\n displayName: string;\n // The person the input acts for: who pays for the turn and answers what it asks.\n actingUserId?: string;\n // The turn whose work produced the input, for loop limits and reply provenance.\n originTurnId?: string;\n idempotencyKey: string;\n // The reply's caption (\"After the 9:00 schedule\"), written by the producer.\n label: string;\n context?: string[];\n turnPolicy?: TurnPolicy | `${TurnPolicy}`;\n}\n\nexport const serviceAuthOf = (args: ServiceInputArgsIface): ChannelAuthIface => ({\n principalId: args.principal.principalId,\n principalType: args.principal.principalType ?? ChannelPrincipalType.Service,\n authenticator: args.principal.authenticator,\n displayName: args.displayName,\n attributes: args.principal.attributes ?? {},\n});\n\nexport const sendServiceInput = async (\n platform: Pick<ChannelPlatformIface, 'channelSessions'>,\n args: ServiceInputArgsIface,\n): Promise<ChannelInputReceiptIface> => {\n if (platform.channelSessions == null)\n throw new Error('channel session operations are unavailable');\n return platform.channelSessions.send(\n { conversationId: args.room, participantId: args.participantId },\n {\n content: args.content,\n idempotencyKey: args.idempotencyKey,\n auth: serviceAuthOf(args),\n actingUserId: args.actingUserId,\n originTurnId: args.originTurnId,\n source: { label: args.label },\n context: args.context,\n turnPolicy: args.turnPolicy,\n },\n );\n};\n"],
|
|
5
|
-
"mappings": "AACA;AAAA,EACE;AAAA,OAMK;AAkCA,MAAM,gBAAgB,CAAC,UAAmD;AAAA,EAC/E,aAAa,KAAK,UAAU;AAAA,EAC5B,eAAe,KAAK,UAAU,iBAAiB,qBAAqB;AAAA,EACpE,eAAe,KAAK,UAAU;AAAA,EAC9B,aAAa,KAAK;AAAA,EAClB,YAAY,KAAK,UAAU,cAAc,CAAC;AAC5C;AAEO,MAAM,mBAAmB,OAC9B,UACA,SACsC;AACtC,MAAI,SAAS,mBAAmB;AAC9B,UAAM,IAAI,MAAM,4CAA4C;AAC9D,SAAO,SAAS,gBAAgB;AAAA,IAC9B,EAAE,gBAAgB,KAAK,MAAM,eAAe,KAAK,cAAc;AAAA,IAC/D;AAAA,MACE,SAAS,KAAK;AAAA,MACd,gBAAgB,KAAK;AAAA,MACrB,MAAM,cAAc,IAAI;AAAA,MACxB,cAAc,KAAK;AAAA,MACnB,cAAc,KAAK;AAAA,MACnB,QAAQ,EAAE,OAAO,KAAK,MAAM;AAAA,MAC5B,SAAS,KAAK;AAAA,MACd,YAAY,KAAK;AAAA,IACnB;AAAA,EACF;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|