@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.
Files changed (74) 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/commands/channels.ts +39 -1
  13. package/src/cli/commands/knowledge.ts +136 -0
  14. package/src/cli/deploy-readiness.ts +19 -0
  15. package/src/cli/help.ts +8 -0
  16. package/src/cli/index.ts +16 -4
  17. package/src/cloud/artifact.ts +92 -1
  18. package/src/cloud/contracts.ts +16 -0
  19. package/src/create-project.ts +38 -6
  20. package/src/index.ts +98 -0
  21. package/src/runtime/channel-buffer.ts +30 -0
  22. package/src/runtime/channels.ts +1 -0
  23. package/src/runtime/chat.ts +21 -2
  24. package/src/runtime/config.ts +167 -0
  25. package/src/runtime/core/manifest.ts +37 -0
  26. package/src/runtime/deploy-readiness.ts +12 -0
  27. package/src/runtime/dev-server.ts +159 -10
  28. package/src/runtime/inspect.ts +34 -0
  29. package/src/runtime/knowledge/chunk.ts +333 -0
  30. package/src/runtime/knowledge/config.ts +135 -0
  31. package/src/runtime/knowledge/embeddings.ts +133 -0
  32. package/src/runtime/knowledge/ingest.ts +521 -0
  33. package/src/runtime/knowledge/prompt-policy.ts +30 -0
  34. package/src/runtime/knowledge/retrieve.ts +283 -0
  35. package/src/runtime/knowledge/schema.ts +56 -0
  36. package/src/runtime/knowledge/tool.ts +64 -0
  37. package/src/runtime/knowledge/vector.ts +258 -0
  38. package/src/runtime/targets/cloudflare/build.ts +469 -4
  39. package/src/storage/sqlite.ts +5 -0
  40. package/src/templates/blank.ts +8 -4
  41. package/src/templates/dentista.ts +3 -1
  42. package/src/templates/skills/agentkit-build-agent/SKILL.md +49 -0
  43. package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +20 -0
  44. package/src/templates/skills/agentkit-build-agent/templates/sales-qualifier.instructions.md +17 -0
  45. package/src/templates/skills/agentkit-build-agent/templates/support-agent.instructions.md +16 -0
  46. package/src/templates/skills/agentkit-capsule/SKILL.md +62 -0
  47. package/src/templates/skills/agentkit-capsule/references/docs-router.md +15 -0
  48. package/src/templates/skills/agentkit-channels/SKILL.md +62 -0
  49. package/src/templates/skills/agentkit-channels/references/channel-buffering.md +58 -0
  50. package/src/templates/skills/agentkit-channels/references/channel-debugging.md +37 -0
  51. package/src/templates/skills/agentkit-channels/references/telegram.md +37 -0
  52. package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +37 -0
  53. package/src/templates/skills/agentkit-database/SKILL.md +42 -0
  54. package/src/templates/skills/agentkit-database/templates/appointments.schema.sql +15 -0
  55. package/src/templates/skills/agentkit-database/templates/leads.schema.sql +17 -0
  56. package/src/templates/skills/agentkit-deploy/SKILL.md +44 -0
  57. package/src/templates/skills/agentkit-evals/SKILL.md +31 -0
  58. package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +14 -0
  59. package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +14 -0
  60. package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +18 -0
  61. package/src/templates/skills/agentkit-knowledge/SKILL.md +40 -0
  62. package/src/templates/skills/agentkit-knowledge/templates/faq.md +14 -0
  63. package/src/templates/skills/agentkit-knowledge/templates/policies.md +14 -0
  64. package/src/templates/skills/agentkit-knowledge/templates/prices.csv +3 -0
  65. package/src/templates/skills/agentkit-prompts/SKILL.md +45 -0
  66. package/src/templates/skills/agentkit-prompts/templates/knowledge-grounded-faq.instructions.md +11 -0
  67. package/src/templates/skills/agentkit-provider/SKILL.md +57 -0
  68. package/src/templates/skills/agentkit-security/SKILL.md +55 -0
  69. package/src/templates/skills/agentkit-tools/SKILL.md +36 -0
  70. package/src/templates/skills/agentkit-tools/examples/database-write.tool.md +35 -0
  71. package/src/templates/skills/agentkit-tools/examples/eval-safe-external-action.tool.md +37 -0
  72. package/src/templates/skills/agentkit-tools/examples/lookup-order.tool.md +46 -0
  73. package/src/templates/skills/agentkit-troubleshooting/SKILL.md +52 -0
  74. package/src/templates/support.ts +8 -4
