/**
 * Active Work Briefing Service
 *
 * Generates an authoritative-state briefing for an agent at session startup
 * (and on-demand mid-session via the `core/get-my-active-work` skill).
 *
 * Today the agent recovery prompt is memory-only — `SessionMemoryService`
 * dumps the last session summary, agent/project knowledge, daily logs, and
 * recent learning files. That is supplementary; it does NOT capture
 * authoritative runtime state. Symptom: an agent restarts amnesiac about
 * Requests it owes a reply on, queued WorkItems that survived the restart,
 * and in-flight delegations.
 *
 * This service mirrors `SessionMemoryService` (`generate*` + `format*`
 * methods, singleton, soft-fail wrapping at the call site) and produces a
 * per-agent slice of the live Request + WorkItem state. The briefing is
 * prepended ABOVE the memory briefing in the agent's startup prompt so the
 * first thing the agent reads is "what am I on the hook for RIGHT NOW".
 *
 * Sources:
 * - {@link RequestService.listAll} — all Requests on disk
 * - {@link TaskPoolService.getAllItems} — all WorkItems in the pool
 *
 * Design doc: `/tmp/agent-state-recovery-design.md` (architect output)
 * GitHub issue: stevehuang0115/crewly#395
 *
 * @module services/agent/active-work-briefing.service
 */
import type { Request, RequestStatus } from '../../types/v2/request.types.js';
import type { WorkItem, WorkItemStatus } from '../../types/v2/work-item.types.js';
/**
 * Narrow projection of `RequestService` used by the briefing — only
 * `listAll` is needed. Defining the contract here lets the test inject a
 * fake without dragging in the full RequestService surface.
 */
interface RequestProvider {
    listAll(): Promise<Request[]>;
}
/**
 * Narrow projection of `TaskPoolService` used by the briefing — only
 * `getAllItems` is needed.
 */
interface TaskPoolProvider {
    getAllItems(): Promise<WorkItem[]>;
}
/**
 * Per-section item cap shape used both for {@link DEFAULT_CAPS} and for
 * {@link GenerateOptions.caps} overrides. Kept as a plain `number` so the
 * orchestrator multiplier path can return the multiplied value without
 * confusing TypeScript with literal types.
 */
export interface BriefingCaps {
    openRequests: number;
    activeWorkItems: number;
    pendingReviews: number;
    outboundDelegations: number;
    recentlyAutoResolved: number;
}
/** Priority value used for sorting briefing rows. */
export type BriefingPriority = 'high' | 'normal' | 'low';
/**
 * One Request row in the briefing.
 */
export interface OpenRequestRow {
    id: string;
    title: string;
    status: RequestStatus;
    priority: BriefingPriority;
    ageHours: number;
    source?: string;
}
/**
 * One WorkItem row in the briefing.
 */
export interface ActiveWorkItemRow {
    id: string;
    title: string;
    status: WorkItemStatus;
    ageHours: number;
    priority: BriefingPriority;
    requestId?: string;
    description?: string;
}
/**
 * Pending review row — a WorkItem in `done_by_worker` awaiting this agent's
 * verification.
 */
export interface PendingReviewRow {
    id: string;
    title: string;
    ageHours: number;
    claimedBy?: string;
}
/**
 * Outbound delegation row — a WorkItem this agent delegated, still in-flight.
 */
export interface OutboundDelegationRow {
    id: string;
    title: string;
    status: WorkItemStatus;
    ageHours: number;
    target?: string;
}
/**
 * Recently auto-resolved row — closed by an SLA / system action within the
 * configured window. Surfaces "this just closed while you were down".
 */
export interface RecentlyResolvedRow {
    id: string;
    title: string;
    resolvedAt: string;
    resolvedReason?: string;
}
/**
 * Per-section truncation marker. When a section exceeds its cap, the
 * service emits the top N rows + a marker telling the agent how many were
 * dropped and how to fetch the rest.
 */
