@poncho-ai/harness 0.11.2 → 0.12.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/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +11 -0
- package/dist/index.d.ts +53 -1
- package/dist/index.js +583 -133
- package/package.json +2 -2
- package/src/config.ts +10 -0
- package/src/harness.ts +191 -23
- package/src/index.ts +1 -0
- package/src/upload-store.ts +387 -0
package/dist/index.js
CHANGED
|
@@ -412,10 +412,313 @@ var createWriteTool = (workingDir) => defineTool({
|
|
|
412
412
|
|
|
413
413
|
// src/harness.ts
|
|
414
414
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
415
|
+
import { getTextContent } from "@poncho-ai/sdk";
|
|
416
|
+
|
|
417
|
+
// src/upload-store.ts
|
|
418
|
+
import { createHash as createHash2 } from "crypto";
|
|
419
|
+
import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile3, rm } from "fs/promises";
|
|
420
|
+
import { createRequire } from "module";
|
|
421
|
+
import { resolve as resolve5 } from "path";
|
|
422
|
+
var tryImport = async (mod, workingDir) => {
|
|
423
|
+
try {
|
|
424
|
+
return await import(
|
|
425
|
+
/* webpackIgnore: true */
|
|
426
|
+
mod
|
|
427
|
+
);
|
|
428
|
+
} catch {
|
|
429
|
+
if (workingDir) {
|
|
430
|
+
const require2 = createRequire(resolve5(workingDir, "package.json"));
|
|
431
|
+
const resolved = require2.resolve(mod);
|
|
432
|
+
return await import(
|
|
433
|
+
/* webpackIgnore: true */
|
|
434
|
+
resolved
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
throw new Error(`Cannot find module "${mod}"`);
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
var PONCHO_UPLOAD_SCHEME = "poncho-upload://";
|
|
441
|
+
var CachedUploadStore = class {
|
|
442
|
+
inner;
|
|
443
|
+
cache = /* @__PURE__ */ new Map();
|
|
444
|
+
maxEntries;
|
|
445
|
+
ttlMs;
|
|
446
|
+
constructor(inner, maxEntries = 64, ttlMs = 10 * 60 * 1e3) {
|
|
447
|
+
this.inner = inner;
|
|
448
|
+
this.maxEntries = maxEntries;
|
|
449
|
+
this.ttlMs = ttlMs;
|
|
450
|
+
}
|
|
451
|
+
async put(key, data, mediaType) {
|
|
452
|
+
const ref = `${PONCHO_UPLOAD_SCHEME}${key}`;
|
|
453
|
+
const now2 = Date.now();
|
|
454
|
+
this.cache.set(ref, { data, ts: now2 });
|
|
455
|
+
this.cache.set(key, { data, ts: now2 });
|
|
456
|
+
this.evict();
|
|
457
|
+
this.inner.put(key, data, mediaType).catch((err) => {
|
|
458
|
+
console.error("[poncho] background upload failed:", err instanceof Error ? err.message : err);
|
|
459
|
+
});
|
|
460
|
+
return ref;
|
|
461
|
+
}
|
|
462
|
+
async get(urlOrKey) {
|
|
463
|
+
const cached = this.cache.get(urlOrKey);
|
|
464
|
+
if (cached && Date.now() - cached.ts < this.ttlMs) {
|
|
465
|
+
return cached.data;
|
|
466
|
+
}
|
|
467
|
+
return this.inner.get(urlOrKey);
|
|
468
|
+
}
|
|
469
|
+
async delete(urlOrKey) {
|
|
470
|
+
this.cache.delete(urlOrKey);
|
|
471
|
+
return this.inner.delete(urlOrKey);
|
|
472
|
+
}
|
|
473
|
+
evict() {
|
|
474
|
+
if (this.cache.size <= this.maxEntries) return;
|
|
475
|
+
let oldest;
|
|
476
|
+
let oldestTs = Infinity;
|
|
477
|
+
for (const [k, v] of this.cache) {
|
|
478
|
+
if (v.ts < oldestTs) {
|
|
479
|
+
oldestTs = v.ts;
|
|
480
|
+
oldest = k;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
if (oldest) this.cache.delete(oldest);
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
var deriveUploadKey = (data, mediaType) => {
|
|
487
|
+
const hash = createHash2("sha256").update(data).digest("hex").slice(0, 24);
|
|
488
|
+
const ext = mimeToExt(mediaType);
|
|
489
|
+
return `${hash}${ext}`;
|
|
490
|
+
};
|
|
491
|
+
var MIME_EXT_MAP = {
|
|
492
|
+
"image/jpeg": ".jpg",
|
|
493
|
+
"image/png": ".png",
|
|
494
|
+
"image/gif": ".gif",
|
|
495
|
+
"image/webp": ".webp",
|
|
496
|
+
"image/svg+xml": ".svg",
|
|
497
|
+
"application/pdf": ".pdf",
|
|
498
|
+
"text/plain": ".txt",
|
|
499
|
+
"text/csv": ".csv",
|
|
500
|
+
"text/html": ".html",
|
|
501
|
+
"application/json": ".json",
|
|
502
|
+
"video/mp4": ".mp4",
|
|
503
|
+
"video/webm": ".webm",
|
|
504
|
+
"audio/mpeg": ".mp3",
|
|
505
|
+
"audio/wav": ".wav"
|
|
506
|
+
};
|
|
507
|
+
var mimeToExt = (mediaType) => MIME_EXT_MAP[mediaType] ?? `.${mediaType.split("/").pop() ?? "bin"}`;
|
|
508
|
+
var LocalUploadStore = class {
|
|
509
|
+
uploadsDir;
|
|
510
|
+
constructor(workingDir) {
|
|
511
|
+
this.uploadsDir = resolve5(workingDir, ".poncho", "uploads");
|
|
512
|
+
}
|
|
513
|
+
async put(_key, data, mediaType) {
|
|
514
|
+
const key = deriveUploadKey(data, mediaType);
|
|
515
|
+
const filePath = resolve5(this.uploadsDir, key);
|
|
516
|
+
await mkdir2(this.uploadsDir, { recursive: true });
|
|
517
|
+
await writeFile3(filePath, data);
|
|
518
|
+
return `${PONCHO_UPLOAD_SCHEME}${key}`;
|
|
519
|
+
}
|
|
520
|
+
async get(urlOrKey) {
|
|
521
|
+
const key = urlOrKey.startsWith(PONCHO_UPLOAD_SCHEME) ? urlOrKey.slice(PONCHO_UPLOAD_SCHEME.length) : urlOrKey;
|
|
522
|
+
return readFile4(resolve5(this.uploadsDir, key));
|
|
523
|
+
}
|
|
524
|
+
async delete(urlOrKey) {
|
|
525
|
+
const key = urlOrKey.startsWith(PONCHO_UPLOAD_SCHEME) ? urlOrKey.slice(PONCHO_UPLOAD_SCHEME.length) : urlOrKey;
|
|
526
|
+
await rm(resolve5(this.uploadsDir, key), { force: true });
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
var VercelBlobUploadStore = class {
|
|
530
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
531
|
+
sdk;
|
|
532
|
+
workingDir;
|
|
533
|
+
access;
|
|
534
|
+
constructor(workingDir, access3 = "public") {
|
|
535
|
+
this.workingDir = workingDir;
|
|
536
|
+
this.access = access3;
|
|
537
|
+
}
|
|
538
|
+
async loadSdk() {
|
|
539
|
+
if (this.sdk) return this.sdk;
|
|
540
|
+
try {
|
|
541
|
+
this.sdk = await tryImport("@vercel/blob", this.workingDir);
|
|
542
|
+
return this.sdk;
|
|
543
|
+
} catch {
|
|
544
|
+
throw new Error(
|
|
545
|
+
'uploads: vercel-blob provider requires the "@vercel/blob" package. Install it with: pnpm add @vercel/blob'
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
async put(key, data, mediaType) {
|
|
550
|
+
const sdk = await this.loadSdk();
|
|
551
|
+
await sdk.put(key, data, {
|
|
552
|
+
access: this.access,
|
|
553
|
+
contentType: mediaType,
|
|
554
|
+
addRandomSuffix: false,
|
|
555
|
+
allowOverwrite: true
|
|
556
|
+
});
|
|
557
|
+
return `${PONCHO_UPLOAD_SCHEME}${key}`;
|
|
558
|
+
}
|
|
559
|
+
async get(urlOrKey) {
|
|
560
|
+
let pathname = urlOrKey;
|
|
561
|
+
if (urlOrKey.startsWith(PONCHO_UPLOAD_SCHEME)) {
|
|
562
|
+
pathname = urlOrKey.slice(PONCHO_UPLOAD_SCHEME.length);
|
|
563
|
+
} else if (urlOrKey.startsWith("https://") || urlOrKey.startsWith("http://")) {
|
|
564
|
+
pathname = new URL(urlOrKey).pathname.slice(1);
|
|
565
|
+
}
|
|
566
|
+
if (this.access === "private") {
|
|
567
|
+
const sdk2 = await this.loadSdk();
|
|
568
|
+
const result = await sdk2.get(pathname, { access: "private" });
|
|
569
|
+
if (!result || result.statusCode !== 200) {
|
|
570
|
+
throw new Error(`uploads: failed to fetch private blob "${pathname}": ${result?.statusCode ?? "not found"}`);
|
|
571
|
+
}
|
|
572
|
+
const chunks = [];
|
|
573
|
+
const reader = result.stream.getReader();
|
|
574
|
+
for (; ; ) {
|
|
575
|
+
const { done, value } = await reader.read();
|
|
576
|
+
if (done) break;
|
|
577
|
+
chunks.push(value);
|
|
578
|
+
}
|
|
579
|
+
return Buffer.concat(chunks);
|
|
580
|
+
}
|
|
581
|
+
const sdk = await this.loadSdk();
|
|
582
|
+
const blob = await sdk.head(pathname);
|
|
583
|
+
const response = await fetch(blob.url);
|
|
584
|
+
if (!response.ok) {
|
|
585
|
+
throw new Error(`uploads: failed to fetch blob "${pathname}": ${response.status}`);
|
|
586
|
+
}
|
|
587
|
+
return Buffer.from(await response.arrayBuffer());
|
|
588
|
+
}
|
|
589
|
+
async delete(urlOrKey) {
|
|
590
|
+
const sdk = await this.loadSdk();
|
|
591
|
+
await sdk.del(urlOrKey);
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
var S3UploadStore = class {
|
|
595
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
596
|
+
s3Sdk;
|
|
597
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
598
|
+
presignerSdk;
|
|
599
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
600
|
+
client;
|
|
601
|
+
bucket;
|
|
602
|
+
region;
|
|
603
|
+
endpoint;
|
|
604
|
+
workingDir;
|
|
605
|
+
constructor(bucket, region, endpoint, workingDir) {
|
|
606
|
+
this.bucket = bucket;
|
|
607
|
+
this.region = region;
|
|
608
|
+
this.endpoint = endpoint;
|
|
609
|
+
this.workingDir = workingDir;
|
|
610
|
+
}
|
|
611
|
+
async ensureClient() {
|
|
612
|
+
if (this.client) return;
|
|
613
|
+
try {
|
|
614
|
+
this.s3Sdk = await tryImport("@aws-sdk/client-s3", this.workingDir);
|
|
615
|
+
this.presignerSdk = await tryImport("@aws-sdk/s3-request-presigner", this.workingDir);
|
|
616
|
+
} catch {
|
|
617
|
+
throw new Error(
|
|
618
|
+
'uploads: s3 provider requires "@aws-sdk/client-s3" and "@aws-sdk/s3-request-presigner". Install with: pnpm add @aws-sdk/client-s3 @aws-sdk/s3-request-presigner'
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
this.client = new this.s3Sdk.S3Client({
|
|
622
|
+
region: this.region ?? process.env.AWS_REGION ?? "us-east-1",
|
|
623
|
+
...this.endpoint ? { endpoint: this.endpoint, forcePathStyle: true } : {}
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
async put(key, data, mediaType) {
|
|
627
|
+
await this.ensureClient();
|
|
628
|
+
await this.client.send(
|
|
629
|
+
new this.s3Sdk.PutObjectCommand({
|
|
630
|
+
Bucket: this.bucket,
|
|
631
|
+
Key: key,
|
|
632
|
+
Body: data,
|
|
633
|
+
ContentType: mediaType
|
|
634
|
+
})
|
|
635
|
+
);
|
|
636
|
+
const url = await this.presignerSdk.getSignedUrl(
|
|
637
|
+
this.client,
|
|
638
|
+
new this.s3Sdk.GetObjectCommand({ Bucket: this.bucket, Key: key }),
|
|
639
|
+
{ expiresIn: 7 * 24 * 60 * 60 }
|
|
640
|
+
);
|
|
641
|
+
return url;
|
|
642
|
+
}
|
|
643
|
+
async get(urlOrKey) {
|
|
644
|
+
if (urlOrKey.startsWith("https://") || urlOrKey.startsWith("http://")) {
|
|
645
|
+
const response = await fetch(urlOrKey);
|
|
646
|
+
if (!response.ok) {
|
|
647
|
+
throw new Error(`uploads: failed to fetch S3 object at ${urlOrKey}: ${response.status}`);
|
|
648
|
+
}
|
|
649
|
+
return Buffer.from(await response.arrayBuffer());
|
|
650
|
+
}
|
|
651
|
+
await this.ensureClient();
|
|
652
|
+
const result = await this.client.send(
|
|
653
|
+
new this.s3Sdk.GetObjectCommand({ Bucket: this.bucket, Key: urlOrKey })
|
|
654
|
+
);
|
|
655
|
+
if (!result.Body) throw new Error(`uploads: empty body for S3 key ${urlOrKey}`);
|
|
656
|
+
return Buffer.from(await result.Body.transformToByteArray());
|
|
657
|
+
}
|
|
658
|
+
async delete(urlOrKey) {
|
|
659
|
+
await this.ensureClient();
|
|
660
|
+
const key = urlOrKey.startsWith("https://") ? new URL(urlOrKey).pathname.slice(1) : urlOrKey;
|
|
661
|
+
await this.client.send(
|
|
662
|
+
new this.s3Sdk.DeleteObjectCommand({ Bucket: this.bucket, Key: key })
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
var warn = (msg) => {
|
|
667
|
+
console.warn(`[poncho] \u26A0 ${msg}`);
|
|
668
|
+
};
|
|
669
|
+
var createUploadStore = async (config, workingDir) => {
|
|
670
|
+
const provider = config?.provider ?? "local";
|
|
671
|
+
if (provider === "vercel-blob") {
|
|
672
|
+
if (!process.env.BLOB_READ_WRITE_TOKEN) {
|
|
673
|
+
warn(
|
|
674
|
+
"uploads: vercel-blob configured but BLOB_READ_WRITE_TOKEN not found in environment. Falling back to local filesystem.\n Make sure BLOB_READ_WRITE_TOKEN is set in your .env file or environment."
|
|
675
|
+
);
|
|
676
|
+
return new LocalUploadStore(workingDir);
|
|
677
|
+
}
|
|
678
|
+
const store = new VercelBlobUploadStore(workingDir, config?.access ?? "public");
|
|
679
|
+
try {
|
|
680
|
+
await store.loadSdk();
|
|
681
|
+
console.log("[poncho] uploads: using vercel-blob store");
|
|
682
|
+
return new CachedUploadStore(store);
|
|
683
|
+
} catch {
|
|
684
|
+
warn(
|
|
685
|
+
'uploads: vercel-blob configured but "@vercel/blob" package is not installed. Falling back to local filesystem.\n Run `poncho build <target>` to auto-add it, or install manually: pnpm add @vercel/blob'
|
|
686
|
+
);
|
|
687
|
+
return new LocalUploadStore(workingDir);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
if (provider === "s3") {
|
|
691
|
+
const bucket = config?.bucket ?? process.env.PONCHO_UPLOADS_BUCKET;
|
|
692
|
+
if (!process.env.AWS_ACCESS_KEY_ID || !bucket) {
|
|
693
|
+
const missing = !process.env.AWS_ACCESS_KEY_ID ? "AWS_ACCESS_KEY_ID" : "bucket (config.uploads.bucket or PONCHO_UPLOADS_BUCKET)";
|
|
694
|
+
warn(
|
|
695
|
+
`uploads: s3 configured but ${missing} not found in environment. Falling back to local filesystem.`
|
|
696
|
+
);
|
|
697
|
+
return new LocalUploadStore(workingDir);
|
|
698
|
+
}
|
|
699
|
+
const store = new S3UploadStore(
|
|
700
|
+
bucket,
|
|
701
|
+
config?.region ?? process.env.AWS_REGION,
|
|
702
|
+
config?.endpoint ?? process.env.PONCHO_UPLOADS_ENDPOINT,
|
|
703
|
+
workingDir
|
|
704
|
+
);
|
|
705
|
+
try {
|
|
706
|
+
await store.ensureClient();
|
|
707
|
+
console.log(`[poncho] uploads: using s3 store (bucket: ${bucket})`);
|
|
708
|
+
return new CachedUploadStore(store);
|
|
709
|
+
} catch {
|
|
710
|
+
warn(
|
|
711
|
+
"uploads: s3 configured but AWS SDK packages are not installed. Falling back to local filesystem.\n Run `poncho build <target>` to auto-add them, or install manually: pnpm add @aws-sdk/client-s3 @aws-sdk/s3-request-presigner"
|
|
712
|
+
);
|
|
713
|
+
return new LocalUploadStore(workingDir);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return new LocalUploadStore(workingDir);
|
|
717
|
+
};
|
|
415
718
|
|
|
416
719
|
// src/memory.ts
|
|
417
|
-
import { mkdir as
|
|
418
|
-
import { dirname as dirname2, resolve as
|
|
720
|
+
import { mkdir as mkdir3, readFile as readFile5, rename, writeFile as writeFile4 } from "fs/promises";
|
|
721
|
+
import { dirname as dirname2, resolve as resolve6 } from "path";
|
|
419
722
|
import { defineTool as defineTool2 } from "@poncho-ai/sdk";
|
|
420
723
|
var DEFAULT_MAIN_MEMORY = {
|
|
421
724
|
content: "",
|
|
@@ -423,9 +726,9 @@ var DEFAULT_MAIN_MEMORY = {
|
|
|
423
726
|
};
|
|
424
727
|
var LOCAL_MEMORY_FILE = "memory.json";
|
|
425
728
|
var writeJsonAtomic = async (filePath, payload) => {
|
|
426
|
-
await
|
|
729
|
+
await mkdir3(dirname2(filePath), { recursive: true });
|
|
427
730
|
const tmpPath = `${filePath}.tmp`;
|
|
428
|
-
await
|
|
731
|
+
await writeFile4(tmpPath, JSON.stringify(payload, null, 2), "utf8");
|
|
429
732
|
await rename(tmpPath, filePath);
|
|
430
733
|
};
|
|
431
734
|
var scoreText = (text, query) => {
|
|
@@ -487,7 +790,7 @@ var FileMainMemoryStore = class {
|
|
|
487
790
|
return;
|
|
488
791
|
}
|
|
489
792
|
const identity = await ensureAgentIdentity(this.workingDir);
|
|
490
|
-
this.filePath =
|
|
793
|
+
this.filePath = resolve6(getAgentStoreDirectory(identity), LOCAL_MEMORY_FILE);
|
|
491
794
|
}
|
|
492
795
|
isExpired(updatedAt) {
|
|
493
796
|
return typeof this.ttlMs === "number" && Date.now() - updatedAt > this.ttlMs;
|
|
@@ -499,7 +802,7 @@ var FileMainMemoryStore = class {
|
|
|
499
802
|
}
|
|
500
803
|
this.loaded = true;
|
|
501
804
|
try {
|
|
502
|
-
const raw = await
|
|
805
|
+
const raw = await readFile5(this.filePath, "utf8");
|
|
503
806
|
const parsed = JSON.parse(raw);
|
|
504
807
|
const content = typeof parsed.main?.content === "string" ? parsed.main.content : "";
|
|
505
808
|
const updatedAt = typeof parsed.main?.updatedAt === "number" ? parsed.main.updatedAt : 0;
|
|
@@ -1385,8 +1688,8 @@ var createModelProvider = (provider) => {
|
|
|
1385
1688
|
};
|
|
1386
1689
|
|
|
1387
1690
|
// src/skill-context.ts
|
|
1388
|
-
import { readFile as
|
|
1389
|
-
import { dirname as dirname3, resolve as
|
|
1691
|
+
import { readFile as readFile6, readdir as readdir2, stat } from "fs/promises";
|
|
1692
|
+
import { dirname as dirname3, resolve as resolve7, normalize } from "path";
|
|
1390
1693
|
import YAML3 from "yaml";
|
|
1391
1694
|
var DEFAULT_SKILL_DIRS = ["skills"];
|
|
1392
1695
|
var resolveSkillDirs = (workingDir, extraPaths) => {
|
|
@@ -1398,7 +1701,7 @@ var resolveSkillDirs = (workingDir, extraPaths) => {
|
|
|
1398
1701
|
}
|
|
1399
1702
|
}
|
|
1400
1703
|
}
|
|
1401
|
-
return dirs.map((d) =>
|
|
1704
|
+
return dirs.map((d) => resolve7(workingDir, d));
|
|
1402
1705
|
};
|
|
1403
1706
|
var FRONTMATTER_PATTERN3 = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
|
|
1404
1707
|
var asRecord2 = (value) => typeof value === "object" && value !== null ? value : {};
|
|
@@ -1467,7 +1770,7 @@ var collectSkillManifests = async (directory) => {
|
|
|
1467
1770
|
const entries = await readdir2(directory, { withFileTypes: true });
|
|
1468
1771
|
const files = [];
|
|
1469
1772
|
for (const entry of entries) {
|
|
1470
|
-
const fullPath =
|
|
1773
|
+
const fullPath = resolve7(directory, entry.name);
|
|
1471
1774
|
let isDir = entry.isDirectory();
|
|
1472
1775
|
let isFile = entry.isFile();
|
|
1473
1776
|
if (entry.isSymbolicLink()) {
|
|
@@ -1502,7 +1805,7 @@ var loadSkillMetadata = async (workingDir, extraSkillPaths) => {
|
|
|
1502
1805
|
const seen = /* @__PURE__ */ new Set();
|
|
1503
1806
|
for (const manifest of allManifests) {
|
|
1504
1807
|
try {
|
|
1505
|
-
const content = await
|
|
1808
|
+
const content = await readFile6(manifest, "utf8");
|
|
1506
1809
|
const parsed = parseSkillFrontmatter(content);
|
|
1507
1810
|
if (parsed && !seen.has(parsed.name)) {
|
|
1508
1811
|
seen.add(parsed.name);
|
|
@@ -1547,7 +1850,7 @@ ${xmlSkills}
|
|
|
1547
1850
|
};
|
|
1548
1851
|
var escapeXml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1549
1852
|
var loadSkillInstructions = async (skill) => {
|
|
1550
|
-
const content = await
|
|
1853
|
+
const content = await readFile6(skill.skillPath, "utf8");
|
|
1551
1854
|
const match = content.match(FRONTMATTER_PATTERN3);
|
|
1552
1855
|
return match ? match[2].trim() : content.trim();
|
|
1553
1856
|
};
|
|
@@ -1556,11 +1859,11 @@ var readSkillResource = async (skill, relativePath) => {
|
|
|
1556
1859
|
if (normalized.startsWith("..") || normalized.startsWith("/")) {
|
|
1557
1860
|
throw new Error("Path must be relative and within the skill directory");
|
|
1558
1861
|
}
|
|
1559
|
-
const fullPath =
|
|
1862
|
+
const fullPath = resolve7(skill.skillDir, normalized);
|
|
1560
1863
|
if (!fullPath.startsWith(skill.skillDir)) {
|
|
1561
1864
|
throw new Error("Path escapes the skill directory");
|
|
1562
1865
|
}
|
|
1563
|
-
return await
|
|
1866
|
+
return await readFile6(fullPath, "utf8");
|
|
1564
1867
|
};
|
|
1565
1868
|
var MAX_INSTRUCTIONS_PER_SKILL = 1200;
|
|
1566
1869
|
var loadSkillContext = async (workingDir) => {
|
|
@@ -1651,7 +1954,7 @@ function convertSchema(schema) {
|
|
|
1651
1954
|
// src/skill-tools.ts
|
|
1652
1955
|
import { defineTool as defineTool3 } from "@poncho-ai/sdk";
|
|
1653
1956
|
import { access as access2, readdir as readdir3, stat as stat2 } from "fs/promises";
|
|
1654
|
-
import { extname, normalize as normalize2, resolve as
|
|
1957
|
+
import { extname, normalize as normalize2, resolve as resolve8, sep as sep2 } from "path";
|
|
1655
1958
|
import { pathToFileURL } from "url";
|
|
1656
1959
|
import { createJiti as createJiti2 } from "jiti";
|
|
1657
1960
|
var createSkillTools = (skills, options) => {
|
|
@@ -1908,7 +2211,7 @@ var collectScriptFiles = async (directory) => {
|
|
|
1908
2211
|
if (entry.name === "node_modules") {
|
|
1909
2212
|
continue;
|
|
1910
2213
|
}
|
|
1911
|
-
const fullPath =
|
|
2214
|
+
const fullPath = resolve8(directory, entry.name);
|
|
1912
2215
|
let isDir = entry.isDirectory();
|
|
1913
2216
|
let isFile = entry.isFile();
|
|
1914
2217
|
if (entry.isSymbolicLink()) {
|
|
@@ -1947,8 +2250,8 @@ var normalizeScriptPolicyPath = (relativePath) => {
|
|
|
1947
2250
|
};
|
|
1948
2251
|
var resolveScriptPath = (baseDir, relativePath) => {
|
|
1949
2252
|
const normalized = normalizeScriptPolicyPath(relativePath);
|
|
1950
|
-
const fullPath =
|
|
1951
|
-
if (!fullPath.startsWith(`${
|
|
2253
|
+
const fullPath = resolve8(baseDir, normalized);
|
|
2254
|
+
if (!fullPath.startsWith(`${resolve8(baseDir)}${sep2}`) && fullPath !== resolve8(baseDir)) {
|
|
1952
2255
|
throw new Error("Script path must stay inside the allowed directory");
|
|
1953
2256
|
}
|
|
1954
2257
|
const extension = extname(fullPath).toLowerCase();
|
|
@@ -2283,6 +2586,7 @@ var AgentHarness = class {
|
|
|
2283
2586
|
modelProviderInjected;
|
|
2284
2587
|
dispatcher = new ToolDispatcher();
|
|
2285
2588
|
approvalHandler;
|
|
2589
|
+
uploadStore;
|
|
2286
2590
|
skillContextWindow = "";
|
|
2287
2591
|
memoryStore;
|
|
2288
2592
|
loadedConfig;
|
|
@@ -2345,6 +2649,7 @@ var AgentHarness = class {
|
|
|
2345
2649
|
this.modelProviderInjected = !!options.modelProvider;
|
|
2346
2650
|
this.modelProvider = options.modelProvider ?? createModelProvider("anthropic");
|
|
2347
2651
|
this.approvalHandler = options.approvalHandler;
|
|
2652
|
+
this.uploadStore = options.uploadStore;
|
|
2348
2653
|
if (options.toolDefinitions?.length) {
|
|
2349
2654
|
this.dispatcher.registerMany(options.toolDefinitions);
|
|
2350
2655
|
}
|
|
@@ -2626,9 +2931,9 @@ var AgentHarness = class {
|
|
|
2626
2931
|
for await (const event of this.run(input)) {
|
|
2627
2932
|
eventQueue.push(event);
|
|
2628
2933
|
if (queueResolve) {
|
|
2629
|
-
const
|
|
2934
|
+
const resolve10 = queueResolve;
|
|
2630
2935
|
queueResolve = null;
|
|
2631
|
-
|
|
2936
|
+
resolve10();
|
|
2632
2937
|
}
|
|
2633
2938
|
}
|
|
2634
2939
|
} catch (error) {
|
|
@@ -2646,8 +2951,8 @@ var AgentHarness = class {
|
|
|
2646
2951
|
if (eventQueue.length > 0) {
|
|
2647
2952
|
yield eventQueue.shift();
|
|
2648
2953
|
} else if (!generatorDone) {
|
|
2649
|
-
await new Promise((
|
|
2650
|
-
queueResolve =
|
|
2954
|
+
await new Promise((resolve10) => {
|
|
2955
|
+
queueResolve = resolve10;
|
|
2651
2956
|
});
|
|
2652
2957
|
}
|
|
2653
2958
|
}
|
|
@@ -2671,6 +2976,8 @@ var AgentHarness = class {
|
|
|
2671
2976
|
const start = now();
|
|
2672
2977
|
const maxSteps = agent.frontmatter.limits?.maxSteps ?? 50;
|
|
2673
2978
|
const timeoutMs = (agent.frontmatter.limits?.timeout ?? 300) * 1e3;
|
|
2979
|
+
const platformMaxDurationSec = Number(process.env.PONCHO_MAX_DURATION) || 0;
|
|
2980
|
+
const softDeadlineMs = platformMaxDurationSec > 0 ? platformMaxDurationSec * 800 : 0;
|
|
2674
2981
|
const messages = [...input.messages ?? []];
|
|
2675
2982
|
const events = [];
|
|
2676
2983
|
const systemPrompt = renderAgentPrompt(agent, {
|
|
@@ -2718,11 +3025,42 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
2718
3025
|
runId,
|
|
2719
3026
|
agentId: agent.frontmatter.id ?? agent.frontmatter.name
|
|
2720
3027
|
});
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
3028
|
+
if (input.files && input.files.length > 0) {
|
|
3029
|
+
const parts = [
|
|
3030
|
+
{ type: "text", text: input.task }
|
|
3031
|
+
];
|
|
3032
|
+
for (const file of input.files) {
|
|
3033
|
+
if (this.uploadStore) {
|
|
3034
|
+
const buf = Buffer.from(file.data, "base64");
|
|
3035
|
+
const key = deriveUploadKey(buf, file.mediaType);
|
|
3036
|
+
const ref = await this.uploadStore.put(key, buf, file.mediaType);
|
|
3037
|
+
parts.push({
|
|
3038
|
+
type: "file",
|
|
3039
|
+
data: ref,
|
|
3040
|
+
mediaType: file.mediaType,
|
|
3041
|
+
filename: file.filename
|
|
3042
|
+
});
|
|
3043
|
+
} else {
|
|
3044
|
+
parts.push({
|
|
3045
|
+
type: "file",
|
|
3046
|
+
data: file.data,
|
|
3047
|
+
mediaType: file.mediaType,
|
|
3048
|
+
filename: file.filename
|
|
3049
|
+
});
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
messages.push({
|
|
3053
|
+
role: "user",
|
|
3054
|
+
content: parts,
|
|
3055
|
+
metadata: { timestamp: now(), id: randomUUID3() }
|
|
3056
|
+
});
|
|
3057
|
+
} else {
|
|
3058
|
+
messages.push({
|
|
3059
|
+
role: "user",
|
|
3060
|
+
content: input.task,
|
|
3061
|
+
metadata: { timestamp: now(), id: randomUUID3() }
|
|
3062
|
+
});
|
|
3063
|
+
}
|
|
2726
3064
|
let responseText = "";
|
|
2727
3065
|
let totalInputTokens = 0;
|
|
2728
3066
|
let totalOutputTokens = 0;
|
|
@@ -2744,6 +3082,19 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
2744
3082
|
});
|
|
2745
3083
|
return;
|
|
2746
3084
|
}
|
|
3085
|
+
if (softDeadlineMs > 0 && now() - start > softDeadlineMs) {
|
|
3086
|
+
const result2 = {
|
|
3087
|
+
status: "completed",
|
|
3088
|
+
response: responseText,
|
|
3089
|
+
steps: step - 1,
|
|
3090
|
+
tokens: { input: totalInputTokens, output: totalOutputTokens, cached: 0 },
|
|
3091
|
+
duration: now() - start,
|
|
3092
|
+
continuation: true,
|
|
3093
|
+
maxSteps
|
|
3094
|
+
};
|
|
3095
|
+
yield pushEvent({ type: "run:completed", runId, result: result2 });
|
|
3096
|
+
return;
|
|
3097
|
+
}
|
|
2747
3098
|
const stepStart = now();
|
|
2748
3099
|
yield pushEvent({ type: "step:started", step });
|
|
2749
3100
|
yield pushEvent({ type: "model:request", tokens: 0 });
|
|
@@ -2765,102 +3116,182 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
2765
3116
|
inputSchema: jsonSchemaToZod(tool.inputSchema)
|
|
2766
3117
|
};
|
|
2767
3118
|
}
|
|
2768
|
-
const
|
|
2769
|
-
(msg)
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
3119
|
+
const convertMessage = async (msg) => {
|
|
3120
|
+
if (msg.role === "tool") {
|
|
3121
|
+
const textContent = typeof msg.content === "string" ? msg.content : getTextContent(msg);
|
|
3122
|
+
try {
|
|
3123
|
+
const parsed = JSON.parse(textContent);
|
|
3124
|
+
if (!Array.isArray(parsed)) {
|
|
3125
|
+
return [];
|
|
3126
|
+
}
|
|
3127
|
+
const toolResults = parsed.filter((item) => {
|
|
3128
|
+
if (typeof item !== "object" || item === null) {
|
|
3129
|
+
return false;
|
|
2775
3130
|
}
|
|
2776
|
-
const
|
|
2777
|
-
|
|
3131
|
+
const row = item;
|
|
3132
|
+
return typeof row.tool_use_id === "string" && typeof row.tool_name === "string" && typeof row.content === "string";
|
|
3133
|
+
});
|
|
3134
|
+
if (toolResults.length === 0) {
|
|
3135
|
+
return [];
|
|
3136
|
+
}
|
|
3137
|
+
return [{
|
|
3138
|
+
role: "tool",
|
|
3139
|
+
content: toolResults.map((tr) => {
|
|
3140
|
+
if (tr.content.startsWith("Tool error:")) {
|
|
3141
|
+
return {
|
|
3142
|
+
type: "tool-result",
|
|
3143
|
+
toolCallId: tr.tool_use_id,
|
|
3144
|
+
toolName: tr.tool_name,
|
|
3145
|
+
output: { type: "text", value: tr.content }
|
|
3146
|
+
};
|
|
3147
|
+
}
|
|
3148
|
+
try {
|
|
3149
|
+
const resultValue = JSON.parse(tr.content);
|
|
3150
|
+
return {
|
|
3151
|
+
type: "tool-result",
|
|
3152
|
+
toolCallId: tr.tool_use_id,
|
|
3153
|
+
toolName: tr.tool_name,
|
|
3154
|
+
output: { type: "json", value: resultValue }
|
|
3155
|
+
};
|
|
3156
|
+
} catch {
|
|
3157
|
+
return {
|
|
3158
|
+
type: "tool-result",
|
|
3159
|
+
toolCallId: tr.tool_use_id,
|
|
3160
|
+
toolName: tr.tool_name,
|
|
3161
|
+
output: { type: "text", value: tr.content }
|
|
3162
|
+
};
|
|
3163
|
+
}
|
|
3164
|
+
})
|
|
3165
|
+
}];
|
|
3166
|
+
} catch {
|
|
3167
|
+
return [];
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
if (msg.role === "assistant") {
|
|
3171
|
+
const assistantText = typeof msg.content === "string" ? msg.content : getTextContent(msg);
|
|
3172
|
+
try {
|
|
3173
|
+
const parsed = JSON.parse(assistantText);
|
|
3174
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
3175
|
+
const parsedRecord = parsed;
|
|
3176
|
+
if (!Array.isArray(parsedRecord.tool_calls)) {
|
|
3177
|
+
return [{ role: "assistant", content: assistantText }];
|
|
3178
|
+
}
|
|
3179
|
+
const textPart = typeof parsedRecord.text === "string" ? parsedRecord.text : "";
|
|
3180
|
+
const validToolCalls = parsedRecord.tool_calls.filter((tc) => {
|
|
3181
|
+
if (typeof tc !== "object" || tc === null) {
|
|
2778
3182
|
return false;
|
|
2779
3183
|
}
|
|
2780
|
-
const
|
|
2781
|
-
return typeof
|
|
2782
|
-
})
|
|
2783
|
-
|
|
3184
|
+
const toolCall = tc;
|
|
3185
|
+
return typeof toolCall.id === "string" && typeof toolCall.name === "string" && toolCall.input !== null && typeof toolCall.input === "object";
|
|
3186
|
+
}).map((tc) => ({
|
|
3187
|
+
type: "tool-call",
|
|
3188
|
+
toolCallId: tc.id,
|
|
3189
|
+
toolName: tc.name,
|
|
3190
|
+
input: tc.input
|
|
3191
|
+
}));
|
|
3192
|
+
if (textPart.length === 0 && validToolCalls.length === 0) {
|
|
2784
3193
|
return [];
|
|
2785
3194
|
}
|
|
2786
3195
|
return [{
|
|
2787
|
-
role: "
|
|
2788
|
-
content:
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
toolCallId: tr.tool_use_id,
|
|
2793
|
-
toolName: tr.tool_name,
|
|
2794
|
-
output: { type: "text", value: tr.content }
|
|
2795
|
-
};
|
|
2796
|
-
}
|
|
2797
|
-
try {
|
|
2798
|
-
const resultValue = JSON.parse(tr.content);
|
|
2799
|
-
return {
|
|
2800
|
-
type: "tool-result",
|
|
2801
|
-
toolCallId: tr.tool_use_id,
|
|
2802
|
-
toolName: tr.tool_name,
|
|
2803
|
-
output: { type: "json", value: resultValue }
|
|
2804
|
-
};
|
|
2805
|
-
} catch {
|
|
2806
|
-
return {
|
|
2807
|
-
type: "tool-result",
|
|
2808
|
-
toolCallId: tr.tool_use_id,
|
|
2809
|
-
toolName: tr.tool_name,
|
|
2810
|
-
output: { type: "text", value: tr.content }
|
|
2811
|
-
};
|
|
2812
|
-
}
|
|
2813
|
-
})
|
|
3196
|
+
role: "assistant",
|
|
3197
|
+
content: [
|
|
3198
|
+
...textPart.length > 0 ? [{ type: "text", text: textPart }] : [],
|
|
3199
|
+
...validToolCalls
|
|
3200
|
+
]
|
|
2814
3201
|
}];
|
|
2815
|
-
} catch {
|
|
2816
|
-
return [];
|
|
2817
3202
|
}
|
|
3203
|
+
} catch {
|
|
2818
3204
|
}
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
3205
|
+
return [{ role: "assistant", content: assistantText }];
|
|
3206
|
+
}
|
|
3207
|
+
if (msg.role === "system") {
|
|
3208
|
+
return [{
|
|
3209
|
+
role: "system",
|
|
3210
|
+
content: typeof msg.content === "string" ? msg.content : getTextContent(msg)
|
|
3211
|
+
}];
|
|
3212
|
+
}
|
|
3213
|
+
if (msg.role === "user") {
|
|
3214
|
+
if (typeof msg.content === "string") {
|
|
3215
|
+
return [{ role: "user", content: msg.content }];
|
|
3216
|
+
}
|
|
3217
|
+
const MODEL_IMAGE_TYPES = /* @__PURE__ */ new Set([
|
|
3218
|
+
"image/jpeg",
|
|
3219
|
+
"image/png",
|
|
3220
|
+
"image/gif",
|
|
3221
|
+
"image/webp"
|
|
3222
|
+
]);
|
|
3223
|
+
const MODEL_FILE_TYPES = /* @__PURE__ */ new Set([
|
|
3224
|
+
"application/pdf"
|
|
3225
|
+
]);
|
|
3226
|
+
const isTextBasedMime = (mt) => mt.startsWith("text/") || mt === "application/json" || mt === "application/xml" || mt === "application/x-yaml" || mt.endsWith("+json") || mt.endsWith("+xml");
|
|
3227
|
+
const userContent = await Promise.all(
|
|
3228
|
+
msg.content.map(async (part) => {
|
|
3229
|
+
if (part.type === "text") {
|
|
3230
|
+
return { type: "text", text: part.text };
|
|
3231
|
+
}
|
|
3232
|
+
const isSupportedImage = MODEL_IMAGE_TYPES.has(part.mediaType);
|
|
3233
|
+
const isSupportedFile = MODEL_FILE_TYPES.has(part.mediaType);
|
|
3234
|
+
if (!isSupportedImage && !isSupportedFile && isTextBasedMime(part.mediaType)) {
|
|
3235
|
+
let textContent;
|
|
3236
|
+
try {
|
|
3237
|
+
if (part.data.startsWith(PONCHO_UPLOAD_SCHEME) && this.uploadStore) {
|
|
3238
|
+
const buf = await this.uploadStore.get(part.data);
|
|
3239
|
+
textContent = buf.toString("utf8");
|
|
3240
|
+
} else if (part.data.startsWith("https://") || part.data.startsWith("http://")) {
|
|
3241
|
+
const resp = await fetch(part.data);
|
|
3242
|
+
textContent = await resp.text();
|
|
3243
|
+
} else {
|
|
3244
|
+
textContent = Buffer.from(part.data, "base64").toString("utf8");
|
|
2831
3245
|
}
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
}).map((tc) => ({
|
|
2835
|
-
type: "tool-call",
|
|
2836
|
-
toolCallId: tc.id,
|
|
2837
|
-
toolName: tc.name,
|
|
2838
|
-
input: tc.input
|
|
2839
|
-
}));
|
|
2840
|
-
if (textPart.length === 0 && validToolCalls.length === 0) {
|
|
2841
|
-
return [];
|
|
3246
|
+
} catch {
|
|
3247
|
+
textContent = "(could not read file)";
|
|
2842
3248
|
}
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
...textPart.length > 0 ? [{ type: "text", text: textPart }] : [],
|
|
2847
|
-
...validToolCalls
|
|
2848
|
-
]
|
|
2849
|
-
}];
|
|
3249
|
+
const label = part.filename ?? part.mediaType;
|
|
3250
|
+
return { type: "text", text: `[File: ${label}]
|
|
3251
|
+
${textContent}` };
|
|
2850
3252
|
}
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
3253
|
+
if (!isSupportedImage && !isSupportedFile) {
|
|
3254
|
+
const label = part.filename ?? part.mediaType;
|
|
3255
|
+
return {
|
|
3256
|
+
type: "text",
|
|
3257
|
+
text: `[Attached file: ${label} (${part.mediaType}) \u2014 this format is not supported by the model and was skipped]`
|
|
3258
|
+
};
|
|
3259
|
+
}
|
|
3260
|
+
let resolvedData;
|
|
3261
|
+
if (part.data.startsWith(PONCHO_UPLOAD_SCHEME) && this.uploadStore) {
|
|
3262
|
+
const buf = await this.uploadStore.get(part.data);
|
|
3263
|
+
resolvedData = buf.toString("base64");
|
|
3264
|
+
} else if (part.data.startsWith("https://") || part.data.startsWith("http://")) {
|
|
3265
|
+
if (this.uploadStore) {
|
|
3266
|
+
const buf = await this.uploadStore.get(part.data);
|
|
3267
|
+
resolvedData = buf.toString("base64");
|
|
3268
|
+
} else {
|
|
3269
|
+
const resp = await fetch(part.data);
|
|
3270
|
+
resolvedData = Buffer.from(await resp.arrayBuffer()).toString("base64");
|
|
3271
|
+
}
|
|
3272
|
+
} else {
|
|
3273
|
+
resolvedData = part.data;
|
|
3274
|
+
}
|
|
3275
|
+
if (isSupportedImage) {
|
|
3276
|
+
return {
|
|
3277
|
+
type: "image",
|
|
3278
|
+
image: resolvedData,
|
|
3279
|
+
mediaType: part.mediaType
|
|
3280
|
+
};
|
|
3281
|
+
}
|
|
3282
|
+
return {
|
|
3283
|
+
type: "file",
|
|
3284
|
+
data: resolvedData,
|
|
3285
|
+
mediaType: part.mediaType,
|
|
3286
|
+
filename: part.filename
|
|
3287
|
+
};
|
|
3288
|
+
})
|
|
3289
|
+
);
|
|
3290
|
+
return [{ role: "user", content: userContent }];
|
|
2862
3291
|
}
|
|
2863
|
-
|
|
3292
|
+
return [];
|
|
3293
|
+
};
|
|
3294
|
+
const coreMessages = (await Promise.all(trimMessageWindow(messages).map(convertMessage))).flat();
|
|
2864
3295
|
const modelName = agent.frontmatter.model?.name ?? "claude-opus-4-5";
|
|
2865
3296
|
const temperature = agent.frontmatter.model?.temperature ?? 0.2;
|
|
2866
3297
|
const maxTokens = agent.frontmatter.model?.maxTokens;
|
|
@@ -2907,8 +3338,8 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
2907
3338
|
let timer;
|
|
2908
3339
|
const nextChunk = await Promise.race([
|
|
2909
3340
|
textIterator.next(),
|
|
2910
|
-
new Promise((
|
|
2911
|
-
timer = setTimeout(() =>
|
|
3341
|
+
new Promise((resolve10) => {
|
|
3342
|
+
timer = setTimeout(() => resolve10(null), timeout);
|
|
2912
3343
|
})
|
|
2913
3344
|
]);
|
|
2914
3345
|
clearTimeout(timer);
|
|
@@ -3183,14 +3614,27 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
3183
3614
|
return;
|
|
3184
3615
|
}
|
|
3185
3616
|
}
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3617
|
+
if (softDeadlineMs > 0) {
|
|
3618
|
+
const result = {
|
|
3619
|
+
status: "completed",
|
|
3620
|
+
response: responseText,
|
|
3621
|
+
steps: maxSteps,
|
|
3622
|
+
tokens: { input: totalInputTokens, output: totalOutputTokens, cached: 0 },
|
|
3623
|
+
duration: now() - start,
|
|
3624
|
+
continuation: true,
|
|
3625
|
+
maxSteps
|
|
3626
|
+
};
|
|
3627
|
+
yield pushEvent({ type: "run:completed", runId, result });
|
|
3628
|
+
} else {
|
|
3629
|
+
yield pushEvent({
|
|
3630
|
+
type: "run:error",
|
|
3631
|
+
runId,
|
|
3632
|
+
error: {
|
|
3633
|
+
code: "MAX_STEPS_EXCEEDED",
|
|
3634
|
+
message: `Run reached maximum of ${maxSteps} steps`
|
|
3635
|
+
}
|
|
3636
|
+
});
|
|
3637
|
+
}
|
|
3194
3638
|
}
|
|
3195
3639
|
async runToCompletion(input) {
|
|
3196
3640
|
const events = [];
|
|
@@ -3270,8 +3714,8 @@ var LatitudeCapture = class {
|
|
|
3270
3714
|
|
|
3271
3715
|
// src/state.ts
|
|
3272
3716
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
3273
|
-
import { mkdir as
|
|
3274
|
-
import { dirname as dirname4, resolve as
|
|
3717
|
+
import { mkdir as mkdir4, readFile as readFile7, readdir as readdir4, rename as rename2, rm as rm2, writeFile as writeFile5 } from "fs/promises";
|
|
3718
|
+
import { dirname as dirname4, resolve as resolve9 } from "path";
|
|
3275
3719
|
var DEFAULT_OWNER = "local-owner";
|
|
3276
3720
|
var LOCAL_STATE_FILE = "state.json";
|
|
3277
3721
|
var CONVERSATIONS_DIRECTORY = "conversations";
|
|
@@ -3287,9 +3731,9 @@ var toStoreIdentity = async ({
|
|
|
3287
3731
|
return { name: ensured.name, id: agentId };
|
|
3288
3732
|
};
|
|
3289
3733
|
var writeJsonAtomic2 = async (filePath, payload) => {
|
|
3290
|
-
await
|
|
3734
|
+
await mkdir4(dirname4(filePath), { recursive: true });
|
|
3291
3735
|
const tmpPath = `${filePath}.tmp`;
|
|
3292
|
-
await
|
|
3736
|
+
await writeFile5(tmpPath, JSON.stringify(payload, null, 2), "utf8");
|
|
3293
3737
|
await rename2(tmpPath, filePath);
|
|
3294
3738
|
};
|
|
3295
3739
|
var formatUtcTimestamp = (value) => new Date(value).toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
@@ -3450,8 +3894,8 @@ var FileConversationStore = class {
|
|
|
3450
3894
|
agentId: this.agentId
|
|
3451
3895
|
});
|
|
3452
3896
|
const agentDir = getAgentStoreDirectory(identity);
|
|
3453
|
-
const conversationsDir =
|
|
3454
|
-
const indexPath =
|
|
3897
|
+
const conversationsDir = resolve9(agentDir, CONVERSATIONS_DIRECTORY);
|
|
3898
|
+
const indexPath = resolve9(conversationsDir, LOCAL_CONVERSATION_INDEX_FILE);
|
|
3455
3899
|
this.paths = { conversationsDir, indexPath };
|
|
3456
3900
|
return this.paths;
|
|
3457
3901
|
}
|
|
@@ -3465,9 +3909,9 @@ var FileConversationStore = class {
|
|
|
3465
3909
|
}
|
|
3466
3910
|
async readConversationFile(fileName) {
|
|
3467
3911
|
const { conversationsDir } = await this.resolvePaths();
|
|
3468
|
-
const filePath =
|
|
3912
|
+
const filePath = resolve9(conversationsDir, fileName);
|
|
3469
3913
|
try {
|
|
3470
|
-
const raw = await
|
|
3914
|
+
const raw = await readFile7(filePath, "utf8");
|
|
3471
3915
|
return JSON.parse(raw);
|
|
3472
3916
|
} catch {
|
|
3473
3917
|
return void 0;
|
|
@@ -3509,7 +3953,7 @@ var FileConversationStore = class {
|
|
|
3509
3953
|
this.loaded = true;
|
|
3510
3954
|
const { indexPath } = await this.resolvePaths();
|
|
3511
3955
|
try {
|
|
3512
|
-
const raw = await
|
|
3956
|
+
const raw = await readFile7(indexPath, "utf8");
|
|
3513
3957
|
const parsed = JSON.parse(raw);
|
|
3514
3958
|
for (const conversation of parsed.conversations ?? []) {
|
|
3515
3959
|
this.conversations.set(conversation.conversationId, conversation);
|
|
@@ -3522,7 +3966,7 @@ var FileConversationStore = class {
|
|
|
3522
3966
|
const { conversationsDir } = await this.resolvePaths();
|
|
3523
3967
|
const existing = this.conversations.get(conversation.conversationId);
|
|
3524
3968
|
const fileName = existing?.fileName ?? this.resolveConversationFileName(conversation);
|
|
3525
|
-
const filePath =
|
|
3969
|
+
const filePath = resolve9(conversationsDir, fileName);
|
|
3526
3970
|
this.writing = this.writing.then(async () => {
|
|
3527
3971
|
await writeJsonAtomic2(filePath, conversation);
|
|
3528
3972
|
this.conversations.set(conversation.conversationId, {
|
|
@@ -3601,7 +4045,7 @@ var FileConversationStore = class {
|
|
|
3601
4045
|
if (removed) {
|
|
3602
4046
|
this.writing = this.writing.then(async () => {
|
|
3603
4047
|
if (existing) {
|
|
3604
|
-
await
|
|
4048
|
+
await rm2(resolve9(conversationsDir, existing.fileName), { force: true });
|
|
3605
4049
|
}
|
|
3606
4050
|
await this.writeIndex();
|
|
3607
4051
|
});
|
|
@@ -3631,7 +4075,7 @@ var FileStateStore = class {
|
|
|
3631
4075
|
workingDir: this.workingDir,
|
|
3632
4076
|
agentId: this.agentId
|
|
3633
4077
|
});
|
|
3634
|
-
this.filePath =
|
|
4078
|
+
this.filePath = resolve9(getAgentStoreDirectory(identity), LOCAL_STATE_FILE);
|
|
3635
4079
|
}
|
|
3636
4080
|
isExpired(state) {
|
|
3637
4081
|
return typeof this.ttlMs === "number" && Date.now() - state.updatedAt > this.ttlMs;
|
|
@@ -3643,7 +4087,7 @@ var FileStateStore = class {
|
|
|
3643
4087
|
}
|
|
3644
4088
|
this.loaded = true;
|
|
3645
4089
|
try {
|
|
3646
|
-
const raw = await
|
|
4090
|
+
const raw = await readFile7(this.filePath, "utf8");
|
|
3647
4091
|
const parsed = JSON.parse(raw);
|
|
3648
4092
|
for (const state of parsed.states ?? []) {
|
|
3649
4093
|
this.states.set(state.runId, state);
|
|
@@ -4319,9 +4763,13 @@ export {
|
|
|
4319
4763
|
InMemoryStateStore,
|
|
4320
4764
|
LatitudeCapture,
|
|
4321
4765
|
LocalMcpBridge,
|
|
4766
|
+
LocalUploadStore,
|
|
4767
|
+
PONCHO_UPLOAD_SCHEME,
|
|
4768
|
+
S3UploadStore,
|
|
4322
4769
|
STORAGE_SCHEMA_VERSION,
|
|
4323
4770
|
TelemetryEmitter,
|
|
4324
4771
|
ToolDispatcher,
|
|
4772
|
+
VercelBlobUploadStore,
|
|
4325
4773
|
buildAgentDirectoryName,
|
|
4326
4774
|
buildSkillContextWindow,
|
|
4327
4775
|
createConversationStore,
|
|
@@ -4331,8 +4779,10 @@ export {
|
|
|
4331
4779
|
createModelProvider,
|
|
4332
4780
|
createSkillTools,
|
|
4333
4781
|
createStateStore,
|
|
4782
|
+
createUploadStore,
|
|
4334
4783
|
createWriteTool,
|
|
4335
4784
|
defineTool4 as defineTool,
|
|
4785
|
+
deriveUploadKey,
|
|
4336
4786
|
ensureAgentIdentity,
|
|
4337
4787
|
generateAgentId,
|
|
4338
4788
|
getAgentStoreDirectory,
|