deadbyte-mcp 0.11.2 → 0.12.0
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 +101 -39
- package/README.txt +10 -9
- package/acceptance/r40/desktop-live-drift.mjs +106 -0
- package/acceptance/r40/desktop-live-ground.mjs +110 -0
- package/acceptance/r40/desktop-live-input.mjs +128 -0
- package/acceptance/r40/desktop-live-observe.mjs +100 -0
- package/acceptance/r40/desktop-live-uia.mjs +121 -0
- package/bin/deadbyte-desktop-input-r40.exe +0 -0
- package/bin/deadbyte-desktop-input-r40.obj +0 -0
- package/bin/deadbyte-desktop-observer-r40.exe +0 -0
- package/bin/deadbyte-desktop-observer-r40.obj +0 -0
- package/bin/deadbyte-desktop-uia-r40.exe +0 -0
- package/bin/deadbyte-desktop-uia-r40.obj +0 -0
- package/controller/README.TXT +7 -5
- package/controller/deadbyte-controller.config.psd1.example +3 -0
- package/controller/deadbyte-controller.ps1 +149 -4
- package/controller/deadbyte-desktop-policy.json +41 -0
- package/docs/ARCHITECTURE.md +6 -1
- package/docs/superpowers/specs/2026-09-19-r40-desktop-computer-use-design.md +214 -0
- package/native/deadbyte-desktop-input.cpp +455 -0
- package/native/deadbyte-desktop-observer.cpp +253 -0
- package/native/deadbyte-desktop-uia.cpp +307 -0
- package/package.json +10 -8
- package/scripts/build-desktop-r40.ps1 +106 -0
- package/scripts/deferred-slot-operation-r39.mjs +124 -0
- package/scripts/deferred-slot-operation-r40.mjs +126 -0
- package/scripts/final-closure-r39.mjs +113 -0
- package/scripts/final-closure-r40.mjs +114 -0
- package/scripts/final-closure-verify-r39.mjs +208 -0
- package/scripts/final-closure-verify-r40.mjs +212 -0
- package/scripts/gate-windows-r39.ps1 +42 -0
- package/scripts/gate-windows-r40.ps1 +43 -0
- package/scripts/init-desktop-policy-r40.ps1 +79 -0
- package/scripts/r40-compact-ledger-probe.mjs +4 -0
- package/scripts/release-parity-r39.mjs +200 -0
- package/scripts/release-parity-r40.mjs +212 -0
- package/scripts/release-parity-tests-r39.ps1 +9 -0
- package/scripts/release-parity-tests-r40.ps1 +9 -0
- package/scripts/windows-gate-evidence-r39.mjs +52 -0
- package/scripts/windows-gate-evidence-r40.mjs +52 -0
- package/src/autonomous-context-engine.mjs +2 -1
- package/src/autonomous-planner-contracts.mjs +19 -1
- package/src/autonomous-planner.mjs +22 -0
- package/src/autonomous-runtime.mjs +144 -24
- package/src/capability-ledger.mjs +6 -1
- package/src/compact-gateway-mcp-tools.mjs +14 -2
- package/src/deadbyte-cli.mjs +79 -47
- package/src/desktop-contract.mjs +263 -0
- package/src/desktop-input-r40.mjs +148 -0
- package/src/desktop-mcp-tools-r40.mjs +126 -0
- package/src/desktop-memory-r40.mjs +272 -0
- package/src/desktop-observer-r40.mjs +231 -0
- package/src/desktop-policy-r40.mjs +110 -0
- package/src/desktop-runtime-r40.mjs +318 -0
- package/src/desktop-uia-r40.mjs +123 -0
- package/src/mcp-server.mjs +74 -19
- package/src/r40-compact-disarmed-probe.mjs +71 -0
- package/src/r40-full-surface-probe.mjs +34 -0
- package/src/release-generation.mjs +38 -5
- package/src/remote-console.mjs +41 -0
- package/src/remote-live-log.mjs +215 -45
- package/src/remote-mcp-desktop-r40-smoke-client.mjs +70 -0
- package/src/remote-mcp-r40-parity-client.mjs +200 -0
- package/src/remote-mcp-smoke-client.mjs +20 -6
- package/src/remote-result-journal.mjs +10 -2
- package/src/runtime-update.mjs +55 -17
- package/src/tool-audit.mjs +29 -2
- package/src/version.mjs +1 -1
- package/test/autonomous-capability-r36.test.mjs +43 -1
- package/test/autonomous-desktop-r40.test.mjs +188 -0
- package/test/capability-ledger-r36.test.mjs +1 -0
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/compact-catalog-r36.test.mjs +2 -2
- package/test/compact-gateway-r36.test.mjs +56 -4
- package/test/compact-max-catalog-r40.test.mjs +95 -0
- package/test/controller-desktop-r40.test.mjs +85 -0
- package/test/core.test.mjs +1 -1
- package/test/desktop-adversarial-r40.test.mjs +62 -0
- package/test/desktop-contract-r40.test.mjs +200 -0
- package/test/desktop-input-r40.test.mjs +63 -0
- package/test/desktop-mcp-tools-r40.test.mjs +61 -0
- package/test/desktop-memory-r40.test.mjs +141 -0
- package/test/desktop-observer-r40.test.mjs +119 -0
- package/test/desktop-policy-r40.test.mjs +111 -0
- package/test/desktop-runtime-r40.test.mjs +278 -0
- package/test/desktop-uia-r40.test.mjs +42 -0
- package/test/full-max-catalog-r40.test.mjs +91 -0
- package/test/helpers/autonomous-r34-fixture.mjs +3 -3
- package/test/production-docs.test.mjs +3 -3
- package/test/r33-closeout-regression.test.mjs +8 -8
- package/test/r33-finalization.test.mjs +14 -14
- package/test/r37-remote-tool-call-timeline.test.mjs +7 -6
- package/test/r39-finalization.test.mjs +74 -0
- package/test/r40-finalization.test.mjs +118 -0
- package/test/release-generation.test.mjs +41 -9
- package/test/release-version.test.mjs +19 -58
- package/test/remote-console-r40.test.mjs +38 -0
- package/test/remote-live-log-r37.test.mjs +89 -32
- package/test/remote-r24-catalog.test.mjs +12 -3
- package/test/remote-result-journal.test.mjs +15 -0
- package/test/remote-session-cli.test.mjs +112 -55
- package/test/runtime-self-update.test.mjs +44 -0
package/src/remote-live-log.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { canonicalJson } from './canonical-json.mjs';
|
|
2
2
|
import { sanitizeAuditArgs } from './tool-audit.mjs';
|
|
3
|
+
import { remoteDisplayText, REMOTE_DISPLAY_MODES } from './remote-console.mjs';
|
|
3
4
|
|
|
4
5
|
const SAFE_ARG_KEYS=Object.freeze(['root_id','path','pattern','command_id','profile_id','session_id','goal_id','task_id','step_id','mode']);
|
|
5
6
|
const LIFECYCLE=Object.freeze({
|
|
@@ -11,6 +12,15 @@ const LIFECYCLE=Object.freeze({
|
|
|
11
12
|
ready:'✅ [READY] DEADBYTE MCP online',
|
|
12
13
|
cleanup:'🔐 [AUTH] Revoking session authority...'
|
|
13
14
|
});
|
|
15
|
+
const LIFECYCLE_ASCII=Object.freeze({
|
|
16
|
+
device_start:'[DEVICE] Starting DEADBYTE MCP...',
|
|
17
|
+
bridge_connected:'[BRIDGE] Local bridge connected',
|
|
18
|
+
cloud_connected:'[CLOUD] Remote MCP connected',
|
|
19
|
+
session_new:'[SESSION] New foreground session',
|
|
20
|
+
session_restored:'[SESSION] Previous result stream replayed',
|
|
21
|
+
ready:'[READY] DEADBYTE MCP online',
|
|
22
|
+
cleanup:'[AUTH] Revoking session authority...'
|
|
23
|
+
});
|
|
14
24
|
|
|
15
25
|
function safeAtom(value){
|
|
16
26
|
if(typeof value==='string'){
|
|
@@ -34,22 +44,99 @@ function summarizeArgs(args){
|
|
|
34
44
|
}
|
|
35
45
|
|
|
36
46
|
const TOOL_COLUMN_WIDTH=32;
|
|
47
|
+
const DISPLAY_UTC_OFFSET_MINUTES=7*60;
|
|
48
|
+
const DISPLAY_UTC_OFFSET_LABEL='+07:00';
|
|
49
|
+
const HEARTBEAT_TOOLS=new Set(['machine_ping','machine_status','machine_system_status','machine_list']);
|
|
50
|
+
const TOOL_ICONS=Object.freeze({read:'📖',write:'📝',edit:'✏️',run:'🏃',verify:'🧾',search:'🔍',heartbeat:'📡',desktop:'🖥️',autonomous:'🤖',process:'⚙️',generic:'•'});
|
|
51
|
+
const TOOL_ICONS_ASCII=Object.freeze({read:'R',write:'W',edit:'E',run:'>',verify:'V',search:'S',heartbeat:'H',desktop:'D',autonomous:'A',process:'P',generic:'*'});
|
|
52
|
+
const START_VISIBLE_PATTERNS=[/(?:^|_)file_write$/,/text_replace$/,/symbol_replace$/,/native_diff_(?:apply|preview)$/,
|
|
53
|
+
/(?:command|benchmark)_run$/,/task_run$/,/goal_run$/,/goal_submit$/,/goal_approve$/,/process_session_(?:start|action|terminate)$/,/^desktop_action$/];
|
|
37
54
|
|
|
38
|
-
function
|
|
39
|
-
|
|
55
|
+
export function truncateUtf8(value,maxBytes,{ellipsis='…'}={}){
|
|
56
|
+
if(typeof value!=='string'||!Number.isInteger(maxBytes)||maxBytes<1) return '';
|
|
57
|
+
const bytes=new TextEncoder().encode(value);
|
|
58
|
+
if(bytes.length<=maxBytes) return value;
|
|
59
|
+
const suffix=new TextEncoder().encode(ellipsis);
|
|
60
|
+
const budget=Math.max(0,maxBytes-suffix.length);
|
|
61
|
+
let end=Math.min(budget,bytes.length);
|
|
62
|
+
while(end>0&&(bytes[end]&0xc0)===0x80) end--;
|
|
63
|
+
return new TextDecoder('utf-8',{fatal:false}).decode(bytes.subarray(0,end))+(suffix.length<=maxBytes?ellipsis:'');
|
|
64
|
+
}
|
|
65
|
+
function clipped(value,max=72){
|
|
66
|
+
if(typeof value!=='string') return '';
|
|
67
|
+
const one=value.replace(/[\r\n\t]+/g,' ').replace(/\s{2,}/g,' ').trim();
|
|
68
|
+
return truncateUtf8(one,max);
|
|
69
|
+
}
|
|
70
|
+
function shortValue(value,max=12){
|
|
71
|
+
if(typeof value!=='string'||!value) return '';
|
|
72
|
+
return value.length<=max?value:value.slice(0,max)+'…';
|
|
73
|
+
}
|
|
74
|
+
function payloadByteCount(value){
|
|
75
|
+
return value&&typeof value==='object'&&value.redacted===true&&Number.isFinite(Number(value.byte_count))?Number(value.byte_count):null;
|
|
76
|
+
}
|
|
77
|
+
function rootPath(args){
|
|
78
|
+
const root=safeAtom(args?.root_id);const p=safeAtom(args?.path);
|
|
79
|
+
if(root&&p) return root+':'+p;
|
|
80
|
+
return root||p||'';
|
|
81
|
+
}
|
|
82
|
+
export function summarizeLiveToolArgs(tool,rawArgs){
|
|
83
|
+
const args=sanitizeAuditArgs(rawArgs??{});const where=rootPath(args);
|
|
84
|
+
if(/(?:file_read|files_read)$/.test(tool)) return where;
|
|
85
|
+
if(/file_write$/.test(tool)){const bytes=payloadByteCount(args.content);return [where,bytes!==null?'('+bytes+'B)':''].filter(Boolean).join(' ');}
|
|
86
|
+
if(/text_replace$|symbol_replace$/.test(tool)){const bytes=payloadByteCount(args.new_string);return [where,bytes!==null?'('+bytes+'B new)':''].filter(Boolean).join(' ');}
|
|
87
|
+
if(/coding_tree$/.test(tool)) return where;
|
|
88
|
+
if(/coding_(?:search|inspect)$/.test(tool)) return [where,args.pattern?'\"'+clipped(String(args.pattern),36)+'\"':'',args.op?String(args.op):''].filter(Boolean).join(' ');
|
|
89
|
+
if(/(?:command|benchmark)_run$/.test(tool)) return [safeAtom(args.root_id),safeAtom(args.command_id),safeAtom(args.script_path)].filter(Boolean).join(' ');
|
|
90
|
+
if(/^machine_(?:ping|status|system_status|list)$/.test(tool)) return safeAtom(args.nonce)?'nonce='+shortValue(String(args.nonce),24):'';
|
|
91
|
+
if(/^process_session/.test(tool)) return [safeAtom(args.profile_id),shortValue(safeAtom(args.session_id)||'',12),safeAtom(args.op)].filter(Boolean).join(' ');
|
|
92
|
+
if(/^desktop_/.test(tool)) return [shortValue(safeAtom(args.frame_id)||'',10),shortValue(safeAtom(args.target_id)||'',10),safeAtom(args.op)].filter(Boolean).join(' ');
|
|
93
|
+
if(/^autonomous_/.test(tool)) return [shortValue(safeAtom(args.goal_id)||'',12),safeAtom(args.step_id)].filter(Boolean).join(' ');
|
|
94
|
+
return summarizeArgs(args).trim();
|
|
95
|
+
}
|
|
96
|
+
function categoryForTool(tool){
|
|
97
|
+
if(/^desktop_/.test(tool)) return 'desktop';
|
|
98
|
+
if(/^autonomous_/.test(tool)) return 'autonomous';
|
|
99
|
+
if(HEARTBEAT_TOOLS.has(tool)) return 'heartbeat';
|
|
100
|
+
if(/(?:receipt_verify|evidence_verify|goal_verify|task_verify|audit|release_gate|native_diff_verify)/.test(tool)) return 'verify';
|
|
101
|
+
if(/(?:search|inspect|symbol_(?:definition|references|implementations|outline|diagnostics)|_stat$|_tree$)/.test(tool)) return 'search';
|
|
102
|
+
if(/(?:file_read|files_read|machine_fs_read|process_session_read)/.test(tool)) return 'read';
|
|
103
|
+
if(/(?:file_write|machine_fs_write|mkdir|move|delete)/.test(tool)) return 'write';
|
|
104
|
+
if(/(?:replace|edit_exact|native_diff_apply)/.test(tool)) return 'edit';
|
|
105
|
+
if(/(?:command_run|benchmark_run|task_run|goal_run|process_session_(?:start|action|terminate))/.test(tool)) return 'run';
|
|
106
|
+
if(/^process_/.test(tool)) return 'process';
|
|
107
|
+
return 'generic';
|
|
108
|
+
}
|
|
109
|
+
function iconForTool(tool,displayMode='unicode'){
|
|
110
|
+
const icons=displayMode==='ascii'?TOOL_ICONS_ASCII:TOOL_ICONS;
|
|
111
|
+
return icons[categoryForTool(tool)]??icons.generic;
|
|
112
|
+
}
|
|
113
|
+
function visibleStart(tool){return START_VISIBLE_PATTERNS.some(pattern=>pattern.test(tool));}
|
|
114
|
+
|
|
115
|
+
function displayTimestamp(ms){
|
|
116
|
+
const shifted=new Date(ms+DISPLAY_UTC_OFFSET_MINUTES*60*1000).toISOString();
|
|
117
|
+
return `${shifted.slice(0,-1)}${DISPLAY_UTC_OFFSET_LABEL}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function eventMs(event,clock){
|
|
121
|
+
for(const value of [event?.ts,event?.created_at_utc,event?.observed_at_utc]){
|
|
40
122
|
const parsed=Date.parse(value);
|
|
41
|
-
if(typeof value==='string'&&Number.isFinite(parsed)) return
|
|
123
|
+
if(typeof value==='string'&&Number.isFinite(parsed)) return parsed;
|
|
42
124
|
}
|
|
43
125
|
const value=Number(clock());
|
|
44
|
-
return
|
|
126
|
+
return Number.isFinite(value)?value:Date.now();
|
|
127
|
+
}
|
|
128
|
+
function eventTimestamp(event,clock){return displayTimestamp(eventMs(event,clock));}
|
|
129
|
+
function displayClock(ms){
|
|
130
|
+
const shifted=new Date(ms+DISPLAY_UTC_OFFSET_MINUTES*60*1000).toISOString();
|
|
131
|
+
return shifted.slice(11,23);
|
|
45
132
|
}
|
|
46
133
|
|
|
47
|
-
export function formatRemoteToolCall(event,{clock=()=>Date.now()}={}){
|
|
134
|
+
export function formatRemoteToolCall(event,{clock=()=>Date.now(),displayMode='unicode'}={}){
|
|
48
135
|
if(!event||typeof event!=='object'||Array.isArray(event)||event.event_type!=='tool.request'||
|
|
49
136
|
typeof event.tool!=='string'||!event.tool) return null;
|
|
50
137
|
if(typeof clock!=='function') throw new Error('remote tool-call clock must be a function');
|
|
51
138
|
const args=sanitizeAuditArgs(event.args??{});
|
|
52
|
-
return `${eventTimestamp(event,clock)} | ${event.tool.padEnd(TOOL_COLUMN_WIDTH)} | Arguments: ${canonicalJson(args)}
|
|
139
|
+
return remoteDisplayText(`${eventTimestamp(event,clock)} | ${event.tool.padEnd(TOOL_COLUMN_WIDTH)} | Arguments: ${canonicalJson(args)}`,displayMode);
|
|
53
140
|
}
|
|
54
141
|
|
|
55
142
|
function formatDuration(ms){
|
|
@@ -70,9 +157,10 @@ export function formatCompactAuditEvent(event){
|
|
|
70
157
|
return null;
|
|
71
158
|
}
|
|
72
159
|
|
|
73
|
-
export function formatRemoteLifecycle(kind){
|
|
160
|
+
export function formatRemoteLifecycle(kind,{displayMode='unicode'}={}){
|
|
74
161
|
if(typeof kind!=='string'||!Object.hasOwn(LIFECYCLE,kind)) throw new Error(`unknown remote lifecycle '${String(kind)}'`);
|
|
75
|
-
|
|
162
|
+
if(!REMOTE_DISPLAY_MODES.includes(displayMode)) throw new Error('remote display mode invalid');
|
|
163
|
+
return displayMode==='ascii'?LIFECYCLE_ASCII[kind]:LIFECYCLE[kind];
|
|
76
164
|
}
|
|
77
165
|
|
|
78
166
|
export function createLiveActivityTracker(){
|
|
@@ -102,56 +190,138 @@ function shortSpanId(requestId){
|
|
|
102
190
|
return typeof requestId==='string'&&requestId.length?requestId.slice(0,8):'????????';
|
|
103
191
|
}
|
|
104
192
|
|
|
105
|
-
export function createLiveTimelineTracker({clock=()=>Date.now()}={}){
|
|
193
|
+
export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,heartbeatBurstLimit=10,heartbeatWindowMs=3000,displayMode='unicode'}={}){
|
|
106
194
|
if(typeof clock!=='function') throw new Error('live timeline clock must be a function');
|
|
195
|
+
if(!REMOTE_DISPLAY_MODES.includes(displayMode)) throw new Error('remote display mode invalid');
|
|
196
|
+
if(!Number.isInteger(heartbeatBurstLimit)||heartbeatBurstLimit<2||heartbeatBurstLimit>100) throw new Error('heartbeatBurstLimit invalid');
|
|
197
|
+
if(!Number.isInteger(heartbeatWindowMs)||heartbeatWindowMs<250||heartbeatWindowMs>60000) throw new Error('heartbeatWindowMs invalid');
|
|
107
198
|
const pending=new Map();
|
|
108
199
|
const completed=new Set();
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
200
|
+
const toolCounts=new Map();
|
|
201
|
+
let resultCount=0,errorCount=0,totalDurationMs=0,burst=null,foldedHeartbeatLines=0;
|
|
202
|
+
|
|
203
|
+
function eventPrefix(event,sequence){
|
|
204
|
+
const seq=Number.isSafeInteger(sequence)&&sequence>0?'#'+sequence+' ':'';
|
|
205
|
+
return iconForTool(event.tool,displayMode)+' '+displayClock(eventMs(event,clock))+' '+seq+'['+shortSpanId(event.request_id)+'] '+event.tool;
|
|
206
|
+
}
|
|
207
|
+
function resultLine(event,span,sequence){
|
|
208
|
+
const error=event.outcome?.is_error===true;
|
|
209
|
+
const unknown=event.outcome?.side_effect_state==='unknown';
|
|
210
|
+
const duration=Number.isFinite(Number(event.outcome?.duration_ms))
|
|
211
|
+
?formatDuration(event.outcome.duration_ms)
|
|
212
|
+
:span?formatDuration(Math.max(0,clock()-span.started_at_ms)):'';
|
|
213
|
+
const summary=span?.summary||'';
|
|
214
|
+
const rawReason=typeof event.outcome?.reason==='string'&&event.outcome.reason?event.outcome.reason:
|
|
215
|
+
(unknown&&typeof event.outcome?.recovery?.reason==='string'?event.outcome.recovery.reason:'');
|
|
216
|
+
const reason=rawReason?clipped(rawReason,180):(unknown?'terminal result unavailable; inspect state before retry':'');
|
|
217
|
+
const marker=unknown?'OUTCOME_UNKNOWN':error?(displayMode==='ascii'?'ERR':'✗'):(displayMode==='ascii'?'OK':'✓');
|
|
218
|
+
return remoteDisplayText(eventPrefix(event,sequence)+(summary?' '+summary:'')+(duration?' '+duration:'')+' '+marker+(reason?' — '+reason:''),displayMode);
|
|
219
|
+
}
|
|
220
|
+
function burstLine(value){
|
|
221
|
+
if(!value) return null;
|
|
222
|
+
const avg=value.count?value.total_ms/value.count:0;
|
|
223
|
+
const seq=Number.isSafeInteger(value.last_sequence)&&value.last_sequence>0?'#'+value.last_sequence+' ':'';
|
|
224
|
+
const startClock=displayClock(value.first_ms);
|
|
225
|
+
const endClock=displayClock(value.last_ms);
|
|
226
|
+
const range=value.first_ms===value.last_ms?endClock:startClock.slice(0,8)+'–'+endClock.slice(0,8);
|
|
227
|
+
if(value.count>1) foldedHeartbeatLines+=value.count-1;
|
|
228
|
+
const marker=displayMode==='ascii'?'OK':'✓';
|
|
229
|
+
return remoteDisplayText(iconForTool(value.tool,displayMode)+' '+range+' '+seq+value.tool+' ×'+value.count+' avg '+formatDuration(avg)+' '+marker,displayMode);
|
|
230
|
+
}
|
|
231
|
+
function flushBurst(){
|
|
232
|
+
const line=burstLine(burst);
|
|
233
|
+
burst=null;
|
|
234
|
+
return line;
|
|
235
|
+
}
|
|
236
|
+
function addStats(event){
|
|
237
|
+
resultCount++;
|
|
238
|
+
toolCounts.set(event.tool,(toolCounts.get(event.tool)??0)+1);
|
|
239
|
+
const duration=Number(event.outcome?.duration_ms);
|
|
240
|
+
if(Number.isFinite(duration)&&duration>=0) totalDurationMs+=duration;
|
|
241
|
+
if(event.outcome?.is_error===true) errorCount++;
|
|
242
|
+
}
|
|
243
|
+
function observe(event,{sequence=null}={}){
|
|
244
|
+
if(!event||typeof event!=='object'||Array.isArray(event)) return null;
|
|
245
|
+
const requestId=typeof event.request_id==='string'&&event.request_id?event.request_id:null;
|
|
246
|
+
const tool=typeof event.tool==='string'&&event.tool?event.tool:null;
|
|
247
|
+
if(event.event_type==='tool.request'&&requestId&&tool){
|
|
248
|
+
if(pending.has(requestId)||completed.has(requestId)) return null;
|
|
249
|
+
const summary=summarizeLiveToolArgs(tool,event.args??{});
|
|
250
|
+
pending.set(requestId,{request_id:requestId,tool,summary,started_at_ms:clock()});
|
|
251
|
+
if(verbose) return formatRemoteToolCall(event,{clock,displayMode});
|
|
252
|
+
if(visibleStart(tool)) return eventPrefix(event,sequence)+(summary?' '+summary:'')+' …';
|
|
132
253
|
return null;
|
|
133
|
-
},
|
|
134
|
-
pending(){return [...pending.values()].map(({request_id,tool})=>({request_id,tool}));},
|
|
135
|
-
reconcileInterrupted(reason='session_end'){
|
|
136
|
-
const suffix=reason==='recovery'?'recovery ended before terminal result':
|
|
137
|
-
reason==='session_end'?'session ended before terminal result':'stream ended before terminal result';
|
|
138
|
-
const lines=[...pending.values()].map(item=>`⚠ [${shortSpanId(item.request_id)}] INTERRUPTED ${item.tool} — ${suffix}`);
|
|
139
|
-
pending.clear();
|
|
140
|
-
return lines;
|
|
141
254
|
}
|
|
255
|
+
if(event.event_type==='tool.result'&&requestId&&tool){
|
|
256
|
+
if(completed.has(requestId)) return null;
|
|
257
|
+
const span=pending.get(requestId)??null;
|
|
258
|
+
pending.delete(requestId);
|
|
259
|
+
completed.add(requestId);
|
|
260
|
+
addStats(event);
|
|
261
|
+
const error=event.outcome?.is_error===true;
|
|
262
|
+
const nowMs=eventMs(event,clock);
|
|
263
|
+
if(HEARTBEAT_TOOLS.has(tool)&&!error){
|
|
264
|
+
const duration=Number(event.outcome?.duration_ms);
|
|
265
|
+
if(burst&&burst.tool===tool&&nowMs-burst.last_ms<=heartbeatWindowMs){
|
|
266
|
+
burst.count++;
|
|
267
|
+
burst.last_ms=nowMs;
|
|
268
|
+
burst.last_sequence=sequence;
|
|
269
|
+
if(Number.isFinite(duration)&&duration>=0) burst.total_ms+=duration;
|
|
270
|
+
}else{
|
|
271
|
+
const prior=flushBurst();
|
|
272
|
+
burst={tool,count:1,total_ms:Number.isFinite(duration)&&duration>=0?duration:0,
|
|
273
|
+
first_ms:nowMs,last_ms:nowMs,first_sequence:sequence,last_sequence:sequence};
|
|
274
|
+
return prior;
|
|
275
|
+
}
|
|
276
|
+
if(burst.count>=heartbeatBurstLimit) return flushBurst();
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
const lines=[];
|
|
280
|
+
const prior=flushBurst();
|
|
281
|
+
if(prior) lines.push(prior);
|
|
282
|
+
lines.push(resultLine(event,span,sequence));
|
|
283
|
+
return lines.length===1?lines[0]:lines;
|
|
284
|
+
}
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
function reconcileInterrupted(reason='session_end'){
|
|
288
|
+
const suffix=reason==='recovery'?'recovery ended before terminal result':
|
|
289
|
+
reason==='session_end'?'session ended before terminal result':'stream ended before terminal result';
|
|
290
|
+
const lines=[];
|
|
291
|
+
const folded=flushBurst();
|
|
292
|
+
if(folded) lines.push(folded);
|
|
293
|
+
for(const item of pending.values()) lines.push(remoteDisplayText((displayMode==='ascii'?'[WARN]':'⚠')+' ['+shortSpanId(item.request_id)+'] OUTCOME_UNKNOWN '+item.tool+' — '+suffix+'; inspect state before retry',displayMode));
|
|
294
|
+
pending.clear();
|
|
295
|
+
return lines;
|
|
296
|
+
}
|
|
297
|
+
function summary(){
|
|
298
|
+
const top=[...toolCounts.entries()].sort((a,b)=>b[1]-a[1]||a[0].localeCompare(b[0])).slice(0,3)
|
|
299
|
+
.map(([tool,count])=>tool+'×'+count).join(', ');
|
|
300
|
+
return remoteDisplayText((displayMode==='ascii'?'[SUMMARY]':'📊 [SUMMARY]')+' '+resultCount+' calls | '+errorCount+' error'+(errorCount===1?'':'s')+
|
|
301
|
+
' | tool time '+(formatDuration(totalDurationMs)||'0ms')+
|
|
302
|
+
(foldedHeartbeatLines?' | folded '+foldedHeartbeatLines+' heartbeat lines':'')+
|
|
303
|
+
(top?' | top: '+top:''),displayMode);
|
|
304
|
+
}
|
|
305
|
+
return Object.freeze({
|
|
306
|
+
observe,
|
|
307
|
+
pending(){return [...pending.values()].map(({request_id,tool})=>({request_id,tool}));},
|
|
308
|
+
reconcileInterrupted,
|
|
309
|
+
summary
|
|
142
310
|
});
|
|
143
311
|
}
|
|
144
312
|
|
|
145
|
-
|
|
146
|
-
export function formatRuntimeDiagnosticLine(file,line){
|
|
313
|
+
export function formatRuntimeDiagnosticLine(file,line,{verbose=false,displayMode='unicode'}={}){
|
|
147
314
|
if(typeof file!=='string'||typeof line!=='string'||!line) return null;
|
|
148
315
|
const label=file.split(/[\\/]/).at(-1).replace(/\.log$/i,'').toUpperCase();
|
|
149
316
|
const input=line.match(/\[TOOL IN \]\s+\S+\s+#\d+\s+([A-Za-z0-9_.-]+)/);
|
|
150
|
-
if(input) return
|
|
317
|
+
if(input) return verbose?remoteDisplayText('['+label+'.DEBUG] IN '+input[1],displayMode):null;
|
|
151
318
|
const output=line.match(/\[TOOL OUT\]\s+\S+\s+#\d+\s+([A-Za-z0-9_.-]+)/);
|
|
152
319
|
if(output){
|
|
153
|
-
if(
|
|
154
|
-
return
|
|
320
|
+
if(!verbose) return null;
|
|
321
|
+
if(/"is_error"\s*:\s*true/.test(line)) return remoteDisplayText('['+label+'.DEBUG] ERR '+output[1],displayMode);
|
|
322
|
+
return remoteDisplayText('['+label+'.DEBUG] OUT '+output[1],displayMode);
|
|
155
323
|
}
|
|
324
|
+
const compact=line.match(/^\[TOOL\]\s+(\S+)\s+#(\d+)\s+(✓|✗|OK|ERR)\s+([A-Za-z0-9_.-]+)\s+([^\s]+)(?:\s+—\s+(.+))?$/u);
|
|
325
|
+
if(compact) return verbose?remoteDisplayText('['+label+'.DEBUG] #'+compact[2]+' '+compact[3]+' '+compact[4]+' '+compact[5]+(compact[6]?' — '+compact[6]:''),displayMode):null;
|
|
156
326
|
return null;
|
|
157
327
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { Client,StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
|
|
7
|
+
import { loadDesktopPolicy } from './desktop-policy-r40.mjs';
|
|
8
|
+
import { verifyDesktopReceiptObject } from './desktop-runtime-r40.mjs';
|
|
9
|
+
import { loadPublicVerifyKey } from './trust.mjs';
|
|
10
|
+
|
|
11
|
+
const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
12
|
+
function gateway(result,label){
|
|
13
|
+
if(result.isError)throw new Error(label+' returned error: '+JSON.stringify(result.structuredContent??result.content));
|
|
14
|
+
const value=result.structuredContent;
|
|
15
|
+
if(!value||value.status!=='ok'||!value.result)throw new Error(label+' malformed');
|
|
16
|
+
return value.result;
|
|
17
|
+
}
|
|
18
|
+
function predispatchForegroundChanged(result){
|
|
19
|
+
if(result?.isError!==true)return false;
|
|
20
|
+
const value=result.structuredContent;
|
|
21
|
+
return value?.status==='denied'&&value?.op==='action'&&value?.error==='desktop foreground window changed before action';
|
|
22
|
+
}
|
|
23
|
+
async function verify(policy,key,id,sha,kind){
|
|
24
|
+
const receipt=JSON.parse(await readFile(path.join(policy.evidence_dir,id+'.json'),'utf8'));
|
|
25
|
+
const proof=verifyDesktopReceiptObject(receipt,{policySha256:policy.policy_sha256,publicKey:key.publicKey,keyId:key.keyId});
|
|
26
|
+
if(!proof.ok||proof.receipt_sha256!==sha||proof.kind!==kind)throw new Error('desktop smoke receipt mismatch '+kind);
|
|
27
|
+
}
|
|
28
|
+
export async function runR40DesktopGuardedSmoke(endpointText){
|
|
29
|
+
const endpoint=new URL(endpointText);if(endpoint.protocol!=='https:')throw new Error('R40 Desktop smoke requires HTTPS');
|
|
30
|
+
const pointer=JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
|
|
31
|
+
const policy=await loadDesktopPolicy(path.join(pointer.generation_root,'desktop-policy.json'));
|
|
32
|
+
const key=await loadPublicVerifyKey(path.join(stateRoot,'trust','ed25519-public.pem'));
|
|
33
|
+
const client=new Client({name:'deadbyte-r40-desktop-guarded-smoke',version:'0.12.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
|
|
34
|
+
const transport=new StreamableHTTPClientTransport(endpoint);
|
|
35
|
+
try{
|
|
36
|
+
await client.connect(transport);
|
|
37
|
+
const status=gateway(await client.callTool({name:'desktop_control',arguments:{op:'status'}}),'desktop status');
|
|
38
|
+
if(status.armed!==true||!status.action_kinds?.includes('key_chord'))throw new Error('Desktop smoke requires separately armed key_chord authority');
|
|
39
|
+
const maxPredispatchAttempts=3;
|
|
40
|
+
let action=null;
|
|
41
|
+
let observationReceiptsVerified=0;
|
|
42
|
+
let predispatchRetryCount=0;
|
|
43
|
+
for(let attempt=1;attempt<=maxPredispatchAttempts;attempt+=1){
|
|
44
|
+
const observed=gateway(await client.callTool({name:'desktop_control',arguments:{op:'observe',preview:false}}),'desktop observe');
|
|
45
|
+
await verify(policy,key,observed.receipt_id,observed.receipt_sha256,'desktop_observation');
|
|
46
|
+
observationReceiptsVerified+=1;
|
|
47
|
+
const actionResult=await client.callTool({name:'desktop_control',arguments:{
|
|
48
|
+
op:'action',intent_id:randomBytes(32).toString('hex'),kind:'key_chord',expected_frame_id:observed.frame.frame_id,
|
|
49
|
+
target_id:null,point:null,scroll_delta:null,key_chord:['SHIFT'],text:null
|
|
50
|
+
}});
|
|
51
|
+
if(predispatchForegroundChanged(actionResult)){
|
|
52
|
+
if(attempt===maxPredispatchAttempts)throw new Error('Desktop guarded smoke foreground remained unstable before dispatch');
|
|
53
|
+
predispatchRetryCount+=1;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
action=gateway(actionResult,'desktop key_chord');
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
if(!action||action.injected!==true||action.side_effect_state!=='confirmed_injected'||action.action_summary?.kind!=='key_chord')throw new Error('Desktop guarded smoke input not confirmed');
|
|
60
|
+
await verify(policy,key,action.started_receipt_id,action.started_receipt_sha256,'desktop_action_started');
|
|
61
|
+
await verify(policy,key,action.post_receipt_id,action.post_receipt_sha256,'desktop_post_observation');
|
|
62
|
+
await verify(policy,key,action.receipt_id,action.receipt_sha256,'desktop_action_completed');
|
|
63
|
+
return {status:'passed',kind:'key_chord',keys:['SHIFT'],native_injected:true,replay_performed:false,
|
|
64
|
+
predispatch_retry_count:predispatchRetryCount,observation_receipts_verified:observationReceiptsVerified,
|
|
65
|
+
observation_receipt_verified:true,action_receipts_verified:3,action_chain_sha256:action.action_chain?.chain_sha256??null,
|
|
66
|
+
effect_id:action.effect_id,receipt_id:action.receipt_id,receipt_sha256:action.receipt_sha256};
|
|
67
|
+
}finally{await client.close().catch(()=>{});}
|
|
68
|
+
}
|
|
69
|
+
if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){const endpoint=process.argv[2];if(!endpoint)throw new Error('usage: node src/remote-mcp-desktop-r40-smoke-client.mjs https://host/mcp/path');
|
|
70
|
+
runR40DesktopGuardedSmoke(endpoint).then(v=>console.log(JSON.stringify(v,null,2))).catch(e=>{console.error(e instanceof Error?e.stack:String(e));process.exitCode=1;});}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
|
|
7
|
+
import { R34_EXPECTED_TOOLS } from './remote-mcp-r34-parity-client.mjs';
|
|
8
|
+
import { loadCodingPolicy } from './coding-plane.mjs';
|
|
9
|
+
import { verifyMachineFsReceiptFile } from './machine-fs.mjs';
|
|
10
|
+
import { verifyObservationReceiptFile } from './observation-runtime.mjs';
|
|
11
|
+
import { loadDesktopPolicy } from './desktop-policy-r40.mjs';
|
|
12
|
+
import { verifyDesktopReceiptObject } from './desktop-runtime-r40.mjs';
|
|
13
|
+
import { loadPublicVerifyKey } from './trust.mjs';
|
|
14
|
+
|
|
15
|
+
export const R40_EXPECTED_TOOLS=Object.freeze([...new Set([
|
|
16
|
+
...R34_EXPECTED_TOOLS,
|
|
17
|
+
'coding_symbol_diagnostics','coding_symbol_implementations','coding_symbol_rename_preview',
|
|
18
|
+
'desktop_status','desktop_observe','desktop_target','desktop_action'
|
|
19
|
+
])].sort());
|
|
20
|
+
if(R40_EXPECTED_TOOLS.length !== 80) throw new Error(`R40 full catalog identity mismatch: ${R40_EXPECTED_TOOLS.length}`);
|
|
21
|
+
export const R40_COMPACT_GATEWAYS=Object.freeze(['capability_manifest','coding_inspect','machine_fs','machine_observe','process_session','desktop_control'].sort());
|
|
22
|
+
export const R40_COMPACT_TOOL_LIMIT=42;
|
|
23
|
+
|
|
24
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
const repoRoot=path.resolve(here,'..');
|
|
26
|
+
const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
27
|
+
const verifyKeyPath=path.join(stateRoot,'trust','ed25519-public.pem');
|
|
28
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
29
|
+
const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
|
|
30
|
+
|
|
31
|
+
function structured(result,label){
|
|
32
|
+
if(result.isError) throw new Error(`${label} returned error: ${JSON.stringify(result.structuredContent??result.content)}`);
|
|
33
|
+
if(!result.structuredContent||typeof result.structuredContent!=='object') throw new Error(`${label} missing structured output`);
|
|
34
|
+
return result.structuredContent;
|
|
35
|
+
}
|
|
36
|
+
function gateway(result,label){
|
|
37
|
+
const value=structured(result,label);
|
|
38
|
+
if(value.status!=='ok'||!value.result||typeof value.result!=='object') throw new Error(`${label} gateway status=${String(value.status)} error=${String(value.error??'')}`);
|
|
39
|
+
return value.result;
|
|
40
|
+
}
|
|
41
|
+
async function fileSha(relative){return sha256(await readFile(path.join(repoRoot,...relative.split('/'))));}
|
|
42
|
+
async function verifyMachineReceipt(policy,receiptId,operation){
|
|
43
|
+
const checked=await verifyMachineFsReceiptFile({policy,verifyKeyPath,receiptId});
|
|
44
|
+
if(!checked.ok||checked.signature_valid!==true||checked.operation!==operation) throw new Error(`machine-fs receipt verification failed for ${operation}`);
|
|
45
|
+
return checked;
|
|
46
|
+
}
|
|
47
|
+
async function verifyObservationReceipt(policy,receiptId){
|
|
48
|
+
const checked=await verifyObservationReceiptFile({policy,verifyKeyPath,receiptId});
|
|
49
|
+
if(!checked.ok||checked.signature_valid!==true) throw new Error('observation receipt verification failed');
|
|
50
|
+
return checked;
|
|
51
|
+
}
|
|
52
|
+
async function verifyDesktopReceipt(policy,verifyKey,receiptId,receiptSha256,kind){
|
|
53
|
+
const file=path.join(policy.evidence_dir,`${receiptId}.json`);
|
|
54
|
+
const receipt=JSON.parse(await readFile(file,'utf8'));
|
|
55
|
+
const checked=verifyDesktopReceiptObject(receipt,{policySha256:policy.policy_sha256,publicKey:verifyKey.publicKey,keyId:verifyKey.keyId});
|
|
56
|
+
if(!checked.ok||checked.receipt_sha256!==receiptSha256||checked.kind!==kind) throw new Error(`desktop receipt verification failed for ${kind}`);
|
|
57
|
+
return checked;
|
|
58
|
+
}
|
|
59
|
+
async function waitSearch(client,searchId){
|
|
60
|
+
for(let attempt=0;attempt<200;attempt+=1){
|
|
61
|
+
const status=gateway(await client.callTool({name:'machine_observe',arguments:{op:'search_status',search_id:searchId}}),'search_status');
|
|
62
|
+
if(['completed','cancelled','failed'].includes(status.state)) return status;
|
|
63
|
+
await sleep(25);
|
|
64
|
+
}
|
|
65
|
+
throw new Error('R40 compact search timeout');
|
|
66
|
+
}
|
|
67
|
+
async function waitProcessOutput(client,sessionId){
|
|
68
|
+
for(let attempt=0;attempt<100;attempt+=1){
|
|
69
|
+
const page=gateway(await client.callTool({name:'process_session',arguments:{op:'read',session_id:sessionId,offset:0,max_bytes:65536}}),'process read');
|
|
70
|
+
if(Buffer.from(page.data_base64??'','base64').toString('utf8').includes('DEADBYTE_ACTION_PING')) return page;
|
|
71
|
+
await sleep(50);
|
|
72
|
+
}
|
|
73
|
+
throw new Error('R40 compact process output timeout');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function runR40CompactParity(endpointText){
|
|
77
|
+
const endpoint=new URL(endpointText);
|
|
78
|
+
if(endpoint.protocol!=='https:') throw new Error('R40 compact parity requires HTTPS');
|
|
79
|
+
const pointer=JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
|
|
80
|
+
const policy=await loadCodingPolicy(path.join(pointer.generation_root,'coding-policy.json'));
|
|
81
|
+
const desktopPolicy=await loadDesktopPolicy(path.join(pointer.generation_root,'desktop-policy.json'));
|
|
82
|
+
const desktopVerifyKey=await loadPublicVerifyKey(verifyKeyPath);
|
|
83
|
+
const client=new Client({name:'deadbyte-r40-compact-parity',version:'0.12.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
|
|
84
|
+
const transport=new StreamableHTTPClientTransport(endpoint);
|
|
85
|
+
const tag=randomBytes(8).toString('hex');
|
|
86
|
+
const scratchRoot='parity_scratch';
|
|
87
|
+
const target=`.deadbyte-r40-parity-${tag}.txt`;
|
|
88
|
+
let processSession=null,targetPresent=false;
|
|
89
|
+
const machineReceipts=[],observationReceipts=[];
|
|
90
|
+
try{
|
|
91
|
+
await client.connect(transport);
|
|
92
|
+
const listed=await client.listTools();
|
|
93
|
+
const names=listed.tools.map(tool=>tool.name).sort();
|
|
94
|
+
if(names.length>R40_COMPACT_TOOL_LIMIT) throw new Error(`R40 compact catalog exceeds ${R40_COMPACT_TOOL_LIMIT}: ${names.length}`);
|
|
95
|
+
if(names.length!==R40_COMPACT_TOOL_LIMIT) throw new Error(`R40 compact catalog must be exactly ${R40_COMPACT_TOOL_LIMIT}, got ${names.length}`);
|
|
96
|
+
for(const name of R40_COMPACT_GATEWAYS) if(!names.includes(name)) throw new Error(`R40 compact gateway missing ${name}`);
|
|
97
|
+
for(const tool of listed.tools){
|
|
98
|
+
if(tool.outputSchema?.type!=='object'||tool.outputSchema?.additionalProperties!==false||!tool.outputSchema?.properties) throw new Error(`tool ${tool.name} missing strict output schema`);
|
|
99
|
+
}
|
|
100
|
+
const manifest=gateway(await client.callTool({name:'capability_manifest',arguments:{}}),'capability_manifest');
|
|
101
|
+
if(manifest.schema!=='deadbyte.capability-manifest.v2'||manifest.profile!=='compact'||!Array.isArray(manifest.capabilities)||!/^[0-9a-f]{64}$/.test(String(manifest.ledger_sha256??''))) throw new Error('R40 compact capability manifest malformed');
|
|
102
|
+
if(manifest.tool_count!==names.length||JSON.stringify(manifest.tool_catalog)!==JSON.stringify(names)||
|
|
103
|
+
manifest.catalog_sha256!==sha256(Buffer.from(JSON.stringify(names),'utf8'))) throw new Error('R40 compact manifest catalog binding mismatch');
|
|
104
|
+
const required=manifest.capabilities.filter(item=>item?.production===true&&item?.host_only!==true);
|
|
105
|
+
for(const item of required){
|
|
106
|
+
if(typeof item.compact_tool!=='string'||!names.includes(item.compact_tool)) throw new Error(`compact capability route missing: ${String(item.id)}`);
|
|
107
|
+
}
|
|
108
|
+
const coding=structured(await client.callTool({name:'coding_session_status',arguments:{}}),'coding_session_status');
|
|
109
|
+
const autonomous=structured(await client.callTool({name:'autonomous_session_status',arguments:{}}),'autonomous_session_status');
|
|
110
|
+
if(coding.armed!==true||autonomous.coding_armed!==true||autonomous.autonomous_armed!==true) throw new Error('R40 compact parity requires coding and autonomous standing grants');
|
|
111
|
+
const desktopStatus=gateway(await client.callTool({name:'desktop_control',arguments:{op:'status'}}),'desktop status');
|
|
112
|
+
if(desktopStatus.configured!==true||desktopStatus.armed!==false) throw new Error('R40 compact parity requires Desktop configured and DISARMED');
|
|
113
|
+
const desktopObserved=gateway(await client.callTool({name:'desktop_control',arguments:{op:'observe',preview:false}}),'desktop observe');
|
|
114
|
+
await verifyDesktopReceipt(desktopPolicy,desktopVerifyKey,desktopObserved.receipt_id,desktopObserved.receipt_sha256,'desktop_observation');
|
|
115
|
+
const desktopTarget=gateway(await client.callTool({name:'desktop_control',arguments:{op:'target',frame_id:desktopObserved.frame.frame_id,uia_query:{kind:'root',value:'',max_results:1,ordinal:0}}}),'desktop target');
|
|
116
|
+
await verifyDesktopReceipt(desktopPolicy,desktopVerifyKey,desktopTarget.grounding.receipt_id,desktopTarget.grounding.receipt_sha256,'desktop_grounding');
|
|
117
|
+
if(desktopTarget.target?.source!=='uia') throw new Error('R40 compact UIA root grounding missing');
|
|
118
|
+
const tree=structured(await client.callTool({name:'coding_tree',arguments:{root_id:'core',path:'workspaces/demo',max_depth:2,max_entries:128}}),'coding_tree');
|
|
119
|
+
if(tree.status!=='ok'||!Array.isArray(tree.entries)) throw new Error('coding_tree malformed');
|
|
120
|
+
const stat=gateway(await client.callTool({name:'coding_inspect',arguments:{op:'stat',root_id:'core',path:'package.json',hash:true}}),'coding stat');
|
|
121
|
+
if(stat.type!=='file'||!/^[0-9a-f]{64}$/.test(String(stat.sha256??''))||!/^[0-9a-f]{64}$/.test(String(stat.observation_sha256??''))) throw new Error('coding stat identity missing');
|
|
122
|
+
const search=gateway(await client.callTool({name:'coding_inspect',arguments:{op:'search',root_id:'core',path:'src',pattern:'R36_CAPABILITY_LEDGER',target:'content',mode:'literal',case_sensitive:true,max_results:20,max_file_bytes:262144,max_scan_bytes:4194304,page_size:20,cursor:null}}),'coding search');
|
|
123
|
+
if(!Array.isArray(search.matches)||search.matches.length<1) throw new Error('coding search returned no canonical ledger match');
|
|
124
|
+
|
|
125
|
+
const buildScript='scripts/autonomous-build.ps1';
|
|
126
|
+
const testScript='scripts/release-parity-tests-r40.ps1';
|
|
127
|
+
const build=structured(await client.callTool({name:'coding_command_run',arguments:{root_id:'core',command_id:'powershell_script',script_path:buildScript,script_sha256:await fileSha(buildScript),args:[],cwd:'.',timeout_ms:120000}},{timeout:180000,maxTotalTimeout:180000}),'coding build');
|
|
128
|
+
if(build.status!=='passed'||build.exit_code!==0) throw new Error('R40 coding build failed');
|
|
129
|
+
const buildVerify=structured(await client.callTool({name:'coding_receipt_verify',arguments:{receipt_id:build.receipt_id}}),'coding build receipt');
|
|
130
|
+
if(buildVerify.ok!==true||buildVerify.signature_valid!==true) throw new Error('R40 coding build receipt invalid');
|
|
131
|
+
const tests=structured(await client.callTool({name:'coding_command_run',arguments:{root_id:'core',command_id:'powershell_script',script_path:testScript,script_sha256:await fileSha(testScript),args:[],cwd:'.',timeout_ms:300000}},{timeout:360000,maxTotalTimeout:360000}),'coding tests');
|
|
132
|
+
if(tests.status!=='passed'||tests.exit_code!==0) throw new Error('R40 coding tests failed');
|
|
133
|
+
const testVerify=structured(await client.callTool({name:'coding_receipt_verify',arguments:{receipt_id:tests.receipt_id}}),'coding test receipt');
|
|
134
|
+
if(testVerify.ok!==true||testVerify.signature_valid!==true) throw new Error('R40 coding test receipt invalid');
|
|
135
|
+
|
|
136
|
+
const initial=`needle-${tag}\n`;
|
|
137
|
+
const write=gateway(await client.callTool({name:'machine_fs',arguments:{op:'write',root_id:scratchRoot,path:target,content:initial,encoding:'utf8',expected_sha256:'absent'}}),'machine_fs write');
|
|
138
|
+
targetPresent=true;machineReceipts.push(write.receipt_id);await verifyMachineReceipt(policy,write.receipt_id,'machine_fs_write');
|
|
139
|
+
const bounded=gateway(await client.callTool({name:'machine_fs',arguments:{op:'read',root_id:scratchRoot,path:target,offset:0,max_bytes:128,hash_file:true}}),'machine_fs read');
|
|
140
|
+
if(Buffer.from(bounded.data_base64,'base64').toString('utf8')!==initial||bounded.file_sha256!==write.sha256) throw new Error('R40 bounded machine read mismatch');
|
|
141
|
+
const edit=gateway(await client.callTool({name:'machine_fs',arguments:{op:'edit_exact',root_id:scratchRoot,path:target,expected_sha256:write.sha256,old_string:'needle',new_string:'beta',expected_replacements:1}}),'machine_fs edit');
|
|
142
|
+
machineReceipts.push(edit.receipt_id);await verifyMachineReceipt(policy,edit.receipt_id,'machine_fs_edit_exact');
|
|
143
|
+
const stale=await client.callTool({name:'machine_fs',arguments:{op:'edit_exact',root_id:scratchRoot,path:target,expected_sha256:write.sha256,old_string:'beta',new_string:'bad',expected_replacements:1}});
|
|
144
|
+
const staleValue=stale.structuredContent;
|
|
145
|
+
if(!staleValue||typeof staleValue!=='object') throw new Error('R40 stale machine-fs denial missing structured output');
|
|
146
|
+
if(stale.isError!==true||staleValue.status!=='denied'||!/precondition mismatch/i.test(String(staleValue.error??''))) {
|
|
147
|
+
throw new Error(`R40 stale machine-fs precondition was not denied: ${JSON.stringify(staleValue)}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const searchStart=gateway(await client.callTool({name:'machine_observe',arguments:{op:'search_start',root_id:scratchRoot,path:'.',pattern:tag,case_sensitive:true,max_results:20,max_files:200,max_bytes:262144,max_file_bytes:16384}}),'observe search start');
|
|
151
|
+
const searchStatus=await waitSearch(client,searchStart.search_id);
|
|
152
|
+
if(searchStatus.state!=='completed'||!searchStatus.receipt_id) throw new Error('R40 compact observation search failed');
|
|
153
|
+
observationReceipts.push(searchStatus.receipt_id);await verifyObservationReceipt(policy,searchStatus.receipt_id);
|
|
154
|
+
const page=gateway(await client.callTool({name:'machine_observe',arguments:{op:'search_page',search_id:searchStart.search_id,limit:20,cursor:null}}),'observe search page');
|
|
155
|
+
if(!page.results.some(item=>item.path===target)) throw new Error('R40 compact search did not return parity file');
|
|
156
|
+
const system=gateway(await client.callTool({name:'machine_observe',arguments:{op:'system_status'}}),'system status');
|
|
157
|
+
observationReceipts.push(system.receipt_id);await verifyObservationReceipt(policy,system.receipt_id);
|
|
158
|
+
const machineStatus=gateway(await client.callTool({name:'machine_observe',arguments:{op:'machine_status'}}),'machine status');
|
|
159
|
+
if(machineStatus.release_id!==pointer.release_id||machineStatus.release_manifest_sha256!==pointer.manifest_sha256) throw new Error('R40 compact machine status release mismatch');
|
|
160
|
+
|
|
161
|
+
const started=gateway(await client.callTool({name:'process_session',arguments:{op:'start',profile_id:'ping_hold',root_id:'core',cwd:'.',args:[]}}),'process start');
|
|
162
|
+
processSession=started.session_id??started.session?.session_id;
|
|
163
|
+
if(!/^[0-9a-f]{64}$/.test(String(processSession??''))) throw new Error('R40 compact process session id missing');
|
|
164
|
+
const action=gateway(await client.callTool({name:'process_session',arguments:{op:'action',session_id:processSession,action_id:'ping'}}),'process action');
|
|
165
|
+
const processRead=await waitProcessOutput(client,processSession);
|
|
166
|
+
const terminated=gateway(await client.callTool({name:'process_session',arguments:{op:'terminate',session_id:processSession}}),'process terminate');
|
|
167
|
+
const terminalState=terminated.state??terminated.session?.state;
|
|
168
|
+
if(action.action_id!=='ping'||terminalState!=='terminated') throw new Error('R40 compact process lifecycle failed');
|
|
169
|
+
|
|
170
|
+
const fsStat=gateway(await client.callTool({name:'machine_fs',arguments:{op:'stat',root_id:scratchRoot,path:target,hash:true}}),'machine_fs stat');
|
|
171
|
+
const deleted=gateway(await client.callTool({name:'machine_fs',arguments:{op:'delete',root_id:scratchRoot,path:target,expected_source_identity:fsStat.identity_sha256}}),'machine_fs delete');
|
|
172
|
+
machineReceipts.push(deleted.receipt_id);await verifyMachineReceipt(policy,deleted.receipt_id,'machine_fs_delete');targetPresent=false;
|
|
173
|
+
|
|
174
|
+
return {status:'passed',protocol_revision:client.getNegotiatedProtocolVersion(),profile:'compact',tool_count:names.length,tools:names,
|
|
175
|
+
catalog_sha256:sha256(Buffer.from(JSON.stringify(names),'utf8')),strict_output_schemas:true,ledger_sha256:manifest.ledger_sha256,
|
|
176
|
+
capability_count:manifest.capabilities.length,production_non_host_capabilities:required.length,capability_closure:true,
|
|
177
|
+
gateway_count:R40_COMPACT_GATEWAYS.length,tree_entries:tree.entries.length,coding_stat_verified:true,coding_search_result_count:search.matches.length,
|
|
178
|
+
desktop_armed:false,desktop_observation_receipt_verified:true,desktop_grounding_receipt_verified:true,
|
|
179
|
+
desktop_frame_id:desktopObserved.frame.frame_id,desktop_target_id:desktopTarget.target.target_id,
|
|
180
|
+
machine_fs_receipts_verified:machineReceipts.length,observation_receipts_verified:observationReceipts.length,
|
|
181
|
+
observation_search_result_count:page.results.length,process_session_id:processSession,process_output_bytes:processRead.stream_head,
|
|
182
|
+
process_journal_head_sha256:terminated.journal_head_sha256??terminated.session?.journal_head_sha256,
|
|
183
|
+
build_receipt_id:build.receipt_id,test_receipt_id:tests.receipt_id,coding_receipts_verified:2,
|
|
184
|
+
execution_provider:'deadbyte_mcp',rdc_operation_count:0};
|
|
185
|
+
} finally {
|
|
186
|
+
if(processSession) await client.callTool({name:'process_session',arguments:{op:'terminate',session_id:processSession}}).catch(()=>{});
|
|
187
|
+
if(targetPresent){
|
|
188
|
+
try{const fsStat=gateway(await client.callTool({name:'machine_fs',arguments:{op:'stat',root_id:scratchRoot,path:target,hash:false}}),'cleanup stat');
|
|
189
|
+
if(fsStat.identity_sha256) await client.callTool({name:'machine_fs',arguments:{op:'delete',root_id:scratchRoot,path:target,expected_source_identity:fsStat.identity_sha256}}).catch(()=>{});
|
|
190
|
+
}catch{}
|
|
191
|
+
}
|
|
192
|
+
await client.close().catch(()=>{});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){
|
|
197
|
+
const endpoint=process.argv[2];
|
|
198
|
+
if(!endpoint) throw new Error('usage: node src/remote-mcp-r40-parity-client.mjs https://host/mcp/path');
|
|
199
|
+
runR40CompactParity(endpoint).then(result=>console.log(JSON.stringify(result,null,2))).catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
200
|
+
}
|