import { type PlanOperation } from "../agent/task-plan.js"; export type TaskState = "pending" | "in_progress" | "done" | "failed" | "skipped"; export type PlanStatus = "draft" | "approved" | "in_progress" | "completed" | "abandoned"; /** Durable, task-scoped facts from successful work. These survive compaction and resume. */ export interface TaskEvidence { successWorkCount: number; lastOkTool?: string | undefined; sawSourceWrite?: boolean | undefined; sawFeatureWrite?: boolean | undefined; sawInstallOk?: boolean | undefined; sawScaffoldOk?: boolean | undefined; /** Local app: shell.start / npm run dev. */ sawDevServerStart?: boolean | undefined; /** Local app: successful localhost HTTP probe. */ sawLocalHttpProbeOk?: boolean | undefined; /** Local app: job log shows ready + URL (Vite/Next/etc.). */ sawServerReady?: boolean | undefined; /** Local app: port LISTEN evidence (lsof/ss). */ sawPortListening?: boolean | undefined; /** Pentest: successful remote recon tool against a target. */ sawRemoteReconOk?: boolean | undefined; /** Pentest: active test / exploit-style tool against a target. */ sawRemoteActiveTestOk?: boolean | undefined; } export interface PlanTask { id: string; title: string; state: TaskState; note?: string | undefined; /** Successful task-scoped evidence, persisted with the plan for resume safety. */ evidence?: TaskEvidence | undefined; /** Model-supplied slugs (id/name) that resolve to this task via task.update. */ aliases?: string[] | undefined; dependencies?: string[] | undefined; resourceLocks?: string[] | undefined; supersededBy?: string | undefined; /** Display hierarchy only; dependency order remains explicit in dependencies. */ parentTaskId?: string | undefined; /** Durable background process linked to this task. */ jobId?: string | undefined; processId?: number | undefined; /** Responder-owned tasks advance from job lifecycle events, not task.update. */ responderOwned?: boolean | undefined; /** * Stable delegation identity created *before* the process launches, * so a fast-exiting job can always be reconciled to its child task even if * linking or the plan save lost a race. */ delegationId?: string | undefined; /** Authoritative result revision this responder child was settled from. */ settledResultRevision?: number | undefined; } /** Durable side-channel facts that survive compaction/resume. */ export interface PlanMeta { projectRoot?: string | undefined; packageManager?: string | undefined; devCommand?: string | undefined; } export interface SessionPlan { schemaVersion?: 2 | undefined; version?: number | undefined; sessionId: string; goal: string; detail: string; tasks: PlanTask[]; status: PlanStatus; kind: string; createdAt: string; updatedAt: string; meta?: PlanMeta | undefined; } /** True when a title is only a bare checklist id (t1, t2, …), not real work. */ export declare function isBareTaskIdTitle(title: string): boolean; /** * Drop phantom tasks whose title is just `t1`/`t2`/… (models sometimes * interleave bare ids with real titles). Keeps real task ids stable so * in-flight task.update calls still resolve. */ export declare function stripBareTaskIdTasks(tasks: PlanTask[]): PlanTask[]; export declare function tasksFromTitles(titles: string[]): PlanTask[]; /** * Heal dependency edges without inventing scheduling. * * Only genuinely broken edges are removed: self-references, ids that no longer * exist, and edges that would close a cycle. Valid forward references are kept * so an authored DAG keeps its parallelism. `dependencies: []` is an explicit * statement of independence and is never replaced; only a legacy row with no * dependency field at all falls back to the previous foreground task, and a * responder child never becomes a blocker. * * Returns true when any task's dependencies changed. */ export declare function normalizeTaskDependencies(tasks: PlanTask[]): boolean; /** * Collapse whitespace and shorten an overlong plan goal to a title. * Models sometimes echo the user's full multi-clause request as the goal. * Prefer cutting at a natural boundary (sentence end, then comma/paren/dash) * so the result still reads as a sensible phrase — never a mid-word or * mid-clause fragment with a dangling ellipsis. */ export declare function shortenPlanGoal(raw: string): string; export declare function createPlan(input: { sessionId: string; goal: string; detail: string; taskTitles: string[]; kind?: string | undefined; meta?: PlanMeta | undefined; }): SessionPlan; export declare function patchPlanMeta(plan: SessionPlan, patch: PlanMeta): SessionPlan; /** * Persist unconditionally (no version check). Used for whole-plan replacement * (creation, approval of a freshly built plan, migrations). Concurrent * transitions must use {@link mutatePlan} instead. */ export declare function savePlan(plan: SessionPlan): Promise; export interface PlanMutationResult { ok: boolean; /** Persisted plan after the reducer ran. */ plan?: SessionPlan | undefined; /** Why the mutation did not apply. */ reason?: "missing-plan" | "version-conflict" | "no-change" | "invalid" | "persist-failed" | "private-mode" | undefined; /** Invariant repairs applied while committing. */ repairs?: string[] | undefined; } /** * The authoritative plan mutation boundary. * * Loads the plan fresh, runs `reducer` on it, enforces domain invariants, then * persists with a version compare-and-set. On a CAS conflict the reducer is * re-run against the newer state (reducers must therefore be idempotent and * expressed as "apply this transition", not "write this snapshot"). * * Return `false` from the reducer to abort without writing. */ export declare function mutatePlan(sessionId: string, reducer: (draft: SessionPlan) => boolean | void, opts?: { expectedVersion?: number | undefined; retries?: number | undefined; }): Promise; export declare function loadPlan(sessionId: string): Promise; export declare function deletePlan(sessionId: string): Promise; export declare function clearAllPlans(): Promise; export declare function applySessionPlanOperation(plan: SessionPlan, operation: PlanOperation): SessionPlan; export declare function validateSessionPlan(plan: SessionPlan): { ok: true; } | { ok: false; reason: string; }; export declare function nextPlanTaskId(tasks: readonly Pick[]): string; export declare function appendPlanTask(plan: SessionPlan, input: Omit & { id?: string | undefined; }): PlanTask; export declare function readyPlanTasks(plan: SessionPlan): PlanTask[]; /** Foreground (non-responder) tasks that are currently `in_progress`. */ export declare function activeForegroundTasks(plan: SessionPlan): PlanTask[]; /** * `count(foreground tasks in_progress) <= 1` is a domain invariant, * not a prompt convention. Applied on every {@link mutatePlan} commit and on * load. The earliest dependency-valid active task is kept; later ones are * demoted to `pending` with a repair note (evidence is preserved). * * A parent/child display relationship never implies a dependency, so responder * children may run concurrently and are ignored here. */ export declare function enforcePlanInvariants(plan: SessionPlan): string[]; /** * Apply a foreground-authored plan snapshot onto fresh state. * * Whole-plan writes (plan.create, revisions, task.add reordering) are authored * against a loaded copy. Replacing the stored plan with that copy dropped * anything an asynchronous writer changed in the meantime. This applies the * snapshot's foreground intent while treating responder children as owned by * process settlement: * * - responder-owned rows keep their stored state/note/job linkage; * - responder children created concurrently are retained; * - stored evidence is kept when the snapshot has none for that task. */ export declare function applyForegroundSnapshot(draft: SessionPlan, snapshot: SessionPlan): void; /** * Apply a task transition. Rejects transitions the table forbids so no * caller can rewind terminal work; use a plan revision to supersede a task. */ export declare function markTask(plan: SessionPlan, taskId: string, state: TaskState, note?: string | undefined): boolean; /** Mark the first not-yet-finished foreground task as the given state. */ export declare function markNextTask(plan: SessionPlan, state: TaskState): PlanTask | undefined; /** Tasks the model owns. Responder children advance from process lifecycle. */ export declare function foregroundTasks(plan: SessionPlan): PlanTask[]; /** Foreground work that is neither settled nor skipped, in plan order. */ export declare function foregroundRemaining(plan: SessionPlan): PlanTask[]; /** The single active foreground task, or the next one to resume. */ export declare function foregroundActiveTask(plan: SessionPlan): PlanTask | undefined; /** Responder children that are still running or awaiting analysis. */ export declare function responderOpenTasks(plan: SessionPlan): PlanTask[]; export declare function planProgress(plan: SessionPlan): { done: number; total: number; }; export declare function isPlanTerminal(plan: SessionPlan): boolean; export declare function isPlanSuccessful(plan: SessionPlan): boolean; /** @deprecated Use isPlanTerminal or isPlanSuccessful explicitly. */ export declare function isPlanComplete(plan: SessionPlan): boolean;