deadbyte-mcp 0.15.0 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/MANIFEST.SHA256 +109 -64
  2. package/PROVENANCE.json +11 -0
  3. package/R45-PARENT.json +18 -0
  4. package/README.txt +36 -20
  5. package/RELEASE.json +53 -0
  6. package/SBOM.cdx.json +46 -0
  7. package/TEST-MATRIX.json +31 -0
  8. package/bin/deadbyte-hardware-observer-r45.exe +0 -0
  9. package/bin/deadbyte-hardware-observer-r45.obj +0 -0
  10. package/controller/README.TXT +2 -2
  11. package/controller/deadbyte-controller.ps1 +51 -3
  12. package/controller/deadbyte-process-policy.json +63 -1
  13. package/docs/ARCHITECTURE.md +3 -4
  14. package/native/deadbyte-hardware-observer.cpp +308 -0
  15. package/package.json +14 -7
  16. package/scripts/autonomous-tests.ps1 +2 -2
  17. package/scripts/build-hardware-r45.ps1 +71 -0
  18. package/scripts/deferred-slot-operation-r45.mjs +126 -0
  19. package/scripts/deferred-slot-operation-r46.mjs +126 -0
  20. package/scripts/derive-r45-parent.mjs +24 -0
  21. package/scripts/derive-r45-release.mjs +72 -0
  22. package/scripts/derive-r45-tests.mjs +20 -0
  23. package/scripts/final-closure-r45.mjs +121 -0
  24. package/scripts/final-closure-r46.mjs +98 -0
  25. package/scripts/final-closure-verify-r45.mjs +220 -0
  26. package/scripts/final-closure-verify-r46.mjs +119 -0
  27. package/scripts/finalize-r45.mjs +219 -0
  28. package/scripts/full-regression-r45.mjs +20 -0
  29. package/scripts/full-regression-r46.mjs +20 -0
  30. package/scripts/full-regression-summary-r45.mjs +14 -0
  31. package/scripts/gate-windows-r45.ps1 +44 -0
  32. package/scripts/gate-windows-r46.ps1 +56 -0
  33. package/scripts/init-host-policy.ps1 +39 -1
  34. package/scripts/inspect-active-release-r45.mjs +20 -0
  35. package/scripts/refresh-process-policy.mjs +64 -7
  36. package/scripts/release-manifest.mjs +3 -1
  37. package/scripts/release-parity-r45.mjs +253 -0
  38. package/scripts/release-parity-r46.mjs +187 -0
  39. package/scripts/release-parity-tests-r45.ps1 +8 -0
  40. package/scripts/verify-r45-parent.mjs +142 -0
  41. package/scripts/windows-gate-evidence-r45.mjs +60 -0
  42. package/scripts/windows-gate-evidence-r46.mjs +110 -0
  43. package/src/autonomous-runtime.mjs +12 -7
  44. package/src/capability-ledger.mjs +2 -0
  45. package/src/compact-gateway-mcp-tools.mjs +11 -7
  46. package/src/deadbyte-cli.mjs +24 -12
  47. package/src/doctor-r45.mjs +65 -0
  48. package/src/host-plane.mjs +133 -85
  49. package/src/machine-fs-mcp-tools.mjs +3 -3
  50. package/src/machine-fs-output-contracts.mjs +1 -1
  51. package/src/machine-fs-read-worker.mjs +46 -0
  52. package/src/machine-fs.mjs +63 -10
  53. package/src/mcp-server.mjs +68 -23
  54. package/src/mcp-smoke-client.mjs +4 -1
  55. package/src/native-diff-v2.mjs +9 -4
  56. package/src/native-diff.mjs +29 -7
  57. package/src/observation-mcp-tools.mjs +5 -1
  58. package/src/observation-output-contracts.mjs +8 -0
  59. package/src/observation-runtime.mjs +57 -5
  60. package/src/observation-smoke-client.mjs +6 -2
  61. package/src/output-contracts.mjs +27 -11
  62. package/src/process-mcp-tools.mjs +18 -5
  63. package/src/process-output-contracts.mjs +20 -3
  64. package/src/process-policy.mjs +3 -1
  65. package/src/process-runtime.mjs +99 -19
  66. package/src/process-smoke-client.mjs +67 -22
  67. package/src/r45-compact-disarmed-probe.mjs +74 -0
  68. package/src/r45-full-surface-probe.mjs +34 -0
  69. package/src/release-generation.mjs +23 -3
  70. package/src/remote-device-r44.mjs +53 -15
  71. package/src/remote-mcp-r45-parity-client.mjs +206 -0
  72. package/src/remote-mcp-smoke-client.mjs +1 -1
  73. package/src/runtime-update.mjs +36 -8
  74. package/src/stdio-console-guard.mjs +24 -0
  75. package/src/version.mjs +1 -1
  76. package/test/agent-runtime.test.mjs +1 -0
  77. package/test/autonomous-loop.test.mjs +24 -0
  78. package/test/autonomous-runtime.test.mjs +1 -0
  79. package/test/cli-entrypoint.test.mjs +4 -4
  80. package/test/coding-plane.test.mjs +1 -0
  81. package/test/compact-gateway-r36.test.mjs +28 -0
  82. package/test/compact-max-catalog-r45.test.mjs +99 -0
  83. package/test/contained.test.mjs +23 -11
  84. package/test/controller.test.mjs +5 -1
  85. package/test/core.test.mjs +1 -1
  86. package/test/full-max-catalog-r45.test.mjs +91 -0
  87. package/test/full-max-catalog-r46.test.mjs +92 -0
  88. package/test/hardware-observation-r45.test.mjs +44 -0
  89. package/test/host-inline-r45.test.mjs +67 -0
  90. package/test/machine-fs-mcp-tools.test.mjs +4 -1
  91. package/test/machine-fs-runtime.test.mjs +18 -2
  92. package/test/machine-mcp-integration.test.mjs +1 -0
  93. package/test/observation-mcp-tools.test.mjs +4 -3
  94. package/test/observation-runtime.test.mjs +29 -2
  95. package/test/output-schema.test.mjs +1 -0
  96. package/test/package-boundary.test.mjs +1 -1
  97. package/test/process-mcp-tools.test.mjs +23 -5
  98. package/test/process-policy-refresh.test.mjs +87 -10
  99. package/test/process-runtime.test.mjs +111 -1
  100. package/test/process-supervision.test.mjs +4 -2
  101. package/test/production-docs.test.mjs +3 -3
  102. package/test/r33-closeout-regression.test.mjs +20 -19
  103. package/test/r33-finalization.test.mjs +3 -3
  104. package/test/r45-autonomous-quick-test.test.mjs +10 -0
  105. package/test/r45-finalization.test.mjs +84 -0
  106. package/test/release-generation.test.mjs +26 -11
  107. package/test/release-version.test.mjs +112 -46
  108. package/test/remote-device-r44.test.mjs +75 -0
  109. package/test/remote-r24-catalog.test.mjs +2 -2
  110. package/test/runtime-host-bootstrap-r45.test.mjs +46 -0
