deadbyte-mcp 0.15.45 → 0.15.47

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 (53) hide show
  1. package/MANIFEST.SHA256 +52 -27
  2. package/PROVENANCE.json +260 -257
  3. package/README.txt +22 -21
  4. package/RELEASE.json +366 -346
  5. package/SBOM.cdx.json +2 -2
  6. package/TEST-MATRIX.json +501 -503
  7. package/bin/deadbyte-kernel-lock-r74.exe +0 -0
  8. package/bridge/package.json +1 -1
  9. package/controller/deadbyte-controller.ps1 +20 -6
  10. package/controller/deadbyte-process-policy.json +7 -7
  11. package/docs/R73-CAPABILITY-GAP-MATRIX.json +17 -0
  12. package/docs/R73-EXECUTION-CONTRACT.md +11 -0
  13. package/docs/R73-IMPLEMENTATION-CHANGELOG.md +12 -0
  14. package/docs/R73-REMAINING-DEFECTS.md +9 -0
  15. package/docs/R74-KERNEL-LOCK-BINARY.json +11 -0
  16. package/native/deadbyte-kernel-lock-r74.cpp +65 -0
  17. package/package.json +2 -2
  18. package/scripts/build-kernel-lock-r74.ps1 +28 -0
  19. package/scripts/gate-windows-r72.ps1 +76 -0
  20. package/scripts/npm-prepublish-contract-r74.mjs +113 -0
  21. package/scripts/npm-release-r48.mjs +4 -0
  22. package/scripts/npm-release-readiness-r72.mjs +76 -0
  23. package/scripts/npm-release-readiness-r74.mjs +46 -0
  24. package/scripts/r73-atomic-benchmark.mjs +39 -0
  25. package/src/atomic-mutation.mjs +99 -9
  26. package/src/deadbyte-cli.mjs +722 -721
  27. package/src/kernel-lock-r74.mjs +125 -0
  28. package/src/launcher-isolation.mjs +58 -0
  29. package/src/remote-authority.mjs +75 -75
  30. package/src/remote-mcp-machine-smoke-client.mjs +84 -84
  31. package/src/runtime-update.mjs +2056 -2008
  32. package/src/staging-input-isolation.mjs +79 -0
  33. package/src/version.mjs +1 -1
  34. package/test/atomic-mutation.test.mjs +68 -2
  35. package/test/commander-schema-differential-r48.test.mjs +1 -1
  36. package/test/deferred-slot-operation-behavior.test.mjs +2 -2
  37. package/test/r58-cleanup-retention.test.mjs +3 -3
  38. package/test/r58-concurrent-recovery.test.mjs +3 -3
  39. package/test/r58-stale-owner.test.mjs +1 -1
  40. package/test/r59-tool-surface-compat.test.mjs +2 -2
  41. package/test/r60-authority-coherence.test.mjs +10 -2
  42. package/test/r71-isolated-slot-rollback-restore.test.mjs +11 -11
  43. package/test/r71-release-identity.test.mjs +12 -10
  44. package/test/r72-launcher-isolation.test.mjs +99 -0
  45. package/test/r72-publication-fence.test.mjs +56 -0
  46. package/test/r72-publication-output-integrity.test.mjs +110 -0
  47. package/test/r72-remote-safe-contract.test.mjs +101 -0
  48. package/test/r72-staging-input-isolation.test.mjs +87 -0
  49. package/test/r72-staging-workspace-isolation.test.mjs +114 -0
  50. package/test/r73-package-boundary.test.mjs +22 -0
  51. package/test/r74-kernel-lock.test.mjs +150 -0
  52. package/test/r74-prepublish-contract.test.mjs +89 -0
  53. package/test/remote-session-cli.test.mjs +110 -64
