@poncho-ai/harness 0.25.0 → 0.27.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/dist/index.js CHANGED
@@ -617,8 +617,10 @@ Response: Server-Sent Events (\`run:started\`, \`model:chunk\`, \`tool:*\`, \`ru
617
617
 
618
618
  On serverless deployments with \`PONCHO_MAX_DURATION\` set, the \`run:completed\` event may
619
619
  include \`continuation: true\` in \`result\`, indicating the agent stopped early due to a
620
- platform timeout and the client should send another message (e.g., \`"Continue"\`) on the
621
- same conversation to resume.
620
+ platform timeout. The server preserves the full internal message chain so the agent
621
+ resumes with complete context. The web UI and client SDK handle continuation automatically
622
+ by re-posting to the same conversation with \`{ continuation: true }\` \u2014 no manual
623
+ "Continue" message is needed.
622
624
 
623
625
  ## Build a custom chat UI
624
626
 
@@ -1288,28 +1290,28 @@ When \`@sparticuz/chromium\` is installed and a serverless environment is detect
1288
1290
 
1289
1291
  ## Subagents
1290
1292
 
1291
- Poncho agents can spawn recursive copies of themselves as **subagents**. Each subagent runs in its own independent conversation with full access to the agent's tools and skills. The parent agent controls the subagent lifecycle and receives results directly.
1293
+ Poncho agents can spawn **subagents** \u2014 independent background tasks that run in their own conversations. Each subagent has full access to the agent's tools and skills. Subagents run asynchronously and their results are delivered back to the parent automatically.
1292
1294
 
1293
1295
  Subagents are useful when an agent needs to parallelize work, delegate a subtask, or isolate a line of investigation without polluting the main conversation context.
1294
1296
 
1295
1297
  ### How it works
1296
1298
 
1297
- When the agent decides to use a subagent, it calls \`spawn_subagent\` with a task description. The subagent runs to completion and the result is returned to the parent \u2014 the call is **blocking**, so the parent waits for the subagent to finish before continuing.
1299
+ When the agent decides to use a subagent, it calls \`spawn_subagent\` with a task description. The tool returns immediately with a subagent ID and \`status: "running"\`. The subagent runs in the background and, when it completes, its result is delivered to the parent conversation as a message \u2014 triggering a callback that lets the parent process or summarize the result.
1298
1300
 
1299
- The parent can also send follow-up messages to existing subagents with \`message_subagent\`, stop a running subagent with \`stop_subagent\`, or list all its subagents with \`list_subagents\`.
1301
+ The agent can spawn multiple subagents in a single response and they run concurrently. The parent can also send follow-up messages to existing subagents with \`message_subagent\`, stop a running subagent with \`stop_subagent\`, or list all its subagents with \`list_subagents\`.
1300
1302
 
1301
1303
  ### Available tools
1302
1304
 
1303
1305
  | Tool | Description |
1304
1306
  |------|-------------|
1305
- | \`spawn_subagent\` | Create a new subagent with a task. Blocks until the subagent completes and returns the result. |
1306
- | \`message_subagent\` | Send a follow-up message to an existing subagent. Blocks until it responds. |
1307
+ | \`spawn_subagent\` | Create a new subagent with a task. Returns immediately; results are delivered asynchronously. |
1308
+ | \`message_subagent\` | Send a follow-up message to an existing subagent. Returns immediately. |
1307
1309
  | \`stop_subagent\` | Stop a running subagent. |
1308
1310
  | \`list_subagents\` | List all subagents for the current conversation with their IDs, tasks, and statuses. |
1309
1311
 
1310
1312
  ### Limits
1311
1313
 
1312
- - **Max depth**: 3 levels of nesting (an agent can spawn a subagent, which can spawn another, but no deeper).
1314
+ - **No nesting**: subagents cannot spawn their own subagents.
1313
1315
  - **Max concurrent**: 5 subagents per parent conversation.
1314
1316
 
1315
1317
  ### Memory isolation
@@ -2030,9 +2032,9 @@ var ponchoDocsTool = defineTool({
2030
2032
 
2031
2033
  // src/harness.ts
2032
2034
  import { randomUUID as randomUUID3 } from "crypto";
2033
- import { readFile as readFile7 } from "fs/promises";
2034
- import { resolve as resolve9 } from "path";
2035
- import { getTextContent as getTextContent3 } from "@poncho-ai/sdk";
2035
+ import { readFile as readFile8 } from "fs/promises";
2036
+ import { resolve as resolve10 } from "path";
2037
+ import { getTextContent as getTextContent2 } from "@poncho-ai/sdk";
2036
2038
 
2037
2039
  // src/upload-store.ts
2038
2040
  import { createHash as createHash2 } from "crypto";
@@ -2346,6 +2348,152 @@ var createUploadStore = async (config, workingDir) => {
2346
2348
  import { mkdir as mkdir3, readFile as readFile5, rename, writeFile as writeFile4 } from "fs/promises";
2347
2349
  import { dirname as dirname2, resolve as resolve6 } from "path";
2348
2350
  import { defineTool as defineTool2 } from "@poncho-ai/sdk";
2351
+
2352
+ // src/kv-store.ts
2353
+ var UpstashKVStore = class {
2354
+ baseUrl;
2355
+ token;
2356
+ constructor(baseUrl, token) {
2357
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
2358
+ this.token = token;
2359
+ }
2360
+ headers() {
2361
+ return { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json" };
2362
+ }
2363
+ async get(key) {
2364
+ const response = await fetch(`${this.baseUrl}/get/${encodeURIComponent(key)}`, {
2365
+ method: "POST",
2366
+ headers: this.headers()
2367
+ });
2368
+ if (!response.ok) return void 0;
2369
+ const payload = await response.json();
2370
+ return payload.result ?? void 0;
2371
+ }
2372
+ async set(key, value) {
2373
+ const response = await fetch(this.baseUrl, {
2374
+ method: "POST",
2375
+ headers: this.headers(),
2376
+ body: JSON.stringify(["SET", key, value])
2377
+ });
2378
+ if (!response.ok) {
2379
+ const text = await response.text().catch(() => "");
2380
+ console.error(`[kv][upstash] SET failed (${response.status}): ${text.slice(0, 200)}`);
2381
+ }
2382
+ }
2383
+ async setWithTtl(key, value, ttl) {
2384
+ const response = await fetch(this.baseUrl, {
2385
+ method: "POST",
2386
+ headers: this.headers(),
2387
+ body: JSON.stringify(["SETEX", key, Math.max(1, ttl), value])
2388
+ });
2389
+ if (!response.ok) {
2390
+ const text = await response.text().catch(() => "");
2391
+ console.error(`[kv][upstash] SETEX failed (${response.status}): ${text.slice(0, 200)}`);
2392
+ }
2393
+ }
2394
+ };
2395
+ var RedisKVStore = class {
2396
+ clientPromise;
2397
+ constructor(url) {
2398
+ this.clientPromise = (async () => {
2399
+ try {
2400
+ const redisModule = await import("redis");
2401
+ const client = redisModule.createClient({ url });
2402
+ await client.connect();
2403
+ return client;
2404
+ } catch {
2405
+ return void 0;
2406
+ }
2407
+ })();
2408
+ }
2409
+ async get(key) {
2410
+ const client = await this.clientPromise;
2411
+ if (!client) throw new Error("Redis unavailable");
2412
+ const value = await client.get(key);
2413
+ return value ?? void 0;
2414
+ }
2415
+ async set(key, value) {
2416
+ const client = await this.clientPromise;
2417
+ if (!client) throw new Error("Redis unavailable");
2418
+ await client.set(key, value);
2419
+ }
2420
+ async setWithTtl(key, value, ttl) {
2421
+ const client = await this.clientPromise;
2422
+ if (!client) throw new Error("Redis unavailable");
2423
+ await client.set(key, value, { EX: Math.max(1, ttl) });
2424
+ }
2425
+ };
2426
+ var DynamoDbKVStore = class {
2427
+ table;
2428
+ clientPromise;
2429
+ constructor(table, region) {
2430
+ this.table = table;
2431
+ this.clientPromise = (async () => {
2432
+ try {
2433
+ const module = await import("@aws-sdk/client-dynamodb");
2434
+ const client = new module.DynamoDBClient({ region });
2435
+ return {
2436
+ send: client.send.bind(client),
2437
+ GetItemCommand: module.GetItemCommand,
2438
+ PutItemCommand: module.PutItemCommand
2439
+ };
2440
+ } catch {
2441
+ return void 0;
2442
+ }
2443
+ })();
2444
+ }
2445
+ async get(key) {
2446
+ const client = await this.clientPromise;
2447
+ if (!client) throw new Error("DynamoDB unavailable");
2448
+ const result = await client.send(
2449
+ new client.GetItemCommand({ TableName: this.table, Key: { runId: { S: key } } })
2450
+ );
2451
+ return result.Item?.value?.S;
2452
+ }
2453
+ async set(key, value) {
2454
+ const client = await this.clientPromise;
2455
+ if (!client) throw new Error("DynamoDB unavailable");
2456
+ await client.send(
2457
+ new client.PutItemCommand({
2458
+ TableName: this.table,
2459
+ Item: { runId: { S: key }, value: { S: value } }
2460
+ })
2461
+ );
2462
+ }
2463
+ async setWithTtl(key, value, ttl) {
2464
+ const client = await this.clientPromise;
2465
+ if (!client) throw new Error("DynamoDB unavailable");
2466
+ const ttlEpoch = Math.floor(Date.now() / 1e3) + Math.max(1, ttl);
2467
+ await client.send(
2468
+ new client.PutItemCommand({
2469
+ TableName: this.table,
2470
+ Item: { runId: { S: key }, value: { S: value }, ttl: { N: String(ttlEpoch) } }
2471
+ })
2472
+ );
2473
+ }
2474
+ };
2475
+ var createRawKVStore = (config) => {
2476
+ const provider = config?.provider ?? "local";
2477
+ if (provider === "upstash") {
2478
+ const urlEnv = config?.urlEnv ?? (process.env.UPSTASH_REDIS_REST_URL ? "UPSTASH_REDIS_REST_URL" : "KV_REST_API_URL");
2479
+ const tokenEnv = config?.tokenEnv ?? (process.env.UPSTASH_REDIS_REST_TOKEN ? "UPSTASH_REDIS_REST_TOKEN" : "KV_REST_API_TOKEN");
2480
+ const url = process.env[urlEnv] ?? "";
2481
+ const token = process.env[tokenEnv] ?? "";
2482
+ if (url && token) return new UpstashKVStore(url, token);
2483
+ }
2484
+ if (provider === "redis") {
2485
+ const urlEnv = config?.urlEnv ?? "REDIS_URL";
2486
+ const url = process.env[urlEnv] ?? "";
2487
+ if (url) return new RedisKVStore(url);
2488
+ }
2489
+ if (provider === "dynamodb") {
2490
+ const table = config?.table ?? process.env.PONCHO_DYNAMODB_TABLE ?? "";
2491
+ if (table) return new DynamoDbKVStore(table, config?.region);
2492
+ }
2493
+ return void 0;
2494
+ };
2495
+
2496
+ // src/memory.ts
2349
2497
  var DEFAULT_MAIN_MEMORY = {
2350
2498
  content: "",
2351
2499
  updatedAt: 0
@@ -2456,19 +2604,21 @@ var FileMainMemoryStore = class {
2456
2604
  return this.mainMemory;
2457
2605
  }
2458
2606
  };
2459
- var KeyValueMainMemoryStoreBase = class {
2607
+ var KVBackedMemoryStore = class {
2608
+ kv;
2609
+ storageKey;
2460
2610
  ttl;
2461
2611
  memoryFallback;
2462
- constructor(ttl) {
2612
+ constructor(kv, storageKey, ttl) {
2613
+ this.kv = kv;
2614
+ this.storageKey = storageKey;
2463
2615
  this.ttl = ttl;
2464
2616
  this.memoryFallback = new InMemoryMemoryStore(ttl);
2465
2617
  }
2466
- async readPayload(key) {
2618
+ async readPayload() {
2467
2619
  try {
2468
- const raw = await this.getRaw(key);
2469
- if (!raw) {
2470
- return { main: { ...DEFAULT_MAIN_MEMORY } };
2471
- }
2620
+ const raw = await this.kv.get(this.storageKey);
2621
+ if (!raw) return { main: { ...DEFAULT_MAIN_MEMORY } };
2472
2622
  const parsed = JSON.parse(raw);
2473
2623
  const content = typeof parsed.main?.content === "string" ? parsed.main.content : "";
2474
2624
  const updatedAt = typeof parsed.main?.updatedAt === "number" ? parsed.main.updatedAt : 0;
@@ -2478,200 +2628,32 @@ var KeyValueMainMemoryStoreBase = class {
2478
2628
  return { main };
2479
2629
  }
2480
2630
  }
2481
- async writePayload(key, payload) {
2631
+ async writePayload(payload) {
2482
2632
  try {
2483
2633
  const serialized = JSON.stringify(payload);
2484
2634
  if (typeof this.ttl === "number") {
2485
- await this.setRawWithTtl(key, serialized, Math.max(1, this.ttl));
2635
+ await this.kv.setWithTtl(this.storageKey, serialized, Math.max(1, this.ttl));
2486
2636
  } else {
2487
- await this.setRaw(key, serialized);
2637
+ await this.kv.set(this.storageKey, serialized);
2488
2638
  }
2489
2639
  } catch {
2490
- await this.memoryFallback.updateMainMemory({
2491
- content: payload.main.content
2492
- });
2640
+ await this.memoryFallback.updateMainMemory({ content: payload.main.content });
2493
2641
  }
2494
2642
  }
2495
2643
  async getMainMemory() {
2496
- const payload = await this.readPayload(this.key());
2644
+ const payload = await this.readPayload();
2497
2645
  return payload.main;
2498
2646
  }
2499
2647
  async updateMainMemory(input) {
2500
- const key = this.key();
2501
- const payload = await this.readPayload(key);
2502
- payload.main = {
2503
- content: input.content.trim(),
2504
- updatedAt: Date.now()
2505
- };
2506
- await this.writePayload(key, payload);
2648
+ const payload = await this.readPayload();
2649
+ payload.main = { content: input.content.trim(), updatedAt: Date.now() };
2650
+ await this.writePayload(payload);
2507
2651
  return payload.main;
2508
2652
  }
2509
2653
  };
2510
- var UpstashMemoryStore = class extends KeyValueMainMemoryStoreBase {
2511
- baseUrl;
2512
- token;
2513
- storageKey;
2514
- constructor(options) {
2515
- super(options.ttl);
2516
- this.baseUrl = options.baseUrl.replace(/\/+$/, "");
2517
- this.token = options.token;
2518
- this.storageKey = options.storageKey;
2519
- }
2520
- key() {
2521
- return this.storageKey;
2522
- }
2523
- headers() {
2524
- return {
2525
- Authorization: `Bearer ${this.token}`,
2526
- "Content-Type": "application/json"
2527
- };
2528
- }
2529
- async getRaw(key) {
2530
- const response = await fetch(`${this.baseUrl}/get/${encodeURIComponent(key)}`, {
2531
- method: "POST",
2532
- headers: this.headers()
2533
- });
2534
- if (!response.ok) {
2535
- return void 0;
2536
- }
2537
- const payload = await response.json();
2538
- return payload.result ?? void 0;
2539
- }
2540
- async setRaw(key, value) {
2541
- await fetch(
2542
- `${this.baseUrl}/set/${encodeURIComponent(key)}/${encodeURIComponent(value)}`,
2543
- { method: "POST", headers: this.headers() }
2544
- );
2545
- }
2546
- async setRawWithTtl(key, value, ttl) {
2547
- await fetch(
2548
- `${this.baseUrl}/setex/${encodeURIComponent(key)}/${Math.max(1, ttl)}/${encodeURIComponent(
2549
- value
2550
- )}`,
2551
- { method: "POST", headers: this.headers() }
2552
- );
2553
- }
2554
- };
2555
- var RedisMemoryStore = class extends KeyValueMainMemoryStoreBase {
2556
- storageKey;
2557
- clientPromise;
2558
- constructor(options) {
2559
- super(options.ttl);
2560
- this.storageKey = options.storageKey;
2561
- this.clientPromise = (async () => {
2562
- try {
2563
- const redisModule = await import("redis");
2564
- const client = redisModule.createClient({ url: options.url });
2565
- await client.connect();
2566
- return client;
2567
- } catch {
2568
- return void 0;
2569
- }
2570
- })();
2571
- }
2572
- key() {
2573
- return this.storageKey;
2574
- }
2575
- async getRaw(key) {
2576
- const client = await this.clientPromise;
2577
- if (!client) {
2578
- throw new Error("Redis unavailable");
2579
- }
2580
- const value = await client.get(key);
2581
- return value ?? void 0;
2582
- }
2583
- async setRaw(key, value) {
2584
- const client = await this.clientPromise;
2585
- if (!client) {
2586
- throw new Error("Redis unavailable");
2587
- }
2588
- await client.set(key, value);
2589
- }
2590
- async setRawWithTtl(key, value, ttl) {
2591
- const client = await this.clientPromise;
2592
- if (!client) {
2593
- throw new Error("Redis unavailable");
2594
- }
2595
- await client.set(key, value, { EX: Math.max(1, ttl) });
2596
- }
2597
- };
2598
- var DynamoDbMemoryStore = class extends KeyValueMainMemoryStoreBase {
2599
- storageKey;
2600
- table;
2601
- clientPromise;
2602
- constructor(options) {
2603
- super(options.ttl);
2604
- this.storageKey = options.storageKey;
2605
- this.table = options.table;
2606
- this.clientPromise = (async () => {
2607
- try {
2608
- const module = await import("@aws-sdk/client-dynamodb");
2609
- const client = new module.DynamoDBClient({ region: options.region });
2610
- return {
2611
- send: client.send.bind(client),
2612
- GetItemCommand: module.GetItemCommand,
2613
- PutItemCommand: module.PutItemCommand
2614
- };
2615
- } catch {
2616
- return void 0;
2617
- }
2618
- })();
2619
- }
2620
- key() {
2621
- return this.storageKey;
2622
- }
2623
- async getRaw(key) {
2624
- const client = await this.clientPromise;
2625
- if (!client) {
2626
- throw new Error("DynamoDB unavailable");
2627
- }
2628
- const result = await client.send(
2629
- new client.GetItemCommand({
2630
- TableName: this.table,
2631
- Key: { runId: { S: key } }
2632
- })
2633
- );
2634
- return result.Item?.value?.S;
2635
- }
2636
- async setRaw(key, value) {
2637
- const client = await this.clientPromise;
2638
- if (!client) {
2639
- throw new Error("DynamoDB unavailable");
2640
- }
2641
- await client.send(
2642
- new client.PutItemCommand({
2643
- TableName: this.table,
2644
- Item: {
2645
- runId: { S: key },
2646
- value: { S: value }
2647
- }
2648
- })
2649
- );
2650
- }
2651
- async setRawWithTtl(key, value, ttl) {
2652
- const client = await this.clientPromise;
2653
- if (!client) {
2654
- throw new Error("DynamoDB unavailable");
2655
- }
2656
- const ttlEpoch = Math.floor(Date.now() / 1e3) + Math.max(1, ttl);
2657
- await client.send(
2658
- new client.PutItemCommand({
2659
- TableName: this.table,
2660
- Item: {
2661
- runId: { S: key },
2662
- value: { S: value },
2663
- ttl: { N: String(ttlEpoch) }
2664
- }
2665
- })
2666
- );
2667
- }
2668
- };
2669
2654
  var createMemoryStore = (agentId, config, options) => {
2670
2655
  const provider = config?.provider ?? "local";
2671
2656
  const ttl = config?.ttl;
2672
- const storageKey = `poncho:${STORAGE_SCHEMA_VERSION}:${slugifyStorageComponent(
2673
- agentId
2674
- )}:memory:main`;
2675
2657
  const workingDir = options?.workingDir ?? process.cwd();
2676
2658
  if (provider === "local") {
2677
2659
  return new FileMainMemoryStore(workingDir, ttl);
@@ -2679,44 +2661,10 @@ var createMemoryStore = (agentId, config, options) => {
2679
2661
  if (provider === "memory") {
2680
2662
  return new InMemoryMemoryStore(ttl);
2681
2663
  }
2682
- if (provider === "upstash") {
2683
- const urlEnv = config?.urlEnv ?? (process.env.UPSTASH_REDIS_REST_URL ? "UPSTASH_REDIS_REST_URL" : "KV_REST_API_URL");
2684
- const tokenEnv = config?.tokenEnv ?? (process.env.UPSTASH_REDIS_REST_TOKEN ? "UPSTASH_REDIS_REST_TOKEN" : "KV_REST_API_TOKEN");
2685
- const url = process.env[urlEnv] ?? "";
2686
- const token = process.env[tokenEnv] ?? "";
2687
- if (url && token) {
2688
- return new UpstashMemoryStore({
2689
- baseUrl: url,
2690
- token,
2691
- storageKey,
2692
- ttl
2693
- });
2694
- }
2695
- return new InMemoryMemoryStore(ttl);
2696
- }
2697
- if (provider === "redis") {
2698
- const urlEnv = config?.urlEnv ?? "REDIS_URL";
2699
- const url = process.env[urlEnv] ?? "";
2700
- if (url) {
2701
- return new RedisMemoryStore({
2702
- url,
2703
- storageKey,
2704
- ttl
2705
- });
2706
- }
2707
- return new InMemoryMemoryStore(ttl);
2708
- }
2709
- if (provider === "dynamodb") {
2710
- const table = config?.table ?? process.env.PONCHO_DYNAMODB_TABLE ?? "";
2711
- if (table) {
2712
- return new DynamoDbMemoryStore({
2713
- table,
2714
- storageKey,
2715
- region: config?.region,
2716
- ttl
2717
- });
2718
- }
2719
- return new InMemoryMemoryStore(ttl);
2664
+ const kv = createRawKVStore(config);
2665
+ if (kv) {
2666
+ const storageKey = `poncho:${STORAGE_SCHEMA_VERSION}:${slugifyStorageComponent(agentId)}:memory:main`;
2667
+ return new KVBackedMemoryStore(kv, storageKey, ttl);
2720
2668
  }
2721
2669
  return new InMemoryMemoryStore(ttl);
2722
2670
  };
@@ -2889,6 +2837,269 @@ ${item.content}`, query)
2889
2837
  ];
