deadbyte-mcp 0.11.0 → 0.11.2
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 +75 -59
- package/README.txt +7 -7
- package/bin/WORKER-REGISTRY.txt +3 -3
- package/bin/appcontainer-stage.exe +0 -0
- package/bin/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.exe +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.exe +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.exe +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.exe +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.exe +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.exe +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.exe +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.exe +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.exe +0 -0
- package/bin/deadbyte-contain.obj +0 -0
- package/bin/deadbyte-exec.exe +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-process-host.exe +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.exe +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/controller/README.TXT +1 -1
- package/controller/deadbyte-controller.ps1 +4 -4
- package/controller/deadbyte-process-policy.json +1 -1
- package/docs/ARCHITECTURE.md +1 -1
- package/package.json +6 -6
- package/scripts/build-containment.ps1 +15 -15
- package/scripts/deferred-slot-operation-r37-0110.mjs +124 -0
- package/scripts/deferred-slot-operation-r38.mjs +124 -0
- package/scripts/deferred-slot-operation.mjs +1 -1
- package/scripts/final-closure-r37-0110.mjs +113 -0
- package/scripts/final-closure-r38.mjs +113 -0
- package/scripts/final-closure-verify-r37-0110.mjs +208 -0
- package/scripts/final-closure-verify-r38.mjs +208 -0
- package/scripts/final-closure-verify.mjs +16 -16
- package/scripts/final-closure.mjs +8 -8
- package/scripts/gate-windows-r37-0110.ps1 +42 -0
- package/scripts/gate-windows-r38.ps1 +42 -0
- package/scripts/gate-windows.ps1 +3 -3
- package/scripts/release-parity-r37-0110.mjs +200 -0
- package/scripts/release-parity-r38.mjs +200 -0
- package/scripts/release-parity-tests-r38.ps1 +9 -0
- package/scripts/release-parity-tests.ps1 +1 -1
- package/scripts/release-parity.mjs +14 -14
- package/scripts/windows-gate-evidence-r37-0110.mjs +52 -0
- package/scripts/windows-gate-evidence-r38.mjs +52 -0
- package/scripts/windows-gate-evidence.mjs +4 -4
- package/src/deadbyte-cli.mjs +58 -23
- package/src/release-generation.mjs +18 -1
- package/src/remote-live-log.mjs +23 -1
- package/src/remote-result-journal.mjs +23 -4
- package/src/version.mjs +1 -1
- package/test/cli-entrypoint.test.mjs +9 -0
- package/test/containment.test.mjs +18 -0
- package/test/controller.test.mjs +11 -0
- package/test/core.test.mjs +1 -1
- package/test/production-docs.test.mjs +3 -3
- package/test/r33-closeout-regression.test.mjs +8 -8
- package/test/r33-finalization.test.mjs +14 -14
- package/test/r36-release-identity.test.mjs +4 -4
- package/test/r37-0110-history.test.mjs +20 -0
- package/test/r37-finalization.test.mjs +14 -14
- package/test/r37-remote-tool-call-timeline.test.mjs +94 -0
- package/test/r38-finalization.test.mjs +74 -0
- package/test/release-generation.test.mjs +13 -3
- package/test/release-version.test.mjs +45 -33
- package/test/remote-live-log-r37.test.mjs +5 -2
- package/test/remote-session-cli.test.mjs +66 -25
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, open, readFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const root=path.resolve(here,'..');
|
|
9
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
10
|
+
const marker='DEADBYTE V0.11.2 / R38 WINDOWS BATCH GATE: PASS';
|
|
11
|
+
function insist(ok,message){if(!ok) throw new Error(message);}
|
|
12
|
+
|
|
13
|
+
export async function writeWindowsGateEvidence({
|
|
14
|
+
candidateRoot=root,
|
|
15
|
+
stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp')),
|
|
16
|
+
clock=()=>new Date()
|
|
17
|
+
}={}){
|
|
18
|
+
const candidate=path.resolve(candidateRoot);
|
|
19
|
+
const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
20
|
+
const manifestSha256=sha256(manifestBytes);
|
|
21
|
+
const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
|
|
22
|
+
insist(pkg.version==='0.11.2',`Windows gate evidence requires V0.11.2, found ${pkg.version}`);
|
|
23
|
+
const completed=clock();
|
|
24
|
+
insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'Windows gate evidence clock invalid');
|
|
25
|
+
const evidence={
|
|
26
|
+
schema:'deadbyte.r38-windows-gate.v1',status:'passed',exit_code:0,version:'0.11.2',
|
|
27
|
+
marker,completed_at_utc:completed.toISOString(),manifest_sha256:manifestSha256
|
|
28
|
+
};
|
|
29
|
+
const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');
|
|
30
|
+
const dir=path.join(path.resolve(stateRoot),'evidence','release');
|
|
31
|
+
await mkdir(dir,{recursive:true});
|
|
32
|
+
const file=path.join(dir,`r38-windows-gate-${manifestSha256.slice(0,16)}.json`);
|
|
33
|
+
try{
|
|
34
|
+
const handle=await open(file,'wx',0o600);
|
|
35
|
+
try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
|
|
36
|
+
return {status:'written',path:file,sha256:sha256(bytes),manifest_sha256:manifestSha256};
|
|
37
|
+
}catch(error){
|
|
38
|
+
if(error?.code!=='EEXIST') throw error;
|
|
39
|
+
const existingBytes=await readFile(file);
|
|
40
|
+
const existing=JSON.parse(existingBytes.toString('utf8'));
|
|
41
|
+
insist(existing.schema===evidence.schema&&existing.status==='passed'&&existing.exit_code===0,'existing Windows gate evidence invalid');
|
|
42
|
+
insist(existing.version==='0.11.2'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing Windows gate evidence binding mismatch');
|
|
43
|
+
return {status:'existing',path:file,sha256:sha256(existingBytes),manifest_sha256:manifestSha256};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
|
|
48
|
+
if(invoked){
|
|
49
|
+
writeWindowsGateEvidence()
|
|
50
|
+
.then(result=>console.log(JSON.stringify(result)))
|
|
51
|
+
.catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
52
|
+
}
|
|
@@ -7,7 +7,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
7
7
|
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
8
8
|
const root=path.resolve(here,'..');
|
|
9
9
|
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
10
|
-
const marker='DEADBYTE V0.11.
|
|
10
|
+
const marker='DEADBYTE V0.11.1 / R37 WINDOWS BATCH GATE: PASS';
|
|
11
11
|
function insist(ok,message){if(!ok) throw new Error(message);}
|
|
12
12
|
|
|
13
13
|
export async function writeWindowsGateEvidence({
|
|
@@ -19,11 +19,11 @@ export async function writeWindowsGateEvidence({
|
|
|
19
19
|
const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
20
20
|
const manifestSha256=sha256(manifestBytes);
|
|
21
21
|
const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
|
|
22
|
-
insist(pkg.version==='0.11.
|
|
22
|
+
insist(pkg.version==='0.11.1',`Windows gate evidence requires V0.11.1, found ${pkg.version}`);
|
|
23
23
|
const completed=clock();
|
|
24
24
|
insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'Windows gate evidence clock invalid');
|
|
25
25
|
const evidence={
|
|
26
|
-
schema:'deadbyte.r37-windows-gate.v1',status:'passed',exit_code:0,version:'0.11.
|
|
26
|
+
schema:'deadbyte.r37-windows-gate.v1',status:'passed',exit_code:0,version:'0.11.1',
|
|
27
27
|
marker,completed_at_utc:completed.toISOString(),manifest_sha256:manifestSha256
|
|
28
28
|
};
|
|
29
29
|
const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');
|
|
@@ -39,7 +39,7 @@ export async function writeWindowsGateEvidence({
|
|
|
39
39
|
const existingBytes=await readFile(file);
|
|
40
40
|
const existing=JSON.parse(existingBytes.toString('utf8'));
|
|
41
41
|
insist(existing.schema===evidence.schema&&existing.status==='passed'&&existing.exit_code===0,'existing Windows gate evidence invalid');
|
|
42
|
-
insist(existing.version==='0.11.
|
|
42
|
+
insist(existing.version==='0.11.1'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing Windows gate evidence binding mismatch');
|
|
43
43
|
return {status:'existing',path:file,sha256:sha256(existingBytes),manifest_sha256:manifestSha256};
|
|
44
44
|
}
|
|
45
45
|
}
|
package/src/deadbyte-cli.mjs
CHANGED
|
@@ -9,14 +9,17 @@ import { fileURLToPath } from 'node:url';
|
|
|
9
9
|
import { defaultPublicKeyPath } from './trust.mjs';
|
|
10
10
|
import { verifyAuditChain } from './audit-verify.mjs';
|
|
11
11
|
import { openRemoteResultJournal, parseRemoteResultCursor } from './remote-result-journal.mjs';
|
|
12
|
-
import {
|
|
12
|
+
import { DEADBYTE_VERSION } from './version.mjs';
|
|
13
|
+
import { createLiveTimelineTracker, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine } from './remote-live-log.mjs';
|
|
13
14
|
|
|
14
15
|
const REMOTE_COMMANDS=new Set([
|
|
15
16
|
'menu','start','resume','park','stop','restart','status','url','test','fulltest','toggle','clean','machinetest',
|
|
16
17
|
'armprocess','disarmprocess','processstatus','armhost','disarmhost','hoststatus','hosttest',
|
|
17
18
|
'armcoding','disarmcoding','codingstatus','codingtest','armautonomous','disarmautonomous','autonomousstatus','autonomoustest'
|
|
18
19
|
]);
|
|
19
|
-
const
|
|
20
|
+
const SESSION_ARM=['armprocess','armcoding','armautonomous'];
|
|
21
|
+
const REMOTE_STATUS_TIMEOUT_MS=30000;
|
|
22
|
+
const REMOTE_START_TIMEOUT_MS=150000;
|
|
20
23
|
const SESSION_STATUS=[
|
|
21
24
|
['processstatus',/^Process plane\s*:\s*ARMED\b/m],
|
|
22
25
|
['codingstatus',/^Coding plane\s*:\s*ARMED\b/m],
|
|
@@ -52,11 +55,16 @@ async function remoteContext(){
|
|
|
52
55
|
const powershell=await requireRegular(path.join(systemRoot,'System32','WindowsPowerShell','v1.0','powershell.exe'),'Windows PowerShell');
|
|
53
56
|
return {root,launcher,powershell};
|
|
54
57
|
}
|
|
55
|
-
function invokeController(ctx,command,{capture=false,allowFailure=false,echoCaptured=true}={}){
|
|
56
|
-
const
|
|
58
|
+
function invokeController(ctx,command,{capture=false,allowFailure=false,echoCaptured=true,timeoutMs=null}={}){
|
|
59
|
+
const options={
|
|
57
60
|
stdio:capture?['ignore','pipe','pipe']:'inherit',encoding:capture?'utf8':undefined,windowsHide:false,shell:false
|
|
58
|
-
}
|
|
59
|
-
if(
|
|
61
|
+
};
|
|
62
|
+
if(Number.isInteger(timeoutMs)&&timeoutMs>0) options.timeout=timeoutMs;
|
|
63
|
+
const result=spawnSync(ctx.powershell,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',ctx.launcher,command],options);
|
|
64
|
+
if(result.error){
|
|
65
|
+
if(result.error?.code==='ETIMEDOUT') throw new Error(`remote controller '${command}' timed out after ${timeoutMs}ms`);
|
|
66
|
+
throw result.error;
|
|
67
|
+
}
|
|
60
68
|
const status=Number.isInteger(result.status)?result.status:1;
|
|
61
69
|
const stdout=capture?String(result.stdout||''):'';
|
|
62
70
|
const stderr=capture?String(result.stderr||''):'';
|
|
@@ -64,6 +72,14 @@ function invokeController(ctx,command,{capture=false,allowFailure=false,echoCapt
|
|
|
64
72
|
if(status!==0&&!allowFailure) throw new Error(`remote controller '${command}' failed exit=${status}`);
|
|
65
73
|
return {status,stdout,stderr};
|
|
66
74
|
}
|
|
75
|
+
function controllerRuntimeReady(text){
|
|
76
|
+
const value=String(text||'');
|
|
77
|
+
return /^DEADBYTE MCP\s*=\s*ONLINE\s*$/m.test(value)&&
|
|
78
|
+
/^Bridge PID\s*:\s*\d+\s+\[verified\]\s*$/m.test(value)&&
|
|
79
|
+
/^Cloud PID\s*:\s*\d+\s+\[verified\]\s*$/m.test(value)&&
|
|
80
|
+
/^Local ready\s*:\s*True\s*$/m.test(value)&&
|
|
81
|
+
/^Loop supervisor:\s*RUNNING\s+\[verified\]\s*$/m.test(value);
|
|
82
|
+
}
|
|
67
83
|
async function listLogFiles(root){
|
|
68
84
|
const files=[];
|
|
69
85
|
const audit=path.join(root,'audit');
|
|
@@ -88,13 +104,17 @@ function formatLogLine(file,line,{activityTracker=null}={}){
|
|
|
88
104
|
return formatRuntimeDiagnosticLine(file,line);
|
|
89
105
|
}
|
|
90
106
|
function remoteResultJournalPath(root,sessionId){return path.join(root,'runtime','remote-results',`${sessionId}.json`);}
|
|
91
|
-
function
|
|
107
|
+
function auditTimelineRecord(file,line){
|
|
92
108
|
if(!/^segment-\d{8}\.jsonl$/.test(path.basename(file))||!line) return null;
|
|
93
109
|
let event;try{event=JSON.parse(line);}catch{return null;}
|
|
94
|
-
if(
|
|
110
|
+
if(typeof event?.request_id!=='string'||!event.request_id||typeof event.tool!=='string'||!event.tool) return null;
|
|
95
111
|
const observed=Number.isFinite(Date.parse(event.created_at_utc))?new Date(Date.parse(event.created_at_utc)).toISOString():new Date().toISOString();
|
|
96
|
-
|
|
97
|
-
|
|
112
|
+
const payload_sha256=createHash('sha256').update(Buffer.from(line,'utf8')).digest('hex');
|
|
113
|
+
if(event.event_type==='tool.request') return {event_type:'tool.call',request_id:event.request_id,tool:event.tool,
|
|
114
|
+
observed_at_utc:observed,arguments:event.args??{},payload_sha256};
|
|
115
|
+
if(event.event_type==='tool.result') return {event_type:'tool.result',request_id:event.request_id,tool:event.tool,
|
|
116
|
+
status:event.outcome?.is_error===true?'error':'ok',observed_at_utc:observed,payload_sha256};
|
|
117
|
+
return null;
|
|
98
118
|
}
|
|
99
119
|
async function replayRemoteResults(root,cursor){
|
|
100
120
|
const parsed=parseRemoteResultCursor(cursor);
|
|
@@ -105,6 +125,12 @@ async function replayRemoteResults(root,cursor){
|
|
|
105
125
|
for(;;){
|
|
106
126
|
const page=await journal.replayAfter(next,{limit:200});
|
|
107
127
|
for(const event of page.events){
|
|
128
|
+
if(event.event_type==='tool.call'){
|
|
129
|
+
const line=formatRemoteToolCall({event_type:'tool.request',tool:event.tool,request_id:event.request_id,
|
|
130
|
+
observed_at_utc:event.observed_at_utc,args:event.arguments});
|
|
131
|
+
process.stdout.write(`${line} | REPLAY cursor r1:${event.session_id}:${event.seq}\n`);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
108
134
|
const marker=event.status==='error'?'[ERR]':'[OK]';
|
|
109
135
|
process.stdout.write(`[REPLAY] ${marker} ${event.tool} [cursor r1:${event.session_id}:${event.seq}]\n`);
|
|
110
136
|
}
|
|
@@ -139,9 +165,11 @@ async function tailRemoteLogs(root,signal,{journal=null,offsets=null,activityTra
|
|
|
139
165
|
const next=await readAppend(file,prior);
|
|
140
166
|
activeOffsets.set(file,next.offset);
|
|
141
167
|
for(const line of next.text.split(/\r?\n/)){
|
|
142
|
-
const
|
|
168
|
+
const timeline=auditTimelineRecord(file,line);
|
|
143
169
|
let recorded=null;
|
|
144
|
-
if(
|
|
170
|
+
if(timeline&&journal) recorded=timeline.event_type==='tool.call'
|
|
171
|
+
? await journal.recordCall(timeline)
|
|
172
|
+
: await journal.recordTerminal(timeline);
|
|
145
173
|
if(recorded?.duplicate) continue;
|
|
146
174
|
const formatted=formatLogLine(file,line,{activityTracker});
|
|
147
175
|
if(!formatted) continue;
|
|
@@ -257,18 +285,21 @@ async function runRemoteSession({resumeCursor=null}={}){
|
|
|
257
285
|
const logOffsets=await snapshotRemoteLogOffsets(ctx.root);
|
|
258
286
|
logTask=tailRemoteLogs(ctx.root,aborter.signal,{journal:resultJournal,offsets:logOffsets,activityTracker});
|
|
259
287
|
process.stdout.write('[BOOT] Starting local + remote runtime...\n');
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
288
|
+
const preflight=invokeController(ctx,'status',{capture:true,allowFailure:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
|
|
289
|
+
let startText=preflight.stdout||'';
|
|
290
|
+
if(preflight.status===0&&controllerRuntimeReady(preflight.stdout)){
|
|
291
|
+
process.stdout.write('[BOOT] Runtime already ONLINE; reusing verified bridge/cloud/supervisor.\n');
|
|
292
|
+
}else{
|
|
293
|
+
process.stdout.write('[BOOT] Starting bridge/runtime...\n');
|
|
294
|
+
const started=invokeController(ctx,'start',{capture:true,echoCaptured:false,timeoutMs:REMOTE_START_TIMEOUT_MS});
|
|
295
|
+
startText=started.stdout||'';
|
|
296
|
+
}
|
|
297
|
+
process.stdout.write(`${formatRemoteLifecycle('bridge_connected')}\n`);
|
|
298
|
+
process.stdout.write(`${formatRemoteLifecycle('cloud_connected')}\n`);
|
|
299
|
+
for(const command of SESSION_ARM){
|
|
300
|
+
process.stdout.write(`🔐 [AUTH] Arming ${command.slice(3)}...\n`);
|
|
301
|
+
invokeController(ctx,command,{capture:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
|
|
270
302
|
}
|
|
271
|
-
const startText=started[0]?.stdout||'';
|
|
272
303
|
const url=startText.match(/^URL\s*:\s*(\S+)/m)?.[1]||'';
|
|
273
304
|
process.stdout.write('[OK] ONLINE\n');
|
|
274
305
|
process.stdout.write(`${formatRemoteLifecycle('ready')}\n`);
|
|
@@ -320,6 +351,10 @@ async function runAudit(args){
|
|
|
320
351
|
}
|
|
321
352
|
async function main(argv){
|
|
322
353
|
const [command,...rest]=argv;
|
|
354
|
+
if(command==='--version'||command==='-v'||command==='version'){
|
|
355
|
+
if(rest.length) throw new Error('version accepts no arguments');
|
|
356
|
+
process.stdout.write(`${DEADBYTE_VERSION}\n`);return;
|
|
357
|
+
}
|
|
323
358
|
if(!command||command==='help'||command==='--help'||command==='-h'){printHelp();return;}
|
|
324
359
|
if(command==='mcp'){
|
|
325
360
|
if(rest.length) throw new Error('mcp accepts no arguments');
|
|
@@ -60,16 +60,33 @@ function rebindCodingPolicy(raw,{developmentRoot,stateRoot,parityScratchRoot}){
|
|
|
60
60
|
policy.roots.parity_scratch={path:parityScratchRoot,read:true,write:true,exec:false,deny_paths:[]};
|
|
61
61
|
return policy;
|
|
62
62
|
}
|
|
63
|
+
async function currentWindowsGateScript(rootPath){
|
|
64
|
+
const packagePath=path.join(rootPath,'package.json');
|
|
65
|
+
const stat=await lstat(packagePath);
|
|
66
|
+
if(!stat.isFile()||stat.isSymbolicLink()) throw new Error('windows release gate package.json invalid');
|
|
67
|
+
let pkg;
|
|
68
|
+
try{ pkg=JSON.parse(await readFile(packagePath,'utf8')); }
|
|
69
|
+
catch{ throw new Error('windows release gate package.json malformed'); }
|
|
70
|
+
const command=String(pkg?.scripts?.['gate:windows']??'').trim();
|
|
71
|
+
const match=/^powershell(?:\.exe)?\s+-NoProfile\s+-ExecutionPolicy\s+Bypass\s+-File\s+(scripts\/[A-Za-z0-9._\/-]+\.ps1)$/i.exec(command);
|
|
72
|
+
if(!match) throw new Error('windows release gate package script invalid');
|
|
73
|
+
return safeRel(match[1]);
|
|
74
|
+
}
|
|
63
75
|
async function rebindAutonomousPolicy(raw,{stateRoot,codingPolicy,codingPolicyBytes}){
|
|
64
76
|
const policy=clone(raw);
|
|
65
77
|
policy.lease_file=path.join(stateRoot,'runtime','autonomous-grant.json');
|
|
66
78
|
policy.evidence_dir=path.join(stateRoot,'evidence','autonomous');
|
|
67
79
|
policy.coding_policy_sha256=sha256(codingPolicyBytes);
|
|
68
80
|
for(const [id,profile] of Object.entries(policy.profiles??{})){
|
|
69
|
-
const rel=safeRel(String(profile.script_path).replaceAll('\\','/'));
|
|
70
81
|
const rootId=String(profile.root_id??'');
|
|
71
82
|
const rootPath=codingPolicy.roots?.[rootId]?.path;
|
|
72
83
|
if(typeof rootPath!=='string'||!path.isAbsolute(rootPath)) throw new Error(`profile root invalid: ${id}`);
|
|
84
|
+
if(id==='windows-release'){
|
|
85
|
+
if(profile.kind!=='command'||rootId!=='core'||profile.command_id!=='powershell_script') throw new Error('windows release profile shape invalid');
|
|
86
|
+
profile.script_path=await currentWindowsGateScript(rootPath);
|
|
87
|
+
profile.description='Full Windows target release gate; runs only when finish is requested.';
|
|
88
|
+
}
|
|
89
|
+
const rel=safeRel(String(profile.script_path).replaceAll('\\','/'));
|
|
73
90
|
const file=path.resolve(rootPath,...rel.split('/'));
|
|
74
91
|
const boundary=path.relative(rootPath,file);
|
|
75
92
|
if(boundary.startsWith(`..${path.sep}`)||path.isAbsolute(boundary)) throw new Error(`profile path escapes coding root: ${id}`);
|
package/src/remote-live-log.mjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { canonicalJson } from './canonical-json.mjs';
|
|
2
|
+
import { sanitizeAuditArgs } from './tool-audit.mjs';
|
|
3
|
+
|
|
1
4
|
const SAFE_ARG_KEYS=Object.freeze(['root_id','path','pattern','command_id','profile_id','session_id','goal_id','task_id','step_id','mode']);
|
|
2
5
|
const LIFECYCLE=Object.freeze({
|
|
3
6
|
device_start:'🚀 [DEVICE] Starting DEADBYTE MCP...',
|
|
@@ -30,6 +33,25 @@ function summarizeArgs(args){
|
|
|
30
33
|
return parts.length?` ${parts.join(' ')}`:'';
|
|
31
34
|
}
|
|
32
35
|
|
|
36
|
+
const TOOL_COLUMN_WIDTH=32;
|
|
37
|
+
|
|
38
|
+
function eventTimestamp(event,clock){
|
|
39
|
+
for(const value of [event?.created_at_utc,event?.observed_at_utc]){
|
|
40
|
+
const parsed=Date.parse(value);
|
|
41
|
+
if(typeof value==='string'&&Number.isFinite(parsed)) return new Date(parsed).toISOString();
|
|
42
|
+
}
|
|
43
|
+
const value=Number(clock());
|
|
44
|
+
return new Date(Number.isFinite(value)?value:Date.now()).toISOString();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function formatRemoteToolCall(event,{clock=()=>Date.now()}={}){
|
|
48
|
+
if(!event||typeof event!=='object'||Array.isArray(event)||event.event_type!=='tool.request'||
|
|
49
|
+
typeof event.tool!=='string'||!event.tool) return null;
|
|
50
|
+
if(typeof clock!=='function') throw new Error('remote tool-call clock must be a function');
|
|
51
|
+
const args=sanitizeAuditArgs(event.args??{});
|
|
52
|
+
return `${eventTimestamp(event,clock)} | ${event.tool.padEnd(TOOL_COLUMN_WIDTH)} | Arguments: ${canonicalJson(args)}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
33
55
|
function formatDuration(ms){
|
|
34
56
|
const value=Number(ms);
|
|
35
57
|
if(!Number.isFinite(value)||value<0) return '';
|
|
@@ -92,7 +114,7 @@ export function createLiveTimelineTracker({clock=()=>Date.now()}={}){
|
|
|
92
114
|
if(event.event_type==='tool.request'&&requestId&&tool){
|
|
93
115
|
if(pending.has(requestId)||completed.has(requestId)) return null;
|
|
94
116
|
pending.set(requestId,{request_id:requestId,tool,started_at_ms:clock()});
|
|
95
|
-
return
|
|
117
|
+
return formatRemoteToolCall(event,{clock});
|
|
96
118
|
}
|
|
97
119
|
if(event.event_type==='tool.result'&&requestId&&tool){
|
|
98
120
|
if(completed.has(requestId)) return null;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { lstat, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { sanitizeAuditArgs } from './tool-audit.mjs';
|
|
3
4
|
|
|
4
5
|
export const REMOTE_RESULT_JOURNAL_SCHEMA='deadbyte.remote-result-journal.v1';
|
|
5
6
|
const SESSION=/^[0-9a-f]{24}$/;
|
|
6
7
|
const HASH=/^[0-9a-f]{64}$/;
|
|
7
8
|
const STATUS=new Set(['ok','error']);
|
|
9
|
+
const EVENT_TYPES=new Set(['tool.call','tool.result']);
|
|
8
10
|
|
|
9
11
|
function text(value,label,max){
|
|
10
12
|
if(typeof value!=='string'||value.length<1||value.length>max||value.includes('\0')) throw new Error(`${label} invalid`);
|
|
@@ -26,14 +28,22 @@ function validateEvent(raw,index=null){
|
|
|
26
28
|
const sessionId=validateSession(raw.session_id);
|
|
27
29
|
const requestId=text(raw.request_id,'remote result request_id',256);
|
|
28
30
|
const tool=text(raw.tool,'remote result tool',128);
|
|
29
|
-
|
|
31
|
+
const eventType=raw.event_type===undefined?'tool.result':raw.event_type;
|
|
32
|
+
if(!EVENT_TYPES.has(eventType)) throw new Error('remote result event_type invalid');
|
|
30
33
|
const observed=iso(raw.observed_at_utc,'remote result observed_at_utc');
|
|
31
34
|
if(typeof raw.payload_sha256!=='string'||!HASH.test(raw.payload_sha256)) throw new Error('remote result payload_sha256 invalid');
|
|
32
35
|
if(index!==null&&seq!==index) throw new Error('remote result sequence mismatch');
|
|
33
|
-
|
|
36
|
+
if(eventType==='tool.call'){
|
|
37
|
+
const rawArgs=raw.arguments??{};
|
|
38
|
+
if(!rawArgs||typeof rawArgs!=='object'||Array.isArray(rawArgs)) throw new Error('remote result call arguments invalid');
|
|
39
|
+
return Object.freeze({session_id:sessionId,seq,event_type:eventType,request_id:requestId,tool,
|
|
40
|
+
observed_at_utc:observed,payload_sha256:raw.payload_sha256,arguments:sanitizeAuditArgs(rawArgs)});
|
|
41
|
+
}
|
|
42
|
+
if(!STATUS.has(raw.status)) throw new Error('remote result status invalid');
|
|
43
|
+
return Object.freeze({session_id:sessionId,seq,event_type:eventType,request_id:requestId,tool,status:raw.status,
|
|
34
44
|
observed_at_utc:observed,payload_sha256:raw.payload_sha256});
|
|
35
45
|
}
|
|
36
|
-
function identity(item){return `${item.request_id}\0${item.status}\0${item.payload_sha256}`;}
|
|
46
|
+
function identity(item){return `${item.event_type}\0${item.request_id}\0${item.status??''}\0${item.payload_sha256}`;}
|
|
37
47
|
export function parseRemoteResultCursor(cursor){
|
|
38
48
|
if(typeof cursor!=='string') throw new Error('remote result cursor invalid');
|
|
39
49
|
const match=/^r1:([0-9a-f]{24}):(0|[1-9][0-9]{0,15})$/.exec(cursor);
|
|
@@ -89,8 +99,17 @@ export async function openRemoteResultJournal({filePath,sessionId,maxEntries=204
|
|
|
89
99
|
return Object.freeze({
|
|
90
100
|
file_path:file,session_id:sid,
|
|
91
101
|
cursor(){return cursorFor(sid,state.next_seq-1);},
|
|
102
|
+
async recordCall(input){
|
|
103
|
+
const candidate=validateEvent({session_id:sid,seq:state.next_seq,event_type:'tool.call',...input});
|
|
104
|
+
const key=identity(candidate);
|
|
105
|
+
const existing=state.events.find(event=>identity(event)===key);
|
|
106
|
+
if(existing) return {duplicate:true,event:existing,cursor:cursorFor(sid,state.next_seq-1)};
|
|
107
|
+
const event=candidate;
|
|
108
|
+
state.events.push(event);state.next_seq+=1;await persist();
|
|
109
|
+
return {duplicate:false,event,cursor:cursorFor(sid,event.seq)};
|
|
110
|
+
},
|
|
92
111
|
async recordTerminal(input){
|
|
93
|
-
const candidate=validateEvent({session_id:sid,seq:state.next_seq,...input});
|
|
112
|
+
const candidate=validateEvent({session_id:sid,seq:state.next_seq,event_type:'tool.result',...input});
|
|
94
113
|
const key=identity(candidate);
|
|
95
114
|
const existing=state.events.find(event=>identity(event)===key);
|
|
96
115
|
if(existing) return {duplicate:true,event:existing,cursor:cursorFor(sid,state.next_seq-1)};
|
package/src/version.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
4
5
|
import path from 'node:path';
|
|
5
6
|
import { fileURLToPath } from 'node:url';
|
|
6
7
|
|
|
@@ -16,6 +17,14 @@ test('package exposes npx-first deadbyte-mcp bin and is registry-publishable',as
|
|
|
16
17
|
assert.equal(pkg.publishConfig?.access,'public');
|
|
17
18
|
});
|
|
18
19
|
|
|
20
|
+
test('npx CLI reports the canonical package version through --version, -v and version',()=>{
|
|
21
|
+
for(const arg of ['--version','-v','version']){
|
|
22
|
+
const result=spawnSync(process.execPath,[cliPath,arg],{cwd:root,encoding:'utf8',windowsHide:true});
|
|
23
|
+
assert.equal(result.status,0,result.stderr);
|
|
24
|
+
assert.equal(result.stdout.trim(),'0.11.2');
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
19
28
|
test('npx CLI remote path invokes stable PS1 directly and never routes through CMD',async()=>{
|
|
20
29
|
const source=await readFile(cliPath,'utf8');
|
|
21
30
|
assert.match(source,/^#!\/usr\/bin\/env node/m);
|
|
@@ -230,6 +230,24 @@ test('native build script targets Windows 10 and preserves MSVC diagnostics', as
|
|
|
230
230
|
assert.doesNotMatch(source, /\$VsWhere = Join-Path \$\{env:ProgramFiles\(x86\)\}/);
|
|
231
231
|
});
|
|
232
232
|
|
|
233
|
+
test('native build is reproducible at both MSVC object and linker boundaries', async () => {
|
|
234
|
+
const buildScript = fileURLToPath(new URL('../scripts/build-containment.ps1', import.meta.url));
|
|
235
|
+
const source = await readFile(buildScript, 'utf8');
|
|
236
|
+
const lines = source.split(/\r?\n/);
|
|
237
|
+
const commonLine = lines.find(line => line.startsWith('$Common ='));
|
|
238
|
+
const bootstrapLine = lines.find(line => line.startsWith('$BootstrapCommon ='));
|
|
239
|
+
assert.ok(commonLine?.includes('/Brepro'), 'common compiler flags must include /Brepro');
|
|
240
|
+
assert.ok(bootstrapLine?.includes('/Brepro'), 'bootstrap compiler flags must include /Brepro');
|
|
241
|
+
for (const name of [
|
|
242
|
+
'Launcher','Probe','BootstrapKernel','BootstrapAdvapi','StageLauncher','BootstrapExit',
|
|
243
|
+
'ChildControlStage','ChildControlProbe','ContainedLauncher','ContainedWorker',
|
|
244
|
+
'ContainedReverseWorker','TunnelHost','ProcessHost'
|
|
245
|
+
]) {
|
|
246
|
+
const line = lines.find(candidate => candidate.startsWith(`$${name}Command =`));
|
|
247
|
+
assert.ok(line?.includes('/link /Brepro /INCREMENTAL:NO'), `${name} linker flags must be reproducible and non-incremental`);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
233
251
|
|
|
234
252
|
|
|
235
253
|
test('native build command composition is Windows PowerShell 5.1 parser-safe', async () => {
|
package/test/controller.test.mjs
CHANGED
|
@@ -215,3 +215,14 @@ test('R34 supervisor lifecycle never arms privileged planes', async () => {
|
|
|
215
215
|
assert.match(block,/Disarm-Process/);
|
|
216
216
|
assert.match(block,/Disarm-Host/);
|
|
217
217
|
});
|
|
218
|
+
|
|
219
|
+
test('supervisor recovery never assigns PowerShell automatic variable $Host', async () => {
|
|
220
|
+
const source=await readFile(controller,'utf8');
|
|
221
|
+
const start=source.indexOf('function Recover-AutonomousSupervisor');
|
|
222
|
+
const end=source.indexOf('function Show-State',start);
|
|
223
|
+
assert.ok(start>=0&&end>start,'supervisor recovery block missing');
|
|
224
|
+
const block=source.slice(start,end);
|
|
225
|
+
assert.doesNotMatch(block,/\$host\s*=/i,'PowerShell $Host is read-only and names are case-insensitive');
|
|
226
|
+
assert.match(block,/\$hostStatus\s*=\s*Get-HostArmStatus\s+\$Cfg/);
|
|
227
|
+
assert.match(block,/Host=\$hostStatus/);
|
|
228
|
+
});
|
package/test/core.test.mjs
CHANGED
|
@@ -105,7 +105,7 @@ test('proof execution creates deterministic output and signed verifiable receipt
|
|
|
105
105
|
assert.equal(result.receipt.request.schema, 'deadbyte.request.v1');
|
|
106
106
|
assert.match(result.receipt.request.nonce, /^[0-9a-f]{64}$/);
|
|
107
107
|
assert.match(result.receipt.request.sha256, /^[0-9a-f]{64}$/);
|
|
108
|
-
assert.equal(result.receipt.runtime.package_version, '0.11.
|
|
108
|
+
assert.equal(result.receipt.runtime.package_version, '0.11.2');
|
|
109
109
|
assert.match(result.receipt.runtime.mcp_ingress_adapter, /^(?:none|tunnel-stdio-probe-compat-v1)$/);
|
|
110
110
|
assert.match(result.receipt.runtime.mcp_ingress_adapter_sha256, /^[0-9a-f]{64}$/);
|
|
111
111
|
assert.match(result.receipt.runtime.mcp_ingress_mode_source_sha256, /^[0-9a-f]{64}$/);
|
|
@@ -9,9 +9,9 @@ const readme=()=>readFile(path.join(root,'README.txt'),'utf8');
|
|
|
9
9
|
|
|
10
10
|
test('production README documents one-command stdio configs for major MCP clients',async()=>{
|
|
11
11
|
const text=await readme();
|
|
12
|
-
assert.match(text,/npx -y deadbyte-mcp@0\.11\.
|
|
12
|
+
assert.match(text,/npx -y deadbyte-mcp@0\.11\.2 mcp/);
|
|
13
13
|
assert.match(text,/CLAUDE DESKTOP/);
|
|
14
|
-
assert.match(text,/"mcpServers"[\s\S]*?"command"\s*:\s*"npx"[\s\S]*?"-y"[\s\S]*?"deadbyte-mcp@0\.11\.
|
|
14
|
+
assert.match(text,/"mcpServers"[\s\S]*?"command"\s*:\s*"npx"[\s\S]*?"-y"[\s\S]*?"deadbyte-mcp@0\.11\.2"[\s\S]*?"mcp"/);
|
|
15
15
|
assert.match(text,/CURSOR/);
|
|
16
16
|
assert.match(text,/\.cursor\/mcp\.json/);
|
|
17
17
|
assert.match(text,/VS CODE/);
|
|
@@ -20,7 +20,7 @@ test('production README documents one-command stdio configs for major MCP client
|
|
|
20
20
|
assert.match(text,/CODEX/);
|
|
21
21
|
assert.match(text,/\[mcp_servers\.deadbyte\]/);
|
|
22
22
|
assert.match(text,/command\s*=\s*"npx"/);
|
|
23
|
-
assert.match(text,/args\s*=\s*\["-y",\s*"deadbyte-mcp@0\.11\.
|
|
23
|
+
assert.match(text,/args\s*=\s*\["-y",\s*"deadbyte-mcp@0\.11\.2",\s*"mcp"\]/);
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
test('production README records tracer bullet, semantic scope, remote UX, safety and document extension',async()=>{
|
|
@@ -5,7 +5,7 @@ import { mkdtemp, mkdir, readFile, rm, writeFile, readdir } from 'node:fs/promis
|
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
|
-
import * as parity from '../scripts/release-parity.mjs';
|
|
8
|
+
import * as parity from '../scripts/release-parity-r38.mjs';
|
|
9
9
|
import { createToolAudit } from '../src/tool-audit.mjs';
|
|
10
10
|
import { verifyAuditChain } from '../src/audit-verify.mjs';
|
|
11
11
|
import { generateTrustKeypair } from '../src/trust.mjs';
|
|
@@ -86,12 +86,12 @@ test('audit restart reconciles an orphan request with an explicit interrupted re
|
|
|
86
86
|
}finally{await rm(stateRoot,{recursive:true,force:true});}
|
|
87
87
|
});
|
|
88
88
|
|
|
89
|
-
test('Windows
|
|
89
|
+
test('Windows R38 gate emits manifest-bound machine-readable evidence outside the candidate tree', async()=>{
|
|
90
90
|
const stateRoot=await temp('deadbyte-r33-windows-evidence-');
|
|
91
91
|
try{
|
|
92
|
-
const gate=await readFile(path.join(root,'scripts','gate-windows.ps1'),'utf8');
|
|
93
|
-
assert.match(gate,/windows-gate-evidence\.mjs/,'gate-windows.ps1 must invoke the evidence writer');
|
|
94
|
-
const result=await run(process.execPath,[path.join(root,'scripts','windows-gate-evidence.mjs')],{
|
|
92
|
+
const gate=await readFile(path.join(root,'scripts','gate-windows-r38.ps1'),'utf8');
|
|
93
|
+
assert.match(gate,/windows-gate-evidence-r38\.mjs/,'gate-windows-r38.ps1 must invoke the evidence writer');
|
|
94
|
+
const result=await run(process.execPath,[path.join(root,'scripts','windows-gate-evidence-r38.mjs')],{
|
|
95
95
|
cwd:root,env:{...process.env,DEADBYTE_STATE_ROOT:stateRoot}
|
|
96
96
|
});
|
|
97
97
|
assert.equal(result.code,0,result.stderr||result.stdout);
|
|
@@ -99,12 +99,12 @@ test('Windows R37 gate emits manifest-bound machine-readable evidence outside th
|
|
|
99
99
|
assert.equal(summary.status,'written');
|
|
100
100
|
const evidence=JSON.parse(await readFile(summary.path,'utf8'));
|
|
101
101
|
const manifestSha=sha256(await readFile(path.join(root,'MANIFEST.SHA256')));
|
|
102
|
-
assert.equal(evidence.schema,'deadbyte.
|
|
102
|
+
assert.equal(evidence.schema,'deadbyte.r38-windows-gate.v1');
|
|
103
103
|
assert.equal(evidence.status,'passed');
|
|
104
104
|
assert.equal(evidence.exit_code,0);
|
|
105
|
-
assert.equal(evidence.version,'0.11.
|
|
105
|
+
assert.equal(evidence.version,'0.11.2');
|
|
106
106
|
assert.equal(evidence.manifest_sha256,manifestSha);
|
|
107
|
-
assert.equal(evidence.marker,'DEADBYTE V0.11.
|
|
107
|
+
assert.equal(evidence.marker,'DEADBYTE V0.11.2 / R38 WINDOWS BATCH GATE: PASS');
|
|
108
108
|
assert.ok(summary.path.startsWith(path.join(stateRoot,'evidence','release')));
|
|
109
109
|
}finally{await rm(stateRoot,{recursive:true,force:true});}
|
|
110
110
|
});
|
|
@@ -2,8 +2,8 @@ import test from 'node:test';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
4
|
import { R34_EXPECTED_TOOLS } from '../src/remote-mcp-r34-parity-client.mjs';
|
|
5
|
-
import { validateParityEvidence } from '../scripts/release-parity.mjs';
|
|
6
|
-
import { verifyFinalClosureObject } from '../scripts/final-closure-verify.mjs';
|
|
5
|
+
import { validateParityEvidence } from '../scripts/release-parity-r38.mjs';
|
|
6
|
+
import { verifyFinalClosureObject } from '../scripts/final-closure-verify-r38.mjs';
|
|
7
7
|
|
|
8
8
|
const root = new URL('../', import.meta.url);
|
|
9
9
|
const text = rel => readFile(new URL(rel, root), 'utf8');
|
|
@@ -19,28 +19,28 @@ test('R34 public parity catalog is exact, unique, and includes production coding
|
|
|
19
19
|
|
|
20
20
|
test('release parity and final verifier are package entry points', async () => {
|
|
21
21
|
const pkg = JSON.parse(await text('package.json'));
|
|
22
|
-
assert.equal(pkg.scripts['release:parity'], 'node scripts/release-parity.mjs');
|
|
23
|
-
assert.equal(pkg.scripts['release:final:write'], 'node scripts/final-closure.mjs');
|
|
24
|
-
assert.equal(pkg.scripts['release:final:verify'], 'node scripts/final-closure-verify.mjs');
|
|
22
|
+
assert.equal(pkg.scripts['release:parity'], 'node scripts/release-parity-r38.mjs');
|
|
23
|
+
assert.equal(pkg.scripts['release:final:write'], 'node scripts/final-closure-r38.mjs');
|
|
24
|
+
assert.equal(pkg.scripts['release:final:verify'], 'node scripts/final-closure-verify-r38.mjs');
|
|
25
25
|
assert.equal(typeof validateParityEvidence, 'function');
|
|
26
26
|
assert.equal(typeof verifyFinalClosureObject, 'function');
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
test('
|
|
30
|
-
const verifier = await text('scripts/final-closure-verify.mjs');
|
|
31
|
-
const writer = await text('scripts/final-closure.mjs');
|
|
29
|
+
test('R38 final closure verifier is implementation-independent from production helpers', async () => {
|
|
30
|
+
const verifier = await text('scripts/final-closure-verify-r38.mjs');
|
|
31
|
+
const writer = await text('scripts/final-closure-r38.mjs');
|
|
32
32
|
assert.doesNotMatch(verifier, /from ['"]\.\.\/src\//);
|
|
33
33
|
assert.match(verifier, /DEADBYTE-R27-SLOT-UPDATE-V1/);
|
|
34
34
|
assert.match(verifier, /DEADBYTE-MACHINE-PING-V1/);
|
|
35
|
-
assert.match(verifier, /DEADBYTE-
|
|
36
|
-
assert.match(writer, /pointer\.version==='0\.11\.
|
|
37
|
-
assert.match(verifier, /envelope\.body\.version==='0\.11\.
|
|
38
|
-
assert.match(verifier, /release_label==='
|
|
39
|
-
assert.match(verifier, /
|
|
35
|
+
assert.match(verifier, /DEADBYTE-R38-FINAL-CLOSURE-V1/);
|
|
36
|
+
assert.match(writer, /pointer\.version==='0\.11\.2'/);
|
|
37
|
+
assert.match(verifier, /envelope\.body\.version==='0\.11\.2'/);
|
|
38
|
+
assert.match(verifier, /release_label==='R38 FINAL MACHINE BASELINE'/);
|
|
39
|
+
assert.match(verifier, /220fac89dd82cdcdddb82cd7d0b71b768f84a5c87d57ebaca0f5383f33d97a76/);
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
test('final closure verifier canonicalizes traversal order before exact-tree comparison', async () => {
|
|
43
|
-
const verifier = await text('scripts/final-closure-verify.mjs');
|
|
43
|
+
const verifier = await text('scripts/final-closure-verify-r38.mjs');
|
|
44
44
|
assert.match(verifier, /const actual=\(await walk\(root\)\)\.filter\(rel=>rel!==manifestName\)\.sort\(\(a,b\)=>a\.localeCompare\(b,'en'\)\);/);
|
|
45
45
|
assert.match(verifier, /const expected=\[\.\.\.entries\.keys\(\)\]\.sort\(\(a,b\)=>a\.localeCompare\(b,'en'\)\);/);
|
|
46
46
|
});
|
|
@@ -10,10 +10,10 @@ const R36_RELEASE_ID='v0.10.0-8d1e5f09745e7647';
|
|
|
10
10
|
const R36_MANIFEST='8d1e5f09745e764796b60e0a8a0168be7334ed4bf834fad5974a80eba295b4e8';
|
|
11
11
|
|
|
12
12
|
test('sealed R36 historical proof scripts preserve 0.10.0 / R36 and exact R35 predecessor',async()=>{
|
|
13
|
-
const [gate,parity,closure,closureVerify,windowsGate,
|
|
13
|
+
const [gate,parity,closure,closureVerify,windowsGate,r37_0110Parity]=await Promise.all([
|
|
14
14
|
text('scripts/gate-windows-r36.ps1'),text('scripts/release-parity-r36.mjs'),
|
|
15
15
|
text('scripts/final-closure-r36.mjs'),text('scripts/final-closure-verify-r36.mjs'),
|
|
16
|
-
text('scripts/windows-gate-evidence-r36.mjs'),text('scripts/release-parity.mjs')
|
|
16
|
+
text('scripts/windows-gate-evidence-r36.mjs'),text('scripts/release-parity-r37-0110.mjs')
|
|
17
17
|
]);
|
|
18
18
|
assert.match(gate,/DEADBYTE V0\.10\.0 \/ R36 WINDOWS BATCH GATE: PASS/);
|
|
19
19
|
assert.match(parity,/deadbyte\.r36-parity\.v1/);
|
|
@@ -23,8 +23,8 @@ test('sealed R36 historical proof scripts preserve 0.10.0 / R36 and exact R35 pr
|
|
|
23
23
|
assert.match(closure,/version:'0\.10\.0'/);
|
|
24
24
|
assert.match(closureVerify,/DEADBYTE-R36-FINAL-CLOSURE-V1/);
|
|
25
25
|
assert.match(windowsGate,/deadbyte\.r36-windows-gate\.v1/);
|
|
26
|
-
assert.match(
|
|
27
|
-
assert.match(
|
|
26
|
+
assert.match(r37_0110Parity,new RegExp(R36_RELEASE_ID.replaceAll('.','\\.')));
|
|
27
|
+
assert.match(r37_0110Parity,new RegExp(R36_MANIFEST));
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
test('current R37 controller still binds production remote to compact profile and restores parent environment',async()=>{
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
const root=new URL('../',import.meta.url);
|
|
5
|
+
const text=rel=>readFile(new URL(rel,root),'utf8');
|
|
6
|
+
|
|
7
|
+
test('sealed public 0.11.0 release scripts remain preserved as historical R37 authority',async()=>{
|
|
8
|
+
const [gate,evidence,parity,closure,verify,deferred]=await Promise.all([
|
|
9
|
+
text('scripts/gate-windows-r37-0110.ps1'),text('scripts/windows-gate-evidence-r37-0110.mjs'),
|
|
10
|
+
text('scripts/release-parity-r37-0110.mjs'),text('scripts/final-closure-r37-0110.mjs'),
|
|
11
|
+
text('scripts/final-closure-verify-r37-0110.mjs'),text('scripts/deferred-slot-operation-r37-0110.mjs')
|
|
12
|
+
]);
|
|
13
|
+
assert.match(gate,/V0\.11\.0 \/ R37/);
|
|
14
|
+
assert.match(evidence,/version:'0\.11\.0'/);
|
|
15
|
+
assert.match(parity,/v0\.10\.0-8d1e5f09745e7647/);
|
|
16
|
+
assert.match(parity,/candidate\?\.version==='0\.11\.0'/);
|
|
17
|
+
assert.match(closure,/version:'0\.11\.0'/);
|
|
18
|
+
assert.match(verify,/value\?\.version==='0\.11\.0'/);
|
|
19
|
+
assert.match(deferred,/--expected-version','0\.11\.0'/);
|
|
20
|
+
});
|