deadbyte-mcp 0.15.53 → 0.15.54
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/MANIFEST.SHA256 +29 -24
- package/PROVENANCE.json +19 -8
- package/README.txt +18 -18
- package/RELEASE.json +19 -6
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +17 -2
- package/controller/deadbyte-process-policy.json +4 -4
- package/package.json +1 -1
- package/scripts/deploy-r48.mjs +51 -5
- package/src/activation-preflight-r65.mjs +35 -5
- package/src/commander-remote-bridge-r47.mjs +20 -2
- package/src/commander-remote-relay-r47.mjs +13 -2
- package/src/deadbyte-cli.mjs +5 -1
- package/src/legacy-offline-policy-proof-r146.mjs +204 -0
- package/src/release-generation.mjs +20 -1
- package/src/remote-device-r44.mjs +1 -1
- package/src/remote-live-log.mjs +43 -7
- package/src/version.mjs +1 -1
- package/test/commander-remote-bridge-r47.test.mjs +20 -1
- package/test/commander-remote-plane-r47.test.mjs +11 -0
- package/test/r130-release-security-contract.test.mjs +7 -7
- package/test/r132-release-identity.test.mjs +8 -8
- package/test/r146-generation-scope-narrowing.test.mjs +64 -0
- package/test/r146-legacy-offline-policy-proof.test.mjs +199 -0
- package/test/r146-livelog-backpressure.test.mjs +105 -0
- package/test/r147-remote-request-hash.test.mjs +54 -0
- package/test/r59-tool-surface-compat.test.mjs +1 -1
- package/test/r65-activation-orchestration.test.mjs +53 -0
- package/test/r74-prepublish-contract.test.mjs +1 -1
- package/test/release-generation.test.mjs +8 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {rebindCodingPolicy} from '../src/release-generation.mjs';
|
|
5
|
+
|
|
6
|
+
const settings={
|
|
7
|
+
developmentRoot:'D:\\Projects\\deadbyte-mcp\\r146',
|
|
8
|
+
stateRoot:'C:\\Users\\DEADBYTE\\.deadbyte-mcp',
|
|
9
|
+
parityScratchRoot:'C:\\Users\\DEADBYTE\\.deadbyte-mcp\\runtime\\r33-parity-scratch'
|
|
10
|
+
};
|
|
11
|
+
const root=folder=>({path:folder,read:true,write:true,exec:true,deny_paths:[]});
|
|
12
|
+
const base=()=>({
|
|
13
|
+
schema:'deadbyte.coding-policy.v1',
|
|
14
|
+
roots:{
|
|
15
|
+
core:root('D:\\Projects\\deadbyte-mcp\\old'),
|
|
16
|
+
projects:root('D:\\Projects'),
|
|
17
|
+
local_c:root('C:\\'),
|
|
18
|
+
local_d:root('D:\\')
|
|
19
|
+
},
|
|
20
|
+
commands:{
|
|
21
|
+
powershell_script:{root_ids:['core','local_c','local_d']},
|
|
22
|
+
cmd_script:{root_ids:['projects','local_d']},
|
|
23
|
+
node_script:{root_ids:['core','local_c']}
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('R146 never inherits historical C/D whole-drive authority',()=>{
|
|
28
|
+
const original=base();
|
|
29
|
+
const rewritten=rebindCodingPolicy(original,settings);
|
|
30
|
+
assert.deepEqual(Object.keys(rewritten.roots).sort(),['core','parity_scratch','projects']);
|
|
31
|
+
assert.equal(rewritten.roots.core.path,settings.developmentRoot);
|
|
32
|
+
assert.equal(rewritten.roots.parity_scratch.exec,false);
|
|
33
|
+
assert.deepEqual(rewritten.commands.powershell_script.root_ids,['core']);
|
|
34
|
+
assert.deepEqual(rewritten.commands.cmd_script.root_ids,['projects']);
|
|
35
|
+
assert.deepEqual(rewritten.commands.node_script.root_ids,['core']);
|
|
36
|
+
assert.equal(original.roots.local_c.path,'C:\\');
|
|
37
|
+
assert.equal(original.roots.local_d.path,'D:\\');
|
|
38
|
+
assert.deepEqual(original.commands.powershell_script.root_ids,['core','local_c','local_d']);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('R146 unknown whole drive root is not silently adopted',()=>{
|
|
42
|
+
const raw=base();raw.roots.other=root('E:\\');
|
|
43
|
+
assert.throws(()=>rebindCodingPolicy(raw,settings),/unrecognized whole-drive Coding root/);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('R146 fail closed if a command loses its last root',()=>{
|
|
47
|
+
const raw=base();raw.commands.powershell_script.root_ids=['local_c'];
|
|
48
|
+
assert.throws(()=>rebindCodingPolicy(raw,settings),/every scoped root/);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('R146 preserves a separately scoped local root',()=>{
|
|
52
|
+
const raw=base();raw.roots.local_d.path='D:\\Projects\\scratch';
|
|
53
|
+
const result=rebindCodingPolicy(raw,settings);
|
|
54
|
+
assert.equal(result.roots.local_d.path,'D:\\Projects\\scratch');
|
|
55
|
+
assert.deepEqual(result.commands.cmd_script.root_ids,['projects','local_d']);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('R146 ordinary scoped policies retain their command roots',()=>{
|
|
59
|
+
const raw=base();delete raw.roots.local_c;delete raw.roots.local_d;
|
|
60
|
+
raw.commands.powershell_script.root_ids=['core'];raw.commands.cmd_script.root_ids=['projects'];raw.commands.node_script.root_ids=['core'];
|
|
61
|
+
const result=rebindCodingPolicy(raw,settings);
|
|
62
|
+
assert.deepEqual(result.commands.powershell_script.root_ids,['core']);
|
|
63
|
+
assert.equal(result.roots.projects.path,'D:\\Projects');
|
|
64
|
+
});
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createHash,generateKeyPairSync,sign} from 'node:crypto';
|
|
4
|
+
import {mkdir,mkdtemp,readFile,rm,writeFile} from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {proveLegacyOfflineScopeRepair} from '../src/legacy-offline-policy-proof-r146.mjs';
|
|
8
|
+
import {canonicalJson} from '../src/canonical-json.mjs';
|
|
9
|
+
|
|
10
|
+
const sha=x=>createHash('sha256').update(x).digest('hex');
|
|
11
|
+
const PLANES=['process','coding','autonomous','desktop','host'];
|
|
12
|
+
const GRANT={process:'grant_file',coding:'lease_file',autonomous:'lease_file',desktop:'grant_file',host:'arm_file'};
|
|
13
|
+
const APPROVAL_DOMAIN=Buffer.from('DEADBYTE-R146-POLICY-MIGRATION-APPROVAL\0','utf8');
|
|
14
|
+
const mkRoot=p=>({path:p,read:true,write:true,exec:true,deny_paths:[]});
|
|
15
|
+
async function fixture({thirdDrive=false,badBinding=false}={}){
|
|
16
|
+
const base=await mkdtemp(path.join(os.tmpdir(),'r146-offline-scope-'));
|
|
17
|
+
const stateRoot=path.join(base,'state'),runtime=path.join(stateRoot,'runtime');
|
|
18
|
+
const gen=path.join(stateRoot,'generations','predecessor');
|
|
19
|
+
await mkdir(runtime,{recursive:true});await mkdir(gen,{recursive:true});
|
|
20
|
+
const policyPaths={},policySha256={},files=[];
|
|
21
|
+
const coding={
|
|
22
|
+
schema:'deadbyte.coding-policy.v1',enabled:true,
|
|
23
|
+
lease_file:path.join(runtime,'coding-grant.json'),
|
|
24
|
+
roots:{core:mkRoot(path.join(base,'project')),local_c:mkRoot('C:\\'),local_d:mkRoot('D:\\')},
|
|
25
|
+
commands:{powershell_script:{root_ids:['core','local_c','local_d']}}
|
|
26
|
+
};
|
|
27
|
+
if(thirdDrive)coding.roots.other=mkRoot('E:\\');
|
|
28
|
+
const codingBytes=Buffer.from(JSON.stringify(coding)+'\n');
|
|
29
|
+
const autonomous={
|
|
30
|
+
schema:'deadbyte.autonomous-policy.v1',enabled:true,
|
|
31
|
+
lease_file:path.join(runtime,'autonomous-grant.json'),
|
|
32
|
+
coding_policy_sha256:badBinding?'a'.repeat(64):sha(codingBytes),
|
|
33
|
+
root_ids:['core'],profiles:{}
|
|
34
|
+
};
|
|
35
|
+
const policies={
|
|
36
|
+
coding,autonomous,
|
|
37
|
+
process:{schema:'deadbyte.process-policy.v1',enabled:true,grant_file:path.join(runtime,'process-grant.json')},
|
|
38
|
+
desktop:{schema:'deadbyte.desktop-policy.v1',enabled:true,grant_file:path.join(runtime,'desktop-grant.json')},
|
|
39
|
+
host:{schema:'deadbyte.host-policy.v2',enabled:true,arm_file:path.join(runtime,'host-grant.json')}
|
|
40
|
+
};
|
|
41
|
+
for(const plane of PLANES){
|
|
42
|
+
const file=path.join(gen,plane+'-policy.json');
|
|
43
|
+
const bytes=plane==='coding'?codingBytes:Buffer.from(JSON.stringify(policies[plane])+'\n');
|
|
44
|
+
await writeFile(file,bytes);
|
|
45
|
+
policyPaths[plane]=file;policySha256[plane]=sha(bytes);
|
|
46
|
+
files.push([plane+'-policy.json',sha(bytes)]);
|
|
47
|
+
}
|
|
48
|
+
const manifest=Buffer.from(files.sort((a,b)=>a[0].localeCompare(b[0],'en')).map(x=>x[1]+' '+x[0]).join('\n')+'\n');
|
|
49
|
+
await writeFile(path.join(gen,'GENERATION.SHA256'),manifest);
|
|
50
|
+
const parent={release_id:'v0.15.45-34aa8d1241cfb8bf',version:'0.15.45',
|
|
51
|
+
manifest_sha256:'1'.repeat(64),generation_manifest_sha256:sha(manifest)};
|
|
52
|
+
const pointer={...parent,generation_root:gen};
|
|
53
|
+
await writeFile(path.join(stateRoot,'active-release.json'),JSON.stringify(pointer)+'\n');
|
|
54
|
+
const authority=Object.fromEntries(PLANES.map(p=>[p,{
|
|
55
|
+
configured:true,armed:false,reason:p==='autonomous'?'unverified':'disarmed'
|
|
56
|
+
}]));
|
|
57
|
+
const authority_proof=Object.fromEntries(PLANES.map(p=>[p,{
|
|
58
|
+
state:p==='autonomous'?'UNVERIFIED':'VERIFIED_DISARMED',
|
|
59
|
+
grant_absent:true,policy_sha256:policySha256[p]
|
|
60
|
+
}]));
|
|
61
|
+
const observation={state:'OFFLINE',local_ready:false,
|
|
62
|
+
bridge:{alive:false},core:{alive:false},cloud:{alive:false},supervisor:{running:false},
|
|
63
|
+
authority,authority_proof,policy_sha256:policySha256,
|
|
64
|
+
active_release_id:parent.release_id,active_manifest_sha256:parent.manifest_sha256,
|
|
65
|
+
active_generation_manifest_sha256:parent.generation_manifest_sha256};
|
|
66
|
+
const candidate={version:'0.15.54',manifest_sha256:'9'.repeat(64)};
|
|
67
|
+
const preflightSubjectSha256='8'.repeat(64);
|
|
68
|
+
const {privateKey,publicKey}=generateKeyPairSync('ed25519');
|
|
69
|
+
const ownerKeyId=sha(publicKey.export({type:'spki',format:'der'}));
|
|
70
|
+
const trust=path.join(stateRoot,'trust');await mkdir(trust,{recursive:true});
|
|
71
|
+
const ownerPublicKeyPath=path.join(trust,'r70-owner-public.pem');
|
|
72
|
+
await writeFile(ownerPublicKeyPath,publicKey.export({type:'spki',format:'pem'}));
|
|
73
|
+
const subject={schema:'deadbyte.r146-policy-migration-subject.v1',action:'legacy_offline_scope_repair',
|
|
74
|
+
predecessor_release_id:parent.release_id,predecessor_version:parent.version,
|
|
75
|
+
predecessor_manifest_sha256:parent.manifest_sha256,
|
|
76
|
+
predecessor_generation_manifest_sha256:parent.generation_manifest_sha256,
|
|
77
|
+
candidate_version:candidate.version,candidate_manifest_sha256:candidate.manifest_sha256,
|
|
78
|
+
predecessor_policy_sha256:policySha256,preflight_subject_sha256:preflightSubjectSha256,
|
|
79
|
+
issued_at_utc:'2026-10-23T00:00:00.000Z',expires_at_utc:'2026-10-23T00:30:00.000Z'};
|
|
80
|
+
const subjectSha256=sha(Buffer.from(canonicalJson(subject),'utf8'));
|
|
81
|
+
const signed={schema:'deadbyte.r146-policy-migration-approval.v1',subject_sha256:subjectSha256,owner_key_id:ownerKeyId};
|
|
82
|
+
const approval={...signed,subject,signature_base64:sign(null,Buffer.concat([APPROVAL_DOMAIN,Buffer.from(canonicalJson(signed),'utf8')]),privateKey).toString('base64')};
|
|
83
|
+
const approvalPath=path.join(base,'approval.json'),approvalBytes=Buffer.from(JSON.stringify(approval));
|
|
84
|
+
await writeFile(approvalPath,approvalBytes);
|
|
85
|
+
return {
|
|
86
|
+
root:base,stateRoot,pointer,expectedParent:parent,observation,policyPaths,policySha256,
|
|
87
|
+
runtime,policies,candidate,expectedCandidate:candidate,preflightSubjectSha256,
|
|
88
|
+
approvalPath,approvalSha256:sha(approvalBytes),ownerPublicKeyPath,
|
|
89
|
+
now:new Date('2026-10-23T00:05:00.000Z'),async close(){await rm(base,{recursive:true,force:true});}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const invoke=x=>proveLegacyOfflineScopeRepair(x);
|
|
93
|
+
test('R146 verifies exact inactive legacy scope only with manifest, direct grant absence and parent binding',async()=>{
|
|
94
|
+
const x=await fixture();
|
|
95
|
+
try{
|
|
96
|
+
const got=await invoke(x);
|
|
97
|
+
assert.equal(got.state,'VERIFIED_OFFLINE_LEGACY_SCOPE_REPAIR');
|
|
98
|
+
assert.equal(got.coding_policy_sha256,x.policySha256.coding);
|
|
99
|
+
assert.equal(got.generation_manifest_sha256,x.pointer.generation_manifest_sha256);
|
|
100
|
+
assert.equal(got.candidate_manifest_sha256,x.candidate.manifest_sha256);
|
|
101
|
+
assert.equal(got.preflight_subject_sha256,x.preflightSubjectSha256);
|
|
102
|
+
assert.equal(got.owner_approval_verified,true);
|
|
103
|
+
assert.match(got.subject_sha256,/^[0-9a-f]{64}$/);
|
|
104
|
+
}finally{await x.close();}
|
|
105
|
+
});
|
|
106
|
+
test('R146 proof rejects candidate or approval subject drift',async()=>{
|
|
107
|
+
const x=await fixture();
|
|
108
|
+
try{
|
|
109
|
+
await assert.rejects(invoke({...x,candidate:{...x.candidate,manifest_sha256:'7'.repeat(64)}}),/candidate|approval/i);
|
|
110
|
+
await assert.rejects(invoke({...x,preflightSubjectSha256:'7'.repeat(64)}),/preflight subject/i);
|
|
111
|
+
}finally{await x.close();}
|
|
112
|
+
});
|
|
113
|
+
test('R146 proof rejects an expired or not-yet-valid owner approval',async()=>{
|
|
114
|
+
const x=await fixture();
|
|
115
|
+
try{
|
|
116
|
+
await assert.rejects(invoke({...x,now:new Date('2026-10-23T00:30:00.000Z')}),/approval expired or invalid/);
|
|
117
|
+
await assert.rejects(invoke({...x,now:new Date('2026-10-22T23:59:59.999Z')}),/approval expired or invalid/);
|
|
118
|
+
}finally{await x.close();}
|
|
119
|
+
});
|
|
120
|
+
test('R146 proof rejects a forged approval even when its file hash is repinned',async()=>{
|
|
121
|
+
const x=await fixture();
|
|
122
|
+
try{
|
|
123
|
+
const approval=JSON.parse(await readFile(x.approvalPath,'utf8'));
|
|
124
|
+
approval.signature_base64=Buffer.alloc(64,0x5a).toString('base64');
|
|
125
|
+
const bytes=Buffer.from(JSON.stringify(approval));
|
|
126
|
+
await writeFile(x.approvalPath,bytes);
|
|
127
|
+
await assert.rejects(invoke({...x,approvalSha256:sha(bytes)}),/approval signature invalid/);
|
|
128
|
+
}finally{await x.close();}
|
|
129
|
+
});
|
|
130
|
+
test('R146 proof rejects an owner trust-key substitution',async()=>{
|
|
131
|
+
const x=await fixture();
|
|
132
|
+
try{
|
|
133
|
+
const {publicKey}=generateKeyPairSync('ed25519');
|
|
134
|
+
await writeFile(x.ownerPublicKeyPath,publicKey.export({type:'spki',format:'pem'}));
|
|
135
|
+
await assert.rejects(invoke(x),/owner key identity mismatch/);
|
|
136
|
+
}finally{await x.close();}
|
|
137
|
+
});
|
|
138
|
+
test('R146 proof rejects any unresolved authority epoch lock',async()=>{
|
|
139
|
+
const x=await fixture();
|
|
140
|
+
try{
|
|
141
|
+
const lock=path.join(x.runtime,'authority-epochs','coding','.lock');
|
|
142
|
+
await mkdir(path.dirname(lock),{recursive:true});await writeFile(lock,'ambiguous');
|
|
143
|
+
await assert.rejects(invoke(x),/authority epoch lock present/);
|
|
144
|
+
}finally{await x.close();}
|
|
145
|
+
});
|
|
146
|
+
test('R146 any real grant overrides apparent DISARMED observation',async()=>{
|
|
147
|
+
const x=await fixture();
|
|
148
|
+
try{
|
|
149
|
+
await writeFile(path.join(x.runtime,'autonomous-grant.json'),'active');
|
|
150
|
+
await assert.rejects(invoke(x),/autonomous grant present/);
|
|
151
|
+
}finally{await x.close();}
|
|
152
|
+
});
|
|
153
|
+
test('R146 foreground ownership marker blocks offline scope repair',async()=>{
|
|
154
|
+
const x=await fixture();
|
|
155
|
+
try{
|
|
156
|
+
await writeFile(path.join(x.runtime,'remote-session.json'),'retained');
|
|
157
|
+
await assert.rejects(invoke(x),/ownership marker present/);
|
|
158
|
+
}finally{await x.close();}
|
|
159
|
+
});
|
|
160
|
+
test('R146 active or uncertain runtime blocks offline scope repair',async()=>{
|
|
161
|
+
const x=await fixture();
|
|
162
|
+
try{
|
|
163
|
+
x.observation.bridge.alive=true;
|
|
164
|
+
await assert.rejects(invoke(x),/not cleanly offline/);
|
|
165
|
+
}finally{await x.close();}
|
|
166
|
+
});
|
|
167
|
+
test('R146 any unrelated unverified authority plane blocks scope repair',async()=>{
|
|
168
|
+
const x=await fixture();
|
|
169
|
+
try{
|
|
170
|
+
x.observation.authority.host.reason='unverified';
|
|
171
|
+
await assert.rejects(invoke(x),/not physically inactive: host/);
|
|
172
|
+
}finally{await x.close();}
|
|
173
|
+
});
|
|
174
|
+
test('R146 extra whole-drive root fails the special repair proof',async()=>{
|
|
175
|
+
const x=await fixture({thirdDrive:true});
|
|
176
|
+
try{
|
|
177
|
+
await assert.rejects(invoke(x),/unexpected drive root/);
|
|
178
|
+
}finally{await x.close();}
|
|
179
|
+
});
|
|
180
|
+
test('R146 autonomous coding policy digest mismatch blocks recovery',async()=>{
|
|
181
|
+
const x=await fixture({badBinding:true});
|
|
182
|
+
try{
|
|
183
|
+
await assert.rejects(invoke(x),/Coding\/Autonomous binding invalid/);
|
|
184
|
+
}finally{await x.close();}
|
|
185
|
+
});
|
|
186
|
+
test('R146 predecessor generation bytes drift blocks offline repair',async()=>{
|
|
187
|
+
const x=await fixture();
|
|
188
|
+
try{
|
|
189
|
+
await writeFile(x.policyPaths.coding,'{}');
|
|
190
|
+
await assert.rejects(invoke(x),/generation file hash mismatch/);
|
|
191
|
+
}finally{await x.close();}
|
|
192
|
+
});
|
|
193
|
+
test('R146 predecessor identity drift cannot be attested',async()=>{
|
|
194
|
+
const x=await fixture();
|
|
195
|
+
try{
|
|
196
|
+
x.expectedParent.manifest_sha256='a'.repeat(64);
|
|
197
|
+
await assert.rejects(invoke(x),/predecessor mismatch/);
|
|
198
|
+
}finally{await x.close();}
|
|
199
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {EventEmitter} from 'node:events';
|
|
4
|
+
import {createLiveTimelineWriter} from '../src/remote-live-log.mjs';
|
|
5
|
+
|
|
6
|
+
function blockedOutput(){
|
|
7
|
+
const stream=new EventEmitter();
|
|
8
|
+
const writes=[];
|
|
9
|
+
stream.isTTY=false;
|
|
10
|
+
let blockNext=false;
|
|
11
|
+
stream.write=chunk=>{
|
|
12
|
+
writes.push(chunk);
|
|
13
|
+
if(blockNext){blockNext=false;return false;}
|
|
14
|
+
return true;
|
|
15
|
+
};
|
|
16
|
+
return {stream,writes,block:()=>{blockNext=true;}};
|
|
17
|
+
}
|
|
18
|
+
test('R146 LiveLogs survives stdout backpressure without unbounded presentation writes',()=>{
|
|
19
|
+
const out=blockedOutput();
|
|
20
|
+
const writer=createLiveTimelineWriter({stream:out.stream,rewrite:false});
|
|
21
|
+
out.block();
|
|
22
|
+
writer.write('one');
|
|
23
|
+
for(let i=0;i<12000;i++)writer.write('status '+i);
|
|
24
|
+
assert.deepEqual(out.writes,['one\n']);
|
|
25
|
+
assert.equal(writer.retention().suppressed_since_drain,12000);
|
|
26
|
+
assert.equal(writer.retention().congested,true);
|
|
27
|
+
out.stream.emit('drain');
|
|
28
|
+
assert.match(out.writes[1],/LIVELOG.*12000.*suppressed.*audit/i);
|
|
29
|
+
assert.equal(writer.retention().suppressed_since_drain,0);
|
|
30
|
+
writer.write('after');
|
|
31
|
+
assert.equal(out.writes.at(-1),'after\n');
|
|
32
|
+
assert.equal(writer.retention().total_suppressed,12000);
|
|
33
|
+
});
|
|
34
|
+
test('R146 LiveLogs emits only one bounded warning for each backpressure window',()=>{
|
|
35
|
+
const out=blockedOutput();
|
|
36
|
+
const writer=createLiveTimelineWriter({stream:out.stream,rewrite:false});
|
|
37
|
+
out.block();writer.write('a');
|
|
38
|
+
writer.write('drop1');writer.write('drop2');
|
|
39
|
+
out.stream.emit('drain');
|
|
40
|
+
out.stream.emit('drain');
|
|
41
|
+
assert.equal(out.writes.length,2);
|
|
42
|
+
assert.match(out.writes[1],/2.*suppressed/);
|
|
43
|
+
});
|
|
44
|
+
test('R146 LiveLogs normal progress and TTY rewrite stay unchanged when drain never needed',()=>{
|
|
45
|
+
const out=blockedOutput();
|
|
46
|
+
out.stream.isTTY=true;
|
|
47
|
+
const writer=createLiveTimelineWriter({stream:out.stream,rewrite:true});
|
|
48
|
+
writer.write('RUN',{event_type:'tool.call',request_id:'a'});
|
|
49
|
+
writer.write('DONE',{event_type:'tool.result',request_id:'a'});
|
|
50
|
+
assert.deepEqual(out.writes,['RUN','\r\x1b[2KDONE\n']);
|
|
51
|
+
assert.equal(writer.retention().total_suppressed,0);
|
|
52
|
+
});
|
|
53
|
+
test('R146 LiveLogs refuses non-evented saturated stream instead of silently dropping output',()=>{
|
|
54
|
+
const sink={write(){return false;}};
|
|
55
|
+
const writer=createLiveTimelineWriter({stream:sink,rewrite:false});
|
|
56
|
+
assert.throws(()=>writer.write('line'),/drain-capable/);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('R146 LiveLogs warns on a stalled TTY without leaving its partial START line attached',()=>{
|
|
60
|
+
const out=blockedOutput();
|
|
61
|
+
out.stream.isTTY=true;
|
|
62
|
+
const writer=createLiveTimelineWriter({stream:out.stream,rewrite:true});
|
|
63
|
+
out.block();
|
|
64
|
+
writer.write('RUN',{event_type:'tool.call',request_id:'a'});
|
|
65
|
+
writer.write('DONE',{event_type:'tool.result',request_id:'a'});
|
|
66
|
+
assert.equal(writer.retention().suppressed_since_drain,1);
|
|
67
|
+
out.stream.emit('drain');
|
|
68
|
+
assert.equal(out.writes[0],'RUN');
|
|
69
|
+
assert.equal(out.writes[1],'\n');
|
|
70
|
+
assert.match(out.writes[2],/1 presentation lines suppressed/);
|
|
71
|
+
writer.write('AFTER');
|
|
72
|
+
assert.equal(out.writes[3],'AFTER\n');
|
|
73
|
+
});
|
|
74
|
+
test('R146 LiveLogs repeated backpressure during warning remains bounded and recovers',()=>{
|
|
75
|
+
const out=blockedOutput();
|
|
76
|
+
const writer=createLiveTimelineWriter({stream:out.stream,rewrite:false});
|
|
77
|
+
out.block();writer.write('START');writer.write('hidden 1');
|
|
78
|
+
out.block();out.stream.emit('drain');
|
|
79
|
+
assert.equal(writer.retention().congested,true);
|
|
80
|
+
writer.write('hidden 2');
|
|
81
|
+
out.stream.emit('drain');
|
|
82
|
+
assert.equal(writer.retention().congested,false);
|
|
83
|
+
assert.equal(writer.retention().total_suppressed,2);
|
|
84
|
+
assert.match(out.writes[1],/1 presentation lines suppressed/);
|
|
85
|
+
assert.match(out.writes[2],/1 presentation lines suppressed/);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('R146 LiveLogs session summary reports untracked overflow beyond its bounded in-flight cap',async()=>{
|
|
89
|
+
const {createLiveTimelineTracker}=await import('../src/remote-live-log.mjs');
|
|
90
|
+
const tracker=createLiveTimelineTracker({displayMode:'ascii'});
|
|
91
|
+
for(let i=0;i<4100;i++){
|
|
92
|
+
tracker.observe({event_type:'tool.request',tool:'coding_file_read',
|
|
93
|
+
request_id:'too-many-'+String(i).padStart(8,'0'),args:{root_id:'core',path:'readme'}},{sequence:i+1});
|
|
94
|
+
}
|
|
95
|
+
assert.equal(tracker.retention().pending,4096);
|
|
96
|
+
assert.equal(tracker.retention().pending_overflow,4);
|
|
97
|
+
assert.match(tracker.summary(),/4.*presentation spans.*(?:overflow|untracked)/i);
|
|
98
|
+
});
|
|
99
|
+
test('R146 CLI reports any still-congested output during final cleanup',async()=>{
|
|
100
|
+
const {readFile}=await import('node:fs/promises');
|
|
101
|
+
const cli=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
102
|
+
assert.match(cli,/timelineWriter\.retention\(\)/);
|
|
103
|
+
assert.match(cli,/suppressed_since_drain/);
|
|
104
|
+
assert.match(cli,/process\.stderr\.write/);
|
|
105
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import {registerCommanderRemoteBridge} from '../src/commander-remote-bridge-r47.mjs';
|
|
5
|
+
|
|
6
|
+
function canonical(value) {
|
|
7
|
+
if(Array.isArray(value)) return value.map(canonical);
|
|
8
|
+
if(value&&typeof value==='object')
|
|
9
|
+
return Object.fromEntries(Object.keys(value).sort((a,b)=>a.localeCompare(b,'en')).map(key=>[key,canonical(value[key])]));
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
const hashRequest=v=>createHash('sha256').update(JSON.stringify(canonical(v)),'utf8').digest('hex');
|
|
13
|
+
function server(){
|
|
14
|
+
const tools=new Map();
|
|
15
|
+
return {tools,registerTool(name,meta,handler){tools.set(name,{meta,handler});}};
|
|
16
|
+
}
|
|
17
|
+
const id='a'.repeat(64),request='b'.repeat(64),epoch=4,cap='machine.status';
|
|
18
|
+
const payload={z:4,a:{inner:1}};
|
|
19
|
+
const digest=hashRequest({device_id:id,epoch,capability_id:cap,payload});
|
|
20
|
+
|
|
21
|
+
test('R147 native relay request_sha256 passes strict remote_mcp schema',()=>{
|
|
22
|
+
const app=server();registerCommanderRemoteBridge(app,{});
|
|
23
|
+
const validation=app.tools.get('remote_mcp').meta.inputSchema.safeParse({
|
|
24
|
+
device_id:id,epoch,capability_id:cap,payload,request_id:request,request_sha256:digest
|
|
25
|
+
});
|
|
26
|
+
assert.equal(validation.success,true,'native relay sends request_sha256 and strict server must accept it');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('R147 remote_mcp rejects a conflicting claimed payload hash before dispatch',async()=>{
|
|
30
|
+
const app=server(),calls=[];
|
|
31
|
+
registerCommanderRemoteBridge(app,{remoteDevicePlane:{async dispatch(value){calls.push(value);return {state:'completed',result_sha256:digest};}}});
|
|
32
|
+
const input={device_id:id,epoch,capability_id:cap,payload,request_id:request,request_sha256:'f'.repeat(64)};
|
|
33
|
+
await assert.rejects(app.tools.get('remote_mcp').handler(input),/request.*hash.*mismatch/i);
|
|
34
|
+
assert.equal(calls.length,0);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('R147 remote_mcp binds matching digest and retains original dispatch contract',async()=>{
|
|
38
|
+
const app=server(),calls=[];
|
|
39
|
+
registerCommanderRemoteBridge(app,{remoteDevicePlane:{async dispatch(value){calls.push(value);return {state:'completed',result_sha256:digest};}}});
|
|
40
|
+
const input={device_id:id,epoch,capability_id:cap,payload,request_id:request,request_sha256:digest};
|
|
41
|
+
const result=await app.tools.get('remote_mcp').handler(input);
|
|
42
|
+
assert.equal(result.state,'completed');
|
|
43
|
+
assert.deepEqual(calls,[{deviceId:id,epoch,capabilityId:cap,payload,requestId:request}]);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('R147 remote_mcp remains backward-compatible with correctly scoped legacy call',async()=>{
|
|
47
|
+
const app=server(),calls=[];
|
|
48
|
+
registerCommanderRemoteBridge(app,{remoteDevicePlane:{async dispatch(v){calls.push(v);return {state:'completed'};}}});
|
|
49
|
+
const result=await app.tools.get('remote_mcp').handler({
|
|
50
|
+
device_id:id,epoch,capability_id:cap,payload,request_id:request
|
|
51
|
+
});
|
|
52
|
+
assert.equal(result.state,'completed');
|
|
53
|
+
assert.equal(calls.length,1);
|
|
54
|
+
});
|
|
@@ -83,7 +83,7 @@ test('R72 compact baseline permits only R64 read offset and authorized coding-on
|
|
|
83
83
|
assert.deepEqual(diff.added,[]);
|
|
84
84
|
assert.deepEqual(diff.removed,[]);
|
|
85
85
|
const packageVersion=JSON.parse(await readFile(path.join(repoRoot,'package.json'),'utf8')).version;
|
|
86
|
-
if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46','0.15.47','0.15.48','0.15.49','0.15.50','0.15.51','0.15.52','0.15.53'].includes(packageVersion)){
|
|
86
|
+
if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46','0.15.47','0.15.48','0.15.49','0.15.50','0.15.51','0.15.52','0.15.53','0.15.54'].includes(packageVersion)){
|
|
87
87
|
assert.deepEqual([...diff.schema_changed].sort(),['machine_fs','native_diff_preview']);
|
|
88
88
|
assert.equal(diff.unchanged.length,41);
|
|
89
89
|
const prior=baseline.tools.find(tool=>tool.name==='machine_fs');
|
|
@@ -178,3 +178,56 @@ test('terminal activation outcome is persisted before activation claim release',
|
|
|
178
178
|
assert.equal(result.state,'ACTIVATED');
|
|
179
179
|
assert.deepEqual(order,['claim','activate-start','activate-end','record','release']);
|
|
180
180
|
});
|
|
181
|
+
|
|
182
|
+
test('R146 legacy offline scope repair is claimed, owner-bound and reproved immediately before activation',async()=>{
|
|
183
|
+
const order=[];
|
|
184
|
+
const disarmed=plane=>({configured:true,armed:false,reason:plane==='autonomous'?'unverified':'disarmed'});
|
|
185
|
+
const proof=plane=>({state:plane==='autonomous'?'UNVERIFIED':'VERIFIED_DISARMED',policy_sha256:policies[plane],grant_absent:true});
|
|
186
|
+
let current={state:'OFFLINE',local_ready:false,bridge:{alive:false},core:{alive:false},cloud:{alive:false},supervisor:{running:false},
|
|
187
|
+
authority:Object.fromEntries(Object.keys(policies).map(plane=>[plane,disarmed(plane)])),
|
|
188
|
+
authority_proof:Object.fromEntries(Object.keys(policies).map(plane=>[plane,proof(plane)])),policy_sha256:policies,
|
|
189
|
+
active_release_id:parent.release_id,active_manifest_sha256:parent.manifest_sha256,
|
|
190
|
+
active_generation_manifest_sha256:parent.generation_manifest_sha256};
|
|
191
|
+
const observe=async()=>current;
|
|
192
|
+
const claim=async subject=>{order.push('claim:'+subject);return async()=>order.push('release');};
|
|
193
|
+
const legacyOfflineRecovery=async({phase,preflight_subject_sha256,candidate:boundCandidate})=>{
|
|
194
|
+
order.push('proof:'+phase);
|
|
195
|
+
assert.match(preflight_subject_sha256,/^[0-9a-f]{64}$/);
|
|
196
|
+
assert.equal(boundCandidate.manifest_sha256,candidate.manifest_sha256);
|
|
197
|
+
return {state:'VERIFIED_OFFLINE_LEGACY_SCOPE_REPAIR',preflight_subject_sha256,
|
|
198
|
+
candidate_manifest_sha256:candidate.manifest_sha256,owner_approval_verified:true,subject_sha256:'7'.repeat(64)};
|
|
199
|
+
};
|
|
200
|
+
const activate=async()=>{
|
|
201
|
+
order.push('activate');
|
|
202
|
+
current={...current,state:'ONLINE',local_ready:true,...ids,supervisor:{running:true},
|
|
203
|
+
authority:Object.fromEntries(Object.keys(policies).map(plane=>[plane,legacyEntry(false)])),
|
|
204
|
+
authority_proof:Object.fromEntries(Object.keys(policies).map(plane=>[plane,{state:'VERIFIED_DISARMED',policy_sha256:policies[plane],grant_absent:true}]))};
|
|
205
|
+
return {verified:true,release_id:'v0.15.42-new'};
|
|
206
|
+
};
|
|
207
|
+
const recordOutcome=async outcome=>{order.push('record:'+outcome.state);};
|
|
208
|
+
const result=await orchestrateActivation({pointer:parent,expectedParent:parent,candidate,expectedCandidate:candidate,
|
|
209
|
+
observe,command:async()=>{throw new Error('no authority command expected');},activate,claim,recordOutcome,legacyOfflineRecovery});
|
|
210
|
+
assert.equal(result.state,'ACTIVATED');
|
|
211
|
+
assert.equal(result.authority_continuity,'LEGACY_SCOPE_REPAIRED_DISARMED');
|
|
212
|
+
assert.deepEqual(order.map(x=>x.replace(/:.*$/,'')),['claim','proof','proof','activate','record','release']);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test('R146 legacy offline scope proof drift after claim never dispatches activation',async()=>{
|
|
216
|
+
const authority=Object.fromEntries(Object.keys(policies).map(plane=>[plane,{configured:true,armed:false,reason:plane==='autonomous'?'unverified':'disarmed'}]));
|
|
217
|
+
const authority_proof=Object.fromEntries(Object.keys(policies).map(plane=>[plane,{state:plane==='autonomous'?'UNVERIFIED':'VERIFIED_DISARMED',policy_sha256:policies[plane],grant_absent:true}]));
|
|
218
|
+
const observation={state:'OFFLINE',local_ready:false,bridge:{alive:false},core:{alive:false},cloud:{alive:false},supervisor:{running:false},authority,authority_proof,policy_sha256:policies,
|
|
219
|
+
active_release_id:parent.release_id,active_manifest_sha256:parent.manifest_sha256,active_generation_manifest_sha256:parent.generation_manifest_sha256};
|
|
220
|
+
let proofs=0,activated=false,released=false;
|
|
221
|
+
const result=await orchestrateActivation({pointer:parent,expectedParent:parent,candidate,expectedCandidate:candidate,
|
|
222
|
+
observe:async()=>observation,command:async()=>{},activate:async()=>{activated=true;return {verified:true};},
|
|
223
|
+
claim:async()=>async()=>{released=true;},recordOutcome:async()=>{},
|
|
224
|
+
legacyOfflineRecovery:async({preflight_subject_sha256})=>{
|
|
225
|
+
proofs++;
|
|
226
|
+
if(proofs===2)throw new Error('legacy policy bytes changed');
|
|
227
|
+
return {state:'VERIFIED_OFFLINE_LEGACY_SCOPE_REPAIR',preflight_subject_sha256,
|
|
228
|
+
candidate_manifest_sha256:candidate.manifest_sha256,owner_approval_verified:true,subject_sha256:'7'.repeat(64)};
|
|
229
|
+
}});
|
|
230
|
+
assert.equal(result.state,'ACTIVATION_REJECTED');
|
|
231
|
+
assert.equal(activated,false);
|
|
232
|
+
assert.equal(released,true);
|
|
233
|
+
});
|
|
@@ -22,7 +22,7 @@ test('R74 prepublish contract: positive path succeeds and verifies identities',
|
|
|
22
22
|
assert.equal(result.identities.package_name, 'deadbyte-mcp');
|
|
23
23
|
// R74 component contract retains its train-specific source and predecessor assertions;
|
|
24
24
|
// the containing R144 package must report its actual immutable package version.
|
|
25
|
-
assert.equal(result.identities.package_version, '0.15.
|
|
25
|
+
assert.equal(result.identities.package_version, '0.15.54');
|
|
26
26
|
assert.equal(result.identities.target_train, 'R74');
|
|
27
27
|
assert.equal(result.identities.published_source_predecessor, '0.15.46');
|
|
28
28
|
assert.notEqual(result.identities.published_source_predecessor, result.identities.runtime_predecessor);
|
|
@@ -41,7 +41,10 @@ test('R27 generation bundle rebinds authority and mutable state outside the rele
|
|
|
41
41
|
]},
|
|
42
42
|
LocalReadyUri:'http://127.0.0.1:8787/healthz',UrlRegex:'https://x',McpUrlFile:'C:/mcp-url.txt',McpUrlFileSha256:EXE,McpPathFile:'C:/token.txt',StartTimeoutSeconds:30,ProbeTimeoutSeconds:3};
|
|
43
43
|
const coding={schema:'deadbyte.coding-policy.v1',enabled:true,lease_file:'C:/old/coding.json',evidence_dir:'C:/old/evidence',
|
|
44
|
-
roots:{core:{path:'C:/old/core',read:true,write:true,exec:true,deny_paths:['node_modules']}
|
|
44
|
+
roots:{core:{path:'C:/old/core',read:true,write:true,exec:true,deny_paths:['node_modules']},
|
|
45
|
+
local_c:{path:'C:\\',read:true,write:true,exec:true,deny_paths:[]},
|
|
46
|
+
local_d:{path:'D:\\',read:true,write:true,exec:true,deny_paths:[]}},
|
|
47
|
+
commands:{tests:command(),powershell_script:{...command(),root_ids:['core','local_c','local_d']}},max_read_bytes:524288,max_write_bytes:2097152};
|
|
45
48
|
const autonomous={schema:'deadbyte.autonomous-policy.v1',enabled:true,lease_file:'C:/old/auto.json',evidence_dir:'C:/old/auto-evidence',coding_policy_sha256:'0'.repeat(64),root_ids:['core'],planner:{backend:'client_sampling',max_tokens:4096},
|
|
46
49
|
profiles:{
|
|
47
50
|
gate:{kind:'command',root_id:'core',command_id:'tests',script_path:'scripts/gate.ps1',script_sha256:'0'.repeat(64),args:[],cwd:'.',timeout_ms:120000,expect_exit_code:0,required_for_release:true,run_after_mutation:true,description:'gate'},
|
|
@@ -97,6 +100,10 @@ test('R27 generation bundle rebinds authority and mutable state outside the rele
|
|
|
97
100
|
const generatedCoding=JSON.parse(await readFile(result.coding_policy_path,'utf8'));
|
|
98
101
|
assert.equal(generatedCoding.roots.core.path,path.resolve(development));
|
|
99
102
|
assert.notEqual(generatedCoding.roots.core.path,path.resolve(release));
|
|
103
|
+
assert.equal(Object.hasOwn(generatedCoding.roots,'local_c'),false);
|
|
104
|
+
assert.equal(Object.hasOwn(generatedCoding.roots,'local_d'),false);
|
|
105
|
+
assert.deepEqual(generatedCoding.commands.powershell_script.root_ids,['core']);
|
|
106
|
+
assert.ok(Object.hasOwn(coding.roots,'local_c'),'predecessor policy must remain untouched');
|
|
100
107
|
const parityScratch=path.join(path.resolve(state),'runtime','r33-parity-scratch');
|
|
101
108
|
assert.deepEqual(generatedCoding.roots.parity_scratch,{path:parityScratch,read:true,write:true,exec:false,deny_paths:[]});
|
|
102
109
|
await access(parityScratch);
|