@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
|
@@ -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`,
|
|
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
|
|
package/docs/llms-full.txt
CHANGED
|
@@ -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
|
|
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.
|
|
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,
|
|
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;
|
|
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.
|
|
753
|
+
${skillLine}- ${input.docsLlms}
|
|
744
754
|
|
|
745
755
|
${goal}
|
|
746
756
|
|
|
747
757
|
Rules:
|
|
748
|
-
-
|
|
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.
|
package/src/cloud/artifact.ts
CHANGED
|
@@ -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
|
}
|