@andreprado/agentkit 0.1.0-alpha.10

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 (122) hide show
  1. package/README.md +69 -0
  2. package/bin/agentkit.mjs +23 -0
  3. package/docs/guides/add-channel.md +114 -0
  4. package/docs/guides/add-knowledge.md +134 -0
  5. package/docs/guides/add-tool.md +342 -0
  6. package/docs/guides/agentkit-skills-architecture.md +471 -0
  7. package/docs/guides/channel-security.md +81 -0
  8. package/docs/guides/channels-implementation-map.md +243 -0
  9. package/docs/guides/channels-production-handoff.md +102 -0
  10. package/docs/guides/connect-telegram.md +110 -0
  11. package/docs/guides/connect-whatsapp-zapster.md +119 -0
  12. package/docs/guides/create-agent.md +220 -0
  13. package/docs/guides/prepare-deploy.md +209 -0
  14. package/docs/guides/run-evals.md +179 -0
  15. package/docs/guides/security-rules.md +156 -0
  16. package/docs/guides/use-provider.md +140 -0
  17. package/docs/llms-full.txt +876 -0
  18. package/docs/llms.txt +83 -0
  19. package/docs/portable-deploy-release-checklist.md +41 -0
  20. package/package.json +47 -0
  21. package/src/cli/args.ts +36 -0
  22. package/src/cli/cloud-client.ts +265 -0
  23. package/src/cli/commands/channels.ts +810 -0
  24. package/src/cli/commands/knowledge.ts +136 -0
  25. package/src/cli/constants.ts +4 -0
  26. package/src/cli/deploy-chat-ui.ts +392 -0
  27. package/src/cli/deploy-readiness.ts +348 -0
  28. package/src/cli/flags.ts +162 -0
  29. package/src/cli/help.ts +184 -0
  30. package/src/cli/index.ts +1276 -0
  31. package/src/cli/process.ts +31 -0
  32. package/src/cloud/artifact.ts +139 -0
  33. package/src/cloud/client.ts +79 -0
  34. package/src/cloud/contracts.ts +63 -0
  35. package/src/cloud/index.ts +3 -0
  36. package/src/create-project.ts +177 -0
  37. package/src/index.ts +408 -0
  38. package/src/providers/index.ts +25 -0
  39. package/src/providers/pi.ts +286 -0
  40. package/src/providers/test.ts +133 -0
  41. package/src/providers/types.ts +34 -0
  42. package/src/runtime/build.ts +43 -0
  43. package/src/runtime/channel-buffer.ts +30 -0
  44. package/src/runtime/channel-test-harness.ts +112 -0
  45. package/src/runtime/channels/telegram.ts +360 -0
  46. package/src/runtime/channels/website.ts +132 -0
  47. package/src/runtime/channels/whatsapp-meta.ts +71 -0
  48. package/src/runtime/channels/whatsapp-zapster.ts +278 -0
  49. package/src/runtime/channels.ts +138 -0
  50. package/src/runtime/chat.ts +218 -0
  51. package/src/runtime/config.ts +684 -0
  52. package/src/runtime/conversations.ts +38 -0
  53. package/src/runtime/core/deploy-state.ts +54 -0
  54. package/src/runtime/core/manifest.ts +213 -0
  55. package/src/runtime/core/targets.ts +133 -0
  56. package/src/runtime/database.ts +256 -0
  57. package/src/runtime/db-commands.ts +167 -0
  58. package/src/runtime/deploy-readiness.ts +105 -0
  59. package/src/runtime/deploy.ts +1 -0
  60. package/src/runtime/dev-server.ts +1247 -0
  61. package/src/runtime/docs.ts +36 -0
  62. package/src/runtime/env.ts +152 -0
  63. package/src/runtime/errors.ts +13 -0
  64. package/src/runtime/evals.ts +509 -0
  65. package/src/runtime/inspect.ts +203 -0
  66. package/src/runtime/knowledge/chunk.ts +333 -0
  67. package/src/runtime/knowledge/config.ts +135 -0
  68. package/src/runtime/knowledge/embeddings.ts +133 -0
  69. package/src/runtime/knowledge/ingest.ts +521 -0
  70. package/src/runtime/knowledge/prompt-policy.ts +30 -0
  71. package/src/runtime/knowledge/retrieve.ts +283 -0
  72. package/src/runtime/knowledge/schema.ts +56 -0
  73. package/src/runtime/knowledge/tool.ts +64 -0
  74. package/src/runtime/knowledge/vector.ts +258 -0
  75. package/src/runtime/runtime-contract.ts +93 -0
  76. package/src/runtime/spec.ts +152 -0
  77. package/src/runtime/sync.ts +144 -0
  78. package/src/runtime/targets/cloudflare/build.ts +2517 -0
  79. package/src/runtime/targets/container/build.ts +146 -0
  80. package/src/runtime/targets/container/server.ts +33 -0
  81. package/src/runtime/targets/vps/deploy.ts +206 -0
  82. package/src/runtime/tool-runner.ts +65 -0
  83. package/src/runtime/tools.ts +470 -0
  84. package/src/runtime/traces.ts +41 -0
  85. package/src/storage/sqlite.ts +1118 -0
  86. package/src/templates/blank.ts +394 -0
  87. package/src/templates/dentista.ts +1003 -0
  88. package/src/templates/index.ts +33 -0
  89. package/src/templates/skills/agentkit-build-agent/SKILL.md +51 -0
  90. package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +20 -0
  91. package/src/templates/skills/agentkit-build-agent/templates/sales-qualifier.instructions.md +17 -0
  92. package/src/templates/skills/agentkit-build-agent/templates/support-agent.instructions.md +16 -0
  93. package/src/templates/skills/agentkit-capsule/SKILL.md +62 -0
  94. package/src/templates/skills/agentkit-capsule/references/docs-router.md +15 -0
  95. package/src/templates/skills/agentkit-channels/SKILL.md +62 -0
  96. package/src/templates/skills/agentkit-channels/references/channel-buffering.md +58 -0
  97. package/src/templates/skills/agentkit-channels/references/channel-debugging.md +41 -0
  98. package/src/templates/skills/agentkit-channels/references/telegram.md +38 -0
  99. package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +44 -0
  100. package/src/templates/skills/agentkit-database/SKILL.md +45 -0
  101. package/src/templates/skills/agentkit-database/templates/appointments.schema.sql +15 -0
  102. package/src/templates/skills/agentkit-database/templates/leads.schema.sql +17 -0
  103. package/src/templates/skills/agentkit-deploy/SKILL.md +44 -0
  104. package/src/templates/skills/agentkit-evals/SKILL.md +60 -0
  105. package/src/templates/skills/agentkit-evals/templates/multi-turn.eval.md +22 -0
  106. package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +14 -0
  107. package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +14 -0
  108. package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +18 -0
  109. package/src/templates/skills/agentkit-knowledge/SKILL.md +40 -0
  110. package/src/templates/skills/agentkit-knowledge/templates/faq.md +14 -0
  111. package/src/templates/skills/agentkit-knowledge/templates/policies.md +14 -0
  112. package/src/templates/skills/agentkit-knowledge/templates/prices.csv +3 -0
  113. package/src/templates/skills/agentkit-prompts/SKILL.md +45 -0
  114. package/src/templates/skills/agentkit-prompts/templates/knowledge-grounded-faq.instructions.md +11 -0
  115. package/src/templates/skills/agentkit-provider/SKILL.md +57 -0
  116. package/src/templates/skills/agentkit-security/SKILL.md +55 -0
  117. package/src/templates/skills/agentkit-tools/SKILL.md +36 -0
  118. package/src/templates/skills/agentkit-tools/examples/database-write.tool.md +35 -0
  119. package/src/templates/skills/agentkit-tools/examples/eval-safe-external-action.tool.md +37 -0
  120. package/src/templates/skills/agentkit-tools/examples/lookup-order.tool.md +46 -0
  121. package/src/templates/skills/agentkit-troubleshooting/SKILL.md +52 -0
  122. package/src/templates/support.ts +401 -0
