deadbyte-mcp 0.15.36 → 0.15.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/MANIFEST.SHA256 +70 -32
  2. package/PROVENANCE.json +127 -87
  3. package/README.txt +12 -12
  4. package/RELEASE.json +226 -187
  5. package/SBOM.cdx.json +10 -25
  6. package/TEST-MATRIX.json +305 -258
  7. package/bridge/package.json +1 -1
  8. package/bridge/src/bridge-health-r59.mjs +4 -3
  9. package/bridge/src/bridge.mjs +75 -18
  10. package/bridge/src/canonical-json-r60.mjs +13 -0
  11. package/bridge/src/r40-governor.mjs +109 -19
  12. package/bridge/src/reconnect-delay-r60.mjs +31 -0
  13. package/bridge/src/remote-call-claims-r60.mjs +157 -0
  14. package/bridge/src/remote-dispatch-r60.mjs +75 -0
  15. package/controller/deadbyte-controller.ps1 +6 -1
  16. package/controller/deadbyte-process-policy.json +4 -4
  17. package/docs/R59-PROVENANCE.json +87 -0
  18. package/docs/R59-RELEASE.json +187 -0
  19. package/docs/R59-SBOM.cdx.json +68 -0
  20. package/docs/R59-TEST-MATRIX.json +258 -0
  21. package/docs/R59-bridge-package.json +13 -0
  22. package/docs/R59-package-lock.json +184 -0
  23. package/docs/R59-package.json +106 -0
  24. package/docs/R59-version.mjs +6 -0
  25. package/docs/R60-TASK-CONTRACT.md +22 -0
  26. package/docs/R61-TASK-CONTRACT.md +11 -0
  27. package/docs/r61-reference-evidence.md +18 -0
  28. package/package.json +6 -3
  29. package/scripts/full-regression-r60.mjs +32 -0
  30. package/scripts/gate-windows-r60-prepublish.ps1 +74 -0
  31. package/scripts/gate-windows-r61-prepublish.ps1 +45 -0
  32. package/scripts/npm-artifact-identity-r61.mjs +22 -0
  33. package/scripts/npm-release-r48.mjs +21 -5
  34. package/scripts/r61-mixed-lane-benchmark.mjs +98 -0
  35. package/src/capability-snapshot.mjs +6 -2
  36. package/src/deadbyte-cli.mjs +89 -40
  37. package/src/remote-authority.mjs +56 -0
  38. package/src/remote-enforcement-probe-r60.mjs +17 -0
  39. package/src/remote-live-log.mjs +47 -7
  40. package/src/remote-log-decode-r60.mjs +32 -0
  41. package/src/remote-mcp-machine-smoke-client.mjs +19 -1
  42. package/src/remote-session-guardian.mjs +20 -3
  43. package/src/runtime-update.mjs +13 -1
  44. package/src/tool-audit.mjs +5 -0
  45. package/src/version.mjs +1 -1
  46. package/test/bridge-runtime-r59.test.mjs +5 -5
  47. package/test/commander-schema-differential-r48.test.mjs +1 -1
  48. package/test/r48-release-contract.test.mjs +1 -1
  49. package/test/r51-runtime-coherence.test.mjs +2 -2
  50. package/test/r58-cleanup-retention.test.mjs +9 -3
  51. package/test/r58-concurrent-recovery.test.mjs +9 -3
  52. package/test/r58-controller-live-local.test.mjs +22 -3
  53. package/test/r58-stale-owner.test.mjs +2 -2
  54. package/test/r59-release-contract.test.mjs +6 -6
  55. package/test/r60-authority-coherence.test.mjs +36 -0
  56. package/test/r60-authority-enforcement.test.mjs +28 -0
  57. package/test/r60-bridge-dispatch.test.mjs +119 -0
  58. package/test/r60-clock-discipline.test.mjs +15 -0
  59. package/test/r60-live-timeline.test.mjs +29 -0
  60. package/test/r60-reconnect-errors.test.mjs +21 -0
  61. package/test/r60-reconnect-jitter.test.mjs +18 -0
  62. package/test/r60-release-contract.test.mjs +82 -0
  63. package/test/r60-remote-call-recovery.test.mjs +170 -0
  64. package/test/r60-windows-log-decode.test.mjs +25 -0
  65. package/test/r61-control-lane.test.mjs +154 -0
  66. package/test/r61-live-timeline-bounds.test.mjs +31 -0
  67. package/test/r61-npm-artifact-identity.test.mjs +17 -0
  68. package/test/r61-release-bridge-binding.test.mjs +32 -0
  69. package/test/r61-release-contract.test.mjs +44 -0
  70. package/test/remote-live-log-r37.test.mjs +24 -7
  71. package/test/remote-session-cli.test.mjs +200 -43
@@ -394,6 +394,18 @@ export async function prepareBridgeSpec(config,bootstrap){
394
394
  return next;
395
395
  }
396
396
 
397
+ export async function prepareReleaseBridgeSpec(config,bootstrap,releaseRoot){
398
+ if(!bootstrap) return structuredClone(config);
399
+ if(typeof releaseRoot!=='string'||!path.isAbsolute(releaseRoot))throw new Error('immutable release root required for Bridge binding');
400
+ // Verify the requested development input, then bind runtime paths only to
401
+ // the manifest-verified immutable slot produced from that same candidate.
402
+ const verifiedSource=await prepareBridgeSpec(config,bootstrap);
403
+ return prepareBridgeSpec(verifiedSource,{
404
+ root:path.join(path.resolve(releaseRoot),'bridge'),
405
+ scriptSha256:verifiedSource.Bridge.ScriptSha256
406
+ });
407
+ }
408
+
397
409
  export async function prepareAutonomousSupervisorSpec(config,{releaseRoot,enable=false}={}){
398
410
  const next=structuredClone(config);
399
411
  if(!enable) return next;
@@ -1682,7 +1694,7 @@ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,e
1682
1694
  if(!control.desktopPolicy) throw new Error('DesktopRuntimeEnabled requires pinned desktop policy bootstrap or existing policy');
1683
1695
  control.config.DesktopRuntimeEnabled=true;
1684
1696
  }
1685
- control.config=await prepareBridgeSpec(control.config,bridgeBootstrap);
1697
+ control.config=await prepareReleaseBridgeSpec(control.config,bridgeBootstrap,slot.release_root);
1686
1698
  control.config=await prepareNamedTunnelCloudSpec(control.config,{releaseRoot:slot.release_root,bootstrap:cloudTransport});
1687
1699
  control.config=await prepareAutonomousSupervisorSpec(control.config,{releaseRoot:slot.release_root,enable:enableAutonomousSupervisor});
