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.
- package/MANIFEST.SHA256 +76 -41
- package/PROVENANCE.json +10 -0
- package/R45-PARENT.json +18 -0
- package/README.txt +14 -14
- package/RELEASE.json +37 -0
- package/SBOM.cdx.json +46 -0
- package/TEST-MATRIX.json +31 -0
- package/bin/deadbyte-hardware-observer-r45.exe +0 -0
- package/bin/deadbyte-hardware-observer-r45.obj +0 -0
- package/controller/README.TXT +2 -2
- package/controller/deadbyte-controller.ps1 +2 -2
- package/docs/ARCHITECTURE.md +1 -1
- package/native/deadbyte-hardware-observer.cpp +308 -0
- package/package.json +12 -7
- package/scripts/autonomous-tests.ps1 +2 -2
- package/scripts/build-hardware-r45.ps1 +71 -0
- package/scripts/deferred-slot-operation-r45.mjs +126 -0
- package/scripts/derive-r45-parent.mjs +24 -0
- package/scripts/derive-r45-release.mjs +72 -0
- package/scripts/derive-r45-tests.mjs +20 -0
- package/scripts/final-closure-r45.mjs +121 -0
- package/scripts/final-closure-verify-r45.mjs +220 -0
- package/scripts/finalize-r45.mjs +219 -0
- package/scripts/full-regression-r45.mjs +20 -0
- package/scripts/full-regression-summary-r45.mjs +14 -0
- package/scripts/gate-windows-r45.ps1 +44 -0
- package/scripts/init-host-policy.ps1 +39 -1
- package/scripts/inspect-active-release-r45.mjs +20 -0
- package/scripts/release-manifest.mjs +3 -1
- package/scripts/release-parity-r45.mjs +253 -0
- package/scripts/release-parity-tests-r45.ps1 +8 -0
- package/scripts/verify-r45-parent.mjs +142 -0
- package/scripts/windows-gate-evidence-r45.mjs +60 -0
- package/src/autonomous-runtime.mjs +12 -7
- package/src/capability-ledger.mjs +1 -0
- package/src/compact-gateway-mcp-tools.mjs +4 -2
- package/src/deadbyte-cli.mjs +17 -9
- package/src/doctor-r45.mjs +65 -0
- package/src/host-plane.mjs +133 -85
- package/src/mcp-server.mjs +64 -23
- package/src/native-diff-v2.mjs +9 -4
- package/src/native-diff.mjs +29 -7
- package/src/observation-mcp-tools.mjs +5 -1
- package/src/observation-output-contracts.mjs +8 -0
- package/src/observation-runtime.mjs +33 -2
- package/src/observation-smoke-client.mjs +6 -2
- package/src/output-contracts.mjs +27 -11
- package/src/r45-compact-disarmed-probe.mjs +74 -0
- package/src/r45-full-surface-probe.mjs +34 -0
- package/src/release-generation.mjs +1 -0
- package/src/remote-mcp-r45-parity-client.mjs +206 -0
- package/src/runtime-update.mjs +36 -8
- package/src/version.mjs +1 -1
- package/test/agent-runtime.test.mjs +1 -0
- package/test/autonomous-loop.test.mjs +24 -0
- package/test/autonomous-runtime.test.mjs +1 -0
- package/test/cli-entrypoint.test.mjs +4 -4
- package/test/coding-plane.test.mjs +1 -0
- package/test/compact-max-catalog-r45.test.mjs +99 -0
- package/test/contained.test.mjs +23 -11
- package/test/controller.test.mjs +5 -1
- package/test/core.test.mjs +1 -1
- package/test/full-max-catalog-r45.test.mjs +91 -0
- package/test/hardware-observation-r45.test.mjs +44 -0
- package/test/host-inline-r45.test.mjs +67 -0
- package/test/machine-mcp-integration.test.mjs +1 -0
- package/test/observation-mcp-tools.test.mjs +4 -3
- package/test/output-schema.test.mjs +1 -0
- package/test/package-boundary.test.mjs +1 -1
- package/test/production-docs.test.mjs +3 -3
- package/test/r33-closeout-regression.test.mjs +20 -19
- package/test/r33-finalization.test.mjs +3 -3
- package/test/r45-autonomous-quick-test.test.mjs +10 -0
- package/test/r45-finalization.test.mjs +84 -0
- package/test/release-generation.test.mjs +1 -0
- package/test/release-version.test.mjs +52 -41
- 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
|
-
|
|
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}`);
|
package/src/output-contracts.mjs
CHANGED
|
@@ -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 =
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
+
}
|
package/src/runtime-update.mjs
CHANGED
|
@@ -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
|
-
|
|
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}`);
|