deadbyte-mcp 0.13.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.
Files changed (153) hide show
  1. package/.gitattributes +7 -0
  2. package/AGENTS.md +18 -0
  3. package/CONTEXT.md +3 -3
  4. package/MANIFEST.SHA256 +152 -75
  5. package/R42-PARENT.json +19 -0
  6. package/R43-PARENT.json +20 -0
  7. package/R44-DONORS.json +99 -0
  8. package/R44-PARENT.json +18 -0
  9. package/README.txt +7 -2
  10. package/bin/appcontainer-stage.obj +0 -0
  11. package/bin/bootstrap-advapi32.obj +0 -0
  12. package/bin/bootstrap-exitcode.obj +0 -0
  13. package/bin/bootstrap-kernel32.obj +0 -0
  14. package/bin/child-control-probe.obj +0 -0
  15. package/bin/child-control-stage.obj +0 -0
  16. package/bin/contained-reverse-worker.obj +0 -0
  17. package/bin/contained-transform-worker.obj +0 -0
  18. package/bin/containment-probe.obj +0 -0
  19. package/bin/deadbyte-contain.obj +0 -0
  20. package/bin/deadbyte-exec.obj +0 -0
  21. package/bin/deadbyte-process-host.obj +0 -0
  22. package/bin/deadbyte-tunnel-host.obj +0 -0
  23. package/controller/README.TXT +4 -5
  24. package/controller/deadbyte-controller.ps1 +1 -1
  25. package/controller/deadbyte-desktop-policy.json +5 -5
  26. package/controller/deadbyte-process-policy.json +4 -4
  27. package/docs/ARCHITECTURE.md +8 -1
  28. package/docs/R44-ARCHITECTURE.md +37 -0
  29. package/harness/r42-agent-evals.json +48 -0
  30. package/harness/task-contract.schema.json +33 -0
  31. package/package.json +15 -8
  32. package/proof/CONTAINMENT-BUILD.txt +6 -6
  33. package/scripts/agent-eval-r42.mjs +46 -0
  34. package/scripts/deferred-slot-operation-r42.mjs +126 -0
  35. package/scripts/deferred-slot-operation-r43.mjs +126 -0
  36. package/scripts/deferred-slot-operation-r44.mjs +126 -0
  37. package/scripts/final-closure-r42.mjs +116 -0
  38. package/scripts/final-closure-r43.mjs +115 -0
  39. package/scripts/final-closure-r44.mjs +121 -0
  40. package/scripts/final-closure-verify-r42.mjs +223 -0
  41. package/scripts/final-closure-verify-r43.mjs +213 -0
  42. package/scripts/final-closure-verify-r44.mjs +220 -0
  43. package/scripts/finalize-r43.mjs +217 -0
  44. package/scripts/finalize-r44.mjs +217 -0
  45. package/scripts/full-regression-r43.mjs +20 -0
  46. package/scripts/full-regression-r44.mjs +20 -0
  47. package/scripts/gate-windows-r42.ps1 +54 -0
  48. package/scripts/gate-windows-r43.ps1 +36 -0
  49. package/scripts/gate-windows-r44.ps1 +36 -0
  50. package/scripts/release-parity-r42.mjs +235 -0
  51. package/scripts/release-parity-r43.mjs +218 -0
  52. package/scripts/release-parity-r44.mjs +242 -0
  53. package/scripts/release-parity-tests-r42.ps1 +6 -0
  54. package/scripts/release-parity-tests-r43.ps1 +8 -0
  55. package/scripts/release-parity-tests-r44.ps1 +8 -0
  56. package/scripts/verify-r42-parent.mjs +101 -0
  57. package/scripts/verify-r43-parent.mjs +126 -0
  58. package/scripts/verify-r44-parent.mjs +142 -0
  59. package/scripts/windows-gate-evidence-r42.mjs +64 -0
  60. package/scripts/windows-gate-evidence-r43.mjs +55 -0
  61. package/scripts/windows-gate-evidence-r44.mjs +55 -0
  62. package/src/agent-backend-r44.mjs +254 -0
  63. package/src/agent-eval-r42.mjs +73 -0
  64. package/src/agent-eval-runner-r42.mjs +138 -0
  65. package/src/agent-governance-r42.mjs +142 -0
  66. package/src/autonomous-context-engine.mjs +2 -0
  67. package/src/autonomous-loop-state.mjs +10 -0
  68. package/src/autonomous-mcp-tools.mjs +21 -6
  69. package/src/autonomous-output-contracts.mjs +18 -2
  70. package/src/autonomous-planner-contracts.mjs +3 -0
  71. package/src/autonomous-planner.mjs +46 -6
  72. package/src/autonomous-policy.mjs +50 -5
  73. package/src/autonomous-runtime.mjs +138 -25
  74. package/src/autonomous-supervisor-host.mjs +12 -2
  75. package/src/autonomous-supervisor.mjs +1 -1
  76. package/src/capability-ledger.mjs +2 -1
  77. package/src/codex-app-server-r44.mjs +154 -0
  78. package/src/coding-plane.mjs +66 -19
  79. package/src/durable-job-r44.mjs +145 -0
  80. package/src/evidence-publisher-r42.mjs +76 -0
  81. package/src/external-job-provider-r44.mjs +126 -0
  82. package/src/host-plane.mjs +27 -6
  83. package/src/job-supervisor-host-r44.mjs +112 -0
  84. package/src/machine-fs-smoke-client.mjs +1 -1
  85. package/src/machine-fs.mjs +21 -10
  86. package/src/mcp-host-smoke-client.mjs +1 -1
  87. package/src/mcp-server.mjs +20 -3
  88. package/src/mcp-smoke-client.mjs +1 -1
  89. package/src/native-diff-v2.mjs +3 -3
  90. package/src/native-diff.mjs +3 -2
  91. package/src/observation-smoke-client.mjs +1 -1
  92. package/src/operator-surface.mjs +2 -2
  93. package/src/process-policy.mjs +19 -0
  94. package/src/process-runtime.mjs +5 -2
  95. package/src/process-smoke-client.mjs +2 -2
  96. package/src/provider-circuit.mjs +10 -4
  97. package/src/r44-compact-disarmed-probe.mjs +74 -0
  98. package/src/r44-full-surface-probe.mjs +34 -0
  99. package/src/remote-device-r44.mjs +205 -0
  100. package/src/remote-mcp-autonomous-smoke-client.mjs +4 -4
  101. package/src/remote-mcp-r44-parity-client.mjs +203 -0
  102. package/src/remote-mcp-smoke-client.mjs +7 -4
  103. package/src/runtime-update.mjs +64 -31
  104. package/src/skill-mcp-tools-r44.mjs +37 -0
  105. package/src/skill-plane-r44.mjs +203 -0
  106. package/src/tunnel-stdio-compat-smoke.mjs +1 -1
  107. package/src/version.mjs +1 -1
  108. package/test/agent-backend-r44.test.mjs +115 -0
  109. package/test/agent-eval-r42.test.mjs +39 -0
  110. package/test/agent-eval-runner-r42.test.mjs +61 -0
  111. package/test/agent-governance-r42.test.mjs +77 -0
  112. package/test/agent-runtime.test.mjs +3 -3
  113. package/test/autonomous-loop.test.mjs +97 -11
  114. package/test/autonomous-r34-e2e.test.mjs +2 -1
  115. package/test/autonomous-runtime.test.mjs +2 -2
  116. package/test/capability-ledger-r36.test.mjs +3 -3
  117. package/test/cli-entrypoint.test.mjs +1 -1
  118. package/test/codex-app-server-r44.test.mjs +115 -0
  119. package/test/codex-autonomous-r44.test.mjs +87 -0
  120. package/test/coding-plane.test.mjs +2 -2
  121. package/test/coding-runtime.test.mjs +36 -1
  122. package/test/compact-catalog-r36.test.mjs +3 -3
  123. package/test/compact-max-catalog-r40.test.mjs +4 -2
  124. package/test/compact-max-catalog-r44.test.mjs +97 -0
  125. package/test/core.test.mjs +1 -1
  126. package/test/durable-job-r44.test.mjs +75 -0
  127. package/test/evidence-publisher-r42.test.mjs +53 -0
  128. package/test/external-job-provider-r44.test.mjs +46 -0
  129. package/test/full-max-catalog-r40.test.mjs +5 -3
  130. package/test/full-max-catalog-r44.test.mjs +91 -0
  131. package/test/host-plane.test.mjs +15 -1
  132. package/test/human-clarification-r42.test.mjs +53 -0
  133. package/test/native-diff.test.mjs +28 -0
  134. package/test/output-schema.test.mjs +2 -2
  135. package/test/process-policy.test.mjs +22 -1
  136. package/test/prompts-resources.test.mjs +1 -1
  137. package/test/provider-responses-r42.test.mjs +90 -0
  138. package/test/r33-closeout-regression.test.mjs +14 -16
  139. package/test/r33-finalization.test.mjs +7 -7
  140. package/test/r42-finalization.test.mjs +38 -0
  141. package/test/r43-finalization.test.mjs +31 -0
  142. package/test/r43-finalizer.test.mjs +34 -0
  143. package/test/r43-parent.test.mjs +29 -0
  144. package/test/r44-finalization.test.mjs +58 -0
  145. package/test/r44-finalizer.test.mjs +48 -0
  146. package/test/r44-parent.test.mjs +25 -0
  147. package/test/release-version.test.mjs +53 -19
  148. package/test/remote-device-r44.test.mjs +104 -0
  149. package/test/runtime-self-update.test.mjs +42 -0
  150. package/test/skill-mcp-r44.test.mjs +38 -0
  151. package/test/skill-plane-r44.test.mjs +93 -0
  152. package/test/standing-grants.test.mjs +5 -1
  153. package/test/task-contract-r42.test.mjs +41 -0
