@andreprado/agentkit 0.1.0-alpha.2 → 0.1.0-alpha.20
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/README.md +67 -6
- package/docs/guides/add-channel.md +118 -7
- package/docs/guides/add-knowledge.md +144 -0
- package/docs/guides/add-managed-composio.md +163 -0
- package/docs/guides/add-tool.md +1 -1
- package/docs/guides/channel-security.md +97 -32
- package/docs/guides/connect-discord.md +178 -0
- package/docs/guides/connect-slack.md +126 -0
- package/docs/guides/connect-telegram.md +78 -1
- package/docs/guides/connect-whatsapp-zapster.md +112 -8
- package/docs/guides/create-agent.md +45 -4
- package/docs/guides/debug-channel.md +147 -0
- package/docs/guides/improve-from-production.md +151 -0
- package/docs/guides/prepare-deploy.md +47 -17
- package/docs/guides/replay-production-traces.md +72 -0
- package/docs/guides/run-evals.md +147 -20
- package/docs/guides/security-rules.md +7 -6
- package/docs/guides/send-feedback.md +135 -0
- package/docs/guides/use-provider.md +27 -3
- package/docs/llms-full.txt +303 -55
- package/docs/llms.txt +57 -7
- package/package.json +2 -5
- package/src/cli/args.ts +57 -0
- package/src/cli/cloud-client.ts +377 -0
- package/src/cli/commands/channels.ts +1315 -0
- package/src/cli/commands/feedback.ts +438 -0
- package/src/cli/commands/knowledge.ts +136 -0
- package/src/cli/commands/transcribe.ts +171 -0
- package/src/cli/constants.ts +4 -0
- package/src/cli/deploy-chat-ui.ts +535 -0
- package/src/cli/deploy-readiness.ts +481 -0
- package/src/cli/flags.ts +162 -0
- package/src/cli/help.ts +236 -0
- package/src/cli/index.ts +1167 -1005
- package/src/cli/process.ts +31 -0
- package/src/cloud/artifact.ts +139 -0
- package/src/cloud/client.ts +80 -0
- package/src/cloud/contracts.ts +63 -0
- package/src/cloud/index.ts +3 -0
- package/src/create-project.ts +21 -6
- package/src/index.ts +479 -7
- package/src/providers/pi.ts +70 -16
- package/src/providers/test.ts +88 -1
- package/src/providers/types.ts +7 -0
- package/src/runtime/channel-buffer.ts +30 -0
- package/src/runtime/channel-test-harness.ts +8 -1
- package/src/runtime/channels/discord.ts +896 -0
- package/src/runtime/channels/slack.ts +646 -0
- package/src/runtime/channels/telegram.ts +466 -23
- package/src/runtime/channels/whatsapp-meta.ts +9 -0
- package/src/runtime/channels/whatsapp-zapster.ts +677 -40
- package/src/runtime/channels.ts +86 -3
- package/src/runtime/chat.ts +130 -38
- package/src/runtime/config.ts +483 -18
- package/src/runtime/core/manifest.ts +103 -5
- package/src/runtime/core/targets.ts +5 -5
- package/src/runtime/database.ts +93 -2
- package/src/runtime/db-commands.ts +9 -0
- package/src/runtime/deploy-readiness.ts +46 -4
- package/src/runtime/deploy.ts +1 -1
- package/src/runtime/dev-server.ts +759 -41
- package/src/runtime/env.ts +8 -3
- package/src/runtime/evals.ts +589 -43
- package/src/runtime/improve.ts +868 -0
- package/src/runtime/inspect.ts +194 -4
- package/src/runtime/integrations/composio.ts +423 -0
- package/src/runtime/knowledge/chunk.ts +333 -0
- package/src/runtime/knowledge/config.ts +135 -0
- package/src/runtime/knowledge/embeddings.ts +133 -0
- package/src/runtime/knowledge/ingest.ts +521 -0
- package/src/runtime/knowledge/prompt-policy.ts +30 -0
- package/src/runtime/knowledge/retrieve.ts +303 -0
- package/src/runtime/knowledge/schema.ts +100 -0
- package/src/runtime/knowledge/tool.ts +64 -0
- package/src/runtime/knowledge/vector.ts +258 -0
- package/src/runtime/prompt-context.ts +141 -0
- package/src/runtime/runtime-contract.ts +86 -8
- package/src/runtime/skills.ts +95 -0
- package/src/runtime/spec.ts +152 -0
- package/src/runtime/sync.ts +144 -0
- package/src/runtime/targets/cloudflare/build.ts +1430 -185
- package/src/runtime/targets/container/server.ts +1 -1
- package/src/runtime/targets/vps/deploy.ts +26 -9
- package/src/runtime/tool-runner.ts +9 -1
- package/src/runtime/tools.ts +128 -2
- package/src/runtime/traces.ts +41 -0
- package/src/runtime/transcription.ts +483 -0
- package/src/storage/sqlite.ts +149 -3
- package/src/templates/blank.ts +76 -17
- package/src/templates/dentista.ts +1011 -0
- package/src/templates/index.ts +2 -0
- package/src/templates/skills/agentkit-build-agent/SKILL.md +52 -0
- package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +21 -0
- package/src/templates/skills/agentkit-build-agent/templates/sales-qualifier.instructions.md +17 -0
- package/src/templates/skills/agentkit-build-agent/templates/support-agent.instructions.md +16 -0
- package/src/templates/skills/agentkit-capsule/SKILL.md +70 -0
- package/src/templates/skills/agentkit-capsule/references/docs-router.md +15 -0
- package/src/templates/skills/agentkit-channels/SKILL.md +104 -0
- package/src/templates/skills/agentkit-channels/references/channel-buffering.md +65 -0
- package/src/templates/skills/agentkit-channels/references/channel-debugging.md +66 -0
- package/src/templates/skills/agentkit-channels/references/discord.md +93 -0
- package/src/templates/skills/agentkit-channels/references/slack.md +56 -0
- package/src/templates/skills/agentkit-channels/references/telegram.md +72 -0
- package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +77 -0
- package/src/templates/skills/agentkit-database/SKILL.md +45 -0
- package/src/templates/skills/agentkit-database/templates/appointments.schema.sql +15 -0
- package/src/templates/skills/agentkit-database/templates/leads.schema.sql +17 -0
- package/src/templates/skills/agentkit-deploy/SKILL.md +50 -0
- package/src/templates/skills/agentkit-evals/SKILL.md +109 -0
- package/src/templates/skills/agentkit-evals/templates/multi-turn.eval.md +29 -0
- package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +18 -0
- package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +18 -0
- package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +27 -0
- package/src/templates/skills/agentkit-improve/SKILL.md +86 -0
- package/src/templates/skills/agentkit-improve/references/replay-side-effects.md +18 -0
- package/src/templates/skills/agentkit-improve/references/trace-packets.md +22 -0
- package/src/templates/skills/agentkit-improve/templates/regression.eval.md +18 -0
- package/src/templates/skills/agentkit-integrations/SKILL.md +76 -0
- package/src/templates/skills/agentkit-knowledge/SKILL.md +43 -0
- package/src/templates/skills/agentkit-knowledge/templates/faq.md +14 -0
- package/src/templates/skills/agentkit-knowledge/templates/policies.md +14 -0
- package/src/templates/skills/agentkit-knowledge/templates/prices.csv +3 -0
- package/src/templates/skills/agentkit-prompts/SKILL.md +47 -0
- package/src/templates/skills/agentkit-prompts/templates/knowledge-grounded-faq.instructions.md +11 -0
- package/src/templates/skills/agentkit-provider/SKILL.md +60 -0
- package/src/templates/skills/agentkit-security/SKILL.md +56 -0
- package/src/templates/skills/agentkit-tools/SKILL.md +37 -0
- package/src/templates/skills/agentkit-tools/examples/database-write.tool.md +35 -0
- package/src/templates/skills/agentkit-tools/examples/eval-safe-external-action.tool.md +37 -0
- package/src/templates/skills/agentkit-tools/examples/lookup-order.tool.md +46 -0
- package/src/templates/skills/agentkit-troubleshooting/SKILL.md +76 -0
- package/src/templates/support.ts +77 -18
- package/docs/guides/channels-production-handoff.md +0 -99
- package/docs/portable-deploy-release-checklist.md +0 -41
- package/src/runtime/targets/cloudflare/deploy.ts +0 -5475
package/src/runtime/config.ts
CHANGED
|
@@ -13,21 +13,38 @@ import type {
|
|
|
13
13
|
AgentChannel,
|
|
14
14
|
ChannelProvider,
|
|
15
15
|
ChannelType,
|
|
16
|
+
DiscordChannelMode,
|
|
16
17
|
TelegramAllowedUpdate,
|
|
18
|
+
AgentKnowledgeEmbeddingProvider,
|
|
19
|
+
ChannelBufferMode,
|
|
20
|
+
AgentIntegration,
|
|
21
|
+
ChannelAudioMode,
|
|
22
|
+
TranscriptionProviderName,
|
|
17
23
|
} from "../index";
|
|
24
|
+
import { MANAGED_COMPOSIO_TOOL_NAME } from "./integrations/composio";
|
|
18
25
|
import { AgentKitError } from "./errors";
|
|
26
|
+
import { isValidTimeZone } from "./prompt-context";
|
|
19
27
|
|
|
20
28
|
const CONFIG_FILE = "agentkit.config.ts";
|
|
21
29
|
const AGENT_RUNTIMES = new Set<AgentRuntime>(["local", "edge"]);
|
|
22
30
|
const PROVIDERS = new Set<AgentProviderName>(["test", "openai", "anthropic", "openrouter", "custom"]);
|
|
23
31
|
const ACCESS_MODES = new Set<AccessMode>(["private", "token", "public"]);
|
|
24
32
|
const TOOL_VISIBILITIES = new Set<ToolVisibility>(["user", "internal"]);
|
|
25
|
-
const CHANNEL_TYPES = new Set<ChannelType>(["website", "telegram", "whatsapp"]);
|
|
26
|
-
const CHANNEL_PROVIDERS = new Set<ChannelProvider>(["agentkit", "telegram", "zapster", "meta"]);
|
|
33
|
+
const CHANNEL_TYPES = new Set<ChannelType>(["website", "telegram", "whatsapp", "discord", "slack"]);
|
|
34
|
+
const CHANNEL_PROVIDERS = new Set<ChannelProvider>(["agentkit", "telegram", "zapster", "meta", "discord", "slack"]);
|
|
35
|
+
const CHANNEL_BUFFER_MODES = new Set<ChannelBufferMode>(["off", "debounce"]);
|
|
36
|
+
const CHANNEL_AUDIO_MODES = new Set<ChannelAudioMode>(["off", "reject", "transcribe"]);
|
|
37
|
+
const DISCORD_CHANNEL_MODES = new Set<DiscordChannelMode>(["interactions", "bot"]);
|
|
27
38
|
const TELEGRAM_ALLOWED_UPDATES = new Set<TelegramAllowedUpdate>(["message"]);
|
|
39
|
+
const KNOWLEDGE_EMBEDDING_PROVIDERS = new Set<AgentKnowledgeEmbeddingProvider>(["none", "test", "openai"]);
|
|
40
|
+
const TRANSCRIPTION_PROVIDERS = new Set<TranscriptionProviderName>(["test", "openai", "groq"]);
|
|
28
41
|
const CHANNEL_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
42
|
+
const INTEGRATION_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
43
|
+
const COMPOSIO_TOOLKIT_PATTERN = /^[a-z][a-z0-9_-]*$/;
|
|
44
|
+
const COMPOSIO_TOOL_SLUG_PATTERN = /^[A-Z][A-Z0-9_]*$/;
|
|
29
45
|
const SECRET_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/;
|
|
30
46
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
47
|
+
let configImportNonce = 0;
|
|
31
48
|
|
|
32
49
|
export type LoadedAgentCapsule = {
|
|
33
50
|
root: string;
|
|
@@ -104,15 +121,13 @@ function resolveLocalStoragePath(root: string, config: AgentConfig): string | un
|
|
|
104
121
|
}
|
|
105
122
|
|
|
106
123
|
async function loadConfigModule(configPath: string): Promise<unknown> {
|
|
107
|
-
const
|
|
108
|
-
configUrl.searchParams.set("t", String(Date.now()));
|
|
124
|
+
const source = await readFile(configPath, "utf8");
|
|
109
125
|
|
|
110
126
|
try {
|
|
111
|
-
|
|
112
|
-
return module.default;
|
|
127
|
+
return await importConfigSource(configPath, source);
|
|
113
128
|
} catch (error) {
|
|
114
129
|
if (isMissingAgentKitSelfImport(error)) {
|
|
115
|
-
return loadConfigModuleWithLocalSelfImport(configPath, error);
|
|
130
|
+
return loadConfigModuleWithLocalSelfImport(configPath, source, error);
|
|
116
131
|
}
|
|
117
132
|
|
|
118
133
|
throw new AgentKitError(
|
|
@@ -123,8 +138,11 @@ async function loadConfigModule(configPath: string): Promise<unknown> {
|
|
|
123
138
|
}
|
|
124
139
|
}
|
|
125
140
|
|
|
126
|
-
async function loadConfigModuleWithLocalSelfImport(
|
|
127
|
-
|
|
141
|
+
async function loadConfigModuleWithLocalSelfImport(
|
|
142
|
+
configPath: string,
|
|
143
|
+
source: string,
|
|
144
|
+
originalError: unknown,
|
|
145
|
+
): Promise<unknown> {
|
|
128
146
|
const localAgentKitUrl = pathToFileURL(resolve(moduleDir, "../index.ts")).href;
|
|
129
147
|
const patchedSource = source.replace(
|
|
130
148
|
/from\s+["']@andreprado\/agentkit["']/g,
|
|
@@ -139,15 +157,20 @@ async function loadConfigModuleWithLocalSelfImport(configPath: string, originalE
|
|
|
139
157
|
);
|
|
140
158
|
}
|
|
141
159
|
|
|
160
|
+
return importConfigSource(configPath, patchedSource);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function importConfigSource(configPath: string, source: string): Promise<unknown> {
|
|
164
|
+
const nonce = nextConfigImportNonce();
|
|
142
165
|
const tempConfigPath = join(
|
|
143
166
|
dirname(configPath),
|
|
144
|
-
`.agentkit.config.loader-${process.pid}-${
|
|
167
|
+
`.agentkit.config.loader-${process.pid}-${nonce}-${Math.random().toString(16).slice(2)}.ts`,
|
|
145
168
|
);
|
|
146
169
|
|
|
147
170
|
try {
|
|
148
|
-
await writeFile(tempConfigPath,
|
|
171
|
+
await writeFile(tempConfigPath, source);
|
|
149
172
|
const configUrl = pathToFileURL(tempConfigPath);
|
|
150
|
-
configUrl.searchParams.set("t",
|
|
173
|
+
configUrl.searchParams.set("t", nonce);
|
|
151
174
|
const module = (await import(configUrl.href)) as { default?: unknown };
|
|
152
175
|
return module.default;
|
|
153
176
|
} catch (error) {
|
|
@@ -161,6 +184,11 @@ async function loadConfigModuleWithLocalSelfImport(configPath: string, originalE
|
|
|
161
184
|
}
|
|
162
185
|
}
|
|
163
186
|
|
|
187
|
+
function nextConfigImportNonce(): string {
|
|
188
|
+
configImportNonce += 1;
|
|
189
|
+
return `${Date.now()}-${configImportNonce}`;
|
|
190
|
+
}
|
|
191
|
+
|
|
164
192
|
function isMissingAgentKitSelfImport(error: unknown): boolean {
|
|
165
193
|
const message = error instanceof Error ? error.message : String(error);
|
|
166
194
|
return message.includes("@andreprado/agentkit");
|
|
@@ -191,8 +219,17 @@ function validateAgentConfig(value: unknown): asserts value is AgentConfig {
|
|
|
191
219
|
throw invalidConfig('provider.model must be "fake" when provider.name is "test".');
|
|
192
220
|
}
|
|
193
221
|
|
|
222
|
+
if (value.timeZone !== undefined) {
|
|
223
|
+
expectString(value.timeZone, "timeZone");
|
|
224
|
+
|
|
225
|
+
if (!isValidTimeZone(value.timeZone)) {
|
|
226
|
+
throw invalidConfig('timeZone must be a valid IANA time zone such as "UTC" or "America/New_York".');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
194
230
|
expectString(value.instructions, "instructions");
|
|
195
231
|
expectStringArray(value.secrets, "secrets");
|
|
232
|
+
validateSecretNames(value.secrets, "secrets");
|
|
196
233
|
|
|
197
234
|
if (value.tools !== undefined) {
|
|
198
235
|
if (!Array.isArray(value.tools)) {
|
|
@@ -204,7 +241,10 @@ function validateAgentConfig(value: unknown): asserts value is AgentConfig {
|
|
|
204
241
|
}
|
|
205
242
|
}
|
|
206
243
|
|
|
207
|
-
|
|
244
|
+
validateIntegrations(value.integrations, value.runtime as AgentRuntime, value.tools);
|
|
245
|
+
validateTranscriptionConfig(value.transcription);
|
|
246
|
+
validateChannels(value.channels, value.runtime as AgentRuntime, Boolean(value.transcription));
|
|
247
|
+
validateKnowledgeConfig(value.knowledge);
|
|
208
248
|
|
|
209
249
|
if (!isRecord(value.access) || !ACCESS_MODES.has(value.access.mode as AccessMode)) {
|
|
210
250
|
throw invalidConfig('access.mode must be "private", "token", or "public".');
|
|
@@ -222,11 +262,310 @@ function validateAgentConfig(value: unknown): asserts value is AgentConfig {
|
|
|
222
262
|
throw invalidConfig("storage.path must be a string when provided.");
|
|
223
263
|
}
|
|
224
264
|
|
|
265
|
+
if (value.storage.driver === "sqlite" && typeof value.storage.path !== "string") {
|
|
266
|
+
throw invalidConfig('storage.path is required when storage.driver is "sqlite".');
|
|
267
|
+
}
|
|
268
|
+
|
|
225
269
|
validateDatabaseConfig(value.storage.database);
|
|
226
270
|
validateFilesConfig(value.storage.files);
|
|
227
271
|
}
|
|
228
272
|
|
|
229
|
-
function
|
|
273
|
+
function validateIntegrations(
|
|
274
|
+
value: unknown,
|
|
275
|
+
runtime: AgentRuntime,
|
|
276
|
+
tools: AgentConfig["tools"] = [],
|
|
277
|
+
): void {
|
|
278
|
+
if (value === undefined) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (!Array.isArray(value)) {
|
|
283
|
+
throw invalidConfig("integrations must be an array when provided.");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (value.length > 0 && runtime !== "edge") {
|
|
287
|
+
throw invalidConfig('managed integrations require runtime: "edge". Use BYO defineTool wrappers for local-only integrations.');
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
let composioCount = 0;
|
|
291
|
+
|
|
292
|
+
for (const [index, integration] of value.entries()) {
|
|
293
|
+
if (!isRecord(integration)) {
|
|
294
|
+
throw invalidConfig(`integrations[${index}] must be an object.`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (integration.provider !== "composio" || integration.type !== "composio" || integration.mode !== "managed") {
|
|
298
|
+
throw invalidConfig(`integrations[${index}] must be composioManaged({...}) or { type: "composio", provider: "composio", mode: "managed", ... }.`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
composioCount += 1;
|
|
302
|
+
validateComposioManagedIntegration(integration as unknown as AgentIntegration, index);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (composioCount > 1) {
|
|
306
|
+
throw invalidConfig("Only one managed Composio integration is supported per agent.");
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const hasExplicitComposioTools = value.some(
|
|
310
|
+
(integration) => isRecord(integration) && integration.provider === "composio" && isRecord(integration.tools),
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
if (hasExplicitComposioTools && tools.some((tool) => isRecord(tool) && tool.name === MANAGED_COMPOSIO_TOOL_NAME)) {
|
|
314
|
+
throw invalidConfig(`tools must not declare reserved managed Composio tool name "${MANAGED_COMPOSIO_TOOL_NAME}".`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function validateComposioManagedIntegration(value: AgentIntegration, index: number): void {
|
|
319
|
+
expectString(value.name, `integrations[${index}].name`);
|
|
320
|
+
|
|
321
|
+
if (!INTEGRATION_NAME_PATTERN.test(value.name)) {
|
|
322
|
+
throw invalidConfig(`integrations[${index}].name must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens.`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (!Array.isArray(value.toolkits) || value.toolkits.length === 0) {
|
|
326
|
+
throw invalidConfig(`integrations[${index}].toolkits must include at least one Composio toolkit slug.`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const toolkits = new Set<string>();
|
|
330
|
+
|
|
331
|
+
for (const [toolkitIndex, toolkit] of value.toolkits.entries()) {
|
|
332
|
+
expectString(toolkit, `integrations[${index}].toolkits[${toolkitIndex}]`);
|
|
333
|
+
|
|
334
|
+
if (!COMPOSIO_TOOLKIT_PATTERN.test(toolkit)) {
|
|
335
|
+
throw invalidConfig(`integrations[${index}].toolkits[${toolkitIndex}] must be a lowercase Composio toolkit slug.`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (toolkits.has(toolkit)) {
|
|
339
|
+
throw invalidConfig(`integrations[${index}].toolkits must not contain duplicates.`);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
toolkits.add(toolkit);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (value.tools !== undefined) {
|
|
346
|
+
if (!isRecord(value.tools)) {
|
|
347
|
+
throw invalidConfig(`integrations[${index}].tools must be an object when provided.`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
for (const [toolkit, tools] of Object.entries(value.tools)) {
|
|
351
|
+
if (!toolkits.has(toolkit)) {
|
|
352
|
+
throw invalidConfig(`integrations[${index}].tools.${toolkit} must reference a toolkit listed in integrations[${index}].toolkits.`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (!Array.isArray(tools) || tools.length === 0) {
|
|
356
|
+
throw invalidConfig(`integrations[${index}].tools.${toolkit} must include at least one Composio action slug.`);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const seenTools = new Set<string>();
|
|
360
|
+
|
|
361
|
+
for (const [toolIndex, tool] of tools.entries()) {
|
|
362
|
+
expectString(tool, `integrations[${index}].tools.${toolkit}[${toolIndex}]`);
|
|
363
|
+
|
|
364
|
+
if (!COMPOSIO_TOOL_SLUG_PATTERN.test(tool)) {
|
|
365
|
+
throw invalidConfig(`integrations[${index}].tools.${toolkit}[${toolIndex}] must be an uppercase Composio action slug.`);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (seenTools.has(tool)) {
|
|
369
|
+
throw invalidConfig(`integrations[${index}].tools.${toolkit} must not contain duplicates.`);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
seenTools.add(tool);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (value.tags !== undefined) {
|
|
378
|
+
validateIntegrationTags(value.tags, `integrations[${index}].tags`);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (value.confirmExternalWrites !== undefined && typeof value.confirmExternalWrites !== "boolean") {
|
|
382
|
+
throw invalidConfig(`integrations[${index}].confirmExternalWrites must be a boolean when provided.`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function validateIntegrationTags(value: unknown, label: string): void {
|
|
387
|
+
if (!isRecord(value)) {
|
|
388
|
+
throw invalidConfig(`${label} must be an object when provided.`);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (value.enable !== undefined) {
|
|
392
|
+
expectStringArray(value.enable, `${label}.enable`);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (value.disable !== undefined) {
|
|
396
|
+
expectStringArray(value.disable, `${label}.disable`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function validateTranscriptionConfig(value: unknown): void {
|
|
401
|
+
if (value === undefined) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (!isRecord(value)) {
|
|
406
|
+
throw invalidConfig("transcription must be an object when provided.");
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (!TRANSCRIPTION_PROVIDERS.has(value.provider as TranscriptionProviderName)) {
|
|
410
|
+
throw invalidConfig(`transcription.provider must be one of: ${Array.from(TRANSCRIPTION_PROVIDERS).join(", ")}.`);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
expectString(value.model, "transcription.model");
|
|
414
|
+
|
|
415
|
+
if (value.secret !== undefined) {
|
|
416
|
+
expectString(value.secret, "transcription.secret");
|
|
417
|
+
validateSecretNames([value.secret], "transcription.secret");
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (value.language !== undefined) {
|
|
421
|
+
expectString(value.language, "transcription.language");
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (value.prompt !== undefined) {
|
|
425
|
+
expectString(value.prompt, "transcription.prompt");
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (value.limits !== undefined) {
|
|
429
|
+
if (!isRecord(value.limits)) {
|
|
430
|
+
throw invalidConfig("transcription.limits must be an object when provided.");
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
validateOptionalPositiveInteger(value.limits.maxDurationSeconds, "transcription.limits.maxDurationSeconds");
|
|
434
|
+
validateOptionalPositiveInteger(value.limits.maxBytes, "transcription.limits.maxBytes");
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
validateOptionalPositiveInteger(value.rawAudioTtlSeconds, "transcription.rawAudioTtlSeconds");
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function validateKnowledgeConfig(value: unknown): void {
|
|
441
|
+
if (value === undefined) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (!isRecord(value)) {
|
|
446
|
+
throw invalidConfig("knowledge must be an object when provided.");
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (value.sources !== undefined) {
|
|
450
|
+
if (!Array.isArray(value.sources)) {
|
|
451
|
+
throw invalidConfig("knowledge.sources must be an array when provided.");
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
for (const [index, source] of value.sources.entries()) {
|
|
455
|
+
validateKnowledgeSource(source, index);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (value.embedding !== undefined) {
|
|
460
|
+
validateKnowledgeEmbedding(value.embedding);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (value.retrieval !== undefined) {
|
|
464
|
+
validateKnowledgeRetrieval(value.retrieval);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function validateKnowledgeSource(value: unknown, index: number): void {
|
|
469
|
+
if (typeof value === "string") {
|
|
470
|
+
expectString(value, `knowledge.sources[${index}]`);
|
|
471
|
+
validateKnowledgeSourceReference(value, `knowledge.sources[${index}]`);
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (!isRecord(value)) {
|
|
476
|
+
throw invalidConfig(`knowledge.sources[${index}] must be a path string or an object with path or url.`);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const hasPath = "path" in value;
|
|
480
|
+
const hasUrl = "url" in value;
|
|
481
|
+
|
|
482
|
+
if (hasPath === hasUrl) {
|
|
483
|
+
throw invalidConfig(`knowledge.sources[${index}] must set exactly one of path or url.`);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (hasPath) {
|
|
487
|
+
expectString(value.path, `knowledge.sources[${index}].path`);
|
|
488
|
+
validateKnowledgeSourceReference(value.path, `knowledge.sources[${index}].path`);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (hasUrl) {
|
|
492
|
+
expectString(value.url, `knowledge.sources[${index}].url`);
|
|
493
|
+
validateKnowledgeUrl(value.url, `knowledge.sources[${index}].url`);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (value.title !== undefined) {
|
|
497
|
+
expectString(value.title, `knowledge.sources[${index}].title`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function validateKnowledgeSourceReference(value: string, label: string): void {
|
|
502
|
+
if (value.includes("\0")) {
|
|
503
|
+
throw invalidConfig(`${label} must not contain null bytes.`);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (/^https?:\/\//i.test(value)) {
|
|
507
|
+
validateKnowledgeUrl(value, label);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function validateKnowledgeUrl(value: string, label: string): void {
|
|
512
|
+
let url: URL;
|
|
513
|
+
|
|
514
|
+
try {
|
|
515
|
+
url = new URL(value);
|
|
516
|
+
} catch {
|
|
517
|
+
throw invalidConfig(`${label} must be a valid http or https URL.`);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
521
|
+
throw invalidConfig(`${label} must be a valid http or https URL.`);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function validateKnowledgeEmbedding(value: unknown): void {
|
|
526
|
+
if (!isRecord(value)) {
|
|
527
|
+
throw invalidConfig("knowledge.embedding must be an object when provided.");
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (value.provider !== undefined && !KNOWLEDGE_EMBEDDING_PROVIDERS.has(value.provider as AgentKnowledgeEmbeddingProvider)) {
|
|
531
|
+
throw invalidConfig(
|
|
532
|
+
`knowledge.embedding.provider must be one of: ${Array.from(KNOWLEDGE_EMBEDDING_PROVIDERS).join(", ")}.`,
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (value.model !== undefined) {
|
|
537
|
+
expectString(value.model, "knowledge.embedding.model");
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (value.secret !== undefined) {
|
|
541
|
+
expectString(value.secret, "knowledge.embedding.secret");
|
|
542
|
+
validateSecretNames([value.secret], "knowledge.embedding.secret");
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const dimensions = value.dimensions;
|
|
546
|
+
if (dimensions !== undefined) {
|
|
547
|
+
if (typeof dimensions !== "number" || !Number.isInteger(dimensions) || dimensions <= 0 || dimensions > 65_536) {
|
|
548
|
+
throw invalidConfig("knowledge.embedding.dimensions must be a positive integer no larger than 65536.");
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function validateKnowledgeRetrieval(value: unknown): void {
|
|
554
|
+
if (!isRecord(value)) {
|
|
555
|
+
throw invalidConfig("knowledge.retrieval must be an object when provided.");
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const topK = value.topK;
|
|
559
|
+
if (topK !== undefined && (typeof topK !== "number" || !Number.isInteger(topK) || topK <= 0 || topK > 50)) {
|
|
560
|
+
throw invalidConfig("knowledge.retrieval.topK must be a positive integer no larger than 50.");
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (value.hybrid !== undefined && typeof value.hybrid !== "boolean") {
|
|
564
|
+
throw invalidConfig("knowledge.retrieval.hybrid must be a boolean when provided.");
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function validateChannels(value: unknown, runtime: AgentRuntime, hasTranscriptionConfig: boolean): void {
|
|
230
569
|
if (value === undefined) {
|
|
231
570
|
return;
|
|
232
571
|
}
|
|
@@ -242,7 +581,7 @@ function validateChannels(value: unknown, runtime: AgentRuntime): void {
|
|
|
242
581
|
const names = new Set<string>();
|
|
243
582
|
|
|
244
583
|
for (const [index, channel] of value.entries()) {
|
|
245
|
-
validateChannel(channel, index);
|
|
584
|
+
validateChannel(channel, index, hasTranscriptionConfig);
|
|
246
585
|
|
|
247
586
|
if (names.has(channel.name)) {
|
|
248
587
|
throw invalidConfig(`channels[${index}].name must be unique within the Agent Capsule.`);
|
|
@@ -252,7 +591,7 @@ function validateChannels(value: unknown, runtime: AgentRuntime): void {
|
|
|
252
591
|
}
|
|
253
592
|
}
|
|
254
593
|
|
|
255
|
-
function validateChannel(value: unknown, index: number): asserts value is AgentChannel {
|
|
594
|
+
function validateChannel(value: unknown, index: number, hasTranscriptionConfig: boolean): asserts value is AgentChannel {
|
|
256
595
|
if (!isRecord(value)) {
|
|
257
596
|
throw invalidConfig(`channels[${index}] must be an object.`);
|
|
258
597
|
}
|
|
@@ -276,6 +615,19 @@ function validateChannel(value: unknown, index: number): asserts value is AgentC
|
|
|
276
615
|
expectStringArray(value.secrets, `channels[${index}].secrets`);
|
|
277
616
|
validateSecretNames(value.secrets, `channels[${index}].secrets`);
|
|
278
617
|
validateChannelLimits(value.limits, index);
|
|
618
|
+
validateChannelBuffer(value.buffer, index);
|
|
619
|
+
|
|
620
|
+
if (value.type === "discord") {
|
|
621
|
+
validateDiscordChannel(value, index);
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if (value.type === "slack") {
|
|
626
|
+
validateSlackChannel(value, index);
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
validateChannelAudio(value.audio, index, hasTranscriptionConfig);
|
|
279
631
|
|
|
280
632
|
if (value.type === "website") {
|
|
281
633
|
validateWebsiteChannel(value, index);
|
|
@@ -290,6 +642,31 @@ function validateChannel(value: unknown, index: number): asserts value is AgentC
|
|
|
290
642
|
validateWhatsappChannel(value, index);
|
|
291
643
|
}
|
|
292
644
|
|
|
645
|
+
function validateChannelAudio(value: unknown, index: number, hasTranscriptionConfig: boolean): void {
|
|
646
|
+
if (value === undefined) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
if (!isRecord(value)) {
|
|
651
|
+
throw invalidConfig(`channels[${index}].audio must be an object when provided.`);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
if (!CHANNEL_AUDIO_MODES.has(value.mode as ChannelAudioMode)) {
|
|
655
|
+
throw invalidConfig(`channels[${index}].audio.mode must be "off", "reject", or "transcribe".`);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (value.mode === "transcribe" && !hasTranscriptionConfig) {
|
|
659
|
+
throw invalidConfig(`channels[${index}].audio.mode "transcribe" requires top-level transcription config.`);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (value.mode !== "off") {
|
|
663
|
+
expectString(value.fallbackMessage, `channels[${index}].audio.fallbackMessage`);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
validateOptionalPositiveInteger(value.maxDurationSeconds, `channels[${index}].audio.maxDurationSeconds`);
|
|
667
|
+
validateOptionalPositiveInteger(value.maxBytes, `channels[${index}].audio.maxBytes`);
|
|
668
|
+
}
|
|
669
|
+
|
|
293
670
|
function validateWebsiteChannel(value: Record<string, unknown>, index: number): void {
|
|
294
671
|
if (value.provider !== "agentkit") {
|
|
295
672
|
throw invalidConfig(`channels[${index}].provider must be "agentkit" when type is "website".`);
|
|
@@ -322,11 +699,15 @@ function validateTelegramChannel(value: Record<string, unknown>, index: number):
|
|
|
322
699
|
|
|
323
700
|
function validateWhatsappChannel(value: Record<string, unknown>, index: number): void {
|
|
324
701
|
if (value.type !== "whatsapp") {
|
|
325
|
-
throw invalidConfig(`channels[${index}].type must be "website", "telegram", or "
|
|
702
|
+
throw invalidConfig(`channels[${index}].type must be "website", "telegram", "whatsapp", "discord", or "slack".`);
|
|
326
703
|
}
|
|
327
704
|
|
|
328
705
|
if (value.provider === "zapster") {
|
|
329
|
-
expectRequiredSecrets(
|
|
706
|
+
expectRequiredSecrets(
|
|
707
|
+
value.secrets,
|
|
708
|
+
["ZAPSTER_API_KEY", "ZAPSTER_INSTANCE_ID", "ZAPSTER_WEBHOOK_ID"],
|
|
709
|
+
`channels[${index}]`,
|
|
710
|
+
);
|
|
330
711
|
return;
|
|
331
712
|
}
|
|
332
713
|
|
|
@@ -342,6 +723,38 @@ function validateWhatsappChannel(value: Record<string, unknown>, index: number):
|
|
|
342
723
|
throw invalidConfig(`channels[${index}].provider must be "zapster" or "meta" when type is "whatsapp".`);
|
|
343
724
|
}
|
|
344
725
|
|
|
726
|
+
function validateDiscordChannel(value: Record<string, unknown>, index: number): void {
|
|
727
|
+
if (value.provider !== "discord") {
|
|
728
|
+
throw invalidConfig(`channels[${index}].provider must be "discord" when type is "discord".`);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
if (value.mode !== undefined && !DISCORD_CHANNEL_MODES.has(value.mode as DiscordChannelMode)) {
|
|
732
|
+
throw invalidConfig(`channels[${index}].mode must be "interactions" or "bot" when type is "discord".`);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
if (value.audio !== undefined) {
|
|
736
|
+
throw invalidConfig(`channels[${index}].audio is not supported when type is "discord".`);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
expectRequiredSecrets(
|
|
740
|
+
value.secrets,
|
|
741
|
+
value.mode === "bot" ? ["DISCORD_BOT_TOKEN"] : ["DISCORD_PUBLIC_KEY"],
|
|
742
|
+
`channels[${index}]`,
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
function validateSlackChannel(value: Record<string, unknown>, index: number): void {
|
|
747
|
+
if (value.provider !== "slack") {
|
|
748
|
+
throw invalidConfig(`channels[${index}].provider must be "slack" when type is "slack".`);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (value.audio !== undefined) {
|
|
752
|
+
throw invalidConfig(`channels[${index}].audio is not supported when type is "slack".`);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
expectRequiredSecrets(value.secrets, ["SLACK_BOT_TOKEN", "SLACK_SIGNING_SECRET"], `channels[${index}]`);
|
|
756
|
+
}
|
|
757
|
+
|
|
345
758
|
function validateChannelLimits(value: unknown, index: number): void {
|
|
346
759
|
if (value === undefined) {
|
|
347
760
|
return;
|
|
@@ -361,6 +774,39 @@ function validateChannelLimits(value: unknown, index: number): void {
|
|
|
361
774
|
}
|
|
362
775
|
}
|
|
363
776
|
|
|
777
|
+
function validateChannelBuffer(value: unknown, index: number): void {
|
|
778
|
+
if (value === undefined) {
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
if (!isRecord(value)) {
|
|
783
|
+
throw invalidConfig(`channels[${index}].buffer must be an object when provided.`);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
if (!CHANNEL_BUFFER_MODES.has(value.mode as ChannelBufferMode)) {
|
|
787
|
+
throw invalidConfig(`channels[${index}].buffer.mode must be "off" or "debounce".`);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
if (value.mode === "off") {
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
for (const field of ["quietWindowMs", "maxWaitMs", "maxMessages", "maxChars"] as const) {
|
|
795
|
+
if (
|
|
796
|
+
value[field] === undefined ||
|
|
797
|
+
typeof value[field] !== "number" ||
|
|
798
|
+
!Number.isInteger(value[field]) ||
|
|
799
|
+
value[field] <= 0
|
|
800
|
+
) {
|
|
801
|
+
throw invalidConfig(`channels[${index}].buffer.${field} must be a positive integer.`);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
if ((value.maxWaitMs as number) < (value.quietWindowMs as number)) {
|
|
806
|
+
throw invalidConfig(`channels[${index}].buffer.maxWaitMs must be greater than or equal to quietWindowMs.`);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
364
810
|
function validateSecretNames(secrets: string[], field: string): void {
|
|
365
811
|
for (const secret of secrets) {
|
|
366
812
|
if (!SECRET_NAME_PATTERN.test(secret)) {
|
|
@@ -369,6 +815,16 @@ function validateSecretNames(secrets: string[], field: string): void {
|
|
|
369
815
|
}
|
|
370
816
|
}
|
|
371
817
|
|
|
818
|
+
function validateOptionalPositiveInteger(value: unknown, field: string): void {
|
|
819
|
+
if (value === undefined) {
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
824
|
+
throw invalidConfig(`${field} must be a positive integer when provided.`);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
372
828
|
function expectRequiredSecrets(value: unknown, requiredSecrets: string[], field: string): void {
|
|
373
829
|
const declaredSecrets = new Set(Array.isArray(value) ? value : []);
|
|
374
830
|
const missingSecrets = requiredSecrets.filter((secret) => !declaredSecrets.has(secret));
|
|
@@ -394,6 +850,10 @@ function validateDatabaseConfig(value: unknown): void {
|
|
|
394
850
|
if (value.schema !== undefined && typeof value.schema !== "string") {
|
|
395
851
|
throw invalidConfig("storage.database.schema must be a string when provided.");
|
|
396
852
|
}
|
|
853
|
+
|
|
854
|
+
if (value.migrations !== undefined && typeof value.migrations !== "string") {
|
|
855
|
+
throw invalidConfig("storage.database.migrations must be a string when provided.");
|
|
856
|
+
}
|
|
397
857
|
}
|
|
398
858
|
|
|
399
859
|
function validateFilesConfig(value: unknown): void {
|
|
@@ -449,6 +909,7 @@ function validateTool(value: unknown, index: number): asserts value is AgentTool
|
|
|
449
909
|
|
|
450
910
|
if (value.secrets !== undefined) {
|
|
451
911
|
expectStringArray(value.secrets, `tools[${index}].secrets`);
|
|
912
|
+
validateSecretNames(value.secrets, `tools[${index}].secrets`);
|
|
452
913
|
}
|
|
453
914
|
|
|
454
915
|
if (value.permissions !== undefined) {
|
|
@@ -464,6 +925,10 @@ function validateTool(value: unknown, index: number): asserts value is AgentTool
|
|
|
464
925
|
if (typeof value.execute !== "function") {
|
|
465
926
|
throw invalidConfig(`tools[${index}].execute must be a function.`);
|
|
466
927
|
}
|
|
928
|
+
|
|
929
|
+
if (value.render !== undefined && typeof value.render !== "function") {
|
|
930
|
+
throw invalidConfig(`tools[${index}].render must be a function when provided.`);
|
|
931
|
+
}
|
|
467
932
|
}
|
|
468
933
|
|
|
469
934
|
function resolvePath(root: string, path: string): string {
|