deadbyte-mcp 0.11.2 → 0.12.0

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 (102) hide show
  1. package/MANIFEST.SHA256 +101 -39
  2. package/README.txt +10 -9
  3. package/acceptance/r40/desktop-live-drift.mjs +106 -0
  4. package/acceptance/r40/desktop-live-ground.mjs +110 -0
  5. package/acceptance/r40/desktop-live-input.mjs +128 -0
  6. package/acceptance/r40/desktop-live-observe.mjs +100 -0
  7. package/acceptance/r40/desktop-live-uia.mjs +121 -0
  8. package/bin/deadbyte-desktop-input-r40.exe +0 -0
  9. package/bin/deadbyte-desktop-input-r40.obj +0 -0
  10. package/bin/deadbyte-desktop-observer-r40.exe +0 -0
  11. package/bin/deadbyte-desktop-observer-r40.obj +0 -0
  12. package/bin/deadbyte-desktop-uia-r40.exe +0 -0
  13. package/bin/deadbyte-desktop-uia-r40.obj +0 -0
  14. package/controller/README.TXT +7 -5
  15. package/controller/deadbyte-controller.config.psd1.example +3 -0
  16. package/controller/deadbyte-controller.ps1 +149 -4
  17. package/controller/deadbyte-desktop-policy.json +41 -0
  18. package/docs/ARCHITECTURE.md +6 -1
  19. package/docs/superpowers/specs/2026-09-19-r40-desktop-computer-use-design.md +214 -0
  20. package/native/deadbyte-desktop-input.cpp +455 -0
  21. package/native/deadbyte-desktop-observer.cpp +253 -0
  22. package/native/deadbyte-desktop-uia.cpp +307 -0
  23. package/package.json +10 -8
  24. package/scripts/build-desktop-r40.ps1 +106 -0
  25. package/scripts/deferred-slot-operation-r39.mjs +124 -0
  26. package/scripts/deferred-slot-operation-r40.mjs +126 -0
  27. package/scripts/final-closure-r39.mjs +113 -0
  28. package/scripts/final-closure-r40.mjs +114 -0
  29. package/scripts/final-closure-verify-r39.mjs +208 -0
  30. package/scripts/final-closure-verify-r40.mjs +212 -0
  31. package/scripts/gate-windows-r39.ps1 +42 -0
  32. package/scripts/gate-windows-r40.ps1 +43 -0
  33. package/scripts/init-desktop-policy-r40.ps1 +79 -0
  34. package/scripts/r40-compact-ledger-probe.mjs +4 -0
  35. package/scripts/release-parity-r39.mjs +200 -0
  36. package/scripts/release-parity-r40.mjs +212 -0
  37. package/scripts/release-parity-tests-r39.ps1 +9 -0
  38. package/scripts/release-parity-tests-r40.ps1 +9 -0
  39. package/scripts/windows-gate-evidence-r39.mjs +52 -0
  40. package/scripts/windows-gate-evidence-r40.mjs +52 -0
  41. package/src/autonomous-context-engine.mjs +2 -1
  42. package/src/autonomous-planner-contracts.mjs +19 -1
  43. package/src/autonomous-planner.mjs +22 -0
  44. package/src/autonomous-runtime.mjs +144 -24
  45. package/src/capability-ledger.mjs +6 -1
  46. package/src/compact-gateway-mcp-tools.mjs +14 -2
  47. package/src/deadbyte-cli.mjs +79 -47
  48. package/src/desktop-contract.mjs +263 -0
  49. package/src/desktop-input-r40.mjs +148 -0
  50. package/src/desktop-mcp-tools-r40.mjs +126 -0
  51. package/src/desktop-memory-r40.mjs +272 -0
  52. package/src/desktop-observer-r40.mjs +231 -0
  53. package/src/desktop-policy-r40.mjs +110 -0
  54. package/src/desktop-runtime-r40.mjs +318 -0
  55. package/src/desktop-uia-r40.mjs +123 -0
  56. package/src/mcp-server.mjs +74 -19
  57. package/src/r40-compact-disarmed-probe.mjs +71 -0
  58. package/src/r40-full-surface-probe.mjs +34 -0
  59. package/src/release-generation.mjs +38 -5
  60. package/src/remote-console.mjs +41 -0
  61. package/src/remote-live-log.mjs +215 -45
  62. package/src/remote-mcp-desktop-r40-smoke-client.mjs +70 -0
  63. package/src/remote-mcp-r40-parity-client.mjs +200 -0
  64. package/src/remote-mcp-smoke-client.mjs +20 -6
  65. package/src/remote-result-journal.mjs +10 -2
  66. package/src/runtime-update.mjs +55 -17
  67. package/src/tool-audit.mjs +29 -2
  68. package/src/version.mjs +1 -1
  69. package/test/autonomous-capability-r36.test.mjs +43 -1
  70. package/test/autonomous-desktop-r40.test.mjs +188 -0
  71. package/test/capability-ledger-r36.test.mjs +1 -0
  72. package/test/cli-entrypoint.test.mjs +1 -1
  73. package/test/compact-catalog-r36.test.mjs +2 -2
  74. package/test/compact-gateway-r36.test.mjs +56 -4
  75. package/test/compact-max-catalog-r40.test.mjs +95 -0
  76. package/test/controller-desktop-r40.test.mjs +85 -0
  77. package/test/core.test.mjs +1 -1
  78. package/test/desktop-adversarial-r40.test.mjs +62 -0
  79. package/test/desktop-contract-r40.test.mjs +200 -0
  80. package/test/desktop-input-r40.test.mjs +63 -0
  81. package/test/desktop-mcp-tools-r40.test.mjs +61 -0
  82. package/test/desktop-memory-r40.test.mjs +141 -0
  83. package/test/desktop-observer-r40.test.mjs +119 -0
  84. package/test/desktop-policy-r40.test.mjs +111 -0
  85. package/test/desktop-runtime-r40.test.mjs +278 -0
  86. package/test/desktop-uia-r40.test.mjs +42 -0
  87. package/test/full-max-catalog-r40.test.mjs +91 -0
  88. package/test/helpers/autonomous-r34-fixture.mjs +3 -3
  89. package/test/production-docs.test.mjs +3 -3
  90. package/test/r33-closeout-regression.test.mjs +8 -8
  91. package/test/r33-finalization.test.mjs +14 -14
  92. package/test/r37-remote-tool-call-timeline.test.mjs +7 -6
  93. package/test/r39-finalization.test.mjs +74 -0
  94. package/test/r40-finalization.test.mjs +118 -0
  95. package/test/release-generation.test.mjs +41 -9
  96. package/test/release-version.test.mjs +19 -58
  97. package/test/remote-console-r40.test.mjs +38 -0
  98. package/test/remote-live-log-r37.test.mjs +89 -32
  99. package/test/remote-r24-catalog.test.mjs +12 -3
  100. package/test/remote-result-journal.test.mjs +15 -0
  101. package/test/remote-session-cli.test.mjs +112 -55
  102. package/test/runtime-self-update.test.mjs +44 -0
