{"version":3,"file":"node.mjs","names":[],"sources":["../../../src/batteries/sandbox/node/srt_enforcer.ts","../../../src/batteries/sandbox/node/search_ripgrep.ts"],"sourcesContent":["import { statSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { spawn } from 'node:child_process'\nimport { quoteShellArgs, validateAbsoluteBinaryPath, validateBinShell } from '../escape'\nimport { derivedRulesFromSrt, primeGlobMatcher, reproduceMandatoryDeny } from './fs_node'\nimport {\n  E_INVALID_SANDBOX_CONFIG,\n  E_SANDBOX_UNSUPPORTED_ENV,\n  E_SANDBOX_POLICY_CONFLICT,\n} from '../exceptions'\nimport type { SandboxPolicy, DerivedRules } from '../types'\nimport type { SandboxPolicyEnforcer } from '../contracts/policy_enforcer'\n\ntype SrtManager = {\n  initialize(config: SrtConfig): Promise<void>\n  isSupportedPlatform(): boolean\n  isSandboxingEnabled(): boolean\n  checkDependenciesAsync(): Promise<{ errors: string[]; warnings: string[] }>\n  wrapWithSandboxArgv(\n    command: string,\n    shell?: string,\n    custom?: unknown,\n    signal?: AbortSignal,\n    cwd?: string,\n    options?: { commandId?: string }\n  ): Promise<{ argv: string[]; env: Record<string, string> }>\n  getFsReadConfig(): { denyOnly: string[]; allowWithinDeny?: string[] }\n  getFsWriteConfig(): { allowOnly: string[]; denyWithinAllow: string[] }\n  getNetworkRestrictionConfig(): {\n    allowedHosts?: string[]\n    deniedHosts?: string[]\n  }\n  getConfig(): SrtConfig | undefined\n  getSandboxViolationStore(): {\n    getViolationsForCommand(command: string): unknown[]\n  }\n  reset(): Promise<void>\n}\ntype SrtConfig = {\n  filesystem: Record<string, unknown>\n  network?: Record<string, unknown>\n  /** SRT's Linux mandatory-deny scan depth is session-level. */\n  mandatoryDenySearchDepth?: number\n  git?: { safeDirectories: string[] }\n  /** Top-level and session-level: SRT reads this from the config `initialize()` stored, not per call. */\n  enableWeakerNestedSandbox?: boolean\n  bwrapPath?: string\n  socatPath?: string\n}\n\nconst unsupported = (): never => {\n  throw new E_SANDBOX_UNSUPPORTED_ENV([\n    `SRT sandbox is unavailable on ${process.platform}; use WSL2 or the SES browser path`,\n  ])\n}\nconst nonEmpty = (v: readonly string[] | undefined): string[] => [...(v ?? [])]\n\n/**\n * Host variables a sandboxed child inherits when the caller names no allow-list.\n *\n * @remarks\n * `PATH` ONLY, and it is the minimum rather than a convenience. The ripgrep searcher spawns `rg` by\n * BARE NAME, so with no `PATH` the child sees only the one a shell synthesises\n * (`/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.`) — which does not contain `/opt/homebrew/bin` or a Nix\n * profile, so `search_files` would fail as `io-failure` on any such host and present as *\"the search\n * tool is broken\"* rather than as a configuration error.\n *\n * `PATH` is not a credential: a model that runs `env` learns where binaries live, not a secret. Every\n * genuinely sensitive variable — and `HOME`, `USER`, `TMPDIR`, which leak host layout — stays out.\n */\nconst DEFAULT_ENV_ALLOW_LIST: readonly string[] = ['PATH']\n\n/** POSIX environment-variable names. Anything else could not be exported by a shell anyway. */\nconst ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/\n\n/**\n * Resolve the host half of a sandboxed child's environment ONCE, at construction.\n *\n * @remarks\n * DENY BY DEFAULT. Before this existed the enforcer spread the entire `process.env` into every child,\n * so a model directing the shell's argv could run `env` and read the host's credentials straight back\n * into its own context — which no filesystem or network policy prevents, because the secret arrives in\n * the tool result rather than over the wire.\n *\n * The allow-list REPLACES this default rather than extending it; that is stated on the option, because\n * the opposite assumption is the natural one and would silently drop `PATH`.\n *\n * @param options - The enforcer's construction options.\n * @returns The host variables to seed the child's environment with.\n */\nconst resolveHostEnv = (options: SrtEnforcerOptions): Record<string, string> => {\n  if (options.inheritHostEnv === true) {\n    const all: Record<string, string> = {}\n    for (const [name, value] of Object.entries(process.env))\n      if (value !== undefined) all[name] = value\n    return all\n  }\n  const names = options.envAllowList ?? DEFAULT_ENV_ALLOW_LIST\n  const picked: Record<string, string> = {}\n  for (const name of names) {\n    if (!ENV_NAME.test(name))\n      throw new E_INVALID_SANDBOX_CONFIG([\n        `envAllowList entry ${JSON.stringify(name)} is not a valid environment variable name`,\n      ])\n    const value = process.env[name]\n    if (value !== undefined) picked[name] = value\n  }\n  return picked\n}\n\n/**\n * Whether `<cwd>/.git` is a real DIRECTORY.\n *\n * @remarks\n * Load-bearing on Linux only: upstream pushes the workspace-root `.git/hooks` and `.git/config` deny\n * entries just when `.git` is a directory, because in a worktree it is a FILE and denying it would\n * make bubblewrap fail. Treating a worktree as a directory would deny what the profile permits.\n */\nconst dotGitIsDirectory = (cwd: string): boolean => {\n  try {\n    return statSync(resolve(cwd, '.git')).isDirectory()\n  } catch {\n    return false\n  }\n}\n\n/**\n * The sole ADK-to-SRT translation point. Keep the upstream type behind this local firewall.\n *\n * @remarks\n * `runtime` carries ADAPTER-level settings that are deliberately absent from `SandboxPolicy`. That\n * type is ADK-owned, SRT-neutral vocabulary — naming an SRT feature in it would make the firewall\n * nominal and leave a non-SRT enforcer unable to implement the contract — so an SRT-specific switch\n * belongs on the adapter's own options, exactly as `binShell` already does.\n */\nconst mapPolicy = (\n  policy: SandboxPolicy,\n  runtime: {\n    enableWeakerNestedSandbox?: boolean\n    bwrapPath?: string\n    socatPath?: string\n  } = {}\n): SrtConfig => {\n  const fs = policy.filesystem\n  const net = policy.network\n  if (\n    net.disabled &&\n    (net.deniedDomains?.length ||\n      Object.keys(net.deniedDomainReasons ?? {}).length ||\n      (net.allowedDomains && !(net.allowedDomains.length === 1 && net.allowedDomains[0] === '*')))\n  )\n    throw new E_INVALID_SANDBOX_CONFIG(['network.disabled contradicts network domain restrictions'])\n  const allowedDomains = net.disabled ? ['*'] : nonEmpty(net.allowedDomains)\n  const deniedDomains = net.disabled ? [] : nonEmpty(net.deniedDomains)\n  const deniedDomainReasons = net.disabled ? {} : { ...(net.deniedDomainReasons ?? {}) }\n  return {\n    ...(fs.mandatoryDenySearchDepth !== undefined\n      ? { mandatoryDenySearchDepth: fs.mandatoryDenySearchDepth }\n      : {}),\n    // Emitted only when enabled, so an untouched configuration is byte-identical to before this\n    // option existed — a drift snapshot taken either side of the upgrade compares equal.\n    ...(runtime.enableWeakerNestedSandbox === true ? { enableWeakerNestedSandbox: true } : {}),\n    ...(runtime.bwrapPath !== undefined ? { bwrapPath: runtime.bwrapPath } : {}),\n    ...(runtime.socatPath !== undefined ? { socatPath: runtime.socatPath } : {}),\n    filesystem: {\n      ...(fs.disabled ? { disabled: true } : {}),\n      denyRead: nonEmpty(fs.denyRead),\n      allowRead: nonEmpty(fs.allowRead),\n      allowWrite: nonEmpty(fs.allowWrite),\n      denyWrite: nonEmpty(fs.denyWrite),\n      allowGitConfig: fs.allowGitConfig ?? false,\n    },\n    // SRT keys network restriction by presence of this object. There is no schema-valid allow-all\n    // domain pattern, so disabled deliberately omits the key instead of emitting the rejected '*'.\n    ...(!net.disabled\n      ? {\n          network: {\n            allowedDomains,\n            deniedDomains,\n            deniedDomainReasons,\n            strictAllowlist: true,\n            allowLocalBinding: false,\n            allowUnixSockets: [],\n            allowMachLookup: [],\n          },\n        }\n      : {}),\n    git: {\n      safeDirectories: nonEmpty(fs.gitSafeDirectories ?? [process.cwd()]),\n    },\n  }\n}\n\nconst toWeb = (stream: NodeJS.ReadableStream): ReadableStream<Uint8Array> =>\n  (\n    ReadableStream as unknown as {\n      from?: (x: unknown) => ReadableStream<Uint8Array>\n    }\n  ).from\n    ? (\n        ReadableStream as unknown as {\n          from: (x: unknown) => ReadableStream<Uint8Array>\n        }\n      ).from(stream)\n    : new ReadableStream({\n        start(controller) {\n          stream.on('data', (x: Buffer) => controller.enqueue(new Uint8Array(x)))\n          stream.on('end', () => controller.close())\n          stream.on('error', (e) => controller.error(e))\n        },\n      })\n\n/** Options for {@link srtEnforcer}. */\n/**\n * Whether an enforcer in THIS process performed the `initialize()` that is currently in force.\n *\n * @remarks\n * UPSTREAM'S OWN FLAG CANNOT ANSWER THIS. `isSandboxingEnabled()` is literally `config !== undefined`\n * (`sandbox-manager.js:672-675`) and `reset()` clears `managerContext`, the proxies, the registries —\n * **but never `config`**. So after any reset the flag stays `true` forever, and an enforcer that\n * trusted it would \"adopt\" a sandbox that is no longer in force, silently reporting a stale policy\n * and refusing to re-initialise. Verified: initialize, reset, and `isSandboxingEnabled()` is still\n * `true`.\n *\n * We therefore track what WE established. `true` here means this module owns the live session, so a\n * subsequent construction must re-initialise rather than adopt. `false` with upstream reporting\n * enabled means somebody else got there first — the genuine adoption case.\n */\nlet selfInitialized = false\n\n/**\n * Whether the session THIS module established is still claimed by a live enforcer.\n *\n * @remarks\n * FIRST-WRITER-WINS IS ENFORCED HERE, not only in the manager. `manager.ts` admits or refuses a\n * second handle by comparing policies — but that check runs AFTER `srtEnforcer()` has already\n * constructed, and construction is what touches the process-global. So a reset-and-reinitialise in the\n * constructor would tear down a live policy before anything could refuse it: caller 2 widens the real\n * sandbox, the manager then throws, and caller 1 carries on against a baseline that is no longer in\n * force. The refusal would arrive after the damage.\n *\n * `claimed` is therefore set when we initialise and cleared only by `dispose()`. While it is `true`,\n * re-initialising is refused outright; once cleared, the next construction may re-establish a session,\n * which is what makes sequential owned handles work.\n */\nlet selfSessionClaimed = false\n\n/**\n * Relinquish this module's ownership marker.\n *\n * @remarks\n * TEST SEAM, and a narrow one. The marker exists because upstream's `isSandboxingEnabled()` is\n * `config !== undefined` and `reset()` never clears `config` — so after the first `initialize()` the\n * flag is permanently `true` and cannot by itself distinguish *\"a session we established\"* from\n * *\"somebody else's\"*. Ownership therefore persists for the life of the module, which is correct in\n * production (a process that initialised once keeps re-initialising its own session) but makes the\n * ADOPTION branch unreachable in a test file that has already constructed an owned enforcer.\n *\n * Production code has no reason to call this: relinquishing ownership while a session we established\n * is still live would make the next construction adopt it and refuse to re-initialise.\n */\nexport const releaseSrtOwnershipForTests = (): void => {\n  selfInitialized = false\n  selfSessionClaimed = false\n}\n\n/** Construction options for {@link srtEnforcer}. */\nexport type SrtEnforcerOptions = {\n  /**\n   * Absolute path to the POSIX shell used to invoke wrapped commands. Defaults to `/bin/bash`.\n   *\n   * @remarks\n   * Validated at construction in TWO checks, both required: it must be ABSOLUTE (a bare `bash` passes\n   * any basename test while remaining `PATH`-dependent — the exact hazard the absolute default\n   * avoids), and its basename must be on the allow-list (`sh`, `bash`, `dash`, `zsh`, `ksh`) — the\n   * shells whose quoting the single escaper is correct for. An allow-list rather than a deny-list is\n   * deliberate: `fish` and `nu` are POSIX-ish enough to look safe and different enough to break\n   * single-quote escaping, so an unverified shell must fail closed.\n   */\n  binShell?: string\n  /**\n   * The ADK-owned policy to enforce.\n   *\n   * @remarks\n   * Mapped to SRT's config inside this module and nowhere else. The derived baseline is captured\n   * immediately after `initialize()`, because `SandboxManager` is a process-global singleton whose\n   * SECOND `initialize()` is a no-op — a later call with a different policy silently keeps the first.\n   */\n  policy: SandboxPolicy\n  /**\n   * Host environment variable NAMES a sandboxed child may inherit. Defaults to `['PATH']`.\n   *\n   * @remarks\n   * The child inherits NOTHING from the host beyond these names. That default is deliberate: a model\n   * that can direct the shell's argv can run `env`, so anything inherited is readable back into its\n   * context — and no filesystem or network policy stops it, because the value arrives in the tool\n   * result rather than over the wire.\n   *\n   * **This REPLACES the default, it does not extend it.** A caller who needs `CARGO_HOME` and still\n   * wants binaries to resolve must pass BOTH: `['PATH', 'CARGO_HOME']`. Passing `['CARGO_HOME']` alone\n   * drops `PATH`, which breaks `search_files` on any host where `rg` lives outside `/usr/bin`.\n   *\n   * An entry that is not a valid POSIX environment-variable name throws `E_INVALID_SANDBOX_CONFIG` at\n   * construction rather than being skipped, so a typo surfaces as a startup error instead of a\n   * variable that silently never arrives.\n   */\n  envAllowList?: readonly string[]\n  /**\n   * Pass the ENTIRE host environment to sandboxed children. Defaults to `false`.\n   *\n   * @remarks\n   * The escape hatch for a deployment that genuinely needs ambient configuration, and it is worth\n   * being blunt about what it re-opens: **every secret in the host process becomes readable by the\n   * model**, because `run_shell_command` exists precisely to run commands the model chose and `env` is\n   * one of them. Prefer naming what you need in `envAllowList`.\n   */\n  inheritHostEnv?: boolean\n  /**\n   * Absolute path to a Linux bubblewrap wrapper or binary. SRT reads it from the config passed to\n   * `initialize()`; on the Linux launch path it becomes argv[0] of the bwrap command\n   * (`linux-sandbox-utils.js:1539`). A wrapper script placed at this path therefore receives every\n   * bwrap argument and can strip or rewrite options such as `--unshare-net`. On Linux a nonexistent\n   * path fails in SRT's `initialize()`; on macOS it is neither validated nor used.\n   */\n  bwrapPath?: string\n  /**\n   * Absolute path to a Linux socat binary or wrapper. SRT passes it to `initializeLinuxNetworkBridge`\n   * on Linux. A nonexistent path fails in SRT's `initialize()`; on macOS it is neither validated nor\n   * used.\n   */\n  socatPath?: string\n  /**\n   * Enable SRT's weaker nested-sandbox mode, for running inside an unprivileged container.\n   *\n   * @remarks\n   * Bubblewrap cannot mount a fresh `/proc` inside an unprivileged container, so the sandbox fails to\n   * start at all — the symptom is `apply-seccomp: write /proc/self/uid_map: Operation not permitted`\n   * with exit 1, an empty stdout and NO diagnostics, which is indistinguishable from a policy denial.\n   * This flag makes the inner sandbox bind-mount the container's EXISTING `/proc` instead.\n   *\n   * **It considerably weakens the boundary**, in upstream's own words: the bind-mounted `/proc`\n   * exposes process information a fresh mount would hide. Only enable it when the OUTER container\n   * already provides the isolation you need — it trades inner isolation for the sandbox running at all.\n   *\n   * Session-level: SRT reads it from the config given to `initialize()`, never per call.\n   */\n  enableWeakerNestedSandbox?: boolean\n}\n\n/**\n * Construct the SRT-backed policy enforcer — the OS boundary.\n *\n * @remarks\n * REFUSES rather than degrades on an unsupported platform: native `win32` throws\n * `E_SANDBOX_UNSUPPORTED_ENV` naming WSL2, because Windows folds all four filesystem-policy lists\n * case-insensitively and throws on per-exec allows. A half-built branch there would be a DIVERGENT\n * boundary rather than a limited one, so there is deliberately no native-Windows evaluator.\n *\n * `run()` resolves on SPAWN with live streams plus a separate `completed` promise, never a settled\n * exit code. That split is not stylistic: one promise cannot both hand over unread child streams AND\n * carry an exit code, because settling requires the streams to have ended and they cannot end before\n * someone drains them. A caller MUST drain both concurrently — pipe buffers are per-fd, so draining\n * one to completion first can block the other and hang the child.\n *\n * @param options - Shell and policy configuration.\n * @returns An enforcer whose derived baseline is already captured.\n */\nexport const srtEnforcer = async (options: SrtEnforcerOptions): Promise<SandboxPolicyEnforcer> => {\n  if (process.platform !== 'darwin' && process.platform !== 'linux') return unsupported()\n  const binShell = validateBinShell(options.binShell)\n  const bwrapPath = validateAbsoluteBinaryPath(options.bwrapPath, 'bwrapPath')\n  const socatPath = validateAbsoluteBinaryPath(options.socatPath, 'socatPath')\n  // Resolved ONCE: the allow-list is fixed for the enforcer's life, so re-picking per spawn would only\n  // add a chance for the two to disagree. Validation throws here, at construction, so a typo'd variable\n  // name is a startup error rather than a variable that silently never arrives.\n  const hostEnv = resolveHostEnv(options)\n  const srt = (await import('@anthropic-ai/sandbox-runtime')) as unknown as {\n    SandboxManager: SrtManager\n  }\n  const manager = srt.SandboxManager\n  // SandboxManager is process-global and first-writer-wins. Detect before initialize: an already\n  // enabled manager belongs to somebody else, and initializing it (even with a different policy)\n  // would leave this handle describing a sandbox that is not in force.\n  // ADOPT only when the live session is somebody ELSE'S. See `selfInitialized`: upstream's flag is\n  // sticky across `reset()`, so it alone would make every enforcer after the first one adopt.\n  // CLAIM FIRST, CLASSIFY SECOND — and both synchronously, because the boundary is a process-global\n  // with no lock to take. An earlier revision computed `adopted` from `isSandboxingEnabled()` and only\n  // claimed after `initialize()` resolved, which left TWO windows for concurrent constructions:\n  //   · both read `claimed === false`, both proceeded, and the second re-initialised over the first;\n  //   · worse, the second saw `enabled === true` (the first had just initialised) with our marker not\n  //     yet set, so it classified a session WE established as FOREIGN and adopted it — reporting the\n  //     other caller's policy as though it were an adopted third-party sandbox.\n  // Verified both: `Promise.all` of two enforcers resolved BOTH, the second with `adopted: true` and\n  // the first's derived lists. Claiming before any await closes them together.\n  const claimedByUs = selfSessionClaimed\n  const alreadyLive = manager.isSandboxingEnabled()\n  const adopted = alreadyLive && !selfInitialized && !claimedByUs\n  if (!adopted) {\n    // RESET BEFORE RE-INITIALISING OUR OWN SESSION. Upstream's `initialize()` assigns `config`\n    // unconditionally, but the DERIVED getters are computed from the managerContext built during\n    // initialisation — so calling it twice without a reset leaves the FIRST policy's derived lists in\n    // force. Verified: init A, then init B, and `getFsReadConfig()` still reports A; reset then init C\n    // reports C. Without this, a second `srtEnforcer()` in one process silently enforces the previous\n    // policy while reporting the new one.\n    //\n    // Only ever our OWN session: `adopted` is false here, and `selfInitialized` proves we established\n    // what is currently live rather than inheriting somebody else's.\n    //\n    // AND ONLY A RELEASED ONE. Resetting a session a live handle still holds would silently replace\n    // that handle's policy with this one — first-writer-wins broken in the constructor, before the\n    // manager's admission check can refuse anything. Refuse here instead, where nothing has changed\n    // yet: the caller must dispose the existing handle before establishing a different policy.\n    if (selfSessionClaimed) {\n      throw new E_SANDBOX_POLICY_CONFLICT([\n        'a sandbox session established by this process is still live; dispose that handle before ' +\n          'constructing another enforcer, or reuse it — replacing it here would widen the live ' +\n          'policy before admission could refuse it',\n      ])\n    }\n    // CLAIM SYNCHRONOUSLY, BEFORE THE FIRST AWAIT. Setting this after `initialize()` leaves a window\n    // in which two concurrent constructions both read `false`, both proceed, and the second silently\n    // re-initialises over the first — verified: `Promise.all` of two enforcers with different policies\n    // resolved BOTH, and the second reported the first's derived lists. The check and the claim must be\n    // one synchronous step, because there is no lock to take: the boundary is a process-global.\n    selfSessionClaimed = true\n    try {\n      if (selfInitialized && manager.isSandboxingEnabled()) await manager.reset()\n      // THE nested-sandbox flag MUST ride this call and only this one. SRT resolves it from the\n      // module-level config `initialize()` stored (`getEnableWeakerNestedSandbox()` reads\n      // `config?.enableWeakerNestedSandbox`), NOT from the per-call config handed to\n      // `wrapWithSandboxArgv` — so passing it only at the `run()` site below would compile, ship, and\n      // silently do nothing.\n      await manager.initialize(\n        mapPolicy(options.policy, {\n          enableWeakerNestedSandbox: options.enableWeakerNestedSandbox,\n          bwrapPath,\n          socatPath,\n        })\n      )\n      selfInitialized = true\n    } catch (error) {\n      // A failed establishment must not leave the claim held, or the process is permanently wedged\n      // with no live session to dispose.\n      selfSessionClaimed = false\n      throw error\n    }\n  }\n\n  const platform = process.platform === 'linux' ? 'linux' : 'darwin'\n  const cwd = process.cwd()\n  const derive = (): DerivedRules => {\n    const config = manager.getConfig()\n    const filesystem = config?.filesystem ?? {}\n    const network = config?.network ?? {}\n    const allowGitConfig = adopted\n      ? Boolean(filesystem.allowGitConfig)\n      : (options.policy.filesystem.allowGitConfig ?? false)\n    const searchDepth = adopted\n      ? (config?.mandatoryDenySearchDepth ?? 3)\n      : (options.policy.filesystem.mandatoryDenySearchDepth ?? 3)\n    const dotGit = dotGitIsDirectory(cwd)\n    return derivedRulesFromSrt({\n      platform,\n      read: manager.getFsReadConfig(),\n      write: manager.getFsWriteConfig(),\n      filesystemDisabled: adopted\n        ? Boolean(filesystem.disabled)\n        : options.policy.filesystem.disabled,\n      // In adoption mode these are literal foreign lists. In particular, ['*'] is not evidence\n      // that the foreign consumer used ADK's disabled mode.\n      network: {\n        // ADOPTION ⇒ always `false`: we did not construct this session, so \"we were constructed in\n        // disabled mode\" cannot be true of it. OWNED ⇒ the ADK policy's own value, which is what makes\n        // the drift SKIP branch and the `false → true` widening check implementable.\n        disabled: adopted ? false : (options.policy.network.disabled ?? false),\n        allowedDomains: adopted\n          ? Array.isArray(network.allowedDomains)\n            ? network.allowedDomains\n            : []\n          : options.policy.network.disabled\n            ? undefined\n            : nonEmpty(options.policy.network.allowedDomains),\n        deniedDomains: adopted\n          ? Array.isArray(network.deniedDomains)\n            ? network.deniedDomains\n            : []\n          : options.policy.network.disabled\n            ? []\n            : nonEmpty(options.policy.network.deniedDomains),\n      },\n      mandatoryDeny: {\n        form: platform === 'linux' ? 'expanded-paths' : 'glob',\n        entries: reproduceMandatoryDeny({\n          cwd,\n          allowGitConfig,\n          platform,\n          dotGitIsDirectory: dotGit,\n        }),\n        allowGitConfig,\n        searchDepth,\n        ...(platform === 'linux' ? { dotGitWasDirectory: dotGit } : {}),\n      },\n    })\n  }\n  // CAPTURE the derived baseline immediately after our initialize, or the live foreign baseline\n  // when adopting. It is deliberately derived from SRT's getters, not from the requested policy.\n  const derived: DerivedRules = derive()\n  // PRIME THE GLOB MATCHER with upstream's own `globToRegex` for every rule in the derived lists.\n  // This is the whole reason `srtEnforcer` is async at the right moment: the evaluator is synchronous\n  // (it is consulted per path) while the upstream import is not, so unless priming happens HERE the\n  // synchronous fallback becomes the only path that ever runs in production — making the plan's\n  // \"deep-import the matcher, never port it\" rule true on paper and false in fact.\n  await primeGlobMatcher([\n    ...derived.read.denyOnly,\n    ...derived.read.allowWithinDeny,\n    ...derived.write.allowOnly,\n    ...derived.write.denyWithinAllow,\n    ...derived.mandatoryDeny.entries,\n  ])\n  const enforcer: SandboxPolicyEnforcer = {\n    isSupported: () => manager.isSupportedPlatform(),\n    adopted,\n    checkDependencies: async () => manager.checkDependenciesAsync(),\n    run: async (op) => {\n      const mapped = mapPolicy(op.policy)\n      const command = await quoteShellArgs(op.argv)\n      const wrapped = await manager.wrapWithSandboxArgv(\n        command,\n        binShell,\n        mapped,\n        op.signal,\n        op.cwd,\n        { commandId: op.correlationId }\n      )\n      const child = spawn(wrapped.argv[0], wrapped.argv.slice(1), {\n        cwd: op.cwd,\n        // SRT's `wrapped.env` is the unchanged process environment, not sandbox plumbing. Merging it\n        // here would defeat the allow-list by reintroducing every host variable. SRT's proxy, CA\n        // bundle, and git safe-directory plumbing are encoded in `wrapped.argv` instead. The caller's\n        // per-call environment remains the final overlay, by contract.\n        env: { ...hostEnv, ...(op.env ?? {}) },\n        stdio: ['ignore', 'pipe', 'pipe'],\n        shell: false,\n        detached: true,\n      })\n      let terminate: (() => void) | undefined\n      if (op.signal) {\n        terminate = () => {\n          try {\n            if (child.pid) process.kill(-child.pid, 'SIGKILL')\n            else child.kill('SIGKILL')\n          } catch {\n            child.kill('SIGKILL')\n          }\n        }\n        if (op.signal.aborted) terminate()\n        else op.signal.addEventListener('abort', terminate, { once: true })\n      }\n      const cleanup = () => {\n        if (terminate && op.signal) op.signal.removeEventListener('abort', terminate)\n        terminate = undefined\n      }\n      const completed = new Promise<{ exitCode: number; failed: boolean }>((settle) => {\n        // 'error' MUST be handled, and not only so `completed` settles: an unhandled 'error' on a\n        // ChildProcess is an uncaught exception that terminates the HOST process. A missing wrapper\n        // binary or an unusable `cwd` would therefore kill the agent instead of failing one command.\n        // Both listeners are `once` and the promise settles first-write-wins, so the pair is safe:\n        // a spawn failure emits 'error' then 'close', and the later 'close' is discarded.\n        child.once('error', () => {\n          cleanup()\n          settle({ exitCode: 1, failed: true })\n        })\n        child.once('close', (code) => {\n          cleanup()\n          settle({ exitCode: code ?? 1, failed: (code ?? 1) !== 0 })\n        })\n      })\n      return {\n        stdout: toWeb(child.stdout!),\n        stderr: toWeb(child.stderr!),\n        completed,\n      }\n    },\n    // Owned sessions are immutable from this adapter's perspective and retain the cheap cached\n    // snapshot. An adopted session must re-read SRT on every call: a foreign updateConfig() can widen\n    // the live sandbox between invocations, and drift() must compare against that live state.\n    effectivePolicy: () => (adopted ? derive() : derived),\n    diagnosticsFor: (id) =>\n      manager.getSandboxViolationStore().getViolationsForCommand(id).map(String),\n    dispose: async () => {\n      // Adoption is reported as a no-op. Resetting here would tear down ACEs owned by the host\n      // application; only the manager we initialized may be reset.\n      // ADOPTION IS A REPORTED NO-OP: resetting here would tear down ACEs owned by the host\n      // application. Only a session we established may be reset.\n      //\n      // `selfInitialized` is deliberately NOT cleared. `reset()` leaves upstream's `config` set, so\n      // `isSandboxingEnabled()` stays `true` forever after the first initialise — and clearing our\n      // marker would make the NEXT construction see \"enabled, not ours\" and adopt a dead session,\n      // silently enforcing the disposed policy. Ownership is a property of this process having\n      // initialised at all, not of a session still being live.\n      if (!adopted && manager.isSandboxingEnabled()) {\n        await manager.reset()\n        // Release the claim so a later construction may establish a new session. `selfInitialized`\n        // deliberately stays true: upstream's `config` survives `reset()`, so clearing it would make\n        // the next construction see \"enabled, not ours\" and adopt a dead session.\n        selfSessionClaimed = false\n      }\n    },\n  }\n  return enforcer\n}\n\nexport { mapPolicy }\n","import { assertArgvValue, assertAllowedRipgrepFlag } from '../escape'\nimport type { HitFrame, PathFrame, SandboxPolicy } from '../types'\nimport type { SandboxPolicyEnforcer } from '../contracts/policy_enforcer'\n\n/**\n * A classified ripgrep outcome.\n *\n * @remarks\n * CLASSIFICATION ORDER IS LOAD-BEARING: diagnostics are checked BEFORE the exit status, because `rg`\n * runs under the sandbox and a denied path surfaces as a non-zero exit *plus* a violation record.\n * Triage generically first and a policy refusal is permanently mislabelled `io-failure` — the model\n * is told \"search broke\" when the truth is \"you may not read there\", and `denied-by-policy` becomes\n * unreachable despite being in the tool's outcome list.\n *\n * `no-matches` is a RESULT, not an error: `rg` exits 1 when it ran correctly and matched nothing.\n */\nexport type RipgrepFailure =\n  | { kind: 'denied-by-policy'; diagnostics: readonly string[] }\n  | { kind: 'invalid-pattern'; message: string }\n  | { kind: 'no-matches' }\n  | { kind: 'io-failure'; message: string; exitCode: number }\n\n/**\n * Convert a classified ripgrep failure into the error the searcher throws.\n *\n * @remarks\n * Exhaustive by construction: a new {@link RipgrepFailure} arm is a compile error here rather than\n * a silently generic message. `denied-by-policy` carries its diagnostics into the message — the\n * classification is checked BEFORE the generic exit-code triage precisely so a policy refusal is\n * not mislabelled `io-failure`, and flattening it back to a bare exit code here would discard the\n * only thing that makes that ordering worth having.\n *\n * @param failure - A classified failure other than `no-matches`, which is a result, not an error.\n * @returns The error to throw.\n */\nconst toRipgrepError = (failure: Exclude<RipgrepFailure, { kind: 'no-matches' }>): Error => {\n  switch (failure.kind) {\n    case 'denied-by-policy':\n      return new Error(`denied-by-policy: ${failure.diagnostics.join('; ')}`)\n    case 'invalid-pattern':\n      return new Error(failure.message)\n    case 'io-failure':\n      return new Error(failure.message || `ripgrep exited ${failure.exitCode}`)\n  }\n}\n\nconst bytes = new TextDecoder()\nconst collect = async (\n  stream: ReadableStream<Uint8Array>,\n  onText: (text: string) => void\n): Promise<void> => {\n  const reader = stream.getReader()\n  try {\n    for (;;) {\n      const next = await reader.read()\n      if (next.done) return\n      onText(bytes.decode(next.value, { stream: true }))\n    }\n  } finally {\n    reader.releaseLock()\n  }\n}\n\n/** Ripgrep backend. Both child pipes are drained immediately and concurrently. */\nexport const createRipgrepSearch = (\n  enforcer: SandboxPolicyEnforcer,\n  policy: SandboxPolicy\n): {\n  searchContent(o: {\n    root: string\n    pattern: string\n    maxDepth: number\n    limit: number\n    ignoreCase?: boolean\n    literal?: boolean\n    glob?: string\n    iglob?: string\n    follow?: boolean\n    hidden?: boolean\n    noIgnore?: boolean\n    signal?: AbortSignal\n  }): AsyncIterable<HitFrame>\n  findPaths(o: {\n    root: string\n    glob: string\n    maxDepth: number\n    limit: number\n    iglob?: string\n    follow?: boolean\n    hidden?: boolean\n    noIgnore?: boolean\n    signal?: AbortSignal\n  }): AsyncIterable<PathFrame>\n} => {\n  const run = async (argv: string[], cwd: string, signal?: AbortSignal) => {\n    const correlationId = `${crypto.randomUUID()}-${Date.now().toString(36)}`\n    const spawned = await enforcer.run({ argv, cwd, policy, correlationId, signal })\n    let out = ''\n    let err = ''\n    const stdout = collect(spawned.stdout, (chunk) => {\n      out += chunk\n    })\n    const stderr = collect(spawned.stderr, (chunk) => {\n      err += chunk\n    })\n    const [, , completed] = await Promise.all([stdout, stderr, spawned.completed])\n    const diagnostics = enforcer.diagnosticsFor(correlationId)\n    const exitCode = completed.exitCode\n    if (diagnostics.length > 0)\n      return { failure: { kind: 'denied-by-policy', diagnostics } as RipgrepFailure, out, err }\n    if (exitCode === 2 && err.startsWith('regex parse error'))\n      return { failure: { kind: 'invalid-pattern', message: err } as RipgrepFailure, out, err }\n    if (exitCode === 1) return { failure: { kind: 'no-matches' } as RipgrepFailure, out, err }\n    if (exitCode !== 0)\n      return { failure: { kind: 'io-failure', message: err, exitCode } as RipgrepFailure, out, err }\n    return { failure: undefined, out, err }\n  }\n  /**\n   * Defence-in-depth check that this adapter only ever emits flags it declared.\n   *\n   * @remarks\n   * Scans the WHOLE argv rather than a fixed index window: the previous `slice(1, 5)` silently depended\n   * on argv layout, so adding or reordering an argument moved the window off the flags. It also stops at\n   * `--`, because everything after the terminator is a model-supplied VALUE — a pattern like `--foo` is\n   * a legitimate search string there, not a flag, and `assertArgvValue` already owns that half.\n   */\n  const assertAdapterFlags = (argv: readonly string[]): void => {\n    for (const arg of argv.slice(1)) {\n      if (arg === '--') break\n      if (arg.startsWith('--')) assertAllowedRipgrepFlag(arg)\n    }\n  }\n  return {\n    async *searchContent(o) {\n      if (!Number.isInteger(o.limit) || o.limit < 1)\n        throw new Error('limit must be a positive integer')\n      if (o.follow)\n        throw new Error('follow is refused: descendant symlink containment audit pending')\n      const argv = [\n        'rg',\n        '--json',\n        ...(o.ignoreCase ? ['--ignore-case'] : []),\n        ...(o.literal ? ['--fixed-strings'] : []),\n        ...(o.glob ? ['--glob', assertArgvValue(o.glob)] : []),\n        ...(o.iglob ? ['--iglob', assertArgvValue(o.iglob)] : []),\n        ...(o.hidden ? ['--hidden'] : []),\n        ...(o.noIgnore ? ['--no-ignore'] : []),\n        '--max-depth',\n        String(o.maxDepth),\n        '--',\n        assertArgvValue(o.pattern),\n        assertArgvValue(o.root),\n      ]\n      assertAdapterFlags(argv)\n      const result = await run(argv, o.root, o.signal)\n      if (result.failure) {\n        if (result.failure.kind === 'no-matches') {\n          yield { kind: 'done', complete: true }\n          return\n        }\n        throw toRipgrepError(result.failure)\n      }\n      let shown = 0\n      let overLimit = false\n      for (const line of result.out.split('\\n')) {\n        if (!line) continue\n        try {\n          const item = JSON.parse(line) as {\n            type: string\n            data?: { path?: { text?: string }; line_number?: number; lines?: { text?: string } }\n          }\n          if (\n            item.type === 'match' &&\n            item.data?.path?.text &&\n            item.data.line_number &&\n            item.data.lines\n          ) {\n            if (shown >= o.limit) {\n              overLimit = true\n              break\n            }\n            yield {\n              kind: 'item',\n              path: item.data.path.text,\n              line: item.data.line_number,\n              text: item.data.lines.text ?? '',\n            }\n            shown++\n          }\n        } catch {\n          /* malformed rg diagnostics are an I/O failure in the real adapter */\n        }\n      }\n      yield overLimit\n        ? { kind: 'done', complete: false, omitted: 'over-limit', bound: 'limit', shown }\n        : { kind: 'done', complete: true }\n    },\n    async *findPaths(o) {\n      if (!Number.isInteger(o.limit) || o.limit < 1)\n        throw new Error('limit must be a positive integer')\n      if (o.follow)\n        throw new Error('follow is refused: descendant symlink containment audit pending')\n      const argv = [\n        'rg',\n        '--files',\n        '--glob',\n        assertArgvValue(o.glob),\n        ...(o.iglob ? ['--iglob', assertArgvValue(o.iglob)] : []),\n        ...(o.hidden ? ['--hidden'] : []),\n        ...(o.noIgnore ? ['--no-ignore'] : []),\n        '--max-depth',\n        String(o.maxDepth),\n        '--',\n        assertArgvValue(o.root),\n      ]\n      assertAdapterFlags(argv)\n      const result = await run(argv, o.root, o.signal)\n      if (result.failure) {\n        if (result.failure.kind === 'no-matches') {\n          yield { kind: 'done', complete: true }\n          return\n        }\n        throw toRipgrepError(result.failure)\n      }\n      let shown = 0\n      let overLimit = false\n      for (const path of result.out.split('\\n')) {\n        if (!path) continue\n        if (shown >= o.limit) {\n          overLimit = true\n          break\n        }\n        yield { kind: 'item', path }\n        shown++\n      }\n      yield overLimit\n        ? { kind: 'done', complete: false, omitted: 'over-limit', bound: 'limit', shown }\n        : { kind: 'done', complete: true }\n    },\n  }\n}\n"],"mappings":"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"}