@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
@@ -8,6 +8,8 @@ import { build as esbuild, type Plugin } from "esbuild";
8
8
  import { loadAgentCapsule, type LoadedAgentCapsule } from "../../config";
9
9
  import { buildSharedAgentManifest, type ManifestSchema, type SharedAgentManifest } from "../../core/manifest";
10
10
  import { AgentKitError } from "../../errors";
11
+ import { resolveKnowledgeConfig } from "../../knowledge/config";
12
+ import { KNOWLEDGE_SCHEMA_SQL } from "../../knowledge/schema";
11
13
 
12
14
  export type BuildTarget = "cloudflare";
13
15
 
@@ -34,6 +36,7 @@ export type AgentManifest = {
34
36
  secrets: SharedAgentManifest["secrets"];
35
37
  tools: SharedAgentManifest["tools"];
36
38
  channels: SharedAgentManifest["channels"];
39
+ knowledge: SharedAgentManifest["knowledge"];
37
40
  access: SharedAgentManifest["access"];
38
41
  storage: {
39
42
  driver: "agentkit";
@@ -145,6 +148,14 @@ function validateCloudflareBuild(capsule: LoadedAgentCapsule): void {
145
148
  );
146
149
  }
147
150
 
151
+ const knowledge = resolveKnowledgeConfig(capsule.config);
152
+
153
+ if (knowledge.enabled && capsule.config.storage.database?.driver !== "turso") {
154
+ throw new AgentKitError(
155
+ "build_knowledge_database",
156
+ 'Cloudflare Knowledge deploy requires storage.database.driver: "turso" so chunks and retrieval state are available to the hosted runtime.',
157
+ );
158
+ }
148
159
  }
149
160
 
150
161
  function buildWarnings(capsule: LoadedAgentCapsule): string[] {
@@ -185,6 +196,7 @@ function buildManifest(
185
196
  secrets: shared.secrets,
186
197
  tools: shared.tools,
187
198
  channels: shared.channels,
199
+ knowledge: shared.knowledge,
188
200
  access: shared.access,
189
201
  storage: {
190
202
  driver: "agentkit",
@@ -210,12 +222,26 @@ function buildCloudflareFilesManifest(files: SharedAgentManifest["storage"]["fil
210
222
 
211
223
  async function loadTursoSchema(capsule: LoadedAgentCapsule): Promise<ManifestSchema | null> {
212
224
  const database = capsule.config.storage.database;
225
+ const knowledge = resolveKnowledgeConfig(capsule.config);
226
+ const sources: string[] = [];
227
+
228
+ if (!database || database.driver !== "turso") {
229
+ return null;
230
+ }
231
+
232
+ if (database.schema) {
233
+ sources.push(await readFile(resolve(capsule.root, database.schema), "utf8"));
234
+ }
235
+
236
+ if (knowledge.enabled) {
237
+ sources.push(KNOWLEDGE_SCHEMA_SQL);
238
+ }
213
239
 
214
- if (!database || database.driver !== "turso" || !database.schema) {
240
+ if (sources.length === 0) {
215
241
  return null;
216
242
  }
217
243
 
218
- const source = await readFile(resolve(capsule.root, database.schema), "utf8");
244
+ const source = sources.map((part) => part.trim()).filter(Boolean).join("\n\n") + "\n";
219
245
  const digest = sha256(source);
220
246
  return { source, sha256: digest };
221
247
  }
@@ -361,6 +387,7 @@ export function websiteChannel(input) {
361
387
  access: input.access ?? { mode: "token" },
362
388
  secrets: normalizeChannelSecrets(input.secrets ?? ["AGENTKIT_WEBSITE_CHANNEL_TOKEN"], "website channel"),
363
389
  ...(input.limits ? { limits: input.limits } : {}),
390
+ ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "website channel") } : {}),
364
391
  };
365
392
  }
366
393
 
@@ -372,6 +399,7 @@ export function telegramChannel(input) {
372
399
  secrets: normalizeChannelSecrets(input.secrets ?? ["TELEGRAM_BOT_TOKEN", "TELEGRAM_WEBHOOK_SECRET"], "telegram channel"),
373
400
  allowedUpdates: input.allowedUpdates ?? ["message"],
374
401
  ...(input.limits ? { limits: input.limits } : {}),
402
+ ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "telegram channel") } : {}),
375
403
  };
