/** * The init choice for a directory, stored **outside** it: its grant and, in v2, explicit project-ledger * consent. * * `PI_DADDY_GRANT` and `PI_DADDY_LEDGER` remain the propagation channel to children — a parent writes them * and every child inherits them. The store is a root-session source only, so an operator does not have to * `source` a file and restart pi after making the project choice. * * **Outside the workspace, and that is the whole design.** A grant is a ceiling; a ceiling a governed child * can rewrite is not a ceiling. `/.pi/pi-daddy/settings.json` is writable by any child holding `tool:write`, so * storing the live grant there would let a child widen the *next* session's ceiling — ADR-0014's * self-defeating case verbatim, which is why persisted approvals were moved out of the workspace in the * first place. This reuses that pattern exactly: `$PI_CODING_AGENT_DIR/pi-daddy/grants/-.json`, keyed by * the directory, unwritable by a narrowed child because a narrowed child holds no write access to `$HOME`. * * **It does not defend against a child holding `bash`** (ADR-0012). Nothing here does. * * `.pi/pi-daddy/settings.json` is written by `init` and is worth committing — it is the *reviewable record* * of the decision, diffable in a PR. It is not the thing the enforcer reads. */ import type { Capability } from "../kernel/resolve.ts"; export interface StoredGrant { grant: Capability[]; /** Whether a root session defaults to `/.pi/pi-daddy/grants.jsonl`. False for every legacy v1 store. */ projectLedger: boolean; } export type GrantStoreRefusalReason = "malformed" | "unsupported-version" | "unreadable" | "wrong-cwd"; export type StoredGrantState = { state: "absent"; } | { state: "valid"; stored: StoredGrant; } | { state: "refuse"; reason: GrantStoreRefusalReason; }; export interface SaveGrantOptions { projectLedger?: boolean; } /** * Where this directory's grant lives. * * Slug plus a 64-bit hash, exactly as `approvalsPath` does it and for the same two reasons: the basename * keeps the directory legible to a human reading it, and the hash is what makes it unambiguous, since two * checkouts can share a basename. */ export declare function grantStorePath(cwd: string): string; /** * Parse a store file's text into its versioned project choice, or null. * * Split out from the readers so the **one** validation lives in one place: both the sync and async paths * must agree, and two parsers is how they come to disagree. * * Rejects every doubt at this parser boundary — it never constructs a partial ceiling. The session currently * treats absent and invalid stores alike (R-175), so do not call that wider behavior fail-closed. The `cwd` * check is R-27's: a copied file describes a directory nobody authorised it for. */ export declare function parseStoredGrantState(text: string, cwd: string): StoredGrantState; export declare function parseStoredGrant(text: string, cwd: string): StoredGrant | null; /** Compatibility view for callers that need only the capability ceiling. */ export declare function parseGrantFile(text: string, cwd: string): Capability[] | null; /** * Read this directory's stored grant, synchronously. * * **Sync on purpose, and this is the constraint that shapes the feature.** Whether `delegate` is registered * at all is decided when the extension factory runs (S-5: a session without `tool:delegate` must not be * offered the tool), and that is before any `await` is possible. An async read would resolve after the * decision it exists to inform, so the store would silently fail to grant delegation — the exact class of * defect R-38 and R-39 were. */ export declare function loadStoredGrantStateSync(cwd: string): StoredGrantState; export declare function loadStoredGrantSync(cwd: string): StoredGrant | null; export declare function loadGrantSync(cwd: string): Capability[] | null; /** Async twin, for callers that already have one. Same parser, so they cannot disagree. */ export declare function loadStoredGrantState(cwd: string): Promise; export declare function loadStoredGrant(cwd: string): Promise; export declare function loadGrant(cwd: string): Promise; /** The one project-local default ADR-0037 binds to an explicit v2 init choice. */ export declare function projectLedgerPath(cwd: string): string; export type SaveOutcome = "saved" | "failed" | "busy"; /** * Write this directory's grant and optional project-ledger consent as one atomic choice. * * Locked with the same lock the ledger and the approval store use, for the same reason: two sessions in one * directory running `/grants init` concurrently must not interleave. A lock this cannot take yields `busy` * and changes nothing — the caller reports it and the operator retries, which is the honest outcome for a * write that never looked at the file (R-68). * * `wx` on the temp file refuses to follow a pre-existing symlink, and `rename` is atomic within a * filesystem, so a reader never sees a half-written grant. Both copied from `writeFileSafely`, deliberately * — a second, subtly different atomic-write is how the two come to disagree about what "safe" meant. */ export declare function saveGrant(cwd: string, grant: Capability[], options?: SaveGrantOptions): Promise; /** Remove this directory's stored grant. True when a file was there to remove. */ export declare function clearGrant(cwd: string): Promise; //# sourceMappingURL=grant-store.d.ts.map