deadbyte-mcp 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +7 -0
- package/AGENTS.md +18 -0
- package/CONTEXT.md +3 -3
- package/MANIFEST.SHA256 +152 -75
- package/R42-PARENT.json +19 -0
- package/R43-PARENT.json +20 -0
- package/R44-DONORS.json +99 -0
- package/R44-PARENT.json +18 -0
- package/README.txt +7 -2
- package/bin/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.obj +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/controller/README.TXT +4 -5
- package/controller/deadbyte-controller.ps1 +1 -1
- package/controller/deadbyte-desktop-policy.json +5 -5
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/ARCHITECTURE.md +8 -1
- package/docs/R44-ARCHITECTURE.md +37 -0
- package/harness/r42-agent-evals.json +48 -0
- package/harness/task-contract.schema.json +33 -0
- package/package.json +15 -8
- package/proof/CONTAINMENT-BUILD.txt +6 -6
- package/scripts/agent-eval-r42.mjs +46 -0
- package/scripts/deferred-slot-operation-r42.mjs +126 -0
- package/scripts/deferred-slot-operation-r43.mjs +126 -0
- package/scripts/deferred-slot-operation-r44.mjs +126 -0
- package/scripts/final-closure-r42.mjs +116 -0
- package/scripts/final-closure-r43.mjs +115 -0
- package/scripts/final-closure-r44.mjs +121 -0
- package/scripts/final-closure-verify-r42.mjs +223 -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-r42.ps1 +54 -0
- package/scripts/gate-windows-r43.ps1 +36 -0
- package/scripts/gate-windows-r44.ps1 +36 -0
- package/scripts/release-parity-r42.mjs +235 -0
- package/scripts/release-parity-r43.mjs +218 -0
- package/scripts/release-parity-r44.mjs +242 -0
- package/scripts/release-parity-tests-r42.ps1 +6 -0
- package/scripts/release-parity-tests-r43.ps1 +8 -0
- package/scripts/release-parity-tests-r44.ps1 +8 -0
- package/scripts/verify-r42-parent.mjs +101 -0
- package/scripts/verify-r43-parent.mjs +126 -0
- package/scripts/verify-r44-parent.mjs +142 -0
- package/scripts/windows-gate-evidence-r42.mjs +64 -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/agent-eval-r42.mjs +73 -0
- package/src/agent-eval-runner-r42.mjs +138 -0
- package/src/agent-governance-r42.mjs +142 -0
- package/src/autonomous-context-engine.mjs +2 -0
- package/src/autonomous-loop-state.mjs +10 -0
- package/src/autonomous-mcp-tools.mjs +21 -6
- package/src/autonomous-output-contracts.mjs +18 -2
- package/src/autonomous-planner-contracts.mjs +3 -0
- package/src/autonomous-planner.mjs +46 -6
- package/src/autonomous-policy.mjs +50 -5
- package/src/autonomous-runtime.mjs +138 -25
- package/src/autonomous-supervisor-host.mjs +12 -2
- package/src/autonomous-supervisor.mjs +1 -1
- 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/evidence-publisher-r42.mjs +76 -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 +10 -4
- 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-eval-r42.test.mjs +39 -0
- package/test/agent-eval-runner-r42.test.mjs +61 -0
- package/test/agent-governance-r42.test.mjs +77 -0
- package/test/agent-runtime.test.mjs +3 -3
- package/test/autonomous-loop.test.mjs +97 -11
- package/test/autonomous-r34-e2e.test.mjs +2 -1
- 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/evidence-publisher-r42.test.mjs +53 -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/human-clarification-r42.test.mjs +53 -0
- 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/provider-responses-r42.test.mjs +90 -0
- package/test/r33-closeout-regression.test.mjs +14 -16
- package/test/r33-finalization.test.mjs +7 -7
- package/test/r42-finalization.test.mjs +38 -0
- package/test/r43-finalization.test.mjs +31 -0
- package/test/r43-finalizer.test.mjs +34 -0
- package/test/r43-parent.test.mjs +29 -0
- package/test/r44-finalization.test.mjs +58 -0
- package/test/r44-finalizer.test.mjs +48 -0
- package/test/r44-parent.test.mjs +25 -0
- package/test/release-version.test.mjs +53 -19
- package/test/remote-device-r44.test.mjs +104 -0
- package/test/runtime-self-update.test.mjs +42 -0
- package/test/skill-mcp-r44.test.mjs +38 -0
- package/test/skill-plane-r44.test.mjs +93 -0
- package/test/standing-grants.test.mjs +5 -1
- package/test/task-contract-r42.test.mjs +41 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { runAgentEvalSuiteR42, readAndVerifyR42EvalEvidence } from '../src/agent-eval-runner-r42.mjs';
|
|
7
|
+
|
|
8
|
+
const H=value=>String(value).repeat(64).slice(0,64);
|
|
9
|
+
const suite={schema:'deadbyte.agent-eval-suite.v1',suite_id:'suite',
|
|
10
|
+
cases:[{schema:'deadbyte.agent-eval-case.v1',case_id:'case-1',task:'Safe action',
|
|
11
|
+
allowed_context:{visible:'YES'},hidden_oracle:{secret:'ORACLE_ONLY',expected:'bounded'},acceptance:['bounded']}]
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
test('R42 model eval keeps hidden oracle out of generator and binds passed evidence to manifest',async()=>{
|
|
15
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r42-eval-'));
|
|
16
|
+
const calls=[];
|
|
17
|
+
try{
|
|
18
|
+
const run=await runAgentEvalSuiteR42({suite,subjectManifestSha256:H('a'),evidenceRoot:root,
|
|
19
|
+
clock:()=>new Date('2026-09-20T02:00:00.000Z'),
|
|
20
|
+
requestModel:async call=>{
|
|
21
|
+
calls.push(call);
|
|
22
|
+
if(call.role==='generator')return {backend:'fake',model:'fake',response_id:'g1',raw_text:JSON.stringify({artifact:{plan:['inspect','verify']}})};
|
|
23
|
+
return {backend:'fake',model:'fake',response_id:'j1',raw_text:JSON.stringify({schema:'deadbyte.agent-eval-judge-result.v1',
|
|
24
|
+
verdict:'pass',advisory:true,findings:[],evidence_refs:[H('1')]})};
|
|
25
|
+
}});
|
|
26
|
+
assert.equal(calls.length,2);
|
|
27
|
+
assert.equal(calls[0].prompt.includes('ORACLE_ONLY'),false);
|
|
28
|
+
assert.equal(calls[1].prompt.includes('ORACLE_ONLY'),true);
|
|
29
|
+
assert.equal(run.payload.status,'passed');
|
|
30
|
+
assert.equal(run.payload.subject_manifest_sha256,H('a'));
|
|
31
|
+
assert.equal(run.publication.status,'created');
|
|
32
|
+
const verified=await readAndVerifyR42EvalEvidence(run.publication.path,{subjectManifestSha256:H('a'),suiteSha256:run.payload.suite_sha256});
|
|
33
|
+
assert.equal(verified.status,'passed');
|
|
34
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('R42 model eval rejects failed judge and publishes no PASS evidence',async()=>{
|
|
38
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r42-eval-fail-'));
|
|
39
|
+
try{
|
|
40
|
+
await assert.rejects(runAgentEvalSuiteR42({suite,subjectManifestSha256:H('b'),evidenceRoot:root,
|
|
41
|
+
requestModel:async call=>call.role==='generator'
|
|
42
|
+
?{raw_text:JSON.stringify({artifact:{plan:['unsafe']}})}
|
|
43
|
+
:{raw_text:JSON.stringify({schema:'deadbyte.agent-eval-judge-result.v1',verdict:'fail',advisory:true,
|
|
44
|
+
findings:[{code:'AUTH',severity:'critical',evidence:'authority widening'}],evidence_refs:[H('2')]})}}),/suite failed/i);
|
|
45
|
+
await assert.rejects(readFile(path.join(root,'agent_eval',`r42-${H('b').slice(0,16)}-`),'utf8'));
|
|
46
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('R42 eval evidence tampering fails independent verification',async()=>{
|
|
50
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r42-eval-tamper-'));
|
|
51
|
+
try{
|
|
52
|
+
const run=await runAgentEvalSuiteR42({suite,subjectManifestSha256:H('c'),evidenceRoot:root,
|
|
53
|
+
requestModel:async call=>call.role==='generator'
|
|
54
|
+
?{raw_text:JSON.stringify({artifact:{plan:['safe']}})}
|
|
55
|
+
:{raw_text:JSON.stringify({schema:'deadbyte.agent-eval-judge-result.v1',verdict:'pass',advisory:true,findings:[],evidence_refs:[H('3')]})}});
|
|
56
|
+
const raw=JSON.parse(await readFile(run.publication.path,'utf8'));
|
|
57
|
+
raw.payload.cases[0].result.verdict='fail';
|
|
58
|
+
await writeFile(run.publication.path,JSON.stringify(raw));
|
|
59
|
+
await assert.rejects(readAndVerifyR42EvalEvidence(run.publication.path,{subjectManifestSha256:H('c')}),/hash mismatch/i);
|
|
60
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
61
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {
|
|
4
|
+
bindApprovalSubject, validateBoundApproval, createTaskDag, invalidateTaskDag,
|
|
5
|
+
createEvidenceClaim, evidenceSatisfies, deriveEffectiveRigor, taskDagFromPlan,
|
|
6
|
+
createDecisionRecord, evaluateDecisionFreshness
|
|
7
|
+
} from '../src/agent-governance-r42.mjs';
|
|
8
|
+
|
|
9
|
+
const H=value=>String(value).repeat(64).slice(0,64);
|
|
10
|
+
|
|
11
|
+
test('R42 approval binds exact artifact, event head, scope and expiry',()=>{
|
|
12
|
+
const subject=bindApprovalSubject({goal_id:H('1'),reviewed_event_seq:9,reviewed_event_sha256:H('2'),
|
|
13
|
+
journal_head_sha256:H('3'),scope:'resume_goal',subject:{diff_id:H('4'),operation_count:2}});
|
|
14
|
+
const approval={schema:'deadbyte.bound-approval.v1',approval_subject_sha256:subject.approval_subject_sha256,
|
|
15
|
+
reviewed_event_seq:9,reviewed_event_sha256:H('2'),journal_head_sha256:H('3'),scope:'resume_goal',
|
|
16
|
+
issued_at_utc:'2026-09-20T01:00:00.000Z',expires_at_utc:'2026-09-20T01:05:00.000Z',provenance:'operator_mcp'};
|
|
17
|
+
assert.equal(validateBoundApproval({approval,subject,now:new Date('2026-09-20T01:01:00Z')}),true);
|
|
18
|
+
assert.throws(()=>validateBoundApproval({approval:{...approval,journal_head_sha256:H('9')},subject,
|
|
19
|
+
now:new Date('2026-09-20T01:01:00Z')}),/journal|subject/i);
|
|
20
|
+
assert.throws(()=>validateBoundApproval({approval,subject:{...subject,subject:{diff_id:H('5'),operation_count:2}},
|
|
21
|
+
now:new Date('2026-09-20T01:01:00Z')}),/subject/i);
|
|
22
|
+
assert.throws(()=>validateBoundApproval({approval,subject,now:new Date('2026-09-20T01:06:00Z')}),/expired/i);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('R42 canonical DAG propagates STALE only through affected descendants',()=>{
|
|
26
|
+
const dag=createTaskDag({run_id:'run-1',goal_id:H('a'),tasks:[
|
|
27
|
+
{task_id:'inspect',depends_on:[],input_artifacts:['source'],output_artifacts:['analysis']},
|
|
28
|
+
{task_id:'patch',depends_on:['inspect'],input_artifacts:['analysis'],output_artifacts:['diff']},
|
|
29
|
+
{task_id:'test',depends_on:['patch'],input_artifacts:['diff'],output_artifacts:['test-result']},
|
|
30
|
+
{task_id:'docs',depends_on:[],input_artifacts:['readme'],output_artifacts:['docs-result']}
|
|
31
|
+
]});
|
|
32
|
+
const next=invalidateTaskDag(dag,{changed_artifact_ids:['analysis']});
|
|
33
|
+
assert.deepEqual(next.tasks.filter(task=>task.state==='STALE').map(task=>task.task_id),['patch','test']);
|
|
34
|
+
assert.equal(next.tasks.find(task=>task.task_id==='inspect').state,'PENDING');
|
|
35
|
+
assert.equal(next.tasks.find(task=>task.task_id==='docs').state,'PENDING');
|
|
36
|
+
assert.throws(()=>createTaskDag({run_id:'x',goal_id:H('a'),tasks:[
|
|
37
|
+
{task_id:'a',depends_on:['b'],input_artifacts:[],output_artifacts:[]},
|
|
38
|
+
{task_id:'b',depends_on:['a'],input_artifacts:[],output_artifacts:[]}
|
|
39
|
+
]}),/cycle/i);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('R42 one canonical DAG is deterministically derived from the authoritative plan',()=>{
|
|
43
|
+
const plan={steps:[{step_id:'inspect',status:'completed'},{step_id:'patch',status:'active'},{step_id:'verify',status:'pending'}]};
|
|
44
|
+
const one=taskDagFromPlan({run_id:H('7'),goal_id:H('7'),plan});
|
|
45
|
+
const two=taskDagFromPlan({run_id:H('7'),goal_id:H('7'),plan});
|
|
46
|
+
assert.equal(one.dag_sha256,two.dag_sha256);
|
|
47
|
+
assert.deepEqual(one.tasks.map(task=>[task.task_id,task.depends_on,task.state]),[
|
|
48
|
+
['inspect',[],'COMPLETE'],['patch',['inspect'],'RUNNING'],['verify',['patch'],'PENDING']
|
|
49
|
+
]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('R42 evidence ladder never lets weak evidence masquerade as runtime proof',()=>{
|
|
53
|
+
const source=createEvidenceClaim({claim_id:'claim-1',statement:'provider shape matches source',level:'source',refs:[H('b')]});
|
|
54
|
+
assert.equal(evidenceSatisfies(source,'source'),true);
|
|
55
|
+
assert.equal(evidenceSatisfies(source,'automated'),false);
|
|
56
|
+
assert.equal(evidenceSatisfies(source,'runtime'),false);
|
|
57
|
+
assert.throws(()=>createEvidenceClaim({claim_id:'claim-2',statement:'bad',level:'runtime',refs:[]}),/reference/i);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('R42 rigor is derived once and cannot downgrade below risk minimum',()=>{
|
|
61
|
+
assert.deepEqual(deriveEffectiveRigor({project_default:'lightweight',risk_level:'high',explicit_override:'structured'}),{
|
|
62
|
+
project_default:'lightweight',risk_minimum:'agentic',explicit_override:'structured',effective_rigor:'agentic'
|
|
63
|
+
});
|
|
64
|
+
assert.equal(deriveEffectiveRigor({project_default:'structured',risk_level:'low',explicit_override:'agentic'}).effective_rigor,'agentic');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('R42 decisions become stale deterministically without deleting history',()=>{
|
|
68
|
+
const record=createDecisionRecord({decision_id:'provider-choice',decision:'Use Responses adapter',reason:'strict bounded JSON',
|
|
69
|
+
evidence_refs:[H('c')],invalidates_if:[
|
|
70
|
+
{kind:'artifact_sha_changed',artifact_id:'provider-contract',expected_sha256:H('d')},
|
|
71
|
+
{kind:'policy_sha_changed',expected_sha256:H('e')}
|
|
72
|
+
]});
|
|
73
|
+
assert.equal(evaluateDecisionFreshness(record,{artifact_sha256:{'provider-contract':H('d')},policy_sha256:H('e')}).state,'CURRENT');
|
|
74
|
+
const stale=evaluateDecisionFreshness(record,{artifact_sha256:{'provider-contract':H('f')},policy_sha256:H('e')});
|
|
75
|
+
assert.equal(stale.state,'STALE');
|
|
76
|
+
assert.deepEqual(stale.invalidated_by,[{kind:'artifact_sha_changed',artifact_id:'provider-contract',expected_sha256:H('d'),observed_sha256:H('f')}]);
|
|
77
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
|
|
4
4
|
import os from 'node:os';
|
|
@@ -20,7 +20,7 @@ const AGENT_TOOLS = [
|
|
|
20
20
|
'agent_task_submit',
|
|
21
21
|
'agent_task_verify'
|
|
22
22
|
];
|
|
23
|
-
const BASE_TOOLS = ['contained_transform', 'evidence_verify', 'file_read', 'proof_execute', 'workspace_open'];
|
|
23
|
+
const BASE_TOOLS = ['contained_transform', 'evidence_verify', 'file_read', 'proof_execute', 'skill_catalog', 'workspace_open'];
|
|
24
24
|
|
|
25
25
|
function structured(result, label) {
|
|
26
26
|
if (result.isError) throw new Error(`${label}: ${JSON.stringify(result.structuredContent ?? result.content)}`);
|
|
@@ -68,7 +68,7 @@ async function cleanup(f, client) {
|
|
|
68
68
|
await rm(f.trust, { recursive: true, force: true }).catch(() => {});
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
test('agent runtime
|
|
71
|
+
test('R44 agent runtime exposes base skill catalog plus durable task tools without host authority', async () => {
|
|
72
72
|
const f = await fixture();
|
|
73
73
|
let client;
|
|
74
74
|
try {
|
|
@@ -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');
|
|
@@ -75,7 +75,8 @@ test('R34 missing authority waits durably and approval resumes the same phase af
|
|
|
75
75
|
assert.equal(journal.events.filter(event=>event.type==='action_started').length,0);
|
|
76
76
|
|
|
77
77
|
await f.arm();
|
|
78
|
-
const approved = await f.runtime.approve({goalId:goal.goal_id,decision:'approve'
|
|
78
|
+
const approved = await f.runtime.approve({goalId:goal.goal_id,decision:'approve',
|
|
79
|
+
approvalSubjectSha256:result.loop.approval_subject.approval_subject_sha256});
|
|
79
80
|
assert.equal(approved.status,'approved');
|
|
80
81
|
assert.equal(approved.loop.phase,'ACTING');
|
|
81
82
|
const resumed = await f.runtime.run({goalId:goal.goal_id,maxCycles:2});
|
|
@@ -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);
|