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
@@ -0,0 +1,111 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, open, readFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { doctor } from '../src/doctor-r45.mjs';
7
+
8
+ const here=path.dirname(fileURLToPath(import.meta.url));
9
+ const root=path.resolve(here,'..');
10
+ const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
11
+ const marker='DEADBYTE V0.15.2 / R46 FILELESS POWERSHELL WINDOWS BATCH GATE: PASS';
12
+ const PARENT_RELEASE_ID='v0.15.1-d55f00b5144dc31a';
13
+ const PARENT_MANIFEST='d55f00b5144dc31a7fe39e4dc60d640a94780da6aaeca0918e87636b816d7854';
14
+ const PARENT_GENERATION='00cde7bf540cc2804d679a39040d5b07193c5f8a0d49cac3b882402f8f4e6ad8';
15
+ const REOPENED_RUNTIME_RELEASE_ID='v0.15.2-0fb46542f6a4345f';
16
+ const REOPENED_RUNTIME_MANIFEST='0fb46542f6a4345f1c527565d06bf6e9b8e283260e47bf684fb70a60308fcf34';
17
+ const REOPENED_RUNTIME_GENERATION='4717e0eb9c515ce4b1e90b8496c49c68abde765a6de7490176b803912385afe4';
18
+ function insist(ok,message){if(!ok)throw new Error(message);}
19
+
20
+ export async function writeWindowsGateEvidence({
21
+ candidateRoot=root,
22
+ stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp')),
23
+ clock=()=>new Date()
24
+ }={}){
25
+ const candidate=path.resolve(candidateRoot);
26
+ const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
27
+ const manifestSha256=sha256(manifestBytes);
28
+ const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
29
+ insist(pkg.version==='0.15.2',`R46 Windows gate evidence requires V0.15.2, found ${pkg.version}`);
30
+ const release=JSON.parse(await readFile(path.join(candidate,'RELEASE.json'),'utf8'));
31
+ insist(release.version==='0.15.2'&&release.train==='R46','R46 release metadata mismatch');
32
+ const active=JSON.parse(await readFile(path.join(path.resolve(stateRoot),'active-release.json'),'utf8'));
33
+ const originalParent=active.release_id===PARENT_RELEASE_ID&&active.manifest_sha256===PARENT_MANIFEST&&active.generation_manifest_sha256===PARENT_GENERATION;
34
+ const reopenedPredecessor=active.release_id===REOPENED_RUNTIME_RELEASE_ID&&active.manifest_sha256===REOPENED_RUNTIME_MANIFEST&&active.generation_manifest_sha256===REOPENED_RUNTIME_GENERATION;
35
+ const currentR46Predecessor=active.version==='0.15.2'&&active.manifest_sha256!==manifestSha256&&/^[0-9a-f]{64}$/.test(active.generation_manifest_sha256??'');
36
+ insist(originalParent||reopenedPredecessor||currentR46Predecessor,'R46 runtime predecessor identity mismatch');
37
+ const parent={release_id:PARENT_RELEASE_ID,version:'0.15.1',manifest_sha256:PARENT_MANIFEST,generation_manifest_sha256:PARENT_GENERATION};
38
+
39
+ const doctorResult=await doctor();
40
+ insist(doctorResult?.status==='ok'&&doctorResult?.package_version==='0.15.2'&&doctorResult?.manifest?.verified===true,'R46 doctor evidence invalid');
41
+ insist(doctorResult?.policy?.host_v2_ready===true,'R46 doctor did not verify Host policy v2');
42
+
43
+ const completed=clock();
44
+ insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'R46 Windows gate evidence clock invalid');
45
+ const evidence={
46
+ schema:'deadbyte.r46-windows-gate.v1',
47
+ status:'passed',
48
+ exit_code:0,
49
+ version:'0.15.2',
50
+ train:'R46',
51
+ marker,
52
+ completed_at_utc:completed.toISOString(),
53
+ manifest_sha256:manifestSha256,
54
+ doctor:{
55
+ status:doctorResult.status,
56
+ manifest_sha256:doctorResult.manifest.sha256,
57
+ verified_entries:doctorResult.manifest.verified_entries,
58
+ host_v2_ready:doctorResult.policy.host_v2_ready
59
+ },
60
+ parent,
61
+ runtime_predecessor:{
62
+ release_id:active.release_id,
63
+ version:active.version,
64
+ manifest_sha256:active.manifest_sha256,
65
+ generation_manifest_sha256:active.generation_manifest_sha256,
66
+ mode:currentR46Predecessor?'current-r46-active':'reopenedPredecessor'===true?'reopened-unpublished-0.15.2':'sealed-0.15.1-parent'
67
+ },
68
+ features:{
69
+ fileless_powershell:true,
70
+ powershell_source_via_process_argv:true,
71
+ no_ps1_materialization_regression:true,
72
+ target_machine_process_profile_repin:true,
73
+ target_machine_cmd_repin:true,
74
+ target_machine_powershell_repin:true,
75
+ powershell_persistent_repl:true,
76
+ guarded_process_raw_input:true,
77
+ process_tail_reads:true,
78
+ process_wait_reads:true,
79
+ process_chunked_output_ring:true,
80
+ remote_reachability_ttl:true,
81
+ remote_probe_timeout_retry:true,
82
+ remote_dispatch_timeout_no_retry:true,
83
+ observation_search_terminal_ttl:true,
84
+ observation_search_hard_cap:128,
85
+ full_regression_required:true,
86
+ compact_tool_count:43,
87
+ full_tool_count:84
88
+ }
89
+ };
90
+ const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');
91
+ const dir=path.join(path.resolve(stateRoot),'evidence','release');
92
+ await mkdir(dir,{recursive:true});
93
+ const file=path.join(dir,`r46-windows-gate-${manifestSha256.slice(0,16)}.json`);
94
+ try{
95
+ const handle=await open(file,'wx',0o600);
96
+ try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
97
+ return {status:'written',path:file,sha256:sha256(bytes),manifest_sha256:manifestSha256,parent:evidence.parent};
98
+ }catch(error){
99
+ if(error?.code!=='EEXIST')throw error;
100
+ const existingBytes=await readFile(file);
101
+ const existing=JSON.parse(existingBytes.toString('utf8').replace(/^\uFEFF/,''));
102
+ insist(existing.schema===evidence.schema&&existing.status==='passed'&&existing.exit_code===0,'existing R46 Windows gate evidence invalid');
103
+ insist(existing.version==='0.15.2'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing R46 Windows gate identity mismatch');
104
+ insist(existing.parent?.release_id===parent.release_id&&existing.parent?.manifest_sha256===parent.manifest_sha256,'existing R46 parent evidence mismatch');
105
+ insist(existing.runtime_predecessor?.release_id===active.release_id&&existing.runtime_predecessor?.manifest_sha256===active.manifest_sha256,'existing R46 runtime predecessor evidence mismatch');
106
+ return {status:'existing',path:file,sha256:sha256(existingBytes),manifest_sha256:manifestSha256,parent:existing.parent};
107
+ }
108
+ }
109
+
110
+ const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
111
+ if(invoked)writeWindowsGateEvidence().then(value=>console.log(JSON.stringify(value))).catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
@@ -0,0 +1,14 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ const files = [
3
+ 'test/output-schema.test.mjs',
4
+ 'test/coding-catalog-consistency.test.mjs',
5
+ 'test/semantic-backend-registry.test.mjs',
6
+ 'test/coding-runtime.test.mjs',
7
+ 'test/autonomous-runtime.test.mjs',
8
+ 'test/process-runtime.test.mjs',
9
+ 'test/workload-substrate.test.mjs'
10
+ ];
11
+ const result = spawnSync(process.execPath, ['--test', '--test-concurrency=1', ...files], { cwd: new URL('..', import.meta.url), stdio: 'inherit', windowsHide: true, shell: false });
12
+ if (result.error) throw result.error;
13
+ if (result.signal) throw new Error(`workload contract regression terminated by ${result.signal}`);
14
+ process.exitCode = Number.isInteger(result.status) ? result.status : 1;
@@ -0,0 +1,16 @@
1
+ import { spawnSync } from 'node:child_process';
2
+
3
+ const files = [
4
+ 'test/workload-substrate.test.mjs',
5
+ 'test/coding-search-r36.test.mjs',
6
+ 'test/process-runtime.test.mjs'
7
+ ];
8
+ const result = spawnSync(process.execPath, ['--test', '--test-concurrency=1', ...files], {
9
+ cwd: new URL('..', import.meta.url),
10
+ stdio: 'inherit',
11
+ windowsHide: true,
12
+ shell: false
13
+ });
14
+ if (result.error) throw result.error;
15
+ if (result.signal) throw new Error(`workload stream regression terminated by ${result.signal}`);
16
+ process.exitCode = Number.isInteger(result.status) ? result.status : 1;
@@ -0,0 +1,12 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ const files = [
3
+ 'test/workload-substrate.test.mjs',
4
+ 'test/coding-search-r36.test.mjs',
5
+ 'test/process-runtime.test.mjs',
6
+ 'test/coding-runtime.test.mjs',
7
+ 'test/autonomous-runtime.test.mjs'
8
+ ];
9
+ const result = spawnSync(process.execPath, ['--test', '--test-concurrency=1', ...files], { cwd: new URL('..', import.meta.url), stdio: 'inherit', windowsHide: true, shell: false });
10
+ if (result.error) throw result.error;
11
+ if (result.signal) throw new Error(`workload substrate regression terminated by ${result.signal}`);
12
+ process.exitCode = Number.isInteger(result.status) ? result.status : 1;
@@ -3,6 +3,7 @@ import { EventEmitter } from 'node:events';
3
3
  import { lstat, mkdir, open, readFile, realpath, rename } from 'node:fs/promises';
