deadbyte-mcp 0.14.0 → 0.15.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/AGENTS.md +11 -18
- package/CONTEXT.md +3 -3
- package/MANIFEST.SHA256 +104 -53
- package/R43-PARENT.json +20 -0
- package/R44-DONORS.json +99 -0
- package/R44-PARENT.json +18 -0
- package/README.txt +7 -2
- package/controller/README.TXT +4 -5
- package/controller/deadbyte-controller.ps1 +1 -1
- package/docs/ARCHITECTURE.md +8 -1
- package/docs/R44-ARCHITECTURE.md +37 -0
- package/package.json +13 -8
- package/scripts/deferred-slot-operation-r43.mjs +126 -0
- package/scripts/deferred-slot-operation-r44.mjs +126 -0
- package/scripts/final-closure-r43.mjs +115 -0
- package/scripts/final-closure-r44.mjs +121 -0
- package/scripts/final-closure-verify-r43.mjs +213 -0
- package/scripts/final-closure-verify-r44.mjs +220 -0
- package/scripts/finalize-r43.mjs +217 -0
- package/scripts/finalize-r44.mjs +217 -0
- package/scripts/full-regression-r43.mjs +20 -0
- package/scripts/full-regression-r44.mjs +20 -0
- package/scripts/gate-windows-r43.ps1 +36 -0
- package/scripts/gate-windows-r44.ps1 +36 -0
- package/scripts/release-parity-r43.mjs +218 -0
- package/scripts/release-parity-r44.mjs +242 -0
- package/scripts/release-parity-tests-r43.ps1 +8 -0
- package/scripts/release-parity-tests-r44.ps1 +8 -0
- package/scripts/verify-r43-parent.mjs +126 -0
- package/scripts/verify-r44-parent.mjs +142 -0
- package/scripts/windows-gate-evidence-r43.mjs +55 -0
- package/scripts/windows-gate-evidence-r44.mjs +55 -0
- package/src/agent-backend-r44.mjs +254 -0
- package/src/autonomous-output-contracts.mjs +1 -1
- package/src/autonomous-policy.mjs +41 -4
- package/src/autonomous-runtime.mjs +25 -12
- package/src/autonomous-supervisor-host.mjs +12 -2
- package/src/capability-ledger.mjs +2 -1
- package/src/codex-app-server-r44.mjs +154 -0
- package/src/coding-plane.mjs +66 -19
- package/src/durable-job-r44.mjs +145 -0
- package/src/external-job-provider-r44.mjs +126 -0
- package/src/host-plane.mjs +27 -6
- package/src/job-supervisor-host-r44.mjs +112 -0
- package/src/machine-fs-smoke-client.mjs +1 -1
- package/src/machine-fs.mjs +21 -10
- package/src/mcp-host-smoke-client.mjs +1 -1
- package/src/mcp-server.mjs +20 -3
- package/src/mcp-smoke-client.mjs +1 -1
- package/src/native-diff-v2.mjs +3 -3
- package/src/native-diff.mjs +3 -2
- package/src/observation-smoke-client.mjs +1 -1
- package/src/operator-surface.mjs +2 -2
- package/src/process-policy.mjs +19 -0
- package/src/process-runtime.mjs +5 -2
- package/src/process-smoke-client.mjs +2 -2
- package/src/provider-circuit.mjs +8 -2
- package/src/r44-compact-disarmed-probe.mjs +74 -0
- package/src/r44-full-surface-probe.mjs +34 -0
- package/src/remote-device-r44.mjs +205 -0
- package/src/remote-mcp-autonomous-smoke-client.mjs +4 -4
- package/src/remote-mcp-r44-parity-client.mjs +203 -0
- package/src/remote-mcp-smoke-client.mjs +7 -4
- package/src/runtime-update.mjs +64 -31
- package/src/skill-mcp-tools-r44.mjs +37 -0
- package/src/skill-plane-r44.mjs +203 -0
- package/src/tunnel-stdio-compat-smoke.mjs +1 -1
- package/src/version.mjs +1 -1
- package/test/agent-backend-r44.test.mjs +115 -0
- package/test/agent-runtime.test.mjs +3 -3
- package/test/autonomous-loop.test.mjs +97 -11
- package/test/autonomous-runtime.test.mjs +2 -2
- package/test/capability-ledger-r36.test.mjs +3 -3
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/codex-app-server-r44.test.mjs +115 -0
- package/test/codex-autonomous-r44.test.mjs +87 -0
- package/test/coding-plane.test.mjs +2 -2
- package/test/coding-runtime.test.mjs +36 -1
- package/test/compact-catalog-r36.test.mjs +3 -3
- package/test/compact-max-catalog-r40.test.mjs +4 -2
- package/test/compact-max-catalog-r44.test.mjs +97 -0
- package/test/core.test.mjs +1 -1
- package/test/durable-job-r44.test.mjs +75 -0
- package/test/external-job-provider-r44.test.mjs +46 -0
- package/test/full-max-catalog-r40.test.mjs +5 -3
- package/test/full-max-catalog-r44.test.mjs +91 -0
- package/test/host-plane.test.mjs +15 -1
- package/test/native-diff.test.mjs +28 -0
- package/test/output-schema.test.mjs +2 -2
- package/test/process-policy.test.mjs +22 -1
- package/test/prompts-resources.test.mjs +1 -1
- package/test/r33-closeout-regression.test.mjs +13 -30
- package/test/r33-finalization.test.mjs +7 -7
- package/test/r43-finalization.test.mjs +31 -0
- package/test/r43-finalizer.test.mjs +34 -0
- package/test/r43-parent.test.mjs +29 -0
- package/test/r44-finalization.test.mjs +58 -0
- package/test/r44-finalizer.test.mjs +48 -0
- package/test/r44-parent.test.mjs +25 -0
- package/test/release-version.test.mjs +53 -19
- package/test/remote-device-r44.test.mjs +104 -0
- package/test/runtime-self-update.test.mjs +42 -0
- package/test/skill-mcp-r44.test.mjs +38 -0
- package/test/skill-plane-r44.test.mjs +93 -0
- package/test/standing-grants.test.mjs +5 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { validateFinalizerAuthorityStatus, validateWindowsGateEvidence } from '../scripts/finalize-r44.mjs';
|
|
4
|
+
import { windowsPowerShellEnvironment } from '../src/runtime-update.mjs';
|
|
5
|
+
|
|
6
|
+
const manifest='a'.repeat(64);
|
|
7
|
+
const parent={
|
|
8
|
+
release_id:'v0.14.1-0a5a833892593283',
|
|
9
|
+
manifest_sha256:'0a5a8338925932830986421dda60ec5643f62f600404de4992fda2a5e79d09a9',
|
|
10
|
+
generation_manifest_sha256:'fcfcb5a47d55039b7bb5d42faa0739b57b13d8a420d4418ae09d96d2862967cb',
|
|
11
|
+
closure_sha256:'2f29e94dd726638c23b2e520a608dcf2a6919e43348855be3940599d557f920b'
|
|
12
|
+
};
|
|
13
|
+
const features=()=>({skill_integrity:true,agent_backend_persistence:true,codex_protocol_contract:true,
|
|
14
|
+
remote_device_identity:true,durable_job_supervisor:true,real_external_supervisor_smoke:true,full_regression_required:true,
|
|
15
|
+
codex_live_binary_required:false});
|
|
16
|
+
const valid=()=>({schema:'deadbyte.r44-windows-gate.v1',status:'passed',exit_code:0,version:'0.15.0',
|
|
17
|
+
marker:'DEADBYTE V0.15.0 / R44 REMOTE AGENT SKILL WINDOWS BATCH GATE: PASS',manifest_sha256:manifest,
|
|
18
|
+
parent:{release_id:parent.release_id,manifest_sha256:parent.manifest_sha256,
|
|
19
|
+
generation_manifest_sha256:parent.generation_manifest_sha256,final_closure_sha256:parent.closure_sha256},
|
|
20
|
+
features:features()});
|
|
21
|
+
|
|
22
|
+
test('R44 detached finalizer requires Process+Coding+Autonomous and Desktop+Host DISARMED',()=>{
|
|
23
|
+
assert.equal(validateFinalizerAuthorityStatus({process_armed:true,coding_armed:true,autonomous_armed:true,desktop_armed:false,host_armed:false}),true);
|
|
24
|
+
assert.throws(()=>validateFinalizerAuthorityStatus({process_armed:false,coding_armed:true,autonomous_armed:true,desktop_armed:false,host_armed:false}),/Process standing grant/);
|
|
25
|
+
assert.throws(()=>validateFinalizerAuthorityStatus({process_armed:true,coding_armed:true,autonomous_armed:true,desktop_armed:true,host_armed:false}),/Desktop DISARMED/);
|
|
26
|
+
assert.throws(()=>validateFinalizerAuthorityStatus({process_armed:true,coding_armed:true,autonomous_armed:true,desktop_armed:false,host_armed:true}),/Host DISARMED/);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('R44 detached finalizer accepts only exact signed-R43-parent feature gate evidence',()=>{
|
|
30
|
+
assert.equal(validateWindowsGateEvidence(valid(),manifest,parent),true);
|
|
31
|
+
assert.throws(()=>validateWindowsGateEvidence({...valid(),manifest_sha256:'b'.repeat(64)},manifest,parent),/candidate mismatch/);
|
|
32
|
+
assert.throws(()=>validateWindowsGateEvidence({...valid(),parent:{...valid().parent,release_id:'v0.14.1-bad'}},manifest,parent),/parent mismatch/);
|
|
33
|
+
assert.throws(()=>validateWindowsGateEvidence({...valid(),parent:{...valid().parent,final_closure_sha256:'b'.repeat(64)}},manifest,parent),/parent/);
|
|
34
|
+
assert.throws(()=>validateWindowsGateEvidence({...valid(),features:{...features(),skill_integrity:false}},manifest,parent),/skill_integrity/);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('R44 detached finalizer isolates Windows PowerShell modules from the parent runtime',()=>{
|
|
38
|
+
const env=windowsPowerShellEnvironment({
|
|
39
|
+
SystemRoot:'C:\\Windows',USERPROFILE:'C:\\Users\\operator',ProgramFiles:'C:\\Program Files',
|
|
40
|
+
PSModulePath:'C:\\poisoned-runtime-modules'
|
|
41
|
+
});
|
|
42
|
+
assert.equal(env.PSModulePath,[
|
|
43
|
+
'C:\\Users\\operator\\Documents\\WindowsPowerShell\\Modules',
|
|
44
|
+
'C:\\Program Files\\WindowsPowerShell\\Modules',
|
|
45
|
+
'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\Modules'
|
|
46
|
+
].join(';'));
|
|
47
|
+
assert.doesNotMatch(env.PSModulePath,/poisoned-runtime-modules/);
|
|
48
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { verifyR44Parent } from '../scripts/verify-r44-parent.mjs';
|
|
6
|
+
|
|
7
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const root=path.resolve(here,'..');
|
|
9
|
+
|
|
10
|
+
test('R44 parent is exact sealed R43 final baseline',async()=>{
|
|
11
|
+
const result=await verifyR44Parent(path.join(root,'R44-PARENT.json'));
|
|
12
|
+
assert.equal(result.ok,true);
|
|
13
|
+
assert.equal(result.release_id,'v0.14.1-0a5a833892593283');
|
|
14
|
+
assert.equal(result.manifest_sha256,'0a5a8338925932830986421dda60ec5643f62f600404de4992fda2a5e79d09a9');
|
|
15
|
+
assert.equal(result.generation_manifest_sha256,'fcfcb5a47d55039b7bb5d42faa0739b57b13d8a420d4418ae09d96d2862967cb');
|
|
16
|
+
assert.equal(result.release_files,1834);
|
|
17
|
+
assert.equal(result.generation_files,7);
|
|
18
|
+
assert.equal(result.closure_sha256,'2f29e94dd726638c23b2e520a608dcf2a6919e43348855be3940599d557f920b');
|
|
19
|
+
assert.equal(result.recorded_all_disarmed,true);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('R44 parent proof is historical and does not require current grants to remain absent',async()=>{
|
|
23
|
+
const result=await verifyR44Parent(path.join(root,'R44-PARENT.json'));
|
|
24
|
+
assert.equal(result.proof_class,'immutable-slot+generation+signed-final-closure');
|
|
25
|
+
});
|
|
@@ -3,23 +3,57 @@ import assert from 'node:assert/strict';
|
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
5
|
const root=new URL('../',import.meta.url);const text=rel=>readFile(new URL(rel,root),'utf8');
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
6
|
+
const PARENT_RELEASE='v0.14.1-0a5a833892593283';
|
|
7
|
+
const PARENT_MANIFEST='0a5a8338925932830986421dda60ec5643f62f600404de4992fda2a5e79d09a9';
|
|
8
|
+
const PARENT_CLOSURE='2f29e94dd726638c23b2e520a608dcf2a6919e43348855be3940599d557f920b';
|
|
9
|
+
|
|
10
|
+
test('R44 current authority agrees on V0.15.0 and preserves exact sealed R43 parent',async()=>{
|
|
11
|
+
const pkg=JSON.parse(await text('package.json'));
|
|
12
|
+
const [version,controller,gate,readme,architecture,controllerReadme,deferred,parity,closure,closureVerify,windowsGate,parent,r43Gate,r43Parity,r43Closure]=await Promise.all([
|
|
13
|
+
text('src/version.mjs'),text('controller/deadbyte-controller.ps1'),text('scripts/gate-windows-r44.ps1'),text('README.txt'),
|
|
14
|
+
text('docs/ARCHITECTURE.md'),text('controller/README.TXT'),text('scripts/deferred-slot-operation-r44.mjs'),
|
|
15
|
+
text('scripts/release-parity-r44.mjs'),text('scripts/final-closure-r44.mjs'),text('scripts/final-closure-verify-r44.mjs'),
|
|
16
|
+
text('scripts/windows-gate-evidence-r44.mjs'),text('R44-PARENT.json'),
|
|
17
|
+
text('scripts/gate-windows-r43.ps1'),text('scripts/release-parity-r43.mjs'),text('scripts/final-closure-r43.mjs')
|
|
18
|
+
]);
|
|
19
|
+
assert.equal(pkg.version,'0.15.0');
|
|
20
|
+
assert.equal(pkg.scripts['gate:windows'],'powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-r44.ps1');
|
|
21
|
+
assert.equal(pkg.scripts['r44:parent:verify'],'node scripts/verify-r44-parent.mjs');
|
|
22
|
+
assert.equal(pkg.scripts['r44:full:test'],'node scripts/full-regression-r44.mjs');
|
|
23
|
+
assert.equal(pkg.scripts['self-update:deferred'],'node scripts/deferred-slot-operation-r44.mjs');
|
|
24
|
+
assert.equal(pkg.scripts['release:parity'],'node scripts/release-parity-r44.mjs');
|
|
25
|
+
assert.equal(pkg.scripts['release:final:write'],'node scripts/final-closure-r44.mjs');
|
|
26
|
+
assert.equal(pkg.scripts['release:final:verify'],'node scripts/final-closure-verify-r44.mjs');
|
|
27
|
+
assert.equal(pkg.scripts['finalize:r44'],'node scripts/finalize-r44.mjs');
|
|
28
|
+
assert.match(version,/DEADBYTE_VERSION = '0\.15\.0'/);
|
|
29
|
+
assert.match(controller,/CONTROLLER V0\.15\.0 \/ R44/);
|
|
30
|
+
assert.match(gate,/DEADBYTE V0\.15\.0 \/ R44 REMOTE AGENT SKILL WINDOWS BATCH GATE: PASS/);
|
|
31
|
+
assert.match(readme,/^DEADBYTE MCP V0\.15\.0 \/ R44 REMOTE AGENT \+ SKILL BASELINE$/m);
|
|
32
|
+
assert.match(architecture,/^# DEADBYTE MCP V0\.15\.0 \/ R44 ARCHITECTURE$/m);
|
|
33
|
+
assert.match(controllerReadme,/^DEADBYTE MCP V0\.15\.0 \/ R44 CONTROLLER$/m);
|
|
34
|
+
assert.match(deferred,/--expected-version','0\.15\.0'/);
|
|
35
|
+
assert.match(parity,/R44_PREDECESSOR_RELEASE_ID='v0\.14\.1-0a5a833892593283'/);assert.match(parity,new RegExp(PARENT_MANIFEST));
|
|
36
|
+
assert.match(parity,/tool_count===81/);assert.match(parity,/tool_count===43/);
|
|
37
|
+
assert.match(closure,/version:'0\.15\.0'/);assert.match(closure,/DEADBYTE-R44-FINAL-CLOSURE-V1/);
|
|
38
|
+
assert.match(closureVerify,/R44_FULL_TOOL_COUNT=81/);assert.match(closureVerify,/R44_COMPACT_TOOL_COUNT=43/);
|
|
39
|
+
assert.match(windowsGate,/deadbyte\.r44-windows-gate\.v1/);
|
|
40
|
+
const parentRecord=JSON.parse(parent);
|
|
41
|
+
assert.equal(parentRecord.parent_release_id,PARENT_RELEASE);assert.equal(parentRecord.parent_manifest_sha256,PARENT_MANIFEST);
|
|
42
|
+
assert.equal(parentRecord.parent_final_closure_sha256,PARENT_CLOSURE);
|
|
43
|
+
assert.match(r43Gate,/DEADBYTE V0\.14\.1 \/ R43 AUTHORITY HARDENING WINDOWS BATCH GATE: PASS/);
|
|
44
|
+
assert.match(r43Parity,/deadbyte\.r43-parity\.v1/);assert.match(r43Closure,/version:'0\.14\.1'/);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('R44 keeps ONLINE reuse and explicit Desktop authority',async()=>{
|
|
48
|
+
const [cli,controller]=await Promise.all([text('src/deadbyte-cli.mjs'),text('controller/deadbyte-controller.ps1')]);
|
|
49
|
+
assert.match(cli,/controllerRuntimeReady/);assert.match(cli,/Runtime already ONLINE; reusing verified bridge\/cloud\/supervisor/);
|
|
50
|
+
const start=controller.indexOf('function Recover-AutonomousSupervisor'),end=controller.indexOf('function Show-State',start);
|
|
51
|
+
assert.ok(start>=0&&end>start);const block=controller.slice(start,end);
|
|
52
|
+
assert.doesNotMatch(block,/\$host\s*=/i);assert.match(block,/\$hostStatus\s*=\s*Get-HostArmStatus/);
|
|
53
|
+
assert.match(controller,/armdesktop/);assert.match(controller,/disarmdesktop/);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('semantic config remains byte-pinned across R44 feature work',async()=>{
|
|
57
|
+
const semantic=await text('controller/deadbyte-semantic-lsp-config.json');
|
|
58
|
+
assert.equal(createHash('sha256').update(Buffer.from(semantic,'utf8')).digest('hex'),'d274b099a298c1033fea8f1e8849751693bbc0448778d619400acb82d4f53a3b');
|
|
22
59
|
});
|
|
23
|
-
test('R40 keeps ONLINE reuse and PowerShell Host collision fix while Desktop stays explicit',async()=>{const [cli,controller]=await Promise.all([text('src/deadbyte-cli.mjs'),text('controller/deadbyte-controller.ps1')]);assert.match(cli,/controllerRuntimeReady/);assert.match(cli,/Runtime already ONLINE; reusing verified bridge\/cloud\/supervisor/);const start=controller.indexOf('function Recover-AutonomousSupervisor'),end=controller.indexOf('function Show-State',start);assert.ok(start>=0&&end>start);const block=controller.slice(start,end);assert.doesNotMatch(block,/\$host\s*=/i);assert.match(block,/\$hostStatus\s*=\s*Get-HostArmStatus/);assert.match(controller,/armdesktop/);assert.match(controller,/disarmdesktop/);});
|
|
24
|
-
test('sealed R39 tooling remains historical 0.11.3 authority with exact 0.11.2 predecessor',async()=>{const [parity,gate,closure]=await Promise.all([text('scripts/release-parity-r39.mjs'),text('scripts/gate-windows-r39.ps1'),text('scripts/final-closure-r39.mjs')]);assert.match(parity,/R39_PREDECESSOR_RELEASE_ID='v0\.11\.2-058605dfccaa3035'/);assert.match(parity,/version==='0\.11\.3'|version:'0\.11\.3'/);assert.match(gate,/DEADBYTE V0\.11\.3 \/ R39 WINDOWS BATCH GATE: PASS/);assert.match(closure,/version:'0\.11\.3'/);});
|
|
25
|
-
test('sealed R37 tooling remains historical 0.11.1 authority',async()=>{const [parity,gate,closure]=await Promise.all([text('scripts/release-parity.mjs'),text('scripts/gate-windows.ps1'),text('scripts/final-closure.mjs')]);assert.match(parity,/R37_PREDECESSOR_RELEASE_ID='v0\.11\.0-08c2a0008b1f84c9'/);assert.match(parity,/version==='0\.11\.1'|version:'0\.11\.1'/);assert.match(gate,/DEADBYTE V0\.11\.1 \/ R37 WINDOWS BATCH GATE: PASS/);assert.match(closure,/version:'0\.11\.1'/);});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { generateKeyPairSync, createHash } from 'node:crypto';
|
|
4
|
+
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { createRemoteDevicePlaneR44 } from '../src/remote-device-r44.mjs';
|
|
8
|
+
|
|
9
|
+
const sha=b=>createHash('sha256').update(b).digest('hex');
|
|
10
|
+
function deviceKey(){
|
|
11
|
+
const {publicKey}=generateKeyPairSync('ed25519');
|
|
12
|
+
const der=Buffer.from(publicKey.export({type:'spki',format:'der'}));
|
|
13
|
+
return {spki:der.toString('base64'),deviceId:sha(der)};
|
|
14
|
+
}
|
|
15
|
+
class FakeRelay{
|
|
16
|
+
constructor(){this.probes=[];this.requests=[];this.drop=false;}
|
|
17
|
+
async probe(value){this.probes.push(structuredClone(value));return {device_id:value.device_id,epoch:value.epoch,nonce:value.nonce,ready:true};}
|
|
18
|
+
async request(value){this.requests.push(structuredClone(value));if(this.drop)return null;return {ack:'accepted',request_id:value.request_id,terminal:{status:'completed',result_sha256:'a'.repeat(64)}};}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
test('R44 remote device reported online is not reachable until identity-bound probe ACK',async()=>{
|
|
22
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-remote-'));
|
|
23
|
+
const relay=new FakeRelay(), key=deviceKey();
|
|
24
|
+
try{
|
|
25
|
+
const plane=await createRemoteDevicePlaneR44({stateDir:root,relay,authorizeCapability:async()=>true});
|
|
26
|
+
const paired=await plane.pair({publicKeySpkiBase64:key.spki,displayName:'lab'});
|
|
27
|
+
assert.equal(paired.device_id,key.deviceId);assert.equal(paired.epoch,1);
|
|
28
|
+
await plane.reportOnline({deviceId:key.deviceId,epoch:1,connectionId:'conn-1'});
|
|
29
|
+
assert.equal((await plane.status({deviceId:key.deviceId})).reachable,false);
|
|
30
|
+
await assert.rejects(plane.dispatch({deviceId:key.deviceId,epoch:1,capabilityId:'coding.search',payload:{q:'x'},requestId:'1'.repeat(64)}),/not reachable/);
|
|
31
|
+
const ready=await plane.probe({deviceId:key.deviceId,epoch:1});
|
|
32
|
+
assert.equal(ready.reachable,true);assert.equal(relay.probes.length,1);
|
|
33
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('R44 remote dispatch is durable-before-send, replay-idempotent and silent drop is terminal',async()=>{
|
|
37
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-remote-dispatch-'));
|
|
38
|
+
const relay=new FakeRelay(), key=deviceKey();
|
|
39
|
+
try{
|
|
40
|
+
const plane=await createRemoteDevicePlaneR44({stateDir:root,relay,authorizeCapability:async()=>true});
|
|
41
|
+
await plane.pair({publicKeySpkiBase64:key.spki,displayName:'lab'});
|
|
42
|
+
await plane.reportOnline({deviceId:key.deviceId,epoch:1,connectionId:'c'});
|
|
43
|
+
await plane.probe({deviceId:key.deviceId,epoch:1});
|
|
44
|
+
const requestId='2'.repeat(64), payload={path:'README.md'};
|
|
45
|
+
const done=await plane.dispatch({deviceId:key.deviceId,epoch:1,capabilityId:'coding.file_read',payload,requestId});
|
|
46
|
+
assert.equal(done.state,'completed');assert.equal(relay.requests.length,1);
|
|
47
|
+
const stored=JSON.parse(await readFile(path.join(root,'remote-devices.json'),'utf8'));
|
|
48
|
+
assert.equal(stored.requests[requestId].state,'completed');
|
|
49
|
+
const replay=await plane.dispatch({deviceId:key.deviceId,epoch:1,capabilityId:'coding.file_read',payload,requestId});
|
|
50
|
+
assert.equal(replay.replay,true);assert.equal(relay.requests.length,1);
|
|
51
|
+
await assert.rejects(plane.dispatch({deviceId:key.deviceId,epoch:1,capabilityId:'coding.file_read',payload:{path:'other'},requestId}),/request id conflict/);
|
|
52
|
+
|
|
53
|
+
relay.drop=true;
|
|
54
|
+
const droppedId='3'.repeat(64);
|
|
55
|
+
const dropped=await plane.dispatch({deviceId:key.deviceId,epoch:1,capabilityId:'coding.file_read',payload,requestId:droppedId});
|
|
56
|
+
assert.equal(dropped.state,'failed');assert.equal(dropped.error_code,'transport_no_ack');
|
|
57
|
+
const after=JSON.parse(await readFile(path.join(root,'remote-devices.json'),'utf8'));
|
|
58
|
+
assert.equal(after.requests[droppedId].state,'failed');
|
|
59
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('R44 remote routing requires local capability authorization and revoke invalidates epoch',async()=>{
|
|
63
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-remote-auth-'));
|
|
64
|
+
const relay=new FakeRelay(), key=deviceKey();
|
|
65
|
+
try{
|
|
66
|
+
const plane=await createRemoteDevicePlaneR44({stateDir:root,relay,authorizeCapability:async({capabilityId})=>capabilityId==='machine.status'});
|
|
67
|
+
await plane.pair({publicKeySpkiBase64:key.spki,displayName:'lab'});
|
|
68
|
+
await plane.reportOnline({deviceId:key.deviceId,epoch:1,connectionId:'c'});await plane.probe({deviceId:key.deviceId,epoch:1});
|
|
69
|
+
await assert.rejects(plane.dispatch({deviceId:key.deviceId,epoch:1,capabilityId:'host.exec',payload:{},requestId:'4'.repeat(64)}),/capability denied/);
|
|
70
|
+
assert.equal(relay.requests.length,0);
|
|
71
|
+
const revoked=await plane.revoke({deviceId:key.deviceId,epoch:1});
|
|
72
|
+
assert.equal(revoked.revoked,true);
|
|
73
|
+
await assert.rejects(plane.reportOnline({deviceId:key.deviceId,epoch:1,connectionId:'new'}),/revoked|epoch/i);
|
|
74
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('R44 credential rotation commits encrypted replacement before supersede callback',async()=>{
|
|
78
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-remote-rotate-'));
|
|
79
|
+
const relay=new FakeRelay(), key=deviceKey(), observed=[];
|
|
80
|
+
try{
|
|
81
|
+
const plane=await createRemoteDevicePlaneR44({stateDir:root,relay,authorizeCapability:async()=>true,
|
|
82
|
+
onCredentialSuperseded:async info=>{
|
|
83
|
+
const current=JSON.parse(await readFile(path.join(root,'credentials',`${key.deviceId}.json`),'utf8'));
|
|
84
|
+
observed.push({info,current});
|
|
85
|
+
}});
|
|
86
|
+
await plane.pair({publicKeySpkiBase64:key.spki,displayName:'lab'});
|
|
87
|
+
const one=Buffer.from('encrypted-token-one').toString('base64');
|
|
88
|
+
const first=await plane.rotateCredential({deviceId:key.deviceId,epoch:1,encryptedBundleBase64:one,expectedPreviousSha256:null});
|
|
89
|
+
const two=Buffer.from('encrypted-token-two').toString('base64');
|
|
90
|
+
const second=await plane.rotateCredential({deviceId:key.deviceId,epoch:1,encryptedBundleBase64:two,expectedPreviousSha256:first.credential_sha256});
|
|
91
|
+
assert.notEqual(second.credential_sha256,first.credential_sha256);
|
|
92
|
+
assert.equal(observed.length,1);
|
|
93
|
+
assert.equal(observed[0].current.credential_sha256,second.credential_sha256,'new credential must be durable before supersede');
|
|
94
|
+
await assert.rejects(plane.rotateCredential({deviceId:key.deviceId,epoch:1,encryptedBundleBase64:one,expectedPreviousSha256:first.credential_sha256}),/precondition/);
|
|
95
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('R44 remote state corruption fails closed',async()=>{
|
|
99
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-remote-corrupt-'));
|
|
100
|
+
try{
|
|
101
|
+
await writeFile(path.join(root,'remote-devices.json'),'{"schema":','utf8');
|
|
102
|
+
await assert.rejects(createRemoteDevicePlaneR44({stateDir:root,relay:new FakeRelay(),authorizeCapability:async()=>true}),/state JSON invalid/);
|
|
103
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
104
|
+
});
|
|
@@ -53,6 +53,29 @@ test('R27 slot-update CLI requires exact stage/development/version/manifest bind
|
|
|
53
53
|
assert.ok(path.isAbsolute(parsed.developmentRoot));
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
+
test('R43 slot authority restore plan carries prior Process grant without restoring Desktop or Host', () => {
|
|
57
|
+
assert.equal(typeof runtimeUpdate.standingAuthorityRestorePlan,'function');
|
|
58
|
+
const parsed=runtimeUpdate.parseControllerStatus([
|
|
59
|
+
'DEADBYTE MCP = ONLINE',
|
|
60
|
+
'Process plane : ARMED [standing-grant]',
|
|
61
|
+
'Coding plane : ARMED [standing-grant]',
|
|
62
|
+
'Autonomous : ARMED [standing-grant]',
|
|
63
|
+
'Desktop plane : ARMED [standing-grant]',
|
|
64
|
+
'Host plane : DISARMED [disarmed]'
|
|
65
|
+
].join('\n'));
|
|
66
|
+
assert.equal(parsed.process_armed,true);
|
|
67
|
+
const plan=runtimeUpdate.standingAuthorityRestorePlan(parsed,true);
|
|
68
|
+
assert.deepEqual(plan.commands,['armcoding','armautonomous','armprocess']);
|
|
69
|
+
assert.deepEqual(plan.expected,{
|
|
70
|
+
process_armed:true,coding_armed:true,autonomous_armed:true,desktop_armed:false,host_armed:false
|
|
71
|
+
});
|
|
72
|
+
const disabled=runtimeUpdate.standingAuthorityRestorePlan(parsed,false);
|
|
73
|
+
assert.deepEqual(disabled.commands,[]);
|
|
74
|
+
assert.deepEqual(disabled.expected,{
|
|
75
|
+
process_armed:false,coding_armed:false,autonomous_armed:false,desktop_armed:false,host_armed:false
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
56
79
|
test('R27 development root is outside immutable/runtime state stores', async () => {
|
|
57
80
|
const state=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r27-state-'));
|
|
58
81
|
const dev=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r27-dev-'));
|
|
@@ -410,6 +433,25 @@ test('R29 bridge bootstrap rebinds only the pinned external bridge script', asyn
|
|
|
410
433
|
await assert.rejects(runtimeUpdate.prepareBridgeSpec(config,{root,scriptSha256:hex('f')}),/bridge script SHA-256 mismatch/i);
|
|
411
434
|
});
|
|
412
435
|
|
|
436
|
+
test('R44 downstream core selection accepts only one exact visible or one opaque child of the verified Bridge', () => {
|
|
437
|
+
assert.equal(typeof runtimeUpdate.selectCoreProcess,'function');
|
|
438
|
+
const root=path.resolve('C:/release'),bridgePid=1200;
|
|
439
|
+
const visible={ProcessId:1300,ParentProcessId:bridgePid,CreationDate:'20260921010000.000000+000',
|
|
440
|
+
ExecutablePath:'C:\\Program Files\\nodejs\\node.exe',CommandLine:`node "${path.join(root,'src','mcp-server.mjs')}"`};
|
|
441
|
+
const exact=runtimeUpdate.selectCoreProcess([visible],root,bridgePid);
|
|
442
|
+
assert.equal(exact.pid,1300);
|
|
443
|
+
assert.equal(exact.identity_basis,'command_line');
|
|
444
|
+
const opaque={ProcessId:1301,ParentProcessId:bridgePid,CreationDate:'20260921010100.000000+000',ExecutablePath:null,CommandLine:null};
|
|
445
|
+
const verifiedBridge={component:'bridge',pid:bridgePid,script_path:'C:\\bridge\\src\\bridge.mjs',script_sha256:hex('a')};
|
|
446
|
+
const fallback=runtimeUpdate.selectCoreProcess([opaque],root,bridgePid,verifiedBridge);
|
|
447
|
+
assert.equal(fallback.pid,1301);
|
|
448
|
+
assert.equal(fallback.identity_basis,'verified_bridge_single_opaque_node_child');
|
|
449
|
+
assert.throws(()=>runtimeUpdate.selectCoreProcess([opaque],root,bridgePid),/requires verified Bridge script binding/i);
|
|
450
|
+
assert.throws(()=>runtimeUpdate.selectCoreProcess([opaque,{...opaque,ProcessId:1302}],root,bridgePid,verifiedBridge),/unambiguous downstream core/i);
|
|
451
|
+
assert.throws(()=>runtimeUpdate.selectCoreProcess([{...visible,CommandLine:'node C:\\other\\server.mjs'}],root,bridgePid),/unambiguous downstream core/i);
|
|
452
|
+
assert.throws(()=>runtimeUpdate.selectCoreProcess([{...opaque,CreationDate:''}],root,bridgePid,verifiedBridge),/identity incomplete/i);
|
|
453
|
+
});
|
|
454
|
+
|
|
413
455
|
test('R31 updater threads pinned process policy and machine-FS enable into successor generation', async () => {
|
|
414
456
|
const source=await readFile(new URL('../src/runtime-update.mjs',import.meta.url),'utf8');
|
|
415
457
|
assert.match(source,/prepareProcessPolicyBootstrap\(processPolicyBootstrap\)/);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { Client } from '@modelcontextprotocol/client';
|
|
4
|
+
import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/client/stdio';
|
|
5
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
|
|
10
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const repoRoot=path.resolve(here,'..');
|
|
12
|
+
const serverPath=path.join(repoRoot,'src','mcp-server.mjs');
|
|
13
|
+
|
|
14
|
+
test('R44 compact surface exposes one read-only skill_catalog without authority',async()=>{
|
|
15
|
+
const state=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-skill-mcp-'));
|
|
16
|
+
const client=new Client({name:'deadbyte-r44-skill-mcp',version:'0.15.0-dev'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
|
|
17
|
+
const env={...getDefaultEnvironment(),DEADBYTE_STATE_ROOT:state,DEADBYTE_CAPABILITY_PROFILE:'compact',
|
|
18
|
+
DEADBYTE_AUDIT_RUNTIME:'0',DEADBYTE_MACHINE_RUNTIME:'0',DEADBYTE_AGENT_RUNTIME:'0',
|
|
19
|
+
DEADBYTE_PROCESS_RUNTIME:'0',DEADBYTE_CODING_RUNTIME:'0',DEADBYTE_MACHINE_FS_RUNTIME:'0',
|
|
20
|
+
DEADBYTE_OBSERVATION_RUNTIME:'0',DEADBYTE_DESKTOP_RUNTIME:'0',DEADBYTE_AUTONOMOUS_RUNTIME:'0'};
|
|
21
|
+
const transport=new StdioClientTransport({command:process.execPath,args:[serverPath],cwd:repoRoot,env,stderr:'pipe'});
|
|
22
|
+
try{
|
|
23
|
+
await client.connect(transport);
|
|
24
|
+
const names=(await client.listTools()).tools.map(t=>t.name).sort();
|
|
25
|
+
assert.ok(names.includes('skill_catalog'),'R44 skill_catalog missing from compact surface');
|
|
26
|
+
assert.equal(names.filter(name=>name.startsWith('skill_')).length,1,'skill plane must remain one-tool surface');
|
|
27
|
+
const listed=await client.callTool({name:'skill_catalog',arguments:{op:'list'}});
|
|
28
|
+
assert.equal(listed.isError,false,JSON.stringify(listed));
|
|
29
|
+
assert.equal(listed.structuredContent.status,'ok');
|
|
30
|
+
assert.equal(listed.structuredContent.op,'list');
|
|
31
|
+
assert.deepEqual(listed.structuredContent.result.skills,[]);
|
|
32
|
+
assert.match(listed.structuredContent.result.registry_sha256,/^[0-9a-f]{64}$/);
|
|
33
|
+
const manifest=await client.callTool({name:'capability_manifest',arguments:{}});
|
|
34
|
+
const cap=manifest.structuredContent.result.capabilities.find(x=>x.id==='skill.catalog');
|
|
35
|
+
assert.ok(cap,'skill.catalog capability missing');
|
|
36
|
+
assert.equal(cap.authority,'none');assert.equal(cap.mutating,false);assert.equal(cap.compact_tool,'skill_catalog');
|
|
37
|
+
}finally{await client.close().catch(()=>{});await rm(state,{recursive:true,force:true});}
|
|
38
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { createHash, createPrivateKey, sign as cryptoSign } from 'node:crypto';
|
|
4
|
+
import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { generateTrustKeypair } from '../src/trust.mjs';
|
|
8
|
+
import { createSkillPlane } from '../src/skill-plane-r44.mjs';
|
|
9
|
+
|
|
10
|
+
const DOMAIN=Buffer.from('DEADBYTE-SKILL-MANIFEST-V1\0','utf8');
|
|
11
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
12
|
+
const canonical=value=>Array.isArray(value)?value.map(canonical):value&&typeof value==='object'
|
|
13
|
+
?Object.fromEntries(Object.keys(value).sort().map(key=>[key,canonical(value[key])])):value;
|
|
14
|
+
const canonicalJson=value=>JSON.stringify(canonical(value));
|
|
15
|
+
|
|
16
|
+
async function fixture(){
|
|
17
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-skill-'));
|
|
18
|
+
const registry=path.join(root,'skills');
|
|
19
|
+
const trust=path.join(root,'trust');
|
|
20
|
+
await mkdir(registry,{recursive:true});await mkdir(trust,{recursive:true});
|
|
21
|
+
const keys=await generateTrustKeypair({privateKeyPath:path.join(trust,'private.pem'),publicKeyPath:path.join(trust,'public.pem')});
|
|
22
|
+
return {root,registry,keys};
|
|
23
|
+
}
|
|
24
|
+
async function installSkill(f,{name='repo-audit',authorityPlanes=['coding'],requiredCapabilities=['coding.search'],extraBody={}}={}){
|
|
25
|
+
const dir=path.join(f.registry,name);await mkdir(path.join(dir,'references'),{recursive:true});
|
|
26
|
+
const skill=`---\nname: ${name}\ndescription: Audit one repository with bounded evidence\nversion: 1.0.0\naudience: agent\n---\n\n# Repo Audit\n\nInspect before mutation.\n`;
|
|
27
|
+
const ref='Use bounded search and cite exact observed files.\n';
|
|
28
|
+
await writeFile(path.join(dir,'SKILL.md'),skill,'utf8');
|
|
29
|
+
await writeFile(path.join(dir,'references','guide.md'),ref,'utf8');
|
|
30
|
+
const body={
|
|
31
|
+
schema:'deadbyte.skill-manifest.v1',name,version:'1.0.0',description:'Audit one repository with bounded evidence',audience:'agent',
|
|
32
|
+
skill_md_sha256:sha(Buffer.from(skill)),resources:[{path:'references/guide.md',sha256:sha(Buffer.from(ref)),kind:'reference'}],
|
|
33
|
+
required_capabilities:requiredCapabilities,authority_planes:authorityPlanes,
|
|
34
|
+
provenance:{source:'test-fixture',origin_url:null,source_revision:null},...extraBody
|
|
35
|
+
};
|
|
36
|
+
const bodyBytes=Buffer.from(canonicalJson(body),'utf8');
|
|
37
|
+
const privateKey=createPrivateKey(await readFile(f.keys.privateKeyPath));
|
|
38
|
+
const envelope={body,body_sha256:sha(bodyBytes),attestation:{algorithm:'ed25519',domain:'DEADBYTE-SKILL-MANIFEST-V1',
|
|
39
|
+
key_id:f.keys.keyId,signature_base64:cryptoSign(null,Buffer.concat([DOMAIN,bodyBytes]),privateKey).toString('base64')}};
|
|
40
|
+
envelope.manifest_sha256=sha(Buffer.from(canonicalJson(envelope),'utf8'));
|
|
41
|
+
await writeFile(path.join(dir,'skill.json'),`${JSON.stringify(envelope,null,2)}\n`,'utf8');
|
|
42
|
+
return {dir,skill,ref,envelope};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
test('R44 skill catalog is metadata-only and load is signature/content bound',async()=>{
|
|
46
|
+
const f=await fixture();
|
|
47
|
+
try{
|
|
48
|
+
await installSkill(f,{});
|
|
49
|
+
const plane=await createSkillPlane({root:f.registry,verifyKeyPath:f.keys.publicKeyPath});
|
|
50
|
+
const listed=await plane.list();
|
|
51
|
+
assert.equal(listed.status,'ok');assert.equal(listed.skills.length,1);
|
|
52
|
+
assert.equal(listed.skills[0].name,'repo-audit');assert.equal(listed.skills[0].authority_planes[0],'coding');
|
|
53
|
+
assert.equal('content' in listed.skills[0],false,'catalog must not leak instructions');
|
|
54
|
+
const loaded=await plane.load({name:'repo-audit'});
|
|
55
|
+
assert.equal(loaded.status,'ok');assert.match(loaded.content,/Inspect before mutation/);
|
|
56
|
+
assert.equal(loaded.verified,true);assert.match(loaded.manifest_sha256,/^[0-9a-f]{64}$/);
|
|
57
|
+
const resource=await plane.readResource({name:'repo-audit',resourcePath:'references/guide.md'});
|
|
58
|
+
assert.equal(resource.content,'Use bounded search and cite exact observed files.\n');
|
|
59
|
+
assert.equal(typeof plane.arm,'undefined');assert.equal(typeof plane.renew,'undefined');
|
|
60
|
+
}finally{await rm(f.root,{recursive:true,force:true});}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('R44 skill plane fails closed on SKILL.md and resource tampering',async()=>{
|
|
64
|
+
const f=await fixture();
|
|
65
|
+
try{
|
|
66
|
+
const installed=await installSkill(f,{});
|
|
67
|
+
const plane=await createSkillPlane({root:f.registry,verifyKeyPath:f.keys.publicKeyPath});
|
|
68
|
+
await writeFile(path.join(installed.dir,'SKILL.md'),'tampered\n','utf8');
|
|
69
|
+
await assert.rejects(plane.load({name:'repo-audit'}),/SKILL\.md hash mismatch/);
|
|
70
|
+
await installSkill(f,{});
|
|
71
|
+
await writeFile(path.join(installed.dir,'references','guide.md'),'tampered\n','utf8');
|
|
72
|
+
await assert.rejects(plane.readResource({name:'repo-audit',resourcePath:'references/guide.md'}),/resource hash mismatch/);
|
|
73
|
+
}finally{await rm(f.root,{recursive:true,force:true});}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('R44 skill plane rejects undeclared payloads, unknown authority and signature drift',async()=>{
|
|
77
|
+
const f=await fixture();
|
|
78
|
+
try{
|
|
79
|
+
const installed=await installSkill(f,{});
|
|
80
|
+
let plane=await createSkillPlane({root:f.registry,verifyKeyPath:f.keys.publicKeyPath});
|
|
81
|
+
await writeFile(path.join(installed.dir,'hidden.ps1'),'Write-Host pwn\n','utf8');
|
|
82
|
+
await assert.rejects(plane.load({name:'repo-audit'}),/undeclared skill file/);
|
|
83
|
+
await rm(path.join(installed.dir,'hidden.ps1'));
|
|
84
|
+
await installSkill(f,{authorityPlanes:['root']});
|
|
85
|
+
await assert.rejects(plane.load({name:'repo-audit'}),/unknown authority plane/);
|
|
86
|
+
await installSkill(f,{});
|
|
87
|
+
const env=JSON.parse(await readFile(path.join(installed.dir,'skill.json'),'utf8'));
|
|
88
|
+
env.body.description='signature drift';
|
|
89
|
+
await writeFile(path.join(installed.dir,'skill.json'),JSON.stringify(env,null,2)+'\n');
|
|
90
|
+
plane=await createSkillPlane({root:f.registry,verifyKeyPath:f.keys.publicKeyPath});
|
|
91
|
+
await assert.rejects(plane.load({name:'repo-audit'}),/body hash mismatch/);
|
|
92
|
+
}finally{await rm(f.root,{recursive:true,force:true});}
|
|
93
|
+
});
|
|
@@ -4,7 +4,7 @@ import { mkdtemp, mkdir, writeFile } from 'node:fs/promises';
|
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { loadCodingPolicy, codingSessionStatus } from '../src/coding-plane.mjs';
|
|
7
|
-
import { loadAutonomousPolicy, autonomousSessionStatus } from '../src/autonomous-policy.mjs';
|
|
7
|
+
import { assertAutonomousArmed, loadAutonomousPolicy, autonomousSessionStatus, recheckAutonomousAuthority } from '../src/autonomous-policy.mjs';
|
|
8
8
|
import { loadHostPolicy, assertHostArmed } from '../src/host-plane.mjs';
|
|
9
9
|
import { createHash } from 'node:crypto';
|
|
10
10
|
|
|
@@ -48,6 +48,10 @@ test('autonomous standing grant remains armed until explicit revocation', async
|
|
|
48
48
|
assert.equal(status.armed, true);
|
|
49
49
|
assert.equal(status.reason, 'standing-grant');
|
|
50
50
|
assert.equal(status.expires_at_utc, null);
|
|
51
|
+
const initial = await assertAutonomousArmed(policy, 'project', farFuture);
|
|
52
|
+
const grant = JSON.parse(await import('node:fs/promises').then(fs => fs.readFile(policy.lease_file, 'utf8')));
|
|
53
|
+
await writeFile(policy.lease_file, JSON.stringify({ ...grant, instance_nonce:'d'.repeat(64) }));
|
|
54
|
+
await assert.rejects(() => recheckAutonomousAuthority(policy, initial, 'project', farFuture), /autonomous authority changed during operation/);
|
|
51
55
|
});
|
|
52
56
|
test('host standing grant remains armed until explicit revocation', async () => {
|
|
53
57
|
const root = await mkdtemp(path.join(os.tmpdir(), 'deadbyte-host-standing-'));
|