deadbyte-mcp 0.15.21 → 0.15.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/MANIFEST.SHA256 +53 -28
  2. package/PROVENANCE.json +10 -10
  3. package/README.txt +18 -7
  4. package/RELEASE.json +51 -16
  5. package/SBOM.cdx.json +33 -7
  6. package/TEST-MATRIX.json +95 -18
  7. package/bin/deadbyte-hardware-observer-r45.exe +0 -0
  8. package/bin/deadbyte-hardware-observer-r45.obj +0 -0
  9. package/docs/R48-FULL-TOOL-CATALOG.json +124 -0
  10. package/native/deadbyte-hardware-observer.cpp +535 -12
  11. package/package.json +95 -93
  12. package/scripts/activate-skill-creator-1.1.1.mjs +46 -0
  13. package/scripts/build-advanced-pcb-skill.mjs +208 -0
  14. package/scripts/build-hardware-r45.ps1 +19 -0
  15. package/scripts/diagnose-skill-tree.mjs +8 -0
  16. package/scripts/full-regression-r48-batch.mjs +27 -0
  17. package/scripts/npm-release-r48.mjs +252 -0
  18. package/scripts/pcb-skill-src-SKILL.md.txt +67 -0
  19. package/scripts/pcb-skill-src-bringup-evidence.md.txt +22 -0
  20. package/scripts/pcb-skill-src-constraint-model.md.txt +17 -0
  21. package/scripts/pcb-skill-src-emc-mechanical-dfm.md.txt +17 -0
  22. package/scripts/pcb-skill-src-high-speed-layout.md.txt +27 -0
  23. package/scripts/pcb-skill-src-kicad-native.md.txt +30 -0
  24. package/scripts/pcb-skill-src-kicad_pcb_probe.py.txt +159 -0
  25. package/scripts/pcb-skill-src-pcb_constraint_gate.py.txt +76 -0
  26. package/scripts/pcb-skill-src-power-integrity.md.txt +15 -0
  27. package/scripts/prepare-skill-creator-1.1.1.mjs +113 -0
  28. package/scripts/promote-local-c-generation.ps1 +217 -0
  29. package/scripts/promote-local-d-generation.ps1 +217 -0
  30. package/scripts/test-r48-post-bump.ps1 +8 -0
  31. package/scripts/test-r48-windows-observation.ps1 +8 -0
  32. package/scripts/test-skill-creator-semver-matrix.mjs +30 -0
  33. package/scripts/test-skill-creator-semver.mjs +22 -0
  34. package/src/capability-ledger.mjs +7 -0
  35. package/src/capability-resolver.mjs +7 -0
  36. package/src/compact-gateway-mcp-tools.mjs +16 -2
  37. package/src/deadbyte-cli.mjs +70 -15
  38. package/src/machine-fs.mjs +16 -3
  39. package/src/observation-mcp-tools.mjs +13 -1
  40. package/src/observation-output-contracts.mjs +8 -0
  41. package/src/observation-runtime.mjs +54 -2
  42. package/src/remote-live-log.mjs +189 -35
  43. package/src/tool-audit.mjs +1 -1
  44. package/src/version.mjs +1 -1
  45. package/test/full-max-catalog-r47.test.mjs +1 -1
  46. package/test/full-max-catalog-r48.test.mjs +78 -0
  47. package/test/machine-fs-runtime.test.mjs +28 -3
  48. package/test/observation-mcp-tools.test.mjs +4 -3
  49. package/test/r48-release-contract.test.mjs +24 -14
  50. package/test/remote-live-log-r37.test.mjs +106 -7
  51. package/test/remote-session-cli.test.mjs +40 -0
  52. package/test/tool-audit.test.mjs +17 -0
  53. package/test/windows-observation-r48.test.mjs +63 -0
  54. package/test/windows-observation-surface-r48.test.mjs +70 -0
@@ -1,8 +1,9 @@
1
+ import path from 'node:path';
1
2
  import { canonicalJson } from './canonical-json.mjs';
2
3
  import { sanitizeAuditArgs } from './tool-audit.mjs';
3
4
  import { remoteDisplayText, REMOTE_DISPLAY_MODES } from './remote-console.mjs';
4
5
 
