@andreprado/agentkit 0.1.0 → 0.2.0
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.
- package/README.md +8 -77
- package/docs/guides/add-channel.md +14 -92
- package/docs/guides/add-knowledge.md +0 -21
- package/docs/guides/add-tool.md +5 -11
- package/docs/guides/channel-security.md +3 -207
- package/docs/guides/connect-discord.md +7 -172
- package/docs/guides/connect-slack.md +6 -121
- package/docs/guides/connect-telegram.md +6 -165
- package/docs/guides/connect-whatsapp-evolution.md +6 -116
- package/docs/guides/connect-whatsapp-uazapi.md +6 -134
- package/docs/guides/connect-whatsapp-zapster.md +6 -202
- package/docs/guides/create-agent.md +5 -14
- package/docs/guides/debug-channel.md +4 -156
- package/docs/guides/improve-local.md +13 -0
- package/docs/guides/local-only-migration.md +35 -0
- package/docs/guides/replay-local-traces.md +11 -0
- package/docs/guides/run-evals.md +2 -4
- package/docs/guides/security-rules.md +5 -154
- package/docs/guides/use-jev.md +3 -6
- package/docs/guides/use-provider.md +0 -3
- package/docs/guides/write-feedback.md +10 -0
- package/docs/llms-full.txt +27 -448
- package/docs/llms.txt +8 -44
- package/package.json +2 -4
- package/src/cli/commands/channels.ts +8 -1613
- package/src/cli/commands/feedback.ts +8 -86
- package/src/cli/constants.ts +0 -3
- package/src/cli/flags.ts +0 -28
- package/src/cli/help.ts +16 -92
- package/src/cli/index.ts +15 -1091
- package/src/index.ts +6 -158
- package/src/providers/pi.ts +2 -0
- package/src/runtime/channels/discord.ts +16 -8
- package/src/runtime/channels/generic-webhook.ts +21 -313
- package/src/runtime/channels/net-guard.ts +480 -0
- package/src/runtime/channels/provider-fetch.ts +54 -0
- package/src/runtime/channels/slack.ts +9 -3
- package/src/runtime/channels/telegram.ts +43 -10
- package/src/runtime/channels/whatsapp-evolution.ts +33 -60
- package/src/runtime/channels/whatsapp-uazapi.ts +33 -164
- package/src/runtime/channels/whatsapp-zapster.ts +35 -7
- package/src/runtime/chat.ts +5 -3
- package/src/runtime/config.ts +36 -147
- package/src/runtime/database.ts +2 -2
- package/src/runtime/dev-server.ts +8 -8
- package/src/runtime/env.ts +11 -0
- package/src/runtime/improve.ts +2 -262
- package/src/runtime/inspect.ts +13 -73
- package/src/runtime/knowledge/embeddings.ts +45 -7
- package/src/runtime/knowledge/ingest.ts +70 -7
- package/src/runtime/knowledge/tool.ts +16 -2
- package/src/runtime/knowledge/vector.ts +31 -31
- package/src/runtime/tool-runner.ts +5 -3
- package/src/runtime/tools.ts +10 -14
- package/src/storage/sqlite.ts +11 -32
- package/src/templates/blank.ts +15 -102
- package/src/templates/common.ts +60 -0
- package/src/templates/dentista.ts +7 -74
- package/src/templates/skills/agentkit-capsule/SKILL.md +5 -7
- package/src/templates/skills/agentkit-capsule/references/docs-router.md +2 -3
- package/src/templates/skills/agentkit-channels/SKILL.md +6 -119
- package/src/templates/skills/agentkit-channels/references/channel-buffering.md +0 -9
- package/src/templates/skills/agentkit-channels/references/channel-debugging.md +1 -64
- package/src/templates/skills/agentkit-channels/references/discord.md +2 -92
- package/src/templates/skills/agentkit-channels/references/slack.md +2 -55
- package/src/templates/skills/agentkit-channels/references/telegram.md +2 -71
- package/src/templates/skills/agentkit-channels/references/whatsapp-evolution.md +2 -56
- package/src/templates/skills/agentkit-channels/references/whatsapp-uazapi.md +2 -53
- package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +2 -70
- package/src/templates/skills/agentkit-database/SKILL.md +2 -4
- package/src/templates/skills/agentkit-evals/SKILL.md +1 -1
- package/src/templates/skills/agentkit-improve/SKILL.md +6 -85
- package/src/templates/skills/agentkit-improve/references/trace-packets.md +1 -1
- package/src/templates/skills/agentkit-provider/SKILL.md +0 -1
- package/src/templates/skills/agentkit-security/SKILL.md +1 -3
- package/src/templates/skills/agentkit-tools/SKILL.md +1 -1
- package/src/templates/skills/agentkit-tools/examples/database-write.tool.md +1 -2
- package/src/templates/skills/agentkit-troubleshooting/SKILL.md +5 -11
- package/src/templates/support.ts +8 -92
- package/docs/guides/add-managed-composio.md +0 -165
- package/docs/guides/improve-from-production.md +0 -151
- package/docs/guides/prepare-deploy.md +0 -227
- package/docs/guides/replay-production-traces.md +0 -72
- package/docs/guides/send-feedback.md +0 -135
- package/src/cli/cloud-client.ts +0 -377
- package/src/cli/deploy-chat-ui.ts +0 -606
- package/src/cli/deploy-readiness.ts +0 -561
- package/src/cloud/artifact.ts +0 -139
- package/src/cloud/client.ts +0 -80
- package/src/cloud/contracts.ts +0 -63
- package/src/cloud/index.ts +0 -3
- package/src/runtime/build.ts +0 -43
- package/src/runtime/core/deploy-state.ts +0 -54
- package/src/runtime/core/manifest.ts +0 -283
- package/src/runtime/core/targets.ts +0 -133
- package/src/runtime/deploy-readiness.ts +0 -135
- package/src/runtime/deploy.ts +0 -1
- package/src/runtime/integrations/composio.ts +0 -425
- package/src/runtime/targets/cloudflare/build.ts +0 -3319
- package/src/runtime/targets/container/build.ts +0 -146
- package/src/runtime/targets/container/server.ts +0 -33
- package/src/runtime/targets/vps/deploy.ts +0 -223
- package/src/templates/skills/agentkit-deploy/SKILL.md +0 -52
- package/src/templates/skills/agentkit-integrations/SKILL.md +0 -98
package/src/runtime/inspect.ts
CHANGED
|
@@ -4,11 +4,6 @@ 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 {
|
|
8
|
-
createManagedComposioTools,
|
|
9
|
-
managedComposioRequiredSecrets,
|
|
10
|
-
resolveManagedComposioIntegrations,
|
|
11
|
-
} from "./integrations/composio";
|
|
12
7
|
import { resolveKnowledgeConfig } from "./knowledge/config";
|
|
13
8
|
import { KNOWLEDGE_SEARCH_TOOL_NAME } from "./knowledge/tool";
|
|
14
9
|
import { resolveRuntimeTimeZone } from "./prompt-context";
|
|
@@ -27,7 +22,6 @@ export type AgentInspectState = {
|
|
|
27
22
|
};
|
|
28
23
|
prompt: string;
|
|
29
24
|
tools: string[];
|
|
30
|
-
integrations: AgentInspectIntegration[];
|
|
31
25
|
channels: AgentInspectChannel[];
|
|
32
26
|
transcription: {
|
|
33
27
|
enabled: boolean;
|
|
@@ -67,16 +61,9 @@ export type AgentInspectState = {
|
|
|
67
61
|
target: "bun";
|
|
68
62
|
database: "sqlite" | "none";
|
|
69
63
|
};
|
|
70
|
-
hosted: {
|
|
71
|
-
environment: "hosted";
|
|
72
|
-
target: "cloudflare";
|
|
73
|
-
database: "turso" | "none";
|
|
74
|
-
providerSupported: boolean;
|
|
75
|
-
};
|
|
76
64
|
aliases: ["ctx.db", "ctx.database", "ctx.storage.sql"];
|
|
77
65
|
};
|
|
78
66
|
secrets: Record<string, SecretState>;
|
|
79
|
-
managedSecrets: string[];
|
|
80
67
|
userSecrets: string[];
|
|
81
68
|
skills: AgentInspectSkills;
|
|
82
69
|
warnings: AgentInspectWarning[];
|
|
@@ -89,12 +76,6 @@ export type AgentInspectDatabase = {
|
|
|
89
76
|
schema: string | null;
|
|
90
77
|
migrations: string | null;
|
|
91
78
|
};
|
|
92
|
-
hosted: {
|
|
93
|
-
driver: "none" | "turso";
|
|
94
|
-
provisioning: "none" | "agentkit-managed";
|
|
95
|
-
schema: string | null;
|
|
96
|
-
migrations: string | null;
|
|
97
|
-
};
|
|
98
79
|
};
|
|
99
80
|
|
|
100
81
|
export type AgentInspectChannel = {
|
|
@@ -127,15 +108,6 @@ export type AgentInspectWarning = {
|
|
|
127
108
|
command?: string;
|
|
128
109
|
};
|
|
129
110
|
|
|
130
|
-
export type AgentInspectIntegration = {
|
|
131
|
-
name: string;
|
|
132
|
-
provider: "composio";
|
|
133
|
-
mode: "managed";
|
|
134
|
-
toolkits: string[];
|
|
135
|
-
tools: string[];
|
|
136
|
-
userId: string;
|
|
137
|
-
};
|
|
138
|
-
|
|
139
111
|
export async function inspectAgentCapsule(
|
|
140
112
|
cwd = process.cwd(),
|
|
141
113
|
env: Record<string, string | undefined> = process.env,
|
|
@@ -151,10 +123,9 @@ export function buildInspectState(
|
|
|
151
123
|
): AgentInspectState {
|
|
152
124
|
const declaredSecrets = new Set(capsule.config.secrets);
|
|
153
125
|
const knowledge = resolveKnowledgeConfig(capsule.config);
|
|
154
|
-
const integrationTools = createManagedComposioTools(capsule.config);
|
|
155
126
|
const transcription = resolveTranscriptionConfig(capsule.config.transcription);
|
|
156
127
|
|
|
157
|
-
for (const tool of
|
|
128
|
+
for (const tool of capsule.config.tools ?? []) {
|
|
158
129
|
for (const secret of tool.secrets ?? []) {
|
|
159
130
|
declaredSecrets.add(secret);
|
|
160
131
|
}
|
|
@@ -174,10 +145,7 @@ export function buildInspectState(
|
|
|
174
145
|
declaredSecrets.add(transcription.secret);
|
|
175
146
|
}
|
|
176
147
|
|
|
177
|
-
const managedSecrets = managedCloudSecrets(capsule);
|
|
178
|
-
const managedSecretSet = new Set(managedSecrets);
|
|
179
148
|
const userSecrets = Array.from(declaredSecrets)
|
|
180
|
-
.filter((secret) => !managedSecretSet.has(secret))
|
|
181
149
|
.sort();
|
|
182
150
|
const secrets: Record<string, SecretState> = {};
|
|
183
151
|
|
|
@@ -202,16 +170,7 @@ export function buildInspectState(
|
|
|
202
170
|
}),
|
|
203
171
|
},
|
|
204
172
|
prompt: toCapsulePath(capsule.root, capsule.instructionsPath),
|
|
205
|
-
tools:
|
|
206
|
-
integrations: resolveManagedComposioIntegrations(capsule.config).map((integration) => ({
|
|
207
|
-
name: integration.name,
|
|
208
|
-
provider: integration.provider,
|
|
209
|
-
mode: integration.mode,
|
|
210
|
-
toolkits: integration.toolkits,
|
|
211
|
-
tools: integration.allowedTools,
|
|
212
|
-
userId: integration.userId,
|
|
213
|
-
confirmExternalWrites: integration.confirmExternalWrites !== false,
|
|
214
|
-
})),
|
|
173
|
+
tools: (capsule.config.tools ?? []).map((tool) => tool.name),
|
|
215
174
|
channels: (capsule.config.channels ?? []).map((channel) => ({
|
|
216
175
|
name: channel.name,
|
|
217
176
|
type: channel.type,
|
|
@@ -264,16 +223,9 @@ export function buildInspectState(
|
|
|
264
223
|
target: "bun",
|
|
265
224
|
database: capsule.storagePath ? "sqlite" : "none",
|
|
266
225
|
},
|
|
267
|
-
hosted: {
|
|
268
|
-
environment: "hosted",
|
|
269
|
-
target: "cloudflare",
|
|
270
|
-
database: database.hosted.driver === "turso" ? "turso" : "none",
|
|
271
|
-
providerSupported: true,
|
|
272
|
-
},
|
|
273
226
|
aliases: ["ctx.db", "ctx.database", "ctx.storage.sql"],
|
|
274
227
|
},
|
|
275
228
|
secrets,
|
|
276
|
-
managedSecrets,
|
|
277
229
|
userSecrets,
|
|
278
230
|
skills,
|
|
279
231
|
warnings,
|
|
@@ -287,8 +239,8 @@ function toCapsulePath(root: string, path: string): string {
|
|
|
287
239
|
|
|
288
240
|
function inspectDatabase(capsule: LoadedAgentCapsule): AgentInspectDatabase {
|
|
289
241
|
const database = capsule.config.storage.database;
|
|
290
|
-
const schema = database?.driver === "
|
|
291
|
-
const migrations = database?.driver === "
|
|
242
|
+
const schema = database?.driver === "sqlite" && database.schema ? database.schema : null;
|
|
243
|
+
const migrations = database?.driver === "sqlite" ? database.migrations ?? "migrations" : null;
|
|
292
244
|
|
|
293
245
|
return {
|
|
294
246
|
local: {
|
|
@@ -297,12 +249,6 @@ function inspectDatabase(capsule: LoadedAgentCapsule): AgentInspectDatabase {
|
|
|
297
249
|
schema,
|
|
298
250
|
migrations,
|
|
299
251
|
},
|
|
300
|
-
hosted: {
|
|
301
|
-
driver: database?.driver === "turso" ? "turso" : "none",
|
|
302
|
-
provisioning: database?.driver === "turso" ? "agentkit-managed" : "none",
|
|
303
|
-
schema,
|
|
304
|
-
migrations,
|
|
305
|
-
},
|
|
306
252
|
};
|
|
307
253
|
}
|
|
308
254
|
|
|
@@ -333,6 +279,15 @@ function inspectWarnings(
|
|
|
333
279
|
): AgentInspectWarning[] {
|
|
334
280
|
const warnings: AgentInspectWarning[] = [];
|
|
335
281
|
|
|
282
|
+
for (const channel of capsule.config.channels ?? []) {
|
|
283
|
+
if (channel.type === "discord" && channel.mode === "bot") {
|
|
284
|
+
warnings.push({
|
|
285
|
+
code: "discord_gateway_bridge_required",
|
|
286
|
+
message: `Discord channel "${channel.name}" uses bot mode, but AgentKit does not connect to the Gateway. Inviting the bot alone cannot deliver messages. Use interactions or run your own Gateway bridge to forward authenticated events.`,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
336
291
|
if (skills.status === "missing" || skills.status === "outdated") {
|
|
337
292
|
warnings.push({
|
|
338
293
|
code: "local_skills_outdated",
|
|
@@ -355,18 +310,3 @@ function inspectWarnings(
|
|
|
355
310
|
|
|
356
311
|
return warnings;
|
|
357
312
|
}
|
|
358
|
-
|
|
359
|
-
function managedCloudSecrets(capsule: LoadedAgentCapsule): string[] {
|
|
360
|
-
const secrets = new Set<string>();
|
|
361
|
-
|
|
362
|
-
if (capsule.config.storage.database?.driver === "turso") {
|
|
363
|
-
secrets.add("TURSO_AUTH_TOKEN");
|
|
364
|
-
secrets.add("TURSO_DATABASE_URL");
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
for (const secret of managedComposioRequiredSecrets(capsule.config)) {
|
|
368
|
-
secrets.add(secret);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
return Array.from(secrets).sort();
|
|
372
|
-
}
|
|
@@ -5,8 +5,15 @@ import { AgentKitError } from "../errors";
|
|
|
5
5
|
|
|
6
6
|
export type EmbeddingProvider = {
|
|
7
7
|
embed(input: string): Promise<number[]>;
|
|
8
|
+
/** Embeds inputs in as few provider requests as possible; results keep input order. */
|
|
9
|
+
embedMany(inputs: string[]): Promise<number[][]>;
|
|
8
10
|
};
|
|
9
11
|
|
|
12
|
+
// Each valid input is at most 8,192 tokens; 36 inputs cannot exceed the
|
|
13
|
+
// 300,000-token request limit, even for long CSV rows or dense text.
|
|
14
|
+
// ponytail: conservative batches; add token-aware packing if throughput needs it.
|
|
15
|
+
const OPENAI_EMBEDDING_BATCH_SIZE = Math.floor(300_000 / 8_192);
|
|
16
|
+
|
|
10
17
|
export type CreateEmbeddingProviderOptions = {
|
|
11
18
|
config: ResolvedKnowledgeConfig["embedding"];
|
|
12
19
|
env?: Record<string, string | undefined>;
|
|
@@ -23,13 +30,26 @@ export function createEmbeddingProvider(options: CreateEmbeddingProviderOptions)
|
|
|
23
30
|
async embed(input) {
|
|
24
31
|
return fakeEmbedding(input, options.config.dimensions ?? 32);
|
|
25
32
|
},
|
|
33
|
+
async embedMany(inputs) {
|
|
34
|
+
return inputs.map((input) => fakeEmbedding(input, options.config.dimensions ?? 32));
|
|
35
|
+
},
|
|
26
36
|
};
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
if (options.config.provider === "openai") {
|
|
30
40
|
return {
|
|
31
41
|
async embed(input) {
|
|
32
|
-
|
|
42
|
+
const [embedding] = await openAiEmbeddings([input], options);
|
|
43
|
+
return embedding;
|
|
44
|
+
},
|
|
45
|
+
async embedMany(inputs) {
|
|
46
|
+
const embeddings: number[][] = [];
|
|
47
|
+
|
|
48
|
+
for (let start = 0; start < inputs.length; start += OPENAI_EMBEDDING_BATCH_SIZE) {
|
|
49
|
+
embeddings.push(...(await openAiEmbeddings(inputs.slice(start, start + OPENAI_EMBEDDING_BATCH_SIZE), options)));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return embeddings;
|
|
33
53
|
},
|
|
34
54
|
};
|
|
35
55
|
}
|
|
@@ -74,7 +94,7 @@ export function cosineSimilarity(left: number[], right: number[]): number {
|
|
|
74
94
|
return dot / (Math.sqrt(leftNorm) * Math.sqrt(rightNorm));
|
|
75
95
|
}
|
|
76
96
|
|
|
77
|
-
async function
|
|
97
|
+
async function openAiEmbeddings(inputs: string[], options: CreateEmbeddingProviderOptions): Promise<number[][]> {
|
|
78
98
|
const secretName = options.config.secret ?? "OPENAI_API_KEY";
|
|
79
99
|
const apiKey = options.env?.[secretName] ?? process.env[secretName];
|
|
80
100
|
|
|
@@ -94,7 +114,7 @@ async function openAiEmbedding(input: string, options: CreateEmbeddingProviderOp
|
|
|
94
114
|
},
|
|
95
115
|
body: JSON.stringify({
|
|
96
116
|
model: options.config.model ?? "text-embedding-3-small",
|
|
97
|
-
input,
|
|
117
|
+
input: inputs,
|
|
98
118
|
encoding_format: "float",
|
|
99
119
|
...(options.config.dimensions ? { dimensions: options.config.dimensions } : {}),
|
|
100
120
|
}),
|
|
@@ -109,13 +129,31 @@ async function openAiEmbedding(input: string, options: CreateEmbeddingProviderOp
|
|
|
109
129
|
}
|
|
110
130
|
|
|
111
131
|
const payload = await response.json();
|
|
112
|
-
const
|
|
132
|
+
const data: unknown[] = Array.isArray(payload?.data) ? payload.data : [];
|
|
133
|
+
const embeddings: number[][] = new Array(inputs.length);
|
|
134
|
+
|
|
135
|
+
for (const [position, item] of data.entries()) {
|
|
136
|
+
const record = item as { index?: unknown; embedding?: unknown };
|
|
137
|
+
const index = typeof record.index === "number" ? record.index : position;
|
|
138
|
+
const embedding = record.embedding;
|
|
139
|
+
|
|
140
|
+
if (
|
|
141
|
+
index >= 0 &&
|
|
142
|
+
index < inputs.length &&
|
|
143
|
+
Array.isArray(embedding) &&
|
|
144
|
+
embedding.every((value) => typeof value === "number")
|
|
145
|
+
) {
|
|
146
|
+
embeddings[index] = embedding;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
113
149
|
|
|
114
|
-
|
|
115
|
-
|
|
150
|
+
for (let index = 0; index < inputs.length; index += 1) {
|
|
151
|
+
if (!embeddings[index]) {
|
|
152
|
+
throw new AgentKitError("knowledge_embedding_failed", "OpenAI embedding response did not contain a numeric vector.");
|
|
153
|
+
}
|
|
116
154
|
}
|
|
117
155
|
|
|
118
|
-
return
|
|
156
|
+
return embeddings;
|
|
119
157
|
}
|
|
120
158
|
|
|
121
159
|
function normalizeVector(values: number[]): number[] {
|
|
@@ -151,7 +151,9 @@ async function ingestKnowledgeSources(
|
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
if (results.some((result) => result.status === "indexed")) {
|
|
155
|
+
await checkpointLocalKnowledgeDatabase(db);
|
|
156
|
+
}
|
|
155
157
|
} finally {
|
|
156
158
|
store.close();
|
|
157
159
|
}
|
|
@@ -269,7 +271,7 @@ async function ingestKnowledgeSource(
|
|
|
269
271
|
if (source.kind === "url") {
|
|
270
272
|
throw new AgentKitError(
|
|
271
273
|
"knowledge_source_unsupported",
|
|
272
|
-
"URL knowledge
|
|
274
|
+
"URL knowledge ingestion is not supported. Local Knowledge currently supports .md, .txt, and .csv files.",
|
|
273
275
|
);
|
|
274
276
|
}
|
|
275
277
|
|
|
@@ -304,13 +306,28 @@ async function ingestKnowledgeSource(
|
|
|
304
306
|
id: string;
|
|
305
307
|
}> = [];
|
|
306
308
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
+
const chunkHashes = chunks.map((chunk) => sha256(chunk.content));
|
|
310
|
+
const embeddingsByHash = embeddingProvider
|
|
311
|
+
? await loadReusableChunkEmbeddings(db, sourceId, resolvedKnowledge)
|
|
312
|
+
: new Map<string, number[]>();
|
|
313
|
+
|
|
314
|
+
if (embeddingProvider) {
|
|
315
|
+
const missingHashes = [...new Set(chunkHashes.filter((hash) => !embeddingsByHash.has(hash)))];
|
|
316
|
+
|
|
317
|
+
if (missingHashes.length > 0) {
|
|
318
|
+
const contentByHash = new Map(chunks.map((chunk, index) => [chunkHashes[index], chunk.content]));
|
|
319
|
+
const embeddings = await embeddingProvider.embedMany(missingHashes.map((hash) => contentByHash.get(hash)!));
|
|
320
|
+
missingHashes.forEach((hash, index) => embeddingsByHash.set(hash, embeddings[index]));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
for (const [index, chunk] of chunks.entries()) {
|
|
325
|
+
const contentSha256 = chunkHashes[index];
|
|
309
326
|
embeddedChunks.push({
|
|
310
327
|
chunk,
|
|
311
|
-
embedding,
|
|
312
|
-
contentSha256
|
|
313
|
-
id: stableId("kchk", `${sourceId}:${chunk.ordinal}:${
|
|
328
|
+
embedding: embeddingProvider ? embeddingsByHash.get(contentSha256) ?? null : null,
|
|
329
|
+
contentSha256,
|
|
330
|
+
id: stableId("kchk", `${sourceId}:${chunk.ordinal}:${contentSha256}`),
|
|
314
331
|
});
|
|
315
332
|
}
|
|
316
333
|
|
|
@@ -491,6 +508,52 @@ function capsuleRelativePath(root: string, absolutePath: string): string {
|
|
|
491
508
|
return relativePath.split("\\").join("/");
|
|
492
509
|
}
|
|
493
510
|
|
|
511
|
+
// Chunks whose text is unchanged keep their stored vector, so editing one part
|
|
512
|
+
// of a file only pays for embedding the chunks that actually changed.
|
|
513
|
+
async function loadReusableChunkEmbeddings(
|
|
514
|
+
db: DatabaseRunner,
|
|
515
|
+
sourceId: string,
|
|
516
|
+
knowledge: ReturnType<typeof resolveKnowledgeConfig>,
|
|
517
|
+
): Promise<Map<string, number[]>> {
|
|
518
|
+
const result = await db.query(
|
|
519
|
+
`
|
|
520
|
+
SELECT content_sha256, embedding_json
|
|
521
|
+
FROM agentkit_knowledge_chunks
|
|
522
|
+
WHERE source_id = ?
|
|
523
|
+
AND embedding_json IS NOT NULL
|
|
524
|
+
AND embedding_provider = ?
|
|
525
|
+
AND embedding_model = ?
|
|
526
|
+
AND (? IS NULL OR embedding_dimensions = ?)
|
|
527
|
+
`,
|
|
528
|
+
[
|
|
529
|
+
sourceId,
|
|
530
|
+
knowledge.embedding.provider,
|
|
531
|
+
knowledge.embedding.model,
|
|
532
|
+
knowledge.embedding.dimensions,
|
|
533
|
+
knowledge.embedding.dimensions,
|
|
534
|
+
],
|
|
535
|
+
);
|
|
536
|
+
const embeddings = new Map<string, number[]>();
|
|
537
|
+
|
|
538
|
+
for (const row of result.rows) {
|
|
539
|
+
if (typeof row.content_sha256 !== "string" || typeof row.embedding_json !== "string") {
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
try {
|
|
544
|
+
const embedding = JSON.parse(row.embedding_json);
|
|
545
|
+
|
|
546
|
+
if (Array.isArray(embedding) && embedding.every((value) => typeof value === "number")) {
|
|
547
|
+
embeddings.set(row.content_sha256, embedding);
|
|
548
|
+
}
|
|
549
|
+
} catch {
|
|
550
|
+
// A corrupt stored vector is simply re-embedded.
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return embeddings;
|
|
555
|
+
}
|
|
556
|
+
|
|
494
557
|
async function findExistingSource(db: DatabaseRunner, sourceKey: string): Promise<DatabaseRow | undefined> {
|
|
495
558
|
const result = await db.query(
|
|
496
559
|
"SELECT id, content_sha256, status FROM agentkit_knowledge_sources WHERE source_key = ?",
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import type { AgentTool } from "../../index";
|
|
2
2
|
import type { LoadedAgentCapsule } from "../config";
|
|
3
3
|
import { resolveKnowledgeConfig } from "./config";
|
|
4
|
-
import { searchKnowledge } from "./retrieve";
|
|
4
|
+
import { searchKnowledge, type KnowledgeSearchResult } from "./retrieve";
|
|
5
5
|
|
|
6
6
|
export const KNOWLEDGE_SEARCH_TOOL_NAME = "agentkit_search_knowledge";
|
|
7
7
|
|
|
8
|
+
// Only the actual built-in tool may expose retrieval facts to the model.
|
|
9
|
+
const knowledgeTools = new WeakSet<AgentTool>();
|
|
10
|
+
|
|
11
|
+
export function knowledgeModelText(tool: AgentTool, output: unknown): string | undefined {
|
|
12
|
+
if (!knowledgeTools.has(tool)) return undefined;
|
|
13
|
+
const { results } = output as { results: Array<Pick<KnowledgeSearchResult, "content" | "source">> };
|
|
14
|
+
return results.map(({ content, source }) => {
|
|
15
|
+
const reference = [source.path, source.title, source.section, source.locator].filter(Boolean).join(" > ");
|
|
16
|
+
return `${content}\nSource: ${reference}`;
|
|
17
|
+
}).join("\n\n") || "No relevant Knowledge results found.";
|
|
18
|
+
}
|
|
19
|
+
|
|
8
20
|
export function createKnowledgeSearchTool(capsule: LoadedAgentCapsule): AgentTool | null {
|
|
9
21
|
const knowledge = resolveKnowledgeConfig(capsule.config);
|
|
10
22
|
|
|
@@ -12,7 +24,7 @@ export function createKnowledgeSearchTool(capsule: LoadedAgentCapsule): AgentToo
|
|
|
12
24
|
return null;
|
|
13
25
|
}
|
|
14
26
|
|
|
15
|
-
|
|
27
|
+
const tool: AgentTool = {
|
|
16
28
|
name: KNOWLEDGE_SEARCH_TOOL_NAME,
|
|
17
29
|
description: "Search the Agent Capsule's configured Knowledge sources for business-specific facts.",
|
|
18
30
|
visibility: "internal",
|
|
@@ -61,4 +73,6 @@ export function createKnowledgeSearchTool(capsule: LoadedAgentCapsule): AgentToo
|
|
|
61
73
|
};
|
|
62
74
|
},
|
|
63
75
|
};
|
|
76
|
+
knowledgeTools.add(tool);
|
|
77
|
+
return tool;
|
|
64
78
|
}
|
|
@@ -2,7 +2,7 @@ import { access } from "node:fs/promises";
|
|
|
2
2
|
import { basename, dirname, extname, join } from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
|
|
5
|
-
import { createClient } from "@libsql/client";
|
|
5
|
+
import { createClient, type InStatement } from "@libsql/client";
|
|
6
6
|
|
|
7
7
|
import type { DatabaseRow } from "../../index";
|
|
8
8
|
|
|
@@ -48,32 +48,6 @@ export async function rebuildLocalKnowledgeVectorIndex(options: {
|
|
|
48
48
|
const dimensions = options.dimensions;
|
|
49
49
|
|
|
50
50
|
try {
|
|
51
|
-
await vectorClient.batch(
|
|
52
|
-
[
|
|
53
|
-
`DROP INDEX IF EXISTS ${KNOWLEDGE_VECTOR_INDEX}`,
|
|
54
|
-
`DROP INDEX IF EXISTS ${KNOWLEDGE_VECTOR_SOURCE_INDEX}`,
|
|
55
|
-
`DROP TABLE IF EXISTS ${KNOWLEDGE_VECTOR_TABLE}`,
|
|
56
|
-
`
|
|
57
|
-
CREATE TABLE ${KNOWLEDGE_VECTOR_TABLE} (
|
|
58
|
-
id INTEGER PRIMARY KEY,
|
|
59
|
-
chunk_id TEXT NOT NULL UNIQUE,
|
|
60
|
-
source_id TEXT NOT NULL,
|
|
61
|
-
source_key TEXT NOT NULL,
|
|
62
|
-
content TEXT NOT NULL,
|
|
63
|
-
title TEXT,
|
|
64
|
-
section TEXT,
|
|
65
|
-
locator TEXT,
|
|
66
|
-
embedding F32_BLOB(${dimensions}) NOT NULL,
|
|
67
|
-
embedding_hash TEXT NOT NULL,
|
|
68
|
-
created_at TEXT NOT NULL
|
|
69
|
-
)
|
|
70
|
-
`,
|
|
71
|
-
`CREATE INDEX ${KNOWLEDGE_VECTOR_SOURCE_INDEX} ON ${KNOWLEDGE_VECTOR_TABLE}(source_id)`,
|
|
72
|
-
`CREATE INDEX ${KNOWLEDGE_VECTOR_INDEX} ON ${KNOWLEDGE_VECTOR_TABLE}(libsql_vector_idx(embedding, 'metric=cosine'))`,
|
|
73
|
-
],
|
|
74
|
-
"write",
|
|
75
|
-
);
|
|
76
|
-
|
|
77
51
|
const rows = await mainClient.execute({
|
|
78
52
|
sql: `
|
|
79
53
|
SELECT
|
|
@@ -97,7 +71,31 @@ export async function rebuildLocalKnowledgeVectorIndex(options: {
|
|
|
97
71
|
args: [dimensions],
|
|
98
72
|
});
|
|
99
73
|
|
|
100
|
-
|
|
74
|
+
// Drop, recreate, and refill in one write batch so a failed rebuild never
|
|
75
|
+
// leaves a half-populated index behind, and inserts share one transaction.
|
|
76
|
+
const schemaStatements: InStatement[] = [
|
|
77
|
+
`DROP INDEX IF EXISTS ${KNOWLEDGE_VECTOR_INDEX}`,
|
|
78
|
+
`DROP INDEX IF EXISTS ${KNOWLEDGE_VECTOR_SOURCE_INDEX}`,
|
|
79
|
+
`DROP TABLE IF EXISTS ${KNOWLEDGE_VECTOR_TABLE}`,
|
|
80
|
+
`
|
|
81
|
+
CREATE TABLE ${KNOWLEDGE_VECTOR_TABLE} (
|
|
82
|
+
id INTEGER PRIMARY KEY,
|
|
83
|
+
chunk_id TEXT NOT NULL UNIQUE,
|
|
84
|
+
source_id TEXT NOT NULL,
|
|
85
|
+
source_key TEXT NOT NULL,
|
|
86
|
+
content TEXT NOT NULL,
|
|
87
|
+
title TEXT,
|
|
88
|
+
section TEXT,
|
|
89
|
+
locator TEXT,
|
|
90
|
+
embedding F32_BLOB(${dimensions}) NOT NULL,
|
|
91
|
+
embedding_hash TEXT NOT NULL,
|
|
92
|
+
created_at TEXT NOT NULL
|
|
93
|
+
)
|
|
94
|
+
`,
|
|
95
|
+
`CREATE INDEX ${KNOWLEDGE_VECTOR_SOURCE_INDEX} ON ${KNOWLEDGE_VECTOR_TABLE}(source_id)`,
|
|
96
|
+
`CREATE INDEX ${KNOWLEDGE_VECTOR_INDEX} ON ${KNOWLEDGE_VECTOR_TABLE}(libsql_vector_idx(embedding, 'metric=cosine'))`,
|
|
97
|
+
];
|
|
98
|
+
const inserts: InStatement[] = [];
|
|
101
99
|
|
|
102
100
|
for (const row of rows.rows) {
|
|
103
101
|
const chunkId = readString(row, "chunk_id");
|
|
@@ -112,7 +110,7 @@ export async function rebuildLocalKnowledgeVectorIndex(options: {
|
|
|
112
110
|
continue;
|
|
113
111
|
}
|
|
114
112
|
|
|
115
|
-
|
|
113
|
+
inserts.push({
|
|
116
114
|
sql: `
|
|
117
115
|
INSERT INTO ${KNOWLEDGE_VECTOR_TABLE} (
|
|
118
116
|
chunk_id,
|
|
@@ -141,9 +139,11 @@ export async function rebuildLocalKnowledgeVectorIndex(options: {
|
|
|
141
139
|
createdAt,
|
|
142
140
|
],
|
|
143
141
|
});
|
|
144
|
-
chunksIndexed += 1;
|
|
145
142
|
}
|
|
146
143
|
|
|
144
|
+
await vectorClient.batch([...schemaStatements, ...inserts], "write");
|
|
145
|
+
const chunksIndexed = inserts.length;
|
|
146
|
+
|
|
147
147
|
await vectorClient.execute("PRAGMA wal_checkpoint(TRUNCATE)").catch(() => undefined);
|
|
148
148
|
|
|
149
149
|
return {
|
|
@@ -181,7 +181,7 @@ export async function searchLocalKnowledgeVectorRows(options: {
|
|
|
181
181
|
const result = await client.execute({
|
|
182
182
|
sql: `
|
|
183
183
|
SELECT
|
|
184
|
-
c.
|
|
184
|
+
c.chunk_id AS chunk_id,
|
|
185
185
|
c.source_id AS source_id,
|
|
186
186
|
c.content AS content,
|
|
187
187
|
c.title AS title,
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { loadAgentCapsule } from "./config";
|
|
2
2
|
import { openPreparedCapsuleStore } from "./database";
|
|
3
|
-
import { loadCapsuleEnv } from "./env";
|
|
3
|
+
import { collectSecretValues, loadCapsuleEnv } from "./env";
|
|
4
4
|
import { isAgentKitError } from "./errors";
|
|
5
|
-
import { createManagedComposioTools } from "./integrations/composio";
|
|
6
5
|
import { buildRuntimeClockContext } from "./prompt-context";
|
|
7
6
|
import { createToolRuntime, type ToolCallResult } from "./tools";
|
|
8
7
|
|
|
@@ -16,6 +15,7 @@ export type RunToolOptions = {
|
|
|
16
15
|
export async function runToolFromCwd(cwd: string, options: RunToolOptions): Promise<ToolCallResult> {
|
|
17
16
|
const capsule = await loadAgentCapsule(cwd);
|
|
18
17
|
const env = await loadCapsuleEnv(capsule.root);
|
|
18
|
+
const secretValues = collectSecretValues(capsule.config, env);
|
|
19
19
|
const { store } = await openPreparedCapsuleStore(capsule);
|
|
20
20
|
const conversation = store.createConversation({
|
|
21
21
|
agentName: capsule.config.name,
|
|
@@ -27,11 +27,12 @@ export async function runToolFromCwd(cwd: string, options: RunToolOptions): Prom
|
|
|
27
27
|
model: capsule.config.provider.model,
|
|
28
28
|
});
|
|
29
29
|
const toolRuntime = createToolRuntime({
|
|
30
|
-
tools:
|
|
30
|
+
tools: capsule.config.tools ?? [],
|
|
31
31
|
store,
|
|
32
32
|
runId: run.id,
|
|
33
33
|
conversationId: conversation.id,
|
|
34
34
|
env,
|
|
35
|
+
secretValues,
|
|
35
36
|
signal: options.signal,
|
|
36
37
|
clock: buildRuntimeClockContext({
|
|
37
38
|
timeZone: capsule.config.timeZone,
|
|
@@ -64,6 +65,7 @@ export async function runToolFromCwd(cwd: string, options: RunToolOptions): Prom
|
|
|
64
65
|
status: "failed",
|
|
65
66
|
errorCode: isAgentKitError(error) ? error.code : "runtime_error",
|
|
66
67
|
errorMessage: error instanceof Error ? error.message : String(error),
|
|
68
|
+
secretValues,
|
|
67
69
|
});
|
|
68
70
|
|
|
69
71
|
throw error;
|
package/src/runtime/tools.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { AgentTool, JsonSchema, ToolContext, ToolRenderResult, ToolRuntimeContext } from "../index";
|
|
2
|
-
import type
|
|
2
|
+
import { redactSecrets, type SqliteAgentKitStore } from "../storage/sqlite";
|
|
3
3
|
import { createLocalDatabaseRunner } from "./database";
|
|
4
|
+
import { collectSecretValues } from "./env";
|
|
5
|
+
import { knowledgeModelText } from "./knowledge/tool";
|
|
4
6
|
import { AgentKitError } from "./errors";
|
|
5
7
|
import type { RuntimeClockContext } from "./prompt-context";
|
|
6
8
|
|
|
@@ -16,6 +18,8 @@ export type ToolCallResult = {
|
|
|
16
18
|
input: unknown;
|
|
17
19
|
output: unknown;
|
|
18
20
|
rendered?: ToolRenderResult;
|
|
21
|
+
// Redacted facts from built-in Knowledge only; never a user-facing renderer.
|
|
22
|
+
modelText?: string;
|
|
19
23
|
};
|
|
20
24
|
|
|
21
25
|
export type ToolRuntime = {
|
|
@@ -29,6 +33,7 @@ export type ToolRuntimeOptions = {
|
|
|
29
33
|
runId: string;
|
|
30
34
|
conversationId: string;
|
|
31
35
|
env?: Record<string, string | undefined>;
|
|
36
|
+
secretValues?: string[];
|
|
32
37
|
signal?: AbortSignal;
|
|
33
38
|
defaultTimeoutMs?: number;
|
|
34
39
|
runtime: ToolRuntimeContext;
|
|
@@ -63,7 +68,7 @@ async function executeToolCall(
|
|
|
63
68
|
): Promise<ToolCallResult> {
|
|
64
69
|
let input: unknown = request.input ?? {};
|
|
65
70
|
const tool = toolsByName.get(request.name);
|
|
66
|
-
const preliminarySecretValues =
|
|
71
|
+
const preliminarySecretValues = collectSecretValues(tool, options.env ?? process.env);
|
|
67
72
|
const toolCall = options.store.createToolCall({
|
|
68
73
|
runId: options.runId,
|
|
69
74
|
conversationId: options.conversationId,
|
|
@@ -112,6 +117,7 @@ async function executeToolCall(
|
|
|
112
117
|
secretValues,
|
|
113
118
|
});
|
|
114
119
|
|
|
120
|
+
const modelText = knowledgeModelText(tool, output);
|
|
115
121
|
return {
|
|
116
122
|
id: toolCall.id,
|
|
117
123
|
name: tool.name,
|
|
@@ -119,9 +125,10 @@ async function executeToolCall(
|
|
|
119
125
|
input,
|
|
120
126
|
output,
|
|
121
127
|
...(rendered ? { rendered } : {}),
|
|
128
|
+
...(modelText !== undefined ? { modelText: redactSecrets(modelText, options.secretValues ?? secretValues) } : {}),
|
|
122
129
|
};
|
|
123
130
|
} catch (error) {
|
|
124
|
-
const secretValues =
|
|
131
|
+
const secretValues = collectSecretValues(tool, options.env ?? process.env);
|
|
125
132
|
|
|
126
133
|
options.store.completeToolCall({
|
|
127
134
|
toolCallId: toolCall.id,
|
|
@@ -287,17 +294,6 @@ function resolveToolSecrets(
|
|
|
287
294
|
return { secrets, secretValues: Object.values(secrets) };
|
|
288
295
|
}
|
|
289
296
|
|
|
290
|
-
function collectConfiguredSecretValues(tool: AgentTool | undefined, env: Record<string, string | undefined>): string[] {
|
|
291
|
-
if (!tool) {
|
|
292
|
-
return [];
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return (tool.secrets ?? []).flatMap((secretName) => {
|
|
296
|
-
const value = env[secretName];
|
|
297
|
-
return value ? [value] : [];
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
|
|
301
297
|
async function runToolWithTimeout(
|
|
302
298
|
tool: AgentTool,
|
|
303
299
|
input: unknown,
|