@dharma-ai-labs/agent-fabric 0.2.181 → 0.2.183

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.
@@ -0,0 +1,15 @@
1
+ import { type ContainerProcessIdentity } from './containerRelayLifecycle.js';
2
+ export declare function matchingCliExecutable(pid: number, caller: string, uid: number): Promise<boolean>;
3
+ interface ProcessVerification {
4
+ uid?: number;
5
+ execPath?: string;
6
+ identity?(pid: number): Promise<ContainerProcessIdentity>;
7
+ resolve?(path: string): Promise<string>;
8
+ executableMatches?(pid: number, caller: string, uid: number): Promise<boolean>;
9
+ verifyRuntime?(candidate: string, caller: string): Promise<string>;
10
+ }
11
+ /** Normalize npm aliases only after verifying the live executable and exact
12
+ * published CLI bytes. Callers still enforce their complete action/flag tuple. */
13
+ export declare function verifiedCliProcessArguments(identity: ContainerProcessIdentity, callerIndex: string, runtime?: ProcessVerification): Promise<string[] | null>;
14
+ export {};
15
+ //# sourceMappingURL=cliProcessIdentity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cliProcessIdentity.d.ts","sourceRoot":"","sources":["../src/cliProcessIdentity.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,KAAK,wBAAwB,EAAC,MAAM,8BAA8B,CAAC;AAqCrE,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,oBAMnF;AAED,UAAU,mBAAmB;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAC1D,OAAO,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,iBAAiB,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/E,aAAa,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACpE;AAED;kFACkF;AAClF,wBAAsB,2BAA2B,CAAC,QAAQ,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,EACvG,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAiC7D"}
@@ -0,0 +1,102 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { constants } from 'node:fs';
3
+ import { open, realpath, stat } from 'node:fs/promises';
4
+ import { basename, dirname, isAbsolute, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { readContainerProcessIdentity, verifyContainerControllerRuntime } from './containerRelayLifecycle.js';
7
+ const executableHashes = new Map();
8
+ async function publicExecutableHash(path, uid) {
9
+ const canonical = await realpath(path);
10
+ const file = await open(canonical, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
11
+ try {
12
+ const before = await file.stat();
13
+ const mapped = await stat(path);
14
+ if (!before.isFile() || ![0, uid].includes(before.uid) || (before.mode & 0o022) !== 0
15
+ || before.size < 1 || before.size > 268_435_456 || before.dev !== mapped.dev || before.ino !== mapped.ino) {
16
+ throw new Error('cli_process_unverified');
17
+ }
18
+ const fingerprint = JSON.stringify([canonical, before.dev, before.ino, before.uid, before.mode,
19
+ before.size, before.mtimeMs, before.ctimeMs]);
20
+ let digest = executableHashes.get(fingerprint);
21
+ if (!digest) {
22
+ digest = (async () => {
23
+ const hash = createHash('sha256');
24
+ for await (const chunk of file.createReadStream({ autoClose: false }))
25
+ hash.update(chunk);
26
+ return hash.digest('hex');
27
+ })();
28
+ if (executableHashes.size >= 16)
29
+ executableHashes.clear();
30
+ executableHashes.set(fingerprint, digest);
31
+ digest.catch(() => executableHashes.delete(fingerprint));
32
+ }
33
+ const result = await digest;
34
+ const after = await file.stat();
35
+ if (JSON.stringify([after.dev, after.ino, after.uid, after.mode, after.size, after.mtimeMs, after.ctimeMs])
36
+ !== JSON.stringify([before.dev, before.ino, before.uid, before.mode, before.size, before.mtimeMs, before.ctimeMs])) {
37
+ executableHashes.delete(fingerprint);
38
+ throw new Error('cli_process_unverified');
39
+ }
40
+ return result;
41
+ }
42
+ finally {
43
+ await file.close();
44
+ }
45
+ }
46
+ export async function matchingCliExecutable(pid, caller, uid) {
47
+ if (await realpath(caller) !== await realpath('/proc/self/exe'))
48
+ return false;
49
+ const [running, trusted] = await Promise.all([
50
+ publicExecutableHash(`/proc/${pid}/exe`, uid), publicExecutableHash('/proc/self/exe', uid),
51
+ ]);
52
+ return running === trusted;
53
+ }
54
+ /** Normalize npm aliases only after verifying the live executable and exact
55
+ * published CLI bytes. Callers still enforce their complete action/flag tuple. */
56
+ export async function verifiedCliProcessArguments(identity, callerIndex, runtime = {}) {
57
+ try {
58
+ const uid = runtime.uid ?? process.getuid?.() ?? -1, execPath = runtime.execPath ?? process.execPath;
59
+ const readIdentity = runtime.identity ?? readContainerProcessIdentity;
60
+ const resolve = runtime.resolve ?? realpath;
61
+ const executableMatches = runtime.executableMatches ?? matchingCliExecutable;
62
+ const verifyRuntime = runtime.verifyRuntime ?? (async (candidate, caller) => {
63
+ const verified = await verifyContainerControllerRuntime(candidate, caller);
64
+ const [installedGuard, callerGuard] = await Promise.all([
65
+ publicExecutableHash(join(dirname(candidate), 'cliProcessIdentity.js'), uid),
66
+ publicExecutableHash(join(dirname(caller), 'cliProcessIdentity.js'), uid),
67
+ ]);
68
+ if (installedGuard !== callerGuard)
69
+ throw new Error('cli_process_unverified');
70
+ return verified;
71
+ });
72
+ if (uid <= 0 || identity.uid !== uid || !Number.isSafeInteger(identity.pid) || identity.pid < 1
73
+ || !/^\d{1,20}$/.test(identity.startTicks) || identity.argv.length < 2)
74
+ return null;
75
+ if (JSON.stringify(await readIdentity(identity.pid)) !== JSON.stringify(identity))
76
+ return null;
77
+ const command = identity.argv[0], script = identity.argv[1];
78
+ if ((command !== 'node' && !isAbsolute(command)) || !isAbsolute(script) || script.length > 4096
79
+ || /[\r\n\0]/.test(script))
80
+ return null;
81
+ const executable = await resolve(`/proc/${identity.pid}/exe`);
82
+ if (command !== 'node' && await resolve(command) !== executable)
83
+ return null;
84
+ if (!await executableMatches(identity.pid, execPath, uid))
85
+ return null;
86
+ const entrypoint = await resolve(script), kind = basename(entrypoint);
87
+ if (!['bin.js', 'index.js'].includes(kind) || basename(dirname(entrypoint)) !== 'dist')
88
+ return null;
89
+ const ownIndex = fileURLToPath(callerIndex), ownBin = join(dirname(ownIndex), 'bin.js');
90
+ const candidateBin = join(dirname(entrypoint), 'bin.js');
91
+ if (await verifyRuntime(candidateBin, ownBin) !== candidateBin)
92
+ return null;
93
+ if (await resolve(script) !== entrypoint || await resolve(`/proc/${identity.pid}/exe`) !== executable
94
+ || JSON.stringify(await readIdentity(identity.pid)) !== JSON.stringify(identity))
95
+ return null;
96
+ return [execPath, join(dirname(ownIndex), kind), ...identity.argv.slice(2)];
97
+ }
98
+ catch {
99
+ return null;
100
+ }
101
+ }
102
+ //# sourceMappingURL=cliProcessIdentity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cliProcessIdentity.js","sourceRoot":"","sources":["../src/cliProcessIdentity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cliProcessIdentity.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cliProcessIdentity.test.d.ts","sourceRoot":"","sources":["../src/cliProcessIdentity.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,152 @@
1
+ import assert from 'node:assert/strict';
2
+ import { spawn } from 'node:child_process';
3
+ import { once } from 'node:events';
4
+ import { chmod, mkdir, mkdtemp, rm, symlink, writeFile } from 'node:fs/promises';
5
+ import { tmpdir } from 'node:os';
6
+ import { join, resolve } from 'node:path';
7
+ import { pathToFileURL } from 'node:url';
8
+ import test from 'node:test';
9
+ import { verifiedCliProcessArguments, matchingCliExecutable } from './cliProcessIdentity.js';
10
+ import { readContainerProcessIdentity } from './containerRelayLifecycle.js';
11
+ const trustedIndex = pathToFileURL(resolve('/trusted/dist/index.js')).href;
12
+ function fixture() {
13
+ const identity = { pid: 101, uid: 1000, parentPid: 100,
14
+ processGroupId: 101, sessionId: 101, startTicks: '123',
15
+ argv: ['node', resolve('/cache/first/node_modules/.bin/dharma'), 'relay', 'supervise', '--policy', resolve('/workspace/.dharma/policy.json')] };
16
+ const node = resolve('/runtime/node'), bin = resolve('/cache/second/node_modules/@dharma-ai-labs/agent-fabric/dist/bin.js');
17
+ let reads = 0;
18
+ const observed = structuredClone(identity);
19
+ const runtime = {
20
+ uid: 1000, execPath: node,
21
+ identity: async (pid) => { assert.equal(pid, 101); reads++; return structuredClone(observed); },
22
+ resolve: async (path) => {
23
+ if (path === '/proc/101/exe' || path === node)
24
+ return node;
25
+ if (path === identity.argv[1])
26
+ return bin;
27
+ throw new Error('fixture_path_unavailable');
28
+ },
29
+ executableMatches: async (pid, caller, uid) => {
30
+ assert.equal(pid, 101);
31
+ assert.equal(caller, node);
32
+ assert.equal(uid, 1000);
33
+ return true;
34
+ },
35
+ verifyRuntime: async (candidate, caller) => {
36
+ assert.equal(candidate, bin);
37
+ assert.equal(caller, resolve('/trusted/dist/bin.js'));
38
+ return candidate;
39
+ },
40
+ };
41
+ return { identity, observed, runtime, node, bin, reads: () => reads };
42
+ }
43
+ test('npm node and dharma aliases normalize only after executable/artifact verification', async () => {
44
+ const f = fixture();
45
+ assert.deepEqual(await verifiedCliProcessArguments(f.identity, trustedIndex, f.runtime), [f.node, resolve('/trusted/dist/bin.js'), ...f.identity.argv.slice(2)]);
46
+ assert.equal(f.reads(), 2);
47
+ });
48
+ test('an absolute command must resolve to the actual live executable', async () => {
49
+ const f = fixture();
50
+ f.identity.argv[0] = f.node;
51
+ f.observed.argv[0] = f.node;
52
+ assert.ok(await verifiedCliProcessArguments(f.identity, trustedIndex, f.runtime));
53
+ f.runtime.resolve = async (path) => path === '/proc/101/exe' ? f.node : '/foreign/node';
54
+ assert.equal(await verifiedCliProcessArguments(f.identity, trustedIndex, f.runtime), null);
55
+ });
56
+ for (const change of ['uid', 'root', 'pid', 'creation', 'command', 'relative-script', 'foreign-runtime', 'foreign-artifact',
57
+ 'unknown-script', 'pid-reuse', 'script-drift', 'executable-drift', 'unavailable']) {
58
+ test(`npm aliases remain fail-closed for ${change}`, async () => {
59
+ const f = fixture();
60
+ if (change === 'uid')
61
+ f.identity.uid = 999;
62
+ if (change === 'root')
63
+ f.runtime.uid = 0;
64
+ if (change === 'pid')
65
+ f.identity.pid = 0;
66
+ if (change === 'creation')
67
+ f.identity.startTicks = 'invalid';
68
+ if (change === 'command') {
69
+ f.identity.argv[0] = 'python';
70
+ f.observed.argv[0] = 'python';
71
+ }
72
+ if (change === 'relative-script') {
73
+ f.identity.argv[1] = '.bin/dharma';
74
+ f.observed.argv[1] = '.bin/dharma';
75
+ }
76
+ if (change === 'foreign-runtime')
77
+ f.runtime.executableMatches = async () => false;
78
+ if (change === 'foreign-artifact')
79
+ f.runtime.verifyRuntime = async () => { throw new Error('different-version-or-bytes'); };
80
+ if (change === 'unknown-script')
81
+ f.runtime.resolve = async (path) => path === '/proc/101/exe' ? f.node : '/foreign/dist/foreign.js';
82
+ if (change === 'pid-reuse')
83
+ f.runtime.identity = async () => ({ ...f.observed, startTicks: '999' });
84
+ if (change === 'script-drift') {
85
+ let scripts = 0;
86
+ const resolve = f.runtime.resolve;
87
+ f.runtime.resolve = async (path) => path === f.identity.argv[1] && ++scripts > 1 ? '/foreign/dist/bin.js' : resolve(path);
88
+ }
89
+ if (change === 'executable-drift') {
90
+ let executables = 0;
91
+ const resolve = f.runtime.resolve;
92
+ f.runtime.resolve = async (path) => path === '/proc/101/exe' && ++executables > 1 ? '/foreign/node' : resolve(path);
93
+ }
94
+ if (change === 'unavailable')
95
+ f.runtime.identity = async () => { throw new Error('process-unavailable'); };
96
+ assert.equal(await verifiedCliProcessArguments(f.identity, trustedIndex, f.runtime), null);
97
+ });
98
+ }
99
+ test('normalization preserves every action argument for the caller exact-tuple check', async () => {
100
+ const f = fixture();
101
+ f.identity.argv.push('--foreign');
102
+ f.observed.argv.push('--foreign');
103
+ const result = await verifiedCliProcessArguments(f.identity, trustedIndex, f.runtime);
104
+ assert.equal(result?.at(-1), '--foreign');
105
+ });
106
+ test('live Linux executable verification uses loaded bytes, not argv names', { skip: process.platform !== 'linux' }, async () => {
107
+ assert.equal(await matchingCliExecutable(process.pid, process.execPath, process.getuid()), true);
108
+ assert.equal(await matchingCliExecutable(process.pid, '/missing/node', process.getuid()).catch(() => false), false);
109
+ });
110
+ test('real npm-style child requires matching verifier bytes across independent package prefixes', { skip: process.platform !== 'linux' || process.getuid?.() === 0, timeout: 15_000 }, async (t) => {
111
+ const root = await mkdtemp(join(tmpdir(), 'dharma-cli-identity-'));
112
+ t.after(() => rm(root, { recursive: true, force: true }));
113
+ const caller = join(root, 'caller'), candidate = join(root, 'candidate');
114
+ const bin = "process.on('message',()=>process.exit(0));process.send('ready');\n";
115
+ for (const prefix of [caller, candidate]) {
116
+ await mkdir(join(prefix, 'dist'), { recursive: true });
117
+ await writeFile(join(prefix, 'package.json'), JSON.stringify({ name: '@dharma-ai-labs/agent-fabric', version: '0.0.0-test' }), { mode: 0o600 });
118
+ for (const [name, bytes] of Object.entries({ 'bin.js': bin, 'index.js': 'synthetic index',
119
+ 'containerRelayLifecycle.js': 'synthetic lifecycle', 'cliProcessIdentity.js': 'synthetic verifier' })) {
120
+ await writeFile(join(prefix, 'dist', name), bytes, { mode: 0o600 });
121
+ }
122
+ }
123
+ const alias = join(root, '.bin', 'dharma');
124
+ await mkdir(join(root, '.bin'));
125
+ await symlink(join(candidate, 'dist', 'bin.js'), alias);
126
+ const child = spawn(process.execPath, [alias, 'relay', 'supervise', '--policy', join(root, 'policy.json')], { argv0: 'node', stdio: ['ignore', 'ignore', 'ignore', 'ipc'] });
127
+ t.after(async () => {
128
+ if (child.exitCode === null && child.signalCode === null) {
129
+ const exited = once(child, 'exit');
130
+ child.send('stop');
131
+ await exited;
132
+ }
133
+ });
134
+ const [ready] = await once(child, 'message');
135
+ assert.equal(ready, 'ready');
136
+ const identity = await readContainerProcessIdentity(child.pid);
137
+ assert.equal(identity.argv[0], 'node');
138
+ assert.equal(identity.argv[1], alias);
139
+ const callerUrl = pathToFileURL(join(caller, 'dist', 'index.js')).href;
140
+ const expected = [process.execPath, join(caller, 'dist', 'bin.js'), ...identity.argv.slice(2)];
141
+ assert.deepEqual(await verifiedCliProcessArguments(identity, callerUrl), expected);
142
+ await writeFile(join(candidate, 'dist', 'cliProcessIdentity.js'), 'altered verifier', { mode: 0o600 });
143
+ assert.equal(await verifiedCliProcessArguments(identity, callerUrl), null);
144
+ await writeFile(join(candidate, 'dist', 'cliProcessIdentity.js'), 'synthetic verifier', { mode: 0o600 });
145
+ await chmod(join(candidate, 'dist', 'cliProcessIdentity.js'), 0o666);
146
+ assert.equal(await verifiedCliProcessArguments(identity, callerUrl), null);
147
+ await chmod(join(candidate, 'dist', 'cliProcessIdentity.js'), 0o600);
148
+ assert.deepEqual(await verifiedCliProcessArguments(identity, callerUrl), expected);
149
+ await writeFile(join(candidate, 'package.json'), JSON.stringify({ name: '@dharma-ai-labs/agent-fabric', version: '0.0.1-foreign' }));
150
+ assert.equal(await verifiedCliProcessArguments(identity, callerUrl), null);
151
+ });
152
+ //# sourceMappingURL=cliProcessIdentity.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cliProcessIdentity.test.js","sourceRoot":"","sources":["../src/cliProcessIdentity.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAC,KAAK,EAAC,MAAM,oBAAoB,CAAC;AACzC,OAAO,EAAC,IAAI,EAAC,MAAM,aAAa,CAAC;AACjC,OAAO,EAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAC,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EAAC,MAAM,EAAC,MAAM,SAAS,CAAC;AAC/B,OAAO,EAAC,IAAI,EAAE,OAAO,EAAC,MAAM,WAAW,CAAC;AACxC,OAAO,EAAC,aAAa,EAAC,MAAM,UAAU,CAAC;AACvC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAC,2BAA2B,EAAE,qBAAqB,EAAC,MAAM,yBAAyB,CAAC;AAC3F,OAAO,EAAC,4BAA4B,EAAgC,MAAM,8BAA8B,CAAC;AAEzG,MAAM,YAAY,GAAG,aAAa,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3E,SAAS,OAAO;IACd,MAAM,QAAQ,GAA6B,EAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG;QAC7E,cAAc,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK;QACtD,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,uCAAuC,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,CAAC,gCAAgC,CAAC,CAAC,EAAC,CAAC;IACjJ,MAAM,IAAI,GAAG,OAAO,CAAC,eAAe,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,qEAAqE,CAAC,CAAC;IAC5H,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG;QACd,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI;QACzB,QAAQ,EAAE,KAAK,EAAE,GAAW,EAAE,EAAE,GAAE,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAA,CAAC;QACrG,OAAO,EAAE,KAAK,EAAE,IAAY,EAAmB,EAAE;YAC/C,IAAI,IAAI,KAAK,eAAe,IAAI,IAAI,KAAK,IAAI;gBAAE,OAAO,IAAI,CAAC;YAC3D,IAAI,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;gBAAE,OAAO,GAAG,CAAC;YAC1C,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9C,CAAC;QACD,iBAAiB,EAAE,KAAK,EAAE,GAAW,EAAE,MAAc,EAAE,GAAW,EAAE,EAAE;YACpE,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAAA,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAAA,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAAA,OAAO,IAAI,CAAC;QACxF,CAAC;QACD,aAAa,EAAE,KAAK,EAAE,SAAiB,EAAE,MAAc,EAAE,EAAE;YACzD,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;YAAA,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC;YAAA,OAAO,SAAS,CAAC;QACtG,CAAC;KACF,CAAC;IACF,OAAO,EAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK,EAAC,CAAC;AACtE,CAAC;AAED,IAAI,CAAC,mFAAmF,EAAE,KAAK,IAAI,EAAE;IACnG,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;IACpB,MAAM,CAAC,SAAS,CAAC,MAAM,2BAA2B,CAAC,CAAC,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,EACrF,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,sBAAsB,CAAC,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1E,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;IAChF,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;IAAA,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IAAA,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IAC5E,MAAM,CAAC,EAAE,CAAC,MAAM,2BAA2B,CAAC,CAAC,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAClF,CAAC,CAAC,OAAO,CAAC,OAAO,GAAG,KAAK,EAAC,IAAI,EAAC,EAAE,CAAC,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC;IACtF,MAAM,CAAC,KAAK,CAAC,MAAM,2BAA2B,CAAC,CAAC,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;AAC7F,CAAC,CAAC,CAAC;AAEH,KAAK,MAAM,MAAM,IAAI,CAAC,KAAK,EAAC,MAAM,EAAC,KAAK,EAAC,UAAU,EAAC,SAAS,EAAC,iBAAiB,EAAC,iBAAiB,EAAC,kBAAkB;IAClH,gBAAgB,EAAC,WAAW,EAAC,cAAc,EAAC,kBAAkB,EAAC,aAAa,CAAU,EAAE,CAAC;IACzF,IAAI,CAAC,sCAAsC,MAAM,EAAE,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;QACpB,IAAI,MAAM,KAAK,KAAK;YAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,GAAG,GAAG,CAAC;QAC3C,IAAI,MAAM,KAAK,MAAM;YAAE,CAAC,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;QACzC,IAAI,MAAM,KAAK,KAAK;YAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAC;QACzC,IAAI,MAAM,KAAK,UAAU;YAAE,CAAC,CAAC,QAAQ,CAAC,UAAU,GAAG,SAAS,CAAC;QAC7D,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAAA,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC;YAAA,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC;QAAA,CAAC;QACxF,IAAI,MAAM,KAAK,iBAAiB,EAAE,CAAC;YAAA,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC;YAAA,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC;QAAA,CAAC;QAC1G,IAAI,MAAM,KAAK,iBAAiB;YAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,GAAG,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC;QAClF,IAAI,MAAM,KAAK,kBAAkB;YAAE,CAAC,CAAC,OAAO,CAAC,aAAa,GAAG,KAAK,IAAI,EAAE,GAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC,CAAA,CAAC,CAAC;QAC1H,IAAI,MAAM,KAAK,gBAAgB;YAAE,CAAC,CAAC,OAAO,CAAC,OAAO,GAAG,KAAK,EAAC,IAAI,EAAC,EAAE,CAAC,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,0BAA0B,CAAC;QAClI,IAAI,MAAM,KAAK,WAAW;YAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC,EAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAC,CAAC,CAAC;QAClG,IAAI,MAAM,KAAK,cAAc,EAAE,CAAC;YAC9B,IAAI,OAAO,GAAG,CAAC,CAAC;YAAA,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;YAClD,CAAC,CAAC,OAAO,CAAC,OAAO,GAAG,KAAK,EAAC,IAAI,EAAC,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1H,CAAC;QACD,IAAI,MAAM,KAAK,kBAAkB,EAAE,CAAC;YAClC,IAAI,WAAW,GAAG,CAAC,CAAC;YAAA,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;YACtD,CAAC,CAAC,OAAO,CAAC,OAAO,GAAG,KAAK,EAAC,IAAI,EAAC,EAAE,CAAC,IAAI,KAAK,eAAe,IAAI,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACpH,CAAC;QACD,IAAI,MAAM,KAAK,aAAa;YAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,GAAG,KAAK,IAAI,EAAE,GAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAA,CAAC,CAAC;QACzG,MAAM,CAAC,KAAK,CAAC,MAAM,2BAA2B,CAAC,CAAC,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;IAC7F,CAAC,CAAC,CAAC;AACL,CAAC;AAED,IAAI,CAAC,gFAAgF,EAAE,KAAK,IAAI,EAAE;IAChG,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;IAAA,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAAA,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACxF,MAAM,MAAM,GAAG,MAAM,2BAA2B,CAAC,CAAC,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;IACtF,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sEAAsE,EAAE,EAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAC,EAAE,KAAK,IAAI,EAAE;IAC5H,MAAM,CAAC,KAAK,CAAC,MAAM,qBAAqB,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAO,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IAClG,MAAM,CAAC,KAAK,CAAC,MAAM,qBAAqB,CAAC,OAAO,CAAC,GAAG,EAAE,eAAe,EAAE,OAAO,CAAC,MAAO,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;AACvH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2FAA2F,EAC9F,EAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,MAAM,EAAC,EAAE,KAAK,EAAC,CAAC,EAAC,EAAE;IAC3F,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,sBAAsB,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,EAAC,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACzE,MAAM,GAAG,GAAG,oEAAoE,CAAC;IACjF,KAAK,MAAM,MAAM,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC;QACzC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAC,SAAS,EAAE,IAAI,EAAC,CAAC,CAAC;QACrD,MAAM,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAC,IAAI,EAAE,8BAA8B,EAAE,OAAO,EAAE,YAAY,EAAC,CAAC,EAAE,EAAC,IAAI,EAAE,KAAK,EAAC,CAAC,CAAC;QAC5I,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,EAAC,QAAQ,EAAE,GAAG,EAAE,UAAU,EAAE,iBAAiB;YACtF,4BAA4B,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,oBAAoB,EAAC,CAAC,EAAE,CAAC;YACvG,MAAM,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,KAAK,EAAE,EAAC,IAAI,EAAE,KAAK,EAAC,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC3C,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,MAAM,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAC;IACzF,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,EACxG,EAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAC,CAAC,CAAC;IACjE,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE;QACjB,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YACzD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAAC,MAAM,MAAM,CAAC;QACvE,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC3E,MAAM,QAAQ,GAAG,MAAM,4BAA4B,CAAC,KAAK,CAAC,GAAI,CAAC,CAAC;IAChE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC9E,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;IACvE,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/F,MAAM,CAAC,SAAS,CAAC,MAAM,2BAA2B,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC;IACnF,MAAM,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,uBAAuB,CAAC,EAAE,kBAAkB,EAAE,EAAC,IAAI,EAAE,KAAK,EAAC,CAAC,CAAC;IACrG,MAAM,CAAC,KAAK,CAAC,MAAM,2BAA2B,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3E,MAAM,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,uBAAuB,CAAC,EAAE,oBAAoB,EAAE,EAAC,IAAI,EAAE,KAAK,EAAC,CAAC,CAAC;IACvG,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,uBAAuB,CAAC,EAAE,KAAK,CAAC,CAAC;IACrE,MAAM,CAAC,KAAK,CAAC,MAAM,2BAA2B,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3E,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,uBAAuB,CAAC,EAAE,KAAK,CAAC,CAAC;IACrE,MAAM,CAAC,SAAS,CAAC,MAAM,2BAA2B,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC;IACnF,MAAM,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAC,IAAI,EAAE,8BAA8B,EAAE,OAAO,EAAE,eAAe,EAAC,CAAC,CAAC,CAAC;IACnI,MAAM,CAAC,KAAK,CAAC,MAAM,2BAA2B,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;AAC7E,CAAC,CAAC,CAAC"}
@@ -97,6 +97,7 @@ async function fixture(t) {
97
97
  [resolve(home, 'relay', 'repositories', workspaceId, 'last-successful-poll.json'), poll],
98
98
  ]);
99
99
  const calls = [], mutations = {};
100
+ const canonicalProcesses = new Map();
100
101
  const dependencies = {
101
102
  resolve, Date, canonicalize, sha256, codexSetupSessionPolicyHash, selectDeviceWorkspace, parseLocalCodexSetupReadiness, parseNamedCodexSkillObservation,
102
103
  isNamedSessionOwnerReceipt, repositoryRelayObservationReady, discoverRepositoryRoleMetadata,
@@ -131,6 +132,7 @@ async function fixture(t) {
131
132
  mutations.process?.();
132
133
  return structuredClone(processes.find(p => p.pid === pid));
133
134
  },
135
+ verifiedCliProcessArguments: async (identity) => canonicalProcesses.has(identity.pid) ? canonicalProcesses.get(identity.pid) : identity.argv,
134
136
  readBootstrapRuntimeJson: async (path, scope) => scope.step(async () => {
135
137
  calls.push('owned-state');
136
138
  if (!paths.has(path))
@@ -151,7 +153,7 @@ async function fixture(t) {
151
153
  return handoffEnabled ? handoff : null;
152
154
  } };
153
155
  return { prepared, vault, observe, providerVault, calls, paths, mutations, row, profile, binding, native, remote,
154
- content, startup, startupState, policy, supervisor, poll, processes, dependencies, handoff,
156
+ content, startup, startupState, policy, supervisor, poll, processes, canonicalProcesses, dependencies, handoff,
155
157
  enableHandoff: () => { handoffEnabled = true; processes[2].argv.push('--setup-handoff'); } };
156
158
  }
157
159
  async function signedReadinessFixture(f) {
@@ -245,7 +247,25 @@ for (const ipc of [false, true]) {
245
247
  const result = await f.observe(f.prepared, f.providerVault, 'no_eligible_history', ipc ? hash : undefined);
246
248
  assert.equal(result.sessionId, f.binding.sessionId);
247
249
  });
250
+ test(`actual completion accepts verified npm aliases with IPC=${ipc}`, async (t) => {
251
+ const f = await fixture(t);
252
+ f.processes[0].argv[0] = 'node';
253
+ f.processes[0].argv[1] = resolve(f.row.path, 'npm-cache', 'node_modules', '.bin', 'dharma');
254
+ f.canonicalProcesses.set(101, ['synthetic-node', resolve(f.row.path, 'bin.js'), ...f.processes[0].argv.slice(2)]);
255
+ if (ipc)
256
+ f.enableHandoff();
257
+ const result = await f.observe(f.prepared, f.providerVault, 'no_eligible_history', ipc ? hash : undefined);
258
+ assert.equal(result.sessionId, f.binding.sessionId);
259
+ });
248
260
  }
261
+ test('actual completion rejects an npm alias without verified executable and package identity', async (t) => {
262
+ const f = await fixture(t);
263
+ f.enableHandoff();
264
+ f.processes[0].argv[0] = 'node';
265
+ f.processes[0].argv[1] = resolve(f.row.path, 'npm-cache', 'node_modules', '.bin', 'dharma');
266
+ f.canonicalProcesses.set(101, null);
267
+ await assert.rejects(f.observe(f.prepared, f.providerVault, 'no_eligible_history', hash), { message: 'setup_runtime_process_unconfirmed' });
268
+ });
249
269
  for (const change of ['foreign-supervisor', 'relay-bin', 'session-bin', 'runtime', 'uid', 'policy', 'extra-args']) {
250
270
  test(`official bin completion still rejects ${change}`, async (t) => {
251
271
  const f = await fixture(t);