deadbyte-mcp 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +11 -18
- package/CONTEXT.md +3 -3
- package/MANIFEST.SHA256 +104 -53
- package/R43-PARENT.json +20 -0
- package/R44-DONORS.json +99 -0
- package/R44-PARENT.json +18 -0
- package/README.txt +7 -2
- package/controller/README.TXT +4 -5
- package/controller/deadbyte-controller.ps1 +1 -1
- package/docs/ARCHITECTURE.md +8 -1
- package/docs/R44-ARCHITECTURE.md +37 -0
- package/package.json +13 -8
- package/scripts/deferred-slot-operation-r43.mjs +126 -0
- package/scripts/deferred-slot-operation-r44.mjs +126 -0
- package/scripts/final-closure-r43.mjs +115 -0
- package/scripts/final-closure-r44.mjs +121 -0
- package/scripts/final-closure-verify-r43.mjs +213 -0
- package/scripts/final-closure-verify-r44.mjs +220 -0
- package/scripts/finalize-r43.mjs +217 -0
- package/scripts/finalize-r44.mjs +217 -0
- package/scripts/full-regression-r43.mjs +20 -0
- package/scripts/full-regression-r44.mjs +20 -0
- package/scripts/gate-windows-r43.ps1 +36 -0
- package/scripts/gate-windows-r44.ps1 +36 -0
- package/scripts/release-parity-r43.mjs +218 -0
- package/scripts/release-parity-r44.mjs +242 -0
- package/scripts/release-parity-tests-r43.ps1 +8 -0
- package/scripts/release-parity-tests-r44.ps1 +8 -0
- package/scripts/verify-r43-parent.mjs +126 -0
- package/scripts/verify-r44-parent.mjs +142 -0
- package/scripts/windows-gate-evidence-r43.mjs +55 -0
- package/scripts/windows-gate-evidence-r44.mjs +55 -0
- package/src/agent-backend-r44.mjs +254 -0
- package/src/autonomous-output-contracts.mjs +1 -1
- package/src/autonomous-policy.mjs +41 -4
- package/src/autonomous-runtime.mjs +25 -12
- package/src/autonomous-supervisor-host.mjs +12 -2
- package/src/capability-ledger.mjs +2 -1
- package/src/codex-app-server-r44.mjs +154 -0
- package/src/coding-plane.mjs +66 -19
- package/src/durable-job-r44.mjs +145 -0
- package/src/external-job-provider-r44.mjs +126 -0
- package/src/host-plane.mjs +27 -6
- package/src/job-supervisor-host-r44.mjs +112 -0
- package/src/machine-fs-smoke-client.mjs +1 -1
- package/src/machine-fs.mjs +21 -10
- package/src/mcp-host-smoke-client.mjs +1 -1
- package/src/mcp-server.mjs +20 -3
- package/src/mcp-smoke-client.mjs +1 -1
- package/src/native-diff-v2.mjs +3 -3
- package/src/native-diff.mjs +3 -2
- package/src/observation-smoke-client.mjs +1 -1
- package/src/operator-surface.mjs +2 -2
- package/src/process-policy.mjs +19 -0
- package/src/process-runtime.mjs +5 -2
- package/src/process-smoke-client.mjs +2 -2
- package/src/provider-circuit.mjs +8 -2
- package/src/r44-compact-disarmed-probe.mjs +74 -0
- package/src/r44-full-surface-probe.mjs +34 -0
- package/src/remote-device-r44.mjs +205 -0
- package/src/remote-mcp-autonomous-smoke-client.mjs +4 -4
- package/src/remote-mcp-r44-parity-client.mjs +203 -0
- package/src/remote-mcp-smoke-client.mjs +7 -4
- package/src/runtime-update.mjs +64 -31
- package/src/skill-mcp-tools-r44.mjs +37 -0
- package/src/skill-plane-r44.mjs +203 -0
- package/src/tunnel-stdio-compat-smoke.mjs +1 -1
- package/src/version.mjs +1 -1
- package/test/agent-backend-r44.test.mjs +115 -0
- package/test/agent-runtime.test.mjs +3 -3
- package/test/autonomous-loop.test.mjs +97 -11
- package/test/autonomous-runtime.test.mjs +2 -2
- package/test/capability-ledger-r36.test.mjs +3 -3
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/codex-app-server-r44.test.mjs +115 -0
- package/test/codex-autonomous-r44.test.mjs +87 -0
- package/test/coding-plane.test.mjs +2 -2
- package/test/coding-runtime.test.mjs +36 -1
- package/test/compact-catalog-r36.test.mjs +3 -3
- package/test/compact-max-catalog-r40.test.mjs +4 -2
- package/test/compact-max-catalog-r44.test.mjs +97 -0
- package/test/core.test.mjs +1 -1
- package/test/durable-job-r44.test.mjs +75 -0
- package/test/external-job-provider-r44.test.mjs +46 -0
- package/test/full-max-catalog-r40.test.mjs +5 -3
- package/test/full-max-catalog-r44.test.mjs +91 -0
- package/test/host-plane.test.mjs +15 -1
- package/test/native-diff.test.mjs +28 -0
- package/test/output-schema.test.mjs +2 -2
- package/test/process-policy.test.mjs +22 -1
- package/test/prompts-resources.test.mjs +1 -1
- package/test/r33-closeout-regression.test.mjs +13 -30
- package/test/r33-finalization.test.mjs +7 -7
- package/test/r43-finalization.test.mjs +31 -0
- package/test/r43-finalizer.test.mjs +34 -0
- package/test/r43-parent.test.mjs +29 -0
- package/test/r44-finalization.test.mjs +58 -0
- package/test/r44-finalizer.test.mjs +48 -0
- package/test/r44-parent.test.mjs +25 -0
- package/test/release-version.test.mjs +53 -19
- package/test/remote-device-r44.test.mjs +104 -0
- package/test/runtime-self-update.test.mjs +42 -0
- package/test/skill-mcp-r44.test.mjs +38 -0
- package/test/skill-plane-r44.test.mjs +93 -0
- package/test/standing-grants.test.mjs +5 -1
|
@@ -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
|
|
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
|
-
|
|
190
|
-
|
|
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('
|
|
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('
|
|
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:
|
|
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
|
-
}), /
|
|
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.
|
|
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('
|
|
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('
|
|
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<=
|
|
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
|
|
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:
|
|
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
|
+
});
|
package/test/core.test.mjs
CHANGED
|
@@ -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.
|
|
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}$/);
|