deadbyte-mcp 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/MANIFEST.SHA256 +69 -55
  2. package/README.txt +13 -14
  3. package/bin/WORKER-REGISTRY.txt +3 -3
  4. package/bin/appcontainer-stage.exe +0 -0
  5. package/bin/appcontainer-stage.obj +0 -0
  6. package/bin/bootstrap-advapi32.exe +0 -0
  7. package/bin/bootstrap-advapi32.obj +0 -0
  8. package/bin/bootstrap-exitcode.exe +0 -0
  9. package/bin/bootstrap-exitcode.obj +0 -0
  10. package/bin/bootstrap-kernel32.exe +0 -0
  11. package/bin/bootstrap-kernel32.obj +0 -0
  12. package/bin/child-control-probe.exe +0 -0
  13. package/bin/child-control-probe.obj +0 -0
  14. package/bin/child-control-stage.exe +0 -0
  15. package/bin/child-control-stage.obj +0 -0
  16. package/bin/contained-reverse-worker.exe +0 -0
  17. package/bin/contained-reverse-worker.obj +0 -0
  18. package/bin/contained-transform-worker.exe +0 -0
  19. package/bin/contained-transform-worker.obj +0 -0
  20. package/bin/containment-probe.exe +0 -0
  21. package/bin/containment-probe.obj +0 -0
  22. package/bin/deadbyte-contain.exe +0 -0
  23. package/bin/deadbyte-contain.obj +0 -0
  24. package/bin/deadbyte-exec.exe +0 -0
  25. package/bin/deadbyte-exec.obj +0 -0
  26. package/bin/deadbyte-process-host.exe +0 -0
  27. package/bin/deadbyte-process-host.obj +0 -0
  28. package/bin/deadbyte-tunnel-host.exe +0 -0
  29. package/bin/deadbyte-tunnel-host.obj +0 -0
  30. package/controller/README.TXT +3 -3
  31. package/controller/deadbyte-controller.ps1 +1 -1
  32. package/controller/deadbyte-process-policy.json +1 -1
  33. package/docs/ARCHITECTURE.md +1 -1
  34. package/package.json +1 -1
  35. package/scripts/deferred-slot-operation.mjs +1 -1
  36. package/scripts/final-closure-r36.mjs +113 -0
  37. package/scripts/final-closure-verify-r36.mjs +208 -0
  38. package/scripts/final-closure-verify.mjs +48 -48
  39. package/scripts/final-closure.mjs +13 -13
  40. package/scripts/gate-windows-r36.ps1 +42 -0
  41. package/scripts/gate-windows.ps1 +3 -3
  42. package/scripts/release-parity-r36.mjs +200 -0
  43. package/scripts/release-parity-tests.ps1 +2 -2
  44. package/scripts/release-parity.mjs +37 -37
  45. package/scripts/windows-gate-evidence-r36.mjs +52 -0
  46. package/scripts/windows-gate-evidence.mjs +5 -5
  47. package/src/agent-memory-plane.mjs +98 -0
  48. package/src/autonomous-output-contracts.mjs +5 -2
  49. package/src/autonomous-runtime.mjs +155 -31
  50. package/src/autonomous-timeout-recovery.mjs +27 -0
  51. package/src/deadbyte-cli.mjs +37 -76
  52. package/src/effect-ledger.mjs +47 -0
  53. package/src/observation-lease.mjs +32 -0
  54. package/src/remote-live-log.mjs +59 -0
  55. package/src/remote-mcp-autonomous-smoke-client.mjs +15 -9
  56. package/src/version.mjs +1 -1
  57. package/src/worker-scheduler.mjs +53 -0
  58. package/test/autonomous-output-contract.test.mjs +1 -1
  59. package/test/core.test.mjs +1 -1
  60. package/test/production-docs.test.mjs +5 -5
  61. package/test/r33-closeout-regression.test.mjs +4 -4
  62. package/test/r33-finalization.test.mjs +6 -6
  63. package/test/r36-finalization.test.mjs +5 -5
  64. package/test/r36-release-identity.test.mjs +12 -19
  65. package/test/r37-agent-fabric.test.mjs +92 -0
  66. package/test/r37-autonomous-timeout-recovery.test.mjs +60 -0
  67. package/test/r37-finalization.test.mjs +74 -0
  68. package/test/release-version.test.mjs +42 -36
  69. package/test/remote-live-log-r37.test.mjs +47 -0
  70. package/test/remote-session-cli.test.mjs +27 -50
