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.
Files changed (105) hide show
  1. package/AGENTS.md +11 -18
  2. package/CONTEXT.md +3 -3
  3. package/MANIFEST.SHA256 +104 -53
  4. package/R43-PARENT.json +20 -0
  5. package/R44-DONORS.json +99 -0
  6. package/R44-PARENT.json +18 -0
  7. package/README.txt +7 -2
  8. package/controller/README.TXT +4 -5
  9. package/controller/deadbyte-controller.ps1 +1 -1
  10. package/docs/ARCHITECTURE.md +8 -1
  11. package/docs/R44-ARCHITECTURE.md +37 -0
  12. package/package.json +13 -8
  13. package/scripts/deferred-slot-operation-r43.mjs +126 -0
  14. package/scripts/deferred-slot-operation-r44.mjs +126 -0
  15. package/scripts/final-closure-r43.mjs +115 -0
  16. package/scripts/final-closure-r44.mjs +121 -0
  17. package/scripts/final-closure-verify-r43.mjs +213 -0
  18. package/scripts/final-closure-verify-r44.mjs +220 -0
  19. package/scripts/finalize-r43.mjs +217 -0
  20. package/scripts/finalize-r44.mjs +217 -0
  21. package/scripts/full-regression-r43.mjs +20 -0
  22. package/scripts/full-regression-r44.mjs +20 -0
  23. package/scripts/gate-windows-r43.ps1 +36 -0
  24. package/scripts/gate-windows-r44.ps1 +36 -0
  25. package/scripts/release-parity-r43.mjs +218 -0
  26. package/scripts/release-parity-r44.mjs +242 -0
  27. package/scripts/release-parity-tests-r43.ps1 +8 -0
  28. package/scripts/release-parity-tests-r44.ps1 +8 -0
  29. package/scripts/verify-r43-parent.mjs +126 -0
  30. package/scripts/verify-r44-parent.mjs +142 -0
  31. package/scripts/windows-gate-evidence-r43.mjs +55 -0
  32. package/scripts/windows-gate-evidence-r44.mjs +55 -0
  33. package/src/agent-backend-r44.mjs +254 -0
  34. package/src/autonomous-output-contracts.mjs +1 -1
  35. package/src/autonomous-policy.mjs +41 -4
  36. package/src/autonomous-runtime.mjs +25 -12
  37. package/src/autonomous-supervisor-host.mjs +12 -2
  38. package/src/capability-ledger.mjs +2 -1
  39. package/src/codex-app-server-r44.mjs +154 -0
  40. package/src/coding-plane.mjs +66 -19
  41. package/src/durable-job-r44.mjs +145 -0
  42. package/src/external-job-provider-r44.mjs +126 -0
  43. package/src/host-plane.mjs +27 -6
  44. package/src/job-supervisor-host-r44.mjs +112 -0
  45. package/src/machine-fs-smoke-client.mjs +1 -1
  46. package/src/machine-fs.mjs +21 -10
  47. package/src/mcp-host-smoke-client.mjs +1 -1
  48. package/src/mcp-server.mjs +20 -3
  49. package/src/mcp-smoke-client.mjs +1 -1
  50. package/src/native-diff-v2.mjs +3 -3
  51. package/src/native-diff.mjs +3 -2
  52. package/src/observation-smoke-client.mjs +1 -1
  53. package/src/operator-surface.mjs +2 -2
  54. package/src/process-policy.mjs +19 -0
  55. package/src/process-runtime.mjs +5 -2
  56. package/src/process-smoke-client.mjs +2 -2
  57. package/src/provider-circuit.mjs +8 -2
  58. package/src/r44-compact-disarmed-probe.mjs +74 -0
  59. package/src/r44-full-surface-probe.mjs +34 -0
  60. package/src/remote-device-r44.mjs +205 -0
  61. package/src/remote-mcp-autonomous-smoke-client.mjs +4 -4
  62. package/src/remote-mcp-r44-parity-client.mjs +203 -0
  63. package/src/remote-mcp-smoke-client.mjs +7 -4
  64. package/src/runtime-update.mjs +64 -31
  65. package/src/skill-mcp-tools-r44.mjs +37 -0
  66. package/src/skill-plane-r44.mjs +203 -0
  67. package/src/tunnel-stdio-compat-smoke.mjs +1 -1
  68. package/src/version.mjs +1 -1
  69. package/test/agent-backend-r44.test.mjs +115 -0
  70. package/test/agent-runtime.test.mjs +3 -3
  71. package/test/autonomous-loop.test.mjs +97 -11
  72. package/test/autonomous-runtime.test.mjs +2 -2
  73. package/test/capability-ledger-r36.test.mjs +3 -3
  74. package/test/cli-entrypoint.test.mjs +1 -1
  75. package/test/codex-app-server-r44.test.mjs +115 -0
  76. package/test/codex-autonomous-r44.test.mjs +87 -0
  77. package/test/coding-plane.test.mjs +2 -2
  78. package/test/coding-runtime.test.mjs +36 -1
  79. package/test/compact-catalog-r36.test.mjs +3 -3
  80. package/test/compact-max-catalog-r40.test.mjs +4 -2
  81. package/test/compact-max-catalog-r44.test.mjs +97 -0
  82. package/test/core.test.mjs +1 -1
  83. package/test/durable-job-r44.test.mjs +75 -0
  84. package/test/external-job-provider-r44.test.mjs +46 -0
  85. package/test/full-max-catalog-r40.test.mjs +5 -3
  86. package/test/full-max-catalog-r44.test.mjs +91 -0
  87. package/test/host-plane.test.mjs +15 -1
  88. package/test/native-diff.test.mjs +28 -0
  89. package/test/output-schema.test.mjs +2 -2
  90. package/test/process-policy.test.mjs +22 -1
  91. package/test/prompts-resources.test.mjs +1 -1
  92. package/test/r33-closeout-regression.test.mjs +13 -30
  93. package/test/r33-finalization.test.mjs +7 -7
  94. package/test/r43-finalization.test.mjs +31 -0
  95. package/test/r43-finalizer.test.mjs +34 -0
  96. package/test/r43-parent.test.mjs +29 -0
  97. package/test/r44-finalization.test.mjs +58 -0
  98. package/test/r44-finalizer.test.mjs +48 -0
  99. package/test/r44-parent.test.mjs +25 -0
  100. package/test/release-version.test.mjs +53 -19
  101. package/test/remote-device-r44.test.mjs +104 -0
  102. package/test/runtime-self-update.test.mjs +42 -0
  103. package/test/skill-mcp-r44.test.mjs +38 -0
  104. package/test/skill-plane-r44.test.mjs +93 -0
  105. package/test/standing-grants.test.mjs +5 -1