@@ -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 { lexical } = await resolveHostWritable(policy, rootId, relativePath);
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 grantBound = Number.isFinite(armed.remaining_ms) ? armed.remaining_ms : command.timeout_ms;
372
- const effectiveTimeout = Math.max(100, Math.min(requestedTimeout, command.timeout_ms, grantBound));
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',
@@ -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
- const { before, after } = await atomicWrite(target.lexical, bytes, expectedSha256);
326
- const signed = await signReceipt(policy, signingKeyPath, grant, 'machine_fs_write', request, {
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
- const { after } = await atomicWrite(found.lexical, nextBytes, expectedSha256);
362
- const signed = await signReceipt(policy, signingKeyPath, grant, 'machine_fs_edit_exact', request, {
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, grant, 'machine_fs_mkdir', request, {
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, grant, 'machine_fs_move', request, {
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, grant, 'machine_fs_delete', request, {
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, grant, 'machine_fs_rmdir_empty', request, {
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({
@@ -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,signingKeyPath,verifyKeyPath }) : null;
205
- const autonomousSupervisor = autonomousRuntime && autonomousPolicy.planner.backend === 'openai_compatible' &&
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]
@@ -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,
@@ -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
  }
@@ -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',
@@ -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=42}){
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 R36 MCP surface profile and compact tool ceiling.',mimeType:'application/json'
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
  })}]}));
