deadbyte-mcp 0.11.3 → 0.13.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 +126 -54
- package/R41-PARENT.json +15 -0
- 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/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.obj +0 -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/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.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/controller/deadbyte-process-policy.json +4 -4
- package/docs/ARCHITECTURE.md +6 -1
- package/docs/superpowers/specs/2026-09-19-r40-desktop-computer-use-design.md +214 -0
- package/docs/superpowers/specs/2026-09-20-r41-deterministic-memory-design.md +132 -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 +12 -8
- package/proof/CONTAINMENT-BUILD.txt +6 -6
- package/scripts/build-containment.ps1 +24 -14
- package/scripts/build-desktop-r40.ps1 +112 -0
- package/scripts/deferred-slot-operation-r40.mjs +126 -0
- package/scripts/deferred-slot-operation-r41.mjs +127 -0
- package/scripts/final-closure-r40.mjs +114 -0
- package/scripts/final-closure-r41.mjs +116 -0
- package/scripts/final-closure-verify-r40.mjs +212 -0
- package/scripts/final-closure-verify-r41.mjs +215 -0
- package/scripts/gate-windows-r40.ps1 +43 -0
- package/scripts/gate-windows-r41.ps1 +48 -0
- package/scripts/init-desktop-policy-r40.ps1 +85 -0
- package/scripts/r40-compact-ledger-probe.mjs +4 -0
- package/scripts/release-parity-r40.mjs +212 -0
- package/scripts/release-parity-r41.mjs +221 -0
- package/scripts/release-parity-tests-r40.ps1 +9 -0
- package/scripts/release-parity-tests-r41.ps1 +9 -0
- package/scripts/verify-memory-r41.mjs +88 -0
- package/scripts/verify-r41-parent.mjs +61 -0
- package/scripts/windows-gate-evidence-r40.mjs +52 -0
- package/scripts/windows-gate-evidence-r41.mjs +53 -0
- package/src/autonomous-context-engine.mjs +3 -1
- package/src/autonomous-planner-contracts.mjs +19 -1
- package/src/autonomous-planner.mjs +24 -0
- package/src/autonomous-runtime.mjs +164 -25
- 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 +87 -19
- package/src/memory-r41.mjs +692 -0
- 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 +86 -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/memory-autonomous-integration-r41.test.mjs +31 -0
- package/test/memory-history-r41.test.mjs +126 -0
- package/test/memory-projections-r41.test.mjs +92 -0
- package/test/memory-retrieval-r41.test.mjs +124 -0
- package/test/memory-snapshot-r41.test.mjs +106 -0
- package/test/r33-closeout-regression.test.mjs +7 -7
- package/test/r33-finalization.test.mjs +15 -15
- package/test/r37-remote-tool-call-timeline.test.mjs +6 -5
- package/test/r40-finalization.test.mjs +118 -0
- package/test/r41-finalization.test.mjs +38 -0
- package/test/release-generation.test.mjs +41 -9
- package/test/release-version.test.mjs +20 -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,88 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readFile,readdir } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { canonicalJson } from '../src/canonical-json.mjs';
|
|
6
|
+
import { loadPublicVerifyKey } from '../src/trust.mjs';
|
|
7
|
+
import { buildMemoryArchive,buildMemoryCorpus,buildRetrievalIndex,verifyMemoryGoal,verifyMemoryJournal,
|
|
8
|
+
verifyMemorySnapshot,verifyRetrievalIndex } from '../src/memory-r41.mjs';
|
|
9
|
+
|
|
10
|
+
const HEX64=/^[0-9a-f]{64}$/;
|
|
11
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
12
|
+
const insist=(ok,message)=>{if(!ok)throw new Error(message);};
|
|
13
|
+
const readJson=async file=>JSON.parse((await readFile(file,'utf8')).replace(/^\uFEFF/,''));
|
|
14
|
+
async function jsonFiles(dir){return (await readdir(dir)).filter(name=>name.endsWith('.json')).sort();}
|
|
15
|
+
function parseArgs(argv){
|
|
16
|
+
const out={};for(let index=0;index<argv.length;index+=2){const key=argv[index];insist(key?.startsWith('--')&&argv[index+1],`invalid argument ${key??''}`);out[key.slice(2)]=argv[index+1];}
|
|
17
|
+
for(const key of ['memory-root','journal-root','manifest','public-key'])insist(out[key],`missing --${key}`);return out;
|
|
18
|
+
}
|
|
19
|
+
async function loadCanonicalGoal(journalRoot,goalId,publicKey,keyId,manifest){
|
|
20
|
+
const root=path.join(journalRoot,goalId);const goal=verifyMemoryGoal(await readJson(path.join(root,'goal.json')),{publicKey,keyId,expectedGoalId:goalId});
|
|
21
|
+
const names=await jsonFiles(path.join(root,'events'));
|
|
22
|
+
insist(names.every((name,index)=>name===`${String(index+1).padStart(6,'0')}.json`),`event gap for ${goalId}`);
|
|
23
|
+
const events=[];for(const name of names)events.push(await readJson(path.join(root,'events',name)));
|
|
24
|
+
verifyMemoryJournal(events,{goalId,publicKey,keyId});
|
|
25
|
+
insist(events[0]?.type==='goal_created'&&events[0]?.payload?.source_release_manifest===manifest,`goal ${goalId} is not bound to release manifest`);
|
|
26
|
+
return {goal,events,corpus:buildMemoryCorpus({goal,events,publicKey,keyId,sourceReleaseManifest:manifest})};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function verifyR41Memory({memoryRoot,journalRoot,sourceReleaseManifest,publicKeyPath}={}){
|
|
30
|
+
insist(path.isAbsolute(memoryRoot)&&path.isAbsolute(journalRoot),'memory verifier roots must be absolute');insist(HEX64.test(sourceReleaseManifest??''),'memory verifier manifest invalid');
|
|
31
|
+
const {publicKey,keyId}=await loadPublicVerifyKey(publicKeyPath);const canonical=new Map();let snapshots=0,indexes=0,archives=0,archiveIndexes=0;
|
|
32
|
+
let journalGoalIds=[];try{journalGoalIds=(await readdir(journalRoot,{withFileTypes:true})).filter(entry=>entry.isDirectory()&&HEX64.test(entry.name)).map(entry=>entry.name).sort();}
|
|
33
|
+
catch(error){if(error?.code!=='ENOENT')throw error;}
|
|
34
|
+
for(const goalId of journalGoalIds){
|
|
35
|
+
const firstPath=path.join(journalRoot,goalId,'events','000001.json');let first;
|
|
36
|
+
try{first=await readJson(firstPath);}catch(error){if(error?.code==='ENOENT')continue;throw error;}
|
|
37
|
+
verifyMemoryJournal([first],{goalId,publicKey,keyId});
|
|
38
|
+
if(first.type!=='goal_created'||first.payload?.source_release_manifest!==sourceReleaseManifest)continue;
|
|
39
|
+
const loaded=await loadCanonicalGoal(journalRoot,goalId,publicKey,keyId,sourceReleaseManifest);canonical.set(goalId,loaded);
|
|
40
|
+
}
|
|
41
|
+
let goalIds=[];try{goalIds=(await readdir(path.join(memoryRoot,'goals'),{withFileTypes:true})).filter(entry=>entry.isDirectory()&&HEX64.test(entry.name)).map(entry=>entry.name).sort();}
|
|
42
|
+
catch(error){if(error?.code!=='ENOENT')throw error;}
|
|
43
|
+
for(const goalId of goalIds){
|
|
44
|
+
const loaded=canonical.get(goalId);insist(loaded,`memory projection goal ${goalId} has no canonical release-bound journal`);
|
|
45
|
+
const base=path.join(memoryRoot,'goals',goalId);const corporaByMemoryHash=new Map();
|
|
46
|
+
for(const name of await jsonFiles(path.join(base,'snapshots'))){
|
|
47
|
+
const value=await readJson(path.join(base,'snapshots',name));
|
|
48
|
+
const proof=verifyMemorySnapshot(value,{goal:loaded.goal,events:loaded.events,publicKey,keyId,sourceReleaseManifest});
|
|
49
|
+
insist(proof.ok,`snapshot invalid ${goalId}/${name}: ${proof.reason}`);
|
|
50
|
+
const prefixEvents=loaded.events.slice(0,proof.last_event_seq);
|
|
51
|
+
const prefixCorpus=buildMemoryCorpus({goal:loaded.goal,events:prefixEvents,publicKey,keyId,sourceReleaseManifest});
|
|
52
|
+
const prior=corporaByMemoryHash.get(prefixCorpus.memory_sha256);
|
|
53
|
+
insist(!prior||canonicalJson(prior)===canonicalJson(prefixCorpus),`snapshot corpus collision ${goalId}/${name}`);
|
|
54
|
+
corporaByMemoryHash.set(prefixCorpus.memory_sha256,prefixCorpus);snapshots+=1;
|
|
55
|
+
}
|
|
56
|
+
for(const name of await jsonFiles(path.join(base,'indexes'))){
|
|
57
|
+
const value=await readJson(path.join(base,'indexes',name));
|
|
58
|
+
const sourceCorpus=corporaByMemoryHash.get(value.source_memory_sha256);
|
|
59
|
+
insist(sourceCorpus,`index source memory hash has no verified snapshot prefix ${goalId}/${name}`);
|
|
60
|
+
const proof=verifyRetrievalIndex(value,{corpus:sourceCorpus,sourceReleaseManifest});
|
|
61
|
+
insist(proof.ok,`index invalid ${goalId}/${name}: ${proof.reason}`);indexes+=1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const archiveByHash=new Map();
|
|
65
|
+
try{for(const name of await jsonFiles(path.join(memoryRoot,'archives'))){const value=await readJson(path.join(memoryRoot,'archives',name));
|
|
66
|
+
const corpora=value.corpus_refs.map(ref=>{
|
|
67
|
+
const loaded=canonical.get(ref.goal_id);insist(loaded,`archive references noncanonical goal ${ref.goal_id}`);
|
|
68
|
+
const headIndex=loaded.events.findIndex(event=>event.event_sha256===ref.journal_head_sha256);
|
|
69
|
+
insist(headIndex>=0,`archive references unknown journal head ${ref.goal_id}`);
|
|
70
|
+
const corpus=buildMemoryCorpus({goal:loaded.goal,events:loaded.events.slice(0,headIndex+1),publicKey,keyId,sourceReleaseManifest});
|
|
71
|
+
insist(corpus.memory_sha256===ref.memory_sha256,`archive references noncanonical goal prefix ${ref.goal_id}`);
|
|
72
|
+
return corpus;
|
|
73
|
+
});
|
|
74
|
+
const expected=buildMemoryArchive(corpora,{sourceReleaseManifest});insist(canonicalJson(expected)===canonicalJson(value),'archive projection mismatch');insist(name===`${value.memory_sha256}.json`,'archive filename mismatch');archiveByHash.set(value.memory_sha256,value);archives+=1;}}
|
|
75
|
+
catch(error){if(error?.code!=='ENOENT')throw error;}
|
|
76
|
+
try{for(const name of await jsonFiles(path.join(memoryRoot,'archive-indexes'))){const value=await readJson(path.join(memoryRoot,'archive-indexes',name));const archive=archiveByHash.get(value.source_memory_sha256);insist(archive,'archive index source missing');
|
|
77
|
+
const proof=verifyRetrievalIndex(value,{corpus:archive,sourceReleaseManifest});insist(proof.ok,`archive index invalid ${name}: ${proof.reason}`);insist(canonicalJson(buildRetrievalIndex(archive,{sourceReleaseManifest}))===canonicalJson(value),'archive index rebuild mismatch');archiveIndexes+=1;}}
|
|
78
|
+
catch(error){if(error?.code!=='ENOENT')throw error;}
|
|
79
|
+
return {schema:'deadbyte.r41-memory-verification.v1',status:'passed',source_release_manifest:sourceReleaseManifest,
|
|
80
|
+
goal_count:goalIds.length,snapshot_count:snapshots,index_count:indexes,archive_count:archives,archive_index_count:archiveIndexes,
|
|
81
|
+
verification_sha256:sha(Buffer.from(canonicalJson({goalIds,snapshots,indexes,archives,archiveIndexes,sourceReleaseManifest}),'utf8'))};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){
|
|
85
|
+
const args=parseArgs(process.argv.slice(2));verifyR41Memory({memoryRoot:path.resolve(args['memory-root']),journalRoot:path.resolve(args['journal-root']),
|
|
86
|
+
sourceReleaseManifest:args.manifest,publicKeyPath:path.resolve(args['public-key'])}).then(result=>console.log(JSON.stringify(result,null,2)))
|
|
87
|
+
.catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
88
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { lstat,readFile,readdir } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
|
7
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
8
|
+
const insist=(ok,message)=>{if(!ok)throw new Error(message);};
|
|
9
|
+
|
|
10
|
+
function parseManifest(bytes){
|
|
11
|
+
const entries=new Map();
|
|
12
|
+
for(const [index,line] of bytes.toString('utf8').split(/\r?\n/).entries()){
|
|
13
|
+
if(!line)continue;
|
|
14
|
+
const match=/^([0-9a-f]{64}) ([^\0]+)$/.exec(line);insist(match,`parent manifest line ${index+1} malformed`);
|
|
15
|
+
const rel=match[2];insist(!path.posix.isAbsolute(rel)&&!rel.split('/').includes('..')&&!entries.has(rel),`unsafe/duplicate parent manifest path ${rel}`);
|
|
16
|
+
entries.set(rel,match[1]);
|
|
17
|
+
}
|
|
18
|
+
return entries;
|
|
19
|
+
}
|
|
20
|
+
async function walk(dir,rel=''){
|
|
21
|
+
const out=[];
|
|
22
|
+
for(const name of (await readdir(path.join(dir,...(rel?rel.split('/'):[])))).sort((a,b)=>a.localeCompare(b,'en'))){
|
|
23
|
+
const child=rel?`${rel}/${name}`:name;const full=path.join(dir,...child.split('/'));const st=await lstat(full);
|
|
24
|
+
insist(!st.isSymbolicLink(),`parent tree symlink forbidden: ${child}`);
|
|
25
|
+
if(st.isDirectory())out.push(...await walk(dir,child));else{insist(st.isFile(),`parent tree non-file forbidden: ${child}`);out.push(child);}
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
async function verifyTree(dir,expectedManifest,{exact}){
|
|
30
|
+
const bytes=await readFile(path.join(dir,'MANIFEST.SHA256'));insist(sha(bytes)===expectedManifest,`parent manifest digest mismatch: ${dir}`);
|
|
31
|
+
const entries=parseManifest(bytes);
|
|
32
|
+
if(exact){
|
|
33
|
+
const actual=(await walk(dir)).filter(rel=>rel!=='MANIFEST.SHA256');
|
|
34
|
+
insist(actual.length===entries.size&&actual.every(rel=>entries.has(rel)),`parent exact tree mismatch: ${dir}`);
|
|
35
|
+
}
|
|
36
|
+
for(const [rel,digest] of entries)insist(sha(await readFile(path.join(dir,...rel.split('/'))))===digest,`parent file hash mismatch: ${dir} :: ${rel}`);
|
|
37
|
+
return entries.size;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function verifyR41Parent(file=path.join(root,'R41-PARENT.json')){
|
|
41
|
+
const record=JSON.parse(await readFile(path.resolve(file),'utf8'));
|
|
42
|
+
insist(record.schema==='deadbyte.r41-parent.v1','R41 parent schema mismatch');
|
|
43
|
+
insist(record.parent_version==='0.12.0'&&record.parent_release_id==='v0.12.0-70906ae0a7e4f062','R41 parent identity mismatch');
|
|
44
|
+
const [sourceFiles,releaseFiles]=await Promise.all([
|
|
45
|
+
verifyTree(path.resolve(record.parent_source_root),record.parent_manifest_sha256,{exact:false}),
|
|
46
|
+
verifyTree(path.resolve(record.parent_release_root),record.parent_manifest_sha256,{exact:true})
|
|
47
|
+
]);
|
|
48
|
+
insist(sourceFiles===record.parent_manifest_file_count&&releaseFiles===record.parent_manifest_file_count,'R41 parent file count mismatch');
|
|
49
|
+
const evidenceBytes=await readFile(path.resolve(record.parent_public_evidence_path));
|
|
50
|
+
insist(sha(evidenceBytes)===record.parent_public_evidence_sha256,'R40 public evidence hash mismatch');
|
|
51
|
+
const evidence=JSON.parse(evidenceBytes.toString('utf8').replace(/^\uFEFF/,''));
|
|
52
|
+
insist(evidence.schema==='deadbyte.r40-public-final-verify.v1'&&evidence.status==='passed','R40 public evidence status invalid');
|
|
53
|
+
insist(evidence.release_id===record.parent_release_id&&evidence.manifest_sha256===record.parent_manifest_sha256,'R40 public evidence identity mismatch');
|
|
54
|
+
return {status:'passed',parent_release_id:record.parent_release_id,parent_manifest_sha256:record.parent_manifest_sha256,
|
|
55
|
+
source_files:sourceFiles,release_files:releaseFiles,parent_public_evidence_sha256:record.parent_public_evidence_sha256};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){
|
|
59
|
+
verifyR41Parent(process.argv[2]).then(value=>console.log(JSON.stringify(value,null,2)))
|
|
60
|
+
.catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
61
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, open, readFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const root=path.resolve(here,'..');
|
|
9
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
10
|
+
const marker='DEADBYTE V0.12.0 / R40 WINDOWS BATCH GATE: PASS';
|
|
11
|
+
function insist(ok,message){if(!ok) throw new Error(message);}
|
|
12
|
+
|
|
13
|
+
export async function writeWindowsGateEvidence({
|
|
14
|
+
candidateRoot=root,
|
|
15
|
+
stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp')),
|
|
16
|
+
clock=()=>new Date()
|
|
17
|
+
}={}){
|
|
18
|
+
const candidate=path.resolve(candidateRoot);
|
|
19
|
+
const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
20
|
+
const manifestSha256=sha256(manifestBytes);
|
|
21
|
+
const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
|
|
22
|
+
insist(pkg.version==='0.12.0',`Windows gate evidence requires V0.12.0, found ${pkg.version}`);
|
|
23
|
+
const completed=clock();
|
|
24
|
+
insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'Windows gate evidence clock invalid');
|
|
25
|
+
const evidence={
|
|
26
|
+
schema:'deadbyte.r40-windows-gate.v1',status:'passed',exit_code:0,version:'0.12.0',
|
|
27
|
+
marker,completed_at_utc:completed.toISOString(),manifest_sha256:manifestSha256
|
|
28
|
+
};
|
|
29
|
+
const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');
|
|
30
|
+
const dir=path.join(path.resolve(stateRoot),'evidence','release');
|
|
31
|
+
await mkdir(dir,{recursive:true});
|
|
32
|
+
const file=path.join(dir,`r40-windows-gate-${manifestSha256.slice(0,16)}.json`);
|
|
33
|
+
try{
|
|
34
|
+
const handle=await open(file,'wx',0o600);
|
|
35
|
+
try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
|
|
36
|
+
return {status:'written',path:file,sha256:sha256(bytes),manifest_sha256:manifestSha256};
|
|
37
|
+
}catch(error){
|
|
38
|
+
if(error?.code!=='EEXIST') throw error;
|
|
39
|
+
const existingBytes=await readFile(file);
|
|
40
|
+
const existing=JSON.parse(existingBytes.toString('utf8'));
|
|
41
|
+
insist(existing.schema===evidence.schema&&existing.status==='passed'&&existing.exit_code===0,'existing Windows gate evidence invalid');
|
|
42
|
+
insist(existing.version==='0.12.0'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing Windows gate evidence binding mismatch');
|
|
43
|
+
return {status:'existing',path:file,sha256:sha256(existingBytes),manifest_sha256:manifestSha256};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
|
|
48
|
+
if(invoked){
|
|
49
|
+
writeWindowsGateEvidence()
|
|
50
|
+
.then(result=>console.log(JSON.stringify(result)))
|
|
51
|
+
.catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
52
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, open, readFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const root=path.resolve(here,'..');
|
|
9
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
10
|
+
const marker='DEADBYTE V0.13.0 / R41 WINDOWS BATCH GATE: PASS';
|
|
11
|
+
function insist(ok,message){if(!ok) throw new Error(message);}
|
|
12
|
+
|
|
13
|
+
export async function writeWindowsGateEvidence({
|
|
14
|
+
candidateRoot=root,
|
|
15
|
+
stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp')),
|
|
16
|
+
clock=()=>new Date()
|
|
17
|
+
}={}){
|
|
18
|
+
const candidate=path.resolve(candidateRoot);
|
|
19
|
+
const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
20
|
+
const manifestSha256=sha256(manifestBytes);
|
|
21
|
+
const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
|
|
22
|
+
insist(pkg.version==='0.13.0',`Windows gate evidence requires V0.13.0, found ${pkg.version}`);
|
|
23
|
+
const completed=clock();
|
|
24
|
+
insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'Windows gate evidence clock invalid');
|
|
25
|
+
const evidence={
|
|
26
|
+
schema:'deadbyte.r41-windows-gate.v1',status:'passed',exit_code:0,version:'0.13.0',
|
|
27
|
+
marker,completed_at_utc:completed.toISOString(),manifest_sha256:manifestSha256
|
|
28
|
+
};
|
|
29
|
+
const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');
|
|
30
|
+
const dir=path.join(path.resolve(stateRoot),'evidence','release');
|
|
31
|
+
await mkdir(dir,{recursive:true});
|
|
32
|
+
const file=path.join(dir,`r41-windows-gate-${manifestSha256.slice(0,16)}.json`);
|
|
33
|
+
try{
|
|
34
|
+
const handle=await open(file,'wx',0o600);
|
|
35
|
+
try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
|
|
36
|
+
return {status:'written',path:file,sha256:sha256(bytes),manifest_sha256:manifestSha256};
|
|
37
|
+
}catch(error){
|
|
38
|
+
if(error?.code!=='EEXIST') throw error;
|
|
39
|
+
const existingBytes=await readFile(file);
|
|
40
|
+
const existing=JSON.parse(existingBytes.toString('utf8'));
|
|
41
|
+
insist(existing.schema===evidence.schema&&existing.status==='passed'&&existing.exit_code===0,'existing Windows gate evidence invalid');
|
|
42
|
+
insist(existing.version==='0.13.0'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing Windows gate evidence binding mismatch');
|
|
43
|
+
return {status:'existing',path:file,sha256:sha256(existingBytes),manifest_sha256:manifestSha256};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
|
|
48
|
+
if(invoked){
|
|
49
|
+
writeWindowsGateEvidence()
|
|
50
|
+
.then(result=>console.log(JSON.stringify(result)))
|
|
51
|
+
.catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
52
|
+
}
|
|
53
|
+
|
|
@@ -213,7 +213,7 @@ function historicalEvidence({ contexts, observations, loopState }) {
|
|
|
213
213
|
};
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
export function buildLoopContext({ goal, policy, loopState = {}, tree = [], contexts = [], observations = [], decisions = [], projectMemory = null, capabilities = null }) {
|
|
216
|
+
export function buildLoopContext({ goal, policy, loopState = {}, tree = [], contexts = [], observations = [], decisions = [], projectMemory = null, desktopMemory = null, longTermMemory = null, capabilities = null }) {
|
|
217
217
|
if (!policy?.limits || !Number.isInteger(policy.limits.max_context_bytes)) throw new Error('loop context policy byte limit missing');
|
|
218
218
|
const relevanceEpoch=Number.isInteger(loopState?.mutation_epoch)&&loopState.mutation_epoch>=0?loopState.mutation_epoch:0;
|
|
219
219
|
const retainedContexts=normalizeEvidence(contexts,{kind:'context',relevanceEpoch});
|
|
@@ -249,6 +249,8 @@ export function buildLoopContext({ goal, policy, loopState = {}, tree = [], cont
|
|
|
249
249
|
},
|
|
250
250
|
historical_evidence:historicalEvidence({contexts:retainedContexts,observations:retainedObservations,loopState}),
|
|
251
251
|
project_memory:memory,
|
|
252
|
+
desktop_memory:desktopMemory===null?null:structuredClone(desktopMemory),
|
|
253
|
+
long_term_memory:longTermMemory===null?null:structuredClone(longTermMemory),
|
|
252
254
|
capabilities:capabilities===null?null:structuredClone(capabilities),
|
|
253
255
|
profiles
|
|
254
256
|
};
|
|
@@ -92,6 +92,14 @@ const r34PatchOperationSchema = z.discriminatedUnion('kind',[
|
|
|
92
92
|
const plannerCursor=z.string().max(4096).nullable().default(null);
|
|
93
93
|
const plannerFiles=z.array(z.strictObject({path:relPath,encoding:z.enum(['utf8','base64']).default('utf8')})).min(1).max(32);
|
|
94
94
|
const plannerSemanticPosition={path:relPath,line:z.number().int().min(0),column:z.number().int().min(0)};
|
|
95
|
+
const plannerDesktopPoint=z.strictObject({x:z.number().int().min(-131072).max(131072),y:z.number().int().min(-131072).max(131072)});
|
|
96
|
+
const plannerDesktopKey=z.string().regex(/^(?:[A-Z0-9]|F(?:[1-9]|1[0-2])|ENTER|ESC|TAB|SPACE|BACKSPACE|DELETE|INSERT|HOME|END|PAGEUP|PAGEDOWN|LEFT|RIGHT|UP|DOWN|CTRL|ALT|SHIFT|WIN)$/);
|
|
97
|
+
const plannerDesktopActionKind=z.enum(['pointer_move','click_primary','click_secondary','double_click_primary','scroll','key_chord','text_input']);
|
|
98
|
+
const plannerDesktopQuery=z.strictObject({
|
|
99
|
+
kind:z.enum(['root','name_exact','name_contains','automation_id_exact','control_type']),
|
|
100
|
+
value:z.string().max(512).default(''),max_results:z.number().int().min(1).max(16).default(8),
|
|
101
|
+
ordinal:z.number().int().min(0).max(15).default(0)
|
|
102
|
+
});
|
|
95
103
|
const r36TypedActionSchemas=[
|
|
96
104
|
z.strictObject({kind:z.literal('search'),path:relPath.default('.'),target:z.enum(['content','path']).default('content'),mode:z.enum(['literal','regex']).default('literal'),pattern:z.string().min(1).max(256),case_sensitive:z.boolean().default(false),max_results:z.number().int().min(1).max(1000).default(200),max_file_bytes:z.number().int().min(1).max(4194304).default(262144),max_scan_bytes:z.number().int().min(1).max(67108864).default(4194304),page_size:z.number().int().min(1).max(500).default(100),cursor:plannerCursor}),
|
|
97
105
|
z.strictObject({kind:z.literal('stat'),path:relPath.default('.'),hash:z.boolean().default(false)}),
|
|
@@ -121,7 +129,17 @@ const r36TypedActionSchemas=[
|
|
|
121
129
|
z.strictObject({kind:z.literal('observation_search_page'),search_id:z.string().regex(HEX64),cursor:plannerCursor,limit:z.number().int().min(1).max(500).default(100)}),
|
|
122
130
|
z.strictObject({kind:z.literal('observation_search_cancel'),search_id:z.string().regex(HEX64)}),
|
|
123
131
|
z.strictObject({kind:z.literal('observation_process_list'),include_external:z.boolean().default(false),cursor:plannerCursor,limit:z.number().int().min(1).max(256).default(100)}),
|
|
124
|
-
z.strictObject({kind:z.literal('observation_system_status')})
|
|
132
|
+
z.strictObject({kind:z.literal('observation_system_status')}),
|
|
133
|
+
z.strictObject({kind:z.literal('desktop_status')}),
|
|
134
|
+
z.strictObject({kind:z.literal('desktop_observe')}),
|
|
135
|
+
z.strictObject({kind:z.literal('desktop_target'),frame_id:z.string().regex(HEX64),uia_query:plannerDesktopQuery}),
|
|
136
|
+
z.strictObject({
|
|
137
|
+
kind:z.literal('desktop_action'),action_kind:plannerDesktopActionKind,expected_frame_id:z.string().regex(HEX64),
|
|
138
|
+
target_id:z.string().regex(HEX64).nullable().default(null),point:plannerDesktopPoint.nullable().default(null),
|
|
139
|
+
scroll_delta:z.number().int().min(-12000).max(12000).nullable().default(null),
|
|
140
|
+
key_chord:z.array(plannerDesktopKey).min(1).max(5).nullable().default(null),
|
|
141
|
+
text:z.string().min(1).max(4096).refine(value=>!value.includes('\u0000'),'desktop text contains NUL').nullable().default(null)
|
|
142
|
+
})
|
|
125
143
|
];
|
|
126
144
|
const r34ActionSchema = z.discriminatedUnion('kind',[
|
|
127
145
|
z.strictObject({kind:z.literal('patch'),operations:z.array(r34PatchOperationSchema).min(1).max(32)}),
|
|
@@ -134,6 +134,26 @@ function validateR36TypedAction(action) {
|
|
|
134
134
|
if (typeof action.destination_path === 'string') pathFields.push(action.destination_path);
|
|
135
135
|
for (const item of action.files ?? []) if (typeof item?.path === 'string') pathFields.push(item.path);
|
|
136
136
|
for (const candidate of pathFields) validatePortableRelativePath(candidate);
|
|
137
|
+
if(action.kind==='desktop_target'){
|
|
138
|
+
const q=action.uia_query;
|
|
139
|
+
if(q.kind==='root'&&q.value!=='') throw new Error('desktop root query value must be empty');
|
|
140
|
+
if(q.kind!=='root'&&q.value.length<1) throw new Error('desktop UIA query value required');
|
|
141
|
+
if(q.ordinal>=q.max_results) throw new Error('desktop UIA ordinal must be below max_results');
|
|
142
|
+
}
|
|
143
|
+
if(action.kind==='desktop_action'){
|
|
144
|
+
const pointer=['pointer_move','click_primary','click_secondary','double_click_primary','scroll'].includes(action.action_kind);
|
|
145
|
+
if(pointer&&((action.target_id===null)===(action.point===null))) throw new Error('desktop pointer action requires exactly one of target_id or point');
|
|
146
|
+
if(!pointer&&(action.target_id!==null||action.point!==null)) throw new Error('desktop non-pointer action cannot carry target_id or point');
|
|
147
|
+
if(action.action_kind==='scroll'){
|
|
148
|
+
if(action.scroll_delta===null||action.scroll_delta===0) throw new Error('desktop scroll requires non-zero scroll_delta');
|
|
149
|
+
}else if(action.scroll_delta!==null) throw new Error('desktop non-scroll action cannot carry scroll_delta');
|
|
150
|
+
if(action.action_kind==='key_chord'){
|
|
151
|
+
if(action.key_chord===null||new Set(action.key_chord).size!==action.key_chord.length) throw new Error('desktop key_chord missing or duplicate');
|
|
152
|
+
}else if(action.key_chord!==null) throw new Error('desktop non-key action cannot carry key_chord');
|
|
153
|
+
if(action.action_kind==='text_input'){
|
|
154
|
+
if(action.text===null) throw new Error('desktop text_input requires text');
|
|
155
|
+
}else if(action.text!==null) throw new Error('desktop non-text action cannot carry text');
|
|
156
|
+
}
|
|
137
157
|
return action;
|
|
138
158
|
}
|
|
139
159
|
function normalizeR36TypedAction(action) {
|
|
@@ -334,6 +354,8 @@ export function buildPlannerPrompt({ goal, policy, view }) {
|
|
|
334
354
|
observations:view.observations ?? [],
|
|
335
355
|
decisions:view.recent_decisions ?? [],
|
|
336
356
|
projectMemory:view.project_memory ?? null,
|
|
357
|
+
desktopMemory:view.desktop_memory ?? null,
|
|
358
|
+
longTermMemory:view.long_term_memory ?? null,
|
|
337
359
|
capabilities:view.capabilities ?? null
|
|
338
360
|
});
|
|
339
361
|
const stateJson = canonicalJson(state);
|
|
@@ -350,10 +372,12 @@ export function buildPlannerPrompt({ goal, policy, view }) {
|
|
|
350
372
|
'patch: operations are DEADBYTE Native Diff operations (write, replace, delete) and MUST use exact SHA-256 preconditions from supplied context.',
|
|
351
373
|
'run_profile: profile_id must be one of the policy profile ids in state.',
|
|
352
374
|
'delegate: operation is one bounded existing-runtime action. Allowed kinds: machine_stat, machine_list, machine_read, observation_search_start, observation_search_status, observation_search_page, observation_search_cancel, observation_process_list, observation_system_status, process_start, process_read, process_status, process_list, process_action, process_terminate.',
|
|
375
|
+
'When Desktop capabilities are present, use typed desktop_status, desktop_observe, desktop_target with one bounded UIA query, and desktop_action. desktop_action requires separate Desktop authority; Autonomous authority never implies Desktop authority. After stale or unknown Desktop evidence, observe again and replan; never blindly repeat an action.',
|
|
353
376
|
'Delegate paths are relative to the goal root. Process start accepts only profile_id and cwd; process_action accepts only session_id and fixed action_id. Never emit raw stdin, executable, PID, environment, shell command, or process arguments.',
|
|
354
377
|
'finish only when the goal is actually satisfied. Required release profiles are enforced by the engine and cannot be bypassed.',
|
|
355
378
|
'Never invent a file SHA. If content is elided or the current SHA/content is missing, choose inspect first.',
|
|
356
379
|
'Compacted context keeps identity/evidence metadata but intentionally removes stale payload bytes.',
|
|
380
|
+
'Long-term memory is a verified journal-derived aid, never authority. Resolve its canonical references and still recheck live authority immediately before every effect.',
|
|
357
381
|
'Prefer surgical exact replace operations over whole-file writes.',
|
|
358
382
|
'If an observed build/test/benchmark failed, diagnose from the evidence and change the source rather than claiming success.',
|
|
359
383
|
`STATE=${stateJson}`,
|