/** * DB-persisted, per-agent goal CRUD store (Layer 4 of SG-COGNITIVE-SUBSTRATE). * * Persists {@link GoalRecord}s in the `tasks_goal` table inside `tasks.db` * (Pattern A — opened through the canonical `getDb` chokepoint, never a raw * `new DatabaseSync`). Every read/write is keyed by the resolved per-agent * identity from E0 (`resolveSessionIdFromEnv` / `resolveAgentIdFromEnv`) so two * concurrent agents never collide on one global row — the session-bleed class * this saga exists to kill. * * JSONB discipline: `goal_kind`, `criteria`, and `last_verdict` are JSONB BLOB * columns. Writes go through the {@link jsonb} customType's `toDriver` (wraps in * the SQL `jsonb()` constructor); whole-value reads MUST project `json(col)` * (see {@link jsonbText}) because the raw-BLOB read path is intentionally * rejected — the on-disk JSONB encoding is version-unstable. * * @module @cleocode/core/goal/store * * @epic T11290 EP-CLEO-GOAL-SYSTEM * @task T11377 * @saga T11283 SG-COGNITIVE-SUBSTRATE */ import type { GoalJudgeVerdict, GoalKind, GoalRecord, GoalStatus } from '@cleocode/contracts'; /** * The resolved per-agent ownership key for a goal row. * * Both fields come from E0's env-first resolvers. When spawn injected no * session/agent identity they are `null` (the global-scope case), which still * isolates correctly because every concurrent agent in a shell DOES carry its * own injected identity. * * @task T11377 */ export interface GoalOwner { /** Resolved session id (`resolveSessionIdFromEnv`), or `null`. */ readonly sessionId: string | null; /** Resolved agent handle (`resolveAgentIdFromEnv`), or `null`. */ readonly agentId: string | null; } /** * Parameters for {@link createGoal}. * * @task T11377 */ export interface CreateGoalParams { /** The goal-kind discriminator + payload (task-completion vs fuzzy). */ readonly goalKind: GoalKind; /** Human-readable intent statement. */ readonly intent: string; /** Hard turn cap before the loop abandons the goal. */ readonly turnBudget: number; /** Initial acceptance criteria (defaults to empty). */ readonly criteria?: readonly string[]; /** Parent goal id when this is a sub-goal. */ readonly parentGoalId?: string | null; /** * Explicit owner override. When omitted, the owner is resolved from the * environment via {@link resolveGoalOwner}. Tests pass an explicit owner to * simulate two distinct agents without mutating `process.env`. */ readonly owner?: GoalOwner; /** * Idempotency key. When omitted a UUID is generated. Supplying a stable key * makes a re-issued create a no-op (the row already exists). */ readonly idempotencyKey?: string; } /** * Mutable fields accepted by {@link updateGoal}. * * `criteria` is replaced wholesale here; use {@link appendCriteria} for the * append-only path. All fields are optional — only the provided ones are * written, and `updated_at` is always refreshed. * * @task T11377 */ export interface UpdateGoalFields { readonly status?: GoalStatus; readonly turnsUsed?: number; readonly pausedReason?: string | null; readonly lastVerdict?: GoalJudgeVerdict | null; readonly criteria?: readonly string[]; } /** * Resolve the current per-agent goal owner from the environment (E0). * * Reads `CLEO_SESSION_ID` (+ precedence) and `CLEO_AGENT_ID` via the canonical * env-first resolvers so the goal store keys identity the SAME way every other * hot path does. Never reads the DB. * * @returns The resolved {@link GoalOwner}. * @task T11377 */ export declare function resolveGoalOwner(): GoalOwner; /** * Create a new goal, persisted in `tasks.db` and keyed to the resolved * per-agent owner. * * Idempotent on the primary key: a re-issued create with the same * `idempotencyKey` coalesces via `onConflictDoNothing` and returns the existing * row rather than inserting a duplicate. * * @param params - Goal creation parameters. * @param cwd - Project root override (defaults to the resolved CLEO project). * @returns The created (or pre-existing) {@link GoalRecord}. * @task T11377 */ export declare function createGoal(params: CreateGoalParams, cwd?: string): Promise; /** * Load a single goal by its id (idempotency key / primary key). * * @param id - The goal id. * @param cwd - Project root override. * @returns The {@link GoalRecord}, or `null` when absent. * @task T11377 */ export declare function getGoalById(id: string, cwd?: string): Promise; /** * Get the most-recently-updated ACTIVE or PAUSED goal owned by an agent. * * This is the dominant lookup ("what is THIS agent working on?") and is served * by the `idx_tasks_goal_owner_active` index. Owner defaults to the * environment-resolved identity so concurrent agents see their OWN goal — never * each other's. Terminal goals (satisfied/abandoned/impossible) are excluded; * an agent's active goal is the live one it can still advance. * * @param cwd - Project root override. * @param owner - Explicit owner override (defaults to {@link resolveGoalOwner}). * @returns The active/paused {@link GoalRecord}, or `null` when none. * @task T11377 */ export declare function getActiveGoal(cwd?: string, owner?: GoalOwner): Promise; /** * List all goals owned by an agent, newest first. * * @param cwd - Project root override. * @param owner - Explicit owner override (defaults to {@link resolveGoalOwner}). * @returns Every {@link GoalRecord} for the owner, ordered by `updatedAt` desc. * @task T11377 */ export declare function listGoals(cwd?: string, owner?: GoalOwner): Promise; /** * Update mutable fields of a goal and refresh `updated_at`. * * Only the provided fields are written. Returns the updated record (re-read so * JSONB columns round-trip through `json(col)`), or `null` if the goal is gone. * * @param id - The goal id. * @param fields - The fields to update. * @param cwd - Project root override. * @returns The updated {@link GoalRecord}, or `null` when the goal is absent. * @task T11377 */ export declare function updateGoal(id: string, fields: UpdateGoalFields, cwd?: string): Promise; /** * Append a single criterion to a goal's criteria array (append-only). * * Reads the current criteria, appends, and writes the whole array back. The * read round-trips through `json(col)` so no raw BLOB is ever parsed. * * @param id - The goal id. * @param criterion - The criterion text to append. * @param cwd - Project root override. * @returns The updated {@link GoalRecord}, or `null` when the goal is absent. * @task T11377 */ export declare function appendCriteria(id: string, criterion: string, cwd?: string): Promise; //# sourceMappingURL=store.d.ts.map