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
@@ -32,7 +32,7 @@ async function startPlanner(decisions) {
32
32
  await new Promise(resolve => server.listen(0,'127.0.0.1',resolve));
33
33
  return { server, prompts, endpoint:`http://127.0.0.1:${server.address().port}/v1/chat/completions` };
34
34
  }
35
- async function fixture(decisionPrefix = []) {
35
+ async function fixture(decisionPrefix = [], options = {}) {
36
36
  const root = await mkdtemp(path.join(os.tmpdir(),'deadbyte-autonomous-loop-'));
37
37
  const project = path.join(root,'project'); const trust = path.join(root,'trust');
38
38
  await mkdir(project); await mkdir(trust);
@@ -101,14 +101,17 @@ async function fixture(decisionPrefix = []) {
101
101
  const policy = await loadAutonomousPolicy(autonomousPolicyPath,codingPolicy);
102
102
  await writeFile(policy.lease_file,JSON.stringify({ schema:'deadbyte.autonomous-lease.v1',
103
103
  policy_sha256:policy.policy_sha256, instance_nonce:randomBytes(32).toString('hex'), root_ids:['project'],
104
- armed_at_utc:new Date().toISOString(), expires_at_utc:new Date(Date.now()+1800000).toISOString() }));
104
+ armed_at_utc:new Date().toISOString(), expires_at_utc:new Date(Date.now()+(options.autonomousLeaseMs??1800000)).toISOString() }));
105
105
  const codingRuntime = createCodingRuntime({ policy:codingPolicy,
106
106
  signingKeyPath:keys.privateKeyPath, verifyKeyPath:keys.publicKeyPath });
107
+ const autonomousCodingRuntime = options.codingRuntimeFactory
108
+ ? options.codingRuntimeFactory(codingRuntime,{policy})
109
+ : codingRuntime;
107
110
  const nativeDiff = createNativeDiffRuntime({ policy,codingRuntime,
108
111
  signingKeyPath:keys.privateKeyPath, verifyKeyPath:keys.publicKeyPath });
109
- const runtime = createAutonomousRuntime({ policy,codingRuntime,nativeDiffRuntime:nativeDiff,
112
+ const runtime = createAutonomousRuntime({ policy,codingRuntime:autonomousCodingRuntime,nativeDiffRuntime:nativeDiff,
110
113
  signingKeyPath:keys.privateKeyPath, verifyKeyPath:keys.publicKeyPath });
111
- return { root,project,program,keys,policy,codingRuntime,nativeDiff,runtime,planner,
114
+ return { root,project,program,keys,policy,codingRuntime,autonomousCodingRuntime,nativeDiff,runtime,planner,
112
115
  initialHash,almostHash,fixedHash };
113
116
  }
114
117
 
@@ -144,6 +147,83 @@ test('R24 autonomous goal diagnoses a failing test, replans with native diff, be
144
147
  }
145
148
  });
146
149
 