@@ -0,0 +1,200 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, readFile, realpath, writeFile } from 'node:fs/promises';
3
+ import { loadPowerShellDataFile } from '../src/runtime-update.mjs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { spawn } from 'node:child_process';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { probeR36FullSurface } from '../src/r36-full-surface-probe.mjs';
9
+ import { runR36CompactParity } from '../src/remote-mcp-r36-parity-client.mjs';
10
+ import { probeR36CompactDisarmed } from '../src/r36-compact-disarmed-probe.mjs';
11
+
12
+ const here=path.dirname(fileURLToPath(import.meta.url));
13
+ const root=path.resolve(here,'..');
14
+ const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
15
+ const deferred=path.join(root,'scripts','deferred-slot-operation.mjs');
16
+ const ps='C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
17
+ const launcher=path.join(stateRoot,'bin','DEADBYTE-MCP.ps1');
18
+ const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
19
+ const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
20
+
21
+ export const R35_PREDECESSOR_RELEASE_ID='v0.9.0-b95e1ec1d9a7e26a';
22
+ export const R35_PREDECESSOR_MANIFEST='b95e1ec1d9a7e26a96ef492306c1be4df18c883ff03ab3b6015f677d7429ec8d';
23
+
24
+ function assert(condition,message){if(!condition) throw new Error(message);}
25
+ function parseFrozenManifest(text){
26
+ const entries=[];const seen=new Set();
27
+ for(const [index,line] of text.split(/\r?\n/).entries()){
28
+ if(!line) continue;
29
+ const match=/^([0-9a-f]{64}) ([^\0]+)$/.exec(line);
30
+ assert(match,`frozen manifest line ${index+1} malformed`);
31
+ const rel=match[2];
32
+ assert(!path.posix.isAbsolute(rel)&&!rel.split('/').includes('..')&&!seen.has(rel),`unsafe/duplicate frozen manifest path: ${rel}`);
33
+ seen.add(rel);entries.push({sha256:match[1],path:rel});
34
+ }
35
+ return entries;
36
+ }
37
+ // Restore the mutable development stage from the immutable active release after live parity work may drift it.
38
+ export async function restoreFrozenStage({developmentRoot,releaseRoot,expectedManifestSha256}){
39
+ const dev=path.resolve(developmentRoot);const release=path.resolve(releaseRoot);
40
+ const manifestBytes=await readFile(path.join(release,'MANIFEST.SHA256'));
41
+ const manifestSha256=sha256(manifestBytes);
42
+ assert(manifestSha256===expectedManifestSha256,`immutable release manifest drift: ${manifestSha256}`);
43
+ const entries=parseFrozenManifest(manifestBytes.toString('utf8'));
44
+ for(const entry of entries){
45
+ const source=path.join(release,...entry.path.split('/'));const bytes=await readFile(source);
46
+ assert(sha256(bytes)===entry.sha256,`immutable release file drift: ${entry.path}`);
47
+ const destination=path.join(dev,...entry.path.split('/'));await mkdir(path.dirname(destination),{recursive:true});
48
+ await writeFile(destination,bytes);assert(sha256(await readFile(destination))===entry.sha256,`development restore verification failed: ${entry.path}`);
49
+ }
50
+ await writeFile(path.join(dev,'MANIFEST.SHA256'),manifestBytes);
51
+ assert(sha256(await readFile(path.join(dev,'MANIFEST.SHA256')))===expectedManifestSha256,'development manifest restore verification failed');
52
+ return {status:'restored',file_count:entries.length,manifest_sha256:manifestSha256};
53
+ }
54
+ function parseJsonOutput(text,label){
55
+ const trimmed=String(text).trim();const starts=[...trimmed.matchAll(/\{/g)].map(match=>match.index);
56
+ for(const start of starts){try{return JSON.parse(trimmed.slice(start));}catch{}}
57
+ throw new Error(`${label} did not emit JSON`);
58
+ }
59
+ function run(command,args,{timeoutMs=900000,env=process.env,cwd=root}={}){
60
+ return new Promise((resolve,reject)=>{
61
+ const child=spawn(command,args,{cwd,shell:false,windowsHide:true,env,stdio:['ignore','pipe','pipe']});
62
+ const out=[],err=[];const timer=setTimeout(()=>{child.kill();reject(new Error(`command timeout: ${path.basename(command)}`));},timeoutMs);
63
+ child.stdout.on('data',chunk=>out.push(Buffer.from(chunk)));child.stderr.on('data',chunk=>err.push(Buffer.from(chunk)));
64
+ child.on('error',error=>{clearTimeout(timer);reject(error);});child.on('exit',code=>{
65
+ clearTimeout(timer);const stdout=Buffer.concat(out).toString('utf8');const stderr=Buffer.concat(err).toString('utf8');
66
+ if(code!==0) reject(new Error(`command failed exit=${code}: ${stderr||stdout}`));else resolve({stdout,stderr,exitCode:code});
67
+ });
68
+ });
69
+ }
70
+ async function publicUrl(){
71
+ const value=(await readFile(path.join(stateRoot,'bridge','mcp-url.txt'),'utf8')).trim();const url=new URL(value);
72
+ if(url.protocol!=='https:') throw new Error('public MCP URL must be HTTPS');return value;
73
+ }
74
+ async function pointer(){return JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));}
75
+ async function disarmAll(){
76
+ for(const command of ['disarmautonomous','disarmcoding','disarmprocess','disarmhost']){
77
+ await run(ps,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,command],{timeoutMs:60000});
78
+ }
79
+ }
80
+ async function waitDeferred(statePath,timeoutMs=180000){
81
+ const deadline=Date.now()+timeoutMs;
82
+ while(Date.now()<deadline){const state=JSON.parse(await readFile(statePath,'utf8'));if(state.state==='completed') return state;if(state.state==='failed') throw new Error(`deferred ${state.mode} failed: ${state.error}`);await sleep(500);}
83
+ throw new Error(`deferred operation timeout: ${statePath}`);
84
+ }
85
+ async function scheduleDeferred(mode,value,{bridgeRoot=null,bridgeScriptSha256=null}={}){
86
+ if((bridgeRoot===null)!==(bridgeScriptSha256===null)) throw new Error('deferred bridge override requires exact root + SHA pair');
87
+ const args=[deferred,mode,value];
88
+ if(bridgeRoot!==null) args.push('--bridge-root',bridgeRoot,'--bridge-script-sha256',bridgeScriptSha256);
89
+ const scheduled=parseJsonOutput((await run(process.execPath,args,{timeoutMs:30000})).stdout,`deferred ${mode}`);
90
+ assert(scheduled.status==='scheduled',`deferred ${mode} was not scheduled`);
91
+ assert(scheduled.execution_provider==='deadbyte_mcp'&&scheduled.rdc_operation_count===0,'deferred operation provider mismatch');
92
+ return {scheduled,completed:await waitDeferred(scheduled.state_path)};
93
+ }
94
+ async function publicR35Disarmed(url){
95
+ const script=path.join(root,'src','remote-mcp-r24-disarmed-smoke-client.mjs');
96
+ const result=parseJsonOutput((await run(process.execPath,[script,url],{timeoutMs:90000})).stdout,'R35 predecessor disarmed gate');
97
+ assert(result.status==='passed'&&result.tool_count===73,'R35 predecessor public catalog mismatch');
98
+ assert(result.coding_armed===false&&result.autonomous_armed===false,'R35 predecessor authority remained armed');
99
+ return result;
100
+ }
101
+ async function mutableCore(pointerValue){
102
+ const policy=JSON.parse(await readFile(path.join(pointerValue.generation_root,'coding-policy.json'),'utf8'));
103
+ const raw=policy?.roots?.core?.path??policy?.roots?.core?.real_path;
104
+ assert(typeof raw==='string'&&raw,'R36 generation missing coding core root');
105
+ const core=path.resolve(raw);const release=path.resolve(pointerValue.release_root);
106
+ assert(core.toLowerCase()!==release.toLowerCase(),'R36 coding core must not point at immutable active release slot');
107
+ return core;
108
+ }
109
+
110
+ export function validateParityEvidence(value){
111
+ assert(value?.schema==='deadbyte.r36-parity.v1','R36 parity schema mismatch');
112
+ assert(value.status==='passed','R36 parity status is not passed');
113
+ assert(value.rdc_operation_count===0&&value.execution_provider==='deadbyte_mcp','R36 parity used non-DEADBYTE execution');
114
+ assert(value.candidate?.version==='0.10.0'&&/^[0-9a-f]{64}$/.test(value.candidate?.manifest_sha256??''),'R36 candidate identity missing');
115
+ assert(value.local_full?.status==='passed'&&value.local_full?.tool_count===76&&value.local_full?.strict_output_schemas===true,'R36 local full 76-tool proof missing');
116
+ assert(value.public_compact?.status==='passed'&&value.public_compact?.tool_count<=42&&value.public_compact?.capability_closure===true&&value.public_compact?.gateway_count===5,'R36 public compact closure missing');
117
+ assert(value.autonomous?.status==='passed'&&value.autonomous?.signed_release_verified===true,'R36 autonomous live proof missing');
118
+ assert(value.before_rollback_disarmed?.status==='passed'&&value.before_rollback_disarmed?.capability_closure===true&&value.before_rollback_disarmed?.coding_armed===false&&value.before_rollback_disarmed?.autonomous_armed===false,'R36 pre-rollback disarm proof missing');
119
+ assert(value.rollback?.state==='completed'&&value.restore?.state==='completed','R36 rollback/restore not completed');
120
+ assert(value.stage_restore?.status==='restored'&&value.stage_restore?.manifest_sha256===value.candidate.manifest_sha256,'R36 stage restore proof missing');
121
+ assert(value.predecessor?.release_id===R35_PREDECESSOR_RELEASE_ID&&value.predecessor?.manifest_sha256===R35_PREDECESSOR_MANIFEST&&value.predecessor?.version==='0.9.0','R35 exact predecessor identity mismatch');
122
+ assert(value.predecessor_disarmed?.status==='passed'&&value.predecessor_disarmed?.tool_count===73,'R35 predecessor public proof missing');
123
+ assert(value.successor?.version==='0.10.0'&&value.successor?.manifest_sha256===value.candidate.manifest_sha256,'R36 exact successor restore identity mismatch');
124
+ assert(value.successor_disarmed?.status==='passed'&&value.successor_disarmed?.capability_closure===true&&value.successor_disarmed?.coding_armed===false&&value.successor_disarmed?.autonomous_armed===false,'R36 successor disarmed proof missing');
125
+ assert(value.immutable_slot?.protected===true&&value.immutable_slot?.development_root!==value.immutable_slot?.release_root,'R36 immutable-slot separation missing');
126
+ for(const key of ['tree_read','precondition_mutation','search','owned_process','observation','coding_loop','autonomy','interrupted_recovery','evidence_verification','compact_closure','full_surface']) assert(value.requirements?.[key]===true,`R36 parity requirement not proven: ${key}`);
127
+ return true;
128
+ }
129
+
130
+ export async function runReleaseParity(activationReceiptPath){
131
+ const active=await pointer();const candidateManifest=sha256(await readFile(path.join(root,'MANIFEST.SHA256')));
132
+ assert(active.version==='0.10.0','R36 parity requires active 0.10.0 candidate');
133
+ assert(active.manifest_sha256===candidateManifest,'active release is not canonical R36 candidate');
134
+ const activeConfig=await loadPowerShellDataFile(active.config_path);
135
+ const successorBridgeRootRaw=activeConfig?.Bridge?.WorkingDirectory;
136
+ const successorBridgeConfiguredSha=activeConfig?.Bridge?.ScriptSha256;
137
+ assert(typeof successorBridgeRootRaw==='string'&&path.isAbsolute(successorBridgeRootRaw),'R36 active bridge root missing');
138
+ assert(typeof successorBridgeConfiguredSha==='string'&&/^[0-9a-f]{64}$/.test(successorBridgeConfiguredSha),'R36 active bridge SHA missing');
139
+ const successorBridgeRoot=await realpath(path.resolve(successorBridgeRootRaw));
140
+ const successorBridgeActualSha=sha256(await readFile(path.join(successorBridgeRoot,'src','bridge.mjs')));
141
+ assert(successorBridgeActualSha===successorBridgeConfiguredSha,'R36 active bridge bytes do not match generation pin');
142
+ const successorBridge={root:successorBridgeRoot,sha256:successorBridgeActualSha};
143
+ const developmentRoot=await mutableCore(active);
144
+ const localFull=await probeR36FullSurface({packageRoot:active.release_root,generationRoot:active.generation_root,stateRoot});
145
+ const url=await publicUrl();
146
+ let publicCompact,autonomous;
147
+ try{
148
+ publicCompact=await runR36CompactParity(url);
149
+ autonomous=parseJsonOutput((await run(process.execPath,[path.join(root,'src','remote-mcp-autonomous-smoke-client.mjs'),url],{timeoutMs:900000})).stdout,'R36 autonomous compact parity');
150
+ assert(autonomous.status==='passed'&&autonomous.signed_release_verified===true,'R36 autonomous compact parity failed');
151
+ }catch(error){await disarmAll().catch(()=>{});throw error;}
152
+
153
+ await disarmAll();
154
+ const beforeRollback=await probeR36CompactDisarmed(url);
155
+ const rollbackPair=await scheduleDeferred('rollback',path.resolve(activationReceiptPath));
156
+ const predecessor=await pointer();
157
+ assert(predecessor.release_id===R35_PREDECESSOR_RELEASE_ID&&predecessor.manifest_sha256===R35_PREDECESSOR_MANIFEST&&predecessor.version==='0.9.0','rollback did not reach exact sealed R35 predecessor');
158
+ const predecessorDisarmed=await publicR35Disarmed(url);
159
+ const stageRestore=await restoreFrozenStage({developmentRoot,releaseRoot:active.release_root,expectedManifestSha256:candidateManifest});
160
+ const restorePair=await scheduleDeferred('activate',candidateManifest,{bridgeRoot:successorBridge.root,bridgeScriptSha256:successorBridge.sha256});
161
+ const successor=await pointer();
162
+ assert(successor.version==='0.10.0'&&successor.manifest_sha256===candidateManifest,'restore did not return exact R36 candidate');
163
+ const successorDisarmed=await probeR36CompactDisarmed(url);
164
+ await disarmAll();
165
+
166
+ const evidence={
167
+ schema:'deadbyte.r36-parity.v1',status:'passed',completed_at_utc:new Date().toISOString(),execution_provider:'deadbyte_mcp',rdc_operation_count:0,
168
+ candidate:{release_id:active.release_id,version:active.version,manifest_sha256:candidateManifest},
169
+ immutable_slot:{protected:true,release_root:path.resolve(active.release_root),development_root:developmentRoot},
170
+ local_full:localFull,public_compact:publicCompact,autonomous,before_rollback_disarmed:beforeRollback,
171
+ rollback:{...rollbackPair.completed,scheduled_operation_id:rollbackPair.scheduled.operation_id},
172
+ predecessor:{release_id:predecessor.release_id,version:predecessor.version,manifest_sha256:predecessor.manifest_sha256},predecessor_disarmed:predecessorDisarmed,
173
+ stage_restore:stageRestore,
174
+ restore:{...restorePair.completed,scheduled_operation_id:restorePair.scheduled.operation_id},
175
+ successor:{release_id:successor.release_id,version:successor.version,manifest_sha256:successor.manifest_sha256},successor_disarmed:successorDisarmed,
176
+ requirements:{
177
+ tree_read:publicCompact.tree_entries>=0&&publicCompact.coding_stat_verified===true,
178
+ precondition_mutation:publicCompact.machine_fs_receipts_verified>=3,
179
+ search:publicCompact.coding_search_result_count>=1&&publicCompact.observation_search_result_count>=1,
180
+ owned_process:typeof publicCompact.process_session_id==='string'&&publicCompact.process_output_bytes>0,
181
+ observation:publicCompact.observation_receipts_verified>=2,
182
+ coding_loop:publicCompact.coding_receipts_verified===2,
183
+ autonomy:autonomous.signed_release_verified===true&&autonomous.observed_failure_before_replan===true,
184
+ interrupted_recovery:rollbackPair.completed.state==='completed'&&restorePair.completed.state==='completed',
185
+ evidence_verification:publicCompact.machine_fs_receipts_verified>=3&&autonomous.signed_release_verified===true,
186
+ compact_closure:publicCompact.capability_closure===true&&successorDisarmed.capability_closure===true,
187
+ full_surface:localFull.tool_count===76&&localFull.strict_output_schemas===true
188
+ }
189
+ };
190
+ validateParityEvidence(evidence);
191
+ const evidenceDir=path.join(stateRoot,'evidence','release');await mkdir(evidenceDir,{recursive:true});
192
+ const evidencePath=path.join(evidenceDir,`r36-parity-${candidateManifest.slice(0,16)}.json`);
193
+ const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');await writeFile(evidencePath,bytes);
194
+ return {evidence,evidence_path:evidencePath,evidence_sha256:sha256(bytes)};
195
+ }
196
+
197
+ if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){
198
+ const receipt=process.argv[2];if(!receipt) throw new Error('usage: node scripts/release-parity.mjs <activation-receipt.json>');
199
+ runReleaseParity(receipt).then(result=>console.log(JSON.stringify({status:'passed',...result},null,2))).catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
200
+ }
@@ -3,7 +3,7 @@ Set-StrictMode -Version 2.0
3
3
  $root = Split-Path -Parent $PSScriptRoot
