import crypto from 'node:crypto'; import { Annotation, END, START, StateGraph, } from '@langchain/langgraph'; import { buildRunEvent, normalizeRunSpec, } from './contracts.mjs'; export const CODE_RUN_SHADOW_WORKFLOW = 'code-run-v1'; const ShadowState = Annotation.Root({ spec: Annotation(), status: Annotation({ reducer: (_current, update) => update, default: () => 'queued', }), phase: Annotation({ reducer: (_current, update) => update, default: () => 'accepted', }), plan: Annotation({ reducer: (_current, update) => update, default: () => null, }), result: Annotation({ reducer: (_current, update) => update, default: () => null, }), events: Annotation({ reducer: (current, update) => [...current, ...update], default: () => [], }), }); function executorIdentityForRun(runId) { const normalizedRunId = String(runId ?? '').trim(); const jobId = `${normalizedRunId}:executor-preview`; if (/^[a-zA-Z0-9:_-]+$/.test(normalizedRunId) && jobId.length <= 128) { return { jobId, idempotencyKey: `${normalizedRunId}:build_plan:v1`, }; } const digest = crypto.createHash('sha256').update(normalizedRunId).digest('hex'); return { jobId: `run_${digest}:executor-preview`, idempotencyKey: `run_${digest}:build_plan:v1`, }; } function eventFor(state, type, data = null) { return buildRunEvent({ runId: state.spec.runId, sequence: state.events.length + 1, type, data, }); } function validateNode(state, { executionEnabled = false } = {}) { const spec = normalizeRunSpec(state.spec); if (spec.workflow.name !== CODE_RUN_SHADOW_WORKFLOW) { const error = new Error(`Unsupported workflow: ${spec.workflow.name}`); error.code = 'WORKFLOW_NOT_SUPPORTED'; throw error; } const observeOnly = spec.policy.executionMode === 'observe-only' && spec.policy.sideEffectsAllowed === false; const active = spec.policy.executionMode === 'active' && spec.policy.sideEffectsAllowed === true && executionEnabled; if (!observeOnly && !active) { const error = new Error( executionEnabled ? 'LangGraph run policy must be observe-only or explicitly active' : 'LangGraph execution is disabled; only observe-only runs are accepted', ); error.code = 'WORKFLOW_OBSERVE_ONLY_REQUIRED'; throw error; } return { spec, status: 'running', phase: 'validated', events: [eventFor(state, 'workflow_validated', { workflow: spec.workflow, executionMode: spec.policy.executionMode, })], }; } async function planNode(state, executorGateway) { const taskType = String(state.spec.input?.taskType ?? '').trim() || 'code-change'; const instruction = String(state.spec.input?.instruction ?? ''); const executorIdentity = executorIdentityForRun(state.spec.runId); const active = state.spec.policy.executionMode === 'active'; const executor = String(state.spec.input?.executor ?? 'goosed').trim().toLowerCase(); const executorJob = await executorGateway.createJob({ jobId: executorIdentity.jobId, idempotencyKey: executorIdentity.idempotencyKey, executor, task: { type: taskType, instruction, workspaceRef: state.spec.input?.workspaceRef ?? null, inputRefs: state.spec.input?.inputRefs ?? [], }, subject: state.spec.subject, authorization: { executionAllowed: active, actorId: state.spec.subject?.userId ?? null, }, policy: { sideEffectsAllowed: active && state.spec.policy.sideEffectsAllowed === true, networkAllowed: active && state.spec.policy.networkAllowed === true, }, controls: { timeoutMs: state.spec.limits?.timeoutMs, cancellationAllowed: true, fallbackExecutor: 'aider', maxAttempts: state.spec.limits?.maxAttempts, }, metadata: { source: 'langgraph-shadow-plan', workflow: state.spec.workflow, }, }); const plan = { taskType, executorAdapter: active ? 'executor-gateway' : 'native-agent-run', executorJob: { kind: 'executor-job', id: executorJob.job.jobId, executor: executorJob.job.executor, status: executorJob.job.status, reason: executorJob.job.reason, durable: executorGateway.status().store.durable, }, instructionCharacters: instruction.length, steps: [ 'accept_control_plane_run', 'project_executor_boundary', 'record_shadow_result', ], }; return { phase: 'planned', plan, events: [eventFor(state, 'workflow_planned', { taskType, executorAdapter: plan.executorAdapter, executorJob: plan.executorJob, stepCount: plan.steps.length, })], }; } function finalizeNode(state) { const queued = state.plan.executorJob.status === 'queued'; const result = { observed: !queued, executed: false, executorAdapter: state.plan.executorAdapter, taskType: state.plan.taskType, }; return { status: queued ? 'waiting' : 'succeeded', phase: queued ? 'executor_queued' : 'completed', result, events: [eventFor( state, queued ? 'workflow_executor_queued' : 'workflow_completed', result, )], }; } export function createCodeRunShadowGraph({ checkpointer, executorGateway, executionEnabled = false, } = {}) { if (!checkpointer) throw new Error('Code run shadow graph requires a checkpointer'); if (!executorGateway?.createJob) { throw new Error('Code run shadow graph requires an Executor Gateway'); } return new StateGraph(ShadowState) .addNode('validate_run', (state) => validateNode(state, { executionEnabled })) .addNode('build_plan', (state) => planNode(state, executorGateway)) .addNode('finalize_run', finalizeNode) .addEdge(START, 'validate_run') .addEdge('validate_run', 'build_plan') .addEdge('build_plan', 'finalize_run') .addEdge('finalize_run', END) .compile({ checkpointer }); } export const codeRunShadowGraphInternals = { ShadowState, executorIdentityForRun, eventFor, validateNode, planNode, finalizeNode, };