2890
2838
  };
2891
2839
 
2840
+ // src/todo-tools.ts
2841
+ import { mkdir as mkdir4, readFile as readFile6, rename as rename2, writeFile as writeFile5 } from "fs/promises";
2842
+ import { dirname as dirname3, resolve as resolve7 } from "path";
2843
+ import { defineTool as defineTool3 } from "@poncho-ai/sdk";
2844
+ var VALID_STATUSES = ["pending", "in_progress", "completed"];
2845
+ var VALID_PRIORITIES = ["high", "medium", "low"];
2846
+ var TODOS_DIRECTORY = "todos";
2847
+ var writeJsonAtomic2 = async (filePath, payload) => {
2848
+ await mkdir4(dirname3(filePath), { recursive: true });
2849
+ const tmpPath = `${filePath}.tmp`;
2850
+ await writeFile5(tmpPath, JSON.stringify(payload, null, 2), "utf8");
2851
+ await rename2(tmpPath, filePath);
2852
+ };
2853
+ var parseTodoList = (raw) => {
2854
+ if (!Array.isArray(raw)) return [];
2855
+ return raw.filter(
2856
+ (item) => typeof item === "object" && item !== null && typeof item.id === "string" && typeof item.content === "string"
2857
+ );
2858
+ };
2859
+ var generateId = () => (globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`).slice(0, 8);
2860
+ var InMemoryTodoStore = class {
2861
+ store = /* @__PURE__ */ new Map();
2862
+ async get(conversationId) {
2863
+ return this.store.get(conversationId) ?? [];
2864
+ }
2865
+ async set(conversationId, todos) {
2866
+ this.store.set(conversationId, todos);
2867
+ }
2868
+ };
2869
+ var FileTodoStore = class {
2870
+ workingDir;
2871
+ todosDir = "";
2872
+ constructor(workingDir) {
2873
+ this.workingDir = workingDir;
2874
+ }
2875
+ async ensureTodosDir() {
2876
+ if (this.todosDir) return this.todosDir;
2877
+ const identity = await ensureAgentIdentity(this.workingDir);
2878
+ this.todosDir = resolve7(getAgentStoreDirectory(identity), TODOS_DIRECTORY);
2879
+ await mkdir4(this.todosDir, { recursive: true });
2880
+ return this.todosDir;
2881
+ }
2882
+ async filePath(conversationId) {
2883
+ const dir = await this.ensureTodosDir();
2884
+ return resolve7(dir, `${slugifyStorageComponent(conversationId)}.json`);
2885
+ }
2886
+ async get(conversationId) {
2887
+ try {
2888
+ const fp = await this.filePath(conversationId);
2889
+ const raw = await readFile6(fp, "utf8");
2890
+ return parseTodoList(JSON.parse(raw));
2891
+ } catch {
2892
+ return [];
2893
+ }
2894
+ }
2895
+ async set(conversationId, todos) {
2896
+ const fp = await this.filePath(conversationId);
2897
+ await writeJsonAtomic2(fp, todos);
2898
+ }
2899
+ };
2900
+ var KVBackedTodoStore = class {
2901
+ kv;
2902
+ baseKey;
2903
+ ttl;
2904
+ memoryFallback = new InMemoryTodoStore();
2905
+ constructor(kv, baseKey, ttl) {
2906
+ this.kv = kv;
2907
+ this.baseKey = baseKey;
2908
+ this.ttl = ttl;
2909
+ }
2910
+ keyFor(conversationId) {
2911
+ return `${this.baseKey}:${slugifyStorageComponent(conversationId)}`;
2912
+ }
2913
+ async get(conversationId) {
2914
+ try {
2915
+ const raw = await this.kv.get(this.keyFor(conversationId));
2916
+ if (!raw) return [];
2917
+ return parseTodoList(JSON.parse(raw));
2918
+ } catch {
2919
+ return this.memoryFallback.get(conversationId);
2920
+ }
2921
+ }
2922
+ async set(conversationId, todos) {
2923
+ try {
2924
+ const serialized = JSON.stringify(todos);
2925
+ const key = this.keyFor(conversationId);
2926
+ if (typeof this.ttl === "number") {
2927
+ await this.kv.setWithTtl(key, serialized, Math.max(1, this.ttl));
2928
+ } else {
2929
+ await this.kv.set(key, serialized);
2930
+ }
2931
+ } catch {
2932
+ await this.memoryFallback.set(conversationId, todos);
2933
+ }
2934
+ }
2935
+ };
2936
+ var createTodoStore = (agentId, config, options) => {
2937
+ const provider = config?.provider ?? "local";
2938
+ const ttl = config?.ttl;
2939
+ const workingDir = options?.workingDir ?? process.cwd();
2940
+ if (provider === "local") {
2941
+ return new FileTodoStore(workingDir);
2942
+ }
2943
+ if (provider === "memory") {
2944
+ return new InMemoryTodoStore();
2945
+ }
2946
+ const kv = createRawKVStore(config);
2947
+ if (kv) {
2948
+ const baseKey = `poncho:${STORAGE_SCHEMA_VERSION}:${slugifyStorageComponent(agentId)}:todos`;
2949
+ return new KVBackedTodoStore(kv, baseKey, ttl);
2950
+ }
2951
+ return new InMemoryTodoStore();
2952
+ };
2953
+ var createTodoTools = (store) => {
2954
+ const resolveKey = (context) => context.conversationId || context.runId;
2955
+ return [
2956
+ defineTool3({
2957
+ name: "todo_list",
2958
+ description: "List all todo items for the current conversation. Use this to check progress and plan next steps.",
2959
+ inputSchema: {
2960
+ type: "object",
2961
+ properties: {
2962
+ status: {
2963
+ type: "string",
2964
+ enum: VALID_STATUSES,
2965
+ description: "Filter by status (omit to list all)"
2966
+ }
2967
+ },
2968
+ additionalProperties: false
2969
+ },
2970
+ handler: async (input, context) => {
2971
+ const key = resolveKey(context);
2972
+ let todos = await store.get(key);
2973
+ const status = typeof input.status === "string" ? input.status : void 0;
2974
+ if (status && VALID_STATUSES.includes(status)) {
2975
+ todos = todos.filter((t) => t.status === status);
2976
+ }
2977
+ return { todos, count: todos.length };
2978
+ }
2979
+ }),
2980
+ defineTool3({
2981
+ name: "todo_add",
2982
+ description: "Add a new todo item for the current conversation. Use proactively for complex multi-step tasks (3+ steps).",
2983
+ inputSchema: {
2984
+ type: "object",
2985
+ properties: {
2986
+ content: {
2987
+ type: "string",
2988
+ description: "Description of the task"
2989
+ },
2990
+ status: {
2991
+ type: "string",
2992
+ enum: VALID_STATUSES,
2993
+ description: "Initial status (default: pending)"
2994
+ },
2995
+ priority: {
2996
+ type: "string",
2997
+ enum: VALID_PRIORITIES,
2998
+ description: "Priority level (default: medium)"
2999
+ }
3000
+ },
3001
+ required: ["content"],
3002
+ additionalProperties: false
3003
+ },
3004
+ handler: async (input, context) => {
3005
+ const content = typeof input.content === "string" ? input.content.trim() : "";
3006
+ if (!content) throw new Error("content is required");
3007
+ const status = typeof input.status === "string" && VALID_STATUSES.includes(input.status) ? input.status : "pending";
3008
+ const priority = typeof input.priority === "string" && VALID_PRIORITIES.includes(input.priority) ? input.priority : "medium";
3009
+ const now2 = Date.now();
3010
+ const todo = {
3011
+ id: generateId(),
3012
+ content,
3013
+ status,
3014
+ priority,
3015
+ createdAt: now2,
3016
+ updatedAt: now2
3017
+ };
3018
+ const key = resolveKey(context);
3019
+ const todos = await store.get(key);
3020
+ todos.push(todo);
3021
+ await store.set(key, todos);
3022
+ return { todo, todos };
3023
+ }
3024
+ }),
3025
+ defineTool3({
3026
+ name: "todo_update",
3027
+ description: "Update an existing todo item's status, content, or priority. Mark tasks in_progress when starting and completed when done.",
3028
+ inputSchema: {
3029
+ type: "object",
3030
+ properties: {
3031
+ id: {
3032
+ type: "string",
3033
+ description: "ID of the todo to update"
3034
+ },
3035
+ status: {
3036
+ type: "string",
3037
+ enum: VALID_STATUSES,
3038
+ description: "New status"
3039
+ },
3040
+ content: {
3041
+ type: "string",
3042
+ description: "New content/description"
3043
+ },
3044
+ priority: {
3045
+ type: "string",
3046
+ enum: VALID_PRIORITIES,
3047
+ description: "New priority level"
3048
+ }
3049
+ },
3050
+ required: ["id"],
3051
+ additionalProperties: false
3052
+ },
3053
+ handler: async (input, context) => {
3054
+ const id = typeof input.id === "string" ? input.id : "";
3055
+ if (!id) throw new Error("id is required");
3056
+ const key = resolveKey(context);
3057
+ const todos = await store.get(key);
3058
+ const todo = todos.find((t) => t.id === id);
3059
+ if (!todo) throw new Error(`Todo with id "${id}" not found`);
3060
+ if (typeof input.status === "string" && VALID_STATUSES.includes(input.status)) {
3061
+ todo.status = input.status;
3062
+ }
3063
+ if (typeof input.content === "string" && input.content.trim()) {
3064
+ todo.content = input.content.trim();
3065
+ }
3066
+ if (typeof input.priority === "string" && VALID_PRIORITIES.includes(input.priority)) {
3067
+ todo.priority = input.priority;
3068
+ }
3069
+ todo.updatedAt = Date.now();
3070
+ await store.set(key, todos);
3071
+ return { todo, todos };
3072
+ }
3073
+ }),
3074
+ defineTool3({
3075
+ name: "todo_remove",
3076
+ description: "Remove a todo item by ID.",
3077
+ inputSchema: {
3078
+ type: "object",
3079
+ properties: {
3080
+ id: {
3081
+ type: "string",
3082
+ description: "ID of the todo to remove"
3083
+ }
3084
+ },
3085
+ required: ["id"],
3086
+ additionalProperties: false
3087
+ },
3088
+ handler: async (input, context) => {
3089
+ const id = typeof input.id === "string" ? input.id : "";
3090
+ if (!id) throw new Error("id is required");
3091
+ const key = resolveKey(context);
3092
+ const todos = await store.get(key);
3093
+ const index = todos.findIndex((t) => t.id === id);
3094
+ if (index === -1) throw new Error(`Todo with id "${id}" not found`);
3095
+ const [removed] = todos.splice(index, 1);
3096
+ await store.set(key, todos);
3097
+ return { removed, todos };
3098
+ }
3099
+ })
3100
+ ];
3101
+ };
3102
+
2892
3103
  // src/mcp.ts
2893
3104
  var McpHttpError = class extends Error {
2894
3105
  status;
@@ -3383,8 +3594,8 @@ var createModelProvider = (provider, config) => {
3383
3594
  };
3384
3595
 
3385
3596
  // src/skill-context.ts
3386
- import { readFile as readFile6, readdir as readdir2, stat } from "fs/promises";
3387
- import { dirname as dirname3, resolve as resolve7, normalize } from "path";
3597
+ import { readFile as readFile7, readdir as readdir2, stat } from "fs/promises";
3598
+ import { dirname as dirname4, resolve as resolve8, normalize } from "path";
3388
3599
  import YAML3 from "yaml";
3389
3600
  var DEFAULT_SKILL_DIRS = ["skills"];
3390
3601
  var resolveSkillDirs = (workingDir, extraPaths) => {
@@ -3396,7 +3607,7 @@ var resolveSkillDirs = (workingDir, extraPaths) => {
3396
3607
  }
3397
3608
  }
3398
3609
  }
3399
- return dirs.map((d) => resolve7(workingDir, d));
3610
+ return dirs.map((d) => resolve8(workingDir, d));
3400
3611
  };
3401
3612
  var FRONTMATTER_PATTERN3 = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
3402
3613
  var asRecord2 = (value) => typeof value === "object" && value !== null ? value : {};
@@ -3465,7 +3676,7 @@ var collectSkillManifests = async (directory) => {
3465
3676
  const entries = await readdir2(directory, { withFileTypes: true });
3466
3677
  const files = [];
3467
3678
  for (const entry of entries) {
3468
- const fullPath = resolve7(directory, entry.name);
3679
+ const fullPath = resolve8(directory, entry.name);
3469
3680
  let isDir = entry.isDirectory();
3470
3681
  let isFile = entry.isFile();
3471
3682
  if (entry.isSymbolicLink()) {
@@ -3500,13 +3711,13 @@ var loadSkillMetadata = async (workingDir, extraSkillPaths) => {
3500
3711
  const seen = /* @__PURE__ */ new Set();
3501
3712
  for (const manifest of allManifests) {
3502
3713
  try {
3503
- const content = await readFile6(manifest, "utf8");
3714
+ const content = await readFile7(manifest, "utf8");
3504
3715
  const parsed = parseSkillFrontmatter(content);
3505
3716
  if (parsed && !seen.has(parsed.name)) {
3506
3717
  seen.add(parsed.name);
3507
3718
  skills.push({
3508
3719
  ...parsed,
3509
- skillDir: dirname3(manifest),
3720
+ skillDir: dirname4(manifest),
3510
3721
  skillPath: manifest
3511
3722
  });
3512
3723
  }
@@ -3545,7 +3756,7 @@ ${xmlSkills}
3545
3756
  };
3546
3757
  var escapeXml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
3547
3758
  var loadSkillInstructions = async (skill) => {
3548
- const content = await readFile6(skill.skillPath, "utf8");
3759
+ const content = await readFile7(skill.skillPath, "utf8");
3549
3760
  const match = content.match(FRONTMATTER_PATTERN3);
3550
3761
  return match ? match[2].trim() : content.trim();
3551
3762
  };
@@ -3554,11 +3765,11 @@ var readSkillResource = async (skill, relativePath) => {
3554
3765
  if (normalized.startsWith("..") || normalized.startsWith("/")) {
3555
3766
  throw new Error("Path must be relative and within the skill directory");
3556
3767
  }
3557
- const fullPath = resolve7(skill.skillDir, normalized);
3768
+ const fullPath = resolve8(skill.skillDir, normalized);
3558
3769
  if (!fullPath.startsWith(skill.skillDir)) {
3559
3770
  throw new Error("Path escapes the skill directory");
3560
3771
  }
3561
- return await readFile6(fullPath, "utf8");
3772
+ return await readFile7(fullPath, "utf8");
3562
3773
  };
3563
3774
  var MAX_INSTRUCTIONS_PER_SKILL = 1200;
3564
3775
  var loadSkillContext = async (workingDir) => {
@@ -3675,16 +3886,16 @@ function convertSchema(schema) {
3675
3886
  }
3676
3887
 
3677
3888
  // src/skill-tools.ts
3678
- import { defineTool as defineTool3 } from "@poncho-ai/sdk";
3889
+ import { defineTool as defineTool4 } from "@poncho-ai/sdk";
3679
3890
  import { access as access2, readdir as readdir3, stat as stat2 } from "fs/promises";
3680
- import { extname, normalize as normalize2, resolve as resolve8, sep as sep2 } from "path";
3891
+ import { extname, normalize as normalize2, resolve as resolve9, sep as sep2 } from "path";
3681
3892
  import { pathToFileURL } from "url";
3682
3893
  import { createJiti as createJiti2 } from "jiti";
3683
3894
  var createSkillTools = (skills, options) => {
3684
3895
  const skillsByName = new Map(skills.map((skill) => [skill.name, skill]));
3685
3896
  const knownNames = skills.length > 0 ? skills.map((skill) => skill.name).join(", ") : "(none)";
3686
3897
  return [
3687
- defineTool3({
3898
+ defineTool4({
3688
3899
  name: "activate_skill",
3689
3900
  description: `Load the full instructions for an available skill. Use this when a user's request matches a skill's description. Available skills: ${knownNames}`,
3690
3901
  inputSchema: {
@@ -3721,7 +3932,7 @@ var createSkillTools = (skills, options) => {
3721
3932
  }
3722
3933
  }
3723
3934
  }),
3724
- defineTool3({
3935
+ defineTool4({
3725
3936
  name: "deactivate_skill",
3726
3937
  description: "Deactivate a previously activated skill and update scoped tool availability.",
3727
3938
  inputSchema: {
@@ -3750,7 +3961,7 @@ var createSkillTools = (skills, options) => {
3750
3961
  }
3751
3962
  }
3752
3963
  }),
3753
- defineTool3({
3964
+ defineTool4({
3754
3965
  name: "list_active_skills",
3755
3966
  description: "List currently active skills with scoped MCP tools.",
3756
3967
  inputSchema: {
@@ -3762,7 +3973,7 @@ var createSkillTools = (skills, options) => {
3762
3973
  activeSkills: options?.onListActiveSkills ? options.onListActiveSkills() : []
3763
3974
  })
3764
3975
  }),
3765
- defineTool3({
3976
+ defineTool4({
3766
3977
  name: "read_skill_resource",
3767
3978
  description: `Read a file from a skill's directory (references, scripts, assets). Use relative paths from the skill root. Available skills: ${knownNames}`,
3768
3979
  inputSchema: {
@@ -3802,7 +4013,7 @@ var createSkillTools = (skills, options) => {
3802
4013
  }
3803
4014
  }
3804
4015
  }),
