deadbyte-mcp 0.14.0 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/AGENTS.md +11 -18
  2. package/CONTEXT.md +3 -3
  3. package/MANIFEST.SHA256 +156 -70
  4. package/PROVENANCE.json +10 -0
  5. package/R43-PARENT.json +20 -0
  6. package/R44-DONORS.json +99 -0
  7. package/R44-PARENT.json +18 -0
  8. package/R45-PARENT.json +18 -0
  9. package/README.txt +19 -14
  10. package/RELEASE.json +37 -0
  11. package/SBOM.cdx.json +46 -0
  12. package/TEST-MATRIX.json +31 -0
  13. package/bin/deadbyte-hardware-observer-r45.exe +0 -0
  14. package/bin/deadbyte-hardware-observer-r45.obj +0 -0
  15. package/controller/README.TXT +5 -6
  16. package/controller/deadbyte-controller.ps1 +2 -2
  17. package/docs/ARCHITECTURE.md +8 -1
  18. package/docs/R44-ARCHITECTURE.md +37 -0
  19. package/native/deadbyte-hardware-observer.cpp +308 -0
  20. package/package.json +18 -8
  21. package/scripts/autonomous-tests.ps1 +2 -2
  22. package/scripts/build-hardware-r45.ps1 +71 -0
  23. package/scripts/deferred-slot-operation-r43.mjs +126 -0
  24. package/scripts/deferred-slot-operation-r44.mjs +126 -0
  25. package/scripts/deferred-slot-operation-r45.mjs +126 -0
  26. package/scripts/derive-r45-parent.mjs +24 -0
  27. package/scripts/derive-r45-release.mjs +72 -0
  28. package/scripts/derive-r45-tests.mjs +20 -0
  29. package/scripts/final-closure-r43.mjs +115 -0
  30. package/scripts/final-closure-r44.mjs +121 -0
  31. package/scripts/final-closure-r45.mjs +121 -0
  32. package/scripts/final-closure-verify-r43.mjs +213 -0
  33. package/scripts/final-closure-verify-r44.mjs +220 -0
  34. package/scripts/final-closure-verify-r45.mjs +220 -0
  35. package/scripts/finalize-r43.mjs +217 -0
  36. package/scripts/finalize-r44.mjs +217 -0
  37. package/scripts/finalize-r45.mjs +219 -0
  38. package/scripts/full-regression-r43.mjs +20 -0
  39. package/scripts/full-regression-r44.mjs +20 -0
  40. package/scripts/full-regression-r45.mjs +20 -0
  41. package/scripts/full-regression-summary-r45.mjs +14 -0
  42. package/scripts/gate-windows-r43.ps1 +36 -0
  43. package/scripts/gate-windows-r44.ps1 +36 -0
  44. package/scripts/gate-windows-r45.ps1 +44 -0
  45. package/scripts/init-host-policy.ps1 +39 -1
  46. package/scripts/inspect-active-release-r45.mjs +20 -0
  47. package/scripts/release-manifest.mjs +3 -1
  48. package/scripts/release-parity-r43.mjs +218 -0
  49. package/scripts/release-parity-r44.mjs +242 -0
  50. package/scripts/release-parity-r45.mjs +253 -0
  51. package/scripts/release-parity-tests-r43.ps1 +8 -0
  52. package/scripts/release-parity-tests-r44.ps1 +8 -0
  53. package/scripts/release-parity-tests-r45.ps1 +8 -0
  54. package/scripts/verify-r43-parent.mjs +126 -0
  55. package/scripts/verify-r44-parent.mjs +142 -0
  56. package/scripts/verify-r45-parent.mjs +142 -0
  57. package/scripts/windows-gate-evidence-r43.mjs +55 -0
  58. package/scripts/windows-gate-evidence-r44.mjs +55 -0
  59. package/scripts/windows-gate-evidence-r45.mjs +60 -0
  60. package/src/agent-backend-r44.mjs +254 -0
  61. package/src/autonomous-output-contracts.mjs +1 -1
  62. package/src/autonomous-policy.mjs +41 -4
  63. package/src/autonomous-runtime.mjs +37 -19
  64. package/src/autonomous-supervisor-host.mjs +12 -2
  65. package/src/capability-ledger.mjs +3 -1
  66. package/src/codex-app-server-r44.mjs +154 -0
  67. package/src/coding-plane.mjs +66 -19
  68. package/src/compact-gateway-mcp-tools.mjs +4 -2
  69. package/src/deadbyte-cli.mjs +17 -9
  70. package/src/doctor-r45.mjs +65 -0
  71. package/src/durable-job-r44.mjs +145 -0
  72. package/src/external-job-provider-r44.mjs +126 -0
  73. package/src/host-plane.mjs +156 -87
  74. package/src/job-supervisor-host-r44.mjs +112 -0
  75. package/src/machine-fs-smoke-client.mjs +1 -1
  76. package/src/machine-fs.mjs +21 -10
  77. package/src/mcp-host-smoke-client.mjs +1 -1
  78. package/src/mcp-server.mjs +84 -26
  79. package/src/mcp-smoke-client.mjs +1 -1
  80. package/src/native-diff-v2.mjs +12 -7
  81. package/src/native-diff.mjs +32 -9
  82. package/src/observation-mcp-tools.mjs +5 -1
  83. package/src/observation-output-contracts.mjs +8 -0
  84. package/src/observation-runtime.mjs +33 -2
  85. package/src/observation-smoke-client.mjs +7 -3
  86. package/src/operator-surface.mjs +2 -2
  87. package/src/output-contracts.mjs +27 -11
  88. package/src/process-policy.mjs +19 -0
  89. package/src/process-runtime.mjs +5 -2
  90. package/src/process-smoke-client.mjs +2 -2
  91. package/src/provider-circuit.mjs +8 -2
  92. package/src/r44-compact-disarmed-probe.mjs +74 -0
  93. package/src/r44-full-surface-probe.mjs +34 -0
  94. package/src/r45-compact-disarmed-probe.mjs +74 -0
  95. package/src/r45-full-surface-probe.mjs +34 -0
  96. package/src/release-generation.mjs +1 -0
  97. package/src/remote-device-r44.mjs +205 -0
  98. package/src/remote-mcp-autonomous-smoke-client.mjs +4 -4
  99. package/src/remote-mcp-r44-parity-client.mjs +203 -0
  100. package/src/remote-mcp-r45-parity-client.mjs +206 -0
  101. package/src/remote-mcp-smoke-client.mjs +7 -4
  102. package/src/runtime-update.mjs +100 -39
  103. package/src/skill-mcp-tools-r44.mjs +37 -0
  104. package/src/skill-plane-r44.mjs +203 -0
  105. package/src/tunnel-stdio-compat-smoke.mjs +1 -1
  106. package/src/version.mjs +1 -1
  107. package/test/agent-backend-r44.test.mjs +115 -0
  108. package/test/agent-runtime.test.mjs +4 -3
  109. package/test/autonomous-loop.test.mjs +121 -11
  110. package/test/autonomous-runtime.test.mjs +3 -2
  111. package/test/capability-ledger-r36.test.mjs +3 -3
  112. package/test/cli-entrypoint.test.mjs +4 -4
  113. package/test/codex-app-server-r44.test.mjs +115 -0
  114. package/test/codex-autonomous-r44.test.mjs +87 -0
  115. package/test/coding-plane.test.mjs +3 -2
  116. package/test/coding-runtime.test.mjs +36 -1
  117. package/test/compact-catalog-r36.test.mjs +3 -3
  118. package/test/compact-max-catalog-r40.test.mjs +4 -2
  119. package/test/compact-max-catalog-r44.test.mjs +97 -0
  120. package/test/compact-max-catalog-r45.test.mjs +99 -0
  121. package/test/contained.test.mjs +23 -11
  122. package/test/controller.test.mjs +5 -1
  123. package/test/core.test.mjs +1 -1
  124. package/test/durable-job-r44.test.mjs +75 -0
  125. package/test/external-job-provider-r44.test.mjs +46 -0
  126. package/test/full-max-catalog-r40.test.mjs +5 -3
  127. package/test/full-max-catalog-r44.test.mjs +91 -0
  128. package/test/full-max-catalog-r45.test.mjs +91 -0
  129. package/test/hardware-observation-r45.test.mjs +44 -0
  130. package/test/host-inline-r45.test.mjs +67 -0
  131. package/test/host-plane.test.mjs +15 -1
  132. package/test/machine-mcp-integration.test.mjs +1 -0
  133. package/test/native-diff.test.mjs +28 -0
  134. package/test/observation-mcp-tools.test.mjs +4 -3
  135. package/test/output-schema.test.mjs +3 -2
  136. package/test/package-boundary.test.mjs +1 -1
  137. package/test/process-policy.test.mjs +22 -1
  138. package/test/production-docs.test.mjs +3 -3
  139. package/test/prompts-resources.test.mjs +1 -1
  140. package/test/r33-closeout-regression.test.mjs +20 -36
  141. package/test/r33-finalization.test.mjs +7 -7
  142. package/test/r43-finalization.test.mjs +31 -0
  143. package/test/r43-finalizer.test.mjs +34 -0
  144. package/test/r43-parent.test.mjs +29 -0
  145. package/test/r44-finalization.test.mjs +58 -0
  146. package/test/r44-finalizer.test.mjs +48 -0
  147. package/test/r44-parent.test.mjs +25 -0
  148. package/test/r45-autonomous-quick-test.test.mjs +10 -0
  149. package/test/r45-finalization.test.mjs +84 -0
  150. package/test/release-generation.test.mjs +1 -0
  151. package/test/release-version.test.mjs +65 -20
  152. package/test/remote-device-r44.test.mjs +104 -0
  153. package/test/runtime-host-bootstrap-r45.test.mjs +46 -0
  154. package/test/runtime-self-update.test.mjs +42 -0
  155. package/test/skill-mcp-r44.test.mjs +38 -0
  156. package/test/skill-plane-r44.test.mjs +93 -0
  157. package/test/standing-grants.test.mjs +5 -1