150
+ test('autonomous profile execution is capped by the absolute autonomous lease without changing coding request identity', async () => {
151
+ const calls=[];
152
+ const f=await fixture([],{
153
+ autonomousLeaseMs:30000,
154
+ codingRuntimeFactory:base=>Object.freeze({
155
+ ...base,
156
+ commandRun:async args=>{
157
+ const result=await base.commandRun(args);
158
+ calls.push({kind:'command',deadline:args.executionDeadlineUtc,has_precheck:typeof args.preExecutionCheck==='function',
159
+ timeout_ms:result.timeout_ms,request_sha256:result.request_sha256});
160
+ return result;
161
+ },
162
+ benchmarkRun:async args=>{
163
+ const result=await base.benchmarkRun(args);
164
+ calls.push({kind:'benchmark',deadline:args.executionDeadlineUtc,has_precheck:typeof args.preExecutionCheck==='function',
165
+ request_sha256:result.request_sha256});
166
+ return result;
167
+ }
168
+ })
169
+ });
170
+ try {
171
+ const submitted=await f.runtime.submit({submissionId:'profile-deadline',rootId:'project',
172
+ title:'deadline-bound profiles',goal:'make exported value equal fixed',
173
+ acceptance:'build, test, and benchmark must pass',maxIterations:10});
174
+ const result=await f.runtime.run({goalId:submitted.goal_id,maxCycles:16});
175
+ assert.equal(result.status,'completed');
176
+ assert.ok(calls.length>=3);
177
+ assert.ok(calls.every(call=>typeof call.deadline==='string'&&Number.isFinite(Date.parse(call.deadline))));
178
+ assert.ok(calls.every(call=>call.has_precheck===true));
179
+ const command=calls.find(call=>call.kind==='command');
180
+ assert.ok(command.timeout_ms>=100&&command.timeout_ms<30000);
181
+ const build=f.policy.profiles.build;
182
+ assert.equal(command.request_sha256,f.codingRuntime.requestSha('coding_command_run',{
183
+ root_id:build.root_id,command_id:build.command_id,script_path:build.script_path,script_sha256:build.script_sha256,
184
+ args:build.args,cwd:build.cwd,timeout_ms:build.timeout_ms
185
+ }));
186
+ } finally {
187
+ await new Promise(resolve=>f.planner.server.close(resolve));
188
+ await rm(f.root,{recursive:true,force:true});
189
+ }
190
+ });
191
+
192
+ test('autonomous profile nonce rotation immediately before spawn fails closed without a coding receipt', async () => {
193
+ let rotated=false;
194
+ const f=await fixture([],{
195
+ codingRuntimeFactory:(base,{policy})=>Object.freeze({
196
+ ...base,
197
+ commandRun:async args=>{
198
+ if(!rotated){
199
+ rotated=true;
200
+ const lease=JSON.parse(await readFile(policy.lease_file,'utf8'));
201
+ await writeFile(policy.lease_file,JSON.stringify({...lease,instance_nonce:'f'.repeat(64)}));
202
+ }
203
+ return base.commandRun(args);
204
+ }
205
+ })
206
+ });
207
+ try {
208
+ const submitted=await f.runtime.submit({submissionId:'profile-revoke-before-spawn',rootId:'project',
209
+ title:'profile revoke',goal:'make exported value equal fixed',
210
+ acceptance:'profile dispatch must fail closed after authority rotation',maxIterations:10});
211
+ const result=await f.runtime.run({goalId:submitted.goal_id,maxCycles:16});
212
+ assert.equal(rotated,true);
213
+ assert.equal(result.status,'paused');
214
+ const journal=await f.runtime.events({goalId:submitted.goal_id,fromSeq:1,limit:200});
215
+ const started=journal.events.find(event=>event.type==='profile_started'&&event.payload?.profile_id==='build');
216
+ assert.ok(started);
217
+ assert.match(started.payload.authority_instance_nonce,/^[0-9a-f]{64}$/);
218
+ assert.ok(journal.events.some(event=>event.type==='goal_paused'&&event.payload?.reason==='profile_execution_without_receipt'));
219
+ assert.equal(journal.events.some(event=>event.type==='profile_completed'&&event.payload?.profile_id==='build'),false);
220
+ assert.equal((await f.codingRuntime.verifyReceiptDetailed(started.payload.receipt_id)).ok,false);
221
+ } finally {
222
+ await new Promise(resolve=>f.planner.server.close(resolve));
223
+ await rm(f.root,{recursive:true,force:true});
224
+ }
225
+ });
226
+
147
227
  test('provider planner repairs an invalid structured decision inside the same run', async () => {
148
228
  const f = await fixture(['not-json']);
149
229
  try {
@@ -166,7 +246,7 @@ test('provider planner repairs an invalid structured decision inside the same ru
166
246
  }
167
247
  });
168
248
 
169
- test('provider circuit survives runtime restart, suppresses OPEN calls, then permits one HALF_OPEN probe', async () => {
249
+ test('provider circuit survives runtime restart and permits exactly one probe at the HALF_OPEN boundary', async () => {
170
250
  const failures=[{__http_status:503},{__http_status:503},{__http_status:503}];
171
251
  const f=await fixture(failures);
172
252
  try {
@@ -178,17 +258,23 @@ test('provider circuit survives runtime restart, suppresses OPEN calls, then per
178
258
  const opened=await f.runtime.run({goalId:submitted.goal_id,maxCycles:2});
179
259
  assert.equal(opened.status,'paused');
180
260
  assert.equal(opened.reason,'provider_circuit_open');
261
+ const openedJournal=await f.runtime.events({goalId:submitted.goal_id,fromSeq:1,limit:200});
262
+ const openPause=[...openedJournal.events].reverse().find(event=>event.type==='goal_paused'&&event.payload?.reason==='provider_circuit_open');
263
+ assert.equal(openPause?.payload?.circuit_state,'OPEN',JSON.stringify(openPause));
181
264
  assert.equal(f.planner.prompts.length,3);
182
265
 
183
- const immediate=await f.runtime.run({goalId:submitted.goal_id,maxCycles:2});
184
- assert.equal(immediate.status,'paused');
185
- assert.equal(f.planner.prompts.length,3,'OPEN circuit must suppress provider invocation');
186
-
187
266
  const restarted=createAutonomousRuntime({policy:f.policy,codingRuntime:f.codingRuntime,nativeDiffRuntime:f.nativeDiff,
188
267
  signingKeyPath:f.keys.privateKeyPath,verifyKeyPath:f.keys.publicKeyPath});
189
- await new Promise(resolve=>setTimeout(resolve,130));
190
- const circuitBeforeResume=await restarted.providerCircuitStatus({goalId:submitted.goal_id});
268
+ const deadline=Date.now()+2_000;
269
+ let circuitBeforeResume;
270
+ do {
271
+ circuitBeforeResume=await restarted.providerCircuitStatus({goalId:submitted.goal_id});
272
+ if(circuitBeforeResume.state==='HALF_OPEN') break;
273
+ assert.equal(circuitBeforeResume.state,'OPEN',JSON.stringify(circuitBeforeResume));
274
+ await new Promise(resolve=>setTimeout(resolve,10));
275
+ } while(Date.now()<deadline);
191
276
  assert.equal(circuitBeforeResume.state,'HALF_OPEN',JSON.stringify(circuitBeforeResume));
277
+
192
278
  const resumed=await restarted.run({goalId:submitted.goal_id,maxCycles:1});
193
279
  assert.notEqual(resumed.status,'paused');
194
280
  assert.equal(f.planner.prompts.length,4,'HALF_OPEN must permit exactly one provider probe');
@@ -20,7 +20,7 @@ const r24Tools = [
20
20
  'native_diff_apply', 'native_diff_preview', 'native_diff_verify'
21
21
  ];
22
22
  const r23Tools = [
23
- 'contained_transform','evidence_verify','file_read','proof_execute','workspace_open',
23
+ 'contained_transform','evidence_verify','file_read','proof_execute','skill_catalog','workspace_open',
24
24
  'agent_task_approve','agent_task_cancel','agent_task_events','agent_task_run',
25
25
  'agent_task_status','agent_task_submit','agent_task_verify',
26
26
  'coding_benchmark_run','coding_command_run','coding_file_read','coding_file_write','coding_files_read',
@@ -75,7 +75,7 @@ async function fixture() {
75
75
  return { root, client, transport };
76
76
  }
77
77
 
78
- test('R36 hermetic autonomous fixture exposes the exact 49-tool no-host catalog', async () => {
78
+ test('R44 hermetic autonomous fixture exposes the exact 50-tool no-host catalog', async () => {
79
79
  const f = await fixture();
80
80
  try {
81
81
  await f.client.connect(f.transport);
@@ -92,12 +92,12 @@ test('R36 closure fails when a production route is lost or advertised tool is ab
92
92
  }), /compact tool/i);
93
93
  });
94
94
 
95
- test('R36 compact surface hard-fails above the observed 42-tool ceiling', () => {
95
+ test('R44 compact surface hard-fails above the observed 43-tool ceiling', () => {
96
96
  const ledger=createCapabilityLedger([entry({})]);
97
- const tooMany=Array.from({length:43},(_,i)=>i===0?'coding_inspect':`extra_${i}`);
97
+ const tooMany=Array.from({length:44},(_,i)=>i===0?'coding_inspect':`extra_${i}`);
98
98
  assert.throws(() => assertCapabilityClosure({
99
99
  ledger,
100
100
  fullToolSnapshot:snapshot(['coding_search']),
101
101
  compactToolSnapshot:snapshot(tooMany)
102
- }), /42/i);
102
+ }), /43/i);
103
103
  });
@@ -21,7 +21,7 @@ test('npx CLI reports the canonical package version through --version, -v and ve
21
21
  for(const arg of ['--version','-v','version']){
22
22
  const result=spawnSync(process.execPath,[cliPath,arg],{cwd:root,encoding:'utf8',windowsHide:true});
23
23
  assert.equal(result.status,0,result.stderr);
24
- assert.equal(result.stdout.trim(),'0.14.0');
24
+ assert.equal(result.stdout.trim(),'0.15.0');
25
25
  }
26
26
  });
27
27
 
@@ -0,0 +1,115 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { EventEmitter } from 'node:events';
4
+ import { mkdtemp, rm } from 'node:fs/promises';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import { createCodexAppServerR44 } from '../src/codex-app-server-r44.mjs';
8
+
9
+ async function waitFor(predicate,timeoutMs=2000){
10
+ const deadline=Date.now()+timeoutMs;
11
+ while(Date.now()<deadline){const value=predicate();if(value)return value;await new Promise(resolve=>setTimeout(resolve,5));}
12
+ throw new Error('waitFor timeout');
13
+ }
14
+ class FakeTransport extends EventEmitter{
15
+ constructor(){super();this.sent=[];}
16
+ async start(){}
17
+ async send(value){this.sent.push(structuredClone(value));}
18
+ async stop(){}
19
+ request(method){return this.sent.findLast?.(x=>x.method===method&&Object.hasOwn(x,'id'))??[...this.sent].reverse().find(x=>x.method===method&&Object.hasOwn(x,'id'));}
20
+ reply(id,result){this.emit('message',{id,result});}
21
+ notify(method,params){this.emit('message',{method,params});}
22
+ serverRequest(id,method,params){this.emit('message',{id,method,params});}
23
+ }
24
+
25
+ test('R44 Codex adapter initializes no-jsonrpc wire and starts safe persistent planner thread',async()=>{
26
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-codex-'));const transport=new FakeTransport();
27
+ try{
28
+ const codex=await createCodexAppServerR44({stateDir:root,transportFactory:async()=>transport,
29
+ model:'gpt-test',cwd:root,clientVersion:'0.15.0'});
30
+ const start=codex.start();
31
+ const init=await waitFor(()=>transport.request('initialize'));
32
+ assert.equal('jsonrpc' in init,false);
33
+ assert.deepEqual(init.params.clientInfo,{name:'deadbyte-mcp',version:'0.15.0'});
34
+ assert.equal(init.params.capabilities.experimentalApi,false);
35
+ transport.reply(init.id,{userAgent:'codex-test'});
36
+ await start;
37
+ const initialized=transport.sent.find(x=>x.method==='initialized'&&!Object.hasOwn(x,'id'));
38
+ assert.ok(initialized);assert.equal('jsonrpc' in initialized,false);
39
+
40
+ const ensure=codex.ensureThread({logicalId:'planner'});
41
+ const req=await waitFor(()=>transport.request('thread/start'));
42
+ assert.equal(req.params.model,'gpt-test');assert.equal(req.params.cwd,root);
43
+ assert.equal(req.params.approvalPolicy,'never');assert.equal(req.params.sandbox,'read-only');
44
+ assert.equal(req.params.ephemeral,false);assert.equal(req.params.approvalsReviewer,'user');
45
+ transport.reply(req.id,{thread:{id:'thread-1'},model:'gpt-test',modelProvider:'openai',cwd:root,
46
+ approvalPolicy:'never',approvalsReviewer:'user',sandbox:{type:'readOnly'}});
47
+ const thread=await ensure;assert.equal(thread.thread_id,'thread-1');assert.equal(thread.resumed,false);
48
+ await codex.close();
49
+ }finally{await rm(root,{recursive:true,force:true});}
50
+ });
51
+
52
+ test('R44 Codex planner routes final answer by turn id and declines provider approvals',async()=>{
53
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-codex-turn-'));const transport=new FakeTransport();
54
+ try{
55
+ const codex=await createCodexAppServerR44({stateDir:root,transportFactory:async()=>transport,model:'gpt-test',cwd:root});
56
+ const start=codex.start();const init=await waitFor(()=>transport.request('initialize'));transport.reply(init.id,{});await start;
57
+ const ensure=codex.ensureThread({logicalId:'planner'});const ts=await waitFor(()=>transport.request('thread/start'));
58
+ transport.reply(ts.id,{thread:{id:'thread-1'},model:'gpt-test'});await ensure;
59
+
60
+ const planned=codex.runPlanner({logicalId:'planner',prompt:'Return JSON',timeoutMs:2000});
61
+ const turn=await waitFor(()=>transport.request('turn/start'));
62
+ assert.equal(turn.params.threadId,'thread-1');assert.deepEqual(turn.params.input,[{type:'text',text:'Return JSON'}]);
63
+ assert.equal(turn.params.approvalPolicy,'never');
64
+ transport.reply(turn.id,{turn:{id:'turn-1',status:'inProgress'}});
65
+ transport.notify('item/completed',{threadId:'thread-1',turnId:'other',item:{id:'x',type:'agentMessage',text:'wrong'}});
66
+ transport.notify('item/completed',{threadId:'thread-1',turnId:'turn-1',item:{id:'m1',type:'agentMessage',text:'working',phase:'commentary'}});
67
+ transport.notify('item/completed',{threadId:'thread-1',turnId:'turn-1',item:{id:'m2',type:'agentMessage',text:'{"schema":"ok"}',phase:'final_answer'}});
68
+ transport.notify('turn/completed',{threadId:'thread-1',turn:{id:'turn-1',status:'completed'}});
69
+ const result=await planned;
70
+ assert.equal(result.backend,'codex_app_server');assert.equal(result.raw_text,'{"schema":"ok"}');assert.equal(result.response_id,'turn-1');
71
+
72
+ transport.serverRequest('approval-1','item/commandExecution/requestApproval',{threadId:'thread-1',turnId:'turn-1'});
73
+ const denial=await waitFor(()=>transport.sent.find(x=>x.id==='approval-1'));
74
+ assert.deepEqual(denial.result,{decision:'decline'});assert.equal(denial.error,undefined);
75
+ await codex.close();
76
+ }finally{await rm(root,{recursive:true,force:true});}
77
+ });
78
+
79
+ test('R44 Codex steer and interrupt bind exact active turn identity',async()=>{
80
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-codex-control-'));const transport=new FakeTransport();
81
+ try{
82
+ const codex=await createCodexAppServerR44({stateDir:root,transportFactory:async()=>transport,model:'gpt-test',cwd:root});
83
+ const start=codex.start();const init=await waitFor(()=>transport.request('initialize'));transport.reply(init.id,{});await start;
84
+ const steerP=codex.steer({threadId:'thread-1',turnId:'turn-1',text:'new direction',timeoutMs:2000});
85
+ const steer=await waitFor(()=>transport.request('turn/steer'));
86
+ assert.deepEqual(steer.params,{threadId:'thread-1',expectedTurnId:'turn-1',input:[{type:'text',text:'new direction'}]});
87
+ transport.reply(steer.id,{turnId:'turn-1'});await steerP;
88
+ const interruptP=codex.interrupt({threadId:'thread-1',turnId:'turn-1',timeoutMs:2000});
89
+ const interrupt=await waitFor(()=>transport.request('turn/interrupt'));
90
+ assert.deepEqual(interrupt.params,{threadId:'thread-1',turnId:'turn-1'});
91
+ transport.reply(interrupt.id,{});await interruptP;
92
+ await codex.close();
93
+ }finally{await rm(root,{recursive:true,force:true});}
94
+ });
95
+
96
+ test('R44 Codex adapter resumes durable thread after backend process recreation',async()=>{
97
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-codex-resume-'));
98
+ try{
99
+ const firstTransport=new FakeTransport();
100
+ const first=await createCodexAppServerR44({stateDir:root,transportFactory:async()=>firstTransport,model:'gpt-test',cwd:root});
101
+ const s1=first.start();const i1=await waitFor(()=>firstTransport.request('initialize'));firstTransport.reply(i1.id,{});await s1;
102
+ const e1=first.ensureThread({logicalId:'planner'});const t1=await waitFor(()=>firstTransport.request('thread/start'));
103
+ firstTransport.reply(t1.id,{thread:{id:'thread-persist'},model:'gpt-test'});await e1;await first.close();
104
+
105
+ const secondTransport=new FakeTransport();
106
+ const second=await createCodexAppServerR44({stateDir:root,transportFactory:async()=>secondTransport,model:'gpt-test',cwd:root});
107
+ const s2=second.start();const i2=await waitFor(()=>secondTransport.request('initialize'));secondTransport.reply(i2.id,{});await s2;
108
+ const e2=second.ensureThread({logicalId:'planner'});const resume=await waitFor(()=>secondTransport.request('thread/resume'));
109
+ assert.equal(resume.params.threadId,'thread-persist');assert.equal(resume.params.approvalPolicy,'never');assert.equal(resume.params.sandbox,'read-only');
110
+ secondTransport.reply(resume.id,{thread:{id:'thread-persist'},model:'gpt-test'});
111
+ const result=await e2;assert.equal(result.thread_id,'thread-persist');assert.equal(result.resumed,true);
112
+ assert.equal(secondTransport.sent.some(x=>x.method==='thread/start'),false);
113
+ await second.close();
114
+ }finally{await rm(root,{recursive:true,force:true});}
115
+ });
@@ -0,0 +1,87 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { createHash, randomBytes } from 'node:crypto';
4
+ import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import { generateTrustKeypair } from '../src/trust.mjs';
8
+ import { createCodingRuntime, loadCodingPolicy } from '../src/coding-plane.mjs';
9
+ import { loadAutonomousPolicy } from '../src/autonomous-policy.mjs';
10
+ import { createNativeDiffRuntime } from '../src/native-diff.mjs';
11
+ import { createAutonomousRuntime } from '../src/autonomous-runtime.mjs';
12
+ import { providerCircuitId } from '../src/provider-circuit.mjs';
13
+
14
+ const sha=b=>createHash('sha256').update(b).digest('hex');
15
+ const nonce=()=>randomBytes(32).toString('hex');
16
+
17
+ async function fixture(){
18
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-codex-auto-'));
19
+ const project=path.join(root,'project'),trust=path.join(root,'trust');
20
+ await mkdir(project);await mkdir(trust);
21
+ const keys=await generateTrustKeypair({privateKeyPath:path.join(trust,'private.pem'),publicKeyPath:path.join(trust,'public.pem')});
22
+ const codingPath=path.join(root,'coding.json');
23
+ await writeFile(codingPath,JSON.stringify({schema:'deadbyte.coding-policy.v1',enabled:true,
24
+ lease_file:path.join(root,'coding-grant.json'),evidence_dir:path.join(root,'coding-evidence'),
25
+ roots:{project:{path:project,read:true,write:true,exec:false,deny_paths:[]}},commands:{}},null,2));
26
+ const codingPolicy=await loadCodingPolicy(codingPath);
27
+ const nodeSha=sha(await readFile(process.execPath));
28
+ const autonomousPath=path.join(root,'autonomous.json');
29
+ await writeFile(autonomousPath,JSON.stringify({schema:'deadbyte.autonomous-policy.v1',enabled:true,
30
+ lease_file:path.join(root,'autonomous-grant.json'),evidence_dir:path.join(root,'autonomous-evidence'),
31
+ coding_policy_sha256:codingPolicy.policy_sha256,root_ids:['project'],
32
+ planner:{backend:'codex_app_server',model:'gpt-test',root_id:'project',executable:process.execPath,
33
+ executable_sha256:nodeSha,args:['app-server'],max_tokens:4096,timeout_ms:30000,
34
+ circuit:{failure_threshold:2,open_ms:1000,half_open_probe_limit:1}},
35
+ profiles:{},limits:{max_iterations:8,max_mutations:4,max_wall_ms:600000,max_context_bytes:262144,
36
+ max_diff_bytes:262144,max_files_per_diff:4,max_stalled_cycles:2,max_post_replan_stalls:2,max_replans:4}},null,2));
37
+ const policy=await loadAutonomousPolicy(autonomousPath,codingPolicy);
38
+ await writeFile(codingPolicy.lease_file,JSON.stringify({schema:'deadbyte.coding-grant.v2',policy_sha256:codingPolicy.policy_sha256,
39
+ instance_nonce:nonce(),root_ids:['project'],armed_at_utc:new Date().toISOString()}));
40
+ await writeFile(policy.lease_file,JSON.stringify({schema:'deadbyte.autonomous-grant.v2',policy_sha256:policy.policy_sha256,
41
+ instance_nonce:nonce(),root_ids:['project'],armed_at_utc:new Date().toISOString()}));
42
+ const codingRuntime=createCodingRuntime({policy:codingPolicy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
43
+ const nativeDiffRuntime=createNativeDiffRuntime({policy,codingRuntime,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
44
+ return {root,project,keys,codingPolicy,policy,codingRuntime,nativeDiffRuntime,cleanup:()=>rm(root,{recursive:true,force:true})};
45
+ }
46
+
47
+ test('R44 policy binds Codex executable, model, root and circuit identity',async()=>{
48
+ const f=await fixture();
49
+ try{
50
+ assert.equal(f.policy.planner.backend,'codex_app_server');
51
+ assert.equal(f.policy.planner.root_id,'project');
52
+ assert.equal(f.policy.planner.executable,process.execPath);
53
+ assert.match(f.policy.planner.executable_sha256,/^[0-9a-f]{64}$/);
54
+ assert.deepEqual(f.policy.planner.args,['app-server']);
55
+ assert.match(providerCircuitId(f.policy.planner),/^codex_app_server:[0-9a-f]{64}$/);
56
+ }finally{await f.cleanup();}
57
+ });
58
+
59
+ test('R44 autonomous runtime consumes injected Codex planner and does not ask client sampling',async()=>{
60
+ const f=await fixture();const calls=[];
61
+ try{
62
+ const plannerProviderRuntime={runPlanner:async({logicalId,prompt,timeoutMs})=>{
63
+ calls.push({logicalId,prompt,timeoutMs});
64
+ return {backend:'codex_app_server',model:'gpt-test',raw_text:JSON.stringify({
65
+ schema:'deadbyte.autonomous-decision.v2',kind:'plan_create',title:'Provider path',
66
+ reason:'prove Codex planner integration',steps:[{step_id:'prove',intent:'observe provider call'}]
67
+ }),response_id:'turn-test-1'};
68
+ }};
69
+ const runtime=createAutonomousRuntime({policy:f.policy,codingRuntime:f.codingRuntime,nativeDiffRuntime:f.nativeDiffRuntime,
70
+ plannerProviderRuntime,signingKeyPath:f.keys.privateKeyPath,verifyKeyPath:f.keys.publicKeyPath});
71
+ const submitted=await runtime.submit({submissionId:'codex-plan',rootId:'project',title:'codex',goal:'prove provider path',acceptance:'pause'});
72
+ const result=await runtime.run({goalId:submitted.goal_id,maxCycles:1});
73
+ assert.notEqual(result.status,'input_required');
74
+ assert.equal(result.status,'yielded');
75
+ assert.equal(calls.length,1);
76
+ assert.equal(calls[0].logicalId,`goal-${submitted.goal_id}`);
77
+ assert.match(calls[0].prompt,/DEADBYTE/);
78
+ }finally{await f.cleanup();}
79
+ });
80
+
81
+ test('R44 autonomous Codex policy fails closed without injected provider runtime',async()=>{
82
+ const f=await fixture();
83
+ try{
84
+ assert.throws(()=>createAutonomousRuntime({policy:f.policy,codingRuntime:f.codingRuntime,nativeDiffRuntime:f.nativeDiffRuntime,
85
+ signingKeyPath:f.keys.privateKeyPath,verifyKeyPath:f.keys.publicKeyPath}),/codex.*provider/i);
86
+ }finally{await f.cleanup();}
87
+ });
@@ -19,7 +19,7 @@ const codingTools = [
19
19
  'coding_task_events', 'coding_task_run', 'coding_task_status', 'coding_task_submit',
20
20
  'coding_task_verify', 'coding_text_replace', 'coding_tree'
21
21
  ];
22
- const baseTools = ['contained_transform', 'evidence_verify', 'file_read', 'proof_execute', 'workspace_open'];
22
+ const baseTools = ['contained_transform', 'evidence_verify', 'file_read', 'proof_execute', 'skill_catalog', 'workspace_open'];
23
23
  const agentTools = [
24
24
  'agent_task_approve', 'agent_task_cancel', 'agent_task_events', 'agent_task_run',
25
25
  'agent_task_status', 'agent_task_submit', 'agent_task_verify'
@@ -69,7 +69,7 @@ async function fixture() {
69
69
  return { root, client, transport };
70
70
  }
71
71
 
72
- test('R36 hermetic coding fixture exposes the exact 37-tool no-host catalog', async () => {
72
+ test('R44 hermetic coding fixture exposes the exact 38-tool no-host catalog', async () => {
73
73
  const f = await fixture();
74
74
  try {
75
75
  await f.client.connect(f.transport);
@@ -5,7 +5,7 @@ import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
5
5
  import os from 'node:os';
6
6
  import path from 'node:path';
7
7
  import { generateTrustKeypair } from '../src/trust.mjs';
8
- import { createCodingRuntime, loadCodingPolicy } from '../src/coding-plane.mjs';
8
+ import { assertCodingArmed, createCodingRuntime, loadCodingPolicy, recheckCodingAuthority } from '../src/coding-plane.mjs';
9
9
  import { createCodingTaskRuntime } from '../src/coding-task-runtime.mjs';
10
10
 
11
11
  function sha256Bytes(bytes) { return createHash('sha256').update(bytes).digest('hex'); }
@@ -78,6 +78,41 @@ test('coding plane rejects mutation when lease is absent or precondition is stal
78
78
  await assert.rejects(() => f.runtime.fileWrite({ rootId: 'project', relativePath: 'x.txt', content: 'x', expectedSha256: 'absent' }), /coding plane is disarmed/);
79
79
  } finally { await rm(f.root, { recursive: true, force: true }); }
80
80
  });
81
+
82
+ test('coding authority recheck rejects lease generation or expiry drift before commit', async () => {
83
+ const f = await fixture();
84
+ try {
85
+ const initial = await assertCodingArmed(f.policy, 'project');
86
+ const lease = JSON.parse(await readFile(f.policy.lease_file, 'utf8'));
87
+ await writeFile(f.policy.lease_file, JSON.stringify({ ...lease, instance_nonce: 'b'.repeat(64) }));
88
+ await assert.rejects(() => recheckCodingAuthority(f.policy, initial, 'project'), /coding authority changed during operation/);
89
+ await writeFile(f.policy.lease_file, JSON.stringify({
90
+ ...lease,
91
+ expires_at_utc: new Date(Date.parse(lease.expires_at_utc) + 60000).toISOString()
92
+ }));
93
+ await assert.rejects(() => recheckCodingAuthority(f.policy, initial, 'project'), /coding authority changed during operation/);
94
+ } finally { await rm(f.root, { recursive: true, force: true }); }
95
+ });
96
+ test('coding execution deadline bounds runtime without changing the signed request identity', async () => {
97
+ const f = await fixture();
98
+ try {
99
+ const script = path.join(f.project, 'deadline.mjs');
100
+ await writeFile(script, "await new Promise(resolve => setTimeout(resolve, 10000));\n");
101
+ const scriptHash = await sha256File(script);
102
+ const args = { rootId:'project', commandId:'node_script', scriptPath:'deadline.mjs', scriptSha256:scriptHash,
103
+ timeoutMs:20000, executionDeadlineUtc:new Date(Date.now()+3000).toISOString() };
104
+ const expectedRequest = f.runtime.requestSha('coding_command_run', {
105
+ root_id:'project', command_id:'node_script', script_path:'deadline.mjs', script_sha256:scriptHash,
106
+ args:[], cwd:'.', timeout_ms:20000
107
+ });
108
+ const result = await f.runtime.commandRun(args);
109
+ assert.equal(result.status, 'observed_failure');
110
+ assert.equal(result.timed_out, true);
111
+ assert.ok(result.timeout_ms >= 500 && result.timeout_ms <= 3000);
112
+ assert.equal(result.request_sha256, expectedRequest);
113
+ } finally { await rm(f.root, { recursive:true, force:true }); }
114
+ });
115
+
81
116
  test('coding command and benchmark produce signed observations', async () => {
82
117
  const f = await fixture();
83
118
  try {
@@ -10,7 +10,7 @@ const repoRoot=path.resolve(here,'..');
10
10
  const serverPath=path.join(repoRoot,'src','mcp-server.mjs');
11
11
  const gatewayNames=['capability_manifest','coding_inspect','desktop_control','machine_fs','machine_observe','process_session'];
12
12
 
13
- test('R36 real compact stdio catalog stays <=42 and exposes all gateways',async()=>{
13
+ test('R44 real compact stdio catalog stays <=43 and exposes all gateways plus skill_catalog',async()=>{
14
14
  const client=new Client({name:'deadbyte-r36-compact-test',version:'0.10.0-dev'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
15
15
  const env={
16
16
  ...getDefaultEnvironment(),
@@ -24,8 +24,8 @@ test('R36 real compact stdio catalog stays <=42 and exposes all gateways',async(
24
24
  await client.connect(transport);
25
25
  const listed=await client.listTools();
26
26
  const names=listed.tools.map(tool=>tool.name).sort();
27
- assert.ok(names.length<=42,`compact tool count ${names.length} exceeds 42`);
28
- for(const name of gatewayNames) assert.ok(names.includes(name),`missing ${name}`);
27
+ assert.ok(names.length<=43,`compact tool count ${names.length} exceeds 43`);
28
+ for(const name of [...gatewayNames,'skill_catalog']) assert.ok(names.includes(name),`missing ${name}`);
29
29
  for(const legacy of ['workspace_open','file_read','proof_execute','contained_transform']) assert.equal(names.includes(legacy),false,`legacy full-only tool leaked: ${legacy}`);
30
30
  const manifest=await client.callTool({name:'capability_manifest',arguments:{}});
31
31
  assert.equal(manifest.isError,false);
@@ -10,7 +10,9 @@ import { createR34Fixture } from './helpers/autonomous-r34-fixture.mjs';
10
10
 
11
11
  const here=path.dirname(fileURLToPath(import.meta.url));
12
12
  const repoRoot=path.resolve(here,'..');
13
- const serverPath=path.join(repoRoot,'src','mcp-server.mjs');
13
+ const parent=JSON.parse(await readFile(path.join(repoRoot,'R44-PARENT.json'),'utf8'));
14
+ const parentRoot=parent.parent_release_root;
15
+ const serverPath=path.join(parentRoot,'src','mcp-server.mjs');
14
16
  const desktopPolicyPath=path.join(repoRoot,'controller','deadbyte-desktop-policy.json');
15
17
  const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
16
18
  const absent=async p=>{try{await access(p);return false;}catch(e){if(e?.code==='ENOENT')return true;throw e;}};
@@ -70,7 +72,7 @@ test('R40 real max-runtime compact stdio catalog is exactly 42 while all authori
70
72
  DEADBYTE_AUTONOMOUS_POLICY:path.join(f.root,'autonomous-policy.json'),
71
73
  DEADBYTE_AUTONOMOUS_SUPERVISOR_MODE:'external'
72
74
  };
73
- const transport=new StdioClientTransport({command:process.execPath,args:[serverPath],cwd:repoRoot,env,stderr:'pipe'});
75
+ const transport=new StdioClientTransport({command:process.execPath,args:[serverPath],cwd:parentRoot,env,stderr:'pipe'});
74
76
  try{
75
77
  await client.connect(transport);
76
78
  const listed=await client.listTools();
@@ -0,0 +1,97 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { createHash } from 'node:crypto';
4
+ import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { Client } from '@modelcontextprotocol/client';
8
+ import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/client/stdio';
9
+ import { createR34Fixture } from './helpers/autonomous-r34-fixture.mjs';
10
+
11
+ const here=path.dirname(fileURLToPath(import.meta.url));
12
+ const repoRoot=path.resolve(here,'..');
13
+ const serverPath=path.join(repoRoot,'src','mcp-server.mjs');
14
+ const desktopPolicyPath=path.join(repoRoot,'controller','deadbyte-desktop-policy.json');
15
+ const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
16
+ const absent=async p=>{try{await access(p);return false;}catch(e){if(e?.code==='ENOENT')return true;throw e;}};
17
+
18
+ test('R44 real max-runtime compact stdio catalog is exactly 43 with verified skill_catalog while all authority remains disarmed',async()=>{
19
+ const f=await createR34Fixture({authority:'disarmed'});
20
+ const processPolicyPath=path.join(f.root,'process-policy.json');
21
+ const processGrant=path.join(f.root,'process-grant.json');
22
+ const processState=path.join(f.root,'process-state');
23
+ const hostPolicyPath=path.join(f.root,'host-policy.json');
24
+ const hostArm=path.join(f.root,'host-arm.json');
25
+ const nodeSha=sha(await readFile(process.execPath));
26
+ await mkdir(processState,{recursive:true});
27
+ await writeFile(processPolicyPath,JSON.stringify({
28
+ schema:'deadbyte.process-policy.v1',enabled:true,grant_file:processGrant,state_dir:processState,
29
+ native_host:{path:process.execPath,sha256:nodeSha},
30
+ roots:{core:{path:f.project,deny_paths:[]}},
31
+ profiles:{node:{executable:process.execPath,executable_sha256:nodeSha,root_ids:['core'],
32
+ prefix_args:[],transport:'pipe',max_args:8,max_arg_bytes:1024,max_sessions:2,
33
+ max_input_bytes:65536,max_retained_output_bytes:262144,env_allow:['PATH','TEMP','TMP'],
34
+ input_actions:{ping:'PING\r\n'}}}
35
+ },null,2));
36
+ await writeFile(hostPolicyPath,JSON.stringify({
37
+ schema:'deadbyte.host-policy.v1',enabled:true,arm_file:hostArm,max_read_bytes:262144,max_write_bytes:1048576,
38
+ roots:{core:{path:f.project,read:true,write:true,exec:true}},
39
+ commands:{node_script:{kind:'script',executable:process.execPath,executable_sha256:nodeSha,
40
+ root_ids:['core'],prefix_args:[],extensions:['.mjs'],timeout_ms:5000,max_output_bytes:65536}}
41
+ },null,2));
42
+
43
+ const desktopRaw=JSON.parse(await readFile(desktopPolicyPath,'utf8'));
44
+ const desktopGrant=desktopRaw.grant_file;
45
+ const codingLease=f.codingPolicy.lease_file;
46
+ const autonomousLease=f.policy.lease_file;
47
+ for(const q of [processGrant,hostArm,desktopGrant,codingLease,autonomousLease]) assert.equal(await absent(q),true,'authority unexpectedly armed before start: '+q);
48
+
49
+ const client=new Client({name:'deadbyte-r40-max-catalog',version:'0.12.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
50
+ const env={
51
+ ...getDefaultEnvironment(),
52
+ DEADBYTE_CAPABILITY_PROFILE:'compact',
53
+ DEADBYTE_AUDIT_RUNTIME:'0',
54
+ DEADBYTE_STATE_ROOT:path.join(f.root,'state'),
55
+ DEADBYTE_WORKSPACE_ROOT:f.root,
56
+ DEADBYTE_SIGNING_KEY:f.keys.privateKeyPath,
57
+ DEADBYTE_VERIFY_KEY:f.keys.publicKeyPath,
58
+ DEADBYTE_PRIVILEGED_POLICY:hostPolicyPath,
59
+ DEADBYTE_MACHINE_RUNTIME:'1',
60
+ DEADBYTE_AGENT_RUNTIME:'0',
61
+ DEADBYTE_PROCESS_RUNTIME:'1',
62
+ DEADBYTE_PROCESS_POLICY:processPolicyPath,
63
+ DEADBYTE_CODING_RUNTIME:'1',
64
+ DEADBYTE_CODING_POLICY:path.join(f.root,'coding-policy.json'),
65
+ DEADBYTE_MACHINE_FS_RUNTIME:'1',
66
+ DEADBYTE_OBSERVATION_RUNTIME:'1',
67
+ DEADBYTE_DESKTOP_RUNTIME:'1',
68
+ DEADBYTE_DESKTOP_POLICY:desktopPolicyPath,
69
+ DEADBYTE_AUTONOMOUS_RUNTIME:'1',
70
+ DEADBYTE_AUTONOMOUS_POLICY:path.join(f.root,'autonomous-policy.json'),
71
+ DEADBYTE_AUTONOMOUS_SUPERVISOR_MODE:'external'
72
+ };
73
+ const transport=new StdioClientTransport({command:process.execPath,args:[serverPath],cwd:repoRoot,env,stderr:'pipe'});
74
+ try{
75
+ await client.connect(transport);
76
+ const listed=await client.listTools();
77
+ const names=listed.tools.map(t=>t.name).sort();
78
+ assert.equal(names.length,43,'expected exact compact max catalog 43, got '+names.length+': '+names.join(','));
79
+ for(const name of ['capability_manifest','desktop_control','coding_inspect','machine_fs','machine_observe','process_session',
80
+ 'host_file_read','host_file_write','host_exec','skill_catalog']) assert.ok(names.includes(name),'missing '+name);
81
+ const manifest=await client.callTool({name:'capability_manifest',arguments:{}});
82
+ assert.equal(manifest.isError,false);
83
+ const m=manifest.structuredContent.result;
84
+ assert.equal(m.schema,'deadbyte.capability-manifest.v2');
85
+ assert.equal(m.profile,'compact');
86
+ assert.equal(m.tool_count,43);
87
+ assert.deepEqual(m.tool_catalog,names);
88
+ assert.match(m.catalog_sha256,/^[0-9a-f]{64}$/);
89
+ const skill=m.capabilities.find(item=>item.id==='skill.catalog');
90
+ assert.ok(skill);assert.equal(skill.authority,'none');assert.equal(skill.mutating,false);
91
+ console.log(JSON.stringify({tool_count:m.tool_count,catalog_sha256:m.catalog_sha256,ledger_sha256:m.ledger_sha256,desktop:true,skill:true,authority:'DISARMED'}));
92
+ } finally {
93
+ await client.close().catch(()=>{});
94
+ for(const q of [processGrant,hostArm,desktopGrant,codingLease,autonomousLease]) assert.equal(await absent(q),true,'authority file appeared during catalog probe: '+q);
95
+ await f.cleanup();
96
+ }
97
+ });
@@ -105,7 +105,7 @@ test('proof execution creates deterministic output and signed verifiable receipt
105
105
  assert.equal(result.receipt.request.schema, 'deadbyte.request.v1');
106
106
  assert.match(result.receipt.request.nonce, /^[0-9a-f]{64}$/);
107
107
  assert.match(result.receipt.request.sha256, /^[0-9a-f]{64}$/);
108
- assert.equal(result.receipt.runtime.package_version, '0.14.0');
108
+ assert.equal(result.receipt.runtime.package_version, '0.15.0');
109
109
  assert.match(result.receipt.runtime.mcp_ingress_adapter, /^(?:none|tunnel-stdio-probe-compat-v1)$/);
110
110
  assert.match(result.receipt.runtime.mcp_ingress_adapter_sha256, /^[0-9a-f]{64}$/);
111
111
  assert.match(result.receipt.runtime.mcp_ingress_mode_source_sha256, /^[0-9a-f]{64}$/);