@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.
Files changed (87) hide show
  1. package/README.md +9 -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 +17 -0
  7. package/docs/guides/connect-whatsapp-zapster.md +16 -0
  8. package/docs/guides/create-agent.md +10 -1
  9. package/docs/guides/run-evals.md +36 -1
  10. package/docs/llms-full.txt +90 -1
  11. package/docs/llms.txt +9 -2
  12. package/package.json +2 -1
  13. package/src/cli/cloud-client.ts +10 -2
  14. package/src/cli/commands/channels.ts +67 -3
  15. package/src/cli/commands/knowledge.ts +136 -0
  16. package/src/cli/deploy-chat-ui.ts +7 -0
  17. package/src/cli/deploy-readiness.ts +19 -0
  18. package/src/cli/help.ts +26 -2
  19. package/src/cli/index.ts +140 -8
  20. package/src/cloud/artifact.ts +92 -1
  21. package/src/cloud/contracts.ts +16 -0
  22. package/src/create-project.ts +38 -6
  23. package/src/index.ts +142 -1
  24. package/src/providers/pi.ts +1 -1
  25. package/src/providers/test.ts +1 -1
  26. package/src/runtime/channel-buffer.ts +30 -0
  27. package/src/runtime/channels.ts +1 -0
  28. package/src/runtime/chat.ts +21 -2
  29. package/src/runtime/config.ts +175 -0
  30. package/src/runtime/core/manifest.ts +37 -0
  31. package/src/runtime/database.ts +93 -2
  32. package/src/runtime/db-commands.ts +9 -0
  33. package/src/runtime/deploy-readiness.ts +12 -0
  34. package/src/runtime/dev-server.ts +201 -11
  35. package/src/runtime/evals.ts +210 -20
  36. package/src/runtime/inspect.ts +39 -0
  37. package/src/runtime/knowledge/chunk.ts +333 -0
  38. package/src/runtime/knowledge/config.ts +135 -0
  39. package/src/runtime/knowledge/embeddings.ts +133 -0
  40. package/src/runtime/knowledge/ingest.ts +521 -0
  41. package/src/runtime/knowledge/prompt-policy.ts +30 -0
  42. package/src/runtime/knowledge/retrieve.ts +283 -0
  43. package/src/runtime/knowledge/schema.ts +56 -0
  44. package/src/runtime/knowledge/tool.ts +64 -0
  45. package/src/runtime/knowledge/vector.ts +258 -0
  46. package/src/runtime/spec.ts +152 -0
  47. package/src/runtime/sync.ts +144 -0
  48. package/src/runtime/targets/cloudflare/build.ts +514 -4
  49. package/src/runtime/tools.ts +121 -1
  50. package/src/runtime/traces.ts +41 -0
  51. package/src/storage/sqlite.ts +141 -0
  52. package/src/templates/blank.ts +16 -5
  53. package/src/templates/dentista.ts +17 -2
  54. package/src/templates/skills/agentkit-build-agent/SKILL.md +51 -0
  55. package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +20 -0
  56. package/src/templates/skills/agentkit-build-agent/templates/sales-qualifier.instructions.md +17 -0
  57. package/src/templates/skills/agentkit-build-agent/templates/support-agent.instructions.md +16 -0
  58. package/src/templates/skills/agentkit-capsule/SKILL.md +62 -0
  59. package/src/templates/skills/agentkit-capsule/references/docs-router.md +15 -0
  60. package/src/templates/skills/agentkit-channels/SKILL.md +62 -0
  61. package/src/templates/skills/agentkit-channels/references/channel-buffering.md +58 -0
  62. package/src/templates/skills/agentkit-channels/references/channel-debugging.md +37 -0
  63. package/src/templates/skills/agentkit-channels/references/telegram.md +37 -0
  64. package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +37 -0
  65. package/src/templates/skills/agentkit-database/SKILL.md +45 -0
  66. package/src/templates/skills/agentkit-database/templates/appointments.schema.sql +15 -0
  67. package/src/templates/skills/agentkit-database/templates/leads.schema.sql +17 -0
  68. package/src/templates/skills/agentkit-deploy/SKILL.md +44 -0
  69. package/src/templates/skills/agentkit-evals/SKILL.md +60 -0
  70. package/src/templates/skills/agentkit-evals/templates/multi-turn.eval.md +22 -0
  71. package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +14 -0
  72. package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +14 -0
  73. package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +18 -0
  74. package/src/templates/skills/agentkit-knowledge/SKILL.md +40 -0
  75. package/src/templates/skills/agentkit-knowledge/templates/faq.md +14 -0
  76. package/src/templates/skills/agentkit-knowledge/templates/policies.md +14 -0
  77. package/src/templates/skills/agentkit-knowledge/templates/prices.csv +3 -0
  78. package/src/templates/skills/agentkit-prompts/SKILL.md +45 -0
  79. package/src/templates/skills/agentkit-prompts/templates/knowledge-grounded-faq.instructions.md +11 -0
  80. package/src/templates/skills/agentkit-provider/SKILL.md +57 -0
  81. package/src/templates/skills/agentkit-security/SKILL.md +55 -0
  82. package/src/templates/skills/agentkit-tools/SKILL.md +36 -0
  83. package/src/templates/skills/agentkit-tools/examples/database-write.tool.md +35 -0
  84. package/src/templates/skills/agentkit-tools/examples/eval-safe-external-action.tool.md +37 -0
  85. package/src/templates/skills/agentkit-tools/examples/lookup-order.tool.md +46 -0
  86. package/src/templates/skills/agentkit-troubleshooting/SKILL.md +52 -0
  87. package/src/templates/support.ts +15 -4