@@ -0,0 +1,203 @@
1
+ import { relative } from "node:path";
2
+
3
+ import type { AgentChannel, AgentProvider, AgentRuntime, AccessMode } from "../index";
4
+ import type { LoadedAgentCapsule } from "./config";
5
+ import { loadAgentCapsule } from "./config";
6
+ import { loadCapsuleEnv } from "./env";
7
+ import { resolveKnowledgeConfig } from "./knowledge/config";
8
+ import { KNOWLEDGE_SEARCH_TOOL_NAME } from "./knowledge/tool";
9
+
10
+ export type SecretState = "set" | "missing";
11
+
12
+ export type AgentInspectState = {
13
+ agent: string;
14
+ runtime: AgentRuntime;
15
+ provider: AgentProvider;
16
+ prompt: string;
17
+ tools: string[];
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
+ };
33
+ access: {
34
+ mode: AccessMode;
35
+ };
36
+ storage: string | null;
37
+ database: AgentInspectDatabase;
38
+ runtimeContract?: {
39
+ local: {
40
+ environment: "local";
41
+ target: "bun";
42
+ database: "sqlite" | "none";
43
+ };
44
+ hosted: {
45
+ environment: "hosted";
46
+ target: "cloudflare";
47
+ database: "turso" | "none";
48
+ providerSupported: boolean;
49
+ };
50
+ aliases: ["ctx.db", "ctx.database", "ctx.storage.sql"];
51
+ };
52
+ secrets: Record<string, SecretState>;
53
+ managedSecrets: string[];
54
+ userSecrets: string[];
55
+ };
56
+
57
+ export type AgentInspectDatabase = {
58
+ local: {
59
+ driver: "sqlite";
60
+ path: string | null;
61
+ schema: string | null;
62
+ migrations: string | null;
63
+ };
64
+ hosted: {
65
+ driver: "none" | "turso";
66
+ provisioning: "none" | "agentkit-managed";
67
+ schema: string | null;
68
+ migrations: string | null;
69
+ };
70
+ };
71
+
72
+ export type AgentInspectChannel = {
73
+ name: string;
74
+ type: AgentChannel["type"];
75
+ provider: AgentChannel["provider"];
76
+ secrets: string[];
77
+ buffer?: AgentChannel["buffer"];
78
+ };
79
+
80
+ export async function inspectAgentCapsule(
81
+ cwd = process.cwd(),
82
+ env: Record<string, string | undefined> = process.env,
83
+ ): Promise<AgentInspectState> {
84
+ const capsule = await loadAgentCapsule(cwd);
85
+ return buildInspectState(capsule, await loadCapsuleEnv(capsule.root, env));
86
+ }
87
+
88
+ export function buildInspectState(
89
+ capsule: LoadedAgentCapsule,
90
+ env: Record<string, string | undefined> = process.env,
91
+ ): AgentInspectState {
92
+ const declaredSecrets = new Set(capsule.config.secrets);
93
+ const knowledge = resolveKnowledgeConfig(capsule.config);
94
+
95
+ for (const tool of capsule.config.tools ?? []) {
96
+ for (const secret of tool.secrets ?? []) {
97
+ declaredSecrets.add(secret);
98
+ }
99
+ }
100
+
101
+ for (const channel of capsule.config.channels ?? []) {
102
+ for (const secret of channel.secrets) {
103
+ declaredSecrets.add(secret);
104
+ }
105
+ }
106
+
107
+ if (knowledge.embedding.secret) {
108
+ declaredSecrets.add(knowledge.embedding.secret);
109
+ }
110
+
111
+ const managedSecrets = managedCloudSecrets(capsule);
112
+ const userSecrets = Array.from(declaredSecrets).sort();
113
+ const secrets: Record<string, SecretState> = {};
114
+
115
+ for (const secret of userSecrets) {
116
+ secrets[secret] = env[secret] ? "set" : "missing";
117
+ }
118
+
119
+ const database = inspectDatabase(capsule);
120
+
121
+ return {
122
+ agent: capsule.config.name,
123
+ runtime: capsule.config.runtime,
124
+ provider: capsule.config.provider,
125
+ prompt: toCapsulePath(capsule.root, capsule.instructionsPath),
126
+ tools: (capsule.config.tools ?? []).map((tool) => tool.name),
127
+ channels: (capsule.config.channels ?? []).map((channel) => ({
128
+ name: channel.name,
129
+ type: channel.type,
130
+ provider: channel.provider,
131
+ secrets: [...channel.secrets],
132
+ ...(channel.buffer ? { buffer: channel.buffer } : {}),
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
+ },
145
+ access: capsule.config.access,
146
+ storage: capsule.storagePath ? toCapsulePath(capsule.root, capsule.storagePath) : null,
147
+ database,
148
+ runtimeContract: {
149
+ local: {
150
+ environment: "local",
151
+ target: "bun",
152
+ database: capsule.storagePath ? "sqlite" : "none",
153
+ },
154
+ hosted: {
155
+ environment: "hosted",
156
+ target: "cloudflare",
157
+ database: database.hosted.driver === "turso" ? "turso" : "none",
158
+ providerSupported: true,
159
+ },
160
+ aliases: ["ctx.db", "ctx.database", "ctx.storage.sql"],
161
+ },
162
+ secrets,
163
+ managedSecrets,
164
+ userSecrets,
165
+ };
166
+ }
167
+
168
+ function toCapsulePath(root: string, path: string): string {
169
+ const localPath = relative(root, path);
170
+ return localPath.length > 0 ? localPath : ".";
171
+ }
172
+
173
+ function inspectDatabase(capsule: LoadedAgentCapsule): AgentInspectDatabase {
174
+ const database = capsule.config.storage.database;
175
+ const schema = database?.driver === "turso" && database.schema ? database.schema : null;
176
+ const migrations = database?.driver === "turso" ? database.migrations ?? "migrations" : null;
177
+
178
+ return {
179
+ local: {
180
+ driver: "sqlite",
181
+ path: capsule.storagePath ? toCapsulePath(capsule.root, capsule.storagePath) : null,
182
+ schema,
183
+ migrations,
184
+ },
185
+ hosted: {
186
+ driver: database?.driver === "turso" ? "turso" : "none",
187
+ provisioning: database?.driver === "turso" ? "agentkit-managed" : "none",
188
+ schema,
189
+ migrations,
190
+ },
191
+ };
192
+ }
193
+
194
+ function managedCloudSecrets(capsule: LoadedAgentCapsule): string[] {
195
+ const secrets = new Set<string>();
196
+
197
+ if (capsule.config.storage.database?.driver === "turso") {
198
+ secrets.add("TURSO_AUTH_TOKEN");
199
+ secrets.add("TURSO_DATABASE_URL");
200
+ }
201
+
202
+ return Array.from(secrets).sort();
203
+ }
@@ -0,0 +1,333 @@
1
+ import { basename, extname } from "node:path";
2
+
3
+ export type KnowledgeChunkMetadata = {
4
+ sourcePath: string;
5
+ title: string;
6
+ section: string | null;
7
+ locator: string | null;
8
+ };
9
+
10
+ export type KnowledgeChunk = {
11
+ ordinal: number;
12
+ content: string;
13
+ tokenCount: number;
14
+ metadata: KnowledgeChunkMetadata;
15
+ };
16
+
17
+ export type ChunkKnowledgeSourceInput = {
18
+ sourcePath: string;
19
+ title?: string | null;
20
+ content: string;
21
+ };
22
+
23
+ const DEFAULT_MAX_WORDS = 720;
24
+ const DEFAULT_OVERLAP_WORDS = 80;
25
+
26
+ export function chunkKnowledgeSource(input: ChunkKnowledgeSourceInput): KnowledgeChunk[] {
27
+ const extension = extname(input.sourcePath).toLowerCase();
28
+
29
+ if (extension === ".csv") {
30
+ return chunkCsv(input);
31
+ }
32
+
33
+ if (extension === ".md" || extension === ".markdown") {
34
+ return chunkMarkdown(input);
35
+ }
36
+
37
+ return chunkText(input);
38
+ }
39
+
40
+ function chunkMarkdown(input: ChunkKnowledgeSourceInput): KnowledgeChunk[] {
41
+ const title = input.title ?? inferTitle(input.sourcePath, input.content);
42
+ const sections = markdownSections(input.content, title);
43
+ const chunks: KnowledgeChunk[] = [];
44
+
45
+ for (const section of sections) {
46
+ const contextualContent = [section.heading, section.body].filter(Boolean).join("\n\n").trim();
47
+ chunks.push(
48
+ ...splitContentIntoChunks({
49
+ sourcePath: input.sourcePath,
50
+ title,
51
+ section: section.heading || null,
52
+ locator: section.heading ? `section:${section.heading}` : null,
53
+ content: contextualContent,
54
+ startingOrdinal: chunks.length,
55
+ }),
56
+ );
57
+ }
58
+
59
+ return ensureAtLeastOneChunk(chunks, input, title);
60
+ }
61
+
62
+ function chunkText(input: ChunkKnowledgeSourceInput): KnowledgeChunk[] {
63
+ const title = input.title ?? inferTitle(input.sourcePath, input.content);
64
+ const paragraphs = input.content
65
+ .split(/\n{2,}/)
66
+ .map((paragraph) => paragraph.trim())
67
+ .filter(Boolean);
68
+ const content = paragraphs.length > 0 ? paragraphs.join("\n\n") : input.content.trim();
69
+
70
+ return ensureAtLeastOneChunk(
71
+ splitContentIntoChunks({
72
+ sourcePath: input.sourcePath,
73
+ title,
74
+ section: null,
75
+ locator: null,
76
+ content,
77
+ startingOrdinal: 0,
78
+ }),
79
+ input,
80
+ title,
81
+ );
82
+ }
83
+
84
+ function chunkCsv(input: ChunkKnowledgeSourceInput): KnowledgeChunk[] {
85
+ const title = input.title ?? inferTitle(input.sourcePath, input.content);
86
+ const rows = parseCsv(input.content);
87
+
88
+ if (rows.length === 0) {
89
+ return ensureAtLeastOneChunk([], input, title);
90
+ }
91
+
92
+ const [headers, ...records] = rows;
93
+
94
+ if (headers.length === 0 || records.length === 0) {
95
+ return chunkText(input);
96
+ }
97
+
98
+ const chunks: KnowledgeChunk[] = [];
99
+
100
+ for (const [index, record] of records.entries()) {
101
+ const content = headers
102
+ .map((header, columnIndex) => {
103
+ const name = header.trim() || `column_${columnIndex + 1}`;
104
+ const value = record[columnIndex]?.trim() ?? "";
105
+ return `${name}: ${value}`;
106
+ })
107
+ .join("\n")
108
+ .trim();
109
+
110
+ if (!content) {
111
+ continue;
112
+ }
113
+
114
+ chunks.push({
115
+ ordinal: chunks.length,
116
+ content,
117
+ tokenCount: estimateTokenCount(content),
118
+ metadata: {
119
+ sourcePath: input.sourcePath,
120
+ title,
121
+ section: null,
122
+ locator: `row:${index + 2}`,
123
+ },
124
+ });
125
+ }
126
+
127
+ return ensureAtLeastOneChunk(chunks, input, title);
128
+ }
129
+
130
+ function markdownSections(content: string, fallbackTitle: string): Array<{ heading: string | null; body: string }> {
131
+ const sections: Array<{ heading: string | null; body: string[] }> = [];
132
+ let current: { heading: string | null; body: string[] } = { heading: null, body: [] };
133
+ const headingStack: string[] = [];
134
+
135
+ for (const line of content.split(/\r?\n/)) {
136
+ const heading = line.match(/^(#{1,6})\s+(.+?)\s*#*\s*$/);
137
+
138
+ if (heading) {
139
+ if (current.body.join("\n").trim()) {
140
+ sections.push(current);
141
+ }
142
+
143
+ const level = heading[1].length;
144
+ headingStack.splice(level - 1);
145
+ headingStack[level - 1] = heading[2].trim();
146
+ current = {
147
+ heading: headingStack.filter(Boolean).join(" > "),
148
+ body: [],
149
+ };
150
+ continue;
151
+ }
152
+
153
+ current.body.push(line);
154
+ }
155
+
156
+ if (current.body.join("\n").trim()) {
157
+ sections.push(current);
158
+ }
159
+
160
+ if (sections.length === 0) {
161
+ return [{ heading: fallbackTitle, body: content.trim() }];
162
+ }
163
+
164
+ return sections.map((section) => ({
165
+ heading: section.heading,
166
+ body: section.body.join("\n").trim(),
167
+ }));
168
+ }
169
+
170
+ function splitContentIntoChunks(input: {
171
+ sourcePath: string;
172
+ title: string;
173
+ section: string | null;
174
+ locator: string | null;
175
+ content: string;
176
+ startingOrdinal: number;
177
+ }): KnowledgeChunk[] {
178
+ const words = wordsOf(input.content);
179
+
180
+ if (words.length <= DEFAULT_MAX_WORDS) {
181
+ const content = input.content.trim();
182
+
183
+ if (!content) {
184
+ return [];
185
+ }
186
+
187
+ return [
188
+ {
189
+ ordinal: input.startingOrdinal,
190
+ content,
191
+ tokenCount: estimateTokenCount(content),
192
+ metadata: {
193
+ sourcePath: input.sourcePath,
194
+ title: input.title,
195
+ section: input.section,
196
+ locator: input.locator,
197
+ },
198
+ },
199
+ ];
200
+ }
201
+
202
+ const chunks: KnowledgeChunk[] = [];
203
+ let start = 0;
204
+
205
+ while (start < words.length) {
206
+ const end = Math.min(start + DEFAULT_MAX_WORDS, words.length);
207
+ const content = words.slice(start, end).join(" ");
208
+
209
+ chunks.push({
210
+ ordinal: input.startingOrdinal + chunks.length,
211
+ content,
212
+ tokenCount: estimateTokenCount(content),
213
+ metadata: {
214
+ sourcePath: input.sourcePath,
215
+ title: input.title,
216
+ section: input.section,
217
+ locator: input.locator ? `${input.locator}:part:${chunks.length + 1}` : `part:${chunks.length + 1}`,
218
+ },
219
+ });
220
+
221
+ if (end === words.length) {
222
+ break;
223
+ }
224
+
225
+ start = Math.max(end - DEFAULT_OVERLAP_WORDS, start + 1);
226
+ }
227
+
228
+ return chunks;
229
+ }
230
+
231
+ function ensureAtLeastOneChunk(
232
+ chunks: KnowledgeChunk[],
233
+ input: ChunkKnowledgeSourceInput,
234
+ title: string,
235
+ ): KnowledgeChunk[] {
236
+ if (chunks.length > 0) {
237
+ return chunks.map((chunk, index) => ({ ...chunk, ordinal: index }));
238
+ }
239
+
240
+ const content = input.content.trim();
241
+
242
+ if (!content) {
243
+ return [];
244
+ }
245
+
246
+ return [
247
+ {
248
+ ordinal: 0,
249
+ content,
250
+ tokenCount: estimateTokenCount(content),
251
+ metadata: {
252
+ sourcePath: input.sourcePath,
253
+ title,
254
+ section: null,
255
+ locator: null,
256
+ },
257
+ },
258
+ ];
259
+ }
260
+
261
+ function parseCsv(content: string): string[][] {
262
+ const rows: string[][] = [];
263
+ let row: string[] = [];
264
+ let field = "";
265
+ let quoted = false;
266
+
267
+ for (let index = 0; index < content.length; index += 1) {
268
+ const char = content[index];
269
+ const next = content[index + 1];
270
+
271
+ if (quoted) {
272
+ if (char === '"' && next === '"') {
273
+ field += '"';
274
+ index += 1;
275
+ continue;
276
+ }
277
+
278
+ if (char === '"') {
279
+ quoted = false;
280
+ continue;
281
+ }
282
+
283
+ field += char;
284
+ continue;
285
+ }
286
+
287
+ if (char === '"') {
288
+ quoted = true;
289
+ continue;
290
+ }
291
+
292
+ if (char === ",") {
293
+ row.push(field);
294
+ field = "";
295
+ continue;
296
+ }
297
+
298
+ if (char === "\n") {
299
+ row.push(field.replace(/\r$/, ""));
300
+ rows.push(row);
301
+ row = [];
302
+ field = "";
303
+ continue;
304
+ }
305
+
306
+ field += char;
307
+ }
308
+
309
+ row.push(field.replace(/\r$/, ""));
310
+ if (row.some((value) => value.length > 0)) {
311
+ rows.push(row);
312
+ }
313
+
314
+ return rows;
315
+ }
316
+
317
+ function inferTitle(sourcePath: string, content: string): string {
318
+ const markdownTitle = content.match(/^#\s+(.+)$/m)?.[1]?.trim();
319
+
320
+ if (markdownTitle) {
321
+ return markdownTitle;
322
+ }
323
+
324
+ return basename(sourcePath).replace(/\.[^.]+$/, "") || sourcePath;
325
+ }
326
+
327
+ function wordsOf(content: string): string[] {
328
+ return content.replace(/\s+/g, " ").trim().split(" ").filter(Boolean);
329
+ }
330
+
331
+ export function estimateTokenCount(content: string): number {
332
+ return Math.max(1, wordsOf(content).length);
333
+ }
@@ -0,0 +1,135 @@
1
+ import type {
2
+ AgentConfig,
3
+ AgentKnowledgeConfig,
4
+ AgentKnowledgeEmbeddingProvider,
5
+ AgentKnowledgeSource,
6
+ } from "../../index";
7
+
8
+ export type ResolvedKnowledgeSource =
9
+ | {
10
+ kind: "file";
11
+ value: string;
12
+ title: string | null;
13
+ }
14
+ | {
15
+ kind: "url";
16
+ value: string;
17
+ title: string | null;
18
+ };
19
+
20
+ export type ResolvedKnowledgeConfig = {
21
+ enabled: boolean;
22
+ sources: ResolvedKnowledgeSource[];
23
+ embedding: {
24
+ provider: AgentKnowledgeEmbeddingProvider;
25
+ model: string | null;
26
+ dimensions: number | null;
27
+ secret: string | null;
28
+ };
29
+ retrieval: {
30
+ topK: number;
31
+ hybrid: boolean;
32
+ };
33
+ };
34
+
35
+ const DEFAULT_TOP_K = 8;
36
+ const DEFAULT_TEST_EMBEDDING_DIMENSIONS = 32;
37
+ const DEFAULT_OPENAI_EMBEDDING_MODEL = "text-embedding-3-small";
38
+ const DEFAULT_OPENAI_EMBEDDING_DIMENSIONS = 1536;
39
+
40
+ export function resolveKnowledgeConfig(config: Pick<AgentConfig, "knowledge">): ResolvedKnowledgeConfig {
41
+ const knowledge = config.knowledge;
42
+
43
+ if (!knowledge) {
44
+ return {
45
+ enabled: false,
46
+ sources: [],
47
+ embedding: {
48
+ provider: "none",
49
+ model: null,
50
+ dimensions: null,
51
+ secret: null,
52
+ },
53
+ retrieval: {
54
+ topK: DEFAULT_TOP_K,
55
+ hybrid: false,
56
+ },
57
+ };
58
+ }
59
+
60
+ const provider = knowledge.embedding?.provider ?? "none";
61
+ const embedding = resolveEmbeddingConfig(provider, knowledge);
62
+ const hybrid = knowledge.retrieval?.hybrid ?? provider !== "none";
63
+
64
+ return {
65
+ enabled: true,
66
+ sources: (knowledge.sources ?? []).map(resolveKnowledgeSource),
67
+ embedding,
68
+ retrieval: {
69
+ topK: knowledge.retrieval?.topK ?? DEFAULT_TOP_K,
70
+ hybrid,
71
+ },
72
+ };
73
+ }
74
+
75
+ function resolveEmbeddingConfig(
76
+ provider: AgentKnowledgeEmbeddingProvider,
77
+ knowledge: AgentKnowledgeConfig,
78
+ ): ResolvedKnowledgeConfig["embedding"] {
79
+ if (provider === "openai") {
80
+ return {
81
+ provider,
82
+ model: knowledge.embedding?.model ?? DEFAULT_OPENAI_EMBEDDING_MODEL,
83
+ dimensions: knowledge.embedding?.dimensions ?? DEFAULT_OPENAI_EMBEDDING_DIMENSIONS,
84
+ secret: knowledge.embedding?.secret ?? "OPENAI_API_KEY",
85
+ };
86
+ }
87
+
88
+ if (provider === "test") {
89
+ return {
90
+ provider,
91
+ model: knowledge.embedding?.model ?? "fake",
92
+ dimensions: knowledge.embedding?.dimensions ?? DEFAULT_TEST_EMBEDDING_DIMENSIONS,
93
+ secret: knowledge.embedding?.secret ?? null,
94
+ };
95
+ }
96
+
97
+ return {
98
+ provider: "none",
99
+ model: knowledge.embedding?.model ?? null,
100
+ dimensions: knowledge.embedding?.dimensions ?? null,
101
+ secret: knowledge.embedding?.secret ?? null,
102
+ };
103
+ }
104
+
105
+ function resolveKnowledgeSource(source: AgentKnowledgeSource): ResolvedKnowledgeSource {
106
+ if (typeof source === "string") {
107
+ if (/^https?:\/\//i.test(source)) {
108
+ return {
109
+ kind: "url",
110
+ value: source,
111
+ title: null,
112
+ };
113
+ }
114
+
115
+ return {
116
+ kind: "file",
117
+ value: source,
118
+ title: null,
119
+ };
120
+ }
121
+
122
+ if ("url" in source) {
123
+ return {
124
+ kind: "url",
125
+ value: source.url,
126
+ title: source.title ?? null,
127
+ };
128
+ }
129
+
130
+ return {
131
+ kind: "file",
132
+ value: source.path,
133
+ title: source.title ?? null,
134
+ };
135
+ }