deadbyte-mcp 0.14.0 → 0.15.1
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.
- package/AGENTS.md +11 -18
- package/CONTEXT.md +3 -3
- package/MANIFEST.SHA256 +156 -70
- package/PROVENANCE.json +10 -0
- package/R43-PARENT.json +20 -0
- package/R44-DONORS.json +99 -0
- package/R44-PARENT.json +18 -0
- package/R45-PARENT.json +18 -0
- package/README.txt +19 -14
- package/RELEASE.json +37 -0
- package/SBOM.cdx.json +46 -0
- package/TEST-MATRIX.json +31 -0
- package/bin/deadbyte-hardware-observer-r45.exe +0 -0
- package/bin/deadbyte-hardware-observer-r45.obj +0 -0
- package/controller/README.TXT +5 -6
- package/controller/deadbyte-controller.ps1 +2 -2
- package/docs/ARCHITECTURE.md +8 -1
- package/docs/R44-ARCHITECTURE.md +37 -0
- package/native/deadbyte-hardware-observer.cpp +308 -0
- package/package.json +18 -8
- package/scripts/autonomous-tests.ps1 +2 -2
- package/scripts/build-hardware-r45.ps1 +71 -0
- package/scripts/deferred-slot-operation-r43.mjs +126 -0
- package/scripts/deferred-slot-operation-r44.mjs +126 -0
- package/scripts/deferred-slot-operation-r45.mjs +126 -0
- package/scripts/derive-r45-parent.mjs +24 -0
- package/scripts/derive-r45-release.mjs +72 -0
- package/scripts/derive-r45-tests.mjs +20 -0
- package/scripts/final-closure-r43.mjs +115 -0
- package/scripts/final-closure-r44.mjs +121 -0
- package/scripts/final-closure-r45.mjs +121 -0
- package/scripts/final-closure-verify-r43.mjs +213 -0
- package/scripts/final-closure-verify-r44.mjs +220 -0
- package/scripts/final-closure-verify-r45.mjs +220 -0
- package/scripts/finalize-r43.mjs +217 -0
- package/scripts/finalize-r44.mjs +217 -0
- package/scripts/finalize-r45.mjs +219 -0
- package/scripts/full-regression-r43.mjs +20 -0
- package/scripts/full-regression-r44.mjs +20 -0
- package/scripts/full-regression-r45.mjs +20 -0
- package/scripts/full-regression-summary-r45.mjs +14 -0
- package/scripts/gate-windows-r43.ps1 +36 -0
- package/scripts/gate-windows-r44.ps1 +36 -0
- package/scripts/gate-windows-r45.ps1 +44 -0
- package/scripts/init-host-policy.ps1 +39 -1
- package/scripts/inspect-active-release-r45.mjs +20 -0
- package/scripts/release-manifest.mjs +3 -1
- package/scripts/release-parity-r43.mjs +218 -0
- package/scripts/release-parity-r44.mjs +242 -0
- package/scripts/release-parity-r45.mjs +253 -0
- package/scripts/release-parity-tests-r43.ps1 +8 -0
- package/scripts/release-parity-tests-r44.ps1 +8 -0
- package/scripts/release-parity-tests-r45.ps1 +8 -0
- package/scripts/verify-r43-parent.mjs +126 -0
- package/scripts/verify-r44-parent.mjs +142 -0
- package/scripts/verify-r45-parent.mjs +142 -0
- package/scripts/windows-gate-evidence-r43.mjs +55 -0
- package/scripts/windows-gate-evidence-r44.mjs +55 -0
- package/scripts/windows-gate-evidence-r45.mjs +60 -0
- package/src/agent-backend-r44.mjs +254 -0
- package/src/autonomous-output-contracts.mjs +1 -1
- package/src/autonomous-policy.mjs +41 -4
- package/src/autonomous-runtime.mjs +37 -19
- package/src/autonomous-supervisor-host.mjs +12 -2
- package/src/capability-ledger.mjs +3 -1
- package/src/codex-app-server-r44.mjs +154 -0
- package/src/coding-plane.mjs +66 -19
- package/src/compact-gateway-mcp-tools.mjs +4 -2
- package/src/deadbyte-cli.mjs +17 -9
- package/src/doctor-r45.mjs +65 -0
- package/src/durable-job-r44.mjs +145 -0
- package/src/external-job-provider-r44.mjs +126 -0
- package/src/host-plane.mjs +156 -87
- package/src/job-supervisor-host-r44.mjs +112 -0
- package/src/machine-fs-smoke-client.mjs +1 -1
- package/src/machine-fs.mjs +21 -10
- package/src/mcp-host-smoke-client.mjs +1 -1
- package/src/mcp-server.mjs +84 -26
- package/src/mcp-smoke-client.mjs +1 -1
- package/src/native-diff-v2.mjs +12 -7
- package/src/native-diff.mjs +32 -9
- package/src/observation-mcp-tools.mjs +5 -1
- package/src/observation-output-contracts.mjs +8 -0
- package/src/observation-runtime.mjs +33 -2
- package/src/observation-smoke-client.mjs +7 -3
- package/src/operator-surface.mjs +2 -2
- package/src/output-contracts.mjs +27 -11
- package/src/process-policy.mjs +19 -0
- package/src/process-runtime.mjs +5 -2
- package/src/process-smoke-client.mjs +2 -2
- package/src/provider-circuit.mjs +8 -2
- package/src/r44-compact-disarmed-probe.mjs +74 -0
- package/src/r44-full-surface-probe.mjs +34 -0
- package/src/r45-compact-disarmed-probe.mjs +74 -0
- package/src/r45-full-surface-probe.mjs +34 -0
- package/src/release-generation.mjs +1 -0
- package/src/remote-device-r44.mjs +205 -0
- package/src/remote-mcp-autonomous-smoke-client.mjs +4 -4
- package/src/remote-mcp-r44-parity-client.mjs +203 -0
- package/src/remote-mcp-r45-parity-client.mjs +206 -0
- package/src/remote-mcp-smoke-client.mjs +7 -4
- package/src/runtime-update.mjs +100 -39
- package/src/skill-mcp-tools-r44.mjs +37 -0
- package/src/skill-plane-r44.mjs +203 -0
- package/src/tunnel-stdio-compat-smoke.mjs +1 -1
- package/src/version.mjs +1 -1
- package/test/agent-backend-r44.test.mjs +115 -0
- package/test/agent-runtime.test.mjs +4 -3
- package/test/autonomous-loop.test.mjs +121 -11
- package/test/autonomous-runtime.test.mjs +3 -2
- package/test/capability-ledger-r36.test.mjs +3 -3
- package/test/cli-entrypoint.test.mjs +4 -4
- package/test/codex-app-server-r44.test.mjs +115 -0
- package/test/codex-autonomous-r44.test.mjs +87 -0
- package/test/coding-plane.test.mjs +3 -2
- package/test/coding-runtime.test.mjs +36 -1
- package/test/compact-catalog-r36.test.mjs +3 -3
- package/test/compact-max-catalog-r40.test.mjs +4 -2
- package/test/compact-max-catalog-r44.test.mjs +97 -0
- package/test/compact-max-catalog-r45.test.mjs +99 -0
- package/test/contained.test.mjs +23 -11
- package/test/controller.test.mjs +5 -1
- package/test/core.test.mjs +1 -1
- package/test/durable-job-r44.test.mjs +75 -0
- package/test/external-job-provider-r44.test.mjs +46 -0
- package/test/full-max-catalog-r40.test.mjs +5 -3
- package/test/full-max-catalog-r44.test.mjs +91 -0
- package/test/full-max-catalog-r45.test.mjs +91 -0
- package/test/hardware-observation-r45.test.mjs +44 -0
- package/test/host-inline-r45.test.mjs +67 -0
- package/test/host-plane.test.mjs +15 -1
- package/test/machine-mcp-integration.test.mjs +1 -0
- package/test/native-diff.test.mjs +28 -0
- package/test/observation-mcp-tools.test.mjs +4 -3
- package/test/output-schema.test.mjs +3 -2
- package/test/package-boundary.test.mjs +1 -1
- package/test/process-policy.test.mjs +22 -1
- package/test/production-docs.test.mjs +3 -3
- package/test/prompts-resources.test.mjs +1 -1
- package/test/r33-closeout-regression.test.mjs +20 -36
- package/test/r33-finalization.test.mjs +7 -7
- package/test/r43-finalization.test.mjs +31 -0
- package/test/r43-finalizer.test.mjs +34 -0
- package/test/r43-parent.test.mjs +29 -0
- package/test/r44-finalization.test.mjs +58 -0
- package/test/r44-finalizer.test.mjs +48 -0
- package/test/r44-parent.test.mjs +25 -0
- package/test/r45-autonomous-quick-test.test.mjs +10 -0
- package/test/r45-finalization.test.mjs +84 -0
- package/test/release-generation.test.mjs +1 -0
- package/test/release-version.test.mjs +65 -20
- package/test/remote-device-r44.test.mjs +104 -0
- package/test/runtime-host-bootstrap-r45.test.mjs +46 -0
- package/test/runtime-self-update.test.mjs +42 -0
- package/test/skill-mcp-r44.test.mjs +38 -0
- package/test/skill-plane-r44.test.mjs +93 -0
- package/test/standing-grants.test.mjs +5 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
$ErrorActionPreference = 'Stop'
|
|
2
|
+
Set-StrictMode -Version 2.0
|
|
3
|
+
function Invoke-GateStep {
|
|
4
|
+
param([Parameter(Mandatory = $true)][string]$Name,[Parameter(Mandatory = $true)][string[]]$NpmArgs)
|
|
5
|
+
Write-Host "==== $Name ===="
|
|
6
|
+
& npm.cmd @NpmArgs
|
|
7
|
+
if ($LASTEXITCODE -ne 0) { throw "DEADBYTE V0.15.1 / R45 gate failed at $Name with exit code $LASTEXITCODE" }
|
|
8
|
+
}
|
|
9
|
+
Invoke-GateStep -Name 'r45-parent-verify' -NpmArgs @('run','r45:parent:verify')
|
|
10
|
+
Invoke-GateStep -Name 'containment-build' -NpmArgs @('run','containment:build')
|
|
11
|
+
Invoke-GateStep -Name 'process-policy-refresh' -NpmArgs @('run','process:policy:refresh')
|
|
12
|
+
Invoke-GateStep -Name 'desktop-policy-refresh' -NpmArgs @('run','desktop:policy:refresh')
|
|
13
|
+
Invoke-GateStep -Name 'hardware-build' -NpmArgs @('run','hardware:build')
|
|
14
|
+
Write-Host '==== host-policy-hermetic-clean ===='
|
|
15
|
+
$HostPolicyPath = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\controller\deadbyte-host-policy.json'))
|
|
16
|
+
Remove-Item -LiteralPath $HostPolicyPath -Force -ErrorAction SilentlyContinue
|
|
17
|
+
if (Test-Path -LiteralPath $HostPolicyPath) { throw "DEADBYTE V0.15.1 / R45 gate could not remove machine-local Host policy before hermetic regression" }
|
|
18
|
+
Write-Host 'HOST_POLICY_HERMETIC_ABSENT'
|
|
19
|
+
Write-Host '==== r45-feature-focused ===='
|
|
20
|
+
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot 'release-parity-tests-r45.ps1')
|
|
21
|
+
if ($LASTEXITCODE -ne 0) { throw "DEADBYTE V0.15.1 / R45 gate failed at r45-feature-focused with exit code $LASTEXITCODE" }
|
|
22
|
+
Invoke-GateStep -Name 'full-regression' -NpmArgs @('run','r45:full:test')
|
|
23
|
+
Invoke-GateStep -Name 'mcp-smoke' -NpmArgs @('run','mcp:smoke')
|
|
24
|
+
Invoke-GateStep -Name 'mcp-agent-smoke' -NpmArgs @('run','mcp:agent:smoke')
|
|
25
|
+
Invoke-GateStep -Name 'mcp-coding-smoke' -NpmArgs @('run','mcp:coding:smoke')
|
|
26
|
+
Invoke-GateStep -Name 'mcp-machine-fs-smoke' -NpmArgs @('run','mcp:machine-fs:smoke')
|
|
27
|
+
Invoke-GateStep -Name 'mcp-observation-smoke' -NpmArgs @('run','mcp:observation:smoke')
|
|
28
|
+
Invoke-GateStep -Name 'mcp-process-smoke' -NpmArgs @('run','mcp:process:smoke')
|
|
29
|
+
Invoke-GateStep -Name 'mcp-autonomous-smoke' -NpmArgs @('run','mcp:autonomous:smoke')
|
|
30
|
+
Invoke-GateStep -Name 'tunnel-stdio-compat-smoke' -NpmArgs @('run','mcp:tunnel-compat:smoke')
|
|
31
|
+
Invoke-GateStep -Name 'containment-smoke' -NpmArgs @('run','containment:smoke')
|
|
32
|
+
Invoke-GateStep -Name 'contained-smoke' -NpmArgs @('run','contained:smoke')
|
|
33
|
+
Invoke-GateStep -Name 'mcp-contained-smoke' -NpmArgs @('run','mcp:contained:smoke')
|
|
34
|
+
Invoke-GateStep -Name 'host-policy-refresh' -NpmArgs @('run','host:policy:init')
|
|
35
|
+
Invoke-GateStep -Name 'release-manifest-write' -NpmArgs @('run','manifest:write')
|
|
36
|
+
Invoke-GateStep -Name 'release-manifest' -NpmArgs @('run','manifest:verify')
|
|
37
|
+
Invoke-GateStep -Name 'doctor' -NpmArgs @('run','doctor')
|
|
38
|
+
Invoke-GateStep -Name 'mcp-host-smoke' -NpmArgs @('run','mcp:host:smoke')
|
|
39
|
+
Invoke-GateStep -Name 'authority-smoke' -NpmArgs @('run','authority:smoke')
|
|
40
|
+
Invoke-GateStep -Name 'release-manifest-final' -NpmArgs @('run','manifest:verify')
|
|
41
|
+
Write-Host '==== windows-gate-evidence ===='
|
|
42
|
+
& node.exe (Join-Path $PSScriptRoot 'windows-gate-evidence-r45.mjs')
|
|
43
|
+
if ($LASTEXITCODE -ne 0) { throw "DEADBYTE V0.15.1 / R45 gate failed at windows-gate-evidence with exit code $LASTEXITCODE" }
|
|
44
|
+
Write-Host '==== DEADBYTE V0.15.1 / R45 MACHINE FREEDOM WINDOWS BATCH GATE: PASS ===='
|
|
@@ -93,16 +93,54 @@ $commands = [ordered]@{
|
|
|
93
93
|
timeout_ms = 120000
|
|
94
94
|
max_output_bytes = 1048576
|
|
95
95
|
}
|
|
96
|
+
powershell_inline = [ordered]@{
|
|
97
|
+
kind = 'inline'
|
|
98
|
+
executable = $powerShellExe
|
|
99
|
+
executable_sha256 = Get-Sha256Lower $powerShellExe
|
|
100
|
+
root_ids = $rootIds
|
|
101
|
+
prefix_args = @('-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-Command')
|
|
102
|
+
timeout_ms = 120000
|
|
103
|
+
max_output_bytes = 1048576
|
|
104
|
+
max_command_chars = 32768
|
|
105
|
+
}
|
|
106
|
+
cmd_inline = [ordered]@{
|
|
107
|
+
kind = 'inline'
|
|
108
|
+
executable = $cmdExe
|
|
109
|
+
executable_sha256 = Get-Sha256Lower $cmdExe
|
|
110
|
+
root_ids = $rootIds
|
|
111
|
+
prefix_args = @('/D','/S','/C')
|
|
112
|
+
timeout_ms = 120000
|
|
113
|
+
max_output_bytes = 1048576
|
|
114
|
+
max_command_chars = 32768
|
|
115
|
+
}
|
|
116
|
+
node_inline = [ordered]@{
|
|
117
|
+
kind = 'inline'
|
|
118
|
+
executable = $nodeExe
|
|
119
|
+
executable_sha256 = Get-Sha256Lower $nodeExe
|
|
120
|
+
root_ids = $rootIds
|
|
121
|
+
prefix_args = @('-e')
|
|
122
|
+
timeout_ms = 120000
|
|
123
|
+
max_output_bytes = 1048576
|
|
124
|
+
max_command_chars = 32768
|
|
125
|
+
}
|
|
96
126
|
}
|
|
97
127
|
|
|
98
128
|
$policy = [ordered]@{
|
|
99
|
-
schema = 'deadbyte.host-policy.
|
|
129
|
+
schema = 'deadbyte.host-policy.v2'
|
|
100
130
|
enabled = $true
|
|
101
131
|
arm_file = $armFile
|
|
102
132
|
max_read_bytes = 262144
|
|
103
133
|
max_write_bytes = 1048576
|
|
104
134
|
roots = $roots
|
|
105
135
|
commands = $commands
|
|
136
|
+
native_exec = [ordered]@{
|
|
137
|
+
enabled = $true
|
|
138
|
+
root_ids = $rootIds
|
|
139
|
+
timeout_ms = 120000
|
|
140
|
+
max_output_bytes = 1048576
|
|
141
|
+
max_args = 64
|
|
142
|
+
max_arg_chars = 8192
|
|
143
|
+
}
|
|
106
144
|
}
|
|
107
145
|
|
|
108
146
|
$parent = Split-Path -Parent $PolicyPath
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
const sha256=b=>createHash('sha256').update(b).digest('hex');
|
|
6
|
+
const root=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
7
|
+
const pointer=JSON.parse(await readFile(path.join(root,'active-release.json'),'utf8'));
|
|
8
|
+
const finalPath=path.join(root,'evidence','release',`r44-final-${pointer.manifest_sha256.slice(0,16)}.json`);
|
|
9
|
+
const finalBytes=await readFile(finalPath);
|
|
10
|
+
const releaseManifest=await readFile(path.join(pointer.release_root,'MANIFEST.SHA256'),'utf8');
|
|
11
|
+
const generationManifest=await readFile(path.join(pointer.generation_root,'GENERATION.SHA256'),'utf8');
|
|
12
|
+
const count=s=>s.split(/\r?\n/).filter(Boolean).length;
|
|
13
|
+
console.log(JSON.stringify({
|
|
14
|
+
pointer,
|
|
15
|
+
final_path:finalPath,
|
|
16
|
+
final_file_sha256:sha256(finalBytes),
|
|
17
|
+
final_closure_sha256:JSON.parse(finalBytes).closure_sha256,
|
|
18
|
+
release_manifest_file_count:count(releaseManifest),
|
|
19
|
+
generation_manifest_file_count:count(generationManifest)
|
|
20
|
+
},null,2));
|
|
@@ -26,6 +26,8 @@ const requiredBundledDependencyRoots = [
|
|
|
26
26
|
'node_modules/@modelcontextprotocol/client/package.json',
|
|
27
27
|
'node_modules/zod/package.json'
|
|
28
28
|
];
|
|
29
|
+
const excludedPackagePrefixes = ['tmp/','.deadbyte-machine-fs-recovery/'];
|
|
30
|
+
const excludedPackagePaths = new Set(['controller/deadbyte-host-policy.json']);
|
|
29
31
|
const allowedWorkspacePayload = new Set([
|
|
30
32
|
'workspaces/autonomous-live/fixture.txt',
|
|
31
33
|
'workspaces/demo/input.txt'
|
|
@@ -57,7 +59,7 @@ function npmPackFiles() {
|
|
|
57
59
|
const raw = item?.path;
|
|
58
60
|
if (typeof raw !== 'string' || raw.length < 1) throw new Error('npm pack file path invalid');
|
|
59
61
|
const rel = raw.replaceAll('\\','/');
|
|
60
|
-
if (rel === manifestName) continue;
|
|
62
|
+
if (rel === manifestName || excludedPackagePaths.has(rel) || excludedPackagePrefixes.some(prefix => rel.startsWith(prefix))) continue;
|
|
61
63
|
if (path.posix.isAbsolute(rel) || rel.split('/').includes('..') || rel.includes('\0')) {
|
|
62
64
|
throw new Error(`unsafe package path '${rel}'`);
|
|
63
65
|
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, readFile, realpath, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { loadPowerShellDataFile } from '../src/runtime-update.mjs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { probeR40FullSurface } from '../src/r40-full-surface-probe.mjs';
|
|
9
|
+
import { runR40CompactParity } from '../src/remote-mcp-r40-parity-client.mjs';
|
|
10
|
+
import { probeR40CompactDisarmed } from '../src/r40-compact-disarmed-probe.mjs';
|
|
11
|
+
import { probeR36CompactDisarmed } from '../src/r36-compact-disarmed-probe.mjs';
|
|
12
|
+
import { runR40DesktopGuardedSmoke } from '../src/remote-mcp-desktop-r40-smoke-client.mjs';
|
|
13
|
+
import { verifyR41Memory } from './verify-memory-r41.mjs';
|
|
14
|
+
|
|
15
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const root=path.resolve(here,'..');
|
|
17
|
+
const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
18
|
+
const deferred=path.join(root,'scripts','deferred-slot-operation-r43.mjs');
|
|
19
|
+
const ps='C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
|
|
20
|
+
const launcher=path.join(stateRoot,'bin','DEADBYTE-MCP.ps1');
|
|
21
|
+
const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
|
|
22
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
23
|
+
|
|
24
|
+
export const R43_PREDECESSOR_RELEASE_ID='v0.14.0-08cd8f3b98c90e0e';
|
|
25
|
+
export const R43_PREDECESSOR_MANIFEST='08cd8f3b98c90e0e250cf506ff396515065ff5457909f850b75b0b6e8ecc2dd1';
|
|
26
|
+
|
|
27
|
+
function assert(condition,message){if(!condition) throw new Error(message);}
|
|
28
|
+
function parseFrozenManifest(text){
|
|
29
|
+
const entries=[];const seen=new Set();
|
|
30
|
+
for(const [index,line] of text.split(/\r?\n/).entries()){
|
|
31
|
+
if(!line) continue;
|
|
32
|
+
const match=/^([0-9a-f]{64}) ([^\0]+)$/.exec(line);
|
|
33
|
+
assert(match,`frozen manifest line ${index+1} malformed`);
|
|
34
|
+
const rel=match[2];
|
|
35
|
+
assert(!path.posix.isAbsolute(rel)&&!rel.split('/').includes('..')&&!seen.has(rel),`unsafe/duplicate frozen manifest path: ${rel}`);
|
|
36
|
+
seen.add(rel);entries.push({sha256:match[1],path:rel});
|
|
37
|
+
}
|
|
38
|
+
return entries;
|
|
39
|
+
}
|
|
40
|
+
// Restore the mutable development stage from the immutable active release after live parity work may drift it.
|
|
41
|
+
export async function restoreFrozenStage({developmentRoot,releaseRoot,expectedManifestSha256}){
|
|
42
|
+
const dev=path.resolve(developmentRoot);const release=path.resolve(releaseRoot);
|
|
43
|
+
const manifestBytes=await readFile(path.join(release,'MANIFEST.SHA256'));
|
|
44
|
+
const manifestSha256=sha256(manifestBytes);
|
|
45
|
+
assert(manifestSha256===expectedManifestSha256,`immutable release manifest drift: ${manifestSha256}`);
|
|
46
|
+
const entries=parseFrozenManifest(manifestBytes.toString('utf8'));
|
|
47
|
+
for(const entry of entries){
|
|
48
|
+
const source=path.join(release,...entry.path.split('/'));const bytes=await readFile(source);
|
|
49
|
+
assert(sha256(bytes)===entry.sha256,`immutable release file drift: ${entry.path}`);
|
|
50
|
+
const destination=path.join(dev,...entry.path.split('/'));await mkdir(path.dirname(destination),{recursive:true});
|
|
51
|
+
await writeFile(destination,bytes);assert(sha256(await readFile(destination))===entry.sha256,`development restore verification failed: ${entry.path}`);
|
|
52
|
+
}
|
|
53
|
+
await writeFile(path.join(dev,'MANIFEST.SHA256'),manifestBytes);
|
|
54
|
+
assert(sha256(await readFile(path.join(dev,'MANIFEST.SHA256')))===expectedManifestSha256,'development manifest restore verification failed');
|
|
55
|
+
return {status:'restored',file_count:entries.length,manifest_sha256:manifestSha256};
|
|
56
|
+
}
|
|
57
|
+
function parseJsonOutput(text,label){
|
|
58
|
+
const trimmed=String(text).trim();const starts=[...trimmed.matchAll(/\{/g)].map(match=>match.index);
|
|
59
|
+
for(const start of starts){try{return JSON.parse(trimmed.slice(start));}catch{}}
|
|
60
|
+
throw new Error(`${label} did not emit JSON`);
|
|
61
|
+
}
|
|
62
|
+
function run(command,args,{timeoutMs=900000,env=process.env,cwd=root}={}){
|
|
63
|
+
return new Promise((resolve,reject)=>{
|
|
64
|
+
const child=spawn(command,args,{cwd,shell:false,windowsHide:true,env,stdio:['ignore','pipe','pipe']});
|
|
65
|
+
const out=[],err=[];const timer=setTimeout(()=>{child.kill();reject(new Error(`command timeout: ${path.basename(command)}`));},timeoutMs);
|
|
66
|
+
child.stdout.on('data',chunk=>out.push(Buffer.from(chunk)));child.stderr.on('data',chunk=>err.push(Buffer.from(chunk)));
|
|
67
|
+
child.on('error',error=>{clearTimeout(timer);reject(error);});child.on('exit',code=>{
|
|
68
|
+
clearTimeout(timer);const stdout=Buffer.concat(out).toString('utf8');const stderr=Buffer.concat(err).toString('utf8');
|
|
69
|
+
if(code!==0) reject(new Error(`command failed exit=${code}: ${stderr||stdout}`));else resolve({stdout,stderr,exitCode:code});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
async function publicUrl(){
|
|
74
|
+
const value=(await readFile(path.join(stateRoot,'bridge','mcp-url.txt'),'utf8')).trim();const url=new URL(value);
|
|
75
|
+
if(url.protocol!=='https:') throw new Error('public MCP URL must be HTTPS');return value;
|
|
76
|
+
}
|
|
77
|
+
async function pointer(){return JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));}
|
|
78
|
+
async function disarmAll(){
|
|
79
|
+
for(const command of ['disarmdesktop','disarmautonomous','disarmcoding','disarmprocess','disarmhost']){
|
|
80
|
+
await run(ps,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,command],{timeoutMs:60000});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function waitDeferred(statePath,timeoutMs=180000){
|
|
84
|
+
const deadline=Date.now()+timeoutMs;
|
|
85
|
+
while(Date.now()<deadline){const state=JSON.parse(await readFile(statePath,'utf8'));if(state.state==='completed') return state;if(state.state==='failed') throw new Error(`deferred ${state.mode} failed: ${state.error}`);await sleep(500);}
|
|
86
|
+
throw new Error(`deferred operation timeout: ${statePath}`);
|
|
87
|
+
}
|
|
88
|
+
async function scheduleDeferred(mode,value,{bridgeRoot=null,bridgeScriptSha256=null}={}){
|
|
89
|
+
if((bridgeRoot===null)!==(bridgeScriptSha256===null)) throw new Error('deferred bridge override requires exact root + SHA pair');
|
|
90
|
+
const args=[deferred,mode,value];
|
|
91
|
+
if(bridgeRoot!==null) args.push('--bridge-root',bridgeRoot,'--bridge-script-sha256',bridgeScriptSha256);
|
|
92
|
+
const scheduled=parseJsonOutput((await run(process.execPath,args,{timeoutMs:30000})).stdout,`deferred ${mode}`);
|
|
93
|
+
assert(scheduled.status==='scheduled',`deferred ${mode} was not scheduled`);
|
|
94
|
+
assert(scheduled.execution_provider==='deadbyte_mcp'&&scheduled.rdc_operation_count===0,'deferred operation provider mismatch');
|
|
95
|
+
return {scheduled,completed:await waitDeferred(scheduled.state_path)};
|
|
96
|
+
}
|
|
97
|
+
async function publicR40PredecessorDisarmed(url){
|
|
98
|
+
const result=await probeR36CompactDisarmed(url);
|
|
99
|
+
assert(result.status==='passed'&&result.tool_count<=42&&result.capability_closure===true,'R42 predecessor public compact catalog mismatch');
|
|
100
|
+
assert(result.release_id===R43_PREDECESSOR_RELEASE_ID&&result.release_manifest_sha256===R43_PREDECESSOR_MANIFEST,'R42 predecessor machine binding mismatch');
|
|
101
|
+
assert(result.coding_armed===false&&result.autonomous_armed===false,'R42 predecessor authority remained armed');
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
async function mutableCore(pointerValue){
|
|
105
|
+
const policy=JSON.parse(await readFile(path.join(pointerValue.generation_root,'coding-policy.json'),'utf8'));
|
|
106
|
+
const raw=policy?.roots?.core?.path??policy?.roots?.core?.real_path;
|
|
107
|
+
assert(typeof raw==='string'&&raw,'R40 generation missing coding core root');
|
|
108
|
+
const core=path.resolve(raw);const release=path.resolve(pointerValue.release_root);
|
|
109
|
+
assert(core.toLowerCase()!==release.toLowerCase(),'R40 coding core must not point at immutable active release slot');
|
|
110
|
+
return core;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function validateParityEvidence(value){
|
|
114
|
+
assert(value?.schema==='deadbyte.r43-parity.v1','R40 parity schema mismatch');
|
|
115
|
+
assert(value.status==='passed','R40 parity status is not passed');
|
|
116
|
+
assert(value.rdc_operation_count===0&&value.execution_provider==='deadbyte_mcp','R40 parity used non-DEADBYTE execution');
|
|
117
|
+
assert(value.candidate?.version==='0.14.1'&&/^[0-9a-f]{64}$/.test(value.candidate?.manifest_sha256??''),'R40 candidate identity missing');
|
|
118
|
+
assert(value.local_full?.status==='passed'&&value.local_full?.tool_count===80&&value.local_full?.strict_output_schemas===true,'R40 local full 80-tool proof missing');
|
|
119
|
+
assert(value.public_compact?.status==='passed'&&value.public_compact?.tool_count===42&&value.public_compact?.capability_closure===true&&value.public_compact?.gateway_count===6&&value.public_compact?.desktop_armed===false&&value.public_compact?.desktop_observation_receipt_verified===true&&value.public_compact?.desktop_grounding_receipt_verified===true,'R40 public compact/Desktop closure missing');
|
|
120
|
+
assert(value.desktop_guarded?.status==='passed'&&value.desktop_guarded?.native_injected===true&&value.desktop_guarded?.replay_performed===false&&
|
|
121
|
+
Number.isInteger(value.desktop_guarded?.predispatch_retry_count)&&value.desktop_guarded.predispatch_retry_count>=0&&value.desktop_guarded.predispatch_retry_count<=2&&
|
|
122
|
+
value.desktop_guarded?.observation_receipts_verified===value.desktop_guarded.predispatch_retry_count+1&&value.desktop_guarded?.action_receipts_verified===3,
|
|
123
|
+
'R40 guarded Desktop proof missing');
|
|
124
|
+
assert(value.autonomous?.status==='passed'&&value.autonomous?.signed_release_verified===true,'R40 autonomous live proof missing');
|
|
125
|
+
assert(value.deterministic_memory?.status==='passed'&&value.deterministic_memory?.goal_count>=1&&value.deterministic_memory?.snapshot_count>=1&&value.deterministic_memory?.index_count>=1,'R41 deterministic memory proof missing');
|
|
126
|
+
assert(value.before_rollback_disarmed?.status==='passed'&&value.before_rollback_disarmed?.capability_closure===true&&value.before_rollback_disarmed?.coding_armed===false&&value.before_rollback_disarmed?.autonomous_armed===false&&value.before_rollback_disarmed?.desktop_armed===false,'R40 pre-rollback disarm proof missing');
|
|
127
|
+
assert(value.rollback?.state==='completed'&&value.restore?.state==='completed','R40 rollback/restore not completed');
|
|
128
|
+
assert(value.stage_restore?.status==='restored'&&value.stage_restore?.manifest_sha256===value.candidate.manifest_sha256,'R40 stage restore proof missing');
|
|
129
|
+
assert(value.predecessor?.release_id===R43_PREDECESSOR_RELEASE_ID&&value.predecessor?.manifest_sha256===R43_PREDECESSOR_MANIFEST&&value.predecessor?.version==='0.14.0','R43 exact R42 predecessor identity mismatch');
|
|
130
|
+
assert(value.predecessor_disarmed?.status==='passed'&&value.predecessor_disarmed?.tool_count<=42&&value.predecessor_disarmed?.capability_closure===true,'R42 predecessor public proof missing');
|
|
131
|
+
assert(value.successor?.version==='0.14.1'&&value.successor?.manifest_sha256===value.candidate.manifest_sha256,'R40 exact successor restore identity mismatch');
|
|
132
|
+
assert(value.successor_disarmed?.status==='passed'&&value.successor_disarmed?.capability_closure===true&&value.successor_disarmed?.coding_armed===false&&value.successor_disarmed?.autonomous_armed===false&&value.successor_disarmed?.desktop_armed===false,'R40 successor disarmed proof missing');
|
|
133
|
+
assert(value.immutable_slot?.protected===true&&value.immutable_slot?.development_root!==value.immutable_slot?.release_root,'R40 immutable-slot separation missing');
|
|
134
|
+
for(const key of ['tree_read','precondition_mutation','search','owned_process','observation','coding_loop','autonomy','interrupted_recovery','evidence_verification','compact_closure','full_surface','desktop_readonly','desktop_guarded_input','deterministic_memory']) assert(value.requirements?.[key]===true,`R43 parity requirement not proven: ${key}`);
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function runReleaseParity(activationReceiptPath){
|
|
139
|
+
const active=await pointer();const candidateManifest=sha256(await readFile(path.join(root,'MANIFEST.SHA256')));
|
|
140
|
+
assert(active.version==='0.14.1','R43 parity requires active 0.14.1 candidate');
|
|
141
|
+
assert(active.manifest_sha256===candidateManifest,'active release is not canonical R40 candidate');
|
|
142
|
+
const activeConfig=await loadPowerShellDataFile(active.config_path);
|
|
143
|
+
const successorBridgeRootRaw=activeConfig?.Bridge?.WorkingDirectory;
|
|
144
|
+
const successorBridgeConfiguredSha=activeConfig?.Bridge?.ScriptSha256;
|
|
145
|
+
assert(typeof successorBridgeRootRaw==='string'&&path.isAbsolute(successorBridgeRootRaw),'R40 active bridge root missing');
|
|
146
|
+
assert(typeof successorBridgeConfiguredSha==='string'&&/^[0-9a-f]{64}$/.test(successorBridgeConfiguredSha),'R40 active bridge SHA missing');
|
|
147
|
+
const successorBridgeRoot=await realpath(path.resolve(successorBridgeRootRaw));
|
|
148
|
+
const successorBridgeActualSha=sha256(await readFile(path.join(successorBridgeRoot,'src','bridge.mjs')));
|
|
149
|
+
assert(successorBridgeActualSha===successorBridgeConfiguredSha,'R40 active bridge bytes do not match generation pin');
|
|
150
|
+
const successorBridge={root:successorBridgeRoot,sha256:successorBridgeActualSha};
|
|
151
|
+
const developmentRoot=await mutableCore(active);
|
|
152
|
+
const localFull=await probeR40FullSurface({packageRoot:active.release_root,generationRoot:active.generation_root,stateRoot});
|
|
153
|
+
const url=await publicUrl();
|
|
154
|
+
let publicCompact,autonomous,desktopGuarded;
|
|
155
|
+
try{
|
|
156
|
+
publicCompact=await runR40CompactParity(url);
|
|
157
|
+
autonomous=parseJsonOutput((await run(process.execPath,[path.join(root,'src','remote-mcp-autonomous-smoke-client.mjs'),url],{timeoutMs:900000})).stdout,'R40 autonomous compact parity');
|
|
158
|
+
assert(autonomous.status==='passed'&&autonomous.signed_release_verified===true,'R40 autonomous compact parity failed');
|
|
159
|
+
await run(ps,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,'armdesktop'],{timeoutMs:60000});
|
|
160
|
+
try{desktopGuarded=await runR40DesktopGuardedSmoke(url);}
|
|
161
|
+
finally{await run(ps,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,'disarmdesktop'],{timeoutMs:60000}).catch(()=>{});}
|
|
162
|
+
assert(desktopGuarded.status==='passed'&&desktopGuarded.native_injected===true&&desktopGuarded.action_receipts_verified===3,'R40 guarded Desktop public smoke failed');
|
|
163
|
+
}catch(error){await disarmAll().catch(()=>{});throw error;}
|
|
164
|
+
const deterministicMemory=await verifyR41Memory({memoryRoot:path.join(stateRoot,'evidence','autonomous','memory-r41',candidateManifest),
|
|
165
|
+
journalRoot:path.join(stateRoot,'evidence','autonomous','goals'),sourceReleaseManifest:candidateManifest,
|
|
166
|
+
publicKeyPath:path.join(stateRoot,'trust','ed25519-public.pem')});
|
|
167
|
+
assert(deterministicMemory.goal_count>=1&&deterministicMemory.snapshot_count>=1&&deterministicMemory.index_count>=1,'R41 live deterministic memory evidence missing');
|
|
168
|
+
await disarmAll();
|
|
169
|
+
const beforeRollback=await probeR40CompactDisarmed(url);
|
|
170
|
+
const rollbackPair=await scheduleDeferred('rollback',path.resolve(activationReceiptPath));
|
|
171
|
+
const predecessor=await pointer();
|
|
172
|
+
assert(predecessor.release_id===R43_PREDECESSOR_RELEASE_ID&&predecessor.manifest_sha256===R43_PREDECESSOR_MANIFEST&&predecessor.version==='0.14.0','rollback did not reach exact sealed R42 predecessor');
|
|
173
|
+
const predecessorDisarmed=await publicR40PredecessorDisarmed(url);
|
|
174
|
+
const stageRestore=await restoreFrozenStage({developmentRoot,releaseRoot:active.release_root,expectedManifestSha256:candidateManifest});
|
|
175
|
+
const restorePair=await scheduleDeferred('activate',candidateManifest,{bridgeRoot:successorBridge.root,bridgeScriptSha256:successorBridge.sha256});
|
|
176
|
+
const successor=await pointer();
|
|
177
|
+
assert(successor.version==='0.14.1'&&successor.manifest_sha256===candidateManifest,'restore did not return exact R40 candidate');
|
|
178
|
+
const successorDisarmed=await probeR40CompactDisarmed(url);
|
|
179
|
+
await disarmAll();
|
|
180
|
+
|
|
181
|
+
const evidence={
|
|
182
|
+
schema:'deadbyte.r43-parity.v1',status:'passed',completed_at_utc:new Date().toISOString(),execution_provider:'deadbyte_mcp',rdc_operation_count:0,
|
|
183
|
+
candidate:{release_id:active.release_id,version:active.version,manifest_sha256:candidateManifest},
|
|
184
|
+
immutable_slot:{protected:true,release_root:path.resolve(active.release_root),development_root:developmentRoot},
|
|
185
|
+
local_full:localFull,public_compact:publicCompact,autonomous,deterministic_memory:deterministicMemory,desktop_guarded:desktopGuarded,before_rollback_disarmed:beforeRollback,
|
|
186
|
+
rollback:{...rollbackPair.completed,scheduled_operation_id:rollbackPair.scheduled.operation_id},
|
|
187
|
+
predecessor:{release_id:predecessor.release_id,version:predecessor.version,manifest_sha256:predecessor.manifest_sha256},predecessor_disarmed:predecessorDisarmed,
|
|
188
|
+
stage_restore:stageRestore,
|
|
189
|
+
restore:{...restorePair.completed,scheduled_operation_id:restorePair.scheduled.operation_id},
|
|
190
|
+
successor:{release_id:successor.release_id,version:successor.version,manifest_sha256:successor.manifest_sha256},successor_disarmed:successorDisarmed,
|
|
191
|
+
requirements:{
|
|
192
|
+
tree_read:publicCompact.tree_entries>=0&&publicCompact.coding_stat_verified===true,
|
|
193
|
+
precondition_mutation:publicCompact.machine_fs_receipts_verified>=3,
|
|
194
|
+
search:publicCompact.coding_search_result_count>=1&&publicCompact.observation_search_result_count>=1,
|
|
195
|
+
owned_process:typeof publicCompact.process_session_id==='string'&&publicCompact.process_output_bytes>0,
|
|
196
|
+
observation:publicCompact.observation_receipts_verified>=2,
|
|
197
|
+
coding_loop:publicCompact.coding_receipts_verified===2,
|
|
198
|
+
autonomy:autonomous.signed_release_verified===true&&autonomous.observed_failure_before_replan===true,
|
|
199
|
+
interrupted_recovery:rollbackPair.completed.state==='completed'&&restorePair.completed.state==='completed',
|
|
200
|
+
evidence_verification:publicCompact.machine_fs_receipts_verified>=3&&autonomous.signed_release_verified===true,
|
|
201
|
+
compact_closure:publicCompact.capability_closure===true&&successorDisarmed.capability_closure===true,
|
|
202
|
+
full_surface:localFull.tool_count===80&&localFull.strict_output_schemas===true,
|
|
203
|
+
desktop_readonly:publicCompact.desktop_armed===false&&publicCompact.desktop_observation_receipt_verified===true&&publicCompact.desktop_grounding_receipt_verified===true,
|
|
204
|
+
desktop_guarded_input:desktopGuarded.native_injected===true&&desktopGuarded.replay_performed===false&&desktopGuarded.action_receipts_verified===3,
|
|
205
|
+
deterministic_memory:deterministicMemory.status==='passed'&&deterministicMemory.goal_count>=1&&deterministicMemory.snapshot_count>=1
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
validateParityEvidence(evidence);
|
|
209
|
+
const evidenceDir=path.join(stateRoot,'evidence','release');await mkdir(evidenceDir,{recursive:true});
|
|
210
|
+
const evidencePath=path.join(evidenceDir,`r43-parity-${candidateManifest.slice(0,16)}.json`);
|
|
211
|
+
const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');await writeFile(evidencePath,bytes);
|
|
212
|
+
return {evidence,evidence_path:evidencePath,evidence_sha256:sha256(bytes)};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){
|
|
216
|
+
const receipt=process.argv[2];if(!receipt) throw new Error('usage: node scripts/release-parity-r43.mjs <activation-receipt.json>');
|
|
217
|
+
runReleaseParity(receipt).then(result=>console.log(JSON.stringify({status:'passed',...result},null,2))).catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
218
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, readFile, realpath, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { loadPowerShellDataFile } from '../src/runtime-update.mjs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { probeR44FullSurface } from '../src/r44-full-surface-probe.mjs';
|
|
9
|
+
import { runR44CompactParity } from '../src/remote-mcp-r44-parity-client.mjs';
|
|
10
|
+
import { probeR44CompactDisarmed } from '../src/r44-compact-disarmed-probe.mjs';
|
|
11
|
+
import { probeR40CompactDisarmed } from '../src/r40-compact-disarmed-probe.mjs';
|
|
12
|
+
import { probeR36CompactDisarmed } from '../src/r36-compact-disarmed-probe.mjs';
|
|
13
|
+
import { runR40DesktopGuardedSmoke } from '../src/remote-mcp-desktop-r40-smoke-client.mjs';
|
|
14
|
+
import { verifyR41Memory } from './verify-memory-r41.mjs';
|
|
15
|
+
|
|
16
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const root=path.resolve(here,'..');
|
|
18
|
+
const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
19
|
+
const deferred=path.join(root,'scripts','deferred-slot-operation-r44.mjs');
|
|
20
|
+
const ps='C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
|
|
21
|
+
const launcher=path.join(stateRoot,'bin','DEADBYTE-MCP.ps1');
|
|
22
|
+
const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
|
|
23
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
24
|
+
|
|
25
|
+
export const R44_PREDECESSOR_RELEASE_ID='v0.14.1-0a5a833892593283';
|
|
26
|
+
export const R44_PREDECESSOR_MANIFEST='0a5a8338925932830986421dda60ec5643f62f600404de4992fda2a5e79d09a9';
|
|
27
|
+
|
|
28
|
+
function assert(condition,message){if(!condition) throw new Error(message);}
|
|
29
|
+
function parseFrozenManifest(text){
|
|
30
|
+
const entries=[];const seen=new Set();
|
|
31
|
+
for(const [index,line] of text.split(/\r?\n/).entries()){
|
|
32
|
+
if(!line) continue;
|
|
33
|
+
const match=/^([0-9a-f]{64}) ([^\0]+)$/.exec(line);
|
|
34
|
+
assert(match,`frozen manifest line ${index+1} malformed`);
|
|
35
|
+
const rel=match[2];
|
|
36
|
+
assert(!path.posix.isAbsolute(rel)&&!rel.split('/').includes('..')&&!seen.has(rel),`unsafe/duplicate frozen manifest path: ${rel}`);
|
|
37
|
+
seen.add(rel);entries.push({sha256:match[1],path:rel});
|
|
38
|
+
}
|
|
39
|
+
return entries;
|
|
40
|
+
}
|
|
41
|
+
// Restore the mutable development stage from the immutable active release after live parity work may drift it.
|
|
42
|
+
export async function restoreFrozenStage({developmentRoot,releaseRoot,expectedManifestSha256}){
|
|
43
|
+
const dev=path.resolve(developmentRoot);const release=path.resolve(releaseRoot);
|
|
44
|
+
const manifestBytes=await readFile(path.join(release,'MANIFEST.SHA256'));
|
|
45
|
+
const manifestSha256=sha256(manifestBytes);
|
|
46
|
+
assert(manifestSha256===expectedManifestSha256,`immutable release manifest drift: ${manifestSha256}`);
|
|
47
|
+
const entries=parseFrozenManifest(manifestBytes.toString('utf8'));
|
|
48
|
+
for(const entry of entries){
|
|
49
|
+
const source=path.join(release,...entry.path.split('/'));const bytes=await readFile(source);
|
|
50
|
+
assert(sha256(bytes)===entry.sha256,`immutable release file drift: ${entry.path}`);
|
|
51
|
+
const destination=path.join(dev,...entry.path.split('/'));await mkdir(path.dirname(destination),{recursive:true});
|
|
52
|
+
await writeFile(destination,bytes);assert(sha256(await readFile(destination))===entry.sha256,`development restore verification failed: ${entry.path}`);
|
|
53
|
+
}
|
|
54
|
+
await writeFile(path.join(dev,'MANIFEST.SHA256'),manifestBytes);
|
|
55
|
+
assert(sha256(await readFile(path.join(dev,'MANIFEST.SHA256')))===expectedManifestSha256,'development manifest restore verification failed');
|
|
56
|
+
return {status:'restored',file_count:entries.length,manifest_sha256:manifestSha256};
|
|
57
|
+
}
|
|
58
|
+
function parseJsonOutput(text,label){
|
|
59
|
+
const trimmed=String(text).trim();const starts=[...trimmed.matchAll(/\{/g)].map(match=>match.index);
|
|
60
|
+
for(const start of starts){try{return JSON.parse(trimmed.slice(start));}catch{}}
|
|
61
|
+
throw new Error(`${label} did not emit JSON`);
|
|
62
|
+
}
|
|
63
|
+
function run(command,args,{timeoutMs=900000,env=process.env,cwd=root}={}){
|
|
64
|
+
return new Promise((resolve,reject)=>{
|
|
65
|
+
const child=spawn(command,args,{cwd,shell:false,windowsHide:true,env,stdio:['ignore','pipe','pipe']});
|
|
66
|
+
const out=[],err=[];const timer=setTimeout(()=>{child.kill();reject(new Error(`command timeout: ${path.basename(command)}`));},timeoutMs);
|
|
67
|
+
child.stdout.on('data',chunk=>out.push(Buffer.from(chunk)));child.stderr.on('data',chunk=>err.push(Buffer.from(chunk)));
|
|
68
|
+
child.on('error',error=>{clearTimeout(timer);reject(error);});child.on('exit',code=>{
|
|
69
|
+
clearTimeout(timer);const stdout=Buffer.concat(out).toString('utf8');const stderr=Buffer.concat(err).toString('utf8');
|
|
70
|
+
if(code!==0) reject(new Error(`command failed exit=${code}: ${stderr||stdout}`));else resolve({stdout,stderr,exitCode:code});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async function publicUrl(){
|
|
75
|
+
const value=(await readFile(path.join(stateRoot,'bridge','mcp-url.txt'),'utf8')).trim();const url=new URL(value);
|
|
76
|
+
if(url.protocol!=='https:') throw new Error('public MCP URL must be HTTPS');return value;
|
|
77
|
+
}
|
|
78
|
+
async function pointer(){return JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));}
|
|
79
|
+
async function disarmAll(){
|
|
80
|
+
for(const command of ['disarmdesktop','disarmautonomous','disarmcoding','disarmprocess','disarmhost']){
|
|
81
|
+
await run(ps,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,command],{timeoutMs:60000});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function waitDeferred(statePath,timeoutMs=180000){
|
|
85
|
+
const deadline=Date.now()+timeoutMs;
|
|
86
|
+
while(Date.now()<deadline){const state=JSON.parse(await readFile(statePath,'utf8'));if(state.state==='completed') return state;if(state.state==='failed') throw new Error(`deferred ${state.mode} failed: ${state.error}`);await sleep(500);}
|
|
87
|
+
throw new Error(`deferred operation timeout: ${statePath}`);
|
|
88
|
+
}
|
|
89
|
+
async function scheduleDeferred(mode,value,{bridgeRoot=null,bridgeScriptSha256=null}={}){
|
|
90
|
+
if((bridgeRoot===null)!==(bridgeScriptSha256===null)) throw new Error('deferred bridge override requires exact root + SHA pair');
|
|
91
|
+
const args=[deferred,mode,value];
|
|
92
|
+
if(bridgeRoot!==null) args.push('--bridge-root',bridgeRoot,'--bridge-script-sha256',bridgeScriptSha256);
|
|
93
|
+
const scheduled=parseJsonOutput((await run(process.execPath,args,{timeoutMs:30000})).stdout,`deferred ${mode}`);
|
|
94
|
+
assert(scheduled.status==='scheduled',`deferred ${mode} was not scheduled`);
|
|
95
|
+
assert(scheduled.execution_provider==='deadbyte_mcp'&&scheduled.rdc_operation_count===0,'deferred operation provider mismatch');
|
|
96
|
+
return {scheduled,completed:await waitDeferred(scheduled.state_path)};
|
|
97
|
+
}
|
|
98
|
+
async function publicR43PredecessorDisarmed(url){
|
|
99
|
+
const result=await probeR40CompactDisarmed(url);
|
|
100
|
+
assert(result.status==='passed'&&result.tool_count===42&&result.capability_closure===true,'R43 predecessor public compact catalog mismatch');
|
|
101
|
+
assert(result.release_id===R44_PREDECESSOR_RELEASE_ID&&result.release_manifest_sha256===R44_PREDECESSOR_MANIFEST,'R43 predecessor machine binding mismatch');
|
|
102
|
+
assert(result.coding_armed===false&&result.autonomous_armed===false&&result.desktop_armed===false,'R43 predecessor authority remained armed');
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
async function mutableCore(pointerValue){
|
|
106
|
+
const policy=JSON.parse(await readFile(path.join(pointerValue.generation_root,'coding-policy.json'),'utf8'));
|
|
107
|
+
const raw=policy?.roots?.core?.path??policy?.roots?.core?.real_path;
|
|
108
|
+
assert(typeof raw==='string'&&raw,'R40 generation missing coding core root');
|
|
109
|
+
const core=path.resolve(raw);const release=path.resolve(pointerValue.release_root);
|
|
110
|
+
assert(core.toLowerCase()!==release.toLowerCase(),'R40 coding core must not point at immutable active release slot');
|
|
111
|
+
return core;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function validateFeatureGate(value,manifest){
|
|
115
|
+
assert(value?.schema==='deadbyte.r44-windows-gate.v1'&&value.status==='passed'&&value.exit_code===0,'R44 feature gate invalid');
|
|
116
|
+
assert(value.version==='0.15.0'&&value.manifest_sha256===manifest,'R44 feature gate candidate mismatch');
|
|
117
|
+
for(const key of ['skill_integrity','agent_backend_persistence','codex_protocol_contract','remote_device_identity','durable_job_supervisor','real_external_supervisor_smoke','full_regression_required']) {
|
|
118
|
+
assert(value.features?.[key]===true,`R44 feature gate missing ${key}`);
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
async function readFeatureGate(manifest){
|
|
123
|
+
const file=path.join(stateRoot,'evidence','release',`r44-windows-gate-${manifest.slice(0,16)}.json`);
|
|
124
|
+
const bytes=await readFile(file);const value=JSON.parse(bytes.toString('utf8').replace(/^\uFEFF/,''));
|
|
125
|
+
validateFeatureGate(value,manifest);
|
|
126
|
+
return {value,path:file,sha256:sha256(bytes)};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function validateParityEvidence(value){
|
|
130
|
+
assert(value?.schema==='deadbyte.r44-parity.v1','R44 parity schema mismatch');
|
|
131
|
+
assert(value.status==='passed','R40 parity status is not passed');
|
|
132
|
+
assert(value.rdc_operation_count===0&&value.execution_provider==='deadbyte_mcp','R40 parity used non-DEADBYTE execution');
|
|
133
|
+
assert(value.candidate?.version==='0.15.0'&&/^[0-9a-f]{64}$/.test(value.candidate?.manifest_sha256??''),'R40 candidate identity missing');
|
|
134
|
+
assert(value.local_full?.status==='passed'&&value.local_full?.tool_count===81&&value.local_full?.strict_output_schemas===true,'R44 local full 81-tool proof missing');
|
|
135
|
+
assert(value.public_compact?.status==='passed'&&value.public_compact?.tool_count===43&&value.public_compact?.capability_closure===true&&value.public_compact?.gateway_count===6&&value.public_compact?.desktop_armed===false&&value.public_compact?.desktop_observation_receipt_verified===true&&value.public_compact?.desktop_grounding_receipt_verified===true,'R44 public compact/Desktop/skill closure missing');
|
|
136
|
+
assert(value.desktop_guarded?.status==='passed'&&value.desktop_guarded?.native_injected===true&&value.desktop_guarded?.replay_performed===false&&
|
|
137
|
+
Number.isInteger(value.desktop_guarded?.predispatch_retry_count)&&value.desktop_guarded.predispatch_retry_count>=0&&value.desktop_guarded.predispatch_retry_count<=2&&
|
|
138
|
+
value.desktop_guarded?.observation_receipts_verified===value.desktop_guarded.predispatch_retry_count+1&&value.desktop_guarded?.action_receipts_verified===3,
|
|
139
|
+
'R40 guarded Desktop proof missing');
|
|
140
|
+
assert(value.autonomous?.status==='passed'&&value.autonomous?.signed_release_verified===true,'R40 autonomous live proof missing');
|
|
141
|
+
assert(value.deterministic_memory?.status==='passed'&&value.deterministic_memory?.goal_count>=1&&value.deterministic_memory?.snapshot_count>=1&&value.deterministic_memory?.index_count>=1,'R41 deterministic memory proof missing');
|
|
142
|
+
assert(value.before_rollback_disarmed?.status==='passed'&&value.before_rollback_disarmed?.capability_closure===true&&value.before_rollback_disarmed?.coding_armed===false&&value.before_rollback_disarmed?.autonomous_armed===false&&value.before_rollback_disarmed?.desktop_armed===false,'R40 pre-rollback disarm proof missing');
|
|
143
|
+
assert(value.rollback?.state==='completed'&&value.restore?.state==='completed','R40 rollback/restore not completed');
|
|
144
|
+
assert(value.stage_restore?.status==='restored'&&value.stage_restore?.manifest_sha256===value.candidate.manifest_sha256,'R40 stage restore proof missing');
|
|
145
|
+
assert(value.predecessor?.release_id===R44_PREDECESSOR_RELEASE_ID&&value.predecessor?.manifest_sha256===R44_PREDECESSOR_MANIFEST&&value.predecessor?.version==='0.14.1','R44 exact R43 predecessor identity mismatch');
|
|
146
|
+
assert(value.predecessor_disarmed?.status==='passed'&&value.predecessor_disarmed?.tool_count===42&&value.predecessor_disarmed?.capability_closure===true,'R43 predecessor public proof missing');
|
|
147
|
+
assert(value.successor?.version==='0.15.0'&&value.successor?.manifest_sha256===value.candidate.manifest_sha256,'R44 exact successor restore identity mismatch');
|
|
148
|
+
assert(value.successor_disarmed?.status==='passed'&&value.successor_disarmed?.capability_closure===true&&value.successor_disarmed?.coding_armed===false&&value.successor_disarmed?.autonomous_armed===false&&value.successor_disarmed?.desktop_armed===false,'R40 successor disarmed proof missing');
|
|
149
|
+
assert(value.immutable_slot?.protected===true&&value.immutable_slot?.development_root!==value.immutable_slot?.release_root,'R40 immutable-slot separation missing');
|
|
150
|
+
for(const key of ['tree_read','precondition_mutation','search','owned_process','observation','coding_loop','autonomy','interrupted_recovery','evidence_verification','compact_closure','full_surface','desktop_readonly','desktop_guarded_input','deterministic_memory','skill_integrity','agent_backend_persistence','codex_protocol_contract','remote_device_identity','durable_job_supervisor','real_external_supervisor_smoke']) assert(value.requirements?.[key]===true,`R44 parity requirement not proven: ${key}`);
|
|
151
|
+
validateFeatureGate(value.feature_gate,value.candidate.manifest_sha256);
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export async function runReleaseParity(activationReceiptPath){
|
|
156
|
+
const active=await pointer();const candidateManifest=sha256(await readFile(path.join(root,'MANIFEST.SHA256')));
|
|
157
|
+
assert(active.version==='0.15.0','R44 parity requires active 0.15.0 candidate');
|
|
158
|
+
assert(active.manifest_sha256===candidateManifest,'active release is not canonical R40 candidate');
|
|
159
|
+
const activeConfig=await loadPowerShellDataFile(active.config_path);
|
|
160
|
+
const successorBridgeRootRaw=activeConfig?.Bridge?.WorkingDirectory;
|
|
161
|
+
const successorBridgeConfiguredSha=activeConfig?.Bridge?.ScriptSha256;
|
|
162
|
+
assert(typeof successorBridgeRootRaw==='string'&&path.isAbsolute(successorBridgeRootRaw),'R40 active bridge root missing');
|
|
163
|
+
assert(typeof successorBridgeConfiguredSha==='string'&&/^[0-9a-f]{64}$/.test(successorBridgeConfiguredSha),'R40 active bridge SHA missing');
|
|
164
|
+
const successorBridgeRoot=await realpath(path.resolve(successorBridgeRootRaw));
|
|
165
|
+
const successorBridgeActualSha=sha256(await readFile(path.join(successorBridgeRoot,'src','bridge.mjs')));
|
|
166
|
+
assert(successorBridgeActualSha===successorBridgeConfiguredSha,'R40 active bridge bytes do not match generation pin');
|
|
167
|
+
const successorBridge={root:successorBridgeRoot,sha256:successorBridgeActualSha};
|
|
168
|
+
const developmentRoot=await mutableCore(active);
|
|
169
|
+
const localFull=await probeR44FullSurface({packageRoot:active.release_root,generationRoot:active.generation_root,stateRoot});
|
|
170
|
+
const url=await publicUrl();
|
|
171
|
+
let publicCompact,autonomous,desktopGuarded;
|
|
172
|
+
try{
|
|
173
|
+
publicCompact=await runR44CompactParity(url);
|
|
174
|
+
autonomous=parseJsonOutput((await run(process.execPath,[path.join(root,'src','remote-mcp-autonomous-smoke-client.mjs'),url],{timeoutMs:900000})).stdout,'R40 autonomous compact parity');
|
|
175
|
+
assert(autonomous.status==='passed'&&autonomous.signed_release_verified===true,'R40 autonomous compact parity failed');
|
|
176
|
+
await run(ps,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,'armdesktop'],{timeoutMs:60000});
|
|
177
|
+
try{desktopGuarded=await runR40DesktopGuardedSmoke(url);}
|
|
178
|
+
finally{await run(ps,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,'disarmdesktop'],{timeoutMs:60000}).catch(()=>{});}
|
|
179
|
+
assert(desktopGuarded.status==='passed'&&desktopGuarded.native_injected===true&&desktopGuarded.action_receipts_verified===3,'R40 guarded Desktop public smoke failed');
|
|
180
|
+
}catch(error){await disarmAll().catch(()=>{});throw error;}
|
|
181
|
+
const deterministicMemory=await verifyR41Memory({memoryRoot:path.join(stateRoot,'evidence','autonomous','memory-r41',candidateManifest),
|
|
182
|
+
journalRoot:path.join(stateRoot,'evidence','autonomous','goals'),sourceReleaseManifest:candidateManifest,
|
|
183
|
+
publicKeyPath:path.join(stateRoot,'trust','ed25519-public.pem')});
|
|
184
|
+
assert(deterministicMemory.goal_count>=1&&deterministicMemory.snapshot_count>=1&&deterministicMemory.index_count>=1,'R41 live deterministic memory evidence missing');
|
|
185
|
+
await disarmAll();
|
|
186
|
+
const beforeRollback=await probeR44CompactDisarmed(url);
|
|
187
|
+
const rollbackPair=await scheduleDeferred('rollback',path.resolve(activationReceiptPath));
|
|
188
|
+
const predecessor=await pointer();
|
|
189
|
+
assert(predecessor.release_id===R44_PREDECESSOR_RELEASE_ID&&predecessor.manifest_sha256===R44_PREDECESSOR_MANIFEST&&predecessor.version==='0.14.1','rollback did not reach exact sealed R43 predecessor');
|
|
190
|
+
const predecessorDisarmed=await publicR43PredecessorDisarmed(url);
|
|
191
|
+
const stageRestore=await restoreFrozenStage({developmentRoot,releaseRoot:active.release_root,expectedManifestSha256:candidateManifest});
|
|
192
|
+
const restorePair=await scheduleDeferred('activate',candidateManifest,{bridgeRoot:successorBridge.root,bridgeScriptSha256:successorBridge.sha256});
|
|
193
|
+
const successor=await pointer();
|
|
194
|
+
assert(successor.version==='0.15.0'&&successor.manifest_sha256===candidateManifest,'restore did not return exact R44 candidate');
|
|
195
|
+
const successorDisarmed=await probeR44CompactDisarmed(url);
|
|
196
|
+
await disarmAll();
|
|
197
|
+
|
|
198
|
+
const featureGate=await readFeatureGate(candidateManifest);
|
|
199
|
+
const evidence={
|
|
200
|
+
schema:'deadbyte.r44-parity.v1',status:'passed',completed_at_utc:new Date().toISOString(),execution_provider:'deadbyte_mcp',rdc_operation_count:0,
|
|
201
|
+
candidate:{release_id:active.release_id,version:active.version,manifest_sha256:candidateManifest},
|
|
202
|
+
immutable_slot:{protected:true,release_root:path.resolve(active.release_root),development_root:developmentRoot},
|
|
203
|
+
local_full:localFull,public_compact:publicCompact,autonomous,deterministic_memory:deterministicMemory,desktop_guarded:desktopGuarded,feature_gate:featureGate.value,feature_gate_sha256:featureGate.sha256,before_rollback_disarmed:beforeRollback,
|
|
204
|
+
rollback:{...rollbackPair.completed,scheduled_operation_id:rollbackPair.scheduled.operation_id},
|
|
205
|
+
predecessor:{release_id:predecessor.release_id,version:predecessor.version,manifest_sha256:predecessor.manifest_sha256},predecessor_disarmed:predecessorDisarmed,
|
|
206
|
+
stage_restore:stageRestore,
|
|
207
|
+
restore:{...restorePair.completed,scheduled_operation_id:restorePair.scheduled.operation_id},
|
|
208
|
+
successor:{release_id:successor.release_id,version:successor.version,manifest_sha256:successor.manifest_sha256},successor_disarmed:successorDisarmed,
|
|
209
|
+
requirements:{
|
|
210
|
+
tree_read:publicCompact.tree_entries>=0&&publicCompact.coding_stat_verified===true,
|
|
211
|
+
precondition_mutation:publicCompact.machine_fs_receipts_verified>=3,
|
|
212
|
+
search:publicCompact.coding_search_result_count>=1&&publicCompact.observation_search_result_count>=1,
|
|
213
|
+
owned_process:typeof publicCompact.process_session_id==='string'&&publicCompact.process_output_bytes>0,
|
|
214
|
+
observation:publicCompact.observation_receipts_verified>=2,
|
|
215
|
+
coding_loop:publicCompact.coding_receipts_verified===2,
|
|
216
|
+
autonomy:autonomous.signed_release_verified===true&&autonomous.observed_failure_before_replan===true,
|
|
217
|
+
interrupted_recovery:rollbackPair.completed.state==='completed'&&restorePair.completed.state==='completed',
|
|
218
|
+
evidence_verification:publicCompact.machine_fs_receipts_verified>=3&&autonomous.signed_release_verified===true,
|
|
219
|
+
compact_closure:publicCompact.capability_closure===true&&successorDisarmed.capability_closure===true,
|
|
220
|
+
full_surface:localFull.tool_count===81&&localFull.strict_output_schemas===true,
|
|
221
|
+
desktop_readonly:publicCompact.desktop_armed===false&&publicCompact.desktop_observation_receipt_verified===true&&publicCompact.desktop_grounding_receipt_verified===true,
|
|
222
|
+
desktop_guarded_input:desktopGuarded.native_injected===true&&desktopGuarded.replay_performed===false&&desktopGuarded.action_receipts_verified===3,
|
|
223
|
+
deterministic_memory:deterministicMemory.status==='passed'&&deterministicMemory.goal_count>=1&&deterministicMemory.snapshot_count>=1,
|
|
224
|
+
skill_integrity:publicCompact.skill_catalog_verified===true&&successorDisarmed.skill_catalog_verified===true&&featureGate.value.features.skill_integrity===true,
|
|
225
|
+
agent_backend_persistence:featureGate.value.features.agent_backend_persistence===true,
|
|
226
|
+
codex_protocol_contract:featureGate.value.features.codex_protocol_contract===true,
|
|
227
|
+
remote_device_identity:featureGate.value.features.remote_device_identity===true,
|
|
228
|
+
durable_job_supervisor:featureGate.value.features.durable_job_supervisor===true,
|
|
229
|
+
real_external_supervisor_smoke:featureGate.value.features.real_external_supervisor_smoke===true
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
validateParityEvidence(evidence);
|
|
233
|
+
const evidenceDir=path.join(stateRoot,'evidence','release');await mkdir(evidenceDir,{recursive:true});
|
|
234
|
+
const evidencePath=path.join(evidenceDir,`r44-parity-${candidateManifest.slice(0,16)}.json`);
|
|
235
|
+
const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');await writeFile(evidencePath,bytes);
|
|
236
|
+
return {evidence,evidence_path:evidencePath,evidence_sha256:sha256(bytes)};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){
|
|
240
|
+
const receipt=process.argv[2];if(!receipt) throw new Error('usage: node scripts/release-parity-r44.mjs <activation-receipt.json>');
|
|
241
|
+
runReleaseParity(receipt).then(result=>console.log(JSON.stringify({status:'passed',...result},null,2))).catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
242
|
+
}
|