deadbyte-mcp 0.11.2 → 0.11.3
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/MANIFEST.SHA256 +25 -17
- package/README.txt +7 -7
- package/controller/README.TXT +1 -1
- package/controller/deadbyte-controller.ps1 +1 -1
- package/docs/ARCHITECTURE.md +1 -1
- package/package.json +6 -6
- package/scripts/deferred-slot-operation-r39.mjs +124 -0
- package/scripts/final-closure-r39.mjs +113 -0
- package/scripts/final-closure-verify-r39.mjs +208 -0
- package/scripts/gate-windows-r39.ps1 +42 -0
- package/scripts/release-parity-r39.mjs +200 -0
- package/scripts/release-parity-tests-r39.ps1 +9 -0
- package/scripts/windows-gate-evidence-r39.mjs +52 -0
- package/src/deadbyte-cli.mjs +7 -3
- package/src/remote-live-log.mjs +9 -2
- package/src/version.mjs +1 -1
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/core.test.mjs +1 -1
- package/test/production-docs.test.mjs +3 -3
- package/test/r33-closeout-regression.test.mjs +8 -8
- package/test/r33-finalization.test.mjs +14 -14
- package/test/r37-remote-tool-call-timeline.test.mjs +2 -2
- package/test/r39-finalization.test.mjs +74 -0
- package/test/release-version.test.mjs +30 -27
- package/test/remote-live-log-r37.test.mjs +1 -1
- package/test/remote-session-cli.test.mjs +46 -12
|
@@ -5,7 +5,7 @@ import { mkdtemp, mkdir, readFile, rm, writeFile, readdir } from 'node:fs/promis
|
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
|
-
import * as parity from '../scripts/release-parity-
|
|
8
|
+
import * as parity from '../scripts/release-parity-r39.mjs';
|
|
9
9
|
import { createToolAudit } from '../src/tool-audit.mjs';
|
|
10
10
|
import { verifyAuditChain } from '../src/audit-verify.mjs';
|
|
11
11
|
import { generateTrustKeypair } from '../src/trust.mjs';
|
|
@@ -86,12 +86,12 @@ test('audit restart reconciles an orphan request with an explicit interrupted re
|
|
|
86
86
|
}finally{await rm(stateRoot,{recursive:true,force:true});}
|
|
87
87
|
});
|
|
88
88
|
|
|
89
|
-
test('Windows
|
|
89
|
+
test('Windows R39 gate emits manifest-bound machine-readable evidence outside the candidate tree', async()=>{
|
|
90
90
|
const stateRoot=await temp('deadbyte-r33-windows-evidence-');
|
|
91
91
|
try{
|
|
92
|
-
const gate=await readFile(path.join(root,'scripts','gate-windows-
|
|
93
|
-
assert.match(gate,/windows-gate-evidence-
|
|
94
|
-
const result=await run(process.execPath,[path.join(root,'scripts','windows-gate-evidence-
|
|
92
|
+
const gate=await readFile(path.join(root,'scripts','gate-windows-r39.ps1'),'utf8');
|
|
93
|
+
assert.match(gate,/windows-gate-evidence-r39\.mjs/,'gate-windows-r39.ps1 must invoke the evidence writer');
|
|
94
|
+
const result=await run(process.execPath,[path.join(root,'scripts','windows-gate-evidence-r39.mjs')],{
|
|
95
95
|
cwd:root,env:{...process.env,DEADBYTE_STATE_ROOT:stateRoot}
|
|
96
96
|
});
|
|
97
97
|
assert.equal(result.code,0,result.stderr||result.stdout);
|
|
@@ -99,12 +99,12 @@ test('Windows R38 gate emits manifest-bound machine-readable evidence outside th
|
|
|
99
99
|
assert.equal(summary.status,'written');
|
|
100
100
|
const evidence=JSON.parse(await readFile(summary.path,'utf8'));
|
|
101
101
|
const manifestSha=sha256(await readFile(path.join(root,'MANIFEST.SHA256')));
|
|
102
|
-
assert.equal(evidence.schema,'deadbyte.
|
|
102
|
+
assert.equal(evidence.schema,'deadbyte.r39-windows-gate.v1');
|
|
103
103
|
assert.equal(evidence.status,'passed');
|
|
104
104
|
assert.equal(evidence.exit_code,0);
|
|
105
|
-
assert.equal(evidence.version,'0.11.
|
|
105
|
+
assert.equal(evidence.version,'0.11.3');
|
|
106
106
|
assert.equal(evidence.manifest_sha256,manifestSha);
|
|
107
|
-
assert.equal(evidence.marker,'DEADBYTE V0.11.
|
|
107
|
+
assert.equal(evidence.marker,'DEADBYTE V0.11.3 / R39 WINDOWS BATCH GATE: PASS');
|
|
108
108
|
assert.ok(summary.path.startsWith(path.join(stateRoot,'evidence','release')));
|
|
109
109
|
}finally{await rm(stateRoot,{recursive:true,force:true});}
|
|
110
110
|
});
|
|
@@ -2,8 +2,8 @@ import test from 'node:test';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
4
|
import { R34_EXPECTED_TOOLS } from '../src/remote-mcp-r34-parity-client.mjs';
|
|
5
|
-
import { validateParityEvidence } from '../scripts/release-parity-
|
|
6
|
-
import { verifyFinalClosureObject } from '../scripts/final-closure-verify-
|
|
5
|
+
import { validateParityEvidence } from '../scripts/release-parity-r39.mjs';
|
|
6
|
+
import { verifyFinalClosureObject } from '../scripts/final-closure-verify-r39.mjs';
|
|
7
7
|
|
|
8
8
|
const root = new URL('../', import.meta.url);
|
|
9
9
|
const text = rel => readFile(new URL(rel, root), 'utf8');
|
|
@@ -19,28 +19,28 @@ test('R34 public parity catalog is exact, unique, and includes production coding
|
|
|
19
19
|
|
|
20
20
|
test('release parity and final verifier are package entry points', async () => {
|
|
21
21
|
const pkg = JSON.parse(await text('package.json'));
|
|
22
|
-
assert.equal(pkg.scripts['release:parity'], 'node scripts/release-parity-
|
|
23
|
-
assert.equal(pkg.scripts['release:final:write'], 'node scripts/final-closure-
|
|
24
|
-
assert.equal(pkg.scripts['release:final:verify'], 'node scripts/final-closure-verify-
|
|
22
|
+
assert.equal(pkg.scripts['release:parity'], 'node scripts/release-parity-r39.mjs');
|
|
23
|
+
assert.equal(pkg.scripts['release:final:write'], 'node scripts/final-closure-r39.mjs');
|
|
24
|
+
assert.equal(pkg.scripts['release:final:verify'], 'node scripts/final-closure-verify-r39.mjs');
|
|
25
25
|
assert.equal(typeof validateParityEvidence, 'function');
|
|
26
26
|
assert.equal(typeof verifyFinalClosureObject, 'function');
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
test('
|
|
30
|
-
const verifier = await text('scripts/final-closure-verify-
|
|
31
|
-
const writer = await text('scripts/final-closure-
|
|
29
|
+
test('R39 final closure verifier is implementation-independent from production helpers', async () => {
|
|
30
|
+
const verifier = await text('scripts/final-closure-verify-r39.mjs');
|
|
31
|
+
const writer = await text('scripts/final-closure-r39.mjs');
|
|
32
32
|
assert.doesNotMatch(verifier, /from ['"]\.\.\/src\//);
|
|
33
33
|
assert.match(verifier, /DEADBYTE-R27-SLOT-UPDATE-V1/);
|
|
34
34
|
assert.match(verifier, /DEADBYTE-MACHINE-PING-V1/);
|
|
35
|
-
assert.match(verifier, /DEADBYTE-
|
|
36
|
-
assert.match(writer, /pointer\.version==='0\.11\.
|
|
37
|
-
assert.match(verifier, /envelope\.body\.version==='0\.11\.
|
|
38
|
-
assert.match(verifier, /release_label==='
|
|
39
|
-
assert.match(verifier, /
|
|
35
|
+
assert.match(verifier, /DEADBYTE-R39-FINAL-CLOSURE-V1/);
|
|
36
|
+
assert.match(writer, /pointer\.version==='0\.11\.3'/);
|
|
37
|
+
assert.match(verifier, /envelope\.body\.version==='0\.11\.3'/);
|
|
38
|
+
assert.match(verifier, /release_label==='R39 FINAL MACHINE BASELINE'/);
|
|
39
|
+
assert.match(verifier, /058605dfccaa3035a57c6393d53f2421c14e5a92bd145c2334477ba24d7cab8b/);
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
test('final closure verifier canonicalizes traversal order before exact-tree comparison', async () => {
|
|
43
|
-
const verifier = await text('scripts/final-closure-verify-
|
|
43
|
+
const verifier = await text('scripts/final-closure-verify-r39.mjs');
|
|
44
44
|
assert.match(verifier, /const actual=\(await walk\(root\)\)\.filter\(rel=>rel!==manifestName\)\.sort\(\(a,b\)=>a\.localeCompare\(b,'en'\)\);/);
|
|
45
45
|
assert.match(verifier, /const expected=\[\.\.\.entries\.keys\(\)\]\.sort\(\(a,b\)=>a\.localeCompare\(b,'en'\)\);/);
|
|
46
46
|
});
|
|
@@ -24,7 +24,7 @@ test('R37 unified timeline renders one sanitized tool-call line with ISO timesta
|
|
|
24
24
|
}
|
|
25
25
|
};
|
|
26
26
|
const line=formatRemoteToolCall(event);
|
|
27
|
-
assert.match(line,/^2026-09-
|
|
27
|
+
assert.match(line,/^2026-09-18T23:24:30\.125\+07:00 \| coding_file_write\s+\| Arguments: \{/);
|
|
28
28
|
assert.match(line,/"root_id":"projects"/);
|
|
29
29
|
assert.match(line,/"path":"src\/example\.mjs"/);
|
|
30
30
|
assert.match(line,/"reason":"payload"/);
|
|
@@ -37,7 +37,7 @@ test('R37 live tracker uses the tool-call line for request and keeps correlated
|
|
|
37
37
|
const tracker=createLiveTimelineTracker({clock:()=>now});
|
|
38
38
|
const call=tracker.observe({event_type:'tool.request',request_id:'abcdef1234567890',tool:'coding_tree',
|
|
39
39
|
created_at_utc:'2026-09-18T16:24:30.125Z',args:{root_id:'projects',path:'src'}});
|
|
40
|
-
assert.match(call,/^2026-09-
|
|
40
|
+
assert.match(call,/^2026-09-18T23:24:30\.125\+07:00 \| coding_tree\s+\| Arguments: \{"path":"src","root_id":"projects"\}$/);
|
|
41
41
|
now+=250;
|
|
42
42
|
assert.equal(tracker.observe({event_type:'tool.result',request_id:'abcdef1234567890',tool:'coding_tree',
|
|
43
43
|
outcome:{is_error:false,duration_ms:250}}),'✓ [abcdef12] OK coding_tree 250ms');
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { validateParityEvidence, R39_PREDECESSOR_RELEASE_ID, R39_PREDECESSOR_MANIFEST } from '../scripts/release-parity-r39.mjs';
|
|
5
|
+
import { verifyFinalClosureObject } from '../scripts/final-closure-verify-r39.mjs';
|
|
6
|
+
|
|
7
|
+
const root=new URL('../',import.meta.url);
|
|
8
|
+
const text=rel=>readFile(new URL(rel,root),'utf8');
|
|
9
|
+
|
|
10
|
+
function parityFixture(){
|
|
11
|
+
const manifest='a'.repeat(64);
|
|
12
|
+
return {
|
|
13
|
+
schema:'deadbyte.r39-parity.v1',status:'passed',execution_provider:'deadbyte_mcp',rdc_operation_count:0,
|
|
14
|
+
candidate:{release_id:`v0.11.3-${manifest.slice(0,16)}`,version:'0.11.3',manifest_sha256:manifest},
|
|
15
|
+
immutable_slot:{protected:true,release_root:'C:/immutable/r39',development_root:'D:/Projects/deadbyte-mcp-r36-dev'},
|
|
16
|
+
local_full:{status:'passed',tool_count:76,strict_output_schemas:true},
|
|
17
|
+
public_compact:{status:'passed',tool_count:41,capability_closure:true,gateway_count:5},
|
|
18
|
+
autonomous:{status:'passed',signed_release_verified:true,observed_failure_before_replan:true},
|
|
19
|
+
before_rollback_disarmed:{status:'passed',capability_closure:true,coding_armed:false,autonomous_armed:false},
|
|
20
|
+
rollback:{state:'completed'},stage_restore:{status:'restored',manifest_sha256:manifest},restore:{state:'completed'},
|
|
21
|
+
predecessor:{release_id:R39_PREDECESSOR_RELEASE_ID,version:'0.11.2',manifest_sha256:R39_PREDECESSOR_MANIFEST},
|
|
22
|
+
predecessor_disarmed:{status:'passed',tool_count:41,capability_closure:true},
|
|
23
|
+
successor:{release_id:`v0.11.3-${manifest.slice(0,16)}`,version:'0.11.3',manifest_sha256:manifest},
|
|
24
|
+
successor_disarmed:{status:'passed',capability_closure:true,coding_armed:false,autonomous_armed:false},
|
|
25
|
+
requirements:Object.fromEntries(['tree_read','precondition_mutation','search','owned_process','observation','coding_loop','autonomy','interrupted_recovery','evidence_verification','compact_closure','full_surface'].map(key=>[key,true]))
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
test('R39 parity validator requires full-76, compact closure, exact public 0.11.2 predecessor and immutable-slot separation',()=>{
|
|
30
|
+
const good=parityFixture();
|
|
31
|
+
assert.equal(validateParityEvidence(good),true);
|
|
32
|
+
assert.throws(()=>validateParityEvidence({...good,local_full:{...good.local_full,tool_count:75}}),/full 76/i);
|
|
33
|
+
assert.throws(()=>validateParityEvidence({...good,public_compact:{...good.public_compact,capability_closure:false}}),/compact closure/i);
|
|
34
|
+
assert.throws(()=>validateParityEvidence({...good,predecessor:{...good.predecessor,manifest_sha256:'b'.repeat(64)}}),/predecessor/i);
|
|
35
|
+
assert.throws(()=>validateParityEvidence({...good,immutable_slot:{...good.immutable_slot,development_root:good.immutable_slot.release_root}}),/immutable-slot/i);
|
|
36
|
+
assert.throws(()=>validateParityEvidence({...good,stage_restore:{status:'missing',manifest_sha256:good.candidate.manifest_sha256}}),/stage restore/i);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('R39 parity captures R39 bridge, exact public 0.11.2 predecessor, stage reseed and successor restore',async()=>{
|
|
40
|
+
const parity=await text('scripts/release-parity-r39.mjs');
|
|
41
|
+
assert.match(parity,/R39_PREDECESSOR_RELEASE_ID='v0\.11\.2-058605dfccaa3035'/);
|
|
42
|
+
assert.match(parity,/058605dfccaa3035a57c6393d53f2421c14e5a92bd145c2334477ba24d7cab8b/);
|
|
43
|
+
assert.match(parity,/const stageRestore=await restoreFrozenStage/);
|
|
44
|
+
assert.match(parity,/publicR39Disarmed/);
|
|
45
|
+
assert.match(parity,/scheduleDeferred\('activate',candidateManifest,\{bridgeRoot:successorBridge\.root,bridgeScriptSha256:successorBridge\.sha256\}\)/);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('R39 current final closure writer and independent verifier bind R39 domains and public 0.11.2 predecessor',async()=>{
|
|
49
|
+
const [writer,verifier]=await Promise.all([text('scripts/final-closure-r39.mjs'),text('scripts/final-closure-verify-r39.mjs')]);
|
|
50
|
+
assert.equal(typeof verifyFinalClosureObject,'function');
|
|
51
|
+
assert.match(writer,/DEADBYTE-R39-FINAL-CLOSURE-V1/);
|
|
52
|
+
assert.match(writer,/deadbyte\.r39-final-closure\.v1/);
|
|
53
|
+
assert.match(writer,/pointer\.version==='0\.11\.3'/);
|
|
54
|
+
assert.match(writer,/R39 FINAL MACHINE BASELINE/);
|
|
55
|
+
assert.match(writer,/deadbyte\.r39-parity\.v1/);
|
|
56
|
+
assert.match(writer,/deadbyte\.r39-windows-gate\.v1/);
|
|
57
|
+
assert.match(writer,/R39_PREDECESSOR_RELEASE_ID/);
|
|
58
|
+
assert.doesNotMatch(verifier,/from ['"]\.\.\/src\//);
|
|
59
|
+
assert.match(verifier,/DEADBYTE-R39-FINAL-CLOSURE-V1/);
|
|
60
|
+
assert.match(verifier,/deadbyte\.r39-final-closure\.v1/);
|
|
61
|
+
assert.match(verifier,/envelope\.body\.version==='0\.11\.3'/);
|
|
62
|
+
assert.match(verifier,/R39 FINAL MACHINE BASELINE/);
|
|
63
|
+
assert.match(verifier,/deadbyte\.r39-parity\.v1/);
|
|
64
|
+
assert.match(verifier,/deadbyte\.r39-windows-gate\.v1/);
|
|
65
|
+
assert.match(verifier,/058605dfccaa3035a57c6393d53f2421c14e5a92bd145c2334477ba24d7cab8b/);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('R39 bounded public parity suite covers current release identity and agent fabric',async()=>{
|
|
69
|
+
const script=await text('scripts/release-parity-tests-r39.ps1');
|
|
70
|
+
assert.match(script,/r39-finalization\.test\.mjs/);
|
|
71
|
+
assert.match(script,/release-version\.test\.mjs/);
|
|
72
|
+
assert.match(script,/r37-agent-fabric\.test\.mjs/);
|
|
73
|
+
assert.match(script,/remote-live-log-r37\.test\.mjs/);
|
|
74
|
+
});
|
|
@@ -5,45 +5,48 @@ import { createHash } from 'node:crypto';
|
|
|
5
5
|
|
|
6
6
|
const root=new URL('../',import.meta.url);
|
|
7
7
|
const text=rel=>readFile(new URL(rel,root),'utf8');
|
|
8
|
-
const
|
|
9
|
-
const
|
|
8
|
+
const R39_PREDECESSOR_RELEASE_ID='v0.11.2-058605dfccaa3035';
|
|
9
|
+
const R39_PREDECESSOR_MANIFEST='058605dfccaa3035a57c6393d53f2421c14e5a92bd145c2334477ba24d7cab8b';
|
|
10
10
|
|
|
11
|
-
test('
|
|
11
|
+
test('R39 hotfix authority agrees on V0.11.3 and preserves exact public 0.11.2 predecessor',async()=>{
|
|
12
12
|
const pkg=JSON.parse(await text('package.json'));
|
|
13
13
|
const [version,controller,gate,readme,architecture,controllerReadme,deferred,parity,closure,closureVerify,windowsGate,semanticConfig]=await Promise.all([
|
|
14
|
-
text('src/version.mjs'),text('controller/deadbyte-controller.ps1'),text('scripts/gate-windows-
|
|
14
|
+
text('src/version.mjs'),text('controller/deadbyte-controller.ps1'),text('scripts/gate-windows-r39.ps1'),
|
|
15
15
|
text('README.txt'),text('docs/ARCHITECTURE.md'),text('controller/README.TXT'),
|
|
16
|
-
text('scripts/deferred-slot-operation-
|
|
17
|
-
text('scripts/final-closure-verify-
|
|
16
|
+
text('scripts/deferred-slot-operation-r39.mjs'),text('scripts/release-parity-r39.mjs'),text('scripts/final-closure-r39.mjs'),
|
|
17
|
+
text('scripts/final-closure-verify-r39.mjs'),text('scripts/windows-gate-evidence-r39.mjs'),
|
|
18
18
|
text('controller/deadbyte-semantic-lsp-config.json')
|
|
19
19
|
]);
|
|
20
|
-
assert.equal(pkg.version,'0.11.
|
|
21
|
-
assert.equal(pkg.scripts['gate:windows'],'powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-
|
|
22
|
-
assert.equal(pkg.scripts['self-update:deferred'],'node scripts/deferred-slot-operation-
|
|
23
|
-
assert.equal(pkg.scripts['release:parity'],'node scripts/release-parity-
|
|
24
|
-
assert.equal(pkg.scripts['release:final:write'],'node scripts/final-closure-
|
|
25
|
-
assert.equal(pkg.scripts['release:final:verify'],'node scripts/final-closure-verify-
|
|
26
|
-
assert.match(version,/DEADBYTE_VERSION = '0\.11\.
|
|
27
|
-
assert.match(controller,/CONTROLLER V0\.11\.
|
|
28
|
-
assert.match(gate,/DEADBYTE V0\.11\.
|
|
29
|
-
assert.match(readme,/^DEADBYTE MCP V0\.11\.
|
|
30
|
-
assert.match(architecture,/^# DEADBYTE MCP V0\.11\.
|
|
31
|
-
assert.match(controllerReadme,/^DEADBYTE MCP V0\.11\.
|
|
32
|
-
assert.match(deferred,/--expected-version','0\.11\.
|
|
33
|
-
assert.match(parity,/
|
|
34
|
-
assert.match(parity,new RegExp(
|
|
35
|
-
assert.match(parity,/version==='0\.11\.
|
|
36
|
-
assert.match(closure,/version:'0\.11\.
|
|
37
|
-
assert.match(closureVerify,/DEADBYTE V0\.11\.
|
|
38
|
-
assert.match(windowsGate,/version:'0\.11\.
|
|
20
|
+
assert.equal(pkg.version,'0.11.3');
|
|
21
|
+
assert.equal(pkg.scripts['gate:windows'],'powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-r39.ps1');
|
|
22
|
+
assert.equal(pkg.scripts['self-update:deferred'],'node scripts/deferred-slot-operation-r39.mjs');
|
|
23
|
+
assert.equal(pkg.scripts['release:parity'],'node scripts/release-parity-r39.mjs');
|
|
24
|
+
assert.equal(pkg.scripts['release:final:write'],'node scripts/final-closure-r39.mjs');
|
|
25
|
+
assert.equal(pkg.scripts['release:final:verify'],'node scripts/final-closure-verify-r39.mjs');
|
|
26
|
+
assert.match(version,/DEADBYTE_VERSION = '0\.11\.3'/);
|
|
27
|
+
assert.match(controller,/CONTROLLER V0\.11\.3 \/ R39/);
|
|
28
|
+
assert.match(gate,/DEADBYTE V0\.11\.3 \/ R39 WINDOWS BATCH GATE: PASS/);
|
|
29
|
+
assert.match(readme,/^DEADBYTE MCP V0\.11\.3 \/ R39 HOTFIX BASELINE$/m);
|
|
30
|
+
assert.match(architecture,/^# DEADBYTE MCP V0\.11\.3 \/ R39 ARCHITECTURE$/m);
|
|
31
|
+
assert.match(controllerReadme,/^DEADBYTE MCP V0\.11\.3 \/ R39 CONTROLLER$/m);
|
|
32
|
+
assert.match(deferred,/--expected-version','0\.11\.3'/);
|
|
33
|
+
assert.match(parity,/R39_PREDECESSOR_RELEASE_ID='v0\.11\.2-058605dfccaa3035'/);
|
|
34
|
+
assert.match(parity,new RegExp(R39_PREDECESSOR_MANIFEST));
|
|
35
|
+
assert.match(parity,/version==='0\.11\.3'|version:'0\.11\.3'/);
|
|
36
|
+
assert.match(closure,/version:'0\.11\.3'/);
|
|
37
|
+
assert.match(closureVerify,/DEADBYTE V0\.11\.3 \/ R39 WINDOWS BATCH GATE: PASS/);
|
|
38
|
+
assert.match(windowsGate,/version:'0\.11\.3'/);
|
|
39
39
|
assert.equal(createHash('sha256').update(Buffer.from(semanticConfig,'utf8')).digest('hex'),'d274b099a298c1033fea8f1e8849751693bbc0448778d619400acb82d4f53a3b');
|
|
40
|
-
assert.equal(
|
|
40
|
+
assert.equal(R39_PREDECESSOR_RELEASE_ID,'v0.11.2-058605dfccaa3035');
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
test('
|
|
43
|
+
test('R39 hotfix keeps ONLINE reuse and fixes PowerShell $Host recovery collision',async()=>{
|
|
44
44
|
const [cli,controller]=await Promise.all([text('src/deadbyte-cli.mjs'),text('controller/deadbyte-controller.ps1')]);
|
|
45
45
|
assert.match(cli,/controllerRuntimeReady/);
|
|
46
46
|
assert.match(cli,/Runtime already ONLINE; reusing verified bridge\/cloud\/supervisor/);
|
|
47
|
+
assert.match(cli,/invokeController\(ctx,'start',\{discardOutput:true,timeoutMs:REMOTE_START_TIMEOUT_MS\}\)/);
|
|
48
|
+
assert.match(cli,/remote controller start returned without a verified ONLINE runtime/);
|
|
49
|
+
assert.match(cli,/discardOutput\?\['ignore','ignore','ignore'\]/);
|
|
47
50
|
const start=controller.indexOf('function Recover-AutonomousSupervisor');
|
|
48
51
|
const end=controller.indexOf('function Show-State',start);
|
|
49
52
|
assert.ok(start>=0&&end>start,'supervisor recovery block missing');
|
|
@@ -7,7 +7,7 @@ test('R37 live timeline renders sanitized tool-call detail and correlates termin
|
|
|
7
7
|
const tracker=createLiveTimelineTracker({clock:()=>now});
|
|
8
8
|
const run=tracker.observe({event_type:'tool.request',tool:'coding_file_read',request_id:'abcdef1234567890',
|
|
9
9
|
args:{root_id:'projects',path:'src/a.mjs',content:'SECRET',token:'SECRET'}});
|
|
10
|
-
assert.match(run,/^1970-01-
|
|
10
|
+
assert.match(run,/^1970-01-01T07:00:01\.000\+07:00 \| coding_file_read\s+\| Arguments: \{/);
|
|
11
11
|
assert.match(run,/\"path\":\"src\/a\.mjs\"/);
|
|
12
12
|
assert.match(run,/\"reason\":\"payload\"/);
|
|
13
13
|
assert.match(run,/\"reason\":\"secret\"/);
|
|
@@ -16,11 +16,15 @@ async function fixture({failCommand='',statusOnline=false}={}){
|
|
|
16
16
|
const state=path.join(dir,'state');
|
|
17
17
|
const bin=path.join(state,'bin');
|
|
18
18
|
const trace=path.join(dir,'trace.txt');
|
|
19
|
+
const startedMarker=path.join(dir,'started.txt');
|
|
20
|
+
const spawnedStdout=path.join(dir,'spawned.stdout.log');
|
|
21
|
+
const spawnedStderr=path.join(dir,'spawned.stderr.log');
|
|
22
|
+
const spawnedPid=path.join(dir,'spawned.pid');
|
|
19
23
|
await mkdir(bin,{recursive:true});
|
|
20
24
|
const launcher=path.join(bin,'DEADBYTE-MCP.ps1');
|
|
21
|
-
const ps1=`param([string]$Command)\n$ErrorActionPreference='Stop'\nfor($i=0;$i -lt 20;$i++){ try { [IO.File]::AppendAllText($env:DEADBYTE_REMOTE_TEST_TRACE,(\"$Command\" + [Environment]::NewLine),[Text.Encoding]::UTF8); break } catch { if($i -eq 19){ throw }; Start-Sleep -Milliseconds 10 } }\nif ($env:DEADBYTE_REMOTE_TEST_FAIL -and $Command -eq $env:DEADBYTE_REMOTE_TEST_FAIL) { Write-Error \"forced $Command failure\"; exit 9 }\nswitch ($Command) {\n 'start' { Write-Host 'DEADBYTE MCP = ONLINE' }\n 'status' { if ($env:DEADBYTE_REMOTE_TEST_STATUS_ONLINE -eq '1') { Write-Host 'DEADBYTE MCP = ONLINE'; Write-Host 'Bridge PID : 1001 [verified]'; Write-Host 'Cloud PID : 1002 [verified]'; Write-Host 'Local ready : True'; Write-Host 'Loop supervisor: RUNNING [verified]' } else { Write-Host 'DEADBYTE MCP = OFFLINE' } }\n 'processstatus' { Write-Host 'Process plane : ARMED [standing-grant]' }\n 'codingstatus' { Write-Host 'Coding plane : ARMED [standing-grant]' }\n 'autonomousstatus' { Write-Host 'Autonomous : ARMED [standing-grant]' }\n 'hoststatus' { Write-Host 'Host plane : DISARMED [disarmed]' }\n 'park' { Write-Host 'DEADBYTE MCP = PARKED' }\n default { Write-Host \"$Command OK\" }\n}\nexit 0\n`;
|
|
25
|
+
const ps1=`param([string]$Command)\n$ErrorActionPreference='Stop'\nfor($i=0;$i -lt 20;$i++){ try { [IO.File]::AppendAllText($env:DEADBYTE_REMOTE_TEST_TRACE,(\"$Command\" + [Environment]::NewLine),[Text.Encoding]::UTF8); break } catch { if($i -eq 19){ throw }; Start-Sleep -Milliseconds 10 } }\nif ($env:DEADBYTE_REMOTE_TEST_FAIL -and $Command -eq $env:DEADBYTE_REMOTE_TEST_FAIL) { Write-Error \"forced $Command failure\"; exit 9 }\nswitch ($Command) {\n 'start' { if ($env:DEADBYTE_REMOTE_TEST_SPAWN_CHILD -eq '1') { $node=(Get-Command node.exe).Source; $spawned=Start-Process -FilePath $node -ArgumentList @('-e','setTimeout(()=>process.exit(0),10000)') -PassThru -WindowStyle Hidden -RedirectStandardOutput $env:DEADBYTE_REMOTE_TEST_SPAWN_STDOUT -RedirectStandardError $env:DEADBYTE_REMOTE_TEST_SPAWN_STDERR; if ($env:DEADBYTE_REMOTE_TEST_SPAWN_PID) { [IO.File]::WriteAllText($env:DEADBYTE_REMOTE_TEST_SPAWN_PID,[string]$spawned.Id,[Text.Encoding]::UTF8) } }; if ($env:DEADBYTE_REMOTE_TEST_STARTED) { [IO.File]::WriteAllText($env:DEADBYTE_REMOTE_TEST_STARTED,'1',[Text.Encoding]::UTF8) }; Write-Host 'DEADBYTE MCP = ONLINE' }\n 'status' { if ($env:DEADBYTE_REMOTE_TEST_STATUS_ONLINE -eq '1' -or ($env:DEADBYTE_REMOTE_TEST_STARTED -and (Test-Path -LiteralPath $env:DEADBYTE_REMOTE_TEST_STARTED))) { Write-Host 'DEADBYTE MCP = ONLINE'; Write-Host 'Bridge PID : 1001 [verified]'; Write-Host 'Cloud PID : 1002 [verified]'; Write-Host 'Local ready : True'; Write-Host 'Loop supervisor: RUNNING [verified]' } else { Write-Host 'DEADBYTE MCP = OFFLINE' } }\n 'processstatus' { Write-Host 'Process plane : ARMED [standing-grant]' }\n 'codingstatus' { Write-Host 'Coding plane : ARMED [standing-grant]' }\n 'autonomousstatus' { Write-Host 'Autonomous : ARMED [standing-grant]' }\n 'hoststatus' { Write-Host 'Host plane : DISARMED [disarmed]' }\n 'park' { if ($env:DEADBYTE_REMOTE_TEST_STARTED) { Remove-Item -LiteralPath $env:DEADBYTE_REMOTE_TEST_STARTED -Force -ErrorAction SilentlyContinue }; Write-Host 'DEADBYTE MCP = PARKED' }\n default { Write-Host \"$Command OK\" }\n}\nexit 0\n`;
|
|
22
26
|
await writeFile(launcher,ps1,'utf8');
|
|
23
|
-
return {dir,state,trace,env:{...process.env,DEADBYTE_STATE_ROOT:state,DEADBYTE_REMOTE_TEST_TRACE:trace,DEADBYTE_REMOTE_TEST_FAIL:failCommand,DEADBYTE_REMOTE_TEST_STATUS_ONLINE:statusOnline?'1':'0'}};
|
|
27
|
+
return {dir,state,trace,env:{...process.env,DEADBYTE_STATE_ROOT:state,DEADBYTE_REMOTE_TEST_TRACE:trace,DEADBYTE_REMOTE_TEST_FAIL:failCommand,DEADBYTE_REMOTE_TEST_STATUS_ONLINE:statusOnline?'1':'0',DEADBYTE_REMOTE_TEST_STARTED:startedMarker,DEADBYTE_REMOTE_TEST_SPAWN_STDOUT:spawnedStdout,DEADBYTE_REMOTE_TEST_SPAWN_STDERR:spawnedStderr,DEADBYTE_REMOTE_TEST_SPAWN_PID:spawnedPid},spawnedPid};
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
async function traceCommands(file,{read=readFile,retryDelayMs=10,maxAttempts=20}={}){
|
|
@@ -90,15 +94,45 @@ winTest('bare remote reuses a fully verified ONLINE runtime without invoking blo
|
|
|
90
94
|
}
|
|
91
95
|
});
|
|
92
96
|
|
|
97
|
+
winTest('bare remote PARKED startup does not wait for a long-lived child holding inherited pipe handles',async()=>{
|
|
98
|
+
const fx=await fixture();
|
|
99
|
+
fx.env.DEADBYTE_REMOTE_TEST_SPAWN_CHILD='1';
|
|
100
|
+
let child=null;
|
|
101
|
+
try{
|
|
102
|
+
const startedAt=Date.now();
|
|
103
|
+
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
104
|
+
const output=await collect(child);
|
|
105
|
+
const ready=await waitFor(fx.trace,commands=>commands.includes('hoststatus'),{timeoutMs:8000});
|
|
106
|
+
const elapsed=Date.now()-startedAt;
|
|
107
|
+
assert.deepEqual(ready.slice(0,10),[
|
|
108
|
+
'status','start','status','armprocess','armcoding','armautonomous',
|
|
109
|
+
'processstatus','codingstatus','autonomousstatus','hoststatus'
|
|
110
|
+
]);
|
|
111
|
+
assert.ok(elapsed<7000,`PARKED startup waited on inherited child pipe handles: ${elapsed}ms`);
|
|
112
|
+
assert.equal(child.exitCode,null,'remote session must remain foreground after PARKED startup');
|
|
113
|
+
assert.match(output.stdout,/\[BOOT\] Starting bridge\/runtime\.\.\./);
|
|
114
|
+
assert.match(output.stdout,/\[OK\] ONLINE/);
|
|
115
|
+
child.stdin.end();
|
|
116
|
+
const ended=await Promise.race([output.closed,sleep(6000).then(()=>({timeout:true}))]);
|
|
117
|
+
assert.equal(ended.timeout,undefined,'remote session did not exit after PARKED startup proof');
|
|
118
|
+
assert.equal(ended.code,0,output.stderr);
|
|
119
|
+
}finally{
|
|
120
|
+
if(child?.exitCode===null) child.kill('SIGKILL');
|
|
121
|
+
try{const pid=Number((await readFile(fx.spawnedPid,'utf8')).trim());if(Number.isInteger(pid)&&pid>0) process.kill(pid,'SIGKILL');}catch{}
|
|
122
|
+
await sleep(100);
|
|
123
|
+
await rm(fx.dir,{recursive:true,force:true});
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
93
127
|
winTest('bare remote uses one unified live timeline, keeps authority semantics, then disarms and parks when the foreground session ends',async()=>{
|
|
94
128
|
const fx=await fixture();
|
|
95
129
|
let child=null;
|
|
96
130
|
try{
|
|
97
131
|
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
98
132
|
const output=await collect(child);
|
|
99
|
-
const ready=await waitFor(fx.trace,commands=>commands.slice(0,
|
|
100
|
-
assert.deepEqual(ready.slice(0,
|
|
101
|
-
'status','start','armprocess','armcoding','armautonomous',
|
|
133
|
+
const ready=await waitFor(fx.trace,commands=>commands.slice(0,10).join(',')==='status,start,status,armprocess,armcoding,armautonomous,processstatus,codingstatus,autonomousstatus,hoststatus');
|
|
134
|
+
assert.deepEqual(ready.slice(0,10),[
|
|
135
|
+
'status','start','status','armprocess','armcoding','armautonomous',
|
|
102
136
|
'processstatus','codingstatus','autonomousstatus','hoststatus'
|
|
103
137
|
]);
|
|
104
138
|
assert.equal(child.exitCode,null,'remote session must remain foreground after becoming ready');
|
|
@@ -136,9 +170,9 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
136
170
|
].join('\n')+'\n','utf8');
|
|
137
171
|
const logDeadline=Date.now()+3000;
|
|
138
172
|
while(Date.now()<logDeadline&&!/✗ \[fedcba98\] ERR coding_file_read 1\.73s/.test(output.stdout)) await sleep(50);
|
|
139
|
-
assert.match(output.stdout,/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}
|
|
173
|
+
assert.match(output.stdout,/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}\+07:00 \| coding_tree\s+\| Arguments: .*\"path\":\"src\".*\"root_id\":\"projects\".*\[cursor r1:[0-9a-f]{24}:1\]$/m);
|
|
140
174
|
assert.match(output.stdout,/✓ \[abcdef12\] OK coding_tree 42ms \[cursor r1:[0-9a-f]{24}:2\]/);
|
|
141
|
-
assert.match(output.stdout,/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}
|
|
175
|
+
assert.match(output.stdout,/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}\+07:00 \| coding_file_read\s+\| Arguments: \{\"path\":\"src\/deadbyte-cli\.mjs\",\"root_id\":\"projects\"\} \[cursor r1:[0-9a-f]{24}:3\]$/m);
|
|
142
176
|
assert.match(output.stdout,/✗ \[fedcba98\] ERR coding_file_read 1\.73s \[cursor r1:[0-9a-f]{24}:4\]/);
|
|
143
177
|
assert.equal((output.stdout.match(/✓ \[abcdef12\] OK coding_tree 42ms/g)||[]).length,1,'duplicate terminal result must print once');
|
|
144
178
|
const resultJournal=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-results',`${marker.result_session_id}.json`),'utf8'));
|
|
@@ -209,7 +243,7 @@ winTest('remote --resume replays sanitized tool-call and terminal result events
|
|
|
209
243
|
while(Date.now()<deadline&&(!/coding_file_read\s+\| Arguments: .*REPLAY cursor r1:[0-9a-f]{24}:3/.test(secondOutput.stdout)||
|
|
210
244
|
!/\[REPLAY\] \[OK\] coding_file_read \[cursor r1:[0-9a-f]{24}:4\]/.test(secondOutput.stdout)||
|
|
211
245
|
!/\[LOG\] Unified live timeline/.test(secondOutput.stdout))) await sleep(50);
|
|
212
|
-
assert.match(secondOutput.stdout,/^2026-09-
|
|
246
|
+
assert.match(secondOutput.stdout,/^2026-09-18T23:30:01\.000\+07:00 \| coding_file_read\s+\| Arguments: .*\"path\":\"src\/deadbyte-cli\.mjs\".*REPLAY cursor r1:[0-9a-f]{24}:3$/m);
|
|
213
247
|
assert.match(secondOutput.stdout,/\[REPLAY\] \[OK\] coding_file_read \[cursor r1:[0-9a-f]{24}:4\]/);
|
|
214
248
|
assert.match(secondOutput.stdout,/♻️ \[SESSION\] Previous result stream replayed/);
|
|
215
249
|
assert.doesNotMatch(secondOutput.stdout,/DO_NOT_REPLAY_SECRET/);
|
|
@@ -249,9 +283,9 @@ winTest('detached guardian revokes authority, parks, and removes the session mar
|
|
|
249
283
|
try{
|
|
250
284
|
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
251
285
|
const output=await collect(child);
|
|
252
|
-
const ready=await waitFor(fx.trace,commands=>commands.slice(0,
|
|
253
|
-
assert.deepEqual(ready.slice(0,
|
|
254
|
-
'status','start','armprocess','armcoding','armautonomous',
|
|
286
|
+
const ready=await waitFor(fx.trace,commands=>commands.slice(0,10).join(',')==='status,start,status,armprocess,armcoding,armautonomous,processstatus,codingstatus,autonomousstatus,hoststatus');
|
|
287
|
+
assert.deepEqual(ready.slice(0,10),[
|
|
288
|
+
'status','start','status','armprocess','armcoding','armautonomous',
|
|
255
289
|
'processstatus','codingstatus','autonomousstatus','hoststatus'
|
|
256
290
|
]);
|
|
257
291
|
const markerPath=path.join(fx.state,'runtime','remote-session.json');
|
|
@@ -294,7 +328,7 @@ winTest('bare remote rolls back authority and parks if one plane fails to arm',a
|
|
|
294
328
|
assert.equal(ended.timeout,undefined,'failed startup did not terminate');
|
|
295
329
|
assert.notEqual(ended.code,0);
|
|
296
330
|
const commands=await traceCommands(fx.trace);
|
|
297
|
-
assert.deepEqual(commands.slice(0,
|
|
331
|
+
assert.deepEqual(commands.slice(0,5),['status','start','status','armprocess','armcoding']);
|
|
298
332
|
assert.deepEqual(commands.slice(-5),['disarmautonomous','disarmcoding','disarmprocess','disarmhost','park']);
|
|
299
333
|
assert.doesNotMatch(output.stdout,/Live logs/i);
|
|
300
334
|
}finally{await rm(fx.dir,{recursive:true,force:true});}
|