/** * BasePeerRunner — Abstract base class for all peer runners (PRI-302). * * Extracts the shared lease → buildContext → invoke → poll → fetch → * validate → succeed/fail pipeline from 8 duplicated runner implementations. * * Subclasses implement: * - permanentErrorCategories (abstract getter) * - buildContext() — runner-specific context assembly * - invokeRuntime() — runner-specific LLM invocation * - validateOutput() — runner-specific output validation * - succeedTask() — runner-specific artifact commit + task success * * Optional hooks: * - emitSuccessTelemetry() — runner-specific success telemetry (default: no-op) * - checkLineageIntegrity() — lineage strip contract check (default: no-op) * * @see docs/adr/0003-peer-agent-state-machine-orchestration.md * @see Linear PRI-302 */ import type { RuntimeStateManager } from '../store/runtime-state-manager.js'; import type { PDRuntimeAdapter, RunHandle, RunStatus, RuntimeKind } from '../runtime-protocol.js'; import type { StoreEventEmitter } from '../store/event-emitter.js'; import type { PIArtifactStore } from '../internalization/pi-artifact.js'; import type { TaskRecord } from '../task-status.js'; import { type PDErrorCategory } from '../error-categories.js'; import { RunnerPhase } from './runner-phase.js'; import type { PeerRunnerOptions, ResolvedPeerRunnerOptions, PeerRunnerDeps, PeerRunnerConfig, PeerRunnerResult, PeerRunnerValidationResult, FailureContext } from './peer-runner-types.js'; /** * Abstract base class for peer runners. * * TContext — the type returned by buildContext(). Must include contextHash. * TOutput — the type of the validated LLM output. */ export declare abstract class BasePeerRunner { protected readonly stateManager: RuntimeStateManager; protected readonly runtimeAdapter: PDRuntimeAdapter; protected readonly eventEmitter: StoreEventEmitter; protected readonly artifactStore: PIArtifactStore; protected readonly resolvedOptions: ResolvedPeerRunnerOptions; protected readonly config: PeerRunnerConfig; /** * Optional store for agent-authored draft context (Task 12). * When undefined, permanent failures do not write a draft — backward compatible. */ private readonly pendingAgentDraftStore?; private phase; /** * PRI-700 因子 B: 当前 run() 的 leased attempt 序号(每次 run() 在 * buildContext 前刷新)。子类用它区分"同一 attempt 的重复 buildContext" * 与"跨 attempt 重试"——前者不应重复回喂同一 validator 错误,后者允许 * 回喂(每次 output-invalid 后 handleValidationError 已用当次新错误 * 覆盖 lastValidatorErrors,rc-7)。 */ protected currentLeasedAttempt: number | undefined; constructor(deps: PeerRunnerDeps, options: PeerRunnerOptions, config: PeerRunnerConfig); /** Current internal phase. For testing/observability only. */ get currentPhase(): RunnerPhase; protected getRuntimeKind(): RuntimeKind; /** Permanent error categories — runner's inherent property. */ abstract get permanentErrorCategories(): ReadonlySet; /** Build runner-specific context from task and predecessor outputs. */ abstract buildContext(taskId: string): Promise; /** Invoke the runtime with runner-specific prompt builder. */ abstract invokeRuntime(taskId: string, context: TContext): Promise; /** Validate the LLM output. Receives untrusted data — must perform runtime validation. */ abstract validateOutput(output: unknown, taskId: string, context: TContext): Promise; /** Commit artifact + mark task succeeded. Runner-specific commit strategy. */ abstract succeedTask(taskId: string, runId: string, output: TOutput, task: TaskRecord, contextHash: string, context: TContext): Promise>; /** * P0 (verdict drift) — completion-intent resume gate. Called right after * lease acquisition, BEFORE any LLM invocation. A runner whose task carries * a pending durable completion intent in the same revision epoch must * resume that intent's effects and return the result here; returning null * proceeds with the normal LLM pipeline. Only runners with verdict * semantics (evaluator / rollout_reviewer) need to override this. */ protected maybeResumePendingIntent(_taskId: string, _leasedTask: TaskRecord): Promise | null>; /** Emit runner-specific success telemetry. Called after validation passes. */ protected emitSuccessTelemetry(_taskId: string, _output: TOutput, _context: TContext): void; /** * Check lineage strip contract. Called AFTER validation passes. * Receives validated output and context — safe to treat as TOutput / TContext. */ protected checkLineageIntegrity(_taskId: string, _output: TOutput, _context: TContext): void; /** * Transform output after fetch, before validation. * Used by runners that need to re-inject lineage fields stripped by the adapter. * Receives untrusted data — must NOT assume TOutput shape. * * Base implementation overrides `generatedAt` with the actual current timestamp, * because LLM may echo the prompt's example date instead of generating the real time. * Unconditionally sets `generatedAt` — if the LLM omitted it, we add it; if the LLM * echoed a stale date, we replace it. Subclasses should call `super.postFetchTransform()` * to inherit this behavior instead of duplicating the override. */ protected postFetchTransform(_taskId: string, untrustedOutput: unknown, _context: TContext): void; /** * Execute the full peer runner lifecycle for a task. * * Pipeline: lease → resolveRunId → buildContext → invoke → poll → * fetch → validate → succeedTask (abstract) * * Each invocation is independent — no mutable state between run() calls. */ run(taskId: string): Promise>; /** * Emit a telemetry event with the runner's name prefix. * Event type: `{runnerName}_{eventType}` */ protected emitEvent(eventType: string, taskId: string, payload: Record): void; protected pollUntilTerminal(runHandle: RunHandle): Promise; /** * Fetch raw output from the runtime adapter. * * Returns `unknown` — the payload is untrusted LLM/runtime output. * Callers MUST validate before treating as TOutput (ERR-001, ERR-005). */ protected fetchAndParseOutput(runId: string, taskId: string): Promise; /** Resolve artifact IDs from predecessor tasks for lineage tracking. */ protected resolveLineageArtifactIds(taskId: string): Promise<{ ids: string[]; hasRejected: boolean; }>; private resolveStoreRunId; /** Compute a deterministic hash from context references (observability only). */ protected static hashContextRefs(refs: readonly string[]): string; private handleLeaseOrPhaseError; protected handlePostLeaseError(taskId: string, task: TaskRecord, error: unknown): Promise>; private handleRuntimeFailure; protected handleValidationError(ctx: { taskId: string; task: TaskRecord; errors: readonly string[]; errorCategory?: PDErrorCategory; }): Promise>; /** * Core retry-or-fail decision. * * Uses try/catch around markTaskFailed/markTaskRetryWait for robustness. * If state manager operations fail, returns storage_unavailable instead of * propagating the exception (ERR-002: graceful degradation with reason). */ protected retryOrFail(ctx: FailureContext): Promise>; private classifyError; /** * PRI-559 P0-2: 把结构化输出失败详情持久化到 task.diagnosticJson。 * * 与 pi_metadata 信封并列新增 `output_failure_details` 键(不破坏 * parsePITaskMetadata 对 pi_metadata 的严格校验)。失败详情包括: * - schemaRef / provider / model(定位环境) * - validationErrors(具体字段路径 + 错误消息 + 实际值预览) * - repairAttempts / repairSummary / finalFailureReason(修复循环诊断) * - rawOutputPreview(原始输出预览) * - validatorErrors(runner 层 validator 的字符串错误列表) * * 持久化失败不影响主流程(best-effort,记录事件即可)。 */ protected persistOutputFailureDetails(taskId: string, existingDiagnosticJson: string | null | undefined, details: Record, extraTopLevelKeys?: Record): Promise; private mapRunStatusToErrorCategory; protected sleep(ms: number): Promise; /** * ADR-0019: Check if diagnostician LLM rate-limit degradation is enabled. * Reads the `diagnostician_llm_degradation` feature flag from effectiveConfig. * Returns false when effectiveConfig is not provided (legacy behavior). */ protected isDegradationEnabled(): boolean; /** * Task 12: Construct an AgentDraftPayload from the permanent-failure * context and write it to pending_agent_drafts via the injected * PendingAgentDraftStore. Best-effort: never throws. All failures are * observed via telemetry (rc-9: no silent fallback) so the maintainer * can diagnose draft-write issues without the markFailed terminal-state * contract being broken. * * No-op when pendingAgentDraftStore is not injected (backward compatible). * * ERR checklist: * - EP-01 / ERR-001, ERR-005, ERR-013: diagnosticJson parsed as unknown, * narrowed with typeof + Object.hasOwn. No `as` casts on parsed data. * - EP-03 / ERR-002: insertPendingDraft returns { ok: false, error }; * we emit a telemetry event with reason + nextAction (rc-9). * - EP-03 / ERR-074, ERR-089: every branch (store missing, insert * returns !ok, insert throws) applies the SAME best-effort contract — * never propagate, always observe. * - EP-08 / ERR-003, ERR-024: redactAbsolutePaths / redactTokenLikeValues * / redactEnvLikeValues are applied to observedFailure BEFORE persist. */ private injectAgentDraftOnPermanentFailure; } //# sourceMappingURL=base-peer-runner.d.ts.map