export interface SectionTruncation {
    /** Total candidate items before the cap was applied. */
    totalCandidates: number;
    /** Per-section cap that was applied. */
    cap: number;
    /** True if any rows were dropped. */
    truncated: boolean;
}
/**
 * Briefing payload. Pure state, no formatting.
 */
export interface ActiveWorkBriefing {
    openRequests: OpenRequestRow[];
    activeWorkItems: ActiveWorkItemRow[];
    pendingReviews: PendingReviewRow[];
    outboundDelegations: OutboundDelegationRow[];
    recentlyAutoResolved: RecentlyResolvedRow[];
    /** Per-section truncation metadata. */
    truncation: {
        openRequests: SectionTruncation;
        activeWorkItems: SectionTruncation;
        pendingReviews: SectionTruncation;
        outboundDelegations: SectionTruncation;
        recentlyAutoResolved: SectionTruncation;
    };
    /** Aggregate truncation flag (any section truncated). */
    truncated: boolean;
    /** Snapshot timestamp. */
    generatedAt: string;
    /**
     * Raw counts before per-section caps. Useful for the agent to know
     * the system-wide load.
     */
    totalCounts: {
        requests: number;
        workItems: number;
    };
}
/**
 * Optional generation overrides — used by tests and by the skill endpoint
 * for fine-grained control.
 */
export interface GenerateOptions {
    /** Override the per-section caps. Useful for tests. */
    caps?: Partial<BriefingCaps>;
    /** Override the recently-resolved window in milliseconds. */
    recentlyResolvedWindowMs?: number;
    /** Override "now" for deterministic tests. */
    nowMs?: number;
}
/**
 * Memory hints map — caller passes keys like `wi:<id>` or `req:<id>`
 * mapped to a short note string. The formatter cross-references this map
 * when emitting each row and appends `(memory: <note>)` to the row line.
 *
 * Keys are namespaced so `req:` and `wi:` cannot collide.
 */
export type MemoryHints = ReadonlyMap<string, string>;
/**
 * Singleton service producing per-agent active-work briefings.
 *
 * Mirrors the `SessionMemoryService.generate* + format*` shape so the
 * `agent-registration.service.ts` glue can call them in parallel and
 * concatenate the markdown.
 *
 * Stateless beyond singleton bookkeeping — every call hits the live
 * `RequestService` + `TaskPoolService` instances.
 */
