import type { CortexStore, ParsedEpisode, SessionRow } from '../db/store.js'; /** * What a subagent concluded, kept after the subagent is gone (FR-19, Story 5.3). * * A dispatched subagent can burn a very large context and leave one paragraph * behind. This records that paragraph where the rest of Cortex can see it — * and the ORDERING is the whole point, not an implementation detail. * * **`collectEvidence` (`src/query/suggest-notes.ts`) reads exactly three things * for a session: episode `summary`, events, and command runs.** It never reads * `last_assistant_message`. For a child session the other two are close to * empty — `handleReadEvent` records only a line range, so a subagent's reads * produce no evidence text at all, and command runs count only on a non-zero * exit. So a subagent that only THINKS, which is precisely the case Story 5.1 * exists to make visible, yields zero suggestions unless its conclusion is * written as an episode summary FIRST. Everything downstream — the Stop nudge, * `suggestNotes`, the whole AC #2 path — is already wired and sees nothing * without this write. */ /** * The episode kind. Registered in THREE places, two of which fail silently: * `KIND_WEIGHTS` (`src/memory/kind-weights.ts`), which the eval gate's * `checkKindCoverage` reads, plus `episodeState` and `episodeImportance` * (`src/memory/items.ts`), which switch on kind and otherwise fall through to * `'warm'` / `0.6` with no error and no gate failure. */ export declare const SUBAGENT_CONCLUSION_KIND = "subagent_conclusion"; /** * How much of a conclusion is kept. * * A final message can run long and a transcript can run to megabytes. Four * thousand characters is roughly a thousand tokens — enough for a real summary * with its reasoning, far short of pasting a transcript into memory. The * episode records whether it truncated, so a reader is never shown a cut answer * that looks complete. */ export declare const DEFAULT_CONCLUSION_MAX_CHARS = 4000; export declare const CONCLUSION_MAX_CHARS_ENV = "CORTEX_SUBAGENT_CONCLUSION_MAX_CHARS"; /** * `Number`, never `parseInt` — reused rather than re-implemented, which is the * sixth time this repository would otherwise have written its own numeric * option parser and the fifth time one of those was wrong. */ export declare function conclusionMaxChars(): number; export interface ConclusionText { text: string; truncated: boolean; /** `message` on the normal path, `transcript` only when the message was absent. */ source: 'message' | 'transcript'; } /** * The host's own record of what a subagent was dispatched with (FR-19, Task 5). * * `/subagents/agent-.meta.json`, carrying * `{agentType, description, toolUseId, spawnDepth}` — verified live on this * machine. Story 5.1 measured that it is written strictly AFTER every * `SubagentStart` hook returns (a 5,259 ms bounded poll inside the hook never * saw it), which is exactly why Story 5.2 could prove its pairing *unambiguous* * and not *right*, and why `SubagentStop` is the first place the check is * possible at all. * * **Everything here is defensive on purpose.** The path is derived, the file is * host-internal and undocumented, and its shape can change without notice. * Every failure returns `undefined`, and the caller must treat that as "no * audit was performed" rather than "the audit failed" — 5.1's review found the * false-alarm class twice, and an absent audit reported as a fault is the same * mistake in a new place. */ export declare function readDispatchSidecar(agentTranscriptPath: string | undefined, transcriptPath: string | undefined, agentId: string): { toolUseId?: string; description?: string; } | undefined; /** * Choose and bound the conclusion text. Returns `undefined` when there is * nothing to record — the ordinary outcome for a subagent that said nothing. */ export declare function resolveConclusionText(lastAssistantMessage: string | undefined, transcriptPath: string | undefined, maxChars?: number): ConclusionText | undefined; /** * The metadata key marking a conclusion the Stop nudge has already offered. * * **The bound this story would otherwise need and not have.** `endOfTurn` * collects suggestions across `getSessionTreeIds`, which is the root primary * plus `getChildSessions` — a bare `SELECT * FROM sessions WHERE * parent_session_id = ?` with no status, recency or limit filter. `suggestNotes` * has no recency filter either, and the primary rarely rotates: `endSessionTree` * runs from `ensurePrimarySession` only when the SCOPE KEY changes, so a * SessionStart on the same branch and worktree ends nothing and children stay * `active` for days. Without a marker, every conclusion written here would * re-surface in the nudge on every later turn that used any subagent, for the * life of the primary — `endOfTurn`'s `seen` set dedupes within one invocation * and nothing dedupes across them. An accepted suggestion that keeps being * re-offered is the cries-wolf half of AD-12: it trains the user to dismiss the * nudge, which costs more than the nudge ever earned. * * A marker rather than a time window, because time is the wrong axis. A * subagent can run for half an hour, so any window short enough to bound the * noise is also short enough to discard the conclusion of a long investigation — * exactly the run this story exists to preserve. */ export declare const CONCLUSION_SURFACED_KEY = "surfaced_at"; /** * The metadata key marking a pairing audit already booked for this subagent. * * Same once-only discipline as {@link CONCLUSION_SURFACED_KEY} and for a * sharper reason: the host can fire `SubagentStop` more than once for one agent, * and a repeated audit inflates the DENOMINATOR of the mispairing rate — the * one counter in this epic that warns. `recordSubagentConclusion` was already * idempotent; the audit beside it was not. */ export declare const CONCLUSION_AUDITED_KEY = "audited_at"; /** The conclusion episode for a session, if it has one. */ export declare function findSubagentConclusion(store: CortexStore, sessionId: string): ParsedEpisode | undefined; /** Whether the Stop nudge has already offered this conclusion. */ export declare function conclusionSurfaced(episode: ParsedEpisode): boolean; /** * Mark a conclusion as offered. Best-effort by contract: this runs on the turn's * critical path and a failed mark must cost a duplicate nudge, never the turn. */ export declare function markConclusionSurfaced(store: CortexStore, episode: ParsedEpisode, now?: string): void; export interface RecordConclusionOptions { /** The CHILD session. The conclusion belongs to the subagent that reached it. */ child: SessionRow; conclusion: ConclusionText; agentType?: string | undefined; transcriptPath?: string | undefined; } /** * Write the conclusion as an episode on the child session. * * Idempotent per child: a second `SubagentStop` for the same agent — which the * host can send, and which Story 5.1's deferred work already records as * reachable for a recycled id — updates nothing and inserts nothing. Replay * produces identical state (N-7). */ export declare function recordSubagentConclusion(store: CortexStore, options: RecordConclusionOptions): ParsedEpisode | undefined; //# sourceMappingURL=subagent-conclusion.d.ts.map