/** * The run→launch-time index (home command-center run metrics; api-types 0.24.0). * * The engine's `AgentSession` (wicked-core `domain.rs`) records no launch timestamp — checked, it * carries only `archived_at` — so WHEN a run was launched is, exactly like its actor (task #88) * and its retry lineage (CREW-UX-3), knowledge only the daemon's HTTP layer ever holds. The * durable record is the `run.launched` audit entry's `ts` (unix MILLIS); this map is the read-side * latency layer that lets `GET /runs` / `GET /runs/:id` echo `AgentSession.created_at` (unix * SECONDS) without a trail scan per request. * * Mirrors `RetryIndex`'s posture exactly: hydrated once at server start from the SAME * `run.launched` scan `RetryIndex` + `GroupIndex` already share (the crew#321 consolidation note), * best-effort — a missing/unreadable trail leaves PRIOR runs undated, the pre-field behaviour, not * an error — and updated at the same post-commit point that writes the audit entry, so the map can * only lag by a failed hydrate, never diverge silently. */ import type { AuditLog } from './audit.js'; import type { Actor, AuditEntry, RecordedEvent, SessionView } from '../core/types.js'; export declare class RunTimingIndex { private readonly runToCreatedAt; /** runId → terminal instant, unix SECONDS (`AgentSession.ended_at`; api-types 0.38.0, crew#496). */ private readonly runToEndedAt; /** crew#985: runId → terminal instant DERIVED from the engine event log (unix SECONDS), for runs * with no `run.ended` entry. Kept apart from {@link runToEndedAt} so it never stands in for the * durable record: the daemon still writes `run.ended` when it sees the frame. */ private readonly runToDerivedEndedAt; /** crew#985: runId → the read of its engine event log (settled = read; in flight = awaited by an * overlapping request, so no request serves a run undated while another is still reading it). */ private readonly endedBackfill; /** runId → launch channel (`AgentSession.channel`; crew#632). */ private readonly runToChannel; /** runId → launch actor string (`AgentSession.launch_actor`; crew#632). */ private readonly runToLaunchActor; /** runId → warm seat count at chat-promotion time (`AgentSession.chat_seat_count`; crew#641/#642). */ private readonly runToChatSeatCount; /** runId → whether the promoted chat was graph-grounded (`AgentSession.chat_grounded`; crew#641/#642). */ private readonly runToChatGrounded; /** * runId → the chat it was launched from (`AgentSession.chat_id`; C1, api-types 0.71.0), for EVERY * run, terminal ones included. crew#619's `runToChat` in `server.ts` is the transcript-retention * hold and releases a run at its terminal frame; this is the permanent read-side index, fed by * the same `run.launched` entries. */ private readonly runToChatId; /** crew#854: runs an ask launched (the `run.launched` entry's `askPath`). */ private readonly askPathRuns; /** crew#1157: runs an ANALYSIS chat launched → its workflow (the entry's `chatWorkflow`). */ private readonly runToChatWorkflow; /** crew#552: chained run → the onboarding run it was launched from (`detail.chainedFrom`). */ private readonly runToChainedFrom; /** crew#552: onboarding run → the capture run chained from it (the idempotence key). */ private readonly chainedRun; /** crew#552: onboarding runs launched with `autoCapture: false`. */ private readonly autoCaptureOffRuns; /** crew#762: the RESOLVED delivery decision the launch recorded (`detail.deliver`). */ private readonly runToLaunchDeliver; /** * Consume pre-read `run.launched` entries — the seam that lets `createServer` feed this index * from the ONE `readAll({ action: 'run.launched' })` it already runs for `RetryIndex` + * `GroupIndex`, so boot stays at the same full-file trail scans, not one more. */ hydrateFromLaunchEntries(entries: AuditEntry[]): void; /** * Load launch times from EVERY `run.launched` entry in the trail — exhaustively, not capped, for * the same reason `RetryIndex.hydrate` is (BRIEF-UX-002 C5): a launch fact thousands of runs deep * must survive a restart, or a time-bucketed KPI silently drops the oldest runs. Still * best-effort: a missing/unreadable trail leaves runs undated, the pre-field answer. Cost is one * full-file scan at boot — the shared scan already parses the whole file. */ hydrate(audit: AuditLog, log?: (msg: string) => void): Promise; /** * Record a run's launch instant (unix MILLIS — the caller passes `Date.now()`, the same clock the * `run.launched` audit entry stamps, so the in-session answer and the post-restart rehydrate * agree to the second). Stored as whole unix seconds. */ set(runId: string, launchedAtMillis: number): void; /** The run's launch time in unix SECONDS, or `undefined` (the DTO spells that as an ABSENT field). */ createdAtFor(runId: string): number | undefined; /** Record the launch channel for a run (crew#632). */ setChannel(runId: string, channel: 'studio' | 'cli' | 'api'): void; /** The launch channel, or `undefined` (ABSENT when the launch did not name one). */ channelFor(runId: string): 'studio' | 'cli' | 'api' | undefined; /** Record the launch actor string for a run (crew#632). */ setLaunchActor(runId: string, actor: string): void; /** The launch actor string, or `undefined` (ABSENT when the launch did not name one). */ launchActorFor(runId: string): string | undefined; /** Record the warm seat count and grounding state at chat-promotion time (crew#641/#642). */ setChatPromotion(runId: string, seatCount: number, grounded: boolean): void; /** Record the chat a run was launched from (C1). */ setChatId(runId: string, chatId: string): void; /** Record that an ask launched the run (crew#854). */ setAskPath(runId: string): void; /** Did an ask launch the run (crew#854)? */ isAskPath(runId: string): boolean; /** crew#1157: record the read-only workflow an analysis chat's run runs. */ setChatWorkflow(runId: string, workflow: string): void; /** crew#1157: the read-only workflow the run's chat runs (`analyze`), or `undefined` for any other run. */ chatWorkflowOf(runId: string): string | undefined; /** crew#552: a launch entry's chain facts (`chainedFrom`, `autoCapture: false`). */ noteChain(runId: string, detail: Record | undefined): void; /** crew#552: the onboarding run a chained run was launched from (`AgentSession.chained_from`). */ chainedFromOf(runId: string): string | undefined; /** crew#552: the capture run chained from an onboarding run, if one was launched. */ chainedRunOf(onboardRunId: string): string | undefined; /** crew#552: was the onboarding run launched with `autoCapture: false`? */ autoCaptureOff(onboardRunId: string): boolean; /** Record the launch's resolved delivery decision (crew#762). */ setLaunchDeliver(runId: string, deliver: 'pr' | 'none'): void; /** What the launch decided about delivery (`'pr'` / `'none'`), or `undefined` when there is no launch record. */ launchDeliverFor(runId: string): 'pr' | 'none' | undefined; /** The chat the run was launched from, or `undefined` (ABSENT when it was not launched from one). */ chatIdFor(runId: string): string | undefined; /** The warm seat count at chat-promotion, or `undefined` (ABSENT when not a chat-promoted run). */ chatSeatCountFor(runId: string): number | undefined; /** Whether the chat was graph-grounded at launch, or `undefined` (ABSENT when not chat-promoted). */ chatGroundedFor(runId: string): boolean | undefined; /** * Consume pre-read `run.ended` entries (crew#496 / studio#230) — the daemon records one when it * sees a run's terminal frame (`sessionCompleted` | `sessionFailed` | `runCancelled`). The run's * FIRST terminal instant wins, the same rule the live {@link setEnded} applies: the trail answers * newest first, so the OLDEST entry per run (the last one seen) is kept — a resume/retry * re-terminal recorded after a hydrate failure never moves an already-dated run either way. * `ts <= 0` stamps nothing. One more filtered scan at boot, in the same try as the `run.launched` * scan (DES-L8 §5 PR-8B). */ hydrateFromEndedEntries(entries: AuditEntry[]): void; /** * Record a run's terminal instant (unix MILLIS — the `run.ended` audit entry's own `ts`, so the * live answer and the post-restart rehydrate agree to the second). Idempotent per run: the first * stamp wins — a second terminal frame (resume, retry) re-stamps nothing. */ setEnded(runId: string, endedAtMillis: number): void; /** The run's terminal time in unix SECONDS, or `undefined` (ABSENT on the wire — never null, * never fabricated: a live run, a pre-field run, a run that terminalled before THIS daemon * booted, a lost trail). */ endedAtFor(runId: string): number | undefined; /** Whether the run has a RECORDED terminal instant (a `run.ended` entry, live or hydrated). The * daemon writes the entry only when this is false, so an event-log derivation never skips it. */ hasRecordedEnd(runId: string): boolean; /** * crew#985: date the terminal runs that have no `run.ended` entry (runs that ended before * crew#496 shipped the entry, or while no daemon was watching) from the engine's own event log: * the FIRST terminal frame's capture `ts`, the same first-wins rule as {@link setEnded}. Read-only * and in memory: nothing is written to the trail, so the next boot derives it again, and a * recorded `run.ended` (live or hydrated) always wins over the derivation. * * Each run's log is read at most once per process (a terminal run's log does not grow). A run * whose log has no terminal frame (it predates the log) stays ABSENT. A failed read is not * remembered, so a later request tries again. */ backfillEndedFromEvents(views: readonly SessionView[], runEvents: (runId: string) => Promise): Promise; } /** * Record a `run.launched` audit entry AND stamp the run-timing index with the SAME durable `ts`, so * `created_at` is answered LIVE (not only after a restart re-hydrates the trail) for EVERY route * that launches a run — `POST /runs` and the recon/steering-author launchers alike. Centralized * here (Copilot #466) so a new launch site can never again record the trail without stamping the * index. `ts === 0` (audit disabled — the `noop` trail route-unit tests build) stamps nothing, * keeping `created_at` derived ONLY from a durable entry, never fabricated. Returns the millis `ts` * for a caller that needs it (0 when audit is disabled). */ export declare function recordRunLaunched(audit: AuditLog, runTimingIndex: RunTimingIndex | undefined, actor: Actor, runId: string, detail: Record): number; //# sourceMappingURL=run-timing-index.d.ts.map