@@ -0,0 +1,55 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, open, readFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { verifyR44Parent } from './verify-r44-parent.mjs';
7
+
8
+ const here=path.dirname(fileURLToPath(import.meta.url));
9
+ const root=path.resolve(here,'..');
10
+ const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
11
+ const marker='DEADBYTE V0.15.0 / R44 REMOTE AGENT SKILL WINDOWS BATCH GATE: PASS';
12
+ function insist(ok,message){if(!ok)throw new Error(message);}
13
+
14
+ export async function writeWindowsGateEvidence({
15
+ candidateRoot=root,
16
+ stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp')),
17
+ clock=()=>new Date()
18
+ }={}){
19
+ const candidate=path.resolve(candidateRoot);
20
+ const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
21
+ const manifestSha256=sha256(manifestBytes);
22
+ const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
23
+ insist(pkg.version==='0.15.0',`Windows gate evidence requires V0.15.0, found ${pkg.version}`);
24
+ const parent=await verifyR44Parent(path.join(candidate,'R44-PARENT.json'));
25
+ const completed=clock();
26
+ insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'Windows gate evidence clock invalid');
27
+ const evidence={
28
+ schema:'deadbyte.r44-windows-gate.v1',status:'passed',exit_code:0,version:'0.15.0',
29
+ marker,completed_at_utc:completed.toISOString(),manifest_sha256:manifestSha256,
30
+ parent:{release_id:parent.release_id,manifest_sha256:parent.manifest_sha256,
31
+ generation_manifest_sha256:parent.generation_manifest_sha256,
32
+ final_closure_sha256:parent.closure_sha256,final_closure_file_sha256:parent.closure_file_sha256,
33
+ signer_key_id:parent.signer_key_id},
34
+ features:{skill_integrity:true,agent_backend_persistence:true,codex_protocol_contract:true,
35
+ remote_device_identity:true,durable_job_supervisor:true,real_external_supervisor_smoke:true,
36
+ full_regression_required:true,codex_live_binary_required:false}
37
+ };
38
+ const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');
39
+ const dir=path.join(path.resolve(stateRoot),'evidence','release');await mkdir(dir,{recursive:true});
40
+ const file=path.join(dir,`r44-windows-gate-${manifestSha256.slice(0,16)}.json`);
41
+ try{
42
+ const handle=await open(file,'wx',0o600);try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
43
+ return {status:'written',path:file,sha256:sha256(bytes),manifest_sha256:manifestSha256,parent:evidence.parent};
44
+ }catch(error){
45
+ if(error?.code!=='EEXIST')throw error;
46
+ const existingBytes=await readFile(file);
47
+ const existing=JSON.parse(existingBytes.toString('utf8').replace(/^\uFEFF/,''));
48
+ insist(existing.schema===evidence.schema&&existing.status==='passed'&&existing.exit_code===0,'existing R44 Windows gate evidence invalid');
49
+ insist(existing.version==='0.15.0'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing R44 Windows gate identity mismatch');
50
+ insist(existing.parent?.release_id===parent.release_id&&existing.parent?.final_closure_sha256===parent.closure_sha256,'existing R44 parent evidence mismatch');
51
+ return {status:'existing',path:file,sha256:sha256(existingBytes),manifest_sha256:manifestSha256,parent:existing.parent};
52
+ }
53
+ }
54
+ const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
55
+ if(invoked)writeWindowsGateEvidence().then(value=>console.log(JSON.stringify(value))).catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
@@ -0,0 +1,254 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { EventEmitter } from 'node:events';
3
+ import { lstat, mkdir, open, readFile, realpath, rename } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { spawn } from 'node:child_process';
6
+
7
+ const STATE_SCHEMA='deadbyte.agent-backend-state.v1';
8
+ const HEX64=/^[0-9a-f]{64}$/;
9
+ const ID=/^[A-Za-z0-9._-]{1,64}$/;
10
+ const METHOD=/^[A-Za-z0-9._\/-]{1,128}$/;
11
+ const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
12
+ const canonical=value=>Array.isArray(value)?value.map(canonical):value&&typeof value==='object'
13
+ ?Object.fromEntries(Object.keys(value).sort((a,b)=>a.localeCompare(b,'en')).map(key=>[key,canonical(value[key])])):value;
14
+ const canonicalJson=value=>JSON.stringify(canonical(value));
15
+ function insist(ok,message){if(!ok) throw new Error(message);}
16
+ function now(){return new Date().toISOString();}
17
+ function isJson(value){try{JSON.stringify(value);return true;}catch{return false;}}
18
+
19
+ async function atomicJson(file,value){
20
+ await mkdir(path.dirname(file),{recursive:true});
21
+ const bytes=Buffer.from(`${JSON.stringify(value,null,2)}\n`,'utf8');
22
+ const temp=`${file}.${process.pid}.${randomBytes(6).toString('hex')}.tmp`;
23
+ const handle=await open(temp,'wx');
24
+ try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
25
+ await rename(temp,file);
26
+ return sha(bytes);
27
+ }
28
+ function validateState(state,backendId){
29
+ insist(state&&typeof state==='object'&&!Array.isArray(state),'agent backend state must be object');
30
+ const allowed=['schema','backend_id','state_epoch','next_sequence','threads','requests','updated_at_utc'];
31
+ insist(Object.keys(state).every(k=>allowed.includes(k))&&allowed.every(k=>Object.hasOwn(state,k)),'agent backend state fields mismatch');
32
+ insist(state.schema===STATE_SCHEMA,'agent backend state schema mismatch');
33
+ insist(state.backend_id===backendId,'agent backend state backend mismatch');
34
+ insist(HEX64.test(state.state_epoch??''),'agent backend state epoch invalid');
35
+ insist(Number.isInteger(state.next_sequence)&&state.next_sequence>=0,'agent backend sequence invalid');
36
+ insist(state.threads&&typeof state.threads==='object'&&!Array.isArray(state.threads),'agent backend threads invalid');
37
+ for(const [logicalId,threadId] of Object.entries(state.threads)){
38
+ insist(ID.test(logicalId),'agent backend logical thread id invalid');
39
+ insist(typeof threadId==='string'&&threadId.length>=1&&threadId.length<=512,'agent backend thread id invalid');
40
+ }
41
+ insist(state.requests&&typeof state.requests==='object'&&!Array.isArray(state.requests),'agent backend requests invalid');
42
+ for(const [id,item] of Object.entries(state.requests)){
43
+ insist(HEX64.test(id),'agent backend request id invalid');
44
+ insist(item&&typeof item==='object'&&!Array.isArray(item),'agent backend request record invalid');
45
+ insist(['pending','completed','failed','interrupted'].includes(item.state),'agent backend request state invalid');
46
+ insist(typeof item.method==='string'&&METHOD.test(item.method),'agent backend request method invalid');
47
+ insist(HEX64.test(item.params_sha256??''),'agent backend params hash invalid');
48
+ insist(typeof item.created_at_utc==='string'&&typeof item.updated_at_utc==='string','agent backend request timestamp invalid');
49
+ }
50
+ insist(typeof state.updated_at_utc==='string','agent backend state timestamp invalid');
51
+ return state;
52
+ }
53
+ async function loadState(file,backendId){
54
+ try{
55
+ const st=await lstat(file);
56
+ insist(st.isFile()&&!st.isSymbolicLink(),'agent backend state must be real file');
57
+ let parsed;
58
+ try{parsed=JSON.parse(await readFile(file,'utf8'));}catch(error){throw new Error(`agent backend state JSON invalid: ${error.message}`);}
59
+ return validateState(parsed,backendId);
60
+ }catch(error){
61
+ if(error?.code!=='ENOENT') throw error;
62
+ return {schema:STATE_SCHEMA,backend_id:backendId,state_epoch:randomBytes(32).toString('hex'),next_sequence:0,threads:{},requests:{},updated_at_utc:now()};
63
+ }
64
+ }
65
+ async function fileSha256(file){
66
+ const st=await lstat(file);
67
+ insist(st.isFile()&&!st.isSymbolicLink(),'agent backend executable must be real file');
68
+ return sha(await readFile(file));
69
+ }
70
+
71
+ class JsonRpcLineTransport extends EventEmitter {
72
+ constructor({executable,args=[],cwd=null,env=null,maxLineBytes=1024*1024}){
73
+ super();this.executable=executable;this.args=args;this.cwd=cwd;this.env=env;this.maxLineBytes=maxLineBytes;this.child=null;this.buffer='';
74
+ }
75
+ async start(){
76
+ if(this.child) throw new Error('agent backend transport already started');
77
+ this.child=spawn(this.executable,this.args,{cwd:this.cwd??undefined,env:this.env??process.env,windowsHide:true,shell:false,stdio:['pipe','pipe','pipe']});
78
+ this.child.stdout.setEncoding('utf8');
79
+ this.child.stdout.on('data',chunk=>{
80
+ this.buffer+=chunk;
81
+ if(Buffer.byteLength(this.buffer,'utf8')>this.maxLineBytes*2){this.emit('protocol_error',new Error('agent backend JSON-RPC buffer limit exceeded'));return;}
82
+ for(;;){
83
+ const nl=this.buffer.indexOf('\n');if(nl<0)break;
84
+ const line=this.buffer.slice(0,nl).trim();this.buffer=this.buffer.slice(nl+1);
85
+ if(!line)continue;
86
+ if(Buffer.byteLength(line,'utf8')>this.maxLineBytes){this.emit('protocol_error',new Error('agent backend JSON-RPC line limit exceeded'));continue;}
87
+ try{this.emit('message',JSON.parse(line));}catch(error){this.emit('protocol_error',new Error(`agent backend JSON-RPC invalid: ${error.message}`));}
88
+ }
89
+ });
90
+ let stderr='';
91
+ this.child.stderr.setEncoding('utf8');
92
+ this.child.stderr.on('data',chunk=>{stderr=(stderr+chunk).slice(-16384);});
93
+ this.child.once('error',error=>this.emit('transport_error',error));
94
+ this.child.once('exit',(code,signal)=>{this.emit('exit',{code,signal,stderr});this.child=null;});
95
+ await new Promise((resolve,reject)=>{
96
+ if(this.child.pid){resolve();return;}
97
+ this.child.once('spawn',resolve);this.child.once('error',reject);
98
+ });
99
+ }
100
+ async send(value){
101
+ insist(this.child?.stdin?.writable,'agent backend transport is not writable');
102
+ const line=`${JSON.stringify(value)}\n`;
103
+ insist(Buffer.byteLength(line,'utf8')<=this.maxLineBytes,'agent backend outbound JSON-RPC line limit exceeded');
104
+ await new Promise((resolve,reject)=>this.child.stdin.write(line,error=>error?reject(error):resolve()));
105
+ }
106
+ async stop(){
107
+ if(!this.child)return;
108
+ const child=this.child;
109
+ const done=new Promise(resolve=>child.once('exit',resolve));
110
+ child.kill();
111
+ await Promise.race([done,new Promise(resolve=>setTimeout(resolve,3000))]);
112
+ }
113
+ }
114
+
115
+ export async function createAgentBackendR44({
116
+ backendId,stateDir,transportFactory=null,serverRequestHandler=null,notificationHandler=null,wireMode='jsonrpc2',
117
+ executable=null,executableSha256=null,args=[],cwd=null,env=null
118
+ }={}){
119
+ insist(typeof backendId==='string'&&ID.test(backendId),'agent backend id invalid');
120
+ insist(typeof stateDir==='string'&&path.isAbsolute(stateDir),'agent backend stateDir must be absolute');
121
+ await mkdir(stateDir,{recursive:true});
122
+ const root=await realpath(stateDir);
123
+ const stateFile=path.join(root,`${backendId}.json`);
124
+ if(transportFactory===null){
125
+ insist(typeof executable==='string'&&path.isAbsolute(executable),'agent backend executable must be absolute');
126
+ insist(HEX64.test(executableSha256??''),'agent backend executable SHA-256 invalid');
127
+ insist(await fileSha256(executable)===executableSha256,'agent backend executable hash mismatch');
128
+ insist(Array.isArray(args)&&args.length<=64&&args.every(x=>typeof x==='string'&&x.length<=4096),'agent backend args invalid');
129
+ transportFactory=async()=>new JsonRpcLineTransport({executable,args,cwd,env});
130
+ }else insist(typeof transportFactory==='function','agent backend transportFactory invalid');
131
+ insist(serverRequestHandler===null||typeof serverRequestHandler==='function','agent backend serverRequestHandler invalid');
132
+ insist(notificationHandler===null||typeof notificationHandler==='function','agent backend notificationHandler invalid');
133
+ insist(['jsonrpc2','codex-app-server'].includes(wireMode),'agent backend wireMode invalid');
134
+ const wire=value=>wireMode==='jsonrpc2'?{jsonrpc:'2.0',...value}:value;
135
+
136
+ let state=await loadState(stateFile,backendId);
137
+ let changed=false;
138
+ for(const item of Object.values(state.requests)){
139
+ if(item.state==='pending'){item.state='interrupted';item.updated_at_utc=now();item.error='backend restarted before terminal response';changed=true;}
140
+ }
141
+ if(changed){state.updated_at_utc=now();await atomicJson(stateFile,state);}
142
+ else{
143
+ try{await lstat(stateFile);}catch(error){if(error?.code==='ENOENT')await atomicJson(stateFile,state);else throw error;}
144
+ }
145
+
146
+ let transport=null;
147
+ let started=false;
148
+ const pending=new Map();
149
+ let writeChain=Promise.resolve();
150
+ const persist=async()=>{
151
+ state.updated_at_utc=now();
152
+ writeChain=writeChain.then(()=>atomicJson(stateFile,state));
153
+ return writeChain;
154
+ };
155
+ const publicState=()=>({
156
+ status:'ok',backend_id:backendId,running:started,state_epoch:state.state_epoch,
157
+ threads:structuredClone(state.threads),requests:structuredClone(state.requests)
158
+ });
159
+
160
+ async function terminal(id,{stateName,result=null,error=null}){
161
+ const record=state.requests[id];if(!record||record.state!=='pending')return;
162
+ record.state=stateName;record.updated_at_utc=now();
163
+ if(result!==null)record.result=result;
164
+ if(error!==null)record.error=String(error);
165
+ await persist();
166
+ const waiter=pending.get(id);pending.delete(id);
167
+ if(!waiter)return;
168
+ if(stateName==='completed')waiter.resolve(result);
169
+ else waiter.reject(new Error(record.error??stateName));
170
+ }
171
+ async function handleMessage(message){
172
+ if(!message||typeof message!=='object'||Array.isArray(message))return;
173
+ if(typeof message.id==='string'&&Object.hasOwn(message,'method')){
174
+ const method=String(message.method);
175
+ try{
176
+ if(!serverRequestHandler) throw Object.assign(new Error('server request denied by DEADBYTE policy'),{code:-32001});
177
+ const result=await serverRequestHandler({method,params:message.params??{},backendId});
178
+ await transport?.send(wire({id:message.id,result}));
179
+ }catch(error){
180
+ await transport?.send(wire({id:message.id,error:{code:Number.isInteger(error?.code)?error.code:-32001,message:String(error?.message??error)}}));
181
+ }
182
+ return;
183
+ }
184
+ if(!Object.hasOwn(message,'id')&&typeof message.method==='string'){
185
+ if(notificationHandler) await notificationHandler({method:String(message.method),params:message.params??{},backendId});
186
+ return;
187
+ }
188
+ if(typeof message.id!=='string'||!state.requests[message.id])return;
189
+ if(Object.hasOwn(message,'result'))await terminal(message.id,{stateName:'completed',result:message.result});
190
+ else if(Object.hasOwn(message,'error'))await terminal(message.id,{stateName:'failed',error:message.error?.message??'JSON-RPC error'});
191
+ }
192
+ async function start(){
193
+ if(started)return publicState();
194
+ transport=await transportFactory();
195
+ insist(transport&&typeof transport.start==='function'&&typeof transport.send==='function'&&typeof transport.on==='function','agent backend transport contract invalid');
196
+ transport.on('message',message=>{void handleMessage(message);});
197
+ transport.on('protocol_error',error=>{
198
+ for(const id of pending.keys())void terminal(id,{stateName:'failed',error:String(error?.message??error)});
199
+ });
200
+ transport.on('transport_error',error=>{
201
+ for(const id of pending.keys())void terminal(id,{stateName:'interrupted',error:String(error?.message??error)});
202
+ started=false;
203
+ });
204
+ transport.on('exit',()=>{
205
+ for(const id of pending.keys())void terminal(id,{stateName:'interrupted',error:'agent backend transport exited'});
206
+ started=false;
207
+ });
208
+ await transport.start();started=true;return publicState();
209
+ }
210
+ async function bindThread({logicalId,threadId}={}){
211
+ insist(typeof logicalId==='string'&&ID.test(logicalId),'logical thread id invalid');
212
+ insist(typeof threadId==='string'&&threadId.length>=1&&threadId.length<=512,'provider thread id invalid');
213
+ const existing=state.threads[logicalId];
214
+ if(existing!==undefined&&existing!==threadId) throw new Error('logical thread already bound to different provider thread');
215
+ state.threads[logicalId]=threadId;await persist();
216
+ return {status:'ok',logical_id:logicalId,thread_id:threadId};
217
+ }
218
+ async function request({method,params={},timeoutMs=60000}={}){
219
+ insist(started&&transport,'agent backend is not started');
220
+ insist(typeof method==='string'&&METHOD.test(method),'agent backend method invalid');
221
+ insist(isJson(params)&&params&&typeof params==='object','agent backend params must be JSON');
222
+ insist(Number.isInteger(timeoutMs)&&timeoutMs>=100&&timeoutMs<=900000,'agent backend timeout invalid');
223
+ const sequence=++state.next_sequence;
224
+ const paramsSha=sha(Buffer.from(canonicalJson(params),'utf8'));
225
+ const id=sha(Buffer.from(canonicalJson({backend_id:backendId,state_epoch:state.state_epoch,sequence,method,params_sha256:paramsSha}),'utf8'));
226
+ state.requests[id]={state:'pending',method,params_sha256:paramsSha,created_at_utc:now(),updated_at_utc:now()};
227
+ await persist();
228
+ let timer;
229
+ const promise=new Promise((resolve,reject)=>{
230
+ pending.set(id,{resolve,reject});
231
+ timer=setTimeout(()=>{void terminal(id,{stateName:'failed',error:'agent backend request timeout'});},timeoutMs);
232
+ }).finally(()=>clearTimeout(timer));
233
+ try{await transport.send(wire({id,method,params}));}
234
+ catch(error){await terminal(id,{stateName:'failed',error:String(error?.message??error)});}
235
+ return promise;
236
+ }
237
+ async function notify({method,params={}}={}){
238
+ insist(started&&transport,'agent backend is not started');
239
+ insist(typeof method==='string'&&METHOD.test(method),'agent backend notification method invalid');
240
+ insist(isJson(params)&&params&&typeof params==='object','agent backend notification params must be JSON');
241
+ await transport.send(wire({method,params}));
242
+ return {status:'sent',method};
243
+ }
244
+ async function status(){return publicState();}
245
+ async function close(){
246
+ for(const id of [...pending.keys()])await terminal(id,{stateName:'interrupted',error:'agent backend closed before response'});
247
+ if(transport?.stop)await transport.stop();transport=null;started=false;return publicState();
248
+ }
249
+ async function simulateCrashForTest(){
250
+ for(const [id,waiter] of pending){waiter.reject(new Error('simulated backend crash'));pending.delete(id);}
251
+ if(transport?.stop)await transport.stop();transport=null;started=false;
252
+ }
253
+ return Object.freeze({start,bindThread,request,notify,status,close,simulateCrashForTest,state_file:stateFile,wire_mode:wireMode});
254
+ }
@@ -68,7 +68,7 @@ export const autonomousSessionStatusOutputSchema = statusContract({
68
68
  shape:{ configured:z.literal(true).optional(), armed:z.boolean().optional(), autonomous_armed:z.boolean().optional(),
69
69
  autonomous_reason:z.string().optional(), autonomous_expires_at_utc:z.string().nullable().optional(),
70
70
  coding_armed:z.boolean().optional(), coding_reason:z.string().optional(), root_ids:z.array(z.string()).max(64).optional(),
71
- planner_backend:z.enum(['client_sampling','openai_compatible','openai_responses']).optional(), profile_ids:z.array(z.string()).max(128).optional(),
71
+ planner_backend:z.enum(['client_sampling','openai_compatible','openai_responses','codex_app_server']).optional(), profile_ids:z.array(z.string()).max(128).optional(),
72
72
  policy_sha256:hex64.optional(), coding_policy_sha256:hex64.optional() }
73
73
  });