@@ -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
  }
@@ -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: 11,
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,
@@ -48,11 +48,17 @@ function openAt(state,ms,policy){
48
48
  }
49
49
 
50
50
  export function providerCircuitId(planner){
51
- if(planner?.backend!=='openai_compatible'||typeof planner.endpoint!=='string'||typeof planner.model!=='string') {
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(`${planner.endpoint}\0${planner.model}`,'utf8')).digest('hex');
55
- return `openai_compatible:${digest}`;
60
+ const digest=createHash('sha256').update(Buffer.from(identity,'utf8')).digest('hex');
61
+ return `${planner.backend}:${digest}`;
56
62
  }
57
63
 
58
64
  export function classifyProviderFailure(error){
@@ -61,7 +67,7 @@ export function classifyProviderFailure(error){
61
67
  if(name==='aborterror'||/\b(?:timed?\s*out|timeout|aborted)\b/.test(text)) return 'timeout';
62
68
  if(/(?:http\s*429|rate[ _-]?limit|too many requests)/.test(text)) return 'rate_limit';
63
69
  if(/(?:http\s*(?:401|403)|unauthori[sz]ed|forbidden|api key|authentication)/.test(text)) return 'auth';
64
- if(/(?:not json|invalid json|missing choices|invalid response|response.*(?:schema|contract|content))/.test(text)) return 'invalid_response';
70
+ if(/(?:not json|invalid json|missing choices|missing output_text|refusal|not completed|incomplete response|invalid response|response.*(?:schema|contract|content))/.test(text)) return 'invalid_response';
65
71
  if(/(?:econnreset|econnrefused|enotfound|fetch failed|network|socket|transport|connection)/.test(text)) return 'transport';
66
72
  return 'other';
67
73
  }