@@ -5,7 +5,7 @@ import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
5
5
  import os from 'node:os';
6
6
  import path from 'node:path';
7
7
  import { generateTrustKeypair } from '../src/trust.mjs';
8
- import { createCodingRuntime, loadCodingPolicy } from '../src/coding-plane.mjs';
8
+ import { assertCodingArmed, createCodingRuntime, loadCodingPolicy, recheckCodingAuthority } from '../src/coding-plane.mjs';
9
9
  import { createCodingTaskRuntime } from '../src/coding-task-runtime.mjs';
10
10
 
11
11
  function sha256Bytes(bytes) { return createHash('sha256').update(bytes).digest('hex'); }
@@ -78,6 +78,41 @@ test('coding plane rejects mutation when lease is absent or precondition is stal
78
78
  await assert.rejects(() => f.runtime.fileWrite({ rootId: 'project', relativePath: 'x.txt', content: 'x', expectedSha256: 'absent' }), /coding plane is disarmed/);
79
79
  } finally { await rm(f.root, { recursive: true, force: true }); }
80
80
  });
81
+
82
+ test('coding authority recheck rejects lease generation or expiry drift before commit', async () => {
83
+ const f = await fixture();
84
+ try {
85
+ const initial = await assertCodingArmed(f.policy, 'project');
86
+ const lease = JSON.parse(await readFile(f.policy.lease_file, 'utf8'));
87
+ await writeFile(f.policy.lease_file, JSON.stringify({ ...lease, instance_nonce: 'b'.repeat(64) }));
88
+ await assert.rejects(() => recheckCodingAuthority(f.policy, initial, 'project'), /coding authority changed during operation/);
89
+ await writeFile(f.policy.lease_file, JSON.stringify({
90
+ ...lease,
91
+ expires_at_utc: new Date(Date.parse(lease.expires_at_utc) + 60000).toISOString()
92
+ }));
93
+ await assert.rejects(() => recheckCodingAuthority(f.policy, initial, 'project'), /coding authority changed during operation/);
94
+ } finally { await rm(f.root, { recursive: true, force: true }); }
95
+ });
96
+ test('coding execution deadline bounds runtime without changing the signed request identity', async () => {
97
+ const f = await fixture();
98
+ try {
99
+ const script = path.join(f.project, 'deadline.mjs');
100
+ await writeFile(script, "await new Promise(resolve => setTimeout(resolve, 10000));\n");
101
+ const scriptHash = await sha256File(script);
102
+ const args = { rootId:'project', commandId:'node_script', scriptPath:'deadline.mjs', scriptSha256:scriptHash,
103
+ timeoutMs:20000, executionDeadlineUtc:new Date(Date.now()+3000).toISOString() };
104
+ const expectedRequest = f.runtime.requestSha('coding_command_run', {
105
+ root_id:'project', command_id:'node_script', script_path:'deadline.mjs', script_sha256:scriptHash,
106
+ args:[], cwd:'.', timeout_ms:20000
107
+ });
108
+ const result = await f.runtime.commandRun(args);
109
+ assert.equal(result.status, 'observed_failure');
110
+ assert.equal(result.timed_out, true);
111
+ assert.ok(result.timeout_ms >= 500 && result.timeout_ms <= 3000);
112
+ assert.equal(result.request_sha256, expectedRequest);
113
+ } finally { await rm(f.root, { recursive:true, force:true }); }
114
+ });
115
+
81
116
  test('coding command and benchmark produce signed observations', async () => {
82
117
  const f = await fixture();
83
118
  try {
@@ -10,7 +10,7 @@ const repoRoot=path.resolve(here,'..');
10
10
  const serverPath=path.join(repoRoot,'src','mcp-server.mjs');
11
11
  const gatewayNames=['capability_manifest','coding_inspect','desktop_control','machine_fs','machine_observe','process_session'];
12
12
 
13
- test('R36 real compact stdio catalog stays <=42 and exposes all gateways',async()=>{
13
+ test('R44 real compact stdio catalog stays <=43 and exposes all gateways plus skill_catalog',async()=>{
14
14
  const client=new Client({name:'deadbyte-r36-compact-test',version:'0.10.0-dev'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
15
15
  const env={
16
16
  ...getDefaultEnvironment(),
@@ -24,8 +24,8 @@ test('R36 real compact stdio catalog stays <=42 and exposes all gateways',async(
24
24
  await client.connect(transport);
25
25
  const listed=await client.listTools();
26
26
  const names=listed.tools.map(tool=>tool.name).sort();
27
- assert.ok(names.length<=42,`compact tool count ${names.length} exceeds 42`);
28
- for(const name of gatewayNames) assert.ok(names.includes(name),`missing ${name}`);
27
+ assert.ok(names.length<=43,`compact tool count ${names.length} exceeds 43`);
28
+ for(const name of [...gatewayNames,'skill_catalog']) assert.ok(names.includes(name),`missing ${name}`);
29
29
  for(const legacy of ['workspace_open','file_read','proof_execute','contained_transform']) assert.equal(names.includes(legacy),false,`legacy full-only tool leaked: ${legacy}`);
30
30
  const manifest=await client.callTool({name:'capability_manifest',arguments:{}});
31
31
  assert.equal(manifest.isError,false);
@@ -10,7 +10,9 @@ import { createR34Fixture } from './helpers/autonomous-r34-fixture.mjs';
10
10
 
11
11
  const here=path.dirname(fileURLToPath(import.meta.url));
12
12
  const repoRoot=path.resolve(here,'..');
13
- const serverPath=path.join(repoRoot,'src','mcp-server.mjs');
13
+ const parent=JSON.parse(await readFile(path.join(repoRoot,'R44-PARENT.json'),'utf8'));
14
+ const parentRoot=parent.parent_release_root;
15
+ const serverPath=path.join(parentRoot,'src','mcp-server.mjs');
14
16
  const desktopPolicyPath=path.join(repoRoot,'controller','deadbyte-desktop-policy.json');
15
17
  const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
16
18
  const absent=async p=>{try{await access(p);return false;}catch(e){if(e?.code==='ENOENT')return true;throw e;}};
@@ -70,7 +72,7 @@ test('R40 real max-runtime compact stdio catalog is exactly 42 while all authori
70
72
  DEADBYTE_AUTONOMOUS_POLICY:path.join(f.root,'autonomous-policy.json'),
71
73
  DEADBYTE_AUTONOMOUS_SUPERVISOR_MODE:'external'
72
74
  };
73
- const transport=new StdioClientTransport({command:process.execPath,args:[serverPath],cwd:repoRoot,env,stderr:'pipe'});
75
+ const transport=new StdioClientTransport({command:process.execPath,args:[serverPath],cwd:parentRoot,env,stderr:'pipe'});
74
76
  try{
75
77
  await client.connect(transport);
76
78
  const listed=await client.listTools();
@@ -0,0 +1,97 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { createHash } from 'node:crypto';
4
+ import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { Client } from '@modelcontextprotocol/client';
8
+ import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/client/stdio';
9
+ import { createR34Fixture } from './helpers/autonomous-r34-fixture.mjs';
10
+
11
+ const here=path.dirname(fileURLToPath(import.meta.url));
12
+ const repoRoot=path.resolve(here,'..');
13
+ const serverPath=path.join(repoRoot,'src','mcp-server.mjs');
14
+ const desktopPolicyPath=path.join(repoRoot,'controller','deadbyte-desktop-policy.json');
15
+ const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
16
+ const absent=async p=>{try{await access(p);return false;}catch(e){if(e?.code==='ENOENT')return true;throw e;}};
17
+
18
+ test('R44 real max-runtime compact stdio catalog is exactly 43 with verified skill_catalog while all authority remains disarmed',async()=>{
19
+ const f=await createR34Fixture({authority:'disarmed'});
20
+ const processPolicyPath=path.join(f.root,'process-policy.json');
21
+ const processGrant=path.join(f.root,'process-grant.json');
22
+ const processState=path.join(f.root,'process-state');
23
+ const hostPolicyPath=path.join(f.root,'host-policy.json');
24
+ const hostArm=path.join(f.root,'host-arm.json');
25
+ const nodeSha=sha(await readFile(process.execPath));
26
+ await mkdir(processState,{recursive:true});
27
+ await writeFile(processPolicyPath,JSON.stringify({
28
+ schema:'deadbyte.process-policy.v1',enabled:true,grant_file:processGrant,state_dir:processState,
29
+ native_host:{path:process.execPath,sha256:nodeSha},
30
+ roots:{core:{path:f.project,deny_paths:[]}},
31
+ profiles:{node:{executable:process.execPath,executable_sha256:nodeSha,root_ids:['core'],
32
+ prefix_args:[],transport:'pipe',max_args:8,max_arg_bytes:1024,max_sessions:2,
33
+ max_input_bytes:65536,max_retained_output_bytes:262144,env_allow:['PATH','TEMP','TMP'],
34
+ input_actions:{ping:'PING\r\n'}}}
35
+ },null,2));
36
+ await writeFile(hostPolicyPath,JSON.stringify({
37
+ schema:'deadbyte.host-policy.v1',enabled:true,arm_file:hostArm,max_read_bytes:262144,max_write_bytes:1048576,
38
+ roots:{core:{path:f.project,read:true,write:true,exec:true}},
39
+ commands:{node_script:{kind:'script',executable:process.execPath,executable_sha256:nodeSha,
40
+ root_ids:['core'],prefix_args:[],extensions:['.mjs'],timeout_ms:5000,max_output_bytes:65536}}
41
+ },null,2));
42
+
43
+ const desktopRaw=JSON.parse(await readFile(desktopPolicyPath,'utf8'));
44
+ const desktopGrant=desktopRaw.grant_file;
45
+ const codingLease=f.codingPolicy.lease_file;
46
+ const autonomousLease=f.policy.lease_file;
47
+ for(const q of [processGrant,hostArm,desktopGrant,codingLease,autonomousLease]) assert.equal(await absent(q),true,'authority unexpectedly armed before start: '+q);
48
+
49
+ const client=new Client({name:'deadbyte-r40-max-catalog',version:'0.12.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
50
+ const env={
51
+ ...getDefaultEnvironment(),
52
+ DEADBYTE_CAPABILITY_PROFILE:'compact',
53
+ DEADBYTE_AUDIT_RUNTIME:'0',
54
+ DEADBYTE_STATE_ROOT:path.join(f.root,'state'),
55
+ DEADBYTE_WORKSPACE_ROOT:f.root,
56
+ DEADBYTE_SIGNING_KEY:f.keys.privateKeyPath,
57
+ DEADBYTE_VERIFY_KEY:f.keys.publicKeyPath,
58
+ DEADBYTE_PRIVILEGED_POLICY:hostPolicyPath,
59
+ DEADBYTE_MACHINE_RUNTIME:'1',
60
+ DEADBYTE_AGENT_RUNTIME:'0',
61
+ DEADBYTE_PROCESS_RUNTIME:'1',
62
+ DEADBYTE_PROCESS_POLICY:processPolicyPath,
63
+ DEADBYTE_CODING_RUNTIME:'1',
64
+ DEADBYTE_CODING_POLICY:path.join(f.root,'coding-policy.json'),
65
+ DEADBYTE_MACHINE_FS_RUNTIME:'1',
66
+ DEADBYTE_OBSERVATION_RUNTIME:'1',
67
+ DEADBYTE_DESKTOP_RUNTIME:'1',
68
+ DEADBYTE_DESKTOP_POLICY:desktopPolicyPath,
69
+ DEADBYTE_AUTONOMOUS_RUNTIME:'1',
70
+ DEADBYTE_AUTONOMOUS_POLICY:path.join(f.root,'autonomous-policy.json'),
71
+ DEADBYTE_AUTONOMOUS_SUPERVISOR_MODE:'external'
72
+ };
73
+ const transport=new StdioClientTransport({command:process.execPath,args:[serverPath],cwd:repoRoot,env,stderr:'pipe'});
74
+ try{
75
+ await client.connect(transport);
76
+ const listed=await client.listTools();
77
+ const names=listed.tools.map(t=>t.name).sort();
78
+ assert.equal(names.length,43,'expected exact compact max catalog 43, got '+names.length+': '+names.join(','));
79
+ for(const name of ['capability_manifest','desktop_control','coding_inspect','machine_fs','machine_observe','process_session',
80
+ 'host_file_read','host_file_write','host_exec','skill_catalog']) assert.ok(names.includes(name),'missing '+name);
81
+ const manifest=await client.callTool({name:'capability_manifest',arguments:{}});
82
+ assert.equal(manifest.isError,false);
83
+ const m=manifest.structuredContent.result;
84
+ assert.equal(m.schema,'deadbyte.capability-manifest.v2');
85
+ assert.equal(m.profile,'compact');
86
+ assert.equal(m.tool_count,43);
87
+ assert.deepEqual(m.tool_catalog,names);
88
+ assert.match(m.catalog_sha256,/^[0-9a-f]{64}$/);
89
+ const skill=m.capabilities.find(item=>item.id==='skill.catalog');
90
+ assert.ok(skill);assert.equal(skill.authority,'none');assert.equal(skill.mutating,false);
91
+ console.log(JSON.stringify({tool_count:m.tool_count,catalog_sha256:m.catalog_sha256,ledger_sha256:m.ledger_sha256,desktop:true,skill:true,authority:'DISARMED'}));
92
+ } finally {
93
+ await client.close().catch(()=>{});
94
+ for(const q of [processGrant,hostArm,desktopGrant,codingLease,autonomousLease]) assert.equal(await absent(q),true,'authority file appeared during catalog probe: '+q);
95
+ await f.cleanup();
96
+ }
97
+ });
@@ -0,0 +1,99 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { createHash } from 'node:crypto';
4
+ import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { Client } from '@modelcontextprotocol/client';
8
+ import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/client/stdio';
9
+ import { createR34Fixture } from './helpers/autonomous-r34-fixture.mjs';
10
+
11
+ const here=path.dirname(fileURLToPath(import.meta.url));
12
+ const repoRoot=path.resolve(here,'..');
13
+ const serverPath=path.join(repoRoot,'src','mcp-server.mjs');
14
+ const desktopPolicyPath=path.join(repoRoot,'controller','deadbyte-desktop-policy.json');
15
+ const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
16
+ const absent=async p=>{try{await access(p);return false;}catch(e){if(e?.code==='ENOENT')return true;throw e;}};
17
+
18
+ test('R45 real max-runtime compact stdio catalog is exactly 43 with verified skill_catalog while all authority remains disarmed',async()=>{
19
+ const f=await createR34Fixture({authority:'disarmed'});
20
+ const processPolicyPath=path.join(f.root,'process-policy.json');
21
+ const processGrant=path.join(f.root,'process-grant.json');
22
+ const processState=path.join(f.root,'process-state');
23
+ const hostPolicyPath=path.join(f.root,'host-policy.json');
24
+ const hostArm=path.join(f.root,'host-arm.json');
25
+ const nodeSha=sha(await readFile(process.execPath));
26
+ await mkdir(processState,{recursive:true});
27
+ await writeFile(processPolicyPath,JSON.stringify({
28
+ schema:'deadbyte.process-policy.v1',enabled:true,grant_file:processGrant,state_dir:processState,
29
+ native_host:{path:process.execPath,sha256:nodeSha},
30
+ roots:{core:{path:f.project,deny_paths:[]}},
31
+ profiles:{node:{executable:process.execPath,executable_sha256:nodeSha,root_ids:['core'],
32
+ prefix_args:[],transport:'pipe',max_args:8,max_arg_bytes:1024,max_sessions:2,
33
+ max_input_bytes:65536,max_retained_output_bytes:262144,env_allow:['PATH','TEMP','TMP'],
34
+ input_actions:{ping:'PING\r\n'}}}
35
+ },null,2));
36
+ await writeFile(hostPolicyPath,JSON.stringify({
37
+ schema:'deadbyte.host-policy.v1',enabled:true,arm_file:hostArm,max_read_bytes:262144,max_write_bytes:1048576,
38
+ roots:{core:{path:f.project,read:true,write:true,exec:true}},
39
+ commands:{node_script:{kind:'script',executable:process.execPath,executable_sha256:nodeSha,
40
+ root_ids:['core'],prefix_args:[],extensions:['.mjs'],timeout_ms:5000,max_output_bytes:65536}}
41
+ },null,2));
42
+
43
+ const desktopRaw=JSON.parse(await readFile(desktopPolicyPath,'utf8'));
44
+ const desktopGrant=desktopRaw.grant_file;
45
+ const codingLease=f.codingPolicy.lease_file;
46
+ const autonomousLease=f.policy.lease_file;
47
+ for(const q of [processGrant,hostArm,desktopGrant,codingLease,autonomousLease]) assert.equal(await absent(q),true,'authority unexpectedly armed before start: '+q);
48
+
49
+ const client=new Client({name:'deadbyte-r40-max-catalog',version:'0.12.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
50
+ const env={
51
+ ...getDefaultEnvironment(),
52
+ DEADBYTE_CAPABILITY_PROFILE:'compact',
53
+ DEADBYTE_AUDIT_RUNTIME:'0',
54
+ DEADBYTE_STATE_ROOT:path.join(f.root,'state'),
55
+ DEADBYTE_WORKSPACE_ROOT:f.root,
56
+ DEADBYTE_SIGNING_KEY:f.keys.privateKeyPath,
57
+ DEADBYTE_VERIFY_KEY:f.keys.publicKeyPath,
58
+ DEADBYTE_PRIVILEGED_POLICY:hostPolicyPath,
59
+ DEADBYTE_MACHINE_RUNTIME:'1',
60
+ DEADBYTE_AGENT_RUNTIME:'0',
61
+ DEADBYTE_PROCESS_RUNTIME:'1',
62
+ DEADBYTE_PROCESS_POLICY:processPolicyPath,
63
+ DEADBYTE_CODING_RUNTIME:'1',
64
+ DEADBYTE_CODING_POLICY:path.join(f.root,'coding-policy.json'),
65
+ DEADBYTE_MACHINE_FS_RUNTIME:'1',
66
+ DEADBYTE_OBSERVATION_RUNTIME:'1',
67
+ DEADBYTE_DESKTOP_RUNTIME:'1',
68
+ DEADBYTE_DESKTOP_POLICY:desktopPolicyPath,
69
+ DEADBYTE_AUTONOMOUS_RUNTIME:'1',
70
+ DEADBYTE_AUTONOMOUS_POLICY:path.join(f.root,'autonomous-policy.json'),
71
+ DEADBYTE_AUTONOMOUS_SUPERVISOR_MODE:'external'
72
+ };
73
+ const transport=new StdioClientTransport({command:process.execPath,args:[serverPath],cwd:repoRoot,env,stderr:'pipe'});
74
+ try{
75
+ await client.connect(transport);
76
+ const listed=await client.listTools();
77
+ const names=listed.tools.map(t=>t.name).sort();
78
+ assert.equal(names.length,43,'expected exact compact max catalog 43, got '+names.length+': '+names.join(','));
79
+ for(const name of ['capability_manifest','desktop_control','coding_inspect','machine_fs','machine_observe','process_session',
80
+ 'host_file_read','host_file_write','host_exec','skill_catalog']) assert.ok(names.includes(name),'missing '+name);
81
+ const manifest=await client.callTool({name:'capability_manifest',arguments:{}});
82
+ assert.equal(manifest.isError,false);
83
+ const m=manifest.structuredContent.result;
84
+ assert.equal(m.schema,'deadbyte.capability-manifest.v2');
85
+ assert.equal(m.profile,'compact');
86
+ assert.equal(m.tool_count,43);
87
+ assert.deepEqual(m.tool_catalog,names);
88
+ assert.match(m.catalog_sha256,/^[0-9a-f]{64}$/);
89
+ const hardware=m.capabilities.find(item=>item.id==='observation.hardware_query');
90
+ assert.ok(hardware);assert.equal(hardware.authority,'none');assert.equal(hardware.compact_tool,'machine_observe');assert.equal(hardware.compact_op,'hardware_query');
91
+ const skill=m.capabilities.find(item=>item.id==='skill.catalog');
92
+ assert.ok(skill);assert.equal(skill.authority,'none');assert.equal(skill.mutating,false);
93
+ console.log(JSON.stringify({tool_count:m.tool_count,catalog_sha256:m.catalog_sha256,ledger_sha256:m.ledger_sha256,desktop:true,hardware_query:true,skill:true,authority:'DISARMED'}));
94
+ } finally {
95
+ await client.close().catch(()=>{});
96
+ for(const q of [processGrant,hostArm,desktopGrant,codingLease,autonomousLease]) assert.equal(await absent(q),true,'authority file appeared during catalog probe: '+q);
97
+ await f.cleanup();
98
+ }
99
+ });
@@ -352,6 +352,10 @@ test('contained execution source has a compile-time worker registry and no user
352
352
  const executor = await readFile(fileURLToPath(new URL('../src/contained-executor.mjs', import.meta.url)), 'utf8');
353
353
  const registry = await readFile(fileURLToPath(new URL('../src/worker-registry.mjs', import.meta.url)), 'utf8');
354
354
  const server = await readFile(fileURLToPath(new URL('../src/mcp-server.mjs', import.meta.url)), 'utf8');
355
+ const containedStart = server.indexOf("toolServer.registerTool(\n 'contained_transform'");
356
+ const containedEnd = server.indexOf('toolServer.registerTool(', containedStart + 1);
357
+ const contained = server.slice(containedStart, containedEnd);
358
+ assert.ok(containedStart >= 0 && containedEnd > containedStart);
355
359
  assert.match(registry, /contained-transform-worker\.exe/);
356
360
  assert.match(registry, /contained-reverse-worker\.exe/);
357
361
  assert.match(registry, /CONTAINED_EXECUTION_REGISTRY/);
@@ -360,9 +364,9 @@ test('contained execution source has a compile-time worker registry and no user
360
364
  assert.match(registry, /lowercase_ascii/);
361
365
  assert.match(registry, /reverse_bytes/);
362
366
  assert.match(executor, /loadVerifiedWorkerRegistry/);
363
- assert.match(server, /'contained_transform'/);
364
- assert.match(server, /z\.enum\(CONTAINED_TRANSFORM_OPERATIONS\)/);
365
- assert.doesNotMatch(server, /executable:\s*z\./);
367
+ assert.match(contained, /'contained_transform'/);
368
+ assert.match(contained, /z\.enum\(CONTAINED_TRANSFORM_OPERATIONS\)/);
369
+ assert.doesNotMatch(contained, /executable:\s*z\./);
366
370
  assert.doesNotMatch(executor, /--worker/);
367
371
  assert.doesNotMatch(server, /registerTool\(\s*['"](?:command_run|shell_execute|powershell_run)['"]/);
368
372
  });
@@ -706,12 +710,16 @@ test('contained execution validator rejects missing or altered worker manifest e
706
710
 
707
711
  test('MCP contained_transform schema is a compile-time operation enum with no executable or invocation-control surface', async () => {
708
712
  const server = await readFile(fileURLToPath(new URL('../src/mcp-server.mjs', import.meta.url)), 'utf8');
709
- assert.match(server, /z\.enum\(CONTAINED_TRANSFORM_OPERATIONS\)/);
710
- assert.equal((server.match(/inputSchema: z\.strictObject\(\{/g) ?? []).length, 8);
713
+ const containedStart = server.indexOf("toolServer.registerTool(\n 'contained_transform'");
714
+ const containedEnd = server.indexOf('toolServer.registerTool(', containedStart + 1);
715
+ const contained = server.slice(containedStart, containedEnd);
716
+ assert.ok(containedStart >= 0 && containedEnd > containedStart);
717
+ assert.match(contained, /z\.enum\(CONTAINED_TRANSFORM_OPERATIONS\)/);
718
+ assert.match(contained, /inputSchema: z\.strictObject\(\{/);
711
719
  assert.match(server, /if \(hostPolicy\)/);
712
720
  for (const name of ['host_file_read', 'host_file_write', 'host_exec']) assert.match(server, new RegExp(`'${name}'`));
713
- assert.doesNotMatch(server, /inputSchema: z\.object\(\{/);
714
- assert.doesNotMatch(server, /executable:\s*z\.|argv:\s*z\.|environment:\s*z\.|challenge:\s*z\.|request_digest:\s*z\.|worker_id:\s*z\./);
721
+ assert.doesNotMatch(contained, /inputSchema: z\.object\(\{/);
722
+ assert.doesNotMatch(contained, /executable:\s*z\.|argv:\s*z\.|environment:\s*z\.|challenge:\s*z\.|request_digest:\s*z\.|worker_id:\s*z\./);
715
723
  });
716
724
 
717
725
  test('R27 Windows batch gate builds native payload before freezing and verifying the candidate', async () => {
@@ -914,13 +922,17 @@ test('contained execution validator fails closed on active network evidence', ()
914
922
 
915
923
  test('execution authority is strict at MCP and native argv boundaries', async () => {
916
924
  const server = await readFile(fileURLToPath(new URL('../src/mcp-server.mjs', import.meta.url)), 'utf8');
925
+ const containedStart = server.indexOf("toolServer.registerTool(\n 'contained_transform'");
926
+ const containedEnd = server.indexOf('toolServer.registerTool(', containedStart + 1);
927
+ const contained = server.slice(containedStart, containedEnd);
917
928
  const launcher = await readFile(fileURLToPath(new URL('../native/deadbyte-exec.cpp', import.meta.url)), 'utf8');
918
929
  const helper = await readFile(fileURLToPath(new URL('../native/contained-network-proof.h', import.meta.url)), 'utf8');
919
930
  const authority = await readFile(fileURLToPath(new URL('../src/authority-smoke.mjs', import.meta.url)), 'utf8');
920
- assert.match(server, /inputSchema: z\.strictObject\(/);
921
- assert.doesNotMatch(server, /worker_id:\s*z\./);
922
- assert.doesNotMatch(server, /executable:\s*z\./);
923
- assert.doesNotMatch(server, /argv:\s*z\./);
931
+ assert.ok(containedStart >= 0 && containedEnd > containedStart);
932
+ assert.match(contained, /inputSchema: z\.strictObject\(/);
933
+ assert.doesNotMatch(contained, /worker_id:\s*z\./);
934
+ assert.doesNotMatch(contained, /executable:\s*z\./);
935
+ assert.doesNotMatch(contained, /argv:\s*z\./);
924
936
  assert.match(launcher, /argc != 7/);
925
937
  assert.match(launcher, /invalid fixed argument contract/);
926
938
  assert.match(helper, /HasExactContainedWorkerArguments/);
@@ -82,11 +82,15 @@ test('controller keeps privileged policy optional and propagates it only to the
82
82
 
83
83
  test('Windows host policy bootstrap pins executables and creates only explicit local roots', async () => {
84
84
  const init = await readFile(fileURLToPath(new URL('../scripts/init-host-policy.ps1', import.meta.url)), 'utf8');
85
- assert.match(init, /deadbyte\.host-policy\.v1/);
85
+ assert.match(init, /deadbyte\.host-policy\.v2/);
86
86
  assert.match(init, /Get-FileHash/);
87
87
  assert.match(init, /powershell_script/);
88
88
  assert.match(init, /cmd_script/);
89
89
  assert.match(init, /node_script/);
90
+ assert.match(init, /powershell_inline/);
91
+ assert.match(init, /cmd_inline/);
92
+ assert.match(init, /node_inline/);
93
+ assert.match(init, /native_exec/);
90
94
  assert.match(init, /D:\\Projects/);
91
95
  assert.match(init, /State\s+: DISARMED/);
92
96
  });
@@ -105,7 +105,7 @@ test('proof execution creates deterministic output and signed verifiable receipt
105
105
  assert.equal(result.receipt.request.schema, 'deadbyte.request.v1');
106
106
  assert.match(result.receipt.request.nonce, /^[0-9a-f]{64}$/);
107
107
  assert.match(result.receipt.request.sha256, /^[0-9a-f]{64}$/);
108
- assert.equal(result.receipt.runtime.package_version, '0.14.0');
108
+ assert.equal(result.receipt.runtime.package_version, '0.15.1');
109
109
  assert.match(result.receipt.runtime.mcp_ingress_adapter, /^(?:none|tunnel-stdio-probe-compat-v1)$/);
110
110
  assert.match(result.receipt.runtime.mcp_ingress_adapter_sha256, /^[0-9a-f]{64}$/);
111
111
  assert.match(result.receipt.runtime.mcp_ingress_mode_source_sha256, /^[0-9a-f]{64}$/);
@@ -0,0 +1,75 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdtemp, rm, writeFile } from 'node:fs/promises';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { createDurableJobSupervisorR44 } from '../src/durable-job-r44.mjs';
7
+
8
+ class FakeProvider{
9
+ constructor({external=true}={}){this.external=external;this.handles=new Map();this.starts=0;}
10
+ descriptor(){return {kind:this.external?'external-supervisor':'controller-local',instance_id:'sup-1'};}
11
+ async start(spec){
12
+ const handle={handle_id:`h-${++this.starts}`,pid:1000+this.starts,creation_id:`c-${this.starts}`,profile_id:spec.profile_id};
13
+ const running={...handle,state:'running'};this.handles.set(handle.handle_id,running);return {...running};
14
+ }
15
+ async reattach(handle){
16
+ const current=this.handles.get(handle.handle_id);return current?{...current}:null;
17
+ }
18
+ async status(handle){return this.handles.get(handle.handle_id)??null;}
19
+ async terminate(handle){const current=this.handles.get(handle.handle_id);if(current){current.state='terminated';return {...current};}return null;}
20
+ }
21
+
22
+ test('R44 controller/session jobs never masquerade as persistent after owner restart',async()=>{
23
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-job-'));const provider=new FakeProvider();
24
+ try{
25
+ const one=await createDurableJobSupervisorR44({stateDir:root,provider,ownerInstanceId:'owner-1'});
26
+ const c=await one.start({lifetime:'controller',profileId:'build',rootId:'core',cwd:'.',args:[]});
27
+ const s=await one.start({lifetime:'session',profileId:'test',rootId:'core',cwd:'.',args:[]});
28
+ assert.equal(c.state,'running');assert.equal(s.state,'running');
29
+ const two=await createDurableJobSupervisorR44({stateDir:root,provider,ownerInstanceId:'owner-2'});
30
+ assert.equal((await two.status({jobId:c.job_id})).state,'interrupted');
31
+ assert.equal((await two.status({jobId:s.job_id})).state,'interrupted');
32
+ }finally{await rm(root,{recursive:true,force:true});}
33
+ });
34
+
35
+ test('R44 persistent job reattaches only through external supervisor with exact process identity',async()=>{
36
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-job-persist-'));const provider=new FakeProvider();
37
+ try{
38
+ const one=await createDurableJobSupervisorR44({stateDir:root,provider,ownerInstanceId:'owner-1'});
39
+ const job=await one.start({lifetime:'persistent',profileId:'watch',rootId:'core',cwd:'.',args:[]});
40
+ const two=await createDurableJobSupervisorR44({stateDir:root,provider,ownerInstanceId:'owner-2'});
41
+ const recovered=await two.status({jobId:job.job_id});
42
+ assert.equal(recovered.state,'running');assert.equal(recovered.recovered,true);
43
+ assert.equal(recovered.pid,job.pid);assert.equal(recovered.creation_id,job.creation_id);
44
+
45
+ const live=provider.handles.get(job.handle_id);live.creation_id='spoofed';
46
+ const three=await createDurableJobSupervisorR44({stateDir:root,provider,ownerInstanceId:'owner-3'});
47
+ assert.equal((await three.status({jobId:job.job_id})).state,'orphaned');
48
+ }finally{await rm(root,{recursive:true,force:true});}
49
+ });
50
+
51
+ test('R44 persistent lifetime rejects controller-local provider and state corruption',async()=>{
52
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-job-local-'));
53
+ try{
54
+ const local=await createDurableJobSupervisorR44({stateDir:root,provider:new FakeProvider({external:false}),ownerInstanceId:'owner'});
55
+ await assert.rejects(local.start({lifetime:'persistent',profileId:'x',rootId:'core',cwd:'.',args:[]}),/external supervisor/);
56
+ }finally{await rm(root,{recursive:true,force:true});}
57
+
58
+ const corrupt=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-job-corrupt-'));
59
+ try{
60
+ await writeFile(path.join(corrupt,'jobs.json'),'{"schema":','utf8');
61
+ await assert.rejects(createDurableJobSupervisorR44({stateDir:corrupt,provider:new FakeProvider(),ownerInstanceId:'owner'}),/state JSON invalid/);
62
+ }finally{await rm(corrupt,{recursive:true,force:true});}
63
+ });
64
+
65
+ test('R44 terminate binds exact supervisor handle and becomes durable terminal state',async()=>{
66
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-job-term-'));const provider=new FakeProvider();
67
+ try{
68
+ const sup=await createDurableJobSupervisorR44({stateDir:root,provider,ownerInstanceId:'owner'});
69
+ const job=await sup.start({lifetime:'persistent',profileId:'watch',rootId:'core',cwd:'.',args:[]});
70
+ const ended=await sup.terminate({jobId:job.job_id});
71
+ assert.equal(ended.state,'terminated');
72
+ const restarted=await createDurableJobSupervisorR44({stateDir:root,provider,ownerInstanceId:'owner-2'});
73
+ assert.equal((await restarted.status({jobId:job.job_id})).state,'terminated');
74
+ }finally{await rm(root,{recursive:true,force:true});}
75
+ });
@@ -0,0 +1,46 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { createHash } from 'node:crypto';
4
+ import { mkdir, mkdtemp, rm, writeFile, readFile } from 'node:fs/promises';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { createDurableJobSupervisorR44 } from '../src/durable-job-r44.mjs';
9
+ import { createExternalJobProviderR44 } from '../src/external-job-provider-r44.mjs';
10
+
11
+ const sha=b=>createHash('sha256').update(b).digest('hex');
12
+ const here=path.dirname(fileURLToPath(import.meta.url));
13
+ const repoRoot=path.resolve(here,'..');
14
+ const workerPath=path.join(repoRoot,'src','job-supervisor-host-r44.mjs');
15
+
16
+ test('R44 real external supervisor preserves persistent job identity across controller recreation',async()=>{
17
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-real-supervisor-'));
18
+ const supervisorState=path.join(root,'host'),jobsState=path.join(root,'jobs'),project=path.join(root,'project');
19
+ await mkdir(project,{recursive:true});
20
+ const script=path.join(project,'hold.mjs');
21
+ await writeFile(script,`process.stdout.write('READY\\n');setInterval(()=>{},1000);\n`,'utf8');
22
+ const nodeSha=sha(await readFile(process.execPath)), workerSha=sha(await readFile(workerPath));
23
+ const config={stateDir:supervisorState,nodeExecutable:process.execPath,nodeExecutableSha256:nodeSha,
24
+ workerPath,workerSha256:workerSha,roots:{core:project},profiles:{
25
+ hold:{executable:process.execPath,executable_sha256:nodeSha,root_ids:['core'],prefix_args:[script],max_args:4,max_arg_bytes:1024}
26
+ }};
27
+ let providerA=null,providerB=null;
28
+ try{
29
+ providerA=await createExternalJobProviderR44(config);
30
+ const one=await createDurableJobSupervisorR44({stateDir:jobsState,provider:providerA,ownerInstanceId:'controller-a'});
31
+ const started=await one.start({lifetime:'persistent',profileId:'hold',rootId:'core',cwd:'.',args:[]});
32
+ assert.equal(started.state,'running');assert.ok(started.pid>0);assert.match(started.creation_id,/^[0-9a-f]{64}$/);
33
+
34
+ providerB=await createExternalJobProviderR44(config);
35
+ assert.equal(providerB.descriptor().instance_id,providerA.descriptor().instance_id,'must reconnect same external supervisor');
36
+ const two=await createDurableJobSupervisorR44({stateDir:jobsState,provider:providerB,ownerInstanceId:'controller-b'});
37
+ const recovered=await two.status({jobId:started.job_id});
38
+ assert.equal(recovered.state,'running');assert.equal(recovered.recovered,true);
39
+ assert.equal(recovered.handle_id,started.handle_id);assert.equal(recovered.pid,started.pid);assert.equal(recovered.creation_id,started.creation_id);
40
+ const ended=await two.terminate({jobId:started.job_id});assert.equal(ended.state,'terminated');
41
+ }finally{
42
+ if(providerB)await providerB.shutdown().catch(()=>{});
43
+ else if(providerA)await providerA.shutdown().catch(()=>{});
44
+ await rm(root,{recursive:true,force:true});
45
+ }
46
+ });
@@ -10,8 +10,10 @@ import { createR34Fixture } from './helpers/autonomous-r34-fixture.mjs';
10
10
 
11
11
  const here=path.dirname(fileURLToPath(import.meta.url));
12
12
  const repoRoot=path.resolve(here,'..');
13
- const serverPath=path.join(repoRoot,'src','mcp-server.mjs');
14
- const desktopPolicyPath=path.join(repoRoot,'controller','deadbyte-desktop-policy.json');
13
+ const parent=JSON.parse(await readFile(path.join(repoRoot,'R44-PARENT.json'),'utf8'));
14
+ const parentRoot=parent.parent_release_root;
15
+ const serverPath=path.join(parentRoot,'src','mcp-server.mjs');
16
+ const desktopPolicyPath=path.join(parentRoot,'controller','deadbyte-desktop-policy.json');
15
17
  const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
16
18
  const absent=async p=>{try{await access(p);return false;}catch(e){if(e?.code==='ENOENT')return true;throw e;}};
17
19
 
@@ -70,7 +72,7 @@ test('R40 real max-runtime full stdio catalog is exactly 80 with four Desktop pr
70
72
  DEADBYTE_AUTONOMOUS_POLICY:path.join(f.root,'autonomous-policy.json'),
71
73
  DEADBYTE_AUTONOMOUS_SUPERVISOR_MODE:'external'
72
74
  };
73
- const transport=new StdioClientTransport({command:process.execPath,args:[serverPath],cwd:repoRoot,env,stderr:'pipe'});
75
+ const transport=new StdioClientTransport({command:process.execPath,args:[serverPath],cwd:parentRoot,env,stderr:'pipe'});
74
76
  try{
75
77
  await client.connect(transport);
76
78
  const listed=await client.listTools();
@@ -0,0 +1,91 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { createHash } from 'node:crypto';
4
+ import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { Client } from '@modelcontextprotocol/client';
8
+ import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/client/stdio';
9
+ import { createR34Fixture } from './helpers/autonomous-r34-fixture.mjs';
10
+
11
+ const here=path.dirname(fileURLToPath(import.meta.url));
12
+ const repoRoot=path.resolve(here,'..');
13
+ const serverPath=path.join(repoRoot,'src','mcp-server.mjs');
14
+ const desktopPolicyPath=path.join(repoRoot,'controller','deadbyte-desktop-policy.json');
15
+ const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
16
+ const absent=async p=>{try{await access(p);return false;}catch(e){if(e?.code==='ENOENT')return true;throw e;}};
17
+
18
+ test('R44 real max-runtime full stdio catalog is exactly 81 with skill and four Desktop primitives while authority remains disarmed',async()=>{
19
+ const f=await createR34Fixture({authority:'disarmed'});
20
+ const processPolicyPath=path.join(f.root,'process-policy.json');
21
+ const processGrant=path.join(f.root,'process-grant.json');
22
+ const processState=path.join(f.root,'process-state');
23
+ const hostPolicyPath=path.join(f.root,'host-policy.json');
24
+ const hostArm=path.join(f.root,'host-arm.json');
25
+ const nodeSha=sha(await readFile(process.execPath));
26
+ await mkdir(processState,{recursive:true});
27
+ await writeFile(processPolicyPath,JSON.stringify({
28
+ schema:'deadbyte.process-policy.v1',enabled:true,grant_file:processGrant,state_dir:processState,
29
+ native_host:{path:process.execPath,sha256:nodeSha},
30
+ roots:{core:{path:f.project,deny_paths:[]}},
31
+ profiles:{node:{executable:process.execPath,executable_sha256:nodeSha,root_ids:['core'],
32
+ prefix_args:[],transport:'pipe',max_args:8,max_arg_bytes:1024,max_sessions:2,
33
+ max_input_bytes:65536,max_retained_output_bytes:262144,env_allow:['PATH','TEMP','TMP'],
34
+ input_actions:{ping:'PING\r\n'}}}
35
+ },null,2));
36
+ await writeFile(hostPolicyPath,JSON.stringify({
37
+ schema:'deadbyte.host-policy.v1',enabled:true,arm_file:hostArm,max_read_bytes:262144,max_write_bytes:1048576,
38
+ roots:{core:{path:f.project,read:true,write:true,exec:true}},
39
+ commands:{node_script:{kind:'script',executable:process.execPath,executable_sha256:nodeSha,
40
+ root_ids:['core'],prefix_args:[],extensions:['.mjs'],timeout_ms:5000,max_output_bytes:65536}}
41
+ },null,2));
42
+
43
+ const desktopRaw=JSON.parse(await readFile(desktopPolicyPath,'utf8'));
44
+ const desktopGrant=desktopRaw.grant_file;
45
+ const codingLease=f.codingPolicy.lease_file;
46
+ const autonomousLease=f.policy.lease_file;
47
+ for(const q of [processGrant,hostArm,desktopGrant,codingLease,autonomousLease]) assert.equal(await absent(q),true,'authority unexpectedly armed before start: '+q);
48
+
49
+ const client=new Client({name:'deadbyte-r44-full-catalog',version:'0.15.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
50
+ const env={
51
+ ...getDefaultEnvironment(),
52
+ DEADBYTE_CAPABILITY_PROFILE:'full',
53
+ DEADBYTE_AUDIT_RUNTIME:'0',
54
+ DEADBYTE_STATE_ROOT:path.join(f.root,'state'),
55
+ DEADBYTE_WORKSPACE_ROOT:f.root,
56
+ DEADBYTE_SIGNING_KEY:f.keys.privateKeyPath,
57
+ DEADBYTE_VERIFY_KEY:f.keys.publicKeyPath,
58
+ DEADBYTE_PRIVILEGED_POLICY:hostPolicyPath,
59
+ DEADBYTE_MACHINE_RUNTIME:'1',
60
+ DEADBYTE_AGENT_RUNTIME:'1',
61
+ DEADBYTE_PROCESS_RUNTIME:'1',
62
+ DEADBYTE_PROCESS_POLICY:processPolicyPath,
63
+ DEADBYTE_CODING_RUNTIME:'1',
64
+ DEADBYTE_CODING_POLICY:path.join(f.root,'coding-policy.json'),
65
+ DEADBYTE_MACHINE_FS_RUNTIME:'1',
66
+ DEADBYTE_OBSERVATION_RUNTIME:'1',
67
+ DEADBYTE_DESKTOP_RUNTIME:'1',
68
+ DEADBYTE_DESKTOP_POLICY:desktopPolicyPath,
69
+ DEADBYTE_AUTONOMOUS_RUNTIME:'1',
70
+ DEADBYTE_AUTONOMOUS_POLICY:path.join(f.root,'autonomous-policy.json'),
71
+ DEADBYTE_AUTONOMOUS_SUPERVISOR_MODE:'external'
72
+ };
73
+ const transport=new StdioClientTransport({command:process.execPath,args:[serverPath],cwd:repoRoot,env,stderr:'pipe'});
74
+ try{
75
+ await client.connect(transport);
76
+ const listed=await client.listTools();
77
+ const names=listed.tools.map(t=>t.name).sort();
78
+ const {R44_EXPECTED_TOOLS}=await import('../src/remote-mcp-r44-parity-client.mjs');
79
+ assert.equal(names.length,81,'expected exact full R44 catalog 81, got '+names.length+': '+names.join(','));
80
+ assert.deepEqual(names,R44_EXPECTED_TOOLS);
81
+ for(const name of ['desktop_status','desktop_observe','desktop_target','desktop_action',
82
+ 'host_file_read','host_file_write','host_exec','skill_catalog']) assert.ok(names.includes(name),'missing '+name);
83
+ for(const tool of listed.tools) assert.equal(tool.outputSchema?.additionalProperties,false,'non-strict output '+tool.name);
84
+ const catalog_sha256=sha(Buffer.from(JSON.stringify(names),'utf8'));
85
+ console.log(JSON.stringify({tool_count:names.length,catalog_sha256,desktop_tools:4,skill:true,authority:'DISARMED'}));
86
+ } finally {
87
+ await client.close().catch(()=>{});
88
+ for(const q of [processGrant,hostArm,desktopGrant,codingLease,autonomousLease]) assert.equal(await absent(q),true,'authority file appeared during catalog probe: '+q);
89
+ await f.cleanup();
90
+ }
91
+ });