376
404
  }
377
405
 
@@ -382,6 +410,7 @@ export function whatsappChannel(input) {
382
410
  name: input.name,
383
411
  secrets: normalizeChannelSecrets(input.secrets ?? defaultWhatsappChannelSecrets(input.provider), \`\${input.provider} whatsapp channel\`),
384
412
  ...(input.limits ? { limits: input.limits } : {}),
413
+ ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, \`\${input.provider} whatsapp channel\`) } : {}),
385
414
  };
386
415
  }
387
416
 
@@ -406,6 +435,36 @@ function normalizeChannelSecrets(secrets, context) {
406
435
 
407
436
  return Array.from(uniqueSecrets).sort();
408
437
  }
438
+
439
+ function normalizeChannelBuffer(input, context) {
440
+ if (input.mode === "off") {
441
+ return { mode: "off" };
442
+ }
443
+
444
+ if (input.mode !== undefined && input.mode !== "debounce") {
445
+ throw new Error(\`\${context} buffer.mode must be "off" or "debounce".\`);
446
+ }
447
+
448
+ const buffer = {
449
+ mode: "debounce",
450
+ quietWindowMs: input.quietWindowMs ?? 2500,
451
+ maxWaitMs: input.maxWaitMs ?? 12000,
452
+ maxMessages: input.maxMessages ?? 20,
453
+ maxChars: input.maxChars ?? 8000,
454
+ };
455
+
456
+ for (const field of ["quietWindowMs", "maxWaitMs", "maxMessages", "maxChars"]) {
457
+ if (!Number.isInteger(buffer[field]) || buffer[field] <= 0) {
458
+ throw new Error(\`\${context} buffer.\${field} must be a positive integer.\`);
459
+ }
460
+ }
461
+
462
+ if (buffer.maxWaitMs < buffer.quietWindowMs) {
463
+ throw new Error(\`\${context} buffer.maxWaitMs must be greater than or equal to buffer.quietWindowMs.\`);
464
+ }
465
+
466
+ return buffer;
467
+ }
409
468
  `;
410
469
  }
411
470
 
@@ -486,7 +545,8 @@ import { getProviderAdapter } from ${providerImportPath};
486
545
 
487
546
  const manifest = ${manifestJson};
488
547
  const instructions = ${instructionsJson};
489
- const hostedTools = agentConfig.tools ?? [];
548
+ const hostedKnowledgeTool = manifest.knowledge.enabled ? createHostedKnowledgeSearchTool() : null;
549
+ const hostedTools = hostedKnowledgeTool ? [hostedKnowledgeTool, ...(agentConfig.tools ?? [])] : (agentConfig.tools ?? []);
490
550
  const DEFAULT_TOOL_TIMEOUT_MS = 30000;
491
551
 
492
552
  export class AgentKitConversationStore extends DurableObject {
@@ -668,6 +728,7 @@ export default {
668
728
  provider: channel.provider,
669
729
  secrets: channel.secrets,
670
730
  })),
731
+ knowledge: manifest.knowledge,
671
732
  access: manifest.access,
672
733
  storage: manifest.storage,
673
734
  database: inspectHostedDatabase(),
@@ -1022,7 +1083,7 @@ async function callHostedProvider(env, messages, signal) {
1022
1083
  runtime: manifest.runtime,
1023
1084
  provider: manifest.provider,
1024
1085
  },
1025
- instructions: withToolVisibilityInstructions(instructions, hostedTools),
1086
+ instructions: withToolVisibilityInstructions(appendHostedKnowledgePromptPolicy(instructions), hostedTools),
1026
1087
  messages,
1027
1088
  tools: hostedTools,
1028
1089
  toolRuntime: createHostedProviderToolRuntime(env, signal),
@@ -1033,6 +1094,20 @@ async function callHostedProvider(env, messages, signal) {
1033
1094
  return result.message.content;
1034
1095
  }
1035
1096
 
