/** * * Project-level red line: no autonomous LLM path is allowed to create a * Git worktree, switch Claude Code's Agent isolation to "worktree", * or fabricate a clean-tree baseline via `git stash` — unless the user * has, in the *current task / current session*, explicitly authorized * that exact operation. Authorization is recorded by * `peaks worktree auth grant` (see `worktree-auth-commands.ts`) and is * scoped to: * * - sessionId: the active peaks session at grant time * - requestId: the active peaks request at grant time (if any) * - operation: one of the OperationType values * - expiresAt: hard deadline (default +5 min from grant) * - consumed: single-use vs multi-use (gate calls `consume()` on use) * * The gate is invoked from `peaks gate enforce` (the PreToolUse hook * command) for every Bash / Agent / EnterWorktree tool call. It is * fail-closed: a malformed grant file, a missing scope, an expired * token, or any other evaluation error → deny with a structured * `WORKTREE_USER_AUTH_REQUIRED` reason. * * Why this is a service, not just a regex: a regex can match the * command but cannot check "did the current user just say so". The * user-originated authorization is a current-task file artifact; * the gate is its consumer. * * Why single-use by default: an LLM can pass the gate once per * authorized intent. If the same LLM needs to run a second * `git worktree add` later, it must `peaks worktree auth grant` * again — which is exactly the moment the user can say no. Multi-use * is a deliberate opt-in via `--multi`. */ import { type WorktreeLease } from '../worktree/worktree-lease.js'; import { type ContainerLease } from '../container/container-lease.js'; export declare const WORKTREE_AUTH_FILE = "worktree-auth.json"; /** The narrow surface the gate actually inspects. Keep this list in lock-step with the regex matchers below. */ export type OperationType = /** `git worktree add` / `git worktree remove` / `git worktree prune` — any Bash command matching the gate's allow-list. */ 'git-worktree' /** Claude Code `Agent` sub-agent dispatched with `isolation: "worktree"`. */ | 'agent-isolation-worktree' /** Bash command starting with `git stash` (push / pop / create / drop / store). Excludes `git stash list` / `git stash show`. */ | 'git-stash-mutating' /** Bash command that calls the `git` CLI in any other worktree-mutating form (rare; explicit escape hatch). */ | 'git-worktree-other'; /** Structured shape of the on-disk authorization record. Always has a `grants` array; older shapes were single-grant. */ export type AuthorizationFile = { readonly schemaVersion: 1; readonly sessionId: string; readonly createdAt: string; readonly grants: ReadonlyArray; }; export type WorktreeAuthorization = { readonly operation: OperationType; readonly reason: string; /** 16-hex SHA-1 prefix of the prompt at grant time. Optional; the gate may match by operation only. */ readonly promptHash: string | null; /** Optional rid scope — when set, the gate will only honor the grant for tool calls whose transcript carries this rid. */ readonly requestId: string | null; readonly issuedAt: string; readonly expiresAt: string; /** `true` = single-use (consumed on first match), `false` = multi-use (only bounded by `expiresAt`). */ readonly consume: boolean; /** `true` after the gate's `consume()` call. Exposed for status; gate sets it. */ readonly consumed: boolean; }; /** Result of a gate check. `allow` permits the tool call; `deny` blocks it. */ export type WorktreeAuthDecision = { readonly allow: true; readonly authorization: WorktreeAuthorization; readonly remaining: number; readonly viaLease: null; } | { readonly allow: true; readonly authorization: null; readonly remaining: 0; readonly viaLease: WorktreeLease | ContainerLease; } | { readonly allow: false; readonly reason: string; readonly code: WorktreeAuthDenyCode; readonly remediation: string; }; export type WorktreeAuthDenyCode = /** No current-task grant for the operation. */ 'WORKTREE_USER_AUTH_REQUIRED' /** Grant exists but `expiresAt` has passed. */ | 'WORKTREE_USER_AUTH_EXPIRED' /** Grant exists but is for a different requestId (current tool call's rid does not match). */ | 'WORKTREE_USER_AUTH_REQUEST_MISMATCH' /** Grant exists but was already consumed and the operation is single-use. */ | 'WORKTREE_USER_AUTH_CONSUMED' /** The grant file is unreadable / malformed — fail-closed, never fail-open. */ | 'WORKTREE_USER_AUTH_FILE_INVALID' /** Lease file referenced by the tool call is unreadable / malformed. */ | 'WORKTREE_LEASE_FILE_INVALID' /** Lease exists but is not active (status != 'active' OR past expiresAt). */ | 'WORKTREE_LEASE_NOT_ACTIVE' /** Lease exists but its rid does not match the current peaks request. */ | 'WORKTREE_LEASE_REQUEST_MISMATCH' /** Slice 2026-07-29-worktree-l2-extended Part 19: container * lease id is set but the lease file is unreadable / malformed. */ | 'CONTAINER_LEASE_FILE_INVALID' /** Host Agent/Task worktree isolation was not issued by Peaks dispatch. */ | 'HOST_AGENT_ISOLATION_UNMANAGED' /** Container lease exists but is not active (status != 'active' OR past expiresAt). */ | 'CONTAINER_LEASE_NOT_ACTIVE' /** Container lease exists but its rid does not match the current peaks request. */ | 'CONTAINER_LEASE_REQUEST_MISMATCH'; export type ToolCallKind = 'Bash' | 'Agent' | 'EnterWorktree' | 'Workflow' | 'Other'; /** What the gate receives from the hook payload. Only fields it inspects. */ export type WorktreeAuthCheckInput = { readonly projectRoot: string; readonly sessionId: string; readonly toolName: ToolCallKind; /** `Bash` → `command`; `Agent` → `prompt` (irrelevant for gate; pass through) and `isolation`. */ readonly command: string | null; /** `Agent` tool calls. The gate only blocks when this is `'worktree'`. */ readonly isolation: string | null; /** Optional rid scope from the calling peaks request artifact. */ readonly requestId: string | null; /** * Optional lease id consulted as a SECOND authorization path when no * `peaks worktree auth grant` token is on file. When set, the gate * reads `.peaks/_runtime//worktree-leases/.json` and * permits the operation iff the lease is `isLeaseActive` and its * `rid` matches the current peaks request. `PEAKS_WORKTREE_LEASE_ID` * env is the canonical source (dispatch injects it; the hook reads * it). When null, the lease fallback is skipped — the gate falls * back to the existing `peaks worktree auth grant` only contract. */ readonly leaseId: string | null; /** * lease id (parallel to `leaseId` for container isolation). * `PEAKS_CONTAINER_LEASE_ID` env is the canonical source. * Permits docker / podman tool calls when the container lease * is active and its rid matches. */ readonly containerLeaseId: string | null; /** Peaks-issued provenance token required for host Agent/Task worktree isolation. */ readonly dispatchProvenanceToken?: string | null; }; /** Stable identifier of the current "user authorization" — derived from session + tool + key args. */ export declare function currentAuthFingerprint(input: WorktreeAuthCheckInput): string; /** The file the gate reads. Default location: `.peaks/_runtime//worktree-auth.json`. */ export declare function worktreeAuthFilePath(projectRoot: string, sessionId: string): string; /** * Read + validate the on-disk grant file. Returns `null` for "no file" (not an error — just no auth * recorded yet). Throws `WorktreeAuthFileInvalidError` for malformed JSON / wrong shape / wrong * sessionId — the gate treats this as deny, not allow. We never silently fail open. */ export declare function readAuthorization(projectRoot: string, sessionId: string): AuthorizationFile | null; export declare class WorktreeAuthFileInvalidError extends Error { readonly path: string; constructor(message: string, opts: { path: string; }); } /** * Map the actual tool call to an `OperationType` the gate should look up. Returns `null` for tool calls * the gate does not care about (e.g. `Read`, `Glob`, or `Bash` running `ls`). The regex is deliberately * conservative: anything ambiguous returns `null` (allow). */ export declare function classifyToolCall(input: WorktreeAuthCheckInput): OperationType | null; /** * Core gate decision. Pure: takes a `WorktreeAuthCheckInput` + the parsed `AuthorizationFile`, * returns `WorktreeAuthDecision`. Side effects (consume / file rewrite) are NOT performed here — see * `evaluateWorktreeAuth` for the write-then-evaluate pattern that `peaks gate enforce` uses. */ export declare function decideFromAuthorization(input: WorktreeAuthCheckInput, operation: OperationType, file: AuthorizationFile | null): WorktreeAuthDecision; /** * Public entry point used by `peaks gate enforce`. Reads the auth file, decides, and (when consume=true * and the call is allowed) atomically rewrites the file with `consumed: true` on the matching grant. * * Returns `deny` on any internal error: malformed file, IO failure, missing session binding, etc. The * gate is fail-closed — never allow on error. */ export declare function evaluateWorktreeAuth(input: WorktreeAuthCheckInput): WorktreeAuthDecision; /** * Part 19: L4 container lease fallback. Reads the container * lease file at `.peaks/_runtime//container-leases/.json` * and permits the operation iff the lease is `isContainerLeaseActive` * and its `rid` matches the current peaks request. Mirrors * `decideFromLease` for the L4 container surface; the L2 worktree * lease is the L2 path, this is the L4 path. * * On malformed file / non-active / rid mismatch the function * fails closed with the new error codes * (CONTAINER_LEASE_FILE_INVALID / NOT_ACTIVE / REQUEST_MISMATCH). */ export declare function decideFromContainerLease(input: WorktreeAuthCheckInput): WorktreeAuthDecision; /** * Pure: consult the lease file referenced by `input.leaseId` and decide * whether the operation may proceed under the lease. Mirrors the * `decideFromAuthorization` contract: malformed lease → fail-closed; * rid mismatch → deny; non-active lease → deny. */ export declare function decideFromLease(input: WorktreeAuthCheckInput): WorktreeAuthDecision; /** CLI writer used by `peaks worktree auth grant`. Replaces (does not merge) the grant list — pass * the existing grants through if you want a multi-grant file. */ export declare function writeAuthorization(projectRoot: string, sessionId: string, authorization: WorktreeAuthorization): void; /** Revoke (delete) every unconsumed grant in the file. Used by `peaks worktree auth revoke`. */ export declare function clearAllGrants(projectRoot: string, sessionId: string): { removed: number; };