deadbyte-mcp 0.15.1 → 0.15.3
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 +95 -64
- package/PROVENANCE.json +6 -5
- package/README.txt +36 -20
- package/RELEASE.json +24 -8
- package/SBOM.cdx.json +3 -3
- package/controller/README.TXT +2 -2
- package/controller/deadbyte-controller.ps1 +50 -2
- package/controller/deadbyte-process-policy.json +63 -1
- package/docs/ARCHITECTURE.md +3 -4
- package/package.json +88 -86
- package/scripts/autonomous-build.ps1 +1 -0
- package/scripts/autonomous-tests.ps1 +1 -0
- package/scripts/check-agent-backend-r46.mjs +77 -0
- package/scripts/check-atomic-resource-r46.mjs +84 -0
- package/scripts/check-autonomous-race-r46.mjs +160 -0
- package/scripts/check-coding-task-deadline-r46.mjs +59 -0
- package/scripts/check-deadline-cancellation-r46.mjs +49 -0
- package/scripts/check-process-deadline-r46.mjs +52 -0
- package/scripts/check-r46-background.mjs +5 -0
- package/scripts/deferred-slot-operation-r46.mjs +126 -0
- package/scripts/extract-r46-failures.mjs +5 -0
- package/scripts/final-closure-r46.mjs +98 -0
- package/scripts/final-closure-verify-r46.mjs +119 -0
- package/scripts/full-regression-r46.mjs +28 -0
- package/scripts/gate-windows-r46.ps1 +58 -0
- package/scripts/inspect-r46-active.mjs +2 -0
- package/scripts/r47-core-failures.mjs +1 -0
- package/scripts/refresh-process-policy.mjs +64 -7
- package/scripts/release-manifest.mjs +1 -1
- package/scripts/release-parity-r46.mjs +187 -0
- package/scripts/run-r46-full-regression-background.mjs +7 -0
- package/scripts/run-r46-windows-gate-background.mjs +6 -0
- package/scripts/run-r46-windows-gate-background.ps1 +10 -0
- package/scripts/windows-gate-evidence-r46.mjs +111 -0
- package/scripts/workload-contract-regression-r47.mjs +14 -0
- package/scripts/workload-stream-regression.mjs +16 -0
- package/scripts/workload-substrate-r47-regression.mjs +12 -0
- package/src/agent-backend-r44.mjs +75 -10
- package/src/atomic-mutation.mjs +55 -0
- package/src/autonomous-mcp-tools.mjs +4 -3
- package/src/autonomous-output-contracts.mjs +16 -3
- package/src/autonomous-runtime.mjs +83 -30
- package/src/capability-ledger.mjs +1 -0
- package/src/coding-mcp-tools.mjs +2 -2
- package/src/coding-output-contracts.mjs +18 -4
- package/src/coding-plane.mjs +3 -24
- package/src/coding-search.mjs +11 -2
- package/src/coding-task-runtime.mjs +41 -6
- package/src/compact-gateway-mcp-tools.mjs +7 -5
- package/src/deadbyte-cli.mjs +7 -3
- package/src/deadline-cancellation.mjs +83 -0
- package/src/machine-fs-mcp-tools.mjs +3 -3
- package/src/machine-fs-output-contracts.mjs +1 -1
- package/src/machine-fs-read-worker.mjs +46 -0
- package/src/machine-fs-smoke-client.mjs +1 -1
- package/src/machine-fs.mjs +66 -33
- package/src/mcp-server.mjs +10 -0
- package/src/mcp-smoke-client.mjs +4 -1
- package/src/observation-runtime.mjs +25 -4
- package/src/observation-smoke-client.mjs +1 -1
- package/src/process-mcp-tools.mjs +29 -8
- package/src/process-output-contracts.mjs +43 -3
- package/src/process-policy.mjs +3 -1
- package/src/process-runtime.mjs +152 -23
- package/src/process-smoke-client.mjs +67 -22
- package/src/release-generation.mjs +22 -3
- package/src/remote-device-r44.mjs +53 -15
- package/src/remote-mcp-smoke-client.mjs +1 -1
- package/src/resource-identity.mjs +59 -0
- package/src/runtime-update.mjs +526 -162
- package/src/stdio-console-guard.mjs +24 -0
- package/src/version.mjs +1 -1
- package/src/workload-substrate.mjs +87 -0
- package/test/cli-entrypoint.test.mjs +3 -3
- package/test/coding-runtime.test.mjs +12 -0
- package/test/coding-search-r36.test.mjs +7 -0
- package/test/compact-gateway-r36.test.mjs +28 -0
- package/test/core.test.mjs +1 -1
- package/test/full-max-catalog-r46.test.mjs +92 -0
- package/test/helpers/autonomous-r34-fixture.mjs +10 -1
- package/test/machine-fs-mcp-tools.test.mjs +4 -1
- package/test/machine-fs-runtime.test.mjs +18 -2
- package/test/observation-runtime.test.mjs +29 -2
- package/test/process-mcp-tools.test.mjs +27 -6
- package/test/process-policy-refresh.test.mjs +87 -10
- package/test/process-runtime.test.mjs +129 -2
- package/test/process-supervision.test.mjs +4 -2
- package/test/production-docs.test.mjs +3 -3
- package/test/r33-finalization.test.mjs +3 -3
- package/test/r45-finalization.test.mjs +1 -1
- package/test/release-generation.test.mjs +25 -11
- package/test/release-version.test.mjs +99 -44
- package/test/remote-device-r44.test.mjs +75 -0
- package/test/remote-r24-catalog.test.mjs +2 -2
- package/test/runtime-self-update.test.mjs +118 -1
- package/test/workload-substrate.test.mjs +22 -0
|
@@ -57,19 +57,34 @@ function parseDeviceKey(spki){
|
|
|
57
57
|
insist(key.asymmetricKeyType==='ed25519','remote device key must be Ed25519');
|
|
58
58
|
return {deviceId:sha(der),spki};
|
|
59
59
|
}
|
|
60
|
-
function publicDevice(d){
|
|
60
|
+
function publicDevice(d,reachableTtlMs=Infinity){
|
|
61
|
+
const lastOk=typeof d.last_probe_ok_at_utc==='string'?Date.parse(d.last_probe_ok_at_utc):NaN;
|
|
62
|
+
const fresh=d.reachable===true&&(reachableTtlMs===Infinity||(Number.isFinite(lastOk)&&Date.now()-lastOk<=reachableTtlMs));
|
|
61
63
|
return {device_id:d.device_id,display_name:d.display_name,epoch:d.epoch,revoked:d.revoked,
|
|
62
|
-
reported_online:d.reported_online,reachable:
|
|
64
|
+
reported_online:d.reported_online,reachable:fresh,connection_id:d.connection_id,
|
|
65
|
+
last_probe_ok_at_utc:d.last_probe_ok_at_utc??null,updated_at_utc:d.updated_at_utc};
|
|
66
|
+
}
|
|
67
|
+
async function withTimeout(operation,timeoutMs,label){
|
|
68
|
+
let timer;
|
|
69
|
+
try{
|
|
70
|
+
return await Promise.race([
|
|
71
|
+
Promise.resolve().then(operation),
|
|
72
|
+
new Promise((_,reject)=>{timer=setTimeout(()=>reject(new Error(`${label} timed out after ${timeoutMs}ms`)),timeoutMs);timer.unref?.();})
|
|
73
|
+
]);
|
|
74
|
+
}finally{if(timer)clearTimeout(timer);}
|
|
63
75
|
}
|
|
64
76
|
function payloadHash({deviceId,epoch,capabilityId,payload}){
|
|
65
77
|
return sha(Buffer.from(canonicalJson({device_id:deviceId,epoch,capability_id:capabilityId,payload}),'utf8'));
|
|
66
78
|
}
|
|
67
79
|
|
|
68
|
-
export async function createRemoteDevicePlaneR44({stateDir,relay,authorizeCapability,onCredentialSuperseded=null}={}){
|
|
80
|
+
export async function createRemoteDevicePlaneR44({stateDir,relay,authorizeCapability,onCredentialSuperseded=null,relayTimeoutMs=15000,probeRetries=1,reachableTtlMs=75000}={}){
|
|
69
81
|
insist(typeof stateDir==='string'&&path.isAbsolute(stateDir),'remote stateDir must be absolute');
|
|
70
82
|
insist(relay&&typeof relay.probe==='function'&&typeof relay.request==='function','remote relay contract invalid');
|
|
71
83
|
insist(typeof authorizeCapability==='function','remote capability authorizer required');
|
|
72
84
|
insist(onCredentialSuperseded===null||typeof onCredentialSuperseded==='function','remote credential supersede callback invalid');
|
|
85
|
+
insist(Number.isInteger(relayTimeoutMs)&&relayTimeoutMs>=100&&relayTimeoutMs<=120000,'remote relayTimeoutMs invalid');
|
|
86
|
+
insist(Number.isInteger(probeRetries)&&probeRetries>=0&&probeRetries<=3,'remote probeRetries invalid');
|
|
87
|
+
insist(Number.isInteger(reachableTtlMs)&&reachableTtlMs>=1000&&reachableTtlMs<=10*60*1000,'remote reachableTtlMs invalid');
|
|
73
88
|
await mkdir(stateDir,{recursive:true});const root=await realpath(stateDir);
|
|
74
89
|
const stateFile=path.join(root,'remote-devices.json'),credentialsDir=path.join(root,'credentials');
|
|
75
90
|
await mkdir(credentialsDir,{recursive:true});
|
|
@@ -79,6 +94,8 @@ export async function createRemoteDevicePlaneR44({stateDir,relay,authorizeCapabi
|
|
|
79
94
|
const run=lock.then(fn,fn);lock=run.catch(()=>{});return run;
|
|
80
95
|
};
|
|
81
96
|
const persist=()=>{state.updated_at_utc=now();return atomicJson(stateFile,state);};
|
|
97
|
+
const pub=d=>publicDevice(d,reachableTtlMs);
|
|
98
|
+
const fresh=d=>pub(d).reachable;
|
|
82
99
|
const getDevice=(deviceId,epoch=null)=>{
|
|
83
100
|
insist(HEX64.test(deviceId??''),'device id invalid');
|
|
84
101
|
const d=state.devices[deviceId];insist(d,'unknown remote device');
|
|
@@ -95,21 +112,21 @@ export async function createRemoteDevicePlaneR44({stateDir,relay,authorizeCapabi
|
|
|
95
112
|
if(existing){
|
|
96
113
|
insist(!existing.revoked,'remote device revoked');
|
|
97
114
|
insist(existing.public_key_spki_base64===spki,'remote device key drift');
|
|
98
|
-
return
|
|
115
|
+
return pub(existing);
|
|
99
116
|
}
|
|
100
117
|
const d={device_id:deviceId,public_key_spki_base64:spki,display_name:displayName,epoch:1,revoked:false,
|
|
101
118
|
reported_online:false,reachable:false,connection_id:null,last_probe_nonce:null,updated_at_utc:now()};
|
|
102
|
-
state.devices[deviceId]=d;await persist();return
|
|
119
|
+
state.devices[deviceId]=d;await persist();return pub(d);
|
|
103
120
|
});
|
|
104
121
|
}
|
|
105
122
|
async function status({deviceId}={}){
|
|
106
|
-
const d=getDevice(deviceId);return
|
|
123
|
+
const d=getDevice(deviceId);return pub(d);
|
|
107
124
|
}
|
|
108
125
|
async function reportOnline({deviceId,epoch,connectionId}={}){
|
|
109
126
|
insist(typeof connectionId==='string'&&connectionId.length>=1&&connectionId.length<=256,'connection id invalid');
|
|
110
127
|
return withLock(async()=>{
|
|
111
128
|
const d=getDevice(deviceId,epoch);d.reported_online=true;d.reachable=false;d.connection_id=connectionId;
|
|
112
|
-
d.last_probe_nonce=null;d.updated_at_utc=now();await persist();return
|
|
129
|
+
d.last_probe_nonce=null;d.updated_at_utc=now();await persist();return pub(d);
|
|
113
130
|
});
|
|
114
131
|
}
|
|
115
132
|
async function probe({deviceId,epoch}={}){
|
|
@@ -119,12 +136,25 @@ export async function createRemoteDevicePlaneR44({stateDir,relay,authorizeCapabi
|
|
|
119
136
|
const d=getDevice(deviceId,epoch);insist(d.reported_online&&d.connection_id,'remote device is not reported online');
|
|
120
137
|
connectionId=d.connection_id;d.reachable=false;d.last_probe_nonce=nonce;d.updated_at_utc=now();await persist();
|
|
121
138
|
});
|
|
122
|
-
|
|
139
|
+
let response=null,lastError=null;
|
|
140
|
+
for(let attempt=0;attempt<=probeRetries;attempt+=1){
|
|
141
|
+
try{
|
|
142
|
+
response=await withTimeout(
|
|
143
|
+
()=>relay.probe({device_id:deviceId,epoch,connection_id:connectionId,nonce}),
|
|
144
|
+
relayTimeoutMs,'remote readiness probe'
|
|
145
|
+
);
|
|
146
|
+
lastError=null;break;
|
|
147
|
+
}catch(error){lastError=error;}
|
|
148
|
+
}
|
|
149
|
+
if(lastError){
|
|
150
|
+
await withLock(async()=>{const d=getDevice(deviceId,epoch);if(d.connection_id===connectionId){d.reachable=false;d.updated_at_utc=now();await persist();}});
|
|
151
|
+
throw lastError;
|
|
152
|
+
}
|
|
123
153
|
return withLock(async()=>{
|
|
124
154
|
const d=getDevice(deviceId,epoch);
|
|
125
155
|
insist(response&&response.device_id===deviceId&&response.epoch===epoch&&response.nonce===nonce&&response.ready===true,'remote readiness probe acknowledgment invalid');
|
|
126
156
|
insist(d.connection_id===connectionId&&d.last_probe_nonce===nonce,'remote connection changed during readiness probe');
|
|
127
|
-
d.reachable=true;d.updated_at_utc=now();await persist();return {...
|
|
157
|
+
d.reachable=true;d.last_probe_ok_at_utc=now();d.updated_at_utc=now();await persist();return {...pub(d),probe_nonce:nonce};
|
|
128
158
|
});
|
|
129
159
|
}
|
|
130
160
|
async function dispatch({deviceId,epoch,capabilityId,payload={},requestId}={}){
|
|
@@ -141,7 +171,7 @@ export async function createRemoteDevicePlaneR44({stateDir,relay,authorizeCapabi
|
|
|
141
171
|
const allowed=await authorizeCapability({deviceId,epoch,capabilityId});
|
|
142
172
|
insist(allowed===true,'remote capability denied by local policy');
|
|
143
173
|
await withLock(async()=>{
|
|
144
|
-
const d=getDevice(deviceId,epoch);insist(d.reported_online&&d
|
|
174
|
+
const d=getDevice(deviceId,epoch);insist(d.reported_online&&fresh(d),'remote device not reachable or readiness probe is stale');
|
|
145
175
|
const again=state.requests[requestId];
|
|
146
176
|
if(again){insist(again.request_sha256===requestSha,'remote request id conflict');return;}
|
|
147
177
|
state.requests[requestId]={request_id:requestId,request_sha256:requestSha,device_id:deviceId,epoch,capability_id:capabilityId,
|
|
@@ -150,9 +180,17 @@ export async function createRemoteDevicePlaneR44({stateDir,relay,authorizeCapabi
|
|
|
150
180
|
});
|
|
151
181
|
if(state.requests[requestId].state!=='accepted')return {...structuredClone(state.requests[requestId]),replay:true};
|
|
152
182
|
let response=null;
|
|
153
|
-
try{
|
|
154
|
-
|
|
155
|
-
|
|
183
|
+
try{
|
|
184
|
+
response=await withTimeout(
|
|
185
|
+
()=>relay.request({device_id:deviceId,epoch,capability_id:capabilityId,payload,request_id:requestId,request_sha256:requestSha}),
|
|
186
|
+
relayTimeoutMs,'remote dispatch'
|
|
187
|
+
);
|
|
188
|
+
}catch(error){
|
|
189
|
+
return withLock(async()=>{
|
|
190
|
+
const rec=state.requests[requestId];rec.state='failed';rec.error_code='transport_error';rec.error=String(error?.message??error);rec.updated_at_utc=now();
|
|
191
|
+
const d=state.devices[deviceId];if(d&&d.epoch===epoch){d.reachable=false;d.updated_at_utc=now();}
|
|
192
|
+
await persist();return structuredClone(rec);
|
|
193
|
+
});
|
|
156
194
|
}
|
|
157
195
|
if(!response||response.ack!=='accepted'||response.request_id!==requestId){
|
|
158
196
|
return withLock(async()=>{const rec=state.requests[requestId];rec.state='failed';rec.error_code='transport_no_ack';rec.updated_at_utc=now();await persist();return structuredClone(rec);});
|
|
@@ -172,7 +210,7 @@ export async function createRemoteDevicePlaneR44({stateDir,relay,authorizeCapabi
|
|
|
172
210
|
async function revoke({deviceId,epoch}={}){
|
|
173
211
|
return withLock(async()=>{
|
|
174
212
|
const d=getDevice(deviceId,epoch);d.revoked=true;d.reported_online=false;d.reachable=false;d.connection_id=null;d.updated_at_utc=now();await persist();
|
|
175
|
-
return {...
|
|
213
|
+
return {...pub(d),revoked:true};
|
|
176
214
|
});
|
|
177
215
|
}
|
|
178
216
|
async function rotateCredential({deviceId,epoch,encryptedBundleBase64,expectedPreviousSha256}={}){
|
|
@@ -200,6 +238,6 @@ export async function createRemoteDevicePlaneR44({stateDir,relay,authorizeCapabi
|
|
|
200
238
|
}
|
|
201
239
|
return {status:'committed',device_id:deviceId,epoch,credential_sha256:credentialSha,previous_credential_sha256:previous?.credential_sha256??null};
|
|
202
240
|
}
|
|
203
|
-
async function list(){return {status:'ok',devices:Object.values(state.devices).map(
|
|
241
|
+
async function list(){return {status:'ok',devices:Object.values(state.devices).map(pub).sort((a,b)=>a.device_id.localeCompare(b.device_id)),requests:structuredClone(state.requests)};}
|
|
204
242
|
return Object.freeze({pair,status,reportOnline,probe,dispatch,revoke,rotateCredential,list,state_file:stateFile});
|
|
205
243
|
}
|
|
@@ -13,7 +13,7 @@ const privilegedTools = ['host_exec', 'host_file_read', 'host_file_write'];
|
|
|
13
13
|
const machineTools = ['machine_list', 'machine_ping', 'machine_revoke', 'machine_status'];
|
|
14
14
|
const processTools = [
|
|
15
15
|
'process_session_action', 'process_session_list', 'process_session_read', 'process_session_start',
|
|
16
|
-
'process_session_status', 'process_session_terminate'
|
|
16
|
+
'process_session_status', 'process_session_terminate', 'process_session_write'
|
|
17
17
|
];
|
|
18
18
|
const machineFsTools = [
|
|
19
19
|
'machine_fs_delete', 'machine_fs_edit_exact', 'machine_fs_list', 'machine_fs_mkdir',
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
const SHA256_RE = /^[0-9a-f]{64}$/;
|
|
4
|
+
|
|
5
|
+
function hashObject(value) {
|
|
6
|
+
const json = JSON.stringify(value);
|
|
7
|
+
return createHash('sha256').update(Buffer.from(json, 'utf8')).digest('hex');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function requiredString(value, name, max = 512) {
|
|
11
|
+
if (typeof value !== 'string' || value.length < 1 || value.length > max) throw new Error(`resource identity ${name} invalid`);
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function createResourceIdentity({
|
|
16
|
+
rootId,
|
|
17
|
+
rootGeneration = null,
|
|
18
|
+
relativePath,
|
|
19
|
+
canonicalPath,
|
|
20
|
+
kind,
|
|
21
|
+
exists,
|
|
22
|
+
size = 0,
|
|
23
|
+
contentSha256 = null,
|
|
24
|
+
objectIdentitySha256 = null
|
|
25
|
+
}) {
|
|
26
|
+
requiredString(rootId, 'root_id', 64);
|
|
27
|
+
requiredString(relativePath, 'relative_path');
|
|
28
|
+
requiredString(canonicalPath, 'canonical_path', 4096);
|
|
29
|
+
requiredString(kind, 'kind', 32);
|
|
30
|
+
if (rootGeneration !== null && typeof rootGeneration !== 'string') throw new Error('resource identity root_generation invalid');
|
|
31
|
+
if (typeof exists !== 'boolean') throw new Error('resource identity exists invalid');
|
|
32
|
+
if (!Number.isSafeInteger(size) || size < 0) throw new Error('resource identity size invalid');
|
|
33
|
+
if (contentSha256 !== null && !SHA256_RE.test(contentSha256)) throw new Error('resource identity content_sha256 invalid');
|
|
34
|
+
if (objectIdentitySha256 !== null && !SHA256_RE.test(objectIdentitySha256)) throw new Error('resource identity object_identity_sha256 invalid');
|
|
35
|
+
|
|
36
|
+
const location = {
|
|
37
|
+
schema: 'deadbyte.resource-location.v1',
|
|
38
|
+
root_id: rootId,
|
|
39
|
+
root_generation: rootGeneration,
|
|
40
|
+
relative_path: relativePath.replaceAll('\\', '/'),
|
|
41
|
+
canonical_path: canonicalPath,
|
|
42
|
+
kind,
|
|
43
|
+
exists
|
|
44
|
+
};
|
|
45
|
+
const resourceIdentitySha256 = hashObject(location);
|
|
46
|
+
|
|
47
|
+
return Object.freeze({
|
|
48
|
+
...location,
|
|
49
|
+
size,
|
|
50
|
+
content_sha256: contentSha256,
|
|
51
|
+
object_identity_sha256: objectIdentitySha256,
|
|
52
|
+
resource_identity_sha256: resourceIdentitySha256
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function contentIdentitySha256(contentSha256) {
|
|
57
|
+
if (contentSha256 !== null && !SHA256_RE.test(contentSha256)) throw new Error('content_sha256 invalid');
|
|
58
|
+
return contentSha256;
|
|
59
|
+
}
|