deadbyte-mcp 0.14.0 → 0.15.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/AGENTS.md +11 -18
- package/CONTEXT.md +3 -3
- package/MANIFEST.SHA256 +104 -53
- package/R43-PARENT.json +20 -0
- package/R44-DONORS.json +99 -0
- package/R44-PARENT.json +18 -0
- package/README.txt +7 -2
- package/controller/README.TXT +4 -5
- package/controller/deadbyte-controller.ps1 +1 -1
- package/docs/ARCHITECTURE.md +8 -1
- package/docs/R44-ARCHITECTURE.md +37 -0
- package/package.json +13 -8
- package/scripts/deferred-slot-operation-r43.mjs +126 -0
- package/scripts/deferred-slot-operation-r44.mjs +126 -0
- package/scripts/final-closure-r43.mjs +115 -0
- package/scripts/final-closure-r44.mjs +121 -0
- package/scripts/final-closure-verify-r43.mjs +213 -0
- package/scripts/final-closure-verify-r44.mjs +220 -0
- package/scripts/finalize-r43.mjs +217 -0
- package/scripts/finalize-r44.mjs +217 -0
- package/scripts/full-regression-r43.mjs +20 -0
- package/scripts/full-regression-r44.mjs +20 -0
- package/scripts/gate-windows-r43.ps1 +36 -0
- package/scripts/gate-windows-r44.ps1 +36 -0
- package/scripts/release-parity-r43.mjs +218 -0
- package/scripts/release-parity-r44.mjs +242 -0
- package/scripts/release-parity-tests-r43.ps1 +8 -0
- package/scripts/release-parity-tests-r44.ps1 +8 -0
- package/scripts/verify-r43-parent.mjs +126 -0
- package/scripts/verify-r44-parent.mjs +142 -0
- package/scripts/windows-gate-evidence-r43.mjs +55 -0
- package/scripts/windows-gate-evidence-r44.mjs +55 -0
- package/src/agent-backend-r44.mjs +254 -0
- package/src/autonomous-output-contracts.mjs +1 -1
- package/src/autonomous-policy.mjs +41 -4
- package/src/autonomous-runtime.mjs +25 -12
- package/src/autonomous-supervisor-host.mjs +12 -2
- package/src/capability-ledger.mjs +2 -1
- package/src/codex-app-server-r44.mjs +154 -0
- package/src/coding-plane.mjs +66 -19
- package/src/durable-job-r44.mjs +145 -0
- package/src/external-job-provider-r44.mjs +126 -0
- package/src/host-plane.mjs +27 -6
- package/src/job-supervisor-host-r44.mjs +112 -0
- package/src/machine-fs-smoke-client.mjs +1 -1
- package/src/machine-fs.mjs +21 -10
- package/src/mcp-host-smoke-client.mjs +1 -1
- package/src/mcp-server.mjs +20 -3
- package/src/mcp-smoke-client.mjs +1 -1
- package/src/native-diff-v2.mjs +3 -3
- package/src/native-diff.mjs +3 -2
- package/src/observation-smoke-client.mjs +1 -1
- package/src/operator-surface.mjs +2 -2
- package/src/process-policy.mjs +19 -0
- package/src/process-runtime.mjs +5 -2
- package/src/process-smoke-client.mjs +2 -2
- package/src/provider-circuit.mjs +8 -2
- package/src/r44-compact-disarmed-probe.mjs +74 -0
- package/src/r44-full-surface-probe.mjs +34 -0
- package/src/remote-device-r44.mjs +205 -0
- package/src/remote-mcp-autonomous-smoke-client.mjs +4 -4
- package/src/remote-mcp-r44-parity-client.mjs +203 -0
- package/src/remote-mcp-smoke-client.mjs +7 -4
- package/src/runtime-update.mjs +64 -31
- package/src/skill-mcp-tools-r44.mjs +37 -0
- package/src/skill-plane-r44.mjs +203 -0
- package/src/tunnel-stdio-compat-smoke.mjs +1 -1
- package/src/version.mjs +1 -1
- package/test/agent-backend-r44.test.mjs +115 -0
- package/test/agent-runtime.test.mjs +3 -3
- package/test/autonomous-loop.test.mjs +97 -11
- package/test/autonomous-runtime.test.mjs +2 -2
- package/test/capability-ledger-r36.test.mjs +3 -3
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/codex-app-server-r44.test.mjs +115 -0
- package/test/codex-autonomous-r44.test.mjs +87 -0
- package/test/coding-plane.test.mjs +2 -2
- package/test/coding-runtime.test.mjs +36 -1
- package/test/compact-catalog-r36.test.mjs +3 -3
- package/test/compact-max-catalog-r40.test.mjs +4 -2
- package/test/compact-max-catalog-r44.test.mjs +97 -0
- package/test/core.test.mjs +1 -1
- package/test/durable-job-r44.test.mjs +75 -0
- package/test/external-job-provider-r44.test.mjs +46 -0
- package/test/full-max-catalog-r40.test.mjs +5 -3
- package/test/full-max-catalog-r44.test.mjs +91 -0
- package/test/host-plane.test.mjs +15 -1
- package/test/native-diff.test.mjs +28 -0
- package/test/output-schema.test.mjs +2 -2
- package/test/process-policy.test.mjs +22 -1
- package/test/prompts-resources.test.mjs +1 -1
- package/test/r33-closeout-regression.test.mjs +13 -30
- package/test/r33-finalization.test.mjs +7 -7
- package/test/r43-finalization.test.mjs +31 -0
- package/test/r43-finalizer.test.mjs +34 -0
- package/test/r43-parent.test.mjs +29 -0
- package/test/r44-finalization.test.mjs +58 -0
- package/test/r44-finalizer.test.mjs +48 -0
- package/test/r44-parent.test.mjs +25 -0
- package/test/release-version.test.mjs +53 -19
- package/test/remote-device-r44.test.mjs +104 -0
- package/test/runtime-self-update.test.mjs +42 -0
- package/test/skill-mcp-r44.test.mjs +38 -0
- package/test/skill-plane-r44.test.mjs +93 -0
- package/test/standing-grants.test.mjs +5 -1
package/src/host-plane.mjs
CHANGED
|
@@ -157,6 +157,22 @@ export async function assertHostArmed(policy, now = new Date()) {
|
|
|
157
157
|
return { arm, standing:false, remaining_ms: expires - now.getTime() };
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
export async function recheckHostAuthority(policy, initial, now = new Date()) {
|
|
161
|
+
if (!initial?.arm || typeof initial.arm.instance_nonce !== 'string') {
|
|
162
|
+
throw new Error('host authority recheck requires an armed initial snapshot');
|
|
163
|
+
}
|
|
164
|
+
let current;
|
|
165
|
+
try { current = await assertHostArmed(policy, now); }
|
|
166
|
+
catch (error) { throw new Error(`host authority changed during operation: ${String(error?.message ?? error)}`); }
|
|
167
|
+
const changed =
|
|
168
|
+
current.arm.schema !== initial.arm.schema ||
|
|
169
|
+
current.arm.instance_nonce !== initial.arm.instance_nonce ||
|
|
170
|
+
current.standing !== initial.standing ||
|
|
171
|
+
(current.arm.expires_at_utc ?? null) !== (initial.arm.expires_at_utc ?? null);
|
|
172
|
+
if (changed) throw new Error('host authority changed during operation');
|
|
173
|
+
return current;
|
|
174
|
+
}
|
|
175
|
+
|
|
160
176
|
function getRoot(policy, rootId, permission) {
|
|
161
177
|
if (!ROOT_ID_RE.test(rootId)) throw new Error('invalid host root id');
|
|
162
178
|
const root = policy.roots[rootId];
|
|
@@ -190,7 +206,7 @@ async function nearestExistingParent(candidate) {
|
|
|
190
206
|
}
|
|
191
207
|
}
|
|
192
208
|
|
|
193
|
-
async function resolveHostWritable(policy, rootId, relativePath) {
|
|
209
|
+
async function resolveHostWritable(policy, rootId, relativePath, authorityCheck = null) {
|
|
194
210
|
validatePortableRelativePath(relativePath);
|
|
195
211
|
const root = getRoot(policy, rootId, 'write');
|
|
196
212
|
const lexical = path.resolve(root.real_path, relativePath);
|
|
@@ -201,6 +217,7 @@ async function resolveHostWritable(policy, rootId, relativePath) {
|
|
|
201
217
|
const realExisting = await realpath(existingParent);
|
|
202
218
|
if (!isInside(root.real_path, realExisting)) throw new Error('host parent symlink escapes allowed root');
|
|
203
219
|
|
|
220
|
+
if (authorityCheck) await authorityCheck();
|
|
204
221
|
await mkdir(parent, { recursive: true });
|
|
205
222
|
const realParent = await realpath(parent);
|
|
206
223
|
if (!isInside(root.real_path, realParent)) throw new Error('host parent resolves outside allowed root');
|
|
@@ -265,8 +282,9 @@ function checkExpectedState(state, expectedSha256) {
|
|
|
265
282
|
}
|
|
266
283
|
|
|
267
284
|
export async function hostFileWrite(policy, { rootId, relativePath, content, encoding = 'utf8', expectedSha256 }) {
|
|
268
|
-
await assertHostArmed(policy);
|
|
269
|
-
const
|
|
285
|
+
const armed = await assertHostArmed(policy);
|
|
286
|
+
const authorityCheck = () => recheckHostAuthority(policy, armed);
|
|
287
|
+
const { lexical } = await resolveHostWritable(policy, rootId, relativePath, authorityCheck);
|
|
270
288
|
const bytes = decodeWriteContent(content, encoding);
|
|
271
289
|
if (bytes.length > policy.max_write_bytes) throw new Error(`host write exceeds ${policy.max_write_bytes} byte limit`);
|
|
272
290
|
|
|
@@ -276,6 +294,7 @@ export async function hostFileWrite(policy, { rootId, relativePath, content, enc
|
|
|
276
294
|
const temp = path.join(path.dirname(lexical), `.${path.basename(lexical)}.deadbyte-${process.pid}-${randomBytes(8).toString('hex')}.tmp`);
|
|
277
295
|
let handle;
|
|
278
296
|
try {
|
|
297
|
+
await authorityCheck();
|
|
279
298
|
handle = await open(temp, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY, 0o600);
|
|
280
299
|
await handle.writeFile(bytes);
|
|
281
300
|
await handle.sync();
|
|
@@ -284,6 +303,7 @@ export async function hostFileWrite(policy, { rootId, relativePath, content, enc
|
|
|
284
303
|
|
|
285
304
|
const raceCheck = await currentTargetState(lexical);
|
|
286
305
|
checkExpectedState(raceCheck, expectedSha256);
|
|
306
|
+
await authorityCheck();
|
|
287
307
|
await rename(temp, lexical);
|
|
288
308
|
} catch (error) {
|
|
289
309
|
if (handle) await handle.close().catch(() => {});
|
|
@@ -366,10 +386,11 @@ export async function hostExec(policy, {
|
|
|
366
386
|
cwdResolved = realCwd;
|
|
367
387
|
}
|
|
368
388
|
|
|
369
|
-
if (Number.isFinite(armed.remaining_ms) && armed.remaining_ms < 100) throw new Error('privileged host plane arm lease expires too soon');
|
|
370
389
|
const requestedTimeout = timeoutMs === null ? command.timeout_ms : finiteInteger(timeoutMs, 'timeout_ms', 100, command.timeout_ms, command.timeout_ms);
|
|
371
|
-
const
|
|
372
|
-
const
|
|
390
|
+
const activeArm = await recheckHostAuthority(policy, armed);
|
|
391
|
+
const grantBound = Number.isFinite(activeArm.remaining_ms) ? Math.floor(activeArm.remaining_ms) : command.timeout_ms;
|
|
392
|
+
if (grantBound < 100) throw new Error('privileged host plane arm lease expires too soon');
|
|
393
|
+
const effectiveTimeout = Math.min(requestedTimeout, command.timeout_ms, grantBound);
|
|
373
394
|
const argv = [...command.prefix_args, script.resolved, ...args];
|
|
374
395
|
|
|
375
396
|
const child = spawn(command.executable, argv, {
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
import { closeSync, openSync } from 'node:fs';
|
|
3
|
+
import { lstat, mkdir, open, readFile, realpath, rename, rm } from 'node:fs/promises';
|
|
4
|
+
import net from 'node:net';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
|
|
8
|
+
const HEX64=/^[0-9a-f]{64}$/;
|
|
9
|
+
const ID=/^[A-Za-z0-9._-]{1,64}$/;
|
|
10
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
11
|
+
function insist(ok,message){if(!ok)throw new Error(message);}
|
|
12
|
+
async function atomicJson(file,value){
|
|
13
|
+
const bytes=Buffer.from(`${JSON.stringify(value,null,2)}\n`,'utf8');
|
|
14
|
+
const temp=`${file}.${process.pid}.${randomBytes(6).toString('hex')}.tmp`;
|
|
15
|
+
const h=await open(temp,'wx');try{await h.writeFile(bytes);await h.sync();}finally{await h.close();}
|
|
16
|
+
await rename(temp,file);
|
|
17
|
+
}
|
|
18
|
+
const arg=process.argv.indexOf('--state-dir');
|
|
19
|
+
if(arg<0||!process.argv[arg+1])throw new Error('R44 job supervisor host requires --state-dir');
|
|
20
|
+
const stateDir=await realpath(path.resolve(process.argv[arg+1]));
|
|
21
|
+
const authPath=path.join(stateDir,'supervisor-auth.json');
|
|
22
|
+
const endpointPath=path.join(stateDir,'supervisor-endpoint.json');
|
|
23
|
+
const auth=JSON.parse(await readFile(authPath,'utf8'));
|
|
24
|
+
insist(auth?.schema==='deadbyte.job-supervisor-auth.v1'&&HEX64.test(auth.token??''),'supervisor auth state invalid');
|
|
25
|
+
const authBytes=Buffer.from(auth.token,'hex');
|
|
26
|
+
const instanceId=randomBytes(32).toString('hex');
|
|
27
|
+
const jobs=new Map();
|
|
28
|
+
const logs=path.join(stateDir,'logs');await mkdir(logs,{recursive:true});
|
|
29
|
+
|
|
30
|
+
function publicHandle(job){
|
|
31
|
+
return {handle_id:job.handle_id,pid:job.pid,creation_id:job.creation_id,profile_id:job.profile_id,state:job.state,exit_code:job.exit_code??null};
|
|
32
|
+
}
|
|
33
|
+
function exactJob(input){
|
|
34
|
+
insist(input&&typeof input==='object','supervisor handle missing');
|
|
35
|
+
const job=jobs.get(input.handle_id);insist(job,'unknown supervisor handle');
|
|
36
|
+
insist(job.pid===input.pid&&job.creation_id===input.creation_id&&job.profile_id===input.profile_id,'supervisor process identity mismatch');
|
|
37
|
+
return job;
|
|
38
|
+
}
|
|
39
|
+
async function verifyExecutable(file,expected){
|
|
40
|
+
insist(path.isAbsolute(file)&&HEX64.test(expected??''),'supervisor executable identity invalid');
|
|
41
|
+
const st=await lstat(file);insist(st.isFile()&&!st.isSymbolicLink(),'supervisor executable must be real file');
|
|
42
|
+
insist(sha(await readFile(file))===expected,'supervisor executable hash mismatch');
|
|
43
|
+
}
|
|
44
|
+
async function handle(op,args){
|
|
45
|
+
if(op==='hello')return {kind:'external-supervisor',instance_id:instanceId,pid:process.pid};
|
|
46
|
+
if(op==='start'){
|
|
47
|
+
insist(args&&HEX64.test(args.job_id??'')&&ID.test(args.profile_id??''),'supervisor start identity invalid');
|
|
48
|
+
insist(Array.isArray(args.argv)&&args.argv.length<=128&&args.argv.every(v=>typeof v==='string'&&v.length<=8192),'supervisor argv invalid');
|
|
49
|
+
await verifyExecutable(args.executable,args.executable_sha256);
|
|
50
|
+
insist(typeof args.cwd==='string'&&path.isAbsolute(args.cwd),'supervisor cwd must be absolute');
|
|
51
|
+
const cwd=await realpath(args.cwd);const cwdSt=await lstat(cwd);insist(cwdSt.isDirectory()&&!cwdSt.isSymbolicLink(),'supervisor cwd invalid');
|
|
52
|
+
const handleId=randomBytes(32).toString('hex'),creationId=randomBytes(32).toString('hex');
|
|
53
|
+
const stdoutPath=path.join(logs,`${handleId}.stdout.log`),stderrPath=path.join(logs,`${handleId}.stderr.log`);
|
|
54
|
+
const outFd=openSync(stdoutPath,'a'),errFd=openSync(stderrPath,'a');
|
|
55
|
+
let child;
|
|
56
|
+
try{
|
|
57
|
+
child=spawn(args.executable,args.argv,{cwd,windowsHide:true,shell:false,detached:true,stdio:['ignore',outFd,errFd]});
|
|
58
|
+
}finally{closeSync(outFd);closeSync(errFd);}
|
|
59
|
+
insist(Number.isInteger(child.pid)&&child.pid>0,'supervisor child spawn failed');
|
|
60
|
+
const job={handle_id:handleId,pid:child.pid,creation_id:creationId,profile_id:args.profile_id,state:'running',exit_code:null,
|
|
61
|
+
child,stdout_path:stdoutPath,stderr_path:stderrPath,started_at_utc:new Date().toISOString()};
|
|
62
|
+
jobs.set(handleId,job);
|
|
63
|
+
child.once('exit',(code)=>{job.state='terminated';job.exit_code=Number.isInteger(code)?code:null;});
|
|
64
|
+
child.once('error',()=>{job.state='failed';});
|
|
65
|
+
child.unref();
|
|
66
|
+
return publicHandle(job);
|
|
67
|
+
}
|
|
68
|
+
if(op==='reattach'||op==='status')return publicHandle(exactJob(args));
|
|
69
|
+
if(op==='terminate'){
|
|
70
|
+
const job=exactJob(args);
|
|
71
|
+
if(job.state==='running'){
|
|
72
|
+
const exited=new Promise(resolve=>job.child.once('exit',resolve));
|
|
73
|
+
job.child.kill();
|
|
74
|
+
await Promise.race([exited,new Promise(resolve=>setTimeout(resolve,3000))]);
|
|
75
|
+
if(job.state==='running')job.state='terminated';
|
|
76
|
+
}
|
|
77
|
+
return publicHandle(job);
|
|
78
|
+
}
|
|
79
|
+
if(op==='shutdown'){
|
|
80
|
+
for(const job of jobs.values())if(job.state==='running')job.child.kill();
|
|
81
|
+
return {status:'shutting_down'};
|
|
82
|
+
}
|
|
83
|
+
throw new Error('unknown supervisor operation');
|
|
84
|
+
}
|
|
85
|
+
function authOk(token){
|
|
86
|
+
if(!HEX64.test(token??''))return false;
|
|
87
|
+
const candidate=Buffer.from(token,'hex');return candidate.length===authBytes.length&&timingSafeEqual(candidate,authBytes);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const server=net.createServer(socket=>{
|
|
91
|
+
socket.setEncoding('utf8');let buffer='';
|
|
92
|
+
socket.on('data',chunk=>{
|
|
93
|
+
buffer+=chunk;
|
|
94
|
+
if(Buffer.byteLength(buffer,'utf8')>1024*1024){socket.destroy();return;}
|
|
95
|
+
const nl=buffer.indexOf('\n');if(nl<0)return;
|
|
96
|
+
const line=buffer.slice(0,nl);buffer='';
|
|
97
|
+
void(async()=>{
|
|
98
|
+
let request;try{request=JSON.parse(line);}catch{socket.end(JSON.stringify({ok:false,error:'invalid JSON'})+'\n');return;}
|
|
99
|
+
if(!authOk(request?.token)){socket.end(JSON.stringify({ok:false,error:'unauthorized'})+'\n');return;}
|
|
100
|
+
try{
|
|
101
|
+
const result=await handle(request.op,request.args??{});
|
|
102
|
+
socket.end(JSON.stringify({ok:true,result})+'\n');
|
|
103
|
+
if(request.op==='shutdown'){
|
|
104
|
+
setTimeout(async()=>{server.close();await rm(endpointPath,{force:true}).catch(()=>{});process.exit(0);},50).unref();
|
|
105
|
+
}
|
|
106
|
+
}catch(error){socket.end(JSON.stringify({ok:false,error:String(error?.message??error)})+'\n');}
|
|
107
|
+
})();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
await new Promise((resolve,reject)=>{server.once('error',reject);server.listen(0,'127.0.0.1',resolve);});
|
|
111
|
+
const address=server.address();insist(address&&typeof address==='object','supervisor listen address invalid');
|
|
112
|
+
await atomicJson(endpointPath,{schema:'deadbyte.job-supervisor-endpoint.v1',host:'127.0.0.1',port:address.port,instance_id:instanceId,pid:process.pid,created_at_utc:new Date().toISOString()});
|
|
@@ -56,7 +56,7 @@ const transport = new StdioClientTransport({
|
|
|
56
56
|
});
|
|
57
57
|
const stderrChunks = [];
|
|
58
58
|
transport.stderr?.on('data', chunk => stderrChunks.push(Buffer.from(chunk)));
|
|
59
|
-
const baseTools = ['contained_transform','evidence_verify','file_read','proof_execute','workspace_open'];
|
|
59
|
+
const baseTools = ['contained_transform','evidence_verify','file_read','proof_execute','skill_catalog','workspace_open'];
|
|
60
60
|
const codingTools = [
|
|
61
61
|
'coding_benchmark_run','coding_command_run','coding_file_read','coding_file_write','coding_files_read',
|
|
62
62
|
'coding_receipt_verify','coding_search','coding_session_status','coding_stat',
|
package/src/machine-fs.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { createReadStream } from 'node:fs';
|
|
|
3
3
|
import { lstat, mkdir as fsMkdir, open, readFile, readdir, realpath, rename, rm, rmdir } from 'node:fs/promises';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { canonicalJson } from './canonical-json.mjs';
|
|
6
|
-
import { assertCodingArmed } from './coding-plane.mjs';
|
|
6
|
+
import { assertCodingArmed, recheckCodingAuthority } from './coding-plane.mjs';
|
|
7
7
|
import { isInside, validatePortableRelativePath } from './path-policy.mjs';
|
|
8
8
|
import { decodeUtf8Strict } from './text-policy.mjs';
|
|
9
9
|
import { loadPrivateSigningKey, loadPublicVerifyKey } from './trust.mjs';
|
|
@@ -219,18 +219,20 @@ function requestSha(operation, args) {
|
|
|
219
219
|
return sha256Object({ schema: 'deadbyte.machine-fs-request.v1', operation, args });
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
async function atomicWrite(target, bytes, expectedSha256) {
|
|
222
|
+
async function atomicWrite(target, bytes, expectedSha256, authorityCheck = null) {
|
|
223
223
|
const before = await regularState(target);
|
|
224
224
|
checkExpectedFile(before, expectedSha256);
|
|
225
225
|
const temp = path.join(path.dirname(target), `.${path.basename(target)}.deadbyte-machine-fs-${process.pid}-${randomBytes(8).toString('hex')}.tmp`);
|
|
226
226
|
let handle;
|
|
227
227
|
try {
|
|
228
|
+
if (authorityCheck) await authorityCheck();
|
|
228
229
|
handle = await open(temp, 'wx', 0o600);
|
|
229
230
|
await handle.writeFile(bytes);
|
|
230
231
|
await handle.sync();
|
|
231
232
|
await handle.close();
|
|
232
233
|
handle = null;
|
|
233
234
|
checkExpectedFile(await regularState(target), expectedSha256);
|
|
235
|
+
if (authorityCheck) await authorityCheck();
|
|
234
236
|
await rename(temp, target);
|
|
235
237
|
} catch (error) {
|
|
236
238
|
if (handle) await handle.close().catch(() => {});
|
|
@@ -322,8 +324,10 @@ export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath }
|
|
|
322
324
|
content_sha256: sha256Bytes(bytes)
|
|
323
325
|
});
|
|
324
326
|
const started = nowUtc();
|
|
325
|
-
|
|
326
|
-
const
|
|
327
|
+
let activeGrant = grant;
|
|
328
|
+
const authorityCheck = async () => { activeGrant = await recheckCodingAuthority(policy, grant, rootId); return activeGrant; };
|
|
329
|
+
const { before, after } = await atomicWrite(target.lexical, bytes, expectedSha256, authorityCheck);
|
|
330
|
+
const signed = await signReceipt(policy, signingKeyPath, activeGrant, 'machine_fs_write', request, {
|
|
327
331
|
root_id: rootId, path: target.relative, started_at_utc: started, completed_at_utc: nowUtc(),
|
|
328
332
|
before_sha256: before.sha256, after_sha256: after.sha256, bytes: after.bytes
|
|
329
333
|
}, receiptId);
|
|
@@ -358,8 +362,10 @@ export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath }
|
|
|
358
362
|
old_sha256: sha256Bytes(Buffer.from(oldString, 'utf8')), new_sha256: sha256Bytes(Buffer.from(newString, 'utf8'))
|
|
359
363
|
});
|
|
360
364
|
const started = nowUtc();
|
|
361
|
-
|
|
362
|
-
const
|
|
365
|
+
let activeGrant = grant;
|
|
366
|
+
const authorityCheck = async () => { activeGrant = await recheckCodingAuthority(policy, grant, rootId); return activeGrant; };
|
|
367
|
+
const { after } = await atomicWrite(found.lexical, nextBytes, expectedSha256, authorityCheck);
|
|
368
|
+
const signed = await signReceipt(policy, signingKeyPath, activeGrant, 'machine_fs_edit_exact', request, {
|
|
363
369
|
root_id: rootId, path: found.relative, started_at_utc: started, completed_at_utc: nowUtc(),
|
|
364
370
|
before_sha256: beforeSha, after_sha256: after.sha256, replacements: count, bytes: after.bytes
|
|
365
371
|
});
|
|
@@ -376,11 +382,12 @@ export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath }
|
|
|
376
382
|
catch (error) { if (error?.code !== 'ENOENT') throw error; }
|
|
377
383
|
const request = requestSha('machine_fs_mkdir', { root_id: rootId, path: target.relative, expected: 'absent' });
|
|
378
384
|
const started = nowUtc();
|
|
385
|
+
const activeGrant = await recheckCodingAuthority(policy, grant, rootId);
|
|
379
386
|
await fsMkdir(target.lexical, { recursive: false });
|
|
380
387
|
try {
|
|
381
388
|
const identity = await objectIdentity(target.lexical);
|
|
382
389
|
if (identity.kind !== 'directory') throw new Error('machine fs mkdir post-commit type mismatch');
|
|
383
|
-
const signed = await signReceipt(policy, signingKeyPath,
|
|
390
|
+
const signed = await signReceipt(policy, signingKeyPath, activeGrant, 'machine_fs_mkdir', request, {
|
|
384
391
|
root_id: rootId, path: target.relative, started_at_utc: started, completed_at_utc: nowUtc(),
|
|
385
392
|
before_identity_sha256: null, after_identity_sha256: identity.identity_sha256
|
|
386
393
|
}, receiptId);
|
|
@@ -409,11 +416,12 @@ export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath }
|
|
|
409
416
|
const started = nowUtc();
|
|
410
417
|
const race = await objectIdentity(source.resolved);
|
|
411
418
|
if (race.identity_sha256 !== expectedSourceIdentity) throw new Error(`machine fs source identity mismatch before commit: expected ${expectedSourceIdentity}, found ${race.identity_sha256}`);
|
|
419
|
+
const activeGrant = await recheckCodingAuthority(policy, grant, rootId);
|
|
412
420
|
await rename(source.lexical, destination.lexical);
|
|
413
421
|
try {
|
|
414
422
|
const after = await objectIdentity(destination.lexical);
|
|
415
423
|
if (after.identity_sha256 !== expectedSourceIdentity) throw new Error('machine fs move post-commit identity mismatch');
|
|
416
|
-
const signed = await signReceipt(policy, signingKeyPath,
|
|
424
|
+
const signed = await signReceipt(policy, signingKeyPath, activeGrant, 'machine_fs_move', request, {
|
|
417
425
|
root_id: rootId, source_path: source.relative, destination_path: destination.relative,
|
|
418
426
|
started_at_utc: started, completed_at_utc: nowUtc(), source_identity_sha256: expectedSourceIdentity
|
|
419
427
|
}, receiptId);
|
|
@@ -436,6 +444,7 @@ export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath }
|
|
|
436
444
|
const before = await objectIdentity(source.resolved);
|
|
437
445
|
if (before.identity_sha256 !== expectedSourceIdentity) throw new Error(`machine fs source identity mismatch: expected ${expectedSourceIdentity}, found ${before.identity_sha256}`);
|
|
438
446
|
const recoveryRoot = path.join(source.root.real_path, '.deadbyte-machine-fs-recovery');
|
|
447
|
+
let activeGrant = await recheckCodingAuthority(policy, grant, rootId);
|
|
439
448
|
await fsMkdir(recoveryRoot, { recursive: true });
|
|
440
449
|
const recoveryKey = sha256Object({ source_path: source.relative, identity_sha256: expectedSourceIdentity });
|
|
441
450
|
const recoveryTarget = path.join(recoveryRoot, recoveryKey);
|
|
@@ -443,11 +452,12 @@ export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath }
|
|
|
443
452
|
catch (error) { if (error?.code !== 'ENOENT') throw error; }
|
|
444
453
|
const request = requestSha('machine_fs_delete', { root_id: rootId, path: source.relative, expected_source_identity: expectedSourceIdentity });
|
|
445
454
|
const started = nowUtc();
|
|
455
|
+
activeGrant = await recheckCodingAuthority(policy, grant, rootId);
|
|
446
456
|
await rename(source.lexical, recoveryTarget);
|
|
447
457
|
try {
|
|
448
458
|
const recovered = await objectIdentity(recoveryTarget);
|
|
449
459
|
if (recovered.identity_sha256 !== expectedSourceIdentity) throw new Error('machine fs delete recovery identity mismatch');
|
|
450
|
-
const signed = await signReceipt(policy, signingKeyPath,
|
|
460
|
+
const signed = await signReceipt(policy, signingKeyPath, activeGrant, 'machine_fs_delete', request, {
|
|
451
461
|
root_id: rootId, path: source.relative, started_at_utc: started, completed_at_utc: nowUtc(),
|
|
452
462
|
source_identity_sha256: expectedSourceIdentity, recovery_key: recoveryKey
|
|
453
463
|
});
|
|
@@ -485,11 +495,12 @@ export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath }
|
|
|
485
495
|
if (race.kind !== 'directory' || race.entries.length !== 0 || race.identity_sha256 !== expectedSourceIdentity) {
|
|
486
496
|
throw new Error('machine fs rmdir precondition changed before commit');
|
|
487
497
|
}
|
|
498
|
+
const activeGrant = await recheckCodingAuthority(policy, grant, rootId);
|
|
488
499
|
await rmdir(source.lexical);
|
|
489
500
|
try {
|
|
490
501
|
try { await lstat(source.lexical); throw new Error('machine fs rmdir post-commit path still exists'); }
|
|
491
502
|
catch (error) { if (error?.code !== 'ENOENT') throw error; }
|
|
492
|
-
const signed = await signReceipt(policy, signingKeyPath,
|
|
503
|
+
const signed = await signReceipt(policy, signingKeyPath, activeGrant, 'machine_fs_rmdir_empty', request, {
|
|
493
504
|
root_id: rootId, path: source.relative, started_at_utc: started, completed_at_utc: nowUtc(),
|
|
494
505
|
before_identity_sha256: expectedSourceIdentity, after_identity_sha256: null
|
|
495
506
|
}, receiptId);
|
|
@@ -84,7 +84,7 @@ function structured(result, label) {
|
|
|
84
84
|
try {
|
|
85
85
|
await client.connect(transport);
|
|
86
86
|
const names = (await client.listTools()).tools.map(t => t.name).sort();
|
|
87
|
-
const expected = ['contained_transform', 'evidence_verify', 'file_read', 'host_exec', 'host_file_read', 'host_file_write', 'proof_execute', 'workspace_open'];
|
|
87
|
+
const expected = ['contained_transform', 'evidence_verify', 'file_read', 'host_exec', 'host_file_read', 'host_file_write', 'proof_execute', 'skill_catalog', 'workspace_open'];
|
|
88
88
|
if (JSON.stringify(names) !== JSON.stringify(expected)) throw new Error(`privileged catalog mismatch: ${JSON.stringify(names)}`);
|
|
89
89
|
|
|
90
90
|
const writeText = structured(await client.callTool({
|
package/src/mcp-server.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { access, readFile } from 'node:fs/promises';
|
|
2
|
+
import { access, mkdir, readFile } from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
@@ -43,6 +43,7 @@ import { registerDesktopTools } from './desktop-mcp-tools-r40.mjs';
|
|
|
43
43
|
import { loadAutonomousPolicy } from './autonomous-policy.mjs';
|
|
44
44
|
import { createNativeDiffRuntime } from './native-diff.mjs';
|
|
45
45
|
import { createAutonomousRuntime } from './autonomous-runtime.mjs';
|
|
46
|
+
import { createCodexAppServerR44 } from './codex-app-server-r44.mjs';
|
|
46
47
|
import { startAutonomousSupervisor } from './autonomous-supervisor.mjs';
|
|
47
48
|
import { registerAutonomousTools } from './autonomous-mcp-tools.mjs';
|
|
48
49
|
import { createToolAudit, installToolAudit } from './tool-audit.mjs';
|
|
@@ -50,6 +51,8 @@ import { createAuditContextProvider } from './audit-context.mjs';
|
|
|
50
51
|
import { registerOperatorSurface } from './operator-surface.mjs';
|
|
51
52
|
import { capabilityProfileFromEnv, COMPACT_SURFACE_TOOL_LIMIT, R36_CAPABILITY_LEDGER } from './capability-ledger.mjs';
|
|
52
53
|
import { registerCompactGatewayTools } from './compact-gateway-mcp-tools.mjs';
|
|
54
|
+
import { createSkillPlane } from './skill-plane-r44.mjs';
|
|
55
|
+
import { registerSkillToolsR44 } from './skill-mcp-tools-r44.mjs';
|
|
53
56
|
import {
|
|
54
57
|
containedTransformOutputSchema,
|
|
55
58
|
evidenceVerifyOutputSchema,
|
|
@@ -200,9 +203,18 @@ const memoryProvider=autonomousPolicy?createR41MemoryProvider({
|
|
|
200
203
|
publicKey:autonomousMemoryVerifyKey.publicKey,keyId:autonomousMemorySigningKey.keyId,
|
|
201
204
|
byteBudget:Math.min(262144,Math.max(65536,Math.floor(autonomousPolicy.limits.max_context_bytes/2))),semantic:{enabled:false}
|
|
202
205
|
}):null;
|
|
206
|
+
const plannerProviderRuntime=autonomousPolicy?.planner.backend==='codex_app_server'
|
|
207
|
+
? await createCodexAppServerR44({
|
|
208
|
+
stateDir:path.join(autonomousPolicy.evidence_dir,'provider-r44','codex-app-server'),
|
|
209
|
+
model:autonomousPolicy.planner.model,cwd:codingPolicy.roots[autonomousPolicy.planner.root_id].real_path,
|
|
210
|
+
clientVersion:DEADBYTE_VERSION,executable:autonomousPolicy.planner.executable,
|
|
211
|
+
executableSha256:autonomousPolicy.planner.executable_sha256,args:autonomousPolicy.planner.args
|
|
212
|
+
})
|
|
213
|
+
: null;
|
|
203
214
|
const autonomousRuntime = autonomousPolicy ? createAutonomousRuntime({ policy:autonomousPolicy,codingRuntime,nativeDiffRuntime,
|
|
204
|
-
machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime,desktopMemoryProvider,memoryProvider,
|
|
205
|
-
|
|
215
|
+
machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime,desktopMemoryProvider,memoryProvider,
|
|
216
|
+
plannerProviderRuntime,signingKeyPath,verifyKeyPath }) : null;
|
|
217
|
+
const autonomousSupervisor = autonomousRuntime && ['openai_compatible','openai_responses','codex_app_server'].includes(autonomousPolicy.planner.backend) &&
|
|
206
218
|
autonomousSupervisorMode === 'in_process'
|
|
207
219
|
? startAutonomousSupervisor({ runtime: autonomousRuntime, policy: autonomousPolicy })
|
|
208
220
|
: null;
|
|
@@ -214,6 +226,9 @@ const auditContextProvider = auditRuntimeMode === '1' ? createAuditContextProvid
|
|
|
214
226
|
const toolAudit = auditRuntimeMode === '1' ? await createToolAudit({
|
|
215
227
|
stateRoot:runtimeStateRoot,signingKeyPath,contextProvider:auditContextProvider
|
|
216
228
|
}) : null;
|
|
229
|
+
const skillRoot=path.join(runtimeStateRoot,'skills');
|
|
230
|
+
await mkdir(skillRoot,{recursive:true});
|
|
231
|
+
const skillRuntime=await createSkillPlane({root:skillRoot,verifyKeyPath});
|
|
217
232
|
|
|
218
233
|
const ingressAdapterMode = currentIngressAdapterMode();
|
|
219
234
|
installTunnelStdioProbeCompat({
|
|
@@ -429,6 +444,8 @@ serveStdio(() => {
|
|
|
429
444
|
registerAutonomousTools(toolServer, { autonomousRuntime, nativeDiffRuntime });
|
|
430
445
|
}
|
|
431
446
|
|
|
447
|
+
registerSkillToolsR44(toolServer,{runtime:skillRuntime});
|
|
448
|
+
|
|
432
449
|
registerCompactGatewayTools(toolServer,{
|
|
433
450
|
profile:surfaceProfile,ledger:R36_CAPABILITY_LEDGER,codingRuntime,semanticRuntime,machineFsRuntime,
|
|
434
451
|
processRuntime,observationRuntime,machineRuntime,desktopRuntime,catalogProvider:()=>[...registeredToolNames]
|
package/src/mcp-smoke-client.mjs
CHANGED
|
@@ -64,7 +64,7 @@ try {
|
|
|
64
64
|
|
|
65
65
|
const listed = await client.listTools();
|
|
66
66
|
const names = listed.tools.map(tool => tool.name).sort();
|
|
67
|
-
const baseTools = ['contained_transform', 'evidence_verify', 'file_read', 'proof_execute', 'workspace_open'];
|
|
67
|
+
const baseTools = ['contained_transform', 'evidence_verify', 'file_read', 'proof_execute', 'skill_catalog', 'workspace_open'];
|
|
68
68
|
const privilegedTools = ['host_exec', 'host_file_read', 'host_file_write'];
|
|
69
69
|
const allowedCatalogs = [
|
|
70
70
|
baseTools,
|
package/src/native-diff-v2.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { canonicalJson } from './canonical-json.mjs';
|
|
5
5
|
import { validatePortableRelativePath } from './path-policy.mjs';
|
|
6
6
|
import { loadPrivateSigningKey, loadPublicVerifyKey } from './trust.mjs';
|
|
7
|
-
import { assertAutonomousArmed } from './autonomous-policy.mjs';
|
|
7
|
+
import { assertAutonomousArmed, recheckAutonomousAuthority } from './autonomous-policy.mjs';
|
|
8
8
|
|
|
9
9
|
export const NATIVE_DIFF_V2_SCHEMA='deadbyte.native-diff.v2';
|
|
10
10
|
export const NATIVE_DIFF_V2_PREIMAGE_SCHEMA='deadbyte.native-diff-preimage.v2';
|
|
@@ -312,11 +312,11 @@ export function createNativeDiffV2Runtime({policy,codingRuntime,machineFsRuntime
|
|
|
312
312
|
async function apply({diffId,expectedOperationCount=null}){
|
|
313
313
|
const {ctx,identity}=await loadDiff(diffId);
|
|
314
314
|
if(expectedOperationCount!==null&&(!Number.isInteger(expectedOperationCount)||expectedOperationCount<1||expectedOperationCount>policy.limits.max_files_per_diff||identity.operations.length!==expectedOperationCount))throw new Error(`native diff V2 operation count mismatch: expected ${String(expectedOperationCount)}, signed ${identity.operations.length}`);
|
|
315
|
-
await assertAutonomousArmed(policy,identity.root_id); const codingStatus=await codingRuntime.sessionStatus(); if(!codingStatus.armed)throw new Error(`coding plane is disarmed: ${codingStatus.reason}`);
|
|
315
|
+
const autonomousAuthority=await assertAutonomousArmed(policy,identity.root_id); const codingStatus=await codingRuntime.sessionStatus(); if(!codingStatus.armed)throw new Error(`coding plane is disarmed: ${codingStatus.reason}`);
|
|
316
316
|
if(await exists(ctx.receiptPath)){const checked=await verify({diffId});if(!checked.ok)throw new Error(`existing native diff V2 receipt invalid: ${checked.reason}`);return {status:'existing',diff_id:diffId,receipt_sha256:checked.receipt_sha256,operation_count:identity.operations.length,current_state_matches:checked.current_state_matches};}
|
|
317
317
|
const preimage=await ensurePreimage(ctx,diffId,identity); const events=await readEvents(ctx); if(!events.some(e=>e.type==='transaction_started'))await appendEvent(ctx,events,diffId,'transaction_started',{operation_count:identity.operations.length,preimage_sha256:preimage.preimage_sha256});
|
|
318
318
|
try{
|
|
319
|
-
for(let index=0;index<identity.operations.length;index+=1){const op=identity.operations[index];const prior=events.find(e=>e.type==='operation_committed'&&e.payload?.index===index);if(prior){if(!(await stateMatches(identity.root_id,op,'after')))throw new Error(`native diff V2 committed state changed index ${index}`);continue;}let executed;if(await stateMatches(identity.root_id,op,'after')){const receiptId=receiptIdFor(diffId,index,'apply');const checked=await verifyOperationReceipt(op,receiptId);if(!checked.ok)throw new Error(`native diff V2 unreceipted mutation index ${index}`);executed={recovered:true,receipt_id:receiptId,receipt_sha256:checked.receipt_sha256,request_sha256:checked.request_sha256,operation:checked.operation,receipt_class:receiptClass(op)};}else if(await stateMatches(identity.root_id,op,'before')) executed=await executeOperation(diffId,identity.root_id,op,index);else throw new Error(`native diff V2 concurrent state conflict index ${index}`);if(!(await stateMatches(identity.root_id,op,'after')))throw new Error(`native diff V2 post-operation mismatch index ${index}`);await appendEvent(ctx,events,diffId,'operation_committed',{index,kind:op.kind,path:op.path??null,source_path:op.source_path??null,destination_path:op.destination_path??null,receipt_class:executed.receipt_class,receipt_id:executed.receipt_id,receipt_sha256:executed.receipt_sha256,request_sha256:executed.request_sha256,recovered:executed.recovered});}
|
|
319
|
+
for(let index=0;index<identity.operations.length;index+=1){const op=identity.operations[index];const prior=events.find(e=>e.type==='operation_committed'&&e.payload?.index===index);if(prior){if(!(await stateMatches(identity.root_id,op,'after')))throw new Error(`native diff V2 committed state changed index ${index}`);continue;}let executed;if(await stateMatches(identity.root_id,op,'after')){const receiptId=receiptIdFor(diffId,index,'apply');const checked=await verifyOperationReceipt(op,receiptId);if(!checked.ok)throw new Error(`native diff V2 unreceipted mutation index ${index}`);executed={recovered:true,receipt_id:receiptId,receipt_sha256:checked.receipt_sha256,request_sha256:checked.request_sha256,operation:checked.operation,receipt_class:receiptClass(op)};}else if(await stateMatches(identity.root_id,op,'before')){await recheckAutonomousAuthority(policy,autonomousAuthority,identity.root_id);executed=await executeOperation(diffId,identity.root_id,op,index);}else throw new Error(`native diff V2 concurrent state conflict index ${index}`);if(!(await stateMatches(identity.root_id,op,'after')))throw new Error(`native diff V2 post-operation mismatch index ${index}`);await appendEvent(ctx,events,diffId,'operation_committed',{index,kind:op.kind,path:op.path??null,source_path:op.source_path??null,destination_path:op.destination_path??null,receipt_class:executed.receipt_class,receipt_id:executed.receipt_id,receipt_sha256:executed.receipt_sha256,request_sha256:executed.request_sha256,recovered:executed.recovered});}
|
|
320
320
|
}catch(error){try{const rollbackReceipts=await rollbackToPreimage(diffId,identity,preimage);await appendEvent(ctx,events,diffId,'transaction_rolled_back',{reason:String(error).slice(0,2048),rollback_receipts:rollbackReceipts});}catch(rollbackError){await appendEvent(ctx,events,diffId,'transaction_paused',{reason:String(error).slice(0,1024),rollback_error:String(rollbackError).slice(0,1024)}).catch(()=>{});throw new Error(`native diff V2 apply failed and rollback incomplete: ${String(error)}; ${String(rollbackError)}`);}throw new Error(`native diff V2 apply rolled back: ${String(error)}`);}
|
|
321
321
|
const receipt=await createApplyReceipt(ctx,diffId,identity,preimage,events);return {status:'committed',diff_id:diffId,receipt_sha256:receipt.receipt_sha256,operation_count:identity.operations.length,operation_receipts:receipt.operation_receipts.map(x=>({index:x.index,kind:x.kind,receipt_class:x.receipt_class,receipt_id:x.receipt_id,receipt_sha256:x.receipt_sha256}))};
|
|
322
322
|
}
|
package/src/native-diff.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { canonicalJson } from './canonical-json.mjs';
|
|
5
5
|
import { validatePortableRelativePath } from './path-policy.mjs';
|
|
6
6
|
import { loadPrivateSigningKey, loadPublicVerifyKey } from './trust.mjs';
|
|
7
|
-
import { assertAutonomousArmed } from './autonomous-policy.mjs';
|
|
7
|
+
import { assertAutonomousArmed, recheckAutonomousAuthority } from './autonomous-policy.mjs';
|
|
8
8
|
import { createNativeDiffV2Runtime, NATIVE_DIFF_V2_SCHEMA } from './native-diff-v2.mjs';
|
|
9
9
|
|
|
10
10
|
export const NATIVE_DIFF_SCHEMA = 'deadbyte.native-diff.v1';
|
|
@@ -294,7 +294,7 @@ function createNativeDiffRuntimeV1({ policy, codingRuntime, signingKeyPath, veri
|
|
|
294
294
|
if (expectedOperationCount !== null && (!Number.isInteger(expectedOperationCount) || expectedOperationCount < 1 || expectedOperationCount > policy.limits.max_files_per_diff || identity.operations.length !== expectedOperationCount)) {
|
|
295
295
|
throw new Error(`native diff operation count mismatch: expected ${String(expectedOperationCount)}, signed ${identity.operations.length}`);
|
|
296
296
|
}
|
|
297
|
-
await assertAutonomousArmed(policy, identity.root_id);
|
|
297
|
+
const autonomousAuthority = await assertAutonomousArmed(policy, identity.root_id);
|
|
298
298
|
const codingStatus = await codingRuntime.sessionStatus();
|
|
299
299
|
if (!codingStatus.armed) throw new Error(`coding plane is disarmed: ${codingStatus.reason}`);
|
|
300
300
|
if (await exists(ctx.receiptPath)) {
|
|
@@ -327,6 +327,7 @@ function createNativeDiffRuntimeV1({ policy, codingRuntime, signingKeyPath, veri
|
|
|
327
327
|
executed = { recovered:true, receipt_id:receiptId, receipt_sha256:checked.receipt_sha256,
|
|
328
328
|
request_sha256:checked.request_sha256, operation:checked.operation };
|
|
329
329
|
} else if (stateMatches(current, op.before_sha256)) {
|
|
330
|
+
await recheckAutonomousAuthority(policy, autonomousAuthority, identity.root_id);
|
|
330
331
|
executed = await executeOperation(diffId, identity.root_id, op, index);
|
|
331
332
|
} else {
|
|
332
333
|
throw new Error(`native diff concurrent state conflict '${op.path}'`);
|
|
@@ -51,7 +51,7 @@ const transport=new StdioClientTransport({command:process.execPath,args:[serverP
|
|
|
51
51
|
env:{...getDefaultEnvironment(),DEADBYTE_MACHINE_RUNTIME:'1',DEADBYTE_CODING_RUNTIME:'1',DEADBYTE_OBSERVATION_RUNTIME:'1',
|
|
52
52
|
DEADBYTE_CODING_POLICY:policyPath,DEADBYTE_STATE_ROOT:state,DEADBYTE_SIGNING_KEY:keys.privateKeyPath,DEADBYTE_VERIFY_KEY:keys.publicKeyPath},stderr:'pipe'});
|
|
53
53
|
const stderr=[]; transport.stderr?.on('data',chunk=>stderr.push(Buffer.from(chunk)));
|
|
54
|
-
const base=['contained_transform','evidence_verify','file_read','proof_execute','workspace_open'];
|
|
54
|
+
const base=['contained_transform','evidence_verify','file_read','proof_execute','skill_catalog','workspace_open'];
|
|
55
55
|
const machine=['machine_list','machine_ping','machine_revoke','machine_status'];
|
|
56
56
|
const coding=['coding_benchmark_run','coding_command_run','coding_file_read','coding_file_write','coding_files_read','coding_receipt_verify','coding_search',
|
|
57
57
|
'coding_session_status','coding_stat','coding_symbol_definition','coding_symbol_diagnostics','coding_symbol_implementations','coding_symbol_outline','coding_symbol_references','coding_symbol_rename_preview','coding_symbol_replace',
|
package/src/operator-surface.mjs
CHANGED
|
@@ -14,7 +14,7 @@ function workflow(goal,verb){
|
|
|
14
14
|
].join('\n');
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function registerOperatorSurface(server,{packageRoot,surfaceProfile='full',compactToolLimit=
|
|
17
|
+
export function registerOperatorSurface(server,{packageRoot,surfaceProfile='full',compactToolLimit=43}){
|
|
18
18
|
if(!server||!packageRoot) throw new Error('operator surface requires server and packageRoot');
|
|
19
19
|
if(!['full','compact'].includes(surfaceProfile)) throw new Error('operator surface profile must be full or compact');
|
|
20
20
|
const contextPath=path.join(packageRoot,'CONTEXT.md');
|
|
@@ -36,7 +36,7 @@ export function registerOperatorSurface(server,{packageRoot,surfaceProfile='full
|
|
|
36
36
|
title:'DEADBYTE MCP Architecture',description:'Production module seams, tracer bullet, policy, telemetry, semantic and remote decisions.',mimeType:'text/markdown'
|
|
37
37
|
},async uri=>({contents:[{uri:uri.href,mimeType:'text/markdown',text:await readFile(architecturePath,'utf8')}]}));
|
|
38
38
|
server.registerResource('deadbyte-surface-profile','deadbyte://surface-profile',{
|
|
39
|
-
title:'DEADBYTE MCP Surface Profile',description:'Active
|
|
39
|
+
title:'DEADBYTE MCP Surface Profile',description:'Active DEADBYTE MCP surface profile and compact tool ceiling.',mimeType:'application/json'
|
|
40
40
|
},async uri=>({contents:[{uri:uri.href,mimeType:'application/json',text:JSON.stringify({
|
|
41
41
|
schema:'deadbyte.surface-profile.v1',profile:surfaceProfile,compact_tool_limit:compactToolLimit
|
|
42
42
|
})}]}));
|
package/src/process-policy.mjs
CHANGED
|
@@ -147,4 +147,23 @@ export async function assertProcessArmed(policy, profileId = null, rootId = null
|
|
|
147
147
|
if (profileId !== null && !status.profile_ids.includes(profileId)) throw new Error(`process profile '${profileId}' is not included in the active grant`);
|
|
148
148
|
if (rootId !== null && !status.root_ids.includes(rootId)) throw new Error(`process root '${rootId}' is not included in the active grant`);
|
|
149
149
|
return status;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export async function recheckProcessAuthority(policy, initial, profileId = null, rootId = null) {
|
|
153
|
+
if (!initial?.armed || typeof initial.instance_nonce !== 'string' ||
|
|
154
|
+
!Array.isArray(initial.profile_ids) || !Array.isArray(initial.root_ids)) {
|
|
155
|
+
throw new Error('process authority recheck requires an armed initial snapshot');
|
|
156
|
+
}
|
|
157
|
+
let current;
|
|
158
|
+
try { current = await assertProcessArmed(policy, profileId, rootId); }
|
|
159
|
+
catch (error) { throw new Error(`process authority changed during operation: ${String(error?.message ?? error)}`); }
|
|
160
|
+
const changed =
|
|
161
|
+
current.instance_nonce !== initial.instance_nonce ||
|
|
162
|
+
current.standing !== initial.standing ||
|
|
163
|
+
current.profile_ids.length !== initial.profile_ids.length ||
|
|
164
|
+
current.root_ids.length !== initial.root_ids.length ||
|
|
165
|
+
current.profile_ids.some((id, index) => id !== initial.profile_ids[index]) ||
|
|
166
|
+
current.root_ids.some((id, index) => id !== initial.root_ids[index]);
|
|
167
|
+
if (changed) throw new Error('process authority changed during operation');
|
|
168
|
+
return current;
|
|
150
169
|
}
|
package/src/process-runtime.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { spawn } from 'node:child_process';
|
|
|
5
5
|
import { canonicalJson } from './canonical-json.mjs';
|
|
6
6
|
import { isInside, validatePortableRelativePath } from './path-policy.mjs';
|
|
7
7
|
import { loadPrivateSigningKey, loadPublicVerifyKey } from './trust.mjs';
|
|
8
|
-
import { assertProcessArmed } from './process-policy.mjs';
|
|
8
|
+
import { assertProcessArmed, recheckProcessAuthority } from './process-policy.mjs';
|
|
9
9
|
|
|
10
10
|
const EVENT_SCHEMA = 'deadbyte.process-event.v1';
|
|
11
11
|
const EVENT_DOMAIN = Buffer.from('DEADBYTE-PROCESS-EVENT-V1\0', 'utf8');
|
|
@@ -256,6 +256,7 @@ export function createProcessRuntime({ policy, signingKeyPath, verifyKeyPath })
|
|
|
256
256
|
if (sha256Bytes(await readFile(policy.native_host.path)) !== policy.native_host.sha256) throw new Error('process native_host executable hash changed');
|
|
257
257
|
if (sha256Bytes(await readFile(profile.executable)) !== profile.executable_sha256) throw new Error(`process profile '${profileId}' executable hash changed`);
|
|
258
258
|
const cwdInfo = await resolveCwd(policy, grant, profile, rootId, cwd);
|
|
259
|
+
await recheckProcessAuthority(policy, grant, profileId, rootId);
|
|
259
260
|
await mkdir(policy.state_dir, { recursive: true });
|
|
260
261
|
const sessionId = randomBytes(32).toString('hex');
|
|
261
262
|
const session = {
|
|
@@ -267,6 +268,7 @@ export function createProcessRuntime({ policy, signingKeyPath, verifyKeyPath })
|
|
|
267
268
|
transport: profile.transport,
|
|
268
269
|
win32_input_mode: false
|
|
269
270
|
};
|
|
271
|
+
await recheckProcessAuthority(policy, grant, profileId, rootId);
|
|
270
272
|
const host = spawn(policy.native_host.path,
|
|
271
273
|
[profile.executable, cwdInfo.resolved, session.job_id, `--deadbyte-transport=${profile.transport}`, ...profile.prefix_args, ...args],
|
|
272
274
|
{ cwd: cwdInfo.resolved, env: processEnvironment(profile), windowsHide: true, shell: false, stdio: ['pipe','pipe','pipe'] });
|
|
@@ -327,7 +329,7 @@ export function createProcessRuntime({ policy, signingKeyPath, verifyKeyPath })
|
|
|
327
329
|
async function action({ sessionId, actionId }) {
|
|
328
330
|
const session = await sessionFor(sessionId);
|
|
329
331
|
if (session.state !== 'running') throw new Error('process action requires a running session');
|
|
330
|
-
await assertProcessArmed(policy, session.profile_id, session.root_id);
|
|
332
|
+
const grant = await assertProcessArmed(policy, session.profile_id, session.root_id);
|
|
331
333
|
const profile = policy.profiles[session.profile_id];
|
|
332
334
|
if (!profile || !Object.prototype.hasOwnProperty.call(profile.input_actions, actionId)) {
|
|
333
335
|
throw new Error(`process action '${actionId}' is not configured`);
|
|
@@ -340,6 +342,7 @@ export function createProcessRuntime({ policy, signingKeyPath, verifyKeyPath })
|
|
|
340
342
|
if (!session.host?.stdin || session.host.stdin.destroyed || !session.host.stdin.writable) {
|
|
341
343
|
throw new Error('process session input is not writable');
|
|
342
344
|
}
|
|
345
|
+
await recheckProcessAuthority(policy, grant, session.profile_id, session.root_id);
|
|
343
346
|
await new Promise((resolve, reject) => session.host.stdin.write(logicalBytes, error => error ? reject(error) : resolve()));
|
|
344
347
|
const logicalSha256 = sha256Bytes(logicalBytes);
|
|
345
348
|
await appendEvent(runtime, session, 'action', { action_id: actionId, logical_bytes: logicalBytes.length, logical_sha256: logicalSha256 });
|
|
@@ -55,7 +55,7 @@ try {
|
|
|
55
55
|
await client.connect(transport);
|
|
56
56
|
const listed = await client.listTools();
|
|
57
57
|
const names = listed.tools.map(tool => tool.name).sort();
|
|
58
|
-
const expected = ['contained_transform','evidence_verify','file_read','proof_execute','workspace_open',
|
|
58
|
+
const expected = ['contained_transform','evidence_verify','file_read','proof_execute','skill_catalog','workspace_open',
|
|
59
59
|
'process_session_action','process_session_list','process_session_read','process_session_start','process_session_status','process_session_terminate'].sort();
|
|
60
60
|
if (JSON.stringify(names) !== JSON.stringify(expected)) throw new Error(`process smoke catalog mismatch: ${JSON.stringify(names)}`);
|
|
61
61
|
const started = structured(await client.callTool({ name: 'process_session_start', arguments: {
|
|
@@ -114,7 +114,7 @@ try {
|
|
|
114
114
|
}
|
|
115
115
|
if (sequence < 3 || previous !== finalHead) throw new Error('process journal head mismatch');
|
|
116
116
|
if ((await readFile(journalPath, 'utf8')).includes('DEADBYTE_ACTION_PING')) throw new Error('process journal leaked action payload');
|
|
117
|
-
console.log(JSON.stringify({ status: 'passed', protocol_revision: '2026-07-28', tool_count:
|
|
117
|
+
console.log(JSON.stringify({ status: 'passed', protocol_revision: '2026-07-28', tool_count: 12,
|
|
118
118
|
profile_id: 'ping_hold', session_id: sessionId, observed_output_bytes: read.stream_head,
|
|
119
119
|
action_id: action.action_id, logical_bytes: action.logical_bytes, logical_sha256: action.logical_sha256,
|
|
120
120
|
final_state: terminated.session.state, event_count: sequence, journal_head_sha256: previous,
|
package/src/provider-circuit.mjs
CHANGED
|
@@ -48,10 +48,16 @@ function openAt(state,ms,policy){
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export function providerCircuitId(planner){
|
|
51
|
-
if(!
|
|
51
|
+
if(!planner||typeof planner.model!=='string') throw new Error('provider circuit planner identity invalid');
|
|
52
|
+
let identity;
|
|
53
|
+
if(['openai_compatible','openai_responses'].includes(planner.backend)&&typeof planner.endpoint==='string') {
|
|
54
|
+
identity=`${planner.backend}\0${planner.endpoint}\0${planner.model}`;
|
|
55
|
+
} else if(planner.backend==='codex_app_server'&&HEX64.test(planner.executable_sha256??'')&&typeof planner.root_id==='string') {
|
|
56
|
+
identity=`${planner.backend}\0${planner.executable_sha256}\0${planner.model}\0${planner.root_id}`;
|
|
57
|
+
} else {
|
|
52
58
|
throw new Error('provider circuit planner identity invalid');
|
|
53
59
|
}
|
|
54
|
-
const digest=createHash('sha256').update(Buffer.from(
|
|
60
|
+
const digest=createHash('sha256').update(Buffer.from(identity,'utf8')).digest('hex');
|
|
55
61
|
return `${planner.backend}:${digest}`;
|
|
56
62
|
}
|
|
57
63
|
|