@@ -0,0 +1,123 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { spawn } from 'node:child_process';
3
+ import { readFile } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+
6
+ export const DESKTOP_UIA_GROUND_SCHEMA='deadbyte.desktop-uia-ground.v1';
7
+ const HEX64=/^[0-9a-f]{64}$/;
8
+ const HWND=/^0x[0-9a-f]{1,16}$/;
9
+ const QUERY_KINDS=new Set(['root','name_exact','name_contains','automation_id_exact','control_type']);
10
+ const sha256Bytes=value=>createHash('sha256').update(value).digest('hex');
11
+
12
+ function insist(c,m){if(!c)throw new Error(m);}
13
+ function integer(v,label,{min=Number.MIN_SAFE_INTEGER,max=Number.MAX_SAFE_INTEGER}={}){
14
+ insist(Number.isSafeInteger(v)&&v>=min&&v<=max,label+' invalid');return v;
15
+ }
16
+ function hwnd(v){const s=String(v??'').toLowerCase();insist(HWND.test(s),'desktop UIA hwnd invalid');return s;}
17
+ function queryInput(query){
18
+ insist(query&&typeof query==='object'&&!Array.isArray(query),'desktop UIA query invalid');
19
+ const kind=String(query.kind??'');
20
+ insist(QUERY_KINDS.has(kind),'desktop UIA query kind invalid');
21
+ const value=query.value??'';
22
+ insist(typeof value==='string'&&!value.includes('\0'),'desktop UIA query value invalid');
23
+ const bytes=Buffer.from(value,'utf8');
24
+ if(kind==='root') insist(bytes.length===0,'desktop UIA root query cannot carry value');
25
+ else insist(bytes.length>=1&&bytes.length<=512,'desktop UIA query value bytes invalid');
26
+ const maxResults=integer(query.max_results??8,'desktop UIA max_results',{min:1,max:16});
27
+ const ordinal=integer(query.ordinal??0,'desktop UIA ordinal',{min:0,max:maxResults-1});
28
+ const querySha256=sha256Bytes(Buffer.from(JSON.stringify({kind,value,max_results:maxResults,ordinal}),'utf8'));
29
+ return {kind,value,bytes,maxResults,ordinal,querySha256};
30
+ }
31
+
32
+ export function serializeDesktopUiaRequest({expectedWindow,query}={}){
33
+ insist(expectedWindow&&typeof expectedWindow==='object','desktop UIA expected window missing');
34
+ const expectedHwnd=hwnd(expectedWindow.hwnd);
35
+ const expectedPid=integer(expectedWindow.pid,'desktop UIA expected pid',{min:1,max:0x7fffffff});
36
+ const q=queryInput(query);
37
+ const header=Buffer.from([
38
+ 'DEADBYTE_DESKTOP_UIA_V1',
39
+ 'expected_hwnd='+expectedHwnd,
40
+ 'expected_pid='+expectedPid,
41
+ 'query_kind='+q.kind,
42
+ 'max_results='+q.maxResults,
43
+ 'query_bytes='+q.bytes.length,
44
+ ''
45
+ ].join('\n'),'utf8');
46
+ const request=Buffer.concat([header,q.bytes]);
47
+ insist(request.length<=8192,'desktop UIA request exceeds bound');
48
+ return {request,query:q};
49
+ }
50
+
51
+ export function parseDesktopUiaResponse(bytes,{expectedWindow,query,expectedBounds}={}){
52
+ insist(Buffer.isBuffer(bytes)&&bytes.length>=2&&bytes.length<=256*1024,'desktop UIA response invalid');
53
+ let raw;try{raw=JSON.parse(bytes.toString('utf8').trim());}catch{throw new Error('desktop UIA response JSON invalid');}
54
+ insist(raw?.schema===DESKTOP_UIA_GROUND_SCHEMA,'desktop UIA response schema mismatch');
55
+ const q=queryInput(query);
56
+ insist(String(raw.query_kind)===q.kind,'desktop UIA response query mismatch');
57
+ insist(hwnd(raw.foreground_hwnd)===hwnd(expectedWindow.hwnd),'desktop UIA response hwnd mismatch');
58
+ insist(integer(raw.foreground_pid,'desktop UIA response pid',{min:1,max:0x7fffffff})===expectedWindow.pid,'desktop UIA response pid mismatch');
59
+ integer(raw.visited,'desktop UIA visited',{min:0,max:2048});
60
+ insist(typeof raw.truncated==='boolean','desktop UIA truncated invalid');
61
+ insist(Array.isArray(raw.matches)&&raw.matches.length<=q.maxResults,'desktop UIA matches invalid');
62
+ const left=integer(expectedBounds.left,'desktop UIA bounds left',{min:-131072,max:131072});
63
+ const top=integer(expectedBounds.top,'desktop UIA bounds top',{min:-131072,max:131072});
64
+ const width=integer(expectedBounds.width,'desktop UIA bounds width',{min:1,max:131072});
65
+ const height=integer(expectedBounds.height,'desktop UIA bounds height',{min:1,max:131072});
66
+ const matches=[];
67
+ for(const item of raw.matches){
68
+ const rect={
69
+ left:integer(item.left,'desktop UIA rect left',{min:-131072,max:131072}),
70
+ top:integer(item.top,'desktop UIA rect top',{min:-131072,max:131072}),
71
+ width:integer(item.width,'desktop UIA rect width',{min:1,max:131072}),
72
+ height:integer(item.height,'desktop UIA rect height',{min:1,max:131072})
73
+ };
74
+ const inside=rect.left>=left&&rect.top>=top&&rect.left+rect.width<=left+width&&rect.top+rect.height<=top+height;
75
+ if(!inside) continue;
76
+ matches.push(Object.freeze({
77
+ rect:Object.freeze(rect),
78
+ control_type:integer(item.control_type,'desktop UIA control_type',{min:0,max:100000}),
79
+ enabled:item.enabled===true,offscreen:item.offscreen===true
80
+ }));
81
+ }
82
+ return Object.freeze({
83
+ status:'ok',query_sha256:q.querySha256,query_kind:q.kind,ordinal:q.ordinal,
84
+ visited:raw.visited,truncated:raw.truncated,matches:Object.freeze(matches),
85
+ provider_receipt_sha256:sha256Bytes(bytes)
86
+ });
87
+ }
88
+
89
+ export async function invokeNativeDesktopGrounder({grounderPath,expectedGrounderSha256,timeoutMs=5000,expectedWindow,expectedBounds,query}={}){
90
+ insist(typeof grounderPath==='string'&&path.isAbsolute(grounderPath),'desktop grounderPath must be absolute');
91
+ insist(HEX64.test(expectedGrounderSha256??''),'desktop expectedGrounderSha256 invalid');
92
+ integer(timeoutMs,'desktop UIA timeoutMs',{min:100,max:30000});
93
+ const exe=await readFile(grounderPath);
94
+ insist(exe.length>0&&exe.length<=4*1024*1024,'desktop UIA binary size invalid');
95
+ insist(sha256Bytes(exe)===expectedGrounderSha256,'desktop UIA executable hash mismatch');
96
+ const packed=serializeDesktopUiaRequest({expectedWindow,query});
97
+ return await new Promise((resolve,reject)=>{
98
+ const child=spawn(grounderPath,[],{shell:false,windowsHide:true,stdio:['pipe','pipe','pipe']});
99
+ const stdout=[];let bytes=0,stderr='',done=false,timer=null;
100
+ const finish=(error,value)=>{if(done)return;done=true;if(timer!==null)clearTimeout(timer);error?reject(error):resolve(value);};
101
+ child.stdout.on('data',chunk=>{bytes+=chunk.length;if(bytes>256*1024){try{child.kill('SIGKILL');}catch{};return finish(new Error('desktop UIA stdout exceeds bound'));}stdout.push(Buffer.from(chunk));});
102
+ child.stderr.on('data',chunk=>{stderr+=chunk.toString('utf8');if(Buffer.byteLength(stderr,'utf8')>64*1024){try{child.kill('SIGKILL');}catch{};finish(new Error('desktop UIA stderr exceeds bound'));}});
103
+ child.once('error',error=>finish(error));
104
+ child.once('exit',(code,signal)=>{
105
+ if(code!==0)return finish(new Error('desktop UIA exit='+code+' signal='+(signal??'none')+' stderr='+stderr.slice(-8192)));
106
+ try{finish(null,parseDesktopUiaResponse(Buffer.concat(stdout),{expectedWindow,query,expectedBounds}));}
107
+ catch(error){finish(error);}
108
+ });
109
+ child.stdin.on('error',error=>finish(error));
110
+ child.stdin.end(packed.request);
111
+ timer=setTimeout(()=>{try{child.kill('SIGKILL');}catch{};finish(new Error('desktop UIA timed out after '+timeoutMs+'ms'));},timeoutMs);
112
+ });
113
+ }
114
+
115
+ export function createNativeDesktopGrounder({grounderPath,expectedGrounderSha256,timeoutMs=5000}={}){
116
+ return async({frame,query}={})=>{
117
+ insist(frame?.active_window,'desktop UIA grounding requires foreground window');
118
+ return invokeNativeDesktopGrounder({
119
+ grounderPath,expectedGrounderSha256,timeoutMs,
120
+ expectedWindow:frame.active_window,expectedBounds:frame.bounds,query
121
+ });
122
+ };
123
+ }
@@ -9,7 +9,7 @@ import { executeProof } from './executor.mjs';
9
9
  import { CONTAINED_TRANSFORM_OPERATIONS, executeContainedTransform } from './contained-executor.mjs';
