@archmax-ai/harness 0.3.1 → 0.4.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/README.md +3 -1
- package/dist/assembly/compose.d.ts +40 -3
- package/dist/assembly/compose.js +81 -7
- package/dist/assembly/compose.js.map +1 -1
- package/dist/assembly/delegation-registry.js +5 -2
- package/dist/assembly/delegation-registry.js.map +1 -1
- package/dist/assembly/image-reads.d.ts +53 -0
- package/dist/assembly/image-reads.js +163 -0
- package/dist/assembly/image-reads.js.map +1 -0
- package/dist/assembly/index.d.ts +21 -0
- package/dist/assembly/index.js +7 -1
- package/dist/assembly/index.js.map +1 -1
- package/dist/assembly/plain.js +6 -3
- package/dist/assembly/plain.js.map +1 -1
- package/dist/authoring-skill/archmax-harness/references/backend-integration.md +48 -1
- package/dist/authoring-skill/archmax-harness/references/hook-and-test-scripts.md +27 -1
- package/dist/authoring-skill/archmax-harness/references/workflow-schema.md +71 -25
- package/dist/authoring-skill/archmax-harness/references/workflow-yaml.md +24 -5
- package/dist/core/binary-read.d.ts +52 -0
- package/dist/core/binary-read.js +129 -0
- package/dist/core/binary-read.js.map +1 -0
- package/dist/core/file-operations.d.ts +43 -0
- package/dist/core/file-operations.js +368 -0
- package/dist/core/file-operations.js.map +1 -0
- package/dist/core/path-mapping.d.ts +10 -3
- package/dist/core/path-mapping.js +27 -0
- package/dist/core/path-mapping.js.map +1 -1
- package/dist/core/platform-prompt.generated.js +1 -1
- package/dist/core/platform-prompt.generated.js.map +1 -1
- package/dist/core/prompt.d.ts +27 -0
- package/dist/core/prompt.js +65 -5
- package/dist/core/prompt.js.map +1 -1
- package/dist/core/session-zone.d.ts +3 -0
- package/dist/core/session-zone.js +9 -0
- package/dist/core/session-zone.js.map +1 -1
- package/dist/core/tool-context.d.ts +23 -0
- package/dist/core/tool-context.js +14 -0
- package/dist/core/tool-context.js.map +1 -0
- package/dist/core/tool-telemetry.js +2 -1
- package/dist/core/tool-telemetry.js.map +1 -1
- package/dist/core/workspace-router.d.ts +11 -1
- package/dist/core/workspace-router.js +47 -3
- package/dist/core/workspace-router.js.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/kernel/kernel.js +167 -119
- package/dist/kernel/kernel.js.map +1 -1
- package/dist/machine/allow.d.ts +22 -0
- package/dist/machine/allow.js +47 -15
- package/dist/machine/allow.js.map +1 -1
- package/dist/machine/lint-spec.js +12 -3
- package/dist/machine/lint-spec.js.map +1 -1
- package/dist/machine/machine.d.ts +10 -1
- package/dist/machine/machine.js +36 -8
- package/dist/machine/machine.js.map +1 -1
- package/dist/machine/spec-schema.js +14 -0
- package/dist/machine/spec-schema.js.map +1 -1
- package/dist/machine/tool-names.d.ts +27 -7
- package/dist/machine/tool-names.js +43 -13
- package/dist/machine/tool-names.js.map +1 -1
- package/dist/machine/tool-paths.d.ts +51 -0
- package/dist/machine/tool-paths.js +127 -0
- package/dist/machine/tool-paths.js.map +1 -0
- package/dist/public/spec.d.ts +6 -1
- package/dist/public/spec.js +5 -1
- package/dist/public/spec.js.map +1 -1
- package/dist/validate/validate.js +10 -5
- package/dist/validate/validate.js.map +1 -1
- package/dist/workflow/agent-tools.d.ts +20 -1
- package/dist/workflow/agent-tools.js +31 -1
- package/dist/workflow/agent-tools.js.map +1 -1
- package/dist/workflow/control-tools.d.ts +4 -1
- package/dist/workflow/control-tools.js +24 -15
- package/dist/workflow/control-tools.js.map +1 -1
- package/dist/workflow/governance.d.ts +2 -0
- package/dist/workflow/governance.js +1 -0
- package/dist/workflow/governance.js.map +1 -1
- package/dist/workflow/middleware.d.ts +2 -0
- package/dist/workflow/middleware.js +27 -12
- package/dist/workflow/middleware.js.map +1 -1
- package/dist/workflow/on-error.js +3 -2
- package/dist/workflow/on-error.js.map +1 -1
- package/dist/workflow/parks.js +5 -5
- package/dist/workflow/parks.js.map +1 -1
- package/dist/workflow/render-prompt.d.ts +2 -0
- package/dist/workflow/render-prompt.js +9 -3
- package/dist/workflow/render-prompt.js.map +1 -1
- package/dist/workflow/returns-check.d.ts +35 -0
- package/dist/workflow/returns-check.js +10 -0
- package/dist/workflow/returns-check.js.map +1 -0
- package/dist/workflow/signature-checks.d.ts +26 -1
- package/dist/workflow/signature-checks.js +35 -10
- package/dist/workflow/signature-checks.js.map +1 -1
- package/dist/workflow/state.d.ts +75 -1
- package/dist/workflow/state.js +83 -0
- package/dist/workflow/state.js.map +1 -1
- package/dist/workflow/sub-workflow.d.ts +16 -5
- package/dist/workflow/sub-workflow.js +84 -37
- package/dist/workflow/sub-workflow.js.map +1 -1
- package/dist/workflow/tool-service.js +57 -10
- package/dist/workflow/tool-service.js.map +1 -1
- package/dist/workflow/turn-boundary.d.ts +8 -7
- package/dist/workflow/turn-boundary.js +10 -9
- package/dist/workflow/turn-boundary.js.map +1 -1
- package/dist/workflow/workflow-tools.js +5 -1
- package/dist/workflow/workflow-tools.js.map +1 -1
- package/package.json +1 -1
- package/skills/archmax-harness/references/backend-integration.md +48 -1
- package/skills/archmax-harness/references/hook-and-test-scripts.md +27 -1
- package/skills/archmax-harness/references/workflow-schema.md +71 -25
- package/skills/archmax-harness/references/workflow-yaml.md +24 -5
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { posix } from "node:path";
|
|
2
|
+
/**
|
|
3
|
+
* Reads are text only. Deep Agents' `read_file` hands a non-text file to the
|
|
4
|
+
* model as a base64 multimodal block (`image`, `audio`, `video`, `file`), which
|
|
5
|
+
* an OpenAI-compatible tool message cannot carry and which would otherwise
|
|
6
|
+
* ride in every checkpoint, script result and event preview; and it decodes a
|
|
7
|
+
* binary file whose extension it does not know as UTF-8 garbage. The workspace
|
|
8
|
+
* router refuses both with the notice built here, and the `read_file`
|
|
9
|
+
* description the model is handed says so.
|
|
10
|
+
*
|
|
11
|
+
* The classification is `read_file`'s own — the route's `mimeType`, else the
|
|
12
|
+
* extension's, through `isTextMimeType` — plus two content checks: bytes, and a
|
|
13
|
+
* NUL character (git's and ripgrep's rule), which survives the backend's UTF-8
|
|
14
|
+
* decoding of an unknown extension.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The non-text entries of Deep Agents' extension table (`backends/utils.ts`,
|
|
18
|
+
* identical in 1.13.4 and 1.14.2), which it does not export. Every other
|
|
19
|
+
* extension maps to a text type, or to `text/plain` by default, so only these
|
|
20
|
+
* decide anything. Pinned against `FilesystemBackend` by `binary-read.test.ts`.
|
|
21
|
+
*/
|
|
22
|
+
export const BINARY_MIME_TYPES = {
|
|
23
|
+
".png": "image/png",
|
|
24
|
+
".jpg": "image/jpeg",
|
|
25
|
+
".jpeg": "image/jpeg",
|
|
26
|
+
".gif": "image/gif",
|
|
27
|
+
".webp": "image/webp",
|
|
28
|
+
".heic": "image/heic",
|
|
29
|
+
".heif": "image/heif",
|
|
30
|
+
".mp3": "audio/mpeg",
|
|
31
|
+
".wav": "audio/wav",
|
|
32
|
+
".aiff": "audio/aiff",
|
|
33
|
+
".aac": "audio/aac",
|
|
34
|
+
".ogg": "audio/ogg",
|
|
35
|
+
".flac": "audio/flac",
|
|
36
|
+
".mp4": "video/mp4",
|
|
37
|
+
".webm": "video/webm",
|
|
38
|
+
".mpeg": "video/mpeg",
|
|
39
|
+
".mov": "video/quicktime",
|
|
40
|
+
".avi": "video/x-msvideo",
|
|
41
|
+
".flv": "video/x-flv",
|
|
42
|
+
".mpg": "video/mpeg",
|
|
43
|
+
".wmv": "video/x-ms-wmv",
|
|
44
|
+
".3gpp": "video/3gpp",
|
|
45
|
+
".pdf": "application/pdf",
|
|
46
|
+
".ppt": "application/vnd.ms-powerpoint",
|
|
47
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
48
|
+
};
|
|
49
|
+
/** The MIME type for a path's binary extension, or `undefined` when Deep Agents would read it as text. */
|
|
50
|
+
export function binaryMimeTypeOf(path) {
|
|
51
|
+
return BINARY_MIME_TYPES[posix.extname(path).toLocaleLowerCase()];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The image types the model may be shown (`images` assembly option): the ones
|
|
55
|
+
* chat-completions vision accepts. HEIC/HEIF are binary but not among them, so
|
|
56
|
+
* they stay refused like every other binary file.
|
|
57
|
+
*/
|
|
58
|
+
export const SHOWN_IMAGE_MIME_TYPES = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
|
|
59
|
+
/** The MIME type of a path the model may be shown as an image, or `undefined`. */
|
|
60
|
+
export function shownImageMimeTypeOf(path) {
|
|
61
|
+
const mime = binaryMimeTypeOf(path);
|
|
62
|
+
return mime !== undefined && SHOWN_IMAGE_MIME_TYPES.has(mime) ? mime : undefined;
|
|
63
|
+
}
|
|
64
|
+
/** Deep Agents' `isTextMimeType`: the types `read_file` renders as text. */
|
|
65
|
+
export function isTextMimeType(mimeType) {
|
|
66
|
+
return (mimeType.startsWith("text/") ||
|
|
67
|
+
mimeType === "application/json" ||
|
|
68
|
+
mimeType === "application/javascript" ||
|
|
69
|
+
mimeType === "image/svg+xml");
|
|
70
|
+
}
|
|
71
|
+
/** Byte length of non-string content: bytes, or the numeric-key object they become once serialized. */
|
|
72
|
+
function byteLength(content) {
|
|
73
|
+
if (ArrayBuffer.isView(content))
|
|
74
|
+
return content.byteLength;
|
|
75
|
+
if (content !== null && typeof content === "object")
|
|
76
|
+
return Object.keys(content).length;
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
/** A byte count as a person reads it: `512 B`, `24.1 KB`, `3.2 MB`. */
|
|
80
|
+
export function formatSize(bytes) {
|
|
81
|
+
if (bytes < 1024)
|
|
82
|
+
return `${bytes} B`;
|
|
83
|
+
if (bytes < 1024 * 1024)
|
|
84
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
85
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The binary notice for a successful read of `path`, or `null` when the result
|
|
89
|
+
* is text. A result carrying an `error` is the route's own answer and is never
|
|
90
|
+
* classified.
|
|
91
|
+
*/
|
|
92
|
+
export function binaryReadError(path, result) {
|
|
93
|
+
if (result.error)
|
|
94
|
+
return null;
|
|
95
|
+
const { content } = result;
|
|
96
|
+
const declared = result.mimeType ?? binaryMimeTypeOf(path) ?? "text/plain";
|
|
97
|
+
const typed = !isTextMimeType(declared);
|
|
98
|
+
const bytes = content !== undefined && typeof content !== "string";
|
|
99
|
+
const nul = typeof content === "string" && content.includes("\u0000");
|
|
100
|
+
if (!typed && !bytes && !nul)
|
|
101
|
+
return null;
|
|
102
|
+
const mime = typed ? declared : "application/octet-stream";
|
|
103
|
+
const size = bytes ? byteLength(content) : undefined;
|
|
104
|
+
const detail = size === undefined ? mime : `${mime}, ${formatSize(size)}`;
|
|
105
|
+
const shown = path.replace(/^\/+/, "");
|
|
106
|
+
return `'${shown}' is a binary file (${detail}) and was not read; read_file returns text files only.`;
|
|
107
|
+
}
|
|
108
|
+
/** The line the `read_file` description carries in place of Deep Agents' multimodal promise. */
|
|
109
|
+
export const READ_FILE_TEXT_ONLY_LINE = "- Text files only: a binary file (an image, audio, video, a PDF, an archive, …) is not returned; " +
|
|
110
|
+
"the result says the file is binary and was not read.";
|
|
111
|
+
/** The same line for an agent whose model is shown the images it reads (`images` option). */
|
|
112
|
+
export const READ_FILE_IMAGES_LINE = "- Text files, and images (PNG, JPEG, GIF, WebP): an image is shown to you right after the tool " +
|
|
113
|
+
"result. Any other binary file (audio, video, a PDF, an archive, …) is not returned; the result " +
|
|
114
|
+
"says the file is binary and was not read.";
|
|
115
|
+
const MULTIMODAL_LINE = /^- Images \(.*multimodal content blocks.*$/m;
|
|
116
|
+
const BINARY_PAGINATION_LINE = /^- For images and PDFs, pagination.*(?:\n|$)/m;
|
|
117
|
+
/**
|
|
118
|
+
* Deep Agents' `read_file` description with its binary-file lines replaced by
|
|
119
|
+
* `line` ({@link READ_FILE_TEXT_ONLY_LINE} by default), or `null` when the multimodal line is not
|
|
120
|
+
* there (reworded upstream) — the caller then keeps the description and warns.
|
|
121
|
+
*/
|
|
122
|
+
export function textOnlyReadFileDescription(description, line = READ_FILE_TEXT_ONLY_LINE) {
|
|
123
|
+
if (description.includes(line))
|
|
124
|
+
return description;
|
|
125
|
+
if (!MULTIMODAL_LINE.test(description))
|
|
126
|
+
return null;
|
|
127
|
+
return description.replace(MULTIMODAL_LINE, line).replace(BINARY_PAGINATION_LINE, "");
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=binary-read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary-read.js","sourceRoot":"","sources":["../../src/core/binary-read.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { StructuredTool } from "@langchain/core/tools";
|
|
2
|
+
import type { BackendProtocolV2 } from "deepagents";
|
|
3
|
+
import type { MountPrefixes } from "./mounts.js";
|
|
4
|
+
/** A successful operation, in workspace form. */
|
|
5
|
+
export interface FileOperationDone {
|
|
6
|
+
source: string;
|
|
7
|
+
destination?: string;
|
|
8
|
+
bytes?: number;
|
|
9
|
+
}
|
|
10
|
+
export type FileOperationResult = FileOperationDone | {
|
|
11
|
+
error: string;
|
|
12
|
+
};
|
|
13
|
+
/** Copy one workspace file. Paths in any spelling; the result names them in canonical form. */
|
|
14
|
+
export declare function copyWorkspaceFile(backend: BackendProtocolV2, source: string, destination: string, options?: {
|
|
15
|
+
overwrite?: boolean;
|
|
16
|
+
}): Promise<FileOperationResult>;
|
|
17
|
+
/**
|
|
18
|
+
* Move one workspace file: the destination is written (and verified) before the
|
|
19
|
+
* source is removed, so a failure leaves a duplicate rather than a loss. A
|
|
20
|
+
* source the workspace serves read-only is refused before anything is written —
|
|
21
|
+
* the kernel refuses it too, but an ungoverned agent has only this.
|
|
22
|
+
*/
|
|
23
|
+
export declare function moveWorkspaceFile(backend: BackendProtocolV2, source: string, destination: string, options?: {
|
|
24
|
+
overwrite?: boolean;
|
|
25
|
+
mountPrefixes?: MountPrefixes;
|
|
26
|
+
}): Promise<FileOperationResult>;
|
|
27
|
+
/** Remove one workspace file — a file, never a folder. */
|
|
28
|
+
export declare function removeWorkspaceFile(backend: BackendProtocolV2, path: string): Promise<FileOperationResult>;
|
|
29
|
+
/** The descriptions the model is handed. */
|
|
30
|
+
export declare const FILE_OPERATION_DESCRIPTIONS: {
|
|
31
|
+
readonly copy_file: string;
|
|
32
|
+
readonly move_file: string;
|
|
33
|
+
readonly remove_file: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The runtime's file operations over `ctx.backend` — the assembly's workspace
|
|
37
|
+
* backend, which resolves agent paths against the session bound for the turn,
|
|
38
|
+
* as Deep Agents' own file tools do.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createFileOperationTools(ctx: {
|
|
41
|
+
backend: BackendProtocolV2;
|
|
42
|
+
mountPrefixes: MountPrefixes;
|
|
43
|
+
}): StructuredTool[];
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { tool } from "langchain";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { COPY_FILE_TOOL, MOVE_FILE_TOOL, REMOVE_FILE_TOOL } from "../machine/tool-names.js";
|
|
4
|
+
import { binaryMimeTypeOf, formatSize } from "./binary-read.js";
|
|
5
|
+
import { canonicalizeRelPath } from "./workspace.js";
|
|
6
|
+
import { classifyWorkspacePath } from "./zones.js";
|
|
7
|
+
/** Why the store refused a path, from whatever it said or threw. */
|
|
8
|
+
function classify(detail) {
|
|
9
|
+
if (/read-only mount/.test(detail))
|
|
10
|
+
return "it is served by a read-only mount";
|
|
11
|
+
if (/symlink|symbolic link|ELOOP/i.test(detail))
|
|
12
|
+
return "it is a symbolic link, which the workspace does not follow";
|
|
13
|
+
if (/EISDIR|is_directory|is a directory/i.test(detail))
|
|
14
|
+
return "it is a directory; this tool works on one file";
|
|
15
|
+
if (/ENOENT|not found|file_not_found/i.test(detail))
|
|
16
|
+
return "it does not exist";
|
|
17
|
+
if (/EACCES|EPERM|permission/i.test(detail))
|
|
18
|
+
return "permission was denied";
|
|
19
|
+
if (/cannot delete|not support|not available/i.test(detail))
|
|
20
|
+
return "the store serving it cannot do that";
|
|
21
|
+
return "the store refused it";
|
|
22
|
+
}
|
|
23
|
+
const message = (err) => (err instanceof Error ? err.message : String(err));
|
|
24
|
+
/** A workspace-form path as every backend accepts it. */
|
|
25
|
+
const keyOf = (path) => `/${path}`;
|
|
26
|
+
/** `TextDecoder` that refuses invalid UTF-8 and keeps a byte-order mark. */
|
|
27
|
+
const strictUtf8 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
28
|
+
function asText(bytes) {
|
|
29
|
+
try {
|
|
30
|
+
return strictUtf8.decode(bytes);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function sameBytes(a, b) {
|
|
37
|
+
if (a.byteLength !== b.byteLength)
|
|
38
|
+
return false;
|
|
39
|
+
for (let i = 0; i < a.byteLength; i++)
|
|
40
|
+
if (a[i] !== b[i])
|
|
41
|
+
return false;
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
/** One path's raw bytes, or `null` when the backend has no raw transfer or it failed. */
|
|
45
|
+
async function download(backend, path) {
|
|
46
|
+
if (!backend.downloadFiles)
|
|
47
|
+
return null;
|
|
48
|
+
try {
|
|
49
|
+
const [res] = await backend.downloadFiles([keyOf(path)]);
|
|
50
|
+
return res?.content ?? null;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* `readRaw` as a gate: the file's content, or the refusal any read of it meets.
|
|
58
|
+
* A filesystem `readRaw` reports a directory as not found; the raw channel names
|
|
59
|
+
* it, and is consulted for the reason only.
|
|
60
|
+
*/
|
|
61
|
+
async function gate(backend, path, verb) {
|
|
62
|
+
let refusal;
|
|
63
|
+
try {
|
|
64
|
+
const res = await backend.readRaw(keyOf(path));
|
|
65
|
+
if (!res.error && res.data)
|
|
66
|
+
return { content: res.data.content };
|
|
67
|
+
refusal = res.error ?? "not found";
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
refusal = message(err);
|
|
71
|
+
}
|
|
72
|
+
let reason = classify(refusal);
|
|
73
|
+
if (reason === "it does not exist" && backend.downloadFiles) {
|
|
74
|
+
try {
|
|
75
|
+
const [res] = await backend.downloadFiles([keyOf(path)]);
|
|
76
|
+
if (res?.error === "is_directory")
|
|
77
|
+
reason = classify(res.error);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// No raw transfer: the first reason stands.
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return { error: `Cannot ${verb} '${path}': ${reason}.` };
|
|
84
|
+
}
|
|
85
|
+
/** Whether a file is already at `path` (a refusal to read it says nothing either way). */
|
|
86
|
+
async function fileExists(backend, path) {
|
|
87
|
+
try {
|
|
88
|
+
const res = await backend.readRaw(keyOf(path));
|
|
89
|
+
return !res.error && res.data !== undefined;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** The source's exact bytes, gated by `readRaw`'s refusals. */
|
|
96
|
+
async function readSource(backend, path, verb) {
|
|
97
|
+
const read = await gate(backend, path, verb);
|
|
98
|
+
if ("error" in read)
|
|
99
|
+
return read;
|
|
100
|
+
const { content } = read;
|
|
101
|
+
if (ArrayBuffer.isView(content)) {
|
|
102
|
+
return { bytes: new Uint8Array(content.buffer, content.byteOffset, content.byteLength) };
|
|
103
|
+
}
|
|
104
|
+
// Text: the backend decoded it, which loses whatever was not UTF-8. The path
|
|
105
|
+
// has passed `readRaw`'s refusals, so its raw bytes may now be taken.
|
|
106
|
+
const exact = await download(backend, path);
|
|
107
|
+
if (exact)
|
|
108
|
+
return { bytes: exact };
|
|
109
|
+
const text = Array.isArray(content) ? content.join("\n") : String(content ?? "");
|
|
110
|
+
// Without the raw channel, a replacement character is where decoding may have
|
|
111
|
+
// lost bytes: refuse rather than write something the source never held.
|
|
112
|
+
if (text.includes("�")) {
|
|
113
|
+
return {
|
|
114
|
+
error: `Cannot ${verb} '${path}': it is not UTF-8 text, and the store serving it cannot hand over ` +
|
|
115
|
+
`its bytes unchanged.`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return { bytes: new TextEncoder().encode(text) };
|
|
119
|
+
}
|
|
120
|
+
/** A `write`/`uploadFiles` outcome as the refusal, or `null` when it succeeded. */
|
|
121
|
+
async function attempt(path, verb, op) {
|
|
122
|
+
try {
|
|
123
|
+
const res = await op();
|
|
124
|
+
return res?.error ? `Cannot ${verb} to '${path}': ${classify(res.error)}.` : null;
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
return `Cannot ${verb} to '${path}': ${classify(message(err))}.`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* What the store now holds at `path`, read back to confirm a write: the raw
|
|
132
|
+
* bytes when it can download them, else what `readRaw` returns (bytes, or text
|
|
133
|
+
* encoded as UTF-8). `null` when it cannot be read back at all.
|
|
134
|
+
*/
|
|
135
|
+
async function readBack(backend, path) {
|
|
136
|
+
const raw = await download(backend, path);
|
|
137
|
+
if (raw)
|
|
138
|
+
return raw;
|
|
139
|
+
try {
|
|
140
|
+
const res = await backend.readRaw(keyOf(path));
|
|
141
|
+
if (res.error || !res.data)
|
|
142
|
+
return null;
|
|
143
|
+
const { content } = res.data;
|
|
144
|
+
if (ArrayBuffer.isView(content))
|
|
145
|
+
return new Uint8Array(content.buffer, content.byteOffset, content.byteLength);
|
|
146
|
+
return new TextEncoder().encode(Array.isArray(content) ? content.join("\n") : String(content));
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Write `bytes` at `path` and confirm the store kept them, or say why not.
|
|
154
|
+
*
|
|
155
|
+
* The text channel goes first whenever it can carry the bytes: base64 for a
|
|
156
|
+
* binary-typed path (Deep Agents' convention — the store decodes it), the text
|
|
157
|
+
* itself when the bytes are valid UTF-8. A store that kept something else — the
|
|
158
|
+
* base64 string as given, a decoded variant — gets the raw channel next, when it
|
|
159
|
+
* has one. Bytes no text channel carries claim the path with an empty `write`
|
|
160
|
+
* first, because the upload follows a symlink and a write does not. Every write
|
|
161
|
+
* is read back; a destination the operation created and could not fill is
|
|
162
|
+
* removed again, so a refusal leaves nothing behind.
|
|
163
|
+
*/
|
|
164
|
+
async function writeDestination(backend, path, bytes, verb, fresh) {
|
|
165
|
+
const binaryType = binaryMimeTypeOf(path) !== undefined;
|
|
166
|
+
const text = binaryType ? null : asText(bytes);
|
|
167
|
+
const kept = async () => {
|
|
168
|
+
const back = await readBack(backend, path);
|
|
169
|
+
return back !== null && sameBytes(back, bytes);
|
|
170
|
+
};
|
|
171
|
+
if (binaryType || text !== null) {
|
|
172
|
+
const content = binaryType ? Buffer.from(bytes).toString("base64") : text;
|
|
173
|
+
const refused = await attempt(path, verb, async () => backend.write(keyOf(path), content));
|
|
174
|
+
if (refused)
|
|
175
|
+
return refused;
|
|
176
|
+
if (await kept())
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
const claimed = await attempt(path, verb, async () => backend.write(keyOf(path), ""));
|
|
181
|
+
if (claimed)
|
|
182
|
+
return claimed;
|
|
183
|
+
}
|
|
184
|
+
if (backend.uploadFiles) {
|
|
185
|
+
const uploadFiles = backend.uploadFiles.bind(backend);
|
|
186
|
+
const refused = await attempt(path, verb, async () => (await uploadFiles([[keyOf(path), bytes]]))[0]);
|
|
187
|
+
if (refused)
|
|
188
|
+
return refused + (await leftBehind(backend, path, fresh));
|
|
189
|
+
if (await kept())
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
const why = binaryType
|
|
193
|
+
? "a write to a binary-typed path carries base64, which the store must decode, and it did not"
|
|
194
|
+
: text === null
|
|
195
|
+
? "it holds files of this type as text, and this file is not text"
|
|
196
|
+
: "what it holds differs from what was written";
|
|
197
|
+
return (`Cannot ${verb} to '${path}': the store serving it did not keep the file's bytes — ${why}.` +
|
|
198
|
+
(await leftBehind(backend, path, fresh)));
|
|
199
|
+
}
|
|
200
|
+
/** Undo a failed write the operation created; the sentence saying what is left. */
|
|
201
|
+
async function leftBehind(backend, path, fresh) {
|
|
202
|
+
if (fresh && (await removeAt(backend, path)) === null)
|
|
203
|
+
return " Nothing was kept.";
|
|
204
|
+
return ` '${path}' was left as the store wrote it.`;
|
|
205
|
+
}
|
|
206
|
+
/** Canonical workspace form of each path, or the refusal of the first that has none. */
|
|
207
|
+
function canonicalPaths(verb, ...given) {
|
|
208
|
+
const paths = [];
|
|
209
|
+
for (const path of given) {
|
|
210
|
+
const resolved = canonicalizeRelPath(path);
|
|
211
|
+
if (resolved.escapes)
|
|
212
|
+
return { error: `Cannot ${verb} '${path}': it resolves outside the workspace root.` };
|
|
213
|
+
if (resolved.path === "")
|
|
214
|
+
return { error: `Cannot ${verb} '${path}': it names the workspace root, not a file.` };
|
|
215
|
+
paths.push(resolved.path);
|
|
216
|
+
}
|
|
217
|
+
return { paths };
|
|
218
|
+
}
|
|
219
|
+
/** The checks a copy and a move share before any byte moves. */
|
|
220
|
+
async function precheckTransfer(backend, verb, source, destination, overwrite) {
|
|
221
|
+
const canonical = canonicalPaths(verb, source, destination);
|
|
222
|
+
if ("error" in canonical)
|
|
223
|
+
return canonical;
|
|
224
|
+
const [from, to] = canonical.paths;
|
|
225
|
+
if (from === to) {
|
|
226
|
+
return { error: `Cannot ${verb} '${from}' onto itself: the source and the destination are the same file.` };
|
|
227
|
+
}
|
|
228
|
+
const existed = await fileExists(backend, to);
|
|
229
|
+
if (existed && !overwrite) {
|
|
230
|
+
return {
|
|
231
|
+
error: `Cannot ${verb} to '${to}': a file is already there. Pass overwrite: true to replace it, or ` +
|
|
232
|
+
`choose another destination.`,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
return { from, to, fresh: !existed };
|
|
236
|
+
}
|
|
237
|
+
/** Copy one workspace file. Paths in any spelling; the result names them in canonical form. */
|
|
238
|
+
export async function copyWorkspaceFile(backend, source, destination, options = {}) {
|
|
239
|
+
const checked = await precheckTransfer(backend, "copy", source, destination, options.overwrite === true);
|
|
240
|
+
if ("error" in checked)
|
|
241
|
+
return checked;
|
|
242
|
+
const read = await readSource(backend, checked.from, "copy");
|
|
243
|
+
if ("error" in read)
|
|
244
|
+
return read;
|
|
245
|
+
const refused = await writeDestination(backend, checked.to, read.bytes, "copy", checked.fresh);
|
|
246
|
+
if (refused)
|
|
247
|
+
return { error: refused };
|
|
248
|
+
return { source: checked.from, destination: checked.to, bytes: read.bytes.byteLength };
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Move one workspace file: the destination is written (and verified) before the
|
|
252
|
+
* source is removed, so a failure leaves a duplicate rather than a loss. A
|
|
253
|
+
* source the workspace serves read-only is refused before anything is written —
|
|
254
|
+
* the kernel refuses it too, but an ungoverned agent has only this.
|
|
255
|
+
*/
|
|
256
|
+
export async function moveWorkspaceFile(backend, source, destination, options = {}) {
|
|
257
|
+
const checked = await precheckTransfer(backend, "move", source, destination, options.overwrite === true);
|
|
258
|
+
if ("error" in checked)
|
|
259
|
+
return checked;
|
|
260
|
+
const { from, to, fresh } = checked;
|
|
261
|
+
if (classifyWorkspacePath(from, options.mountPrefixes) === "authored") {
|
|
262
|
+
return {
|
|
263
|
+
error: `Cannot move '${from}': it is served by a read-only mount, so it cannot be removed. Use ` +
|
|
264
|
+
`copy_file to copy it instead.`,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
const read = await readSource(backend, from, "move");
|
|
268
|
+
if ("error" in read)
|
|
269
|
+
return read;
|
|
270
|
+
const refused = await writeDestination(backend, to, read.bytes, "move", fresh);
|
|
271
|
+
if (refused)
|
|
272
|
+
return { error: refused };
|
|
273
|
+
const removed = await removeAt(backend, from);
|
|
274
|
+
if (removed) {
|
|
275
|
+
return {
|
|
276
|
+
error: `Copied '${from}' to '${to}', but could not remove '${from}': ${removed}. Both files now ` +
|
|
277
|
+
`exist.`,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
return { source: from, destination: to, bytes: read.bytes.byteLength };
|
|
281
|
+
}
|
|
282
|
+
/** Delete the file at `path`; the reason it was refused, or `null`. */
|
|
283
|
+
async function removeAt(backend, path) {
|
|
284
|
+
if (!backend.delete)
|
|
285
|
+
return "the store serving it cannot delete";
|
|
286
|
+
try {
|
|
287
|
+
const res = await backend.delete(keyOf(path));
|
|
288
|
+
return res.error ? classify(res.error) : null;
|
|
289
|
+
}
|
|
290
|
+
catch (err) {
|
|
291
|
+
return classify(message(err));
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/** Remove one workspace file — a file, never a folder. */
|
|
295
|
+
export async function removeWorkspaceFile(backend, path) {
|
|
296
|
+
const canonical = canonicalPaths("remove", path);
|
|
297
|
+
if ("error" in canonical)
|
|
298
|
+
return canonical;
|
|
299
|
+
const [target] = canonical.paths;
|
|
300
|
+
// The same refusals a read meets: a folder, a missing file, a symlink.
|
|
301
|
+
const read = await gate(backend, target, "remove");
|
|
302
|
+
if ("error" in read)
|
|
303
|
+
return read;
|
|
304
|
+
const refused = await removeAt(backend, target);
|
|
305
|
+
if (refused)
|
|
306
|
+
return { error: `Cannot remove '${target}': ${refused}.` };
|
|
307
|
+
return { source: target };
|
|
308
|
+
}
|
|
309
|
+
/** The descriptions the model is handed. */
|
|
310
|
+
export const FILE_OPERATION_DESCRIPTIONS = {
|
|
311
|
+
[COPY_FILE_TOOL]: [
|
|
312
|
+
"Copies one file to another path, byte for byte — text or binary (an image, a PDF, a .docx) — " +
|
|
313
|
+
"without reading it into the conversation. Creates the destination's folders and leaves the " +
|
|
314
|
+
"source untouched.",
|
|
315
|
+
"",
|
|
316
|
+
"Usage:",
|
|
317
|
+
"- Use it to start from a template or asset (e.g. one in a skill's assets/) instead of " +
|
|
318
|
+
"read_file followed by write_file.",
|
|
319
|
+
"- A file already at destination is refused unless overwrite is true.",
|
|
320
|
+
"- It copies a single file, not a folder.",
|
|
321
|
+
].join("\n"),
|
|
322
|
+
[MOVE_FILE_TOOL]: [
|
|
323
|
+
"Moves one file to another path, byte for byte, without reading it into the conversation. The " +
|
|
324
|
+
"destination is written before the source is removed, so a failure leaves a copy, never a loss.",
|
|
325
|
+
"",
|
|
326
|
+
"Usage:",
|
|
327
|
+
"- A file already at destination is refused unless overwrite is true.",
|
|
328
|
+
"- A file in a read-only folder cannot be moved; use copy_file for it.",
|
|
329
|
+
"- It moves a single file, not a folder.",
|
|
330
|
+
].join("\n"),
|
|
331
|
+
[REMOVE_FILE_TOOL]: [
|
|
332
|
+
"Deletes one file.",
|
|
333
|
+
"",
|
|
334
|
+
"Usage:",
|
|
335
|
+
"- It deletes a single file, not a folder.",
|
|
336
|
+
"- A file in a read-only folder cannot be removed.",
|
|
337
|
+
].join("\n"),
|
|
338
|
+
};
|
|
339
|
+
const transferSchema = (verb) => z.object({
|
|
340
|
+
source: z.string().describe(`Path of the file to ${verb}`),
|
|
341
|
+
destination: z.string().describe(`Path to ${verb} it to`),
|
|
342
|
+
overwrite: z
|
|
343
|
+
.boolean()
|
|
344
|
+
.optional()
|
|
345
|
+
.describe("Replace a file already at destination (default false: such a destination is refused)"),
|
|
346
|
+
});
|
|
347
|
+
/** One line for the model: the success sentence, or `Error: …`. */
|
|
348
|
+
function render(res, done) {
|
|
349
|
+
return "error" in res ? `Error: ${res.error}` : done(res);
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* The runtime's file operations over `ctx.backend` — the assembly's workspace
|
|
353
|
+
* backend, which resolves agent paths against the session bound for the turn,
|
|
354
|
+
* as Deep Agents' own file tools do.
|
|
355
|
+
*/
|
|
356
|
+
export function createFileOperationTools(ctx) {
|
|
357
|
+
const { backend, mountPrefixes } = ctx;
|
|
358
|
+
return [
|
|
359
|
+
tool(async ({ source, destination, overwrite }) => render(await copyWorkspaceFile(backend, source, destination, { overwrite: overwrite === true }), (ok) => `Copied '${ok.source}' to '${ok.destination}' (${formatSize(ok.bytes ?? 0)}).`), { name: COPY_FILE_TOOL, description: FILE_OPERATION_DESCRIPTIONS[COPY_FILE_TOOL], schema: transferSchema("copy") }),
|
|
360
|
+
tool(async ({ source, destination, overwrite }) => render(await moveWorkspaceFile(backend, source, destination, { overwrite: overwrite === true, mountPrefixes }), (ok) => `Moved '${ok.source}' to '${ok.destination}' (${formatSize(ok.bytes ?? 0)}).`), { name: MOVE_FILE_TOOL, description: FILE_OPERATION_DESCRIPTIONS[MOVE_FILE_TOOL], schema: transferSchema("move") }),
|
|
361
|
+
tool(async ({ file_path }) => render(await removeWorkspaceFile(backend, file_path), (ok) => `Removed '${ok.source}'.`), {
|
|
362
|
+
name: REMOVE_FILE_TOOL,
|
|
363
|
+
description: FILE_OPERATION_DESCRIPTIONS[REMOVE_FILE_TOOL],
|
|
364
|
+
schema: z.object({ file_path: z.string().describe("Path of the file to delete") }),
|
|
365
|
+
}),
|
|
366
|
+
];
|
|
367
|
+
}
|
|
368
|
+
//# sourceMappingURL=file-operations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-operations.js","sourceRoot":"","sources":["../../src/core/file-operations.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5F,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEhE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AA4CnD,oEAAoE;AACpE,SAAS,QAAQ,CAAC,MAAc;IAC9B,IAAI,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,mCAAmC,CAAC;IAC/E,IAAI,8BAA8B,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,4DAA4D,CAAC;IACrH,IAAI,qCAAqC,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,gDAAgD,CAAC;IAChH,IAAI,kCAAkC,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,mBAAmB,CAAC;IAChF,IAAI,0BAA0B,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,uBAAuB,CAAC;IAC5E,IAAI,0CAA0C,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,qCAAqC,CAAC;IAC1G,OAAO,sBAAsB,CAAC;AAChC,CAAC;AAED,MAAM,OAAO,GAAG,CAAC,GAAY,EAAU,EAAE,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AAE7F,yDAAyD;AACzD,MAAM,KAAK,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;AAEnD,4EAA4E;AAC5E,MAAM,UAAU,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAE9E,SAAS,MAAM,CAAC,KAAiB;IAC/B,IAAI,CAAC;QACH,OAAO,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,CAAa,EAAE,CAAa;IAC7C,IAAI,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAChD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC,EAAE;QAAE,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;IACvE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,yFAAyF;AACzF,KAAK,UAAU,QAAQ,CAAC,OAA0B,EAAE,IAAY;IAC9D,IAAI,CAAC,OAAO,CAAC,aAAa;QAAE,OAAO,IAAI,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzD,OAAO,GAAG,EAAE,OAAO,IAAI,IAAI,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,IAAI,CACjB,OAA0B,EAC1B,IAAY,EACZ,IAAU;IAEV,IAAI,OAA2B,CAAC;IAChC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/C,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI;YAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACjE,OAAO,GAAG,GAAG,CAAC,KAAK,IAAI,WAAW,CAAC;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IACD,IAAI,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC/B,IAAI,MAAM,KAAK,mBAAmB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QAC5D,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACzD,IAAI,GAAG,EAAE,KAAK,KAAK,cAAc;gBAAE,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAClE,CAAC;QAAC,MAAM,CAAC;YACP,4CAA4C;QAC9C,CAAC;IACH,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,UAAU,IAAI,KAAK,IAAI,MAAM,MAAM,GAAG,EAAE,CAAC;AAC3D,CAAC;AAED,0FAA0F;AAC1F,KAAK,UAAU,UAAU,CAAC,OAA0B,EAAE,IAAY;IAChE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,+DAA+D;AAC/D,KAAK,UAAU,UAAU,CACvB,OAA0B,EAC1B,IAAY,EACZ,IAAU;IAEV,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,IAAI,OAAO,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACzB,IAAI,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,OAAO,EAAE,KAAK,EAAE,IAAI,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;IAC3F,CAAC;IACD,6EAA6E;IAC7E,sEAAsE;IACtE,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC5C,IAAI,KAAK;QAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACnC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IACjF,8EAA8E;IAC9E,wEAAwE;IACxE,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO;YACL,KAAK,EACH,UAAU,IAAI,KAAK,IAAI,qEAAqE;gBAC5F,sBAAsB;SACzB,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;AACnD,CAAC;AAED,mFAAmF;AACnF,KAAK,UAAU,OAAO,CACpB,IAAY,EACZ,IAAU,EACV,EAAwD;IAExD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,EAAE,CAAC;QACvB,OAAO,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,UAAU,IAAI,QAAQ,IAAI,MAAM,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IACpF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,UAAU,IAAI,QAAQ,IAAI,MAAM,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;IACnE,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,QAAQ,CAAC,OAA0B,EAAE,IAAY;IAC9D,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC1C,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/C,IAAI,GAAG,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACxC,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;QAC7B,IAAI,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;QAC/G,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACjG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,gBAAgB,CAC7B,OAA0B,EAC1B,IAAY,EACZ,KAAiB,EACjB,IAAU,EACV,KAAc;IAEd,MAAM,UAAU,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC;IACxD,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE;QACtB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC3C,OAAO,IAAI,KAAK,IAAI,IAAI,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACjD,CAAC,CAAC;IACF,IAAI,UAAU,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAK,CAAC;QAC3E,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QAC3F,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;QAC5B,IAAI,MAAM,IAAI,EAAE;YAAE,OAAO,IAAI,CAAC;IAChC,CAAC;SAAM,CAAC;QACN,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACtF,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;IAC9B,CAAC;IACD,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QACxB,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACtG,IAAI,OAAO;YAAE,OAAO,OAAO,GAAG,CAAC,MAAM,UAAU,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;QACvE,IAAI,MAAM,IAAI,EAAE;YAAE,OAAO,IAAI,CAAC;IAChC,CAAC;IACD,MAAM,GAAG,GAAG,UAAU;QACpB,CAAC,CAAC,4FAA4F;QAC9F,CAAC,CAAC,IAAI,KAAK,IAAI;YACb,CAAC,CAAC,gEAAgE;YAClE,CAAC,CAAC,6CAA6C,CAAC;IACpD,OAAO,CACL,UAAU,IAAI,QAAQ,IAAI,2DAA2D,GAAG,GAAG;QAC3F,CAAC,MAAM,UAAU,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CACzC,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,KAAK,UAAU,UAAU,CAAC,OAA0B,EAAE,IAAY,EAAE,KAAc;IAChF,IAAI,KAAK,IAAI,CAAC,MAAM,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI;QAAE,OAAO,oBAAoB,CAAC;IACnF,OAAO,KAAK,IAAI,mCAAmC,CAAC;AACtD,CAAC;AAED,wFAAwF;AACxF,SAAS,cAAc,CAAC,IAAU,EAAE,GAAG,KAAe;IACpD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,QAAQ,CAAC,OAAO;YAAE,OAAO,EAAE,KAAK,EAAE,UAAU,IAAI,KAAK,IAAI,4CAA4C,EAAE,CAAC;QAC5G,IAAI,QAAQ,CAAC,IAAI,KAAK,EAAE;YAAE,OAAO,EAAE,KAAK,EAAE,UAAU,IAAI,KAAK,IAAI,6CAA6C,EAAE,CAAC;QACjH,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,CAAC;AACnB,CAAC;AAED,gEAAgE;AAChE,KAAK,UAAU,gBAAgB,CAC7B,OAA0B,EAC1B,IAAqB,EACrB,MAAc,EACd,WAAmB,EACnB,SAAkB;IAElB,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAC5D,IAAI,OAAO,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3C,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC,KAAyB,CAAC;IACvD,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QAChB,OAAO,EAAE,KAAK,EAAE,UAAU,IAAI,KAAK,IAAI,kEAAkE,EAAE,CAAC;IAC9G,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC9C,IAAI,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC;QAC1B,OAAO;YACL,KAAK,EACH,UAAU,IAAI,QAAQ,EAAE,qEAAqE;gBAC7F,6BAA6B;SAChC,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE,CAAC;AACvC,CAAC;AAED,+FAA+F;AAC/F,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAA0B,EAC1B,MAAc,EACd,WAAmB,EACnB,UAAmC,EAAE;IAErC,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC;IACzG,IAAI,OAAO,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IACvC,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC7D,IAAI,OAAO,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC/F,IAAI,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IACvC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;AACzF,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAA0B,EAC1B,MAAc,EACd,WAAmB,EACnB,UAAkE,EAAE;IAEpE,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC;IACzG,IAAI,OAAO,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IACvC,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IACpC,IAAI,qBAAqB,CAAC,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,KAAK,UAAU,EAAE,CAAC;QACtE,OAAO;YACL,KAAK,EACH,gBAAgB,IAAI,qEAAqE;gBACzF,+BAA+B;SAClC,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACrD,IAAI,OAAO,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAC/E,IAAI,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IACvC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9C,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO;YACL,KAAK,EACH,WAAW,IAAI,SAAS,EAAE,4BAA4B,IAAI,MAAM,OAAO,mBAAmB;gBAC1F,QAAQ;SACX,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;AACzE,CAAC;AAED,uEAAuE;AACvE,KAAK,UAAU,QAAQ,CAAC,OAA0B,EAAE,IAAY;IAC9D,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,oCAAoC,CAAC;IACjE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9C,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAChC,CAAC;AACH,CAAC;AAED,0DAA0D;AAC1D,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,OAA0B,EAAE,IAAY;IAChF,MAAM,SAAS,GAAG,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACjD,IAAI,OAAO,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3C,MAAM,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,KAAiB,CAAC;IAC7C,uEAAuE;IACvE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IACnD,IAAI,OAAO,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAChD,IAAI,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;IACxE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC;AAED,4CAA4C;AAC5C,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,CAAC,cAAc,CAAC,EAAE;QAChB,+FAA+F;YAC7F,6FAA6F;YAC7F,mBAAmB;QACrB,EAAE;QACF,QAAQ;QACR,wFAAwF;YACtF,mCAAmC;QACrC,sEAAsE;QACtE,0CAA0C;KAC3C,CAAC,IAAI,CAAC,IAAI,CAAC;IACZ,CAAC,cAAc,CAAC,EAAE;QAChB,+FAA+F;YAC7F,gGAAgG;QAClG,EAAE;QACF,QAAQ;QACR,sEAAsE;QACtE,uEAAuE;QACvE,yCAAyC;KAC1C,CAAC,IAAI,CAAC,IAAI,CAAC;IACZ,CAAC,gBAAgB,CAAC,EAAE;QAClB,mBAAmB;QACnB,EAAE;QACF,QAAQ;QACR,2CAA2C;QAC3C,mDAAmD;KACpD,CAAC,IAAI,CAAC,IAAI,CAAC;CACJ,CAAC;AAEX,MAAM,cAAc,GAAG,CAAC,IAAqB,EAAE,EAAE,CAC/C,CAAC,CAAC,MAAM,CAAC;IACP,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uBAAuB,IAAI,EAAE,CAAC;IAC1D,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,IAAI,QAAQ,CAAC;IACzD,SAAS,EAAE,CAAC;SACT,OAAO,EAAE;SACT,QAAQ,EAAE;SACV,QAAQ,CAAC,sFAAsF,CAAC;CACpG,CAAC,CAAC;AAEL,mEAAmE;AACnE,SAAS,MAAM,CAAC,GAAwB,EAAE,IAAuC;IAC/E,OAAO,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAGxC;IACC,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,GAAG,CAAC;IACvC,OAAO;QACL,IAAI,CACF,KAAK,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,EAAE,CAC3C,MAAM,CACJ,MAAM,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,SAAS,KAAK,IAAI,EAAE,CAAC,EACxF,CAAC,EAAE,EAAE,EAAE,CAAC,WAAW,EAAE,CAAC,MAAM,SAAS,EAAE,CAAC,WAAW,MAAM,UAAU,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CACvF,EACH,EAAE,IAAI,EAAE,cAAc,EAAE,WAAW,EAAE,2BAA2B,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,EAAE,CACnH;QACD,IAAI,CACF,KAAK,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,EAAE,CAC3C,MAAM,CACJ,MAAM,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,SAAS,KAAK,IAAI,EAAE,aAAa,EAAE,CAAC,EACvG,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,EAAE,CAAC,MAAM,SAAS,EAAE,CAAC,WAAW,MAAM,UAAU,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CACtF,EACH,EAAE,IAAI,EAAE,cAAc,EAAE,WAAW,EAAE,2BAA2B,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,EAAE,CACnH;QACD,IAAI,CACF,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CACtB,MAAM,CAAC,MAAM,mBAAmB,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,YAAY,EAAE,CAAC,MAAM,IAAI,CAAC,EAC1F;YACE,IAAI,EAAE,gBAAgB;YACtB,WAAW,EAAE,2BAA2B,CAAC,gBAAgB,CAAC;YAC1D,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC,EAAE,CAAC;SACnF,CACF;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -15,12 +15,19 @@ import type { BackendProtocolV2 } from "deepagents";
|
|
|
15
15
|
* agent-visible listings, so it is stripped on the way out.
|
|
16
16
|
*
|
|
17
17
|
* Inbound: `/x` → `/<prefix>/x`. Outbound: every `path` a result carries
|
|
18
|
-
* (`ls`/`glob` entries, `grep` matches, `write`/`edit` results
|
|
18
|
+
* (`ls`/`glob` entries, `grep` matches, `write`/`edit`/`delete` results,
|
|
19
|
+
* download and upload responses) is mapped back.
|
|
19
20
|
* A result path that does not start with the prefix is left untouched — the
|
|
20
21
|
* inner backend is free to report paths we did not ask about.
|
|
21
22
|
*/
|
|
22
23
|
/** The prefix in effect for a call: a fixed string, or resolved per call. */
|
|
23
24
|
export type PrefixResolver = () => string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* A mapped backend: raw-byte transfer and deletion are always present. Transfer
|
|
27
|
+
* throws when the wrapped backend lacks it, as `CompositeBackend` does; deletion
|
|
28
|
+
* answers an error, as `CompositeBackend` does.
|
|
29
|
+
*/
|
|
30
|
+
export type MappedBackend = BackendProtocolV2 & Required<Pick<BackendProtocolV2, "downloadFiles" | "uploadFiles" | "delete">>;
|
|
24
31
|
/**
|
|
25
32
|
* Wrap `backend` so it is addressed under `prefix` from the outside, mapping
|
|
26
33
|
* paths in both directions. `prefix` may be a resolver, returning `undefined`
|
|
@@ -35,7 +42,7 @@ export type PrefixResolver = () => string | undefined;
|
|
|
35
42
|
export declare function mountSubtree(backend: BackendProtocolV2, prefix: string | PrefixResolver, options?: {
|
|
36
43
|
passthrough?: (relPath: string, prefix: string) => boolean;
|
|
37
44
|
/**
|
|
38
|
-
* Refuse `write`/`edit` at the mount instead of delegating. Read-only is a
|
|
45
|
+
* Refuse `write`/`edit`/`uploadFiles`/`delete` at the mount instead of delegating. Read-only is a
|
|
39
46
|
* property of the mount, so an authored mount cannot be written through even
|
|
40
47
|
* by a caller that bypasses governance — the kernel's `zone.read-only` rule
|
|
41
48
|
* remains the agent-facing diagnostic, not the only enforcement.
|
|
@@ -49,4 +56,4 @@ export declare function mountSubtree(backend: BackendProtocolV2, prefix: string
|
|
|
49
56
|
* which is exactly the confusion that makes it retry with invented paths.
|
|
50
57
|
*/
|
|
51
58
|
displayPrefix?: string;
|
|
52
|
-
}):
|
|
59
|
+
}): MappedBackend;
|
|
@@ -102,6 +102,33 @@ export function mountSubtree(backend, prefix, options = {}) {
|
|
|
102
102
|
const res = await backend.edit(mapIn(filePath, active), oldString, newString, replaceAll);
|
|
103
103
|
return res.path === undefined ? res : { ...res, path: mapOut(res.path, active) };
|
|
104
104
|
},
|
|
105
|
+
// Raw-byte transfer. A backend without it throws, as `CompositeBackend` does
|
|
106
|
+
// for a route without it, so a caller has one "unsupported" signal to handle.
|
|
107
|
+
async downloadFiles(paths) {
|
|
108
|
+
if (!backend.downloadFiles)
|
|
109
|
+
throw new Error("Backend does not support downloadFiles");
|
|
110
|
+
const actives = paths.map(prefixFor);
|
|
111
|
+
const res = await backend.downloadFiles(paths.map((path, i) => mapIn(path, actives[i])));
|
|
112
|
+
return res.map((entry, i) => ({ ...entry, path: mapOut(entry.path, actives[i]) }));
|
|
113
|
+
},
|
|
114
|
+
async delete(filePath) {
|
|
115
|
+
if (options.readOnly)
|
|
116
|
+
return readOnlyError("delete", filePath);
|
|
117
|
+
if (!backend.delete)
|
|
118
|
+
return { error: `Cannot delete '${displayPath(filePath)}': the store serving it cannot delete.` };
|
|
119
|
+
const active = prefixFor(filePath);
|
|
120
|
+
const res = await backend.delete(mapIn(filePath, active));
|
|
121
|
+
return res.path === undefined ? res : { ...res, path: mapOut(res.path, active) };
|
|
122
|
+
},
|
|
123
|
+
async uploadFiles(files) {
|
|
124
|
+
if (options.readOnly)
|
|
125
|
+
return files.map(([path]) => ({ path, error: "permission_denied" }));
|
|
126
|
+
if (!backend.uploadFiles)
|
|
127
|
+
throw new Error("Backend does not support uploadFiles");
|
|
128
|
+
const actives = files.map(([path]) => prefixFor(path));
|
|
129
|
+
const res = await backend.uploadFiles(files.map(([path, content], i) => [mapIn(path, actives[i]), content]));
|
|
130
|
+
return res.map((entry, i) => ({ ...entry, path: mapOut(entry.path, actives[i]) }));
|
|
131
|
+
},
|
|
105
132
|
};
|
|
106
133
|
}
|
|
107
134
|
function optionalRel(prefix) {
|