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
|
@@ -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())
|
|
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())
|
|
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
|
-
|
|
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
|
-
|
|
232
|
-
|
|
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){
|
|
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(),
|
|
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'],
|