1688
1700
  authorityPreparation=await prepareAuthorityPolicies({hostPolicy:control.hostPolicy,codingPolicy:control.codingPolicy},{commandPins:hostCommandPins});
@@ -116,7 +116,12 @@ function summarizeOutcome(result,error,durationMs){
116
116
  const structured=result?.structuredContent;
117
117
  const status=typeof structured?.status==='string'?structured.status:(result?.isError===true?'error':'ok');
118
118
  const isError=result?.isError===true;
119
+ const capability=structured?.result;
120
+ const snapshot=capability?.schema==='deadbyte.capability-manifest.v3'&&HEX64.test(String(capability.snapshot_id??''))&&
121
+ Number.isInteger(capability.state_epoch)&&capability.state_epoch>0
122
+ ?{snapshot_id:capability.snapshot_id,state_epoch:capability.state_epoch}:{};
119
123
  return {status:String(status).slice(0,128),is_error:isError,duration_ms:duration,receipt_sha256s:outcomeReceiptHashes(result),
124
+ ...snapshot,
120
125
  ...(isError?{reason:resultOutcomeReason(result)}:{})};
121
126
  }
122
127
 
package/src/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export const DEADBYTE_VERSION = '0.15.36';
1
+ export const DEADBYTE_VERSION = '0.15.38';
2
2
  export const MCP_PROTOCOL_VERSION = '2026-07-28';
3
3
  export const RECEIPT_SCHEMA_V1 = 'deadbyte.receipt.v1';
4
4
  export const RECEIPT_SCHEMA_V2 = 'deadbyte.receipt.v2';
