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
@@ -0,0 +1,30 @@
1
+ import { mkdtemp, rm } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { promisify } from "node:util";
5
+ import { execFile } from "node:child_process";
6
+ const ex=promisify(execFile);
7
+ const creator=process.argv[2];
8
+ if(!creator) throw new Error("creator path required");
9
+ const valid=["1.0.0","0.0.1","1.1.1","12.34.56","1.0.0-rc.1"];
10
+ const invalid=["1.0","v1.0.0","1.a.0","",String.fromCharCode(49,92,97,48,92,98,48)];
11
+ const results=[];
12
+ for(const version of valid){
13
+ const tmp=await mkdtemp(path.join(os.tmpdir(),"deadbyte-semver-valid-"));
14
+ try{
15
+ await ex(process.execPath,[creator,tmp,"fixture",version],{timeout:30000,maxBuffer:1048576});
16
+ results.push({version,expected:"PASS",observed:"PASS"});
17
+ }finally{await rm(tmp,{recursive:true,force:true});}
18
+ }
19
+ for(const version of invalid){
20
+ const tmp=await mkdtemp(path.join(os.tmpdir(),"deadbyte-semver-invalid-"));
21
+ let rejected=false;
22
+ try{
23
+ await ex(process.execPath,[creator,tmp,"fixture",version],{timeout:30000,maxBuffer:1048576});
24
+ }catch(e){
25
+ if(e.code===1 && String(e.stderr).includes("invalid version")) rejected=true;
26
+ }finally{await rm(tmp,{recursive:true,force:true});}
27
+ if(!rejected) throw new Error("invalid version accepted: "+JSON.stringify(version));
28
+ results.push({version,expected:"FAIL",observed:"FAIL"});
29
+ }
30
+ console.log(JSON.stringify({status:"PASS",cases:results},null,2));
@@ -0,0 +1,22 @@
1
+ import { mkdtemp, rm } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { promisify } from "node:util";
5
+ import { execFile } from "node:child_process";
6
+
7
+ const ex = promisify(execFile);
8
+ const creator = process.argv[2];
9
+ if (!creator) throw new Error("usage: node test-skill-creator-semver.mjs <create-skill.mjs>");
10
+ const tmp = await mkdtemp(path.join(os.tmpdir(), "deadbyte-skill-creator-semver-"));
11
+ try {
12
+ const r = await ex(process.execPath, [creator, tmp, "semver-regression-fixture", "1.0.0"], {
13
+ timeout: 30000,
14
+ maxBuffer: 1024 * 1024
15
+ });
16
+ if (!r.stdout.includes("semver-regression-fixture")) {
17
+ throw new Error("create-skill returned success without fixture path");
18
+ }
19
+ console.log(JSON.stringify({status:"PASS", version:"1.0.0", stdout:r.stdout.trim()}));
20
+ } finally {
21
+ await rm(tmp, {recursive:true, force:true});
22
+ }
@@ -137,6 +137,13 @@ export const R36_CAPABILITY_ENTRIES=Object.freeze([
137
137
  gateway('observation.process_list','machine_process_list','machine_observe','process_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
138
138
  gateway('observation.system_status','machine_system_status','machine_observe','system_status','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
139
139
  gateway('observation.hardware_query','machine_hardware_query','machine_observe','hardware_query','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
140
+ gateway('observation.registry_query','machine_windows_query','machine_observe','registry_query','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
141
+ gateway('observation.service_list','machine_windows_query','machine_observe','service_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
142
+ gateway('observation.driver_list','machine_windows_query','machine_observe','driver_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
143
+ gateway('observation.socket_list','machine_windows_query','machine_observe','socket_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
144
+ gateway('observation.volume_list','machine_windows_query','machine_observe','volume_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
145
+ gateway('observation.usb_list','machine_windows_query','machine_observe','usb_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
146
+ gateway('observation.wsl_list','machine_windows_query','machine_observe','wsl_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
140
147
 
141
148
  gateway('desktop.status','desktop_status','desktop_control','status','DEADBYTE_DESKTOP_RUNTIME','none',{receipt_class:'desktop'}),
142
149
  gateway('desktop.observe','desktop_observe','desktop_control','observe','DEADBYTE_DESKTOP_RUNTIME','none',{receipt_class:'desktop'}),
@@ -67,6 +67,13 @@ const CAPABILITY_OPERATIONS = Object.freeze({
67
67
  'observation.process_list': ['process_list'],
68
68
  'observation.system_status': ['system_status'],
69
69
  'observation.hardware_query': ['hardware_query'],
70
+ 'observation.registry_query': ['registry_query'],
71
+ 'observation.service_list': ['service_list'],
72
+ 'observation.driver_list': ['driver_list'],
73
+ 'observation.socket_list': ['socket_list'],
74
+ 'observation.volume_list': ['volume_list'],
75
+ 'observation.usb_list': ['usb_list'],
76
+ 'observation.wsl_list': ['wsl_list'],
70
77
  'desktop.status': ['status'],
71
78
  'desktop.observe': ['observe'],
72
79
  'desktop.target': ['target'],
@@ -55,7 +55,14 @@ export const machineObserveInputSchema=z.discriminatedUnion('op',[
55
55
  z.strictObject({op:z.literal('search_cancel'),search_id:hex64}),
56
56
  z.strictObject({op:z.literal('process_list'),limit:z.number().int().min(1).max(256).default(100),include_external:z.boolean().default(false),cursor:z.string().max(512).nullable().default(null)}),
57
57
  z.strictObject({op:z.literal('system_status')}),
58
- z.strictObject({op:z.literal('hardware_query'),query:z.enum(['cpu','gpu','memory','storage','network','pci'])})
58
+ z.strictObject({op:z.literal('hardware_query'),query:z.enum(['cpu','gpu','memory','storage','network','pci'])}),
59
+ z.strictObject({op:z.literal('registry_query'),hive:z.enum(['HKLM','HKCU']),key:z.string().min(1).max(1024),value:z.string().max(260).nullable().default(null)}),
60
+ z.strictObject({op:z.literal('service_list')}),
61
+ z.strictObject({op:z.literal('driver_list')}),
62
+ z.strictObject({op:z.literal('socket_list')}),
63
+ z.strictObject({op:z.literal('volume_list')}),
64
+ z.strictObject({op:z.literal('usb_list')}),
65
+ z.strictObject({op:z.literal('wsl_list')})
59
66
  ]);
60
67
 
61
68
  const capabilityManifestInputSchema=z.strictObject({});
@@ -119,7 +126,14 @@ async function machineObserve(args,machineRuntime,observationRuntime){
119
126
  search_cancel:r=>r.searchCancel({searchId:args.search_id}),
120
127
  process_list:r=>r.processList({limit:args.limit,includeExternal:args.include_external,cursor:args.cursor}),
121
128
  system_status:r=>r.systemStatus(),
122
- hardware_query:r=>r.hardwareQuery({query:args.query})
129
+ hardware_query:r=>r.hardwareQuery({query:args.query}),
130
+ registry_query:r=>r.windowsQuery({query:'registry',hive:args.hive,key:args.key,value:args.value}),
131
+ service_list:r=>r.windowsQuery({query:'services'}),
132
+ driver_list:r=>r.windowsQuery({query:'drivers'}),
133
+ socket_list:r=>r.windowsQuery({query:'sockets'}),
134
+ volume_list:r=>r.windowsQuery({query:'volumes'}),
135
+ usb_list:r=>r.windowsQuery({query:'usb'}),
136
+ wsl_list:r=>r.windowsQuery({query:'wsl'})
123
137
  };
124
138
  return invoke(args.op,observationRuntime,'observation',map[args.op]);
125
139
  }
@@ -13,7 +13,7 @@ import { openRemoteResultJournal, parseRemoteResultCursor } from './remote-resul
13
13
  import { DEADBYTE_VERSION } from './version.mjs';
14
14
  import { parseActivePointer } from './release-slot.mjs';
15
15
  import { printDoctor } from './doctor-r45.mjs';
16
- import { createLiveTimelineTracker, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine } from './remote-live-log.mjs';
16
+ import { createLiveTimelineFilter, createLiveTimelineTracker, createLiveTimelineWriter, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine } from './remote-live-log.mjs';
17
17
  import { prepareRemoteConsole, remoteDisplayText } from './remote-console.mjs';
18
18
 
19
19
  const REMOTE_COMMANDS=new Set([
@@ -46,6 +46,8 @@ function printHelp(){
46
46
  ` npx -y ${exact} mcp # stdio MCP server for local clients`,
47
47
  ` npx -y ${exact} remote # unified foreground device session + live timeline`,
48
48
  ` npx -y ${exact} remote --resume <cursor> # replay missed results, then reconnect`,
49
+ ` npx -y ${exact} remote --trace # show trace/span identity without payload detail`,
50
+ ` npx -y ${exact} remote --filter <expr> # presentation filter: tool/category/status/trace`,
49
51
  ` npx -y ${exact} remote status`,
50
52
  ` npx -y ${exact} remote restart`,
51
53
  ` npx -y ${exact} supervisor status`,
@@ -71,6 +73,21 @@ async function readActiveRuntimeBinding(root){
71
73
  try{return parseActivePointer(await readFile(file,'utf8'));}
72
74
  catch(error){if(error?.code==='ENOENT') return null;throw new Error(`active runtime pointer invalid: ${boundedSingleLine(error?.message||error,512)}`);}
73
75
  }
76
+ async function readLiveRootPaths(root){
77
+ const active=await readActiveRuntimeBinding(root);
78
+ if(!active) return Object.freeze({});
79
+ const file=path.join(active.generation_root,'coding-policy.json');
80
+ let raw;
81
+ try{raw=JSON.parse(await readFile(file,'utf8'));}
82
+ catch(error){if(error?.code==='ENOENT') return Object.freeze({});throw new Error(`live root policy invalid: ${boundedSingleLine(error?.message||error,512)}`);}
83
+ if(raw?.schema!=='deadbyte.coding-policy.v1'||!raw.roots||typeof raw.roots!=='object'||Array.isArray(raw.roots)) throw new Error('live root policy schema invalid');
84
+ const roots={};
85
+ for(const [id,spec] of Object.entries(raw.roots)){
86
+ if(!/^[A-Za-z0-9._-]{1,64}$/.test(id)||!spec||typeof spec!=='object'||typeof spec.path!=='string'||!path.isAbsolute(spec.path)) continue;
87
+ roots[id]=path.resolve(spec.path);
88
+ }
89
+ return Object.freeze(roots);
90
+ }
74
91
  async function reportVersionCoherence(root,{displayMode='ascii'}={}){
75
92
  const active=await readActiveRuntimeBinding(root);
76
93
  if(!active||active.version===DEADBYTE_VERSION) return active;
@@ -227,7 +244,22 @@ async function snapshotRemoteLogOffsets(root){
227
244
  }
228
245
  return offsets;
229
246
  }
230
- async function tailRemoteLogs(root,signal,{journal=null,offsets=null,activityTracker=null,verbose=false,displayMode='unicode'}={}){
247
+ function timelineFilterEvent(timeline){
248
+ if(!timeline) return null;
249
+ if(timeline.event_type==='tool.call') return {
250
+ event_type:'tool.request',request_id:timeline.request_id,tool:timeline.tool,args:timeline.arguments??{}
251
+ };
252
+ return {
253
+ event_type:'tool.result',request_id:timeline.request_id,tool:timeline.tool,
254
+ outcome:{
255
+ is_error:timeline.status==='error',
256
+ ...(Number.isFinite(Number(timeline.duration_ms))?{duration_ms:Number(timeline.duration_ms)}:{}),
257
+ ...(timeline.side_effect_state?{side_effect_state:timeline.side_effect_state}:{}),
258
+ ...(timeline.reason?{reason:timeline.reason}:{})
259
+ }
260
+ };
261
+ }
262
+ async function tailRemoteLogs(root,signal,{journal=null,offsets=null,activityTracker=null,timelineFilter=null,timelineWriter=null,verbose=false,displayMode='unicode'}={}){
231
263
  process.stdout.write(verbose
232
264
  ? '[LOG] Verbose live timeline. Full sanitized request detail enabled; Ctrl+C disconnects and revokes authority.\n'
233
265
  : '[LOG] Human live timeline. Transport IN/OUT hidden; full replay/audit detail remains on disk. Ctrl+C disconnects and revokes authority.\n');
@@ -244,10 +276,14 @@ async function tailRemoteLogs(root,signal,{journal=null,offsets=null,activityTra
244
276
  ? await journal.recordCall(timeline)
245
277
  : await journal.recordTerminal(timeline);
246
278
  if(recorded?.duplicate) continue;
279
+ const accepted=timelineFilter&&timeline?timelineFilter.accept(timelineFilterEvent(timeline)):true;
247
280
  const formatted=formatLogLine(file,line,{activityTracker,sequence:recorded?.event?.seq??null,verbose,displayMode});
248
- if(!formatted) continue;
281
+ if(!formatted||!accepted) continue;
249
282
  const output=Array.isArray(formatted)?formatted:[formatted];
250
- for(const item of output) if(item) process.stdout.write(`${item}\n`);
283
+ for(const item of output) if(item){
284
+ if(timelineWriter) timelineWriter.write(item,{event_type:timeline?.event_type??null,request_id:timeline?.request_id??null});
285
+ else process.stdout.write(`${item}\n`);
286
+ }
251
287
  }
252
288
  }
253
289
  try{await sleep(250,null,{signal});}catch(error){if(error?.name!=='AbortError') throw error;}
@@ -341,7 +377,7 @@ async function releaseRemoteSession(ctx,marker,{displayMode='unicode'}={}){
341
377
  const same=current?.schema==='deadbyte.remote-session.v2'&&current.owner_pid===marker.owner_pid&&current.session_nonce===marker.session_nonce;
342
378
  if(same) await rm(remoteSessionMarker(ctx.root),{force:true});
343
379
  }
344
- async function runRemoteSession({resumeCursor=null,verbose=false}={}){
380
+ async function runRemoteSession({resumeCursor=null,verbose=false,trace=false,filterExpression=''}={}){
345
381
  const display=prepareRemoteConsole();
346
382
  const displayMode=display.mode;
347
383
  const ctx=await remoteContext();
@@ -353,7 +389,10 @@ async function runRemoteSession({resumeCursor=null,verbose=false}={}){
353
389
  const resultJournal=await openRemoteResultJournal({
354
390
  filePath:remoteResultJournalPath(ctx.root,marker.result_session_id),sessionId:marker.result_session_id,maxEntries:2048
355
391
  });
356
- const activityTracker=createLiveTimelineTracker({verbose,displayMode});
392
+ const rootPaths=await readLiveRootPaths(ctx.root);
393
+ const activityTracker=createLiveTimelineTracker({verbose,displayMode,rootPaths,trace});
394
+ const timelineFilter=filterExpression?createLiveTimelineFilter(filterExpression):null;
395
+ const timelineWriter=createLiveTimelineWriter({stream:process.stdout,displayMode,rewrite:!verbose});
357
396
  const aborter=new AbortController();
358
397
  const abort=()=>aborter.abort();
359
398
  const watchStdin=!process.stdin.isTTY;
@@ -366,7 +405,7 @@ async function runRemoteSession({resumeCursor=null,verbose=false}={}){
366
405
  process.stdout.write(`${formatRemoteLifecycle('device_start',{displayMode})}\n`);
367
406
  process.stdout.write(`${formatRemoteLifecycle(resumeCursor!==null?'session_restored':'session_new',{displayMode})}\n`);
368
407
  const logOffsets=await snapshotRemoteLogOffsets(ctx.root);
369
- logTask=tailRemoteLogs(ctx.root,aborter.signal,{journal:resultJournal,offsets:logOffsets,activityTracker,verbose,displayMode});
408
+ logTask=tailRemoteLogs(ctx.root,aborter.signal,{journal:resultJournal,offsets:logOffsets,activityTracker,timelineFilter,timelineWriter,verbose,displayMode});
370
409
  process.stdout.write('[BOOT] Starting local + remote runtime...\n');
371
410
  const preflight=invokeController(ctx,'status',{capture:true,allowFailure:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
372
411
  let startText=preflight.stdout||'';
@@ -415,6 +454,7 @@ async function runRemoteSession({resumeCursor=null,verbose=false}={}){
415
454
  const wasAborted=aborter.signal.aborted;
416
455
  aborter.abort();
417
456
  if(logTask) await logTask.catch(()=>{});
457
+ timelineWriter.flush();
418
458
  for(const line of activityTracker.reconcileInterrupted('session_end')) process.stdout.write(`${line}\n`);
419
459
  process.stdout.write(`${activityTracker.summary()}\n`);
420
460
  process.removeListener('SIGINT',abort);process.removeListener('SIGTERM',abort);
@@ -426,14 +466,29 @@ async function runRemoteSession({resumeCursor=null,verbose=false}={}){
426
466
  }
427
467
  }
428
468
  async function runRemote(args){
429
- if(args.length===0){await runRemoteSession();return;}
430
- const verbose=args.includes('--verbose')||args.includes('-v');
431
- const filtered=args.filter(arg=>arg!=='--verbose'&&arg!=='-v');
432
- if(filtered.length===0){await runRemoteSession({verbose});return;}
433
- if(filtered.length===2&&filtered[0]==='--resume'){await runRemoteSession({resumeCursor:filtered[1],verbose});return;}
434
- if(verbose) throw new Error('remote --verbose/-v is valid only for foreground session or --resume <cursor>');
435
- if(filtered.length>1) throw new Error('remote accepts one controller command or --resume <cursor>');
436
- const command=filtered[0];if(!REMOTE_COMMANDS.has(command)) throw new Error(`unsupported remote command '${command}'`);
469
+ let verbose=false,trace=false,filterExpression='',resumeCursor=null;
470
+ const positional=[];
471
+ for(let index=0;index<args.length;index++){
472
+ const arg=args[index];
473
+ if(arg==='--verbose'||arg==='-v'){verbose=true;continue;}
474
+ if(arg==='--trace'){trace=true;continue;}
475
+ if(arg==='--filter'){
476
+ if(filterExpression) throw new Error('remote --filter may be specified once');
477
+ if(index+1>=args.length) throw new Error('remote --filter requires an expression');
478
+ filterExpression=args[++index];continue;
479
+ }
480
+ if(arg==='--resume'){
481
+ if(resumeCursor!==null) throw new Error('remote --resume may be specified once');
482
+ if(index+1>=args.length) throw new Error('remote --resume requires a cursor');
483
+ resumeCursor=args[++index];continue;
484
+ }
485
+ positional.push(arg);
486
+ }
487
+ const presentation=verbose||trace||Boolean(filterExpression)||resumeCursor!==null;
488
+ if(positional.length===0){await runRemoteSession({resumeCursor,verbose,trace,filterExpression});return;}
489
+ if(presentation) throw new Error('remote presentation flags are valid only for foreground session');
490
+ if(positional.length>1) throw new Error('remote accepts one controller command');
491
+ const command=positional[0];if(!REMOTE_COMMANDS.has(command)) throw new Error(`unsupported remote command '${command}'`);
437
492
  const ctx=await remoteContext();
438
493
  await reportVersionCoherence(ctx.root,{displayMode:'ascii'});
439
494
  const result=invokeController(ctx,command,{allowFailure:true});
@@ -338,7 +338,7 @@ async function atomicWrite(target, bytes, expectedSha256, authorityCheck = null)
338
338
  assertExpected: checkExpectedFile, authorityCheck, tempTag: 'deadbyte-machine-fs' });
339
339
  }
340
340
 
341
- export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath, readWorkerPath = machineFsReadWorkerPath }) {
341
+ export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath, readWorkerPath = machineFsReadWorkerPath, listEntryLstat = lstat, listEntrySha256File = sha256File }) {
342
342
  if (!policy) throw new Error('machine fs runtime requires coding policy');
343
343
  if (!signingKeyPath || !verifyKeyPath) throw new Error('machine fs runtime requires trust key paths');
344
344
 
@@ -384,9 +384,22 @@ export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath,
384
384
  const rel = found.relative === '.' ? child.name : `${found.relative}/${child.name}`;
385
385
  if (child.isSymbolicLink()) return { path: rel, kind: 'reparse', bytes: 0, mtime_utc: null, sha256: null };
386
386
  const childPath = path.join(found.resolved, child.name);
387
- const childInfo = await lstat(childPath);
387
+ let childInfo;
388
+ try { childInfo = await listEntryLstat(childPath); }
389
+ catch (error) {
390
+ const code = typeof error?.code === 'string' && /^[A-Z0-9_]+$/.test(error.code) ? error.code : 'UNKNOWN';
391
+ return { path: rel, kind: 'unreadable', bytes: 0, mtime_utc: null, sha256: null, error: { code, operation: 'lstat' } };
392
+ }
388
393
  const kind = kindOf(childInfo);
389
- const digest = hashFiles && kind === 'file' ? await sha256File(childPath) : null;
394
+ let digest = null;
395
+ if (hashFiles && kind === 'file') {
396
+ try { digest = await listEntrySha256File(childPath); }
397
+ catch (error) {
398
+ const code = typeof error?.code === 'string' && /^[A-Z0-9_]+$/.test(error.code) ? error.code : 'UNKNOWN';
399
+ return { path: rel, kind, bytes: childInfo.size, mtime_utc: childInfo.mtime.toISOString(), sha256: null,
400
+ error: { code, operation: 'sha256' } };
401
+ }
402
+ }
390
403
  return { path: rel, kind, bytes: kind === 'file' ? childInfo.size : 0, mtime_utc: childInfo.mtime.toISOString(), sha256: digest };
391
404
  }));
392
405
  const nextCursor = filtered.length > selected.length && selected.length
@@ -1,7 +1,7 @@
1
1
  import * as z from 'zod/v4';
2
2
  import {
3
3
  observationHardwareQueryOutputSchema,observationProcessListOutputSchema,observationSearchCancelOutputSchema,observationSearchPageOutputSchema,
4
- observationSearchStartOutputSchema,observationSearchStatusOutputSchema,observationStructuredResult,observationSystemStatusOutputSchema
4
+ observationSearchStartOutputSchema,observationSearchStatusOutputSchema,observationStructuredResult,observationSystemStatusOutputSchema,observationWindowsQueryOutputSchema
5
5
  } from './observation-output-contracts.mjs';
6
6
 
7
7
  const hex64=z.string().regex(/^[0-9a-f]{64}$/);
@@ -45,4 +45,16 @@ export function registerObservationTools(server,{runtime}){
45
45
  description:'Read one typed native hardware snapshot (CPU, GPU, memory, storage, network, or PCI) with signed observation evidence. No shell or Host authority is used.',
46
46
  inputSchema:z.strictObject({query:z.enum(['cpu','gpu','memory','storage','network','pci'])}),outputSchema:observationHardwareQueryOutputSchema
47
47
  },async args=>{try{return ok(observationHardwareQueryOutputSchema,await runtime.hardwareQuery({query:args.query}));}catch(error){return fail(observationHardwareQueryOutputSchema,error);}});
48
+ server.registerTool('machine_windows_query',{
49
+ description:'Read one typed native Windows snapshot for Registry, services, drivers, sockets, volumes, USB, or WSL. No shell parsing is used.',
50
+ inputSchema:z.discriminatedUnion('query',[
51
+ z.strictObject({query:z.literal('registry'),hive:z.enum(['HKLM','HKCU']),key:z.string().min(1).max(1024),value:z.string().max(260).nullable().default(null)}),
52
+ z.strictObject({query:z.literal('services')}),
53
+ z.strictObject({query:z.literal('drivers')}),
54
+ z.strictObject({query:z.literal('sockets')}),
55
+ z.strictObject({query:z.literal('volumes')}),
56
+ z.strictObject({query:z.literal('usb')}),
57
+ z.strictObject({query:z.literal('wsl')})
58
+ ]),outputSchema:observationWindowsQueryOutputSchema
59
+ },async args=>{try{return ok(observationWindowsQueryOutputSchema,await runtime.windowsQuery(args));}catch(error){return fail(observationWindowsQueryOutputSchema,error);}});
48
60
  }
@@ -55,6 +55,14 @@ export const observationHardwareQueryOutputSchema=statusContract({
55
55
  ...Object.fromEntries(Object.entries(evidenceShape).map(([k,v])=>[k,v.optional()]))
56
56
  },['observed_at_utc','query','data','request_sha256','result_head_sha256','receipt_id','receipt_sha256']);
57
57
 
58
+ export const observationWindowsQueryOutputSchema=statusContract({
59
+ observed_at_utc:z.string().min(1).optional(),
60
+ query:z.enum(['registry','services','drivers','sockets','volumes','usb','wsl']).optional(),
61
+ data:z.record(z.string(),z.json()).optional(),
62
+ observer_sha256:hex64.optional(),
63
+ ...Object.fromEntries(Object.entries(evidenceShape).map(([k,v])=>[k,v.optional()]))
64
+ },['observed_at_utc','query','data','request_sha256','result_head_sha256','receipt_id','receipt_sha256']);
65
+
58
66
  export function observationStructuredResult(schema,value,isError=false){
59
67
  return structuredToolResult(schema,value,isError);
60
68
  }
@@ -15,6 +15,8 @@ const ZERO='0'.repeat(64);
15
15
  const HEX64=/^[0-9a-f]{64}$/;
16
16
  const ID=/^[A-Za-z0-9._-]{1,64}$/;
17
17
  const HARDWARE_QUERIES=new Set(['cpu','gpu','memory','storage','network','pci']);
18
+ const WINDOWS_QUERIES=new Set(['registry','services','drivers','sockets','volumes','usb','wsl']);
19
+ const REGISTRY_HIVES=new Set(['HKLM','HKCU']);
18
20
  const sha256Bytes=bytes=>createHash('sha256').update(bytes).digest('hex');
19
21
  const sha256Object=value=>sha256Bytes(Buffer.from(canonicalJson(value),'utf8'));
20
22
  const nowUtc=()=>new Date().toISOString();
@@ -131,12 +133,49 @@ async function defaultHardwareSnapshotProvider(query){
131
133
  }
132
134
  return {...parsed,observer_sha256:executableSha256};
133
135
  }
136
+ function normalizeWindowsQueryArgs(input={}){
137
+ const query=String(input.query??'');
138
+ if(!WINDOWS_QUERIES.has(query)) throw new Error('windows query must be one of registry,services,drivers,sockets,volumes,usb,wsl');
139
+ if(query==='registry'){
140
+ const hive=String(input.hive??'');
141
+ const key=String(input.key??'');
142
+ const value=input.value===null||input.value===undefined?null:String(input.value);
143
+ if(!REGISTRY_HIVES.has(hive)) throw new Error('registry hive must be HKLM or HKCU');
144
+ if(key.length<1||key.length>1024||key.includes('\0')) throw new Error('registry key must contain 1-1024 non-NUL characters');
145
+ if(value!==null&&(value.length>260||value.includes('\0'))) throw new Error('registry value must be null or at most 260 non-NUL characters');
146
+ return {query,hive,key,value};
147
+ }
148
+ const extra=Object.keys(input).filter(k=>k!=='query'&&input[k]!==undefined&&input[k]!==null);
149
+ if(extra.length) throw new Error(`windows query '${query}' does not accept additional selectors`);
150
+ return {query};
151
+ }
152
+ async function defaultWindowsSnapshotProvider(input){
153
+ if(process.platform!=='win32') throw new Error('native Windows observation is supported on win32 only');
154
+ const args=normalizeWindowsQueryArgs(input);
155
+ const executable=fileURLToPath(new URL('../bin/deadbyte-hardware-observer-r45.exe',import.meta.url));
156
+ const executableSha256=sha256Bytes(await readFile(executable));
157
+ const argv=[args.query];
158
+ if(args.query==='registry'){
159
+ argv.push(args.hive,args.key);
160
+ if(args.value!==null) argv.push(args.value);
161
+ }
162
+ const result=spawnSync(executable,argv,{encoding:'utf8',windowsHide:true,shell:false,timeout:15000,maxBuffer:16*1024*1024});
163
+ if(result.error) throw result.error;
164
+ if(result.status!==0) throw new Error(`Windows observation failed query=${args.query}: ${String(result.stderr??result.stdout??'').trim()}`);
165
+ let parsed;
166
+ try{parsed=JSON.parse(String(result.stdout??'').trim());}
167
+ catch{throw new Error(`Windows observation returned invalid JSON query=${args.query}`);}
168
+ if(parsed?.schema!=='deadbyte.windows-query.v1'||parsed?.query!==args.query||!parsed.data||typeof parsed.data!=='object'||Array.isArray(parsed.data)){
169
+ throw new Error(`Windows observation contract mismatch query=${args.query}`);
170
+ }
171
+ return {...parsed,observer_sha256:executableSha256};
172
+ }
134
173
  function requestSha(policy,operation,args){
135
174
  return sha256Object({schema:'deadbyte.observation-request.v1',policy_sha256:policy.policy_sha256,operation,args});
136
175
  }
137
176
  export function createObservationRuntime({policy,signingKeyPath,verifyKeyPath,processRuntime=null,stateRoot=path.join(os.homedir(),'.deadbyte-mcp'),
138
177
  processSnapshotProvider=defaultProcessSnapshotProvider,systemSnapshotProvider=defaultSystemSnapshotProvider,
139
- ownedRuntimePidProvider=defaultOwnedRuntimePidProvider,hardwareSnapshotProvider=defaultHardwareSnapshotProvider,
178
+ ownedRuntimePidProvider=defaultOwnedRuntimePidProvider,hardwareSnapshotProvider=defaultHardwareSnapshotProvider,windowsSnapshotProvider=defaultWindowsSnapshotProvider,
140
179
  searchSessionTtlMs=5*60*1000,maxSearchSessions=128,clockMs=()=>Date.now()}={}){
141
180
  if(!policy||!signingKeyPath||!verifyKeyPath) throw new Error('observation runtime requires policy and trust keys');
142
181
  if(!Number.isInteger(searchSessionTtlMs)||searchSessionTtlMs<10||searchSessionTtlMs>24*60*60*1000) throw new Error('searchSessionTtlMs out of range');
@@ -337,5 +376,18 @@ export function createObservationRuntime({policy,signingKeyPath,verifyKeyPath,pr
337
376
  const receipt=await writeReceipt(runtime,'machine_hardware_query',req,head,{observed_at_utc:observedAt,query,observer_sha256:result.observer_sha256??null});
338
377
  return {...result,request_sha256:req,result_head_sha256:head,...receipt};
339
378
  }
340
- return Object.freeze({policy,searchStart,searchPage,searchStatus,searchCancel,processList,systemStatus,hardwareQuery});
379
+ async function windowsQuery(input={}){
380
+ const args=normalizeWindowsQueryArgs(input);
381
+ const observedAt=nowUtc();
382
+ const snapshot=await windowsSnapshotProvider(args);
383
+ if(!snapshot||snapshot.schema!=='deadbyte.windows-query.v1'||snapshot.query!==args.query||!snapshot.data||typeof snapshot.data!=='object'||Array.isArray(snapshot.data)){
384
+ throw new Error('windows query provider contract mismatch');
385
+ }
386
+ const result={status:'ok',observed_at_utc:observedAt,query:args.query,data:snapshot.data};
387
+ if(typeof snapshot.observer_sha256==='string'&&HEX64.test(snapshot.observer_sha256)) result.observer_sha256=snapshot.observer_sha256;
388
+ const req=requestSha(policy,'machine_windows_query',args); const head=sha256Object(result);
389
+ const receipt=await writeReceipt(runtime,'machine_windows_query',req,head,{observed_at_utc:observedAt,query:args.query,observer_sha256:result.observer_sha256??null});
390
+ return {...result,request_sha256:req,result_head_sha256:head,...receipt};
391
+ }
392
+ return Object.freeze({policy,searchStart,searchPage,searchStatus,searchCancel,processList,systemStatus,hardwareQuery,windowsQuery});
341
393
  }