@@ -50,6 +50,22 @@ export default defineAgent({
50
50
  });
51
51
  ```
52
52
 
53
+ To handle clients who send several WhatsApp messages before waiting, enable channel buffering:
54
+
55
+ ```ts
56
+ whatsappChannel({
57
+ name: "support-whatsapp",
58
+ provider: "zapster",
59
+ buffer: {
60
+ mode: "debounce",
61
+ quietWindowMs: 2500,
62
+ maxWaitMs: 12000,
63
+ maxMessages: 20,
64
+ maxChars: 8000,
65
+ },
66
+ })
67
+ ```
68
+
53
69
  ## Setup Behavior
54
70
 
55
71
  `agentkit channels setup support-whatsapp` prints the stable AgentKit webhook URL. Paste it into Zapster webhook settings and configure Zapster to send the same shared secret as `ZAPSTER_WEBHOOK_SECRET`.
@@ -84,7 +84,7 @@ Primary flow:
84
84
  Develop an appointment and intake agent for an ophthalmology office.
85
85
  ```
86
86
 
87
- The generated `AGENTS.md`, `AGENTKIT.md`, and `CLAUDE.md` tell the coding agent which files to edit and which verification commands to run. There is no wizard or recipe layer: the coding agent edits the capsule directly from the scaffold, contract, and owner request.
87
+ The generated `AGENTS.md`, `AGENTKIT.md`, `CLAUDE.md`, and `skills/` pack tell the coding agent which files to edit, which task skill to load, and which verification commands to run. There is no wizard or recipe layer: the coding agent edits the capsule directly from the scaffold, contract, and owner request. The default router is `skills/agentkit-capsule/SKILL.md`; `llms-full.txt` is reserved for complete-contract checks.
88
88
 
89
89
  Optional shortcut when copying a prompt into another coding agent:
90
90
 
@@ -40,6 +40,10 @@ agentkit chat-ui --deploy
40
40
  agentkit chat-ui --deploy [--port <number>] [--token-file <path>]
41
41
  agentkit chat --message <text> [--conversation-id <id>]
42
42
  agentkit tool <name> [--input <path-or-json>]
43
+ agentkit knowledge add <path-or-url>
44
+ agentkit knowledge sync
45
+ agentkit knowledge inspect
46
+ agentkit knowledge search <query> [--top-k <number>]
43
47
  agentkit db migrate
44
48
  agentkit db reset --yes
45
49
  agentkit db shell
@@ -144,7 +148,7 @@ npm run agentkit -- handoff codex "Develop an appointment and intake agent for a
144
148
  npm run agentkit -- handoff claude "Develop an appointment and intake agent for an ophthalmology office."
