deadbyte-mcp 0.13.0 → 0.15.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 (153) hide show
  1. package/.gitattributes +7 -0
  2. package/AGENTS.md +18 -0
  3. package/CONTEXT.md +3 -3
  4. package/MANIFEST.SHA256 +152 -75
  5. package/R42-PARENT.json +19 -0
  6. package/R43-PARENT.json +20 -0
  7. package/R44-DONORS.json +99 -0
  8. package/R44-PARENT.json +18 -0
  9. package/README.txt +7 -2
  10. package/bin/appcontainer-stage.obj +0 -0
  11. package/bin/bootstrap-advapi32.obj +0 -0
  12. package/bin/bootstrap-exitcode.obj +0 -0
  13. package/bin/bootstrap-kernel32.obj +0 -0
  14. package/bin/child-control-probe.obj +0 -0
  15. package/bin/child-control-stage.obj +0 -0
  16. package/bin/contained-reverse-worker.obj +0 -0
  17. package/bin/contained-transform-worker.obj +0 -0
  18. package/bin/containment-probe.obj +0 -0
  19. package/bin/deadbyte-contain.obj +0 -0
  20. package/bin/deadbyte-exec.obj +0 -0
  21. package/bin/deadbyte-process-host.obj +0 -0
  22. package/bin/deadbyte-tunnel-host.obj +0 -0
  23. package/controller/README.TXT +4 -5
  24. package/controller/deadbyte-controller.ps1 +1 -1
  25. package/controller/deadbyte-desktop-policy.json +5 -5
  26. package/controller/deadbyte-process-policy.json +4 -4
  27. package/docs/ARCHITECTURE.md +8 -1
  28. package/docs/R44-ARCHITECTURE.md +37 -0
  29. package/harness/r42-agent-evals.json +48 -0
  30. package/harness/task-contract.schema.json +33 -0
  31. package/package.json +15 -8
  32. package/proof/CONTAINMENT-BUILD.txt +6 -6
  33. package/scripts/agent-eval-r42.mjs +46 -0
  34. package/scripts/deferred-slot-operation-r42.mjs +126 -0
  35. package/scripts/deferred-slot-operation-r43.mjs +126 -0
  36. package/scripts/deferred-slot-operation-r44.mjs +126 -0
  37. package/scripts/final-closure-r42.mjs +116 -0
  38. package/scripts/final-closure-r43.mjs +115 -0
  39. package/scripts/final-closure-r44.mjs +121 -0
  40. package/scripts/final-closure-verify-r42.mjs +223 -0
  41. package/scripts/final-closure-verify-r43.mjs +213 -0
  42. package/scripts/final-closure-verify-r44.mjs +220 -0
  43. package/scripts/finalize-r43.mjs +217 -0
  44. package/scripts/finalize-r44.mjs +217 -0
  45. package/scripts/full-regression-r43.mjs +20 -0
  46. package/scripts/full-regression-r44.mjs +20 -0
  47. package/scripts/gate-windows-r42.ps1 +54 -0
  48. package/scripts/gate-windows-r43.ps1 +36 -0
  49. package/scripts/gate-windows-r44.ps1 +36 -0
  50. package/scripts/release-parity-r42.mjs +235 -0
  51. package/scripts/release-parity-r43.mjs +218 -0
  52. package/scripts/release-parity-r44.mjs +242 -0
  53. package/scripts/release-parity-tests-r42.ps1 +6 -0
  54. package/scripts/release-parity-tests-r43.ps1 +8 -0
  55. package/scripts/release-parity-tests-r44.ps1 +8 -0
  56. package/scripts/verify-r42-parent.mjs +101 -0
  57. package/scripts/verify-r43-parent.mjs +126 -0
  58. package/scripts/verify-r44-parent.mjs +142 -0
  59. package/scripts/windows-gate-evidence-r42.mjs +64 -0
  60. package/scripts/windows-gate-evidence-r43.mjs +55 -0
  61. package/scripts/windows-gate-evidence-r44.mjs +55 -0
  62. package/src/agent-backend-r44.mjs +254 -0
  63. package/src/agent-eval-r42.mjs +73 -0
  64. package/src/agent-eval-runner-r42.mjs +138 -0
  65. package/src/agent-governance-r42.mjs +142 -0
  66. package/src/autonomous-context-engine.mjs +2 -0
  67. package/src/autonomous-loop-state.mjs +10 -0
  68. package/src/autonomous-mcp-tools.mjs +21 -6
  69. package/src/autonomous-output-contracts.mjs +18 -2
  70. package/src/autonomous-planner-contracts.mjs +3 -0
  71. package/src/autonomous-planner.mjs +46 -6
  72. package/src/autonomous-policy.mjs +50 -5
  73. package/src/autonomous-runtime.mjs +138 -25
  74. package/src/autonomous-supervisor-host.mjs +12 -2
  75. package/src/autonomous-supervisor.mjs +1 -1
  76. package/src/capability-ledger.mjs +2 -1
  77. package/src/codex-app-server-r44.mjs +154 -0
  78. package/src/coding-plane.mjs +66 -19
  79. package/src/durable-job-r44.mjs +145 -0
  80. package/src/evidence-publisher-r42.mjs +76 -0
  81. package/src/external-job-provider-r44.mjs +126 -0
  82. package/src/host-plane.mjs +27 -6
  83. package/src/job-supervisor-host-r44.mjs +112 -0
  84. package/src/machine-fs-smoke-client.mjs +1 -1
  85. package/src/machine-fs.mjs +21 -10
  86. package/src/mcp-host-smoke-client.mjs +1 -1
  87. package/src/mcp-server.mjs +20 -3
  88. package/src/mcp-smoke-client.mjs +1 -1
  89. package/src/native-diff-v2.mjs +3 -3
  90. package/src/native-diff.mjs +3 -2
  91. package/src/observation-smoke-client.mjs +1 -1
  92. package/src/operator-surface.mjs +2 -2
  93. package/src/process-policy.mjs +19 -0
  94. package/src/process-runtime.mjs +5 -2
  95. package/src/process-smoke-client.mjs +2 -2
  96. package/src/provider-circuit.mjs +10 -4
  97. package/src/r44-compact-disarmed-probe.mjs +74 -0
  98. package/src/r44-full-surface-probe.mjs +34 -0
  99. package/src/remote-device-r44.mjs +205 -0
  100. package/src/remote-mcp-autonomous-smoke-client.mjs +4 -4
  101. package/src/remote-mcp-r44-parity-client.mjs +203 -0
  102. package/src/remote-mcp-smoke-client.mjs +7 -4
  103. package/src/runtime-update.mjs +64 -31
  104. package/src/skill-mcp-tools-r44.mjs +37 -0
  105. package/src/skill-plane-r44.mjs +203 -0
  106. package/src/tunnel-stdio-compat-smoke.mjs +1 -1
  107. package/src/version.mjs +1 -1
  108. package/test/agent-backend-r44.test.mjs +115 -0
  109. package/test/agent-eval-r42.test.mjs +39 -0
  110. package/test/agent-eval-runner-r42.test.mjs +61 -0
  111. package/test/agent-governance-r42.test.mjs +77 -0
  112. package/test/agent-runtime.test.mjs +3 -3
  113. package/test/autonomous-loop.test.mjs +97 -11
  114. package/test/autonomous-r34-e2e.test.mjs +2 -1
  115. package/test/autonomous-runtime.test.mjs +2 -2
  116. package/test/capability-ledger-r36.test.mjs +3 -3
  117. package/test/cli-entrypoint.test.mjs +1 -1
  118. package/test/codex-app-server-r44.test.mjs +115 -0
  119. package/test/codex-autonomous-r44.test.mjs +87 -0
  120. package/test/coding-plane.test.mjs +2 -2
  121. package/test/coding-runtime.test.mjs +36 -1
  122. package/test/compact-catalog-r36.test.mjs +3 -3
  123. package/test/compact-max-catalog-r40.test.mjs +4 -2
  124. package/test/compact-max-catalog-r44.test.mjs +97 -0
  125. package/test/core.test.mjs +1 -1
  126. package/test/durable-job-r44.test.mjs +75 -0
  127. package/test/evidence-publisher-r42.test.mjs +53 -0
  128. package/test/external-job-provider-r44.test.mjs +46 -0
  129. package/test/full-max-catalog-r40.test.mjs +5 -3
  130. package/test/full-max-catalog-r44.test.mjs +91 -0
  131. package/test/host-plane.test.mjs +15 -1
  132. package/test/human-clarification-r42.test.mjs +53 -0
  133. package/test/native-diff.test.mjs +28 -0
  134. package/test/output-schema.test.mjs +2 -2
  135. package/test/process-policy.test.mjs +22 -1
  136. package/test/prompts-resources.test.mjs +1 -1
  137. package/test/provider-responses-r42.test.mjs +90 -0
  138. package/test/r33-closeout-regression.test.mjs +14 -16
  139. package/test/r33-finalization.test.mjs +7 -7
  140. package/test/r42-finalization.test.mjs +38 -0
  141. package/test/r43-finalization.test.mjs +31 -0
  142. package/test/r43-finalizer.test.mjs +34 -0
  143. package/test/r43-parent.test.mjs +29 -0
  144. package/test/r44-finalization.test.mjs +58 -0
  145. package/test/r44-finalizer.test.mjs +48 -0
  146. package/test/r44-parent.test.mjs +25 -0
  147. package/test/release-version.test.mjs +53 -19
  148. package/test/remote-device-r44.test.mjs +104 -0
  149. package/test/runtime-self-update.test.mjs +42 -0
  150. package/test/skill-mcp-r44.test.mjs +38 -0
  151. package/test/skill-plane-r44.test.mjs +93 -0
  152. package/test/standing-grants.test.mjs +5 -1
  153. package/test/task-contract-r42.test.mjs +41 -0
