@andreprado/agentkit 0.1.0-alpha.5 → 0.1.0-alpha.7
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 +9 -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 +17 -0
- package/docs/guides/connect-whatsapp-zapster.md +16 -0
- package/docs/guides/create-agent.md +10 -1
- package/docs/guides/run-evals.md +36 -1
- package/docs/llms-full.txt +90 -1
- package/docs/llms.txt +9 -2
- package/package.json +2 -1
- package/src/cli/cloud-client.ts +10 -2
- package/src/cli/commands/channels.ts +67 -3
- package/src/cli/commands/knowledge.ts +136 -0
- package/src/cli/deploy-chat-ui.ts +7 -0
- package/src/cli/deploy-readiness.ts +19 -0
- package/src/cli/help.ts +26 -2
- package/src/cli/index.ts +140 -8
- 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 +142 -1
- package/src/providers/pi.ts +1 -1
- package/src/providers/test.ts +1 -1
- 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 +175 -0
- package/src/runtime/core/manifest.ts +37 -0
- package/src/runtime/database.ts +93 -2
- package/src/runtime/db-commands.ts +9 -0
- package/src/runtime/deploy-readiness.ts +12 -0
- package/src/runtime/dev-server.ts +201 -11
- package/src/runtime/evals.ts +210 -20
- package/src/runtime/inspect.ts +39 -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/spec.ts +152 -0
- package/src/runtime/sync.ts +144 -0
- package/src/runtime/targets/cloudflare/build.ts +514 -4
- package/src/runtime/tools.ts +121 -1
- package/src/runtime/traces.ts +41 -0
- package/src/storage/sqlite.ts +141 -0
- package/src/templates/blank.ts +16 -5
- package/src/templates/dentista.ts +17 -2
- package/src/templates/skills/agentkit-build-agent/SKILL.md +51 -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 +45 -0
- package/src/templates/skills/agentkit-database/templates/appointments.schema.sql +15 -0
- package/src/templates/skills/agentkit-database/templates/leads.schema.sql +17 -0
- package/src/templates/skills/agentkit-deploy/SKILL.md +44 -0
- package/src/templates/skills/agentkit-evals/SKILL.md +60 -0
- package/src/templates/skills/agentkit-evals/templates/multi-turn.eval.md +22 -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 +15 -4
|
@@ -51,6 +51,21 @@ export default defineAgent({
|
|
|
51
51
|
});
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
To answer once after a burst of Telegram messages, enable channel buffering:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
telegramChannel({
|
|
58
|
+
name: "support-telegram",
|
|
59
|
+
buffer: {
|
|
60
|
+
mode: "debounce",
|
|
61
|
+
quietWindowMs: 1500,
|
|
62
|
+
maxWaitMs: 8000,
|
|
63
|
+
maxMessages: 20,
|
|
64
|
+
maxChars: 8000,
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
54
69
|
## Setup Behavior
|
|
55
70
|
|
|
56
71
|
`agentkit channels setup support-telegram` is read-only and prints the webhook URL.
|
|
@@ -61,6 +76,8 @@ export default defineAgent({
|
|
|
61
76
|
- `secret_token`: `TELEGRAM_WEBHOOK_SECRET`;
|
|
62
77
|
- `allowed_updates`: `["message"]`.
|
|
63
78
|
|
|
79
|
+
Against AgentKit Cloud, `--apply` runs through the Cloud API and uses managed hosted secrets. The legacy local fallback only uses `TELEGRAM_BOT_TOKEN` and `TELEGRAM_WEBHOOK_SECRET` from the shell when the Cloud API does not expose channel setup.
|
|
80
|
+
|
|
64
81
|
## Safety Rules
|
|
65
82
|
|
|
66
83
|
- Use `--apply` only when real Telegram secrets are present.
|
|
@@ -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,16 @@ 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
|
+
|
|
89
|
+
After the owner gives the general idea, the coding agent should create or update the implementation contract itself:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
npm run agentkit -- spec init --brief "Develop an appointment and intake agent for an ophthalmology office."
|
|
93
|
+
npm run agentkit -- spec check
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`AGENT_SPEC.md` is an internal working contract for the coding agent. It is not a form the owner must fill before work starts.
|
|
88
97
|
|
|
89
98
|
Optional shortcut when copying a prompt into another coding agent:
|
|
90
99
|
|
package/docs/guides/run-evals.md
CHANGED
|
@@ -22,6 +22,15 @@ Run evals:
|
|
|
22
22
|
agentkit eval run
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
Create an eval from a stored conversation:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
agentkit conversations list
|
|
29
|
+
agentkit conversations trace <conversation-id>
|
|
30
|
+
agentkit eval from-conversation <conversation-id>
|
|
31
|
+
agentkit eval run
|
|
32
|
+
```
|
|
33
|
+
|
|
25
34
|
## Files Created Or Edited
|
|
26
35
|
|
|
27
36
|
Create or edit:
|
|
@@ -62,7 +71,33 @@ matches_regex
|
|
|
62
71
|
persisted_tool_call
|
|
63
72
|
```
|
|
64
73
|
|
|
65
|
-
|
|
74
|
+
Multi-turn conversation evals use `turns`:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
export default {
|
|
78
|
+
name: "buyer under budget",
|
|
79
|
+
turns: [
|
|
80
|
+
{
|
|
81
|
+
input: "I want a house up to 600k near Pinheiros.",
|
|
82
|
+
expect: {
|
|
83
|
+
persisted_tool_call: {
|
|
84
|
+
name: "buscar_imoveis",
|
|
85
|
+
status: "completed",
|
|
86
|
+
input: { maxPrice: 600000 },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
input: "Show me the best two.",
|
|
92
|
+
expect: {
|
|
93
|
+
contains: ["Pinheiros", "R$"],
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`persisted_tool_call` validates the tool call saved in local SQLite `tool_calls`, not a provider-specific raw response shape. It can be a tool name string or an object with `name`, `input`, `output`, `rendered`, `status`, and/or `visibility`.
|
|
66
101
|
|
|
67
102
|
Use response assertions and persisted tool assertions together when internal operational output must not leak:
|
|
68
103
|
|
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.7",
|
|
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"
|
package/src/cli/cloud-client.ts
CHANGED
|
@@ -153,9 +153,13 @@ export async function cloudPost<T>(apiUrl: string, path: string, body: unknown):
|
|
|
153
153
|
return payload as T;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
export function cloudFetch(apiUrl: string, path: string, init: RequestInit = {}): Promise<Response> {
|
|
156
|
+
export async function cloudFetch(apiUrl: string, path: string, init: RequestInit = {}): Promise<Response> {
|
|
157
157
|
const headers = new Headers(init.headers);
|
|
158
|
-
const
|
|
158
|
+
const auth = await readCloudAuth();
|
|
159
|
+
const apiToken =
|
|
160
|
+
auth?.source === "file" && !cloudAuthMatchesApiUrl(auth, apiUrl)
|
|
161
|
+
? undefined
|
|
162
|
+
: auth?.token;
|
|
159
163
|
|
|
160
164
|
if (apiToken && !headers.has("Authorization")) {
|
|
161
165
|
headers.set("Authorization", `Bearer ${apiToken}`);
|
|
@@ -167,6 +171,10 @@ export function cloudFetch(apiUrl: string, path: string, init: RequestInit = {})
|
|
|
167
171
|
});
|
|
168
172
|
}
|
|
169
173
|
|
|
174
|
+
function cloudAuthMatchesApiUrl(auth: CloudAuthConfig, apiUrl: string): boolean {
|
|
175
|
+
return parseCloudApiUrl(auth.apiUrl) === parseCloudApiUrl(apiUrl);
|
|
176
|
+
}
|
|
177
|
+
|
|
170
178
|
export async function cloudApiRequest(apiUrl: string, path: string, init: RequestInit): Promise<unknown> {
|
|
171
179
|
const auth = await readCloudAuth();
|
|
172
180
|
const headers = new Headers(init.headers);
|
|
@@ -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),
|
|
@@ -94,7 +99,7 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
|
|
|
94
99
|
printChannelStatus(channel);
|
|
95
100
|
if (channel.type === "telegram") {
|
|
96
101
|
if (args.flags.apply) {
|
|
97
|
-
await applyTelegramWebhook(channel);
|
|
102
|
+
await applyTelegramWebhook(channel, args.flags.api);
|
|
98
103
|
console.log("Telegram webhook updated.");
|
|
99
104
|
} else {
|
|
100
105
|
console.log(`Setup: call Telegram setWebhook with ${channel.webhook_url}`);
|
|
@@ -196,7 +201,23 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
|
|
|
196
201
|
);
|
|
197
202
|
}
|
|
198
203
|
|
|
199
|
-
async function applyTelegramWebhook(
|
|
204
|
+
async function applyTelegramWebhook(
|
|
205
|
+
channel: CloudChannel,
|
|
206
|
+
apiFlag: string | boolean | undefined,
|
|
207
|
+
): Promise<void> {
|
|
208
|
+
try {
|
|
209
|
+
await cloudPost(
|
|
210
|
+
parseCloudApiUrl(apiFlag),
|
|
211
|
+
`/v1/channels/${encodeURIComponent(channel.id)}/setup`,
|
|
212
|
+
{},
|
|
213
|
+
);
|
|
214
|
+
return;
|
|
215
|
+
} catch (error) {
|
|
216
|
+
if (!shouldFallbackToLocalTelegramSetup(error)) {
|
|
217
|
+
throw error;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
200
221
|
const botToken = process.env.TELEGRAM_BOT_TOKEN;
|
|
201
222
|
const secretToken = process.env.TELEGRAM_WEBHOOK_SECRET;
|
|
202
223
|
|
|
@@ -227,6 +248,16 @@ async function applyTelegramWebhook(channel: CloudChannel): Promise<void> {
|
|
|
227
248
|
}
|
|
228
249
|
}
|
|
229
250
|
|
|
251
|
+
function shouldFallbackToLocalTelegramSetup(error: unknown): boolean {
|
|
252
|
+
if (!(error instanceof Error)) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return /Unknown AgentKit Cloud route|does not support channel delivery records yet|does not support channel resources yet/.test(
|
|
257
|
+
error.message,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
230
261
|
function isTelegramOk(payload: unknown): payload is { ok: true } {
|
|
231
262
|
return Boolean(payload && typeof payload === "object" && "ok" in payload && payload.ok === true);
|
|
232
263
|
}
|
|
@@ -280,6 +311,15 @@ function printChannelStatus(channel: CloudChannel): void {
|
|
|
280
311
|
for (const secret of channel.required_secrets) {
|
|
281
312
|
console.log(` ${secret.name}: ${secret.status}`);
|
|
282
313
|
}
|
|
314
|
+
if (channel.buffer) {
|
|
315
|
+
if (channel.buffer.mode === "off") {
|
|
316
|
+
console.log("Buffer: off");
|
|
317
|
+
} else {
|
|
318
|
+
console.log(
|
|
319
|
+
`Buffer: debounce quiet=${channel.buffer.quietWindowMs}ms max_wait=${channel.buffer.maxWaitMs}ms max_messages=${channel.buffer.maxMessages} max_chars=${channel.buffer.maxChars}`,
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
283
323
|
}
|
|
284
324
|
|
|
285
325
|
async function printChannelDeliverySummary(
|
|
@@ -352,6 +392,30 @@ function defaultChannelSecretsForCli(type: string, provider: string): string[] {
|
|
|
352
392
|
return [];
|
|
353
393
|
}
|
|
354
394
|
|
|
395
|
+
async function readLocalChannelConfig(
|
|
396
|
+
cwd: string,
|
|
397
|
+
type: string,
|
|
398
|
+
provider: string,
|
|
399
|
+
name: string,
|
|
400
|
+
): Promise<AgentChannel | null> {
|
|
401
|
+
const capsule = await loadAgentCapsule(cwd);
|
|
402
|
+
return (
|
|
403
|
+
(capsule.config.channels ?? []).find(
|
|
404
|
+
(channel) => channel.name === name && channel.type === type && channel.provider === provider,
|
|
405
|
+
) ?? null
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function channelLimitsForApi(limits: NonNullable<AgentChannel["limits"]>): {
|
|
410
|
+
messages_per_day?: number;
|
|
411
|
+
usd_per_day?: number;
|
|
412
|
+
} {
|
|
413
|
+
return {
|
|
414
|
+
...(limits.messagesPerDay ? { messages_per_day: limits.messagesPerDay } : {}),
|
|
415
|
+
...(limits.usdPerDay ? { usd_per_day: limits.usdPerDay } : {}),
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
355
419
|
async function channelTestBody(
|
|
356
420
|
channel: CloudChannel,
|
|
357
421
|
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
|
+
}
|
|
@@ -335,6 +335,13 @@ function renderDeployChatUi(input: { deployUrl: string }): string {
|
|
|
335
335
|
messages.scrollTop = messages.scrollHeight;
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
+
textarea.addEventListener("keydown", (event) => {
|
|
339
|
+
if (event.key !== "Enter" || event.shiftKey || event.isComposing) return;
|
|
340
|
+
event.preventDefault();
|
|
341
|
+
if (send.disabled) return;
|
|
342
|
+
form.requestSubmit();
|
|
343
|
+
});
|
|
344
|
+
|
|
338
345
|
form.addEventListener("submit", async (event) => {
|
|
339
346
|
event.preventDefault();
|
|
340
347
|
const message = textarea.value.trim();
|
|
@@ -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",
|