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.
- package/.gitattributes +7 -0
- package/AGENTS.md +18 -0
- package/CONTEXT.md +3 -3
- package/MANIFEST.SHA256 +152 -75
- package/R42-PARENT.json +19 -0
- package/R43-PARENT.json +20 -0
- package/R44-DONORS.json +99 -0
- package/R44-PARENT.json +18 -0
- package/README.txt +7 -2
- package/bin/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.obj +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/controller/README.TXT +4 -5
- package/controller/deadbyte-controller.ps1 +1 -1
- package/controller/deadbyte-desktop-policy.json +5 -5
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/ARCHITECTURE.md +8 -1
- package/docs/R44-ARCHITECTURE.md +37 -0
- package/harness/r42-agent-evals.json +48 -0
- package/harness/task-contract.schema.json +33 -0
- package/package.json +15 -8
- package/proof/CONTAINMENT-BUILD.txt +6 -6
- package/scripts/agent-eval-r42.mjs +46 -0
- package/scripts/deferred-slot-operation-r42.mjs +126 -0
- package/scripts/deferred-slot-operation-r43.mjs +126 -0
- package/scripts/deferred-slot-operation-r44.mjs +126 -0
- package/scripts/final-closure-r42.mjs +116 -0
- package/scripts/final-closure-r43.mjs +115 -0
- package/scripts/final-closure-r44.mjs +121 -0
- package/scripts/final-closure-verify-r42.mjs +223 -0
- package/scripts/final-closure-verify-r43.mjs +213 -0
- package/scripts/final-closure-verify-r44.mjs +220 -0
- package/scripts/finalize-r43.mjs +217 -0
- package/scripts/finalize-r44.mjs +217 -0
- package/scripts/full-regression-r43.mjs +20 -0
- package/scripts/full-regression-r44.mjs +20 -0
- package/scripts/gate-windows-r42.ps1 +54 -0
- package/scripts/gate-windows-r43.ps1 +36 -0
- package/scripts/gate-windows-r44.ps1 +36 -0
- package/scripts/release-parity-r42.mjs +235 -0
- package/scripts/release-parity-r43.mjs +218 -0
- package/scripts/release-parity-r44.mjs +242 -0
- package/scripts/release-parity-tests-r42.ps1 +6 -0
- package/scripts/release-parity-tests-r43.ps1 +8 -0
- package/scripts/release-parity-tests-r44.ps1 +8 -0
- package/scripts/verify-r42-parent.mjs +101 -0
- package/scripts/verify-r43-parent.mjs +126 -0
- package/scripts/verify-r44-parent.mjs +142 -0
- package/scripts/windows-gate-evidence-r42.mjs +64 -0
- package/scripts/windows-gate-evidence-r43.mjs +55 -0
- package/scripts/windows-gate-evidence-r44.mjs +55 -0
- package/src/agent-backend-r44.mjs +254 -0
- package/src/agent-eval-r42.mjs +73 -0
- package/src/agent-eval-runner-r42.mjs +138 -0
- package/src/agent-governance-r42.mjs +142 -0
- package/src/autonomous-context-engine.mjs +2 -0
- package/src/autonomous-loop-state.mjs +10 -0
- package/src/autonomous-mcp-tools.mjs +21 -6
- package/src/autonomous-output-contracts.mjs +18 -2
- package/src/autonomous-planner-contracts.mjs +3 -0
- package/src/autonomous-planner.mjs +46 -6
- package/src/autonomous-policy.mjs +50 -5
- package/src/autonomous-runtime.mjs +138 -25
- package/src/autonomous-supervisor-host.mjs +12 -2
- package/src/autonomous-supervisor.mjs +1 -1
- package/src/capability-ledger.mjs +2 -1
- package/src/codex-app-server-r44.mjs +154 -0
- package/src/coding-plane.mjs +66 -19
- package/src/durable-job-r44.mjs +145 -0
- package/src/evidence-publisher-r42.mjs +76 -0
- package/src/external-job-provider-r44.mjs +126 -0
- package/src/host-plane.mjs +27 -6
- package/src/job-supervisor-host-r44.mjs +112 -0
- package/src/machine-fs-smoke-client.mjs +1 -1
- package/src/machine-fs.mjs +21 -10
- package/src/mcp-host-smoke-client.mjs +1 -1
- package/src/mcp-server.mjs +20 -3
- package/src/mcp-smoke-client.mjs +1 -1
- package/src/native-diff-v2.mjs +3 -3
- package/src/native-diff.mjs +3 -2
- package/src/observation-smoke-client.mjs +1 -1
- package/src/operator-surface.mjs +2 -2
- package/src/process-policy.mjs +19 -0
- package/src/process-runtime.mjs +5 -2
- package/src/process-smoke-client.mjs +2 -2
- package/src/provider-circuit.mjs +10 -4
- package/src/r44-compact-disarmed-probe.mjs +74 -0
- package/src/r44-full-surface-probe.mjs +34 -0
- package/src/remote-device-r44.mjs +205 -0
- package/src/remote-mcp-autonomous-smoke-client.mjs +4 -4
- package/src/remote-mcp-r44-parity-client.mjs +203 -0
- package/src/remote-mcp-smoke-client.mjs +7 -4
- package/src/runtime-update.mjs +64 -31
- package/src/skill-mcp-tools-r44.mjs +37 -0
- package/src/skill-plane-r44.mjs +203 -0
- package/src/tunnel-stdio-compat-smoke.mjs +1 -1
- package/src/version.mjs +1 -1
- package/test/agent-backend-r44.test.mjs +115 -0
- package/test/agent-eval-r42.test.mjs +39 -0
- package/test/agent-eval-runner-r42.test.mjs +61 -0
- package/test/agent-governance-r42.test.mjs +77 -0
- package/test/agent-runtime.test.mjs +3 -3
- package/test/autonomous-loop.test.mjs +97 -11
- package/test/autonomous-r34-e2e.test.mjs +2 -1
- package/test/autonomous-runtime.test.mjs +2 -2
- package/test/capability-ledger-r36.test.mjs +3 -3
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/codex-app-server-r44.test.mjs +115 -0
- package/test/codex-autonomous-r44.test.mjs +87 -0
- package/test/coding-plane.test.mjs +2 -2
- package/test/coding-runtime.test.mjs +36 -1
- package/test/compact-catalog-r36.test.mjs +3 -3
- package/test/compact-max-catalog-r40.test.mjs +4 -2
- package/test/compact-max-catalog-r44.test.mjs +97 -0
- package/test/core.test.mjs +1 -1
- package/test/durable-job-r44.test.mjs +75 -0
- package/test/evidence-publisher-r42.test.mjs +53 -0
- package/test/external-job-provider-r44.test.mjs +46 -0
- package/test/full-max-catalog-r40.test.mjs +5 -3
- package/test/full-max-catalog-r44.test.mjs +91 -0
- package/test/host-plane.test.mjs +15 -1
- package/test/human-clarification-r42.test.mjs +53 -0
- package/test/native-diff.test.mjs +28 -0
- package/test/output-schema.test.mjs +2 -2
- package/test/process-policy.test.mjs +22 -1
- package/test/prompts-resources.test.mjs +1 -1
- package/test/provider-responses-r42.test.mjs +90 -0
- package/test/r33-closeout-regression.test.mjs +14 -16
- package/test/r33-finalization.test.mjs +7 -7
- package/test/r42-finalization.test.mjs +38 -0
- package/test/r43-finalization.test.mjs +31 -0
- package/test/r43-finalizer.test.mjs +34 -0
- package/test/r43-parent.test.mjs +29 -0
- package/test/r44-finalization.test.mjs +58 -0
- package/test/r44-finalizer.test.mjs +48 -0
- package/test/r44-parent.test.mjs +25 -0
- package/test/release-version.test.mjs +53 -19
- package/test/remote-device-r44.test.mjs +104 -0
- package/test/runtime-self-update.test.mjs +42 -0
- package/test/skill-mcp-r44.test.mjs +38 -0
- package/test/skill-plane-r44.test.mjs +93 -0
- package/test/standing-grants.test.mjs +5 -1
- 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
|
|
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
|
|
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
|
|
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
|
|
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=
|
|
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
|
|
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'
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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=
|
|
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
|
+
}
|