@@ -3,13 +3,14 @@ import { access, mkdir, open, readFile, readdir, rm, writeFile } from 'node:fs/p
3
3
  import path from 'node:path';
4
4
  import { canonicalJson } from './canonical-json.mjs';
5
5
  import { loadPrivateSigningKey, loadPublicVerifyKey } from './trust.mjs';
6
- import { assertAutonomousArmed, autonomousSessionStatus } from './autonomous-policy.mjs';
6
+ import { assertAutonomousArmed, autonomousSessionStatus, recheckAutonomousAuthority } from './autonomous-policy.mjs';
7
7
  import { buildPlannerPrompt, parsePlannerDecision, parseR34PlannerDecision, requestProviderDecision } from './autonomous-planner.mjs';
8
8
  import { actionIdentity, reconcileStartedAction } from './autonomous-action-reconcile.mjs';
9
9
  import { createPlan, replanPlan, completePlanStep, planIsComplete as r34PlanIsComplete } from './autonomous-plan.mjs';
10
10
  import { deriveLoopState, repairFingerprint } from './autonomous-loop-state.mjs';
11
11
  import { judgeProgress, stallDisposition } from './autonomous-progress.mjs';
12
12
  import { classifyProviderFailure, deriveCircuit, providerCallDisposition, providerCircuitId } from './provider-circuit.mjs';
13
+ import { bindApprovalSubject, deriveEffectiveRigor, taskDagFromPlan, validateBoundApproval } from './agent-governance-r42.mjs';
13
14
  import { verifyObservationReceiptFile } from './observation-runtime.mjs';
14
15
  import { R36_CAPABILITY_LEDGER } from './capability-ledger.mjs';
15
16
  import { deriveAgentMemory } from './agent-memory-plane.mjs';
@@ -129,6 +130,11 @@ function deriveState(goal, events) {
129
130
  const binding = { ...p, event_sha256:event.event_sha256 };
130
131
  observations.push(binding);
131
132
  }
133
+ if (event.type === 'human_input_provided') {
134
+ observations.push({kind:'human_input',question:p.question,answer:p.answer,
135
+ answer_sha256:p.answer_sha256,trust_class:'operator_input',observed_at_utc:event.created_at_utc,
136
+ event_sha256:event.event_sha256});
137
+ }
132
138
  if (event.type === 'native_diff_committed') {
133
139
  mutationEpoch = Number(p.mutation_epoch ?? mutationEpoch + 1); mutationCount += Number(p.mutation_count ?? 1);
134
140
  diffs.push({ ...p, event_sha256:event.event_sha256 });
@@ -268,16 +274,24 @@ async function executeProfile({ ctx, events, goalId, actionKey, profileId, epoch
268
274
  await appendEvent(ctx,events,signingKeyPath,'profile_completed',payload);
269
275
  return { status:outcome.passed ? 'passed' : 'failed', observation:payload, existing:false };
270
276
  }
277
+ const autonomousAuthority = await assertAutonomousArmed(policy,profile.root_id);
278
+ if (Number.isFinite(autonomousAuthority.remaining_ms) && autonomousAuthority.remaining_ms < 100) {
279
+ throw new Error('autonomous lease expires too soon to execute profile');
280
+ }
271
281
  await appendEvent(ctx,events,signingKeyPath,'profile_started',{ action_key:actionKey, profile_id:profileId,
272
- mutation_epoch:epoch, receipt_id:receiptId, request_sha256:requestSha256 });
282
+ mutation_epoch:epoch, receipt_id:receiptId, request_sha256:requestSha256,
283
+ authority_instance_nonce:autonomousAuthority.instance_nonce,
284
+ authority_expires_at_utc:autonomousAuthority.expires_at_utc });
285
+ const preExecutionCheck = () => recheckAutonomousAuthority(policy,autonomousAuthority,profile.root_id);
273
286
  let result;
