deadbyte-mcp 0.13.0 → 0.14.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 (67) hide show
  1. package/.gitattributes +7 -0
  2. package/AGENTS.md +25 -0
  3. package/CONTEXT.md +3 -3
  4. package/MANIFEST.SHA256 +66 -40
  5. package/R42-PARENT.json +19 -0
  6. package/README.txt +2 -2
  7. package/bin/appcontainer-stage.obj +0 -0
  8. package/bin/bootstrap-advapi32.obj +0 -0
  9. package/bin/bootstrap-exitcode.obj +0 -0
  10. package/bin/bootstrap-kernel32.obj +0 -0
  11. package/bin/child-control-probe.obj +0 -0
  12. package/bin/child-control-stage.obj +0 -0
  13. package/bin/contained-reverse-worker.obj +0 -0
  14. package/bin/contained-transform-worker.obj +0 -0
  15. package/bin/containment-probe.obj +0 -0
  16. package/bin/deadbyte-contain.obj +0 -0
  17. package/bin/deadbyte-exec.obj +0 -0
  18. package/bin/deadbyte-process-host.obj +0 -0
  19. package/bin/deadbyte-tunnel-host.obj +0 -0
  20. package/controller/README.TXT +1 -1
  21. package/controller/deadbyte-controller.ps1 +1 -1
  22. package/controller/deadbyte-desktop-policy.json +5 -5
  23. package/controller/deadbyte-process-policy.json +4 -4
  24. package/docs/ARCHITECTURE.md +1 -1
  25. package/harness/r42-agent-evals.json +48 -0
  26. package/harness/task-contract.schema.json +33 -0
  27. package/package.json +9 -7
  28. package/proof/CONTAINMENT-BUILD.txt +6 -6
  29. package/scripts/agent-eval-r42.mjs +46 -0
  30. package/scripts/deferred-slot-operation-r42.mjs +126 -0
  31. package/scripts/final-closure-r42.mjs +116 -0
  32. package/scripts/final-closure-verify-r42.mjs +223 -0
  33. package/scripts/gate-windows-r42.ps1 +54 -0
  34. package/scripts/release-parity-r42.mjs +235 -0
  35. package/scripts/release-parity-tests-r42.ps1 +6 -0
  36. package/scripts/verify-r42-parent.mjs +101 -0
  37. package/scripts/windows-gate-evidence-r42.mjs +64 -0
  38. package/src/agent-eval-r42.mjs +73 -0
  39. package/src/agent-eval-runner-r42.mjs +138 -0
  40. package/src/agent-governance-r42.mjs +142 -0
  41. package/src/autonomous-context-engine.mjs +2 -0
  42. package/src/autonomous-loop-state.mjs +10 -0
  43. package/src/autonomous-mcp-tools.mjs +21 -6
  44. package/src/autonomous-output-contracts.mjs +18 -2
  45. package/src/autonomous-planner-contracts.mjs +3 -0
  46. package/src/autonomous-planner.mjs +46 -6
  47. package/src/autonomous-policy.mjs +10 -2
  48. package/src/autonomous-runtime.mjs +116 -16
  49. package/src/autonomous-supervisor.mjs +1 -1
  50. package/src/evidence-publisher-r42.mjs +76 -0
  51. package/src/mcp-server.mjs +1 -1
  52. package/src/provider-circuit.mjs +4 -4
  53. package/src/version.mjs +1 -1
  54. package/test/agent-eval-r42.test.mjs +39 -0
  55. package/test/agent-eval-runner-r42.test.mjs +61 -0
  56. package/test/agent-governance-r42.test.mjs +77 -0
  57. package/test/autonomous-r34-e2e.test.mjs +2 -1
  58. package/test/cli-entrypoint.test.mjs +1 -1
  59. package/test/core.test.mjs +1 -1
  60. package/test/evidence-publisher-r42.test.mjs +53 -0
  61. package/test/human-clarification-r42.test.mjs +53 -0
  62. package/test/provider-responses-r42.test.mjs +90 -0
  63. package/test/r33-closeout-regression.test.mjs +27 -12
  64. package/test/r33-finalization.test.mjs +3 -3
  65. package/test/r42-finalization.test.mjs +38 -0
  66. package/test/release-version.test.mjs +13 -13
  67. package/test/task-contract-r42.test.mjs +41 -0
@@ -25,6 +25,17 @@ const rmdirOperation = z.strictObject({ kind:z.literal('rmdir'), path:relPath, e
25
25
  const nativeOperation = z.union([writeOperation,replaceOperation,deleteOperation,mkdirOperation,moveOperation,rmdirOperation]);
26
26
  const plannerResponse = z.strictObject({ request_id:hex64, raw_text:z.string().min(2).max(8*1024*1024),
27
27
  model:z.string().min(1).max(128).default('client-model') });
28
+ const taskContract = z.strictObject({
29
+ schema:z.literal('deadbyte.task-contract.v1'),
30
+ scope:z.array(z.string().min(1).max(512)).max(32).default([]),
31
+ non_goals:z.array(z.string().min(1).max(512)).max(24).default([]),
32
+ constraints:z.array(z.string().min(1).max(512)).max(32).default([]),
33
+ evidence_requirements:z.array(z.string().min(1).max(512)).max(32).default([]),
34
+ risk_level:z.enum(['low','medium','high']).default('medium'),
35
+ clarification_policy:z.enum(['ask_when_blocked','fail_closed']).default('ask_when_blocked'),
36
+ project_default:z.enum(['lightweight','structured','agentic']).default('structured'),
37
+ explicit_override:z.enum(['lightweight','structured','agentic']).nullable().default(null)
38
+ });
28
39
  function result(schema,value,isError=false) { return autonomousStructuredResult(schema,value,isError); }
29
40
  function fail(schema,error,status='failed') { return result(schema,{ status, error:String(error) },true); }
30
41
 
@@ -60,11 +71,12 @@ export function registerAutonomousTools(server,{ autonomousRuntime, nativeDiffRu
60
71
  description:'Submit one bounded R24 production goal. Execution requires separate live coding and autonomous leases.',
61
72
  inputSchema:z.strictObject({ submission_id:id, root_id:id, title:z.string().max(160).default(''),
62
73
  goal:z.string().min(1).max(8192), acceptance:z.string().max(4096).default(''),
74
+ task_contract:taskContract.nullable().default(null),
63
75
  max_iterations:z.number().int().min(1).max(64).nullable().default(null) }),
64
76
  outputSchema:autonomousGoalSubmitOutputSchema
65
- },async({submission_id,root_id,title,goal,acceptance,max_iterations})=>{ try {
77
+ },async({submission_id,root_id,title,goal,acceptance,task_contract,max_iterations})=>{ try {
66
78
  return result(autonomousGoalSubmitOutputSchema,await autonomousRuntime.submit({ submissionId:submission_id,
67
- rootId:root_id,title,goal,acceptance,maxIterations:max_iterations })); }
79
+ rootId:root_id,title,goal,acceptance,taskContract:task_contract,maxIterations:max_iterations })); }
68
80
  catch(error){ return fail(autonomousGoalSubmitOutputSchema,error); } });