@@ -15,14 +15,15 @@ function runtime(){
15
15
  searchStatus:async()=>({status:'ok',search_id:hex('a'),state:'completed',result_count:0,files_scanned:0,bytes_scanned:0,truncated:false,request_sha256:hex('b'),result_head_sha256:hex('c'),receipt_id:receipt,receipt_sha256:hex('d'),error:null}),
16
16
  searchCancel:async()=>({status:'ok',search_id:hex('a'),state:'cancelled',cancel_requested:true}),
17
17
  processList:async()=>({status:'ok',snapshot_id:hex('e'),observed_at_utc:new Date(0).toISOString(),processes:[],next_cursor:null,request_sha256:hex('b'),result_head_sha256:hex('c'),receipt_id:receipt,receipt_sha256:hex('d')}),
18
- systemStatus:async()=>({status:'ok',observed_at_utc:new Date(0).toISOString(),hostname:'x',platform:'win32',architecture:'x64',uptime_seconds:1,total_memory_bytes:2,free_memory_bytes:1,cpu_count:1,load_average:[0,0,0],request_sha256:hex('b'),result_head_sha256:hex('c'),receipt_id:receipt,receipt_sha256:hex('d')})
18
+ systemStatus:async()=>({status:'ok',observed_at_utc:new Date(0).toISOString(),hostname:'x',platform:'win32',architecture:'x64',uptime_seconds:1,total_memory_bytes:2,free_memory_bytes:1,cpu_count:1,load_average:[0,0,0],request_sha256:hex('b'),result_head_sha256:hex('c'),receipt_id:receipt,receipt_sha256:hex('d')}),
19
+ hardwareQuery:async({query})=>({status:'ok',query,observed_at_utc:new Date(0).toISOString(),observer_sha256:hex('e'),data:{vendor:'x'},request_sha256:hex('b'),result_head_sha256:hex('c'),receipt_id:receipt,receipt_sha256:hex('d')})
19
20
  };
20
21
  }
21
22
 
