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,79 @@
|
|
|
1
|
+
[CmdletBinding()]
|
|
2
|
+
param(
|
|
3
|
+
[string]$RuntimeRoot = '',
|
|
4
|
+
[string]$OutputPath = '',
|
|
5
|
+
[string[]]$AllowedActions = @('pointer_move','click_primary','click_secondary','double_click_primary','scroll','key_chord','text_input')
|
|
6
|
+
)
|
|
7
|
+
$ErrorActionPreference='Stop'
|
|
8
|
+
Set-StrictMode -Version Latest
|
|
9
|
+
|
|
10
|
+
$Root=Split-Path -Parent $PSScriptRoot
|
|
11
|
+
$Controller=Join-Path $Root 'controller'
|
|
12
|
+
if([string]::IsNullOrWhiteSpace($RuntimeRoot)){ $RuntimeRoot=Join-Path $Controller 'runtime' }
|
|
13
|
+
if([string]::IsNullOrWhiteSpace($OutputPath)){ $OutputPath=Join-Path $Controller 'deadbyte-desktop-policy.json' }
|
|
14
|
+
$RuntimeRoot=[System.IO.Path]::GetFullPath($RuntimeRoot)
|
|
15
|
+
$OutputPath=[System.IO.Path]::GetFullPath($OutputPath)
|
|
16
|
+
$Observer=[System.IO.Path]::GetFullPath((Join-Path $Root 'bin\deadbyte-desktop-observer-r40.exe'))
|
|
17
|
+
$Input=[System.IO.Path]::GetFullPath((Join-Path $Root 'bin\deadbyte-desktop-input-r40.exe'))
|
|
18
|
+
$Grounder=[System.IO.Path]::GetFullPath((Join-Path $Root 'bin\deadbyte-desktop-uia-r40.exe'))
|
|
19
|
+
if(-not (Test-Path -LiteralPath $Observer -PathType Leaf)){throw "observer binary missing: $Observer"}
|
|
20
|
+
if(-not (Test-Path -LiteralPath $Input -PathType Leaf)){throw "input binary missing: $Input"}
|
|
21
|
+
if(-not (Test-Path -LiteralPath $Grounder -PathType Leaf)){throw "UIA grounder binary missing: $Grounder"}
|
|
22
|
+
$Known=@('pointer_move','click_primary','click_secondary','double_click_primary','scroll','key_chord','text_input')
|
|
23
|
+
if($AllowedActions.Count -lt 1){throw 'AllowedActions cannot be empty'}
|
|
24
|
+
$Unique=@($AllowedActions | Sort-Object -Unique)
|
|
25
|
+
if($Unique.Count -ne $AllowedActions.Count -or @($AllowedActions | Where-Object { $_ -notin $Known }).Count -gt 0){throw 'AllowedActions invalid'}
|
|
26
|
+
New-Item -ItemType Directory -Force -Path $RuntimeRoot | Out-Null
|
|
27
|
+
$Evidence=Join-Path $RuntimeRoot 'desktop-evidence'
|
|
28
|
+
New-Item -ItemType Directory -Force -Path $Evidence | Out-Null
|
|
29
|
+
$Grant=Join-Path $RuntimeRoot 'desktop-grant.json'
|
|
30
|
+
Remove-Item -LiteralPath $Grant -Force -ErrorAction SilentlyContinue
|
|
31
|
+
$Policy=[ordered]@{
|
|
32
|
+
schema='deadbyte.desktop-policy.v1'
|
|
33
|
+
enabled=$true
|
|
34
|
+
grant_file=$Grant
|
|
35
|
+
evidence_dir=$Evidence
|
|
36
|
+
observer=[ordered]@{
|
|
37
|
+
executable=$Observer
|
|
38
|
+
sha256=(Get-FileHash -LiteralPath $Observer -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
39
|
+
backend='gdi-bitblt-fallback'
|
|
40
|
+
}
|
|
41
|
+
input=[ordered]@{
|
|
42
|
+
executable=$Input
|
|
43
|
+
sha256=(Get-FileHash -LiteralPath $Input -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
44
|
+
}
|
|
45
|
+
grounder=[ordered]@{
|
|
46
|
+
executable=$Grounder
|
|
47
|
+
sha256=(Get-FileHash -LiteralPath $Grounder -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
48
|
+
}
|
|
49
|
+
allowed_input_desktops=@('Default')
|
|
50
|
+
allowed_actions=@($AllowedActions)
|
|
51
|
+
limits=[ordered]@{
|
|
52
|
+
max_frame_age_ms=2000
|
|
53
|
+
max_frame_bytes=134217728
|
|
54
|
+
capture_timeout_ms=10000
|
|
55
|
+
input_timeout_ms=5000
|
|
56
|
+
ground_timeout_ms=5000
|
|
57
|
+
post_observe_timeout_ms=10000
|
|
58
|
+
max_preview_bytes=1048576
|
|
59
|
+
max_text_bytes=4096
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
$Parent=Split-Path -Parent $OutputPath
|
|
63
|
+
New-Item -ItemType Directory -Force -Path $Parent | Out-Null
|
|
64
|
+
$Json=$Policy | ConvertTo-Json -Depth 8
|
|
65
|
+
$Utf8=New-Object System.Text.UTF8Encoding($false)
|
|
66
|
+
$Tmp=$OutputPath+'.tmp'
|
|
67
|
+
[System.IO.File]::WriteAllText($Tmp,$Json,$Utf8)
|
|
68
|
+
Move-Item -LiteralPath $Tmp -Destination $OutputPath -Force
|
|
69
|
+
[pscustomobject]@{
|
|
70
|
+
status='created'
|
|
71
|
+
policy_path=$OutputPath
|
|
72
|
+
policy_sha256=(Get-FileHash -LiteralPath $OutputPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
73
|
+
observer_sha256=$Policy.observer.sha256
|
|
74
|
+
input_sha256=$Policy.input.sha256
|
|
75
|
+
grounder_sha256=$Policy.grounder.sha256
|
|
76
|
+
grant_file=$Grant
|
|
77
|
+
grant_state='DISARMED'
|
|
78
|
+
action_kinds=@($AllowedActions)
|
|
79
|
+
}|ConvertTo-Json -Depth 6
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { R36_CAPABILITY_LEDGER } from '../src/capability-ledger.mjs';
|
|
2
|
+
const names=new Set(['capability_manifest']);
|
|
3
|
+
for(const item of R36_CAPABILITY_LEDGER.entries) if(item.compact_tool) names.add(item.compact_tool);
|
|
4
|
+
console.log(JSON.stringify({count:names.size,names:[...names].sort(),ledger_sha256:R36_CAPABILITY_LEDGER.ledger_sha256},null,2));
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, readFile, realpath, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { loadPowerShellDataFile } from '../src/runtime-update.mjs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { probeR40FullSurface } from '../src/r40-full-surface-probe.mjs';
|
|
9
|
+
import { runR40CompactParity } from '../src/remote-mcp-r40-parity-client.mjs';
|
|
10
|
+
import { probeR40CompactDisarmed } from '../src/r40-compact-disarmed-probe.mjs';
|
|
11
|
+
import { probeR36CompactDisarmed } from '../src/r36-compact-disarmed-probe.mjs';
|
|
12
|
+
import { runR40DesktopGuardedSmoke } from '../src/remote-mcp-desktop-r40-smoke-client.mjs';
|
|
13
|
+
|
|
14
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const root=path.resolve(here,'..');
|
|
16
|
+
const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
17
|
+
const deferred=path.join(root,'scripts','deferred-slot-operation-r40.mjs');
|
|
18
|
+
const ps='C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
|
|
19
|
+
const launcher=path.join(stateRoot,'bin','DEADBYTE-MCP.ps1');
|
|
20
|
+
const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
|
|
21
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
22
|
+
|
|
23
|
+
export const R40_PREDECESSOR_RELEASE_ID='v0.11.3-ce761dd6e014d42e';
|
|
24
|
+
export const R40_PREDECESSOR_MANIFEST='ce761dd6e014d42eddb635855038e0ce049c63c6e3af7e63c3005bbe198bcf7b';
|
|
25
|
+
|
|
26
|
+
function assert(condition,message){if(!condition) throw new Error(message);}
|
|
27
|
+
function parseFrozenManifest(text){
|
|
28
|
+
const entries=[];const seen=new Set();
|
|
29
|
+
for(const [index,line] of text.split(/\r?\n/).entries()){
|
|
30
|
+
if(!line) continue;
|
|
31
|
+
const match=/^([0-9a-f]{64}) ([^\0]+)$/.exec(line);
|
|
32
|
+
assert(match,`frozen manifest line ${index+1} malformed`);
|
|
33
|
+
const rel=match[2];
|
|
34
|
+
assert(!path.posix.isAbsolute(rel)&&!rel.split('/').includes('..')&&!seen.has(rel),`unsafe/duplicate frozen manifest path: ${rel}`);
|
|
35
|
+
seen.add(rel);entries.push({sha256:match[1],path:rel});
|
|
36
|
+
}
|
|
37
|
+
return entries;
|
|
38
|
+
}
|
|
39
|
+
// Restore the mutable development stage from the immutable active release after live parity work may drift it.
|
|
40
|
+
export async function restoreFrozenStage({developmentRoot,releaseRoot,expectedManifestSha256}){
|
|
41
|
+
const dev=path.resolve(developmentRoot);const release=path.resolve(releaseRoot);
|
|
42
|
+
const manifestBytes=await readFile(path.join(release,'MANIFEST.SHA256'));
|
|
43
|
+
const manifestSha256=sha256(manifestBytes);
|
|
44
|
+
assert(manifestSha256===expectedManifestSha256,`immutable release manifest drift: ${manifestSha256}`);
|
|
45
|
+
const entries=parseFrozenManifest(manifestBytes.toString('utf8'));
|
|
46
|
+
for(const entry of entries){
|
|
47
|
+
const source=path.join(release,...entry.path.split('/'));const bytes=await readFile(source);
|
|
48
|
+
assert(sha256(bytes)===entry.sha256,`immutable release file drift: ${entry.path}`);
|
|
49
|
+
const destination=path.join(dev,...entry.path.split('/'));await mkdir(path.dirname(destination),{recursive:true});
|
|
50
|
+
await writeFile(destination,bytes);assert(sha256(await readFile(destination))===entry.sha256,`development restore verification failed: ${entry.path}`);
|
|
51
|
+
}
|
|
52
|
+
await writeFile(path.join(dev,'MANIFEST.SHA256'),manifestBytes);
|
|
53
|
+
assert(sha256(await readFile(path.join(dev,'MANIFEST.SHA256')))===expectedManifestSha256,'development manifest restore verification failed');
|
|
54
|
+
return {status:'restored',file_count:entries.length,manifest_sha256:manifestSha256};
|
|
55
|
+
}
|
|
56
|
+
function parseJsonOutput(text,label){
|
|
57
|
+
const trimmed=String(text).trim();const starts=[...trimmed.matchAll(/\{/g)].map(match=>match.index);
|
|
58
|
+
for(const start of starts){try{return JSON.parse(trimmed.slice(start));}catch{}}
|
|
59
|
+
throw new Error(`${label} did not emit JSON`);
|
|
60
|
+
}
|
|
61
|
+
function run(command,args,{timeoutMs=900000,env=process.env,cwd=root}={}){
|
|
62
|
+
return new Promise((resolve,reject)=>{
|
|
63
|
+
const child=spawn(command,args,{cwd,shell:false,windowsHide:true,env,stdio:['ignore','pipe','pipe']});
|
|
64
|
+
const out=[],err=[];const timer=setTimeout(()=>{child.kill();reject(new Error(`command timeout: ${path.basename(command)}`));},timeoutMs);
|
|
65
|
+
child.stdout.on('data',chunk=>out.push(Buffer.from(chunk)));child.stderr.on('data',chunk=>err.push(Buffer.from(chunk)));
|
|
66
|
+
child.on('error',error=>{clearTimeout(timer);reject(error);});child.on('exit',code=>{
|
|
67
|
+
clearTimeout(timer);const stdout=Buffer.concat(out).toString('utf8');const stderr=Buffer.concat(err).toString('utf8');
|
|
68
|
+
if(code!==0) reject(new Error(`command failed exit=${code}: ${stderr||stdout}`));else resolve({stdout,stderr,exitCode:code});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async function publicUrl(){
|
|
73
|
+
const value=(await readFile(path.join(stateRoot,'bridge','mcp-url.txt'),'utf8')).trim();const url=new URL(value);
|
|
74
|
+
if(url.protocol!=='https:') throw new Error('public MCP URL must be HTTPS');return value;
|
|
75
|
+
}
|
|
76
|
+
async function pointer(){return JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));}
|
|
77
|
+
async function disarmAll(){
|
|
78
|
+
for(const command of ['disarmdesktop','disarmautonomous','disarmcoding','disarmprocess','disarmhost']){
|
|
79
|
+
await run(ps,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,command],{timeoutMs:60000});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function waitDeferred(statePath,timeoutMs=180000){
|
|
83
|
+
const deadline=Date.now()+timeoutMs;
|
|
84
|
+
while(Date.now()<deadline){const state=JSON.parse(await readFile(statePath,'utf8'));if(state.state==='completed') return state;if(state.state==='failed') throw new Error(`deferred ${state.mode} failed: ${state.error}`);await sleep(500);}
|
|
85
|
+
throw new Error(`deferred operation timeout: ${statePath}`);
|
|
86
|
+
}
|
|
87
|
+
async function scheduleDeferred(mode,value,{bridgeRoot=null,bridgeScriptSha256=null}={}){
|
|
88
|
+
if((bridgeRoot===null)!==(bridgeScriptSha256===null)) throw new Error('deferred bridge override requires exact root + SHA pair');
|
|
89
|
+
const args=[deferred,mode,value];
|
|
90
|
+
if(bridgeRoot!==null) args.push('--bridge-root',bridgeRoot,'--bridge-script-sha256',bridgeScriptSha256);
|
|
91
|
+
const scheduled=parseJsonOutput((await run(process.execPath,args,{timeoutMs:30000})).stdout,`deferred ${mode}`);
|
|
92
|
+
assert(scheduled.status==='scheduled',`deferred ${mode} was not scheduled`);
|
|
93
|
+
assert(scheduled.execution_provider==='deadbyte_mcp'&&scheduled.rdc_operation_count===0,'deferred operation provider mismatch');
|
|
94
|
+
return {scheduled,completed:await waitDeferred(scheduled.state_path)};
|
|
95
|
+
}
|
|
96
|
+
async function publicR40PredecessorDisarmed(url){
|
|
97
|
+
const result=await probeR36CompactDisarmed(url);
|
|
98
|
+
assert(result.status==='passed'&&result.tool_count<=42&&result.capability_closure===true,'R40 predecessor public compact catalog mismatch');
|
|
99
|
+
assert(result.release_id===R40_PREDECESSOR_RELEASE_ID&&result.release_manifest_sha256===R40_PREDECESSOR_MANIFEST,'R40 predecessor machine binding mismatch');
|
|
100
|
+
assert(result.coding_armed===false&&result.autonomous_armed===false,'R40 predecessor authority remained armed');
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
async function mutableCore(pointerValue){
|
|
104
|
+
const policy=JSON.parse(await readFile(path.join(pointerValue.generation_root,'coding-policy.json'),'utf8'));
|
|
105
|
+
const raw=policy?.roots?.core?.path??policy?.roots?.core?.real_path;
|
|
106
|
+
assert(typeof raw==='string'&&raw,'R40 generation missing coding core root');
|
|
107
|
+
const core=path.resolve(raw);const release=path.resolve(pointerValue.release_root);
|
|
108
|
+
assert(core.toLowerCase()!==release.toLowerCase(),'R40 coding core must not point at immutable active release slot');
|
|
109
|
+
return core;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function validateParityEvidence(value){
|
|
113
|
+
assert(value?.schema==='deadbyte.r40-parity.v1','R40 parity schema mismatch');
|
|
114
|
+
assert(value.status==='passed','R40 parity status is not passed');
|
|
115
|
+
assert(value.rdc_operation_count===0&&value.execution_provider==='deadbyte_mcp','R40 parity used non-DEADBYTE execution');
|
|
116
|
+
assert(value.candidate?.version==='0.12.0'&&/^[0-9a-f]{64}$/.test(value.candidate?.manifest_sha256??''),'R40 candidate identity missing');
|
|
117
|
+
assert(value.local_full?.status==='passed'&&value.local_full?.tool_count===80&&value.local_full?.strict_output_schemas===true,'R40 local full 80-tool proof missing');
|
|
118
|
+
assert(value.public_compact?.status==='passed'&&value.public_compact?.tool_count===42&&value.public_compact?.capability_closure===true&&value.public_compact?.gateway_count===6&&value.public_compact?.desktop_armed===false&&value.public_compact?.desktop_observation_receipt_verified===true&&value.public_compact?.desktop_grounding_receipt_verified===true,'R40 public compact/Desktop closure missing');
|
|
119
|
+
assert(value.desktop_guarded?.status==='passed'&&value.desktop_guarded?.native_injected===true&&value.desktop_guarded?.replay_performed===false&&
|
|
120
|
+
Number.isInteger(value.desktop_guarded?.predispatch_retry_count)&&value.desktop_guarded.predispatch_retry_count>=0&&value.desktop_guarded.predispatch_retry_count<=2&&
|
|
121
|
+
value.desktop_guarded?.observation_receipts_verified===value.desktop_guarded.predispatch_retry_count+1&&value.desktop_guarded?.action_receipts_verified===3,
|
|
122
|
+
'R40 guarded Desktop proof missing');
|
|
123
|
+
assert(value.autonomous?.status==='passed'&&value.autonomous?.signed_release_verified===true,'R40 autonomous live proof missing');
|
|
124
|
+
assert(value.before_rollback_disarmed?.status==='passed'&&value.before_rollback_disarmed?.capability_closure===true&&value.before_rollback_disarmed?.coding_armed===false&&value.before_rollback_disarmed?.autonomous_armed===false&&value.before_rollback_disarmed?.desktop_armed===false,'R40 pre-rollback disarm proof missing');
|
|
125
|
+
assert(value.rollback?.state==='completed'&&value.restore?.state==='completed','R40 rollback/restore not completed');
|
|
126
|
+
assert(value.stage_restore?.status==='restored'&&value.stage_restore?.manifest_sha256===value.candidate.manifest_sha256,'R40 stage restore proof missing');
|
|
127
|
+
assert(value.predecessor?.release_id===R40_PREDECESSOR_RELEASE_ID&&value.predecessor?.manifest_sha256===R40_PREDECESSOR_MANIFEST&&value.predecessor?.version==='0.11.3','R40 exact predecessor identity mismatch');
|
|
128
|
+
assert(value.predecessor_disarmed?.status==='passed'&&value.predecessor_disarmed?.tool_count<=42&&value.predecessor_disarmed?.capability_closure===true,'R40 predecessor public proof missing');
|
|
129
|
+
assert(value.successor?.version==='0.12.0'&&value.successor?.manifest_sha256===value.candidate.manifest_sha256,'R40 exact successor restore identity mismatch');
|
|
130
|
+
assert(value.successor_disarmed?.status==='passed'&&value.successor_disarmed?.capability_closure===true&&value.successor_disarmed?.coding_armed===false&&value.successor_disarmed?.autonomous_armed===false&&value.successor_disarmed?.desktop_armed===false,'R40 successor disarmed proof missing');
|
|
131
|
+
assert(value.immutable_slot?.protected===true&&value.immutable_slot?.development_root!==value.immutable_slot?.release_root,'R40 immutable-slot separation missing');
|
|
132
|
+
for(const key of ['tree_read','precondition_mutation','search','owned_process','observation','coding_loop','autonomy','interrupted_recovery','evidence_verification','compact_closure','full_surface','desktop_readonly','desktop_guarded_input']) assert(value.requirements?.[key]===true,`R40 parity requirement not proven: ${key}`);
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function runReleaseParity(activationReceiptPath){
|
|
137
|
+
const active=await pointer();const candidateManifest=sha256(await readFile(path.join(root,'MANIFEST.SHA256')));
|
|
138
|
+
assert(active.version==='0.12.0','R40 parity requires active 0.12.0 candidate');
|
|
139
|
+
assert(active.manifest_sha256===candidateManifest,'active release is not canonical R40 candidate');
|
|
140
|
+
const activeConfig=await loadPowerShellDataFile(active.config_path);
|
|
141
|
+
const successorBridgeRootRaw=activeConfig?.Bridge?.WorkingDirectory;
|
|
142
|
+
const successorBridgeConfiguredSha=activeConfig?.Bridge?.ScriptSha256;
|
|
143
|
+
assert(typeof successorBridgeRootRaw==='string'&&path.isAbsolute(successorBridgeRootRaw),'R40 active bridge root missing');
|
|
144
|
+
assert(typeof successorBridgeConfiguredSha==='string'&&/^[0-9a-f]{64}$/.test(successorBridgeConfiguredSha),'R40 active bridge SHA missing');
|
|
145
|
+
const successorBridgeRoot=await realpath(path.resolve(successorBridgeRootRaw));
|
|
146
|
+
const successorBridgeActualSha=sha256(await readFile(path.join(successorBridgeRoot,'src','bridge.mjs')));
|
|
147
|
+
assert(successorBridgeActualSha===successorBridgeConfiguredSha,'R40 active bridge bytes do not match generation pin');
|
|
148
|
+
const successorBridge={root:successorBridgeRoot,sha256:successorBridgeActualSha};
|
|
149
|
+
const developmentRoot=await mutableCore(active);
|
|
150
|
+
const localFull=await probeR40FullSurface({packageRoot:active.release_root,generationRoot:active.generation_root,stateRoot});
|
|
151
|
+
const url=await publicUrl();
|
|
152
|
+
let publicCompact,autonomous,desktopGuarded;
|
|
153
|
+
try{
|
|
154
|
+
publicCompact=await runR40CompactParity(url);
|
|
155
|
+
autonomous=parseJsonOutput((await run(process.execPath,[path.join(root,'src','remote-mcp-autonomous-smoke-client.mjs'),url],{timeoutMs:900000})).stdout,'R40 autonomous compact parity');
|
|
156
|
+
assert(autonomous.status==='passed'&&autonomous.signed_release_verified===true,'R40 autonomous compact parity failed');
|
|
157
|
+
await run(ps,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,'armdesktop'],{timeoutMs:60000});
|
|
158
|
+
try{desktopGuarded=await runR40DesktopGuardedSmoke(url);}
|
|
159
|
+
finally{await run(ps,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,'disarmdesktop'],{timeoutMs:60000}).catch(()=>{});}
|
|
160
|
+
assert(desktopGuarded.status==='passed'&&desktopGuarded.native_injected===true&&desktopGuarded.action_receipts_verified===3,'R40 guarded Desktop public smoke failed');
|
|
161
|
+
}catch(error){await disarmAll().catch(()=>{});throw error;}
|
|
162
|
+
|
|
163
|
+
await disarmAll();
|
|
164
|
+
const beforeRollback=await probeR40CompactDisarmed(url);
|
|
165
|
+
const rollbackPair=await scheduleDeferred('rollback',path.resolve(activationReceiptPath));
|
|
166
|
+
const predecessor=await pointer();
|
|
167
|
+
assert(predecessor.release_id===R40_PREDECESSOR_RELEASE_ID&&predecessor.manifest_sha256===R40_PREDECESSOR_MANIFEST&&predecessor.version==='0.11.3','rollback did not reach exact sealed R40 predecessor');
|
|
168
|
+
const predecessorDisarmed=await publicR40PredecessorDisarmed(url);
|
|
169
|
+
const stageRestore=await restoreFrozenStage({developmentRoot,releaseRoot:active.release_root,expectedManifestSha256:candidateManifest});
|
|
170
|
+
const restorePair=await scheduleDeferred('activate',candidateManifest,{bridgeRoot:successorBridge.root,bridgeScriptSha256:successorBridge.sha256});
|
|
171
|
+
const successor=await pointer();
|
|
172
|
+
assert(successor.version==='0.12.0'&&successor.manifest_sha256===candidateManifest,'restore did not return exact R40 candidate');
|
|
173
|
+
const successorDisarmed=await probeR40CompactDisarmed(url);
|
|
174
|
+
await disarmAll();
|
|
175
|
+
|
|
176
|
+
const evidence={
|
|
177
|
+
schema:'deadbyte.r40-parity.v1',status:'passed',completed_at_utc:new Date().toISOString(),execution_provider:'deadbyte_mcp',rdc_operation_count:0,
|
|
178
|
+
candidate:{release_id:active.release_id,version:active.version,manifest_sha256:candidateManifest},
|
|
179
|
+
immutable_slot:{protected:true,release_root:path.resolve(active.release_root),development_root:developmentRoot},
|
|
180
|
+
local_full:localFull,public_compact:publicCompact,autonomous,desktop_guarded:desktopGuarded,before_rollback_disarmed:beforeRollback,
|
|
181
|
+
rollback:{...rollbackPair.completed,scheduled_operation_id:rollbackPair.scheduled.operation_id},
|
|
182
|
+
predecessor:{release_id:predecessor.release_id,version:predecessor.version,manifest_sha256:predecessor.manifest_sha256},predecessor_disarmed:predecessorDisarmed,
|
|
183
|
+
stage_restore:stageRestore,
|
|
184
|
+
restore:{...restorePair.completed,scheduled_operation_id:restorePair.scheduled.operation_id},
|
|
185
|
+
successor:{release_id:successor.release_id,version:successor.version,manifest_sha256:successor.manifest_sha256},successor_disarmed:successorDisarmed,
|
|
186
|
+
requirements:{
|
|
187
|
+
tree_read:publicCompact.tree_entries>=0&&publicCompact.coding_stat_verified===true,
|
|
188
|
+
precondition_mutation:publicCompact.machine_fs_receipts_verified>=3,
|
|
189
|
+
search:publicCompact.coding_search_result_count>=1&&publicCompact.observation_search_result_count>=1,
|
|
190
|
+
owned_process:typeof publicCompact.process_session_id==='string'&&publicCompact.process_output_bytes>0,
|
|
191
|
+
observation:publicCompact.observation_receipts_verified>=2,
|
|
192
|
+
coding_loop:publicCompact.coding_receipts_verified===2,
|
|
193
|
+
autonomy:autonomous.signed_release_verified===true&&autonomous.observed_failure_before_replan===true,
|
|
194
|
+
interrupted_recovery:rollbackPair.completed.state==='completed'&&restorePair.completed.state==='completed',
|
|
195
|
+
evidence_verification:publicCompact.machine_fs_receipts_verified>=3&&autonomous.signed_release_verified===true,
|
|
196
|
+
compact_closure:publicCompact.capability_closure===true&&successorDisarmed.capability_closure===true,
|
|
197
|
+
full_surface:localFull.tool_count===80&&localFull.strict_output_schemas===true,
|
|
198
|
+
desktop_readonly:publicCompact.desktop_armed===false&&publicCompact.desktop_observation_receipt_verified===true&&publicCompact.desktop_grounding_receipt_verified===true,
|
|
199
|
+
desktop_guarded_input:desktopGuarded.native_injected===true&&desktopGuarded.replay_performed===false&&desktopGuarded.action_receipts_verified===3
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
validateParityEvidence(evidence);
|
|
203
|
+
const evidenceDir=path.join(stateRoot,'evidence','release');await mkdir(evidenceDir,{recursive:true});
|
|
204
|
+
const evidencePath=path.join(evidenceDir,`r40-parity-${candidateManifest.slice(0,16)}.json`);
|
|
205
|
+
const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');await writeFile(evidencePath,bytes);
|
|
206
|
+
return {evidence,evidence_path:evidencePath,evidence_sha256:sha256(bytes)};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){
|
|
210
|
+
const receipt=process.argv[2];if(!receipt) throw new Error('usage: node scripts/release-parity-r40.mjs <activation-receipt.json>');
|
|
211
|
+
runReleaseParity(receipt).then(result=>console.log(JSON.stringify({status:'passed',...result},null,2))).catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
212
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
$ErrorActionPreference = 'Stop'
|
|
2
|
+
Set-StrictMode -Version 2.0
|
|
3
|
+
$root = Split-Path -Parent $PSScriptRoot
|
|
4
|
+
Push-Location $root
|
|
5
|
+
try {
|
|
6
|
+
& node.exe --test test\controller.test.mjs test\controller-desktop-r40.test.mjs test\r40-finalization.test.mjs test\release-version.test.mjs test\desktop-contract-r40.test.mjs test\desktop-policy-r40.test.mjs test\desktop-observer-r40.test.mjs test\desktop-input-r40.test.mjs test\desktop-uia-r40.test.mjs test\desktop-runtime-r40.test.mjs test\desktop-memory-r40.test.mjs test\desktop-mcp-tools-r40.test.mjs test\desktop-adversarial-r40.test.mjs test\autonomous-desktop-r40.test.mjs test\release-generation.test.mjs test\runtime-self-update.test.mjs test\r37-0110-history.test.mjs test\r37-agent-fabric.test.mjs test\remote-live-log-r37.test.mjs test\remote-session-cli.test.mjs test\r36-finalization.test.mjs test\r36-release-identity.test.mjs test\deferred-slot-operation.test.mjs
|
|
7
|
+
if ($LASTEXITCODE -ne 0) { throw "R40 release parity tests failed with exit code $LASTEXITCODE" }
|
|
8
|
+
Write-Output 'R40 RELEASE PARITY TEST PASS'
|
|
9
|
+
} finally { Pop-Location }
|
|
@@ -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
|
+
}
|
|
@@ -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, 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,7 @@ 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),
|
|
252
253
|
capabilities:capabilities===null?null:structuredClone(capabilities),
|
|
253
254
|
profiles
|
|
254
255
|
};
|
|
@@ -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,7 @@ 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,
|
|
337
358
|
capabilities:view.capabilities ?? null
|
|
338
359
|
});
|
|
339
360
|
const stateJson = canonicalJson(state);
|
|
@@ -350,6 +371,7 @@ export function buildPlannerPrompt({ goal, policy, view }) {
|
|
|
350
371
|
'patch: operations are DEADBYTE Native Diff operations (write, replace, delete) and MUST use exact SHA-256 preconditions from supplied context.',
|
|
351
372
|
'run_profile: profile_id must be one of the policy profile ids in state.',
|
|
352
373
|
'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.',
|
|
374
|
+
'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
375
|
'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
376
|
'finish only when the goal is actually satisfied. Required release profiles are enforced by the engine and cannot be bypassed.',
|
|
355
377
|
'Never invent a file SHA. If content is elided or the current SHA/content is missing, choose inspect first.',
|