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
|
@@ -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}$/);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { createDurableJobSupervisorR44 } from '../src/durable-job-r44.mjs';
|
|
7
|
+
|
|
8
|
+
class FakeProvider{
|
|
9
|
+
constructor({external=true}={}){this.external=external;this.handles=new Map();this.starts=0;}
|
|
10
|
+
descriptor(){return {kind:this.external?'external-supervisor':'controller-local',instance_id:'sup-1'};}
|
|
11
|
+
async start(spec){
|
|
12
|
+
const handle={handle_id:`h-${++this.starts}`,pid:1000+this.starts,creation_id:`c-${this.starts}`,profile_id:spec.profile_id};
|
|
13
|
+
const running={...handle,state:'running'};this.handles.set(handle.handle_id,running);return {...running};
|
|
14
|
+
}
|
|
15
|
+
async reattach(handle){
|
|
16
|
+
const current=this.handles.get(handle.handle_id);return current?{...current}:null;
|
|
17
|
+
}
|
|
18
|
+
async status(handle){return this.handles.get(handle.handle_id)??null;}
|
|
19
|
+
async terminate(handle){const current=this.handles.get(handle.handle_id);if(current){current.state='terminated';return {...current};}return null;}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
test('R44 controller/session jobs never masquerade as persistent after owner restart',async()=>{
|
|
23
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-job-'));const provider=new FakeProvider();
|
|
24
|
+
try{
|
|
25
|
+
const one=await createDurableJobSupervisorR44({stateDir:root,provider,ownerInstanceId:'owner-1'});
|
|
26
|
+
const c=await one.start({lifetime:'controller',profileId:'build',rootId:'core',cwd:'.',args:[]});
|
|
27
|
+
const s=await one.start({lifetime:'session',profileId:'test',rootId:'core',cwd:'.',args:[]});
|
|
28
|
+
assert.equal(c.state,'running');assert.equal(s.state,'running');
|
|
29
|
+
const two=await createDurableJobSupervisorR44({stateDir:root,provider,ownerInstanceId:'owner-2'});
|
|
30
|
+
assert.equal((await two.status({jobId:c.job_id})).state,'interrupted');
|
|
31
|
+
assert.equal((await two.status({jobId:s.job_id})).state,'interrupted');
|
|
32
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('R44 persistent job reattaches only through external supervisor with exact process identity',async()=>{
|
|
36
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-job-persist-'));const provider=new FakeProvider();
|
|
37
|
+
try{
|
|
38
|
+
const one=await createDurableJobSupervisorR44({stateDir:root,provider,ownerInstanceId:'owner-1'});
|
|
39
|
+
const job=await one.start({lifetime:'persistent',profileId:'watch',rootId:'core',cwd:'.',args:[]});
|
|
40
|
+
const two=await createDurableJobSupervisorR44({stateDir:root,provider,ownerInstanceId:'owner-2'});
|
|
41
|
+
const recovered=await two.status({jobId:job.job_id});
|
|
42
|
+
assert.equal(recovered.state,'running');assert.equal(recovered.recovered,true);
|
|
43
|
+
assert.equal(recovered.pid,job.pid);assert.equal(recovered.creation_id,job.creation_id);
|
|
44
|
+
|
|
45
|
+
const live=provider.handles.get(job.handle_id);live.creation_id='spoofed';
|
|
46
|
+
const three=await createDurableJobSupervisorR44({stateDir:root,provider,ownerInstanceId:'owner-3'});
|
|
47
|
+
assert.equal((await three.status({jobId:job.job_id})).state,'orphaned');
|
|
48
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('R44 persistent lifetime rejects controller-local provider and state corruption',async()=>{
|
|
52
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-job-local-'));
|
|
53
|
+
try{
|
|
54
|
+
const local=await createDurableJobSupervisorR44({stateDir:root,provider:new FakeProvider({external:false}),ownerInstanceId:'owner'});
|
|
55
|
+
await assert.rejects(local.start({lifetime:'persistent',profileId:'x',rootId:'core',cwd:'.',args:[]}),/external supervisor/);
|
|
56
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
57
|
+
|
|
58
|
+
const corrupt=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-job-corrupt-'));
|
|
59
|
+
try{
|
|
60
|
+
await writeFile(path.join(corrupt,'jobs.json'),'{"schema":','utf8');
|
|
61
|
+
await assert.rejects(createDurableJobSupervisorR44({stateDir:corrupt,provider:new FakeProvider(),ownerInstanceId:'owner'}),/state JSON invalid/);
|
|
62
|
+
}finally{await rm(corrupt,{recursive:true,force:true});}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('R44 terminate binds exact supervisor handle and becomes durable terminal state',async()=>{
|
|
66
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-job-term-'));const provider=new FakeProvider();
|
|
67
|
+
try{
|
|
68
|
+
const sup=await createDurableJobSupervisorR44({stateDir:root,provider,ownerInstanceId:'owner'});
|
|
69
|
+
const job=await sup.start({lifetime:'persistent',profileId:'watch',rootId:'core',cwd:'.',args:[]});
|
|
70
|
+
const ended=await sup.terminate({jobId:job.job_id});
|
|
71
|
+
assert.equal(ended.state,'terminated');
|
|
72
|
+
const restarted=await createDurableJobSupervisorR44({stateDir:root,provider,ownerInstanceId:'owner-2'});
|
|
73
|
+
assert.equal((await restarted.status({jobId:job.job_id})).state,'terminated');
|
|
74
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
75
|
+
});
|
|
@@ -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
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { mkdir, mkdtemp, rm, writeFile, readFile } from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { createDurableJobSupervisorR44 } from '../src/durable-job-r44.mjs';
|
|
9
|
+
import { createExternalJobProviderR44 } from '../src/external-job-provider-r44.mjs';
|
|
10
|
+
|
|
11
|
+
const sha=b=>createHash('sha256').update(b).digest('hex');
|
|
12
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const repoRoot=path.resolve(here,'..');
|
|
14
|
+
const workerPath=path.join(repoRoot,'src','job-supervisor-host-r44.mjs');
|
|
15
|
+
|
|
16
|
+
test('R44 real external supervisor preserves persistent job identity across controller recreation',async()=>{
|
|
17
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-real-supervisor-'));
|
|
18
|
+
const supervisorState=path.join(root,'host'),jobsState=path.join(root,'jobs'),project=path.join(root,'project');
|
|
19
|
+
await mkdir(project,{recursive:true});
|
|
20
|
+
const script=path.join(project,'hold.mjs');
|
|
21
|
+
await writeFile(script,`process.stdout.write('READY\\n');setInterval(()=>{},1000);\n`,'utf8');
|
|
22
|
+
const nodeSha=sha(await readFile(process.execPath)), workerSha=sha(await readFile(workerPath));
|
|
23
|
+
const config={stateDir:supervisorState,nodeExecutable:process.execPath,nodeExecutableSha256:nodeSha,
|
|
24
|
+
workerPath,workerSha256:workerSha,roots:{core:project},profiles:{
|
|
25
|
+
hold:{executable:process.execPath,executable_sha256:nodeSha,root_ids:['core'],prefix_args:[script],max_args:4,max_arg_bytes:1024}
|
|
26
|
+
}};
|
|
27
|
+
let providerA=null,providerB=null;
|
|
28
|
+
try{
|
|
29
|
+
providerA=await createExternalJobProviderR44(config);
|
|
30
|
+
const one=await createDurableJobSupervisorR44({stateDir:jobsState,provider:providerA,ownerInstanceId:'controller-a'});
|
|
31
|
+
const started=await one.start({lifetime:'persistent',profileId:'hold',rootId:'core',cwd:'.',args:[]});
|
|
32
|
+
assert.equal(started.state,'running');assert.ok(started.pid>0);assert.match(started.creation_id,/^[0-9a-f]{64}$/);
|
|
33
|
+
|
|
34
|
+
providerB=await createExternalJobProviderR44(config);
|
|
35
|
+
assert.equal(providerB.descriptor().instance_id,providerA.descriptor().instance_id,'must reconnect same external supervisor');
|
|
36
|
+
const two=await createDurableJobSupervisorR44({stateDir:jobsState,provider:providerB,ownerInstanceId:'controller-b'});
|
|
37
|
+
const recovered=await two.status({jobId:started.job_id});
|
|
38
|
+
assert.equal(recovered.state,'running');assert.equal(recovered.recovered,true);
|
|
39
|
+
assert.equal(recovered.handle_id,started.handle_id);assert.equal(recovered.pid,started.pid);assert.equal(recovered.creation_id,started.creation_id);
|
|
40
|
+
const ended=await two.terminate({jobId:started.job_id});assert.equal(ended.state,'terminated');
|
|
41
|
+
}finally{
|
|
42
|
+
if(providerB)await providerB.shutdown().catch(()=>{});
|
|
43
|
+
else if(providerA)await providerA.shutdown().catch(()=>{});
|
|
44
|
+
await rm(root,{recursive:true,force:true});
|
|
45
|
+
}
|
|
46
|
+
});
|
|
@@ -10,8 +10,10 @@ 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
|
|
14
|
-
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');
|
|
16
|
+
const desktopPolicyPath=path.join(parentRoot,'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;}};
|
|
17
19
|
|
|
@@ -70,7 +72,7 @@ test('R40 real max-runtime full stdio catalog is exactly 80 with four Desktop pr
|
|
|
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,91 @@
|
|
|
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 full stdio catalog is exactly 81 with skill and four Desktop primitives while 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-r44-full-catalog',version:'0.15.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
|
|
50
|
+
const env={
|
|
51
|
+
...getDefaultEnvironment(),
|
|
52
|
+
DEADBYTE_CAPABILITY_PROFILE:'full',
|
|
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:'1',
|
|
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
|
+
const {R44_EXPECTED_TOOLS}=await import('../src/remote-mcp-r44-parity-client.mjs');
|
|
79
|
+
assert.equal(names.length,81,'expected exact full R44 catalog 81, got '+names.length+': '+names.join(','));
|
|
80
|
+
assert.deepEqual(names,R44_EXPECTED_TOOLS);
|
|
81
|
+
for(const name of ['desktop_status','desktop_observe','desktop_target','desktop_action',
|
|
82
|
+
'host_file_read','host_file_write','host_exec','skill_catalog']) assert.ok(names.includes(name),'missing '+name);
|
|
83
|
+
for(const tool of listed.tools) assert.equal(tool.outputSchema?.additionalProperties,false,'non-strict output '+tool.name);
|
|
84
|
+
const catalog_sha256=sha(Buffer.from(JSON.stringify(names),'utf8'));
|
|
85
|
+
console.log(JSON.stringify({tool_count:names.length,catalog_sha256,desktop_tools:4,skill:true,authority:'DISARMED'}));
|
|
86
|
+
} finally {
|
|
87
|
+
await client.close().catch(()=>{});
|
|
88
|
+
for(const q of [processGrant,hostArm,desktopGrant,codingLease,autonomousLease]) assert.equal(await absent(q),true,'authority file appeared during catalog probe: '+q);
|
|
89
|
+
await f.cleanup();
|
|
90
|
+
}
|
|
91
|
+
});
|
package/test/host-plane.test.mjs
CHANGED
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
hostExec,
|
|
12
12
|
hostFileRead,
|
|
13
13
|
hostFileWrite,
|
|
14
|
-
loadHostPolicy
|
|
14
|
+
loadHostPolicy,
|
|
15
|
+
recheckHostAuthority
|
|
15
16
|
} from '../src/host-plane.mjs';
|
|
16
17
|
|
|
17
18
|
const sha = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
@@ -66,6 +67,19 @@ test('privileged host plane requires a live policy-bound arm lease', async () =>
|
|
|
66
67
|
await assert.rejects(() => assertHostArmed(expired.policy), /arm expired/);
|
|
67
68
|
});
|
|
68
69
|
|
|
70
|
+
test('host authority recheck rejects arm generation or expiry drift before side effects', async () => {
|
|
71
|
+
const f = await fixture();
|
|
72
|
+
const initial = await assertHostArmed(f.policy);
|
|
73
|
+
const arm = JSON.parse(await readFile(f.arm, 'utf8'));
|
|
74
|
+
await writeFile(f.arm, JSON.stringify({ ...arm, instance_nonce: 'b'.repeat(64) }));
|
|
75
|
+
await assert.rejects(() => recheckHostAuthority(f.policy, initial), /host authority changed during operation/);
|
|
76
|
+
await writeFile(f.arm, JSON.stringify({
|
|
77
|
+
...arm,
|
|
78
|
+
expires_at_utc: new Date(Date.parse(arm.expires_at_utc) + 60000).toISOString()
|
|
79
|
+
}));
|
|
80
|
+
await assert.rejects(() => recheckHostAuthority(f.policy, initial), /host authority changed during operation/);
|
|
81
|
+
});
|
|
82
|
+
|
|
69
83
|
test('host_file_write is root-scoped hash-precondition and host_file_read returns a verified digest', async () => {
|
|
70
84
|
const f = await fixture();
|
|
71
85
|
const first = await hostFileWrite(f.policy, {
|
|
@@ -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
|
+
});
|
|
@@ -79,6 +79,34 @@ test('native diff rolls back earlier operations when a later operation fails', a
|
|
|
79
79
|
} finally { await rm(f.root,{recursive:true,force:true}); }
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
+
test('native diff revocation between operations rolls back to preimage without autonomous rollback authority', async () => {
|
|
83
|
+
const f = await fixture();
|
|
84
|
+
try {
|
|
85
|
+
let writes = 0;
|
|
86
|
+
const revokingCoding = Object.freeze({
|
|
87
|
+
...f.codingRuntime,
|
|
88
|
+
fileWrite: async args => {
|
|
89
|
+
const result = await f.codingRuntime.fileWrite(args);
|
|
90
|
+
writes += 1;
|
|
91
|
+
if (writes === 1) {
|
|
92
|
+
const lease = JSON.parse(await readFile(f.policy.lease_file, 'utf8'));
|
|
93
|
+
await writeFile(f.policy.lease_file, JSON.stringify({ ...lease, instance_nonce:'e'.repeat(64) }));
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
const runtime = createNativeDiffRuntime({ policy:f.policy, codingRuntime:revokingCoding,
|
|
99
|
+
signingKeyPath:f.keys.privateKeyPath, verifyKeyPath:f.keys.publicKeyPath });
|
|
100
|
+
const preview = await runtime.preview({ rootId:'project', title:'revoke mid transaction', operations:[
|
|
101
|
+
{ kind:'write', path:'first.txt', expected_sha256:'absent', encoding:'utf8', content:'first\n' },
|
|
102
|
+
{ kind:'write', path:'second.txt', expected_sha256:'absent', encoding:'utf8', content:'second\n' }
|
|
103
|
+
] });
|
|
104
|
+
await assert.rejects(runtime.apply({ diffId:preview.diff_id }), /rolled back.*autonomous authority changed/i);
|
|
105
|
+
assert.equal(await exists(path.join(f.project,'first.txt')), false);
|
|
106
|
+
assert.equal(await exists(path.join(f.project,'second.txt')), false);
|
|
107
|
+
} finally { await rm(f.root,{recursive:true,force:true}); }
|
|
108
|
+
});
|
|
109
|
+
|
|
82
110
|
test('autonomous application scope isolates repeated identical native diffs across goals', async () => {
|
|
83
111
|
const f = await fixture();
|
|
84
112
|
try {
|
|
@@ -19,7 +19,7 @@ const EXPECTED_TOOLS = [
|
|
|
19
19
|
'agent_task_run', 'agent_task_status', 'agent_task_submit',
|
|
20
20
|
'agent_task_verify', 'contained_transform', 'evidence_verify',
|
|
21
21
|
'file_read',
|
|
22
|
-
'proof_execute', 'workspace_open'
|
|
22
|
+
'proof_execute', 'skill_catalog', 'workspace_open'
|
|
23
23
|
];
|
|
24
24
|
|
|
25
25
|
async function fixture() {
|
|
@@ -47,7 +47,7 @@ async function fixture() {
|
|
|
47
47
|
return { trust, client, transport };
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
test('
|
|
50
|
+
test('R44 hermetic base-plus-agent catalog advertises 13 strict output schemas', async () => {
|
|
51
51
|
const f = await fixture();
|
|
52
52
|
try {
|
|
53
53
|
await f.client.connect(f.transport);
|