/** * Ticket work guard: an owner request is a tracked deliverable. * * When an agent ends its turn and a ticket assigned to it has no WorkItem, * the harness creates one — assigned to that agent, linked to the ticket, * with the owner's words as the brief. The WorkItem is what the active-work * briefing, the idle-stop check and the reconciler's redelivery all look at; * without it a promise to the owner lives only in the agent's context * (2026-10-10: "make three videos" was never a deliverable; one attempt was * made, the owner asked 2h45m later). * * A request that lists several deliverables also gets a checklist on the * ticket (its acceptance criteria), so partial delivery shows as "1 of 3". * * @module services/v3/ticket-work-guard * @see specs/2026-10-10-agent-follow-through.md */ import type { Request } from '../../types/v2/request.types.js'; import { type WorkItem } from '../../types/v2/work-item.types.js'; /** Metadata marker on WorkItems this guard created. */ export declare const TICKET_FOLLOW_THROUGH_MARKER = "ticket-follow-through"; /** Tickets older than this are not picked up (a stale ticket is not "just asked") */ export declare const TICKET_WORK_MAX_AGE_MS: number; /** What the guard needs from the rest of the backend. */ export interface TicketWorkGuardDeps { listTickets: () => Promise; listWorkItems: () => Promise; /** Add to the pool (links the WorkItem to its Request, stamps the owner thread) */ addWorkItem: (workItem: WorkItem, creatorSession: string) => Promise; /** Write the checklist onto the ticket */ setChecklist: (ticketId: string, items: ReadonlyArray<{ text: string; }>) => Promise; /** Whether the agent holds an unfulfilled stated intent */ holdsIntent: (agent: string) => boolean; /** When the agent last posted a non-intent reply to the owner */ lastRealReplyAt: (agent: string) => number | undefined; /** The tickets' team, to route the WorkItem like other team work */ teamIdOf?: (agent: string) => string | undefined; now?: () => number; } /** Result for one ticket the guard acted on. */ export interface TicketWorkCreated { ticketId: string; workItemId: string; checklist: number; } /** * The brief an agent gets for a ticket the harness turned into work. * * @param ticket - The ticket * @param deliverables - Checklist items, when the request lists several * @returns Markdown brief (internal: never shown to the owner) */ export declare function buildTicketWorkBrief(ticket: Request, deliverables: readonly string[]): string; /** * Create a WorkItem for each open ticket assigned to `agent` that has none * and is not already answered. * * A ticket is skipped when it is a pure question, needs no review (cron / * mission / communication), is older than a day, is not open, already has a * WorkItem (linked or in the pool), or was answered with a real reply and * lists only one deliverable and the agent holds no unfulfilled promise. * * @param agent - The agent whose turn ended * @param deps - Seams * @returns What was created */ export declare function ensureTicketWorkItems(agent: string, deps: TicketWorkGuardDeps): Promise; /** * "N of M delivered" for a ticket's checklist (criteria the agent marked * `pass`), or null when it lists fewer than two. * * @param ticket - The ticket * @returns Counts, or null */ export declare function checklistProgress(ticket: Pick): { delivered: number; total: number; } | null; //# sourceMappingURL=ticket-work-guard.d.ts.map