@@ -0,0 +1,39 @@
1
+ import {createHash} from 'node:crypto';
2
+ import {mkdtemp,readFile,rm,writeFile} from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import {performance} from 'node:perf_hooks';
6
+ import {pathToFileURL} from 'node:url';
7
+
8
+ const baseline=process.argv[2];
9
+ if(!baseline||!path.isAbsolute(baseline))throw new Error('absolute verified predecessor source required');
10
+ const candidate=path.resolve('src/atomic-mutation.mjs');
11
+ const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
12
+ const modules={baseline:(await import(pathToFileURL(baseline))).atomicMutation,
13
+ candidate:(await import(pathToFileURL(candidate))).atomicMutation};
14
+ const rounds=[];
15
+ for(const order of [['baseline','candidate'],['candidate','baseline'],['baseline','candidate'],['candidate','baseline']]){
16
+ for(const label of order){
17
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r73-bench-'));
18
+ const target=path.join(root,'value.bin');
19
+ const initial=Buffer.from('before');await writeFile(target,initial);
20
+ const readState=async file=>{const bytes=await readFile(file);return {exists:true,sha256:sha(bytes)};};
21
+ let prior=sha(initial);
22
+ const times=[];
23
+ try{
24
+ for(let i=0;i<110;i++){
25
+ const next=Buffer.from(`bounded replacement ${i}`);
26
+ const start=performance.now();
27
+ await modules[label]({target,bytes:next,expected:prior,readState,
28
+ assertExpected(state,expected){if(state.sha256!==expected)throw new Error('benchmark identity drift');}});
29
+ const elapsed=performance.now()-start;
30
+ if(i>=10)times.push(elapsed);
31
+ prior=sha(next);
32
+ }
33
+ times.sort((a,b)=>a-b);
34
+ const at=q=>Number(times[Math.ceil(q*times.length)-1].toFixed(4));
35
+ rounds.push({label,n:times.length,p50_ms:at(.5),p95_ms:at(.95),max_ms:at(1)});
36
+ }finally{await rm(root,{recursive:true,force:true});}
37
+ }
38
+ }
39
+ console.log(JSON.stringify({schema:'deadbyte.r73-atomic-benchmark.v1',workload:'exact-precondition fsync+rename on isolated Windows scratch',rounds}));
@@ -1,6 +1,8 @@
1
1
  import { createHash, randomBytes } from 'node:crypto';