1097
+ function appendHostedKnowledgePromptPolicy(baseInstructions) {
1098
+ if (!manifest.knowledge.enabled) {
1099
+ return baseInstructions;
1100
+ }
1101
+
1102
+ return \`\${baseInstructions.trimEnd()}
1103
+
1104
+ Knowledge rules:
1105
+ - Use agentkit_search_knowledge before answering business-specific factual questions about the user's documents, policies, prices, availability, services, procedures, or source-specific claims.
1106
+ - Do not invent prices, policies, legal/clinical claims, availability, or source-specific facts when Knowledge has not returned support for the answer.
1107
+ - Use retrieved Knowledge as context for the final answer, but do not expose raw retrieval JSON, scores, chunk IDs, or internal tool output objects to the user.
1108
+ \`;
1109
+ }
1110
+
1036
1111
  function withToolVisibilityInstructions(baseInstructions, tools) {
1037
1112
  const internalTools = tools
1038
1113
  .filter((tool) => tool.visibility === "internal")
@@ -1052,6 +1127,396 @@ Tool visibility rules:
1052
1127
  \`;
1053
1128
  }
1054
1129
 
1130
+ function createHostedKnowledgeSearchTool() {
1131
+ return {
1132
+ name: manifest.knowledge.internalTool || "agentkit_search_knowledge",
1133
+ description: "Search the Agent Capsule's configured Knowledge sources for business-specific facts.",
1134
+ visibility: "internal",
1135
+ inputSchema: {
1136
+ type: "object",
1137
+ properties: {
1138
+ query: { type: "string" },
1139
+ topK: { type: "integer" },
1140
+ },
1141
+ required: ["query"],
1142
+ additionalProperties: false,
1143
+ },
1144
+ outputSchema: {
1145
+ type: "object",
1146
+ properties: {
1147
+ results: {
1148
+ type: "array",
1149
+ items: {
1150
+ type: "object",
1151
+ properties: {
1152
+ content: { type: "string" },
1153
+ score: { type: "number" },
1154
+ source: { type: "object" },
1155
+ },
1156
+ required: ["content", "score", "source"],
1157
+ },
1158
+ },
1159
+ },
1160
+ required: ["results"],
1161
+ additionalProperties: false,
1162
+ },
1163
+ secrets: manifest.knowledge.embedding.secret ? [manifest.knowledge.embedding.secret] : [],
1164
+ async execute(input, ctx) {
1165
+ const query = typeof input?.query === "string" ? input.query : "";
1166
+ const topK = normalizeKnowledgeTopK(input?.topK ?? manifest.knowledge.retrieval.topK);
1167
+ const results = await searchHostedKnowledge(ctx.database, query, topK, ctx.secrets ?? {});
1168
+
1169
+ return {
1170
+ results: results.map((result) => ({
1171
+ content: result.content,
1172
+ score: result.score,
1173
+ source: result.source,
1174
+ })),
1175
+ };
1176
+ },
1177
+ };
1178
+ }
1179
+
1180
+ async function searchHostedKnowledge(database, query, topK, secrets) {
1181
+ const normalizedQuery = String(query || "").trim();
1182
+
1183
+ if (!normalizedQuery) {
1184
+ throw agentKitError("knowledge_query_invalid", "Knowledge search query must be a non-empty string.");
1185
+ }
1186
+
1187
+ const hasEmbeddingProvider = manifest.knowledge.embedding.provider !== "none";
1188
+ const lexical =
1189
+ manifest.knowledge.retrieval.hybrid || !hasEmbeddingProvider
1190
+ ? await searchHostedKnowledgeLexical(database, normalizedQuery, Math.max(topK * 4, topK))
1191
+ : [];
1192
+ const semantic = hasEmbeddingProvider
1193
+ ? await searchHostedKnowledgeSemantic(database, normalizedQuery, topK * 4, secrets)
1194
+ : [];
1195
+
1196
+ return combineHostedKnowledgeResults(lexical, semantic, topK);
1197
+ }
1198
+
1199
+ async function searchHostedKnowledgeLexical(database, query, limit) {
1200
+ const tokens = hostedKnowledgeQueryTokens(query);
1201
+
1202
+ if (tokens.length === 0) {
1203
+ return [];
1204
+ }
1205
+
1206
+ try {
1207
+ const result = await database.query(
1208
+ \`
1209
+ SELECT
1210
+ c.id AS chunk_id,
1211
+ c.source_id AS source_id,
1212
+ c.content AS content,
1213
+ c.title AS title,
1214
+ c.section AS section,
1215
+ c.locator AS locator,
1216
+ s.source_key AS source_key,
1217
+ rank
1218
+ FROM agentkit_knowledge_fts
1219
+ JOIN agentkit_knowledge_chunks c ON c.id = agentkit_knowledge_fts.chunk_id
1220
+ JOIN agentkit_knowledge_sources s ON s.id = c.source_id
1221
+ WHERE agentkit_knowledge_fts MATCH ?
1222
+ ORDER BY rank ASC
1223
+ LIMIT ?
1224
+ \`,
1225
+ [tokens.map((token) => \`"\${token.replace(/"/g, '""')}"\`).join(" OR "), limit],
1226
+ );
1227
+
1228
+ return result.rows.map((row, index) => hostedKnowledgeResultFromRow(row, hostedLexicalScore(index), null));
1229
+ } catch {
1230
+ return searchHostedKnowledgeLexicalFallback(database, query, limit);
1231
+ }
1232
+ }
1233
+
1234
+ async function searchHostedKnowledgeLexicalFallback(database, query, limit) {
1235
+ const like = \`%\${query.toLowerCase()}%\`;
1236
+ const result = await database.query(
1237
+ \`
1238
+ SELECT
1239
+ c.id AS chunk_id,
1240
+ c.source_id AS source_id,
1241
+ c.content AS content,
1242
+ c.title AS title,
1243
+ c.section AS section,
1244
+ c.locator AS locator,
1245
+ s.source_key AS source_key
1246
+ FROM agentkit_knowledge_chunks c
1247
+ JOIN agentkit_knowledge_sources s ON s.id = c.source_id
1248
+ WHERE lower(c.content) LIKE ?
1249
+ OR lower(COALESCE(c.title, '')) LIKE ?
1250
+ OR lower(COALESCE(c.section, '')) LIKE ?
1251
+ ORDER BY c.created_at DESC, c.ordinal ASC
1252
+ LIMIT ?
1253
+ \`,
1254
+ [like, like, like, limit],
1255
+ );
1256
+
1257
+ return result.rows.map((row, index) => hostedKnowledgeResultFromRow(row, hostedLexicalScore(index), null));
1258
+ }
1259
+
1260
+ async function searchHostedKnowledgeSemantic(database, query, limit, secrets) {
1261
+ const queryEmbedding = await createHostedKnowledgeEmbedding(query, secrets);
1262
+
1263
+ if (!queryEmbedding) {
1264
+ return [];
1265
+ }
1266
+
1267
+ try {
1268
+ return await searchHostedKnowledgeNativeVector(database, queryEmbedding, limit);
1269
+ } catch {
1270
+ return searchHostedKnowledgeSemanticFallback(database, queryEmbedding, limit);
1271
+ }
1272
+ }
1273
+
1274
+ async function searchHostedKnowledgeNativeVector(database, queryEmbedding, limit) {
1275
+ const vector = JSON.stringify(queryEmbedding);
1276
+ const result = await database.query(
1277
+ \`
1278
+ SELECT
1279
+ c.id AS chunk_id,
1280
+ c.source_id AS source_id,
1281
+ c.content AS content,
1282
+ c.title AS title,
1283
+ c.section AS section,
1284
+ c.locator AS locator,
1285
+ s.source_key AS source_key,
1286
+ vector_distance_cos(v.embedding, vector32(?)) AS vector_distance
1287
+ FROM vector_top_k('agentkit_knowledge_vectors_embedding_idx', vector32(?), ?) nearest
1288
+ JOIN agentkit_knowledge_vectors v ON v.id = nearest.id
1289
+ JOIN agentkit_knowledge_chunks c ON c.id = v.chunk_id
1290
+ JOIN agentkit_knowledge_sources s ON s.id = c.source_id
1291
+ ORDER BY vector_distance ASC
1292
+ \`,
1293
+ [vector, vector, limit],
1294
+ );
1295
+
1296
+ return result.rows
1297
+ .map((row) => {
1298
+ const distance = Number(row.vector_distance);
1299
+ const semantic = Number.isFinite(distance) ? 1 - distance : 0;
1300
+ return hostedKnowledgeResultFromRow(row, 0, semantic);
1301
+ })
1302
+ .filter((result) => result.match.semantic !== null && result.match.semantic > 0)
1303
+ .slice(0, limit);
1304
+ }
1305
+
1306
+ async function searchHostedKnowledgeSemanticFallback(database, queryEmbedding, limit) {
1307
+ const result = await database.query(
1308
+ \`
1309
+ SELECT
1310
+ c.id AS chunk_id,
1311
+ c.source_id AS source_id,
1312
+ c.content AS content,
1313
+ c.title AS title,
1314
+ c.section AS section,
1315
+ c.locator AS locator,
1316
+ c.embedding_json AS embedding_json,
1317
+ s.source_key AS source_key
1318
+ FROM agentkit_knowledge_chunks c
1319
+ JOIN agentkit_knowledge_sources s ON s.id = c.source_id
1320
+ WHERE c.embedding_json IS NOT NULL
1321
+ ORDER BY c.created_at DESC, c.ordinal ASC
1322
+ \`,
1323
+ );
1324
+
1325
+ return result.rows
1326
+ .map((row) => {
1327
+ const embedding = parseHostedKnowledgeEmbedding(row.embedding_json);
1328
+ const semantic = embedding ? hostedCosineSimilarity(queryEmbedding, embedding) : 0;
1329
+ return hostedKnowledgeResultFromRow(row, 0, semantic);
1330
+ })
1331
+ .filter((result) => result.match.semantic !== null && result.match.semantic > 0)
1332
+ .sort((left, right) => (right.match.semantic ?? 0) - (left.match.semantic ?? 0))
1333
+ .slice(0, limit);
1334
+ }
1335
+
1336
+ async function createHostedKnowledgeEmbedding(input, secrets) {
1337
+ const provider = manifest.knowledge.embedding.provider;
1338
+
1339
+ if (provider === "none") {
1340
+ return null;
1341
+ }
1342
+
1343
+ if (provider === "test") {
1344
+ return hostedFakeEmbedding(input, manifest.knowledge.embedding.dimensions || 32);
1345
+ }
1346
+
1347
+ if (provider !== "openai") {
1348
+ throw agentKitError("knowledge_embedding_provider_unsupported", "Unsupported knowledge embedding provider.");
1349
+ }
1350
+
1351
+ const secretName = manifest.knowledge.embedding.secret || "OPENAI_API_KEY";
1352
+ const apiKey = secrets[secretName];
1353
+
1354
+ if (!apiKey) {
1355
+ throw agentKitError("knowledge_embedding_secret_missing", \`Knowledge embeddings require missing secret "\${secretName}".\`);
1356
+ }
1357
+
1358
+ const response = await fetch("https://api.openai.com/v1/embeddings", {
1359
+ method: "POST",
1360
+ headers: {
1361
+ "content-type": "application/json",
1362
+ authorization: \`Bearer \${apiKey}\`,
1363
+ },
1364
+ body: JSON.stringify({
1365
+ model: manifest.knowledge.embedding.model || "text-embedding-3-small",
1366
+ input,
1367
+ encoding_format: "float",
1368
+ ...(manifest.knowledge.embedding.dimensions ? { dimensions: manifest.knowledge.embedding.dimensions } : {}),
1369
+ }),
1370
+ });
1371
+
1372
+ if (!response.ok) {
1373
+ const text = await response.text().catch(() => "");
1374
+ throw agentKitError(
1375
+ "knowledge_embedding_failed",
1376
+ \`OpenAI embedding request failed with HTTP \${response.status}.\${text ? " " + text.slice(0, 240) : ""}\`,
1377
+ );
1378
+ }
1379
+
1380
+ const payload = await response.json();
1381
+ const embedding = payload?.data?.[0]?.embedding;
1382
+
1383
+ if (!Array.isArray(embedding) || embedding.some((value) => typeof value !== "number")) {
1384
+ throw agentKitError("knowledge_embedding_failed", "OpenAI embedding response did not contain a numeric vector.");
1385
+ }
1386
+
1387
+ return embedding;
1388
+ }
1389
+
1390
+ function combineHostedKnowledgeResults(lexical, semantic, topK) {
1391
+ const byChunk = new Map();
1392
+
1393
+ for (const result of lexical) {
1394
+ byChunk.set(result.chunkId, result);
1395
+ }
1396
+
1397
+ for (const result of semantic) {
1398
+ const existing = byChunk.get(result.chunkId);
1399
+
1400
+ if (!existing) {
1401
+ byChunk.set(result.chunkId, {
1402
+ ...result,
1403
+ score: hostedCombinedScore(0, result.match.semantic),
1404
+ });
1405
+ continue;
1406
+ }
1407
+
1408
+ byChunk.set(result.chunkId, {
1409
+ ...existing,
1410
+ score: hostedCombinedScore(existing.match.lexical, result.match.semantic),
1411
+ match: {
1412
+ lexical: existing.match.lexical,
1413
+ semantic: result.match.semantic,
1414
+ },
1415
+ });
1416
+ }
1417
+
1418
+ return Array.from(byChunk.values())
1419
+ .map((result) => ({
1420
+ ...result,
1421
+ score: result.score || hostedCombinedScore(result.match.lexical, result.match.semantic),
1422
+ }))
1423
+ .sort((left, right) => right.score - left.score || left.source.path.localeCompare(right.source.path))
1424
+ .slice(0, topK);
1425
+ }
1426
+
1427
+ function hostedKnowledgeResultFromRow(row, lexical, semantic) {
1428
+ return {
1429
+ chunkId: String(row.chunk_id),
1430
+ content: String(row.content),
1431
+ score: hostedCombinedScore(lexical, semantic),
1432
+ source: {
1433
+ id: String(row.source_id),
1434
+ path: String(row.source_key),
1435
+ title: typeof row.title === "string" ? row.title : null,
1436
+ section: typeof row.section === "string" ? row.section : null,
1437
+ locator: typeof row.locator === "string" ? row.locator : null,
1438
+ },
1439
+ match: {
1440
+ lexical,
1441
+ semantic,
1442
+ },
1443
+ };
1444
+ }
1445
+
1446
+ function normalizeKnowledgeTopK(value) {
1447
+ const topK = Number(value);
1448
+
1449
+ if (!Number.isInteger(topK) || topK <= 0 || topK > 50) {
1450
+ throw agentKitError("knowledge_query_invalid", "Knowledge search topK must be a positive integer no larger than 50.");
1451
+ }
1452
+
1453
+ return topK;
1454
+ }
1455
+
1456
+ function hostedKnowledgeQueryTokens(query) {
1457
+ return Array.from(new Set(String(query).toLowerCase().match(/[\\p{L}\\p{N}_-]+/gu) ?? [])).slice(0, 12);
1458
+ }
1459
+
1460
+ function parseHostedKnowledgeEmbedding(value) {
1461
+ if (typeof value !== "string") {
1462
+ return null;
1463
+ }
1464
+
1465
+ try {
1466
+ const parsed = JSON.parse(value);
1467
+ return Array.isArray(parsed) && parsed.every((item) => typeof item === "number") ? parsed : null;
1468
+ } catch {
1469
+ return null;
1470
+ }
1471
+ }
1472
+
1473
+ function hostedCombinedScore(lexical, semantic) {
1474
+ const semanticScore = semantic === null ? 0 : Math.max(0, (semantic + 1) / 2);
1475
+ return Number((lexical * 0.6 + semanticScore * 0.4).toFixed(6));
1476
+ }
1477
+
1478
+ function hostedLexicalScore(index) {
1479
+ return Number((1 / (index + 1)).toFixed(6));
1480
+ }
1481
+
1482
+ function hostedCosineSimilarity(left, right) {
1483
+ if (left.length !== right.length || left.length === 0) {
1484
+ return 0;
1485
+ }
1486
+
1487
+ let dot = 0;
1488
+ let leftNorm = 0;
1489
+ let rightNorm = 0;
1490
+
1491
+ for (let index = 0; index < left.length; index += 1) {
1492
+ dot += left[index] * right[index];
1493
+ leftNorm += left[index] * left[index];
1494
+ rightNorm += right[index] * right[index];
1495
+ }
1496
+
1497
+ if (leftNorm === 0 || rightNorm === 0) {
1498
+ return 0;
1499
+ }
1500
+
1501
+ return dot / (Math.sqrt(leftNorm) * Math.sqrt(rightNorm));
1502
+ }
1503
+
1504
+ async function hostedFakeEmbedding(input, dimensions) {
1505
+ const values = Array.from({ length: dimensions }, () => 0);
1506
+ const tokens = String(input).toLowerCase().match(/[\\p{L}\\p{N}_-]+/gu) ?? [];
1507
+ const encoder = new TextEncoder();
1508
+
1509
+ for (const token of tokens) {
1510
+ const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", encoder.encode(token)));
1511
+ const slot = hash[0] % dimensions;
1512
+ const sign = hash[1] % 2 === 0 ? 1 : -1;
1513
+ values[slot] += sign * (1 + (hash[2] % 7) / 10);
1514
+ }
1515
+
1516
+ const norm = Math.sqrt(values.reduce((sum, value) => sum + value * value, 0));
1517
+ return norm === 0 ? values : values.map((value) => Number((value / norm).toFixed(8)));
1518
+ }
1519
+
1055
1520
  async function persistFailure(env, conversationId, userMessage, error) {
1056
1521
  await persist(env, {
1057
1522
  conversationId,
@@ -6,6 +6,7 @@ import type { DatabaseArgs, DatabaseResult, DatabaseRow, DatabaseStatement } fro
6
6
  import type { AgentMessageRole, ProviderRunResult } from "../providers";
7
7
  import type { LoadedAgentCapsule } from "../runtime/config";
8
8
  import { AgentKitError } from "../runtime/errors";
9
+ import { KNOWLEDGE_MIGRATION_ID, KNOWLEDGE_SCHEMA_SQL } from "../runtime/knowledge/schema";
9
10
 
10
11
  export type ConversationSummary = {
11
12
  id: string;
@@ -153,6 +154,10 @@ const MIGRATIONS = [
153
154
  ADD COLUMN visibility TEXT NOT NULL DEFAULT 'user';
154
155
  `,
155
156
  },