4
4
  import path from 'node:path';
5
5
  import { spawn } from 'node:child_process';
6
+ import { classifyTermination, composeAbortSignal, createCancellationToken, createDeadline } from './deadline-cancellation.mjs';
6
7
 
7
8
  const STATE_SCHEMA='deadbyte.agent-backend-state.v1';
8
9
  const HEX64=/^[0-9a-f]{64}$/;
@@ -46,6 +47,16 @@ function validateState(state,backendId){
46
47
  insist(typeof item.method==='string'&&METHOD.test(item.method),'agent backend request method invalid');
47
48
  insist(HEX64.test(item.params_sha256??''),'agent backend params hash invalid');
48
49
  insist(typeof item.created_at_utc==='string'&&typeof item.updated_at_utc==='string','agent backend request timestamp invalid');
50
+ if(item.deadline!==undefined){
51
+ insist(item.deadline&&item.deadline.schema==='deadbyte.deadline.v1','agent backend request deadline invalid');
52
+ insist(item.deadline.timeout_ms===null||Number.isInteger(item.deadline.timeout_ms),'agent backend request deadline timeout invalid');
53
+ insist(item.deadline.deadline_ms===null||Number.isFinite(item.deadline.deadline_ms),'agent backend request deadline timestamp invalid');
54
+ }
55
+ if(item.termination!==undefined){
56
+ insist(item.termination&&typeof item.termination==='object'&&!Array.isArray(item.termination),'agent backend request termination invalid');
57
+ for(const key of ['deadline_exceeded','cancellation_requested','physical_termination','verified'])insist(typeof item.termination[key]==='boolean',`agent backend request termination ${key} invalid`);
58
+ insist(typeof item.termination.terminal_semantics==='string','agent backend request termination semantics invalid');
59
+ }
49
60
  }
