@bjoernrochel/sevro 0.1.0-rc.10 → 0.1.0-rc.11
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/docs/codex-host.md +18 -1
- package/docs/installing.md +2 -2
- package/docs/runtime-v1.md +9 -0
- package/package.json +1 -1
- package/src/hosts/codex-skill-reads.ts +122 -16
- package/src/hosts/codex-state.ts +2 -2
- package/src/hosts/linux-sandbox.ts +1 -1
- package/src/hosts/mac-sandbox.ts +14 -5
- package/src/native-transcript-state.ts +22 -3
- package/src/runtime-hooks.ts +1 -1
package/docs/codex-host.md
CHANGED
|
@@ -37,7 +37,24 @@ partial, as do failed, malformed, or incomplete recognized reads. Direct
|
|
|
37
37
|
exact `cat` wrapped by `lean-ctx -c` is accepted only when its reported output
|
|
38
38
|
still contains the full body; a compressed summary cannot prove the read.
|
|
39
39
|
The same exact-body check accepts a literal read followed by one literal `rg`
|
|
40
|
-
command, separated by a semicolon or newline. It also accepts a
|
|
40
|
+
command, separated by a semicolon or newline. It also accepts a single-file
|
|
41
|
+
literal `cat` of the skill followed by one or more literal `cat` reads of
|
|
42
|
+
non-skill files, then one literal `rg` command, with the same separators. For
|
|
43
|
+
example:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
cat .agents/skills/darrow-guide/SKILL.md; cat docs/specs/repository-guide.md; rg -n -i 'offline|\bsla\b|service.level|guarantee|deployment' --glob '*.md' --glob '!docs/research/**' --glob '!**/SKILL.md' .
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This sequence uses the shared stream and native read check. Quoted separators
|
|
50
|
+
remain part of their literal operand. The initial skill body must match the
|
|
51
|
+
beginning of the retained output, so later documentation or search output cannot
|
|
52
|
+
replace an altered or incomplete initial read. Subsequent reads must not name
|
|
53
|
+
another `SKILL.md` or resolve to a mounted or receipt-bound skill through an alias.
|
|
54
|
+
The 64-operand bound applies across all reads in the sequence. Shell expansions,
|
|
55
|
+
including unquoted glob and tilde operands, are unsupported in this form.
|
|
56
|
+
|
|
57
|
+
The check also accepts a chain of literal
|
|
41
58
|
`cat` fallbacks with `2>/dev/null` and `||`, ending in `true` followed by a newline
|
|
42
59
|
and one literal `rg` discovery command. A fallback chain must resolve to one
|
|
43
60
|
canonical file across all its read operands; absent alternate targets do not
|
package/docs/installing.md
CHANGED
|
@@ -22,10 +22,10 @@ Local installation records the selected version in the project lockfile.
|
|
|
22
22
|
|
|
23
23
|
## Current release
|
|
24
24
|
|
|
25
|
-
The current release is `0.1.0-rc.
|
|
25
|
+
The current release is `0.1.0-rc.11`. Install this exact version:
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
|
-
bun add --exact @bjoernrochel/sevro@0.1.0-rc.
|
|
28
|
+
bun add --exact @bjoernrochel/sevro@0.1.0-rc.11
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
<!-- sevro-current-release:end -->
|
package/docs/runtime-v1.md
CHANGED
|
@@ -289,6 +289,15 @@ instead of being skipped. The wrapper denies network access and protected
|
|
|
289
289
|
roots and owns asynchronous hook cleanup. Native timeout/cancellation stops
|
|
290
290
|
owned hook descendants before private state is removed.
|
|
291
291
|
|
|
292
|
+
Prepared native plugin projections live outside the fixture in the current
|
|
293
|
+
host request's private native state. Curated hook read grants validate the
|
|
294
|
+
canonical projection layout, ownership, private state permissions, and binding
|
|
295
|
+
to that request. Only the selected plugin subtree is readable; it stays
|
|
296
|
+
read-only, and credentials, private siblings, and peer requests remain denied.
|
|
297
|
+
Ordinary filesystem declarations cannot select this exception. Codex still
|
|
298
|
+
validates and filters installed plugins with a runtime policy when hooks are
|
|
299
|
+
disabled. Projections are removed with their owning host request.
|
|
300
|
+
|
|
292
301
|
Auxiliary `.mcp.json` and `.lsp.json` files and plugin mods are unsupported under
|
|
293
302
|
hook authority. Manifest MCP/LSP declarations are removed from curated mounts.
|
|
294
303
|
|
package/package.json
CHANGED
|
@@ -38,6 +38,7 @@ function shellPayload(command: string): string {
|
|
|
38
38
|
interface DirectRead {
|
|
39
39
|
path: string;
|
|
40
40
|
fallbackPaths?: string[];
|
|
41
|
+
documentationPaths?: string[];
|
|
41
42
|
operandCount?: number;
|
|
42
43
|
firstLine?: number;
|
|
43
44
|
lastLine?: number;
|
|
@@ -68,9 +69,11 @@ function sedRead(payload: string, path: string): DirectRead | null {
|
|
|
68
69
|
return { path: capturedReadPath(sed, 3), firstLine, lastLine };
|
|
69
70
|
}
|
|
70
71
|
function directRead(command: string): DirectRead | null {
|
|
71
|
-
const
|
|
72
|
+
const shell = shellPayload(command).trim();
|
|
73
|
+
const payload = compoundReadPayload(shell);
|
|
72
74
|
const path = "(?:\"([^\"]+)\"|'([^']+)'|([^\\s'\"`$;&|<>]+))";
|
|
73
75
|
return (
|
|
76
|
+
sequentialRead(shell, path) ??
|
|
74
77
|
catRead(payload, path) ??
|
|
75
78
|
sedRead(payload, path) ??
|
|
76
79
|
fallbackRead(payload, path)
|
|
@@ -79,6 +82,65 @@ function directRead(command: string): DirectRead | null {
|
|
|
79
82
|
|
|
80
83
|
const LITERAL_WORD = "(?:\"[^\"$`\\\\]*\"|'[^']*'|[^\\s\"'\\\\`$;&|<>(){}#]+)";
|
|
81
84
|
const LITERAL_WORDS = `${LITERAL_WORD}(?:[ \\t]+${LITERAL_WORD})*`;
|
|
85
|
+
const SEQUENTIAL_WORD =
|
|
86
|
+
"(?:\"[^\"$`\\\\]*\"|'[^']*'|[^\\s\"'\\\\`$;&|<>(){}#*?\\[\\]~]+)";
|
|
87
|
+
const SEQUENTIAL_WORDS = `${SEQUENTIAL_WORD}(?:[ \\t]+${SEQUENTIAL_WORD})*`;
|
|
88
|
+
|
|
89
|
+
// Recognize only literal commands; quoted separators remain part of their word.
|
|
90
|
+
function literalCommands(payload: string): string[] | null {
|
|
91
|
+
const clause = new RegExp(
|
|
92
|
+
`[ \\t\\r\\n]*(${SEQUENTIAL_WORDS})[ \\t]*(?:[;\\n]|$)`,
|
|
93
|
+
"gy",
|
|
94
|
+
);
|
|
95
|
+
const commands: string[] = [];
|
|
96
|
+
while (clause.lastIndex < payload.length) {
|
|
97
|
+
const match = clause.exec(payload);
|
|
98
|
+
if (!match || commands.length > MAX_NATIVE_READ_ATTEMPTS) return null;
|
|
99
|
+
commands.push(match.slice(1, 2).join(""));
|
|
100
|
+
}
|
|
101
|
+
return commands;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function sequentialRead(payload: string, path: string): DirectRead | null {
|
|
105
|
+
const commands = literalCommands(payload);
|
|
106
|
+
if (!commands || commands.length < 3) return null;
|
|
107
|
+
const search = commands.slice(-1).join("");
|
|
108
|
+
if (!new RegExp(`^(?:/usr/bin/)?rg[ \\t]+${LITERAL_WORDS}$`).test(search))
|
|
109
|
+
return null;
|
|
110
|
+
return sequentialCatRead(commands.slice(0, -1), path);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function sequentialCatRead(
|
|
114
|
+
commands: string[],
|
|
115
|
+
path: string,
|
|
116
|
+
): DirectRead | null {
|
|
117
|
+
const first = singleSkillRead(commands.slice(0, 1).join(""), path);
|
|
118
|
+
if (!first) return null;
|
|
119
|
+
const operands = commands
|
|
120
|
+
.slice(1)
|
|
121
|
+
.map((command) => catOperands(command, path));
|
|
122
|
+
if (!operands.every((paths): paths is string[] => paths !== null))
|
|
123
|
+
return null;
|
|
124
|
+
const paths = operands.flat();
|
|
125
|
+
if (unsupportedDocumentationOperands(paths)) return null;
|
|
126
|
+
return {
|
|
127
|
+
...first,
|
|
128
|
+
documentationPaths: paths,
|
|
129
|
+
operandCount: paths.length + 1,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function singleSkillRead(payload: string, path: string): DirectRead | null {
|
|
134
|
+
const read = catRead(payload, path);
|
|
135
|
+
return read?.operandCount === 1 ? read : null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function unsupportedDocumentationOperands(paths: string[]): boolean {
|
|
139
|
+
return (
|
|
140
|
+
paths.length >= MAX_NATIVE_READ_ATTEMPTS ||
|
|
141
|
+
paths.some((path) => /(?:^|\/)SKILL\.md$/.test(path))
|
|
142
|
+
);
|
|
143
|
+
}
|
|
82
144
|
|
|
83
145
|
function compoundReadPayload(payload: string): string {
|
|
84
146
|
const sequential = payload.match(
|
|
@@ -102,14 +164,11 @@ function fallbackRead(payload: string, path: string): DirectRead | null {
|
|
|
102
164
|
].map((match) => catRead(match.slice(1, 2).join(""), path));
|
|
103
165
|
if (
|
|
104
166
|
fallbackOperandCount(reads) > MAX_NATIVE_READ_ATTEMPTS ||
|
|
105
|
-
reads.
|
|
167
|
+
!reads.every((read): read is DirectRead => read !== null)
|
|
106
168
|
)
|
|
107
169
|
return null;
|
|
108
|
-
const paths = reads.
|
|
109
|
-
|
|
110
|
-
return first === undefined
|
|
111
|
-
? null
|
|
112
|
-
: { path: first, fallbackPaths: paths.slice(1) };
|
|
170
|
+
const paths = reads.map((read) => read.path);
|
|
171
|
+
return { path: paths.slice(0, 1).join(""), fallbackPaths: paths.slice(1) };
|
|
113
172
|
}
|
|
114
173
|
|
|
115
174
|
function fallbackOperandCount(reads: Array<DirectRead | null>): number {
|
|
@@ -121,7 +180,7 @@ function unsupportedCatOptions(payload: string, paths: string[]): boolean {
|
|
|
121
180
|
return paths.some((path) => path.startsWith("-"));
|
|
122
181
|
}
|
|
123
182
|
|
|
124
|
-
function
|
|
183
|
+
function catOperands(payload: string, path: string): string[] | null {
|
|
125
184
|
const cat = payload.match(
|
|
126
185
|
new RegExp(`^(?:/bin/)?cat\\s+(?:--\\s+)?(${path}(?:\\s+${path})*)$`),
|
|
127
186
|
);
|
|
@@ -129,12 +188,14 @@ function catRead(payload: string, path: string): DirectRead | null {
|
|
|
129
188
|
const paths = [
|
|
130
189
|
...cat.slice(1, 2).join("").matchAll(new RegExp(path, "g")),
|
|
131
190
|
].map((match) => capturedReadPath(match, 1));
|
|
191
|
+
return unsupportedCatOptions(payload, paths) ? null : paths;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function catRead(payload: string, path: string): DirectRead | null {
|
|
195
|
+
const paths = catOperands(payload, path);
|
|
196
|
+
if (!paths) return null;
|
|
132
197
|
const skills = paths.filter((value) => /(?:^|\/)SKILL\.md$/.test(value));
|
|
133
|
-
if (
|
|
134
|
-
skills.length !== 1 ||
|
|
135
|
-
paths.length > MAX_NATIVE_READ_ATTEMPTS ||
|
|
136
|
-
unsupportedCatOptions(payload, paths)
|
|
137
|
-
)
|
|
198
|
+
if (skills.length !== 1 || paths.length > MAX_NATIVE_READ_ATTEMPTS)
|
|
138
199
|
return null;
|
|
139
200
|
return { path: skills.join(""), operandCount: paths.length };
|
|
140
201
|
}
|
|
@@ -197,11 +258,26 @@ async function mountedReadPath(
|
|
|
197
258
|
);
|
|
198
259
|
}
|
|
199
260
|
function readAttemptPaths(command: string): string[] {
|
|
200
|
-
const
|
|
261
|
+
const shell = shellPayload(command).trim();
|
|
262
|
+
const payload = compoundReadPayload(shell);
|
|
201
263
|
const path = "(?:\"([^\"]+)\"|'([^']+)'|([^\\s'\"`$;&|<>]+))";
|
|
202
264
|
const fallback = fallbackRead(payload, path);
|
|
203
265
|
if (fallback) return [fallback.path, ...(fallback.fallbackPaths ?? [])];
|
|
204
|
-
|
|
266
|
+
const sequential = sequentialRead(shell, path);
|
|
267
|
+
if (sequential) return [sequential.path];
|
|
268
|
+
return [
|
|
269
|
+
...literalReadAttemptPaths(payload, path),
|
|
270
|
+
...sequentialReadAttemptPaths(shell, path),
|
|
271
|
+
];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function sequentialReadAttemptPaths(payload: string, path: string): string[] {
|
|
275
|
+
const first = payload.match(
|
|
276
|
+
new RegExp(`^(${SEQUENTIAL_WORDS})[ \\t]*[;\\n]`),
|
|
277
|
+
);
|
|
278
|
+
if (!first) return [];
|
|
279
|
+
// The suffix may be unsupported. This records an attempt, never read credit.
|
|
280
|
+
return literalReadAttemptPaths(first.slice(1, 2).join(""), path);
|
|
205
281
|
}
|
|
206
282
|
function literalReadAttemptPaths(payload: string, path: string): string[] {
|
|
207
283
|
const cat = payload.match(
|
|
@@ -323,8 +399,29 @@ async function resolvedSkillRead(
|
|
|
323
399
|
if (skill === null) return null;
|
|
324
400
|
const text = await verifiedSkillBody(actual, skill);
|
|
325
401
|
if (text === null) return null;
|
|
402
|
+
if (
|
|
403
|
+
await ambiguousSequentialRead(
|
|
404
|
+
declaration.read,
|
|
405
|
+
commandCwd,
|
|
406
|
+
workspace,
|
|
407
|
+
pluginRoots,
|
|
408
|
+
)
|
|
409
|
+
)
|
|
410
|
+
return null;
|
|
326
411
|
return skillReadReceipt(actual, skill, declaration, text);
|
|
327
412
|
}
|
|
413
|
+
|
|
414
|
+
async function ambiguousSequentialRead(
|
|
415
|
+
read: DirectRead,
|
|
416
|
+
commandCwd: string,
|
|
417
|
+
workspace: string,
|
|
418
|
+
pluginRoots: string[],
|
|
419
|
+
): Promise<boolean> {
|
|
420
|
+
for (const path of read.documentationPaths ?? [])
|
|
421
|
+
if (await mountedReadPath(path, commandCwd, workspace, pluginRoots))
|
|
422
|
+
return true;
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
328
425
|
async function resolvedReadPath(
|
|
329
426
|
read: DirectRead,
|
|
330
427
|
commandCwd: string,
|
|
@@ -344,10 +441,19 @@ function skillReadReceipt(
|
|
|
344
441
|
): VerifiedRead | null {
|
|
345
442
|
const range = readRange(text, declaration.read);
|
|
346
443
|
if (range[0] === range[1]) return null;
|
|
347
|
-
if (!declaration
|
|
444
|
+
if (!matchesReadOutput(declaration, text.slice(...range))) return null;
|
|
348
445
|
return { skill, path: actual, range, bodyLength: text.length };
|
|
349
446
|
}
|
|
350
447
|
|
|
448
|
+
function matchesReadOutput(
|
|
449
|
+
declaration: { read: DirectRead; output: string },
|
|
450
|
+
expected: string,
|
|
451
|
+
): boolean {
|
|
452
|
+
if (declaration.read.documentationPaths !== undefined)
|
|
453
|
+
return declaration.output.startsWith(expected);
|
|
454
|
+
return declaration.output.includes(expected);
|
|
455
|
+
}
|
|
456
|
+
|
|
351
457
|
function fullCoverage(
|
|
352
458
|
ranges: Array<[number, number]>,
|
|
353
459
|
length: number,
|
package/src/hosts/codex-state.ts
CHANGED
|
@@ -77,7 +77,7 @@ export async function codexHookMounts(
|
|
|
77
77
|
if (!request.runtimePolicy) return undefined;
|
|
78
78
|
const hooks = await prepareHookMounts({
|
|
79
79
|
workspace: request.workspace,
|
|
80
|
-
stateRoot:
|
|
80
|
+
stateRoot: state.stateRoot,
|
|
81
81
|
privateRoot: state.codexHome,
|
|
82
82
|
pluginRoots: installedPluginRoots,
|
|
83
83
|
protectedRoots: state.protectedRoots,
|
|
@@ -380,7 +380,7 @@ export async function prepareCodexState(
|
|
|
380
380
|
const shellRoot = await fixtureShellRoot(request, paths.parentHome);
|
|
381
381
|
const env = codexEnvironment(paths, request, shellRoot);
|
|
382
382
|
applyCodexRuntime(runtime, env, paths);
|
|
383
|
-
return { ...paths, sandboxBinary, profileId, env, protectedRoots };
|
|
383
|
+
return { ...paths, stateRoot, sandboxBinary, profileId, env, protectedRoots };
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
function requireCodexRuntimeReadRoots(
|
|
@@ -54,7 +54,7 @@ export async function prepareLinuxSandboxCommand(
|
|
|
54
54
|
requireLinuxSandbox();
|
|
55
55
|
await requireNativeTranscriptViews(options.transcriptReadRoots);
|
|
56
56
|
await requireCandidateArtifactRoots(options.candidateArtifactRoots);
|
|
57
|
-
await requireNativeHookRoots(options.hookReadRoots);
|
|
57
|
+
await requireNativeHookRoots(options.hookReadRoots, options.privateStateRoot);
|
|
58
58
|
requireIsolationPaths(options);
|
|
59
59
|
const workspace = await realpath(options.workspace);
|
|
60
60
|
const roots = options.protectedRootsCanonical
|
package/src/hosts/mac-sandbox.ts
CHANGED
|
@@ -2,7 +2,10 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { mkdir, realpath, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
requireNativeTranscriptViews,
|
|
7
|
+
requireNativeHookRoots,
|
|
8
|
+
} from "../native-transcript-state";
|
|
6
9
|
import { requireCandidateArtifactRoots } from "../candidate-artifacts";
|
|
7
10
|
import { workspaceBoundaryException } from "../workspace-state";
|
|
8
11
|
import {
|
|
@@ -148,6 +151,7 @@ export async function prepareMacSandboxCommand(
|
|
|
148
151
|
requireSandboxAvailable();
|
|
149
152
|
await requireNativeTranscriptViews(options.transcriptReadRoots);
|
|
150
153
|
await requireCandidateArtifactRoots(options.candidateArtifactRoots);
|
|
154
|
+
await requireNativeHookRoots(options.hookReadRoots, options.privateStateRoot);
|
|
151
155
|
requireIsolationPaths(options);
|
|
152
156
|
const workspace = await realpath(options.workspace);
|
|
153
157
|
const roots = options.protectedRootsCanonical
|
|
@@ -170,10 +174,7 @@ export async function prepareMacSandboxCommand(
|
|
|
170
174
|
options.denyNetwork,
|
|
171
175
|
readRoots,
|
|
172
176
|
ownedRoot,
|
|
173
|
-
|
|
174
|
-
...(options.transcriptReadRoots ?? []),
|
|
175
|
-
...Object.values(options.candidateArtifactRoots ?? {}),
|
|
176
|
-
],
|
|
177
|
+
curatedReadRoots(options),
|
|
177
178
|
workspace,
|
|
178
179
|
),
|
|
179
180
|
{ flag: "wx", mode: 0o600 },
|
|
@@ -186,6 +187,14 @@ export async function prepareMacSandboxCommand(
|
|
|
186
187
|
};
|
|
187
188
|
}
|
|
188
189
|
|
|
190
|
+
function curatedReadRoots(options: IsolationOptions): string[] {
|
|
191
|
+
return [
|
|
192
|
+
...(options.transcriptReadRoots ?? []),
|
|
193
|
+
...(options.hookReadRoots ?? []),
|
|
194
|
+
...Object.values(options.candidateArtifactRoots ?? {}),
|
|
195
|
+
];
|
|
196
|
+
}
|
|
197
|
+
|
|
189
198
|
function requireSandboxAvailable(): void {
|
|
190
199
|
if (process.platform !== "darwin" || !existsSync(SANDBOX_EXEC))
|
|
191
200
|
throw new HostIsolationError("macOS sandbox-exec isolation is unavailable");
|
|
@@ -84,20 +84,39 @@ export async function requireNativeTranscriptViews(
|
|
|
84
84
|
/** Curated hook copies are a separate runner-owned exception, never policy roots. */
|
|
85
85
|
export async function requireNativeHookRoots(
|
|
86
86
|
roots: string[] | undefined,
|
|
87
|
+
privateRoot: string,
|
|
87
88
|
): Promise<void> {
|
|
88
|
-
for (const root of roots ?? [])
|
|
89
|
+
for (const root of roots ?? [])
|
|
90
|
+
await requireNativeHookRoot(root, privateRoot);
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
async function requireNativeHookRoot(
|
|
93
|
+
async function requireNativeHookRoot(
|
|
94
|
+
root: string,
|
|
95
|
+
privateRoot: string,
|
|
96
|
+
): Promise<void> {
|
|
92
97
|
const parent = await nativeStateParent();
|
|
93
98
|
const state = dirname(dirname(root));
|
|
94
99
|
if (!nativeHookLayout(root, state, parent) || (await realpath(root)) !== root)
|
|
95
100
|
throw new Error("native hook read root must be a canonical curated mount");
|
|
101
|
+
await requireNativeHookOwner(state, privateRoot);
|
|
96
102
|
const info = await lstat(root);
|
|
97
103
|
if (!info.isDirectory() || info.uid !== userInfo().uid)
|
|
98
104
|
throw new Error("native hook read root must be owned");
|
|
99
105
|
}
|
|
100
106
|
|
|
107
|
+
async function requireNativeHookOwner(
|
|
108
|
+
state: string,
|
|
109
|
+
privateRoot: string,
|
|
110
|
+
): Promise<void> {
|
|
111
|
+
if (
|
|
112
|
+
dirname(privateRoot) !== state ||
|
|
113
|
+
(await realpath(privateRoot)) !== privateRoot
|
|
114
|
+
)
|
|
115
|
+
throw new Error("native hook read root must belong to the current request");
|
|
116
|
+
requirePrivateNamespace(await lstat(state));
|
|
117
|
+
requirePrivateNamespace(await lstat(privateRoot));
|
|
118
|
+
}
|
|
119
|
+
|
|
101
120
|
function nativeHookLayout(
|
|
102
121
|
root: string,
|
|
103
122
|
state: string,
|
|
@@ -105,7 +124,7 @@ function nativeHookLayout(
|
|
|
105
124
|
): boolean {
|
|
106
125
|
return (
|
|
107
126
|
dirname(state) === parent &&
|
|
108
|
-
basename(state)
|
|
127
|
+
/^(?:claude|codex)-/.test(basename(state)) &&
|
|
109
128
|
basename(dirname(root)) === "runtime-plugins" &&
|
|
110
129
|
/^plugin-[0-9]+$/.test(basename(root))
|
|
111
130
|
);
|
package/src/runtime-hooks.ts
CHANGED
|
@@ -217,7 +217,7 @@ async function hookReadRoots(
|
|
|
217
217
|
];
|
|
218
218
|
const curated = trusted ? [root] : [];
|
|
219
219
|
requireRuntimeReadRoots(ordinary, options.protectedRoots);
|
|
220
|
-
await requireNativeHookRoots(curated);
|
|
220
|
+
await requireNativeHookRoots(curated, options.privateRoot);
|
|
221
221
|
return { ordinary, curated };
|
|
222
222
|
}
|
|
223
223
|
|