4
4
  Push-Location $root
5
5
  try {
6
- & node.exe --test test\r36-finalization.test.mjs test\r36-release-identity.test.mjs test\deferred-slot-operation.test.mjs
6
+ & node.exe --test test\r37-finalization.test.mjs test\release-version.test.mjs test\r37-agent-fabric.test.mjs test\remote-live-log-r37.test.mjs test\remote-session-cli.test.mjs test\r36-finalization.test.mjs test\r36-release-identity.test.mjs test\deferred-slot-operation.test.mjs
7
7
  if ($LASTEXITCODE -ne 0) { throw "release parity tests failed with exit code $LASTEXITCODE" }
8
- Write-Output 'R36 RELEASE PARITY TEST PASS'
8
+ Write-Output 'R37 RELEASE PARITY TEST PASS'
9
9
  } finally { Pop-Location }
@@ -18,8 +18,8 @@ const launcher=path.join(stateRoot,'bin','DEADBYTE-MCP.ps1');
18
18
  const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
19
19
  const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
20
20
 
21
- export const R35_PREDECESSOR_RELEASE_ID='v0.9.0-b95e1ec1d9a7e26a';
22
- export const R35_PREDECESSOR_MANIFEST='b95e1ec1d9a7e26a96ef492306c1be4df18c883ff03ab3b6015f677d7429ec8d';
21
+ export const R36_PREDECESSOR_RELEASE_ID='v0.10.0-8d1e5f09745e7647';
22
+ export const R36_PREDECESSOR_MANIFEST='8d1e5f09745e764796b60e0a8a0168be7334ed4bf834fad5974a80eba295b4e8';
23
23
 
