/**
 * Filesystem path policy — defense-in-depth guard for native file tools.
 *
 * Background — issue #3 (private):
 *   `write.ts`, `edit.ts`, and `notebook-edit.ts` previously called
 *   `path.resolve(input.path)` and went straight to `writeFile`. They
 *   trusted the conversation layer's task-scope approval to be the only
 *   safety net. That works when the only caller is a humans-in-the-loop
 *   REPL session, but every new caller (headless mode, sub-agents, MCP
 *   bridges, future remote tools) inherits the assumption silently. The
 *   tools themselves were not defensive.
 *
 *   This module is the per-tool last line of defense: even if an upstream
 *   approval layer said yes, the path itself must still survive the
 *   guard.
 *
 * Write rules — small, deterministic, no policy DSL:
 *   1. Reject empty / non-string / whitespace-only paths.
 *   2. Resolve to an absolute path; reject anything outside the configured
 *      workspace root after resolution. Default root = `process.cwd()` at
 *      call time. Extra allowed roots may be added via the env var
 *      `OWLCODA_ALLOW_FS_ROOTS=<root1>:<root2>` (POSIX `:` separator).
 *   3. Reject sensitive locations regardless of workspace overlap:
 *      ~/.ssh, ~/.aws, ~/.gnupg, ~/.config/gh, /etc, /var, /usr, /bin,
 *      /sbin, /System (macOS), /Library (macOS system), /boot, and
 *      OWLCODA_HOME itself (don't let an agent rewrite its own config).
 *   4. Resolve symlinks on every existing parent component and reject
 *      when the real path leaves the allowed root. New files (whose own
 *      target doesn't exist yet) are still validated by walking up to
 *      the nearest existing parent.
 *
 * Read rules:
 *   - Reuse the same normalization, tilde expansion, symlink-parent
 *     resolution, and sensitive-path deny list.
 *   - Do not enforce the write-side workspace boundary yet: read tools have
 *     historically been low-friction for absolute temp files and repo-adjacent
 *     diagnostics. The P1 security invariant is that sensitive paths are
 *     denied by OwlCoda itself instead of relying on OS permissions.
 *
 * What this is NOT:
 *   - Not an approval system. It can only say allow/deny based on the
 *     target path itself; it does not see who is calling.
 *   - Not a replacement for task-scope approval (`evaluateWriteGuard`).
 *     They run independently — task-scope at the conversation loop,
 *     this one inside the tool itself.
 */
export interface FsPolicyOptions {
    /** Workspace root that writes must stay inside. Default: `process.cwd()`. */
    workspaceRoot?: string;
    /** Extra allowed roots beyond the workspace. Useful for tests / future task-contract bridges. */
    allowedRoots?: string[];
    /**
     * User-declared external deliverable scopes from `extractUserDeclaredExternalRoots`.
     * Distinct from `allowedRoots` because each scope carries a `kind`:
     *   - kind === 'file': exact-match only — descendants are denied.
     *   - kind === 'directory': exact or descendant paths allowed (same as allowedRoots entries).
     *
     * Env-var OWLCODA_ALLOW_FS_ROOTS entries always behave as 'directory' scopes (no kind annotation).
     */
    externalScopes?: Array<{
        path: string;
        kind: 'file' | 'directory';
    }>;
    /** Override the env-var lookup (test seam). */
    envAllowFsRoots?: string | undefined;
    /** Override OWLCODA_HOME lookup (test seam). */
    owlcodaHome?: string | undefined;
    /** Override homedir lookup (test seam). */
    homeDir?: string;
    /** Override platform lookup (test seam). */
    platformName?: NodeJS.Platform;
}
export type FsPolicyResult = {
    allowed: true;
    resolvedPath: string;
} | {
    allowed: false;
    reason: string;
    attemptedPath: string;
};
/**
 * Evaluate whether a write to `targetPath` should be allowed under the
 * current policy. Pure: never mutates the filesystem, never throws —
 * always returns a structured decision.
 *
 * Callers are expected to short-circuit on `allowed: false` and surface
 * `reason` to the caller (or LLM) without performing the underlying
 * `writeFile`/`rename`/`unlink`/etc. This is the contract the tests
 * pin: rejection happens BEFORE mutation.
 */
export declare function checkWritePathAllowed(targetPath: unknown, opts?: FsPolicyOptions): FsPolicyResult;
/**
 * Evaluate whether a file read should be allowed under the current policy.
 *
 * Reads intentionally keep their historical broad path reach, but they now
 * share the write-side normalization and sensitive-path deny list. This closes
 * the gap where `Read({path: "~/.ssh/id_rsa"})` or `/etc/shadow` was limited
 * only by OS permissions.
 */
export declare function checkReadPathAllowed(targetPath: unknown, opts?: FsPolicyOptions): FsPolicyResult;
