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
@@ -3,6 +3,7 @@ import { spawnSync } from 'node:child_process';
3
3
  import { lstat, mkdir, open, readFile, readdir, realpath } from 'node:fs/promises';
4
4
  import os from 'node:os';
5
5
  import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
6
7
  import { canonicalJson } from './canonical-json.mjs';
7
8
  import { decodeUtf8Strict } from './text-policy.mjs';
8
9
  import { isInside, validatePortableRelativePath } from './path-policy.mjs';
@@ -13,6 +14,7 @@ const RECEIPT_DOMAIN=Buffer.from('DEADBYTE-OBSERVATION-RECEIPT-V1\0','utf8');
13
14
  const ZERO='0'.repeat(64);
14
15
  const HEX64=/^[0-9a-f]{64}$/;
15
16
  const ID=/^[A-Za-z0-9._-]{1,64}$/;
17
+ const HARDWARE_QUERIES=new Set(['cpu','gpu','memory','storage','network','pci']);
16
18
  const sha256Bytes=bytes=>createHash('sha256').update(bytes).digest('hex');
17
19
  const sha256Object=value=>sha256Bytes(Buffer.from(canonicalJson(value),'utf8'));
18
20
  const nowUtc=()=>new Date().toISOString();
@@ -110,12 +112,28 @@ function defaultSystemSnapshotProvider(){
110
112
  return {hostname:os.hostname(),platform:process.platform,architecture:process.arch,uptime_seconds:Math.floor(os.uptime()),
111
113
  total_memory_bytes:os.totalmem(),free_memory_bytes:os.freemem(),cpu_count:os.cpus().length,load_average:os.loadavg().map(Number)};
112
114
  }
115
+ async function defaultHardwareSnapshotProvider(query){
116
+ if(process.platform!=='win32') throw new Error('native hardware observation is supported on win32 only');
117
+ if(!HARDWARE_QUERIES.has(query)) throw new Error('unsupported hardware query');
118
+ const executable=fileURLToPath(new URL('../bin/deadbyte-hardware-observer-r45.exe',import.meta.url));
119
+ const executableSha256=sha256Bytes(await readFile(executable));
120
+ const result=spawnSync(executable,[query],{encoding:'utf8',windowsHide:true,shell:false,timeout:15000,maxBuffer:16*1024*1024});
121
+ if(result.error) throw result.error;
122
+ if(result.status!==0) throw new Error(`hardware observation failed query=${query}: ${String(result.stderr??result.stdout??'').trim()}`);
123
+ let parsed;
124
+ try{parsed=JSON.parse(String(result.stdout??'').trim());}
125
+ catch{throw new Error(`hardware observation returned invalid JSON query=${query}`);}
126
+ if(parsed?.schema!=='deadbyte.hardware-query.v1'||parsed?.query!==query||!parsed.data||typeof parsed.data!=='object'||Array.isArray(parsed.data)){
127
+ throw new Error(`hardware observation contract mismatch query=${query}`);
128
+ }
129
+ return {...parsed,observer_sha256:executableSha256};
130
+ }
113
131
  function requestSha(policy,operation,args){
114
132
  return sha256Object({schema:'deadbyte.observation-request.v1',policy_sha256:policy.policy_sha256,operation,args});
115
133
  }
