deadbyte-mcp 0.10.0 → 0.11.1

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 (82) hide show
  1. package/MANIFEST.SHA256 +81 -59
  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/build-containment.ps1 +15 -15
  36. package/scripts/deferred-slot-operation-r37-0110.mjs +124 -0
  37. package/scripts/deferred-slot-operation.mjs +1 -1
  38. package/scripts/final-closure-r36.mjs +113 -0
  39. package/scripts/final-closure-r37-0110.mjs +113 -0
  40. package/scripts/final-closure-verify-r36.mjs +208 -0
  41. package/scripts/final-closure-verify-r37-0110.mjs +208 -0
  42. package/scripts/final-closure-verify.mjs +48 -48
  43. package/scripts/final-closure.mjs +13 -13
  44. package/scripts/gate-windows-r36.ps1 +42 -0
  45. package/scripts/gate-windows-r37-0110.ps1 +42 -0
  46. package/scripts/gate-windows.ps1 +3 -3
  47. package/scripts/release-parity-r36.mjs +200 -0
  48. package/scripts/release-parity-r37-0110.mjs +200 -0
  49. package/scripts/release-parity-tests.ps1 +2 -2
  50. package/scripts/release-parity.mjs +37 -37
  51. package/scripts/windows-gate-evidence-r36.mjs +52 -0
  52. package/scripts/windows-gate-evidence-r37-0110.mjs +52 -0
  53. package/scripts/windows-gate-evidence.mjs +5 -5
  54. package/src/agent-memory-plane.mjs +98 -0
  55. package/src/autonomous-output-contracts.mjs +5 -2
  56. package/src/autonomous-runtime.mjs +155 -31
  57. package/src/autonomous-timeout-recovery.mjs +27 -0
  58. package/src/deadbyte-cli.mjs +90 -94
  59. package/src/effect-ledger.mjs +47 -0
  60. package/src/observation-lease.mjs +32 -0
  61. package/src/remote-live-log.mjs +81 -0
  62. package/src/remote-mcp-autonomous-smoke-client.mjs +15 -9
  63. package/src/remote-result-journal.mjs +23 -4
  64. package/src/version.mjs +1 -1
  65. package/src/worker-scheduler.mjs +53 -0
  66. package/test/autonomous-output-contract.test.mjs +1 -1
  67. package/test/cli-entrypoint.test.mjs +9 -0
  68. package/test/containment.test.mjs +18 -0
  69. package/test/core.test.mjs +1 -1
  70. package/test/production-docs.test.mjs +5 -5
  71. package/test/r33-closeout-regression.test.mjs +4 -4
  72. package/test/r33-finalization.test.mjs +6 -6
  73. package/test/r36-finalization.test.mjs +5 -5
  74. package/test/r36-release-identity.test.mjs +12 -19
  75. package/test/r37-0110-history.test.mjs +20 -0
  76. package/test/r37-agent-fabric.test.mjs +92 -0
  77. package/test/r37-autonomous-timeout-recovery.test.mjs +60 -0
  78. package/test/r37-finalization.test.mjs +74 -0
  79. package/test/r37-remote-tool-call-timeline.test.mjs +94 -0
  80. package/test/release-version.test.mjs +35 -36
  81. package/test/remote-live-log-r37.test.mjs +50 -0
  82. package/test/remote-session-cli.test.mjs +86 -68
