@andreprado/agentkit 0.1.0-alpha.4 → 0.1.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +5 -0
  2. package/docs/guides/add-channel.md +25 -0
  3. package/docs/guides/add-knowledge.md +134 -0
  4. package/docs/guides/agentkit-skills-architecture.md +471 -0
  5. package/docs/guides/channels-production-handoff.md +2 -0
  6. package/docs/guides/connect-telegram.md +15 -0
  7. package/docs/guides/connect-whatsapp-zapster.md +16 -0
  8. package/docs/guides/create-agent.md +1 -1
  9. package/docs/llms-full.txt +90 -1
  10. package/docs/llms.txt +9 -2
  11. package/package.json +2 -1
  12. package/src/cli/args.ts +36 -0
  13. package/src/cli/cloud-client.ts +257 -0
  14. package/src/cli/commands/channels.ts +459 -0
  15. package/src/cli/commands/knowledge.ts +136 -0
  16. package/src/cli/constants.ts +4 -0
  17. package/src/cli/deploy-chat-ui.ts +385 -0
  18. package/src/cli/deploy-readiness.ts +348 -0
  19. package/src/cli/flags.ts +162 -0
  20. package/src/cli/help.ts +166 -0
  21. package/src/cli/index.ts +219 -1912
  22. package/src/cli/process.ts +31 -0
  23. package/src/cloud/artifact.ts +92 -1
  24. package/src/cloud/contracts.ts +16 -0
  25. package/src/create-project.ts +38 -6
  26. package/src/index.ts +98 -0
  27. package/src/runtime/channel-buffer.ts +30 -0
  28. package/src/runtime/channels.ts +1 -0
  29. package/src/runtime/chat.ts +21 -2
  30. package/src/runtime/config.ts +167 -0
  31. package/src/runtime/core/manifest.ts +37 -0
  32. package/src/runtime/deploy-readiness.ts +12 -0
  33. package/src/runtime/dev-server.ts +159 -10
  34. package/src/runtime/inspect.ts +34 -0
  35. package/src/runtime/knowledge/chunk.ts +333 -0
  36. package/src/runtime/knowledge/config.ts +135 -0
  37. package/src/runtime/knowledge/embeddings.ts +133 -0
  38. package/src/runtime/knowledge/ingest.ts +521 -0
  39. package/src/runtime/knowledge/prompt-policy.ts +30 -0
  40. package/src/runtime/knowledge/retrieve.ts +283 -0
  41. package/src/runtime/knowledge/schema.ts +56 -0
  42. package/src/runtime/knowledge/tool.ts +64 -0
  43. package/src/runtime/knowledge/vector.ts +258 -0
  44. package/src/runtime/targets/cloudflare/build.ts +469 -4
  45. package/src/storage/sqlite.ts +5 -0
  46. package/src/templates/blank.ts +8 -4
  47. package/src/templates/dentista.ts +3 -1
  48. package/src/templates/skills/agentkit-build-agent/SKILL.md +49 -0
  49. package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +20 -0
  50. package/src/templates/skills/agentkit-build-agent/templates/sales-qualifier.instructions.md +17 -0
  51. package/src/templates/skills/agentkit-build-agent/templates/support-agent.instructions.md +16 -0
  52. package/src/templates/skills/agentkit-capsule/SKILL.md +62 -0
  53. package/src/templates/skills/agentkit-capsule/references/docs-router.md +15 -0
  54. package/src/templates/skills/agentkit-channels/SKILL.md +62 -0
  55. package/src/templates/skills/agentkit-channels/references/channel-buffering.md +58 -0
  56. package/src/templates/skills/agentkit-channels/references/channel-debugging.md +37 -0
  57. package/src/templates/skills/agentkit-channels/references/telegram.md +37 -0
  58. package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +37 -0
  59. package/src/templates/skills/agentkit-database/SKILL.md +42 -0
  60. package/src/templates/skills/agentkit-database/templates/appointments.schema.sql +15 -0
  61. package/src/templates/skills/agentkit-database/templates/leads.schema.sql +17 -0
  62. package/src/templates/skills/agentkit-deploy/SKILL.md +44 -0
  63. package/src/templates/skills/agentkit-evals/SKILL.md +31 -0
  64. package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +14 -0
  65. package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +14 -0
  66. package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +18 -0
  67. package/src/templates/skills/agentkit-knowledge/SKILL.md +40 -0
  68. package/src/templates/skills/agentkit-knowledge/templates/faq.md +14 -0
  69. package/src/templates/skills/agentkit-knowledge/templates/policies.md +14 -0
  70. package/src/templates/skills/agentkit-knowledge/templates/prices.csv +3 -0
  71. package/src/templates/skills/agentkit-prompts/SKILL.md +45 -0
  72. package/src/templates/skills/agentkit-prompts/templates/knowledge-grounded-faq.instructions.md +11 -0
  73. package/src/templates/skills/agentkit-provider/SKILL.md +57 -0
  74. package/src/templates/skills/agentkit-security/SKILL.md +55 -0
  75. package/src/templates/skills/agentkit-tools/SKILL.md +36 -0
  76. package/src/templates/skills/agentkit-tools/examples/database-write.tool.md +35 -0
  77. package/src/templates/skills/agentkit-tools/examples/eval-safe-external-action.tool.md +37 -0
  78. package/src/templates/skills/agentkit-tools/examples/lookup-order.tool.md +46 -0
  79. package/src/templates/skills/agentkit-troubleshooting/SKILL.md +52 -0
  80. package/src/templates/support.ts +8 -4