145
149
  ```
146
150
 
147
- The command prints a ready-to-paste prompt that points the coding agent at `AGENTKIT.md` and the packaged `llms-full.txt` contract.
151
+ The command prints a ready-to-paste prompt that points the coding agent at `AGENTKIT.md`, the repo-local `skills/agentkit-capsule/SKILL.md` router when present, and the packaged `llms.txt` docs router. Load `llms-full.txt` only when a skill or ambiguous framework behavior requires the complete contract.
148
152
 
149
153
  The generated docs and handoff prompt must make UI testing explicit. For local UI testing, run `npm run dev`, open the printed `Chat:` URL, and tell the owner the exact URL. For hosted UI testing after deploy, run `npm run agentkit -- chat-ui --deploy`, open the printed `Chat:` URL, and tell the owner it is connected to the hosted deploy.
150
154
 
@@ -239,6 +243,73 @@ npm run chat -- --message "hello"
239
243
 
240
244
  If a provider key is missing, the runtime returns `secret_not_found`.
241
245
 
246
+ ## Knowledge Contract
247
+
248
+ Knowledge is AgentKit's native retrieval layer for facts the agent should ground in source files. Use it for FAQs, prices, policies, service descriptions, procedures, CSV tables, and reference docs. Do not put secrets, credentials, `.env` contents, or live customer/payment records in Knowledge. Use tools for live or authorization-sensitive data.
249
+
250
+ Configure Knowledge in `agentkit.config.ts`:
251
+
252
+ ```ts
253
+ knowledge: {
254
+ sources: [
255
+ "knowledge/faq.md",
256
+ { path: "knowledge/prices.csv", title: "Prices" },
257
+ ],
258
+ retrieval: {
259
+ topK: 8,
260
+ hybrid: false,
261
+ },
262
+ },
263
+ ```
264
+
265
+ Local Knowledge supports `.md`, `.markdown`, `.txt`, and `.csv` sources inside the Agent Capsule. Markdown chunks follow headings, text chunks follow paragraphs, and CSV chunks preserve row data with headers.
266
+
267
+ Embeddings are configured separately from the chat provider. The default provider is `none`, which gives local lexical search without an API key. For OpenAI embeddings:
268
+
269
+ ```ts
270
+ knowledge: {
271
+ sources: ["knowledge/faq.md"],
272
+ embedding: {
273
+ provider: "openai",
274
+ model: "text-embedding-3-small",
275
+ secret: "KNOWLEDGE_OPENAI_API_KEY",
276
+ },
277
+ retrieval: {
278
+ topK: 8,
279
+ hybrid: true,
280
+ },
281
+ },
282
+ ```
283
+
284
+ Set the local embedding secret with `agentkit env set KNOWLEDGE_OPENAI_API_KEY --stdin`. Do not commit the value.
285
+
286
+ Knowledge commands:
287
+
288
+ ```sh
289
+ agentkit knowledge add knowledge/faq.md
290
+ agentkit knowledge sync
291
+ agentkit knowledge inspect
292
+ agentkit knowledge search "refund policy" --top-k 3
293
+ ```
294
+
295
+ `knowledge add` indexes one local path. `knowledge sync` indexes all configured `knowledge.sources` and skips unchanged files by content hash. `agentkit dev` and `agentkit chat` also sync configured Knowledge automatically before local runs. `knowledge inspect` lists indexed sources and chunk counts. `knowledge search` validates retrieval before relying on the agent. When embeddings are configured locally, AgentKit stores canonical chunks in `.agentkit/agentkit.db`, rebuilds a local libSQL vector sidecar at `.agentkit/agentkit.vectors.db`, uses native `libsql_vector_idx` semantic search, and falls back to stored JSON embeddings if the native vector path is unavailable.
296
+
297
+ When `knowledge` is configured, AgentKit automatically registers the internal chat tool `agentkit_search_knowledge` and appends a prompt policy. The policy tells the agent to search before answering business-specific factual questions and not to expose raw retrieval JSON, scores, chunk IDs, or tool output objects. With `test/fake`, verify the internal tool directly:
298
+
299
+ ```sh
300
+ agentkit chat --message '{"tool":"agentkit_search_knowledge","input":{"query":"refund policy","topK":1}}'
301
+ ```
302
+
303
+ Expected output:
304
+
305
+ ```txt
306
+ Tool agentkit_search_knowledge: completed
307
+ ```
308
+
309
+ Cloudflare Knowledge deploys require `storage.driver: "agentkit"` and `storage.database.driver: "turso"`. `agentkit deploy doctor` and `agentkit build --target cloudflare` fail clearly when Knowledge is configured without Turso. Cloudflare artifacts include the Knowledge manifest, required embedding secret names, internal Knowledge schema, packaged local source contents, prompt policy, and hosted `agentkit_search_knowledge` runtime. During `agentkit deploy`, AgentKit Cloud applies the Knowledge schema, chunks packaged local sources, creates embeddings when configured, deletes stale hosted sources, and syncs sources, chunks, embedding metadata, FTS rows, and a native Turso `libsql_vector_idx` index into the project Turso database before publishing the Worker. Local `agentkit knowledge add/sync`, `agentkit dev`, and `agentkit chat` index configured Knowledge into local SQLite and the local libSQL vector sidecar; hosted deploy syncs configured local Knowledge sources automatically from the deploy artifact so private source material and embedding secrets do not move into client code. Hosted semantic search uses Turso native vector search when embeddings are configured and falls back to stored JSON embeddings if the native vector path is unavailable.
310
+
311
+ Full guide: `docs/guides/add-knowledge.md`.
312
+
242
313
  ## Channel Contract
243
314
 
244
315
  Channels are hosted inbound/outbound conversation transports. They are separate from tools: channels receive user messages, while tools let the agent call external systems.
@@ -272,6 +343,24 @@ Rules:
272
343
  - Config stores secret names only, never secret values.
273
344
  - AgentKit owns channel webhook URLs, dedupe, identities, queue state, and delivery logs.
274
345
  - Do not store channel plumbing in the user's Turso database.
346
+ - Use `buffer.mode: "debounce"` when a channel should coalesce rapid client messages into one agent run.
347
+ - Buffered deliveries show `buffered`, then flush to one `queued` run after `quietWindowMs`, `maxWaitMs`, `maxMessages`, or `maxChars`.
348
+
349
+ Channel buffer example:
350
+
351
+ ```ts
352
+ whatsappChannel({
353
+ name: "support-whatsapp",
354
+ provider: "zapster",
355
+ buffer: {
356
+ mode: "debounce",
357
+ quietWindowMs: 2500,
358
+ maxWaitMs: 12000,
359
+ maxMessages: 20,
360
+ maxChars: 8000,
361
+ },
362
+ })
363
+ ```
275
364
 
276
365
  Useful guides:
277
366
 
package/docs/llms.txt CHANGED
@@ -2,12 +2,13 @@
2
2
 
3
3
  AgentKit creates and runs Agent Capsules: folders with `agentkit.config.ts`, prompts, tools, evals, local storage, and agent-facing docs.
4
4
 
5
- Read `docs/llms-full.txt` when you need the whole operating contract.
5
+ Read `docs/llms-full.txt` only when you need the whole operating contract.
6
6
 
7
7
  Task guides:
8
8
 
9
9
  - Create a capsule: `docs/guides/create-agent.md`
10
10
  - Add a TypeScript tool: `docs/guides/add-tool.md`
11
+ - Add Knowledge from local docs/CSVs: `docs/guides/add-knowledge.md`
11
12
  - Run or prepare evals: `docs/guides/run-evals.md`
12
13
  - Switch from `test/fake` to a real provider: `docs/guides/use-provider.md`
13
14
  - Prepare for hosted deploy: `docs/guides/prepare-deploy.md`
@@ -15,11 +16,13 @@ Task guides:
15
16
  - Build container artifact: `agentkit build --target container`
16
17
  - Generate VPS handoff: `agentkit deploy --target vps --host agent.example.com --dry-run`
17
18
  - Add hosted channels: `docs/guides/add-channel.md`
19
+ - Buffer rapid channel messages: `docs/guides/add-channel.md#buffer-bursty-messages`
18
20
  - Connect Telegram: `docs/guides/connect-telegram.md`