24
24
  function assert(condition,message){if(!condition) throw new Error(message);}
25
25
  function parseFrozenManifest(text){
@@ -91,54 +91,54 @@ async function scheduleDeferred(mode,value,{bridgeRoot=null,bridgeScriptSha256=n
91
91
  assert(scheduled.execution_provider==='deadbyte_mcp'&&scheduled.rdc_operation_count===0,'deferred operation provider mismatch');
92
92
  return {scheduled,completed:await waitDeferred(scheduled.state_path)};
93
93
  }
94
- async function publicR35Disarmed(url){
95
- const script=path.join(root,'src','remote-mcp-r24-disarmed-smoke-client.mjs');
96
- const result=parseJsonOutput((await run(process.execPath,[script,url],{timeoutMs:90000})).stdout,'R35 predecessor disarmed gate');
97
- assert(result.status==='passed'&&result.tool_count===73,'R35 predecessor public catalog mismatch');
98
- assert(result.coding_armed===false&&result.autonomous_armed===false,'R35 predecessor authority remained armed');
94
+ async function publicR36Disarmed(url){
95
+ const result=await probeR36CompactDisarmed(url);
96
+ assert(result.status==='passed'&&result.tool_count<=42&&result.capability_closure===true,'R36 predecessor public compact catalog mismatch');
97
+ assert(result.release_id===R36_PREDECESSOR_RELEASE_ID&&result.release_manifest_sha256===R36_PREDECESSOR_MANIFEST,'R36 predecessor machine binding mismatch');
98
+ assert(result.coding_armed===false&&result.autonomous_armed===false,'R36 predecessor authority remained armed');
99
99
  return result;
100
100
  }
101
101
  async function mutableCore(pointerValue){
102
102
  const policy=JSON.parse(await readFile(path.join(pointerValue.generation_root,'coding-policy.json'),'utf8'));
103
103
  const raw=policy?.roots?.core?.path??policy?.roots?.core?.real_path;
104
- assert(typeof raw==='string'&&raw,'R36 generation missing coding core root');
104
+ assert(typeof raw==='string'&&raw,'R37 generation missing coding core root');
105
105
  const core=path.resolve(raw);const release=path.resolve(pointerValue.release_root);
106
- assert(core.toLowerCase()!==release.toLowerCase(),'R36 coding core must not point at immutable active release slot');
106
+ assert(core.toLowerCase()!==release.toLowerCase(),'R37 coding core must not point at immutable active release slot');
107
107
  return core;
108
108
  }
109
109
 
110
110
  export function validateParityEvidence(value){
111
- assert(value?.schema==='deadbyte.r36-parity.v1','R36 parity schema mismatch');
112
- assert(value.status==='passed','R36 parity status is not passed');
113
- assert(value.rdc_operation_count===0&&value.execution_provider==='deadbyte_mcp','R36 parity used non-DEADBYTE execution');
114
- assert(value.candidate?.version==='0.10.0'&&/^[0-9a-f]{64}$/.test(value.candidate?.manifest_sha256??''),'R36 candidate identity missing');
115
- assert(value.local_full?.status==='passed'&&value.local_full?.tool_count===76&&value.local_full?.strict_output_schemas===true,'R36 local full 76-tool proof missing');
116
- assert(value.public_compact?.status==='passed'&&value.public_compact?.tool_count<=42&&value.public_compact?.capability_closure===true&&value.public_compact?.gateway_count===5,'R36 public compact closure missing');
117
- assert(value.autonomous?.status==='passed'&&value.autonomous?.signed_release_verified===true,'R36 autonomous live proof missing');
118
- assert(value.before_rollback_disarmed?.status==='passed'&&value.before_rollback_disarmed?.capability_closure===true&&value.before_rollback_disarmed?.coding_armed===false&&value.before_rollback_disarmed?.autonomous_armed===false,'R36 pre-rollback disarm proof missing');
119
- assert(value.rollback?.state==='completed'&&value.restore?.state==='completed','R36 rollback/restore not completed');
120
- assert(value.stage_restore?.status==='restored'&&value.stage_restore?.manifest_sha256===value.candidate.manifest_sha256,'R36 stage restore proof missing');
121
- assert(value.predecessor?.release_id===R35_PREDECESSOR_RELEASE_ID&&value.predecessor?.manifest_sha256===R35_PREDECESSOR_MANIFEST&&value.predecessor?.version==='0.9.0','R35 exact predecessor identity mismatch');
122
- assert(value.predecessor_disarmed?.status==='passed'&&value.predecessor_disarmed?.tool_count===73,'R35 predecessor public proof missing');
123
- assert(value.successor?.version==='0.10.0'&&value.successor?.manifest_sha256===value.candidate.manifest_sha256,'R36 exact successor restore identity mismatch');
124
- assert(value.successor_disarmed?.status==='passed'&&value.successor_disarmed?.capability_closure===true&&value.successor_disarmed?.coding_armed===false&&value.successor_disarmed?.autonomous_armed===false,'R36 successor disarmed proof missing');
125
- assert(value.immutable_slot?.protected===true&&value.immutable_slot?.development_root!==value.immutable_slot?.release_root,'R36 immutable-slot separation missing');
126
- for(const key of ['tree_read','precondition_mutation','search','owned_process','observation','coding_loop','autonomy','interrupted_recovery','evidence_verification','compact_closure','full_surface']) assert(value.requirements?.[key]===true,`R36 parity requirement not proven: ${key}`);
111
+ assert(value?.schema==='deadbyte.r37-parity.v1','R37 parity schema mismatch');
112
+ assert(value.status==='passed','R37 parity status is not passed');
113
+ assert(value.rdc_operation_count===0&&value.execution_provider==='deadbyte_mcp','R37 parity used non-DEADBYTE execution');
114
+ assert(value.candidate?.version==='0.11.0'&&/^[0-9a-f]{64}$/.test(value.candidate?.manifest_sha256??''),'R37 candidate identity missing');
115
+ assert(value.local_full?.status==='passed'&&value.local_full?.tool_count===76&&value.local_full?.strict_output_schemas===true,'R37 local full 76-tool proof missing');
116
+ assert(value.public_compact?.status==='passed'&&value.public_compact?.tool_count<=42&&value.public_compact?.capability_closure===true&&value.public_compact?.gateway_count===5,'R37 public compact closure missing');
117
+ assert(value.autonomous?.status==='passed'&&value.autonomous?.signed_release_verified===true,'R37 autonomous live proof missing');
118
+ assert(value.before_rollback_disarmed?.status==='passed'&&value.before_rollback_disarmed?.capability_closure===true&&value.before_rollback_disarmed?.coding_armed===false&&value.before_rollback_disarmed?.autonomous_armed===false,'R37 pre-rollback disarm proof missing');
119
+ assert(value.rollback?.state==='completed'&&value.restore?.state==='completed','R37 rollback/restore not completed');
120
+ assert(value.stage_restore?.status==='restored'&&value.stage_restore?.manifest_sha256===value.candidate.manifest_sha256,'R37 stage restore proof missing');
121
+ assert(value.predecessor?.release_id===R36_PREDECESSOR_RELEASE_ID&&value.predecessor?.manifest_sha256===R36_PREDECESSOR_MANIFEST&&value.predecessor?.version==='0.10.0','R36 exact predecessor identity mismatch');
122
+ assert(value.predecessor_disarmed?.status==='passed'&&value.predecessor_disarmed?.tool_count<=42&&value.predecessor_disarmed?.capability_closure===true,'R36 predecessor public proof missing');
123
+ assert(value.successor?.version==='0.11.0'&&value.successor?.manifest_sha256===value.candidate.manifest_sha256,'R37 exact successor restore identity mismatch');
124
+ assert(value.successor_disarmed?.status==='passed'&&value.successor_disarmed?.capability_closure===true&&value.successor_disarmed?.coding_armed===false&&value.successor_disarmed?.autonomous_armed===false,'R37 successor disarmed proof missing');
125
+ assert(value.immutable_slot?.protected===true&&value.immutable_slot?.development_root!==value.immutable_slot?.release_root,'R37 immutable-slot separation missing');
126
+ for(const key of ['tree_read','precondition_mutation','search','owned_process','observation','coding_loop','autonomy','interrupted_recovery','evidence_verification','compact_closure','full_surface']) assert(value.requirements?.[key]===true,`R37 parity requirement not proven: ${key}`);
127
127
  return true;
128
128
  }
129
129
 
130
130
  export async function runReleaseParity(activationReceiptPath){
131
131
  const active=await pointer();const candidateManifest=sha256(await readFile(path.join(root,'MANIFEST.SHA256')));
132
- assert(active.version==='0.10.0','R36 parity requires active 0.10.0 candidate');
133
- assert(active.manifest_sha256===candidateManifest,'active release is not canonical R36 candidate');
132
+ assert(active.version==='0.11.0','R37 parity requires active 0.11.0 candidate');
133
+ assert(active.manifest_sha256===candidateManifest,'active release is not canonical R37 candidate');
134
134
  const activeConfig=await loadPowerShellDataFile(active.config_path);
135
135
  const successorBridgeRootRaw=activeConfig?.Bridge?.WorkingDirectory;
136
136
  const successorBridgeConfiguredSha=activeConfig?.Bridge?.ScriptSha256;
137
- assert(typeof successorBridgeRootRaw==='string'&&path.isAbsolute(successorBridgeRootRaw),'R36 active bridge root missing');
138
- assert(typeof successorBridgeConfiguredSha==='string'&&/^[0-9a-f]{64}$/.test(successorBridgeConfiguredSha),'R36 active bridge SHA missing');
137
+ assert(typeof successorBridgeRootRaw==='string'&&path.isAbsolute(successorBridgeRootRaw),'R37 active bridge root missing');
138
+ assert(typeof successorBridgeConfiguredSha==='string'&&/^[0-9a-f]{64}$/.test(successorBridgeConfiguredSha),'R37 active bridge SHA missing');
139
139
  const successorBridgeRoot=await realpath(path.resolve(successorBridgeRootRaw));
140
140
  const successorBridgeActualSha=sha256(await readFile(path.join(successorBridgeRoot,'src','bridge.mjs')));
141
- assert(successorBridgeActualSha===successorBridgeConfiguredSha,'R36 active bridge bytes do not match generation pin');
141
+ assert(successorBridgeActualSha===successorBridgeConfiguredSha,'R37 active bridge bytes do not match generation pin');
142
142
  const successorBridge={root:successorBridgeRoot,sha256:successorBridgeActualSha};
143
143
  const developmentRoot=await mutableCore(active);
144
144
  const localFull=await probeR36FullSurface({packageRoot:active.release_root,generationRoot:active.generation_root,stateRoot});
@@ -146,25 +146,25 @@ export async function runReleaseParity(activationReceiptPath){
146
146
  let publicCompact,autonomous;
147
147
  try{
148
148
  publicCompact=await runR36CompactParity(url);
149
- autonomous=parseJsonOutput((await run(process.execPath,[path.join(root,'src','remote-mcp-autonomous-smoke-client.mjs'),url],{timeoutMs:900000})).stdout,'R36 autonomous compact parity');
150
- assert(autonomous.status==='passed'&&autonomous.signed_release_verified===true,'R36 autonomous compact parity failed');
149
+ autonomous=parseJsonOutput((await run(process.execPath,[path.join(root,'src','remote-mcp-autonomous-smoke-client.mjs'),url],{timeoutMs:900000})).stdout,'R37 autonomous compact parity');
150
+ assert(autonomous.status==='passed'&&autonomous.signed_release_verified===true,'R37 autonomous compact parity failed');
151
151
  }catch(error){await disarmAll().catch(()=>{});throw error;}
152
152
 
153
153
  await disarmAll();
154
154
  const beforeRollback=await probeR36CompactDisarmed(url);
155
155
  const rollbackPair=await scheduleDeferred('rollback',path.resolve(activationReceiptPath));
156
156
  const predecessor=await pointer();
157
- assert(predecessor.release_id===R35_PREDECESSOR_RELEASE_ID&&predecessor.manifest_sha256===R35_PREDECESSOR_MANIFEST&&predecessor.version==='0.9.0','rollback did not reach exact sealed R35 predecessor');
158
- const predecessorDisarmed=await publicR35Disarmed(url);
157
+ assert(predecessor.release_id===R36_PREDECESSOR_RELEASE_ID&&predecessor.manifest_sha256===R36_PREDECESSOR_MANIFEST&&predecessor.version==='0.10.0','rollback did not reach exact sealed R36 predecessor');
158
+ const predecessorDisarmed=await publicR36Disarmed(url);
159
159
  const stageRestore=await restoreFrozenStage({developmentRoot,releaseRoot:active.release_root,expectedManifestSha256:candidateManifest});
160
160
  const restorePair=await scheduleDeferred('activate',candidateManifest,{bridgeRoot:successorBridge.root,bridgeScriptSha256:successorBridge.sha256});
161
161
  const successor=await pointer();
162
- assert(successor.version==='0.10.0'&&successor.manifest_sha256===candidateManifest,'restore did not return exact R36 candidate');
162
+ assert(successor.version==='0.11.0'&&successor.manifest_sha256===candidateManifest,'restore did not return exact R37 candidate');
163
163
  const successorDisarmed=await probeR36CompactDisarmed(url);
164
164
  await disarmAll();
165
165
 
166
166
  const evidence={
167
- schema:'deadbyte.r36-parity.v1',status:'passed',completed_at_utc:new Date().toISOString(),execution_provider:'deadbyte_mcp',rdc_operation_count:0,
167
+ schema:'deadbyte.r37-parity.v1',status:'passed',completed_at_utc:new Date().toISOString(),execution_provider:'deadbyte_mcp',rdc_operation_count:0,
168
168
  candidate:{release_id:active.release_id,version:active.version,manifest_sha256:candidateManifest},
169
169
  immutable_slot:{protected:true,release_root:path.resolve(active.release_root),development_root:developmentRoot},
170
170
  local_full:localFull,public_compact:publicCompact,autonomous,before_rollback_disarmed:beforeRollback,
@@ -189,7 +189,7 @@ export async function runReleaseParity(activationReceiptPath){
189
189
  };
190
190
  validateParityEvidence(evidence);
191
191
  const evidenceDir=path.join(stateRoot,'evidence','release');await mkdir(evidenceDir,{recursive:true});
192
- const evidencePath=path.join(evidenceDir,`r36-parity-${candidateManifest.slice(0,16)}.json`);
192
+ const evidencePath=path.join(evidenceDir,`r37-parity-${candidateManifest.slice(0,16)}.json`);
193
193
  const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');await writeFile(evidencePath,bytes);
194
194
  return {evidence,evidence_path:evidencePath,evidence_sha256:sha256(bytes)};
195
195
  }
@@ -0,0 +1,52 @@
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
+
7
+ const here=path.dirname(fileURLToPath(import.meta.url));
8
+ const root=path.resolve(here,'..');
9
+ const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
10
+ const marker='DEADBYTE V0.10.0 / R36 WINDOWS BATCH GATE: PASS';
11
+ function insist(ok,message){if(!ok) throw new Error(message);}
12
+
13
+ export async function writeWindowsGateEvidence({
14
+ candidateRoot=root,
15
+ stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp')),
16
+ clock=()=>new Date()
17
+ }={}){
18
+ const candidate=path.resolve(candidateRoot);
19
+ const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
20
+ const manifestSha256=sha256(manifestBytes);
21
+ const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
22
+ insist(pkg.version==='0.10.0',`Windows gate evidence requires V0.10.0, found ${pkg.version}`);
23
+ const completed=clock();
24
+ insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'Windows gate evidence clock invalid');
25
+ const evidence={
26
+ schema:'deadbyte.r36-windows-gate.v1',status:'passed',exit_code:0,version:'0.10.0',
27
+ marker,completed_at_utc:completed.toISOString(),manifest_sha256:manifestSha256
28
+ };
29
+ const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');
30
+ const dir=path.join(path.resolve(stateRoot),'evidence','release');
31
+ await mkdir(dir,{recursive:true});
32
+ const file=path.join(dir,`r36-windows-gate-${manifestSha256.slice(0,16)}.json`);
33
+ try{
34
+ const handle=await open(file,'wx',0o600);
35
+ try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
36
+ return {status:'written',path:file,sha256:sha256(bytes),manifest_sha256:manifestSha256};
37
+ }catch(error){
38
+ if(error?.code!=='EEXIST') throw error;
39
+ const existingBytes=await readFile(file);
40
+ const existing=JSON.parse(existingBytes.toString('utf8'));
41
+ insist(existing.schema===evidence.schema&&existing.status==='passed'&&existing.exit_code===0,'existing Windows gate evidence invalid');
42
+ insist(existing.version==='0.10.0'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing Windows gate evidence binding mismatch');
43
+ return {status:'existing',path:file,sha256:sha256(existingBytes),manifest_sha256:manifestSha256};
44
+ }
45
+ }
46
+
47
+ const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
48
+ if(invoked){
49
+ writeWindowsGateEvidence()
50
+ .then(result=>console.log(JSON.stringify(result)))
51
+ .catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
52
+ }
@@ -7,7 +7,7 @@ import { fileURLToPath } from 'node:url';
7
7
  const here=path.dirname(fileURLToPath(import.meta.url));
8
8
  const root=path.resolve(here,'..');
9
9
  const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
10
- const marker='DEADBYTE V0.10.0 / R36 WINDOWS BATCH GATE: PASS';
10
+ const marker='DEADBYTE V0.11.0 / R37 WINDOWS BATCH GATE: PASS';
11
11
  function insist(ok,message){if(!ok) throw new Error(message);}
12
12
 
13
13
  export async function writeWindowsGateEvidence({
@@ -19,17 +19,17 @@ export async function writeWindowsGateEvidence({
19
19
  const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
20
20
  const manifestSha256=sha256(manifestBytes);
21
21
  const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
22
- insist(pkg.version==='0.10.0',`Windows gate evidence requires V0.10.0, found ${pkg.version}`);
22
+ insist(pkg.version==='0.11.0',`Windows gate evidence requires V0.11.0, found ${pkg.version}`);
23
23
  const completed=clock();
24
24
  insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'Windows gate evidence clock invalid');
25
25
  const evidence={
26
- schema:'deadbyte.r36-windows-gate.v1',status:'passed',exit_code:0,version:'0.10.0',
26
+ schema:'deadbyte.r37-windows-gate.v1',status:'passed',exit_code:0,version:'0.11.0',
27
27
  marker,completed_at_utc:completed.toISOString(),manifest_sha256:manifestSha256
28
28
  };
29
29
  const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');
30
30
  const dir=path.join(path.resolve(stateRoot),'evidence','release');
31
31
  await mkdir(dir,{recursive:true});
32
- const file=path.join(dir,`r36-windows-gate-${manifestSha256.slice(0,16)}.json`);
32
+ const file=path.join(dir,`r37-windows-gate-${manifestSha256.slice(0,16)}.json`);
33
33
  try{
34
34
  const handle=await open(file,'wx',0o600);
35
35
  try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
@@ -39,7 +39,7 @@ export async function writeWindowsGateEvidence({
39
39
  const existingBytes=await readFile(file);
40
40
  const existing=JSON.parse(existingBytes.toString('utf8'));
41
41
  insist(existing.schema===evidence.schema&&existing.status==='passed'&&existing.exit_code===0,'existing Windows gate evidence invalid');
42
- insist(existing.version==='0.10.0'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing Windows gate evidence binding mismatch');
42
+ insist(existing.version==='0.11.0'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing Windows gate evidence binding mismatch');
43
43
  return {status:'existing',path:file,sha256:sha256(existingBytes),manifest_sha256:manifestSha256};
44
44
  }
45
45
  }
@@ -0,0 +1,98 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { canonicalJson } from './canonical-json.mjs';
3
+
4
+ export const AGENT_MEMORY_SCHEMA='deadbyte.agent-memory.v1';
5
+ const HEX64=/^[0-9a-f]{64}$/;
6
+ const sha=value=>createHash('sha256').update(Buffer.from(canonicalJson(value),'utf8')).digest('hex');
7
+ const uniq=values=>[...new Set(values.filter(v=>typeof v==='string'&&v.length>0))];
8
+
9
+ function tokens(text){return uniq(String(text??'').toLowerCase().split(/[^a-z0-9_.\/-]+/).filter(x=>x.length>1));}
10
+ function compactEvent(event){
11
+ const p=event?.payload&&typeof event.payload==='object'?event.payload:{};
12
+ const safe={seq:event?.seq??null,type:event?.type??'unknown',event_sha256:HEX64.test(event?.event_sha256??'')?event.event_sha256:null};
13
+ for(const key of ['reason','profile_id','mutation_epoch','receipt_sha256','diff_receipt_sha256','action_key']){
14
+ if(p[key]!==undefined) safe[key]=p[key];
15
+ }
16
+ if(Array.isArray(p.paths)) safe.paths=p.paths.filter(x=>typeof x==='string').slice(0,32);
17
+ return safe;
18
+ }
19
+
20
+ export function deriveAgentMemory(events,{maxArchival=96}={}){
21
+ if(!Array.isArray(events)) throw new Error('memory events must be array');
22
+ const acceptance=[],proven=[],rejected=[],blockers=new Set(),files=new Map(),profiles=new Map();
23
+ const archival=[];
24
+ for(const event of events){
25
+ const p=event?.payload&&typeof event.payload==='object'?event.payload:{};
26
+ if(event?.type==='goal_created'&&typeof p.acceptance==='string'&&p.acceptance) acceptance.push(p.acceptance);
27
+ if(event?.type==='profile_completed'&&typeof p.profile_id==='string'){
28
+ profiles.set(p.profile_id,{id:`profile:${p.profile_id}`,kind:'profile',label:p.profile_id,passed:p.passed===true,
29
+ receipt_sha256:HEX64.test(p.receipt_sha256??'')?p.receipt_sha256:null});
30
+ if(p.passed===true){proven.push(`${p.profile_id}:passed`);blockers.delete(`profile:${p.profile_id}`);}
31
+ else blockers.add(`profile:${p.profile_id}`);
32
+ }
33
+ if(event?.type==='native_diff_committed'){
34
+ for(const file of p.paths??[]) if(typeof file==='string'&&file) files.set(file,{id:`file:${file}`,kind:'file',label:file});
35
+ }
36
+ if(event?.type==='planner_rejected'&&typeof p.reason==='string') rejected.push(p.reason);
37
+ if(event?.type==='waiting_entered'&&typeof p.reason==='string') blockers.add(`wait:${p.reason}`);
38
+ if(event?.type==='waiting_resumed'&&typeof p.previous_reason==='string') blockers.delete(`wait:${p.previous_reason}`);
39
+ if(['planner_rejected','profile_completed','native_diff_committed','waiting_entered','waiting_resumed','release_completed'].includes(event?.type)){
40
+ archival.push(compactEvent(event));
41
+ }
42
+ }
43
+ const core={acceptance_constraints:uniq(acceptance),proven_invariants:uniq(proven),
44
+ rejected_approaches:uniq(rejected),unresolved_blockers:[...blockers].sort()};
45
+ const nodes=[
46
+ {id:'goal:current',kind:'goal',label:core.acceptance_constraints.join(' | ')||'current goal'},
47
+ ...[...files.values()].sort((a,b)=>a.id.localeCompare(b.id,'en')),
48
+ ...[...profiles.values()].sort((a,b)=>a.id.localeCompare(b.id,'en')),
49
+ ...core.unresolved_blockers.map(id=>({id:`blocker:${id}`,kind:'blocker',label:id}))
50
+ ];
51
+ const edges=[];
52
+ for(const node of nodes){
53
+ if(node.id==='goal:current') continue;
54
+ edges.push({source:'goal:current',target:node.id,kind:node.kind==='file'?'touches':node.kind==='profile'?'verified_by':'blocked_by'});
55
+ }
56
+ const body={schema:AGENT_MEMORY_SCHEMA,core,archival:archival.slice(-maxArchival),graph:{nodes,edges}};
57
+ return Object.freeze({...body,memory_sha256:sha(body)});
58
+ }
59
+
60
+ function score(value,needles){
61
+ const text=JSON.stringify(value).toLowerCase();
62
+ return needles.reduce((n,t)=>n+(text.includes(t)?1:0),0);
63
+ }
64
+ function ranked(values,needles,limit){
65
+ return values.map(value=>({value,score:score(value,needles)})).filter(x=>x.score>0)
66
+ .sort((a,b)=>b.score-a.score||JSON.stringify(a.value).localeCompare(JSON.stringify(b.value),'en'))
67
+ .slice(0,limit).map(x=>x.value);
68
+ }
69
+ export function queryAgentMemory(memory,{query,mode='local',limit=12}={}){
70
+ if(!memory||memory.schema!==AGENT_MEMORY_SCHEMA) throw new Error('memory snapshot invalid');
71
+ if(!['local','global','drift'].includes(mode)) throw new Error('memory query mode invalid');
72
+ if(!Number.isInteger(limit)||limit<1||limit>128) throw new Error('memory query limit invalid');
73
+ const needles=tokens(query);
74
+ let results=[];
75
+ if(mode==='global'){
76
+ results=[
77
+ {kind:'core',...memory.core},
78
+ {kind:'graph_summary',node_count:memory.graph.nodes.length,edge_count:memory.graph.edges.length,
79
+ node_kinds:Object.fromEntries([...new Set(memory.graph.nodes.map(n=>n.kind))].sort().map(k=>[k,memory.graph.nodes.filter(n=>n.kind===k).length]))}
80
+ ];
81
+ if(needles.length) results=ranked([...results,...memory.archival],needles,limit);
82
+ else results=results.slice(0,limit);
83
+ }else{
84
+ const local=ranked([...memory.graph.nodes,...memory.archival],needles,limit);
85
+ results=[...local];
86
+ if(mode==='drift'){
87
+ const ids=new Set(local.filter(x=>x?.id).map(x=>x.id));
88
+ const neighborIds=new Set();
89
+ for(const edge of memory.graph.edges){
90
+ if(ids.has(edge.source)) neighborIds.add(edge.target);
91
+ if(ids.has(edge.target)) neighborIds.add(edge.source);
92
+ }
93
+ for(const node of memory.graph.nodes) if(neighborIds.has(node.id)&&!results.some(x=>x?.id===node.id)) results.push(node);
94
+ results=results.slice(0,limit);
95
+ }
96
+ }
97
+ return Object.freeze({schema:'deadbyte.agent-memory-query.v1',mode,query:String(query??''),memory_sha256:memory.memory_sha256,results});
98
+ }
@@ -98,9 +98,12 @@ export const autonomousGoalRunOutputSchema = z.strictObject({
98
98
  if (value.status === 'input_required') requireFields(value,ctx,['planner_request']);
99
99
  });
100
100
  export const autonomousGoalStatusOutputSchema = statusContract({
101
- statuses:['ok','failed'], success:['ok'], required:['goal_id','goal_sha256','title','root_id','journal_verified',...Object.keys(derivedShape)],
101
+ statuses:['ok','failed'], success:['ok'], required:['goal_id','goal_sha256','title','root_id','journal_verified',
102
+ 'run_busy','run_owner_pid','run_lock_created_at_utc',...Object.keys(derivedShape)],
102
103
  shape:{ goal_id:goalId.optional(), goal_sha256:hex64.optional(), title:z.string().optional(), root_id:z.string().optional(),
103
- journal_verified:z.literal(true).optional(), loop:publicLoopSchema.optional(), ...runShape }
104
+ journal_verified:z.literal(true).optional(), run_busy:z.boolean().optional(),
105
+ run_owner_pid:z.number().int().min(1).nullable().optional(),
106
+ run_lock_created_at_utc:z.string().nullable().optional(), loop:publicLoopSchema.optional(), ...runShape }
104
107
  });
105
108
  export const autonomousGoalEventsOutputSchema = statusContract({
106
109
  statuses:['ok','failed'], success:['ok'], required:['goal_id','journal_verified','journal_head_sha256','events'],