3805
- defineTool3({
4016
+ defineTool4({
3806
4017
  name: "list_skill_scripts",
3807
4018
  description: `List JavaScript/TypeScript script files available under a skill directory (recursive). Available skills: ${knownNames}`,
3808
4019
  inputSchema: {
@@ -3837,7 +4048,7 @@ var createSkillTools = (skills, options) => {
3837
4048
  }
3838
4049
  }
3839
4050
  }),
3840
- defineTool3({
4051
+ defineTool4({
3841
4052
  name: "run_skill_script",
3842
4053
  description: `Run a JavaScript/TypeScript module in a skill or project directory. Uses default export function or named run/main/handler function. Available skills: ${knownNames}`,
3843
4054
  inputSchema: {
@@ -3934,7 +4145,7 @@ var collectScriptFiles = async (directory) => {
3934
4145
  if (entry.name === "node_modules") {
3935
4146
  continue;
3936
4147
  }
3937
- const fullPath = resolve8(directory, entry.name);
4148
+ const fullPath = resolve9(directory, entry.name);
3938
4149
  let isDir = entry.isDirectory();
3939
4150
  let isFile = entry.isFile();
3940
4151
  if (entry.isSymbolicLink()) {
@@ -3973,8 +4184,8 @@ var normalizeScriptPolicyPath = (relativePath) => {
3973
4184
  };
3974
4185
  var resolveScriptPath = (baseDir, relativePath) => {
3975
4186
  const normalized = normalizeScriptPolicyPath(relativePath);
3976
- const fullPath = resolve8(baseDir, normalized);
3977
- if (!fullPath.startsWith(`${resolve8(baseDir)}${sep2}`) && fullPath !== resolve8(baseDir)) {
4187
+ const fullPath = resolve9(baseDir, normalized);
4188
+ if (!fullPath.startsWith(`${resolve9(baseDir)}${sep2}`) && fullPath !== resolve9(baseDir)) {
3978
4189
  throw new Error("Script path must stay inside the allowed directory");
3979
4190
  }
3980
4191
  const extension = extname(fullPath).toLowerCase();
@@ -4048,36 +4259,11 @@ var extractRunnableFunction = (value) => {
4048
4259
  };
4049
4260
 
4050
4261
  // src/subagent-tools.ts
4051
- import { defineTool as defineTool4, getTextContent as getTextContent2 } from "@poncho-ai/sdk";
4052
- var LAST_MESSAGES_TO_RETURN = 10;
4053
- var summarizeResult = (r) => {
4054
- const summary = {
4055
- subagentId: r.subagentId,
4056
- status: r.status
4057
- };
4058
- if (r.result) {
4059
- summary.result = {
4060
- status: r.result.status,
4061
- response: r.result.response,
4062
- steps: r.result.steps,
4063
- duration: r.result.duration
4064
- };
4065
- }
4066
- if (r.error) {
4067
- summary.error = r.error;
4068
- }
4069
- if (r.latestMessages && r.latestMessages.length > 0) {
4070
- summary.latestMessages = r.latestMessages.slice(-LAST_MESSAGES_TO_RETURN).map((m) => ({
4071
- role: m.role,
4072
- content: getTextContent2(m).slice(0, 2e3)
4073
- }));
4074
- }
4075
- return summary;
4076
- };
4077
- var createSubagentTools = (manager, getConversationId, getOwnerId) => [
4078
- defineTool4({
4262
+ import { defineTool as defineTool5 } from "@poncho-ai/sdk";
4263
+ var createSubagentTools = (manager) => [
4264
+ defineTool5({
4079
4265
  name: "spawn_subagent",
4080
- description: "Spawn a subagent to work on a task and wait for it to finish. The subagent is a full copy of yourself running in its own conversation context with access to the same tools (except memory writes). This call blocks until the subagent completes and returns its result.\n\nGuidelines:\n- Use subagents to parallelize work: call spawn_subagent multiple times in one response for independent sub-tasks -- they run concurrently.\n- Prefer doing work yourself for simple or quick tasks. Spawn subagents for substantial, self-contained work.\n- The subagent has no memory of your conversation -- write thorough, self-contained instructions in the task.",
4266
+ description: "Spawn a subagent to work on a task in the background. Returns immediately with a subagent ID. The subagent runs independently and its result will be delivered to you as a message in the conversation when it completes.\n\nGuidelines:\n- Spawn all needed subagents in a SINGLE response (they run concurrently), then end your turn with a brief message to the user.\n- Do NOT spawn more subagents in follow-up steps. Wait for results to be delivered before deciding if more work is needed.\n- Prefer doing work yourself for simple or quick tasks. Spawn subagents for substantial, self-contained work.\n- The subagent has no memory of your conversation -- write thorough, self-contained instructions in the task.",
4081
4267
  inputSchema: {
4082
4268
  type: "object",
4083
4269
  properties: {
@@ -4089,26 +4275,27 @@ var createSubagentTools = (manager, getConversationId, getOwnerId) => [
4089
4275
  required: ["task"],
4090
4276
  additionalProperties: false
4091
4277
  },
4092
- handler: async (input) => {
4278
+ handler: async (input, context) => {
4093
4279
  const task = typeof input.task === "string" ? input.task : "";
4094
4280
  if (!task.trim()) {
4095
4281
  return { error: "task is required" };
4096
4282
  }
4097
- const conversationId = getConversationId();
4283
+ const conversationId = context.conversationId;
4098
4284
  if (!conversationId) {
4099
4285
  return { error: "no active conversation to spawn subagent from" };
4100
4286
  }
4101
- const result = await manager.spawn({
4287
+ const ownerId = typeof context.parameters.__ownerId === "string" ? context.parameters.__ownerId : "anonymous";
4288
+ const { subagentId } = await manager.spawn({
4102
4289
  task: task.trim(),
4103
4290
  parentConversationId: conversationId,
4104
- ownerId: getOwnerId()
4291
+ ownerId
4105
4292
  });
4106
- return summarizeResult(result);
4293
+ return { subagentId, status: "running" };
4107
4294
  }
4108
4295
  }),
4109
- defineTool4({
4296
+ defineTool5({
4110
4297
  name: "message_subagent",
4111
- description: "Send a follow-up message to a completed or stopped subagent and wait for it to finish. This restarts the subagent with the new message and blocks until it completes. Only works when the subagent is not currently running.",
4298
+ description: "Send a follow-up message to a completed or stopped subagent. The subagent restarts in the background and its result will be delivered to you as a message when it completes. Only works when the subagent is not currently running.",
4112
4299
  inputSchema: {
4113
4300
  type: "object",
4114
4301
  properties: {
@@ -4130,11 +4317,11 @@ var createSubagentTools = (manager, getConversationId, getOwnerId) => [
4130
4317
  if (!subagentId || !message.trim()) {
4131
4318
  return { error: "subagent_id and message are required" };
4132
4319
  }
4133
- const result = await manager.sendMessage(subagentId, message.trim());
4134
- return summarizeResult(result);
4320
+ const { subagentId: id } = await manager.sendMessage(subagentId, message.trim());
4321
+ return { subagentId: id, status: "running" };
4135
4322
  }
4136
4323
  }),
4137
- defineTool4({
4324
+ defineTool5({
4138
4325
  name: "stop_subagent",
4139
4326
  description: "Stop a running subagent. The subagent's conversation is preserved but it will stop processing. Use this to cancel work that is no longer needed.",
4140
4327
  inputSchema: {
@@ -4157,7 +4344,7 @@ var createSubagentTools = (manager, getConversationId, getOwnerId) => [
4157
4344
  return { message: `Subagent "${subagentId}" has been stopped.` };
4158
4345
  }
4159
4346
  }),
4160
- defineTool4({
4347
+ defineTool5({
4161
4348
  name: "list_subagents",
4162
4349
  description: "List all subagents that have been spawned in this conversation. Returns each subagent's ID, original task, current status, and message count. Use this to look up subagent IDs before calling message_subagent or stop_subagent.",
4163
4350
  inputSchema: {
@@ -4165,8 +4352,8 @@ var createSubagentTools = (manager, getConversationId, getOwnerId) => [
4165
4352
  properties: {},
4166
4353
  additionalProperties: false
4167
4354
  },
4168
- handler: async () => {
4169
- const conversationId = getConversationId();
4355
+ handler: async (_input, context) => {
4356
+ const conversationId = context.conversationId;
4170
4357
  if (!conversationId) {
4171
4358
  return { error: "no active conversation" };
4172
4359
  }
@@ -4705,6 +4892,7 @@ var AgentHarness = class _AgentHarness {
4705
4892
  uploadStore;
4706
4893
  skillContextWindow = "";
4707
4894
  memoryStore;
4895
+ todoStore;
4708
4896
  loadedConfig;
4709
4897
  loadedSkills = [];
4710
4898
  skillFingerprint = "";
@@ -4761,11 +4949,7 @@ var AgentHarness = class _AgentHarness {
4761
4949
  setSubagentManager(manager) {
4762
4950
  this.subagentManager = manager;
4763
4951
  this.dispatcher.registerMany(
4764
- createSubagentTools(
4765
- manager,
4766
- () => this._currentRunConversationId,
4767
- () => this._currentRunOwnerId ?? "anonymous"
4768
- )
4952
+ createSubagentTools(manager)
4769
4953
  );
4770
4954
  }
4771
4955
  registerConfiguredBuiltInTools(config) {
@@ -4813,6 +4997,10 @@ var AgentHarness = class _AgentHarness {
4813
4997
  get frontmatter() {
4814
4998
  return this.parsedAgent?.frontmatter;
4815
4999
  }
5000
+ async getTodos(conversationId) {
5001
+ if (!this.todoStore) return [];
5002
+ return this.todoStore.get(conversationId);
5003
+ }
4816
5004
  listActiveSkills() {
4817
5005
  return [...this.activeSkillNames].sort();
4818
5006
  }
@@ -5008,8 +5196,8 @@ var AgentHarness = class _AgentHarness {
5008
5196
  return false;
5009
5197
  }
5010
5198
  try {
5011
- const agentFilePath = resolve9(this.workingDir, "AGENT.md");
5012
- const rawContent = await readFile7(agentFilePath, "utf8");
5199
+ const agentFilePath = resolve10(this.workingDir, "AGENT.md");
5200
+ const rawContent = await readFile8(agentFilePath, "utf8");
5013
5201
  if (rawContent === this.agentFileFingerprint) {
5014
5202
  return false;
5015
5203
  }
@@ -5078,8 +5266,8 @@ var AgentHarness = class _AgentHarness {
5078
5266
  }
5079
5267
  }
5080
5268
  async initialize() {
5081
- const agentFilePath = resolve9(this.workingDir, "AGENT.md");
5082
- const agentRawContent = await readFile7(agentFilePath, "utf8");
5269
+ const agentFilePath = resolve10(this.workingDir, "AGENT.md");
5270
+ const agentRawContent = await readFile8(agentFilePath, "utf8");
5083
5271
  this.parsedAgent = parseAgentMarkdown(agentRawContent);
5084
5272
  this.agentFileFingerprint = agentRawContent;
5085
5273
  const identity = await ensureAgentIdentity(this.workingDir);
@@ -5102,8 +5290,8 @@ var AgentHarness = class _AgentHarness {
5102
5290
  this.skillContextWindow = buildSkillContextWindow(skillMetadata);
5103
5291
  this.skillFingerprint = this.buildSkillFingerprint(skillMetadata);
5104
5292
  this.registerSkillTools(skillMetadata);
5293
+ const agentId = this.parsedAgent.frontmatter.id ?? this.parsedAgent.frontmatter.name;
5105
5294
  if (memoryConfig?.enabled) {
5106
- const agentId = this.parsedAgent.frontmatter.id ?? this.parsedAgent.frontmatter.name;
5107
5295
  this.memoryStore = createMemoryStore(
5108
5296
  agentId,
5109
5297
  memoryConfig,
@@ -5115,6 +5303,13 @@ var AgentHarness = class _AgentHarness {
5115
5303
  })
5116
5304
  );
5117
5305
  }
5306
+ const stateConfig = resolveStateConfig(config);
5307
+ this.todoStore = createTodoStore(agentId, stateConfig, { workingDir: this.workingDir });
5308
+ for (const tool of createTodoTools(this.todoStore)) {
5309
+ if (this.isToolEnabled(tool.name)) {
5310
+ this.registerIfMissing(tool);
5311
+ }
5312
+ }
5118
5313
  if (config?.browser) {
5119
5314
  await this.initBrowserTools(config).catch((e) => {
5120
5315
  console.warn(
@@ -5154,14 +5349,14 @@ var AgentHarness = class _AgentHarness {
5154
5349
  const filePath = pathResolve(stateDir, `${sessionId}.json`);
5155
5350
  return {
5156
5351
  async save(json) {
5157
- const { mkdir: mkdir5, writeFile: writeFile6 } = await import("fs/promises");
5158
- await mkdir5(stateDir, { recursive: true });
5159
- await writeFile6(filePath, json, "utf8");
5352
+ const { mkdir: mkdir6, writeFile: writeFile7 } = await import("fs/promises");
5353
+ await mkdir6(stateDir, { recursive: true });
5354
+ await writeFile7(filePath, json, "utf8");
5160
5355
  },
5161
5356
  async load() {
5162
- const { readFile: readFile9 } = await import("fs/promises");
5357
+ const { readFile: readFile10 } = await import("fs/promises");
5163
5358
  try {
5164
- return await readFile9(filePath, "utf8");
5359
+ return await readFile10(filePath, "utf8");
5165
5360
  } catch {
5166
5361
  return void 0;
5167
5362
  }
@@ -5227,7 +5422,7 @@ var AgentHarness = class _AgentHarness {
5227
5422
  let browserMod;
5228
5423
  try {
5229
5424
  const { existsSync } = await import("fs");
5230
- const { join, dirname: dirname5 } = await import("path");
5425
+ const { join, dirname: dirname6 } = await import("path");
5231
5426
  const { pathToFileURL: pathToFileURL2 } = await import("url");
5232
5427
  let searchDir = this.workingDir;
5233
5428
  let entryPath;
@@ -5237,7 +5432,7 @@ var AgentHarness = class _AgentHarness {
5237
5432
  entryPath = candidate;
5238
5433
  break;
5239
5434
  }
5240
- const parent = dirname5(searchDir);
5435
+ const parent = dirname6(searchDir);
5241
5436
  if (parent === searchDir) break;
5242
5437
  searchDir = parent;
5243
5438
  }
@@ -5260,8 +5455,7 @@ var AgentHarness = class _AgentHarness {
5260
5455
  const session = new browserMod.BrowserSession(sessionId, browserCfg);
5261
5456
  this._browserSession = session;
5262
5457
  const tools = browserMod.createBrowserTools(
5263
- () => session,
5264
- () => this._currentRunConversationId ?? "__default__"
5458
+ () => session
5265
5459
  );
5266
5460
  for (const tool of tools) {
5267
5461
  if (this.isToolEnabled(tool.name)) {
@@ -5269,10 +5463,6 @@ var AgentHarness = class _AgentHarness {
5269
5463
  }
5270
5464
  }
5271
5465
  }
5272
- /** Conversation ID of the currently executing run (set during run, cleared after). */
5273
- _currentRunConversationId;
5274
- /** Owner ID of the currently executing run (used by subagent tools). */
5275
- _currentRunOwnerId;
5276
5466
  get browserSession() {
5277
5467
  return this._browserSession;
5278
5468
  }
@@ -5325,9 +5515,9 @@ var AgentHarness = class _AgentHarness {
5325
5515
  for await (const event of this.run(input)) {
5326
5516
  eventQueue.push(event);
5327
5517
  if (queueResolve) {
5328
- const resolve11 = queueResolve;
5518
+ const resolve12 = queueResolve;
5329
5519
  queueResolve = null;
5330
- resolve11();
5520
+ resolve12();
5331
5521
  }
5332
5522
  }
5333
5523
  } catch (error) {
@@ -5346,8 +5536,8 @@ var AgentHarness = class _AgentHarness {
5346
5536
  if (eventQueue.length > 0) {
5347
5537
  yield eventQueue.shift();
5348
5538
  } else if (!generatorDone) {
5349
- await new Promise((resolve11) => {
5350
- queueResolve = resolve11;
5539
+ await new Promise((resolve12) => {
5540
+ queueResolve = resolve12;
5351
5541
  });
5352
5542
  }
5353
5543
  }
@@ -5387,11 +5577,6 @@ var AgentHarness = class _AgentHarness {
5387
5577
  const memoryPromise = this.memoryStore ? this.memoryStore.getMainMemory() : void 0;
5388
5578
  await this.refreshAgentIfChanged();
5389
5579
  await this.refreshSkillsIfChanged();
5390
- this._currentRunConversationId = input.conversationId;
5391
- const ownerParam = input.parameters?.__ownerId;
5392
- if (typeof ownerParam === "string") {
5393
- this._currentRunOwnerId = ownerParam;
5394
- }
5395
5580
  let agent = this.parsedAgent;
5396
5581
  const runId = `run_${randomUUID3()}`;
5397
5582
  const start = now();
@@ -5399,7 +5584,7 @@ var AgentHarness = class _AgentHarness {
5399
5584
  const configuredTimeout = agent.frontmatter.limits?.timeout;
5400
5585
  const timeoutMs = this.environment === "development" && configuredTimeout == null ? 0 : (configuredTimeout ?? 300) * 1e3;
5401
5586
  const platformMaxDurationSec = Number(process.env.PONCHO_MAX_DURATION) || 0;
5402
- const softDeadlineMs = platformMaxDurationSec > 0 ? platformMaxDurationSec * 800 : 0;
5587
+ const softDeadlineMs = input.disableSoftDeadline || platformMaxDurationSec <= 0 ? 0 : platformMaxDurationSec * 800;
5403
5588
  const messages = [...input.messages ?? []];
5404
5589
  const inputMessageCount = messages.length;
5405
5590
  const events = [];
@@ -5533,6 +5718,15 @@ ${this.skillFingerprint}`;
5533
5718
  metadata: { timestamp: now(), id: randomUUID3() }
5534
5719
  });
5535
5720
  }
5721
+ } else {
5722
+ const lastMsg = messages[messages.length - 1];
5723
+ if (lastMsg && lastMsg.role !== "user") {
5724
+ messages.push({
5725
+ role: "user",
5726
+ content: "[System: Your previous turn was interrupted by a time limit. Continue from where you left off \u2014 do NOT repeat what you already said. Proceed directly with the next action or tool call.]",
5727
+ metadata: { timestamp: now(), id: randomUUID3() }
5728
+ });
5729
+ }
5536
5730
  }
5537
5731
  let responseText = "";
5538
5732
  let totalInputTokens = 0;
@@ -5567,6 +5761,7 @@ ${this.skillFingerprint}`;
5567
5761
  tokens: { input: totalInputTokens, output: totalOutputTokens, cached: totalCachedTokens },
5568
5762
  duration: now() - start,
5569
5763
  continuation: true,
5764
+ continuationMessages: [...messages],
5570
5765
  maxSteps
5571
5766
  };
5572
5767
  yield pushEvent({ type: "run:completed", runId, result: result2 });
@@ -5600,7 +5795,7 @@ ${this.skillFingerprint}`;
5600
5795
  if (rich && rich.length > 0) {
5601
5796
  return [{ role: "tool", content: rich }];
5602
5797
  }
5603
- const textContent = typeof msg.content === "string" ? msg.content : getTextContent3(msg);
5798
+ const textContent = typeof msg.content === "string" ? msg.content : getTextContent2(msg);
5604
5799
  try {
5605
5800
  const parsed = JSON.parse(textContent);
5606
5801
  if (!Array.isArray(parsed)) {
@@ -5650,7 +5845,7 @@ ${this.skillFingerprint}`;
5650
5845
  }
5651
5846
  }
5652
5847
  if (msg.role === "assistant") {
5653
- const assistantText = typeof msg.content === "string" ? msg.content : getTextContent3(msg);
5848
+ const assistantText = typeof msg.content === "string" ? msg.content : getTextContent2(msg);
5654
5849
  try {
5655
5850
  const parsed = JSON.parse(assistantText);
5656
5851
  if (typeof parsed === "object" && parsed !== null) {
@@ -5684,12 +5879,15 @@ ${this.skillFingerprint}`;
5684
5879
  }
5685
5880
  } catch {
5686
5881
  }
5882
+ if (!assistantText || assistantText.trim().length === 0) {
5883
+ return [];
5884
+ }
5687
5885
  return [{ role: "assistant", content: assistantText }];
5688
5886
  }
5689
5887
  if (msg.role === "system") {
5690
5888
  return [{
5691
5889
  role: "system",
5692
- content: typeof msg.content === "string" ? msg.content : getTextContent3(msg)
5890
+ content: typeof msg.content === "string" ? msg.content : getTextContent2(msg)
5693
5891
  }];
5694
5892
  }
5695
5893
  if (msg.role === "user") {
@@ -5881,8 +6079,8 @@ ${textContent}` };
5881
6079
  let timer;
5882
6080
  nextPart = await Promise.race([
5883
6081
  fullStreamIterator.next(),
5884
- new Promise((resolve11) => {
5885
- timer = setTimeout(() => resolve11(null), timeout);
6082
+ new Promise((resolve12) => {
6083
+ timer = setTimeout(() => resolve12(null), timeout);
5886
6084
  })
5887
6085
  ]);
5888
6086
  clearTimeout(timer);
@@ -6243,6 +6441,7 @@ ${this.skillFingerprint}`;
6243
6441
  tokens: { input: totalInputTokens, output: totalOutputTokens, cached: totalCachedTokens },
6244
6442
  duration: now() - start,
6245
6443
  continuation: true,
6444
+ continuationMessages: [...messages],
6246
6445
  maxSteps
6247
6446
  };
6248
6447
  yield pushEvent({ type: "run:completed", runId, result });
@@ -6391,8 +6590,8 @@ var LatitudeCapture = class {
6391
6590
 
6392
6591
  // src/state.ts
6393
6592
  import { randomUUID as randomUUID4 } from "crypto";
6394
- import { mkdir as mkdir4, readFile as readFile8, readdir as readdir4, rename as rename2, rm as rm3, writeFile as writeFile5 } from "fs/promises";
6395
- import { dirname as dirname4, resolve as resolve10 } from "path";
6593
+ import { mkdir as mkdir5, readFile as readFile9, readdir as readdir4, rename as rename3, rm as rm3, writeFile as writeFile6 } from "fs/promises";
6594
+ import { dirname as dirname5, resolve as resolve11 } from "path";
6396
6595
  var DEFAULT_OWNER = "local-owner";
6397
6596
  var LOCAL_STATE_FILE = "state.json";
6398
6597
  var CONVERSATIONS_DIRECTORY = "conversations";
@@ -6407,11 +6606,11 @@ var toStoreIdentity = async ({
6407
6606
  }
6408
6607
  return { name: ensured.name, id: agentId };
6409
6608
  };
6410
- var writeJsonAtomic2 = async (filePath, payload) => {
6411
- await mkdir4(dirname4(filePath), { recursive: true });
6609
+ var writeJsonAtomic3 = async (filePath, payload) => {
6610
+ await mkdir5(dirname5(filePath), { recursive: true });
6412
6611
  const tmpPath = `${filePath}.tmp`;
6413
- await writeFile5(tmpPath, JSON.stringify(payload, null, 2), "utf8");
6414
- await rename2(tmpPath, filePath);
6612
+ await writeFile6(tmpPath, JSON.stringify(payload, null, 2), "utf8");
6613
+ await rename3(tmpPath, filePath);
6415
6614
  };
6416
6615
  var formatUtcTimestamp = (value) => new Date(value).toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
6417
6616
  var normalizeTitle = (title) => {
@@ -6564,6 +6763,13 @@ var InMemoryConversationStore = class {
6564
6763
  async delete(conversationId) {
6565
6764
  return this.conversations.delete(conversationId);
6566
6765
  }
6766
+ async appendSubagentResult(conversationId, result) {
6767
+ const conversation = this.conversations.get(conversationId);
6768
+ if (!conversation) return;
6769
+ if (!conversation.pendingSubagentResults) conversation.pendingSubagentResults = [];
6770
+ conversation.pendingSubagentResults.push(result);
6771
+ conversation.updatedAt = Date.now();
6772
+ }
6567
6773
  };
6568
6774
  var FileConversationStore = class {
6569
6775
  workingDir;
@@ -6585,8 +6791,8 @@ var FileConversationStore = class {
6585
6791
  agentId: this.agentId
6586
6792
  });
6587
6793
  const agentDir = getAgentStoreDirectory(identity);
6588
- const conversationsDir = resolve10(agentDir, CONVERSATIONS_DIRECTORY);
6589
- const indexPath = resolve10(conversationsDir, LOCAL_CONVERSATION_INDEX_FILE);
6794
+ const conversationsDir = resolve11(agentDir, CONVERSATIONS_DIRECTORY);
6795
+ const indexPath = resolve11(conversationsDir, LOCAL_CONVERSATION_INDEX_FILE);
6590
6796
  this.paths = { conversationsDir, indexPath };
6591
6797
  return this.paths;
6592
6798
  }
@@ -6596,13 +6802,13 @@ var FileConversationStore = class {
6596
6802
  schemaVersion: STORAGE_SCHEMA_VERSION,
6597
6803
  conversations: Array.from(this.conversations.values()).sort((a, b) => b.updatedAt - a.updatedAt)
6598
6804
  };
6599
- await writeJsonAtomic2(indexPath, payload);
6805
+ await writeJsonAtomic3(indexPath, payload);
6600
6806
  }
6601
6807
  async readConversationFile(fileName) {
6602
6808
  const { conversationsDir } = await this.resolvePaths();
6603
- const filePath = resolve10(conversationsDir, fileName);
6809
+ const filePath = resolve11(conversationsDir, fileName);
6604
6810
  try {
6605
- const raw = await readFile8(filePath, "utf8");
6811
+ const raw = await readFile9(filePath, "utf8");
6606
6812
  return JSON.parse(raw);
6607
6813
  } catch {
6608
6814
  return void 0;
@@ -6648,7 +6854,7 @@ var FileConversationStore = class {
6648
6854
  this.loaded = true;
6649
6855
  const { indexPath } = await this.resolvePaths();
6650
6856
  try {
6651
- const raw = await readFile8(indexPath, "utf8");
6857
+ const raw = await readFile9(indexPath, "utf8");
6652
6858
  const parsed = JSON.parse(raw);
6653
6859
  for (const conversation of parsed.conversations ?? []) {
6654
6860
  this.conversations.set(conversation.conversationId, conversation);
@@ -6671,9 +6877,9 @@ var FileConversationStore = class {
6671
6877
  const { conversationsDir } = await this.resolvePaths();
6672
6878
  const existing = this.conversations.get(conversation.conversationId);
6673
6879
  const fileName = existing?.fileName ?? this.resolveConversationFileName(conversation);
6674
- const filePath = resolve10(conversationsDir, fileName);
6880
+ const filePath = resolve11(conversationsDir, fileName);
6675
6881
  this.writing = this.writing.then(async () => {
6676
- await writeJsonAtomic2(filePath, conversation);
6882
+ await writeJsonAtomic3(filePath, conversation);
6677
6883
  this.conversations.set(conversation.conversationId, {
6678
6884
  conversationId: conversation.conversationId,
6679
6885
  title: conversation.title,
@@ -6769,7 +6975,7 @@ var FileConversationStore = class {
6769
6975
  if (removed) {
6770
6976
  this.writing = this.writing.then(async () => {
6771
6977
  if (existing) {
6772
- await rm3(resolve10(conversationsDir, existing.fileName), { force: true });
6978
+ await rm3(resolve11(conversationsDir, existing.fileName), { force: true });
6773
6979
  }
6774
6980
  await this.writeIndex();
6775
6981
  });
@@ -6777,6 +6983,15 @@ var FileConversationStore = class {
6777
6983
  }
6778
6984
  return removed;
6779
6985
  }
6986
+ async appendSubagentResult(conversationId, result) {
6987
+ await this.ensureLoaded();
6988
+ const conversation = await this.get(conversationId);
6989
+ if (!conversation) return;
6990
+ if (!conversation.pendingSubagentResults) conversation.pendingSubagentResults = [];
6991
+ conversation.pendingSubagentResults.push(result);
6992
+ conversation.updatedAt = Date.now();
6993
+ await this.update(conversation);
6994
+ }
6780
6995
  };
6781
6996
  var FileStateStore = class {
6782
6997
  workingDir;
@@ -6799,7 +7014,7 @@ var FileStateStore = class {
6799
7014
  workingDir: this.workingDir,
6800
7015
  agentId: this.agentId
6801
7016
  });
6802
- this.filePath = resolve10(getAgentStoreDirectory(identity), LOCAL_STATE_FILE);
7017
+ this.filePath = resolve11(getAgentStoreDirectory(identity), LOCAL_STATE_FILE);
6803
7018
  }
6804
7019
  isExpired(state) {
6805
7020
  return typeof this.ttlMs === "number" && Date.now() - state.updatedAt > this.ttlMs;
@@ -6811,7 +7026,7 @@ var FileStateStore = class {
6811
7026
  }
6812
7027
  this.loaded = true;
6813
7028
  try {
6814
- const raw = await readFile8(this.filePath, "utf8");
7029
+ const raw = await readFile9(this.filePath, "utf8");
6815
7030
  const parsed = JSON.parse(raw);
6816
7031
  for (const state of parsed.states ?? []) {
6817
7032
  this.states.set(state.runId, state);
@@ -6824,7 +7039,7 @@ var FileStateStore = class {
6824
7039
  states: Array.from(this.states.values())
6825
7040
  };
6826
7041
  this.writing = this.writing.then(async () => {
6827
- await writeJsonAtomic2(this.filePath, payload);
7042
+ await writeJsonAtomic3(this.filePath, payload);
6828
7043
  });
6829
7044
  await this.writing;
6830
7045
  }
@@ -6856,6 +7071,7 @@ var KeyValueConversationStoreBase = class {
6856
7071
  ttl;
6857
7072
  agentIdPromise;
6858
7073
  ownerLocks = /* @__PURE__ */ new Map();
7074
+ appendLocks = /* @__PURE__ */ new Map();
6859
7075
  memoryFallback;
6860
7076
  constructor(ttl, workingDir, agentId) {
6861
7077
  this.ttl = ttl;
@@ -6874,6 +7090,18 @@ var KeyValueConversationStoreBase = class {
6874
7090
  }
6875
7091
  }
6876
7092
  }
7093
+ async withAppendLock(conversationId, task) {
7094
+ const prev = this.appendLocks.get(conversationId) ?? Promise.resolve();
7095
+ const next = prev.then(task, task);
7096
+ this.appendLocks.set(conversationId, next);
7097
+ try {
7098
+ await next;
7099
+ } finally {
7100
+ if (this.appendLocks.get(conversationId) === next) {
7101
+ this.appendLocks.delete(conversationId);
7102
+ }
7103
+ }
7104
+ }
6877
7105
  async namespace() {
6878
7106
  const agentId = await this.agentIdPromise;
6879
7107
  return `poncho:${STORAGE_SCHEMA_VERSION}:${slugifyStorageComponent(agentId)}`;
@@ -7094,6 +7322,16 @@ var KeyValueConversationStoreBase = class {
7094
7322
  });
7095
7323
  return true;
7096
7324
  }
7325
+ async appendSubagentResult(conversationId, result) {
7326
+ await this.withAppendLock(conversationId, async () => {
7327
+ const conversation = await this.get(conversationId);
7328
+ if (!conversation) return;
7329
+ if (!conversation.pendingSubagentResults) conversation.pendingSubagentResults = [];
7330
+ conversation.pendingSubagentResults.push(result);
7331
+ conversation.updatedAt = Date.now();
7332
+ await this.update(conversation);
7333
+ });
7334
+ }
7097
7335
  };
7098
7336
  var UpstashConversationStore = class extends KeyValueConversationStoreBase {
7099
7337
  baseUrl;
@@ -7136,20 +7374,26 @@ var UpstashConversationStore = class extends KeyValueConversationStoreBase {
7136
7374
  return (payload.result ?? []).map((v) => v ?? void 0);
7137
7375
  },
7138
7376
  set: async (key, value, ttl) => {
7139
- const endpoint = typeof ttl === "number" ? `${this.baseUrl}/setex/${encodeURIComponent(key)}/${Math.max(
7140
- 1,
7141
- ttl
7142
- )}/${encodeURIComponent(value)}` : `${this.baseUrl}/set/${encodeURIComponent(key)}/${encodeURIComponent(value)}`;
7143
- await fetch(endpoint, {
7377
+ const command = typeof ttl === "number" ? ["SETEX", key, Math.max(1, ttl), value] : ["SET", key, value];
7378
+ const response = await fetch(this.baseUrl, {
7144
7379
  method: "POST",
7145
- headers: this.headers()
7380
+ headers: this.headers(),
7381
+ body: JSON.stringify(command)
7146
7382
  });
7383
+ if (!response.ok) {
7384
+ const text = await response.text().catch(() => "");
7385
+ console.error(`[store][upstash] SET failed (${response.status}): ${text.slice(0, 200)}`);
7386
+ }
7147
7387
  },
7148
7388
  del: async (key) => {
7149
- await fetch(`${this.baseUrl}/del/${encodeURIComponent(key)}`, {
7389
+ const response = await fetch(`${this.baseUrl}/del/${encodeURIComponent(key)}`, {
7150
7390
  method: "POST",
7151
7391
  headers: this.headers()
7152
7392
  });
7393
+ if (!response.ok) {
7394
+ const text = await response.text().catch(() => "");
7395
+ console.error(`[store][upstash] DEL failed (${response.status}): ${text.slice(0, 200)}`);
7396
+ }
7153
7397
  }
7154
7398
  };
7155
7399
  }
@@ -7538,7 +7782,7 @@ var TelemetryEmitter = class {
7538
7782
  };
7539
7783
 
7540
7784
  // src/index.ts
7541
- import { defineTool as defineTool5 } from "@poncho-ai/sdk";
7785
+ import { defineTool as defineTool6 } from "@poncho-ai/sdk";
7542
7786
  export {
7543
7787
  AgentHarness,
7544
7788
  InMemoryConversationStore,
@@ -7568,7 +7812,7 @@ export {
7568
7812
  createSubagentTools,
7569
7813
  createUploadStore,
7570
7814
  createWriteTool,
7571
- defineTool5 as defineTool,
7815
+ defineTool6 as defineTool,
7572
7816
  deriveUploadKey,
7573
7817
  ensureAgentIdentity,
7574
7818
  estimateTokens,