deadbyte-mcp 0.11.0 → 0.11.2
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 +75 -59
- package/README.txt +7 -7
- package/bin/WORKER-REGISTRY.txt +3 -3
- package/bin/appcontainer-stage.exe +0 -0
- package/bin/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.exe +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.exe +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.exe +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.exe +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.exe +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.exe +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.exe +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.exe +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.exe +0 -0
- package/bin/deadbyte-contain.obj +0 -0
- package/bin/deadbyte-exec.exe +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-process-host.exe +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.exe +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/controller/README.TXT +1 -1
- package/controller/deadbyte-controller.ps1 +4 -4
- package/controller/deadbyte-process-policy.json +1 -1
- package/docs/ARCHITECTURE.md +1 -1
- package/package.json +6 -6
- package/scripts/build-containment.ps1 +15 -15
- package/scripts/deferred-slot-operation-r37-0110.mjs +124 -0
- package/scripts/deferred-slot-operation-r38.mjs +124 -0
- package/scripts/deferred-slot-operation.mjs +1 -1
- package/scripts/final-closure-r37-0110.mjs +113 -0
- package/scripts/final-closure-r38.mjs +113 -0
- package/scripts/final-closure-verify-r37-0110.mjs +208 -0
- package/scripts/final-closure-verify-r38.mjs +208 -0
- package/scripts/final-closure-verify.mjs +16 -16
- package/scripts/final-closure.mjs +8 -8
- package/scripts/gate-windows-r37-0110.ps1 +42 -0
- package/scripts/gate-windows-r38.ps1 +42 -0
- package/scripts/gate-windows.ps1 +3 -3
- package/scripts/release-parity-r37-0110.mjs +200 -0
- package/scripts/release-parity-r38.mjs +200 -0
- package/scripts/release-parity-tests-r38.ps1 +9 -0
- package/scripts/release-parity-tests.ps1 +1 -1
- package/scripts/release-parity.mjs +14 -14
- package/scripts/windows-gate-evidence-r37-0110.mjs +52 -0
- package/scripts/windows-gate-evidence-r38.mjs +52 -0
- package/scripts/windows-gate-evidence.mjs +4 -4
- package/src/deadbyte-cli.mjs +58 -23
- package/src/release-generation.mjs +18 -1
- package/src/remote-live-log.mjs +23 -1
- package/src/remote-result-journal.mjs +23 -4
- package/src/version.mjs +1 -1
- package/test/cli-entrypoint.test.mjs +9 -0
- package/test/containment.test.mjs +18 -0
- package/test/controller.test.mjs +11 -0
- 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/r36-release-identity.test.mjs +4 -4
- package/test/r37-0110-history.test.mjs +20 -0
- package/test/r37-finalization.test.mjs +14 -14
- package/test/r37-remote-tool-call-timeline.test.mjs +94 -0
- package/test/r38-finalization.test.mjs +74 -0
- package/test/release-generation.test.mjs +13 -3
- package/test/release-version.test.mjs +45 -33
- package/test/remote-live-log-r37.test.mjs +5 -2
- package/test/remote-session-cli.test.mjs +66 -25
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
|
-
import { validateParityEvidence,
|
|
4
|
+
import { validateParityEvidence, R37_PREDECESSOR_RELEASE_ID, R37_PREDECESSOR_MANIFEST } from '../scripts/release-parity.mjs';
|
|
5
5
|
import { verifyFinalClosureObject } from '../scripts/final-closure-verify.mjs';
|
|
6
6
|
|
|
7
7
|
const root=new URL('../',import.meta.url);
|
|
@@ -11,22 +11,22 @@ function parityFixture(){
|
|
|
11
11
|
const manifest='a'.repeat(64);
|
|
12
12
|
return {
|
|
13
13
|
schema:'deadbyte.r37-parity.v1',status:'passed',execution_provider:'deadbyte_mcp',rdc_operation_count:0,
|
|
14
|
-
candidate:{release_id:`v0.11.
|
|
14
|
+
candidate:{release_id:`v0.11.1-${manifest.slice(0,16)}`,version:'0.11.1',manifest_sha256:manifest},
|
|
15
15
|
immutable_slot:{protected:true,release_root:'C:/immutable/r37',development_root:'D:/Projects/deadbyte-mcp-r36-dev'},
|
|
16
16
|
local_full:{status:'passed',tool_count:76,strict_output_schemas:true},
|
|
17
17
|
public_compact:{status:'passed',tool_count:41,capability_closure:true,gateway_count:5},
|
|
18
18
|
autonomous:{status:'passed',signed_release_verified:true,observed_failure_before_replan:true},
|
|
19
19
|
before_rollback_disarmed:{status:'passed',capability_closure:true,coding_armed:false,autonomous_armed:false},
|
|
20
20
|
rollback:{state:'completed'},stage_restore:{status:'restored',manifest_sha256:manifest},restore:{state:'completed'},
|
|
21
|
-
predecessor:{release_id:
|
|
21
|
+
predecessor:{release_id:R37_PREDECESSOR_RELEASE_ID,version:'0.11.0',manifest_sha256:R37_PREDECESSOR_MANIFEST},
|
|
22
22
|
predecessor_disarmed:{status:'passed',tool_count:41,capability_closure:true},
|
|
23
|
-
successor:{release_id:`v0.11.
|
|
23
|
+
successor:{release_id:`v0.11.1-${manifest.slice(0,16)}`,version:'0.11.1',manifest_sha256:manifest},
|
|
24
24
|
successor_disarmed:{status:'passed',capability_closure:true,coding_armed:false,autonomous_armed:false},
|
|
25
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
26
|
};
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
test('R37 parity validator requires full-76, compact closure, exact
|
|
29
|
+
test('R37 parity validator requires full-76, compact closure, exact public 0.11.0 predecessor and immutable-slot separation',()=>{
|
|
30
30
|
const good=parityFixture();
|
|
31
31
|
assert.equal(validateParityEvidence(good),true);
|
|
32
32
|
assert.throws(()=>validateParityEvidence({...good,local_full:{...good.local_full,tool_count:75}}),/full 76/i);
|
|
@@ -36,33 +36,33 @@ test('R37 parity validator requires full-76, compact closure, exact R36 predeces
|
|
|
36
36
|
assert.throws(()=>validateParityEvidence({...good,stage_restore:{status:'missing',manifest_sha256:good.candidate.manifest_sha256}}),/stage restore/i);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
test('R37 parity captures R37 bridge, exact
|
|
39
|
+
test('R37 parity captures R37 bridge, exact public 0.11.0 predecessor, stage reseed and successor restore',async()=>{
|
|
40
40
|
const parity=await text('scripts/release-parity.mjs');
|
|
41
|
-
assert.match(parity,/
|
|
42
|
-
assert.match(parity,/
|
|
41
|
+
assert.match(parity,/R37_PREDECESSOR_RELEASE_ID='v0\.11\.0-08c2a0008b1f84c9'/);
|
|
42
|
+
assert.match(parity,/08c2a0008b1f84c90f7ce7faa1e3decbd03f33612bc0fdc7d1ba591b548798e8/);
|
|
43
43
|
assert.match(parity,/const stageRestore=await restoreFrozenStage/);
|
|
44
|
-
assert.match(parity,/
|
|
44
|
+
assert.match(parity,/publicR37Disarmed/);
|
|
45
45
|
assert.match(parity,/scheduleDeferred\('activate',candidateManifest,\{bridgeRoot:successorBridge\.root,bridgeScriptSha256:successorBridge\.sha256\}\)/);
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
test('R37 current final closure writer and independent verifier bind R37 domains and
|
|
48
|
+
test('R37 current final closure writer and independent verifier bind R37 domains and public 0.11.0 predecessor',async()=>{
|
|
49
49
|
const [writer,verifier]=await Promise.all([text('scripts/final-closure.mjs'),text('scripts/final-closure-verify.mjs')]);
|
|
50
50
|
assert.equal(typeof verifyFinalClosureObject,'function');
|
|
51
51
|
assert.match(writer,/DEADBYTE-R37-FINAL-CLOSURE-V1/);
|
|
52
52
|
assert.match(writer,/deadbyte\.r37-final-closure\.v1/);
|
|
53
|
-
assert.match(writer,/pointer\.version==='0\.11\.
|
|
53
|
+
assert.match(writer,/pointer\.version==='0\.11\.1'/);
|
|
54
54
|
assert.match(writer,/R37 FINAL MACHINE BASELINE/);
|
|
55
55
|
assert.match(writer,/deadbyte\.r37-parity\.v1/);
|
|
56
56
|
assert.match(writer,/deadbyte\.r37-windows-gate\.v1/);
|
|
57
|
-
assert.match(writer,/
|
|
57
|
+
assert.match(writer,/R37_PREDECESSOR_RELEASE_ID/);
|
|
58
58
|
assert.doesNotMatch(verifier,/from ['"]\.\.\/src\//);
|
|
59
59
|
assert.match(verifier,/DEADBYTE-R37-FINAL-CLOSURE-V1/);
|
|
60
60
|
assert.match(verifier,/deadbyte\.r37-final-closure\.v1/);
|
|
61
|
-
assert.match(verifier,/envelope\.body\.version==='0\.11\.
|
|
61
|
+
assert.match(verifier,/envelope\.body\.version==='0\.11\.1'/);
|
|
62
62
|
assert.match(verifier,/R37 FINAL MACHINE BASELINE/);
|
|
63
63
|
assert.match(verifier,/deadbyte\.r37-parity\.v1/);
|
|
64
64
|
assert.match(verifier,/deadbyte\.r37-windows-gate\.v1/);
|
|
65
|
-
assert.match(verifier,/
|
|
65
|
+
assert.match(verifier,/08c2a0008b1f84c90f7ce7faa1e3decbd03f33612bc0fdc7d1ba591b548798e8/);
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
test('R37 bounded public parity suite covers current release identity and agent fabric',async()=>{
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { createLiveTimelineTracker, formatRemoteToolCall } from '../src/remote-live-log.mjs';
|
|
7
|
+
import { openRemoteResultJournal } from '../src/remote-result-journal.mjs';
|
|
8
|
+
|
|
9
|
+
const H=c=>c.repeat(64);
|
|
10
|
+
const session='c'.repeat(24);
|
|
11
|
+
|
|
12
|
+
test('R37 unified timeline renders one sanitized tool-call line with ISO timestamp, padded tool name, and JSON arguments',()=>{
|
|
13
|
+
const event={
|
|
14
|
+
event_type:'tool.request',
|
|
15
|
+
request_id:'abcdef1234567890',
|
|
16
|
+
tool:'coding_file_write',
|
|
17
|
+
created_at_utc:'2026-09-18T16:24:30.125Z',
|
|
18
|
+
args:{
|
|
19
|
+
root_id:'projects',
|
|
20
|
+
path:'src/example.mjs',
|
|
21
|
+
content:'TOP_SECRET_PAYLOAD',
|
|
22
|
+
token:'TOP_SECRET_TOKEN',
|
|
23
|
+
nested:{authorization:'Bearer abc.def.ghi',safe:true}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const line=formatRemoteToolCall(event);
|
|
27
|
+
assert.match(line,/^2026-09-18T16:24:30\.125Z \| coding_file_write\s+\| Arguments: \{/);
|
|
28
|
+
assert.match(line,/"root_id":"projects"/);
|
|
29
|
+
assert.match(line,/"path":"src\/example\.mjs"/);
|
|
30
|
+
assert.match(line,/"reason":"payload"/);
|
|
31
|
+
assert.match(line,/"reason":"secret"/);
|
|
32
|
+
assert.doesNotMatch(line,/TOP_SECRET_PAYLOAD|TOP_SECRET_TOKEN|Bearer abc\.def\.ghi/);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('R37 live tracker uses the tool-call line for request and keeps correlated terminal result',()=>{
|
|
36
|
+
let now=Date.parse('2026-09-18T16:24:30.125Z');
|
|
37
|
+
const tracker=createLiveTimelineTracker({clock:()=>now});
|
|
38
|
+
const call=tracker.observe({event_type:'tool.request',request_id:'abcdef1234567890',tool:'coding_tree',
|
|
39
|
+
created_at_utc:'2026-09-18T16:24:30.125Z',args:{root_id:'projects',path:'src'}});
|
|
40
|
+
assert.match(call,/^2026-09-18T16:24:30\.125Z \| coding_tree\s+\| Arguments: \{"path":"src","root_id":"projects"\}$/);
|
|
41
|
+
now+=250;
|
|
42
|
+
assert.equal(tracker.observe({event_type:'tool.result',request_id:'abcdef1234567890',tool:'coding_tree',
|
|
43
|
+
outcome:{is_error:false,duration_ms:250}}),'✓ [abcdef12] OK coding_tree 250ms');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('R37 remote result journal persists sanitized tool-call and result in one monotonic cursor stream',async()=>{
|
|
47
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r37-toolcall-journal-'));
|
|
48
|
+
const file=path.join(root,'journal.json');
|
|
49
|
+
try{
|
|
50
|
+
const journal=await openRemoteResultJournal({filePath:file,sessionId:session,maxEntries:16});
|
|
51
|
+
const call=await journal.recordCall({
|
|
52
|
+
request_id:'req-1',tool:'coding_file_write',observed_at_utc:'2026-09-18T16:24:30.125Z',
|
|
53
|
+
arguments:{root_id:'projects',path:'x.txt',content:'DO_NOT_STORE_PAYLOAD',token:'DO_NOT_STORE_TOKEN'},
|
|
54
|
+
payload_sha256:H('1')
|
|
55
|
+
});
|
|
56
|
+
const duplicate=await journal.recordCall({
|
|
57
|
+
request_id:'req-1',tool:'coding_file_write',observed_at_utc:'2026-09-18T16:24:30.125Z',
|
|
58
|
+
arguments:{root_id:'projects',path:'x.txt',content:'DO_NOT_STORE_PAYLOAD',token:'DO_NOT_STORE_TOKEN'},
|
|
59
|
+
payload_sha256:H('1')
|
|
60
|
+
});
|
|
61
|
+
const result=await journal.recordTerminal({
|
|
62
|
+
request_id:'req-1',tool:'coding_file_write',status:'ok',observed_at_utc:'2026-09-18T16:24:30.500Z',
|
|
63
|
+
payload_sha256:H('2')
|
|
64
|
+
});
|
|
65
|
+
assert.equal(call.event.seq,1);
|
|
66
|
+
assert.equal(call.event.event_type,'tool.call');
|
|
67
|
+
assert.equal(duplicate.duplicate,true);
|
|
68
|
+
assert.equal(result.event.seq,2);
|
|
69
|
+
assert.equal(result.event.event_type,'tool.result');
|
|
70
|
+
assert.equal(journal.cursor(),'r1:'+session+':2');
|
|
71
|
+
const replay=await journal.replayAfter('r1:'+session+':0',{limit:8});
|
|
72
|
+
assert.deepEqual(replay.events.map(e=>[e.seq,e.event_type]),[[1,'tool.call'],[2,'tool.result']]);
|
|
73
|
+
const disk=await readFile(file,'utf8');
|
|
74
|
+
assert.doesNotMatch(disk,/DO_NOT_STORE_PAYLOAD|DO_NOT_STORE_TOKEN/);
|
|
75
|
+
assert.match(disk,/"reason":"payload"/);
|
|
76
|
+
assert.match(disk,/"reason":"secret"/);
|
|
77
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('R37 journal remains backward-compatible with pre-tool-call result-only events',async()=>{
|
|
81
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r37-old-result-journal-'));
|
|
82
|
+
const file=path.join(root,'journal.json');
|
|
83
|
+
try{
|
|
84
|
+
await import('node:fs/promises').then(({writeFile})=>writeFile(file,JSON.stringify({
|
|
85
|
+
schema:'deadbyte.remote-result-journal.v1',session_id:session,next_seq:2,
|
|
86
|
+
events:[{session_id:session,seq:1,request_id:'old-1',tool:'coding_tree',status:'ok',
|
|
87
|
+
observed_at_utc:'2026-09-17T00:00:00.000Z',payload_sha256:H('3')}]
|
|
88
|
+
})+'\n','utf8'));
|
|
89
|
+
const journal=await openRemoteResultJournal({filePath:file,sessionId:session,maxEntries:16});
|
|
90
|
+
const replay=await journal.replayAfter('r1:'+session+':0',{limit:8});
|
|
91
|
+
assert.equal(replay.events[0].event_type,'tool.result');
|
|
92
|
+
assert.equal(replay.events[0].status,'ok');
|
|
93
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
94
|
+
});
|
|
@@ -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, R38_PREDECESSOR_RELEASE_ID, R38_PREDECESSOR_MANIFEST } from '../scripts/release-parity-r38.mjs';
|
|
5
|
+
import { verifyFinalClosureObject } from '../scripts/final-closure-verify-r38.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.r38-parity.v1',status:'passed',execution_provider:'deadbyte_mcp',rdc_operation_count:0,
|
|
14
|
+
candidate:{release_id:`v0.11.2-${manifest.slice(0,16)}`,version:'0.11.2',manifest_sha256:manifest},
|
|
15
|
+
immutable_slot:{protected:true,release_root:'C:/immutable/r38',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:R38_PREDECESSOR_RELEASE_ID,version:'0.11.1',manifest_sha256:R38_PREDECESSOR_MANIFEST},
|
|
22
|
+
predecessor_disarmed:{status:'passed',tool_count:41,capability_closure:true},
|
|
23
|
+
successor:{release_id:`v0.11.2-${manifest.slice(0,16)}`,version:'0.11.2',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('R38 parity validator requires full-76, compact closure, exact public 0.11.1 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('R38 parity captures R38 bridge, exact public 0.11.1 predecessor, stage reseed and successor restore',async()=>{
|
|
40
|
+
const parity=await text('scripts/release-parity-r38.mjs');
|
|
41
|
+
assert.match(parity,/R38_PREDECESSOR_RELEASE_ID='v0\.11\.1-220fac89dd82cdcd'/);
|
|
42
|
+
assert.match(parity,/220fac89dd82cdcdddb82cd7d0b71b768f84a5c87d57ebaca0f5383f33d97a76/);
|
|
43
|
+
assert.match(parity,/const stageRestore=await restoreFrozenStage/);
|
|
44
|
+
assert.match(parity,/publicR38Disarmed/);
|
|
45
|
+
assert.match(parity,/scheduleDeferred\('activate',candidateManifest,\{bridgeRoot:successorBridge\.root,bridgeScriptSha256:successorBridge\.sha256\}\)/);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('R38 current final closure writer and independent verifier bind R38 domains and public 0.11.1 predecessor',async()=>{
|
|
49
|
+
const [writer,verifier]=await Promise.all([text('scripts/final-closure-r38.mjs'),text('scripts/final-closure-verify-r38.mjs')]);
|
|
50
|
+
assert.equal(typeof verifyFinalClosureObject,'function');
|
|
51
|
+
assert.match(writer,/DEADBYTE-R38-FINAL-CLOSURE-V1/);
|
|
52
|
+
assert.match(writer,/deadbyte\.r38-final-closure\.v1/);
|
|
53
|
+
assert.match(writer,/pointer\.version==='0\.11\.2'/);
|
|
54
|
+
assert.match(writer,/R38 FINAL MACHINE BASELINE/);
|
|
55
|
+
assert.match(writer,/deadbyte\.r38-parity\.v1/);
|
|
56
|
+
assert.match(writer,/deadbyte\.r38-windows-gate\.v1/);
|
|
57
|
+
assert.match(writer,/R38_PREDECESSOR_RELEASE_ID/);
|
|
58
|
+
assert.doesNotMatch(verifier,/from ['"]\.\.\/src\//);
|
|
59
|
+
assert.match(verifier,/DEADBYTE-R38-FINAL-CLOSURE-V1/);
|
|
60
|
+
assert.match(verifier,/deadbyte\.r38-final-closure\.v1/);
|
|
61
|
+
assert.match(verifier,/envelope\.body\.version==='0\.11\.2'/);
|
|
62
|
+
assert.match(verifier,/R38 FINAL MACHINE BASELINE/);
|
|
63
|
+
assert.match(verifier,/deadbyte\.r38-parity\.v1/);
|
|
64
|
+
assert.match(verifier,/deadbyte\.r38-windows-gate\.v1/);
|
|
65
|
+
assert.match(verifier,/220fac89dd82cdcdddb82cd7d0b71b768f84a5c87d57ebaca0f5383f33d97a76/);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('R38 bounded public parity suite covers current release identity and agent fabric',async()=>{
|
|
69
|
+
const script=await text('scripts/release-parity-tests-r38.ps1');
|
|
70
|
+
assert.match(script,/r38-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
|
+
});
|
|
@@ -30,16 +30,23 @@ test('R27 generation bundle rebinds authority and mutable state outside the rele
|
|
|
30
30
|
]},
|
|
31
31
|
LocalReadyUri:'http://127.0.0.1:8787/healthz',UrlRegex:'https://x',McpUrlFile:'C:/mcp-url.txt',McpUrlFileSha256:EXE,McpPathFile:'C:/token.txt',StartTimeoutSeconds:30,ProbeTimeoutSeconds:3};
|
|
32
32
|
const coding={schema:'deadbyte.coding-policy.v1',enabled:true,lease_file:'C:/old/coding.json',evidence_dir:'C:/old/evidence',
|
|
33
|
-
roots:{core:{path:'C:/old/core',read:true,write:true,exec:true,deny_paths:['node_modules']}},commands:{tests:command()},max_read_bytes:524288,max_write_bytes:2097152};
|
|
33
|
+
roots:{core:{path:'C:/old/core',read:true,write:true,exec:true,deny_paths:['node_modules']}},commands:{tests:command(),powershell_script:command()},max_read_bytes:524288,max_write_bytes:2097152};
|
|
34
34
|
const autonomous={schema:'deadbyte.autonomous-policy.v1',enabled:true,lease_file:'C:/old/auto.json',evidence_dir:'C:/old/auto-evidence',coding_policy_sha256:'0'.repeat(64),root_ids:['core'],planner:{backend:'client_sampling',max_tokens:4096},
|
|
35
|
-
profiles:{
|
|
35
|
+
profiles:{
|
|
36
|
+
gate:{kind:'command',root_id:'core',command_id:'tests',script_path:'scripts/gate.ps1',script_sha256:'0'.repeat(64),args:[],cwd:'.',timeout_ms:120000,expect_exit_code:0,required_for_release:true,run_after_mutation:true,description:'gate'},
|
|
37
|
+
'windows-release':{kind:'command',root_id:'core',command_id:'powershell_script',script_path:'scripts/gate-windows-old.ps1',script_sha256:'0'.repeat(64),args:[],cwd:'.',timeout_ms:300000,expect_exit_code:0,required_for_release:true,run_after_mutation:false,description:'historical Windows release gate'}
|
|
38
|
+
},
|
|
36
39
|
limits:{max_iterations:12,max_mutations:24,max_wall_ms:1800000,max_context_bytes:1048576,max_diff_bytes:1048576,max_files_per_diff:8}};
|
|
37
40
|
const host={schema:'deadbyte.host-policy.v1',enabled:true,arm_file:'C:/old/host.json',roots:{core:{path:'C:/old/core',read:true,write:true,exec:true}},commands:{tests:command()},max_read_bytes:262144,max_write_bytes:1048576};
|
|
38
41
|
const processPolicy={schema:'deadbyte.process-policy.v1',enabled:true,grant_file:'C:/old/process-grant.json',state_dir:'C:/old/process-state',
|
|
39
42
|
native_host:{path:'C:/old/deadbyte-process-host.exe',sha256:'0'.repeat(64)},
|
|
40
43
|
roots:{core:{path:'C:/old/core',deny_paths:['node_modules']}},
|
|
41
44
|
profiles:{probe:{executable:'C:/Windows/System32/ping.exe',executable_sha256:EXE,root_ids:['core'],prefix_args:['127.0.0.1','-n','2'],max_args:0,max_arg_bytes:256,max_sessions:1,max_input_bytes:4096,max_retained_output_bytes:65536,env_allow:['PATH','TEMP','TMP'],input_actions:{}}}};
|
|
42
|
-
const development=path.join(base,'development'); await mkdir(development,{recursive:true}); await mkdir(path.join(development,'scripts'),{recursive:true});
|
|
45
|
+
const development=path.join(base,'development'); await mkdir(development,{recursive:true}); await mkdir(path.join(development,'scripts'),{recursive:true});
|
|
46
|
+
await writeFile(path.join(development,'scripts','gate.ps1'),'Write-Output gate\n');
|
|
47
|
+
await writeFile(path.join(development,'scripts','gate-windows-old.ps1'),'Write-Output old-gate\n');
|
|
48
|
+
await writeFile(path.join(development,'scripts','gate-windows-current.ps1'),'Write-Output current-gate\n');
|
|
49
|
+
await writeFile(path.join(development,'package.json'),JSON.stringify({scripts:{'gate:windows':'powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-current.ps1'}}));
|
|
43
50
|
const result=await createGenerationBundle({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy});
|
|
44
51
|
const verified=await verifyGenerationBundle(result.generation_root,result.generation_manifest_sha256);
|
|
45
52
|
assert.equal(verified.files,6);
|
|
@@ -62,6 +69,9 @@ test('R27 generation bundle rebinds authority and mutable state outside the rele
|
|
|
62
69
|
assert.equal(generatedAuto.coding_policy_sha256,sha(codingBytes));
|
|
63
70
|
assert.equal(generatedAuto.lease_file,path.join(path.resolve(state),'runtime','autonomous-grant.json'));
|
|
64
71
|
assert.equal(generatedAuto.profiles.gate.script_sha256,sha(Buffer.from('Write-Output gate\n')));
|
|
72
|
+
assert.equal(generatedAuto.profiles['windows-release'].script_path,'scripts/gate-windows-current.ps1');
|
|
73
|
+
assert.equal(generatedAuto.profiles['windows-release'].script_sha256,sha(Buffer.from('Write-Output current-gate\n')));
|
|
74
|
+
assert.equal(generatedAuto.profiles['windows-release'].description,'Full Windows target release gate; runs only when finish is requested.');
|
|
65
75
|
const generatedHost=JSON.parse(await readFile(result.host_policy_path,'utf8'));
|
|
66
76
|
assert.equal(generatedHost.roots.core.path,path.resolve(development));
|
|
67
77
|
assert.notEqual(generatedHost.roots.core.path,path.resolve(release));
|
|
@@ -5,47 +5,59 @@ 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 R38_PREDECESSOR_RELEASE_ID='v0.11.1-220fac89dd82cdcd';
|
|
9
|
+
const R38_PREDECESSOR_MANIFEST='220fac89dd82cdcdddb82cd7d0b71b768f84a5c87d57ebaca0f5383f33d97a76';
|
|
10
10
|
|
|
11
|
-
test('
|
|
11
|
+
test('R38 hotfix authority agrees on V0.11.2 and preserves exact public 0.11.1 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.ps1'),
|
|
14
|
+
text('src/version.mjs'),text('controller/deadbyte-controller.ps1'),text('scripts/gate-windows-r38.ps1'),
|
|
15
15
|
text('README.txt'),text('docs/ARCHITECTURE.md'),text('controller/README.TXT'),
|
|
16
|
-
text('scripts/deferred-slot-operation.mjs'),text('scripts/release-parity.mjs'),text('scripts/final-closure.mjs'),
|
|
17
|
-
text('scripts/final-closure-verify.mjs'),text('scripts/windows-gate-evidence.mjs'),
|
|
16
|
+
text('scripts/deferred-slot-operation-r38.mjs'),text('scripts/release-parity-r38.mjs'),text('scripts/final-closure-r38.mjs'),
|
|
17
|
+
text('scripts/final-closure-verify-r38.mjs'),text('scripts/windows-gate-evidence-r38.mjs'),
|
|
18
18
|
text('controller/deadbyte-semantic-lsp-config.json')
|
|
19
19
|
]);
|
|
20
|
-
assert.equal(pkg.version,'0.11.
|
|
21
|
-
assert.
|
|
22
|
-
assert.
|
|
23
|
-
assert.
|
|
24
|
-
assert.
|
|
25
|
-
assert.
|
|
26
|
-
assert.match(
|
|
27
|
-
assert.match(
|
|
28
|
-
assert.match(
|
|
29
|
-
assert.match(
|
|
30
|
-
assert.match(
|
|
31
|
-
assert.match(
|
|
32
|
-
assert.match(
|
|
33
|
-
assert.match(parity
|
|
34
|
-
assert.match(
|
|
35
|
-
assert.match(
|
|
36
|
-
assert.match(
|
|
37
|
-
assert.match(
|
|
20
|
+
assert.equal(pkg.version,'0.11.2');
|
|
21
|
+
assert.equal(pkg.scripts['gate:windows'],'powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-r38.ps1');
|
|
22
|
+
assert.equal(pkg.scripts['self-update:deferred'],'node scripts/deferred-slot-operation-r38.mjs');
|
|
23
|
+
assert.equal(pkg.scripts['release:parity'],'node scripts/release-parity-r38.mjs');
|
|
24
|
+
assert.equal(pkg.scripts['release:final:write'],'node scripts/final-closure-r38.mjs');
|
|
25
|
+
assert.equal(pkg.scripts['release:final:verify'],'node scripts/final-closure-verify-r38.mjs');
|
|
26
|
+
assert.match(version,/DEADBYTE_VERSION = '0\.11\.2'/);
|
|
27
|
+
assert.match(controller,/CONTROLLER V0\.11\.2 \/ R38/);
|
|
28
|
+
assert.match(gate,/DEADBYTE V0\.11\.2 \/ R38 WINDOWS BATCH GATE: PASS/);
|
|
29
|
+
assert.match(readme,/^DEADBYTE MCP V0\.11\.2 \/ R38 HOTFIX BASELINE$/m);
|
|
30
|
+
assert.match(architecture,/^# DEADBYTE MCP V0\.11\.2 \/ R38 ARCHITECTURE$/m);
|
|
31
|
+
assert.match(controllerReadme,/^DEADBYTE MCP V0\.11\.2 \/ R38 CONTROLLER$/m);
|
|
32
|
+
assert.match(deferred,/--expected-version','0\.11\.2'/);
|
|
33
|
+
assert.match(parity,/R38_PREDECESSOR_RELEASE_ID='v0\.11\.1-220fac89dd82cdcd'/);
|
|
34
|
+
assert.match(parity,new RegExp(R38_PREDECESSOR_MANIFEST));
|
|
35
|
+
assert.match(parity,/version==='0\.11\.2'|version:'0\.11\.2'/);
|
|
36
|
+
assert.match(closure,/version:'0\.11\.2'/);
|
|
37
|
+
assert.match(closureVerify,/DEADBYTE V0\.11\.2 \/ R38 WINDOWS BATCH GATE: PASS/);
|
|
38
|
+
assert.match(windowsGate,/version:'0\.11\.2'/);
|
|
38
39
|
assert.equal(createHash('sha256').update(Buffer.from(semanticConfig,'utf8')).digest('hex'),'d274b099a298c1033fea8f1e8849751693bbc0448778d619400acb82d4f53a3b');
|
|
40
|
+
assert.equal(R38_PREDECESSOR_RELEASE_ID,'v0.11.1-220fac89dd82cdcd');
|
|
39
41
|
});
|
|
40
42
|
|
|
41
|
-
test('
|
|
42
|
-
const [
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
test('R38 hotfix keeps ONLINE reuse and fixes PowerShell $Host recovery collision',async()=>{
|
|
44
|
+
const [cli,controller]=await Promise.all([text('src/deadbyte-cli.mjs'),text('controller/deadbyte-controller.ps1')]);
|
|
45
|
+
assert.match(cli,/controllerRuntimeReady/);
|
|
46
|
+
assert.match(cli,/Runtime already ONLINE; reusing verified bridge\/cloud\/supervisor/);
|
|
47
|
+
const start=controller.indexOf('function Recover-AutonomousSupervisor');
|
|
48
|
+
const end=controller.indexOf('function Show-State',start);
|
|
49
|
+
assert.ok(start>=0&&end>start,'supervisor recovery block missing');
|
|
50
|
+
const block=controller.slice(start,end);
|
|
51
|
+
assert.doesNotMatch(block,/\$host\s*=/i);
|
|
52
|
+
assert.match(block,/\$hostStatus\s*=\s*Get-HostArmStatus/);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('sealed R37 tooling remains historical 0.11.1 authority',async()=>{
|
|
56
|
+
const [parity,gate,closure]=await Promise.all([
|
|
57
|
+
text('scripts/release-parity.mjs'),text('scripts/gate-windows.ps1'),text('scripts/final-closure.mjs')
|
|
46
58
|
]);
|
|
47
|
-
assert.match(parity,/
|
|
48
|
-
assert.match(parity,/
|
|
49
|
-
|
|
50
|
-
assert.match(
|
|
59
|
+
assert.match(parity,/R37_PREDECESSOR_RELEASE_ID='v0\.11\.0-08c2a0008b1f84c9'/);
|
|
60
|
+
assert.match(parity,/version==='0\.11\.1'|version:'0\.11\.1'/);
|
|
61
|
+
assert.match(gate,/DEADBYTE V0\.11\.1 \/ R37 WINDOWS BATCH GATE: PASS/);
|
|
62
|
+
assert.match(closure,/version:'0\.11\.1'/);
|
|
51
63
|
});
|
|
@@ -2,12 +2,15 @@ import test from 'node:test';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { createLiveTimelineTracker } from '../src/remote-live-log.mjs';
|
|
4
4
|
|
|
5
|
-
test('R37 live timeline
|
|
5
|
+
test('R37 live timeline renders sanitized tool-call detail and correlates terminal result with stable short span id',()=>{
|
|
6
6
|
let now=1000;
|
|
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.
|
|
10
|
+
assert.match(run,/^1970-01-01T00:00:01\.000Z \| coding_file_read\s+\| Arguments: \{/);
|
|
11
|
+
assert.match(run,/\"path\":\"src\/a\.mjs\"/);
|
|
12
|
+
assert.match(run,/\"reason\":\"payload\"/);
|
|
13
|
+
assert.match(run,/\"reason\":\"secret\"/);
|
|
11
14
|
now=1250;
|
|
12
15
|
const done=tracker.observe({event_type:'tool.result',tool:'coding_file_read',request_id:'abcdef1234567890',
|
|
13
16
|
outcome:{is_error:false,duration_ms:250}});
|
|
@@ -11,16 +11,16 @@ const cliPath=path.join(root,'src','deadbyte-cli.mjs');
|
|
|
11
11
|
const isWindows=process.platform==='win32';
|
|
12
12
|
const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
|
|
13
13
|
|
|
14
|
-
async function fixture({failCommand=''}={}){
|
|
14
|
+
async function fixture({failCommand='',statusOnline=false}={}){
|
|
15
15
|
const dir=await mkdtemp(path.join(os.tmpdir(),'deadbyte-remote-session-'));
|
|
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
19
|
await mkdir(bin,{recursive:true});
|
|
20
20
|
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 '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`;
|
|
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`;
|
|
22
22
|
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}};
|
|
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'}};
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
async function traceCommands(file,{read=readFile,retryDelayMs=10,maxAttempts=20}={}){
|
|
@@ -65,15 +65,40 @@ async function collect(child){
|
|
|
65
65
|
|
|
66
66
|
const winTest=isWindows?test:test.skip;
|
|
67
67
|
|
|
68
|
+
winTest('bare remote reuses a fully verified ONLINE runtime without invoking blocking start',async()=>{
|
|
69
|
+
const fx=await fixture({failCommand:'start',statusOnline:true});
|
|
70
|
+
let child=null;
|
|
71
|
+
try{
|
|
72
|
+
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
73
|
+
const output=await collect(child);
|
|
74
|
+
const ready=await waitFor(fx.trace,commands=>commands.includes('hoststatus'),{timeoutMs:8000});
|
|
75
|
+
assert.deepEqual(ready.slice(0,8),[
|
|
76
|
+
'status','armprocess','armcoding','armautonomous',
|
|
77
|
+
'processstatus','codingstatus','autonomousstatus','hoststatus'
|
|
78
|
+
]);
|
|
79
|
+
assert.doesNotMatch(ready.join(','),/(^|,)start(,|$)/);
|
|
80
|
+
assert.equal(child.exitCode,null,'remote session must remain foreground after reusing ONLINE runtime');
|
|
81
|
+
assert.match(output.stdout,/Runtime already ONLINE; reusing verified bridge\/cloud\/supervisor/);
|
|
82
|
+
const authDeadline=Date.now()+3000;
|
|
83
|
+
while(Date.now()<authDeadline&&!/🔐 \[AUTH\] ARMED: process, coding, autonomous \| host DISARMED/.test(output.stdout)) await sleep(50);
|
|
84
|
+
assert.match(output.stdout,/🔐 \[AUTH\] ARMED: process, coding, autonomous \| host DISARMED/);
|
|
85
|
+
child.kill('SIGINT');
|
|
86
|
+
await output.closed;
|
|
87
|
+
}finally{
|
|
88
|
+
if(child&&child.exitCode===null) child.kill('SIGKILL');
|
|
89
|
+
await rm(fx.dir,{recursive:true,force:true});
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
68
93
|
winTest('bare remote uses one unified live timeline, keeps authority semantics, then disarms and parks when the foreground session ends',async()=>{
|
|
69
94
|
const fx=await fixture();
|
|
70
95
|
let child=null;
|
|
71
96
|
try{
|
|
72
97
|
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
73
98
|
const output=await collect(child);
|
|
74
|
-
const ready=await waitFor(fx.trace,commands=>commands.slice(0,
|
|
75
|
-
assert.deepEqual(ready.slice(0,
|
|
76
|
-
'start','armprocess','armcoding','armautonomous',
|
|
99
|
+
const ready=await waitFor(fx.trace,commands=>commands.slice(0,9).join(',')==='status,start,armprocess,armcoding,armautonomous,processstatus,codingstatus,autonomousstatus,hoststatus');
|
|
100
|
+
assert.deepEqual(ready.slice(0,9),[
|
|
101
|
+
'status','start','armprocess','armcoding','armautonomous',
|
|
77
102
|
'processstatus','codingstatus','autonomousstatus','hoststatus'
|
|
78
103
|
]);
|
|
79
104
|
assert.equal(child.exitCode,null,'remote session must remain foreground after becoming ready');
|
|
@@ -111,13 +136,14 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
111
136
|
].join('\n')+'\n','utf8');
|
|
112
137
|
const logDeadline=Date.now()+3000;
|
|
113
138
|
while(Date.now()<logDeadline&&!/✗ \[fedcba98\] ERR coding_file_read 1\.73s/.test(output.stdout)) await sleep(50);
|
|
114
|
-
assert.match(output.stdout
|
|
115
|
-
assert.match(output.stdout,/✓ \[abcdef12\] OK coding_tree 42ms \[cursor r1:[0-9a-f]{24}:
|
|
116
|
-
assert.match(output.stdout
|
|
117
|
-
assert.match(output.stdout,/✗ \[fedcba98\] ERR coding_file_read 1\.73s \[cursor r1:[0-9a-f]{24}:
|
|
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}Z \| coding_tree\s+\| Arguments: .*\"path\":\"src\".*\"root_id\":\"projects\".*\[cursor r1:[0-9a-f]{24}:1\]$/m);
|
|
140
|
+
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}Z \| coding_file_read\s+\| Arguments: \{\"path\":\"src\/deadbyte-cli\.mjs\",\"root_id\":\"projects\"\} \[cursor r1:[0-9a-f]{24}:3\]$/m);
|
|
142
|
+
assert.match(output.stdout,/✗ \[fedcba98\] ERR coding_file_read 1\.73s \[cursor r1:[0-9a-f]{24}:4\]/);
|
|
118
143
|
assert.equal((output.stdout.match(/✓ \[abcdef12\] OK coding_tree 42ms/g)||[]).length,1,'duplicate terminal result must print once');
|
|
119
144
|
const resultJournal=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-results',`${marker.result_session_id}.json`),'utf8'));
|
|
120
|
-
assert.deepEqual(resultJournal.events.map(event=>event.seq),[1,2]);
|
|
145
|
+
assert.deepEqual(resultJournal.events.map(event=>event.seq),[1,2,3,4]);
|
|
146
|
+
assert.deepEqual(resultJournal.events.map(event=>event.event_type),['tool.call','tool.result','tool.call','tool.result']);
|
|
121
147
|
assert.equal(resultJournal.events[0].request_id,'abcdef1234567890');
|
|
122
148
|
assert.ok(resultJournal.events.every(event=>/^[0-9a-f]{64}$/.test(event.payload_sha256)));
|
|
123
149
|
assert.doesNotMatch(JSON.stringify(resultJournal),/TOOL IN|TOOL OUT|stdout_excerpt|stderr_excerpt/);
|
|
@@ -146,7 +172,7 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
146
172
|
}
|
|
147
173
|
});
|
|
148
174
|
|
|
149
|
-
winTest('remote --resume replays
|
|
175
|
+
winTest('remote --resume replays sanitized tool-call and terminal result events after the cursor without duplicates or loss',async()=>{
|
|
150
176
|
const fx=await fixture();
|
|
151
177
|
let first=null,second=null;
|
|
152
178
|
try{
|
|
@@ -157,14 +183,22 @@ winTest('remote --resume replays only terminal results after the cursor before s
|
|
|
157
183
|
while(Date.now()<liveDeadline&&!/\[LOG\] Unified live timeline/.test(firstOutput.stdout)) await sleep(50);
|
|
158
184
|
const auditDir=path.join(fx.state,'audit');await mkdir(auditDir,{recursive:true});
|
|
159
185
|
const auditFile=path.join(auditDir,'segment-00000001.jsonl');
|
|
160
|
-
|
|
186
|
+
const firstBatch=[
|
|
187
|
+
{seq:1,event_type:'tool.request',tool:'coding_tree',request_id:'resume-request-1',created_at_utc:'2026-09-18T16:30:00.000Z',args:{root_id:'projects',path:'src'}},
|
|
188
|
+
{seq:2,event_type:'tool.result',tool:'coding_tree',request_id:'resume-request-1',created_at_utc:'2026-09-18T16:30:00.100Z',outcome:{is_error:false}}
|
|
189
|
+
];
|
|
190
|
+
await writeFile(auditFile,firstBatch.map(JSON.stringify).join('\n')+'\n','utf8');
|
|
161
191
|
let deadline=Date.now()+3000;
|
|
162
|
-
while(Date.now()<deadline&&!/coding_tree .*\[cursor r1:[0-9a-f]{24}:
|
|
163
|
-
const cursor1=firstOutput.stdout.match(/coding_tree .*\[cursor (r1:[0-9a-f]{24}:
|
|
192
|
+
while(Date.now()<deadline&&!/coding_tree .*\[cursor r1:[0-9a-f]{24}:2\]/.test(firstOutput.stdout)) await sleep(50);
|
|
193
|
+
const cursor1=firstOutput.stdout.match(/coding_tree .*\[cursor (r1:[0-9a-f]{24}:2)\]/)?.[1];
|
|
164
194
|
assert.ok(cursor1,'first result cursor missing');
|
|
165
|
-
|
|
195
|
+
const secondBatch=[
|
|
196
|
+
{seq:3,event_type:'tool.request',tool:'coding_file_read',request_id:'resume-request-2',created_at_utc:'2026-09-18T16:30:01.000Z',args:{root_id:'projects',path:'src/deadbyte-cli.mjs',token:'DO_NOT_REPLAY_SECRET'}},
|
|
197
|
+
{seq:4,event_type:'tool.result',tool:'coding_file_read',request_id:'resume-request-2',created_at_utc:'2026-09-18T16:30:01.100Z',outcome:{is_error:false}}
|
|
198
|
+
];
|
|
199
|
+
await writeFile(auditFile,secondBatch.map(JSON.stringify).join('\n')+'\n',{encoding:'utf8',flag:'a'});
|
|
166
200
|
deadline=Date.now()+3000;
|
|
167
|
-
while(Date.now()<deadline&&!/coding_file_read .*\[cursor r1:[0-9a-f]{24}:
|
|
201
|
+
while(Date.now()<deadline&&!/coding_file_read .*\[cursor r1:[0-9a-f]{24}:4\]/.test(firstOutput.stdout)) await sleep(50);
|
|
168
202
|
first.stdin.end();
|
|
169
203
|
const firstEnded=await Promise.race([firstOutput.closed,sleep(6000).then(()=>({timeout:true}))]);
|
|
170
204
|
assert.equal(firstEnded.timeout,undefined);
|
|
@@ -172,14 +206,20 @@ winTest('remote --resume replays only terminal results after the cursor before s
|
|
|
172
206
|
second=spawn(process.execPath,[cliPath,'remote','--resume',cursor1],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
173
207
|
const secondOutput=await collect(second);
|
|
174
208
|
deadline=Date.now()+5000;
|
|
175
|
-
while(Date.now()<deadline&&(
|
|
209
|
+
while(Date.now()<deadline&&(!/coding_file_read\s+\| Arguments: .*REPLAY cursor r1:[0-9a-f]{24}:3/.test(secondOutput.stdout)||
|
|
210
|
+
!/\[REPLAY\] \[OK\] coding_file_read \[cursor r1:[0-9a-f]{24}:4\]/.test(secondOutput.stdout)||
|
|
176
211
|
!/\[LOG\] Unified live timeline/.test(secondOutput.stdout))) await sleep(50);
|
|
177
|
-
assert.match(secondOutput.stdout
|
|
212
|
+
assert.match(secondOutput.stdout,/^2026-09-18T16:30:01\.000Z \| coding_file_read\s+\| Arguments: .*\"path\":\"src\/deadbyte-cli\.mjs\".*REPLAY cursor r1:[0-9a-f]{24}:3$/m);
|
|
213
|
+
assert.match(secondOutput.stdout,/\[REPLAY\] \[OK\] coding_file_read \[cursor r1:[0-9a-f]{24}:4\]/);
|
|
178
214
|
assert.match(secondOutput.stdout,/♻️ \[SESSION\] Previous result stream replayed/);
|
|
215
|
+
assert.doesNotMatch(secondOutput.stdout,/DO_NOT_REPLAY_SECRET/);
|
|
179
216
|
assert.doesNotMatch(secondOutput.stdout,/\[REPLAY\] \[OK\] coding_tree/);
|
|
180
|
-
|
|
217
|
+
assert.equal((secondOutput.stdout.match(/\| coding_file_read\s+\| Arguments:/g)||[]).length,1,'tool-call replay duplicated');
|
|
218
|
+
assert.equal((secondOutput.stdout.match(/\[REPLAY\] \[OK\] coding_file_read/g)||[]).length,1,'terminal replay duplicated');
|
|
219
|
+
const callReplayIndex=secondOutput.stdout.indexOf('| coding_file_read');
|
|
220
|
+
const resultReplayIndex=secondOutput.stdout.indexOf('[REPLAY] [OK] coding_file_read');
|
|
181
221
|
const liveIndex=secondOutput.stdout.indexOf('[LOG] Unified live timeline');
|
|
182
|
-
assert.ok(
|
|
222
|
+
assert.ok(callReplayIndex>=0&&resultReplayIndex>callReplayIndex&&liveIndex>resultReplayIndex,'resume ordering must be call -> result -> live tail');
|
|
183
223
|
second.stdin.end();
|
|
184
224
|
const secondEnded=await Promise.race([secondOutput.closed,sleep(6000).then(()=>({timeout:true}))]);
|
|
185
225
|
assert.equal(secondEnded.timeout,undefined);
|
|
@@ -210,8 +250,8 @@ winTest('detached guardian revokes authority, parks, and removes the session mar
|
|
|
210
250
|
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
211
251
|
const output=await collect(child);
|
|
212
252
|
const ready=await waitFor(fx.trace,commands=>commands.slice(0,8).join(',')==='start,armprocess,armcoding,armautonomous,processstatus,codingstatus,autonomousstatus,hoststatus');
|
|
213
|
-
assert.deepEqual(ready.slice(0,
|
|
214
|
-
'start','armprocess','armcoding','armautonomous',
|
|
253
|
+
assert.deepEqual(ready.slice(0,9),[
|
|
254
|
+
'status','start','armprocess','armcoding','armautonomous',
|
|
215
255
|
'processstatus','codingstatus','autonomousstatus','hoststatus'
|
|
216
256
|
]);
|
|
217
257
|
const markerPath=path.join(fx.state,'runtime','remote-session.json');
|
|
@@ -254,7 +294,7 @@ winTest('bare remote rolls back authority and parks if one plane fails to arm',a
|
|
|
254
294
|
assert.equal(ended.timeout,undefined,'failed startup did not terminate');
|
|
255
295
|
assert.notEqual(ended.code,0);
|
|
256
296
|
const commands=await traceCommands(fx.trace);
|
|
257
|
-
assert.deepEqual(commands.slice(0,
|
|
297
|
+
assert.deepEqual(commands.slice(0,4),['status','start','armprocess','armcoding']);
|
|
258
298
|
assert.deepEqual(commands.slice(-5),['disarmautonomous','disarmcoding','disarmprocess','disarmhost','park']);
|
|
259
299
|
assert.doesNotMatch(output.stdout,/Live logs/i);
|
|
260
300
|
}finally{await rm(fx.dir,{recursive:true,force:true});}
|
|
@@ -271,7 +311,8 @@ winTest('bare remote recovers a stale crashed session before arming a new one',a
|
|
|
271
311
|
const output=await collect(child);
|
|
272
312
|
const ready=await waitFor(fx.trace,commands=>commands.includes('hoststatus'));
|
|
273
313
|
assert.deepEqual(ready.slice(0,5),['disarmautonomous','disarmcoding','disarmprocess','disarmhost','park']);
|
|
274
|
-
assert.equal(ready[5],'
|
|
314
|
+
assert.equal(ready[5],'status');
|
|
315
|
+
assert.equal(ready[6],'start');
|
|
275
316
|
assert.match(output.stdout,/♻️ \[RECOVERY\] Stale remote session pid=2147483000; revoking authority before restart\./);
|
|
276
317
|
child.stdin.end();
|
|
277
318
|
const ended=await Promise.race([output.closed,sleep(6000).then(()=>({timeout:true}))]);
|