@@ -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.11.0 / R37 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.11.0',`Windows gate evidence requires V0.11.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.r37-windows-gate.v1',status:'passed',exit_code:0,version:'0.11.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,`r37-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.11.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.1 / 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.1',`Windows gate evidence requires V0.11.1, 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.1',
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.1'&&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'],
@@ -12,6 +12,10 @@ import { judgeProgress, stallDisposition } from './autonomous-progress.mjs';
12
12
  import { classifyProviderFailure, deriveCircuit, providerCallDisposition, providerCircuitId } from './provider-circuit.mjs';
13
13
  import { verifyObservationReceiptFile } from './observation-runtime.mjs';
14
14
  import { R36_CAPABILITY_LEDGER } from './capability-ledger.mjs';
15
+ import { deriveAgentMemory } from './agent-memory-plane.mjs';
16
+ import { effectIdentity, loopGuardDisposition, bindEffectCompletion } from './effect-ledger.mjs';
17
+ import { createWorkerScheduler } from './worker-scheduler.mjs';
18
+ import { issueObservationLease, verifyObservationLease } from './observation-lease.mjs';
15
19
 
16
20
  export const AUTONOMOUS_GOAL_SCHEMA = 'deadbyte.autonomous-goal.v1';
17
21
  export const AUTONOMOUS_EVENT_SCHEMA = 'deadbyte.autonomous-event.v1';
@@ -164,6 +168,22 @@ async function acquireLock(ctx) {
164
168
  throw new Error('autonomous lock acquisition failed');
165
169
  }
166
170
  async function releaseLock(ctx) { await rm(ctx.lockPath,{force:true}); }
171
+ async function runLockStatus(ctx) {
172
+ let parsed;
173
+ try { parsed = JSON.parse(await readFile(ctx.lockPath,'utf8')); }
174
+ catch (error) {
175
+ if (error?.code === 'ENOENT') return { run_busy:false, run_owner_pid:null, run_lock_created_at_utc:null };
176
+ throw new Error('autonomous lock malformed');
177
+ }
178
+ if (parsed?.schema !== 'deadbyte.autonomous-lock.v1' || parsed?.goal_id !== path.basename(ctx.dir) ||
179
+ !Number.isInteger(parsed?.pid) || parsed.pid < 1 || typeof parsed?.created_at_utc !== 'string' || !parsed.created_at_utc) {
180
+ throw new Error('autonomous lock malformed');
181
+ }
182
+ const alive=await processExists(parsed.pid);
183
+ return alive
184
+ ? { run_busy:true, run_owner_pid:parsed.pid, run_lock_created_at_utc:parsed.created_at_utc }
185
+ : { run_busy:false, run_owner_pid:null, run_lock_created_at_utc:null };
186
+ }
167
187
  async function readCancel(ctx, verifyKeyPath) {
168
188
  if (!await exists(ctx.cancelPath)) return null;
169
189
  const parsed = JSON.parse(await readFile(ctx.cancelPath,'utf8'));
@@ -339,11 +359,12 @@ function configuredCapabilityManifest({machineFsRuntime,observationRuntime,proce
339
359
  entries:Object.freeze(entries)});
340
360
  }
341
361
 
342
- async function plannerView(goal, state, codingRuntime, capabilityManifest) {
362
+ async function plannerView(goal, state, events, codingRuntime, capabilityManifest) {
343
363
  const tree = await codingRuntime.tree({ rootId:goal.root_id, path:'.', maxDepth:4, maxEntries:600 });
364
+ const memory=deriveAgentMemory(events,{maxArchival:24});
344
365
  return { mutation_epoch:state.mutation_epoch, planner_round:state.planner_round, tree:tree.entries,
345
366
  contexts:state.contexts.slice(-16), observations:state.observations.slice(-24), active_plan:state.active_plan,
346
- capabilities:capabilityManifest,
367
+ capabilities:capabilityManifest, project_memory:memory,
347
368
  recent_decisions:state.decisions.slice(-8).map(item => ({ planner_round:item.planner_round,
348
369
  action:item.decision?.action, summary:item.decision?.summary })) };
349
370
  }
@@ -361,7 +382,7 @@ async function requestPlanner(ctx, goal, events, state, policy, codingRuntime, c
361
382
  await appendEvent(ctx,events,signingKeyPath,'goal_paused',{ reason:'wall_time_limit', max_wall_ms:policy.limits.max_wall_ms });
362
383
  return null;
363
384
  }
364
- const view = await plannerView(goal,state,codingRuntime,capabilityManifest);
385
+ const view = await plannerView(goal,state,events,codingRuntime,capabilityManifest);
365
386
  let prompt = buildPlannerPrompt({ goal:{ title:goal.title, goal:goal.goal, acceptance:goal.acceptance }, policy, view });
366
387
  const repair = [...(state.planner_rejections ?? [])].reverse().find(item => item.planner_round === round) ?? null;
367
388
  if (repair) {
@@ -749,14 +770,36 @@ async function executeDelegate({ ctx, events, goal, decisionEvent, decision, pol
749
770
  }
750
771
  }
751
772
 
752
- async function runMutationProfiles({ ctx, events, goal, epoch, actionKey, policy, codingRuntime, signingKeyPath }) {
773
+ async function executeProfileWithLease({workerScheduler,ctx,events,goal,epoch,actionKey,profileId,policy,codingRuntime,signingKeyPath,r34ResumePhase=null}) {
774
+ if(!workerScheduler) throw new Error('R37 worker scheduler unavailable');
775
+ const taskId=`${goal.goal_id}:${actionKey}:${profileId}`;
776
+ const lease=workerScheduler.acquire({taskId,workerClass:'profile',cpuUnits:1,memoryMb:256});
777
+ await appendEvent(ctx,events,signingKeyPath,'worker_lease_acquired',{
778
+ lease_id:lease.lease_id,task_id:lease.task_id,worker_class:lease.worker_class,profile_id:profileId,
779
+ attempt:lease.attempt,cpu_units:lease.cpu_units,memory_mb:lease.memory_mb,expires_at_ms:lease.expires_at_ms
780
+ });
781
+ let outcomeStatus='failed';
782
+ try {
783
+ const result=await executeProfile({ctx,events,goalId:goal.goal_id,actionKey,profileId,epoch,policy,codingRuntime,signingKeyPath,r34ResumePhase});
784
+ outcomeStatus=result.status;
785
+ return result;
786
+ } finally {
787
+ const released=workerScheduler.release(lease.lease_id,{status:outcomeStatus});
788
+ await appendEvent(ctx,events,signingKeyPath,'worker_lease_released',{
789
+ lease_id:lease.lease_id,task_id:lease.task_id,worker_class:lease.worker_class,profile_id:profileId,
790
+ status:released.lease.status,released_at_ms:released.lease.released_at_ms
791
+ });
792
+ }
793
+ }
794
+
795
+ async function runMutationProfiles({ ctx, events, goal, epoch, actionKey, policy, codingRuntime, signingKeyPath, workerScheduler }) {
753
796
  const profileIds = Object.entries(policy.profiles)
754
797
  .filter(([,profile]) => profile.run_after_mutation)
755
798
  .map(([id]) => id);
756
799
  const results = [];
757
800
  for (const profileId of profileIds) {
758
- const result = await executeProfile({ ctx,events,goalId:goal.goal_id,actionKey:`${actionKey}:${profileId}`,
759
- profileId,epoch,policy,codingRuntime,signingKeyPath });
801
+ const result = await executeProfileWithLease({workerScheduler,ctx,events,goal,epoch,
802
+ actionKey:`${actionKey}:${profileId}`,profileId,policy,codingRuntime,signingKeyPath});
760
803
  results.push({ profile_id:profileId, status:result.status, observation:result.observation });
761
804
  if (result.status === 'paused') break;
762
805
  }
@@ -817,7 +860,7 @@ async function appendPatchCommit({ctx,events,signingKeyPath,started,decisionEven
817
860
  });
818
861
  }
819
862
  async function executePatch({ ctx, events, goal, state, decisionEvent, decision, nativeDiffRuntime,
820
- policy, codingRuntime, signingKeyPath, actionCoordinatesOverride = null }) {
863
+ policy, codingRuntime, signingKeyPath, workerScheduler, actionCoordinatesOverride = null }) {
821
864
  let started = patchStartedForDecision(events,decisionEvent.event_sha256);
822
865
  if (!started && state.mutation_count + decision.operations.length > policy.limits.max_mutations) {
823
866
  await appendEvent(ctx,events,signingKeyPath,'goal_paused',{
@@ -853,7 +896,7 @@ async function executePatch({ ctx, events, goal, state, decisionEvent, decision,
853
896
  if (!committed) throw new Error('completed patch action missing native diff commit');
854
897
  const epoch = committed.payload.mutation_epoch;
855
898
  const profiles = await runMutationProfiles({ ctx,events,goal,epoch,
856
- actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath });
899
+ actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath,workerScheduler });
857
900
  return { status:profiles.some(item => item.status === 'paused') ? 'paused' : 'completed',
858
901
  diff_id:diffId, mutation_epoch:epoch, profiles, recovered:true };
859
902
  }
@@ -901,12 +944,12 @@ async function executePatch({ ctx, events, goal, state, decisionEvent, decision,
901
944
  }
902
945
  const epoch = committed.payload.mutation_epoch;
903
946
  const profiles = await runMutationProfiles({ ctx,events,goal,epoch,
904
- actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath });
947
+ actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath,workerScheduler });
905
948
  return { status:profiles.some(item => item.status === 'paused') ? 'paused' : 'completed',
906
949
  diff_id:diffId, mutation_epoch:epoch, profiles, recovered };
907
950
  }
908
951
  async function executePreparedDiff({ctx,events,goal,state,decisionEvent,diffId,expectedOperationCount,
909
- policy,codingRuntime,nativeDiffRuntime,signingKeyPath}) {
952
+ policy,codingRuntime,nativeDiffRuntime,signingKeyPath,workerScheduler}) {
910
953
  let started=patchStartedForDecision(events,decisionEvent.event_sha256);
911
954
  if(!started&&state.mutation_count+expectedOperationCount>policy.limits.max_mutations){
912
955
  await appendEvent(ctx,events,signingKeyPath,'goal_paused',{reason:'mutation_limit',max_mutations:policy.limits.max_mutations});
@@ -929,7 +972,7 @@ async function executePreparedDiff({ctx,events,goal,state,decisionEvent,diffId,e
929
972
  const committed=patchCommitForAction(events,actionId);
930
973
  if(!committed) throw new Error('completed prepared diff action missing native diff commit');
931
974
  const epoch=committed.payload.mutation_epoch;
932
- const profiles=await runMutationProfiles({ctx,events,goal,epoch,actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath});
975
+ const profiles=await runMutationProfiles({ctx,events,goal,epoch,actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath,workerScheduler});
933
976
  return {status:profiles.some(item=>item.status==='paused')?'paused':'completed',diff_id:diffId,mutation_epoch:epoch,profiles,recovered:true};
934
977
  }
935
978
  const reconciliation=await reconcileStartedAction({startedEvent:started,journal:events,runtimes:{nativeDiffRuntime,codingRuntime}});
@@ -963,10 +1006,10 @@ async function executePreparedDiff({ctx,events,goal,state,decisionEvent,diffId,e
963
1006
  receipt_sha256:verified.receipt_sha256,recovered});
964
1007
  }
965
1008
  const epoch=committed.payload.mutation_epoch;
966
- const profiles=await runMutationProfiles({ctx,events,goal,epoch,actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath});
1009
+ const profiles=await runMutationProfiles({ctx,events,goal,epoch,actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath,workerScheduler});
967
1010
  return {status:profiles.some(item=>item.status==='paused')?'paused':'completed',diff_id:diffId,mutation_epoch:epoch,profiles,recovered};
968
1011
  }
969
- async function runRequiredReleaseProfiles({ ctx, events, goal, state, policy, codingRuntime, signingKeyPath }) {
1012
+ async function runRequiredReleaseProfiles({ ctx, events, goal, state, policy, codingRuntime, signingKeyPath, workerScheduler }) {
970
1013
  const requiredIds = Object.entries(policy.profiles)
971
1014
  .filter(([,profile]) => profile.required_for_release)
972
1015
  .map(([id]) => id);
@@ -977,9 +1020,9 @@ async function runRequiredReleaseProfiles({ ctx, events, goal, state, policy, co
977
1020
  results.push({ profile_id:profileId, status:'passed', observation:existing, existing:true });
978
1021
  continue;
979
1022
  }
980
- const result = await executeProfile({ ctx,events,goalId:goal.goal_id,
981
- actionKey:`release-${state.mutation_epoch}:${profileId}`, profileId,
982
- epoch:state.mutation_epoch,policy,codingRuntime,signingKeyPath });
1023
+ const result = await executeProfileWithLease({workerScheduler,ctx,events,goal,
1024
+ actionKey:`release-${state.mutation_epoch}:${profileId}`,profileId,
1025
+ epoch:state.mutation_epoch,policy,codingRuntime,signingKeyPath});
983
1026
  results.push({ profile_id:profileId, ...result });
984
1027
  if (result.status === 'paused') break;
985
1028
  }
@@ -1153,7 +1196,7 @@ function r34ReasonHash(goal,decisionEvent,reason) {
1153
1196
  }
1154
1197
  async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,policy,codingRuntime,
1155
1198
  nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,assertRunAuthority,
1156
- signingKeyPath,verifyKeyPath}) {
1199
+ signingKeyPath,verifyKeyPath,workerScheduler}) {
1157
1200
  let loop=deriveLoopState(goal,events);
1158
1201
  if (decision.kind === 'plan_create') {
1159
1202
  if (loop.phase !== 'PLAN' || loop.plan) throw new Error('R34 plan_create requires empty PLANNING state');
@@ -1196,7 +1239,7 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
1196
1239
  try {
1197
1240
  outcome=await executePatch({ctx,events,goal,state,decisionEvent,
1198
1241
  decision:{operations:decision.action.operations,summary:`R34 ${currentStep.step_id}`},nativeDiffRuntime,
1199
- policy,codingRuntime,signingKeyPath,actionCoordinatesOverride:{planId:loop.plan_id,
1242
+ policy,codingRuntime,signingKeyPath,workerScheduler,actionCoordinatesOverride:{planId:loop.plan_id,
1200
1243
  planVersion:loop.plan_version,stepId:currentStep.step_id}});
1201
1244
  } catch (error) {
1202
1245
  const detail=truncated(error,512);
@@ -1209,7 +1252,7 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
1209
1252
  }
1210
1253
  } else if (decision.action.kind === 'run_profile') {
1211
1254
  await assertRunAuthority(goal.root_id);
1212
- outcome=await executeProfile({ctx,events,goalId:goal.goal_id,
1255
+ outcome=await executeProfileWithLease({workerScheduler,ctx,events,goal,
1213
1256
  actionKey:`r34-${decisionEvent.event_sha256}`,profileId:decision.action.profile_id,
1214
1257
  epoch:state.mutation_epoch,policy,codingRuntime,signingKeyPath,r34ResumePhase:loop.phase});
1215
1258
  } else if (decision.action.kind === 'delegate') {
@@ -1219,7 +1262,7 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
1219
1262
  } else if (decision.action.kind === 'native_diff_apply') {
1220
1263
  await assertRunAuthority(goal.root_id);
1221
1264
  outcome=await executePreparedDiff({ctx,events,goal,state,decisionEvent,diffId:decision.action.diff_id,
1222
- expectedOperationCount:decision.action.expected_operation_count,policy,codingRuntime,nativeDiffRuntime,signingKeyPath});
1265
+ expectedOperationCount:decision.action.expected_operation_count,policy,codingRuntime,nativeDiffRuntime,signingKeyPath,workerScheduler});
1223
1266
  } else if (['machine_fs_mkdir','machine_fs_move','machine_fs_delete'].includes(decision.action.kind)) {
1224
1267
  await assertRunAuthority(goal.root_id);
1225
1268
  if(!machineFsRuntime) throw new Error('machine filesystem runtime unavailable');
@@ -1240,7 +1283,7 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
1240
1283
  }
1241
1284
  }
1242
1285
  outcome=await executePatch({ctx,events,goal,state,decisionEvent,decision:{operations,summary:`R36 ${decision.action.kind}`},
1243
- nativeDiffRuntime,policy,codingRuntime,signingKeyPath});
1286
+ nativeDiffRuntime,policy,codingRuntime,signingKeyPath,workerScheduler});
1244
1287
  } else if (capabilityIdForOperation(decision.action)) {
1245
1288
  outcome=await executeDelegate({ctx,events,goal,decisionEvent,
1246
1289
  decision:{operation:decision.action},policy,
@@ -1347,14 +1390,14 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
1347
1390
  throw new Error(`unsupported R34 decision '${String(decision.kind)}'`);
1348
1391
  }
1349
1392
 
1350
- async function executeDecision({ ctx, events, goal, state, decisionEvent, policy, codingRuntime,
1393
+ async function executeDecisionCore({ ctx, events, goal, state, decisionEvent, policy, codingRuntime,
1351
1394
  nativeDiffRuntime, machineFsRuntime, observationRuntime, processRuntime, semanticRuntime, assertRunAuthority,
1352
- signingKeyPath, verifyKeyPath }) {
1395
+ signingKeyPath, verifyKeyPath, workerScheduler }) {
1353
1396
  const decision = decisionEvent.decision;
1354
1397
  if (decision?.schema === 'deadbyte.autonomous-decision.v2') {
1355
1398
  const outcome=await executeR34Decision({ctx,events,goal,state,decisionEvent,decision,policy,codingRuntime,
1356
1399
  nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,assertRunAuthority,
1357
- signingKeyPath,verifyKeyPath});
1400
+ signingKeyPath,verifyKeyPath,workerScheduler});
1358
1401
  await appendEvent(ctx,events,signingKeyPath,'decision_completed',{
1359
1402
  decision_event_sha256:decisionEvent.event_sha256,action:decision.kind,
1360
1403
  outcome_status:outcome.status ?? 'completed'
@@ -1384,15 +1427,15 @@ async function executeDecision({ ctx, events, goal, state, decisionEvent, policy
1384
1427
  } else if (decision.action === 'patch') {
1385
1428
  await assertRunAuthority(goal.root_id);
1386
1429
  outcome = await executePatch({ ctx,events,goal,state,decisionEvent,decision,nativeDiffRuntime,
1387
- policy,codingRuntime,signingKeyPath });
1430
+ policy,codingRuntime,signingKeyPath,workerScheduler });
1388
1431
  } else if (decision.action === 'delegate') {
1389
1432
  outcome = await executeDelegate({ ctx,events,goal,decisionEvent,decision,policy,
1390
1433
  runtimes:{ machineFsRuntime,observationRuntime,processRuntime },assertRunAuthority,signingKeyPath,verifyKeyPath });
1391
1434
  } else if (decision.action === 'run_profile') {
1392
1435
  await assertRunAuthority(goal.root_id);
1393
- outcome = await executeProfile({ ctx,events,goalId:goal.goal_id,
1394
- actionKey:`planner-${decisionEvent.event_sha256}`, profileId:decision.profile_id,
1395
- epoch:state.mutation_epoch,policy,codingRuntime,signingKeyPath });
1436
+ outcome = await executeProfileWithLease({workerScheduler,ctx,events,goal,
1437
+ actionKey:`planner-${decisionEvent.event_sha256}`,profileId:decision.profile_id,
1438
+ epoch:state.mutation_epoch,policy,codingRuntime,signingKeyPath});
1396
1439
  } else if (decision.action === 'pause') {
1397
1440
  await appendEvent(ctx,events,signingKeyPath,'goal_paused',{
1398
1441
  reason:decision.reason, planner_summary:decision.summary
@@ -1414,6 +1457,83 @@ async function executeDecision({ ctx, events, goal, state, decisionEvent, policy
1414
1457
  return outcome;
1415
1458
  }
1416
1459
 
1460
+ function effectDescriptor(goal,state,events,decisionEvent){
1461
+ const decision=decisionEvent?.decision;
1462
+ let action=null,coordinates=null;
1463
+ if(decision?.schema==='deadbyte.autonomous-decision.v2'&&decision.kind==='act'){
1464
+ const loop=deriveLoopState(goal,events);
1465
+ if(!loop.plan_id||!loop.current_step?.step_id) return null;
1466
+ action=decision.action;
1467
+ coordinates={planId:loop.plan_id,stepId:loop.current_step.step_id};
1468
+ }else if(['patch','delegate','run_profile'].includes(decision?.action)){
1469
+ coordinates=patchActionCoordinates(state);
1470
+ if(decision.action==='patch') action={kind:'patch',operations:decision.operations};
1471
+ else if(decision.action==='delegate') action={kind:'delegate',operation:decision.operation};
1472
+ else action={kind:'run_profile',profile_id:decision.profile_id};
1473
+ }
1474
+ if(!action||!coordinates) return null;
1475
+ const observed=[...events].reverse().find(event=>[
1476
+ 'inspection_completed','inspect_completed','profile_completed','delegated_action_completed','observation_completed'
1477
+ ].includes(event.type)&&SHA256_RE.test(event.event_sha256??''))?.event_sha256 ?? state.journal_head_sha256;
1478
+ return {action,planId:coordinates.planId,stepId:coordinates.stepId,observationHeadSha256:observed};
1479
+ }
1480
+
1481
+ async function executeDecision(args) {
1482
+ const {ctx,events,goal,state,decisionEvent,signingKeyPath}=args;
1483
+ const descriptor=effectDescriptor(goal,state,events,decisionEvent);
1484
+ if(!descriptor) return executeDecisionCore(args);
1485
+ const lease=issueObservationLease({goalId:goal.goal_id,mutationEpoch:state.mutation_epoch,
1486
+ observations:[{event_sha256:descriptor.observationHeadSha256}],ttlMs:120000});
1487
+ verifyObservationLease(lease,{goalId:goal.goal_id,currentMutationEpoch:state.mutation_epoch,
1488
+ observationHeadSha256:descriptor.observationHeadSha256});
1489
+ if(!events.some(event=>event.type==='observation_lease_issued'&&event.payload?.lease_id===lease.lease_id)){
1490
+ await appendEvent(ctx,events,signingKeyPath,'observation_lease_issued',{
1491
+ lease_id:lease.lease_id,mutation_epoch:lease.mutation_epoch,
1492
+ observation_head_sha256:lease.observation_head_sha256,expires_at_ms:lease.expires_at_ms,
1493
+ decision_event_sha256:decisionEvent.event_sha256
1494
+ });
1495
+ }
1496
+ const effect=effectIdentity({goalId:goal.goal_id,planId:descriptor.planId,stepId:descriptor.stepId,
1497
+ mutationEpoch:state.mutation_epoch,action:descriptor.action,observationHeadSha256:descriptor.observationHeadSha256});
1498
+ const guard=loopGuardDisposition(events,effect,{maxRepeats:2,window:48});
1499
+ if(!guard.allowed){
1500
+ await appendEvent(ctx,events,signingKeyPath,'effect_guard_blocked',{
1501
+ effect_id:effect.effect_id,fingerprint:effect.fingerprint,repeated_count:guard.repeated_count,
1502
+ max_repeats:guard.max_repeats,reason:guard.reason,decision_event_sha256:decisionEvent.event_sha256
1503
+ });
1504
+ await appendEvent(ctx,events,signingKeyPath,'goal_paused',{reason:'effect_repeat_limit',
1505
+ effect_id:effect.effect_id,fingerprint:effect.fingerprint});
1506
+ return {status:'paused',reason:'effect_repeat_limit'};
1507
+ }
1508
+ if(!events.some(event=>event.type==='effect_started'&&event.payload?.effect_id===effect.effect_id)){
1509
+ await appendEvent(ctx,events,signingKeyPath,'effect_started',{
1510
+ ...effect,observation_lease_id:lease.lease_id,decision_event_sha256:decisionEvent.event_sha256
1511
+ });
1512
+ }
1513
+ try{
1514
+ const outcome=await executeDecisionCore(args);
1515
+ if(!events.some(event=>['effect_completed','effect_failed'].includes(event.type)&&event.payload?.effect_id===effect.effect_id)){
1516
+ const completion=bindEffectCompletion(effect,{status:outcome?.status??'completed',
1517
+ mutation_epoch:deriveState(goal,events).mutation_epoch,observation_lease_id:lease.lease_id});
1518
+ await appendEvent(ctx,events,signingKeyPath,'effect_completed',{
1519
+ ...completion,observation_lease_id:lease.lease_id,decision_event_sha256:decisionEvent.event_sha256,
1520
+ outcome_status:outcome?.status??'completed'
1521
+ });
1522
+ }
1523
+ return outcome;
1524
+ }catch(error){
1525
+ if(!events.some(event=>['effect_completed','effect_failed'].includes(event.type)&&event.payload?.effect_id===effect.effect_id)){
1526
+ const completion=bindEffectCompletion(effect,{status:'failed',error:truncated(error,512),
1527
+ mutation_epoch:deriveState(goal,events).mutation_epoch,observation_lease_id:lease.lease_id});
1528
+ await appendEvent(ctx,events,signingKeyPath,'effect_failed',{
1529
+ ...completion,observation_lease_id:lease.lease_id,decision_event_sha256:decisionEvent.event_sha256,
1530
+ detail:truncated(error,512)
1531
+ });
1532
+ }
1533
+ throw error;
1534
+ }
1535
+ }
1536
+
1417
1537
  function terminalState(state) {
1418
1538
  return ['succeeded','cancelled'].includes(state.state);
1419
1539
  }
@@ -1442,6 +1562,9 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1442
1562
  if (!policy || !codingRuntime || !nativeDiffRuntime) throw new Error('autonomous runtime requires policy/coding/native-diff runtimes');
1443
1563
  if (!signingKeyPath || !verifyKeyPath) throw new Error('autonomous runtime requires trust keys');
1444
1564
  const capabilityManifest=configuredCapabilityManifest({machineFsRuntime,observationRuntime,processRuntime,semanticRuntime});
1565
+ const profileTimeoutCeiling=Math.max(120000,...Object.values(policy.profiles??{}).map(profile=>
1566
+ Number.isInteger(profile?.timeout_ms)&&profile.timeout_ms>0?profile.timeout_ms+120000:120000));
1567
+ const workerScheduler=createWorkerScheduler({maxConcurrent:2,totalCpuUnits:2,totalMemoryMb:2048,leaseMs:profileTimeoutCeiling});
1445
1568
  async function sessionStatus() {
1446
1569
  const autonomous = await autonomousSessionStatus(policy);
1447
1570
  const coding = await codingRuntime.sessionStatus();
@@ -1500,8 +1623,9 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1500
1623
  const journal = await readEvents(ctx,verifyKeyPath);
1501
1624
  const derived = deriveState(goal,journal);
1502
1625
  const loop = publicLoopState(loopStateOrNull(goal,journal));
1626
+ const lock=await runLockStatus(ctx);
1503
1627
  return { status:'ok', goal_id:goalId, goal_sha256:goal.goal_sha256,
1504
- title:goal.title, root_id:goal.root_id, journal_verified:true, ...derived,
1628
+ title:goal.title, root_id:goal.root_id, journal_verified:true, ...derived, ...lock,
1505
1629
  ...(loop ? {loop} : {}) };
1506
1630
  }
1507
1631
 
@@ -1577,7 +1701,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1577
1701
  }
1578
1702
  state = deriveState(goal,journal);
1579
1703
  const profiles = await runRequiredReleaseProfiles({ ctx,events:journal,goal,state,
1580
- policy,codingRuntime,signingKeyPath });
1704
+ policy,codingRuntime,signingKeyPath,workerScheduler });
1581
1705
  if (profiles.some(item => item.status === 'paused')) {
1582
1706
  return { status:'paused', goal_id:goal.goal_id, ready:false,
1583
1707
  reason:'release_profile_interrupted', mutation_epoch:state.mutation_epoch };
@@ -1658,7 +1782,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1658
1782
  try {
1659
1783
  outcome = await executeDecision({ ctx,events:journal,goal,state,
1660
1784
  decisionEvent:state.pending_decision,policy,codingRuntime,nativeDiffRuntime,machineFsRuntime,
1661
- observationRuntime,processRuntime,semanticRuntime,assertRunAuthority,signingKeyPath,verifyKeyPath });
1785
+ observationRuntime,processRuntime,semanticRuntime,assertRunAuthority,signingKeyPath,verifyKeyPath,workerScheduler });
1662
1786
  } catch (error) {
1663
1787
  const loop=loopStateOrNull(goal,journal);
1664
1788
  if (!loop || loop.pending_action || !isAuthorityUnavailable(error)) throw error;
@@ -0,0 +1,27 @@
1
+ const HEX64=/^[0-9a-f]{64}$/;
2
+
3
+ export function classifyAutonomousTimeoutRecovery(status,{beforeJournalHead}={}){
4
+ if(!status||typeof status!=='object'||Array.isArray(status)) throw new Error('autonomous timeout status invalid');
5
+ if(!HEX64.test(beforeJournalHead??'')) throw new Error('autonomous timeout before journal head invalid');
6
+ if(status.status!=='ok') throw new Error('autonomous timeout status not ok');
7
+ if(!HEX64.test(status.journal_head_sha256??'')) throw new Error('autonomous timeout journal head invalid');
8
+ if(status.state==='succeeded') return Object.freeze({kind:'completed',state:'succeeded',journal_head_sha256:status.journal_head_sha256});
9
+ if(status.state==='cancelled'||status.state==='paused'){
10
+ return Object.freeze({kind:'terminal_failure',state:status.state,reason:status.reason??null,journal_head_sha256:status.journal_head_sha256});
11
+ }
12
+ if(status.state!=='pending'&&status.state!=='running') throw new Error(`autonomous timeout unexpected state: ${status.state}`);
13
+ if(status.run_busy===true){
14
+ return Object.freeze({kind:'wait',state:status.state,reason:'server_run_in_flight',journal_head_sha256:status.journal_head_sha256});
15
+ }
16
+ if(status.run_busy!==false) throw new Error('autonomous timeout run-lock state unavailable');
17
+ if(status.pending_request!==null&&status.pending_request!==undefined){
18
+ return Object.freeze({kind:'wait',state:status.state,reason:'planner_request_in_flight',journal_head_sha256:status.journal_head_sha256});
19
+ }
20
+ if(status.pending_decision!==null&&status.pending_decision!==undefined){
21
+ return Object.freeze({kind:'wait',state:status.state,reason:'decision_in_flight',journal_head_sha256:status.journal_head_sha256});
22
+ }
23
+ if(status.journal_head_sha256===beforeJournalHead){
24
+ return Object.freeze({kind:'wait',state:status.state,reason:'no_durable_progress',journal_head_sha256:status.journal_head_sha256});
25
+ }
26
+ return Object.freeze({kind:'resumable',state:status.state,reason:'durable_cycle_completed',journal_head_sha256:status.journal_head_sha256});
27
+ }