deadbyte-mcp 0.15.34 → 0.15.36
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 +100 -38
- package/PROVENANCE.json +87 -41
- package/README.txt +72 -14
- package/RELEASE.json +187 -150
- package/SBOM.cdx.json +25 -3
- package/TEST-MATRIX.json +258 -151
- package/bridge/package.json +13 -0
- package/bridge/src/bridge-health-r59.mjs +52 -0
- package/bridge/src/bridge.mjs +487 -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 +206 -0
- package/controller/deadbyte-controller.ps1 +152 -18
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R59-TASK-CONTRACT.md +26 -0
- package/package.json +7 -3
- package/proof/r59-full-regression-current.json +13 -0
- package/proof/r59-full-regression-current.stderr.log +0 -0
- package/proof/r59-full-regression-current.stdout.log +1268 -0
- package/scripts/bridge-benchmark-r58.mjs +67 -0
- package/scripts/deploy-r48.mjs +2 -3
- package/scripts/gate-windows-r58.ps1 +33 -0
- package/scripts/gate-windows-r59-prepublish.ps1 +41 -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/r59-full-regression-capture.mjs +35 -0
- package/scripts/windows-gate-evidence-r58.mjs +93 -0
- package/src/capability-resolver.mjs +25 -7
- package/src/capability-snapshot.mjs +52 -0
- package/src/compact-gateway-mcp-tools.mjs +10 -11
- 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/mcp-server.mjs +179 -118
- package/src/r40-compact-disarmed-probe.mjs +1 -1
- package/src/r44-compact-disarmed-probe.mjs +1 -1
- package/src/r45-compact-disarmed-probe.mjs +1 -1
- package/src/release-generation.mjs +1 -0
- package/src/release-slot.mjs +2 -1
- package/src/remote-mcp-r40-parity-client.mjs +1 -1
- package/src/remote-mcp-r44-parity-client.mjs +1 -1
- package/src/remote-mcp-r45-parity-client.mjs +1 -1
- package/src/remote-mcp-smoke-client.mjs +1 -1
- package/src/remote-session-guardian.mjs +8 -1
- package/src/runtime-plane-registry.mjs +74 -0
- package/src/runtime-update.mjs +85 -2
- package/src/version.mjs +1 -1
- package/test/bridge-runtime-r59.test.mjs +60 -0
- package/test/capability-resolver.test.mjs +16 -0
- package/test/capability-snapshot-r59.test.mjs +39 -0
- package/test/compact-gateway-r36.test.mjs +22 -2
- package/test/compact-max-catalog-r44.test.mjs +1 -1
- package/test/compact-max-catalog-r45.test.mjs +1 -1
- package/test/contained.test.mjs +2 -1
- package/test/controller-policy-preflight-r59.test.mjs +39 -0
- package/test/controller-r211.test.mjs +4 -1
- package/test/controller.test.mjs +2 -1
- package/test/fixtures/r59-compact-surface-v0.15.35.json +222 -0
- package/test/helpers/r58-doctor-fixture.mjs +26 -0
- package/test/observation-integration.test.mjs +2 -3
- package/test/optional-plane-degraded-r59.test.mjs +44 -0
- package/test/r49-windows-gate.test.mjs +2 -2
- package/test/r57-release-contract.test.mjs +5 -3
- package/test/r58-bridge-dependency-binding.test.mjs +62 -0
- package/test/r58-bundled-bridge.test.mjs +24 -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 +98 -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 +50 -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 +32 -0
- package/test/r59-release-contract.test.mjs +61 -0
- package/test/r59-tool-surface-compat.test.mjs +90 -0
- package/test/release-generation.test.mjs +4 -1
- package/test/remote-session-cli.test.mjs +55 -2
- package/test/runtime-plane-registry-r59.test.mjs +40 -0
|
@@ -0,0 +1,206 @@
|
|
|
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:[],admitted:0,completed:0,rejected:0,timed_out:0,cancelled:0}]));
|
|
107
|
+
let sequence=0;
|
|
108
|
+
let closed=false;
|
|
109
|
+
const drainWaiters=[];
|
|
110
|
+
|
|
111
|
+
function finishDrain(){
|
|
112
|
+
if(!closed||[...state.values()].some(item=>item.active>0||item.queue.some(waiter=>!waiter.done))) return;
|
|
113
|
+
while(drainWaiters.length) drainWaiters.shift()();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function dispatch(name){
|
|
117
|
+
const cfg=limits[name],s=state.get(name);
|
|
118
|
+
while(s.active<cfg.max_active&&s.queue.length){
|
|
119
|
+
const waiter=s.queue.shift();
|
|
120
|
+
if(waiter.done) continue;
|
|
121
|
+
waiter.done=true; clearTimeout(waiter.timer); waiter.cleanup(); s.active++; s.admitted++;
|
|
122
|
+
waiter.resolve(makeTicket(name,waiter.tool,waiter.id,waiter.queuedAt));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function makeTicket(name,tool,id,queuedAt){
|
|
126
|
+
let released=false;
|
|
127
|
+
return Object.freeze({
|
|
128
|
+
id,class:name,tool,queued_ms:Math.max(0,Date.now()-queuedAt),
|
|
129
|
+
release(){
|
|
130
|
+
if(released) return false;
|
|
131
|
+
released=true;
|
|
132
|
+
const s=state.get(name);
|
|
133
|
+
s.active=Math.max(0,s.active-1);s.completed++;
|
|
134
|
+
dispatch(name);
|
|
135
|
+
finishDrain();
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
async function acquire(tool,{signal}={}){
|
|
141
|
+
const name=toolClass(tool),cfg=limits[name],s=state.get(name),id=++sequence,queuedAt=Date.now();
|
|
142
|
+
if(closed){s.rejected++;throw new Error('bridge governor closed');}
|
|
143
|
+
if(signal?.aborted){s.cancelled++;throw signal.reason instanceof Error?signal.reason:new Error('bridge request aborted');}
|
|
144
|
+
if(s.active<cfg.max_active){
|
|
145
|
+
s.active++;s.admitted++;
|
|
146
|
+
return makeTicket(name,String(tool),id,queuedAt);
|
|
147
|
+
}
|
|
148
|
+
if(s.queue.length>=cfg.max_queue){s.rejected++;throw new Error(`bridge backpressure queue full for ${name}`);}
|
|
149
|
+
return await new Promise((resolve,reject)=>{
|
|
150
|
+
const waiter={id,tool:String(tool),queuedAt,resolve,reject,done:false,timer:null,cleanup:()=>{}};
|
|
151
|
+
const abort=()=>{
|
|
152
|
+
if(waiter.done)return;
|
|
153
|
+
waiter.done=true;clearTimeout(waiter.timer);waiter.cleanup();
|
|
154
|
+
const index=s.queue.indexOf(waiter);if(index>=0)s.queue.splice(index,1);
|
|
155
|
+
s.cancelled++;reject(signal?.reason instanceof Error?signal.reason:new Error('bridge request aborted'));finishDrain();
|
|
156
|
+
};
|
|
157
|
+
waiter.cleanup=()=>signal?.removeEventListener?.('abort',abort);
|
|
158
|
+
signal?.addEventListener?.('abort',abort,{once:true});
|
|
159
|
+
waiter.timer=setTimeout(()=>{
|
|
160
|
+
if(waiter.done)return;
|
|
161
|
+
waiter.done=true;waiter.cleanup();s.timed_out++;
|
|
162
|
+
const index=s.queue.indexOf(waiter); if(index>=0)s.queue.splice(index,1);
|
|
163
|
+
reject(new Error(`bridge backpressure queue timeout for ${name}`));
|
|
164
|
+
finishDrain();
|
|
165
|
+
},cfg.queue_timeout_ms);
|
|
166
|
+
s.queue.push(waiter);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
function close(reason='bridge governor closed'){
|
|
170
|
+
if(!closed){
|
|
171
|
+
closed=true;
|
|
172
|
+
for(const s of state.values()){
|
|
173
|
+
for(const waiter of s.queue.splice(0)){
|
|
174
|
+
if(waiter.done)continue;
|
|
175
|
+
waiter.done=true;clearTimeout(waiter.timer);waiter.cleanup();s.cancelled++;
|
|
176
|
+
waiter.reject(new Error(String(reason)));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if([...state.values()].every(item=>item.active===0)) return Promise.resolve();
|
|
181
|
+
return new Promise(resolve=>drainWaiters.push(resolve));
|
|
182
|
+
}
|
|
183
|
+
function reconcile(reason='bridge downstream reconciling'){
|
|
184
|
+
if(closed) return 0;
|
|
185
|
+
let removed=0;
|
|
186
|
+
for(const s of state.values()){
|
|
187
|
+
for(const waiter of s.queue.splice(0)){
|
|
188
|
+
if(waiter.done)continue;
|
|
189
|
+
waiter.done=true;clearTimeout(waiter.timer);waiter.cleanup();s.cancelled++;removed++;
|
|
190
|
+
waiter.reject(new Error(String(reason)));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return removed;
|
|
194
|
+
}
|
|
195
|
+
function snapshot(){
|
|
196
|
+
return Object.freeze(Object.fromEntries(classes.map(name=>{
|
|
197
|
+
const s=state.get(name),cfg=limits[name];
|
|
198
|
+
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,
|
|
199
|
+
admitted:s.admitted,completed:s.completed,rejected:s.rejected,timed_out:s.timed_out,cancelled:s.cancelled,saturated:s.active>=cfg.max_active})];
|
|
200
|
+
})));
|
|
201
|
+
}
|
|
202
|
+
return Object.freeze({acquire,reconcile,close,snapshot});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export const BRIDGE_R40_DEFAULT_LIMITS=DEFAULT_LIMITS;
|
|
206
|
+
|
|
@@ -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()
|
|
@@ -683,11 +685,31 @@ function Stop-OwnedComponent([string]$Name) {
|
|
|
683
685
|
Remove-Item -LiteralPath (Ledger-Path $Name) -Force -ErrorAction SilentlyContinue
|
|
684
686
|
}
|
|
685
687
|
|
|
686
|
-
function
|
|
688
|
+
function Get-LocalHealth($Cfg) {
|
|
689
|
+
$response = $null
|
|
690
|
+
try {
|
|
691
|
+
$request = [System.Net.HttpWebRequest]::Create([string]$Cfg.LocalReadyUri)
|
|
692
|
+
$request.Timeout = [int]$Cfg.ProbeTimeoutSeconds * 1000
|
|
693
|
+
$request.ReadWriteTimeout = [int]$Cfg.ProbeTimeoutSeconds * 1000
|
|
694
|
+
$response = $request.GetResponse()
|
|
695
|
+
} catch [System.Net.WebException] {
|
|
696
|
+
$response = $_.Exception.Response
|
|
697
|
+
if ($null -eq $response) { return [pscustomobject]@{ State='UNREACHABLE'; StatusCode=0; Diagnostic='readiness endpoint unreachable' } }
|
|
698
|
+
} catch { return [pscustomobject]@{ State='UNREACHABLE'; StatusCode=0; Diagnostic='readiness probe failed' } }
|
|
687
699
|
try {
|
|
688
|
-
$
|
|
689
|
-
|
|
690
|
-
|
|
700
|
+
$status = [int]$response.StatusCode
|
|
701
|
+
$reader = New-Object System.IO.StreamReader($response.GetResponseStream())
|
|
702
|
+
try { $body = $reader.ReadToEnd() } finally { $reader.Dispose() }
|
|
703
|
+
try { $parsed = $body | ConvertFrom-Json } catch { return [pscustomobject]@{ State='INVALID'; StatusCode=$status; Diagnostic='readiness response JSON invalid' } }
|
|
704
|
+
if ($parsed.schema -ne 'deadbyte.bridge-health.v1' -or [string]$parsed.state -notin @('STARTING','READY','DEGRADED','RECONCILING','STOPPING')) {
|
|
705
|
+
return [pscustomobject]@{ State='INVALID'; StatusCode=$status; Diagnostic='readiness response schema invalid' }
|
|
706
|
+
}
|
|
707
|
+
return [pscustomobject]@{ State=[string]$parsed.state; StatusCode=$status; Diagnostic=[string]$parsed.diagnostic }
|
|
708
|
+
} finally { if ($null -ne $response) { $response.Dispose() } }
|
|
709
|
+
}
|
|
710
|
+
function Test-LocalReady($Cfg) {
|
|
711
|
+
$health = Get-LocalHealth $Cfg
|
|
712
|
+
return ($health.State -eq 'READY' -and $health.StatusCode -ge 200 -and $health.StatusCode -lt 300)
|
|
691
713
|
}
|
|
692
714
|
function Get-LocalReadyListenerOwners($Cfg) {
|
|
693
715
|
try { $uri = [Uri]([string]$Cfg.LocalReadyUri) } catch { Fail 'LocalReadyUri is invalid' }
|
|
@@ -717,16 +739,43 @@ function Test-LocalReadyOwned($Cfg, $Bridge) {
|
|
|
717
739
|
if ($owners.Count -ne 1 -or $owners[0] -ne [int]$Bridge.Ledger.pid) { return $false }
|
|
718
740
|
return (Test-LocalReady $Cfg)
|
|
719
741
|
}
|
|
742
|
+
function Get-LocalHealthOwned($Cfg, $Bridge) {
|
|
743
|
+
if (-not $Bridge -or -not $Bridge.Alive) { return [pscustomobject]@{ State='UNREACHABLE'; StatusCode=0; Diagnostic='bridge not alive' } }
|
|
744
|
+
$owners = @(Get-LocalReadyListenerOwners $Cfg)
|
|
745
|
+
if ($owners.Count -ne 1 -or $owners[0] -ne [int]$Bridge.Ledger.pid) { return [pscustomobject]@{ State='UNATTRIBUTED'; StatusCode=0; Diagnostic='readiness listener ownership mismatch' } }
|
|
746
|
+
return (Get-LocalHealth $Cfg)
|
|
747
|
+
}
|
|
720
748
|
|
|
721
749
|
function Get-ObservedState($Cfg) {
|
|
722
750
|
$bridge = Test-OwnedInstance 'Bridge'
|
|
723
751
|
$cloud = Test-OwnedInstance 'Cloud'
|
|
724
|
-
$
|
|
752
|
+
$core = Get-DownstreamCoreObservation $Cfg $bridge
|
|
753
|
+
$health = if ($bridge.Alive -and $core.alive) { try { Get-LocalHealthOwned $Cfg $bridge } catch { [pscustomobject]@{ State='UNREACHABLE'; StatusCode=0; Diagnostic='readiness attribution failed' } } } else { [pscustomobject]@{ State='UNREACHABLE'; StatusCode=0; Diagnostic='core not alive' } }
|
|
754
|
+
$ready = $health.State -eq 'READY'
|
|
725
755
|
$state = if (-not $bridge.Alive -and -not $cloud.Alive) { 'OFFLINE' }
|
|
726
756
|
elseif (-not $bridge.Alive -and $cloud.Alive) { 'PARKED' }
|
|
727
757
|
elseif ($bridge.Alive -and $cloud.Alive -and $ready) { 'ONLINE' }
|
|
728
|
-
|
|
729
|
-
|
|
758
|
+
elseif ($bridge.Alive -and $cloud.Alive -and $core.alive -and $health.State -eq 'DEGRADED') { 'DEGRADED' }
|
|
759
|
+
elseif ($bridge.Alive -and $core.reason -eq 'startup-zero-child') { 'STARTING' }
|
|
760
|
+
else { 'FAILED' }
|
|
761
|
+
[pscustomobject]@{ State=$state; Bridge=$bridge; Core=$core; Cloud=$cloud; LocalReady=$ready; LocalHealth=$health }
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function Wait-LocalOperational($Cfg) {
|
|
765
|
+
$deadline = [DateTime]::UtcNow.AddSeconds([int]$Cfg.StartTimeoutSeconds)
|
|
766
|
+
do {
|
|
767
|
+
$bridge = Test-OwnedInstance 'Bridge'
|
|
768
|
+
if (-not $bridge.Alive) { return $null }
|
|
769
|
+
$core = Get-DownstreamCoreObservation $Cfg $bridge
|
|
770
|
+
if (-not $core.alive -and $core.reason -ne 'startup-zero-child') { return $null }
|
|
771
|
+
if ($core.alive) {
|
|
772
|
+
$health = Get-LocalHealthOwned $Cfg $bridge
|
|
773
|
+
if ($health.State -in @('READY','DEGRADED')) { return $health }
|
|
774
|
+
}
|
|
775
|
+
if ([DateTime]::UtcNow -ge $deadline) { return $null }
|
|
776
|
+
Start-Sleep -Milliseconds 250
|
|
777
|
+
} while ([DateTime]::UtcNow -lt $deadline)
|
|
778
|
+
return $null
|
|
730
779
|
}
|
|
731
780
|
|
|
732
781
|
function Wait-LocalReady($Cfg) {
|
|
@@ -734,7 +783,10 @@ function Wait-LocalReady($Cfg) {
|
|
|
734
783
|
do {
|
|
735
784
|
$bridge = Test-OwnedInstance 'Bridge'
|
|
736
785
|
if (-not $bridge.Alive) { return $false }
|
|
737
|
-
|
|
786
|
+
$core = Get-DownstreamCoreObservation $Cfg $bridge
|
|
787
|
+
if (-not $core.alive -and $core.reason -ne 'startup-zero-child') { return $false }
|
|
788
|
+
if ($core.alive -and (Test-LocalReadyOwned $Cfg $bridge)) { return $true }
|
|
789
|
+
if ([DateTime]::UtcNow -ge $deadline) { return $false }
|
|
738
790
|
Start-Sleep -Milliseconds 250
|
|
739
791
|
} while ([DateTime]::UtcNow -lt $deadline)
|
|
740
792
|
return $false
|
|
@@ -835,17 +887,18 @@ function Invoke-CoreGate($Cfg, [string]$ScriptName, [string[]]$ExtraArguments =
|
|
|
835
887
|
|
|
836
888
|
function Start-Deadbyte($Cfg) {
|
|
837
889
|
$state = Get-ObservedState $Cfg
|
|
838
|
-
if ($state.State -
|
|
839
|
-
if ($state.State -
|
|
890
|
+
if ($state.State -in @('ONLINE','DEGRADED')) { return $state }
|
|
891
|
+
if ($state.State -in @('FAILED','STARTING')) { Fail "runtime is $($state.State); inspect status and reconcile verified owned components before restart" }
|
|
840
892
|
$cloudWasAlive = [bool]$state.Cloud.Alive
|
|
841
893
|
Assert-LocalReadyPortAvailable $Cfg
|
|
842
894
|
try {
|
|
843
895
|
Start-OwnedComponent 'Bridge' $Cfg.Bridge $Cfg | Out-Null
|
|
844
|
-
|
|
896
|
+
$localHealth = Wait-LocalOperational $Cfg
|
|
897
|
+
if ($null -eq $localHealth) { Fail 'bridge started but owned local readiness gate failed' }
|
|
845
898
|
Start-OwnedComponent 'Cloud' $Cfg.Cloud $Cfg | Out-Null
|
|
846
899
|
if (-not (Wait-CurrentUrl $Cfg)) { Fail 'public endpoint metadata did not become available before timeout' }
|
|
847
900
|
$final = Get-ObservedState $Cfg
|
|
848
|
-
if ($final.State -
|
|
901
|
+
if ($final.State -notin @('ONLINE','DEGRADED')) { Fail "start gate failed: observed $($final.State)" }
|
|
849
902
|
return $final
|
|
850
903
|
} catch {
|
|
851
904
|
try { Stop-OwnedComponent 'Bridge' } catch {}
|
|
@@ -872,6 +925,18 @@ function Stop-Deadbyte($Cfg) {
|
|
|
872
925
|
return $s
|
|
873
926
|
}
|
|
874
927
|
|
|
928
|
+
function Assert-PolicyRootsAvailable($Policy, [string]$Plane) {
|
|
929
|
+
$rootIds = @($Policy.roots.PSObject.Properties.Name)
|
|
930
|
+
if ($rootIds.Count -lt 1) { Fail "$Plane policy requires at least one root" }
|
|
931
|
+
foreach ($rootId in $rootIds) {
|
|
932
|
+
$raw = [string]$Policy.roots.$rootId.path
|
|
933
|
+
if ([string]::IsNullOrWhiteSpace($raw)) { Fail "$Plane policy root '$rootId' path missing" }
|
|
934
|
+
try { $full = [System.IO.Path]::GetFullPath($raw) } catch { Fail "$Plane policy root '$rootId' path invalid" }
|
|
935
|
+
if (-not (Test-Path -LiteralPath $full -PathType Container)) { Fail "$Plane policy root '$rootId' missing: $full" }
|
|
936
|
+
}
|
|
937
|
+
return $rootIds
|
|
938
|
+
}
|
|
939
|
+
|
|
875
940
|
function Get-HostPolicyMetadata($Cfg) {
|
|
876
941
|
if ([string]::IsNullOrWhiteSpace([string]$Cfg.HostPolicyFile)) { Fail 'privileged host plane is not configured' }
|
|
877
942
|
$policyPath = [string]$Cfg.HostPolicyFile
|
|
@@ -881,6 +946,7 @@ function Get-HostPolicyMetadata($Cfg) {
|
|
|
881
946
|
$armFile = [System.IO.Path]::GetFullPath([string]$policy.arm_file)
|
|
882
947
|
$runtimeReal = [System.IO.Path]::GetFullPath($RuntimeDir).TrimEnd('\') + '\'
|
|
883
948
|
if (-not $armFile.StartsWith($runtimeReal, [System.StringComparison]::OrdinalIgnoreCase)) { Fail 'host policy arm_file must live under controller runtime directory' }
|
|
949
|
+
Assert-PolicyRootsAvailable $policy 'host' | Out-Null
|
|
884
950
|
[pscustomobject]@{
|
|
885
951
|
Path = $policyPath
|
|
886
952
|
Sha256 = Get-ExecutableHash $policyPath
|
|
@@ -943,7 +1009,7 @@ function Get-ProcessPolicyMetadata($Cfg) {
|
|
|
943
1009
|
$runtimeReal = [System.IO.Path]::GetFullPath($RuntimeDir).TrimEnd('\') + '\'
|
|
944
1010
|
if (-not $grantFile.StartsWith($runtimeReal, [System.StringComparison]::OrdinalIgnoreCase)) { Fail 'process policy grant_file must live under controller runtime directory' }
|
|
945
1011
|
$profileIds = @($policy.profiles.PSObject.Properties.Name)
|
|
946
|
-
$rootIds = @($policy
|
|
1012
|
+
$rootIds = @(Assert-PolicyRootsAvailable $policy 'process')
|
|
947
1013
|
if ($profileIds.Count -lt 1 -or $rootIds.Count -lt 1) { Fail 'process policy requires profiles and roots' }
|
|
948
1014
|
[pscustomobject]@{ Path=$policyPath; Sha256=Get-ExecutableHash $policyPath; GrantFile=$grantFile; ProfileIds=$profileIds; RootIds=$rootIds }
|
|
949
1015
|
}
|
|
@@ -1003,8 +1069,7 @@ function Get-CodingPolicyMetadata($Cfg) {
|
|
|
1003
1069
|
$leaseFile = [System.IO.Path]::GetFullPath([string]$policy.lease_file)
|
|
1004
1070
|
$runtimeReal = [System.IO.Path]::GetFullPath($RuntimeDir).TrimEnd('\') + '\'
|
|
1005
1071
|
if (-not $leaseFile.StartsWith($runtimeReal, [System.StringComparison]::OrdinalIgnoreCase)) { Fail 'coding policy lease_file must live under controller runtime directory' }
|
|
1006
|
-
$rootIds = @($policy
|
|
1007
|
-
if ($rootIds.Count -lt 1) { Fail 'coding policy requires at least one root' }
|
|
1072
|
+
$rootIds = @(Assert-PolicyRootsAvailable $policy 'coding')
|
|
1008
1073
|
[pscustomobject]@{ Path=$policyPath; Sha256=Get-ExecutableHash $policyPath; LeaseFile=$leaseFile; RootIds=$rootIds }
|
|
1009
1074
|
}
|
|
1010
1075
|
|
|
@@ -1265,6 +1330,73 @@ function Recover-AutonomousSupervisor($Cfg) {
|
|
|
1265
1330
|
return [pscustomobject]@{ Runtime=$runtime; Supervisor=$supervisor; Process=$process; Coding=$coding; Autonomous=$autonomous; Desktop=$desktop; Host=$hostStatus }
|
|
1266
1331
|
}
|
|
1267
1332
|
|
|
1333
|
+
function Get-BoundedObservationReason($Value) {
|
|
1334
|
+
$reason = [string]$Value
|
|
1335
|
+
if ($reason -cmatch '^[a-z][a-z0-9-]{0,63}$') { return $reason }
|
|
1336
|
+
return 'unverified'
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
function Convert-OwnedObservation($Owned) {
|
|
1340
|
+
$pidValue = $null; $birth = $null
|
|
1341
|
+
if ($Owned.Alive -and $Owned.Ledger) {
|
|
1342
|
+
$pidValue = [int]$Owned.Ledger.pid
|
|
1343
|
+
$birth = [string]$Owned.Ledger.creation_time_utc
|
|
1344
|
+
}
|
|
1345
|
+
[ordered]@{ alive=[bool]$Owned.Alive; reason=(Get-BoundedObservationReason $Owned.Reason); pid=$pidValue; creation_time_utc=$birth }
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
function Convert-AuthorityObservation($Authority) {
|
|
1349
|
+
[ordered]@{ configured=[bool]$Authority.Configured; armed=[bool]$Authority.Armed; reason=(Get-BoundedObservationReason $Authority.Reason) }
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
function Get-DownstreamCoreObservation($Cfg, $Bridge) {
|
|
1353
|
+
$out = [ordered]@{ alive=$false; reason='bridge-unavailable'; pid=$null; parent_pid=$null; creation_time_utc=$null; identity_basis=$null }
|
|
1354
|
+
if (-not $Bridge.Alive) { return $out }
|
|
1355
|
+
$bridgePid = [int]$Bridge.Ledger.pid
|
|
1356
|
+
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) {
|
|
1357
|
+
$out.reason='bridge-config-mismatch'; return $out
|
|
1358
|
+
}
|
|
1359
|
+
try {
|
|
1360
|
+
$children = @(Get-CimInstance Win32_Process -Filter ("ParentProcessId={0} AND Name='node.exe'" -f $bridgePid) -ErrorAction Stop)
|
|
1361
|
+
if ($children.Count -eq 0) { $out.reason='startup-zero-child'; return $out }
|
|
1362
|
+
if ($children.Count -ne 1) { $out.reason='ambiguous-core'; return $out }
|
|
1363
|
+
$child = $children[0]
|
|
1364
|
+
if ([int]$child.ParentProcessId -ne $bridgePid -or [int]$child.ProcessId -lt 1 -or $null -eq $child.CreationDate) { $out.reason='core-identity-incomplete'; return $out }
|
|
1365
|
+
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 }
|
|
1366
|
+
$command = [string]$child.CommandLine
|
|
1367
|
+
$needle = (Join-Path ([string]$Cfg.CoreRoot) 'src\mcp-server.mjs').Replace('/','\').ToLowerInvariant()
|
|
1368
|
+
if ([string]::IsNullOrWhiteSpace($command)) { $basis='verified-bridge-single-opaque-node-child' }
|
|
1369
|
+
elseif ($command.Replace('/','\').ToLowerInvariant().Contains($needle)) { $basis='command-line' }
|
|
1370
|
+
else { $out.reason='wrong-core-generation'; return $out }
|
|
1371
|
+
$out.alive=$true; $out.reason='verified'; $out.pid=[int]$child.ProcessId; $out.parent_pid=$bridgePid
|
|
1372
|
+
$out.creation_time_utc=$child.CreationDate.ToUniversalTime().ToString('o'); $out.identity_basis=$basis
|
|
1373
|
+
return $out
|
|
1374
|
+
} catch { $out.reason='core-observation-failed'; return $out }
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
function Get-StructuredObservation($Cfg) {
|
|
1378
|
+
$s = Get-ObservedState $Cfg
|
|
1379
|
+
$supervisor = Get-AutonomousSupervisorStatus $Cfg
|
|
1380
|
+
[ordered]@{
|
|
1381
|
+
schema='deadbyte.controller-observation.v1'
|
|
1382
|
+
observed_at_utc=[DateTime]::UtcNow.ToString('o')
|
|
1383
|
+
state=[string]$s.State
|
|
1384
|
+
core_root=[string]$Cfg.CoreRoot
|
|
1385
|
+
local_ready=[bool]$s.LocalReady
|
|
1386
|
+
bridge=(Convert-OwnedObservation $s.Bridge)
|
|
1387
|
+
core=$s.Core
|
|
1388
|
+
cloud=(Convert-OwnedObservation $s.Cloud)
|
|
1389
|
+
supervisor=[ordered]@{ configured=[bool]$supervisor.Configured; running=[bool]$supervisor.Running; reason=(Get-BoundedObservationReason $supervisor.Reason) }
|
|
1390
|
+
authority=[ordered]@{
|
|
1391
|
+
process=(Convert-AuthorityObservation (Get-ProcessGrantStatus $Cfg))
|
|
1392
|
+
coding=(Convert-AuthorityObservation (Get-CodingLeaseStatus $Cfg))
|
|
1393
|
+
autonomous=(Convert-AuthorityObservation (Get-AutonomousLeaseStatus $Cfg))
|
|
1394
|
+
desktop=(Convert-AuthorityObservation (Get-DesktopGrantStatus $Cfg))
|
|
1395
|
+
host=(Convert-AuthorityObservation (Get-HostArmStatus $Cfg))
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1268
1400
|
function Show-State($Cfg) {
|
|
1269
1401
|
$s = Get-ObservedState $Cfg
|
|
1270
1402
|
$bp = if ($s.Bridge.Alive) { $s.Bridge.Ledger.pid } else { '-' }
|
|
@@ -1272,6 +1404,7 @@ function Show-State($Cfg) {
|
|
|
1272
1404
|
$url = Get-CurrentUrl $Cfg
|
|
1273
1405
|
Write-Host ("DEADBYTE MCP = {0}" -f $s.State)
|
|
1274
1406
|
Write-Host ("Bridge PID : {0} [{1}]" -f $bp, $s.Bridge.Reason)
|
|
1407
|
+
Write-Host ("Core PID : {0} [{1}]" -f $(if ($s.Core.alive) { $s.Core.pid } else { '-' }), $s.Core.reason)
|
|
1275
1408
|
Write-Host ("Cloud PID : {0} [{1}]" -f $cp, $s.Cloud.Reason)
|
|
1276
1409
|
Write-Host ("Local ready : {0}" -f $s.LocalReady)
|
|
1277
1410
|
Write-Host ("URL : {0}" -f $(if ($url) { $url } else { '<unknown>' }))
|
|
@@ -1370,14 +1503,15 @@ function Clean-Stale {
|
|
|
1370
1503
|
function Run-CommandCore([string]$Name) {
|
|
1371
1504
|
$cfg = Get-Config
|
|
1372
1505
|
Assert-ControllerRootBound $cfg
|
|
1373
|
-
|
|
1506
|
+
$readOnly = $Name -in @('status','url','hoststatus','processstatus','codingstatus','autonomousstatus','desktopstatus','supervisorstatus')
|
|
1507
|
+
if (-not $readOnly) { Ensure-Directory $RuntimeDir; Ensure-Directory $LogDir }
|
|
1374
1508
|
switch ($Name) {
|
|
1375
1509
|
'start' { Start-Deadbyte $cfg | Out-Null; if ([bool]$cfg.AutonomousSupervisorEnabled) { Start-AutonomousSupervisor $cfg | Out-Null }; Show-State $cfg | Out-Null }
|
|
1376
1510
|
'resume' { Start-Deadbyte $cfg | Out-Null; if ([bool]$cfg.AutonomousSupervisorEnabled) { Start-AutonomousSupervisor $cfg | Out-Null }; Show-State $cfg | Out-Null }
|
|
1377
1511
|
'park' { Park-Deadbyte $cfg | Out-Null; Show-State $cfg | Out-Null }
|
|
1378
1512
|
'stop' { if ([bool]$cfg.AutonomousSupervisorEnabled) { Stop-AutonomousSupervisor $cfg | Out-Null }; Stop-Deadbyte $cfg | Out-Null; Show-State $cfg | Out-Null }
|
|
1379
1513
|
'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 }
|
|
1514
|
+
'status' { if ($Json) { Get-StructuredObservation $cfg | ConvertTo-Json -Depth 6 -Compress | Write-Output } else { Show-State $cfg | Out-Null } }
|
|
1381
1515
|
'url' { $u=Get-CurrentUrl $cfg; if (-not $u) { Fail 'URL unavailable' }; Write-Output $u }
|
|
1382
1516
|
'test' { Invoke-Smoke $cfg $false }
|
|
1383
1517
|
'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.36-r59\\controller\\runtime\\process-grant.json",
|
|
5
|
+
"state_dir": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.36-r59\\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.36-r59\\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.36-r59",
|
|
13
13
|
"deny_paths": [
|
|
14
14
|
"node_modules",
|
|
15
15
|
"controller/runtime",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# R59 Task Contract
|
|
2
|
+
|
|
3
|
+
- TASK_ID: deadbyte-r59-robust-runtime
|
|
4
|
+
- OBJECTIVE: Produce backward-compatible DEADBYTE MCP 0.15.36 with fault-isolated optional planes, live capability snapshots, resilient Bridge/Core lifecycle, and verified direct-cloudflared frontdoor operation.
|
|
5
|
+
- ACTIVE_SKILL: implement-controlled-software-changes; final verification uses verify-production-implementation without repair authority.
|
|
6
|
+
- LEAD_ROLE: runtime implementation
|
|
7
|
+
- REVIEW_ROLES: independent release verifier after the candidate is frozen
|
|
8
|
+
- SOURCES: owner-approved 0.15.35 to 0.15.36 plan; exact 0.15.35 tarball SHA-256 `8259e30946d28dd138c1e2ce2f483fe1370d1658544f2adbfebafa4db8875bba`; sealed parent manifest `1b5715b276facbc00f8e341a554472cb4829e5af60e221c357c41b66053af9d4`
|
|
9
|
+
- FIXED: existing public tool names/counts/input contracts; authority planes; no Wrangler; no generic shell/PID/executable/stdin authority; no mutation of sealed 0.15.35 bytes
|
|
10
|
+
- TARGETS: Core initialization, capability resolver/manifest, bundled Bridge/governor, controller diagnostics, frontdoor verification and release evidence
|
|
11
|
+
- BOUNDS: optional configured planes may degrade independently; signed ledger remains the authority ceiling; no transparent replay after dispatch
|
|
12
|
+
- CLASS_2_DISCRETION: bounded retry/backoff/TTL values inside owner-approved limits, reversible and test-bound
|
|
13
|
+
- CLASS_3_DISCRETION: internal module boundaries, helper names and test organization
|
|
14
|
+
- PROHIBITED: dynamic plugin loading; authority widening; Host/Desktop auto-arm; in-place production edits; Wrangler transport
|
|
15
|
+
- METHOD_LOCK: direct cloudflared frontdoor; state-machine Bridge/Core lifecycle; signed static capability ceiling with live reducing overlay; status METHOD_CLOSED by owner instruction
|
|
16
|
+
- PRODUCTION_LINEAGE: source parent 0.15.35 tarball; delivery target npm `deadbyte-mcp@0.15.36`; runtime representation signed release/generation slot
|
|
17
|
+
- DEPENDENCIES: Node.js/PowerShell/MSVC already pinned by the parent; no new dependencies
|
|
18
|
+
- CAPABILITY_CONTEXT: local Windows host with D: isolated test state; npm and production activation only after deterministic gates
|
|
19
|
+
- ASSURANCE_MODE: HIGH_RISK
|
|
20
|
+
- TESTS: focused RED/GREEN, full regression, Windows gate, isolated acceptance, four-hour soak, registry round-trip, activation/rollback/restore
|
|
21
|
+
- ACCEPTANCE_ORACLES: actual MCP protocol/catalog, Ed25519 ping verification, OS process identity, protected hashes, npm registry bytes and independent receipt verifier
|
|
22
|
+
- ACCEPTANCE_ENVIRONMENT: current production Windows host with isolated D: state; clean-machine proof is not claimed
|
|
23
|
+
- PASS_CONDITION: every mandatory gate passes with fresh evidence and final authority is disarmed
|
|
24
|
+
- STOP_CONDITION: protected parent bytes drift, authority widens, unknown mutation is replayed, mandatory gate fails, or credentials/human authentication are required
|
|
25
|
+
- ROLLBACK: exact signed 0.15.35 pointer plus owned compatibility junction
|
|
26
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deadbyte-mcp",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.36",
|
|
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-r59-prepublish.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",
|
|
@@ -79,7 +79,11 @@
|
|
|
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
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"
|
|
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",
|
|
85
|
+
"r59:focused:test": "node --test --test-concurrency=1 test/capability-snapshot-r59.test.mjs test/capability-resolver.test.mjs test/runtime-plane-registry-r59.test.mjs test/optional-plane-degraded-r59.test.mjs test/bridge-runtime-r59.test.mjs test/controller-policy-preflight-r59.test.mjs test/r59-tool-surface-compat.test.mjs test/r58-controller-state-coherence.test.mjs",
|
|
86
|
+
"r59:release:test": "node --test --test-concurrency=1 test/r59-release-contract.test.mjs test/r59-tool-surface-compat.test.mjs test/bridge-runtime-r59.test.mjs test/controller-policy-preflight-r59.test.mjs"
|
|
83
87
|
},
|
|
84
88
|
"bundleDependencies": [
|
|
85
89
|
"@modelcontextprotocol/server",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "deadbyte.r59-full-regression-capture.v1",
|
|
3
|
+
"started_at_utc": "2026-10-02T18:38:13.917Z",
|
|
4
|
+
"finished_at_utc": "2026-10-02T18:43:52.171Z",
|
|
5
|
+
"exit_code": 0,
|
|
6
|
+
"signal": null,
|
|
7
|
+
"error": null,
|
|
8
|
+
"stdout_sha256": "36848d4d222085e1c7099ceb77261b91eb5daf42e540b5878c2e58024c81cf66",
|
|
9
|
+
"stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
10
|
+
"stdout_bytes": 113612,
|
|
11
|
+
"stderr_bytes": 0,
|
|
12
|
+
"pass": true
|
|
13
|
+
}
|
|
File without changes
|