deadbyte-mcp 0.15.33 → 0.15.35
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 +71 -22
- package/PROVENANCE.json +57 -28
- package/README.txt +22 -14
- package/RELEASE.json +41 -29
- package/SBOM.cdx.json +25 -3
- package/TEST-MATRIX.json +135 -43
- package/bridge/package.json +11 -0
- package/bridge/src/bridge.mjs +412 -0
- package/bridge/src/node-http-adapter.mjs +56 -0
- package/bridge/src/public-tool-definitions.mjs +14 -0
- package/bridge/src/r40-governor.mjs +160 -0
- package/controller/deadbyte-controller.ps1 +84 -8
- package/controller/deadbyte-process-policy.json +4 -4
- package/package.json +6 -3
- package/scripts/bridge-benchmark-r58.mjs +67 -0
- package/scripts/deploy-r48.mjs +2 -3
- package/scripts/gate-windows-r57.ps1 +27 -0
- package/scripts/gate-windows-r58.ps1 +33 -0
- package/scripts/owner-probe-r58.mjs +31 -0
- package/scripts/performance-r58.mjs +57 -0
- package/scripts/public-baseline-r58.mjs +72 -0
- package/scripts/public-surface-r58.mjs +84 -0
- package/scripts/r58-cross-plane.ps1 +6 -0
- package/scripts/r58-focused.ps1 +6 -0
- package/scripts/windows-gate-evidence-r57.mjs +81 -0
- package/scripts/windows-gate-evidence-r58.mjs +93 -0
- package/src/deadbyte-cli.mjs +40 -12
- package/src/doctor-foreground-r58.mjs +44 -0
- package/src/doctor-installation-r58.mjs +63 -0
- package/src/doctor-r45.mjs +21 -4
- package/src/doctor-runtime-r58.mjs +82 -0
- package/src/doctor-transactions-r58.mjs +40 -0
- package/src/foreground-process-identity.mjs +27 -0
- package/src/foreground-session-lock.mjs +20 -0
- package/src/foreground-session-lock.ps1 +15 -0
- package/src/release-generation.mjs +1 -0
- package/src/release-slot.mjs +2 -1
- package/src/remote-session-guardian.mjs +8 -1
- package/src/runtime-update.mjs +117 -3
- package/src/version.mjs +1 -1
- package/test/controller-r211.test.mjs +4 -1
- package/test/controller.test.mjs +2 -1
- package/test/helpers/r58-doctor-fixture.mjs +26 -0
- package/test/r49-windows-gate.test.mjs +14 -10
- package/test/r56-release-contract.test.mjs +5 -3
- package/test/r57-core-startup-race.test.mjs +85 -0
- package/test/r57-release-contract.test.mjs +67 -0
- package/test/r58-bridge-dependency-binding.test.mjs +62 -0
- package/test/r58-bundled-bridge.test.mjs +22 -0
- package/test/r58-cleanup-retention.test.mjs +45 -0
- package/test/r58-completed-update-receipt.test.mjs +76 -0
- package/test/r58-concurrent-recovery.test.mjs +54 -0
- package/test/r58-controller-core-observation.test.mjs +36 -0
- package/test/r58-controller-json.test.mjs +48 -0
- package/test/r58-controller-live-local.test.mjs +85 -0
- package/test/r58-controller-readonly.test.mjs +23 -0
- package/test/r58-controller-state-coherence.test.mjs +45 -0
- package/test/r58-doctor-cli.test.mjs +20 -0
- package/test/r58-doctor-foreground.test.mjs +50 -0
- package/test/r58-doctor-installation.test.mjs +55 -0
- package/test/r58-doctor-pointer.test.mjs +41 -0
- package/test/r58-doctor-runtime.test.mjs +45 -0
- package/test/r58-doctor-transactions.test.mjs +21 -0
- package/test/r58-guardian-channel.test.mjs +26 -0
- package/test/r58-launcher-shell.test.mjs +28 -0
- package/test/r58-owner-identity.test.mjs +20 -0
- package/test/r58-release-contract.test.mjs +49 -0
- package/test/r58-session-lock.test.mjs +26 -0
- package/test/r58-stale-owner.test.mjs +71 -0
- package/test/r58-windows-gate.test.mjs +31 -0
- package/test/release-generation.test.mjs +4 -1
- package/test/remote-session-cli.test.mjs +55 -2
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {once} from 'node:events';
|
|
2
|
+
|
|
3
|
+
function requestHeaders(headers){
|
|
4
|
+
const out=new Headers();
|
|
5
|
+
for(const [name,value] of Object.entries(headers??{})){
|
|
6
|
+
if(value===undefined) continue;
|
|
7
|
+
if(Array.isArray(value)){for(const item of value) out.append(name,String(item));}
|
|
8
|
+
else out.append(name,String(value));
|
|
9
|
+
}
|
|
10
|
+
return out;
|
|
11
|
+
}
|
|
12
|
+
async function requestBody(req,maxBodyBytes){
|
|
13
|
+
const method=String(req.method??'GET').toUpperCase();
|
|
14
|
+
if(method==='GET'||method==='HEAD') return undefined;
|
|
15
|
+
const chunks=[];let total=0;
|
|
16
|
+
for await(const chunk of req){
|
|
17
|
+
const bytes=Buffer.isBuffer(chunk)?chunk:Buffer.from(chunk);
|
|
18
|
+
total+=bytes.length;
|
|
19
|
+
if(total>maxBodyBytes) throw new Error(`bridge request body exceeds ${maxBodyBytes} bytes`);
|
|
20
|
+
chunks.push(bytes);
|
|
21
|
+
}
|
|
22
|
+
return Buffer.concat(chunks,total);
|
|
23
|
+
}
|
|
24
|
+
async function writeResponse(res,response){
|
|
25
|
+
const headers={};
|
|
26
|
+
for(const [name,value] of response.headers.entries()) headers[name]=value;
|
|
27
|
+
res.writeHead(response.status,headers);
|
|
28
|
+
if(!response.body){res.end();return;}
|
|
29
|
+
const reader=response.body.getReader();
|
|
30
|
+
try{
|
|
31
|
+
while(true){
|
|
32
|
+
const {done,value}=await reader.read();
|
|
33
|
+
if(done) break;
|
|
34
|
+
if(res.destroyed){await reader.cancel().catch(()=>{});return;}
|
|
35
|
+
if(!res.write(value)) await once(res,'drain');
|
|
36
|
+
}
|
|
37
|
+
}finally{reader.releaseLock();}
|
|
38
|
+
if(!res.destroyed) res.end();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function createDeadbyteNodeHandler(handler,{maxBodyBytes=1024*1024}={}){
|
|
42
|
+
if(!handler||typeof handler.fetch!=='function') throw new Error('bridge node adapter requires fetch-shaped MCP handler');
|
|
43
|
+
if(!Number.isInteger(maxBodyBytes)||maxBodyBytes<1024||maxBodyBytes>4*1024*1024) throw new Error('bridge node adapter maxBodyBytes invalid');
|
|
44
|
+
return async function nodeHandler(req,res){
|
|
45
|
+
const method=String(req.method??'GET').toUpperCase();
|
|
46
|
+
const url=new URL(String(req.url??'/'),'http://127.0.0.1');
|
|
47
|
+
const body=await requestBody(req,maxBodyBytes);
|
|
48
|
+
const init={method,headers:requestHeaders(req.headers)};
|
|
49
|
+
if(body!==undefined) init.body=body;
|
|
50
|
+
const request=new Request(url,init);
|
|
51
|
+
const options=req.auth?{authInfo:req.auth}:undefined;
|
|
52
|
+
const response=await handler.fetch(request,options);
|
|
53
|
+
if(!(response instanceof Response)) throw new Error('bridge MCP handler returned non-Response');
|
|
54
|
+
await writeResponse(res,response);
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {fromJsonSchema} from '@modelcontextprotocol/server';
|
|
2
|
+
|
|
3
|
+
// Catalog is fixed by the existing downstream startup attestation. Only schema
|
|
4
|
+
// definitions/validators are shared; servers, callbacks and requestState aren't.
|
|
5
|
+
export function preparePublicToolDefinitions(tools){
|
|
6
|
+
return Object.freeze(tools.map(tool=>{
|
|
7
|
+
const input=structuredClone(tool.inputSchema??{type:'object',properties:{},additionalProperties:false});
|
|
8
|
+
if(tool.name==='autonomous_goal_run'){
|
|
9
|
+
if(input.properties&&typeof input.properties==='object')delete input.properties.planner_response;
|
|
10
|
+
if(Array.isArray(input.required))input.required=input.required.filter(name=>name!=='planner_response');
|
|
11
|
+
}
|
|
12
|
+
return Object.freeze({...tool,inputSchema:fromJsonSchema(input),outputSchema:tool.outputSchema?fromJsonSchema(tool.outputSchema):undefined});
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
const DESKTOP_RAW_KEYS=new Set([
|
|
4
|
+
'pixels','pixel_bytes','frame_bytes','raw_bgra','raw_rgba','raw_image','screenshot_bytes','bitmap_bytes'
|
|
5
|
+
]);
|
|
6
|
+
const DEFAULT_LIMITS=Object.freeze({
|
|
7
|
+
normal:Object.freeze({max_active:8,max_queue:32,queue_timeout_ms:30000}),
|
|
8
|
+
autonomous:Object.freeze({max_active:2,max_queue:8,queue_timeout_ms:60000}),
|
|
9
|
+
desktop_observe:Object.freeze({max_active:2,max_queue:4,queue_timeout_ms:10000}),
|
|
10
|
+
desktop_action:Object.freeze({max_active:1,max_queue:2,queue_timeout_ms:5000})
|
|
11
|
+
});
|
|
12
|
+
const DEADLINES=Object.freeze({
|
|
13
|
+
normal:Object.freeze({timeout:120000,maxTotalTimeout:180000}),
|
|
14
|
+
autonomous:Object.freeze({timeout:900000,maxTotalTimeout:1200000}),
|
|
15
|
+
desktop_observe:Object.freeze({timeout:30000,maxTotalTimeout:60000}),
|
|
16
|
+
desktop_action:Object.freeze({timeout:30000,maxTotalTimeout:45000})
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
function insist(condition,message){ if(!condition) throw new Error(message); }
|
|
20
|
+
function toolClass(name){
|
|
21
|
+
const value=String(name??'');
|
|
22
|
+
if(value==='autonomous_goal_run') return 'autonomous';
|
|
23
|
+
if(value==='desktop_action') return 'desktop_action';
|
|
24
|
+
if(value.startsWith('desktop_')) return 'desktop_observe';
|
|
25
|
+
return 'normal';
|
|
26
|
+
}
|
|
27
|
+
function estimateBase64Bytes(value){
|
|
28
|
+
if(typeof value!=='string') return 0;
|
|
29
|
+
const clean=value.replace(/\s/g,'');
|
|
30
|
+
if(clean.length===0) return 0;
|
|
31
|
+
const padding=clean.endsWith('==')?2:clean.endsWith('=')?1:0;
|
|
32
|
+
return Math.floor(clean.length*3/4)-padding;
|
|
33
|
+
}
|
|
34
|
+
function scanDesktopPayload(value,{maxPreviewBytes},path='result',seen=new Set()){
|
|
35
|
+
if(value===null||value===undefined) return;
|
|
36
|
+
if(typeof value==='string'||typeof value==='number'||typeof value==='boolean') return;
|
|
37
|
+
if(typeof value!=='object') return;
|
|
38
|
+
if(seen.has(value)) throw new Error('bridge desktop result contains cyclic value');
|
|
39
|
+
seen.add(value);
|
|
40
|
+
if(Array.isArray(value)){
|
|
41
|
+
for(let i=0;i<value.length;i++) scanDesktopPayload(value[i],{maxPreviewBytes},`${path}[${i}]`,seen);
|
|
42
|
+
seen.delete(value); return;
|
|
43
|
+
}
|
|
44
|
+
for(const [key,child] of Object.entries(value)){
|
|
45
|
+
const lower=key.toLowerCase();
|
|
46
|
+
if(DESKTOP_RAW_KEYS.has(lower)) throw new Error(`bridge desktop result contains forbidden raw frame field '${path}.${key}'`);
|
|
47
|
+
if((lower==='preview_png_base64'||lower==='preview_jpeg_base64')&&typeof child==='string'){
|
|
48
|
+
const bytes=estimateBase64Bytes(child);
|
|
49
|
+
if(bytes>maxPreviewBytes) throw new Error(`bridge desktop preview exceeds ${maxPreviewBytes} byte limit`);
|
|
50
|
+
}
|
|
51
|
+
if(lower==='data'&&value.type==='image'&&typeof child==='string'){
|
|
52
|
+
const bytes=estimateBase64Bytes(child);
|
|
53
|
+
if(bytes>maxPreviewBytes) throw new Error(`bridge MCP image content exceeds ${maxPreviewBytes} byte limit`);
|
|
54
|
+
}
|
|
55
|
+
scanDesktopPayload(child,{maxPreviewBytes},`${path}.${key}`,seen);
|
|
56
|
+
}
|
|
57
|
+
seen.delete(value);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function bridgeToolClass(name){ return toolClass(name); }
|
|
61
|
+
export function bridgeDeadlineForTool(name){ return DEADLINES[toolClass(name)]; }
|
|
62
|
+
export function bridgeCatalogSha256(toolNames){
|
|
63
|
+
insist(Array.isArray(toolNames)&&toolNames.every(x=>typeof x==='string'),'bridge catalog names invalid');
|
|
64
|
+
const sorted=[...new Set(toolNames)].sort();
|
|
65
|
+
insist(sorted.length===toolNames.length,'bridge catalog contains duplicate tool name');
|
|
66
|
+
return createHash('sha256').update(JSON.stringify(sorted),'utf8').digest('hex');
|
|
67
|
+
}
|
|
68
|
+
export function verifyCompactCatalogAttestation(actualNames,toolResult){
|
|
69
|
+
insist(Array.isArray(actualNames)&&actualNames.every(x=>typeof x==='string'&&x.length>0),'actual bridge catalog invalid');
|
|
70
|
+
const actual=[...actualNames].sort();
|
|
71
|
+
insist(new Set(actual).size===actual.length,'actual bridge catalog contains duplicate tool name');
|
|
72
|
+
const envelope=toolResult?.structuredContent;
|
|
73
|
+
insist(envelope?.status==='ok'&&envelope?.op==='manifest','capability manifest tool result invalid');
|
|
74
|
+
const manifest=envelope.result;
|
|
75
|
+
insist(manifest?.schema==='deadbyte.capability-manifest.v2'||manifest?.schema==='deadbyte.capability-manifest.v3','capability manifest schema mismatch');
|
|
76
|
+
insist(manifest.profile==='compact','capability manifest profile mismatch');
|
|
77
|
+
insist(typeof manifest.ledger_sha256==='string'&&/^[0-9a-f]{64}$/.test(manifest.ledger_sha256),'capability manifest ledger hash invalid');
|
|
78
|
+
insist(Array.isArray(manifest.tool_catalog)&&manifest.tool_catalog.every(x=>typeof x==='string'&&x.length>0),'capability manifest tool_catalog invalid');
|
|
79
|
+
const declared=[...manifest.tool_catalog];
|
|
80
|
+
insist(declared.length===manifest.tool_count,'capability manifest tool_count mismatch');
|
|
81
|
+
insist(new Set(declared).size===declared.length,'capability manifest catalog contains duplicate tool name');
|
|
82
|
+
insist(JSON.stringify(declared)===JSON.stringify([...declared].sort()),'capability manifest catalog must be sorted');
|
|
83
|
+
const declaredSha=bridgeCatalogSha256(declared);
|
|
84
|
+
insist(declaredSha===manifest.catalog_sha256,'capability manifest catalog hash mismatch');
|
|
85
|
+
insist(JSON.stringify(actual)===JSON.stringify(declared),'bridge listTools/catalog attestation mismatch');
|
|
86
|
+
return Object.freeze({
|
|
87
|
+
ok:true,profile:'compact',tool_count:declared.length,catalog_sha256:declaredSha,ledger_sha256:manifest.ledger_sha256
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function enforceDesktopBridgeResult(toolName,result,{maxPreviewBytes=1024*1024}={}){
|
|
92
|
+
if(!String(toolName??'').startsWith('desktop_')) return result;
|
|
93
|
+
insist(Number.isInteger(maxPreviewBytes)&&maxPreviewBytes>=16*1024&&maxPreviewBytes<=4*1024*1024,'maxPreviewBytes invalid');
|
|
94
|
+
scanDesktopPayload(result,{maxPreviewBytes});
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function createBridgeGovernor({limits=DEFAULT_LIMITS}={}){
|
|
99
|
+
const classes=['normal','autonomous','desktop_observe','desktop_action'];
|
|
100
|
+
for(const name of classes){
|
|
101
|
+
const cfg=limits[name];
|
|
102
|
+
insist(cfg&&Number.isInteger(cfg.max_active)&&cfg.max_active>=1&&cfg.max_active<=64,`bridge limit ${name}.max_active invalid`);
|
|
103
|
+
insist(Number.isInteger(cfg.max_queue)&&cfg.max_queue>=0&&cfg.max_queue<=256,`bridge limit ${name}.max_queue invalid`);
|
|
104
|
+
insist(Number.isInteger(cfg.queue_timeout_ms)&&cfg.queue_timeout_ms>=100&&cfg.queue_timeout_ms<=120000,`bridge limit ${name}.queue_timeout_ms invalid`);
|
|
105
|
+
}
|
|
106
|
+
const state=new Map(classes.map(name=>[name,{active:0,queue:[]}]));
|
|
107
|
+
let sequence=0;
|
|
108
|
+
|
|
109
|
+
function dispatch(name){
|
|
110
|
+
const cfg=limits[name],s=state.get(name);
|
|
111
|
+
while(s.active<cfg.max_active&&s.queue.length){
|
|
112
|
+
const waiter=s.queue.shift();
|
|
113
|
+
if(waiter.done) continue;
|
|
114
|
+
waiter.done=true; clearTimeout(waiter.timer); s.active++;
|
|
115
|
+
waiter.resolve(makeTicket(name,waiter.tool,waiter.id,waiter.queuedAt));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function makeTicket(name,tool,id,queuedAt){
|
|
119
|
+
let released=false;
|
|
120
|
+
return Object.freeze({
|
|
121
|
+
id,class:name,tool,queued_ms:Math.max(0,Date.now()-queuedAt),
|
|
122
|
+
release(){
|
|
123
|
+
if(released) return false;
|
|
124
|
+
released=true;
|
|
125
|
+
const s=state.get(name);
|
|
126
|
+
s.active=Math.max(0,s.active-1);
|
|
127
|
+
dispatch(name);
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
async function acquire(tool){
|
|
133
|
+
const name=toolClass(tool),cfg=limits[name],s=state.get(name),id=++sequence,queuedAt=Date.now();
|
|
134
|
+
if(s.active<cfg.max_active){
|
|
135
|
+
s.active++;
|
|
136
|
+
return makeTicket(name,String(tool),id,queuedAt);
|
|
137
|
+
}
|
|
138
|
+
if(s.queue.length>=cfg.max_queue) throw new Error(`bridge backpressure queue full for ${name}`);
|
|
139
|
+
return await new Promise((resolve,reject)=>{
|
|
140
|
+
const waiter={id,tool:String(tool),queuedAt,resolve,reject,done:false,timer:null};
|
|
141
|
+
waiter.timer=setTimeout(()=>{
|
|
142
|
+
if(waiter.done)return;
|
|
143
|
+
waiter.done=true;
|
|
144
|
+
const index=s.queue.indexOf(waiter); if(index>=0)s.queue.splice(index,1);
|
|
145
|
+
reject(new Error(`bridge backpressure queue timeout for ${name}`));
|
|
146
|
+
},cfg.queue_timeout_ms);
|
|
147
|
+
s.queue.push(waiter);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
function snapshot(){
|
|
151
|
+
return Object.freeze(Object.fromEntries(classes.map(name=>{
|
|
152
|
+
const s=state.get(name),cfg=limits[name];
|
|
153
|
+
return [name,Object.freeze({active:s.active,queued:s.queue.filter(x=>!x.done).length,max_active:cfg.max_active,max_queue:cfg.max_queue})];
|
|
154
|
+
})));
|
|
155
|
+
}
|
|
156
|
+
return Object.freeze({acquire,snapshot});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export const BRIDGE_R40_DEFAULT_LIMITS=DEFAULT_LIMITS;
|
|
160
|
+
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
param(
|
|
4
4
|
[Parameter(Position=0)]
|
|
5
5
|
[ValidateSet('menu','start','resume','park','stop','restart','status','url','test','fulltest','toggle','clean','machinetest','armprocess','disarmprocess','processstatus','armhost','disarmhost','hoststatus','hosttest','armcoding','disarmcoding','codingstatus','codingtest','armautonomous','disarmautonomous','autonomousstatus','autonomoustest','supervisorstart','supervisorstop','supervisorstatus','supervisorrecover','armdesktop','disarmdesktop','desktopstatus')]
|
|
6
|
-
[string]$Command = 'menu'
|
|
6
|
+
[string]$Command = 'menu',
|
|
7
|
+
[switch]$Json
|
|
7
8
|
)
|
|
8
9
|
|
|
9
10
|
Set-StrictMode -Version 3.0
|
|
@@ -16,6 +17,7 @@ $LogDir = Join-Path $RuntimeDir 'logs'
|
|
|
16
17
|
$ConfigPath = if ($env:DEADBYTE_CONTROLLER_CONFIG) { $env:DEADBYTE_CONTROLLER_CONFIG } else { Join-Path $Root 'deadbyte-controller.config.psd1' }
|
|
17
18
|
|
|
18
19
|
function Fail([string]$Message) { throw "DEADBYTE controller: $Message" }
|
|
20
|
+
if ($Json -and $Command -ne 'status') { Fail 'JSON observation is available only for status' }
|
|
19
21
|
|
|
20
22
|
function Get-ControllerMutationMutexName {
|
|
21
23
|
$normalized = ([System.IO.Path]::GetFullPath($RuntimeDir)).TrimEnd('\\').ToLowerInvariant()
|
|
@@ -721,12 +723,14 @@ function Test-LocalReadyOwned($Cfg, $Bridge) {
|
|
|
721
723
|
function Get-ObservedState($Cfg) {
|
|
722
724
|
$bridge = Test-OwnedInstance 'Bridge'
|
|
723
725
|
$cloud = Test-OwnedInstance 'Cloud'
|
|
724
|
-
$
|
|
726
|
+
$core = Get-DownstreamCoreObservation $Cfg $bridge
|
|
727
|
+
$ready = if ($bridge.Alive -and $core.alive) { try { Test-LocalReadyOwned $Cfg $bridge } catch { $false } } else { $false }
|
|
725
728
|
$state = if (-not $bridge.Alive -and -not $cloud.Alive) { 'OFFLINE' }
|
|
726
729
|
elseif (-not $bridge.Alive -and $cloud.Alive) { 'PARKED' }
|
|
727
730
|
elseif ($bridge.Alive -and $cloud.Alive -and $ready) { 'ONLINE' }
|
|
728
|
-
|
|
729
|
-
|
|
731
|
+
elseif ($bridge.Alive -and $core.reason -eq 'startup-zero-child') { 'STARTING' }
|
|
732
|
+
else { 'FAILED' }
|
|
733
|
+
[pscustomobject]@{ State=$state; Bridge=$bridge; Core=$core; Cloud=$cloud; LocalReady=$ready }
|
|
730
734
|
}
|
|
731
735
|
|
|
732
736
|
function Wait-LocalReady($Cfg) {
|
|
@@ -734,7 +738,10 @@ function Wait-LocalReady($Cfg) {
|
|
|
734
738
|
do {
|
|
735
739
|
$bridge = Test-OwnedInstance 'Bridge'
|
|
736
740
|
if (-not $bridge.Alive) { return $false }
|
|
737
|
-
|
|
741
|
+
$core = Get-DownstreamCoreObservation $Cfg $bridge
|
|
742
|
+
if (-not $core.alive -and $core.reason -ne 'startup-zero-child') { return $false }
|
|
743
|
+
if ($core.alive -and (Test-LocalReadyOwned $Cfg $bridge)) { return $true }
|
|
744
|
+
if ([DateTime]::UtcNow -ge $deadline) { return $false }
|
|
738
745
|
Start-Sleep -Milliseconds 250
|
|
739
746
|
} while ([DateTime]::UtcNow -lt $deadline)
|
|
740
747
|
return $false
|
|
@@ -836,7 +843,7 @@ function Invoke-CoreGate($Cfg, [string]$ScriptName, [string[]]$ExtraArguments =
|
|
|
836
843
|
function Start-Deadbyte($Cfg) {
|
|
837
844
|
$state = Get-ObservedState $Cfg
|
|
838
845
|
if ($state.State -eq 'ONLINE') { return $state }
|
|
839
|
-
if ($state.State -
|
|
846
|
+
if ($state.State -in @('FAILED','STARTING')) { Fail "runtime is $($state.State); inspect status and reconcile verified owned components before restart" }
|
|
840
847
|
$cloudWasAlive = [bool]$state.Cloud.Alive
|
|
841
848
|
Assert-LocalReadyPortAvailable $Cfg
|
|
842
849
|
try {
|
|
@@ -1265,6 +1272,73 @@ function Recover-AutonomousSupervisor($Cfg) {
|
|
|
1265
1272
|
return [pscustomobject]@{ Runtime=$runtime; Supervisor=$supervisor; Process=$process; Coding=$coding; Autonomous=$autonomous; Desktop=$desktop; Host=$hostStatus }
|
|
1266
1273
|
}
|
|
1267
1274
|
|
|
1275
|
+
function Get-BoundedObservationReason($Value) {
|
|
1276
|
+
$reason = [string]$Value
|
|
1277
|
+
if ($reason -cmatch '^[a-z][a-z0-9-]{0,63}$') { return $reason }
|
|
1278
|
+
return 'unverified'
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
function Convert-OwnedObservation($Owned) {
|
|
1282
|
+
$pidValue = $null; $birth = $null
|
|
1283
|
+
if ($Owned.Alive -and $Owned.Ledger) {
|
|
1284
|
+
$pidValue = [int]$Owned.Ledger.pid
|
|
1285
|
+
$birth = [string]$Owned.Ledger.creation_time_utc
|
|
1286
|
+
}
|
|
1287
|
+
[ordered]@{ alive=[bool]$Owned.Alive; reason=(Get-BoundedObservationReason $Owned.Reason); pid=$pidValue; creation_time_utc=$birth }
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
function Convert-AuthorityObservation($Authority) {
|
|
1291
|
+
[ordered]@{ configured=[bool]$Authority.Configured; armed=[bool]$Authority.Armed; reason=(Get-BoundedObservationReason $Authority.Reason) }
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
function Get-DownstreamCoreObservation($Cfg, $Bridge) {
|
|
1295
|
+
$out = [ordered]@{ alive=$false; reason='bridge-unavailable'; pid=$null; parent_pid=$null; creation_time_utc=$null; identity_basis=$null }
|
|
1296
|
+
if (-not $Bridge.Alive) { return $out }
|
|
1297
|
+
$bridgePid = [int]$Bridge.Ledger.pid
|
|
1298
|
+
if (-not [string]::Equals([IO.Path]::GetFullPath([string]$Bridge.Ledger.script_path), [IO.Path]::GetFullPath([string]$Cfg.Bridge.ScriptPath), [StringComparison]::OrdinalIgnoreCase) -or [string]$Bridge.Ledger.script_sha256 -ne [string]$Cfg.Bridge.ScriptSha256) {
|
|
1299
|
+
$out.reason='bridge-config-mismatch'; return $out
|
|
1300
|
+
}
|
|
1301
|
+
try {
|
|
1302
|
+
$children = @(Get-CimInstance Win32_Process -Filter ("ParentProcessId={0} AND Name='node.exe'" -f $bridgePid) -ErrorAction Stop)
|
|
1303
|
+
if ($children.Count -eq 0) { $out.reason='startup-zero-child'; return $out }
|
|
1304
|
+
if ($children.Count -ne 1) { $out.reason='ambiguous-core'; return $out }
|
|
1305
|
+
$child = $children[0]
|
|
1306
|
+
if ([int]$child.ParentProcessId -ne $bridgePid -or [int]$child.ProcessId -lt 1 -or $null -eq $child.CreationDate) { $out.reason='core-identity-incomplete'; return $out }
|
|
1307
|
+
if (-not [string]::IsNullOrWhiteSpace([string]$child.ExecutablePath) -and -not [string]::Equals([string]$child.ExecutablePath,[string]$Cfg.Bridge.FilePath,[StringComparison]::OrdinalIgnoreCase)) { $out.reason='core-image-mismatch'; return $out }
|
|
1308
|
+
$command = [string]$child.CommandLine
|
|
1309
|
+
$needle = (Join-Path ([string]$Cfg.CoreRoot) 'src\mcp-server.mjs').Replace('/','\').ToLowerInvariant()
|
|
1310
|
+
if ([string]::IsNullOrWhiteSpace($command)) { $basis='verified-bridge-single-opaque-node-child' }
|
|
1311
|
+
elseif ($command.Replace('/','\').ToLowerInvariant().Contains($needle)) { $basis='command-line' }
|
|
1312
|
+
else { $out.reason='wrong-core-generation'; return $out }
|
|
1313
|
+
$out.alive=$true; $out.reason='verified'; $out.pid=[int]$child.ProcessId; $out.parent_pid=$bridgePid
|
|
1314
|
+
$out.creation_time_utc=$child.CreationDate.ToUniversalTime().ToString('o'); $out.identity_basis=$basis
|
|
1315
|
+
return $out
|
|
1316
|
+
} catch { $out.reason='core-observation-failed'; return $out }
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
function Get-StructuredObservation($Cfg) {
|
|
1320
|
+
$s = Get-ObservedState $Cfg
|
|
1321
|
+
$supervisor = Get-AutonomousSupervisorStatus $Cfg
|
|
1322
|
+
[ordered]@{
|
|
1323
|
+
schema='deadbyte.controller-observation.v1'
|
|
1324
|
+
observed_at_utc=[DateTime]::UtcNow.ToString('o')
|
|
1325
|
+
state=[string]$s.State
|
|
1326
|
+
core_root=[string]$Cfg.CoreRoot
|
|
1327
|
+
local_ready=[bool]$s.LocalReady
|
|
1328
|
+
bridge=(Convert-OwnedObservation $s.Bridge)
|
|
1329
|
+
core=$s.Core
|
|
1330
|
+
cloud=(Convert-OwnedObservation $s.Cloud)
|
|
1331
|
+
supervisor=[ordered]@{ configured=[bool]$supervisor.Configured; running=[bool]$supervisor.Running; reason=(Get-BoundedObservationReason $supervisor.Reason) }
|
|
1332
|
+
authority=[ordered]@{
|
|
1333
|
+
process=(Convert-AuthorityObservation (Get-ProcessGrantStatus $Cfg))
|
|
1334
|
+
coding=(Convert-AuthorityObservation (Get-CodingLeaseStatus $Cfg))
|
|
1335
|
+
autonomous=(Convert-AuthorityObservation (Get-AutonomousLeaseStatus $Cfg))
|
|
1336
|
+
desktop=(Convert-AuthorityObservation (Get-DesktopGrantStatus $Cfg))
|
|
1337
|
+
host=(Convert-AuthorityObservation (Get-HostArmStatus $Cfg))
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1268
1342
|
function Show-State($Cfg) {
|
|
1269
1343
|
$s = Get-ObservedState $Cfg
|
|
1270
1344
|
$bp = if ($s.Bridge.Alive) { $s.Bridge.Ledger.pid } else { '-' }
|
|
@@ -1272,6 +1346,7 @@ function Show-State($Cfg) {
|
|
|
1272
1346
|
$url = Get-CurrentUrl $Cfg
|
|
1273
1347
|
Write-Host ("DEADBYTE MCP = {0}" -f $s.State)
|
|
1274
1348
|
Write-Host ("Bridge PID : {0} [{1}]" -f $bp, $s.Bridge.Reason)
|
|
1349
|
+
Write-Host ("Core PID : {0} [{1}]" -f $(if ($s.Core.alive) { $s.Core.pid } else { '-' }), $s.Core.reason)
|
|
1275
1350
|
Write-Host ("Cloud PID : {0} [{1}]" -f $cp, $s.Cloud.Reason)
|
|
1276
1351
|
Write-Host ("Local ready : {0}" -f $s.LocalReady)
|
|
1277
1352
|
Write-Host ("URL : {0}" -f $(if ($url) { $url } else { '<unknown>' }))
|
|
@@ -1370,14 +1445,15 @@ function Clean-Stale {
|
|
|
1370
1445
|
function Run-CommandCore([string]$Name) {
|
|
1371
1446
|
$cfg = Get-Config
|
|
1372
1447
|
Assert-ControllerRootBound $cfg
|
|
1373
|
-
|
|
1448
|
+
$readOnly = $Name -in @('status','url','hoststatus','processstatus','codingstatus','autonomousstatus','desktopstatus','supervisorstatus')
|
|
1449
|
+
if (-not $readOnly) { Ensure-Directory $RuntimeDir; Ensure-Directory $LogDir }
|
|
1374
1450
|
switch ($Name) {
|
|
1375
1451
|
'start' { Start-Deadbyte $cfg | Out-Null; if ([bool]$cfg.AutonomousSupervisorEnabled) { Start-AutonomousSupervisor $cfg | Out-Null }; Show-State $cfg | Out-Null }
|
|
1376
1452
|
'resume' { Start-Deadbyte $cfg | Out-Null; if ([bool]$cfg.AutonomousSupervisorEnabled) { Start-AutonomousSupervisor $cfg | Out-Null }; Show-State $cfg | Out-Null }
|
|
1377
1453
|
'park' { Park-Deadbyte $cfg | Out-Null; Show-State $cfg | Out-Null }
|
|
1378
1454
|
'stop' { if ([bool]$cfg.AutonomousSupervisorEnabled) { Stop-AutonomousSupervisor $cfg | Out-Null }; Stop-Deadbyte $cfg | Out-Null; Show-State $cfg | Out-Null }
|
|
1379
1455
|
'restart' { if ([bool]$cfg.AutonomousSupervisorEnabled) { Stop-AutonomousSupervisor $cfg | Out-Null }; Stop-Deadbyte $cfg | Out-Null; Start-Deadbyte $cfg | Out-Null; if ([bool]$cfg.AutonomousSupervisorEnabled) { Start-AutonomousSupervisor $cfg | Out-Null }; Show-State $cfg | Out-Null }
|
|
1380
|
-
'status' { Show-State $cfg | Out-Null }
|
|
1456
|
+
'status' { if ($Json) { Get-StructuredObservation $cfg | ConvertTo-Json -Depth 6 -Compress | Write-Output } else { Show-State $cfg | Out-Null } }
|
|
1381
1457
|
'url' { $u=Get-CurrentUrl $cfg; if (-not $u) { Fail 'URL unavailable' }; Write-Output $u }
|
|
1382
1458
|
'test' { Invoke-Smoke $cfg $false }
|
|
1383
1459
|
'fulltest'{ Invoke-Smoke $cfg $true }
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "deadbyte.process-policy.v1",
|
|
3
3
|
"enabled": true,
|
|
4
|
-
"grant_file": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.
|
|
5
|
-
"state_dir": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.
|
|
4
|
+
"grant_file": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.35-r58\\controller\\runtime\\process-grant.json",
|
|
5
|
+
"state_dir": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.35-r58\\controller\\runtime\\process-state",
|
|
6
6
|
"native_host": {
|
|
7
|
-
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.
|
|
7
|
+
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.35-r58\\bin\\deadbyte-process-host.exe",
|
|
8
8
|
"sha256": "daf9cfb371bee88cafbf5597eaa507fec504dbe0b0ddd40d0c3c2e02817f49e4"
|
|
9
9
|
},
|
|
10
10
|
"roots": {
|
|
11
11
|
"core": {
|
|
12
|
-
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.
|
|
12
|
+
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.35-r58",
|
|
13
13
|
"deny_paths": [
|
|
14
14
|
"node_modules",
|
|
15
15
|
"controller/runtime",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deadbyte-mcp",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.35",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Authenticated MCP agentic runtime with signed skills, persistent model backends, identity-bound remote devices, durable job supervision, immutable release slots, and separately revocable machine authority.",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"contained:run": "node src/contained-cli.mjs workspaces/demo input.txt",
|
|
43
43
|
"contained:smoke": "node src/contained-smoke.mjs",
|
|
44
44
|
"mcp:contained:smoke": "node src/mcp-contained-smoke-client.mjs",
|
|
45
|
-
"gate:windows": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-
|
|
45
|
+
"gate:windows": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-r58.ps1",
|
|
46
46
|
"authority:smoke": "node src/authority-smoke.mjs",
|
|
47
47
|
"mcp:remote:smoke": "node src/remote-mcp-smoke-client.mjs",
|
|
48
48
|
"mcp:host:smoke": "node src/mcp-host-smoke-client.mjs",
|
|
@@ -78,7 +78,10 @@
|
|
|
78
78
|
"r53:focused:test": "node --test --test-concurrency=1 test/r53-release-contract.test.mjs test/stabilization-update-child-root.test.mjs test/stabilization-state-root.test.mjs test/stabilization-guardian.test.mjs test/stabilization-semver.test.mjs test/stabilization-powershell.test.mjs test/release-slot.test.mjs test/runtime-self-update.test.mjs test/r51-runtime-coherence.test.mjs test/remote-session-cli.test.mjs test/r52-offline-predecessor-recovery.test.mjs test/r52-host-revocation.test.mjs",
|
|
79
79
|
"r54:focused:test": "node --test --test-concurrency=1 test/r54-release-contract.test.mjs test/r54-offline-predecessor-self-exclusion.test.mjs test/r52-offline-predecessor-recovery.test.mjs test/runtime-self-update.test.mjs test/r51-runtime-coherence.test.mjs test/remote-session-cli.test.mjs",
|
|
80
80
|
"r55:focused:test": "node --test --test-concurrency=1 test/r55-release-contract.test.mjs test/r55-offline-predecessor-guardian-family.test.mjs test/r54-offline-predecessor-self-exclusion.test.mjs test/r52-offline-predecessor-recovery.test.mjs test/runtime-self-update.test.mjs test/r51-runtime-coherence.test.mjs test/remote-session-cli.test.mjs",
|
|
81
|
-
"r56:focused:test": "node --test --test-concurrency=1 test/r56-release-contract.test.mjs test/r56-rollback-launcher-restore.test.mjs test/r55-offline-predecessor-guardian-family.test.mjs test/r54-offline-predecessor-self-exclusion.test.mjs test/runtime-self-update.test.mjs test/release-slot.test.mjs test/r51-runtime-coherence.test.mjs"
|
|
81
|
+
"r56:focused:test": "node --test --test-concurrency=1 test/r56-release-contract.test.mjs test/r56-rollback-launcher-restore.test.mjs test/r55-offline-predecessor-guardian-family.test.mjs test/r54-offline-predecessor-self-exclusion.test.mjs test/runtime-self-update.test.mjs test/release-slot.test.mjs test/r51-runtime-coherence.test.mjs",
|
|
82
|
+
"r57:focused:test": "node --test --test-concurrency=1 test/r57-release-contract.test.mjs test/r57-core-startup-race.test.mjs test/r56-rollback-launcher-restore.test.mjs test/r55-offline-predecessor-guardian-family.test.mjs test/r54-offline-predecessor-self-exclusion.test.mjs test/runtime-self-update.test.mjs test/release-slot.test.mjs test/r51-runtime-coherence.test.mjs test/remote-session-cli.test.mjs",
|
|
83
|
+
"r58:focused:test": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/r58-focused.ps1",
|
|
84
|
+
"r58:cross-plane:test": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/r58-cross-plane.ps1"
|
|
82
85
|
},
|
|
83
86
|
"bundleDependencies": [
|
|
84
87
|
"@modelcontextprotocol/server",
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {mkdir,readFile,writeFile,rm} from 'node:fs/promises';
|
|
2
|
+
import {spawn} from 'node:child_process';
|
|
3
|
+
import {randomBytes,createHash} from 'node:crypto';
|
|
4
|
+
import {createServer} from 'node:net';
|
|
5
|
+
import {performance} from 'node:perf_hooks';
|
|
6
|
+
import {setTimeout as delay} from 'node:timers/promises';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import {pathToFileURL} from 'node:url';
|
|
9
|
+
import {Client,StreamableHTTPClientTransport} from '@modelcontextprotocol/client';
|
|
10
|
+
import {generateTrustKeypair} from '../src/trust.mjs';
|
|
11
|
+
import {loadCodingPolicy} from '../src/coding-plane.mjs';
|
|
12
|
+
|
|
13
|
+
const run=path.resolve(process.argv[2]||'');const base=path.resolve('D:/Projects/deadbyte-mcp/testing/0.15.35');const rel=path.relative(base,run);
|
|
14
|
+
if(!rel||rel.startsWith('..')||path.isAbsolute(rel))throw new Error('new D: R58 run root required');await mkdir(run);
|
|
15
|
+
const core='C:/Users/DEADBYTE/.deadbyte-mcp/releases/v0.15.34-2199bb59f0e0fc20';
|
|
16
|
+
const targets={baseline:'D:/Projects/deadbyte-chatgpt-bridge-r40-dev',candidate:'D:/Projects/deadbyte-mcp/development/deadbyte-mcp-0.15.35-r58/bridge'};
|
|
17
|
+
const entries=Object.entries(targets);if(process.argv[3]==='candidate-first')entries.reverse();
|
|
18
|
+
const sha=b=>createHash('sha256').update(b).digest('hex');
|
|
19
|
+
if(sha(await readFile(path.join(core,'MANIFEST.SHA256')))!=='2199bb59f0e0fc205d470d11f990d3ce3df444be5eba2a7d8dbc60d5f5b790bb')throw new Error('core baseline drift');
|
|
20
|
+
const summaries={};
|
|
21
|
+
const extended=process.argv.includes('--extended');
|
|
22
|
+
const corpus=[{name:'coding_session_status',arguments:{}},{name:'capability_manifest',arguments:{}}];
|
|
23
|
+
if(extended)corpus.push({name:'coding_file_read',arguments:{root_id:'bench',path:'input.txt'}},{name:'coding_tree',arguments:{root_id:'bench',path:'.',max_depth:2,max_entries:100}});
|
|
24
|
+
for(const [label,target] of entries){
|
|
25
|
+
const root=path.join(run,label);await mkdir(root);const state=path.join(root,'state'),workspace=path.join(root,'workspace'),temp=path.join(root,'temp');
|
|
26
|
+
for(const dir of [state,workspace,temp])await mkdir(dir);
|
|
27
|
+
const keys=await generateTrustKeypair({privateKeyPath:path.join(root,'private.pem'),publicKeyPath:path.join(root,'public.pem')});
|
|
28
|
+
const policy=path.join(root,'policy.json');
|
|
29
|
+
await writeFile(policy,JSON.stringify({schema:'deadbyte.coding-policy.v1',enabled:true,lease_file:path.join(state,'grant.json'),evidence_dir:path.join(state,'evidence'),roots:{bench:{path:workspace,read:true,write:false,exec:false,deny_paths:[]}},commands:{},max_read_bytes:65536,max_write_bytes:65536}),{flag:'wx'});
|
|
30
|
+
const fixture='bounded fixture alpha\n'.repeat(100);await writeFile(path.join(workspace,'input.txt'),fixture,{flag:'wx'});
|
|
31
|
+
if(extended){const loaded=await loadCodingPolicy(policy);await writeFile(path.join(state,'grant.json'),JSON.stringify({schema:'deadbyte.coding-grant.v2',policy_sha256:loaded.policy_sha256,instance_nonce:randomBytes(32).toString('hex'),root_ids:['bench']}),{flag:'wx'});}
|
|
32
|
+
const reservation=createServer();await new Promise(resolve=>reservation.listen(0,'127.0.0.1',resolve));const port=reservation.address().port;await new Promise(resolve=>reservation.close(resolve));
|
|
33
|
+
const token=randomBytes(32).toString('hex');const env={...process.env};for(const key of Object.keys(env))if(key.startsWith('DEADBYTE_'))delete env[key];
|
|
34
|
+
Object.assign(env,{TEMP:temp,TMP:temp,npm_config_cache:path.join(root,'npm-cache'),DEADBYTE_CORE_ROOT:core,DEADBYTE_STATE_ROOT:state,DEADBYTE_WORKSPACE_ROOT:workspace,DEADBYTE_BRIDGE_PORT:String(port),DEADBYTE_BRIDGE_PATH_TOKEN:token,DEADBYTE_SIGNING_KEY:keys.privateKeyPath,DEADBYTE_VERIFY_KEY:keys.publicKeyPath,DEADBYTE_PRIVILEGED_POLICY:'',DEADBYTE_CAPABILITY_PROFILE:'compact',DEADBYTE_MACHINE_RUNTIME:'0',DEADBYTE_AGENT_RUNTIME:'0',DEADBYTE_PROCESS_RUNTIME:'0',DEADBYTE_CODING_RUNTIME:'1',DEADBYTE_CODING_POLICY:policy,DEADBYTE_MACHINE_FS_RUNTIME:'0',DEADBYTE_OBSERVATION_RUNTIME:'0',DEADBYTE_DESKTOP_RUNTIME:'0',DEADBYTE_AUTONOMOUS_RUNTIME:'0',DEADBYTE_AUDIT_RUNTIME:'0'});
|
|
35
|
+
const bridgePath=path.join(target,'src/bridge.mjs');const script=`process.on('message',m=>{if(m==='stop')process.emit('SIGTERM')});await import(${JSON.stringify(pathToFileURL(bridgePath).href)});`;
|
|
36
|
+
const start=performance.now();const child=spawn(process.execPath,['--input-type=module','-e',script],{cwd:target,env,windowsHide:true,stdio:['ignore','pipe','pipe','ipc']});
|
|
37
|
+
let stdout='',stderr='';child.stdout.on('data',b=>{stdout+=b;});child.stderr.on('data',b=>{stderr+=b;});const exited=new Promise(resolve=>child.once('exit',code=>resolve(code)));
|
|
38
|
+
const samples=[];let catalog;
|
|
39
|
+
try{
|
|
40
|
+
const deadline=Date.now()+20000;while(!stdout.includes('BRIDGE_READY')&&child.exitCode===null&&Date.now()<deadline)await delay(20);
|
|
41
|
+
if(!stdout.includes('BRIDGE_READY'))throw new Error(`${label} bridge startup failed: ${stderr}`);
|
|
42
|
+
const startup=performance.now()-start;
|
|
43
|
+
for(let batch=0;batch<3;batch++){
|
|
44
|
+
const client=new Client({name:'r58-isolated-bridge-benchmark',version:'1.0.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
|
|
45
|
+
try{
|
|
46
|
+
await client.connect(new StreamableHTTPClientTransport(new URL(`http://127.0.0.1:${port}/mcp/${token}`)));
|
|
47
|
+
const tools=(await client.listTools()).tools;if(!catalog){catalog=tools;await writeFile(path.join(root,'catalog.json'),JSON.stringify(tools,null,2),{flag:'wx'});}
|
|
48
|
+
for(let iteration=0;iteration<120;iteration++)for(const request of corpus){
|
|
49
|
+
const {name}=request;const t=performance.now();const result=await client.callTool(request);const elapsed=performance.now()-t;
|
|
50
|
+
if(result.isError||!result.structuredContent)throw new Error(`${label} benchmark tool failed: ${name}`);
|
|
51
|
+
if(name==='coding_file_read'&&(result.structuredContent.content!==fixture||result.structuredContent.sha256!==sha(fixture)))throw new Error('read behavior/hash oracle mismatch');
|
|
52
|
+
if(iteration>=20)samples.push({batch,iteration:iteration-20,tool:name,roundtrip_ms:elapsed});
|
|
53
|
+
}
|
|
54
|
+
}finally{await client.close();}
|
|
55
|
+
}
|
|
56
|
+
const quant=values=>{const v=[...values].sort((a,b)=>a-b),q=p=>v[Math.ceil(v.length*p)-1];return{n:v.length,min:v[0],p50:q(.5),p90:q(.9),p95:q(.95),p99:q(.99),max:v.at(-1)}};
|
|
57
|
+
summaries[label]={startup_ms:startup,script_sha256:sha(await readFile(bridgePath)),catalog_count:catalog.length,rows:Object.fromEntries(corpus.map(({name})=>[name,quant(samples.filter(s=>s.tool===name).map(s=>s.roundtrip_ms))])),aggregate:quant(samples.map(s=>s.roundtrip_ms))};
|
|
58
|
+
await writeFile(path.join(root,'raw.json'),JSON.stringify(samples),{flag:'wx'});
|
|
59
|
+
}finally{
|
|
60
|
+
await rm(path.join(state,'grant.json'),{force:true});
|
|
61
|
+
if(child.connected)child.send('stop');
|
|
62
|
+
const code=await Promise.race([exited,delay(5000).then(()=>null)]);if(code===null){child.kill();throw new Error('owned fixture bridge did not close through normal shutdown');}
|
|
63
|
+
await writeFile(path.join(root,'stdout.log'),stdout,{flag:'wx'});await writeFile(path.join(root,'stderr.log'),stderr,{flag:'wx'});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const result={schema:'deadbyte.r58.isolated-bridge-performance.v1',status:'MEASURED_NOT_FULL_PERFORMANCE_ACCEPTANCE',core,corpus,workload_sha256:sha(JSON.stringify(corpus)),test_read_grant:extended,grant_removed_after_target:true,summaries,p95_reduction_pct:100*(1-summaries.candidate.aggregate.p95/summaries.baseline.aggregate.p95),limitations:['bounded read-only categories only','not production machine ping/public transport','no mutation/build/test/durable workload','sequential target order; no statistical inference','no production activation']};
|
|
67
|
+
await writeFile(path.join(run,'summary.json'),JSON.stringify(result,null,2),{flag:'wx'});console.log(JSON.stringify(result,null,2));
|
package/scripts/deploy-r48.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import os from 'node:os';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { spawnSync } from 'node:child_process';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
|
-
import {
|
|
7
|
+
import { runSlotUpdateCli } from '../src/runtime-update.mjs';
|
|
8
8
|
import { parseActivePointer } from '../src/release-slot.mjs';
|
|
9
9
|
|
|
10
10
|
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
|
@@ -58,8 +58,7 @@ async function waitForActivation({handoffPath,expectedVersion,expectedManifest,t
|
|
|
58
58
|
|
|
59
59
|
const manifest=await sha(path.join(root,'MANIFEST.SHA256'));
|
|
60
60
|
const pointer=JSON.parse(await readFile(path.join(state,'active-release.json'),'utf8'));
|
|
61
|
-
const
|
|
62
|
-
const bridgeRoot=path.resolve(cfg.Bridge.WorkingDirectory);
|
|
61
|
+
const bridgeRoot=path.join(root,'bridge');
|
|
63
62
|
const bridgeScript=path.join(bridgeRoot,'src','bridge.mjs');
|
|
64
63
|
const pkg=JSON.parse(await readFile(path.join(root,'package.json'),'utf8'));
|
|
65
64
|
const expectedVersion=pkg.version;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
$ErrorActionPreference = 'Stop'
|
|
2
|
+
Set-StrictMode -Version 3.0
|
|
3
|
+
|
|
4
|
+
function Invoke-GateStep {
|
|
5
|
+
param([Parameter(Mandatory)][string]$Name,[Parameter(Mandatory)][string[]]$NpmArgs)
|
|
6
|
+
Write-Host "==== $Name ===="
|
|
7
|
+
& npm.cmd @NpmArgs
|
|
8
|
+
if ($LASTEXITCODE -ne 0) { throw "DEADBYTE 0.15.34 / R57 gate failed at $Name with exit code $LASTEXITCODE" }
|
|
9
|
+
}
|
|
10
|
+
function Invoke-NodeTest {
|
|
11
|
+
param([Parameter(Mandatory)][string]$Name,[Parameter(Mandatory)][string[]]$TestFiles)
|
|
12
|
+
Write-Host "==== $Name ===="
|
|
13
|
+
& node.exe --test --test-concurrency=1 @TestFiles
|
|
14
|
+
if ($LASTEXITCODE -ne 0) { throw "DEADBYTE 0.15.34 / R57 gate failed at $Name with exit code $LASTEXITCODE" }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
Invoke-NodeTest 'r57-package-boundary' @('test/package-boundary.test.mjs')
|
|
18
|
+
Invoke-GateStep 'r57-focused-contracts' @('run','r57:focused:test')
|
|
19
|
+
Write-Host '==== full-regression-evidence ===='
|
|
20
|
+
$matrix=Get-Content -LiteralPath (Join-Path (Split-Path $PSScriptRoot -Parent) 'TEST-MATRIX.json') -Raw | ConvertFrom-Json
|
|
21
|
+
if($matrix.observed_pre_release_evidence.full_regression.status -ne 'PASS'){ throw 'R57 full regression evidence is not PASS' }
|
|
22
|
+
Invoke-GateStep 'manifest-write' @('run','manifest:write')
|
|
23
|
+
Invoke-GateStep 'manifest-verify' @('run','manifest:verify')
|
|
24
|
+
Invoke-GateStep 'doctor' @('run','doctor')
|
|
25
|
+
& node.exe (Join-Path $PSScriptRoot 'windows-gate-evidence-r57.mjs')
|
|
26
|
+
if ($LASTEXITCODE -ne 0) { throw "R57 evidence writer failed with exit code $LASTEXITCODE" }
|
|
27
|
+
Write-Host '==== DEADBYTE 0.15.34 / R57 WINDOWS GATE: PASS ===='
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
$ErrorActionPreference = 'Stop'
|
|
2
|
+
Set-StrictMode -Version 3.0
|
|
3
|
+
|
|
4
|
+
function Invoke-GateStep {
|
|
5
|
+
param([Parameter(Mandatory)][string]$Name,[Parameter(Mandatory)][string[]]$NpmArgs)
|
|
6
|
+
Write-Host "==== $Name ===="
|
|
7
|
+
& npm.cmd @NpmArgs
|
|
8
|
+
if ($LASTEXITCODE -ne 0) { throw "DEADBYTE 0.15.35 / R58 gate failed at $Name with exit code $LASTEXITCODE" }
|
|
9
|
+
}
|
|
10
|
+
function Invoke-NodeTest {
|
|
11
|
+
param([Parameter(Mandatory)][string]$Name,[Parameter(Mandatory)][string[]]$TestFiles)
|
|
12
|
+
Write-Host "==== $Name ===="
|
|
13
|
+
& node.exe --test --test-concurrency=1 @TestFiles
|
|
14
|
+
if ($LASTEXITCODE -ne 0) { throw "DEADBYTE 0.15.35 / R58 gate failed at $Name with exit code $LASTEXITCODE" }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
Invoke-NodeTest 'r58-package-boundary' @('test/package-boundary.test.mjs','test/r58-bundled-bridge.test.mjs')
|
|
18
|
+
Invoke-NodeTest 'r58-process-policy' @('test/process-policy-refresh.test.mjs','test/process-release-gate.test.mjs')
|
|
19
|
+
Invoke-GateStep 'r58-focused-contracts' @('run','r58:focused:test')
|
|
20
|
+
|
|
21
|
+
Write-Host '==== recorded-prepublish-evidence ===='
|
|
22
|
+
$matrix=Get-Content -LiteralPath (Join-Path (Split-Path $PSScriptRoot -Parent) 'TEST-MATRIX.json') -Raw | ConvertFrom-Json
|
|
23
|
+
if($matrix.observed_pre_release_evidence.performance.status -ne 'PASS_BOUNDED_PREPUBLISH'){ throw 'R58 performance evidence is not PASS_BOUNDED_PREPUBLISH' }
|
|
24
|
+
if($matrix.observed_pre_release_evidence.cross_plane.status -ne 'PASS'){ throw 'R58 cross-plane evidence is not PASS' }
|
|
25
|
+
if($matrix.observed_pre_release_evidence.public_surface.status -ne 'PASS'){ throw 'R58 public surface evidence is not PASS' }
|
|
26
|
+
if($matrix.observed_pre_release_evidence.full_regression.status -ne 'PASS'){ throw 'R58 fresh full regression evidence is not PASS' }
|
|
27
|
+
|
|
28
|
+
Invoke-GateStep 'manifest-write' @('run','manifest:write')
|
|
29
|
+
Invoke-GateStep 'manifest-verify' @('run','manifest:verify')
|
|
30
|
+
Invoke-GateStep 'doctor' @('run','doctor')
|
|
31
|
+
& node.exe (Join-Path $PSScriptRoot 'windows-gate-evidence-r58.mjs')
|
|
32
|
+
if ($LASTEXITCODE -ne 0) { throw "R58 evidence writer failed with exit code $LASTEXITCODE" }
|
|
33
|
+
Write-Host '==== DEADBYTE 0.15.35 / R58 WINDOWS GATE: PASS ===='
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {spawn,spawnSync} from 'node:child_process';
|
|
2
|
+
import {writeFile} from 'node:fs/promises';
|
|
3
|
+
import {performance} from 'node:perf_hooks';
|
|
4
|
+
import {createHash} from 'node:crypto';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {windowsPowerShellEnvironment} from '../src/runtime-update.mjs';
|
|
7
|
+
|
|
8
|
+
// Diagnostic only. Queries only a child created and held by this process.
|
|
9
|
+
// Not an ownership authority, cleanup path, or public PID control tool.
|
|
10
|
+
const out=path.resolve(process.argv[2]||'');
|
|
11
|
+
const base=path.resolve('D:/Projects/deadbyte-mcp/testing/0.15.35');
|
|
12
|
+
const rel=path.relative(base,out);
|
|
13
|
+
if(!rel||rel.startsWith('..')||path.isAbsolute(rel))throw new Error('D: R58 evidence destination required');
|
|
14
|
+
const ps=path.join(process.env.SystemRoot,'System32/WindowsPowerShell/v1.0/powershell.exe');
|
|
15
|
+
const child=spawn(process.execPath,['-e','setTimeout(()=>{},30000)'],{stdio:'ignore',windowsHide:true});
|
|
16
|
+
const closed=new Promise(resolve=>child.once('close',resolve));
|
|
17
|
+
const samples=[];
|
|
18
|
+
try{
|
|
19
|
+
if(!Number.isInteger(child.pid)||child.pid<1)throw new Error('diagnostic child failed');
|
|
20
|
+
for(let i=0;i<3;i++){
|
|
21
|
+
const query=`$p=Get-CimInstance Win32_Process -Filter 'ProcessId=${child.pid}'; if ($null -eq $p) {exit 2}; [pscustomobject]@{pid=$p.ProcessId; parent_pid=$p.ParentProcessId; creation_utc=$p.CreationDate.ToUniversalTime().ToString('o'); executable_path=$p.ExecutablePath; command_line=$p.CommandLine} | ConvertTo-Json -Compress`;
|
|
22
|
+
const t=performance.now();const result=spawnSync(ps,['-NoLogo','-NoProfile','-Command',query],{encoding:'utf8',windowsHide:true,env:windowsPowerShellEnvironment(process.env),timeout:15000});
|
|
23
|
+
if(result.error||result.status!==0)throw new Error('narrow process diagnostic failed');
|
|
24
|
+
const info=JSON.parse(result.stdout);
|
|
25
|
+
if(info.pid!==child.pid||info.parent_pid!==process.pid||path.resolve(info.executable_path).toLowerCase()!==path.resolve(process.execPath).toLowerCase())throw new Error('owned diagnostic process identity mismatch');
|
|
26
|
+
samples.push({pid:info.pid,parent_pid:info.parent_pid,creation_utc:info.creation_utc,executable_path:info.executable_path,command_line_sha256:createHash('sha256').update(info.command_line).digest('hex'),query_ms:performance.now()-t});
|
|
27
|
+
}
|
|
28
|
+
if(new Set(samples.map(s=>s.creation_utc)).size!==1)throw new Error('creation identity changed');
|
|
29
|
+
await writeFile(out,JSON.stringify({schema:'deadbyte.r58.owner-probe.v1',observed_at:new Date().toISOString(),status:'DIAGNOSTIC_ONLY',samples,limitations:['not production ownership verification','not PID-reuse recovery acceptance','CIM spawn latency unsuitable for frequent guardian polling without measured redesign']},null,2),{flag:'wx'});
|
|
30
|
+
console.log(JSON.stringify({status:'DIAGNOSTIC_ONLY',samples:samples.length,query_ms:samples.map(s=>s.query_ms)}));
|
|
31
|
+
} finally {if(child.exitCode===null)child.kill();await closed;}
|