deadbyte-mcp 0.15.45 → 0.15.46
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 +34 -24
- package/PROVENANCE.json +260 -257
- package/README.txt +22 -21
- package/RELEASE.json +367 -346
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +501 -503
- package/bridge/package.json +1 -1
- package/controller/deadbyte-controller.ps1 +20 -6
- package/controller/deadbyte-process-policy.json +4 -4
- package/package.json +2 -2
- package/scripts/gate-windows-r72.ps1 +76 -0
- package/scripts/npm-release-r48.mjs +2 -0
- package/scripts/npm-release-readiness-r72.mjs +76 -0
- package/src/deadbyte-cli.mjs +722 -721
- package/src/launcher-isolation.mjs +58 -0
- package/src/remote-authority.mjs +75 -75
- package/src/remote-mcp-machine-smoke-client.mjs +84 -84
- package/src/runtime-update.mjs +2056 -2008
- package/src/staging-input-isolation.mjs +79 -0
- package/src/version.mjs +1 -1
- package/test/deferred-slot-operation-behavior.test.mjs +2 -2
- package/test/r58-cleanup-retention.test.mjs +3 -3
- package/test/r58-concurrent-recovery.test.mjs +3 -3
- package/test/r58-stale-owner.test.mjs +1 -1
- package/test/r59-tool-surface-compat.test.mjs +2 -2
- package/test/r60-authority-coherence.test.mjs +10 -2
- package/test/r71-isolated-slot-rollback-restore.test.mjs +11 -11
- package/test/r71-release-identity.test.mjs +12 -10
- package/test/r72-launcher-isolation.test.mjs +99 -0
- package/test/r72-publication-fence.test.mjs +56 -0
- package/test/r72-publication-output-integrity.test.mjs +110 -0
- package/test/r72-remote-safe-contract.test.mjs +101 -0
- package/test/r72-staging-input-isolation.test.mjs +87 -0
- package/test/r72-staging-workspace-isolation.test.mjs +114 -0
- package/test/remote-session-cli.test.mjs +110 -64
|
@@ -12,6 +12,15 @@ const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
|
12
12
|
const cliPath=path.join(root,'src','deadbyte-cli.mjs');
|
|
13
13
|
const isWindows=process.platform==='win32';
|
|
14
14
|
const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
|
|
15
|
+
const fixtureTimeoutMs=30000;
|
|
16
|
+
async function boundedCompletion(promise,timeoutMs=fixtureTimeoutMs){
|
|
17
|
+
let timer;
|
|
18
|
+
try{return await Promise.race([promise,new Promise(resolve=>{
|
|
19
|
+
timer=setTimeout(()=>resolve({timeout:true}),timeoutMs);
|
|
20
|
+
timer.unref?.();
|
|
21
|
+
})]);}
|
|
22
|
+
finally{clearTimeout(timer);}
|
|
23
|
+
}
|
|
15
24
|
|
|
16
25
|
async function fixture({failCommand='',statusOnline=false}={}){
|
|
17
26
|
const dir=await mkdtemp(path.join(os.tmpdir(),'deadbyte-remote-session-'));
|
|
@@ -45,7 +54,7 @@ async function traceCommands(file,{read=readFile,retryDelayMs=10,maxAttempts=20}
|
|
|
45
54
|
}
|
|
46
55
|
return [];
|
|
47
56
|
}
|
|
48
|
-
async function waitFor(file,predicate,{timeoutMs=
|
|
57
|
+
async function waitFor(file,predicate,{timeoutMs=fixtureTimeoutMs}={}){
|
|
49
58
|
const deadline=Date.now()+timeoutMs;
|
|
50
59
|
while(Date.now()<deadline){
|
|
51
60
|
const commands=await traceCommands(file);
|
|
@@ -73,6 +82,17 @@ async function collect(child){
|
|
|
73
82
|
const closed=new Promise(resolve=>child.once('close',(code,signal)=>resolve({code,signal})));
|
|
74
83
|
return {closed,get stdout(){return stdout;},get stderr(){return stderr;}};
|
|
75
84
|
}
|
|
85
|
+
// Windows: SIGKILL leaves PowerShell child holding a handle on the .ps1 fixture
|
|
86
|
+
// for a short window; retry rm() until it succeeds or gives up.
|
|
87
|
+
async function safeRm(dir,{maxAttempts=10,retryDelayMs=200}={}){
|
|
88
|
+
for(let i=0;i<maxAttempts;i++){
|
|
89
|
+
try{await rm(dir,{recursive:true,force:true});return;}
|
|
90
|
+
catch(err){
|
|
91
|
+
if(err?.code==='EBUSY'&&i<maxAttempts-1){await sleep(retryDelayMs);continue;}
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
76
96
|
|
|
77
97
|
const winTest=isWindows?test:test.skip;
|
|
78
98
|
|
|
@@ -80,14 +100,14 @@ winTest('bare remote reuses a fully verified ONLINE runtime without invoking blo
|
|
|
80
100
|
const fx=await fixture({failCommand:'start',statusOnline:true});
|
|
81
101
|
let child=null;
|
|
82
102
|
try{
|
|
83
|
-
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
103
|
+
child=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
84
104
|
const output=await collect(child);
|
|
85
|
-
const ready=await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=3,{timeoutMs:
|
|
105
|
+
const ready=await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=3,{timeoutMs:fixtureTimeoutMs});
|
|
86
106
|
assert.deepEqual(ready.slice(0,6),['status','status','armprocess','armcoding','armautonomous','status'],output.stderr);
|
|
87
107
|
assert.doesNotMatch(ready.join(','),/(^|,)start(,|$)/);
|
|
88
108
|
assert.equal(child.exitCode,null,'remote session must remain foreground after reusing ONLINE runtime');
|
|
89
109
|
assert.match(output.stdout,/Runtime already ONLINE; reusing verified bridge\/cloud\/supervisor/);
|
|
90
|
-
const authDeadline=Date.now()+
|
|
110
|
+
const authDeadline=Date.now()+fixtureTimeoutMs;
|
|
91
111
|
while(Date.now()<authDeadline&&!/\[AUTH\] process ARMED \| coding ARMED \| autonomous ARMED \| desktop DISARMED \| host DISARMED/.test(output.stdout)) await sleep(50);
|
|
92
112
|
assert.match(output.stdout,/\[DISPLAY\] ASCII fallback: stdout_not_tty/);
|
|
93
113
|
assert.match(output.stdout,/\[AUTH\] process ARMED \| coding ARMED \| autonomous ARMED \| desktop DISARMED \| host DISARMED/);
|
|
@@ -95,7 +115,31 @@ winTest('bare remote reuses a fully verified ONLINE runtime without invoking blo
|
|
|
95
115
|
await output.closed;
|
|
96
116
|
}finally{
|
|
97
117
|
if(child&&child.exitCode===null) child.kill('SIGKILL');
|
|
98
|
-
await
|
|
118
|
+
await safeRm(fx.dir);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
winTest('R72 bare remote reaches READY with every authority disarmed and never arms a plane',async()=>{
|
|
123
|
+
const fx=await fixture({statusOnline:true});
|
|
124
|
+
let child=null;
|
|
125
|
+
try{
|
|
126
|
+
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
127
|
+
const output=await collect(child);
|
|
128
|
+
const deadline=Date.now()+fixtureTimeoutMs;
|
|
129
|
+
while(Date.now()<deadline&&!/\[READY\]/.test(output.stdout)&&child.exitCode===null)await sleep(50);
|
|
130
|
+
assert.match(output.stdout,/\[READY\]/,output.stderr);
|
|
131
|
+
assert.match(output.stdout,/\[AUTH\] process DISARMED \| coding DISARMED \| autonomous DISARMED \| desktop DISARMED \| host DISARMED/);
|
|
132
|
+
const before=await traceCommands(fx.trace);
|
|
133
|
+
assert.deepEqual(before.slice(0,6),['status','status','disarmautonomous','disarmcoding','disarmprocess','status']);
|
|
134
|
+
assert.equal(before.filter(x=>/^arm(?:process|coding|autonomous|host|desktop)$/.test(x)).length,0,'bare remote must issue zero ARM commands');
|
|
135
|
+
child.stdin.end();
|
|
136
|
+
const ended=await boundedCompletion(output.closed);
|
|
137
|
+
assert.equal(ended.timeout,undefined,'bare remote did not stop');
|
|
138
|
+
assert.equal(ended.code,0,output.stderr);
|
|
139
|
+
assert.equal((await traceCommands(fx.trace)).at(-1),'park');
|
|
140
|
+
}finally{
|
|
141
|
+
if(child?.exitCode===null)child.kill('SIGKILL');
|
|
142
|
+
await safeRm(fx.dir);
|
|
99
143
|
}
|
|
100
144
|
});
|
|
101
145
|
|
|
@@ -106,16 +150,16 @@ winTest('bounded remote disarms a stale standing Host grant before READY',async(
|
|
|
106
150
|
try{
|
|
107
151
|
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
108
152
|
const output=await collect(child);
|
|
109
|
-
const commands=await waitFor(fx.trace,items=>items.filter(x=>x==='status').length>=3,{timeoutMs:
|
|
153
|
+
const commands=await waitFor(fx.trace,items=>items.filter(x=>x==='status').length>=3,{timeoutMs:fixtureTimeoutMs});
|
|
110
154
|
assert.ok(commands.indexOf('disarmhost')>commands.indexOf('status'));
|
|
111
|
-
const deadline=Date.now()+
|
|
155
|
+
const deadline=Date.now()+fixtureTimeoutMs;
|
|
112
156
|
while(Date.now()<deadline&&!/\[READY\]/.test(output.stdout)) await sleep(50);
|
|
113
157
|
assert.match(output.stdout,/\[READY\]/,output.stderr);
|
|
114
158
|
child.stdin.end();
|
|
115
159
|
assert.equal((await output.closed).code,0,output.stderr);
|
|
116
160
|
}finally{
|
|
117
161
|
if(child&&child.exitCode===null) child.kill('SIGKILL');
|
|
118
|
-
await
|
|
162
|
+
await safeRm(fx.dir);
|
|
119
163
|
}
|
|
120
164
|
});
|
|
121
165
|
|
|
@@ -125,7 +169,7 @@ winTest('remote --full verifies all five planes before READY and revokes them on
|
|
|
125
169
|
try{
|
|
126
170
|
child=spawn(process.execPath,[cliPath,'remote','--full'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
127
171
|
const output=await collect(child);
|
|
128
|
-
const deadline=Date.now()+
|
|
172
|
+
const deadline=Date.now()+fixtureTimeoutMs;
|
|
129
173
|
while(Date.now()<deadline&&!/\[READY\]/.test(output.stdout)) await sleep(50);
|
|
130
174
|
assert.match(output.stdout,/\[AUTH\] process ARMED \| coding ARMED \| autonomous ARMED \| desktop ARMED \| host ARMED/,output.stderr);
|
|
131
175
|
assert.match(output.stdout,/\[READY\]/);
|
|
@@ -137,7 +181,7 @@ winTest('remote --full verifies all five planes before READY and revokes them on
|
|
|
137
181
|
assert.deepEqual(after.slice(-8),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','status','machinetest','park']);
|
|
138
182
|
}finally{
|
|
139
183
|
if(child?.exitCode===null) child.kill('SIGKILL');
|
|
140
|
-
await
|
|
184
|
+
await safeRm(fx.dir);
|
|
141
185
|
}
|
|
142
186
|
});
|
|
143
187
|
|
|
@@ -148,7 +192,7 @@ for(const [flag,armedPlane,disarmedPlane] of [['--desktop','desktop','host'],['-
|
|
|
148
192
|
try{
|
|
149
193
|
child=spawn(process.execPath,[cliPath,'remote',flag],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
150
194
|
const output=await collect(child);
|
|
151
|
-
const deadline=Date.now()+
|
|
195
|
+
const deadline=Date.now()+fixtureTimeoutMs;
|
|
152
196
|
while(Date.now()<deadline&&!/\[READY\]/.test(output.stdout)) await sleep(50);
|
|
153
197
|
assert.match(output.stdout,/\[READY\]/,output.stderr);
|
|
154
198
|
assert.match(output.stdout,new RegExp(`${armedPlane} ARMED`));
|
|
@@ -160,7 +204,7 @@ for(const [flag,armedPlane,disarmedPlane] of [['--desktop','desktop','host'],['-
|
|
|
160
204
|
assert.equal((await output.closed).code,0,output.stderr);
|
|
161
205
|
}finally{
|
|
162
206
|
if(child?.exitCode===null) child.kill('SIGKILL');
|
|
163
|
-
await
|
|
207
|
+
await safeRm(fx.dir);
|
|
164
208
|
}
|
|
165
209
|
});
|
|
166
210
|
}
|
|
@@ -173,7 +217,7 @@ winTest('conflicting remote authority modes fail before controller mutation',asy
|
|
|
173
217
|
assert.notEqual((await output.closed).code,0);
|
|
174
218
|
assert.match(output.stderr,/mutually exclusive/);
|
|
175
219
|
assert.deepEqual(await traceCommands(fx.trace),[]);
|
|
176
|
-
}finally{await
|
|
220
|
+
}finally{await safeRm(fx.dir);}
|
|
177
221
|
});
|
|
178
222
|
|
|
179
223
|
winTest('remote refuses READY when public signed MCP execution probe fails',async()=>{
|
|
@@ -182,14 +226,14 @@ winTest('remote refuses READY when public signed MCP execution probe fails',asyn
|
|
|
182
226
|
try{
|
|
183
227
|
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
184
228
|
const output=await collect(child);
|
|
185
|
-
const ended=await
|
|
229
|
+
const ended=await boundedCompletion(output.closed);
|
|
186
230
|
assert.equal(ended.timeout,undefined,'remote did not fail closed after execution probe failure');
|
|
187
231
|
assert.notEqual(ended.code,0);
|
|
188
232
|
assert.doesNotMatch(output.stdout,/\[READY\]|\[OK\] ONLINE/);
|
|
189
233
|
assert.match(output.stderr,/machinetest|execution probe/i);
|
|
190
234
|
}finally{
|
|
191
235
|
if(child?.exitCode===null) child.kill('SIGKILL');
|
|
192
|
-
await
|
|
236
|
+
await safeRm(fx.dir);
|
|
193
237
|
}
|
|
194
238
|
});
|
|
195
239
|
|
|
@@ -201,14 +245,14 @@ winTest('remote refuses READY when controller snapshot lies about Host enforceme
|
|
|
201
245
|
try{
|
|
202
246
|
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
203
247
|
const output=await collect(child);
|
|
204
|
-
const ended=await
|
|
248
|
+
const ended=await boundedCompletion(output.closed);
|
|
205
249
|
assert.equal(ended.timeout,undefined,`remote did not reject split authority truth; stdout=${output.stdout.slice(-1200)} stderr=${output.stderr.slice(-1200)} trace=${(await traceCommands(fx.trace)).join(',')}`);
|
|
206
250
|
assert.notEqual(ended.code,0);
|
|
207
251
|
assert.doesNotMatch(output.stdout,/\[READY\]|\[OK\] ONLINE/);
|
|
208
252
|
assert.match(output.stderr,/machinetest|enforcement/i);
|
|
209
253
|
}finally{
|
|
210
254
|
if(child?.exitCode===null) child.kill('SIGKILL');
|
|
211
|
-
await
|
|
255
|
+
await safeRm(fx.dir);
|
|
212
256
|
}
|
|
213
257
|
});
|
|
214
258
|
|
|
@@ -220,7 +264,7 @@ winTest('foreground cleanup retains ownership when status lies about a no-op Hos
|
|
|
220
264
|
try{
|
|
221
265
|
child=spawn(process.execPath,[cliPath,'remote','--full'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
222
266
|
const output=await collect(child);
|
|
223
|
-
const deadline=Date.now()+
|
|
267
|
+
const deadline=Date.now()+fixtureTimeoutMs;
|
|
224
268
|
while(Date.now()<deadline&&!/\[READY\]/.test(output.stdout)) await sleep(50);
|
|
225
269
|
assert.match(output.stdout,/\[READY\]/,output.stderr);
|
|
226
270
|
child.stdin.end();
|
|
@@ -230,7 +274,7 @@ winTest('foreground cleanup retains ownership when status lies about a no-op Hos
|
|
|
230
274
|
assert.ok(!(await traceCommands(fx.trace)).includes('park'));
|
|
231
275
|
}finally{
|
|
232
276
|
if(child?.exitCode===null) child.kill('SIGKILL');
|
|
233
|
-
await
|
|
277
|
+
await safeRm(fx.dir);
|
|
234
278
|
}
|
|
235
279
|
});
|
|
236
280
|
|
|
@@ -240,26 +284,28 @@ winTest('bare remote PARKED startup does not wait for a long-lived child holding
|
|
|
240
284
|
let child=null;
|
|
241
285
|
try{
|
|
242
286
|
const startedAt=Date.now();
|
|
243
|
-
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
287
|
+
child=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
244
288
|
const output=await collect(child);
|
|
245
|
-
const
|
|
289
|
+
const afterStart=await waitFor(fx.trace,commands=>commands.length>=3,{timeoutMs:15000});
|
|
246
290
|
const elapsed=Date.now()-startedAt;
|
|
291
|
+
assert.deepEqual(afterStart.slice(0,3),['status','start','status']);
|
|
292
|
+
assert.ok(elapsed<12000,`PARKED startup waited on inherited child pipe handles: ${elapsed}ms`);
|
|
293
|
+
const ready=await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=4,{timeoutMs:fixtureTimeoutMs});
|
|
247
294
|
assert.deepEqual(ready.slice(0,8),['status','start','status','status','armprocess','armcoding','armautonomous','status']);
|
|
248
|
-
assert.ok(elapsed<7000,`PARKED startup waited on inherited child pipe handles: ${elapsed}ms`);
|
|
249
295
|
assert.equal(child.exitCode,null,'remote session must remain foreground after PARKED startup');
|
|
250
296
|
assert.match(output.stdout,/\[BOOT\] Starting bridge\/runtime\.\.\./);
|
|
251
|
-
const readyDeadline=Date.now()+
|
|
297
|
+
const readyDeadline=Date.now()+fixtureTimeoutMs;
|
|
252
298
|
while(Date.now()<readyDeadline&&!/\[OK\] ONLINE/.test(output.stdout)) await sleep(50);
|
|
253
299
|
assert.match(output.stdout,/\[OK\] ONLINE/);
|
|
254
300
|
child.stdin.end();
|
|
255
|
-
const ended=await
|
|
301
|
+
const ended=await boundedCompletion(output.closed);
|
|
256
302
|
assert.equal(ended.timeout,undefined,'remote session did not exit after PARKED startup proof');
|
|
257
303
|
assert.equal(ended.code,0,output.stderr);
|
|
258
304
|
}finally{
|
|
259
305
|
if(child?.exitCode===null) child.kill('SIGKILL');
|
|
260
306
|
try{const pid=Number((await readFile(fx.spawnedPid,'utf8')).trim());if(Number.isInteger(pid)&&pid>0) process.kill(pid,'SIGKILL');}catch{}
|
|
261
307
|
await sleep(100);
|
|
262
|
-
await
|
|
308
|
+
await safeRm(fx.dir);
|
|
263
309
|
}
|
|
264
310
|
});
|
|
265
311
|
|
|
@@ -267,12 +313,12 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
267
313
|
const fx=await fixture();
|
|
268
314
|
let child=null;
|
|
269
315
|
try{
|
|
270
|
-
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
316
|
+
child=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
271
317
|
const output=await collect(child);
|
|
272
318
|
const ready=await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=4);
|
|
273
319
|
assert.deepEqual(ready.slice(0,8),['status','start','status','status','armprocess','armcoding','armautonomous','status']);
|
|
274
320
|
assert.equal(child.exitCode,null,'remote session must remain foreground after becoming ready');
|
|
275
|
-
const tailDeadline=Date.now()+
|
|
321
|
+
const tailDeadline=Date.now()+fixtureTimeoutMs;
|
|
276
322
|
while(Date.now()<tailDeadline&&(!/\[AUTH\] process ARMED \| coding ARMED \| autonomous ARMED \| desktop DISARMED \| host DISARMED/.test(output.stdout)||
|
|
277
323
|
!/\[READY\] DEADBYTE MCP online/.test(output.stdout))) await sleep(50);
|
|
278
324
|
assert.match(output.stdout,/\[DISPLAY\] ASCII fallback: stdout_not_tty/);
|
|
@@ -306,7 +352,7 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
306
352
|
'[TOOL OUT] 2026-09-16T00:00:00.001Z #000001 coding_tree {"is_error":false}',
|
|
307
353
|
'[TOOL OUT] 2026-09-16T00:00:00.002Z #000002 coding_file_read {"is_error":true}'
|
|
308
354
|
].join('\n')+'\n','utf8');
|
|
309
|
-
const logDeadline=Date.now()+
|
|
355
|
+
const logDeadline=Date.now()+fixtureTimeoutMs;
|
|
310
356
|
while(Date.now()<logDeadline&&!/^R .*#4 \[fedcba98\] coding_file_read projects:src\/deadbyte-cli\.mjs 1\.73s \[SLOW\] ERR$/m.test(output.stdout)) await sleep(50);
|
|
311
357
|
assert.doesNotMatch(output.stdout,/\| Arguments:/,'default human view must not print request JSON');
|
|
312
358
|
assert.match(output.stdout,/^S .*#2 \[abcdef12\] coding_tree projects:src 42ms OK$/m);
|
|
@@ -326,7 +372,7 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
326
372
|
assert.doesNotMatch(output.stdout,/\[TOOL IN \]|\[TOOL OUT\]/);
|
|
327
373
|
assert.doesNotMatch(output.stdout,/payload bytes=/);
|
|
328
374
|
child.stdin.end();
|
|
329
|
-
const ended=await
|
|
375
|
+
const ended=await boundedCompletion(output.closed);
|
|
330
376
|
assert.equal(ended.timeout,undefined,'remote session did not exit after foreground input closed');
|
|
331
377
|
const commands=await traceCommands(fx.trace);
|
|
332
378
|
assert.deepEqual(commands.slice(-8),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','status','machinetest','park']);
|
|
@@ -337,7 +383,7 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
337
383
|
assert.doesNotMatch(output.stdout,/\p{Extended_Pictographic}/u,'piped Windows fallback must remain ASCII-safe');
|
|
338
384
|
}finally{
|
|
339
385
|
if(child?.exitCode===null) child.kill();
|
|
340
|
-
await
|
|
386
|
+
await safeRm(fx.dir);
|
|
341
387
|
}
|
|
342
388
|
});
|
|
343
389
|
|
|
@@ -374,7 +420,7 @@ winTest('remote --resume replays sanitized tool-call and terminal result events
|
|
|
374
420
|
deadline=Date.now()+3000;
|
|
375
421
|
while(Date.now()<deadline&&!/^R .*#4 \[resume-r\] coding_file_read projects:src\/deadbyte-cli\.mjs.*OK$/m.test(firstOutput.stdout)) await sleep(50);
|
|
376
422
|
first.stdin.end();
|
|
377
|
-
const firstEnded=await
|
|
423
|
+
const firstEnded=await boundedCompletion(firstOutput.closed);
|
|
378
424
|
assert.equal(firstEnded.timeout,undefined);
|
|
379
425
|
|
|
380
426
|
second=spawn(process.execPath,[cliPath,'remote','--resume',cursor1],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
@@ -395,12 +441,12 @@ winTest('remote --resume replays sanitized tool-call and terminal result events
|
|
|
395
441
|
const liveIndex=secondOutput.stdout.indexOf('[LOG] Human live timeline');
|
|
396
442
|
assert.ok(callReplayIndex>=0&&resultReplayIndex>callReplayIndex&&liveIndex>resultReplayIndex,'resume ordering must be call -> result -> live tail');
|
|
397
443
|
second.stdin.end();
|
|
398
|
-
const secondEnded=await
|
|
444
|
+
const secondEnded=await boundedCompletion(secondOutput.closed);
|
|
399
445
|
assert.equal(secondEnded.timeout,undefined);
|
|
400
446
|
assert.equal(secondEnded.code,0,secondOutput.stderr);
|
|
401
447
|
}finally{
|
|
402
448
|
if(first?.exitCode===null) first.kill();if(second?.exitCode===null) second.kill();
|
|
403
|
-
await
|
|
449
|
+
await safeRm(fx.dir);
|
|
404
450
|
}
|
|
405
451
|
});
|
|
406
452
|
|
|
@@ -424,14 +470,14 @@ winTest('remote --verbose enables sanitized request detail while preserving fore
|
|
|
424
470
|
assert.match(output.stdout,/coding_tree\s+\| Arguments: .*"path":"src".*"root_id":"projects"/);
|
|
425
471
|
assert.doesNotMatch(output.stdout,/DO_NOT_PRINT_VERBOSE_SECRET/);
|
|
426
472
|
child.stdin.end();
|
|
427
|
-
const ended=await
|
|
473
|
+
const ended=await boundedCompletion(output.closed);
|
|
428
474
|
assert.equal(ended.timeout,undefined);
|
|
429
475
|
assert.equal(ended.code,0,output.stderr);
|
|
430
476
|
const commands=await traceCommands(fx.trace);
|
|
431
477
|
assert.deepEqual(commands.slice(-8),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','status','machinetest','park']);
|
|
432
478
|
}finally{
|
|
433
479
|
if(child?.exitCode===null) child.kill('SIGKILL');
|
|
434
|
-
await
|
|
480
|
+
await safeRm(fx.dir);
|
|
435
481
|
}
|
|
436
482
|
});
|
|
437
483
|
|
|
@@ -439,7 +485,7 @@ winTest('detached guardian revokes authority, parks, and removes the session mar
|
|
|
439
485
|
const fx=await fixture();
|
|
440
486
|
let child=null;
|
|
441
487
|
try{
|
|
442
|
-
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
488
|
+
child=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
443
489
|
const output=await collect(child);
|
|
444
490
|
const ready=await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=4);
|
|
445
491
|
assert.deepEqual(ready.slice(0,8),['status','start','status','status','armprocess','armcoding','armautonomous','status']);
|
|
@@ -470,7 +516,7 @@ winTest('detached guardian revokes authority, parks, and removes the session mar
|
|
|
470
516
|
assert.equal(markerGone,true,'guardian left stale remote-session marker after hard kill');
|
|
471
517
|
}finally{
|
|
472
518
|
if(child?.exitCode===null) child.kill();
|
|
473
|
-
await
|
|
519
|
+
await safeRm(fx.dir);
|
|
474
520
|
}
|
|
475
521
|
});
|
|
476
522
|
|
|
@@ -481,12 +527,12 @@ winTest('guardian retains ownership marker when disarm exits zero but Host remai
|
|
|
481
527
|
try{
|
|
482
528
|
child=spawn(process.execPath,[cliPath,'remote','--full'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
483
529
|
const output=await collect(child);
|
|
484
|
-
const deadline=Date.now()+
|
|
530
|
+
const deadline=Date.now()+fixtureTimeoutMs;
|
|
485
531
|
while(Date.now()<deadline&&!/\[READY\]/.test(output.stdout)) await sleep(50);
|
|
486
532
|
assert.match(output.stdout,/\[READY\]/,output.stderr);
|
|
487
533
|
child.kill('SIGKILL');
|
|
488
534
|
await output.closed;
|
|
489
|
-
await waitFor(fx.trace,commands=>commands.includes('disarmhost'),{timeoutMs:
|
|
535
|
+
await waitFor(fx.trace,commands=>commands.includes('disarmhost'),{timeoutMs:fixtureTimeoutMs});
|
|
490
536
|
await sleep(750);
|
|
491
537
|
const marker=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-session.json'),'utf8'));
|
|
492
538
|
assert.equal(marker.schema,'deadbyte.remote-session.v2');
|
|
@@ -494,23 +540,23 @@ winTest('guardian retains ownership marker when disarm exits zero but Host remai
|
|
|
494
540
|
assert.ok(!commands.includes('park'),'guardian must not park after unverified revocation');
|
|
495
541
|
}finally{
|
|
496
542
|
if(child?.exitCode===null) child.kill('SIGKILL');
|
|
497
|
-
await
|
|
543
|
+
await safeRm(fx.dir);
|
|
498
544
|
}
|
|
499
545
|
});
|
|
500
546
|
|
|
501
547
|
winTest('bare remote rolls back authority and parks if one plane fails to arm',async()=>{
|
|
502
548
|
const fx=await fixture({failCommand:'armcoding'});
|
|
503
549
|
try{
|
|
504
|
-
const child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['ignore','pipe','pipe'],windowsHide:true});
|
|
550
|
+
const child=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['ignore','pipe','pipe'],windowsHide:true});
|
|
505
551
|
const output=await collect(child);
|
|
506
|
-
const ended=await
|
|
552
|
+
const ended=await boundedCompletion(output.closed);
|
|
507
553
|
assert.equal(ended.timeout,undefined,`failed startup did not terminate; stdout=${output.stdout.slice(-1200)} stderr=${output.stderr.slice(-1200)} trace=${(await traceCommands(fx.trace)).join(',')}`);
|
|
508
554
|
assert.notEqual(ended.code,0);
|
|
509
555
|
const commands=await traceCommands(fx.trace);
|
|
510
556
|
assert.deepEqual(commands.slice(0,6),['status','start','status','status','armprocess','armcoding']);
|
|
511
557
|
assert.deepEqual(commands.slice(-8),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','status','machinetest','park']);
|
|
512
558
|
assert.doesNotMatch(output.stdout,/Live logs/i);
|
|
513
|
-
}finally{await
|
|
559
|
+
}finally{await safeRm(fx.dir);}
|
|
514
560
|
});
|
|
515
561
|
|
|
516
562
|
winTest('bare remote recovers a stale crashed session before arming a new one',async()=>{
|
|
@@ -522,18 +568,18 @@ winTest('bare remote recovers a stale crashed session before arming a new one',a
|
|
|
522
568
|
await writeFile(path.join(runtime,'remote-session.json'),JSON.stringify({schema:'deadbyte.remote-session.v1',pid:2147483000,started_at_utc:'2026-01-01T00:00:00.000Z'}),'utf8');
|
|
523
569
|
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
524
570
|
const output=await collect(child);
|
|
525
|
-
const ready=await waitFor(fx.trace,commands=>commands.
|
|
571
|
+
const ready=await waitFor(fx.trace,commands=>commands.length>=10&&commands[9]==='start');
|
|
526
572
|
assert.deepEqual(ready.slice(0,8),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','status','machinetest','park']);
|
|
527
573
|
assert.equal(ready[8],'status');
|
|
528
574
|
assert.equal(ready[9],'start');
|
|
529
575
|
assert.match(output.stdout,/\[RECOVERY\] Stale remote session pid=2147483000; revoking authority before restart\./);
|
|
530
576
|
child.stdin.end();
|
|
531
|
-
const ended=await
|
|
577
|
+
const ended=await boundedCompletion(output.closed);
|
|
532
578
|
assert.equal(ended.timeout,undefined);
|
|
533
579
|
assert.equal(ended.code,0,output.stderr);
|
|
534
580
|
}finally{
|
|
535
581
|
if(child?.exitCode===null) child.kill();
|
|
536
|
-
await
|
|
582
|
+
await safeRm(fx.dir);
|
|
537
583
|
}
|
|
538
584
|
});
|
|
539
585
|
|
|
@@ -547,13 +593,13 @@ winTest('remote warning lines use deterministic ASCII fallback on piped Windows
|
|
|
547
593
|
const markerPath=path.join(fx.state,'runtime','remote-session.json');
|
|
548
594
|
await writeFile(markerPath,'{invalid-json\n','utf8');
|
|
549
595
|
child.stdin.end();
|
|
550
|
-
const ended=await
|
|
596
|
+
const ended=await boundedCompletion(output.closed);
|
|
551
597
|
assert.equal(ended.timeout,undefined,'remote session did not exit after warning fixture');
|
|
552
598
|
assert.match(output.stderr,/^\[WARN\] remote session marker cleanup:/m);
|
|
553
599
|
assert.doesNotMatch(output.stderr,/\p{Extended_Pictographic}/u);
|
|
554
600
|
}finally{
|
|
555
601
|
if(child?.exitCode===null) child.kill();
|
|
556
|
-
await
|
|
602
|
+
await safeRm(fx.dir);
|
|
557
603
|
}
|
|
558
604
|
});
|
|
559
605
|
|
|
@@ -561,7 +607,7 @@ winTest('R58 concurrent cold remote claims have one winner and no loser cleanup'
|
|
|
561
607
|
const fx=await fixture({statusOnline:true});const children=[],outputs=[];
|
|
562
608
|
try{
|
|
563
609
|
for(let i=0;i<3;i++){
|
|
564
|
-
const child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
610
|
+
const child=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
565
611
|
children.push(child);outputs.push(await collect(child));
|
|
566
612
|
}
|
|
567
613
|
const commands=await waitFor(fx.trace,items=>items.filter(x=>x==='status').length>=3);
|
|
@@ -579,18 +625,18 @@ winTest('R58 concurrent cold remote claims have one winner and no loser cleanup'
|
|
|
579
625
|
live[0].stdin.end();assert.equal((await outputs[children.indexOf(live[0])].closed).code,0);
|
|
580
626
|
}finally{
|
|
581
627
|
for(const child of children)if(child.exitCode===null)child.kill();
|
|
582
|
-
await
|
|
628
|
+
await safeRm(fx.dir);
|
|
583
629
|
}
|
|
584
630
|
});
|
|
585
631
|
|
|
586
632
|
winTest('R58 duplicate remote verifies the real live owner and leaves its authority untouched',async()=>{
|
|
587
633
|
const fx=await fixture({statusOnline:true});let first=null,second=null;
|
|
588
634
|
try{
|
|
589
|
-
first=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
635
|
+
first=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
590
636
|
const firstOutput=await collect(first);
|
|
591
637
|
const commands=await waitFor(fx.trace,items=>items.filter(x=>x==='status').length>=3);
|
|
592
638
|
assert.ok(commands.filter(x=>x==='status').length>=3,firstOutput.stderr);
|
|
593
|
-
const ownerReadyDeadline=Date.now()+
|
|
639
|
+
const ownerReadyDeadline=Date.now()+fixtureTimeoutMs;
|
|
594
640
|
while(Date.now()<ownerReadyDeadline&&!/\[READY\]/.test(firstOutput.stdout)) await sleep(50);
|
|
595
641
|
assert.match(firstOutput.stdout,/\[READY\]/,firstOutput.stderr);
|
|
596
642
|
const before=await traceCommands(fx.trace);
|
|
@@ -598,7 +644,7 @@ winTest('R58 duplicate remote verifies the real live owner and leaves its author
|
|
|
598
644
|
const marker=JSON.parse(await readFile(markerPath,'utf8'));
|
|
599
645
|
assert.equal(marker.owner_identity.pid,first.pid);
|
|
600
646
|
assert.equal(marker.guardian_identity.parent_pid,first.pid);
|
|
601
|
-
second=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
647
|
+
second=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
602
648
|
const secondOutput=await collect(second);
|
|
603
649
|
const stopped=await secondOutput.closed;
|
|
604
650
|
assert.notEqual(stopped.code,0);
|
|
@@ -609,7 +655,7 @@ winTest('R58 duplicate remote verifies the real live owner and leaves its author
|
|
|
609
655
|
first.stdin.end();assert.equal((await firstOutput.closed).code,0,firstOutput.stderr);
|
|
610
656
|
}finally{
|
|
611
657
|
if(first?.exitCode===null)first.kill();if(second?.exitCode===null)second.kill();
|
|
612
|
-
await
|
|
658
|
+
await safeRm(fx.dir);
|
|
613
659
|
}
|
|
614
660
|
});
|
|
615
661
|
|
|
@@ -624,14 +670,14 @@ winTest('bare remote refuses unverified legacy ownership without touching author
|
|
|
624
670
|
const output=await collect(child);
|
|
625
671
|
await sleep(500);
|
|
626
672
|
if(child.exitCode===null) child.stdin.end();
|
|
627
|
-
const ended=await
|
|
673
|
+
const ended=await boundedCompletion(output.closed);
|
|
628
674
|
assert.equal(ended.timeout,undefined);
|
|
629
675
|
assert.notEqual(ended.code,0);
|
|
630
676
|
assert.deepEqual(await traceCommands(fx.trace),[]);
|
|
631
677
|
assert.match(output.stderr,/remote session ownership unverified/i);
|
|
632
678
|
}finally{
|
|
633
679
|
if(child?.exitCode===null) child.kill();
|
|
634
|
-
await
|
|
680
|
+
await safeRm(fx.dir);
|
|
635
681
|
}
|
|
636
682
|
});
|
|
637
683
|
|
|
@@ -640,11 +686,11 @@ winTest('explicit remote status remains a one-shot controller command',async()=>
|
|
|
640
686
|
try{
|
|
641
687
|
const child=spawn(process.execPath,[cliPath,'remote','status'],{env:fx.env,stdio:['ignore','pipe','pipe'],windowsHide:true});
|
|
642
688
|
const output=await collect(child);
|
|
643
|
-
const ended=await
|
|
689
|
+
const ended=await boundedCompletion(output.closed);
|
|
644
690
|
assert.equal(ended.timeout,undefined);
|
|
645
691
|
assert.equal(ended.code,0,output.stderr);
|
|
646
692
|
assert.deepEqual(await traceCommands(fx.trace),['status']);
|
|
647
|
-
}finally{await
|
|
693
|
+
}finally{await safeRm(fx.dir);}
|
|
648
694
|
});
|
|
649
695
|
|
|
650
696
|
winTest('explicit remote status keeps the coherent runtime one-shot and silent',async()=>{
|
|
@@ -659,12 +705,12 @@ winTest('explicit remote status keeps the coherent runtime one-shot and silent',
|
|
|
659
705
|
}),'utf8');
|
|
660
706
|
const child=spawn(process.execPath,[cliPath,'remote','status'],{env:fx.env,stdio:['ignore','pipe','pipe'],windowsHide:true});
|
|
661
707
|
const output=await collect(child);
|
|
662
|
-
const ended=await
|
|
708
|
+
const ended=await boundedCompletion(output.closed);
|
|
663
709
|
assert.equal(ended.timeout,undefined);
|
|
664
710
|
assert.equal(ended.code,0,output.stderr);
|
|
665
711
|
assert.doesNotMatch(output.stderr,/VERSION SKEW/);
|
|
666
712
|
assert.deepEqual(await traceCommands(fx.trace),['status']);
|
|
667
|
-
}finally{await
|
|
713
|
+
}finally{await safeRm(fx.dir);}
|
|
668
714
|
});
|
|
669
715
|
|
|
670
716
|
winTest('bare remote surfaces bounded controller-start-failure evidence instead of only exit code',async()=>{
|
|
@@ -672,12 +718,12 @@ winTest('bare remote surfaces bounded controller-start-failure evidence instead
|
|
|
672
718
|
try{
|
|
673
719
|
const child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['ignore','pipe','pipe'],windowsHide:true});
|
|
674
720
|
const output=await collect(child);
|
|
675
|
-
const ended=await
|
|
721
|
+
const ended=await boundedCompletion(output.closed);
|
|
676
722
|
assert.equal(ended.timeout,undefined,'failed start did not terminate');
|
|
677
723
|
assert.notEqual(ended.code,0);
|
|
678
724
|
assert.match(output.stderr,/fixture controller evidence detail/);
|
|
679
725
|
assert.match(output.stderr,/controller-start-failure/i);
|
|
680
|
-
}finally{await
|
|
726
|
+
}finally{await safeRm(fx.dir);}
|
|
681
727
|
});
|
|
682
728
|
|
|
683
729
|
|
|
@@ -711,11 +757,11 @@ winTest('remote --trace --filter selects presentation without dropping journaled
|
|
|
711
757
|
const journal=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-results',marker.result_session_id+'.json'),'utf8'));
|
|
712
758
|
assert.equal(journal.events.length,4,'presentation filter must not remove journal evidence');
|
|
713
759
|
child.stdin.end();
|
|
714
|
-
const ended=await
|
|
760
|
+
const ended=await boundedCompletion(output.closed);
|
|
715
761
|
assert.equal(ended.timeout,undefined);
|
|
716
762
|
assert.equal(ended.code,0,output.stderr);
|
|
717
763
|
}finally{
|
|
718
764
|
if(child?.exitCode===null) child.kill('SIGKILL');
|
|
719
|
-
await
|
|
765
|
+
await safeRm(fx.dir);
|
|
720
766
|
}
|
|
721
767
|
});
|