/** * Run trace hooks: the one-line calls the rest of the backend makes to start * traces, carry them onto entities, and record what happened. * * Every function here is fire-and-forget: it catches its own errors and never * throws, so a trace problem can never break a turn, a delivery or a request. * * specs/2026-10-03-run-traces.md * * @module services/trace/trace-recorder */ import type { Request } from '../../types/v2/request.types.js'; import type { WorkItem, WorkItemStatus } from '../../types/v2/work-item.types.js'; import type { OwnerDecision } from '../../types/decision.types.js'; import { type TraceEventType, type TraceOutcome, type TraceRefs } from './trace.types.js'; /** * The trace a session's turn is on (no pending root is materialised). * * @param session - Agent session * @returns Trace id, or null */ export declare function currentTraceOf(session: string | null | undefined): string | null; /** * The trace a session's turn is on; a waiting owner message becomes a root now * because the turn is starting work. * * @param session - Agent session * @returns Trace id, or null */ export declare function ensureTraceForSession(session: string | null | undefined): string | null; /** * A message was written into an agent turn (PTY or in-process). * * @param session - Agent session * @param text - Exactly what was delivered * @param runtime - `pty` or `in-process` * @returns The trace the turn is now on, or null */ export declare function noteTurnDelivery(session: string, text: string, runtime?: string): string | null; /** * A message handed to a busy agent mid-turn, as a tool-boundary hook note * (PostToolUse `additionalContext`). It is now the latest input the agent * answers, so the turn-trigger tracker records it as a delivery would; the * turn's run trace does not change, because the turn is still the one it was * (crewly#1381 item 2). * * @param session - Agent session * @param text - Exactly what the note handed over (the queued text) */ export declare function noteMidTurnDelivery(session: string, text: string): void; /** * An in-process turn failed. * * @param session - Agent session * @param error - What failed */ export declare function traceTurnError(session: string, error: unknown): void; /** * An agent's turn became busy or idle; records `turn.ended` with the busy * time when it ends (see {@link TraceContext.noteTurnActivity}). * * @param session - Agent session * @param busy - True when the turn started, false when it ended * @param runtime - `pty` or `in-process` * @returns True when an event was recorded */ export declare function traceTurnActivity(session: string, busy: boolean, runtime?: string): boolean; /** Why a runtime stopped serving a session. */ export type RuntimeBlockReason = 'usage_limit' | 'billing' | 'login'; /** * A session's runtime ran out of usage or credit, or its login expired: * record `runtime.blocked` in the trace the session is on. * * @param session - Agent session * @param reason - usage_limit, billing or login * @param runtime - Runtime (target) that is blocked * @param detail - Short detail (rule id, reset time) * @returns True when recorded */ export declare function traceRuntimeBlocked(session: string, reason: RuntimeBlockReason, runtime: string, detail?: string): boolean; /** * The subagent guard (#852) sent a no-op subagent of this session back to * work: record `harness.subagent_sendback` in the session's trace. * * @param session - Parent agent session * @returns True when recorded */ export declare function traceSubagentSendBack(session: string): boolean; /** * The owner changed something from the dashboard (a write with no agent * session): when a path segment is an entity the trace index knows, record * `owner.action` in that trace — a manual intervention. * * @param input.method - HTTP method * @param input.path - Path relative to /api * @param input.status - Response status * @returns The trace it was recorded in, or null */ export declare function traceOwnerAction(input: { method: string; path: string; status: number; }): string | null; /** * Start a goal or experiment trace (POST /api/traces, #986 experiment cards). * * @param input - Kind, summary, who, refs, session to bind * @returns Trace id, or null */ export declare function startGoalTrace(input: { kind: 'goal' | 'experiment'; summary: string; session?: string; refs?: TraceRefs; }): string | null; /** * Give a new Request its trace (call before its first save): an explicit or * creator trace, the parent ticket's trace, or a new `request` root. Sets * `request.traceId` and records `request.created`. * * @param request - The Request about to be saved (mutated) * @param opts.traceId - Trace to join (e.g. the creating agent's) * @param opts.creatorSession - Agent creating it, when known * @returns The trace id, or null */ export declare function assignRequestTrace(request: Request, opts?: { traceId?: string; creatorSession?: string; }): string | null; /** * Who an `awaiting_followup` ticket waits on: the owner when every active * open item is a conditional promise waiting on them, else an agent. * * @param request - The Request * @returns `owner` or `agent` */ export declare function followupWaitsOn(request: Pick): 'owner' | 'agent'; /** * A Request (ticket) changed status: submitted for review, accepted, sent * back, reopened, cancelled. * * @param request - The Request after the change * @param previous - Its status before */ export declare function traceRequestStatus(request: Request, previous: string): void; /** * A project ticket was created: link it to the trace of its Request or of the * creating agent's turn, and record `ticket.created`. * * @param ticket - The ticket * @param actor - Who created it (agent session, or a non-agent label) * @returns The trace id, or null */ export declare function traceProjectTicketCreated(ticket: { id: string; title: string; requestId?: string | null; assignee?: string | null; }, actor: string): string | null; /** * Give a work item its trace before it is stored. Order: its own `traceId`; * the item it continues (parent / verify-of / source); its Request; its * project ticket; the creating agent's turn. Sets `workItem.traceId`. * * @param workItem - The item (mutated) * @param creatorSession - Agent creating it (X-Agent-Session), when known * @returns The trace id, or null */ export declare function assignWorkItemTrace(workItem: WorkItem, creatorSession?: string): string | null; /** * A work item entered the pool (PoolStorage.addWorkItem): fill in its trace if * the caller did not, and record `workitem.created`. * * @param workItem - The stored item (mutated when its trace is filled in) */ export declare function traceWorkItemCreated(workItem: WorkItem): void; /** * A work item changed status (PoolStorage.updateWorkItem). `running` is the * claim. * * @param workItem - The item after the change * @param previous - Its status before */ export declare function traceWorkItemStatus(workItem: WorkItem, previous: WorkItemStatus): void; /** * The `[TRACE:…]` header line for a work item's prompt. * * @param workItem - The item * @returns The marker, or '' when the item has no trace */ export declare function workItemTraceMarker(workItem: Pick): string; /** * Append a work item's trace marker as the last line of a hand-over message, * unless it is already there. Appended, never prepended: routing parsers * anchor on the start of a delivered message (`[CHAT:…]`, `[TASK]`). * * @param message - The hand-over text * @param workItem - The item handed over * @returns The text with the marker last */ export declare function withWorkItemTraceMarker(message: string, workItem: Pick): string; /** * One agent messaged another through `/terminal/:to/*`: return the text with * a trace appended so the receiving turn joins it, and record `message.agent`. * An explicit link in the text (`[TRACE:…]`, work item id, `[TICKET:…]`) wins. * Otherwise the sender's trace is appended, except when that trace is tagged * with an autopilot project none of the recipient's teams works on: an * orchestrator relay sent while its turn sat on another project's run would * bill the recipient's work to that run (CREW-396). * * @param sender - Sending agent session * @param target - Receiving session * @param text - Message text * @param maxLength - Longest text the endpoint accepts; the marker is skipped when it would not fit * @returns The text to deliver (unchanged when no trace applies) */ export declare function carryAgentMessageTrace(sender: string, target: string, text: string, maxLength?: number): string; /** * A status report was routed. Returns the trace so the routed text can carry * its marker. * * @param input.sender - Reporting agent * @param input.content - Report text * @param input.workItem - The work item it is about, if found * @param input.action - Route (`orc`, `team-lead`, `digest`, `record`) * @param input.target - Who receives it * @returns The trace id, or null */ export declare function traceStatusRouted(input: { sender: string; content: string; workItem?: Pick | null; action: string; target?: string; }): string | null; /** * Append the trace marker to a routed status text. * * @param text - Status text * @param traceId - Trace (no-op when null) * @returns The text with the marker last */ export declare function withTraceMarker(text: string, traceId: string | null): string; /** What the reply resolver did with an agent message (subset of ReplyDelivery). */ export interface OutboundReplyResult { ok: boolean; error?: string; messageId?: string; conversationId?: string; slackChannelId?: string; messageTs?: string; destination?: { kind?: string; source?: string; }; } /** * The reply resolver (#954) delivered or refused an agent message. * * @param input.session - Agent * @param input.content - Message text * @param input.reference - The reference it named (ticket / work item / decision / message) * @param result - The delivery */ export declare function traceOutboundReply(input: { session: string; content: string; reference?: { messageId?: string; ticket?: string; workItemId?: string; decisionId?: string; }; }, result: OutboundReplyResult): void; /** * A decision card was asked: link it to the trace of its work item, Request, * project ticket or asker's turn, and record `decision.created`. * * @param decision - The stored decision */ export declare function traceDecisionCreated(decision: OwnerDecision): void; /** * A decision changed state (answered, defaulted, cancelled, …). * * @param previous - Before * @param next - After */ export declare function traceDecisionChanged(previous: Pick, next: OwnerDecision): void; /** Input of {@link traceHarness}. */ export interface HarnessEventInput { /** Session acted on */ session?: string; workItemId?: string; workItem?: Pick | null; /** Trace, when the caller knows it */ traceId?: string | null; summary: string; outcome?: TraceOutcome; data?: Record; } /** * The harness intervened (redelivery, wake, correction, nudge, guard block): * record it in the trace of the work item, else of the session's turn. * * @param type - Event type * @param input - What and on whom * @returns True when recorded */ export declare function traceHarness(type: Extract, input: HarnessEventInput): boolean; /** A token-ledger entry (subset of TokenUsageEvent). */ export interface UsageEntry { timestamp: string; input: number; output: number; model: string; cachedInput?: number; /** Cache-write tokens (part of cachedInput for Claude Code turns), for the cost */ cacheWrite?: number; runtime?: string; } /** * A token-ledger entry was recorded: find the trace the session was on at * that time, record `usage`, and return the id to stamp on the entry. * * @param session - Agent session * @param entry - The usage * @returns Trace id, or null */ export declare function traceUsage(session: string, entry: UsageEntry): string | null; /** The part of an experiment card the trace needs. */ export interface ExperimentTraceInput { /** `EXP-n` */ id: string; title: string; /** The ticket it rides on: a project ticket id, or a TKT-n label / request id */ ticket?: { kind: 'project' | 'harness'; id: string; }; } /** * An experiment card was created: join its ticket's trace when the ticket * has one, else start an `experiment` root. Links `experiment:` (and * the ticket) in the index. * * @param experiment - The new card * @param caller - Who created it (agent session, or `owner`) * @returns The trace id, or null */ export declare function startExperimentTrace(experiment: ExperimentTraceInput, caller: string): string | null; /** * Mirror one experiment timeline entry (created, shipped, baseline, measured, * verdict, …) into its trace. * * @param traceId - The card's trace * @param experimentId - `EXP-n` * @param event - Timeline event name * @param detail - Optional detail */ export declare function traceExperimentEvent(traceId: string, experimentId: string, event: string, detail?: string): void; //# sourceMappingURL=trace-recorder.d.ts.map