@andreprado/agentkit 0.1.0-alpha.14 → 0.1.0-alpha.16
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 +2 -0
- package/docs/guides/add-channel.md +63 -0
- package/docs/guides/channel-security.md +32 -0
- package/docs/guides/connect-telegram.md +58 -0
- package/docs/guides/connect-whatsapp-zapster.md +65 -0
- package/docs/guides/run-evals.md +73 -25
- package/docs/llms-full.txt +24 -9
- package/docs/llms.txt +2 -0
- package/package.json +1 -1
- package/src/cli/cloud-client.ts +30 -10
- package/src/cli/commands/channels.ts +2 -0
- package/src/cli/deploy-readiness.ts +32 -11
- package/src/cli/index.ts +20 -6
- package/src/cloud/client.ts +4 -3
- package/src/cloud/contracts.ts +1 -1
- package/src/create-project.ts +1 -1
- package/src/index.ts +110 -1
- package/src/providers/pi.ts +14 -1
- package/src/providers/test.ts +36 -0
- package/src/runtime/channel-test-harness.ts +2 -0
- package/src/runtime/channels/telegram.ts +326 -10
- package/src/runtime/channels/whatsapp-zapster.ts +319 -0
- package/src/runtime/channels.ts +47 -1
- package/src/runtime/chat.ts +59 -42
- package/src/runtime/config.ts +96 -4
- package/src/runtime/core/manifest.ts +35 -3
- package/src/runtime/deploy-readiness.ts +3 -3
- package/src/runtime/dev-server.ts +243 -17
- package/src/runtime/env.ts +8 -3
- package/src/runtime/evals.ts +404 -69
- package/src/runtime/inspect.ts +46 -0
- package/src/runtime/prompt-context.ts +141 -0
- package/src/runtime/runtime-contract.ts +17 -7
- package/src/runtime/targets/cloudflare/build.ts +25 -3
- package/src/runtime/targets/container/server.ts +1 -1
- package/src/runtime/targets/vps/deploy.ts +25 -8
- package/src/runtime/tool-runner.ts +7 -0
- package/src/runtime/tools.ts +8 -2
- package/src/runtime/transcription.ts +483 -0
- package/src/templates/blank.ts +8 -3
- package/src/templates/dentista.ts +18 -10
- package/src/templates/skills/agentkit-build-agent/SKILL.md +6 -5
- package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +2 -1
- package/src/templates/skills/agentkit-capsule/SKILL.md +1 -1
- package/src/templates/skills/agentkit-channels/SKILL.md +34 -1
- package/src/templates/skills/agentkit-channels/references/channel-debugging.md +13 -0
- package/src/templates/skills/agentkit-channels/references/telegram.md +32 -0
- package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +29 -0
- package/src/templates/skills/agentkit-evals/SKILL.md +53 -13
- package/src/templates/skills/agentkit-evals/templates/multi-turn.eval.md +13 -6
- package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +8 -4
- package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +8 -4
- package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +16 -7
- package/src/templates/skills/agentkit-prompts/SKILL.md +3 -1
- package/src/templates/skills/agentkit-tools/SKILL.md +2 -1
- package/src/templates/support.ts +8 -3
|
@@ -7,7 +7,7 @@ import { Readable } from "node:stream";
|
|
|
7
7
|
|
|
8
8
|
import { openCapsuleStore, type ConversationRecord } from "../storage/sqlite";
|
|
9
9
|
import type { AgentChannel, ChannelProvider, ChannelType } from "../index";
|
|
10
|
-
import type { ChannelAdapter, RawWebhookEvent } from "./channels";
|
|
10
|
+
import type { ChannelAdapter, NormalizedChannelMessage, RawWebhookEvent } from "./channels";
|
|
11
11
|
import { telegramChannelAdapter } from "./channels/telegram";
|
|
12
12
|
import { websiteChannelAdapter } from "./channels/website";
|
|
13
13
|
import { metaWhatsappChannelAdapter } from "./channels/whatsapp-meta";
|
|
@@ -20,11 +20,13 @@ import { loadCapsuleEnv } from "./env";
|
|
|
20
20
|
import { buildInspectState } from "./inspect";
|
|
21
21
|
import { syncConfiguredKnowledgeSources } from "./knowledge/ingest";
|
|
22
22
|
import { runToolFromCwd } from "./tool-runner";
|
|
23
|
+
import { resolveTranscriptionConfig, transcribeAudio } from "./transcription";
|
|
23
24
|
|
|
24
25
|
export type AgentDevServerOptions = {
|
|
25
26
|
port?: number;
|
|
26
27
|
hostname?: string;
|
|
27
28
|
basePort?: number;
|
|
29
|
+
access?: AgentDevServerAccessPolicy;
|
|
28
30
|
};
|
|
29
31
|
|
|
30
32
|
export type AgentDevServer = {
|
|
@@ -49,6 +51,12 @@ export type AgentDevServer = {
|
|
|
49
51
|
const DEFAULT_PORT = 4123;
|
|
50
52
|
const DEFAULT_HOSTNAME = "localhost";
|
|
51
53
|
|
|
54
|
+
export type AgentDevServerAccessPolicy = "local" | "enforce";
|
|
55
|
+
|
|
56
|
+
type DevHttpServerOptions = {
|
|
57
|
+
access: AgentDevServerAccessPolicy;
|
|
58
|
+
};
|
|
59
|
+
|
|
52
60
|
type DevChannelRuntimeState = {
|
|
53
61
|
dedupeKeys: Set<string>;
|
|
54
62
|
buffers: Map<string, DevChannelBuffer>;
|
|
@@ -74,7 +82,9 @@ export async function startAgentDevServer(
|
|
|
74
82
|
const hostname = options.hostname ?? DEFAULT_HOSTNAME;
|
|
75
83
|
const requestedPort = options.port ?? options.basePort ?? DEFAULT_PORT;
|
|
76
84
|
const autoPort = options.port === undefined;
|
|
77
|
-
const start = await listenForDevServer(capsule, hostname, requestedPort, autoPort
|
|
85
|
+
const start = await listenForDevServer(capsule, hostname, requestedPort, autoPort, {
|
|
86
|
+
access: options.access ?? "local",
|
|
87
|
+
});
|
|
78
88
|
|
|
79
89
|
if (start.actualPort === undefined) {
|
|
80
90
|
throw new AgentKitError("runtime_error", "Could not determine the AgentKit dev server port.");
|
|
@@ -106,9 +116,10 @@ async function listenForDevServer(
|
|
|
106
116
|
hostname: string,
|
|
107
117
|
requestedPort: number,
|
|
108
118
|
autoPort: boolean,
|
|
119
|
+
serverOptions: DevHttpServerOptions,
|
|
109
120
|
): Promise<{ server: Server; actualPort: number; portConflict: AgentDevServer["portConflict"] }> {
|
|
110
121
|
if (requestedPort === 0) {
|
|
111
|
-
const server = createDevHttpServer(capsule);
|
|
122
|
+
const server = createDevHttpServer(capsule, serverOptions);
|
|
112
123
|
const actualPort = await listen(server, hostname, await findAvailablePort());
|
|
113
124
|
return { server, actualPort, portConflict: null };
|
|
114
125
|
}
|
|
@@ -117,7 +128,7 @@ async function listenForDevServer(
|
|
|
117
128
|
const lastPort = autoPort ? requestedPort + 50 : requestedPort;
|
|
118
129
|
|
|
119
130
|
for (let port = requestedPort; port <= lastPort; port += 1) {
|
|
120
|
-
const server = createDevHttpServer(capsule);
|
|
131
|
+
const server = createDevHttpServer(capsule, serverOptions);
|
|
121
132
|
|
|
122
133
|
try {
|
|
123
134
|
const actualPort = await listen(server, hostname, port);
|
|
@@ -142,14 +153,14 @@ async function listenForDevServer(
|
|
|
142
153
|
);
|
|
143
154
|
}
|
|
144
155
|
|
|
145
|
-
function createDevHttpServer(capsule: LoadedAgentCapsule): Server {
|
|
156
|
+
function createDevHttpServer(capsule: LoadedAgentCapsule, options: DevHttpServerOptions): Server {
|
|
146
157
|
const channelRuntime: DevChannelRuntimeState = {
|
|
147
158
|
dedupeKeys: new Set<string>(),
|
|
148
159
|
buffers: new Map<string, DevChannelBuffer>(),
|
|
149
160
|
};
|
|
150
161
|
const capsuleRoot = capsule.root;
|
|
151
162
|
return createHttpServer((incoming, outgoing) => {
|
|
152
|
-
void handleNodeRequest(capsuleRoot, incoming, outgoing, channelRuntime);
|
|
163
|
+
void handleNodeRequest(capsuleRoot, incoming, outgoing, channelRuntime, options);
|
|
153
164
|
});
|
|
154
165
|
}
|
|
155
166
|
|
|
@@ -158,11 +169,12 @@ async function handleNodeRequest(
|
|
|
158
169
|
incoming: IncomingMessage,
|
|
159
170
|
outgoing: ServerResponse,
|
|
160
171
|
channelRuntime: DevChannelRuntimeState,
|
|
172
|
+
options: DevHttpServerOptions,
|
|
161
173
|
): Promise<void> {
|
|
162
174
|
try {
|
|
163
175
|
const capsule = await loadAgentCapsule(capsuleRoot);
|
|
164
176
|
const request = nodeRequestToFetchRequest(incoming);
|
|
165
|
-
const response = await handleDevServerRequest(capsule, request, channelRuntime);
|
|
177
|
+
const response = await handleDevServerRequest(capsule, request, channelRuntime, options);
|
|
166
178
|
await writeFetchResponse(outgoing, response);
|
|
167
179
|
} catch (error) {
|
|
168
180
|
await writeFetchResponse(outgoing, jsonError(error, statusForError(error)));
|
|
@@ -222,27 +234,43 @@ async function handleDevServerRequest(
|
|
|
222
234
|
capsule: LoadedAgentCapsule,
|
|
223
235
|
request: Request,
|
|
224
236
|
channelRuntime: DevChannelRuntimeState,
|
|
237
|
+
options: DevHttpServerOptions,
|
|
225
238
|
): Promise<Response> {
|
|
226
239
|
try {
|
|
227
240
|
const url = new URL(request.url);
|
|
228
241
|
const route = normalizePath(url.pathname);
|
|
229
242
|
|
|
243
|
+
if (request.method === "GET" && route === "/health") {
|
|
244
|
+
return jsonResponse({ status: "ok", agent: capsule.config.name });
|
|
245
|
+
}
|
|
246
|
+
|
|
230
247
|
if (request.method === "GET" && route === "/") {
|
|
248
|
+
const auth = await authorizeDevRoute(capsule, request, options, "chat");
|
|
249
|
+
if (auth) {
|
|
250
|
+
return auth;
|
|
251
|
+
}
|
|
252
|
+
|
|
231
253
|
const env = await loadCapsuleEnv(capsule.root);
|
|
232
254
|
return htmlResponse(renderChatApp(capsule, url.origin, env));
|
|
233
255
|
}
|
|
234
256
|
|
|
235
257
|
if (request.method === "GET" && route === "/_agentkit") {
|
|
258
|
+
const auth = await authorizeDevRoute(capsule, request, options, "admin");
|
|
259
|
+
if (auth) {
|
|
260
|
+
return auth;
|
|
261
|
+
}
|
|
262
|
+
|
|
236
263
|
const env = await loadCapsuleEnv(capsule.root);
|
|
237
264
|
return jsonResponse(buildInspectState(capsule, env));
|
|
238
265
|
}
|
|
239
266
|
|
|
240
|
-
if (request.method === "GET" && route === "/health") {
|
|
241
|
-
return jsonResponse({ status: "ok", agent: capsule.config.name });
|
|
242
|
-
}
|
|
243
|
-
|
|
244
267
|
if (request.method === "POST" && (route === "/v1/chat" || route === "/chat")) {
|
|
245
268
|
const body = await readJsonBody(request);
|
|
269
|
+
const auth = await authorizeDevRoute(capsule, request, options, "chat", body);
|
|
270
|
+
if (auth) {
|
|
271
|
+
return auth;
|
|
272
|
+
}
|
|
273
|
+
|
|
246
274
|
const { message, conversationId } = readChatRequest(body);
|
|
247
275
|
const result = await runAgentMessage(capsule, {
|
|
248
276
|
message,
|
|
@@ -262,6 +290,11 @@ async function handleDevServerRequest(
|
|
|
262
290
|
const toolMatch = route.match(/^\/tools\/([^/]+)$/);
|
|
263
291
|
|
|
264
292
|
if (request.method === "POST" && toolMatch) {
|
|
293
|
+
const auth = await authorizeDevRoute(capsule, request, options, "admin");
|
|
294
|
+
if (auth) {
|
|
295
|
+
return auth;
|
|
296
|
+
}
|
|
297
|
+
|
|
265
298
|
const body = await readJsonBody(request);
|
|
266
299
|
const result = await runToolFromCwd(capsule.root, {
|
|
267
300
|
name: decodeURIComponent(toolMatch[1]),
|
|
@@ -275,6 +308,11 @@ async function handleDevServerRequest(
|
|
|
275
308
|
const fileMatch = route.match(/^\/files\/(.+)$/);
|
|
276
309
|
|
|
277
310
|
if (fileMatch) {
|
|
311
|
+
const auth = await authorizeDevRoute(capsule, request, options, "admin");
|
|
312
|
+
if (auth) {
|
|
313
|
+
return auth;
|
|
314
|
+
}
|
|
315
|
+
|
|
278
316
|
return await handleLocalFile(capsule, request, decodeURIComponent(fileMatch[1]));
|
|
279
317
|
}
|
|
280
318
|
|
|
@@ -289,18 +327,33 @@ async function handleDevServerRequest(
|
|
|
289
327
|
}
|
|
290
328
|
|
|
291
329
|
if (request.method === "GET" && route === "/v1/conversations") {
|
|
330
|
+
const auth = await authorizeDevRoute(capsule, request, options, "admin");
|
|
331
|
+
if (auth) {
|
|
332
|
+
return auth;
|
|
333
|
+
}
|
|
334
|
+
|
|
292
335
|
return jsonResponse({ conversations: await listConversations(capsule) });
|
|
293
336
|
}
|
|
294
337
|
|
|
295
338
|
const conversationMatch = route.match(/^\/v1\/conversations\/([^/]+)$/);
|
|
296
339
|
|
|
297
340
|
if (request.method === "GET" && conversationMatch) {
|
|
341
|
+
const auth = await authorizeDevRoute(capsule, request, options, "admin");
|
|
342
|
+
if (auth) {
|
|
343
|
+
return auth;
|
|
344
|
+
}
|
|
345
|
+
|
|
298
346
|
return jsonResponse(await getConversation(capsule, decodeURIComponent(conversationMatch[1])));
|
|
299
347
|
}
|
|
300
348
|
|
|
301
349
|
const conversationTraceMatch = route.match(/^\/v1\/conversations\/([^/]+)\/trace$/);
|
|
302
350
|
|
|
303
351
|
if (request.method === "GET" && conversationTraceMatch) {
|
|
352
|
+
const auth = await authorizeDevRoute(capsule, request, options, "admin");
|
|
353
|
+
if (auth) {
|
|
354
|
+
return auth;
|
|
355
|
+
}
|
|
356
|
+
|
|
304
357
|
return jsonResponse(await getConversationTrace(capsule, decodeURIComponent(conversationTraceMatch[1])));
|
|
305
358
|
}
|
|
306
359
|
|
|
@@ -310,6 +363,109 @@ async function handleDevServerRequest(
|
|
|
310
363
|
}
|
|
311
364
|
}
|
|
312
365
|
|
|
366
|
+
async function authorizeDevRoute(
|
|
367
|
+
capsule: LoadedAgentCapsule,
|
|
368
|
+
request: Request,
|
|
369
|
+
options: DevHttpServerOptions,
|
|
370
|
+
kind: "chat" | "admin",
|
|
371
|
+
body?: unknown,
|
|
372
|
+
): Promise<Response | null> {
|
|
373
|
+
if (options.access === "local") {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const env = await loadCapsuleEnv(capsule.root);
|
|
378
|
+
|
|
379
|
+
if (kind === "admin") {
|
|
380
|
+
return authorizeBearerSecret(request, body, env.AGENTKIT_PRIVATE_TOKEN, {
|
|
381
|
+
missingCode: "deploy_not_ready",
|
|
382
|
+
missingMessage: "Privileged AgentKit endpoints require AGENTKIT_PRIVATE_TOKEN to be set.",
|
|
383
|
+
unauthorizedMessage: "Missing or invalid AgentKit admin token.",
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (capsule.config.access.mode === "public") {
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (capsule.config.access.mode === "private") {
|
|
392
|
+
return authorizeBearerSecret(request, body, env.AGENTKIT_PRIVATE_TOKEN, {
|
|
393
|
+
missingCode: "deploy_not_ready",
|
|
394
|
+
missingMessage: "Private deploy requires AGENTKIT_PRIVATE_TOKEN to be set.",
|
|
395
|
+
unauthorizedMessage: "Missing or invalid deploy access token.",
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return authorizeBearerSecret(request, body, env.AGENTKIT_ACCESS_TOKEN, {
|
|
400
|
+
missingCode: "deploy_not_ready",
|
|
401
|
+
missingMessage: "Token deploy requires AGENTKIT_ACCESS_TOKEN to be set.",
|
|
402
|
+
unauthorizedMessage: "Missing or invalid deploy access token.",
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function authorizeBearerSecret(
|
|
407
|
+
request: Request,
|
|
408
|
+
body: unknown,
|
|
409
|
+
secret: string | undefined,
|
|
410
|
+
messages: { missingCode: string; missingMessage: string; unauthorizedMessage: string },
|
|
411
|
+
): Response | null {
|
|
412
|
+
if (!secret) {
|
|
413
|
+
return jsonResponse(
|
|
414
|
+
{
|
|
415
|
+
error: {
|
|
416
|
+
code: messages.missingCode,
|
|
417
|
+
message: messages.missingMessage,
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
{ status: 503 },
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (readRequestAccessToken(request, body) !== secret) {
|
|
425
|
+
return jsonResponse(
|
|
426
|
+
{
|
|
427
|
+
error: {
|
|
428
|
+
code: "unauthorized",
|
|
429
|
+
message: messages.unauthorizedMessage,
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
{ status: 401 },
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function readRequestAccessToken(request: Request, body: unknown): string {
|
|
440
|
+
const authorization = request.headers.get("authorization");
|
|
441
|
+
|
|
442
|
+
if (authorization?.startsWith("Bearer ")) {
|
|
443
|
+
return authorization.slice("Bearer ".length).trim();
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const header =
|
|
447
|
+
request.headers.get("x-agentkit-access-token") ??
|
|
448
|
+
request.headers.get("x-agentkit-deploy-access-token") ??
|
|
449
|
+
"";
|
|
450
|
+
const token = header.startsWith("Bearer ") ? header.slice("Bearer ".length) : header;
|
|
451
|
+
if (token.trim()) {
|
|
452
|
+
return token.trim();
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (body && typeof body === "object") {
|
|
456
|
+
const record = body as { access_token?: unknown; accessToken?: unknown };
|
|
457
|
+
const bodyToken =
|
|
458
|
+
typeof record.access_token === "string"
|
|
459
|
+
? record.access_token
|
|
460
|
+
: typeof record.accessToken === "string"
|
|
461
|
+
? record.accessToken
|
|
462
|
+
: "";
|
|
463
|
+
return bodyToken.trim();
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return "";
|
|
467
|
+
}
|
|
468
|
+
|
|
313
469
|
async function handlePortableChannel(
|
|
314
470
|
capsule: LoadedAgentCapsule,
|
|
315
471
|
request: Request,
|
|
@@ -360,7 +516,9 @@ async function handlePortableChannel(
|
|
|
360
516
|
|
|
361
517
|
channelRuntime.dedupeKeys.add(event.dedupeKey);
|
|
362
518
|
|
|
363
|
-
|
|
519
|
+
const processedMessage = await processDevChannelMessage(capsule, adapter, channel, event.message, secrets);
|
|
520
|
+
|
|
521
|
+
if (event.kind !== "message" || !processedMessage) {
|
|
364
522
|
deliveries.push({ state: "skipped", dedupeKey: event.dedupeKey, unsupportedReason: event.unsupportedReason });
|
|
365
523
|
continue;
|
|
366
524
|
}
|
|
@@ -376,7 +534,7 @@ async function handlePortableChannel(
|
|
|
376
534
|
bufferDevChannelMessage(capsule, channelRuntime, {
|
|
377
535
|
bufferKey: `${channel.name}:${event.externalIdentity.key}`,
|
|
378
536
|
conversationId,
|
|
379
|
-
content:
|
|
537
|
+
content: processedMessage.content,
|
|
380
538
|
receivedAt: event.receivedAt,
|
|
381
539
|
buffer,
|
|
382
540
|
});
|
|
@@ -390,7 +548,7 @@ async function handlePortableChannel(
|
|
|
390
548
|
}
|
|
391
549
|
|
|
392
550
|
const result = await runAgentMessage(capsule, {
|
|
393
|
-
message:
|
|
551
|
+
message: processedMessage.content,
|
|
394
552
|
conversationId,
|
|
395
553
|
signal: request.signal,
|
|
396
554
|
});
|
|
@@ -405,6 +563,71 @@ async function handlePortableChannel(
|
|
|
405
563
|
return jsonResponse({ deliveries });
|
|
406
564
|
}
|
|
407
565
|
|
|
566
|
+
async function processDevChannelMessage(
|
|
567
|
+
capsule: LoadedAgentCapsule,
|
|
568
|
+
adapter: ChannelAdapter,
|
|
569
|
+
channel: AgentChannel,
|
|
570
|
+
message: NormalizedChannelMessage | undefined,
|
|
571
|
+
secrets: Record<string, string>,
|
|
572
|
+
): Promise<NormalizedChannelMessage | null> {
|
|
573
|
+
if (!message) {
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (message.contentType === "text" && message.content) {
|
|
578
|
+
return message;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (message.contentType !== "audio" || !message.audio || channel.audio?.mode !== "transcribe") {
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (!adapter.downloadMedia) {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const env = await loadCapsuleEnv(capsule.root);
|
|
590
|
+
const transcriptionConfig = resolveTranscriptionConfig(capsule.config.transcription);
|
|
591
|
+
const secretName = transcriptionConfig.secret;
|
|
592
|
+
const download = await adapter.downloadMedia({
|
|
593
|
+
type: channel.type,
|
|
594
|
+
provider: channel.provider,
|
|
595
|
+
channelId: channel.name,
|
|
596
|
+
message,
|
|
597
|
+
secrets: {
|
|
598
|
+
...secrets,
|
|
599
|
+
...(secretName ? { [secretName]: env[secretName] ?? "" } : {}),
|
|
600
|
+
...(env.AGENTKIT_TELEGRAM_API_BASE_URL ? { AGENTKIT_TELEGRAM_API_BASE_URL: env.AGENTKIT_TELEGRAM_API_BASE_URL } : {}),
|
|
601
|
+
},
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
if (!download.ok) {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const transcription = await transcribeAudio({
|
|
609
|
+
config: transcriptionConfig,
|
|
610
|
+
secrets: {
|
|
611
|
+
...(secretName ? { [secretName]: env[secretName] ?? "" } : {}),
|
|
612
|
+
},
|
|
613
|
+
audio: download.audio,
|
|
614
|
+
filename: download.filename,
|
|
615
|
+
...(download.mimeType ? { mimeType: download.mimeType } : {}),
|
|
616
|
+
...(download.durationSeconds !== undefined ? { durationSeconds: download.durationSeconds } : {}),
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
if (!transcription.ok) {
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
return {
|
|
624
|
+
role: "user",
|
|
625
|
+
contentType: "text",
|
|
626
|
+
content: `The user sent an audio message.\n\nTranscript:\n${transcription.text}`,
|
|
627
|
+
providerMessageId: message.providerMessageId,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
|
|
408
631
|
function bufferDevChannelMessage(
|
|
409
632
|
capsule: LoadedAgentCapsule,
|
|
410
633
|
runtime: DevChannelRuntimeState,
|
|
@@ -509,6 +732,10 @@ function unrefTimer(timer: ReturnType<typeof setTimeout>): void {
|
|
|
509
732
|
}
|
|
510
733
|
|
|
511
734
|
async function handleLocalFile(capsule: LoadedAgentCapsule, request: Request, rawKey: string): Promise<Response> {
|
|
735
|
+
if (capsule.config.storage.files?.driver !== "r2") {
|
|
736
|
+
throw new AgentKitError("files_not_configured", "File storage is not configured for this Agent Capsule.");
|
|
737
|
+
}
|
|
738
|
+
|
|
512
739
|
const key = normalizeFileKey(rawKey);
|
|
513
740
|
const root = resolve(capsule.root, ".agentkit/files");
|
|
514
741
|
const path = resolve(root, key);
|
|
@@ -519,9 +746,7 @@ async function handleLocalFile(capsule: LoadedAgentCapsule, request: Request, ra
|
|
|
519
746
|
|
|
520
747
|
if (request.method === "PUT") {
|
|
521
748
|
const bytes = new Uint8Array(await request.arrayBuffer());
|
|
522
|
-
const maxUploadBytes = capsule.config.storage.files
|
|
523
|
-
? capsule.config.storage.files.maxUploadBytes ?? 10 * 1024 * 1024
|
|
524
|
-
: 10 * 1024 * 1024;
|
|
749
|
+
const maxUploadBytes = capsule.config.storage.files.maxUploadBytes ?? 10 * 1024 * 1024;
|
|
525
750
|
|
|
526
751
|
if (bytes.byteLength > maxUploadBytes) {
|
|
527
752
|
throw new AgentKitError("file_too_large", `File exceeds max upload size of ${maxUploadBytes} bytes.`);
|
|
@@ -715,6 +940,7 @@ function statusForError(error: unknown): number {
|
|
|
715
940
|
error.code === "conversation_not_found" ||
|
|
716
941
|
error.code === "not_found" ||
|
|
717
942
|
error.code === "tool_not_found" ||
|
|
943
|
+
error.code === "files_not_configured" ||
|
|
718
944
|
error.code === "file_not_found" ||
|
|
719
945
|
error.code === "channel_not_found"
|
|
720
946
|
) {
|
package/src/runtime/env.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFile, writeFile } from "node:fs/promises";
|
|
1
|
+
import { chmod, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { AgentKitError } from "./errors";
|
|
@@ -83,7 +83,7 @@ export async function setCapsuleEnv(root: string, name: string, value: string):
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
await
|
|
86
|
+
await writeSecureDotEnv(path, `${nextLines.join("\n").replace(/\n*$/, "")}\n`);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
export async function unsetCapsuleEnv(root: string, name: string): Promise<boolean> {
|
|
@@ -103,10 +103,15 @@ export async function unsetCapsuleEnv(root: string, name: string): Promise<boole
|
|
|
103
103
|
return false;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
await
|
|
106
|
+
await writeSecureDotEnv(path, `${nextLines.join("\n").replace(/\n*$/, "")}\n`);
|
|
107
107
|
return true;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
async function writeSecureDotEnv(path: string, source: string): Promise<void> {
|
|
111
|
+
await writeFile(path, source, { mode: 0o600 });
|
|
112
|
+
await chmod(path, 0o600);
|
|
113
|
+
}
|
|
114
|
+
|
|
110
115
|
async function readDotEnvSource(path: string): Promise<string> {
|
|
111
116
|
try {
|
|
112
117
|
return await readFile(path, "utf8");
|