{"version":3,"file":"remote-mission-executor.d.ts","sourceRoot":"","sources":["../../../src/core/remote-execution/remote-mission-executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Remote Mission Executor (2.14.0).\n *\n * A `MissionExecutor` whose child executes on a remote target while the\n * Bucephalus control plane keeps the durable authority. It is structurally the\n * same contract as `ProcessMissionExecutor` (launch → handle → awaitResult →\n * MissionResult), but the harness is a remote transport instead of local\n * `child_process.spawn`.\n *\n * Invariants:\n *   - A raw remote exit 0 without verification is PARTIAL, never SUCCEEDED.\n *   - Duplicate launch of one execution identity is rejected (idempotency).\n *   - Transport close without a terminal frame is never success.\n *   - Remote result is an observation; the durable coordinator accepts/rejects\n *     the terminal commit under the existing execution fence.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport type {\n\tMissionExecutionCorrelation,\n\tMissionExecutor,\n\tMissionLaunchOptions,\n} from \"../mission-domain/mission-executor.js\";\nimport { createMissionHandle, type MissionHandle } from \"../mission-domain/mission-handle.js\";\nimport type { MissionRequest } from \"../mission-domain/mission-request.js\";\nimport { validateMissionRequest } from \"../mission-domain/mission-request.js\";\nimport {\n\tclassifyExecutorOutcome,\n\tcreateMissionResult,\n\ttype ExecutorDiagnostics,\n\ttype MissionResult,\n\ttype StructuredFailure,\n} from \"../mission-domain/mission-result.js\";\nimport { MissionStateTracker } from \"../mission-domain/mission-state.js\";\nimport type { ProcessMissionOutcome, ProcessMissionVerifier } from \"../mission-domain/process-mission-executor.js\";\nimport { RemoteExecutionError } from \"./remote-execution-error.js\";\nimport type { RemoteExecutionTarget } from \"./remote-target-types.js\";\nimport type {\n\tRemoteChildLaunch,\n\tRemoteCommandRunner,\n\tRemoteExecutionTransport,\n\tRemoteTransportEvent,\n} from \"./remote-transport.js\";\nimport { buildRemoteAcceptanceCriteriaVerifier } from \"./remote-verification.js\";\n\n// =============================================================================\n// Options\n// =============================================================================\n\nexport interface RemoteMissionExecutorOptions {\n\texecutorId?: string;\n\ttarget: RemoteExecutionTarget;\n\ttransport: RemoteExecutionTransport & RemoteCommandRunner;\n\t/**\n\t * Concrete remote child launch for this mission's resume. The closure is\n\t * built by the caller (worker/resume path) and captures the resume prompt +\n\t * child session id, exactly like `ProcessMissionExecutor.buildLaunch`. The\n\t * resolved remote dirs are supplied so the launch can point `--session-dir`\n\t * and the working directory at the execution-scoped workspace.\n\t */\n\tbuildRemoteLaunch: (input: {\n\t\trequest: MissionRequest;\n\t\tworkspaceDir: string;\n\t\tagentDir: string;\n\t\tsessionDir: string;\n\t}) => RemoteChildLaunch;\n\t/** Initial workspace files to materialise on the target (remote-relative). */\n\tworkspaceFiles?: (request: MissionRequest) => { path: string; content: string }[];\n\t/** Materialised remote models.json content (ephemeral protected config). */\n\tmodelsJson: string;\n\t/** Child session identity + file content to materialise on the target. */\n\tchildSessionId?: string;\n\tsessionFileContent?: string;\n\t/** Absolute remote temp root for the execution-scoped workspace. */\n\tremoteTempRoot: string;\n\t/** Remote-relative files to hash before/after (location proof). */\n\tevidenceFiles?: string[];\n\t/** Optional verifier override (defaults to remote acceptance-criteria). */\n\tverifier?: ProcessMissionVerifier;\n\t/**\n\t * Optional shared-inference admission wiring. When set, the remote child\n\t * routes its shared-resource inference through the central scheduler via a\n\t * reverse tunnel that lives exactly as long as this execution's SSH session.\n\t * `tokenIssuer` is called with the concrete executionId so the token is\n\t * execution-scoped (never persisted, never placed in argv).\n\t */\n\tadmission?: {\n\t\tlocalPort: number;\n\t\tremotePort: number;\n\t\ttokenIssuer: (executionId: string) => { token: string } | Promise<{ token: string }>;\n\t};\n\texecutionIdFactory?: (request: MissionRequest) => string;\n\tlaunchIdFactory?: () => string;\n\ttimeoutMs?: number;\n\theartbeatMs?: number;\n\tnow?: () => number;\n\t/** Observer for actual remote execution and retry events; excludes polling. */\n\teventObserver?: (event: RemoteTransportEvent) => void;\n}\n\ninterface ActiveMission {\n\trequest: MissionRequest;\n\texecutionId: string;\n\tlaunchId: string;\n\tworkspaceDir: string;\n\tagentDir: string;\n\ttracker: MissionStateTracker;\n\tstartedAtMs: number;\n\tfinishedAtMs?: number;\n\tcontroller: AbortController;\n\tcorrelation: MissionExecutionCorrelation;\n\thandle: import(\"./remote-transport.js\").RemoteExecutionHandle;\n\tcancelled: boolean;\n}\n\nexport class RemoteMissionExecutor implements MissionExecutor {\n\treadonly executorId: string;\n\tprivate readonly _target: RemoteExecutionTarget;\n\tprivate readonly _transport: RemoteExecutionTransport & RemoteCommandRunner;\n\tprivate readonly _buildRemoteLaunch: (input: {\n\t\trequest: MissionRequest;\n\t\tworkspaceDir: string;\n\t\tagentDir: string;\n\t\tsessionDir: string;\n\t}) => RemoteChildLaunch;\n\tprivate readonly _workspaceFiles?: (request: MissionRequest) => { path: string; content: string }[];\n\tprivate readonly _modelsJson: string;\n\tprivate readonly _childSessionId?: string;\n\tprivate readonly _sessionFileContent?: string;\n\tprivate readonly _remoteTempRoot: string;\n\tprivate readonly _evidenceFiles?: string[];\n\tprivate readonly _verifier?: ProcessMissionVerifier;\n\tprivate readonly _admission?: {\n\t\tlocalPort: number;\n\t\tremotePort: number;\n\t\ttokenIssuer: (executionId: string) => { token: string } | Promise<{ token: string }>;\n\t};\n\tprivate readonly _executionIdFactory: (request: MissionRequest) => string;\n\tprivate readonly _launchIdFactory: () => string;\n\tprivate readonly _timeoutMs?: number;\n\tprivate readonly _heartbeatMs?: number;\n\tprivate readonly _now: () => number;\n\tprivate readonly _eventObserver?: (event: RemoteTransportEvent) => void;\n\tprivate readonly _active = new Map<string, ActiveMission>();\n\tprivate readonly _launchedIds = new Set<string>();\n\n\tconstructor(options: RemoteMissionExecutorOptions) {\n\t\tthis.executorId = options.executorId ?? \"remote\";\n\t\tthis._target = options.target;\n\t\tthis._transport = options.transport;\n\t\tthis._buildRemoteLaunch = options.buildRemoteLaunch;\n\t\tthis._workspaceFiles = options.workspaceFiles;\n\t\tthis._modelsJson = options.modelsJson;\n\t\tthis._childSessionId = options.childSessionId;\n\t\tthis._sessionFileContent = options.sessionFileContent;\n\t\tthis._remoteTempRoot = options.remoteTempRoot;\n\t\tthis._evidenceFiles = options.evidenceFiles;\n\t\tthis._verifier = options.verifier;\n\t\tthis._admission = options.admission;\n\t\tthis._executionIdFactory = options.executionIdFactory ?? (() => `exec_${randomUUID()}`);\n\t\tthis._launchIdFactory = options.launchIdFactory ?? (() => `launch_${randomUUID()}`);\n\t\tthis._timeoutMs = options.timeoutMs;\n\t\tthis._heartbeatMs = options.heartbeatMs;\n\t\tthis._now = options.now ?? (() => Date.now());\n\t\tthis._eventObserver = options.eventObserver;\n\t}\n\n\tasync launch(request: MissionRequest, options: MissionLaunchOptions = {}): Promise<MissionHandle> {\n\t\tconst validation = validateMissionRequest(request);\n\t\tif (!validation.valid) {\n\t\t\tthrow new Error(`Invalid MissionRequest: ${validation.errors.join(\", \")}`);\n\t\t}\n\n\t\tconst executionId = options.executionId ?? this._executionIdFactory(request);\n\t\tconst launchId = this._launchIdFactory();\n\t\tconst correlation: MissionExecutionCorrelation = {\n\t\t\tmissionId: request.missionId,\n\t\t\tassignmentId: options.assignmentId,\n\t\t\tattemptId: options.attemptId,\n\t\t\texecutionId,\n\t\t\tsessionId: options.sessionId ?? request.childSessionId,\n\t\t};\n\t\tthis._emitEvent({\n\t\t\teventId: `${request.missionId}:${executionId}:connected`,\n\t\t\ttype: \"connected\",\n\t\t\tatMs: this._now(),\n\t\t\tpayload: { correlation },\n\t\t});\n\n\t\tif (this._launchedIds.has(executionId)) {\n\t\t\tthrow new RemoteExecutionError(\n\t\t\t\t\"REMOTE_DUPLICATE_LAUNCH\",\n\t\t\t\t`Execution ${executionId} was already launched remotely`,\n\t\t\t\t{ executionId },\n\t\t\t);\n\t\t}\n\t\tthis._launchedIds.add(executionId);\n\n\t\tconst workspaceDir = `${this._remoteTempRoot.replace(/[\\\\/]+$/, \"\")}\\\\jensen-remote-qa\\\\${executionId}`;\n\t\tconst agentDir = `${workspaceDir}\\\\agent`;\n\t\tconst sessionDir = `${agentDir}\\\\child-sessions`;\n\n\t\tconst controller = new AbortController();\n\t\tif (options.signal) {\n\t\t\tif (options.signal.aborted) controller.abort();\n\t\t\telse options.signal.addEventListener(\"abort\", () => controller.abort(), { once: true });\n\t\t}\n\n\t\tconst tracker = new MissionStateTracker(\"CREATED\");\n\t\ttracker.transition(\"QUEUED\");\n\t\ttracker.transition(\"RUNNING\");\n\n\t\tconst remoteLaunch = this._buildRemoteLaunch({ request, workspaceDir, agentDir, sessionDir });\n\n\t\t// Shared-inference admission: issue an execution-scoped token and inject\n\t\t// the admission endpoint into the remote child env (never argv).\n\t\tlet admissionEnv: Record<string, string> = {};\n\t\tif (this._admission) {\n\t\t\tconst { token } = await this._admission.tokenIssuer(executionId);\n\t\t\tadmissionEnv = {\n\t\t\t\tJENSEN_SHARED_INFERENCE_ADMISSION_URL: `http://127.0.0.1:${this._admission.remotePort}`,\n\t\t\t\tJENSEN_SHARED_INFERENCE_TOKEN: token,\n\t\t\t\tJENSEN_SHARED_INFERENCE_EXECUTION_ID: executionId,\n\t\t\t};\n\t\t}\n\n\t\tconst workspaceFiles = (this._workspaceFiles?.(request) ?? []).map((file) => ({\n\t\t\tpath: file.path,\n\t\t\tcontentB64: Buffer.from(file.content, \"utf8\").toString(\"base64\"),\n\t\t}));\n\t\tconst handle = await this._transport.launch(\n\t\t\tthis._target,\n\t\t\t{\n\t\t\t\texecutionId,\n\t\t\t\tlaunchId,\n\t\t\t\tremoteTargetId: this._target.targetId,\n\t\t\t\tfencing: options.fencing,\n\t\t\t\tworkspaceDir,\n\t\t\t\tagentDir,\n\t\t\t\tmodelsJson: this._modelsJson,\n\t\t\t\tchildSessionId: this._childSessionId,\n\t\t\t\tsessionFileContent: this._sessionFileContent,\n\t\t\t\tworkspaceFiles,\n\t\t\t\tlaunch: {\n\t\t\t\t\t...remoteLaunch,\n\t\t\t\t\tcwd: workspaceDir,\n\t\t\t\t\tenv: { ...(remoteLaunch.env ?? {}), ...admissionEnv },\n\t\t\t\t},\n\t\t\t\theartbeatMs: this._heartbeatMs,\n\t\t\t\ttimeoutMs: this._timeoutMs,\n\t\t\t\tevidenceFiles: this._evidenceFiles,\n\t\t\t\tadmissionTunnel: this._admission\n\t\t\t\t\t? { localPort: this._admission.localPort, remotePort: this._admission.remotePort }\n\t\t\t\t\t: undefined,\n\t\t\t\tcorrelation,\n\t\t\t\tcallbacks: {\n\t\t\t\t\tonFrame: (frame) =>\n\t\t\t\t\t\tthis._emitEvent({\n\t\t\t\t\t\t\ttype: \"frame\",\n\t\t\t\t\t\t\tatMs: this._now(),\n\t\t\t\t\t\t\tpayload: {\n\t\t\t\t\t\t\t\teventId: `${request.missionId}:${executionId}:frame:${frame.type}`,\n\t\t\t\t\t\t\t\tcorrelation,\n\t\t\t\t\t\t\t\tframe,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\tonEvent: (event) => this._emitEvent(event),\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ signal: controller.signal },\n\t\t);\n\n\t\tconst active: ActiveMission = {\n\t\t\trequest,\n\t\t\texecutionId,\n\t\t\tlaunchId,\n\t\t\tworkspaceDir,\n\t\t\tagentDir,\n\t\t\ttracker,\n\t\t\tstartedAtMs: this._now(),\n\t\t\tcontroller,\n\t\t\tcorrelation,\n\t\t\thandle,\n\t\t\tcancelled: false,\n\t\t};\n\t\tthis._active.set(request.missionId, active);\n\n\t\treturn createMissionHandle({\n\t\t\tmissionId: request.missionId,\n\t\t\tparentMissionId: request.parentMissionId,\n\t\t\tdepth: request.depth,\n\t\t\texecutionId,\n\t\t\tstate: tracker.state,\n\t\t\tcreatedAtMs: request.createdAtMs,\n\t\t\tstartedAtMs: active.startedAtMs,\n\t\t\tcancel: (reason) => this._cancelActive(active, reason),\n\t\t});\n\t}\n\n\tasync awaitResult(handle: MissionHandle): Promise<MissionResult> {\n\t\tconst active = this._active.get(handle.missionId);\n\t\tif (!active) {\n\t\t\tthrow new Error(`Unknown mission: ${handle.missionId}`);\n\t\t}\n\n\t\tlet outcome: ProcessMissionOutcome;\n\t\tlet raw: import(\"./remote-transport.js\").RemoteTransportOutcome | undefined;\n\t\ttry {\n\t\t\traw = await active.handle.outcomePromise;\n\t\t\tif (raw.errorCode) {\n\t\t\t\tconst knownCode: RemoteExecutionError[\"code\"] = raw.errorCode as RemoteExecutionError[\"code\"];\n\t\t\t\tthrow new RemoteExecutionError(knownCode, `remote transport error: ${raw.errorCode}`, {\n\t\t\t\t\texecutionId: active.executionId,\n\t\t\t\t});\n\t\t\t}\n\t\t\toutcome = {\n\t\t\t\texitCode: raw.exitCode,\n\t\t\t\tstdout: raw.stdout,\n\t\t\t\tstderr: raw.stderr,\n\t\t\t\tlaunchError: raw.launchError,\n\t\t\t\ttimedOut: raw.timedOut,\n\t\t\t};\n\t\t\tactive.finishedAtMs = this._now();\n\t\t} catch (error) {\n\t\t\tactive.finishedAtMs = this._now();\n\t\t\tconst remoteCode = error instanceof RemoteExecutionError ? error.code : undefined;\n\t\t\tif (\n\t\t\t\tremoteCode &&\n\t\t\t\t[\n\t\t\t\t\t\"REMOTE_TARGET_UNAVAILABLE\",\n\t\t\t\t\t\"REMOTE_CONNECT_TIMEOUT\",\n\t\t\t\t\t\"REMOTE_EXECUTION_LOST\",\n\t\t\t\t\t\"REMOTE_HEARTBEAT_TIMEOUT\",\n\t\t\t\t].includes(remoteCode)\n\t\t\t) {\n\t\t\t\tthis._emitEvent({\n\t\t\t\t\ttype: \"transport_error\",\n\t\t\t\t\teventId: `${active.request.missionId}:${active.executionId}:transport_error`,\n\t\t\t\t\tatMs: active.finishedAtMs,\n\t\t\t\t\tpayload: {\n\t\t\t\t\t\teventId: `${active.request.missionId}:${active.executionId}:transport_error`,\n\t\t\t\t\t\terrorCode: remoteCode,\n\t\t\t\t\t\treason: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\tcorrelation: {\n\t\t\t\t\t\t\tmissionId: active.request.missionId,\n\t\t\t\t\t\t\tassignmentId: active.correlation.assignmentId,\n\t\t\t\t\t\t\tattemptId: active.correlation.attemptId,\n\t\t\t\t\t\t\texecutionId: active.executionId,\n\t\t\t\t\t\t\tsessionId: active.correlation.sessionId,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t\t// A structured transport failure (duplicate/launch/lost/timeout) is a\n\t\t\t// hard executor failure, never a fabricated success.\n\t\t\tconst diagnostics = this._diagnostics(undefined);\n\t\t\tconst { failure, state } = this._failureFromTransport(error);\n\t\t\tconst failures: StructuredFailure[] = [failure];\n\t\t\treturn createMissionResult({\n\t\t\t\tmissionId: active.request.missionId,\n\t\t\t\tparentMissionId: active.request.parentMissionId,\n\t\t\t\tdepth: active.request.depth,\n\t\t\t\tstate,\n\t\t\t\texecutionOutcome: state === \"CANCELLED\" ? \"CANCELLED\" : \"CRASHED\",\n\t\t\t\tverification: { status: \"unverified\" },\n\t\t\t\tcompletionDecision: \"unavailable\",\n\t\t\t\tfailures,\n\t\t\t\texecutorDiagnostics: diagnostics,\n\t\t\t\tstartedAtMs: active.startedAtMs,\n\t\t\t\tfinishedAtMs: active.finishedAtMs ?? this._now(),\n\t\t\t});\n\t\t}\n\n\t\tconst { state, executionOutcome, failure, verification, completionDecision, outputText } = await this._classify(\n\t\t\tactive,\n\t\t\toutcome,\n\t\t);\n\n\t\tconst failures: StructuredFailure[] = [];\n\t\tif (failure) failures.push(failure);\n\n\t\treturn createMissionResult({\n\t\t\tmissionId: active.request.missionId,\n\t\t\tparentMissionId: active.request.parentMissionId,\n\t\t\tdepth: active.request.depth,\n\t\t\tstate,\n\t\t\texecutionOutcome,\n\t\t\toutputText,\n\t\t\tevidenceRefs: [],\n\t\t\tverification,\n\t\t\tcompletionDecision,\n\t\t\tfailures,\n\t\t\texecutorDiagnostics: this._diagnostics(raw),\n\t\t\tstartedAtMs: active.startedAtMs,\n\t\t\tfinishedAtMs: active.finishedAtMs ?? this._now(),\n\t\t});\n\t}\n\n\tasync cancel(handle: MissionHandle, reason?: string): Promise<void> {\n\t\tconst active = this._active.get(handle.missionId);\n\t\tif (!active) return;\n\t\tawait this._cancelActive(active, reason);\n\t}\n\n\tprivate async _cancelActive(active: ActiveMission, reason?: string): Promise<void> {\n\t\tactive.cancelled = true;\n\t\tactive.tracker.transition(\"CANCELLED\");\n\t\tactive.controller.abort(reason ?? \"mission cancelled by operator\");\n\t\ttry {\n\t\t\tawait active.handle.cancel(reason);\n\t\t} catch {\n\t\t\t// Best-effort; the fenced terminal write remains the authority.\n\t\t}\n\t}\n\n\tprivate _emitEvent(event: Omit<RemoteTransportEvent, \"eventId\"> & { eventId?: string }): void {\n\t\ttry {\n\t\t\tthis._eventObserver?.({\n\t\t\t\t...event,\n\t\t\t\teventId: event.eventId ?? `remote:${event.type}:${event.atMs}`,\n\t\t\t});\n\t\t} catch {\n\t\t\t// Observers are telemetry only and never alter execution authority.\n\t\t}\n\t}\n\n\tprivate _diagnostics(\n\t\toutcome: import(\"./remote-transport.js\").RemoteTransportOutcome | undefined,\n\t): ExecutorDiagnostics {\n\t\treturn {\n\t\t\texecutorId: this.executorId,\n\t\t\tprocessExitCode: outcome?.exitCode ?? null,\n\t\t\tstderr: (outcome?.stderr ?? \"\").slice(0, 16_000),\n\t\t\tremoteLocation: outcome?.location,\n\t\t\tremoteEvidence: outcome?.evidence,\n\t\t\tremoteTargetId: this._target.targetId,\n\t\t};\n\t}\n\n\tprivate _failureFromTransport(error: unknown): { state: MissionResult[\"state\"]; failure: StructuredFailure } {\n\t\tif (error instanceof RemoteExecutionError) {\n\t\t\tif (error.code === \"REMOTE_DUPLICATE_LAUNCH\") {\n\t\t\t\treturn {\n\t\t\t\t\tstate: \"FAILED\",\n\t\t\t\t\tfailure: { category: \"LAUNCH\", message: error.message, code: error.code },\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (error.code === \"REMOTE_EXECUTION_LOST\" || error.code === \"REMOTE_HEARTBEAT_TIMEOUT\") {\n\t\t\t\treturn {\n\t\t\t\t\tstate: \"FAILED\",\n\t\t\t\t\tfailure: { category: \"EXECUTION\", message: error.message, code: error.code },\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstate: \"FAILED\",\n\t\t\t\tfailure: { category: \"LAUNCH\", message: error.message, code: error.code },\n\t\t\t};\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\treturn { state: \"FAILED\", failure: { category: \"EXECUTION\", message } };\n\t}\n\n\tprivate async _classify(\n\t\tactive: ActiveMission,\n\t\toutcome: ProcessMissionOutcome,\n\t): Promise<{\n\t\tstate: MissionResult[\"state\"];\n\t\texecutionOutcome: MissionResult[\"executionOutcome\"];\n\t\tfailure?: StructuredFailure;\n\t\tverification: MissionResult[\"verification\"];\n\t\tcompletionDecision: MissionResult[\"completionDecision\"];\n\t\toutputText?: string;\n\t}> {\n\t\tconst outputText = outcome.stdout.trim().slice(0, 16_000) || undefined;\n\n\t\tif (active.cancelled) {\n\t\t\treturn {\n\t\t\t\tstate: \"CANCELLED\",\n\t\t\t\texecutionOutcome: \"CANCELLED\",\n\t\t\t\tfailure: { category: \"CANCELLED\", message: \"mission cancelled by operator\" },\n\t\t\t\tverification: { status: \"unverified\" },\n\t\t\t\tcompletionDecision: \"unavailable\",\n\t\t\t\toutputText,\n\t\t\t};\n\t\t}\n\n\t\tconst base = classifyExecutorOutcome(\n\t\t\t{ exitCode: outcome.exitCode, launchError: outcome.launchError, timedOut: outcome.timedOut },\n\t\t\t{ verified: false },\n\t\t);\n\n\t\tconst verifier =\n\t\t\tthis._verifier ??\n\t\t\tbuildRemoteAcceptanceCriteriaVerifier(active.request, {\n\t\t\t\trunner: this._transport,\n\t\t\t\ttarget: this._target,\n\t\t\t\tcwd: active.workspaceDir,\n\t\t\t});\n\n\t\tif (base.state === \"PARTIAL\" && verifier) {\n\t\t\tconst verificationResult = await verifier({ request: active.request, outcome });\n\t\t\tif (verificationResult.verified) {\n\t\t\t\tactive.tracker.transition(\"SUCCEEDED\");\n\t\t\t\treturn {\n\t\t\t\t\tstate: \"SUCCEEDED\",\n\t\t\t\t\texecutionOutcome: \"COMPLETED\",\n\t\t\t\t\tverification: {\n\t\t\t\t\t\tstatus: \"verified\",\n\t\t\t\t\t\tsummary: verificationResult.summary,\n\t\t\t\t\t\tcriterionIds: verificationResult.criterionIds,\n\t\t\t\t\t},\n\t\t\t\t\tcompletionDecision: \"accepted\",\n\t\t\t\t\toutputText,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstate: \"FAILED\",\n\t\t\t\texecutionOutcome: \"COMPLETED\",\n\t\t\t\tfailure: {\n\t\t\t\t\tcategory: \"VERIFICATION\",\n\t\t\t\t\tmessage: verificationResult.summary ?? \"verification failed\",\n\t\t\t\t},\n\t\t\t\tverification: { status: \"failed\", summary: verificationResult.summary },\n\t\t\t\tcompletionDecision: \"rejected\",\n\t\t\t\toutputText,\n\t\t\t};\n\t\t}\n\n\t\tconst state = base.state;\n\t\tif (state !== \"PARTIAL\") active.tracker.transition(state);\n\t\telse active.tracker.transition(\"PARTIAL\");\n\n\t\treturn {\n\t\t\tstate,\n\t\t\texecutionOutcome: base.executionOutcome,\n\t\t\tfailure: base.failure,\n\t\t\tverification: { status: \"unverified\", summary: base.reason },\n\t\t\tcompletionDecision: state === \"SUCCEEDED\" ? \"accepted\" : \"unavailable\",\n\t\t\toutputText,\n\t\t};\n\t}\n}\n"]}