157
+ {
158
+ id: KNOWLEDGE_MIGRATION_ID,
159
+ sql: KNOWLEDGE_SCHEMA_SQL,
160
+ },
156
161
  ] as const;
157
162
 
158
163
  export async function openCapsuleStore(capsule: LoadedAgentCapsule): Promise<SqliteAgentKitStore> {
@@ -147,7 +147,7 @@ When the owner opens this folder in Codex, Claude Code, or another coding agent
147
147
 
148
148
  Start building immediately:
149
149
 
150
- - Read \`AGENTKIT.md\` and the full docs path from \`npm run agentkit -- docs full\`.
150
+ - Start with \`skills/agentkit-capsule/SKILL.md\`, then use \`npm run agentkit -- docs llms\` as the docs router.
151
151
  - Infer the first useful version from the owner's request.
152
152
  - Edit \`prompts/instructions.md\` for the agent behavior.
153
153
  - Edit \`agentkit.config.ts\` for provider, tools, secrets, access, and storage.
@@ -170,7 +170,8 @@ Start building immediately:
170
170
  - \`npm run agentkit -- inspect\`: print machine-readable capsule state.
171
171
  - \`printf %s "$VALUE" | npm run agentkit -- env set <NAME> --stdin\`: write a local secret value to ignored \`.env\` without putting it in shell history.
172
172
  - \`npm run agentkit -- env list\`: list local secret names without printing values.
173
- - \`npm run agentkit -- docs full\`: print the full AgentKit contract path.
173
+ - \`npm run agentkit -- docs llms\`: print the lightweight AgentKit docs router.
174
+ - \`npm run agentkit -- docs full\`: print the full AgentKit contract path only when a skill asks for it.
174
175
 
175
176
  ## Testing With A UI
176
177
 
@@ -305,12 +306,14 @@ If you add a tool, also run a fake-provider tool smoke test:
305
306
  npm run agentkit -- tool tool_name --input '{}'
306
307
  \`\`\`
307
308
 
308
- For the full framework contract, read the path printed by:
309
+ For the lightweight docs router, read the path printed by:
309
310
 
310
311
  \`\`\`sh
311
- npm run agentkit -- docs full
312
+ npm run agentkit -- docs llms
312
313
  \`\`\`
313
314
 
315
+ Read the full framework contract with \`npm run agentkit -- docs full\` only when a skill asks for it.
316
+
314
317
  ## Hosted Deploy
315
318
 
316
319
  This capsule is hosted-deploy ready by default.
@@ -343,6 +346,7 @@ Use AgentKit conventions when editing this project.
343
346
  - Production secrets must be managed secrets, not committed files.
344
347
  - Keep required local secret names in \`.env.schema\` and values in ignored \`.env\`. AgentKit local commands load \`.env\` directly.
345
348
  - Treat the owner's natural-language request as the brief and start implementing inside this capsule.
349
+ - Start with \`skills/agentkit-capsule/SKILL.md\` when the task is not obvious.
346
350
  `,
347
351
  },
