deadbyte-mcp 0.12.0 → 0.14.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 +25 -0
- package/CONTEXT.md +3 -3
- package/MANIFEST.SHA256 +89 -45
- package/R41-PARENT.json +15 -0
- package/R42-PARENT.json +19 -0
- package/README.txt +2 -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 +1 -1
- 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 +1 -1
- package/docs/superpowers/specs/2026-09-20-r41-deterministic-memory-design.md +132 -0
- package/harness/r42-agent-evals.json +48 -0
- package/harness/task-contract.schema.json +33 -0
- package/package.json +11 -7
- package/proof/CONTAINMENT-BUILD.txt +6 -6
- package/scripts/agent-eval-r42.mjs +46 -0
- package/scripts/build-containment.ps1 +24 -14
- package/scripts/build-desktop-r40.ps1 +15 -9
- package/scripts/deferred-slot-operation-r41.mjs +127 -0
- package/scripts/deferred-slot-operation-r42.mjs +126 -0
- package/scripts/final-closure-r41.mjs +116 -0
- package/scripts/final-closure-r42.mjs +116 -0
- package/scripts/final-closure-verify-r41.mjs +215 -0
- package/scripts/final-closure-verify-r42.mjs +223 -0
- package/scripts/gate-windows-r41.ps1 +48 -0
- package/scripts/gate-windows-r42.ps1 +54 -0
- package/scripts/init-desktop-policy-r40.ps1 +10 -4
- package/scripts/release-parity-r41.mjs +221 -0
- package/scripts/release-parity-r42.mjs +235 -0
- package/scripts/release-parity-tests-r41.ps1 +9 -0
- package/scripts/release-parity-tests-r42.ps1 +6 -0
- package/scripts/verify-memory-r41.mjs +88 -0
- package/scripts/verify-r41-parent.mjs +61 -0
- package/scripts/verify-r42-parent.mjs +101 -0
- package/scripts/windows-gate-evidence-r41.mjs +53 -0
- package/scripts/windows-gate-evidence-r42.mjs +64 -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 +4 -1
- 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 +48 -6
- package/src/autonomous-policy.mjs +10 -2
- package/src/autonomous-runtime.mjs +144 -25
- package/src/autonomous-supervisor.mjs +1 -1
- package/src/evidence-publisher-r42.mjs +76 -0
- package/src/mcp-server.mjs +15 -2
- package/src/memory-r41.mjs +692 -0
- package/src/provider-circuit.mjs +4 -4
- package/src/version.mjs +1 -1
- 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/autonomous-r34-e2e.test.mjs +2 -1
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/controller-desktop-r40.test.mjs +2 -1
- package/test/core.test.mjs +1 -1
- package/test/evidence-publisher-r42.test.mjs +53 -0
- package/test/helpers/autonomous-r34-fixture.mjs +3 -3
- package/test/human-clarification-r42.test.mjs +53 -0
- package/test/memory-autonomous-integration-r41.test.mjs +31 -0
- package/test/memory-history-r41.test.mjs +126 -0
- package/test/memory-projections-r41.test.mjs +92 -0
- package/test/memory-retrieval-r41.test.mjs +124 -0
- package/test/memory-snapshot-r41.test.mjs +106 -0
- package/test/provider-responses-r42.test.mjs +90 -0
- package/test/r33-closeout-regression.test.mjs +27 -12
- package/test/r33-finalization.test.mjs +4 -4
- package/test/r41-finalization.test.mjs +38 -0
- package/test/r42-finalization.test.mjs +38 -0
- package/test/release-version.test.mjs +14 -13
- package/test/task-contract-r42.test.mjs +41 -0
package/src/provider-circuit.mjs
CHANGED
|
@@ -48,11 +48,11 @@ function openAt(state,ms,policy){
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export function providerCircuitId(planner){
|
|
51
|
-
if(planner?.backend
|
|
51
|
+
if(!['openai_compatible','openai_responses'].includes(planner?.backend)||typeof planner.endpoint!=='string'||typeof planner.model!=='string') {
|
|
52
52
|
throw new Error('provider circuit planner identity invalid');
|
|
53
53
|
}
|
|
54
|
-
const digest=createHash('sha256').update(Buffer.from(`${planner.endpoint}\0${planner.model}`,'utf8')).digest('hex');
|
|
55
|
-
return
|
|
54
|
+
const digest=createHash('sha256').update(Buffer.from(`${planner.backend}\0${planner.endpoint}\0${planner.model}`,'utf8')).digest('hex');
|
|
55
|
+
return `${planner.backend}:${digest}`;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
export function classifyProviderFailure(error){
|
|
@@ -61,7 +61,7 @@ export function classifyProviderFailure(error){
|
|
|
61
61
|
if(name==='aborterror'||/\b(?:timed?\s*out|timeout|aborted)\b/.test(text)) return 'timeout';
|
|
62
62
|
if(/(?:http\s*429|rate[ _-]?limit|too many requests)/.test(text)) return 'rate_limit';
|
|
63
63
|
if(/(?:http\s*(?:401|403)|unauthori[sz]ed|forbidden|api key|authentication)/.test(text)) return 'auth';
|
|
64
|
-
if(/(?:not json|invalid json|missing choices|invalid response|response.*(?:schema|contract|content))/.test(text)) return 'invalid_response';
|
|
64
|
+
if(/(?:not json|invalid json|missing choices|missing output_text|refusal|not completed|incomplete response|invalid response|response.*(?:schema|contract|content))/.test(text)) return 'invalid_response';
|
|
65
65
|
if(/(?:econnreset|econnrefused|enotfound|fetch failed|network|socket|transport|connection)/.test(text)) return 'transport';
|
|
66
66
|
return 'other';
|
|
67
67
|
}
|
package/src/version.mjs
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { runIsolatedEvaluation, buildFreshCriticContext, triageJudgeResult } from '../src/agent-eval-r42.mjs';
|
|
4
|
+
|
|
5
|
+
const H=value=>String(value).repeat(64).slice(0,64);
|
|
6
|
+
|
|
7
|
+
test('R42 generator never receives judge-only oracle or hidden ground truth',async()=>{
|
|
8
|
+
const oracleToken='ORACLE_ONLY_7fca1b';
|
|
9
|
+
let generatorInput,judgeInput;
|
|
10
|
+
const result=await runIsolatedEvaluation({caseSpec:{schema:'deadbyte.agent-eval-case.v1',case_id:'no-leak',
|
|
11
|
+
task:'Return a safe plan.',allowed_context:{files:['a.mjs']},hidden_oracle:{secret:oracleToken,expected:'bounded'},
|
|
12
|
+
acceptance:['No oracle leakage']},
|
|
13
|
+
generator:async input=>{generatorInput=input;return {artifact:{plan:['inspect','verify']},trace:{private_reasoning:'not shared'}};},
|
|
14
|
+
judge:async input=>{judgeInput=input;return {verdict:'pass',findings:[],evidence_refs:[H('1')]};}
|
|
15
|
+
});
|
|
16
|
+
assert.equal(JSON.stringify(generatorInput).includes(oracleToken),false);
|
|
17
|
+
assert.equal(Object.hasOwn(generatorInput,'hidden_oracle'),false);
|
|
18
|
+
assert.equal(judgeInput.hidden_oracle.secret,oracleToken);
|
|
19
|
+
assert.equal(Object.hasOwn(judgeInput,'generator_trace'),false);
|
|
20
|
+
assert.equal(result.advisory,true);
|
|
21
|
+
assert.match(result.case_sha256,/^[0-9a-f]{64}$/);
|
|
22
|
+
assert.match(result.candidate_sha256,/^[0-9a-f]{64}$/);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('R42 fresh critic context contains artifacts and evidence but no implementer assumptions',()=>{
|
|
26
|
+
const context=buildFreshCriticContext({case_id:'fresh-review',artifact:{files:['x.mjs']},evidence:[{id:H('2'),level:'automated'}],
|
|
27
|
+
criteria:['No authority widening']});
|
|
28
|
+
assert.deepEqual(Object.keys(context).sort(),['artifact','case_id','criteria','evidence','schema']);
|
|
29
|
+
assert.equal(JSON.stringify(context).includes('reasoning'),false);
|
|
30
|
+
assert.throws(()=>buildFreshCriticContext({case_id:'bad',artifact:{},evidence:[],criteria:[],implementer_reasoning:'trust me'}),/unknown field/i);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('R42 judge remains advisory and controller triage owns repair decision',()=>{
|
|
34
|
+
const judge={schema:'deadbyte.agent-eval-judge-result.v1',verdict:'fail',advisory:true,
|
|
35
|
+
findings:[{code:'AUTH_WIDEN',severity:'critical',evidence:'schema admits command'}],evidence_refs:[H('3')]};
|
|
36
|
+
const triage=triageJudgeResult(judge,{critical_action:'pause',noncritical_action:'repair'});
|
|
37
|
+
assert.deepEqual(triage,{action:'pause',reason_codes:['AUTH_WIDEN'],judge_advisory:true});
|
|
38
|
+
assert.throws(()=>triageJudgeResult({...judge,advisory:false},{critical_action:'pause',noncritical_action:'repair'}),/advisory/i);
|
|
39
|
+
});
|
|
@@ -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
|
+
});
|
|
@@ -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});
|
|
@@ -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.14.0');
|
|
25
25
|
}
|
|
26
26
|
});
|
|
27
27
|
|
|
@@ -70,7 +70,8 @@ test('R40 policy initializer pins current native observer and input and starts D
|
|
|
70
70
|
assert.match(source,/deadbyte-desktop-observer-r40\.exe/);
|
|
71
71
|
assert.match(source,/deadbyte-desktop-input-r40\.exe/);
|
|
72
72
|
assert.match(source,/deadbyte-desktop-uia-r40\.exe/);
|
|
73
|
-
assert.match(source,/
|
|
73
|
+
assert.match(source,/System\.Security\.Cryptography\.SHA256/);
|
|
74
|
+
assert.match(source,/Get-Sha256Hex -Path \$Observer/);
|
|
74
75
|
assert.match(source,/Remove-Item -LiteralPath \$Grant/);
|
|
75
76
|
assert.match(source,/grant_state='DISARMED'/);
|
|
76
77
|
assert.match(source,/deadbyte\.desktop-policy\.v1/);
|
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.14.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}$/);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {
|
|
7
|
+
publishEvidenceAtomic, createExecutionReceipt, createVerificationResult
|
|
8
|
+
} from '../src/evidence-publisher-r42.mjs';
|
|
9
|
+
|
|
10
|
+
const H=value=>String(value).repeat(64).slice(0,64);
|
|
11
|
+
|
|
12
|
+
test('R42 evidence publication is atomic, no-clobber and idempotent for identical bytes',async()=>{
|
|
13
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r42-evidence-'));
|
|
14
|
+
try{
|
|
15
|
+
const one=await publishEvidenceAtomic({root,kind:'eval',evidenceId:'case-1',payload:{status:'passed',score:1}});
|
|
16
|
+
assert.equal(one.status,'created');
|
|
17
|
+
const two=await publishEvidenceAtomic({root,kind:'eval',evidenceId:'case-1',payload:{status:'passed',score:1}});
|
|
18
|
+
assert.equal(two.status,'existing');
|
|
19
|
+
assert.equal(two.evidence_sha256,one.evidence_sha256);
|
|
20
|
+
const bytes=await readFile(one.path);
|
|
21
|
+
assert.equal(H('0')===one.evidence_sha256,false);
|
|
22
|
+
assert.match(bytes.toString('utf8'),/"payload_sha256":"[0-9a-f]{64}"/);
|
|
23
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('R42 conflicting concurrent evidence identity fails closed without overwrite',async()=>{
|
|
27
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r42-evidence-race-'));
|
|
28
|
+
try{
|
|
29
|
+
const settled=await Promise.allSettled([
|
|
30
|
+
publishEvidenceAtomic({root,kind:'closure',evidenceId:'release-final',payload:{candidate:'a'}}),
|
|
31
|
+
publishEvidenceAtomic({root,kind:'closure',evidenceId:'release-final',payload:{candidate:'b'}})
|
|
32
|
+
]);
|
|
33
|
+
assert.equal(settled.filter(item=>item.status==='fulfilled').length,1);
|
|
34
|
+
assert.equal(settled.filter(item=>item.status==='rejected').length,1);
|
|
35
|
+
assert.match(String(settled.find(item=>item.status==='rejected').reason),/EVIDENCE_ID_CONFLICT/);
|
|
36
|
+
const winner=settled.find(item=>item.status==='fulfilled').value;
|
|
37
|
+
const parsed=JSON.parse(await readFile(winner.path,'utf8'));
|
|
38
|
+
assert.ok(['a','b'].includes(parsed.payload.candidate));
|
|
39
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('R42 execution receipt and outcome verification are distinct typed artifacts',()=>{
|
|
43
|
+
const receipt=createExecutionReceipt({execution_id:'exec-1',subject_sha256:H('1'),request_sha256:H('2'),
|
|
44
|
+
observed_exit_code:0,started_at_utc:'2026-09-20T01:00:00.000Z',completed_at_utc:'2026-09-20T01:00:01.000Z'});
|
|
45
|
+
assert.equal(receipt.schema,'deadbyte.execution-receipt.v1');
|
|
46
|
+
assert.equal(Object.hasOwn(receipt,'outcome_passed'),false);
|
|
47
|
+
const verified=createVerificationResult({verification_id:'verify-1',subject_sha256:H('1'),
|
|
48
|
+
execution_receipt_sha256:receipt.receipt_sha256,outcome_passed:true,oracle:'runtime_behavior',evidence_refs:[H('3')]});
|
|
49
|
+
assert.equal(verified.schema,'deadbyte.verification-result.v1');
|
|
50
|
+
assert.equal(verified.outcome_passed,true);
|
|
51
|
+
assert.throws(()=>createExecutionReceipt({execution_id:'exec-2',subject_sha256:H('1'),request_sha256:H('2'),
|
|
52
|
+
observed_exit_code:0,started_at_utc:'2026-09-20T01:00:00.000Z',completed_at_utc:'2026-09-20T01:00:01.000Z',outcome_passed:true}),/unknown field/i);
|
|
53
|
+
});
|
|
@@ -12,7 +12,7 @@ import { createAutonomousRuntime } from '../../src/autonomous-runtime.mjs';
|
|
|
12
12
|
const sha = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
13
13
|
const nonce = () => randomBytes(32).toString('hex');
|
|
14
14
|
|
|
15
|
-
export async function createR34Fixture({authority='armed',withFailingProfile=false,withStatefulProfile=false,codingRuntimeFactory=null,nativeDiffRuntimeFactory=null,desktopMemoryProvider=null,desktopRuntime=null}={}) {
|
|
15
|
+
export async function createR34Fixture({authority='armed',withFailingProfile=false,withStatefulProfile=false,codingRuntimeFactory=null,nativeDiffRuntimeFactory=null,desktopMemoryProvider=null,memoryProvider=null,desktopRuntime=null}={}) {
|
|
16
16
|
const root = await mkdtemp(path.join(os.tmpdir(),'deadbyte-r34-e2e-'));
|
|
17
17
|
const project = path.join(root,'project');
|
|
18
18
|
const trust = path.join(root,'trust');
|
|
@@ -73,8 +73,8 @@ export async function createR34Fixture({authority='armed',withFailingProfile=fal
|
|
|
73
73
|
const machineFsRuntime = createMachineFsRuntime({policy:codingPolicy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
74
74
|
const baseNativeDiffRuntime = createNativeDiffRuntime({policy,codingRuntime,machineFsRuntime,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
75
75
|
const nativeDiffRuntime = nativeDiffRuntimeFactory ? nativeDiffRuntimeFactory(baseNativeDiffRuntime) : baseNativeDiffRuntime;
|
|
76
|
-
const createRuntime = ({codingRuntimeOverride=codingRuntime,nativeDiffRuntimeOverride=nativeDiffRuntime,desktopMemoryProviderOverride=desktopMemoryProvider,desktopRuntimeOverride=desktopRuntime}={}) => createAutonomousRuntime({policy,codingRuntime:codingRuntimeOverride,nativeDiffRuntime:nativeDiffRuntimeOverride,machineFsRuntime,
|
|
77
|
-
desktopRuntime:desktopRuntimeOverride,desktopMemoryProvider:desktopMemoryProviderOverride,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
76
|
+
const createRuntime = ({codingRuntimeOverride=codingRuntime,nativeDiffRuntimeOverride=nativeDiffRuntime,desktopMemoryProviderOverride=desktopMemoryProvider,memoryProviderOverride=memoryProvider,desktopRuntimeOverride=desktopRuntime}={}) => createAutonomousRuntime({policy,codingRuntime:codingRuntimeOverride,nativeDiffRuntime:nativeDiffRuntimeOverride,machineFsRuntime,
|
|
77
|
+
desktopRuntime:desktopRuntimeOverride,desktopMemoryProvider:desktopMemoryProviderOverride,memoryProvider:memoryProviderOverride,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
78
78
|
return {
|
|
79
79
|
root,project,target,keys,codingPolicy,policy,baseCodingRuntime,codingRuntime,machineFsRuntime,nativeDiffRuntime,createRuntime,runtime:createRuntime(),
|
|
80
80
|
initial_sha256:sha(await readFile(target)),arm,disarm,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { createR34Fixture, planCreateDecision, sendPlannerDecision } from './helpers/autonomous-r34-fixture.mjs';
|
|
4
|
+
|
|
5
|
+
async function startPlannedGoal(f) {
|
|
6
|
+
const goal=await f.runtime.submit({submissionId:'r42-clarify',rootId:'project',title:'Clarification',
|
|
7
|
+
goal:'Choose the correct bounded implementation.',acceptance:'Use the operator choice.',maxIterations:8});
|
|
8
|
+
let result=await f.runtime.run({goalId:goal.goal_id,maxCycles:2});
|
|
9
|
+
result=await sendPlannerDecision(f.runtime,goal.goal_id,result,planCreateDecision(),{maxCycles:2});
|
|
10
|
+
return {goal,result};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
test('R42 planner can request typed human clarification and resume with signed answer context',async()=>{
|
|
14
|
+
const f=await createR34Fixture();
|
|
15
|
+
try {
|
|
16
|
+
const {goal,result:first}=await startPlannedGoal(f);
|
|
17
|
+
const clarify={schema:'deadbyte.autonomous-decision.v2',kind:'clarify',
|
|
18
|
+
question:'Which compatibility target should be authoritative?',
|
|
19
|
+
choices:['Preserve R41 behavior','Adopt R42 behavior']};
|
|
20
|
+
const paused=await sendPlannerDecision(f.runtime,goal.goal_id,first,clarify,{maxCycles:2});
|
|
21
|
+
assert.equal(paused.status,'paused');
|
|
22
|
+
assert.equal(paused.reason,'human_input_required');
|
|
23
|
+
assert.deepEqual(paused.loop.clarification,{question:clarify.question,choices:clarify.choices});
|
|
24
|
+
const subject=paused.loop.approval_subject.approval_subject_sha256;
|
|
25
|
+
await assert.rejects(f.runtime.approve({goalId:goal.goal_id,decision:'approve',answer:'Preserve R41 behavior',
|
|
26
|
+
approvalSubjectSha256:'f'.repeat(64)}),/approval subject mismatch/i);
|
|
27
|
+
await assert.rejects(f.runtime.approve({goalId:goal.goal_id,decision:'approve',approvalSubjectSha256:subject}),/answer.*required/i);
|
|
28
|
+
const approved=await f.runtime.approve({goalId:goal.goal_id,decision:'approve',answer:'Preserve R41 behavior',
|
|
29
|
+
approvalSubjectSha256:subject});
|
|
30
|
+
assert.equal(approved.status,'approved');
|
|
31
|
+
const next=await f.runtime.run({goalId:goal.goal_id,maxCycles:2});
|
|
32
|
+
assert.equal(next.status,'input_required');
|
|
33
|
+
assert.match(next.planner_request.prompt,/Preserve R41 behavior/);
|
|
34
|
+
const events=await f.runtime.events({goalId:goal.goal_id,fromSeq:1,limit:200});
|
|
35
|
+
const provided=events.events.find(event=>event.type==='human_input_provided');
|
|
36
|
+
assert.equal(provided.payload.answer,'Preserve R41 behavior');
|
|
37
|
+
assert.match(provided.payload.answer_sha256,/^[0-9a-f]{64}$/);
|
|
38
|
+
const approval=events.events.find(event=>event.type==='approval_granted');
|
|
39
|
+
assert.equal(approval.payload.approval_subject_sha256,subject);
|
|
40
|
+
assert.equal(approval.payload.scope,'human_clarification');
|
|
41
|
+
} finally { await f.cleanup(); }
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('R42 clarification contract rejects injected fields and oversized choices',async()=>{
|
|
45
|
+
const f=await createR34Fixture();
|
|
46
|
+
try {
|
|
47
|
+
const {goal,result:first}=await startPlannedGoal(f);
|
|
48
|
+
const injected={schema:'deadbyte.autonomous-decision.v2',kind:'clarify',question:'Choose?',choices:[],command:'cmd.exe'};
|
|
49
|
+
const repaired=await sendPlannerDecision(f.runtime,goal.goal_id,first,injected,{maxCycles:2});
|
|
50
|
+
assert.equal(repaired.status,'input_required');
|
|
51
|
+
assert.match(repaired.planner_request.prompt,/PREVIOUS_RESPONSE_REJECTED/);
|
|
52
|
+
} finally { await f.cleanup(); }
|
|
53
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import { createR34Fixture } from './helpers/autonomous-r34-fixture.mjs';
|
|
6
|
+
import { loadPrivateSigningKey,loadPublicVerifyKey } from '../src/trust.mjs';
|
|
7
|
+
import { createR41MemoryProvider } from '../src/memory-r41.mjs';
|
|
8
|
+
|
|
9
|
+
const sha=value=>createHash('sha256').update(String(value)).digest('hex');
|
|
10
|
+
|
|
11
|
+
test('R41 planner request binds verified bounded memory before a mutating decision',async()=>{
|
|
12
|
+
const f=await createR34Fixture();
|
|
13
|
+
try{
|
|
14
|
+
const signing=await loadPrivateSigningKey(f.keys.privateKeyPath);
|
|
15
|
+
const verify=await loadPublicVerifyKey(f.keys.publicKeyPath);
|
|
16
|
+
const provider=createR41MemoryProvider({memoryRoot:path.join(f.root,'memory'),sourceReleaseManifest:sha('r41-release'),
|
|
17
|
+
writerVersion:'0.13.0',privateKey:signing.privateKey,publicKey:verify.publicKey,keyId:signing.keyId,byteBudget:65536});
|
|
18
|
+
const runtime=f.createRuntime({memoryProviderOverride:provider});
|
|
19
|
+
const goal=await runtime.submit({submissionId:'r41-memory-binding',rootId:'project',title:'repair',goal:'repair x.txt',acceptance:'x is y'});
|
|
20
|
+
const run=await runtime.run({goalId:goal.goal_id,maxCycles:1});
|
|
21
|
+
assert.equal(run.status,'input_required');
|
|
22
|
+
assert.match(run.planner_request.prompt,/"long_term_memory"/);
|
|
23
|
+
assert.match(run.planner_request.prompt,/"working_memory_sha256":"[0-9a-f]{64}"/);
|
|
24
|
+
const journal=await runtime.events({goalId:goal.goal_id,fromSeq:1,limit:100});
|
|
25
|
+
const requested=journal.events.find(event=>event.type==='planner_requested');
|
|
26
|
+
assert.match(requested.payload.working_memory_sha256,/^[0-9a-f]{64}$/);
|
|
27
|
+
assert.match(requested.payload.memory_snapshot_sha256,/^[0-9a-f]{64}$/);
|
|
28
|
+
assert.match(requested.payload.memory_index_sha256,/^[0-9a-f]{64}$/);
|
|
29
|
+
assert.equal(requested.payload.memory_source_release_manifest,sha('r41-release'));
|
|
30
|
+
}finally{await f.cleanup();}
|
|
31
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { createHash,generateKeyPairSync,sign as cryptoSign } from 'node:crypto';
|
|
4
|
+
import { mkdir,mkdtemp,rm,writeFile } from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { canonicalJson } from '../src/canonical-json.mjs';
|
|
8
|
+
import { createR41MemoryProvider } from '../src/memory-r41.mjs';
|
|
9
|
+
import { verifyR41Memory } from '../scripts/verify-memory-r41.mjs';
|
|
10
|
+
|
|
11
|
+
const EVENT_DOMAIN=Buffer.from('DEADBYTE-AUTONOMOUS-EVENT-V1\0');
|
|
12
|
+
const GOAL_DOMAIN=Buffer.from('DEADBYTE-AUTONOMOUS-GOAL-V1\0');
|
|
13
|
+
const ZERO='0'.repeat(64);
|
|
14
|
+
const sha=value=>createHash('sha256').update(Buffer.from(typeof value==='string'?value:canonicalJson(value),'utf8')).digest('hex');
|
|
15
|
+
const {privateKey,publicKey}=generateKeyPairSync('ed25519');
|
|
16
|
+
const keyId=createHash('sha256').update(Buffer.from(publicKey.export({type:'spki',format:'der'}))).digest('hex');
|
|
17
|
+
const manifest=sha('r41-history-release');
|
|
18
|
+
|
|
19
|
+
function eventJournal(goalId,items){
|
|
20
|
+
const events=[];
|
|
21
|
+
for(const [index,[type,payload]] of items.entries()){
|
|
22
|
+
const body={schema:'deadbyte.autonomous-event.v1',seq:index+1,goal_id:goalId,
|
|
23
|
+
created_at_utc:`2026-09-20T08:00:${String(index).padStart(2,'0')}.000Z`,prev_sha256:events.at(-1)?.event_sha256??ZERO,type,payload};
|
|
24
|
+
const event_sha256=sha(body);const signed={...body,event_sha256};
|
|
25
|
+
events.push({...signed,attestation:{algorithm:'ed25519',key_id:keyId,
|
|
26
|
+
signature_base64:cryptoSign(null,Buffer.concat([EVENT_DOMAIN,Buffer.from(canonicalJson(signed),'utf8')]),privateKey).toString('base64')}});
|
|
27
|
+
}
|
|
28
|
+
return events;
|
|
29
|
+
}
|
|
30
|
+
function signedGoal(goalId,title){
|
|
31
|
+
const identity={schema:'deadbyte.autonomous-goal.v1',policy_sha256:sha('policy'),root_id:'core',submission_id:title,
|
|
32
|
+
title,goal:`repair ${title}`,acceptance:'tests pass',max_iterations:8};
|
|
33
|
+
const body={...identity,goal_sha256:sha(identity),goal_id:goalId};
|
|
34
|
+
return {...body,attestation:{algorithm:'ed25519',key_id:keyId,
|
|
35
|
+
signature_base64:cryptoSign(null,Buffer.concat([GOAL_DOMAIN,Buffer.from(canonicalJson(body),'utf8')]),privateKey).toString('base64')}};
|
|
36
|
+
}
|
|
37
|
+
async function writeHistory(root,goal,events){
|
|
38
|
+
const dir=path.join(root,goal.goal_id);await mkdir(path.join(dir,'events'),{recursive:true});
|
|
39
|
+
await writeFile(path.join(dir,'goal.json'),`${canonicalJson(goal)}\n`);
|
|
40
|
+
for(const event of events)await writeFile(path.join(dir,'events',`${String(event.seq).padStart(6,'0')}.json`),`${canonicalJson(event)}\n`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
test('R41 provider retrieves verified prior-goal memory and excludes cross-release journals',async()=>{
|
|
44
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r41-history-'));
|
|
45
|
+
try{
|
|
46
|
+
const journalRoot=path.join(root,'goals');const memoryRoot=path.join(root,'memory');
|
|
47
|
+
const priorId=sha('prior-goal');const priorGoal=signedGoal(priorId,'ParserSymbol timeout');
|
|
48
|
+
const priorEvents=eventJournal(priorId,[
|
|
49
|
+
['goal_created',{source_release_manifest:manifest}],
|
|
50
|
+
['profile_completed',{profile_id:'test',passed:false,diagnostic_codes:['ERR_PARSER_TIMEOUT'],symbols:['ParserSymbol']}]
|
|
51
|
+
]);
|
|
52
|
+
await writeHistory(journalRoot,priorGoal,priorEvents);
|
|
53
|
+
const foreignId=sha('foreign-goal');const foreignGoal=signedGoal(foreignId,'ParserSymbol foreign');
|
|
54
|
+
await writeHistory(journalRoot,foreignGoal,eventJournal(foreignId,[['goal_created',{source_release_manifest:sha('other-release')}]]));
|
|
55
|
+
const currentId=sha('current-goal');const signedCurrent=signedGoal(currentId,'ParserSymbol regression');
|
|
56
|
+
await writeHistory(journalRoot,signedCurrent,eventJournal(currentId,[['goal_created',{source_release_manifest:manifest}]]));
|
|
57
|
+
const currentGoal={...signedCurrent};delete currentGoal.attestation;
|
|
58
|
+
const currentEvents=eventJournal(currentId,[['goal_created',{source_release_manifest:manifest}]]);
|
|
59
|
+
const provider=createR41MemoryProvider({memoryRoot,journalRoot,sourceReleaseManifest:manifest,privateKey,publicKey,keyId,byteBudget:32768});
|
|
60
|
+
const result=await provider.refresh({goal:currentGoal,events:currentEvents,currentSymbols:['ParserSymbol']});
|
|
61
|
+
assert.equal(result.historical_goal_count,1);
|
|
62
|
+
assert.ok(result.planner_context.historical_memory.length>=1);
|
|
63
|
+
assert.equal(result.planner_context.historical_memory.some(item=>item.goal?.goal_id===foreignId||item.goal_id===foreignId),false);
|
|
64
|
+
assert.equal(result.planner_context.historical_archive_sha256,result.historical_archive_sha256);
|
|
65
|
+
const grownPriorEvents=eventJournal(priorId,[
|
|
66
|
+
['goal_created',{source_release_manifest:manifest}],
|
|
67
|
+
['profile_completed',{profile_id:'test',passed:false,diagnostic_codes:['ERR_PARSER_TIMEOUT'],symbols:['ParserSymbol']}],
|
|
68
|
+
['planner_decision',{decision:{summary:'retry parser',action:'run_profile'}}],
|
|
69
|
+
['profile_completed',{profile_id:'test',passed:true,symbols:['ParserSymbol']}],
|
|
70
|
+
['release_completed',{receipt_sha256:sha('prior-release-grown')}]
|
|
71
|
+
]);
|
|
72
|
+
await rm(path.join(journalRoot,priorId),{recursive:true,force:true});
|
|
73
|
+
await writeHistory(journalRoot,priorGoal,grownPriorEvents);
|
|
74
|
+
const refreshed=await provider.refresh({goal:currentGoal,events:currentEvents,currentSymbols:['ParserSymbol']});
|
|
75
|
+
assert.notEqual(refreshed.historical_archive_sha256,result.historical_archive_sha256);
|
|
76
|
+
const publicKeyPath=path.join(root,'public.pem');await writeFile(publicKeyPath,publicKey.export({type:'spki',format:'pem'}));
|
|
77
|
+
const verified=await verifyR41Memory({memoryRoot,journalRoot,sourceReleaseManifest:manifest,publicKeyPath});
|
|
78
|
+
assert.equal(verified.status,'passed');assert.equal(verified.goal_count,1);assert.equal(verified.archive_count,2);
|
|
79
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('R41 provider fails closed on a poisoned same-release historical chain',async()=>{
|
|
83
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r41-history-poison-'));
|
|
84
|
+
try{
|
|
85
|
+
const journalRoot=path.join(root,'goals');const goalId=sha('poisoned-goal');const historical=signedGoal(goalId,'poisoned');
|
|
86
|
+
const events=eventJournal(goalId,[['goal_created',{source_release_manifest:manifest}],['release_completed',{}]]);
|
|
87
|
+
events[1].prev_sha256=sha('forged');
|
|
88
|
+
await writeHistory(journalRoot,historical,events);
|
|
89
|
+
const currentId=sha('current-poison-check');const current={...signedGoal(currentId,'current')};delete current.attestation;
|
|
90
|
+
const provider=createR41MemoryProvider({memoryRoot:path.join(root,'memory'),journalRoot,sourceReleaseManifest:manifest,privateKey,publicKey,keyId});
|
|
91
|
+
await assert.rejects(provider.refresh({goal:current,events:eventJournal(currentId,[['goal_created',{source_release_manifest:manifest}]])}),/predecessor mismatch|hash mismatch/);
|
|
92
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
test('R41 verifier accepts content-addressed indexes from earlier verified snapshot prefixes',async()=>{
|
|
97
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r41-history-prefix-index-'));
|
|
98
|
+
try{
|
|
99
|
+
const journalRoot=path.join(root,'goals');const memoryRoot=path.join(root,'memory');
|
|
100
|
+
const goalId=sha('growing-goal');const signed=signedGoal(goalId,'growing journal');const goal={...signed};delete goal.attestation;
|
|
101
|
+
const firstEvents=eventJournal(goalId,[
|
|
102
|
+
['goal_created',{source_release_manifest:manifest}],
|
|
103
|
+
['profile_completed',{profile_id:'test',passed:false,diagnostic_codes:['ERR_FIRST']}]
|
|
104
|
+
]);
|
|
105
|
+
await writeHistory(journalRoot,signed,firstEvents);
|
|
106
|
+
const provider=createR41MemoryProvider({memoryRoot,journalRoot,sourceReleaseManifest:manifest,privateKey,publicKey,keyId,byteBudget:32768});
|
|
107
|
+
const first=await provider.refresh({goal,events:firstEvents});
|
|
108
|
+
const grownEvents=eventJournal(goalId,[
|
|
109
|
+
['goal_created',{source_release_manifest:manifest}],
|
|
110
|
+
['profile_completed',{profile_id:'test',passed:false,diagnostic_codes:['ERR_FIRST']}],
|
|
111
|
+
['planner_decision',{decision:{summary:'retry safely',action:'run_profile'}}],
|
|
112
|
+
['profile_completed',{profile_id:'test',passed:true}],
|
|
113
|
+
['release_completed',{receipt_sha256:sha('grown-release')}]
|
|
114
|
+
]);
|
|
115
|
+
await rm(path.join(journalRoot,goalId),{recursive:true,force:true});
|
|
116
|
+
await writeHistory(journalRoot,signed,grownEvents);
|
|
117
|
+
const grown=await provider.refresh({goal,events:grownEvents});
|
|
118
|
+
assert.notEqual(first.memory_sha256,grown.memory_sha256);
|
|
119
|
+
assert.notEqual(first.index_sha256,grown.index_sha256);
|
|
120
|
+
const publicKeyPath=path.join(root,'public.pem');await writeFile(publicKeyPath,publicKey.export({type:'spki',format:'pem'}));
|
|
121
|
+
const verified=await verifyR41Memory({memoryRoot,journalRoot,sourceReleaseManifest:manifest,publicKeyPath});
|
|
122
|
+
assert.equal(verified.status,'passed');
|
|
123
|
+
assert.equal(verified.snapshot_count,2);
|
|
124
|
+
assert.equal(verified.index_count,2);
|
|
125
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
126
|
+
});
|