export declare class ActiveWorkBriefingService {
    private readonly requestServiceFactory;
    private readonly taskPoolFactory;
    private static instance;
    /**
     * Memoised promise of the in-flight async `getInstance()` resolution.
     *
     * We cache the *promise* (not the resolved value) so that concurrent
     * callers awaiting `getInstance()` during the cold-start window all
     * land on the same async resolution — only ONE pair of dynamic
     * `import()` calls runs, even under heavy parallelism. Once resolved,
     * subsequent `await getInstance()` calls are microtask-only overhead
     * (the promise is already settled, so `await` resolves on the next
     * microtask without re-entering the import pipeline).
     *
     * Cleared by {@link resetInstance} for tests so each test can
     * independently re-load the dynamic imports.
     */
    private static instancePromise;
    private readonly logger;
    /**
     * Private constructor — use {@link getInstance}. The `requestServiceFactory`
     * and `taskPoolFactory` are dependency seams for tests; production code
     * lets {@link getInstance} provide the live singletons.
     */
    private constructor();
    /**
     * Returns the singleton instance.
     *
     * **Async** — the `RequestService` and `TaskPoolService` modules are
     * loaded LAZILY via ESM dynamic `import()` so that consumers of this
     * module do not pay the import cost — and more importantly, do not
     * transitively load `chokidar` via `v3-data.service.ts` — until they
     * actually call `getInstance()`. This keeps the agent-registration
     * test suite mockable without forcing every caller to mock the v3
     * pipeline.
     *
     * The async contract was introduced by the build-fix that succeeded
     * PR #494 (`fa05720e`). PR #494 used CJS `require` via a
     * `createRequire` shim anchored to `process.argv[1]`; that anchor
     * worked for bare module names (Node walks up to `node_modules/`)
     * but BROKE relative-path resolution — symptom: this method threw
     * `Cannot find module '../v3/request.service.js'` for every
     * agent registration that flowed through here. The fix replaces
     * `require('<relative-path>')` with `await import('<relative-path>')`
     * which resolves correctly from this file under both ESM (production)
     * and ts-jest's CJS transpile (jest treats dynamic `import()` like a
     * resolvable function).
     *
     * **Promise memoization**: the resolved promise is cached so concurrent
     * cold-start callers all land on the same async resolution — only ONE
     * pair of dynamic imports runs even under heavy parallelism. Once
     * resolved, repeat `await getInstance()` calls are microtask-only
     * overhead.
     *
     * @returns Promise resolving to the singleton instance
     */
    static getInstance(): Promise<ActiveWorkBriefingService>;
    /**
     * Resets the singleton — for tests only.
     *
     * Clears BOTH the resolved instance AND the memoised in-flight
     * promise so the next `await getInstance()` re-runs the dynamic
     * imports cleanly (e.g. picks up freshly-mocked modules).
     */
    static resetInstance(): void;
    /**
     * Test-only escape hatch to swap the underlying service factories.
     * Production code never calls this; the public surface is the singleton.
     *
     * @param requestServiceFactory - Returns a `listAll` provider
     * @param taskPoolFactory - Returns a `getAllItems` provider
     */
    static createForTest(requestServiceFactory: () => RequestProvider, taskPoolFactory: () => TaskPoolProvider): ActiveWorkBriefingService;
    /**
     * Compute the effective per-section caps for the requested role. Orchestrator
     * gets caps × 3 for system-wide visibility; everyone else uses the defaults
     * (with optional overrides from {@link GenerateOptions.caps}).
     *
     * @param role - Agent role (orchestrator gets the multiplier)
     * @param overrides - Optional explicit caps that win over both defaults and
     *   the role multiplier.
     * @returns Effective caps for each section
     */
    private resolveCaps;
    /**
     * Convert a 'low' | 'normal' | 'high' priority to a sort key.
     * Higher number = higher priority (sorted DESC).
     *
     * @param p - Priority value
     * @returns Numeric sort key
     */
    private priorityRank;
    /**
     * Compute age in hours since `createdAt`. Negative or NaN inputs are
     * coerced to 0 — defensive against malformed timestamps.
     *
     * @param createdAt - ISO8601 timestamp
     * @param nowMs - Current time in milliseconds
     * @returns Age in hours (>= 0)
     */
    private ageHours;
    /**
     * Read a string-typed field from a `metadata` blob, or null if missing /
     * not a string. WorkItem.metadata is typed as `Record<string, unknown>` so
     * every read needs this guard.
     *
     * @param metadata - WorkItem.metadata or undefined
     * @param key - Field name to read
     * @returns Trimmed string value or null
     */
    private readMetadataString;
    /**
     * Apply the per-section cap. Returns the slice + a {@link SectionTruncation}
     * object that the formatter renders as a "... and X more" marker.
     *
     * @param items - Sorted candidate rows
     * @param cap - Per-section cap
     * @returns Truncation metadata + the kept rows
     */
    private applyCap;
    /**
     * Project Requests visible to this agent. Non-orchestrator agents only see
     * Requests where they are the `ownerAgent`; the orchestrator sees all open
     * Requests system-wide.
     *
     * @param requests - All Requests from {@link RequestService.listAll}
     * @param sessionName - Agent session name
     * @param role - Agent role
     * @param nowMs - "Now" for age computation
     * @returns Sorted candidate rows (priority DESC, then age DESC — older first)
     */
    private projectRequests;
    /**
     * Project active WorkItems for this agent: items the agent is on the hook
     * to execute (target=me) OR items the agent has actively claimed
     * (claimedBy=me, surfaced via `metadata.claimedBy`). Items targeted at this
     * agent but already claimed by *another* agent are excluded — they are
     * not actionable for the current session.
     *
     * Orchestrator override: include ALL active work items.
     *
     * @param items - All WorkItems from {@link TaskPoolService.getAllItems}
     * @param sessionName - Agent session name
     * @param role - Agent role
     * @param nowMs - "Now" for age computation
     * @returns Sorted candidate rows
     */
    private projectActiveWorkItems;
    /**
     * Pending review rows — WorkItems in `done_by_worker` where this agent
     * is the verifier. Verifier signal = `metadata.verifier === sessionName`
     * (TL/orchestrator pattern). For the orchestrator role we surface ALL
     * `done_by_worker` items system-wide.
     *
     * @param items - All WorkItems from the pool
     * @param sessionName - Agent session name
     * @param role - Agent role
     * @param nowMs - "Now" for age computation
     * @returns Sorted candidate rows (oldest first — review backlog)
     */
    private projectPendingReviews;
    /**
     * Outbound delegations — WorkItems this agent delegated and that haven't
     * resolved yet. Identified by `metadata.delegatedBy === sessionName`.
     * Forward-compatible: if upstream code does not yet populate
     * `metadata.delegatedBy`, this section is empty.
     *
     * @param items - All WorkItems from the pool
     * @param sessionName - Agent session name
     * @param nowMs - "Now" for age computation
     * @returns Sorted candidate rows
     */
    private projectOutboundDelegations;
    /**
     * Recently auto-resolved WorkItems within the configured window
     * (default 30 minutes). Surfaced so the agent knows what closed
     * automatically while it was down. Identified by
     * `metadata.slaResolvedAt` falling inside the window.
     *
     * @param items - All WorkItems from the pool
     * @param sessionName - Agent session name (filters to items relevant to this agent)
     * @param role - Agent role
     * @param nowMs - "Now" for age computation
     * @param windowMs - Window size in milliseconds
     * @returns Sorted candidate rows (most recent first)
     */
    private projectRecentlyResolved;
    /**
     * Read a priority hint from WorkItem.metadata. Falls back to `'normal'`.
     *
     * @param metadata - WorkItem metadata blob
     * @returns Briefing priority
     */
    private readPriorityFromMetadata;
    /**
     * Truncate a row title to {@link MAX_TITLE_CHARS}.
     *
     * @param title - Raw title
     * @returns Truncated title (with ellipsis suffix if cut)
     */
    private truncateTitle;
    /**
     * Truncate a row description to {@link MAX_DESCRIPTION_CHARS}. Replaces
     * newlines with spaces so a description never wraps the briefing layout.
     *
     * @param desc - Raw description
     * @returns Truncated description
     */
    private truncateDescription;
    /**
     * Generates an active-work briefing for the given agent.
     *
     * Pulls from {@link RequestService} + {@link TaskPoolService}, filters
     * to the agent's slice (orchestrator gets system-wide), applies per-section
     * caps, and returns the structured payload. The caller renders it via
     * {@link formatBriefingAsMarkdown}.
     *
     * Soft-fail policy: any error from the underlying services bubbles up.
     * The CALL SITE in `agent-registration.service.ts` wraps this in a try/catch
     * with WARN-level logging — we deliberately do NOT swallow errors here so
     * test scenarios can assert that the rejection occurs, and so the optional
     * skill endpoint can return a structured 500 response.
     *
     * @param sessionName - Agent session name (e.g. `crewly-product-sam-dd2b46f7`)
     * @param role - Agent role (`developer`, `orchestrator`, etc.)
     * @param options - Optional caps + window overrides for testing
     * @returns The structured briefing
     *
     * @throws Rethrows any error from {@link RequestService.listAll} or
     *   {@link TaskPoolService.getAllItems}.
     *
     * @example
     * ```typescript
     * const briefing = await ActiveWorkBriefingService.getInstance()
     *   .generateActiveWorkBriefing('dev-001', 'developer');
     * const md = ActiveWorkBriefingService.getInstance()
     *   .formatBriefingAsMarkdown(briefing);
     * ```
     */
    generateActiveWorkBriefing(sessionName: string, role: string, options?: GenerateOptions): Promise<ActiveWorkBriefing>;
    /**
     * Format a briefing as markdown suitable for prompt injection.
     *
     * Output structure:
     *
     * ```
     * ## Your Active Work
     *
     * ### Open Requests
     * - **request-abc** — Slack inbound — _open · 2h_ *(memory: marked done — verify)*
     * ...
     * ### Active WorkItems
     * - **wi-123** — Implement feature X — _running · 1h_
     *   - desc here
     * ...
     * ```
     *
     * If a {@link MemoryHints} map is provided, contradicting memory notes are
     * appended INLINE on each row, per design Q5/decision #2.
     *
     * Soft-formatting policy: empty briefing collapses to "(No active work —
     * fresh start)". Sections with zero rows are omitted (no empty headings).
     *
     * @param briefing - Structured briefing payload
     * @param memoryHints - Optional namespaced map of `req:<id>` / `wi:<id>` →
     *   short note string
     * @returns Markdown string
     */
    formatBriefingAsMarkdown(briefing: ActiveWorkBriefing, memoryHints?: MemoryHints): string;
    /**
     * Render the five briefing sub-sections to markdown lines. Pulled out of
     * {@link formatBriefingAsMarkdown} so the overflow-recover path can reuse
     * the same renderer with `compact=true` (drops descriptions to bound
     * the orchestrator briefing).
     *
     * @param briefing - Briefing payload
     * @param memoryHints - Optional contradiction notes
     * @param compact - If true, drop description lines under WorkItem rows
     * @returns Array of markdown blocks (joined by blank lines by caller)
     */
    private renderSections;
    /**
     * Render the truncation marker line for a section. No-op when the section
     * was not truncated.
     *
     * @param truncation - Section truncation metadata
     * @returns Marker line (empty string if not truncated)
     */
    private renderTruncationMarker;
    /**
     * Append a memory-hint annotation to a row line, if a hint exists for the
     * given key. Renders the annotation italicized so it visually subordinates
     * to the actionable state row.
     *
     * @param line - Row line so far
     * @param key - Hint lookup key (e.g. `req:abc-123`)
     * @param hints - Memory hints map (optional)
     * @returns The line with annotation appended, or unchanged
     */
    private appendMemoryHint;
    /** Format hours into a compact "Nh" / "Nm" string for row metadata. */
    private formatAge;
    /**
     * Render the Open Requests section.
     *
     * @param rows - Capped rows
     * @param truncation - Section truncation metadata
     * @param hints - Memory hints
     * @returns Markdown block
     */
    private renderOpenRequests;
    /**
     * Render the Active WorkItems section.
     *
     * @param rows - Capped rows
     * @param truncation - Section truncation metadata
     * @param hints - Memory hints
     * @param compact - If true, drop description lines (orchestrator overflow)
     * @returns Markdown block
     */
    private renderActiveWorkItems;
    /**
     * Render the Pending Reviews section.
     *
     * @param rows - Capped rows
     * @param truncation - Section truncation metadata
     * @param hints - Memory hints
     * @returns Markdown block
     */
    private renderPendingReviews;
    /**
     * Render the Outbound Delegations section.
     *
     * @param rows - Capped rows
     * @param truncation - Section truncation metadata
     * @param hints - Memory hints
     * @returns Markdown block
     */
    private renderOutboundDelegations;
    /**
     * Render the Recently Auto-Resolved section.
     *
     * @param rows - Capped rows
     * @param truncation - Section truncation metadata
     * @param hints - Memory hints
     * @returns Markdown block
     */
    private renderRecentlyResolved;
}
export {};
//# sourceMappingURL=active-work-briefing.service.d.ts.map