/** * The contract between the cold path (Node) and the hot path (bash) for * verified read substitution (FR-6, Story 4.5). * * **Everything the hook cannot compute for itself lives here**, in one module, * so the two sides cannot drift. The hook resolves files as `"$CWD/.cortex.*"` * in pure bash and may not open SQLite (AD-2) or spawn Node (N-4), which means * three facts have to be *published* to it rather than derived by it: * * - which Cortex session is the current primary (AD-16 refund eligibility — * the payload's own `session_id` is Claude Code's, not Cortex's); * - the scope key in the exact escaped form `formatIndexLine` wrote it, so the * index lookup is one anchored `grep -F -m1` rather than a multi-line scan; * - the scope root, normalized the way `normalizeFilePathKey` normalizes it, * so bash can derive the scope-relative key with builtins alone. * * They are published as `key=value` lines in `/.cortex.state`, which * `inject-header` already rewrites wholesale at every SessionStart. Per-session * facts in a per-session file is the right coupling: a stale session id would * be the AD-16 failure this whole mechanism exists to prevent, and rewriting * them together with the session that produced them makes staleness * unreachable. * * The **enable flag** deliberately does *not* live there. `.cortex.state` is * read with `grep`, and a second key would mean a second process on every tool * call including `Edit`, `Write`, `Bash` and `Agent` — a tax on paths that can * never substitute. A marker file is `[ -f ]`, a bash builtin, and it survives * the wholesale rewrite for free. */ /** Existence is "on" (AC #6). Tested with `[ -f ]`, so the check costs nothing. */ export declare const SUBSTITUTION_FLAG_FILENAME = ".cortex.substitution"; /** * Paths evaluated for substitution during the current turn (AC #4). * * Appended by the hook, removed by `cortex-end-of-turn.sh` at Stop — the * lifecycle `.cortex.agent-used` already established. Over-suppression (a * subagent's reads accumulate until the parent's Stop, because SubagentStop is * a different event) degrades to a miss, which is the safe direction. */ export declare const TURN_READS_FILENAME = ".cortex.turn-reads"; /** * Below this a refund is not worth taking: the substitution payload itself * costs tokens, so a small file's "saving" is noise or negative. */ export declare const DEFAULT_SUBST_MIN_BYTES = 2048; /** * B-4a's amendment requires a ceiling above which substitution is skipped, and * this is it — but the honest reason is not the one the amendment gives. * * Measured on the reference platform (Git Bash 5.2.37, median of 25 warm runs): * `sha256sum` of nothing costs 55 ms, of 512 KiB 57 ms, of 2 MiB 62 ms. Hashing * is ~2–7 ms; the **process** is the whole cost. So the ceiling is not what * keeps the hit path inside its budget (B-4a as re-based 2026-08-03: hit * ≤800 ms p95 end-to-end, structural spawn count primary) — the spawn count * is. It ships anyway because the amendment requires it and because a platform * with a slower `sha256sum` is a real possibility. * * 1 MiB, deliberately **below** `CORTEX_DIGEST_MAX_BYTES` (2 MiB): a ceiling * equal to the digest ceiling could never fire, because past that the recorded * `sha256` is NULL and no record can match. In practice the full-read check * subsumes most of this — `Read` truncates at 2000 lines — and the ceiling is * documented as belt-and-braces rather than credited with the budget. */ export declare const DEFAULT_SUBST_MAX_BYTES: number; export declare function resolveSubstMinBytes(raw?: string | undefined): number; export declare function resolveSubstMaxBytes(raw?: string | undefined): number; /** * The `.cortex.state` keys the hook reads. Exported so the shell script's * literals can be asserted against them by test instead of matching by * inspection. */ export declare const HOT_PATH_STATE_KEYS: { readonly sessionId: "session_id"; readonly indexScope: "index_scope"; readonly scopeRoot: "scope_root"; readonly pathFold: "path_fold"; }; export declare function deriveSubstitutionFlagPath(projectRoot: string): string; export declare function deriveTurnReadsPath(projectRoot: string): string; /** * `statSync().isFile()`, not `existsSync`. * * The hook's gate is `[ -f ]`, which is false for a directory. `existsSync` * is true for one, and the two sides disagreeing about the single gate AC #6 * rests on is exactly the drift this module exists to prevent. */ export declare function isSubstitutionEnabled(projectRoot: string): boolean; /** Returns whether the state actually changed, so callers can report honestly. */ export declare function setSubstitutionEnabled(projectRoot: string, enabled: boolean): boolean; export interface HotPathStateFacts { /** Cortex's current primary session id. */ sessionId: string; /** The session's scope key, unescaped. */ scopeKey: string; /** `worktree_path` for that scope, or null when it cannot be resolved. */ scopeRoot: string | null; } /** * The lines `inject-header` appends to `.cortex.state`, or `''` when the facts * cannot be published safely or completely. * * An empty result is not a failure to handle — it is the AD-6 answer. Without * these keys the hook cannot establish eligibility and misses, which costs a * refund and can never grant a false one. */ export declare function renderHotPathStateLines(facts: HotPathStateFacts): string; //# sourceMappingURL=substitution.d.ts.map