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.
Files changed (96) hide show
  1. package/MANIFEST.SHA256 +95 -64
  2. package/PROVENANCE.json +6 -5
  3. package/README.txt +36 -20
  4. package/RELEASE.json +24 -8
  5. package/SBOM.cdx.json +3 -3
  6. package/controller/README.TXT +2 -2
  7. package/controller/deadbyte-controller.ps1 +50 -2
  8. package/controller/deadbyte-process-policy.json +63 -1
  9. package/docs/ARCHITECTURE.md +3 -4
  10. package/package.json +88 -86
  11. package/scripts/autonomous-build.ps1 +1 -0
  12. package/scripts/autonomous-tests.ps1 +1 -0
  13. package/scripts/check-agent-backend-r46.mjs +77 -0
  14. package/scripts/check-atomic-resource-r46.mjs +84 -0
  15. package/scripts/check-autonomous-race-r46.mjs +160 -0
  16. package/scripts/check-coding-task-deadline-r46.mjs +59 -0
  17. package/scripts/check-deadline-cancellation-r46.mjs +49 -0
  18. package/scripts/check-process-deadline-r46.mjs +52 -0
  19. package/scripts/check-r46-background.mjs +5 -0
  20. package/scripts/deferred-slot-operation-r46.mjs +126 -0
  21. package/scripts/extract-r46-failures.mjs +5 -0
  22. package/scripts/final-closure-r46.mjs +98 -0
  23. package/scripts/final-closure-verify-r46.mjs +119 -0
  24. package/scripts/full-regression-r46.mjs +28 -0
  25. package/scripts/gate-windows-r46.ps1 +58 -0
  26. package/scripts/inspect-r46-active.mjs +2 -0
  27. package/scripts/r47-core-failures.mjs +1 -0
  28. package/scripts/refresh-process-policy.mjs +64 -7
  29. package/scripts/release-manifest.mjs +1 -1
  30. package/scripts/release-parity-r46.mjs +187 -0
  31. package/scripts/run-r46-full-regression-background.mjs +7 -0
  32. package/scripts/run-r46-windows-gate-background.mjs +6 -0
  33. package/scripts/run-r46-windows-gate-background.ps1 +10 -0
  34. package/scripts/windows-gate-evidence-r46.mjs +111 -0
  35. package/scripts/workload-contract-regression-r47.mjs +14 -0
  36. package/scripts/workload-stream-regression.mjs +16 -0
  37. package/scripts/workload-substrate-r47-regression.mjs +12 -0
  38. package/src/agent-backend-r44.mjs +75 -10
  39. package/src/atomic-mutation.mjs +55 -0
  40. package/src/autonomous-mcp-tools.mjs +4 -3
  41. package/src/autonomous-output-contracts.mjs +16 -3
  42. package/src/autonomous-runtime.mjs +83 -30
  43. package/src/capability-ledger.mjs +1 -0
  44. package/src/coding-mcp-tools.mjs +2 -2
  45. package/src/coding-output-contracts.mjs +18 -4
  46. package/src/coding-plane.mjs +3 -24
  47. package/src/coding-search.mjs +11 -2
  48. package/src/coding-task-runtime.mjs +41 -6
  49. package/src/compact-gateway-mcp-tools.mjs +7 -5
  50. package/src/deadbyte-cli.mjs +7 -3
  51. package/src/deadline-cancellation.mjs +83 -0
  52. package/src/machine-fs-mcp-tools.mjs +3 -3
  53. package/src/machine-fs-output-contracts.mjs +1 -1
  54. package/src/machine-fs-read-worker.mjs +46 -0
  55. package/src/machine-fs-smoke-client.mjs +1 -1
  56. package/src/machine-fs.mjs +66 -33
  57. package/src/mcp-server.mjs +10 -0
  58. package/src/mcp-smoke-client.mjs +4 -1
  59. package/src/observation-runtime.mjs +25 -4
  60. package/src/observation-smoke-client.mjs +1 -1
  61. package/src/process-mcp-tools.mjs +29 -8
  62. package/src/process-output-contracts.mjs +43 -3
  63. package/src/process-policy.mjs +3 -1
  64. package/src/process-runtime.mjs +152 -23
  65. package/src/process-smoke-client.mjs +67 -22
  66. package/src/release-generation.mjs +22 -3
  67. package/src/remote-device-r44.mjs +53 -15
  68. package/src/remote-mcp-smoke-client.mjs +1 -1
  69. package/src/resource-identity.mjs +59 -0
  70. package/src/runtime-update.mjs +526 -162
  71. package/src/stdio-console-guard.mjs +24 -0
  72. package/src/version.mjs +1 -1
  73. package/src/workload-substrate.mjs +87 -0
  74. package/test/cli-entrypoint.test.mjs +3 -3
  75. package/test/coding-runtime.test.mjs +12 -0
  76. package/test/coding-search-r36.test.mjs +7 -0
  77. package/test/compact-gateway-r36.test.mjs +28 -0
  78. package/test/core.test.mjs +1 -1
  79. package/test/full-max-catalog-r46.test.mjs +92 -0
  80. package/test/helpers/autonomous-r34-fixture.mjs +10 -1
  81. package/test/machine-fs-mcp-tools.test.mjs +4 -1
  82. package/test/machine-fs-runtime.test.mjs +18 -2
  83. package/test/observation-runtime.test.mjs +29 -2
  84. package/test/process-mcp-tools.test.mjs +27 -6
  85. package/test/process-policy-refresh.test.mjs +87 -10
  86. package/test/process-runtime.test.mjs +129 -2
  87. package/test/process-supervision.test.mjs +4 -2
  88. package/test/production-docs.test.mjs +3 -3
  89. package/test/r33-finalization.test.mjs +3 -3
  90. package/test/r45-finalization.test.mjs +1 -1
  91. package/test/release-generation.test.mjs +25 -11
  92. package/test/release-version.test.mjs +99 -44
  93. package/test/remote-device-r44.test.mjs +75 -0
  94. package/test/remote-r24-catalog.test.mjs +2 -2
  95. package/test/runtime-self-update.test.mjs +118 -1
  96. 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:d.reachable,connection_id:d.connection_id,updated_at_utc:d.updated_at_utc};
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 publicDevice(existing);
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 publicDevice(d);
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 publicDevice(d);
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 publicDevice(d);
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
- const response=await relay.probe({device_id:deviceId,epoch,connection_id:connectionId,nonce});
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 {...publicDevice(d),probe_nonce:nonce};
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.reachable,'remote device not reachable');
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{response=await relay.request({device_id:deviceId,epoch,capability_id:capabilityId,payload,request_id:requestId,request_sha256:requestSha});}
154
- catch(error){
155
- return withLock(async()=>{const rec=state.requests[requestId];rec.state='failed';rec.error_code='transport_error';rec.error=String(error?.message??error);rec.updated_at_utc=now();await persist();return structuredClone(rec);});
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 {...publicDevice(d),revoked:true};
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(publicDevice).sort((a,b)=>a.device_id.localeCompare(b.device_id)),requests:structuredClone(state.requests)};}
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
+ }