10
10
  import { resolveExistingInside, resolveWorkspace } from './path-policy.mjs';
11
11
  import { verifyReceipt } from './verifier.mjs';
12
- import { defaultPrivateKeyPath, defaultPublicKeyPath } from './trust.mjs';
12
+ import { defaultPrivateKeyPath, defaultPublicKeyPath, loadPrivateSigningKey, loadPublicVerifyKey } from './trust.mjs';
13
13
  import { DEADBYTE_VERSION } from './version.mjs';
14
14
  import { currentIngressAdapterMode, TUNNEL_STDIO_PROBE_COMPAT_MODE } from './ingress-mode.mjs';
15
15
  import { installTunnelStdioProbeCompat } from './tunnel-stdio-compat.mjs';
@@ -31,6 +31,13 @@ import { createMachineFsRuntime } from './machine-fs.mjs';
31
31
  import { registerMachineFsTools } from './machine-fs-mcp-tools.mjs';
32
32
  import { createObservationRuntime } from './observation-runtime.mjs';
33
33
  import { registerObservationTools } from './observation-mcp-tools.mjs';
34
+ import { loadDesktopPolicy } from './desktop-policy-r40.mjs';
35
+ import { createDesktopObserverRuntime } from './desktop-observer-r40.mjs';
36
+ import { createNativeDesktopInputProvider } from './desktop-input-r40.mjs';
37
+ import { createNativeDesktopGrounder } from './desktop-uia-r40.mjs';
38
+ import { createDesktopRuntime } from './desktop-runtime-r40.mjs';
39
+ import { createDesktopMemoryProvider } from './desktop-memory-r40.mjs';
40
+ import { registerDesktopTools } from './desktop-mcp-tools-r40.mjs';
34
41
  import { loadAutonomousPolicy } from './autonomous-policy.mjs';
35
42
  import { createNativeDiffRuntime } from './native-diff.mjs';
36
43
  import { createAutonomousRuntime } from './autonomous-runtime.mjs';
@@ -125,6 +132,44 @@ const observationRuntime = observationRuntimeMode === '1'
125
132
  stateRoot: runtimeStateRoot })
126
133
  : null;
127
134
 
135
+ const desktopRuntimeMode=process.env.DEADBYTE_DESKTOP_RUNTIME??'0';
136
+ if(!['0','1'].includes(desktopRuntimeMode)) throw new Error('DEADBYTE_DESKTOP_RUNTIME must be 0 or 1');
137
+ const localDesktopPolicyPath=path.join(packageRoot,'controller','deadbyte-desktop-policy.json');
138
+ let desktopPolicyPath=process.env.DEADBYTE_DESKTOP_POLICY??'';
139
+ if(desktopRuntimeMode==='1'&&!desktopPolicyPath){
140
+ try{await access(localDesktopPolicyPath);desktopPolicyPath=localDesktopPolicyPath;}
141
+ catch(error){if(error?.code!=='ENOENT')throw error;}
142
+ }
143
+ if(desktopRuntimeMode==='1'&&!desktopPolicyPath) throw new Error('DEADBYTE_DESKTOP_RUNTIME=1 requires a desktop policy');
144
+ const desktopPolicy=desktopRuntimeMode==='1'?await loadDesktopPolicy(desktopPolicyPath):null;
145
+ const desktopObserverRuntime=desktopPolicy?createDesktopObserverRuntime({
146
+ observerPath:desktopPolicy.observer.executable,expectedObserverSha256:desktopPolicy.observer.sha256,
147
+ maxFrameBytes:desktopPolicy.limits.max_frame_bytes,timeoutMs:desktopPolicy.limits.capture_timeout_ms,
148
+ allowInputDesktops:desktopPolicy.allowed_input_desktops
149
+ }):null;
150
+ const desktopInputProvider=desktopPolicy?createNativeDesktopInputProvider({
151
+ inputPath:desktopPolicy.input.executable,expectedInputSha256:desktopPolicy.input.sha256,
152
+ timeoutMs:desktopPolicy.limits.input_timeout_ms
153
+ }):null;
154
+ const desktopGrounderProvider=desktopPolicy?createNativeDesktopGrounder({
155
+ grounderPath:desktopPolicy.grounder.executable,expectedGrounderSha256:desktopPolicy.grounder.sha256,
156
+ timeoutMs:desktopPolicy.limits.ground_timeout_ms
157
+ }):null;
158
+ const desktopSigningKey=desktopPolicy?await loadPrivateSigningKey(signingKeyPath):null;
159
+ const desktopVerifyKey=desktopPolicy?await loadPublicVerifyKey(verifyKeyPath):null;
160
+ if(desktopPolicy&&desktopSigningKey.keyId!==desktopVerifyKey.keyId) throw new Error('Desktop signing/verify key mismatch');
161
+ const desktopRuntime=desktopPolicy?createDesktopRuntime({
162
+ policy:desktopPolicy,observerRuntime:desktopObserverRuntime,inputProvider:desktopInputProvider,
163
+ grounderProvider:desktopGrounderProvider,
164
+ privateKey:desktopSigningKey.privateKey,publicKey:desktopVerifyKey.publicKey,keyId:desktopSigningKey.keyId
165
+ }):null;
166
+ const desktopMemoryProvider=desktopPolicy?createDesktopMemoryProvider({
167
+ evidenceDir:desktopPolicy.evidence_dir,
168
+ snapshotPath:path.join(desktopPolicy.evidence_dir,'desktop-memory.json'),
169
+ policySha256:desktopPolicy.policy_sha256,
170
+ privateKey:desktopSigningKey.privateKey,publicKey:desktopVerifyKey.publicKey,keyId:desktopSigningKey.keyId
171
+ }):null;
172
+
128
173
  const autonomousRuntimeMode = process.env.DEADBYTE_AUTONOMOUS_RUNTIME ?? '0';