22
- test('R32 registers exactly six bounded observation tools',()=>{
23
+ test('R45 registers exactly seven bounded observation tools including typed native hardware',()=>{
23
24
  const server=new FakeServer(); registerObservationTools(server,{runtime:runtime()});
24
25
  assert.deepEqual([...server.tools.keys()].sort(),[
25
- 'machine_process_list','machine_search_cancel','machine_search_page','machine_search_start','machine_search_status','machine_system_status'
26
+ 'machine_hardware_query','machine_process_list','machine_search_cancel','machine_search_page','machine_search_start','machine_search_status','machine_system_status'
26
27
  ]);
27
28
  for(const [name,tool] of server.tools){ assert.ok(tool.spec.inputSchema,`${name} input`); assert.ok(tool.spec.outputSchema,`${name} output`); }
28
29
  });
@@ -7,7 +7,7 @@ import { generateTrustKeypair } from '../src/trust.mjs';
7
7
  import { loadCodingPolicy } from '../src/coding-plane.mjs';
8
8
  import { createObservationRuntime, verifyObservationReceiptFile } from '../src/observation-runtime.mjs';
9
9
 
10
- async function fixture() {
10
+ async function fixture(runtimeOptions={}) {
11
11
  const root = await mkdtemp(path.join(os.tmpdir(), 'deadbyte-observation-'));
12
12
  const project = path.join(root, 'project');
13
13
  await mkdir(project); await mkdir(path.join(project, 'blocked'));
@@ -36,7 +36,8 @@ async function fixture() {
36
36
  const runtime = createObservationRuntime({
37
37
  policy, signingKeyPath:keys.privateKeyPath, verifyKeyPath:keys.publicKeyPath,
38
38
  processRuntime, stateRoot:root, processSnapshotProvider, systemSnapshotProvider,
39
- ownedRuntimePidProvider: async () => new Set([111])
39
+ ownedRuntimePidProvider: async () => new Set([111]),
40
+ ...runtimeOptions
40
41
  });
41
42
  return { root, project, policy, keys, runtime, getProcessCalls:() => processCalls };
42
43
  }
@@ -121,3 +122,29 @@ test('R32 system observation is timestamped and signed without mutation authorit
121
122
  assert.equal(verified.ok,true);
122
123
  assert.equal(verified.result_head_sha256,status.result_head_sha256);
123
124
  });
125
+
126
+
127
+ test('R46 observation search sessions expire after bounded terminal TTL and free capacity', async () => {
128
+ let now=1000;
129
+ const f=await fixture({searchSessionTtlMs:50,maxSearchSessions:1,clockMs:()=>now});
130
+ await writeFile(path.join(f.project,'ttl.txt'),'needle\n');
131
+ const first=await f.runtime.searchStart({rootId:'project',relativePath:'.',pattern:'needle',maxResults:10,maxFiles:10,maxBytes:65536});
132
+ const done=await waitTerminal(f.runtime,first.search_id);
133
+ assert.equal(done.state,'completed');
134
+ now+=60;
135
+ await assert.rejects(f.runtime.searchStatus({searchId:first.search_id}),/unknown or expired search/);
136
+ const second=await f.runtime.searchStart({rootId:'project',relativePath:'.',pattern:'needle',maxResults:10,maxFiles:10,maxBytes:65536});
137
+ assert.match(second.search_id,/^[0-9a-f]{64}$/);
138
+ });
139
+
140
+ test('R46 observation search hard cap fails closed while all retained sessions are active', async () => {
141
+ const f=await fixture({searchSessionTtlMs:5000,maxSearchSessions:2});
142
+ await writeFile(path.join(f.project,'cap.txt'),'needle\n');
143
+ const a=await f.runtime.searchStart({rootId:'project',relativePath:'.',pattern:'needle'});
144
+ const b=await f.runtime.searchStart({rootId:'project',relativePath:'.',pattern:'needle'});
145
+ assert.match(a.search_id,/^[0-9a-f]{64}$/);
146
+ assert.match(b.search_id,/^[0-9a-f]{64}$/);
147
+ await assert.rejects(f.runtime.searchStart({rootId:'project',relativePath:'.',pattern:'needle'}),/session limit reached/);
148
+ await waitTerminal(f.runtime,a.search_id).catch(()=>{});
149
+ await waitTerminal(f.runtime,b.search_id).catch(()=>{});
150
+ });
@@ -40,6 +40,7 @@ async function fixture() {
40
40
  DEADBYTE_WORKSPACE_ROOT: workspaceRoot,
41
41
  DEADBYTE_SIGNING_KEY: keys.privateKeyPath,
42
42
  DEADBYTE_VERIFY_KEY: keys.publicKeyPath,
43
+ DEADBYTE_PRIVILEGED_POLICY: '',
43
44
  DEADBYTE_AGENT_RUNTIME: '1'
44
45
  },
45
46
  stderr: 'pipe'
@@ -39,6 +39,6 @@ test('R28 package boundary contains only immutable workspace seed files', () =>
39
39
 
40
40
  test('publish boundary excludes live state, private keys, grants and non-example controller secrets',()=>{
41
41
  const files=packFiles();
42
- const forbidden=files.filter(rel=>/(^|\/)(active-release\.json|runtime\/|audit\/|evidence\/)|\.pem$|(^|\/)deadbyte-controller\.config\.psd1$|(^|\/)deadbyte-host-policy\.json$|(^|\/)[^/]*(?:grant|arm)\.json$/i.test(rel));
42
+ const forbidden=files.filter(rel=>rel.startsWith('tmp/')||rel.startsWith('.deadbyte-machine-fs-recovery/')||/(^|\/)(active-release\.json|runtime\/|audit\/|evidence\/)|\.pem$|(^|\/)deadbyte-controller\.config\.psd1$|(^|\/)deadbyte-host-policy\.json$|(^|\/)[^/]*(?:grant|arm)\.json$/i.test(rel));
43
43
  assert.deepEqual(forbidden,[]);
44
44
  });
@@ -1,4 +1,4 @@
1
- import test from 'node:test';
1
+ import test from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
3
  import { registerProcessTools } from '../src/process-mcp-tools.mjs';
4
4
 
@@ -20,18 +20,19 @@ function runtime() {
20
20
  return {
21
21
  start: async () => session(), read: async () => ({
22
22
  session_id: hex('a'), offset: 0, next_offset: 4, earliest_offset: 0, stream_head: 4,
23
- stream_sha256: hex('b'), truncated: false, data_base64: 'VEVTVA==', state: 'running', exit_code: null
23
+ stream_sha256: hex('b'), truncated: false, wait_satisfied: true, data_base64: 'VEVTVA==', state: 'running', exit_code: null
24
24
  }),
25
25
  status: async () => session(), list: async () => ({ sessions: [session()] }),
26
+ write: async ({ input, appendNewline }) => ({ logical_bytes: Buffer.byteLength(input + (appendNewline ? '\r\n' : '')), logical_sha256: hex('e'), append_newline: appendNewline, session: session() }),
26
27
  action: async ({ actionId }) => ({ action_id: actionId, logical_bytes: 4, logical_sha256: hex('d'), session: session() }),
27
28
  terminate: async () => session('terminated')
28
29
  };
29
30
  }
30
- test('R32 registers exactly six bounded process supervision tools', () => {
31
+ test('R46 registers exactly seven bounded process supervision tools', () => {
31
32
  const server = new FakeServer();
32
33
  registerProcessTools(server, { runtime: runtime() });
33
34
  assert.deepEqual([...server.tools.keys()].sort(), [
34
- 'process_session_action','process_session_list','process_session_read','process_session_start','process_session_status','process_session_terminate'
35
+ 'process_session_action','process_session_list','process_session_read','process_session_start','process_session_status','process_session_terminate','process_session_write'
35
36
  ]);
36
37
  for (const [name, tool] of server.tools) {
37
38
  assert.ok(tool.spec.inputSchema, `${name} input schema missing`);
@@ -41,7 +42,7 @@ test('R32 registers exactly six bounded process supervision tools', () => {
41
42
  }
42
43
  });
43
44
 
44
- test('R32 process tool inputs expose IDs and bounds but no executable or raw input field', () => {
45
+ test('R46 process tool inputs expose guarded raw input only through process_session_write', () => {
45
46
  const server = new FakeServer();
46
47
  registerProcessTools(server, { runtime: runtime() });
47
48
  const start = server.tools.get('process_session_start').spec.inputSchema;
@@ -52,6 +53,10 @@ test('R32 process tool inputs expose IDs and bounds but no executable or raw inp
52
53
  assert.equal(start.safeParse({ profile_id: 'hold', root_id: 'core', input: 'whoami' }).success, false);
53
54
  assert.equal(terminate.safeParse({ session_id: hex('a') }).success, true);
54
55
  assert.equal(terminate.safeParse({ pid: 1234 }).success, false);
56
+ const write = server.tools.get('process_session_write').spec.inputSchema;
57
+ assert.equal(write.safeParse({ session_id: hex('a'), input: 'Write-Output 42', append_newline: true }).success, true);
58
+ assert.equal(write.safeParse({ session_id: hex('a'), input: '' }).success, false);
59
+ assert.equal(write.safeParse({ session_id: hex('a'), input: 'x', pid: 1234 }).success, false);
55
60
  assert.equal(action.safeParse({ session_id: hex('a'), action_id: 'ping' }).success, true);
56
61
  for (const forbidden of ['input','command','executable','environment','pid']) {
57
62
  assert.equal(action.safeParse({ session_id: hex('a'), action_id: 'ping', [forbidden]: 'forbidden' }).success, false);
@@ -69,3 +74,16 @@ test('R32 process action handler returns only strict logical identity and update
69
74
  assert.equal(result.structuredContent.session.state, 'running');
70
75
  assert.equal(tool.spec.outputSchema.safeParse({ ...result.structuredContent, raw_input: 'forbidden' }).success, false);
71
76
  });
77
+
78
+
79
+ test('R46 process write handler returns only input identity and session state', async () => {
80
+ const server = new FakeServer();
81
+ registerProcessTools(server, { runtime: runtime() });
82
+ const tool = server.tools.get('process_session_write');
83
+ const result = await tool.handler({ session_id: hex('a'), input: 'Write-Output 42', append_newline: true });
84
+ assert.equal(result.isError, false);
85
+ assert.deepEqual(Object.keys(result.structuredContent).sort(), ['append_newline','logical_bytes','logical_sha256','session','status']);
86
+ assert.equal(result.structuredContent.append_newline, true);
87
+ assert.equal(result.structuredContent.session.state, 'running');
88
+ assert.equal(tool.spec.outputSchema.safeParse({ ...result.structuredContent, raw_input: 'forbidden' }).success, false);
89
+ });
@@ -13,18 +13,58 @@ async function fixture() {
13
13
  const root = await mkdtemp(path.join(os.tmpdir(), 'deadbyte-process-refresh-'));
14
14
  const bin = path.join(root, 'bin');
15
15
  const controller = path.join(root, 'controller');
16
+ const systemRoot = path.join(root, 'Windows');
17
+ const system32 = path.join(systemRoot, 'System32');
18
+ const powershellDir = path.join(system32, 'WindowsPowerShell', 'v1.0');
16
19
  await mkdir(bin, { recursive: true });
17
20
  await mkdir(controller, { recursive: true });
21
+ await mkdir(powershellDir, { recursive: true });
22
+
18
23
  const hostPath = path.join(bin, 'deadbyte-process-host.exe');
19
24
  const hostBytes = Buffer.from('fresh-native-host\n');
25
+ const cmdPath = path.join(system32, 'cmd.exe');
26
+ const cmdBytes = Buffer.from('target-cmd\n');
27
+ const powershellPath = path.join(powershellDir, 'powershell.exe');
28
+ const powershellBytes = Buffer.from('target-powershell\n');
20
29
  await writeFile(hostPath, hostBytes);
30
+ await writeFile(cmdPath, cmdBytes);
31
+ await writeFile(powershellPath, powershellBytes);
32
+
21
33
  const policyPath = path.join(controller, 'deadbyte-process-policy.json');
22
- const policy = { schema:'deadbyte.process-policy.v1', enabled:true, grant_file:path.join(root,'runtime','grant.json'),
23
- state_dir:path.join(root,'state'), native_host:{ path:hostPath, sha256:'0'.repeat(64) },
24
- roots:{core:{path:root,deny_paths:[]}}, profiles:{probe:{executable:process.execPath,executable_sha256:'1'.repeat(64),root_ids:['core'],prefix_args:[],max_args:0,max_arg_bytes:256,max_sessions:1,max_input_bytes:4096,max_retained_output_bytes:4096,env_allow:[],input_actions:{}}}};
34
+ const profile = executable => ({
35
+ executable,
36
+ executable_sha256:'1'.repeat(64),
37
+ root_ids:['core'],
38
+ prefix_args:[],
39
+ transport:'pipe',
40
+ max_args:1,
41
+ max_arg_bytes:8192,
42
+ max_sessions:1,
43
+ max_input_bytes:4096,
44
+ max_retained_output_bytes:4096,
45
+ env_allow:[],
46
+ input_actions:{}
47
+ });
48
+ const policy = {
49
+ schema:'deadbyte.process-policy.v1',
50
+ enabled:true,
51
+ grant_file:path.join(root,'runtime','grant.json'),
52
+ state_dir:path.join(root,'state'),
53
+ native_host:{ path:hostPath, sha256:'0'.repeat(64) },
54
+ roots:{core:{path:root,deny_paths:[]}},
55
+ profiles:{
56
+ ping_hold:profile(cmdPath),
57
+ powershell:profile(powershellPath),
58
+ powershell_repl:profile(powershellPath)
59
+ }
60
+ };
25
61
  await writeFile(policyPath, `${JSON.stringify(policy,null,2)}\n`);
26
- return { root, policyPath, hostPath, hostBytes, policy };
62
+ return {
63
+ root, policyPath, hostPath, hostBytes, systemRoot,
64
+ cmdPath, cmdBytes, powershellPath, powershellBytes, policy
65
+ };
27
66
  }
67
+
28
68
  test('R33 candidate process policy self-binds to the current repository root', async () => {
29
69
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
30
70
  const policyPath = path.join(repoRoot, 'controller', 'deadbyte-process-policy.json');
@@ -33,32 +73,69 @@ test('R33 candidate process policy self-binds to the current repository root', a
33
73
  assert.equal(policy.state_dir, path.join(repoRoot, 'controller', 'runtime', 'process-state'));
34
74
  assert.equal(policy.native_host.path, path.join(repoRoot, 'bin', 'deadbyte-process-host.exe'));
35
75
  assert.equal(policy.roots.core.path, repoRoot);
76
+ assert.ok(policy.profiles.ping_hold);
77
+ assert.ok(policy.profiles.powershell);
78
+ assert.ok(policy.profiles.powershell_repl);
79
+ assert.equal(policy.profiles.powershell_repl.allow_raw_input,true);
80
+ assert.deepEqual(policy.profiles.powershell.prefix_args, ['-NoLogo','-NoProfile','-NonInteractive','-Command']);
81
+ assert.equal(policy.profiles.powershell.max_args, 1);
82
+ assert.equal(policy.profiles.powershell.max_arg_bytes, 8192);
36
83
  });
37
84
 
38
- test('R31 refresh changes only the native-host digest after the Windows build', async () => {
85
+ test('process policy refresh repins native host and Windows system executables', async () => {
39
86
  const f = await fixture();
40
- const result = await refreshProcessPolicy({ repoRoot:f.root, policyPath:f.policyPath });
87
+ const result = await refreshProcessPolicy({ repoRoot:f.root, policyPath:f.policyPath, systemRoot:f.systemRoot });
41
88
  const after = JSON.parse(await readFile(f.policyPath,'utf8'));
42
89
  assert.equal(after.native_host.path, f.hostPath);
43
90
  assert.equal(after.native_host.sha256, sha256(f.hostBytes));
91
+ assert.equal(after.profiles.ping_hold.executable, f.cmdPath);
92
+ assert.equal(after.profiles.ping_hold.executable_sha256, sha256(f.cmdBytes));
93
+ assert.equal(after.profiles.powershell.executable, f.powershellPath);
94
+ assert.equal(after.profiles.powershell.executable_sha256, sha256(f.powershellBytes));
95
+ assert.equal(after.profiles.powershell_repl.executable, f.powershellPath);
96
+ assert.equal(after.profiles.powershell_repl.executable_sha256, sha256(f.powershellBytes));
44
97
  assert.equal(result.native_host_sha256, sha256(f.hostBytes));
98
+ assert.equal(result.cmd_executable_sha256, sha256(f.cmdBytes));
99
+ assert.equal(result.powershell_executable_sha256, sha256(f.powershellBytes));
100
+ assert.equal(result.powershell_repl_executable_sha256, sha256(f.powershellBytes));
101
+
45
102
  const expected = structuredClone(f.policy);
46
103
  expected.native_host.sha256 = sha256(f.hostBytes);
104
+ expected.profiles.ping_hold.executable_sha256 = sha256(f.cmdBytes);
105
+ expected.profiles.powershell.executable_sha256 = sha256(f.powershellBytes);
106
+ expected.profiles.powershell_repl.executable_sha256 = sha256(f.powershellBytes);
47
107
  assert.deepEqual(after, expected);
48
108
  });
49
109
 
50
- test('R31 refresh fails closed when native-host path is not the candidate bin path', async () => {
110
+ test('process policy refresh fails closed when native-host path is not the candidate bin path', async () => {
51
111
  const f = await fixture();
52
112
  const raw = JSON.parse(await readFile(f.policyPath,'utf8'));
53
113
  raw.native_host.path = process.execPath;
54
114
  await writeFile(f.policyPath, `${JSON.stringify(raw,null,2)}\n`);
55
- await assert.rejects(() => refreshProcessPolicy({ repoRoot:f.root, policyPath:f.policyPath }), /native_host path must equal candidate bin/);
115
+ await assert.rejects(
116
+ () => refreshProcessPolicy({ repoRoot:f.root, policyPath:f.policyPath, systemRoot:f.systemRoot }),
117
+ /native_host path must equal candidate bin/
118
+ );
119
+ });
120
+
121
+ test('process policy refresh fails closed when a system profile path is not target-bound', async () => {
122
+ const f = await fixture();
123
+ const raw = JSON.parse(await readFile(f.policyPath,'utf8'));
124
+ raw.profiles.powershell.executable = process.execPath;
125
+ await writeFile(f.policyPath, `${JSON.stringify(raw,null,2)}\n`);
126
+ await assert.rejects(
127
+ () => refreshProcessPolicy({ repoRoot:f.root, policyPath:f.policyPath, systemRoot:f.systemRoot }),
128
+ /profile 'powershell' executable path mismatch/
129
+ );
56
130
  });
57
131
 
58
- test('R31 refresh refuses a malformed or disabled process policy', async () => {
132
+ test('process policy refresh refuses a malformed or disabled process policy', async () => {
59
133
  const f = await fixture();
60
134
  const raw = JSON.parse(await readFile(f.policyPath,'utf8'));
61
135
  raw.enabled = false;
62
136
  await writeFile(f.policyPath, `${JSON.stringify(raw,null,2)}\n`);
63
- await assert.rejects(() => refreshProcessPolicy({ repoRoot:f.root, policyPath:f.policyPath }), /schema\/enabled mismatch/);
137
+ await assert.rejects(
138
+ () => refreshProcessPolicy({ repoRoot:f.root, policyPath:f.policyPath, systemRoot:f.systemRoot }),
139
+ /schema\/enabled mismatch/
140
+ );
64
141
  });
@@ -12,6 +12,7 @@ import { generateTrustKeypair } from '../src/trust.mjs';
12
12
  const repo = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
13
13
  const host = path.join(repo, 'bin', 'deadbyte-process-host.exe');
14
14
  const cmd = 'C:\\Windows\\System32\\cmd.exe';
15
+ const powershell = 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
15
16
  const sha = bytes => createHash('sha256').update(bytes).digest('hex');
16
17
  const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
17
18
 
@@ -25,6 +26,7 @@ async function fixture() {
25
26
  const policyPath = path.join(root, 'process-policy.json');
26
27
  const hostSha = sha(await readFile(host));
27
28
  const cmdSha = sha(await readFile(cmd));
29
+ const powershellSha = sha(await readFile(powershell));
28
30
  const raw = {
29
31
  schema: PROCESS_POLICY_SCHEMA, enabled: true, grant_file: grantFile, state_dir: stateDir,
30
32
  native_host: { path: host, sha256: hostSha },
@@ -47,6 +49,18 @@ async function fixture() {
47
49
  prefix_args: ['/d', '/q'], max_args: 0, max_arg_bytes: 256,
48
50
  max_sessions: 1, max_input_bytes: 4096, max_retained_output_bytes: 65536,
49
51
  env_allow: ['PATH', 'TEMP', 'TMP'], input_actions: { ping: 'echo mustnotrun\r\n' }
52
+ },
53
+ powershell: {
54
+ executable: powershell, executable_sha256: powershellSha, root_ids: ['core'], transport: 'pipe',
55
+ prefix_args: ['-NoLogo','-NoProfile','-NonInteractive','-Command'], max_args: 1, max_arg_bytes: 8192,
56
+ max_sessions: 2, max_input_bytes: 65536, max_retained_output_bytes: 1048576,
57
+ env_allow: ['PATH','TEMP','TMP','SystemRoot','WINDIR','USERPROFILE','PSModulePath','COMSPEC','PATHEXT'], input_actions: {}
58
+ },
59
+ powershell_repl: {
60
+ executable: powershell, executable_sha256: powershellSha, root_ids: ['core'], transport: 'pipe', allow_raw_input: true,
61
+ prefix_args: ['-NoLogo','-NoProfile','-NonInteractive'], max_args: 0, max_arg_bytes: 256,
62
+ max_sessions: 2, max_input_bytes: 65536, max_retained_output_bytes: 4 * 1024 * 1024,
63
+ env_allow: ['PATH','TEMP','TMP','SystemRoot','WINDIR','USERPROFILE','PSModulePath','COMSPEC','PATHEXT'], input_actions: {}
50
64
  }
51
65
  }
52
66
  };
@@ -54,7 +68,7 @@ async function fixture() {
54
68
  const policy = await loadProcessPolicy(policyPath);
55
69
  await writeFile(grantFile, JSON.stringify({
56
70
  schema: PROCESS_GRANT_SCHEMA, policy_sha256: policy.policy_sha256,
57
- instance_nonce: 'a'.repeat(64), profile_ids: ['echo', 'hold', 'conptyhold'], root_ids: ['core'],
71
+ instance_nonce: 'a'.repeat(64), profile_ids: ['echo', 'hold', 'conptyhold', 'powershell', 'powershell_repl'], root_ids: ['core'],
58
72
  granted_at_utc: '2026-09-14T00:00:00Z'
59
73
  }));
60
74
  const keys = await generateTrustKeypair({
@@ -90,6 +104,102 @@ test('R31 runtime starts one approved profile and exposes ordered bounded output
90
104
  assert.equal(observed.page.truncated, false);
91
105
  });
92
106
 
107
+ test('R46 runtime executes fileless PowerShell source without ps1 materialization', { skip: process.platform !== 'win32' }, async () => {
108
+ const f = await fixture();
109
+ const source = "$ErrorActionPreference='Stop';$before=@(Get-ChildItem -LiteralPath $env:TEMP -Filter '*.ps1' -File -ErrorAction SilentlyContinue|ForEach-Object{$_.FullName});[Console]::WriteLine('DB_FILELESS_PS_TEST');[Console]::WriteLine('PSCommandPath=' + [string]$PSCommandPath);[Console]::WriteLine('InvocationPath=' + [string]$MyInvocation.MyCommand.Path);$after=@(Get-ChildItem -LiteralPath $env:TEMP -Filter '*.ps1' -File -ErrorAction SilentlyContinue|ForEach-Object{$_.FullName});$new=@($after|Where-Object{$_ -notin $before});[Console]::WriteLine('NEW_TEMP_PS1_COUNT=' + $new.Count)";
110
+ const started = await f.runtime.start({ profileId: 'powershell', rootId: 'core', cwd: '.', args: [source] });
111
+ const observed = await waitFor(f.runtime, started.session_id, text => text.includes('NEW_TEMP_PS1_COUNT=0'));
112
+ assert.match(observed.text, /DB_FILELESS_PS_TEST/);
113
+ assert.match(observed.text, /PSCommandPath=\r?\n/);
114
+ assert.match(observed.text, /InvocationPath=\r?\n/);
115
+ assert.match(observed.text, /NEW_TEMP_PS1_COUNT=0/);
116
+ const deadline = Date.now() + 5000;
117
+ let status = await f.runtime.status({ sessionId: started.session_id });
118
+ while (status.state === 'running' && Date.now() < deadline) {
119
+ await sleep(25);
120
+ status = await f.runtime.status({ sessionId: started.session_id });
121
+ }
122
+ assert.equal(status.state, 'exited');
123
+ assert.equal(status.exit_code, 0);
124
+ const verified = await f.runtime.verifyEventJournal({ sessionId: started.session_id });
125
+ assert.equal(verified.ok, true);
126
+ });
127
+
128
+ test('R46 process output retention evicts oldest bytes without losing tail identity under multi-megabyte output', { skip: process.platform !== 'win32' }, async () => {
129
+ const f = await fixture();
130
+ const source = "[Console]::Write(('A'*2097152));[Console]::WriteLine('DB_RING_TAIL_MARK')";
131
+ const started = await f.runtime.start({ profileId: 'powershell', rootId: 'core', cwd: '.', args: [source] });
132
+ const deadline = Date.now() + 10000;
133
+ let status = await f.runtime.status({ sessionId: started.session_id });
134
+ while (status.state === 'running' && Date.now() < deadline) {
135
+ await sleep(25);
136
+ status = await f.runtime.status({ sessionId: started.session_id });
137
+ }
138
+ assert.equal(status.state, 'exited');
139
+ assert.equal(status.exit_code, 0);
140
+ assert.ok(status.stream_head > 2 * 1024 * 1024);
141
+ assert.ok(status.earliest_offset > 0);
142
+ assert.ok(status.stream_head - status.earliest_offset <= 1024 * 1024);
143
+ const fromStart = await f.runtime.read({ sessionId: started.session_id, offset: 0, maxBytes: 4096 });
144
+ assert.equal(fromStart.truncated, true);
145
+ assert.equal(fromStart.offset, status.earliest_offset);
146
+ const tail = await f.runtime.read({ sessionId: started.session_id, offset: -4096, maxBytes: 4096 });
147
+ const tailText = Buffer.from(tail.data_base64, 'base64').toString('utf8');
148
+ assert.match(tailText, /DB_RING_TAIL_MARK/);
149
+ const verified = await f.runtime.verifyEventJournal({ sessionId: started.session_id });
150
+ assert.equal(verified.ok, true);
151
+ });
152
+
153
+ test('R46 process raw input keeps persistent PowerShell state with bounded wait and tail reads', { skip: process.platform !== 'win32' }, async () => {
154
+ const f = await fixture();
155
+ const started = await f.runtime.start({ profileId: 'powershell_repl', rootId: 'core', cwd: '.', args: [] });
156
+ try {
157
+ const first = await f.runtime.write({ sessionId: started.session_id, input: "$x=41;[Console]::WriteLine('DB_REPL_SET')", appendNewline: true });
158
+ assert.equal(first.session.state, 'running');
159
+ assert.match(first.logical_sha256, /^[0-9a-f]{64}$/);
160
+ const page1 = await f.runtime.read({ sessionId: started.session_id, offset: 0, maxBytes: 65536, waitMs: 3000, waitFor: 'DB_REPL_SET' });
161
+ const text1 = Buffer.from(page1.data_base64, 'base64').toString('utf8');
162
+ assert.match(text1, /DB_REPL_SET/);
163
+
164
+ const beforeSecond = (await f.runtime.status({ sessionId: started.session_id })).stream_head;
165
+ await f.runtime.write({ sessionId: started.session_id, input: "[Console]::WriteLine('DB_REPL_VALUE=' + ($x+1))", appendNewline: true });
166
+ const waited = await f.runtime.read({ sessionId: started.session_id, offset: beforeSecond, maxBytes: 65536, waitMs: 3000, waitFor: 'DB_REPL_VALUE=42' });
167
+ const waitedText = Buffer.from(waited.data_base64, 'base64').toString('utf8');
168
+ assert.equal(waited.wait_satisfied, true);
169
+ assert.match(waitedText, /DB_REPL_VALUE=42/);
170
+ const tail = await f.runtime.read({ sessionId: started.session_id, offset: -4096, maxBytes: 4096, waitMs: 0 });
171
+ const tailText = Buffer.from(tail.data_base64, 'base64').toString('utf8');
172
+ assert.match(tailText, /DB_REPL_VALUE=42/);
173
+
174
+ const journal = await readFile(path.join(f.stateDir, `${started.session_id}.jsonl`), 'utf8');
175
+ assert.doesNotMatch(journal, /DB_REPL_SET|DB_REPL_VALUE|\$x=41/);
176
+ const verified = await f.runtime.verifyEventJournal({ sessionId: started.session_id });
177
+ assert.equal(verified.ok, true);
178
+ assert.ok(verified.event_count >= 3);
179
+ } finally {
180
+ await f.runtime.terminate({ sessionId: started.session_id }).catch(() => {});
181
+ }
182
+ });
183
+
184
+ test('R46 raw process input is profile-opt-in, bounded, and rechecks standing authority', { skip: process.platform !== 'win32' }, async () => {
185
+ const f = await fixture();
186
+ const hold = await f.runtime.start({ profileId: 'hold', rootId: 'core', cwd: '.', args: [] });
187
+ try {
188
+ await assert.rejects(() => f.runtime.write({ sessionId: hold.session_id, input: 'echo forbidden', appendNewline: true }), /does not allow raw input/);
189
+ } finally {
190
+ await f.runtime.terminate({ sessionId: hold.session_id }).catch(() => {});
191
+ }
192
+
193
+ const repl = await f.runtime.start({ profileId: 'powershell_repl', rootId: 'core', cwd: '.', args: [] });
194
+ try {
195
+ await assert.rejects(() => f.runtime.write({ sessionId: repl.session_id, input: 'x'.repeat(65536), appendNewline: true }), /exceeds profile input bound/);
196
+ await rm(f.grantFile);
197
+ await assert.rejects(() => f.runtime.write({ sessionId: repl.session_id, input: 'Write-Output denied', appendNewline: true }), /process plane is disarmed/);
198
+ } finally {
199
+ await f.runtime.terminate({ sessionId: repl.session_id }).catch(() => {});
200
+ }
201
+ });
202
+
93
203
  test('R31 runtime lists, reports and terminates only its owned session', { skip: process.platform !== 'win32' }, async () => {
94
204
  const f = await fixture();
95
205
  const started = await f.runtime.start({ profileId: 'hold', rootId: 'core', cwd: '.', args: [] });
@@ -7,7 +7,7 @@ const runtimePath = fileURLToPath(new URL('../src/process-runtime.mjs', import.m
7
7
  const serverPath = fileURLToPath(new URL('../src/mcp-server.mjs', import.meta.url));
8
8
  const toolsPath = fileURLToPath(new URL('../src/process-mcp-tools.mjs', import.meta.url));
9
9
 
10
- test('R32 process supervision is policy-gated and registers the bounded named-action tool', async () => {
10
+ test('R46 process supervision is policy-gated and exposes bounded action plus opt-in raw input', async () => {
11
11
  const runtimeSource = await readFile(runtimePath, 'utf8');
12
12
  const serverSource = await readFile(serverPath, 'utf8');
13
13
  const toolsSource = await readFile(toolsPath, 'utf8');
@@ -20,5 +20,7 @@ test('R32 process supervision is policy-gated and registers the bounded named-ac
20
20
  assert.match(serverSource, /processRuntime/);
21
21
  assert.match(toolsSource, /process_session_action/);
22
22
  assert.match(toolsSource, /action_id/);
23
- assert.doesNotMatch(toolsSource, /raw_stdin|raw_input/);
23
+ assert.match(toolsSource, /process_session_write/);
24
+ assert.match(toolsSource, /append_newline/);
25
+ assert.doesNotMatch(toolsSource, /caller-selected executable|raw_stdin/);
24
26
  });
@@ -9,9 +9,9 @@ const readme=()=>readFile(path.join(root,'README.txt'),'utf8');
9
9
 
10
10
  test('production README documents one-command stdio configs for major MCP clients',async()=>{
11
11
  const text=await readme();
12
- assert.match(text,/npx -y deadbyte-mcp@0\.11\.3 mcp/);
12
+ assert.match(text,/npx -y deadbyte-mcp@0\.15\.2 mcp/);
13
13
  assert.match(text,/CLAUDE DESKTOP/);
14
- assert.match(text,/"mcpServers"[\s\S]*?"command"\s*:\s*"npx"[\s\S]*?"-y"[\s\S]*?"deadbyte-mcp@0\.11\.3"[\s\S]*?"mcp"/);
14
+ assert.match(text,/"mcpServers"[\s\S]*?"command"\s*:\s*"npx"[\s\S]*?"-y"[\s\S]*?"deadbyte-mcp@0\.15\.2"[\s\S]*?"mcp"/);
15
15
  assert.match(text,/CURSOR/);
16
16
  assert.match(text,/\.cursor\/mcp\.json/);
17
17
  assert.match(text,/VS CODE/);
@@ -20,7 +20,7 @@ test('production README documents one-command stdio configs for major MCP client
20
20
  assert.match(text,/CODEX/);
21
21
  assert.match(text,/\[mcp_servers\.deadbyte\]/);
22
22
  assert.match(text,/command\s*=\s*"npx"/);
23
- assert.match(text,/args\s*=\s*\["-y",\s*"deadbyte-mcp@0\.11\.3",\s*"mcp"\]/);
23
+ assert.match(text,/args\s*=\s*\["-y",\s*"deadbyte-mcp@0\.15\.2",\s*"mcp"\]/);
24
24
  });
25
25
 
26
26
  test('production README records tracer bullet, semantic scope, remote UX, safety and document extension',async()=>{
@@ -86,23 +86,24 @@ test('audit restart reconciles an orphan request with an explicit interrupted re
86
86
  }finally{await rm(stateRoot,{recursive:true,force:true});}
87
87
  });
88
88
 
89
- test('Windows current R44 gate emits signed-R43-parent feature evidence outside the candidate tree', async()=>{
90
- const stateRoot=await temp('deadbyte-r44-windows-evidence-');
91
- try{
92
- const gate=await readFile(path.join(root,'scripts','gate-windows-r44.ps1'),'utf8');
93
- assert.match(gate,/windows-gate-evidence-r44\.mjs/);assert.match(gate,/r44-parent-verify/);
94
- const manifestSha=sha256(await readFile(path.join(root,'MANIFEST.SHA256')));
95
- const {writeWindowsGateEvidence}=await import('../scripts/windows-gate-evidence-r44.mjs');
96
- const summary=await writeWindowsGateEvidence({candidateRoot:root,stateRoot,clock:()=>new Date('2026-09-21T00:01:00.000Z')});
97
- const evidence=JSON.parse(await readFile(summary.path,'utf8'));
98
- assert.equal(evidence.schema,'deadbyte.r44-windows-gate.v1');assert.equal(evidence.version,'0.15.0');
99
- assert.equal(evidence.manifest_sha256,manifestSha);
100
- assert.equal(evidence.marker,'DEADBYTE V0.15.0 / R44 REMOTE AGENT SKILL WINDOWS BATCH GATE: PASS');
101
- assert.equal(evidence.parent.release_id,'v0.14.1-0a5a833892593283');
102
- assert.equal(evidence.parent.final_closure_sha256,'2f29e94dd726638c23b2e520a608dcf2a6919e43348855be3940599d557f920b');
103
- for(const key of ['skill_integrity','agent_backend_persistence','codex_protocol_contract','remote_device_identity','durable_job_supervisor','real_external_supervisor_smoke','full_regression_required']) assert.equal(evidence.features[key],true,key);
104
- assert.ok(summary.path.startsWith(path.join(stateRoot,'evidence','release')));
105
- const historical=await readFile(path.join(root,'scripts','windows-gate-evidence-r43.mjs'),'utf8');
106
- assert.match(historical,/version:'0\.14\.1'/);assert.match(historical,/deadbyte\.r43-windows-gate\.v1/);
107
- }finally{await rm(stateRoot,{recursive:true,force:true});}
89
+ test('Windows current R45 gate binds exact sealed R44 parent and machine-freedom evidence contract', async()=>{
90
+ const gate=await readFile(path.join(root,'scripts','gate-windows-r45.ps1'),'utf8');
91
+ const evidenceSource=await readFile(path.join(root,'scripts','windows-gate-evidence-r45.mjs'),'utf8');
92
+ const parent=JSON.parse(await readFile(path.join(root,'R45-PARENT.json'),'utf8'));
93
+ assert.match(gate,/windows-gate-evidence-r45\.mjs/);
94
+ assert.match(gate,/r45-parent-verify/);
95
+ assert.match(gate,/host-policy-refresh/);
96
+ assert.match(gate,/hardware-build/);
97
+ assert.match(gate,/doctor/);
98
+ assert.match(evidenceSource,/deadbyte\.r45-windows-gate\.v1/);
99
+ assert.match(evidenceSource,/version:'0\.15\.1'/);
100
+ assert.match(evidenceSource,/DEADBYTE V0\.15\.1 \/ R45 MACHINE FREEDOM WINDOWS BATCH GATE: PASS/);
101
+ for(const key of ['host_inline_native','native_hardware_observation','doctor_release_truth']) assert.match(evidenceSource,new RegExp(key+':true'));
102
+ assert.equal(parent.parent_release_id,'v0.15.0-c15fa41719be61b3');
103
+ assert.equal(parent.parent_manifest_sha256,'c15fa41719be61b3186e49b2367a5f4c057365cfb465102dbefb7d7355f45483');
104
+ assert.equal(parent.parent_generation_manifest_sha256,'6d7429c050b7fe893492e102f00282019116e0c9e54e4f25a5791defa1661d30');
105
+ assert.equal(parent.parent_final_closure_sha256,'533b7e0ff9ded04086dd90ed34e3b87bc427acc3c55c25bb9f4ec66cb448e667');
106
+ const historical=await readFile(path.join(root,'scripts','windows-gate-evidence-r44.mjs'),'utf8');
107
+ assert.match(historical,/version:'0\.15\.0'/);
108
+ assert.match(historical,/deadbyte\.r44-windows-gate\.v1/);
108
109
  });
@@ -19,9 +19,9 @@ test('R34 public parity catalog is exact, unique, and includes production coding
19
19
 
20
20
  test('release parity and final verifier are package entry points', async () => {
21
21
  const pkg = JSON.parse(await text('package.json'));
22
- assert.equal(pkg.scripts['release:parity'], 'node scripts/release-parity-r44.mjs');
23
- assert.equal(pkg.scripts['release:final:write'], 'node scripts/final-closure-r44.mjs');
24
- assert.equal(pkg.scripts['release:final:verify'], 'node scripts/final-closure-verify-r44.mjs');
22
+ assert.equal(pkg.scripts['release:parity'], 'node scripts/release-parity-r46.mjs');
23
+ assert.equal(pkg.scripts['release:final:write'], 'node scripts/final-closure-r46.mjs');
24
+ assert.equal(pkg.scripts['release:final:verify'], 'node scripts/final-closure-verify-r46.mjs');
25
25
  assert.equal(typeof validateParityEvidence, 'function');
26
26
  assert.equal(typeof verifyFinalClosureObject, 'function');
27
27
  });
@@ -0,0 +1,10 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { readFile } from 'node:fs/promises';
4
+
5
+ test('R45 autonomous quick-test is fixture-scoped and does not invoke legacy npm test matrix', async () => {
6
+ const text=await readFile(new URL('../scripts/autonomous-tests.ps1',import.meta.url),'utf8');
7
+ assert.match(text,/node\.exe --test test\/autonomous-live-fixture\.test\.mjs/);
8
+ assert.doesNotMatch(text,/npm\.cmd test/);
9
+ assert.match(text,/R24 QUICK TEST PASS/);
10
+ });