@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
@@ -1,6 +1,6 @@
1
- import { readdir } from "node:fs/promises";
1
+ import { mkdir, readdir, stat, writeFile } from "node:fs/promises";
2
2
  import type { Dirent } from "node:fs";
3
- import { join, relative } from "node:path";
3
+ import { dirname, join, relative, resolve } from "node:path";
4
4
  import { pathToFileURL } from "node:url";
5
5
 
6
6
  import type { AgentRunResult } from "./chat";
@@ -8,6 +8,7 @@ import { runAgentMessageFromCwd } from "./chat";
8
8
  import { findAgentCapsuleRoot, loadAgentCapsule } from "./config";
9
9
  import { AgentKitError } from "./errors";
10
10
  import { openCapsuleStore, type StoredToolCall } from "../storage/sqlite";
11
+ import { getConversationTraceFromCwd } from "./traces";
11
12
 
12
13
  export type EvalRunSummary = {
13
14
  root: string;
@@ -27,9 +28,17 @@ export type EvalResult = {
27
28
  type EvalCase = {
28
29
  name?: string;
29
30
  input?: string;
31
+ turns?: EvalTurn[];
30
32
  expect?: EvalExpect;
31
33
  };
32
34
 
35
+ type EvalTurn =
36
+ | string
37
+ | {
38
+ input: string;
39
+ expect?: EvalExpect;
40
+ };
41
+
33
42
  type EvalExpect = {
34
43
  contains?: string | string[];
35
44
  not_contains?: string | string[];
@@ -55,16 +64,25 @@ type ToolExpectation =
55
64
  output?: unknown;
56
65
  status?: "running" | "completed" | "failed";
57
66
  visibility?: "user" | "internal";
67
+ rendered?: unknown;
58
68
  };
59
69
 
60
70
  type ToolCallSnapshot = {
61
71
  name?: unknown;
62
72
  input?: unknown;
63
73
  output?: unknown;
74
+ rendered?: unknown;
64
75
  status?: unknown;
65
76
  visibility?: unknown;
66
77
  };
67
78
 
79
+ export type EvalFromConversationResult = {
80
+ root: string;
81
+ file: string;
82
+ conversationId: string;
83
+ turns: number;
84
+ };
85
+
68
86
  export async function runEvalsFromCwd(cwd: string): Promise<EvalRunSummary> {
69
87
  const root = await findAgentCapsuleRoot(cwd);
70
88
  const evalFiles = await findEvalFiles(join(root, "evals"));
@@ -77,28 +95,14 @@ export async function runEvalsFromCwd(cwd: string): Promise<EvalRunSummary> {
77
95
 
78
96
  for (const file of evalFiles) {
79
97
  const evalCase = await loadEvalCase(file);
80
- const input = evalCase.input;
81
-
82
- if (typeof input !== "string" || input.trim().length === 0) {
83
- throw new AgentKitError("validation_error", `${relative(root, file)} must export an eval with a non-empty input string.`);
84
- }
85
-
86
- const run = await runAgentMessageFromCwd(root, {
87
- message: input,
88
- runtime: {
89
- environment: "eval",
90
- invocation: "eval",
91
- },
92
- });
93
- const persistedToolCalls = await loadPersistedToolCalls(root, run.runId);
94
- const failures = evaluateExpectations(evalCase.expect ?? {}, run, persistedToolCalls);
98
+ const run = await runEvalCase(root, relative(root, file), evalCase);
95
99
 
96
100
  results.push({
97
101
  name: evalCase.name ?? relative(root, file),
98
102
  file: relative(root, file),
99
- passed: failures.length === 0,
100
- failures,
101
- output: run.message.content,
103
+ passed: run.failures.length === 0,
104
+ failures: run.failures,
105
+ output: run.output,
102
106
  });
103
107
  }
104
108
 
@@ -112,6 +116,133 @@ export async function runEvalsFromCwd(cwd: string): Promise<EvalRunSummary> {
112
116
  };
113
117
  }
114
118
 
119
+ export async function writeEvalFromConversation(
120
+ cwd: string,
121
+ input: {
122
+ conversationId: string;
123
+ out?: string;
124
+ name?: string;
125
+ force?: boolean;
126
+ },
127
+ ): Promise<EvalFromConversationResult> {
128
+ const root = await findAgentCapsuleRoot(cwd);
129
+ const trace = await getConversationTraceFromCwd(root, input.conversationId);
130
+ const turns = replayTurnsFromMessages(trace.messages);
131
+
132
+ if (turns.length === 0) {
133
+ throw new AgentKitError(
134
+ "validation_error",
135
+ `Conversation "${input.conversationId}" does not contain user/assistant turns that can become an eval.`,
136
+ );
137
+ }
138
+
139
+ const file = resolve(root, input.out ?? join("evals", `replay-${slugify(trace.title ?? trace.id)}.eval.ts`));
140
+
141
+ if (!input.force && await pathExists(file)) {
142
+ throw new AgentKitError(
143
+ "validation_error",
144
+ `${relative(root, file)} already exists. Re-run with --force or choose --out <path>.`,
145
+ );
146
+ }
147
+
148
+ await mkdir(dirname(file), { recursive: true });
149
+ await writeFile(
150
+ file,
151
+ `export default {
152
+ name: ${JSON.stringify(input.name ?? `replay ${trace.title ?? trace.id}`)},
153
+ turns: ${formatEvalTurns(turns)},
154
+ };
155
+ `,
156
+ );
157
+
158
+ return {
159
+ root,
160
+ file,
161
+ conversationId: trace.id,
162
+ turns: turns.length,
163
+ };
164
+ }
165
+
166
+ async function runEvalCase(
167
+ root: string,
168
+ file: string,
169
+ evalCase: EvalCase,
170
+ ): Promise<{ failures: string[]; output: string }> {
171
+ const turns = normalizeEvalTurns(evalCase);
172
+
173
+ if (turns.length === 0) {
174
+ throw new AgentKitError(
175
+ "validation_error",
176
+ `${file} must export either a non-empty input string or a non-empty turns array.`,
177
+ );
178
+ }
179
+
180
+ const conversationId = `eval_${crypto.randomUUID()}`;
181
+ const failures: string[] = [];
182
+ let output = "";
183
+
184
+ for (const [index, turn] of turns.entries()) {
185
+ const run = await runAgentMessageFromCwd(root, {
186
+ message: turn.input,
187
+ conversationId,
188
+ runtime: {
189
+ environment: "eval",
190
+ invocation: "eval",
191
+ },
192
+ });
193
+ const persistedToolCalls = await loadPersistedToolCalls(root, run.runId);
194
+ const turnFailures = evaluateExpectations(turn.expect ?? {}, run, persistedToolCalls);
195
+
196
+ for (const failure of turnFailures) {
197
+ failures.push(turns.length === 1 ? failure : `turn ${index + 1}: ${failure}`);
198
+ }
199
+
200
+ output = run.message.content;
201
+ }
202
+
203
+ return { failures, output };
204
+ }
205
+
206
+ function normalizeEvalTurns(evalCase: EvalCase): Array<{ input: string; expect?: EvalExpect }> {
207
+ if (evalCase.turns !== undefined) {
208
+ if (!Array.isArray(evalCase.turns)) {
209
+ throw new AgentKitError("validation_error", "eval turns must be an array when provided.");
210
+ }
211
+
212
+ return evalCase.turns.map((turn, index) => normalizeEvalTurn(turn, index));
213
+ }
214
+
215
+ if (typeof evalCase.input === "string" && evalCase.input.trim().length > 0) {
216
+ return [
217
+ {
218
+ input: evalCase.input,
219
+ expect: evalCase.expect,
220
+ },
221
+ ];
222
+ }
223
+
224
+ return [];
225
+ }
226
+
227
+ function normalizeEvalTurn(turn: EvalTurn, index: number): { input: string; expect?: EvalExpect } {
228
+ if (typeof turn === "string") {
229
+ if (turn.trim().length === 0) {
230
+ throw new AgentKitError("validation_error", `eval turns[${index}] must be non-empty.`);
231
+ }
232
+
233
+ return { input: turn };
234
+ }
235
+
236
+ if (!isRecord(turn) || typeof turn.input !== "string" || turn.input.trim().length === 0) {
237
+ throw new AgentKitError("validation_error", `eval turns[${index}].input must be a non-empty string.`);
238
+ }
239
+
240
+ return {
241
+ input: turn.input,
242
+ ...(turn.expect ? { expect: turn.expect } : {}),
243
+ };
244
+ }
245
+
115
246
  async function findEvalFiles(directory: string): Promise<string[]> {
116
247
  let entries: Dirent[];
117
248
 
@@ -253,6 +384,10 @@ function matchesToolExpectation(toolCall: ToolCallSnapshot, expectation: ToolExp
253
384
  return false;
254
385
  }
255
386
 
387
+ if ("rendered" in expectation && !jsonContains(toolCall.rendered, expectation.rendered)) {
388
+ return false;
389
+ }
390
+
256
391
  return true;
257
392
  }
258
393
 
@@ -269,11 +404,66 @@ function toolCallSnapshotFromStored(toolCall: StoredToolCall): ToolCallSnapshot
269
404
  name: toolCall.toolName,
270
405
  input: toolCall.input,
271
406
  output: toolCall.output,
407
+ rendered: toolCall.rendered,
272
408
  status: toolCall.status,
273
409
  visibility: toolCall.visibility,
274
410
  };
275
411
  }
276
412
 
413
+ function replayTurnsFromMessages(
414
+ messages: Array<{ role: string; content: string }>,
415
+ ): Array<{ input: string; expect?: EvalExpect }> {
416
+ const turns: Array<{ input: string; expect?: EvalExpect }> = [];
417
+
418
+ for (let index = 0; index < messages.length; index += 1) {
419
+ const message = messages[index];
420
+
421
+ if (message.role !== "user") {
422
+ continue;
423
+ }
424
+
425
+ const nextAssistant = messages.slice(index + 1).find((candidate) => candidate.role === "assistant");
426
+ turns.push({
427
+ input: message.content,
428
+ ...(nextAssistant
429
+ ? {
430
+ expect: {
431
+ contains: nextAssistant.content,
432
+ },
433
+ }
434
+ : {}),
435
+ });
436
+ }
437
+
438
+ return turns;
439
+ }
440
+
441
+ function formatEvalTurns(turns: Array<{ input: string; expect?: EvalExpect }>): string {
442
+ return JSON.stringify(turns, null, 4)
443
+ .split("\n")
444
+ .map((line, index) => (index === 0 ? line : ` ${line}`))
445
+ .join("\n");
446
+ }
447
+
448
+ function slugify(value: string): string {
449
+ const slug = value
450
+ .toLowerCase()
451
+ .replace(/[^a-z0-9]+/g, "-")
452
+ .replace(/^-+|-+$/g, "")
453
+ .slice(0, 48);
454
+
455
+ return slug || "conversation";
456
+ }
457
+
458
+ async function pathExists(path: string): Promise<boolean> {
459
+ try {
460
+ await stat(path);
461
+ return true;
462
+ } catch {
463
+ return false;
464
+ }
465
+ }
466
+
277
467
  function jsonContains(actual: unknown, expected: unknown): boolean {
278
468
  if (Array.isArray(expected)) {
279
469
  return jsonEqual(actual, expected);
@@ -4,6 +4,8 @@ import type { AgentChannel, AgentProvider, AgentRuntime, AccessMode } from "../i
4
4
  import type { LoadedAgentCapsule } from "./config";
5
5
  import { loadAgentCapsule } from "./config";
6
6
  import { loadCapsuleEnv } from "./env";
7
+ import { resolveKnowledgeConfig } from "./knowledge/config";
8
+ import { KNOWLEDGE_SEARCH_TOOL_NAME } from "./knowledge/tool";
7
9
 
8
10
  export type SecretState = "set" | "missing";
9
11
 
@@ -14,6 +16,20 @@ export type AgentInspectState = {
14
16
  prompt: string;
15
17
  tools: string[];
16
18
  channels: AgentInspectChannel[];
19
+ knowledge: {
20
+ enabled: boolean;
21
+ sources: string[];
22
+ embedding: {
23
+ provider: string;
24
+ model: string | null;
25
+ dimensions: number | null;
26
+ };
27
+ retrieval: {
28
+ topK: number;
29
+ hybrid: boolean;
30
+ };
31
+ internalTool: string | null;
32
+ };
17
33
  access: {
18
34
  mode: AccessMode;
19
35
  };
@@ -43,11 +59,13 @@ export type AgentInspectDatabase = {
43
59
  driver: "sqlite";
44
60
  path: string | null;
45
61
  schema: string | null;
62
+ migrations: string | null;
46
63
  };
47
64
  hosted: {
48
65
  driver: "none" | "turso";
49
66
  provisioning: "none" | "agentkit-managed";
50
67
  schema: string | null;
68
+ migrations: string | null;
51
69
  };
52
70
  };
53
71
 
@@ -56,6 +74,7 @@ export type AgentInspectChannel = {
56
74
  type: AgentChannel["type"];
57
75
  provider: AgentChannel["provider"];
58
76
  secrets: string[];
77
+ buffer?: AgentChannel["buffer"];
59
78
  };
60
79
 
61
80
  export async function inspectAgentCapsule(
@@ -71,6 +90,7 @@ export function buildInspectState(
71
90
  env: Record<string, string | undefined> = process.env,
72
91
  ): AgentInspectState {
73
92
  const declaredSecrets = new Set(capsule.config.secrets);
93
+ const knowledge = resolveKnowledgeConfig(capsule.config);
74
94
 
75
95
  for (const tool of capsule.config.tools ?? []) {
76
96
  for (const secret of tool.secrets ?? []) {
@@ -84,6 +104,10 @@ export function buildInspectState(
84
104
  }
85
105
  }
86
106
 
107
+ if (knowledge.embedding.secret) {
108
+ declaredSecrets.add(knowledge.embedding.secret);
109
+ }
110
+
87
111
  const managedSecrets = managedCloudSecrets(capsule);
88
112
  const userSecrets = Array.from(declaredSecrets).sort();
89
113
  const secrets: Record<string, SecretState> = {};
@@ -105,7 +129,19 @@ export function buildInspectState(
105
129
  type: channel.type,
106
130
  provider: channel.provider,
107
131
  secrets: [...channel.secrets],
132
+ ...(channel.buffer ? { buffer: channel.buffer } : {}),
108
133
  })),
134
+ knowledge: {
135
+ enabled: knowledge.enabled,
136
+ sources: knowledge.sources.map((source) => source.value),
137
+ embedding: {
138
+ provider: knowledge.embedding.provider,
139
+ model: knowledge.embedding.model,
140
+ dimensions: knowledge.embedding.dimensions,
141
+ },
142
+ retrieval: knowledge.retrieval,
143
+ internalTool: knowledge.enabled ? KNOWLEDGE_SEARCH_TOOL_NAME : null,
144
+ },
109
145
  access: capsule.config.access,
110
146
  storage: capsule.storagePath ? toCapsulePath(capsule.root, capsule.storagePath) : null,
111
147
  database,
@@ -137,17 +173,20 @@ function toCapsulePath(root: string, path: string): string {
137
173
  function inspectDatabase(capsule: LoadedAgentCapsule): AgentInspectDatabase {
138
174
  const database = capsule.config.storage.database;
139
175
  const schema = database?.driver === "turso" && database.schema ? database.schema : null;
176
+ const migrations = database?.driver === "turso" ? database.migrations ?? "migrations" : null;
140
177
 
141
178
  return {
142
179
  local: {
143
180
  driver: "sqlite",
144
181
  path: capsule.storagePath ? toCapsulePath(capsule.root, capsule.storagePath) : null,
145
182
  schema,
183
+ migrations,
146
184
  },
147
185
  hosted: {
148
186
  driver: database?.driver === "turso" ? "turso" : "none",
149
187
  provisioning: database?.driver === "turso" ? "agentkit-managed" : "none",
150
188
  schema,
189
+ migrations,
151
190
  },
152
191
  };
153
192
  }