deadbyte-mcp 0.11.3 → 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.
- package/MANIFEST.SHA256 +92 -38
- package/README.txt +5 -4
- package/acceptance/r40/desktop-live-drift.mjs +106 -0
- package/acceptance/r40/desktop-live-ground.mjs +110 -0
- package/acceptance/r40/desktop-live-input.mjs +128 -0
- package/acceptance/r40/desktop-live-observe.mjs +100 -0
- package/acceptance/r40/desktop-live-uia.mjs +121 -0
- package/bin/deadbyte-desktop-input-r40.exe +0 -0
- package/bin/deadbyte-desktop-input-r40.obj +0 -0
- package/bin/deadbyte-desktop-observer-r40.exe +0 -0
- package/bin/deadbyte-desktop-observer-r40.obj +0 -0
- package/bin/deadbyte-desktop-uia-r40.exe +0 -0
- package/bin/deadbyte-desktop-uia-r40.obj +0 -0
- package/controller/README.TXT +7 -5
- package/controller/deadbyte-controller.config.psd1.example +3 -0
- package/controller/deadbyte-controller.ps1 +149 -4
- package/controller/deadbyte-desktop-policy.json +41 -0
- package/docs/ARCHITECTURE.md +6 -1
- package/docs/superpowers/specs/2026-09-19-r40-desktop-computer-use-design.md +214 -0
- package/native/deadbyte-desktop-input.cpp +455 -0
- package/native/deadbyte-desktop-observer.cpp +253 -0
- package/native/deadbyte-desktop-uia.cpp +307 -0
- package/package.json +10 -8
- package/scripts/build-desktop-r40.ps1 +106 -0
- package/scripts/deferred-slot-operation-r40.mjs +126 -0
- package/scripts/final-closure-r40.mjs +114 -0
- package/scripts/final-closure-verify-r40.mjs +212 -0
- package/scripts/gate-windows-r40.ps1 +43 -0
- package/scripts/init-desktop-policy-r40.ps1 +79 -0
- package/scripts/r40-compact-ledger-probe.mjs +4 -0
- package/scripts/release-parity-r40.mjs +212 -0
- package/scripts/release-parity-tests-r40.ps1 +9 -0
- package/scripts/windows-gate-evidence-r40.mjs +52 -0
- package/src/autonomous-context-engine.mjs +2 -1
- package/src/autonomous-planner-contracts.mjs +19 -1
- package/src/autonomous-planner.mjs +22 -0
- package/src/autonomous-runtime.mjs +144 -24
- package/src/capability-ledger.mjs +6 -1
- package/src/compact-gateway-mcp-tools.mjs +14 -2
- package/src/deadbyte-cli.mjs +72 -44
- package/src/desktop-contract.mjs +263 -0
- package/src/desktop-input-r40.mjs +148 -0
- package/src/desktop-mcp-tools-r40.mjs +126 -0
- package/src/desktop-memory-r40.mjs +272 -0
- package/src/desktop-observer-r40.mjs +231 -0
- package/src/desktop-policy-r40.mjs +110 -0
- package/src/desktop-runtime-r40.mjs +318 -0
- package/src/desktop-uia-r40.mjs +123 -0
- package/src/mcp-server.mjs +74 -19
- package/src/r40-compact-disarmed-probe.mjs +71 -0
- package/src/r40-full-surface-probe.mjs +34 -0
- package/src/release-generation.mjs +38 -5
- package/src/remote-console.mjs +41 -0
- package/src/remote-live-log.mjs +208 -45
- package/src/remote-mcp-desktop-r40-smoke-client.mjs +70 -0
- package/src/remote-mcp-r40-parity-client.mjs +200 -0
- package/src/remote-mcp-smoke-client.mjs +20 -6
- package/src/remote-result-journal.mjs +10 -2
- package/src/runtime-update.mjs +55 -17
- package/src/tool-audit.mjs +29 -2
- package/src/version.mjs +1 -1
- package/test/autonomous-capability-r36.test.mjs +43 -1
- package/test/autonomous-desktop-r40.test.mjs +188 -0
- package/test/capability-ledger-r36.test.mjs +1 -0
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/compact-catalog-r36.test.mjs +2 -2
- package/test/compact-gateway-r36.test.mjs +56 -4
- package/test/compact-max-catalog-r40.test.mjs +95 -0
- package/test/controller-desktop-r40.test.mjs +85 -0
- package/test/core.test.mjs +1 -1
- package/test/desktop-adversarial-r40.test.mjs +62 -0
- package/test/desktop-contract-r40.test.mjs +200 -0
- package/test/desktop-input-r40.test.mjs +63 -0
- package/test/desktop-mcp-tools-r40.test.mjs +61 -0
- package/test/desktop-memory-r40.test.mjs +141 -0
- package/test/desktop-observer-r40.test.mjs +119 -0
- package/test/desktop-policy-r40.test.mjs +111 -0
- package/test/desktop-runtime-r40.test.mjs +278 -0
- package/test/desktop-uia-r40.test.mjs +42 -0
- package/test/full-max-catalog-r40.test.mjs +91 -0
- package/test/helpers/autonomous-r34-fixture.mjs +3 -3
- package/test/r33-closeout-regression.test.mjs +7 -7
- package/test/r33-finalization.test.mjs +14 -14
- package/test/r37-remote-tool-call-timeline.test.mjs +6 -5
- package/test/r40-finalization.test.mjs +118 -0
- package/test/release-generation.test.mjs +41 -9
- package/test/release-version.test.mjs +19 -61
- package/test/remote-console-r40.test.mjs +38 -0
- package/test/remote-live-log-r37.test.mjs +89 -32
- package/test/remote-r24-catalog.test.mjs +12 -3
- package/test/remote-result-journal.test.mjs +15 -0
- package/test/remote-session-cli.test.mjs +69 -46
- package/test/runtime-self-update.test.mjs +44 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
4
|
+
import { access, mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { generateTrustKeypair, loadPrivateSigningKey, loadPublicVerifyKey } from '../../src/trust.mjs';
|
|
9
|
+
import { loadDesktopPolicy, desktopSessionStatus } from '../../src/desktop-policy-r40.mjs';
|
|
10
|
+
import { createDesktopObserverRuntime } from '../../src/desktop-observer-r40.mjs';
|
|
11
|
+
import { createNativeDesktopInputProvider } from '../../src/desktop-input-r40.mjs';
|
|
12
|
+
import { createDesktopRuntime, verifyDesktopReceiptObject } from '../../src/desktop-runtime-r40.mjs';
|
|
13
|
+
|
|
14
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const root=path.resolve(here,'../..');
|
|
16
|
+
const sourcePolicyPath=path.join(root,'controller','deadbyte-desktop-policy.json');
|
|
17
|
+
const sha=value=>createHash('sha256').update(value).digest('hex');
|
|
18
|
+
const absent=async p=>{try{await access(p);return false;}catch(e){if(e?.code==='ENOENT')return true;throw e;}};
|
|
19
|
+
|
|
20
|
+
test('R40 guarded native input accepts one SHIFT press-release under scoped authority then revokes it',async()=>{
|
|
21
|
+
const temp=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r40-live-input-'));
|
|
22
|
+
let grantFile='';
|
|
23
|
+
let sourceGrant='';
|
|
24
|
+
try{
|
|
25
|
+
const raw=JSON.parse(await readFile(sourcePolicyPath,'utf8'));
|
|
26
|
+
sourceGrant=raw.grant_file;
|
|
27
|
+
assert.equal(await absent(sourceGrant),true,'source Desktop grant must remain absent');
|
|
28
|
+
|
|
29
|
+
grantFile=path.join(temp,'desktop-grant.json');
|
|
30
|
+
const evidenceDir=path.join(temp,'desktop-evidence');
|
|
31
|
+
const policyPath=path.join(temp,'desktop-policy.json');
|
|
32
|
+
raw.grant_file=grantFile;
|
|
33
|
+
raw.evidence_dir=evidenceDir;
|
|
34
|
+
raw.allowed_actions=['key_chord'];
|
|
35
|
+
await writeFile(policyPath,JSON.stringify(raw,null,2));
|
|
36
|
+
|
|
37
|
+
const policy=await loadDesktopPolicy(policyPath);
|
|
38
|
+
const trustDir=path.join(temp,'trust');
|
|
39
|
+
await mkdir(trustDir,{recursive:true});
|
|
40
|
+
const keys=await generateTrustKeypair({
|
|
41
|
+
privateKeyPath:path.join(trustDir,'private.pem'),
|
|
42
|
+
publicKeyPath:path.join(trustDir,'public.pem')
|
|
43
|
+
});
|
|
44
|
+
const signer=await loadPrivateSigningKey(keys.privateKeyPath);
|
|
45
|
+
const verifier=await loadPublicVerifyKey(keys.publicKeyPath);
|
|
46
|
+
|
|
47
|
+
await writeFile(grantFile,JSON.stringify({
|
|
48
|
+
schema:'deadbyte.desktop-grant.v1',
|
|
49
|
+
policy_sha256:policy.policy_sha256,
|
|
50
|
+
instance_nonce:randomBytes(32).toString('hex'),
|
|
51
|
+
action_kinds:['key_chord'],
|
|
52
|
+
granted_at_utc:new Date().toISOString()
|
|
53
|
+
}));
|
|
54
|
+
const authority=await desktopSessionStatus(policy);
|
|
55
|
+
assert.equal(authority.armed,true);
|
|
56
|
+
assert.deepEqual(authority.action_kinds,['key_chord']);
|
|
57
|
+
|
|
58
|
+
const observer=createDesktopObserverRuntime({
|
|
59
|
+
observerPath:policy.observer.executable,
|
|
60
|
+
expectedObserverSha256:policy.observer.sha256,
|
|
61
|
+
maxFrameBytes:policy.limits.max_frame_bytes,
|
|
62
|
+
timeoutMs:policy.limits.capture_timeout_ms,
|
|
63
|
+
allowInputDesktops:policy.allowed_input_desktops
|
|
64
|
+
});
|
|
65
|
+
const input=createNativeDesktopInputProvider({
|
|
66
|
+
inputPath:policy.input.executable,
|
|
67
|
+
expectedInputSha256:policy.input.sha256,
|
|
68
|
+
timeoutMs:policy.limits.input_timeout_ms
|
|
69
|
+
});
|
|
70
|
+
const runtime=createDesktopRuntime({
|
|
71
|
+
policy,observerRuntime:observer,inputProvider:input,
|
|
72
|
+
privateKey:signer.privateKey,keyId:signer.keyId
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const expected=await runtime.observe();
|
|
76
|
+
const intentId=sha(Buffer.from('R40 guarded live acceptance: SHIFT press-release','utf8'));
|
|
77
|
+
const result=await runtime.action({
|
|
78
|
+
intentId,kind:'key_chord',expectedFrameId:expected.frame.frame_id,keyChord:['SHIFT']
|
|
79
|
+
});
|
|
80
|
+
assert.equal(result.status,'ok');
|
|
81
|
+
assert.equal(result.injected,true);
|
|
82
|
+
assert.equal(result.action.kind,'key_chord');
|
|
83
|
+
assert.deepEqual(result.action.key_chord,['SHIFT']);
|
|
84
|
+
assert.equal(result.side_effect_state,'confirmed_injected');
|
|
85
|
+
assert.match(result.started_receipt_id,/^[0-9a-f]{24}$/);
|
|
86
|
+
assert.match(result.post_receipt_id,/^[0-9a-f]{24}$/);
|
|
87
|
+
assert.match(result.post_receipt_sha256,/^[0-9a-f]{64}$/);
|
|
88
|
+
assert.match(result.action_chain.chain_sha256,/^[0-9a-f]{64}$/);
|
|
89
|
+
assert.match(result.effect_id,/^[0-9a-f]{64}$/);
|
|
90
|
+
assert.match(result.effect_fingerprint,/^[0-9a-f]{64}$/);
|
|
91
|
+
assert.equal(result.effect_completion.schema,'deadbyte.effect-completion.v1');
|
|
92
|
+
assert.match(result.receipt_id,/^[0-9a-f]{24}$/);
|
|
93
|
+
|
|
94
|
+
const ids=[expected.receipt_id,result.started_receipt_id,result.post_receipt_id,result.receipt_id];
|
|
95
|
+
const verified=[];
|
|
96
|
+
for(const id of ids){
|
|
97
|
+
const receipt=JSON.parse(await readFile(path.join(evidenceDir,id+'.json'),'utf8'));
|
|
98
|
+
const proof=verifyDesktopReceiptObject(receipt,{
|
|
99
|
+
policySha256:policy.policy_sha256,publicKey:verifier.publicKey,keyId:verifier.keyId
|
|
100
|
+
});
|
|
101
|
+
assert.equal(proof.ok,true,'receipt verification failed: '+id);
|
|
102
|
+
verified.push({id,kind:proof.kind,sha256:proof.receipt_sha256});
|
|
103
|
+
}
|
|
104
|
+
assert.deepEqual(verified.map(v=>v.kind),['desktop_observation','desktop_action_started','desktop_post_observation','desktop_action_completed']);
|
|
105
|
+
const postReceipt=JSON.parse(await readFile(path.join(evidenceDir,result.post_receipt_id+'.json'),'utf8'));
|
|
106
|
+
assert.equal(postReceipt.payload.post_observation_sha256,result.post_observation.post_observation_sha256);
|
|
107
|
+
const completed=JSON.parse(await readFile(path.join(evidenceDir,result.receipt_id+'.json'),'utf8'));
|
|
108
|
+
assert.equal(completed.payload.effect.injected,true);
|
|
109
|
+
assert.match(completed.payload.effect.provider_receipt_sha256,/^[0-9a-f]{64}$/);
|
|
110
|
+
|
|
111
|
+
console.log(JSON.stringify({
|
|
112
|
+
status:'PASS',action:'key_chord',keys:['SHIFT'],native_injected:true,
|
|
113
|
+
authority_scope:['key_chord'],expected_frame_id:expected.frame.frame_id,
|
|
114
|
+
action_sha256:result.action.action_sha256,guard_sha256:result.guard.guard_sha256,
|
|
115
|
+
provider_receipt_sha256:completed.payload.effect.provider_receipt_sha256,
|
|
116
|
+
action_chain_sha256:result.action_chain.chain_sha256,
|
|
117
|
+
effect_id:result.effect_id,effect_fingerprint:result.effect_fingerprint,
|
|
118
|
+
effect_completion_result_sha256:result.effect_completion.result_sha256,
|
|
119
|
+
observation_receipt:verified[0],started_receipt:verified[1],post_receipt:verified[2],completed_receipt:verified[3],
|
|
120
|
+
replay_performed:false
|
|
121
|
+
}));
|
|
122
|
+
} finally {
|
|
123
|
+
if(grantFile) await rm(grantFile,{force:true}).catch(()=>{});
|
|
124
|
+
if(sourceGrant) assert.equal(await absent(sourceGrant),true,'source Desktop grant changed during acceptance');
|
|
125
|
+
if(grantFile) assert.equal(await absent(grantFile),true,'temporary Desktop grant was not revoked');
|
|
126
|
+
await rm(temp,{recursive:true,force:true});
|
|
127
|
+
}
|
|
128
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { access, mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { generateTrustKeypair, loadPrivateSigningKey, loadPublicVerifyKey } from '../../src/trust.mjs';
|
|
8
|
+
import { loadDesktopPolicy } from '../../src/desktop-policy-r40.mjs';
|
|
9
|
+
import { createDesktopObserverRuntime } from '../../src/desktop-observer-r40.mjs';
|
|
10
|
+
import { createNativeDesktopInputProvider } from '../../src/desktop-input-r40.mjs';
|
|
11
|
+
import { createDesktopRuntime, verifyDesktopReceiptObject } from '../../src/desktop-runtime-r40.mjs';
|
|
12
|
+
|
|
13
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const root=path.resolve(here,'../..');
|
|
15
|
+
const sourcePolicyPath=path.join(root,'controller','deadbyte-desktop-policy.json');
|
|
16
|
+
const absent=async p=>{try{await access(p);return false;}catch(e){if(e?.code==='ENOENT')return true;throw e;}};
|
|
17
|
+
|
|
18
|
+
test('R40 live native Desktop status and observation succeed while mutation authority stays DISARMED',async()=>{
|
|
19
|
+
const temp=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r40-live-observe-'));
|
|
20
|
+
try{
|
|
21
|
+
const raw=JSON.parse(await readFile(sourcePolicyPath,'utf8'));
|
|
22
|
+
const sourceGrant=raw.grant_file;
|
|
23
|
+
assert.equal(await absent(sourceGrant),true,'source Desktop grant must be absent before live observation');
|
|
24
|
+
|
|
25
|
+
const grantFile=path.join(temp,'desktop-grant.json');
|
|
26
|
+
const evidenceDir=path.join(temp,'desktop-evidence');
|
|
27
|
+
const policyPath=path.join(temp,'desktop-policy.json');
|
|
28
|
+
await mkdir(evidenceDir,{recursive:true});
|
|
29
|
+
raw.grant_file=grantFile;
|
|
30
|
+
raw.evidence_dir=evidenceDir;
|
|
31
|
+
await writeFile(policyPath,JSON.stringify(raw,null,2));
|
|
32
|
+
|
|
33
|
+
const policy=await loadDesktopPolicy(policyPath);
|
|
34
|
+
const trustDir=path.join(temp,'trust');
|
|
35
|
+
await mkdir(trustDir,{recursive:true});
|
|
36
|
+
const keys=await generateTrustKeypair({
|
|
37
|
+
privateKeyPath:path.join(trustDir,'private.pem'),
|
|
38
|
+
publicKeyPath:path.join(trustDir,'public.pem')
|
|
39
|
+
});
|
|
40
|
+
const signer=await loadPrivateSigningKey(keys.privateKeyPath);
|
|
41
|
+
const verifier=await loadPublicVerifyKey(keys.publicKeyPath);
|
|
42
|
+
|
|
43
|
+
const observer=createDesktopObserverRuntime({
|
|
44
|
+
observerPath:policy.observer.executable,
|
|
45
|
+
expectedObserverSha256:policy.observer.sha256,
|
|
46
|
+
maxFrameBytes:policy.limits.max_frame_bytes,
|
|
47
|
+
timeoutMs:policy.limits.capture_timeout_ms,
|
|
48
|
+
allowInputDesktops:policy.allowed_input_desktops
|
|
49
|
+
});
|
|
50
|
+
const input=createNativeDesktopInputProvider({
|
|
51
|
+
inputPath:policy.input.executable,
|
|
52
|
+
expectedInputSha256:policy.input.sha256,
|
|
53
|
+
timeoutMs:policy.limits.input_timeout_ms
|
|
54
|
+
});
|
|
55
|
+
const runtime=createDesktopRuntime({
|
|
56
|
+
policy,observerRuntime:observer,inputProvider:input,
|
|
57
|
+
privateKey:signer.privateKey,keyId:signer.keyId
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const status=await runtime.status();
|
|
61
|
+
assert.equal(status.status,'ok');
|
|
62
|
+
assert.equal(status.armed,false);
|
|
63
|
+
assert.equal(status.reason,'disarmed');
|
|
64
|
+
assert.deepEqual(status.action_kinds,[]);
|
|
65
|
+
assert.equal(await absent(grantFile),true);
|
|
66
|
+
|
|
67
|
+
const observed=await runtime.observe();
|
|
68
|
+
assert.equal(observed.status,'ok');
|
|
69
|
+
assert.match(observed.frame.frame_id,/^[0-9a-f]{64}$/);
|
|
70
|
+
assert.match(observed.frame.image_sha256,/^[0-9a-f]{64}$/);
|
|
71
|
+
assert.match(observed.frame.topology_sha256,/^[0-9a-f]{64}$/);
|
|
72
|
+
assert.ok(observed.byte_length>0);
|
|
73
|
+
assert.equal(Object.hasOwn(observed,'pixels'),false,'public observation must not expose raw frame pixels');
|
|
74
|
+
assert.match(observed.receipt_id,/^[0-9a-f]{24}$/);
|
|
75
|
+
assert.match(observed.receipt_sha256,/^[0-9a-f]{64}$/);
|
|
76
|
+
|
|
77
|
+
const receipt=JSON.parse(await readFile(path.join(evidenceDir,observed.receipt_id+'.json'),'utf8'));
|
|
78
|
+
const proof=verifyDesktopReceiptObject(receipt,{
|
|
79
|
+
policySha256:policy.policy_sha256,publicKey:verifier.publicKey,keyId:verifier.keyId
|
|
80
|
+
});
|
|
81
|
+
assert.equal(proof.ok,true);
|
|
82
|
+
assert.equal(proof.kind,'desktop_observation');
|
|
83
|
+
|
|
84
|
+
assert.equal(await absent(grantFile),true,'live observe must not create mutation authority');
|
|
85
|
+
assert.equal(await absent(sourceGrant),true,'live observe must not touch source Desktop grant');
|
|
86
|
+
|
|
87
|
+
const w=observed.frame.active_window;
|
|
88
|
+
console.log(JSON.stringify({
|
|
89
|
+
status:'PASS',desktop_authority:'DISARMED',observer_backend:observed.backend,
|
|
90
|
+
frame_id:observed.frame.frame_id,desktop_epoch:observed.frame.desktop_epoch,
|
|
91
|
+
topology_sha256:observed.frame.topology_sha256,image_sha256:observed.frame.image_sha256,
|
|
92
|
+
bounds:observed.frame.bounds,byte_length:observed.byte_length,
|
|
93
|
+
active_window:w?{hwnd:w.hwnd,pid:w.pid,process_image_sha256:w.process_image_sha256??null,title_sha256:w.title_sha256??null}:null,
|
|
94
|
+
receipt_id:observed.receipt_id,receipt_sha256:observed.receipt_sha256,receipt_verified:true,
|
|
95
|
+
raw_pixels_exposed:false
|
|
96
|
+
}));
|
|
97
|
+
} finally {
|
|
98
|
+
await rm(temp,{recursive:true,force:true});
|
|
99
|
+
}
|
|
100
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { access, mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { generateTrustKeypair, loadPrivateSigningKey, loadPublicVerifyKey } from '../../src/trust.mjs';
|
|
8
|
+
import { loadDesktopPolicy } from '../../src/desktop-policy-r40.mjs';
|
|
9
|
+
import { createDesktopObserverRuntime } from '../../src/desktop-observer-r40.mjs';
|
|
10
|
+
import { createNativeDesktopGrounder } from '../../src/desktop-uia-r40.mjs';
|
|
11
|
+
import { createDesktopRuntime, verifyDesktopReceiptObject } from '../../src/desktop-runtime-r40.mjs';
|
|
12
|
+
|
|
13
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const root=path.resolve(here,'../..');
|
|
15
|
+
const sourcePolicyPath=path.join(root,'controller','deadbyte-desktop-policy.json');
|
|
16
|
+
const absent=async p=>{try{await access(p);return false;}catch(e){if(e?.code==='ENOENT')return true;throw e;}};
|
|
17
|
+
|
|
18
|
+
test('R40 T5 live UIA root grounding and bounded preview stay read-only and signed',async()=>{
|
|
19
|
+
const temp=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r40-live-uia-'));
|
|
20
|
+
let sourceGrant='';
|
|
21
|
+
try{
|
|
22
|
+
const raw=JSON.parse(await readFile(sourcePolicyPath,'utf8'));
|
|
23
|
+
sourceGrant=raw.grant_file;
|
|
24
|
+
assert.equal(await absent(sourceGrant),true,'source Desktop grant must be absent');
|
|
25
|
+
|
|
26
|
+
const grantFile=path.join(temp,'desktop-grant.json');
|
|
27
|
+
const evidenceDir=path.join(temp,'desktop-evidence');
|
|
28
|
+
const policyPath=path.join(temp,'desktop-policy.json');
|
|
29
|
+
raw.grant_file=grantFile;
|
|
30
|
+
raw.evidence_dir=evidenceDir;
|
|
31
|
+
await writeFile(policyPath,JSON.stringify(raw,null,2));
|
|
32
|
+
const policy=await loadDesktopPolicy(policyPath);
|
|
33
|
+
|
|
34
|
+
const trustDir=path.join(temp,'trust');
|
|
35
|
+
await mkdir(trustDir,{recursive:true});
|
|
36
|
+
const keys=await generateTrustKeypair({
|
|
37
|
+
privateKeyPath:path.join(trustDir,'private.pem'),
|
|
38
|
+
publicKeyPath:path.join(trustDir,'public.pem')
|
|
39
|
+
});
|
|
40
|
+
const signer=await loadPrivateSigningKey(keys.privateKeyPath);
|
|
41
|
+
const verifier=await loadPublicVerifyKey(keys.publicKeyPath);
|
|
42
|
+
|
|
43
|
+
const observer=createDesktopObserverRuntime({
|
|
44
|
+
observerPath:policy.observer.executable,
|
|
45
|
+
expectedObserverSha256:policy.observer.sha256,
|
|
46
|
+
maxFrameBytes:policy.limits.max_frame_bytes,
|
|
47
|
+
timeoutMs:policy.limits.capture_timeout_ms,
|
|
48
|
+
allowInputDesktops:policy.allowed_input_desktops,
|
|
49
|
+
maxCachedFrames:4
|
|
50
|
+
});
|
|
51
|
+
const grounder=createNativeDesktopGrounder({
|
|
52
|
+
grounderPath:policy.grounder.executable,
|
|
53
|
+
expectedGrounderSha256:policy.grounder.sha256,
|
|
54
|
+
timeoutMs:policy.limits.ground_timeout_ms
|
|
55
|
+
});
|
|
56
|
+
const runtime=createDesktopRuntime({
|
|
57
|
+
policy,observerRuntime:observer,
|
|
58
|
+
inputProvider:async()=>{throw new Error('input must not be called by T5 live grounding');},
|
|
59
|
+
grounderProvider:grounder,
|
|
60
|
+
privateKey:signer.privateKey,keyId:signer.keyId
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const status=await runtime.status();
|
|
64
|
+
assert.equal(status.armed,false);
|
|
65
|
+
assert.equal(await absent(grantFile),true);
|
|
66
|
+
|
|
67
|
+
const observed=await runtime.observe({preview:true});
|
|
68
|
+
assert.equal(observed.status,'ok');
|
|
69
|
+
assert.ok(observed.frame.active_window,'T5 live UIA acceptance requires one foreground window');
|
|
70
|
+
assert.equal(observed.preview.schema,'deadbyte.desktop-preview.v1');
|
|
71
|
+
assert.equal(observed.preview.encoding,'ppm-base64');
|
|
72
|
+
assert.ok(observed.preview.byte_length<=policy.limits.max_preview_bytes);
|
|
73
|
+
assert.match(observed.preview.sha256,/^[0-9a-f]{64}$/);
|
|
74
|
+
assert.ok(Buffer.from(observed.preview.data_base64,'base64').length===observed.preview.byte_length);
|
|
75
|
+
|
|
76
|
+
const grounded=await runtime.ground({
|
|
77
|
+
frameId:observed.frame.frame_id,
|
|
78
|
+
query:{kind:'root',value:'',max_results:1,ordinal:0}
|
|
79
|
+
});
|
|
80
|
+
assert.equal(grounded.status,'ok');
|
|
81
|
+
assert.equal(grounded.target.source,'uia');
|
|
82
|
+
assert.equal(grounded.target.frame_id,grounded.frame.frame_id);
|
|
83
|
+
assert.equal(grounded.grounding.query_kind,'root');
|
|
84
|
+
assert.equal(grounded.grounding.match_count,1);
|
|
85
|
+
assert.equal(grounded.grounding.ordinal,0);
|
|
86
|
+
assert.match(grounded.target.target_id,/^[0-9a-f]{64}$/);
|
|
87
|
+
assert.match(grounded.grounding.provider_receipt_sha256,/^[0-9a-f]{64}$/);
|
|
88
|
+
|
|
89
|
+
const b=grounded.frame.bounds,r=grounded.target.rect;
|
|
90
|
+
assert.ok(r.left>=b.left&&r.top>=b.top&&r.left+r.width<=b.left+b.width&&r.top+r.height<=b.top+b.height);
|
|
91
|
+
|
|
92
|
+
const receipt=JSON.parse(await readFile(path.join(evidenceDir,grounded.grounding.receipt_id+'.json'),'utf8'));
|
|
93
|
+
const proof=verifyDesktopReceiptObject(receipt,{
|
|
94
|
+
policySha256:policy.policy_sha256,publicKey:verifier.publicKey,keyId:verifier.keyId
|
|
95
|
+
});
|
|
96
|
+
assert.equal(proof.ok,true);
|
|
97
|
+
assert.equal(proof.kind,'desktop_grounding');
|
|
98
|
+
assert.equal(receipt.payload.target_id,grounded.target.target_id);
|
|
99
|
+
assert.equal(receipt.payload.query_sha256,grounded.grounding.query_sha256);
|
|
100
|
+
|
|
101
|
+
assert.equal(await absent(grantFile),true,'T5 grounding must not create Desktop mutation grant');
|
|
102
|
+
assert.equal(await absent(sourceGrant),true,'source Desktop grant changed');
|
|
103
|
+
|
|
104
|
+
console.log(JSON.stringify({
|
|
105
|
+
status:'PASS',authority:'DISARMED',preview:{
|
|
106
|
+
width:observed.preview.width,height:observed.preview.height,
|
|
107
|
+
byte_length:observed.preview.byte_length,sha256:observed.preview.sha256
|
|
108
|
+
},
|
|
109
|
+
foreground:{hwnd:grounded.frame.active_window?.hwnd??null,pid:grounded.frame.active_window?.pid??null},
|
|
110
|
+
target_id:grounded.target.target_id,rect:grounded.target.rect,
|
|
111
|
+
query_sha256:grounded.grounding.query_sha256,
|
|
112
|
+
provider_receipt_sha256:grounded.grounding.provider_receipt_sha256,
|
|
113
|
+
grounding_receipt_id:grounded.grounding.receipt_id,
|
|
114
|
+
grounding_receipt_sha256:grounded.grounding.receipt_sha256,
|
|
115
|
+
receipt_verified:true,mutation:false
|
|
116
|
+
}));
|
|
117
|
+
}finally{
|
|
118
|
+
if(sourceGrant) assert.equal(await absent(sourceGrant),true,'source Desktop grant changed during cleanup');
|
|
119
|
+
await rm(temp,{recursive:true,force:true});
|
|
120
|
+
}
|
|
121
|
+
});
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/controller/README.TXT
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
DEADBYTE MCP V0.
|
|
1
|
+
DEADBYTE MCP V0.12.0 / R40 CONTROLLER
|
|
2
2
|
====================================
|
|
3
3
|
|
|
4
4
|
ENTRY POINT
|
|
@@ -8,12 +8,14 @@ Primary operator entry point:
|
|
|
8
8
|
npx deadbyte-mcp@latest remote
|
|
9
9
|
|
|
10
10
|
Bare `remote` is a foreground device session. It starts the verified runtime, arms process,
|
|
11
|
-
coding, and autonomous authority, verifies those three planes are ARMED, and requires
|
|
12
|
-
DISARMED.
|
|
13
|
-
|
|
11
|
+
coding, and autonomous authority, verifies those three planes are ARMED, and requires Desktop and Host
|
|
12
|
+
to stay DISARMED. R40 Desktop observation/UIA grounding remains available read-only; GUI mutation needs
|
|
13
|
+
the separate explicit Desktop standing grant and Autonomous authority cannot substitute for it.
|
|
14
|
+
The production Bridge uses the compact capability profile (exact 42 tools / 6 gateways) while local
|
|
15
|
+
stdio MCP keeps the exact full 80-tool profile. The compact console shows request-before-result
|
|
14
16
|
Live Activity without raw SHA text, request IDs, payload JSON, or duplicate Bridge stdout by
|
|
15
17
|
default; use `remote --verbose` for the detailed audit/runtime stream. Ctrl+C/SIGTERM or foreground
|
|
16
|
-
input closure revokes process/coding/autonomous authority, applies fail-safe Host disarm, and parks
|
|
18
|
+
input closure revokes Desktop/process/coding/autonomous authority, applies fail-safe Host disarm, and parks
|
|
17
19
|
the Bridge. A detached nonce-bound guardian performs the same fixed revoke+park cleanup if the
|
|
18
20
|
foreground owner is hard-killed. The v2 session marker records owner PID, guardian PID, and random
|
|
19
21
|
256-bit nonce so a guardian cannot remove a replacement session marker; a later start recovers only
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
MachineFsRuntimeEnabled = $false
|
|
18
18
|
# R32 observation is read-only over registered roots/local runtime and never arms coding mutation authority.
|
|
19
19
|
ObservationRuntimeEnabled = $false
|
|
20
|
+
# R40 Desktop observation/mutation runtime. Enabling runtime never arms mutation authority.
|
|
21
|
+
DesktopRuntimeEnabled = $false
|
|
22
|
+
DesktopPolicyFile = ''
|
|
20
23
|
CodingPolicyFile = ''
|
|
21
24
|
# Optional R35 multi-language semantic LSP config. Path and SHA-256 must be supplied together.
|
|
22
25
|
# The release generator copies these exact bytes into the immutable generation when configured.
|