deadbyte-mcp 0.9.0 → 0.10.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/MANIFEST.SHA256 +119 -91
- package/README.txt +16 -11
- package/bench/fixtures/corpus.json +32 -13
- package/bench/fixtures/r36/case-map.json +8 -0
- package/bin/WORKER-REGISTRY.txt +3 -3
- package/bin/appcontainer-stage.exe +0 -0
- package/bin/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.exe +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.exe +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.exe +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.exe +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.exe +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.exe +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.exe +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.exe +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.exe +0 -0
- package/bin/deadbyte-contain.obj +0 -0
- package/bin/deadbyte-exec.exe +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-process-host.exe +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.exe +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/controller/README.TXT +8 -6
- package/controller/deadbyte-controller.ps1 +48 -2
- package/controller/deadbyte-process-policy.json +5 -5
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/R36-CAPABILITY-EVIDENCE.md +43 -0
- package/docs/superpowers/plans/2026-09-18-r36-zero-loss-autonomy.md +739 -0
- package/docs/superpowers/specs/2026-09-18-r36-zero-loss-autonomy-design.md +640 -0
- package/package.json +1 -1
- package/proof/CONTAINMENT-BUILD.txt +6 -6
- package/scripts/deferred-slot-operation.mjs +53 -15
- package/scripts/final-closure-verify.mjs +88 -39
- package/scripts/final-closure.mjs +63 -56
- package/scripts/gate-windows.ps1 +3 -3
- package/scripts/release-parity-tests.ps1 +9 -0
- package/scripts/release-parity.mjs +149 -139
- package/scripts/windows-gate-evidence.mjs +5 -5
- package/src/autonomous-context-engine.mjs +2 -1
- package/src/autonomous-mcp-tools.mjs +5 -1
- package/src/autonomous-output-contracts.mjs +4 -4
- package/src/autonomous-planner-contracts.mjs +36 -1
- package/src/autonomous-planner.mjs +25 -3
- package/src/autonomous-runtime.mjs +191 -21
- package/src/capability-benchmark-runner.mjs +86 -1
- package/src/capability-benchmark.mjs +45 -0
- package/src/capability-ledger.mjs +158 -0
- package/src/coding-mcp-tools.mjs +31 -17
- package/src/coding-output-contracts.mjs +48 -10
- package/src/coding-plane.mjs +45 -9
- package/src/coding-search.mjs +130 -47
- package/src/compact-gateway-mcp-tools.mjs +128 -0
- package/src/compact-gateway-output-contracts.mjs +16 -0
- package/src/deadbyte-cli.mjs +51 -28
- package/src/machine-fs-smoke-client.mjs +1 -1
- package/src/machine-fs.mjs +51 -8
- package/src/mcp-server.mjs +32 -9
- package/src/native-diff-v2.mjs +337 -0
- package/src/native-diff.mjs +50 -2
- package/src/observation-smoke-client.mjs +1 -1
- package/src/operator-surface.mjs +7 -1
- package/src/process-runtime.mjs +98 -11
- package/src/process-smoke-client.mjs +11 -3
- package/src/r36-compact-disarmed-probe.mjs +50 -0
- package/src/r36-full-surface-probe.mjs +56 -0
- package/src/remote-live-log.mjs +76 -0
- package/src/remote-mcp-autonomous-smoke-client.mjs +7 -3
- package/src/remote-mcp-coding-smoke-client.mjs +1 -1
- package/src/remote-mcp-r36-parity-client.mjs +175 -0
- package/src/remote-mcp-smoke-client.mjs +112 -56
- package/src/runtime-update.mjs +51 -1
- package/src/search-cursor.mjs +45 -0
- package/src/semantic-code.mjs +72 -5
- package/src/semantic-lsp-backend.mjs +39 -3
- package/src/version.mjs +1 -1
- package/test/autonomous-capability-r36.test.mjs +151 -0
- package/test/autonomous-runtime.test.mjs +2 -2
- package/test/capability-benchmark-r36.test.mjs +107 -0
- package/test/capability-benchmark-semantic-r36.test.mjs +52 -0
- package/test/capability-claims-r36.test.mjs +23 -0
- package/test/capability-ledger-r36.test.mjs +102 -0
- package/test/coding-catalog-consistency.test.mjs +2 -2
- package/test/coding-plane.test.mjs +2 -2
- package/test/coding-runtime.test.mjs +16 -0
- package/test/coding-search-r36.test.mjs +90 -0
- package/test/compact-catalog-r36.test.mjs +37 -0
- package/test/compact-gateway-r36.test.mjs +66 -0
- package/test/contained.test.mjs +1 -1
- package/test/core.test.mjs +1 -1
- package/test/deferred-slot-operation.test.mjs +9 -0
- package/test/fixtures/lsp-framed-server.mjs +2 -0
- package/test/helpers/autonomous-r34-fixture.mjs +5 -3
- package/test/multi-file-read.test.mjs +3 -0
- package/test/native-diff-topology-r36.test.mjs +105 -0
- package/test/package-boundary.test.mjs +6 -0
- package/test/process-longrun-r36.test.mjs +82 -0
- package/test/process-release-gate.test.mjs +9 -0
- package/test/production-docs.test.mjs +3 -3
- package/test/prompts-resources.test.mjs +7 -1
- package/test/r33-closeout-regression.test.mjs +4 -3
- package/test/r33-finalization.test.mjs +14 -9
- package/test/r36-finalization.test.mjs +104 -0
- package/test/r36-release-identity.test.mjs +52 -0
- package/test/release-version.test.mjs +18 -15
- package/test/remote-live-log-r36.test.mjs +37 -0
- package/test/remote-live-log-recovery-r36.test.mjs +41 -0
- package/test/remote-r24-catalog.test.mjs +13 -0
- package/test/remote-session-cli.test.mjs +40 -30
- package/test/runtime-self-update.test.mjs +58 -1
- package/test/semantic-lsp-backend.test.mjs +9 -0
- package/test/semantic-r36-rename.test.mjs +82 -0
- package/deadbyte-mcp-0.8.9.tgz +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { Client } from '@modelcontextprotocol/client';
|
|
4
|
+
import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/client/stdio';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
|
|
8
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const repoRoot=path.resolve(here,'..');
|
|
10
|
+
const serverPath=path.join(repoRoot,'src','mcp-server.mjs');
|
|
11
|
+
const gatewayNames=['capability_manifest','coding_inspect','machine_fs','machine_observe','process_session'];
|
|
12
|
+
|
|
13
|
+
test('R36 real compact stdio catalog stays <=42 and exposes all gateways',async()=>{
|
|
14
|
+
const client=new Client({name:'deadbyte-r36-compact-test',version:'0.10.0-dev'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
|
|
15
|
+
const env={
|
|
16
|
+
...getDefaultEnvironment(),
|
|
17
|
+
DEADBYTE_CAPABILITY_PROFILE:'compact',
|
|
18
|
+
DEADBYTE_AUDIT_RUNTIME:'0',DEADBYTE_MACHINE_RUNTIME:'0',DEADBYTE_AGENT_RUNTIME:'0',
|
|
19
|
+
DEADBYTE_PROCESS_RUNTIME:'0',DEADBYTE_CODING_RUNTIME:'0',DEADBYTE_MACHINE_FS_RUNTIME:'0',
|
|
20
|
+
DEADBYTE_OBSERVATION_RUNTIME:'0',DEADBYTE_AUTONOMOUS_RUNTIME:'0'
|
|
21
|
+
};
|
|
22
|
+
const transport=new StdioClientTransport({command:process.execPath,args:[serverPath],cwd:repoRoot,env,stderr:'pipe'});
|
|
23
|
+
try{
|
|
24
|
+
await client.connect(transport);
|
|
25
|
+
const listed=await client.listTools();
|
|
26
|
+
const names=listed.tools.map(tool=>tool.name).sort();
|
|
27
|
+
assert.ok(names.length<=42,`compact tool count ${names.length} exceeds 42`);
|
|
28
|
+
for(const name of gatewayNames) assert.ok(names.includes(name),`missing ${name}`);
|
|
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
|
+
const manifest=await client.callTool({name:'capability_manifest',arguments:{}});
|
|
31
|
+
assert.equal(manifest.isError,false);
|
|
32
|
+
assert.equal(manifest.structuredContent.status,'ok');
|
|
33
|
+
assert.equal(manifest.structuredContent.result.profile,'compact');
|
|
34
|
+
assert.match(manifest.structuredContent.result.ledger_sha256,/^[0-9a-f]{64}$/);
|
|
35
|
+
assert.ok(manifest.structuredContent.result.capabilities.length>40);
|
|
36
|
+
} finally {await client.close().catch(()=>{});}
|
|
37
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {
|
|
4
|
+
codingInspectInputSchema,
|
|
5
|
+
machineFsGatewayInputSchema,
|
|
6
|
+
processSessionGatewayInputSchema,
|
|
7
|
+
machineObserveInputSchema,
|
|
8
|
+
registerCompactGatewayTools
|
|
9
|
+
} from '../src/compact-gateway-mcp-tools.mjs';
|
|
10
|
+
|
|
11
|
+
const fakeServer=()=>{
|
|
12
|
+
const tools=new Map();
|
|
13
|
+
return {
|
|
14
|
+
tools,
|
|
15
|
+
registerTool(name,definition,handler){
|
|
16
|
+
if(tools.has(name)) throw new Error(`duplicate ${name}`);
|
|
17
|
+
tools.set(name,{definition,handler});
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
test('R36 compact gateway schemas reject generic arbitrary dispatch',()=>{
|
|
23
|
+
assert.equal(codingInspectInputSchema.safeParse({op:'call',tool_name:'host_exec',args:{}}).success,false);
|
|
24
|
+
assert.equal(machineFsGatewayInputSchema.safeParse({op:'exec',command:'whoami'}).success,false);
|
|
25
|
+
assert.equal(processSessionGatewayInputSchema.safeParse({op:'stdin',session_id:'0'.repeat(64),input:'raw'}).success,false);
|
|
26
|
+
assert.equal(machineObserveInputSchema.safeParse({op:'revoke',reason:'no'}).success,false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('R36 compact schemas preserve representative preconditions',()=>{
|
|
30
|
+
assert.equal(machineFsGatewayInputSchema.safeParse({
|
|
31
|
+
op:'move',root_id:'core',source_path:'a.txt',destination_path:'b.txt',expected_source_identity:'a'.repeat(64)
|
|
32
|
+
}).success,true);
|
|
33
|
+
assert.equal(machineFsGatewayInputSchema.safeParse({
|
|
34
|
+
op:'move',root_id:'core',source_path:'a.txt',destination_path:'b.txt'
|
|
35
|
+
}).success,false);
|
|
36
|
+
assert.equal(processSessionGatewayInputSchema.safeParse({op:'action',session_id:'b'.repeat(64),action_id:'ping'}).success,true);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('R36 registers exactly five compact gateway names',()=>{
|
|
40
|
+
const server=fakeServer();
|
|
41
|
+
registerCompactGatewayTools(server,{
|
|
42
|
+
profile:'compact',ledger:{schema:'deadbyte.capability-ledger.v1',entries:[],ledger_sha256:'0'.repeat(64)},
|
|
43
|
+
codingRuntime:null,semanticRuntime:null,machineFsRuntime:null,processRuntime:null,observationRuntime:null,machineRuntime:null
|
|
44
|
+
});
|
|
45
|
+
assert.deepEqual([...server.tools.keys()].sort(),[
|
|
46
|
+
'capability_manifest','coding_inspect','machine_fs','machine_observe','process_session'
|
|
47
|
+
]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('R36 coding_inspect gateway calls runtime directly and never MCP-recurses',async()=>{
|
|
51
|
+
const calls=[];
|
|
52
|
+
const codingRuntime={
|
|
53
|
+
stat:async args=>{calls.push(['stat',args]);return {status:'ok',root_id:args.rootId,path:args.relativePath,type:'file'};}
|
|
54
|
+
};
|
|
55
|
+
const server=fakeServer();
|
|
56
|
+
registerCompactGatewayTools(server,{
|
|
57
|
+
profile:'compact',ledger:{schema:'deadbyte.capability-ledger.v1',entries:[],ledger_sha256:'0'.repeat(64)},
|
|
58
|
+
codingRuntime,semanticRuntime:null,machineFsRuntime:null,processRuntime:null,observationRuntime:null,machineRuntime:null
|
|
59
|
+
});
|
|
60
|
+
const tool=server.tools.get('coding_inspect');
|
|
61
|
+
const result=await tool.handler({op:'stat',root_id:'core',path:'src/a.mjs',hash:false});
|
|
62
|
+
assert.deepEqual(calls,[['stat',{rootId:'core',relativePath:'src/a.mjs',hash:false}]]);
|
|
63
|
+
assert.equal(result.structuredContent.status,'ok');
|
|
64
|
+
assert.equal(result.structuredContent.op,'stat');
|
|
65
|
+
assert.equal(result.structuredContent.result.path,'src/a.mjs');
|
|
66
|
+
});
|
package/test/contained.test.mjs
CHANGED
|
@@ -364,7 +364,7 @@ test('contained execution source has a compile-time worker registry and no user
|
|
|
364
364
|
assert.match(server, /z\.enum\(CONTAINED_TRANSFORM_OPERATIONS\)/);
|
|
365
365
|
assert.doesNotMatch(server, /executable:\s*z\./);
|
|
366
366
|
assert.doesNotMatch(executor, /--worker/);
|
|
367
|
-
assert.doesNotMatch(server, /command_run|shell_execute|powershell_run/);
|
|
367
|
+
assert.doesNotMatch(server, /registerTool\(\s*['"](?:command_run|shell_execute|powershell_run)['"]/);
|
|
368
368
|
});
|
|
369
369
|
|
|
370
370
|
test('contained launcher imports by stdin/profile copy and never mounts a host workspace', async () => {
|
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.10.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}$/);
|
|
@@ -12,6 +12,15 @@ test('R34 deferred slot operation preserves supervisor bootstrap across rollback
|
|
|
12
12
|
assert.match(source,/verifySlotUpdateEnvelope/);
|
|
13
13
|
assert.match(source,/runSlotUpdateCli\(args,\{stateRoot\}\)/);
|
|
14
14
|
assert.match(source,/--enable-autonomous-supervisor/);
|
|
15
|
+
assert.match(source,/loadPowerShellDataFile/);
|
|
16
|
+
assert.match(source,/active-release\.json/);
|
|
17
|
+
assert.match(source,/Bridge\.WorkingDirectory/);
|
|
18
|
+
assert.match(source,/src['"],['"]bridge\.mjs/);
|
|
19
|
+
assert.match(source,/--bridge-root/);
|
|
20
|
+
assert.match(source,/--bridge-script-sha256/);
|
|
21
|
+
assert.match(source,/explicitBridgeRoot/);
|
|
22
|
+
assert.match(source,/explicitBridgeSha256/);
|
|
23
|
+
assert.match(source,/bridge override requires exact root \+ SHA pair/i);
|
|
15
24
|
assert.match(source,/detached:true/);
|
|
16
25
|
assert.match(source,/rdc_operation_count:0/);
|
|
17
26
|
assert.doesNotMatch(source,/shell:\s*true/);
|
|
@@ -18,6 +18,8 @@ function respond(msg){
|
|
|
18
18
|
if(msg.method==='textDocument/documentSymbol') { if(documentSymbolNullCount>0){documentSymbolNullCount-=1;return send({jsonrpc:'2.0',id:msg.id,result:null});} return send({jsonrpc:'2.0',id:msg.id,result:[{name:'alpha',kind:12,range:{start:{line:0,character:0},end:{line:0,character:26}},selectionRange:{start:{line:0,character:16},end:{line:0,character:21}},children:[]}]}); }
|
|
19
19
|
if(msg.method==='textDocument/definition') { if(!Number.isInteger(msg.params?.position?.character)) return send({jsonrpc:'2.0',id:msg.id,error:{code:-32602,message:'position.character required'}}); if(!ready())return send({jsonrpc:'2.0',id:msg.id,result:null}); if(requiredOpenCount>0&&openedUris.length<requiredOpenCount)return send({jsonrpc:'2.0',id:msg.id,result:null}); const targetUri=requiredOpenCount>0?(openedUris.find(uri=>uri!==msg.params.textDocument.uri)??msg.params.textDocument.uri):msg.params.textDocument.uri; return send({jsonrpc:'2.0',id:msg.id,result:{uri:targetUri,range:{start:{line:0,character:16},end:{line:0,character:21}}}}); }
|
|
20
20
|
if(msg.method==='textDocument/references') { if(!Number.isInteger(msg.params?.position?.character)) return send({jsonrpc:'2.0',id:msg.id,error:{code:-32602,message:'position.character required'}}); if(referencesEmptyCount>0){referencesEmptyCount-=1;return send({jsonrpc:'2.0',id:msg.id,result:[]});} if(requiredOpenCount>0&&openedUris.length<requiredOpenCount)return send({jsonrpc:'2.0',id:msg.id,result:[]}); const uris=requiredOpenCount>0?openedUris:[msg.params.textDocument.uri]; return send({jsonrpc:'2.0',id:msg.id,result:uris.map(uri=>({uri,range:{start:{line:0,character:16},end:{line:0,character:21}}}))}); }
|
|
21
|
+
if(msg.method==='textDocument/implementation') { if(msg.params?.position?.character===999) return send({jsonrpc:'2.0',id:msg.id,error:{code:-32601,message:'method not found'}}); const uri=openedUris.find(x=>x!==msg.params.textDocument.uri)??msg.params.textDocument.uri; return send({jsonrpc:'2.0',id:msg.id,result:[{uri,range:{start:{line:0,character:16},end:{line:0,character:21}}}]}); }
|
|
22
|
+
if(msg.method==='textDocument/rename') { if(msg.params?.newName==='UNSUPPORTED') return send({jsonrpc:'2.0',id:msg.id,error:{code:-32601,message:'method not found'}}); const changes={}; for(const uri of (openedUris.length?openedUris:[msg.params.textDocument.uri])) changes[uri]=[{range:{start:{line:0,character:16},end:{line:0,character:21}},newText:msg.params.newName}]; return send({jsonrpc:'2.0',id:msg.id,result:{changes}}); }
|
|
21
23
|
if(msg.method==='textDocument/diagnostic') return send({jsonrpc:'2.0',id:msg.id,result:{kind:'full',items:[{range:{start:{line:0,character:0},end:{line:0,character:5}},severity:1,code:'fixture',message:'fixture diagnostic'}]}});
|
|
22
24
|
if(msg.method==='never') return;
|
|
23
25
|
if(msg.method==='oversize') return send({jsonrpc:'2.0',id:msg.id,result:{blob:'x'.repeat(65536)}});
|
|
@@ -4,6 +4,7 @@ import os from 'node:os';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { generateTrustKeypair } from '../../src/trust.mjs';
|
|
6
6
|
import { createCodingRuntime, loadCodingPolicy } from '../../src/coding-plane.mjs';
|
|
7
|
+
import { createMachineFsRuntime } from '../../src/machine-fs.mjs';
|
|
7
8
|
import { loadAutonomousPolicy } from '../../src/autonomous-policy.mjs';
|
|
8
9
|
import { createNativeDiffRuntime } from '../../src/native-diff.mjs';
|
|
9
10
|
import { createAutonomousRuntime } from '../../src/autonomous-runtime.mjs';
|
|
@@ -69,12 +70,13 @@ export async function createR34Fixture({authority='armed',withFailingProfile=fal
|
|
|
69
70
|
if(authority==='armed') await arm();
|
|
70
71
|
const baseCodingRuntime = createCodingRuntime({policy:codingPolicy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
71
72
|
const codingRuntime = codingRuntimeFactory ? codingRuntimeFactory(baseCodingRuntime) : baseCodingRuntime;
|
|
72
|
-
const
|
|
73
|
+
const machineFsRuntime = createMachineFsRuntime({policy:codingPolicy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
74
|
+
const baseNativeDiffRuntime = createNativeDiffRuntime({policy,codingRuntime,machineFsRuntime,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
73
75
|
const nativeDiffRuntime = nativeDiffRuntimeFactory ? nativeDiffRuntimeFactory(baseNativeDiffRuntime) : baseNativeDiffRuntime;
|
|
74
|
-
const createRuntime = ({codingRuntimeOverride=codingRuntime,nativeDiffRuntimeOverride=nativeDiffRuntime}={}) => createAutonomousRuntime({policy,codingRuntime:codingRuntimeOverride,nativeDiffRuntime:nativeDiffRuntimeOverride,
|
|
76
|
+
const createRuntime = ({codingRuntimeOverride=codingRuntime,nativeDiffRuntimeOverride=nativeDiffRuntime}={}) => createAutonomousRuntime({policy,codingRuntime:codingRuntimeOverride,nativeDiffRuntime:nativeDiffRuntimeOverride,machineFsRuntime,
|
|
75
77
|
signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
76
78
|
return {
|
|
77
|
-
root,project,target,keys,codingPolicy,policy,baseCodingRuntime,codingRuntime,nativeDiffRuntime,createRuntime,runtime:createRuntime(),
|
|
79
|
+
root,project,target,keys,codingPolicy,policy,baseCodingRuntime,codingRuntime,machineFsRuntime,nativeDiffRuntime,createRuntime,runtime:createRuntime(),
|
|
78
80
|
initial_sha256:sha(await readFile(target)),arm,disarm,
|
|
79
81
|
cleanup:()=>rm(root,{recursive:true,force:true})
|
|
80
82
|
};
|
|
@@ -35,6 +35,9 @@ test('coding_files_read reads bounded mixed encodings in one MCP call and fails
|
|
|
35
35
|
assert.ok((await f.client.listTools()).tools.some(t=>t.name==='coding_files_read'));
|
|
36
36
|
const result=structured(await f.client.callTool({name:'coding_files_read',arguments:{root_id:'project',files:[{path:'a.txt',encoding:'utf8'},{path:'b.txt',encoding:'utf8'},{path:'c.bin',encoding:'base64'}],max_total_bytes:64}}));
|
|
37
37
|
assert.equal(result.status,'ok'); assert.equal(result.files.length,3); assert.equal(result.total_bytes,15); assert.equal(result.files[0].content,'alpha\n'); assert.equal(result.files[2].content,Buffer.from([0,1,2,255]).toString('base64'));
|
|
38
|
+
assert.match(result.observation_sha256,/^[0-9a-f]{64}$/);
|
|
39
|
+
const repeat=structured(await f.client.callTool({name:'coding_files_read',arguments:{root_id:'project',files:[{path:'a.txt',encoding:'utf8'},{path:'b.txt',encoding:'utf8'},{path:'c.bin',encoding:'base64'}],max_total_bytes:64}}));
|
|
40
|
+
assert.equal(repeat.observation_sha256,result.observation_sha256);
|
|
38
41
|
const denied=await f.client.callTool({name:'coding_files_read',arguments:{root_id:'project',files:[{path:'a.txt',encoding:'utf8'},{path:'.git/secret',encoding:'utf8'}],max_total_bytes:64}});
|
|
39
42
|
assert.equal(denied.isError,true); assert.equal(denied.structuredContent.status,'failed'); assert.match(denied.structuredContent.error,/denied by policy/);
|
|
40
43
|
} finally {await f.client.close().catch(()=>{}); await rm(f.root,{recursive:true,force:true}).catch(()=>{});}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
4
|
+
import { access, mkdtemp, mkdir, readFile, rm, symlink, writeFile } from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { generateTrustKeypair } from '../src/trust.mjs';
|
|
8
|
+
import { createCodingRuntime, loadCodingPolicy } from '../src/coding-plane.mjs';
|
|
9
|
+
import { createMachineFsRuntime } from '../src/machine-fs.mjs';
|
|
10
|
+
import { loadAutonomousPolicy } from '../src/autonomous-policy.mjs';
|
|
11
|
+
import { createNativeDiffRuntime, NATIVE_DIFF_V2_SCHEMA } from '../src/native-diff.mjs';
|
|
12
|
+
|
|
13
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
14
|
+
const exists=file=>access(file).then(()=>true,()=>false);
|
|
15
|
+
async function fixture(){
|
|
16
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r36-topology-'));
|
|
17
|
+
const project=path.join(root,'project'); const trust=path.join(root,'trust');
|
|
18
|
+
await mkdir(project); await mkdir(trust);
|
|
19
|
+
const keys=await generateTrustKeypair({privateKeyPath:path.join(trust,'private.pem'),publicKeyPath:path.join(trust,'public.pem')});
|
|
20
|
+
const codingPath=path.join(root,'coding-policy.json');
|
|
21
|
+
await writeFile(codingPath,JSON.stringify({schema:'deadbyte.coding-policy.v1',enabled:true,lease_file:path.join(root,'coding-lease.json'),evidence_dir:path.join(root,'coding-evidence'),roots:{project:{path:project,read:true,write:true,exec:false,deny_paths:[]}},commands:{},max_read_bytes:524288,max_write_bytes:2097152}));
|
|
22
|
+
const codingPolicy=await loadCodingPolicy(codingPath);
|
|
23
|
+
await writeFile(codingPolicy.lease_file,JSON.stringify({schema:'deadbyte.coding-lease.v1',policy_sha256:codingPolicy.policy_sha256,instance_nonce:randomBytes(32).toString('hex'),root_ids:['project'],armed_at_utc:new Date().toISOString(),expires_at_utc:new Date(Date.now()+3600000).toISOString()}));
|
|
24
|
+
const autoPath=path.join(root,'autonomous-policy.json');
|
|
25
|
+
await writeFile(autoPath,JSON.stringify({schema:'deadbyte.autonomous-policy.v1',enabled:true,lease_file:path.join(root,'auto-lease.json'),evidence_dir:path.join(root,'auto-evidence'),root_ids:['project'],coding_policy_sha256:codingPolicy.policy_sha256,planner:{backend:'client_sampling',max_tokens:2048},profiles:{},limits:{max_files_per_diff:12,max_diff_bytes:1048576}}));
|
|
26
|
+
const policy=await loadAutonomousPolicy(autoPath,codingPolicy);
|
|
27
|
+
await writeFile(policy.lease_file,JSON.stringify({schema:'deadbyte.autonomous-lease.v1',policy_sha256:policy.policy_sha256,instance_nonce:randomBytes(32).toString('hex'),root_ids:['project'],armed_at_utc:new Date().toISOString(),expires_at_utc:new Date(Date.now()+1800000).toISOString()}));
|
|
28
|
+
const codingRuntime=createCodingRuntime({policy:codingPolicy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
29
|
+
const machineFsRuntime=createMachineFsRuntime({policy:codingPolicy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
30
|
+
const nativeDiff=createNativeDiffRuntime({policy,codingRuntime,machineFsRuntime,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
31
|
+
return {root,project,keys,policy,codingRuntime,machineFsRuntime,nativeDiff};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
test('R36 Native Diff V2 commits mkdir write move and independently verifies mixed receipts',async()=>{
|
|
35
|
+
const f=await fixture(); try{
|
|
36
|
+
await writeFile(path.join(f.project,'seed.txt'),'seed\n');
|
|
37
|
+
const seed=await f.machineFsRuntime.stat({rootId:'project',relativePath:'seed.txt',hash:true});
|
|
38
|
+
const preview=await f.nativeDiff.preview({rootId:'project',title:'topology commit',operations:[
|
|
39
|
+
{kind:'mkdir',path:'dst'},
|
|
40
|
+
{kind:'write',path:'dst/new.txt',expected_sha256:'absent',encoding:'utf8',content:'new\n'},
|
|
41
|
+
{kind:'move',source_path:'seed.txt',destination_path:'dst/seed.txt',expected_source_identity:seed.identity_sha256}
|
|
42
|
+
]});
|
|
43
|
+
assert.equal(preview.schema,NATIVE_DIFF_V2_SCHEMA); assert.equal(preview.operation_count,3);
|
|
44
|
+
const applied=await f.nativeDiff.apply({diffId:preview.diff_id});
|
|
45
|
+
assert.equal(applied.status,'committed');
|
|
46
|
+
assert.equal(await readFile(path.join(f.project,'dst','new.txt'),'utf8'),'new\n');
|
|
47
|
+
assert.equal(await readFile(path.join(f.project,'dst','seed.txt'),'utf8'),'seed\n');
|
|
48
|
+
assert.equal(await exists(path.join(f.project,'seed.txt')),false);
|
|
49
|
+
const verified=await f.nativeDiff.verify({diffId:preview.diff_id});
|
|
50
|
+
assert.equal(verified.ok,true); assert.equal(verified.current_state_matches,true);
|
|
51
|
+
assert.equal(verified.operation_receipts_verified,3);
|
|
52
|
+
} finally {await rm(f.root,{recursive:true,force:true});}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('R36 Native Diff V2 rolls back mkdir/write when later topology operation fails',async()=>{
|
|
56
|
+
const f=await fixture(); try{
|
|
57
|
+
await writeFile(path.join(f.project,'seed.txt'),'seed\n');
|
|
58
|
+
const seed=await f.machineFsRuntime.stat({rootId:'project',relativePath:'seed.txt',hash:true});
|
|
59
|
+
const preview=await f.nativeDiff.preview({rootId:'project',title:'topology rollback',operations:[
|
|
60
|
+
{kind:'mkdir',path:'dst'},
|
|
61
|
+
{kind:'write',path:'dst/temp.txt',expected_sha256:'absent',encoding:'utf8',content:'temporary\n'},
|
|
62
|
+
{kind:'move',source_path:'seed.txt',destination_path:'dst/seed.txt',expected_source_identity:seed.identity_sha256}
|
|
63
|
+
]});
|
|
64
|
+
const sabotaged=Object.freeze({...f.machineFsRuntime,move:async()=>{throw new Error('forced topology failure');}});
|
|
65
|
+
const runtime=createNativeDiffRuntime({policy:f.policy,codingRuntime:f.codingRuntime,machineFsRuntime:sabotaged,signingKeyPath:f.keys.privateKeyPath,verifyKeyPath:f.keys.publicKeyPath});
|
|
66
|
+
await assert.rejects(runtime.apply({diffId:preview.diff_id}),/rolled back/i);
|
|
67
|
+
assert.equal(await exists(path.join(f.project,'dst')),false);
|
|
68
|
+
assert.equal(await readFile(path.join(f.project,'seed.txt'),'utf8'),'seed\n');
|
|
69
|
+
} finally {await rm(f.root,{recursive:true,force:true});}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('R36 Native Diff V2 rejects stale move identity and destination collision before mutation',async()=>{
|
|
73
|
+
const f=await fixture(); try{
|
|
74
|
+
await writeFile(path.join(f.project,'a.txt'),'one'); await writeFile(path.join(f.project,'b.txt'),'occupied');
|
|
75
|
+
const state=await f.machineFsRuntime.stat({rootId:'project',relativePath:'a.txt',hash:true});
|
|
76
|
+
await writeFile(path.join(f.project,'a.txt'),'two');
|
|
77
|
+
await assert.rejects(f.nativeDiff.preview({rootId:'project',operations:[{kind:'move',source_path:'a.txt',destination_path:'c.txt',expected_source_identity:state.identity_sha256}]}),/identity|precondition/i);
|
|
78
|
+
const fresh=await f.machineFsRuntime.stat({rootId:'project',relativePath:'a.txt',hash:true});
|
|
79
|
+
await assert.rejects(f.nativeDiff.preview({rootId:'project',operations:[{kind:'move',source_path:'a.txt',destination_path:'b.txt',expected_source_identity:fresh.identity_sha256}]}),/destination.*absent|collision|precondition/i);
|
|
80
|
+
assert.equal(await readFile(path.join(f.project,'a.txt'),'utf8'),'two');
|
|
81
|
+
} finally {await rm(f.root,{recursive:true,force:true});}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('R36 Native Diff V2 rmdir accepts only exact empty directory identity',async()=>{
|
|
85
|
+
const f=await fixture(); try{
|
|
86
|
+
await mkdir(path.join(f.project,'empty')); await mkdir(path.join(f.project,'nonempty')); await writeFile(path.join(f.project,'nonempty','x.txt'),'x');
|
|
87
|
+
const empty=await f.machineFsRuntime.stat({rootId:'project',relativePath:'empty'});
|
|
88
|
+
const nonempty=await f.machineFsRuntime.stat({rootId:'project',relativePath:'nonempty'});
|
|
89
|
+
await assert.rejects(f.nativeDiff.preview({rootId:'project',operations:[{kind:'rmdir',path:'nonempty',expected_source_identity:nonempty.identity_sha256}]}),/empty/i);
|
|
90
|
+
const preview=await f.nativeDiff.preview({rootId:'project',operations:[{kind:'rmdir',path:'empty',expected_source_identity:empty.identity_sha256}]});
|
|
91
|
+
assert.equal((await f.nativeDiff.apply({diffId:preview.diff_id})).status,'committed');
|
|
92
|
+
assert.equal(await exists(path.join(f.project,'empty')),false);
|
|
93
|
+
assert.equal((await f.nativeDiff.verify({diffId:preview.diff_id})).ok,true);
|
|
94
|
+
} finally {await rm(f.root,{recursive:true,force:true});}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('R36 Native Diff V2 denies topology through reparse points',async t=>{
|
|
98
|
+
const f=await fixture(); try{
|
|
99
|
+
const outside=path.join(f.root,'outside'); await mkdir(outside);
|
|
100
|
+
const link=path.join(f.project,'link');
|
|
101
|
+
try { await symlink(outside,link,process.platform==='win32'?'junction':'dir'); }
|
|
102
|
+
catch(error){ if(['EPERM','EACCES','ENOSYS'].includes(error?.code)){t.skip(`symlink unavailable: ${error.code}`);return;} throw error; }
|
|
103
|
+
await assert.rejects(f.nativeDiff.preview({rootId:'project',operations:[{kind:'rmdir',path:'link',expected_source_identity:'0'.repeat(64)}]}),/reparse|symlink/i);
|
|
104
|
+
} finally {await rm(f.root,{recursive:true,force:true});}
|
|
105
|
+
});
|
|
@@ -2,6 +2,7 @@ import test from 'node:test';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { readdirSync } from 'node:fs';
|
|
5
6
|
import { fileURLToPath } from 'node:url';
|
|
6
7
|
|
|
7
8
|
const root = path.resolve(fileURLToPath(new URL('..', import.meta.url)));
|
|
@@ -22,6 +23,11 @@ function packFiles() {
|
|
|
22
23
|
return entry.files.map(item => item.path.replaceAll('\\', '/')).sort();
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
test('R36 successor source root contains no nested npm tarball artifacts',()=>{
|
|
27
|
+
const tarballs=readdirSync(root,{withFileTypes:true}).filter(item=>item.isFile()&&item.name.endsWith('.tgz')).map(item=>item.name).sort();
|
|
28
|
+
assert.deepEqual(tarballs,[]);
|
|
29
|
+
});
|
|
30
|
+
|
|
25
31
|
test('R28 package boundary contains only immutable workspace seed files', () => {
|
|
26
32
|
const files = packFiles();
|
|
27
33
|
const workspacePayload = files.filter(rel => rel.startsWith('workspaces/'));
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { PROCESS_GRANT_SCHEMA, PROCESS_POLICY_SCHEMA, loadProcessPolicy } from '../src/process-policy.mjs';
|
|
9
|
+
import { createProcessRuntime } from '../src/process-runtime.mjs';
|
|
10
|
+
import { generateTrustKeypair } from '../src/trust.mjs';
|
|
11
|
+
|
|
12
|
+
const repo=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
|
13
|
+
const host=path.join(repo,'bin','deadbyte-process-host.exe');
|
|
14
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
15
|
+
const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
|
|
16
|
+
|
|
17
|
+
async function fixture(){
|
|
18
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r36-longrun-'));
|
|
19
|
+
const project=path.join(root,'project'); const trust=path.join(root,'trust');
|
|
20
|
+
await mkdir(project); await mkdir(trust);
|
|
21
|
+
const script=path.join(project,'longrun.mjs');
|
|
22
|
+
await writeFile(script,`process.stdout.write('chunk-1\\n');\nsetTimeout(()=>process.stdout.write('chunk-2\\n'),180);\nsetTimeout(()=>process.stdout.write('chunk-3\\n'),360);\nsetTimeout(()=>process.exit(0),540);\n`);
|
|
23
|
+
const grantFile=path.join(root,'process-grant.json'); const stateDir=path.join(root,'state'); const policyPath=path.join(root,'process-policy.json');
|
|
24
|
+
const raw={schema:PROCESS_POLICY_SCHEMA,enabled:true,grant_file:grantFile,state_dir:stateDir,native_host:{path:host,sha256:sha(await readFile(host))},roots:{core:{path:project,deny_paths:[]}},profiles:{longrun:{executable:process.execPath,executable_sha256:sha(await readFile(process.execPath)),root_ids:['core'],transport:'pipe',prefix_args:[script],max_args:0,max_arg_bytes:256,max_sessions:1,max_input_bytes:4096,max_retained_output_bytes:65536,env_allow:['PATH','TEMP','TMP'],input_actions:{}}}};
|
|
25
|
+
await writeFile(policyPath,JSON.stringify(raw));
|
|
26
|
+
const policy=await loadProcessPolicy(policyPath);
|
|
27
|
+
await writeFile(grantFile,JSON.stringify({schema:PROCESS_GRANT_SCHEMA,policy_sha256:policy.policy_sha256,instance_nonce:'6'.repeat(64),profile_ids:['longrun'],root_ids:['core'],granted_at_utc:new Date().toISOString()}));
|
|
28
|
+
const keys=await generateTrustKeypair({privateKeyPath:path.join(trust,'private.pem'),publicKeyPath:path.join(trust,'public.pem')});
|
|
29
|
+
const make=()=>createProcessRuntime({policy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
30
|
+
return {root,project,stateDir,policy,keys,make,runtime:make()};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function waitState(runtime,id,state,timeout=5000){const end=Date.now()+timeout;while(Date.now()<end){const s=await runtime.status({sessionId:id});if(s.state===state)return s;await sleep(30);}throw new Error(`timeout waiting ${state}`);}
|
|
34
|
+
|
|
35
|
+
test('R36 process runtime publishes terminal state only after durable terminal journal commit', async()=>{
|
|
36
|
+
const source=await readFile(path.join(repo,'src','process-runtime.mjs'),'utf8');
|
|
37
|
+
assert.match(source,/const terminalState = session\.terminate_requested \? 'terminated' : 'exited'/);
|
|
38
|
+
const commit=source.indexOf("await appendEvent(runtime, session, terminalState");
|
|
39
|
+
const publish=source.indexOf('session.state = terminalState;',commit);
|
|
40
|
+
assert.ok(commit>=0&&publish>commit,'terminal state must publish after terminal event commit');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('R36 long process returns before exit and exposes monotonic cursor chunks', {skip:process.platform!=='win32'}, async()=>{
|
|
44
|
+
const f=await fixture(); try{
|
|
45
|
+
const startedAt=Date.now();
|
|
46
|
+
const started=await f.runtime.start({profileId:'longrun',rootId:'core',cwd:'.',args:[]});
|
|
47
|
+
assert.equal(started.state,'running');
|
|
48
|
+
assert.ok(Date.now()-startedAt<500,'start waited for child exit');
|
|
49
|
+
let offset=0; let text=''; let sawPartial=false;
|
|
50
|
+
const end=Date.now()+5000;
|
|
51
|
+
while(Date.now()<end){
|
|
52
|
+
const page=await f.runtime.read({sessionId:started.session_id,offset,maxBytes:8});
|
|
53
|
+
assert.ok(page.next_offset>=offset); assert.ok(page.next_offset<=page.stream_head);
|
|
54
|
+
if(page.next_offset<page.stream_head||page.state==='running') sawPartial=true;
|
|
55
|
+
text+=Buffer.from(page.data_base64,'base64').toString('utf8'); offset=page.next_offset;
|
|
56
|
+
if(page.state==='exited'&&offset===page.stream_head) break;
|
|
57
|
+
await sleep(35);
|
|
58
|
+
}
|
|
59
|
+
assert.equal(sawPartial,true); assert.match(text,/chunk-1/); assert.match(text,/chunk-2/); assert.match(text,/chunk-3/);
|
|
60
|
+
const terminal=await waitState(f.runtime,started.session_id,'exited'); assert.equal(terminal.exit_code,0);
|
|
61
|
+
await assert.rejects(()=>f.runtime.status({sessionId:'f'.repeat(64)}),/unknown process session/i);
|
|
62
|
+
} finally {await rm(f.root,{recursive:true,force:true});}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('R36 terminal process session is recovered from signed journal after wrapper restart', {skip:process.platform!=='win32'}, async()=>{
|
|
66
|
+
const f=await fixture(); try{
|
|
67
|
+
const started=await f.runtime.start({profileId:'longrun',rootId:'core',cwd:'.',args:[]});
|
|
68
|
+
await waitState(f.runtime,started.session_id,'exited');
|
|
69
|
+
const original=await f.runtime.status({sessionId:started.session_id});
|
|
70
|
+
const checked=await f.runtime.verifyEventJournal({sessionId:started.session_id}); assert.equal(checked.ok,true);
|
|
71
|
+
const restarted=f.make();
|
|
72
|
+
const recovered=await restarted.status({sessionId:started.session_id});
|
|
73
|
+
assert.equal(recovered.state,'exited'); assert.equal(recovered.exit_code,0); assert.equal(recovered.session_id,started.session_id);
|
|
74
|
+
assert.equal(recovered.journal_head_sha256,original.journal_head_sha256);
|
|
75
|
+
const listed=await restarted.list(); assert.ok(listed.sessions.some(s=>s.session_id===started.session_id));
|
|
76
|
+
const page=await restarted.read({sessionId:started.session_id,offset:0,maxBytes:65536});
|
|
77
|
+
const text=Buffer.from(page.data_base64,'base64').toString('utf8');
|
|
78
|
+
assert.match(text,/chunk-1/); assert.match(text,/chunk-3/); assert.equal(page.state,'exited');
|
|
79
|
+
const verified=await restarted.verifyEventJournal({sessionId:started.session_id}); assert.equal(verified.ok,true); assert.equal(verified.head_sha256,recovered.journal_head_sha256);
|
|
80
|
+
await assert.rejects(()=>restarted.status({sessionId:'e'.repeat(64)}),/unknown process session/i);
|
|
81
|
+
} finally {await rm(f.root,{recursive:true,force:true});}
|
|
82
|
+
});
|
|
@@ -5,12 +5,21 @@ import { fileURLToPath } from 'node:url';
|
|
|
5
5
|
|
|
6
6
|
const packagePath = fileURLToPath(new URL('../package.json', import.meta.url));
|
|
7
7
|
const gatePath = fileURLToPath(new URL('../scripts/gate-windows.ps1', import.meta.url));
|
|
8
|
+
const processSmokePath = fileURLToPath(new URL('../src/process-smoke-client.mjs', import.meta.url));
|
|
8
9
|
|
|
9
10
|
test('R31 package exposes the bounded process smoke command', async () => {
|
|
10
11
|
const pkg = JSON.parse(await readFile(packagePath, 'utf8'));
|
|
11
12
|
assert.equal(pkg.scripts['mcp:process:smoke'], 'node src/process-smoke-client.mjs');
|
|
12
13
|
});
|
|
13
14
|
|
|
15
|
+
test('R36 process smoke isolates grant and recovery state outside the release source tree', async () => {
|
|
16
|
+
const source = await readFile(processSmokePath, 'utf8');
|
|
17
|
+
assert.match(source, /smoke-policy\.json/);
|
|
18
|
+
assert.match(source, /grant_file:\s*path\.join\(smokeTrust,\s*'process-grant\.json'\)/);
|
|
19
|
+
assert.match(source, /state_dir:\s*path\.join\(smokeTrust,\s*'process-state'\)/);
|
|
20
|
+
assert.match(source, /DEADBYTE_PROCESS_POLICY:\s*smokePolicyPath/);
|
|
21
|
+
});
|
|
22
|
+
|
|
14
23
|
test('R31 Windows gate refreshes the native-host policy pin before manifest freeze', async () => {
|
|
15
24
|
const pkg = JSON.parse(await readFile(packagePath, 'utf8'));
|
|
16
25
|
const source = await readFile(gatePath, 'utf8');
|
|
@@ -9,9 +9,9 @@ const readme=()=>readFile(path.join(root,'README.txt'),'utf8');
|
|
|
9
9
|
|
|
10
10
|
test('production README documents one-command stdio configs for major MCP clients',async()=>{
|
|
11
11
|
const text=await readme();
|
|
12
|
-
assert.match(text,/npx -y deadbyte-mcp@0\.
|
|
12
|
+
assert.match(text,/npx -y deadbyte-mcp@0\.10\.0 mcp/);
|
|
13
13
|
assert.match(text,/CLAUDE DESKTOP/);
|
|
14
|
-
assert.match(text,/"mcpServers"[\s\S]*?"command"\s*:\s*"npx"[\s\S]*?"-y"[\s\S]*?"deadbyte-mcp@0\.
|
|
14
|
+
assert.match(text,/"mcpServers"[\s\S]*?"command"\s*:\s*"npx"[\s\S]*?"-y"[\s\S]*?"deadbyte-mcp@0\.10\.0"[\s\S]*?"mcp"/);
|
|
15
15
|
assert.match(text,/CURSOR/);
|
|
16
16
|
assert.match(text,/\.cursor\/mcp\.json/);
|
|
17
17
|
assert.match(text,/VS CODE/);
|
|
@@ -20,7 +20,7 @@ test('production README documents one-command stdio configs for major MCP client
|
|
|
20
20
|
assert.match(text,/CODEX/);
|
|
21
21
|
assert.match(text,/\[mcp_servers\.deadbyte\]/);
|
|
22
22
|
assert.match(text,/command\s*=\s*"npx"/);
|
|
23
|
-
assert.match(text,/args\s*=\s*\["-y",\s*"deadbyte-mcp@0\.
|
|
23
|
+
assert.match(text,/args\s*=\s*\["-y",\s*"deadbyte-mcp@0\.10\.0",\s*"mcp"\]/);
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
test('production README records tracer bullet, semantic scope, remote UX, safety and document extension',async()=>{
|
|
@@ -24,7 +24,13 @@ test('MCP exposes compact workflow prompts and source-of-truth context resources
|
|
|
24
24
|
|
|
25
25
|
const resources=await client.listResources();
|
|
26
26
|
const uris=resources.resources.map(r=>r.uri).sort();
|
|
27
|
-
assert.deepEqual(uris,['deadbyte://architecture','deadbyte://context']);
|
|
27
|
+
assert.deepEqual(uris,['deadbyte://architecture','deadbyte://context','deadbyte://surface-profile']);
|
|
28
|
+
const surface=await client.readResource({uri:'deadbyte://surface-profile'});
|
|
29
|
+
const surfaceText=surface.contents.map(c=>'text' in c?c.text:'').join('\n');
|
|
30
|
+
const surfaceState=JSON.parse(surfaceText);
|
|
31
|
+
assert.equal(surfaceState.schema,'deadbyte.surface-profile.v1');
|
|
32
|
+
assert.equal(surfaceState.profile,'full');
|
|
33
|
+
assert.equal(surfaceState.compact_tool_limit,42);
|
|
28
34
|
const context=await client.readResource({uri:'deadbyte://context'});
|
|
29
35
|
const contextText=context.contents.map(c=>'text' in c?c.text:'').join('\n');
|
|
30
36
|
assert.match(contextText,/Machine truth/i); assert.match(contextText,/Authority plane/i); assert.match(contextText,/Loop Supervisor/i);
|
|
@@ -86,7 +86,7 @@ test('audit restart reconciles an orphan request with an explicit interrupted re
|
|
|
86
86
|
}finally{await rm(stateRoot,{recursive:true,force:true});}
|
|
87
87
|
});
|
|
88
88
|
|
|
89
|
-
test('Windows
|
|
89
|
+
test('Windows R36 gate emits manifest-bound machine-readable evidence outside the candidate tree', async()=>{
|
|
90
90
|
const stateRoot=await temp('deadbyte-r33-windows-evidence-');
|
|
91
91
|
try{
|
|
92
92
|
const gate=await readFile(path.join(root,'scripts','gate-windows.ps1'),'utf8');
|
|
@@ -99,11 +99,12 @@ test('Windows R35 gate emits manifest-bound machine-readable evidence outside th
|
|
|
99
99
|
assert.equal(summary.status,'written');
|
|
100
100
|
const evidence=JSON.parse(await readFile(summary.path,'utf8'));
|
|
101
101
|
const manifestSha=sha256(await readFile(path.join(root,'MANIFEST.SHA256')));
|
|
102
|
-
assert.equal(evidence.schema,'deadbyte.
|
|
102
|
+
assert.equal(evidence.schema,'deadbyte.r36-windows-gate.v1');
|
|
103
103
|
assert.equal(evidence.status,'passed');
|
|
104
104
|
assert.equal(evidence.exit_code,0);
|
|
105
|
+
assert.equal(evidence.version,'0.10.0');
|
|
105
106
|
assert.equal(evidence.manifest_sha256,manifestSha);
|
|
106
|
-
assert.equal(evidence.marker,'DEADBYTE V0.
|
|
107
|
+
assert.equal(evidence.marker,'DEADBYTE V0.10.0 / R36 WINDOWS BATCH GATE: PASS');
|
|
107
108
|
assert.ok(summary.path.startsWith(path.join(stateRoot,'evidence','release')));
|
|
108
109
|
}finally{await rm(stateRoot,{recursive:true,force:true});}
|
|
109
110
|
});
|
|
@@ -26,16 +26,17 @@ test('release parity and final verifier are package entry points', async () => {
|
|
|
26
26
|
assert.equal(typeof verifyFinalClosureObject, 'function');
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
test('final closure verifier is implementation-independent from production helpers', async () => {
|
|
29
|
+
test('R36 final closure verifier is implementation-independent from production helpers', async () => {
|
|
30
30
|
const verifier = await text('scripts/final-closure-verify.mjs');
|
|
31
31
|
const writer = await text('scripts/final-closure.mjs');
|
|
32
32
|
assert.doesNotMatch(verifier, /from ['"]\.\.\/src\//);
|
|
33
33
|
assert.match(verifier, /DEADBYTE-R27-SLOT-UPDATE-V1/);
|
|
34
34
|
assert.match(verifier, /DEADBYTE-MACHINE-PING-V1/);
|
|
35
|
-
assert.match(verifier, /DEADBYTE-
|
|
36
|
-
assert.match(writer, /pointer\.version==='0\.
|
|
37
|
-
assert.match(verifier, /envelope\.body\.version==='0\.
|
|
38
|
-
assert.match(verifier, /release_label==='
|
|
35
|
+
assert.match(verifier, /DEADBYTE-R36-FINAL-CLOSURE-V1/);
|
|
36
|
+
assert.match(writer, /pointer\.version==='0\.10\.0'/);
|
|
37
|
+
assert.match(verifier, /envelope\.body\.version==='0\.10\.0'/);
|
|
38
|
+
assert.match(verifier, /release_label==='R36 FINAL MACHINE BASELINE'/);
|
|
39
|
+
assert.match(verifier, /b95e1ec1d9a7e26a96ef492306c1be4df18c883ff03ab3b6015f677d7429ec8d/);
|
|
39
40
|
});
|
|
40
41
|
|
|
41
42
|
test('final closure verifier canonicalizes traversal order before exact-tree comparison', async () => {
|
|
@@ -44,11 +45,15 @@ test('final closure verifier canonicalizes traversal order before exact-tree com
|
|
|
44
45
|
assert.match(verifier, /const expected=\[\.\.\.entries\.keys\(\)\]\.sort\(\(a,b\)=>a\.localeCompare\(b,'en'\)\);/);
|
|
45
46
|
});
|
|
46
47
|
|
|
47
|
-
test('autonomous live gate
|
|
48
|
+
test('R36 autonomous live gate binds the compact gateway contract instead of the historical 73-tool public catalog', async () => {
|
|
48
49
|
const autonomous = await text('src/remote-mcp-autonomous-smoke-client.mjs');
|
|
49
|
-
assert.match(autonomous, /
|
|
50
|
-
assert.
|
|
51
|
-
assert.match(autonomous, /
|
|
50
|
+
assert.match(autonomous, /R36_COMPACT_GATEWAYS/);
|
|
51
|
+
assert.match(autonomous, /R36_COMPACT_TOOL_LIMIT/);
|
|
52
|
+
assert.match(autonomous, /names\.length > R36_COMPACT_TOOL_LIMIT/);
|
|
53
|
+
for (const name of ['autonomous_goal_submit','autonomous_goal_run','autonomous_goal_verify','autonomous_release_gate','native_diff_apply','native_diff_verify']) {
|
|
54
|
+
assert.match(autonomous,new RegExp(name));
|
|
55
|
+
}
|
|
56
|
+
assert.doesNotMatch(autonomous, /R34_EXPECTED_TOOLS/);
|
|
52
57
|
});
|
|
53
58
|
|
|
54
59
|
test('R34 parity proves coding package tests before package-visible scratch mutation', async () => {
|