@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/config.ts
CHANGED
|
@@ -17,17 +17,15 @@ import type {
|
|
|
17
17
|
TelegramAllowedUpdate,
|
|
18
18
|
AgentKnowledgeEmbeddingProvider,
|
|
19
19
|
ChannelBufferMode,
|
|
20
|
-
AgentIntegration,
|
|
21
20
|
ChannelAudioMode,
|
|
22
21
|
TranscriptionProviderName,
|
|
23
22
|
ChannelReplyRouteConfig,
|
|
24
23
|
} from "../index";
|
|
25
|
-
import { MANAGED_COMPOSIO_TOOL_NAME } from "./integrations/composio";
|
|
26
24
|
import { AgentKitError } from "./errors";
|
|
27
25
|
import { isValidTimeZone } from "./prompt-context";
|
|
28
26
|
|
|
29
27
|
const CONFIG_FILE = "agentkit.config.ts";
|
|
30
|
-
const AGENT_RUNTIMES = new Set<AgentRuntime>(["local"
|
|
28
|
+
const AGENT_RUNTIMES = new Set<AgentRuntime>(["local"]);
|
|
31
29
|
const PROVIDERS = new Set<AgentProviderName>(["test", "openai", "openai-codex", "anthropic", "openrouter", "opencode", "opencode-go", "custom"]);
|
|
32
30
|
const ACCESS_MODES = new Set<AccessMode>(["private", "token", "public"]);
|
|
33
31
|
const TOOL_VISIBILITIES = new Set<ToolVisibility>(["user", "internal"]);
|
|
@@ -41,9 +39,6 @@ const KNOWLEDGE_EMBEDDING_PROVIDERS = new Set<AgentKnowledgeEmbeddingProvider>([
|
|
|
41
39
|
const TRANSCRIPTION_PROVIDERS = new Set<TranscriptionProviderName>(["test", "openai", "groq"]);
|
|
42
40
|
const CHANNEL_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
43
41
|
const CHANNEL_REPLY_PATH_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
44
|
-
const INTEGRATION_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
45
|
-
const COMPOSIO_TOOLKIT_PATTERN = /^[a-z][a-z0-9_-]*$/;
|
|
46
|
-
const COMPOSIO_TOOL_SLUG_PATTERN = /^[A-Z][A-Z0-9_]*$/;
|
|
47
42
|
const SECRET_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/;
|
|
48
43
|
const HTTP_HEADER_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9-]*$/;
|
|
49
44
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
@@ -123,9 +118,32 @@ function resolveLocalStoragePath(root: string, config: AgentConfig): string | un
|
|
|
123
118
|
return undefined;
|
|
124
119
|
}
|
|
125
120
|
|
|
121
|
+
// Every import of the config goes through a uniquely named temp module, and
|
|
122
|
+
// Node never evicts imported modules. Re-importing on each request (the dev and
|
|
123
|
+
// local servers load the capsule per request) grows memory without bound,
|
|
124
|
+
// so reuse the import while the config source is unchanged. Edits still reload.
|
|
125
|
+
const configModuleCache = new Map<string, { source: string; value: Promise<unknown> }>();
|
|
126
|
+
|
|
126
127
|
async function loadConfigModule(configPath: string): Promise<unknown> {
|
|
127
128
|
const source = await readFile(configPath, "utf8");
|
|
129
|
+
const cached = configModuleCache.get(configPath);
|
|
130
|
+
|
|
131
|
+
if (cached?.source === source) {
|
|
132
|
+
return cached.value;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const value = importConfigModule(configPath, source);
|
|
136
|
+
configModuleCache.set(configPath, { source, value });
|
|
137
|
+
value.catch(() => {
|
|
138
|
+
if (configModuleCache.get(configPath)?.value === value) {
|
|
139
|
+
configModuleCache.delete(configPath);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
128
142
|
|
|
143
|
+
return value;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function importConfigModule(configPath: string, source: string): Promise<unknown> {
|
|
129
147
|
try {
|
|
130
148
|
return await importConfigSource(configPath, source);
|
|
131
149
|
} catch (error) {
|
|
@@ -205,7 +223,7 @@ function validateAgentConfig(value: unknown): asserts value is AgentConfig {
|
|
|
205
223
|
expectString(value.name, "name");
|
|
206
224
|
|
|
207
225
|
if (!AGENT_RUNTIMES.has(value.runtime as AgentRuntime)) {
|
|
208
|
-
throw invalidConfig('runtime must be "local"
|
|
226
|
+
throw invalidConfig('runtime must be "local". Hosted runtimes are no longer supported.');
|
|
209
227
|
}
|
|
210
228
|
|
|
211
229
|
if (!isRecord(value.provider)) {
|
|
@@ -244,9 +262,11 @@ function validateAgentConfig(value: unknown): asserts value is AgentConfig {
|
|
|
244
262
|
}
|
|
245
263
|
}
|
|
246
264
|
|
|
247
|
-
|
|
265
|
+
if (value.integrations !== undefined) {
|
|
266
|
+
throw invalidConfig("Managed integrations are no longer supported. Connect external services through defineTool.");
|
|
267
|
+
}
|
|
248
268
|
validateTranscriptionConfig(value.transcription);
|
|
249
|
-
validateChannels(value.channels,
|
|
269
|
+
validateChannels(value.channels, Boolean(value.transcription));
|
|
250
270
|
validateKnowledgeConfig(value.knowledge);
|
|
251
271
|
|
|
252
272
|
if (!isRecord(value.access) || !ACCESS_MODES.has(value.access.mode as AccessMode)) {
|
|
@@ -273,133 +293,6 @@ function validateAgentConfig(value: unknown): asserts value is AgentConfig {
|
|
|
273
293
|
validateFilesConfig(value.storage.files);
|
|
274
294
|
}
|
|
275
295
|
|
|
276
|
-
function validateIntegrations(
|
|
277
|
-
value: unknown,
|
|
278
|
-
runtime: AgentRuntime,
|
|
279
|
-
tools: AgentConfig["tools"] = [],
|
|
280
|
-
): void {
|
|
281
|
-
if (value === undefined) {
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (!Array.isArray(value)) {
|
|
286
|
-
throw invalidConfig("integrations must be an array when provided.");
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (value.length > 0 && runtime !== "edge") {
|
|
290
|
-
throw invalidConfig('managed integrations require runtime: "edge". Use BYO defineTool wrappers for local-only integrations.');
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
let composioCount = 0;
|
|
294
|
-
|
|
295
|
-
for (const [index, integration] of value.entries()) {
|
|
296
|
-
if (!isRecord(integration)) {
|
|
297
|
-
throw invalidConfig(`integrations[${index}] must be an object.`);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
if (integration.provider !== "composio" || integration.type !== "composio" || integration.mode !== "managed") {
|
|
301
|
-
throw invalidConfig(`integrations[${index}] must be composioManaged({...}) or { type: "composio", provider: "composio", mode: "managed", ... }.`);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
composioCount += 1;
|
|
305
|
-
validateComposioManagedIntegration(integration as unknown as AgentIntegration, index);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
if (composioCount > 1) {
|
|
309
|
-
throw invalidConfig("Only one managed Composio integration is supported per agent.");
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const hasExplicitComposioTools = value.some(
|
|
313
|
-
(integration) => isRecord(integration) && integration.provider === "composio" && isRecord(integration.tools),
|
|
314
|
-
);
|
|
315
|
-
|
|
316
|
-
if (hasExplicitComposioTools && tools.some((tool) => isRecord(tool) && tool.name === MANAGED_COMPOSIO_TOOL_NAME)) {
|
|
317
|
-
throw invalidConfig(`tools must not declare reserved managed Composio tool name "${MANAGED_COMPOSIO_TOOL_NAME}".`);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function validateComposioManagedIntegration(value: AgentIntegration, index: number): void {
|
|
322
|
-
expectString(value.name, `integrations[${index}].name`);
|
|
323
|
-
|
|
324
|
-
if (!INTEGRATION_NAME_PATTERN.test(value.name)) {
|
|
325
|
-
throw invalidConfig(`integrations[${index}].name must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens.`);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
if (!Array.isArray(value.toolkits) || value.toolkits.length === 0) {
|
|
329
|
-
throw invalidConfig(`integrations[${index}].toolkits must include at least one Composio toolkit slug.`);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
const toolkits = new Set<string>();
|
|
333
|
-
|
|
334
|
-
for (const [toolkitIndex, toolkit] of value.toolkits.entries()) {
|
|
335
|
-
expectString(toolkit, `integrations[${index}].toolkits[${toolkitIndex}]`);
|
|
336
|
-
|
|
337
|
-
if (!COMPOSIO_TOOLKIT_PATTERN.test(toolkit)) {
|
|
338
|
-
throw invalidConfig(`integrations[${index}].toolkits[${toolkitIndex}] must be a lowercase Composio toolkit slug.`);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (toolkits.has(toolkit)) {
|
|
342
|
-
throw invalidConfig(`integrations[${index}].toolkits must not contain duplicates.`);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
toolkits.add(toolkit);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
if (value.tools !== undefined) {
|
|
349
|
-
if (!isRecord(value.tools)) {
|
|
350
|
-
throw invalidConfig(`integrations[${index}].tools must be an object when provided.`);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
for (const [toolkit, tools] of Object.entries(value.tools)) {
|
|
354
|
-
if (!toolkits.has(toolkit)) {
|
|
355
|
-
throw invalidConfig(`integrations[${index}].tools.${toolkit} must reference a toolkit listed in integrations[${index}].toolkits.`);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
if (!Array.isArray(tools) || tools.length === 0) {
|
|
359
|
-
throw invalidConfig(`integrations[${index}].tools.${toolkit} must include at least one Composio action slug.`);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const seenTools = new Set<string>();
|
|
363
|
-
|
|
364
|
-
for (const [toolIndex, tool] of tools.entries()) {
|
|
365
|
-
expectString(tool, `integrations[${index}].tools.${toolkit}[${toolIndex}]`);
|
|
366
|
-
|
|
367
|
-
if (!COMPOSIO_TOOL_SLUG_PATTERN.test(tool)) {
|
|
368
|
-
throw invalidConfig(`integrations[${index}].tools.${toolkit}[${toolIndex}] must be an uppercase Composio action slug.`);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
if (seenTools.has(tool)) {
|
|
372
|
-
throw invalidConfig(`integrations[${index}].tools.${toolkit} must not contain duplicates.`);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
seenTools.add(tool);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
if (value.tags !== undefined) {
|
|
381
|
-
validateIntegrationTags(value.tags, `integrations[${index}].tags`);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
if (value.confirmExternalWrites !== undefined && typeof value.confirmExternalWrites !== "boolean") {
|
|
385
|
-
throw invalidConfig(`integrations[${index}].confirmExternalWrites must be a boolean when provided.`);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
function validateIntegrationTags(value: unknown, label: string): void {
|
|
390
|
-
if (!isRecord(value)) {
|
|
391
|
-
throw invalidConfig(`${label} must be an object when provided.`);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
if (value.enable !== undefined) {
|
|
395
|
-
expectStringArray(value.enable, `${label}.enable`);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
if (value.disable !== undefined) {
|
|
399
|
-
expectStringArray(value.disable, `${label}.disable`);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
296
|
function validateTranscriptionConfig(value: unknown): void {
|
|
404
297
|
if (value === undefined) {
|
|
405
298
|
return;
|
|
@@ -568,7 +461,7 @@ function validateKnowledgeRetrieval(value: unknown): void {
|
|
|
568
461
|
}
|
|
569
462
|
}
|
|
570
463
|
|
|
571
|
-
function validateChannels(value: unknown,
|
|
464
|
+
function validateChannels(value: unknown, hasTranscriptionConfig: boolean): void {
|
|
572
465
|
if (value === undefined) {
|
|
573
466
|
return;
|
|
574
467
|
}
|
|
@@ -577,10 +470,6 @@ function validateChannels(value: unknown, runtime: AgentRuntime, hasTranscriptio
|
|
|
577
470
|
throw invalidConfig("channels must be an array when provided.");
|
|
578
471
|
}
|
|
579
472
|
|
|
580
|
-
if (value.length > 0 && runtime !== "edge") {
|
|
581
|
-
throw invalidConfig('channels require runtime: "edge".');
|
|
582
|
-
}
|
|
583
|
-
|
|
584
473
|
const names = new Set<string>();
|
|
585
474
|
|
|
586
475
|
for (const [index, channel] of value.entries()) {
|
|
@@ -1022,8 +911,8 @@ function validateDatabaseConfig(value: unknown): void {
|
|
|
1022
911
|
throw invalidConfig("storage.database must be an object when provided.");
|
|
1023
912
|
}
|
|
1024
913
|
|
|
1025
|
-
if (value.driver !== "none" && value.driver !== "
|
|
1026
|
-
throw invalidConfig('storage.database.driver must be "none" or "
|
|
914
|
+
if (value.driver !== "none" && value.driver !== "sqlite") {
|
|
915
|
+
throw invalidConfig('storage.database.driver must be "none" or "sqlite".');
|
|
1027
916
|
}
|
|
1028
917
|
|
|
1029
918
|
if (value.schema !== undefined && typeof value.schema !== "string") {
|
|
@@ -1044,12 +933,12 @@ function validateFilesConfig(value: unknown): void {
|
|
|
1044
933
|
throw invalidConfig("storage.files must be an object when provided.");
|
|
1045
934
|
}
|
|
1046
935
|
|
|
1047
|
-
if (value.driver !== "none" && value.driver !== "
|
|
1048
|
-
throw invalidConfig('storage.files.driver must be "none" or "
|
|
936
|
+
if (value.driver !== "none" && value.driver !== "local") {
|
|
937
|
+
throw invalidConfig('storage.files.driver must be "none" or "local".');
|
|
1049
938
|
}
|
|
1050
939
|
|
|
1051
|
-
if (value.bucket !== undefined
|
|
1052
|
-
throw invalidConfig("storage.files.bucket
|
|
940
|
+
if (value.bucket !== undefined) {
|
|
941
|
+
throw invalidConfig("storage.files.bucket is no longer supported. Files are stored locally.");
|
|
1053
942
|
}
|
|
1054
943
|
|
|
1055
944
|
if (value.prefix !== undefined && typeof value.prefix !== "string") {
|
package/src/runtime/database.ts
CHANGED
|
@@ -47,7 +47,7 @@ export async function applyCapsuleDatabaseMigrations(
|
|
|
47
47
|
): Promise<LocalDatabasePrepareResult["migrations"]> {
|
|
48
48
|
const database = capsule.config.storage.database;
|
|
49
49
|
|
|
50
|
-
if (!database || database.driver !== "
|
|
50
|
+
if (!database || database.driver !== "sqlite") {
|
|
51
51
|
return {
|
|
52
52
|
configured: false,
|
|
53
53
|
path: null,
|
|
@@ -125,7 +125,7 @@ export async function applyCapsuleDatabaseSchema(
|
|
|
125
125
|
): Promise<LocalDatabasePrepareResult["schema"]> {
|
|
126
126
|
const database = capsule.config.storage.database;
|
|
127
127
|
|
|
128
|
-
if (!database || database.driver !== "
|
|
128
|
+
if (!database || database.driver !== "sqlite" || !database.schema) {
|
|
129
129
|
return {
|
|
130
130
|
configured: false,
|
|
131
131
|
applied: false,
|
|
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { createServer as createHttpServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
|
|
4
4
|
import { createServer as createNetServer } from "node:net";
|
|
5
|
-
import { dirname,
|
|
5
|
+
import { dirname, relative, resolve } from "node:path";
|
|
6
6
|
import { Readable } from "node:stream";
|
|
7
7
|
|
|
8
8
|
import { openCapsuleStore, type ConversationRecord } from "../storage/sqlite";
|
|
@@ -423,6 +423,7 @@ async function authorizeDevRoute(
|
|
|
423
423
|
|
|
424
424
|
const env = await loadCapsuleEnv(capsule.root);
|
|
425
425
|
|
|
426
|
+
// Preserve the legacy deploy_not_ready code for clients handling missing local credentials.
|
|
426
427
|
if (kind === "admin") {
|
|
427
428
|
return authorizeBearerSecret(request, body, env.AGENTKIT_PRIVATE_TOKEN, {
|
|
428
429
|
missingCode: "deploy_not_ready",
|
|
@@ -438,15 +439,15 @@ async function authorizeDevRoute(
|
|
|
438
439
|
if (capsule.config.access.mode === "private") {
|
|
439
440
|
return authorizeBearerSecret(request, body, env.AGENTKIT_PRIVATE_TOKEN, {
|
|
440
441
|
missingCode: "deploy_not_ready",
|
|
441
|
-
missingMessage: "Private
|
|
442
|
-
unauthorizedMessage: "Missing or invalid
|
|
442
|
+
missingMessage: "Private access requires AGENTKIT_PRIVATE_TOKEN to be set.",
|
|
443
|
+
unauthorizedMessage: "Missing or invalid access token.",
|
|
443
444
|
});
|
|
444
445
|
}
|
|
445
446
|
|
|
446
447
|
return authorizeBearerSecret(request, body, env.AGENTKIT_ACCESS_TOKEN, {
|
|
447
448
|
missingCode: "deploy_not_ready",
|
|
448
|
-
missingMessage: "Token
|
|
449
|
-
unauthorizedMessage: "Missing or invalid
|
|
449
|
+
missingMessage: "Token access requires AGENTKIT_ACCESS_TOKEN to be set.",
|
|
450
|
+
unauthorizedMessage: "Missing or invalid access token.",
|
|
450
451
|
});
|
|
451
452
|
}
|
|
452
453
|
|
|
@@ -492,7 +493,6 @@ function readRequestAccessToken(request: Request, body: unknown): string {
|
|
|
492
493
|
|
|
493
494
|
const header =
|
|
494
495
|
request.headers.get("x-agentkit-access-token") ??
|
|
495
|
-
request.headers.get("x-agentkit-deploy-access-token") ??
|
|
496
496
|
"";
|
|
497
497
|
const token = header.startsWith("Bearer ") ? header.slice("Bearer ".length) : header;
|
|
498
498
|
if (token.trim()) {
|
|
@@ -900,7 +900,7 @@ async function flushDevChannelBuffer(
|
|
|
900
900
|
},
|
|
901
901
|
});
|
|
902
902
|
} catch {
|
|
903
|
-
// Local channel buffering is best-effort;
|
|
903
|
+
// Local channel buffering is best-effort; the buffer is not a durable delivery queue.
|
|
904
904
|
}
|
|
905
905
|
}
|
|
906
906
|
|
|
@@ -911,7 +911,7 @@ function unrefTimer(timer: ReturnType<typeof setTimeout>): void {
|
|
|
911
911
|
}
|
|
912
912
|
|
|
913
913
|
async function handleLocalFile(capsule: LoadedAgentCapsule, request: Request, rawKey: string): Promise<Response> {
|
|
914
|
-
if (capsule.config.storage.files?.driver !== "
|
|
914
|
+
if (capsule.config.storage.files?.driver !== "local") {
|
|
915
915
|
throw new AgentKitError("files_not_configured", "File storage is not configured for this Agent Capsule.");
|
|
916
916
|
}
|
|
917
917
|
|
package/src/runtime/env.ts
CHANGED
|
@@ -3,6 +3,17 @@ import { join } from "node:path";
|
|
|
3
3
|
|
|
4
4
|
import { AgentKitError } from "./errors";
|
|
5
5
|
|
|
6
|
+
export function collectSecretValues(
|
|
7
|
+
scope: { secrets?: string[]; tools?: Array<{ secrets?: string[] }> } | undefined,
|
|
8
|
+
env: Record<string, string | undefined>,
|
|
9
|
+
): string[] {
|
|
10
|
+
const names = [...(scope?.secrets ?? []), ...(scope?.tools ?? []).flatMap((tool) => tool.secrets ?? [])];
|
|
11
|
+
return names.flatMap((name) => {
|
|
12
|
+
const value = env[name];
|
|
13
|
+
return value ? [value] : [];
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
6
17
|
export async function loadCapsuleEnv(
|
|
7
18
|
root: string,
|
|
8
19
|
baseEnv: Record<string, string | undefined> = process.env,
|
package/src/runtime/improve.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { AgentKitError, isAgentKitError } from "./errors";
|
|
|
8
8
|
import { runAgentMessageFromCwd } from "./chat";
|
|
9
9
|
import { getConversationTraceFromCwd } from "./traces";
|
|
10
10
|
|
|
11
|
-
export type ImproveSource = "local"
|
|
11
|
+
export type ImproveSource = "local";
|
|
12
12
|
|
|
13
13
|
export type ImproveTraceMessage = {
|
|
14
14
|
role: "user" | "assistant" | "system" | "tool";
|
|
@@ -35,8 +35,6 @@ export type ImproveBundle = {
|
|
|
35
35
|
created_at: string;
|
|
36
36
|
source: ImproveSource;
|
|
37
37
|
since?: string;
|
|
38
|
-
deploy_id?: string;
|
|
39
|
-
evidence?: unknown;
|
|
40
38
|
traces: ImproveTracePacket[];
|
|
41
39
|
};
|
|
42
40
|
|
|
@@ -71,16 +69,6 @@ export type ImproveReplayResult = {
|
|
|
71
69
|
}>;
|
|
72
70
|
};
|
|
73
71
|
|
|
74
|
-
export type DeployImproveCollector = {
|
|
75
|
-
exportEvidence?(input: {
|
|
76
|
-
deployId: string;
|
|
77
|
-
since?: string;
|
|
78
|
-
conversationId?: string;
|
|
79
|
-
}): Promise<unknown | null>;
|
|
80
|
-
listConversations(): Promise<unknown>;
|
|
81
|
-
getTrace(conversationId: string): Promise<unknown>;
|
|
82
|
-
};
|
|
83
|
-
|
|
84
72
|
export async function collectLocalImproveBundle(
|
|
85
73
|
cwd: string,
|
|
86
74
|
options: {
|
|
@@ -112,69 +100,6 @@ export async function collectLocalImproveBundle(
|
|
|
112
100
|
}, options.out);
|
|
113
101
|
}
|
|
114
102
|
|
|
115
|
-
export async function collectDeployImproveBundle(
|
|
116
|
-
cwd: string,
|
|
117
|
-
options: {
|
|
118
|
-
collector: DeployImproveCollector;
|
|
119
|
-
deployId: string;
|
|
120
|
-
since?: string;
|
|
121
|
-
conversationId?: string;
|
|
122
|
-
out?: string;
|
|
123
|
-
},
|
|
124
|
-
): Promise<ImproveCollectResult> {
|
|
125
|
-
const root = await findAgentCapsuleRoot(cwd);
|
|
126
|
-
|
|
127
|
-
if (options.collector.exportEvidence) {
|
|
128
|
-
const exported = await options.collector.exportEvidence({
|
|
129
|
-
deployId: options.deployId,
|
|
130
|
-
...(options.since ? { since: options.since } : {}),
|
|
131
|
-
...(options.conversationId ? { conversationId: options.conversationId } : {}),
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
if (exported) {
|
|
135
|
-
const normalized = normalizeDeployEvidenceExport(exported);
|
|
136
|
-
const traces = await tracePacketsFromDeployEvidenceExport({
|
|
137
|
-
collector: options.collector,
|
|
138
|
-
deployId: options.deployId,
|
|
139
|
-
evidence: normalized,
|
|
140
|
-
source: "deploy",
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
return writeImproveBundle(root, {
|
|
144
|
-
schema_version: 1,
|
|
145
|
-
created_at: normalized.created_at ?? new Date().toISOString(),
|
|
146
|
-
source: "deploy",
|
|
147
|
-
deploy_id: options.deployId,
|
|
148
|
-
...(options.since ? { since: options.since } : {}),
|
|
149
|
-
evidence: redactEvidenceValue(normalized.raw),
|
|
150
|
-
traces,
|
|
151
|
-
}, options.out);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const sinceDate = resolveSinceDate(options.since);
|
|
156
|
-
const summaries = options.conversationId
|
|
157
|
-
? [{ id: options.conversationId }]
|
|
158
|
-
: normalizeConversationSummaries(await options.collector.listConversations()).filter((conversation) =>
|
|
159
|
-
sinceDate && conversation.updated_at ? dateIsOnOrAfter(conversation.updated_at, sinceDate) : true,
|
|
160
|
-
);
|
|
161
|
-
const traces: ImproveTracePacket[] = [];
|
|
162
|
-
|
|
163
|
-
for (const summary of summaries) {
|
|
164
|
-
const trace = await options.collector.getTrace(summary.id);
|
|
165
|
-
traces.push(tracePacketFromPayload("deploy", summary.id, trace));
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return writeImproveBundle(root, {
|
|
169
|
-
schema_version: 1,
|
|
170
|
-
created_at: new Date().toISOString(),
|
|
171
|
-
source: "deploy",
|
|
172
|
-
deploy_id: options.deployId,
|
|
173
|
-
...(options.since ? { since: options.since } : {}),
|
|
174
|
-
traces,
|
|
175
|
-
}, options.out);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
103
|
export async function generateImproveEvals(
|
|
179
104
|
cwd: string,
|
|
180
105
|
options: {
|
|
@@ -319,7 +244,6 @@ async function writeImproveBundle(root: string, bundle: ImproveBundle, out?: str
|
|
|
319
244
|
{
|
|
320
245
|
created_at: bundle.created_at,
|
|
321
246
|
source: bundle.source,
|
|
322
|
-
deploy_id: bundle.deploy_id ?? null,
|
|
323
247
|
trace_count: bundle.traces.length,
|
|
324
248
|
conversations: bundle.traces.map((trace) => ({
|
|
325
249
|
trace_id: trace.id,
|
|
@@ -329,7 +253,6 @@ async function writeImproveBundle(root: string, bundle: ImproveBundle, out?: str
|
|
|
329
253
|
messages: trace.trace.messages.length,
|
|
330
254
|
runs: Array.isArray(trace.trace.runs) ? trace.trace.runs.length : 0,
|
|
331
255
|
})),
|
|
332
|
-
evidence: summarizeImproveEvidence(bundle.evidence),
|
|
333
256
|
},
|
|
334
257
|
null,
|
|
335
258
|
2,
|
|
@@ -350,160 +273,6 @@ async function writeImproveBundle(root: string, bundle: ImproveBundle, out?: str
|
|
|
350
273
|
};
|
|
351
274
|
}
|
|
352
275
|
|
|
353
|
-
type NormalizedDeployEvidenceExport = {
|
|
354
|
-
created_at?: string;
|
|
355
|
-
deploy_id?: string;
|
|
356
|
-
traces: unknown[];
|
|
357
|
-
conversations: Array<{ id: string; updated_at?: string }>;
|
|
358
|
-
runs: unknown[];
|
|
359
|
-
raw: unknown;
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
function normalizeDeployEvidenceExport(payload: unknown): NormalizedDeployEvidenceExport {
|
|
363
|
-
const evidence = isRecord(payload) && isRecord(payload.evidence) ? payload.evidence : payload;
|
|
364
|
-
|
|
365
|
-
if (!isRecord(evidence)) {
|
|
366
|
-
throw new AgentKitError("deploy_improve_response_invalid", "Improve evidence export response was not an object.");
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
const traces = Array.isArray(evidence.traces) ? evidence.traces : [];
|
|
370
|
-
const conversations = Array.isArray(evidence.conversations)
|
|
371
|
-
? evidence.conversations
|
|
372
|
-
.flatMap((conversation) => {
|
|
373
|
-
if (!isRecord(conversation)) {
|
|
374
|
-
return [];
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
const id = readString(conversation, "id") ?? readString(conversation, "conversation_id");
|
|
378
|
-
|
|
379
|
-
if (!id) {
|
|
380
|
-
return [];
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
const updatedAt = readString(conversation, "updated_at") ?? readString(conversation, "updatedAt");
|
|
384
|
-
return {
|
|
385
|
-
id,
|
|
386
|
-
...(updatedAt ? { updated_at: updatedAt } : {}),
|
|
387
|
-
};
|
|
388
|
-
})
|
|
389
|
-
: [];
|
|
390
|
-
|
|
391
|
-
return {
|
|
392
|
-
created_at: readString(evidence, "created_at") ?? readString(evidence, "createdAt"),
|
|
393
|
-
deploy_id: readString(evidence, "deploy_id") ?? readString(evidence, "deployId"),
|
|
394
|
-
traces,
|
|
395
|
-
conversations,
|
|
396
|
-
runs: Array.isArray(evidence.runs) ? evidence.runs : [],
|
|
397
|
-
raw: evidence,
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
async function tracePacketsFromDeployEvidenceExport(input: {
|
|
402
|
-
collector: DeployImproveCollector;
|
|
403
|
-
deployId: string;
|
|
404
|
-
evidence: NormalizedDeployEvidenceExport;
|
|
405
|
-
source: ImproveSource;
|
|
406
|
-
}): Promise<ImproveTracePacket[]> {
|
|
407
|
-
const traces: ImproveTracePacket[] = [];
|
|
408
|
-
const seenConversations = new Set<string>();
|
|
409
|
-
|
|
410
|
-
for (const rawTrace of input.evidence.traces) {
|
|
411
|
-
const trace = tracePacketFromPayload(input.source, input.deployId, rawTrace);
|
|
412
|
-
traces.push(trace);
|
|
413
|
-
seenConversations.add(trace.conversation_id);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
for (const conversation of input.evidence.conversations) {
|
|
417
|
-
if (seenConversations.has(conversation.id)) {
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
try {
|
|
422
|
-
const trace = await input.collector.getTrace(conversation.id);
|
|
423
|
-
const packet = tracePacketFromPayload(input.source, conversation.id, trace);
|
|
424
|
-
traces.push(packet);
|
|
425
|
-
seenConversations.add(packet.conversation_id);
|
|
426
|
-
} catch {
|
|
427
|
-
traces.push(syntheticEvidenceTrace(input.source, conversation.id, {
|
|
428
|
-
updated_at: conversation.updated_at,
|
|
429
|
-
reason: "conversation trace was not available from the hosted runtime",
|
|
430
|
-
}));
|
|
431
|
-
seenConversations.add(conversation.id);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
for (const run of input.evidence.runs) {
|
|
436
|
-
if (!isRecord(run)) {
|
|
437
|
-
continue;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
const conversationId = readString(run, "conversation_id") ?? readString(run, "conversationId");
|
|
441
|
-
|
|
442
|
-
if (conversationId && seenConversations.has(conversationId)) {
|
|
443
|
-
continue;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
const runId = readString(run, "id") ?? readString(run, "run_id") ?? `run_${shortHash(JSON.stringify(run))}`;
|
|
447
|
-
traces.push(syntheticEvidenceTrace(input.source, conversationId ?? runId, {
|
|
448
|
-
title: readString(run, "title") ?? runId,
|
|
449
|
-
updated_at: readString(run, "updated_at") ?? readString(run, "updatedAt"),
|
|
450
|
-
raw: run,
|
|
451
|
-
}));
|
|
452
|
-
|
|
453
|
-
if (conversationId) {
|
|
454
|
-
seenConversations.add(conversationId);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
return traces;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
function syntheticEvidenceTrace(
|
|
462
|
-
source: ImproveSource,
|
|
463
|
-
conversationId: string,
|
|
464
|
-
input: {
|
|
465
|
-
title?: string;
|
|
466
|
-
updated_at?: string;
|
|
467
|
-
reason?: string;
|
|
468
|
-
raw?: unknown;
|
|
469
|
-
},
|
|
470
|
-
): ImproveTracePacket {
|
|
471
|
-
const safeConversationId = redactEvalText(conversationId);
|
|
472
|
-
const title = input.title ? redactEvalText(input.title) : "Hosted evidence";
|
|
473
|
-
|
|
474
|
-
return {
|
|
475
|
-
id: `trace_${slugify(title)}_${shortHash(`${source}:${conversationId}:${JSON.stringify(input.raw ?? input.reason ?? "")}`)}`,
|
|
476
|
-
source,
|
|
477
|
-
conversation_id: safeConversationId,
|
|
478
|
-
title,
|
|
479
|
-
updated_at: input.updated_at,
|
|
480
|
-
trace: {
|
|
481
|
-
messages: [
|
|
482
|
-
{
|
|
483
|
-
role: "system",
|
|
484
|
-
content: redactEvalText(input.reason ?? "Hosted evidence did not include replayable conversation messages."),
|
|
485
|
-
...(input.updated_at ? { created_at: input.updated_at } : {}),
|
|
486
|
-
},
|
|
487
|
-
],
|
|
488
|
-
raw: redactEvidenceValue(input.raw ?? input.reason ?? null),
|
|
489
|
-
},
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
function summarizeImproveEvidence(evidence: unknown): unknown {
|
|
494
|
-
if (!isRecord(evidence)) {
|
|
495
|
-
return evidence === undefined ? undefined : { available: true };
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
return {
|
|
499
|
-
deploy_id: readString(evidence, "deploy_id") ?? readString(evidence, "deployId") ?? null,
|
|
500
|
-
runs: Array.isArray(evidence.runs) ? evidence.runs.length : 0,
|
|
501
|
-
conversations: Array.isArray(evidence.conversations) ? evidence.conversations.length : 0,
|
|
502
|
-
deliveries: Array.isArray(evidence.deliveries) ? evidence.deliveries.length : 0,
|
|
503
|
-
deploy_events: Array.isArray(evidence.deploy_events) ? evidence.deploy_events.length : 0,
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
|
|
507
276
|
async function readImproveBundle(path: string): Promise<ImproveBundle> {
|
|
508
277
|
const parsed = JSON.parse(await readFile(path, "utf8")) as unknown;
|
|
509
278
|
|
|
@@ -567,35 +336,6 @@ function resolveInsideCapsule(root: string, path: string, context: string): stri
|
|
|
567
336
|
return resolved;
|
|
568
337
|
}
|
|
569
338
|
|
|
570
|
-
function normalizeConversationSummaries(payload: unknown): Array<{ id: string; updated_at?: string }> {
|
|
571
|
-
const conversations = Array.isArray(payload)
|
|
572
|
-
? payload
|
|
573
|
-
: isRecord(payload) && Array.isArray(payload.conversations)
|
|
574
|
-
? payload.conversations
|
|
575
|
-
: null;
|
|
576
|
-
|
|
577
|
-
if (!conversations) {
|
|
578
|
-
throw new AgentKitError("deploy_conversation_response_invalid", "Conversation list response did not include conversations.");
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
return conversations.map((conversation) => {
|
|
582
|
-
if (!isRecord(conversation)) {
|
|
583
|
-
throw new AgentKitError("deploy_conversation_response_invalid", "Conversation list contained a non-object entry.");
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
const id = readString(conversation, "id") ?? readString(conversation, "conversation_id");
|
|
587
|
-
|
|
588
|
-
if (!id) {
|
|
589
|
-
throw new AgentKitError("deploy_conversation_response_invalid", "Conversation list entry did not include an id.");
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
return {
|
|
593
|
-
id,
|
|
594
|
-
updated_at: readString(conversation, "updated_at") ?? readString(conversation, "updatedAt"),
|
|
595
|
-
};
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
|
-
|
|
599
339
|
function tracePacketFromPayload(source: ImproveSource, fallbackConversationId: string, payload: unknown): ImproveTracePacket {
|
|
600
340
|
const trace = normalizeTracePayload(fallbackConversationId, payload);
|
|
601
341
|
const safeConversationId = redactEvalText(trace.conversation_id);
|
|
@@ -628,7 +368,7 @@ function normalizeTracePayload(
|
|
|
628
368
|
runs?: unknown[];
|
|
629
369
|
} {
|
|
630
370
|
if (!isRecord(payload)) {
|
|
631
|
-
throw new AgentKitError("
|
|
371
|
+
throw new AgentKitError("conversation_response_invalid", "Conversation trace response was not an object.");
|
|
632
372
|
}
|
|
633
373
|
|
|
634
374
|
const conversationId = readString(payload, "id") ?? readString(payload, "conversation_id") ?? fallbackConversationId;
|