@andreprado/agentkit 0.1.0-alpha.6 → 0.1.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -37,9 +37,13 @@ agentkit tool <name> [--input <path-or-json>]
37
37
  agentkit knowledge add <path-or-url>
38
38
  agentkit knowledge sync
39
39
  agentkit knowledge search <query> [--top-k <number>]
40
+ agentkit spec init --brief <text>
40
41
  agentkit db migrate
42
+ agentkit sync run
41
43
  agentkit eval run
44
+ agentkit eval from-conversation <conversation-id>
42
45
  agentkit conversations list
46
+ agentkit conversations trace <conversation-id>
43
47
  ```
44
48
 
45
49
  Knowledge indexes local `.md`, `.txt`, and `.csv` sources into the capsule database and registers the internal `agentkit_search_knowledge` chat tool when `knowledge` is configured in `agentkit.config.ts`. `agentkit dev` and `agentkit chat` sync configured sources automatically; when embeddings are configured, local semantic search uses a libSQL vector sidecar. Hosted Cloudflare deploys package configured local Knowledge sources and sync them into the project Turso database automatically during `agentkit deploy`; when embeddings are configured, the deploy also creates and populates the hosted Turso vector index.
@@ -76,6 +76,8 @@ telegramChannel({
76
76
  - `secret_token`: `TELEGRAM_WEBHOOK_SECRET`;
77
77
  - `allowed_updates`: `["message"]`.
78
78
 
79
+ Against AgentKit Cloud, `--apply` runs through the Cloud API and uses managed hosted secrets. The legacy local fallback only uses `TELEGRAM_BOT_TOKEN` and `TELEGRAM_WEBHOOK_SECRET` from the shell when the Cloud API does not expose channel setup.
80
+
79
81
  ## Safety Rules
80
82
 
81
83
  - Use `--apply` only when real Telegram secrets are present.
@@ -86,6 +86,15 @@ Develop an appointment and intake agent for an ophthalmology office.
86
86
 
87
87
  The generated `AGENTS.md`, `AGENTKIT.md`, `CLAUDE.md`, and `skills/` pack tell the coding agent which files to edit, which task skill to load, and which verification commands to run. There is no wizard or recipe layer: the coding agent edits the capsule directly from the scaffold, contract, and owner request. The default router is `skills/agentkit-capsule/SKILL.md`; `llms-full.txt` is reserved for complete-contract checks.
88
88
 
89
+ After the owner gives the general idea, the coding agent should create or update the implementation contract itself:
90
+
91
+ ```sh
92
+ npm run agentkit -- spec init --brief "Develop an appointment and intake agent for an ophthalmology office."
93
+ npm run agentkit -- spec check
94
+ ```
95
+
96
+ `AGENT_SPEC.md` is an internal working contract for the coding agent. It is not a form the owner must fill before work starts.
97
+
89
98
  Optional shortcut when copying a prompt into another coding agent:
90
99
 
91
100
  ```sh
@@ -22,6 +22,15 @@ Run evals:
22
22
  agentkit eval run
23
23
  ```
24
24
 
25
+ Create an eval from a stored conversation:
26
+
27
+ ```sh
28
+ agentkit conversations list
29
+ agentkit conversations trace <conversation-id>
30
+ agentkit eval from-conversation <conversation-id>
31
+ agentkit eval run
32
+ ```
33
+
25
34
  ## Files Created Or Edited
26
35
 
27
36
  Create or edit:
@@ -62,7 +71,33 @@ matches_regex
62
71
  persisted_tool_call
63
72
  ```
64
73
 
