@andreprado/agentkit 0.1.0-alpha.4 → 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.
Files changed (80) hide show
  1. package/README.md +5 -0
  2. package/docs/guides/add-channel.md +25 -0
  3. package/docs/guides/add-knowledge.md +134 -0
  4. package/docs/guides/agentkit-skills-architecture.md +471 -0
  5. package/docs/guides/channels-production-handoff.md +2 -0
  6. package/docs/guides/connect-telegram.md +15 -0
  7. package/docs/guides/connect-whatsapp-zapster.md +16 -0
  8. package/docs/guides/create-agent.md +1 -1
  9. package/docs/llms-full.txt +90 -1
  10. package/docs/llms.txt +9 -2
  11. package/package.json +2 -1
  12. package/src/cli/args.ts +36 -0
  13. package/src/cli/cloud-client.ts +257 -0
  14. package/src/cli/commands/channels.ts +459 -0
  15. package/src/cli/commands/knowledge.ts +136 -0
  16. package/src/cli/constants.ts +4 -0
  17. package/src/cli/deploy-chat-ui.ts +385 -0
  18. package/src/cli/deploy-readiness.ts +348 -0
  19. package/src/cli/flags.ts +162 -0
  20. package/src/cli/help.ts +166 -0
  21. package/src/cli/index.ts +219 -1912
  22. package/src/cli/process.ts +31 -0
  23. package/src/cloud/artifact.ts +92 -1
  24. package/src/cloud/contracts.ts +16 -0
  25. package/src/create-project.ts +38 -6
  26. package/src/index.ts +98 -0
  27. package/src/runtime/channel-buffer.ts +30 -0
  28. package/src/runtime/channels.ts +1 -0
  29. package/src/runtime/chat.ts +21 -2
  30. package/src/runtime/config.ts +167 -0
  31. package/src/runtime/core/manifest.ts +37 -0
  32. package/src/runtime/deploy-readiness.ts +12 -0
  33. package/src/runtime/dev-server.ts +159 -10
  34. package/src/runtime/inspect.ts +34 -0
  35. package/src/runtime/knowledge/chunk.ts +333 -0
  36. package/src/runtime/knowledge/config.ts +135 -0
  37. package/src/runtime/knowledge/embeddings.ts +133 -0
  38. package/src/runtime/knowledge/ingest.ts +521 -0
  39. package/src/runtime/knowledge/prompt-policy.ts +30 -0
  40. package/src/runtime/knowledge/retrieve.ts +283 -0
  41. package/src/runtime/knowledge/schema.ts +56 -0
  42. package/src/runtime/knowledge/tool.ts +64 -0
  43. package/src/runtime/knowledge/vector.ts +258 -0
  44. package/src/runtime/targets/cloudflare/build.ts +469 -4
  45. package/src/storage/sqlite.ts +5 -0
  46. package/src/templates/blank.ts +8 -4
  47. package/src/templates/dentista.ts +3 -1
  48. package/src/templates/skills/agentkit-build-agent/SKILL.md +49 -0
  49. package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +20 -0
  50. package/src/templates/skills/agentkit-build-agent/templates/sales-qualifier.instructions.md +17 -0
  51. package/src/templates/skills/agentkit-build-agent/templates/support-agent.instructions.md +16 -0
  52. package/src/templates/skills/agentkit-capsule/SKILL.md +62 -0
  53. package/src/templates/skills/agentkit-capsule/references/docs-router.md +15 -0
  54. package/src/templates/skills/agentkit-channels/SKILL.md +62 -0
  55. package/src/templates/skills/agentkit-channels/references/channel-buffering.md +58 -0
  56. package/src/templates/skills/agentkit-channels/references/channel-debugging.md +37 -0
  57. package/src/templates/skills/agentkit-channels/references/telegram.md +37 -0
  58. package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +37 -0
  59. package/src/templates/skills/agentkit-database/SKILL.md +42 -0
  60. package/src/templates/skills/agentkit-database/templates/appointments.schema.sql +15 -0
  61. package/src/templates/skills/agentkit-database/templates/leads.schema.sql +17 -0
  62. package/src/templates/skills/agentkit-deploy/SKILL.md +44 -0
  63. package/src/templates/skills/agentkit-evals/SKILL.md +31 -0
  64. package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +14 -0
  65. package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +14 -0
  66. package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +18 -0
  67. package/src/templates/skills/agentkit-knowledge/SKILL.md +40 -0
  68. package/src/templates/skills/agentkit-knowledge/templates/faq.md +14 -0
  69. package/src/templates/skills/agentkit-knowledge/templates/policies.md +14 -0
  70. package/src/templates/skills/agentkit-knowledge/templates/prices.csv +3 -0
  71. package/src/templates/skills/agentkit-prompts/SKILL.md +45 -0
  72. package/src/templates/skills/agentkit-prompts/templates/knowledge-grounded-faq.instructions.md +11 -0
  73. package/src/templates/skills/agentkit-provider/SKILL.md +57 -0
  74. package/src/templates/skills/agentkit-security/SKILL.md +55 -0
  75. package/src/templates/skills/agentkit-tools/SKILL.md +36 -0
  76. package/src/templates/skills/agentkit-tools/examples/database-write.tool.md +35 -0
  77. package/src/templates/skills/agentkit-tools/examples/eval-safe-external-action.tool.md +37 -0
  78. package/src/templates/skills/agentkit-tools/examples/lookup-order.tool.md +46 -0
  79. package/src/templates/skills/agentkit-troubleshooting/SKILL.md +52 -0
  80. package/src/templates/support.ts +8 -4