50
61
  insist(typeof state.updated_at_utc==='string','agent backend state timestamp invalid');
51
62
  return state;
@@ -157,11 +168,12 @@ export async function createAgentBackendR44({
157
168
  threads:structuredClone(state.threads),requests:structuredClone(state.requests)
158
169
  });
159
170
 
160
- async function terminal(id,{stateName,result=null,error=null}){
171
+ async function terminal(id,{stateName,result=null,error=null,termination=null}){
161
172
  const record=state.requests[id];if(!record||record.state!=='pending')return;
162
173
  record.state=stateName;record.updated_at_utc=now();
163
174
  if(result!==null)record.result=result;
164
175
  if(error!==null)record.error=String(error);
176
+ if(termination!==null)record.termination=termination;
165
177
  await persist();
166
178
  const waiter=pending.get(id);pending.delete(id);
167
179
  if(!waiter)return;
@@ -198,11 +210,29 @@ export async function createAgentBackendR44({
198
210
  for(const id of pending.keys())void terminal(id,{stateName:'failed',error:String(error?.message??error)});
199
211
  });
200
212
  transport.on('transport_error',error=>{
201
- for(const id of pending.keys())void terminal(id,{stateName:'interrupted',error:String(error?.message??error)});
213
+ for(const id of pending.keys()){
214
+ const record=state.requests[id];
215
+ const termination=classifyTermination({
216
+ deadlineExceeded:record?.termination?.deadline_exceeded===true,
217
+ cancellationRequested:record?.termination?.cancellation_requested===true,
218
+ physicallyTerminated:false,
219
+ verified:false
220
+ });
221
+ void terminal(id,{stateName:'interrupted',error:String(error?.message??error),termination});
222
+ }
202
223
  started=false;
203
224
  });
204
225
  transport.on('exit',()=>{
205
- for(const id of pending.keys())void terminal(id,{stateName:'interrupted',error:'agent backend transport exited'});
226
+ for(const id of pending.keys()){
227
+ const record=state.requests[id];
228
+ const termination=classifyTermination({
229
+ deadlineExceeded:record?.termination?.deadline_exceeded===true,
230
+ cancellationRequested:record?.termination?.cancellation_requested===true,
231
+ physicallyTerminated:true,
232
+ verified:true
233
+ });
234
+ void terminal(id,{stateName:'interrupted',error:'agent backend transport exited',termination});
235
+ }
206
236
  started=false;
207
237
  });
208
238
  await transport.start();started=true;return publicState();
@@ -223,17 +253,52 @@ export async function createAgentBackendR44({
223
253
  const sequence=++state.next_sequence;
224
254
  const paramsSha=sha(Buffer.from(canonicalJson(params),'utf8'));
225
255
  const id=sha(Buffer.from(canonicalJson({backend_id:backendId,state_epoch:state.state_epoch,sequence,method,params_sha256:paramsSha}),'utf8'));
226
- state.requests[id]={state:'pending',method,params_sha256:paramsSha,created_at_utc:now(),updated_at_utc:now()};
256
+ const deadline=createDeadline({timeoutMs,id});
257
+ const cancellation=createCancellationToken({id});
258
+ const composed=composeAbortSignal({deadline,cancellation});
259
+ state.requests[id]={
260
+ state:'pending',method,params_sha256:paramsSha,created_at_utc:now(),updated_at_utc:now(),
261
+ deadline,
262
+ termination:classifyTermination()
263
+ };
227
264
  await persist();
228
- let timer;
229
265
  const promise=new Promise((resolve,reject)=>{
230
- pending.set(id,{resolve,reject});
231
- timer=setTimeout(()=>{void terminal(id,{stateName:'failed',error:'agent backend request timeout'});},timeoutMs);
232
- }).finally(()=>clearTimeout(timer));
266
+ pending.set(id,{resolve,reject,cancellation,composed});
267
+ composed.signal.addEventListener('abort',()=>{
268
+ const reason=String(composed.signal.reason??'cancelled');
269
+ const deadlineExceeded=reason==='deadline_exceeded';
270
+ const cancellationRequested=cancellation.snapshot().requested;
271
+ const termination=classifyTermination({deadlineExceeded,cancellationRequested,physicallyTerminated:false,verified:false});
272
+ void terminal(id,{stateName:'failed',error:deadlineExceeded?'agent backend request deadline exceeded':'agent backend request cancelled',termination});
273
+ },{once:true});
274
+ }).finally(()=>{
275
+ composed.close();
276
+ const waiter=pending.get(id);
277
+ if(waiter?.composed===composed)pending.delete(id);
278
+ });
233
279
  try{await transport.send(wire({id,method,params}));}
234
- catch(error){await terminal(id,{stateName:'failed',error:String(error?.message??error)});}
280
+ catch(error){
281
+ const record=state.requests[id];
282
+ const termination=classifyTermination({
283
+ deadlineExceeded:record?.termination?.deadline_exceeded===true,
284
+ cancellationRequested:record?.termination?.cancellation_requested===true,
285
+ physicallyTerminated:false,
286
+ verified:false
287
+ });
288
+ await terminal(id,{stateName:'failed',error:String(error?.message??error),termination});
289
+ }
235
290
  return promise;
236
291
  }
292
+ async function cancelRequest(id,{reason='operator_request'}={}){
293
+ insist(HEX64.test(id),'agent backend request id invalid');
294
+ const record=state.requests[id];
295
+ if(!record)return {status:'missing',request_id:id};
296
+ if(record.state!=='pending')return {status:'terminal',request_id:id,state:record.state,termination:record.termination??null};
297
+ const waiter=pending.get(id);
298
+ if(!waiter?.cancellation)return {status:'pending',request_id:id,termination:record.termination??null};
299
+ waiter.cancellation.request(reason);
300
+ return {status:'requested',request_id:id};
301
+ }
237
302
  async function notify({method,params={}}={}){
238
303
  insist(started&&transport,'agent backend is not started');
239
304
  insist(typeof method==='string'&&METHOD.test(method),'agent backend notification method invalid');
@@ -250,5 +315,5 @@ export async function createAgentBackendR44({
250
315
  for(const [id,waiter] of pending){waiter.reject(new Error('simulated backend crash'));pending.delete(id);}
251
316
  if(transport?.stop)await transport.stop();transport=null;started=false;
252
317
  }
253
- return Object.freeze({start,bindThread,request,notify,status,close,simulateCrashForTest,state_file:stateFile,wire_mode:wireMode});
318
+ return Object.freeze({start,bindThread,request,cancelRequest,notify,status,close,simulateCrashForTest,state_file:stateFile,wire_mode:wireMode});
254
319
  }
@@ -0,0 +1,55 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { open, rename, rm } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+
5
+ const sha256Bytes = bytes => createHash('sha256').update(bytes).digest('hex');
6
+
7
+ export async function atomicMutation({
8
+ target,
9
+ bytes,
10
+ expected,
11
+ readState,
12
+ assertExpected,
13
+ authorityCheck = null,
14
+ tempTag = 'deadbyte-atomic',
15
+ verifyPostcondition = null
16
+ }) {
17
+ if (typeof target !== 'string' || target.length === 0) throw new Error('atomic mutation target is required');
18
+ if (!Buffer.isBuffer(bytes)) throw new Error('atomic mutation bytes must be a Buffer');
19
+ if (typeof readState !== 'function' || typeof assertExpected !== 'function') {
20
+ throw new Error('atomic mutation requires readState and assertExpected');
21
+ }
22
+ const before = await readState(target);
23
+ assertExpected(before, expected);
24
+ const temp = path.join(
25
+ path.dirname(target),
26
+ `.\${path.basename(target)}.\${tempTag}-\${process.pid}-\${randomBytes(8).toString('hex')}.tmp`
27
+ );
28
+ let handle;
29
+ try {
30
+ if (authorityCheck) await authorityCheck();
31
+ handle = await open(temp, 'wx', 0o600);
32
+ await handle.writeFile(bytes);
33
+ await handle.sync();
34
+ await handle.close();
35
+ handle = null;
36
+
37
+ const race = await readState(target);
38
+ assertExpected(race, expected);
39
+ if (authorityCheck) await authorityCheck();
40
+
41
+ await rename(temp, target);
42
+ } catch (error) {
43
+ if (handle) await handle.close().catch(() => {});
44
+ await rm(temp, { force: true }).catch(() => {});
45
+ throw error;
46
+ }
47
+
48
+ const after = await readState(target);
49
+ const expectedAfter = sha256Bytes(bytes);
50
+ if (!after.exists || after.sha256 !== expectedAfter) {
51
+ throw new Error('atomic mutation post-commit hash mismatch');
52
+ }
53
+ if (verifyPostcondition) await verifyPostcondition({ before, after, expected_after_sha256: expectedAfter });
54
+ return { before, after, expected_after_sha256: expectedAfter };
55
+ }
@@ -72,11 +72,12 @@ export function registerAutonomousTools(server,{ autonomousRuntime, nativeDiffRu
72
72
  inputSchema:z.strictObject({ submission_id:id, root_id:id, title:z.string().max(160).default(''),
73
73
  goal:z.string().min(1).max(8192), acceptance:z.string().max(4096).default(''),
74
74
  task_contract:taskContract.nullable().default(null),
75
- max_iterations:z.number().int().min(1).max(64).nullable().default(null) }),
75
+ max_iterations:z.number().int().min(1).max(64).nullable().default(null),
76
+ timeout_ms:z.number().int().min(100).max(900000).nullable().default(null) }),
76
77
  outputSchema:autonomousGoalSubmitOutputSchema
77
- },async({submission_id,root_id,title,goal,acceptance,task_contract,max_iterations})=>{ try {
78
+ },async({submission_id,root_id,title,goal,acceptance,task_contract,max_iterations,timeout_ms})=>{ try {
78
79
  return result(autonomousGoalSubmitOutputSchema,await autonomousRuntime.submit({ submissionId:submission_id,
79
- rootId:root_id,title,goal,acceptance,taskContract:task_contract,maxIterations:max_iterations })); }
80
+ rootId:root_id,title,goal,acceptance,taskContract:task_contract,maxIterations:max_iterations,timeoutMs:timeout_ms })); }
80
81
  catch(error){ return fail(autonomousGoalSubmitOutputSchema,error); } });
