@andreprado/agentkit 0.1.0-alpha.5 → 0.1.0-alpha.6
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 +5 -0
- package/docs/guides/add-channel.md +25 -0
- package/docs/guides/add-knowledge.md +134 -0
- package/docs/guides/agentkit-skills-architecture.md +471 -0
- package/docs/guides/channels-production-handoff.md +2 -0
- package/docs/guides/connect-telegram.md +15 -0
- package/docs/guides/connect-whatsapp-zapster.md +16 -0
- package/docs/guides/create-agent.md +1 -1
- package/docs/llms-full.txt +90 -1
- package/docs/llms.txt +9 -2
- package/package.json +2 -1
- package/src/cli/commands/channels.ts +39 -1
- package/src/cli/commands/knowledge.ts +136 -0
- package/src/cli/deploy-readiness.ts +19 -0
- package/src/cli/help.ts +8 -0
- package/src/cli/index.ts +16 -4
- package/src/cloud/artifact.ts +92 -1
- package/src/cloud/contracts.ts +16 -0
- package/src/create-project.ts +38 -6
- package/src/index.ts +98 -0
- package/src/runtime/channel-buffer.ts +30 -0
- package/src/runtime/channels.ts +1 -0
- package/src/runtime/chat.ts +21 -2
- package/src/runtime/config.ts +167 -0
- package/src/runtime/core/manifest.ts +37 -0
- package/src/runtime/deploy-readiness.ts +12 -0
- package/src/runtime/dev-server.ts +159 -10
- package/src/runtime/inspect.ts +34 -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 +283 -0
- package/src/runtime/knowledge/schema.ts +56 -0
- package/src/runtime/knowledge/tool.ts +64 -0
- package/src/runtime/knowledge/vector.ts +258 -0
- package/src/runtime/targets/cloudflare/build.ts +469 -4
- package/src/storage/sqlite.ts +5 -0
- package/src/templates/blank.ts +8 -4
- package/src/templates/dentista.ts +3 -1
- package/src/templates/skills/agentkit-build-agent/SKILL.md +49 -0
- package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +20 -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 +62 -0
- package/src/templates/skills/agentkit-capsule/references/docs-router.md +15 -0
- package/src/templates/skills/agentkit-channels/SKILL.md +62 -0
- package/src/templates/skills/agentkit-channels/references/channel-buffering.md +58 -0
- package/src/templates/skills/agentkit-channels/references/channel-debugging.md +37 -0
- package/src/templates/skills/agentkit-channels/references/telegram.md +37 -0
- package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +37 -0
- package/src/templates/skills/agentkit-database/SKILL.md +42 -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 +44 -0
- package/src/templates/skills/agentkit-evals/SKILL.md +31 -0
- package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +14 -0
- package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +14 -0
- package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +18 -0
- package/src/templates/skills/agentkit-knowledge/SKILL.md +40 -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 +45 -0
- package/src/templates/skills/agentkit-prompts/templates/knowledge-grounded-faq.instructions.md +11 -0
- package/src/templates/skills/agentkit-provider/SKILL.md +57 -0
- package/src/templates/skills/agentkit-security/SKILL.md +55 -0
- package/src/templates/skills/agentkit-tools/SKILL.md +36 -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 +52 -0
- package/src/templates/support.ts +8 -4
package/src/cloud/contracts.ts
CHANGED
|
@@ -16,6 +16,17 @@ export type HostedDeployArtifact = {
|
|
|
16
16
|
source: string;
|
|
17
17
|
sha256: string;
|
|
18
18
|
};
|
|
19
|
+
knowledge_sources?: {
|
|
20
|
+
content_type: "application/json";
|
|
21
|
+
sources: Array<{
|
|
22
|
+
kind: "file";
|
|
23
|
+
source_key: string;
|
|
24
|
+
title: string | null;
|
|
25
|
+
content: string;
|
|
26
|
+
sha256: string;
|
|
27
|
+
}>;
|
|
28
|
+
sha256: string;
|
|
29
|
+
};
|
|
19
30
|
};
|
|
20
31
|
|
|
21
32
|
export type CreateDeployRequest = {
|
|
@@ -37,6 +48,11 @@ export type CreateDeployResponse = {
|
|
|
37
48
|
mode: string;
|
|
38
49
|
private_token?: string;
|
|
39
50
|
};
|
|
51
|
+
knowledge?: {
|
|
52
|
+
enabled: boolean;
|
|
53
|
+
sources: number;
|
|
54
|
+
chunks: number;
|
|
55
|
+
};
|
|
40
56
|
};
|
|
41
57
|
};
|
|
42
58
|
|
package/src/create-project.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
-
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
3
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
2
|
+
import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
|
-
import { getTemplate } from "./templates";
|
|
7
|
+
import { getTemplate, type TemplateFile } from "./templates";
|
|
8
8
|
|
|
9
9
|
const execFileAsync = promisify(execFile);
|
|
10
10
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
@@ -27,9 +27,12 @@ export async function createProject(options: CreateProjectOptions): Promise<Crea
|
|
|
27
27
|
|
|
28
28
|
const template = getTemplate(options.template);
|
|
29
29
|
const projectPath = target.path;
|
|
30
|
-
const files =
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
const files = [
|
|
31
|
+
...template.files(target.name, {
|
|
32
|
+
agentkitDependency: await resolveAgentKitDependency(),
|
|
33
|
+
}),
|
|
34
|
+
...(await loadDefaultSkillFiles()),
|
|
35
|
+
];
|
|
33
36
|
|
|
34
37
|
if (target.currentDirectory) {
|
|
35
38
|
await ensureNoFileCollisions(projectPath, files.map((file) => file.path));
|
|
@@ -126,6 +129,35 @@ async function resolveAgentKitDependency(): Promise<string> {
|
|
|
126
129
|
return packageJson.version;
|
|
127
130
|
}
|
|
128
131
|
|
|
132
|
+
async function loadDefaultSkillFiles(): Promise<TemplateFile[]> {
|
|
133
|
+
const sourceRoot = join(moduleDir, "templates", "skills");
|
|
134
|
+
const sourceFiles = await listFiles(sourceRoot);
|
|
135
|
+
|
|
136
|
+
return Promise.all(
|
|
137
|
+
sourceFiles.map(async (sourcePath) => ({
|
|
138
|
+
path: join("skills", relative(sourceRoot, sourcePath)).replaceAll("\\", "/"),
|
|
139
|
+
contents: await readFile(sourcePath, "utf8"),
|
|
140
|
+
})),
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function listFiles(directory: string): Promise<string[]> {
|
|
145
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
146
|
+
const files = await Promise.all(
|
|
147
|
+
entries.map(async (entry) => {
|
|
148
|
+
const path = join(directory, entry.name);
|
|
149
|
+
|
|
150
|
+
if (entry.isDirectory()) {
|
|
151
|
+
return listFiles(path);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return entry.isFile() ? [path] : [];
|
|
155
|
+
}),
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
return files.flat().sort();
|
|
159
|
+
}
|
|
160
|
+
|
|
129
161
|
async function directoryExists(path: string): Promise<boolean> {
|
|
130
162
|
try {
|
|
131
163
|
const result = await stat(path);
|
package/src/index.ts
CHANGED
|
@@ -17,6 +17,28 @@ export type ChannelLimits = {
|
|
|
17
17
|
usdPerDay?: number;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
export type ChannelBufferMode = "off" | "debounce";
|
|
21
|
+
|
|
22
|
+
export type ChannelBufferInput = {
|
|
23
|
+
mode?: ChannelBufferMode;
|
|
24
|
+
quietWindowMs?: number;
|
|
25
|
+
maxWaitMs?: number;
|
|
26
|
+
maxMessages?: number;
|
|
27
|
+
maxChars?: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type ChannelBufferConfig =
|
|
31
|
+
| {
|
|
32
|
+
mode: "off";
|
|
33
|
+
}
|
|
34
|
+
| {
|
|
35
|
+
mode: "debounce";
|
|
36
|
+
quietWindowMs: number;
|
|
37
|
+
maxWaitMs: number;
|
|
38
|
+
maxMessages: number;
|
|
39
|
+
maxChars: number;
|
|
40
|
+
};
|
|
41
|
+
|
|
20
42
|
export type WebsiteChannelAccess = {
|
|
21
43
|
mode: "token";
|
|
22
44
|
};
|
|
@@ -26,6 +48,7 @@ export type WebsiteChannelInput = {
|
|
|
26
48
|
access?: WebsiteChannelAccess;
|
|
27
49
|
secrets?: string[];
|
|
28
50
|
limits?: ChannelLimits;
|
|
51
|
+
buffer?: ChannelBufferInput;
|
|
29
52
|
};
|
|
30
53
|
|
|
31
54
|
export type WebsiteChannelConfig = {
|
|
@@ -35,6 +58,7 @@ export type WebsiteChannelConfig = {
|
|
|
35
58
|
access: WebsiteChannelAccess;
|
|
36
59
|
secrets: string[];
|
|
37
60
|
limits?: ChannelLimits;
|
|
61
|
+
buffer?: ChannelBufferConfig;
|
|
38
62
|
};
|
|
39
63
|
|
|
40
64
|
export type TelegramChannelInput = {
|
|
@@ -42,6 +66,7 @@ export type TelegramChannelInput = {
|
|
|
42
66
|
secrets?: string[];
|
|
43
67
|
allowedUpdates?: TelegramAllowedUpdate[];
|
|
44
68
|
limits?: ChannelLimits;
|
|
69
|
+
buffer?: ChannelBufferInput;
|
|
45
70
|
};
|
|
46
71
|
|
|
47
72
|
export type TelegramChannelConfig = {
|
|
@@ -51,6 +76,7 @@ export type TelegramChannelConfig = {
|
|
|
51
76
|
secrets: string[];
|
|
52
77
|
allowedUpdates: TelegramAllowedUpdate[];
|
|
53
78
|
limits?: ChannelLimits;
|
|
79
|
+
buffer?: ChannelBufferConfig;
|
|
54
80
|
};
|
|
55
81
|
|
|
56
82
|
export type WhatsappChannelInput = {
|
|
@@ -58,6 +84,7 @@ export type WhatsappChannelInput = {
|
|
|
58
84
|
provider: WhatsappChannelProvider;
|
|
59
85
|
secrets?: string[];
|
|
60
86
|
limits?: ChannelLimits;
|
|
87
|
+
buffer?: ChannelBufferInput;
|
|
61
88
|
};
|
|
62
89
|
|
|
63
90
|
export type WhatsappChannelConfig = {
|
|
@@ -66,6 +93,7 @@ export type WhatsappChannelConfig = {
|
|
|
66
93
|
name: string;
|
|
67
94
|
secrets: string[];
|
|
68
95
|
limits?: ChannelLimits;
|
|
96
|
+
buffer?: ChannelBufferConfig;
|
|
69
97
|
};
|
|
70
98
|
|
|
71
99
|
export type AgentChannel = WebsiteChannelConfig | TelegramChannelConfig | WhatsappChannelConfig;
|
|
@@ -90,6 +118,37 @@ export type AgentFilesConfig =
|
|
|
90
118
|
maxUploadBytes?: number;
|
|
91
119
|
};
|
|
92
120
|
|
|
121
|
+
export type AgentKnowledgeSource =
|
|
122
|
+
| string
|
|
123
|
+
| {
|
|
124
|
+
path: string;
|
|
125
|
+
title?: string;
|
|
126
|
+
}
|
|
127
|
+
| {
|
|
128
|
+
url: string;
|
|
129
|
+
title?: string;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export type AgentKnowledgeEmbeddingProvider = "none" | "test" | "openai";
|
|
133
|
+
|
|
134
|
+
export type AgentKnowledgeEmbeddingConfig = {
|
|
135
|
+
provider?: AgentKnowledgeEmbeddingProvider;
|
|
136
|
+
model?: string;
|
|
137
|
+
dimensions?: number;
|
|
138
|
+
secret?: string;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export type AgentKnowledgeRetrievalConfig = {
|
|
142
|
+
topK?: number;
|
|
143
|
+
hybrid?: boolean;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export type AgentKnowledgeConfig = {
|
|
147
|
+
sources?: AgentKnowledgeSource[];
|
|
148
|
+
embedding?: AgentKnowledgeEmbeddingConfig;
|
|
149
|
+
retrieval?: AgentKnowledgeRetrievalConfig;
|
|
150
|
+
};
|
|
151
|
+
|
|
93
152
|
export type JsonSchema = {
|
|
94
153
|
type: string | string[];
|
|
95
154
|
properties?: Record<string, JsonSchema>;
|
|
@@ -157,6 +216,7 @@ export type AgentConfig = {
|
|
|
157
216
|
secrets: string[];
|
|
158
217
|
tools?: AgentTool[];
|
|
159
218
|
channels?: AgentChannel[];
|
|
219
|
+
knowledge?: AgentKnowledgeConfig;
|
|
160
220
|
access: {
|
|
161
221
|
mode: AccessMode;
|
|
162
222
|
};
|
|
@@ -184,6 +244,7 @@ export function websiteChannel(input: WebsiteChannelInput): WebsiteChannelConfig
|
|
|
184
244
|
access: input.access ?? { mode: "token" },
|
|
185
245
|
secrets: normalizeChannelSecrets(input.secrets ?? ["AGENTKIT_WEBSITE_CHANNEL_TOKEN"], "website channel"),
|
|
186
246
|
...(input.limits ? { limits: input.limits } : {}),
|
|
247
|
+
...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "website channel") } : {}),
|
|
187
248
|
};
|
|
188
249
|
}
|
|
189
250
|
|
|
@@ -198,6 +259,7 @@ export function telegramChannel(input: TelegramChannelInput): TelegramChannelCon
|
|
|
198
259
|
),
|
|
199
260
|
allowedUpdates: input.allowedUpdates ?? ["message"],
|
|
200
261
|
...(input.limits ? { limits: input.limits } : {}),
|
|
262
|
+
...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "telegram channel") } : {}),
|
|
201
263
|
};
|
|
202
264
|
}
|
|
203
265
|
|
|
@@ -211,6 +273,7 @@ export function whatsappChannel(input: WhatsappChannelInput): WhatsappChannelCon
|
|
|
211
273
|
`${input.provider} whatsapp channel`,
|
|
212
274
|
),
|
|
213
275
|
...(input.limits ? { limits: input.limits } : {}),
|
|
276
|
+
...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, `${input.provider} whatsapp channel`) } : {}),
|
|
214
277
|
};
|
|
215
278
|
}
|
|
216
279
|
|
|
@@ -236,6 +299,41 @@ function normalizeChannelSecrets(secrets: string[], context: string): string[] {
|
|
|
236
299
|
return Array.from(uniqueSecrets).sort();
|
|
237
300
|
}
|
|
238
301
|
|
|
302
|
+
function normalizeChannelBuffer(input: ChannelBufferInput, context: string): ChannelBufferConfig {
|
|
303
|
+
if (input.mode === "off") {
|
|
304
|
+
return { mode: "off" };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (input.mode !== undefined && input.mode !== "debounce") {
|
|
308
|
+
throw new Error(`${context} buffer.mode must be "off" or "debounce".`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const buffer: ChannelBufferConfig = {
|
|
312
|
+
mode: "debounce",
|
|
313
|
+
quietWindowMs: input.quietWindowMs ?? 2500,
|
|
314
|
+
maxWaitMs: input.maxWaitMs ?? 12000,
|
|
315
|
+
maxMessages: input.maxMessages ?? 20,
|
|
316
|
+
maxChars: input.maxChars ?? 8000,
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
validatePositiveInteger(buffer.quietWindowMs, `${context} buffer.quietWindowMs`);
|
|
320
|
+
validatePositiveInteger(buffer.maxWaitMs, `${context} buffer.maxWaitMs`);
|
|
321
|
+
validatePositiveInteger(buffer.maxMessages, `${context} buffer.maxMessages`);
|
|
322
|
+
validatePositiveInteger(buffer.maxChars, `${context} buffer.maxChars`);
|
|
323
|
+
|
|
324
|
+
if (buffer.maxWaitMs < buffer.quietWindowMs) {
|
|
325
|
+
throw new Error(`${context} buffer.maxWaitMs must be greater than or equal to buffer.quietWindowMs.`);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return buffer;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function validatePositiveInteger(value: number, field: string): void {
|
|
332
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
333
|
+
throw new Error(`${field} must be a positive integer.`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
239
337
|
export { loadAgentCapsule, findAgentCapsuleRoot } from "./runtime/config";
|
|
240
338
|
export { runAgentMessage, runAgentMessageFromCwd } from "./runtime/chat";
|
|
241
339
|
export { listConversationsFromCwd, getConversationFromCwd } from "./runtime/conversations";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ChannelBufferConfig, ChannelType } from "../index";
|
|
2
|
+
|
|
3
|
+
export type ChannelBufferCarrier = {
|
|
4
|
+
type: ChannelType;
|
|
5
|
+
buffer?: ChannelBufferConfig;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type ChannelBufferedMessage = {
|
|
9
|
+
content: string;
|
|
10
|
+
receivedAt: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function resolveChannelBuffer(channel: ChannelBufferCarrier): ChannelBufferConfig {
|
|
14
|
+
return channel.buffer ?? { mode: "off" };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isChannelBufferEnabled(channel: ChannelBufferCarrier): boolean {
|
|
18
|
+
return resolveChannelBuffer(channel).mode === "debounce";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function renderBufferedChannelMessage(messages: ChannelBufferedMessage[]): string {
|
|
22
|
+
if (messages.length === 1) {
|
|
23
|
+
return messages[0]?.content ?? "";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const lines = messages.map((message, index) => `${index + 1}. ${message.content}`);
|
|
27
|
+
|
|
28
|
+
return `The client sent ${messages.length} messages in one burst. Answer once, using the full context below:\n\n${lines.join("\n")}`;
|
|
29
|
+
}
|
|
30
|
+
|
package/src/runtime/channels.ts
CHANGED
package/src/runtime/chat.ts
CHANGED
|
@@ -7,6 +7,9 @@ import { loadAgentCapsule } from "./config";
|
|
|
7
7
|
import { openPreparedCapsuleStore } from "./database";
|
|
8
8
|
import { loadCapsuleEnv } from "./env";
|
|
9
9
|
import { AgentKitError, isAgentKitError } from "./errors";
|
|
10
|
+
import { syncConfiguredKnowledgeSources } from "./knowledge/ingest";
|
|
11
|
+
import { appendKnowledgePromptPolicy } from "./knowledge/prompt-policy";
|
|
12
|
+
import { createKnowledgeSearchTool } from "./knowledge/tool";
|
|
10
13
|
import { createToolRuntime } from "./tools";
|
|
11
14
|
|
|
12
15
|
export type RunAgentMessageOptions = {
|
|
@@ -37,6 +40,7 @@ export async function runAgentMessage(
|
|
|
37
40
|
|
|
38
41
|
const adapter = getProviderAdapter(capsule.config.provider);
|
|
39
42
|
const env = await loadCapsuleEnv(capsule.root);
|
|
43
|
+
await syncConfiguredKnowledgeSources(capsule);
|
|
40
44
|
const { store } = await openPreparedCapsuleStore(capsule);
|
|
41
45
|
const conversationId = normalizeConversationId(options.conversationId);
|
|
42
46
|
const { conversation, previousMessages } = resolveConversation(store, {
|
|
@@ -55,8 +59,9 @@ export async function runAgentMessage(
|
|
|
55
59
|
provider: capsule.config.provider.name,
|
|
56
60
|
model: capsule.config.provider.model,
|
|
57
61
|
});
|
|
62
|
+
const runtimeTools = withKnowledgeTools(capsule);
|
|
58
63
|
const toolRuntime = createToolRuntime({
|
|
59
|
-
tools:
|
|
64
|
+
tools: runtimeTools,
|
|
60
65
|
store,
|
|
61
66
|
runId: run.id,
|
|
62
67
|
conversationId: conversation.id,
|
|
@@ -78,7 +83,10 @@ export async function runAgentMessage(
|
|
|
78
83
|
runtime: capsule.config.runtime,
|
|
79
84
|
provider: capsule.config.provider,
|
|
80
85
|
},
|
|
81
|
-
instructions: withToolVisibilityInstructions(
|
|
86
|
+
instructions: withToolVisibilityInstructions(
|
|
87
|
+
appendKnowledgePromptPolicy(capsule.instructions, capsule.config),
|
|
88
|
+
runtimeTools,
|
|
89
|
+
),
|
|
82
90
|
messages,
|
|
83
91
|
tools: toolRuntime.definitions,
|
|
84
92
|
toolRuntime,
|
|
@@ -119,6 +127,17 @@ export async function runAgentMessage(
|
|
|
119
127
|
}
|
|
120
128
|
}
|
|
121
129
|
|
|
130
|
+
function withKnowledgeTools(capsule: LoadedAgentCapsule): NonNullable<LoadedAgentCapsule["config"]["tools"]> {
|
|
131
|
+
const tools = [...(capsule.config.tools ?? [])];
|
|
132
|
+
const knowledgeTool = createKnowledgeSearchTool(capsule);
|
|
133
|
+
|
|
134
|
+
if (knowledgeTool) {
|
|
135
|
+
return [knowledgeTool, ...tools];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return tools;
|
|
139
|
+
}
|
|
140
|
+
|
|
122
141
|
function resolveConversation(
|
|
123
142
|
store: Awaited<ReturnType<typeof openPreparedCapsuleStore>>["store"],
|
|
124
143
|
input: {
|
package/src/runtime/config.ts
CHANGED
|
@@ -14,6 +14,8 @@ import type {
|
|
|
14
14
|
ChannelProvider,
|
|
15
15
|
ChannelType,
|
|
16
16
|
TelegramAllowedUpdate,
|
|
17
|
+
AgentKnowledgeEmbeddingProvider,
|
|
18
|
+
ChannelBufferMode,
|
|
17
19
|
} from "../index";
|
|
18
20
|
import { AgentKitError } from "./errors";
|
|
19
21
|
|
|
@@ -24,7 +26,9 @@ const ACCESS_MODES = new Set<AccessMode>(["private", "token", "public"]);
|
|
|
24
26
|
const TOOL_VISIBILITIES = new Set<ToolVisibility>(["user", "internal"]);
|
|
25
27
|
const CHANNEL_TYPES = new Set<ChannelType>(["website", "telegram", "whatsapp"]);
|
|
26
28
|
const CHANNEL_PROVIDERS = new Set<ChannelProvider>(["agentkit", "telegram", "zapster", "meta"]);
|
|
29
|
+
const CHANNEL_BUFFER_MODES = new Set<ChannelBufferMode>(["off", "debounce"]);
|
|
27
30
|
const TELEGRAM_ALLOWED_UPDATES = new Set<TelegramAllowedUpdate>(["message"]);
|
|
31
|
+
const KNOWLEDGE_EMBEDDING_PROVIDERS = new Set<AgentKnowledgeEmbeddingProvider>(["none", "test", "openai"]);
|
|
28
32
|
const CHANNEL_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
29
33
|
const SECRET_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/;
|
|
30
34
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
@@ -205,6 +209,7 @@ function validateAgentConfig(value: unknown): asserts value is AgentConfig {
|
|
|
205
209
|
}
|
|
206
210
|
|
|
207
211
|
validateChannels(value.channels, value.runtime as AgentRuntime);
|
|
212
|
+
validateKnowledgeConfig(value.knowledge);
|
|
208
213
|
|
|
209
214
|
if (!isRecord(value.access) || !ACCESS_MODES.has(value.access.mode as AccessMode)) {
|
|
210
215
|
throw invalidConfig('access.mode must be "private", "token", or "public".');
|
|
@@ -226,6 +231,134 @@ function validateAgentConfig(value: unknown): asserts value is AgentConfig {
|
|
|
226
231
|
validateFilesConfig(value.storage.files);
|
|
227
232
|
}
|
|
228
233
|
|
|
234
|
+
function validateKnowledgeConfig(value: unknown): void {
|
|
235
|
+
if (value === undefined) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (!isRecord(value)) {
|
|
240
|
+
throw invalidConfig("knowledge must be an object when provided.");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (value.sources !== undefined) {
|
|
244
|
+
if (!Array.isArray(value.sources)) {
|
|
245
|
+
throw invalidConfig("knowledge.sources must be an array when provided.");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
for (const [index, source] of value.sources.entries()) {
|
|
249
|
+
validateKnowledgeSource(source, index);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (value.embedding !== undefined) {
|
|
254
|
+
validateKnowledgeEmbedding(value.embedding);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (value.retrieval !== undefined) {
|
|
258
|
+
validateKnowledgeRetrieval(value.retrieval);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function validateKnowledgeSource(value: unknown, index: number): void {
|
|
263
|
+
if (typeof value === "string") {
|
|
264
|
+
expectString(value, `knowledge.sources[${index}]`);
|
|
265
|
+
validateKnowledgeSourceReference(value, `knowledge.sources[${index}]`);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (!isRecord(value)) {
|
|
270
|
+
throw invalidConfig(`knowledge.sources[${index}] must be a path string or an object with path or url.`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const hasPath = "path" in value;
|
|
274
|
+
const hasUrl = "url" in value;
|
|
275
|
+
|
|
276
|
+
if (hasPath === hasUrl) {
|
|
277
|
+
throw invalidConfig(`knowledge.sources[${index}] must set exactly one of path or url.`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (hasPath) {
|
|
281
|
+
expectString(value.path, `knowledge.sources[${index}].path`);
|
|
282
|
+
validateKnowledgeSourceReference(value.path, `knowledge.sources[${index}].path`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (hasUrl) {
|
|
286
|
+
expectString(value.url, `knowledge.sources[${index}].url`);
|
|
287
|
+
validateKnowledgeUrl(value.url, `knowledge.sources[${index}].url`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (value.title !== undefined) {
|
|
291
|
+
expectString(value.title, `knowledge.sources[${index}].title`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function validateKnowledgeSourceReference(value: string, label: string): void {
|
|
296
|
+
if (value.includes("\0")) {
|
|
297
|
+
throw invalidConfig(`${label} must not contain null bytes.`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (/^https?:\/\//i.test(value)) {
|
|
301
|
+
validateKnowledgeUrl(value, label);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function validateKnowledgeUrl(value: string, label: string): void {
|
|
306
|
+
let url: URL;
|
|
307
|
+
|
|
308
|
+
try {
|
|
309
|
+
url = new URL(value);
|
|
310
|
+
} catch {
|
|
311
|
+
throw invalidConfig(`${label} must be a valid http or https URL.`);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
315
|
+
throw invalidConfig(`${label} must be a valid http or https URL.`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function validateKnowledgeEmbedding(value: unknown): void {
|
|
320
|
+
if (!isRecord(value)) {
|
|
321
|
+
throw invalidConfig("knowledge.embedding must be an object when provided.");
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (value.provider !== undefined && !KNOWLEDGE_EMBEDDING_PROVIDERS.has(value.provider as AgentKnowledgeEmbeddingProvider)) {
|
|
325
|
+
throw invalidConfig(
|
|
326
|
+
`knowledge.embedding.provider must be one of: ${Array.from(KNOWLEDGE_EMBEDDING_PROVIDERS).join(", ")}.`,
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (value.model !== undefined) {
|
|
331
|
+
expectString(value.model, "knowledge.embedding.model");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (value.secret !== undefined) {
|
|
335
|
+
expectString(value.secret, "knowledge.embedding.secret");
|
|
336
|
+
validateSecretNames([value.secret], "knowledge.embedding.secret");
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const dimensions = value.dimensions;
|
|
340
|
+
if (dimensions !== undefined) {
|
|
341
|
+
if (typeof dimensions !== "number" || !Number.isInteger(dimensions) || dimensions <= 0 || dimensions > 65_536) {
|
|
342
|
+
throw invalidConfig("knowledge.embedding.dimensions must be a positive integer no larger than 65536.");
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function validateKnowledgeRetrieval(value: unknown): void {
|
|
348
|
+
if (!isRecord(value)) {
|
|
349
|
+
throw invalidConfig("knowledge.retrieval must be an object when provided.");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const topK = value.topK;
|
|
353
|
+
if (topK !== undefined && (typeof topK !== "number" || !Number.isInteger(topK) || topK <= 0 || topK > 50)) {
|
|
354
|
+
throw invalidConfig("knowledge.retrieval.topK must be a positive integer no larger than 50.");
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (value.hybrid !== undefined && typeof value.hybrid !== "boolean") {
|
|
358
|
+
throw invalidConfig("knowledge.retrieval.hybrid must be a boolean when provided.");
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
229
362
|
function validateChannels(value: unknown, runtime: AgentRuntime): void {
|
|
230
363
|
if (value === undefined) {
|
|
231
364
|
return;
|
|
@@ -276,6 +409,7 @@ function validateChannel(value: unknown, index: number): asserts value is AgentC
|
|
|
276
409
|
expectStringArray(value.secrets, `channels[${index}].secrets`);
|
|
277
410
|
validateSecretNames(value.secrets, `channels[${index}].secrets`);
|
|
278
411
|
validateChannelLimits(value.limits, index);
|
|
412
|
+
validateChannelBuffer(value.buffer, index);
|
|
279
413
|
|
|
280
414
|
if (value.type === "website") {
|
|
281
415
|
validateWebsiteChannel(value, index);
|
|
@@ -361,6 +495,39 @@ function validateChannelLimits(value: unknown, index: number): void {
|
|
|
361
495
|
}
|
|
362
496
|
}
|
|
363
497
|
|
|
498
|
+
function validateChannelBuffer(value: unknown, index: number): void {
|
|
499
|
+
if (value === undefined) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (!isRecord(value)) {
|
|
504
|
+
throw invalidConfig(`channels[${index}].buffer must be an object when provided.`);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (!CHANNEL_BUFFER_MODES.has(value.mode as ChannelBufferMode)) {
|
|
508
|
+
throw invalidConfig(`channels[${index}].buffer.mode must be "off" or "debounce".`);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (value.mode === "off") {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
for (const field of ["quietWindowMs", "maxWaitMs", "maxMessages", "maxChars"] as const) {
|
|
516
|
+
if (
|
|
517
|
+
value[field] === undefined ||
|
|
518
|
+
typeof value[field] !== "number" ||
|
|
519
|
+
!Number.isInteger(value[field]) ||
|
|
520
|
+
value[field] <= 0
|
|
521
|
+
) {
|
|
522
|
+
throw invalidConfig(`channels[${index}].buffer.${field} must be a positive integer.`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if ((value.maxWaitMs as number) < (value.quietWindowMs as number)) {
|
|
527
|
+
throw invalidConfig(`channels[${index}].buffer.maxWaitMs must be greater than or equal to quietWindowMs.`);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
364
531
|
function validateSecretNames(secrets: string[], field: string): void {
|
|
365
532
|
for (const secret of secrets) {
|
|
366
533
|
if (!SECRET_NAME_PATTERN.test(secret)) {
|
|
@@ -2,6 +2,8 @@ import { relative, resolve } from "node:path";
|
|
|
2
2
|
|
|
3
3
|
import type { AgentConfig, AgentTool } from "../../index";
|
|
4
4
|
import type { LoadedAgentCapsule } from "../config";
|
|
5
|
+
import { resolveKnowledgeConfig } from "../knowledge/config";
|
|
6
|
+
import { KNOWLEDGE_SEARCH_TOOL_NAME } from "../knowledge/tool";
|
|
5
7
|
|
|
6
8
|
export type ManifestSchema = {
|
|
7
9
|
source: string;
|
|
@@ -25,6 +27,25 @@ export type SharedAgentManifest = {
|
|
|
25
27
|
timeoutMs: number | null;
|
|
26
28
|
}>;
|
|
27
29
|
channels: NonNullable<AgentConfig["channels"]>;
|
|
30
|
+
knowledge: {
|
|
31
|
+
enabled: boolean;
|
|
32
|
+
sources: Array<{
|
|
33
|
+
kind: "file" | "url";
|
|
34
|
+
value: string;
|
|
35
|
+
title: string | null;
|
|
36
|
+
}>;
|
|
37
|
+
embedding: {
|
|
38
|
+
provider: string;
|
|
39
|
+
model: string | null;
|
|
40
|
+
dimensions: number | null;
|
|
41
|
+
secret: string | null;
|
|
42
|
+
};
|
|
43
|
+
retrieval: {
|
|
44
|
+
topK: number;
|
|
45
|
+
hybrid: boolean;
|
|
46
|
+
};
|
|
47
|
+
internalTool: string | null;
|
|
48
|
+
};
|
|
28
49
|
access: AgentConfig["access"];
|
|
29
50
|
storage: {
|
|
30
51
|
driver: AgentConfig["storage"]["driver"];
|
|
@@ -57,6 +78,7 @@ export function buildSharedAgentManifest(
|
|
|
57
78
|
const requiredSecrets = new Set(capsule.config.secrets);
|
|
58
79
|
const database = buildDatabaseManifest(capsule, options.databaseSchema ?? null);
|
|
59
80
|
const files = buildFilesManifest(capsule);
|
|
81
|
+
const knowledge = resolveKnowledgeConfig(capsule.config);
|
|
60
82
|
|
|
61
83
|
if (capsule.config.access.mode === "private") {
|
|
62
84
|
requiredSecrets.add("AGENTKIT_PRIVATE_TOKEN");
|
|
@@ -75,6 +97,10 @@ export function buildSharedAgentManifest(
|
|
|
75
97
|
requiredSecrets.add("TURSO_AUTH_TOKEN");
|
|
76
98
|
}
|
|
77
99
|
|
|
100
|
+
if (knowledge.embedding.secret) {
|
|
101
|
+
requiredSecrets.add(knowledge.embedding.secret);
|
|
102
|
+
}
|
|
103
|
+
|
|
78
104
|
for (const tool of capsule.config.tools ?? []) {
|
|
79
105
|
for (const secret of tool.secrets ?? []) {
|
|
80
106
|
requiredSecrets.add(secret);
|
|
@@ -104,6 +130,17 @@ export function buildSharedAgentManifest(
|
|
|
104
130
|
timeoutMs: tool.timeoutMs ?? null,
|
|
105
131
|
})),
|
|
106
132
|
channels: capsule.config.channels ?? [],
|
|
133
|
+
knowledge: {
|
|
134
|
+
enabled: knowledge.enabled,
|
|
135
|
+
sources: knowledge.sources.map((source) => ({
|
|
136
|
+
kind: source.kind,
|
|
137
|
+
value: source.value,
|
|
138
|
+
title: source.title,
|
|
139
|
+
})),
|
|
140
|
+
embedding: knowledge.embedding,
|
|
141
|
+
retrieval: knowledge.retrieval,
|
|
142
|
+
internalTool: knowledge.enabled ? KNOWLEDGE_SEARCH_TOOL_NAME : null,
|
|
143
|
+
},
|
|
107
144
|
access: capsule.config.access,
|
|
108
145
|
storage: {
|
|
109
146
|
driver: capsule.config.storage.driver,
|
|
@@ -24,6 +24,12 @@ export type DeployReadinessContext = {
|
|
|
24
24
|
userManagedSecrets: string[];
|
|
25
25
|
agentKitManagedSecrets: string[];
|
|
26
26
|
localSecrets: Record<string, DeployReadinessSecretSource>;
|
|
27
|
+
knowledge: {
|
|
28
|
+
enabled: boolean;
|
|
29
|
+
sourceCount: number;
|
|
30
|
+
embeddingProvider: string;
|
|
31
|
+
databaseDriver: "none" | "turso";
|
|
32
|
+
};
|
|
27
33
|
};
|
|
28
34
|
|
|
29
35
|
export async function loadDeployReadinessContext(
|
|
@@ -70,6 +76,12 @@ export async function loadDeployReadinessContext(
|
|
|
70
76
|
userManagedSecrets: requiredSecrets.filter((name) => !managed.has(name)),
|
|
71
77
|
agentKitManagedSecrets,
|
|
72
78
|
localSecrets,
|
|
79
|
+
knowledge: {
|
|
80
|
+
enabled: manifest.knowledge.enabled,
|
|
81
|
+
sourceCount: manifest.knowledge.sources.length,
|
|
82
|
+
embeddingProvider: manifest.knowledge.embedding.provider,
|
|
83
|
+
databaseDriver: manifest.storage.database.driver,
|
|
84
|
+
},
|
|
73
85
|
};
|
|
74
86
|
}
|
|
75
87
|
|