deadbyte-mcp 0.15.28 → 0.15.30

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 (78) hide show
  1. package/MANIFEST.SHA256 +76 -54
  2. package/PROVENANCE.json +21 -10
  3. package/README.txt +16 -14
  4. package/RELEASE.json +29 -10
  5. package/SBOM.cdx.json +3 -3
  6. package/STABILIZATION-PROGRESS.md +60 -0
  7. package/TEST-MATRIX.json +51 -93
  8. package/bin/appcontainer-stage.obj +0 -0
  9. package/bin/bootstrap-advapi32.obj +0 -0
  10. package/bin/bootstrap-exitcode.obj +0 -0
  11. package/bin/bootstrap-kernel32.obj +0 -0
  12. package/bin/child-control-probe.obj +0 -0
  13. package/bin/child-control-stage.obj +0 -0
  14. package/bin/contained-reverse-worker.obj +0 -0
  15. package/bin/contained-transform-worker.obj +0 -0
  16. package/bin/containment-probe.obj +0 -0
  17. package/bin/deadbyte-contain.obj +0 -0
  18. package/bin/deadbyte-exec.obj +0 -0
  19. package/bin/deadbyte-hardware-observer-r45.obj +0 -0
  20. package/bin/deadbyte-process-host.exe +0 -0
  21. package/bin/deadbyte-process-host.obj +0 -0
  22. package/bin/deadbyte-tunnel-host.obj +0 -0
  23. package/controller/deadbyte-controller.ps1 +4 -1
  24. package/controller/deadbyte-desktop-policy.json +5 -5
  25. package/controller/deadbyte-host-policy.json +170 -0
  26. package/controller/deadbyte-process-policy.json +5 -5
  27. package/docs/R51-PROVENANCE.json +18 -0
  28. package/docs/R51-RELEASE.json +115 -0
  29. package/docs/R51-TEST-MATRIX.json +166 -0
  30. package/docs/R52-ACTIVATION-FORENSICS.md +85 -0
  31. package/native/deadbyte-process-host.cpp +5 -3
  32. package/package.json +96 -94
  33. package/proof/CONTAINMENT-BUILD.txt +6 -6
  34. package/scripts/gate-windows-r52.ps1 +58 -0
  35. package/scripts/gate-windows-r53.ps1 +58 -0
  36. package/scripts/verify-r42-parent.mjs +1 -1
  37. package/scripts/verify-r43-parent.mjs +1 -1
  38. package/scripts/verify-r44-parent.mjs +1 -1
  39. package/scripts/verify-r45-parent.mjs +1 -1
  40. package/scripts/windows-gate-evidence-r52.mjs +94 -0
  41. package/scripts/windows-gate-evidence-r53.mjs +102 -0
  42. package/src/authority-smoke.mjs +1 -0
  43. package/src/coding-plane.mjs +43 -12
  44. package/src/coding-task-runtime.mjs +10 -4
  45. package/src/deadbyte-cli.mjs +4 -2
  46. package/src/doctor-r45.mjs +2 -1
  47. package/src/machine-fs-smoke-client.mjs +1 -0
  48. package/src/mcp-contained-smoke-client.mjs +1 -0
  49. package/src/mcp-host-smoke-client.mjs +1 -0
  50. package/src/mcp-smoke-client.mjs +1 -0
  51. package/src/process-smoke-client.mjs +1 -0
  52. package/src/release-slot.mjs +5 -2
  53. package/src/remote-session-guardian.mjs +12 -4
  54. package/src/runtime-coherence.mjs +13 -4
  55. package/src/runtime-update.mjs +28 -7
  56. package/src/state-root.mjs +8 -0
  57. package/src/trust.mjs +2 -1
  58. package/src/tunnel-stdio-compat-smoke.mjs +1 -0
  59. package/src/version.mjs +1 -1
  60. package/test/coding-runtime.test.mjs +70 -1
  61. package/test/commander-biff-reference-r48.test.mjs +1 -1
  62. package/test/helpers/coding-disconnect-server.mjs +11 -0
  63. package/test/r44-finalizer.test.mjs +4 -3
  64. package/test/r49-windows-gate.test.mjs +19 -9
  65. package/test/r51-release-contract.test.mjs +22 -20
  66. package/test/r52-host-revocation.test.mjs +65 -0
  67. package/test/r52-offline-predecessor-recovery.test.mjs +77 -0
  68. package/test/r52-release-contract.test.mjs +64 -0
  69. package/test/r53-release-contract.test.mjs +66 -0
  70. package/test/release-slot.test.mjs +2 -1
  71. package/test/skill-mcp-r44.test.mjs +3 -0
  72. package/test/stabilization-guardian.test.mjs +25 -0
  73. package/test/stabilization-parent-context.test.mjs +18 -0
  74. package/test/stabilization-powershell.test.mjs +26 -0
  75. package/test/stabilization-semver.test.mjs +23 -0
  76. package/test/stabilization-smoke-root.test.mjs +23 -0
  77. package/test/stabilization-state-root.test.mjs +60 -0
  78. package/test/stabilization-update-child-root.test.mjs +20 -0
@@ -39,7 +39,7 @@ deadbyte-process-host.cpp
39
39
  exit_code=0
40
40
  ==== probe-dependencies ====
41
41
 
42
- Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-r42-agentic-dev\bin\containment-probe.exe
42
+ Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.30-stabilization\bin\containment-probe.exe
43
43
 
44
44
  File Type: EXECUTABLE IMAGE
45
45
 
@@ -59,7 +59,7 @@ File Type: EXECUTABLE IMAGE
59
59
  exit_code=0
60
60
  ==== contained-launcher-dependencies ====
61
61
 
62
- Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-r42-agentic-dev\bin\deadbyte-exec.exe
62
+ Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.30-stabilization\bin\deadbyte-exec.exe
63
63
 
64
64
  File Type: EXECUTABLE IMAGE
65
65
 
@@ -83,7 +83,7 @@ File Type: EXECUTABLE IMAGE
83
83
  exit_code=0
84
84
  ==== contained-worker-dependencies ====
85
85
 
86
- Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-r42-agentic-dev\bin\contained-transform-worker.exe
86
+ Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.30-stabilization\bin\contained-transform-worker.exe
87
87
 
88
88
  File Type: EXECUTABLE IMAGE
89
89
 
@@ -104,7 +104,7 @@ File Type: EXECUTABLE IMAGE
104
104
  exit_code=0
