@poncho-ai/harness 0.43.1 → 0.44.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 +75 -0
- package/dist/index.d.ts +96 -45
- package/dist/index.js +365 -97
- package/package.json +1 -1
- package/src/harness.ts +32 -2
- package/src/orchestrator/run-conversation-turn.ts +2 -2
- package/src/storage/schema.ts +19 -0
- package/src/storage/sql-dialect.ts +5 -2
- package/src/upload-store.ts +27 -0
- package/src/vfs/bash-manager.ts +6 -3
- package/src/vfs/poncho-fs-adapter.ts +333 -51
- package/test/upload-store-decode.test.ts +43 -0
- package/test/vfs.test.ts +111 -0
package/dist/index.js
CHANGED
|
@@ -2340,7 +2340,7 @@ var ponchoDocsTool = defineTool({
|
|
|
2340
2340
|
|
|
2341
2341
|
// src/harness.ts
|
|
2342
2342
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
2343
|
-
import { readFile as
|
|
2343
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
2344
2344
|
import { resolve as resolve11 } from "path";
|
|
2345
2345
|
import { defineTool as defineTool12, getTextContent as getTextContent2, createLogger as createLogger6, formatError as fmtErr, url as urlColor } from "@poncho-ai/sdk";
|
|
2346
2346
|
|
|
@@ -2422,6 +2422,23 @@ var deriveUploadKey = (data, mediaType) => {
|
|
|
2422
2422
|
const ext = mimeToExt(mediaType);
|
|
2423
2423
|
return `${hash}${ext}`;
|
|
2424
2424
|
};
|
|
2425
|
+
var DATA_URI_PREFIX = /^data:[^,]*?;base64,/;
|
|
2426
|
+
var decodeFileInputData = async (data) => {
|
|
2427
|
+
if (data.startsWith("http://") || data.startsWith("https://")) {
|
|
2428
|
+
const resp = await fetch(data);
|
|
2429
|
+
if (!resp.ok) {
|
|
2430
|
+
throw new Error(
|
|
2431
|
+
`uploads: failed to fetch file at ${data}: ${resp.status} ${resp.statusText}`
|
|
2432
|
+
);
|
|
2433
|
+
}
|
|
2434
|
+
return Buffer.from(await resp.arrayBuffer());
|
|
2435
|
+
}
|
|
2436
|
+
const match = data.match(DATA_URI_PREFIX);
|
|
2437
|
+
if (match) {
|
|
2438
|
+
return Buffer.from(data.slice(match[0].length), "base64");
|
|
2439
|
+
}
|
|
2440
|
+
return Buffer.from(data, "base64");
|
|
2441
|
+
};
|
|
2425
2442
|
var MIME_EXT_MAP = {
|
|
2426
2443
|
"image/jpeg": ".jpg",
|
|
2427
2444
|
"image/png": ".png",
|
|
@@ -2470,9 +2487,9 @@ var VercelBlobUploadStore = class {
|
|
|
2470
2487
|
sdk;
|
|
2471
2488
|
workingDir;
|
|
2472
2489
|
access;
|
|
2473
|
-
constructor(workingDir,
|
|
2490
|
+
constructor(workingDir, access4 = "public") {
|
|
2474
2491
|
this.workingDir = workingDir;
|
|
2475
|
-
this.access =
|
|
2492
|
+
this.access = access4;
|
|
2476
2493
|
}
|
|
2477
2494
|
async loadSdk() {
|
|
2478
2495
|
if (this.sdk) return this.sdk;
|
|
@@ -3319,6 +3336,25 @@ var migrations = [
|
|
|
3319
3336
|
ON conversations (parent_conversation_id, parent_message_id)
|
|
3320
3337
|
WHERE parent_message_id IS NOT NULL`
|
|
3321
3338
|
]
|
|
3339
|
+
},
|
|
3340
|
+
{
|
|
3341
|
+
version: 7,
|
|
3342
|
+
name: "fix_reminder_scheduled_at_precision",
|
|
3343
|
+
// Postgres maps `REAL` to float4 (4 bytes, ~7 digit precision),
|
|
3344
|
+
// which silently rounds millisecond epochs (13 digits) — every
|
|
3345
|
+
// reminder write+read on Postgres returned a different value than
|
|
3346
|
+
// it stored. SQLite's REAL is float8 (15+ digit precision) so it
|
|
3347
|
+
// was always fine there.
|
|
3348
|
+
//
|
|
3349
|
+
// Convert the Postgres column to BIGINT so future writes are exact
|
|
3350
|
+
// (already-stored rounded values aren't recoverable but they were
|
|
3351
|
+
// never correct in the first place).
|
|
3352
|
+
up: (d) => {
|
|
3353
|
+
if (d === "sqlite") return [];
|
|
3354
|
+
return [
|
|
3355
|
+
`ALTER TABLE reminders ALTER COLUMN scheduled_at TYPE BIGINT USING scheduled_at::bigint`
|
|
3356
|
+
];
|
|
3357
|
+
}
|
|
3322
3358
|
}
|
|
3323
3359
|
];
|
|
3324
3360
|
|
|
@@ -3982,9 +4018,9 @@ var SqlStorageEngine = class {
|
|
|
3982
4018
|
}
|
|
3983
4019
|
},
|
|
3984
4020
|
deleteFile: async (tenantId, path) => {
|
|
3985
|
-
const
|
|
3986
|
-
if (!
|
|
3987
|
-
if (
|
|
4021
|
+
const stat4 = await this.vfs.stat(tenantId, path);
|
|
4022
|
+
if (!stat4) throw new Error(`ENOENT: no such file or directory, unlink '${path}'`);
|
|
4023
|
+
if (stat4.type === "directory") throw new Error(`EISDIR: illegal operation on a directory, unlink '${path}'`);
|
|
3988
4024
|
await this.executor.run(
|
|
3989
4025
|
rewrite(
|
|
3990
4026
|
"DELETE FROM vfs_entries WHERE agent_id = $1 AND tenant_id = $2 AND path = $3",
|
|
@@ -4224,7 +4260,10 @@ var SqlStorageEngine = class {
|
|
|
4224
4260
|
return {
|
|
4225
4261
|
id: row.id,
|
|
4226
4262
|
task: row.task,
|
|
4227
|
-
|
|
4263
|
+
// Postgres-js returns BIGINT columns as strings to avoid silent
|
|
4264
|
+
// precision loss in JS. Coerce to Number — ms epochs max out at
|
|
4265
|
+
// ~10^16 in year 2286, well under Number.MAX_SAFE_INTEGER (2^53).
|
|
4266
|
+
scheduledAt: Number(row.scheduled_at),
|
|
4228
4267
|
timezone: row.timezone ?? void 0,
|
|
4229
4268
|
status: row.status,
|
|
4230
4269
|
createdAt: new Date(row.created_at).getTime(),
|
|
@@ -4232,7 +4271,7 @@ var SqlStorageEngine = class {
|
|
|
4232
4271
|
ownerId: row.owner_id ?? void 0,
|
|
4233
4272
|
tenantId: tid === DEFAULT_TENANT2 ? null : tid,
|
|
4234
4273
|
recurrence,
|
|
4235
|
-
occurrenceCount: row.occurrence_count ?? 0
|
|
4274
|
+
occurrenceCount: Number(row.occurrence_count ?? 0)
|
|
4236
4275
|
};
|
|
4237
4276
|
}
|
|
4238
4277
|
toUint8Array(value) {
|
|
@@ -4528,6 +4567,9 @@ function createReminderStoreFromEngine(engine) {
|
|
|
4528
4567
|
import { Bash } from "just-bash";
|
|
4529
4568
|
|
|
4530
4569
|
// src/vfs/poncho-fs-adapter.ts
|
|
4570
|
+
import * as nodeFs from "fs/promises";
|
|
4571
|
+
import * as nodeFsSync from "fs";
|
|
4572
|
+
import * as nodePath from "path";
|
|
4531
4573
|
var MIME_MAP = {
|
|
4532
4574
|
".txt": "text/plain",
|
|
4533
4575
|
".html": "text/html",
|
|
@@ -4584,52 +4626,190 @@ var normalize = (path) => {
|
|
|
4584
4626
|
}
|
|
4585
4627
|
return "/" + out.join("/");
|
|
4586
4628
|
};
|
|
4629
|
+
var READ_ONLY_ERROR = (path, op) => new Error(`EROFS: read-only mount, ${op} '${path}'`);
|
|
4587
4630
|
var PonchoFsAdapter = class {
|
|
4588
|
-
constructor(engine, tenantId, limits) {
|
|
4631
|
+
constructor(engine, tenantId, limits, mounts = []) {
|
|
4589
4632
|
this.engine = engine;
|
|
4590
4633
|
this.tenantId = tenantId;
|
|
4591
4634
|
this.limits = limits;
|
|
4635
|
+
this.mounts = mounts.map((m) => {
|
|
4636
|
+
const prefix = m.prefix.endsWith("/") ? m.prefix : m.prefix + "/";
|
|
4637
|
+
return {
|
|
4638
|
+
prefix,
|
|
4639
|
+
prefixNoSlash: prefix.slice(0, -1),
|
|
4640
|
+
source: m.source.replace(/\/+$/, "")
|
|
4641
|
+
};
|
|
4642
|
+
});
|
|
4643
|
+
}
|
|
4644
|
+
mounts;
|
|
4645
|
+
/** Find which mount, if any, a normalised VFS path falls under.
|
|
4646
|
+
* Returns the relative path within the mount's source dir (empty string
|
|
4647
|
+
* when the path is exactly the mount root). */
|
|
4648
|
+
routeToMount(np) {
|
|
4649
|
+
for (const m of this.mounts) {
|
|
4650
|
+
if (np === m.prefixNoSlash) return { mount: m, relative: "" };
|
|
4651
|
+
if (np.startsWith(m.prefix)) return { mount: m, relative: np.slice(m.prefix.length) };
|
|
4652
|
+
}
|
|
4653
|
+
return null;
|
|
4654
|
+
}
|
|
4655
|
+
/** Treat `np` as a directory and return mount-root segments that should be
|
|
4656
|
+
* listed as virtual subdirectories. E.g. with mount "/system/", reading
|
|
4657
|
+
* "/" returns ["system"]; reading "/system" goes via routeToMount and
|
|
4658
|
+
* serves from local FS instead. */
|
|
4659
|
+
virtualChildrenAt(np) {
|
|
4660
|
+
const dirPrefix = np === "/" ? "/" : np + "/";
|
|
4661
|
+
const out = [];
|
|
4662
|
+
for (const m of this.mounts) {
|
|
4663
|
+
if (m.prefix.startsWith(dirPrefix) && m.prefix !== dirPrefix) {
|
|
4664
|
+
const remaining = m.prefix.slice(dirPrefix.length);
|
|
4665
|
+
const seg = remaining.split("/")[0];
|
|
4666
|
+
if (seg && !out.includes(seg)) out.push(seg);
|
|
4667
|
+
}
|
|
4668
|
+
}
|
|
4669
|
+
return out;
|
|
4670
|
+
}
|
|
4671
|
+
toLocal(mount, relative) {
|
|
4672
|
+
return nodePath.join(mount.source, relative);
|
|
4673
|
+
}
|
|
4674
|
+
/** Build an FsStat from a node fs.Stats. */
|
|
4675
|
+
toFsStat(s) {
|
|
4676
|
+
return {
|
|
4677
|
+
isFile: s.isFile(),
|
|
4678
|
+
isDirectory: s.isDirectory(),
|
|
4679
|
+
isSymbolicLink: s.isSymbolicLink(),
|
|
4680
|
+
mode: s.mode,
|
|
4681
|
+
size: s.size,
|
|
4682
|
+
mtime: s.mtime
|
|
4683
|
+
};
|
|
4684
|
+
}
|
|
4685
|
+
/** Synthesise a directory stat for a virtual ancestor (e.g. "/system"
|
|
4686
|
+
* when "/system/jobs/" is mounted but "/system" itself isn't a real dir
|
|
4687
|
+
* on disk). Used so `ls /` and `stat /system` work without surprises. */
|
|
4688
|
+
syntheticDirStat() {
|
|
4689
|
+
return {
|
|
4690
|
+
isFile: false,
|
|
4691
|
+
isDirectory: true,
|
|
4692
|
+
isSymbolicLink: false,
|
|
4693
|
+
mode: 493,
|
|
4694
|
+
size: 0,
|
|
4695
|
+
mtime: /* @__PURE__ */ new Date(0)
|
|
4696
|
+
};
|
|
4592
4697
|
}
|
|
4593
4698
|
// --- Reads ---
|
|
4594
4699
|
async readFile(path, _options) {
|
|
4595
|
-
const
|
|
4700
|
+
const np = normalize(path);
|
|
4701
|
+
const route = this.routeToMount(np);
|
|
4702
|
+
if (route) {
|
|
4703
|
+
const buf2 = await nodeFs.readFile(this.toLocal(route.mount, route.relative));
|
|
4704
|
+
return buf2.toString("utf8");
|
|
4705
|
+
}
|
|
4706
|
+
const buf = await this.engine.vfs.readFile(this.tenantId, np);
|
|
4596
4707
|
return new TextDecoder().decode(buf);
|
|
4597
4708
|
}
|
|
4598
4709
|
async readFileBuffer(path) {
|
|
4599
|
-
|
|
4710
|
+
const np = normalize(path);
|
|
4711
|
+
const route = this.routeToMount(np);
|
|
4712
|
+
if (route) {
|
|
4713
|
+
const buf = await nodeFs.readFile(this.toLocal(route.mount, route.relative));
|
|
4714
|
+
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
4715
|
+
}
|
|
4716
|
+
return this.engine.vfs.readFile(this.tenantId, np);
|
|
4600
4717
|
}
|
|
4601
4718
|
async exists(path) {
|
|
4602
|
-
const
|
|
4603
|
-
|
|
4719
|
+
const np = normalize(path);
|
|
4720
|
+
const route = this.routeToMount(np);
|
|
4721
|
+
if (route) {
|
|
4722
|
+
try {
|
|
4723
|
+
await nodeFs.access(this.toLocal(route.mount, route.relative));
|
|
4724
|
+
return true;
|
|
4725
|
+
} catch {
|
|
4726
|
+
return false;
|
|
4727
|
+
}
|
|
4728
|
+
}
|
|
4729
|
+
const s = await this.engine.vfs.stat(this.tenantId, np);
|
|
4730
|
+
if (s) return true;
|
|
4731
|
+
return this.virtualChildrenAt(np).length > 0;
|
|
4604
4732
|
}
|
|
4605
4733
|
async stat(path) {
|
|
4606
4734
|
const np = normalize(path);
|
|
4735
|
+
const route = this.routeToMount(np);
|
|
4736
|
+
if (route) {
|
|
4737
|
+
try {
|
|
4738
|
+
const s2 = await nodeFs.stat(this.toLocal(route.mount, route.relative));
|
|
4739
|
+
return this.toFsStat(s2);
|
|
4740
|
+
} catch {
|
|
4741
|
+
throw new Error(`ENOENT: no such file or directory, stat '${path}'`);
|
|
4742
|
+
}
|
|
4743
|
+
}
|
|
4607
4744
|
const s = await this.engine.vfs.stat(this.tenantId, np);
|
|
4608
|
-
if (
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4745
|
+
if (s) {
|
|
4746
|
+
return {
|
|
4747
|
+
isFile: s.type === "file",
|
|
4748
|
+
isDirectory: s.type === "directory",
|
|
4749
|
+
isSymbolicLink: s.type === "symlink",
|
|
4750
|
+
mode: s.mode,
|
|
4751
|
+
size: s.size,
|
|
4752
|
+
mtime: new Date(s.updatedAt)
|
|
4753
|
+
};
|
|
4754
|
+
}
|
|
4755
|
+
if (this.virtualChildrenAt(np).length > 0) return this.syntheticDirStat();
|
|
4756
|
+
throw new Error(`ENOENT: no such file or directory, stat '${path}'`);
|
|
4617
4757
|
}
|
|
4618
4758
|
async readdir(path) {
|
|
4619
|
-
const
|
|
4620
|
-
|
|
4759
|
+
const np = normalize(path);
|
|
4760
|
+
const route = this.routeToMount(np);
|
|
4761
|
+
if (route) {
|
|
4762
|
+
return nodeFs.readdir(this.toLocal(route.mount, route.relative));
|
|
4763
|
+
}
|
|
4764
|
+
let engineNames = [];
|
|
4765
|
+
try {
|
|
4766
|
+
const entries = await this.engine.vfs.readdir(this.tenantId, np);
|
|
4767
|
+
engineNames = entries.map((e) => e.name);
|
|
4768
|
+
} catch {
|
|
4769
|
+
}
|
|
4770
|
+
const virtualSegs = this.virtualChildrenAt(np);
|
|
4771
|
+
if (virtualSegs.length === 0) return engineNames;
|
|
4772
|
+
const merged = new Set(engineNames);
|
|
4773
|
+
for (const seg of virtualSegs) merged.add(seg);
|
|
4774
|
+
return Array.from(merged);
|
|
4621
4775
|
}
|
|
4622
4776
|
async readdirWithFileTypes(path) {
|
|
4623
|
-
const
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4777
|
+
const np = normalize(path);
|
|
4778
|
+
const route = this.routeToMount(np);
|
|
4779
|
+
if (route) {
|
|
4780
|
+
const entries = await nodeFs.readdir(this.toLocal(route.mount, route.relative), { withFileTypes: true });
|
|
4781
|
+
return entries.map((e) => ({
|
|
4782
|
+
name: e.name,
|
|
4783
|
+
isFile: e.isFile(),
|
|
4784
|
+
isDirectory: e.isDirectory(),
|
|
4785
|
+
isSymbolicLink: e.isSymbolicLink()
|
|
4786
|
+
}));
|
|
4787
|
+
}
|
|
4788
|
+
let engineEntries = [];
|
|
4789
|
+
try {
|
|
4790
|
+
const entries = await this.engine.vfs.readdir(this.tenantId, np);
|
|
4791
|
+
engineEntries = entries.map((e) => ({
|
|
4792
|
+
name: e.name,
|
|
4793
|
+
isFile: e.type === "file",
|
|
4794
|
+
isDirectory: e.type === "directory",
|
|
4795
|
+
isSymbolicLink: e.type === "symlink"
|
|
4796
|
+
}));
|
|
4797
|
+
} catch {
|
|
4798
|
+
}
|
|
4799
|
+
const virtualSegs = this.virtualChildrenAt(np);
|
|
4800
|
+
if (virtualSegs.length === 0) return engineEntries;
|
|
4801
|
+
const seen = new Set(engineEntries.map((e) => e.name));
|
|
4802
|
+
for (const seg of virtualSegs) {
|
|
4803
|
+
if (!seen.has(seg)) {
|
|
4804
|
+
engineEntries.push({ name: seg, isFile: false, isDirectory: true, isSymbolicLink: false });
|
|
4805
|
+
}
|
|
4806
|
+
}
|
|
4807
|
+
return engineEntries;
|
|
4630
4808
|
}
|
|
4631
4809
|
// --- Writes ---
|
|
4632
4810
|
async writeFile(path, content, _options) {
|
|
4811
|
+
const np = normalize(path);
|
|
4812
|
+
if (this.routeToMount(np)) throw READ_ONLY_ERROR(path, "writeFile");
|
|
4633
4813
|
const buf = typeof content === "string" ? new TextEncoder().encode(content) : content;
|
|
4634
4814
|
if (buf.byteLength > this.limits.maxFileSize) {
|
|
4635
4815
|
throw new Error(
|
|
@@ -4637,17 +4817,22 @@ var PonchoFsAdapter = class {
|
|
|
4637
4817
|
);
|
|
4638
4818
|
}
|
|
4639
4819
|
const mime = mimeFromExtension(path);
|
|
4640
|
-
await this.engine.vfs.writeFile(this.tenantId,
|
|
4820
|
+
await this.engine.vfs.writeFile(this.tenantId, np, buf, mime);
|
|
4641
4821
|
}
|
|
4642
4822
|
async appendFile(path, content, _options) {
|
|
4823
|
+
const np = normalize(path);
|
|
4824
|
+
if (this.routeToMount(np)) throw READ_ONLY_ERROR(path, "appendFile");
|
|
4643
4825
|
const buf = typeof content === "string" ? new TextEncoder().encode(content) : content;
|
|
4644
|
-
await this.engine.vfs.appendFile(this.tenantId,
|
|
4826
|
+
await this.engine.vfs.appendFile(this.tenantId, np, buf);
|
|
4645
4827
|
}
|
|
4646
4828
|
async mkdir(path, options) {
|
|
4647
|
-
|
|
4829
|
+
const np = normalize(path);
|
|
4830
|
+
if (this.routeToMount(np)) throw READ_ONLY_ERROR(path, "mkdir");
|
|
4831
|
+
await this.engine.vfs.mkdir(this.tenantId, np, options?.recursive);
|
|
4648
4832
|
}
|
|
4649
4833
|
async rm(path, options) {
|
|
4650
4834
|
const np = normalize(path);
|
|
4835
|
+
if (this.routeToMount(np)) throw READ_ONLY_ERROR(path, "rm");
|
|
4651
4836
|
const s = await this.engine.vfs.stat(this.tenantId, np);
|
|
4652
4837
|
if (!s) {
|
|
4653
4838
|
if (options?.force) return;
|
|
@@ -4663,25 +4848,30 @@ var PonchoFsAdapter = class {
|
|
|
4663
4848
|
async cp(src, dest, options) {
|
|
4664
4849
|
const srcNorm = normalize(src);
|
|
4665
4850
|
const destNorm = normalize(dest);
|
|
4666
|
-
|
|
4851
|
+
if (this.routeToMount(destNorm)) throw READ_ONLY_ERROR(dest, "cp");
|
|
4852
|
+
const srcStat = await this.stat(srcNorm).catch(() => null);
|
|
4667
4853
|
if (!srcStat) throw new Error(`ENOENT: no such file or directory, cp '${src}'`);
|
|
4668
|
-
if (srcStat.
|
|
4854
|
+
if (srcStat.isDirectory) {
|
|
4669
4855
|
if (!options?.recursive) {
|
|
4670
4856
|
throw new Error(`EISDIR: cp -r not specified; omitting directory '${src}'`);
|
|
4671
4857
|
}
|
|
4672
4858
|
await this.engine.vfs.mkdir(this.tenantId, destNorm, true);
|
|
4673
|
-
const entries = await this.
|
|
4674
|
-
for (const
|
|
4675
|
-
await this.cp(`${srcNorm}/${
|
|
4859
|
+
const entries = await this.readdir(srcNorm);
|
|
4860
|
+
for (const name of entries) {
|
|
4861
|
+
await this.cp(`${srcNorm}/${name}`, `${destNorm}/${name}`, options);
|
|
4676
4862
|
}
|
|
4677
4863
|
} else {
|
|
4678
|
-
const content = await this.
|
|
4864
|
+
const content = await this.readFileBuffer(srcNorm);
|
|
4679
4865
|
const mime = mimeFromExtension(destNorm);
|
|
4680
4866
|
await this.engine.vfs.writeFile(this.tenantId, destNorm, content, mime);
|
|
4681
4867
|
}
|
|
4682
4868
|
}
|
|
4683
4869
|
async mv(src, dest) {
|
|
4684
|
-
|
|
4870
|
+
const srcNorm = normalize(src);
|
|
4871
|
+
const destNorm = normalize(dest);
|
|
4872
|
+
if (this.routeToMount(srcNorm)) throw READ_ONLY_ERROR(src, "mv (source)");
|
|
4873
|
+
if (this.routeToMount(destNorm)) throw READ_ONLY_ERROR(dest, "mv (dest)");
|
|
4874
|
+
await this.engine.vfs.rename(this.tenantId, srcNorm, destNorm);
|
|
4685
4875
|
}
|
|
4686
4876
|
// --- Path resolution ---
|
|
4687
4877
|
resolvePath(base, path) {
|
|
@@ -4690,8 +4880,22 @@ var PonchoFsAdapter = class {
|
|
|
4690
4880
|
}
|
|
4691
4881
|
async realpath(path) {
|
|
4692
4882
|
const np = normalize(path);
|
|
4883
|
+
const route = this.routeToMount(np);
|
|
4884
|
+
if (route) {
|
|
4885
|
+
const localResolved = await nodeFs.realpath(this.toLocal(route.mount, route.relative));
|
|
4886
|
+
const localRoot = await nodeFs.realpath(route.mount.source).catch(() => route.mount.source);
|
|
4887
|
+
if (localResolved === localRoot) return route.mount.prefixNoSlash;
|
|
4888
|
+
if (localResolved.startsWith(localRoot + nodePath.sep)) {
|
|
4889
|
+
const rel = localResolved.slice(localRoot.length + 1).split(nodePath.sep).join("/");
|
|
4890
|
+
return `${route.mount.prefix}${rel}`;
|
|
4891
|
+
}
|
|
4892
|
+
return np;
|
|
4893
|
+
}
|
|
4693
4894
|
const s = await this.engine.vfs.lstat(this.tenantId, np);
|
|
4694
|
-
if (!s)
|
|
4895
|
+
if (!s) {
|
|
4896
|
+
if (this.virtualChildrenAt(np).length > 0) return np;
|
|
4897
|
+
throw new Error(`ENOENT: no such file or directory, realpath '${path}'`);
|
|
4898
|
+
}
|
|
4695
4899
|
if (s.type === "symlink" && s.symlinkTarget) {
|
|
4696
4900
|
const target = s.symlinkTarget.startsWith("/") ? s.symlinkTarget : normalize(`${np.slice(0, np.lastIndexOf("/"))}/${s.symlinkTarget}`);
|
|
4697
4901
|
return this.realpath(target);
|
|
@@ -4700,39 +4904,92 @@ var PonchoFsAdapter = class {
|
|
|
4700
4904
|
}
|
|
4701
4905
|
// --- Sync: required by just-bash for glob/find ---
|
|
4702
4906
|
getAllPaths() {
|
|
4703
|
-
|
|
4907
|
+
const enginePaths = this.engine.vfs.listAllPaths(this.tenantId);
|
|
4908
|
+
if (this.mounts.length === 0) return enginePaths;
|
|
4909
|
+
const out = new Set(enginePaths);
|
|
4910
|
+
for (const m of this.mounts) {
|
|
4911
|
+
out.add(m.prefixNoSlash);
|
|
4912
|
+
try {
|
|
4913
|
+
const stack = [
|
|
4914
|
+
{ abs: m.source, vfs: m.prefixNoSlash }
|
|
4915
|
+
];
|
|
4916
|
+
while (stack.length > 0) {
|
|
4917
|
+
const { abs, vfs } = stack.pop();
|
|
4918
|
+
let entries;
|
|
4919
|
+
try {
|
|
4920
|
+
entries = nodeFsSync.readdirSync(abs, { withFileTypes: true });
|
|
4921
|
+
} catch {
|
|
4922
|
+
continue;
|
|
4923
|
+
}
|
|
4924
|
+
for (const e of entries) {
|
|
4925
|
+
const childVfs = `${vfs}/${e.name}`;
|
|
4926
|
+
out.add(childVfs);
|
|
4927
|
+
if (e.isDirectory()) {
|
|
4928
|
+
stack.push({ abs: nodePath.join(abs, e.name), vfs: childVfs });
|
|
4929
|
+
}
|
|
4930
|
+
}
|
|
4931
|
+
}
|
|
4932
|
+
} catch {
|
|
4933
|
+
}
|
|
4934
|
+
}
|
|
4935
|
+
return Array.from(out);
|
|
4704
4936
|
}
|
|
4705
4937
|
// --- Metadata ---
|
|
4706
4938
|
async chmod(path, mode) {
|
|
4707
|
-
|
|
4939
|
+
const np = normalize(path);
|
|
4940
|
+
if (this.routeToMount(np)) throw READ_ONLY_ERROR(path, "chmod");
|
|
4941
|
+
await this.engine.vfs.chmod(this.tenantId, np, mode);
|
|
4708
4942
|
}
|
|
4709
4943
|
async utimes(path, _atime, mtime) {
|
|
4710
|
-
|
|
4944
|
+
const np = normalize(path);
|
|
4945
|
+
if (this.routeToMount(np)) throw READ_ONLY_ERROR(path, "utimes");
|
|
4946
|
+
await this.engine.vfs.utimes(this.tenantId, np, mtime);
|
|
4711
4947
|
}
|
|
4712
4948
|
// --- Symlinks ---
|
|
4713
4949
|
async symlink(target, linkPath) {
|
|
4714
|
-
|
|
4950
|
+
const np = normalize(linkPath);
|
|
4951
|
+
if (this.routeToMount(np)) throw READ_ONLY_ERROR(linkPath, "symlink");
|
|
4952
|
+
await this.engine.vfs.symlink(this.tenantId, target, np);
|
|
4715
4953
|
}
|
|
4716
4954
|
async link(existingPath, newPath) {
|
|
4717
|
-
const
|
|
4955
|
+
const npNew = normalize(newPath);
|
|
4956
|
+
if (this.routeToMount(npNew)) throw READ_ONLY_ERROR(newPath, "link");
|
|
4957
|
+
const content = await this.readFileBuffer(existingPath);
|
|
4718
4958
|
const mime = mimeFromExtension(newPath);
|
|
4719
|
-
await this.engine.vfs.writeFile(this.tenantId,
|
|
4959
|
+
await this.engine.vfs.writeFile(this.tenantId, npNew, content, mime);
|
|
4720
4960
|
}
|
|
4721
4961
|
async readlink(path) {
|
|
4722
|
-
|
|
4962
|
+
const np = normalize(path);
|
|
4963
|
+
const route = this.routeToMount(np);
|
|
4964
|
+
if (route) {
|
|
4965
|
+
return nodeFs.readlink(this.toLocal(route.mount, route.relative));
|
|
4966
|
+
}
|
|
4967
|
+
return this.engine.vfs.readlink(this.tenantId, np);
|
|
4723
4968
|
}
|
|
4724
4969
|
async lstat(path) {
|
|
4725
4970
|
const np = normalize(path);
|
|
4971
|
+
const route = this.routeToMount(np);
|
|
4972
|
+
if (route) {
|
|
4973
|
+
try {
|
|
4974
|
+
const s2 = await nodeFs.lstat(this.toLocal(route.mount, route.relative));
|
|
4975
|
+
return this.toFsStat(s2);
|
|
4976
|
+
} catch {
|
|
4977
|
+
throw new Error(`ENOENT: no such file or directory, lstat '${path}'`);
|
|
4978
|
+
}
|
|
4979
|
+
}
|
|
4726
4980
|
const s = await this.engine.vfs.lstat(this.tenantId, np);
|
|
4727
|
-
if (
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4981
|
+
if (s) {
|
|
4982
|
+
return {
|
|
4983
|
+
isFile: s.type === "file",
|
|
4984
|
+
isDirectory: s.type === "directory",
|
|
4985
|
+
isSymbolicLink: s.type === "symlink",
|
|
4986
|
+
mode: s.mode,
|
|
4987
|
+
size: s.size,
|
|
4988
|
+
mtime: new Date(s.updatedAt)
|
|
4989
|
+
};
|
|
4990
|
+
}
|
|
4991
|
+
if (this.virtualChildrenAt(np).length > 0) return this.syntheticDirStat();
|
|
4992
|
+
throw new Error(`ENOENT: no such file or directory, lstat '${path}'`);
|
|
4736
4993
|
}
|
|
4737
4994
|
};
|
|
4738
4995
|
|
|
@@ -4909,21 +5166,23 @@ function toBashOptions(cfg, network) {
|
|
|
4909
5166
|
return opts;
|
|
4910
5167
|
}
|
|
4911
5168
|
var BashEnvironmentManager = class {
|
|
4912
|
-
constructor(engine, limits, workingDir, bashConfig, network) {
|
|
5169
|
+
constructor(engine, limits, workingDir, bashConfig, network, virtualMounts = []) {
|
|
4913
5170
|
this.engine = engine;
|
|
4914
5171
|
this.limits = limits;
|
|
4915
5172
|
this.workingDir = workingDir;
|
|
4916
5173
|
this.bashOptions = toBashOptions(bashConfig, network);
|
|
5174
|
+
this.virtualMounts = virtualMounts;
|
|
4917
5175
|
}
|
|
4918
5176
|
environments = /* @__PURE__ */ new Map();
|
|
4919
5177
|
filesystems = /* @__PURE__ */ new Map();
|
|
4920
5178
|
workingDir;
|
|
4921
5179
|
bashOptions;
|
|
5180
|
+
virtualMounts;
|
|
4922
5181
|
/** Return the combined IFileSystem (VFS + optional /project mount) for a tenant. */
|
|
4923
5182
|
getFs(tenantId) {
|
|
4924
5183
|
let fs = this.filesystems.get(tenantId);
|
|
4925
5184
|
if (!fs) {
|
|
4926
|
-
const adapter = new PonchoFsAdapter(this.engine, tenantId, this.limits);
|
|
5185
|
+
const adapter = new PonchoFsAdapter(this.engine, tenantId, this.limits, this.virtualMounts);
|
|
4927
5186
|
fs = createBashFs(adapter, this.workingDir);
|
|
4928
5187
|
this.filesystems.set(tenantId, fs);
|
|
4929
5188
|
}
|
|
@@ -4943,7 +5202,7 @@ var BashEnvironmentManager = class {
|
|
|
4943
5202
|
return bash;
|
|
4944
5203
|
}
|
|
4945
5204
|
getAdapter(tenantId) {
|
|
4946
|
-
return new PonchoFsAdapter(this.engine, tenantId, this.limits);
|
|
5205
|
+
return new PonchoFsAdapter(this.engine, tenantId, this.limits, this.virtualMounts);
|
|
4947
5206
|
}
|
|
4948
5207
|
/** Refresh the PostgreSQL path cache before a bash.exec() call. */
|
|
4949
5208
|
async refreshPathCache(tenantId) {
|
|
@@ -5062,8 +5321,8 @@ var createReadFileTool = (getFs) => defineTool3({
|
|
|
5062
5321
|
if (!await fs.exists(filePath)) {
|
|
5063
5322
|
throw new Error(`File not found: ${filePath}`);
|
|
5064
5323
|
}
|
|
5065
|
-
const
|
|
5066
|
-
if (
|
|
5324
|
+
const stat4 = await fs.stat(filePath);
|
|
5325
|
+
if (stat4.isDirectory) {
|
|
5067
5326
|
throw new Error(`${filePath} is a directory, not a file`);
|
|
5068
5327
|
}
|
|
5069
5328
|
const mediaType = mimeFromPath(filePath) ?? "application/octet-stream";
|
|
@@ -5115,8 +5374,8 @@ var createEditFileTool = (getFs) => defineTool4({
|
|
|
5115
5374
|
const tenantId = context.tenantId ?? "__default__";
|
|
5116
5375
|
const fs = getFs(tenantId);
|
|
5117
5376
|
if (!await fs.exists(filePath)) throw new Error(`File not found: ${filePath}`);
|
|
5118
|
-
const
|
|
5119
|
-
if (
|
|
5377
|
+
const stat4 = await fs.stat(filePath);
|
|
5378
|
+
if (stat4.isDirectory) throw new Error(`${filePath} is a directory`);
|
|
5120
5379
|
const content = await fs.readFile(filePath);
|
|
5121
5380
|
const first = content.indexOf(oldStr);
|
|
5122
5381
|
if (first === -1) {
|
|
@@ -5611,7 +5870,7 @@ var createTodoTools = (store) => {
|
|
|
5611
5870
|
|
|
5612
5871
|
// src/secrets-store.ts
|
|
5613
5872
|
import { createCipheriv, createDecipheriv, randomBytes as randomBytes2, createHash as createHash3 } from "crypto";
|
|
5614
|
-
import { mkdir as mkdir3, readFile as
|
|
5873
|
+
import { mkdir as mkdir3, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
|
|
5615
5874
|
import { dirname as dirname3, resolve as resolve7 } from "path";
|
|
5616
5875
|
function deriveKey(signingKey) {
|
|
5617
5876
|
return createHash3("sha256").update("poncho-secrets-v1:" + signingKey).digest();
|
|
@@ -5657,7 +5916,7 @@ var FileSecretsStore = class {
|
|
|
5657
5916
|
}
|
|
5658
5917
|
async readAll(tenantId) {
|
|
5659
5918
|
try {
|
|
5660
|
-
const raw = await
|
|
5919
|
+
const raw = await readFile6(await this.filePath(tenantId), "utf8");
|
|
5661
5920
|
return JSON.parse(raw);
|
|
5662
5921
|
} catch {
|
|
5663
5922
|
return {};
|
|
@@ -6567,7 +6826,7 @@ import { createAnthropic } from "@ai-sdk/anthropic";
|
|
|
6567
6826
|
// src/openai-codex-auth.ts
|
|
6568
6827
|
import { homedir as homedir3 } from "os";
|
|
6569
6828
|
import { dirname as dirname4, resolve as resolve8 } from "path";
|
|
6570
|
-
import { mkdir as mkdir4, readFile as
|
|
6829
|
+
import { mkdir as mkdir4, readFile as readFile7, chmod, writeFile as writeFile5, rm as rm3 } from "fs/promises";
|
|
6571
6830
|
var OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
6572
6831
|
var OPENAI_AUTH_ISSUER = "https://auth.openai.com";
|
|
6573
6832
|
var REFRESH_TOKEN_GRACE_MS = 5 * 60 * 1e3;
|
|
@@ -6606,7 +6865,7 @@ var getOpenAICodexAuthFilePath = (config) => {
|
|
|
6606
6865
|
var readOpenAICodexSession = async (config) => {
|
|
6607
6866
|
const filePath = getOpenAICodexAuthFilePath(config);
|
|
6608
6867
|
try {
|
|
6609
|
-
const content = await
|
|
6868
|
+
const content = await readFile7(filePath, "utf8");
|
|
6610
6869
|
const parsed = JSON.parse(content);
|
|
6611
6870
|
if (typeof parsed.refreshToken !== "string" || parsed.refreshToken.length === 0) {
|
|
6612
6871
|
return void 0;
|
|
@@ -6941,7 +7200,7 @@ var createModelProvider = (provider, config) => {
|
|
|
6941
7200
|
};
|
|
6942
7201
|
|
|
6943
7202
|
// src/skill-context.ts
|
|
6944
|
-
import { readFile as
|
|
7203
|
+
import { readFile as readFile8, readdir as readdir3, stat as stat2 } from "fs/promises";
|
|
6945
7204
|
import { dirname as dirname5, resolve as resolve9, normalize as normalize2 } from "path";
|
|
6946
7205
|
import YAML3 from "yaml";
|
|
6947
7206
|
import { createLogger as createLogger4 } from "@poncho-ai/sdk";
|
|
@@ -7022,7 +7281,7 @@ var parseSkillFrontmatter = (content) => {
|
|
|
7022
7281
|
};
|
|
7023
7282
|
};
|
|
7024
7283
|
var collectSkillManifests = async (directory) => {
|
|
7025
|
-
const entries = await
|
|
7284
|
+
const entries = await readdir3(directory, { withFileTypes: true });
|
|
7026
7285
|
const files = [];
|
|
7027
7286
|
for (const entry of entries) {
|
|
7028
7287
|
const fullPath = resolve9(directory, entry.name);
|
|
@@ -7030,7 +7289,7 @@ var collectSkillManifests = async (directory) => {
|
|
|
7030
7289
|
let isFile = entry.isFile();
|
|
7031
7290
|
if (entry.isSymbolicLink()) {
|
|
7032
7291
|
try {
|
|
7033
|
-
const s = await
|
|
7292
|
+
const s = await stat2(fullPath);
|
|
7034
7293
|
isDir = s.isDirectory();
|
|
7035
7294
|
isFile = s.isFile();
|
|
7036
7295
|
} catch {
|
|
@@ -7060,7 +7319,7 @@ var loadSkillMetadata = async (workingDir, extraSkillPaths) => {
|
|
|
7060
7319
|
const seen = /* @__PURE__ */ new Set();
|
|
7061
7320
|
for (const manifest of allManifests) {
|
|
7062
7321
|
try {
|
|
7063
|
-
const content = await
|
|
7322
|
+
const content = await readFile8(manifest, "utf8");
|
|
7064
7323
|
const parsed = parseSkillFrontmatter(content);
|
|
7065
7324
|
if (parsed && !seen.has(parsed.name)) {
|
|
7066
7325
|
seen.add(parsed.name);
|
|
@@ -7163,7 +7422,7 @@ var mergeSkills = (repoSkills, vfsSkills, onCollision) => {
|
|
|
7163
7422
|
var loadSkillInstructions = async (skill, engine) => {
|
|
7164
7423
|
const raw = skill.source.kind === "vfs" ? decoder.decode(
|
|
7165
7424
|
await requireEngine(engine).vfs.readFile(skill.source.tenantId, skill.skillPath)
|
|
7166
|
-
) : await
|
|
7425
|
+
) : await readFile8(skill.skillPath, "utf8");
|
|
7167
7426
|
const match = raw.match(FRONTMATTER_PATTERN3);
|
|
7168
7427
|
return match ? match[2].trim() : raw.trim();
|
|
7169
7428
|
};
|
|
@@ -7184,7 +7443,7 @@ var readSkillResource = async (skill, relativePath, engine) => {
|
|
|
7184
7443
|
if (!fullPath.startsWith(skill.skillDir)) {
|
|
7185
7444
|
throw new Error("Path escapes the skill directory");
|
|
7186
7445
|
}
|
|
7187
|
-
return await
|
|
7446
|
+
return await readFile8(fullPath, "utf8");
|
|
7188
7447
|
};
|
|
7189
7448
|
var requireEngine = (engine) => {
|
|
7190
7449
|
if (!engine) {
|
|
@@ -7312,8 +7571,8 @@ function convertSchema(schema) {
|
|
|
7312
7571
|
|
|
7313
7572
|
// src/skill-tools.ts
|
|
7314
7573
|
import { defineTool as defineTool9 } from "@poncho-ai/sdk";
|
|
7315
|
-
import { access as
|
|
7316
|
-
import { extname, normalize as normalize3, resolve as resolve10, sep as
|
|
7574
|
+
import { access as access3, readdir as readdir4, stat as stat3 } from "fs/promises";
|
|
7575
|
+
import { extname, normalize as normalize3, resolve as resolve10, sep as sep3 } from "path";
|
|
7317
7576
|
import { pathToFileURL } from "url";
|
|
7318
7577
|
import { createJiti as createJiti2 } from "jiti";
|
|
7319
7578
|
var findSkill = async (options, tenantId, name) => {
|
|
@@ -7498,7 +7757,7 @@ var createSkillTools = (options) => {
|
|
|
7498
7757
|
error: `Script "${resolved2.relativePath}" for skill "${name}" is not allowed by policy.`
|
|
7499
7758
|
};
|
|
7500
7759
|
}
|
|
7501
|
-
await
|
|
7760
|
+
await access3(resolved2.fullPath);
|
|
7502
7761
|
const fn2 = await loadRunnableScriptFunction(resolved2.fullPath);
|
|
7503
7762
|
const output2 = await fn2(payload, {
|
|
7504
7763
|
scope: "skill",
|
|
@@ -7514,7 +7773,7 @@ var createSkillTools = (options) => {
|
|
|
7514
7773
|
error: `Script "${resolved.relativePath}" is not allowed by policy.`
|
|
7515
7774
|
};
|
|
7516
7775
|
}
|
|
7517
|
-
await
|
|
7776
|
+
await access3(resolved.fullPath);
|
|
7518
7777
|
const fn = await loadRunnableScriptFunction(resolved.fullPath);
|
|
7519
7778
|
const output = await fn(payload, {
|
|
7520
7779
|
scope: "agent",
|
|
@@ -7534,7 +7793,7 @@ var SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", "
|
|
|
7534
7793
|
var VFS_SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".ts", ".mts"]);
|
|
7535
7794
|
var listRepoSkillScripts = async (skill, isScriptAllowed) => {
|
|
7536
7795
|
const scripts = await collectScriptFiles(skill.skillDir);
|
|
7537
|
-
return scripts.map((fullPath) => fullPath.slice(skill.skillDir.length + 1).split(
|
|
7796
|
+
return scripts.map((fullPath) => fullPath.slice(skill.skillDir.length + 1).split(sep3).join("/")).filter((relativePath) => relativePath.toLowerCase() !== "skill.md").map(
|
|
7538
7797
|
(relativePath) => relativePath.includes("/") ? relativePath : `./${relativePath}`
|
|
7539
7798
|
).filter((path) => isScriptAllowed ? isScriptAllowed(skill.name, path) : true).sort();
|
|
7540
7799
|
};
|
|
@@ -7567,7 +7826,7 @@ var listVfsSkillScripts = async (skill, engine, isScriptAllowed) => {
|
|
|
7567
7826
|
return found.filter((path) => isScriptAllowed ? isScriptAllowed(skill.name, path) : true).sort();
|
|
7568
7827
|
};
|
|
7569
7828
|
var collectScriptFiles = async (directory) => {
|
|
7570
|
-
const entries = await
|
|
7829
|
+
const entries = await readdir4(directory, { withFileTypes: true });
|
|
7571
7830
|
const files = [];
|
|
7572
7831
|
for (const entry of entries) {
|
|
7573
7832
|
if (entry.name === "node_modules") continue;
|
|
@@ -7576,7 +7835,7 @@ var collectScriptFiles = async (directory) => {
|
|
|
7576
7835
|
let isFile = entry.isFile();
|
|
7577
7836
|
if (entry.isSymbolicLink()) {
|
|
7578
7837
|
try {
|
|
7579
|
-
const s = await
|
|
7838
|
+
const s = await stat3(fullPath);
|
|
7580
7839
|
isDir = s.isDirectory();
|
|
7581
7840
|
isFile = s.isFile();
|
|
7582
7841
|
} catch {
|
|
@@ -7598,7 +7857,7 @@ var collectScriptFiles = async (directory) => {
|
|
|
7598
7857
|
};
|
|
7599
7858
|
var normalizeScriptPolicyPath = (relativePath) => {
|
|
7600
7859
|
const trimmed = relativePath.trim();
|
|
7601
|
-
const normalized = normalize3(trimmed).split(
|
|
7860
|
+
const normalized = normalize3(trimmed).split(sep3).join("/");
|
|
7602
7861
|
if (normalized.startsWith("/")) {
|
|
7603
7862
|
throw new Error("Script path must be relative and within the allowed directory");
|
|
7604
7863
|
}
|
|
@@ -7612,7 +7871,7 @@ var resolveScriptPath = (baseDir, relativePath, containmentDir) => {
|
|
|
7612
7871
|
const normalized = normalizeScriptPolicyPath(relativePath);
|
|
7613
7872
|
const fullPath = resolve10(baseDir, normalized);
|
|
7614
7873
|
const boundary = resolve10(containmentDir ?? baseDir);
|
|
7615
|
-
if (!fullPath.startsWith(`${boundary}${
|
|
7874
|
+
if (!fullPath.startsWith(`${boundary}${sep3}`) && fullPath !== boundary) {
|
|
7616
7875
|
throw new Error("Script path must stay inside the allowed directory");
|
|
7617
7876
|
}
|
|
7618
7877
|
const extension = extname(fullPath).toLowerCase();
|
|
@@ -8776,6 +9035,8 @@ var AgentHarness = class _AgentHarness {
|
|
|
8776
9035
|
storageEngine;
|
|
8777
9036
|
/** Bash environment manager (creates per-tenant bash instances). */
|
|
8778
9037
|
bashManager;
|
|
9038
|
+
/** Read-only virtual mounts overlaid on the VFS. Empty by default. */
|
|
9039
|
+
virtualMounts = [];
|
|
8779
9040
|
resolveToolAccess(toolName) {
|
|
8780
9041
|
const tools = this.loadedConfig?.tools;
|
|
8781
9042
|
if (!tools) return true;
|
|
@@ -8789,8 +9050,8 @@ var AgentHarness = class _AgentHarness {
|
|
|
8789
9050
|
return true;
|
|
8790
9051
|
}
|
|
8791
9052
|
isToolEnabled(name) {
|
|
8792
|
-
const
|
|
8793
|
-
if (
|
|
9053
|
+
const access4 = this.resolveToolAccess(name);
|
|
9054
|
+
if (access4 === false) return false;
|
|
8794
9055
|
if (name === "write_file" || name === "edit_file" || name === "delete_file" || name === "delete_directory") {
|
|
8795
9056
|
return this.shouldEnableWriteTool();
|
|
8796
9057
|
}
|
|
@@ -8947,6 +9208,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
8947
9208
|
this.storageEngine = options.storageEngine;
|
|
8948
9209
|
this.injectedStorageEngine = true;
|
|
8949
9210
|
}
|
|
9211
|
+
this.virtualMounts = options.virtualMounts ?? [];
|
|
8950
9212
|
if (options.toolDefinitions?.length) {
|
|
8951
9213
|
this.dispatcher.registerMany(options.toolDefinitions);
|
|
8952
9214
|
}
|
|
@@ -9118,6 +9380,10 @@ var AgentHarness = class _AgentHarness {
|
|
|
9118
9380
|
return this.loadedSkills;
|
|
9119
9381
|
}
|
|
9120
9382
|
const effectiveTenant = tenantId || "__default__";
|
|
9383
|
+
const engineWithRefresh = this.storageEngine;
|
|
9384
|
+
if (typeof engineWithRefresh.refreshPathCache === "function") {
|
|
9385
|
+
await engineWithRefresh.refreshPathCache(effectiveTenant);
|
|
9386
|
+
}
|
|
9121
9387
|
const fingerprint = this.computeVfsSkillFingerprint(effectiveTenant);
|
|
9122
9388
|
const cached = this.skillCache.get(effectiveTenant);
|
|
9123
9389
|
if (cached && cached.fingerprint === fingerprint) {
|
|
@@ -9368,7 +9634,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
9368
9634
|
}
|
|
9369
9635
|
try {
|
|
9370
9636
|
const agentFilePath = resolve11(this.workingDir, "AGENT.md");
|
|
9371
|
-
const rawContent = await
|
|
9637
|
+
const rawContent = await readFile9(agentFilePath, "utf8");
|
|
9372
9638
|
if (rawContent === this.agentFileFingerprint) {
|
|
9373
9639
|
return false;
|
|
9374
9640
|
}
|
|
@@ -9441,7 +9707,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
9441
9707
|
}
|
|
9442
9708
|
} else {
|
|
9443
9709
|
const agentFilePath = resolve11(this.workingDir, "AGENT.md");
|
|
9444
|
-
const agentRawContent = await
|
|
9710
|
+
const agentRawContent = await readFile9(agentFilePath, "utf8");
|
|
9445
9711
|
this.parsedAgent = parseAgentMarkdown(agentRawContent);
|
|
9446
9712
|
this.agentFileFingerprint = agentRawContent;
|
|
9447
9713
|
const identity = await ensureAgentIdentity(this.workingDir);
|
|
@@ -9488,7 +9754,8 @@ var AgentHarness = class _AgentHarness {
|
|
|
9488
9754
|
{ maxFileSize, maxTotalStorage },
|
|
9489
9755
|
bashWorkingDir,
|
|
9490
9756
|
config?.bash,
|
|
9491
|
-
config?.network
|
|
9757
|
+
config?.network,
|
|
9758
|
+
this.virtualMounts
|
|
9492
9759
|
);
|
|
9493
9760
|
this.registerIfMissing(createBashTool(this.bashManager));
|
|
9494
9761
|
const getFs = (tenantId) => this.bashManager.getFs(tenantId);
|
|
@@ -9586,9 +9853,9 @@ var AgentHarness = class _AgentHarness {
|
|
|
9586
9853
|
await writeFile6(filePath, json, "utf8");
|
|
9587
9854
|
},
|
|
9588
9855
|
async load() {
|
|
9589
|
-
const { readFile:
|
|
9856
|
+
const { readFile: readFile10 } = await import("fs/promises");
|
|
9590
9857
|
try {
|
|
9591
|
-
return await
|
|
9858
|
+
return await readFile10(filePath, "utf8");
|
|
9592
9859
|
} catch {
|
|
9593
9860
|
return void 0;
|
|
9594
9861
|
}
|
|
@@ -9607,9 +9874,9 @@ var AgentHarness = class _AgentHarness {
|
|
|
9607
9874
|
await writeFile6(filePath, json, "utf8");
|
|
9608
9875
|
},
|
|
9609
9876
|
async load() {
|
|
9610
|
-
const { readFile:
|
|
9877
|
+
const { readFile: readFile10 } = await import("fs/promises");
|
|
9611
9878
|
try {
|
|
9612
|
-
return await
|
|
9879
|
+
return await readFile10(filePath, "utf8");
|
|
9613
9880
|
} catch {
|
|
9614
9881
|
return void 0;
|
|
9615
9882
|
}
|
|
@@ -9623,12 +9890,12 @@ var AgentHarness = class _AgentHarness {
|
|
|
9623
9890
|
let browserMod;
|
|
9624
9891
|
try {
|
|
9625
9892
|
const { existsSync } = await import("fs");
|
|
9626
|
-
const { join, dirname: dirname6 } = await import("path");
|
|
9893
|
+
const { join: join2, dirname: dirname6 } = await import("path");
|
|
9627
9894
|
const { pathToFileURL: pathToFileURL2 } = await import("url");
|
|
9628
9895
|
let searchDir = this.workingDir;
|
|
9629
9896
|
let entryPath;
|
|
9630
9897
|
for (; ; ) {
|
|
9631
|
-
const candidate =
|
|
9898
|
+
const candidate = join2(searchDir, "node_modules", "@poncho-ai", "browser", "dist", "index.js");
|
|
9632
9899
|
if (existsSync(candidate)) {
|
|
9633
9900
|
entryPath = candidate;
|
|
9634
9901
|
break;
|
|
@@ -9949,7 +10216,7 @@ ${this.skillFingerprint}`;
|
|
|
9949
10216
|
];
|
|
9950
10217
|
for (const file of input.files) {
|
|
9951
10218
|
if (this.uploadStore) {
|
|
9952
|
-
const buf =
|
|
10219
|
+
const buf = await decodeFileInputData(file.data);
|
|
9953
10220
|
const key = deriveUploadKey(buf, file.mediaType);
|
|
9954
10221
|
const ref = await this.uploadStore.put(key, buf, file.mediaType);
|
|
9955
10222
|
parts.push({
|
|
@@ -13064,7 +13331,7 @@ var runConversationTurn = async (opts) => {
|
|
|
13064
13331
|
if (opts.files && opts.files.length > 0 && opts.harness.uploadStore) {
|
|
13065
13332
|
const uploadedParts = await Promise.all(
|
|
13066
13333
|
opts.files.map(async (f) => {
|
|
13067
|
-
const buf =
|
|
13334
|
+
const buf = await decodeFileInputData(f.data);
|
|
13068
13335
|
const key = deriveUploadKey(buf, f.mediaType);
|
|
13069
13336
|
const ref = await opts.harness.uploadStore.put(key, buf, f.mediaType);
|
|
13070
13337
|
return {
|
|
@@ -13418,6 +13685,7 @@ export {
|
|
|
13418
13685
|
createTurnDraftState,
|
|
13419
13686
|
createUploadStore,
|
|
13420
13687
|
createWriteTool,
|
|
13688
|
+
decodeFileInputData,
|
|
13421
13689
|
defaultAgentDefinition,
|
|
13422
13690
|
defineTool13 as defineTool,
|
|
13423
13691
|
deleteOpenAICodexSession,
|