65
- `persisted_tool_call` validates the tool call saved in local SQLite `tool_calls`, not a provider-specific raw response shape. It can be a tool name string or an object with `name`, `input`, `output`, `status`, and/or `visibility`.
74
+ Multi-turn conversation evals use `turns`:
75
+
76
+ ```ts
77
+ export default {
78
+ name: "buyer under budget",
79
+ turns: [
80
+ {
81
+ input: "I want a house up to 600k near Pinheiros.",
82
+ expect: {
83
+ persisted_tool_call: {
84
+ name: "buscar_imoveis",
85
+ status: "completed",
86
+ input: { maxPrice: 600000 },
87
+ },
88
+ },
89
+ },
90
+ {
91
+ input: "Show me the best two.",
92
+ expect: {
93
+ contains: ["Pinheiros", "R$"],
94
+ },
95
+ },
96
+ ],
97
+ };
98
+ ```
99
+
100
+ `persisted_tool_call` validates the tool call saved in local SQLite `tool_calls`, not a provider-specific raw response shape. It can be a tool name string or an object with `name`, `input`, `output`, `rendered`, `status`, and/or `visibility`.
66
101
 
67
102
  Use response assertions and persisted tool assertions together when internal operational output must not leak:
68
103
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andreprado/agentkit",
3
- "version": "0.1.0-alpha.6",
3
+ "version": "0.1.0-alpha.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "repository": {
@@ -153,9 +153,13 @@ export async function cloudPost<T>(apiUrl: string, path: string, body: unknown):
153
153
  return payload as T;
154
154
  }
155
155
 
156
- export function cloudFetch(apiUrl: string, path: string, init: RequestInit = {}): Promise<Response> {
156
+ export async function cloudFetch(apiUrl: string, path: string, init: RequestInit = {}): Promise<Response> {
157
157
  const headers = new Headers(init.headers);
158
- const apiToken = process.env.AGENTKIT_CLOUD_API_TOKEN;
158
+ const auth = await readCloudAuth();
159
+ const apiToken =
160
+ auth?.source === "file" && !cloudAuthMatchesApiUrl(auth, apiUrl)
161
+ ? undefined
162
+ : auth?.token;
159
163
 
160
164
  if (apiToken && !headers.has("Authorization")) {
161
165
  headers.set("Authorization", `Bearer ${apiToken}`);
@@ -167,6 +171,10 @@ export function cloudFetch(apiUrl: string, path: string, init: RequestInit = {})
167
171
  });
168
172
  }
169
173
 
