// Sandburg helper installers and shell wrapper generation import { createGrepToolDefinition, type ExtensionContext } from "@earendil-works/pi-coding-agent"; import { spawnSync } from "child_process"; import { accessSync, chmodSync, closeSync, constants, existsSync, mkdirSync, openSync, readFileSync, readSync, realpathSync, renameSync, rmSync, statSync, writeFileSync, } from "fs"; import { basename, delimiter, dirname, isAbsolute, join, resolve } from "path"; import { AGENT_BIN_DIR, PI_WRAPPER_PATH, RG_WRAPPER_PATH, SANDBURG_AGENT_DIR_ENV, SANDBURG_PRIVATE_PATHS_ENV, TOOL_SANDBOX_RUNNER_PATH, } from "./private-roots.js"; import { SANDBURG_EXTENSION_PATH_ENV, SANDBURG_PROPAGATED_CHILD_ENV, SANDBURG_REAL_PI_ARGS_JSON_ENV, SANDBURG_REAL_PI_COMMAND_ENV, } from "./runtime-state.js"; export const ACTIVE_MARKER = "sandburg-extension-v1"; const RG_PROBE_MESSAGE = "sandburg-rg-wrapper-probe-hit"; export const TOOL_SANDBOX_RUNNER_MARKER = "# sandburg-extension-managed: tool-sandbox-runner"; export const RG_MARKER = "# sandburg-extension-managed: rg-wrapper"; export const PI_WRAPPER_MARKER = "// sandburg-extension-managed: pi-wrapper"; type ManagedExecutableSpec = { label: string; path: string; marker: string; content: string; }; export type PiWrapperInstallResult = { status: "installed" | "unavailable"; path: string; violations: string[]; }; const PI_WRAPPER_SCRIPT = `#!/usr/bin/env node ${PI_WRAPPER_MARKER} // // Generated by the Sandburg Pi extension. Safe to remove when Pi is not using it; // the extension recreates it on startup. Do not edit in place: if customization is // needed, change the extension that generated this file. const { spawn } = require("node:child_process"); const fs = require("node:fs"); const path = require("node:path"); const ACTIVE_ENV = "SANDBURG_ACTIVE"; const ACTIVE_MARKER = ${JSON.stringify(ACTIVE_MARKER)}; const WRAPPER_MARKER = ${JSON.stringify(PI_WRAPPER_MARKER)}; const REAL_PI_COMMAND_ENV = ${JSON.stringify(SANDBURG_REAL_PI_COMMAND_ENV)}; const REAL_PI_ARGS_JSON_ENV = ${JSON.stringify(SANDBURG_REAL_PI_ARGS_JSON_ENV)}; const SANDBURG_EXTENSION_ENV = ${JSON.stringify(SANDBURG_EXTENSION_PATH_ENV)}; const PROPAGATED_CHILD_ENV = ${JSON.stringify(SANDBURG_PROPAGATED_CHILD_ENV)}; const PACKAGE_COMMANDS = new Set(["install", "remove", "uninstall", "update", "list", "config"]); const HELP_OR_VERSION = new Set(["--help", "-h", "--version", "-v"]); function fail(message) { console.error("sandburg pi wrapper: " + message); process.exit(127); } function parseArgsPrefix(value) { try { const parsed = JSON.parse(value ?? ""); if (Array.isArray(parsed) && parsed.every((entry) => typeof entry === "string")) return parsed; } catch { // Fall through to fail below. } fail(REAL_PI_ARGS_JSON_ENV + " is missing or invalid"); } function removeSandburgExtensionArgs(args, sandburgExtensionPath) { const result = []; for (let index = 0; index < args.length; index++) { const arg = args[index]; if ((arg === "-e" || arg === "--extension") && index + 1 < args.length) { const value = args[index + 1]; if (value === sandburgExtensionPath) { index++; continue; } result.push(arg, value); index++; continue; } result.push(arg); } return result; } function realpathIfPossible(candidate) { try { return fs.realpathSync(candidate); } catch { return path.resolve(candidate); } } function isExecutable(candidate) { try { fs.accessSync(candidate, fs.constants.X_OK); return true; } catch { return false; } } function isManagedPiWrapper(candidate) { try { return fs.readFileSync(candidate, "utf8").includes(WRAPPER_MARKER); } catch { return false; } } function findFallbackPi() { const currentWrapper = realpathIfPossible(process.argv[1] || ""); for (const dir of (process.env.PATH || "").split(path.delimiter).filter(Boolean)) { if (!path.isAbsolute(dir)) continue; const candidate = path.join(dir, "pi"); if (!isExecutable(candidate)) continue; if (realpathIfPossible(candidate) === currentWrapper) continue; if (isManagedPiWrapper(candidate)) continue; return candidate; } return undefined; } function spawnAndExit(command, args, env = process.env) { const child = spawn(command, args, { stdio: "inherit", env }); child.on("error", (error) => fail("failed to launch real pi: " + error.message)); child.on("exit", (code, signal) => { if (signal) { process.kill(process.pid, signal); return; } process.exit(code ?? 1); }); } function delegateUnchangedOutsideSandburg(originalArgs) { const fallbackPi = findFallbackPi(); if (!fallbackPi) fail("not active and no real pi found on PATH"); spawnAndExit(fallbackPi, originalArgs); } function main() { const originalArgs = process.argv.slice(2); if (process.env[ACTIVE_ENV] !== ACTIVE_MARKER) { delegateUnchangedOutsideSandburg(originalArgs); return; } const realPiCommand = process.env[REAL_PI_COMMAND_ENV]; if (!realPiCommand) fail(REAL_PI_COMMAND_ENV + " is not set"); const realPiArgsPrefix = parseArgsPrefix(process.env[REAL_PI_ARGS_JSON_ENV]); const sandburgExtensionPath = process.env[SANDBURG_EXTENSION_ENV]; const firstArg = originalArgs[0]; const shouldBypassInjection = PACKAGE_COMMANDS.has(firstArg) || HELP_OR_VERSION.has(firstArg); let piArgs = originalArgs; if (!shouldBypassInjection) { if (!sandburgExtensionPath) fail(SANDBURG_EXTENSION_ENV + " is not set"); piArgs = ["-e", sandburgExtensionPath, ...removeSandburgExtensionArgs(originalArgs, sandburgExtensionPath)]; } spawnAndExit(realPiCommand, [...realPiArgsPrefix, ...piArgs], { ...process.env, [PROPAGATED_CHILD_ENV]: "1", }); } main(); `; const TOOL_SANDBOX_RUNNER_SCRIPT = `#!/usr/bin/env bash ${TOOL_SANDBOX_RUNNER_MARKER} # # Generated by the Sandburg Pi extension. Safe to remove when Pi is not using it; # the extension recreates it on startup. Do not edit in place: if customization is # needed, change the extension that generated this file. set -euo pipefail if (($# == 0)); then echo "Usage: sandburg-tool-sandbox COMMAND [ARG...]" >&2 exit 2 fi active_marker=${shQuote(ACTIVE_MARKER)} agent_dir_env=${shQuote(SANDBURG_AGENT_DIR_ENV)} private_paths_env=${shQuote(SANDBURG_PRIVATE_PATHS_ENV)} if [[ \${SANDBURG_ACTIVE:-} != "$active_marker" ]]; then echo "sandburg-tool-sandbox: SANDBURG_ACTIVE is missing or invalid" >&2 exit 2 fi agent_dir=\${!agent_dir_env:-} if [[ -z $agent_dir ]]; then echo "sandburg-tool-sandbox: $agent_dir_env is not set" >&2 exit 2 fi raw_agent_dir=$agent_dir agent_dir=$(realpath -e -- "$raw_agent_dir") || { echo "sandburg-tool-sandbox: cannot canonicalize Pi agent directory: $raw_agent_dir" >&2 exit 2 } private_paths=\${!private_paths_env:-} pass_vars=\${SANDBURG_PASS_VARS:-} if [[ ! -d $agent_dir ]]; then echo "sandburg-tool-sandbox: Pi agent directory not found: $agent_dir" >&2 exit 2 fi bwrap_args=( --unshare-all --die-with-parent --new-session --bind / / --dev /dev --proc /proc ) hidden_paths=() path_within_or_equal() { local path=\${1%/} local root=\${2%/} [[ $root == / || $path == "$root" || $path == "$root"/* ]] } add_hidden_path() { local path=$1 local requirement=$2 local existing local -a kept_paths=() case $requirement in required|optional) ;; *) echo "sandburg-tool-sandbox: internal error: invalid hidden path requirement: $requirement" >&2 exit 2 ;; esac if [[ $requirement == required || -e $path ]]; then path=$(realpath -e -- "$path") || { echo "sandburg-tool-sandbox: cannot canonicalize private path: $path" >&2 exit 2 } else path=$(realpath -m -- "$path") || { echo "sandburg-tool-sandbox: cannot canonicalize private path: $path" >&2 exit 2 } fi if [[ $path == / ]]; then echo "sandburg-tool-sandbox: refusing to hide / as a private path" >&2 exit 2 fi for existing in "\${hidden_paths[@]}"; do # An existing broader root already hides this path. if path_within_or_equal "$path" "$existing"; then return 0 fi # This broader root will hide the existing narrower path. if path_within_or_equal "$existing" "$path"; then continue fi kept_paths+=("$existing") done hidden_paths=("\${kept_paths[@]}" "$path") } validate_extra_private_path() { local path=$1 if [[ -z $path ]]; then echo "sandburg-tool-sandbox: empty path in $private_paths_env" >&2 exit 2 fi if [[ $path != /* ]]; then echo "sandburg-tool-sandbox: path in $private_paths_env is not absolute: $path" >&2 exit 2 fi if [[ ! -d $path ]]; then echo "sandburg-tool-sandbox: path in $private_paths_env is not an existing directory: $path" >&2 exit 2 fi } # Hide extra private roots before hiding Sandburg's own defaults. This lets a # broad configured root subsume narrower defaults cleanly. if [[ -n $private_paths ]]; then if [[ $private_paths == :* || $private_paths == *: || $private_paths == *::* ]]; then echo "sandburg-tool-sandbox: empty path in $private_paths_env" >&2 exit 2 fi IFS=: read -r -a private_path_array <<< "$private_paths" for path in "\${private_path_array[@]}"; do validate_extra_private_path "$path" add_hidden_path "$path" required done fi add_hidden_path "$agent_dir/bin" required add_hidden_path /tmp/jiti optional add_hidden_path "$agent_dir" required for path in "\${hidden_paths[@]}"; do bwrap_args+=(--tmpfs "$path") done for path in "\${hidden_paths[@]}"; do bwrap_args+=(--remount-ro "$path") done env_args=(--clearenv) declare -A passed_env_names=() env_is_exported() { local declaration declaration=$(declare -p "$1" 2>/dev/null) || return 1 case $declaration in declare\\ -*x*) return 0 ;; *) return 1 ;; esac } pass_env_if_set() { local name=$1 [[ -z \${passed_env_names[$name]+x} ]] || return 0 passed_env_names[$name]=1 if env_is_exported "$name"; then env_args+=(--setenv "$name" "\${!name}") fi } pass_configured_var() { local name=$1 if [[ -z $name ]]; then echo "sandburg-tool-sandbox: empty variable name in SANDBURG_PASS_VARS" >&2 exit 2 fi if [[ ! $name =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then echo "sandburg-tool-sandbox: invalid variable name in SANDBURG_PASS_VARS: $name" >&2 exit 2 fi case $name in SANDBURG_*) echo "sandburg-tool-sandbox: reserved variable name in SANDBURG_PASS_VARS: $name" >&2 exit 2 ;; esac pass_env_if_set "$name" } for name in HOME USER LOGNAME SHELL PATH TERM LANG COLORTERM; do pass_env_if_set "$name" done for name in "\${!LC_@}"; do pass_env_if_set "$name" done # Preserve Sandkasten nested-sandbox path policy through Sandburg's # additional tool sandbox. for name in SKN_PATH_CHECK SKN_RO_BINDS; do pass_env_if_set "$name" done # Trusted launch configuration may pass additional exact-name environment # variables. Missing listed variables are omitted, like missing default # variables above. if [[ -n $pass_vars ]]; then if [[ $pass_vars == :* || $pass_vars == *: || $pass_vars == *::* ]]; then echo "sandburg-tool-sandbox: empty variable name in SANDBURG_PASS_VARS" >&2 exit 2 fi IFS=: read -r -a pass_vars_array <<< "$pass_vars" for name in "\${pass_vars_array[@]}"; do pass_configured_var "$name" done fi env_args+=( --setenv SANDBURG_ACTIVE "$SANDBURG_ACTIVE" --setenv SANDBURG_TOOL_SANDBOX 1 ) exec bwrap \ "\${bwrap_args[@]}" \ "\${env_args[@]}" \ "$@" `; export function shQuote(value: string): string { return `'${value.replace(/'/g, `'\\''`)}'`; } function rgWrapperScript(realRgPath: string, toolSandboxRunnerPath: string): string { return `#!/bin/sh ${RG_MARKER} # # Generated by the Sandburg Pi extension. Safe to remove when Pi is not using it; # the extension recreates it on startup. Do not edit in place: if customization is # needed, change the extension that generated this file. set -eu active_marker=${shQuote(ACTIVE_MARKER)} probe_message=${shQuote(RG_PROBE_MESSAGE)} real_rg=${shQuote(realRgPath)} tool_sandbox_runner=${shQuote(toolSandboxRunnerPath)} # Startup self-test hook used by the extension to verify that Pi’s grep tool # reaches this wrapper. This intentionally writes only to stderr and exits # before entering the sandbox or touching the filesystem. if [ "\${SANDBURG_RG_WRAPPER_PROBE:-}" = "$active_marker" ]; then script_path=$(readlink -f -- "$0") || script_path=$0 echo "$probe_message $script_path" >&2 exit 86 fi if [ "\${SANDBURG_ACTIVE:-}" != "$active_marker" ] || [ "\${SANDBURG_TOOL_SANDBOX:-}" = 1 ]; then exec "$real_rg" "$@" fi exec "$tool_sandbox_runner" "$real_rg" "$@" `; } function errorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); } function isExecutable(path: string): boolean { try { accessSync(path, constants.X_OK); return true; } catch { return false; } } function isScriptFile(path: string): boolean { let fd: number | undefined; try { fd = openSync(realpathSync(path), "r"); const bytes = Buffer.allocUnsafe(2); const bytesRead = readSync(fd, bytes, 0, bytes.length, 0); return bytesRead === 2 && bytes[0] === 0x23 && bytes[1] === 0x21; } catch { return false; } finally { if (fd !== undefined) { try { closeSync(fd); } catch { // Ignore close errors in this best-effort file probe. } } } } function isRipgrep(path: string): boolean { if (!isExecutable(path)) return false; const result = spawnSync(path, ["--version"], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 3000, }); return result.status === 0 && result.stdout.startsWith("ripgrep "); } function isPlainRipgrep(path: string): boolean { return !isScriptFile(path) && isRipgrep(path); } function writeExecutable(path: string, content: string) { const tempPath = join(dirname(path), `.${basename(path)}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`); try { writeFileSync(tempPath, content, { encoding: "utf-8", mode: 0o755, flag: "wx" }); chmodSync(tempPath, 0o755); renameSync(tempPath, path); } catch (error) { rmSync(tempPath, { force: true }); throw error; } } function ensureExecutable(path: string, label: string): string[] { if (isExecutable(path)) return []; try { chmodSync(path, statSync(path).mode | 0o100); } catch (error) { return [`${label}: cannot make executable: ${path}: ${errorMessage(error)}`]; } return isExecutable(path) ? [] : [`${label}: is not executable: ${path}`]; } function readExistingManagedExecutable(spec: ManagedExecutableSpec): { content?: string; violations: string[] } { if (!existsSync(spec.path)) return { violations: [] }; let content: string; try { content = readFileSync(spec.path, "utf-8"); } catch (error) { return { violations: [`${spec.label}: cannot read: ${spec.path}: ${errorMessage(error)}`] }; } if (!content.includes(spec.marker)) { return { violations: [`${spec.label}: unmanaged file at ${spec.path}`] }; } return { content, violations: [] }; } function installManagedExecutable(spec: ManagedExecutableSpec): string[] { const existing = readExistingManagedExecutable(spec); if (existing.violations.length > 0) return existing.violations; if (existing.content !== spec.content) { try { writeExecutable(spec.path, spec.content); } catch (error) { return [`${spec.label}: cannot install: ${spec.path}: ${errorMessage(error)}`]; } } return ensureExecutable(spec.path, spec.label); } function checkManagedExecutable(spec: ManagedExecutableSpec): string[] { if (!existsSync(spec.path)) return [`${spec.label}: missing: ${spec.path}`]; const existing = readExistingManagedExecutable(spec); if (existing.violations.length > 0) return existing.violations; const violations: string[] = []; if (existing.content !== spec.content) { violations.push(`${spec.label}: contents differ from this extension's expected helper: ${spec.path}`); } if (!isExecutable(spec.path)) { violations.push(`${spec.label}: not executable: ${spec.path}`); } return violations; } function pathEntries(pathValue: string): string[] { return pathValue.split(delimiter).filter(Boolean); } function sameExistingPath(a: string, b: string): boolean { try { return realpathSync(a) === realpathSync(b); } catch { return resolve(a) === resolve(b); } } export function ensurePathEntryFirst(pathValue: string | undefined, entry: string): { value: string; updated: boolean } { const entries = pathEntries(pathValue ?? ""); let found = false; const entriesWithoutTarget = entries.filter((existing) => { if (sameExistingPath(existing, entry)) { found = true; return false; } return true; }); if (found && entries.length > 0 && sameExistingPath(entries[0], entry)) { return { value: pathValue ?? "", updated: false }; } return { value: [entry, ...entriesWithoutTarget].join(delimiter), updated: true }; } function findRealRgOnPathSkipping(binDir: string): string | undefined { for (const dir of pathEntries(process.env.PATH ?? "")) { if (!isAbsolute(dir)) continue; const candidateDir = resolve(dir); if (sameExistingPath(candidateDir, binDir)) continue; const candidate = join(candidateDir, "rg"); if (existsSync(candidate) && isPlainRipgrep(candidate)) { return realpathSync(candidate); } } return undefined; } const NO_REAL_RG_VIOLATION = `rg wrapper: no real ripgrep binary found outside ${AGENT_BIN_DIR}; install ripgrep or adjust PATH, then /reload.`; // Resolve the real ripgrep that the persistent wrapper will delegate to. PATH // fallback deliberately skips getAgentDir()/bin and relative entries so the // wrapper cannot delegate to itself. function resolveRealRg(): string | undefined { return findRealRgOnPathSkipping(AGENT_BIN_DIR); } function getManagedHelperSpecs(): { specs: ManagedExecutableSpec[]; violations: string[] } { const specs: ManagedExecutableSpec[] = [ { label: "tool sandbox runner", path: TOOL_SANDBOX_RUNNER_PATH, marker: TOOL_SANDBOX_RUNNER_MARKER, content: TOOL_SANDBOX_RUNNER_SCRIPT, }, ]; const realRg = resolveRealRg(); if (!realRg) return { specs, violations: [NO_REAL_RG_VIOLATION] }; specs.push({ label: "rg wrapper", path: RG_WRAPPER_PATH, marker: RG_MARKER, content: rgWrapperScript(realRg, TOOL_SANDBOX_RUNNER_PATH), }); return { specs, violations: [] }; } export function installSandburgHelpers(): string[] { try { mkdirSync(AGENT_BIN_DIR, { recursive: true }); } catch (error) { return [`agent bin directory: cannot create ${AGENT_BIN_DIR}: ${errorMessage(error)}`]; } const { specs, violations } = getManagedHelperSpecs(); if (violations.length > 0) return violations; for (const spec of specs) { const specViolations = installManagedExecutable(spec); if (specViolations.length > 0) return specViolations; } return []; } export function checkSandburgHelpers(): string[] { const { specs, violations } = getManagedHelperSpecs(); return [...violations, ...specs.flatMap((spec) => checkManagedExecutable(spec))]; } export function checkBubblewrapUsable(): string[] { const result = spawnSync( "bwrap", [ "--unshare-all", "--die-with-parent", "--new-session", "--bind", "/", "/", "--dev", "/dev", "--proc", "/proc", "--tmpfs", "/tmp", "--remount-ro", "/tmp", "--clearenv", "/bin/true", ], { encoding: "utf-8", stdio: ["ignore", "ignore", "pipe"], timeout: 5000, }, ); if (result.status === 0) return []; if (result.error?.message) return [`bwrap: usability probe failed: ${result.error.message}`]; const stderr = result.stderr.trim().replace(/\s+/g, " ").slice(0, 500); const detail = result.signal ? `signal ${result.signal}` : `exit code ${result.status ?? "unknown"}`; return [`bwrap: usability probe failed with ${detail}${stderr ? `: ${stderr}` : ""}`]; } function getPiWrapperSpec(): ManagedExecutableSpec { return { label: "pi wrapper", path: PI_WRAPPER_PATH, marker: PI_WRAPPER_MARKER, content: PI_WRAPPER_SCRIPT, }; } export function installSandburgPiWrapper(): PiWrapperInstallResult { try { mkdirSync(AGENT_BIN_DIR, { recursive: true }); } catch (error) { return { status: "unavailable", path: PI_WRAPPER_PATH, violations: [`agent bin directory: cannot create ${AGENT_BIN_DIR}: ${errorMessage(error)}`], }; } const violations = installManagedExecutable(getPiWrapperSpec()); return { status: violations.length === 0 ? "installed" : "unavailable", path: PI_WRAPPER_PATH, violations, }; } export function isSandburgManagedPiWrapper(path: string | undefined): boolean { if (!path) return false; try { return readFileSync(path, "utf-8").includes(PI_WRAPPER_MARKER); } catch { return false; } } export function findFirstPiOnPath(pathValue = process.env.PATH ?? ""): string | undefined { for (const dir of pathEntries(pathValue)) { if (!isAbsolute(dir)) continue; const candidate = join(dir, "pi"); if (existsSync(candidate) && isExecutable(candidate)) return candidate; } return undefined; } function probeExtensionContext(cwd: string): ExtensionContext { return { ui: { select: async () => undefined, confirm: async () => false, input: async () => undefined, notify: () => {}, onTerminalInput: () => () => {}, setStatus: () => {}, setWorkingMessage: () => {}, setWorkingVisible: () => {}, setWorkingIndicator: () => {}, setHiddenThinkingLabel: () => {}, setWidget: () => {}, setFooter: () => {}, setHeader: () => {}, setTitle: () => {}, custom: async () => { throw new Error("Sandburg grep-wrapper probe does not support interactive UI."); }, pasteToEditor: () => {}, setEditorText: () => {}, getEditorText: () => "", editor: async () => undefined, addAutocompleteProvider: () => {}, setEditorComponent: () => {}, getEditorComponent: () => undefined, theme: undefined as never, getAllThemes: () => [], getTheme: () => undefined, setTheme: () => ({ success: false, error: "Sandburg grep-wrapper probe does not support themes." }), getToolsExpanded: () => false, setToolsExpanded: () => {}, }, hasUI: false, cwd, sessionManager: undefined as never, modelRegistry: undefined as never, model: undefined, isIdle: () => true, signal: undefined, abort: () => {}, hasPendingMessages: () => false, shutdown: () => {}, getContextUsage: () => undefined, compact: () => {}, getSystemPrompt: () => "", }; } // Probe Pi's public grep tool factory rather than guessing lookup behavior. // The generated rg wrapper exits early with a distinctive stderr message when // this env var is set, so the probe does not write to disk or enter the tool sandbox. export async function verifyPiGrepReachesRgWrapper(): Promise { const previousProbe = process.env.SANDBURG_RG_WRAPPER_PROBE; process.env.SANDBURG_RG_WRAPPER_PROBE = ACTIVE_MARKER; try { await createGrepToolDefinition(AGENT_BIN_DIR).execute( "sandburg-rg-wrapper-probe", { pattern: "sandburg-wrapper-probe-pattern-that-should-not-matter", path: TOOL_SANDBOX_RUNNER_PATH, literal: true, limit: 1, }, undefined, undefined, probeExtensionContext(AGENT_BIN_DIR), ); } catch (error) { const message = errorMessage(error); const probeMatch = new RegExp(`${RG_PROBE_MESSAGE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")} (.+)`, "m").exec(message); if (probeMatch) { const reportedWrapperPath = probeMatch[1].trim(); const expectedWrapperPath = realpathSync(RG_WRAPPER_PATH); if (reportedWrapperPath === expectedWrapperPath) return []; return [ `Pi's grep tool reached a stale Sandburg rg wrapper: ${reportedWrapperPath}; expected: ${expectedWrapperPath}`, ]; } return [ `Pi's grep tool invoked rg, but the managed wrapper probe did not respond as expected; wrapper: ${RG_WRAPPER_PATH}; error: ${message}`, ]; } finally { if (previousProbe === undefined) delete process.env.SANDBURG_RG_WRAPPER_PROBE; else process.env.SANDBURG_RG_WRAPPER_PROBE = previousProbe; } return [ `Pi's grep tool did not invoke the managed rg wrapper: ${RG_WRAPPER_PATH}; bash would be sandboxed, but grep would bypass the tool sandbox.`, ]; }