348
352
  {
@@ -887,7 +887,8 @@ A Clara conversa com clientes, coleta nome, email e telefone, consulta disponibi
887
887
  - \`npm run eval\`: rodar evals.
888
888
  - \`npm run dev\`: rodar a runtime local.
889
889
  - \`npm run agentkit -- inspect\`: imprimir o estado da cápsula.
890
- - \`npm run agentkit -- docs full\`: imprimir o contrato completo do AgentKit.
890
+ - \`npm run agentkit -- docs llms\`: imprimir o roteador leve da documentação AgentKit.
891
+ - \`npm run agentkit -- docs full\`: imprimir o contrato completo do AgentKit somente quando uma skill pedir.
891
892
 
892
893
  ## Teste Com UI
893
894
 
@@ -903,6 +904,7 @@ A Clara conversa com clientes, coleta nome, email e telefone, consulta disponibi
903
904
  - Mantenha segredos em \`.env\`, nunca em arquivos versionados.
904
905
  - Não espere wizard ou recipe. AgentKit fornece o scaffold e o contrato; implemente diretamente conforme o brief do dono.
905
906
  - Para trocar para um provider real, o dono deve escolher OpenRouter, OpenAI, Anthropic ou outro provider suportado. Depois edite \`agentkit.config.ts\`, atualize \`.env.schema\` e configure secrets locais/hosted.
907
+ - Quando a próxima ação não for óbvia, comece por \`skills/agentkit-capsule/SKILL.md\`.
906
908
  `,
907
909
  },