274
287
  try {
275
288
  result = profile.kind === 'command'
276
289
  ? await codingRuntime.commandRun({ rootId:profile.root_id, commandId:profile.command_id, scriptPath:profile.script_path,
277
290
  scriptSha256:profile.script_sha256, args:profile.args, cwd:profile.cwd, timeoutMs:profile.timeout_ms,
278
- receiptId, requestSha256 })
291
+ executionDeadlineUtc:autonomousAuthority.expires_at_utc, preExecutionCheck, receiptId, requestSha256 })
279
292
  : await codingRuntime.benchmarkRun({ rootId:profile.root_id, commandId:profile.command_id, scriptPath:profile.script_path,
280
293
  scriptSha256:profile.script_sha256, args:profile.args, cwd:profile.cwd, timeoutMs:profile.timeout_ms,
294
+ executionDeadlineUtc:autonomousAuthority.expires_at_utc, preExecutionCheck,
281
295
  warmups:profile.warmups, iterations:profile.iterations, baselineMedianMs:profile.baseline_median_ms,
282
296
  maxRegressionPct:profile.max_regression_pct, receiptId, requestSha256 });
283
297
  } catch (error) {
@@ -325,7 +339,31 @@ function releaseReadiness(policy, state) {
325
339
  });
326
340
  return { ready:results.every(item => item.passed), mutation_epoch:state.mutation_epoch, required_profiles:results };
327
341
  }