174
+ function cloudAuthMatchesApiUrl(auth: CloudAuthConfig, apiUrl: string): boolean {
175
+ return parseCloudApiUrl(auth.apiUrl) === parseCloudApiUrl(apiUrl);
176
+ }
177
+
170
178
  export async function cloudApiRequest(apiUrl: string, path: string, init: RequestInit): Promise<unknown> {
171
179
  const auth = await readCloudAuth();
172
180
  const headers = new Headers(init.headers);
@@ -99,7 +99,7 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
99
99
  printChannelStatus(channel);
100
100
  if (channel.type === "telegram") {
101
101
  if (args.flags.apply) {
102
- await applyTelegramWebhook(channel);
102
+ await applyTelegramWebhook(channel, args.flags.api);
103
103
  console.log("Telegram webhook updated.");
104
104
  } else {
105
105
  console.log(`Setup: call Telegram setWebhook with ${channel.webhook_url}`);
@@ -201,7 +201,23 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
201
201
  );
202
202
  }
203
203
 
204
- async function applyTelegramWebhook(channel: CloudChannel): Promise<void> {
204
+ async function applyTelegramWebhook(
205
+ channel: CloudChannel,
206
+ apiFlag: string | boolean | undefined,
207
+ ): Promise<void> {
208
+ try {
209
+ await cloudPost(
210
+ parseCloudApiUrl(apiFlag),
211
+ `/v1/channels/${encodeURIComponent(channel.id)}/setup`,
212
+ {},
213
+ );
214
+ return;
215
+ } catch (error) {
216
+ if (!shouldFallbackToLocalTelegramSetup(error)) {
217
+ throw error;
218
+ }
219
+ }
220
+
205
221
  const botToken = process.env.TELEGRAM_BOT_TOKEN;
206
222
  const secretToken = process.env.TELEGRAM_WEBHOOK_SECRET;
207
223
 
@@ -232,6 +248,16 @@ async function applyTelegramWebhook(channel: CloudChannel): Promise<void> {
232
248
  }
233
249
  }
234
250
 
251
+ function shouldFallbackToLocalTelegramSetup(error: unknown): boolean {
252
+ if (!(error instanceof Error)) {
253
+ return false;
254
+ }
255
+
256
+ return /Unknown AgentKit Cloud route|does not support channel delivery records yet|does not support channel resources yet/.test(
257
+ error.message,
258
+ );
259
+ }
260
+
235
261
  function isTelegramOk(payload: unknown): payload is { ok: true } {
236
262
  return Boolean(payload && typeof payload === "object" && "ok" in payload && payload.ok === true);
237
263
  }
@@ -335,6 +335,13 @@ function renderDeployChatUi(input: { deployUrl: string }): string {
335
335
  messages.scrollTop = messages.scrollHeight;
336
336
  }
337
337
 
338
+ textarea.addEventListener("keydown", (event) => {
339
+ if (event.key !== "Enter" || event.shiftKey || event.isComposing) return;
340
+ event.preventDefault();
341
+ if (send.disabled) return;
342
+ form.requestSubmit();
343
+ });
344
+
338
345
  form.addEventListener("submit", async (event) => {
339
346
  event.preventDefault();
340
347
  const message = textarea.value.trim();
package/src/cli/help.ts CHANGED
@@ -23,9 +23,12 @@ Start:
23
23
  Build:
24
24
  agentkit knowledge add <path>
25
25
  agentkit knowledge search <query>
26
+ agentkit spec init --brief <text>
27
+ agentkit sync run
26
28
  agentkit tool <name> [--input <path-or-json>]
27
29
  agentkit db migrate
28
30
  agentkit eval run
31
+ agentkit eval from-conversation <id>
29
32
  agentkit conversations list
30
33
 
31
34
  Deploy:
@@ -59,13 +62,19 @@ Usage:
59
62
  agentkit knowledge sync
60
63
  agentkit knowledge inspect
61
64
  agentkit knowledge search <query> [--top-k <number>]
65
+ agentkit spec init --brief <text> [--out <path>] [--force]
66
+ agentkit spec check [--file <path>]
67
+ agentkit sync init [--force]
68
+ agentkit sync run [--file <path>]
62
69
  agentkit db migrate
63
70
  agentkit db reset --yes
64
71
  agentkit db shell
65
72
  agentkit db seed [--file <path>]
66
73
  agentkit eval run
74
+ agentkit eval from-conversation <conversation-id> [--out <path>] [--force]
67
75
  agentkit conversations list
68
76
  agentkit conversations show <conversation-id>
77
+ agentkit conversations trace <conversation-id>
69
78
  agentkit channels list
70
79
  agentkit channels add <website|telegram|whatsapp> <name> [--provider zapster|meta] [--api <url>]
71
80
  agentkit channels setup <name> [--apply] [--api <url>]
@@ -113,9 +122,11 @@ Commands:
113
122
  chat Send one message to the local Agent Capsule
114
123
  tool Run one registered tool directly
115
124
  knowledge Add, sync, inspect, and search local Knowledge sources
125
+ spec Create or check the agent implementation contract from the owner brief
126
+ sync Run or scaffold catalog/data synchronization
116
127
  db Manage the local SQLite database
117
- eval Run local eval files
118
- conversations List and inspect stored local conversations
128
+ eval Run local eval files or create one from a conversation
129
+ conversations List, inspect, and trace stored local conversations
119
130
  channels Manage hosted channel resources
120
131
  inspect Print machine-readable Agent Capsule state
121
132
  build Build a hosted deploy artifact
@@ -150,6 +161,11 @@ export function renderCommandHelp(command: string): string {
150
161
  chat: 'agentkit chat --message "hello" [--conversation-id <id>]',
151
162
  tool: "agentkit tool <name> [--input <path-or-json>]",
152
163
  knowledge: "agentkit knowledge add <path-or-url> | sync | inspect | search <query> [--top-k <number>]",
164
+ spec: 'agentkit spec init --brief "owner request" | agentkit spec check',
165
+ sync: "agentkit sync init [--force] | agentkit sync run [--file <path>]",
166
+ eval: "agentkit eval run | agentkit eval from-conversation <conversation-id> [--out <path>] [--force]",
167
+ conversations:
168
+ "agentkit conversations list | agentkit conversations show <conversation-id> | agentkit conversations trace <conversation-id>",
153
169
  deploy:
154
170
  'agentkit deploy [--smoke "hello"] | agentkit deploy smoke [--message "hello"] | agentkit deploy doctor | status | pause | resume',
155
171
  secret:
package/src/cli/index.ts CHANGED
@@ -9,6 +9,7 @@ import { findAgentCapsuleRoot, loadAgentCapsule } from "../runtime/config";
9
9
  import { readLocalDeployStateIfExists, type LocalDeployState } from "../runtime/core/deploy-state";
10
10
  import { resolveBuildTarget, resolveDeployTarget } from "../runtime/core/targets";
11
11
  import { getConversationFromCwd, listConversationsFromCwd } from "../runtime/conversations";
12
+ import { getConversationTraceFromCwd } from "../runtime/traces";
12
13
  import { runAgentMessageFromCwd } from "../runtime/chat";
13
14
  import { deployBuildToAgentKitCloud, writeLocalDeployState } from "../runtime/deploy";
14
15
  import {
@@ -17,9 +18,11 @@ import {
17
18
  resetLocalDatabase,
18
19
  seedLocalDatabase,
19
20
  } from "../runtime/db-commands";
20
- import { runEvalsFromCwd } from "../runtime/evals";
21
+ import { runEvalsFromCwd, writeEvalFromConversation } from "../runtime/evals";
21
22
  import { findAgentKitDocs } from "../runtime/docs";
22
23
  import { listCapsuleEnv, setCapsuleEnv, unsetCapsuleEnv } from "../runtime/env";
24
+ import { checkAgentSpec, initializeAgentSpec } from "../runtime/spec";
25
+ import { initializeSyncScaffold, runCapsuleSync } from "../runtime/sync";
23
26
  import { runToolFromCwd } from "../runtime/tool-runner";
24
27
  import { startAgentDevServer } from "../runtime/dev-server";
25
28
  import { inspectAgentCapsule } from "../runtime/inspect";
@@ -147,6 +150,7 @@ async function main() {
147
150
  console.log(`API: ${devServer.urls.api}`);
148
151
  console.log(`Inspect: ${devServer.urls.inspect}`);
149
152
  console.log(`Storage: ${devServer.storage ?? "managed"}`);
153
+ console.log("Reload: prompts/config reload per request; restart after changing tool modules or dependencies");
150
154
  await waitForShutdown(devServer.stop);
151
155
  return;
152
156
  }
@@ -224,6 +228,8 @@ async function main() {
224
228
  const result = await migrateLocalDatabase(process.cwd());
225
229
  console.log("Local database migrated");
226
230
  console.log(`Database: ${result.databasePath}`);
231
+ console.log(`Migrations: ${result.migrationsPath ?? "none"}`);
232
+ console.log(`Applied: ${result.migrationsApplied.length > 0 ? result.migrationsApplied.join(", ") : "none"}`);
227
233
  console.log(`Schema: ${result.schemaPath ?? "none"}`);
228
234
  return;
229
235
  }
@@ -237,6 +243,8 @@ async function main() {
237
243
  console.log("Local database reset");
238
244
  console.log(`Deleted: ${result.resetPath}`);
239
245
  console.log(`Database: ${result.databasePath}`);
246
+ console.log(`Migrations: ${result.migrationsPath ?? "none"}`);
247
+ console.log(`Applied: ${result.migrationsApplied.length > 0 ? result.migrationsApplied.join(", ") : "none"}`);
240
248
  console.log(`Schema: ${result.schemaPath ?? "none"}`);
241
249
  return;
242
250
  }
@@ -259,6 +267,8 @@ async function main() {
259
267
  const result = await seedLocalDatabase(process.cwd(), seedFile);
260
268
  console.log("Local database seeded");
261
269
  console.log(`Database: ${result.databasePath}`);
270
+ console.log(`Migrations: ${result.migrationsPath ?? "none"}`);
271
+ console.log(`Applied: ${result.migrationsApplied.length > 0 ? result.migrationsApplied.join(", ") : "none"}`);
262
272
  console.log(`Schema: ${result.schemaPath ?? "none"}`);
263
273
  console.log(`Seed: ${result.seedPath}`);
264
274
  return;
@@ -268,10 +278,39 @@ async function main() {
268
278
  }
269
279
 
270
280
  if (args.command === "eval") {
271
- const [subcommand] = args.positional;
281
+ const [subcommand, conversationId] = args.positional;
282
+
283
+ if (subcommand === "from-conversation") {
284
+ if (!conversationId) {
285
+ throw new Error("Missing conversation id. Usage: agentkit eval from-conversation <conversation-id> [--out <path>] [--force]");
286
+ }
287
+
288
+ const out = parseOptionalStringFlag(
289
+ args.flags.out,
290
+ "out",
291
+ "agentkit eval from-conversation <conversation-id> --out <path>",
292
+ );
293
+ const name = parseOptionalStringFlag(
294
+ args.flags.name,
295
+ "name",
296
+ "agentkit eval from-conversation <conversation-id> --name <name>",
297
+ );
298
+ const result = await writeEvalFromConversation(process.cwd(), {
299
+ conversationId,
300
+ out,
301
+ name,
302
+ force: args.flags.force === true,
303
+ });
304
+
305
+ console.log("Eval created from conversation");
306
+ console.log(`Conversation: ${result.conversationId}`);
307
+ console.log(`File: ${relativePath(result.root, result.file)}`);
308
+ console.log(`Turns: ${result.turns}`);
309
+ return;
310
+ }
272
311
 
273
312
  if (subcommand !== "run") {
274
- throw new Error("Usage: agentkit eval run");
313
+ throw new Error("Usage: agentkit eval run | agentkit eval from-conversation <conversation-id> [--out <path>] [--force]");
275
314
  }
276
315
 
277
316
  const summary = await runEvalsFromCwd(process.cwd());
@@ -340,7 +379,17 @@ async function main() {
340
379
  return;
341
380
  }
342
381
 
343
- throw new Error("Usage: agentkit conversations list | agentkit conversations show <conversation-id>");
382
+ if (subcommand === "trace") {
383
+ if (!conversationId) {
384
+ throw new Error("Missing conversation id. Usage: agentkit conversations trace <conversation-id>");
385
+ }
386
+
387
+ const trace = await getConversationTraceFromCwd(process.cwd(), conversationId);
388
+ console.log(JSON.stringify(trace, null, 2));
389
+ return;
390
+ }
391
+
392
+ throw new Error("Usage: agentkit conversations list | agentkit conversations show <conversation-id> | agentkit conversations trace <conversation-id>");
344
393
  }
345
394
 
346
395
  if (args.command === "knowledge") {
@@ -348,6 +397,77 @@ async function main() {
348
397
  return;
349
398
  }
350
399
 
400
+ if (args.command === "sync") {
401
+ const [subcommand] = args.positional;
402
+
403
+ if (subcommand === "init") {
404
+ const result = await initializeSyncScaffold(process.cwd(), {
405
+ force: args.flags.force === true,
406
+ });
407
+
408
+ console.log("Sync scaffold ready");
409
+ if (result.files.length === 0) {
410
+ console.log("Files: none written; existing files kept");
411
+ } else {
412
+ console.log(`Files: ${result.files.join(", ")}`);
413
+ }
414
+ return;
415
+ }
416
+
417
+ if (subcommand === "run") {
418
+ const file = parseOptionalStringFlag(args.flags.file, "file", "agentkit sync run --file <path>");
419
+ const result = await runCapsuleSync(process.cwd(), { file });
420
+
421
+ console.log("Sync completed");
422
+ console.log(`File: ${relativePath(result.root, result.file)}`);
423
+ console.log(`Output: ${JSON.stringify(result.output)}`);
424
+ return;
425
+ }
426
+
427
+ throw new Error("Usage: agentkit sync init [--force] | agentkit sync run [--file <path>]");
428
+ }
429
+
430
+ if (args.command === "spec") {
431
+ const [subcommand] = args.positional;
432
+
433
+ if (subcommand === "init") {
434
+ const brief = parseRequiredStringFlag(
435
+ args.flags.brief,
436
+ "brief",
437
+ 'agentkit spec init --brief "agent for a real estate brokerage"',
438
+ );
439
+ const out = parseOptionalStringFlag(args.flags.out, "out", "agentkit spec init --out <path>");
440
+ const result = await initializeAgentSpec(process.cwd(), {
441
+ brief,
442
+ out,
443
+ force: args.flags.force === true,
444
+ });
445
+
446
+ console.log("Agent spec created");
447
+ console.log(`File: ${relativePath(result.root, result.file)}`);
448
+ return;
449
+ }
450
+
451
+ if (subcommand === "check") {
452
+ const file = parseOptionalStringFlag(args.flags.file, "file", "agentkit spec check --file <path>");
453
+ const result = await checkAgentSpec(process.cwd(), file);
454
+
455
+ if (result.ok) {
456
+ console.log(`Agent spec ok: ${relativePath(result.root, result.file)}`);
457
+ return;
458
+ }
459
+
460
+ console.log(`Agent spec incomplete: ${relativePath(result.root, result.file)}`);
461
+ for (const heading of result.missingHeadings) {
462
+ console.log(` - missing ## ${heading}`);
463
+ }
464
+ process.exitCode = 1;
465
+ return;
466
+ }
467
+
468
+ throw new Error('Usage: agentkit spec init --brief "<owner request>" [--out <path>] [--force] | agentkit spec check [--file <path>]');
469
+ }
470
+
351
471
  if (args.command === "inspect") {
352
472
  const state = await inspectAgentCapsule(process.cwd());
353
473
  console.log(JSON.stringify(state, null, 2));
package/src/index.ts CHANGED
@@ -105,6 +105,7 @@ export type AgentDatabaseConfig =
105
105
  | {
106
106
  driver: "turso";
107
107
  schema?: string;
108
+ migrations?: string;
108
109
  };
109
110
 
110
111
  export type AgentFilesConfig =
@@ -178,9 +179,42 @@ export type DatabaseRunner = {
178
179
  batch(statements: DatabaseStatement[]): Promise<DatabaseResult[]>;
179
180
  };
180
181
 
182
+ export type ToolRenderField = {
183
+ label: string;
184
+ value: string;
185
+ };
186
+
187
+ export type ToolRenderAction = {
188
+ label: string;
189
+ href?: string;
190
+ };
191
+
192
+ export type ToolRenderBlock =
193
+ | {
194
+ type: "text";
195
+ text: string;
196
+ }
197
+ | {
198
+ type: "card";
199
+ title: string;
200
+ subtitle?: string;
201
+ fields?: ToolRenderField[];
202
+ actions?: ToolRenderAction[];
203
+ };
204
+
205
+ export type ToolRenderResult = {
206
+ text: string;
207
+ blocks?: ToolRenderBlock[];
208
+ };
209
+
210
+ export type ToolRenderContext<Input = unknown> = {
211
+ input: Input;
212
+ runtime: ToolRuntimeContext;
213
+ };
214
+
181
215
  export type ToolRuntimeContext = {
182
216
  environment: "local" | "hosted" | "eval" | "test";
183
- invocation: "chat" | "tool" | "dev" | "channel" | "eval";
217
+ invocation: "chat" | "tool" | "dev" | "channel" | "eval" | "sync";
184
218
  target: "bun" | "cloudflare";
185
219
  database: "sqlite" | "turso" | "none";
186
220
  };
@@ -196,6 +230,13 @@ export type ToolContext = {
196
230
  };
197
231
  };
198
232
 
233
+ export type SyncContext = {
234
+ env: Record<string, string | undefined>;
235
+ database: DatabaseRunner;
236
+ db: DatabaseRunner;
237
+ runtime: ToolRuntimeContext;
238
+ };
239
+
199
240
  export type AgentTool<Input = unknown, Output = unknown> = {
200
241
  name: string;
201
242
  description: string;
@@ -206,6 +247,7 @@ export type AgentTool<Input = unknown, Output = unknown> = {
206
247
  permissions?: string[];
207
248
  timeoutMs?: number;
208
249
  execute(input: Input, ctx: ToolContext): Promise<Output> | Output;
250
+ render?(output: Output, ctx: ToolRenderContext<Input>): string | ToolRenderResult;
209
251
  };
210
252
 
211
253
  export type AgentConfig = {
@@ -338,6 +380,7 @@ export { loadAgentCapsule, findAgentCapsuleRoot } from "./runtime/config";
338
380
  export { runAgentMessage, runAgentMessageFromCwd } from "./runtime/chat";
339
381
  export { listConversationsFromCwd, getConversationFromCwd } from "./runtime/conversations";
340
382
  export { runEvalsFromCwd } from "./runtime/evals";
383
+ export { getConversationTraceFromCwd } from "./runtime/traces";
341
384
  export { runToolFromCwd } from "./runtime/tool-runner";
342
385
  export { startAgentDevServer } from "./runtime/dev-server";
343
386
  export { inspectAgentCapsule } from "./runtime/inspect";
@@ -97,7 +97,7 @@ async function runPiProvider(input: ProviderRunInput): Promise<ProviderRunResult
97
97
  content: [
98
98
  {
99
99
  type: "text",
100
- text: JSON.stringify(result.output),
100
+ text: result.rendered?.text ?? JSON.stringify(result.output),
101
101
  },
102
102
  ],
103
103
  isError: false,
@@ -29,7 +29,7 @@ export const testProviderAdapter: ProviderAdapter = {
29
29
  content:
30
30
  toolResult.visibility === "internal"
31
31
  ? `Tool ${toolResult.name}: completed`
32
- : `Tool ${toolResult.name}: ${JSON.stringify(toolResult.output)}`,
32
+ : toolResult.rendered?.text ?? `Tool ${toolResult.name}: ${JSON.stringify(toolResult.output)}`,
33
33
  },
34
34
  raw: {
35
35
  toolCalls: [toolResult],
@@ -561,6 +561,10 @@ function validateDatabaseConfig(value: unknown): void {
561
561
  if (value.schema !== undefined && typeof value.schema !== "string") {
562
562
  throw invalidConfig("storage.database.schema must be a string when provided.");
563
563
  }
564
+
565
+ if (value.migrations !== undefined && typeof value.migrations !== "string") {
566
+ throw invalidConfig("storage.database.migrations must be a string when provided.");
567
+ }
564
568
  }
565
569
 
566
570
  function validateFilesConfig(value: unknown): void {
@@ -631,6 +635,10 @@ function validateTool(value: unknown, index: number): asserts value is AgentTool
631
635
  if (typeof value.execute !== "function") {
632
636
  throw invalidConfig(`tools[${index}].execute must be a function.`);
633
637
  }
638
+
639
+ if (value.render !== undefined && typeof value.render !== "function") {
640
+ throw invalidConfig(`tools[${index}].render must be a function when provided.`);
641
+ }
634
642
  }
635
643
 
636
644
  function resolvePath(root: string, path: string): string {