908
910
  {
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: agentkit-build-agent
3
+ description: Use when the owner gives a natural-language brief for a new or changed AgentKit agent and expects the coding agent to turn it into a working local capsule with prompts, tools, schema, evals, and verification.
4
+ ---
5
+
6
+ # Build An AgentKit Agent
7
+
8
+ Use this when the owner asks for an agent in plain language.
9
+
10
+ ## Workflow
11
+
12
+ 1. Read `agentkit.config.ts`, `prompts/instructions.md`, `schema.sql`, `evals/`, and existing `tools/`.
13
+ 2. Infer the first useful local version from the owner's brief.
14
+ 3. Edit `prompts/instructions.md` for behavior, boundaries, intake questions, escalation rules, and tool-use policy.
15
+ 4. Add tools only when the agent needs action, live data, authorization-sensitive data, or durable writes.
16
+ 5. Add database tables to `schema.sql` when the agent owns records.
17
+ 6. Add or update evals for the main flow.
18
+ 7. Keep the capsule runnable on `test/fake` unless the owner has chosen a real provider.
19
+
20
+ ## Templates
21
+
22
+ Use these only when they match the brief:
23
+
24
+ - `templates/support-agent.instructions.md`
25
+ - `templates/appointment-intake.instructions.md`
26
+ - `templates/sales-qualifier.instructions.md`
27
+
28
+ For prompt-only work, use `skills/agentkit-prompts/SKILL.md`.
29
+ For database-backed tools, use `skills/agentkit-database/SKILL.md`.
30
+
31
+ ## Verification
32
+
33
+ ```sh
34
+ npm run typecheck
35
+ npm run agentkit -- inspect
36
+ npm run chat -- --message "hello"
37
+ npm run eval
38
+ ```
39
+
40
+ If a tool was added:
41
+
42
+ ```sh
43
+ npm run agentkit -- tool <tool_name> --input '<json>'
44
+ ```
45
+
46
+ ## Final Response
47
+
48
+ Summarize the files changed, assumptions made, verification results, and whether the behavior was tested with `test/fake` or a real provider selected by the owner.
49
+