2
- import { open, rename, rm, stat } from 'node:fs/promises';
2
+ import { open, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
+ import {performance} from 'node:perf_hooks';
5
+ import {acquireKernelExclusiveLock, verifyKernelLockBinary} from './kernel-lock-r74.mjs';
4
6
 
5
7
  const sha256Bytes = bytes => createHash('sha256').update(bytes).digest('hex');
6
8
  const LOCK_WAIT_MS = 10;
@@ -9,20 +11,90 @@ const LOCK_STALE_MS = 120000;
9
11
 
10
12
  async function acquireMutationLock(target) {
11
13
  const lock = `${target}.deadbyte-mutation.lock`;
12
- const started = Date.now();
14
+ const kernelLock = `${target}.deadbyte-mutation.lock.kernel`;
15
+ const ownership = `${process.pid}:${Date.now()}:${randomBytes(16).toString('hex')}`;
16
+ const verified = await verifyKernelLockBinary();
17
+
18
+ if (verified.ok && process.env.DEADBYTE_R74_KERNEL_LOCK !== '0') {
19
+ try {
20
+ const existing = await readFile(lock, 'utf8').catch(() => null);
21
+ if (existing && existing.includes('abandoned')) {
22
+ throw new Error('atomic mutation lock abandoned; reconciliation required');
23
+ }
24
+ } catch (err) {
25
+ if (err.message.includes('abandoned')) throw err;
26
+ }
27
+
28
+ const releaseKernel = await acquireKernelExclusiveLock(kernelLock, {
29
+ timeoutMs: LOCK_TIMEOUT_MS,
30
+ pollMs: LOCK_WAIT_MS
31
+ });
32
+
33
+ try {
34
+ await writeFile(lock, ownership);
35
+ } catch (err) {
36
+ await releaseKernel().catch(() => {});
37
+ throw err;
38
+ }
39
+
40
+ return async () => {
41
+ let ownershipLost = false;
42
+ try {
43
+ const onDisk = await readFile(lock, 'utf8').catch(() => null);
44
+ if (onDisk !== ownership) {
45
+ ownershipLost = true;
46
+ }
47
+ } finally {
48
+ await releaseKernel().catch(() => {});
49
+ await rm(kernelLock, {force: true}).catch(() => {});
50
+ if (!ownershipLost) {
51
+ await rm(lock, {force: true}).catch(() => {});
52
+ }
53
+ }
54
+ if (ownershipLost) {
55
+ throw new Error('atomic mutation lock ownership lost');
56
+ }
57
+ };
58
+ }
59
+
60
+ const started = performance.now();
13
61
  while (true) {
14
62
  try {
15
63
  const handle = await open(lock, 'wx', 0o600);
16
- await handle.writeFile(`${process.pid}\\n${Date.now()}\\n`);
17
- await handle.sync();
18
- await handle.close();
19
- return async () => { await rm(lock, { force: true }).catch(() => {}); };
64
+ try {
65
+ await handle.writeFile(ownership);
66
+ await handle.sync();
67
+ await handle.close();
68
+ } catch (error) {
69
+ await handle.close().catch(() => {});
70
+ try { await rm(lock, {force:true}); }
71
+ catch { error.lock_cleanup_failed = true; }
72
+ throw error;
73
+ }
74
+ return async () => {
75
+ if (await readFile(lock, 'utf8').catch(() => null) !== ownership)
76
+ throw new Error('atomic mutation lock ownership lost');
77
+ await rm(lock, { force: true });
78
+ };
20
79
  } catch (error) {
21
80
  if (error?.code !== 'EEXIST') throw error;
22
- if (Date.now() - started >= LOCK_TIMEOUT_MS) throw new Error('atomic mutation lock timed out');
81
+ if (performance.now() - started >= LOCK_TIMEOUT_MS) throw new Error('atomic mutation lock timed out');
23
82
  try {
24
83
  const info = await stat(lock);
25
- if (Date.now() - info.mtimeMs > LOCK_STALE_MS) await rm(lock, { force: true }).catch(() => {});
84
+ if (Date.now() - info.mtimeMs > LOCK_STALE_MS) {
85
+ const contents = await readFile(lock, 'utf8').catch(() => null);
86
+ const owner = Number(/^\d+/.exec(contents??'')?.[0]);
87
+ let ownerAlive = true;
88
+ if (Number.isSafeInteger(owner) && owner > 0) {
89
+ try { process.kill(owner, 0); }
90
+ catch (error) { ownerAlive = error?.code !== 'ESRCH'; }
91
+ }
92
+ if (!ownerAlive) {
93
+ // A check-then-unlink race can remove a *new* writer's lock. Without
94
+ // an atomic compare-and-delete primitive, preserve evidence and stop.
95
+ throw new Error('atomic mutation lock abandoned; reconciliation required');
96
+ }
97
+ }
26
98
  } catch (statError) {
27
99
  if (statError?.code !== 'ENOENT') throw statError;
28
100
  }
@@ -47,6 +119,8 @@ export async function atomicMutation({
47
119
  throw new Error('atomic mutation requires readState and assertExpected');
48
120
  }
49
121
  const releaseLock = await acquireMutationLock(target);
122
+ let committed = false;
123
+ let operationFailure = null;
50
124
  try {
51
125
  const before = await readState(target);
52
126
  assertExpected(before, expected);
@@ -68,6 +142,7 @@ export async function atomicMutation({
68
142
  if (authorityCheck) await authorityCheck();
69
143
 
70
144
  await rename(temp, target);
145
+ committed = true;
71
146
  } catch (error) {
72
147
  if (handle) await handle.close().catch(() => {});
73
148
  await rm(temp, { force: true }).catch(() => {});
@@ -87,7 +162,22 @@ export async function atomicMutation({
87
162
  failure.expected_after_sha256 = expectedAfter;
88
163
  throw failure;
89
164
  }
165
+ } catch (error) {
166
+ operationFailure = error;
167
+ throw error;
90
168
  } finally {
91
- await releaseLock();
169
+ try { await releaseLock(); }
170
+ catch (error) {
171
+ if (operationFailure) operationFailure.lock_cleanup_failed = true;
172
+ else {
173
+ error.lock_cleanup_failed = true;
174
+ if (committed) {
175
+ error.outcome = 'FAILED_AFTER_UNKNOWN_EFFECT';
176
+ error.side_effect_state = 'unknown';
177
+ error.expected_after_sha256 = sha256Bytes(bytes);
178
+ }
179
+ throw error;
180
+ }
181
+ }
92
182
  }
93
183
  }