deadbyte-mcp 0.15.67 → 0.15.69

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 (87) hide show
  1. package/MANIFEST.SHA256 +85 -40
  2. package/PROVENANCE.json +46 -24
  3. package/README.txt +46 -33
  4. package/RELEASE.json +44 -17
  5. package/SBOM.cdx.json +2 -2
  6. package/TEST-MATRIX.json +25 -2
  7. package/bridge/package.json +1 -1
  8. package/controller/deadbyte-controller.ps1 +65 -1
  9. package/controller/deadbyte-process-policy.json +4 -4
  10. package/docs/R184-package-lock.json +97 -0
  11. package/docs/R186-OWNER-CONSENT-HANDOFF.txt +87 -0
  12. package/package.json +3 -2
  13. package/scripts/install-r186-owner-trust.ps1 +59 -0
  14. package/scripts/r173-protected-preflight.mjs +42 -0
  15. package/scripts/r175-focused-runner.mjs +38 -0
  16. package/scripts/r176-log-preview.mjs +18 -0
  17. package/scripts/r177-owner-trust-inventory.ps1 +36 -0
  18. package/scripts/r178-full-regression.mjs +21 -0
  19. package/scripts/r178-npm-ci.mjs +10 -0
  20. package/scripts/r178-npm-preflight.mjs +16 -0
  21. package/scripts/r178-release-gates-readonly.mjs +9 -0
  22. package/scripts/r178-shard-regression.mjs +25 -0
  23. package/scripts/r179-predecessor-registry.mjs +24 -0
  24. package/scripts/r179-protected-inventory.ps1 +21 -0
  25. package/scripts/r186-focused-runner.mjs +22 -0
  26. package/scripts/r186-owner-anchor-inspect.mjs +6 -0
  27. package/scripts/r186-owner-consent-check.mjs +86 -0
  28. package/scripts/r186-owner-grant-cli.mjs +45 -0
  29. package/scripts/r186-owner-key-local.mjs +22 -0
  30. package/scripts/r186-owner-review-export.mjs +34 -0
  31. package/src/activation-authority-proof-r75.mjs +28 -4
  32. package/src/activation-preflight-r65.mjs +2 -1
  33. package/src/deadbyte-cli.mjs +69 -56
  34. package/src/r174-owner-webauthn.mjs +176 -0
  35. package/src/r175-foreground-boundary.mjs +28 -0
  36. package/src/r186-owner-anchor.mjs +12 -0
  37. package/src/r186-owner-consent-preflight.mjs +63 -0
  38. package/src/r186-owner-grant-broker.mjs +96 -0
  39. package/src/r186-owner-local-signing.mjs +25 -0
  40. package/src/r186-owner-request.mjs +40 -0
  41. package/src/r186-owner-review-export.mjs +49 -0
  42. package/src/remote-live-log.mjs +81 -5
  43. package/src/remote-session-guardian.mjs +23 -0
  44. package/src/version.mjs +1 -1
  45. package/test/helpers/r175-active-pointer.mjs +28 -0
  46. package/test/helpers/release-lock.mjs +12 -0
  47. package/test/r129-sdk-security-baseline.test.mjs +2 -1
  48. package/test/r130-release-security-contract.test.mjs +11 -10
  49. package/test/r132-release-identity.test.mjs +12 -11
  50. package/test/r153-prepublish-identity.test.mjs +1 -1
  51. package/test/r155-default-live-log.test.mjs +17 -16
  52. package/test/r160-package-decision-manifest.test.mjs +1 -1
  53. package/test/r160-release-contract.test.mjs +4 -5
  54. package/test/r163-isolated-slot-rollback-restore.test.mjs +6 -6
  55. package/test/r165-live-cli-skew.test.mjs +6 -6
  56. package/test/r167-release-contract.test.mjs +15 -13
  57. package/test/r168-protected-entrypoint.test.mjs +14 -11
  58. package/test/r168-release-contract.test.mjs +4 -3
  59. package/test/r168-remote-one-shot-coherence.test.mjs +2 -1
  60. package/test/r172-release-contract.test.mjs +8 -8
  61. package/test/r173-release-contract.test.mjs +33 -0
  62. package/test/r174-owner-webauthn.test.mjs +144 -0
  63. package/test/r175-cli-flags.test.mjs +23 -0
  64. package/test/r175-foreground-boundary.test.mjs +87 -0
  65. package/test/r175-guardian-owner-boundary.test.mjs +56 -0
  66. package/test/r176-pretty-live-log.test.mjs +54 -0
  67. package/test/r177-windows-absolute-live-path.test.mjs +37 -0
  68. package/test/r179-absolute-path-live-log.test.mjs +40 -0
  69. package/test/r179-release-contract.test.mjs +62 -0
  70. package/test/r186-controller-owner-grant.test.mjs +20 -0
  71. package/test/r186-owner-consent-preflight.test.mjs +98 -0
  72. package/test/r186-owner-enrollment.test.mjs +20 -0
  73. package/test/r186-owner-grant-broker.test.mjs +85 -0
  74. package/test/r186-owner-local-signing.test.mjs +30 -0
  75. package/test/r186-owner-request.test.mjs +28 -0
  76. package/test/r186-owner-review-export.test.mjs +52 -0
  77. package/test/r48-release-contract.test.mjs +2 -1
  78. package/test/r51-runtime-coherence.test.mjs +4 -2
  79. package/test/r58-cleanup-retention.test.mjs +5 -12
  80. package/test/r58-concurrent-recovery.test.mjs +5 -1
  81. package/test/r58-stale-owner.test.mjs +9 -3
  82. package/test/r59-tool-surface-compat.test.mjs +1 -1
  83. package/test/r75-authority-evidence.test.mjs +32 -0
  84. package/test/remote-live-log-r37.test.mjs +2 -2
  85. package/test/remote-live-log-recovery-r36.test.mjs +1 -1
  86. package/test/remote-session-cli.test.mjs +92 -126
  87. package/node_modules/which/CHANGELOG.md +0 -166
