@fungi.computer/knapsack 0.1.0 → 0.2.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 CHANGED
@@ -36,6 +36,15 @@ catalog or a result. Servers connect concurrently under a timeout. One that
36
36
  rejects, refuses its credential, times out or advertises an unusable list is
37
37
  dropped and reported through `onUnavailable`, while the others stay available.
38
38
 
39
+ `Knapsack.ui({ id, hasSession })` connects the App open in an Agent Session's
40
+ browser. Its `source` lends each Session run the `ui` module: `ui.discover`
41
+ lists the App's live Whistle commands (its agent projection) and `ui.execute`
42
+ runs one. The run returns the module when it ends. Its `fetch` serves
43
+ `/browser/sessions/<sessionId>/browser/{events,claims,results}`: the browser
44
+ claims each request before it runs it, and the host accepts only the matching
45
+ result. The browser side is `connectBrowser({ baseUrl, whistle })` from
46
+ `@fungi.computer/knapsack/ui/browser`.
47
+
39
48
  Knapsack does not own model loops, tool execution policy, Computer storage, or
40
49
  provider-specific operations.
41
50
 
@@ -43,6 +52,13 @@ provider-specific operations.
43
52
  contracts without loading tool connections or the agent runtime. Browser and
44
53
  transport consumers use this subpath.
45
54
 
55
+ `@fungi.computer/knapsack/files-search` supplies `SearchVisibility` to Local and
56
+ hosted Files adapters. One invocation-local policy checks canonical
57
+ root-relative search paths (the root and every entry), excludes `.git` path
58
+ components and respects excluded ancestors. Adapters supply bounded ignore-file
59
+ text; maintained `ignore` owns gitignore grammar and nested negation. Exact-path
60
+ reads do not use search visibility.
61
+
46
62
  ## Set up
47
63
 