@@ -12,21 +12,21 @@ const limits={
12
12
 
13
13
  test('R59 bridge governor removes cancelled queued work and drains without replay',async()=>{
14
14
  const governor=createBridgeGovernor({limits});
15
- const first=await governor.acquire('machine_status');
15
+ const first=await governor.acquire('coding_file_read');
16
16
  const abort=new AbortController();
17
- const queued=governor.acquire('machine_ping',{signal:abort.signal});
17
+ const queued=governor.acquire('coding_file_read',{signal:abort.signal});
18
18
  abort.abort(new Error('caller disconnected'));
19
19
  await assert.rejects(queued,/caller disconnected|aborted/);
20
20
  assert.equal(governor.snapshot().normal.queued,0);
21
21
  assert.equal(governor.snapshot().normal.cancelled,1);
22
- const queuedDuringRecovery=governor.acquire('machine_ping');
22
+ const queuedDuringRecovery=governor.acquire('coding_file_read');
23
23
  assert.equal(governor.reconcile('core generation changed'),1);
24
24
  await assert.rejects(queuedDuringRecovery,/core generation changed/);
25
25
  first.release();
26
- const after=await governor.acquire('machine_status');
26
+ const after=await governor.acquire('coding_file_read');
27
27
  after.release();
28
28
  const drain=governor.close('bridge reconciling');
29
- await assert.rejects(governor.acquire('machine_status'),/closed/);
29
+ await assert.rejects(governor.acquire('coding_file_read'),/closed/);
30
30
  await drain;
31
31
  assert.equal(governor.snapshot().normal.active,0);
32
32
  });
@@ -5,7 +5,7 @@ import {readFile} from 'node:fs/promises';
5
5
  import {createCommanderWireAdapter} from '../src/commander-wire-adapter-r47.mjs';
6
6
  import {registerCommanderExtendedBridge} from '../src/commander-extended-bridge-r47.mjs';
7
7
 
8
- const referencePath='D:\\Projects\\desktop-commander-stage-20260922\\desktop-commander\\dist\\tools\\schemas.js';
8
+ const referencePath='D:\\Projects\\deadbyte-mcp\\references\\desktop-commander\\dist\\tools\\schemas.js';
9
9
  const expectedHash='f342345e34e48769dabaefe1b932f09d41e2e207b422f7794a15ee50ffa8f140';
10
10
  async function referenceSchemas(){
11
11
  let source=await readFile(referencePath,'utf8');
@@ -5,7 +5,7 @@ import { DEADBYTE_VERSION } from '../src/version.mjs';
5
5
 
6
6
  const root = new URL('../', import.meta.url);
7
7
  const read = rel => readFile(new URL(rel, root), 'utf8');
8
- const json = rel => read(rel).then(JSON.parse);
8
+ const json = rel => read(rel).then(raw => JSON.parse(raw.replace(/^\uFEFF/,'')));
9
9
 
10
10
  test('R48 compatibility release contract remains coherent in the current successor', async () => {
11
11
  const [pkg, lock, release, provenance, matrix] = await Promise.all([
@@ -53,7 +53,7 @@ test('R51 bare remote wires coherence before live generation binding and deploy
53
53
  test('R51 foreground authority contract explicitly verifies and cleans desktop plus host',async()=>{
54
54
  const {readFile}=await import('node:fs/promises');
55
55
  const cli=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
56
- assert.match(cli,/\['desktopstatus',\/\^Desktop plane/);
56
+ assert.match(cli,/assertAuthorityVector\(assertRuntimeReadyObservation\(observedAuthority/);
57
57
  assert.match(cli,/SESSION_CLEANUP=.*disarmdesktop.*disarmhost/s);
58
- assert.match(cli,/desktop, host DISARMED/);
58
+ assert.match(cli,/authorityBanner\(authority\)/);
59
59
  });
@@ -11,7 +11,12 @@ test('R58 foreground cleanup failure retains ownership evidence for recovery', {
11
11
  await mkdir(base,{recursive:true});
12
12
  const root=await mkdtemp(path.join(base,'cleanup-'));
13
13
  await mkdir(path.join(root,'bin'));
14
- await writeFile(path.join(root,'bin','DEADBYTE-MCP.ps1'),`param([string]$Command)
14
+ await writeFile(path.join(root,'bin','DEADBYTE-MCP.ps1'),`param([string]$Command,[switch]$Json)
15
+ if ($Command -eq 'status' -and $Json) {
16
+ $on=@{configured=$true;armed=$true;reason='standing-grant'}; $off=@{configured=$true;armed=$false;reason='disarmed'}
17
+ @{schema='deadbyte.controller-observation.v1';state='ONLINE';core_root='fixture';local_ready=$true;bridge=@{alive=$true;reason='verified'};core=@{alive=$true;reason='verified'};cloud=@{alive=$true;reason='verified'};supervisor=@{running=$true};authority=@{process=$on;coding=$on;autonomous=$on;desktop=$off;host=$off}} | ConvertTo-Json -Depth 5 -Compress
18
+ exit 0
19
+ }
15
20
  switch ($Command) {
16
21
  'status' { Write-Output 'DEADBYTE MCP = ONLINE'; Write-Output 'Bridge PID : 1001 [verified]'; Write-Output 'Cloud PID : 1002 [verified]'; Write-Output 'Local ready : True'; Write-Output 'Loop supervisor: RUNNING [verified]' }
17
22
  'processstatus' { Write-Output 'Process plane : ARMED [standing-grant]' }
@@ -30,8 +35,9 @@ exit 0
30
35
  const closed=new Promise(resolve=>child.once('close',code=>resolve(code)));
31
36
  try {
32
37
  const deadline=Date.now()+12000;
33
- while(!stdout.includes('ARMED: process, coding, autonomous')&&child.exitCode===null&&Date.now()<deadline) await delay(50);
34
- assert.match(stdout,/ARMED: process, coding, autonomous/,stderr);
38
+ while(!stdout.includes('[READY]')&&child.exitCode===null&&Date.now()<deadline) await delay(50);
39
+ assert.match(stdout,/\[READY\]/,stderr);
40
+ assert.match(stdout,/\[AUTH\] process ARMED \| coding ARMED \| autonomous ARMED \| desktop DISARMED \| host DISARMED/,stderr);
35
41
  child.stdin.end();
36
42
  assert.notEqual(await closed,0,'cleanup failure must not report a successful foreground exit');
37
43
  assert.match(stderr,/cleanup disarmprocess exit=9/);
@@ -11,9 +11,15 @@ test('R58 simultaneous stale recovery cannot both revoke the shared authority',
11
11
  const root=await mkdtemp(path.join(base,'concurrent-recovery-'));await mkdir(path.join(root,'bin'));await mkdir(path.join(root,'runtime'));
12
12
  const trace=path.join(root,'trace.txt'),markerPath=path.join(root,'runtime','remote-session.json');
13
13
  await writeFile(markerPath,JSON.stringify({schema:'deadbyte.remote-session.v1',pid:2147483000,started_at_utc:'2000-01-01T00:00:00.000Z'}));
14
- await writeFile(path.join(root,'bin','DEADBYTE-MCP.ps1'),`param([string]$Command)
14
+ await writeFile(path.join(root,'bin','DEADBYTE-MCP.ps1'),`param([string]$Command,[switch]$Json)
15
15
  $ErrorActionPreference='Stop'
16
16
  for($i=0;$i -lt 40;$i++){try{[IO.File]::AppendAllText('${trace.replaceAll("'","''")}',$Command+[Environment]::NewLine);break}catch{if($i -eq 39){throw};Start-Sleep -Milliseconds 10}}
17
+ if ($Command -match '^(arm|disarm)(process|coding|autonomous|desktop|host)$') { $file=Join-Path $env:DEADBYTE_STATE_ROOT ('runtime/' + $Matches[2] + '.armed'); if ($Matches[1] -eq 'arm') { [IO.File]::WriteAllText($file,'1') } else { Remove-Item -LiteralPath $file -Force -ErrorAction SilentlyContinue } }
18
+ if ($Command -eq 'status' -and $Json) {
19
+ $authority=@{};foreach($name in @('process','coding','autonomous','desktop','host')) { $armed=Test-Path -LiteralPath (Join-Path $env:DEADBYTE_STATE_ROOT ('runtime/' + $name + '.armed'));$authority[$name]=@{configured=$true;armed=[bool]$armed;reason=$(if($armed){'standing-grant'}else{'disarmed'})} }
20
+ @{schema='deadbyte.controller-observation.v1';state='ONLINE';core_root='fixture';local_ready=$true;bridge=@{alive=$true;reason='verified'};core=@{alive=$true;reason='verified'};cloud=@{alive=$true;reason='verified'};supervisor=@{running=$true};authority=$authority} | ConvertTo-Json -Depth 5 -Compress
21
+ exit 0
22
+ }
17
23
  switch($Command){
18
24
  'status' {Write-Output 'DEADBYTE MCP = ONLINE';Write-Output 'Bridge PID : 1001 [verified]';Write-Output 'Cloud PID : 1002 [verified]';Write-Output 'Local ready : True';Write-Output 'Loop supervisor: RUNNING [verified]'}
19
25
  'processstatus' {Write-Output 'Process plane : ARMED [standing-grant]'}
@@ -42,9 +48,9 @@ exit 0
42
48
  const o={stdout:'',stderr:''};c.stdout.on('data',b=>{o.stdout+=b;});c.stderr.on('data',b=>{o.stderr+=b;});o.exit=new Promise(resolve=>c.once('exit',resolve));children.push(c);outputs.push(o);
43
49
  }
44
50
  const until=Date.now()+12000;let commands=[];
45
- while(Date.now()<until){try{commands=(await readFile(trace,'utf8')).trim().split(/\r?\n/);}catch{}if(commands.includes('hoststatus'))break;await delay(50);}
51
+ while(Date.now()<until){try{commands=(await readFile(trace,'utf8')).trim().split(/\r?\n/);}catch{}if(outputs.some(o=>o.stdout.includes('[READY]')))break;await delay(50);}
46
52
  await writeFile(path.join(root,'observation.json'),JSON.stringify({commands,outputs:outputs.map(o=>({stdout:o.stdout,stderr:o.stderr})),simulation:'concurrent stale marker recovery with delayed second park'}),{flag:'wx'});
47
- assert.ok(commands.includes('hoststatus'),'a recovered foreground must become ready');
53
+ assert.ok(outputs.some(o=>o.stdout.includes('[READY]')),'a recovered foreground must become ready');
48
54
  for(const cmd of ['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost'])assert.equal(commands.filter(x=>x===cmd).length,1,`stale recovery must serialize ${cmd}`);
49
55
  }finally{
50
56
  for(const c of children)if(c.exitCode===null)c.stdin.end();
@@ -1,6 +1,6 @@
1
1
  import test from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
- import {mkdir,cp,readFile,writeFile} from 'node:fs/promises';
3
+ import {mkdir,cp,readFile,readdir,writeFile} from 'node:fs/promises';
4
4
  import {spawnSync} from 'node:child_process';
5
5
  import {createServer} from 'node:net';
6
6
  import {createHash,randomBytes,generateKeyPairSync} from 'node:crypto';
@@ -8,6 +8,7 @@ import path from 'node:path';
8
8
  import {makeDoctorCliFixture} from './helpers/r58-doctor-fixture.mjs';
9
9
  import {prepareBridgeSpec,windowsPowerShellEnvironment} from '../src/runtime-update.mjs';
10
10
  import {queryForegroundIdentity as queryForegroundProcessIdentity,foregroundIdentityMatches as sameForegroundProcessIdentity} from '../src/foreground-process-identity.mjs';
11
+ import {Client,StreamableHTTPClientTransport} from '@modelcontextprotocol/client';
11
12
 
12
13
  const sha=b=>createHash('sha256').update(b).digest('hex');
13
14
  const quote=s=>"'"+s.replaceAll("'","''")+"'";
@@ -31,7 +32,7 @@ test('R58 actual isolated controller/bridge/core reaches ONLINE and stops withou
31
32
  const bridgeScript=path.join(bridgeRoot,'src','bridge.mjs'),nodeHash=sha(await readFile(process.execPath));
32
33
  const preparedBridge=await prepareBridgeSpec({Bridge:{FilePath:process.execPath,FileSha256:nodeHash,Arguments:'src\\bridge.mjs',WorkingDirectory:bridgeRoot},Cloud:{}},{root:bridgeRoot,scriptSha256:sha(await readFile(bridgeScript))});
33
34
  const dependencies=preparedBridge.Bridge.Dependencies.map(dependency=>`@{Name=${quote(dependency.Name)};Path=${quote(dependency.Path)};Sha256='${dependency.Sha256}'}`);
34
- assert.deepEqual(preparedBridge.Bridge.Dependencies.map(x=>x.Name),['bridge-health-r59.mjs','node-http-adapter.mjs','public-tool-definitions.mjs','r40-governor.mjs']);
35
+ assert.deepEqual(preparedBridge.Bridge.Dependencies.map(x=>x.Name),['bridge-health-r59.mjs','canonical-json-r60.mjs','node-http-adapter.mjs','public-tool-definitions.mjs','r40-governor.mjs','reconnect-delay-r60.mjs','remote-call-claims-r60.mjs','remote-dispatch-r60.mjs']);
35
36
  const config=path.join(f.root,'controller-config.psd1'),runtime=path.join(f.state,'runtime');
36
37
  await writeFile(config,`@{ Schema='deadbyte.controller.v1'; CoreRoot=${quote(f.pkg)}; WorkspaceRoot=${quote(workspace)}; Bridge=@{FilePath=${quote(process.execPath)};FileSha256='${nodeHash}';Arguments='src\\bridge.mjs';WorkingDirectory=${quote(bridgeRoot)};ScriptPath=${quote(bridgeScript)};ScriptSha256='${sha(await readFile(bridgeScript))}';Dependencies=@(${dependencies.join(';')})}; Cloud=@{FilePath=${quote(process.execPath)};FileSha256='${nodeHash}';Arguments=${quote('"'+cloud+'"')};WorkingDirectory=${quote(f.root)};Dependencies=@(@{Name='fixture-cloud';Path=${quote(cloud)};Sha256='${sha(await readFile(cloud))}'})}; MachineRuntimeEnabled=$false;AgentRuntimeEnabled=$false;ProcessRuntimeEnabled=$false;ProcessPolicyFile='';CodingRuntimeEnabled=$false;CodingPolicyFile='';MachineFsRuntimeEnabled=$false;ObservationRuntimeEnabled=$false;DesktopRuntimeEnabled=$false;DesktopPolicyFile='';AutonomousRuntimeEnabled=$false;AutonomousSupervisorEnabled=$false;AutonomousPolicyFile='';HostPolicyFile='';LocalReadyUri='http://127.0.0.1:${port}/healthz';UrlRegex='';McpUrlFile=${quote(url)};McpPathFile='';StartTimeoutSeconds=15;ProbeTimeoutSeconds=1 }`);
37
38
  // Controller's existing audit mode requires machine runtime. It is observation
@@ -61,7 +62,8 @@ test('R58 actual isolated controller/bridge/core reaches ONLINE and stops withou
61
62
  launcher_sha256:'0'.repeat(64),
62
63
  config_path:generationConfig
63
64
  },null,2)+'\n');
64
- const env=windowsPowerShellEnvironment({...f.env,DEADBYTE_CONTROLLER_CONFIG:config,DEADBYTE_CONTROLLER_RUNTIME_ROOT:runtime,DEADBYTE_BRIDGE_PORT:String(port),DEADBYTE_BRIDGE_PATH_TOKEN:randomBytes(32).toString('hex')});
65
+ const bridgeToken=randomBytes(32).toString('hex');
66
+ const env=windowsPowerShellEnvironment({...f.env,DEADBYTE_CONTROLLER_CONFIG:config,DEADBYTE_CONTROLLER_RUNTIME_ROOT:runtime,DEADBYTE_BRIDGE_PORT:String(port),DEADBYTE_BRIDGE_PATH_TOKEN:bridgeToken});
65
67
  const run=(command,json=false)=>spawnSync(path.join(process.env.SystemRoot,'System32/WindowsPowerShell/v1.0/powershell.exe'),['-NoProfile','-File',controller,command,...(json?['-Json']:[])],{env,encoding:'utf8',timeout:45000});
66
68
  let coreIdentity=null;
67
69
  try{
@@ -70,6 +72,17 @@ test('R58 actual isolated controller/bridge/core reaches ONLINE and stops withou
70
72
  const status=run('status',true);assert.equal(status.status,0,status.stderr);const observed=JSON.parse(status.stdout);
71
73
  assert.equal(observed.state,'ONLINE');assert.equal(observed.local_ready,true);assert.equal(observed.core.alive,true);assert.equal(observed.core.parent_pid,observed.bridge.pid);
72
74
  for(const plane of Object.values(observed.authority))assert.equal(plane.armed,false);
75
+ const publicClient=new Client({name:'r60-isolated-ingress',version:'0.15.37'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
76
+ try{
77
+ await publicClient.connect(new StreamableHTTPClientTransport(new URL(`http://127.0.0.1:${port}/mcp/${bridgeToken}`)));
78
+ const publicStatus=await publicClient.callTool({name:'machine_status',arguments:{}});
79
+ assert.equal(publicStatus.structuredContent?.status,'ok','actual Bridge ingress must execute through the guarded dispatch');
80
+ const claimRoot=path.join(f.state,'runtime','remote-call-claims');
81
+ const shards=await readdir(claimRoot);
82
+ assert.equal(shards.length,1,'public ingress must create a durable claim');
83
+ const records=await readdir(path.join(claimRoot,shards[0]));
84
+ assert.equal(records.filter(name=>name.endsWith('.terminal.json')).length,1,'Core result must publish a terminal record');
85
+ }finally{await publicClient.close().catch(()=>{});}
73
86
  const firstCore=await queryForegroundProcessIdentity(observed.core.pid);assert.ok(firstCore);
74
87
  process.kill(firstCore.pid);
75
88
  let recovered=null;
@@ -83,6 +96,12 @@ test('R58 actual isolated controller/bridge/core reaches ONLINE and stops withou
83
96
  }
84
97
  assert.ok(recovered,'bridge did not recover a fresh Core within bounded restart budget');
85
98
  assert.equal(recovered.core.parent_pid,recovered.bridge.pid);
99
+ const reattached=new Client({name:'r60-recovered-execution',version:'0.15.37'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
100
+ try{
101
+ await reattached.connect(new StreamableHTTPClientTransport(new URL(`http://127.0.0.1:${port}/mcp/${bridgeToken}`)));
102
+ const proof=await reattached.callTool({name:'machine_status',arguments:{}});
103
+ assert.equal(proof.structuredContent?.status,'ok','recovered Core must execute a bounded MCP call');
104
+ }finally{await reattached.close().catch(()=>{});}
86
105
  coreIdentity=await queryForegroundProcessIdentity(recovered.core.pid);assert.ok(coreIdentity);
87
106
  const stopped=run('stop');assert.equal(stopped.status,0,stopped.stderr);
88
107
  const terminal=run('status',true);assert.equal(terminal.status,0,terminal.stderr);const end=JSON.parse(terminal.stdout);
@@ -51,7 +51,7 @@ test('R58 bound stale birth identity reconciles on the first invocation without
51
51
  await mkdir(base,{recursive:true});const root=await mkdtemp(path.join(base,'stale-birth-'));
52
52
  await mkdir(path.join(root,'runtime'));await mkdir(path.join(root,'bin'));
53
53
  const trace=path.join(root,'trace.txt');
54
- await writeFile(path.join(root,'bin','DEADBYTE-MCP.ps1'),`param([string]$Command)\n[IO.File]::AppendAllText('${trace.replaceAll("'","''")}',$Command+[Environment]::NewLine)\nexit 0\n`);
54
+ await writeFile(path.join(root,'bin','DEADBYTE-MCP.ps1'),`param([string]$Command,[switch]$Json)\n[IO.File]::AppendAllText('${trace.replaceAll("'","''")}',$Command+[Environment]::NewLine)\nif ($Command -eq 'status' -and $Json) { $off=@{configured=$true;armed=$false;reason='disarmed'}; @{schema='deadbyte.controller-observation.v1';state='OFFLINE';authority=@{process=$off;coding=$off;autonomous=$off;desktop=$off;host=$off}} | ConvertTo-Json -Depth 5 -Compress }\nexit 0\n`);
55
55
  const current=queryForegroundIdentity(process.pid);
56
56
  const stale={...current,creation_utc:'2000-01-01T00:00:00.000Z'};
57
57
  const marker={schema:'deadbyte.remote-session.v2',owner_pid:process.pid,guardian_pid:process.pid,session_nonce:'b'.repeat(64),owner_identity:stale,guardian_identity:stale};
@@ -63,7 +63,7 @@ test('R58 bound stale birth identity reconciles on the first invocation without
63
63
  await writeFile(path.join(root,'observation.json'),JSON.stringify({code,stdout,stderr,simulation:'birth identity drift at a live unrelated numeric PID'}),{flag:'wx'});
64
64
  assert.match(stdout,/Stale remote session/);
65
65
  assert.doesNotMatch(stderr,/already active|ownership unverified/);
66
- assert.deepEqual((await readFile(trace,'utf8')).trim().split(/\r?\n/).slice(0,6),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','park']);
66
+ assert.deepEqual((await readFile(trace,'utf8')).trim().split(/\r?\n/).slice(0,7),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','status','park']);
67
67
  assert.match(stderr,/without a verified ONLINE runtime/,'fixture deliberately has no real runtime; recovery must already have converged');
68
68
  await assert.rejects(readFile(markerPath),{code:'ENOENT'});
69
69
  assert.equal(foregroundAlive(),true,'unrelated live process must not be killed');
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict';
3
3
  import {readFile} from 'node:fs/promises';
4
4
 
5
5
  const read=rel=>readFile(new URL('../'+rel,import.meta.url),'utf8');
6
- const json=rel=>read(rel).then(JSON.parse);
6
+ const json=rel=>read(rel).then(raw=>JSON.parse(raw.replace(/^\uFEFF/,'')));
7
7
 
8
8
  const PARENT={
9
9
  version:'0.15.35',
@@ -15,8 +15,8 @@ const PARENT={
15
15
 
16
16
  test('R59 identity is coherent and binds exact accepted 0.15.35 predecessor',async()=>{
17
17
  const [pkg,lock,release,prov,matrix,sbom,version,bridgePkg]=await Promise.all([
18
- json('package.json'),json('package-lock.json'),json('RELEASE.json'),json('PROVENANCE.json'),
19
- json('TEST-MATRIX.json'),json('SBOM.cdx.json'),read('src/version.mjs'),json('bridge/package.json')
18
+ json('docs/R59-package.json'),json('docs/R59-package-lock.json'),json('docs/R59-RELEASE.json'),json('docs/R59-PROVENANCE.json'),
19
+ json('docs/R59-TEST-MATRIX.json'),json('docs/R59-SBOM.cdx.json'),read('docs/R59-version.mjs'),json('docs/R59-bridge-package.json')
20
20
  ]);
21
21
  assert.equal(pkg.version,'0.15.36');
22
22
  assert.equal(lock.version,pkg.version);
@@ -40,7 +40,7 @@ test('R59 identity is coherent and binds exact accepted 0.15.35 predecessor',asy
40
40
  });
41
41
 
42
42
  test('R59 release records runtime hardening without expanding public authority',async()=>{
43
- const [release,prov,matrix]=await Promise.all([json('RELEASE.json'),json('PROVENANCE.json'),json('TEST-MATRIX.json')]);
43
+ const [release,prov,matrix]=await Promise.all([json('docs/R59-RELEASE.json'),json('docs/R59-PROVENANCE.json'),json('docs/R59-TEST-MATRIX.json')]);
44
44
  assert.equal(release.r59_runtime_hardening.optional_plane_degradation,true);
45
45
  assert.equal(release.r59_runtime_hardening.capability_manifest_v3,true);
46
46
  assert.equal(release.r59_runtime_hardening.live_overlay_authority_ceiling,true);
@@ -53,8 +53,8 @@ test('R59 release records runtime hardening without expanding public authority',
53
53
  assert.equal(matrix.observed_pre_release_evidence.r59_contracts.tests,19);
54
54
  assert.equal(matrix.observed_pre_release_evidence.r59_public_surface.status,'PASS');
55
55
  assert.equal(matrix.observed_pre_release_evidence.r59_public_surface.tool_count,43);
56
- assert.equal(prov.operator_override.four_hour_soak,'NOT_COMPLETED');
57
- assert.equal(prov.operator_override.clean_machine,'NOT_COMPLETED');
56
+ assert.equal(prov.operator_override.four_hour_soak,'WAIVED_BY_OPERATOR');
57
+ assert.equal(prov.operator_override.clean_machine,'WAIVED_BY_OPERATOR');
58
58
  assert.equal(prov.operator_override.stable_final_claim,false);
59
59
  assert.equal(matrix.operator_override.stable_final_claim,false);
60
60
  assert.equal(release.stabilization.stable_final_claim,false);
@@ -0,0 +1,36 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { authorityVector, parseAuthorityObservation, assertAuthorityVector, assertRuntimeReadyObservation } from '../src/remote-authority.mjs';
4
+
5
+ const observed = (armed={}) => ({
6
+ schema:'deadbyte.controller-observation.v1',
7
+ authority:Object.fromEntries(['process','coding','autonomous','desktop','host'].map(name=>[
8
+ name,{configured:true,armed:Boolean(armed[name]),reason:armed[name]?'standing-grant':'disarmed'}
9
+ ]))
10
+ });
11
+
12
+ test('remote modes have exact non-widening authority vectors',()=>{
13
+ assert.deepEqual(authorityVector('default'),{process:true,coding:true,autonomous:true,desktop:false,host:false});
14
+ assert.deepEqual(authorityVector('desktop'),{process:true,coding:true,autonomous:true,desktop:true,host:false});
15
+ assert.deepEqual(authorityVector('host'),{process:true,coding:true,autonomous:true,desktop:false,host:true});
16
+ assert.deepEqual(authorityVector('full'),{process:true,coding:true,autonomous:true,desktop:true,host:true});
17
+ });
18
+
19
+ test('observed standing Host grant rejects bounded READY',()=>{
20
+ const snapshot=parseAuthorityObservation(JSON.stringify(observed({process:true,coding:true,autonomous:true,host:true})));
21
+ assert.throws(()=>assertAuthorityVector(snapshot,authorityVector('default')),/host.*expected DISARMED/);
22
+ });
23
+
24
+ test('malformed or incomplete controller observation fails closed',()=>{
25
+ assert.throws(()=>parseAuthorityObservation('{}'),/observation/);
26
+ assert.throws(()=>parseAuthorityObservation(JSON.stringify({schema:'deadbyte.controller-observation.v1',authority:{process:{armed:true}}})),/authority/);
27
+ });
28
+
29
+ test('readiness requires verified Bridge Core Cloud and local MCP, not just ONLINE text',()=>{
30
+ const value={...observed({process:true,coding:true,autonomous:true}),state:'ONLINE',local_ready:true,
31
+ bridge:{alive:true,reason:'verified'},core:{alive:true,reason:'verified'},cloud:{alive:true,reason:'verified'},
32
+ supervisor:{running:true},core_root:'D:\\exact-release'};
33
+ assertRuntimeReadyObservation(value,{coreRoot:'D:\\exact-release'});
34
+ assert.throws(()=>assertRuntimeReadyObservation({...value,core:{alive:false,reason:'wrong-core-generation'}}),/core/);
35
+ assert.throws(()=>assertRuntimeReadyObservation({...value,core_root:'D:\\wrong'} ,{coreRoot:'D:\\exact-release'}),/core root/);
36
+ });
@@ -0,0 +1,28 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { probeRemoteEnforcement } from '../src/remote-enforcement-probe-r60.mjs';
4
+
5
+ function client({hostArmed=false,desktopArmed=false}={}){
6
+ return {callTool:async ({name})=>name==='desktop_control'
7
+ ? {isError:false,structuredContent:{status:'ok',op:'status',result:{armed:desktopArmed}}}
8
+ : hostArmed
9
+ ? {isError:false,structuredContent:{status:'ok',content:'{}',sha256:'a'.repeat(64)}}
10
+ : {isError:true,structuredContent:{status:'denied'}}};
11
+ }
12
+
13
+ test('default remote verifies Host denial and Desktop disarm through actual MCP tools',async()=>{
14
+ await probeRemoteEnforcement({client:client(),mode:'default'});
15
+ await assert.rejects(probeRemoteEnforcement({client:client({hostArmed:true}),mode:'default'}),/Host.*enforcement/);
16
+ await assert.rejects(probeRemoteEnforcement({client:client({desktopArmed:true}),mode:'default'}),/Desktop.*enforcement/);
17
+ });
18
+
19
+ test('full remote requires both Host read and Desktop armed state',async()=>{
20
+ await probeRemoteEnforcement({client:client({hostArmed:true,desktopArmed:true}),mode:'full'});
21
+ await assert.rejects(probeRemoteEnforcement({client:client({hostArmed:false,desktopArmed:true}),mode:'full'}),/Host.*enforcement/);
22
+ });
23
+
24
+ test('post-session revoked probe independently rejects lingering Host and Desktop grants',async()=>{
25
+ await probeRemoteEnforcement({client:client(),mode:'revoked'});
26
+ await assert.rejects(probeRemoteEnforcement({client:client({hostArmed:true}),mode:'revoked'}),/Host.*enforcement/);
27
+ await assert.rejects(probeRemoteEnforcement({client:client({desktopArmed:true}),mode:'revoked'}),/Desktop.*enforcement/);
28
+ });
@@ -0,0 +1,119 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {mkdtemp,rm} from 'node:fs/promises';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import {openRemoteCallClaims} from '../bridge/src/remote-call-claims-r60.mjs';
7
+ import {dispatchBridgeCall,classifyRemoteOperation} from '../bridge/src/remote-dispatch-r60.mjs';
8
+ import {createCapabilitySnapshotTracker} from '../src/capability-snapshot.mjs';
9
+
10
+ const base={device_id:'device-1',session_id:'stateless',request_id:'call-1',tool:'coding_file_write',
11
+ arguments:{root_id:'fixture',path:'a.txt',content:'A'},expires_at_utc:'2030-01-01T00:00:00.000Z',
12
+ release_id:'v0.15.37-test',generation_manifest_sha256:'a'.repeat(64),machine_id:'d'.repeat(64),online_epoch:'c'.repeat(64),classification:'mutating'};
13
+
14
+ async function fixture(run){
15
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r60-bridge-dispatch-'));
16
+ try{await run(openRemoteCallClaims({root,clock:()=>Date.parse('2029-01-01T00:00:00.000Z')}));}
17
+ finally{await rm(root,{recursive:true,force:true});}
18
+ }
19
+
20
+ test('remote operation classifier marks only pinned read-only forms safe for recovery',()=>{
21
+ assert.equal(classifyRemoteOperation({name:'machine_status'},{}),'read_only');
22
+ assert.equal(classifyRemoteOperation({name:'machine_fs'},{op:'read',root_id:'fixture',path:'a.txt'}),'read_only');
23
+ assert.equal(classifyRemoteOperation({name:'machine_fs'},{op:'write',root_id:'fixture',path:'a.txt'}),'mutating');
24
+ assert.equal(classifyRemoteOperation({name:'coding_file_write'},{}),'mutating');
25
+ assert.equal(classifyRemoteOperation({name:'unknown_future_tool'},{}),'unknown');
26
+ });
27
+
28
+ test('capability snapshot describes the Bridge idempotency boundary without adding signed capabilities',()=>{
29
+ const snapshot=createCapabilitySnapshotTracker({clock:()=>1000}).snapshot({profile:'compact',
30
+ ledger:{ledger_sha256:'a'.repeat(64),entries:[]},toolCatalog:['capability_manifest'],catalogSha256:'b'.repeat(64),
31
+ resolution:{capabilities:[]},bindings:{},planes:{}});
32
+ assert.equal(snapshot.remote_dispatch_contract.scope,'bridge_ingress_only');
33
+ assert.equal(snapshot.remote_dispatch_contract.keyed_mutation,'same stable key and canonical payload: no duplicate dispatch; unknown post-dispatch outcome is not retried');
34
+ assert.equal(snapshot.remote_dispatch_contract.keyless_mutation,'cross-reconnect logical retry safety unavailable; ambiguous outcome is not auto-replayed');
35
+ assert.deepEqual(snapshot.capabilities,[]);
36
+ });
37
+
38
+ test('Bridge keyed mutation duplicate executes the downstream side effect once and replays result',async()=>fixture(async claims=>{
39
+ let effects=0;
40
+ const binding={...base,idempotency_key:'stable-write-1'};
41
+ const invoke=async()=>{effects++;return {structuredContent:{status:'ok',receipt_sha256:'b'.repeat(64)}};};
42
+ const first=await dispatchBridgeCall({claims,binding,classification:'mutating',invoke});
43
+ const duplicate=await dispatchBridgeCall({claims,binding:{...binding,session_id:'new-session',request_id:'call-99'},classification:'mutating',invoke});
44
+ assert.deepEqual(duplicate,first);
45
+ assert.equal(effects,1);
46
+ await assert.rejects(dispatchBridgeCall({claims,binding:{...binding,arguments:{...binding.arguments,content:'B'}},classification:'mutating',invoke}),/idempotency_key_conflict/);
47
+ assert.equal(effects,1);
48
+ }));
49
+
50
+ test('Bridge keyless exact duplicate does not replay and uncertain post-dispatch mutation stays unknown',async()=>fixture(async claims=>{
51
+ let effects=0;
52
+ const first=await dispatchBridgeCall({claims,binding:base,classification:'mutating',invoke:async()=>{effects++;throw new Error('transport lost');}}).catch(error=>error);
53
+ assert.match(first.message,/unknown/i);
54
+ const second=await dispatchBridgeCall({claims,binding:base,classification:'mutating',invoke:async()=>{effects++;}}).catch(error=>error);
55
+ assert.match(second.message,/unknown/i);
56
+ assert.equal(effects,1);
57
+ }));
58
+
59
+ test('keyless JSON-RPC ids do not become cross-delivery mutation identities',async()=>fixture(async claims=>{
60
+ let effects=0;
61
+ const invoke=async()=>({structuredContent:{status:'ok',effect_number:++effects}});
62
+ const first=await dispatchBridgeCall({claims,binding:{...base,session_id:'delivery-a'},classification:'mutating',invoke});
63
+ const second=await dispatchBridgeCall({claims,binding:{...base,session_id:'delivery-b'},classification:'mutating',invoke});
64
+ assert.equal(first.structuredContent.effect_number,1);
65
+ assert.equal(second.structuredContent.effect_number,2);
66
+ assert.equal(effects,2,'legacy calls do not acquire exactly-once semantics from a reused JSON-RPC id');
67
+ }));
68
+
69
+ test('concurrent Bridge deliveries race one durable dispatch marker and one side effect',async()=>fixture(async claims=>{
70
+ let effects=0;
71
+ const binding={...base,idempotency_key:'concurrent-write-1'};
72
+ const outcomes=await Promise.allSettled(Array.from({length:12},()=>dispatchBridgeCall({claims,binding,classification:'mutating',invoke:async()=>{
73
+ effects++;
74
+ return {structuredContent:{status:'ok',receipt_sha256:'b'.repeat(64)}};
75
+ }})));
76
+ assert.equal(effects,1);
77
+ assert.ok(outcomes.some(item=>item.status==='fulfilled'));
78
+ assert.ok(outcomes.every(item=>item.status==='fulfilled'||/in flight|unknown|no replay/i.test(item.reason.message)));
79
+ }));
80
+
81
+ test('two Bridge claim-store instances sharing one root still allow only one keyed side effect',async()=>{
82
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r60-two-bridges-'));
83
+ try{
84
+ const one=openRemoteCallClaims({root,clock:()=>Date.parse('2029-01-01T00:00:00.000Z')});
85
+ const two=openRemoteCallClaims({root,clock:()=>Date.parse('2029-01-01T00:00:00.000Z')});
86
+ const binding={...base,idempotency_key:'two-bridge-race'};
87
+ let effects=0;
88
+ const invoke=async()=>{effects++;return {structuredContent:{status:'ok'}};};
89
+ const outcomes=await Promise.allSettled([
90
+ dispatchBridgeCall({claims:one,binding,classification:'mutating',invoke}),
91
+ dispatchBridgeCall({claims:two,binding:{...binding,session_id:'second',request_id:'second'},classification:'mutating',invoke})
92
+ ]);
93
+ assert.equal(effects,1);
94
+ assert.ok(outcomes.some(item=>item.status==='fulfilled'));
95
+ assert.ok(outcomes.every(item=>item.status==='fulfilled'||/in flight|unknown|no replay/i.test(item.reason.message)));
96
+ }finally{await rm(root,{recursive:true,force:true});}
97
+ });
98
+
99
+ test('a keyed result is replayable after the reply is lost, without invoking Core again',async()=>fixture(async claims=>{
100
+ const binding={...base,idempotency_key:'lost-reply'};
101
+ let effects=0;
102
+ await dispatchBridgeCall({claims,binding,classification:'mutating',invoke:async()=>({structuredContent:{status:'ok',ordinal:++effects}})});
103
+ const replay=await dispatchBridgeCall({claims,binding:{...binding,session_id:'reattached',request_id:'new-rpc-id'},classification:'mutating',
104
+ invoke:async()=>{throw new Error('must not reach Core');}});
105
+ assert.equal(replay.structuredContent.ordinal,1);
106
+ assert.equal(effects,1);
107
+ }));
108
+
109
+ test('read-only post-crash claim can re-execute after recovery without mutating retry',async()=>fixture(async claims=>{
110
+ const binding={...base,tool:'machine_status',classification:'read_only',arguments:{},idempotency_key:'read-status-1'};
111
+ const first=await claims.claim(binding);
112
+ await claims.dispatch(first.claim_id);
113
+ await claims.recover({limit:100});
114
+ let calls=0;
115
+ const result=await dispatchBridgeCall({claims,binding,classification:'read_only',invoke:async()=>{calls++;return {structuredContent:{status:'ok'}};}});
116
+ assert.equal(result.structuredContent.status,'ok');
117
+ assert.equal(calls,1);
118
+ assert.equal((await claims.state(first.claim_id)).state,'TERMINAL');
119
+ }));
@@ -0,0 +1,15 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { createBridgeHealth } from '../bridge/src/bridge-health-r59.mjs';
4
+
5
+ test('backward wall-clock jump cannot extend capability freshness',()=>{
6
+ let wall=100000,elapsed=0;
7
+ const health=createBridgeHealth({clock:()=>wall,elapsedClock:()=>elapsed,maxCapabilityAgeMs:15000});
8
+ health.coreConnected({childIdentity:'core',protocolEra:'modern',catalogSha256:'a'.repeat(64)});
9
+ health.capabilityObserved({schema:'deadbyte.capability-manifest.v3',snapshot_id:'b'.repeat(64),state_epoch:1,
10
+ observed_at_utc:new Date(wall).toISOString(),valid_until_utc:new Date(wall+60000).toISOString(),planes:{}});
11
+ assert.equal(health.snapshot().state,'READY');
12
+ wall-=60000;
13
+ elapsed+=16000;
14
+ assert.equal(health.snapshot().state,'DEGRADED');
15
+ });
@@ -0,0 +1,29 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { createLiveTimelineTracker } from '../src/remote-live-log.mjs';
4
+
5
+ test('capability polling prints initial/changed identity and suppresses only unchanged observations',()=>{
6
+ let now=1000;
7
+ const tracker=createLiveTimelineTracker({clock:()=>now,heartbeatBurstLimit:3,heartbeatWindowMs:3000});
8
+ const lines=[];
9
+ for(let i=0;i<3;i++){
10
+ const request_id=`cap-${i}`;
11
+ lines.push(tracker.observe({event_type:'tool.request',tool:'capability_manifest',request_id,args:{}},{sequence:i*2+1}));
12
+ now+=10;
13
+ lines.push(tracker.observe({event_type:'tool.result',tool:'capability_manifest',request_id,outcome:{is_error:false,duration_ms:2,
14
+ snapshot_id:i===2?'b'.repeat(64):'a'.repeat(64),state_epoch:i===2?2:1}},{sequence:i*2+2}));
15
+ }
16
+ assert.match(lines[1],/capability_manifest.*epoch=1/);
17
+ assert.equal(lines[3],null);
18
+ assert.match(lines[5],/capability_manifest.*epoch=2/);
19
+ assert.match(tracker.reconcileInterrupted().join('\n'),/capability_manifest ×1 unchanged suppressed/);
20
+ assert.match(tracker.summary(),/3 calls \| 3 ok/);
21
+ });
22
+
23
+ test('machine status and ping remain visible in R60 human timeline',()=>{
24
+ const tracker=createLiveTimelineTracker({clock:()=>1000});
25
+ for(const tool of ['machine_status','machine_ping']){
26
+ tracker.observe({event_type:'tool.request',tool,request_id:tool,args:{}},{});
27
+ assert.match(tracker.observe({event_type:'tool.result',tool,request_id:tool,outcome:{is_error:false,duration_ms:1}},{}),new RegExp(tool));
28
+ }
29
+ });
@@ -0,0 +1,21 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { classifyReconnectFailure } from '../bridge/src/reconnect-delay-r60.mjs';
4
+
5
+ test('identity, catalog and protocol drift terminate reconnect budget immediately',()=>{
6
+ for(const message of ['core script identity drift','replacement core catalog drift','replacement core protocol is not modern'])
7
+ assert.equal(classifyReconnectFailure(new Error(message)),'TERMINAL');
8
+ assert.equal(classifyReconnectFailure(Object.assign(new Error('connection reset'),{code:'ECONNRESET'})),'TRANSIENT');
9
+ assert.equal(classifyReconnectFailure(Object.assign(new Error('permission denied'),{code:'EACCES'})),'TERMINAL');
10
+ });
11
+
12
+ test('typed terminal and transient failures are not inferred from incidental message text',()=>{
13
+ assert.equal(classifyReconnectFailure(Object.assign(new Error('connection reset'),{kind:'terminal',code:'DEADBYTE_GENERATION_MISMATCH'})),'TERMINAL');
14
+ assert.equal(classifyReconnectFailure(Object.assign(new Error('generation mismatch'),{kind:'transient',code:'ECONNRESET'})),'TRANSIENT');
15
+ for(const code of ['DEADBYTE_AUTHORITY_REVOKED','DEADBYTE_POLICY_DENIED','DEADBYTE_SCHEMA_INVALID',
16
+ 'DEADBYTE_RELEASE_MISMATCH','DEADBYTE_GENERATION_MISMATCH','DEADBYTE_IDEMPOTENCY_CONFLICT',
17
+ 'DEADBYTE_CLAIM_CORRUPT','DEADBYTE_CREDENTIAL_EXPIRED'])
18
+ assert.equal(classifyReconnectFailure(Object.assign(new Error('arbitrary'),{code})),'TERMINAL');
19
+ for(const code of ['ECONNRESET','ECONNREFUSED','EPIPE','ETIMEDOUT','EHOSTUNREACH'])
20
+ assert.equal(classifyReconnectFailure(Object.assign(new Error('arbitrary'),{code})),'TRANSIENT');
21
+ });
@@ -0,0 +1,18 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { reconnectDelayMs, waitForReconnect } from '../bridge/src/reconnect-delay-r60.mjs';
4
+
5
+ test('bounded reconnect delays jitter each attempt without exceeding the restart budget',()=>{
6
+ const low=[0,1,2,3,4].map(index=>reconnectDelayMs(index,()=>0));
7
+ const high=[0,1,2,3,4].map(index=>reconnectDelayMs(index,()=>1));
8
+ assert.deepEqual(low,[188,375,750,1500,3000]);
9
+ assert.deepEqual(high,[313,625,1250,2500,5000]);
10
+ assert.throws(()=>reconnectDelayMs(5),/attempt/);
11
+ });
12
+
13
+ test('shutdown aborts a scheduled reconnect before another child can start',async()=>{
14
+ const controller=new AbortController();
15
+ const pending=waitForReconnect(5000,controller.signal);
16
+ controller.abort();
17
+ assert.equal(await pending,false);
18
+ });