/** * Leased agent claims on tasks — lease arithmetic and the typed refusal (T12502). * * A claim is four columns on `tasks_tasks` (`claimed_by_session`, * `claimed_by_agent`, `claimed_at`, `lease_expires_at`), separate from the * human `assignee`. It is written ONLY through the T12503 field chokepoint * (`updateTaskFields`) with a `TaskClaimGuard`, which turns the claim into a * compare-and-set in the UPDATE's WHERE clause (see * `sqlite-data-accessor.ts#claimPredicate`). This module holds what the * chokepoint and its callers share: * * - the lease TTL ({@link taskClaimLeaseMs}) and expiry * ({@link leaseExpiresAtFrom}); * - the row → {@link TaskClaim} mapping ({@link claimFromColumns}); * - the `E_TASK_CLAIMED` error ({@link taskClaimedError}), which names the * holder, the lease expiry and the one explicit, audited override. * * Lifecycle: taken by `cleo start` / `cleo claim` / spawn; renewed by the * holder session's mutations (dispatch heartbeat) or `cleo claim --renew`; * released by `cleo stop`, `cleo unclaim`, and — via SQL triggers — by the * task reaching done / cancelled / archived and by the holder session ending * or being deleted. An expired lease is never taken silently: `--take-over`. * * @task T12502 * @epic T12497 */ import type { TaskClaim, TaskClaimMode } from '@cleocode/contracts'; import { CleoError } from '../errors.js'; /** Default claim lease: 30 minutes, renewed by the holder's activity. */ export declare const DEFAULT_TASK_CLAIM_LEASE_MS: number; /** * Environment override for the lease length, in whole minutes. Values that * are not positive integers are ignored. */ export declare const TASK_CLAIM_LEASE_ENV = "CLEO_CLAIM_LEASE_MINUTES"; /** * Resolve the claim lease length in milliseconds. * * @returns `CLEO_CLAIM_LEASE_MINUTES` in ms when set to a positive integer, * else {@link DEFAULT_TASK_CLAIM_LEASE_MS}. * @example * ```ts * const expires = leaseExpiresAtFrom(now, taskClaimLeaseMs()); * ``` */ export declare function taskClaimLeaseMs(): number; /** * Compute a lease expiry. * * @param now - ISO-8601 UTC instant the lease starts or is renewed. * @param leaseMs - Lease length in milliseconds. * @returns The ISO-8601 UTC expiry instant. * @example * ```ts * leaseExpiresAtFrom('2026-09-29T00:00:00.000Z', 60_000); // '2026-09-29T00:01:00.000Z' * ``` */ export declare function leaseExpiresAtFrom(now: string, leaseMs: number): string; /** The claim columns as stored on a task row. */ export interface TaskClaimColumns { /** `claimed_by_session`. */ claimedBySession: string | null; /** `claimed_by_agent`. */ claimedByAgent: string | null; /** `claimed_at`. */ claimedAt: string | null; /** `lease_expires_at`. */ leaseExpiresAt: string | null; } /** * Map stored claim columns to a {@link TaskClaim}. * * @param row - The claim columns of a task row. * @returns The lease, or `undefined` when the task is unclaimed. * @example * ```ts * const claim = claimFromColumns(row); // undefined for an unclaimed task * ``` */ export declare function claimFromColumns(row: TaskClaimColumns): TaskClaim | undefined; /** * The claim columns for a loaded task's lease (all `null` when unclaimed). * * @param claim - The lease on a task, if any. * @returns The four claim columns. * @example * ```ts * claimColumnsOf(task.claim).claimedBySession; * ``` */ export declare function claimColumnsOf(claim: TaskClaim | undefined): TaskClaimColumns; /** * Whether a lease has lapsed at `now`. A lease with no recorded expiry * counts as lapsed (it can never be renewed into validity by its holder * anyway, and must not block forever). * * @param claim - The stored lease. * @param now - ISO-8601 UTC instant to compare against. * @returns `true` when `leaseExpiresAt <= now`. * @example * ```ts * isClaimExpired(holder, new Date().toISOString()); * ``` */ export declare function isClaimExpired(claim: TaskClaim, now: string): boolean; /** * Whether a claim write in `mode` by `sessionId` may replace the stored lease. * This is the JavaScript mirror of the SQL predicate; the chokepoint uses the * SQL form for the write and this form only to explain a refusal. * * @param stored - The lease on the row, or `undefined` when unclaimed. * @param mode - The claim mode. * @param sessionId - The caller's session (`null` when unbound). * @param now - ISO-8601 UTC instant for the expiry comparison. * @param allowedHolders - Sessions whose lease `acquire` / `take-over` may take * even while live: the spawn hand-off source and the caller's spawn-edge * parent and children (`tasks_sessions.parent_session_id`). * @returns `true` when the write is allowed. * @example * ```ts * claimAllows(stored, 'acquire', 'ses_a', now); // false when ses_b holds it * ``` */ export declare function claimAllows(stored: TaskClaim | undefined, mode: TaskClaimMode, sessionId: string | null, now: string, allowedHolders?: readonly string[]): boolean; /** * Build the `E_TASK_CLAIMED` refusal. * * @param taskId - The claimed task. * @param holder - The lease stored on the task. * @param requester - The refused caller. * @param now - ISO-8601 UTC instant used for the expiry comparison. * @param verb - The refused command, for the fix hint (`start`, `claim`, …). * @returns A {@link CleoError} with `ExitCode.TASK_CLAIMED` (LAFS * `E_TASK_CLAIMED`) and {@link TaskClaimedDetails} as `details`. * @example * ```ts * throw taskClaimedError('T1', holder, { sessionId: 'ses_b', agentId: null }, now); * ``` */ export declare function taskClaimedError(taskId: string, holder: TaskClaim, requester: { sessionId: string | null; agentId: string | null; }, now: string, verb?: string): CleoError; //# sourceMappingURL=task-claim.d.ts.map