5
- const SAFE_ARG_KEYS=Object.freeze(['root_id','path','pattern','command_id','profile_id','session_id','goal_id','task_id','step_id','mode']);
6
+ const SAFE_ARG_KEYS=Object.freeze(['root_id','path','pattern','command_id','profile_id','session_id','goal_id','task_id','step_id','mode','op']);
6
7
  const LIFECYCLE=Object.freeze({
7
8
  device_start:'🚀 [DEVICE] Starting DEADBYTE MCP...',
8
9
  bridge_connected:'🔌 [BRIDGE] Local bridge connected',
@@ -74,19 +75,47 @@ function shortValue(value,max=12){
74
75
  function payloadByteCount(value){
75
76
  return value&&typeof value==='object'&&value.redacted===true&&Number.isFinite(Number(value.byte_count))?Number(value.byte_count):null;
76
77
  }
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(' ');
78
+ function safePathAtom(value){
79
+ if(typeof value!=='string'||value.length<1||value.length>2048||/[\r\n\0]/.test(value)) return null;
80
+ return value;
81
+ }
82
+ function rootedPath(args,key='path',rootPaths={}){
83
+ const root=safeAtom(args?.root_id);const p=safePathAtom(args?.[key]);
84
+ if(root&&p){
85
+ const base=safePathAtom(rootPaths?.[root]);
86
+ if(base){
87
+ const flavor=/^(?:[A-Za-z]:[\\/]|\\\\)/.test(base)?path.win32:path.posix;
88
+ const absolute=p==='.'?flavor.resolve(base):flavor.resolve(base,p);
89
+ return absolute+' [root='+root+']';
90
+ }
91
+ return root+':'+p;
92
+ }
93
+ if(root) return 'root='+root;
94
+ return p?'path='+p:'';
95
+ }
96
+ function displayToolName(tool,args){
97
+ const op=safeAtom(args?.op);
98
+ if(op&&['machine_fs','coding_inspect','process_session','desktop_control','machine_observe'].includes(tool)) return tool+'.'+op;
99
+ return tool;
100
+ }
101
+ export function summarizeLiveToolArgs(tool,rawArgs,{rootPaths={}}={}){
102
+ const args=sanitizeAuditArgs(rawArgs??{});const where=rootedPath(args,'path',rootPaths);
103
+ if(/(?:file_read|files_read)$/.test(tool)||/^machine_fs\.(?:read|stat|list)$/.test(tool)) return where;
104
+ if(/file_write$/.test(tool)||/^machine_fs\.write$/.test(tool)){const bytes=payloadByteCount(args.content);return [where,bytes!==null?'('+bytes+'B)':''].filter(Boolean).join(' ');}
105
+ if(/text_replace$|symbol_replace$/.test(tool)||/^machine_fs\.edit_exact$/.test(tool)){const bytes=payloadByteCount(args.new_string);return [where,bytes!==null?'('+bytes+'B new)':''].filter(Boolean).join(' ');}
106
+ if(/^machine_fs\.(?:mkdir|delete)$/.test(tool)||/coding_tree$/.test(tool)) return where;
107
+ if(/^machine_fs\.move$/.test(tool)) return [
108
+ rootedPath({...args,path:args.source_path},'path',rootPaths),
109
+ '->',
110
+ rootedPath({...args,path:args.destination_path},'path',rootPaths)
111
+ ].filter(Boolean).join(' ');
112
+ if(/coding_(?:search|inspect)(?:\.|$)/.test(tool)) return [where,args.pattern?'\"'+clipped(String(args.pattern),36)+'\"':''].filter(Boolean).join(' ');
113
+ if(/(?:command|benchmark)_run$/.test(tool)){
114
+ const root=safeAtom(args.root_id);const command=safeAtom(args.command_id);const scriptPath=safePathAtom(args.script_path);
115
+ if(root&&safePathAtom(rootPaths?.[root])) return [rootedPath({...args,path:scriptPath},'path',rootPaths),command].filter(Boolean).join(' ');
116
+ return [root,command,scriptPath].filter(Boolean).join(' ');
117
+ }
118
+ if(/^host_exec$/.test(tool)) return [rootedPath(args,'path',rootPaths),safeAtom(args.command_id),safeAtom(args.mode)].filter(Boolean).join(' ');
90
119
  if(/^machine_(?:ping|status|system_status|list)$/.test(tool)) return safeAtom(args.nonce)?'nonce='+shortValue(String(args.nonce),24):'';
91
120
  if(/^process_session/.test(tool)) return [safeAtom(args.profile_id),shortValue(safeAtom(args.session_id)||'',12),safeAtom(args.op)].filter(Boolean).join(' ');
92
121
  if(/^desktop_/.test(tool)) return [shortValue(safeAtom(args.frame_id)||'',10),shortValue(safeAtom(args.target_id)||'',10),safeAtom(args.op)].filter(Boolean).join(' ');
@@ -98,11 +127,11 @@ function categoryForTool(tool){
98
127
  if(/^autonomous_/.test(tool)) return 'autonomous';
99
128
  if(HEARTBEAT_TOOLS.has(tool)) return 'heartbeat';
100
129
  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';
130
+ if(/(?:search|inspect|symbol_(?:definition|references|implementations|outline|diagnostics)|(?:^|[._])stat$|(?:^|[._])list$|_tree$)/.test(tool)) return 'search';
131
+ if(/(?:file_read|files_read|machine_fs[._]read|process_session[._]read|host_file_read)/.test(tool)) return 'read';
132
+ if(/(?:file_write|machine_fs[._]write|host_file_write|(?:^|[._])mkdir$|(?:^|[._])move$|(?:^|[._])delete$)/.test(tool)) return 'write';
104
133
  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';
134
+ if(/(?:command_run|benchmark_run|task_run|goal_run|process_session[._](?:start|action|terminate)|host_exec)/.test(tool)) return 'run';
106
135
  if(/^process_/.test(tool)) return 'process';
107
136
  return 'generic';
108
137
  }
@@ -110,7 +139,10 @@ function iconForTool(tool,displayMode='unicode'){
110
139
  const icons=displayMode==='ascii'?TOOL_ICONS_ASCII:TOOL_ICONS;
111
140
  return icons[categoryForTool(tool)]??icons.generic;
112
141
  }
113
- function visibleStart(tool){return START_VISIBLE_PATTERNS.some(pattern=>pattern.test(tool));}
142
+ function visibleStart(tool){
143
+ if(['write','edit','run'].includes(categoryForTool(tool))) return true;
144
+ return START_VISIBLE_PATTERNS.some(pattern=>pattern.test(tool));
145
+ }
114
146
 
115
147
  function displayTimestamp(ms){
116
148
  const shifted=new Date(ms+DISPLAY_UTC_OFFSET_MINUTES*60*1000).toISOString();
@@ -189,24 +221,121 @@ export function createLiveActivityTracker(){
189
221
  function shortSpanId(requestId){
190
222
  return typeof requestId==='string'&&requestId.length?requestId.slice(0,8):'????????';
191
223
  }
224
+ function traceIdentity(event,args=event?.args??{}){
225
+ const context=event?.context&&typeof event.context==='object'&&!Array.isArray(event.context)?event.context:{};
226
+ const first=(...values)=>values.map(safeAtom).find(Boolean)||null;
227
+ const spanId=safeAtom(event?.request_id);
228
+ return {
229
+ trace_id:first(context.trace_id,context.job_id,context.session_id,args.goal_id,args.task_id,args.job_id,args.session_id,spanId),
230
+ span_id:spanId,
231
+ parent_span_id:first(context.parent_span_id,context.parent_request_id)
232
+ };
233
+ }
234
+ function shortTraceValue(value,displayMode){
235
+ if(typeof value!=='string'||!value) return '';
236
+ if(value.length<=8) return value;
237
+ return value.slice(0,8)+(displayMode==='ascii'?'...':'…');
238
+ }
239
+ function traceText(meta,displayMode='unicode'){
240
+ if(!meta?.trace_id||!meta?.span_id) return '';
241
+ return 'trace='+shortTraceValue(meta.trace_id,displayMode)+' span='+shortTraceValue(meta.span_id,displayMode)+
242
+ (meta.parent_span_id?' parent='+shortTraceValue(meta.parent_span_id,displayMode):'');
243
+ }
244
+ function outcomeClass(event){
245
+ if(event?.outcome?.side_effect_state==='unknown') return 'unknown';
246
+ return event?.outcome?.is_error===true?'error':'ok';
247
+ }
248
+ function parseFilterExpression(expression){
249
+ if(expression===null||expression===undefined||expression==='') return Object.freeze({});
250
+ if(typeof expression!=='string'||expression.length>512) throw new Error('timeline filter invalid');
251
+ const out={};
252
+ for(const raw of expression.split(',')){
253
+ const part=raw.trim();if(!part) continue;
254
+ const at=part.indexOf('=');if(at<1||at===part.length-1) throw new Error('timeline filter term must be key=value');
255
+ const key=part.slice(0,at).trim();const value=part.slice(at+1).trim();
256
+ if(!['tool','category','status','trace'].includes(key)) throw new Error(`timeline filter key '${key}' unsupported`);
257
+ if(Object.hasOwn(out,key)) throw new Error(`timeline filter key '${key}' repeated`);
258
+ if(key==='status'&&!['ok','error','unknown'].includes(value)) throw new Error('timeline filter status invalid');
259
+ if(key==='category'&&!Object.hasOwn(TOOL_ICONS,value)) throw new Error('timeline filter category invalid');
260
+ out[key]=value;
261
+ }
262
+ return Object.freeze(out);
263
+ }
264
+ function toolFilterMatch(pattern,tool){
265
+ if(!pattern) return true;
266
+ return pattern.endsWith('*')?tool.startsWith(pattern.slice(0,-1)):tool===pattern;
267
+ }
268
+ export function createLiveTimelineFilter(expression=''){
269
+ const spec=parseFilterExpression(expression);
270
+ const pending=new Map();
271
+ const baseMatch=meta=>(!spec.tool||toolFilterMatch(spec.tool,meta.tool))&&(!spec.category||spec.category===meta.category)&&(!spec.trace||spec.trace===meta.trace_id);
272
+ return Object.freeze({
273
+ accept(event){
274
+ if(!event||typeof event!=='object'||Array.isArray(event)) return false;
275
+ const requestId=safeAtom(event.request_id);const rawTool=safeAtom(event.tool);
276
+ if(!requestId||!rawTool) return false;
277
+ if(event.event_type==='tool.request'){
278
+ const args=event.args??{};const tool=displayToolName(rawTool,args);const trace=traceIdentity(event,args);
279
+ const meta={tool,category:categoryForTool(tool),trace_id:trace.trace_id};
280
+ pending.set(requestId,meta);
281
+ return spec.status?false:baseMatch(meta);
282
+ }
283
+ if(event.event_type==='tool.result'){
284
+ const meta=pending.get(requestId)??{tool:rawTool,category:categoryForTool(rawTool),trace_id:traceIdentity(event).trace_id};
285
+ pending.delete(requestId);
286
+ return baseMatch(meta)&&(!spec.status||spec.status===outcomeClass(event));
287
+ }
288
+ return false;
289
+ },
290
+ spec
291
+ });
292
+ }
293
+ export function createLiveTimelineWriter({stream=process.stdout,displayMode='unicode',rewrite=true}={}){
294
+ if(!stream||typeof stream.write!=='function') throw new Error('timeline writer stream invalid');
295
+ if(!REMOTE_DISPLAY_MODES.includes(displayMode)) throw new Error('timeline writer display mode invalid');
296
+ if(typeof rewrite!=='boolean') throw new Error('timeline writer rewrite invalid');
297
+ let active=null;
298
+ function flush(){
299
+ if(active){stream.write('\n');active=null;}
300
+ }
301
+ function write(line,{event_type=null,request_id=null}={}){
302
+ if(typeof line!=='string'||!line) return;
303
+ const tty=stream.isTTY===true&&rewrite;
304
+ if(!tty){stream.write(line+'\n');return;}
305
+ if(active&&event_type==='tool.result'&&request_id&&request_id===active.request_id){
306
+ stream.write('\r\x1b[2K'+line+'\n');active=null;return;
307
+ }
308
+ if(active) flush();
309
+ if(event_type==='tool.call'&&request_id){stream.write(line);active={request_id};return;}
310
+ stream.write(line+'\n');
311
+ }
312
+ return Object.freeze({write,flush,pending(){return active?{...active}:null;}});
313
+ }
192
314
 
193
- export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,heartbeatBurstLimit=10,heartbeatWindowMs=3000,displayMode='unicode'}={}){
315
+ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,heartbeatBurstLimit=10,heartbeatWindowMs=3000,displayMode='unicode',rootPaths={},trace=false}={}){
194
316
  if(typeof clock!=='function') throw new Error('live timeline clock must be a function');
195
317
  if(!REMOTE_DISPLAY_MODES.includes(displayMode)) throw new Error('remote display mode invalid');
196
318
  if(!Number.isInteger(heartbeatBurstLimit)||heartbeatBurstLimit<2||heartbeatBurstLimit>100) throw new Error('heartbeatBurstLimit invalid');
197
319
  if(!Number.isInteger(heartbeatWindowMs)||heartbeatWindowMs<250||heartbeatWindowMs>60000) throw new Error('heartbeatWindowMs invalid');
320
+ if(!rootPaths||typeof rootPaths!=='object'||Array.isArray(rootPaths)) throw new Error('live timeline rootPaths invalid');
321
+ if(typeof trace!=='boolean') throw new Error('live timeline trace invalid');
198
322
  const pending=new Map();
199
323
  const completed=new Set();
200
324
  const toolCounts=new Map();
201
- let resultCount=0,errorCount=0,totalDurationMs=0,burst=null,foldedHeartbeatLines=0;
325
+ const durationSamples=[];
326
+ let resultCount=0,okCount=0,errorCount=0,unknownCount=0,mutationCount=0,totalDurationMs=0,slowest=null,burst=null,foldedHeartbeatLines=0;
202
327
 
203
- function eventPrefix(event,sequence){
328
+ function eventPrefix(event,sequence,{displayTool=null,icon=null}={}){
204
329
  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;
330
+ const tool=displayTool||event.tool;
331
+ const visual=icon||iconForTool(tool,displayMode);
332
+ return visual+' '+displayClock(eventMs(event,clock))+' '+seq+'['+shortSpanId(event.request_id)+'] '+tool;
206
333
  }
334
+ function traceSuffix(meta){return trace&&meta?' '+traceText(meta,displayMode):'';}
207
335
  function resultLine(event,span,sequence){
208
336
  const error=event.outcome?.is_error===true;
209
337
  const unknown=event.outcome?.side_effect_state==='unknown';
338
+ const displayTool=span?.display_tool||event.tool;
210
339
  const duration=Number.isFinite(Number(event.outcome?.duration_ms))
211
340
  ?formatDuration(event.outcome.duration_ms)
212
341
  :span?formatDuration(Math.max(0,clock()-span.started_at_ms)):'';
@@ -214,8 +343,17 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,he
214
343
  const rawReason=typeof event.outcome?.reason==='string'&&event.outcome.reason?event.outcome.reason:
215
344
  (unknown&&typeof event.outcome?.recovery?.reason==='string'?event.outcome.recovery.reason:'');
216
345
  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);
346
+ const category=categoryForTool(displayTool);
347
+ const receipt=Array.isArray(event.outcome?.receipt_sha256s)&&typeof event.outcome.receipt_sha256s[0]==='string'
348
+ ?shortValue(event.outcome.receipt_sha256s[0],8):'';
349
+ const marker=unknown?'FAILED_AFTER_UNKNOWN_EFFECT':
350
+ error?(displayMode==='ascii'?'ERR':'✗'):
351
+ ['write','edit','run'].includes(category)?(displayMode==='ascii'?'SUCCEEDED':'✅ SUCCEEDED'):
352
+ (displayMode==='ascii'?'OK':'✓');
353
+ const prefixIcon=unknown?(displayMode==='ascii'?'WARN':'⚠️'):null;
354
+ const evidence=receipt?' '+(displayMode==='ascii'?'RECEIPT':'🧾 receipt')+(displayMode==='ascii'?'=':'=')+receipt:'';
355
+ const reasonSep=displayMode==='ascii'?' - ':' — ';
356
+ return remoteDisplayText(eventPrefix(event,sequence,{displayTool,icon:prefixIcon})+traceSuffix(span?.trace_meta)+(summary?' '+summary:'')+(duration?' '+duration:'')+' '+marker+evidence+(reason?reasonSep+reason:''),displayMode);
219
357
  }
220
358
  function burstLine(value){
221
359
  if(!value) return null;
@@ -233,12 +371,19 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,he
233
371
  burst=null;
234
372
  return line;
235
373
  }
236
- function addStats(event){
374
+ function addStats(event,span){
237
375
  resultCount++;
238
- toolCounts.set(event.tool,(toolCounts.get(event.tool)??0)+1);
376
+ const displayTool=span?.display_tool||event.tool;
377
+ toolCounts.set(displayTool,(toolCounts.get(displayTool)??0)+1);
378
+ const outcome=outcomeClass(event);
379
+ if(outcome==='unknown') unknownCount++;else if(outcome==='error') errorCount++;else okCount++;
380
+ if(['write','edit','run'].includes(categoryForTool(displayTool))) mutationCount++;
239
381
  const duration=Number(event.outcome?.duration_ms);
240
- if(Number.isFinite(duration)&&duration>=0) totalDurationMs+=duration;
241
- if(event.outcome?.is_error===true) errorCount++;
382
+ if(Number.isFinite(duration)&&duration>=0){
383
+ totalDurationMs+=duration;
384
+ if(durationSamples.length<2048) durationSamples.push(duration);
385
+ if(!slowest||duration>slowest.duration_ms) slowest={tool:displayTool,duration_ms:duration};
386
+ }
242
387
  }
243
388
  function observe(event,{sequence=null}={}){
244
389
  if(!event||typeof event!=='object'||Array.isArray(event)) return null;
@@ -246,10 +391,13 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,he
246
391
  const tool=typeof event.tool==='string'&&event.tool?event.tool:null;
247
392
  if(event.event_type==='tool.request'&&requestId&&tool){
248
393
  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:'')+' …';
394
+ const args=event.args??{};
395
+ const displayTool=displayToolName(tool,args);
396
+ const summary=summarizeLiveToolArgs(displayTool,args,{rootPaths});
397
+ const traceMeta=traceIdentity(event,args);
398
+ pending.set(requestId,{request_id:requestId,tool,display_tool:displayTool,summary,trace_meta:traceMeta,started_at_ms:clock()});
399
+ if(verbose) return formatRemoteToolCall({...event,tool:displayTool},{clock,displayMode})+traceSuffix(traceMeta);
400
+ if(visibleStart(displayTool)) return eventPrefix(event,sequence,{displayTool})+traceSuffix(traceMeta)+(summary?' '+summary:'')+' …';
253
401
  return null;
254
402
  }
255
403
  if(event.event_type==='tool.result'&&requestId&&tool){
@@ -257,7 +405,7 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,he
257
405
  const span=pending.get(requestId)??null;
258
406
  pending.delete(requestId);
259
407
  completed.add(requestId);
260
- addStats(event);
408
+ addStats(event,span);
261
409
  const error=event.outcome?.is_error===true;
262
410
  const nowMs=eventMs(event,clock);
263
411
  if(HEARTBEAT_TOOLS.has(tool)&&!error){
@@ -297,8 +445,14 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,he
297
445
  function summary(){
298
446
  const top=[...toolCounts.entries()].sort((a,b)=>b[1]-a[1]||a[0].localeCompare(b[0])).slice(0,3)
299
447
  .map(([tool,count])=>tool+'×'+count).join(', ');
300
- return remoteDisplayText((displayMode==='ascii'?'[SUMMARY]':'📊 [SUMMARY]')+' '+resultCount+' calls | '+errorCount+' error'+(errorCount===1?'':'s')+
448
+ const sorted=[...durationSamples].sort((a,b)=>a-b);
449
+ const p50=sorted.length?(sorted.length%2?sorted[(sorted.length-1)/2]:(sorted[sorted.length/2-1]+sorted[sorted.length/2])/2):null;
450
+ const p95=sorted.length?sorted[Math.max(0,Math.ceil(sorted.length*0.95)-1)]:null;
451
+ return remoteDisplayText((displayMode==='ascii'?'[SUMMARY]':'📊 [SUMMARY]')+' '+resultCount+' calls | '+okCount+' ok | '+
452
+ errorCount+' error'+(errorCount===1?'':'s')+' | '+unknownCount+' unknown | '+mutationCount+' mutation'+(mutationCount===1?'':'s')+
301
453
  ' | tool time '+(formatDuration(totalDurationMs)||'0ms')+
454
+ (p50!==null?' | p50 '+formatDuration(p50)+' | p95 '+formatDuration(p95):'')+
455
+ (slowest?' | slowest '+slowest.tool+' '+formatDuration(slowest.duration_ms):'')+
302
456
  (foldedHeartbeatLines?' | folded '+foldedHeartbeatLines+' heartbeat lines':'')+
303
457
  (top?' | top: '+top:''),displayMode);
304
458
  }
@@ -86,7 +86,7 @@ function safeOutcomeReason(value){
86
86
  if(value===null||value===undefined) return null;
87
87
  let text=typeof value==='string'?value:(value instanceof Error?String(value.message??value):null);
88
88
  if(text===null) return null;
89
- text=text.replace(/[\\r\\n\\t]+/g,' ').replace(/\\s{2,}/g,' ').trim();
89
+ text=text.replace(/[\r\n\t]+/g,' ').replace(/\s{2,}/g,' ').trim();
90
90
  if(!text) return null;
91
91
  const originalBytes=Buffer.from(text,'utf8');
92
92
  if(secretValuePattern.test(text)) return `<redacted reason bytes=${originalBytes.length} sha256=${sha256(originalBytes)}>`;
package/src/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export const DEADBYTE_VERSION = '0.15.21';
1
+ export const DEADBYTE_VERSION = '0.15.23';
2
2
  export const MCP_PROTOCOL_VERSION = '2026-07-28';
3
3
  export const RECEIPT_SCHEMA_V1 = 'deadbyte.receipt.v1';
4
4
  export const RECEIPT_SCHEMA_V2 = 'deadbyte.receipt.v2';
@@ -15,7 +15,7 @@ const desktopPolicyPath=path.join(repoRoot,'controller','deadbyte-desktop-policy
15
15
  const catalogPath=path.join(repoRoot,'docs','R47-FULL-TOOL-CATALOG.json');
16
16
  const absent=async p=>{try{await access(p);return false;}catch(e){if(e?.code==='ENOENT')return true;throw e;}};
17
17
 
18
- test('R47 runtime full stdio catalog exactly matches canonical registration surface',async()=>{
18
+ test('R47 runtime full stdio catalog exactly matches canonical registration surface',{skip:'historical R47 catalog; R48 supersedes it'},async()=>{
19
19
  const canonical=JSON.parse(await readFile(catalogPath,'utf8'));
20
20
  assert.equal(canonical.schema,'deadbyte.r47-full-catalog.v1');
21
21
  assert.equal(canonical.train,'R47');
@@ -0,0 +1,78 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { createHash } from 'node:crypto';
4
+ import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { Client } from '@modelcontextprotocol/client';
8
+ import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/client/stdio';
9
+ import { createR34Fixture } from './helpers/autonomous-r34-fixture.mjs';
10
+
11
+ const here=path.dirname(fileURLToPath(import.meta.url));
12
+ const repoRoot=path.resolve(here,'..');
13
+ const serverPath=path.join(repoRoot,'src','mcp-server.mjs');
14
+ const desktopPolicyPath=path.join(repoRoot,'controller','deadbyte-desktop-policy.json');
15
+ const catalogPath=path.join(repoRoot,'docs','R48-FULL-TOOL-CATALOG.json');
16
+ const absent=async p=>{try{await access(p);return false;}catch(e){if(e?.code==='ENOENT')return true;throw e;}};
17
+
18
+ test('R48 runtime full stdio catalog exactly matches canonical registration surface',async()=>{
19
+ const canonical=JSON.parse(await readFile(catalogPath,'utf8'));
20
+ assert.equal(canonical.schema,'deadbyte.r48-full-catalog.v1');
21
+ assert.equal(canonical.train,'R48');
22
+ assert.ok(Array.isArray(canonical.tools));
23
+ assert.equal(canonical.tool_count,canonical.tools.length);
24
+ assert.equal(new Set(canonical.tools).size,canonical.tools.length);
25
+ assert.deepEqual([...canonical.tools].sort(),canonical.tools);
26
+
27
+ const f=await createR34Fixture({authority:'disarmed'});
28
+ const processPolicyPath=path.join(f.root,'process-policy.json');
29
+ const processGrant=path.join(f.root,'process-grant.json');
30
+ const processState=path.join(f.root,'process-state');
31
+ const hostPolicyPath=path.join(f.root,'host-policy.json');
32
+ const hostArm=path.join(f.root,'host-arm.json');
33
+ const desktopRaw=JSON.parse(await readFile(desktopPolicyPath,'utf8'));
34
+ const desktopGrant=desktopRaw.grant_file;
35
+ await mkdir(processState,{recursive:true});
36
+ const nodeSha=createHash('sha256').update(await readFile(process.execPath)).digest('hex');
37
+ await writeFile(processPolicyPath,JSON.stringify({
38
+ schema:'deadbyte.process-policy.v1',enabled:true,grant_file:processGrant,state_dir:processState,
39
+ native_host:{path:process.execPath,sha256:nodeSha},
40
+ roots:{core:{path:f.project,deny_paths:[]}},
41
+ profiles:{node:{executable:process.execPath,executable_sha256:nodeSha,root_ids:['core'],prefix_args:[],transport:'pipe',max_args:8,max_arg_bytes:1024,max_sessions:2,max_input_bytes:65536,max_retained_output_bytes:262144,env_allow:['PATH','TEMP','TMP'],input_actions:{ping:'PING\\r\\n'}}}
42
+ },null,2));
43
+ await writeFile(hostPolicyPath,JSON.stringify({
44
+ schema:'deadbyte.host-policy.v1',enabled:true,arm_file:hostArm,max_read_bytes:262144,max_write_bytes:1048576,
45
+ roots:{core:{path:f.project,read:true,write:true,exec:true}},
46
+ commands:{node_script:{kind:'script',executable:process.execPath,executable_sha256:nodeSha,root_ids:['core'],prefix_args:[],extensions:['.mjs'],timeout_ms:5000,max_output_bytes:65536}}
47
+ },null,2));
48
+ const codingLease=f.codingPolicy.lease_file;
49
+ const autonomousLease=f.policy.lease_file;
50
+ for(const q of [processGrant,hostArm,desktopGrant,codingLease,autonomousLease]) assert.equal(await absent(q),true,'authority unexpectedly armed before start: '+q);
51
+
52
+ const client=new Client({name:'deadbyte-r48-full-catalog',version:'0.15.23'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
53
+ const env={
54
+ ...getDefaultEnvironment(),DEADBYTE_CAPABILITY_PROFILE:'full',DEADBYTE_AUDIT_RUNTIME:'0',
55
+ DEADBYTE_STATE_ROOT:path.join(f.root,'state'),DEADBYTE_WORKSPACE_ROOT:f.root,
56
+ DEADBYTE_SIGNING_KEY:f.keys.privateKeyPath,DEADBYTE_VERIFY_KEY:f.keys.publicKeyPath,
57
+ DEADBYTE_PRIVILEGED_POLICY:hostPolicyPath,DEADBYTE_MACHINE_RUNTIME:'1',DEADBYTE_AGENT_RUNTIME:'1',
58
+ DEADBYTE_PROCESS_RUNTIME:'1',DEADBYTE_PROCESS_POLICY:processPolicyPath,DEADBYTE_CODING_RUNTIME:'1',
59
+ DEADBYTE_CODING_POLICY:path.join(f.root,'coding-policy.json'),DEADBYTE_MACHINE_FS_RUNTIME:'1',
60
+ DEADBYTE_OBSERVATION_RUNTIME:'1',DEADBYTE_DESKTOP_RUNTIME:'1',DEADBYTE_DESKTOP_POLICY:desktopPolicyPath,
61
+ DEADBYTE_AUTONOMOUS_RUNTIME:'1',DEADBYTE_AUTONOMOUS_POLICY:path.join(f.root,'autonomous-policy.json'),
62
+ DEADBYTE_AUTONOMOUS_SUPERVISOR_MODE:'external'
63
+ };
64
+ const transport=new StdioClientTransport({command:process.execPath,args:[serverPath],cwd:repoRoot,env,stderr:'pipe'});
65
+ try{
66
+ await client.connect(transport);
67
+ const listed=await client.listTools();
68
+ const names=listed.tools.map(t=>t.name).sort();
69
+ assert.deepEqual(names,canonical.tools);
70
+ assert.equal(new Set(names).size,names.length,'runtime duplicate tool names');
71
+ for(const tool of listed.tools) if(tool.outputSchema!==undefined) assert.equal(tool.outputSchema?.additionalProperties,false,'non-strict output '+tool.name);
72
+ for(const name of ['agent_task_submit','autonomous_goal_submit','coding_task_submit','process_session_start','machine_fs_write','machine_search_start','desktop_action','host_exec','native_diff_apply','skill_catalog']) assert.ok(names.includes(name),'missing R48 surface '+name);
73
+ } finally {
74
+ await client.close().catch(()=>{});
75
+ for(const q of [processGrant,hostArm,desktopGrant,codingLease,autonomousLease]) assert.equal(await absent(q),true,'authority file appeared during catalog probe: '+q);
76
+ await f.cleanup();
77
+ }
78
+ });
@@ -1,7 +1,7 @@
1
1
  import test from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
3
  import { createHash } from 'node:crypto';
4
- import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
4
+ import { lstat, mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
5
5
  import os from 'node:os';
6
6
  import path from 'node:path';
7
7
  import { generateTrustKeypair } from '../src/trust.mjs';
@@ -10,7 +10,7 @@ import { createMachineFsRuntime, verifyMachineFsReceiptFile } from '../src/machi
10
10
 
11
11
  const sha = bytes => createHash('sha256').update(bytes).digest('hex');
12
12
 
13
- async function fixture({ readWorkerPath = undefined } = {}) {
13
+ async function fixture({ readWorkerPath = undefined, listEntryLstat = undefined } = {}) {
14
14
  const root = await mkdtemp(path.join(os.tmpdir(), 'deadbyte-machine-fs-'));
15
15
  const project = path.join(root, 'project');
16
16
  await mkdir(project);
@@ -30,7 +30,7 @@ async function fixture({ readWorkerPath = undefined } = {}) {
30
30
  privateKeyPath: path.join(root, 'private.pem'), publicKeyPath: path.join(root, 'public.pem')
31
31
  });
32
32
  const runtime = createMachineFsRuntime({
33
- policy, signingKeyPath: keys.privateKeyPath, verifyKeyPath: keys.publicKeyPath, readWorkerPath
33
+ policy, signingKeyPath: keys.privateKeyPath, verifyKeyPath: keys.publicKeyPath, readWorkerPath, listEntryLstat
34
34
  });
35
35
  return { root, project, policy, keys, runtime };
36
36
  }
@@ -87,6 +87,31 @@ test('R31 machine fs supports exact mkdir/write/stat/list/range-read/edit/move/d
87
87
  assert.equal(second.next_cursor, null);
88
88
  });
89
89
 
90
+ test('R48 machine fs listing isolates one unreadable child instead of failing the page', async () => {
91
+ let projectPath = null;
92
+ const injectedLstat = async target => {
93
+ if (projectPath && path.basename(target) === 'bad.txt') {
94
+ const error = new Error('simulated Windows lstat EINVAL');
95
+ error.code = 'EINVAL';
96
+ error.syscall = 'lstat';
97
+ throw error;
98
+ }
99
+ return lstat(target);
100
+ };
101
+ const f = await fixture({ listEntryLstat: injectedLstat });
102
+ projectPath = f.project;
103
+ await writeFile(path.join(f.project, 'bad.txt'), 'bad');
104
+ await writeFile(path.join(f.project, 'good.txt'), 'good');
105
+ const listed = await f.runtime.list({ rootId:'project', relativePath:'.', limit:10, hashFiles:true });
106
+ assert.deepEqual(listed.entries.map(item => item.path), ['bad.txt','good.txt']);
107
+ assert.deepEqual(listed.entries[0], {
108
+ path:'bad.txt', kind:'unreadable', bytes:0, mtime_utc:null, sha256:null,
109
+ error:{ code:'EINVAL', operation:'lstat' }
110
+ });
111
+ assert.equal(listed.entries[1].kind, 'file');
112
+ assert.equal(listed.entries[1].sha256, sha(Buffer.from('good')));
113
+ });
114
+
90
115
  test('R31 machine fs fails closed on traversal, denied paths and stale source identity', async () => {
91
116
  const f = await fixture();
92
117
  await mkdir(path.join(f.project, 'blocked'));
@@ -16,14 +16,15 @@ function runtime(){
16
16
  searchCancel:async()=>({status:'ok',search_id:hex('a'),state:'cancelled',cancel_requested:true}),
17
17
  processList:async()=>({status:'ok',snapshot_id:hex('e'),observed_at_utc:new Date(0).toISOString(),processes:[],next_cursor:null,request_sha256:hex('b'),result_head_sha256:hex('c'),receipt_id:receipt,receipt_sha256:hex('d')}),
18
18
  systemStatus:async()=>({status:'ok',observed_at_utc:new Date(0).toISOString(),hostname:'x',platform:'win32',architecture:'x64',uptime_seconds:1,total_memory_bytes:2,free_memory_bytes:1,cpu_count:1,load_average:[0,0,0],request_sha256:hex('b'),result_head_sha256:hex('c'),receipt_id:receipt,receipt_sha256:hex('d')}),
19
- hardwareQuery:async({query})=>({status:'ok',query,observed_at_utc:new Date(0).toISOString(),observer_sha256:hex('e'),data:{vendor:'x'},request_sha256:hex('b'),result_head_sha256:hex('c'),receipt_id:receipt,receipt_sha256:hex('d')})
19
+ hardwareQuery:async({query})=>({status:'ok',query,observed_at_utc:new Date(0).toISOString(),observer_sha256:hex('e'),data:{vendor:'x'},request_sha256:hex('b'),result_head_sha256:hex('c'),receipt_id:receipt,receipt_sha256:hex('d')}),
20
+ windowsQuery:async({query})=>({status:'ok',query,observed_at_utc:new Date(0).toISOString(),observer_sha256:hex('e'),data:{query},request_sha256:hex('b'),result_head_sha256:hex('c'),receipt_id:receipt,receipt_sha256:hex('d')})
20
21
  };
21
22
  }
22
23
 
23
- test('R45 registers exactly seven bounded observation tools including typed native hardware',()=>{
24
+ test('R48 registers exactly eight bounded observation tools including typed native Windows observation',()=>{
24
25
  const server=new FakeServer(); registerObservationTools(server,{runtime:runtime()});
25
26
  assert.deepEqual([...server.tools.keys()].sort(),[
26
- 'machine_hardware_query','machine_process_list','machine_search_cancel','machine_search_page','machine_search_start','machine_search_status','machine_system_status'
27
+ 'machine_hardware_query','machine_process_list','machine_search_cancel','machine_search_page','machine_search_start','machine_search_status','machine_system_status','machine_windows_query'
27
28
  ]);
28
29
  for(const [name,tool] of server.tools){ assert.ok(tool.spec.inputSchema,`${name} input`); assert.ok(tool.spec.outputSchema,`${name} output`); }
29
30
  });
@@ -6,31 +6,41 @@ import { DEADBYTE_VERSION } from '../src/version.mjs';
6
6
  const root=new URL('../',import.meta.url);
7
7
  const read=rel=>readFile(new URL(rel,root),'utf8');
8
8
 
9
- test('R48 successor identity is coherent and binds active 0.15.20 parent plus sealed 0.15.20 source predecessor',async()=>{
9
+ test('R48 0.15.23 successor binds active 0.15.22 parent and livelog trace evidence',async()=>{
10
10
  const [pkg,lock,release,provenance]=await Promise.all([
11
11
  read('package.json').then(JSON.parse),
12
12
  read('package-lock.json').then(JSON.parse),
13
13
  read('RELEASE.json').then(JSON.parse),
14
14
  read('PROVENANCE.json').then(JSON.parse)
15
15
  ]);
16
- assert.equal(pkg.version,'0.15.21');
17
- assert.equal(lock.version,'0.15.21');
18
- assert.equal(lock.packages[''].version,'0.15.21');
19
- assert.equal(DEADBYTE_VERSION,'0.15.21');
20
- assert.equal(release.version,'0.15.21');
21
- assert.equal(provenance.version,'0.15.21');
16
+ assert.equal(pkg.version,'0.15.23');
17
+ assert.equal(lock.version,'0.15.23');
18
+ assert.equal(lock.packages[''].version,'0.15.23');
19
+ assert.equal(DEADBYTE_VERSION,'0.15.23');
20
+ assert.equal(release.version,'0.15.23');
21
+ assert.equal(provenance.version,'0.15.23');
22
22
  assert.equal(release.train,'R48');
23
- assert.equal(release.label,'CAPABILITY MANIFEST AUTHORITY TRUTH');
24
- assert.equal(release.parent.release_id,'v0.15.20-bab990b08c9c1f07');
25
- assert.equal(release.parent.manifest_sha256,'bab990b08c9c1f0788a1ede6b0695d293f8433e4ef0f541eca59635cf125a43d');
26
- assert.equal(release.parent.generation_manifest_sha256,'617ea6bfbfc7e1952d17e7fea1043fd70b32bb1edacbd3ed6002fec7235c7221');
27
- assert.equal(release.source_predecessor.version,'0.15.20');
28
- assert.equal(release.source_predecessor.tarball_sha256,'eb7cbe8465ae3834c9857fc2115c7ab4eb3d1a018aa1695b799912def5dc3cee');
29
- assert.equal(release.source_predecessor.manifest_file_sha256,'bab990b08c9c1f0788a1ede6b0695d293f8433e4ef0f541eca59635cf125a43d');
23
+ assert.equal(release.label,'HUMAN LIVELOG TRACE + VISUAL OPCODE BASELINE');
24
+ assert.equal(release.parent.release_id,'v0.15.22-ed178d33bf883f68');
25
+ assert.equal(release.parent.manifest_sha256,'ed178d33bf883f68b4f39f2e8bb805094bc8024b30114c4e6a34b80930cfcff8');
26
+ assert.equal(release.parent.generation_manifest_sha256,'029b177dc1838388ddec154be38cb597a4dcfa0711b2297196d5a9e7cb8dbe18');
27
+ assert.equal(release.source_predecessor.version,'0.15.22');
28
+ assert.equal(release.source_predecessor.tarball_sha256,'fda51e06fa16116179d9c48f1466432886c10e62807482326d2dc5ccb7b66eac');
29
+ assert.equal(release.source_predecessor.manifest_file_sha256,'ed178d33bf883f68b4f39f2e8bb805094bc8024b30114c4e6a34b80930cfcff8');
30
30
  assert.equal(release.source_predecessor.reference_server_sha256,'dfc303bb7dd2e8d01debac50fc43bb8644d00089bffdf2887e149497cff8b305');
31
31
  assert.equal(release.source_predecessor.reference_schemas_sha256,'f342345e34e48769dabaefe1b932f09d41e2e207b422f7794a15ee50ffa8f140');
32
+ assert.equal(release.capabilities.full_tool_count,116);
33
+ assert.equal(release.capabilities.full_tool_catalog.path,'docs/R48-FULL-TOOL-CATALOG.json');
34
+ assert.deepEqual(release.capabilities.windows_queries,['registry','services','drivers','sockets','volumes','usb','wsl']);
35
+ assert.equal(release.capabilities.machine_fs_list_entry_fault_isolation,true);
32
36
  assert.equal(release.capabilities.capability_manifest_authority_truth,true);
33
37
  assert.equal(release.capabilities.capability_manifest_host_grant_truth,true);
38
+ assert.equal(release.capabilities.human_livelog_visual_opcode,true);
39
+ assert.equal(release.capabilities.human_livelog_absolute_root_paths,true);
40
+ assert.equal(release.capabilities.human_livelog_trace_span,true);
41
+ assert.equal(release.capabilities.human_livelog_presentation_filter,true);
42
+ assert.equal(release.capabilities.human_livelog_tty_in_place_rewrite,true);
43
+ assert.equal(release.capabilities.human_livelog_expanded_summary,true);
34
44
  assert.equal(provenance.parent_release_id,release.parent.release_id);
35
45
  assert.equal(provenance.parent_manifest_sha256,release.parent.manifest_sha256);
36
46
  assert.equal(provenance.parent_generation_manifest_sha256,release.parent.generation_manifest_sha256);