129
174
  if (!['0','1'].includes(autonomousRuntimeMode)) throw new Error('DEADBYTE_AUTONOMOUS_RUNTIME must be 0 or 1');
130
175
  const autonomousSupervisorMode = process.env.DEADBYTE_AUTONOMOUS_SUPERVISOR_MODE ?? 'in_process';
@@ -143,7 +188,7 @@ const autonomousPolicy = autonomousRuntimeMode === '1' ? await loadAutonomousPol
143
188
  const nativeDiffRuntime = autonomousPolicy ? createNativeDiffRuntime({ policy:autonomousPolicy,codingRuntime,machineFsRuntime,signingKeyPath,verifyKeyPath }) : null;
144
189
  semanticRuntime = codingRuntime ? createSemanticCodeRuntime({ codingRuntime, backends: semanticLsp.backends, nativeDiffRuntime }) : null;
145
190
  const autonomousRuntime = autonomousPolicy ? createAutonomousRuntime({ policy:autonomousPolicy,codingRuntime,nativeDiffRuntime,
146
- machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,signingKeyPath,verifyKeyPath }) : null;
191
+ machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime,desktopMemoryProvider,signingKeyPath,verifyKeyPath }) : null;
147
192
  const autonomousSupervisor = autonomousRuntime && autonomousPolicy.planner.backend === 'openai_compatible' &&
148
193
  autonomousSupervisorMode === 'in_process'
149
194
  ? startAutonomousSupervisor({ runtime: autonomousRuntime, policy: autonomousPolicy })