69
81
  server.registerTool('autonomous_goal_run',{
70
82
  description:'Advance one autonomous production goal through inspect/native-diff/build/test/benchmark/replan cycles. Client-planner mode returns a typed input_required sentinel for the Bridge MRTR adapter.',
@@ -100,11 +112,14 @@ export function registerAutonomousTools(server,{ autonomousRuntime, nativeDiffRu
100
112
  catch(error){ return fail(autonomousGoalCancelOutputSchema,error); } });
101
113
 
102
114
  server.registerTool('autonomous_goal_approve',{
103
- description:'Approve or reject resuming a paused autonomous goal. This never arms or renews any lease and cannot bypass release evidence.',
104
- inputSchema:z.strictObject({ goal_id:hex64, decision:z.enum(['approve','reject']).default('approve') }),
115
+ description:'Approve or reject resuming a paused autonomous goal, optionally supplying the bounded answer required by a typed human clarification. This never arms or renews any lease and cannot bypass release evidence.',
116
+ inputSchema:z.strictObject({ goal_id:hex64, decision:z.enum(['approve','reject']).default('approve'),
117
+ answer:z.string().min(1).max(4096).nullable().default(null),
118
+ approval_subject_sha256:hex64.nullable().default(null),
119
+ expires_in_ms:z.number().int().min(1000).max(3600000).default(300000) }),
105
120
  outputSchema:autonomousGoalApproveOutputSchema
106
- },async({goal_id,decision})=>{ try { return result(autonomousGoalApproveOutputSchema,
107
- await autonomousRuntime.approve({goalId:goal_id,decision})); }
121
+ },async({goal_id,decision,answer,approval_subject_sha256,expires_in_ms})=>{ try { return result(autonomousGoalApproveOutputSchema,
122
+ await autonomousRuntime.approve({goalId:goal_id,decision,answer,approvalSubjectSha256:approval_subject_sha256,expiresInMs:expires_in_ms})); }
108
123
  catch(error){ return fail(autonomousGoalApproveOutputSchema,error); } });
109
124
 
110
125
  server.registerTool('autonomous_goal_verify',{
@@ -15,12 +15,28 @@ const loopProgress = z.strictObject({
15
15
  const loopPendingAction = z.strictObject({
16
16
  action_id:hex64, request_sha256:hex64, kind:z.string().regex(/^[A-Za-z0-9._-]{1,64}$/)
17
17
  });
18
+ const loopClarification = z.strictObject({
19
+ question:z.string().min(1).max(1000), choices:z.array(z.string().min(1).max(256)).max(5)
20
+ });
21
+ const loopApprovalSubject = z.strictObject({
22
+ goal_id:hex64,reviewed_event_seq:z.number().int().min(1),reviewed_event_sha256:hex64,
23
+ journal_head_sha256:hex64,scope:z.enum(['resume_goal','human_clarification','desktop_resume','interrupted_effect_resume']),
24
+ subject:z.strictObject({reason:z.string().min(1).max(1024),waiting_payload_sha256:hex64}),approval_subject_sha256:hex64
25
+ });
26
+ const taskDagTask=z.strictObject({task_id:z.string().regex(/^[A-Za-z0-9._-]{1,64}$/),depends_on:z.array(z.string()).max(256),
27
+ input_artifacts:z.array(z.string()).max(128),output_artifacts:z.array(z.string()).max(128),
28
+ state:z.enum(['PENDING','RUNNING','COMPLETE','FAILED','STALE'])});
29
+ const taskDag=z.strictObject({schema:z.literal('deadbyte.task-dag.v1'),run_id:z.string().regex(/^[A-Za-z0-9._-]{1,96}$/),
30
+ goal_id:hex64,tasks:z.array(taskDagTask).min(1).max(256),dag_sha256:hex64});
18
31
  const publicLoopSchema = z.strictObject({
19
32
  schema:z.literal('deadbyte.autonomous-loop-state.v1'), phase:loopPhase,
20
33
  plan_id:hex64.nullable(), plan_version:z.number().int().min(1).nullable(),
21
34
  current_step:loopStep.nullable(), replan_count:z.number().int().min(0),
22
35
  progress:loopProgress.nullable(), pending_action:loopPendingAction.nullable(),
23
- pending_approval:z.boolean(), wait_reason:z.string().min(1).max(1024).nullable()
36
+ pending_approval:z.boolean(), wait_reason:z.string().min(1).max(1024).nullable(),
37
+ clarification:loopClarification.nullable().default(null),
38
+ approval_subject:loopApprovalSubject.nullable().default(null),
39
+ task_dag:taskDag.nullable().default(null)
24
40
  });
25
41
  function requireFields(value,ctx,fields) {
26
42
  for (const field of fields) if (value[field] === undefined) ctx.addIssue({ code:'custom', path:[field], message:`${field} is required` });
@@ -52,7 +68,7 @@ export const autonomousSessionStatusOutputSchema = statusContract({
52
68
  shape:{ configured:z.literal(true).optional(), armed:z.boolean().optional(), autonomous_armed:z.boolean().optional(),
53
69
  autonomous_reason:z.string().optional(), autonomous_expires_at_utc:z.string().nullable().optional(),
54
70
  coding_armed:z.boolean().optional(), coding_reason:z.string().optional(), root_ids:z.array(z.string()).max(64).optional(),
55
- planner_backend:z.enum(['client_sampling','openai_compatible']).optional(), profile_ids:z.array(z.string()).max(128).optional(),
71
+ planner_backend:z.enum(['client_sampling','openai_compatible','openai_responses']).optional(), profile_ids:z.array(z.string()).max(128).optional(),
56
72
  policy_sha256:hex64.optional(), coding_policy_sha256:hex64.optional() }
57
73
  });
58
74
 
@@ -163,6 +163,9 @@ export const r34PlannerDecisionSchema = z.discriminatedUnion('kind',[
163
163
  step_id:z.string().regex(ID),evidence:z.array(r34EvidenceSchema).min(1).max(32)}),
164
164
  z.strictObject({schema:z.literal('deadbyte.autonomous-decision.v2'),kind:z.literal('replan'),
165
165
  reason:z.string().min(1).max(512),steps:z.array(r34PlanStepSchema).min(1).max(24)}),
166
+ z.strictObject({schema:z.literal('deadbyte.autonomous-decision.v2'),kind:z.literal('clarify'),
167
+ question:z.string().min(1).max(1000),
168
+ choices:z.array(z.string().min(1).max(256)).min(2).max(5).default([])}),
166
169
  z.strictObject({schema:z.literal('deadbyte.autonomous-decision.v2'),kind:z.literal('pause'),
167
170
  reason:z.string().min(1).max(1000)}),
168
171
  z.strictObject({schema:z.literal('deadbyte.autonomous-decision.v2'),kind:z.literal('finish'),
@@ -276,7 +276,7 @@ export function parseR34PlannerDecision(rawText, policy) {
276
276
  assertR34UniqueSteps(decision.steps,'R34 replan');
277
277
  return decision;
278
278
  }
279
- if (decision.kind === 'pause' || decision.kind === 'finish') return decision;
279
+ if (decision.kind === 'clarify' || decision.kind === 'pause' || decision.kind === 'finish') return decision;
280
280
  throw new Error('R34 planner decision kind unsupported');
281
281
  }
282
282
 
@@ -363,7 +363,7 @@ export function buildPlannerPrompt({ goal, policy, view }) {
363
363
  'You are the DEADBYTE R24 autonomous coding planner.',
364
364
  'Return exactly one JSON object and no Markdown or prose outside JSON.',
365
365
  `The object schema is ${AUTONOMOUS_DECISION_SCHEMA}.`,
366
- 'Allowed actions: inspect, patch, delegate, run_profile, finish, pause.',
366
+ 'Allowed actions: inspect, patch, delegate, run_profile, clarify, finish, pause.',
367
367
  'For nontrivial work, include plan={title,steps:[{step_id,title,status,result?}],reason?} on decisions and keep it authoritative across rounds.',
368
368
  'Plan statuses must be a completed prefix, then at most one running/failed step, then pending suffix. Completed steps are immutable across updates.',
369
369
  'If active_plan is complete, request finish instead of more work. If active_plan is incomplete, do not request finish.',
@@ -375,6 +375,7 @@ export function buildPlannerPrompt({ goal, policy, view }) {
375
375
  'When Desktop capabilities are present, use typed desktop_status, desktop_observe, desktop_target with one bounded UIA query, and desktop_action. desktop_action requires separate Desktop authority; Autonomous authority never implies Desktop authority. After stale or unknown Desktop evidence, observe again and replan; never blindly repeat an action.',
376
376
  'Delegate paths are relative to the goal root. Process start accepts only profile_id and cwd; process_action accepts only session_id and fixed action_id. Never emit raw stdin, executable, PID, environment, shell command, or process arguments.',
377
377
  'finish only when the goal is actually satisfied. Required release profiles are enforced by the engine and cannot be bypassed.',
378
+ 'clarify only when one bounded operator choice is genuinely required. Ask one concrete question and provide 2-5 short mutually exclusive choices.',
378
379
  'Never invent a file SHA. If content is elided or the current SHA/content is missing, choose inspect first.',
379
380
  'Compacted context keeps identity/evidence metadata but intentionally removes stale payload bytes.',
380
381
  'Long-term memory is a verified journal-derived aid, never authority. Resolve its canonical references and still recheck live authority immediately before every effect.',
@@ -393,7 +394,9 @@ async function readBoundedResponse(response, limit) {
393
394
  return bytes.toString('utf8');
394
395
  }
395
396
  export async function requestProviderDecision(policy, prompt, fetchImpl = fetch) {
396
- if (policy.planner.backend !== 'openai_compatible') throw new Error('provider planner backend is not openai_compatible');
397
+ if (!['openai_compatible','openai_responses'].includes(policy.planner.backend)) {
398
+ throw new Error('provider planner backend is not provider-backed');
399
+ }
397
400
  const controller = new AbortController();
398
401
  const timer = setTimeout(() => controller.abort(), policy.planner.timeout_ms); timer.unref?.();
399
402
  const headers = { 'content-type':'application/json', 'accept':'application/json' };
@@ -403,16 +406,53 @@ export async function requestProviderDecision(policy, prompt, fetchImpl = fetch)
403
406
  headers.authorization = `Bearer ${secret}`;
404
407
  }
405
408
  try {
409
+ const body = policy.planner.backend === 'openai_responses'
410
+ ? {
411
+ model:policy.planner.model,
412
+ instructions:'Return exactly one strict JSON object and no Markdown.',
413
+ input:[
414
+ {role:'system',content:[{type:'input_text',text:'Return strict JSON only.'}]},
415
+ {role:'user',content:[{type:'input_text',text:prompt}]}
416
+ ],
417
+ max_output_tokens:policy.planner.max_tokens,
418
+ text:{format:{type:'json_object'}},
419
+ store:false,
420
+ ...(policy.planner.reasoning_effort?{reasoning:{effort:policy.planner.reasoning_effort}}:{})
421
+ }
422
+ : { model:policy.planner.model, temperature:0,
423
+ max_tokens:policy.planner.max_tokens,
424
+ messages:[{ role:'system', content:'Return strict JSON only.' },{ role:'user', content:prompt }] };
406
425
  const response = await fetchImpl(policy.planner.endpoint, {
407
426
  method:'POST', redirect:'error', signal:controller.signal, headers,
408
- body:JSON.stringify({ model:policy.planner.model, temperature:0,
409
- max_tokens:policy.planner.max_tokens,
410
- messages:[{ role:'system', content:'Return strict JSON only.' },{ role:'user', content:prompt }] })
427
+ body:JSON.stringify(body)
411
428
  });
412
429
  const rawBody = await readBoundedResponse(response, policy.planner.max_response_bytes);
413
430
  if (!response.ok) throw new Error(`planner HTTP ${response.status}: ${rawBody.slice(0,512)}`);
414
431
  let parsed;
415
432
  try { parsed = JSON.parse(rawBody); } catch { throw new Error('planner HTTP response is not JSON'); }
433
+ if (policy.planner.backend === 'openai_responses') {
434
+ if (parsed?.status !== 'completed') throw new Error(`planner Responses API response not completed: ${String(parsed?.status ?? 'missing')}`);
435
+ const content=[];
436
+ let refused=false;
437
+ for (const item of Array.isArray(parsed?.output) ? parsed.output : []) {
438
+ if (item?.type !== 'message') continue;
439
+ for (const part of Array.isArray(item.content) ? item.content : []) {
440
+ if (part?.type === 'refusal') refused=true;
441
+ if (part?.type === 'output_text' && typeof part.text === 'string') content.push(part.text);
442
+ }
443
+ }
444
+ if (refused) throw new Error('planner Responses API refusal');
445
+ if (content.length < 1) throw new Error('planner Responses API response missing output_text');
446
+ const usage=parsed?.usage && Number.isInteger(parsed.usage.input_tokens) && Number.isInteger(parsed.usage.output_tokens) && Number.isInteger(parsed.usage.total_tokens)
447
+ ? {input_tokens:parsed.usage.input_tokens,output_tokens:parsed.usage.output_tokens,total_tokens:parsed.usage.total_tokens}
448
+ : null;
449
+ return {
450
+ backend:'openai_responses', model:typeof parsed.model === 'string' ? parsed.model.slice(0,128) : policy.planner.model,
451
+ raw_text:content.join(''),
452
+ response_id:typeof parsed.id === 'string' ? parsed.id.slice(0,160) : null,
453
+ usage
454
+ };
455
+ }
416
456
  const content = parsed?.choices?.[0]?.message?.content;
417
457
  if (typeof content !== 'string') throw new Error('planner HTTP response missing choices[0].message.content');
418
458
  return { backend:'openai_compatible', model:typeof parsed.model === 'string' ? parsed.model : policy.planner.model,
@@ -33,7 +33,7 @@ function normalizeProviderCircuit(raw) {
33
33
  function normalizePlanner(raw) {
34
34
  if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new Error('autonomous planner missing');
35
35
  const backend = raw.backend;
36
- if (!['client_sampling', 'openai_compatible'].includes(backend)) throw new Error('autonomous planner backend invalid');
36
+ if (!['client_sampling', 'openai_compatible', 'openai_responses'].includes(backend)) throw new Error('autonomous planner backend invalid');
37
37
  const common = { backend, max_tokens: integer(raw.max_tokens, 'planner.max_tokens', 256, 16384, 4096) };
38
38
  if (backend === 'client_sampling') return common;
39
39
  if (typeof raw.endpoint !== 'string') throw new Error('planner.endpoint required');
@@ -46,12 +46,20 @@ function normalizePlanner(raw) {
46
46
  if (typeof raw.model !== 'string' || raw.model.length < 1 || raw.model.length > 128) throw new Error('planner.model invalid');
47
47
  const apiKeyEnv = raw.api_key_env ?? null;
48
48
  if (apiKeyEnv !== null && !ENV_RE.test(apiKeyEnv)) throw new Error('planner.api_key_env invalid');
49
- return {
49
+ const provider = {
50
50
  ...common, endpoint: endpoint.toString(), model: raw.model, api_key_env: apiKeyEnv,
51
51
  timeout_ms: integer(raw.timeout_ms, 'planner.timeout_ms', 1000, 300000, 60000),
52
52
  max_response_bytes: integer(raw.max_response_bytes, 'planner.max_response_bytes', 4096, 2 * 1024 * 1024, 512 * 1024),
53
53
  circuit:normalizeProviderCircuit(raw.circuit)
54
54
  };
55
+ if (backend === 'openai_responses') {
56
+ const effort = raw.reasoning_effort ?? null;
57
+ if (effort !== null && !['minimal','low','medium','high','xhigh'].includes(effort)) {
58
+ throw new Error('planner.reasoning_effort invalid');
59
+ }
60
+ return { ...provider, reasoning_effort:effort, store:false };
61
+ }
62
+ return provider;
55
63
  }
56
64
  function normalizeProfile(id, raw, codingPolicy) {
57
65
  if (!ID_RE.test(id)) throw new Error(`invalid autonomous profile '${id}'`);
@@ -10,6 +10,7 @@ import { createPlan, replanPlan, completePlanStep, planIsComplete as r34PlanIsCo
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 });
@@ -325,7 +331,31 @@ function releaseReadiness(policy, state) {
325
331
  });
326
332
  return { ready:results.every(item => item.passed), mutation_epoch:state.mutation_epoch, required_profiles:results };
327
333
  }
328
- function normalizeGoal(policy, { submissionId, rootId, title = '', goal, acceptance = '', maxIterations = null }) {
334
+ function normalizeTaskContract(raw) {
335
+ const value=raw ?? {schema:'deadbyte.task-contract.v1',scope:[],non_goals:[],constraints:[],
336
+ evidence_requirements:[],risk_level:'medium',clarification_policy:'ask_when_blocked',
337
+ project_default:'structured',explicit_override:null};
338
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('autonomous task contract invalid');
339
+ const allowed=['schema','scope','non_goals','constraints','evidence_requirements','risk_level','clarification_policy','project_default','explicit_override'];
340
+ const unknown=Object.keys(value).filter(key=>!allowed.includes(key));
341
+ if (unknown.length) throw new Error(`autonomous task contract unknown field '${unknown[0]}'`);
342
+ if (value.schema !== 'deadbyte.task-contract.v1') throw new Error('autonomous task contract schema invalid');
343
+ const list=(field,maxItems,maxLength)=>{
344
+ const items=value[field] ?? [];
345
+ if (!Array.isArray(items) || items.length > maxItems || items.some(item=>typeof item !== 'string' || item.length < 1 || item.length > maxLength || item.includes('\0'))) {
346
+ throw new Error(`autonomous task contract ${field} invalid`);
347
+ }
348
+ return [...items];
349
+ };
350
+ if (!['low','medium','high'].includes(value.risk_level)) throw new Error('autonomous task contract risk_level invalid');
351
+ if (!['ask_when_blocked','fail_closed'].includes(value.clarification_policy)) throw new Error('autonomous task contract clarification_policy invalid');
352
+ const rigor=deriveEffectiveRigor({project_default:value.project_default??'structured',risk_level:value.risk_level,
353
+ explicit_override:value.explicit_override??null});
354
+ return Object.freeze({schema:value.schema,scope:list('scope',32,512),non_goals:list('non_goals',24,512),
355
+ constraints:list('constraints',32,512),evidence_requirements:list('evidence_requirements',32,512),
356
+ risk_level:value.risk_level,clarification_policy:value.clarification_policy,...rigor});
357
+ }
358
+ function normalizeGoal(policy, { submissionId, rootId, title = '', goal, acceptance = '', taskContract = null, maxIterations = null }) {
329
359
  if (!ID_RE.test(submissionId ?? '')) throw new Error('autonomous submission_id invalid');
330
360
  if (!policy.root_ids.includes(rootId)) throw new Error(`autonomous root '${rootId}' is not allowed`);
331
361
  if (typeof title !== 'string' || title.length > 160) throw new Error('autonomous title must be <=160 characters');
@@ -335,7 +365,7 @@ function normalizeGoal(policy, { submissionId, rootId, title = '', goal, accepta
335
365
  if (!Number.isInteger(iterations) || iterations < 1 || iterations > policy.limits.max_iterations) throw new Error('autonomous max_iterations exceeds policy');
336
366
  const body = { schema:AUTONOMOUS_GOAL_SCHEMA, policy_sha256:policy.policy_sha256,
337
367
  coding_policy_sha256:policy.coding_policy_sha256, submission_id:submissionId, root_id:rootId,
338
- title, goal, acceptance, max_iterations:iterations };
368
+ title, goal, acceptance, task_contract:normalizeTaskContract(taskContract),max_iterations:iterations };
339
369
  return Object.freeze({ ...body, goal_sha256:sha256Object(body) });
340
370
  }
341
371
 
@@ -389,7 +419,8 @@ async function requestPlanner(ctx, goal, events, state, policy, codingRuntime, c
389
419
  return null;
390
420
  }
391
421
  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 });
422
+ let prompt = buildPlannerPrompt({ goal:{ goal_id:goal.goal_id,title:goal.title, goal:goal.goal, acceptance:goal.acceptance,
423
+ task_contract:goal.task_contract }, policy, view });
393
424
  const repair = [...(state.planner_rejections ?? [])].reverse().find(item => item.planner_round === round) ?? null;
394
425
  if (repair) {
395
426
  const repairState = canonicalJson({ request_id:repair.request_id, response_sha256:repair.response_sha256,
@@ -452,7 +483,24 @@ function legacyPublicLoopPhase(phase) {
452
483
  return ({PLAN:'PLANNING',INSPECT:'INSPECTING',ACT:'ACTING',VERIFY:'VERIFYING',CRITIC:'REPLANNING',
453
484
  REPAIR:'REPLANNING',RELEASE:'VERIFYING',WAITING:'WAITING',SUCCEEDED:'COMPLETE',FAILED:'WAITING'})[phase] ?? 'WAITING';
454
485
  }
455
- function publicLoopState(loop) {
486
+ function approvalScopeFor(reason){
487
+ if(reason==='human_input_required') return 'human_clarification';
488
+ if(String(reason??'').startsWith('desktop_')) return 'desktop_resume';
489
+ if(['ambiguous_interrupted_action','profile_execution_without_receipt'].includes(reason)) return 'interrupted_effect_resume';
490
+ return 'resume_goal';
491
+ }
492
+ function pendingApprovalSubject(goal,events,loop){
493
+ if(!loop?.pending_approval) return null;
494
+ const waiting=[...events].reverse().find(event=>event.type==='waiting_entered')??null;
495
+ if(!waiting) return null;
496
+ const payload=waiting.payload??{};
497
+ return bindApprovalSubject({
498
+ goal_id:goal.goal_id,reviewed_event_seq:waiting.seq,reviewed_event_sha256:waiting.event_sha256,
499
+ journal_head_sha256:events.at(-1)?.event_sha256??ZERO_HASH,scope:approvalScopeFor(payload.reason),
500
+ subject:{reason:payload.reason??loop.wait_reason??'paused',waiting_payload_sha256:sha256Object(payload)}
501
+ });
502
+ }
503
+ function publicLoopState(loop,goal=null,events=null) {
456
504
  if (!loop) return null;
457
505
  const currentStep=loop.current_step ? {
458
506
  step_id:loop.current_step.step_id,intent:loop.current_step.intent,status:loop.current_step.status
@@ -468,15 +516,18 @@ function publicLoopState(loop) {
468
516
  return {
469
517
  schema:'deadbyte.autonomous-loop-state.v1',phase:legacyPublicLoopPhase(loop.phase),plan_id:loop.plan_id,plan_version:loop.plan_version,
470
518
  current_step:currentStep,replan_count:loop.replan_count,progress,
471
- pending_action:pendingAction,pending_approval:loop.pending_approval,wait_reason:loop.wait_reason
519
+ pending_action:pendingAction,pending_approval:loop.pending_approval,wait_reason:loop.wait_reason,
520
+ clarification:loop.clarification ?? null,
521
+ approval_subject:goal&&events?pendingApprovalSubject(goal,events,loop):null,
522
+ task_dag:goal&&loop.plan?taskDagFromPlan({run_id:goal.goal_id,goal_id:goal.goal_id,plan:loop.plan}):null
472
523
  };
473
524
  }
474
525
  function withLoopResult(goal,events,result) {
475
- const loop=publicLoopState(loopStateOrNull(goal,events));
526
+ const loop=publicLoopState(loopStateOrNull(goal,events),goal,events);
476
527
  return loop ? {...result,loop} : result;
477
528
  }
478
529
  function providerCircuitSnapshot(policy,events,observedAtUtc=new Date().toISOString()) {
479
- if (policy.planner.backend !== 'openai_compatible') return null;
530
+ if (!['openai_compatible','openai_responses'].includes(policy.planner.backend)) return null;
480
531
  return deriveCircuit(events,providerCircuitId(policy.planner),policy.planner.circuit,observedAtUtc);
481
532
  }
482
533
  async function enterProviderCircuitWait({ctx,events,goal,policy,signingKeyPath,reason,circuit}) {
@@ -517,7 +568,9 @@ async function requestProviderWithCircuit({ctx,events,goal,policy,pending,signin
517
568
  try {
518
569
  const response=await requestProviderDecision(policy,pending.prompt);
519
570
  await appendEvent(ctx,events,signingKeyPath,'provider_call_succeeded',{
520
- provider_id:providerId,request_id:pending.request_id,probe:disposition.probe===true
571
+ provider_id:providerId,request_id:pending.request_id,probe:disposition.probe===true,
572
+ ...(response.response_id?{response_id:response.response_id}:{}),
573
+ ...(response.usage?{usage:response.usage}:{})
521
574
  });
522
575
  return {response};
523
576
  } catch (error) {
@@ -543,7 +596,7 @@ async function acceptPlannerDecision({ ctx, events, goal, state, policy, codingR
543
596
  let suppliedResponse = plannerResponse;
544
597
  while (pending) {
545
598
  let response;
546
- if (policy.planner.backend === 'openai_compatible') {
599
+ if (['openai_compatible','openai_responses'].includes(policy.planner.backend)) {
547
600
  const provider=await requestProviderWithCircuit({ctx,events,goal,policy,pending,signingKeyPath});
548
601
  if (provider.waiting) return {needs_planner:false,rejected:false,paused:true,provider_waiting:true};
549
602
  response=provider.response;
@@ -585,6 +638,8 @@ async function acceptPlannerDecision({ ctx, events, goal, state, policy, codingR
585
638
  const event = await appendEvent(ctx,events,signingKeyPath,'planner_decision',{
586
639
  request_id:pending.request_id, planner_round:pending.planner_round, mutation_epoch:pending.mutation_epoch,
587
640
  backend:response.backend, model:response.model ?? 'unknown', response_sha256:sha256Bytes(Buffer.from(response.raw_text,'utf8')),
641
+ ...(response.response_id?{response_id:response.response_id}:{}),
642
+ ...(response.usage?{usage:response.usage}:{}),
588
643
  ...(pending.working_memory_sha256?{
589
644
  working_memory_sha256:pending.working_memory_sha256,memory_snapshot_sha256:pending.memory_snapshot_sha256,
590
645
  memory_index_sha256:pending.memory_index_sha256,
@@ -1506,6 +1561,20 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
1506
1561
  return r34EnterWaiting({ctx,events,goal,signingKeyPath,reason:decision.reason,resumePhase:loop.phase});
1507
1562
  }
1508
1563
 
1564
+ if (decision.kind === 'clarify') {
1565
+ await appendEvent(ctx,events,signingKeyPath,'human_input_requested',{
1566
+ decision_event_sha256:decisionEvent.event_sha256,question:decision.question,choices:decision.choices
1567
+ });
1568
+ await appendEvent(ctx,events,signingKeyPath,'waiting_entered',{
1569
+ reason:'human_input_required',requires_approval:true,resume_phase:'PLAN',
1570
+ question:decision.question,choices:decision.choices
1571
+ });
1572
+ await appendEvent(ctx,events,signingKeyPath,'goal_paused',{
1573
+ reason:'human_input_required',decision_event_sha256:decisionEvent.event_sha256
1574
+ });
1575
+ return {status:'paused',reason:'human_input_required'};
1576
+ }
1577
+
1509
1578
  if (decision.kind === 'finish') {
1510
1579
  if (!r34PlanIsComplete(loop.plan)) {
1511
1580
  await appendEvent(ctx,events,signingKeyPath,'plan_guard_blocked',{
@@ -1676,7 +1745,7 @@ function terminalState(state) {
1676
1745
  }
1677
1746
  function pauseState(state) { return state.state === 'paused'; }
1678
1747
  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;
1748
+ if (state.state!=='paused'||state.reason!=='provider_circuit_open'||!['openai_compatible','openai_responses'].includes(policy.planner.backend)) return state;
1680
1749
  const circuit=providerCircuitSnapshot(policy,journal);
1681
1750
  const disposition=providerCallDisposition(circuit);
1682
1751
  if (!disposition.allowed||circuit.state!=='HALF_OPEN') return state;
@@ -1715,7 +1784,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1715
1784
  coding_policy_sha256:policy.coding_policy_sha256 };
1716
1785
  }
1717
1786
  async function providerCircuitStatus({goalId}) {
1718
- if (policy.planner.backend!=='openai_compatible') return null;
1787
+ if (!['openai_compatible','openai_responses'].includes(policy.planner.backend)) return null;
1719
1788
  const ctx=goalContext(policy,goalId);
1720
1789
  await loadGoal(ctx,policy,verifyKeyPath);
1721
1790
  const journal=await readEvents(ctx,verifyKeyPath);
@@ -1730,9 +1799,9 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1730
1799
  return { autonomous, coding };
1731
1800
  }
1732
1801
 
1733
- async function submit({ submissionId, rootId, title = '', goal, acceptance = '', maxIterations = null }) {
1802
+ async function submit({ submissionId, rootId, title = '', goal, acceptance = '', taskContract = null, maxIterations = null }) {
1734
1803
  await assertRunAuthority(rootId);
1735
- const normalized = normalizeGoal(policy,{ submissionId,rootId,title,goal,acceptance,maxIterations });
1804
+ const normalized = normalizeGoal(policy,{ submissionId,rootId,title,goal,acceptance,taskContract,maxIterations });
1736
1805
  const goalId = goalIdFor(policy.policy_sha256,submissionId);
1737
1806
  const ctx = goalContext(policy,goalId);
1738
1807
  if (await exists(ctx.dir)) {
@@ -1749,7 +1818,8 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1749
1818
  await writeFile(ctx.goalPath,`${canonicalJson(envelope)}\n`,{flag:'wx'});
1750
1819
  const journal = [];
1751
1820
  await appendEvent(ctx,journal,signingKeyPath,'goal_created',{
1752
- submission_id:submissionId, root_id:rootId, title, goal_sha256:normalized.goal_sha256,
1821
+ submission_id:submissionId, root_id:rootId, title, acceptance:normalized.acceptance,
1822
+ task_contract:normalized.task_contract,goal_sha256:normalized.goal_sha256,
1753
1823
  max_iterations:normalized.max_iterations,
1754
1824
  ...(memoryProvider?{source_release_manifest:memoryProvider.source_release_manifest}:{})
1755
1825
  });
@@ -1761,7 +1831,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1761
1831
  const goal = await loadGoal(ctx,policy,verifyKeyPath);
1762
1832
  const journal = await readEvents(ctx,verifyKeyPath);
1763
1833
  const derived = deriveState(goal,journal);
1764
- const loop = publicLoopState(loopStateOrNull(goal,journal));
1834
+ const loop = publicLoopState(loopStateOrNull(goal,journal),goal,journal);
1765
1835
  const lock=await runLockStatus(ctx);
1766
1836
  return { status:'ok', goal_id:goalId, goal_sha256:goal.goal_sha256,
1767
1837
  title:goal.title, root_id:goal.root_id, journal_verified:true, ...derived, ...lock,
@@ -1795,8 +1865,11 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1795
1865
  return { status:'existing', goal_id:goalId, state:current.state };
1796
1866
  }
1797
1867
  }
1798
- async function approve({ goalId, decision = 'approve' }) {
1868
+ async function approve({ goalId, decision = 'approve', answer = null, approvalSubjectSha256 = null, expiresInMs = 300000 }) {
1799
1869
  if (!['approve','reject'].includes(decision)) throw new Error('autonomous approval decision invalid');
1870
+ if (answer !== null && (typeof answer !== 'string' || answer.length < 1 || answer.length > 4096 || answer.includes('\0'))) {
1871
+ throw new Error('autonomous approval answer invalid');
1872
+ }
1800
1873
  const ctx = goalContext(policy,goalId);
1801
1874
  await acquireLock(ctx);
1802
1875
  try {
@@ -1811,7 +1884,34 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1811
1884
  }
1812
1885
  if (hasR34Loop(journal)) {
1813
1886
  await assertRunAuthority(goal.root_id);
1887
+ const loopState=loopStateOrNull(goal,journal);
1888
+ const approvalSubject=pendingApprovalSubject(goal,journal,loopState);
1889
+ if(!approvalSubject) throw new Error('autonomous approval subject unavailable');
1890
+ if(typeof approvalSubjectSha256!=='string'||approvalSubjectSha256!==approvalSubject.approval_subject_sha256){
1891
+ throw new Error('autonomous approval subject mismatch');
1892
+ }
1893
+ if(!Number.isInteger(expiresInMs)||expiresInMs<1000||expiresInMs>3_600_000) throw new Error('autonomous approval lifetime invalid');
1814
1894
  const waiting=[...journal].reverse().find(event=>event.type==='waiting_entered') ?? null;
1895
+ if (waiting?.payload?.reason === 'human_input_required' && answer === null) {
1896
+ throw new Error('autonomous clarification answer is required');
1897
+ }
1898
+ if (waiting?.payload?.reason !== 'human_input_required' && answer !== null) {
1899
+ throw new Error('autonomous approval answer is only valid for human clarification');
1900
+ }
1901
+ const issuedAt=new Date();
1902
+ const approval={schema:'deadbyte.bound-approval.v1',approval_subject_sha256:approvalSubjectSha256,
1903
+ reviewed_event_seq:approvalSubject.reviewed_event_seq,reviewed_event_sha256:approvalSubject.reviewed_event_sha256,
1904
+ journal_head_sha256:approvalSubject.journal_head_sha256,scope:approvalSubject.scope,
1905
+ issued_at_utc:issuedAt.toISOString(),expires_at_utc:new Date(issuedAt.getTime()+expiresInMs).toISOString(),
1906
+ provenance:'operator_mcp'};
1907
+ validateBoundApproval({approval,subject:approvalSubject,now:issuedAt});
1908
+ await appendEvent(ctx,journal,signingKeyPath,'approval_granted',approval);
1909
+ if (waiting?.payload?.reason === 'human_input_required') {
1910
+ await appendEvent(ctx,journal,signingKeyPath,'human_input_provided',{
1911
+ request_event_sha256:waiting.event_sha256,question:waiting.payload.question,
1912
+ answer,answer_sha256:sha256Bytes(Buffer.from(answer,'utf8'))
1913
+ });
1914
+ }
1815
1915
  const resumePhase=typeof waiting?.payload?.resume_phase === 'string' ? waiting.payload.resume_phase : 'PLAN';
1816
1916
  await appendEvent(ctx,journal,signingKeyPath,'waiting_resumed',{
1817
1917
  to:resumePhase,reason:waiting?.payload?.reason ?? current.reason ?? 'operator_approved',
@@ -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
  }
@@ -0,0 +1,76 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { link, mkdir, open, readFile, realpath, rm } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { canonicalJson } from './canonical-json.mjs';
5
+
6
+ const HEX64=/^[0-9a-f]{64}$/;
7
+ const ID=/^[A-Za-z0-9._-]{1,96}$/;
8
+ const shaBytes=bytes=>createHash('sha256').update(bytes).digest('hex');
9
+ const sha=value=>shaBytes(Buffer.from(canonicalJson(value),'utf8'));
10
+ function object(value,label){if(!value||typeof value!=='object'||Array.isArray(value))throw new Error(`${label} invalid`);return value;}
11
+ function keys(value,allowed,label){const extra=Object.keys(value).filter(key=>!allowed.includes(key));if(extra.length)throw new Error(`${label} unknown field '${extra[0]}'`);}
12
+ function id(value,label){if(typeof value!=='string'||!ID.test(value))throw new Error(`${label} invalid`);return value;}
13
+ function hex(value,label){if(typeof value!=='string'||!HEX64.test(value))throw new Error(`${label} invalid`);return value;}
14
+ function iso(value,label){if(typeof value!=='string'||!Number.isFinite(Date.parse(value)))throw new Error(`${label} invalid`);return value;}
15
+
16
+ export async function publishEvidenceAtomic({root,kind,evidenceId=null,payload}){
17
+ if(typeof root!=='string'||!path.isAbsolute(root)) throw new Error('evidence root must be absolute');
18
+ id(kind,'evidence kind');object(payload,'evidence payload');
19
+ const payloadSha=sha(payload);
20
+ const resolvedId=id(evidenceId??payloadSha,'evidence id');
21
+ const body={schema:'deadbyte.evidence-publication.v1',kind,evidence_id:resolvedId,payload_sha256:payloadSha,payload:structuredClone(payload)};
22
+ const envelope={...body,evidence_sha256:sha(body)};
23
+ const bytes=Buffer.from(`${canonicalJson(envelope)}\n`,'utf8');
24
+ const base=path.resolve(root);await mkdir(base,{recursive:true});
25
+ const canonicalRoot=await realpath(base);
26
+ const pendingDir=path.join(canonicalRoot,'.pending');const finalDir=path.join(canonicalRoot,kind);
27
+ await mkdir(pendingDir,{recursive:true});await mkdir(finalDir,{recursive:true});
28
+ const target=path.join(finalDir,`${resolvedId}.json`);
29
+ const temp=path.join(pendingDir,`${kind}-${resolvedId}-${randomUUID()}.tmp`);
30
+ let handle;
31
+ try{
32
+ handle=await open(temp,'wx',0o600);await handle.writeFile(bytes);await handle.sync();await handle.close();handle=null;
33
+ const staged=await readFile(temp);
34
+ if(shaBytes(staged)!==shaBytes(bytes)) throw new Error('staged evidence hash verification failed');
35
+ try{
36
+ await link(temp,target);
37
+ return Object.freeze({status:'created',kind,evidence_id:resolvedId,evidence_sha256:envelope.evidence_sha256,
38
+ bytes:bytes.length,path:target});
39
+ }catch(error){
40
+ if(error?.code!=='EEXIST') throw error;
41
+ const existing=await readFile(target);
42
+ if(!existing.equals(bytes)){
43
+ const conflict=new Error(`EVIDENCE_ID_CONFLICT: ${kind}/${resolvedId}`);conflict.code='EVIDENCE_ID_CONFLICT';throw conflict;
44
+ }
45
+ return Object.freeze({status:'existing',kind,evidence_id:resolvedId,evidence_sha256:envelope.evidence_sha256,
46
+ bytes:bytes.length,path:target});
47
+ }
48
+ }finally{
49
+ if(handle) await handle.close().catch(()=>{});
50
+ await rm(temp,{force:true}).catch(()=>{});
51
+ }
52
+ }
53
+
54
+ export function createExecutionReceipt(raw){
55
+ const value=object(raw,'execution receipt');
56
+ keys(value,['execution_id','subject_sha256','request_sha256','observed_exit_code','started_at_utc','completed_at_utc'],'execution receipt');
57
+ const core={schema:'deadbyte.execution-receipt.v1',execution_id:id(value.execution_id,'execution_id'),
58
+ subject_sha256:hex(value.subject_sha256,'execution subject_sha256'),request_sha256:hex(value.request_sha256,'execution request_sha256'),
59
+ observed_exit_code:value.observed_exit_code,started_at_utc:iso(value.started_at_utc,'execution started_at_utc'),
60
+ completed_at_utc:iso(value.completed_at_utc,'execution completed_at_utc')};
61
+ if(!Number.isInteger(core.observed_exit_code)) throw new Error('execution exit code invalid');
62
+ if(Date.parse(core.completed_at_utc)<Date.parse(core.started_at_utc)) throw new Error('execution time order invalid');
63
+ return Object.freeze({...core,receipt_sha256:sha(core)});
64
+ }
65
+
66
+ export function createVerificationResult(raw){
67
+ const value=object(raw,'verification result');
68
+ keys(value,['verification_id','subject_sha256','execution_receipt_sha256','outcome_passed','oracle','evidence_refs'],'verification result');
69
+ if(typeof value.outcome_passed!=='boolean') throw new Error('verification outcome invalid');
70
+ if(!Array.isArray(value.evidence_refs)||value.evidence_refs.length<1||value.evidence_refs.length>64||value.evidence_refs.some(ref=>!HEX64.test(ref))) throw new Error('verification evidence refs invalid');
71
+ const core={schema:'deadbyte.verification-result.v1',verification_id:id(value.verification_id,'verification_id'),
72
+ subject_sha256:hex(value.subject_sha256,'verification subject_sha256'),
73
+ execution_receipt_sha256:hex(value.execution_receipt_sha256,'verification receipt_sha256'),
74
+ outcome_passed:value.outcome_passed,oracle:id(value.oracle,'verification oracle'),evidence_refs:[...value.evidence_refs]};
75
+ return Object.freeze({...core,verification_sha256:sha(core)});
76
+ }
@@ -202,7 +202,7 @@ const memoryProvider=autonomousPolicy?createR41MemoryProvider({
202
202
  }):null;
203
203
  const autonomousRuntime = autonomousPolicy ? createAutonomousRuntime({ policy:autonomousPolicy,codingRuntime,nativeDiffRuntime,
204
204
  machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime,desktopMemoryProvider,memoryProvider,signingKeyPath,verifyKeyPath }) : null;
205
- const autonomousSupervisor = autonomousRuntime && autonomousPolicy.planner.backend === 'openai_compatible' &&
205
+ const autonomousSupervisor = autonomousRuntime && ['openai_compatible','openai_responses'].includes(autonomousPolicy.planner.backend) &&
206
206
  autonomousSupervisorMode === 'in_process'
207
207
  ? startAutonomousSupervisor({ runtime: autonomousRuntime, policy: autonomousPolicy })
208
208
  : null;
@@ -48,11 +48,11 @@ function openAt(state,ms,policy){
48
48
  }
49
49
 
50
50
  export function providerCircuitId(planner){
51
- if(planner?.backend!=='openai_compatible'||typeof planner.endpoint!=='string'||typeof planner.model!=='string') {
51
+ if(!['openai_compatible','openai_responses'].includes(planner?.backend)||typeof planner.endpoint!=='string'||typeof planner.model!=='string') {
52
52
  throw new Error('provider circuit planner identity invalid');
53
53
  }
54
- const digest=createHash('sha256').update(Buffer.from(`${planner.endpoint}\0${planner.model}`,'utf8')).digest('hex');
55
- return `openai_compatible:${digest}`;
54
+ const digest=createHash('sha256').update(Buffer.from(`${planner.backend}\0${planner.endpoint}\0${planner.model}`,'utf8')).digest('hex');
55
+ return `${planner.backend}:${digest}`;
56
56
  }
57
57
 
58
58
  export function classifyProviderFailure(error){
@@ -61,7 +61,7 @@ export function classifyProviderFailure(error){
61
61
  if(name==='aborterror'||/\b(?:timed?\s*out|timeout|aborted)\b/.test(text)) return 'timeout';
62
62
  if(/(?:http\s*429|rate[ _-]?limit|too many requests)/.test(text)) return 'rate_limit';
63
63
  if(/(?:http\s*(?:401|403)|unauthori[sz]ed|forbidden|api key|authentication)/.test(text)) return 'auth';
64
- if(/(?:not json|invalid json|missing choices|invalid response|response.*(?:schema|contract|content))/.test(text)) return 'invalid_response';
64
+ if(/(?:not json|invalid json|missing choices|missing output_text|refusal|not completed|incomplete response|invalid response|response.*(?:schema|contract|content))/.test(text)) return 'invalid_response';
65
65
  if(/(?:econnreset|econnrefused|enotfound|fetch failed|network|socket|transport|connection)/.test(text)) return 'transport';
66
66
  return 'other';
67
67
  }
package/src/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export const DEADBYTE_VERSION = '0.13.0';
1
+ export const DEADBYTE_VERSION = '0.14.0';
2
2
  export const MCP_PROTOCOL_VERSION = '2026-07-28';
3
3
  export const RECEIPT_SCHEMA_V1 = 'deadbyte.receipt.v1';
4
4
  export const RECEIPT_SCHEMA_V2 = 'deadbyte.receipt.v2';