/** * Harness hint resolver — determines whether the calling harness already has * CLEO-INJECTION.md loaded (so tier-1 prompt can skip the ~9KB embed). * * Resolution cascade (highest priority first): * 1. Explicit function-call option: `options.explicit` * 2. Env var: `CLEO_HARNESS=` * (CLI flag wrappers SHOULD set this env var so the cascade sees it.) * 2b. Env var: `CLEO_HARNESS_LOADS_AGENTS_MD=1` → `claude-code` * Set by harnesses that already inject `AGENTS.md` (and thus CLEO-INJECTION.md) * into the subagent context. Equivalent to `CLEO_HARNESS=claude-code` but * named after the concrete mechanism so operators understand WHY dedup fires. * 3. Persisted: `/.cleo/harness-profile.json` * 4. Auto-detect: both `CLAUDECODE=1` AND `CLAUDE_CODE_ENTRYPOINT` set → `claude-code` * 5. Default: `generic` * * The dedup budget is the ~9KB tier-1 CLEO-INJECTION.md embed. Harnesses that * already inject the protocol (e.g. Claude Code via `AGENTS.md`) can safely * skip the embed and save the tokens. Non-injecting harnesses (generic, * bare) must receive the full embed so the subagent sees the protocol at * least once per prompt. * * @task T889 Orchestration Coherence v3 * @task T893 Harness-aware dedup (W3-2) */ /** * Identifiers for the harness context the spawn prompt is being built for. * * - `claude-code` — the Claude Code CLI (auto-injects `AGENTS.md`, so the * tier-1 CLEO-INJECTION embed is redundant). * - `generic` — any non-Claude-Code agent runtime (OpenAI Agents SDK, * LangGraph, Gemini, hand-rolled wrappers). Default. MUST receive the * embed so the protocol reaches the subagent. * - `bare` — no harness whatsoever (raw API call). Treated like * `generic` for embed purposes but reserved for future behaviour * divergence (e.g. skipping session commands the runtime can't exec). */ export type HarnessHint = 'claude-code' | 'generic' | 'bare'; /** * Approximate byte size of the tier-1 `CLEO-INJECTION.md` embed that gets * deduplicated when a harness already has the protocol loaded. * * Used only for diagnostic accounting — the actual embed skip is binary * (present or absent), and the real byte count depends on the current * template. 9000 chars is a conservative upper bound matching the current * template size as of 2026-04-17. */ export declare const DEDUP_EMBED_CHARS = 9000; /** * Input shape for {@link resolveHarnessHint}. */ export interface ResolveHarnessHintOptions { /** * Explicit override — wins over env, profile, and auto-detect. * * CLI callers translating a `--harness-hint` flag SHOULD pass the parsed * value here rather than mutating `process.env`. */ explicit?: HarnessHint; /** * Absolute path to the project root. When supplied, the resolver reads * `/.cleo/harness-profile.json` as the third cascade step. * When omitted, the profile lookup is skipped. */ projectRoot?: string; /** * Env source for deterministic testing. Defaults to `process.env`. */ env?: NodeJS.ProcessEnv; } /** * Resolution result from {@link resolveHarnessHint}. */ export interface HarnessHintResult { /** Resolved harness hint. */ hint: HarnessHint; /** * Which cascade step produced the hint. Useful for diagnostics and for * tests that want to assert the precedence order. */ source: 'option' | 'env' | 'profile' | 'auto-detect' | 'default'; /** * Estimated characters saved by skipping the tier-1 CLEO-INJECTION embed. * `DEDUP_EMBED_CHARS` for `claude-code`, `0` for every other hint. */ dedupSavedChars: number; } /** * On-disk profile shape written by {@link persistHarnessProfile}. */ export interface HarnessProfile { /** Detected or persisted harness hint. */ harness: HarnessHint; /** ISO 8601 timestamp when the profile was written. */ detectedAt: string; } /** * Resolve the active harness hint using the documented cascade. * * @param options - See {@link ResolveHarnessHintOptions}. Empty by default. * @returns Resolution envelope identifying the hint, the cascade step, and * the dedup budget. * * @example * ```typescript * // From the CLI, honouring a `--harness-hint` flag: * const { hint, source } = resolveHarnessHint({ * explicit: flagValue, * projectRoot, * }); * * // From the test suite, pinning the env: * const result = resolveHarnessHint({ * env: { CLEO_HARNESS: 'claude-code' }, * }); * ``` * * @task T889 / T893 / W3-2 */ export declare function resolveHarnessHint(options?: ResolveHarnessHintOptions): HarnessHintResult; /** * Persist a harness hint to `/.cleo/harness-profile.json` using * an atomic `.tmp` write + rename so readers never observe a half-written * file. * * The `.cleo/` directory is created if it does not yet exist — callers do * NOT need to pre-create it. The written profile carries the ISO timestamp * at which persistence occurred, which makes the profile self-describing for * the `cleo agent doctor` walk. * * @param projectRoot - Absolute path to the project root. * @param hint - Harness hint to persist. * * @throws {Error} Surface I/O errors verbatim. Callers that want best-effort * persistence should wrap the call in try/catch — this * function does not swallow filesystem failures. * * @task T889 / T893 / W3-2 */ export declare function persistHarnessProfile(projectRoot: string, hint: HarnessHint): Promise; /** * Load the persisted harness profile from * `/.cleo/harness-profile.json`, or `null` when absent or * malformed. * * This is the async counterpart to the synchronous profile read used by * {@link resolveHarnessHint}. Prefer this entry point for tooling that wants * the full envelope (including `detectedAt`) and treats missing profiles as * a non-error. * * @param projectRoot - Absolute path to the project root. * @returns The parsed profile, or `null` when the file is missing, * unreadable, or carries an unknown `harness` value. * * @task T889 / T893 / W3-2 */ export declare function loadHarnessProfile(projectRoot: string): Promise; //# sourceMappingURL=harness-hint.d.ts.map