48
64
  ```sh
@@ -51,8 +67,9 @@ npm install @fungi.computer/knapsack
51
67
 
52
68
  The package is an ESM public candidate, not a claim of npm publication. It
53
69
  depends on Cairn, Forage, Mule, Mycelium, the MCP SDK client, Effect, Zod and
54
- `diff`. Its explicit file list contains compiled JavaScript and declarations,
55
- the API reference, this README and the license texts. It has no install scripts.
70
+ `diff` and `ignore`. Its explicit file list contains compiled JavaScript and
71
+ declarations, the API reference, this README and the license texts. It has no
72
+ install scripts.
56
73
 
57
74
  ## Quick start: validate a read request
58
75
 
@@ -89,6 +106,12 @@ replace atomically or refuse a conflict. A native patch operation is available
89
106
  only when that port supplies a patch engine. Paths are validated, not confined
90
107
  by Knapsack: the captured host port enforces its own root and authority.
91
108
 
109
+ `Knapsack.ui` keeps one browser host per Session, opened by the browser's event
110
+ stream for a stored Session or by a run, and dropped when neither holds it.
111
+ Neither its `fetch` nor the browser's requests are authenticated by Knapsack:
112
+ mount `fetch` behind the host's own admission. `close()` ends every stream;
113
+ later requests answer 410 and later runs fail to borrow.
114
+
92
115
  ## API reference
93
116
 
94
117
  The [generated reference](docs/api/README.md) covers every public entry:
@@ -100,6 +123,7 @@ The [generated reference](docs/api/README.md) covers every public entry:
100
123
  | `/files-read` | Bounded text read schemas and shared path validation. |
101
124
  | `/patch` | Read Codex patch operations for presentation; the native engine validates and applies them. |
102
125
  | `/files-read-codec` | Encode read requests and adapt a framed host transport to the Files read port. |
126
+ | `/ui/browser` | Connect an App's Whistle runtime to one Agent Session's `Knapsack.ui` transport. |
103
127
 
104
128
  ## Examples and checks
105
129
 
@@ -107,8 +131,12 @@ The [generated reference](docs/api/README.md) covers every public entry:
107
131
  provider failures and invocation context. [Files tests](test/files.test.ts)
108
132
  exercise bounded reads and edits.
109
133
  [Read codec tests](test/files-read-codec.test.ts) exercise the framed transport.
110
- The packed-consumer runtime and typecheck fixtures import only the five public
111
- entries from an isolated tarball installation.
134
+ [Knapsack.ui tests](test/ui.test.ts) exercise run borrows and teardown; the
135
+ [host](test/ui-host.test.ts), [event stream](test/ui-eventsource.test.ts) and
136
+ [Session host](test/ui-sessions.test.ts) tests cover discovery, execution,
137
+ claims, disconnects and multi-tab delivery. The packed-consumer runtime and
138
+ typecheck fixtures import the root and Files entries from an isolated tarball
139
+ installation.
112
140
 
113
141
  From a configured repository checkout:
114
142
 
@@ -131,6 +159,11 @@ search results contain at most 256 entries and expose truncation. Schemas reject
131
159
  invalid requests and results. `FilesEditError` distinguishes conflicts, missing
132
160
  or ambiguous matches, oversized files and partial-character edits.
133
161
 
162
+ One Session's browser serves one outstanding request: a second gets
163
+ `browser_busy`, a missing browser `browser_not_connected`. If the browser
164
+ disconnects after it claimed an execution, the result is
165
+ `browser_execution_indeterminate`, because the command may already have run.
166
+
134
167
  ## Contribute and license
135
168
 
136
169
  Knapsack's package license is [MIT](LICENSE). The retained Codex patch grammar
@@ -13,7 +13,14 @@ export declare const patchOutputSchema: z.ZodLazy<z.ZodUnion<readonly [z.ZodObje
13
13
  exitCode: z.ZodNumber;
14
14
  stdout: z.ZodString;
15
15
  stderr: z.ZodString;
16
- truncated: z.ZodBoolean;
16
+ truncated: z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodArray<z.ZodObject<{
17
+ stream: z.ZodEnum<{
18
+ stdout: "stdout";
19
+ stderr: "stderr";
20
+ }>;
21
+ keptBytes: z.ZodNumber;
22
+ totalBytes: z.ZodNumber;
23
+ }, z.core.$strict>>]>;
17
24
  }, z.core.$strict>, z.ZodObject<{
18
25
  status: z.ZodEnum<{
19
26
  completed: "completed";
@@ -23,7 +30,14 @@ export declare const patchOutputSchema: z.ZodLazy<z.ZodUnion<readonly [z.ZodObje
23
30
  exitCode: z.ZodNumber;
24
31
  stdout: z.ZodString;
25
32
  stderr: z.ZodString;
26
- truncated: z.ZodBoolean;
33
+ truncated: z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodArray<z.ZodObject<{
34
+ stream: z.ZodEnum<{
35
+ stdout: "stdout";
36
+ stderr: "stderr";
37
+ }>;
38
+ keptBytes: z.ZodNumber;
39
+ totalBytes: z.ZodNumber;
40
+ }, z.core.$strict>>]>;
27
41
  error: z.ZodObject<{
28
42
  kind: z.ZodLiteral<"mismatch">;
29
43
  }, z.core.$strict>;
@@ -6,12 +6,19 @@ export const patchInputSchema = z.lazy(() => z.strictObject({
6
6
  description: `Codex patch text, at most ${String(maximumReadBytes)} UTF-8 bytes.`,
7
7
  }),
8
8
  }));
9
+ /** One patch output stream cut at its byte bound; absent streams were whole. */
10
+ const patchTruncationSchema = z.strictObject({
11
+ stream: z.enum(["stdout", "stderr"]),
12
+ keptBytes: z.number().int().nonnegative(),
13
+ totalBytes: z.number().int().positive(),
14
+ });
9
15
  const patchResultSchema = z.strictObject({
10
16
  status: z.enum(["completed", "failed", "cancelled"]),
11
17
  exitCode: z.number().int(),
12
18
  stdout: z.string(),
13
19
  stderr: z.string(),
14
- truncated: z.boolean(),
20
+ /** `false` when both streams are whole, else one entry per cut stream. */
21
+ truncated: z.union([z.literal(false), z.array(patchTruncationSchema).min(1)]),
15
22
  });
16
23
  /** Native patch completion, cancellation or failure, including an optional mismatch. */
17
24
  export const patchOutputSchema = z.lazy(() => z.union([
@@ -1,6 +1,14 @@
1
1
  import * as z from "zod";
2
2
  /** Maximum UTF-8 byte length of a Files text page. */
3
3
  export declare const maximumReadBytes: number;
4
+ /** Maximum byte length of an image the read tool returns; a host refuses an image it cannot bring within it. */
5
+ export declare const maximumImageBytes: number;
6
+ /** Bytes needed at the start of a file to recognise a supported image. */
7
+ export declare const imageSniffBytes = 12;
8
+ /** Identify png, jpeg, gif or webp by their full magic bytes; `undefined` means not a supported image. */
9
+ export declare function sniffImageMimeType(head: Uint8Array): string | undefined;
10
+ /** Message for an image above a host's read cap, shared by every Files host. */
11
+ export declare function imageTooLargeMessage(path: string, size: number, limit: number): string;
4
12
  /** Nonempty file path bounded to 4,096 characters, without null bytes. */
5
13
  export declare const pathSchema: z.ZodLazy<z.ZodString>;
6
14
  /** Validate text against a minimum character length and the Files UTF-8 byte limit. */
@@ -11,15 +19,112 @@ export declare const readInputSchema: z.ZodLazy<z.ZodObject<{
11
19
  offset: z.ZodDefault<z.ZodNumber>;
12
20
  limit: z.ZodDefault<z.ZodNumber>;
13
21
  }, z.core.$strict>>;
22
+ /** The image types a Files read can return. */
23
+ export declare const readImageMimeType: z.ZodEnum<{
24
+ "image/png": "image/png";
25
+ "image/jpeg": "image/jpeg";
26
+ "image/gif": "image/gif";
27
+ "image/webp": "image/webp";
28
+ }>;
29
+ /** Whole png, jpeg, gif or webp file, at most `maximumImageBytes`, as base64. */
30
+ export declare const readImageOutputSchema: z.ZodLazy<z.ZodObject<{
31
+ path: z.ZodLazy<z.ZodString>;
32
+ image: z.ZodObject<{
33
+ mimeType: z.ZodEnum<{
34
+ "image/png": "image/png";
35
+ "image/jpeg": "image/jpeg";
36
+ "image/gif": "image/gif";
37
+ "image/webp": "image/webp";
38
+ }>;
39
+ data: z.ZodBase64;
40
+ bytes: z.ZodNumber;
41
+ }, z.core.$strict>;
42
+ file: z.ZodExactOptional<z.ZodLazy<z.ZodObject<{
43
+ computerId: z.ZodString;
44
+ path: z.ZodLazy<z.ZodString>;
45
+ }, z.core.$strict>>>;
46
+ }, z.core.$strict>>;
14
47
  /** Bounded text page, continuation offset and optional hosted file identity. */
15
- export declare const readOutputSchema: z.ZodLazy<z.ZodObject<{
48
+ export declare const readTextOutputSchema: z.ZodLazy<z.ZodObject<{
16
49
  path: z.ZodLazy<z.ZodString>;
17
50
  content: z.ZodString;
18
51
  offset: z.ZodNumber;
19
52
  nextOffset: z.ZodNullable<z.ZodNumber>;
20
53
  truncated: z.ZodBoolean;
21
- file: z.ZodExactOptional<z.ZodObject<{
54
+ file: z.ZodExactOptional<z.ZodLazy<z.ZodObject<{
22
55
  computerId: z.ZodString;
23
56
  path: z.ZodLazy<z.ZodString>;
24
- }, z.core.$strict>>;
57
+ }, z.core.$strict>>>;
58
+ }, z.core.$strict>>;
59
+ /**
60
+ * What guest code and MCP callers see: an image never travels as base64 in the
61
+ * value. It is pushed through the execution's image collector and the value is
62
+ * only this caption.
63
+ */
64
+ export declare const readImageCaptionSchema: z.ZodLazy<z.ZodObject<{
65
+ path: z.ZodLazy<z.ZodString>;
66
+ image: z.ZodObject<{
67
+ ref: z.ZodString;
68
+ mimeType: z.ZodEnum<{
69
+ "image/png": "image/png";
70
+ "image/jpeg": "image/jpeg";
71
+ "image/gif": "image/gif";
72
+ "image/webp": "image/webp";
73
+ }>;
74
+ bytes: z.ZodNumber;
75
+ }, z.core.$strict>;
25
76
  }, z.core.$strict>>;
77
+ /** Text page or image caption returned to guest code. */
78
+ export declare const readGuestOutputSchema: z.ZodLazy<z.ZodUnion<readonly [z.ZodLazy<z.ZodObject<{
79
+ path: z.ZodLazy<z.ZodString>;
80
+ content: z.ZodString;
81
+ offset: z.ZodNumber;
82
+ nextOffset: z.ZodNullable<z.ZodNumber>;
83
+ truncated: z.ZodBoolean;
84
+ file: z.ZodExactOptional<z.ZodLazy<z.ZodObject<{
85
+ computerId: z.ZodString;
86
+ path: z.ZodLazy<z.ZodString>;
87
+ }, z.core.$strict>>>;
88
+ }, z.core.$strict>>, z.ZodLazy<z.ZodObject<{
89
+ path: z.ZodLazy<z.ZodString>;
90
+ image: z.ZodObject<{
91
+ ref: z.ZodString;
92
+ mimeType: z.ZodEnum<{
93
+ "image/png": "image/png";
94
+ "image/jpeg": "image/jpeg";
95
+ "image/gif": "image/gif";
96
+ "image/webp": "image/webp";
97
+ }>;
98
+ bytes: z.ZodNumber;
99
+ }, z.core.$strict>;
100
+ }, z.core.$strict>>]>>;
101
+ /** A bounded text page or a whole image; images are only read from offset 0. */
102
+ export declare const readOutputSchema: z.ZodLazy<z.ZodUnion<readonly [z.ZodLazy<z.ZodObject<{
103
+ path: z.ZodLazy<z.ZodString>;
104
+ content: z.ZodString;
105
+ offset: z.ZodNumber;
106
+ nextOffset: z.ZodNullable<z.ZodNumber>;
107
+ truncated: z.ZodBoolean;
108
+ file: z.ZodExactOptional<z.ZodLazy<z.ZodObject<{
109
+ computerId: z.ZodString;
110
+ path: z.ZodLazy<z.ZodString>;
111
+ }, z.core.$strict>>>;
112
+ }, z.core.$strict>>, z.ZodLazy<z.ZodObject<{
113
+ path: z.ZodLazy<z.ZodString>;
114
+ image: z.ZodObject<{
115
+ mimeType: z.ZodEnum<{
116
+ "image/png": "image/png";
117
+ "image/jpeg": "image/jpeg";
118
+ "image/gif": "image/gif";
119
+ "image/webp": "image/webp";
120
+ }>;
121
+ data: z.ZodBase64;
122
+ bytes: z.ZodNumber;
123
+ }, z.core.$strict>;
124
+ file: z.ZodExactOptional<z.ZodLazy<z.ZodObject<{
125
+ computerId: z.ZodString;
126
+ path: z.ZodLazy<z.ZodString>;
127
+ }, z.core.$strict>>>;
128
+ }, z.core.$strict>>]>>;
129
+ /** Build the image read result for a recognised image's complete bytes. */
130
+ export declare function imageReadResult(path: string, mimeType: string, bytes: Uint8Array): z.output<typeof readImageOutputSchema>;
@@ -1,7 +1,37 @@
1
+ import * as Base64 from "effect/encoding/Base64";
1
2
  import * as z from "zod";
2
3
  /** Maximum UTF-8 byte length of a Files text page. */
3
4
  export const maximumReadBytes = 128 * 1_024;
5
+ /** Maximum byte length of an image the read tool returns; a host refuses an image it cannot bring within it. */
6
+ export const maximumImageBytes = 512 * 1_024;
4
7
  const utf8 = new TextEncoder();
8
+ const signatures = [
9
+ {
10
+ mimeType: "image/png",
11
+ bytes: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],
12
+ },
13
+ { mimeType: "image/jpeg", bytes: [0xff, 0xd8, 0xff] },
14
+ { mimeType: "image/gif", bytes: [0x47, 0x49, 0x46, 0x38, 0x37, 0x61] },
15
+ { mimeType: "image/gif", bytes: [0x47, 0x49, 0x46, 0x38, 0x39, 0x61] },
16
+ ];
17
+ const riff = [0x52, 0x49, 0x46, 0x46];
18
+ const webp = [0x57, 0x45, 0x42, 0x50];
19
+ /** Bytes needed at the start of a file to recognise a supported image. */
20
+ export const imageSniffBytes = 12;
21
+ const hasBytesAt = (head, offset, bytes) => bytes.every((byte, index) => head[offset + index] === byte);
22
+ /** Identify png, jpeg, gif or webp by their full magic bytes; `undefined` means not a supported image. */
23
+ export function sniffImageMimeType(head) {
24
+ for (const { mimeType, bytes } of signatures)
25
+ if (hasBytesAt(head, 0, bytes))
26
+ return mimeType;
27
+ return hasBytesAt(head, 0, riff) && hasBytesAt(head, 8, webp)
28
+ ? "image/webp"
29
+ : undefined;
30
+ }
31
+ /** Message for an image above a host's read cap, shared by every Files host. */
32
+ export function imageTooLargeMessage(path, size, limit) {
33
+ return `Image ${path} is ${String(size)} bytes; the read limit is ${String(limit)} bytes. Refusing to read it.`;
34
+ }
5
35
  /** Nonempty file path bounded to 4,096 characters, without null bytes. */
6
36
  export const pathSchema = z.lazy(() => z
7
37
  .string()
@@ -26,18 +56,58 @@ export const readInputSchema = z.lazy(() => z.strictObject({
26
56
  .max(maximumReadBytes)
27
57
  .default(maximumReadBytes),
28
58
  }));
59
+ const fileIdentitySchema = z.lazy(() => z.strictObject({
60
+ computerId: z.string().min(1).max(256),
61
+ path: pathSchema.refine((path) => path.startsWith("/"), "Computer file paths must be absolute"),
62
+ }));
63
+ /** The image types a Files read can return. */
64
+ export const readImageMimeType = z.enum([
65
+ "image/png",
66
+ "image/jpeg",
67
+ "image/gif",
68
+ "image/webp",
69
+ ]);
70
+ /** Whole png, jpeg, gif or webp file, at most `maximumImageBytes`, as base64. */
71
+ export const readImageOutputSchema = z.lazy(() => z.strictObject({
72
+ path: pathSchema,
73
+ image: z.strictObject({
74
+ mimeType: readImageMimeType,
75
+ data: z.base64(),
76
+ bytes: z.number().int().min(1).max(maximumImageBytes),
77
+ }),
78
+ file: fileIdentitySchema.exactOptional(),
79
+ }));
29
80
  /** Bounded text page, continuation offset and optional hosted file identity. */
30
- export const readOutputSchema = z.lazy(() => z.strictObject({
81
+ export const readTextOutputSchema = z.lazy(() => z.strictObject({
31
82
  path: pathSchema,
32
83
  content: boundedTextSchema(),
33
84
  offset: z.number().int().min(0),
34
85
  nextOffset: z.number().int().min(0).nullable(),
35
86
  truncated: z.boolean(),
36
87
  /** Captured hosted file identity; a local Files host may have no Computer identity. */
37
- file: z
38
- .strictObject({
39
- computerId: z.string().min(1).max(256),
40
- path: pathSchema.refine((path) => path.startsWith("/"), "Computer file paths must be absolute"),
41
- })
42
- .exactOptional(),
88
+ file: fileIdentitySchema.exactOptional(),
89
+ }));
90
+ /**
91
+ * What guest code and MCP callers see: an image never travels as base64 in the
92
+ * value. It is pushed through the execution's image collector and the value is
93
+ * only this caption.
94
+ */
95
+ export const readImageCaptionSchema = z.lazy(() => z.strictObject({
96
+ path: pathSchema,
97
+ image: z.strictObject({
98
+ ref: z.string().min(1).max(32),
99
+ mimeType: readImageMimeType,
100
+ bytes: z.number().int().min(1).max(maximumImageBytes),
101
+ }),
43
102
  }));
103
+ /** Text page or image caption returned to guest code. */
104
+ export const readGuestOutputSchema = z.lazy(() => z.union([readTextOutputSchema, readImageCaptionSchema]));
105
+ /** A bounded text page or a whole image; images are only read from offset 0. */
106
+ export const readOutputSchema = z.lazy(() => z.union([readTextOutputSchema, readImageOutputSchema]));
107
+ /** Build the image read result for a recognised image's complete bytes. */
108
+ export function imageReadResult(path, mimeType, bytes) {
109
+ return readImageOutputSchema.parse({
110
+ path,
111
+ image: { mimeType, data: Base64.encode(bytes), bytes: bytes.byteLength },
112
+ });
113
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Invocation-local search visibility shared by Local and hosted Files.
3
+ * Adapters supply bounded `.gitignore` text; `ignore` owns its grammar.
4
+ * All paths here are canonical, root-relative paths with `/` separators;
5
+ * the captured root itself is `""`. Exact reads do not use this policy.
6
+ */
7
+ export declare class SearchVisibility {
8
+ private readonly readGitignore;
9
+ private readonly matchers;
10
+ constructor(readGitignore: (directory: string) => Promise<string | undefined>);
11
+ /** Check the search root and every entry with the same ancestor precedence. */
12
+ visible(path: string, isDirectory: boolean): Promise<boolean>;
13
+ private at;
14
+ }
@@ -0,0 +1,52 @@
1
+ import ignore, {} from "ignore";
2
+ /**
3
+ * Invocation-local search visibility shared by Local and hosted Files.
4
+ * Adapters supply bounded `.gitignore` text; `ignore` owns its grammar.
5
+ * All paths here are canonical, root-relative paths with `/` separators;
6
+ * the captured root itself is `""`. Exact reads do not use this policy.
7
+ */
8
+ export class SearchVisibility {
9
+ readGitignore;
10
+ matchers = new Map();
11
+ constructor(readGitignore) {
12
+ this.readGitignore = readGitignore;
13
+ }
14
+ /** Check the search root and every entry with the same ancestor precedence. */
15
+ async visible(path, isDirectory) {
16
+ if (path === "")
17
+ return true;
18
+ const parts = path.split("/");
19
+ if (parts.includes(".git"))
20
+ return false;
21
+ const layers = [];
22
+ for (let index = 0; index < parts.length; index++) {
23
+ const base = parts.slice(0, index).join("/");
24
+ layers.push({ base, matcher: await this.at(base) });
25
+ const candidate = parts.slice(0, index + 1).join("/");
26
+ const directory = index < parts.length - 1 || isDirectory;
27
+ // Never reopen an excluded ancestor with a descendant's negation.
28
+ for (const layer of layers.toReversed()) {
29
+ const local = layer.base === ""
30
+ ? candidate
31
+ : candidate.slice(layer.base.length + 1);
32
+ const verdict = layer.matcher.test(directory ? `${local}/` : local);
33
+ if (verdict.ignored)
34
+ return false;
35
+ if (verdict.unignored)
36
+ break;
37
+ }
38
+ }
39
+ return true;
40
+ }
41
+ async at(directory) {
42
+ const cached = this.matchers.get(directory);
43
+ if (cached !== undefined)
44
+ return cached;
45
+ const content = await this.readGitignore(directory);
46
+ const matcher = ignore();
47
+ if (content !== undefined)
48
+ matcher.add(content);
49
+ this.matchers.set(directory, matcher);
50
+ return matcher;
51
+ }
52
+ }
package/dist/files.d.ts CHANGED
@@ -2,66 +2,53 @@ import { type Access, type InvocationContext, type LabelledTool, type ModuleRegi
2
2
  import { patchInputSchema, patchOutputSchema } from "./files-patch.js";
3
3
  export { patchInputSchema, patchOutputSchema } from "./files-patch.js";
4
4
  import { z } from "zod";
5
- import { readInputSchema, readOutputSchema } from "./files-read.js";
6
- export { readInputSchema, readOutputSchema };
5
+ import { readInputSchema, readGuestOutputSchema, readOutputSchema } from "./files-read.js";
6
+ export { readGuestOutputSchema, readInputSchema, readOutputSchema };
7
7
  /** Typed refusal from a bounded or conditional Files edit. */
8
- export type FilesEditErrorReason = "not-found" | "ambiguous" | "file-too-large" | "partial-character" | "unchanged" | "conflict";
8
+ export type FilesEditErrorReason = "not-found" | "ambiguous" | "file-too-large" | "partial-character" | "unchanged" | "not-text" | "conflict";
9
9
  /** Expected Files edit refusal, kept distinct from unexpected host failures. */
10
10
  export declare class FilesEditError extends Error {
11
11
  readonly reason: FilesEditErrorReason;
12
12
  readonly path: string;
13
13
  constructor(reason: FilesEditErrorReason, path: string, detail: string);
14
14
  }
15
- /** Bounded directory listing request. */
16
- export declare const listInputSchema: z.ZodObject<{
17
- path: z.ZodLazy<z.ZodString>;
15
+ /** List one directory level. */
16
+ export declare const listInputSchema: z.ZodLazy<z.ZodObject<{
17
+ path: z.ZodDefault<z.ZodLazy<z.ZodString>>;
18
18
  limit: z.ZodDefault<z.ZodNumber>;
19
- }, z.core.$strict>;
20
- /** Directory entries and an explicit truncation flag. */
21
- export declare const listOutputSchema: z.ZodLazy<z.ZodObject<{
22
- path: z.ZodLazy<z.ZodString>;
23
- entries: z.ZodArray<z.ZodLazy<z.ZodObject<{
24
- path: z.ZodLazy<z.ZodString>;
25
- name: z.ZodString;
26
- type: z.ZodEnum<{
27
- file: "file";
28
- directory: "directory";
29
- symlink: "symlink";
30
- }>;
31
- size: z.ZodNumber;
32
- modifiedAt: z.ZodNumber;
33
- }, z.core.$strict>>>;
34
- truncated: z.ZodBoolean;
35
19
  }, z.core.$strict>>;
36
- /** Bounded path-pattern search request. */
20
+ /** Match paths below a directory by glob. */
37
21
  export declare const globInputSchema: z.ZodLazy<z.ZodObject<{
38
- path: z.ZodLazy<z.ZodString>;
22
+ path: z.ZodDefault<z.ZodLazy<z.ZodString>>;
39
23
  pattern: z.ZodString;
40
24
  limit: z.ZodDefault<z.ZodNumber>;
41
25
  }, z.core.$strict>>;
42
- /** Matching paths and an explicit truncation flag. */
43
- export declare const globOutputSchema: z.ZodLazy<z.ZodObject<{
44
- path: z.ZodLazy<z.ZodString>;
45
- matches: z.ZodArray<z.ZodLazy<z.ZodString>>;
46
- truncated: z.ZodBoolean;
47
- }, z.core.$strict>>;
48
- /** Bounded content-search request with optional regular-expression matching. */
26
+ /** Search file content, optionally restricted to matching paths. */
49
27
  export declare const grepInputSchema: z.ZodLazy<z.ZodObject<{
50
- path: z.ZodLazy<z.ZodString>;
28
+ path: z.ZodDefault<z.ZodLazy<z.ZodString>>;
51
29
  pattern: z.ZodString;
30
+ include: z.ZodOptional<z.ZodString>;
52
31
  regex: z.ZodDefault<z.ZodBoolean>;
53
32
  ignoreCase: z.ZodDefault<z.ZodBoolean>;
54
33
  limit: z.ZodDefault<z.ZodNumber>;
55
34
  }, z.core.$strict>>;
56
- /** Content matches and an explicit truncation flag. */
57
- export declare const grepOutputSchema: z.ZodLazy<z.ZodObject<{
35
+ /** Found paths or matching lines, and which cap (if any) cut the search short. */
36
+ export declare const searchOutputSchema: z.ZodLazy<z.ZodObject<{
58
37
  path: z.ZodLazy<z.ZodString>;
59
- matches: z.ZodArray<z.ZodObject<{
38
+ results: z.ZodArray<z.ZodLazy<z.ZodObject<{
60
39
  path: z.ZodLazy<z.ZodString>;
61
- line: z.ZodNumber;
62
- text: z.ZodString;
63
- }, z.core.$strict>>;
64
- truncated: z.ZodBoolean;
40
+ type: z.ZodExactOptional<z.ZodEnum<{
41
+ file: "file";
42
+ directory: "directory";
43
+ symlink: "symlink";
44
+ }>>;
45
+ line: z.ZodExactOptional<z.ZodNumber>;
46
+ text: z.ZodExactOptional<z.ZodString>;
47
+ }, z.core.$strict>>>;
48
+ truncated: z.ZodNullable<z.ZodEnum<{
49
+ limit: "limit";
50
+ scan: "scan";
51
+ }>>;
65
52
  }, z.core.$strict>>;
66
53
  /** Text replacement request decoded before the captured Files port is called. */
67
54
  export declare const editInputSchema: z.ZodLazy<z.ZodObject<{
@@ -79,6 +66,7 @@ export declare const filesEditFailureSchema: z.ZodLazy<z.ZodObject<{
79
66
  "file-too-large": "file-too-large";
80
67
  "partial-character": "partial-character";
81
68
  unchanged: "unchanged";
69
+ "not-text": "not-text";
82
70
  conflict: "conflict";
83
71
  }>;
84
72
  path: z.ZodLazy<z.ZodString>;
@@ -105,6 +93,7 @@ export declare const editOutputSchema: z.ZodUnion<readonly [z.ZodLazy<z.ZodObjec
105
93
  "file-too-large": "file-too-large";
106
94
  "partial-character": "partial-character";
107
95
  unchanged: "unchanged";
96
+ "not-text": "not-text";
108
97
  conflict: "conflict";
109
98
  }>;
110
99
  path: z.ZodLazy<z.ZodString>;
@@ -119,12 +108,12 @@ export type FilesPort = Readonly<{
119
108
  patch?: FilesPatch;
120
109
  /** Read a bounded text page. */
121
110
  read: (input: z.output<typeof readInputSchema>, context: InvocationContext) => Promise<z.output<typeof readOutputSchema>>;
122
- /** List bounded directory entries. */
123
- list: (input: z.output<typeof listInputSchema>, context: InvocationContext) => Promise<z.output<typeof listOutputSchema>>;
124
- /** Match bounded paths under the admitted root. */
125
- glob: (input: z.output<typeof globInputSchema>, context: InvocationContext) => Promise<z.output<typeof globOutputSchema>>;
126
- /** Search bounded file content under the admitted root. */
127
- grep: (input: z.output<typeof grepInputSchema>, context: InvocationContext) => Promise<z.output<typeof grepOutputSchema>>;
111
+ /** List one directory level under the admitted root. */
112
+ list: (input: z.output<typeof listInputSchema>, context: InvocationContext) => Promise<z.output<typeof searchOutputSchema>>;
113
+ /** Match paths below the admitted root by glob. */
114
+ glob: (input: z.output<typeof globInputSchema>, context: InvocationContext) => Promise<z.output<typeof searchOutputSchema>>;
115
+ /** Search file content under the admitted root; the host owns the walk. */
116
+ grep: (input: z.output<typeof grepInputSchema>, context: InvocationContext) => Promise<z.output<typeof searchOutputSchema>>;
128
117
  /** Replace atomically only if the stored content still matches the baseline. */
129
118
  replace: (input: Readonly<{
130
119
  path: string;
@@ -135,7 +124,7 @@ export type FilesPort = Readonly<{
135
124
  bytes: number;
136
125
  }>>;
137
126
  }>;
138
- /** Closed set of direct, model-visible Files tools; widening one is an ADR. */
127
+ /** Closed set of direct, model-visible Files tools; changing it is an ADR. */
139
128
  export type FileToolName = "read" | "list" | "glob" | "grep" | "apply_patch";
140
129
  declare const fileToolBrand: unique symbol;
141
130
  /**
@@ -166,5 +155,10 @@ export type FilesConnectionOptions = Readonly<{
166
155
  }>;
167
156
  /** Apply the same edit behavior as the Files edit operation directly through a captured FilesPort. */
168
157
  export declare function edit(port: FilesPort, input: z.output<typeof editInputSchema>, context: InvocationContext): Promise<z.output<typeof editOutputSchema>>;
158
+ /**
159
+ * Guest code and MCP callers never receive base64: the image goes through the
160
+ * execution's image collector (the `images.view` path) and the value is a caption.
161
+ */
162
+ export declare function presentReadForGuest(result: z.output<typeof readOutputSchema>, context: InvocationContext): z.output<typeof readGuestOutputSchema>;
169
163
  /** Build both patch presentations around one injected Files operation. */
170
164
  export declare function files(input: FilesConnectionOptions): FilesSurface;