@@ -5,6 +5,9 @@ import path from 'node:path';
5
5
  import {fileURLToPath} from 'node:url';
6
6
  import {spawn} from 'node:child_process';
7
7
  import {setTimeout as delay} from 'node:timers/promises';
8
+ import {installExactActivePointer} from './helpers/r175-active-pointer.mjs';
9
+
10
+ const packageRoot=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
8
11
 
9
12
  test('R75 simultaneous owner-unverified recovery attempts both retain authority and marker', {timeout:30000},async()=>{
10
13
  const base='D:/Projects/deadbyte-mcp/testing/0.15.35/fixtures';await mkdir(base,{recursive:true});
@@ -41,6 +44,7 @@ switch($Command){
41
44
  }
42
45
  exit 0
43
46
  `);
47
+ await installExactActivePointer({stateRoot:root,packageRoot,launcherPath:path.join(root,'bin','DEADBYTE-MCP.ps1')});
44
48
  const children=[],outputs=[];
45
49
  try{
46
50
  for(let i=0;i<2;i++){
@@ -52,7 +56,7 @@ exit 0
52
56
  const commands=await readFile(trace,'utf8').catch(error=>error.code==='ENOENT'?'':Promise.reject(error));
53
57
  await writeFile(path.join(root,'observation.json'),JSON.stringify({commands,exits,outputs:outputs.map(o=>({stdout:o.stdout,stderr:o.stderr})),simulation:'concurrent owner-unverified stale marker'}),{flag:'wx'});
54
58
  assert.ok(exits.every(code=>code!==0));
55
- for(const output of outputs)assert.match(output.stderr,/ownership(?: identity)? unverified.*marker retained|already claimed \(startup\/recovery lock\)/i);
59
+ for(const output of outputs)assert.match(output.stderr,/R179_LEGACY_FOREGROUND_RECOVERY_REQUIRES_EXPLICIT_OWNER_COMMAND|already claimed \(startup\/recovery lock\)/i);
56
60
  assert.equal(commands,'','neither attempt may touch authority');
57
61
  assert.equal(JSON.parse(await readFile(markerPath,'utf8')).pid,2147483000);
58
62
  }finally{
@@ -5,12 +5,16 @@ import path from 'node:path';
5
5
  import {fileURLToPath} from 'node:url';
6
6
  import {spawn} from 'node:child_process';
7
7
  import {queryForegroundIdentity} from '../src/foreground-process-identity.mjs';
8
+ import {installExactActivePointer} from './helpers/r175-active-pointer.mjs';
9
+
10
+ const packageRoot=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
8
11
 
9
12
  test('R58 inconsistent owner PID binding cannot trigger stale-session authority cleanup', {timeout:20000}, async()=>{
10
13
  const base='D:/Projects/deadbyte-mcp/testing/0.15.35/fixtures';await mkdir(base,{recursive:true});
11
14
  const root=await mkdtemp(path.join(base,'owner-binding-'));await mkdir(path.join(root,'runtime'));await mkdir(path.join(root,'bin'));
12
15
  const trace=path.join(root,'trace.txt');
13
16
  await writeFile(path.join(root,'bin','DEADBYTE-MCP.ps1'),`param([string]$Command)\n[IO.File]::AppendAllText('${trace.replaceAll("'","''")}',$Command+[Environment]::NewLine)\nexit 0\n`);
17
+ await installExactActivePointer({stateRoot:root,packageRoot,launcherPath:path.join(root,'bin','DEADBYTE-MCP.ps1')});
14
18
  const identity=queryForegroundIdentity(process.pid);
15
19
  const marker={schema:'deadbyte.remote-session.v2',owner_pid:process.pid,guardian_pid:0,session_nonce:'c'.repeat(64),owner_identity:{...identity,pid:process.pid+1}};
16
20
  const markerPath=path.join(root,'runtime','remote-session.json');const bytes=JSON.stringify(marker);await writeFile(markerPath,bytes);
@@ -18,7 +22,7 @@ test('R58 inconsistent owner PID binding cannot trigger stale-session authority
18
22
  let stderr='';child.stderr.on('data',b=>{stderr+=b;});child.stdout.resume();
19
23
  const code=await new Promise(resolve=>child.once('close',resolve));
20
24
  await writeFile(path.join(root,'observation.json'),JSON.stringify({code,stderr}),{flag:'wx'});
21
- assert.notEqual(code,0);assert.match(stderr,/ownership identity|binding/i);
25
+ assert.notEqual(code,0);assert.match(stderr,/R179_LEGACY_FOREGROUND_RECOVERY_REQUIRES_EXPLICIT_OWNER_COMMAND/i);
22
26
  assert.equal(await readFile(trace,'utf8').catch(e=>e.code==='ENOENT'?'':Promise.reject(e)),'','inconsistent marker must cause no controller actions');
23
27
  assert.equal(await readFile(markerPath,'utf8'),bytes,'retain ambiguous ownership evidence');
24
28
  });
@@ -29,6 +33,7 @@ test('R58 live unrelated PID is not accepted as a remote foreground owner', {tim
29
33
  await mkdir(path.join(root,'runtime'));await mkdir(path.join(root,'bin'));
30
34
  const trace=path.join(root,'trace.txt');
31
35
  await writeFile(path.join(root,'bin','DEADBYTE-MCP.ps1'),`param([string]$Command)\n[IO.File]::AppendAllText('${trace.replaceAll("'","''")}',$Command+[Environment]::NewLine)\nexit 0\n`);
36
+ await installExactActivePointer({stateRoot:root,packageRoot,launcherPath:path.join(root,'bin','DEADBYTE-MCP.ps1')});
32
37
  // The test runner is alive but neither a remote CLI nor a guardian. This
33
38
  // represents a stale numeric PID now occupied by an unrelated process.
34
39
  const marker={schema:'deadbyte.remote-session.v2',owner_pid:process.pid,guardian_pid:process.pid,session_nonce:'a'.repeat(64),started_at_utc:'2000-01-01T00:00:00.000Z'};
@@ -41,7 +46,7 @@ test('R58 live unrelated PID is not accepted as a remote foreground owner', {tim
41
46
  // Fail-closed ambiguity is acceptable; do not kill the unrelated process or
42
47
  // mutate authority based on unverified ownership. Automatic convergence still
43
48
  // requires separately observed exact owner/guardian identity evidence.
44
- assert.match(stderr,/identity|ownership|ambiguous|unverified/i);
49
+ assert.match(stderr,/R179_LEGACY_FOREGROUND_RECOVERY_REQUIRES_EXPLICIT_OWNER_COMMAND/i);
45
50
  assert.deepEqual(JSON.parse(await readFile(path.join(root,'runtime','remote-session.json'),'utf8')),marker);
46
51
  assert.notEqual(code,0);
47
52
  });
@@ -52,6 +57,7 @@ test('R75 bound stale birth identity retains marker without touching a reused PI
52
57
  await mkdir(path.join(root,'runtime'));await mkdir(path.join(root,'bin'));
53
58
  const trace=path.join(root,'trace.txt');
54
59
  await writeFile(path.join(root,'bin','DEADBYTE-MCP.ps1'),`param([string]$Command,[switch]$Json)\n[IO.File]::AppendAllText('${trace.replaceAll("'","''")}',$Command+[Environment]::NewLine)\nif ($Command -eq 'status' -and $Json) { $off=@{configured=$true;armed=$false;reason='disarmed'}; @{schema='deadbyte.controller-observation.v1';state='OFFLINE';authority=@{process=$off;coding=$off;autonomous=$off;desktop=$off;host=$off}} | ConvertTo-Json -Depth 5 -Compress }\nexit 0\n`);
60
+ await installExactActivePointer({stateRoot:root,packageRoot,launcherPath:path.join(root,'bin','DEADBYTE-MCP.ps1')});
55
61
  const current=queryForegroundIdentity(process.pid);
56
62
  const stale={...current,creation_utc:'2000-01-01T00:00:00.000Z'};
57
63
  const marker={schema:'deadbyte.remote-session.v2',owner_pid:process.pid,guardian_pid:process.pid,session_nonce:'b'.repeat(64),owner_identity:stale,guardian_identity:stale};
@@ -62,7 +68,7 @@ test('R75 bound stale birth identity retains marker without touching a reused PI
62
68
  const code=await new Promise(resolve=>child.once('close',resolve));
63
69
  await writeFile(path.join(root,'observation.json'),JSON.stringify({code,stdout,stderr,simulation:'birth identity drift at a live unrelated numeric PID'}),{flag:'wx'});
64
70
  assert.notEqual(code,0);
65
- assert.match(stderr,/owner identity mismatch; marker retained/i);
71
+ assert.match(stderr,/R179_LEGACY_FOREGROUND_RECOVERY_REQUIRES_EXPLICIT_OWNER_COMMAND/i);
66
72
  assert.equal(await readFile(trace,'utf8').catch(error=>error.code==='ENOENT'?'':Promise.reject(error)),'','identity drift must cause no authority commands');
67
73
  assert.deepEqual(JSON.parse(await readFile(markerPath,'utf8')),marker);
68
74
  assert.equal(foregroundAlive(),true,'unrelated live process must not be killed');
@@ -83,7 +83,7 @@ test('R72 compact baseline permits only R64 read offset and authorized coding-on
83
83
  assert.deepEqual(diff.added,[]);
84
84
  assert.deepEqual(diff.removed,[]);
85
85
  const packageVersion=JSON.parse(await readFile(path.join(repoRoot,'package.json'),'utf8')).version;
86
- if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46','0.15.47','0.15.48','0.15.49','0.15.50','0.15.51','0.15.52','0.15.53','0.15.54','0.15.55','0.15.56','0.15.57','0.15.58','0.15.59','0.15.60','0.15.61','0.15.62','0.15.63','0.15.64','0.15.65','0.15.66','0.15.67'].includes(packageVersion)){
86
+ if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46','0.15.47','0.15.48','0.15.49','0.15.50','0.15.51','0.15.52','0.15.53','0.15.54','0.15.55','0.15.56','0.15.57','0.15.58','0.15.59','0.15.60','0.15.61','0.15.62','0.15.63','0.15.64','0.15.65','0.15.66','0.15.67','0.15.68','0.15.69'].includes(packageVersion)){
87
87
  assert.deepEqual([...diff.schema_changed].sort(),['machine_fs','native_diff_preview']);
88
88
  assert.equal(diff.unchanged.length,41);
89
89
  const prior=baseline.tools.find(tool=>tool.name==='machine_fs');
@@ -49,6 +49,38 @@ test('a present grant with controller armed=false is unverified, not clean',asyn
49
49
  }finally{await f.cleanup();}
50
50
  });
51
51
 
52
+ test('a signed transaction revoke proves epoch-revoked disarmed while captured grant bytes remain',async()=>{
53
+ const stateRoot=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r173-epoch-proof-'));
54
+ try{
55
+ const runtime=path.join(stateRoot,'runtime'),policies=path.join(stateRoot,'policies');
56
+ await mkdir(runtime);await mkdir(policies);
57
+ const policyPaths={},policySha256={},authority={};
58
+ let codingGrant=null,codingPolicyBytes=null;
59
+ for(const plane of planes){
60
+ const grant=path.join(runtime,`${plane}-grant.json`),policyPath=path.join(policies,`${plane}.json`);
61
+ const bytes=Buffer.from(JSON.stringify({schema:`deadbyte.${plane}-policy.v${plane==='host'?2:1}`,enabled:true,
62
+ [grantField[plane]]:grant,...(plane==='coding'?{roots:{project:{path:path.join(stateRoot,'project')}}}:{})}));
63
+ await writeFile(policyPath,bytes);policyPaths[plane]=policyPath;policySha256[plane]=sha(bytes);
64
+ authority[plane]={configured:true,armed:false,reason:plane==='coding'?'epoch-revoked':'disarmed'};
65
+ if(plane==='coding'){codingGrant=grant;codingPolicyBytes=bytes;}
66
+ }
67
+ const nonce='7'.repeat(64),grantBytes=Buffer.from(JSON.stringify({schema:'deadbyte.coding-grant.v2',
68
+ policy_sha256:sha(codingPolicyBytes),instance_nonce:nonce,root_ids:['project'],granted_at_utc:new Date().toISOString()}));
69
+ await writeFile(codingGrant,grantBytes);
70
+ const grantSha=sha(grantBytes),eventSha='8'.repeat(64);
71
+ const proof=await observeAuthorityEvidence({stateRoot,policyPaths,policySha256,authority,
72
+ epochStoreFactory:()=>({
73
+ auditHistory:async()=>({status:'PASS',verified_events:2}),
74
+ readHead:async()=>({action:'transaction_revoke',seq:2,event_sha256:eventSha,policy_sha256:sha(codingPolicyBytes),
75
+ grant_sha256:grantSha,grant_instance_nonce:nonce,update_id:'r65-'+'8'.repeat(32)}),
76
+ verifyGrant:async()=>({trusted:false,reason:'epoch-revoked',event_sha256:eventSha})
77
+ })});
78
+ assert.equal(proof.coding.state,'VERIFIED_DISARMED');
79
+ assert.equal(proof.coding.grant_absent,false);
80
+ assert.equal(proof.coding.transaction_revoked,true);
81
+ }finally{await rm(stateRoot,{recursive:true,force:true});}
82
+ });
83
+
52
84
  test('malformed policy cannot provide disarmed proof',async()=>{
53
85
  const f=await fixture();
54
86
  try{
@@ -73,8 +73,8 @@ test('R40 CLI exposes explicit verbose mode while keeping default live mode comp
73
73
  const src=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
74
74
  assert.match(src,/readLiveRootPaths\(ctx\.root\)/);
75
75
  assert.match(src,/createLiveTimelineTracker\(\{verbose,displayMode,rootPaths,trace,\.\.\.defaultForegroundTimelineOptions\(\{verbose\}\)\}\)/);
76
- assert.deepEqual(live.defaultForegroundTimelineOptions({verbose:false}),{commanderStyle:true,foldHeartbeats:false});
77
- assert.deepEqual(live.defaultForegroundTimelineOptions({verbose:true}),{commanderStyle:false,foldHeartbeats:false});
76
+ assert.deepEqual(live.defaultForegroundTimelineOptions({verbose:false}),{commanderStyle:false,foldHeartbeats:true,prettyStyle:true});
77
+ assert.deepEqual(live.defaultForegroundTimelineOptions({verbose:true}),{commanderStyle:false,foldHeartbeats:false,prettyStyle:false});
78
78
  assert.match(src,/createLiveTimelineFilter\(filterExpression\)/);
79
79
  assert.match(src,/createLiveTimelineWriter\(\{stream:process\.stdout,displayMode,rewrite:false\}\)/);
80
80
  assert.match(src,/arg==='--verbose'\|\|arg==='-v'/);
@@ -9,7 +9,7 @@ test('R36 lifecycle formatter exposes device/connect/ready/cleanup states with b
9
9
  assert.equal(formatRemoteLifecycle('session_new'),'[SESSION] New foreground session');
10
10
  assert.equal(formatRemoteLifecycle('session_restored'),'♻️ [SESSION] Previous result stream replayed');
11
11
  assert.equal(formatRemoteLifecycle('ready'),'✅ [READY] DEADBYTE MCP online');
12
- assert.equal(formatRemoteLifecycle('cleanup'),'🔐 [AUTH] Revoking session authority...');
12
+ assert.equal(formatRemoteLifecycle('cleanup'),'[SESSION] Closing observation session...');
13
13
  assert.throws(()=>formatRemoteLifecycle('invented'),/unknown remote lifecycle/i);
14
14
  });
15
15