stitchkit 0.83.1 → 0.84.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/CHANGELOG.md +63 -0
- package/dist/agent-runtime/coding-tool-contract.d.ts +11 -0
- package/dist/agent-runtime/coding-tool-contract.d.ts.map +1 -1
- package/dist/agent-runtime/coding-tool-files.d.ts.map +1 -1
- package/dist/agent-runtime/coding-tool-listing.d.ts.map +1 -1
- package/dist/agent-runtime/coding-tool-paths.d.ts +30 -0
- package/dist/agent-runtime/coding-tool-paths.d.ts.map +1 -1
- package/dist/agent-runtime/coding-tool-search-patch.d.ts.map +1 -1
- package/dist/agent-runtime/coding-tool-shell.d.ts.map +1 -1
- package/dist/agent-runtime/contained-files.d.ts +2 -1
- package/dist/agent-runtime/contained-files.d.ts.map +1 -1
- package/dist/agent-runtime-coding-tools.d.ts +1 -1
- package/dist/agent-runtime-coding-tools.d.ts.map +1 -1
- package/dist/agent-runtime-coding-tools.js +49 -9
- package/dist/agent-runtime-harness.js +1 -1
- package/dist/{index-rqpdar1e.js → index-t7gh5w3v.js} +12 -3
- package/llms-full.txt +80 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,69 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
|
|
|
15
15
|
|
|
16
16
|
## [Unreleased]
|
|
17
17
|
|
|
18
|
+
## [0.84.0] — 2026-09-07
|
|
19
|
+
|
|
20
|
+
### ⚠️ Breaking changes
|
|
21
|
+
|
|
22
|
+
**Who must act:** anyone using `createAgentCodingTools`. The separator fix
|
|
23
|
+
below reaches BOTH callbacks — the required `authorize({ operation, path })`
|
|
24
|
+
and the optional `authorizePath`. The recursion fix reaches `authorizePath`
|
|
25
|
+
only. Anyone passing a non-canonical `cwd` to `run_command` is affected by the
|
|
26
|
+
separator fix even if they implement neither policy shape.
|
|
27
|
+
|
|
28
|
+
- **A backslash in a requested path is refused instead of silently becoming a
|
|
29
|
+
separator.** `contained-files` splits a relative path on `[\/]`, so
|
|
30
|
+
`credentials\token.txt` reached `openat` as two segments while every
|
|
31
|
+
authorization callback was handed the string whole and read it as one name.
|
|
32
|
+
A rule denying `credentials/token.txt` refused that spelling and served — then
|
|
33
|
+
overwrote — the same file spelled with a backslash. This bypassed the
|
|
34
|
+
**required** `authorize` callback, not only the `authorizePath` added in
|
|
35
|
+
0.83.2, and it has been reachable since 0.70.0. Requests carrying `\` now get
|
|
36
|
+
a typed `FORBIDDEN`; `run_command`'s `cwd` goes through the same segment and
|
|
37
|
+
separator validation as every file tool.
|
|
38
|
+
`// before: read_file('a\b.txt') → served past a rule denying 'a/b.txt'` →
|
|
39
|
+
`// after: read_file('a\b.txt') → FORBIDDEN, use 'a/b.txt'`
|
|
40
|
+
|
|
41
|
+
- **`authorizePath` denies a directory on every surface it governs.** Direct
|
|
42
|
+
read, write and edit asked the path policy only about the leaf, so `authorizePath: p => p !== 'credentials'`
|
|
43
|
+
hid the directory from `glob` and served `credentials/token.txt` to
|
|
44
|
+
`read_file`, created files under it through `write_file` and rewrote it
|
|
45
|
+
through `edit_file`; `list_directory` and `glob` given a base path *inside* a
|
|
46
|
+
denied directory disclosed its contents and disagreed about the refusal shape.
|
|
47
|
+
Direct access and discovery base paths now ask about `.` and each ancestor,
|
|
48
|
+
outermost first, short-circuiting on the first refusal — segment-wise, so
|
|
49
|
+
denying `credentials` does not deny `credentials-backup`.
|
|
50
|
+
`// before: authorizePath asked once, about the leaf` →
|
|
51
|
+
`// after: asked about '.', then each ancestor, then the leaf`
|
|
52
|
+
|
|
53
|
+
**The policy must now admit every directory on the way to a file**, the way
|
|
54
|
+
POSIX needs `+x` on each directory in a path. A DENY-list is unaffected:
|
|
55
|
+
refusing `credentials` now refuses everything under it, which is what the rule
|
|
56
|
+
always read as. An ALLOW-list must be widened to name the directories it
|
|
57
|
+
leads through — `p => p.startsWith('src/')` refuses `src` itself and so loses
|
|
58
|
+
`src/index.ts`; write `p => p === 'src' || p.startsWith('src/')`. The
|
|
59
|
+
workspace root is not asked about: it is the boundary the tools already own.
|
|
60
|
+
A path of depth N costs N questions, and a policy keeping an audit no longer
|
|
61
|
+
sees the leaf once an ancestor refused.
|
|
62
|
+
|
|
63
|
+
This recursion is `authorizePath` only. The required `authorize({ operation,
|
|
64
|
+
path })` is still asked once, about the operation's own path, and a broad
|
|
65
|
+
`search` or `glob` is still one decision covering every file the walk opens —
|
|
66
|
+
which is the reason `authorizePath` exists at all. → ADR 0172 (amended).
|
|
67
|
+
|
|
68
|
+
## [0.83.2] — 2026-09-07
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
|
|
72
|
+
- **Agent coding tools can enforce one path policy before disclosure or content reads.**
|
|
73
|
+
`createAgentCodingTools({ authorizePath })` accepts an async or synchronous callback shared by
|
|
74
|
+
direct read/write/edit, `list_directory`, `glob` and both `search_files` modes. Direct access to a
|
|
75
|
+
denied path returns `FORBIDDEN`; discovery omits it. The contained walker applies the decision
|
|
76
|
+
before descending into a directory or opening a file, and `search_files.include` now filters
|
|
77
|
+
before reading instead of after the whole scan. This lets a host protect `.env` or credential
|
|
78
|
+
paths without copying the framework scanner. Shell remains explicitly outside the guarantee: an
|
|
79
|
+
executable requires process isolation, not path filtering. → ADR 0172.
|
|
80
|
+
|
|
18
81
|
## [0.83.1] — 2026-09-07
|
|
19
82
|
|
|
20
83
|
### Fixed
|
|
@@ -58,9 +58,20 @@ export declare const AgentCodingToolAuthorizationSchema: z.ZodDiscriminatedUnion
|
|
|
58
58
|
}, z.core.$strict>], "operation">;
|
|
59
59
|
export type AgentCodingToolLimits = z.infer<typeof AgentCodingToolLimitsSchema>;
|
|
60
60
|
export type AgentCodingToolAuthorization = z.infer<typeof AgentCodingToolAuthorizationSchema>;
|
|
61
|
+
export declare const AgentCodingToolPathAuthorizationSchema: z.ZodObject<{
|
|
62
|
+
path: z.ZodString;
|
|
63
|
+
}, z.core.$strict>;
|
|
64
|
+
export type AgentCodingToolPathAuthorization = z.infer<typeof AgentCodingToolPathAuthorizationSchema>;
|
|
61
65
|
export interface AgentCodingToolConfig {
|
|
62
66
|
root: string;
|
|
63
67
|
authorize(input: AgentCodingToolAuthorization): boolean | Promise<boolean>;
|
|
68
|
+
/**
|
|
69
|
+
* One operation-independent admission policy for workspace paths.
|
|
70
|
+
*
|
|
71
|
+
* A denied path is refused by direct file tools and omitted from discovery.
|
|
72
|
+
* The callback runs before a file is opened or its content is read.
|
|
73
|
+
*/
|
|
74
|
+
authorizePath?(input: AgentCodingToolPathAuthorization): boolean | Promise<boolean>;
|
|
64
75
|
executables?: Readonly<Record<string, string>>;
|
|
65
76
|
environment?: Readonly<Record<string, string>>;
|
|
66
77
|
artifacts?: AgentCodingArtifactStore;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coding-tool-contract.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-tool-contract.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"coding-tool-contract.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-tool-contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coding-tool-files.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-tool-files.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAK3B,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"coding-tool-files.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-tool-files.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAK3B,MAAM,wBAAwB,CAAC;AA+BhC,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,qBAAqB,EAC7B,MAAM,EAAE,qBAAqB,GAC5B,SAAS,yBAAyB,EAAE,CAwJtC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coding-tool-listing.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-tool-listing.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACtB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"coding-tool-listing.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-tool-listing.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACtB,MAAM,wBAAwB,CAAC;AAchC,iFAAiF;AACjF,eAAO,MAAM,4BAA4B,UAQxC,CAAC;AAoCF;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA2B5D;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,qBAAqB,EAC7B,MAAM,EAAE,qBAAqB,GAC5B,SAAS,yBAAyB,EAAE,CAuGtC"}
|
|
@@ -13,6 +13,36 @@ export declare function editableCodingPath(root: string, requested: string, maxP
|
|
|
13
13
|
relative: string;
|
|
14
14
|
}>;
|
|
15
15
|
export declare function authorizeCodingTool(config: AgentCodingToolConfig, request: AgentCodingToolAuthorization): Promise<void>;
|
|
16
|
+
export declare function isCodingPathAuthorized(config: AgentCodingToolConfig, path: string): Promise<boolean>;
|
|
17
|
+
export declare function authorizeCodingPath(config: AgentCodingToolConfig, path: string): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Ask the policy about the target AND every directory above it.
|
|
20
|
+
*
|
|
21
|
+
* A denied directory means two different things until this exists. The walk
|
|
22
|
+
* refuses to descend into one, so denial is recursive during discovery; direct
|
|
23
|
+
* access asked only about the leaf, so a host writing the obvious rule —
|
|
24
|
+
* `path !== 'credentials'` — got a listing that hid the directory and a
|
|
25
|
+
* `read_file` that served the secret inside it, plus a `write_file` that
|
|
26
|
+
* created directories under it and an `edit_file` that rewrote it.
|
|
27
|
+
*
|
|
28
|
+
* Outermost first, so the broadest refusal is the one the caller is told about
|
|
29
|
+
* and nothing below a denied directory is ever asked about. Segment-wise, not
|
|
30
|
+
* `startsWith`: a rule denying `credentials` must not also deny
|
|
31
|
+
* `credentials-backup`.
|
|
32
|
+
*
|
|
33
|
+
* The chain asks about the ANCESTORS OF THE PATH, and `.` is one only when the
|
|
34
|
+
* path itself is `.`. Asking about `.` unconditionally looks symmetrical with
|
|
35
|
+
* the walk — `search_files` and `list_directory('.')` do ask it — but there `.`
|
|
36
|
+
* is the base path the caller named, not an ancestor of it. Asked on every
|
|
37
|
+
* direct access it silently inverts every ALLOW-list: a host exposing one
|
|
38
|
+
* subtree with `p => p.startsWith('src/')` answers `false` for `.` and loses
|
|
39
|
+
* `src/index.ts` too, having denied nothing.
|
|
40
|
+
*
|
|
41
|
+
* The cost is stated rather than hidden: a path of depth N costs N questions,
|
|
42
|
+
* and a policy that keeps an audit no longer sees the leaf once an ancestor has
|
|
43
|
+
* refused.
|
|
44
|
+
*/
|
|
45
|
+
export declare function authorizeCodingPathChain(config: AgentCodingToolConfig, relative: string): Promise<void>;
|
|
16
46
|
/** Serialize framework-owned compare-and-replace transactions for one canonical target. */
|
|
17
47
|
export declare function withCodingPathLock<T>(target: string, work: () => Promise<T>): Promise<T>;
|
|
18
48
|
export declare function textOccurrences(source: string, search: string): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coding-tool-paths.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-tool-paths.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,4BAA4B,EAEjC,KAAK,qBAAqB,
|
|
1
|
+
{"version":3,"file":"coding-tool-paths.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-tool-paths.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,4BAA4B,EAEjC,KAAK,qBAAqB,EAE3B,MAAM,wBAAwB,CAAC;AAkChC,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAmCzF;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM;;;GAKrB;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM;;;GAarB;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM;;;GAQrB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,qBAAqB,EAC7B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,OAAO,CAAC,CAIlB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,qBAAqB,EAC7B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,qBAAqB,EAC7B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAMf;AAID,2FAA2F;AAC3F,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACrB,OAAO,CAAC,CAAC,CAAC,CAeZ;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAUtE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coding-tool-search-patch.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-tool-search-patch.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACtB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"coding-tool-search-patch.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-tool-search-patch.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACtB,MAAM,wBAAwB,CAAC;AA0HhC,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,qBAAqB,EAC7B,MAAM,EAAE,qBAAqB,GAC5B,SAAS,yBAAyB,EAAE,CAuMtC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coding-tool-shell.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-tool-shell.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAG3B,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"coding-tool-shell.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-tool-shell.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAG3B,MAAM,wBAAwB,CAAC;AAoKhC,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,qBAAqB,EAC7B,MAAM,EAAE,qBAAqB,GAC5B,yBAAyB,CA4D3B"}
|
|
@@ -31,7 +31,7 @@ interface ContainedEntryMetadata {
|
|
|
31
31
|
isSymbolicLink(): boolean;
|
|
32
32
|
}
|
|
33
33
|
/** One directory's direct children, read through pinned descriptors. */
|
|
34
|
-
export declare function listContainedDirectory(root: string, relative: string, maxEntries: number): Promise<{
|
|
34
|
+
export declare function listContainedDirectory(root: string, relative: string, maxEntries: number, authorizePath?: (relative: string) => boolean | Promise<boolean>): Promise<{
|
|
35
35
|
entries: {
|
|
36
36
|
name: string;
|
|
37
37
|
kind: 'file' | 'directory' | 'symlink' | 'other';
|
|
@@ -93,6 +93,7 @@ export declare function scanContainedFiles(input: {
|
|
|
93
93
|
readMaxBytes?: number;
|
|
94
94
|
skipUnreadable?: boolean;
|
|
95
95
|
excludeDirectory?: (relative: string) => boolean;
|
|
96
|
+
authorizePath?: (relative: string, kind: 'file' | 'directory' | 'symlink' | 'other') => boolean | Promise<boolean>;
|
|
96
97
|
}): Promise<ContainedFileScan>;
|
|
97
98
|
export {};
|
|
98
99
|
//# sourceMappingURL=contained-files.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contained-files.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/contained-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,KAAK,EAEX,MAAM,SAAS,CAAC;AAejB,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACzD;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,CACF,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClC,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC;CACxB;AAwKD,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,IAAI,OAAO,CAAC;IACvB,MAAM,IAAI,OAAO,CAAC;IAClB,cAAc,IAAI,OAAO,CAAC;CAC3B;AA8CD,wEAAwE;AACxE,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"contained-files.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/contained-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { type AgentCodingArtifactStore, type AgentCodingToolAuthorization, AgentCodingToolAuthorizationSchema, type AgentCodingToolConfig, type AgentCodingToolDefinition, type AgentCodingToolLimits, AgentCodingToolLimitsSchema, createAgentCodingTools, } from './agent-runtime/coding-tools.js';
|
|
1
|
+
export { type AgentCodingArtifactStore, type AgentCodingToolAuthorization, AgentCodingToolAuthorizationSchema, type AgentCodingToolConfig, type AgentCodingToolDefinition, type AgentCodingToolLimits, AgentCodingToolLimitsSchema, type AgentCodingToolPathAuthorization, AgentCodingToolPathAuthorizationSchema, createAgentCodingTools, } from './agent-runtime/coding-tools.js';
|
|
2
2
|
//# sourceMappingURL=agent-runtime-coding-tools.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-runtime-coding-tools.d.ts","sourceRoot":"","sources":["../src/agent-runtime-coding-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,4BAA4B,EACjC,kCAAkC,EAClC,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC1B,2BAA2B,EAC3B,sBAAsB,GACvB,MAAM,8BAA8B,CAAC"}
|
|
1
|
+
{"version":3,"file":"agent-runtime-coding-tools.d.ts","sourceRoot":"","sources":["../src/agent-runtime-coding-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,4BAA4B,EACjC,kCAAkC,EAClC,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC1B,2BAA2B,EAC3B,KAAK,gCAAgC,EACrC,sCAAsC,EACtC,sBAAsB,GACvB,MAAM,8BAA8B,CAAC"}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
readContainedUtf8Handle,
|
|
11
11
|
scanContainedFiles,
|
|
12
12
|
writeContainedFile
|
|
13
|
-
} from "./index-
|
|
13
|
+
} from "./index-t7gh5w3v.js";
|
|
14
14
|
import {
|
|
15
15
|
defineRuntimeTool
|
|
16
16
|
} from "./index-dx9xehwt.js";
|
|
@@ -89,6 +89,7 @@ var AgentCodingToolAuthorizationSchema = z.discriminatedUnion("operation", [
|
|
|
89
89
|
cwd: z.string().min(1)
|
|
90
90
|
}).strict()
|
|
91
91
|
]);
|
|
92
|
+
var AgentCodingToolPathAuthorizationSchema = z.object({ path: z.string().min(1) }).strict();
|
|
92
93
|
var FileReadInputSchema = z.object({
|
|
93
94
|
path: z.string().min(1),
|
|
94
95
|
offset: z.int().nonnegative().default(0),
|
|
@@ -242,7 +243,13 @@ function inputPath(root, requested, maxPathBytes) {
|
|
|
242
243
|
}
|
|
243
244
|
function boundedCodingRelativePath(requested, maxPathBytes) {
|
|
244
245
|
inputPath(path.parse(process.cwd()).root, requested, maxPathBytes);
|
|
245
|
-
|
|
246
|
+
if (requested.includes("\\")) {
|
|
247
|
+
codingRefusal("FORBIDDEN", "Path separators must be `/`", {
|
|
248
|
+
details: { path: requested },
|
|
249
|
+
hint: "Use `/` between segments; a backslash is refused because the workspace walk reads it as a separator."
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
const segments = requested.split("/");
|
|
246
253
|
if (segments.some((segment) => segment === "" || segment === "." || segment === "..")) {
|
|
247
254
|
codingRefusal("FORBIDDEN", "Path segments must name real entries under the root", {
|
|
248
255
|
details: { path: requested },
|
|
@@ -262,6 +269,28 @@ async function authorizeCodingTool(config, request) {
|
|
|
262
269
|
if (!await config.authorize(parsed))
|
|
263
270
|
forbidden("Coding tool permission denied");
|
|
264
271
|
}
|
|
272
|
+
async function isCodingPathAuthorized(config, path2) {
|
|
273
|
+
if (!config.authorizePath)
|
|
274
|
+
return true;
|
|
275
|
+
const parsed = AgentCodingToolPathAuthorizationSchema.parse({ path: path2 });
|
|
276
|
+
return await config.authorizePath(parsed);
|
|
277
|
+
}
|
|
278
|
+
async function authorizeCodingPath(config, path2) {
|
|
279
|
+
if (!await isCodingPathAuthorized(config, path2)) {
|
|
280
|
+
codingRefusal("FORBIDDEN", "Coding tool path permission denied", {
|
|
281
|
+
details: { path: path2 },
|
|
282
|
+
hint: "The host policy refused this path or a directory above it."
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async function authorizeCodingPathChain(config, relative) {
|
|
287
|
+
if (!config.authorizePath)
|
|
288
|
+
return;
|
|
289
|
+
const segments = relative === "." ? ["."] : relative.split("/");
|
|
290
|
+
for (let depth = 1;depth <= segments.length; depth += 1) {
|
|
291
|
+
await authorizeCodingPath(config, segments.slice(0, depth).join("/"));
|
|
292
|
+
}
|
|
293
|
+
}
|
|
265
294
|
var codingPathLocks = new Map;
|
|
266
295
|
async function withCodingPathLock(target, work) {
|
|
267
296
|
const previous = codingPathLocks.get(target) ?? Promise.resolve();
|
|
@@ -320,6 +349,7 @@ function createFileCodingTools(config, limits) {
|
|
|
320
349
|
handler: async ({ input }) => {
|
|
321
350
|
const root = await realpath2(config.root);
|
|
322
351
|
const relative = boundedCodingRelativePath(input.path, limits.maxPathBytes);
|
|
352
|
+
await authorizeCodingPathChain(config, relative);
|
|
323
353
|
const handle = await openContainedFile(root, relative).catch((error) => refuseMissingCodingPath(error, relative));
|
|
324
354
|
const maximum = Math.min(input.maxBytes ?? limits.maxReadBytes, limits.maxReadBytes);
|
|
325
355
|
let selected;
|
|
@@ -366,6 +396,7 @@ function createFileCodingTools(config, limits) {
|
|
|
366
396
|
handler: async ({ input }) => {
|
|
367
397
|
const root = await realpath2(config.root);
|
|
368
398
|
const relative = boundedCodingRelativePath(input.path, limits.maxPathBytes);
|
|
399
|
+
await authorizeCodingPathChain(config, relative);
|
|
369
400
|
const bytes = Buffer.byteLength(input.content);
|
|
370
401
|
if (bytes > limits.maxWriteBytes) {
|
|
371
402
|
codingPathRefusal("BAD_REQUEST", `The content is ${bytes} bytes, over the ${limits.maxWriteBytes}-byte limit`, relative, {
|
|
@@ -500,8 +531,9 @@ function createListingCodingTools(config, limits) {
|
|
|
500
531
|
handler: async ({ input }) => {
|
|
501
532
|
const root = await realpath3(config.root);
|
|
502
533
|
const relative = input.path === "." ? "." : boundedCodingRelativePath(input.path, limits.maxPathBytes);
|
|
534
|
+
await authorizeCodingPathChain(config, relative);
|
|
503
535
|
await authorizeCodingTool(config, { operation: "list", path: relative });
|
|
504
|
-
const listing = await listContainedDirectory(root, relative, limits.maxListEntries).catch((error) => {
|
|
536
|
+
const listing = await listContainedDirectory(root, relative, limits.maxListEntries, (candidate) => isCodingPathAuthorized(config, candidate)).catch((error) => {
|
|
505
537
|
if (error instanceof Error && error.message.includes("ancestor is not a directory")) {
|
|
506
538
|
codingPathRefusal("BAD_REQUEST", "This path is not a directory", relative, {
|
|
507
539
|
hint: "Pass a directory; use read_file for a file."
|
|
@@ -533,6 +565,7 @@ function createListingCodingTools(config, limits) {
|
|
|
533
565
|
transports: ["AGENT"],
|
|
534
566
|
handler: async ({ input }) => {
|
|
535
567
|
const relative = input.path === "." ? "." : boundedCodingRelativePath(input.path, limits.maxPathBytes);
|
|
568
|
+
await authorizeCodingPathChain(config, relative);
|
|
536
569
|
await authorizeCodingTool(config, {
|
|
537
570
|
operation: "glob",
|
|
538
571
|
pattern: input.pattern,
|
|
@@ -553,7 +586,8 @@ function createListingCodingTools(config, limits) {
|
|
|
553
586
|
maxDepth: limits.maxSearchDepth,
|
|
554
587
|
maxFiles: limits.maxSearchFiles,
|
|
555
588
|
symlinks: "skip",
|
|
556
|
-
excludeDirectory: (directory) => directory.split(path2.sep).some((segment) => excluded.has(segment))
|
|
589
|
+
excludeDirectory: (directory) => directory.split(path2.sep).some((segment) => excluded.has(segment)),
|
|
590
|
+
authorizePath: (candidate) => isCodingPathAuthorized(config, candidate)
|
|
557
591
|
});
|
|
558
592
|
const prefix = relative === "." ? "" : `${relative}${path2.sep}`;
|
|
559
593
|
const matched = scan.files.filter((file) => file.relative.startsWith(prefix)).map((file) => file.relative).filter((candidate) => matcher.test(candidate.slice(prefix.length))).sort();
|
|
@@ -646,7 +680,9 @@ function createSearchAndPatchCodingTools(config, limits) {
|
|
|
646
680
|
mode: input.mode
|
|
647
681
|
});
|
|
648
682
|
const root = await realpath4(config.root);
|
|
683
|
+
await authorizeCodingPath(config, ".");
|
|
649
684
|
const excluded = new Set(config.search?.excludeDirectories ?? DEFAULT_EXCLUDED_DIRECTORIES);
|
|
685
|
+
const included = input.include ? compileWorkspaceGlob(input.include) : null;
|
|
650
686
|
const scan = await scanContainedFiles({
|
|
651
687
|
root,
|
|
652
688
|
maxDepth: limits.maxSearchDepth,
|
|
@@ -656,10 +692,14 @@ function createSearchAndPatchCodingTools(config, limits) {
|
|
|
656
692
|
readMaxBytes: limits.maxReadBytes,
|
|
657
693
|
skipUnreadable: true
|
|
658
694
|
},
|
|
659
|
-
excludeDirectory: (relative) => relative.split(path3.sep).some((segment) => excluded.has(segment))
|
|
695
|
+
excludeDirectory: (relative) => relative.split(path3.sep).some((segment) => excluded.has(segment)),
|
|
696
|
+
authorizePath: async (candidate, kind) => {
|
|
697
|
+
if (kind === "file" && included && !included.test(candidate))
|
|
698
|
+
return false;
|
|
699
|
+
return await isCodingPathAuthorized(config, candidate);
|
|
700
|
+
}
|
|
660
701
|
});
|
|
661
702
|
const matcher = input.regex ? compileSearchRegex(input.query) : null;
|
|
662
|
-
const included = input.include ? compileWorkspaceGlob(input.include) : null;
|
|
663
703
|
const hit = (line) => {
|
|
664
704
|
if (line.length > MAX_SEARCHED_LINE_BYTES)
|
|
665
705
|
return false;
|
|
@@ -672,8 +712,6 @@ function createSearchAndPatchCodingTools(config, limits) {
|
|
|
672
712
|
const matches = [];
|
|
673
713
|
let truncated = false;
|
|
674
714
|
for (const file of scan.files) {
|
|
675
|
-
if (included && !included.test(file.relative))
|
|
676
|
-
continue;
|
|
677
715
|
if (input.mode === "path") {
|
|
678
716
|
if (hit(file.relative))
|
|
679
717
|
matches.push({ path: file.relative });
|
|
@@ -724,6 +762,7 @@ function createSearchAndPatchCodingTools(config, limits) {
|
|
|
724
762
|
handler: async ({ input }) => {
|
|
725
763
|
const root = await realpath4(config.root);
|
|
726
764
|
const relative = boundedCodingRelativePath(input.path, limits.maxPathBytes);
|
|
765
|
+
await authorizeCodingPathChain(config, relative);
|
|
727
766
|
const parent = await openContainedParent(root, relative).catch((error) => refuseMissingCodingPath(error, relative));
|
|
728
767
|
try {
|
|
729
768
|
return await withCodingPathLock(`${root}:${relative}`, async () => {
|
|
@@ -967,7 +1006,7 @@ function createShellCodingTool(config, limits) {
|
|
|
967
1006
|
if (!executable)
|
|
968
1007
|
throw new Error("Coding tool executable is not declared");
|
|
969
1008
|
const root = await realpath5(config.root);
|
|
970
|
-
const cwd = await existingCodingPath(root, input.cwd, limits.maxPathBytes);
|
|
1009
|
+
const cwd = await existingCodingPath(root, input.cwd === "." ? "." : boundedCodingRelativePath(input.cwd, limits.maxPathBytes), limits.maxPathBytes);
|
|
971
1010
|
if (!(await stat(cwd.absolute)).isDirectory()) {
|
|
972
1011
|
throw new Error("Coding tool cwd is not a directory");
|
|
973
1012
|
}
|
|
@@ -1073,6 +1112,7 @@ function createAgentCodingTools(config) {
|
|
|
1073
1112
|
}
|
|
1074
1113
|
export {
|
|
1075
1114
|
createAgentCodingTools,
|
|
1115
|
+
AgentCodingToolPathAuthorizationSchema,
|
|
1076
1116
|
AgentCodingToolLimitsSchema,
|
|
1077
1117
|
AgentCodingToolAuthorizationSchema
|
|
1078
1118
|
};
|
|
@@ -156,7 +156,7 @@ async function listAt(directory) {
|
|
|
156
156
|
}
|
|
157
157
|
return readdir(descriptorPath(directory), { withFileTypes: true });
|
|
158
158
|
}
|
|
159
|
-
async function listContainedDirectory(root, relative, maxEntries) {
|
|
159
|
+
async function listContainedDirectory(root, relative, maxEntries, authorizePath) {
|
|
160
160
|
let current = await openPinnedDirectory(root);
|
|
161
161
|
try {
|
|
162
162
|
if (relative !== ".") {
|
|
@@ -173,8 +173,14 @@ async function listContainedDirectory(root, relative, maxEntries) {
|
|
|
173
173
|
}
|
|
174
174
|
const raw = [...await listAt(current)];
|
|
175
175
|
raw.sort((left, right) => left.name.localeCompare(right.name));
|
|
176
|
+
const admitted = [];
|
|
177
|
+
for (const entry of raw) {
|
|
178
|
+
const entryPath = relative === "." ? entry.name : path.join(relative, entry.name);
|
|
179
|
+
if (!authorizePath || await authorizePath(entryPath))
|
|
180
|
+
admitted.push(entry);
|
|
181
|
+
}
|
|
176
182
|
const entries = [];
|
|
177
|
-
for (const entry of
|
|
183
|
+
for (const entry of admitted.slice(0, maxEntries)) {
|
|
178
184
|
const kind = entry.isDirectory() ? "directory" : entry.isSymbolicLink() ? "symlink" : entry.isFile() ? "file" : "other";
|
|
179
185
|
if (kind !== "file") {
|
|
180
186
|
entries.push({ name: entry.name, kind });
|
|
@@ -192,7 +198,7 @@ async function listContainedDirectory(root, relative, maxEntries) {
|
|
|
192
198
|
return left.name.localeCompare(right.name);
|
|
193
199
|
return left.kind === "directory" ? -1 : right.kind === "directory" ? 1 : 0;
|
|
194
200
|
});
|
|
195
|
-
return { entries, truncated:
|
|
201
|
+
return { entries, truncated: admitted.length > maxEntries };
|
|
196
202
|
} finally {
|
|
197
203
|
await current.close();
|
|
198
204
|
}
|
|
@@ -430,6 +436,9 @@ async function scanContainedFiles(input) {
|
|
|
430
436
|
entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
431
437
|
for (const entry of entries) {
|
|
432
438
|
const relative = relativeDirectory ? path.join(relativeDirectory, entry.name) : entry.name;
|
|
439
|
+
const kind = entry.isDirectory() ? "directory" : entry.isSymbolicLink() ? "symlink" : entry.isFile() ? "file" : "other";
|
|
440
|
+
if (input.authorizePath && !await input.authorizePath(relative, kind))
|
|
441
|
+
continue;
|
|
433
442
|
if (entry.isSymbolicLink()) {
|
|
434
443
|
if (input.symlinks === "refuse") {
|
|
435
444
|
throw new Error(`Contained file traversal refuses symlink: ${relative}`);
|
package/llms-full.txt
CHANGED
|
@@ -63,11 +63,11 @@ own, recorded as an ADR.
|
|
|
63
63
|
| `stitchkit/tracking/server` | server (Bun or Node) | evolving | the decisions a tracking backend makes — dispositions, visit lease over an application-owned store, active intervals, presence; no database |
|
|
64
64
|
| `stitchkit/release` | browser **and** server | evolving | a page follows the release it was built for — `createReleaseMarker` on the server, `createReleaseWatcher` in the browser, the `X-Build-Id` header and a socket event between them |
|
|
65
65
|
| `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
|
|
66
|
-
| `stitchkit/observability` | server | stable<br>_redefined in 1 of the
|
|
66
|
+
| `stitchkit/observability` | server | stable<br>_redefined in 1 of the 29 minors since 0.56.2, most recently 0.83.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
|
|
67
67
|
| `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the store and managed-resource conformance kits |
|
|
68
68
|
| `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
|
|
69
69
|
| `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
|
|
70
|
-
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in
|
|
70
|
+
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 15 of the 29 minors since 0.56.2, most recently 0.84.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
|
|
71
71
|
| `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
|
|
72
72
|
| `stitchkit/agent-runtime/coding-tools` | server (Bun or Node) | evolving | bounded host-authorized direct file and shell tools; a root boundary, not an OS sandbox |
|
|
73
73
|
| `stitchkit/agent-runtime/openrouter` | server | evolving | isolated OpenRouter language-model adapter |
|
|
@@ -75,7 +75,7 @@ own, recorded as an ADR.
|
|
|
75
75
|
| `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
|
|
76
76
|
| `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
|
|
77
77
|
| `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
|
|
78
|
-
| `stitchkit/application` | browser + server | evolving<br>_redefined in 7 of the
|
|
78
|
+
| `stitchkit/application` | browser + server | evolving<br>_redefined in 7 of the 29 minors since 0.56.2, most recently 0.83.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
|
|
79
79
|
| `stitchkit/application/grammy` | server | evolving | isolated grammY polling and webhook lifecycle adapters |
|
|
80
80
|
| `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
|
|
81
81
|
| `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
|
|
@@ -4716,8 +4716,16 @@ approval input fails the run with a private diagnostic rather than starting a fr
|
|
|
4716
4716
|
|
|
4717
4717
|
`stitchkit/agent-runtime/coding-tools` returns ordinary direct runtime tools named `read_file`,
|
|
4718
4718
|
`write_file`, `edit_file`, `list_directory`, `glob`, `search_files`, `run_command` and optional
|
|
4719
|
-
`read_output`. Every call passes a
|
|
4720
|
-
|
|
4719
|
+
`read_output`. Every call passes a required host authorization callback. An optional async/sync
|
|
4720
|
+
`authorizePath({ path })` is the one operation-independent policy for direct file access and
|
|
4721
|
+
discovery: direct read/write/edit refuses a denied path, while listing, glob and search omit it.
|
|
4722
|
+
Denial is recursive on every surface — direct access and discovery base paths ask about `.` and each
|
|
4723
|
+
ancestor, outermost first, stopping at the first refusal, so denying `credentials` denies everything
|
|
4724
|
+
under it. The comparison is segment-wise, so it does not deny `credentials-backup`, and a path of
|
|
4725
|
+
depth N costs N+1 questions. Directory decisions happen before descent; file decisions and
|
|
4726
|
+
`search_files.include` happen before opening content. Requested paths use `/`: a backslash is
|
|
4727
|
+
refused rather than normalised, because the workspace walk reads it as a separator and every
|
|
4728
|
+
authorization callback would otherwise be asked about a different decomposition than the one opened. File paths are relative, bounded and contained after
|
|
4721
4729
|
descriptor-relative resolution: each ancestor is opened without following symlinks and remains
|
|
4722
4730
|
pinned through authorization and the filesystem effect. Reads revalidate the pinned file identity;
|
|
4723
4731
|
writes and patches revalidate the pinned parent identity; search and resource discovery descend
|
|
@@ -4730,7 +4738,8 @@ UTF-8 and retained bytes are finite. Shell accepts a finite alias mapped by the
|
|
|
4730
4738
|
argument array — never a shell command string — and uses only the explicitly supplied environment.
|
|
4731
4739
|
Arguments, output and time are bounded, while cancellation terminates the child. The configured
|
|
4732
4740
|
root and cwd are path boundaries, not a security sandbox: isolate the process when an executable
|
|
4733
|
-
must not access the rest of the machine.
|
|
4741
|
+
must not access the rest of the machine. Path admission does not constrain `run_command`; an
|
|
4742
|
+
executable requires process isolation for that guarantee. → ADR 0172
|
|
4734
4743
|
|
|
4735
4744
|
`edit_file` replaces one exact snippet. `oldText` is itself the freshness guard for the region it
|
|
4736
4745
|
changes, so the digest is the optional `expectedSha256` and an edit is one call; pass the digest
|
|
@@ -11027,6 +11036,59 @@ makes one thing your job rather than the resolver's:
|
|
|
11027
11036
|
The mechanical part is identical either way. Only the *noticing* differs, and an
|
|
11028
11037
|
exact pin moves it onto you.
|
|
11029
11038
|
|
|
11039
|
+
## Released migration: 0.84.0
|
|
11040
|
+
|
|
11041
|
+
Only if you call `createAgentCodingTools`.
|
|
11042
|
+
|
|
11043
|
+
```bash
|
|
11044
|
+
rg -n "createAgentCodingTools"
|
|
11045
|
+
```
|
|
11046
|
+
|
|
11047
|
+
**1. Backslashes are refused.** If anything in your system hands the tools a Windows-style path,
|
|
11048
|
+
it now gets `FORBIDDEN`. Convert to `/` at the boundary:
|
|
11049
|
+
|
|
11050
|
+
```ts
|
|
11051
|
+
// before: read_file({ path: 'src\\index.ts' }) // reached openat as two segments
|
|
11052
|
+
// after: read_file({ path: 'src/index.ts' })
|
|
11053
|
+
```
|
|
11054
|
+
|
|
11055
|
+
This is the half that matters most: until now a backslash bypassed your **required**
|
|
11056
|
+
`authorize({ operation, path })` callback, not just `authorizePath`. A rule denying
|
|
11057
|
+
`credentials/token.txt` refused that spelling and served — and overwrote — the same file spelled
|
|
11058
|
+
`credentials\token.txt`. If your policy denies specific files, re-read your audit logs for
|
|
11059
|
+
backslash spellings.
|
|
11060
|
+
|
|
11061
|
+
**2. Denying a directory now denies everything under it — and a policy must admit every directory
|
|
11062
|
+
on the way to a file**, the way POSIX needs `+x` on each directory in a path.
|
|
11063
|
+
|
|
11064
|
+
If your policy is a DENY-list, it now does what it read as:
|
|
11065
|
+
|
|
11066
|
+
```ts
|
|
11067
|
+
authorizePath: ({ path }) => path !== 'credentials'
|
|
11068
|
+
// before: hid `credentials` from glob, served `credentials/token.txt` to read_file
|
|
11069
|
+
// after: refuses `credentials/token.txt`, `credentials/nested/deep.txt` and writes under it
|
|
11070
|
+
```
|
|
11071
|
+
|
|
11072
|
+
If you relied on the old behaviour — a directory hidden from discovery but readable directly — you
|
|
11073
|
+
were relying on a defect, and the two halves of one callback disagreeing. Name the paths you
|
|
11074
|
+
actually want reachable instead.
|
|
11075
|
+
|
|
11076
|
+
If your policy is an ALLOW-list, widen it to the directories it leads through, or nothing under
|
|
11077
|
+
them is reachable:
|
|
11078
|
+
|
|
11079
|
+
```ts
|
|
11080
|
+
// before: allowed src/index.ts, because only the leaf was ever asked
|
|
11081
|
+
authorizePath: ({ path }) => path.startsWith('src/')
|
|
11082
|
+
// after: `src` itself is asked first and refused, so the file is refused too
|
|
11083
|
+
authorizePath: ({ path }) => path === 'src' || path.startsWith('src/')
|
|
11084
|
+
```
|
|
11085
|
+
|
|
11086
|
+
The workspace root is not asked about — it is the boundary the tools already own.
|
|
11087
|
+
|
|
11088
|
+
**3. The policy is called more often.** A path of depth N costs N+1 calls (`.`, then each ancestor,
|
|
11089
|
+
then the leaf), outermost first, stopping at the first refusal. A policy that logs or meters will
|
|
11090
|
+
see a different sequence, and will no longer see the leaf once an ancestor has refused.
|
|
11091
|
+
|
|
11030
11092
|
## Released migration: 0.83.0
|
|
11031
11093
|
|
|
11032
11094
|
Two things, both mechanical, and only if you touch an observability sink.
|
|
@@ -15253,8 +15315,9 @@ artifact store is supplied, `read_output`.
|
|
|
15253
15315
|
| `createAgentCodingTools` | function | construct direct host-authorized bounded file, listing, glob, search, exact-snippet edit, shell and artifact runtime-tool definitions; every ordinary refusal is a typed code with an instructive `hint`, and filesystem operations use Linux `/proc/self/fd` or the packaged macOS Node-API backend and otherwise fail closed |
|
|
15254
15316
|
| `AGENT_CODING_TOOL_NAMES` | const | the mounted tool names — `read_file`, `write_file`, `edit_file`, `list_directory`, `glob`, `search_files`, `run_command`, `read_output` |
|
|
15255
15317
|
| `AgentCodingToolDefinition` | _type_ | peer-free structural direct-tool shape accepted by the canonical runtime-tool surface |
|
|
15256
|
-
| `AgentCodingToolConfig` | _type_ | absolute root, required authorization
|
|
15318
|
+
| `AgentCodingToolConfig` | _type_ | absolute root, required operation authorization, optional async/sync `authorizePath({ path })` shared by direct file effects and discovery, finite executable alias map, exact child environment and optional limits |
|
|
15257
15319
|
| `AgentCodingToolAuthorizationSchema` / `AgentCodingToolAuthorization` | schema / _type_ | discriminated read/write/search/patch/shell/artifact decision presented to host policy before effect |
|
|
15320
|
+
| `AgentCodingToolPathAuthorizationSchema` / `AgentCodingToolPathAuthorization` | schema / _type_ | strict workspace-relative path presented to the shared path policy before disclosure, opening or mutation |
|
|
15258
15321
|
| `AgentCodingToolLimitsSchema` / `AgentCodingToolLimits` | schema / _type_ | explicit path/read/write/argument-count/argument-byte/output/artifact/timeout/termination-grace ceilings |
|
|
15259
15322
|
| `AgentCodingArtifactStore` | _type_ | host-owned opaque artifact write and bounded read boundary |
|
|
15260
15323
|
| `FileReadInputSchema` / `FileReadOutputSchema` | schema | bounded strict-UTF-8 byte slice; offsets must align with UTF-8 code-point boundaries |
|
|
@@ -15268,6 +15331,16 @@ of aggregate argument text, 4 MiB per artifact and 30 seconds. The root is a pat
|
|
|
15268
15331
|
boundary, not an OS sandbox; executable behavior, process
|
|
15269
15332
|
isolation, credentials and external-effect idempotency remain host responsibilities.
|
|
15270
15333
|
|
|
15334
|
+
When `authorizePath` denies a path, direct read/write/edit returns `FORBIDDEN`, while
|
|
15335
|
+
`list_directory`, `glob` and `search_files` omit it. Denial is recursive: direct access and
|
|
15336
|
+
discovery base paths are checked against `.` and every ancestor, outermost first and segment-wise,
|
|
15337
|
+
so denying `credentials` denies `credentials/token.txt` but not `credentials-backup`. Directory
|
|
15338
|
+
admission happens before descent and file admission before opening; `search_files.include` is also
|
|
15339
|
+
applied before content is read. Requested paths must use `/` — a backslash is refused, because the
|
|
15340
|
+
containment walk reads it as a separator while a callback would read it as one name.
|
|
15341
|
+
`run_command` is intentionally outside this guarantee because an executable needs process isolation,
|
|
15342
|
+
not path filtering, to constrain its filesystem access. → ADR 0172.
|
|
15343
|
+
|
|
15271
15344
|
## `stitchkit/agent-runtime/browser`
|
|
15272
15345
|
|
|
15273
15346
|
Browser-safe canonical agent data. It re-exports the run, message, part, usage,
|
package/package.json
CHANGED