@@ -0,0 +1,521 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile, stat } from "node:fs/promises";
3
+ import { extname, isAbsolute, relative, resolve } from "node:path";
4
+
5
+ import type { DatabaseRunner, DatabaseRow } from "../../index";
6
+ import { openPreparedCapsuleStore, createLocalDatabaseRunner } from "../database";
7
+ import { loadCapsuleEnv } from "../env";
8
+ import { AgentKitError } from "../errors";
9
+ import type { LoadedAgentCapsule } from "../config";
10
+ import { loadAgentCapsule } from "../config";
11
+ import { chunkKnowledgeSource } from "./chunk";
12
+ import { resolveKnowledgeConfig, type ResolvedKnowledgeSource } from "./config";
13
+ import { createEmbeddingProvider } from "./embeddings";
14
+ import {
15
+ localKnowledgeVectorDatabaseExists,
16
+ rebuildLocalKnowledgeVectorIndex,
17
+ type LocalKnowledgeVectorStatus,
18
+ } from "./vector";
19
+
20
+ export type KnowledgeIngestStatus = "indexed" | "skipped" | "failed";
21
+
22
+ export type KnowledgeIngestResult = {
23
+ source: string;
24
+ status: KnowledgeIngestStatus;
25
+ chunksCreated: number;
26
+ message: string;
27
+ };
28
+
29
+ export type KnowledgeIngestSummary = {
30
+ databasePath: string;
31
+ indexed: number;
32
+ skipped: number;
33
+ failed: number;
34
+ chunksCreated: number;
35
+ vector: LocalKnowledgeVectorStatus;
36
+ results: KnowledgeIngestResult[];
37
+ };
38
+
39
+ export type KnowledgeSourceSummary = {
40
+ source: string;
41
+ kind: string;
42
+ title: string | null;
43
+ status: string;
44
+ chunkCount: number;
45
+ updatedAt: string;
46
+ indexedAt: string | null;
47
+ errorMessage: string | null;
48
+ };
49
+
50
+ const SUPPORTED_EXTENSIONS = new Set([".md", ".markdown", ".txt", ".csv"]);
51
+
52
+ export async function addKnowledgeSourceFromCwd(cwd: string, source: string): Promise<KnowledgeIngestSummary> {
53
+ const capsule = await loadAgentCapsule(cwd);
54
+ return ingestKnowledgeSources(capsule, [resolveFileSource(source)]);
55
+ }
56
+
57
+ export async function syncKnowledgeSourcesFromCwd(cwd: string): Promise<KnowledgeIngestSummary> {
58
+ const capsule = await loadAgentCapsule(cwd);
59
+ const knowledge = resolveKnowledgeConfig(capsule.config);
60
+
61
+ if (knowledge.sources.length === 0) {
62
+ throw new AgentKitError(
63
+ "knowledge_not_configured",
64
+ "No knowledge.sources are configured in agentkit.config.ts. Add sources or run `agentkit knowledge add <path>`.",
65
+ );
66
+ }
67
+
68
+ return ingestKnowledgeSources(capsule, knowledge.sources);
69
+ }
70
+
71
+ export async function syncConfiguredKnowledgeSources(capsule: LoadedAgentCapsule): Promise<KnowledgeIngestSummary | null> {
72
+ const knowledge = resolveKnowledgeConfig(capsule.config);
73
+
74
+ if (!knowledge.enabled || knowledge.sources.length === 0) {
75
+ return null;
76
+ }
77
+
78
+ const summary = await ingestKnowledgeSources(capsule, knowledge.sources);
79
+
80
+ if (summary.failed > 0) {
81
+ const failures = summary.results
82
+ .filter((result) => result.status === "failed")
83
+ .map((result) => `${result.source}: ${result.message}`)
84
+ .join("; ");
85
+ throw new AgentKitError(
86
+ "knowledge_sync_failed",
87
+ `Configured Knowledge sync failed before local run. ${failures}`,
88
+ );
89
+ }
90
+
91
+ return summary;
92
+ }
93
+
94
+ export async function listKnowledgeSourcesFromCwd(cwd: string): Promise<{
95
+ databasePath: string;
96
+ sources: KnowledgeSourceSummary[];
97
+ }> {
98
+ const capsule = await loadAgentCapsule(cwd);
99
+ const { store, databasePath } = await openPreparedCapsuleStore(capsule);
100
+
101
+ try {
102
+ const db = createLocalDatabaseRunner(store);
103
+ const result = await db.query(`
104
+ SELECT
105
+ source_key,
106
+ kind,
107
+ title,
108
+ status,
109
+ chunk_count,
110
+ updated_at,
111
+ indexed_at,
112
+ error_message
113
+ FROM agentkit_knowledge_sources
114
+ ORDER BY updated_at DESC, source_key ASC
115
+ `);
116
+
117
+ return {
118
+ databasePath,
119
+ sources: result.rows.map(knowledgeSourceSummaryFromRow),
120
+ };
121
+ } finally {
122
+ store.close();
123
+ }
124
+ }
125
+
126
+ async function ingestKnowledgeSources(
127
+ capsule: LoadedAgentCapsule,
128
+ sources: ResolvedKnowledgeSource[],
129
+ ): Promise<KnowledgeIngestSummary> {
130
+ const { store, databasePath } = await openPreparedCapsuleStore(capsule);
131
+ const db = createLocalDatabaseRunner(store);
132
+ const env = await loadCapsuleEnv(capsule.root);
133
+ const knowledge = resolveKnowledgeConfig(capsule.config);
134
+ const embeddingProvider = createEmbeddingProvider({
135
+ config: knowledge.embedding,
136
+ env,
137
+ });
138
+ const results: KnowledgeIngestResult[] = [];
139
+
140
+ try {
141
+ for (const source of sources) {
142
+ try {
143
+ results.push(await ingestKnowledgeSource(capsule, db, source, embeddingProvider));
144
+ } catch (error) {
145
+ results.push({
146
+ source: source.value,
147
+ status: "failed",
148
+ chunksCreated: 0,
149
+ message: error instanceof Error ? error.message : String(error),
150
+ });
151
+ }
152
+ }
153
+
154
+ await checkpointLocalKnowledgeDatabase(db);
155
+ } finally {
156
+ store.close();
157
+ }
158
+
159
+ const vector = await syncLocalKnowledgeVectorIndex({
160
+ databasePath,
161
+ embeddingProviderEnabled: Boolean(embeddingProvider),
162
+ dimensions: knowledge.embedding.dimensions,
163
+ shouldRebuild: results.some((result) => result.status === "indexed"),
164
+ });
165
+
166
+ return {
167
+ databasePath,
168
+ indexed: results.filter((result) => result.status === "indexed").length,
169
+ skipped: results.filter((result) => result.status === "skipped").length,
170
+ failed: results.filter((result) => result.status === "failed").length,
171
+ chunksCreated: results.reduce((sum, result) => sum + result.chunksCreated, 0),
172
+ vector,
173
+ results,
174
+ };
175
+ }
176
+
177
+ async function checkpointLocalKnowledgeDatabase(db: DatabaseRunner): Promise<void> {
178
+ await db.execute("PRAGMA wal_checkpoint(TRUNCATE)").catch(() => undefined);
179
+ }
180
+
181
+ async function syncLocalKnowledgeVectorIndex(options: {
182
+ databasePath: string;
183
+ embeddingProviderEnabled: boolean;
184
+ dimensions: number | null;
185
+ shouldRebuild: boolean;
186
+ }): Promise<LocalKnowledgeVectorStatus> {
187
+ if (!options.embeddingProviderEnabled) {
188
+ return localKnowledgeVectorDisabled();
189
+ }
190
+
191
+ const hasVectorDatabase = await localKnowledgeVectorDatabaseExists(options.databasePath);
192
+
193
+ if (!options.shouldRebuild && hasVectorDatabase) {
194
+ return {
195
+ enabled: true,
196
+ provider: "libsql",
197
+ chunksIndexed: 0,
198
+ message: "Local libSQL vector database is already up to date.",
199
+ };
200
+ }
201
+
202
+ return rebuildLocalKnowledgeVectorIndex({
203
+ databasePath: options.databasePath,
204
+ dimensions: options.dimensions,
205
+ });
206
+ }
207
+
208
+ function localKnowledgeVectorDisabled(): LocalKnowledgeVectorStatus {
209
+ return {
210
+ enabled: false,
211
+ provider: "none",
212
+ chunksIndexed: 0,
213
+ message: "Knowledge embeddings are disabled, so no local vector index was created.",
214
+ };
215
+ }
216
+
217
+ async function sourceEmbeddingsAreCurrent(
218
+ db: DatabaseRunner,
219
+ sourceId: string,
220
+ knowledge: ReturnType<typeof resolveKnowledgeConfig>,
221
+ embeddingProviderEnabled: boolean,
222
+ ): Promise<boolean> {
223
+ const result = await db.query(
224
+ `
225
+ SELECT
226
+ COUNT(*) AS chunk_count,
227
+ SUM(
228
+ CASE
229
+ WHEN ? = 0
230
+ AND embedding_json IS NULL
231
+ AND embedding_provider IS NULL
232
+ AND embedding_model IS NULL
233
+ AND embedding_dimensions IS NULL
234
+ THEN 1
235
+ WHEN ? = 1
236
+ AND embedding_json IS NOT NULL
237
+ AND embedding_provider = ?
238
+ AND embedding_model = ?
239
+ AND embedding_dimensions = ?
240
+ THEN 1
241
+ ELSE 0
242
+ END
243
+ ) AS current_count
244
+ FROM agentkit_knowledge_chunks
245
+ WHERE source_id = ?
246
+ `,
247
+ [
248
+ embeddingProviderEnabled ? 1 : 0,
249
+ embeddingProviderEnabled ? 1 : 0,
250
+ knowledge.embedding.provider,
251
+ knowledge.embedding.model,
252
+ knowledge.embedding.dimensions,
253
+ sourceId,
254
+ ],
255
+ );
256
+ const row = result.rows[0];
257
+ const chunkCount = Number(row?.chunk_count ?? 0);
258
+ const currentCount = Number(row?.current_count ?? 0);
259
+
260
+ return chunkCount === currentCount;
261
+ }
262
+
263
+ async function ingestKnowledgeSource(
264
+ capsule: LoadedAgentCapsule,
265
+ db: DatabaseRunner,
266
+ source: ResolvedKnowledgeSource,
267
+ embeddingProvider: ReturnType<typeof createEmbeddingProvider>,
268
+ ): Promise<KnowledgeIngestResult> {
269
+ if (source.kind === "url") {
270
+ throw new AgentKitError(
271
+ "knowledge_source_unsupported",
272
+ "URL knowledge sources are planned for hosted ingestion. Local Knowledge currently supports .md, .txt, and .csv files.",
273
+ );
274
+ }
275
+
276
+ const file = await readKnowledgeFile(capsule.root, source.value);
277
+ const contentSha256 = sha256(file.content);
278
+ const existing = await findExistingSource(db, file.sourceKey);
279
+ const resolvedKnowledge = resolveKnowledgeConfig(capsule.config);
280
+ const sourceId = typeof existing?.id === "string" ? existing.id : stableId("ksrc", file.sourceKey);
281
+
282
+ if (
283
+ existing?.content_sha256 === contentSha256 &&
284
+ existing.status === "indexed" &&
285
+ (await sourceEmbeddingsAreCurrent(db, sourceId, resolvedKnowledge, Boolean(embeddingProvider)))
286
+ ) {
287
+ return {
288
+ source: file.sourceKey,
289
+ status: "skipped",
290
+ chunksCreated: 0,
291
+ message: "unchanged",
292
+ };
293
+ }
294
+
295
+ const chunks = chunkKnowledgeSource({
296
+ sourcePath: file.sourceKey,
297
+ title: source.title,
298
+ content: file.content,
299
+ });
300
+ const embeddedChunks: Array<{
301
+ chunk: (typeof chunks)[number];
302
+ embedding: number[] | null;
303
+ contentSha256: string;
304
+ id: string;
305
+ }> = [];
306
+
307
+ for (const chunk of chunks) {
308
+ const embedding = embeddingProvider ? await embeddingProvider.embed(chunk.content) : null;
309
+ embeddedChunks.push({
310
+ chunk,
311
+ embedding,
312
+ contentSha256: sha256(chunk.content),
313
+ id: stableId("kchk", `${sourceId}:${chunk.ordinal}:${sha256(chunk.content)}`),
314
+ });
315
+ }
316
+
317
+ const now = new Date().toISOString();
318
+
319
+ await db.transaction(async (tx) => {
320
+ await tx.execute(
321
+ `
322
+ INSERT INTO agentkit_knowledge_sources (
323
+ id,
324
+ source_key,
325
+ kind,
326
+ path,
327
+ url,
328
+ title,
329
+ content_sha256,
330
+ status,
331
+ chunk_count,
332
+ metadata_json,
333
+ created_at,
334
+ updated_at,
335
+ indexed_at,
336
+ error_message
337
+ )
338
+ VALUES (?, ?, ?, ?, ?, ?, ?, 'indexed', ?, '{}', ?, ?, ?, NULL)
339
+ ON CONFLICT(source_key) DO UPDATE SET
340
+ kind = excluded.kind,
341
+ path = excluded.path,
342
+ url = excluded.url,
343
+ title = excluded.title,
344
+ content_sha256 = excluded.content_sha256,
345
+ status = 'indexed',
346
+ chunk_count = excluded.chunk_count,
347
+ updated_at = excluded.updated_at,
348
+ indexed_at = excluded.indexed_at,
349
+ error_message = NULL
350
+ `,
351
+ [
352
+ sourceId,
353
+ file.sourceKey,
354
+ "file",
355
+ file.sourceKey,
356
+ null,
357
+ source.title ?? embeddedChunks[0]?.chunk.metadata.title ?? null,
358
+ contentSha256,
359
+ embeddedChunks.length,
360
+ now,
361
+ now,
362
+ now,
363
+ ],
364
+ );
365
+ await tx.execute("DELETE FROM agentkit_knowledge_fts WHERE source_id = ?", [sourceId]);
366
+ await tx.execute("DELETE FROM agentkit_knowledge_chunks WHERE source_id = ?", [sourceId]);
367
+
368
+ for (const item of embeddedChunks) {
369
+ const embeddingJson = item.embedding ? JSON.stringify(item.embedding) : null;
370
+ const embeddingHash = embeddingJson ? sha256(embeddingJson) : null;
371
+
372
+ await tx.execute(
373
+ `
374
+ INSERT INTO agentkit_knowledge_chunks (
375
+ id,
376
+ source_id,
377
+ ordinal,
378
+ content,
379
+ token_count,
380
+ title,
381
+ section,
382
+ locator,
383
+ content_sha256,
384
+ embedding_json,
385
+ embedding_hash,
386
+ embedding_provider,
387
+ embedding_model,
388
+ embedding_dimensions,
389
+ metadata_json,
390
+ created_at
391
+ )
392
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
393
+ `,
394
+ [
395
+ item.id,
396
+ sourceId,
397
+ item.chunk.ordinal,
398
+ item.chunk.content,
399
+ item.chunk.tokenCount,
400
+ item.chunk.metadata.title,
401
+ item.chunk.metadata.section,
402
+ item.chunk.metadata.locator,
403
+ item.contentSha256,
404
+ embeddingJson,
405
+ embeddingHash,
406
+ item.embedding ? resolvedKnowledge.embedding.provider : null,
407
+ item.embedding ? resolvedKnowledge.embedding.model : null,
408
+ item.embedding ? item.embedding.length : null,
409
+ JSON.stringify(item.chunk.metadata),
410
+ now,
411
+ ],
412
+ );
413
+ await tx.execute(
414
+ `
415
+ INSERT INTO agentkit_knowledge_fts (chunk_id, source_id, title, section, content)
416
+ VALUES (?, ?, ?, ?, ?)
417
+ `,
418
+ [
419
+ item.id,
420
+ sourceId,
421
+ item.chunk.metadata.title,
422
+ item.chunk.metadata.section,
423
+ item.chunk.content,
424
+ ],
425
+ );
426
+ }
427
+ });
428
+
429
+ return {
430
+ source: file.sourceKey,
431
+ status: "indexed",
432
+ chunksCreated: embeddedChunks.length,
433
+ message: `${embeddedChunks.length} chunks indexed`,
434
+ };
435
+ }
436
+
437
+ function resolveFileSource(value: string): ResolvedKnowledgeSource {
438
+ if (/^https?:\/\//i.test(value)) {
439
+ return {
440
+ kind: "url",
441
+ value,
442
+ title: null,
443
+ };
444
+ }
445
+
446
+ return {
447
+ kind: "file",
448
+ value,
449
+ title: null,
450
+ };
451
+ }
452
+
453
+ async function readKnowledgeFile(root: string, inputPath: string): Promise<{ sourceKey: string; content: string }> {
454
+ const absolutePath = resolve(root, inputPath);
455
+ const sourceKey = capsuleRelativePath(root, absolutePath);
456
+ const extension = extname(sourceKey).toLowerCase();
457
+
458
+ if (!SUPPORTED_EXTENSIONS.has(extension)) {
459
+ throw new AgentKitError(
460
+ "knowledge_source_unsupported",
461
+ `Knowledge source "${sourceKey}" is not supported yet. Use .md, .txt, or .csv.`,
462
+ );
463
+ }
464
+
465
+ const stats = await stat(absolutePath).catch((error) => {
466
+ throw new AgentKitError("knowledge_source_missing", `Knowledge source "${sourceKey}" was not found.`, {
467
+ cause: error,
468
+ });
469
+ });
470
+
471
+ if (!stats.isFile()) {
472
+ throw new AgentKitError("knowledge_source_invalid", `Knowledge source "${sourceKey}" must be a file.`);
473
+ }
474
+
475
+ return {
476
+ sourceKey,
477
+ content: await readFile(absolutePath, "utf8"),
478
+ };
479
+ }
480
+
481
+ function capsuleRelativePath(root: string, absolutePath: string): string {
482
+ const relativePath = relative(root, absolutePath);
483
+
484
+ if (!relativePath || relativePath.startsWith("..") || isAbsolute(relativePath)) {
485
+ throw new AgentKitError(
486
+ "knowledge_source_outside_capsule",
487
+ "Knowledge source paths must stay inside the Agent Capsule.",
488
+ );
489
+ }
490
+
491
+ return relativePath.split("\\").join("/");
492
+ }
493
+
494
+ async function findExistingSource(db: DatabaseRunner, sourceKey: string): Promise<DatabaseRow | undefined> {
495
+ const result = await db.query(
496
+ "SELECT id, content_sha256, status FROM agentkit_knowledge_sources WHERE source_key = ?",
497
+ [sourceKey],
498
+ );
499
+ return result.rows[0];
500
+ }
501
+
502
+ function knowledgeSourceSummaryFromRow(row: DatabaseRow): KnowledgeSourceSummary {
503
+ return {
504
+ source: String(row.source_key),
505
+ kind: String(row.kind),
506
+ title: typeof row.title === "string" ? row.title : null,
507
+ status: String(row.status),
508
+ chunkCount: Number(row.chunk_count),
509
+ updatedAt: String(row.updated_at),
510
+ indexedAt: typeof row.indexed_at === "string" ? row.indexed_at : null,
511
+ errorMessage: typeof row.error_message === "string" ? row.error_message : null,
512
+ };
513
+ }
514
+
515
+ function stableId(prefix: string, value: string): string {
516
+ return `${prefix}_${sha256(value).slice(0, 24)}`;
517
+ }
518
+
519
+ function sha256(value: string): string {
520
+ return createHash("sha256").update(value).digest("hex");
521
+ }
@@ -0,0 +1,30 @@
1
+ import type { AgentConfig } from "../../index";
2
+ import { resolveKnowledgeConfig } from "./config";
3
+ import { KNOWLEDGE_SEARCH_TOOL_NAME } from "./tool";
4
+
5
+ export function knowledgePromptPolicy(config: Pick<AgentConfig, "knowledge">): string | null {
6
+ const knowledge = resolveKnowledgeConfig(config);
7
+
8
+ if (!knowledge.enabled) {
9
+ return null;
10
+ }
11
+
12
+ return `Knowledge rules:
13
+ - Use ${KNOWLEDGE_SEARCH_TOOL_NAME} before answering business-specific factual questions about prices, policies, availability, procedures, requirements, documents, or source-specific details.
14
+ - If Knowledge has no relevant result, say that the Knowledge sources do not contain the answer and ask for the missing detail or escalation path.
15
+ - Do not invent prices, policies, availability, legal, clinical, or source-specific facts.
16
+ - Use Knowledge results to answer naturally. Do not expose raw search JSON, scores, chunk IDs, or internal tool details to the user.`;
17
+ }
18
+
19
+ export function appendKnowledgePromptPolicy(instructions: string, config: Pick<AgentConfig, "knowledge">): string {
20
+ const policy = knowledgePromptPolicy(config);
21
+
22
+ if (!policy) {
23
+ return instructions;
24
+ }
25
+
26
+ return `${instructions.trimEnd()}
27
+
28
+ ${policy}
29
+ `;
30
+ }