@@ -185,11 +230,17 @@ function filteredToolServer(server,allowed){
185
230
 
186
231
  serveStdio(() => {
187
232
  const server = new McpServer({ name: 'deadbyte-mcp', version: DEADBYTE_VERSION });
233
+ const registeredToolNames=new Set();
234
+ const toolServer={registerTool(name,...args){
235
+ if(registeredToolNames.has(name)) throw new Error(`duplicate tool registration: ${name}`);
236
+ registeredToolNames.add(name);
237
+ return server.registerTool(name,...args);
238
+ }};
188
239
  registerOperatorSurface(server,{packageRoot,surfaceProfile,compactToolLimit:COMPACT_SURFACE_TOOL_LIMIT});
189
240
  if (toolAudit) installToolAudit(server,toolAudit);
190
241
 
191
242
  if (surfaceProfile === 'full') {
192
- server.registerTool(
243
+ toolServer.registerTool(
193
244
  'workspace_open',
194
245
  {
195
246
  description: 'Open a named workspace below the configured DEADBYTE workspace root.',
@@ -206,7 +257,7 @@ serveStdio(() => {
206
257
  }
207
258
  );
208
259
 
209
- server.registerTool(
260
+ toolServer.registerTool(
210
261
  'file_read',
211
262
  {
212
263
  description: 'Read one UTF-8 file inside a named workspace. Path traversal and symlink escape are denied.',
@@ -229,7 +280,7 @@ serveStdio(() => {
229
280
  }
230
281
  );
231
282
 
232
- server.registerTool(
283
+ toolServer.registerTool(
233
284
  'proof_execute',
234
285
  {
235
286
  description: 'Run the fixed DEADBYTE V0 proof worker inside one workspace and produce an Ed25519-signed hashed receipt. This is not a general shell.',
@@ -263,7 +314,7 @@ serveStdio(() => {
263
314
  }
264
315
  );
265
316
 
266
- server.registerTool(
317
+ toolServer.registerTool(
267
318
  'contained_transform',
268
319
  {
269
320
  description: 'Run one compile-time allowlisted ASCII transform inside the proven Windows AppContainer + atomic Job boundary. Input is copied into a private sandbox profile; the host workspace is never mounted. This is not a shell and does not accept an executable path.',
@@ -306,7 +357,7 @@ serveStdio(() => {
306
357
 
307
358
  }
308
359
 
309
- server.registerTool(
360
+ toolServer.registerTool(
310
361
  'evidence_verify',
311
362
  {
312
363
  description: 'Independently verify Ed25519 receipt authenticity and recompute file, stream, and worker hashes.',
@@ -327,7 +378,7 @@ serveStdio(() => {
327
378
  );
328
379
 
329
380
  if (machineRuntime) {
330
- registerMachineTools(server, {
381
+ registerMachineTools(toolServer, {
331
382
  runtime: machineRuntime,
332
383
  assertRevokeAuthority: async () => {
333
384
  if (!hostPolicy) throw new Error('privileged host plane is not configured');
@@ -337,37 +388,41 @@ serveStdio(() => {
337
388
  }
338
389
 
339
390
  if (agentRuntime && surfaceProfile === 'full') {
340
- registerAgentTools(server, { runtime: agentRuntime, workspacePathFromId });
391
+ registerAgentTools(toolServer, { runtime: agentRuntime, workspacePathFromId });
341
392
  }
342
393
 
343
394
  if (processRuntime && surfaceProfile === 'full') {
344
- registerProcessTools(server, { runtime: processRuntime });
395
+ registerProcessTools(toolServer, { runtime: processRuntime });
345
396
  }
346
397
 
347
398
  if (codingRuntime && codingTaskRuntime && semanticRuntime) {
348
- const codingServer=surfaceProfile==='full'?server:filteredToolServer(server,COMPACT_CODING_DIRECT_TOOLS);
399
+ const codingServer=surfaceProfile==='full'?toolServer:filteredToolServer(toolServer,COMPACT_CODING_DIRECT_TOOLS);
349
400
  registerCodingTools(codingServer, { codingRuntime, taskRuntime: codingTaskRuntime, semanticRuntime });
350
401
  }
351
402
 
352
403
  if (machineFsRuntime && surfaceProfile === 'full') {
353
- registerMachineFsTools(server, { runtime: machineFsRuntime });
404
+ registerMachineFsTools(toolServer, { runtime: machineFsRuntime });
354
405
  }
355
406
 
356
407
  if (observationRuntime && surfaceProfile === 'full') {
357
- registerObservationTools(server, { runtime: observationRuntime });
408
+ registerObservationTools(toolServer, { runtime: observationRuntime });
409
+ }
410
+
411
+ if (desktopRuntime && surfaceProfile === 'full') {
412
+ registerDesktopTools(toolServer,{runtime:desktopRuntime});
358
413
  }
359
414
 
360
415
  if (autonomousRuntime && nativeDiffRuntime) {
361
- registerAutonomousTools(server, { autonomousRuntime, nativeDiffRuntime });
416
+ registerAutonomousTools(toolServer, { autonomousRuntime, nativeDiffRuntime });
362
417
  }
363
418
 
364
- registerCompactGatewayTools(server,{
419
+ registerCompactGatewayTools(toolServer,{
365
420
  profile:surfaceProfile,ledger:R36_CAPABILITY_LEDGER,codingRuntime,semanticRuntime,machineFsRuntime,
366
- processRuntime,observationRuntime,machineRuntime
421
+ processRuntime,observationRuntime,machineRuntime,desktopRuntime,catalogProvider:()=>[...registeredToolNames]
367
422
  });
368
423
 
369
424
  if (hostPolicy) {
370
- server.registerTool(
425
+ toolServer.registerTool(
371
426
  'host_file_read',
372
427
  {
373
428
  description: 'Privileged host-plane read from a locally allowlisted root. Requires a live local arm lease; path traversal and symlink escape are denied.',
@@ -388,7 +443,7 @@ serveStdio(() => {
388
443
  }
389
444
  );
390
445
 
391
- server.registerTool(
446
+ toolServer.registerTool(
392
447
  'host_file_write',
393
448
  {
394
449
  description: 'Privileged host-plane hash-precondition file write inside a locally allowlisted root. Commit is same-directory atomic rename and post-commit SHA-256 verified. Requires a live local arm lease.',
@@ -417,7 +472,7 @@ serveStdio(() => {
417
472
  }
418
473
  );
419
474
 
420
- server.registerTool(
475
+ toolServer.registerTool(
421
476
  'host_exec',
422
477
  {
423
478
  description: 'Privileged host-plane script execution through a locally allowlisted and SHA-256-pinned executable. No raw shell command string is accepted. Requires a live local arm lease.',
@@ -0,0 +1,71 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { readFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
7
+ import { R40_COMPACT_GATEWAYS,R40_COMPACT_TOOL_LIMIT } from './remote-mcp-r40-parity-client.mjs';
8
+ import { loadDesktopPolicy } from './desktop-policy-r40.mjs';
9
+ import { verifyDesktopReceiptObject } from './desktop-runtime-r40.mjs';
10
+ import { loadPublicVerifyKey } from './trust.mjs';
11
+
12
+ const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
13
+ const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
14
+ const verifyKeyPath=path.join(stateRoot,'trust','ed25519-public.pem');
15
+ function structured(result,label){
16
+ if(result.isError) throw new Error(label+' returned error: '+JSON.stringify(result.structuredContent??result.content));
17
+ if(!result.structuredContent||typeof result.structuredContent!=='object') throw new Error(label+' missing structured output');
18
+ return result.structuredContent;
19
+ }
20
+ function gateway(result,label){
21
+ const value=structured(result,label);
22
+ if(value.status!=='ok'||!value.result||typeof value.result!=='object') throw new Error(label+' gateway status='+String(value.status));
23
+ return value.result;
24
+ }
25
+ async function verifyDesktop(policy,verifyKey,id,sha,kind){
26
+ const receipt=JSON.parse(await readFile(path.join(policy.evidence_dir,id+'.json'),'utf8'));
27
+ const proof=verifyDesktopReceiptObject(receipt,{policySha256:policy.policy_sha256,publicKey:verifyKey.publicKey,keyId:verifyKey.keyId});
28
+ if(!proof.ok||proof.receipt_sha256!==sha||proof.kind!==kind) throw new Error('R40 desktop '+kind+' receipt verification failed');
29
+ }
30
+ export async function probeR40CompactDisarmed(endpointText){
31
+ const endpoint=new URL(endpointText);if(endpoint.protocol!=='https:')throw new Error('R40 compact disarmed probe requires HTTPS');
32
+ const pointer=JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
33
+ const desktopPolicy=await loadDesktopPolicy(path.join(pointer.generation_root,'desktop-policy.json'));
34
+ const verifyKey=await loadPublicVerifyKey(verifyKeyPath);
35
+ const client=new Client({name:'deadbyte-r40-compact-disarmed-probe',version:'0.12.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
36
+ const transport=new StreamableHTTPClientTransport(endpoint);
37
+ try{
38
+ await client.connect(transport);
39
+ const listed=await client.listTools(),names=listed.tools.map(t=>t.name).sort();
40
+ if(names.length!==R40_COMPACT_TOOL_LIMIT)throw new Error('R40 compact disarmed catalog must be exactly '+R40_COMPACT_TOOL_LIMIT+', got '+names.length);
41
+ for(const name of R40_COMPACT_GATEWAYS)if(!names.includes(name))throw new Error('R40 compact gateway missing '+name);
42
+ for(const tool of listed.tools)if(tool.outputSchema?.type!=='object'||tool.outputSchema?.additionalProperties!==false||!tool.outputSchema?.properties)throw new Error('tool '+tool.name+' missing strict output schema');
43
+ const manifest=gateway(await client.callTool({name:'capability_manifest',arguments:{}}),'capability_manifest');
44
+ if(manifest.schema!=='deadbyte.capability-manifest.v2'||manifest.profile!=='compact'||manifest.tool_count!==names.length||
45
+ JSON.stringify(manifest.tool_catalog)!==JSON.stringify(names))throw new Error('R40 compact manifest malformed');
46
+ const required=manifest.capabilities.filter(x=>x?.production===true&&x?.host_only!==true);
47
+ for(const item of required)if(typeof item.compact_tool!=='string'||!names.includes(item.compact_tool))throw new Error('compact route missing '+String(item.id));
48
+ const coding=structured(await client.callTool({name:'coding_session_status',arguments:{}}),'coding status');
49
+ const autonomous=structured(await client.callTool({name:'autonomous_session_status',arguments:{}}),'autonomous status');
50
+ const desktop=gateway(await client.callTool({name:'desktop_control',arguments:{op:'status'}}),'desktop status');
51
+ if(coding.armed!==false||autonomous.coding_armed!==false||autonomous.autonomous_armed!==false||desktop.armed!==false)throw new Error('R40 closure requires Coding/Autonomous/Desktop DISARMED');
52
+ const observed=gateway(await client.callTool({name:'desktop_control',arguments:{op:'observe',preview:false}}),'desktop observe');
53
+ await verifyDesktop(desktopPolicy,verifyKey,observed.receipt_id,observed.receipt_sha256,'desktop_observation');
54
+ const grounded=gateway(await client.callTool({name:'desktop_control',arguments:{op:'target',frame_id:observed.frame.frame_id,uia_query:{kind:'root',value:'',max_results:1,ordinal:0}}}),'desktop target');
55
+ await verifyDesktop(desktopPolicy,verifyKey,grounded.grounding.receipt_id,grounded.grounding.receipt_sha256,'desktop_grounding');
56
+ const machine=gateway(await client.callTool({name:'machine_observe',arguments:{op:'machine_status'}}),'machine status');
57
+ const nonce='r40-final-'+randomBytes(16).toString('hex');
58
+ const ping=gateway(await client.callTool({name:'machine_observe',arguments:{op:'machine_ping',nonce}}),'machine ping');
59
+ if(ping.nonce!==nonce||ping.release_id!==machine.release_id||ping.release_manifest_sha256!==machine.release_manifest_sha256)throw new Error('R40 machine ping binding mismatch');
60
+ return {status:'passed',profile:'compact',tool_count:names.length,tools:names,catalog_sha256:sha256(Buffer.from(JSON.stringify(names),'utf8')),
61
+ strict_output_schemas:true,ledger_sha256:manifest.ledger_sha256,capability_count:manifest.capabilities.length,
62
+ production_non_host_capabilities:required.length,capability_closure:true,coding_armed:false,autonomous_armed:false,desktop_armed:false,
63
+ desktop_observation_receipt_verified:true,desktop_grounding_receipt_verified:true,
64
+ desktop_frame_id:observed.frame.frame_id,desktop_target_id:grounded.target.target_id,
65
+ release_id:machine.release_id,release_manifest_sha256:machine.release_manifest_sha256,machine_status:machine,machine_ping:ping,nonce,
66
+ protocol_revision:client.getNegotiatedProtocolVersion()};
67
+ }finally{await client.close().catch(()=>{});}
68
+ }
69
+ const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
70
+ if(invoked){const endpoint=process.argv[2];if(!endpoint)throw new Error('usage: node src/r40-compact-disarmed-probe.mjs https://host/mcp/path');
71
+ probeR40CompactDisarmed(endpoint).then(v=>console.log(JSON.stringify(v,null,2))).catch(e=>{console.error(e instanceof Error?e.stack:String(e));process.exitCode=1;});}
@@ -0,0 +1,34 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { Client } from '@modelcontextprotocol/client';
6
+ import { StdioClientTransport,getDefaultEnvironment } from '@modelcontextprotocol/client/stdio';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { R40_EXPECTED_TOOLS } from './remote-mcp-r40-parity-client.mjs';
9
+ const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
10
+ export async function probeR40FullSurface({packageRoot,generationRoot,stateRoot=path.join(os.homedir(),'.deadbyte-mcp')}={}){
11
+ const pkgRoot=packageRoot?path.resolve(packageRoot):path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
12
+ const gen=path.resolve(generationRoot),semanticPath=path.join(gen,'semantic-lsp-config.json');
13
+ const semanticSha=sha256(await readFile(semanticPath));
14
+ const env={...getDefaultEnvironment(),DEADBYTE_STATE_ROOT:path.resolve(stateRoot),DEADBYTE_CORE_ROOT:pkgRoot,
15
+ DEADBYTE_WORKSPACE_ROOT:path.join(pkgRoot,'workspaces'),DEADBYTE_CAPABILITY_PROFILE:'full',DEADBYTE_AUDIT_RUNTIME:'0',
16
+ DEADBYTE_MACHINE_RUNTIME:'1',DEADBYTE_AGENT_RUNTIME:'1',DEADBYTE_PROCESS_RUNTIME:'1',DEADBYTE_PROCESS_POLICY:path.join(gen,'process-policy.json'),
17
+ DEADBYTE_CODING_RUNTIME:'1',DEADBYTE_MACHINE_FS_RUNTIME:'1',DEADBYTE_OBSERVATION_RUNTIME:'1',
18
+ DEADBYTE_DESKTOP_RUNTIME:'1',DEADBYTE_DESKTOP_POLICY:path.join(gen,'desktop-policy.json'),
19
+ DEADBYTE_CODING_POLICY:path.join(gen,'coding-policy.json'),DEADBYTE_SEMANTIC_LSP_CONFIG:semanticPath,DEADBYTE_SEMANTIC_LSP_CONFIG_SHA256:semanticSha,
20
+ DEADBYTE_AUTONOMOUS_RUNTIME:'1',DEADBYTE_AUTONOMOUS_POLICY:path.join(gen,'autonomous-policy.json'),
21
+ DEADBYTE_AUTONOMOUS_SUPERVISOR_MODE:'external',DEADBYTE_PRIVILEGED_POLICY:path.join(gen,'host-policy.json')};
22
+ const client=new Client({name:'deadbyte-r40-full-surface-probe',version:'0.12.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
23
+ const transport=new StdioClientTransport({command:process.execPath,args:[path.join(pkgRoot,'src','mcp-server.mjs')],cwd:pkgRoot,env,stderr:'pipe'});
24
+ try{
25
+ await client.connect(transport);const listed=await client.listTools(),names=listed.tools.map(t=>t.name).sort();
26
+ if(JSON.stringify(names)!==JSON.stringify(R40_EXPECTED_TOOLS))throw new Error('R40 full catalog mismatch count='+names.length);
27
+ for(const tool of listed.tools)if(tool.outputSchema?.type!=='object'||tool.outputSchema?.additionalProperties!==false||!tool.outputSchema?.properties)throw new Error('R40 full tool '+tool.name+' missing strict output schema');
28
+ return {status:'passed',profile:'full',tool_count:names.length,tools:names,catalog_sha256:sha256(Buffer.from(JSON.stringify(names),'utf8')),
29
+ strict_output_schemas:true,desktop_tools:['desktop_status','desktop_observe','desktop_target','desktop_action'],protocol_revision:client.getNegotiatedProtocolVersion()};
30
+ }finally{await client.close().catch(()=>{});}
31
+ }
32
+ if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){const [packageRoot,generationRoot]=process.argv.slice(2);
33
+ if(!packageRoot||!generationRoot)throw new Error('usage: node src/r40-full-surface-probe.mjs <package-root> <generation-root>');
34
+ probeR40FullSurface({packageRoot,generationRoot}).then(v=>console.log(JSON.stringify(v,null,2))).catch(e=>{console.error(e instanceof Error?e.stack:String(e));process.exitCode=1;});}
@@ -118,7 +118,31 @@ async function rebindProcessPolicy(raw,{developmentRoot,stateRoot,releaseRoot}){
118
118
  return policy;
119
119
  }
120
120
 
121
- function renderControllerConfig({currentConfig,coreRoot,workspaceRoot,processPolicyPath,codingPolicyPath,semanticConfigPath,semanticConfigSha256,autonomousPolicyPath,hostPolicyPath}){
121
+ async function rebindDesktopPolicy(raw,{stateRoot,releaseRoot}){
122
+ if(!raw) return null;
123
+ const policy=clone(raw);
124
+ if(policy.schema!=='deadbyte.desktop-policy.v1'||policy.enabled!==true) throw new Error('desktop policy schema/enabled mismatch');
125
+ policy.grant_file=path.join(stateRoot,'runtime','desktop-grant.json');
126
+ policy.evidence_dir=path.join(stateRoot,'runtime','desktop-evidence');
127
+ const providers={
128
+ observer:'deadbyte-desktop-observer-r40.exe',
129
+ input:'deadbyte-desktop-input-r40.exe',
130
+ grounder:'deadbyte-desktop-uia-r40.exe'
131
+ };
132
+ for(const [name,file] of Object.entries(providers)){
133
+ const provider=policy[name];
134
+ if(!provider||typeof provider.sha256!=='string'||!HEX64.test(provider.sha256)) throw new Error(`desktop ${name} policy pin invalid`);
135
+ const providerPath=path.join(releaseRoot,'bin',file);
136
+ const stat=await lstat(providerPath);
137
+ if(!stat.isFile()||stat.isSymbolicLink()) throw new Error(`desktop ${name} helper invalid in release slot`);
138
+ const actual=sha256(await readFile(providerPath));
139
+ if(actual!==provider.sha256) throw new Error(`desktop ${name} helper SHA-256 mismatch: policy=${provider.sha256} release=${actual}`);
140
+ provider.executable=providerPath;
141
+ }
142
+ return policy;
143
+ }
144
+
145
+ function renderControllerConfig({currentConfig,coreRoot,workspaceRoot,processPolicyPath,desktopPolicyPath,codingPolicyPath,semanticConfigPath,semanticConfigSha256,autonomousPolicyPath,hostPolicyPath}){
122
146
  const supervisor=currentConfig.Supervisor??{},bridge=currentConfig.Bridge??{},cloud=currentConfig.Cloud??{};
123
147
  return [
124
148
  '@{'," Schema = 'deadbyte.controller.v1'",` CoreRoot = ${psQuote(coreRoot)}`,
@@ -130,6 +154,8 @@ function renderControllerConfig({currentConfig,coreRoot,workspaceRoot,processPol
130
154
  ` CodingRuntimeEnabled = ${psBool(currentConfig.CodingRuntimeEnabled)}`,
131
155
  ` MachineFsRuntimeEnabled = ${psBool(currentConfig.MachineFsRuntimeEnabled)}`,
132
156
  ` ObservationRuntimeEnabled = ${psBool(currentConfig.ObservationRuntimeEnabled)}`,
157
+ ` DesktopRuntimeEnabled = ${psBool(currentConfig.DesktopRuntimeEnabled)}`,
158
+ ` DesktopPolicyFile = ${psQuote(desktopPolicyPath??'')}`,
133
159
  ` CodingPolicyFile = ${psQuote(codingPolicyPath)}`,
134
160
  ` SemanticLspConfigFile = ${psQuote(semanticConfigPath??'')}`,
135
161
  ` SemanticLspConfigSha256 = ${psQuote(semanticConfigSha256??'')}`,
@@ -172,7 +198,7 @@ function renderControllerConfig({currentConfig,coreRoot,workspaceRoot,processPol
172
198
  '}',''
173
199
  ].join('\r\n');
174
200
  }
175
- export async function createGenerationBundle({stateRoot,releaseRoot,developmentRoot,workspaceRoot,releaseId,currentConfig,processPolicy,codingPolicy,autonomousPolicy,hostPolicy}){
201
+ export async function createGenerationBundle({stateRoot,releaseRoot,developmentRoot,workspaceRoot,releaseId,currentConfig,processPolicy,desktopPolicy,codingPolicy,autonomousPolicy,hostPolicy}){
176
202
  const state=path.resolve(stateRoot),release=path.resolve(releaseRoot),development=path.resolve(developmentRoot),workspace=path.resolve(workspaceRoot);
177
203
  if(typeof releaseId!=='string'||!/^[A-Za-z0-9._-]{1,96}$/.test(releaseId)) throw new Error('generation release id invalid');
178
204
  if(typeof currentConfig?.Bridge?.FileSha256!=='string'||!HEX64.test(currentConfig.Bridge.FileSha256)) throw new Error('generation Bridge FileSha256 invalid');
@@ -230,6 +256,9 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
230
256
  const hostBytes=host?jsonBytes(host):null;
231
257
  const processBound=await rebindProcessPolicy(processPolicy,{developmentRoot:development,stateRoot:state,releaseRoot:release});
232
258
  const processBytes=processBound?jsonBytes(processBound):null;
259
+ const desktopBound=await rebindDesktopPolicy(desktopPolicy,{stateRoot:state,releaseRoot:release});
260
+ const desktopBytes=desktopBound?jsonBytes(desktopBound):null;
261
+ if(currentConfig.DesktopRuntimeEnabled===true&&!desktopBytes) throw new Error('DesktopRuntimeEnabled requires desktop policy');
233
262
  const seedBody=canonical({
234
263
  release_id:releaseId,core_root:release,development_root:development,workspace_root:workspace,
235
264
  machine_runtime_enabled:currentConfig.MachineRuntimeEnabled!==false,
@@ -238,6 +267,7 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
238
267
  coding_runtime_enabled:currentConfig.CodingRuntimeEnabled===true,
239
268
  machine_fs_runtime_enabled:currentConfig.MachineFsRuntimeEnabled===true,
240
269
  observation_runtime_enabled:currentConfig.ObservationRuntimeEnabled===true,
270
+ desktop_runtime_enabled:currentConfig.DesktopRuntimeEnabled===true,
241
271
  autonomous_runtime_enabled:currentConfig.AutonomousRuntimeEnabled===true,
242
272
  autonomous_supervisor_enabled:currentConfig.AutonomousSupervisorEnabled===true,
243
273
  supervisor:currentConfig.AutonomousSupervisorEnabled===true?(currentConfig.Supervisor??{}):null,
@@ -247,6 +277,7 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
247
277
  start_timeout_seconds:Number(currentConfig.StartTimeoutSeconds??30),probe_timeout_seconds:Number(currentConfig.ProbeTimeoutSeconds??3),
248
278
  semantic_lsp_config_sha256:semanticConfigSha256,
249
279
  process_policy_sha256:processBytes?sha256(processBytes):null,
280
+ desktop_policy_sha256:desktopBytes?sha256(desktopBytes):null,
250
281
  coding_policy_sha256:sha256(codingBytes),autonomous_policy_sha256:sha256(autonomousBytes),host_policy_sha256:hostBytes?sha256(hostBytes):null
251
282
  });
252
283
  const generationSeed=sha256(Buffer.from(JSON.stringify(seedBody),'utf8'));
@@ -254,6 +285,7 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
254
285
  const generationsRoot=path.join(state,'generations');
255
286
  const generationRoot=path.join(generationsRoot,generationId);
256
287
  const processPath=processBytes?path.join(generationRoot,'process-policy.json'):null;
288
+ const desktopPath=desktopBytes?path.join(generationRoot,'desktop-policy.json'):null;
257
289
  const codingPath=path.join(generationRoot,'coding-policy.json');
258
290
  const semanticPath=semanticBytes?path.join(generationRoot,'semantic-lsp-config.json'):null;
259
291
  const autonomousPath=path.join(generationRoot,'autonomous-policy.json');
@@ -262,15 +294,16 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
262
294
  const files=new Map([
263
295
  ['coding-policy.json',codingBytes],
264
296
  ['autonomous-policy.json',autonomousBytes],
265
- ['controller-config.psd1',Buffer.from(renderControllerConfig({currentConfig,coreRoot:release,workspaceRoot:workspace,processPolicyPath:processPath,codingPolicyPath:codingPath,semanticConfigPath:semanticPath,semanticConfigSha256,autonomousPolicyPath:autonomousPath,hostPolicyPath:hostPath}),'utf8')]
297
+ ['controller-config.psd1',Buffer.from(renderControllerConfig({currentConfig,coreRoot:release,workspaceRoot:workspace,processPolicyPath:processPath,desktopPolicyPath:desktopPath,codingPolicyPath:codingPath,semanticConfigPath:semanticPath,semanticConfigSha256,autonomousPolicyPath:autonomousPath,hostPolicyPath:hostPath}),'utf8')]
266
298
  ]);
267
299
  if(semanticBytes) files.set('semantic-lsp-config.json',semanticBytes);
268
300
  if(processBytes) files.set('process-policy.json',processBytes);
301
+ if(desktopBytes) files.set('desktop-policy.json',desktopBytes);
269
302
  if(hostBytes) files.set('host-policy.json',hostBytes);
270
303
  const manifestBytes=generationManifest(files),manifestSha256=sha256(manifestBytes);
271
304
  if(existsSync(generationRoot)){
272
305
  const verified=await verifyGenerationBundle(generationRoot,manifestSha256);
273
- return {generation_id:generationId,generation_seed_sha256:generationSeed,generation_root:generationRoot,generation_manifest_sha256:manifestSha256,config_path:configPath,process_policy_path:processPath,coding_policy_path:codingPath,semantic_config_path:semanticPath,autonomous_policy_path:autonomousPath,host_policy_path:hostPath,adopted:true,...verified};
306
+ return {generation_id:generationId,generation_seed_sha256:generationSeed,generation_root:generationRoot,generation_manifest_sha256:manifestSha256,config_path:configPath,process_policy_path:processPath,desktop_policy_path:desktopPath,coding_policy_path:codingPath,semantic_config_path:semanticPath,autonomous_policy_path:autonomousPath,host_policy_path:hostPath,adopted:true,...verified};
274
307
  }
275
308
  await mkdir(generationsRoot,{recursive:true});
276
309
  const staging=path.join(generationsRoot,`.staging-${generationId}-${process.pid}-${randomBytes(4).toString('hex')}`);
@@ -285,5 +318,5 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
285
318
  throw error;
286
319
  }
287
320
  const verified=await verifyGenerationBundle(generationRoot,manifestSha256);
288
- return {generation_id:generationId,generation_seed_sha256:generationSeed,generation_root:generationRoot,generation_manifest_sha256:manifestSha256,config_path:configPath,process_policy_path:processPath,coding_policy_path:codingPath,semantic_config_path:semanticPath,autonomous_policy_path:autonomousPath,host_policy_path:hostPath,adopted:false,...verified};
321
+ return {generation_id:generationId,generation_seed_sha256:generationSeed,generation_root:generationRoot,generation_manifest_sha256:manifestSha256,config_path:configPath,process_policy_path:processPath,desktop_policy_path:desktopPath,coding_policy_path:codingPath,semantic_config_path:semanticPath,autonomous_policy_path:autonomousPath,host_policy_path:hostPath,adopted:false,...verified};
289
322
  }
@@ -0,0 +1,41 @@
1
+ import path from 'node:path';
2
+ import { spawnSync } from 'node:child_process';
3
+
4
+ export const REMOTE_DISPLAY_MODES=Object.freeze(['unicode','ascii']);
5
+
6
+ function checkedMode(mode){
7
+ if(!REMOTE_DISPLAY_MODES.includes(mode)) throw new Error(`remote display mode invalid: ${String(mode)}`);
8
+ return mode;
9
+ }
10
+
11
+ export function remoteDisplayText(value,mode='unicode'){
12
+ checkedMode(mode);
13
+ const text=String(value??'');
14
+ if(mode==='unicode') return text;
15
+ let out='';
16
+ for(const ch of text){
17
+ const cp=ch.codePointAt(0);
18
+ if(cp===9||cp===10||cp===13||(cp>=0x20&&cp<=0x7e)) out+=ch;
19
+ else out+=`\\u{${cp.toString(16).toUpperCase()}}`;
20
+ }
21
+ return out;
22
+ }
23
+
24
+ export function prepareRemoteConsole({
25
+ platform=process.platform,
26
+ isTTY=Boolean(process.stdout?.isTTY),
27
+ env=process.env,
28
+ spawnSyncFn=spawnSync
29
+ }={}){
30
+ if(String(env?.DEADBYTE_REMOTE_ASCII??'')==='1') return Object.freeze({mode:'ascii',code_page:null,reason:'forced_ascii'});
31
+ if(platform!=='win32') return Object.freeze({mode:'unicode',code_page:null,reason:'non_windows'});
32
+ if(!isTTY) return Object.freeze({mode:'ascii',code_page:null,reason:'stdout_not_tty'});
33
+ const systemRoot=env?.SystemRoot||env?.WINDIR;
34
+ if(typeof systemRoot!=='string'||!systemRoot) return Object.freeze({mode:'ascii',code_page:null,reason:'system_root_missing'});
35
+ const chcp=path.win32.join(systemRoot,'System32','chcp.com');
36
+ let result;
37
+ try{ result=spawnSyncFn(chcp,['65001'],{stdio:'ignore',windowsHide:true,shell:false}); }
38
+ catch{ return Object.freeze({mode:'ascii',code_page:null,reason:'codepage_probe_threw'}); }
39
+ if(result?.error||result?.status!==0) return Object.freeze({mode:'ascii',code_page:null,reason:'codepage_65001_unavailable'});
40
+ return Object.freeze({mode:'unicode',code_page:65001,reason:'codepage_65001'});
41
+ }