116
134
  export function createObservationRuntime({policy,signingKeyPath,verifyKeyPath,processRuntime=null,stateRoot=path.join(os.homedir(),'.deadbyte-mcp'),
117
135
  processSnapshotProvider=defaultProcessSnapshotProvider,systemSnapshotProvider=defaultSystemSnapshotProvider,
118
- ownedRuntimePidProvider=defaultOwnedRuntimePidProvider}={}){
136
+ ownedRuntimePidProvider=defaultOwnedRuntimePidProvider,hardwareSnapshotProvider=defaultHardwareSnapshotProvider}={}){
119
137
  if(!policy||!signingKeyPath||!verifyKeyPath) throw new Error('observation runtime requires policy and trust keys');
120
138
  const searches=new Map(),snapshots=new Map();
121
139
  let signingPromise=null;
@@ -253,5 +271,18 @@ export function createObservationRuntime({policy,signingKeyPath,verifyKeyPath,pr
253
271
  const receipt=await writeReceipt(runtime,'machine_system_status',req,head,{observed_at_utc:observedAt});
254
272
  return {...result,request_sha256:req,result_head_sha256:head,...receipt};
255
273
  }
256
- return Object.freeze({policy,searchStart,searchPage,searchStatus,searchCancel,processList,systemStatus});
274
+ async function hardwareQuery({query}={}){
275
+ if(typeof query!=='string'||!HARDWARE_QUERIES.has(query)) throw new Error('hardware query must be one of cpu,gpu,memory,storage,network,pci');
276
+ const observedAt=nowUtc();
277
+ const snapshot=await hardwareSnapshotProvider(query);
278
+ if(!snapshot||snapshot.schema!=='deadbyte.hardware-query.v1'||snapshot.query!==query||!snapshot.data||typeof snapshot.data!=='object'||Array.isArray(snapshot.data)){
279
+ throw new Error('hardware query provider contract mismatch');
280
+ }
281
+ const result={status:'ok',observed_at_utc:observedAt,query,data:snapshot.data};
282
+ if(typeof snapshot.observer_sha256==='string'&&HEX64.test(snapshot.observer_sha256)) result.observer_sha256=snapshot.observer_sha256;
283
+ const req=requestSha(policy,'machine_hardware_query',{query}); const head=sha256Object(result);
284
+ const receipt=await writeReceipt(runtime,'machine_hardware_query',req,head,{observed_at_utc:observedAt,query,observer_sha256:result.observer_sha256??null});
285
+ return {...result,request_sha256:req,result_head_sha256:head,...receipt};
286
+ }
287
+ return Object.freeze({policy,searchStart,searchPage,searchStatus,searchCancel,processList,systemStatus,hardwareQuery});
257
288
  }
@@ -57,7 +57,7 @@ const coding=['coding_benchmark_run','coding_command_run','coding_file_read','co
57
57
  'coding_session_status','coding_stat','coding_symbol_definition','coding_symbol_diagnostics','coding_symbol_implementations','coding_symbol_outline','coding_symbol_references','coding_symbol_rename_preview','coding_symbol_replace',
58
58
  'coding_task_approve','coding_task_cancel','coding_task_events','coding_task_run','coding_task_status',
59
59
  'coding_task_submit','coding_task_verify','coding_text_replace','coding_tree'];
60
- const observation=['machine_process_list','machine_search_cancel','machine_search_page','machine_search_start','machine_search_status','machine_system_status'];
60
+ const observation=['machine_hardware_query','machine_process_list','machine_search_cancel','machine_search_page','machine_search_start','machine_search_status','machine_system_status'];
61
61
  const expected=[...base,...machine,...coding,...observation].sort();
62
62
  try{
63
63
  await client.connect(transport);
@@ -70,6 +70,10 @@ try{
70
70
  if(system.status!=='ok'||!/^[0-9a-f]{24}$/.test(system.receipt_id??'')) throw new Error('system observation missing signed receipt');
71
71
  const systemVerify=await verifyObservationReceiptFile({policy,verifyKeyPath:keys.publicKeyPath,receiptId:system.receipt_id});
72
72
  if(!systemVerify.ok||systemVerify.signature_valid!==true||systemVerify.request_sha256!==system.request_sha256) throw new Error('system observation receipt verification failed');
73
+ const hardware=structured(await client.callTool({name:'machine_hardware_query',arguments:{query:'cpu'}}),'machine_hardware_query');
74
+ if(hardware.status!=='ok'||hardware.query!=='cpu'||!/^[0-9a-f]{24}$/.test(hardware.receipt_id??'')||!/^[0-9a-f]{64}$/.test(hardware.observer_sha256??'')) throw new Error('hardware observation missing signed native evidence');
75
+ const hardwareVerify=await verifyObservationReceiptFile({policy,verifyKeyPath:keys.publicKeyPath,receiptId:hardware.receipt_id});
76
+ if(!hardwareVerify.ok||hardwareVerify.signature_valid!==true||hardwareVerify.request_sha256!==hardware.request_sha256) throw new Error('hardware observation receipt verification failed');
73
77
  const processes=structured(await client.callTool({name:'machine_process_list',arguments:{limit:16,include_external:false,cursor:null}}),'machine_process_list');
74
78
  if(processes.status!=='ok'||!/^[0-9a-f]{24}$/.test(processes.receipt_id??'')) throw new Error('process observation missing signed receipt');
75
79
  const started=structured(await client.callTool({name:'machine_search_start',arguments:{root_id:'core',path:'.',pattern:'needle',case_sensitive:true,
@@ -87,7 +91,7 @@ try{
87
91
  if(!searchVerify.ok||searchVerify.signature_valid!==true||searchVerify.result_head_sha256!==status.result_head_sha256) throw new Error('search receipt verification failed');
88
92
  console.log(JSON.stringify({status:'passed',protocol_revision:'2026-07-28',tool_count:names.length,
89
93
  coding_armed:false,search_results:page.results.length,process_snapshot_id:processes.snapshot_id,
90
- system_receipt_id:system.receipt_id,search_receipt_id:status.receipt_id,strict_output_schemas:true,
94
+ system_receipt_id:system.receipt_id,hardware_receipt_id:hardware.receipt_id,hardware_observer_sha256:hardware.observer_sha256,search_receipt_id:status.receipt_id,strict_output_schemas:true,
91
95
  signature_valid:true,policy_sha256:policy.policy_sha256},null,2));
92
96
  }catch(error){
93
97
  const detail=Buffer.concat(stderr).toString('utf8').trim(); if(detail) console.error(`server stderr:\n${detail}`);
@@ -226,17 +226,33 @@ export const hostFileWriteOutputSchema = statusContract({
226
226
  successStatuses: ['committed'], successRequired: ['root_id', 'path', 'previous_sha256', 'sha256', 'bytes', 'created'], errorStatuses: ['denied']
227
227
  });
228
228
 
229
- export const hostExecOutputSchema = statusContract({
230
- statuses: ['passed', 'failed', 'denied'],
231
- shape: {
232
- root_id: z.string().min(1).max(64).optional(), command_id: z.string().min(1).max(64).optional(),
233
- executable_sha256: hex64.optional(), script_sha256: hex64.optional(), exit_code: z.number().int().nullable().optional(),
234
- signal: z.string().nullable().optional(), timed_out: z.boolean().optional(), timeout_ms: z.number().int().min(100).optional(),
235
- stdout: z.string().optional(), stderr: z.string().optional()
236
- },
237
- successStatuses: ['passed', 'failed'],
238
- successRequired: ['root_id', 'command_id', 'executable_sha256', 'script_sha256', 'exit_code', 'signal', 'timed_out', 'timeout_ms', 'stdout', 'stderr'],
239
- errorStatuses: ['denied']
229
+ export const hostExecOutputSchema = z.strictObject({
230
+ status: z.enum(['passed', 'failed', 'denied']),
231
+ error: z.string().optional(),
232
+ root_id: z.string().min(1).max(64).optional(),
233
+ mode: z.enum(['script','inline','native']).optional(),
234
+ command_id: z.string().min(1).max(64).optional(),
235
+ executable_sha256: hex64.optional(),
236
+ script_sha256: hex64.optional(),
237
+ command_sha256: hex64.optional(),
238
+ argv_sha256: hex64.optional(),
239
+ stdout_sha256: hex64.optional(),
240
+ stderr_sha256: hex64.optional(),
241
+ exit_code: z.number().int().nullable().optional(),
242
+ signal: z.string().nullable().optional(),
243
+ timed_out: z.boolean().optional(),
244
+ timeout_ms: z.number().int().min(100).optional(),
245
+ stdout: z.string().optional(),
246
+ stderr: z.string().optional()
247
+ }).superRefine((value,ctx)=>{
248
+ if(value.status==='denied'){
249
+ if(!value.error) ctx.addIssue({code:'custom',path:['error'],message:'error required'});
250
+ return;
251
+ }
252
+ if(value.error!==undefined) ctx.addIssue({code:'custom',path:['error'],message:'error invalid for executed result'});
253
+ requireFields(value,ctx,['root_id','mode','command_id','executable_sha256','argv_sha256','stdout_sha256','stderr_sha256','exit_code','signal','timed_out','timeout_ms','stdout','stderr']);
254
+ if(value.mode==='script'&&value.script_sha256===undefined) ctx.addIssue({code:'custom',path:['script_sha256'],message:'script_sha256 required for script mode'});
255
+ if(value.mode==='inline'&&value.command_sha256===undefined) ctx.addIssue({code:'custom',path:['command_sha256'],message:'command_sha256 required for inline mode'});
240
256
  });
241
257
 
242
258
  export function structuredToolResult(schema, object, isError = false) {
@@ -0,0 +1,74 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { 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 { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
7
+ import { R45_COMPACT_GATEWAYS,R45_COMPACT_TOOL_LIMIT } from './remote-mcp-r45-parity-client.mjs';
8
+ import { loadDesktopPolicy } from './desktop-policy-r40.mjs';
9
+ import { verifyDesktopReceiptObject } from './desktop-runtime-r40.mjs';
10
+ import { loadPublicVerifyKey } from './trust.mjs';
11
+
12
+ const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
13
+ const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
14
+ const verifyKeyPath=path.join(stateRoot,'trust','ed25519-public.pem');
15
+ function structured(result,label){
16
+ if(result.isError) throw new Error(label+' returned error: '+JSON.stringify(result.structuredContent??result.content));
17
+ if(!result.structuredContent||typeof result.structuredContent!=='object') throw new Error(label+' missing structured output');
18
+ return result.structuredContent;
19
+ }
20
+ function gateway(result,label){
21
+ const value=structured(result,label);
22
+ if(value.status!=='ok'||!value.result||typeof value.result!=='object') throw new Error(label+' gateway status='+String(value.status));
23
+ return value.result;
24
+ }
25
+ async function verifyDesktop(policy,verifyKey,id,sha,kind){
26
+ const receipt=JSON.parse(await readFile(path.join(policy.evidence_dir,id+'.json'),'utf8'));
27
+ const proof=verifyDesktopReceiptObject(receipt,{policySha256:policy.policy_sha256,publicKey:verifyKey.publicKey,keyId:verifyKey.keyId});
28
+ if(!proof.ok||proof.receipt_sha256!==sha||proof.kind!==kind) throw new Error('R45 desktop '+kind+' receipt verification failed');
29
+ }
30
+ export async function probeR45CompactDisarmed(endpointText){
31
+ const endpoint=new URL(endpointText);if(endpoint.protocol!=='https:')throw new Error('R45 compact disarmed probe requires HTTPS');
32
+ const pointer=JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
33
+ const desktopPolicy=await loadDesktopPolicy(path.join(pointer.generation_root,'desktop-policy.json'));
34
+ const verifyKey=await loadPublicVerifyKey(verifyKeyPath);
35
+ const client=new Client({name:'deadbyte-r45-compact-disarmed-probe',version:'0.12.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
36
+ const transport=new StreamableHTTPClientTransport(endpoint);
37
+ try{
38
+ await client.connect(transport);
39
+ const listed=await client.listTools(),names=listed.tools.map(t=>t.name).sort();
40
+ if(names.length!==R45_COMPACT_TOOL_LIMIT)throw new Error('R45 compact disarmed catalog must be exactly '+R45_COMPACT_TOOL_LIMIT+', got '+names.length);
41
+ for(const name of R45_COMPACT_GATEWAYS)if(!names.includes(name))throw new Error('R45 compact gateway missing '+name);
42
+ for(const tool of listed.tools)if(tool.outputSchema?.type!=='object'||tool.outputSchema?.additionalProperties!==false||!tool.outputSchema?.properties)throw new Error('tool '+tool.name+' missing strict output schema');
43
+ const manifest=gateway(await client.callTool({name:'capability_manifest',arguments:{}}),'capability_manifest');
44
+ if(manifest.schema!=='deadbyte.capability-manifest.v2'||manifest.profile!=='compact'||manifest.tool_count!==names.length||
45
+ JSON.stringify(manifest.tool_catalog)!==JSON.stringify(names))throw new Error('R45 compact manifest malformed');
46
+ const required=manifest.capabilities.filter(x=>x?.production===true&&x?.host_only!==true);
47
+ for(const item of required)if(typeof item.compact_tool!=='string'||!names.includes(item.compact_tool))throw new Error('compact route missing '+String(item.id));
48
+ const skillCatalog=structured(await client.callTool({name:'skill_catalog',arguments:{op:'list'}}),'skill_catalog');
49
+ if(skillCatalog.status!=='ok'||skillCatalog.op!=='list'||!skillCatalog.result||!Array.isArray(skillCatalog.result.skills)||
50
+ !/^[0-9a-f]{64}$/.test(String(skillCatalog.result.registry_sha256??'')))throw new Error('R45 skill catalog verification failed');
51
+ const coding=structured(await client.callTool({name:'coding_session_status',arguments:{}}),'coding status');
52
+ const autonomous=structured(await client.callTool({name:'autonomous_session_status',arguments:{}}),'autonomous status');
53
+ const desktop=gateway(await client.callTool({name:'desktop_control',arguments:{op:'status'}}),'desktop status');
54
+ if(coding.armed!==false||autonomous.coding_armed!==false||autonomous.autonomous_armed!==false||desktop.armed!==false)throw new Error('R45 closure requires Coding/Autonomous/Desktop DISARMED');
55
+ const observed=gateway(await client.callTool({name:'desktop_control',arguments:{op:'observe',preview:false}}),'desktop observe');
56
+ await verifyDesktop(desktopPolicy,verifyKey,observed.receipt_id,observed.receipt_sha256,'desktop_observation');
57
+ const grounded=gateway(await client.callTool({name:'desktop_control',arguments:{op:'target',frame_id:observed.frame.frame_id,uia_query:{kind:'root',value:'',max_results:1,ordinal:0}}}),'desktop target');
58
+ await verifyDesktop(desktopPolicy,verifyKey,grounded.grounding.receipt_id,grounded.grounding.receipt_sha256,'desktop_grounding');
59
+ const machine=gateway(await client.callTool({name:'machine_observe',arguments:{op:'machine_status'}}),'machine status');
60
+ const nonce='r45-final-'+randomBytes(16).toString('hex');
61
+ const ping=gateway(await client.callTool({name:'machine_observe',arguments:{op:'machine_ping',nonce}}),'machine ping');
62
+ if(ping.nonce!==nonce||ping.release_id!==machine.release_id||ping.release_manifest_sha256!==machine.release_manifest_sha256)throw new Error('R45 machine ping binding mismatch');
63
+ return {status:'passed',profile:'compact',tool_count:names.length,tools:names,catalog_sha256:sha256(Buffer.from(JSON.stringify(names),'utf8')),
64
+ strict_output_schemas:true,ledger_sha256:manifest.ledger_sha256,skill_catalog_verified:true,skill_count:skillCatalog.result.skills.length,capability_count:manifest.capabilities.length,
65
+ production_non_host_capabilities:required.length,capability_closure:true,coding_armed:false,autonomous_armed:false,desktop_armed:false,
66
+ desktop_observation_receipt_verified:true,desktop_grounding_receipt_verified:true,
67
+ desktop_frame_id:observed.frame.frame_id,desktop_target_id:grounded.target.target_id,
68
+ release_id:machine.release_id,release_manifest_sha256:machine.release_manifest_sha256,machine_status:machine,machine_ping:ping,nonce,
69
+ protocol_revision:client.getNegotiatedProtocolVersion()};
70
+ }finally{await client.close().catch(()=>{});}
71
+ }
72
+ const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
73
+ if(invoked){const endpoint=process.argv[2];if(!endpoint)throw new Error('usage: node src/r45-compact-disarmed-probe.mjs https://host/mcp/path');
74
+ probeR45CompactDisarmed(endpoint).then(v=>console.log(JSON.stringify(v,null,2))).catch(e=>{console.error(e instanceof Error?e.stack:String(e));process.exitCode=1;});}
@@ -0,0 +1,34 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { Client } from '@modelcontextprotocol/client';
6
+ import { StdioClientTransport,getDefaultEnvironment } from '@modelcontextprotocol/client/stdio';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { R45_EXPECTED_TOOLS } from './remote-mcp-r45-parity-client.mjs';
9
+ const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
10
+ export async function probeR45FullSurface({packageRoot,generationRoot,stateRoot=path.join(os.homedir(),'.deadbyte-mcp')}={}){
11
+ const pkgRoot=packageRoot?path.resolve(packageRoot):path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
12
+ const gen=path.resolve(generationRoot),semanticPath=path.join(gen,'semantic-lsp-config.json');
13
+ const semanticSha=sha256(await readFile(semanticPath));
14
+ const env={...getDefaultEnvironment(),DEADBYTE_STATE_ROOT:path.resolve(stateRoot),DEADBYTE_CORE_ROOT:pkgRoot,
15
+ DEADBYTE_WORKSPACE_ROOT:path.join(pkgRoot,'workspaces'),DEADBYTE_CAPABILITY_PROFILE:'full',DEADBYTE_AUDIT_RUNTIME:'0',
16
+ DEADBYTE_MACHINE_RUNTIME:'1',DEADBYTE_AGENT_RUNTIME:'1',DEADBYTE_PROCESS_RUNTIME:'1',DEADBYTE_PROCESS_POLICY:path.join(gen,'process-policy.json'),
17
+ DEADBYTE_CODING_RUNTIME:'1',DEADBYTE_MACHINE_FS_RUNTIME:'1',DEADBYTE_OBSERVATION_RUNTIME:'1',
18
+ DEADBYTE_DESKTOP_RUNTIME:'1',DEADBYTE_DESKTOP_POLICY:path.join(gen,'desktop-policy.json'),
19
+ DEADBYTE_CODING_POLICY:path.join(gen,'coding-policy.json'),DEADBYTE_SEMANTIC_LSP_CONFIG:semanticPath,DEADBYTE_SEMANTIC_LSP_CONFIG_SHA256:semanticSha,
20
+ DEADBYTE_AUTONOMOUS_RUNTIME:'1',DEADBYTE_AUTONOMOUS_POLICY:path.join(gen,'autonomous-policy.json'),
21
+ DEADBYTE_AUTONOMOUS_SUPERVISOR_MODE:'external',DEADBYTE_PRIVILEGED_POLICY:path.join(gen,'host-policy.json')};
22
+ const client=new Client({name:'deadbyte-r45-full-surface-probe',version:'0.12.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
23
+ const transport=new StdioClientTransport({command:process.execPath,args:[path.join(pkgRoot,'src','mcp-server.mjs')],cwd:pkgRoot,env,stderr:'pipe'});
24
+ try{
25
+ await client.connect(transport);const listed=await client.listTools(),names=listed.tools.map(t=>t.name).sort();
26
+ if(JSON.stringify(names)!==JSON.stringify(R45_EXPECTED_TOOLS))throw new Error('R45 full catalog mismatch count='+names.length);
27
+ for(const tool of listed.tools)if(tool.outputSchema?.type!=='object'||tool.outputSchema?.additionalProperties!==false||!tool.outputSchema?.properties)throw new Error('R45 full tool '+tool.name+' missing strict output schema');
28
+ return {status:'passed',profile:'full',tool_count:names.length,tools:names,catalog_sha256:sha256(Buffer.from(JSON.stringify(names),'utf8')),
29
+ strict_output_schemas:true,desktop_tools:['desktop_status','desktop_observe','desktop_target','desktop_action'],protocol_revision:client.getNegotiatedProtocolVersion()};
30
+ }finally{await client.close().catch(()=>{});}
31
+ }
32
+ if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){const [packageRoot,generationRoot]=process.argv.slice(2);
33
+ if(!packageRoot||!generationRoot)throw new Error('usage: node src/r45-full-surface-probe.mjs <package-root> <generation-root>');
34
+ probeR45FullSurface({packageRoot,generationRoot}).then(v=>console.log(JSON.stringify(v,null,2))).catch(e=>{console.error(e instanceof Error?e.stack:String(e));process.exitCode=1;});}
@@ -84,6 +84,7 @@ async function rebindAutonomousPolicy(raw,{stateRoot,codingPolicy,codingPolicyBy
84
84
  if(id==='windows-release'){
85
85
  if(profile.kind!=='command'||rootId!=='core'||profile.command_id!=='powershell_script') throw new Error('windows release profile shape invalid');
86
86
  profile.script_path=await currentWindowsGateScript(rootPath);
87
+ profile.timeout_ms=600000;
87
88
  profile.description='Full Windows target release gate; runs only when finish is requested.';
88
89
  }
89
90
  const rel=safeRel(String(profile.script_path).replaceAll('\\','/'));
@@ -0,0 +1,206 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { 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 { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
7
+ import { R34_EXPECTED_TOOLS } from './remote-mcp-r34-parity-client.mjs';
8
+ import { loadCodingPolicy } from './coding-plane.mjs';
9
+ import { verifyMachineFsReceiptFile } from './machine-fs.mjs';
10
+ import { verifyObservationReceiptFile } from './observation-runtime.mjs';
11
+ import { loadDesktopPolicy } from './desktop-policy-r40.mjs';
12
+ import { verifyDesktopReceiptObject } from './desktop-runtime-r40.mjs';
13
+ import { loadPublicVerifyKey } from './trust.mjs';
14
+
15
+ export const R45_EXPECTED_TOOLS=Object.freeze([...new Set([
16
+ ...R34_EXPECTED_TOOLS,
17
+ 'coding_symbol_diagnostics','coding_symbol_implementations','coding_symbol_rename_preview',
18
+ 'desktop_status','desktop_observe','desktop_target','desktop_action','skill_catalog','machine_hardware_query'
19
+ ])].sort());
20
+ if(R45_EXPECTED_TOOLS.length !== 82) throw new Error(`R45 full catalog identity mismatch: ${R45_EXPECTED_TOOLS.length}`);
21
+ export const R45_COMPACT_GATEWAYS=Object.freeze(['capability_manifest','coding_inspect','machine_fs','machine_observe','process_session','desktop_control'].sort());
22
+ export const R45_COMPACT_TOOL_LIMIT=43;
23
+
24
+ const here=path.dirname(fileURLToPath(import.meta.url));
25
+ const repoRoot=path.resolve(here,'..');
26
+ const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
27
+ const verifyKeyPath=path.join(stateRoot,'trust','ed25519-public.pem');
28
+ const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
29
+ const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
30
+
31
+ function structured(result,label){
32
+ if(result.isError) throw new Error(`${label} returned error: ${JSON.stringify(result.structuredContent??result.content)}`);
33
+ if(!result.structuredContent||typeof result.structuredContent!=='object') throw new Error(`${label} missing structured output`);
34
+ return result.structuredContent;
35
+ }
36
+ function gateway(result,label){
37
+ const value=structured(result,label);
38
+ if(value.status!=='ok'||!value.result||typeof value.result!=='object') throw new Error(`${label} gateway status=${String(value.status)} error=${String(value.error??'')}`);
39
+ return value.result;
40
+ }
41
+ async function fileSha(relative){return sha256(await readFile(path.join(repoRoot,...relative.split('/'))));}
42
+ async function verifyMachineReceipt(policy,receiptId,operation){
43
+ const checked=await verifyMachineFsReceiptFile({policy,verifyKeyPath,receiptId});
44
+ if(!checked.ok||checked.signature_valid!==true||checked.operation!==operation) throw new Error(`machine-fs receipt verification failed for ${operation}`);
45
+ return checked;
46
+ }
47
+ async function verifyObservationReceipt(policy,receiptId){
48
+ const checked=await verifyObservationReceiptFile({policy,verifyKeyPath,receiptId});
49
+ if(!checked.ok||checked.signature_valid!==true) throw new Error('observation receipt verification failed');
50
+ return checked;
51
+ }
52
+ async function verifyDesktopReceipt(policy,verifyKey,receiptId,receiptSha256,kind){
53
+ const file=path.join(policy.evidence_dir,`${receiptId}.json`);
54
+ const receipt=JSON.parse(await readFile(file,'utf8'));
55
+ const checked=verifyDesktopReceiptObject(receipt,{policySha256:policy.policy_sha256,publicKey:verifyKey.publicKey,keyId:verifyKey.keyId});
56
+ if(!checked.ok||checked.receipt_sha256!==receiptSha256||checked.kind!==kind) throw new Error(`desktop receipt verification failed for ${kind}`);
57
+ return checked;
58
+ }
59
+ async function waitSearch(client,searchId){
60
+ for(let attempt=0;attempt<200;attempt+=1){
61
+ const status=gateway(await client.callTool({name:'machine_observe',arguments:{op:'search_status',search_id:searchId}}),'search_status');
62
+ if(['completed','cancelled','failed'].includes(status.state)) return status;
63
+ await sleep(25);
64
+ }
65
+ throw new Error('R45 compact search timeout');
66
+ }
67
+ async function waitProcessOutput(client,sessionId){
68
+ for(let attempt=0;attempt<100;attempt+=1){
69
+ const page=gateway(await client.callTool({name:'process_session',arguments:{op:'read',session_id:sessionId,offset:0,max_bytes:65536}}),'process read');
70
+ if(Buffer.from(page.data_base64??'','base64').toString('utf8').includes('DEADBYTE_ACTION_PING')) return page;
71
+ await sleep(50);
72
+ }
73
+ throw new Error('R45 compact process output timeout');
74
+ }
75
+
76
+ export async function runR45CompactParity(endpointText){
77
+ const endpoint=new URL(endpointText);
78
+ if(endpoint.protocol!=='https:') throw new Error('R45 compact parity requires HTTPS');
79
+ const pointer=JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
80
+ const policy=await loadCodingPolicy(path.join(pointer.generation_root,'coding-policy.json'));
81
+ const desktopPolicy=await loadDesktopPolicy(path.join(pointer.generation_root,'desktop-policy.json'));
82
+ const desktopVerifyKey=await loadPublicVerifyKey(verifyKeyPath);
83
+ const client=new Client({name:'deadbyte-r45-compact-parity',version:'0.12.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
84
+ const transport=new StreamableHTTPClientTransport(endpoint);
85
+ const tag=randomBytes(8).toString('hex');
86
+ const scratchRoot='parity_scratch';
87
+ const target=`.deadbyte-r45-parity-${tag}.txt`;
88
+ let processSession=null,targetPresent=false;
89
+ const machineReceipts=[],observationReceipts=[];
90
+ try{
91
+ await client.connect(transport);
92
+ const listed=await client.listTools();
93
+ const names=listed.tools.map(tool=>tool.name).sort();
94
+ if(names.length>R45_COMPACT_TOOL_LIMIT) throw new Error(`R45 compact catalog exceeds ${R45_COMPACT_TOOL_LIMIT}: ${names.length}`);
95
+ if(names.length!==R45_COMPACT_TOOL_LIMIT) throw new Error(`R45 compact catalog must be exactly ${R45_COMPACT_TOOL_LIMIT}, got ${names.length}`);
96
+ for(const name of R45_COMPACT_GATEWAYS) if(!names.includes(name)) throw new Error(`R45 compact gateway missing ${name}`);
97
+ for(const tool of listed.tools){
98
+ if(tool.outputSchema?.type!=='object'||tool.outputSchema?.additionalProperties!==false||!tool.outputSchema?.properties) throw new Error(`tool ${tool.name} missing strict output schema`);
99
+ }
100
+ const manifest=gateway(await client.callTool({name:'capability_manifest',arguments:{}}),'capability_manifest');
101
+ if(manifest.schema!=='deadbyte.capability-manifest.v2'||manifest.profile!=='compact'||!Array.isArray(manifest.capabilities)||!/^[0-9a-f]{64}$/.test(String(manifest.ledger_sha256??''))) throw new Error('R45 compact capability manifest malformed');
102
+ if(manifest.tool_count!==names.length||JSON.stringify(manifest.tool_catalog)!==JSON.stringify(names)||
103
+ manifest.catalog_sha256!==sha256(Buffer.from(JSON.stringify(names),'utf8'))) throw new Error('R45 compact manifest catalog binding mismatch');
104
+ const required=manifest.capabilities.filter(item=>item?.production===true&&item?.host_only!==true);
105
+ for(const item of required){
106
+ if(typeof item.compact_tool!=='string'||!names.includes(item.compact_tool)) throw new Error(`compact capability route missing: ${String(item.id)}`);
107
+ }
108
+ const skillCatalog=structured(await client.callTool({name:'skill_catalog',arguments:{op:'list'}}),'skill_catalog');
109
+ if(skillCatalog.status!=='ok'||skillCatalog.op!=='list'||!skillCatalog.result||!Array.isArray(skillCatalog.result.skills)||
110
+ !/^[0-9a-f]{64}$/.test(String(skillCatalog.result.registry_sha256??''))) throw new Error('R45 skill catalog verification failed');
111
+ const coding=structured(await client.callTool({name:'coding_session_status',arguments:{}}),'coding_session_status');
112
+ const autonomous=structured(await client.callTool({name:'autonomous_session_status',arguments:{}}),'autonomous_session_status');
113
+ if(coding.armed!==true||autonomous.coding_armed!==true||autonomous.autonomous_armed!==true) throw new Error('R45 compact parity requires coding and autonomous standing grants');
114
+ const desktopStatus=gateway(await client.callTool({name:'desktop_control',arguments:{op:'status'}}),'desktop status');
115
+ if(desktopStatus.configured!==true||desktopStatus.armed!==false) throw new Error('R45 compact parity requires Desktop configured and DISARMED');
116
+ const desktopObserved=gateway(await client.callTool({name:'desktop_control',arguments:{op:'observe',preview:false}}),'desktop observe');
117
+ await verifyDesktopReceipt(desktopPolicy,desktopVerifyKey,desktopObserved.receipt_id,desktopObserved.receipt_sha256,'desktop_observation');
118
+ const desktopTarget=gateway(await client.callTool({name:'desktop_control',arguments:{op:'target',frame_id:desktopObserved.frame.frame_id,uia_query:{kind:'root',value:'',max_results:1,ordinal:0}}}),'desktop target');
119
+ await verifyDesktopReceipt(desktopPolicy,desktopVerifyKey,desktopTarget.grounding.receipt_id,desktopTarget.grounding.receipt_sha256,'desktop_grounding');
120
+ if(desktopTarget.target?.source!=='uia') throw new Error('R45 compact UIA root grounding missing');
121
+ const tree=structured(await client.callTool({name:'coding_tree',arguments:{root_id:'core',path:'workspaces/demo',max_depth:2,max_entries:128}}),'coding_tree');
122
+ if(tree.status!=='ok'||!Array.isArray(tree.entries)) throw new Error('coding_tree malformed');
123
+ const stat=gateway(await client.callTool({name:'coding_inspect',arguments:{op:'stat',root_id:'core',path:'package.json',hash:true}}),'coding stat');
124
+ if(stat.type!=='file'||!/^[0-9a-f]{64}$/.test(String(stat.sha256??''))||!/^[0-9a-f]{64}$/.test(String(stat.observation_sha256??''))) throw new Error('coding stat identity missing');
125
+ const search=gateway(await client.callTool({name:'coding_inspect',arguments:{op:'search',root_id:'core',path:'src',pattern:'R36_CAPABILITY_LEDGER',target:'content',mode:'literal',case_sensitive:true,max_results:20,max_file_bytes:262144,max_scan_bytes:4194304,page_size:20,cursor:null}}),'coding search');
126
+ if(!Array.isArray(search.matches)||search.matches.length<1) throw new Error('coding search returned no canonical ledger match');
127
+
128
+ const buildScript='scripts/autonomous-build.ps1';
129
+ const testScript='scripts/release-parity-tests-r45.ps1';
130
+ const build=structured(await client.callTool({name:'coding_command_run',arguments:{root_id:'core',command_id:'powershell_script',script_path:buildScript,script_sha256:await fileSha(buildScript),args:[],cwd:'.',timeout_ms:120000}},{timeout:180000,maxTotalTimeout:180000}),'coding build');
131
+ if(build.status!=='passed'||build.exit_code!==0) throw new Error('R45 coding build failed');
132
+ const buildVerify=structured(await client.callTool({name:'coding_receipt_verify',arguments:{receipt_id:build.receipt_id}}),'coding build receipt');
133
+ if(buildVerify.ok!==true||buildVerify.signature_valid!==true) throw new Error('R45 coding build receipt invalid');
134
+ const tests=structured(await client.callTool({name:'coding_command_run',arguments:{root_id:'core',command_id:'powershell_script',script_path:testScript,script_sha256:await fileSha(testScript),args:[],cwd:'.',timeout_ms:300000}},{timeout:360000,maxTotalTimeout:360000}),'coding tests');
135
+ if(tests.status!=='passed'||tests.exit_code!==0) throw new Error('R45 coding tests failed');
136
+ const testVerify=structured(await client.callTool({name:'coding_receipt_verify',arguments:{receipt_id:tests.receipt_id}}),'coding test receipt');
137
+ if(testVerify.ok!==true||testVerify.signature_valid!==true) throw new Error('R45 coding test receipt invalid');
138
+
139
+ const initial=`needle-${tag}\n`;
140
+ const write=gateway(await client.callTool({name:'machine_fs',arguments:{op:'write',root_id:scratchRoot,path:target,content:initial,encoding:'utf8',expected_sha256:'absent'}}),'machine_fs write');
141
+ targetPresent=true;machineReceipts.push(write.receipt_id);await verifyMachineReceipt(policy,write.receipt_id,'machine_fs_write');
142
+ const bounded=gateway(await client.callTool({name:'machine_fs',arguments:{op:'read',root_id:scratchRoot,path:target,offset:0,max_bytes:128,hash_file:true}}),'machine_fs read');
143
+ if(Buffer.from(bounded.data_base64,'base64').toString('utf8')!==initial||bounded.file_sha256!==write.sha256) throw new Error('R45 bounded machine read mismatch');
144
+ const edit=gateway(await client.callTool({name:'machine_fs',arguments:{op:'edit_exact',root_id:scratchRoot,path:target,expected_sha256:write.sha256,old_string:'needle',new_string:'beta',expected_replacements:1}}),'machine_fs edit');
145
+ machineReceipts.push(edit.receipt_id);await verifyMachineReceipt(policy,edit.receipt_id,'machine_fs_edit_exact');
146
+ const stale=await client.callTool({name:'machine_fs',arguments:{op:'edit_exact',root_id:scratchRoot,path:target,expected_sha256:write.sha256,old_string:'beta',new_string:'bad',expected_replacements:1}});
147
+ const staleValue=stale.structuredContent;
148
+ if(!staleValue||typeof staleValue!=='object') throw new Error('R45 stale machine-fs denial missing structured output');
149
+ if(stale.isError!==true||staleValue.status!=='denied'||!/precondition mismatch/i.test(String(staleValue.error??''))) {
150
+ throw new Error(`R45 stale machine-fs precondition was not denied: ${JSON.stringify(staleValue)}`);
151
+ }
152
+
153
+ const searchStart=gateway(await client.callTool({name:'machine_observe',arguments:{op:'search_start',root_id:scratchRoot,path:'.',pattern:tag,case_sensitive:true,max_results:20,max_files:200,max_bytes:262144,max_file_bytes:16384}}),'observe search start');
154
+ const searchStatus=await waitSearch(client,searchStart.search_id);
155
+ if(searchStatus.state!=='completed'||!searchStatus.receipt_id) throw new Error('R45 compact observation search failed');
156
+ observationReceipts.push(searchStatus.receipt_id);await verifyObservationReceipt(policy,searchStatus.receipt_id);
157
+ const page=gateway(await client.callTool({name:'machine_observe',arguments:{op:'search_page',search_id:searchStart.search_id,limit:20,cursor:null}}),'observe search page');
158
+ if(!page.results.some(item=>item.path===target)) throw new Error('R45 compact search did not return parity file');
159
+ const system=gateway(await client.callTool({name:'machine_observe',arguments:{op:'system_status'}}),'system status');
160
+ observationReceipts.push(system.receipt_id);await verifyObservationReceipt(policy,system.receipt_id);
161
+ const hardware=gateway(await client.callTool({name:'machine_observe',arguments:{op:'hardware_query',query:'cpu'}}),'hardware query');
162
+ if(hardware.query!=='cpu'||!hardware.data||typeof hardware.data!=='object'||!/^[0-9a-f]{64}$/.test(String(hardware.observer_sha256??''))) throw new Error('R45 native hardware observation malformed');
163
+ observationReceipts.push(hardware.receipt_id);await verifyObservationReceipt(policy,hardware.receipt_id);
164
+ const machineStatus=gateway(await client.callTool({name:'machine_observe',arguments:{op:'machine_status'}}),'machine status');
165
+ if(machineStatus.release_id!==pointer.release_id||machineStatus.release_manifest_sha256!==pointer.manifest_sha256) throw new Error('R45 compact machine status release mismatch');
166
+
167
+ const started=gateway(await client.callTool({name:'process_session',arguments:{op:'start',profile_id:'ping_hold',root_id:'core',cwd:'.',args:[]}}),'process start');
168
+ processSession=started.session_id??started.session?.session_id;
169
+ if(!/^[0-9a-f]{64}$/.test(String(processSession??''))) throw new Error('R45 compact process session id missing');
170
+ const action=gateway(await client.callTool({name:'process_session',arguments:{op:'action',session_id:processSession,action_id:'ping'}}),'process action');
171
+ const processRead=await waitProcessOutput(client,processSession);
172
+ const terminated=gateway(await client.callTool({name:'process_session',arguments:{op:'terminate',session_id:processSession}}),'process terminate');
173
+ const terminalState=terminated.state??terminated.session?.state;
174
+ if(action.action_id!=='ping'||terminalState!=='terminated') throw new Error('R45 compact process lifecycle failed');
175
+
176
+ const fsStat=gateway(await client.callTool({name:'machine_fs',arguments:{op:'stat',root_id:scratchRoot,path:target,hash:true}}),'machine_fs stat');
177
+ const deleted=gateway(await client.callTool({name:'machine_fs',arguments:{op:'delete',root_id:scratchRoot,path:target,expected_source_identity:fsStat.identity_sha256}}),'machine_fs delete');
178
+ machineReceipts.push(deleted.receipt_id);await verifyMachineReceipt(policy,deleted.receipt_id,'machine_fs_delete');targetPresent=false;
179
+
180
+ return {status:'passed',protocol_revision:client.getNegotiatedProtocolVersion(),profile:'compact',tool_count:names.length,tools:names,
181
+ catalog_sha256:sha256(Buffer.from(JSON.stringify(names),'utf8')),strict_output_schemas:true,ledger_sha256:manifest.ledger_sha256,
182
+ capability_count:manifest.capabilities.length,production_non_host_capabilities:required.length,capability_closure:true,
183
+ gateway_count:R45_COMPACT_GATEWAYS.length,skill_catalog_verified:true,skill_count:skillCatalog.result.skills.length,tree_entries:tree.entries.length,coding_stat_verified:true,coding_search_result_count:search.matches.length,
184
+ desktop_armed:false,desktop_observation_receipt_verified:true,desktop_grounding_receipt_verified:true,
185
+ desktop_frame_id:desktopObserved.frame.frame_id,desktop_target_id:desktopTarget.target.target_id,
186
+ machine_fs_receipts_verified:machineReceipts.length,observation_receipts_verified:observationReceipts.length,
187
+ observation_search_result_count:page.results.length,native_hardware_query_verified:true,hardware_observer_sha256:hardware.observer_sha256,process_session_id:processSession,process_output_bytes:processRead.stream_head,
188
+ process_journal_head_sha256:terminated.journal_head_sha256??terminated.session?.journal_head_sha256,
189
+ build_receipt_id:build.receipt_id,test_receipt_id:tests.receipt_id,coding_receipts_verified:2,
190
+ execution_provider:'deadbyte_mcp',rdc_operation_count:0};
191
+ } finally {
192
+ if(processSession) await client.callTool({name:'process_session',arguments:{op:'terminate',session_id:processSession}}).catch(()=>{});
193
+ if(targetPresent){
194
+ try{const fsStat=gateway(await client.callTool({name:'machine_fs',arguments:{op:'stat',root_id:scratchRoot,path:target,hash:false}}),'cleanup stat');
195
+ if(fsStat.identity_sha256) await client.callTool({name:'machine_fs',arguments:{op:'delete',root_id:scratchRoot,path:target,expected_source_identity:fsStat.identity_sha256}}).catch(()=>{});
196
+ }catch{}
197
+ }
198
+ await client.close().catch(()=>{});
199
+ }
200
+ }
201
+
202
+ if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){
203
+ const endpoint=process.argv[2];
204
+ if(!endpoint) throw new Error('usage: node src/remote-mcp-r45-parity-client.mjs https://host/mcp/path');
205
+ runR45CompactParity(endpoint).then(result=>console.log(JSON.stringify(result,null,2))).catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
206
+ }
@@ -45,14 +45,17 @@ function normalizePersistentPublicOrigin(value){
45
45
  return publicOrigin.origin;
46
46
  }
47
47
  export function parseUpdateArgs(argv){
48
- const out={stage:null,developmentRoot:null,expectedVersion:null,expectedManifest:null,sourceRoot:null,restoreStandingGrants:false,recoverOfflinePredecessor:false,enableMachineFs:false,enableObservation:false,enableDesktop:false,enableAutonomousSupervisor:false,cloudTransport:null,bridgeBootstrap:null,processPolicyBootstrap:null,desktopPolicyBootstrap:null,semanticLspBootstrap:null,hostCommandPins:[]};
49
- const cloud={},bridge={},processPolicy={},desktopPolicy={},semanticLsp={},seen=new Set(),hostPinIds=new Set();
48
+ const out={stage:null,developmentRoot:null,expectedVersion:null,expectedManifest:null,sourceRoot:null,restoreStandingGrants:false,recoverOfflinePredecessor:false,enableMachineFs:false,enableObservation:false,enableDesktop:false,enableAutonomousSupervisor:false,cloudTransport:null,bridgeBootstrap:null,processPolicyBootstrap:null,hostPolicyBootstrap:null,desktopPolicyBootstrap:null,semanticLspBootstrap:null,hostCommandPins:[]};
49
+ const cloud={},bridge={},processPolicy={},hostPolicy={},desktopPolicy={},semanticLsp={},seen=new Set(),hostPinIds=new Set();
50
50
  const bridgeFlags=new Map([
51
51
  ['--bridge-root','root'],['--bridge-script-sha256','scriptSha256']
52
52
  ]);
53
53
  const processPolicyFlags=new Map([
54
54
  ['--process-policy','path'],['--process-policy-sha256','sha256']
55
55
  ]);
56
+ const hostPolicyFlags=new Map([
57
+ ['--host-policy','path'],['--host-policy-sha256','sha256']
58
+ ]);
56
59
  const desktopPolicyFlags=new Map([
57
60
  ['--desktop-policy','path'],['--desktop-policy-sha256','sha256']
58
61
  ]);
@@ -64,7 +67,7 @@ export function parseUpdateArgs(argv){
64
67
  ['--cloud-token-file','tokenFilePath'],['--cloud-token-sha256','tokenFileSha256'],
65
68
  ['--cloud-child-working-directory','childWorkingDirectory'],['--cloud-public-origin','publicOrigin']
66
69
  ]);
67
- const valued=new Set(['--stage','--development-root','--expected-version','--expected-manifest','--source-root','--host-command-pin',...bridgeFlags.keys(),...processPolicyFlags.keys(),...desktopPolicyFlags.keys(),...semanticLspFlags.keys(),...cloudFlags.keys()]);
70
+ const valued=new Set(['--stage','--development-root','--expected-version','--expected-manifest','--source-root','--host-command-pin',...bridgeFlags.keys(),...processPolicyFlags.keys(),...hostPolicyFlags.keys(),...desktopPolicyFlags.keys(),...semanticLspFlags.keys(),...cloudFlags.keys()]);
68
71
  for(let index=0;index<argv.length;index++){
69
72
  const arg=argv[index];
70
73
  if(arg==='--restore-standing-grants'||arg==='--recover-offline-predecessor'||arg==='--enable-machine-fs'||arg==='--enable-observation'||arg==='--enable-desktop'||arg==='--enable-autonomous-supervisor'){
@@ -99,6 +102,7 @@ export function parseUpdateArgs(argv){
99
102
  else if(arg==='--source-root') out.sourceRoot=path.resolve(value);
100
103
  else if(bridgeFlags.has(arg)) bridge[bridgeFlags.get(arg)]=value;
101
104
  else if(processPolicyFlags.has(arg)) processPolicy[processPolicyFlags.get(arg)]=value;
105
+ else if(hostPolicyFlags.has(arg)) hostPolicy[hostPolicyFlags.get(arg)]=value;
102
106
  else if(desktopPolicyFlags.has(arg)) desktopPolicy[desktopPolicyFlags.get(arg)]=value;
103
107
  else if(semanticLspFlags.has(arg)) semanticLsp[semanticLspFlags.get(arg)]=value;
104
108
  else cloud[cloudFlags.get(arg)]=value;
@@ -121,6 +125,13 @@ export function parseUpdateArgs(argv){
121
125
  if(!HEX64.test(sha256)) throw new Error('process policy SHA-256 must be lowercase hex');
122
126
  out.processPolicyBootstrap={path:path.resolve(processPolicy.path),sha256};
123
127
  }
128
+ const hostPolicyFields=[...hostPolicyFlags.values()],hostPolicyPresent=hostPolicyFields.filter(key=>hostPolicy[key]!==undefined);
129
+ if(hostPolicyPresent.length!==0&&hostPolicyPresent.length!==hostPolicyFields.length) throw new Error('host policy bootstrap requires path plus SHA-256 as one pair');
130
+ if(hostPolicyPresent.length===hostPolicyFields.length){
131
+ const sha256=String(hostPolicy.sha256).toLowerCase();
132
+ if(!HEX64.test(sha256)) throw new Error('host policy SHA-256 must be lowercase hex');
133
+ out.hostPolicyBootstrap={path:path.resolve(hostPolicy.path),sha256};
134
+ }
124
135
  const desktopPolicyFields=[...desktopPolicyFlags.values()],desktopPolicyPresent=desktopPolicyFields.filter(key=>desktopPolicy[key]!==undefined);
125
136
  if(desktopPolicyPresent.length!==0&&desktopPolicyPresent.length!==desktopPolicyFields.length) throw new Error('desktop policy bootstrap requires path plus SHA-256 as one pair');
126
137
  if(desktopPolicyPresent.length===desktopPolicyFields.length){
@@ -333,6 +344,20 @@ export async function prepareProcessPolicyBootstrap(bootstrap){
333
344
  return {...pinned,policy};
334
345
  }
335
346
 
347
+ export async function prepareHostPolicyBootstrap(bootstrap){
348
+ if(!bootstrap) return null;
349
+ const pinned=await verifyPinnedRegularFile(bootstrap.path,bootstrap.sha256,'host policy');
350
+ const policy=await readJson(pinned.path);
351
+ if(!['deadbyte.host-policy.v1','deadbyte.host-policy.v2'].includes(policy?.schema)||policy?.enabled!==true||
352
+ !policy?.commands||typeof policy.commands!=='object'||Array.isArray(policy.commands)) {
353
+ throw new Error('host policy schema/enabled/commands mismatch');
354
+ }
355
+ if(policy.schema==='deadbyte.host-policy.v2'&&(policy.native_exec?.enabled!==true||!Array.isArray(policy.native_exec?.root_ids))) {
356
+ throw new Error('host policy v2 native_exec contract invalid');
357
+ }
358
+ return {...pinned,policy};
359
+ }
360
+
336
361
  export async function prepareDesktopPolicyBootstrap(bootstrap){
337
362
  if(!bootstrap) return null;
338
363
  const pinned=await verifyPinnedRegularFile(bootstrap.path,bootstrap.sha256,'desktop policy');
@@ -358,7 +383,8 @@ export async function prepareSemanticLspConfigBootstrap(bootstrap){
358
383
 
359
384
  async function preparePinnedCommandPolicy(policy,{commandPins=[],schema,label}={}){
360
385
  if(!policy) return {policy:null,repinned:[],matchedCommandIds:[]};
361
- if(policy.schema!==schema||policy.enabled!==true||!policy.commands||typeof policy.commands!=='object'||Array.isArray(policy.commands)) {
386
+ const acceptedSchemas=Array.isArray(schema)?schema:[schema];
387
+ if(!acceptedSchemas.includes(policy.schema)||policy.enabled!==true||!policy.commands||typeof policy.commands!=='object'||Array.isArray(policy.commands)) {
362
388
  throw new Error(`${label} policy schema/enabled/commands mismatch`);
363
389
  }
364
390
  const requested=new Map();
@@ -390,7 +416,7 @@ async function preparePinnedCommandPolicy(policy,{commandPins=[],schema,label}={
390
416
 
391
417
  export async function prepareHostPolicy(policy,{commandPins=[]}={}){
392
418
  if(!policy&&commandPins.length) throw new Error('host command pins supplied but host policy is not configured');
393
- const result=await preparePinnedCommandPolicy(policy,{commandPins,schema:'deadbyte.host-policy.v1',label:'host'});
419
+ const result=await preparePinnedCommandPolicy(policy,{commandPins,schema:['deadbyte.host-policy.v1','deadbyte.host-policy.v2'],label:'host'});
394
420
  const matched=new Set(result.matchedCommandIds);
395
421
  const unknown=commandPins.find(pin=>!matched.has(String(pin?.commandId??'')));
396
422
  if(unknown) throw new Error(`unknown host command pin '${unknown.commandId}'`);
@@ -398,7 +424,7 @@ export async function prepareHostPolicy(policy,{commandPins=[]}={}){
398
424
  }
399
425
 
400
426
  export async function prepareAuthorityPolicies({hostPolicy,codingPolicy},{commandPins=[]}={}){
401
- const host=await preparePinnedCommandPolicy(hostPolicy,{commandPins,schema:'deadbyte.host-policy.v1',label:'host'});
427
+ const host=await preparePinnedCommandPolicy(hostPolicy,{commandPins,schema:['deadbyte.host-policy.v1','deadbyte.host-policy.v2'],label:'host'});
402
428
  const coding=await preparePinnedCommandPolicy(codingPolicy,{commandPins,schema:'deadbyte.coding-policy.v1',label:'coding'});
403
429
  const matched=new Set([...host.matchedCommandIds,...coding.matchedCommandIds]);
404
430
  const unknown=commandPins.find(pin=>!matched.has(String(pin?.commandId??'')));
@@ -862,7 +888,7 @@ function publicPointer(pointer){
862
888
  release_root:pointer.release_root,generation_root:pointer.generation_root,generation_manifest_sha256:pointer.generation_manifest_sha256,
863
889
  launcher_sha256:pointer.launcher_sha256,config_path:pointer.config_path};
864
890
  }
865
- export async function performSlotUpdate({stage,developmentRoot,expectedVersion,expectedManifest,sourceRoot=null,stateRoot=DEFAULT_STATE_ROOT,restoreStandingGrants=false,recoverOfflinePredecessor=false,cloudTransport=null,bridgeBootstrap=null,processPolicyBootstrap=null,desktopPolicyBootstrap=null,semanticLspBootstrap=null,enableMachineFs=false,enableObservation=false,enableDesktop=false,enableAutonomousSupervisor=false,hostCommandPins=[]}){
891
+ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,expectedManifest,sourceRoot=null,stateRoot=DEFAULT_STATE_ROOT,restoreStandingGrants=false,recoverOfflinePredecessor=false,cloudTransport=null,bridgeBootstrap=null,processPolicyBootstrap=null,hostPolicyBootstrap=null,desktopPolicyBootstrap=null,semanticLspBootstrap=null,enableMachineFs=false,enableObservation=false,enableDesktop=false,enableAutonomousSupervisor=false,hostCommandPins=[]}){
866
892
  if(process.platform!=='win32') throw new Error('R27 slot update is Windows-only');
867
893
  const state=path.resolve(stateRoot),stageRoot=path.resolve(stage),development=assertDevelopmentRootSafe(developmentRoot,state);
868
894
  const key=await loadUpdateKey(state); const updateId=`r27-${expectedManifest.slice(0,16)}-${Date.now().toString(36)}-${randomBytes(4).toString('hex')}`;
@@ -882,6 +908,8 @@ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,e
882
908
  const developmentVerified=await verifyPackageRoot(development,expectedVersion,expectedManifest,'development');
883
909
  const slot=await materializeReleaseSlot({source:stageRoot,stateRoot:state,version:expectedVersion,manifestSha256:expectedManifest});
884
910
  control=await loadControlInputs({stateRoot:state,sourceRoot,pointerInfo});
911
+ const hostBootstrap=await prepareHostPolicyBootstrap(hostPolicyBootstrap);
912
+ if(hostBootstrap) control.hostPolicy=hostBootstrap.policy;
885
913
  const processBootstrap=await prepareProcessPolicyBootstrap(processPolicyBootstrap);
886
914
  if(processBootstrap){
887
915
  control.processPolicy=processBootstrap.policy;
@@ -1113,7 +1141,7 @@ export async function runSlotUpdateCli(argv=process.argv.slice(2),{stateRoot=DEF
1113
1141
  const args=parseUpdateArgs(argv);
1114
1142
  const result=await performSlotUpdate({stage:args.stage,developmentRoot:args.developmentRoot,expectedVersion:args.expectedVersion,
1115
1143
  expectedManifest:args.expectedManifest,sourceRoot:args.sourceRoot,stateRoot,restoreStandingGrants:args.restoreStandingGrants,recoverOfflinePredecessor:args.recoverOfflinePredecessor,
1116
- cloudTransport:args.cloudTransport,bridgeBootstrap:args.bridgeBootstrap,processPolicyBootstrap:args.processPolicyBootstrap,desktopPolicyBootstrap:args.desktopPolicyBootstrap,semanticLspBootstrap:args.semanticLspBootstrap,enableMachineFs:args.enableMachineFs,
1144
+ cloudTransport:args.cloudTransport,bridgeBootstrap:args.bridgeBootstrap,processPolicyBootstrap:args.processPolicyBootstrap,hostPolicyBootstrap:args.hostPolicyBootstrap,desktopPolicyBootstrap:args.desktopPolicyBootstrap,semanticLspBootstrap:args.semanticLspBootstrap,enableMachineFs:args.enableMachineFs,
1117
1145
  enableObservation:args.enableObservation,enableDesktop:args.enableDesktop,enableAutonomousSupervisor:args.enableAutonomousSupervisor,hostCommandPins:args.hostCommandPins});
1118
1146
  console.log(`R27_SLOT_UPDATE_PASS version=${args.expectedVersion}`);
1119
1147
  console.log(`UPDATE_ID ${result.update_id}`); console.log(`RECEIPT_SHA256 ${result.receipt_sha256}`);