81
82
  server.registerTool('autonomous_goal_run',{
82
83
  description:'Advance one autonomous production goal through inspect/native-diff/build/test/benchmark/replan cycles. Client-planner mode returns a typed input_required sentinel for the Bridge MRTR adapter.',
@@ -3,7 +3,17 @@ import * as z from 'zod/v4';
3
3
  const hex64 = z.string().regex(/^[0-9a-f]{64}$/);
4
4
  const goalId = hex64;
5
5
  const jsonObject = z.record(z.string(),z.json());
6
+ const streamCoverage = z.strictObject({
7
+ logical_first_seq:z.number().int().min(1), logical_last_seq:z.number().int().min(0),
8
+ retained_first_seq:z.number().int().min(1), retained_last_seq:z.number().int().min(0),
9
+ complete:z.boolean(), truncated:z.boolean(),
10
+ missing_ranges:z.array(z.strictObject({from:z.number().int().min(1),to:z.number().int().min(0)})).max(100000),
11
+ logical_item_count:z.number().int().min(0), retained_item_count:z.number().int().min(0),
12
+ logical_bytes:z.number().int().min(0), retained_bytes:z.number().int().min(0),
13
+ dropped_items:z.number().int().min(0), dropped_bytes:z.number().int().min(0), status:z.enum(['open','closed'])
14
+ });
6
15
  const goalState = z.enum(['pending','running','paused','cancelled','succeeded']);
16
+ const deadline = z.strictObject({ schema:z.literal('deadbyte.deadline.v1'), id:z.string().nullable(), created_at_ms:z.number(), timeout_ms:z.number().int().min(1).nullable(), deadline_ms:z.number().nullable() });
7
17
  const loopPhase = z.enum(['PLANNING','INSPECTING','ACTING','VERIFYING','REPLANNING','WAITING','COMPLETE']);
8
18
  const loopStep = z.strictObject({
9
19
  step_id:z.string().regex(/^[A-Za-z0-9._-]{1,64}$/), intent:z.string().min(1).max(512), status:z.literal('active')
@@ -106,7 +116,7 @@ export const autonomousGoalSubmitOutputSchema = statusContract({
106
116
  const runShape = Object.fromEntries(Object.entries(derivedShape).map(([key,schema]) => [key,schema.optional()]));
107
117
  export const autonomousGoalRunOutputSchema = z.strictObject({
108
118
  status:z.enum(['terminal','paused','cancelled','completed','yielded','running','input_required','failed']), error:z.string().optional(),
109
- goal_id:goalId.optional(), release:jsonObject.optional(), planner_request:jsonObject.optional(), loop:publicLoopSchema.optional(),
119
+ goal_id:goalId.optional(), release:jsonObject.optional(), planner_request:jsonObject.optional(), deadline:deadline.optional(), loop:publicLoopSchema.optional(),
110
120
  cycles:z.number().int().min(1).max(32).optional(), ...runShape
111
121
  }).superRefine((value,ctx) => {
112
122
  if (value.error !== undefined) { if (value.status !== 'failed') ctx.addIssue({code:'custom',path:['error'],message:'error wrapper requires failed'}); return; }
@@ -119,12 +129,15 @@ export const autonomousGoalStatusOutputSchema = statusContract({
119
129
  shape:{ goal_id:goalId.optional(), goal_sha256:hex64.optional(), title:z.string().optional(), root_id:z.string().optional(),
120
130
  journal_verified:z.literal(true).optional(), run_busy:z.boolean().optional(),
121
131
  run_owner_pid:z.number().int().min(1).nullable().optional(),
122
- run_lock_created_at_utc:z.string().nullable().optional(), loop:publicLoopSchema.optional(), ...runShape }
132
+ run_lock_created_at_utc:z.string().nullable().optional(), timeout_ms:z.number().int().min(100).max(900000).nullable().optional(), deadline:deadline.optional(), loop:publicLoopSchema.optional(),
133
+ stream_id:goalId.nullable().optional(), stream_generation:z.number().int().min(1).nullable().optional(),
134
+ stream_next_seq:z.number().int().min(1).nullable().optional(), stream_coverage:streamCoverage.nullable().optional(), ...runShape }
123
135
  });
124
136
  export const autonomousGoalEventsOutputSchema = statusContract({
125
137
  statuses:['ok','failed'], success:['ok'], required:['goal_id','journal_verified','journal_head_sha256','events'],
126
138
  shape:{ goal_id:goalId.optional(), journal_verified:z.literal(true).optional(), journal_head_sha256:hex64.optional(),
127
- events:z.array(jsonObject).max(200).optional() }
139
+ events:z.array(jsonObject).max(200).optional(), stream_id:goalId.optional(), stream_generation:z.number().int().min(1).optional(),
140
+ stream_next_seq:z.number().int().min(1).optional(), stream_coverage:streamCoverage.optional() }
128
141
  });
129
142
  export const autonomousGoalCancelOutputSchema = statusContract({
130
143
  statuses:['terminal','requested','existing','failed'], success:['terminal','requested','existing'], required:['goal_id','state'],