19
21
  - Connect WhatsApp through Zapster: `docs/guides/connect-whatsapp-zapster.md`
20
22
  - Follow channel webhook and delivery-log safety rules: `docs/guides/channel-security.md`
21
23
  - Prepare Channels for production: `docs/guides/channels-production-handoff.md`
22
24
  - Follow secret, access, and tool safety rules: `docs/guides/security-rules.md`
25
+ - Plan repo-local skills for coding agents: `docs/guides/agentkit-skills-architecture.md`
23
26
 
24
27
  Current local commands:
25
28
 
@@ -33,6 +36,10 @@ agentkit env unset <NAME>
33
36
  agentkit handoff codex|claude [goal]
34
37
  agentkit chat --message "hello"
35
38
  agentkit tool <name> --input <path-or-json>
39
+ agentkit knowledge add <path-or-url>
40
+ agentkit knowledge sync
41
+ agentkit knowledge inspect
42
+ agentkit knowledge search <query> [--top-k <number>]
36
43
  agentkit eval run
37
44
  agentkit conversations list
38
45
  agentkit conversations show <conversation-id>
@@ -58,7 +65,7 @@ agentkit dev
58
65
  agentkit open
59
66
  ```
60
67
 
61
- Generated capsules include `AGENTKIT.md` and `AGENTS.md` so Codex, Claude Code, or another coding agent can treat the owner's natural-language request as the brief and start building immediately. `agentkit handoff codex "Develop an ophthalmology office intake agent"` is an optional prompt-printing shortcut for users who are not already inside a coding-agent workspace. There is no wizard or recipe layer: the coding agent edits the capsule directly from the scaffold and contract.
68
+ Generated capsules include `AGENTKIT.md`, `AGENTS.md`, and a repo-local `skills/` pack so Codex, Claude Code, or another coding agent can treat the owner's natural-language request as the brief and start building immediately without loading the full contract by default. Start with `skills/agentkit-capsule/SKILL.md`, then load the task skill for the current work. `agentkit handoff codex "Develop an ophthalmology office intake agent"` is an optional prompt-printing shortcut for users who are not already inside a coding-agent workspace. There is no wizard or recipe layer: the coding agent edits the capsule directly from the scaffold and contract.
62
69
 
63
70
  UI testing is part of the handoff. For local UI testing, run `agentkit dev`, open the printed `Chat:` URL, and tell the owner the exact URL. After hosted deploy, run `agentkit chat-ui --deploy`, open the printed `Chat:` URL, and tell the owner it is connected to the deploy.
64
71
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andreprado/agentkit",
3
- "version": "0.1.0-alpha.5",
3
+ "version": "0.1.0-alpha.6",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "repository": {
@@ -35,6 +35,7 @@
35
35
  "dependencies": {
36
36
  "@earendil-works/pi-ai": "^0.75.5",
37
37
  "@earendil-works/pi-coding-agent": "^0.75.5",
38
+ "@libsql/client": "^0.15.15",
38
39
  "esbuild": "^0.28.0",
39
40
  "tsx": "^4.22.3",
40
41
  "typebox": "^1.1.38"
@@ -6,6 +6,7 @@ import { cloudFetch, cloudGet, cloudPost } from "../cloud-client";
6
6
  import { parseCloudApiUrl } from "../flags";
7
7
  import { loadAgentCapsule } from "../../runtime/config";
8
8
  import { readLocalDeployStateIfExists, type LocalDeployState } from "../../runtime/core/deploy-state";
9
+ import type { AgentChannel } from "../../index";
9
10
 
10
11
  type DeployState = LocalDeployState;
11
12
 
@@ -17,6 +18,7 @@ type CloudChannel = {
17
18
  status: string;
18
19
  webhook_url: string;
19
20
  required_secrets: Array<{ name: string; status: string }>;
21
+ buffer?: AgentChannel["buffer"];
20
22
  };
21
23
 
22
24
  type CloudDelivery = {
@@ -70,11 +72,14 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
70
72
 
71
73
  const deployState = await readDeployStateRequired(process.cwd());
72
74
  const provider = channelProviderForCli(type, args.flags.provider);
75
+ const localChannel = await readLocalChannelConfig(process.cwd(), type, provider, name);
73
76
  const payload = {
74
77
  name,
75
78
  type,
76
79
  provider,
77
- secrets: defaultChannelSecretsForCli(type, provider),
80
+ secrets: localChannel?.secrets ?? defaultChannelSecretsForCli(type, provider),
81
+ ...(localChannel?.limits ? { limits: channelLimitsForApi(localChannel.limits) } : {}),
82
+ ...(localChannel?.buffer ? { buffer: localChannel.buffer } : {}),
78
83
  };
79
84
  const response = await cloudPost<{ channel: CloudChannel }>(
80
85
  parseCloudApiUrl(args.flags.api),
@@ -280,6 +285,15 @@ function printChannelStatus(channel: CloudChannel): void {
280
285
  for (const secret of channel.required_secrets) {
281
286
  console.log(` ${secret.name}: ${secret.status}`);
282
287
  }
288
+ if (channel.buffer) {
289
+ if (channel.buffer.mode === "off") {
290
+ console.log("Buffer: off");
291
+ } else {
292
+ console.log(
293
+ `Buffer: debounce quiet=${channel.buffer.quietWindowMs}ms max_wait=${channel.buffer.maxWaitMs}ms max_messages=${channel.buffer.maxMessages} max_chars=${channel.buffer.maxChars}`,
294
+ );
295
+ }
296
+ }
283
297
  }
284
298
 
285
299
  async function printChannelDeliverySummary(
@@ -352,6 +366,30 @@ function defaultChannelSecretsForCli(type: string, provider: string): string[] {
352
366
  return [];
353
367
  }
354
368
 
369
+ async function readLocalChannelConfig(
370
+ cwd: string,
371
+ type: string,
372
+ provider: string,
373
+ name: string,
374
+ ): Promise<AgentChannel | null> {
375
+ const capsule = await loadAgentCapsule(cwd);
376
+ return (
377
+ (capsule.config.channels ?? []).find(
378
+ (channel) => channel.name === name && channel.type === type && channel.provider === provider,
379
+ ) ?? null
380
+ );
381
+ }
382
+
383
+ function channelLimitsForApi(limits: NonNullable<AgentChannel["limits"]>): {
384
+ messages_per_day?: number;
385
+ usd_per_day?: number;
386
+ } {
387
+ return {
388
+ ...(limits.messagesPerDay ? { messages_per_day: limits.messagesPerDay } : {}),
389
+ ...(limits.usdPerDay ? { usd_per_day: limits.usdPerDay } : {}),
390
+ };
391
+ }
392
+
355
393
  async function channelTestBody(
356
394
  channel: CloudChannel,
357
395
  messageFlag: string | boolean | undefined,
@@ -0,0 +1,136 @@
1
+ import {
2
+ addKnowledgeSourceFromCwd,
3
+ listKnowledgeSourcesFromCwd,
4
+ syncKnowledgeSourcesFromCwd,
5
+ } from "../../runtime/knowledge/ingest";
6
+ import { searchKnowledgeFromCwd } from "../../runtime/knowledge/retrieve";
7
+ import type { ParsedArgs } from "../args";
8
+
9
+ export async function handleKnowledgeCommand(args: ParsedArgs): Promise<void> {
10
+ const [subcommand, ...rest] = args.positional;
11
+
12
+ if (subcommand === "add") {
13
+ const [source] = rest;
14
+
15
+ if (!source) {
16
+ throw new Error("Usage: agentkit knowledge add <path-or-url>");
17
+ }
18
+
19
+ const summary = await addKnowledgeSourceFromCwd(process.cwd(), source);
20
+ printIngestSummary("Knowledge add", summary);
21
+ return;
22
+ }
23
+
24
+ if (subcommand === "sync") {
25
+ const summary = await syncKnowledgeSourcesFromCwd(process.cwd());
26
+ printIngestSummary("Knowledge sync", summary);
27
+ return;
28
+ }
29
+
30
+ if (subcommand === "inspect") {
31
+ const result = await listKnowledgeSourcesFromCwd(process.cwd());
32
+
33
+ console.log("Knowledge sources");
34
+ console.log(`Database: ${result.databasePath}`);
35
+
36
+ if (result.sources.length === 0) {
37
+ console.log("No knowledge sources indexed.");
38
+ return;
39
+ }
40
+
41
+ console.log("");
42
+ console.log("source\tstatus\tchunks\tupdated_at\ttitle");
43
+
44
+ for (const source of result.sources) {
45
+ console.log(
46
+ [
47
+ source.source,
48
+ source.status,
49
+ String(source.chunkCount),
50
+ source.updatedAt,
51
+ source.title ?? "",
52
+ ].join("\t"),
53
+ );
54
+ }
55
+
56
+ return;
57
+ }
58
+
59
+ if (subcommand === "search") {
60
+ const query = rest.join(" ").trim();
61
+
62
+ if (!query) {
63
+ throw new Error("Usage: agentkit knowledge search <query> [--top-k <number>]");
64
+ }
65
+
66
+ const topK = parseTopK(args.flags["top-k"] ?? args.flags.topK);
67
+ const result = await searchKnowledgeFromCwd(process.cwd(), query, { topK });
68
+
69
+ console.log("Knowledge search");
70
+ console.log(`Database: ${result.databasePath}`);
71
+ console.log(`Query: ${query}`);
72
+
73
+ if (result.results.length === 0) {
74
+ console.log("No matching knowledge chunks found.");
75
+ return;
76
+ }
77
+
78
+ console.log("");
79
+
80
+ for (const [index, item] of result.results.entries()) {
81
+ const source = [
82
+ item.source.path,
83
+ item.source.section ? `section: ${item.source.section}` : null,
84
+ item.source.locator,
85
+ ].filter(Boolean).join(" | ");
86
+
87
+ console.log(`${index + 1}. ${source}`);
88
+ console.log(` score: ${item.score}`);
89
+ console.log(` ${oneLine(item.content)}`);
90
+ }
91
+
92
+ return;
93
+ }
94
+
95
+ throw new Error(
96
+ "Usage: agentkit knowledge add <path-or-url> | sync | inspect | search <query> [--top-k <number>]",
97
+ );
98
+ }
99
+
100
+ function printIngestSummary(label: string, summary: Awaited<ReturnType<typeof addKnowledgeSourceFromCwd>>): void {
101
+ console.log(label);
102
+ console.log(`Database: ${summary.databasePath}`);
103
+ console.log(`${summary.indexed} indexed, ${summary.skipped} skipped, ${summary.failed} failed, ${summary.chunksCreated} chunks`);
104
+
105
+ if (summary.results.length > 0) {
106
+ console.log("");
107
+ console.log("source\tstatus\tchunks\tmessage");
108
+
109
+ for (const result of summary.results) {
110
+ console.log([result.source, result.status, String(result.chunksCreated), result.message].join("\t"));
111
+ }
112
+ }
113
+
114
+ if (summary.failed > 0) {
115
+ process.exitCode = 1;
116
+ }
117
+ }
118
+
119
+ function parseTopK(value: unknown): number | undefined {
120
+ if (value === undefined) {
121
+ return undefined;
122
+ }
123
+
124
+ const topK = Number(value);
125
+
126
+ if (!Number.isInteger(topK) || topK <= 0 || topK > 50) {
127
+ throw new Error("knowledge search --top-k must be a positive integer no larger than 50.");
128
+ }
129
+
130
+ return topK;
131
+ }
132
+
133
+ function oneLine(value: string): string {
134
+ const compact = value.replace(/\s+/g, " ").trim();
135
+ return compact.length <= 220 ? compact : `${compact.slice(0, 217)}...`;
136
+ }
@@ -179,6 +179,25 @@ export async function checkDeployReadiness(options: {
179
179
  });
180
180
  }
181
181
 
182
+ if (context.knowledge.enabled) {
183
+ if (context.knowledge.databaseDriver !== "turso") {
184
+ checks.push({
185
+ status: "fail",
186
+ title: "Knowledge storage",
187
+ message: "Hosted Knowledge requires a Turso database so indexed chunks are available to the Cloudflare runtime.",
188
+ action: 'set storage.database.driver: "turso" in agentkit.config.ts before deploying Knowledge',
189
+ });
190
+ } else {
191
+ checks.push({
192
+ status: "pass",
193
+ title: "Knowledge storage",
194
+ message: `Knowledge is configured with ${context.knowledge.sourceCount} source${
195
+ context.knowledge.sourceCount === 1 ? "" : "s"
196
+ } and Turso storage.`,
197
+ });
198
+ }
199
+ }
200
+
182
201
  if (context.userManagedSecrets.length === 0) {
183
202
  checks.push({
184
203
  status: "pass",
package/src/cli/help.ts CHANGED
@@ -21,6 +21,8 @@ Start:
21
21
  agentkit inspect
22
22
 
23
23
  Build:
24
+ agentkit knowledge add <path>
25
+ agentkit knowledge search <query>
24
26
  agentkit tool <name> [--input <path-or-json>]
25
27
  agentkit db migrate
26
28
  agentkit eval run
@@ -53,6 +55,10 @@ Usage:
53
55
  agentkit chat-ui --deploy [--port <number>] [--token-file <path>]
54
56
  agentkit chat --message <text> [--conversation-id <id>]
55
57
  agentkit tool <name> [--input <path-or-json>]
58
+ agentkit knowledge add <path-or-url>
59
+ agentkit knowledge sync
60
+ agentkit knowledge inspect
61
+ agentkit knowledge search <query> [--top-k <number>]
56
62
  agentkit db migrate
57
63
  agentkit db reset --yes
58
64
  agentkit db shell
@@ -106,6 +112,7 @@ Commands:
106
112
  chat-ui Run a local chat UI, optionally pointed at a hosted deploy
107
113
  chat Send one message to the local Agent Capsule
108
114
  tool Run one registered tool directly
115
+ knowledge Add, sync, inspect, and search local Knowledge sources
109
116
  db Manage the local SQLite database
110
117
  eval Run local eval files
111
118
  conversations List and inspect stored local conversations
@@ -142,6 +149,7 @@ export function renderCommandHelp(command: string): string {
142
149
  "chat-ui": "agentkit chat-ui --deploy [--port <number>] [--token-file <path>]",
143
150
  chat: 'agentkit chat --message "hello" [--conversation-id <id>]',
144
151
  tool: "agentkit tool <name> [--input <path-or-json>]",
152
+ knowledge: "agentkit knowledge add <path-or-url> | sync | inspect | search <query> [--top-k <number>]",
145
153
  deploy:
146
154
  'agentkit deploy [--smoke "hello"] | agentkit deploy smoke [--message "hello"] | agentkit deploy doctor | status | pause | resume',
147
155
  secret:
package/src/cli/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { spawn } from "node:child_process";
2
+ import { existsSync } from "node:fs";
2
3
  import { mkdir, readFile, writeFile } from "node:fs/promises";
3
4
  import { dirname, join, relative, resolve } from "node:path";
4
5
 
@@ -61,6 +62,7 @@ import {
61
62
  import { renderCommandHelp, renderCommandReference, renderHelp } from "./help";
62
63
  import { runInheritedCommand, waitForShutdown } from "./process";
63
64
  import { handleChannelsCommand } from "./commands/channels";
65
+ import { handleKnowledgeCommand } from "./commands/knowledge";
64
66
 
65
67
  async function main() {
66
68
  const args = parseArgs(process.argv.slice(2));
@@ -118,6 +120,7 @@ async function main() {
118
120
  console.log(" npm install");
119
121
  }
120
122
  console.log(" open this folder in Codex or Claude Code");
123
+ console.log(" coding agents start at skills/agentkit-capsule/SKILL.md");
121
124
  console.log(' ask: "Develop an appointment and intake agent for an ophthalmology office"');
122
125
  console.log(' npm run chat -- --message "hello"');
123
126
  console.log(" npm run dev");
@@ -340,6 +343,11 @@ async function main() {
340
343
  throw new Error("Usage: agentkit conversations list | agentkit conversations show <conversation-id>");
341
344
  }
342
345
 
346
+ if (args.command === "knowledge") {
347
+ await handleKnowledgeCommand(args);
348
+ return;
349
+ }
350
+
343
351
  if (args.command === "inspect") {
344
352
  const state = await inspectAgentCapsule(process.cwd());
345
353
  console.log(JSON.stringify(state, null, 2));
@@ -718,15 +726,17 @@ async function main() {
718
726
 
719
727
  const root = await findAgentCapsuleRoot(process.cwd());
720
728
  const docs = findAgentKitDocs();
721
- console.log(renderHandoffPrompt({ target, root, docsFull: docs.llmsFull, goal: goalParts.join(" ").trim() }));
729
+ console.log(renderHandoffPrompt({ target, root, docsLlms: docs.llms, goal: goalParts.join(" ").trim() }));
722
730
  return;
723
731
  }
724
732
 
725
733
  throw new Error(`Unknown command: ${args.command}`);
726
734
  }
727
735
 
728
- function renderHandoffPrompt(input: { target: string; root: string; docsFull: string; goal: string }): string {
736
+ function renderHandoffPrompt(input: { target: string; root: string; docsLlms: string; goal: string }): string {
729
737
  const agentName = input.target === "claude" ? "Claude Code" : "Codex";
738
+ const skillRouter = join(input.root, "skills/agentkit-capsule/SKILL.md");
739
+ const skillLine = existsSync(skillRouter) ? `- ${skillRouter}\n` : "";
730
740
  const goal = input.goal
731
741
  ? `Owner request:
732
742
  ${input.goal}`
@@ -740,12 +750,14 @@ ${input.root}
740
750
  Read these files first:
741
751
 
742
752
  - ${join(input.root, "AGENTKIT.md")}
743
- - ${input.docsFull}
753
+ ${skillLine}- ${input.docsLlms}
744
754
 
745
755
  ${goal}
746
756
 
747
757
  Rules:
748
- - Follow the AgentKit contract in llms-full.txt.
758
+ - Start with the AgentKit capsule skill when it exists.
759
+ - Use llms.txt as the docs router.
760
+ - Read llms-full.txt only when a skill or ambiguous framework behavior requires the complete contract.
749
761
  - Treat the owner's request as the brief.
750
762
  - Build or update the agent inside this capsule as far as you can without asking for obvious restatements.
751
763
  - Do not wait for a wizard or recipe; implement directly in the capsule files.
@@ -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
  }