@@ -0,0 +1,31 @@
1
+ import { spawn } from "node:child_process";
2
+
3
+ export function runInheritedCommand(command: string, args: string[], cwd: string): Promise<number> {
4
+ return new Promise((resolvePromise, reject) => {
5
+ const child = spawn(command, args, {
6
+ cwd,
7
+ stdio: "inherit",
8
+ });
9
+
10
+ child.once("error", reject);
11
+ child.once("close", (code) => resolvePromise(code ?? 1));
12
+ });
13
+ }
14
+
15
+ export async function waitForShutdown(stop: () => Promise<void>): Promise<void> {
16
+ await new Promise<void>((resolve) => {
17
+ let stopped = false;
18
+
19
+ const shutdown = () => {
20
+ if (stopped) {
21
+ return;
22
+ }
23
+
24
+ stopped = true;
25
+ void stop().finally(resolve);
26
+ };
27
+
28
+ process.once("SIGINT", shutdown);
29
+ process.once("SIGTERM", shutdown);
30
+ });
31
+ }
@@ -1,10 +1,15 @@
1
1
  import { createHash } from "node:crypto";
2
- import { readFile } from "node:fs/promises";
2
+ import { readFile, stat } from "node:fs/promises";
3
+ import { extname, isAbsolute, relative, resolve } from "node:path";
3
4
 
4
5
  import type { AgentBuildResult, AgentManifestWithBundle } from "../runtime/targets/cloudflare/build";
5
6
  import { AgentKitError } from "../runtime/errors";
6
7
  import type { HostedDeployArtifact } from "./contracts";
7
8
 
9
+ const HOSTED_KNOWLEDGE_MAX_SOURCE_BYTES = 5 * 1024 * 1024;
10
+ const HOSTED_KNOWLEDGE_MAX_TOTAL_BYTES = 9 * 1024 * 1024;
11
+ const HOSTED_KNOWLEDGE_SUPPORTED_EXTENSIONS = new Set([".md", ".markdown", ".txt", ".csv"]);
12
+
8
13
  export async function createHostedDeployArtifact(build: AgentBuildResult): Promise<HostedDeployArtifact> {
9
14
  const [manifestSource, workerSource, wranglerSource] = await Promise.all([
10
15
  readFile(build.manifestPath, "utf8"),
@@ -40,9 +45,95 @@ export async function createHostedDeployArtifact(build: AgentBuildResult): Promi
40
45
  };
41
46
  }
42
47
 
48
+ if (manifest.knowledge.enabled) {
49
+ artifact.knowledge_sources = await createHostedKnowledgeSources(build.root, manifest);
50
+ }
51
+
43
52
  return artifact;
44
53
  }
45
54
 
