deadbyte-mcp 0.15.0 → 0.15.1

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.
Files changed (77) hide show
  1. package/MANIFEST.SHA256 +76 -41
  2. package/PROVENANCE.json +10 -0
  3. package/R45-PARENT.json +18 -0
  4. package/README.txt +14 -14
  5. package/RELEASE.json +37 -0
  6. package/SBOM.cdx.json +46 -0
  7. package/TEST-MATRIX.json +31 -0
  8. package/bin/deadbyte-hardware-observer-r45.exe +0 -0
  9. package/bin/deadbyte-hardware-observer-r45.obj +0 -0
  10. package/controller/README.TXT +2 -2
  11. package/controller/deadbyte-controller.ps1 +2 -2
  12. package/docs/ARCHITECTURE.md +1 -1
  13. package/native/deadbyte-hardware-observer.cpp +308 -0
  14. package/package.json +12 -7
  15. package/scripts/autonomous-tests.ps1 +2 -2
  16. package/scripts/build-hardware-r45.ps1 +71 -0
  17. package/scripts/deferred-slot-operation-r45.mjs +126 -0
  18. package/scripts/derive-r45-parent.mjs +24 -0
  19. package/scripts/derive-r45-release.mjs +72 -0
  20. package/scripts/derive-r45-tests.mjs +20 -0
  21. package/scripts/final-closure-r45.mjs +121 -0
  22. package/scripts/final-closure-verify-r45.mjs +220 -0
  23. package/scripts/finalize-r45.mjs +219 -0
  24. package/scripts/full-regression-r45.mjs +20 -0
  25. package/scripts/full-regression-summary-r45.mjs +14 -0
  26. package/scripts/gate-windows-r45.ps1 +44 -0
  27. package/scripts/init-host-policy.ps1 +39 -1
  28. package/scripts/inspect-active-release-r45.mjs +20 -0
  29. package/scripts/release-manifest.mjs +3 -1
  30. package/scripts/release-parity-r45.mjs +253 -0
  31. package/scripts/release-parity-tests-r45.ps1 +8 -0
  32. package/scripts/verify-r45-parent.mjs +142 -0
  33. package/scripts/windows-gate-evidence-r45.mjs +60 -0
  34. package/src/autonomous-runtime.mjs +12 -7
  35. package/src/capability-ledger.mjs +1 -0
  36. package/src/compact-gateway-mcp-tools.mjs +4 -2
  37. package/src/deadbyte-cli.mjs +17 -9
  38. package/src/doctor-r45.mjs +65 -0
  39. package/src/host-plane.mjs +133 -85
  40. package/src/mcp-server.mjs +64 -23
  41. package/src/native-diff-v2.mjs +9 -4
  42. package/src/native-diff.mjs +29 -7
  43. package/src/observation-mcp-tools.mjs +5 -1
  44. package/src/observation-output-contracts.mjs +8 -0
  45. package/src/observation-runtime.mjs +33 -2
  46. package/src/observation-smoke-client.mjs +6 -2
  47. package/src/output-contracts.mjs +27 -11
  48. package/src/r45-compact-disarmed-probe.mjs +74 -0
  49. package/src/r45-full-surface-probe.mjs +34 -0
  50. package/src/release-generation.mjs +1 -0
  51. package/src/remote-mcp-r45-parity-client.mjs +206 -0
  52. package/src/runtime-update.mjs +36 -8
  53. package/src/version.mjs +1 -1
  54. package/test/agent-runtime.test.mjs +1 -0
  55. package/test/autonomous-loop.test.mjs +24 -0
  56. package/test/autonomous-runtime.test.mjs +1 -0
  57. package/test/cli-entrypoint.test.mjs +4 -4
  58. package/test/coding-plane.test.mjs +1 -0
  59. package/test/compact-max-catalog-r45.test.mjs +99 -0
  60. package/test/contained.test.mjs +23 -11
  61. package/test/controller.test.mjs +5 -1
  62. package/test/core.test.mjs +1 -1
  63. package/test/full-max-catalog-r45.test.mjs +91 -0
  64. package/test/hardware-observation-r45.test.mjs +44 -0
  65. package/test/host-inline-r45.test.mjs +67 -0
  66. package/test/machine-mcp-integration.test.mjs +1 -0
  67. package/test/observation-mcp-tools.test.mjs +4 -3
  68. package/test/output-schema.test.mjs +1 -0
  69. package/test/package-boundary.test.mjs +1 -1
  70. package/test/production-docs.test.mjs +3 -3
  71. package/test/r33-closeout-regression.test.mjs +20 -19
  72. package/test/r33-finalization.test.mjs +3 -3
  73. package/test/r45-autonomous-quick-test.test.mjs +10 -0
  74. package/test/r45-finalization.test.mjs +84 -0
  75. package/test/release-generation.test.mjs +1 -0
  76. package/test/release-version.test.mjs +52 -41
  77. package/test/runtime-host-bootstrap-r45.test.mjs +46 -0
