@poncho-ai/harness 0.43.1 → 0.45.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 +118 -0
- package/dist/index.d.ts +140 -47
- package/dist/index.js +575 -103
- package/package.json +2 -2
- package/src/config.ts +20 -1
- package/src/harness.ts +115 -6
- package/src/index.ts +1 -1
- package/src/orchestrator/orchestrator.ts +46 -0
- package/src/orchestrator/run-conversation-turn.ts +48 -2
- package/src/orchestrator/turn.ts +3 -0
- package/src/state.ts +9 -0
- package/src/storage/schema.ts +19 -0
- package/src/storage/sql-dialect.ts +5 -2
- package/src/subagent-manager.ts +20 -0
- package/src/subagent-tools.ts +62 -0
- 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
|
@@ -505,6 +505,13 @@ var compactMessages = async (model, messages, config, options) => {
|
|
|
505
505
|
import { access } from "fs/promises";
|
|
506
506
|
import { resolve as resolve3 } from "path";
|
|
507
507
|
import { createJiti } from "jiti";
|
|
508
|
+
var normalizeToolAccess = (value) => {
|
|
509
|
+
if (value === "approval") return { access: "approval" };
|
|
510
|
+
if (value && typeof value === "object") {
|
|
511
|
+
return { access: value.access, dispatch: value.dispatch };
|
|
512
|
+
}
|
|
513
|
+
return {};
|
|
514
|
+
};
|
|
508
515
|
var resolveTtl = (ttl, key) => {
|
|
509
516
|
if (typeof ttl === "number") {
|
|
510
517
|
return ttl;
|
|
@@ -2340,7 +2347,7 @@ var ponchoDocsTool = defineTool({
|
|
|
2340
2347
|
|
|
2341
2348
|
// src/harness.ts
|
|
2342
2349
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
2343
|
-
import { readFile as
|
|
2350
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
2344
2351
|
import { resolve as resolve11 } from "path";
|
|
2345
2352
|
import { defineTool as defineTool12, getTextContent as getTextContent2, createLogger as createLogger6, formatError as fmtErr, url as urlColor } from "@poncho-ai/sdk";
|
|
2346
2353
|
|
|
@@ -2422,6 +2429,23 @@ var deriveUploadKey = (data, mediaType) => {
|
|
|
2422
2429
|
const ext = mimeToExt(mediaType);
|
|
2423
2430
|
return `${hash}${ext}`;
|
|
2424
2431
|
};
|
|
2432
|
+
var DATA_URI_PREFIX = /^data:[^,]*?;base64,/;
|
|
2433
|
+
var decodeFileInputData = async (data) => {
|
|
2434
|
+
if (data.startsWith("http://") || data.startsWith("https://")) {
|
|
2435
|
+
const resp = await fetch(data);
|
|
2436
|
+
if (!resp.ok) {
|
|
2437
|
+
throw new Error(
|
|
2438
|
+
`uploads: failed to fetch file at ${data}: ${resp.status} ${resp.statusText}`
|
|
2439
|
+
);
|
|
2440
|
+
}
|
|
2441
|
+
return Buffer.from(await resp.arrayBuffer());
|
|
2442
|
+
}
|
|
2443
|
+
const match = data.match(DATA_URI_PREFIX);
|
|
2444
|
+
if (match) {
|
|
2445
|
+
return Buffer.from(data.slice(match[0].length), "base64");
|
|
2446
|
+
}
|
|
2447
|
+
return Buffer.from(data, "base64");
|
|
2448
|
+
};
|
|
2425
2449
|
var MIME_EXT_MAP = {
|
|
2426
2450
|
"image/jpeg": ".jpg",
|
|
2427
2451
|
"image/png": ".png",
|
|
@@ -2470,9 +2494,9 @@ var VercelBlobUploadStore = class {
|
|
|
2470
2494
|
sdk;
|
|
2471
2495
|
workingDir;
|
|
2472
2496
|
access;
|
|
2473
|
-
constructor(workingDir,
|
|
2497
|
+
constructor(workingDir, access4 = "public") {
|
|
2474
2498
|
this.workingDir = workingDir;
|
|
2475
|
-
this.access =
|
|
2499
|
+
this.access = access4;
|
|
2476
2500
|
}
|
|
2477
2501
|
async loadSdk() {
|
|
2478
2502
|
if (this.sdk) return this.sdk;
|
|
@@ -3319,6 +3343,25 @@ var migrations = [
|
|
|
3319
3343
|
ON conversations (parent_conversation_id, parent_message_id)
|
|
3320
3344
|
WHERE parent_message_id IS NOT NULL`
|
|
3321
3345
|
]
|
|
3346
|
+
},
|
|
3347
|
+
{
|
|
3348
|
+
version: 7,
|
|
3349
|
+
name: "fix_reminder_scheduled_at_precision",
|
|
3350
|
+
// Postgres maps `REAL` to float4 (4 bytes, ~7 digit precision),
|
|
3351
|
+
// which silently rounds millisecond epochs (13 digits) — every
|
|
3352
|
+
// reminder write+read on Postgres returned a different value than
|
|
3353
|
+
// it stored. SQLite's REAL is float8 (15+ digit precision) so it
|
|
3354
|
+
// was always fine there.
|
|
3355
|
+
//
|
|
3356
|
+
// Convert the Postgres column to BIGINT so future writes are exact
|
|
3357
|
+
// (already-stored rounded values aren't recoverable but they were
|
|
3358
|
+
// never correct in the first place).
|
|
3359
|
+
up: (d) => {
|
|
3360
|
+
if (d === "sqlite") return [];
|
|
3361
|
+
return [
|
|
3362
|
+
`ALTER TABLE reminders ALTER COLUMN scheduled_at TYPE BIGINT USING scheduled_at::bigint`
|
|
3363
|
+
];
|
|
3364
|
+
}
|
|
3322
3365
|
}
|
|
3323
3366
|
];
|
|
3324
3367
|
|
|
@@ -3982,9 +4025,9 @@ var SqlStorageEngine = class {
|
|
|
3982
4025
|
}
|
|
3983
4026
|
},
|
|
3984
4027
|
deleteFile: async (tenantId, path) => {
|
|
3985
|
-
const
|
|
3986
|
-
if (!
|
|
3987
|
-
if (
|
|
4028
|
+
const stat4 = await this.vfs.stat(tenantId, path);
|
|
4029
|
+
if (!stat4) throw new Error(`ENOENT: no such file or directory, unlink '${path}'`);
|
|
4030
|
+
if (stat4.type === "directory") throw new Error(`EISDIR: illegal operation on a directory, unlink '${path}'`);
|
|
3988
4031
|
await this.executor.run(
|
|
3989
4032
|
rewrite(
|
|
3990
4033
|
"DELETE FROM vfs_entries WHERE agent_id = $1 AND tenant_id = $2 AND path = $3",
|
|
@@ -4224,7 +4267,10 @@ var SqlStorageEngine = class {
|
|
|
4224
4267
|
return {
|
|
4225
4268
|
id: row.id,
|
|
4226
4269
|
task: row.task,
|
|
4227
|
-
|
|
4270
|
+
// Postgres-js returns BIGINT columns as strings to avoid silent
|
|
4271
|
+
// precision loss in JS. Coerce to Number — ms epochs max out at
|
|
4272
|
+
// ~10^16 in year 2286, well under Number.MAX_SAFE_INTEGER (2^53).
|
|
4273
|
+
scheduledAt: Number(row.scheduled_at),
|
|
4228
4274
|
timezone: row.timezone ?? void 0,
|
|
4229
4275
|
status: row.status,
|
|
4230
4276
|
createdAt: new Date(row.created_at).getTime(),
|
|
@@ -4232,7 +4278,7 @@ var SqlStorageEngine = class {
|
|
|
4232
4278
|
ownerId: row.owner_id ?? void 0,
|
|
4233
4279
|
tenantId: tid === DEFAULT_TENANT2 ? null : tid,
|
|
4234
4280
|
recurrence,
|
|
4235
|
-
occurrenceCount: row.occurrence_count ?? 0
|
|
4281
|
+
occurrenceCount: Number(row.occurrence_count ?? 0)
|
|
4236
4282
|
};
|
|
4237
4283
|
}
|
|
4238
4284
|
toUint8Array(value) {
|
|
@@ -4528,6 +4574,9 @@ function createReminderStoreFromEngine(engine) {
|
|
|
4528
4574
|
import { Bash } from "just-bash";
|
|
4529
4575
|
|
|
4530
4576
|
// src/vfs/poncho-fs-adapter.ts
|
|
4577
|
+
import * as nodeFs from "fs/promises";
|
|
4578
|
+
import * as nodeFsSync from "fs";
|
|
4579
|
+
import * as nodePath from "path";
|
|
4531
4580
|
var MIME_MAP = {
|
|
4532
4581
|
".txt": "text/plain",
|
|
4533
4582
|
".html": "text/html",
|
|
@@ -4584,52 +4633,190 @@ var normalize = (path) => {
|
|
|
4584
4633
|
}
|
|
4585
4634
|
return "/" + out.join("/");
|
|
4586
4635
|
};
|
|
4636
|
+
var READ_ONLY_ERROR = (path, op) => new Error(`EROFS: read-only mount, ${op} '${path}'`);
|
|
4587
4637
|
var PonchoFsAdapter = class {
|
|
4588
|
-
constructor(engine, tenantId, limits) {
|
|
4638
|
+
constructor(engine, tenantId, limits, mounts = []) {
|
|
4589
4639
|
this.engine = engine;
|
|
4590
4640
|
this.tenantId = tenantId;
|
|
4591
4641
|
this.limits = limits;
|
|
4642
|
+
this.mounts = mounts.map((m) => {
|
|
4643
|
+
const prefix = m.prefix.endsWith("/") ? m.prefix : m.prefix + "/";
|
|
4644
|
+
return {
|
|
4645
|
+
prefix,
|
|
4646
|
+
prefixNoSlash: prefix.slice(0, -1),
|
|
4647
|
+
source: m.source.replace(/\/+$/, "")
|
|
4648
|
+
};
|
|
4649
|
+
});
|
|
4650
|
+
}
|
|
4651
|
+
mounts;
|
|
4652
|
+
/** Find which mount, if any, a normalised VFS path falls under.
|
|
4653
|
+
* Returns the relative path within the mount's source dir (empty string
|
|
4654
|
+
* when the path is exactly the mount root). */
|
|
4655
|
+
routeToMount(np) {
|
|
4656
|
+
for (const m of this.mounts) {
|
|
4657
|
+
if (np === m.prefixNoSlash) return { mount: m, relative: "" };
|
|
4658
|
+
if (np.startsWith(m.prefix)) return { mount: m, relative: np.slice(m.prefix.length) };
|
|
4659
|
+
}
|
|
4660
|
+
return null;
|
|
4661
|
+
}
|
|
4662
|
+
/** Treat `np` as a directory and return mount-root segments that should be
|
|
4663
|
+
* listed as virtual subdirectories. E.g. with mount "/system/", reading
|
|
4664
|
+
* "/" returns ["system"]; reading "/system" goes via routeToMount and
|
|
4665
|
+
* serves from local FS instead. */
|
|
4666
|
+
virtualChildrenAt(np) {
|
|
4667
|
+
const dirPrefix = np === "/" ? "/" : np + "/";
|
|
4668
|
+
const out = [];
|
|
4669
|
+
for (const m of this.mounts) {
|
|
4670
|
+
if (m.prefix.startsWith(dirPrefix) && m.prefix !== dirPrefix) {
|
|
4671
|
+
const remaining = m.prefix.slice(dirPrefix.length);
|
|
4672
|
+
const seg = remaining.split("/")[0];
|
|
4673
|
+
if (seg && !out.includes(seg)) out.push(seg);
|
|
4674
|
+
}
|
|
4675
|
+
}
|
|
4676
|
+
return out;
|
|
4677
|
+
}
|
|
4678
|
+
toLocal(mount, relative) {
|
|
4679
|
+
return nodePath.join(mount.source, relative);
|
|
4680
|
+
}
|
|
4681
|
+
/** Build an FsStat from a node fs.Stats. */
|
|
4682
|
+
toFsStat(s) {
|
|
4683
|
+
return {
|
|
4684
|
+
isFile: s.isFile(),
|
|
4685
|
+
isDirectory: s.isDirectory(),
|
|
4686
|
+
isSymbolicLink: s.isSymbolicLink(),
|
|
4687
|
+
mode: s.mode,
|
|
4688
|
+
size: s.size,
|
|
4689
|
+
mtime: s.mtime
|
|
4690
|
+
};
|
|
4691
|
+
}
|
|
4692
|
+
/** Synthesise a directory stat for a virtual ancestor (e.g. "/system"
|
|
4693
|
+
* when "/system/jobs/" is mounted but "/system" itself isn't a real dir
|
|
4694
|
+
* on disk). Used so `ls /` and `stat /system` work without surprises. */
|
|
4695
|
+
syntheticDirStat() {
|
|
4696
|
+
return {
|
|
4697
|
+
isFile: false,
|
|
4698
|
+
isDirectory: true,
|
|
4699
|
+
isSymbolicLink: false,
|
|
4700
|
+
mode: 493,
|
|
4701
|
+
size: 0,
|
|
4702
|
+
mtime: /* @__PURE__ */ new Date(0)
|
|
4703
|
+
};
|
|
4592
4704
|
}
|
|
4593
4705
|
// --- Reads ---
|
|
4594
4706
|
async readFile(path, _options) {
|
|
4595
|
-
const
|
|
4707
|
+
const np = normalize(path);
|
|
4708
|
+
const route = this.routeToMount(np);
|
|
4709
|
+
if (route) {
|
|
4710
|
+
const buf2 = await nodeFs.readFile(this.toLocal(route.mount, route.relative));
|
|
4711
|
+
return buf2.toString("utf8");
|
|
4712
|
+
}
|
|
4713
|
+
const buf = await this.engine.vfs.readFile(this.tenantId, np);
|
|
4596
4714
|
return new TextDecoder().decode(buf);
|
|
4597
4715
|
}
|
|
4598
4716
|
async readFileBuffer(path) {
|
|
4599
|
-
|
|
4717
|
+
const np = normalize(path);
|
|
4718
|
+
const route = this.routeToMount(np);
|
|
4719
|
+
if (route) {
|
|
4720
|
+
const buf = await nodeFs.readFile(this.toLocal(route.mount, route.relative));
|
|
4721
|
+
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
4722
|
+
}
|
|
4723
|
+
return this.engine.vfs.readFile(this.tenantId, np);
|
|
4600
4724
|
}
|
|
4601
4725
|
async exists(path) {
|
|
4602
|
-
const
|
|
4603
|
-
|
|
4726
|
+
const np = normalize(path);
|
|
4727
|
+
const route = this.routeToMount(np);
|
|
4728
|
+
if (route) {
|
|
4729
|
+
try {
|
|
4730
|
+
await nodeFs.access(this.toLocal(route.mount, route.relative));
|
|
4731
|
+
return true;
|
|
4732
|
+
} catch {
|
|
4733
|
+
return false;
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4736
|
+
const s = await this.engine.vfs.stat(this.tenantId, np);
|
|
4737
|
+
if (s) return true;
|
|
4738
|
+
return this.virtualChildrenAt(np).length > 0;
|
|
4604
4739
|
}
|
|
4605
4740
|
async stat(path) {
|
|
4606
4741
|
const np = normalize(path);
|
|
4742
|
+
const route = this.routeToMount(np);
|
|
4743
|
+
if (route) {
|
|
4744
|
+
try {
|
|
4745
|
+
const s2 = await nodeFs.stat(this.toLocal(route.mount, route.relative));
|
|
4746
|
+
return this.toFsStat(s2);
|
|
4747
|
+
} catch {
|
|
4748
|
+
throw new Error(`ENOENT: no such file or directory, stat '${path}'`);
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4607
4751
|
const s = await this.engine.vfs.stat(this.tenantId, np);
|
|
4608
|
-
if (
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4752
|
+
if (s) {
|
|
4753
|
+
return {
|
|
4754
|
+
isFile: s.type === "file",
|
|
4755
|
+
isDirectory: s.type === "directory",
|
|
4756
|
+
isSymbolicLink: s.type === "symlink",
|
|
4757
|
+
mode: s.mode,
|
|
4758
|
+
size: s.size,
|
|
4759
|
+
mtime: new Date(s.updatedAt)
|
|
4760
|
+
};
|
|
4761
|
+
}
|
|
4762
|
+
if (this.virtualChildrenAt(np).length > 0) return this.syntheticDirStat();
|
|
4763
|
+
throw new Error(`ENOENT: no such file or directory, stat '${path}'`);
|
|
4617
4764
|
}
|
|
4618
4765
|
async readdir(path) {
|
|
4619
|
-
const
|
|
4620
|
-
|
|
4766
|
+
const np = normalize(path);
|
|
4767
|
+
const route = this.routeToMount(np);
|
|
4768
|
+
if (route) {
|
|
4769
|
+
return nodeFs.readdir(this.toLocal(route.mount, route.relative));
|
|
4770
|
+
}
|
|
4771
|
+
let engineNames = [];
|
|
4772
|
+
try {
|
|
4773
|
+
const entries = await this.engine.vfs.readdir(this.tenantId, np);
|
|
4774
|
+
engineNames = entries.map((e) => e.name);
|
|
4775
|
+
} catch {
|
|
4776
|
+
}
|
|
4777
|
+
const virtualSegs = this.virtualChildrenAt(np);
|
|
4778
|
+
if (virtualSegs.length === 0) return engineNames;
|
|
4779
|
+
const merged = new Set(engineNames);
|
|
4780
|
+
for (const seg of virtualSegs) merged.add(seg);
|
|
4781
|
+
return Array.from(merged);
|
|
4621
4782
|
}
|
|
4622
4783
|
async readdirWithFileTypes(path) {
|
|
4623
|
-
const
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4784
|
+
const np = normalize(path);
|
|
4785
|
+
const route = this.routeToMount(np);
|
|
4786
|
+
if (route) {
|
|
4787
|
+
const entries = await nodeFs.readdir(this.toLocal(route.mount, route.relative), { withFileTypes: true });
|
|
4788
|
+
return entries.map((e) => ({
|
|
4789
|
+
name: e.name,
|
|
4790
|
+
isFile: e.isFile(),
|
|
4791
|
+
isDirectory: e.isDirectory(),
|
|
4792
|
+
isSymbolicLink: e.isSymbolicLink()
|
|
4793
|
+
}));
|
|
4794
|
+
}
|
|
4795
|
+
let engineEntries = [];
|
|
4796
|
+
try {
|
|
4797
|
+
const entries = await this.engine.vfs.readdir(this.tenantId, np);
|
|
4798
|
+
engineEntries = entries.map((e) => ({
|
|
4799
|
+
name: e.name,
|
|
4800
|
+
isFile: e.type === "file",
|
|
4801
|
+
isDirectory: e.type === "directory",
|
|
4802
|
+
isSymbolicLink: e.type === "symlink"
|
|
4803
|
+
}));
|
|
4804
|
+
} catch {
|
|
4805
|
+
}
|
|
4806
|
+
const virtualSegs = this.virtualChildrenAt(np);
|
|
4807
|
+
if (virtualSegs.length === 0) return engineEntries;
|
|
4808
|
+
const seen = new Set(engineEntries.map((e) => e.name));
|
|
4809
|
+
for (const seg of virtualSegs) {
|
|
4810
|
+
if (!seen.has(seg)) {
|
|
4811
|
+
engineEntries.push({ name: seg, isFile: false, isDirectory: true, isSymbolicLink: false });
|
|
4812
|
+
}
|
|
4813
|
+
}
|
|
4814
|
+
return engineEntries;
|
|
4630
4815
|
}
|
|
4631
4816
|
// --- Writes ---
|
|
4632
4817
|
async writeFile(path, content, _options) {
|
|
4818
|
+
const np = normalize(path);
|
|
4819
|
+
if (this.routeToMount(np)) throw READ_ONLY_ERROR(path, "writeFile");
|
|
4633
4820
|
const buf = typeof content === "string" ? new TextEncoder().encode(content) : content;
|
|
4634
4821
|
if (buf.byteLength > this.limits.maxFileSize) {
|
|
4635
4822
|
throw new Error(
|
|
@@ -4637,17 +4824,22 @@ var PonchoFsAdapter = class {
|
|
|
4637
4824
|
);
|
|
4638
4825
|
}
|
|
4639
4826
|
const mime = mimeFromExtension(path);
|
|
4640
|
-
await this.engine.vfs.writeFile(this.tenantId,
|
|
4827
|
+
await this.engine.vfs.writeFile(this.tenantId, np, buf, mime);
|
|
4641
4828
|
}
|
|
4642
4829
|
async appendFile(path, content, _options) {
|
|
4830
|
+
const np = normalize(path);
|
|
4831
|
+
if (this.routeToMount(np)) throw READ_ONLY_ERROR(path, "appendFile");
|
|
4643
4832
|
const buf = typeof content === "string" ? new TextEncoder().encode(content) : content;
|
|
4644
|
-
await this.engine.vfs.appendFile(this.tenantId,
|
|
4833
|
+
await this.engine.vfs.appendFile(this.tenantId, np, buf);
|
|
4645
4834
|
}
|
|
4646
4835
|
async mkdir(path, options) {
|
|
4647
|
-
|
|
4836
|
+
const np = normalize(path);
|
|
4837
|
+
if (this.routeToMount(np)) throw READ_ONLY_ERROR(path, "mkdir");
|
|
4838
|
+
await this.engine.vfs.mkdir(this.tenantId, np, options?.recursive);
|
|
4648
4839
|
}
|
|
4649
4840
|
async rm(path, options) {
|
|
4650
4841
|
const np = normalize(path);
|
|
4842
|
+
if (this.routeToMount(np)) throw READ_ONLY_ERROR(path, "rm");
|
|
4651
4843
|
const s = await this.engine.vfs.stat(this.tenantId, np);
|
|
4652
4844
|
if (!s) {
|
|
4653
4845
|
if (options?.force) return;
|
|
@@ -4663,25 +4855,30 @@ var PonchoFsAdapter = class {
|
|
|
4663
4855
|
async cp(src, dest, options) {
|
|
4664
4856
|
const srcNorm = normalize(src);
|
|
4665
4857
|
const destNorm = normalize(dest);
|
|
4666
|
-
|
|
4858
|
+
if (this.routeToMount(destNorm)) throw READ_ONLY_ERROR(dest, "cp");
|
|
4859
|
+
const srcStat = await this.stat(srcNorm).catch(() => null);
|
|
4667
4860
|
if (!srcStat) throw new Error(`ENOENT: no such file or directory, cp '${src}'`);
|
|
4668
|
-
if (srcStat.
|
|
4861
|
+
if (srcStat.isDirectory) {
|
|
4669
4862
|
if (!options?.recursive) {
|
|
4670
4863
|
throw new Error(`EISDIR: cp -r not specified; omitting directory '${src}'`);
|
|
4671
4864
|
}
|
|
4672
4865
|
await this.engine.vfs.mkdir(this.tenantId, destNorm, true);
|
|
4673
|
-
const entries = await this.
|
|
4674
|
-
for (const
|
|
4675
|
-
await this.cp(`${srcNorm}/${
|
|
4866
|
+
const entries = await this.readdir(srcNorm);
|
|
4867
|
+
for (const name of entries) {
|
|
4868
|
+
await this.cp(`${srcNorm}/${name}`, `${destNorm}/${name}`, options);
|
|
4676
4869
|
}
|
|
4677
4870
|
} else {
|
|
4678
|
-
const content = await this.
|
|
4871
|
+
const content = await this.readFileBuffer(srcNorm);
|
|
4679
4872
|
const mime = mimeFromExtension(destNorm);
|
|
4680
4873
|
await this.engine.vfs.writeFile(this.tenantId, destNorm, content, mime);
|
|
4681
4874
|
}
|
|
4682
4875
|
}
|
|
4683
4876
|
async mv(src, dest) {
|
|
4684
|
-
|
|
4877
|
+
const srcNorm = normalize(src);
|
|
4878
|
+
const destNorm = normalize(dest);
|
|
4879
|
+
if (this.routeToMount(srcNorm)) throw READ_ONLY_ERROR(src, "mv (source)");
|
|
4880
|
+
if (this.routeToMount(destNorm)) throw READ_ONLY_ERROR(dest, "mv (dest)");
|
|
4881
|
+
await this.engine.vfs.rename(this.tenantId, srcNorm, destNorm);
|
|
4685
4882
|
}
|
|
4686
4883
|
// --- Path resolution ---
|
|
4687
4884
|
resolvePath(base, path) {
|
|
@@ -4690,8 +4887,22 @@ var PonchoFsAdapter = class {
|
|
|
4690
4887
|
}
|
|
4691
4888
|
async realpath(path) {
|
|
4692
4889
|
const np = normalize(path);
|
|
4890
|
+
const route = this.routeToMount(np);
|
|
4891
|
+
if (route) {
|
|
4892
|
+
const localResolved = await nodeFs.realpath(this.toLocal(route.mount, route.relative));
|
|
4893
|
+
const localRoot = await nodeFs.realpath(route.mount.source).catch(() => route.mount.source);
|
|
4894
|
+
if (localResolved === localRoot) return route.mount.prefixNoSlash;
|
|
4895
|
+
if (localResolved.startsWith(localRoot + nodePath.sep)) {
|
|
4896
|
+
const rel = localResolved.slice(localRoot.length + 1).split(nodePath.sep).join("/");
|
|
4897
|
+
return `${route.mount.prefix}${rel}`;
|
|
4898
|
+
}
|
|
4899
|
+
return np;
|
|
4900
|
+
}
|
|
4693
4901
|
const s = await this.engine.vfs.lstat(this.tenantId, np);
|
|
4694
|
-
if (!s)
|
|
4902
|
+
if (!s) {
|
|
4903
|
+
if (this.virtualChildrenAt(np).length > 0) return np;
|
|
4904
|
+
throw new Error(`ENOENT: no such file or directory, realpath '${path}'`);
|
|
4905
|
+
}
|
|
4695
4906
|
if (s.type === "symlink" && s.symlinkTarget) {
|
|
4696
4907
|
const target = s.symlinkTarget.startsWith("/") ? s.symlinkTarget : normalize(`${np.slice(0, np.lastIndexOf("/"))}/${s.symlinkTarget}`);
|
|
4697
4908
|
return this.realpath(target);
|
|
@@ -4700,39 +4911,92 @@ var PonchoFsAdapter = class {
|
|
|
4700
4911
|
}
|
|
4701
4912
|
// --- Sync: required by just-bash for glob/find ---
|
|
4702
4913
|
getAllPaths() {
|
|
4703
|
-
|
|
4914
|
+
const enginePaths = this.engine.vfs.listAllPaths(this.tenantId);
|
|
4915
|
+
if (this.mounts.length === 0) return enginePaths;
|
|
4916
|
+
const out = new Set(enginePaths);
|
|
4917
|
+
for (const m of this.mounts) {
|
|
4918
|
+
out.add(m.prefixNoSlash);
|
|
4919
|
+
try {
|
|
4920
|
+
const stack = [
|
|
4921
|
+
{ abs: m.source, vfs: m.prefixNoSlash }
|
|
4922
|
+
];
|
|
4923
|
+
while (stack.length > 0) {
|
|
4924
|
+
const { abs, vfs } = stack.pop();
|
|
4925
|
+
let entries;
|
|
4926
|
+
try {
|
|
4927
|
+
entries = nodeFsSync.readdirSync(abs, { withFileTypes: true });
|
|
4928
|
+
} catch {
|
|
4929
|
+
continue;
|
|
4930
|
+
}
|
|
4931
|
+
for (const e of entries) {
|
|
4932
|
+
const childVfs = `${vfs}/${e.name}`;
|
|
4933
|
+
out.add(childVfs);
|
|
4934
|
+
if (e.isDirectory()) {
|
|
4935
|
+
stack.push({ abs: nodePath.join(abs, e.name), vfs: childVfs });
|
|
4936
|
+
}
|
|
4937
|
+
}
|
|
4938
|
+
}
|
|
4939
|
+
} catch {
|
|
4940
|
+
}
|
|
4941
|
+
}
|
|
4942
|
+
return Array.from(out);
|
|
4704
4943
|
}
|
|
4705
4944
|
// --- Metadata ---
|
|
4706
4945
|
async chmod(path, mode) {
|
|
4707
|
-
|
|
4946
|
+
const np = normalize(path);
|
|
4947
|
+
if (this.routeToMount(np)) throw READ_ONLY_ERROR(path, "chmod");
|
|
4948
|
+
await this.engine.vfs.chmod(this.tenantId, np, mode);
|
|
4708
4949
|
}
|
|
4709
4950
|
async utimes(path, _atime, mtime) {
|
|
4710
|
-
|
|
4951
|
+
const np = normalize(path);
|
|
4952
|
+
if (this.routeToMount(np)) throw READ_ONLY_ERROR(path, "utimes");
|
|
4953
|
+
await this.engine.vfs.utimes(this.tenantId, np, mtime);
|
|
4711
4954
|
}
|
|
4712
4955
|
// --- Symlinks ---
|
|
4713
4956
|
async symlink(target, linkPath) {
|
|
4714
|
-
|
|
4957
|
+
const np = normalize(linkPath);
|
|
4958
|
+
if (this.routeToMount(np)) throw READ_ONLY_ERROR(linkPath, "symlink");
|
|
4959
|
+
await this.engine.vfs.symlink(this.tenantId, target, np);
|
|
4715
4960
|
}
|
|
4716
4961
|
async link(existingPath, newPath) {
|
|
4717
|
-
const
|
|
4962
|
+
const npNew = normalize(newPath);
|
|
4963
|
+
if (this.routeToMount(npNew)) throw READ_ONLY_ERROR(newPath, "link");
|
|
4964
|
+
const content = await this.readFileBuffer(existingPath);
|
|
4718
4965
|
const mime = mimeFromExtension(newPath);
|
|
4719
|
-
await this.engine.vfs.writeFile(this.tenantId,
|
|
4966
|
+
await this.engine.vfs.writeFile(this.tenantId, npNew, content, mime);
|
|
4720
4967
|
}
|
|
4721
4968
|
async readlink(path) {
|
|
4722
|
-
|
|
4969
|
+
const np = normalize(path);
|
|
4970
|
+
const route = this.routeToMount(np);
|
|
4971
|
+
if (route) {
|
|
4972
|
+
return nodeFs.readlink(this.toLocal(route.mount, route.relative));
|
|
4973
|
+
}
|
|
4974
|
+
return this.engine.vfs.readlink(this.tenantId, np);
|
|
4723
4975
|
}
|
|
4724
4976
|
async lstat(path) {
|
|
4725
4977
|
const np = normalize(path);
|
|
4978
|
+
const route = this.routeToMount(np);
|
|
4979
|
+
if (route) {
|
|
4980
|
+
try {
|
|
4981
|
+
const s2 = await nodeFs.lstat(this.toLocal(route.mount, route.relative));
|
|
4982
|
+
return this.toFsStat(s2);
|
|
4983
|
+
} catch {
|
|
4984
|
+
throw new Error(`ENOENT: no such file or directory, lstat '${path}'`);
|
|
4985
|
+
}
|
|
4986
|
+
}
|
|
4726
4987
|
const s = await this.engine.vfs.lstat(this.tenantId, np);
|
|
4727
|
-
if (
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4988
|
+
if (s) {
|
|
4989
|
+
return {
|
|
4990
|
+
isFile: s.type === "file",
|
|
4991
|
+
isDirectory: s.type === "directory",
|
|
4992
|
+
isSymbolicLink: s.type === "symlink",
|
|
4993
|
+
mode: s.mode,
|
|
4994
|
+
size: s.size,
|
|
4995
|
+
mtime: new Date(s.updatedAt)
|
|
4996
|
+
};
|
|
4997
|
+
}
|
|
4998
|
+
if (this.virtualChildrenAt(np).length > 0) return this.syntheticDirStat();
|
|
4999
|
+
throw new Error(`ENOENT: no such file or directory, lstat '${path}'`);
|
|
4736
5000
|
}
|
|
4737
5001
|
};
|
|
4738
5002
|
|
|
@@ -4909,21 +5173,23 @@ function toBashOptions(cfg, network) {
|
|
|
4909
5173
|
return opts;
|
|
4910
5174
|
}
|
|
4911
5175
|
var BashEnvironmentManager = class {
|
|
4912
|
-
constructor(engine, limits, workingDir, bashConfig, network) {
|
|
5176
|
+
constructor(engine, limits, workingDir, bashConfig, network, virtualMounts = []) {
|
|
4913
5177
|
this.engine = engine;
|
|
4914
5178
|
this.limits = limits;
|
|
4915
5179
|
this.workingDir = workingDir;
|
|
4916
5180
|
this.bashOptions = toBashOptions(bashConfig, network);
|
|
5181
|
+
this.virtualMounts = virtualMounts;
|
|
4917
5182
|
}
|
|
4918
5183
|
environments = /* @__PURE__ */ new Map();
|
|
4919
5184
|
filesystems = /* @__PURE__ */ new Map();
|
|
4920
5185
|
workingDir;
|
|
4921
5186
|
bashOptions;
|
|
5187
|
+
virtualMounts;
|
|
4922
5188
|
/** Return the combined IFileSystem (VFS + optional /project mount) for a tenant. */
|
|
4923
5189
|
getFs(tenantId) {
|
|
4924
5190
|
let fs = this.filesystems.get(tenantId);
|
|
4925
5191
|
if (!fs) {
|
|
4926
|
-
const adapter = new PonchoFsAdapter(this.engine, tenantId, this.limits);
|
|
5192
|
+
const adapter = new PonchoFsAdapter(this.engine, tenantId, this.limits, this.virtualMounts);
|
|
4927
5193
|
fs = createBashFs(adapter, this.workingDir);
|
|
4928
5194
|
this.filesystems.set(tenantId, fs);
|
|
4929
5195
|
}
|
|
@@ -4943,7 +5209,7 @@ var BashEnvironmentManager = class {
|
|
|
4943
5209
|
return bash;
|
|
4944
5210
|
}
|
|
4945
5211
|
getAdapter(tenantId) {
|
|
4946
|
-
return new PonchoFsAdapter(this.engine, tenantId, this.limits);
|
|
5212
|
+
return new PonchoFsAdapter(this.engine, tenantId, this.limits, this.virtualMounts);
|
|
4947
5213
|
}
|
|
4948
5214
|
/** Refresh the PostgreSQL path cache before a bash.exec() call. */
|
|
4949
5215
|
async refreshPathCache(tenantId) {
|
|
@@ -5062,8 +5328,8 @@ var createReadFileTool = (getFs) => defineTool3({
|
|
|
5062
5328
|
if (!await fs.exists(filePath)) {
|
|
5063
5329
|
throw new Error(`File not found: ${filePath}`);
|
|
5064
5330
|
}
|
|
5065
|
-
const
|
|
5066
|
-
if (
|
|
5331
|
+
const stat4 = await fs.stat(filePath);
|
|
5332
|
+
if (stat4.isDirectory) {
|
|
5067
5333
|
throw new Error(`${filePath} is a directory, not a file`);
|
|
5068
5334
|
}
|
|
5069
5335
|
const mediaType = mimeFromPath(filePath) ?? "application/octet-stream";
|
|
@@ -5115,8 +5381,8 @@ var createEditFileTool = (getFs) => defineTool4({
|
|
|
5115
5381
|
const tenantId = context.tenantId ?? "__default__";
|
|
5116
5382
|
const fs = getFs(tenantId);
|
|
5117
5383
|
if (!await fs.exists(filePath)) throw new Error(`File not found: ${filePath}`);
|
|
5118
|
-
const
|
|
5119
|
-
if (
|
|
5384
|
+
const stat4 = await fs.stat(filePath);
|
|
5385
|
+
if (stat4.isDirectory) throw new Error(`${filePath} is a directory`);
|
|
5120
5386
|
const content = await fs.readFile(filePath);
|
|
5121
5387
|
const first = content.indexOf(oldStr);
|
|
5122
5388
|
if (first === -1) {
|
|
@@ -5611,7 +5877,7 @@ var createTodoTools = (store) => {
|
|
|
5611
5877
|
|
|
5612
5878
|
// src/secrets-store.ts
|
|
5613
5879
|
import { createCipheriv, createDecipheriv, randomBytes as randomBytes2, createHash as createHash3 } from "crypto";
|
|
5614
|
-
import { mkdir as mkdir3, readFile as
|
|
5880
|
+
import { mkdir as mkdir3, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
|
|
5615
5881
|
import { dirname as dirname3, resolve as resolve7 } from "path";
|
|
5616
5882
|
function deriveKey(signingKey) {
|
|
5617
5883
|
return createHash3("sha256").update("poncho-secrets-v1:" + signingKey).digest();
|
|
@@ -5657,7 +5923,7 @@ var FileSecretsStore = class {
|
|
|
5657
5923
|
}
|
|
5658
5924
|
async readAll(tenantId) {
|
|
5659
5925
|
try {
|
|
5660
|
-
const raw = await
|
|
5926
|
+
const raw = await readFile6(await this.filePath(tenantId), "utf8");
|
|
5661
5927
|
return JSON.parse(raw);
|
|
5662
5928
|
} catch {
|
|
5663
5929
|
return {};
|
|
@@ -6567,7 +6833,7 @@ import { createAnthropic } from "@ai-sdk/anthropic";
|
|
|
6567
6833
|
// src/openai-codex-auth.ts
|
|
6568
6834
|
import { homedir as homedir3 } from "os";
|
|
6569
6835
|
import { dirname as dirname4, resolve as resolve8 } from "path";
|
|
6570
|
-
import { mkdir as mkdir4, readFile as
|
|
6836
|
+
import { mkdir as mkdir4, readFile as readFile7, chmod, writeFile as writeFile5, rm as rm3 } from "fs/promises";
|
|
6571
6837
|
var OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
6572
6838
|
var OPENAI_AUTH_ISSUER = "https://auth.openai.com";
|
|
6573
6839
|
var REFRESH_TOKEN_GRACE_MS = 5 * 60 * 1e3;
|
|
@@ -6606,7 +6872,7 @@ var getOpenAICodexAuthFilePath = (config) => {
|
|
|
6606
6872
|
var readOpenAICodexSession = async (config) => {
|
|
6607
6873
|
const filePath = getOpenAICodexAuthFilePath(config);
|
|
6608
6874
|
try {
|
|
6609
|
-
const content = await
|
|
6875
|
+
const content = await readFile7(filePath, "utf8");
|
|
6610
6876
|
const parsed = JSON.parse(content);
|
|
6611
6877
|
if (typeof parsed.refreshToken !== "string" || parsed.refreshToken.length === 0) {
|
|
6612
6878
|
return void 0;
|
|
@@ -6941,7 +7207,7 @@ var createModelProvider = (provider, config) => {
|
|
|
6941
7207
|
};
|
|
6942
7208
|
|
|
6943
7209
|
// src/skill-context.ts
|
|
6944
|
-
import { readFile as
|
|
7210
|
+
import { readFile as readFile8, readdir as readdir3, stat as stat2 } from "fs/promises";
|
|
6945
7211
|
import { dirname as dirname5, resolve as resolve9, normalize as normalize2 } from "path";
|
|
6946
7212
|
import YAML3 from "yaml";
|
|
6947
7213
|
import { createLogger as createLogger4 } from "@poncho-ai/sdk";
|
|
@@ -7022,7 +7288,7 @@ var parseSkillFrontmatter = (content) => {
|
|
|
7022
7288
|
};
|
|
7023
7289
|
};
|
|
7024
7290
|
var collectSkillManifests = async (directory) => {
|
|
7025
|
-
const entries = await
|
|
7291
|
+
const entries = await readdir3(directory, { withFileTypes: true });
|
|
7026
7292
|
const files = [];
|
|
7027
7293
|
for (const entry of entries) {
|
|
7028
7294
|
const fullPath = resolve9(directory, entry.name);
|
|
@@ -7030,7 +7296,7 @@ var collectSkillManifests = async (directory) => {
|
|
|
7030
7296
|
let isFile = entry.isFile();
|
|
7031
7297
|
if (entry.isSymbolicLink()) {
|
|
7032
7298
|
try {
|
|
7033
|
-
const s = await
|
|
7299
|
+
const s = await stat2(fullPath);
|
|
7034
7300
|
isDir = s.isDirectory();
|
|
7035
7301
|
isFile = s.isFile();
|
|
7036
7302
|
} catch {
|
|
@@ -7060,7 +7326,7 @@ var loadSkillMetadata = async (workingDir, extraSkillPaths) => {
|
|
|
7060
7326
|
const seen = /* @__PURE__ */ new Set();
|
|
7061
7327
|
for (const manifest of allManifests) {
|
|
7062
7328
|
try {
|
|
7063
|
-
const content = await
|
|
7329
|
+
const content = await readFile8(manifest, "utf8");
|
|
7064
7330
|
const parsed = parseSkillFrontmatter(content);
|
|
7065
7331
|
if (parsed && !seen.has(parsed.name)) {
|
|
7066
7332
|
seen.add(parsed.name);
|
|
@@ -7163,7 +7429,7 @@ var mergeSkills = (repoSkills, vfsSkills, onCollision) => {
|
|
|
7163
7429
|
var loadSkillInstructions = async (skill, engine) => {
|
|
7164
7430
|
const raw = skill.source.kind === "vfs" ? decoder.decode(
|
|
7165
7431
|
await requireEngine(engine).vfs.readFile(skill.source.tenantId, skill.skillPath)
|
|
7166
|
-
) : await
|
|
7432
|
+
) : await readFile8(skill.skillPath, "utf8");
|
|
7167
7433
|
const match = raw.match(FRONTMATTER_PATTERN3);
|
|
7168
7434
|
return match ? match[2].trim() : raw.trim();
|
|
7169
7435
|
};
|
|
@@ -7184,7 +7450,7 @@ var readSkillResource = async (skill, relativePath, engine) => {
|
|
|
7184
7450
|
if (!fullPath.startsWith(skill.skillDir)) {
|
|
7185
7451
|
throw new Error("Path escapes the skill directory");
|
|
7186
7452
|
}
|
|
7187
|
-
return await
|
|
7453
|
+
return await readFile8(fullPath, "utf8");
|
|
7188
7454
|
};
|
|
7189
7455
|
var requireEngine = (engine) => {
|
|
7190
7456
|
if (!engine) {
|
|
@@ -7312,8 +7578,8 @@ function convertSchema(schema) {
|
|
|
7312
7578
|
|
|
7313
7579
|
// src/skill-tools.ts
|
|
7314
7580
|
import { defineTool as defineTool9 } from "@poncho-ai/sdk";
|
|
7315
|
-
import { access as
|
|
7316
|
-
import { extname, normalize as normalize3, resolve as resolve10, sep as
|
|
7581
|
+
import { access as access3, readdir as readdir4, stat as stat3 } from "fs/promises";
|
|
7582
|
+
import { extname, normalize as normalize3, resolve as resolve10, sep as sep3 } from "path";
|
|
7317
7583
|
import { pathToFileURL } from "url";
|
|
7318
7584
|
import { createJiti as createJiti2 } from "jiti";
|
|
7319
7585
|
var findSkill = async (options, tenantId, name) => {
|
|
@@ -7498,7 +7764,7 @@ var createSkillTools = (options) => {
|
|
|
7498
7764
|
error: `Script "${resolved2.relativePath}" for skill "${name}" is not allowed by policy.`
|
|
7499
7765
|
};
|
|
7500
7766
|
}
|
|
7501
|
-
await
|
|
7767
|
+
await access3(resolved2.fullPath);
|
|
7502
7768
|
const fn2 = await loadRunnableScriptFunction(resolved2.fullPath);
|
|
7503
7769
|
const output2 = await fn2(payload, {
|
|
7504
7770
|
scope: "skill",
|
|
@@ -7514,7 +7780,7 @@ var createSkillTools = (options) => {
|
|
|
7514
7780
|
error: `Script "${resolved.relativePath}" is not allowed by policy.`
|
|
7515
7781
|
};
|
|
7516
7782
|
}
|
|
7517
|
-
await
|
|
7783
|
+
await access3(resolved.fullPath);
|
|
7518
7784
|
const fn = await loadRunnableScriptFunction(resolved.fullPath);
|
|
7519
7785
|
const output = await fn(payload, {
|
|
7520
7786
|
scope: "agent",
|
|
@@ -7534,7 +7800,7 @@ var SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", "
|
|
|
7534
7800
|
var VFS_SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".ts", ".mts"]);
|
|
7535
7801
|
var listRepoSkillScripts = async (skill, isScriptAllowed) => {
|
|
7536
7802
|
const scripts = await collectScriptFiles(skill.skillDir);
|
|
7537
|
-
return scripts.map((fullPath) => fullPath.slice(skill.skillDir.length + 1).split(
|
|
7803
|
+
return scripts.map((fullPath) => fullPath.slice(skill.skillDir.length + 1).split(sep3).join("/")).filter((relativePath) => relativePath.toLowerCase() !== "skill.md").map(
|
|
7538
7804
|
(relativePath) => relativePath.includes("/") ? relativePath : `./${relativePath}`
|
|
7539
7805
|
).filter((path) => isScriptAllowed ? isScriptAllowed(skill.name, path) : true).sort();
|
|
7540
7806
|
};
|
|
@@ -7567,7 +7833,7 @@ var listVfsSkillScripts = async (skill, engine, isScriptAllowed) => {
|
|
|
7567
7833
|
return found.filter((path) => isScriptAllowed ? isScriptAllowed(skill.name, path) : true).sort();
|
|
7568
7834
|
};
|
|
7569
7835
|
var collectScriptFiles = async (directory) => {
|
|
7570
|
-
const entries = await
|
|
7836
|
+
const entries = await readdir4(directory, { withFileTypes: true });
|
|
7571
7837
|
const files = [];
|
|
7572
7838
|
for (const entry of entries) {
|
|
7573
7839
|
if (entry.name === "node_modules") continue;
|
|
@@ -7576,7 +7842,7 @@ var collectScriptFiles = async (directory) => {
|
|
|
7576
7842
|
let isFile = entry.isFile();
|
|
7577
7843
|
if (entry.isSymbolicLink()) {
|
|
7578
7844
|
try {
|
|
7579
|
-
const s = await
|
|
7845
|
+
const s = await stat3(fullPath);
|
|
7580
7846
|
isDir = s.isDirectory();
|
|
7581
7847
|
isFile = s.isFile();
|
|
7582
7848
|
} catch {
|
|
@@ -7598,7 +7864,7 @@ var collectScriptFiles = async (directory) => {
|
|
|
7598
7864
|
};
|
|
7599
7865
|
var normalizeScriptPolicyPath = (relativePath) => {
|
|
7600
7866
|
const trimmed = relativePath.trim();
|
|
7601
|
-
const normalized = normalize3(trimmed).split(
|
|
7867
|
+
const normalized = normalize3(trimmed).split(sep3).join("/");
|
|
7602
7868
|
if (normalized.startsWith("/")) {
|
|
7603
7869
|
throw new Error("Script path must be relative and within the allowed directory");
|
|
7604
7870
|
}
|
|
@@ -7612,7 +7878,7 @@ var resolveScriptPath = (baseDir, relativePath, containmentDir) => {
|
|
|
7612
7878
|
const normalized = normalizeScriptPolicyPath(relativePath);
|
|
7613
7879
|
const fullPath = resolve10(baseDir, normalized);
|
|
7614
7880
|
const boundary = resolve10(containmentDir ?? baseDir);
|
|
7615
|
-
if (!fullPath.startsWith(`${boundary}${
|
|
7881
|
+
if (!fullPath.startsWith(`${boundary}${sep3}`) && fullPath !== boundary) {
|
|
7616
7882
|
throw new Error("Script path must stay inside the allowed directory");
|
|
7617
7883
|
}
|
|
7618
7884
|
const extension = extname(fullPath).toLowerCase();
|
|
@@ -7997,6 +8263,57 @@ var createSubagentTools = (manager) => [
|
|
|
7997
8263
|
}
|
|
7998
8264
|
return { subagents };
|
|
7999
8265
|
}
|
|
8266
|
+
}),
|
|
8267
|
+
defineTool11({
|
|
8268
|
+
name: "read_subagent",
|
|
8269
|
+
description: "Fetch the conversation transcript of a subagent you spawned. Use this to inspect a subagent's intermediate reasoning, tool calls, or full output -- instead of asking it to repeat its work via message_subagent.\n\nModes:\n- 'final' (default): just the last assistant message. Cheap.\n- 'assistant': all assistant messages, no tool calls/results.\n- 'full': every message including tool calls and results. Can be large.\n\nUse since_index / max_messages to page through long transcripts. Only works on subagents directly spawned by this conversation.",
|
|
8270
|
+
inputSchema: {
|
|
8271
|
+
type: "object",
|
|
8272
|
+
properties: {
|
|
8273
|
+
subagent_id: {
|
|
8274
|
+
type: "string",
|
|
8275
|
+
description: "The subagent ID (from spawn_subagent or list_subagents)."
|
|
8276
|
+
},
|
|
8277
|
+
mode: {
|
|
8278
|
+
type: "string",
|
|
8279
|
+
enum: ["final", "assistant", "full"],
|
|
8280
|
+
description: "How much of the transcript to return. Defaults to 'final'."
|
|
8281
|
+
},
|
|
8282
|
+
since_index: {
|
|
8283
|
+
type: "number",
|
|
8284
|
+
description: "Skip messages before this index (applied after mode filter)."
|
|
8285
|
+
},
|
|
8286
|
+
max_messages: {
|
|
8287
|
+
type: "number",
|
|
8288
|
+
description: "Cap the number of messages returned."
|
|
8289
|
+
}
|
|
8290
|
+
},
|
|
8291
|
+
required: ["subagent_id"],
|
|
8292
|
+
additionalProperties: false
|
|
8293
|
+
},
|
|
8294
|
+
handler: async (input, context) => {
|
|
8295
|
+
const subagentId = typeof input.subagent_id === "string" ? input.subagent_id : "";
|
|
8296
|
+
if (!subagentId) {
|
|
8297
|
+
return { error: "subagent_id is required" };
|
|
8298
|
+
}
|
|
8299
|
+
const parentConversationId = context.conversationId;
|
|
8300
|
+
if (!parentConversationId) {
|
|
8301
|
+
return { error: "no active conversation" };
|
|
8302
|
+
}
|
|
8303
|
+
const rawMode = typeof input.mode === "string" ? input.mode : "final";
|
|
8304
|
+
const mode = rawMode === "assistant" || rawMode === "full" ? rawMode : "final";
|
|
8305
|
+
try {
|
|
8306
|
+
return await manager.getTranscript({
|
|
8307
|
+
subagentId,
|
|
8308
|
+
parentConversationId,
|
|
8309
|
+
mode,
|
|
8310
|
+
sinceIndex: typeof input.since_index === "number" ? input.since_index : void 0,
|
|
8311
|
+
maxMessages: typeof input.max_messages === "number" ? input.max_messages : void 0
|
|
8312
|
+
});
|
|
8313
|
+
} catch (err) {
|
|
8314
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
8315
|
+
}
|
|
8316
|
+
}
|
|
8000
8317
|
})
|
|
8001
8318
|
];
|
|
8002
8319
|
|
|
@@ -8776,6 +9093,8 @@ var AgentHarness = class _AgentHarness {
|
|
|
8776
9093
|
storageEngine;
|
|
8777
9094
|
/** Bash environment manager (creates per-tenant bash instances). */
|
|
8778
9095
|
bashManager;
|
|
9096
|
+
/** Read-only virtual mounts overlaid on the VFS. Empty by default. */
|
|
9097
|
+
virtualMounts = [];
|
|
8779
9098
|
resolveToolAccess(toolName) {
|
|
8780
9099
|
const tools = this.loadedConfig?.tools;
|
|
8781
9100
|
if (!tools) return true;
|
|
@@ -8783,14 +9102,23 @@ var AgentHarness = class _AgentHarness {
|
|
|
8783
9102
|
const envOverride = tools.byEnvironment?.[env]?.[toolName];
|
|
8784
9103
|
if (envOverride !== void 0) return envOverride;
|
|
8785
9104
|
const flatValue = tools[toolName];
|
|
8786
|
-
if (typeof flatValue === "boolean" || flatValue === "approval")
|
|
9105
|
+
if (typeof flatValue === "boolean" || flatValue === "approval" || flatValue !== null && typeof flatValue === "object" && !Array.isArray(flatValue) && // distinguish a ToolAccess object from the nested `defaults` /
|
|
9106
|
+
// `byEnvironment` sibling fields by checking it has only the
|
|
9107
|
+
// expected ToolAccess keys.
|
|
9108
|
+
Object.keys(flatValue).every((k) => k === "access" || k === "dispatch")) {
|
|
9109
|
+
return flatValue;
|
|
9110
|
+
}
|
|
8787
9111
|
const legacyValue = tools.defaults?.[toolName];
|
|
8788
9112
|
if (legacyValue !== void 0) return legacyValue;
|
|
8789
9113
|
return true;
|
|
8790
9114
|
}
|
|
9115
|
+
/** Returns the normalized {access, dispatch} mode for the tool. */
|
|
9116
|
+
resolveToolMode(toolName) {
|
|
9117
|
+
return normalizeToolAccess(this.resolveToolAccess(toolName));
|
|
9118
|
+
}
|
|
8791
9119
|
isToolEnabled(name) {
|
|
8792
|
-
const
|
|
8793
|
-
if (
|
|
9120
|
+
const access4 = this.resolveToolAccess(name);
|
|
9121
|
+
if (access4 === false) return false;
|
|
8794
9122
|
if (name === "write_file" || name === "edit_file" || name === "delete_file" || name === "delete_directory") {
|
|
8795
9123
|
return this.shouldEnableWriteTool();
|
|
8796
9124
|
}
|
|
@@ -8947,6 +9275,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
8947
9275
|
this.storageEngine = options.storageEngine;
|
|
8948
9276
|
this.injectedStorageEngine = true;
|
|
8949
9277
|
}
|
|
9278
|
+
this.virtualMounts = options.virtualMounts ?? [];
|
|
8950
9279
|
if (options.toolDefinitions?.length) {
|
|
8951
9280
|
this.dispatcher.registerMany(options.toolDefinitions);
|
|
8952
9281
|
}
|
|
@@ -9118,6 +9447,10 @@ var AgentHarness = class _AgentHarness {
|
|
|
9118
9447
|
return this.loadedSkills;
|
|
9119
9448
|
}
|
|
9120
9449
|
const effectiveTenant = tenantId || "__default__";
|
|
9450
|
+
const engineWithRefresh = this.storageEngine;
|
|
9451
|
+
if (typeof engineWithRefresh.refreshPathCache === "function") {
|
|
9452
|
+
await engineWithRefresh.refreshPathCache(effectiveTenant);
|
|
9453
|
+
}
|
|
9121
9454
|
const fingerprint = this.computeVfsSkillFingerprint(effectiveTenant);
|
|
9122
9455
|
const cached = this.skillCache.get(effectiveTenant);
|
|
9123
9456
|
if (cached && cached.fingerprint === fingerprint) {
|
|
@@ -9270,7 +9603,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
9270
9603
|
);
|
|
9271
9604
|
}
|
|
9272
9605
|
requiresApprovalForToolCall(toolName, input) {
|
|
9273
|
-
if (this.
|
|
9606
|
+
if (this.resolveToolMode(toolName).access === "approval") {
|
|
9274
9607
|
return true;
|
|
9275
9608
|
}
|
|
9276
9609
|
if (toolName === "run_skill_script") {
|
|
@@ -9368,7 +9701,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
9368
9701
|
}
|
|
9369
9702
|
try {
|
|
9370
9703
|
const agentFilePath = resolve11(this.workingDir, "AGENT.md");
|
|
9371
|
-
const rawContent = await
|
|
9704
|
+
const rawContent = await readFile9(agentFilePath, "utf8");
|
|
9372
9705
|
if (rawContent === this.agentFileFingerprint) {
|
|
9373
9706
|
return false;
|
|
9374
9707
|
}
|
|
@@ -9441,7 +9774,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
9441
9774
|
}
|
|
9442
9775
|
} else {
|
|
9443
9776
|
const agentFilePath = resolve11(this.workingDir, "AGENT.md");
|
|
9444
|
-
const agentRawContent = await
|
|
9777
|
+
const agentRawContent = await readFile9(agentFilePath, "utf8");
|
|
9445
9778
|
this.parsedAgent = parseAgentMarkdown(agentRawContent);
|
|
9446
9779
|
this.agentFileFingerprint = agentRawContent;
|
|
9447
9780
|
const identity = await ensureAgentIdentity(this.workingDir);
|
|
@@ -9488,7 +9821,8 @@ var AgentHarness = class _AgentHarness {
|
|
|
9488
9821
|
{ maxFileSize, maxTotalStorage },
|
|
9489
9822
|
bashWorkingDir,
|
|
9490
9823
|
config?.bash,
|
|
9491
|
-
config?.network
|
|
9824
|
+
config?.network,
|
|
9825
|
+
this.virtualMounts
|
|
9492
9826
|
);
|
|
9493
9827
|
this.registerIfMissing(createBashTool(this.bashManager));
|
|
9494
9828
|
const getFs = (tenantId) => this.bashManager.getFs(tenantId);
|
|
@@ -9586,9 +9920,9 @@ var AgentHarness = class _AgentHarness {
|
|
|
9586
9920
|
await writeFile6(filePath, json, "utf8");
|
|
9587
9921
|
},
|
|
9588
9922
|
async load() {
|
|
9589
|
-
const { readFile:
|
|
9923
|
+
const { readFile: readFile10 } = await import("fs/promises");
|
|
9590
9924
|
try {
|
|
9591
|
-
return await
|
|
9925
|
+
return await readFile10(filePath, "utf8");
|
|
9592
9926
|
} catch {
|
|
9593
9927
|
return void 0;
|
|
9594
9928
|
}
|
|
@@ -9607,9 +9941,9 @@ var AgentHarness = class _AgentHarness {
|
|
|
9607
9941
|
await writeFile6(filePath, json, "utf8");
|
|
9608
9942
|
},
|
|
9609
9943
|
async load() {
|
|
9610
|
-
const { readFile:
|
|
9944
|
+
const { readFile: readFile10 } = await import("fs/promises");
|
|
9611
9945
|
try {
|
|
9612
|
-
return await
|
|
9946
|
+
return await readFile10(filePath, "utf8");
|
|
9613
9947
|
} catch {
|
|
9614
9948
|
return void 0;
|
|
9615
9949
|
}
|
|
@@ -9623,12 +9957,12 @@ var AgentHarness = class _AgentHarness {
|
|
|
9623
9957
|
let browserMod;
|
|
9624
9958
|
try {
|
|
9625
9959
|
const { existsSync } = await import("fs");
|
|
9626
|
-
const { join, dirname: dirname6 } = await import("path");
|
|
9960
|
+
const { join: join2, dirname: dirname6 } = await import("path");
|
|
9627
9961
|
const { pathToFileURL: pathToFileURL2 } = await import("url");
|
|
9628
9962
|
let searchDir = this.workingDir;
|
|
9629
9963
|
let entryPath;
|
|
9630
9964
|
for (; ; ) {
|
|
9631
|
-
const candidate =
|
|
9965
|
+
const candidate = join2(searchDir, "node_modules", "@poncho-ai", "browser", "dist", "index.js");
|
|
9632
9966
|
if (existsSync(candidate)) {
|
|
9633
9967
|
entryPath = candidate;
|
|
9634
9968
|
break;
|
|
@@ -9949,7 +10283,7 @@ ${this.skillFingerprint}`;
|
|
|
9949
10283
|
];
|
|
9950
10284
|
for (const file of input.files) {
|
|
9951
10285
|
if (this.uploadStore) {
|
|
9952
|
-
const buf =
|
|
10286
|
+
const buf = await decodeFileInputData(file.data);
|
|
9953
10287
|
const key = deriveUploadKey(buf, file.mediaType);
|
|
9954
10288
|
const ref = await this.uploadStore.put(key, buf, file.mediaType);
|
|
9955
10289
|
parts.push({
|
|
@@ -10628,6 +10962,7 @@ ${textContent}` };
|
|
|
10628
10962
|
const richToolResults = [];
|
|
10629
10963
|
const approvedCalls = [];
|
|
10630
10964
|
const approvalNeeded = [];
|
|
10965
|
+
const deviceNeeded = [];
|
|
10631
10966
|
for (const call of toolCalls) {
|
|
10632
10967
|
if (isCancelled()) {
|
|
10633
10968
|
yield emitCancellation();
|
|
@@ -10642,6 +10977,13 @@ ${textContent}` };
|
|
|
10642
10977
|
name: runtimeToolName,
|
|
10643
10978
|
input: call.input
|
|
10644
10979
|
});
|
|
10980
|
+
} else if (this.resolveToolMode(runtimeToolName).dispatch === "device") {
|
|
10981
|
+
deviceNeeded.push({
|
|
10982
|
+
approvalId: `device_${randomUUID5()}`,
|
|
10983
|
+
id: call.id,
|
|
10984
|
+
name: runtimeToolName,
|
|
10985
|
+
input: call.input
|
|
10986
|
+
});
|
|
10645
10987
|
} else {
|
|
10646
10988
|
approvedCalls.push({
|
|
10647
10989
|
id: call.id,
|
|
@@ -10690,6 +11032,46 @@ ${textContent}` };
|
|
|
10690
11032
|
});
|
|
10691
11033
|
return;
|
|
10692
11034
|
}
|
|
11035
|
+
if (deviceNeeded.length > 0) {
|
|
11036
|
+
for (const dn of deviceNeeded) {
|
|
11037
|
+
yield pushEvent({
|
|
11038
|
+
type: "tool:device:required",
|
|
11039
|
+
tool: dn.name,
|
|
11040
|
+
input: dn.input,
|
|
11041
|
+
requestId: dn.approvalId
|
|
11042
|
+
});
|
|
11043
|
+
}
|
|
11044
|
+
const assistantContent2 = JSON.stringify({
|
|
11045
|
+
text: fullText,
|
|
11046
|
+
tool_calls: toolCalls.map((tc) => ({
|
|
11047
|
+
id: tc.id,
|
|
11048
|
+
name: exposedToolNames.get(tc.name) ?? tc.name,
|
|
11049
|
+
input: tc.input
|
|
11050
|
+
}))
|
|
11051
|
+
});
|
|
11052
|
+
const assistantMsg = {
|
|
11053
|
+
role: "assistant",
|
|
11054
|
+
content: assistantContent2,
|
|
11055
|
+
metadata: { timestamp: now(), id: randomUUID5(), step, runId }
|
|
11056
|
+
};
|
|
11057
|
+
const deltaMessages = [...messages.slice(inputMessageCount), assistantMsg];
|
|
11058
|
+
yield pushEvent({
|
|
11059
|
+
type: "tool:device:checkpoint",
|
|
11060
|
+
approvals: deviceNeeded.map((dn) => ({
|
|
11061
|
+
approvalId: dn.approvalId,
|
|
11062
|
+
tool: dn.name,
|
|
11063
|
+
toolCallId: dn.id,
|
|
11064
|
+
input: dn.input
|
|
11065
|
+
})),
|
|
11066
|
+
checkpointMessages: deltaMessages,
|
|
11067
|
+
pendingToolCalls: toolCalls.map((tc) => ({
|
|
11068
|
+
id: tc.id,
|
|
11069
|
+
name: exposedToolNames.get(tc.name) ?? tc.name,
|
|
11070
|
+
input: tc.input
|
|
11071
|
+
}))
|
|
11072
|
+
});
|
|
11073
|
+
return;
|
|
11074
|
+
}
|
|
10693
11075
|
const batchStart = now();
|
|
10694
11076
|
if (isCancelled()) {
|
|
10695
11077
|
yield emitCancellation();
|
|
@@ -11703,7 +12085,8 @@ var buildApprovalCheckpoints = ({
|
|
|
11703
12085
|
runId,
|
|
11704
12086
|
checkpointMessages,
|
|
11705
12087
|
baseMessageCount,
|
|
11706
|
-
pendingToolCalls
|
|
12088
|
+
pendingToolCalls,
|
|
12089
|
+
kind = "approval"
|
|
11707
12090
|
}) => approvals.map((approval) => ({
|
|
11708
12091
|
approvalId: approval.approvalId,
|
|
11709
12092
|
runId,
|
|
@@ -11712,7 +12095,8 @@ var buildApprovalCheckpoints = ({
|
|
|
11712
12095
|
input: approval.input,
|
|
11713
12096
|
checkpointMessages,
|
|
11714
12097
|
baseMessageCount,
|
|
11715
|
-
pendingToolCalls
|
|
12098
|
+
pendingToolCalls,
|
|
12099
|
+
kind
|
|
11716
12100
|
}));
|
|
11717
12101
|
var applyTurnMetadata = (conv, meta, opts = {}) => {
|
|
11718
12102
|
const {
|
|
@@ -13001,6 +13385,48 @@ ${resultBody}`,
|
|
|
13001
13385
|
}
|
|
13002
13386
|
}
|
|
13003
13387
|
return results;
|
|
13388
|
+
},
|
|
13389
|
+
getTranscript: async (opts) => {
|
|
13390
|
+
const conversation = await this.conversationStore.get(opts.subagentId);
|
|
13391
|
+
if (!conversation) {
|
|
13392
|
+
throw new Error(`Subagent "${opts.subagentId}" not found.`);
|
|
13393
|
+
}
|
|
13394
|
+
if (!conversation.parentConversationId) {
|
|
13395
|
+
throw new Error(`Conversation "${opts.subagentId}" is not a subagent.`);
|
|
13396
|
+
}
|
|
13397
|
+
if (conversation.parentConversationId !== opts.parentConversationId) {
|
|
13398
|
+
throw new Error(`Subagent "${opts.subagentId}" was not spawned by this conversation.`);
|
|
13399
|
+
}
|
|
13400
|
+
const all = conversation.messages;
|
|
13401
|
+
let filtered;
|
|
13402
|
+
if (opts.mode === "final") {
|
|
13403
|
+
let lastAssistant;
|
|
13404
|
+
for (let i = all.length - 1; i >= 0; i--) {
|
|
13405
|
+
if (all[i].role === "assistant") {
|
|
13406
|
+
lastAssistant = all[i];
|
|
13407
|
+
break;
|
|
13408
|
+
}
|
|
13409
|
+
}
|
|
13410
|
+
filtered = lastAssistant ? [lastAssistant] : [];
|
|
13411
|
+
} else if (opts.mode === "assistant") {
|
|
13412
|
+
filtered = all.filter((m) => m.role === "assistant");
|
|
13413
|
+
} else {
|
|
13414
|
+
filtered = all;
|
|
13415
|
+
}
|
|
13416
|
+
const startIndex = Math.max(0, opts.sinceIndex ?? 0);
|
|
13417
|
+
const sliced = filtered.slice(startIndex);
|
|
13418
|
+
const cap = opts.maxMessages !== void 0 && opts.maxMessages >= 0 ? opts.maxMessages : sliced.length;
|
|
13419
|
+
const messages = sliced.slice(0, cap);
|
|
13420
|
+
const truncated = startIndex + messages.length < filtered.length;
|
|
13421
|
+
return {
|
|
13422
|
+
subagentId: conversation.conversationId,
|
|
13423
|
+
task: conversation.subagentMeta?.task ?? conversation.title,
|
|
13424
|
+
status: conversation.subagentMeta?.status ?? "stopped",
|
|
13425
|
+
totalMessages: filtered.length,
|
|
13426
|
+
startIndex,
|
|
13427
|
+
messages,
|
|
13428
|
+
truncated
|
|
13429
|
+
};
|
|
13004
13430
|
}
|
|
13005
13431
|
};
|
|
13006
13432
|
}
|
|
@@ -13064,7 +13490,7 @@ var runConversationTurn = async (opts) => {
|
|
|
13064
13490
|
if (opts.files && opts.files.length > 0 && opts.harness.uploadStore) {
|
|
13065
13491
|
const uploadedParts = await Promise.all(
|
|
13066
13492
|
opts.files.map(async (f) => {
|
|
13067
|
-
const buf =
|
|
13493
|
+
const buf = await decodeFileInputData(f.data);
|
|
13068
13494
|
const key = deriveUploadKey(buf, f.mediaType);
|
|
13069
13495
|
const ref = await opts.harness.uploadStore.put(key, buf, f.mediaType);
|
|
13070
13496
|
return {
|
|
@@ -13200,7 +13626,33 @@ var runConversationTurn = async (opts) => {
|
|
|
13200
13626
|
input: event.input ?? {},
|
|
13201
13627
|
checkpointMessages: void 0,
|
|
13202
13628
|
baseMessageCount: historyMessages.length,
|
|
13203
|
-
pendingToolCalls: []
|
|
13629
|
+
pendingToolCalls: [],
|
|
13630
|
+
kind: "approval"
|
|
13631
|
+
}
|
|
13632
|
+
];
|
|
13633
|
+
conversation.updatedAt = Date.now();
|
|
13634
|
+
await opts.conversationStore.update(conversation);
|
|
13635
|
+
}
|
|
13636
|
+
await persistDraft();
|
|
13637
|
+
}
|
|
13638
|
+
if (event.type === "tool:device:required") {
|
|
13639
|
+
const toolText = `- device dispatch \`${event.tool}\``;
|
|
13640
|
+
draft.toolTimeline.push(toolText);
|
|
13641
|
+
draft.currentTools.push(toolText);
|
|
13642
|
+
const existing = Array.isArray(conversation.pendingApprovals) ? conversation.pendingApprovals : [];
|
|
13643
|
+
if (!existing.some((a) => a.approvalId === event.requestId)) {
|
|
13644
|
+
conversation.pendingApprovals = [
|
|
13645
|
+
...existing,
|
|
13646
|
+
{
|
|
13647
|
+
approvalId: event.requestId,
|
|
13648
|
+
runId: latestRunId || conversation.runtimeRunId || "",
|
|
13649
|
+
tool: event.tool,
|
|
13650
|
+
toolCallId: void 0,
|
|
13651
|
+
input: event.input ?? {},
|
|
13652
|
+
checkpointMessages: void 0,
|
|
13653
|
+
baseMessageCount: historyMessages.length,
|
|
13654
|
+
pendingToolCalls: [],
|
|
13655
|
+
kind: "device"
|
|
13204
13656
|
}
|
|
13205
13657
|
];
|
|
13206
13658
|
conversation.updatedAt = Date.now();
|
|
@@ -13215,7 +13667,25 @@ var runConversationTurn = async (opts) => {
|
|
|
13215
13667
|
runId: latestRunId,
|
|
13216
13668
|
checkpointMessages: event.checkpointMessages,
|
|
13217
13669
|
baseMessageCount: historyMessages.length,
|
|
13218
|
-
pendingToolCalls: event.pendingToolCalls
|
|
13670
|
+
pendingToolCalls: event.pendingToolCalls,
|
|
13671
|
+
kind: "approval"
|
|
13672
|
+
});
|
|
13673
|
+
conversation._toolResultArchive = opts.harness.getToolResultArchive(
|
|
13674
|
+
opts.conversationId
|
|
13675
|
+
);
|
|
13676
|
+
conversation.updatedAt = Date.now();
|
|
13677
|
+
await opts.conversationStore.update(conversation);
|
|
13678
|
+
checkpointedRun = true;
|
|
13679
|
+
}
|
|
13680
|
+
if (event.type === "tool:device:checkpoint") {
|
|
13681
|
+
conversation.messages = buildMessages();
|
|
13682
|
+
conversation.pendingApprovals = buildApprovalCheckpoints({
|
|
13683
|
+
approvals: event.approvals,
|
|
13684
|
+
runId: latestRunId,
|
|
13685
|
+
checkpointMessages: event.checkpointMessages,
|
|
13686
|
+
baseMessageCount: historyMessages.length,
|
|
13687
|
+
pendingToolCalls: event.pendingToolCalls,
|
|
13688
|
+
kind: "device"
|
|
13219
13689
|
});
|
|
13220
13690
|
conversation._toolResultArchive = opts.harness.getToolResultArchive(
|
|
13221
13691
|
opts.conversationId
|
|
@@ -13418,6 +13888,7 @@ export {
|
|
|
13418
13888
|
createTurnDraftState,
|
|
13419
13889
|
createUploadStore,
|
|
13420
13890
|
createWriteTool,
|
|
13891
|
+
decodeFileInputData,
|
|
13421
13892
|
defaultAgentDefinition,
|
|
13422
13893
|
defineTool13 as defineTool,
|
|
13423
13894
|
deleteOpenAICodexSession,
|
|
@@ -13448,6 +13919,7 @@ export {
|
|
|
13448
13919
|
normalizeApprovalCheckpoint,
|
|
13449
13920
|
normalizeOtlp,
|
|
13450
13921
|
normalizeScriptPolicyPath,
|
|
13922
|
+
normalizeToolAccess,
|
|
13451
13923
|
parseAgentFile,
|
|
13452
13924
|
parseAgentMarkdown,
|
|
13453
13925
|
parseSkillFrontmatter,
|