55
+ async function createHostedKnowledgeSources(
56
+ root: string,
57
+ manifest: AgentManifestWithBundle,
58
+ ): Promise<NonNullable<HostedDeployArtifact["knowledge_sources"]>> {
59
+ const sources: NonNullable<HostedDeployArtifact["knowledge_sources"]>["sources"] = [];
60
+ let totalBytes = 0;
61
+
62
+ for (const source of manifest.knowledge.sources) {
63
+ if (source.kind === "url") {
64
+ throw new AgentKitError(
65
+ "artifact_knowledge_source_unsupported",
66
+ "Hosted deploy can automatically sync local Knowledge files only. URL Knowledge sources require a hosted ingestion connector.",
67
+ );
68
+ }
69
+
70
+ const absolutePath = resolve(root, source.value);
71
+ const sourceKey = capsuleRelativePath(root, absolutePath);
72
+ const extension = extname(sourceKey).toLowerCase();
73
+
74
+ if (!HOSTED_KNOWLEDGE_SUPPORTED_EXTENSIONS.has(extension)) {
75
+ throw new AgentKitError(
76
+ "artifact_knowledge_source_unsupported",
77
+ `Knowledge source "${sourceKey}" is not supported for hosted deploy. Use .md, .txt, or .csv.`,
78
+ );
79
+ }
80
+
81
+ const stats = await stat(absolutePath).catch((error) => {
82
+ throw new AgentKitError("artifact_knowledge_source_missing", `Knowledge source "${sourceKey}" was not found.`, {
83
+ cause: error,
84
+ });
85
+ });
86
+
87
+ if (!stats.isFile()) {
88
+ throw new AgentKitError("artifact_knowledge_source_invalid", `Knowledge source "${sourceKey}" must be a file.`);
89
+ }
90
+
91
+ if (stats.size > HOSTED_KNOWLEDGE_MAX_SOURCE_BYTES) {
92
+ throw new AgentKitError(
93
+ "artifact_knowledge_source_too_large",
94
+ `Knowledge source "${sourceKey}" is too large for automatic deploy sync. Keep each source under 5MB.`,
95
+ );
96
+ }
97
+
98
+ totalBytes += stats.size;
99
+
100
+ if (totalBytes > HOSTED_KNOWLEDGE_MAX_TOTAL_BYTES) {
101
+ throw new AgentKitError(
102
+ "artifact_knowledge_sources_too_large",
103
+ "Knowledge sources are too large for automatic deploy sync. Keep total packaged Knowledge under 9MB.",
104
+ );
105
+ }
106
+
107
+ const content = await readFile(absolutePath, "utf8");
108
+ sources.push({
109
+ kind: "file",
110
+ source_key: sourceKey,
111
+ title: source.title,
112
+ content,
113
+ sha256: sha256(content),
114
+ });
115
+ }
116
+
117
+ return {
118
+ content_type: "application/json",
119
+ sources,
120
+ sha256: sha256(JSON.stringify(sources)),
121
+ };
122
+ }
123
+
124
+ function capsuleRelativePath(root: string, absolutePath: string): string {
125
+ const relativePath = relative(root, absolutePath);
126
+
127
+ if (!relativePath || relativePath.startsWith("..") || isAbsolute(relativePath)) {
128
+ throw new AgentKitError(
129
+ "artifact_knowledge_source_outside_capsule",
130
+ "Knowledge source paths must stay inside the Agent Capsule.",
131
+ );
132
+ }
133
+
134
+ return relativePath.split("\\").join("/");
135
+ }
136
+
46
137
  function sha256(source: string): string {
47
138
  return createHash("sha256").update(source).digest("hex");
48
139
  }
@@ -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
 
@@ -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 = template.files(target.name, {
31
- agentkitDependency: await resolveAgentKitDependency(),
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
+
@@ -8,6 +8,7 @@ export type ChannelDeliveryState =
8
8
  | "received"
9
9
  | "validated"
10
10
  | "duplicate"
11
+ | "buffered"
11
12
  | "queued"
12
13
  | "running"
13
14
  | "sent"
@@ -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: capsule.config.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(capsule.instructions, capsule.config.tools ?? []),
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: {