@@ -0,0 +1,253 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, readFile, realpath, writeFile } from 'node:fs/promises';
3
+ import { loadPowerShellDataFile } from '../src/runtime-update.mjs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { spawn } from 'node:child_process';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { probeR45FullSurface } from '../src/r45-full-surface-probe.mjs';
9
+ import { runR45CompactParity } from '../src/remote-mcp-r45-parity-client.mjs';
10
+ import { probeR45CompactDisarmed } from '../src/r45-compact-disarmed-probe.mjs';
11
+ import { probeR44CompactDisarmed as probeR44PredecessorDisarmed } from '../src/r44-compact-disarmed-probe.mjs';
12
+ import { probeR36CompactDisarmed } from '../src/r36-compact-disarmed-probe.mjs';
13
+ import { runR40DesktopGuardedSmoke } from '../src/remote-mcp-desktop-r40-smoke-client.mjs';
14
+ import { verifyR41Memory } from './verify-memory-r41.mjs';
15
+
16
+ const here=path.dirname(fileURLToPath(import.meta.url));
17
+ const root=path.resolve(here,'..');
18
+ const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
19
+ const deferred=path.join(root,'scripts','deferred-slot-operation-r45.mjs');
20
+ const ps='C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
21
+ const launcher=path.join(stateRoot,'bin','DEADBYTE-MCP.ps1');
22
+ const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
23
+ const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
24
+
25
+ export const R45_PREDECESSOR_RELEASE_ID='v0.15.0-c15fa41719be61b3';
26
+ export const R45_PREDECESSOR_MANIFEST='c15fa41719be61b3186e49b2367a5f4c057365cfb465102dbefb7d7355f45483';
27
+
28
+ function assert(condition,message){if(!condition) throw new Error(message);}
29
+ function parseFrozenManifest(text){
30
+ const entries=[];const seen=new Set();
31
+ for(const [index,line] of text.split(/\r?\n/).entries()){
32
+ if(!line) continue;
33
+ const match=/^([0-9a-f]{64}) ([^\0]+)$/.exec(line);
34
+ assert(match,`frozen manifest line ${index+1} malformed`);
35
+ const rel=match[2];
36
+ assert(!path.posix.isAbsolute(rel)&&!rel.split('/').includes('..')&&!seen.has(rel),`unsafe/duplicate frozen manifest path: ${rel}`);
37
+ seen.add(rel);entries.push({sha256:match[1],path:rel});
38
+ }
39
+ return entries;
40
+ }
41
+ // Restore the mutable development stage from the immutable active release after live parity work may drift it.
42
+ export async function restoreFrozenStage({developmentRoot,releaseRoot,expectedManifestSha256}){
43
+ const dev=path.resolve(developmentRoot);const release=path.resolve(releaseRoot);
44
+ const manifestBytes=await readFile(path.join(release,'MANIFEST.SHA256'));
45
+ const manifestSha256=sha256(manifestBytes);
46
+ assert(manifestSha256===expectedManifestSha256,`immutable release manifest drift: ${manifestSha256}`);
47
+ const entries=parseFrozenManifest(manifestBytes.toString('utf8'));
48
+ for(const entry of entries){
49
+ const source=path.join(release,...entry.path.split('/'));const bytes=await readFile(source);
50
+ assert(sha256(bytes)===entry.sha256,`immutable release file drift: ${entry.path}`);
51
+ const destination=path.join(dev,...entry.path.split('/'));await mkdir(path.dirname(destination),{recursive:true});
52
+ await writeFile(destination,bytes);assert(sha256(await readFile(destination))===entry.sha256,`development restore verification failed: ${entry.path}`);
53
+ }
54
+ await writeFile(path.join(dev,'MANIFEST.SHA256'),manifestBytes);
55
+ assert(sha256(await readFile(path.join(dev,'MANIFEST.SHA256')))===expectedManifestSha256,'development manifest restore verification failed');
56
+ return {status:'restored',file_count:entries.length,manifest_sha256:manifestSha256};
57
+ }
58
+ function parseJsonOutput(text,label){
59
+ const trimmed=String(text).trim();const starts=[...trimmed.matchAll(/\{/g)].map(match=>match.index);
60
+ for(const start of starts){try{return JSON.parse(trimmed.slice(start));}catch{}}
61
+ throw new Error(`${label} did not emit JSON`);
62
+ }
63
+ function run(command,args,{timeoutMs=900000,env=process.env,cwd=root}={}){
64
+ return new Promise((resolve,reject)=>{
65
+ const child=spawn(command,args,{cwd,shell:false,windowsHide:true,env,stdio:['ignore','pipe','pipe']});
66
+ const out=[],err=[];const timer=setTimeout(()=>{child.kill();reject(new Error(`command timeout: ${path.basename(command)}`));},timeoutMs);
67
+ child.stdout.on('data',chunk=>out.push(Buffer.from(chunk)));child.stderr.on('data',chunk=>err.push(Buffer.from(chunk)));
68
+ child.on('error',error=>{clearTimeout(timer);reject(error);});child.on('exit',code=>{
69
+ clearTimeout(timer);const stdout=Buffer.concat(out).toString('utf8');const stderr=Buffer.concat(err).toString('utf8');
70
+ if(code!==0) reject(new Error(`command failed exit=${code}: ${stderr||stdout}`));else resolve({stdout,stderr,exitCode:code});
71
+ });
72
+ });
73
+ }
74
+ async function publicUrl(){
75
+ const value=(await readFile(path.join(stateRoot,'bridge','mcp-url.txt'),'utf8')).trim();const url=new URL(value);
76
+ if(url.protocol!=='https:') throw new Error('public MCP URL must be HTTPS');return value;
77
+ }
78
+ async function pointer(){return JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));}
79
+ async function waitForExactPointer({releaseId,version,manifestSha256,timeoutMs=120000}){
80
+ const deadline=Date.now()+timeoutMs;let last=null;
81
+ while(Date.now()<deadline){
82
+ try{
83
+ last=await pointer();
84
+ if(last?.release_id===releaseId&&last?.version===version&&last?.manifest_sha256===manifestSha256) return last;
85
+ }catch{}
86
+ await sleep(500);
87
+ }
88
+ throw new Error(`active pointer convergence timeout expected=${releaseId}/${version}/${manifestSha256} observed=${last?.release_id??'unreadable'}/${last?.version??'unreadable'}/${last?.manifest_sha256??'unreadable'}`);
89
+ }
90
+ async function disarmAll(){
91
+ for(const command of ['disarmdesktop','disarmautonomous','disarmcoding','disarmprocess','disarmhost']){
92
+ await run(ps,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,command],{timeoutMs:60000});
93
+ }
94
+ }
95
+ async function waitDeferred(statePath,timeoutMs=180000){
96
+ const deadline=Date.now()+timeoutMs;
97
+ while(Date.now()<deadline){const state=JSON.parse(await readFile(statePath,'utf8'));if(state.state==='completed') return state;if(state.state==='failed') throw new Error(`deferred ${state.mode} failed: ${state.error}`);await sleep(500);}
98
+ throw new Error(`deferred operation timeout: ${statePath}`);
99
+ }
100
+ async function scheduleDeferred(mode,value,{bridgeRoot=null,bridgeScriptSha256=null}={}){
101
+ if((bridgeRoot===null)!==(bridgeScriptSha256===null)) throw new Error('deferred bridge override requires exact root + SHA pair');
102
+ const args=[deferred,mode,value];
103
+ if(bridgeRoot!==null) args.push('--bridge-root',bridgeRoot,'--bridge-script-sha256',bridgeScriptSha256);
104
+ const scheduled=parseJsonOutput((await run(process.execPath,args,{timeoutMs:30000})).stdout,`deferred ${mode}`);
105
+ assert(scheduled.status==='scheduled',`deferred ${mode} was not scheduled`);
106
+ assert(scheduled.execution_provider==='deadbyte_mcp'&&scheduled.rdc_operation_count===0,'deferred operation provider mismatch');
107
+ return {scheduled,completed:await waitDeferred(scheduled.state_path)};
108
+ }
109
+ async function publicR44PredecessorDisarmed(url){
110
+ const result=await probeR44PredecessorDisarmed(url);
111
+ assert(result.status==='passed'&&result.tool_count===43&&result.capability_closure===true,'R44 predecessor public compact catalog mismatch');
112
+ assert(result.release_id===R45_PREDECESSOR_RELEASE_ID&&result.release_manifest_sha256===R45_PREDECESSOR_MANIFEST,'R44 predecessor machine binding mismatch');
113
+ assert(result.coding_armed===false&&result.autonomous_armed===false&&result.desktop_armed===false,'R44 predecessor authority remained armed');
114
+ return result;
115
+ }
116
+ async function mutableCore(pointerValue){
117
+ const policy=JSON.parse(await readFile(path.join(pointerValue.generation_root,'coding-policy.json'),'utf8'));
118
+ const raw=policy?.roots?.core?.path??policy?.roots?.core?.real_path;
119
+ assert(typeof raw==='string'&&raw,'R40 generation missing coding core root');
120
+ const core=path.resolve(raw);const release=path.resolve(pointerValue.release_root);
121
+ assert(core.toLowerCase()!==release.toLowerCase(),'R40 coding core must not point at immutable active release slot');
122
+ return core;
123
+ }
124
+
125
+ function validateFeatureGate(value,manifest){
126
+ assert(value?.schema==='deadbyte.r45-windows-gate.v1'&&value.status==='passed'&&value.exit_code===0,'R45 feature gate invalid');
127
+ assert(value.version==='0.15.1'&&value.manifest_sha256===manifest,'R45 feature gate candidate mismatch');
128
+ for(const key of ['skill_integrity','agent_backend_persistence','codex_protocol_contract','remote_device_identity','durable_job_supervisor','real_external_supervisor_smoke','full_regression_required','host_inline_native','native_hardware_observation','doctor_release_truth']) {
129
+ assert(value.features?.[key]===true,`R45 feature gate missing ${key}`);
130
+ }
131
+ return true;
132
+ }
133
+ async function readFeatureGate(manifest){
134
+ const file=path.join(stateRoot,'evidence','release',`r45-windows-gate-${manifest.slice(0,16)}.json`);
135
+ const bytes=await readFile(file);const value=JSON.parse(bytes.toString('utf8').replace(/^\uFEFF/,''));
136
+ validateFeatureGate(value,manifest);
137
+ return {value,path:file,sha256:sha256(bytes)};
138
+ }
139
+
140
+ export function validateParityEvidence(value){
141
+ assert(value?.schema==='deadbyte.r45-parity.v1','R45 parity schema mismatch');
142
+ assert(value.status==='passed','R40 parity status is not passed');
143
+ assert(value.rdc_operation_count===0&&value.execution_provider==='deadbyte_mcp','R40 parity used non-DEADBYTE execution');
144
+ assert(value.candidate?.version==='0.15.1'&&/^[0-9a-f]{64}$/.test(value.candidate?.manifest_sha256??''),'R40 candidate identity missing');
145
+ assert(value.local_full?.status==='passed'&&value.local_full?.tool_count===82&&value.local_full?.strict_output_schemas===true,'R45 local full 82-tool proof missing');
146
+ assert(value.public_compact?.status==='passed'&&value.public_compact?.tool_count===43&&value.public_compact?.capability_closure===true&&value.public_compact?.gateway_count===6&&value.public_compact?.desktop_armed===false&&value.public_compact?.desktop_observation_receipt_verified===true&&value.public_compact?.desktop_grounding_receipt_verified===true,'R45 public compact/Desktop/skill closure missing');
147
+ assert(value.desktop_guarded?.status==='passed'&&value.desktop_guarded?.native_injected===true&&value.desktop_guarded?.replay_performed===false&&
148
+ Number.isInteger(value.desktop_guarded?.predispatch_retry_count)&&value.desktop_guarded.predispatch_retry_count>=0&&value.desktop_guarded.predispatch_retry_count<=2&&
149
+ value.desktop_guarded?.observation_receipts_verified===value.desktop_guarded.predispatch_retry_count+1&&value.desktop_guarded?.action_receipts_verified===3,
150
+ 'R40 guarded Desktop proof missing');
151
+ assert(value.autonomous?.status==='passed'&&value.autonomous?.signed_release_verified===true,'R40 autonomous live proof missing');
152
+ assert(value.deterministic_memory?.status==='passed'&&value.deterministic_memory?.goal_count>=1&&value.deterministic_memory?.snapshot_count>=1&&value.deterministic_memory?.index_count>=1,'R41 deterministic memory proof missing');
153
+ assert(value.before_rollback_disarmed?.status==='passed'&&value.before_rollback_disarmed?.capability_closure===true&&value.before_rollback_disarmed?.coding_armed===false&&value.before_rollback_disarmed?.autonomous_armed===false&&value.before_rollback_disarmed?.desktop_armed===false,'R40 pre-rollback disarm proof missing');
154
+ assert(value.rollback?.state==='completed'&&value.restore?.state==='completed','R40 rollback/restore not completed');
155
+ assert(value.stage_restore?.status==='restored'&&value.stage_restore?.manifest_sha256===value.candidate.manifest_sha256,'R40 stage restore proof missing');
156
+ assert(value.predecessor?.release_id===R45_PREDECESSOR_RELEASE_ID&&value.predecessor?.manifest_sha256===R45_PREDECESSOR_MANIFEST&&value.predecessor?.version==='0.15.0','R45 exact R44 predecessor identity mismatch');
157
+ assert(value.predecessor_disarmed?.status==='passed'&&value.predecessor_disarmed?.tool_count===43&&value.predecessor_disarmed?.capability_closure===true,'R44 predecessor public proof missing');
158
+ assert(value.successor?.version==='0.15.1'&&value.successor?.manifest_sha256===value.candidate.manifest_sha256,'R45 exact successor restore identity mismatch');
159
+ assert(value.successor_disarmed?.status==='passed'&&value.successor_disarmed?.capability_closure===true&&value.successor_disarmed?.coding_armed===false&&value.successor_disarmed?.autonomous_armed===false&&value.successor_disarmed?.desktop_armed===false,'R40 successor disarmed proof missing');
160
+ assert(value.immutable_slot?.protected===true&&value.immutable_slot?.development_root!==value.immutable_slot?.release_root,'R40 immutable-slot separation missing');
161
+ for(const key of ['tree_read','precondition_mutation','search','owned_process','observation','coding_loop','autonomy','interrupted_recovery','evidence_verification','compact_closure','full_surface','desktop_readonly','desktop_guarded_input','deterministic_memory','skill_integrity','agent_backend_persistence','codex_protocol_contract','remote_device_identity','durable_job_supervisor','real_external_supervisor_smoke']) assert(value.requirements?.[key]===true,`R45 parity requirement not proven: ${key}`);
162
+ validateFeatureGate(value.feature_gate,value.candidate.manifest_sha256);
163
+ return true;
164
+ }
165
+
166
+ export async function runReleaseParity(activationReceiptPath){
167
+ const active=await pointer();const candidateManifest=sha256(await readFile(path.join(root,'MANIFEST.SHA256')));
168
+ assert(active.version==='0.15.1','R45 parity requires active 0.15.1 candidate');
169
+ assert(active.manifest_sha256===candidateManifest,'active release is not canonical R40 candidate');
170
+ const activeConfig=await loadPowerShellDataFile(active.config_path);
171
+ const successorBridgeRootRaw=activeConfig?.Bridge?.WorkingDirectory;
172
+ const successorBridgeConfiguredSha=activeConfig?.Bridge?.ScriptSha256;
173
+ assert(typeof successorBridgeRootRaw==='string'&&path.isAbsolute(successorBridgeRootRaw),'R40 active bridge root missing');
174
+ assert(typeof successorBridgeConfiguredSha==='string'&&/^[0-9a-f]{64}$/.test(successorBridgeConfiguredSha),'R40 active bridge SHA missing');
175
+ const successorBridgeRoot=await realpath(path.resolve(successorBridgeRootRaw));
176
+ const successorBridgeActualSha=sha256(await readFile(path.join(successorBridgeRoot,'src','bridge.mjs')));
177
+ assert(successorBridgeActualSha===successorBridgeConfiguredSha,'R40 active bridge bytes do not match generation pin');
178
+ const successorBridge={root:successorBridgeRoot,sha256:successorBridgeActualSha};
179
+ const developmentRoot=await mutableCore(active);
180
+ const localFull=await probeR45FullSurface({packageRoot:active.release_root,generationRoot:active.generation_root,stateRoot});
181
+ const url=await publicUrl();
182
+ let publicCompact,autonomous,desktopGuarded;
183
+ try{
184
+ publicCompact=await runR45CompactParity(url);
185
+ autonomous=parseJsonOutput((await run(process.execPath,[path.join(root,'src','remote-mcp-autonomous-smoke-client.mjs'),url],{timeoutMs:900000})).stdout,'R40 autonomous compact parity');
186
+ assert(autonomous.status==='passed'&&autonomous.signed_release_verified===true,'R40 autonomous compact parity failed');
187
+ await run(ps,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,'armdesktop'],{timeoutMs:60000});
188
+ try{desktopGuarded=await runR40DesktopGuardedSmoke(url);}
189
+ finally{await run(ps,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,'disarmdesktop'],{timeoutMs:60000}).catch(()=>{});}
190
+ assert(desktopGuarded.status==='passed'&&desktopGuarded.native_injected===true&&desktopGuarded.action_receipts_verified===3,'R40 guarded Desktop public smoke failed');
191
+ }catch(error){await disarmAll().catch(()=>{});throw error;}
192
+ const deterministicMemory=await verifyR41Memory({memoryRoot:path.join(stateRoot,'evidence','autonomous','memory-r41',candidateManifest),
193
+ journalRoot:path.join(stateRoot,'evidence','autonomous','goals'),sourceReleaseManifest:candidateManifest,
194
+ publicKeyPath:path.join(stateRoot,'trust','ed25519-public.pem')});
195
+ assert(deterministicMemory.goal_count>=1&&deterministicMemory.snapshot_count>=1&&deterministicMemory.index_count>=1,'R41 live deterministic memory evidence missing');
196
+ await disarmAll();
197
+ const beforeRollback=await probeR45CompactDisarmed(url);
198
+ const rollbackPair=await scheduleDeferred('rollback',path.resolve(activationReceiptPath));
199
+ assert(rollbackPair.completed.active_release===R45_PREDECESSOR_RELEASE_ID,'rollback worker completed without exact R44 active release');
200
+ const predecessor=await waitForExactPointer({releaseId:R45_PREDECESSOR_RELEASE_ID,version:'0.15.0',manifestSha256:R45_PREDECESSOR_MANIFEST});
201
+ const predecessorDisarmed=await publicR44PredecessorDisarmed(url);
202
+ const stageRestore=await restoreFrozenStage({developmentRoot,releaseRoot:active.release_root,expectedManifestSha256:candidateManifest});
203
+ const restorePair=await scheduleDeferred('activate',candidateManifest,{bridgeRoot:successorBridge.root,bridgeScriptSha256:successorBridge.sha256});
204
+ assert(restorePair.completed.active_release===active.release_id,'restore worker completed without exact R45 active release');
205
+ const successor=await waitForExactPointer({releaseId:active.release_id,version:'0.15.1',manifestSha256:candidateManifest});
206
+ const successorDisarmed=await probeR45CompactDisarmed(url);
207
+ await disarmAll();
208
+
209
+ const featureGate=await readFeatureGate(candidateManifest);
210
+ const evidence={
211
+ schema:'deadbyte.r45-parity.v1',status:'passed',completed_at_utc:new Date().toISOString(),execution_provider:'deadbyte_mcp',rdc_operation_count:0,
212
+ candidate:{release_id:active.release_id,version:active.version,manifest_sha256:candidateManifest},
213
+ immutable_slot:{protected:true,release_root:path.resolve(active.release_root),development_root:developmentRoot},
214
+ local_full:localFull,public_compact:publicCompact,autonomous,deterministic_memory:deterministicMemory,desktop_guarded:desktopGuarded,feature_gate:featureGate.value,feature_gate_sha256:featureGate.sha256,before_rollback_disarmed:beforeRollback,
215
+ rollback:{...rollbackPair.completed,scheduled_operation_id:rollbackPair.scheduled.operation_id},
216
+ predecessor:{release_id:predecessor.release_id,version:predecessor.version,manifest_sha256:predecessor.manifest_sha256},predecessor_disarmed:predecessorDisarmed,
217
+ stage_restore:stageRestore,
218
+ restore:{...restorePair.completed,scheduled_operation_id:restorePair.scheduled.operation_id},
219
+ successor:{release_id:successor.release_id,version:successor.version,manifest_sha256:successor.manifest_sha256},successor_disarmed:successorDisarmed,
220
+ requirements:{
221
+ tree_read:publicCompact.tree_entries>=0&&publicCompact.coding_stat_verified===true,
222
+ precondition_mutation:publicCompact.machine_fs_receipts_verified>=3,
223
+ search:publicCompact.coding_search_result_count>=1&&publicCompact.observation_search_result_count>=1,
224
+ owned_process:typeof publicCompact.process_session_id==='string'&&publicCompact.process_output_bytes>0,
225
+ observation:publicCompact.observation_receipts_verified>=2,
226
+ coding_loop:publicCompact.coding_receipts_verified===2,
227
+ autonomy:autonomous.signed_release_verified===true&&autonomous.observed_failure_before_replan===true,
228
+ interrupted_recovery:rollbackPair.completed.state==='completed'&&restorePair.completed.state==='completed',
229
+ evidence_verification:publicCompact.machine_fs_receipts_verified>=3&&autonomous.signed_release_verified===true,
230
+ compact_closure:publicCompact.capability_closure===true&&successorDisarmed.capability_closure===true,
231
+ full_surface:localFull.tool_count===82&&localFull.strict_output_schemas===true,
232
+ desktop_readonly:publicCompact.desktop_armed===false&&publicCompact.desktop_observation_receipt_verified===true&&publicCompact.desktop_grounding_receipt_verified===true,
233
+ desktop_guarded_input:desktopGuarded.native_injected===true&&desktopGuarded.replay_performed===false&&desktopGuarded.action_receipts_verified===3,
234
+ deterministic_memory:deterministicMemory.status==='passed'&&deterministicMemory.goal_count>=1&&deterministicMemory.snapshot_count>=1,
235
+ skill_integrity:publicCompact.skill_catalog_verified===true&&successorDisarmed.skill_catalog_verified===true&&featureGate.value.features.skill_integrity===true,
236
+ agent_backend_persistence:featureGate.value.features.agent_backend_persistence===true,
237
+ codex_protocol_contract:featureGate.value.features.codex_protocol_contract===true,
238
+ remote_device_identity:featureGate.value.features.remote_device_identity===true,
239
+ durable_job_supervisor:featureGate.value.features.durable_job_supervisor===true,
240
+ real_external_supervisor_smoke:featureGate.value.features.real_external_supervisor_smoke===true
241
+ }
242
+ };
243
+ validateParityEvidence(evidence);
244
+ const evidenceDir=path.join(stateRoot,'evidence','release');await mkdir(evidenceDir,{recursive:true});
245
+ const evidencePath=path.join(evidenceDir,`r45-parity-${candidateManifest.slice(0,16)}.json`);
246
+ const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');await writeFile(evidencePath,bytes);
247
+ return {evidence,evidence_path:evidencePath,evidence_sha256:sha256(bytes)};
248
+ }
249
+
250
+ if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){
251
+ const receipt=process.argv[2];if(!receipt) throw new Error('usage: node scripts/release-parity-r45.mjs <activation-receipt.json>');
252
+ runReleaseParity(receipt).then(result=>console.log(JSON.stringify({status:'passed',...result},null,2))).catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
253
+ }
@@ -0,0 +1,8 @@
1
+ $ErrorActionPreference='Stop'
2
+ Set-StrictMode -Version 2.0
3
+ & node.exe --test --test-concurrency=1 test\host-inline-r45.test.mjs test\hardware-observation-r45.test.mjs test\runtime-host-bootstrap-r45.test.mjs test\full-max-catalog-r45.test.mjs test\compact-max-catalog-r45.test.mjs test\r45-finalization.test.mjs test\skill-plane-r44.test.mjs test\skill-mcp-r44.test.mjs test\agent-backend-r44.test.mjs test\codex-app-server-r44.test.mjs test\codex-autonomous-r44.test.mjs test\remote-device-r44.test.mjs test\durable-job-r44.test.mjs test\external-job-provider-r44.test.mjs test\coding-runtime.test.mjs test\host-plane.test.mjs test\process-policy.test.mjs test\process-runtime.test.mjs test\native-diff.test.mjs test\native-diff-topology-r36.test.mjs test\autonomous-loop.test.mjs test\autonomous-adversarial.test.mjs test\standing-grants.test.mjs test\runtime-self-update.test.mjs
4
+ if($LASTEXITCODE -ne 0){exit $LASTEXITCODE}
5
+ & node.exe --test --test-concurrency=1 test\r44-finalization.test.mjs test\r43-finalization.test.mjs test\provider-responses-r42.test.mjs test\task-contract-r42.test.mjs test\human-clarification-r42.test.mjs test\agent-governance-r42.test.mjs test\evidence-publisher-r42.test.mjs test\agent-eval-r42.test.mjs test\agent-eval-runner-r42.test.mjs test\r42-finalization.test.mjs
6
+ if($LASTEXITCODE -ne 0){exit $LASTEXITCODE}
7
+ & node.exe --test --test-concurrency=1 test\memory-snapshot-r41.test.mjs test\memory-projections-r41.test.mjs test\memory-retrieval-r41.test.mjs test\memory-history-r41.test.mjs test\memory-autonomous-integration-r41.test.mjs
8
+ exit $LASTEXITCODE
@@ -0,0 +1,142 @@
1
+ import { createHash, createPublicKey, verify as cryptoVerify } from 'node:crypto';
2
+ import { lstat, readFile, readdir } 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 CLOSURE_DOMAIN=Buffer.from('DEADBYTE-R44-FINAL-CLOSURE-V1\0','utf8');
8
+ const HEX64=/^[0-9a-f]{64}$/;
9
+ const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
10
+ const canonical=value=>Array.isArray(value)?value.map(canonical):value&&typeof value==='object'
11
+ ?Object.fromEntries(Object.keys(value).sort().map(key=>[key,canonical(value[key])])):value;
12
+ const canonicalJson=value=>JSON.stringify(canonical(value));
13
+ const same=(a,b)=>canonicalJson(a)===canonicalJson(b);
14
+ const samePath=(a,b)=>path.resolve(String(a)).toLowerCase()===path.resolve(String(b)).toLowerCase();
15
+ function insist(ok,message){if(!ok) throw new Error(message);}
16
+
17
+ function parseManifest(text){
18
+ const out=new Map();
19
+ for(const [i,line] of text.split(/\r?\n/).entries()){
20
+ if(!line) continue;
21
+ const m=/^([0-9a-f]{64}) ([^\0]+)$/.exec(line);
22
+ insist(m,`manifest line ${i+1} malformed`);
23
+ const rel=m[2];
24
+ insist(!path.posix.isAbsolute(rel)&&!rel.split('/').includes('..')&&!out.has(rel),`unsafe/duplicate manifest path ${rel}`);
25
+ out.set(rel,m[1]);
26
+ }
27
+ return out;
28
+ }
29
+ async function walk(root,rel=''){
30
+ const dir=path.join(root,...(rel?rel.split('/'):[]));
31
+ const names=(await readdir(dir)).sort((a,b)=>a.localeCompare(b,'en'));
32
+ const files=[];
33
+ for(const name of names){
34
+ const childRel=rel?`${rel}/${name}`:name;
35
+ const full=path.join(root,...childRel.split('/'));
36
+ const st=await lstat(full);
37
+ insist(!st.isSymbolicLink(),`symlink forbidden: ${childRel}`);
38
+ if(st.isDirectory()) files.push(...await walk(root,childRel));
39
+ else { insist(st.isFile(),`non-file forbidden: ${childRel}`); files.push(childRel); }
40
+ }
41
+ return files;
42
+ }
43
+ async function verifyTree(root,manifestName,expectedDigest,expectedCount){
44
+ const manifestBytes=await readFile(path.join(root,manifestName));
45
+ insist(sha256(manifestBytes)===expectedDigest,`${manifestName} digest mismatch`);
46
+ const entries=parseManifest(manifestBytes.toString('utf8'));
47
+ insist(entries.size===expectedCount,`${manifestName} file count mismatch`);
48
+ const actual=(await walk(root)).filter(rel=>rel!==manifestName).sort((a,b)=>a.localeCompare(b,'en'));
49
+ const expected=[...entries.keys()].sort((a,b)=>a.localeCompare(b,'en'));
50
+ insist(same(actual,expected),`${manifestName} exact tree mismatch`);
51
+ for(const [rel,digest] of entries){
52
+ insist(sha256(await readFile(path.join(root,...rel.split('/'))))===digest,`${manifestName} file hash mismatch: ${rel}`);
53
+ }
54
+ return entries.size;
55
+ }
56
+
57
+ export async function verifyR45Parent(parentFile,{stateRoot=path.join(os.homedir(),'.deadbyte-mcp')}={}){
58
+ const parentPath=path.resolve(parentFile);
59
+ const parent=JSON.parse(await readFile(parentPath,'utf8'));
60
+ insist(parent?.schema==='deadbyte.r45-parent.v1','R45 parent schema mismatch');
61
+ insist(parent.parent_release_id==='v0.15.0-c15fa41719be61b3','R45 parent release id mismatch');
62
+ insist(parent.parent_version==='0.15.0','R45 parent version mismatch');
63
+ insist(parent.parent_manifest_sha256==='c15fa41719be61b3186e49b2367a5f4c057365cfb465102dbefb7d7355f45483','R45 parent manifest mismatch');
64
+ insist(parent.parent_manifest_file_count===1871,'R45 parent file count mismatch');
65
+ insist(parent.parent_generation_manifest_sha256==='6d7429c050b7fe893492e102f00282019116e0c9e54e4f25a5791defa1661d30','R45 parent generation mismatch');
66
+ insist(parent.parent_generation_file_count===7,'R45 parent generation file count mismatch');
67
+ insist(parent.parent_final_closure_file_sha256==='8aa9c31ab18dfde67275d7cd59295ba08099839d35d6c97ee277fb184c05cae0','R45 parent closure file hash mismatch');
68
+ insist(parent.parent_final_closure_sha256==='533b7e0ff9ded04086dd90ed34e3b87bc427acc3c55c25bb9f4ec66cb448e667','R45 parent closure hash mismatch');
69
+ insist(parent.parent_final_closure_signer_key_id==='c0727a7911e29a0f1edd3cd910e47f89f43edb484743988c89cd82bfd5e37936','R45 parent closure signer mismatch');
70
+ insist(parent.parent_proof==='immutable-slot+generation+signed-final-closure','R45 parent proof class mismatch');
71
+
72
+ const closureBytes=await readFile(path.resolve(parent.parent_final_closure_path));
73
+ insist(sha256(closureBytes)===parent.parent_final_closure_file_sha256,'R45 parent closure file bytes mismatch');
74
+ const envelope=JSON.parse(closureBytes.toString('utf8'));
75
+ insist(envelope?.body?.schema==='deadbyte.r44-final-closure.v1','R45 parent closure schema mismatch');
76
+ insist(envelope.body.version===parent.parent_version,'R45 parent closure version mismatch');
77
+ insist(envelope.body.release_label==='R44 REMOTE AGENT SKILL BASELINE','R45 parent closure label mismatch');
78
+
79
+ const publicKey=createPublicKey(await readFile(path.join(path.resolve(stateRoot),'trust','ed25519-public.pem')));
80
+ insist(publicKey.asymmetricKeyType==='ed25519','R45 parent trust key must be Ed25519');
81
+ const keyId=sha256(Buffer.from(publicKey.export({type:'spki',format:'der'})));
82
+ insist(keyId===parent.parent_final_closure_signer_key_id,'R45 parent trust key id mismatch');
83
+ const bodyBytes=Buffer.from(canonicalJson(envelope.body),'utf8');
84
+ insist(sha256(bodyBytes)===envelope.body_sha256,'R45 parent closure body hash mismatch');
85
+ const {closure_sha256,...unsigned}=envelope;
86
+ insist(sha256(Buffer.from(canonicalJson(unsigned),'utf8'))===closure_sha256,'R45 parent closure envelope hash mismatch');
87
+ insist(closure_sha256===parent.parent_final_closure_sha256,'R45 parent closure content address mismatch');
88
+ insist(envelope.attestation?.algorithm==='ed25519'&&envelope.attestation?.domain==='DEADBYTE-R44-FINAL-CLOSURE-V1','R45 parent closure attestation contract mismatch');
89
+ insist(envelope.attestation.key_id===keyId,'R45 parent closure attestation signer mismatch');
90
+ insist(cryptoVerify(null,Buffer.concat([CLOSURE_DOMAIN,bodyBytes]),publicKey,Buffer.from(envelope.attestation.signature_base64,'base64')),'R45 parent closure signature invalid');
91
+
92
+ const pointer=envelope.body.active_pointer;
93
+ insist(pointer?.release_id===parent.parent_release_id&&pointer?.version===parent.parent_version&&pointer?.manifest_sha256===parent.parent_manifest_sha256,'R45 parent closure release binding mismatch');
94
+ insist(pointer.generation_manifest_sha256===parent.parent_generation_manifest_sha256,'R45 parent closure generation binding mismatch');
95
+ insist(pointer.launcher_sha256===parent.parent_launcher_sha256,'R45 parent launcher binding mismatch');
96
+ insist(samePath(pointer.release_root,parent.parent_release_root),'R45 parent release root mismatch');
97
+ insist(samePath(pointer.generation_root,parent.parent_generation_root),'R45 parent generation root mismatch');
98
+ insist(envelope.body.immutable_slot?.protected===true,'R45 parent immutable slot proof missing');
99
+ insist(samePath(envelope.body.immutable_slot.release_root,parent.parent_release_root),'R45 parent immutable release root mismatch');
100
+ insist(!samePath(envelope.body.immutable_slot.development_root,parent.parent_release_root),'R45 parent development root aliases release slot');
101
+ insist(samePath(envelope.body.immutable_slot.development_root,parent.development_root),'R45 parent development root mismatch');
102
+ insist(envelope.body.authority?.all_disarmed===true,'R45 parent closure did not record all-disarmed state');
103
+ for(const name of ['process-grant.json','coding-grant.json','autonomous-grant.json','desktop-grant.json','host-grant.json']){
104
+ insist(envelope.body.authority.grants_absent?.[name]===true,`R45 parent closure grant absence missing: ${name}`);
105
+ }
106
+
107
+ const evidence=envelope.body.evidence;
108
+ insist(evidence&&evidence.parity&&evidence.activation&&evidence.rollback&&evidence.restore&&evidence.windows_gate,'R45 parent closure evidence set incomplete');
109
+ for(const [name,item] of Object.entries(evidence)){
110
+ insist(typeof item?.path==='string'&&HEX64.test(item?.sha256??''),`R45 parent ${name} evidence identity malformed`);
111
+ insist(sha256(await readFile(item.path))===item.sha256,`R45 parent ${name} evidence hash mismatch`);
112
+ }
113
+ const parity=JSON.parse(await readFile(evidence.parity.path,'utf8'));
114
+ insist(parity?.schema==='deadbyte.r44-parity.v1'&&parity.status==='passed','R45 parent parity status mismatch');
115
+ insist(parity.candidate?.release_id===parent.parent_release_id&&parity.candidate?.manifest_sha256===parent.parent_manifest_sha256,'R45 parent parity candidate mismatch');
116
+ insist(parity.successor?.release_id===parent.parent_release_id&&parity.successor?.manifest_sha256===parent.parent_manifest_sha256,'R45 parent parity successor mismatch');
117
+ insist(parity.successor_disarmed?.status==='passed','R45 parent successor-disarmed proof missing');
118
+
119
+ const releaseFiles=await verifyTree(parent.parent_release_root,'MANIFEST.SHA256',parent.parent_manifest_sha256,parent.parent_manifest_file_count);
120
+ const generationFiles=await verifyTree(parent.parent_generation_root,'GENERATION.SHA256',parent.parent_generation_manifest_sha256,parent.parent_generation_file_count);
121
+ return {
122
+ ok:true,
123
+ status:'passed',
124
+ proof_class:parent.parent_proof,
125
+ release_id:parent.parent_release_id,
126
+ manifest_sha256:parent.parent_manifest_sha256,
127
+ generation_manifest_sha256:parent.parent_generation_manifest_sha256,
128
+ release_files:releaseFiles,
129
+ generation_files:generationFiles,
130
+ closure_sha256:parent.parent_final_closure_sha256,
131
+ closure_file_sha256:parent.parent_final_closure_file_sha256,
132
+ signer_key_id:keyId,
133
+ recorded_all_disarmed:true
134
+ };
135
+ }
136
+
137
+ const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
138
+ if(invoked){
139
+ const file=process.argv[2]??path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..','R45-PARENT.json');
140
+ verifyR45Parent(file).then(result=>console.log(JSON.stringify(result,null,2)))
141
+ .catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
142
+ }
@@ -0,0 +1,60 @@
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
+ import { verifyR45Parent } from './verify-r45-parent.mjs';
7
+ import { doctor } from '../src/doctor-r45.mjs';
8
+
9
+ const here=path.dirname(fileURLToPath(import.meta.url));
10
+ const root=path.resolve(here,'..');
11
+ const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
12
+ const marker='DEADBYTE V0.15.1 / R45 MACHINE FREEDOM WINDOWS BATCH GATE: PASS';
13
+ function insist(ok,message){if(!ok)throw new Error(message);}
14
+
15
+ export async function writeWindowsGateEvidence({
16
+ candidateRoot=root,
17
+ stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp')),
18
+ clock=()=>new Date()
19
+ }={}){
20
+ const candidate=path.resolve(candidateRoot);
21
+ const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
22
+ const manifestSha256=sha256(manifestBytes);
23
+ const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
24
+ insist(pkg.version==='0.15.1',`Windows gate evidence requires V0.15.1, found ${pkg.version}`);
25
+ const parent=await verifyR45Parent(path.join(candidate,'R45-PARENT.json'));
26
+ const doctorResult=await doctor();
27
+ insist(doctorResult?.status==='ok'&&doctorResult?.package_version==='0.15.1'&&doctorResult?.manifest?.verified===true,'R45 doctor evidence invalid');
28
+ insist(doctorResult?.policy?.host_v2_ready===true,'R45 doctor did not verify Host policy v2');
29
+ const completed=clock();
30
+ insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'Windows gate evidence clock invalid');
31
+ const evidence={
32
+ schema:'deadbyte.r45-windows-gate.v1',status:'passed',exit_code:0,version:'0.15.1',
33
+ marker,completed_at_utc:completed.toISOString(),manifest_sha256:manifestSha256,
34
+ doctor:{status:doctorResult.status,manifest_sha256:doctorResult.manifest.sha256,verified_entries:doctorResult.manifest.verified_entries,host_v2_ready:doctorResult.policy.host_v2_ready},
35
+ parent:{release_id:parent.release_id,manifest_sha256:parent.manifest_sha256,
36
+ generation_manifest_sha256:parent.generation_manifest_sha256,
37
+ final_closure_sha256:parent.closure_sha256,final_closure_file_sha256:parent.closure_file_sha256,
38
+ signer_key_id:parent.signer_key_id},
39
+ features:{skill_integrity:true,agent_backend_persistence:true,codex_protocol_contract:true,
40
+ remote_device_identity:true,durable_job_supervisor:true,real_external_supervisor_smoke:true,
41
+ full_regression_required:true,codex_live_binary_required:false,host_inline_native:true,native_hardware_observation:true,doctor_release_truth:true}
42
+ };
43
+ const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');
44
+ const dir=path.join(path.resolve(stateRoot),'evidence','release');await mkdir(dir,{recursive:true});
45
+ const file=path.join(dir,`r45-windows-gate-${manifestSha256.slice(0,16)}.json`);
46
+ try{
47
+ const handle=await open(file,'wx',0o600);try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
48
+ return {status:'written',path:file,sha256:sha256(bytes),manifest_sha256:manifestSha256,parent:evidence.parent};
49
+ }catch(error){
50
+ if(error?.code!=='EEXIST')throw error;
51
+ const existingBytes=await readFile(file);
52
+ const existing=JSON.parse(existingBytes.toString('utf8').replace(/^\uFEFF/,''));
53
+ insist(existing.schema===evidence.schema&&existing.status==='passed'&&existing.exit_code===0,'existing R45 Windows gate evidence invalid');
54
+ insist(existing.version==='0.15.1'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing R45 Windows gate identity mismatch');
55
+ insist(existing.parent?.release_id===parent.release_id&&existing.parent?.final_closure_sha256===parent.closure_sha256,'existing R45 parent evidence mismatch');
56
+ return {status:'existing',path:file,sha256:sha256(existingBytes),manifest_sha256:manifestSha256,parent:existing.parent};
57
+ }
58
+ }
59
+ const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
60
+ if(invoked)writeWindowsGateEvidence().then(value=>console.log(JSON.stringify(value))).catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
@@ -1230,10 +1230,13 @@ async function runRequiredReleaseProfiles({ ctx, events, goal, state, policy, co
1230
1230
  return results;
1231
1231
  }
1232
1232
 
1233
- async function collectNativeHead(state, nativeDiffRuntime, codingRuntime) {
1233
+ async function collectNativeHead(state, nativeDiffRuntime, codingRuntime, { requireCurrentState = true } = {}) {
1234
1234
  const diffBindings = []; const head = new Map();
1235
1235
  for (const item of state.diffs) {
1236
- const checked = await nativeDiffRuntime.verify({ diffId:item.diff_id });
1236
+ const verifier = requireCurrentState
1237
+ ? nativeDiffRuntime.verify.bind(nativeDiffRuntime)
1238
+ : (nativeDiffRuntime.verifyEvidence ?? nativeDiffRuntime.verify).bind(nativeDiffRuntime);
1239
+ const checked = await verifier({ diffId:item.diff_id });
1237
1240
  if (!checked.ok || !Array.isArray(checked.final_states)) throw new Error(`release diff '${item.diff_id}' verification failed`);
1238
1241
  diffBindings.push({ diff_id:item.diff_id, receipt_sha256:checked.receipt_sha256,
1239
1242
  coding_receipts_verified:checked.coding_receipts_verified });
@@ -1243,10 +1246,12 @@ async function collectNativeHead(state, nativeDiffRuntime, codingRuntime) {
1243
1246
  }
1244
1247
  }
1245
1248
  const finalSourceStates = [...head.values()].sort((a,b) => `${a.root_id}/${a.path}`.localeCompare(`${b.root_id}/${b.path}`));
1246
- for (const expected of finalSourceStates) {
1247
- const current = await codingRuntime.fileState({ rootId:expected.root_id, relativePath:expected.path });
1248
- if (current.exists !== expected.exists || current.sha256 !== expected.sha256) {
1249
- throw new Error(`release native HEAD mismatch '${expected.path}'`);
1249
+ if (requireCurrentState) {
1250
+ for (const expected of finalSourceStates) {
1251
+ const current = await codingRuntime.fileState({ rootId:expected.root_id, relativePath:expected.path });
1252
+ if (current.exists !== expected.exists || current.sha256 !== expected.sha256) {
1253
+ throw new Error(`release native HEAD mismatch '${expected.path}'`);
1254
+ }
1250
1255
  }
1251
1256
  }
1252
1257
  return { diffBindings, finalSourceStates };
@@ -1309,7 +1314,7 @@ async function verifyReleaseReceipt({ ctx, goal, events, policy, verifyKeyPath,
1309
1314
  return { ok:false, reason:'release profile evidence mismatch' };
1310
1315
  }
1311
1316
  let nativeHead;
1312
- try { nativeHead = await collectNativeHead(preCompletionState,nativeDiffRuntime,codingRuntime); }
1317
+ try { nativeHead = await collectNativeHead(preCompletionState,nativeDiffRuntime,codingRuntime,{requireCurrentState:false}); }
1313
1318
  catch (error) { return { ok:false, reason:truncated(error) }; }
1314
1319
  if (canonicalJson(nativeHead.diffBindings) !== canonicalJson(body.native_diffs ?? [])) {
1315
1320
  return { ok:false, reason:'release native diff binding mismatch' };
@@ -135,6 +135,7 @@ export const R36_CAPABILITY_ENTRIES=Object.freeze([
135
135
  gateway('observation.search_cancel','machine_search_cancel','machine_observe','search_cancel','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
136
136
  gateway('observation.process_list','machine_process_list','machine_observe','process_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
137
137
  gateway('observation.system_status','machine_system_status','machine_observe','system_status','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
138
+ gateway('observation.hardware_query','machine_hardware_query','machine_observe','hardware_query','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
138
139
 
139
140
  gateway('desktop.status','desktop_status','desktop_control','status','DEADBYTE_DESKTOP_RUNTIME','none',{receipt_class:'desktop'}),
140
141
  gateway('desktop.observe','desktop_observe','desktop_control','observe','DEADBYTE_DESKTOP_RUNTIME','none',{receipt_class:'desktop'}),
@@ -52,7 +52,8 @@ export const machineObserveInputSchema=z.discriminatedUnion('op',[
52
52
  z.strictObject({op:z.literal('search_status'),search_id:hex64}),
53
53
  z.strictObject({op:z.literal('search_cancel'),search_id:hex64}),
54
54
  z.strictObject({op:z.literal('process_list'),limit:z.number().int().min(1).max(256).default(100),include_external:z.boolean().default(false),cursor:z.string().max(512).nullable().default(null)}),
55
- z.strictObject({op:z.literal('system_status')})
55
+ z.strictObject({op:z.literal('system_status')}),
56
+ z.strictObject({op:z.literal('hardware_query'),query:z.enum(['cpu','gpu','memory','storage','network','pci'])})
56
57
  ]);
57
58
 
58
59
  const capabilityManifestInputSchema=z.strictObject({});
@@ -114,7 +115,8 @@ async function machineObserve(args,machineRuntime,observationRuntime){
114
115
  search_status:r=>r.searchStatus({searchId:args.search_id}),
115
116
  search_cancel:r=>r.searchCancel({searchId:args.search_id}),
116
117
  process_list:r=>r.processList({limit:args.limit,includeExternal:args.include_external,cursor:args.cursor}),
117
- system_status:r=>r.systemStatus()
118
+ system_status:r=>r.systemStatus(),
119
+ hardware_query:r=>r.hardwareQuery({query:args.query})
118
120
  };
119
121
  return invoke(args.op,observationRuntime,'observation',map[args.op]);
120
122
  }
@@ -10,12 +10,14 @@ import { defaultPublicKeyPath } from './trust.mjs';
10
10
  import { verifyAuditChain } from './audit-verify.mjs';
11
11
  import { openRemoteResultJournal, parseRemoteResultCursor } from './remote-result-journal.mjs';
12
12
  import { DEADBYTE_VERSION } from './version.mjs';
13
+ import { printDoctor } from './doctor-r45.mjs';
13
14
  import { createLiveTimelineTracker, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine } from './remote-live-log.mjs';
14
15
  import { prepareRemoteConsole, remoteDisplayText } from './remote-console.mjs';
15
16
 
16
17
  const REMOTE_COMMANDS=new Set([
17
18
  'menu','start','resume','park','stop','restart','status','url','test','fulltest','toggle','clean','machinetest',
18
19
  'armprocess','disarmprocess','processstatus','armhost','disarmhost','hoststatus','hosttest',
20
+ 'armdesktop','disarmdesktop','desktopstatus',
19
21
  'armcoding','disarmcoding','codingstatus','codingtest','armautonomous','disarmautonomous','autonomousstatus','autonomoustest'
20
22
  ]);
21
23
  const SESSION_ARM=['armprocess','armcoding','armautonomous'];
@@ -35,17 +37,19 @@ const SUPERVISOR_COMMANDS=Object.freeze({
35
37
  function stateRoot(){return path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));}
36
38
  async function requireRegular(file,label){const info=await lstat(file);if(!info.isFile()||info.isSymbolicLink()) throw new Error(`${label} must be a regular non-symlink file`);return file;}
37
39
  function printHelp(){
40
+ const exact=`deadbyte-mcp@${DEADBYTE_VERSION}`;
38
41
  process.stdout.write([
39
42
  'DEADBYTE MCP CLI','',
40
- ' npx deadbyte-mcp@latest mcp # stdio MCP server for local clients',
41
- ' npx deadbyte-mcp@latest remote # unified foreground device session + live timeline',
42
- ' npx deadbyte-mcp@latest remote --resume <cursor> # replay missed results, then reconnect',
43
- ' npx deadbyte-mcp@latest remote status',
44
- ' npx deadbyte-mcp@latest remote restart',
45
- ' npx deadbyte-mcp@latest supervisor status',
46
- ' npx deadbyte-mcp@latest supervisor recover',
47
- ' npx deadbyte-mcp@latest audit verify',
48
- ' npx deadbyte-mcp@latest audit status',''
43
+ ` npx -y ${exact} doctor # verify current package/release truth`,
44
+ ` npx -y ${exact} mcp # stdio MCP server for local clients`,
45
+ ` npx -y ${exact} remote # unified foreground device session + live timeline`,
46
+ ` npx -y ${exact} remote --resume <cursor> # replay missed results, then reconnect`,
47
+ ` npx -y ${exact} remote status`,
48
+ ` npx -y ${exact} remote restart`,
49
+ ` npx -y ${exact} supervisor status`,
50
+ ` npx -y ${exact} supervisor recover`,
51
+ ` npx -y ${exact} audit verify`,
52
+ ` npx -y ${exact} audit status`,''
49
53
  ].join('\n'));
50
54
  }
51
55
  async function remoteContext(){
@@ -388,6 +392,10 @@ async function main(argv){
388
392
  process.stdout.write(`${DEADBYTE_VERSION}\n`);return;
389
393
  }
390
394
  if(!command||command==='help'||command==='--help'||command==='-h'){printHelp();return;}
395
+ if(command==='doctor'){
396
+ if(rest.length) throw new Error('doctor accepts no arguments');
397
+ await printDoctor();return;
398
+ }
391
399
  if(command==='mcp'){
392
400
  if(rest.length) throw new Error('mcp accepts no arguments');
393
401
  await import('./mcp-server.mjs');