@@ -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.4",
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"
@@ -0,0 +1,36 @@
1
+ export type ParsedArgs = {
2
+ command?: string;
3
+ positional: string[];
4
+ flags: Record<string, string | boolean>;
5
+ };
6
+
7
+ export function parseArgs(argv: string[]): ParsedArgs {
8
+ const [command, ...rest] = argv;
9
+ const positional: string[] = [];
10
+ const flags: Record<string, string | boolean> = {};
11
+
12
+ for (let index = 0; index < rest.length; index += 1) {
13
+ const value = rest[index];
14
+
15
+ if (!value.startsWith("--")) {
16
+ positional.push(value);
17
+ continue;
18
+ }
19
+
20
+ const name = value.slice(2);
21
+ const next = rest[index + 1];
22
+
23
+ if (next && !next.startsWith("--")) {
24
+ flags[name] = next;
25
+ index += 1;
26
+ } else {
27
+ flags[name] = true;
28
+ }
29
+ }
30
+
31
+ return { command, positional, flags };
32
+ }
33
+
34
+ export function isHelpRequested(args: ParsedArgs): boolean {
35
+ return args.flags.help === true || args.flags.h === true || args.positional.includes("--help") || args.positional.includes("-h");
36
+ }
@@ -0,0 +1,257 @@
1
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+
5
+ import { AgentKitError } from "../runtime/errors";
6
+ import { defaultAgentKitCloudApiUrl } from "./constants";
7
+ import { parseCloudApiUrl } from "./flags";
8
+
9
+ export type CloudAuthConfig = {
10
+ apiUrl: string;
11
+ token: string;
12
+ source?: "environment" | "file";
13
+ };
14
+
15
+ export type CloudMeResponse = {
16
+ account?: {
17
+ email?: string;
18
+ status?: string;
19
+ };
20
+ };
21
+
22
+ export type CloudCapabilitiesResponse = {
23
+ capabilities?: Array<{
24
+ name?: string;
25
+ status?: string;
26
+ }>;
27
+ };
28
+
29
+ export type CloudLimitsResponse = {
30
+ online_agents?: {
31
+ account?: {
32
+ used?: number | null;
33
+ limit?: number | null;
34
+ };
35
+ global?: {
36
+ used?: number | null;
37
+ limit?: number | null;
38
+ };
39
+ };
40
+ };
41
+
42
+ export type DeployAccessTokenCreateResponse = {
43
+ access_token?: {
44
+ id?: string;
45
+ deploy_id?: string;
46
+ name?: string;
47
+ token?: string;
48
+ };
49
+ };
50
+
51
+ export type DeployAccessTokenListResponse = {
52
+ access_tokens?: Array<{
53
+ id?: string;
54
+ deploy_id?: string;
55
+ name?: string;
56
+ }>;
57
+ };
58
+
59
+ export type CloudSecretsResponse = {
60
+ secrets?: Array<{
61
+ name?: string;
62
+ status?: string;
63
+ updated_at?: string;
64
+ }>;
65
+ };
66
+
67
+ export function cloudAuthPath(): string {
68
+ return join(homedir(), ".agentkit", "cloud.json");
69
+ }
70
+
71
+ export async function readCloudAuth(): Promise<CloudAuthConfig | null> {
72
+ if (process.env.AGENTKIT_CLOUD_API_TOKEN) {
73
+ return {
74
+ apiUrl: process.env.AGENTKIT_CLOUD_API_URL ?? defaultAgentKitCloudApiUrl,
75
+ token: process.env.AGENTKIT_CLOUD_API_TOKEN,
76
+ source: "environment",
77
+ };
78
+ }
79
+
80
+ try {
81
+ const parsed = JSON.parse(await readFile(cloudAuthPath(), "utf8"));
82
+
83
+ if (parsed && typeof parsed === "object" && typeof parsed.token === "string") {
84
+ return {
85
+ apiUrl: typeof parsed.apiUrl === "string" ? parsed.apiUrl : defaultAgentKitCloudApiUrl,
86
+ token: parsed.token,
87
+ source: "file",
88
+ };
89
+ }
90
+ } catch {
91
+ return null;
92
+ }
93
+
94
+ return null;
95
+ }
96
+
97
+ export async function writeCloudAuth(config: CloudAuthConfig): Promise<void> {
98
+ await mkdir(join(homedir(), ".agentkit"), { recursive: true });
99
+ await writeFile(cloudAuthPath(), `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
100
+ }
101
+
102
+ export async function clearCloudAuth(): Promise<void> {
103
+ await rm(cloudAuthPath(), { force: true });
104
+ }
105
+
106
+ export async function resolveCloudApiUrl(flag: string | boolean | undefined): Promise<string> {
107
+ if (flag !== undefined) {
108
+ return parseCloudApiUrl(flag);
109
+ }
110
+
111
+ const auth = await readCloudAuth();
112
+ return auth?.apiUrl ?? parseCloudApiUrl(undefined);
113
+ }
114
+
115
+ export async function verifyCloudLogin(
116
+ apiUrl: string,
117
+ token: string,
118
+ ): Promise<{ me: CloudMeResponse; capabilities: CloudCapabilitiesResponse }> {
119
+ const me = await cloudApiRequestWithToken<CloudMeResponse>(apiUrl, "/v1/me", token, { method: "GET" });
120
+ const capabilities = await cloudApiRequestWithToken<CloudCapabilitiesResponse>(
121
+ apiUrl,
122
+ "/v1/me/capabilities",
123
+ token,
124
+ { method: "GET" },
125
+ );
126
+
127
+ return { me, capabilities };
128
+ }
129
+
130
+ export async function cloudGet<T>(apiUrl: string, path: string): Promise<T> {
131
+ const response = await cloudFetch(apiUrl, path);
132
+ const payload = await response.json();
133
+
134
+ if (!response.ok) {
135
+ throw new Error(readCloudError(payload, response.status));
136
+ }
137
+
138
+ return payload as T;
139
+ }
140
+
141
+ export async function cloudPost<T>(apiUrl: string, path: string, body: unknown): Promise<T> {
142
+ const response = await cloudFetch(apiUrl, path, {
143
+ method: "POST",
144
+ headers: { "Content-Type": "application/json" },
145
+ body: JSON.stringify(body),
146
+ });
147
+ const payload = await response.json();
148
+
149
+ if (!response.ok) {
150
+ throw new Error(readCloudError(payload, response.status));
151
+ }
152
+
153
+ return payload as T;
154
+ }
155
+
156
+ export function cloudFetch(apiUrl: string, path: string, init: RequestInit = {}): Promise<Response> {
157
+ const headers = new Headers(init.headers);
158
+ const apiToken = process.env.AGENTKIT_CLOUD_API_TOKEN;
159
+
160
+ if (apiToken && !headers.has("Authorization")) {
161
+ headers.set("Authorization", `Bearer ${apiToken}`);
162
+ }
163
+
164
+ return fetch(new URL(path, parseCloudApiUrl(apiUrl)).href, {
165
+ ...init,
166
+ headers,
167
+ });
168
+ }
169
+
170
+ export async function cloudApiRequest(apiUrl: string, path: string, init: RequestInit): Promise<unknown> {
171
+ const auth = await readCloudAuth();
172
+ const headers = new Headers(init.headers);
173
+
174
+ if (!headers.has("Content-Type") && init.body !== undefined) {
175
+ headers.set("Content-Type", "application/json");
176
+ }
177
+
178
+ if (auth?.token && !headers.has("Authorization")) {
179
+ headers.set("Authorization", `Bearer ${auth.token}`);
180
+ }
181
+
182
+ const response = await fetch(new URL(path, parseCloudApiUrl(apiUrl)).href, {
183
+ ...init,
184
+ headers,
185
+ });
186
+ const payload = await response.json().catch(() => null);
187
+
188
+ if (!response.ok) {
189
+ const code =
190
+ payload && typeof payload === "object" && "error" in payload
191
+ ? String((payload as { error?: { code?: unknown } }).error?.code ?? "cloud_request_failed")
192
+ : "cloud_request_failed";
193
+ let message = readCloudError(payload, response.status);
194
+
195
+ if (code === "auth_token_invalid") {
196
+ message = `${message} Token source: ${auth ? formatCloudAuthSource(auth) : "none"}. Run: agentkit login --token <token>`;
197
+ }
198
+
199
+ throw new AgentKitError(code, message);
200
+ }
201
+
202
+ return payload;
203
+ }
204
+
205
+ export async function cloudApiRequestWithToken<T>(
206
+ apiUrl: string,
207
+ path: string,
208
+ token: string,
209
+ init: RequestInit,
210
+ ): Promise<T> {
211
+ const headers = new Headers(init.headers);
212
+
213
+ if (!headers.has("Content-Type") && init.body !== undefined) {
214
+ headers.set("Content-Type", "application/json");
215
+ }
216
+
217
+ headers.set("Authorization", `Bearer ${token}`);
218
+
219
+ const response = await fetch(new URL(path, parseCloudApiUrl(apiUrl)).href, {
220
+ ...init,
221
+ headers,
222
+ });
223
+ const payload = await response.json().catch(() => null);
224
+
225
+ if (!response.ok) {
226
+ const code =
227
+ payload && typeof payload === "object" && "error" in payload
228
+ ? String((payload as { error?: { code?: unknown } }).error?.code ?? "cloud_request_failed")
229
+ : "cloud_request_failed";
230
+ throw new AgentKitError(code, readCloudError(payload, response.status));
231
+ }
232
+
233
+ return payload as T;
234
+ }
235
+
236
+ export function readCloudError(payload: unknown, status: number): string {
237
+ if (payload && typeof payload === "object" && "error" in payload) {
238
+ const error = (payload as { error?: { message?: unknown } }).error;
239
+ if (typeof error?.message === "string") {
240
+ return error.message;
241
+ }
242
+ }
243
+
244
+ return `AgentKit Cloud request failed with HTTP ${status}.`;
245
+ }
246
+
247
+ export function formatCloudAuthSource(auth: CloudAuthConfig): string {
248
+ if (auth.source === "environment") {
249
+ return "AGENTKIT_CLOUD_API_TOKEN";
250
+ }
251
+
252
+ if (auth.source === "file") {
253
+ return cloudAuthPath();
254
+ }
255
+
256
+ return "AgentKit Cloud auth";
257
+ }