105
105
  ==== contained-reverse-worker-dependencies ====
106
106
 
107
- Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-r42-agentic-dev\bin\contained-reverse-worker.exe
107
+ Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.30-stabilization\bin\contained-reverse-worker.exe
108
108
 
109
109
  File Type: EXECUTABLE IMAGE
110
110
 
@@ -125,7 +125,7 @@ File Type: EXECUTABLE IMAGE
125
125
  exit_code=0
126
126
  ==== named-tunnel-host-dependencies ====
127
127
 
128
- Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-r42-agentic-dev\bin\deadbyte-tunnel-host.exe
128
+ Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.30-stabilization\bin\deadbyte-tunnel-host.exe
129
129
 
130
130
  File Type: EXECUTABLE IMAGE
131
131
 
@@ -144,7 +144,7 @@ File Type: EXECUTABLE IMAGE
144
144
  exit_code=0
145
145
  ==== process-host-dependencies ====
146
146
 
147
- Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-r42-agentic-dev\bin\deadbyte-process-host.exe
147
+ Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.30-stabilization\bin\deadbyte-process-host.exe
148
148
 
149
149
  File Type: EXECUTABLE IMAGE
150
150
 
@@ -0,0 +1,58 @@
1
+ $ErrorActionPreference = 'Stop'
2
+ Set-StrictMode -Version 3.0
3
+
4
+ function Invoke-GateStep {
5
+ param([Parameter(Mandatory)][string]$Name,[Parameter(Mandatory)][string[]]$NpmArgs)
6
+ Write-Host "==== $Name ===="
7
+ & npm.cmd @NpmArgs
8
+ if ($LASTEXITCODE -ne 0) { throw "DEADBYTE 0.15.29 / R52 gate failed at $Name with exit code $LASTEXITCODE" }
9
+ }
10
+
11
+ function Invoke-NodeTest {
12
+ param([Parameter(Mandatory)][string]$Name,[Parameter(Mandatory)][string[]]$TestFiles)
13
+ Write-Host "==== $Name ===="
14
+ & node.exe --test --test-concurrency=1 @TestFiles
15
+ if ($LASTEXITCODE -ne 0) { throw "DEADBYTE 0.15.29 / R52 gate failed at $Name with exit code $LASTEXITCODE" }
16
+ }
17
+
18
+ Invoke-NodeTest 'r52-package-boundary' @('test/package-boundary.test.mjs')
19
+ Invoke-NodeTest 'r52-release-smoke-isolation' @('test/release-smoke-isolation.test.mjs')
20
+ Invoke-GateStep 'r52-focused-contracts' @('run','r52:focused:test')
21
+ Invoke-GateStep 'containment-build' @('run','containment:build')
22
+ Invoke-GateStep 'process-policy-refresh' @('run','process:policy:refresh')
23
+ Write-Host "==== desktop-policy-refresh ===="
24
+ & (Join-Path $PSScriptRoot 'init-desktop-policy-r40.ps1')
25
+ Invoke-GateStep 'hardware-build' @('run','hardware:build')
26
+
27
+ $hostPolicy = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\controller\deadbyte-host-policy.json'))
28
+ Remove-Item -LiteralPath $hostPolicy -Force -ErrorAction SilentlyContinue
29
+ if (Test-Path -LiteralPath $hostPolicy) { throw 'could not establish hermetic Host policy absence' }
30
+
31
+ Invoke-GateStep 'r52-release-focused' @('run','r48:release:test')
32
+ Invoke-NodeTest 'r49-donor-proof' @('test/r49-remote-control-donors.test.mjs')
33
+ Invoke-NodeTest 'r49-desktop-wait' @('test/desktop-runtime-r40.test.mjs','test/desktop-mcp-tools-r40.test.mjs','test/autonomous-desktop-r40.test.mjs')
34
+ Invoke-GateStep 'r49-catalog' @('run','r48:catalog:test')
35
+ Invoke-GateStep 'commander-hardening' @('run','r47:hardening:test')
36
+ Invoke-GateStep 'full-regression' @('run','r48:full:test')
37
+ Invoke-GateStep 'mcp-smoke' @('run','mcp:smoke')
38
+ Invoke-GateStep 'mcp-agent-smoke' @('run','mcp:agent:smoke')
39
+ Invoke-GateStep 'mcp-coding-smoke' @('run','mcp:coding:smoke')
40
+ Invoke-GateStep 'mcp-machine-fs-smoke' @('run','mcp:machine-fs:smoke')
41
+ Invoke-GateStep 'mcp-observation-smoke' @('run','mcp:observation:smoke')
42
+ Invoke-GateStep 'mcp-process-smoke' @('run','mcp:process:smoke')
43
+ Invoke-GateStep 'mcp-autonomous-smoke' @('run','mcp:autonomous:smoke')
44
+ Invoke-GateStep 'tunnel-stdio-compat-smoke' @('run','mcp:tunnel-compat:smoke')
45
+ Invoke-GateStep 'containment-smoke' @('run','containment:smoke')
46
+ Invoke-GateStep 'contained-smoke' @('run','contained:smoke')
47
+ Invoke-GateStep 'mcp-contained-smoke' @('run','mcp:contained:smoke')
48
+ Invoke-GateStep 'host-policy-refresh' @('run','host:policy:init')
49
+ Invoke-GateStep 'manifest-write' @('run','manifest:write')
50
+ Invoke-GateStep 'manifest-verify' @('run','manifest:verify')
51
+ Invoke-GateStep 'doctor' @('run','doctor')
52
+ Invoke-GateStep 'mcp-host-smoke' @('run','mcp:host:smoke')
53
+ Invoke-GateStep 'authority-smoke' @('run','authority:smoke')
54
+ Invoke-GateStep 'manifest-final-verify' @('run','manifest:verify')
55
+
56
+ & node.exe (Join-Path $PSScriptRoot 'windows-gate-evidence-r52.mjs')
57
+ if ($LASTEXITCODE -ne 0) { throw "R52 evidence writer failed with exit code $LASTEXITCODE" }
58
+ Write-Host '==== DEADBYTE 0.15.29 / R52 WINDOWS GATE: PASS ===='
@@ -0,0 +1,58 @@
1
+ $ErrorActionPreference = 'Stop'
2
+ Set-StrictMode -Version 3.0
3
+
4
+ function Invoke-GateStep {
5
+ param([Parameter(Mandatory)][string]$Name,[Parameter(Mandatory)][string[]]$NpmArgs)
6
+ Write-Host "==== $Name ===="
7
+ & npm.cmd @NpmArgs
8
+ if ($LASTEXITCODE -ne 0) { throw "DEADBYTE 0.15.30 / R53 gate failed at $Name with exit code $LASTEXITCODE" }
9
+ }
10
+
11
+ function Invoke-NodeTest {
12
+ param([Parameter(Mandatory)][string]$Name,[Parameter(Mandatory)][string[]]$TestFiles)
13
+ Write-Host "==== $Name ===="
14
+ & node.exe --test --test-concurrency=1 @TestFiles
15
+ if ($LASTEXITCODE -ne 0) { throw "DEADBYTE 0.15.30 / R53 gate failed at $Name with exit code $LASTEXITCODE" }
16
+ }
17
+
18
+ Invoke-NodeTest 'r53-package-boundary' @('test/package-boundary.test.mjs')
19
+ Invoke-NodeTest 'r53-release-smoke-isolation' @('test/release-smoke-isolation.test.mjs')
20
+ Invoke-GateStep 'r53-focused-contracts' @('run','r53:focused:test')
21
+ Invoke-GateStep 'containment-build' @('run','containment:build')
22
+ Invoke-GateStep 'process-policy-refresh' @('run','process:policy:refresh')
23
+ Write-Host "==== desktop-policy-refresh ===="
24
+ & (Join-Path $PSScriptRoot 'init-desktop-policy-r40.ps1')
25
+ Invoke-GateStep 'hardware-build' @('run','hardware:build')
26
+
27
+ $hostPolicy = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\controller\deadbyte-host-policy.json'))
28
+ Remove-Item -LiteralPath $hostPolicy -Force -ErrorAction SilentlyContinue
29
+ if (Test-Path -LiteralPath $hostPolicy) { throw 'could not establish hermetic Host policy absence' }
30
+
31
+ Invoke-GateStep 'r53-release-focused' @('run','r48:release:test')
32
+ Invoke-NodeTest 'r49-donor-proof' @('test/r49-remote-control-donors.test.mjs')
33
+ Invoke-NodeTest 'r49-desktop-wait' @('test/desktop-runtime-r40.test.mjs','test/desktop-mcp-tools-r40.test.mjs','test/autonomous-desktop-r40.test.mjs')
34
+ Invoke-GateStep 'r49-catalog' @('run','r48:catalog:test')
35
+ Invoke-GateStep 'commander-hardening' @('run','r47:hardening:test')
36
+ Invoke-GateStep 'full-regression' @('run','r48:full:test')
37
+ Invoke-GateStep 'mcp-smoke' @('run','mcp:smoke')
38
+ Invoke-GateStep 'mcp-agent-smoke' @('run','mcp:agent:smoke')
39
+ Invoke-GateStep 'mcp-coding-smoke' @('run','mcp:coding:smoke')
40
+ Invoke-GateStep 'mcp-machine-fs-smoke' @('run','mcp:machine-fs:smoke')
41
+ Invoke-GateStep 'mcp-observation-smoke' @('run','mcp:observation:smoke')
42
+ Invoke-GateStep 'mcp-process-smoke' @('run','mcp:process:smoke')
43
+ Invoke-GateStep 'mcp-autonomous-smoke' @('run','mcp:autonomous:smoke')
44
+ Invoke-GateStep 'tunnel-stdio-compat-smoke' @('run','mcp:tunnel-compat:smoke')
45
+ Invoke-GateStep 'containment-smoke' @('run','containment:smoke')
46
+ Invoke-GateStep 'contained-smoke' @('run','contained:smoke')
47
+ Invoke-GateStep 'mcp-contained-smoke' @('run','mcp:contained:smoke')
48
+ Invoke-GateStep 'host-policy-refresh' @('run','host:policy:init')
49
+ Invoke-GateStep 'manifest-write' @('run','manifest:write')
50
+ Invoke-GateStep 'manifest-verify' @('run','manifest:verify')
51
+ Invoke-GateStep 'doctor' @('run','doctor')
52
+ Invoke-GateStep 'mcp-host-smoke' @('run','mcp:host:smoke')
53
+ Invoke-GateStep 'authority-smoke' @('run','authority:smoke')
54
+ Invoke-GateStep 'manifest-final-verify' @('run','manifest:verify')
55
+
56
+ & node.exe (Join-Path $PSScriptRoot 'windows-gate-evidence-r53.mjs')
57
+ if ($LASTEXITCODE -ne 0) { throw "R53 evidence writer failed with exit code $LASTEXITCODE" }
58
+ Write-Host '==== DEADBYTE 0.15.30 / R53 WINDOWS GATE: PASS ===='
@@ -82,7 +82,7 @@ export async function verifyR42Parent(file=path.join(root,'R42-PARENT.json')){
82
82
  const closureBytes=await readFile(path.resolve(record.parent_final_closure_path));
83
83
  insist(sha256(closureBytes)===record.parent_final_closure_sha256,'R41 parent final closure raw hash mismatch');
84
84
  const closure=JSON.parse(closureBytes.toString('utf8').replace(/^\uFEFF/,''));
85
- const pointer=verifySignedClosure(closure,record,await readFile(path.join(stateRoot,'trust','ed25519-public.pem')));
85
+ const pointer=verifySignedClosure(closure,record,await readFile(path.resolve(process.env.DEADBYTE_PARENT_VERIFY_KEY||path.join(stateRoot,'trust','ed25519-public.pem'))));
86
86
  const generationFiles=await verifyTree(path.resolve(pointer.generation_root),'GENERATION.SHA256',record.parent_generation_manifest_sha256,{exact:true});
87
87
 
88
88
  const tarball=await readFile(path.resolve(record.parent_public_tarball_path));
@@ -107,7 +107,7 @@ export async function verifyR43Parent(file=path.join(root,'R43-PARENT.json')){
107
107
  const activationBytes=await readFile(path.resolve(record.parent_activation_receipt_path));
108
108
  insist(sha256(activationBytes)===record.parent_activation_receipt_file_sha256,'R42 activation raw file hash mismatch');
109
109
  const activation=JSON.parse(activationBytes.toString('utf8').replace(/^\uFEFF/,''));
110
- const keyInfo=publicKeyInfo(await readFile(path.join(stateRoot,'trust','ed25519-public.pem')));
110
+ const keyInfo=publicKeyInfo(await readFile(path.resolve(process.env.DEADBYTE_PARENT_VERIFY_KEY||path.join(stateRoot,'trust','ed25519-public.pem'))));
111
111
  verifyActivationEnvelope(activation,keyInfo,record);
112
112
 
113
113
  return {
@@ -76,7 +76,7 @@ export async function verifyR44Parent(parentFile,{stateRoot=path.join(os.homedir
76
76
  insist(envelope.body.version===parent.parent_version,'R44 parent closure version mismatch');
77
77
  insist(envelope.body.release_label==='R43 AUTHORITY HARDENING PATCH','R44 parent closure label mismatch');
78
78
 
79
- const publicKey=createPublicKey(await readFile(path.join(path.resolve(stateRoot),'trust','ed25519-public.pem')));
79
+ const publicKey=createPublicKey(await readFile(path.resolve(process.env.DEADBYTE_PARENT_VERIFY_KEY||path.join(path.resolve(stateRoot),'trust','ed25519-public.pem'))));
80
80
  insist(publicKey.asymmetricKeyType==='ed25519','R44 parent trust key must be Ed25519');
81
81
  const keyId=sha256(Buffer.from(publicKey.export({type:'spki',format:'der'})));
82
82
  insist(keyId===parent.parent_final_closure_signer_key_id,'R44 parent trust key id mismatch');
@@ -76,7 +76,7 @@ export async function verifyR45Parent(parentFile,{stateRoot=path.join(os.homedir
76
76
  insist(envelope.body.version===parent.parent_version,'R45 parent closure version mismatch');
77
77
  insist(envelope.body.release_label==='R44 REMOTE AGENT SKILL BASELINE','R45 parent closure label mismatch');
78
78
 
79
- const publicKey=createPublicKey(await readFile(path.join(path.resolve(stateRoot),'trust','ed25519-public.pem')));
79
+ const publicKey=createPublicKey(await readFile(path.resolve(process.env.DEADBYTE_PARENT_VERIFY_KEY||path.join(path.resolve(stateRoot),'trust','ed25519-public.pem'))));
80
80
  insist(publicKey.asymmetricKeyType==='ed25519','R45 parent trust key must be Ed25519');
81
81
  const keyId=sha256(Buffer.from(publicKey.export({type:'spki',format:'der'})));
82
82
  insist(keyId===parent.parent_final_closure_signer_key_id,'R45 parent trust key id mismatch');
@@ -0,0 +1,94 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, open, readFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { doctor } from '../src/doctor-r45.mjs';
7
+
8
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
9
+ const stateRoot = path.resolve(process.env.DEADBYTE_STATE_ROOT || path.join(os.homedir(), '.deadbyte-mcp'));
10
+ const sha256 = bytes => createHash('sha256').update(bytes).digest('hex');
11
+ const insist = (ok, message) => { if (!ok) throw new Error(message); };
12
+ const pkg = JSON.parse(await readFile(path.join(root, 'package.json'), 'utf8'));
13
+ const release = JSON.parse(await readFile(path.join(root, 'RELEASE.json'), 'utf8'));
14
+ const provenance = JSON.parse(await readFile(path.join(root, 'PROVENANCE.json'), 'utf8'));
15
+ const active = JSON.parse(await readFile(path.join(stateRoot, 'active-release.json'), 'utf8'));
16
+ const manifestSha256 = sha256(await readFile(path.join(root, 'MANIFEST.SHA256')));
17
+
18
+ insist(pkg.version === '0.15.29' && release.version === pkg.version && release.train === 'R52', 'R52 candidate identity mismatch');
19
+ insist(
20
+ active.release_id === release.runtime_predecessor.release_id &&
21
+ active.manifest_sha256 === release.runtime_predecessor.manifest_sha256 &&
22
+ active.generation_manifest_sha256 === release.runtime_predecessor.generation_manifest_sha256,
23
+ 'active runtime is not the exact sealed R52 runtime rollback target'
24
+ );
25
+ insist(release.parent.version === '0.15.28' && release.parent.manifest_sha256 === release.source_predecessor.manifest_file_sha256 && release.parent.tarball_sha256 === release.source_predecessor.tarball_sha256, 'R52 published parent binding mismatch');
26
+ insist(release.source_predecessor.version === '0.15.28', 'R52 source predecessor version mismatch');
27
+ insist(release.source_predecessor.tarball_sha256 === '0a74332ba715d80a532dc871ece4826bdb917b8c7a442e71388a06d44698c411', 'R52 source predecessor tarball mismatch');
28
+ insist(provenance.source_predecessor_activation_failure_receipt_sha256 === '090f165c96da0d5a8e9c0815fd389fe94e8fe36c23a6878ee825b8f46bfb0bc2', 'R52 failure receipt binding mismatch');
29
+
30
+ const health = await doctor();
31
+ insist(health.status === 'ok' && health.package_version === pkg.version && health.manifest?.verified === true, 'R52 doctor evidence invalid');
32
+ insist(health.policy?.host_v2_ready === true, 'R52 Host policy v2 is not ready');
33
+
34
+ const evidence = {
35
+ schema: 'deadbyte.r52-windows-gate.v1',
36
+ status: 'passed',
37
+ exit_code: 0,
38
+ version: pkg.version,
39
+ train: 'R52',
40
+ completed_at_utc: new Date().toISOString(),
41
+ manifest_sha256: manifestSha256,
42
+ parent: release.parent,
43
+ runtime_predecessor: release.runtime_predecessor,
44
+ source_predecessor: release.source_predecessor,
45
+ activation_failure_receipt_sha256: provenance.source_predecessor_activation_failure_receipt_sha256,
46
+ active_predecessor: {
47
+ release_id: active.release_id,
48
+ version: active.version,
49
+ manifest_sha256: active.manifest_sha256,
50
+ generation_manifest_sha256: active.generation_manifest_sha256
51
+ },
52
+ doctor: { verified_entries: health.manifest.verified_entries, host_v2_ready: true },
53
+ gates: {
54
+ offline_predecessor_host_revoke_before_capture: true,
55
+ package_boundary: true,
56
+ release_smoke_isolation: true,
57
+ runtime_coherence: true,
58
+ coding_command_singleflight: true,
59
+ foreground_desktop_host_fail_closed: true,
60
+ livelog_optimizer: true,
61
+ full_regression: true,
62
+ commander_hardening: true,
63
+ donor_proof: true,
64
+ desktop_wait: true,
65
+ exact_catalog: true,
66
+ stdio_smokes: true,
67
+ manifest_final_verify: true
68
+ }
69
+ };
70
+
71
+ const bytes = Buffer.from(`${JSON.stringify(evidence, null, 2)}\n`);
72
+ const dir = path.join(stateRoot, 'evidence', 'release');
73
+ await mkdir(dir, { recursive: true });
74
+ const target = path.join(dir, `r52-windows-gate-${manifestSha256.slice(0, 16)}.json`);
75
+
76
+ try {
77
+ const handle = await open(target, 'wx', 0o600);
78
+ try { await handle.writeFile(bytes); await handle.sync(); }
79
+ finally { await handle.close(); }
80
+ } catch (error) {
81
+ if (error?.code !== 'EEXIST') throw error;
82
+ const prior = JSON.parse(await readFile(target, 'utf8'));
83
+ insist(
84
+ prior.schema === evidence.schema &&
85
+ prior.status === 'passed' &&
86
+ prior.version === evidence.version &&
87
+ prior.manifest_sha256 === manifestSha256 &&
88
+ prior.parent?.release_id === release.parent.release_id &&
89
+ prior.activation_failure_receipt_sha256 === evidence.activation_failure_receipt_sha256,
90
+ 'conflicting R52 gate evidence identity'
91
+ );
92
+ }
93
+
94
+ console.log(JSON.stringify({ status: 'passed', path: target, sha256: sha256(bytes), manifest_sha256: manifestSha256 }));
@@ -0,0 +1,102 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, open, readFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { doctor } from '../src/doctor-r45.mjs';
7
+
8
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
9
+ const stateRoot = path.resolve(process.env.DEADBYTE_STATE_ROOT || path.join(os.homedir(), '.deadbyte-mcp'));
10
+ const sha256 = bytes => createHash('sha256').update(bytes).digest('hex');
11
+ const insist = (ok, message) => { if (!ok) throw new Error(message); };
12
+ const pkg = JSON.parse(await readFile(path.join(root, 'package.json'), 'utf8'));
13
+ const release = JSON.parse(await readFile(path.join(root, 'RELEASE.json'), 'utf8'));
14
+ const provenance = JSON.parse(await readFile(path.join(root, 'PROVENANCE.json'), 'utf8'));
15
+ const matrix = JSON.parse(await readFile(path.join(root, 'TEST-MATRIX.json'), 'utf8'));
16
+ const active = JSON.parse(await readFile(path.join(stateRoot, 'active-release.json'), 'utf8'));
17
+ const manifestSha256 = sha256(await readFile(path.join(root, 'MANIFEST.SHA256')));
18
+
19
+ insist(pkg.version === '0.15.30' && release.version === pkg.version && release.train === 'R53', 'R53 candidate identity mismatch');
20
+ insist(
21
+ active.release_id === release.runtime_predecessor.release_id &&
22
+ active.manifest_sha256 === release.runtime_predecessor.manifest_sha256 &&
23
+ active.generation_manifest_sha256 === release.runtime_predecessor.generation_manifest_sha256,
24
+ 'active runtime is not exact sealed 0.15.29 predecessor'
25
+ );
26
+ insist(release.parent.version === '0.15.29', 'R53 parent version mismatch');
27
+ insist(release.parent.release_id === 'v0.15.29-f74eaa1b3d00e6fb', 'R53 parent release id mismatch');
28
+ insist(release.parent.manifest_sha256 === 'f74eaa1b3d00e6fb5a36b2358efb815e3686c9fe9dbd3b84d25a713ef957748c', 'R53 parent manifest mismatch');
29
+ insist(release.parent.generation_manifest_sha256 === '289b4fc065de0681da6864cc1bbe09aab198ca804945ffc27c786438dad13f5a', 'R53 parent generation mismatch');
30
+ insist(release.parent.tarball_sha256 === 'f535d7121a384bed106d34bc7525afa9d4048c7e290f270699e25136d1e82eeb', 'R53 parent tarball mismatch');
31
+ insist(provenance.parent_release_id === release.parent.release_id, 'R53 provenance parent release mismatch');
32
+ insist(provenance.parent_manifest_sha256 === release.parent.manifest_sha256, 'R53 provenance parent manifest mismatch');
33
+ insist(provenance.parent_generation_manifest_sha256 === release.parent.generation_manifest_sha256, 'R53 provenance parent generation mismatch');
34
+ insist(provenance.operator_override?.four_hour_soak === 'NOT_COMPLETED', 'R53 soak bypass missing');
35
+ insist(provenance.operator_override?.stable_final_claim === false, 'R53 must not claim stable/final');
36
+ insist(matrix.operator_override?.four_hour_soak === 'NOT_COMPLETED', 'R53 matrix soak bypass missing');
37
+
38
+ const health = await doctor();
39
+ insist(health.status === 'ok' && health.package_version === pkg.version && health.manifest?.verified === true, 'R53 doctor evidence invalid');
40
+ insist(health.policy?.host_v2_ready === true, 'R53 Host policy v2 is not ready');
41
+
42
+ const evidence = {
43
+ schema: 'deadbyte.r53-windows-gate.v1',
44
+ status: 'passed',
45
+ exit_code: 0,
46
+ version: pkg.version,
47
+ train: 'R53',
48
+ completed_at_utc: new Date().toISOString(),
49
+ manifest_sha256: manifestSha256,
50
+ parent: release.parent,
51
+ runtime_predecessor: release.runtime_predecessor,
52
+ stabilization: release.stabilization,
53
+ operator_override: provenance.operator_override,
54
+ active_predecessor: {
55
+ release_id: active.release_id,
56
+ version: active.version,
57
+ manifest_sha256: active.manifest_sha256,
58
+ generation_manifest_sha256: active.generation_manifest_sha256
59
+ },
60
+ doctor: { verified_entries: health.manifest.verified_entries, host_v2_ready: true },
61
+ gates: {
62
+ r53_focused: true,
63
+ package_boundary: true,
64
+ release_smoke_isolation: true,
65
+ runtime_coherence: true,
66
+ coding_command_singleflight: true,
67
+ foreground_desktop_host_fail_closed: true,
68
+ full_regression: true,
69
+ commander_hardening: true,
70
+ donor_proof: true,
71
+ desktop_wait: true,
72
+ exact_catalog: true,
73
+ stdio_smokes: true,
74
+ manifest_final_verify: true,
75
+ four_hour_soak_completed: false
76
+ }
77
+ };
78
+
79
+ const bytes = Buffer.from(`${JSON.stringify(evidence, null, 2)}\n`);
80
+ const dir = path.join(stateRoot, 'evidence', 'release');
81
+ await mkdir(dir, { recursive: true });
82
+ const target = path.join(dir, `r53-windows-gate-${manifestSha256.slice(0, 16)}.json`);
83
+
84
+ try {
85
+ const handle = await open(target, 'wx', 0o600);
86
+ try { await handle.writeFile(bytes); await handle.sync(); }
87
+ finally { await handle.close(); }
88
+ } catch (error) {
89
+ if (error?.code !== 'EEXIST') throw error;
90
+ const prior = JSON.parse(await readFile(target, 'utf8'));
91
+ insist(
92
+ prior.schema === evidence.schema &&
93
+ prior.status === 'passed' &&
94
+ prior.version === evidence.version &&
95
+ prior.manifest_sha256 === manifestSha256 &&
96
+ prior.parent?.release_id === release.parent.release_id &&
97
+ prior.operator_override?.four_hour_soak === 'NOT_COMPLETED',
98
+ 'conflicting R53 gate evidence identity'
99
+ );
100
+ }
101
+
102
+ console.log(JSON.stringify({ status: 'passed', path: target, sha256: sha256(bytes), manifest_sha256: manifestSha256 }));
@@ -60,6 +60,7 @@ const transport = new StdioClientTransport({
60
60
  cwd: repoRoot,
61
61
  env: {
62
62
  ...getDefaultEnvironment(),
63
+ DEADBYTE_STATE_ROOT: path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(trust,'state')),
63
64
  DEADBYTE_WORKSPACE_ROOT: workspaceRoot,
64
65
  DEADBYTE_SIGNING_KEY: keys.privateKeyPath,
65
66
  DEADBYTE_VERIFY_KEY: keys.publicKeyPath
@@ -11,6 +11,7 @@ import { loadPrivateSigningKey, loadPublicVerifyKey } from './trust.mjs';
11
11
  import { createCodingSearch } from './coding-search.mjs';
12
12
  import { createSearchCursorCodec } from './search-cursor.mjs';
13
13
  import { atomicMutation } from './atomic-mutation.mjs';
14
+ import { fileURLToPath } from 'node:url';
14
15
 
15
16
  export const CODING_POLICY_SCHEMA = 'deadbyte.coding-policy.v1';
16
17
  export const CODING_LEASE_SCHEMA = 'deadbyte.coding-lease.v1';
@@ -591,7 +592,7 @@ export function createCodingRuntime({ policy, signingKeyPath, verifyKeyPath }) {
591
592
  receipt_sha256: signed.receipt.receipt_sha256, request_sha256: request };
592
593
  }
593
594
  async function runCommandOnce({ rootId, commandId, scriptPath, scriptSha256, args = [], cwd = '.', timeoutMs = null,
594
- executionDeadlineUtc = null, preExecutionCheck = null }) {
595
+ executionDeadlineUtc = null, preExecutionCheck = null, cancellationCheck = null }) {
595
596
  const lease = await assertCodingArmed(policy, rootId);
596
597
  if (!ID_RE.test(commandId ?? '')) throw new Error('invalid coding command id');
597
598
  const command = policy.commands[commandId];
@@ -633,28 +634,58 @@ export function createCodingRuntime({ policy, signingKeyPath, verifyKeyPath }) {
633
634
  if (deadlineBound < 100) throw new Error('coding execution deadline expires too soon to execute command');
634
635
  const effectiveTimeout = Math.min(requestedTimeout, command.timeout_ms, grantBound, deadlineBound);
635
636
  const argv = [...command.prefix_args, script.resolved, ...args];
637
+ // The internal Windows host owns a kill-on-close Job Object. Its identity
638
+ // is package-pinned, never selected by a caller or granted public PID input.
639
+ let executable=command.executable, launchArgs=argv;
640
+ if(process.platform==='win32'){
641
+ const host=fileURLToPath(new URL('../bin/deadbyte-process-host.exe',import.meta.url));
642
+ const hostPolicy=JSON.parse(await readFile(new URL('../controller/deadbyte-process-policy.json',import.meta.url),'utf8'));
643
+ if(await sha256File(host)!==hostPolicy.native_host?.sha256) throw new Error('coding process host hash changed');
644
+ executable=host;
645
+ launchArgs=[command.executable,cwdResolved,randomBytes(32).toString('hex'),'--deadbyte-transport=pipe-quiet',...argv];
646
+ }
647
+ if(cancellationCheck!==null&&typeof cancellationCheck!=='function') throw new Error('cancellation check must be internal function');
648
+ if(cancellationCheck&&await cancellationCheck()) throw new Error('coding command cancelled before dispatch');
649
+ await recheckCodingAuthority(policy,activeLease,rootId);
636
650
  const startMs = performance.now();
637
- const child = spawn(command.executable, argv, {
651
+ const child = spawn(executable, launchArgs, {
638
652
  cwd: cwdResolved, env: codingEnvironment(), shell: false, windowsHide: true,
639
- detached: false, stdio: ['ignore', 'pipe', 'pipe']
653
+ detached: process.platform!=='win32', stdio: ['ignore', 'pipe', 'pipe']
640
654
  });
655
+ const killOwned=()=>{
656
+ if(child.exitCode!==null||child.signalCode!==null)return;
657
+ if(process.platform==='win32') child.kill('SIGKILL');
658
+ else {try{process.kill(-child.pid,'SIGKILL');}catch(error){if(error.code!=='ESRCH')throw error;}}
659
+ };
641
660
  const stdoutState = { chunks: [], bytes: 0, exceeded: null };
642
661
  const stderrState = { chunks: [], bytes: 0, exceeded: null };
643
- collectBounded(child.stdout, command.max_output_bytes, stdoutState, 'stdout', () => child.kill('SIGKILL'));
644
- collectBounded(child.stderr, command.max_output_bytes, stderrState, 'stderr', () => child.kill('SIGKILL'));
662
+ collectBounded(child.stdout, command.max_output_bytes, stdoutState, 'stdout', killOwned);
663
+ collectBounded(child.stderr, command.max_output_bytes, stderrState, 'stderr', killOwned);
645
664
  let timedOut = false;
646
665
  let timer;
666
+ let cancelTimer,checking=false,cancellationError=null,finished=false;
667
+ if(cancellationCheck){
668
+ cancelTimer=setInterval(async()=>{
669
+ if(checking||finished)return;
670
+ checking=true;
671
+ try{if(await cancellationCheck())killOwned();}
672
+ catch(error){cancellationError=error;killOwned();}
673
+ finally{checking=false;}
674
+ },50);
675
+ cancelTimer.unref?.();
676
+ }
647
677
  const timerDelay = deadlineAtMs === null
648
678
  ? effectiveTimeout
649
679
  : Math.max(1, Math.min(effectiveTimeout, Math.floor(deadlineAtMs - Date.now())));
650
680
  const completion = new Promise((resolve, reject) => {
651
681
  child.once('error', reject);
652
682
  child.once('close', (code, signal) => resolve({ code, signal }));
653
- timer = setTimeout(() => { timedOut = true; child.kill('SIGKILL'); }, timerDelay);
683
+ timer = setTimeout(() => { timedOut = true; killOwned(); }, timerDelay);
654
684
  timer.unref?.();
655
685
  });
656
686
  let result;
657
- try { result = await completion; } finally { if (timer) clearTimeout(timer); }
687
+ try { result = await completion; } finally { finished=true;if (timer) clearTimeout(timer);if(cancelTimer)clearInterval(cancelTimer); }
688
+ if(cancellationError)throw cancellationError;
658
689
  if (stdoutState.exceeded || stderrState.exceeded) throw new Error(stdoutState.exceeded ?? stderrState.exceeded);
659
690
  const stdoutBytes = Buffer.concat(stdoutState.chunks);
660
691
  const stderrBytes = Buffer.concat(stderrState.chunks);
@@ -669,7 +700,7 @@ export function createCodingRuntime({ policy, signingKeyPath, verifyKeyPath }) {
669
700
  }
670
701
 
671
702
  async function commandRun({ rootId, commandId, scriptPath, scriptSha256, args = [], cwd = '.', timeoutMs = null,
672
- executionDeadlineUtc = null, preExecutionCheck = null, receiptId = null, requestSha256 = null }) {
703
+ executionDeadlineUtc = null, preExecutionCheck = null, cancellationCheck = null, receiptId = null, requestSha256 = null }) {
673
704
  const request = requestSha256 ?? requestSha('coding_command_run', {
674
705
  root_id: rootId, command_id: commandId, script_path: scriptPath, script_sha256: scriptSha256,
675
706
  args, cwd, timeout_ms: timeoutMs
@@ -704,7 +735,7 @@ export function createCodingRuntime({ policy, signingKeyPath, verifyKeyPath }) {
704
735
  }
705
736
  const execution = (async () => {
706
737
  const started = new Date().toISOString();
707
- const result = await runCommandOnce({ rootId, commandId, scriptPath, scriptSha256, args, cwd, timeoutMs, executionDeadlineUtc, preExecutionCheck });
738
+ const result = await runCommandOnce({ rootId, commandId, scriptPath, scriptSha256, args, cwd, timeoutMs, executionDeadlineUtc, preExecutionCheck, cancellationCheck });
708
739
  const signed = await signCodingReceipt(policy, signingKeyPath, result.lease, {
709
740
  operation: 'coding_command_run', request_sha256: request, root_id: rootId, command_id: commandId,
710
741
  script_path: scriptPath, executable_sha256: result.executable_sha256, script_sha256: result.script_sha256,
@@ -751,7 +782,7 @@ export function createCodingRuntime({ policy, signingKeyPath, verifyKeyPath }) {
751
782
 
752
783
  async function benchmarkRun({ rootId, commandId, scriptPath, scriptSha256, args = [], cwd = '.', timeoutMs = null,
753
784
  executionDeadlineUtc = null, preExecutionCheck = null, warmups = 1, iterations = 5, baselineMedianMs = null, maxRegressionPct = 5,
754
- receiptId = null, requestSha256 = null }) {
785
+ receiptId = null, requestSha256 = null, cancellationCheck = null }) {
755
786
  if (!Number.isInteger(warmups) || warmups < 0 || warmups > 3) throw new Error('warmups must be in [0, 3]');
756
787
  if (!Number.isInteger(iterations) || iterations < 1 || iterations > 20) throw new Error('iterations must be in [1, 20]');
757
788
  if (baselineMedianMs !== null && (!Number.isFinite(baselineMedianMs) || baselineMedianMs <= 0)) throw new Error('baseline_median_ms must be positive or null');
@@ -762,13 +793,13 @@ export function createCodingRuntime({ policy, signingKeyPath, verifyKeyPath }) {
762
793
  max_regression_pct: maxRegressionPct
763
794
  });
764
795
  for (let i = 0; i < warmups; i += 1) {
765
- const warm = await runCommandOnce({ rootId, commandId, scriptPath, scriptSha256, args, cwd, timeoutMs, executionDeadlineUtc, preExecutionCheck });
796
+ const warm = await runCommandOnce({ rootId, commandId, scriptPath, scriptSha256, args, cwd, timeoutMs, executionDeadlineUtc, preExecutionCheck, cancellationCheck });
766
797
  if (warm.timed_out || warm.exit_code !== 0) throw new Error(`benchmark warmup failed exit=${String(warm.exit_code)} timed_out=${warm.timed_out}`);
767
798
  }
768
799
  const samples = [];
769
800
  let last = null;
770
801
  for (let i = 0; i < iterations; i += 1) {
771
- last = await runCommandOnce({ rootId, commandId, scriptPath, scriptSha256, args, cwd, timeoutMs, executionDeadlineUtc, preExecutionCheck });
802
+ last = await runCommandOnce({ rootId, commandId, scriptPath, scriptSha256, args, cwd, timeoutMs, executionDeadlineUtc, preExecutionCheck, cancellationCheck });
772
803
  if (last.timed_out || last.exit_code !== 0) throw new Error(`benchmark iteration ${i + 1} failed exit=${String(last.exit_code)} timed_out=${last.timed_out}`);
773
804
  samples.push(last.elapsed_ms);
774
805
  }
@@ -474,7 +474,7 @@ async function reconcileInterrupted(ctx, plan, events, derived, codingRuntime, s
474
474
  return deriveState(plan, events);
475
475
  }
476
476
 
477
- async function executeStep(codingRuntime, plan, step, attempt) {
477
+ async function executeStep(codingRuntime, plan, step, attempt, cancellationCheck = null) {
478
478
  const receiptId = receiptIdFor(plan.task_id, step.step_id, attempt, plan.plan_sha256);
479
479
  const requestSha256 = expectedRequest(codingRuntime, step);
480
480
  if (step.kind === 'write') {
@@ -486,12 +486,12 @@ async function executeStep(codingRuntime, plan, step, attempt) {
486
486
  } else if (step.kind === 'command') {
487
487
  await codingRuntime.commandRun({ rootId: step.root_id, commandId: step.command_id, scriptPath: step.script_path,
488
488
  scriptSha256: step.script_sha256, args: step.args, cwd: step.cwd, timeoutMs: step.timeout_ms,
489
- receiptId, requestSha256 });
489
+ receiptId, requestSha256, cancellationCheck });
490
490
  } else if (step.kind === 'benchmark') {
491
491
  await codingRuntime.benchmarkRun({ rootId: step.root_id, commandId: step.command_id, scriptPath: step.script_path,
492
492
  scriptSha256: step.script_sha256, args: step.args, cwd: step.cwd, timeoutMs: step.timeout_ms,
493
493
  warmups: step.warmups, iterations: step.iterations, baselineMedianMs: step.baseline_median_ms,
494
- maxRegressionPct: step.max_regression_pct, receiptId, requestSha256 });
494
+ maxRegressionPct: step.max_regression_pct, receiptId, requestSha256, cancellationCheck });
495
495
  } else {
496
496
  throw new Error(`coding step '${step.step_id}' is not executable`);
497
497
  }
@@ -670,7 +670,10 @@ export function createCodingTaskRuntime({ codingRuntime, signingKeyPath, verifyK
670
670
  try {
671
671
  const remainingMs = timeoutMs === null ? null : Math.max(100, Math.floor(deadlineState(deadline).remaining_ms));
672
672
  const boundedStep = remainingMs === null ? next : { ...next, timeout_ms: next.timeout_ms === null || next.timeout_ms === undefined ? remainingMs : Math.min(next.timeout_ms, remainingMs) };
673
- const executed = await executeStep(codingRuntime, plan, boundedStep, attempt);
673
+ const executed = await executeStep(codingRuntime, plan, boundedStep, attempt,()=>readCancel(ctx,verifyKeyPath));
674
+ if(await consumeCancel(ctx,journal,signingKeyPath,verifyKeyPath)){
675
+ derived=deriveState(plan,journal);return {status:'cancelled',task_id:taskId,...derived};
676
+ }
674
677
  if (!executed.outcome.valid) {
675
678
  await appendEvent(ctx, journal, signingKeyPath, 'task_paused', {
676
679
  stepId: next.step_id, payload: { reason: 'post_execute_receipt_mismatch', detail: executed.outcome.reason }
@@ -700,6 +703,9 @@ export function createCodingTaskRuntime({ codingRuntime, signingKeyPath, verifyK
700
703
  }
701
704
  derived = deriveState(plan, journal);
702
705
  } catch (error) {
706
+ if(await consumeCancel(ctx,journal,signingKeyPath,verifyKeyPath)){
707
+ derived=deriveState(plan,journal);return {status:'cancelled',task_id:taskId,...derived};
708
+ }
703
709
  if (deadlineExpired()) {
704
710
  derived = await failDeadline(ctx, journal, plan, next.step_id);
705
711
  return { status: 'failed', task_id: taskId, ...derived, deadline };
@@ -16,6 +16,8 @@ import { printDoctor } from './doctor-r45.mjs';
16
16
  import { createLiveTimelineFilter, createLiveTimelineTracker, createLiveTimelineWriter, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine, selectLiveLogNames } from './remote-live-log.mjs';
17
17
  import { prepareRemoteConsole, remoteDisplayText } from './remote-console.mjs';
18
18
  import { ensureRuntimeCoherence } from './runtime-coherence.mjs';
19
+ import { windowsPowerShellEnvironment } from './runtime-update.mjs';
20
+ import { resolveStateRoot } from './state-root.mjs';
19
21
 
20
22
  const REMOTE_COMMANDS=new Set([
21
23
  'menu','start','resume','park','stop','restart','status','url','test','fulltest','toggle','clean','machinetest',
@@ -42,7 +44,7 @@ const CURRENT_DEPLOY_SCRIPT=path.join(CURRENT_PACKAGE_ROOT,'scripts','deploy-r48
42
44
  const REMOTE_UPDATE_TIMEOUT_MS=900000;
43
45
  const LIVE_LOG_DISCOVERY_INTERVAL_MS=1000;
44
46
 
45
- function stateRoot(){return path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));}
47
+ function stateRoot(){return resolveStateRoot();}
46
48
  async function requireRegular(file,label){const info=await lstat(file);if(!info.isFile()||info.isSymbolicLink()) throw new Error(`${label} must be a regular non-symlink file`);return file;}
47
49
  function printHelp(){
48
50
  const exact=`deadbyte-mcp@${DEADBYTE_VERSION}`;
@@ -171,7 +173,7 @@ function invokeController(ctx,command,{capture=false,allowFailure=false,echoCapt
171
173
  stderrFd=openSync(redirectOutput.stderr_path,'w');
172
174
  stdio=['ignore',stdoutFd,stderrFd];
173
175
  }else stdio=capture?['ignore','pipe','pipe']:'inherit';
174
- const options={stdio,encoding:capture?'utf8':undefined,windowsHide:false,shell:false};
176
+ const options={stdio,encoding:capture?'utf8':undefined,windowsHide:false,shell:false,env:windowsPowerShellEnvironment()};
175
177
  if(Number.isInteger(timeoutMs)&&timeoutMs>0) options.timeout=timeoutMs;
176
178
  result=spawnSync(ctx.powershell,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',ctx.launcher,command],options);
177
179
  }finally{
@@ -4,9 +4,10 @@ import os from 'node:os';
4
4
  import path from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { DEADBYTE_VERSION, MCP_PROTOCOL_VERSION } from './version.mjs';
7
+ import { resolveStateRoot } from './state-root.mjs';
7
8
 
8
9
  const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
9
- const stateRoot=()=>path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
10
+ const stateRoot=()=>resolveStateRoot();
10
11
  const sha256=b=>createHash('sha256').update(b).digest('hex');
11
12
  async function jsonOrNull(file){try{return JSON.parse((await readFile(file,'utf8')).replace(/^\uFEFF/,''));}catch(e){if(e?.code==='ENOENT')return null;throw e;}}
12
13
  async function fileHash(file){return sha256(await readFile(file));}