328
- function normalizeGoal(policy, { submissionId, rootId, title = '', goal, acceptance = '', maxIterations = null }) {
342
+ function normalizeTaskContract(raw) {
343
+ const value=raw ?? {schema:'deadbyte.task-contract.v1',scope:[],non_goals:[],constraints:[],
344
+ evidence_requirements:[],risk_level:'medium',clarification_policy:'ask_when_blocked',
345
+ project_default:'structured',explicit_override:null};
346
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('autonomous task contract invalid');
347
+ const allowed=['schema','scope','non_goals','constraints','evidence_requirements','risk_level','clarification_policy','project_default','explicit_override'];
348
+ const unknown=Object.keys(value).filter(key=>!allowed.includes(key));
349
+ if (unknown.length) throw new Error(`autonomous task contract unknown field '${unknown[0]}'`);
350
+ if (value.schema !== 'deadbyte.task-contract.v1') throw new Error('autonomous task contract schema invalid');
351
+ const list=(field,maxItems,maxLength)=>{
352
+ const items=value[field] ?? [];
353
+ if (!Array.isArray(items) || items.length > maxItems || items.some(item=>typeof item !== 'string' || item.length < 1 || item.length > maxLength || item.includes('\0'))) {
354
+ throw new Error(`autonomous task contract ${field} invalid`);
355
+ }
356
+ return [...items];
357
+ };
358
+ if (!['low','medium','high'].includes(value.risk_level)) throw new Error('autonomous task contract risk_level invalid');
359
+ if (!['ask_when_blocked','fail_closed'].includes(value.clarification_policy)) throw new Error('autonomous task contract clarification_policy invalid');
360
+ const rigor=deriveEffectiveRigor({project_default:value.project_default??'structured',risk_level:value.risk_level,
361
+ explicit_override:value.explicit_override??null});
362
+ return Object.freeze({schema:value.schema,scope:list('scope',32,512),non_goals:list('non_goals',24,512),
363
+ constraints:list('constraints',32,512),evidence_requirements:list('evidence_requirements',32,512),
364
+ risk_level:value.risk_level,clarification_policy:value.clarification_policy,...rigor});
365
+ }
366
+ function normalizeGoal(policy, { submissionId, rootId, title = '', goal, acceptance = '', taskContract = null, maxIterations = null }) {
329
367
  if (!ID_RE.test(submissionId ?? '')) throw new Error('autonomous submission_id invalid');
330
368
  if (!policy.root_ids.includes(rootId)) throw new Error(`autonomous root '${rootId}' is not allowed`);
331
369
  if (typeof title !== 'string' || title.length > 160) throw new Error('autonomous title must be <=160 characters');
@@ -335,7 +373,7 @@ function normalizeGoal(policy, { submissionId, rootId, title = '', goal, accepta
335
373
  if (!Number.isInteger(iterations) || iterations < 1 || iterations > policy.limits.max_iterations) throw new Error('autonomous max_iterations exceeds policy');
336
374
  const body = { schema:AUTONOMOUS_GOAL_SCHEMA, policy_sha256:policy.policy_sha256,
337
375
  coding_policy_sha256:policy.coding_policy_sha256, submission_id:submissionId, root_id:rootId,
338
- title, goal, acceptance, max_iterations:iterations };
376
+ title, goal, acceptance, task_contract:normalizeTaskContract(taskContract),max_iterations:iterations };
339
377
  return Object.freeze({ ...body, goal_sha256:sha256Object(body) });
340
378
  }
341
379
 
@@ -389,7 +427,8 @@ async function requestPlanner(ctx, goal, events, state, policy, codingRuntime, c
389
427
  return null;
390
428
  }
391
429
  const view = await plannerView(goal,state,events,codingRuntime,capabilityManifest,desktopMemoryProvider,memoryProvider);
392
- let prompt = buildPlannerPrompt({ goal:{ title:goal.title, goal:goal.goal, acceptance:goal.acceptance }, policy, view });
430
+ let prompt = buildPlannerPrompt({ goal:{ goal_id:goal.goal_id,title:goal.title, goal:goal.goal, acceptance:goal.acceptance,
431
+ task_contract:goal.task_contract }, policy, view });
393
432
  const repair = [...(state.planner_rejections ?? [])].reverse().find(item => item.planner_round === round) ?? null;
394
433
  if (repair) {
395
434
  const repairState = canonicalJson({ request_id:repair.request_id, response_sha256:repair.response_sha256,
@@ -452,7 +491,24 @@ function legacyPublicLoopPhase(phase) {
452
491
  return ({PLAN:'PLANNING',INSPECT:'INSPECTING',ACT:'ACTING',VERIFY:'VERIFYING',CRITIC:'REPLANNING',
453
492
  REPAIR:'REPLANNING',RELEASE:'VERIFYING',WAITING:'WAITING',SUCCEEDED:'COMPLETE',FAILED:'WAITING'})[phase] ?? 'WAITING';
454
493
  }
455
- function publicLoopState(loop) {
494
+ function approvalScopeFor(reason){
495
+ if(reason==='human_input_required') return 'human_clarification';
496
+ if(String(reason??'').startsWith('desktop_')) return 'desktop_resume';
497
+ if(['ambiguous_interrupted_action','profile_execution_without_receipt'].includes(reason)) return 'interrupted_effect_resume';
498
+ return 'resume_goal';
499
+ }
500
+ function pendingApprovalSubject(goal,events,loop){
501
+ if(!loop?.pending_approval) return null;
502
+ const waiting=[...events].reverse().find(event=>event.type==='waiting_entered')??null;
503
+ if(!waiting) return null;
504
+ const payload=waiting.payload??{};
505
+ return bindApprovalSubject({
506
+ goal_id:goal.goal_id,reviewed_event_seq:waiting.seq,reviewed_event_sha256:waiting.event_sha256,
507
+ journal_head_sha256:events.at(-1)?.event_sha256??ZERO_HASH,scope:approvalScopeFor(payload.reason),
508
+ subject:{reason:payload.reason??loop.wait_reason??'paused',waiting_payload_sha256:sha256Object(payload)}
509
+ });
510
+ }
511
+ function publicLoopState(loop,goal=null,events=null) {
456
512
  if (!loop) return null;
457
513
  const currentStep=loop.current_step ? {
458
514
  step_id:loop.current_step.step_id,intent:loop.current_step.intent,status:loop.current_step.status
@@ -468,15 +524,18 @@ function publicLoopState(loop) {
468
524
  return {
469
525
  schema:'deadbyte.autonomous-loop-state.v1',phase:legacyPublicLoopPhase(loop.phase),plan_id:loop.plan_id,plan_version:loop.plan_version,
470
526
  current_step:currentStep,replan_count:loop.replan_count,progress,
471
- pending_action:pendingAction,pending_approval:loop.pending_approval,wait_reason:loop.wait_reason
527
+ pending_action:pendingAction,pending_approval:loop.pending_approval,wait_reason:loop.wait_reason,
528
+ clarification:loop.clarification ?? null,
529
+ approval_subject:goal&&events?pendingApprovalSubject(goal,events,loop):null,
530
+ task_dag:goal&&loop.plan?taskDagFromPlan({run_id:goal.goal_id,goal_id:goal.goal_id,plan:loop.plan}):null
472
531
  };
473
532
  }
474
533
  function withLoopResult(goal,events,result) {
475
- const loop=publicLoopState(loopStateOrNull(goal,events));
534
+ const loop=publicLoopState(loopStateOrNull(goal,events),goal,events);
476
535
  return loop ? {...result,loop} : result;
477
536
  }
478
537
  function providerCircuitSnapshot(policy,events,observedAtUtc=new Date().toISOString()) {
479
- if (policy.planner.backend !== 'openai_compatible') return null;
538
+ if (!['openai_compatible','openai_responses','codex_app_server'].includes(policy.planner.backend)) return null;
480
539
  return deriveCircuit(events,providerCircuitId(policy.planner),policy.planner.circuit,observedAtUtc);
481
540
  }
482
541
  async function enterProviderCircuitWait({ctx,events,goal,policy,signingKeyPath,reason,circuit}) {
@@ -494,7 +553,7 @@ async function enterProviderCircuitWait({ctx,events,goal,policy,signingKeyPath,r
494
553
  });
495
554
  return {status:'paused',reason,circuit};
496
555
  }
497
- async function requestProviderWithCircuit({ctx,events,goal,policy,pending,signingKeyPath}) {
556
+ async function requestProviderWithCircuit({ctx,events,goal,policy,pending,signingKeyPath,plannerProviderRuntime}) {
498
557
  const providerId=providerCircuitId(policy.planner);
499
558
  let circuit=providerCircuitSnapshot(policy,events);
500
559
  let disposition=providerCallDisposition(circuit);
@@ -515,9 +574,13 @@ async function requestProviderWithCircuit({ctx,events,goal,policy,pending,signin
515
574
  });
516
575
  }
517
576
  try {
518
- const response=await requestProviderDecision(policy,pending.prompt);
577
+ const response=policy.planner.backend==='codex_app_server'
578
+ ? await plannerProviderRuntime.runPlanner({logicalId:`goal-${goal.goal_id}`,prompt:pending.prompt,timeoutMs:policy.planner.timeout_ms})
579
+ : await requestProviderDecision(policy,pending.prompt);
519
580
  await appendEvent(ctx,events,signingKeyPath,'provider_call_succeeded',{
520
- provider_id:providerId,request_id:pending.request_id,probe:disposition.probe===true
581
+ provider_id:providerId,request_id:pending.request_id,probe:disposition.probe===true,
582
+ ...(response.response_id?{response_id:response.response_id}:{}),
583
+ ...(response.usage?{usage:response.usage}:{})
521
584
  });
522
585
  return {response};
523
586
  } catch (error) {
@@ -536,15 +599,15 @@ async function requestProviderWithCircuit({ctx,events,goal,policy,pending,signin
536
599
  throw error;
537
600
  }
538
601
  }
539
- async function acceptPlannerDecision({ ctx, events, goal, state, policy, codingRuntime, capabilityManifest, desktopMemoryProvider,memoryProvider, signingKeyPath, plannerResponse }) {
602
+ async function acceptPlannerDecision({ ctx, events, goal, state, policy, codingRuntime, capabilityManifest, desktopMemoryProvider,memoryProvider, signingKeyPath, plannerResponse, plannerProviderRuntime }) {
540
603
  let currentState = state;
541
604
  let pending = currentState.pending_request ?? await requestPlanner(ctx,goal,events,currentState,policy,codingRuntime,capabilityManifest,desktopMemoryProvider,memoryProvider,signingKeyPath);
542
605
  if (!pending) return null;
543
606
  let suppliedResponse = plannerResponse;
544
607
  while (pending) {
545
608
  let response;
546
- if (policy.planner.backend === 'openai_compatible') {
547
- const provider=await requestProviderWithCircuit({ctx,events,goal,policy,pending,signingKeyPath});
609
+ if (['openai_compatible','openai_responses','codex_app_server'].includes(policy.planner.backend)) {
610
+ const provider=await requestProviderWithCircuit({ctx,events,goal,policy,pending,signingKeyPath,plannerProviderRuntime});
548
611
  if (provider.waiting) return {needs_planner:false,rejected:false,paused:true,provider_waiting:true};
549
612
  response=provider.response;
550
613
  } else {
@@ -585,6 +648,8 @@ async function acceptPlannerDecision({ ctx, events, goal, state, policy, codingR
585
648
  const event = await appendEvent(ctx,events,signingKeyPath,'planner_decision',{
586
649
  request_id:pending.request_id, planner_round:pending.planner_round, mutation_epoch:pending.mutation_epoch,
587
650
  backend:response.backend, model:response.model ?? 'unknown', response_sha256:sha256Bytes(Buffer.from(response.raw_text,'utf8')),
651
+ ...(response.response_id?{response_id:response.response_id}:{}),
652
+ ...(response.usage?{usage:response.usage}:{}),
588
653
  ...(pending.working_memory_sha256?{
589
654
  working_memory_sha256:pending.working_memory_sha256,memory_snapshot_sha256:pending.memory_snapshot_sha256,
590
655
  memory_index_sha256:pending.memory_index_sha256,
@@ -1506,6 +1571,20 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
1506
1571
  return r34EnterWaiting({ctx,events,goal,signingKeyPath,reason:decision.reason,resumePhase:loop.phase});
1507
1572
  }
1508
1573
 
1574
+ if (decision.kind === 'clarify') {
1575
+ await appendEvent(ctx,events,signingKeyPath,'human_input_requested',{
1576
+ decision_event_sha256:decisionEvent.event_sha256,question:decision.question,choices:decision.choices
1577
+ });
1578
+ await appendEvent(ctx,events,signingKeyPath,'waiting_entered',{
1579
+ reason:'human_input_required',requires_approval:true,resume_phase:'PLAN',
1580
+ question:decision.question,choices:decision.choices
1581
+ });
1582
+ await appendEvent(ctx,events,signingKeyPath,'goal_paused',{
1583
+ reason:'human_input_required',decision_event_sha256:decisionEvent.event_sha256
1584
+ });
1585
+ return {status:'paused',reason:'human_input_required'};
1586
+ }
1587
+
1509
1588
  if (decision.kind === 'finish') {
1510
1589
  if (!r34PlanIsComplete(loop.plan)) {
1511
1590
  await appendEvent(ctx,events,signingKeyPath,'plan_guard_blocked',{
@@ -1676,7 +1755,7 @@ function terminalState(state) {
1676
1755
  }
1677
1756
  function pauseState(state) { return state.state === 'paused'; }
1678
1757
  async function resumeProviderWaitIfReady({ctx,journal,goal,state,policy,signingKeyPath}) {
1679
- if (state.state!=='paused'||state.reason!=='provider_circuit_open'||policy.planner.backend!=='openai_compatible') return state;
1758
+ if (state.state!=='paused'||state.reason!=='provider_circuit_open'||!['openai_compatible','openai_responses'].includes(policy.planner.backend)) return state;
1680
1759
  const circuit=providerCircuitSnapshot(policy,journal);
1681
1760
  const disposition=providerCallDisposition(circuit);
1682
1761
  if (!disposition.allowed||circuit.state!=='HALF_OPEN') return state;
@@ -1696,9 +1775,12 @@ function isAuthorityUnavailable(error) {
1696
1775
 
1697
1776
  export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRuntime, machineFsRuntime = null,
1698
1777
  observationRuntime = null, processRuntime = null, semanticRuntime = null, desktopRuntime = null,
1699
- desktopMemoryProvider = null,memoryProvider=null, signingKeyPath, verifyKeyPath }) {
1778
+ desktopMemoryProvider = null,memoryProvider=null, plannerProviderRuntime=null, signingKeyPath, verifyKeyPath }) {
1700
1779
  if (!policy || !codingRuntime || !nativeDiffRuntime) throw new Error('autonomous runtime requires policy/coding/native-diff runtimes');
1701
1780
  if (!signingKeyPath || !verifyKeyPath) throw new Error('autonomous runtime requires trust keys');
1781
+ if (policy.planner?.backend==='codex_app_server' && (!plannerProviderRuntime || typeof plannerProviderRuntime.runPlanner!=='function')) {
1782
+ throw new Error('Codex planner provider runtime is required by autonomous policy');
1783
+ }
1702
1784
  const capabilityManifest=configuredCapabilityManifest({machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime});
1703
1785
  const profileTimeoutCeiling=Math.max(120000,...Object.values(policy.profiles??{}).map(profile=>
1704
1786
  Number.isInteger(profile?.timeout_ms)&&profile.timeout_ms>0?profile.timeout_ms+120000:120000));
@@ -1715,7 +1797,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1715
1797
  coding_policy_sha256:policy.coding_policy_sha256 };
1716
1798
  }
1717
1799
  async function providerCircuitStatus({goalId}) {
1718
- if (policy.planner.backend!=='openai_compatible') return null;
1800
+ if (!['openai_compatible','openai_responses','codex_app_server'].includes(policy.planner.backend)) return null;
1719
1801
  const ctx=goalContext(policy,goalId);
1720
1802
  await loadGoal(ctx,policy,verifyKeyPath);
1721
1803
  const journal=await readEvents(ctx,verifyKeyPath);
@@ -1730,9 +1812,9 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1730
1812
  return { autonomous, coding };
1731
1813
  }
1732
1814
 
1733
- async function submit({ submissionId, rootId, title = '', goal, acceptance = '', maxIterations = null }) {
1815
+ async function submit({ submissionId, rootId, title = '', goal, acceptance = '', taskContract = null, maxIterations = null }) {
1734
1816
  await assertRunAuthority(rootId);
1735
- const normalized = normalizeGoal(policy,{ submissionId,rootId,title,goal,acceptance,maxIterations });
1817
+ const normalized = normalizeGoal(policy,{ submissionId,rootId,title,goal,acceptance,taskContract,maxIterations });
1736
1818
  const goalId = goalIdFor(policy.policy_sha256,submissionId);
1737
1819
  const ctx = goalContext(policy,goalId);
1738
1820
  if (await exists(ctx.dir)) {
@@ -1749,7 +1831,8 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1749
1831
  await writeFile(ctx.goalPath,`${canonicalJson(envelope)}\n`,{flag:'wx'});
1750
1832
  const journal = [];
1751
1833
  await appendEvent(ctx,journal,signingKeyPath,'goal_created',{
1752
- submission_id:submissionId, root_id:rootId, title, goal_sha256:normalized.goal_sha256,
1834
+ submission_id:submissionId, root_id:rootId, title, acceptance:normalized.acceptance,
1835
+ task_contract:normalized.task_contract,goal_sha256:normalized.goal_sha256,
1753
1836
  max_iterations:normalized.max_iterations,
1754
1837
  ...(memoryProvider?{source_release_manifest:memoryProvider.source_release_manifest}:{})
1755
1838
  });
@@ -1761,7 +1844,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1761
1844
  const goal = await loadGoal(ctx,policy,verifyKeyPath);
1762
1845
  const journal = await readEvents(ctx,verifyKeyPath);
1763
1846
  const derived = deriveState(goal,journal);
1764
- const loop = publicLoopState(loopStateOrNull(goal,journal));
1847
+ const loop = publicLoopState(loopStateOrNull(goal,journal),goal,journal);
1765
1848
  const lock=await runLockStatus(ctx);
1766
1849
  return { status:'ok', goal_id:goalId, goal_sha256:goal.goal_sha256,
1767
1850
  title:goal.title, root_id:goal.root_id, journal_verified:true, ...derived, ...lock,
@@ -1795,8 +1878,11 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1795
1878
  return { status:'existing', goal_id:goalId, state:current.state };
1796
1879
  }
1797
1880
  }
1798
- async function approve({ goalId, decision = 'approve' }) {
1881
+ async function approve({ goalId, decision = 'approve', answer = null, approvalSubjectSha256 = null, expiresInMs = 300000 }) {
1799
1882
  if (!['approve','reject'].includes(decision)) throw new Error('autonomous approval decision invalid');
1883
+ if (answer !== null && (typeof answer !== 'string' || answer.length < 1 || answer.length > 4096 || answer.includes('\0'))) {
1884
+ throw new Error('autonomous approval answer invalid');
1885
+ }
1800
1886
  const ctx = goalContext(policy,goalId);
1801
1887
  await acquireLock(ctx);
1802
1888
  try {
@@ -1811,7 +1897,34 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1811
1897
  }
1812
1898
  if (hasR34Loop(journal)) {
1813
1899
  await assertRunAuthority(goal.root_id);
1900
+ const loopState=loopStateOrNull(goal,journal);
1901
+ const approvalSubject=pendingApprovalSubject(goal,journal,loopState);
1902
+ if(!approvalSubject) throw new Error('autonomous approval subject unavailable');
1903
+ if(typeof approvalSubjectSha256!=='string'||approvalSubjectSha256!==approvalSubject.approval_subject_sha256){
1904
+ throw new Error('autonomous approval subject mismatch');
1905
+ }
1906
+ if(!Number.isInteger(expiresInMs)||expiresInMs<1000||expiresInMs>3_600_000) throw new Error('autonomous approval lifetime invalid');
1814
1907
  const waiting=[...journal].reverse().find(event=>event.type==='waiting_entered') ?? null;
1908
+ if (waiting?.payload?.reason === 'human_input_required' && answer === null) {
1909
+ throw new Error('autonomous clarification answer is required');
1910
+ }
1911
+ if (waiting?.payload?.reason !== 'human_input_required' && answer !== null) {
1912
+ throw new Error('autonomous approval answer is only valid for human clarification');
1913
+ }
1914
+ const issuedAt=new Date();
1915
+ const approval={schema:'deadbyte.bound-approval.v1',approval_subject_sha256:approvalSubjectSha256,
1916
+ reviewed_event_seq:approvalSubject.reviewed_event_seq,reviewed_event_sha256:approvalSubject.reviewed_event_sha256,
1917
+ journal_head_sha256:approvalSubject.journal_head_sha256,scope:approvalSubject.scope,
1918
+ issued_at_utc:issuedAt.toISOString(),expires_at_utc:new Date(issuedAt.getTime()+expiresInMs).toISOString(),
1919
+ provenance:'operator_mcp'};
1920
+ validateBoundApproval({approval,subject:approvalSubject,now:issuedAt});
1921
+ await appendEvent(ctx,journal,signingKeyPath,'approval_granted',approval);
1922
+ if (waiting?.payload?.reason === 'human_input_required') {
1923
+ await appendEvent(ctx,journal,signingKeyPath,'human_input_provided',{
1924
+ request_event_sha256:waiting.event_sha256,question:waiting.payload.question,
1925
+ answer,answer_sha256:sha256Bytes(Buffer.from(answer,'utf8'))
1926
+ });
1927
+ }
1815
1928
  const resumePhase=typeof waiting?.payload?.resume_phase === 'string' ? waiting.payload.resume_phase : 'PLAN';
1816
1929
  await appendEvent(ctx,journal,signingKeyPath,'waiting_resumed',{
1817
1930
  to:resumePhase,reason:waiting?.payload?.reason ?? current.reason ?? 'operator_approved',
@@ -1946,7 +2059,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1946
2059
  continue;
1947
2060
  }
1948
2061
  const accepted = await acceptPlannerDecision({ ctx,events:journal,goal,state,policy,codingRuntime,capabilityManifest,
1949
- desktopMemoryProvider,memoryProvider,signingKeyPath,plannerResponse:suppliedResponse });
2062
+ desktopMemoryProvider,memoryProvider,signingKeyPath,plannerResponse:suppliedResponse,plannerProviderRuntime });
1950
2063
  suppliedResponse = null;
1951
2064
  if (accepted?.needs_planner) {
1952
2065
  state = deriveState(goal,journal);
@@ -7,6 +7,8 @@ import { createCodingRuntime, loadCodingPolicy } from './coding-plane.mjs';
7
7
  import { loadAutonomousPolicy } from './autonomous-policy.mjs';
8
8
  import { createNativeDiffRuntime } from './native-diff.mjs';
9
9
  import { createAutonomousRuntime } from './autonomous-runtime.mjs';
10
+ import { createCodexAppServerR44 } from './codex-app-server-r44.mjs';
11
+ import { DEADBYTE_VERSION } from './version.mjs';
10
12
  import { startLocalSupervisorHost } from './autonomous-local-supervisor.mjs';
11
13
 
12
14
  const packageRoot=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
@@ -30,7 +32,15 @@ const codingPolicy=await loadCodingPolicy(codingPolicyPath);
30
32
  const codingRuntime=createCodingRuntime({policy:codingPolicy,signingKeyPath,verifyKeyPath});
31
33
  const autonomousPolicy=await loadAutonomousPolicy(autonomousPolicyPath,codingPolicy);
32
34
  const nativeDiffRuntime=createNativeDiffRuntime({policy:autonomousPolicy,codingRuntime,signingKeyPath,verifyKeyPath});
33
- const autonomousRuntime=createAutonomousRuntime({policy:autonomousPolicy,codingRuntime,nativeDiffRuntime,signingKeyPath,verifyKeyPath});
35
+ const plannerProviderRuntime=autonomousPolicy.planner.backend==='codex_app_server'
36
+ ? await createCodexAppServerR44({
37
+ stateDir:path.join(autonomousPolicy.evidence_dir,'provider-r44','codex-app-server'),
38
+ model:autonomousPolicy.planner.model,cwd:codingPolicy.roots[autonomousPolicy.planner.root_id].real_path,
39
+ clientVersion:DEADBYTE_VERSION,executable:autonomousPolicy.planner.executable,
40
+ executableSha256:autonomousPolicy.planner.executable_sha256,args:autonomousPolicy.planner.args
41
+ })
42
+ : null;
43
+ const autonomousRuntime=createAutonomousRuntime({policy:autonomousPolicy,codingRuntime,nativeDiffRuntime,plannerProviderRuntime,signingKeyPath,verifyKeyPath});
34
44
  const markerPath=path.resolve(process.env.DEADBYTE_SUPERVISOR_MARKER||path.join(autonomousPolicy.evidence_dir,'supervisor-owner.json'));
35
45
  const host=await startLocalSupervisorHost({runtime:autonomousRuntime,policy:autonomousPolicy,markerPath,
36
46
  idleDelayMs:750,maxCycles:32,continuous:onceMode!=='1'});
@@ -43,7 +53,7 @@ let stopping=false;
43
53
  async function stopAndExit(code=0){
44
54
  if(stopping) return;
45
55
  stopping=true;
46
- try { await host.stop(); }
56
+ try { await host.stop(); await plannerProviderRuntime?.close(); }
47
57
  catch(error){ process.stderr.write(`DEADBYTE supervisor host stop failed: ${String(error)}\n`); code=1; }
48
58
  process.exitCode=code;
49
59
  }
@@ -24,7 +24,7 @@ async function listGoalIds(policy) {
24
24
  export function startAutonomousSupervisor({ runtime, policy = runtime?.policy,
25
25
  idleDelayMs = 750, maxCycles = 32, logger = console, continuous = true } = {}) {
26
26
  if (!runtime || !policy) throw new Error('autonomous supervisor requires runtime + policy');
27
- const providerBacked = policy.planner.backend === 'openai_compatible';
27
+ const providerBacked = ['openai_compatible','openai_responses'].includes(policy.planner.backend);
28
28
  if (!Number.isInteger(idleDelayMs) || idleDelayMs < 25 || idleDelayMs > 60_000) {
29
29
  throw new Error('autonomous supervisor idleDelayMs out of range');
30
30
  }
@@ -3,7 +3,7 @@ import { createHash } from 'node:crypto';
3
3
  export const CAPABILITY_LEDGER_SCHEMA='deadbyte.capability-ledger.v1';
4
4
  const AUTHORITIES=new Set(['none','process','coding','autonomous','desktop','host']);
5
5
  const PROFILE_ENV='DEADBYTE_CAPABILITY_PROFILE';
6
- const COMPACT_TOOL_LIMIT=42;
6
+ const COMPACT_TOOL_LIMIT=43;
7
7
 
8
8
  function sha256(text){return createHash('sha256').update(text,'utf8').digest('hex');}
9
9
  function canonicalJson(value){
@@ -81,6 +81,7 @@ const gateway=(id,full_tool,compact_tool,compact_op,runtime_flag,authority,{muta
81
81
 
82
82
  export const R36_CAPABILITY_ENTRIES=Object.freeze([
83
83
  direct('evidence.verify','evidence_verify',null,'none',{receipt_class:'verification'}),
84
+ direct('skill.catalog','skill_catalog',null,'none',{receipt_class:'skill'}),
84
85
  gateway('machine.list','machine_list','machine_observe','machine_list','DEADBYTE_MACHINE_RUNTIME','none',{receipt_class:'machine'}),
85
86
  gateway('machine.status','machine_status','machine_observe','machine_status','DEADBYTE_MACHINE_RUNTIME','none',{receipt_class:'machine'}),
86
87
  gateway('machine.ping','machine_ping','machine_observe','machine_ping','DEADBYTE_MACHINE_RUNTIME','none',{receipt_class:'machine'}),
@@ -0,0 +1,154 @@
1
+ import path from 'node:path';
2
+ import { createAgentBackendR44 } from './agent-backend-r44.mjs';
3
+
4
+ const ID=/^[A-Za-z0-9._-]{1,64}$/;
5
+ function insist(ok,message){if(!ok)throw new Error(message);}
6
+ function text(value,label,max=262144){insist(typeof value==='string'&&value.length>=1&&value.length<=max,`${label} invalid`);return value;}
7
+ function threadIdFromResponse(value){
8
+ const id=value?.thread?.id;insist(typeof id==='string'&&id.length>=1&&id.length<=512,'Codex thread response missing thread.id');return id;
9
+ }
10
+ function turnIdFromResponse(value){
11
+ const id=value?.turn?.id;insist(typeof id==='string'&&id.length>=1&&id.length<=512,'Codex turn response missing turn.id');return id;
12
+ }
13
+ function safeThreadParams({model,cwd}){
14
+ return {model,cwd,approvalPolicy:'never',approvalsReviewer:'user',sandbox:'read-only',ephemeral:false};
15
+ }
16
+ function approvalHandler({method}){
17
+ if(method==='item/commandExecution/requestApproval'||method==='item/fileChange/requestApproval')return {decision:'decline'};
18
+ if(method==='item/permissions/requestApproval')return {permissions:{},scope:'turn'};
19
+ if(method==='item/tool/requestUserInput')return {answers:{}};
20
+ const error=new Error(`Codex server request denied: ${method}`);error.code=-32001;throw error;
21
+ }
22
+
23
+ export async function createCodexAppServerR44({
24
+ stateDir,model,cwd,clientVersion='0.15.0',transportFactory=null,
25
+ executable=null,executableSha256=null,args=['app-server'],env=null
26
+ }={}){
27
+ insist(typeof stateDir==='string'&&path.isAbsolute(stateDir),'Codex stateDir must be absolute');
28
+ text(model,'Codex model',128);insist(typeof cwd==='string'&&path.isAbsolute(cwd),'Codex cwd must be absolute');
29
+ text(clientVersion,'Codex client version',64);
30
+ const turns=new Map();
31
+ const attachedThreads=new Set();
32
+ let initialized=false;
33
+
34
+ function turnRecord(turnId,threadId=null){
35
+ let rec=turns.get(turnId);
36
+ if(!rec){rec={turn_id:turnId,thread_id:threadId,final_answer:null,unknown_answer:null,status:null,error:null,waiters:[]};turns.set(turnId,rec);}
37
+ else if(threadId&&rec.thread_id===null)rec.thread_id=threadId;
38
+ return rec;
39
+ }
40
+ function settle(rec){
41
+ if(!rec.status||!['completed','failed','interrupted'].includes(rec.status))return;
42
+ const waiters=rec.waiters.splice(0);
43
+ if(rec.status==='completed'){
44
+ const raw=rec.final_answer??rec.unknown_answer;
45
+ for(const waiter of waiters){
46
+ clearTimeout(waiter.timer);
47
+ if(typeof raw==='string'&&raw.length>0)waiter.resolve({raw_text:raw});
48
+ else waiter.reject(new Error('Codex turn completed without agent final response'));
49
+ }
50
+ }else{
51
+ for(const waiter of waiters){clearTimeout(waiter.timer);waiter.reject(new Error(rec.error??`Codex turn ${rec.status}`));}
52
+ }
53
+ }
54
+ async function notificationHandler({method,params}){
55
+ if(method==='item/completed'){
56
+ const tid=params?.turnId,threadId=params?.threadId,item=params?.item;
57
+ if(typeof tid!=='string'||!item||typeof item!=='object')return;
58
+ const rec=turnRecord(tid,typeof threadId==='string'?threadId:null);
59
+ if(item.type==='agentMessage'||item.type==='agent_message'){
60
+ if(typeof item.text!=='string')return;
61
+ if(item.phase==='final_answer'||item.phase==='finalAnswer')rec.final_answer=item.text;
62
+ else if(item.phase===null||item.phase===undefined)rec.unknown_answer=item.text;
63
+ }
64
+ return;
65
+ }
66
+ if(method==='turn/completed'){
67
+ const turn=params?.turn;const tid=turn?.id;
68
+ if(typeof tid!=='string')return;
69
+ const rec=turnRecord(tid,typeof params?.threadId==='string'?params.threadId:null);
70
+ rec.status=String(turn.status??'completed');
71
+ if(rec.status==='failed')rec.error=turn.error?.message??'Codex turn failed';
72
+ settle(rec);
73
+ }
74
+ }
75
+
76
+ const backend=await createAgentBackendR44({
77
+ backendId:'codex-app-server',stateDir:path.join(stateDir,'backend'),transportFactory,
78
+ executable,executableSha256,args,cwd,env,wireMode:'codex-app-server',
79
+ serverRequestHandler:approvalHandler,notificationHandler
80
+ });
81
+
82
+ async function start(){
83
+ if(initialized)return {status:'ok',initialized:true};
84
+ await backend.start();
85
+ await backend.request({method:'initialize',params:{
86
+ clientInfo:{name:'deadbyte-mcp',version:clientVersion},
87
+ capabilities:{experimentalApi:false,requestAttestation:false,optOutNotificationMethods:null}
88
+ },timeoutMs:30000});
89
+ await backend.notify({method:'initialized',params:{}});
90
+ initialized=true;
91
+ return {status:'ok',initialized:true,backend:'codex_app_server'};
92
+ }
93
+ async function ensureStarted(){if(!initialized)await start();}
94
+ async function ensureThread({logicalId}={}){
95
+ await ensureStarted();insist(typeof logicalId==='string'&&ID.test(logicalId),'Codex logical thread id invalid');
96
+ const status=await backend.status();const existing=status.threads[logicalId];
97
+ if(existing){
98
+ if(!attachedThreads.has(existing)){
99
+ const response=await backend.request({method:'thread/resume',params:{threadId:existing,...safeThreadParams({model,cwd}),excludeTurns:true},timeoutMs:60000});
100
+ const resumed=threadIdFromResponse(response);insist(resumed===existing,'Codex resumed thread identity mismatch');attachedThreads.add(existing);
101
+ }
102
+ return {status:'ok',logical_id:logicalId,thread_id:existing,resumed:true};
103
+ }
104
+ const response=await backend.request({method:'thread/start',params:safeThreadParams({model,cwd}),timeoutMs:60000});
105
+ const created=threadIdFromResponse(response);
106
+ await backend.bindThread({logicalId,threadId:created});attachedThreads.add(created);
107
+ return {status:'ok',logical_id:logicalId,thread_id:created,resumed:false};
108
+ }
109
+ function waitTurn(turnId,threadId,timeoutMs){
110
+ insist(Number.isInteger(timeoutMs)&&timeoutMs>=100&&timeoutMs<=900000,'Codex turn timeout invalid');
111
+ const rec=turnRecord(turnId,threadId);
112
+ if(rec.status&&['completed','failed','interrupted'].includes(rec.status)){
113
+ return new Promise((resolve,reject)=>{
114
+ const timer=setTimeout(()=>{},1);rec.waiters.push({resolve,reject,timer});settle(rec);
115
+ });
116
+ }
117
+ return new Promise((resolve,reject)=>{
118
+ const timer=setTimeout(()=>{
119
+ const index=rec.waiters.findIndex(x=>x.timer===timer);if(index>=0)rec.waiters.splice(index,1);
120
+ reject(new Error('Codex turn completion timeout'));
121
+ },timeoutMs);
122
+ rec.waiters.push({resolve,reject,timer});
123
+ });
124
+ }
125
+ async function runPlanner({logicalId='planner',prompt,timeoutMs=120000}={}){
126
+ text(prompt,'Codex planner prompt',1024*1024);
127
+ const thread=await ensureThread({logicalId});
128
+ const started=await backend.request({method:'turn/start',params:{
129
+ threadId:thread.thread_id,input:[{type:'text',text:prompt}],approvalPolicy:'never'
130
+ },timeoutMs:Math.min(timeoutMs,60000)});
131
+ const turnId=turnIdFromResponse(started);turnRecord(turnId,thread.thread_id);
132
+ const completed=await waitTurn(turnId,thread.thread_id,timeoutMs);
133
+ return {backend:'codex_app_server',model,raw_text:completed.raw_text,response_id:turnId,thread_id:thread.thread_id};
134
+ }
135
+ async function steer({threadId,turnId,text:message,timeoutMs=30000}={}){
136
+ await ensureStarted();text(threadId,'Codex threadId',512);text(turnId,'Codex turnId',512);text(message,'Codex steer text',262144);
137
+ return backend.request({method:'turn/steer',params:{threadId,expectedTurnId:turnId,input:[{type:'text',text:message}]},timeoutMs});
138
+ }
139
+ async function interrupt({threadId,turnId,timeoutMs=30000}={}){
140
+ await ensureStarted();text(threadId,'Codex threadId',512);text(turnId,'Codex turnId',512);
141
+ return backend.request({method:'turn/interrupt',params:{threadId,turnId},timeoutMs});
142
+ }
143
+ async function modelList({includeHidden=false}={}){
144
+ await ensureStarted();return backend.request({method:'model/list',params:{includeHidden:includeHidden===true},timeoutMs:30000});
145
+ }
146
+ async function status(){const value=await backend.status();return {...value,initialized,attached_threads:[...attachedThreads].sort()};}
147
+ async function close(){
148
+ for(const rec of turns.values()){
149
+ for(const waiter of rec.waiters.splice(0)){clearTimeout(waiter.timer);waiter.reject(new Error('Codex adapter closed before turn completion'));}
150
+ }
151
+ initialized=false;attachedThreads.clear();return backend.close();
152
+ }
153
+ return Object.freeze({start,ensureThread,runPlanner,steer,interrupt,modelList,status,close,backend});
154
+ }