/** * Harnery config reader: the settings the coord/hook layer consults when it * can't see the consumer CLI's own process. * * Two layers, project-over-user (project wins field-by-field): * 1. `~/.config/harnery/config.jsonc` — user-global base (optional) * 2. `/.harnery/config.jsonc` — project override (authoritative) * * Fields owned here: `binName` (host CLI name for agent-facing strings), * `hooksSetupHint`, `hooks`, `agents`, `instructions`, `tools`, `workflow`, `skills`, `presence`, plus the tunable * `coord` (heartbeat freshness), `logs` (structured-log storage budgets), * `artifacts` (working-file retention), * `backup` (restic repo/password/prune policy), `sync` (rclone * remote/prefix), and `web` (dashboard port) sections. The `files` deny/override section * is parsed separately by `web/lib/files.ts`. * * Env vars and CLI flags override any config value per invocation (each accessor * documents its own precedence). Dependency-free (no jsonc npm dep) so it runs on * both Bun and Node, and so the ADR-009 vendored copies stay portable. */ import { type PromptContextConfig } from "./hooks/prompt-context/contract.js"; export { DEFAULT_WEB_PORT } from "../lib/local-file-url.js"; export { DEFAULT_EVENT_LEDGER_ROTATE_ACTIVE_BYTES } from "./events/v3/rotation-config.js"; /** The standalone CLI's bin name: the resolution floor when nothing else is set. */ export declare const DEFAULT_BIN_NAME = "harn"; /** Heartbeat-freshness default (seconds): the sweep window when nothing overrides it. */ export declare const DEFAULT_FRESHNESS_SECS = 600; /** Keep a fresh host reminder bounded to one short prompt-context line. */ export declare const MAX_HOST_PROMPT_REMINDER_CHARS = 500; export interface SessionFinalizationConfig { archiveGraceSeconds: number; idleObserveSeconds: number; idleFinalizeSeconds: number; cascadeGraceSeconds: number; reconcileIntervalSeconds: number; } export declare const DEFAULT_SESSION_FINALIZATION_CONFIG: SessionFinalizationConfig; export type AgentFinalizationDisposition = "git" | "output"; export interface AgentFinalizationRoot { path: string; disposition: AgentFinalizationDisposition; } /** Strip `//` and `/* *​/` comments from JSONC, ignoring comment-like runs inside strings. */ export declare function stripJsonComments(input: string): string; export interface CoordRunQualityConfigSource { value: unknown; invalid: boolean; /** Stable digest seed that contains no config values when parsing failed. */ digest_seed: unknown; } export interface LogStorageConfigLayerSource { layer: "user" | "project"; value: unknown; invalid: boolean; /** Stable file signature for bounded diagnostics without retaining values. */ signature: string | null; } export interface LogStorageConfigSource { layers: readonly [LogStorageConfigLayerSource, LogStorageConfigLayerSource]; } /** * Raw user and project `logs.storage` layers with their origin intact. * * Storage retention validates these as one fail-closed unit. It cannot use the * ordinary merged reader because commands must explain whether each effective * scalar came from a user class, user family, project class, or project family. */ export declare function logStorageConfigSource(root: string): LogStorageConfigSource; /** * Effective user-plus-project `coord.run_quality` value with parse diagnostics. * The ordinary config reader stays fail-soft; this one lets the guard visibly * disable itself rather than silently substituting defaults for malformed JSONC. */ export declare function coordRunQualityConfigSource(root: string): CoordRunQualityConfigSource; /** * Resolve the host CLI's bin name for user-facing strings. Precedence: * 1. `HARNERY_BIN` env (explicit per-process override) * 2. `.harnery/config.jsonc` `binName` (stamped by `harn init`) * 3. `"harn"` (standalone default) * * `coordRoot` is resolved via `findCoordRoot()` when not passed. */ export declare function resolveBinName(coordRoot?: string | null): string; /** * The binName explicitly pinned in `/.harnery/config.jsonc`, or * null when absent. Unlike `resolveBinName()` this ignores `HARNERY_BIN` and * never falls back to the default — it answers "did someone deliberately pin * a name for THIS project?". `init` uses it so a re-run from a different host * CLI can't silently re-stamp its own name over a committed pin (the harnery * repo itself pins `"harn"` while living embedded in a host monorepo whose * CLI would otherwise stamp the host's name into public, committed surfaces). */ export declare function pinnedBinName(projectRoot: string): string | null; /** * The host's git-hook (re)install command, for the "commit guard not wired" * nudge. Returns the configured `hooksSetupHint` (e.g. "scripts/setup-hooks.sh") * or null when unset — callers fall back to a generic, host-agnostic message. * `coordRoot` is resolved via `findCoordRoot()` when not passed. */ export declare function resolveHooksSetupHint(coordRoot?: string | null): string | null; /** * One project-owned reminder that a supported prompt hook can place immediately * before a model response. The value is deliberately project-only: host policy * must not follow the user-global config into unrelated repositories. It is * emitted directly from config and never copied into coordination state. * * The reminder must be one non-empty line of at most 500 characters. Invalid * values fail closed to no reminder so a malformed optional setting cannot * break the prompt hook. */ export declare function hostPromptReminder(coordRoot?: string | null): string | null; /** * Project-owned prompt-context provider settings. The user-global config is * ignored because one project's executable and data policy must not become a * default for another project. Invalid values fail closed to `null`; callers * treat that the same as a disabled provider and keep the prompt hook usable. */ export declare function hostPromptContextConfig(coordRoot?: string | null): PromptContextConfig | null; /** * Whether the host requires the guarded Git check at the end of tool-using * turns. Default false: Harnery exposes `agents status --end-turn` as a capability * but does not impose a commit-and-push policy on embedding projects. * * `.harnery/config.jsonc`: * `{ "agents": { "requireGitFinalization": true } }` * * `HARNERY_AGENTS_REQUIRE_GIT_FINALIZATION=1|0` overrides per process. */ export declare function agentsRequireGitFinalization(coordRoot?: string | null): boolean; /** * Extra roots whose guarded writes have an explicit end-turn disposition. * * This trust boundary comes only from the project config. A user-global config * may tune ordinary behavior, but it cannot grant one project filesystem * authority outside its coordination root. Paths may be absolute or relative * to the coordination root. Invalid entries are ignored here and fail closed * when the finalization policy validates them. */ export declare function agentsFinalizationRoots(coordRoot?: string | null): AgentFinalizationRoot[]; /** The status command automatic prompts and Stop remediation should request. */ export declare function endOfTurnStatusCommand(coordRoot?: string | null): string; /** * Whether the host project consented to automatic ripgrep provisioning: * `.harnery/config.jsonc` `{ "tools": { "ripgrep": { "autoInstall": true } } }`. * A repo commits that once and every clone self-heals on first `grep`; without * it, a missing rg only produces a rate-limited hint (`doctor --fix` installs * explicitly). `HARNERY_TOOLS_AUTOINSTALL=1|0` overrides per process. * `coordRoot` is resolved via `findCoordRoot()` when not passed. */ export declare function ripgrepAutoInstall(coordRoot?: string | null): boolean; /** * Whether this repo pins workflow runs to subscription billing: * `.harnery/config.jsonc` `{ "workflow": { "subscriptionOnly": true } }`. * The `workflow run --subscription-only` flag turns it on per invocation; * `HARNERY_WORKFLOW_SUBSCRIPTION_ONLY=1|0` overrides per process (the `0` * escape hatch exists for a key-only CI job inside a pinned repo). * `coordRoot` is resolved via `findCoordRoot()` when not passed. */ export declare function workflowSubscriptionOnly(coordRoot?: string | null): boolean; /** * Whether cross-machine presence (ADR 0016) is enabled for this repo. * Default ON — the transport itself additionally gates on an origin remote * existing and fails silent everywhere. Opt out via * `.harnery/config.jsonc` `{ "presence": { "enabled": false } }`; * `HARNERY_PRESENCE=1|0` overrides per process. */ export declare function presenceEnabled(coordRoot?: string | null): boolean; /** * The presence relay URL for this repo, or null when the relay transport is * not configured (git-refs only). `HARNERY_PRESENCE_RELAY` overrides per * process (empty string or "0" disables). Requires `presenceEnabled()` to be * true — a disabled presence section disables the relay too. */ export declare function presenceRelayUrl(coordRoot?: string | null): string | null; /** * The heartbeat-freshness window (seconds): the age above which the sweeper * prunes an agent, and the cutoff the `agents` surface uses to fold stale peers. * Precedence: * 1. `HARNERY_AGENT_COORD_FRESHNESS` env * 2. `.harnery/config.jsonc` `coord.freshness_seconds` * 3. `600` (10 minutes) * `coordRoot` is resolved via `findCoordRoot()` when not passed. */ export declare function coordFreshnessSeconds(coordRoot?: string | null): number; /** * Active-segment byte size at which the V3 event ledger rotates to a fresh * epoch. Every reader validates the complete epoch, so an unbounded active * segment makes each cold read (one per hook process) scale with all history. * Precedence: * 1. `HARNERY_EVENT_V3_ROTATE_ACTIVE_BYTES` env * 2. `.harnery/config.jsonc` `events.rotate_active_bytes` * 3. 33554432 (32 MiB) * `0` (or a negative value) disables automatic rotation. */ export declare function eventLedgerRotateActiveBytes(coordRoot?: string | null): number; export interface EventLedgerArchivePolicy { maxBytes: number; maxAgeDays: number; keepMin: number; autoClean: boolean; } /** Bounded retention for closed V3 epochs under `ledgers/v3-archives`. */ export declare function eventLedgerArchivePolicy(coordRoot?: string | null): EventLedgerArchivePolicy; /** * Resolve the standalone dashboard port. * * Precedence: explicit `--port` flag, `HARNERY_WEB_PORT`, merged * `.harnery/config.jsonc` `web.port`, then the mnemonic built-in default 4276. */ export declare function resolveWebPort(explicitPort?: string, coordRoot?: string | null): number; /** Policy for converging independent termination signals on one finalizer. */ export declare function sessionFinalizationConfig(coordRoot?: string | null): SessionFinalizationConfig; /** * Default retention for a newly-created working artifact. Precedence: * `HARNERY_ARTIFACT_RETENTION_DAYS` -> project/user config * `artifacts.default_retention_days` -> 3 days. */ export declare function artifactDefaultRetentionDays(coordRoot?: string | null): number; /** Soft repository budget for managed working artifacts. */ export declare function artifactMaxBytes(coordRoot?: string | null): number; /** Size at which one bundle requires an explicit `artifacts create --big` acknowledgement. */ export declare function artifactMaxUnitBytes(coordRoot?: string | null): number; /** * Whether opportunistic cleanup of expired artifact workspaces runs. * Precedence: `HARNERY_ARTIFACT_AUTO_CLEAN` (0/false disables) -> * `artifacts.auto_clean` -> enabled. The sweep only ever deletes * `managed-expired` entries via the same guarded classifier as * `artifacts clean --yes`. */ export declare function artifactAutoCleanEnabled(coordRoot?: string | null): boolean; /** * Whether `qa-run` and `review-pack create` sweep expired page review packs * out of the artifact store before starting. Precedence: * `HARNERY_REVIEW_PACK_AUTO_CLEAN` (1/true enables, 0/false disables) -> * `review_pack.auto_clean` -> disabled. The sweep deletes only packs whose * manifest says `managed: true` with `retention.expires_at` in the past. */ export declare function reviewPackAutoCleanEnabled(coordRoot?: string | null): boolean; /** * Sweep cadence in hours. Env-only (`HARNERY_ARTIFACT_AUTO_CLEAN_INTERVAL_HOURS`, * mainly for tests). Session starts and new artifact work share the hourly * throttle so minute-scale retention is useful within long sessions. */ export declare function artifactAutoCleanIntervalHours(): number; /** Resolved `harn backup` defaults (restic repo/password + prune policy). */ export interface BackupConfig { repo: string; passwordFile: string; include: readonly string[]; exclude: readonly string[]; maxBytes: number; schedule: { ifStale: string; tags: readonly string[]; } | null; keepDaily: number; keepWeekly: number; keepMonthly: number; } /** * `harn backup` (restic) defaults. Per field, precedence is env → config → built-in: * repo: `HARNERY_RESTIC_REPO` → `backup.repo` → `~/.cache/harnery/restic-repo` * passwordFile: `HARNERY_RESTIC_PASSWORD_FILE` → `backup.password_file` → `~/.config/harnery/restic-password` * include/exclude/maxBytes/schedule: `backup.*` → catalog defaults / 50 MiB / disabled * keepDaily/Weekly/Monthly: `backup.keep_*` → 7 / 4 / 6 * `coordRoot` is resolved via `findCoordRoot()` when not passed. */ export declare function backupConfig(coordRoot?: string | null): BackupConfig; /** * `harn sync` (rclone) remote/prefix from `.harnery/config.jsonc` `sync`, or null * when unset. This is the config-file layer only; `harn sync` consults env * (`HARNERY_SYNC_REMOTE`/`_PREFIX`) first and the `~/.config/harnery/sync.json` * file (written by `harn sync init`) as a lower-precedence fallback. * `coordRoot` is resolved via `findCoordRoot()` when not passed. */ export declare function syncJsoncConfig(coordRoot?: string | null): { remote: string; prefix: string; } | null; //# sourceMappingURL=config.d.ts.map