74
74
 
@@ -30,12 +30,31 @@ function normalizeProviderCircuit(raw) {
30
30
  half_open_probe_limit:integer(value.half_open_probe_limit,'planner.circuit.half_open_probe_limit',1,8,1)
31
31
  });
32
32
  }
33
- function normalizePlanner(raw) {
33
+ function normalizePlanner(raw,codingPolicy,rootIds) {
34
34
  if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new Error('autonomous planner missing');
35
35
  const backend = raw.backend;
36
- if (!['client_sampling', 'openai_compatible', 'openai_responses'].includes(backend)) throw new Error('autonomous planner backend invalid');
36
+ if (!['client_sampling', 'openai_compatible', 'openai_responses', 'codex_app_server'].includes(backend)) throw new Error('autonomous planner backend invalid');
37
37
  const common = { backend, max_tokens: integer(raw.max_tokens, 'planner.max_tokens', 256, 16384, 4096) };
38
38
  if (backend === 'client_sampling') return common;
39
+ if (typeof raw.model !== 'string' || raw.model.length < 1 || raw.model.length > 128) throw new Error('planner.model invalid');
40
+ if (backend === 'codex_app_server') {
41
+ if (!ID_RE.test(raw.root_id ?? '') || !rootIds.includes(raw.root_id) || !codingPolicy.roots[raw.root_id]) {
42
+ throw new Error('planner.root_id invalid');
43
+ }
44
+ if (typeof raw.executable !== 'string' || !path.isAbsolute(raw.executable)) throw new Error('planner.executable must be absolute');
45
+ if (!SHA256_RE.test(raw.executable_sha256 ?? '')) throw new Error('planner.executable_sha256 invalid');
46
+ const args=raw.args ?? ['app-server'];
47
+ if (!Array.isArray(args) || args.length < 1 || args.length > 16 ||
48
+ args.some(value=>typeof value!=='string'||value.length<1||value.length>2048||value.includes('\0'))) {
49
+ throw new Error('planner.args invalid');
50
+ }
51
+ return {
52
+ ...common, model:raw.model, root_id:raw.root_id, executable:path.resolve(raw.executable),
53
+ executable_sha256:raw.executable_sha256, args:[...args],
54
+ timeout_ms:integer(raw.timeout_ms,'planner.timeout_ms',1000,300000,60000),
55
+ circuit:normalizeProviderCircuit(raw.circuit)
56
+ };
57
+ }
39
58
  if (typeof raw.endpoint !== 'string') throw new Error('planner.endpoint required');
40
59
  const endpoint = new URL(raw.endpoint);
41
60
  const loopback = ['127.0.0.1', 'localhost', '::1'].includes(endpoint.hostname);
@@ -43,7 +62,6 @@ function normalizePlanner(raw) {
43
62
  throw new Error('planner endpoint must be HTTPS or loopback HTTP');
44
63
  }
45
64
  if (endpoint.username || endpoint.password || endpoint.hash) throw new Error('planner endpoint credentials/fragments forbidden');
46
- if (typeof raw.model !== 'string' || raw.model.length < 1 || raw.model.length > 128) throw new Error('planner.model invalid');
47
65
  const apiKeyEnv = raw.api_key_env ?? null;
48
66
  if (apiKeyEnv !== null && !ENV_RE.test(apiKeyEnv)) throw new Error('planner.api_key_env invalid');
49
67
  const provider = {
@@ -117,7 +135,7 @@ export async function loadAutonomousPolicy(policyPath, codingPolicy) {
117
135
  schema: AUTONOMOUS_POLICY_SCHEMA, enabled: true, policy_path: absolute,
118
136
  lease_file: path.resolve(raw.lease_file), evidence_dir: path.resolve(raw.evidence_dir),
119
137
  coding_policy_sha256: codingPolicy.policy_sha256, root_ids: rootIds,
120
- planner: normalizePlanner(raw.planner), profiles,
138
+ planner: normalizePlanner(raw.planner,codingPolicy,rootIds), profiles,
121
139
  limits: {
122
140
  max_iterations: integer(limits.max_iterations, 'limits.max_iterations', 1, 64, 12),
123
141
  max_mutations: integer(limits.max_mutations, 'limits.max_mutations', 1, 128, 24),
@@ -165,3 +183,22 @@ export async function assertAutonomousArmed(policy, rootId = null, now = new Dat
165
183
  if (rootId !== null && !status.root_ids.includes(rootId)) throw new Error(`autonomous root '${rootId}' is outside active lease`);
166
184
  return status;
167
185
  }
186
+
187
+ export async function recheckAutonomousAuthority(policy, initial, rootId = null, now = new Date()) {
188
+ if (!initial?.armed || typeof initial.instance_nonce !== 'string' || !Array.isArray(initial.root_ids)) {
189
+ throw new Error('autonomous authority recheck requires an armed initial snapshot');
190
+ }
191
+ let current;
192
+ try { current = await assertAutonomousArmed(policy, rootId, now); }
193
+ catch (error) { throw new Error(`autonomous authority changed during operation: ${String(error?.message ?? error)}`); }
194
+ const initialRoots = [...initial.root_ids].sort();
195
+ const currentRoots = [...current.root_ids].sort();
196
+ const changed =
197
+ current.instance_nonce !== initial.instance_nonce ||
198
+ current.standing !== initial.standing ||
199
+ current.expires_at_utc !== initial.expires_at_utc ||
200
+ initialRoots.length !== currentRoots.length ||
201
+ initialRoots.some((id, index) => id !== currentRoots[index]);
202
+ if (changed) throw new Error('autonomous authority changed during operation');
203
+ return current;
204
+ }
@@ -3,7 +3,7 @@ import { access, mkdir, open, readFile, readdir, rm, writeFile } from 'node:fs/p
3
3
  import path from 'node:path';
4
4
  import { canonicalJson } from './canonical-json.mjs';
5
5
  import { loadPrivateSigningKey, loadPublicVerifyKey } from './trust.mjs';
6
- import { assertAutonomousArmed, autonomousSessionStatus } from './autonomous-policy.mjs';
6
+ import { assertAutonomousArmed, autonomousSessionStatus, recheckAutonomousAuthority } from './autonomous-policy.mjs';
7
7
  import { buildPlannerPrompt, parsePlannerDecision, parseR34PlannerDecision, requestProviderDecision } from './autonomous-planner.mjs';
8
8
  import { actionIdentity, reconcileStartedAction } from './autonomous-action-reconcile.mjs';
9
9
  import { createPlan, replanPlan, completePlanStep, planIsComplete as r34PlanIsComplete } from './autonomous-plan.mjs';
@@ -274,16 +274,24 @@ async function executeProfile({ ctx, events, goalId, actionKey, profileId, epoch
274
274
  await appendEvent(ctx,events,signingKeyPath,'profile_completed',payload);
275
275
  return { status:outcome.passed ? 'passed' : 'failed', observation:payload, existing:false };
276
276
  }
277
+ const autonomousAuthority = await assertAutonomousArmed(policy,profile.root_id);
278
+ if (Number.isFinite(autonomousAuthority.remaining_ms) && autonomousAuthority.remaining_ms < 100) {
279
+ throw new Error('autonomous lease expires too soon to execute profile');
280
+ }
277
281
  await appendEvent(ctx,events,signingKeyPath,'profile_started',{ action_key:actionKey, profile_id:profileId,
278
- mutation_epoch:epoch, receipt_id:receiptId, request_sha256:requestSha256 });
282
+ mutation_epoch:epoch, receipt_id:receiptId, request_sha256:requestSha256,
283
+ authority_instance_nonce:autonomousAuthority.instance_nonce,
284
+ authority_expires_at_utc:autonomousAuthority.expires_at_utc });
285
+ const preExecutionCheck = () => recheckAutonomousAuthority(policy,autonomousAuthority,profile.root_id);
279
286
  let result;
280
287
  try {
281
288
  result = profile.kind === 'command'
282
289
  ? await codingRuntime.commandRun({ rootId:profile.root_id, commandId:profile.command_id, scriptPath:profile.script_path,
283
290
  scriptSha256:profile.script_sha256, args:profile.args, cwd:profile.cwd, timeoutMs:profile.timeout_ms,
284
- receiptId, requestSha256 })
291
+ executionDeadlineUtc:autonomousAuthority.expires_at_utc, preExecutionCheck, receiptId, requestSha256 })
285
292
  : await codingRuntime.benchmarkRun({ rootId:profile.root_id, commandId:profile.command_id, scriptPath:profile.script_path,
286
293
  scriptSha256:profile.script_sha256, args:profile.args, cwd:profile.cwd, timeoutMs:profile.timeout_ms,
294
+ executionDeadlineUtc:autonomousAuthority.expires_at_utc, preExecutionCheck,
287
295
  warmups:profile.warmups, iterations:profile.iterations, baselineMedianMs:profile.baseline_median_ms,
288
296
  maxRegressionPct:profile.max_regression_pct, receiptId, requestSha256 });
289
297
  } catch (error) {
@@ -527,7 +535,7 @@ function withLoopResult(goal,events,result) {
527
535
  return loop ? {...result,loop} : result;
528
536
  }
529
537
  function providerCircuitSnapshot(policy,events,observedAtUtc=new Date().toISOString()) {
530
- if (!['openai_compatible','openai_responses'].includes(policy.planner.backend)) return null;
538
+ if (!['openai_compatible','openai_responses','codex_app_server'].includes(policy.planner.backend)) return null;
531
539
  return deriveCircuit(events,providerCircuitId(policy.planner),policy.planner.circuit,observedAtUtc);
532
540
  }
533
541
  async function enterProviderCircuitWait({ctx,events,goal,policy,signingKeyPath,reason,circuit}) {
@@ -545,7 +553,7 @@ async function enterProviderCircuitWait({ctx,events,goal,policy,signingKeyPath,r
545
553
  });
546
554
  return {status:'paused',reason,circuit};
547
555
  }
548
- async function requestProviderWithCircuit({ctx,events,goal,policy,pending,signingKeyPath}) {
556
+ async function requestProviderWithCircuit({ctx,events,goal,policy,pending,signingKeyPath,plannerProviderRuntime}) {
549
557
  const providerId=providerCircuitId(policy.planner);
550
558
  let circuit=providerCircuitSnapshot(policy,events);
551
559
  let disposition=providerCallDisposition(circuit);
@@ -566,7 +574,9 @@ async function requestProviderWithCircuit({ctx,events,goal,policy,pending,signin
566
574
  });
567
575
  }
568
576
  try {
569
- const response=await requestProviderDecision(policy,pending.prompt);
577
+ const response=policy.planner.backend==='codex_app_server'
578
+ ? await plannerProviderRuntime.runPlanner({logicalId:`goal-${goal.goal_id}`,prompt:pending.prompt,timeoutMs:policy.planner.timeout_ms})
579
+ : await requestProviderDecision(policy,pending.prompt);
570
580
  await appendEvent(ctx,events,signingKeyPath,'provider_call_succeeded',{
571
581
  provider_id:providerId,request_id:pending.request_id,probe:disposition.probe===true,
572
582
  ...(response.response_id?{response_id:response.response_id}:{}),
@@ -589,15 +599,15 @@ async function requestProviderWithCircuit({ctx,events,goal,policy,pending,signin
589
599
  throw error;
590
600
  }
591
601
  }
592
- async function acceptPlannerDecision({ ctx, events, goal, state, policy, codingRuntime, capabilityManifest, desktopMemoryProvider,memoryProvider, signingKeyPath, plannerResponse }) {
602
+ async function acceptPlannerDecision({ ctx, events, goal, state, policy, codingRuntime, capabilityManifest, desktopMemoryProvider,memoryProvider, signingKeyPath, plannerResponse, plannerProviderRuntime }) {
593
603
  let currentState = state;
594
604
  let pending = currentState.pending_request ?? await requestPlanner(ctx,goal,events,currentState,policy,codingRuntime,capabilityManifest,desktopMemoryProvider,memoryProvider,signingKeyPath);
595
605
  if (!pending) return null;
596
606
  let suppliedResponse = plannerResponse;
597
607
  while (pending) {
598
608
  let response;
599
- if (['openai_compatible','openai_responses'].includes(policy.planner.backend)) {
600
- const provider=await requestProviderWithCircuit({ctx,events,goal,policy,pending,signingKeyPath});
609
+ if (['openai_compatible','openai_responses','codex_app_server'].includes(policy.planner.backend)) {
610
+ const provider=await requestProviderWithCircuit({ctx,events,goal,policy,pending,signingKeyPath,plannerProviderRuntime});
601
611
  if (provider.waiting) return {needs_planner:false,rejected:false,paused:true,provider_waiting:true};
602
612
  response=provider.response;
603
613
  } else {
@@ -1765,9 +1775,12 @@ function isAuthorityUnavailable(error) {
1765
1775
 
1766
1776
  export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRuntime, machineFsRuntime = null,
1767
1777
  observationRuntime = null, processRuntime = null, semanticRuntime = null, desktopRuntime = null,
1768
- desktopMemoryProvider = null,memoryProvider=null, signingKeyPath, verifyKeyPath }) {
1778
+ desktopMemoryProvider = null,memoryProvider=null, plannerProviderRuntime=null, signingKeyPath, verifyKeyPath }) {
1769
1779
  if (!policy || !codingRuntime || !nativeDiffRuntime) throw new Error('autonomous runtime requires policy/coding/native-diff runtimes');
1770
1780
  if (!signingKeyPath || !verifyKeyPath) throw new Error('autonomous runtime requires trust keys');
1781
+ if (policy.planner?.backend==='codex_app_server' && (!plannerProviderRuntime || typeof plannerProviderRuntime.runPlanner!=='function')) {
1782
+ throw new Error('Codex planner provider runtime is required by autonomous policy');
1783
+ }
1771
1784
  const capabilityManifest=configuredCapabilityManifest({machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime});
1772
1785
  const profileTimeoutCeiling=Math.max(120000,...Object.values(policy.profiles??{}).map(profile=>
1773
1786
  Number.isInteger(profile?.timeout_ms)&&profile.timeout_ms>0?profile.timeout_ms+120000:120000));
@@ -1784,7 +1797,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1784
1797
  coding_policy_sha256:policy.coding_policy_sha256 };
1785
1798
  }
1786
1799
  async function providerCircuitStatus({goalId}) {
1787
- if (!['openai_compatible','openai_responses'].includes(policy.planner.backend)) return null;
1800
+ if (!['openai_compatible','openai_responses','codex_app_server'].includes(policy.planner.backend)) return null;
1788
1801
  const ctx=goalContext(policy,goalId);
1789
1802
  await loadGoal(ctx,policy,verifyKeyPath);
1790
1803
  const journal=await readEvents(ctx,verifyKeyPath);
@@ -2046,7 +2059,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
2046
2059
  continue;
2047
2060
  }
2048
2061
  const accepted = await acceptPlannerDecision({ ctx,events:journal,goal,state,policy,codingRuntime,capabilityManifest,
2049
- desktopMemoryProvider,memoryProvider,signingKeyPath,plannerResponse:suppliedResponse });
2062
+ desktopMemoryProvider,memoryProvider,signingKeyPath,plannerResponse:suppliedResponse,plannerProviderRuntime });
2050
2063
  suppliedResponse = null;
2051
2064
  if (accepted?.needs_planner) {
2052
2065
  state = deriveState(goal,journal);
@@ -7,6 +7,8 @@ import { createCodingRuntime, loadCodingPolicy } from './coding-plane.mjs';
7
7
  import { loadAutonomousPolicy } from './autonomous-policy.mjs';
8
8
  import { createNativeDiffRuntime } from './native-diff.mjs';
9
9
  import { createAutonomousRuntime } from './autonomous-runtime.mjs';
10
+ import { createCodexAppServerR44 } from './codex-app-server-r44.mjs';
11
+ import { DEADBYTE_VERSION } from './version.mjs';
10
12
  import { startLocalSupervisorHost } from './autonomous-local-supervisor.mjs';
11
13
 
12
14
  const packageRoot=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
@@ -30,7 +32,15 @@ const codingPolicy=await loadCodingPolicy(codingPolicyPath);
30
32
  const codingRuntime=createCodingRuntime({policy:codingPolicy,signingKeyPath,verifyKeyPath});
31
33
  const autonomousPolicy=await loadAutonomousPolicy(autonomousPolicyPath,codingPolicy);
32
34
  const nativeDiffRuntime=createNativeDiffRuntime({policy:autonomousPolicy,codingRuntime,signingKeyPath,verifyKeyPath});
33
- const autonomousRuntime=createAutonomousRuntime({policy:autonomousPolicy,codingRuntime,nativeDiffRuntime,signingKeyPath,verifyKeyPath});
35
+ const plannerProviderRuntime=autonomousPolicy.planner.backend==='codex_app_server'
36
+ ? await createCodexAppServerR44({
37
+ stateDir:path.join(autonomousPolicy.evidence_dir,'provider-r44','codex-app-server'),
38
+ model:autonomousPolicy.planner.model,cwd:codingPolicy.roots[autonomousPolicy.planner.root_id].real_path,
39
+ clientVersion:DEADBYTE_VERSION,executable:autonomousPolicy.planner.executable,
40
+ executableSha256:autonomousPolicy.planner.executable_sha256,args:autonomousPolicy.planner.args
41
+ })
42
+ : null;
43
+ const autonomousRuntime=createAutonomousRuntime({policy:autonomousPolicy,codingRuntime,nativeDiffRuntime,plannerProviderRuntime,signingKeyPath,verifyKeyPath});
34
44
  const markerPath=path.resolve(process.env.DEADBYTE_SUPERVISOR_MARKER||path.join(autonomousPolicy.evidence_dir,'supervisor-owner.json'));
35
45
  const host=await startLocalSupervisorHost({runtime:autonomousRuntime,policy:autonomousPolicy,markerPath,
36
46
  idleDelayMs:750,maxCycles:32,continuous:onceMode!=='1'});
@@ -43,7 +53,7 @@ let stopping=false;
43
53
  async function stopAndExit(code=0){
44
54
  if(stopping) return;
45
55
  stopping=true;
46
- try { await host.stop(); }
56
+ try { await host.stop(); await plannerProviderRuntime?.close(); }
47
57
  catch(error){ process.stderr.write(`DEADBYTE supervisor host stop failed: ${String(error)}\n`); code=1; }
48
58
  process.exitCode=code;
49
59
  }
@@ -3,7 +3,7 @@ import { createHash } from 'node:crypto';
3
3
  export const CAPABILITY_LEDGER_SCHEMA='deadbyte.capability-ledger.v1';
4
4
  const AUTHORITIES=new Set(['none','process','coding','autonomous','desktop','host']);
5
5
  const PROFILE_ENV='DEADBYTE_CAPABILITY_PROFILE';
6
- const COMPACT_TOOL_LIMIT=42;
6
+ const COMPACT_TOOL_LIMIT=43;
7
7
 
8
8
  function sha256(text){return createHash('sha256').update(text,'utf8').digest('hex');}
9
9
  function canonicalJson(value){
@@ -81,6 +81,7 @@ const gateway=(id,full_tool,compact_tool,compact_op,runtime_flag,authority,{muta
81
81
 
82
82
  export const R36_CAPABILITY_ENTRIES=Object.freeze([
83
83
  direct('evidence.verify','evidence_verify',null,'none',{receipt_class:'verification'}),
84
+ direct('skill.catalog','skill_catalog',null,'none',{receipt_class:'skill'}),
84
85
  gateway('machine.list','machine_list','machine_observe','machine_list','DEADBYTE_MACHINE_RUNTIME','none',{receipt_class:'machine'}),
85
86
  gateway('machine.status','machine_status','machine_observe','machine_status','DEADBYTE_MACHINE_RUNTIME','none',{receipt_class:'machine'}),
86
87
  gateway('machine.ping','machine_ping','machine_observe','machine_ping','DEADBYTE_MACHINE_RUNTIME','none',{receipt_class:'machine'}),